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.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/wizard/run.ts","../src/wizard/render.ts","../src/wizard/detect.ts","../src/wizard/augment.ts","../src/wizard/i18n/en.ts","../src/wizard/i18n/th.ts","../src/wizard/i18n/index.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":["/**\n * The wizard entry point — `runWizard()`.\n *\n * Two modes:\n *\n * 1. **Interactive (default).** Uses `@clack/prompts` to ask the user\n * for project name, adapter, and sample-data inclusion. Cancellation\n * at any prompt aborts cleanly with a non-zero exit code.\n *\n * 2. **Non-interactive (`yes: true`).** Skips every prompt and uses the\n * values supplied in `WizardOptions`. Missing values become defaults.\n * This is the path tests take — no terminal needed, fully scriptable.\n *\n * The function never spawns child processes (no `npm install` etc.). It\n * only writes files and returns. The shell wrapper around `npm create` is\n * responsible for installing — we keep this layer pure so it's trivially\n * testable and so adding a `--no-install` flag later is a no-op.\n */\n\nimport { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport * as p from '@clack/prompts'\nimport pc from 'picocolors'\nimport { NOYDB_IN_VERSION, ownVersion, renderTemplate, templateDir, type RenderTokens } from './render.js'\nimport type {\n WizardAdapter,\n WizardOptions,\n WizardResult,\n WizardFreshResult,\n WizardAugmentResult,\n WizardTemplate,\n} from './types.js'\nimport { detectNuxtProject } from './detect.js'\nimport { augmentNuxtConfig, writeAugmentedConfig } from './augment.js'\nimport { detectLocale, loadMessages, type WizardMessages } from './i18n/index.js'\n\n/**\n * Default invoice records the wizard injects when `sampleData: true`.\n * Kept here (not in the template) so the same records can be reused by\n * the `noy-db add` command for collections with `--seed`. Three records\n * is enough to demo a query that returns more than one row.\n */\nconst DEFAULT_SEED = [\n {\n id: 'inv-001',\n client: 'Acme Holdings',\n amount: 1500,\n status: 'open',\n dueDate: '2026-05-01',\n },\n {\n id: 'inv-002',\n client: 'Globex Inc',\n amount: 4200,\n status: 'paid',\n dueDate: '2026-04-15',\n },\n {\n id: 'inv-003',\n client: 'Initech LLC',\n amount: 850,\n status: 'overdue',\n dueDate: '2026-03-20',\n },\n]\n\nfunction adapterLabels(msg: WizardMessages): Record<WizardAdapter, string> {\n return {\n browser: msg.adapterBrowserLabel,\n file: msg.adapterFileLabel,\n memory: msg.adapterMemoryLabel,\n }\n}\n\n/**\n * Validates the project name. Rules are intentionally narrow:\n * - non-empty\n * - lowercase letters, digits, hyphens, dots, underscores only\n * - cannot start with a hyphen, dot, or underscore\n * - max 214 chars (npm package-name limit)\n *\n * The narrow rule set means whatever the user types is also a valid npm\n * package name, so the generated `package.json#name` field is always safe.\n */\nexport function validateProjectName(name: string): string | null {\n if (!name || name.trim() === '') return 'Project name cannot be empty'\n if (name.length > 214) return 'Project name must be 214 characters or fewer'\n if (!/^[a-z0-9][a-z0-9._-]*$/.test(name)) {\n return 'Project name must start with a lowercase letter or digit and contain only lowercase letters, digits, hyphens, dots, or underscores'\n }\n return null\n}\n\n/**\n * Main entry point. Detects whether `cwd` is an existing Nuxt 4\n * project and routes to one of two modes:\n *\n * - **Fresh mode** (the original behavior): prompts for\n * project name, creates a new directory, renders the Nuxt 4\n * starter template. Returns a `WizardFreshResult`.\n *\n * - **Augment mode** (new in, ): patches the existing\n * `nuxt.config.ts` via magicast to add `@noy-db/in-nuxt` to the\n * modules array and a `noydb:` config key. Shows a unified\n * diff and asks for confirmation before writing. Supports\n * `--dry-run`. Returns a `WizardAugmentResult`.\n *\n * The auto-detection rule: if cwd has both a `nuxt.config.ts`\n * (or `.js`/`.mjs`) AND a `package.json` that lists `nuxt` in any\n * dependency section, augment mode fires. Otherwise fresh mode.\n * Users can force fresh mode via `forceFresh: true` (CLI:\n * `--force-fresh`) when they want to create a sub-project inside\n * an existing Nuxt workspace.\n *\n * Both modes refuse to clobber existing work: fresh mode rejects\n * non-empty target dirs; augment mode rejects unsupported config\n * shapes (opaque exports, non-array modules, etc.).\n */\nexport async function runWizard(options: WizardOptions = {}): Promise<WizardResult> {\n const cwd = options.cwd ?? process.cwd()\n const yes = options.yes ?? false\n\n // Resolve the message bundle once at the top so every downstream\n // helper sees a consistent locale. Explicit `options.locale` wins;\n // otherwise we fall back to POSIX env-var detection (LC_ALL → LANG).\n // Tests pin the locale to keep snapshots deterministic.\n const msg = loadMessages(options.locale ?? detectLocale())\n\n // ── Detect existing Nuxt project ───────────────────────────────────\n // Runs BEFORE any prompts so the interactive flow branches cleanly\n // into fresh vs augment without asking the user questions that\n // don't apply to their mode. `forceFresh` short-circuits the\n // detection — CI tests use this to scaffold into a temp dir that\n // happens to sit under an existing Nuxt project.\n const detection = options.forceFresh\n ? null\n : await detectNuxtProject(cwd)\n\n if (detection?.existing && detection.configPath) {\n return runAugmentMode(options, cwd, detection.configPath, msg)\n }\n\n return runFreshMode(options, cwd, yes, msg)\n}\n\n/**\n * The original fresh-project path, factored out of the\n * main entry so the augment branch above can coexist. Behavior\n * is unchanged from except the return shape now includes\n * the `kind: 'fresh'` discriminator.\n */\nasync function runFreshMode(\n options: WizardOptions,\n cwd: string,\n yes: boolean,\n msg: WizardMessages,\n): Promise<WizardFreshResult> {\n // ── Resolve answers ────────────────────────────────────────────────\n // In non-interactive mode every prompt is short-circuited; in\n // interactive mode we only prompt for fields the caller didn't supply.\n const projectName = yes\n ? options.projectName ?? 'my-noy-db-app'\n : await promptProjectName(options.projectName, msg)\n\n const adapter: WizardAdapter = yes\n ? options.adapter ?? 'browser'\n : await promptAdapter(options.adapter, msg)\n\n const sampleData: boolean = yes\n ? options.sampleData ?? true\n : await promptSampleData(options.sampleData, msg)\n\n const template: WizardTemplate = yes\n ? options.template ?? 'nuxt-default'\n : await promptTemplate(options.template, msg)\n\n const syncAdapter: WizardAdapter | 'none' = yes\n ? options.syncAdapter ?? 'none'\n : await promptSyncAdapter(options.syncAdapter, adapter, msg)\n\n // ── Validate target directory ──────────────────────────────────────\n // We refuse to write into a non-empty directory. Empty + missing are\n // both fine — fs.mkdir { recursive: true } handles both. The `Cancelled`\n // exit is intentional: cleaner than throwing an Error from a wizard.\n const projectPath = path.resolve(cwd, projectName)\n await assertWritableTarget(projectPath)\n\n // ── Render the template ────────────────────────────────────────────\n const tokens: RenderTokens = {\n PROJECT_NAME: projectName,\n ADAPTER: adapter,\n DEVTOOLS: 'true',\n SEED_INVOICES: sampleData\n ? JSON.stringify(DEFAULT_SEED, null, 2).replace(/\\n/g, '\\n ')\n : '[]',\n NOYDB_VERSION: await ownVersion(),\n NOYDB_IN_VERSION,\n }\n\n await fs.mkdir(projectPath, { recursive: true })\n const files = await renderTemplate(\n templateDir(template),\n projectPath,\n tokens,\n )\n\n if (!yes) {\n // Print a friendly summary so the user knows what happened. We don't\n // run `npm install` ourselves — the user picks the package manager.\n p.note(\n [\n `${pc.bold('cd')} ${projectName}`,\n `${pc.bold('pnpm install')} ${pc.dim('(or npm/yarn/bun)')}`,\n `${pc.bold('pnpm dev')}`,\n ].join('\\n'),\n msg.freshNextStepsTitle,\n )\n p.outro(pc.green(msg.freshOutroDone))\n }\n\n return {\n kind: 'fresh',\n options: {\n projectName,\n adapter,\n sampleData,\n cwd,\n template,\n syncAdapter,\n },\n projectPath,\n files,\n }\n}\n\n/**\n * The new augment-existing-project path. Runs magicast\n * on the detected nuxt.config, shows a unified diff, asks for\n * confirmation, and writes. Supports `--dry-run` to see the diff\n * without touching disk.\n *\n * Three outcomes:\n * - **written**: the file was patched successfully\n * - **already-configured**: both target mutations are already\n * present (idempotent no-op)\n * - **cancelled**: user said no at the confirmation prompt\n * - **dry-run**: `options.dryRun` was set, we showed the diff\n * and returned without writing\n * - **unsupported-shape**: the config file uses a shape we can't\n * safely mutate (opaque export, non-array modules, etc.)\n */\nasync function runAugmentMode(\n options: WizardOptions,\n cwd: string,\n configPath: string,\n msg: WizardMessages,\n): Promise<WizardAugmentResult> {\n const yes = options.yes ?? false\n const dryRun = options.dryRun ?? false\n\n if (!yes) {\n p.note(\n [\n `${pc.dim(msg.augmentDetectedPrefix)}`,\n ` ${pc.cyan(configPath)}`,\n '',\n msg.augmentDescription,\n ].join('\\n'),\n msg.augmentModeTitle,\n )\n }\n\n // In augment mode we only need ONE prompt from the user: which\n // adapter to wire into the `noydb: { adapter }` key. Everything\n // else is decided by the config we're patching.\n const adapter: WizardAdapter = yes\n ? options.adapter ?? 'browser'\n : await promptAdapter(options.adapter, msg)\n\n const result = await augmentNuxtConfig({\n configPath,\n adapter,\n dryRun,\n })\n\n if (result.kind === 'already-configured') {\n if (!yes) {\n p.note(\n `${pc.yellow(msg.augmentNothingToDo)} ${result.reason}`,\n msg.augmentAlreadyConfiguredTitle,\n )\n p.outro(pc.green(msg.augmentAlreadyOutro))\n }\n return {\n kind: 'augment',\n configPath,\n adapter,\n changed: false,\n reason: 'already-configured',\n }\n }\n\n if (result.kind === 'unsupported-shape') {\n if (!yes) {\n p.cancel(`${pc.red(msg.augmentUnsupportedPrefix)} ${result.reason}`)\n }\n return {\n kind: 'augment',\n configPath,\n adapter,\n changed: false,\n reason: 'unsupported-shape',\n }\n }\n\n // result.kind === 'proposed-change' — print the diff and either\n // write (after confirmation) or bail in dry-run mode.\n if (!yes || dryRun) {\n p.note(renderDiff(result.diff), msg.augmentProposedChangesTitle)\n }\n\n if (dryRun) {\n if (!yes) p.outro(pc.green(msg.augmentDryRunOutro))\n return {\n kind: 'augment',\n configPath,\n adapter,\n changed: false,\n reason: 'dry-run',\n diff: result.diff,\n }\n }\n\n let shouldWrite = yes\n if (!yes) {\n const confirmed = await p.confirm({\n message: msg.augmentApplyConfirm,\n initialValue: true,\n })\n if (p.isCancel(confirmed) || confirmed !== true) {\n p.cancel(msg.augmentAborted)\n return {\n kind: 'augment',\n configPath,\n adapter,\n changed: false,\n reason: 'cancelled',\n diff: result.diff,\n }\n }\n shouldWrite = true\n }\n\n if (shouldWrite) {\n await writeAugmentedConfig(configPath, result.newCode)\n if (!yes) {\n p.note(\n [\n pc.dim(msg.augmentInstallIntro),\n '',\n `${pc.bold('pnpm add')} @noy-db/in-nuxt @noy-db/in-pinia @noy-db/hub @noy-db/to-browser-idb @pinia/nuxt pinia`,\n pc.dim(msg.augmentInstallPmHint),\n ].join('\\n'),\n msg.augmentNextStepTitle,\n )\n p.outro(pc.green(msg.augmentDoneOutro))\n }\n }\n\n return {\n kind: 'augment',\n configPath,\n adapter,\n changed: true,\n reason: 'written',\n diff: result.diff,\n }\n}\n\n/**\n * Clean up a unified diff for terminal display. Strips the `===`\n * separators the `diff` package emits, keeps a reasonable max\n * width, and drops the Index/------ preamble that's only useful\n * for `patch -p1`. The result is a short, colorized-ready string\n * that fits inside a clack `note()` block.\n */\nfunction renderDiff(diff: string): string {\n const lines = diff.split('\\n')\n const keep: string[] = []\n for (const line of lines) {\n if (line.startsWith('Index:')) continue\n if (line.startsWith('=')) continue\n if (line.startsWith('---') || line.startsWith('+++')) continue\n if (line.startsWith('+')) keep.push(pc.green(line))\n else if (line.startsWith('-')) keep.push(pc.red(line))\n else if (line.startsWith('@@')) keep.push(pc.dim(line))\n else keep.push(line)\n }\n return keep.join('\\n').trim()\n}\n\n// ─── Prompt helpers ──────────────────────────────────────────────────────\n\nasync function promptProjectName(initial: string | undefined, msg: WizardMessages): Promise<string> {\n if (initial) {\n const err = validateProjectName(initial)\n if (err) throw new Error(err)\n return initial\n }\n const result = await p.text({\n message: msg.promptProjectName,\n placeholder: msg.promptProjectNamePlaceholder,\n initialValue: 'my-noy-db-app',\n validate: (v) => validateProjectName(v ?? '') ?? undefined,\n })\n if (p.isCancel(result)) {\n p.cancel(msg.cancelled)\n process.exit(1)\n }\n return result\n}\n\nasync function promptAdapter(initial: WizardAdapter | undefined, msg: WizardMessages): Promise<WizardAdapter> {\n if (initial) return initial\n const labels = adapterLabels(msg)\n const result = await p.select<WizardAdapter>({\n message: msg.promptAdapter,\n options: (['browser', 'file', 'memory'] as const).map((value) => ({\n value,\n label: labels[value],\n })),\n initialValue: 'browser',\n })\n if (p.isCancel(result)) {\n p.cancel(msg.cancelled)\n process.exit(1)\n }\n return result\n}\n\nasync function promptSyncAdapter(\n initial: WizardAdapter | 'none' | undefined,\n primary: WizardAdapter,\n _msg: WizardMessages,\n): Promise<WizardAdapter | 'none'> {\n if (initial !== undefined) return initial\n const choices = (['none', 'memory', 'file', 'browser'] as const).filter(\n (x) => x !== primary,\n )\n const labels: Record<WizardAdapter | 'none', string> = {\n none: 'No sync — local only',\n memory: 'In-memory (testing / CI)',\n file: 'JSON files (USB / shared drive)',\n browser: 'IndexedDB (other tabs / PWAs)',\n }\n const result = await p.select<WizardAdapter | 'none'>({\n message: 'Add a sync target?',\n options: choices.map((value) => ({ value, label: labels[value] })),\n initialValue: 'none',\n })\n if (p.isCancel(result)) {\n process.exit(1)\n }\n return result\n}\n\nasync function promptTemplate(\n initial: WizardTemplate | undefined,\n _msg: WizardMessages,\n): Promise<WizardTemplate> {\n if (initial) return initial\n const result = await p.select<WizardTemplate>({\n message: 'Pick a starter template',\n options: [\n {\n value: 'nuxt-default',\n label: 'Nuxt 4 + Pinia',\n hint: 'SSR + file-based routing (recommended default)',\n },\n {\n value: 'vite-vue',\n label: 'Vite + Vue 3 + Pinia',\n hint: 'Client-side SPA, shorter install graph',\n },\n {\n value: 'vanilla',\n label: 'Vite + TypeScript',\n hint: 'No framework, smallest footprint',\n },\n {\n value: 'electron',\n label: 'Electron + Vue 3',\n hint: 'Desktop app with to-file adapter — USB workflow',\n },\n ],\n initialValue: 'nuxt-default',\n })\n if (p.isCancel(result)) {\n process.exit(1)\n }\n return result\n}\n\nasync function promptSampleData(initial: boolean | undefined, msg: WizardMessages): Promise<boolean> {\n if (typeof initial === 'boolean') return initial\n const result = await p.confirm({\n message: msg.promptSampleData,\n initialValue: true,\n })\n if (p.isCancel(result)) {\n p.cancel(msg.cancelled)\n process.exit(1)\n }\n return result\n}\n\nasync function assertWritableTarget(target: string): Promise<void> {\n try {\n const entries = await fs.readdir(target)\n if (entries.length > 0) {\n throw new Error(\n `Target directory '${target}' already exists and is not empty. Refusing to overwrite — pick a different project name or remove the directory first.`,\n )\n }\n } catch (err: unknown) {\n // ENOENT is the happy path — the directory doesn't exist yet.\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return\n throw err\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 /** \"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 */\nexport const NOYDB_IN_VERSION = '0.8.0'\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","/**\n * Detect whether the wizard is being invoked inside an existing\n * Nuxt 4 project that should be augmented in place, vs a blank\n * directory where the wizard should create a fresh starter.\n *\n * The detection rule is intentionally narrow so we don't trip on\n * random `package.json` files sitting in the parent tree: a project\n * counts as \"existing Nuxt 4\" only when the SAME directory has\n * BOTH `nuxt.config.ts` (or `.js`) AND a `package.json` that lists\n * `nuxt` in any of the dependency sections. Either alone is\n * ambiguous:\n *\n * - `nuxt.config.ts` without a package.json could be a stray\n * template scratch file the user moved here.\n * - `package.json` with `nuxt` but no config file could be a\n * half-deleted project or a pnpm workspace root that pulls\n * nuxt in transitively for docs.\n *\n * Requiring both avoids both false positives.\n *\n * We deliberately do NOT walk upward to find a parent Nuxt project.\n * The user's cwd is load-bearing — if they wanted to augment a\n * parent dir, they should cd there first. Walking up would make\n * \"run the wizard to create a fresh app inside my existing\n * monorepo\" silently augment the monorepo root instead.\n */\n\nimport { promises as fs } from 'node:fs'\nimport path from 'node:path'\n\nexport interface NuxtDetection {\n /** Whether cwd contains an existing Nuxt 4 project. */\n readonly existing: boolean\n /** Absolute path of the detected config file, if any. */\n readonly configPath: string | null\n /** Absolute path of the detected package.json, if any. */\n readonly packageJsonPath: string | null\n /** Reason strings explaining the detection outcome — useful for diagnostic messages. */\n readonly reasons: readonly string[]\n}\n\n/**\n * Inspect `cwd` for an existing Nuxt 4 project. Returns a detailed\n * result object so the caller can both branch on `existing` and\n * surface the specific reasons to the user.\n *\n * Pure in terms of filesystem reads — no writes, no network, no\n * caching. Callers who need cached detection should memoize on\n * their side.\n */\nexport async function detectNuxtProject(cwd: string): Promise<NuxtDetection> {\n const reasons: string[] = []\n\n // Step 1: look for a Nuxt config file. Both extensions are\n // valid; Nuxt itself accepts either.\n const configCandidates = ['nuxt.config.ts', 'nuxt.config.js', 'nuxt.config.mjs']\n let configPath: string | null = null\n for (const name of configCandidates) {\n const candidate = path.join(cwd, name)\n if (await pathExists(candidate)) {\n configPath = candidate\n reasons.push(`Found ${name}`)\n break\n }\n }\n if (!configPath) {\n reasons.push('No nuxt.config.{ts,js,mjs} in cwd')\n return {\n existing: false,\n configPath: null,\n packageJsonPath: null,\n reasons,\n }\n }\n\n // Step 2: look for a package.json in the same directory.\n const pkgPath = path.join(cwd, 'package.json')\n if (!(await pathExists(pkgPath))) {\n reasons.push('Config file present but no package.json — ambiguous, skipping')\n return {\n existing: false,\n configPath,\n packageJsonPath: null,\n reasons,\n }\n }\n\n // Step 3: verify `nuxt` is in one of the dependency sections.\n // We're lenient about WHICH section (dependencies, devDependencies,\n // peerDependencies) because real-world projects put it in all of\n // them depending on the tooling.\n let pkg: Record<string, unknown>\n try {\n pkg = JSON.parse(await fs.readFile(pkgPath, 'utf8')) as Record<string, unknown>\n } catch (err) {\n reasons.push(`package.json is not valid JSON: ${(err as Error).message}`)\n return {\n existing: false,\n configPath,\n packageJsonPath: pkgPath,\n reasons,\n }\n }\n\n const depSections = ['dependencies', 'devDependencies', 'peerDependencies'] as const\n let nuxtVersion: string | undefined\n for (const section of depSections) {\n const deps = pkg[section]\n if (deps && typeof deps === 'object' && 'nuxt' in deps) {\n nuxtVersion = (deps as Record<string, string>)['nuxt']\n reasons.push(`Found nuxt@${nuxtVersion} in ${section}`)\n break\n }\n }\n if (!nuxtVersion) {\n reasons.push('Config file present, but package.json does not list `nuxt` as a dependency')\n return {\n existing: false,\n configPath,\n packageJsonPath: pkgPath,\n reasons,\n }\n }\n\n return {\n existing: true,\n configPath,\n packageJsonPath: pkgPath,\n reasons,\n }\n}\n\n/** Cheap fs.access wrapper that returns a boolean instead of throwing. */\nasync function pathExists(target: string): Promise<boolean> {\n try {\n await fs.access(target)\n return true\n } catch {\n return false\n }\n}\n","/**\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","/**\n * English (`en`) wizard messages — the default locale.\n *\n * Editing note: if you change a key's value here, update the\n * matching key in every other locale bundle (`th.ts`, future\n * additions). The key-parity test in `__tests__/i18n.test.ts`\n * is the safety net: it fails if any locale is missing a key\n * or has an extra one, so a forgotten update surfaces in CI\n * rather than in a user report.\n */\n\nimport type { WizardMessages } from './types.js'\n\nexport const en: WizardMessages = {\n wizardIntro:\n 'A wizard for noy-db — None Of Your DataBase.\\n' +\n 'Generates a fresh Nuxt 4 + Pinia + encrypted-store starter.',\n\n promptProjectName: 'Project name',\n promptProjectNamePlaceholder: 'my-noy-db-app',\n promptAdapter: 'Storage adapter',\n adapterBrowserLabel:\n 'browser — localStorage / IndexedDB (recommended for web apps)',\n adapterFileLabel:\n 'file — JSON files on disk (Electron / Tauri / USB workflows)',\n adapterMemoryLabel:\n 'memory — no persistence (ideal for tests and demos)',\n promptSampleData: 'Include sample invoice records?',\n\n freshNextStepsTitle: 'Next steps',\n freshOutroDone: '✔ Done — happy encrypting!',\n\n augmentModeTitle: 'Augment mode',\n augmentDetectedPrefix: 'Detected existing Nuxt 4 project:',\n augmentDescription:\n 'The wizard will add @noy-db/in-nuxt to your modules array\\n' +\n 'and a noydb: config key. You can review the diff before\\n' +\n 'anything is written to disk.',\n\n augmentProposedChangesTitle: 'Proposed changes',\n augmentApplyConfirm: 'Apply these changes?',\n\n augmentAlreadyConfiguredTitle: 'Already configured',\n augmentNothingToDo: 'Nothing to do:',\n augmentAlreadyOutro: '✔ Your Nuxt config is already wired up.',\n augmentAborted: 'Aborted — your config is unchanged.',\n augmentDryRunOutro: '✔ Dry run — no files were modified.',\n augmentNextStepTitle: 'Next step',\n augmentInstallIntro:\n 'Install the @noy-db packages your config now depends on:',\n augmentInstallPmHint: '(or use npm/yarn/bun as appropriate)',\n augmentDoneOutro: '✔ Config updated — happy encrypting!',\n augmentUnsupportedPrefix: 'Cannot safely patch this config:',\n\n cancelled: 'Cancelled.',\n}\n","/**\n * Thai (`th`) wizard messages.\n *\n * Editing note: this bundle MUST stay in key-parity with `en.ts`.\n * The key-parity test in `__tests__/i18n.test.ts` fails if any key\n * is missing or extra here, so a forgotten translation surfaces in\n * CI rather than as a runtime crash for a Thai-speaking user.\n *\n * Translation conventions:\n * - Technical identifiers (`modules`, `noydb:`, `nuxt.config.ts`,\n * `Nuxt 4`, `Pinia`, adapter names like `browser`/`file`/`memory`)\n * stay in English. This matches how Thai developers actually\n * read and write code — translating them would just add a\n * mental round-trip.\n * - Product name \"noy-db\" and the \"None Of Your DataBase\" tagline\n * also stay in English; they're brand strings, not prose.\n * - Validation/error strings are NOT in this file — they stay in\n * English so bug reports look the same in any locale (see the\n * docstring on `WizardMessages` in `types.ts` for the rationale).\n */\n\nimport type { WizardMessages } from './types.js'\n\nexport const th: WizardMessages = {\n wizardIntro:\n 'ตัวช่วยสร้างสำหรับ noy-db — None Of Your DataBase\\n' +\n 'สร้างโปรเจกต์เริ่มต้น Nuxt 4 + Pinia พร้อมที่เก็บข้อมูลแบบเข้ารหัส',\n\n promptProjectName: 'ชื่อโปรเจกต์',\n promptProjectNamePlaceholder: 'my-noy-db-app',\n promptAdapter: 'อะแดปเตอร์จัดเก็บข้อมูล',\n adapterBrowserLabel:\n 'browser — localStorage / IndexedDB (แนะนำสำหรับเว็บแอป)',\n adapterFileLabel:\n 'file — ไฟล์ JSON บนดิสก์ (Electron / Tauri / USB)',\n adapterMemoryLabel:\n 'memory — ไม่บันทึกข้อมูล (เหมาะสำหรับการทดสอบและตัวอย่าง)',\n promptSampleData: 'เพิ่มข้อมูลตัวอย่างใบแจ้งหนี้หรือไม่?',\n\n freshNextStepsTitle: 'ขั้นตอนถัดไป',\n freshOutroDone: '✔ เสร็จเรียบร้อย — ขอให้สนุกกับการเข้ารหัส!',\n\n augmentModeTitle: 'โหมดเสริมโปรเจกต์เดิม',\n augmentDetectedPrefix: 'พบโปรเจกต์ Nuxt 4 ที่มีอยู่แล้ว:',\n augmentDescription:\n 'ตัวช่วยจะเพิ่ม @noy-db/in-nuxt เข้าใน modules\\n' +\n 'และเพิ่มคีย์ noydb: ในไฟล์ config คุณสามารถดู diff\\n' +\n 'ก่อนที่จะเขียนไฟล์ลงดิสก์ได้',\n\n augmentProposedChangesTitle: 'รายการเปลี่ยนแปลงที่จะทำ',\n augmentApplyConfirm: 'ยืนยันการเปลี่ยนแปลงเหล่านี้?',\n\n augmentAlreadyConfiguredTitle: 'ตั้งค่าไว้แล้ว',\n augmentNothingToDo: 'ไม่มีอะไรต้องทำ:',\n augmentAlreadyOutro: '✔ ไฟล์ Nuxt config ของคุณตั้งค่าครบแล้ว',\n augmentAborted: 'ยกเลิก — ไฟล์ config ของคุณไม่ถูกแก้ไข',\n augmentDryRunOutro: '✔ Dry run — ไม่มีไฟล์ใดถูกแก้ไข',\n augmentNextStepTitle: 'ขั้นตอนถัดไป',\n augmentInstallIntro:\n 'ติดตั้งแพ็กเกจ @noy-db ที่ config ของคุณต้องใช้:',\n augmentInstallPmHint: '(หรือใช้ npm/yarn/bun ตามความเหมาะสม)',\n augmentDoneOutro: '✔ อัปเดต config เรียบร้อย — ขอให้สนุกกับการเข้ารหัส!',\n augmentUnsupportedPrefix: 'ไม่สามารถแก้ไข config นี้ได้อย่างปลอดภัย:',\n\n cancelled: 'ยกเลิกแล้ว',\n}\n","/**\n * i18n entrypoint for the `create-noy-db` wizard.\n *\n * Three responsibilities:\n *\n * 1. Re-export `Locale` and `WizardMessages` so callers don't\n * need to know about the bundle layout.\n * 2. `detectLocale(env)` — pure function that maps Unix-style\n * `LC_ALL` / `LANG` / `LANGUAGE` env vars to a supported\n * `Locale`. Returns `'en'` for anything we don't recognise.\n * 3. `loadMessages(locale)` — synchronous lookup that returns\n * the message bundle for a locale. Synchronous (not dynamic\n * `import()`) on purpose: bundles are tiny (< 2 KB each), the\n * wizard reads them on every prompt, and async would force\n * every caller to be async. tsup tree-shakes unused locales\n * out of the bin only if we use top-level `import`s.\n *\n * ## Why env-var detection instead of `Intl.DateTimeFormat().resolvedOptions().locale`\n *\n * The Intl approach reads the JS engine's *display* locale, which\n * on most CI runners and Docker images is `en-US` regardless of\n * the user's actual setup. The Unix env vars (`LC_ALL`, `LANG`)\n * are how shells, terminals, and CLI tools have negotiated locale\n * for 30+ years — that's what a Thai-speaking dev's terminal will\n * actually have set. Following that convention also means power\n * users can override per-invocation with `LANG=th_TH.UTF-8 npm\n * create noy-db`, no flag required.\n */\n\nimport { en } from './en.js'\nimport { th } from './th.js'\nimport type { Locale, WizardMessages } from './types.js'\n\nexport type { Locale, WizardMessages } from './types.js'\n\nconst BUNDLES: Record<Locale, WizardMessages> = { en, th }\n\n/** Every locale we ship a bundle for. Used by tests and `--lang` validation. */\nexport const SUPPORTED_LOCALES: readonly Locale[] = ['en', 'th'] as const\n\n/**\n * Resolve a locale code to its message bundle. Falls back to `en`\n * if the requested locale isn't shipped — defensive, since\n * `Locale` is a union type and TS already prevents this at compile\n * time, but `--lang` parsing comes from user input at runtime.\n */\nexport function loadMessages(locale: Locale): WizardMessages {\n return BUNDLES[locale] ?? BUNDLES.en\n}\n\n/**\n * Auto-detect a locale from POSIX env vars. Returns `'en'` when\n * nothing is set or when the value doesn't match a supported\n * locale — never throws.\n *\n * Inspection order matches the POSIX spec:\n * 1. `LC_ALL` (overrides everything)\n * 2. `LC_MESSAGES` (the category we actually care about)\n * 3. `LANG` (system default)\n * 4. `LANGUAGE` (GNU extension, comma-separated preference list)\n *\n * The first non-empty value wins. We then strip the encoding\n * suffix (`th_TH.UTF-8` → `th_TH`) and the region (`th_TH` → `th`)\n * before matching against `SUPPORTED_LOCALES`.\n */\nexport function detectLocale(env: NodeJS.ProcessEnv = process.env): Locale {\n const candidates = [\n env.LC_ALL,\n env.LC_MESSAGES,\n env.LANG,\n // LANGUAGE is a comma-separated preference list — take the first\n // entry. We deliberately do NOT walk the whole list; the wizard\n // ships exactly two locales, so a \"best fit\" walk would be\n // overkill and would obscure unexpected behaviour.\n env.LANGUAGE?.split(':')[0]?.split(',')[0],\n ]\n\n for (const raw of candidates) {\n if (!raw) continue\n const normalised = raw\n .split('.')[0]! // strip encoding: th_TH.UTF-8 → th_TH\n .split('@')[0]! // strip modifier: en_US@euro → en_US\n .toLowerCase()\n .split('_')[0]! // strip region: th_th → th\n\n if ((SUPPORTED_LOCALES as readonly string[]).includes(normalised)) {\n return normalised as Locale\n }\n }\n\n return 'en'\n}\n\n/**\n * Parse a `--lang` CLI argument into a `Locale`. Throws a clear\n * error for unsupported values — the caller (parse-args) catches\n * and reformats into a usage message.\n */\nexport function parseLocaleFlag(value: string): Locale {\n const normalised = value.toLowerCase().trim()\n if ((SUPPORTED_LOCALES as readonly string[]).includes(normalised)) {\n return normalised as Locale\n }\n throw new Error(\n `Unsupported --lang value: \"${value}\". Supported: ${SUPPORTED_LOCALES.join(', ')}`,\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"],"mappings":"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","names":["fs","path","fs","path","fs","path","fs","fs","path","pathExists","p","createNoydb","isCancel","cancel","createNoydb","withTeam","createNoydb","toFile","toFile","createNoydb","withTeam","fs","path","createNoydb","toFile","path","toFile","createNoydb","fs"]}
1
+ {"version":3,"sources":["../src/wizard/augment.ts","../src/wizard/run.ts","../src/wizard/render.ts","../src/wizard/detect.ts","../src/wizard/i18n/en.ts","../src/wizard/i18n/th.ts","../src/wizard/i18n/index.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/config.ts","../src/commands/pod/monitor.ts","../src/commands/pod/describe.ts","../src/commands/doctor.ts","../src/commands/add-package.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","/**\n * The wizard entry point — `runWizard()`.\n *\n * Two modes:\n *\n * 1. **Interactive (default).** Uses `@clack/prompts` to ask the user\n * for project name, adapter, and sample-data inclusion. Cancellation\n * at any prompt aborts cleanly with a non-zero exit code.\n *\n * 2. **Non-interactive (`yes: true`).** Skips every prompt and uses the\n * values supplied in `WizardOptions`. Missing values become defaults.\n * This is the path tests take — no terminal needed, fully scriptable.\n *\n * The function never spawns child processes (no `npm install` etc.). It\n * only writes files and returns. The shell wrapper around `npm create` is\n * responsible for installing — we keep this layer pure so it's trivially\n * testable and so adding a `--no-install` flag later is a no-op.\n */\n\nimport { promises as fs } from 'node:fs'\nimport path from 'node:path'\nimport * as p from '@clack/prompts'\nimport pc from 'picocolors'\nimport { ADAPTER_PACKAGES, NOYDB_IN_VERSION, ownVersion, renderTemplate, templateDir, type RenderTokens } from './render.js'\nimport type {\n WizardAdapter,\n WizardOptions,\n WizardResult,\n WizardFreshResult,\n WizardAugmentResult,\n WizardTemplate,\n} from './types.js'\nimport { detectNuxtProject } from './detect.js'\nimport { augmentNuxtConfig, writeAugmentedConfig } from './augment.js'\nimport { detectLocale, loadMessages, type WizardMessages } from './i18n/index.js'\n\n/**\n * Default invoice records the wizard injects when `sampleData: true`.\n * Kept here (not in the template) so the same records can be reused by\n * the `noy-db add` command for collections with `--seed`. Three records\n * is enough to demo a query that returns more than one row.\n */\nconst DEFAULT_SEED = [\n {\n id: 'inv-001',\n client: 'Acme Holdings',\n amount: 1500,\n status: 'open',\n dueDate: '2026-05-01',\n },\n {\n id: 'inv-002',\n client: 'Globex Inc',\n amount: 4200,\n status: 'paid',\n dueDate: '2026-04-15',\n },\n {\n id: 'inv-003',\n client: 'Initech LLC',\n amount: 850,\n status: 'overdue',\n dueDate: '2026-03-20',\n },\n]\n\nfunction adapterLabels(msg: WizardMessages): Record<WizardAdapter, string> {\n return {\n browser: msg.adapterBrowserLabel,\n file: msg.adapterFileLabel,\n memory: msg.adapterMemoryLabel,\n }\n}\n\n/**\n * Validates the project name. Rules are intentionally narrow:\n * - non-empty\n * - lowercase letters, digits, hyphens, dots, underscores only\n * - cannot start with a hyphen, dot, or underscore\n * - max 214 chars (npm package-name limit)\n *\n * The narrow rule set means whatever the user types is also a valid npm\n * package name, so the generated `package.json#name` field is always safe.\n */\nexport function validateProjectName(name: string): string | null {\n if (!name || name.trim() === '') return 'Project name cannot be empty'\n if (name.length > 214) return 'Project name must be 214 characters or fewer'\n if (!/^[a-z0-9][a-z0-9._-]*$/.test(name)) {\n return 'Project name must start with a lowercase letter or digit and contain only lowercase letters, digits, hyphens, dots, or underscores'\n }\n return null\n}\n\n/**\n * Main entry point. Detects whether `cwd` is an existing Nuxt 4\n * project and routes to one of two modes:\n *\n * - **Fresh mode** (the original behavior): prompts for\n * project name, creates a new directory, renders the Nuxt 4\n * starter template. Returns a `WizardFreshResult`.\n *\n * - **Augment mode** (new in, ): patches the existing\n * `nuxt.config.ts` via magicast to add `@noy-db/in-nuxt` to the\n * modules array and a `noydb:` config key. Shows a unified\n * diff and asks for confirmation before writing. Supports\n * `--dry-run`. Returns a `WizardAugmentResult`.\n *\n * The auto-detection rule: if cwd has both a `nuxt.config.ts`\n * (or `.js`/`.mjs`) AND a `package.json` that lists `nuxt` in any\n * dependency section, augment mode fires. Otherwise fresh mode.\n * Users can force fresh mode via `forceFresh: true` (CLI:\n * `--force-fresh`) when they want to create a sub-project inside\n * an existing Nuxt workspace.\n *\n * Both modes refuse to clobber existing work: fresh mode rejects\n * non-empty target dirs; augment mode rejects unsupported config\n * shapes (opaque exports, non-array modules, etc.).\n */\nexport async function runWizard(options: WizardOptions = {}): Promise<WizardResult> {\n const cwd = options.cwd ?? process.cwd()\n const yes = options.yes ?? false\n\n // Resolve the message bundle once at the top so every downstream\n // helper sees a consistent locale. Explicit `options.locale` wins;\n // otherwise we fall back to POSIX env-var detection (LC_ALL → LANG).\n // Tests pin the locale to keep snapshots deterministic.\n const msg = loadMessages(options.locale ?? detectLocale())\n\n // ── Detect existing Nuxt project ───────────────────────────────────\n // Runs BEFORE any prompts so the interactive flow branches cleanly\n // into fresh vs augment without asking the user questions that\n // don't apply to their mode. `forceFresh` short-circuits the\n // detection — CI tests use this to scaffold into a temp dir that\n // happens to sit under an existing Nuxt project.\n const detection = options.forceFresh\n ? null\n : await detectNuxtProject(cwd)\n\n if (detection?.existing && detection.configPath) {\n return runAugmentMode(options, cwd, detection.configPath, msg)\n }\n\n return runFreshMode(options, cwd, yes, msg)\n}\n\n/**\n * The original fresh-project path, factored out of the\n * main entry so the augment branch above can coexist. Behavior\n * is unchanged from except the return shape now includes\n * the `kind: 'fresh'` discriminator.\n */\nasync function runFreshMode(\n options: WizardOptions,\n cwd: string,\n yes: boolean,\n msg: WizardMessages,\n): Promise<WizardFreshResult> {\n // ── Resolve answers ────────────────────────────────────────────────\n // In non-interactive mode every prompt is short-circuited; in\n // interactive mode we only prompt for fields the caller didn't supply.\n const projectName = yes\n ? options.projectName ?? 'my-noy-db-app'\n : await promptProjectName(options.projectName, msg)\n\n // ── The gate (family#115) ──────────────────────────────────────────\n // One question hides the rest: \"use the recommended setup?\" Yes means the\n // `minimal` template with browser storage — hub + one store, one file.\n // Only a \"no\" opens template / adapter / sample-data. Any explicit flag\n // (--template, --adapter, --no-sample-data) skips the gate, as does --yes.\n const explicit = options.template !== undefined || options.adapter !== undefined || options.sampleData !== undefined\n const recommended = yes || explicit ? false : await promptRecommended(msg)\n\n const template: WizardTemplate = recommended\n ? 'minimal'\n : yes\n ? options.template ?? 'minimal'\n : await promptTemplate(options.template, msg)\n\n const adapter: WizardAdapter = recommended\n ? 'browser'\n : yes\n ? options.adapter ?? 'browser'\n : await promptAdapter(options.adapter, msg)\n\n // `minimal` has no sample data to seed; don't ask.\n const sampleData: boolean = recommended || template === 'minimal'\n ? options.sampleData ?? false\n : yes\n ? options.sampleData ?? true\n : await promptSampleData(options.sampleData, msg)\n\n // No sync prompt: until 2026-10-11 the wizard asked for a sync target and no\n // template consumed the answer (family#108). The `--sync` flag is still\n // parsed and carried in the result so a template CAN consume it; nothing asks.\n const syncAdapter: WizardAdapter | 'none' = options.syncAdapter ?? 'none'\n\n // ── Validate target directory ──────────────────────────────────────\n // We refuse to write into a non-empty directory. Empty + missing are\n // both fine — fs.mkdir { recursive: true } handles both. The `Cancelled`\n // exit is intentional: cleaner than throwing an Error from a wizard.\n const projectPath = path.resolve(cwd, projectName)\n await assertWritableTarget(projectPath)\n\n // ── Render the template ────────────────────────────────────────────\n const tokens: RenderTokens = {\n PROJECT_NAME: projectName,\n ADAPTER: adapter,\n ADAPTER_PACKAGE: ADAPTER_PACKAGES[adapter],\n DEVTOOLS: 'true',\n SEED_INVOICES: sampleData\n ? JSON.stringify(DEFAULT_SEED, null, 2).replace(/\\n/g, '\\n ')\n : '[]',\n NOYDB_VERSION: await ownVersion(),\n NOYDB_IN_VERSION,\n }\n\n await fs.mkdir(projectPath, { recursive: true })\n const files = await renderTemplate(\n templateDir(template),\n projectPath,\n tokens,\n )\n\n // The next steps print in BOTH modes. Under --yes they were gated with the\n // decorations, so `npm create noy-db --yes` wrote a project and said nothing\n // — measured at 38 bytes, a spinner (family#108). We don't run `npm install`\n // ourselves — the user picks the package manager.\n const nextSteps = [\n `${pc.bold('cd')} ${projectName}`,\n `${pc.bold('pnpm install')} ${pc.dim('(or npm/yarn/bun)')}`,\n `${pc.bold('pnpm dev')}`,\n ].join('\\n')\n if (!yes) {\n p.note(nextSteps, msg.freshNextStepsTitle)\n p.outro(pc.green(msg.freshOutroDone))\n } else {\n process.stdout.write(`${msg.freshOutroDone}\\n\\n${msg.freshNextStepsTitle}\\n${nextSteps}\\n`)\n }\n\n return {\n kind: 'fresh',\n options: {\n projectName,\n adapter,\n sampleData,\n cwd,\n template,\n syncAdapter,\n },\n projectPath,\n files,\n }\n}\n\n/**\n * The new augment-existing-project path. Runs magicast\n * on the detected nuxt.config, shows a unified diff, asks for\n * confirmation, and writes. Supports `--dry-run` to see the diff\n * without touching disk.\n *\n * Three outcomes:\n * - **written**: the file was patched successfully\n * - **already-configured**: both target mutations are already\n * present (idempotent no-op)\n * - **cancelled**: user said no at the confirmation prompt\n * - **dry-run**: `options.dryRun` was set, we showed the diff\n * and returned without writing\n * - **unsupported-shape**: the config file uses a shape we can't\n * safely mutate (opaque export, non-array modules, etc.)\n */\nasync function runAugmentMode(\n options: WizardOptions,\n cwd: string,\n configPath: string,\n msg: WizardMessages,\n): Promise<WizardAugmentResult> {\n const yes = options.yes ?? false\n const dryRun = options.dryRun ?? false\n\n if (!yes) {\n p.note(\n [\n `${pc.dim(msg.augmentDetectedPrefix)}`,\n ` ${pc.cyan(configPath)}`,\n '',\n msg.augmentDescription,\n ].join('\\n'),\n msg.augmentModeTitle,\n )\n }\n\n // In augment mode we only need ONE prompt from the user: which\n // adapter to wire into the `noydb: { adapter }` key. Everything\n // else is decided by the config we're patching.\n const adapter: WizardAdapter = yes\n ? options.adapter ?? 'browser'\n : await promptAdapter(options.adapter, msg)\n\n const result = await augmentNuxtConfig({\n configPath,\n adapter,\n dryRun,\n })\n\n if (result.kind === 'already-configured') {\n if (!yes) {\n p.note(\n `${pc.yellow(msg.augmentNothingToDo)} ${result.reason}`,\n msg.augmentAlreadyConfiguredTitle,\n )\n p.outro(pc.green(msg.augmentAlreadyOutro))\n }\n return {\n kind: 'augment',\n configPath,\n adapter,\n changed: false,\n reason: 'already-configured',\n }\n }\n\n if (result.kind === 'unsupported-shape') {\n if (!yes) {\n p.cancel(`${pc.red(msg.augmentUnsupportedPrefix)} ${result.reason}`)\n }\n return {\n kind: 'augment',\n configPath,\n adapter,\n changed: false,\n reason: 'unsupported-shape',\n }\n }\n\n // result.kind === 'proposed-change' — print the diff and either\n // write (after confirmation) or bail in dry-run mode.\n if (!yes || dryRun) {\n p.note(renderDiff(result.diff), msg.augmentProposedChangesTitle)\n }\n\n if (dryRun) {\n if (!yes) p.outro(pc.green(msg.augmentDryRunOutro))\n return {\n kind: 'augment',\n configPath,\n adapter,\n changed: false,\n reason: 'dry-run',\n diff: result.diff,\n }\n }\n\n let shouldWrite = yes\n if (!yes) {\n const confirmed = await p.confirm({\n message: msg.augmentApplyConfirm,\n initialValue: true,\n })\n if (p.isCancel(confirmed) || confirmed !== true) {\n p.cancel(msg.augmentAborted)\n return {\n kind: 'augment',\n configPath,\n adapter,\n changed: false,\n reason: 'cancelled',\n diff: result.diff,\n }\n }\n shouldWrite = true\n }\n\n if (shouldWrite) {\n await writeAugmentedConfig(configPath, result.newCode)\n if (!yes) {\n p.note(\n [\n pc.dim(msg.augmentInstallIntro),\n '',\n `${pc.bold('pnpm add')} @noy-db/in-nuxt @noy-db/in-pinia @noy-db/hub @noy-db/to-browser-idb @pinia/nuxt pinia`,\n pc.dim(msg.augmentInstallPmHint),\n ].join('\\n'),\n msg.augmentNextStepTitle,\n )\n p.outro(pc.green(msg.augmentDoneOutro))\n }\n }\n\n return {\n kind: 'augment',\n configPath,\n adapter,\n changed: true,\n reason: 'written',\n diff: result.diff,\n }\n}\n\n/**\n * Clean up a unified diff for terminal display. Strips the `===`\n * separators the `diff` package emits, keeps a reasonable max\n * width, and drops the Index/------ preamble that's only useful\n * for `patch -p1`. The result is a short, colorized-ready string\n * that fits inside a clack `note()` block.\n */\nfunction renderDiff(diff: string): string {\n const lines = diff.split('\\n')\n const keep: string[] = []\n for (const line of lines) {\n if (line.startsWith('Index:')) continue\n if (line.startsWith('=')) continue\n if (line.startsWith('---') || line.startsWith('+++')) continue\n if (line.startsWith('+')) keep.push(pc.green(line))\n else if (line.startsWith('-')) keep.push(pc.red(line))\n else if (line.startsWith('@@')) keep.push(pc.dim(line))\n else keep.push(line)\n }\n return keep.join('\\n').trim()\n}\n\n// ─── Prompt helpers ──────────────────────────────────────────────────────\n\nasync function promptProjectName(initial: string | undefined, msg: WizardMessages): Promise<string> {\n if (initial) {\n const err = validateProjectName(initial)\n if (err) throw new Error(err)\n return initial\n }\n const result = await p.text({\n message: msg.promptProjectName,\n placeholder: msg.promptProjectNamePlaceholder,\n initialValue: 'my-noy-db-app',\n validate: (v) => validateProjectName(v ?? '') ?? undefined,\n })\n if (p.isCancel(result)) {\n p.cancel(msg.cancelled)\n process.exit(1)\n }\n return result\n}\n\nasync function promptAdapter(initial: WizardAdapter | undefined, msg: WizardMessages): Promise<WizardAdapter> {\n if (initial) return initial\n const labels = adapterLabels(msg)\n const result = await p.select<WizardAdapter>({\n message: msg.promptAdapter,\n options: (['browser', 'file', 'memory'] as const).map((value) => ({\n value,\n label: labels[value],\n })),\n initialValue: 'browser',\n })\n if (p.isCancel(result)) {\n p.cancel(msg.cancelled)\n process.exit(1)\n }\n return result\n}\n\nasync function promptRecommended(msg: WizardMessages): Promise<boolean> {\n const result = await p.confirm({ message: msg.promptRecommended, initialValue: true })\n if (p.isCancel(result)) {\n process.exit(1)\n }\n return result\n}\n\nasync function promptTemplate(\n initial: WizardTemplate | undefined,\n _msg: WizardMessages,\n): Promise<WizardTemplate> {\n if (initial) return initial\n const result = await p.select<WizardTemplate>({\n message: 'Pick a starter template',\n options: [\n {\n value: 'minimal',\n label: 'Minimal — Vite + TypeScript, one file',\n hint: 'hub + browser storage, put/get (recommended)',\n },\n {\n value: 'nuxt-default',\n label: 'Nuxt 4 + Pinia',\n hint: 'SSR + file-based routing',\n },\n {\n value: 'vite-vue',\n label: 'Vite + Vue 3 + Pinia',\n hint: 'Client-side SPA, shorter install graph',\n },\n {\n value: 'vanilla',\n label: 'Vite + TypeScript',\n hint: 'No framework, smallest footprint',\n },\n {\n value: 'electron',\n label: 'Electron + Vue 3',\n hint: 'Desktop app with to-file adapter — USB workflow',\n },\n ],\n initialValue: 'minimal',\n })\n if (p.isCancel(result)) {\n process.exit(1)\n }\n return result\n}\n\nasync function promptSampleData(initial: boolean | undefined, msg: WizardMessages): Promise<boolean> {\n if (typeof initial === 'boolean') return initial\n const result = await p.confirm({\n message: msg.promptSampleData,\n initialValue: true,\n })\n if (p.isCancel(result)) {\n p.cancel(msg.cancelled)\n process.exit(1)\n }\n return result\n}\n\nasync function assertWritableTarget(target: string): Promise<void> {\n try {\n const entries = await fs.readdir(target)\n if (entries.length > 0) {\n throw new Error(\n `Target directory '${target}' already exists and is not empty. Refusing to overwrite — pick a different project name or remove the directory first.`,\n )\n }\n } catch (err: unknown) {\n // ENOENT is the happy path — the directory doesn't exist yet.\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') return\n throw err\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","/**\n * Detect whether the wizard is being invoked inside an existing\n * Nuxt 4 project that should be augmented in place, vs a blank\n * directory where the wizard should create a fresh starter.\n *\n * The detection rule is intentionally narrow so we don't trip on\n * random `package.json` files sitting in the parent tree: a project\n * counts as \"existing Nuxt 4\" only when the SAME directory has\n * BOTH `nuxt.config.ts` (or `.js`) AND a `package.json` that lists\n * `nuxt` in any of the dependency sections. Either alone is\n * ambiguous:\n *\n * - `nuxt.config.ts` without a package.json could be a stray\n * template scratch file the user moved here.\n * - `package.json` with `nuxt` but no config file could be a\n * half-deleted project or a pnpm workspace root that pulls\n * nuxt in transitively for docs.\n *\n * Requiring both avoids both false positives.\n *\n * We deliberately do NOT walk upward to find a parent Nuxt project.\n * The user's cwd is load-bearing — if they wanted to augment a\n * parent dir, they should cd there first. Walking up would make\n * \"run the wizard to create a fresh app inside my existing\n * monorepo\" silently augment the monorepo root instead.\n */\n\nimport { promises as fs } from 'node:fs'\nimport path from 'node:path'\n\nexport interface NuxtDetection {\n /** Whether cwd contains an existing Nuxt 4 project. */\n readonly existing: boolean\n /** Absolute path of the detected config file, if any. */\n readonly configPath: string | null\n /** Absolute path of the detected package.json, if any. */\n readonly packageJsonPath: string | null\n /** Reason strings explaining the detection outcome — useful for diagnostic messages. */\n readonly reasons: readonly string[]\n}\n\n/**\n * Inspect `cwd` for an existing Nuxt 4 project. Returns a detailed\n * result object so the caller can both branch on `existing` and\n * surface the specific reasons to the user.\n *\n * Pure in terms of filesystem reads — no writes, no network, no\n * caching. Callers who need cached detection should memoize on\n * their side.\n */\nexport async function detectNuxtProject(cwd: string): Promise<NuxtDetection> {\n const reasons: string[] = []\n\n // Step 1: look for a Nuxt config file. Both extensions are\n // valid; Nuxt itself accepts either.\n const configCandidates = ['nuxt.config.ts', 'nuxt.config.js', 'nuxt.config.mjs']\n let configPath: string | null = null\n for (const name of configCandidates) {\n const candidate = path.join(cwd, name)\n if (await pathExists(candidate)) {\n configPath = candidate\n reasons.push(`Found ${name}`)\n break\n }\n }\n if (!configPath) {\n reasons.push('No nuxt.config.{ts,js,mjs} in cwd')\n return {\n existing: false,\n configPath: null,\n packageJsonPath: null,\n reasons,\n }\n }\n\n // Step 2: look for a package.json in the same directory.\n const pkgPath = path.join(cwd, 'package.json')\n if (!(await pathExists(pkgPath))) {\n reasons.push('Config file present but no package.json — ambiguous, skipping')\n return {\n existing: false,\n configPath,\n packageJsonPath: null,\n reasons,\n }\n }\n\n // Step 3: verify `nuxt` is in one of the dependency sections.\n // We're lenient about WHICH section (dependencies, devDependencies,\n // peerDependencies) because real-world projects put it in all of\n // them depending on the tooling.\n let pkg: Record<string, unknown>\n try {\n pkg = JSON.parse(await fs.readFile(pkgPath, 'utf8')) as Record<string, unknown>\n } catch (err) {\n reasons.push(`package.json is not valid JSON: ${(err as Error).message}`)\n return {\n existing: false,\n configPath,\n packageJsonPath: pkgPath,\n reasons,\n }\n }\n\n const depSections = ['dependencies', 'devDependencies', 'peerDependencies'] as const\n let nuxtVersion: string | undefined\n for (const section of depSections) {\n const deps = pkg[section]\n if (deps && typeof deps === 'object' && 'nuxt' in deps) {\n nuxtVersion = (deps as Record<string, string>)['nuxt']\n reasons.push(`Found nuxt@${nuxtVersion} in ${section}`)\n break\n }\n }\n if (!nuxtVersion) {\n reasons.push('Config file present, but package.json does not list `nuxt` as a dependency')\n return {\n existing: false,\n configPath,\n packageJsonPath: pkgPath,\n reasons,\n }\n }\n\n return {\n existing: true,\n configPath,\n packageJsonPath: pkgPath,\n reasons,\n }\n}\n\n/** Cheap fs.access wrapper that returns a boolean instead of throwing. */\nasync function pathExists(target: string): Promise<boolean> {\n try {\n await fs.access(target)\n return true\n } catch {\n return false\n }\n}\n","/**\n * English (`en`) wizard messages — the default locale.\n *\n * Editing note: if you change a key's value here, update the\n * matching key in every other locale bundle (`th.ts`, future\n * additions). The key-parity test in `__tests__/i18n.test.ts`\n * is the safety net: it fails if any locale is missing a key\n * or has an extra one, so a forgotten update surfaces in CI\n * rather than in a user report.\n */\n\nimport type { WizardMessages } from './types.js'\n\nexport const en: WizardMessages = {\n wizardIntro:\n 'A wizard for noy-db — None Of Your DataBase.\\n' +\n 'Generates a fresh encrypted-store starter — minimal by default, Nuxt and more on request.',\n\n promptProjectName: 'Project name',\n promptProjectNamePlaceholder: 'my-noy-db-app',\n promptAdapter: 'Storage adapter',\n adapterBrowserLabel:\n 'browser — localStorage / IndexedDB (recommended for web apps)',\n adapterFileLabel:\n 'file — JSON files on disk (Electron / Tauri / USB workflows)',\n adapterMemoryLabel:\n 'memory — no persistence (ideal for tests and demos)',\n promptSampleData: 'Include sample invoice records?',\n promptRecommended: 'Use the recommended setup? (hub + browser storage, no framework — add more later with `noy-db add`)',\n\n freshNextStepsTitle: 'Next steps',\n freshOutroDone: '✔ Done — happy encrypting!',\n\n augmentModeTitle: 'Augment mode',\n augmentDetectedPrefix: 'Detected existing Nuxt 4 project:',\n augmentDescription:\n 'The wizard will add @noy-db/in-nuxt to your modules array\\n' +\n 'and a noydb: config key. You can review the diff before\\n' +\n 'anything is written to disk.',\n\n augmentProposedChangesTitle: 'Proposed changes',\n augmentApplyConfirm: 'Apply these changes?',\n\n augmentAlreadyConfiguredTitle: 'Already configured',\n augmentNothingToDo: 'Nothing to do:',\n augmentAlreadyOutro: '✔ Your Nuxt config is already wired up.',\n augmentAborted: 'Aborted — your config is unchanged.',\n augmentDryRunOutro: '✔ Dry run — no files were modified.',\n augmentNextStepTitle: 'Next step',\n augmentInstallIntro:\n 'Install the @noy-db packages your config now depends on:',\n augmentInstallPmHint: '(or use npm/yarn/bun as appropriate)',\n augmentDoneOutro: '✔ Config updated — happy encrypting!',\n augmentUnsupportedPrefix: 'Cannot safely patch this config:',\n\n cancelled: 'Cancelled.',\n}\n","/**\n * Thai (`th`) wizard messages.\n *\n * Editing note: this bundle MUST stay in key-parity with `en.ts`.\n * The key-parity test in `__tests__/i18n.test.ts` fails if any key\n * is missing or extra here, so a forgotten translation surfaces in\n * CI rather than as a runtime crash for a Thai-speaking user.\n *\n * Translation conventions:\n * - Technical identifiers (`modules`, `noydb:`, `nuxt.config.ts`,\n * `Nuxt 4`, `Pinia`, adapter names like `browser`/`file`/`memory`)\n * stay in English. This matches how Thai developers actually\n * read and write code — translating them would just add a\n * mental round-trip.\n * - Product name \"noy-db\" and the \"None Of Your DataBase\" tagline\n * also stay in English; they're brand strings, not prose.\n * - Validation/error strings are NOT in this file — they stay in\n * English so bug reports look the same in any locale (see the\n * docstring on `WizardMessages` in `types.ts` for the rationale).\n */\n\nimport type { WizardMessages } from './types.js'\n\nexport const th: WizardMessages = {\n wizardIntro:\n 'ตัวช่วยสร้างสำหรับ noy-db — None Of Your DataBase\\n' +\n 'สร้างโปรเจกต์เริ่มต้น Nuxt 4 + Pinia พร้อมที่เก็บข้อมูลแบบเข้ารหัส',\n\n promptProjectName: 'ชื่อโปรเจกต์',\n promptProjectNamePlaceholder: 'my-noy-db-app',\n promptAdapter: 'อะแดปเตอร์จัดเก็บข้อมูล',\n adapterBrowserLabel:\n 'browser — localStorage / IndexedDB (แนะนำสำหรับเว็บแอป)',\n adapterFileLabel:\n 'file — ไฟล์ JSON บนดิสก์ (Electron / Tauri / USB)',\n adapterMemoryLabel:\n 'memory — ไม่บันทึกข้อมูล (เหมาะสำหรับการทดสอบและตัวอย่าง)',\n promptSampleData: 'เพิ่มข้อมูลตัวอย่างใบแจ้งหนี้หรือไม่?',\n promptRecommended: 'ใช้การตั้งค่าที่แนะนำไหม? (hub + พื้นที่เก็บข้อมูลในเบราว์เซอร์ ไม่มีเฟรมเวิร์ก — เพิ่มทีหลังได้ด้วย `noy-db add`)',\n\n freshNextStepsTitle: 'ขั้นตอนถัดไป',\n freshOutroDone: '✔ เสร็จเรียบร้อย — ขอให้สนุกกับการเข้ารหัส!',\n\n augmentModeTitle: 'โหมดเสริมโปรเจกต์เดิม',\n augmentDetectedPrefix: 'พบโปรเจกต์ Nuxt 4 ที่มีอยู่แล้ว:',\n augmentDescription:\n 'ตัวช่วยจะเพิ่ม @noy-db/in-nuxt เข้าใน modules\\n' +\n 'และเพิ่มคีย์ noydb: ในไฟล์ config คุณสามารถดู diff\\n' +\n 'ก่อนที่จะเขียนไฟล์ลงดิสก์ได้',\n\n augmentProposedChangesTitle: 'รายการเปลี่ยนแปลงที่จะทำ',\n augmentApplyConfirm: 'ยืนยันการเปลี่ยนแปลงเหล่านี้?',\n\n augmentAlreadyConfiguredTitle: 'ตั้งค่าไว้แล้ว',\n augmentNothingToDo: 'ไม่มีอะไรต้องทำ:',\n augmentAlreadyOutro: '✔ ไฟล์ Nuxt config ของคุณตั้งค่าครบแล้ว',\n augmentAborted: 'ยกเลิก — ไฟล์ config ของคุณไม่ถูกแก้ไข',\n augmentDryRunOutro: '✔ Dry run — ไม่มีไฟล์ใดถูกแก้ไข',\n augmentNextStepTitle: 'ขั้นตอนถัดไป',\n augmentInstallIntro:\n 'ติดตั้งแพ็กเกจ @noy-db ที่ config ของคุณต้องใช้:',\n augmentInstallPmHint: '(หรือใช้ npm/yarn/bun ตามความเหมาะสม)',\n augmentDoneOutro: '✔ อัปเดต config เรียบร้อย — ขอให้สนุกกับการเข้ารหัส!',\n augmentUnsupportedPrefix: 'ไม่สามารถแก้ไข config นี้ได้อย่างปลอดภัย:',\n\n cancelled: 'ยกเลิกแล้ว',\n}\n","/**\n * i18n entrypoint for the `create-noy-db` wizard.\n *\n * Three responsibilities:\n *\n * 1. Re-export `Locale` and `WizardMessages` so callers don't\n * need to know about the bundle layout.\n * 2. `detectLocale(env)` — pure function that maps Unix-style\n * `LC_ALL` / `LANG` / `LANGUAGE` env vars to a supported\n * `Locale`. Returns `'en'` for anything we don't recognise.\n * 3. `loadMessages(locale)` — synchronous lookup that returns\n * the message bundle for a locale. Synchronous (not dynamic\n * `import()`) on purpose: bundles are tiny (< 2 KB each), the\n * wizard reads them on every prompt, and async would force\n * every caller to be async. tsup tree-shakes unused locales\n * out of the bin only if we use top-level `import`s.\n *\n * ## Why env-var detection instead of `Intl.DateTimeFormat().resolvedOptions().locale`\n *\n * The Intl approach reads the JS engine's *display* locale, which\n * on most CI runners and Docker images is `en-US` regardless of\n * the user's actual setup. The Unix env vars (`LC_ALL`, `LANG`)\n * are how shells, terminals, and CLI tools have negotiated locale\n * for 30+ years — that's what a Thai-speaking dev's terminal will\n * actually have set. Following that convention also means power\n * users can override per-invocation with `LANG=th_TH.UTF-8 npm\n * create noy-db`, no flag required.\n */\n\nimport { en } from './en.js'\nimport { th } from './th.js'\nimport type { Locale, WizardMessages } from './types.js'\n\nexport type { Locale, WizardMessages } from './types.js'\n\nconst BUNDLES: Record<Locale, WizardMessages> = { en, th }\n\n/** Every locale we ship a bundle for. Used by tests and `--lang` validation. */\nexport const SUPPORTED_LOCALES: readonly Locale[] = ['en', 'th'] as const\n\n/**\n * Resolve a locale code to its message bundle. Falls back to `en`\n * if the requested locale isn't shipped — defensive, since\n * `Locale` is a union type and TS already prevents this at compile\n * time, but `--lang` parsing comes from user input at runtime.\n */\nexport function loadMessages(locale: Locale): WizardMessages {\n return BUNDLES[locale] ?? BUNDLES.en\n}\n\n/**\n * Auto-detect a locale from POSIX env vars. Returns `'en'` when\n * nothing is set or when the value doesn't match a supported\n * locale — never throws.\n *\n * Inspection order matches the POSIX spec:\n * 1. `LC_ALL` (overrides everything)\n * 2. `LC_MESSAGES` (the category we actually care about)\n * 3. `LANG` (system default)\n * 4. `LANGUAGE` (GNU extension, comma-separated preference list)\n *\n * The first non-empty value wins. We then strip the encoding\n * suffix (`th_TH.UTF-8` → `th_TH`) and the region (`th_TH` → `th`)\n * before matching against `SUPPORTED_LOCALES`.\n */\nexport function detectLocale(env: NodeJS.ProcessEnv = process.env): Locale {\n const candidates = [\n env.LC_ALL,\n env.LC_MESSAGES,\n env.LANG,\n // LANGUAGE is a comma-separated preference list — take the first\n // entry. We deliberately do NOT walk the whole list; the wizard\n // ships exactly two locales, so a \"best fit\" walk would be\n // overkill and would obscure unexpected behaviour.\n env.LANGUAGE?.split(':')[0]?.split(',')[0],\n ]\n\n for (const raw of candidates) {\n if (!raw) continue\n const normalised = raw\n .split('.')[0]! // strip encoding: th_TH.UTF-8 → th_TH\n .split('@')[0]! // strip modifier: en_US@euro → en_US\n .toLowerCase()\n .split('_')[0]! // strip region: th_th → th\n\n if ((SUPPORTED_LOCALES as readonly string[]).includes(normalised)) {\n return normalised as Locale\n }\n }\n\n return 'en'\n}\n\n/**\n * Parse a `--lang` CLI argument into a `Locale`. Throws a clear\n * error for unsupported values — the caller (parse-args) catches\n * and reformats into a usage message.\n */\nexport function parseLocaleFlag(value: string): Locale {\n const normalised = value.toLowerCase().trim()\n if ((SUPPORTED_LOCALES as readonly string[]).includes(normalised)) {\n return normalised as Locale\n }\n throw new Error(\n `Unsupported --lang value: \"${value}\". Supported: ${SUPPORTED_LOCALES.join(', ')}`,\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 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 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 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"],"mappings":"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","names":["fs","fs","path","fs","path","path","fs","fs","path","pathExists","p","createNoydb","isCancel","cancel","createNoydb","withTeam","createNoydb","toFile","toFile","createNoydb","withTeam","fs","path","createNoydb","toFile","path","toFile","createNoydb","fs","readPodHeader","readFile","path","readFile","createNoydb","readPod","memoryStore","backup","dirname","join","resolve","pathToFileURL","isRecord","p","join","pathToFileURL","dirname","resolve","existsSync","readFileSync","join","pc","readFileSync","p","pc","existsSync","join","augmentNuxtConfig","writeAugmentedConfig"]}