@noy-db/cli 0.4.0-pre.7 → 0.4.0-pre.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/noydb.js CHANGED
@@ -184,10 +184,10 @@ function scaffold(profile) {
184
184
  notes: "Local-only single-user. No cloud.",
185
185
  code: [
186
186
  `import { createNoydb } from '@noy-db/hub'`,
187
- `import { jsonFile } from '@noy-db/to-file'`,
187
+ `import { toFile } from '@noy-db/to-file'`,
188
188
  ``,
189
189
  `export default {`,
190
- ` store: jsonFile({ dir: process.env.NOYDB_DATA_DIR ?? './data' }),`,
190
+ ` store: toFile({ dir: process.env.NOYDB_DATA_DIR ?? './data' }),`,
191
191
  ` user: process.env.NOYDB_USER ?? 'owner',`,
192
192
  ` secret: process.env.NOYDB_SECRET,`,
193
193
  `}`
@@ -203,11 +203,11 @@ NOYDB_DATA_DIR=./data
203
203
  notes: "Offline-first + cloud mirror. Local authoritative, sync opportunistically.",
204
204
  code: [
205
205
  `import { createNoydb, INDEXED_STORE_POLICY } from '@noy-db/hub'`,
206
- `import { browserIdbStore } from '@noy-db/to-browser-idb'`,
206
+ `import { toBrowserIdb } from '@noy-db/to-browser-idb'`,
207
207
  `import { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,
208
208
  ``,
209
209
  `export default {`,
210
- ` store: browserIdbStore({ prefix: process.env.NOYDB_APP ?? 'myapp' }),`,
210
+ ` store: toBrowserIdb({ prefix: process.env.NOYDB_APP ?? 'myapp' }),`,
211
211
  ` sync: [{`,
212
212
  ` store: awsDynamoStore({`,
213
213
  ` table: process.env.NOYDB_DYNAMO_TABLE!,`,
@@ -319,12 +319,12 @@ NOYDB_COLD_AFTER_DAYS=90
319
319
  notes: "Multi-peer team sync. Primary + peer + backup + archive.",
320
320
  code: [
321
321
  `import { createNoydb } from '@noy-db/hub'`,
322
- `import { browserIdbStore } from '@noy-db/to-browser-idb'`,
322
+ `import { toBrowserIdb } from '@noy-db/to-browser-idb'`,
323
323
  `import { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,
324
324
  `import { awsS3Store } from '@noy-db/to-aws-s3'`,
325
325
  ``,
326
326
  `export default {`,
327
- ` store: browserIdbStore({ prefix: process.env.NOYDB_APP ?? 'team' }),`,
327
+ ` store: toBrowserIdb({ prefix: process.env.NOYDB_APP ?? 'team' }),`,
328
328
  ` sync: [`,
329
329
  ` { store: awsDynamoStore({ table: process.env.NOYDB_DYNAMO_TABLE! }), role: 'sync-peer', label: 'team-hot' },`,
330
330
  ` { store: awsS3Store({ bucket: process.env.NOYDB_S3_BACKUP! }), role: 'backup', label: 'team-backup' },`,
@@ -348,12 +348,12 @@ NOYDB_S3_ARCHIVE=team-archive
348
348
  notes: "CRDT collaboration \u2014 Yjs-backed shared records over the encrypted envelope.",
349
349
  code: [
350
350
  `import { createNoydb } from '@noy-db/hub'`,
351
- `import { browserIdbStore } from '@noy-db/to-browser-idb'`,
351
+ `import { toBrowserIdb } from '@noy-db/to-browser-idb'`,
352
352
  `import { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,
353
353
  `// import { yjsCollection } from '@noy-db/in-yjs' // use inside your app`,
354
354
  ``,
355
355
  `export default {`,
356
- ` store: browserIdbStore({ prefix: 'collab' }),`,
356
+ ` store: toBrowserIdb({ prefix: 'collab' }),`,
357
357
  ` sync: [{ store: awsDynamoStore({ table: process.env.NOYDB_DYNAMO_TABLE! }), role: 'sync-peer' }],`,
358
358
  ` user: process.env.NOYDB_USER!,`,
359
359
  ` secret: process.env.NOYDB_SECRET,`,
@@ -372,10 +372,10 @@ NOYDB_DYNAMO_TABLE=collab-live
372
372
  notes: "USB-portable \u2014 everything on a single file store, no cloud.",
373
373
  code: [
374
374
  `import { createNoydb } from '@noy-db/hub'`,
375
- `import { jsonFile } from '@noy-db/to-file'`,
375
+ `import { toFile } from '@noy-db/to-file'`,
376
376
  ``,
377
377
  `export default {`,
378
- ` store: jsonFile({ dir: process.env.NOYDB_DATA_DIR ?? '/Volumes/MY_USB/data' }),`,
378
+ ` store: toFile({ dir: process.env.NOYDB_DATA_DIR ?? '/Volumes/MY_USB/data' }),`,
379
379
  ` user: process.env.NOYDB_USER!,`,
380
380
  ` secret: process.env.NOYDB_SECRET,`,
381
381
  `}`
@@ -414,12 +414,12 @@ NOYDB_REGION=ap-southeast-1
414
414
  notes: "Authentication bridge (secret-less unlock via OIDC or WebAuthn).",
415
415
  code: [
416
416
  `import { createNoydb } from '@noy-db/hub'`,
417
- `import { browserIdbStore } from '@noy-db/to-browser-idb'`,
417
+ `import { toBrowserIdb } from '@noy-db/to-browser-idb'`,
418
418
  `// import { unlockWebAuthn } from '@noy-db/on-webauthn' // in the browser`,
419
419
  `// import { keyConnector } from '@noy-db/on-oidc' // in a server app`,
420
420
  ``,
421
421
  `export default {`,
422
- ` store: browserIdbStore({ prefix: process.env.NOYDB_APP ?? 'app' }),`,
422
+ ` store: toBrowserIdb({ prefix: process.env.NOYDB_APP ?? 'app' }),`,
423
423
  ` user: process.env.NOYDB_USER!,`,
424
424
  ` // secret supplied by the unlock method at openVault() time, not here.`,
425
425
  `}`
@@ -541,7 +541,7 @@ async function runMonitor(argv) {
541
541
  process.stderr.write("config has no `store` \u2014 nothing to monitor\n");
542
542
  return 1;
543
543
  }
544
- const { store: metered, meter } = toMeter(innerStore, {
544
+ const metered = toMeter(innerStore, {
545
545
  degradedMs: 500,
546
546
  onDegraded: (e) => process.stderr.write(`DEGRADED: ${e.reason}
547
547
  `),
@@ -554,16 +554,16 @@ async function runMonitor(argv) {
554
554
  process.stdout.write(`monitoring ${file} \u2014 interval ${intervalMs}ms \u2014 Ctrl-C to stop
555
555
 
556
556
  `);
557
- const stop = installSigintHandler(meter);
557
+ const stop = installSigintHandler(metered.meter);
558
558
  return new Promise((resolveP) => {
559
559
  const timer = setInterval(() => {
560
560
  if (stop.signalled) {
561
561
  clearInterval(timer);
562
- meter.close();
562
+ metered.meter.close();
563
563
  resolveP(0);
564
564
  return;
565
565
  }
566
- const snap = meter.snapshot();
566
+ const snap = metered.meter.snapshot();
567
567
  process.stdout.write(formatSnapshot(snap) + "\n");
568
568
  }, intervalMs);
569
569
  });
@@ -885,7 +885,7 @@ ${usage()}
885
885
  }
886
886
 
887
887
  // src/bin/noydb.ts
888
- var VERSION2 = "0.4.0-pre.7";
888
+ var VERSION2 = "0.4.0-pre.8";
889
889
  function usage2() {
890
890
  return [
891
891
  "noydb \u2014 command-line tools for @noy-db",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/inspect.ts","../../src/commands/verify.ts","../../src/commands/config.ts","../../src/commands/monitor.ts","../../src/commands/describe.ts","../../src/bin/noydb.ts"],"sourcesContent":["/**\n * `noydb 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 { readNoydbBundleHeader } 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 = readNoydbBundleHeader(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: noydb 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 * `noydb 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 { readNoydbBundle, readNoydbBundleHeader } 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 = readNoydbBundleHeader(bytes)\n checks.magic = true\n checks.header = true\n handle = header.handle\n bodyBytes = header.bodyBytes\n\n // readNoydbBundle() verifies bodySha256 internally — if it doesn't\n // throw, the body hash matched.\n await readNoydbBundle(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: noydb 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 * `noydb config validate` and `noydb 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 from `docs/guides/topology-matrix.md`. 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 noydb) 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} && noydb config validate ${filePath.replace(/\\.[mc]?ts$/, '.js')}\\n` +\n ` (b) run via tsx: npx tsx $(which noydb) 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: noydb 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/** Profiles match `docs/guides/topology-matrix.md` § View 3. */\nexport type Profile = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J'\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 { jsonFile } from '@noy-db/to-file'`,\n ``,\n `export default {`,\n ` store: jsonFile({ 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 { browserIdbStore } from '@noy-db/to-browser-idb'`,\n `import { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n ``,\n `export default {`,\n ` store: browserIdbStore({ prefix: process.env.NOYDB_APP ?? 'myapp' }),`,\n ` sync: [{`,\n ` store: awsDynamoStore({`,\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 { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n `import { awsS3Store } from '@noy-db/to-aws-s3'`,\n ``,\n `export default {`,\n ` store: routeStore({`,\n ` default: awsDynamoStore({`,\n ` table: process.env.NOYDB_DYNAMO_TABLE!,`,\n ` region: process.env.AWS_REGION ?? 'us-east-1',`,\n ` }),`,\n ` blobs: awsS3Store({`,\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 { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n ``,\n `export default {`,\n ` store: wrapStore(`,\n ` awsDynamoStore({ 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 { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n `import { awsS3Store } from '@noy-db/to-aws-s3'`,\n ``,\n `export default {`,\n ` store: routeStore({`,\n ` default: awsDynamoStore({ table: process.env.NOYDB_DYNAMO_TABLE! }),`,\n ` age: {`,\n ` cold: awsS3Store({ 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 { browserIdbStore } from '@noy-db/to-browser-idb'`,\n `import { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n `import { awsS3Store } from '@noy-db/to-aws-s3'`,\n ``,\n `export default {`,\n ` store: browserIdbStore({ prefix: process.env.NOYDB_APP ?? 'team' }),`,\n ` sync: [`,\n ` { store: awsDynamoStore({ table: process.env.NOYDB_DYNAMO_TABLE! }), role: 'sync-peer', label: 'team-hot' },`,\n ` { store: awsS3Store({ bucket: process.env.NOYDB_S3_BACKUP! }), role: 'backup', label: 'team-backup' },`,\n ` { store: awsS3Store({ 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 { browserIdbStore } from '@noy-db/to-browser-idb'`,\n `import { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n `// import { yjsCollection } from '@noy-db/in-yjs' // use inside your app`,\n ``,\n `export default {`,\n ` store: browserIdbStore({ prefix: 'collab' }),`,\n ` sync: [{ store: awsDynamoStore({ 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 { jsonFile } from '@noy-db/to-file'`,\n ``,\n `export default {`,\n ` store: jsonFile({ 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 { awsDynamoStore } 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: awsDynamoStore({ 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 { browserIdbStore } from '@noy-db/to-browser-idb'`,\n `// import { unlockWebAuthn } from '@noy-db/on-webauthn' // in the browser`,\n `// import { keyConnector } from '@noy-db/on-oidc' // in a server app`,\n ``,\n `export default {`,\n ` store: browserIdbStore({ 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\nexport async function runConfigScaffold(argv: readonly string[]): Promise<number> {\n const profileArg = argv.find((a) => a.startsWith('--profile='))\n const profile = (profileArg?.split('=')[1] ?? 'A') as Profile\n if (!/^[A-J]$/.test(profile)) {\n process.stderr.write(`unknown profile: ${profile}. Valid: A-J (see docs/guides/topology-matrix.md)\\n`)\n return 2\n }\n const out = scaffold(profile)\n // The config goes to stdout so `noydb 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(`// ── noydb 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 * `noydb monitor <config.ts>` — live text dashboard of store metrics.\n *\n * Loads a `NoydbOptions` from a config file, wraps the primary store\n * in `@noy-db/to-meter`, creates a `Noydb` instance, and prints a\n * refreshing snapshot to stdout at a configurable interval. Ctrl-C\n * to stop.\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 { toMeter } from '@noy-db/to-meter'\nimport type { MeterSnapshot } from '@noy-db/to-meter'\nimport { loadOptionsFromFile } from './config.js'\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: noydb 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 const { store: metered, meter } = toMeter(innerStore as never, {\n degradedMs: 500,\n onDegraded: (e) => process.stderr.write(`DEGRADED: ${e.reason}\\n`),\n onRestored: (e) => process.stderr.write(`RESTORED: ${e.reason}\\n`),\n })\n\n // Replace the store in the options object so the Noydb instance\n // goes through the meter.\n const liveOpts = { ...opts, store: metered } as Record<string, unknown>\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(liveOpts)\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: MeterSnapshot): 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 * `noydb 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 `docs/superpowers/specs/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 {\n EncryptedEnvelope,\n NoydbStore,\n VaultSnapshot,\n VaultSchemaSnapshot,\n} from '@noy-db/hub'\nimport {\n createNoydb,\n readNoydbBundle,\n ConflictError,\n loadPersistedSchema,\n SCHEMAS_COLLECTION,\n} from '@noy-db/hub'\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 readNoydbBundle(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: 1,\n _provenance: {\n generatedBy: `noydb 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 ...(rest.aclRoles !== undefined ? { aclRoles: rest.aclRoles } : {}),\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<CryptoKey> } },\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<CryptoKey> } },\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: noydb 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// Re-export for tests\nexport { loadPersistedSchema, SCHEMAS_COLLECTION }\n","#!/usr/bin/env node\n/**\n * `noydb` — command dispatcher.\n *\n * Routes argv to the appropriate subcommand. Kept minimal by design\n * (no commander/yargs dep) — scope is small enough for flat\n * if/else routing. Revisit if/when the CLI grows past ~5 commands.\n *\n * noydb inspect <file.noydb>\n * noydb verify <file.noydb>\n * noydb describe <file.noydb>\n * noydb config validate <file.ts>\n * noydb config scaffold [--profile=<A-J>]\n * noydb monitor <config.ts> [--interval=ms]\n * noydb --help\n * noydb --version\n *\n * @module\n */\nimport { runInspect } from '../commands/inspect.js'\nimport { runVerify } from '../commands/verify.js'\nimport { runConfigValidate, runConfigScaffold } from '../commands/config.js'\nimport { runMonitor } from '../commands/monitor.js'\nimport { runDescribe } from '../commands/describe.js'\n\n// Replaced at build time by tsup's `define` with the package.json version.\ndeclare const __CLI_VERSION__: string\nconst VERSION = __CLI_VERSION__\n\nfunction usage(): string {\n return [\n 'noydb — command-line tools for @noy-db',\n '',\n 'Usage:',\n ' noydb inspect <file.noydb> Print bundle header (no secret)',\n ' noydb verify <file.noydb> Verify bundle integrity (no secret)',\n ' noydb describe <file.noydb> [options] Emit YAML/JSON audit of a bundle',\n ' noydb config validate <file.js|mjs> Sanity-check a NoydbOptions file',\n ' noydb config scaffold [--profile=<A-J>] Emit a topology-profile skeleton',\n ' noydb monitor <file.js|mjs> [--interval=ms] Live dashboard of store metrics',\n '',\n 'TypeScript configs: run under a TS-capable runtime, e.g. `npx tsx $(which noydb) config validate foo.ts`.',\n ' noydb --version Print CLI version',\n ' noydb --help Show this message',\n '',\n 'Profiles for `config scaffold` map to docs/guides/topology-matrix.md § View 3.',\n ].join('\\n')\n}\n\nasync function main(argv: readonly string[]): Promise<number> {\n const [cmd, ...rest] = argv\n\n if (!cmd || cmd === '--help' || cmd === '-h' || cmd === 'help') {\n process.stdout.write(usage() + '\\n')\n return 0\n }\n if (cmd === '--version' || cmd === '-v') {\n process.stdout.write(VERSION + '\\n')\n return 0\n }\n\n switch (cmd) {\n case 'inspect': return runInspect(rest)\n case 'verify': return runVerify(rest)\n case 'describe': return runDescribe(rest)\n case 'monitor': return runMonitor(rest)\n case 'config': {\n const [sub, ...subRest] = rest\n if (sub === 'validate') return runConfigValidate(subRest)\n if (sub === 'scaffold') return runConfigScaffold(subRest)\n process.stderr.write(`unknown config subcommand: ${sub ?? '(none)'}\\n`)\n return 2\n }\n default:\n process.stderr.write(`unknown command: ${cmd}\\n\\n${usage()}\\n`)\n return 2\n }\n}\n\nmain(process.argv.slice(2))\n .then((code) => process.exit(code))\n .catch((err) => {\n process.stderr.write(`fatal: ${(err as Error).message}\\n`)\n process.exit(1)\n })\n"],"mappings":"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","names":["readNoydbBundleHeader","readFile","readFile","readNoydbBundle","VERSION","usage"]}
1
+ {"version":3,"sources":["../../src/commands/inspect.ts","../../src/commands/verify.ts","../../src/commands/config.ts","../../src/commands/monitor.ts","../../src/commands/describe.ts","../../src/bin/noydb.ts"],"sourcesContent":["/**\n * `noydb 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 { readNoydbBundleHeader } 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 = readNoydbBundleHeader(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: noydb 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 * `noydb 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 { readNoydbBundle, readNoydbBundleHeader } 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 = readNoydbBundleHeader(bytes)\n checks.magic = true\n checks.header = true\n handle = header.handle\n bodyBytes = header.bodyBytes\n\n // readNoydbBundle() verifies bodySha256 internally — if it doesn't\n // throw, the body hash matched.\n await readNoydbBundle(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: noydb 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 * `noydb config validate` and `noydb 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 from `docs/guides/topology-matrix.md`. 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 noydb) 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} && noydb config validate ${filePath.replace(/\\.[mc]?ts$/, '.js')}\\n` +\n ` (b) run via tsx: npx tsx $(which noydb) 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: noydb 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/** Profiles match `docs/guides/topology-matrix.md` § View 3. */\nexport type Profile = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J'\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 { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n ``,\n `export default {`,\n ` store: toBrowserIdb({ prefix: process.env.NOYDB_APP ?? 'myapp' }),`,\n ` sync: [{`,\n ` store: awsDynamoStore({`,\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 { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n `import { awsS3Store } from '@noy-db/to-aws-s3'`,\n ``,\n `export default {`,\n ` store: routeStore({`,\n ` default: awsDynamoStore({`,\n ` table: process.env.NOYDB_DYNAMO_TABLE!,`,\n ` region: process.env.AWS_REGION ?? 'us-east-1',`,\n ` }),`,\n ` blobs: awsS3Store({`,\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 { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n ``,\n `export default {`,\n ` store: wrapStore(`,\n ` awsDynamoStore({ 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 { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n `import { awsS3Store } from '@noy-db/to-aws-s3'`,\n ``,\n `export default {`,\n ` store: routeStore({`,\n ` default: awsDynamoStore({ table: process.env.NOYDB_DYNAMO_TABLE! }),`,\n ` age: {`,\n ` cold: awsS3Store({ 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 { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n `import { awsS3Store } from '@noy-db/to-aws-s3'`,\n ``,\n `export default {`,\n ` store: toBrowserIdb({ prefix: process.env.NOYDB_APP ?? 'team' }),`,\n ` sync: [`,\n ` { store: awsDynamoStore({ table: process.env.NOYDB_DYNAMO_TABLE! }), role: 'sync-peer', label: 'team-hot' },`,\n ` { store: awsS3Store({ bucket: process.env.NOYDB_S3_BACKUP! }), role: 'backup', label: 'team-backup' },`,\n ` { store: awsS3Store({ 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 { awsDynamoStore } 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: awsDynamoStore({ 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 { awsDynamoStore } 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: awsDynamoStore({ 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 { keyConnector } 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\nexport async function runConfigScaffold(argv: readonly string[]): Promise<number> {\n const profileArg = argv.find((a) => a.startsWith('--profile='))\n const profile = (profileArg?.split('=')[1] ?? 'A') as Profile\n if (!/^[A-J]$/.test(profile)) {\n process.stderr.write(`unknown profile: ${profile}. Valid: A-J (see docs/guides/topology-matrix.md)\\n`)\n return 2\n }\n const out = scaffold(profile)\n // The config goes to stdout so `noydb 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(`// ── noydb 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 * `noydb monitor <config.ts>` — live text dashboard of store metrics.\n *\n * Loads a `NoydbOptions` from a config file, wraps the primary store\n * in `@noy-db/to-meter`, creates a `Noydb` instance, and prints a\n * refreshing snapshot to stdout at a configurable interval. Ctrl-C\n * to stop.\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 { toMeter } from '@noy-db/to-meter'\nimport type { MeterSnapshot } from '@noy-db/to-meter'\nimport { loadOptionsFromFile } from './config.js'\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: noydb 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 now returns the store itself, with the handle attached.\n const metered = toMeter(innerStore as never, {\n degradedMs: 500,\n onDegraded: (e) => process.stderr.write(`DEGRADED: ${e.reason}\\n`),\n onRestored: (e) => process.stderr.write(`RESTORED: ${e.reason}\\n`),\n })\n\n // Replace the store in the options object so the Noydb instance\n // goes through the meter.\n const liveOpts = { ...opts, store: metered } as Record<string, unknown>\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(liveOpts)\n\n process.stdout.write(`monitoring ${file} — interval ${intervalMs}ms — Ctrl-C to stop\\n\\n`)\n\n const stop = installSigintHandler(metered.meter)\n\n return new Promise<number>((resolveP) => {\n const timer = setInterval(() => {\n if (stop.signalled) {\n clearInterval(timer)\n metered.meter.close()\n resolveP(0)\n return\n }\n const snap = metered.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: MeterSnapshot): 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 * `noydb 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 `docs/superpowers/specs/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 {\n EncryptedEnvelope,\n NoydbStore,\n VaultSnapshot,\n VaultSchemaSnapshot,\n} from '@noy-db/hub'\nimport {\n createNoydb,\n readNoydbBundle,\n ConflictError,\n loadPersistedSchema,\n SCHEMAS_COLLECTION,\n} from '@noy-db/hub'\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 readNoydbBundle(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: 1,\n _provenance: {\n generatedBy: `noydb 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 ...(rest.aclRoles !== undefined ? { aclRoles: rest.aclRoles } : {}),\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<CryptoKey> } },\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<CryptoKey> } },\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: noydb 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// Re-export for tests\nexport { loadPersistedSchema, SCHEMAS_COLLECTION }\n","#!/usr/bin/env node\n/**\n * `noydb` — command dispatcher.\n *\n * Routes argv to the appropriate subcommand. Kept minimal by design\n * (no commander/yargs dep) — scope is small enough for flat\n * if/else routing. Revisit if/when the CLI grows past ~5 commands.\n *\n * noydb inspect <file.noydb>\n * noydb verify <file.noydb>\n * noydb describe <file.noydb>\n * noydb config validate <file.ts>\n * noydb config scaffold [--profile=<A-J>]\n * noydb monitor <config.ts> [--interval=ms]\n * noydb --help\n * noydb --version\n *\n * @module\n */\nimport { runInspect } from '../commands/inspect.js'\nimport { runVerify } from '../commands/verify.js'\nimport { runConfigValidate, runConfigScaffold } from '../commands/config.js'\nimport { runMonitor } from '../commands/monitor.js'\nimport { runDescribe } from '../commands/describe.js'\n\n// Replaced at build time by tsup's `define` with the package.json version.\ndeclare const __CLI_VERSION__: string\nconst VERSION = __CLI_VERSION__\n\nfunction usage(): string {\n return [\n 'noydb — command-line tools for @noy-db',\n '',\n 'Usage:',\n ' noydb inspect <file.noydb> Print bundle header (no secret)',\n ' noydb verify <file.noydb> Verify bundle integrity (no secret)',\n ' noydb describe <file.noydb> [options] Emit YAML/JSON audit of a bundle',\n ' noydb config validate <file.js|mjs> Sanity-check a NoydbOptions file',\n ' noydb config scaffold [--profile=<A-J>] Emit a topology-profile skeleton',\n ' noydb monitor <file.js|mjs> [--interval=ms] Live dashboard of store metrics',\n '',\n 'TypeScript configs: run under a TS-capable runtime, e.g. `npx tsx $(which noydb) config validate foo.ts`.',\n ' noydb --version Print CLI version',\n ' noydb --help Show this message',\n '',\n 'Profiles for `config scaffold` map to docs/guides/topology-matrix.md § View 3.',\n ].join('\\n')\n}\n\nasync function main(argv: readonly string[]): Promise<number> {\n const [cmd, ...rest] = argv\n\n if (!cmd || cmd === '--help' || cmd === '-h' || cmd === 'help') {\n process.stdout.write(usage() + '\\n')\n return 0\n }\n if (cmd === '--version' || cmd === '-v') {\n process.stdout.write(VERSION + '\\n')\n return 0\n }\n\n switch (cmd) {\n case 'inspect': return runInspect(rest)\n case 'verify': return runVerify(rest)\n case 'describe': return runDescribe(rest)\n case 'monitor': return runMonitor(rest)\n case 'config': {\n const [sub, ...subRest] = rest\n if (sub === 'validate') return runConfigValidate(subRest)\n if (sub === 'scaffold') return runConfigScaffold(subRest)\n process.stderr.write(`unknown config subcommand: ${sub ?? '(none)'}\\n`)\n return 2\n }\n default:\n process.stderr.write(`unknown command: ${cmd}\\n\\n${usage()}\\n`)\n return 2\n }\n}\n\nmain(process.argv.slice(2))\n .then((code) => process.exit(code))\n .catch((err) => {\n process.stderr.write(`fatal: ${(err as Error).message}\\n`)\n process.exit(1)\n })\n"],"mappings":"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","names":["readNoydbBundleHeader","readFile","readFile","readNoydbBundle","VERSION","usage"]}
package/dist/index.js CHANGED
@@ -182,10 +182,10 @@ function scaffold(profile) {
182
182
  notes: "Local-only single-user. No cloud.",
183
183
  code: [
184
184
  `import { createNoydb } from '@noy-db/hub'`,
185
- `import { jsonFile } from '@noy-db/to-file'`,
185
+ `import { toFile } from '@noy-db/to-file'`,
186
186
  ``,
187
187
  `export default {`,
188
- ` store: jsonFile({ dir: process.env.NOYDB_DATA_DIR ?? './data' }),`,
188
+ ` store: toFile({ dir: process.env.NOYDB_DATA_DIR ?? './data' }),`,
189
189
  ` user: process.env.NOYDB_USER ?? 'owner',`,
190
190
  ` secret: process.env.NOYDB_SECRET,`,
191
191
  `}`
@@ -201,11 +201,11 @@ NOYDB_DATA_DIR=./data
201
201
  notes: "Offline-first + cloud mirror. Local authoritative, sync opportunistically.",
202
202
  code: [
203
203
  `import { createNoydb, INDEXED_STORE_POLICY } from '@noy-db/hub'`,
204
- `import { browserIdbStore } from '@noy-db/to-browser-idb'`,
204
+ `import { toBrowserIdb } from '@noy-db/to-browser-idb'`,
205
205
  `import { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,
206
206
  ``,
207
207
  `export default {`,
208
- ` store: browserIdbStore({ prefix: process.env.NOYDB_APP ?? 'myapp' }),`,
208
+ ` store: toBrowserIdb({ prefix: process.env.NOYDB_APP ?? 'myapp' }),`,
209
209
  ` sync: [{`,
210
210
  ` store: awsDynamoStore({`,
211
211
  ` table: process.env.NOYDB_DYNAMO_TABLE!,`,
@@ -317,12 +317,12 @@ NOYDB_COLD_AFTER_DAYS=90
317
317
  notes: "Multi-peer team sync. Primary + peer + backup + archive.",
318
318
  code: [
319
319
  `import { createNoydb } from '@noy-db/hub'`,
320
- `import { browserIdbStore } from '@noy-db/to-browser-idb'`,
320
+ `import { toBrowserIdb } from '@noy-db/to-browser-idb'`,
321
321
  `import { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,
322
322
  `import { awsS3Store } from '@noy-db/to-aws-s3'`,
323
323
  ``,
324
324
  `export default {`,
325
- ` store: browserIdbStore({ prefix: process.env.NOYDB_APP ?? 'team' }),`,
325
+ ` store: toBrowserIdb({ prefix: process.env.NOYDB_APP ?? 'team' }),`,
326
326
  ` sync: [`,
327
327
  ` { store: awsDynamoStore({ table: process.env.NOYDB_DYNAMO_TABLE! }), role: 'sync-peer', label: 'team-hot' },`,
328
328
  ` { store: awsS3Store({ bucket: process.env.NOYDB_S3_BACKUP! }), role: 'backup', label: 'team-backup' },`,
@@ -346,12 +346,12 @@ NOYDB_S3_ARCHIVE=team-archive
346
346
  notes: "CRDT collaboration \u2014 Yjs-backed shared records over the encrypted envelope.",
347
347
  code: [
348
348
  `import { createNoydb } from '@noy-db/hub'`,
349
- `import { browserIdbStore } from '@noy-db/to-browser-idb'`,
349
+ `import { toBrowserIdb } from '@noy-db/to-browser-idb'`,
350
350
  `import { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,
351
351
  `// import { yjsCollection } from '@noy-db/in-yjs' // use inside your app`,
352
352
  ``,
353
353
  `export default {`,
354
- ` store: browserIdbStore({ prefix: 'collab' }),`,
354
+ ` store: toBrowserIdb({ prefix: 'collab' }),`,
355
355
  ` sync: [{ store: awsDynamoStore({ table: process.env.NOYDB_DYNAMO_TABLE! }), role: 'sync-peer' }],`,
356
356
  ` user: process.env.NOYDB_USER!,`,
357
357
  ` secret: process.env.NOYDB_SECRET,`,
@@ -370,10 +370,10 @@ NOYDB_DYNAMO_TABLE=collab-live
370
370
  notes: "USB-portable \u2014 everything on a single file store, no cloud.",
371
371
  code: [
372
372
  `import { createNoydb } from '@noy-db/hub'`,
373
- `import { jsonFile } from '@noy-db/to-file'`,
373
+ `import { toFile } from '@noy-db/to-file'`,
374
374
  ``,
375
375
  `export default {`,
376
- ` store: jsonFile({ dir: process.env.NOYDB_DATA_DIR ?? '/Volumes/MY_USB/data' }),`,
376
+ ` store: toFile({ dir: process.env.NOYDB_DATA_DIR ?? '/Volumes/MY_USB/data' }),`,
377
377
  ` user: process.env.NOYDB_USER!,`,
378
378
  ` secret: process.env.NOYDB_SECRET,`,
379
379
  `}`
@@ -412,12 +412,12 @@ NOYDB_REGION=ap-southeast-1
412
412
  notes: "Authentication bridge (secret-less unlock via OIDC or WebAuthn).",
413
413
  code: [
414
414
  `import { createNoydb } from '@noy-db/hub'`,
415
- `import { browserIdbStore } from '@noy-db/to-browser-idb'`,
415
+ `import { toBrowserIdb } from '@noy-db/to-browser-idb'`,
416
416
  `// import { unlockWebAuthn } from '@noy-db/on-webauthn' // in the browser`,
417
417
  `// import { keyConnector } from '@noy-db/on-oidc' // in a server app`,
418
418
  ``,
419
419
  `export default {`,
420
- ` store: browserIdbStore({ prefix: process.env.NOYDB_APP ?? 'app' }),`,
420
+ ` store: toBrowserIdb({ prefix: process.env.NOYDB_APP ?? 'app' }),`,
421
421
  ` user: process.env.NOYDB_USER!,`,
422
422
  ` // secret supplied by the unlock method at openVault() time, not here.`,
423
423
  `}`
@@ -539,7 +539,7 @@ async function runMonitor(argv) {
539
539
  process.stderr.write("config has no `store` \u2014 nothing to monitor\n");
540
540
  return 1;
541
541
  }
542
- const { store: metered, meter } = toMeter(innerStore, {
542
+ const metered = toMeter(innerStore, {
543
543
  degradedMs: 500,
544
544
  onDegraded: (e) => process.stderr.write(`DEGRADED: ${e.reason}
545
545
  `),
@@ -552,16 +552,16 @@ async function runMonitor(argv) {
552
552
  process.stdout.write(`monitoring ${file} \u2014 interval ${intervalMs}ms \u2014 Ctrl-C to stop
553
553
 
554
554
  `);
555
- const stop = installSigintHandler(meter);
555
+ const stop = installSigintHandler(metered.meter);
556
556
  return new Promise((resolveP) => {
557
557
  const timer = setInterval(() => {
558
558
  if (stop.signalled) {
559
559
  clearInterval(timer);
560
- meter.close();
560
+ metered.meter.close();
561
561
  resolveP(0);
562
562
  return;
563
563
  }
564
- const snap = meter.snapshot();
564
+ const snap = metered.meter.snapshot();
565
565
  process.stdout.write(formatSnapshot(snap) + "\n");
566
566
  }, intervalMs);
567
567
  });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/commands/inspect.ts","../src/commands/verify.ts","../src/commands/config.ts","../src/commands/monitor.ts","../src/commands/describe.ts"],"sourcesContent":["/**\n * `noydb 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 { readNoydbBundleHeader } 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 = readNoydbBundleHeader(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: noydb 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 * `noydb 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 { readNoydbBundle, readNoydbBundleHeader } 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 = readNoydbBundleHeader(bytes)\n checks.magic = true\n checks.header = true\n handle = header.handle\n bodyBytes = header.bodyBytes\n\n // readNoydbBundle() verifies bodySha256 internally — if it doesn't\n // throw, the body hash matched.\n await readNoydbBundle(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: noydb 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 * `noydb config validate` and `noydb 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 from `docs/guides/topology-matrix.md`. 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 noydb) 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} && noydb config validate ${filePath.replace(/\\.[mc]?ts$/, '.js')}\\n` +\n ` (b) run via tsx: npx tsx $(which noydb) 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: noydb 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/** Profiles match `docs/guides/topology-matrix.md` § View 3. */\nexport type Profile = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J'\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 { jsonFile } from '@noy-db/to-file'`,\n ``,\n `export default {`,\n ` store: jsonFile({ 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 { browserIdbStore } from '@noy-db/to-browser-idb'`,\n `import { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n ``,\n `export default {`,\n ` store: browserIdbStore({ prefix: process.env.NOYDB_APP ?? 'myapp' }),`,\n ` sync: [{`,\n ` store: awsDynamoStore({`,\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 { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n `import { awsS3Store } from '@noy-db/to-aws-s3'`,\n ``,\n `export default {`,\n ` store: routeStore({`,\n ` default: awsDynamoStore({`,\n ` table: process.env.NOYDB_DYNAMO_TABLE!,`,\n ` region: process.env.AWS_REGION ?? 'us-east-1',`,\n ` }),`,\n ` blobs: awsS3Store({`,\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 { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n ``,\n `export default {`,\n ` store: wrapStore(`,\n ` awsDynamoStore({ 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 { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n `import { awsS3Store } from '@noy-db/to-aws-s3'`,\n ``,\n `export default {`,\n ` store: routeStore({`,\n ` default: awsDynamoStore({ table: process.env.NOYDB_DYNAMO_TABLE! }),`,\n ` age: {`,\n ` cold: awsS3Store({ 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 { browserIdbStore } from '@noy-db/to-browser-idb'`,\n `import { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n `import { awsS3Store } from '@noy-db/to-aws-s3'`,\n ``,\n `export default {`,\n ` store: browserIdbStore({ prefix: process.env.NOYDB_APP ?? 'team' }),`,\n ` sync: [`,\n ` { store: awsDynamoStore({ table: process.env.NOYDB_DYNAMO_TABLE! }), role: 'sync-peer', label: 'team-hot' },`,\n ` { store: awsS3Store({ bucket: process.env.NOYDB_S3_BACKUP! }), role: 'backup', label: 'team-backup' },`,\n ` { store: awsS3Store({ 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 { browserIdbStore } from '@noy-db/to-browser-idb'`,\n `import { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n `// import { yjsCollection } from '@noy-db/in-yjs' // use inside your app`,\n ``,\n `export default {`,\n ` store: browserIdbStore({ prefix: 'collab' }),`,\n ` sync: [{ store: awsDynamoStore({ 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 { jsonFile } from '@noy-db/to-file'`,\n ``,\n `export default {`,\n ` store: jsonFile({ 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 { awsDynamoStore } 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: awsDynamoStore({ 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 { browserIdbStore } from '@noy-db/to-browser-idb'`,\n `// import { unlockWebAuthn } from '@noy-db/on-webauthn' // in the browser`,\n `// import { keyConnector } from '@noy-db/on-oidc' // in a server app`,\n ``,\n `export default {`,\n ` store: browserIdbStore({ 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\nexport async function runConfigScaffold(argv: readonly string[]): Promise<number> {\n const profileArg = argv.find((a) => a.startsWith('--profile='))\n const profile = (profileArg?.split('=')[1] ?? 'A') as Profile\n if (!/^[A-J]$/.test(profile)) {\n process.stderr.write(`unknown profile: ${profile}. Valid: A-J (see docs/guides/topology-matrix.md)\\n`)\n return 2\n }\n const out = scaffold(profile)\n // The config goes to stdout so `noydb 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(`// ── noydb 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 * `noydb monitor <config.ts>` — live text dashboard of store metrics.\n *\n * Loads a `NoydbOptions` from a config file, wraps the primary store\n * in `@noy-db/to-meter`, creates a `Noydb` instance, and prints a\n * refreshing snapshot to stdout at a configurable interval. Ctrl-C\n * to stop.\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 { toMeter } from '@noy-db/to-meter'\nimport type { MeterSnapshot } from '@noy-db/to-meter'\nimport { loadOptionsFromFile } from './config.js'\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: noydb 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 const { store: metered, meter } = toMeter(innerStore as never, {\n degradedMs: 500,\n onDegraded: (e) => process.stderr.write(`DEGRADED: ${e.reason}\\n`),\n onRestored: (e) => process.stderr.write(`RESTORED: ${e.reason}\\n`),\n })\n\n // Replace the store in the options object so the Noydb instance\n // goes through the meter.\n const liveOpts = { ...opts, store: metered } as Record<string, unknown>\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(liveOpts)\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: MeterSnapshot): 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 * `noydb 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 `docs/superpowers/specs/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 {\n EncryptedEnvelope,\n NoydbStore,\n VaultSnapshot,\n VaultSchemaSnapshot,\n} from '@noy-db/hub'\nimport {\n createNoydb,\n readNoydbBundle,\n ConflictError,\n loadPersistedSchema,\n SCHEMAS_COLLECTION,\n} from '@noy-db/hub'\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 readNoydbBundle(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: 1,\n _provenance: {\n generatedBy: `noydb 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 ...(rest.aclRoles !== undefined ? { aclRoles: rest.aclRoles } : {}),\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<CryptoKey> } },\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<CryptoKey> } },\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: noydb 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// Re-export for tests\nexport { loadPersistedSchema, SCHEMAS_COLLECTION }\n"],"mappings":"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","names":["readNoydbBundleHeader","readFile","readFile","readNoydbBundle"]}
1
+ {"version":3,"sources":["../src/commands/inspect.ts","../src/commands/verify.ts","../src/commands/config.ts","../src/commands/monitor.ts","../src/commands/describe.ts"],"sourcesContent":["/**\n * `noydb 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 { readNoydbBundleHeader } 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 = readNoydbBundleHeader(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: noydb 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 * `noydb 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 { readNoydbBundle, readNoydbBundleHeader } 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 = readNoydbBundleHeader(bytes)\n checks.magic = true\n checks.header = true\n handle = header.handle\n bodyBytes = header.bodyBytes\n\n // readNoydbBundle() verifies bodySha256 internally — if it doesn't\n // throw, the body hash matched.\n await readNoydbBundle(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: noydb 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 * `noydb config validate` and `noydb 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 from `docs/guides/topology-matrix.md`. 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 noydb) 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} && noydb config validate ${filePath.replace(/\\.[mc]?ts$/, '.js')}\\n` +\n ` (b) run via tsx: npx tsx $(which noydb) 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: noydb 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/** Profiles match `docs/guides/topology-matrix.md` § View 3. */\nexport type Profile = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J'\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 { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n ``,\n `export default {`,\n ` store: toBrowserIdb({ prefix: process.env.NOYDB_APP ?? 'myapp' }),`,\n ` sync: [{`,\n ` store: awsDynamoStore({`,\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 { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n `import { awsS3Store } from '@noy-db/to-aws-s3'`,\n ``,\n `export default {`,\n ` store: routeStore({`,\n ` default: awsDynamoStore({`,\n ` table: process.env.NOYDB_DYNAMO_TABLE!,`,\n ` region: process.env.AWS_REGION ?? 'us-east-1',`,\n ` }),`,\n ` blobs: awsS3Store({`,\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 { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n ``,\n `export default {`,\n ` store: wrapStore(`,\n ` awsDynamoStore({ 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 { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n `import { awsS3Store } from '@noy-db/to-aws-s3'`,\n ``,\n `export default {`,\n ` store: routeStore({`,\n ` default: awsDynamoStore({ table: process.env.NOYDB_DYNAMO_TABLE! }),`,\n ` age: {`,\n ` cold: awsS3Store({ 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 { awsDynamoStore } from '@noy-db/to-aws-dynamo'`,\n `import { awsS3Store } from '@noy-db/to-aws-s3'`,\n ``,\n `export default {`,\n ` store: toBrowserIdb({ prefix: process.env.NOYDB_APP ?? 'team' }),`,\n ` sync: [`,\n ` { store: awsDynamoStore({ table: process.env.NOYDB_DYNAMO_TABLE! }), role: 'sync-peer', label: 'team-hot' },`,\n ` { store: awsS3Store({ bucket: process.env.NOYDB_S3_BACKUP! }), role: 'backup', label: 'team-backup' },`,\n ` { store: awsS3Store({ 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 { awsDynamoStore } 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: awsDynamoStore({ 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 { awsDynamoStore } 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: awsDynamoStore({ 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 { keyConnector } 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\nexport async function runConfigScaffold(argv: readonly string[]): Promise<number> {\n const profileArg = argv.find((a) => a.startsWith('--profile='))\n const profile = (profileArg?.split('=')[1] ?? 'A') as Profile\n if (!/^[A-J]$/.test(profile)) {\n process.stderr.write(`unknown profile: ${profile}. Valid: A-J (see docs/guides/topology-matrix.md)\\n`)\n return 2\n }\n const out = scaffold(profile)\n // The config goes to stdout so `noydb 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(`// ── noydb 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 * `noydb monitor <config.ts>` — live text dashboard of store metrics.\n *\n * Loads a `NoydbOptions` from a config file, wraps the primary store\n * in `@noy-db/to-meter`, creates a `Noydb` instance, and prints a\n * refreshing snapshot to stdout at a configurable interval. Ctrl-C\n * to stop.\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 { toMeter } from '@noy-db/to-meter'\nimport type { MeterSnapshot } from '@noy-db/to-meter'\nimport { loadOptionsFromFile } from './config.js'\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: noydb 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 now returns the store itself, with the handle attached.\n const metered = toMeter(innerStore as never, {\n degradedMs: 500,\n onDegraded: (e) => process.stderr.write(`DEGRADED: ${e.reason}\\n`),\n onRestored: (e) => process.stderr.write(`RESTORED: ${e.reason}\\n`),\n })\n\n // Replace the store in the options object so the Noydb instance\n // goes through the meter.\n const liveOpts = { ...opts, store: metered } as Record<string, unknown>\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(liveOpts)\n\n process.stdout.write(`monitoring ${file} — interval ${intervalMs}ms — Ctrl-C to stop\\n\\n`)\n\n const stop = installSigintHandler(metered.meter)\n\n return new Promise<number>((resolveP) => {\n const timer = setInterval(() => {\n if (stop.signalled) {\n clearInterval(timer)\n metered.meter.close()\n resolveP(0)\n return\n }\n const snap = metered.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: MeterSnapshot): 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 * `noydb 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 `docs/superpowers/specs/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 {\n EncryptedEnvelope,\n NoydbStore,\n VaultSnapshot,\n VaultSchemaSnapshot,\n} from '@noy-db/hub'\nimport {\n createNoydb,\n readNoydbBundle,\n ConflictError,\n loadPersistedSchema,\n SCHEMAS_COLLECTION,\n} from '@noy-db/hub'\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 readNoydbBundle(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: 1,\n _provenance: {\n generatedBy: `noydb 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 ...(rest.aclRoles !== undefined ? { aclRoles: rest.aclRoles } : {}),\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<CryptoKey> } },\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<CryptoKey> } },\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: noydb 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// Re-export for tests\nexport { loadPersistedSchema, SCHEMAS_COLLECTION }\n"],"mappings":"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","names":["readNoydbBundleHeader","readFile","readFile","readNoydbBundle"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noy-db/cli",
3
- "version": "0.4.0-pre.7",
3
+ "version": "0.4.0-pre.8",
4
4
  "description": "Command-line tool for noy-db — inspect .noydb bundles without decrypting, verify integrity, validate config objects, scaffold topology profiles, and monitor a live vault's store metrics.",
5
5
  "license": "MIT",
6
6
  "author": "vLannaAi <vicio@lanna.ai>",
@@ -36,18 +36,16 @@
36
36
  },
37
37
  "peerDependencies": {
38
38
  "yaml": "^2.9.0",
39
- "@noy-db/to-meter": "0.4.0-pre.7",
40
- "@noy-db/hub": "0.4.0-pre.7",
41
- "@noy-db/to-probe": "0.4.0-pre.7"
39
+ "@noy-db/hub": "0.4.0-pre.8",
40
+ "@noy-db/to-meter": "0.4.0-pre.8"
42
41
  },
43
42
  "devDependencies": {
44
43
  "@types/node": "^22.0.0",
45
44
  "yaml": "^2.9.0",
46
45
  "zod": "^4.0.0",
47
46
  "zod-to-json-schema": "^3.25.2",
48
- "@noy-db/to-meter": "0.4.0-pre.7",
49
- "@noy-db/to-probe": "0.4.0-pre.7",
50
- "@noy-db/hub": "0.4.0-pre.7"
47
+ "@noy-db/hub": "0.4.0-pre.8",
48
+ "@noy-db/to-meter": "0.4.0-pre.8"
51
49
  },
52
50
  "keywords": [
53
51
  "noy-db",