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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -26,7 +26,7 @@ yarn create noy-db my-app
26
26
  bun create noy-db my-app
27
27
  ```
28
28
 
29
- The wizard asks at most 5 questions — project name, adapter, sample data, starter template, and sync adapter — then writes a complete starter into `./my-app/`. Nothing is installed automatically: pick your package manager and run it yourself (the wizard closes by printing `cd <project>`, `pnpm install`, `pnpm dev`).
29
+ The wizard asks at most 4 questions — project name, adapter, sample data, and starter template — then writes a complete starter into `./my-app/`. Nothing is installed automatically: pick your package manager and run it yourself (the wizard closes by printing `cd <project>`, `pnpm install`, `pnpm dev`).
30
30
 
31
31
  Four starter templates ship in the package:
32
32
 
@@ -34,7 +34,8 @@ Four starter templates ship in the package:
34
34
  |---|---|---|
35
35
  | `nuxt-default` | Nuxt 4 + Pinia | SSR + file-based routing — **the default** |
36
36
  | `vite-vue` | Vite + Vue 3 + Pinia | Client-side SPA, shorter install graph |
37
- | `vanilla` | Vite + TypeScript | No framework, smallest footprint |
37
+ | `minimal` | Vite + TypeScript, one file | **Default.** hub + browser storage, put/get; add more with `noy-db add` |
38
+ | `vanilla` | Vite + TypeScript | No framework, an invoice-table UI |
38
39
  | `electron` | Electron + Vue 3 | Desktop app with the `to-file` adapter — USB workflow |
39
40
 
40
41
  Skip the prompts with `--yes`:
@@ -120,9 +121,9 @@ Validation errors and stack traces stay in English regardless of language so bug
120
121
 
121
122
  ---
122
123
 
123
- ## `noy-db` — the CLI tool
124
+ ## `noy-db` — the one command line
124
125
 
125
- The `noy-db` bin ships inside the same `create-noy-db` package. Install it as a dev dependency and it's available via `pnpm exec` / `npx`:
126
+ The `noy-db` bin ships inside the same `create-noy-db` package; it is the only CLI you need after `npm create noy-db` (the former `@noy-db/cli` is a redirect to it). Install it as a dev dependency and it's available via `pnpm exec` / `npx`:
126
127
 
127
128
  ```bash
128
129
  pnpm add -D create-noy-db
@@ -134,16 +135,26 @@ pnpm exec noy-db <command>
134
135
  | Command | Purpose |
135
136
  |---|---|
136
137
  | `noy-db add <collection>` | Scaffold a new Pinia store + Vue page for a collection |
137
- | `noy-db add user <id> <role>` | Grant a new user access to a compartment |
138
- | `noy-db verify` | In-memory crypto round-trip integrity check |
139
- | `noy-db rotate` | Rotate DEKs for one or more collections |
140
- | `noy-db backup <target>` | Dump a compartment to an encrypted file |
141
-
142
- All commands that touch real compartments use the **file adapter** and require these flags:
138
+ | `noy-db user add <id> <role>` | Grant a new user access to a vault |
139
+ | `noy-db user rotate` | Rotate DEKs for one or more collections |
140
+ | `noy-db backup <target>` | Dump a vault to an encrypted file |
141
+ | `noy-db doctor crypto` | In-memory crypto round-trip check |
142
+ | `noy-db pod inspect <file.noydb>` | Print a pod's header — no secret needed |
143
+ | `noy-db pod verify <file.noydb>` | Verify a pod's integrity — no secret needed |
144
+ | `noy-db describe <file.noydb>` | YAML/JSON schema audit of a pod |
145
+ | `noy-db config validate <file>` | Sanity-check a `NoydbOptions` file |
146
+ | `noy-db config scaffold --profile=<id>` | Topology skeleton to stdout, `.env` template to stderr |
147
+ | `noy-db config profiles` | List the ten topology profiles |
148
+ | `noy-db monitor <file>` | Live store metrics (the config's store must be metered) |
149
+ | `noy-db inspect <file> --vault=<v>` | Terminal inspector — uses `@noy-db/in-devtools-tui` when installed |
150
+
151
+ The old spellings `add user`, `rotate` and `verify` still work and print the new one.
152
+
153
+ Commands that touch a real vault use the **file adapter** and take these flags:
143
154
 
144
155
  - `--dir <path>` — the data directory. Defaults to `./data`.
145
- - `--compartment <name>` — the compartment (tenant) name. Required.
146
- - `--user <id>` — your own user id in the compartment. Required.
156
+ - `--vault <name>` — the vault (tenant) name. Required.
157
+ - `--user <id>` — your own user id in the vault. Required.
147
158
 
148
159
  You'll be prompted for your secret at runtime. Secrets are never echoed, never logged, never written to disk, and cleared from process memory when the command exits.
149
160
 
@@ -160,14 +171,14 @@ pnpm exec noy-db add clients
160
171
 
161
172
  Refuses to overwrite existing files — if either target already exists, the command exits non-zero without touching anything. There's no `--force` flag; delete the old files manually if you want to regenerate.
162
173
 
163
- ### `noy-db add user <userId> <role> [options]`
174
+ ### `noy-db user add <userId> <role> [options]`
164
175
 
165
- Grants a new user access to a compartment. Two secret prompts: yours (caller), then the new user's (with confirmation).
176
+ Grants a new user access to a vault. Two secret prompts: yours (caller), then the new user's (with confirmation).
166
177
 
167
178
  ```bash
168
- pnpm exec noy-db add user accountant-ann operator \
179
+ pnpm exec noy-db user add accountant-ann operator \
169
180
  --dir ./data \
170
- --compartment demo-co \
181
+ --vault demo-co \
171
182
  --user owner-alice \
172
183
  --collections invoices:rw,clients:ro
173
184
  ```
@@ -184,27 +195,27 @@ Roles:
184
195
 
185
196
  For `operator` and `client`, the `--collections` flag is required. Format: `name1:rw,name2:ro,name3:rw`.
186
197
 
187
- ### `noy-db verify`
198
+ ### `noy-db doctor crypto`
188
199
 
189
200
  Runs an end-to-end crypto round-trip against an in-memory adapter. No real data is touched; the command creates a throwaway compartment, writes a record, reads it back, and verifies it decrypts correctly. Useful as a sanity check that `@noy-db/hub` and your local Node version agree on Web Crypto.
190
201
 
191
202
  ```bash
192
- pnpm exec noy-db verify
203
+ pnpm exec noy-db doctor crypto
193
204
  # ✔ noy-db integrity check passed (126ms)
194
205
  ```
195
206
 
196
207
  Exits non-zero if anything diverges.
197
208
 
198
- ### `noy-db rotate [options]`
209
+ ### `noy-db user rotate [options]`
199
210
 
200
- Rotate the DEKs for one or more collections in a compartment. Generates fresh keys, re-encrypts every record with the new keys, and re-wraps the new keys into every user's keyring. Nobody is revoked — everyone keeps their current permissions with fresh key material.
211
+ Rotate the DEKs for one or more collections in a vault. Generates fresh keys, re-encrypts every record with the new keys, and re-wraps the new keys into every user's keyring. Nobody is revoked — everyone keeps their current permissions with fresh key material.
201
212
 
202
213
  ```bash
203
214
  # Rotate every collection in the compartment
204
- pnpm exec noy-db rotate --dir ./data --compartment demo-co --user owner-alice
215
+ pnpm exec noy-db user rotate --dir ./data --vault demo-co --user owner-alice
205
216
 
206
217
  # Rotate specific collections only
207
- pnpm exec noy-db rotate --dir ./data --compartment demo-co --user owner-alice \
218
+ pnpm exec noy-db user rotate --dir ./data --vault demo-co --user owner-alice \
208
219
  --collections invoices,clients
209
220
  ```
210
221
 
@@ -221,7 +232,7 @@ Dump a compartment to a local file. The dump is a **verifiable backup**: it incl
221
232
 
222
233
  ```bash
223
234
  pnpm exec noy-db backup ./backups/demo-2026-04-07.json \
224
- --dir ./data --compartment demo-co --user owner-alice
235
+ --dir ./data --vault demo-co --user owner-alice
225
236
  ```
226
237
 
227
238
  Target paths:
@@ -14,7 +14,12 @@ import pc from "picocolors";
14
14
  import { promises as fs } from "fs";
15
15
  import path from "path";
16
16
  import { fileURLToPath } from "url";
17
- var NOYDB_IN_VERSION = "0.8.0";
17
+ var NOYDB_IN_VERSION = "0.10.0-pre.1";
18
+ var ADAPTER_PACKAGES = {
19
+ browser: "@noy-db/to-browser-idb",
20
+ file: "@noy-db/to-file",
21
+ memory: "@noy-db/hub"
22
+ };
18
23
  async function renderTemplate(src, dest, tokens) {
19
24
  const written = [];
20
25
  await walk(src, dest, "", tokens, written);
@@ -247,7 +252,7 @@ function isProxyArray(value) {
247
252
 
248
253
  // src/wizard/i18n/en.ts
249
254
  var en = {
250
- wizardIntro: "A wizard for noy-db \u2014 None Of Your DataBase.\nGenerates a fresh Nuxt 4 + Pinia + encrypted-store starter.",
255
+ wizardIntro: "A wizard for noy-db \u2014 None Of Your DataBase.\nGenerates a fresh encrypted-store starter \u2014 minimal by default, Nuxt and more on request.",
251
256
  promptProjectName: "Project name",
252
257
  promptProjectNamePlaceholder: "my-noy-db-app",
253
258
  promptAdapter: "Storage adapter",
@@ -255,6 +260,7 @@ var en = {
255
260
  adapterFileLabel: "file \u2014 JSON files on disk (Electron / Tauri / USB workflows)",
256
261
  adapterMemoryLabel: "memory \u2014 no persistence (ideal for tests and demos)",
257
262
  promptSampleData: "Include sample invoice records?",
263
+ promptRecommended: "Use the recommended setup? (hub + browser storage, no framework \u2014 add more later with `noy-db add`)",
258
264
  freshNextStepsTitle: "Next steps",
259
265
  freshOutroDone: "\u2714 Done \u2014 happy encrypting!",
260
266
  augmentModeTitle: "Augment mode",
@@ -285,6 +291,7 @@ var th = {
285
291
  adapterFileLabel: "file \u2014 \u0E44\u0E1F\u0E25\u0E4C JSON \u0E1A\u0E19\u0E14\u0E34\u0E2A\u0E01\u0E4C (Electron / Tauri / USB)",
286
292
  adapterMemoryLabel: "memory \u2014 \u0E44\u0E21\u0E48\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25 (\u0E40\u0E2B\u0E21\u0E32\u0E30\u0E2A\u0E33\u0E2B\u0E23\u0E31\u0E1A\u0E01\u0E32\u0E23\u0E17\u0E14\u0E2A\u0E2D\u0E1A\u0E41\u0E25\u0E30\u0E15\u0E31\u0E27\u0E2D\u0E22\u0E48\u0E32\u0E07)",
287
293
  promptSampleData: "\u0E40\u0E1E\u0E34\u0E48\u0E21\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25\u0E15\u0E31\u0E27\u0E2D\u0E22\u0E48\u0E32\u0E07\u0E43\u0E1A\u0E41\u0E08\u0E49\u0E07\u0E2B\u0E19\u0E35\u0E49\u0E2B\u0E23\u0E37\u0E2D\u0E44\u0E21\u0E48?",
294
+ promptRecommended: "\u0E43\u0E0A\u0E49\u0E01\u0E32\u0E23\u0E15\u0E31\u0E49\u0E07\u0E04\u0E48\u0E32\u0E17\u0E35\u0E48\u0E41\u0E19\u0E30\u0E19\u0E33\u0E44\u0E2B\u0E21? (hub + \u0E1E\u0E37\u0E49\u0E19\u0E17\u0E35\u0E48\u0E40\u0E01\u0E47\u0E1A\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25\u0E43\u0E19\u0E40\u0E1A\u0E23\u0E32\u0E27\u0E4C\u0E40\u0E0B\u0E2D\u0E23\u0E4C \u0E44\u0E21\u0E48\u0E21\u0E35\u0E40\u0E1F\u0E23\u0E21\u0E40\u0E27\u0E34\u0E23\u0E4C\u0E01 \u2014 \u0E40\u0E1E\u0E34\u0E48\u0E21\u0E17\u0E35\u0E2B\u0E25\u0E31\u0E07\u0E44\u0E14\u0E49\u0E14\u0E49\u0E27\u0E22 `noy-db add`)",
288
295
  freshNextStepsTitle: "\u0E02\u0E31\u0E49\u0E19\u0E15\u0E2D\u0E19\u0E16\u0E31\u0E14\u0E44\u0E1B",
289
296
  freshOutroDone: "\u2714 \u0E40\u0E2A\u0E23\u0E47\u0E08\u0E40\u0E23\u0E35\u0E22\u0E1A\u0E23\u0E49\u0E2D\u0E22 \u2014 \u0E02\u0E2D\u0E43\u0E2B\u0E49\u0E2A\u0E19\u0E38\u0E01\u0E01\u0E31\u0E1A\u0E01\u0E32\u0E23\u0E40\u0E02\u0E49\u0E32\u0E23\u0E2B\u0E31\u0E2A!",
290
297
  augmentModeTitle: "\u0E42\u0E2B\u0E21\u0E14\u0E40\u0E2A\u0E23\u0E34\u0E21\u0E42\u0E1B\u0E23\u0E40\u0E08\u0E01\u0E15\u0E4C\u0E40\u0E14\u0E34\u0E21",
@@ -392,15 +399,18 @@ async function runWizard(options = {}) {
392
399
  }
393
400
  async function runFreshMode(options, cwd, yes, msg) {
394
401
  const projectName = yes ? options.projectName ?? "my-noy-db-app" : await promptProjectName(options.projectName, msg);
395
- const adapter = yes ? options.adapter ?? "browser" : await promptAdapter(options.adapter, msg);
396
- const sampleData = yes ? options.sampleData ?? true : await promptSampleData(options.sampleData, msg);
397
- const template = yes ? options.template ?? "nuxt-default" : await promptTemplate(options.template, msg);
398
- const syncAdapter = yes ? options.syncAdapter ?? "none" : await promptSyncAdapter(options.syncAdapter, adapter, msg);
402
+ const explicit = options.template !== void 0 || options.adapter !== void 0 || options.sampleData !== void 0;
403
+ const recommended = yes || explicit ? false : await promptRecommended(msg);
404
+ const template = recommended ? "minimal" : yes ? options.template ?? "minimal" : await promptTemplate(options.template, msg);
405
+ const adapter = recommended ? "browser" : yes ? options.adapter ?? "browser" : await promptAdapter(options.adapter, msg);
406
+ const sampleData = recommended || template === "minimal" ? options.sampleData ?? false : yes ? options.sampleData ?? true : await promptSampleData(options.sampleData, msg);
407
+ const syncAdapter = options.syncAdapter ?? "none";
399
408
  const projectPath = path3.resolve(cwd, projectName);
400
409
  await assertWritableTarget(projectPath);
401
410
  const tokens = {
402
411
  PROJECT_NAME: projectName,
403
412
  ADAPTER: adapter,
413
+ ADAPTER_PACKAGE: ADAPTER_PACKAGES[adapter],
404
414
  DEVTOOLS: "true",
405
415
  SEED_INVOICES: sampleData ? JSON.stringify(DEFAULT_SEED, null, 2).replace(/\n/g, "\n ") : "[]",
406
416
  NOYDB_VERSION: await ownVersion(),
@@ -412,16 +422,20 @@ async function runFreshMode(options, cwd, yes, msg) {
412
422
  projectPath,
413
423
  tokens
414
424
  );
425
+ const nextSteps = [
426
+ `${pc.bold("cd")} ${projectName}`,
427
+ `${pc.bold("pnpm install")} ${pc.dim("(or npm/yarn/bun)")}`,
428
+ `${pc.bold("pnpm dev")}`
429
+ ].join("\n");
415
430
  if (!yes) {
416
- p.note(
417
- [
418
- `${pc.bold("cd")} ${projectName}`,
419
- `${pc.bold("pnpm install")} ${pc.dim("(or npm/yarn/bun)")}`,
420
- `${pc.bold("pnpm dev")}`
421
- ].join("\n"),
422
- msg.freshNextStepsTitle
423
- );
431
+ p.note(nextSteps, msg.freshNextStepsTitle);
424
432
  p.outro(pc.green(msg.freshOutroDone));
433
+ } else {
434
+ process.stdout.write(`${msg.freshOutroDone}
435
+
436
+ ${msg.freshNextStepsTitle}
437
+ ${nextSteps}
438
+ `);
425
439
  }
426
440
  return {
427
441
  kind: "fresh",
@@ -591,22 +605,8 @@ async function promptAdapter(initial, msg) {
591
605
  }
592
606
  return result;
593
607
  }
594
- async function promptSyncAdapter(initial, primary, _msg) {
595
- if (initial !== void 0) return initial;
596
- const choices = ["none", "memory", "file", "browser"].filter(
597
- (x) => x !== primary
598
- );
599
- const labels = {
600
- none: "No sync \u2014 local only",
601
- memory: "In-memory (testing / CI)",
602
- file: "JSON files (USB / shared drive)",
603
- browser: "IndexedDB (other tabs / PWAs)"
604
- };
605
- const result = await p.select({
606
- message: "Add a sync target?",
607
- options: choices.map((value) => ({ value, label: labels[value] })),
608
- initialValue: "none"
609
- });
608
+ async function promptRecommended(msg) {
609
+ const result = await p.confirm({ message: msg.promptRecommended, initialValue: true });
610
610
  if (p.isCancel(result)) {
611
611
  process.exit(1);
612
612
  }
@@ -617,10 +617,15 @@ async function promptTemplate(initial, _msg) {
617
617
  const result = await p.select({
618
618
  message: "Pick a starter template",
619
619
  options: [
620
+ {
621
+ value: "minimal",
622
+ label: "Minimal \u2014 Vite + TypeScript, one file",
623
+ hint: "hub + browser storage, put/get (recommended)"
624
+ },
620
625
  {
621
626
  value: "nuxt-default",
622
627
  label: "Nuxt 4 + Pinia",
623
- hint: "SSR + file-based routing (recommended default)"
628
+ hint: "SSR + file-based routing"
624
629
  },
625
630
  {
626
631
  value: "vite-vue",
@@ -638,7 +643,7 @@ async function promptTemplate(initial, _msg) {
638
643
  hint: "Desktop app with to-file adapter \u2014 USB workflow"
639
644
  }
640
645
  ],
641
- initialValue: "nuxt-default"
646
+ initialValue: "minimal"
642
647
  });
643
648
  if (p.isCancel(result)) {
644
649
  process.exit(1);
@@ -673,6 +678,7 @@ async function assertWritableTarget(target) {
673
678
 
674
679
  // src/wizard/types.ts
675
680
  var WIZARD_TEMPLATES = [
681
+ "minimal",
676
682
  "nuxt-default",
677
683
  "vanilla",
678
684
  "vite-vue",
@@ -773,7 +779,7 @@ The wizard auto-detects whether cwd is an existing Nuxt 4 project:
773
779
  Options:
774
780
  -y, --yes Skip prompts; use defaults for missing values
775
781
  --adapter <name> Adapter: browser (default) | file | memory
776
- --template <name> Template: nuxt-default (default) | vite-vue | vanilla | electron
782
+ --template <name> Template: minimal (default) | nuxt-default | vite-vue | vanilla | electron
777
783
  --sync <name> Sync target (multi-backend):
778
784
  none (default) | memory | file | browser
779
785
  --no-sample-data (fresh mode) Skip the seed invoice records
@@ -788,6 +794,8 @@ Examples:
788
794
  # Fresh project in a new directory
789
795
  npm create noy-db my-app
790
796
  npm create noy-db my-app --yes --adapter file
797
+ npm create noy-db my-app --yes # minimal: hub + browser storage
798
+ npm create noy-db my-app --yes --template nuxt-default
791
799
  npm create noy-db my-app --yes --template vite-vue
792
800
  npm create noy-db my-app --yes --template vanilla
793
801