@nbt-dev/nbt 0.0.1 → 0.0.4

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.
Files changed (145) hide show
  1. package/LICENSE +177 -21
  2. package/README.md +16 -13
  3. package/TRADEMARKS.md +49 -0
  4. package/dist/nbt.js +398 -19
  5. package/package.json +5 -4
  6. package/stdlib/auth/README.md +83 -0
  7. package/stdlib/auth/migrations/20260424144652_initial/migration.nbt +48 -0
  8. package/stdlib/auth/migrations/20260424144652_initial/schema_snapshot.nbt +58 -0
  9. package/stdlib/auth/migrations/20260521191014_update_user/migration.nbt +3 -0
  10. package/stdlib/auth/migrations/20260521191014_update_user/schema_snapshot.nbt +59 -0
  11. package/stdlib/auth/schema.nbt +142 -0
  12. package/stdlib/calendar/adapters/gohighlevel/tests/fixtures/v2_calendar_pilot.json +12 -0
  13. package/stdlib/calendar/adapters/gohighlevel/tests/fixtures/webhooks/appointment_changed.json +70 -0
  14. package/stdlib/calendar/adapters/gohighlevel/tests/fixtures/webhooks/appointment_created.json +72 -0
  15. package/stdlib/calendar/migrations/20260501210107_initial/migration.nbt +60 -0
  16. package/stdlib/calendar/migrations/20260501210107_initial/schema_snapshot.nbt +66 -0
  17. package/stdlib/calendar/migrations/20260513151050_schema_update/migration.nbt +17 -0
  18. package/stdlib/calendar/migrations/20260513151050_schema_update/schema_snapshot.nbt +83 -0
  19. package/stdlib/calendar/schema.nbt +86 -0
  20. package/stdlib/chat/migrations/20260429222411_initial/migration.nbt +59 -0
  21. package/stdlib/chat/migrations/20260429222411_initial/schema_snapshot.nbt +71 -0
  22. package/stdlib/chat/migrations/20260430185225_add_messagereaction/migration.nbt +9 -0
  23. package/stdlib/chat/migrations/20260430185225_add_messagereaction/schema_snapshot.nbt +78 -0
  24. package/stdlib/chat/migrations/20260518191152_update_message/migration.nbt +3 -0
  25. package/stdlib/chat/migrations/20260518191152_update_message/schema_snapshot.nbt +81 -0
  26. package/stdlib/chat/schema.nbt +130 -0
  27. package/stdlib/crm/adapters/gohighlevel/README.md +85 -0
  28. package/stdlib/crm/adapters/gohighlevel/tests/README.md +159 -0
  29. package/stdlib/crm/adapters/gohighlevel/tests/fixtures/webhooks/contact_138fields.json +222 -0
  30. package/stdlib/crm/adapters/gohighlevel/tests/fixtures/webhooks/contact_140fields.json +219 -0
  31. package/stdlib/crm/adapters/gohighlevel/tests/fixtures/webhooks/contact_alt.json +212 -0
  32. package/stdlib/crm/adapters/gohighlevel/tests/fixtures/webhooks/contact_changed.json +102 -0
  33. package/stdlib/crm/adapters/gohighlevel/tests/fixtures/webhooks/contact_created.json +95 -0
  34. package/stdlib/crm/adapters/gohighlevel/tests/fixtures/webhooks/contact_full.json +213 -0
  35. package/stdlib/crm/adapters/gohighlevel/tests/fixtures/webhooks/contact_sparse.json +161 -0
  36. package/stdlib/crm/adapters/gohighlevel/tests/fixtures/webhooks/contact_update_a.json +197 -0
  37. package/stdlib/crm/adapters/gohighlevel/tests/fixtures/webhooks/contact_update_b.json +197 -0
  38. package/stdlib/crm/adapters/gohighlevel/tests/fixtures/webhooks/opportunity_changed.json +85 -0
  39. package/stdlib/crm/adapters/gohighlevel/tests/fixtures/webhooks/opportunity_created.json +85 -0
  40. package/stdlib/crm/adapters/gohighlevel/tests/fixtures/webhooks/v2_contact_pilot.json +43 -0
  41. package/stdlib/crm/adapters/gohighlevel/tests/fixtures/webhooks/v2_contact_with_price_closed.json +7 -0
  42. package/stdlib/crm/adapters/gohighlevel/tests/fixtures/webhooks/v2_contact_with_price_open.json +7 -0
  43. package/stdlib/crm/adapters/gohighlevel/tests/fixtures/webhooks/v2_event_appointment_delete.json +1 -0
  44. package/stdlib/crm/adapters/gohighlevel/tests/fixtures/webhooks/v2_event_calendar_update.json +1 -0
  45. package/stdlib/crm/adapters/gohighlevel/tests/fixtures/webhooks/v2_event_contact_create.json +1 -0
  46. package/stdlib/crm/adapters/gohighlevel/tests/fixtures/webhooks/v2_event_opp_status_update.json +1 -0
  47. package/stdlib/crm/adapters/gohighlevel/tests/fixtures/webhooks/v2_opportunity_pilot.json +16 -0
  48. package/stdlib/crm/adapters/gohighlevel/tests/fixtures/webhooks/v2_pipelines_pilot.json +137 -0
  49. package/stdlib/crm/migrations/20260501210107_initial/migration.nbt +63 -0
  50. package/stdlib/crm/migrations/20260501210107_initial/schema_snapshot.nbt +73 -0
  51. package/stdlib/crm/migrations/20260513151050_schema_update/migration.nbt +13 -0
  52. package/stdlib/crm/migrations/20260513151050_schema_update/schema_snapshot.nbt +86 -0
  53. package/stdlib/crm/schema.nbt +148 -0
  54. package/stdlib/design/migrations/20260501210107_initial/migration.nbt +19 -0
  55. package/stdlib/design/migrations/20260501210107_initial/schema_snapshot.nbt +21 -0
  56. package/stdlib/design/migrations/20260610130000_design_system/migration.nbt +50 -0
  57. package/stdlib/design/migrations/20260610130000_design_system/schema_snapshot.nbt +80 -0
  58. package/stdlib/design/schema.nbt +140 -0
  59. package/stdlib/dns/migrations/20260501210107_initial/migration.nbt +32 -0
  60. package/stdlib/dns/migrations/20260501210107_initial/schema_snapshot.nbt +36 -0
  61. package/stdlib/dns/schema.nbt +68 -0
  62. package/stdlib/email/migrations/20260427235207_initial/migration.nbt +75 -0
  63. package/stdlib/email/migrations/20260427235207_initial/schema_snapshot.nbt +87 -0
  64. package/stdlib/email/schema.nbt +145 -0
  65. package/stdlib/ingest/README.md +29 -0
  66. package/stdlib/ingest/migrations/20260424144652_initial/migration.nbt +18 -0
  67. package/stdlib/ingest/migrations/20260424144652_initial/schema_snapshot.nbt +20 -0
  68. package/stdlib/ingest/migrations/20260429203747_schema_update/migration.nbt +3 -0
  69. package/stdlib/ingest/migrations/20260429203747_schema_update/schema_snapshot.nbt +21 -0
  70. package/stdlib/ingest/schema.nbt +37 -0
  71. package/stdlib/notifications/README.md +118 -0
  72. package/stdlib/notifications/migrations/20260430204408_initial/migration.nbt +42 -0
  73. package/stdlib/notifications/migrations/20260430204408_initial/schema_snapshot.nbt +46 -0
  74. package/stdlib/notifications/schema.nbt +67 -0
  75. package/stdlib/phone/migrations/20260605205722_initial/migration.nbt +50 -0
  76. package/stdlib/phone/migrations/20260605205722_initial/schema_snapshot.nbt +56 -0
  77. package/stdlib/phone/schema.nbt +95 -0
  78. package/stdlib/registry/migrations/20260602181932_initial/migration.nbt +8 -0
  79. package/stdlib/registry/migrations/20260602181932_initial/schema_snapshot.nbt +8 -0
  80. package/stdlib/registry/schema.nbt +20 -0
  81. package/stdlib/workflows/schema.nbt +44 -0
  82. package/vendor/linux-x64/cartridges/auth/migrations/20260424144652_initial/migration.nbt +48 -0
  83. package/vendor/linux-x64/cartridges/auth/migrations/20260424144652_initial/schema_snapshot.nbt +58 -0
  84. package/vendor/linux-x64/cartridges/auth/migrations/20260521191014_update_user/migration.nbt +3 -0
  85. package/vendor/linux-x64/cartridges/auth/migrations/20260521191014_update_user/schema_snapshot.nbt +59 -0
  86. package/vendor/linux-x64/cartridges/auth/schema.nbt +142 -0
  87. package/vendor/linux-x64/cartridges/calendar/migrations/20260501210107_initial/migration.nbt +60 -0
  88. package/vendor/linux-x64/cartridges/calendar/migrations/20260501210107_initial/schema_snapshot.nbt +66 -0
  89. package/vendor/linux-x64/cartridges/calendar/migrations/20260513151050_schema_update/migration.nbt +17 -0
  90. package/vendor/linux-x64/cartridges/calendar/migrations/20260513151050_schema_update/schema_snapshot.nbt +83 -0
  91. package/vendor/linux-x64/cartridges/calendar/schema.nbt +86 -0
  92. package/vendor/linux-x64/cartridges/chat/migrations/20260429222411_initial/migration.nbt +59 -0
  93. package/vendor/linux-x64/cartridges/chat/migrations/20260429222411_initial/schema_snapshot.nbt +71 -0
  94. package/vendor/linux-x64/cartridges/chat/migrations/20260430185225_add_messagereaction/migration.nbt +9 -0
  95. package/vendor/linux-x64/cartridges/chat/migrations/20260430185225_add_messagereaction/schema_snapshot.nbt +78 -0
  96. package/vendor/linux-x64/cartridges/chat/migrations/20260518191152_update_message/migration.nbt +3 -0
  97. package/vendor/linux-x64/cartridges/chat/migrations/20260518191152_update_message/schema_snapshot.nbt +81 -0
  98. package/vendor/linux-x64/cartridges/chat/schema.nbt +130 -0
  99. package/vendor/linux-x64/cartridges/crm/migrations/20260501210107_initial/migration.nbt +63 -0
  100. package/vendor/linux-x64/cartridges/crm/migrations/20260501210107_initial/schema_snapshot.nbt +73 -0
  101. package/vendor/linux-x64/cartridges/crm/migrations/20260513151050_schema_update/migration.nbt +13 -0
  102. package/vendor/linux-x64/cartridges/crm/migrations/20260513151050_schema_update/schema_snapshot.nbt +86 -0
  103. package/vendor/linux-x64/cartridges/crm/schema.nbt +148 -0
  104. package/vendor/linux-x64/cartridges/design/migrations/20260501210107_initial/migration.nbt +19 -0
  105. package/vendor/linux-x64/cartridges/design/migrations/20260501210107_initial/schema_snapshot.nbt +21 -0
  106. package/vendor/linux-x64/cartridges/design/migrations/20260610130000_design_system/migration.nbt +50 -0
  107. package/vendor/linux-x64/cartridges/design/migrations/20260610130000_design_system/schema_snapshot.nbt +80 -0
  108. package/vendor/linux-x64/cartridges/design/schema.nbt +140 -0
  109. package/vendor/linux-x64/cartridges/dns/migrations/20260501210107_initial/migration.nbt +32 -0
  110. package/vendor/linux-x64/cartridges/dns/migrations/20260501210107_initial/schema_snapshot.nbt +36 -0
  111. package/vendor/linux-x64/cartridges/dns/schema.nbt +68 -0
  112. package/vendor/linux-x64/cartridges/email/migrations/20260427235207_initial/migration.nbt +75 -0
  113. package/vendor/linux-x64/cartridges/email/migrations/20260427235207_initial/schema_snapshot.nbt +87 -0
  114. package/vendor/linux-x64/cartridges/email/schema.nbt +145 -0
  115. package/vendor/linux-x64/cartridges/ingest/migrations/20260424144652_initial/migration.nbt +18 -0
  116. package/vendor/linux-x64/cartridges/ingest/migrations/20260424144652_initial/schema_snapshot.nbt +20 -0
  117. package/vendor/linux-x64/cartridges/ingest/migrations/20260429203747_schema_update/migration.nbt +3 -0
  118. package/vendor/linux-x64/cartridges/ingest/migrations/20260429203747_schema_update/schema_snapshot.nbt +21 -0
  119. package/vendor/linux-x64/cartridges/ingest/schema.nbt +37 -0
  120. package/vendor/linux-x64/cartridges/notifications/migrations/20260430204408_initial/migration.nbt +42 -0
  121. package/vendor/linux-x64/cartridges/notifications/migrations/20260430204408_initial/schema_snapshot.nbt +46 -0
  122. package/vendor/linux-x64/cartridges/notifications/schema.nbt +67 -0
  123. package/vendor/linux-x64/cartridges/phone/migrations/20260605205722_initial/migration.nbt +50 -0
  124. package/vendor/linux-x64/cartridges/phone/migrations/20260605205722_initial/schema_snapshot.nbt +56 -0
  125. package/vendor/linux-x64/cartridges/phone/schema.nbt +95 -0
  126. package/vendor/linux-x64/cartridges/registry/migrations/20260602181932_initial/migration.nbt +8 -0
  127. package/vendor/linux-x64/cartridges/registry/migrations/20260602181932_initial/schema_snapshot.nbt +8 -0
  128. package/vendor/linux-x64/cartridges/registry/schema.nbt +20 -0
  129. package/vendor/linux-x64/cartridges/workflows/schema.nbt +44 -0
  130. package/vendor/linux-x64/console +0 -0
  131. package/vendor/linux-x64/nbt +0 -0
  132. package/contracts/audit/.dist/contract.json +0 -56
  133. package/contracts/auth/.dist/contract.json +0 -252
  134. package/contracts/calendar/.dist/contract.json +0 -141
  135. package/contracts/chat/.dist/contract.json +0 -229
  136. package/contracts/crm/.dist/contract.json +0 -239
  137. package/contracts/design/.dist/contract.json +0 -85
  138. package/contracts/dns/.dist/contract.json +0 -123
  139. package/contracts/email/.dist/contract.json +0 -267
  140. package/contracts/embed/.dist/contract.json +0 -137
  141. package/contracts/ingest/.dist/contract.json +0 -86
  142. package/contracts/notifications/.dist/contract.json +0 -133
  143. package/contracts/phone/.dist/contract.json +0 -168
  144. package/contracts/registry/.dist/contract.json +0 -49
  145. package/contracts/workflows/.dist/contract.json +0 -106
package/dist/nbt.js CHANGED
@@ -3393,8 +3393,10 @@ var program = new Command();
3393
3393
 
3394
3394
  // src/nbt.ts
3395
3395
  var import_node_child_process2 = require("node:child_process");
3396
+ var net = __toESM(require("node:net"));
3396
3397
  var path3 = __toESM(require("node:path"));
3397
3398
  var fs2 = __toESM(require("node:fs"));
3399
+ var readline = __toESM(require("node:readline/promises"));
3398
3400
 
3399
3401
  // src/bundler.ts
3400
3402
  var import_esbuild = __toESM(require("esbuild"));
@@ -3423,9 +3425,11 @@ if (process.platform !== "linux" || process.arch !== "x64") {
3423
3425
  process.exit(1);
3424
3426
  }
3425
3427
  var VENDOR = path3.join(__dirname, "..", "vendor", "linux-x64");
3426
- var CONTRACTS = path3.join(__dirname, "..", "contracts");
3428
+ var STDLIB = path3.join(__dirname, "..", "stdlib");
3429
+ var NBT_BIN = path3.join(VENDOR, "nbt");
3430
+ var CONSOLE_BIN = path3.join(VENDOR, "console");
3427
3431
  function nativeEnv() {
3428
- return { ...process.env, NBT_PLATFORM_CONTRACTS: CONTRACTS };
3432
+ return { ...process.env };
3429
3433
  }
3430
3434
  function corsOriginsFromNbtJson() {
3431
3435
  let dir = process.cwd();
@@ -3456,17 +3460,10 @@ function consoleEnv() {
3456
3460
  return env;
3457
3461
  }
3458
3462
  function forwardToNative(argv2) {
3459
- let bin;
3460
- let args;
3461
- const isConsole = argv2[0] === "console";
3462
- if (isConsole) {
3463
- bin = path3.join(VENDOR, "console");
3464
- args = argv2.slice(1);
3465
- } else {
3466
- bin = path3.join(VENDOR, "nbt");
3467
- args = argv2;
3468
- }
3463
+ const isConsole = consoleCommands.has(argv2[0]);
3464
+ const bin = isConsole ? CONSOLE_BIN : NBT_BIN;
3469
3465
  const env = isConsole ? consoleEnv() : nativeEnv();
3466
+ const nativeArgv = argv2[0] === "up" ? withProjectUpDefaults(argv2) : argv2;
3470
3467
  if (!fs2.existsSync(bin)) {
3471
3468
  console.error(
3472
3469
  `@nbt-dev/nbt: bundled binary not found at ${bin} (broken install?)`
@@ -3478,9 +3475,9 @@ function forwardToNative(argv2) {
3478
3475
  } catch (_) {
3479
3476
  }
3480
3477
  if (typeof process.execve === "function") {
3481
- process.execve(bin, [bin, ...args], env);
3478
+ process.execve(bin, [bin, ...nativeArgv], env);
3482
3479
  }
3483
- const r = (0, import_node_child_process2.spawnSync)(bin, args, { stdio: "inherit", env });
3480
+ const r = (0, import_node_child_process2.spawnSync)(bin, nativeArgv, { stdio: "inherit", env });
3484
3481
  if (r.error) {
3485
3482
  console.error(`@nbt-dev/nbt: failed to exec ${bin}: ${r.error.message}`);
3486
3483
  process.exit(1);
@@ -3489,17 +3486,340 @@ function forwardToNative(argv2) {
3489
3486
  }
3490
3487
  var program2 = new Command();
3491
3488
  program2.name("nbt").description("nbt CLI + console daemon for the nbt-dev console (linux/x64).");
3492
- var NATIVE = [
3493
- ["init", "Scaffold a new project or cartridge"],
3489
+ var NBT = [
3494
3490
  ["install", "Install a cartridge on a live instance"],
3495
3491
  ["generate", "Generate a typed client from contracts"],
3496
- ["migrate", "Schema migration tooling (dev | deploy)"],
3497
- ["console", "Run / manage the console daemon"]
3492
+ ["migrate", "Create migrations and deploy local cartridges"],
3493
+ ["lsp", "Run the NBT language server over stdio (editors)"],
3494
+ ["editor", "Install the NBT VS Code extension (editor install)"]
3495
+ ];
3496
+ var CONSOLE = [
3497
+ ["up", "Boot the console daemon"],
3498
+ ["gen-cluster-key", "Generate a cluster signing key (copy to every node)"]
3498
3499
  ];
3499
- for (const [name, summary] of NATIVE) {
3500
+ var consoleCommands = new Set(CONSOLE.map(([n]) => n));
3501
+ for (const [name, summary] of [...NBT, ...CONSOLE]) {
3500
3502
  program2.command(name).description(`${summary} (native)`);
3501
3503
  }
3502
3504
  var customCommands = /* @__PURE__ */ new Set();
3505
+ customCommands.add("init");
3506
+ program2.command("init").description("Scaffold an nbt project (nbt.json, nbt/, generated/)").argument("[dir]", "directory to create the project in (default: current directory)").option("-y, --yes", "skip prompts; include the hello-world example").option("--no-example", "skip the hello-world example cartridge").action((dir, opts) => {
3507
+ runInit(dir, opts).catch((e) => {
3508
+ console.error(`nbt init: ${e?.message ?? e}`);
3509
+ process.exit(1);
3510
+ });
3511
+ });
3512
+ var NBT_JSON_TEMPLATE = `{
3513
+ "carts": "nbt",
3514
+ "generated": "generated",
3515
+ "environments": {
3516
+ "dev": { "host": "127.0.0.1", "port": 8080 }
3517
+ }
3518
+ }
3519
+ `;
3520
+ var HELLO_SCHEMA = `# A minimal entity. CRUD routes are generated automatically:
3521
+ # GET /api/hello/note list
3522
+ # POST /api/hello/note create
3523
+ # GET /api/hello/note/:id get
3524
+ # PUT /api/hello/note/:id update
3525
+ # DELETE /api/hello/note/:id delete
3526
+ entity Note {
3527
+ id: ulid
3528
+ createdAt: DateTime @default(now())
3529
+ updatedAt: DateTime @updatedAt
3530
+ title: string
3531
+ body: string
3532
+
3533
+ @@index([title])
3534
+ }
3535
+ `;
3536
+ async function runInit(dir, opts) {
3537
+ const root = dir ? path3.resolve(process.cwd(), dir) : process.cwd();
3538
+ if (dir) fs2.mkdirSync(root, { recursive: true });
3539
+ if (fs2.existsSync(path3.join(root, "nbt.json"))) {
3540
+ throw new Error(`nbt.json already exists ${dir ? `in ${dir}` : "here"} \u2014 this is already an nbt project`);
3541
+ }
3542
+ if (!fs2.existsSync(path3.join(STDLIB, "auth", "schema.nbt"))) {
3543
+ throw new Error("bundled auth cartridge is missing (broken install)");
3544
+ }
3545
+ if (fs2.existsSync(path3.join(root, "nbt", "auth"))) {
3546
+ throw new Error("nbt/auth already exists; refusing to overwrite it during init");
3547
+ }
3548
+ fs2.writeFileSync(path3.join(root, "nbt.json"), NBT_JSON_TEMPLATE);
3549
+ fs2.mkdirSync(path3.join(root, "nbt"), { recursive: true });
3550
+ fs2.mkdirSync(path3.join(root, "generated"), { recursive: true });
3551
+ addStandardCartridge("auth", path3.join(root, "nbt"), false);
3552
+ console.log("Created project:");
3553
+ console.log(" nbt.json (dev environment configured)");
3554
+ console.log(" nbt/auth/ (local core auth cartridge)");
3555
+ console.log(" generated/ (typed client output \u2014 nbt generate)");
3556
+ const example = opts.example !== false && (opts.yes === true || await wantExample());
3557
+ if (example) {
3558
+ scaffoldHelloCart(path3.join(root, "nbt", "hello"));
3559
+ console.log("\nCreated example cartridge nbt/hello/ (entity Note).");
3560
+ console.log("\nNext:");
3561
+ if (dir) console.log(` cd ${dir}`);
3562
+ console.log(" nbt dev # boot the console + live-reload on save");
3563
+ } else {
3564
+ if (dir) console.log(`
3565
+ Next: cd ${dir}, then run \`nbt dev\` or add a standard cartridge with \`nbt add <name>\`.`);
3566
+ else console.log("\nNext: run `nbt dev`, or add another standard cartridge with `nbt add <name>`.");
3567
+ }
3568
+ }
3569
+ async function wantExample() {
3570
+ if (!process.stdin.isTTY) return true;
3571
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
3572
+ try {
3573
+ const ans = (await rl.question("Add a hello-world example cartridge? [Y/n] ")).trim().toLowerCase();
3574
+ return ans === "" || ans === "y" || ans === "yes";
3575
+ } finally {
3576
+ rl.close();
3577
+ }
3578
+ }
3579
+ function scaffoldHelloCart(dir) {
3580
+ fs2.mkdirSync(path3.join(dir, "migrations"), { recursive: true });
3581
+ fs2.writeFileSync(path3.join(dir, "schema.nbt"), HELLO_SCHEMA);
3582
+ }
3583
+ customCommands.add("add");
3584
+ program2.command("add").description("Vendor standard-library cartridges into this project").argument("<cartridge...>", "standard cartridge name(s)").option("--overwrite", "replace an existing local cartridge").action((cartridges, opts) => {
3585
+ try {
3586
+ const proj = loadNbtProject();
3587
+ for (const name of cartridges) {
3588
+ const dest = addStandardCartridge(name, proj.cartsDir, opts.overwrite === true);
3589
+ console.log(`Added ${name} -> ${path3.relative(proj.rootDir, dest)}`);
3590
+ }
3591
+ console.log("Run `nbt migrate deploy` to deploy the local cartridge set.");
3592
+ } catch (e) {
3593
+ console.error(`nbt add: ${e?.message ?? e}`);
3594
+ process.exit(1);
3595
+ }
3596
+ });
3597
+ function addStandardCartridge(name, cartsDir, overwrite) {
3598
+ if (!/^[a-z0-9][a-z0-9_-]*$/.test(name)) {
3599
+ throw new Error(`invalid cartridge name '${name}'`);
3600
+ }
3601
+ const src = path3.join(STDLIB, name);
3602
+ if (!fs2.existsSync(path3.join(src, "schema.nbt"))) {
3603
+ const available = fs2.existsSync(STDLIB) ? fs2.readdirSync(STDLIB, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort().join(", ") : "";
3604
+ throw new Error(
3605
+ `unknown standard cartridge '${name}'${available ? ` (available: ${available})` : " (broken install: stdlib missing)"}`
3606
+ );
3607
+ }
3608
+ const dest = path3.join(cartsDir, name);
3609
+ if (fs2.existsSync(dest)) {
3610
+ if (!overwrite) throw new Error(`${dest} already exists; use --overwrite to replace it`);
3611
+ fs2.rmSync(dest, { recursive: true, force: true });
3612
+ }
3613
+ fs2.mkdirSync(cartsDir, { recursive: true });
3614
+ fs2.cpSync(src, dest, { recursive: true });
3615
+ return dest;
3616
+ }
3617
+ customCommands.add("list");
3618
+ program2.command("list").description("List available standard-library modules").action(() => {
3619
+ if (!fs2.existsSync(STDLIB)) {
3620
+ console.error("nbt list: bundled stdlib missing (broken install)");
3621
+ process.exit(1);
3622
+ }
3623
+ const proj = loadNbtProject();
3624
+ const names = fs2.readdirSync(STDLIB, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).filter((n) => fs2.existsSync(path3.join(STDLIB, n, "schema.nbt"))).sort();
3625
+ for (const n of names) {
3626
+ const installed = fs2.existsSync(path3.join(proj.cartsDir, n, "schema.nbt"));
3627
+ console.log(installed ? `${n} (installed)` : n);
3628
+ }
3629
+ });
3630
+ customCommands.add("update");
3631
+ program2.command("update").description("Update vendored standard-library modules from the bundled stdlib").argument("<module...>", "standard module name(s)").action((modules) => {
3632
+ try {
3633
+ const proj = loadNbtProject();
3634
+ for (const name of modules) {
3635
+ const src = path3.join(STDLIB, name);
3636
+ if (!fs2.existsSync(path3.join(src, "schema.nbt"))) {
3637
+ throw new Error(`unknown standard module '${name}' (see: nbt list)`);
3638
+ }
3639
+ const dest = path3.join(proj.cartsDir, name);
3640
+ if (!fs2.existsSync(path3.join(dest, "schema.nbt"))) {
3641
+ throw new Error(`'${name}' is not installed in this project \u2014 run: nbt add ${name}`);
3642
+ }
3643
+ const written = copyStdlibOver(src, dest);
3644
+ for (const f of written) console.log(` wrote ${path3.relative(proj.rootDir, f)}`);
3645
+ console.log(`Updated ${name}.`);
3646
+ }
3647
+ console.log("Run `nbt migrate create <module>` to capture any schema changes, then `nbt migrate deploy`.");
3648
+ } catch (e) {
3649
+ console.error(`nbt update: ${e?.message ?? e}`);
3650
+ process.exit(1);
3651
+ }
3652
+ });
3653
+ function copyStdlibOver(src, dest) {
3654
+ const written = [];
3655
+ const walk = (s, d) => {
3656
+ fs2.mkdirSync(d, { recursive: true });
3657
+ for (const e of fs2.readdirSync(s, { withFileTypes: true })) {
3658
+ const sp = path3.join(s, e.name);
3659
+ const dp = path3.join(d, e.name);
3660
+ if (e.isDirectory()) walk(sp, dp);
3661
+ else {
3662
+ fs2.copyFileSync(sp, dp);
3663
+ written.push(dp);
3664
+ }
3665
+ }
3666
+ };
3667
+ walk(src, dest);
3668
+ return written;
3669
+ }
3670
+ customCommands.add("dev");
3671
+ program2.command("dev").description("Boot the console and live-reload local carts on .nbt change").option("-p, --port <port>", "console HTTP port (default: nbt.json dev env, else 8080)").option("-d, --data-dir <dir>", "console data dir (default: ./data)").action((opts) => {
3672
+ runDev(opts).catch((e) => {
3673
+ console.error(`nbt dev: ${e?.message ?? e}`);
3674
+ process.exit(1);
3675
+ });
3676
+ });
3677
+ function loadNbtProject() {
3678
+ let dir = process.cwd();
3679
+ for (let i = 0; i < 32; i++) {
3680
+ const candidate = path3.join(dir, "nbt.json");
3681
+ if (fs2.existsSync(candidate)) {
3682
+ try {
3683
+ const cfg = JSON.parse(fs2.readFileSync(candidate, "utf8"));
3684
+ const carts = typeof cfg?.carts === "string" ? cfg.carts : "nbt";
3685
+ const port = Number(cfg?.environments?.dev?.port) || 8080;
3686
+ return { rootDir: dir, cartsDir: path3.resolve(dir, carts), port };
3687
+ } catch {
3688
+ break;
3689
+ }
3690
+ }
3691
+ const parent = path3.dirname(dir);
3692
+ if (parent === dir) break;
3693
+ dir = parent;
3694
+ }
3695
+ return { rootDir: process.cwd(), cartsDir: path3.resolve(process.cwd(), "nbt"), port: 8080 };
3696
+ }
3697
+ function withProjectUpDefaults(argv2) {
3698
+ const hasPort = argv2.some((arg) => arg === "--port" || arg === "-p" || arg.startsWith("--port="));
3699
+ if (hasPort) return argv2;
3700
+ return [...argv2, "--port", String(loadNbtProject().port)];
3701
+ }
3702
+ function discoverCarts(cartsDir) {
3703
+ if (!fs2.existsSync(cartsDir)) return [];
3704
+ return fs2.readdirSync(cartsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path3.join(cartsDir, e.name)).filter((d) => fs2.existsSync(path3.join(d, "schema.nbt")));
3705
+ }
3706
+ function newestNbtMtime(dir) {
3707
+ let newest = 0;
3708
+ const walk = (d) => {
3709
+ let entries;
3710
+ try {
3711
+ entries = fs2.readdirSync(d, { withFileTypes: true });
3712
+ } catch {
3713
+ return;
3714
+ }
3715
+ for (const e of entries) {
3716
+ const p = path3.join(d, e.name);
3717
+ if (e.isDirectory()) {
3718
+ if (e.name !== ".dist" && e.name !== "node_modules") walk(p);
3719
+ } else if (e.name.endsWith(".nbt")) {
3720
+ try {
3721
+ const m = fs2.statSync(p).mtimeMs;
3722
+ if (m > newest) newest = m;
3723
+ } catch {
3724
+ }
3725
+ }
3726
+ }
3727
+ };
3728
+ walk(dir);
3729
+ return newest;
3730
+ }
3731
+ function tcpUp(port, host = "127.0.0.1") {
3732
+ return new Promise((resolve3) => {
3733
+ const sock = net.connect({ port, host });
3734
+ sock.once("connect", () => {
3735
+ sock.destroy();
3736
+ resolve3(true);
3737
+ });
3738
+ sock.once("error", () => resolve3(false));
3739
+ sock.setTimeout(500, () => {
3740
+ sock.destroy();
3741
+ resolve3(false);
3742
+ });
3743
+ });
3744
+ }
3745
+ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
3746
+ function installCart(cartDir, port) {
3747
+ const r = (0, import_node_child_process2.spawnSync)(NBT_BIN, ["install", "--path", cartDir, "--port", String(port)], {
3748
+ stdio: "inherit",
3749
+ env: nativeEnv()
3750
+ });
3751
+ return r.status === 0;
3752
+ }
3753
+ async function runDev(opts) {
3754
+ const proj = loadNbtProject();
3755
+ const port = opts.port ? Number(opts.port) : proj.port;
3756
+ if (!Number.isFinite(port) || port <= 0) throw new Error(`invalid port '${opts.port}'`);
3757
+ const upArgs = ["up", "--port", String(port)];
3758
+ if (opts.dataDir) upArgs.push("--data-dir", opts.dataDir);
3759
+ console.log(`nbt dev: starting console on :${port}`);
3760
+ const daemon = (0, import_node_child_process2.spawn)(CONSOLE_BIN, upArgs, { stdio: "inherit", env: consoleEnv() });
3761
+ let shuttingDown = false;
3762
+ const shutdown = (code = 0) => {
3763
+ if (shuttingDown) return;
3764
+ shuttingDown = true;
3765
+ try {
3766
+ daemon.kill("SIGTERM");
3767
+ } catch {
3768
+ }
3769
+ process.exit(code);
3770
+ };
3771
+ process.on("SIGINT", () => shutdown(0));
3772
+ process.on("SIGTERM", () => shutdown(0));
3773
+ daemon.on("exit", (code) => {
3774
+ if (!shuttingDown) {
3775
+ console.error(`nbt dev: console exited (${code ?? "signal"})`);
3776
+ process.exit(code ?? 1);
3777
+ }
3778
+ });
3779
+ let ready = false;
3780
+ for (let i = 0; i < 60; i++) {
3781
+ if (await tcpUp(port)) {
3782
+ ready = true;
3783
+ break;
3784
+ }
3785
+ await sleep(250);
3786
+ }
3787
+ if (!ready) {
3788
+ console.error("nbt dev: console did not come up in time");
3789
+ return shutdown(1);
3790
+ }
3791
+ const carts = discoverCarts(proj.cartsDir);
3792
+ if (carts.length === 0) {
3793
+ console.warn(`nbt dev: no carts found under ${proj.cartsDir} (nothing to watch)`);
3794
+ }
3795
+ const applied = /* @__PURE__ */ new Map();
3796
+ for (const c of carts) {
3797
+ console.log(`nbt dev: install ${path3.basename(c)}`);
3798
+ installCart(c, port);
3799
+ applied.set(c, newestNbtMtime(c));
3800
+ }
3801
+ console.log(`nbt dev: watching ${proj.cartsDir} \u2014 edit .nbt to reload (Ctrl-C to stop)`);
3802
+ let busy = false;
3803
+ setInterval(async () => {
3804
+ if (busy || shuttingDown) return;
3805
+ busy = true;
3806
+ try {
3807
+ for (const c of discoverCarts(proj.cartsDir)) {
3808
+ const m = newestNbtMtime(c);
3809
+ const prev = applied.get(c) ?? 0;
3810
+ if (m > prev) {
3811
+ await sleep(150);
3812
+ const settled = newestNbtMtime(c);
3813
+ console.log(`nbt dev: reload ${path3.basename(c)}`);
3814
+ installCart(c, port);
3815
+ applied.set(c, settled);
3816
+ }
3817
+ }
3818
+ } finally {
3819
+ busy = false;
3820
+ }
3821
+ }, 400);
3822
+ }
3503
3823
  customCommands.add("bundle");
3504
3824
  program2.command("bundle").description("Bundle TypeScript workflow code via esbuild").argument("<entry...>", "workflow entry point file(s)").option("-o, --outfile <dir>", "output file").action((entry, opts) => {
3505
3825
  if (!opts.outfile) {
@@ -3508,8 +3828,67 @@ program2.command("bundle").description("Bundle TypeScript workflow code via esbu
3508
3828
  }
3509
3829
  bundleWorkflows({ entryPoints: entry, outfile: opts.outfile });
3510
3830
  });
3831
+ customCommands.add("completions");
3832
+ program2.command("completions").description("Emit a shell completion script (bash | zsh | fish)").argument("<shell>", "bash | zsh | fish").action((shell) => {
3833
+ const script = completionScript(shell);
3834
+ if (!script) {
3835
+ console.error(`completions: unknown shell '${shell}' (want bash|zsh|fish)`);
3836
+ process.exit(1);
3837
+ }
3838
+ process.stdout.write(script);
3839
+ });
3840
+ function completionScript(shell) {
3841
+ const n = "nbt";
3842
+ if (shell === "bash") {
3843
+ return `_${n}_complete() {
3844
+ local cur="\${COMP_WORDS[COMP_CWORD]}"
3845
+ local IFS=$'\\n'
3846
+ COMPREPLY=( $(${n} __complete__ "\${COMP_WORDS[@]:1}") )
3847
+ }
3848
+ complete -F _${n}_complete ${n}
3849
+ `;
3850
+ }
3851
+ if (shell === "zsh") {
3852
+ return `#compdef ${n}
3853
+ _${n}() {
3854
+ local -a cands
3855
+ cands=( $(${n} __complete__ "\${(@)words[2,$CURRENT]}") )
3856
+ compadd -a cands
3857
+ }
3858
+ compdef _${n} ${n}
3859
+ `;
3860
+ }
3861
+ if (shell === "fish") {
3862
+ return `complete -c ${n} -f -a "(${n} __complete__ (commandline -opc)[2..])"
3863
+ `;
3864
+ }
3865
+ return "";
3866
+ }
3867
+ function runCompletion(words) {
3868
+ if (words.length <= 1) {
3869
+ const prefix = words[0] ?? "";
3870
+ const names = [
3871
+ ...NBT.map(([n]) => n),
3872
+ ...CONSOLE.map(([n]) => n),
3873
+ ...customCommands
3874
+ ];
3875
+ const cands = [...new Set(names)].filter((n) => n.startsWith(prefix)).sort();
3876
+ if (cands.length) process.stdout.write(cands.join("\n") + "\n");
3877
+ process.exit(0);
3878
+ }
3879
+ const first2 = words[0];
3880
+ if (customCommands.has(first2)) process.exit(0);
3881
+ const isConsole = consoleCommands.has(first2);
3882
+ const bin = isConsole ? CONSOLE_BIN : NBT_BIN;
3883
+ const env = isConsole ? consoleEnv() : nativeEnv();
3884
+ const r = (0, import_node_child_process2.spawnSync)(bin, ["__complete__", ...words], { stdio: "inherit", env });
3885
+ process.exit(r.status === null ? 0 : r.status);
3886
+ }
3511
3887
  var argv = process.argv.slice(2);
3512
3888
  var first = argv[0];
3889
+ if (first === "__complete__") {
3890
+ runCompletion(argv.slice(1));
3891
+ }
3513
3892
  if (!first || first === "-h" || first === "--help" || first === "help") {
3514
3893
  program2.outputHelp();
3515
3894
  process.exit(first ? 0 : 1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nbt-dev/nbt",
3
- "version": "0.0.1",
3
+ "version": "0.0.4",
4
4
  "description": "The nbt CLI and console daemon for the nbt-dev console — a fixed-target OS for web development. Linux/x64 prebuilt binaries.",
5
5
  "bin": {
6
6
  "nbt": "dist/nbt.js"
@@ -8,9 +8,10 @@
8
8
  "files": [
9
9
  "dist/",
10
10
  "vendor/linux-x64/",
11
- "contracts/",
11
+ "stdlib/",
12
12
  "README.md",
13
- "LICENSE"
13
+ "LICENSE",
14
+ "TRADEMARKS.md"
14
15
  ],
15
16
  "scripts": {
16
17
  "build": "node scripts/build.mjs",
@@ -29,7 +30,7 @@
29
30
  "publishConfig": {
30
31
  "access": "public"
31
32
  },
32
- "license": "MIT",
33
+ "license": "CPAL-1.0",
33
34
  "dependencies": {
34
35
  "esbuild": "^0.28.0"
35
36
  },
@@ -0,0 +1,83 @@
1
+ # Auth Cartridge
2
+
3
+ User identity, sessions, API keys, and cross-cartridge middleware.
4
+
5
+ ## Middleware
6
+
7
+ The cartridge exports `authenticate` — validates the `session_token` cookie
8
+ against stored Sessions. Returns 401 if missing or invalid. On success, sets
9
+ `X-User-Id` on the forwarded request.
10
+
11
+ To protect another cartridge, add this to its schema.nbt:
12
+
13
+ ```
14
+ import "auth";
15
+ ```
16
+
17
+ All routes on that cartridge now require a valid session cookie. The auth
18
+ cartridge's own routes remain unprotected.
19
+
20
+ ## How middleware works
21
+
22
+ ```
23
+ client request
24
+ |
25
+ v
26
+ console proxy sees "guarded" cartridge has depends:auth
27
+ |
28
+ v
29
+ POST /_middleware/authenticate -> auth cartridge socket
30
+ |
31
+ +-- no session_token cookie -> 401 "unauthorized"
32
+ +-- token not in Sessions -> 401 "invalid session"
33
+ +-- valid session -> 200 + X-User-Id header
34
+ |
35
+ v (if 200)
36
+ forward request to guarded cartridge with X-User-Id injected
37
+ ```
38
+
39
+ ## Writing middleware
40
+
41
+ Middleware is a function with access to the incoming request. Available helpers:
42
+
43
+ ```nbt
44
+ middleware authenticate(request: Request) {
45
+ jai {
46
+ // read headers/cookies from the request
47
+ token := get_cookie(request, "session_token");
48
+ key := get_header(request, "X-Api-Key");
49
+
50
+ // reject
51
+ respond(401, "unauthorized");
52
+
53
+ // accept and enrich the forwarded request
54
+ set_header("X-User-Id", "42");
55
+ respond(200, "");
56
+ }
57
+ }
58
+ ```
59
+
60
+ | Helper | Does |
61
+ |------------------------------|-------------------------------------------|
62
+ | `get_cookie(request, name)` | Parse and return a cookie value |
63
+ | `get_header(request, name)` | Case-insensitive header lookup |
64
+ | `set_header(name, value)` | Add header to forwarded request (max 16) |
65
+ | `respond(status, body)` | Set response and return from handler |
66
+
67
+ `respond()` is terminal — it writes the HTTP response and exits the handler.
68
+
69
+ ## Manifest
70
+
71
+ After `nbt build`, the auth cartridge's manifest contains:
72
+
73
+ ```
74
+ user
75
+ session
76
+ account
77
+ verification
78
+ apikey
79
+ middleware:authenticate
80
+ ```
81
+
82
+ A cartridge that imports auth gets `depends:auth` in its own manifest.
83
+ The console reads both at startup to build the middleware dependency map.
@@ -0,0 +1,48 @@
1
+ migration initial {
2
+ add_entity User
3
+ add_field User name string default("")
4
+ add_field User username string default("")
5
+ add_field User email string default("")
6
+ add_field User emailVerified bool default(false)
7
+ add_field User externalId string default("")
8
+ add_field User capsVersion u32 default(0)
9
+ add_index User [email]
10
+ add_index User [externalId]
11
+ add_entity UserRole
12
+ add_field UserRole userId string default("")
13
+ add_field UserRole cart string default("")
14
+ add_field UserRole role string default("")
15
+ add_index UserRole [userId]
16
+ add_entity Session
17
+ add_field Session userId string default("")
18
+ add_field Session token string default("")
19
+ add_field Session expiresAt DateTime default(0)
20
+ add_field Session ipAddress string default("")
21
+ add_field Session userAgent string default("")
22
+ add_index Session [token]
23
+ add_index Session [userId]
24
+ add_entity Account
25
+ add_field Account userId string default("")
26
+ add_field Account providerId string default("")
27
+ add_field Account password string default("")
28
+ add_field Account accessToken string default("")
29
+ add_field Account refreshToken string default("")
30
+ add_field Account idToken string default("")
31
+ add_field Account accessTokenExpiresAt DateTime default(0)
32
+ add_field Account refreshTokenExpiresAt DateTime default(0)
33
+ add_field Account scope string default("")
34
+ add_index Account [userId]
35
+ add_entity Verification
36
+ add_field Verification identifier string default("")
37
+ add_field Verification value string default("")
38
+ add_field Verification expiresAt DateTime default(0)
39
+ add_index Verification [identifier]
40
+ add_entity ApiKey
41
+ add_field ApiKey name string default("")
42
+ add_field ApiKey projectId string default("")
43
+ add_field ApiKey start string default("")
44
+ add_field ApiKey prefix string default("")
45
+ add_field ApiKey key string default("")
46
+ add_field ApiKey permissions string default("")
47
+ add_field ApiKey roles string default("")
48
+ }
@@ -0,0 +1,58 @@
1
+ entity User {
2
+ name: string
3
+ username?: string
4
+ email?: string
5
+ emailVerified: bool
6
+ externalId?: string
7
+ capsVersion: u32
8
+ @@index([email])
9
+ @@index([externalId])
10
+ }
11
+
12
+ entity UserRole {
13
+ userId: string
14
+ cart: string
15
+ role: string
16
+ @@index([userId])
17
+ }
18
+
19
+ entity Session {
20
+ userId: string
21
+ token: string
22
+ expiresAt: DateTime
23
+ ipAddress?: string
24
+ userAgent?: string
25
+ @@index([token])
26
+ @@index([userId])
27
+ }
28
+
29
+ entity Account {
30
+ userId: string
31
+ providerId: string
32
+ password: string
33
+ accessToken: string
34
+ refreshToken: string
35
+ idToken: string
36
+ accessTokenExpiresAt: DateTime
37
+ refreshTokenExpiresAt: DateTime
38
+ scope: string
39
+ @@index([userId])
40
+ }
41
+
42
+ entity Verification {
43
+ identifier: string
44
+ value: string
45
+ expiresAt: DateTime
46
+ @@index([identifier])
47
+ }
48
+
49
+ entity ApiKey {
50
+ name: string
51
+ projectId: string
52
+ start: string
53
+ prefix: string
54
+ key: string
55
+ permissions: string
56
+ roles: string
57
+ }
58
+
@@ -0,0 +1,3 @@
1
+ migration update_user {
2
+ add_unique User [email]
3
+ }