diff --git a/apps/dev-playground/package.json b/apps/dev-playground/package.json index 56357404..aca5e420 100644 --- a/apps/dev-playground/package.json +++ b/apps/dev-playground/package.json @@ -37,7 +37,7 @@ "@types/react-syntax-highlighter": "^15.5.13", "@vitejs/plugin-react": "^5.0.4", "dotenv": "^16.0.0", - "tsdown": "^0.15.7", + "tsdown": "^0.20.3", "tsx": "^4.20.6", "vite": "npm:rolldown-vite@7.1.14" }, diff --git a/docs/docs/development/project-setup.md b/docs/docs/development/project-setup.md index 101fd7a9..8aea2e14 100644 --- a/docs/docs/development/project-setup.md +++ b/docs/docs/development/project-setup.md @@ -63,7 +63,7 @@ The AppKit `server()` plugin automatically serves: "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", "@vitejs/plugin-react": "^5.1.1", - "tsdown": "^0.15.7", + "tsdown": "^0.20.3", "tsx": "^4.19.0", "typescript": "~5.6.0", "vite": "^7.2.4" diff --git a/docs/docs/plugins/plugin-management.md b/docs/docs/plugins/plugin-management.md index 1fa25068..41be9ff6 100644 --- a/docs/docs/plugins/plugin-management.md +++ b/docs/docs/plugins/plugin-management.md @@ -6,6 +6,8 @@ sidebar_position: 6 AppKit includes a CLI for managing plugins. All commands are available under `npx @databricks/appkit plugin`. +**Manifest convention:** `manifest.json` is the default and recommended format for CLI commands (`sync`, `list`, `validate`). For zero-trust safety, JS manifests (`manifest.js`/`manifest.cjs`) are ignored unless you pass `--allow-js-manifest`, which executes plugin code and should be used only with trusted sources. The **add-resource** command only edits `manifest.json` in place. + ## Create a plugin Scaffold a new plugin interactively: @@ -20,7 +22,7 @@ The wizard walks you through: - **Resources**: Which Databricks resources the plugin needs (SQL Warehouse, Secret, etc.) and whether each is required or optional - **Optional fields**: Author, version, license -The command generates a complete plugin scaffold with `manifest.json`, TypeScript class, and barrel exports — ready to register in your app. +The command generates a complete plugin scaffold with `manifest.json` and a TypeScript plugin class that imports the manifest directly — ready to register in your app. ## Sync plugin manifests @@ -32,6 +34,12 @@ npx @databricks/appkit plugin sync --write This discovers plugin manifests from installed packages and local imports, then writes a consolidated manifest used by deployment tooling. Plugins referenced in your `createApp({ plugins: [...] })` call are automatically marked as required. +Trusted installed Databricks packages (for example `@databricks/appkit`) are allowed to load bundled JS manifests during `plugin sync`. For other sources, if you intentionally rely on JS manifests, opt in explicitly: + +```bash +npx @databricks/appkit plugin sync --write --allow-js-manifest +``` + Use the `--silent` flag in build hooks to suppress output: ```json @@ -58,6 +66,8 @@ npx @databricks/appkit plugin validate plugins/my-plugin appkit.plugins.json The validator auto-detects whether a file is a plugin manifest or a template manifest (from `$schema`) and reports errors with humanized paths and expected values. +To include JS manifests in validation, pass `--allow-js-manifest`. + ## List plugins View registered plugins from `appkit.plugins.json` or scan a directory: @@ -69,13 +79,16 @@ npx @databricks/appkit plugin list # Scan a directory for plugin folders npx @databricks/appkit plugin list --dir plugins/ +# Scan a directory and include JS manifests (trusted code only) +npx @databricks/appkit plugin list --dir plugins/ --allow-js-manifest + # JSON output for scripting npx @databricks/appkit plugin list --json ``` ## Add a resource to a plugin -Interactively add a new resource requirement to an existing plugin manifest: +Interactively add a new resource requirement to an existing plugin manifest. **Requires `manifest.json`** in the plugin directory (the command edits it in place; it does not modify `manifest.js`): ```bash npx @databricks/appkit plugin add-resource diff --git a/package.json b/package.json index 9f557673..54364909 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "plop": "^4.0.4", "publint": "^0.3.15", "release-it": "^19.1.0", - "tsdown": "^0.15.7", + "tsdown": "^0.20.3", "tsx": "^4.20.6", "turbo": "^2.6.1", "typescript": "^5.6.0", diff --git a/packages/appkit-ui/package.json b/packages/appkit-ui/package.json index c02de498..e8fcce5e 100644 --- a/packages/appkit-ui/package.json +++ b/packages/appkit-ui/package.json @@ -2,6 +2,7 @@ "name": "@databricks/appkit-ui", "type": "module", "version": "0.15.0", + "license": "Apache-2.0", "repository": { "type": "git", "url": "git+https://github.com/databricks/appkit.git" @@ -14,6 +15,7 @@ "docs", "docs.md", "CLAUDE.md", + "LICENSE", "llms.txt", "README.md", "DCO", diff --git a/packages/appkit-ui/tsdown.config.ts b/packages/appkit-ui/tsdown.config.ts index fc329e1c..f7cb4d4a 100644 --- a/packages/appkit-ui/tsdown.config.ts +++ b/packages/appkit-ui/tsdown.config.ts @@ -9,12 +9,13 @@ export default defineConfig([ platform: "browser", minify: false, dts: { - resolve: true, + resolver: "oxc", }, copy: [ { from: "src/react/styles/globals.css", - to: "dist/styles.css", + to: "dist", + rename: "styles.css", }, ], clean: false, diff --git a/packages/appkit/package.json b/packages/appkit/package.json index 56c1657c..938ebfc6 100644 --- a/packages/appkit/package.json +++ b/packages/appkit/package.json @@ -5,6 +5,7 @@ "main": "./dist/index.js", "types": "./dist/index.d.ts", "packageManager": "pnpm@10.21.0", + "license": "Apache-2.0", "repository": { "type": "git", "url": "git+https://github.com/databricks/appkit.git" @@ -16,6 +17,7 @@ "docs", "docs.md", "CLAUDE.md", + "LICENSE", "llms.txt", "README.md", "DCO", diff --git a/packages/appkit/src/connectors/lakebase/index.ts b/packages/appkit/src/connectors/lakebase/index.ts index cc121a28..1f70e4ec 100644 --- a/packages/appkit/src/connectors/lakebase/index.ts +++ b/packages/appkit/src/connectors/lakebase/index.ts @@ -2,7 +2,7 @@ import { createLakebasePool as createLakebasePoolBase, type LakebasePoolConfig, } from "@databricks/lakebase"; -import type pg from "pg"; +import type { Pool } from "pg"; import { createLogger } from "@/logging/logger"; /** @@ -12,9 +12,7 @@ import { createLogger } from "@/logging/logger"; * @param config - Lakebase pool configuration * @returns PostgreSQL pool with appkit integration */ -export function createLakebasePool( - config?: Partial, -): pg.Pool { +export function createLakebasePool(config?: Partial): Pool { const logger = createLogger("connectors:lakebase"); return createLakebasePoolBase({ diff --git a/packages/appkit/src/plugins/analytics/analytics.ts b/packages/appkit/src/plugins/analytics/analytics.ts index 1619bdf0..288526fc 100644 --- a/packages/appkit/src/plugins/analytics/analytics.ts +++ b/packages/appkit/src/plugins/analytics/analytics.ts @@ -14,8 +14,9 @@ import { } from "../../context"; import { createLogger } from "../../logging/logger"; import { Plugin, toPlugin } from "../../plugin"; +import type { PluginManifest } from "../../registry"; import { queryDefaults } from "./defaults"; -import { analyticsManifest } from "./manifest"; +import manifest from "./manifest.json"; import { QueryProcessor } from "./query"; import type { AnalyticsQueryResponse, @@ -29,7 +30,7 @@ export class AnalyticsPlugin extends Plugin { name = "analytics"; /** Plugin manifest declaring metadata and resource requirements */ - static manifest = analyticsManifest; + static manifest = manifest as PluginManifest; protected static description = "Analytics plugin for data analysis"; protected declare config: IAnalyticsConfig; diff --git a/packages/appkit/src/plugins/analytics/index.ts b/packages/appkit/src/plugins/analytics/index.ts index 56774782..9ad02125 100644 --- a/packages/appkit/src/plugins/analytics/index.ts +++ b/packages/appkit/src/plugins/analytics/index.ts @@ -1,3 +1,2 @@ export * from "./analytics"; -export * from "./manifest"; export * from "./types"; diff --git a/packages/appkit/src/plugins/analytics/manifest.ts b/packages/appkit/src/plugins/analytics/manifest.ts deleted file mode 100644 index fe74e345..00000000 --- a/packages/appkit/src/plugins/analytics/manifest.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { readFileSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; -import type { PluginManifest } from "../../registry"; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -/** - * Analytics plugin manifest. - * - * The analytics plugin requires a SQL Warehouse for executing queries - * against Databricks data sources. - * - * @remarks - * The source of truth for this manifest is `manifest.json` in the same directory. - * This file loads the JSON and exports it with proper TypeScript typing. - */ -export const analyticsManifest: PluginManifest = JSON.parse( - readFileSync(join(__dirname, "manifest.json"), "utf-8"), -) as PluginManifest; diff --git a/packages/appkit/src/plugins/genie/genie.ts b/packages/appkit/src/plugins/genie/genie.ts index 1656a8ea..e78cdaa4 100644 --- a/packages/appkit/src/plugins/genie/genie.ts +++ b/packages/appkit/src/plugins/genie/genie.ts @@ -5,8 +5,9 @@ import { GenieConnector } from "../../connectors"; import { getWorkspaceClient } from "../../context"; import { createLogger } from "../../logging"; import { Plugin, toPlugin } from "../../plugin"; +import type { PluginManifest } from "../../registry"; import { genieStreamDefaults } from "./defaults"; -import { genieManifest } from "./manifest"; +import manifest from "./manifest.json"; import type { GenieConversationHistoryResponse, GenieSendMessageRequest, @@ -19,7 +20,7 @@ const logger = createLogger("genie"); export class GeniePlugin extends Plugin { name = "genie"; - static manifest = genieManifest; + static manifest = manifest as PluginManifest; protected static description = "AI/BI Genie space integration for natural language data queries"; diff --git a/packages/appkit/src/plugins/genie/index.ts b/packages/appkit/src/plugins/genie/index.ts index 6726262f..39487a9b 100644 --- a/packages/appkit/src/plugins/genie/index.ts +++ b/packages/appkit/src/plugins/genie/index.ts @@ -1,3 +1,2 @@ export * from "./genie"; -export * from "./manifest"; export * from "./types"; diff --git a/packages/appkit/src/plugins/genie/manifest.ts b/packages/appkit/src/plugins/genie/manifest.ts deleted file mode 100644 index cf3d98fb..00000000 --- a/packages/appkit/src/plugins/genie/manifest.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { readFileSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; -import type { PluginManifest } from "../../registry"; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -export const genieManifest: PluginManifest = JSON.parse( - readFileSync(join(__dirname, "manifest.json"), "utf-8"), -) as PluginManifest; diff --git a/packages/appkit/src/plugins/lakebase/index.ts b/packages/appkit/src/plugins/lakebase/index.ts index 73bcff5e..31b2b16b 100644 --- a/packages/appkit/src/plugins/lakebase/index.ts +++ b/packages/appkit/src/plugins/lakebase/index.ts @@ -1,3 +1,2 @@ export * from "./lakebase"; -export * from "./manifest"; export * from "./types"; diff --git a/packages/appkit/src/plugins/lakebase/lakebase.ts b/packages/appkit/src/plugins/lakebase/lakebase.ts index e8a1f18c..b6e0c961 100644 --- a/packages/appkit/src/plugins/lakebase/lakebase.ts +++ b/packages/appkit/src/plugins/lakebase/lakebase.ts @@ -1,4 +1,4 @@ -import type pg from "pg"; +import type { Pool, QueryResult, QueryResultRow } from "pg"; import { createLakebasePool, getLakebaseOrmConfig, @@ -7,7 +7,8 @@ import { } from "../../connectors/lakebase"; import { createLogger } from "../../logging/logger"; import { Plugin, toPlugin } from "../../plugin"; -import { lakebaseManifest } from "./manifest"; +import type { PluginManifest } from "../../registry"; +import manifest from "./manifest.json"; import type { ILakebaseConfig } from "./types"; const logger = createLogger("lakebase"); @@ -33,10 +34,10 @@ export class LakebasePlugin extends Plugin { name = "lakebase"; /** Plugin manifest declaring metadata and resource requirements */ - static manifest = lakebaseManifest; + static manifest = manifest as PluginManifest; protected declare config: ILakebaseConfig; - private pool: pg.Pool | null = null; + private pool: Pool | null = null; constructor(config: ILakebaseConfig) { super(config); @@ -72,10 +73,10 @@ export class LakebasePlugin extends Plugin { * ); * ``` */ - async query( + async query( text: string, values?: unknown[], - ): Promise> { + ): Promise> { // biome-ignore lint/style/noNonNullAssertion: pool is guaranteed non-null after setup(), which AppKit always awaits before exposing the plugin API return this.pool!.query(text, values); } diff --git a/packages/appkit/src/plugins/lakebase/manifest.ts b/packages/appkit/src/plugins/lakebase/manifest.ts deleted file mode 100644 index 7575062a..00000000 --- a/packages/appkit/src/plugins/lakebase/manifest.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { readFileSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; -import type { PluginManifest } from "../../registry"; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -export const lakebaseManifest: PluginManifest = JSON.parse( - readFileSync(join(__dirname, "manifest.json"), "utf-8"), -) as PluginManifest; diff --git a/packages/appkit/src/plugins/server/index.ts b/packages/appkit/src/plugins/server/index.ts index e7b05129..9398dc00 100644 --- a/packages/appkit/src/plugins/server/index.ts +++ b/packages/appkit/src/plugins/server/index.ts @@ -7,8 +7,9 @@ import type { PluginPhase } from "shared"; import { ServerError } from "../../errors"; import { createLogger } from "../../logging/logger"; import { Plugin, toPlugin } from "../../plugin"; +import type { PluginManifest } from "../../registry"; import { instrumentations } from "../../telemetry"; -import { serverManifest } from "./manifest"; +import manifest from "./manifest.json"; import { RemoteTunnelController } from "./remote-tunnel/remote-tunnel-controller"; import { StaticServer } from "./static-server"; import type { ServerConfig } from "./types"; @@ -41,7 +42,7 @@ export class ServerPlugin extends Plugin { }; /** Plugin manifest declaring metadata and resource requirements */ - static manifest = serverManifest; + static manifest = manifest as PluginManifest; public name = "server" as const; private serverApplication: express.Application; @@ -360,5 +361,4 @@ export const server = toPlugin( ); // Export manifest and types -export { serverManifest } from "./manifest"; export type { ServerConfig } from "./types"; diff --git a/packages/appkit/src/plugins/server/manifest.ts b/packages/appkit/src/plugins/server/manifest.ts deleted file mode 100644 index 97a4c716..00000000 --- a/packages/appkit/src/plugins/server/manifest.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { readFileSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; -import type { PluginManifest } from "../../registry"; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -/** - * Server plugin manifest. - * - * The server plugin doesn't require any Databricks resources - it only - * provides HTTP server functionality and static file serving. - * - * @remarks - * The source of truth for this manifest is `manifest.json` in the same directory. - * This file loads the JSON and exports it with proper TypeScript typing. - */ -export const serverManifest: PluginManifest = JSON.parse( - readFileSync(join(__dirname, "manifest.json"), "utf-8"), -) as PluginManifest; diff --git a/packages/appkit/tsdown.config.ts b/packages/appkit/tsdown.config.ts index 5fa71e97..5886fc18 100644 --- a/packages/appkit/tsdown.config.ts +++ b/packages/appkit/tsdown.config.ts @@ -11,11 +11,14 @@ export default defineConfig([ platform: "node", minify: false, dts: { - resolve: true, + resolver: "oxc", }, sourcemap: false, clean: false, unbundle: true, + outExtensions: () => ({ + js: ".js", + }), noExternal: ["shared"], external: (id) => { // Bundle "shared" workspace package and @/ path aliases @@ -37,23 +40,6 @@ export default defineConfig([ from: "src/plugins/server/remote-tunnel/denied.html", to: "dist/plugins/server/remote-tunnel/denied.html", }, - // Plugin manifest JSON files (source of truth for static analysis) - { - from: "src/plugins/analytics/manifest.json", - to: "dist/plugins/analytics/manifest.json", - }, - { - from: "src/plugins/genie/manifest.json", - to: "dist/plugins/genie/manifest.json", - }, - { - from: "src/plugins/lakebase/manifest.json", - to: "dist/plugins/lakebase/manifest.json", - }, - { - from: "src/plugins/server/manifest.json", - to: "dist/plugins/server/manifest.json", - }, ], }, ]); diff --git a/packages/lakebase/src/pool-config.ts b/packages/lakebase/src/pool-config.ts index bba4d663..a843bc17 100644 --- a/packages/lakebase/src/pool-config.ts +++ b/packages/lakebase/src/pool-config.ts @@ -1,4 +1,4 @@ -import type pg from "pg"; +import type { PoolConfig } from "pg"; import { getUsernameSync, parsePoolConfig } from "./config"; import { type DriverTelemetry, initTelemetry } from "./telemetry"; import { createTokenRefreshCallback } from "./token-refresh"; @@ -13,7 +13,7 @@ import type { LakebasePoolConfig, Logger } from "./types"; */ function mapSslConfig( sslMode: "require" | "prefer" | "disable", -): pg.PoolConfig["ssl"] { +): PoolConfig["ssl"] { switch (sslMode) { case "require": return { rejectUnauthorized: true }; @@ -43,7 +43,7 @@ export function getLakebasePgConfig( config?: Partial, telemetry?: DriverTelemetry, logger?: Logger, -): pg.PoolConfig { +): PoolConfig { const userConfig = config ?? {}; const poolConfig = parsePoolConfig(userConfig); const username = getUsernameSync(userConfig); diff --git a/packages/lakebase/tsdown.config.ts b/packages/lakebase/tsdown.config.ts index 293e2fc3..4bcf8600 100644 --- a/packages/lakebase/tsdown.config.ts +++ b/packages/lakebase/tsdown.config.ts @@ -11,12 +11,15 @@ export default defineConfig([ platform: "node", minify: false, dts: { - resolve: true, + resolver: "oxc", }, sourcemap: false, clean: false, unbundle: true, noExternal: [], + outExtensions: () => ({ + js: ".js", + }), external: (id) => { // Bundle all internal modules if (id.startsWith("@/")) return false; diff --git a/packages/shared/package.json b/packages/shared/package.json index d09ef87f..5d482b44 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -29,8 +29,6 @@ "@types/ws": "^8.18.1", "dependency-tree": "^11.2.0" }, - "main": "./dist/index.js", - "module": "./dist/index.js", "types": "./dist/index.d.ts", "publishConfig": { "exports": { diff --git a/packages/shared/src/cli/commands/plugin/add-resource/add-resource.ts b/packages/shared/src/cli/commands/plugin/add-resource/add-resource.ts index 6c6adef8..c6b3dd98 100644 --- a/packages/shared/src/cli/commands/plugin/add-resource/add-resource.ts +++ b/packages/shared/src/cli/commands/plugin/add-resource/add-resource.ts @@ -5,6 +5,7 @@ import { cancel, intro, outro } from "@clack/prompts"; import { Command } from "commander"; import { promptOneResource } from "../create/prompt-resource"; import { humanizeResourceType } from "../create/resource-defaults"; +import { resolveManifestInDir } from "../manifest-resolve"; import type { PluginManifest } from "../manifest-types"; import { validateManifest } from "../validate/validate-manifest"; @@ -18,13 +19,24 @@ async function runPluginAddResource(options: { path?: string }): Promise { const cwd = process.cwd(); const pluginDir = path.resolve(cwd, options.path ?? "."); - const manifestPath = path.join(pluginDir, "manifest.json"); + const resolved = resolveManifestInDir(pluginDir, { allowJsManifest: true }); - if (!fs.existsSync(manifestPath)) { - console.error(`manifest.json not found at ${manifestPath}`); + if (!resolved) { + console.error( + `No manifest found in ${pluginDir}. This command requires manifest.json (manifest.js cannot be edited in place).`, + ); + process.exit(1); + } + + if (resolved.type !== "json") { + console.error( + `Editable manifest not found. add-resource only supports plugin directories that contain manifest.json (found ${path.basename(resolved.path)}).`, + ); process.exit(1); } + const manifestPath = resolved.path; + let manifest: ManifestWithExtras; try { const raw = fs.readFileSync(manifestPath, "utf-8"); @@ -81,7 +93,7 @@ export const pluginAddResourceCommand = new Command("add-resource") ) .option( "-p, --path ", - "Plugin directory containing manifest.json (default: .)", + "Plugin directory containing manifest.json, which will be edited in place (default: .)", ) .action((opts) => runPluginAddResource(opts).catch((err) => { diff --git a/packages/shared/src/cli/commands/plugin/create/scaffold.test.ts b/packages/shared/src/cli/commands/plugin/create/scaffold.test.ts index b74f4c5c..66cd1e25 100644 --- a/packages/shared/src/cli/commands/plugin/create/scaffold.test.ts +++ b/packages/shared/src/cli/commands/plugin/create/scaffold.test.ts @@ -56,7 +56,7 @@ describe("scaffold", () => { }); describe("scaffoldPlugin (in-repo)", () => { - it("creates core files: manifest.json, manifest.ts, plugin.ts, index.ts", () => { + it("creates core files: manifest.json, plugin.ts, index.ts (no manifest.ts)", () => { const tmp = makeTempDir(); tempDirs.push(tmp); const targetDir = path.join(tmp, "my-plugin"); @@ -64,7 +64,7 @@ describe("scaffold", () => { scaffoldPlugin(targetDir, BASE_ANSWERS, { isolated: false }); expect(fs.existsSync(path.join(targetDir, "manifest.json"))).toBe(true); - expect(fs.existsSync(path.join(targetDir, "manifest.ts"))).toBe(true); + expect(fs.existsSync(path.join(targetDir, "manifest.ts"))).toBe(false); expect(fs.existsSync(path.join(targetDir, "my-plugin.ts"))).toBe(true); expect(fs.existsSync(path.join(targetDir, "index.ts"))).toBe(true); expect(fs.existsSync(path.join(targetDir, "package.json"))).toBe(false); @@ -88,7 +88,7 @@ describe("scaffold", () => { expect(manifest.$schema).toContain("plugin-manifest.schema.json"); }); - it("generates plugin class with PascalCase name", () => { + it("generates plugin class with PascalCase name and direct manifest.json import", () => { const tmp = makeTempDir(); tempDirs.push(tmp); const targetDir = path.join(tmp, "test"); @@ -101,6 +101,8 @@ describe("scaffold", () => { ); expect(pluginTs).toContain("class MyPlugin"); expect(pluginTs).toContain("export const myPlugin = toPlugin"); + expect(pluginTs).toContain('import manifest from "./manifest.json"'); + expect(pluginTs).toContain("manifest as PluginManifest"); }); it("generates index.ts with correct exports", () => { @@ -240,7 +242,6 @@ describe("scaffold", () => { ).toThrow(); expect(fs.existsSync(path.join(targetDir, "manifest.json"))).toBe(false); - expect(fs.existsSync(path.join(targetDir, "manifest.ts"))).toBe(false); }); }); }); diff --git a/packages/shared/src/cli/commands/plugin/create/scaffold.ts b/packages/shared/src/cli/commands/plugin/create/scaffold.ts index d93c127a..0149826b 100644 --- a/packages/shared/src/cli/commands/plugin/create/scaffold.ts +++ b/packages/shared/src/cli/commands/plugin/create/scaffold.ts @@ -92,7 +92,7 @@ function rollback(written: string[], targetDir: string): void { /** * Scaffold plugin files into targetDir. Pure: no interactive I/O. - * Writes manifest.json, manifest.ts, {name}.ts, index.ts; for isolated also package.json, tsconfig.json, README.md. + * Writes manifest.json, {name}.ts, index.ts; for isolated also package.json, tsconfig.json, README.md. * On failure, rolls back any files already written. */ export function scaffoldPlugin( @@ -115,27 +115,18 @@ export function scaffoldPlugin( written, ); - const manifestTs = `import { readFileSync } from "node:fs"; -import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; -import type { PluginManifest } from "@databricks/appkit"; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -export const manifest = JSON.parse( - readFileSync(join(__dirname, "manifest.json"), "utf-8"), -) as PluginManifest; -`; - - writeTracked(path.join(targetDir, "manifest.ts"), manifestTs, written); - - const pluginTs = `import { Plugin, toPlugin, type IAppRouter } from "@databricks/appkit"; -import { manifest } from "./manifest.js"; + const pluginTs = `import { + Plugin, + toPlugin, + type IAppRouter, + type PluginManifest, +} from "@databricks/appkit"; +import manifest from "./manifest.json"; export class ${className} extends Plugin { name = "${answers.name}"; - static manifest = manifest; + static manifest = manifest as PluginManifest; injectRoutes(router: IAppRouter): void { // Add your routes here, e.g.: @@ -159,7 +150,7 @@ export const ${exportName} = toPlugin< writeTracked(path.join(targetDir, `${answers.name}.ts`), pluginTs, written); - const indexTs = `export { ${className}, ${exportName} } from "./${answers.name}.js"; + const indexTs = `export { ${className}, ${exportName} } from "./${answers.name}"; `; writeTracked(path.join(targetDir, "index.ts"), indexTs, written); diff --git a/packages/shared/src/cli/commands/plugin/list/list.test.ts b/packages/shared/src/cli/commands/plugin/list/list.test.ts index b6a37f60..2315362c 100644 --- a/packages/shared/src/cli/commands/plugin/list/list.test.ts +++ b/packages/shared/src/cli/commands/plugin/list/list.test.ts @@ -122,7 +122,7 @@ describe("list", () => { }); describe("listFromDirectory", () => { - it("returns plugin rows from subdirectories with manifest.json", () => { + it("returns plugin rows from subdirectories with manifest.json", async () => { const tmp = makeTempDir("list-dir"); tempDirs.push(tmp); const pluginDir = path.join(tmp, "my-feature"); @@ -132,7 +132,7 @@ describe("list", () => { JSON.stringify(PLUGIN_MANIFEST_JSON, null, 2), ); - const rows = listFromDirectory(tmp, path.dirname(tmp)); + const rows = await listFromDirectory(tmp, path.dirname(tmp)); expect(rows).toHaveLength(1); expect(rows[0].name).toBe("my-feature"); @@ -142,21 +142,21 @@ describe("list", () => { expect(rows[0].optional).toBe(0); }); - it("returns empty array when directory does not exist", () => { - const rows = listFromDirectory("/nonexistent/dir", "/"); + it("returns empty array when directory does not exist", async () => { + const rows = await listFromDirectory("/nonexistent/dir", "/"); expect(rows).toEqual([]); }); - it("returns empty array when directory has no plugin subdirs with manifest.json", () => { + it("returns empty array when directory has no plugin subdirs with manifest", async () => { const tmp = makeTempDir("list-dir-empty"); tempDirs.push(tmp); fs.mkdirSync(path.join(tmp, "empty-subdir"), { recursive: true }); - const rows = listFromDirectory(tmp, path.dirname(tmp)); + const rows = await listFromDirectory(tmp, path.dirname(tmp)); expect(rows).toEqual([]); }); - it("skips subdirs without manifest.json", () => { + it("skips subdirs without manifest.json", async () => { const tmp = makeTempDir("list-dir-skip"); tempDirs.push(tmp); const withManifest = path.join(tmp, "with-manifest"); @@ -167,9 +167,79 @@ describe("list", () => { ); fs.mkdirSync(path.join(tmp, "no-manifest"), { recursive: true }); - const rows = listFromDirectory(tmp, path.dirname(tmp)); + const rows = await listFromDirectory(tmp, path.dirname(tmp)); expect(rows).toHaveLength(1); expect(rows[0].name).toBe("my-feature"); }); + + it("does not load JS-only manifests by default", async () => { + const tmp = makeTempDir("list-dir-js-disabled"); + tempDirs.push(tmp); + const jsOnlyDir = path.join(tmp, "js-only"); + fs.mkdirSync(jsOnlyDir, { recursive: true }); + fs.writeFileSync( + path.join(jsOnlyDir, "manifest.js"), + `export default ${JSON.stringify(PLUGIN_MANIFEST_JSON)}`, + ); + + const rows = await listFromDirectory(tmp, path.dirname(tmp)); + expect(rows).toEqual([]); + }); + + it("loads JS-only manifests when explicitly enabled", async () => { + const tmp = makeTempDir("list-dir-js-enabled"); + tempDirs.push(tmp); + const jsOnlyDir = path.join(tmp, "js-only"); + fs.mkdirSync(jsOnlyDir, { recursive: true }); + fs.writeFileSync( + path.join(jsOnlyDir, "manifest.js"), + `export default ${JSON.stringify(PLUGIN_MANIFEST_JSON)}`, + ); + + const rows = await listFromDirectory(tmp, path.dirname(tmp), true); + expect(rows).toHaveLength(1); + expect(rows[0].name).toBe("my-feature"); + }); + + it("loads JS manifests from trusted node_modules packages by default", async () => { + const tmp = makeTempDir("list-dir-trusted-node-modules"); + tempDirs.push(tmp); + const pluginDir = path.join( + tmp, + "node_modules", + "@databricks", + "appkit", + "my-feature", + ); + fs.mkdirSync(pluginDir, { recursive: true }); + fs.writeFileSync( + path.join(pluginDir, "manifest.js"), + `export default ${JSON.stringify(PLUGIN_MANIFEST_JSON)}`, + ); + + const rows = await listFromDirectory(tmp, tmp); + expect(rows).toHaveLength(1); + expect(rows[0].name).toBe("my-feature"); + }); + + it("does not load JS manifests from untrusted node_modules packages by default", async () => { + const tmp = makeTempDir("list-dir-untrusted-node-modules"); + tempDirs.push(tmp); + const pluginDir = path.join( + tmp, + "node_modules", + "@acme", + "plugin", + "my-feature", + ); + fs.mkdirSync(pluginDir, { recursive: true }); + fs.writeFileSync( + path.join(pluginDir, "manifest.js"), + `export default ${JSON.stringify(PLUGIN_MANIFEST_JSON)}`, + ); + + const rows = await listFromDirectory(tmp, tmp); + expect(rows).toEqual([]); + }); }); }); diff --git a/packages/shared/src/cli/commands/plugin/list/list.ts b/packages/shared/src/cli/commands/plugin/list/list.ts index 1da5e8af..d9bdc206 100644 --- a/packages/shared/src/cli/commands/plugin/list/list.ts +++ b/packages/shared/src/cli/commands/plugin/list/list.ts @@ -2,8 +2,16 @@ import fs from "node:fs"; import path from "node:path"; import process from "node:process"; import { Command } from "commander"; +import { + loadManifestFromFile, + resolveManifestInDir, +} from "../manifest-resolve"; +import { shouldAllowJsManifestForDir } from "../trusted-js-manifest"; import { validateManifest } from "../validate/validate-manifest"; +/** Safety limit for recursive directory scanning to prevent runaway traversal. */ +const MAX_SCAN_DEPTH = 5; + export interface PluginRow { name: string; displayName: string; @@ -53,40 +61,87 @@ export function listFromManifestFile(manifestPath: string): PluginRow[] { })); } -export function listFromDirectory(dirPath: string, cwd: string): PluginRow[] { +async function collectPluginsRecursive( + dir: string, + cwd: string, + rows: PluginRow[], + allowJsManifest: boolean, + depth = 0, +): Promise { + if ( + !fs.existsSync(dir) || + !fs.statSync(dir).isDirectory() || + depth >= MAX_SCAN_DEPTH + ) + return; + + const entries = fs.readdirSync(dir, { withFileTypes: true }); + for (const entry of entries) { + if (!entry.isDirectory()) continue; + + const childPath = path.join(dir, entry.name); + const allowJsForChild = + allowJsManifest || shouldAllowJsManifestForDir(childPath); + const resolvedManifest = resolveManifestInDir(childPath, { + allowJsManifest: allowJsForChild, + }); + + if (resolvedManifest) { + try { + const obj = await loadManifestFromFile( + resolvedManifest.path, + resolvedManifest.type, + { allowJsManifest: allowJsForChild }, + ); + const result = validateManifest(obj); + const manifest = result.valid ? result.manifest : null; + if (manifest) { + const relPath = path.relative( + cwd, + path.dirname(resolvedManifest.path), + ); + const packagePath = relPath.startsWith(".") + ? relPath + : `./${relPath}`; + rows.push({ + name: manifest.name, + displayName: manifest.displayName ?? manifest.name, + package: packagePath, + required: Array.isArray(manifest.resources?.required) + ? manifest.resources.required.length + : 0, + optional: Array.isArray(manifest.resources?.optional) + ? manifest.resources.optional.length + : 0, + }); + } + } catch { + // skip invalid manifests + } + continue; + } + + await collectPluginsRecursive( + childPath, + cwd, + rows, + allowJsManifest, + depth + 1, + ); + } +} + +export async function listFromDirectory( + dirPath: string, + cwd: string, + allowJsManifest = false, +): Promise { const resolved = path.resolve(cwd, dirPath); if (!fs.existsSync(resolved) || !fs.statSync(resolved).isDirectory()) { return []; } - const entries = fs.readdirSync(resolved, { withFileTypes: true }); const rows: PluginRow[] = []; - for (const entry of entries) { - if (!entry.isDirectory()) continue; - const manifestPath = path.join(resolved, entry.name, "manifest.json"); - if (!fs.existsSync(manifestPath)) continue; - try { - const raw = fs.readFileSync(manifestPath, "utf-8"); - const obj = JSON.parse(raw); - const result = validateManifest(obj); - const manifest = result.valid ? result.manifest : null; - if (!manifest) continue; - const relPath = path.relative(cwd, path.dirname(manifestPath)); - const packagePath = relPath.startsWith(".") ? relPath : `./${relPath}`; - rows.push({ - name: manifest.name, - displayName: manifest.displayName ?? manifest.name, - package: packagePath, - required: Array.isArray(manifest.resources?.required) - ? manifest.resources.required.length - : 0, - optional: Array.isArray(manifest.resources?.optional) - ? manifest.resources.optional.length - : 0, - }); - } catch { - // skip invalid manifests - } - } + await collectPluginsRecursive(resolved, cwd, rows, allowJsManifest); return rows; } @@ -120,19 +175,26 @@ function printTable(rows: PluginRow[]): void { } } -function runPluginList(options: { +async function runPluginList(options: { manifest?: string; dir?: string; json?: boolean; -}): void { + allowJsManifest?: boolean; +}): Promise { const cwd = process.cwd(); + const allowJsManifest = Boolean(options.allowJsManifest); + if (allowJsManifest) { + console.warn( + "Warning: --allow-js-manifest executes manifest.js/manifest.cjs files. Only use with trusted code.", + ); + } let rows: PluginRow[]; if (options.dir !== undefined) { - rows = listFromDirectory(options.dir, cwd); + rows = await listFromDirectory(options.dir, cwd, allowJsManifest); if (rows.length === 0 && options.dir) { console.error( - `No plugin directories with manifest.json found in ${options.dir}`, + `No plugin directories with ${allowJsManifest ? "manifest.json or manifest.js" : "manifest.json"} found in ${options.dir}`, ); process.exit(1); } @@ -169,7 +231,16 @@ export const pluginListCommand = new Command("list") ) .option( "-d, --dir ", - "Scan directory for plugin folders (each with manifest.json)", + "Scan directory recursively for plugin folders (manifest.json by default)", + ) + .option( + "--allow-js-manifest", + "Allow reading manifest.js/manifest.cjs (executes code; use only with trusted plugins)", ) .option("--json", "Output as JSON") - .action(runPluginList); + .action((opts) => + runPluginList(opts).catch((err) => { + console.error(err); + process.exit(1); + }), + ); diff --git a/packages/shared/src/cli/commands/plugin/manifest-resolve.test.ts b/packages/shared/src/cli/commands/plugin/manifest-resolve.test.ts new file mode 100644 index 00000000..69a1a868 --- /dev/null +++ b/packages/shared/src/cli/commands/plugin/manifest-resolve.test.ts @@ -0,0 +1,192 @@ +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { afterEach, describe, expect, it } from "vitest"; +import { loadManifestFromFile, resolveManifestInDir } from "./manifest-resolve"; + +function makeTempDir(prefix: string): string { + return fs.mkdtempSync(path.join(os.tmpdir(), `${prefix}-`)); +} + +function cleanDir(dir: string): void { + try { + fs.rmSync(dir, { recursive: true, force: true }); + } catch { + // best effort + } +} + +const SAMPLE_MANIFEST = { + name: "test-plugin", + displayName: "Test Plugin", + description: "A test", + resources: { required: [], optional: [] }, +}; + +describe("manifest-resolve", () => { + const tempDirs: string[] = []; + + afterEach(() => { + for (const dir of tempDirs) cleanDir(dir); + tempDirs.length = 0; + }); + + describe("resolveManifestInDir", () => { + it("returns manifest.json when present", () => { + const dir = makeTempDir("resolve-json"); + tempDirs.push(dir); + fs.writeFileSync( + path.join(dir, "manifest.json"), + JSON.stringify(SAMPLE_MANIFEST), + ); + + const result = resolveManifestInDir(dir); + expect(result).not.toBeNull(); + expect(result?.path).toContain("manifest.json"); + expect(result?.type).toBe("json"); + }); + + it("prefers manifest.json over manifest.js even when JS is allowed", () => { + const dir = makeTempDir("resolve-order"); + tempDirs.push(dir); + fs.writeFileSync( + path.join(dir, "manifest.json"), + JSON.stringify(SAMPLE_MANIFEST), + ); + fs.writeFileSync( + path.join(dir, "manifest.js"), + `export default ${JSON.stringify(SAMPLE_MANIFEST)}`, + ); + + const result = resolveManifestInDir(dir, { allowJsManifest: true }); + expect(result?.path).toContain("manifest.json"); + expect(result?.type).toBe("json"); + }); + + it("returns null for JS-only plugin when JS manifests are disabled", () => { + const dir = makeTempDir("resolve-js"); + tempDirs.push(dir); + fs.writeFileSync( + path.join(dir, "manifest.js"), + `export default ${JSON.stringify(SAMPLE_MANIFEST)}`, + ); + + const result = resolveManifestInDir(dir); + expect(result).toBeNull(); + }); + + it("returns manifest.js when manifest.json is absent and JS is allowed", () => { + const dir = makeTempDir("resolve-js-allowed"); + tempDirs.push(dir); + fs.writeFileSync( + path.join(dir, "manifest.js"), + `export default ${JSON.stringify(SAMPLE_MANIFEST)}`, + ); + + const result = resolveManifestInDir(dir, { allowJsManifest: true }); + expect(result).not.toBeNull(); + expect(result?.path).toContain("manifest.js"); + expect(result?.type).toBe("js"); + }); + + it("returns null when no manifest file exists", () => { + const dir = makeTempDir("resolve-none"); + tempDirs.push(dir); + + const result = resolveManifestInDir(dir); + expect(result).toBeNull(); + }); + }); + + describe("loadManifestFromFile", () => { + it("loads JSON manifest", async () => { + const dir = makeTempDir("load-json"); + tempDirs.push(dir); + const manifestPath = path.join(dir, "manifest.json"); + fs.writeFileSync(manifestPath, JSON.stringify(SAMPLE_MANIFEST)); + + const loaded = await loadManifestFromFile(manifestPath, "json"); + expect(loaded).toEqual(SAMPLE_MANIFEST); + }); + + it("returns object for JSON path when type is json", async () => { + const dir = makeTempDir("load-json-path"); + tempDirs.push(dir); + const manifestPath = path.join(dir, "custom.json"); + fs.writeFileSync(manifestPath, JSON.stringify(SAMPLE_MANIFEST)); + + const loaded = await loadManifestFromFile(manifestPath, "json"); + expect(loaded).toEqual(SAMPLE_MANIFEST); + }); + + it("throws for JS manifest when JS loading is disabled", async () => { + const dir = makeTempDir("load-js-disabled"); + tempDirs.push(dir); + const manifestPath = path.join(dir, "manifest.js"); + fs.writeFileSync( + manifestPath, + `export default ${JSON.stringify(SAMPLE_MANIFEST)};`, + ); + + await expect(loadManifestFromFile(manifestPath, "js")).rejects.toThrow( + /Refusing to execute JS manifest/, + ); + }); + + it("loads ESM manifest.js via dynamic import when JS is allowed", async () => { + const dir = makeTempDir("load-esm"); + tempDirs.push(dir); + const manifestPath = path.join(dir, "manifest.js"); + fs.writeFileSync( + manifestPath, + `export default ${JSON.stringify(SAMPLE_MANIFEST)};`, + ); + + const loaded = await loadManifestFromFile(manifestPath, "js", { + allowJsManifest: true, + }); + expect(loaded).toEqual(SAMPLE_MANIFEST); + }); + + it("loads CJS manifest.cjs via require when JS is allowed", async () => { + const dir = makeTempDir("load-cjs"); + tempDirs.push(dir); + const manifestPath = path.join(dir, "manifest.cjs"); + fs.writeFileSync( + manifestPath, + `module.exports = ${JSON.stringify(SAMPLE_MANIFEST)};`, + ); + + const loaded = await loadManifestFromFile(manifestPath, "js", { + allowJsManifest: true, + }); + expect(loaded).toEqual(SAMPLE_MANIFEST); + }); + + it("loads CJS manifest.cjs that uses module.exports.default when JS is allowed", async () => { + const dir = makeTempDir("load-cjs-default"); + tempDirs.push(dir); + const manifestPath = path.join(dir, "manifest.cjs"); + fs.writeFileSync( + manifestPath, + `module.exports.default = ${JSON.stringify(SAMPLE_MANIFEST)};`, + ); + + const loaded = await loadManifestFromFile(manifestPath, "js", { + allowJsManifest: true, + }); + expect(loaded).toEqual(SAMPLE_MANIFEST); + }); + + it("throws on malformed JSON", async () => { + const dir = makeTempDir("load-bad-json"); + tempDirs.push(dir); + const manifestPath = path.join(dir, "manifest.json"); + fs.writeFileSync(manifestPath, "{ not valid json }"); + + await expect( + loadManifestFromFile(manifestPath, "json"), + ).rejects.toThrow(); + }); + }); +}); diff --git a/packages/shared/src/cli/commands/plugin/manifest-resolve.ts b/packages/shared/src/cli/commands/plugin/manifest-resolve.ts new file mode 100644 index 00000000..40093720 --- /dev/null +++ b/packages/shared/src/cli/commands/plugin/manifest-resolve.ts @@ -0,0 +1,91 @@ +import fs from "node:fs"; +import { createRequire } from "node:module"; +import path from "node:path"; +import { pathToFileURL } from "node:url"; + +const MANIFEST_JSON = "manifest.json"; +const MANIFEST_JS = "manifest.js"; +const MANIFEST_CJS = "manifest.cjs"; + +/** Resolution order for manifest files in a plugin directory. */ +const MANIFEST_CANDIDATES = [MANIFEST_JSON, MANIFEST_JS, MANIFEST_CJS] as const; + +export type ManifestFileType = "json" | "js"; + +export interface ResolvedManifest { + /** Absolute path to the manifest file */ + path: string; + /** How to load it: JSON (read + parse) or JS (dynamic import / require) */ + type: ManifestFileType; +} + +export interface ManifestLoadOptions { + /** + * Allow loading JS manifests via import/require. + * Disabled by default to avoid executing untrusted code. + */ + allowJsManifest?: boolean; +} + +/** + * Resolve the manifest file in a plugin directory. + * By default tries only manifest.json. If allowJsManifest=true, then also + * tries manifest.js and manifest.cjs. + * + * @param pluginDir - Absolute path to the plugin directory + * @returns The resolved file path and type, or null if none found + */ +export function resolveManifestInDir( + pluginDir: string, + options: ManifestLoadOptions = {}, +): ResolvedManifest | null { + const candidates = options.allowJsManifest + ? MANIFEST_CANDIDATES + : [MANIFEST_JSON]; + for (const name of candidates) { + const manifestPath = path.join(pluginDir, name); + if (fs.existsSync(manifestPath) && fs.statSync(manifestPath).isFile()) { + return { + path: path.resolve(manifestPath), + type: name === MANIFEST_JSON ? "json" : "js", + }; + } + } + return null; +} + +/** + * Load a manifest from a file (JSON or JS). + * JSON: read and parse. JS: dynamic import (ESM) or require (CJS); the module must default-export the manifest object. + * + * @param manifestPath - Absolute path to manifest.json or manifest.js/.cjs + * @param type - "json" or "js" + * @returns The parsed manifest object (caller should validate with schema) + */ +export async function loadManifestFromFile( + manifestPath: string, + type: ManifestFileType, + options: ManifestLoadOptions = {}, +): Promise { + if (type === "json") { + const raw = fs.readFileSync(manifestPath, "utf-8"); + return JSON.parse(raw) as unknown; + } + + if (!options.allowJsManifest) { + throw new Error( + `Refusing to execute JS manifest at ${manifestPath}. Pass --allow-js-manifest to opt in.`, + ); + } + + const ext = path.extname(manifestPath).toLowerCase(); + if (ext === ".cjs") { + const require = createRequire(import.meta.url); + const mod = require(manifestPath); + return mod?.default ?? mod; + } + + const url = pathToFileURL(manifestPath).href; + const mod = await import(url); + return mod?.default ?? mod; +} diff --git a/packages/shared/src/cli/commands/plugin/sync/sync.test.ts b/packages/shared/src/cli/commands/plugin/sync/sync.test.ts index 737dc246..64eec572 100644 --- a/packages/shared/src/cli/commands/plugin/sync/sync.test.ts +++ b/packages/shared/src/cli/commands/plugin/sync/sync.test.ts @@ -1,7 +1,12 @@ import path from "node:path"; import { Lang, parse } from "@ast-grep/napi"; import { describe, expect, it } from "vitest"; -import { isWithinDirectory, parseImports, parsePluginUsages } from "./sync"; +import { + isWithinDirectory, + parseImports, + parsePluginUsages, + shouldAllowJsManifestForPackage, +} from "./sync"; describe("plugin sync", () => { describe("isWithinDirectory", () => { @@ -163,4 +168,18 @@ describe("plugin sync", () => { expect(Array.from(used)).toEqual(["server"]); }); }); + + describe("shouldAllowJsManifestForPackage", () => { + it("allows trusted Databricks package scopes", () => { + expect(shouldAllowJsManifestForPackage("@databricks/appkit")).toBe(true); + expect(shouldAllowJsManifestForPackage("@databricks/custom-plugin")).toBe( + true, + ); + }); + + it("rejects untrusted package scopes by default", () => { + expect(shouldAllowJsManifestForPackage("my-plugin")).toBe(false); + expect(shouldAllowJsManifestForPackage("@acme/plugin")).toBe(false); + }); + }); }); diff --git a/packages/shared/src/cli/commands/plugin/sync/sync.ts b/packages/shared/src/cli/commands/plugin/sync/sync.ts index 0e041071..b553c45a 100644 --- a/packages/shared/src/cli/commands/plugin/sync/sync.ts +++ b/packages/shared/src/cli/commands/plugin/sync/sync.ts @@ -2,11 +2,17 @@ import fs from "node:fs"; import path from "node:path"; import { Lang, parse, type SgNode } from "@ast-grep/napi"; import { Command } from "commander"; +import { + loadManifestFromFile, + type ResolvedManifest, + resolveManifestInDir, +} from "../manifest-resolve"; import type { PluginManifest, TemplatePlugin, TemplatePluginsManifest, } from "../manifest-types"; +import { shouldAllowJsManifestForPackage } from "../trusted-js-manifest"; import { formatValidationErrors, validateManifest, @@ -48,6 +54,40 @@ function validateManifestWithSchema( return null; } +/** Safety limit for recursive directory scanning to prevent runaway traversal. */ +const MAX_SCAN_DEPTH = 5; + +/** + * Load and validate a resolved manifest, returning a TemplatePlugin entry or null. + * Centralises the resolve → load → validate → build-entry pipeline used by + * multiple discovery functions. + */ +async function loadPluginEntry( + resolved: ResolvedManifest, + pkg: string, + allowJsManifest: boolean, +): Promise<[string, TemplatePlugin] | null> { + const parsed = await loadManifestFromFile(resolved.path, resolved.type, { + allowJsManifest, + }); + const manifest = validateManifestWithSchema(parsed, resolved.path); + if (!manifest || manifest.hidden) return null; + + return [ + manifest.name, + { + name: manifest.name, + displayName: manifest.displayName, + description: manifest.description, + package: pkg, + resources: manifest.resources, + ...(manifest.onSetupMessage && { + onSetupMessage: manifest.onSetupMessage, + }), + }, + ]; +} + /** * Known packages that may contain AppKit plugins. * Always scanned for manifests, even if not imported in the server file. @@ -60,6 +100,13 @@ const KNOWN_PLUGIN_PACKAGES = ["@databricks/appkit"]; */ const SERVER_FILE_CANDIDATES = ["server/server.ts", "server/index.ts"]; +/** + * Conventional directories to scan for local plugin manifests when + * --local-plugins-dir is not set. Checked in order; each that exists is scanned. + * Plugins found here are added to the manifest even if not imported in the server. + */ +const CONVENTIONAL_LOCAL_PLUGIN_DIRS = ["plugins", "server"]; + /** * Find the server entry file by checking candidate paths in order. * @@ -182,23 +229,25 @@ function parsePluginUsages(root: SgNode): Set { const RESOLVE_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx"]; /** - * Resolve a relative import source to the plugin directory containing a manifest.json. - * Follows the convention that plugins live in their own directory with a manifest.json. + * Resolve a relative import source to the plugin directory containing a manifest + * (manifest.json or manifest.js). Follows the convention that plugins live in + * their own directory with a manifest file. * * Resolution strategy: - * 1. If the import path is a directory, look for manifest.json directly in it - * 2. If the import path + extension is a file, look for manifest.json in its parent directory - * 3. If the import path is a directory with an index file, look for manifest.json in that directory + * 1. If the import path is a directory, look for manifest.json/js in it + * 2. If the import path + extension is a file, look for manifest in its parent directory + * 3. If the import path is a directory with an index file, look for manifest in that directory * * @param importSource - The relative import specifier (e.g. "./plugins/my-plugin") * @param serverFileDir - Absolute path to the directory containing the server file - * @returns Absolute path to manifest.json, or null if not found + * @returns Resolved manifest file path and type, or null if not found */ function resolveLocalManifest( importSource: string, serverFileDir: string, + allowJsManifest: boolean, projectRoot?: string, -): string | null { +): ResolvedManifest | null { const resolved = path.resolve(serverFileDir, importSource); // Security: Reject paths that escape the project root @@ -210,34 +259,26 @@ function resolveLocalManifest( return null; } - // Case 1: Import path is a directory with manifest.json - // e.g. ./plugins/my-plugin → ./plugins/my-plugin/manifest.json + // Case 1: Import path is a directory if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) { - const manifestPath = path.join(resolved, "manifest.json"); - if (fs.existsSync(manifestPath)) return manifestPath; + return resolveManifestInDir(resolved, { allowJsManifest }); } - // Case 2: Import path + extension resolves to a file - // e.g. ./plugins/my-plugin → ./plugins/my-plugin.ts - // Look for manifest.json in the same directory + // Case 2: Import path + extension resolves to a file — manifest in parent dir for (const ext of RESOLVE_EXTENSIONS) { const filePath = `${resolved}${ext}`; if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) { const dir = path.dirname(filePath); - const manifestPath = path.join(dir, "manifest.json"); - if (fs.existsSync(manifestPath)) return manifestPath; - break; + if (!isWithinDirectory(dir, boundary)) return null; + return resolveManifestInDir(dir, { allowJsManifest }); } } // Case 3: Import path is a directory with an index file - // e.g. ./plugins/my-plugin → ./plugins/my-plugin/index.ts for (const ext of RESOLVE_EXTENSIONS) { const indexPath = path.join(resolved, `index${ext}`); if (fs.existsSync(indexPath)) { - const manifestPath = path.join(resolved, "manifest.json"); - if (fs.existsSync(manifestPath)) return manifestPath; - break; + return resolveManifestInDir(resolved, { allowJsManifest }); } } @@ -246,45 +287,41 @@ function resolveLocalManifest( /** * Discover plugin manifests from local (relative) imports in the server file. - * Resolves each relative import to a directory and looks for manifest.json. + * Resolves each relative import to a directory and loads manifest.json or manifest.js. * * @param relativeImports - Parsed imports with relative sources (starting with . or /) * @param serverFileDir - Absolute path to the directory containing the server file * @param cwd - Current working directory (for computing relative paths in output) * @returns Map of plugin name to template plugin entry for local plugins */ -function discoverLocalPlugins( +async function discoverLocalPlugins( relativeImports: ParsedImport[], serverFileDir: string, cwd: string, -): TemplatePluginsManifest["plugins"] { + allowJsManifest: boolean, +): Promise { const plugins: TemplatePluginsManifest["plugins"] = {}; for (const imp of relativeImports) { - const manifestPath = resolveLocalManifest(imp.source, serverFileDir, cwd); - if (!manifestPath) continue; + const resolved = resolveLocalManifest( + imp.source, + serverFileDir, + allowJsManifest, + cwd, + ); + if (!resolved) continue; try { - const content = fs.readFileSync(manifestPath, "utf-8"); - const parsed = JSON.parse(content); - const manifest = validateManifestWithSchema(parsed, manifestPath); - if (!manifest || manifest.hidden) continue; - - const relativePath = path.relative(cwd, path.dirname(manifestPath)); - - plugins[manifest.name] = { - name: manifest.name, - displayName: manifest.displayName, - description: manifest.description, - package: `./${relativePath}`, - resources: manifest.resources, - ...(manifest.onSetupMessage && { - onSetupMessage: manifest.onSetupMessage, - }), - }; + const relativePath = path.relative(cwd, path.dirname(resolved.path)); + const entry = await loadPluginEntry( + resolved, + `./${relativePath}`, + allowJsManifest, + ); + if (entry) plugins[entry[0]] = entry[1]; } catch (error) { console.warn( - `Warning: Failed to parse manifest at ${manifestPath}:`, + `Warning: Failed to load manifest at ${resolved.path}:`, error instanceof Error ? error.message : error, ); } @@ -295,12 +332,15 @@ function discoverLocalPlugins( /** * Discover plugin manifests from a package's dist folder. - * Looks for manifest.json files in dist/plugins/{plugin-name}/ directories. + * Looks for manifest.json or manifest.js in dist/plugins/{plugin-name}/ directories. * * @param packagePath - Path to the package in node_modules * @returns Array of plugin manifests found in the package */ -function discoverPluginManifests(packagePath: string): PluginManifest[] { +async function discoverPluginManifests( + packagePath: string, + allowJsManifest: boolean, +): Promise { const pluginsDir = path.join(packagePath, "dist", "plugins"); const manifests: PluginManifest[] = []; @@ -310,23 +350,25 @@ function discoverPluginManifests(packagePath: string): PluginManifest[] { const entries = fs.readdirSync(pluginsDir, { withFileTypes: true }); for (const entry of entries) { - if (entry.isDirectory()) { - const manifestPath = path.join(pluginsDir, entry.name, "manifest.json"); - if (fs.existsSync(manifestPath)) { - try { - const content = fs.readFileSync(manifestPath, "utf-8"); - const parsed = JSON.parse(content); - const manifest = validateManifestWithSchema(parsed, manifestPath); - if (manifest) { - manifests.push(manifest); - } - } catch (error) { - console.warn( - `Warning: Failed to parse manifest at ${manifestPath}:`, - error instanceof Error ? error.message : error, - ); - } + if (!entry.isDirectory()) continue; + const resolved = resolveManifestInDir(path.join(pluginsDir, entry.name), { + allowJsManifest, + }); + if (!resolved) continue; + + try { + const parsed = await loadManifestFromFile(resolved.path, resolved.type, { + allowJsManifest, + }); + const manifest = validateManifestWithSchema(parsed, resolved.path); + if (manifest) { + manifests.push(manifest); } + } catch (error) { + console.warn( + `Warning: Failed to load manifest at ${resolved.path}:`, + error instanceof Error ? error.message : error, + ); } } @@ -340,10 +382,11 @@ function discoverPluginManifests(packagePath: string): PluginManifest[] { * @param packages - Set of npm package names to scan for plugin manifests * @returns Map of plugin name to template plugin entry */ -function scanForPlugins( +async function scanForPlugins( cwd: string, packages: Iterable, -): TemplatePluginsManifest["plugins"] { + allowJsManifest: boolean, +): Promise { const plugins: TemplatePluginsManifest["plugins"] = {}; for (const packageName of packages) { @@ -352,7 +395,13 @@ function scanForPlugins( continue; } - const manifests = discoverPluginManifests(packagePath); + const allowJsForPackage = + allowJsManifest || shouldAllowJsManifestForPackage(packageName); + + const manifests = await discoverPluginManifests( + packagePath, + allowJsForPackage, + ); for (const manifest of manifests) { if (manifest.hidden) continue; plugins[manifest.name] = { @@ -364,26 +413,78 @@ function scanForPlugins( ...(manifest.onSetupMessage && { onSetupMessage: manifest.onSetupMessage, }), - }; + } satisfies TemplatePlugin; + } + } + + return plugins; +} + +/** + * Recursively scan a directory for plugin manifests. Any directory that + * contains manifest.json or manifest.js is treated as a plugin root; we do + * not descend into that directory's children. Used for local plugins discovery + * so nested paths like server/plugins/category/my-plugin are found. + */ +async function scanPluginsDirRecursive( + dir: string, + cwd: string, + allowJsManifest: boolean, + depth = 0, +): Promise { + const plugins: TemplatePluginsManifest["plugins"] = {}; + if (!fs.existsSync(dir) || depth >= MAX_SCAN_DEPTH) return plugins; + + const entries = fs.readdirSync(dir, { withFileTypes: true }); + for (const entry of entries) { + if (!entry.isDirectory()) continue; + + const pluginDir = path.join(dir, entry.name); + const resolved = resolveManifestInDir(pluginDir, { allowJsManifest }); + + if (resolved) { + const pkg = `./${path.relative(cwd, pluginDir)}`; + try { + const pluginEntry = await loadPluginEntry( + resolved, + pkg, + allowJsManifest, + ); + if (pluginEntry) plugins[pluginEntry[0]] = pluginEntry[1]; + } catch (error) { + console.warn( + `Warning: Failed to load manifest at ${resolved.path}:`, + error instanceof Error ? error.message : error, + ); + } + continue; } + + Object.assign( + plugins, + await scanPluginsDirRecursive(pluginDir, cwd, allowJsManifest, depth + 1), + ); } return plugins; } /** - * Scan a directory for plugin manifests in direct subdirectories. - * Each subdirectory is expected to contain a manifest.json file. + * Scan a directory for plugin manifests in direct subdirectories only. + * Each subdirectory may contain manifest.json or manifest.js. * Used with --plugins-dir to discover plugins from source instead of node_modules. * * @param dir - Absolute path to the directory containing plugin subdirectories - * @param packageName - Package name to assign to discovered plugins + * @param packageName - Package name to assign to discovered plugins (used when cwd is not set) + * @param cwd - When set, each plugin's package is set to ./, e.g. ./server/my-plugin * @returns Map of plugin name to template plugin entry */ -function scanPluginsDir( +async function scanPluginsDir( dir: string, packageName: string, -): TemplatePluginsManifest["plugins"] { + allowJsManifest: boolean, + cwd?: string, +): Promise { const plugins: TemplatePluginsManifest["plugins"] = {}; if (!fs.existsSync(dir)) return plugins; @@ -392,28 +493,19 @@ function scanPluginsDir( for (const entry of entries) { if (!entry.isDirectory()) continue; - const manifestPath = path.join(dir, entry.name, "manifest.json"); - if (!fs.existsSync(manifestPath)) continue; + const pluginDir = path.join(dir, entry.name); + const resolved = resolveManifestInDir(pluginDir, { allowJsManifest }); + if (!resolved) continue; + + const pkg = + cwd !== undefined ? `./${path.relative(cwd, pluginDir)}` : packageName; try { - const content = fs.readFileSync(manifestPath, "utf-8"); - const parsed = JSON.parse(content); - const manifest = validateManifestWithSchema(parsed, manifestPath); - if (manifest && !manifest.hidden) { - plugins[manifest.name] = { - name: manifest.name, - displayName: manifest.displayName, - description: manifest.description, - package: packageName, - resources: manifest.resources, - ...(manifest.onSetupMessage && { - onSetupMessage: manifest.onSetupMessage, - }), - }; - } + const pluginEntry = await loadPluginEntry(resolved, pkg, allowJsManifest); + if (pluginEntry) plugins[pluginEntry[0]] = pluginEntry[1]; } catch (error) { console.warn( - `Warning: Failed to parse manifest at ${manifestPath}:`, + `Warning: Failed to load manifest at ${resolved.path}:`, error instanceof Error ? error.message : error, ); } @@ -461,15 +553,18 @@ function writeManifest( * manifests, then marks plugins that are actually used in the `plugins: [...]` * array as requiredByTemplate. */ -function runPluginsSync(options: { +async function runPluginsSync(options: { write?: boolean; output?: string; silent?: boolean; requirePlugins?: string; pluginsDir?: string; packageName?: string; -}) { + localPluginsDir?: string; + allowJsManifest?: boolean; +}): Promise { const cwd = process.cwd(); + const allowJsManifest = Boolean(options.allowJsManifest); const outputPath = path.resolve(cwd, options.output || "appkit.plugins.json"); // Security: Reject output paths that escape the project root @@ -482,6 +577,11 @@ function runPluginsSync(options: { if (!options.silent) { console.log("Scanning for AppKit plugins...\n"); + if (allowJsManifest) { + console.warn( + "Warning: --allow-js-manifest executes manifest.js/manifest.cjs files. Only use with trusted code.", + ); + } } // Step 1: Parse server file to discover imports and plugin usages @@ -526,22 +626,56 @@ function runPluginsSync(options: { if (!options.silent) { console.log(`Scanning plugins directory: ${options.pluginsDir}`); } - Object.assign(plugins, scanPluginsDir(resolvedDir, pkgName)); + Object.assign( + plugins, + await scanPluginsDir(resolvedDir, pkgName, allowJsManifest), + ); } else { const npmPackages = new Set([ ...KNOWN_PLUGIN_PACKAGES, ...npmImports.map((i) => i.source), ]); - Object.assign(plugins, scanForPlugins(cwd, npmPackages)); + Object.assign( + plugins, + await scanForPlugins(cwd, npmPackages, allowJsManifest), + ); } // Step 4: Discover local plugin manifests from relative imports if (serverFile && localImports.length > 0) { const serverFileDir = path.dirname(serverFile); - const localPlugins = discoverLocalPlugins(localImports, serverFileDir, cwd); + const localPlugins = await discoverLocalPlugins( + localImports, + serverFileDir, + cwd, + allowJsManifest, + ); Object.assign(plugins, localPlugins); } + // Step 4b: Discover local plugins from conventional directory (or --local-plugins-dir). + // These are included even when not imported in the server. + const localDirsToScan: string[] = options.localPluginsDir + ? [options.localPluginsDir] + : CONVENTIONAL_LOCAL_PLUGIN_DIRS.filter((d) => + fs.existsSync(path.join(cwd, d)), + ); + for (const dir of localDirsToScan) { + const resolvedDir = path.resolve(cwd, dir); + if (!fs.existsSync(resolvedDir)) continue; + if (!options.silent) { + console.log(`Scanning local plugins directory: ${dir}`); + } + const discovered = await scanPluginsDirRecursive( + resolvedDir, + cwd, + allowJsManifest, + ); + for (const [name, entry] of Object.entries(discovered)) { + if (!plugins[name]) plugins[name] = entry; + } + } + const pluginCount = Object.keys(plugins).length; if (pluginCount === 0) { @@ -551,7 +685,9 @@ function runPluginsSync(options: { } console.log("No plugins found."); if (options.pluginsDir) { - console.log(`\nNo manifest.json files found in: ${options.pluginsDir}`); + console.log( + `\nNo manifest (${allowJsManifest ? "manifest.json or manifest.js" : "manifest.json"}) found in: ${options.pluginsDir}`, + ); } else { console.log("\nMake sure you have plugin packages installed."); } @@ -625,8 +761,13 @@ function runPluginsSync(options: { writeManifest(outputPath, { plugins }, options); } -/** Exported for testing: path boundary check, AST parsing. */ -export { isWithinDirectory, parseImports, parsePluginUsages }; +/** Exported for testing: path boundary check, AST parsing, trust checks. */ +export { + isWithinDirectory, + parseImports, + parsePluginUsages, + shouldAllowJsManifestForPackage, +}; export const pluginsSyncCommand = new Command("sync") .description( @@ -653,4 +794,17 @@ export const pluginsSyncCommand = new Command("sync") "--package-name ", "Package name to assign to plugins found via --plugins-dir (default: @databricks/appkit)", ) - .action(runPluginsSync); + .option( + "--local-plugins-dir ", + "Also scan this directory for local plugin manifests (default: plugins, server)", + ) + .option( + "--allow-js-manifest", + "Allow reading manifest.js/manifest.cjs (executes code; use only with trusted plugins)", + ) + .action((opts) => + runPluginsSync(opts).catch((err) => { + console.error(err); + process.exit(1); + }), + ); diff --git a/packages/shared/src/cli/commands/plugin/trusted-js-manifest.ts b/packages/shared/src/cli/commands/plugin/trusted-js-manifest.ts new file mode 100644 index 00000000..1787f518 --- /dev/null +++ b/packages/shared/src/cli/commands/plugin/trusted-js-manifest.ts @@ -0,0 +1,32 @@ +import path from "node:path"; + +export const TRUSTED_JS_MANIFEST_PACKAGE_PREFIXES = ["@databricks/"]; + +export function shouldAllowJsManifestForPackage(packageName: string): boolean { + return TRUSTED_JS_MANIFEST_PACKAGE_PREFIXES.some((prefix) => + packageName.startsWith(prefix), + ); +} + +export function getNodeModulesPackageName(filePath: string): string | null { + const parts = path.resolve(filePath).split(path.sep).filter(Boolean); + const nodeModulesIndex = parts.lastIndexOf("node_modules"); + if (nodeModulesIndex === -1 || nodeModulesIndex + 1 >= parts.length) { + return null; + } + + const maybeScope = parts[nodeModulesIndex + 1]; + if (maybeScope.startsWith("@")) { + const packageName = parts[nodeModulesIndex + 2]; + if (!packageName) return null; + return `${maybeScope}/${packageName}`; + } + + return maybeScope; +} + +export function shouldAllowJsManifestForDir(dirPath: string): boolean { + const packageName = getNodeModulesPackageName(dirPath); + if (!packageName) return false; + return shouldAllowJsManifestForPackage(packageName); +} diff --git a/packages/shared/src/cli/commands/plugin/validate/validate.ts b/packages/shared/src/cli/commands/plugin/validate/validate.ts index 12a44b1e..76ccfbae 100644 --- a/packages/shared/src/cli/commands/plugin/validate/validate.ts +++ b/packages/shared/src/cli/commands/plugin/validate/validate.ts @@ -2,6 +2,11 @@ import fs from "node:fs"; import path from "node:path"; import process from "node:process"; import { Command } from "commander"; +import { + loadManifestFromFile, + type ResolvedManifest, + resolveManifestInDir, +} from "../manifest-resolve"; import { detectSchemaType, formatValidationErrors, @@ -9,8 +14,12 @@ import { validateTemplateManifest, } from "./validate-manifest"; -function resolveManifestPaths(paths: string[], cwd: string): string[] { - const out: string[] = []; +function resolveManifestPaths( + paths: string[], + cwd: string, + allowJsManifest: boolean, +): ResolvedManifest[] { + const out: ResolvedManifest[] = []; for (const p of paths) { const resolved = path.resolve(cwd, p); if (!fs.existsSync(resolved)) { @@ -19,33 +28,54 @@ function resolveManifestPaths(paths: string[], cwd: string): string[] { } const stat = fs.statSync(resolved); if (stat.isDirectory()) { - const pluginManifest = path.join(resolved, "manifest.json"); - const templateManifest = path.join(resolved, "appkit.plugins.json"); let found = false; - if (fs.existsSync(pluginManifest)) { - out.push(pluginManifest); + const pluginResolved = resolveManifestInDir(resolved, { + allowJsManifest, + }); + if (pluginResolved) { + out.push(pluginResolved); found = true; } + const templateManifest = path.join(resolved, "appkit.plugins.json"); if (fs.existsSync(templateManifest)) { - out.push(templateManifest); + out.push({ path: templateManifest, type: "json" }); found = true; } if (!found) { console.error( - `No manifest.json or appkit.plugins.json in directory: ${p}`, + `No ${allowJsManifest ? "manifest.json, manifest.js, or" : "manifest.json or"} appkit.plugins.json in directory: ${p}`, ); } } else { - out.push(resolved); + const ext = path.extname(resolved).toLowerCase(); + if (!allowJsManifest && (ext === ".js" || ext === ".cjs")) { + console.error( + `JS manifest provided but disabled by default: ${p}. Re-run with --allow-js-manifest to opt in.`, + ); + continue; + } + out.push({ + path: resolved, + type: ext === ".js" || ext === ".cjs" ? "js" : "json", + }); } } return out; } -function runPluginValidate(paths: string[]): void { +async function runPluginValidate( + paths: string[], + options: { allowJsManifest?: boolean }, +): Promise { const cwd = process.cwd(); + const allowJsManifest = Boolean(options.allowJsManifest); + if (allowJsManifest) { + console.warn( + "Warning: --allow-js-manifest executes manifest.js/manifest.cjs files. Only use with trusted code.", + ); + } const toValidate = paths.length > 0 ? paths : ["."]; - const manifestPaths = resolveManifestPaths(toValidate, cwd); + const manifestPaths = resolveManifestPaths(toValidate, cwd, allowJsManifest); if (manifestPaths.length === 0) { console.error("No manifest files to validate."); @@ -53,11 +83,10 @@ function runPluginValidate(paths: string[]): void { } let hasFailure = false; - for (const manifestPath of manifestPaths) { + for (const { path: manifestPath, type } of manifestPaths) { let obj: unknown; try { - const raw = fs.readFileSync(manifestPath, "utf-8"); - obj = JSON.parse(raw); + obj = await loadManifestFromFile(manifestPath, type, { allowJsManifest }); } catch (err) { console.error(`✗ ${manifestPath}`); console.error(` ${err instanceof Error ? err.message : String(err)}`); @@ -92,6 +121,15 @@ export const pluginValidateCommand = new Command("validate") ) .argument( "[paths...]", - "Paths to manifest.json, appkit.plugins.json, or plugin directories (default: .)", + "Paths to manifest.json or appkit.plugins.json (or plugin directories); use --allow-js-manifest to include manifest.js", + ) + .option( + "--allow-js-manifest", + "Allow reading manifest.js/manifest.cjs (executes code; use only with trusted plugins)", ) - .action(runPluginValidate); + .action((paths: string[], opts: { allowJsManifest?: boolean }) => + runPluginValidate(paths, opts).catch((err) => { + console.error(err); + process.exit(1); + }), + ); diff --git a/packages/shared/tsdown.config.ts b/packages/shared/tsdown.config.ts index 98128e34..d118f7ab 100644 --- a/packages/shared/tsdown.config.ts +++ b/packages/shared/tsdown.config.ts @@ -15,17 +15,20 @@ export default defineConfig({ skipNodeModulesBundle: true, external: [/^@databricks\//], tsconfig: "./tsconfig.json", + outExtensions: () => ({ + js: ".js", + }), exports: { devExports: "development", }, copy: [ { from: "src/schemas/plugin-manifest.schema.json", - to: "dist/schemas/plugin-manifest.schema.json", + to: "dist/schemas", }, { from: "src/schemas/template-plugins.schema.json", - to: "dist/schemas/template-plugins.schema.json", + to: "dist/schemas", }, ], }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7c4005bd..05a67f18 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -60,8 +60,8 @@ importers: specifier: ^19.1.0 version: 19.2.0(@types/node@24.7.2)(magicast@0.3.5) tsdown: - specifier: ^0.15.7 - version: 0.15.7(publint@0.3.15)(typescript@5.9.3) + specifier: ^0.20.3 + version: 0.20.3(publint@0.3.15)(typescript@5.9.3) tsx: specifier: ^4.20.6 version: 4.20.6 @@ -164,8 +164,8 @@ importers: specifier: ^16.0.0 version: 16.6.1 tsdown: - specifier: ^0.15.7 - version: 0.15.7(publint@0.3.15)(typescript@5.9.3) + specifier: ^0.20.3 + version: 0.20.3(publint@0.3.15)(typescript@5.9.3) tsx: specifier: ^4.20.6 version: 4.20.6 @@ -767,6 +767,10 @@ packages: resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} engines: {node: '>=6.9.0'} + '@babel/generator@8.0.0-rc.1': + resolution: {integrity: sha512-3ypWOOiC4AYHKr8vYRVtWtWmyvcoItHtVqF8paFax+ydpmUdPsJpLBkBBs5ItmhdrwC3a0ZSqqFAdzls4ODP3w==} + engines: {node: ^20.19.0 || >=22.12.0} + '@babel/helper-annotate-as-pure@7.27.3': resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} @@ -838,6 +842,10 @@ packages: resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@8.0.0-rc.2': + resolution: {integrity: sha512-noLx87RwlBEMrTzncWd/FvTxoJ9+ycHNg0n8yyYydIoDsLZuxknKgWRJUqcrVkNrJ74uGyhWQzQaS3q8xfGAhQ==} + engines: {node: ^20.19.0 || >=22.12.0} + '@babel/helper-validator-identifier@7.27.1': resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} @@ -846,6 +854,10 @@ packages: resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@8.0.0-rc.1': + resolution: {integrity: sha512-I4YnARytXC2RzkLNVnf5qFNFMzp679qZpmtw/V3Jt2uGnWiIxyJtaukjG7R8pSx8nG2NamICpGfljQsogj+FbQ==} + engines: {node: ^20.19.0 || >=22.12.0} + '@babel/helper-validator-option@7.27.1': resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} @@ -868,6 +880,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@8.0.0-rc.1': + resolution: {integrity: sha512-6HyyU5l1yK/7h9Ki52i5h6mDAx4qJdiLQO4FdCyJNoB/gy3T3GGJdhQzzbZgvgZCugYBvwtQiWRt94QKedHnkA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} engines: {node: '>=6.9.0'} @@ -1354,6 +1371,10 @@ packages: resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} + '@babel/types@8.0.0-rc.1': + resolution: {integrity: sha512-ubmJ6TShyaD69VE9DQrlXcdkvJbmwWPB8qYj0H2kaJi29O7vJT9ajSdBd2W8CG34pwL9pYA74fi7RHC1qbLoVQ==} + engines: {node: ^20.19.0 || >=22.12.0} + '@biomejs/biome@2.2.6': resolution: {integrity: sha512-yKTCNGhek0rL5OEW1jbLeZX8LHaM8yk7+3JRGv08my+gkpmtb5dDE+54r2ZjZx0ediFEn1pYBOJSmOdDP9xtFw==} engines: {node: '>=14.21.3'} @@ -3077,8 +3098,11 @@ packages: resolution: {integrity: sha512-Z7x2dZOmznihvdvCvLKMl+nswtOSVxS2H2ocar+U9xx6iMfTp0VGIrX6a4xB1v80IwOPC7dT1LXIJrY70Xu3Jw==} engines: {node: ^20.19.0 || >=22.12.0} - '@oxc-project/types@0.115.0': - resolution: {integrity: sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==} + '@oxc-project/types@0.112.0': + resolution: {integrity: sha512-m6RebKHIRsax2iCwVpYW2ErQwa4ywHJrE4sCK3/8JK8ZZAWOKXaRJFl/uP51gaVyyXlaS4+chU1nSCdzYf6QqQ==} + + '@oxc-project/types@0.114.0': + resolution: {integrity: sha512-//nBfbzHQHvJs8oFIjv6coZ6uxQ4alLfiPe6D5vit6c4pmxATHHlVwgB1k+Hv4yoAMyncdxgRBF5K4BYWUCzvA==} '@oxc-project/types@0.93.0': resolution: {integrity: sha512-yNtwmWZIBtJsMr5TEfoZFDxIWV6OdScOpza/f5YxbqUMJk+j6QX3Cf3jgZShGEFYWQJ5j9mJ6jM0tZHu2J9Yrg==} @@ -3145,8 +3169,8 @@ packages: resolution: {integrity: sha512-S+9ANAvUmjutrshV4jZjaiG8XQyuJIZ8a4utWmN/vW1sgQ9IfBnPndwkmQYw53QmouOIytT874u65HEmu6H5jw==} engines: {node: '>=18'} - '@quansync/fs@0.1.5': - resolution: {integrity: sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA==} + '@quansync/fs@1.0.0': + resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} '@radix-ui/number@1.1.1': resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} @@ -3803,8 +3827,14 @@ packages: cpu: [arm64] os: [android] - '@rolldown/binding-android-arm64@1.0.0-rc.6': - resolution: {integrity: sha512-kvjTSWGcrv+BaR2vge57rsKiYdVR8V8CoS0vgKrc570qRBfty4bT+1X0z3j2TaVV+kAYzA0PjeB9+mdZyqUZlg==} + '@rolldown/binding-android-arm64@1.0.0-rc.3': + resolution: {integrity: sha512-0T1k9FinuBZ/t7rZ8jN6OpUKPnUjNdYHoj/cESWrQ3ZraAJ4OMm6z7QjSfCxqj8mOp9kTKc1zHK3kGz5vMu+nQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-android-arm64@1.0.0-rc.5': + resolution: {integrity: sha512-zCEmUrt1bggwgBgeKLxNj217J1OrChrp3jJt24VK9jAharSTeVaHODNL+LpcQVhRz+FktYWfT9cjo5oZ99ZLpg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] @@ -3815,8 +3845,14 @@ packages: cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-arm64@1.0.0-rc.6': - resolution: {integrity: sha512-+tJhD21KvGNtUrpLXrZQlT+j5HZKiEwR2qtcZb3vNOUpvoT9QjEykr75ZW/Kr0W89gose/HVXU6351uVZD8Qvw==} + '@rolldown/binding-darwin-arm64@1.0.0-rc.3': + resolution: {integrity: sha512-JWWLzvcmc/3pe7qdJqPpuPk91SoE/N+f3PcWx/6ZwuyDVyungAEJPvKm/eEldiDdwTmaEzWfIR+HORxYWrCi1A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-arm64@1.0.0-rc.5': + resolution: {integrity: sha512-ZP9xb9lPAex36pvkNWCjSEJW/Gfdm9I3ssiqOFLmpZ/vosPXgpoGxCmh+dX1Qs+/bWQE6toNFXWWL8vYoKoK9Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] @@ -3827,8 +3863,14 @@ packages: cpu: [x64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-rc.6': - resolution: {integrity: sha512-DKNhjMk38FAWaHwUt1dFR3rA/qRAvn2NUvSG2UGvxvlMxSmN/qqww/j4ABAbXhNRXtGQNmrAINMXRuwHl16ZHg==} + '@rolldown/binding-darwin-x64@1.0.0-rc.3': + resolution: {integrity: sha512-MTakBxfx3tde5WSmbHxuqlDsIW0EzQym+PJYGF4P6lG2NmKzi128OGynoFUqoD5ryCySEY85dug4v+LWGBElIw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.0-rc.5': + resolution: {integrity: sha512-7IdrPunf6dp9mywMgTOKMMGDnMHQ6+h5gRl6LW8rhD8WK2kXX0IwzcM5Zc0B5J7xQs8QWOlKjv8BJsU/1CD3pg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] @@ -3839,8 +3881,14 @@ packages: cpu: [x64] os: [freebsd] - '@rolldown/binding-freebsd-x64@1.0.0-rc.6': - resolution: {integrity: sha512-8TThsRkCPAnfyMBShxrGdtoOE6h36QepqRQI97iFaQSCRbHFWHcDHppcojZnzXoruuhPnjMEygzaykvPVJsMRg==} + '@rolldown/binding-freebsd-x64@1.0.0-rc.3': + resolution: {integrity: sha512-jje3oopyOLs7IwfvXoS6Lxnmie5JJO7vW29fdGFu5YGY1EDbVDhD+P9vDihqS5X6fFiqL3ZQZCMBg6jyHkSVww==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-freebsd-x64@1.0.0-rc.5': + resolution: {integrity: sha512-o/JCk+dL0IN68EBhZ4DqfsfvxPfMeoM6cJtxORC1YYoxGHZyth2Kb2maXDb4oddw2wu8iIbnYXYPEzBtAF5CAg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] @@ -3851,8 +3899,14 @@ packages: cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.6': - resolution: {integrity: sha512-ZfmFoOwPUZCWtGOVC9/qbQzfc0249FrRUOzV2XabSMUV60Crp211OWLQN1zmQAsRIVWRcEwhJ46Z1mXGo/L/nQ==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.3': + resolution: {integrity: sha512-A0n8P3hdLAaqzSFrQoA42p23ZKBYQOw+8EH5r15Sa9X1kD9/JXe0YT2gph2QTWvdr0CVK2BOXiK6ENfy6DXOag==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.5': + resolution: {integrity: sha512-IIBwTtA6VwxQLcEgq2mfrUgam7VvPZjhd/jxmeS1npM+edWsrrpRLHUdze+sk4rhb8/xpP3flemgcZXXUW6ukw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] @@ -3863,8 +3917,14 @@ packages: cpu: [arm64] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.6': - resolution: {integrity: sha512-ZsGzbNETxPodGlLTYHaCSGVhNN/rvkMDCJYHdT7PZr5jFJRmBfmDi2awhF64Dt2vxrJqY6VeeYSgOzEbHRsb7Q==} + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.3': + resolution: {integrity: sha512-kWXkoxxarYISBJ4bLNf5vFkEbb4JvccOwxWDxuK9yee8lg5XA7OpvlTptfRuwEvYcOZf+7VS69Uenpmpyo5Bjw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.5': + resolution: {integrity: sha512-KSol1De1spMZL+Xg7K5IBWXIvRWv7+pveaxFWXpezezAG7CS6ojzRjtCGCiLxQricutTAi/LkNWKMsd2wNhMKQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] @@ -3875,8 +3935,14 @@ packages: cpu: [arm64] os: [linux] - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.6': - resolution: {integrity: sha512-elPpdevtCdUOqziemR86C4CSCr/5sUxalzDrf/CJdMT+kZt2C556as++qHikNOz0vuFf52h+GJNXZM08eWgGPQ==} + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.3': + resolution: {integrity: sha512-Z03/wrqau9Bicfgb3Dbs6SYTHliELk2PM2LpG2nFd+cGupTMF5kanLEcj2vuuJLLhptNyS61rtk7SOZ+lPsTUA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.5': + resolution: {integrity: sha512-WFljyDkxtXRlWxMjxeegf7xMYXxUr8u7JdXlOEWKYgDqEgxUnSEsVDxBiNWQ1D5kQKwf8Wo4sVKEYPRhCdsjwA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] @@ -3887,8 +3953,14 @@ packages: cpu: [x64] os: [linux] - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.6': - resolution: {integrity: sha512-IBwXsf56o3xhzAyaZxdM1CX8UFiBEUFCjiVUgny67Q8vPIqkjzJj0YKhd3TbBHanuxThgBa59f6Pgutg2OGk5A==} + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.3': + resolution: {integrity: sha512-iSXXZsQp08CSilff/DCTFZHSVEpEwdicV3W8idHyrByrcsRDVh9sGC3sev6d8BygSGj3vt8GvUKBPCoyMA4tgQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.5': + resolution: {integrity: sha512-CUlplTujmbDWp2gamvrqVKi2Or8lmngXT1WxsizJfts7JrvfGhZObciaY/+CbdbS9qNnskvwMZNEhTPrn7b+WA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] @@ -3899,8 +3971,14 @@ packages: cpu: [x64] os: [linux] - '@rolldown/binding-linux-x64-musl@1.0.0-rc.6': - resolution: {integrity: sha512-vOk7G8V9Zm+8a6PL6JTpCea61q491oYlGtO6CvnsbhNLlKdf0bbCPytFzGQhYmCKZDKkEbmnkcIprTEGCURnwg==} + '@rolldown/binding-linux-x64-musl@1.0.0-rc.3': + resolution: {integrity: sha512-qaj+MFudtdCv9xZo9znFvkgoajLdc+vwf0Kz5N44g+LU5XMe+IsACgn3UG7uTRlCCvhMAGXm1XlpEA5bZBrOcw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-linux-x64-musl@1.0.0-rc.5': + resolution: {integrity: sha512-wdf7g9NbVZCeAo2iGhsjJb7I8ZFfs6X8bumfrWg82VK+8P6AlLXwk48a1ASiJQDTS7Svq2xVzZg3sGO2aXpHRA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] @@ -3911,8 +3989,14 @@ packages: cpu: [arm64] os: [openharmony] - '@rolldown/binding-openharmony-arm64@1.0.0-rc.6': - resolution: {integrity: sha512-ASjEDI4MRv7XCQb2JVaBzfEYO98JKCGrAgoW6M03fJzH/ilCnC43Mb3ptB9q/lzsaahoJyIBoAGKAYEjUvpyvQ==} + '@rolldown/binding-openharmony-arm64@1.0.0-rc.3': + resolution: {integrity: sha512-U662UnMETyjT65gFmG9ma+XziENrs7BBnENi/27swZPYagubfHRirXHG2oMl+pEax2WvO7Kb9gHZmMakpYqBHQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-openharmony-arm64@1.0.0-rc.5': + resolution: {integrity: sha512-0CWY7ubu12nhzz+tkpHjoG3IRSTlWYe0wrfJRf4qqjqQSGtAYgoL9kwzdvlhaFdZ5ffVeyYw9qLsChcjUMEloQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] @@ -3922,8 +4006,13 @@ packages: engines: {node: '>=14.0.0'} cpu: [wasm32] - '@rolldown/binding-wasm32-wasi@1.0.0-rc.6': - resolution: {integrity: sha512-mYa1+h2l6Zc0LvmwUh0oXKKYihnw/1WC73vTqw+IgtfEtv47A+rWzzcWwVDkW73+UDr0d/Ie/HRXoaOY22pQDw==} + '@rolldown/binding-wasm32-wasi@1.0.0-rc.3': + resolution: {integrity: sha512-gekrQ3Q2HiC1T5njGyuUJoGpK/l6B/TNXKed3fZXNf9YRTJn3L5MOZsFBn4bN2+UX+8+7hgdlTcEsexX988G4g==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@rolldown/binding-wasm32-wasi@1.0.0-rc.5': + resolution: {integrity: sha512-LztXnGzv6t2u830mnZrFLRVqT/DPJ9DL4ZTz/y93rqUVkeHjMMYIYaFj+BUthiYxbVH9dH0SZYufETspKY/NhA==} engines: {node: '>=14.0.0'} cpu: [wasm32] @@ -3933,8 +4022,14 @@ packages: cpu: [arm64] os: [win32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.6': - resolution: {integrity: sha512-e2ABskbNH3MRUBMjgxaMjYIw11DSwjLJxBII3UgpF6WClGLIh8A20kamc+FKH5vIaFVnYQInmcLYSUVpqMPLow==} + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.3': + resolution: {integrity: sha512-85y5JifyMgs8m5K2XzR/VDsapKbiFiohl7s5lEj7nmNGO0pkTXE7q6TQScei96BNAsoK7JC3pA7ukA8WRHVJpg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.5': + resolution: {integrity: sha512-jUct1XVeGtyjqJXEAfvdFa8xoigYZ2rge7nYEm70ppQxpfH9ze2fbIrpHmP2tNM2vL/F6Dd0CpXhpjPbC6bSxQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] @@ -3951,8 +4046,14 @@ packages: cpu: [x64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.6': - resolution: {integrity: sha512-dJVc3ifhaRXxIEh1xowLohzFrlQXkJ66LepHm+CmSprTWgVrPa8Fx3OL57xwIqDEH9hufcKkDX2v65rS3NZyRA==} + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.3': + resolution: {integrity: sha512-a4VUQZH7LxGbUJ3qJ/TzQG8HxdHvf+jOnqf7B7oFx1TEBm+j2KNL2zr5SQ7wHkNAcaPevF6gf9tQnVBnC4mD+A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.5': + resolution: {integrity: sha512-VQ8F9ld5gw29epjnVGdrx8ugiLTe8BMqmhDYy7nGbdeDo4HAt4bgdZvLbViEhg7DZyHLpiEUlO5/jPSUrIuxRQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -3966,8 +4067,11 @@ packages: '@rolldown/pluginutils@1.0.0-beta.47': resolution: {integrity: sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==} - '@rolldown/pluginutils@1.0.0-rc.6': - resolution: {integrity: sha512-Y0+JT8Mi1mmW08K6HieG315XNRu4L0rkfCpA364HtytjgiqYnMYRdFPcxRl+BQQqNXzecL2S9nii+RUpO93XIA==} + '@rolldown/pluginutils@1.0.0-rc.3': + resolution: {integrity: sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==} + + '@rolldown/pluginutils@1.0.0-rc.5': + resolution: {integrity: sha512-RxlLX/DPoarZ9PtxVrQgZhPoor987YtKQqCo5zkjX+0S0yLJ7Vv515Wk6+xtTL67VONKJKxETWZwuZjss2idYw==} '@rollup/rollup-android-arm-eabi@4.52.4': resolution: {integrity: sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==} @@ -4579,6 +4683,9 @@ packages: '@types/istanbul-reports@3.0.4': resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + '@types/jsesc@2.5.1': + resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -5116,8 +5223,8 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - ast-kit@2.1.3: - resolution: {integrity: sha512-TH+b3Lv6pUjy/Nu0m6A2JULtdzLpmqF9x1Dhj00ZoEiML8qvVA9j1flkzTKNYgdEhWrjDwtWNpyyCUbfQe514g==} + ast-kit@3.0.0-beta.1: + resolution: {integrity: sha512-trmleAnZ2PxN/loHWVhhx1qeOHSRXq4TDsBBxq3GqeJitfk3+jTQ+v/C1km/KYq9M7wKqCewMh+/NAvVH7m+bw==} engines: {node: '>=20.19.0'} ast-module-types@6.0.1: @@ -5223,8 +5330,8 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} - birpc@2.6.1: - resolution: {integrity: sha512-LPnFhlDpdSH6FJhJyn4M0kFO7vtQ5iPw24FnG0y21q09xC7e8+1LeR31S1MAIrDAHp4m7aas4bEkTDTvMAtebQ==} + birpc@4.0.0: + resolution: {integrity: sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw==} bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} @@ -5418,10 +5525,6 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} - chokidar@4.0.3: - resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} - engines: {node: '>= 14.16.0'} - chokidar@5.0.0: resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} engines: {node: '>= 20.19.0'} @@ -6251,10 +6354,6 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - diff@8.0.2: - resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==} - engines: {node: '>=0.3.1'} - dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -6436,9 +6535,9 @@ packages: sqlite3: optional: true - dts-resolver@2.1.2: - resolution: {integrity: sha512-xeXHBQkn2ISSXxbJWD828PFjtyg+/UrMDo7W4Ffcs7+YWCquxU8YjV1KoxuiL+eJ5pg3ll+bC6flVv61L3LKZg==} - engines: {node: '>=20.18.0'} + dts-resolver@2.1.3: + resolution: {integrity: sha512-bihc7jPC90VrosXNzK0LTE2cuLP6jr0Ro8jk+kMugHReJVLIpHz/xadeq3MhuwyO4TD4OA3L1Q8pBBFRc08Tsw==} + engines: {node: '>=20.19.0'} peerDependencies: oxc-resolver: '>=11.0.0' peerDependenciesMeta: @@ -7018,6 +7117,9 @@ packages: get-tsconfig@4.12.0: resolution: {integrity: sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw==} + get-tsconfig@4.13.6: + resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} + get-uri@6.0.5: resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} engines: {node: '>= 14'} @@ -7297,8 +7399,8 @@ packages: resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} engines: {node: '>=0.10.0'} - hookable@5.5.3: - resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + hookable@6.0.1: + resolution: {integrity: sha512-uKGyY8BuzN/a5gvzvA+3FVWo0+wUjgtfSdnmjtrOVwQCZPHpHDH2WRO3VZSOeluYrHoDCiXFffZXs8Dj1ULWtw==} hosted-git-info@8.1.0: resolution: {integrity: sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==} @@ -7463,6 +7565,10 @@ packages: import-meta-resolve@4.2.0: resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + import-without-cache@0.2.5: + resolution: {integrity: sha512-B6Lc2s6yApwnD2/pMzFh/d5AVjdsDXjgkeJ766FmFuJELIGHNycKRj+l3A39yZPM4CchqNCB4RITEAYB1KUM6A==} + engines: {node: '>=20.19.0'} + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -9536,8 +9642,8 @@ packages: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} - quansync@0.2.11: - resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + quansync@1.0.0: + resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -9705,10 +9811,6 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} - readdirp@4.1.2: - resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} - engines: {node: '>= 14.18.0'} - readdirp@5.0.0: resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} engines: {node: '>= 20.19.0'} @@ -9919,15 +10021,15 @@ packages: robust-predicates@3.0.2: resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} - rolldown-plugin-dts@0.16.11: - resolution: {integrity: sha512-9IQDaPvPqTx3RjG2eQCK5GYZITo203BxKunGI80AGYicu1ySFTUyugicAaTZWRzFWh9DSnzkgNeMNbDWBbSs0w==} - engines: {node: '>=20.18.0'} + rolldown-plugin-dts@0.22.2: + resolution: {integrity: sha512-Ge+XF962Kobjr0hRPx1neVnLU2jpKkD2zevZTfPKf/0el4eYo9SyGPm0stiHDG2JQuL0Q3HLD0Kn+ST8esvVdA==} + engines: {node: '>=20.19.0'} peerDependencies: '@ts-macro/tsc': ^0.3.6 '@typescript/native-preview': '>=7.0.0-dev.20250601.1' - rolldown: ^1.0.0-beta.9 - typescript: ^5.0.0 - vue-tsc: ~3.1.0 + rolldown: ^1.0.0-rc.3 + typescript: ^5.0.0 || ^6.0.0-beta + vue-tsc: ~3.2.0 peerDependenciesMeta: '@ts-macro/tsc': optional: true @@ -9983,8 +10085,13 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rolldown@1.0.0-rc.6: - resolution: {integrity: sha512-B8vFPV1ADyegoYfhg+E7RAucYKv0xdVlwYYsIJgfPNeiSxZGWNxts9RqhyGzC11ULK/VaeXyKezGCwpMiH8Ktw==} + rolldown@1.0.0-rc.3: + resolution: {integrity: sha512-Po/YZECDOqVXjIXrtC5h++a5NLvKAQNrd9ggrIG3sbDfGO5BqTUsrI6l8zdniKRp3r5Tp/2JTrXqx4GIguFCMw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rolldown@1.0.0-rc.5: + resolution: {integrity: sha512-0AdalTs6hNTioaCYIkAa7+xsmHBfU5hCNclZnM/lp7lGGDuUOb6N4BVNtwiomybbencDjq/waKjTImqiGCs5sw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -10548,6 +10655,10 @@ packages: tinyexec@1.0.1: resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + engines: {node: '>=18'} + tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} @@ -10653,12 +10764,13 @@ packages: resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} engines: {node: '>=6'} - tsdown@0.15.7: - resolution: {integrity: sha512-uFaVgWAogjOMqjY+CQwrUt3C6wzy6ynt82CIoXymnbS17ipUZ8WDXUceJjkislUahF/BZc5+W44Ue3p2oWtqUg==} + tsdown@0.20.3: + resolution: {integrity: sha512-qWOUXSbe4jN8JZEgrkc/uhJpC8VN2QpNu3eZkBWwNuTEjc/Ik1kcc54ycfcQ5QPRHeu9OQXaLfCI3o7pEJgB2w==} engines: {node: '>=20.19.0'} hasBin: true peerDependencies: '@arethetypeswrong/core': ^0.18.1 + '@vitejs/devtools': '*' publint: ^0.3.0 typescript: ^5.0.0 unplugin-lightningcss: ^0.4.0 @@ -10666,6 +10778,8 @@ packages: peerDependenciesMeta: '@arethetypeswrong/core': optional: true + '@vitejs/devtools': + optional: true publint: optional: true typescript: @@ -10862,8 +10976,8 @@ packages: resolution: {integrity: sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==} engines: {node: '>=0.10.0'} - unconfig@7.3.3: - resolution: {integrity: sha512-QCkQoOnJF8L107gxfHL0uavn7WD9b3dpBcFX6HtfQYmjw2YzWxGuFQ0N0J6tE9oguCBJn9KOvfqYDCMPHIZrBA==} + unconfig-core@7.5.0: + resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} @@ -10959,6 +11073,16 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} + unrun@0.2.28: + resolution: {integrity: sha512-LqMrI3ZEUMZ2476aCsbUTfy95CHByqez05nju4AQv4XFPkxh5yai7Di1/Qb0FoELHEEPDWhQi23EJeFyrBV0Og==} + engines: {node: '>=20.19.0'} + hasBin: true + peerDependencies: + synckit: ^0.11.11 + peerDependenciesMeta: + synckit: + optional: true + update-browserslist-db@1.1.4: resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} hasBin: true @@ -11747,6 +11871,15 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 + '@babel/generator@8.0.0-rc.1': + dependencies: + '@babel/parser': 8.0.0-rc.1 + '@babel/types': 8.0.0-rc.1 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@types/jsesc': 2.5.1 + jsesc: 3.1.0 + '@babel/helper-annotate-as-pure@7.27.3': dependencies: '@babel/types': 7.28.5 @@ -11857,10 +11990,14 @@ snapshots: '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@8.0.0-rc.2': {} + '@babel/helper-validator-identifier@7.27.1': {} '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@8.0.0-rc.1': {} + '@babel/helper-validator-option@7.27.1': {} '@babel/helper-wrap-function@7.28.3': @@ -11884,6 +12021,10 @@ snapshots: dependencies: '@babel/types': 7.28.5 + '@babel/parser@8.0.0-rc.1': + dependencies: + '@babel/types': 8.0.0-rc.1 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -12518,6 +12659,11 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@8.0.0-rc.1': + dependencies: + '@babel/helper-string-parser': 8.0.0-rc.2 + '@babel/helper-validator-identifier': 8.0.0-rc.1 + '@biomejs/biome@2.2.6': optionalDependencies: '@biomejs/cli-darwin-arm64': 2.2.6 @@ -15074,7 +15220,9 @@ snapshots: '@oxc-project/runtime@0.92.0': {} - '@oxc-project/types@0.115.0': {} + '@oxc-project/types@0.112.0': {} + + '@oxc-project/types@0.114.0': {} '@oxc-project/types@0.93.0': {} @@ -15126,9 +15274,9 @@ snapshots: '@publint/pack@0.1.2': {} - '@quansync/fs@0.1.5': + '@quansync/fs@1.0.0': dependencies: - quansync: 0.2.11 + quansync: 1.0.0 '@radix-ui/number@1.1.1': {} @@ -15822,61 +15970,91 @@ snapshots: '@rolldown/binding-android-arm64@1.0.0-beta.41': optional: true - '@rolldown/binding-android-arm64@1.0.0-rc.6': + '@rolldown/binding-android-arm64@1.0.0-rc.3': + optional: true + + '@rolldown/binding-android-arm64@1.0.0-rc.5': optional: true '@rolldown/binding-darwin-arm64@1.0.0-beta.41': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-rc.6': + '@rolldown/binding-darwin-arm64@1.0.0-rc.3': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.0-rc.5': optional: true '@rolldown/binding-darwin-x64@1.0.0-beta.41': optional: true - '@rolldown/binding-darwin-x64@1.0.0-rc.6': + '@rolldown/binding-darwin-x64@1.0.0-rc.3': + optional: true + + '@rolldown/binding-darwin-x64@1.0.0-rc.5': optional: true '@rolldown/binding-freebsd-x64@1.0.0-beta.41': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-rc.6': + '@rolldown/binding-freebsd-x64@1.0.0-rc.3': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.0-rc.5': optional: true '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.41': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.6': + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.3': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.5': optional: true '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.41': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.6': + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.3': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.5': optional: true '@rolldown/binding-linux-arm64-musl@1.0.0-beta.41': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.6': + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.3': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.5': optional: true '@rolldown/binding-linux-x64-gnu@1.0.0-beta.41': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.6': + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.3': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.5': optional: true '@rolldown/binding-linux-x64-musl@1.0.0-beta.41': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-rc.6': + '@rolldown/binding-linux-x64-musl@1.0.0-rc.3': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.0-rc.5': optional: true '@rolldown/binding-openharmony-arm64@1.0.0-beta.41': optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-rc.6': + '@rolldown/binding-openharmony-arm64@1.0.0-rc.3': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.0-rc.5': optional: true '@rolldown/binding-wasm32-wasi@1.0.0-beta.41': @@ -15884,7 +16062,12 @@ snapshots: '@napi-rs/wasm-runtime': 1.0.7 optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-rc.6': + '@rolldown/binding-wasm32-wasi@1.0.0-rc.3': + dependencies: + '@napi-rs/wasm-runtime': 1.1.1 + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0-rc.5': dependencies: '@napi-rs/wasm-runtime': 1.1.1 optional: true @@ -15892,7 +16075,10 @@ snapshots: '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.41': optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.6': + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.3': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.5': optional: true '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.41': @@ -15901,7 +16087,10 @@ snapshots: '@rolldown/binding-win32-x64-msvc@1.0.0-beta.41': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.6': + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.3': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.5': optional: true '@rolldown/pluginutils@1.0.0-beta.38': {} @@ -15910,7 +16099,9 @@ snapshots: '@rolldown/pluginutils@1.0.0-beta.47': {} - '@rolldown/pluginutils@1.0.0-rc.6': {} + '@rolldown/pluginutils@1.0.0-rc.3': {} + + '@rolldown/pluginutils@1.0.0-rc.5': {} '@rollup/rollup-android-arm-eabi@4.52.4': optional: true @@ -16543,6 +16734,8 @@ snapshots: dependencies: '@types/istanbul-lib-report': 3.0.3 + '@types/jsesc@2.5.1': {} + '@types/json-schema@7.0.15': {} '@types/liftoff@4.0.3': @@ -17232,9 +17425,10 @@ snapshots: assertion-error@2.0.1: {} - ast-kit@2.1.3: + ast-kit@3.0.0-beta.1: dependencies: - '@babel/parser': 7.28.5 + '@babel/parser': 8.0.0-rc.1 + estree-walker: 3.0.3 pathe: 2.0.3 ast-module-types@6.0.1: {} @@ -17333,7 +17527,7 @@ snapshots: binary-extensions@2.3.0: {} - birpc@2.6.1: {} + birpc@4.0.0: {} bl@4.1.0: dependencies: @@ -17597,10 +17791,6 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - chokidar@4.0.3: - dependencies: - readdirp: 4.1.2 - chokidar@5.0.0: dependencies: readdirp: 5.0.0 @@ -18443,8 +18633,6 @@ snapshots: dependencies: dequal: 2.0.3 - diff@8.0.2: {} - dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -18558,7 +18746,7 @@ snapshots: '@types/pg': 8.16.0 pg: 8.18.0 - dts-resolver@2.1.2: {} + dts-resolver@2.1.3: {} dunder-proto@1.0.1: dependencies: @@ -19221,6 +19409,10 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + get-tsconfig@4.13.6: + dependencies: + resolve-pkg-maps: 1.0.0 + get-uri@6.0.5: dependencies: basic-ftp: 5.0.5 @@ -19708,7 +19900,7 @@ snapshots: dependencies: parse-passwd: 1.0.0 - hookable@5.5.3: {} + hookable@6.0.1: {} hosted-git-info@8.1.0: dependencies: @@ -19893,6 +20085,8 @@ snapshots: import-meta-resolve@4.2.0: {} + import-without-cache@0.2.5: {} + imurmurhash@0.1.4: {} indent-string@4.0.0: {} @@ -22279,7 +22473,7 @@ snapshots: dependencies: side-channel: 1.1.0 - quansync@0.2.11: {} + quansync@1.0.0: {} queue-microtask@1.2.3: {} @@ -22457,8 +22651,6 @@ snapshots: dependencies: picomatch: 2.3.1 - readdirp@4.1.2: {} - readdirp@5.0.0: {} recharts-scale@0.4.5: @@ -22762,23 +22954,22 @@ snapshots: robust-predicates@3.0.2: {} - rolldown-plugin-dts@0.16.11(rolldown@1.0.0-rc.6)(typescript@5.9.3): - dependencies: - '@babel/generator': 7.28.3 - '@babel/parser': 7.28.5 - '@babel/types': 7.28.4 - ast-kit: 2.1.3 - birpc: 2.6.1 - debug: 4.4.3 - dts-resolver: 2.1.2 - get-tsconfig: 4.12.0 - magic-string: 0.30.19 - rolldown: 1.0.0-rc.6 + rolldown-plugin-dts@0.22.2(rolldown@1.0.0-rc.3)(typescript@5.9.3): + dependencies: + '@babel/generator': 8.0.0-rc.1 + '@babel/helper-validator-identifier': 8.0.0-rc.1 + '@babel/parser': 8.0.0-rc.1 + '@babel/types': 8.0.0-rc.1 + ast-kit: 3.0.0-beta.1 + birpc: 4.0.0 + dts-resolver: 2.1.3 + get-tsconfig: 4.13.6 + obug: 2.1.1 + rolldown: 1.0.0-rc.3 optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - oxc-resolver - - supports-color rolldown-vite@7.1.14(@types/node@20.19.21)(esbuild@0.25.10)(jiti@2.6.1)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1): dependencies: @@ -22837,24 +23028,43 @@ snapshots: '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.41 '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.41 - rolldown@1.0.0-rc.6: + rolldown@1.0.0-rc.3: dependencies: - '@oxc-project/types': 0.115.0 - '@rolldown/pluginutils': 1.0.0-rc.6 + '@oxc-project/types': 0.112.0 + '@rolldown/pluginutils': 1.0.0-rc.3 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-rc.6 - '@rolldown/binding-darwin-arm64': 1.0.0-rc.6 - '@rolldown/binding-darwin-x64': 1.0.0-rc.6 - '@rolldown/binding-freebsd-x64': 1.0.0-rc.6 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.6 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.6 - '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.6 - '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.6 - '@rolldown/binding-linux-x64-musl': 1.0.0-rc.6 - '@rolldown/binding-openharmony-arm64': 1.0.0-rc.6 - '@rolldown/binding-wasm32-wasi': 1.0.0-rc.6 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.6 - '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.6 + '@rolldown/binding-android-arm64': 1.0.0-rc.3 + '@rolldown/binding-darwin-arm64': 1.0.0-rc.3 + '@rolldown/binding-darwin-x64': 1.0.0-rc.3 + '@rolldown/binding-freebsd-x64': 1.0.0-rc.3 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.3 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.3 + '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.3 + '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.3 + '@rolldown/binding-linux-x64-musl': 1.0.0-rc.3 + '@rolldown/binding-openharmony-arm64': 1.0.0-rc.3 + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.3 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.3 + '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.3 + + rolldown@1.0.0-rc.5: + dependencies: + '@oxc-project/types': 0.114.0 + '@rolldown/pluginutils': 1.0.0-rc.5 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0-rc.5 + '@rolldown/binding-darwin-arm64': 1.0.0-rc.5 + '@rolldown/binding-darwin-x64': 1.0.0-rc.5 + '@rolldown/binding-freebsd-x64': 1.0.0-rc.5 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.5 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.5 + '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.5 + '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.5 + '@rolldown/binding-linux-x64-musl': 1.0.0-rc.5 + '@rolldown/binding-openharmony-arm64': 1.0.0-rc.5 + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.5 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.5 + '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.5 rollup@4.52.4: dependencies: @@ -23456,6 +23666,8 @@ snapshots: tinyexec@1.0.1: {} + tinyexec@1.0.2: {} + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) @@ -23536,22 +23748,24 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 - tsdown@0.15.7(publint@0.3.15)(typescript@5.9.3): + tsdown@0.20.3(publint@0.3.15)(typescript@5.9.3): dependencies: ansis: 4.2.0 cac: 6.7.14 - chokidar: 4.0.3 - debug: 4.4.3 - diff: 8.0.2 + defu: 6.1.4 empathic: 2.0.0 - hookable: 5.5.3 - rolldown: 1.0.0-rc.6 - rolldown-plugin-dts: 0.16.11(rolldown@1.0.0-rc.6)(typescript@5.9.3) + hookable: 6.0.1 + import-without-cache: 0.2.5 + obug: 2.1.1 + picomatch: 4.0.3 + rolldown: 1.0.0-rc.3 + rolldown-plugin-dts: 0.22.2(rolldown@1.0.0-rc.3)(typescript@5.9.3) semver: 7.7.3 - tinyexec: 1.0.1 + tinyexec: 1.0.2 tinyglobby: 0.2.15 tree-kill: 1.2.2 - unconfig: 7.3.3 + unconfig-core: 7.5.0 + unrun: 0.2.28 optionalDependencies: publint: 0.3.15 typescript: 5.9.3 @@ -23559,7 +23773,7 @@ snapshots: - '@ts-macro/tsc' - '@typescript/native-preview' - oxc-resolver - - supports-color + - synckit - vue-tsc tslib@2.3.0: {} @@ -23695,12 +23909,10 @@ snapshots: unc-path-regex@0.1.2: {} - unconfig@7.3.3: + unconfig-core@7.5.0: dependencies: - '@quansync/fs': 0.1.5 - defu: 6.1.4 - jiti: 2.6.1 - quansync: 0.2.11 + '@quansync/fs': 1.0.0 + quansync: 1.0.0 undici-types@6.21.0: {} @@ -23808,6 +24020,10 @@ snapshots: unpipe@1.0.0: {} + unrun@0.2.28: + dependencies: + rolldown: 1.0.0-rc.5 + update-browserslist-db@1.1.4(browserslist@4.28.0): dependencies: browserslist: 4.28.0