primitive-admin 1.1.0-alpha.44 → 1.1.0-alpha.45

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 (37) hide show
  1. package/dist/bin/primitive.js +0 -0
  2. package/dist/src/commands/blob-buckets.js +20 -7
  3. package/dist/src/commands/blob-buckets.js.map +1 -1
  4. package/dist/src/commands/sync.js +113 -28
  5. package/dist/src/commands/sync.js.map +1 -1
  6. package/dist/src/commands/workflows.js +185 -20
  7. package/dist/src/commands/workflows.js.map +1 -1
  8. package/dist/src/lib/api-client.d.ts +1 -0
  9. package/dist/src/lib/api-client.js +4 -0
  10. package/dist/src/lib/api-client.js.map +1 -1
  11. package/dist/src/lib/codegen-shared/generatedFiles.d.ts +61 -0
  12. package/dist/src/lib/codegen-shared/generatedFiles.js +127 -0
  13. package/dist/src/lib/codegen-shared/generatedFiles.js.map +1 -0
  14. package/dist/src/lib/db-codegen/dbGenerator.d.ts +30 -5
  15. package/dist/src/lib/db-codegen/dbGenerator.js +227 -165
  16. package/dist/src/lib/db-codegen/dbGenerator.js.map +1 -1
  17. package/dist/src/lib/db-codegen/dbTemplates.d.ts +11 -23
  18. package/dist/src/lib/db-codegen/dbTemplates.js +21 -41
  19. package/dist/src/lib/db-codegen/dbTemplates.js.map +1 -1
  20. package/dist/src/lib/db-codegen/dbTsTypes.d.ts +7 -3
  21. package/dist/src/lib/db-codegen/dbTsTypes.js +23 -1
  22. package/dist/src/lib/db-codegen/dbTsTypes.js.map +1 -1
  23. package/dist/src/lib/generated-allowlist.js +13 -0
  24. package/dist/src/lib/generated-allowlist.js.map +1 -1
  25. package/dist/src/lib/workflow-codegen/generated-schema-descriptor.d.ts +96 -0
  26. package/dist/src/lib/workflow-codegen/generated-schema-descriptor.js +112 -0
  27. package/dist/src/lib/workflow-codegen/generated-schema-descriptor.js.map +1 -0
  28. package/dist/src/lib/workflow-codegen/generator.d.ts +70 -0
  29. package/dist/src/lib/workflow-codegen/generator.js +186 -0
  30. package/dist/src/lib/workflow-codegen/generator.js.map +1 -0
  31. package/dist/src/lib/workflow-codegen/naming.d.ts +33 -0
  32. package/dist/src/lib/workflow-codegen/naming.js +81 -0
  33. package/dist/src/lib/workflow-codegen/naming.js.map +1 -0
  34. package/dist/src/lib/workflow-codegen/schemaToTs.d.ts +55 -0
  35. package/dist/src/lib/workflow-codegen/schemaToTs.js +237 -0
  36. package/dist/src/lib/workflow-codegen/schemaToTs.js.map +1 -0
  37. package/package.json +4 -2
@@ -1,7 +1,9 @@
1
- import { readFileSync, writeFileSync, statSync } from "fs";
1
+ import { readFileSync, writeFileSync, statSync, existsSync, readdirSync, } from "fs";
2
2
  import { basename } from "path";
3
+ import * as path from "path";
3
4
  import { parseConfigToml, stringifyConfigToml } from "../lib/config-toml.js";
4
5
  import { lookup as mimeLookup } from "mime-types";
6
+ import { generateWorkflowTypes, } from "../lib/workflow-codegen/generator.js";
5
7
  import { ApiClient } from "../lib/api-client.js";
6
8
  import { resolveAppId } from "../lib/config.js";
7
9
  import { validateWorkflowToml, formatWorkflowTomlErrors, } from "../lib/workflow-toml-validator.js";
@@ -246,7 +248,7 @@ Examples:
246
248
  workflows
247
249
  .command("get")
248
250
  .description("Get workflow details")
249
- .argument("<workflow-id>", "Workflow ID")
251
+ .argument("<workflow-id>", "Workflow ID or key")
250
252
  .option("--app <app-id>", "App ID (uses current app if not specified)")
251
253
  .option("--json", "Output as JSON")
252
254
  .action(async (workflowId, options) => {
@@ -327,7 +329,7 @@ Examples:
327
329
  workflows
328
330
  .command("update")
329
331
  .description("Update workflow metadata, or push a new body with --from-file")
330
- .argument("<workflow-id>", "Workflow ID")
332
+ .argument("<workflow-id>", "Workflow ID or key")
331
333
  .option("--app <app-id>", "App ID (uses current app if not specified)")
332
334
  .option("--name <name>", "Display name")
333
335
  .option("--description <desc>", "Description")
@@ -506,7 +508,7 @@ Examples:
506
508
  workflows
507
509
  .command("delete")
508
510
  .description("Delete or archive a workflow")
509
- .argument("<workflow-id>", "Workflow ID")
511
+ .argument("<workflow-id>", "Workflow ID or key")
510
512
  .option("--app <app-id>", "App ID (uses current app if not specified)")
511
513
  .option("--hard", "Permanently delete instead of archive")
512
514
  .option("-y, --yes", "Skip confirmation prompt")
@@ -573,7 +575,7 @@ Examples:
573
575
  draft
574
576
  .command("update")
575
577
  .description("Update workflow draft steps (deprecated; use 'workflows configs' for staged rollouts)")
576
- .argument("<workflow-id>", "Workflow ID")
578
+ .argument("<workflow-id>", "Workflow ID or key")
577
579
  .option("--app <app-id>", "App ID (uses current app if not specified)")
578
580
  .option("--from-file <path>", "Load steps from TOML file")
579
581
  .option("--json", "Output as JSON")
@@ -642,7 +644,7 @@ Examples:
642
644
  workflows
643
645
  .command("publish")
644
646
  .description("Publish the current draft as a new revision (deprecated for new-model workflows; use 'workflows configs activate')")
645
- .argument("<workflow-id>", "Workflow ID")
647
+ .argument("<workflow-id>", "Workflow ID or key")
646
648
  .option("--app <app-id>", "App ID (uses current app if not specified)")
647
649
  .option("--json", "Output as JSON")
648
650
  .action(async (workflowId, options) => {
@@ -686,7 +688,7 @@ Examples:
686
688
  workflows
687
689
  .command("preview")
688
690
  .description("Run a preview execution of the workflow")
689
- .argument("<workflow-id>", "Workflow ID")
691
+ .argument("<workflow-id>", "Workflow ID or key")
690
692
  .option("--app <app-id>", "App ID (uses current app if not specified)")
691
693
  .option("--config <config-id>", "Use specific configuration")
692
694
  .option("--draft", "Force preview of the draft version, even if active is newer")
@@ -841,7 +843,7 @@ Examples:
841
843
  runs
842
844
  .command("list")
843
845
  .description("List workflow runs")
844
- .argument("<workflow-id>", "Workflow ID")
846
+ .argument("<workflow-id>", "Workflow ID or key")
845
847
  .option("--app <app-id>", "App ID (uses current app if not specified)")
846
848
  .option("--status <status>", "Filter by status: pending, running, completed, failed")
847
849
  .option("--limit <n>", "Number of runs to show", "20")
@@ -879,7 +881,7 @@ Examples:
879
881
  runs
880
882
  .command("status")
881
883
  .description("Get status of a workflow run")
882
- .argument("<workflow-id>", "Workflow ID")
884
+ .argument("<workflow-id>", "Workflow ID or key")
883
885
  .argument("<run-id>", "Run ID")
884
886
  .option("--app <app-id>", "App ID (uses current app if not specified)")
885
887
  .option("--json", "Output as JSON")
@@ -928,7 +930,7 @@ Examples:
928
930
  runs
929
931
  .command("steps")
930
932
  .description("Show step-level details for a workflow run")
931
- .argument("<workflow-id>", "Workflow ID")
933
+ .argument("<workflow-id>", "Workflow ID or key")
932
934
  .argument("<run-id>", "Run ID")
933
935
  .option("--app <app-id>", "App ID")
934
936
  .option("--json", "Output as JSON")
@@ -978,7 +980,7 @@ Examples:
978
980
  runs
979
981
  .command("step-detail")
980
982
  .description("Show full details for a single step")
981
- .argument("<workflow-id>", "Workflow ID")
983
+ .argument("<workflow-id>", "Workflow ID or key")
982
984
  .argument("<run-id>", "Run ID")
983
985
  .argument("<step-id>", "Step ID")
984
986
  .option("--app <app-id>", "App ID")
@@ -1100,7 +1102,7 @@ Examples:
1100
1102
  runs
1101
1103
  .command("error")
1102
1104
  .description("Show error details for a failed workflow run")
1103
- .argument("<workflow-id>", "Workflow ID")
1105
+ .argument("<workflow-id>", "Workflow ID or key")
1104
1106
  .argument("<run-id>", "Run ID")
1105
1107
  .option("--app <app-id>", "App ID")
1106
1108
  .option("--json", "Output as JSON")
@@ -1169,7 +1171,7 @@ Examples:
1169
1171
  runs
1170
1172
  .command("failures")
1171
1173
  .description("List recent workflow run failures")
1172
- .argument("<workflow-id>", "Workflow ID")
1174
+ .argument("<workflow-id>", "Workflow ID or key")
1173
1175
  .option("--app <app-id>", "App ID")
1174
1176
  .option("--limit <n>", "Number of failures to show", "10")
1175
1177
  .option("--json", "Output as JSON")
@@ -1398,7 +1400,7 @@ Examples:
1398
1400
  configs
1399
1401
  .command("list")
1400
1402
  .description("List configurations for a workflow")
1401
- .argument("<workflow-id>", "Workflow ID")
1403
+ .argument("<workflow-id>", "Workflow ID or key")
1402
1404
  .option("--app <app-id>", "App ID (uses current app if not specified)")
1403
1405
  .option("--json", "Output as JSON")
1404
1406
  .action(async (workflowId, options) => {
@@ -1438,7 +1440,7 @@ Examples:
1438
1440
  configs
1439
1441
  .command("get")
1440
1442
  .description("Get configuration details")
1441
- .argument("<workflow-id>", "Workflow ID")
1443
+ .argument("<workflow-id>", "Workflow ID or key")
1442
1444
  .argument("<config-id>", "Configuration ID")
1443
1445
  .option("--app <app-id>", "App ID (uses current app if not specified)")
1444
1446
  .option("--json", "Output as JSON")
@@ -1474,7 +1476,7 @@ Examples:
1474
1476
  configs
1475
1477
  .command("create")
1476
1478
  .description("Create a new configuration")
1477
- .argument("<workflow-id>", "Workflow ID")
1479
+ .argument("<workflow-id>", "Workflow ID or key")
1478
1480
  .option("--app <app-id>", "App ID (uses current app if not specified)")
1479
1481
  .option("--name <name>", "Configuration name (required)")
1480
1482
  .option("--description <desc>", "Description")
@@ -1528,7 +1530,7 @@ Examples:
1528
1530
  configs
1529
1531
  .command("update")
1530
1532
  .description("Update a configuration")
1531
- .argument("<workflow-id>", "Workflow ID")
1533
+ .argument("<workflow-id>", "Workflow ID or key")
1532
1534
  .argument("<config-id>", "Configuration ID")
1533
1535
  .option("--app <app-id>", "App ID (uses current app if not specified)")
1534
1536
  .option("--name <name>", "Configuration name")
@@ -1584,7 +1586,7 @@ Examples:
1584
1586
  configs
1585
1587
  .command("activate")
1586
1588
  .description("Set a configuration as the active (default) configuration")
1587
- .argument("<workflow-id>", "Workflow ID")
1589
+ .argument("<workflow-id>", "Workflow ID or key")
1588
1590
  .argument("<config-id>", "Configuration ID")
1589
1591
  .option("--app <app-id>", "App ID (uses current app if not specified)")
1590
1592
  .option("--json", "Output as JSON")
@@ -1609,7 +1611,7 @@ Examples:
1609
1611
  configs
1610
1612
  .command("duplicate")
1611
1613
  .description("Duplicate a configuration")
1612
- .argument("<workflow-id>", "Workflow ID")
1614
+ .argument("<workflow-id>", "Workflow ID or key")
1613
1615
  .argument("<config-id>", "Configuration ID to duplicate")
1614
1616
  .option("--app <app-id>", "App ID (uses current app if not specified)")
1615
1617
  .option("--name <name>", "Name for the new configuration")
@@ -1637,7 +1639,7 @@ Examples:
1637
1639
  configs
1638
1640
  .command("archive")
1639
1641
  .description("Archive a configuration")
1640
- .argument("<workflow-id>", "Workflow ID")
1642
+ .argument("<workflow-id>", "Workflow ID or key")
1641
1643
  .argument("<config-id>", "Configuration ID")
1642
1644
  .option("--app <app-id>", "App ID (uses current app if not specified)")
1643
1645
  .option("-y, --yes", "Skip confirmation prompt")
@@ -2456,6 +2458,169 @@ Examples:
2456
2458
  process.exit(1);
2457
2459
  }
2458
2460
  });
2461
+ // ============================================
2462
+ // Codegen command (issue #1442)
2463
+ // ============================================
2464
+ workflows
2465
+ .command("codegen")
2466
+ .description("Generate typed TypeScript signatures from the local workflows/*.toml schemas. Emits one <key>.generated.ts per workflow (<Key>Input/<Key>Output types + a <key>(client) typed invoker factory returning { runSync?, start }).")
2467
+ .argument("[workflow-key]", "Generate for a single workflow (defaults to every workflows/*.toml found)")
2468
+ .option("--app <app-id>", "App ID")
2469
+ .option("--sync-dir <path>", "Override path to the sync directory (defaults to ./.primitive/sync/<env>/<appId>/)")
2470
+ .option("-o, --output <dir>", "Output directory for the <key>.generated.ts files (defaults to <sync-dir>/workflows/generated/)")
2471
+ .option("--check", "Exit non-zero if generated output is out of date (CI guard); does not write.")
2472
+ .option("--json", "Output the result summary as JSON")
2473
+ .action(async (workflowKey, options) => {
2474
+ try {
2475
+ // 1. Locate the workflows directory. Mirrors `databases codegen`'s
2476
+ // sync-dir resolution: honor --sync-dir, else search any
2477
+ // env/appId subtree under ./.primitive/sync that has a workflows/
2478
+ // directory. When --app is given, restrict the scan to that app's
2479
+ // subtree (the sync-tree layout is <env>/<appId>/), so a multi-app
2480
+ // tree generates only the requested app's workflows instead of
2481
+ // combining every app's and writing into the first one found.
2482
+ const workflowDirs = [];
2483
+ if (options.syncDir) {
2484
+ const candidate = path.join(options.syncDir, "workflows");
2485
+ if (existsSync(candidate)) {
2486
+ workflowDirs.push(candidate);
2487
+ }
2488
+ }
2489
+ else {
2490
+ const root = path.join(process.cwd(), ".primitive", "sync");
2491
+ if (existsSync(root)) {
2492
+ for (const env of readdirSync(root)) {
2493
+ const envDir = path.join(root, env);
2494
+ try {
2495
+ for (const app of readdirSync(envDir)) {
2496
+ if (options.app && app !== options.app)
2497
+ continue;
2498
+ const candidate = path.join(envDir, app, "workflows");
2499
+ if (existsSync(candidate)) {
2500
+ workflowDirs.push(candidate);
2501
+ }
2502
+ }
2503
+ }
2504
+ catch {
2505
+ // skip non-dirs
2506
+ }
2507
+ }
2508
+ }
2509
+ }
2510
+ if (workflowDirs.length === 0) {
2511
+ error(options.app
2512
+ ? `No workflows/ directory found for app "${options.app}" under .primitive/sync/. Check the --app id, pass --sync-dir, or run \`primitive sync pull\` first.`
2513
+ : "No workflows/ directory found. Pass --sync-dir or run `primitive sync pull` first.");
2514
+ process.exit(1);
2515
+ }
2516
+ // Codegen writes one <key>.generated.ts per workflow into a single
2517
+ // output dir derived from the first source dir below. Generating from
2518
+ // more than one discovered workflows/ directory (multiple apps, or the
2519
+ // same app across multiple synced envs) would combine unrelated apps'
2520
+ // workflows into the first one's output dir and write nothing for the
2521
+ // rest. Require the caller to disambiguate rather than combining.
2522
+ if (workflowDirs.length > 1) {
2523
+ const appsFound = [
2524
+ ...new Set(workflowDirs.map((d) => path.basename(path.dirname(d)))),
2525
+ ];
2526
+ error(`Found workflows/ directories for more than one app/env:\n` +
2527
+ workflowDirs
2528
+ .map((d) => ` ${path.relative(process.cwd(), d)}`)
2529
+ .join("\n") +
2530
+ `\nCodegen writes into one output dir, so it must run against` +
2531
+ ` one app. ` +
2532
+ (options.app
2533
+ ? `App "${options.app}" resolves to multiple synced envs — pass --sync-dir to pick one.`
2534
+ : `Pass --app <app-id> (found: ${appsFound.join(", ")}) or` +
2535
+ ` --sync-dir to select one.`));
2536
+ process.exit(1);
2537
+ }
2538
+ // 2. Collect the source .toml files (one per workflow), filtered to a
2539
+ // single workflow when an argument is given. Match on the file stem;
2540
+ // the generator resolves the real key from `[workflow].key`.
2541
+ const inputs = [];
2542
+ const seenStems = new Set();
2543
+ for (const dir of workflowDirs) {
2544
+ for (const fileName of readdirSync(dir)) {
2545
+ if (!fileName.endsWith(".toml"))
2546
+ continue;
2547
+ const stem = fileName.slice(0, -".toml".length);
2548
+ if (workflowKey && stem !== workflowKey)
2549
+ continue;
2550
+ if (seenStems.has(stem))
2551
+ continue;
2552
+ seenStems.add(stem);
2553
+ const tomlPath = path.join(dir, fileName);
2554
+ inputs.push({
2555
+ fileStem: stem,
2556
+ tomlPath,
2557
+ tomlContent: readFileSync(tomlPath, "utf-8"),
2558
+ });
2559
+ }
2560
+ }
2561
+ if (inputs.length === 0) {
2562
+ error(workflowKey
2563
+ ? `No workflows/${workflowKey}.toml found under .primitive/sync/.`
2564
+ : "No workflows/*.toml files found to generate from.");
2565
+ process.exit(1);
2566
+ }
2567
+ // 3. Resolve the output directory. Default: a `generated/` subdir of
2568
+ // the first source dir (keeps generated output beside the schema).
2569
+ const outputDir = options.output
2570
+ ? path.resolve(options.output)
2571
+ : path.join(path.dirname(inputs[0].tomlPath), "generated");
2572
+ // 4. Run codegen (or --check).
2573
+ const codegenResult = await generateWorkflowTypes({
2574
+ inputs,
2575
+ outputDir,
2576
+ check: !!options.check,
2577
+ singleWorkflow: !!workflowKey,
2578
+ });
2579
+ if (options.check) {
2580
+ if (codegenResult.mismatches.length > 0) {
2581
+ if (options.json) {
2582
+ json({ ok: false, mismatches: codegenResult.mismatches });
2583
+ }
2584
+ else {
2585
+ error(`Check failed: ${codegenResult.mismatches.length} file(s) out of date.`);
2586
+ for (const m of codegenResult.mismatches) {
2587
+ error(` ${m.reason}: ${path.relative(process.cwd(), m.filePath)}`);
2588
+ }
2589
+ info("Run `primitive workflows codegen` to regenerate.");
2590
+ }
2591
+ process.exit(1);
2592
+ }
2593
+ if (options.json) {
2594
+ json({ ok: true, checked: codegenResult.writtenFiles.length });
2595
+ }
2596
+ else {
2597
+ success(`Check passed: ${codegenResult.writtenFiles.length} file(s) up to date.`);
2598
+ }
2599
+ return;
2600
+ }
2601
+ if (options.json) {
2602
+ json({
2603
+ written: codegenResult.writtenFiles,
2604
+ deleted: codegenResult.deletedFiles,
2605
+ });
2606
+ return;
2607
+ }
2608
+ success(`Generated ${codegenResult.writtenFiles.length} file(s)` +
2609
+ (codegenResult.deletedFiles.length > 0
2610
+ ? `, deleted ${codegenResult.deletedFiles.length} stale file(s).`
2611
+ : "."));
2612
+ for (const f of codegenResult.writtenFiles) {
2613
+ keyValue(" wrote", path.relative(process.cwd(), f));
2614
+ }
2615
+ for (const f of codegenResult.deletedFiles) {
2616
+ keyValue(" deleted", path.relative(process.cwd(), f));
2617
+ }
2618
+ }
2619
+ catch (err) {
2620
+ error(err.message);
2621
+ process.exit(1);
2622
+ }
2623
+ });
2459
2624
  }
2460
2625
  function formatFileSize(bytes) {
2461
2626
  if (bytes < 1024)