@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 +17 -17
- package/dist/bin/noydb.js.map +1 -1
- package/dist/index.js +16 -16
- package/dist/index.js.map +1 -1
- package/package.json +5 -7
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 {
|
|
187
|
+
`import { toFile } from '@noy-db/to-file'`,
|
|
188
188
|
``,
|
|
189
189
|
`export default {`,
|
|
190
|
-
` store:
|
|
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 {
|
|
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:
|
|
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 {
|
|
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:
|
|
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 {
|
|
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:
|
|
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 {
|
|
375
|
+
`import { toFile } from '@noy-db/to-file'`,
|
|
376
376
|
``,
|
|
377
377
|
`export default {`,
|
|
378
|
-
` store:
|
|
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 {
|
|
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:
|
|
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
|
|
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.
|
|
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",
|
package/dist/bin/noydb.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","../../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 {
|
|
185
|
+
`import { toFile } from '@noy-db/to-file'`,
|
|
186
186
|
``,
|
|
187
187
|
`export default {`,
|
|
188
|
-
` store:
|
|
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 {
|
|
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:
|
|
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 {
|
|
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:
|
|
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 {
|
|
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:
|
|
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 {
|
|
373
|
+
`import { toFile } from '@noy-db/to-file'`,
|
|
374
374
|
``,
|
|
375
375
|
`export default {`,
|
|
376
|
-
` store:
|
|
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 {
|
|
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:
|
|
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
|
|
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.
|
|
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/
|
|
40
|
-
"@noy-db/
|
|
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/
|
|
49
|
-
"@noy-db/to-
|
|
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",
|