create-noy-db 0.9.0 → 0.10.0-pre.1

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/bin/noy-db.ts","../../src/commands/add.ts","../../src/commands/verify.ts","../../src/commands/rotate.ts","../../src/commands/shared.ts","../../src/commands/add-user.ts","../../src/commands/backup.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * The `noy-db` bin. Invoked from inside an existing project by\n * `pnpm exec noy-db <command>`, `npx noy-db <command>`, etc.\n *\n * Subcommands:\n *\n * add <collection> Scaffold store + page files\n * add user <id> <role> [--collections…] Grant a new user access\n * verify In-memory integrity check\n * rotate --vault … --user … Rotate DEKs for a vault\n * backup <target> --vault … … Dump a vault to a file\n * help Show usage\n *\n * Future subcommands (deferred to a follow-up):\n * seed Re-run the project's seeder script (needs\n * a design decision on how seed scripts auth)\n * backup s3://... S3 targets (would bundle @aws-sdk; lives in\n * an optional companion package instead)\n * restore <file> Load a dumped backup into a vault\n *\n * ## Dispatcher design\n *\n * The dispatcher is intentionally dead simple: read argv[2], pick\n * a handler, pass the remaining args. No flag DSL, no auto-help\n * generation. Each subcommand's argv parser lives next to the\n * subcommand itself so the wiring is obvious and the pure\n * subcommand functions stay testable in isolation.\n *\n * ## Secret handling invariants\n *\n * Every subcommand that unlocks a vault goes through the\n * shared `defaultReadSecret` helper (see `commands/shared.ts`).\n * That helper:\n *\n * - Uses @clack/prompts `password()` so nothing echoes to stdout\n * - Never logs the returned value\n * - Validates \"not empty\" up front; the real strength check\n * happens inside the core's KEK derivation\n * - Aborts the process on Ctrl-C before any I/O happens\n *\n * The secret never leaves the closure in which it was read —\n * every subcommand closes the Noydb instance in a `finally` block\n * so the KEK is cleared from memory on the way out.\n */\n\nimport pc from 'picocolors'\nimport { addCollection } from '../commands/add.js'\nimport { verifyIntegrity } from '../commands/verify.js'\nimport { rotate, type RotateOptions } from '../commands/rotate.js'\nimport { addUser, type AddUserOptions } from '../commands/add-user.js'\nimport { backup } from '../commands/backup.js'\nimport { assertRole, parseCollectionList } from '../commands/shared.js'\n\nconst HELP = `Usage: noy-db <command> [args]\n\nCommands:\n add <collection> Scaffold a new collection store + page\n add user <userId> <role> [options] Grant a new user access to a vault\n verify In-memory crypto integrity check\n rotate [options] Rotate DEKs for a vault\n backup <target> [options] Dump a vault to a file\n help Show this message\n\nCommon options for rotate / add user / backup:\n --dir <path> Data directory (file adapter). Default: ./data\n --vault <name> Vault (tenant) name. Required.\n --user <id> Your user id in the vault. Required.\n --collections <list> Comma-separated collection list. Format:\n rotate: invoices,clients\n add user: invoices:rw,clients:ro (operator/client only)\n\nExamples:\n noy-db add invoices\n noy-db add user accountant-ann operator --dir ./data --vault demo-co --user owner-alice --collections invoices:rw,clients:ro\n noy-db verify\n noy-db rotate --dir ./data --vault demo-co --user owner-alice\n noy-db backup ./backups/demo-co-2026-04-07.json --dir ./data --vault demo-co --user owner-alice\n\nRun from the root of a project that already has a noy-db file\nadapter directory in place. For new projects, use\n\\`npm create noy-db\\` instead.\n`\n\nasync function main(): Promise<void> {\n const [, , command, ...rest] = process.argv\n\n switch (command) {\n case undefined:\n case 'help':\n case '-h':\n case '--help':\n process.stdout.write(HELP)\n return\n\n case 'add':\n await runAdd(rest)\n return\n\n case 'verify':\n await runVerify()\n return\n\n case 'rotate':\n await runRotate(rest)\n return\n\n case 'backup':\n await runBackup(rest)\n return\n\n default:\n process.stderr.write(`${pc.red('error:')} unknown command '${command}'\\n\\n${HELP}`)\n process.exit(2)\n }\n}\n\n// ─── `add` dispatcher — branches between `add <collection>` and `add user …` ─\n\nasync function runAdd(args: string[]): Promise<void> {\n const first = args[0]\n if (first === 'user') {\n await runAddUser(args.slice(1))\n return\n }\n // Legacy path: `noy-db add <collection>` scaffolds store + page files.\n if (!first) {\n process.stderr.write(\n `${pc.red('error:')} \\`noy-db add\\` requires either a collection name or the \\`user\\` subcommand\\n\\n` +\n `Examples:\\n noy-db add invoices\\n noy-db add user alice operator ...\\n`,\n )\n process.exit(2)\n }\n try {\n const result = await addCollection({ name: first })\n process.stdout.write(`${pc.green('✔')} Created:\\n`)\n for (const file of result.files) {\n process.stdout.write(` ${pc.dim('→')} ${file}\\n`)\n }\n process.stdout.write(\n `\\nNext: visit ${pc.cyan(`/${first}`)} in your dev server, then edit ${pc.bold(`app/stores/${first}.ts`)} to match your domain.\\n`,\n )\n } catch (err) {\n process.stderr.write(`${pc.red('error:')} ${(err as Error).message}\\n`)\n process.exit(1)\n }\n}\n\n// ─── `verify` ──────────────────────────────────────────────────────────\n\nasync function runVerify(): Promise<void> {\n process.stdout.write(`${pc.dim('▸ Running noy-db integrity check…')}\\n`)\n const result = await verifyIntegrity()\n if (result.ok) {\n process.stdout.write(`${pc.green('✔')} ${result.message} ${pc.dim(`(${result.durationMs}ms)`)}\\n`)\n return\n }\n process.stderr.write(`${pc.red('✘')} ${result.message} ${pc.dim(`(${result.durationMs}ms)`)}\\n`)\n process.exit(1)\n}\n\n// ─── `add user` ────────────────────────────────────────────────────────\n\nasync function runAddUser(args: string[]): Promise<void> {\n // Positional: userId, role. Then flag bag.\n const userId = args[0]\n const roleInput = args[1]\n if (!userId || !roleInput) {\n process.stderr.write(\n `${pc.red('error:')} \\`noy-db add user\\` requires <userId> <role>\\n\\n` +\n `Example: noy-db add user ann operator --dir ./data --vault demo-co --user owner-alice --collections invoices:rw\\n`,\n )\n process.exit(2)\n }\n\n let role\n try {\n role = assertRole(roleInput)\n } catch (err) {\n process.stderr.write(`${pc.red('error:')} ${(err as Error).message}\\n`)\n process.exit(2)\n return\n }\n\n const flags = parseFlags(args.slice(2))\n const dir = flags.dir ?? './data'\n const vault = requireFlag(flags, 'vault')\n const callerUser = requireFlag(flags, 'user')\n\n const permissions = parsePermissions(flags['collections'])\n\n const opts: AddUserOptions = {\n dir,\n vault,\n callerUser,\n newUserId: userId,\n role,\n }\n if (flags['display-name']) opts.newUserDisplayName = flags['display-name']\n if (permissions) opts.permissions = permissions\n\n try {\n const result = await addUser(opts)\n process.stdout.write(\n `${pc.green('✔')} Granted ${pc.bold(result.role)} access to ${pc.cyan(result.userId)} in vault ${pc.cyan(vault)}.\\n`,\n )\n } catch (err) {\n process.stderr.write(`${pc.red('error:')} ${(err as Error).message}\\n`)\n process.exit(1)\n }\n}\n\n// ─── `rotate` ──────────────────────────────────────────────────────────\n\nasync function runRotate(args: string[]): Promise<void> {\n const flags = parseFlags(args)\n const dir = flags.dir ?? './data'\n const vault = requireFlag(flags, 'vault')\n const user = requireFlag(flags, 'user')\n\n const opts: RotateOptions = { dir, vault, user }\n const collections = parseCollectionList(flags['collections'])\n if (collections) opts.collections = collections\n\n try {\n const result = await rotate(opts)\n process.stdout.write(\n `${pc.green('✔')} Rotated ${pc.bold(String(result.rotated.length))} collection(s) in ${pc.cyan(vault)}:\\n`,\n )\n for (const name of result.rotated) {\n process.stdout.write(` ${pc.dim('→')} ${name}\\n`)\n }\n } catch (err) {\n process.stderr.write(`${pc.red('error:')} ${(err as Error).message}\\n`)\n process.exit(1)\n }\n}\n\n// ─── `backup` ──────────────────────────────────────────────────────────\n\nasync function runBackup(args: string[]): Promise<void> {\n const target = args[0]\n if (!target) {\n process.stderr.write(\n `${pc.red('error:')} \\`noy-db backup\\` requires a target path\\n\\n` +\n `Example: noy-db backup ./backups/demo.json --dir ./data --vault demo-co --user owner-alice\\n`,\n )\n process.exit(2)\n }\n\n const flags = parseFlags(args.slice(1))\n const dir = flags.dir ?? './data'\n const vault = requireFlag(flags, 'vault')\n const user = requireFlag(flags, 'user')\n\n try {\n const result = await backup({ dir, vault, user, target })\n process.stdout.write(\n `${pc.green('✔')} Wrote backup: ${pc.cyan(result.path)} ${pc.dim(`(${result.bytes} bytes)`)}\\n`,\n )\n } catch (err) {\n process.stderr.write(`${pc.red('error:')} ${(err as Error).message}\\n`)\n process.exit(1)\n }\n}\n\n// ─── Shared argv helpers ───────────────────────────────────────────────\n\n/**\n * Parse a sequence of `--key value` flag pairs into a record.\n * Boolean flags (`--flag` with no value) become `\"true\"`. Unknown\n * shapes (positional args after the known set) are ignored — the\n * caller has already peeled positionals before passing in.\n */\nfunction parseFlags(args: string[]): Record<string, string> {\n const out: Record<string, string> = {}\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]\n if (!arg || !arg.startsWith('--')) continue\n const key = arg.slice(2)\n const next = args[i + 1]\n if (next && !next.startsWith('--')) {\n out[key] = next\n i++\n } else {\n out[key] = 'true'\n }\n }\n return out\n}\n\nfunction requireFlag(flags: Record<string, string>, name: string): string {\n const value = flags[name]\n if (!value) {\n process.stderr.write(\n `${pc.red('error:')} missing required flag --${name}\\n`,\n )\n process.exit(2)\n }\n return value\n}\n\n/**\n * Parse `--collections invoices:rw,clients:ro` into a\n * `{ invoices: 'rw', clients: 'ro' }` record. Returns `null` when\n * the input is empty or undefined.\n */\nfunction parsePermissions(\n input: string | undefined,\n): Record<string, 'rw' | 'ro'> | null {\n if (!input) return null\n const out: Record<string, 'rw' | 'ro'> = {}\n for (const pair of input.split(',').map((s) => s.trim()).filter(Boolean)) {\n const [name, mode] = pair.split(':')\n if (!name || !mode || (mode !== 'rw' && mode !== 'ro')) {\n throw new Error(\n `Invalid --collections entry \"${pair}\" — expected \"name:rw\" or \"name:ro\"`,\n )\n }\n out[name] = mode\n }\n return Object.keys(out).length > 0 ? out : null\n}\n\nmain().catch((err: unknown) => {\n process.stderr.write(`${pc.red('fatal:')} ${(err as Error).message}\\n`)\n process.exit(1)\n})\n","/**\n * `noy-db add <collection>` — scaffold a new collection inside an existing\n * Nuxt 4 project that already has `@noy-db/in-nuxt` configured.\n *\n * The command writes two files:\n *\n * 1. `app/stores/<collection>.ts` — a `defineNoydbStore<T>()` call with\n * a placeholder `T` interface and one example field. The user fills\n * in the real shape after the file is created.\n *\n * 2. `app/pages/<collection>.vue` — a minimal CRUD page that lists,\n * adds, and deletes records. The store ID and collection name are\n * derived from the argument; everything else is boilerplate.\n *\n * The command refuses to overwrite existing files. If either target\n * already exists it logs which one and exits non-zero — the user has to\n * delete or move the file first. There's no `--force` because forcing an\n * overwrite of generated UI code is almost always a footgun in disguise.\n */\n\nimport { promises as fs } from 'node:fs'\nimport path from 'node:path'\n\nexport interface AddCollectionOptions {\n /** The collection name. Must be a lowercase identifier. */\n name: string\n /** Project root. Defaults to `process.cwd()`. */\n cwd?: string\n /** Compartment id to embed in the generated store. Defaults to `default`. */\n vault?: string\n}\n\n/**\n * Result returned to callers (the bin entry uses this to format output;\n * tests assert on the file paths).\n */\nexport interface AddCollectionResult {\n /** Files written, in the order they were created. */\n files: string[]\n}\n\n/**\n * Lowercase identifier check, narrower than the project-name check —\n * collection names become Vue component names, Pinia store IDs, AND TS\n * symbols, so we keep them simple: letters, digits, hyphens, must start\n * with a letter.\n */\nexport function validateCollectionName(name: string): string | null {\n if (!name) return 'Collection name is required'\n if (!/^[a-z][a-z0-9-]*$/.test(name)) {\n return 'Collection name must start with a lowercase letter and contain only lowercase letters, digits, or hyphens'\n }\n return null\n}\n\nexport async function addCollection(\n options: AddCollectionOptions,\n): Promise<AddCollectionResult> {\n const err = validateCollectionName(options.name)\n if (err) throw new Error(err)\n\n const cwd = options.cwd ?? process.cwd()\n const vault = options.vault ?? 'default'\n const name = options.name\n\n // Convert kebab-case to PascalCase for the TS interface name and the\n // Vue helper variable.\n const PascalName = name\n .split('-')\n .map((s) => (s.length === 0 ? s : (s[0]?.toUpperCase() ?? '') + s.slice(1)))\n .join('')\n const useFnName = `use${PascalName}`\n\n const storePath = path.join(cwd, 'app', 'stores', `${name}.ts`)\n const pagePath = path.join(cwd, 'app', 'pages', `${name}.vue`)\n\n // Refuse to overwrite. Doing both checks before writing either file\n // means a partial write is impossible — either both files land or\n // neither does.\n for (const target of [storePath, pagePath]) {\n if (await pathExists(target)) {\n throw new Error(`Refusing to overwrite existing file: ${target}`)\n }\n }\n\n await fs.mkdir(path.dirname(storePath), { recursive: true })\n await fs.mkdir(path.dirname(pagePath), { recursive: true })\n\n await fs.writeFile(storePath, renderStore(name, PascalName, useFnName, vault), 'utf8')\n await fs.writeFile(pagePath, renderPage(name, PascalName, useFnName), 'utf8')\n\n return { files: [storePath, pagePath] }\n}\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await fs.access(p)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Renders the `app/stores/<name>.ts` file. The interface is intentionally\n * minimal — `id` and `name` are the only fields. The user is expected to\n * extend it after generation; we'd rather not lock them into a domain\n * shape we guessed.\n */\nfunction renderStore(\n name: string,\n PascalName: string,\n useFnName: string,\n vault: string,\n): string {\n return `// Generated by \\`noy-db add ${name}\\`.\n//\n// Edit the ${PascalName} interface to match your domain, then call\n// \\`${useFnName}()\\` from any component.\n//\n// defineNoydbStore is auto-imported by @noy-db/in-nuxt. The vault\n// id is the tenant/company namespace — change it if you have multiple.\n\nexport interface ${PascalName} {\n id: string\n name: string\n}\n\nexport const ${useFnName} = defineNoydbStore<${PascalName}>('${name}', {\n vault: '${vault}',\n})\n`\n}\n\n/**\n * Renders the `app/pages/<name>.vue` page — list + add + delete. Picks\n * the simplest possible template-renderer pattern (`v-for`, no fancy\n * components) so the generated file is easy to read and modify.\n */\nfunction renderPage(name: string, PascalName: string, useFnName: string): string {\n return `<!--\n Generated by \\`noy-db add ${name}\\`.\n Visit /${name} in your dev server.\n-->\n<script setup lang=\"ts\">\nconst ${name} = ${useFnName}()\nawait ${name}.$ready\n\nfunction addOne() {\n ${name}.add({\n id: crypto.randomUUID(),\n name: 'New ${PascalName}',\n })\n}\n\nfunction removeOne(id: string) {\n ${name}.remove(id)\n}\n</script>\n\n<template>\n <main>\n <h1>${PascalName}</h1>\n <button @click=\"addOne\">Add ${PascalName}</button>\n <ul>\n <li v-for=\"item in ${name}.items\" :key=\"item.id\">\n {{ item.name }}\n <button @click=\"removeOne(item.id)\">Delete</button>\n </li>\n </ul>\n </main>\n</template>\n`\n}\n","/**\n * `noy-db verify` — end-to-end integrity check.\n *\n * Opens an in-memory NOYDB instance, writes a record, reads it back,\n * decrypts it, and asserts the round-trip is byte-identical. The check\n * exercises the full crypto path (PBKDF2 → KEK → DEK → AES-GCM) without\n * touching any user data on disk.\n *\n * Why an in-memory check is the right scope:\n * - It validates that @noy-db/core, @noy-db/memory, and the user's\n * installed Node version all agree on Web Crypto. That's the most\n * common silent failure for first-time installers.\n * - It cannot accidentally corrupt user data because there isn't any.\n * - It runs in well under one second, so users actually run it.\n *\n * What this command does NOT do (intentionally):\n * - Open the user's actual vault file/dynamo/s3/browser store.\n * That requires the user's secret — not something we want a CLI\n * `verify` command to prompt for. The full secret-driven verify\n * belongs in `nuxi noydb verify` once the auth story for CLIs lands\n * in. For now `noy-db verify` is the dependency-graph smoke test.\n */\n\nimport { createNoydb, memoryStore } from '@noy-db/hub'\n\nexport interface VerifyResult {\n /** `true` if the round-trip succeeded; `false` if anything diverged. */\n ok: boolean\n /** Human-readable status. Always set, even on success. */\n message: string\n /** Wall-clock time the integrity check took, in ms. */\n durationMs: number\n}\n\n/**\n * Runs the end-to-end check. Pure function — no console output, no\n * `process.exit`. The bin wrapper handles formatting and exit codes so\n * the function is trivial to call from tests.\n */\nexport async function verifyIntegrity(): Promise<VerifyResult> {\n const start = performance.now()\n try {\n const db = await createNoydb({\n store: memoryStore(),\n user: 'noy-db-verify',\n // The secret here is throwaway — the in-memory adapter never\n // persists anything, and the KEK is destroyed when we call close()\n // a few lines down. We use a non-trivial value just to exercise\n // PBKDF2 properly.\n secret: 'noy-db-verify-secret-2026',\n })\n const company = await db.openVault('verify-co')\n const collection = company.collection<{ id: string; n: number }>('verify')\n\n // Round-trip a single record. We pick a value that's small enough\n // to print on failure but large enough to ensure encryption isn't\n // accidentally a no-op.\n const original = { id: 'verify-1', n: 42 }\n await collection.put('verify-1', original)\n const got = await collection.get('verify-1')\n if (!got || got.id !== original.id || got.n !== original.n) {\n return fail(start, `Round-trip mismatch: got ${JSON.stringify(got)}`)\n }\n\n // Make sure the query DSL works too — this catches the case where\n // the user's @noy-db/core install is at (no query DSL) but the\n // CLI was updated to.\n const found = collection.query().where('n', '==', 42).toArray()\n if (found.length !== 1) {\n return fail(start, `Query DSL mismatch: expected 1 result, got ${found.length}`)\n }\n\n db.close()\n\n return {\n ok: true,\n message: 'noy-db integrity check passed',\n durationMs: Math.round(performance.now() - start),\n }\n } catch (err) {\n return fail(start, `Integrity check threw: ${(err as Error).message}`)\n }\n}\n\nfunction fail(start: number, message: string): VerifyResult {\n return {\n ok: false,\n message,\n durationMs: Math.round(performance.now() - start),\n }\n}\n","/**\n * `noy-db rotate` — rotate the DEKs for one or more collections in\n * a vault.\n *\n * What it does\n * ------------\n * For each target collection:\n *\n * 1. Generate a fresh DEK\n * 2. Decrypt every record with the old DEK\n * 3. Re-encrypt every record with the new DEK\n * 4. Re-wrap the new DEK into every remaining user's keyring\n *\n * The old DEKs become unreachable as soon as the keyring files are\n * updated. This is the \"just rotate\" path — nobody is revoked,\n * everybody keeps their current permissions, but the key material\n * is replaced.\n *\n * Why expose this as a CLI command\n * --------------------------------\n * Two real-world scenarios:\n *\n * 1. **Suspected key leak.** An operator lost a laptop, a\n * developer accidentally pasted a secret into a Slack\n * channel, a USB stick went missing. Even if you think the\n * secret is safe, rotating is cheap insurance.\n *\n * 2. **Scheduled rotation.** Some compliance regimes require\n * periodic key rotation regardless of exposure. A CLI makes\n * this scriptable from cron or a CI job.\n *\n * This module is test-first: all inputs are plain options, the\n * secret reader is injected, and the Noydb factory is\n * injectable. The production bin is a thin wrapper that defaults\n * those injections to their real implementations.\n */\n\nimport { withTeam } from '@noy-db/hub/team'\nimport { createNoydb, type Noydb, type NoydbStore } from '@noy-db/hub'\nimport { toFile } from '@noy-db/to-file'\nimport type { ReadSecret } from './shared.js'\nimport { defaultReadSecret } from './shared.js'\n\nexport interface RotateOptions {\n /** Directory containing the vault data (file adapter only). */\n dir: string\n /** Vault (tenant) name to rotate keys in. */\n vault: string\n /** The user id of the operator running the rotate. */\n user: string\n /**\n * Explicit list of collections to rotate. When undefined, the\n * rotation targets every collection the user has a DEK for —\n * resolved at run time by reading the vault snapshot.\n */\n collections?: string[]\n /** Injected secret reader. Defaults to the clack implementation. */\n readSecret?: ReadSecret\n /**\n * Injected Noydb factory. Production code leaves this undefined\n * and gets `createNoydb`; tests pass a constructor that builds\n * against an in-memory adapter.\n */\n createDb?: typeof createNoydb\n /**\n * Injected adapter factory. Production code leaves this undefined\n * and gets `toFile`; tests pass one that returns the shared\n * in-memory adapter their fixture used.\n */\n buildAdapter?: (dir: string) => NoydbStore\n}\n\nexport interface RotateResult {\n /** The collections that were actually rotated. */\n rotated: string[]\n}\n\n/**\n * Run the rotate flow against a file-adapter vault. Returns\n * the list of collections that were rotated so callers can display\n * it to the user.\n *\n * Throws `Error` on any auth/adapter/rotate failure. The bin\n * catches these and prints a friendly message; direct callers\n * (tests) can inspect the error message to assert specific\n * failure modes.\n */\nexport async function rotate(options: RotateOptions): Promise<RotateResult> {\n const readSecret = options.readSecret ?? defaultReadSecret\n const buildAdapter = options.buildAdapter ?? ((dir) => toFile({ dir }))\n const createDb = options.createDb ?? createNoydb\n\n // Read the secret BEFORE opening the database. This way a\n // cancelled prompt (Ctrl-C at the password entry) leaves the\n // adapter completely untouched — no files opened, no locks held.\n const secret = await readSecret(`Secret for ${options.user}`)\n\n let db: Noydb | null = null\n try {\n db = await createDb({\n store: buildAdapter(options.dir),\n user: options.user,\n secret,\n // Key rotation is the opt-in team capability (#267).\n teamStrategy: withTeam(),\n })\n\n // Resolve \"all collections\" by asking the vault. This\n // happens BEFORE rotate() is called, so the list is stable\n // across the operation — adding a new collection mid-rotate\n // would be a race we're not guarding against (single-writer\n // assumption applies).\n const vault = await db.openVault(options.vault)\n const targets = options.collections && options.collections.length > 0\n ? options.collections\n : await vault.collections()\n\n if (targets.length === 0) {\n throw new Error(\n `Vault \"${options.vault}\" has no collections to rotate.`,\n )\n }\n\n await db.rotate(options.vault, targets)\n return { rotated: targets }\n } finally {\n // Always close the DB on exit — success or failure. Close()\n // clears the KEK and DEKs from process memory, which is the\n // final line of defense if the secret somehow leaked\n // into a log line above this block.\n db?.close()\n }\n}\n","/**\n * Shared primitives for the interactive `noy-db` subcommands that\n * need to unlock a real vault.\n *\n * Three things live here:\n *\n * 1. `ReadSecret` — a tiny interface for \"prompt the user for\n * a secret\", with a test-friendly default. Subcommands take\n * this as an injected dependency so tests can short-circuit\n * the prompt without spawning a pty.\n *\n * 2. `defaultReadSecret` — the production implementation,\n * built on `@clack/prompts` `password()`. Never echoes the\n * value to the terminal, never logs it, clears it from the\n * returned promise after the caller consumes it.\n *\n * 3. `assertRole` — narrow unknown string input to the Role type\n * with a consistent error message.\n *\n * ## Why pull this out\n *\n * `rotate`, `addUser`, and `backup` all need the same \"prompt for\n * a secret\" shape and the same \"open a file adapter and get\n * back a Noydb instance\" shape. Duplicating it in three files would\n * drift over time; centralizing means one place to audit the\n * secret-handling contract (never log, never persist, clear\n * local variables after use).\n */\n\nimport { password, isCancel, cancel } from '@clack/prompts'\nimport type { Role } from '@noy-db/hub'\n\nconst VALID_ROLES = ['owner', 'admin', 'operator', 'viewer', 'client'] as const\n\n/**\n * Asynchronous secret reader. Production code passes\n * `defaultReadSecret`; tests pass a stub that returns a fixed\n * string without touching stdin.\n *\n * The `label` is shown to the user as the prompt message. It\n * should never contain the expected secret or any secret.\n */\nexport type ReadSecret = (label: string) => Promise<string>\n\n/**\n * Clack-based secret prompt. Cancellation (Ctrl-C) aborts the\n * process with exit code 1 — prompts are always the first thing to\n * fire in a subcommand, so aborting here doesn't leave the system\n * in a half-mutated state.\n */\nexport const defaultReadSecret: ReadSecret = async (label) => {\n const value = await password({\n message: label,\n // Basic sanity: reject empty strings up front. We don't enforce\n // length here because the caller's KEK-derivation step will\n // reject weak secrets with its own, richer error.\n validate: (v) => (v.length === 0 ? 'Secret cannot be empty' : undefined),\n })\n if (isCancel(value)) {\n cancel('Cancelled.')\n process.exit(1)\n }\n return value\n}\n\n/**\n * Narrow an unknown string to the `Role` type from @noy-db/core.\n * Used by the `add user` subcommand to validate the role argument\n * before passing it to `noydb.grant()`.\n */\nexport function assertRole(input: string): Role {\n if (!(VALID_ROLES as readonly string[]).includes(input)) {\n throw new Error(\n `Invalid role \"${input}\" — must be one of: ${VALID_ROLES.join(', ')}`,\n )\n }\n return input as Role\n}\n\n/**\n * Split a comma-separated collection list into an array of names,\n * trimming whitespace and dropping empties. Returns null if the\n * input itself is empty or undefined — the caller decides whether\n * that means \"all collections\" or \"error\".\n */\nexport function parseCollectionList(input: string | undefined): string[] | null {\n if (!input) return null\n const parts = input\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0)\n return parts.length > 0 ? parts : null\n}\n","/**\n * `noy-db add user <id> <role>` — grant a new user access to a\n * vault.\n *\n * What it does\n * ------------\n * Wraps `noydb.grant()` in the CLI's auth-prompt ritual:\n *\n * 1. Prompt the caller for their own secret (to unlock the\n * caller's keyring and derive the wrapping key).\n * 2. Prompt for the new user's secret.\n * 3. Prompt for confirmation of the new secret.\n * 4. Reject on mismatch.\n * 5. Call `noydb.grant(vault, { userId, role, secret, permissions })`.\n *\n * For owner/admin/viewer roles, every collection is granted\n * automatically (the core keyring.ts grant logic handles that via\n * the `permissions` field). For operator/client, the caller must\n * pass a `--collections` list because those roles need explicit\n * per-collection permissions.\n *\n * ## What this does NOT do\n *\n * - No email/invite flow — is about local-CLI key management,\n * not out-of-band user enrollment.\n * - No rollback on partial failure — `grant()` is atomic at the\n * core level (keyring file writes last, after DEK wrapping), so\n * partial-state-on-crash is already handled.\n */\n\nimport { withTeam } from '@noy-db/hub/team'\nimport { createNoydb, type Noydb, type NoydbStore, type Role } from '@noy-db/hub'\nimport { toFile } from '@noy-db/to-file'\nimport type { ReadSecret } from './shared.js'\nimport { defaultReadSecret } from './shared.js'\n\nexport interface AddUserOptions {\n /** Directory containing the vault data (file adapter only). */\n dir: string\n /** Vault (tenant) name to grant access to. */\n vault: string\n /** The user id of the caller running the grant. */\n callerUser: string\n /** The new user's id (must not already exist in the vault keyring). */\n newUserId: string\n /** The new user's display name — shown in UI and audit logs. Defaults to `newUserId`. */\n newUserDisplayName?: string\n /** The new user's role. */\n role: Role\n /**\n * Per-collection permissions. Required when `role` is operator or\n * client; ignored for owner/admin/viewer (they get everything\n * via the core's resolvePermissions logic).\n *\n * Shape: `{ invoices: 'rw', clients: 'ro' }`. CLI callers pass\n * `--collections invoices:rw,clients:ro` and the argv parser\n * converts it to this shape.\n */\n permissions?: Record<string, 'rw' | 'ro'>\n /** Injected secret reader. Defaults to the clack implementation. */\n readSecret?: ReadSecret\n /** Injected Noydb factory. */\n createDb?: typeof createNoydb\n /** Injected adapter factory. */\n buildAdapter?: (dir: string) => NoydbStore\n}\n\nexport interface AddUserResult {\n /** The userId that was granted access. */\n userId: string\n /** The role they were granted. */\n role: Role\n}\n\n/**\n * Run the grant flow. Two secret prompts: caller's, then new\n * user's (twice for confirmation). Calls `noydb.grant()` with the\n * collected values.\n */\nexport async function addUser(options: AddUserOptions): Promise<AddUserResult> {\n const readSecret = options.readSecret ?? defaultReadSecret\n const buildAdapter = options.buildAdapter ?? ((dir) => toFile({ dir }))\n const createDb = options.createDb ?? createNoydb\n\n // Operator/client roles NEED explicit permissions. Reject here\n // rather than in the middle of the grant, so the caller sees the\n // problem before any I/O happens.\n if (\n (options.role === 'operator' || options.role === 'client') &&\n (!options.permissions || Object.keys(options.permissions).length === 0)\n ) {\n throw new Error(\n `Role \"${options.role}\" requires explicit --collections — e.g. --collections invoices:rw,clients:ro`,\n )\n }\n\n const callerSecret = await readSecret(\n `Your secret (${options.callerUser})`,\n )\n const newSecret = await readSecret(\n `New secret for ${options.newUserId}`,\n )\n const confirmSecret = await readSecret(\n `Confirm secret for ${options.newUserId}`,\n )\n\n if (newSecret !== confirmSecret) {\n throw new Error(`Secrets do not match — grant aborted.`)\n }\n\n let db: Noydb | null = null\n try {\n db = await createDb({\n store: buildAdapter(options.dir),\n user: options.callerUser,\n secret: callerSecret,\n // Multi-user grant is the opt-in team capability (#267) — the CLI\n // add-user command is by definition a multi-user operation.\n teamStrategy: withTeam(),\n })\n\n // Build the grant options. Only include `permissions` when the\n // caller actually supplied them — otherwise the core's\n // resolvePermissions fills in the role defaults. The spread\n // (rather than post-assignment) keeps the object literal\n // compatible with `GrantOptions`'s readonly `permissions`.\n const grantOpts: Parameters<Noydb['grant']>[1] = {\n userId: options.newUserId,\n displayName: options.newUserDisplayName ?? options.newUserId,\n role: options.role,\n secret: newSecret,\n ...(options.permissions ? { permissions: options.permissions } : {}),\n }\n\n await db.grant(options.vault, grantOpts)\n\n return {\n userId: options.newUserId,\n role: options.role,\n }\n } finally {\n db?.close()\n }\n}\n","/**\n * `noy-db backup <target>` — dump a vault to a local file.\n *\n * What it does\n * ------------\n * Wraps `vault.dump()` in the CLI's auth-prompt ritual, then\n * writes the serialized backup to the requested path. As of,\n * `dump()` already produces a verifiable backup (embedded\n * ledgerHead, full `_ledger` / `_ledger_deltas` snapshots) — the\n * CLI just moves bytes; the integrity guarantees come from core.\n *\n * ## Target URI support\n *\n * ships **`file://` only** (or a plain filesystem path).\n * The issue spec originally called for `s3://` as well, but\n * wiring @aws-sdk into create-noy-db would defeat the\n * zero-runtime-deps story for the CLI package. S3 backup is\n * deferred to a follow-up that can live in @noy-db/s3-cli or a\n * similar optional companion package.\n *\n * Accepted forms:\n * - `file:///absolute/path.json`\n * - `file://./relative/path.json`\n * - `/absolute/path.json` (treated as `file://`)\n * - `./relative/path.json` (treated as `file://`)\n *\n * ## What this does NOT do\n *\n * - No encryption of the backup BEYOND what noy-db already does.\n * The dumped file is a valid noy-db backup, which means\n * individual records are still encrypted but the keyring is\n * included (wrapped with each user's KEK). Anyone who loads\n * the backup still needs the correct secret to read.\n * - No restore — that's a separate subcommand tracked as a\n * follow-up. For now users can restore via\n * `vault.load(backupString)` from their own app code.\n */\n\nimport { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport { createNoydb, type Noydb, type NoydbStore } from '@noy-db/hub'\nimport { withHistory } from '@noy-db/hub/history'\nimport { toFile } from '@noy-db/to-file'\nimport type { ReadSecret } from './shared.js'\nimport { defaultReadSecret } from './shared.js'\n\nexport interface BackupOptions {\n /** Directory containing the vault data (file adapter only). */\n dir: string\n /** Vault (tenant) name to back up. */\n vault: string\n /** The user id of the operator running the backup. */\n user: string\n /**\n * Where to write the backup. Accepts a `file://` URI or a plain\n * filesystem path. Relative paths resolve against `process.cwd()`.\n */\n target: string\n /** Injected secret reader. */\n readSecret?: ReadSecret\n /** Injected Noydb factory. */\n createDb?: typeof createNoydb\n /** Injected adapter factory. */\n buildAdapter?: (dir: string) => NoydbStore\n}\n\nexport interface BackupResult {\n /** Absolute filesystem path the backup was written to. */\n path: string\n /** Size of the serialized backup in bytes. */\n bytes: number\n}\n\n/**\n * Parse a backup target into an absolute filesystem path. Rejects\n * unsupported URI schemes (s3://, https://, etc.) early so the\n * caller doesn't silently write to the wrong place.\n */\nexport function resolveBackupTarget(target: string, cwd: string = process.cwd()): string {\n // Strip the `file://` prefix if present. The rest of the string\n // is treated as a filesystem path. We accept both `file:///abs`\n // (three slashes, absolute) and `file://./rel` (two slashes,\n // relative) because real-world users write both.\n let raw = target\n if (target.startsWith('file://')) {\n raw = target.slice('file://'.length)\n } else if (target.includes('://')) {\n // Any other scheme is unsupported.\n throw new Error(\n `Unsupported backup target scheme: \"${target.split('://')[0]}://\". ` +\n `Only file:// and plain filesystem paths are supported in. ` +\n `S3 backups will land in a follow-up.`,\n )\n }\n return path.resolve(cwd, raw)\n}\n\nexport async function backup(options: BackupOptions): Promise<BackupResult> {\n const readSecret = options.readSecret ?? defaultReadSecret\n const buildAdapter = options.buildAdapter ?? ((dir) => toFile({ dir }))\n const createDb = options.createDb ?? createNoydb\n\n // Resolve the target FIRST so a bad URI fails before any\n // secret is collected. This keeps the UX clean: a typo in\n // `s3://bucket/x` rejects without asking for a secret the user\n // would then have to type again.\n const absolutePath = resolveBackupTarget(options.target)\n\n const secret = await readSecret(`Secret for ${options.user}`)\n\n let db: Noydb | null = null\n try {\n db = await createDb({\n store: buildAdapter(options.dir),\n user: options.user,\n secret,\n // Opt into the history strategy so `vault.dump()` embeds\n // `ledgerHead` for tamper-evident verification at restore.\n // Source vaults that were never written with history simply\n // have an empty ledger; the embedded head is omitted in that\n // case and the backup is treated as legacy on load.\n historyStrategy: withHistory(),\n })\n const vault = await db.openVault(options.vault)\n const serialized = await vault.dump()\n\n // Make sure the parent directory exists. If the user passed\n // `./backups/2026/demo.json` and `./backups/2026` doesn't\n // exist yet, we create it. This is the common case for\n // scripted rotations dropping into a date-based folder.\n await fs.mkdir(path.dirname(absolutePath), { recursive: true })\n await fs.writeFile(absolutePath, serialized, 'utf8')\n\n return {\n path: absolutePath,\n bytes: Buffer.byteLength(serialized, 'utf8'),\n }\n } finally {\n db?.close()\n }\n}\n"],"mappings":"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","names":["createNoydb","createNoydb","withTeam","createNoydb","toFile","toFile","createNoydb","withTeam","fs","path","createNoydb","toFile","path","toFile","createNoydb","fs"]}
1
+ {"version":3,"sources":["../../src/wizard/augment.ts","../../src/bin/noy-db.ts","../../src/commands/resolve-bin.ts","../../src/commands/add.ts","../../src/commands/verify.ts","../../src/commands/rotate.ts","../../src/commands/shared.ts","../../src/commands/add-user.ts","../../src/commands/backup.ts","../../src/commands/pod/inspect.ts","../../src/commands/pod/verify.ts","../../src/commands/pod/describe.ts","../../src/commands/pod/config.ts","../../src/commands/pod/monitor.ts","../../src/commands/doctor.ts","../../src/commands/add-package.ts","../../src/wizard/render.ts"],"sourcesContent":["/**\n * Augment an existing Nuxt 4 project with `@noy-db/in-nuxt`.\n *\n * The entry point is `augmentNuxtConfig()`, which reads a\n * `nuxt.config.ts` (or `.js` / `.mjs`) via magicast, mutates the\n * AST to:\n *\n * 1. Add `'@noy-db/in-nuxt'` to the `modules` array (creating the\n * array if it doesn't exist)\n * 2. Add the `noydb: { adapter, pinia: true, devtools: true }`\n * config key (creating it if it doesn't exist)\n *\n * then generates the new source code, computes a unified diff\n * against the original, and either writes the result (with\n * optional user confirmation) or returns the diff unchanged for\n * `--dry-run`.\n *\n * ## Idempotency\n *\n * Re-running the wizard on an already-augmented project is a\n * no-op: we check whether the target values are already present\n * before mutating, and if both are, we short-circuit before\n * calling `generateCode()`. The caller sees\n * `{ changed: false, reason: 'already configured' }` and can\n * exit cleanly.\n *\n * ## Edge cases\n *\n * - `export default defineNuxtConfig({...})` — the common case,\n * handled directly.\n * - `export default {...}` — plain object literal, also handled.\n * - `export default someVar` — opaque reference, we bail with a\n * clear error telling the user to edit manually. We don't try\n * to chase the variable.\n * - `modules` declared as an object instead of an array — rare\n * but possible in some Nuxt tooling. We bail.\n * - `modules` already contains `'@noy-db/in-nuxt'` — idempotent skip.\n * - `noydb` key already present — we preserve the existing value\n * UNLESS `force: true` is passed (not yet surfaced in the CLI;\n * reserved for a future `--overwrite-config` flag).\n *\n * ## Why magicast instead of a regex/string-patch\n *\n * Regex-based config patching produces correct-looking code 90%\n * of the time and silently wrong code the other 10%. Anything\n * non-trivial (nested options, comments, multi-line strings,\n * conditional exports) breaks. Magicast walks a real Babel AST,\n * preserves unrelated formatting, and round-trips the file\n * cleanly — including comments, trailing commas, and property\n * order.\n */\n\nimport { promises as fs } from 'node:fs'\nimport { loadFile, generateCode, builders } from 'magicast'\nimport { createPatch } from 'diff'\nimport type { WizardAdapter } from './types.js'\n\nexport interface AugmentOptions {\n /** Absolute path of the nuxt.config.{ts,js,mjs} to patch. */\n configPath: string\n /** Adapter string to write into `noydb: { adapter }`. */\n adapter: WizardAdapter\n /**\n * When true, skip the write and return the would-be result.\n * The CLI's `--dry-run` flag threads through to here.\n */\n dryRun?: boolean\n}\n\nexport type AugmentResult =\n | {\n readonly kind: 'already-configured'\n readonly configPath: string\n readonly reason: string\n }\n | {\n readonly kind: 'proposed-change'\n readonly configPath: string\n readonly originalCode: string\n readonly newCode: string\n /** Unified diff string suitable for printing to the terminal. */\n readonly diff: string\n /** `true` when `options.dryRun` was set — the caller skips the write. */\n readonly dryRun: boolean\n }\n | {\n readonly kind: 'unsupported-shape'\n readonly configPath: string\n readonly reason: string\n }\n\n/**\n * Parse, mutate, and compute the diff. Does NOT write the file —\n * that's the caller's responsibility, after (optionally) prompting\n * the user for confirmation.\n *\n * The split exists so the CLI can interleave the prompt between\n * \"show the diff\" and \"write the file\", and so tests can inspect\n * the proposed change without any filesystem side effect beyond\n * the initial read.\n */\nexport async function augmentNuxtConfig(\n options: AugmentOptions,\n): Promise<AugmentResult> {\n const originalCode = await fs.readFile(options.configPath, 'utf8')\n const mod = await loadFile(options.configPath)\n\n // The entry point is whatever `export default` points at. In a\n // Nuxt config this is either `defineNuxtConfig({...})` or a\n // plain object literal.\n const exported = mod.exports.default as unknown\n if (exported === undefined || exported === null) {\n return {\n kind: 'unsupported-shape',\n configPath: options.configPath,\n reason: `${options.configPath} has no default export. Expected \\`export default defineNuxtConfig({...})\\` or \\`export default {...}\\`.`,\n }\n }\n\n // Both the `defineNuxtConfig({...})` call and the plain-object\n // case expose the config as a proxified object. For the function\n // call, magicast's proxy makes `exported.$args[0]` the config\n // object; for a plain object it's `exported` itself. We test\n // both shapes and pick the first one that has `modules` or can\n // accept a `modules` write.\n //\n // This looks a bit like JavaScript duck-typing because it IS\n // duck-typing — magicast's proxy tolerates either shape and the\n // API surface is identical once you reach the config object.\n const config = resolveConfigObject(exported)\n if (!config) {\n return {\n kind: 'unsupported-shape',\n configPath: options.configPath,\n reason: `Could not find the config object in ${options.configPath}. ` +\n `Expected \\`export default defineNuxtConfig({ modules: [], ... })\\` or a plain object literal.`,\n }\n }\n\n const skipReasons: string[] = []\n\n // --- Modules array -------------------------------------------------\n // Ensure `modules` is an array; create it if missing. Magicast's\n // proxy handles the array-vs-missing distinction via typeof checks\n // on the array's length property.\n const modulesRaw: unknown = config.modules\n let modulesWasMissing = false\n if (modulesRaw === undefined) {\n modulesWasMissing = true\n config.modules = []\n } else if (typeof modulesRaw !== 'object' || !isProxyArray(modulesRaw)) {\n return {\n kind: 'unsupported-shape',\n configPath: options.configPath,\n reason: `\\`modules\\` in ${options.configPath} is not an array literal. ` +\n `Edit it manually and re-run the wizard if you want to continue.`,\n }\n }\n\n // Push '@noy-db/in-nuxt' if it's not already listed. We compare by\n // stringification because magicast's proxy yields primitive\n // strings for literal entries.\n const modules = config.modules as string[]\n const alreadyHasModule = Array.from(modules).some((m) => String(m) === '@noy-db/in-nuxt')\n if (alreadyHasModule) {\n skipReasons.push('`@noy-db/in-nuxt` already in modules')\n } else {\n // Insert as a literal string so magicast writes `'@noy-db/in-nuxt'`\n // rather than an object wrapper.\n modules.push('@noy-db/in-nuxt')\n }\n\n // --- `noydb` config key --------------------------------------------\n // If the user already has a noydb key, preserve it — they might\n // have custom options we don't want to clobber. If it doesn't\n // exist, add it with the adapter the wizard collected.\n const noydbRaw: unknown = config.noydb\n if (noydbRaw !== undefined) {\n skipReasons.push('`noydb` key already set')\n } else {\n // `builders.raw` parses the literal into an AST node, so the\n // output is a real object expression in the generated code\n // instead of a stringified opaque blob.\n config.noydb = builders.raw(\n `{ adapter: '${options.adapter}', pinia: true, devtools: true }`,\n )\n }\n\n // If nothing changed, short-circuit. `modulesWasMissing` is a\n // change even if we didn't push anything, because we wrote an\n // empty array into the config — but that's an unusual case so\n // we lump it in with the regular \"changed\" path.\n if (skipReasons.length === 2 && !modulesWasMissing) {\n return {\n kind: 'already-configured',\n configPath: options.configPath,\n reason: skipReasons.join('; '),\n }\n }\n\n // Generate the new source, then compute a unified diff against\n // the original. `createPatch` is from the `diff` package — same\n // one `jest-diff` and `vitest` use under the hood. The empty\n // header strings keep the diff output compact for terminal\n // display; a full unified diff header isn't needed since we're\n // going to print the diff inline, not pipe it to `patch -p1`.\n const generated = generateCode(mod).code\n const diff = createPatch(\n options.configPath,\n originalCode,\n generated,\n '',\n '',\n { context: 3 },\n )\n\n return {\n kind: 'proposed-change',\n configPath: options.configPath,\n originalCode,\n newCode: generated,\n diff,\n dryRun: options.dryRun === true,\n }\n}\n\n/**\n * Write the augmented config to disk. Call this after the user\n * has seen and approved the diff from `augmentNuxtConfig()`. The\n * split between \"compute\" and \"write\" is deliberate — see the\n * module docstring.\n */\nexport async function writeAugmentedConfig(\n configPath: string,\n newCode: string,\n): Promise<void> {\n await fs.writeFile(configPath, newCode, 'utf8')\n}\n\n// ─── Internal helpers ─────────────────────────────────────────────────\n\n/**\n * Given the proxified default export, return the config object\n * itself. Handles both `defineNuxtConfig({...})` (proxified function\n * call whose `$args[0]` is the config) and `{...}` (plain object\n * proxy). Returns null for shapes we don't recognize.\n *\n * Important: magicast's `$args` is a PROXY, not a real array, so\n * `Array.isArray($args)` returns false. We access `$args[0]`\n * directly and check whether the result is a usable object.\n */\nfunction resolveConfigObject(\n exported: unknown,\n): Record<string, unknown> | null {\n if (!exported || typeof exported !== 'object') return null\n const proxy = exported as Record<string, unknown> & {\n $type?: string\n $args?: Record<number, unknown>\n }\n\n // defineNuxtConfig(...) call — magicast exposes the args through\n // a proxied $args accessor. The config literal is at $args[0].\n if (proxy.$type === 'function-call' && proxy.$args) {\n const firstArg = proxy.$args[0]\n if (firstArg && typeof firstArg === 'object') {\n return firstArg as Record<string, unknown>\n }\n return null\n }\n\n // Plain object literal export — the proxy IS the config.\n if (proxy.$type === 'object' || proxy.$type === undefined) {\n return proxy\n }\n\n return null\n}\n\n/**\n * Check whether a magicast-proxied value is an array. The proxy\n * reports `$type === 'array'` for array literals; plain objects\n * and strings come back with different tags.\n */\nfunction isProxyArray(value: unknown): boolean {\n if (!value || typeof value !== 'object') return false\n const proxy = value as { $type?: string }\n return proxy.$type === 'array'\n}\n","#!/usr/bin/env node\n/**\n * The `noy-db` bin — THE ONE DOOR after `npm create noy-db` (family#114).\n *\n * Until 2026-10-11 a newcomer met three bins from three packages: this one\n * (`add`, `verify`, `rotate`, `backup`), `@noy-db/cli`'s `noydb` (`inspect`,\n * `verify`, `describe`, `config`, `monitor`) and `@noy-db/in-devtools-tui`'s\n * `noydb-inspect`. Two of them had a `verify` that meant different things. The\n * docs carried a \"which binary is which\" table instead of there being one\n * binary. This file is that one binary; `@noy-db/cli` is now a shim that\n * redirects here.\n *\n * noy-db add <collection> scaffold a store + page\n * noy-db user add <id> <role> [options] grant a user (was `add user`)\n * noy-db user rotate [options] rotate DEKs (was `rotate`)\n * noy-db backup <target> [options] dump a vault to a file\n * noy-db doctor [config] [--mode] [--fix] readiness checks with stable ids (family#116)\n * noy-db doctor crypto in-memory crypto round-trip (was `verify`)\n * noy-db pod inspect <file.noydb> bundle header, no secret\n * noy-db pod verify <file.noydb> bundle integrity, no secret\n * noy-db describe <file.noydb> [options] YAML/JSON schema audit\n * noy-db config validate <file> sanity-check a NoydbOptions file\n * noy-db config scaffold [--profile=<id>] topology skeleton to stdout, .env to stderr\n * noy-db config profiles list the ten profiles\n * noy-db monitor <file> [--interval=ms] live store metrics (store must be metered)\n * noy-db inspect <file> --vault=<v> […] the TUI inspector (reached lazily, see below)\n *\n * ## Why `inspect` SHELLS OUT instead of importing the TUI\n *\n * The TUI (`@noy-db/in-devtools-tui`) depends on `@noy-db/in-devtools`, a\n * satellite in `noy-db/in`. A core package importing a satellite is the\n * back-edge the one-way law forbids. So this door REACHES the TUI when it is\n * installed and names `noy-db add in-devtools-tui` when it is not — the same\n * shape `monitor` uses for `to-meter`. One door, no back-edge.\n *\n * ## Dispatcher design\n *\n * Deliberately flat: read argv, pick a handler, pass the rest. Each\n * subcommand's argv parser lives next to the subcommand so the pure functions\n * stay testable in isolation. The old spellings (`add user`, `rotate`,\n * `verify`) keep working for one line, printing the new one.\n *\n * ## Secret handling invariants\n *\n * Every subcommand that unlocks a vault goes through `defaultReadSecret`\n * (`commands/shared.ts`): @clack `password()` so nothing echoes, the value is\n * never logged, and the Noydb instance closes in a `finally` so the KEK is\n * cleared on the way out.\n */\n\nimport { spawnSync } from 'node:child_process'\nimport pc from 'picocolors'\nimport { resolveBin } from '../commands/resolve-bin.js'\nimport { addCollection } from '../commands/add.js'\nimport { verifyIntegrity } from '../commands/verify.js'\nimport { rotate, type RotateOptions } from '../commands/rotate.js'\nimport { addUser, type AddUserOptions } from '../commands/add-user.js'\nimport { backup } from '../commands/backup.js'\nimport { assertRole, parseCollectionList } from '../commands/shared.js'\nimport { runInspect as runPodInspect } from '../commands/pod/inspect.js'\nimport { runVerify as runPodVerify } from '../commands/pod/verify.js'\nimport { runDescribe } from '../commands/pod/describe.js'\nimport { runConfigValidate, runConfigScaffold, runConfigProfiles } from '../commands/pod/config.js'\nimport { runMonitor } from '../commands/pod/monitor.js'\nimport { runDoctor } from '../commands/doctor.js'\nimport { runAddPackage } from '../commands/add-package.js'\n\nconst HELP = `Usage: noy-db <command> [args]\n\nProject\n add <collection> Scaffold a new collection store + page\n add <@noy-db/package> [--dev] [--dry-run] Install a package, wire it, print the trust boundary\n add --list [to|in|on|as|by|at] The landscape — every package, by layer\n user add <userId> <role> [options] Grant a new user access to a vault\n user rotate [options] Rotate DEKs for a vault\n backup <target> [options] Dump a vault to a file\n doctor [config] [--mode=prod] [--fix] Readiness checks: secret, recovery, topology, capsule, deps, hygiene\n doctor crypto In-memory crypto round-trip check\n\nPods (.noydb files)\n pod inspect <file.noydb> Print the bundle header (no secret)\n pod verify <file.noydb> Verify bundle integrity (no secret)\n describe <file.noydb> [options] YAML/JSON schema audit (needs the secret)\n\nConfiguration\n config validate <file.js|mjs> Sanity-check a NoydbOptions file\n config scaffold [--profile=<id>] Topology skeleton → stdout, .env template → stderr\n config profiles List the ten topology profiles\n\nRunning\n monitor <file.js|mjs> [--interval=ms] Live store metrics (the store must be metered)\n inspect <file.js|mjs> --vault=<name> [...] Terminal inspector (installs on demand)\n\nCommon options for user add / user rotate / backup:\n --dir <path> Data directory (file adapter). Default: ./data\n --vault <name> Vault (tenant) name. Required.\n --user <id> Your user id in the vault. Required.\n --collections <list> rotate: invoices,clients · user add: invoices:rw,clients:ro\n\nExamples:\n noy-db add invoices\n noy-db user add ann operator --dir ./data --vault demo-co --user owner-alice --collections invoices:rw\n noy-db config profiles\n noy-db config scaffold --profile=B > noydb.config.mjs\n noy-db doctor --mode=prod --fail-level=warn\n\nNew project? \\`npm create noy-db@latest\\`.\n`\n\n/** One line, once, when an old spelling is used. The old spelling still works. */\nfunction renamed(from: string, to: string): void {\n process.stderr.write(`${pc.dim(`(\\`noy-db ${from}\\` is now \\`noy-db ${to}\\`)`)}\\n`)\n}\n\nasync function main(): Promise<void> {\n const [, , command, ...rest] = process.argv\n\n switch (command) {\n case undefined:\n case 'help':\n case '-h':\n case '--help':\n process.stdout.write(HELP)\n return\n\n case 'add': {\n if (rest[0] === 'user') { renamed('add user', 'user add'); await runAddUser(rest.slice(1)); return }\n // A @noy-db package, a prefixed name (to-aws-s3), or --list → the landscape verb.\n // A bare word (invoices) → scaffold a collection, as before.\n const first = rest[0]\n if (rest.some((a) => a === '--list' || a === '--help') || (first && /^(@noy-db\\/|to-|in-|on-|as-|by-|at-)/.test(first))) {\n process.exit(await runAddPackage(rest))\n }\n await runAdd(rest)\n return\n }\n\n case 'user': {\n const [sub, ...subRest] = rest\n if (sub === 'add') { await runAddUser(subRest); return }\n if (sub === 'rotate') { await runRotate(subRest); return }\n fail(`\\`noy-db user\\` takes \\`add\\` or \\`rotate\\``)\n return\n }\n\n case 'rotate':\n renamed('rotate', 'user rotate')\n await runRotate(rest)\n return\n\n case 'backup':\n await runBackup(rest)\n return\n\n case 'verify':\n renamed('verify', 'doctor crypto')\n await runCryptoCheck()\n return\n\n case 'doctor': {\n if (rest[0] === 'crypto') { await runCryptoCheck(); return }\n process.exit(await runDoctor(rest))\n return\n }\n\n case 'pod': {\n const [sub, ...subRest] = rest\n if (sub === 'inspect') process.exit(await runPodInspect(subRest))\n if (sub === 'verify') process.exit(await runPodVerify(subRest))\n fail(`\\`noy-db pod\\` takes \\`inspect\\` or \\`verify\\``)\n return\n }\n\n case 'describe':\n process.exit(await runDescribe(rest))\n return\n\n case 'config': {\n const [sub, ...subRest] = rest\n if (sub === 'validate') process.exit(await runConfigValidate(subRest))\n if (sub === 'scaffold') process.exit(await runConfigScaffold(subRest))\n if (sub === 'profiles') process.exit(runConfigProfiles())\n fail(`\\`noy-db config\\` takes \\`validate\\`, \\`scaffold\\` or \\`profiles\\``)\n return\n }\n\n case 'monitor':\n process.exit(await runMonitor(rest))\n return\n\n case 'inspect':\n process.exit(runTuiInspect(rest))\n return\n\n default:\n process.stderr.write(`${pc.red('error:')} unknown command '${command}'\\n\\n${HELP}`)\n process.exit(2)\n }\n}\n\nfunction fail(message: string): never {\n process.stderr.write(`${pc.red('error:')} ${message}\\n`)\n process.exit(2)\n}\n\n// ─── `inspect` — reach the TUI without importing it ────────────────────\n\n/**\n * Resolve `noydb-inspect` from the caller's project and exec it. We resolve\n * rather than `npx` so an installed TUI is used as-is and an absent one is\n * named precisely, instead of npx silently downloading something at a version\n * nobody chose.\n */\nexport function runTuiInspect(args: readonly string[]): number {\n const bin = resolveBin('@noy-db/in-devtools-tui', 'noydb-inspect')\n if (!bin) {\n process.stderr.write(\n `${pc.red('error:')} the terminal inspector is not installed in this project.\\n\\n` +\n ` ${pc.bold('pnpm add -D @noy-db/in-devtools-tui')} ${pc.dim('(or npm/yarn/bun)')}\\n\\n` +\n `then re-run \\`noy-db inspect\\`. It needs a config file, --vault=<name>, and a secret (--secret or NOYDB_SECRET).\\n`,\n )\n return 2\n }\n const r = spawnSync(process.execPath, [bin, ...args], { stdio: 'inherit' })\n return r.status ?? 1\n}\n\n\n// ─── `add` ──────────────────────────────────────────────────────────────\n\nasync function runAdd(args: string[]): Promise<void> {\n const first = args[0]\n if (!first) {\n process.stderr.write(\n `${pc.red('error:')} \\`noy-db add\\` requires a collection name\\n\\n` +\n `Examples:\\n noy-db add invoices\\n noy-db user add alice operator ...\\n`,\n )\n process.exit(2)\n }\n try {\n const result = await addCollection({ name: first })\n process.stdout.write(`${pc.green('✔')} Created:\\n`)\n for (const file of result.files) {\n process.stdout.write(` ${pc.dim('→')} ${file}\\n`)\n }\n process.stdout.write(\n `\\nNext: visit ${pc.cyan(`/${first}`)} in your dev server, then edit ${pc.bold(`app/stores/${first}.ts`)} to match your domain.\\n`,\n )\n } catch (err) {\n process.stderr.write(`${pc.red('error:')} ${(err as Error).message}\\n`)\n process.exit(1)\n }\n}\n\n// ─── `doctor crypto` ───────────────────────────────────────────────────\n\nasync function runCryptoCheck(): Promise<void> {\n process.stdout.write(`${pc.dim('▸ Running noy-db crypto round-trip…')}\\n`)\n const result = await verifyIntegrity()\n if (result.ok) {\n process.stdout.write(`${pc.green('✔')} ${result.message} ${pc.dim(`(${result.durationMs}ms)`)}\\n`)\n return\n }\n process.stderr.write(`${pc.red('✘')} ${result.message} ${pc.dim(`(${result.durationMs}ms)`)}\\n`)\n process.exit(1)\n}\n\n// ─── `user add` ────────────────────────────────────────────────────────\n\nasync function runAddUser(args: string[]): Promise<void> {\n const userId = args[0]\n const roleInput = args[1]\n if (!userId || !roleInput) {\n process.stderr.write(\n `${pc.red('error:')} \\`noy-db user add\\` requires <userId> <role>\\n\\n` +\n `Example: noy-db user add ann operator --dir ./data --vault demo-co --user owner-alice --collections invoices:rw\\n`,\n )\n process.exit(2)\n }\n\n let role\n try {\n role = assertRole(roleInput)\n } catch (err) {\n process.stderr.write(`${pc.red('error:')} ${(err as Error).message}\\n`)\n process.exit(2)\n return\n }\n\n const flags = parseFlags(args.slice(2))\n const dir = flags.dir ?? './data'\n const vault = requireFlag(flags, 'vault')\n const callerUser = requireFlag(flags, 'user')\n\n const permissions = parsePermissions(flags['collections'])\n\n const opts: AddUserOptions = {\n dir,\n vault,\n callerUser,\n newUserId: userId,\n role,\n }\n if (flags['display-name']) opts.newUserDisplayName = flags['display-name']\n if (permissions) opts.permissions = permissions\n\n try {\n const result = await addUser(opts)\n process.stdout.write(\n `${pc.green('✔')} Granted ${pc.bold(result.role)} access to ${pc.cyan(result.userId)} in vault ${pc.cyan(vault)}.\\n`,\n )\n } catch (err) {\n process.stderr.write(`${pc.red('error:')} ${(err as Error).message}\\n`)\n process.exit(1)\n }\n}\n\n// ─── `user rotate` ─────────────────────────────────────────────────────\n\nasync function runRotate(args: string[]): Promise<void> {\n const flags = parseFlags(args)\n const dir = flags.dir ?? './data'\n const vault = requireFlag(flags, 'vault')\n const user = requireFlag(flags, 'user')\n\n const opts: RotateOptions = { dir, vault, user }\n const collections = parseCollectionList(flags['collections'])\n if (collections) opts.collections = collections\n\n try {\n const result = await rotate(opts)\n process.stdout.write(\n `${pc.green('✔')} Rotated ${pc.bold(String(result.rotated.length))} collection(s) in ${pc.cyan(vault)}:\\n`,\n )\n for (const name of result.rotated) {\n process.stdout.write(` ${pc.dim('→')} ${name}\\n`)\n }\n } catch (err) {\n process.stderr.write(`${pc.red('error:')} ${(err as Error).message}\\n`)\n process.exit(1)\n }\n}\n\n// ─── `backup` ──────────────────────────────────────────────────────────\n\nasync function runBackup(args: string[]): Promise<void> {\n const target = args[0]\n if (!target) {\n process.stderr.write(\n `${pc.red('error:')} \\`noy-db backup\\` requires a target path\\n\\n` +\n `Example: noy-db backup ./backups/demo.json --dir ./data --vault demo-co --user owner-alice\\n`,\n )\n process.exit(2)\n }\n\n const flags = parseFlags(args.slice(1))\n const dir = flags.dir ?? './data'\n const vault = requireFlag(flags, 'vault')\n const user = requireFlag(flags, 'user')\n\n try {\n const result = await backup({ dir, vault, user, target })\n process.stdout.write(\n `${pc.green('✔')} Wrote backup: ${pc.cyan(result.path)} ${pc.dim(`(${result.bytes} bytes)`)}\\n`,\n )\n } catch (err) {\n process.stderr.write(`${pc.red('error:')} ${(err as Error).message}\\n`)\n process.exit(1)\n }\n}\n\n// ─── Shared argv helpers ───────────────────────────────────────────────\n\n/**\n * Parse a sequence of `--key value` flag pairs into a record. Boolean flags\n * (`--flag` with no value) become `\"true\"`. Positionals are peeled by the\n * caller before this sees the args.\n */\nfunction parseFlags(args: string[]): Record<string, string> {\n const out: Record<string, string> = {}\n for (let i = 0; i < args.length; i++) {\n const arg = args[i]\n if (!arg || !arg.startsWith('--')) continue\n const key = arg.slice(2)\n const next = args[i + 1]\n if (next && !next.startsWith('--')) {\n out[key] = next\n i++\n } else {\n out[key] = 'true'\n }\n }\n return out\n}\n\nfunction requireFlag(flags: Record<string, string>, name: string): string {\n const value = flags[name]\n if (!value) {\n process.stderr.write(`${pc.red('error:')} missing required flag --${name}\\n`)\n process.exit(2)\n }\n return value\n}\n\n/** `--collections invoices:rw,clients:ro` → `{ invoices: 'rw', clients: 'ro' }`, or null when empty. */\nfunction parsePermissions(input: string | undefined): Record<string, 'rw' | 'ro'> | null {\n if (!input) return null\n const out: Record<string, 'rw' | 'ro'> = {}\n for (const pair of input.split(',').map((s) => s.trim()).filter(Boolean)) {\n const [name, mode] = pair.split(':')\n if (!name || !mode || (mode !== 'rw' && mode !== 'ro')) {\n throw new Error(`Invalid --collections entry \"${pair}\" — expected \"name:rw\" or \"name:ro\"`)\n }\n out[name] = mode\n }\n return Object.keys(out).length > 0 ? out : null\n}\n\nmain().catch((err: unknown) => {\n process.stderr.write(`${pc.red('fatal:')} ${(err as Error).message}\\n`)\n process.exit(1)\n})\n","/**\n * Locate a dependency's bin in the CALLER's project, without importing the package.\n *\n * `noy-db inspect` reaches `@noy-db/in-devtools-tui` this way (family#114): the TUI\n * depends on `@noy-db/in-devtools`, a satellite, and a core package importing a\n * satellite is the back-edge the one-way law forbids. So the one door REACHES it\n * when it is installed and names it when it is not.\n */\nimport { existsSync, readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\n\n/**\n * The path of a dependency's bin as installed in the caller's project, or null.\n * Walks `node_modules` by PATH from the cwd upward. Not `require.resolve(pkg +\n * '/package.json')`: a package whose `exports` map omits `./package.json` makes\n * Node refuse that subpath, and most modern packages — this one included — do.\n */\nexport function resolveBin(pkg: string, binName: string, from = process.cwd()): string | null {\n let dir = from\n for (;;) {\n const manifestPath = join(dir, 'node_modules', ...pkg.split('/'), 'package.json')\n if (existsSync(manifestPath)) {\n try {\n const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as { bin?: string | Record<string, string> }\n const rel = typeof manifest.bin === 'string' ? manifest.bin : manifest.bin?.[binName]\n return rel ? join(dirname(manifestPath), rel) : null\n } catch {\n return null\n }\n }\n const parent = dirname(dir)\n if (parent === dir) return null\n dir = parent\n }\n}\n","/**\n * `noy-db add <collection>` — scaffold a new collection inside an existing\n * Nuxt 4 project that already has `@noy-db/in-nuxt` configured.\n *\n * The command writes two files:\n *\n * 1. `app/stores/<collection>.ts` — a `defineNoydbStore<T>()` call with\n * a placeholder `T` interface and one example field. The user fills\n * in the real shape after the file is created.\n *\n * 2. `app/pages/<collection>.vue` — a minimal CRUD page that lists,\n * adds, and deletes records. The store ID and collection name are\n * derived from the argument; everything else is boilerplate.\n *\n * The command refuses to overwrite existing files. If either target\n * already exists it logs which one and exits non-zero — the user has to\n * delete or move the file first. There's no `--force` because forcing an\n * overwrite of generated UI code is almost always a footgun in disguise.\n */\n\nimport { promises as fs } from 'node:fs'\nimport path from 'node:path'\n\nexport interface AddCollectionOptions {\n /** The collection name. Must be a lowercase identifier. */\n name: string\n /** Project root. Defaults to `process.cwd()`. */\n cwd?: string\n /** Compartment id to embed in the generated store. Defaults to `default`. */\n vault?: string\n}\n\n/**\n * Result returned to callers (the bin entry uses this to format output;\n * tests assert on the file paths).\n */\nexport interface AddCollectionResult {\n /** Files written, in the order they were created. */\n files: string[]\n}\n\n/**\n * Lowercase identifier check, narrower than the project-name check —\n * collection names become Vue component names, Pinia store IDs, AND TS\n * symbols, so we keep them simple: letters, digits, hyphens, must start\n * with a letter.\n */\nexport function validateCollectionName(name: string): string | null {\n if (!name) return 'Collection name is required'\n if (!/^[a-z][a-z0-9-]*$/.test(name)) {\n return 'Collection name must start with a lowercase letter and contain only lowercase letters, digits, or hyphens'\n }\n return null\n}\n\nexport async function addCollection(\n options: AddCollectionOptions,\n): Promise<AddCollectionResult> {\n const err = validateCollectionName(options.name)\n if (err) throw new Error(err)\n\n const cwd = options.cwd ?? process.cwd()\n const vault = options.vault ?? 'default'\n const name = options.name\n\n // Convert kebab-case to PascalCase for the TS interface name and the\n // Vue helper variable.\n const PascalName = name\n .split('-')\n .map((s) => (s.length === 0 ? s : (s[0]?.toUpperCase() ?? '') + s.slice(1)))\n .join('')\n const useFnName = `use${PascalName}`\n\n const storePath = path.join(cwd, 'app', 'stores', `${name}.ts`)\n const pagePath = path.join(cwd, 'app', 'pages', `${name}.vue`)\n\n // Refuse to overwrite. Doing both checks before writing either file\n // means a partial write is impossible — either both files land or\n // neither does.\n for (const target of [storePath, pagePath]) {\n if (await pathExists(target)) {\n throw new Error(`Refusing to overwrite existing file: ${target}`)\n }\n }\n\n await fs.mkdir(path.dirname(storePath), { recursive: true })\n await fs.mkdir(path.dirname(pagePath), { recursive: true })\n\n await fs.writeFile(storePath, renderStore(name, PascalName, useFnName, vault), 'utf8')\n await fs.writeFile(pagePath, renderPage(name, PascalName, useFnName), 'utf8')\n\n return { files: [storePath, pagePath] }\n}\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await fs.access(p)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Renders the `app/stores/<name>.ts` file. The interface is intentionally\n * minimal — `id` and `name` are the only fields. The user is expected to\n * extend it after generation; we'd rather not lock them into a domain\n * shape we guessed.\n */\nfunction renderStore(\n name: string,\n PascalName: string,\n useFnName: string,\n vault: string,\n): string {\n return `// Generated by \\`noy-db add ${name}\\`.\n//\n// Edit the ${PascalName} interface to match your domain, then call\n// \\`${useFnName}()\\` from any component.\n//\n// defineNoydbStore is auto-imported by @noy-db/in-nuxt. The vault\n// id is the tenant/company namespace — change it if you have multiple.\n\nexport interface ${PascalName} {\n id: string\n name: string\n}\n\nexport const ${useFnName} = defineNoydbStore<${PascalName}>('${name}', {\n vault: '${vault}',\n})\n`\n}\n\n/**\n * Renders the `app/pages/<name>.vue` page — list + add + delete. Picks\n * the simplest possible template-renderer pattern (`v-for`, no fancy\n * components) so the generated file is easy to read and modify.\n */\nfunction renderPage(name: string, PascalName: string, useFnName: string): string {\n return `<!--\n Generated by \\`noy-db add ${name}\\`.\n Visit /${name} in your dev server.\n-->\n<script setup lang=\"ts\">\nconst ${name} = ${useFnName}()\nawait ${name}.$ready\n\nfunction addOne() {\n ${name}.add({\n id: crypto.randomUUID(),\n name: 'New ${PascalName}',\n })\n}\n\nfunction removeOne(id: string) {\n ${name}.remove(id)\n}\n</script>\n\n<template>\n <main>\n <h1>${PascalName}</h1>\n <button @click=\"addOne\">Add ${PascalName}</button>\n <ul>\n <li v-for=\"item in ${name}.items\" :key=\"item.id\">\n {{ item.name }}\n <button @click=\"removeOne(item.id)\">Delete</button>\n </li>\n </ul>\n </main>\n</template>\n`\n}\n","/**\n * `noy-db verify` — end-to-end integrity check.\n *\n * Opens an in-memory NOYDB instance, writes a record, reads it back,\n * decrypts it, and asserts the round-trip is byte-identical. The check\n * exercises the full crypto path (PBKDF2 → KEK → DEK → AES-GCM) without\n * touching any user data on disk.\n *\n * Why an in-memory check is the right scope:\n * - It validates that @noy-db/core, @noy-db/memory, and the user's\n * installed Node version all agree on Web Crypto. That's the most\n * common silent failure for first-time installers.\n * - It cannot accidentally corrupt user data because there isn't any.\n * - It runs in well under one second, so users actually run it.\n *\n * What this command does NOT do (intentionally):\n * - Open the user's actual vault file/dynamo/s3/browser store.\n * That requires the user's secret — not something we want a CLI\n * `verify` command to prompt for. The full secret-driven verify\n * belongs in `nuxi noydb verify` once the auth story for CLIs lands\n * in. For now `noy-db verify` is the dependency-graph smoke test.\n */\n\nimport { createNoydb, memoryStore } from '@noy-db/hub'\n\nexport interface VerifyResult {\n /** `true` if the round-trip succeeded; `false` if anything diverged. */\n ok: boolean\n /** Human-readable status. Always set, even on success. */\n message: string\n /** Wall-clock time the integrity check took, in ms. */\n durationMs: number\n}\n\n/**\n * Runs the end-to-end check. Pure function — no console output, no\n * `process.exit`. The bin wrapper handles formatting and exit codes so\n * the function is trivial to call from tests.\n */\nexport async function verifyIntegrity(): Promise<VerifyResult> {\n const start = performance.now()\n try {\n const db = await createNoydb({\n store: memoryStore(),\n user: 'noy-db-verify',\n // The secret here is throwaway — the in-memory adapter never\n // persists anything, and the KEK is destroyed when we call close()\n // a few lines down. We use a non-trivial value just to exercise\n // PBKDF2 properly.\n secret: 'noy-db-verify-secret-2026',\n })\n const company = await db.openVault('verify-co')\n const collection = company.collection<{ id: string; n: number }>('verify')\n\n // Round-trip a single record. We pick a value that's small enough\n // to print on failure but large enough to ensure encryption isn't\n // accidentally a no-op.\n const original = { id: 'verify-1', n: 42 }\n await collection.put('verify-1', original)\n const got = await collection.get('verify-1')\n if (!got || got.id !== original.id || got.n !== original.n) {\n return fail(start, `Round-trip mismatch: got ${JSON.stringify(got)}`)\n }\n\n // Make sure the query DSL works too — this catches the case where\n // the user's @noy-db/core install is at (no query DSL) but the\n // CLI was updated to.\n const found = collection.query().where('n', '==', 42).toArray()\n if (found.length !== 1) {\n return fail(start, `Query DSL mismatch: expected 1 result, got ${found.length}`)\n }\n\n db.close()\n\n return {\n ok: true,\n message: 'noy-db integrity check passed',\n durationMs: Math.round(performance.now() - start),\n }\n } catch (err) {\n return fail(start, `Integrity check threw: ${(err as Error).message}`)\n }\n}\n\nfunction fail(start: number, message: string): VerifyResult {\n return {\n ok: false,\n message,\n durationMs: Math.round(performance.now() - start),\n }\n}\n","/**\n * `noy-db rotate` — rotate the DEKs for one or more collections in\n * a vault.\n *\n * What it does\n * ------------\n * For each target collection:\n *\n * 1. Generate a fresh DEK\n * 2. Decrypt every record with the old DEK\n * 3. Re-encrypt every record with the new DEK\n * 4. Re-wrap the new DEK into every remaining user's keyring\n *\n * The old DEKs become unreachable as soon as the keyring files are\n * updated. This is the \"just rotate\" path — nobody is revoked,\n * everybody keeps their current permissions, but the key material\n * is replaced.\n *\n * Why expose this as a CLI command\n * --------------------------------\n * Two real-world scenarios:\n *\n * 1. **Suspected key leak.** An operator lost a laptop, a\n * developer accidentally pasted a secret into a Slack\n * channel, a USB stick went missing. Even if you think the\n * secret is safe, rotating is cheap insurance.\n *\n * 2. **Scheduled rotation.** Some compliance regimes require\n * periodic key rotation regardless of exposure. A CLI makes\n * this scriptable from cron or a CI job.\n *\n * This module is test-first: all inputs are plain options, the\n * secret reader is injected, and the Noydb factory is\n * injectable. The production bin is a thin wrapper that defaults\n * those injections to their real implementations.\n */\n\nimport { withTeam } from '@noy-db/hub/team'\nimport { createNoydb, type Noydb, type NoydbStore } from '@noy-db/hub'\nimport { toFile } from '@noy-db/to-file'\nimport type { ReadSecret } from './shared.js'\nimport { defaultReadSecret } from './shared.js'\n\nexport interface RotateOptions {\n /** Directory containing the vault data (file adapter only). */\n dir: string\n /** Vault (tenant) name to rotate keys in. */\n vault: string\n /** The user id of the operator running the rotate. */\n user: string\n /**\n * Explicit list of collections to rotate. When undefined, the\n * rotation targets every collection the user has a DEK for —\n * resolved at run time by reading the vault snapshot.\n */\n collections?: string[]\n /** Injected secret reader. Defaults to the clack implementation. */\n readSecret?: ReadSecret\n /**\n * Injected Noydb factory. Production code leaves this undefined\n * and gets `createNoydb`; tests pass a constructor that builds\n * against an in-memory adapter.\n */\n createDb?: typeof createNoydb\n /**\n * Injected adapter factory. Production code leaves this undefined\n * and gets `toFile`; tests pass one that returns the shared\n * in-memory adapter their fixture used.\n */\n buildAdapter?: (dir: string) => NoydbStore\n}\n\nexport interface RotateResult {\n /** The collections that were actually rotated. */\n rotated: string[]\n}\n\n/**\n * Run the rotate flow against a file-adapter vault. Returns\n * the list of collections that were rotated so callers can display\n * it to the user.\n *\n * Throws `Error` on any auth/adapter/rotate failure. The bin\n * catches these and prints a friendly message; direct callers\n * (tests) can inspect the error message to assert specific\n * failure modes.\n */\nexport async function rotate(options: RotateOptions): Promise<RotateResult> {\n const readSecret = options.readSecret ?? defaultReadSecret\n const buildAdapter = options.buildAdapter ?? ((dir) => toFile({ dir }))\n const createDb = options.createDb ?? createNoydb\n\n // Read the secret BEFORE opening the database. This way a\n // cancelled prompt (Ctrl-C at the password entry) leaves the\n // adapter completely untouched — no files opened, no locks held.\n const secret = await readSecret(`Secret for ${options.user}`)\n\n let db: Noydb | null = null\n try {\n db = await createDb({\n store: buildAdapter(options.dir),\n user: options.user,\n secret,\n // Key rotation is the opt-in team capability (#267).\n teamStrategy: withTeam(),\n })\n\n // Resolve \"all collections\" by asking the vault. This\n // happens BEFORE rotate() is called, so the list is stable\n // across the operation — adding a new collection mid-rotate\n // would be a race we're not guarding against (single-writer\n // assumption applies).\n const vault = await db.openVault(options.vault)\n const targets = options.collections && options.collections.length > 0\n ? options.collections\n : await vault.collections()\n\n if (targets.length === 0) {\n throw new Error(\n `Vault \"${options.vault}\" has no collections to rotate.`,\n )\n }\n\n await db.rotate(options.vault, targets)\n return { rotated: targets }\n } finally {\n // Always close the DB on exit — success or failure. Close()\n // clears the KEK and DEKs from process memory, which is the\n // final line of defense if the secret somehow leaked\n // into a log line above this block.\n db?.close()\n }\n}\n","/**\n * Shared primitives for the interactive `noy-db` subcommands that\n * need to unlock a real vault.\n *\n * Three things live here:\n *\n * 1. `ReadSecret` — a tiny interface for \"prompt the user for\n * a secret\", with a test-friendly default. Subcommands take\n * this as an injected dependency so tests can short-circuit\n * the prompt without spawning a pty.\n *\n * 2. `defaultReadSecret` — the production implementation,\n * built on `@clack/prompts` `password()`. Never echoes the\n * value to the terminal, never logs it, clears it from the\n * returned promise after the caller consumes it.\n *\n * 3. `assertRole` — narrow unknown string input to the Role type\n * with a consistent error message.\n *\n * ## Why pull this out\n *\n * `rotate`, `addUser`, and `backup` all need the same \"prompt for\n * a secret\" shape and the same \"open a file adapter and get\n * back a Noydb instance\" shape. Duplicating it in three files would\n * drift over time; centralizing means one place to audit the\n * secret-handling contract (never log, never persist, clear\n * local variables after use).\n */\n\nimport { password, isCancel, cancel } from '@clack/prompts'\nimport type { Role } from '@noy-db/hub'\n\nconst VALID_ROLES = ['owner', 'admin', 'operator', 'viewer', 'client'] as const\n\n/**\n * Asynchronous secret reader. Production code passes\n * `defaultReadSecret`; tests pass a stub that returns a fixed\n * string without touching stdin.\n *\n * The `label` is shown to the user as the prompt message. It\n * should never contain the expected secret or any secret.\n */\nexport type ReadSecret = (label: string) => Promise<string>\n\n/**\n * Clack-based secret prompt. Cancellation (Ctrl-C) aborts the\n * process with exit code 1 — prompts are always the first thing to\n * fire in a subcommand, so aborting here doesn't leave the system\n * in a half-mutated state.\n */\nexport const defaultReadSecret: ReadSecret = async (label) => {\n const value = await password({\n message: label,\n // Basic sanity: reject empty strings up front. We don't enforce\n // length here because the caller's KEK-derivation step will\n // reject weak secrets with its own, richer error.\n validate: (v) => (v.length === 0 ? 'Secret cannot be empty' : undefined),\n })\n if (isCancel(value)) {\n cancel('Cancelled.')\n process.exit(1)\n }\n return value\n}\n\n/**\n * Narrow an unknown string to the `Role` type from @noy-db/core.\n * Used by the `add user` subcommand to validate the role argument\n * before passing it to `noydb.grant()`.\n */\nexport function assertRole(input: string): Role {\n if (!(VALID_ROLES as readonly string[]).includes(input)) {\n throw new Error(\n `Invalid role \"${input}\" — must be one of: ${VALID_ROLES.join(', ')}`,\n )\n }\n return input as Role\n}\n\n/**\n * Split a comma-separated collection list into an array of names,\n * trimming whitespace and dropping empties. Returns null if the\n * input itself is empty or undefined — the caller decides whether\n * that means \"all collections\" or \"error\".\n */\nexport function parseCollectionList(input: string | undefined): string[] | null {\n if (!input) return null\n const parts = input\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0)\n return parts.length > 0 ? parts : null\n}\n","/**\n * `noy-db add user <id> <role>` — grant a new user access to a\n * vault.\n *\n * What it does\n * ------------\n * Wraps `noydb.grant()` in the CLI's auth-prompt ritual:\n *\n * 1. Prompt the caller for their own secret (to unlock the\n * caller's keyring and derive the wrapping key).\n * 2. Prompt for the new user's secret.\n * 3. Prompt for confirmation of the new secret.\n * 4. Reject on mismatch.\n * 5. Call `noydb.grant(vault, { userId, role, secret, permissions })`.\n *\n * For owner/admin/viewer roles, every collection is granted\n * automatically (the core keyring.ts grant logic handles that via\n * the `permissions` field). For operator/client, the caller must\n * pass a `--collections` list because those roles need explicit\n * per-collection permissions.\n *\n * ## What this does NOT do\n *\n * - No email/invite flow — is about local-CLI key management,\n * not out-of-band user enrollment.\n * - No rollback on partial failure — `grant()` is atomic at the\n * core level (keyring file writes last, after DEK wrapping), so\n * partial-state-on-crash is already handled.\n */\n\nimport { withTeam } from '@noy-db/hub/team'\nimport { createNoydb, type Noydb, type NoydbStore, type Role } from '@noy-db/hub'\nimport { toFile } from '@noy-db/to-file'\nimport type { ReadSecret } from './shared.js'\nimport { defaultReadSecret } from './shared.js'\n\nexport interface AddUserOptions {\n /** Directory containing the vault data (file adapter only). */\n dir: string\n /** Vault (tenant) name to grant access to. */\n vault: string\n /** The user id of the caller running the grant. */\n callerUser: string\n /** The new user's id (must not already exist in the vault keyring). */\n newUserId: string\n /** The new user's display name — shown in UI and audit logs. Defaults to `newUserId`. */\n newUserDisplayName?: string\n /** The new user's role. */\n role: Role\n /**\n * Per-collection permissions. Required when `role` is operator or\n * client; ignored for owner/admin/viewer (they get everything\n * via the core's resolvePermissions logic).\n *\n * Shape: `{ invoices: 'rw', clients: 'ro' }`. CLI callers pass\n * `--collections invoices:rw,clients:ro` and the argv parser\n * converts it to this shape.\n */\n permissions?: Record<string, 'rw' | 'ro'>\n /** Injected secret reader. Defaults to the clack implementation. */\n readSecret?: ReadSecret\n /** Injected Noydb factory. */\n createDb?: typeof createNoydb\n /** Injected adapter factory. */\n buildAdapter?: (dir: string) => NoydbStore\n}\n\nexport interface AddUserResult {\n /** The userId that was granted access. */\n userId: string\n /** The role they were granted. */\n role: Role\n}\n\n/**\n * Run the grant flow. Two secret prompts: caller's, then new\n * user's (twice for confirmation). Calls `noydb.grant()` with the\n * collected values.\n */\nexport async function addUser(options: AddUserOptions): Promise<AddUserResult> {\n const readSecret = options.readSecret ?? defaultReadSecret\n const buildAdapter = options.buildAdapter ?? ((dir) => toFile({ dir }))\n const createDb = options.createDb ?? createNoydb\n\n // Operator/client roles NEED explicit permissions. Reject here\n // rather than in the middle of the grant, so the caller sees the\n // problem before any I/O happens.\n if (\n (options.role === 'operator' || options.role === 'client') &&\n (!options.permissions || Object.keys(options.permissions).length === 0)\n ) {\n throw new Error(\n `Role \"${options.role}\" requires explicit --collections — e.g. --collections invoices:rw,clients:ro`,\n )\n }\n\n const callerSecret = await readSecret(\n `Your secret (${options.callerUser})`,\n )\n const newSecret = await readSecret(\n `New secret for ${options.newUserId}`,\n )\n const confirmSecret = await readSecret(\n `Confirm secret for ${options.newUserId}`,\n )\n\n if (newSecret !== confirmSecret) {\n throw new Error(`Secrets do not match — grant aborted.`)\n }\n\n let db: Noydb | null = null\n try {\n db = await createDb({\n store: buildAdapter(options.dir),\n user: options.callerUser,\n secret: callerSecret,\n // Multi-user grant is the opt-in team capability (#267) — the CLI\n // add-user command is by definition a multi-user operation.\n teamStrategy: withTeam(),\n })\n\n // Build the grant options. Only include `permissions` when the\n // caller actually supplied them — otherwise the core's\n // resolvePermissions fills in the role defaults. The spread\n // (rather than post-assignment) keeps the object literal\n // compatible with `GrantOptions`'s readonly `permissions`.\n const grantOpts: Parameters<Noydb['grant']>[1] = {\n userId: options.newUserId,\n displayName: options.newUserDisplayName ?? options.newUserId,\n role: options.role,\n secret: newSecret,\n ...(options.permissions ? { permissions: options.permissions } : {}),\n }\n\n await db.grant(options.vault, grantOpts)\n\n return {\n userId: options.newUserId,\n role: options.role,\n }\n } finally {\n db?.close()\n }\n}\n","/**\n * `noy-db backup <target>` — dump a vault to a local file.\n *\n * What it does\n * ------------\n * Wraps `vault.dump()` in the CLI's auth-prompt ritual, then\n * writes the serialized backup to the requested path. As of,\n * `dump()` already produces a verifiable backup (embedded\n * ledgerHead, full `_ledger` / `_ledger_deltas` snapshots) — the\n * CLI just moves bytes; the integrity guarantees come from core.\n *\n * ## Target URI support\n *\n * ships **`file://` only** (or a plain filesystem path).\n * The issue spec originally called for `s3://` as well, but\n * wiring @aws-sdk into create-noy-db would defeat the\n * zero-runtime-deps story for the CLI package. S3 backup is\n * deferred to a follow-up that can live in @noy-db/s3-cli or a\n * similar optional companion package.\n *\n * Accepted forms:\n * - `file:///absolute/path.json`\n * - `file://./relative/path.json`\n * - `/absolute/path.json` (treated as `file://`)\n * - `./relative/path.json` (treated as `file://`)\n *\n * ## What this does NOT do\n *\n * - No encryption of the backup BEYOND what noy-db already does.\n * The dumped file is a valid noy-db backup, which means\n * individual records are still encrypted but the keyring is\n * included (wrapped with each user's KEK). Anyone who loads\n * the backup still needs the correct secret to read.\n * - No restore — that's a separate subcommand tracked as a\n * follow-up. For now users can restore via\n * `vault.load(backupString)` from their own app code.\n */\n\nimport { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport { createNoydb, type Noydb, type NoydbStore } from '@noy-db/hub'\nimport { withHistory } from '@noy-db/hub/history'\nimport { toFile } from '@noy-db/to-file'\nimport type { ReadSecret } from './shared.js'\nimport { defaultReadSecret } from './shared.js'\n\nexport interface BackupOptions {\n /** Directory containing the vault data (file adapter only). */\n dir: string\n /** Vault (tenant) name to back up. */\n vault: string\n /** The user id of the operator running the backup. */\n user: string\n /**\n * Where to write the backup. Accepts a `file://` URI or a plain\n * filesystem path. Relative paths resolve against `process.cwd()`.\n */\n target: string\n /** Injected secret reader. */\n readSecret?: ReadSecret\n /** Injected Noydb factory. */\n createDb?: typeof createNoydb\n /** Injected adapter factory. */\n buildAdapter?: (dir: string) => NoydbStore\n}\n\nexport interface BackupResult {\n /** Absolute filesystem path the backup was written to. */\n path: string\n /** Size of the serialized backup in bytes. */\n bytes: number\n}\n\n/**\n * Parse a backup target into an absolute filesystem path. Rejects\n * unsupported URI schemes (s3://, https://, etc.) early so the\n * caller doesn't silently write to the wrong place.\n */\nexport function resolveBackupTarget(target: string, cwd: string = process.cwd()): string {\n // Strip the `file://` prefix if present. The rest of the string\n // is treated as a filesystem path. We accept both `file:///abs`\n // (three slashes, absolute) and `file://./rel` (two slashes,\n // relative) because real-world users write both.\n let raw = target\n if (target.startsWith('file://')) {\n raw = target.slice('file://'.length)\n } else if (target.includes('://')) {\n // Any other scheme is unsupported.\n throw new Error(\n `Unsupported backup target scheme: \"${target.split('://')[0]}://\". ` +\n `Only file:// and plain filesystem paths are supported in. ` +\n `S3 backups will land in a follow-up.`,\n )\n }\n return path.resolve(cwd, raw)\n}\n\nexport async function backup(options: BackupOptions): Promise<BackupResult> {\n const readSecret = options.readSecret ?? defaultReadSecret\n const buildAdapter = options.buildAdapter ?? ((dir) => toFile({ dir }))\n const createDb = options.createDb ?? createNoydb\n\n // Resolve the target FIRST so a bad URI fails before any\n // secret is collected. This keeps the UX clean: a typo in\n // `s3://bucket/x` rejects without asking for a secret the user\n // would then have to type again.\n const absolutePath = resolveBackupTarget(options.target)\n\n const secret = await readSecret(`Secret for ${options.user}`)\n\n let db: Noydb | null = null\n try {\n db = await createDb({\n store: buildAdapter(options.dir),\n user: options.user,\n secret,\n // Opt into the history strategy so `vault.dump()` embeds\n // `ledgerHead` for tamper-evident verification at restore.\n // Source vaults that were never written with history simply\n // have an empty ledger; the embedded head is omitted in that\n // case and the backup is treated as legacy on load.\n historyStrategy: withHistory(),\n })\n const vault = await db.openVault(options.vault)\n const serialized = await vault.dump()\n\n // Make sure the parent directory exists. If the user passed\n // `./backups/2026/demo.json` and `./backups/2026` doesn't\n // exist yet, we create it. This is the common case for\n // scripted rotations dropping into a date-based folder.\n await fs.mkdir(path.dirname(absolutePath), { recursive: true })\n await fs.writeFile(absolutePath, serialized, 'utf8')\n\n return {\n path: absolutePath,\n bytes: Buffer.byteLength(serialized, 'utf8'),\n }\n } finally {\n db?.close()\n }\n}\n","/**\n * `noy-db pod inspect <file.noydb>` — print the unencrypted header.\n *\n * This command is the security-sensitive one in the CLI: it must\n * never prompt for a secret and never decrypt anything. It reads\n * exactly the unencrypted header bytes (magic + flags + header\n * length prefix + JSON header) and returns structured metadata.\n *\n * What the header reveals:\n * - `formatVersion` — container format version\n * - `handle` — ULID identifier\n * - `bodyBytes` — compressed body size\n * - `bodySha256` — body integrity hash\n *\n * What it does **not** reveal: record counts, collection names,\n * ciphertext, keyring, timestamps inside records. Those live past\n * the decryption boundary.\n *\n * @module\n */\nimport { readPodHeader } from '@noy-db/hub'\nimport { readFile } from 'node:fs/promises'\n\nexport interface InspectResult {\n formatVersion: number\n handle: string\n bodyBytes: number\n bodySha256: string\n}\n\nexport async function inspect(filePath: string): Promise<InspectResult> {\n const bytes = await readFile(filePath)\n const header = readPodHeader(new Uint8Array(bytes))\n return {\n formatVersion: header.formatVersion,\n handle: header.handle,\n bodyBytes: header.bodyBytes,\n bodySha256: header.bodySha256,\n }\n}\n\nexport async function runInspect(argv: readonly string[]): Promise<number> {\n const file = argv[0]\n if (!file) {\n process.stderr.write('usage: noy-db pod inspect <file.noydb>\\n')\n return 2\n }\n try {\n const info = await inspect(file)\n process.stdout.write(JSON.stringify(info, null, 2) + '\\n')\n return 0\n } catch (err) {\n process.stderr.write(`inspect failed: ${(err as Error).message}\\n`)\n return 1\n }\n}\n","/**\n * `noy-db pod verify <file.noydb>` — integrity check.\n *\n * Validates the three integrity properties of a bundle that can be\n * verified **without the secret**:\n *\n * 1. Magic prefix + format version match what this CLI supports.\n * 2. Header parses as JSON with the minimum-disclosure key set.\n * 3. Compressed body SHA-256 matches `header.bodySha256`.\n *\n * Ledger-head verification (which does need the secret) is not\n * covered here — that's a future enhancement when an `open` subcommand\n * lands. For now, `verify` answers \"was this file transmitted intact?\"\n * without involving keys.\n *\n * @module\n */\nimport { readPod, readPodHeader } from '@noy-db/hub'\nimport { readFile } from 'node:fs/promises'\n\nexport interface VerifyReport {\n ok: boolean\n file: string\n handle: string\n bodyBytes: number\n checks: { magic: boolean; header: boolean; bodyHash: boolean }\n error?: string\n}\n\nexport async function verify(filePath: string): Promise<VerifyReport> {\n const bytes = new Uint8Array(await readFile(filePath))\n const checks = { magic: false, header: false, bodyHash: false }\n\n let handle = ''\n let bodyBytes = 0\n\n try {\n const header = readPodHeader(bytes)\n checks.magic = true\n checks.header = true\n handle = header.handle\n bodyBytes = header.bodyBytes\n\n // readPod() verifies bodySha256 internally — if it doesn't\n // throw, the body hash matched.\n await readPod(bytes)\n checks.bodyHash = true\n\n return { ok: true, file: filePath, handle, bodyBytes, checks }\n } catch (err) {\n return {\n ok: false, file: filePath, handle, bodyBytes, checks,\n error: (err as Error).message,\n }\n }\n}\n\nexport async function runVerify(argv: readonly string[]): Promise<number> {\n const file = argv[0]\n if (!file) {\n process.stderr.write('usage: noy-db pod verify <file.noydb>\\n')\n return 2\n }\n const report = await verify(file)\n process.stdout.write(JSON.stringify(report, null, 2) + '\\n')\n return report.ok ? 0 : 1\n}\n","/**\n * `noy-db describe <file.noydb>` — emit a human-readable YAML/JSON\n * description of a bundle's structure (collections, fields, indexes,\n * FKs, MVs, overlays, derivations) with optional counters.\n *\n * Loads the bundle into an in-memory store, unlocks it with the\n * supplied secret + user, calls `vault.dumpSchema()`, then emits.\n *\n * See `design-history/2026-05-22-schema-dump-design.md`.\n *\n * @module\n */\n\nimport { readFile, writeFile, mkdir } from 'node:fs/promises'\nimport { dirname, basename, join } from 'node:path'\nimport { createHash } from 'node:crypto'\nimport { stringify as yamlStringify } from 'yaml'\nimport type { VaultSchemaSnapshot } from '@noy-db/hub/introspection'\nimport type { EncryptedEnvelope, NoydbStore, VaultSnapshot } from '@noy-db/hub/to'\nimport { createNoydb, readPod, loadPersistedSchema, type EnclaveKey } from '@noy-db/hub'\nimport { ConflictError } from '@noy-db/hub/to'\n\nconst VERSION = '0.1.0'\n\nexport type DescribeFormat = 'yaml' | 'json'\nexport type SchemasMode = 'none' | 'full' | 'sidecar'\n\nexport interface DescribeOptions {\n readonly bundlePath: string\n readonly user: string\n readonly secret: string\n readonly format: DescribeFormat\n readonly withStats: boolean\n readonly schemas: SchemasMode\n readonly outPath?: string\n readonly sampleSize: number\n}\n\ninterface SnapshotWithProvenance extends VaultSchemaSnapshot {\n _provenance: {\n generatedBy: string\n source: string\n sourceSha256: string\n emittedAt: string\n }\n}\n\n/** In-memory adapter — used only for the temporary vault that hosts the loaded bundle. */\nfunction memoryStore(): NoydbStore {\n const data = new Map<string, Map<string, Map<string, EncryptedEnvelope>>>()\n const getColl = (v: string, c: string) => {\n let vm = data.get(v); if (!vm) { vm = new Map(); data.set(v, vm) }\n let cm = vm.get(c); if (!cm) { cm = new Map(); vm.set(c, cm) }\n return cm\n }\n return {\n async get(v, c, id) { return data.get(v)?.get(c)?.get(id) ?? null },\n async put(v, c, id, env, ev) {\n const coll = getColl(v, c); const ex = coll.get(id)\n if (ev !== undefined && ex && ex._v !== ev) throw new ConflictError(ex._v)\n coll.set(id, env)\n },\n async delete(v, c, id) { data.get(v)?.get(c)?.delete(id) },\n async list(v, c) { return [...(data.get(v)?.get(c)?.keys() ?? [])] },\n async loadAll(v) {\n const vm = data.get(v); const snap: VaultSnapshot = {}\n if (vm) for (const [cn, cm] of vm) {\n if (cn.startsWith('_')) continue\n const r: Record<string, EncryptedEnvelope> = {}\n for (const [id, e] of cm) r[id] = e\n snap[cn] = r\n }\n return snap\n },\n async saveAll(v, snap) {\n for (const [cn, recs] of Object.entries(snap)) {\n const coll = getColl(v, cn)\n for (const [id, env] of Object.entries(recs)) coll.set(id, env)\n }\n },\n }\n}\n\ninterface BackupShape {\n _compartment?: string\n}\n\n/** Run the full describe pipeline and return the emitted string. */\nexport async function describeBundle(opts: DescribeOptions): Promise<string> {\n const bytes = await readFile(opts.bundlePath)\n const sourceSha256 = createHash('sha256').update(bytes).digest('hex')\n\n const { dumpJson } = await readPod(new Uint8Array(bytes))\n const backup = JSON.parse(dumpJson) as BackupShape\n const compartmentName = backup._compartment ?? 'vault'\n\n // Build a fresh in-memory Noydb to host the loaded bundle. The\n // `user` + `secret` must match a keyring in the bundle.\n const db = await createNoydb({\n store: memoryStore(),\n user: opts.user,\n secret: opts.secret,\n })\n const vault = await db.openVault(compartmentName)\n await vault.load(dumpJson)\n\n // Touch each collection so its config (refs, validator if registered\n // in-process) is in the cache. For a fresh bundle load, there's no\n // app code registering schemas, so collections without persisted\n // _schemas envelopes will show `source: 'unknown'`.\n const collectionNames = await vault.collections()\n for (const name of collectionNames) {\n if (!name.startsWith('_')) vault.collection(name)\n }\n\n const snapshot = await vault.dumpSchema({\n withStats: opts.withStats,\n sampleSize: opts.sampleSize,\n })\n\n // Optionally inline the persisted JSON Schemas under each collection.\n const enriched = opts.schemas === 'full'\n ? await enrichWithFullSchemas(snapshot, vault, collectionNames)\n : snapshot\n\n // Attach provenance at the top. Build the object in key-order\n // we want emitted: _noydb_snapshot first, _provenance second, then\n // every snapshot field (without _noydb_snapshot which we already set).\n const { _noydb_snapshot: _ignore, ...rest } = enriched\n void _ignore\n const withProvenance: SnapshotWithProvenance = {\n _noydb_snapshot: 2,\n // #1447 — carried through from the snapshot rather than restated, so a CLI\n // dump reports the same emitter capabilities the vault did. Restating it\n // here would let the two drift, and a reader cannot tell which is right.\n reports: rest.reports,\n _provenance: {\n generatedBy: `noy-db describe v${VERSION}`,\n source: basename(opts.bundlePath),\n sourceSha256,\n emittedAt: enriched.emittedAt,\n },\n vault: rest.vault,\n emittedAt: rest.emittedAt,\n subsystems: rest.subsystems,\n collections: rest.collections,\n materializedViews: rest.materializedViews,\n overlayViews: rest.overlayViews,\n derivations: rest.derivations,\n ...(rest.internal !== undefined ? { internal: rest.internal } : {}),\n }\n\n // Sidecar emission — write each collection's JSON Schema to a\n // separate file next to the main output.\n if (opts.schemas === 'sidecar' && opts.outPath) {\n await writeSidecarSchemas(opts.outPath, vault, collectionNames)\n }\n\n const emitted = emit(withProvenance, opts.format)\n if (opts.outPath) {\n await mkdir(dirname(opts.outPath), { recursive: true })\n await writeFile(opts.outPath, emitted)\n }\n return emitted\n}\n\nasync function enrichWithFullSchemas(\n snapshot: VaultSchemaSnapshot,\n vault: { _introspectState(): { adapter: NoydbStore; name: string; getDEK: (n: string) => Promise<EnclaveKey> } },\n collectionNames: readonly string[],\n): Promise<VaultSchemaSnapshot> {\n const state = vault._introspectState()\n const collections: Record<string, unknown> = {}\n for (const name of Object.keys(snapshot.collections)) {\n const base = snapshot.collections[name]\n if (!base) continue\n if (!collectionNames.includes(name)) {\n collections[name] = base\n continue\n }\n try {\n const dek = await state.getDEK(name)\n const persisted = await loadPersistedSchema(state.adapter, state.name, name, dek)\n if (persisted?.jsonSchema) {\n collections[name] = { ...base, jsonSchema: persisted.jsonSchema }\n } else {\n collections[name] = base\n }\n } catch {\n collections[name] = base\n }\n }\n return { ...snapshot, collections: collections as VaultSchemaSnapshot['collections'] }\n}\n\nasync function writeSidecarSchemas(\n outPath: string,\n vault: { _introspectState(): { adapter: NoydbStore; name: string; getDEK: (n: string) => Promise<EnclaveKey> } },\n collectionNames: readonly string[],\n): Promise<void> {\n const state = vault._introspectState()\n const sidecarDir = `${outPath}.schemas`\n await mkdir(sidecarDir, { recursive: true })\n for (const name of collectionNames) {\n if (name.startsWith('_')) continue\n try {\n const dek = await state.getDEK(name)\n const persisted = await loadPersistedSchema(state.adapter, state.name, name, dek)\n if (persisted?.jsonSchema) {\n await writeFile(\n join(sidecarDir, `${name}.schema.json`),\n JSON.stringify(persisted.jsonSchema, null, 2),\n )\n }\n } catch {\n // Skip collections we can't decrypt.\n }\n }\n}\n\nfunction emit(snapshot: SnapshotWithProvenance, format: DescribeFormat): string {\n if (format === 'json') {\n return JSON.stringify(snapshot, null, 2) + '\\n'\n }\n // YAML — strict scalars; no anchors / refs for predictable output.\n return yamlStringify(snapshot, {\n aliasDuplicateObjects: false,\n indent: 2,\n lineWidth: 0, // never wrap; preserves long strings\n })\n}\n\n// ─── CLI dispatch ────────────────────────────────────────────\n\ninterface ParsedArgs {\n bundlePath: string\n user?: string\n secret?: string\n format: DescribeFormat\n withStats: boolean\n schemas: SchemasMode\n outPath?: string\n sampleSize: number\n}\n\nfunction parseArgs(argv: readonly string[]): ParsedArgs | { error: string } {\n const out: ParsedArgs = {\n bundlePath: '',\n format: 'yaml',\n withStats: false,\n schemas: 'none',\n sampleSize: 50,\n }\n let i = 0\n while (i < argv.length) {\n const arg = argv[i]\n if (arg === '--format') {\n const v = argv[++i]\n if (v !== 'yaml' && v !== 'json') return { error: `--format must be yaml or json (got: ${v ?? '<none>'})` }\n out.format = v\n } else if (arg === '--with-stats') {\n out.withStats = true\n } else if (arg === '--schemas') {\n const v = argv[++i]\n if (v !== 'none' && v !== 'full' && v !== 'sidecar') {\n return { error: `--schemas must be none|full|sidecar (got: ${v ?? '<none>'})` }\n }\n out.schemas = v\n } else if (arg === '-o' || arg === '--out') {\n const v = argv[++i]\n if (v === undefined) return { error: '-o requires a value' }\n out.outPath = v\n } else if (arg === '--secret') {\n const v = argv[++i]\n if (v === undefined) return { error: '--secret requires a value' }\n out.secret = v\n } else if (arg === '--user') {\n const v = argv[++i]\n if (v === undefined) return { error: '--user requires a value' }\n out.user = v\n } else if (arg === '--sample') {\n const n = parseInt(argv[++i] ?? '', 10)\n if (Number.isNaN(n) || n < 0) return { error: `--sample must be a non-negative integer` }\n out.sampleSize = n\n } else if (arg === '-h' || arg === '--help') {\n return { error: 'help' }\n } else if (arg?.startsWith('-')) {\n return { error: `unknown option: ${arg}` }\n } else if (!out.bundlePath) {\n out.bundlePath = arg ?? ''\n } else {\n return { error: `unexpected positional arg: ${arg}` }\n }\n i++\n }\n if (!out.bundlePath) return { error: 'missing bundle path' }\n return out\n}\n\nfunction usage(): string {\n return [\n 'usage: noy-db describe <bundle-path> [options]',\n '',\n ' --format <yaml|json> output format (default: yaml)',\n ' --with-stats include records / bytes / oldest / newest per collection',\n ' --schemas <mode> JSON Schema body inclusion (default: none)',\n ' - none: summary only',\n ' - full: inline complete JSON Schema per collection',\n ' - sidecar: write `<out>.schemas/<col>.schema.json` siblings',\n ' -o, --out <file> write to file instead of stdout',\n ' --user <userId> keyring user identifier (required)',\n ' --secret <p> decryption secret (or via NOYDB_SECRET env)',\n ' --sample <n> max records sampled when no persisted/live schema (default: 50)',\n '',\n 'Exit codes: 0 ok, 1 internal error, 2 usage error, 3 auth failure',\n ].join('\\n')\n}\n\nexport async function runDescribe(argv: readonly string[]): Promise<number> {\n const parsed = parseArgs(argv)\n if ('error' in parsed) {\n if (parsed.error === 'help') {\n process.stdout.write(usage() + '\\n')\n return 0\n }\n process.stderr.write(`describe: ${parsed.error}\\n\\n${usage()}\\n`)\n return 2\n }\n const secret = parsed.secret ?? process.env.NOYDB_SECRET\n if (!secret) {\n process.stderr.write('describe: secret required — pass --secret or set NOYDB_SECRET env\\n')\n return 3\n }\n if (parsed.secret) {\n process.stderr.write(\n '[noy-db] warning: --secret appears in shell history; consider NOYDB_SECRET env instead\\n',\n )\n }\n if (!parsed.user) {\n process.stderr.write('describe: --user <userId> is required to unlock the bundle\\n')\n return 2\n }\n\n try {\n const out = await describeBundle({\n bundlePath: parsed.bundlePath,\n user: parsed.user,\n secret,\n format: parsed.format,\n withStats: parsed.withStats,\n schemas: parsed.schemas,\n ...(parsed.outPath !== undefined ? { outPath: parsed.outPath } : {}),\n sampleSize: parsed.sampleSize,\n })\n if (!parsed.outPath) process.stdout.write(out)\n return 0\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err)\n if (/decrypt|tamper|wrong key|invalid/i.test(msg)) {\n process.stderr.write(`describe: decryption failed — check --user and --secret\\n ${msg}\\n`)\n return 3\n }\n process.stderr.write(`describe: ${msg}\\n`)\n return 1\n }\n}\n\n","/**\n * `noy-db config validate` and `noy-db config scaffold`.\n *\n * **validate** — sanity-check a `NoydbOptions` value at runtime. The\n * input file is expected to default-export a `NoydbOptions` object\n * (or a factory returning one). We dynamically import it and check:\n *\n * - `store` is present and exposes the 6-method `NoydbStore` shape.\n * - Sync targets have role ∈ {sync-peer, backup, archive} and a store.\n * - `archive` targets don't carry a pull policy (archive is push-only).\n * - `syncPolicy` pairs with at least one sync target.\n * - `blob` routes (if present) sit on a bundle-capable store.\n *\n * **scaffold** — emit a working skeleton for one of the topology\n * profiles (`noy-db config profiles` lists them). The `.ts`/`.mjs`\n * config is written to **stdout** (pipe it straight to a file, e.g.\n * `> noydb.config.mjs`); the companion `.env` template is written to\n * **stderr** so it stays out of the piped config.\n *\n * @module\n */\nimport { pathToFileURL } from 'node:url'\nimport { resolve } from 'node:path'\n\nexport interface ValidationIssue {\n severity: 'warn' | 'error'\n code: string\n path: string\n message: string\n}\n\nexport interface ValidationReport {\n ok: boolean\n issues: ValidationIssue[]\n}\n\n/**\n * Validate a NoydbOptions-shaped object (not a file path — this is\n * the programmatic API). Conservative: if a check can't be made\n * confidently, it's skipped rather than warned, to avoid nagging.\n */\n/** Coerces arbitrary values to a human-readable string without triggering\n * `[object Object]` — used by the config validator to report bad field types. */\nfunction safeStringify(v: unknown): string {\n if (typeof v === 'string') return v\n if (typeof v === 'number' || typeof v === 'boolean' || v === null || v === undefined) {\n return String(v)\n }\n try { return JSON.stringify(v) } catch { return '<unserializable>' }\n}\n\nexport function validateOptions(opts: unknown): ValidationReport {\n const issues: ValidationIssue[] = []\n\n if (!isRecord(opts)) {\n issues.push({ severity: 'error', code: 'not-object', path: '<root>',\n message: 'NoydbOptions must be an object' })\n return { ok: false, issues }\n }\n\n // Required: store\n if (!opts.store) {\n issues.push({ severity: 'error', code: 'missing-store', path: 'store',\n message: '`store` is required' })\n } else if (!isStoreShape(opts.store)) {\n issues.push({ severity: 'error', code: 'bad-store-shape', path: 'store',\n message: '`store` does not expose the 6-method NoydbStore contract' })\n }\n\n // sync — accept single store, SyncTarget, or SyncTarget[]\n if (opts.sync !== undefined) {\n const targets = normalizeSync(opts.sync)\n targets.forEach((t, i) => validateTarget(t, `sync[${i}]`, issues))\n }\n\n // syncPolicy requires at least one sync target\n if (opts.syncPolicy !== undefined && opts.sync === undefined) {\n issues.push({ severity: 'warn', code: 'policy-without-sync', path: 'syncPolicy',\n message: '`syncPolicy` has no effect without a `sync` target' })\n }\n\n // user is recommended\n if (typeof opts.user !== 'string' || !opts.user) {\n issues.push({ severity: 'warn', code: 'missing-user', path: 'user',\n message: '`user` identifier is recommended — audit entries default to \"anonymous\" without it' })\n }\n\n // secret-or-encryption check\n if (opts.secret === undefined && opts.secret === undefined && opts.encrypt !== false) {\n issues.push({ severity: 'warn', code: 'no-secret', path: 'secret',\n message: '`secret` / `secret` missing and `encrypt` not explicitly false — vault open will fail' })\n }\n\n const hasError = issues.some((i) => i.severity === 'error')\n return { ok: !hasError, issues }\n}\n\n/** Dynamically import a JS/MJS/CJS config file and pull the NoydbOptions\n * out of it. Accepts a default export that is either the options\n * object or a zero-arg function (sync or async) returning the options.\n *\n * **TypeScript note.** Node cannot `import()` a bare `.ts` file without\n * a loader. If the adopter's config is TypeScript, they should either\n * compile it first (`tsc foo.ts`) or run the CLI under a TS-capable\n * runtime (e.g. `tsx $(which noy-db) config validate foo.ts`). The\n * function throws a human-readable error for `.ts` input rather than\n * propagating Node's cryptic `Unknown file extension` message. */\nexport async function loadOptionsFromFile(filePath: string): Promise<unknown> {\n const abs = resolve(filePath)\n if (abs.endsWith('.ts') || abs.endsWith('.mts') || abs.endsWith('.cts')) {\n throw new Error(\n `TypeScript config files are not directly loadable — Node has no native .ts loader.\\n` +\n ` Options:\\n` +\n ` (a) compile first: tsc ${filePath} && noy-db config validate ${filePath.replace(/\\.[mc]?ts$/, '.js')}\\n` +\n ` (b) run via tsx: npx tsx $(which noy-db) config validate ${filePath}\\n` +\n ` (c) rename to .mjs/.js if your config has no TS-only syntax`,\n )\n }\n const mod = await import(pathToFileURL(abs).href) as { default?: unknown }\n const value = mod.default ?? mod\n if (typeof value === 'function') {\n return await (value as () => Promise<unknown>)()\n }\n return value\n}\n\nexport async function runConfigValidate(argv: readonly string[]): Promise<number> {\n const file = argv[0]\n if (!file) {\n process.stderr.write('usage: noy-db config validate <file.ts|js>\\n')\n return 2\n }\n let opts: unknown\n try {\n opts = await loadOptionsFromFile(file)\n } catch (err) {\n process.stderr.write(`failed to load ${file}: ${(err as Error).message}\\n`)\n return 1\n }\n\n const report = validateOptions(opts)\n process.stdout.write(JSON.stringify(report, null, 2) + '\\n')\n return report.ok ? 0 : 1\n}\n\n// ── Scaffold ────────────────────────────────────────────────────────────\n\n/**\n * The ten topology profiles. ⛔ This table IS the documentation: until 2026-10-11\n * every pointer went to `docs/guides/topology-matrix.md`, a path that existed in\n * no public repo, so profiles B–J were undiscoverable from the CLI (family#109).\n * `noy-db config profiles` prints it; a profile's `notes` string stays the one\n * authority for what it is.\n */\nexport const PROFILE_IDS = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'] as const\nexport type Profile = (typeof PROFILE_IDS)[number]\n\nexport interface ScaffoldResult {\n profile: Profile\n code: string\n env: string\n notes: string\n}\n\nexport function scaffold(profile: Profile): ScaffoldResult {\n switch (profile) {\n case 'A': return {\n profile, notes: 'Local-only single-user. No cloud.',\n code: [\n `import { createNoydb } from '@noy-db/hub'`,\n `import { toFile } from '@noy-db/to-file'`,\n ``,\n `export default {`,\n ` store: toFile({ dir: process.env.NOYDB_DATA_DIR ?? './data' }),`,\n ` user: process.env.NOYDB_USER ?? 'owner',`,\n ` secret: process.env.NOYDB_SECRET,`,\n `}`,\n ].join('\\n'),\n env: `NOYDB_USER=owner\\nNOYDB_SECRET=\\nNOYDB_DATA_DIR=./data\\n`,\n }\n case 'B': return {\n profile, notes: 'Offline-first + cloud mirror. Local authoritative, sync opportunistically.',\n code: [\n `import { createNoydb, INDEXED_STORE_POLICY } from '@noy-db/hub'`,\n `import { toBrowserIdb } from '@noy-db/to-browser-idb'`,\n `import { toAwsDynamo } from '@noy-db/to-aws-dynamo'`,\n ``,\n `export default {`,\n ` store: toBrowserIdb({ prefix: process.env.NOYDB_APP ?? 'myapp' }),`,\n ` sync: [{`,\n ` store: toAwsDynamo({`,\n ` table: process.env.NOYDB_DYNAMO_TABLE!,`,\n ` region: process.env.AWS_REGION ?? 'us-east-1',`,\n ` }),`,\n ` role: 'sync-peer',`,\n ` label: 'dynamo-live',`,\n ` }],`,\n ` syncPolicy: INDEXED_STORE_POLICY,`,\n ` user: process.env.NOYDB_USER ?? 'owner',`,\n ` secret: process.env.NOYDB_SECRET,`,\n `}`,\n ].join('\\n'),\n env: `NOYDB_USER=owner\\nNOYDB_SECRET=\\nNOYDB_APP=myapp\\nNOYDB_DYNAMO_TABLE=myapp-live\\nAWS_REGION=us-east-1\\n`,\n }\n case 'C': return {\n profile, notes: 'Records + blobs split (routeStore). Dynamo for records, S3 for blobs.',\n code: [\n `import { createNoydb, routeStore } from '@noy-db/hub'`,\n `import { toAwsDynamo } from '@noy-db/to-aws-dynamo'`,\n `import { toAwsS3 } from '@noy-db/to-aws-s3'`,\n ``,\n `export default {`,\n ` store: routeStore({`,\n ` default: toAwsDynamo({`,\n ` table: process.env.NOYDB_DYNAMO_TABLE!,`,\n ` region: process.env.AWS_REGION ?? 'us-east-1',`,\n ` }),`,\n ` blobs: toAwsS3({`,\n ` bucket: process.env.NOYDB_S3_BUCKET!,`,\n ` region: process.env.AWS_REGION ?? 'us-east-1',`,\n ` }),`,\n ` }),`,\n ` user: process.env.NOYDB_USER ?? 'owner',`,\n ` secret: process.env.NOYDB_SECRET,`,\n `}`,\n ].join('\\n'),\n env: `NOYDB_USER=owner\\nNOYDB_SECRET=\\nNOYDB_DYNAMO_TABLE=myapp-records\\nNOYDB_S3_BUCKET=myapp-blobs\\nAWS_REGION=us-east-1\\n`,\n }\n case 'G': return {\n profile, notes: 'Middleware-hardened production: retry + breaker + cache + metrics.',\n code: [\n `import { createNoydb, wrapStore, withRetry, withCircuitBreaker, withCache, withHealthCheck, withMetrics } from '@noy-db/hub'`,\n `import { toAwsDynamo } from '@noy-db/to-aws-dynamo'`,\n ``,\n `export default {`,\n ` store: wrapStore(`,\n ` toAwsDynamo({ table: process.env.NOYDB_DYNAMO_TABLE! }),`,\n ` withRetry({ maxRetries: 3 }),`,\n ` withCircuitBreaker({ failureThreshold: 5, resetTimeoutMs: 30_000 }),`,\n ` withCache({ ttlMs: 60_000 }),`,\n ` withHealthCheck(),`,\n ` withMetrics({ onOperation: op => console.log(op) }),`,\n ` ),`,\n ` user: process.env.NOYDB_USER ?? 'owner',`,\n ` secret: process.env.NOYDB_SECRET,`,\n `}`,\n ].join('\\n'),\n env: `NOYDB_USER=owner\\nNOYDB_SECRET=\\nNOYDB_DYNAMO_TABLE=myapp-prod\\n`,\n }\n case 'D': return {\n profile, notes: 'Hot + cold tiered. Records age out to archive after N days.',\n code: [\n `import { createNoydb, routeStore } from '@noy-db/hub'`,\n `import { toAwsDynamo } from '@noy-db/to-aws-dynamo'`,\n `import { toAwsS3 } from '@noy-db/to-aws-s3'`,\n ``,\n `export default {`,\n ` store: routeStore({`,\n ` default: toAwsDynamo({ table: process.env.NOYDB_DYNAMO_TABLE! }),`,\n ` age: {`,\n ` cold: toAwsS3({ bucket: process.env.NOYDB_S3_COLD! }),`,\n ` coldAfterDays: Number(process.env.NOYDB_COLD_AFTER_DAYS ?? '90'),`,\n ` },`,\n ` }),`,\n ` user: process.env.NOYDB_USER ?? 'owner',`,\n ` secret: process.env.NOYDB_SECRET,`,\n `}`,\n ].join('\\n'),\n env: `NOYDB_USER=owner\\nNOYDB_SECRET=\\nNOYDB_DYNAMO_TABLE=myapp-hot\\nNOYDB_S3_COLD=myapp-archive\\nNOYDB_COLD_AFTER_DAYS=90\\n`,\n }\n case 'E': return {\n profile, notes: 'Multi-peer team sync. Primary + peer + backup + archive.',\n code: [\n `import { createNoydb } from '@noy-db/hub'`,\n `import { toBrowserIdb } from '@noy-db/to-browser-idb'`,\n `import { toAwsDynamo } from '@noy-db/to-aws-dynamo'`,\n `import { toAwsS3 } from '@noy-db/to-aws-s3'`,\n ``,\n `export default {`,\n ` store: toBrowserIdb({ prefix: process.env.NOYDB_APP ?? 'team' }),`,\n ` sync: [`,\n ` { store: toAwsDynamo({ table: process.env.NOYDB_DYNAMO_TABLE! }), role: 'sync-peer', label: 'team-hot' },`,\n ` { store: toAwsS3({ bucket: process.env.NOYDB_S3_BACKUP! }), role: 'backup', label: 'team-backup' },`,\n ` { store: toAwsS3({ bucket: process.env.NOYDB_S3_ARCHIVE! }), role: 'archive', label: 'team-archive' },`,\n ` ],`,\n ` user: process.env.NOYDB_USER ?? 'member',`,\n ` secret: process.env.NOYDB_SECRET,`,\n `}`,\n ].join('\\n'),\n env: `NOYDB_USER=member\\nNOYDB_SECRET=\\nNOYDB_APP=team\\nNOYDB_DYNAMO_TABLE=team-hot\\nNOYDB_S3_BACKUP=team-backup\\nNOYDB_S3_ARCHIVE=team-archive\\n`,\n }\n case 'F': return {\n profile, notes: 'CRDT collaboration — Yjs-backed shared records over the encrypted envelope.',\n code: [\n `import { createNoydb } from '@noy-db/hub'`,\n `import { toBrowserIdb } from '@noy-db/to-browser-idb'`,\n `import { toAwsDynamo } from '@noy-db/to-aws-dynamo'`,\n `// import { yjsCollection } from '@noy-db/in-yjs' // use inside your app`,\n ``,\n `export default {`,\n ` store: toBrowserIdb({ prefix: 'collab' }),`,\n ` sync: [{ store: toAwsDynamo({ table: process.env.NOYDB_DYNAMO_TABLE! }), role: 'sync-peer' }],`,\n ` user: process.env.NOYDB_USER!,`,\n ` secret: process.env.NOYDB_SECRET,`,\n `}`,\n ``,\n `// After createNoydb(), replace normal collections with yjsCollection() for CRDT fields.`,\n ].join('\\n'),\n env: `NOYDB_USER=\\nNOYDB_SECRET=\\nNOYDB_DYNAMO_TABLE=collab-live\\n`,\n }\n case 'H': return {\n profile, notes: 'USB-portable — everything on a single file store, no cloud.',\n code: [\n `import { createNoydb } from '@noy-db/hub'`,\n `import { toFile } from '@noy-db/to-file'`,\n ``,\n `export default {`,\n ` store: toFile({ dir: process.env.NOYDB_DATA_DIR ?? '/Volumes/MY_USB/data' }),`,\n ` user: process.env.NOYDB_USER!,`,\n ` secret: process.env.NOYDB_SECRET,`,\n `}`,\n ].join('\\n'),\n env: `NOYDB_USER=\\nNOYDB_SECRET=\\nNOYDB_DATA_DIR=/Volumes/MY_USB/data\\n`,\n }\n case 'I': return {\n profile, notes: 'Multi-tenant geographic sharding — per-vault primary per region.',\n code: [\n `import { createNoydb } from '@noy-db/hub'`,\n `import { toAwsDynamo } from '@noy-db/to-aws-dynamo'`,\n ``,\n `// Pick the nearest region at init time based on tenant config.`,\n `const region = process.env.NOYDB_REGION ?? 'ap-southeast-1'`,\n `const table = \\`myapp-\\${region}\\``,\n ``,\n `export default {`,\n ` store: toAwsDynamo({ table, region }),`,\n ` user: process.env.NOYDB_USER!,`,\n ` secret: process.env.NOYDB_SECRET,`,\n `}`,\n ].join('\\n'),\n env: `NOYDB_USER=\\nNOYDB_SECRET=\\nNOYDB_REGION=ap-southeast-1\\n`,\n }\n case 'J': return {\n profile, notes: 'Authentication bridge (secret-less unlock via OIDC or WebAuthn).',\n code: [\n `import { createNoydb } from '@noy-db/hub'`,\n `import { toBrowserIdb } from '@noy-db/to-browser-idb'`,\n `// import { unlockWebAuthn } from '@noy-db/on-webauthn' // in the browser`,\n `// import { unlockOidc } from '@noy-db/on-oidc' // in a server app`,\n ``,\n `export default {`,\n ` store: toBrowserIdb({ prefix: process.env.NOYDB_APP ?? 'app' }),`,\n ` user: process.env.NOYDB_USER!,`,\n ` // secret supplied by the unlock method at openVault() time, not here.`,\n `}`,\n ].join('\\n'),\n env: `NOYDB_USER=\\nNOYDB_APP=app\\n`,\n }\n }\n}\n\n/** One line per profile — the `notes` string of each, which is the authority. */\nexport function listProfiles(): string {\n return PROFILE_IDS.map((id) => ` ${id} ${scaffold(id).notes}`).join('\\n')\n}\n\nexport function runConfigProfiles(): number {\n process.stdout.write(`Topology profiles for \\`noy-db config scaffold --profile=<id>\\`:\\n${listProfiles()}\\n`)\n return 0\n}\n\nexport async function runConfigScaffold(argv: readonly string[]): Promise<number> {\n if (argv.includes('--help') || argv.includes('-h')) {\n process.stdout.write(`Usage: noy-db config scaffold [--profile=<id>] (default A)\\n\\n${listProfiles()}\\n\\nThe config goes to stdout; the .env template goes to stderr.\\n`)\n return 0\n }\n const profileArg = argv.find((a) => a.startsWith('--profile='))\n const profile = (profileArg?.split('=')[1] ?? 'A') as Profile\n if (!(PROFILE_IDS as readonly string[]).includes(profile)) {\n process.stderr.write(`unknown profile: ${profile}. Valid: ${PROFILE_IDS.join(' ')} — run \\`noy-db config profiles\\`\\n`)\n return 2\n }\n const out = scaffold(profile)\n // The config goes to stdout so `noy-db config scaffold > noydb.config.mjs`\n // captures a loadable module with no .env contamination; the companion\n // .env template goes to stderr as guidance (see #705).\n process.stdout.write(`// ── noy-db config (profile ${out.profile}) ───────────────────\\n`)\n process.stdout.write(`// ${out.notes}\\n\\n`)\n process.stdout.write(out.code + '\\n')\n if (out.env) {\n process.stderr.write(`\\n# ── .env template (profile ${out.profile}) ─────────────────\\n`)\n process.stderr.write(out.env)\n }\n return 0\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────────\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v)\n}\n\nfunction isStoreShape(v: unknown): boolean {\n if (!isRecord(v)) return false\n return ['get', 'put', 'delete', 'list', 'loadAll', 'saveAll']\n .every((m) => typeof v[m] === 'function')\n}\n\nfunction normalizeSync(v: unknown): unknown[] {\n if (Array.isArray(v)) return v\n return [v]\n}\n\nfunction validateTarget(t: unknown, path: string, issues: ValidationIssue[]): void {\n if (!isRecord(t)) {\n issues.push({ severity: 'error', code: 'bad-target', path,\n message: 'sync target must be an object with { store, role }' })\n return\n }\n if (t['role'] === undefined) {\n // Bare store is allowed — core wraps it as { store, role: 'sync-peer' }\n if (!isStoreShape(t)) {\n issues.push({ severity: 'error', code: 'bad-store-shape', path,\n message: 'sync target store does not expose the 6-method contract' })\n }\n return\n }\n const validRoles = ['sync-peer', 'backup', 'archive']\n if (!validRoles.includes(safeStringify(t['role']))) {\n issues.push({ severity: 'error', code: 'bad-role', path: `${path}.role`,\n message: `role must be one of ${validRoles.join(', ')}; got ${safeStringify(t['role'])}` })\n }\n if (!t['store'] || !isStoreShape(t['store'])) {\n issues.push({ severity: 'error', code: 'bad-store-shape', path: `${path}.store`,\n message: 'sync target store does not expose the 6-method contract' })\n }\n if (t['role'] === 'archive' && isRecord(t['policy']) && isRecord(t['policy']['pull'])) {\n issues.push({ severity: 'error', code: 'archive-pull-configured', path: `${path}.policy.pull`,\n message: 'archive targets are push-only — pull policy is invalid' })\n }\n}\n","/**\n * `noy-db monitor <config.ts>` — live text dashboard of store metrics.\n *\n * Loads a `NoydbOptions` from a config file whose `store` is ALREADY metered,\n * creates a `Noydb` instance so traffic flows through it, and prints a\n * refreshing snapshot to stdout at a configurable interval. Ctrl-C to stop.\n *\n * ⛔ **This command does NOT wrap the store, and must not depend on\n * `@noy-db/to-meter` in any form** (family#100). to-meter is moving to\n * `noy-db/to`, and core publishes BEFORE its satellites — so a dependency\n * here is a publish-order back-edge. ⚠️ An `import type` does not buy an\n * exemption: what makes the back-edge is the MANIFEST entry the type needs,\n * and `workspace:*` stops resolving the moment the package leaves this\n * workspace, which breaks `pnpm install` for the whole monorepo. The types\n * below are therefore declared locally and structurally. **Do not\n * \"simplify\" them back into an import.**\n *\n * ⭐ The caller wraps instead — `store: toMeter(inner, { degradedMs: 500 })`\n * in the config file, which is also where `onDegraded` / `onRestored` now\n * live. That also makes the config honest: this command used to swap the\n * store out from under it and then report on a store the config never\n * described.\n *\n * This is intentionally CLI-first. A web dashboard is deferred: the\n * meter handle already exposes everything a dashboard needs via\n * `snapshot()` + `subscribe()`.\n *\n * @module\n */\nimport { loadOptionsFromFile } from './config.js'\n\n/**\n * One method's timings. Names only the fields this formatter READS; the index\n * signature carries the rest a real snapshot reports (`p90` today).\n *\n * ⭐ Open on purpose. Mirroring to-meter's exact field list would make this a\n * structural twin that drifts the moment to-meter adds a percentile — and\n * drift is the cost the local declaration exists to avoid, not one to\n * re-import.\n */\ninterface MeterMethodStats {\n readonly count: number\n readonly errors: number\n readonly p50: number\n readonly p99: number\n readonly max: number\n readonly avg: number\n readonly [stat: string]: number\n}\n\n/**\n * The six core store methods are always present. A real snapshot carries more\n * — `p90`, plus the optional half of the store contract (`listPage`, `tx`, …)\n * — and the index signature is what lets it satisfy this view; the NAMED\n * members are what keep `byMethod[m]` safe under `noUncheckedIndexedAccess`.\n *\n * ⚠️ That a real snapshot populates EVERY key is to-meter's invariant, not\n * ours, and it is witnessed there (`__tests__/meter-composition.test.ts`).\n * This view deliberately cannot assert it.\n */\ninterface MeterByMethod {\n readonly get: MeterMethodStats\n readonly put: MeterMethodStats\n readonly delete: MeterMethodStats\n readonly list: MeterMethodStats\n readonly loadAll: MeterMethodStats\n readonly saveAll: MeterMethodStats\n readonly [method: string]: MeterMethodStats\n}\n\n/** The slice of to-meter's `MeterSnapshot` this formatter reads. */\nexport interface MeterSnapshotView {\n readonly collectedAt: string\n readonly status: string\n readonly totalCalls: number\n readonly casConflicts: number\n readonly windowMs: number\n readonly byMethod: MeterByMethod\n}\n\n/** The slice of to-meter's meter handle this command drives. */\ninterface MeterHandleView {\n snapshot(): MeterSnapshotView\n close(): void\n}\n\nexport interface MonitorOptions {\n intervalMs: number\n iterations?: number // undefined → run forever\n}\n\nexport async function runMonitor(argv: readonly string[]): Promise<number> {\n const file = argv[0]\n if (!file) {\n process.stderr.write('usage: noy-db monitor <config.ts> [--interval=ms]\\n')\n return 2\n }\n\n const intervalArg = argv.find((a) => a.startsWith('--interval='))\n const intervalMs = intervalArg ? parseInt(intervalArg.split('=')[1] ?? '5000', 10) : 5_000\n\n let opts: Record<string, unknown>\n try {\n const loaded = await loadOptionsFromFile(file)\n if (typeof loaded !== 'object' || loaded === null) {\n process.stderr.write(`config file must export a NoydbOptions-shaped object\\n`)\n return 1\n }\n opts = loaded as Record<string, unknown>\n } catch (err) {\n process.stderr.write(`failed to load ${file}: ${(err as Error).message}\\n`)\n return 1\n }\n\n const innerStore = opts['store']\n if (!innerStore || typeof innerStore !== 'object') {\n process.stderr.write('config has no `store` — nothing to monitor\\n')\n return 1\n }\n\n // #845 — toMeter returns the store itself with the handle attached, so a\n // metered config store carries `.meter`. family#100 — the caller wraps.\n const meter = (innerStore as { meter?: MeterHandleView }).meter\n if (!meter || typeof meter.snapshot !== 'function') {\n process.stderr.write(\n 'config `store` carries no meter handle — wrap it in the config file:\\n' +\n \" import { toMeter } from '@noy-db/to-meter'\\n\" +\n ' export default { store: toMeter(inner, { degradedMs: 500 }), ... }\\n' +\n 'onDegraded / onRestored belong there too.\\n',\n )\n return 1\n }\n\n // Dynamically import hub so the CLI doesn't need to bundle it at\n // build time. Adopter's installed @noy-db/hub version wins.\n const hub = await import('@noy-db/hub') as { createNoydb: (o: unknown) => Promise<unknown> }\n await hub.createNoydb(opts)\n\n process.stdout.write(`monitoring ${file} — interval ${intervalMs}ms — Ctrl-C to stop\\n\\n`)\n\n const stop = installSigintHandler(meter)\n\n return new Promise<number>((resolveP) => {\n const timer = setInterval(() => {\n if (stop.signalled) {\n clearInterval(timer)\n meter.close()\n resolveP(0)\n return\n }\n const snap = meter.snapshot()\n process.stdout.write(formatSnapshot(snap) + '\\n')\n }, intervalMs)\n })\n}\n\nfunction installSigintHandler(meter: { close(): void }): { signalled: boolean } {\n const state = { signalled: false }\n const handler = () => { state.signalled = true; meter.close() }\n process.on('SIGINT', handler)\n process.on('SIGTERM', handler)\n return state\n}\n\nexport function formatSnapshot(snap: MeterSnapshotView): string {\n const lines: string[] = []\n const ts = new Date(snap.collectedAt).toISOString().slice(11, 19)\n lines.push(`[${ts}] status=${snap.status} calls=${snap.totalCalls} casConflicts=${snap.casConflicts} windowMs=${snap.windowMs}`)\n for (const m of ['get', 'put', 'delete', 'list', 'loadAll', 'saveAll'] as const) {\n const s = snap.byMethod[m]\n if (s.count === 0) continue\n lines.push(` ${m.padEnd(7)} count=${s.count} errors=${s.errors} p50=${s.p50}ms p99=${s.p99}ms max=${s.max}ms avg=${s.avg}ms`)\n }\n return lines.join('\\n')\n}\n","/**\n * `noy-db doctor` — readiness checks for a noy-db project (family#116).\n *\n * ## Why a doctor and not a prompt\n *\n * No scaffolder in the surveyed field (Next, Nuxt, Astro, sv, Convex, Clerk,\n * Supabase, Jazz, Better Auth…) asks about recovery, secret handling or tiers\n * at scaffold time. The ones that handle safety well do it the way Django's\n * `check --deploy`, `expo-doctor` and Supabase's advisor do: **stable ids, a\n * level, a fix hint per finding, dev/prod awareness, `--json`, and a default\n * exit threshold stated out loud** — because every lenient default measured\n * (Django exits 0 on warnings, `supabase db lint --fail-on` defaults to none)\n * was a readiness check nobody could gate on.\n *\n * ## Ids\n *\n * NOY-S0nn secret how the secret reaches createNoydb\n * NOY-R0nn recovery can the owner get back in\n * NOY-T0nn topology store, sync, local/cloud posture\n * NOY-C0nn capsule is zero-knowledge actually on\n * NOY-D0nn dependencies what is installed vs what the config imports\n * NOY-H0nn hygiene .env, .gitignore, the things git would leak\n *\n * An id never changes meaning once shipped; a retired check keeps its number.\n *\n * ## What it reads, and with what\n *\n * Everything here runs WITHOUT the user's secret. It reads the config file the\n * project already has (`loadOptionsFromFile`, the same loader `config validate`\n * uses) and, through the config's own store, the vault's public rows: the\n * `_keyring` listing (user ids, roles, slot counts — the keyring FILE is\n * plaintext JSON wrapping ciphertext DEKs) and `_meta/recovery-*`. It never\n * derives a key and never decrypts a record.\n *\n * ⚠️ Hub's root barrel exports only the PAPER recovery reader; the Shamir side\n * is internal (the same paper-first bias family#94 fixed at the read layer,\n * repeated at the export layer). So NOY-R reads `_meta/recovery-shamir`\n * through the store and parses the documented shape. ⛔ Do not \"fix\" this by\n * asking hub to export the document readers — that publishes a storage shape\n * that family#96 may retire. The right swap is hub's structured\n * `listRecoveryEnrollments(store, vault)` (family#122); adopt it when it ships.\n *\n * ## Satellite checks\n *\n * A package can contribute checks by exporting `noydbDoctorChecks` (an array of\n * {@link DoctorCheck}) from its ROOT entry. The doctor imports each `@noy-db/*`\n * dependency named in the project's package.json and runs what it finds. A\n * package that exports nothing contributes nothing; a package that fails to\n * import is reported as `NOY-D002`, never thrown.\n */\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs'\nimport { dirname, join, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport type { NoydbStore } from '@noy-db/hub'\nimport { loadOptionsFromFile } from './pod/config.js'\n\nexport type DoctorLevel = 'error' | 'warn' | 'info'\nexport type DoctorMode = 'dev' | 'prod'\n\nexport interface DoctorFinding {\n /** Stable, e.g. `NOY-R001`. */\n readonly id: string\n readonly level: DoctorLevel\n /** One line: what is wrong. */\n readonly message: string\n /** One line: what to do. Always present — a finding without a fix is a complaint. */\n readonly fix: string\n /** Where it was seen: a config path, a file, a vault. */\n readonly where?: string\n}\n\n/** The input a check sees. Nothing here requires a secret. */\nexport interface DoctorContext {\n readonly mode: DoctorMode\n /** The project directory (where package.json lives). */\n readonly root: string\n /** The config file, when one was found/given. */\n readonly configPath?: string\n /** The loaded options object, untyped on purpose — a check narrows what it reads. */\n readonly options?: Record<string, unknown>\n /** The store from the config, when it has one. */\n readonly store?: NoydbStore\n /** Vault names to inspect. From `--vault`, else the ones the store lists, else none. */\n readonly vaults: readonly string[]\n /** package.json of the project, parsed. */\n readonly manifest: { dependencies?: Record<string, string>; devDependencies?: Record<string, string> }\n}\n\nexport interface DoctorCheck {\n readonly id: string\n readonly run: (ctx: DoctorContext) => Promise<readonly DoctorFinding[]> | readonly DoctorFinding[]\n}\n\nexport interface DoctorReport {\n readonly mode: DoctorMode\n readonly findings: readonly DoctorFinding[]\n readonly counts: Record<DoctorLevel, number>\n /** Which check ids ran, so a `--json` consumer can tell \"clean\" from \"not covered\". */\n readonly ran: readonly string[]\n}\n\n// ─── Helpers ──────────────────────────────────────────────────────────────\n\nconst f = (id: string, level: DoctorLevel, message: string, fix: string, where?: string): DoctorFinding =>\n where === undefined ? { id, level, message, fix } : { id, level, message, fix, where }\n\nconst isRecord = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null\n\n/** `.gitignore` says it ignores `name` — a plain-line check, not a full gitignore engine. */\nfunction gitignores(root: string, name: string): boolean {\n const p = join(root, '.gitignore')\n if (!existsSync(p)) return false\n return readFileSync(p, 'utf8').split('\\n').some((l) => {\n const t = l.trim()\n return t === name || t === `/${name}` || t === `${name}*` || t === '.env*' && name.startsWith('.env')\n })\n}\n\nasync function listSafely(store: NoydbStore, vault: string, collection: string): Promise<string[] | null> {\n try { return await store.list(vault, collection) } catch { return null }\n}\n\n// ─── Built-in checks ──────────────────────────────────────────────────────\n\nconst secretChecks: DoctorCheck = {\n id: 'NOY-S',\n run: ({ options, configPath, mode }) => {\n if (!options) return []\n const out: DoctorFinding[] = []\n const secret = options['secret']\n if (typeof secret === 'string' && secret.length > 0) {\n out.push(f('NOY-S001', 'error',\n 'the secret is a literal string in the config — it ships with your source',\n 'read it from the environment (`secret: process.env.NOYDB_SECRET`) or prompt for it; keep `.env` out of git (see NOY-H001)',\n configPath))\n } else if (secret === undefined && options['getKeyring'] === undefined && options['secretMode'] !== 'managed') {\n out.push(f('NOY-S002', mode === 'prod' ? 'warn' : 'info',\n 'no secret and no getKeyring in the config — the app must supply one at runtime',\n 'that is fine for an app that prompts; set `secret: process.env.NOYDB_SECRET` for a headless process',\n configPath))\n }\n if (options['secretMode'] === 'managed' && options['sealingKey'] === undefined) {\n out.push(f('NOY-S003', 'error',\n '`secretMode: \"managed\"` without a `sealingKey` — managed mode has nothing to seal with',\n 'pass a sealer from an `@noy-db/at-*` package (`sealingKey: awsKms({...})`), or drop managed mode',\n configPath))\n }\n return out\n },\n}\n\nconst capsuleChecks: DoctorCheck = {\n id: 'NOY-C',\n run: ({ options, configPath, mode, manifest }) => {\n const out: DoctorFinding[] = []\n if (options?.['encrypt'] === false) {\n out.push(f('NOY-C001', mode === 'prod' ? 'error' : 'warn',\n '`encrypt: false` — records are stored as plaintext; this is not zero-knowledge',\n 'remove `encrypt: false` for anything that leaves your machine; it exists for the \"see the ciphertext\" control and tests',\n configPath))\n }\n const deps = { ...manifest.dependencies, ...manifest.devDependencies }\n if ('@noy-db/exclave-plain' in deps) {\n out.push(f('NOY-C002', mode === 'prod' ? 'error' : 'warn',\n '`@noy-db/exclave-plain` is installed — when bound (`noy-db:exclave-plain` condition) the vault stores readable rows',\n 'keep it to builds whose access control lives in the store (IAM, a DB grant); never in a build that serves untrusted readers'))\n }\n return out\n },\n}\n\nconst topologyChecks: DoctorCheck = {\n id: 'NOY-T',\n run: ({ options, configPath, mode }) => {\n if (!options) return []\n const out: DoctorFinding[] = []\n const store = options['store']\n const storeName = isRecord(store) && typeof store['name'] === 'string' ? store['name'] : undefined\n if (storeName === 'memory' || storeName === 'memoryStore') {\n out.push(f('NOY-T001', mode === 'prod' ? 'error' : 'info',\n 'the store is in-memory — data does not survive a reload',\n 'pick a persistent store: `noy-db add @noy-db/to-browser-idb` (browser) or `@noy-db/to-file` (Node)',\n configPath))\n }\n const sync = options['sync']\n const hasSync = Array.isArray(sync) ? sync.length > 0 : sync !== undefined && sync !== null\n if (!hasSync) {\n out.push(f('NOY-T002', 'info',\n 'no sync target — this vault lives in one store only; lose it and the data is gone',\n 'add a second store as a sync target (a cloud bucket, a file on a USB stick): `noy-db config profiles` shows the shapes',\n configPath))\n }\n return out\n },\n}\n\nconst recoveryChecks: DoctorCheck = {\n id: 'NOY-R',\n run: async ({ store, vaults, options, mode }) => {\n if (!store || vaults.length === 0) return []\n const out: DoctorFinding[] = []\n for (const vault of vaults) {\n const users = await listSafely(store, vault, '_keyring')\n if (users === null) continue\n if (users.length === 0) continue // not created yet — nothing to say\n\n // Recovery: paper entries live in _meta/recovery-paper, Shamir in _meta/recovery-shamir.\n // Both are plaintext JSON docs with an `entries` array (the credential is what the\n // user holds; the doc is inert without it) — see hub's with-party/team/recovery.ts.\n const entries = async (id: string): Promise<number> => {\n try {\n const env = await store.get(vault, '_meta', id)\n if (!env) return 0\n const doc = JSON.parse(env._data ?? '') as { entries?: unknown[] }\n return Array.isArray(doc.entries) ? doc.entries.length : 0\n } catch { return 0 }\n }\n const paper = await entries('recovery-paper')\n const shamir = await entries('recovery-shamir')\n if (paper + shamir === 0) {\n out.push(f('NOY-R001', mode === 'prod' ? 'error' : 'warn',\n `vault \"${vault}\" has no recovery enrolled — a forgotten secret means the data is gone`,\n 'enrol one: `noy-db add @noy-db/on-recovery` (printable codes) or `@noy-db/on-shamir` (k-of-n shares), then `db.team.enrollRecovery(...)`',\n vault))\n } else if (shamir === 0 && options?.['secretMode'] === 'managed') {\n out.push(f('NOY-R002', 'error',\n `vault \"${vault}\" is managed-mode with only paper recovery — managed mode requires a strong (Shamir) profile`,\n 'enrol Shamir: `noy-db add @noy-db/on-shamir`', vault))\n }\n\n // Owners: one owner is one point of total loss.\n let owners = 0\n for (const uid of users) {\n try {\n const env = await store.get(vault, '_keyring', uid)\n const file = env ? (JSON.parse(env._data ?? '') as { role?: string }) : null\n if (file?.role === 'owner') owners++\n } catch { /* unreadable keyring row: not this check's finding */ }\n }\n if (owners === 1 && users.length > 1) {\n out.push(f('NOY-R003', 'info',\n `vault \"${vault}\" has one owner among ${users.length} users`,\n 'a second owner can recover members and rotate keys when the first is unavailable: `noy-db user add <id> owner …`',\n vault))\n }\n }\n return out\n },\n}\n\nconst dependencyChecks: DoctorCheck = {\n id: 'NOY-D',\n run: ({ root, manifest, configPath }) => {\n const out: DoctorFinding[] = []\n const deps = { ...manifest.dependencies, ...manifest.devDependencies }\n const hub = deps['@noy-db/hub']\n if (!hub) {\n out.push(f('NOY-D001', 'error', '`@noy-db/hub` is not a dependency of this project', 'add it: `pnpm add @noy-db/hub`', join(root, 'package.json')))\n }\n // Every @noy-db package the config file imports must be installed here, or the\n // app fails at import time in production (the scaffold-symbol class, family#107).\n if (configPath && existsSync(configPath)) {\n const src = readFileSync(configPath, 'utf8')\n const named = new Set([...src.matchAll(/from\\s+['\"](@noy-db\\/[a-z0-9-]+)/g)].map((m) => m[1]!))\n for (const pkg of named) {\n if (!(pkg in deps)) {\n out.push(f('NOY-D002', 'error', `the config imports \\`${pkg}\\`, which is not in package.json`, `install it: \\`pnpm add ${pkg}\\``, configPath))\n }\n }\n }\n return out\n },\n}\n\nconst hygieneChecks: DoctorCheck = {\n id: 'NOY-H',\n run: ({ root }) => {\n const out: DoctorFinding[] = []\n const hasEnv = existsSync(join(root, '.env')) || existsSync(join(root, '.env.local'))\n if (hasEnv && !gitignores(root, '.env')) {\n out.push(f('NOY-H001', 'error', 'a `.env` file exists and `.gitignore` does not ignore it — one `git add .` away from committing secrets',\n 'add `.env` and `.env.*` to .gitignore (`noy-db doctor --fix` does this)', join(root, '.gitignore')))\n }\n return out\n },\n}\n\nexport const BUILTIN_CHECKS: readonly DoctorCheck[] = [secretChecks, capsuleChecks, topologyChecks, recoveryChecks, dependencyChecks, hygieneChecks]\n\n// ─── Satellite checks ─────────────────────────────────────────────────────\n\n/** Import each `@noy-db/*` dependency and collect its `noydbDoctorChecks`, if any. */\nasync function satelliteChecks(root: string, manifest: DoctorContext['manifest']): Promise<{ checks: DoctorCheck[]; findings: DoctorFinding[] }> {\n const checks: DoctorCheck[] = []\n const findings: DoctorFinding[] = []\n const names = Object.keys({ ...manifest.dependencies, ...manifest.devDependencies }).filter((n) => n.startsWith('@noy-db/') && n !== '@noy-db/hub')\n for (const name of names) {\n const manifestPath = join(root, 'node_modules', ...name.split('/'), 'package.json')\n if (!existsSync(manifestPath)) continue // declared, not installed — NOY-D is about the config, not the lockfile\n try {\n const pkg = JSON.parse(readFileSync(manifestPath, 'utf8')) as { exports?: unknown; main?: string; module?: string }\n const entry = isRecord(pkg.exports) && '.' in pkg.exports\n ? (() => { const dot = pkg.exports['.']; return typeof dot === 'string' ? dot : isRecord(dot) ? (dot['default'] as string | undefined) ?? (dot['import'] as string | undefined) : undefined })()\n : pkg.module ?? pkg.main\n if (!entry) continue\n const mod = await import(pathToFileURL(join(dirname(manifestPath), entry)).href) as { noydbDoctorChecks?: unknown }\n if (Array.isArray(mod.noydbDoctorChecks)) {\n for (const c of mod.noydbDoctorChecks as DoctorCheck[]) if (c && typeof c.id === 'string' && typeof c.run === 'function') checks.push(c)\n }\n } catch (err) {\n findings.push(f('NOY-D003', 'warn', `\\`${name}\\` is installed but failed to import: ${(err as Error).message.split('\\n')[0]}`, 'reinstall it, or check its peer dependencies', name))\n }\n }\n return { checks, findings }\n}\n\n// ─── The run ──────────────────────────────────────────────────────────────\n\nexport interface DoctorOptions {\n readonly root?: string\n readonly configPath?: string\n readonly mode?: DoctorMode\n readonly vaults?: readonly string[]\n /** Skip importing satellites (tests, or an untrusted node_modules). */\n readonly noSatellites?: boolean\n}\n\nexport async function doctor(opts: DoctorOptions = {}): Promise<DoctorReport> {\n const root = resolve(opts.root ?? process.cwd())\n const mode: DoctorMode = opts.mode ?? (process.env['NODE_ENV'] === 'production' ? 'prod' : 'dev')\n const manifestPath = join(root, 'package.json')\n const manifest = existsSync(manifestPath) ? (JSON.parse(readFileSync(manifestPath, 'utf8')) as DoctorContext['manifest']) : {}\n\n const configPath = opts.configPath ?? ['noydb.config.mjs', 'noydb.config.js'].map((n) => join(root, n)).find((p) => existsSync(p))\n let options: Record<string, unknown> | undefined\n let store: NoydbStore | undefined\n const findings: DoctorFinding[] = []\n if (configPath) {\n try {\n const loaded = await loadOptionsFromFile(configPath)\n if (isRecord(loaded)) {\n options = loaded\n const s = loaded['store']\n if (isRecord(s) && typeof s['get'] === 'function' && typeof s['list'] === 'function') store = s as unknown as NoydbStore\n }\n } catch (err) {\n findings.push(f('NOY-D004', 'error', `the config file did not load: ${(err as Error).message.split('\\n')[0]}`, 'run `noy-db config validate <file>` for the details', configPath))\n }\n }\n\n let vaults: readonly string[] = opts.vaults ?? []\n if (vaults.length === 0 && store && typeof (store as { listVaults?: unknown }).listVaults === 'function') {\n try { vaults = await (store as unknown as { listVaults(): Promise<string[]> }).listVaults() } catch { vaults = [] }\n }\n\n const ctx: DoctorContext = { mode, root, manifest, vaults, ...(configPath ? { configPath } : {}), ...(options ? { options } : {}), ...(store ? { store } : {}) }\n const sat = opts.noSatellites ? { checks: [], findings: [] } : await satelliteChecks(root, manifest)\n findings.push(...sat.findings)\n const checks = [...BUILTIN_CHECKS, ...sat.checks]\n const ran: string[] = []\n for (const c of checks) {\n ran.push(c.id)\n try { findings.push(...(await c.run(ctx))) }\n catch (err) { findings.push(f('NOY-D005', 'warn', `check ${c.id} threw: ${(err as Error).message.split('\\n')[0]}`, 'report this against the package that registered the check')) }\n }\n const counts: Record<DoctorLevel, number> = { error: 0, warn: 0, info: 0 }\n for (const x of findings) counts[x.level]++\n return { mode, findings, counts, ran }\n}\n\n// ─── --fix ────────────────────────────────────────────────────────────────\n\n/** The idempotent fixes. Returns what it changed. Only NOY-H001 today. */\nexport function applyFixes(root: string, report: DoctorReport): string[] {\n const done: string[] = []\n if (report.findings.some((x) => x.id === 'NOY-H001')) {\n const p = join(root, '.gitignore')\n const cur = existsSync(p) ? readFileSync(p, 'utf8') : ''\n const add = ['.env', '.env.*'].filter((l) => !cur.split('\\n').some((x) => x.trim() === l))\n if (add.length) {\n const next = cur + (cur.endsWith('\\n') || cur === '' ? '' : '\\n') + '# noy-db doctor (NOY-H001): never commit secrets\\n' + add.join('\\n') + '\\n'\n writeFileSync(p, next)\n done.push(`.gitignore += ${add.join(', ')}`)\n }\n }\n return done\n}\n\n// ─── CLI ──────────────────────────────────────────────────────────────────\n\nconst LEVEL_RANK: Record<DoctorLevel, number> = { error: 0, warn: 1, info: 2 }\nconst ICON: Record<DoctorLevel, string> = { error: '✗', warn: '!', info: '·' }\n\nexport function formatReport(r: DoctorReport): string {\n const lines = [`noy-db doctor — mode ${r.mode}, ${r.ran.length} check group(s)`]\n if (r.findings.length === 0) lines.push(' ✓ nothing to report')\n for (const x of [...r.findings].sort((a, b) => LEVEL_RANK[a.level] - LEVEL_RANK[b.level] || a.id.localeCompare(b.id))) {\n lines.push(` ${ICON[x.level]} ${x.id} ${x.message}${x.where ? ` (${x.where})` : ''}`)\n lines.push(` → ${x.fix}`)\n }\n lines.push(` ${r.counts.error} error · ${r.counts.warn} warn · ${r.counts.info} info`)\n return lines.join('\\n')\n}\n\nexport async function runDoctor(argv: readonly string[]): Promise<number> {\n const flag = (n: string): string | undefined => argv.find((a) => a.startsWith(`--${n}=`))?.slice(n.length + 3)\n if (argv.includes('--help') || argv.includes('-h')) {\n process.stdout.write([\n 'Usage: noy-db doctor [config.mjs] [--mode=dev|prod] [--vault=<name>] [--json] [--fix] [--fail-level=error|warn|info]',\n '',\n 'Readiness checks that need no secret. Ids are stable (NOY-S secret · NOY-R recovery · NOY-T topology ·',\n 'NOY-C capsule · NOY-D dependencies · NOY-H hygiene); every finding carries a fix.',\n '',\n ' --mode dev (default unless NODE_ENV=production) or prod — the same finding can be a warn in dev and an error in prod',\n ' --fail-level exit 1 at or above this level. DEFAULT: error. Pass --fail-level=warn to gate a CI job on warnings too.',\n ' --fix apply the idempotent fixes (today: gitignore .env)',\n ' --json machine-readable report',\n ' crypto the in-memory crypto round-trip (formerly `noy-db verify`)',\n ].join('\\n') + '\\n')\n return 0\n }\n const configArg = argv.find((a) => !a.startsWith('--'))\n const modeArg = flag('mode')\n if (modeArg !== undefined && modeArg !== 'dev' && modeArg !== 'prod') { process.stderr.write(`--mode must be dev or prod (got ${modeArg})\\n`); return 2 }\n const failArg = (flag('fail-level') ?? 'error') as DoctorLevel\n if (!(failArg in LEVEL_RANK)) { process.stderr.write(`--fail-level must be error, warn or info (got ${failArg})\\n`); return 2 }\n const vault = flag('vault')\n\n const report = await doctor({\n ...(configArg ? { configPath: resolve(configArg) } : {}),\n ...(modeArg ? { mode: modeArg } : {}),\n ...(vault ? { vaults: [vault] } : {}),\n })\n let fixed: string[] = []\n if (argv.includes('--fix')) fixed = applyFixes(process.cwd(), report)\n\n if (argv.includes('--json')) process.stdout.write(JSON.stringify({ ...report, fixed }, null, 2) + '\\n')\n else {\n process.stdout.write(formatReport(report) + '\\n')\n for (const d of fixed) process.stdout.write(` fixed: ${d}\\n`)\n process.stdout.write(` (exit is non-zero at --fail-level=${failArg}; default error)\\n`)\n }\n const worst = Math.min(...report.findings.map((x) => LEVEL_RANK[x.level]), 99)\n return worst <= LEVEL_RANK[failArg] ? 1 : 0\n}\n","/**\n * `noy-db add <package>` and `noy-db add --list [prefix]` — the landscape, one\n * verb (family#118).\n *\n * ## What it is\n *\n * The scaffolder keeps the first install to hub + one store. Everything else\n * — 60-odd `to-* in-* on-* as-* by-* at-*` packages — is reached from here,\n * the way `astro add`, `nuxt module add` and `sv add` reach their ecosystems:\n * install it, wire what can be wired, and say what you just did to your trust\n * boundary. That last sentence is the part no other framework needs and this\n * one cannot skip: `as-aws-s3` and `to-aws-s3` differ in exactly the property\n * that decides who can read your data, and the prefix is the only tell.\n *\n * ## The catalog\n *\n * `--list` reads `catalog.json`, generated by the docs repo from\n * `features.yaml` and served at the docs site root. The CLI tries the live\n * URL (a few seconds, no retries) and falls back to the snapshot it ships in\n * `templates/catalog.json`, so the list works offline and the day the site is\n * down. The trust text per prefix travels IN the catalog so the family states\n * it once; this file holds no copy of it.\n *\n * ## What `add` wires\n *\n * - any package: `pnpm add <pkg>` (npm/yarn/bun detected from the lockfile).\n * - an `in-*` Nuxt module in a Nuxt project: appended to `modules` through the\n * same magicast path the wizard's augment mode uses — idempotent, diffed.\n * - everything else: the import line to paste, because a store, an unlock\n * method or an export format is composed in code, where a config is the\n * app's and not ours to rewrite.\n */\nimport { existsSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { spawnSync } from 'node:child_process'\nimport pc from 'picocolors'\nimport { templateDir } from '../wizard/render.js'\n\nexport const CATALOG_URL = 'https://noy-db.lanna.ai/catalog.json'\n\nexport interface CatalogPrefix {\n readonly reads: string\n readonly layer: string\n readonly plaintext: string\n readonly trust: string\n}\nexport interface CatalogPackage {\n readonly id: string\n readonly name: string\n readonly package: string\n readonly prefix: string\n readonly section: string\n readonly status: string\n readonly producer: string | null\n readonly useCase?: string\n}\nexport interface Catalog {\n readonly schemaVersion: number\n readonly prefixes: Record<string, CatalogPrefix>\n readonly packages: readonly CatalogPackage[]\n}\n\n/** The shipped snapshot — always available. */\nexport function bundledCatalog(): Catalog {\n // templateDir resolves `<package>/templates/<name>` from the built file's location.\n return JSON.parse(readFileSync(templateDir('catalog.json'), 'utf8')) as Catalog\n}\n\n/** Live first, snapshot second. Says which one it used. */\nexport async function loadCatalog(opts: { offline?: boolean; timeoutMs?: number; fetchImpl?: typeof fetch } = {}): Promise<{ catalog: Catalog; source: 'live' | 'bundled' }> {\n if (!opts.offline) {\n try {\n const ctl = new AbortController()\n const t = setTimeout(() => ctl.abort(), opts.timeoutMs ?? 3000)\n const res = await (opts.fetchImpl ?? fetch)(CATALOG_URL, { signal: ctl.signal })\n clearTimeout(t)\n if (res.ok) {\n const catalog = (await res.json()) as Catalog\n if (catalog.schemaVersion === 1 && Array.isArray(catalog.packages)) return { catalog, source: 'live' }\n }\n } catch { /* fall through to the snapshot */ }\n }\n return { catalog: bundledCatalog(), source: 'bundled' }\n}\n\n/** `to-`, `to`, `@noy-db/to-` and `storage`-style words all mean the to- prefix. */\nexport function normalisePrefix(input: string | undefined): string | undefined {\n if (!input) return undefined\n const s = input.replace(/^@noy-db\\//, '').replace(/-$/, '')\n return /^(to|in|on|as|by|at)$/.test(s) ? `${s}-` : undefined\n}\n\nexport function formatList(catalog: Catalog, prefix?: string): string {\n const groups = new Map<string, CatalogPackage[]>()\n for (const p of catalog.packages) {\n if (prefix && p.prefix !== prefix) continue\n ;(groups.get(p.prefix) ?? groups.set(p.prefix, []).get(p.prefix)!).push(p)\n }\n const out: string[] = []\n for (const [pfx, pkgs] of groups) {\n const g = catalog.prefixes[pfx]\n out.push(`${pc.bold(pfx)} ${g?.reads ?? ''} ${pc.dim(`· plaintext: ${g?.plaintext ?? '?'}`)}`)\n for (const p of pkgs) out.push(` ${p.package.padEnd(30)} ${p.name}${p.status !== 'stable' ? pc.dim(` (${p.status})`) : ''}`)\n out.push('')\n }\n out.push(pc.dim(`noy-db add <package> installs one and tells you what trust boundary it crosses.`))\n return out.join('\\n')\n}\n\n/** Resolve a user's spelling to a catalog row: `to-aws-s3`, `aws-s3`, `@noy-db/to-aws-s3`. */\nexport function findPackage(catalog: Catalog, input: string): CatalogPackage | undefined {\n const full = input.startsWith('@noy-db/') ? input : `@noy-db/${input}`\n return catalog.packages.find((p) => p.package === full)\n ?? catalog.packages.find((p) => p.package.endsWith(`-${input}`))\n}\n\nexport type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun'\n/** Detect from the lockfile present in `root`; pnpm when nothing says otherwise. */\nexport function detectPackageManager(root: string): PackageManager {\n if (existsSync(join(root, 'bun.lockb')) || existsSync(join(root, 'bun.lock'))) return 'bun'\n if (existsSync(join(root, 'yarn.lock'))) return 'yarn'\n if (existsSync(join(root, 'package-lock.json'))) return 'npm'\n return 'pnpm'\n}\n\n/** The install command, as argv. `yarn add` and `bun add` differ from `npm install`. */\nexport function installArgv(pm: PackageManager, pkg: string, dev: boolean): string[] {\n switch (pm) {\n case 'npm': return ['npm', 'install', ...(dev ? ['--save-dev'] : []), pkg]\n case 'yarn': return ['yarn', 'add', ...(dev ? ['--dev'] : []), pkg]\n case 'bun': return ['bun', 'add', ...(dev ? ['--dev'] : []), pkg]\n default: return ['pnpm', 'add', ...(dev ? ['-D'] : []), pkg]\n }\n}\n\n/** What to paste after installing, by layer. Short on purpose: the README of the package is the manual. */\nexport function snippetFor(p: CatalogPackage): string {\n const name = p.package.replace('@noy-db/', '')\n const camel = name.replace(/-([a-z0-9])/g, (_, c: string) => c.toUpperCase())\n switch (p.prefix) {\n case 'to-': return `import { ${camel} } from '${p.package}'\\n// createNoydb({ store: ${camel}({ /* … */ }), user, secret }) — or as a sync target: sync: [{ store: ${camel}(...), role: 'backup' }]`\n case 'as-': return `import { ${camel.replace(/^as/, 'to')} } from '${p.package}'\\n// await vault.export(${camel.replace(/^as/, 'to')}(), { collections: ['…'] }) ⚠️ the output is plaintext`\n case 'on-': return `import … from '${p.package}'\\n// an unlock method is enrolled per user; see the package README for enroll*/unlock* and \\`noy-db doctor\\` for what is enrolled`\n case 'by-': return `import … from '${p.package}'\\n// a session-share transport; pair it on both peers that already hold the secret`\n case 'at-': return `import … from '${p.package}'\\n// createNoydb({ secretMode: 'managed', sealingKey: <provider>(…) }) ⚠️ the host you name can decrypt the slice it unseals`\n default: return `import … from '${p.package}'`\n }\n}\n\nexport interface AddOptions {\n readonly root?: string\n readonly dev?: boolean\n readonly dryRun?: boolean\n readonly offline?: boolean\n readonly fetchImpl?: typeof fetch\n}\n\nexport interface AddResult {\n readonly pkg: CatalogPackage\n readonly installed: boolean\n readonly command: string[]\n readonly wired: 'nuxt-module' | null\n readonly trust: string\n readonly snippet: string\n}\n\nexport async function addPackage(input: string, opts: AddOptions = {}): Promise<AddResult> {\n const root = opts.root ?? process.cwd()\n const { catalog } = await loadCatalog({ ...(opts.offline !== undefined ? { offline: opts.offline } : {}), ...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}) })\n const pkg = findPackage(catalog, input)\n if (!pkg) {\n const near = catalog.packages.filter((p) => p.package.includes(input.replace(/^@noy-db\\//, ''))).map((p) => p.package).slice(0, 5)\n throw new Error(`no package named \"${input}\" in the catalog${near.length ? ` — did you mean: ${near.join(', ')}` : ''}. \\`noy-db add --list\\` shows them all.`)\n }\n const pm = detectPackageManager(root)\n const command = installArgv(pm, pkg.package, opts.dev ?? false)\n let installed = false\n if (!opts.dryRun) {\n const r = spawnSync(command[0]!, command.slice(1), { cwd: root, stdio: 'inherit' })\n if (r.status !== 0) throw new Error(`${command.join(' ')} exited ${r.status ?? 'with a signal'}`)\n installed = true\n }\n\n // Wire a Nuxt module when there is a nuxt.config and the package is an in-* with a Nuxt module.\n let wired: AddResult['wired'] = null\n if (pkg.prefix === 'in-' && pkg.package === '@noy-db/in-nuxt') {\n const cfg = ['nuxt.config.ts', 'nuxt.config.js', 'nuxt.config.mjs'].map((n) => join(root, n)).find((p) => existsSync(p))\n if (cfg && !opts.dryRun) {\n const { augmentNuxtConfig, writeAugmentedConfig } = await import('../wizard/augment.js')\n const res = await augmentNuxtConfig({ configPath: cfg, adapter: 'browser' })\n if (res.kind === 'proposed-change') { await writeAugmentedConfig(cfg, res.newCode); wired = 'nuxt-module' }\n else if (res.kind === 'already-configured') wired = 'nuxt-module'\n }\n }\n const trust = catalog.prefixes[pkg.prefix]?.trust ?? ''\n return { pkg, installed, command, wired, trust, snippet: snippetFor(pkg) }\n}\n\n// ─── CLI ──────────────────────────────────────────────────────────────────\n\nexport async function runAddPackage(argv: readonly string[]): Promise<number> {\n const flags = new Set(argv.filter((a) => a.startsWith('--')))\n const positional = argv.filter((a) => !a.startsWith('--'))\n if (flags.has('--help') || flags.has('-h')) {\n process.stdout.write([\n 'Usage: noy-db add <package> [--dev] [--dry-run] [--offline]',\n ' noy-db add --list [to|in|on|as|by|at]',\n '',\n 'Installs a @noy-db package with your package manager, wires it where a config can be edited',\n '(a Nuxt module), prints the import to paste otherwise, and says what trust boundary the layer crosses.',\n `The list comes from ${CATALOG_URL}, falling back to the copy this version ships.`,\n ].join('\\n') + '\\n')\n return 0\n }\n if (flags.has('--list')) {\n const prefix = normalisePrefix(positional[0])\n if (positional[0] && !prefix) { process.stderr.write(`unknown prefix \"${positional[0]}\" — one of to in on as by at\\n`); return 2 }\n const { catalog, source } = await loadCatalog({ offline: flags.has('--offline') })\n process.stdout.write(formatList(catalog, prefix) + '\\n' + pc.dim(`(catalog: ${source}${source === 'bundled' ? ', ' + catalog.packages.length + ' packages' : ''})`) + '\\n')\n return 0\n }\n const input = positional[0]\n if (!input) { process.stderr.write('usage: noy-db add <package> (or `noy-db add --list`)\\n'); return 2 }\n try {\n const r = await addPackage(input, { dev: flags.has('--dev'), dryRun: flags.has('--dry-run'), offline: flags.has('--offline') })\n process.stdout.write(`${r.installed ? pc.green('✔') : pc.dim('dry:')} ${r.command.join(' ')}\\n`)\n if (r.wired) process.stdout.write(`${pc.green('✔')} wired into nuxt.config (${r.wired})\\n`)\n process.stdout.write(`\\n${pc.bold(r.pkg.prefix)} ${r.trust}\\n\\n${r.snippet}\\n`)\n return 0\n } catch (err) {\n process.stderr.write(`${pc.red('error:')} ${(err as Error).message}\\n`)\n return 1\n }\n}\n","/**\n * Template loader and renderer.\n *\n * Templates live under `packages/create-noy-db/templates/<template-name>/`\n * as a literal directory tree. Files are read verbatim from disk; the\n * renderer walks the tree, substitutes a small set of placeholder tokens,\n * and writes the result to the target directory.\n *\n * The placeholder syntax is intentionally minimal: `{{TOKEN}}` (uppercase,\n * no spaces, no nested expressions). This avoids dragging in handlebars\n * or any templating library, keeps the bundle small, and means there's\n * exactly one rule to learn when authoring a template.\n *\n * Files whose names start with `_` are renamed to `.` on copy. This is\n * how we ship `_gitignore` (`.gitignore`) without npm trying to interpret\n * it as part of the package contents.\n */\n\nimport { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\n/**\n * Tokens the renderer recognizes. Authoring rule for templates: every\n * placeholder must be a member of this set, otherwise template authors\n * will hit silent no-ops at render time.\n */\nexport interface RenderTokens {\n /** The user-supplied project name. Used for `package.json#name` etc. */\n PROJECT_NAME: string\n /** The chosen adapter (browser/file/memory). */\n ADAPTER: string\n /**\n * The npm package that adapter resolves to, for prose: `@noy-db/to-browser-idb`,\n * `@noy-db/to-file`, or `@noy-db/hub` (memory is hub's built-in `memoryStore`).\n * Until 2026-10-11 the Nuxt README rendered `@noy-db/{{ADAPTER}}` → `@noy-db/browser`,\n * a package that does not exist (family#108).\n */\n ADAPTER_PACKAGE: string\n /** \"true\" or \"false\" — written into `nuxt.config.ts` for `noydb.devtools`. */\n DEVTOOLS: string\n /** A literal `[...]` block of seed records, or `[]` if `sampleData` is false. */\n SEED_INVOICES: string\n /**\n * The version every `@noy-db/*` dependency (and the template's own\n * `create-noy-db` devDep) is pinned to, written as `^{{NOYDB_VERSION}}`\n * in template `package.json`s. Filled from this package's own version\n * (see `ownVersion()`), so scaffolded apps always depend on the family\n * line the wizard itself shipped with (#703).\n *\n * ⚠️ That is correct ONLY while this package shares the family's lockstep\n * line. It stopped sharing it once — the release normaliser filtered\n * members by the `@noy-db/` prefix and this is the one unscoped package —\n * and every scaffolded app then pinned `^0.3.4` against a `0.7.x` line\n * (#1313). The normaliser now tests workspace membership, and\n * `__tests__/pins-resolve.test.ts` asserts the REAL token value equals the\n * workspace version of every package the templates pin.\n */\n NOYDB_VERSION: string\n /**\n * The version every `@noy-db/in-*` dependency is pinned to, written as\n * `^{{NOYDB_IN_VERSION}}`. The framework bindings left the core monorepo\n * for `noy-db/in` on 2026-09-21 and version on their OWN line, so\n * `ownVersion()` no longer speaks for them. Filled from\n * `NOYDB_IN_VERSION` below.\n */\n NOYDB_IN_VERSION: string\n}\n\n/**\n * The published `noy-db/in` line the templates pin. Not derivable from this\n * workspace — the packages are not in it — so it is a constant, bumped when\n * `noy-db/in` cuts. `__tests__/pins-resolve.test.ts`'s registry test\n * (`NOYDB_SCAFFOLD_INSTALL=1`) is what proves the value resolves on npm.\n */\n// ⚠️ Was '0.8.0' through create-noy-db 0.9.0, whose scaffolds paired hub 0.9.0\n// with in-* 0.8.0 (peer hub ^0.8.0) — an ERESOLVE under npm's strict peers.\n// Set to the in line cut in the SAME family cut as this hub (0.10.0-pre.0,\n// ruled 2026-09-29); resolvable once `in` has published, minutes after core.\nexport const NOYDB_IN_VERSION = '0.10.0-pre.1'\n\n/** The package a wizard adapter choice names in generated prose (see `ADAPTER_PACKAGE`). */\nexport const ADAPTER_PACKAGES = {\n browser: '@noy-db/to-browser-idb',\n file: '@noy-db/to-file',\n memory: '@noy-db/hub',\n} as const\n\n/**\n * Walks `src` recursively and copies every file into `dest`, substituting\n * `{{TOKEN}}` placeholders along the way.\n *\n * Returns the relative paths of every file written, sorted alphabetically.\n * Tests use this to assert that the expected file set was produced without\n * having to walk `dest` themselves.\n *\n * The function is intentionally synchronous-feeling (uses `await` inside\n * a depth-first walk) so the order of writes is deterministic — easier\n * to reason about when something fails halfway through.\n */\nexport async function renderTemplate(\n src: string,\n dest: string,\n tokens: RenderTokens,\n): Promise<string[]> {\n const written: string[] = []\n await walk(src, dest, '', tokens, written)\n written.sort()\n return written\n}\n\nasync function walk(\n srcRoot: string,\n destRoot: string,\n rel: string,\n tokens: RenderTokens,\n written: string[],\n): Promise<void> {\n const srcDir = path.join(srcRoot, rel)\n const entries = await fs.readdir(srcDir, { withFileTypes: true })\n\n for (const entry of entries) {\n const srcEntry = path.join(srcDir, entry.name)\n // _gitignore → .gitignore, _npmrc → .npmrc, etc.\n // npm strips files starting with `.` from the published tarball, so\n // shipping them as `_<name>` is the standard workaround.\n const destName = entry.name.startsWith('_')\n ? `.${entry.name.slice(1)}`\n : entry.name\n const destRel = rel ? path.join(rel, destName) : destName\n const destEntry = path.join(destRoot, destRel)\n\n if (entry.isDirectory()) {\n await fs.mkdir(destEntry, { recursive: true })\n await walk(srcRoot, destRoot, path.join(rel, entry.name), tokens, written)\n continue\n }\n\n const raw = await fs.readFile(srcEntry, 'utf8')\n const rendered = applyTokens(raw, tokens)\n await fs.mkdir(path.dirname(destEntry), { recursive: true })\n await fs.writeFile(destEntry, rendered, 'utf8')\n written.push(destRel)\n }\n}\n\n/**\n * Substitutes every `{{KEY}}` in `input` with the corresponding value\n * from `tokens`. Unknown keys are left untouched (so the user can spot\n * them visually in the generated output if a template author makes a\n * typo). Empty values are allowed.\n */\nexport function applyTokens(input: string, tokens: RenderTokens): string {\n const bag = tokens as unknown as Record<string, string>\n return input.replace(/\\{\\{(\\w+)\\}\\}/g, (match, key: string) => {\n const value = bag[key]\n return value === undefined ? match : value\n })\n}\n\n/**\n * Resolves the absolute path of a template directory shipped inside the\n * package. The package layout is:\n *\n * packages/create-noy-db/\n * dist/\n * wizard/render.js ← we are here at runtime (after tsup build)\n * templates/\n * nuxt-default/ ← we want to point at this\n *\n * In the published tarball the same relative path holds: `dist/` and\n * `templates/` are siblings under the package root. Computing the path\n * relative to `import.meta.url` lets us avoid hardcoding any assumption\n * about whether we're running from source, from `dist/`, or from a\n * globally installed copy.\n */\nexport function templateDir(name: string): string {\n // From `dist/wizard/render.js` we go up two levels to the package root,\n // then into `templates/<name>`.\n const here = fileURLToPath(import.meta.url)\n const packageRoot = path.resolve(path.dirname(here), '..', '..')\n return path.join(packageRoot, 'templates', name)\n}\n\nlet cachedOwnVersion: string | undefined\n\n/**\n * This package's own version, read from its `package.json` (a sibling of\n * `dist/` and `templates/`, same layout reasoning as `templateDir`). The\n * wizard versions in lockstep with the `@noy-db/*` family, so this is the\n * value `{{NOYDB_VERSION}}` resolves to at scaffold time.\n */\nexport async function ownVersion(): Promise<string> {\n if (cachedOwnVersion === undefined) {\n const here = fileURLToPath(import.meta.url)\n const packageRoot = path.resolve(path.dirname(here), '..', '..')\n const raw = await fs.readFile(path.join(packageRoot, 'package.json'), 'utf8')\n cachedOwnVersion = (JSON.parse(raw) as { version: string }).version\n }\n return cachedOwnVersion\n}\n"],"mappings":"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","names":["fs","spawnSync","pc","createNoydb","createNoydb","withTeam","createNoydb","toFile","toFile","createNoydb","withTeam","fs","path","createNoydb","toFile","path","toFile","createNoydb","fs","readPodHeader","readFile","readFile","dirname","join","createNoydb","readPod","memoryStore","backup","path","existsSync","readFileSync","dirname","join","resolve","pathToFileURL","isRecord","join","existsSync","readFileSync","pathToFileURL","dirname","resolve","existsSync","readFileSync","join","fs","path","path","readFileSync","existsSync","join","augmentNuxtConfig","writeAugmentedConfig","pc","fail","spawnSync"]}