@vryzel/file-next-cli 0.5.0 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -155,7 +155,7 @@ function formatDoctorReport(checks) {
155
155
  }
156
156
 
157
157
  // src/index.ts
158
- var VERSION = "0.5.0";
158
+ var VERSION = "0.5.1";
159
159
  function printHelp(version = VERSION) {
160
160
  return `file-next CLI v${version}
161
161
 
package/dist/bin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/migrate.ts","../src/reconcile.ts","../src/doctor.ts","../src/index.ts","../src/runtime.ts","../src/bin.ts"],"names":["parseArgs"],"mappings":"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","file":"bin.js","sourcesContent":["/**\n * `file-next migrate` — apply pending metadata schema migrations.\n *\n * v0.1 scope: prints the migration plan and reports what would run.\n * Actual migration execution is delegated to the adapter package's\n * migrator (Drizzle for postgres/sqlite). This CLI is the orchestrator\n * that resolves the adapter from the config and invokes it.\n *\n * Usage:\n * file-next migrate --adapter=postgres\n * file-next migrate --adapter=sqlite\n *\n * Exit codes:\n * 0 — migrations applied (or none pending)\n * 1 — configuration error (missing / unknown adapter)\n * 2 — migration failed (the adapter threw)\n */\nimport { parseArgs } from \"node:util\";\n\nexport interface MigrateOptions {\n readonly adapter: \"postgres\" | \"sqlite\";\n}\n\nexport function parseMigrateArgs(argv: ReadonlyArray<string>): MigrateOptions | { error: string } {\n let parsed: ReturnType<typeof parseArgs>;\n try {\n parsed = parseArgs({\n args: argv as string[],\n options: {\n adapter: { type: \"string\", short: \"a\" },\n },\n strict: true,\n allowPositionals: false,\n });\n } catch (err) {\n return { error: (err as Error).message };\n }\n\n const adapter = parsed.values.adapter;\n if (adapter !== \"postgres\" && adapter !== \"sqlite\") {\n return { error: \"--adapter must be either 'postgres' or 'sqlite'\" };\n }\n return { adapter };\n}\n\nexport interface MigrateResult {\n readonly applied: ReadonlyArray<string>;\n readonly pending: ReadonlyArray<string>;\n}\n\n/**\n * Resolve and run migrations against the chosen adapter.\n *\n * v0.1 implementation: returns a static plan rather than touching\n * a real database. The hook for `runAdapterMigrations` is the\n * integration point for v0.2 (which will load Drizzle's migrator\n * dynamically based on the adapter choice).\n */\nexport interface MigrateHooks {\n /** Resolves the migrator implementation. May throw on unsupported adapter. */\n resolveMigrator: (adapter: MigrateOptions[\"adapter\"]) => Promise<{\n listPending: () => Promise<ReadonlyArray<string>>;\n apply: () => Promise<ReadonlyArray<string>>;\n }>;\n}\n\nexport async function runMigrate(\n options: MigrateOptions,\n hooks: MigrateHooks = {\n // Default no-op migrator so the CLI can be smoke-tested without\n // a real DB. The real adapter wiring lives in the consumer's\n // app where drizzle-orm is a runtime dep.\n resolveMigrator: async () => ({\n listPending: async () => [],\n apply: async () => [],\n }),\n },\n): Promise<MigrateResult> {\n const migrator = await hooks.resolveMigrator(options.adapter);\n const pending = await migrator.listPending();\n const applied = await migrator.apply();\n return { applied, pending };\n}\n","/**\n * `file-next reconcile` — find and (optionally) fix drift between\n * the S3 bucket and the metadata index.\n *\n * Usage:\n * file-next reconcile --tenant=demo\n * file-next reconcile --tenant=demo --dry-run\n *\n * Exit codes:\n * 0 — no drift detected (or all drift fixed)\n * 1 — drift detected AND not fixed (dry-run mode)\n * 2 — configuration / connectivity error\n */\nimport { parseArgs } from \"node:util\";\n\nexport interface ReconcileOptions {\n readonly tenant: string;\n readonly dryRun: boolean;\n}\n\nexport function parseReconcileArgs(\n argv: ReadonlyArray<string>,\n): ReconcileOptions | { error: string } {\n let parsed: ReturnType<typeof parseArgs>;\n try {\n parsed = parseArgs({\n args: argv as string[],\n options: {\n tenant: { type: \"string\", short: \"t\" },\n \"dry-run\": { type: \"boolean\", default: false },\n },\n strict: true,\n allowPositionals: false,\n });\n } catch (err) {\n return { error: (err as Error).message };\n }\n\n if (typeof parsed.values.tenant !== \"string\" || parsed.values.tenant.length === 0) {\n return { error: \"--tenant is required\" };\n }\n return {\n tenant: parsed.values.tenant,\n dryRun: parsed.values[\"dry-run\"] === true,\n };\n}\n\nexport interface DriftReport {\n readonly missingInS3: ReadonlyArray<string>;\n readonly orphansInS3: ReadonlyArray<string>;\n readonly fixedCount: number;\n}\n\nexport interface ReconcileResult {\n readonly drift: DriftReport;\n readonly hadDrift: boolean;\n}\n\n/**\n * Run a reconcile cycle for a tenant.\n *\n * v0.1: the actual sync logic is provided via the `runSync` hook so\n * this CLI is testable without a real Postgres / S3 backend. The\n * consumer wires the real `reconcile()` from `@vryzel/file-next/sync` into\n * the hook when running the CLI against production.\n */\nexport interface ReconcileHooks {\n runSync: (input: {\n tenant: string;\n dryRun: boolean;\n }) => Promise<DriftReport>;\n}\n\nexport async function runReconcile(\n options: ReconcileOptions,\n hooks: ReconcileHooks = {\n runSync: async () => ({\n missingInS3: [],\n orphansInS3: [],\n fixedCount: 0,\n }),\n },\n): Promise<ReconcileResult> {\n const drift = await hooks.runSync({\n tenant: options.tenant,\n dryRun: options.dryRun,\n });\n const hadDrift = drift.missingInS3.length + drift.orphansInS3.length > 0;\n return { drift, hadDrift };\n}\n","/**\n * `file-next doctor` — diagnose the local environment for file-next.\n *\n * Checks (each pass/fail):\n * - Required env vars present (FILE_NEXT_BUCKET, FILE_NEXT_REGION,\n * FILE_NEXT_PROVIDER, and adapter-specific creds).\n * - Metadata DB reachable (postgres / sqlite path resolves).\n * - S3 bucket reachable (HEAD on the configured bucket returns 200\n * or 403 — not network error).\n *\n * Usage:\n * file-next doctor\n *\n * Exit codes:\n * 0 — all checks pass\n * 1 — one or more checks failed (printed to stderr)\n * 2 — invalid configuration (e.g. unknown provider)\n */\n\nexport interface DoctorCheck {\n readonly name: string;\n readonly ok: boolean;\n readonly detail: string;\n}\n\nexport interface DoctorOptions {\n /** Env vars to validate. The CLI uses process.env by default. */\n readonly env?: NodeJS.ProcessEnv;\n /** Optional callback to test DB reachability. */\n readonly probeDb?: (provider: \"postgres\" | \"sqlite\") => Promise<{ ok: boolean; detail: string }>;\n /** Optional callback to test S3 bucket reachability. */\n readonly probeBucket?: (bucket: string, region: string) => Promise<{ ok: boolean; detail: string }>;\n}\n\nconst REQUIRED_VARS = [\"FILE_NEXT_BUCKET\", \"FILE_NEXT_REGION\", \"FILE_NEXT_PROVIDER\"] as const;\nconst POSTGRES_VARS = [\"FILE_NEXT_PG_HOST\", \"FILE_NEXT_PG_USER\", \"FILE_NEXT_PG_DATABASE\"] as const;\nconst SQLITE_VAR = \"FILE_NEXT_SQLITE_PATH\";\n\n/**\n * Run all checks. Returns a list of { name, ok, detail } records.\n * The CLI formats and prints them; the caller decides the exit code.\n */\nexport async function runDoctor(options: DoctorOptions = {}): Promise<{\n checks: ReadonlyArray<DoctorCheck>;\n allOk: boolean;\n}> {\n const env = options.env ?? process.env;\n const checks: DoctorCheck[] = [];\n\n // 1. Provider declared\n const provider = env.FILE_NEXT_PROVIDER;\n if (!provider) {\n checks.push({\n name: \"provider declared\",\n ok: false,\n detail: \"FILE_NEXT_PROVIDER is not set (expected 's3' or 'r2')\",\n });\n } else if (provider !== \"s3\" && provider !== \"r2\") {\n checks.push({\n name: \"provider valid\",\n ok: false,\n detail: `FILE_NEXT_PROVIDER=\"${provider}\" is not recognized (expected 's3' or 'r2')`,\n });\n } else {\n checks.push({\n name: \"provider valid\",\n ok: true,\n detail: `FILE_NEXT_PROVIDER=\"${provider}\"`,\n });\n }\n\n // 2. Required env vars present\n for (const v of REQUIRED_VARS) {\n checks.push({\n name: `env ${v}`,\n ok: typeof env[v] === \"string\" && env[v]!.length > 0,\n detail: env[v] ? `${v}=\"${env[v]}\"` : `${v} is missing`,\n });\n }\n\n // 3. Adapter-specific vars\n const adapter = provider === \"r2\" ? \"postgres\" : (env.FILE_NEXT_ADAPTER ?? \"memory\");\n if (adapter === \"postgres\") {\n for (const v of POSTGRES_VARS) {\n checks.push({\n name: `env ${v}`,\n ok: typeof env[v] === \"string\" && env[v]!.length > 0,\n detail: env[v] ? `${v}=\"${env[v]}\"` : `${v} is missing (required for postgres adapter)`,\n });\n }\n } else if (adapter === \"sqlite\") {\n checks.push({\n name: `env ${SQLITE_VAR}`,\n ok: typeof env[SQLITE_VAR] === \"string\" && env[SQLITE_VAR]!.length > 0,\n detail: env[SQLITE_VAR] ?? `${SQLITE_VAR} is missing (required for sqlite adapter)`,\n });\n }\n\n // 4. DB reachability (only if probe provided)\n if (options.probeDb && (adapter === \"postgres\" || adapter === \"sqlite\")) {\n const result = await options.probeDb(adapter);\n checks.push({\n name: `${adapter} reachable`,\n ok: result.ok,\n detail: result.detail,\n });\n }\n\n // 5. S3 reachability (only if probe provided)\n if (options.probeBucket && env.FILE_NEXT_BUCKET && env.FILE_NEXT_REGION) {\n const result = await options.probeBucket(env.FILE_NEXT_BUCKET, env.FILE_NEXT_REGION);\n checks.push({\n name: \"bucket reachable\",\n ok: result.ok,\n detail: result.detail,\n });\n }\n\n const allOk = checks.every((c) => c.ok);\n return { checks, allOk };\n}\n\n/**\n * Pretty-print the doctor report to stdout. Used by the CLI dispatcher.\n */\nexport function formatDoctorReport(checks: ReadonlyArray<DoctorCheck>): string {\n const lines = checks.map((c) => {\n const mark = c.ok ? \"✓\" : \"✗\";\n return ` ${mark} ${c.name.padEnd(28)} ${c.detail}`;\n });\n const passed = checks.filter((c) => c.ok).length;\n const total = checks.length;\n return [`file-next doctor: ${passed}/${total} checks passed`, ...lines].join(\"\\n\") + \"\\n\";\n}\n","/**\n * `file-next` CLI library — exports `dispatch()` and command parsers\n * for programmatic use AND for the bin shim.\n *\n * The actual binary entry point is `./bin.mjs` (in this directory;\n * tsup writes it to `dist/bin.mjs`). It imports `dispatch` from\n * here and handles the process.exit / stdout / stderr wiring.\n *\n * Why split it this way:\n * - Tests can import from `@/index` (this file) without triggering\n * any process.exit side effects.\n * - The bin file is tiny (3 lines) and exists solely to translate\n * `dispatch()`'s return value into the correct process exit +\n * stdout/stderr writes.\n * - No brittle `import.meta.url === process.argv[1]` heuristics\n * (those break on macOS symlinks like /tmp → /private/tmp).\n */\nimport { parseMigrateArgs, runMigrate, type MigrateHooks } from \"./migrate.js\";\nimport { parseReconcileArgs, runReconcile, type ReconcileHooks } from \"./reconcile.js\";\nimport { runDoctor, formatDoctorReport } from \"./doctor.js\";\n\nexport const VERSION = \"0.5.0\";\n\nexport function printHelp(version: string = VERSION): string {\n return `file-next CLI v${version}\n\nUsage:\n file-next <command> [options]\n\nCommands:\n migrate Apply pending metadata schema migrations\n --adapter=<postgres|sqlite> (required)\n reconcile Detect and (optionally) fix S3 vs metadata drift\n --tenant=<id> (required)\n --dry-run (default: false)\n doctor Diagnose the local environment\n (checks env vars + adapter reachability)\n\nFlags:\n --version Print the CLI version\n --help Print this help\n\nExit codes:\n 0 success\n 1 expected failure (drift detected, doctor found an issue)\n 2 configuration / runtime error\n`;\n}\n\nexport interface DispatchHooks {\n readonly migrate?: MigrateHooks;\n readonly reconcile?: ReconcileHooks;\n}\n\n/**\n * Dispatch a CLI invocation to the right command and return the exit code.\n *\n * Does NOT call `process.exit` — that is the caller's job (the bin\n * script at the bottom of this file). Pure dispatch logic so it\n * can be unit-tested without spawning a subprocess.\n */\nexport async function dispatch(\n argv: ReadonlyArray<string>,\n hooks: DispatchHooks = {},\n version: string = VERSION,\n): Promise<{ exitCode: number; stdout: string; stderr: string }> {\n const out = { stdout: \"\", stderr: \"\" };\n const writeOut = (s: string) => (out.stdout += s);\n const writeErr = (s: string) => (out.stderr += s);\n const command = argv[0];\n\n switch (command) {\n case \"--version\":\n case \"-v\":\n writeOut(`file-next ${version}\\n`);\n return { exitCode: 0, ...out };\n\n case \"--help\":\n case \"-h\":\n writeOut(printHelp(version));\n return { exitCode: 0, ...out };\n\n case undefined:\n writeOut(printHelp(version));\n return { exitCode: 1, ...out };\n\n case \"migrate\": {\n const parsed = parseMigrateArgs(argv.slice(1));\n if (\"error\" in parsed) {\n writeErr(`file-next migrate: ${parsed.error}\\n`);\n return { exitCode: 2, ...out };\n }\n try {\n const result = await runMigrate(parsed, hooks.migrate);\n writeOut(\n `Applied ${result.applied.length} migration(s); ${result.pending.length} pending.\\n`,\n );\n return { exitCode: 0, ...out };\n } catch (err) {\n writeErr(`file-next migrate: ${(err as Error).message}\\n`);\n return { exitCode: 2, ...out };\n }\n }\n\n case \"reconcile\": {\n const parsed = parseReconcileArgs(argv.slice(1));\n if (\"error\" in parsed) {\n writeErr(`file-next reconcile: ${parsed.error}\\n`);\n return { exitCode: 2, ...out };\n }\n try {\n const result = await runReconcile(parsed, hooks.reconcile);\n writeOut(\n `tenant=${parsed.tenant} drift: missingInS3=${result.drift.missingInS3.length} ` +\n `orphansInS3=${result.drift.orphansInS3.length} ` +\n `fixed=${result.drift.fixedCount} ` +\n `${parsed.dryRun ? \"(dry-run)\" : \"\"}\\n`,\n );\n return { exitCode: result.hadDrift ? 1 : 0, ...out };\n } catch (err) {\n writeErr(`file-next reconcile: ${(err as Error).message}\\n`);\n return { exitCode: 2, ...out };\n }\n }\n\n case \"doctor\": {\n try {\n const { checks, allOk } = await runDoctor();\n writeOut(formatDoctorReport(checks));\n return { exitCode: allOk ? 0 : 1, ...out };\n } catch (err) {\n writeErr(`file-next doctor: ${(err as Error).message}\\n`);\n return { exitCode: 2, ...out };\n }\n }\n\n default:\n writeErr(`Unknown command: ${command}\\nRun 'file-next --help'.\\n`);\n return { exitCode: 2, ...out };\n }\n}\n\n// No top-level process.exit / dispatch here. The bin shim\n// (`bin.mjs` in this directory; emitted to `dist/bin.mjs` by tsup)\n// is the only entry point that calls `dispatch()` and writes\n// its stdout/stderr to the actual process streams.\n","/**\n * Default CLI hooks — actually open the store / filesystem from env.\n */\nimport {\n asTenantId,\n createFileSystem,\n createMemoryFileSystem,\n createPostgresStore,\n createSqliteStore,\n parseFileSystemConfig,\n type MetadataStore,\n} from \"@vryzel/file-next\";\nimport { createWriteThrough } from \"@vryzel/file-next/sync\";\nimport type { MigrateHooks } from \"./migrate.js\";\nimport type { ReconcileHooks } from \"./reconcile.js\";\n\nconst sqlitePath = (): string =>\n process.env.FILE_NEXT_SQLITE_PATH ?? \".data/metadata.db\";\n\nconst postgresUrl = (): string => {\n const url = process.env.FILE_NEXT_DATABASE_URL ?? process.env.DATABASE_URL;\n if (!url) {\n throw new Error(\"FILE_NEXT_DATABASE_URL (or DATABASE_URL) is required\");\n }\n return url;\n};\n\nconst openStore = (adapter: \"postgres\" | \"sqlite\"): MetadataStore =>\n adapter === \"postgres\"\n ? createPostgresStore({ connectionString: postgresUrl() })\n : createSqliteStore({ path: sqlitePath() });\n\nconst openFileSystem = (store: MetadataStore) => {\n const parsed = parseFileSystemConfig({\n provider: process.env.FILE_NEXT_PROVIDER,\n bucket: process.env.FILE_NEXT_BUCKET,\n region: process.env.FILE_NEXT_REGION,\n endpoint: process.env.FILE_NEXT_ENDPOINT,\n credentials:\n process.env.FILE_NEXT_ACCESS_KEY_ID && process.env.FILE_NEXT_SECRET_ACCESS_KEY\n ? {\n accessKeyId: process.env.FILE_NEXT_ACCESS_KEY_ID,\n secretAccessKey: process.env.FILE_NEXT_SECRET_ACCESS_KEY,\n }\n : undefined,\n forcePathStyle:\n process.env.FILE_NEXT_PROVIDER === \"r2\"\n ? true\n : process.env.FILE_NEXT_FORCE_PATH_STYLE === \"true\",\n });\n if (!parsed.ok) {\n return createMemoryFileSystem({ store });\n }\n return createFileSystem(parsed.value, { store });\n};\n\nexport const defaultMigrateHooks = (): MigrateHooks => ({\n resolveMigrator: async (adapter) => ({\n listPending: async () => [],\n apply: async () => {\n const store = openStore(adapter);\n await store.listChildren({\n tenantId: asTenantId(\"cli\"),\n parentId: null,\n limit: 1,\n });\n return [adapter === \"postgres\" ? \"postgres-nodes\" : \"sqlite-nodes\"];\n },\n }),\n});\n\nexport const defaultReconcileHooks = (): ReconcileHooks => ({\n runSync: async ({ tenant, dryRun }) => {\n const adapter =\n process.env.FILE_NEXT_DATABASE_URL || process.env.DATABASE_URL\n ? \"postgres\"\n : \"sqlite\";\n const store = openStore(adapter);\n const fs = openFileSystem(store);\n const wt = createWriteThrough(fs, store);\n const result = await wt.reconcile({ tenantId: tenant, dryRun });\n if (!result.ok) throw result.error;\n return {\n missingInS3: [...result.value.missingInS3],\n orphansInS3: [...result.value.orphansInS3],\n fixedCount: result.value.fixed,\n };\n },\n});\n","#!/usr/bin/env node\n/**\n * Bin shim for `@vryzel/file-next-cli`.\n *\n * This file is the only place that translates the pure `dispatch()`\n * return value into process streams + exit. Splitting it out keeps\n * `src/index.ts` side-effect-free (importable by tests without\n * triggering any process.exit).\n *\n * tsup writes this file to `dist/bin.mjs`; the `bin` field in\n * package.json points the `file-next` command at it.\n */\nimport { dispatch } from \"./index.js\";\nimport { defaultMigrateHooks, defaultReconcileHooks } from \"./runtime.js\";\n\ndispatch(process.argv.slice(2), {\n migrate: defaultMigrateHooks(),\n reconcile: defaultReconcileHooks(),\n})\n .then(({ exitCode, stdout, stderr }) => {\n if (stdout) process.stdout.write(stdout);\n if (stderr) process.stderr.write(stderr);\n process.exit(exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(`Unexpected error: ${(err as Error).message}\\n`);\n process.exit(2);\n });\n"]}
1
+ {"version":3,"sources":["../src/migrate.ts","../src/reconcile.ts","../src/doctor.ts","../src/index.ts","../src/runtime.ts","../src/bin.ts"],"names":["parseArgs"],"mappings":"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","file":"bin.js","sourcesContent":["/**\n * `file-next migrate` — apply pending metadata schema migrations.\n *\n * v0.1 scope: prints the migration plan and reports what would run.\n * Actual migration execution is delegated to the adapter package's\n * migrator (Drizzle for postgres/sqlite). This CLI is the orchestrator\n * that resolves the adapter from the config and invokes it.\n *\n * Usage:\n * file-next migrate --adapter=postgres\n * file-next migrate --adapter=sqlite\n *\n * Exit codes:\n * 0 — migrations applied (or none pending)\n * 1 — configuration error (missing / unknown adapter)\n * 2 — migration failed (the adapter threw)\n */\nimport { parseArgs } from \"node:util\";\n\nexport interface MigrateOptions {\n readonly adapter: \"postgres\" | \"sqlite\";\n}\n\nexport function parseMigrateArgs(argv: ReadonlyArray<string>): MigrateOptions | { error: string } {\n let parsed: ReturnType<typeof parseArgs>;\n try {\n parsed = parseArgs({\n args: argv as string[],\n options: {\n adapter: { type: \"string\", short: \"a\" },\n },\n strict: true,\n allowPositionals: false,\n });\n } catch (err) {\n return { error: (err as Error).message };\n }\n\n const adapter = parsed.values.adapter;\n if (adapter !== \"postgres\" && adapter !== \"sqlite\") {\n return { error: \"--adapter must be either 'postgres' or 'sqlite'\" };\n }\n return { adapter };\n}\n\nexport interface MigrateResult {\n readonly applied: ReadonlyArray<string>;\n readonly pending: ReadonlyArray<string>;\n}\n\n/**\n * Resolve and run migrations against the chosen adapter.\n *\n * v0.1 implementation: returns a static plan rather than touching\n * a real database. The hook for `runAdapterMigrations` is the\n * integration point for v0.2 (which will load Drizzle's migrator\n * dynamically based on the adapter choice).\n */\nexport interface MigrateHooks {\n /** Resolves the migrator implementation. May throw on unsupported adapter. */\n resolveMigrator: (adapter: MigrateOptions[\"adapter\"]) => Promise<{\n listPending: () => Promise<ReadonlyArray<string>>;\n apply: () => Promise<ReadonlyArray<string>>;\n }>;\n}\n\nexport async function runMigrate(\n options: MigrateOptions,\n hooks: MigrateHooks = {\n // Default no-op migrator so the CLI can be smoke-tested without\n // a real DB. The real adapter wiring lives in the consumer's\n // app where drizzle-orm is a runtime dep.\n resolveMigrator: async () => ({\n listPending: async () => [],\n apply: async () => [],\n }),\n },\n): Promise<MigrateResult> {\n const migrator = await hooks.resolveMigrator(options.adapter);\n const pending = await migrator.listPending();\n const applied = await migrator.apply();\n return { applied, pending };\n}\n","/**\n * `file-next reconcile` — find and (optionally) fix drift between\n * the S3 bucket and the metadata index.\n *\n * Usage:\n * file-next reconcile --tenant=demo\n * file-next reconcile --tenant=demo --dry-run\n *\n * Exit codes:\n * 0 — no drift detected (or all drift fixed)\n * 1 — drift detected AND not fixed (dry-run mode)\n * 2 — configuration / connectivity error\n */\nimport { parseArgs } from \"node:util\";\n\nexport interface ReconcileOptions {\n readonly tenant: string;\n readonly dryRun: boolean;\n}\n\nexport function parseReconcileArgs(\n argv: ReadonlyArray<string>,\n): ReconcileOptions | { error: string } {\n let parsed: ReturnType<typeof parseArgs>;\n try {\n parsed = parseArgs({\n args: argv as string[],\n options: {\n tenant: { type: \"string\", short: \"t\" },\n \"dry-run\": { type: \"boolean\", default: false },\n },\n strict: true,\n allowPositionals: false,\n });\n } catch (err) {\n return { error: (err as Error).message };\n }\n\n if (typeof parsed.values.tenant !== \"string\" || parsed.values.tenant.length === 0) {\n return { error: \"--tenant is required\" };\n }\n return {\n tenant: parsed.values.tenant,\n dryRun: parsed.values[\"dry-run\"] === true,\n };\n}\n\nexport interface DriftReport {\n readonly missingInS3: ReadonlyArray<string>;\n readonly orphansInS3: ReadonlyArray<string>;\n readonly fixedCount: number;\n}\n\nexport interface ReconcileResult {\n readonly drift: DriftReport;\n readonly hadDrift: boolean;\n}\n\n/**\n * Run a reconcile cycle for a tenant.\n *\n * v0.1: the actual sync logic is provided via the `runSync` hook so\n * this CLI is testable without a real Postgres / S3 backend. The\n * consumer wires the real `reconcile()` from `@vryzel/file-next/sync` into\n * the hook when running the CLI against production.\n */\nexport interface ReconcileHooks {\n runSync: (input: {\n tenant: string;\n dryRun: boolean;\n }) => Promise<DriftReport>;\n}\n\nexport async function runReconcile(\n options: ReconcileOptions,\n hooks: ReconcileHooks = {\n runSync: async () => ({\n missingInS3: [],\n orphansInS3: [],\n fixedCount: 0,\n }),\n },\n): Promise<ReconcileResult> {\n const drift = await hooks.runSync({\n tenant: options.tenant,\n dryRun: options.dryRun,\n });\n const hadDrift = drift.missingInS3.length + drift.orphansInS3.length > 0;\n return { drift, hadDrift };\n}\n","/**\n * `file-next doctor` — diagnose the local environment for file-next.\n *\n * Checks (each pass/fail):\n * - Required env vars present (FILE_NEXT_BUCKET, FILE_NEXT_REGION,\n * FILE_NEXT_PROVIDER, and adapter-specific creds).\n * - Metadata DB reachable (postgres / sqlite path resolves).\n * - S3 bucket reachable (HEAD on the configured bucket returns 200\n * or 403 — not network error).\n *\n * Usage:\n * file-next doctor\n *\n * Exit codes:\n * 0 — all checks pass\n * 1 — one or more checks failed (printed to stderr)\n * 2 — invalid configuration (e.g. unknown provider)\n */\n\nexport interface DoctorCheck {\n readonly name: string;\n readonly ok: boolean;\n readonly detail: string;\n}\n\nexport interface DoctorOptions {\n /** Env vars to validate. The CLI uses process.env by default. */\n readonly env?: NodeJS.ProcessEnv;\n /** Optional callback to test DB reachability. */\n readonly probeDb?: (provider: \"postgres\" | \"sqlite\") => Promise<{ ok: boolean; detail: string }>;\n /** Optional callback to test S3 bucket reachability. */\n readonly probeBucket?: (bucket: string, region: string) => Promise<{ ok: boolean; detail: string }>;\n}\n\nconst REQUIRED_VARS = [\"FILE_NEXT_BUCKET\", \"FILE_NEXT_REGION\", \"FILE_NEXT_PROVIDER\"] as const;\nconst POSTGRES_VARS = [\"FILE_NEXT_PG_HOST\", \"FILE_NEXT_PG_USER\", \"FILE_NEXT_PG_DATABASE\"] as const;\nconst SQLITE_VAR = \"FILE_NEXT_SQLITE_PATH\";\n\n/**\n * Run all checks. Returns a list of { name, ok, detail } records.\n * The CLI formats and prints them; the caller decides the exit code.\n */\nexport async function runDoctor(options: DoctorOptions = {}): Promise<{\n checks: ReadonlyArray<DoctorCheck>;\n allOk: boolean;\n}> {\n const env = options.env ?? process.env;\n const checks: DoctorCheck[] = [];\n\n // 1. Provider declared\n const provider = env.FILE_NEXT_PROVIDER;\n if (!provider) {\n checks.push({\n name: \"provider declared\",\n ok: false,\n detail: \"FILE_NEXT_PROVIDER is not set (expected 's3' or 'r2')\",\n });\n } else if (provider !== \"s3\" && provider !== \"r2\") {\n checks.push({\n name: \"provider valid\",\n ok: false,\n detail: `FILE_NEXT_PROVIDER=\"${provider}\" is not recognized (expected 's3' or 'r2')`,\n });\n } else {\n checks.push({\n name: \"provider valid\",\n ok: true,\n detail: `FILE_NEXT_PROVIDER=\"${provider}\"`,\n });\n }\n\n // 2. Required env vars present\n for (const v of REQUIRED_VARS) {\n checks.push({\n name: `env ${v}`,\n ok: typeof env[v] === \"string\" && env[v]!.length > 0,\n detail: env[v] ? `${v}=\"${env[v]}\"` : `${v} is missing`,\n });\n }\n\n // 3. Adapter-specific vars\n const adapter = provider === \"r2\" ? \"postgres\" : (env.FILE_NEXT_ADAPTER ?? \"memory\");\n if (adapter === \"postgres\") {\n for (const v of POSTGRES_VARS) {\n checks.push({\n name: `env ${v}`,\n ok: typeof env[v] === \"string\" && env[v]!.length > 0,\n detail: env[v] ? `${v}=\"${env[v]}\"` : `${v} is missing (required for postgres adapter)`,\n });\n }\n } else if (adapter === \"sqlite\") {\n checks.push({\n name: `env ${SQLITE_VAR}`,\n ok: typeof env[SQLITE_VAR] === \"string\" && env[SQLITE_VAR]!.length > 0,\n detail: env[SQLITE_VAR] ?? `${SQLITE_VAR} is missing (required for sqlite adapter)`,\n });\n }\n\n // 4. DB reachability (only if probe provided)\n if (options.probeDb && (adapter === \"postgres\" || adapter === \"sqlite\")) {\n const result = await options.probeDb(adapter);\n checks.push({\n name: `${adapter} reachable`,\n ok: result.ok,\n detail: result.detail,\n });\n }\n\n // 5. S3 reachability (only if probe provided)\n if (options.probeBucket && env.FILE_NEXT_BUCKET && env.FILE_NEXT_REGION) {\n const result = await options.probeBucket(env.FILE_NEXT_BUCKET, env.FILE_NEXT_REGION);\n checks.push({\n name: \"bucket reachable\",\n ok: result.ok,\n detail: result.detail,\n });\n }\n\n const allOk = checks.every((c) => c.ok);\n return { checks, allOk };\n}\n\n/**\n * Pretty-print the doctor report to stdout. Used by the CLI dispatcher.\n */\nexport function formatDoctorReport(checks: ReadonlyArray<DoctorCheck>): string {\n const lines = checks.map((c) => {\n const mark = c.ok ? \"✓\" : \"✗\";\n return ` ${mark} ${c.name.padEnd(28)} ${c.detail}`;\n });\n const passed = checks.filter((c) => c.ok).length;\n const total = checks.length;\n return [`file-next doctor: ${passed}/${total} checks passed`, ...lines].join(\"\\n\") + \"\\n\";\n}\n","/**\n * `file-next` CLI library — exports `dispatch()` and command parsers\n * for programmatic use AND for the bin shim.\n *\n * The actual binary entry point is `./bin.mjs` (in this directory;\n * tsup writes it to `dist/bin.mjs`). It imports `dispatch` from\n * here and handles the process.exit / stdout / stderr wiring.\n *\n * Why split it this way:\n * - Tests can import from `@/index` (this file) without triggering\n * any process.exit side effects.\n * - The bin file is tiny (3 lines) and exists solely to translate\n * `dispatch()`'s return value into the correct process exit +\n * stdout/stderr writes.\n * - No brittle `import.meta.url === process.argv[1]` heuristics\n * (those break on macOS symlinks like /tmp → /private/tmp).\n */\nimport { parseMigrateArgs, runMigrate, type MigrateHooks } from \"./migrate.js\";\nimport { parseReconcileArgs, runReconcile, type ReconcileHooks } from \"./reconcile.js\";\nimport { runDoctor, formatDoctorReport } from \"./doctor.js\";\n\nexport const VERSION = \"0.5.1\";\n\nexport function printHelp(version: string = VERSION): string {\n return `file-next CLI v${version}\n\nUsage:\n file-next <command> [options]\n\nCommands:\n migrate Apply pending metadata schema migrations\n --adapter=<postgres|sqlite> (required)\n reconcile Detect and (optionally) fix S3 vs metadata drift\n --tenant=<id> (required)\n --dry-run (default: false)\n doctor Diagnose the local environment\n (checks env vars + adapter reachability)\n\nFlags:\n --version Print the CLI version\n --help Print this help\n\nExit codes:\n 0 success\n 1 expected failure (drift detected, doctor found an issue)\n 2 configuration / runtime error\n`;\n}\n\nexport interface DispatchHooks {\n readonly migrate?: MigrateHooks;\n readonly reconcile?: ReconcileHooks;\n}\n\n/**\n * Dispatch a CLI invocation to the right command and return the exit code.\n *\n * Does NOT call `process.exit` — that is the caller's job (the bin\n * script at the bottom of this file). Pure dispatch logic so it\n * can be unit-tested without spawning a subprocess.\n */\nexport async function dispatch(\n argv: ReadonlyArray<string>,\n hooks: DispatchHooks = {},\n version: string = VERSION,\n): Promise<{ exitCode: number; stdout: string; stderr: string }> {\n const out = { stdout: \"\", stderr: \"\" };\n const writeOut = (s: string) => (out.stdout += s);\n const writeErr = (s: string) => (out.stderr += s);\n const command = argv[0];\n\n switch (command) {\n case \"--version\":\n case \"-v\":\n writeOut(`file-next ${version}\\n`);\n return { exitCode: 0, ...out };\n\n case \"--help\":\n case \"-h\":\n writeOut(printHelp(version));\n return { exitCode: 0, ...out };\n\n case undefined:\n writeOut(printHelp(version));\n return { exitCode: 1, ...out };\n\n case \"migrate\": {\n const parsed = parseMigrateArgs(argv.slice(1));\n if (\"error\" in parsed) {\n writeErr(`file-next migrate: ${parsed.error}\\n`);\n return { exitCode: 2, ...out };\n }\n try {\n const result = await runMigrate(parsed, hooks.migrate);\n writeOut(\n `Applied ${result.applied.length} migration(s); ${result.pending.length} pending.\\n`,\n );\n return { exitCode: 0, ...out };\n } catch (err) {\n writeErr(`file-next migrate: ${(err as Error).message}\\n`);\n return { exitCode: 2, ...out };\n }\n }\n\n case \"reconcile\": {\n const parsed = parseReconcileArgs(argv.slice(1));\n if (\"error\" in parsed) {\n writeErr(`file-next reconcile: ${parsed.error}\\n`);\n return { exitCode: 2, ...out };\n }\n try {\n const result = await runReconcile(parsed, hooks.reconcile);\n writeOut(\n `tenant=${parsed.tenant} drift: missingInS3=${result.drift.missingInS3.length} ` +\n `orphansInS3=${result.drift.orphansInS3.length} ` +\n `fixed=${result.drift.fixedCount} ` +\n `${parsed.dryRun ? \"(dry-run)\" : \"\"}\\n`,\n );\n return { exitCode: result.hadDrift ? 1 : 0, ...out };\n } catch (err) {\n writeErr(`file-next reconcile: ${(err as Error).message}\\n`);\n return { exitCode: 2, ...out };\n }\n }\n\n case \"doctor\": {\n try {\n const { checks, allOk } = await runDoctor();\n writeOut(formatDoctorReport(checks));\n return { exitCode: allOk ? 0 : 1, ...out };\n } catch (err) {\n writeErr(`file-next doctor: ${(err as Error).message}\\n`);\n return { exitCode: 2, ...out };\n }\n }\n\n default:\n writeErr(`Unknown command: ${command}\\nRun 'file-next --help'.\\n`);\n return { exitCode: 2, ...out };\n }\n}\n\n// No top-level process.exit / dispatch here. The bin shim\n// (`bin.mjs` in this directory; emitted to `dist/bin.mjs` by tsup)\n// is the only entry point that calls `dispatch()` and writes\n// its stdout/stderr to the actual process streams.\n","/**\n * Default CLI hooks — actually open the store / filesystem from env.\n */\nimport {\n asTenantId,\n createFileSystem,\n createMemoryFileSystem,\n createPostgresStore,\n createSqliteStore,\n parseFileSystemConfig,\n type MetadataStore,\n} from \"@vryzel/file-next\";\nimport { createWriteThrough } from \"@vryzel/file-next/sync\";\nimport type { MigrateHooks } from \"./migrate.js\";\nimport type { ReconcileHooks } from \"./reconcile.js\";\n\nconst sqlitePath = (): string =>\n process.env.FILE_NEXT_SQLITE_PATH ?? \".data/metadata.db\";\n\nconst postgresUrl = (): string => {\n const url = process.env.FILE_NEXT_DATABASE_URL ?? process.env.DATABASE_URL;\n if (!url) {\n throw new Error(\"FILE_NEXT_DATABASE_URL (or DATABASE_URL) is required\");\n }\n return url;\n};\n\nconst openStore = (adapter: \"postgres\" | \"sqlite\"): MetadataStore =>\n adapter === \"postgres\"\n ? createPostgresStore({ connectionString: postgresUrl() })\n : createSqliteStore({ path: sqlitePath() });\n\nconst openFileSystem = (store: MetadataStore) => {\n const parsed = parseFileSystemConfig({\n provider: process.env.FILE_NEXT_PROVIDER,\n bucket: process.env.FILE_NEXT_BUCKET,\n region: process.env.FILE_NEXT_REGION,\n endpoint: process.env.FILE_NEXT_ENDPOINT,\n credentials:\n process.env.FILE_NEXT_ACCESS_KEY_ID && process.env.FILE_NEXT_SECRET_ACCESS_KEY\n ? {\n accessKeyId: process.env.FILE_NEXT_ACCESS_KEY_ID,\n secretAccessKey: process.env.FILE_NEXT_SECRET_ACCESS_KEY,\n }\n : undefined,\n forcePathStyle:\n process.env.FILE_NEXT_PROVIDER === \"r2\"\n ? true\n : process.env.FILE_NEXT_FORCE_PATH_STYLE === \"true\",\n });\n if (!parsed.ok) {\n return createMemoryFileSystem({ store });\n }\n return createFileSystem(parsed.value, { store });\n};\n\nexport const defaultMigrateHooks = (): MigrateHooks => ({\n resolveMigrator: async (adapter) => ({\n listPending: async () => [],\n apply: async () => {\n const store = openStore(adapter);\n await store.listChildren({\n tenantId: asTenantId(\"cli\"),\n parentId: null,\n limit: 1,\n });\n return [adapter === \"postgres\" ? \"postgres-nodes\" : \"sqlite-nodes\"];\n },\n }),\n});\n\nexport const defaultReconcileHooks = (): ReconcileHooks => ({\n runSync: async ({ tenant, dryRun }) => {\n const adapter =\n process.env.FILE_NEXT_DATABASE_URL || process.env.DATABASE_URL\n ? \"postgres\"\n : \"sqlite\";\n const store = openStore(adapter);\n const fs = openFileSystem(store);\n const wt = createWriteThrough(fs, store);\n const result = await wt.reconcile({ tenantId: tenant, dryRun });\n if (!result.ok) throw result.error;\n return {\n missingInS3: [...result.value.missingInS3],\n orphansInS3: [...result.value.orphansInS3],\n fixedCount: result.value.fixed,\n };\n },\n});\n","#!/usr/bin/env node\n/**\n * Bin shim for `@vryzel/file-next-cli`.\n *\n * This file is the only place that translates the pure `dispatch()`\n * return value into process streams + exit. Splitting it out keeps\n * `src/index.ts` side-effect-free (importable by tests without\n * triggering any process.exit).\n *\n * tsup writes this file to `dist/bin.mjs`; the `bin` field in\n * package.json points the `file-next` command at it.\n */\nimport { dispatch } from \"./index.js\";\nimport { defaultMigrateHooks, defaultReconcileHooks } from \"./runtime.js\";\n\ndispatch(process.argv.slice(2), {\n migrate: defaultMigrateHooks(),\n reconcile: defaultReconcileHooks(),\n})\n .then(({ exitCode, stdout, stderr }) => {\n if (stdout) process.stdout.write(stdout);\n if (stderr) process.stderr.write(stderr);\n process.exit(exitCode);\n })\n .catch((err: unknown) => {\n process.stderr.write(`Unexpected error: ${(err as Error).message}\\n`);\n process.exit(2);\n });\n"]}
package/dist/index.d.ts CHANGED
@@ -55,7 +55,7 @@ interface ReconcileHooks {
55
55
  * (those break on macOS symlinks like /tmp → /private/tmp).
56
56
  */
57
57
 
58
- declare const VERSION = "0.5.0";
58
+ declare const VERSION = "0.5.1";
59
59
  declare function printHelp(version?: string): string;
60
60
  interface DispatchHooks {
61
61
  readonly migrate?: MigrateHooks;
package/dist/index.js CHANGED
@@ -153,7 +153,7 @@ function formatDoctorReport(checks) {
153
153
  }
154
154
 
155
155
  // src/index.ts
156
- var VERSION = "0.5.0";
156
+ var VERSION = "0.5.1";
157
157
  function printHelp(version = VERSION) {
158
158
  return `file-next CLI v${version}
159
159
 
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/migrate.ts","../src/reconcile.ts","../src/doctor.ts","../src/index.ts"],"names":["parseArgs"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * `file-next migrate` — apply pending metadata schema migrations.\n *\n * v0.1 scope: prints the migration plan and reports what would run.\n * Actual migration execution is delegated to the adapter package's\n * migrator (Drizzle for postgres/sqlite). This CLI is the orchestrator\n * that resolves the adapter from the config and invokes it.\n *\n * Usage:\n * file-next migrate --adapter=postgres\n * file-next migrate --adapter=sqlite\n *\n * Exit codes:\n * 0 — migrations applied (or none pending)\n * 1 — configuration error (missing / unknown adapter)\n * 2 — migration failed (the adapter threw)\n */\nimport { parseArgs } from \"node:util\";\n\nexport interface MigrateOptions {\n readonly adapter: \"postgres\" | \"sqlite\";\n}\n\nexport function parseMigrateArgs(argv: ReadonlyArray<string>): MigrateOptions | { error: string } {\n let parsed: ReturnType<typeof parseArgs>;\n try {\n parsed = parseArgs({\n args: argv as string[],\n options: {\n adapter: { type: \"string\", short: \"a\" },\n },\n strict: true,\n allowPositionals: false,\n });\n } catch (err) {\n return { error: (err as Error).message };\n }\n\n const adapter = parsed.values.adapter;\n if (adapter !== \"postgres\" && adapter !== \"sqlite\") {\n return { error: \"--adapter must be either 'postgres' or 'sqlite'\" };\n }\n return { adapter };\n}\n\nexport interface MigrateResult {\n readonly applied: ReadonlyArray<string>;\n readonly pending: ReadonlyArray<string>;\n}\n\n/**\n * Resolve and run migrations against the chosen adapter.\n *\n * v0.1 implementation: returns a static plan rather than touching\n * a real database. The hook for `runAdapterMigrations` is the\n * integration point for v0.2 (which will load Drizzle's migrator\n * dynamically based on the adapter choice).\n */\nexport interface MigrateHooks {\n /** Resolves the migrator implementation. May throw on unsupported adapter. */\n resolveMigrator: (adapter: MigrateOptions[\"adapter\"]) => Promise<{\n listPending: () => Promise<ReadonlyArray<string>>;\n apply: () => Promise<ReadonlyArray<string>>;\n }>;\n}\n\nexport async function runMigrate(\n options: MigrateOptions,\n hooks: MigrateHooks = {\n // Default no-op migrator so the CLI can be smoke-tested without\n // a real DB. The real adapter wiring lives in the consumer's\n // app where drizzle-orm is a runtime dep.\n resolveMigrator: async () => ({\n listPending: async () => [],\n apply: async () => [],\n }),\n },\n): Promise<MigrateResult> {\n const migrator = await hooks.resolveMigrator(options.adapter);\n const pending = await migrator.listPending();\n const applied = await migrator.apply();\n return { applied, pending };\n}\n","/**\n * `file-next reconcile` — find and (optionally) fix drift between\n * the S3 bucket and the metadata index.\n *\n * Usage:\n * file-next reconcile --tenant=demo\n * file-next reconcile --tenant=demo --dry-run\n *\n * Exit codes:\n * 0 — no drift detected (or all drift fixed)\n * 1 — drift detected AND not fixed (dry-run mode)\n * 2 — configuration / connectivity error\n */\nimport { parseArgs } from \"node:util\";\n\nexport interface ReconcileOptions {\n readonly tenant: string;\n readonly dryRun: boolean;\n}\n\nexport function parseReconcileArgs(\n argv: ReadonlyArray<string>,\n): ReconcileOptions | { error: string } {\n let parsed: ReturnType<typeof parseArgs>;\n try {\n parsed = parseArgs({\n args: argv as string[],\n options: {\n tenant: { type: \"string\", short: \"t\" },\n \"dry-run\": { type: \"boolean\", default: false },\n },\n strict: true,\n allowPositionals: false,\n });\n } catch (err) {\n return { error: (err as Error).message };\n }\n\n if (typeof parsed.values.tenant !== \"string\" || parsed.values.tenant.length === 0) {\n return { error: \"--tenant is required\" };\n }\n return {\n tenant: parsed.values.tenant,\n dryRun: parsed.values[\"dry-run\"] === true,\n };\n}\n\nexport interface DriftReport {\n readonly missingInS3: ReadonlyArray<string>;\n readonly orphansInS3: ReadonlyArray<string>;\n readonly fixedCount: number;\n}\n\nexport interface ReconcileResult {\n readonly drift: DriftReport;\n readonly hadDrift: boolean;\n}\n\n/**\n * Run a reconcile cycle for a tenant.\n *\n * v0.1: the actual sync logic is provided via the `runSync` hook so\n * this CLI is testable without a real Postgres / S3 backend. The\n * consumer wires the real `reconcile()` from `@vryzel/file-next/sync` into\n * the hook when running the CLI against production.\n */\nexport interface ReconcileHooks {\n runSync: (input: {\n tenant: string;\n dryRun: boolean;\n }) => Promise<DriftReport>;\n}\n\nexport async function runReconcile(\n options: ReconcileOptions,\n hooks: ReconcileHooks = {\n runSync: async () => ({\n missingInS3: [],\n orphansInS3: [],\n fixedCount: 0,\n }),\n },\n): Promise<ReconcileResult> {\n const drift = await hooks.runSync({\n tenant: options.tenant,\n dryRun: options.dryRun,\n });\n const hadDrift = drift.missingInS3.length + drift.orphansInS3.length > 0;\n return { drift, hadDrift };\n}\n","/**\n * `file-next doctor` — diagnose the local environment for file-next.\n *\n * Checks (each pass/fail):\n * - Required env vars present (FILE_NEXT_BUCKET, FILE_NEXT_REGION,\n * FILE_NEXT_PROVIDER, and adapter-specific creds).\n * - Metadata DB reachable (postgres / sqlite path resolves).\n * - S3 bucket reachable (HEAD on the configured bucket returns 200\n * or 403 — not network error).\n *\n * Usage:\n * file-next doctor\n *\n * Exit codes:\n * 0 — all checks pass\n * 1 — one or more checks failed (printed to stderr)\n * 2 — invalid configuration (e.g. unknown provider)\n */\n\nexport interface DoctorCheck {\n readonly name: string;\n readonly ok: boolean;\n readonly detail: string;\n}\n\nexport interface DoctorOptions {\n /** Env vars to validate. The CLI uses process.env by default. */\n readonly env?: NodeJS.ProcessEnv;\n /** Optional callback to test DB reachability. */\n readonly probeDb?: (provider: \"postgres\" | \"sqlite\") => Promise<{ ok: boolean; detail: string }>;\n /** Optional callback to test S3 bucket reachability. */\n readonly probeBucket?: (bucket: string, region: string) => Promise<{ ok: boolean; detail: string }>;\n}\n\nconst REQUIRED_VARS = [\"FILE_NEXT_BUCKET\", \"FILE_NEXT_REGION\", \"FILE_NEXT_PROVIDER\"] as const;\nconst POSTGRES_VARS = [\"FILE_NEXT_PG_HOST\", \"FILE_NEXT_PG_USER\", \"FILE_NEXT_PG_DATABASE\"] as const;\nconst SQLITE_VAR = \"FILE_NEXT_SQLITE_PATH\";\n\n/**\n * Run all checks. Returns a list of { name, ok, detail } records.\n * The CLI formats and prints them; the caller decides the exit code.\n */\nexport async function runDoctor(options: DoctorOptions = {}): Promise<{\n checks: ReadonlyArray<DoctorCheck>;\n allOk: boolean;\n}> {\n const env = options.env ?? process.env;\n const checks: DoctorCheck[] = [];\n\n // 1. Provider declared\n const provider = env.FILE_NEXT_PROVIDER;\n if (!provider) {\n checks.push({\n name: \"provider declared\",\n ok: false,\n detail: \"FILE_NEXT_PROVIDER is not set (expected 's3' or 'r2')\",\n });\n } else if (provider !== \"s3\" && provider !== \"r2\") {\n checks.push({\n name: \"provider valid\",\n ok: false,\n detail: `FILE_NEXT_PROVIDER=\"${provider}\" is not recognized (expected 's3' or 'r2')`,\n });\n } else {\n checks.push({\n name: \"provider valid\",\n ok: true,\n detail: `FILE_NEXT_PROVIDER=\"${provider}\"`,\n });\n }\n\n // 2. Required env vars present\n for (const v of REQUIRED_VARS) {\n checks.push({\n name: `env ${v}`,\n ok: typeof env[v] === \"string\" && env[v]!.length > 0,\n detail: env[v] ? `${v}=\"${env[v]}\"` : `${v} is missing`,\n });\n }\n\n // 3. Adapter-specific vars\n const adapter = provider === \"r2\" ? \"postgres\" : (env.FILE_NEXT_ADAPTER ?? \"memory\");\n if (adapter === \"postgres\") {\n for (const v of POSTGRES_VARS) {\n checks.push({\n name: `env ${v}`,\n ok: typeof env[v] === \"string\" && env[v]!.length > 0,\n detail: env[v] ? `${v}=\"${env[v]}\"` : `${v} is missing (required for postgres adapter)`,\n });\n }\n } else if (adapter === \"sqlite\") {\n checks.push({\n name: `env ${SQLITE_VAR}`,\n ok: typeof env[SQLITE_VAR] === \"string\" && env[SQLITE_VAR]!.length > 0,\n detail: env[SQLITE_VAR] ?? `${SQLITE_VAR} is missing (required for sqlite adapter)`,\n });\n }\n\n // 4. DB reachability (only if probe provided)\n if (options.probeDb && (adapter === \"postgres\" || adapter === \"sqlite\")) {\n const result = await options.probeDb(adapter);\n checks.push({\n name: `${adapter} reachable`,\n ok: result.ok,\n detail: result.detail,\n });\n }\n\n // 5. S3 reachability (only if probe provided)\n if (options.probeBucket && env.FILE_NEXT_BUCKET && env.FILE_NEXT_REGION) {\n const result = await options.probeBucket(env.FILE_NEXT_BUCKET, env.FILE_NEXT_REGION);\n checks.push({\n name: \"bucket reachable\",\n ok: result.ok,\n detail: result.detail,\n });\n }\n\n const allOk = checks.every((c) => c.ok);\n return { checks, allOk };\n}\n\n/**\n * Pretty-print the doctor report to stdout. Used by the CLI dispatcher.\n */\nexport function formatDoctorReport(checks: ReadonlyArray<DoctorCheck>): string {\n const lines = checks.map((c) => {\n const mark = c.ok ? \"✓\" : \"✗\";\n return ` ${mark} ${c.name.padEnd(28)} ${c.detail}`;\n });\n const passed = checks.filter((c) => c.ok).length;\n const total = checks.length;\n return [`file-next doctor: ${passed}/${total} checks passed`, ...lines].join(\"\\n\") + \"\\n\";\n}\n","/**\n * `file-next` CLI library — exports `dispatch()` and command parsers\n * for programmatic use AND for the bin shim.\n *\n * The actual binary entry point is `./bin.mjs` (in this directory;\n * tsup writes it to `dist/bin.mjs`). It imports `dispatch` from\n * here and handles the process.exit / stdout / stderr wiring.\n *\n * Why split it this way:\n * - Tests can import from `@/index` (this file) without triggering\n * any process.exit side effects.\n * - The bin file is tiny (3 lines) and exists solely to translate\n * `dispatch()`'s return value into the correct process exit +\n * stdout/stderr writes.\n * - No brittle `import.meta.url === process.argv[1]` heuristics\n * (those break on macOS symlinks like /tmp → /private/tmp).\n */\nimport { parseMigrateArgs, runMigrate, type MigrateHooks } from \"./migrate.js\";\nimport { parseReconcileArgs, runReconcile, type ReconcileHooks } from \"./reconcile.js\";\nimport { runDoctor, formatDoctorReport } from \"./doctor.js\";\n\nexport const VERSION = \"0.5.0\";\n\nexport function printHelp(version: string = VERSION): string {\n return `file-next CLI v${version}\n\nUsage:\n file-next <command> [options]\n\nCommands:\n migrate Apply pending metadata schema migrations\n --adapter=<postgres|sqlite> (required)\n reconcile Detect and (optionally) fix S3 vs metadata drift\n --tenant=<id> (required)\n --dry-run (default: false)\n doctor Diagnose the local environment\n (checks env vars + adapter reachability)\n\nFlags:\n --version Print the CLI version\n --help Print this help\n\nExit codes:\n 0 success\n 1 expected failure (drift detected, doctor found an issue)\n 2 configuration / runtime error\n`;\n}\n\nexport interface DispatchHooks {\n readonly migrate?: MigrateHooks;\n readonly reconcile?: ReconcileHooks;\n}\n\n/**\n * Dispatch a CLI invocation to the right command and return the exit code.\n *\n * Does NOT call `process.exit` — that is the caller's job (the bin\n * script at the bottom of this file). Pure dispatch logic so it\n * can be unit-tested without spawning a subprocess.\n */\nexport async function dispatch(\n argv: ReadonlyArray<string>,\n hooks: DispatchHooks = {},\n version: string = VERSION,\n): Promise<{ exitCode: number; stdout: string; stderr: string }> {\n const out = { stdout: \"\", stderr: \"\" };\n const writeOut = (s: string) => (out.stdout += s);\n const writeErr = (s: string) => (out.stderr += s);\n const command = argv[0];\n\n switch (command) {\n case \"--version\":\n case \"-v\":\n writeOut(`file-next ${version}\\n`);\n return { exitCode: 0, ...out };\n\n case \"--help\":\n case \"-h\":\n writeOut(printHelp(version));\n return { exitCode: 0, ...out };\n\n case undefined:\n writeOut(printHelp(version));\n return { exitCode: 1, ...out };\n\n case \"migrate\": {\n const parsed = parseMigrateArgs(argv.slice(1));\n if (\"error\" in parsed) {\n writeErr(`file-next migrate: ${parsed.error}\\n`);\n return { exitCode: 2, ...out };\n }\n try {\n const result = await runMigrate(parsed, hooks.migrate);\n writeOut(\n `Applied ${result.applied.length} migration(s); ${result.pending.length} pending.\\n`,\n );\n return { exitCode: 0, ...out };\n } catch (err) {\n writeErr(`file-next migrate: ${(err as Error).message}\\n`);\n return { exitCode: 2, ...out };\n }\n }\n\n case \"reconcile\": {\n const parsed = parseReconcileArgs(argv.slice(1));\n if (\"error\" in parsed) {\n writeErr(`file-next reconcile: ${parsed.error}\\n`);\n return { exitCode: 2, ...out };\n }\n try {\n const result = await runReconcile(parsed, hooks.reconcile);\n writeOut(\n `tenant=${parsed.tenant} drift: missingInS3=${result.drift.missingInS3.length} ` +\n `orphansInS3=${result.drift.orphansInS3.length} ` +\n `fixed=${result.drift.fixedCount} ` +\n `${parsed.dryRun ? \"(dry-run)\" : \"\"}\\n`,\n );\n return { exitCode: result.hadDrift ? 1 : 0, ...out };\n } catch (err) {\n writeErr(`file-next reconcile: ${(err as Error).message}\\n`);\n return { exitCode: 2, ...out };\n }\n }\n\n case \"doctor\": {\n try {\n const { checks, allOk } = await runDoctor();\n writeOut(formatDoctorReport(checks));\n return { exitCode: allOk ? 0 : 1, ...out };\n } catch (err) {\n writeErr(`file-next doctor: ${(err as Error).message}\\n`);\n return { exitCode: 2, ...out };\n }\n }\n\n default:\n writeErr(`Unknown command: ${command}\\nRun 'file-next --help'.\\n`);\n return { exitCode: 2, ...out };\n }\n}\n\n// No top-level process.exit / dispatch here. The bin shim\n// (`bin.mjs` in this directory; emitted to `dist/bin.mjs` by tsup)\n// is the only entry point that calls `dispatch()` and writes\n// its stdout/stderr to the actual process streams.\n"]}
1
+ {"version":3,"sources":["../src/migrate.ts","../src/reconcile.ts","../src/doctor.ts","../src/index.ts"],"names":["parseArgs"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * `file-next migrate` — apply pending metadata schema migrations.\n *\n * v0.1 scope: prints the migration plan and reports what would run.\n * Actual migration execution is delegated to the adapter package's\n * migrator (Drizzle for postgres/sqlite). This CLI is the orchestrator\n * that resolves the adapter from the config and invokes it.\n *\n * Usage:\n * file-next migrate --adapter=postgres\n * file-next migrate --adapter=sqlite\n *\n * Exit codes:\n * 0 — migrations applied (or none pending)\n * 1 — configuration error (missing / unknown adapter)\n * 2 — migration failed (the adapter threw)\n */\nimport { parseArgs } from \"node:util\";\n\nexport interface MigrateOptions {\n readonly adapter: \"postgres\" | \"sqlite\";\n}\n\nexport function parseMigrateArgs(argv: ReadonlyArray<string>): MigrateOptions | { error: string } {\n let parsed: ReturnType<typeof parseArgs>;\n try {\n parsed = parseArgs({\n args: argv as string[],\n options: {\n adapter: { type: \"string\", short: \"a\" },\n },\n strict: true,\n allowPositionals: false,\n });\n } catch (err) {\n return { error: (err as Error).message };\n }\n\n const adapter = parsed.values.adapter;\n if (adapter !== \"postgres\" && adapter !== \"sqlite\") {\n return { error: \"--adapter must be either 'postgres' or 'sqlite'\" };\n }\n return { adapter };\n}\n\nexport interface MigrateResult {\n readonly applied: ReadonlyArray<string>;\n readonly pending: ReadonlyArray<string>;\n}\n\n/**\n * Resolve and run migrations against the chosen adapter.\n *\n * v0.1 implementation: returns a static plan rather than touching\n * a real database. The hook for `runAdapterMigrations` is the\n * integration point for v0.2 (which will load Drizzle's migrator\n * dynamically based on the adapter choice).\n */\nexport interface MigrateHooks {\n /** Resolves the migrator implementation. May throw on unsupported adapter. */\n resolveMigrator: (adapter: MigrateOptions[\"adapter\"]) => Promise<{\n listPending: () => Promise<ReadonlyArray<string>>;\n apply: () => Promise<ReadonlyArray<string>>;\n }>;\n}\n\nexport async function runMigrate(\n options: MigrateOptions,\n hooks: MigrateHooks = {\n // Default no-op migrator so the CLI can be smoke-tested without\n // a real DB. The real adapter wiring lives in the consumer's\n // app where drizzle-orm is a runtime dep.\n resolveMigrator: async () => ({\n listPending: async () => [],\n apply: async () => [],\n }),\n },\n): Promise<MigrateResult> {\n const migrator = await hooks.resolveMigrator(options.adapter);\n const pending = await migrator.listPending();\n const applied = await migrator.apply();\n return { applied, pending };\n}\n","/**\n * `file-next reconcile` — find and (optionally) fix drift between\n * the S3 bucket and the metadata index.\n *\n * Usage:\n * file-next reconcile --tenant=demo\n * file-next reconcile --tenant=demo --dry-run\n *\n * Exit codes:\n * 0 — no drift detected (or all drift fixed)\n * 1 — drift detected AND not fixed (dry-run mode)\n * 2 — configuration / connectivity error\n */\nimport { parseArgs } from \"node:util\";\n\nexport interface ReconcileOptions {\n readonly tenant: string;\n readonly dryRun: boolean;\n}\n\nexport function parseReconcileArgs(\n argv: ReadonlyArray<string>,\n): ReconcileOptions | { error: string } {\n let parsed: ReturnType<typeof parseArgs>;\n try {\n parsed = parseArgs({\n args: argv as string[],\n options: {\n tenant: { type: \"string\", short: \"t\" },\n \"dry-run\": { type: \"boolean\", default: false },\n },\n strict: true,\n allowPositionals: false,\n });\n } catch (err) {\n return { error: (err as Error).message };\n }\n\n if (typeof parsed.values.tenant !== \"string\" || parsed.values.tenant.length === 0) {\n return { error: \"--tenant is required\" };\n }\n return {\n tenant: parsed.values.tenant,\n dryRun: parsed.values[\"dry-run\"] === true,\n };\n}\n\nexport interface DriftReport {\n readonly missingInS3: ReadonlyArray<string>;\n readonly orphansInS3: ReadonlyArray<string>;\n readonly fixedCount: number;\n}\n\nexport interface ReconcileResult {\n readonly drift: DriftReport;\n readonly hadDrift: boolean;\n}\n\n/**\n * Run a reconcile cycle for a tenant.\n *\n * v0.1: the actual sync logic is provided via the `runSync` hook so\n * this CLI is testable without a real Postgres / S3 backend. The\n * consumer wires the real `reconcile()` from `@vryzel/file-next/sync` into\n * the hook when running the CLI against production.\n */\nexport interface ReconcileHooks {\n runSync: (input: {\n tenant: string;\n dryRun: boolean;\n }) => Promise<DriftReport>;\n}\n\nexport async function runReconcile(\n options: ReconcileOptions,\n hooks: ReconcileHooks = {\n runSync: async () => ({\n missingInS3: [],\n orphansInS3: [],\n fixedCount: 0,\n }),\n },\n): Promise<ReconcileResult> {\n const drift = await hooks.runSync({\n tenant: options.tenant,\n dryRun: options.dryRun,\n });\n const hadDrift = drift.missingInS3.length + drift.orphansInS3.length > 0;\n return { drift, hadDrift };\n}\n","/**\n * `file-next doctor` — diagnose the local environment for file-next.\n *\n * Checks (each pass/fail):\n * - Required env vars present (FILE_NEXT_BUCKET, FILE_NEXT_REGION,\n * FILE_NEXT_PROVIDER, and adapter-specific creds).\n * - Metadata DB reachable (postgres / sqlite path resolves).\n * - S3 bucket reachable (HEAD on the configured bucket returns 200\n * or 403 — not network error).\n *\n * Usage:\n * file-next doctor\n *\n * Exit codes:\n * 0 — all checks pass\n * 1 — one or more checks failed (printed to stderr)\n * 2 — invalid configuration (e.g. unknown provider)\n */\n\nexport interface DoctorCheck {\n readonly name: string;\n readonly ok: boolean;\n readonly detail: string;\n}\n\nexport interface DoctorOptions {\n /** Env vars to validate. The CLI uses process.env by default. */\n readonly env?: NodeJS.ProcessEnv;\n /** Optional callback to test DB reachability. */\n readonly probeDb?: (provider: \"postgres\" | \"sqlite\") => Promise<{ ok: boolean; detail: string }>;\n /** Optional callback to test S3 bucket reachability. */\n readonly probeBucket?: (bucket: string, region: string) => Promise<{ ok: boolean; detail: string }>;\n}\n\nconst REQUIRED_VARS = [\"FILE_NEXT_BUCKET\", \"FILE_NEXT_REGION\", \"FILE_NEXT_PROVIDER\"] as const;\nconst POSTGRES_VARS = [\"FILE_NEXT_PG_HOST\", \"FILE_NEXT_PG_USER\", \"FILE_NEXT_PG_DATABASE\"] as const;\nconst SQLITE_VAR = \"FILE_NEXT_SQLITE_PATH\";\n\n/**\n * Run all checks. Returns a list of { name, ok, detail } records.\n * The CLI formats and prints them; the caller decides the exit code.\n */\nexport async function runDoctor(options: DoctorOptions = {}): Promise<{\n checks: ReadonlyArray<DoctorCheck>;\n allOk: boolean;\n}> {\n const env = options.env ?? process.env;\n const checks: DoctorCheck[] = [];\n\n // 1. Provider declared\n const provider = env.FILE_NEXT_PROVIDER;\n if (!provider) {\n checks.push({\n name: \"provider declared\",\n ok: false,\n detail: \"FILE_NEXT_PROVIDER is not set (expected 's3' or 'r2')\",\n });\n } else if (provider !== \"s3\" && provider !== \"r2\") {\n checks.push({\n name: \"provider valid\",\n ok: false,\n detail: `FILE_NEXT_PROVIDER=\"${provider}\" is not recognized (expected 's3' or 'r2')`,\n });\n } else {\n checks.push({\n name: \"provider valid\",\n ok: true,\n detail: `FILE_NEXT_PROVIDER=\"${provider}\"`,\n });\n }\n\n // 2. Required env vars present\n for (const v of REQUIRED_VARS) {\n checks.push({\n name: `env ${v}`,\n ok: typeof env[v] === \"string\" && env[v]!.length > 0,\n detail: env[v] ? `${v}=\"${env[v]}\"` : `${v} is missing`,\n });\n }\n\n // 3. Adapter-specific vars\n const adapter = provider === \"r2\" ? \"postgres\" : (env.FILE_NEXT_ADAPTER ?? \"memory\");\n if (adapter === \"postgres\") {\n for (const v of POSTGRES_VARS) {\n checks.push({\n name: `env ${v}`,\n ok: typeof env[v] === \"string\" && env[v]!.length > 0,\n detail: env[v] ? `${v}=\"${env[v]}\"` : `${v} is missing (required for postgres adapter)`,\n });\n }\n } else if (adapter === \"sqlite\") {\n checks.push({\n name: `env ${SQLITE_VAR}`,\n ok: typeof env[SQLITE_VAR] === \"string\" && env[SQLITE_VAR]!.length > 0,\n detail: env[SQLITE_VAR] ?? `${SQLITE_VAR} is missing (required for sqlite adapter)`,\n });\n }\n\n // 4. DB reachability (only if probe provided)\n if (options.probeDb && (adapter === \"postgres\" || adapter === \"sqlite\")) {\n const result = await options.probeDb(adapter);\n checks.push({\n name: `${adapter} reachable`,\n ok: result.ok,\n detail: result.detail,\n });\n }\n\n // 5. S3 reachability (only if probe provided)\n if (options.probeBucket && env.FILE_NEXT_BUCKET && env.FILE_NEXT_REGION) {\n const result = await options.probeBucket(env.FILE_NEXT_BUCKET, env.FILE_NEXT_REGION);\n checks.push({\n name: \"bucket reachable\",\n ok: result.ok,\n detail: result.detail,\n });\n }\n\n const allOk = checks.every((c) => c.ok);\n return { checks, allOk };\n}\n\n/**\n * Pretty-print the doctor report to stdout. Used by the CLI dispatcher.\n */\nexport function formatDoctorReport(checks: ReadonlyArray<DoctorCheck>): string {\n const lines = checks.map((c) => {\n const mark = c.ok ? \"✓\" : \"✗\";\n return ` ${mark} ${c.name.padEnd(28)} ${c.detail}`;\n });\n const passed = checks.filter((c) => c.ok).length;\n const total = checks.length;\n return [`file-next doctor: ${passed}/${total} checks passed`, ...lines].join(\"\\n\") + \"\\n\";\n}\n","/**\n * `file-next` CLI library — exports `dispatch()` and command parsers\n * for programmatic use AND for the bin shim.\n *\n * The actual binary entry point is `./bin.mjs` (in this directory;\n * tsup writes it to `dist/bin.mjs`). It imports `dispatch` from\n * here and handles the process.exit / stdout / stderr wiring.\n *\n * Why split it this way:\n * - Tests can import from `@/index` (this file) without triggering\n * any process.exit side effects.\n * - The bin file is tiny (3 lines) and exists solely to translate\n * `dispatch()`'s return value into the correct process exit +\n * stdout/stderr writes.\n * - No brittle `import.meta.url === process.argv[1]` heuristics\n * (those break on macOS symlinks like /tmp → /private/tmp).\n */\nimport { parseMigrateArgs, runMigrate, type MigrateHooks } from \"./migrate.js\";\nimport { parseReconcileArgs, runReconcile, type ReconcileHooks } from \"./reconcile.js\";\nimport { runDoctor, formatDoctorReport } from \"./doctor.js\";\n\nexport const VERSION = \"0.5.1\";\n\nexport function printHelp(version: string = VERSION): string {\n return `file-next CLI v${version}\n\nUsage:\n file-next <command> [options]\n\nCommands:\n migrate Apply pending metadata schema migrations\n --adapter=<postgres|sqlite> (required)\n reconcile Detect and (optionally) fix S3 vs metadata drift\n --tenant=<id> (required)\n --dry-run (default: false)\n doctor Diagnose the local environment\n (checks env vars + adapter reachability)\n\nFlags:\n --version Print the CLI version\n --help Print this help\n\nExit codes:\n 0 success\n 1 expected failure (drift detected, doctor found an issue)\n 2 configuration / runtime error\n`;\n}\n\nexport interface DispatchHooks {\n readonly migrate?: MigrateHooks;\n readonly reconcile?: ReconcileHooks;\n}\n\n/**\n * Dispatch a CLI invocation to the right command and return the exit code.\n *\n * Does NOT call `process.exit` — that is the caller's job (the bin\n * script at the bottom of this file). Pure dispatch logic so it\n * can be unit-tested without spawning a subprocess.\n */\nexport async function dispatch(\n argv: ReadonlyArray<string>,\n hooks: DispatchHooks = {},\n version: string = VERSION,\n): Promise<{ exitCode: number; stdout: string; stderr: string }> {\n const out = { stdout: \"\", stderr: \"\" };\n const writeOut = (s: string) => (out.stdout += s);\n const writeErr = (s: string) => (out.stderr += s);\n const command = argv[0];\n\n switch (command) {\n case \"--version\":\n case \"-v\":\n writeOut(`file-next ${version}\\n`);\n return { exitCode: 0, ...out };\n\n case \"--help\":\n case \"-h\":\n writeOut(printHelp(version));\n return { exitCode: 0, ...out };\n\n case undefined:\n writeOut(printHelp(version));\n return { exitCode: 1, ...out };\n\n case \"migrate\": {\n const parsed = parseMigrateArgs(argv.slice(1));\n if (\"error\" in parsed) {\n writeErr(`file-next migrate: ${parsed.error}\\n`);\n return { exitCode: 2, ...out };\n }\n try {\n const result = await runMigrate(parsed, hooks.migrate);\n writeOut(\n `Applied ${result.applied.length} migration(s); ${result.pending.length} pending.\\n`,\n );\n return { exitCode: 0, ...out };\n } catch (err) {\n writeErr(`file-next migrate: ${(err as Error).message}\\n`);\n return { exitCode: 2, ...out };\n }\n }\n\n case \"reconcile\": {\n const parsed = parseReconcileArgs(argv.slice(1));\n if (\"error\" in parsed) {\n writeErr(`file-next reconcile: ${parsed.error}\\n`);\n return { exitCode: 2, ...out };\n }\n try {\n const result = await runReconcile(parsed, hooks.reconcile);\n writeOut(\n `tenant=${parsed.tenant} drift: missingInS3=${result.drift.missingInS3.length} ` +\n `orphansInS3=${result.drift.orphansInS3.length} ` +\n `fixed=${result.drift.fixedCount} ` +\n `${parsed.dryRun ? \"(dry-run)\" : \"\"}\\n`,\n );\n return { exitCode: result.hadDrift ? 1 : 0, ...out };\n } catch (err) {\n writeErr(`file-next reconcile: ${(err as Error).message}\\n`);\n return { exitCode: 2, ...out };\n }\n }\n\n case \"doctor\": {\n try {\n const { checks, allOk } = await runDoctor();\n writeOut(formatDoctorReport(checks));\n return { exitCode: allOk ? 0 : 1, ...out };\n } catch (err) {\n writeErr(`file-next doctor: ${(err as Error).message}\\n`);\n return { exitCode: 2, ...out };\n }\n }\n\n default:\n writeErr(`Unknown command: ${command}\\nRun 'file-next --help'.\\n`);\n return { exitCode: 2, ...out };\n }\n}\n\n// No top-level process.exit / dispatch here. The bin shim\n// (`bin.mjs` in this directory; emitted to `dist/bin.mjs` by tsup)\n// is the only entry point that calls `dispatch()` and writes\n// its stdout/stderr to the actual process streams.\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vryzel/file-next-cli",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "CLI for file-next — migrate metadata schemas, reconcile drift between S3 and the metadata index, and diagnose environment / connectivity issues.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -21,7 +21,7 @@
21
21
  "README.md"
22
22
  ],
23
23
  "dependencies": {
24
- "@vryzel/file-next": "0.5.0"
24
+ "@vryzel/file-next": "0.5.2"
25
25
  },
26
26
  "devDependencies": {
27
27
  "tsup": "^8.3.5",