@beignet/cli 0.0.36 → 0.0.38

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 (95) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +36 -5
  3. package/dist/config.d.ts +15 -1
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/config.js +30 -1
  6. package/dist/config.js.map +1 -1
  7. package/dist/inspect.js +21 -17
  8. package/dist/inspect.js.map +1 -1
  9. package/dist/lint.d.ts.map +1 -1
  10. package/dist/lint.js +27 -6
  11. package/dist/lint.js.map +1 -1
  12. package/dist/make/inbox.js +7 -7
  13. package/dist/make/inbox.js.map +1 -1
  14. package/dist/make/payments.d.ts +2 -1
  15. package/dist/make/payments.d.ts.map +1 -1
  16. package/dist/make/payments.js +15 -11
  17. package/dist/make/payments.js.map +1 -1
  18. package/dist/make/shared.d.ts +1 -1
  19. package/dist/make/shared.d.ts.map +1 -1
  20. package/dist/make/shared.js +49 -37
  21. package/dist/make/shared.js.map +1 -1
  22. package/dist/make/tenancy.js +8 -8
  23. package/dist/make/tenancy.js.map +1 -1
  24. package/dist/make.d.ts.map +1 -1
  25. package/dist/make.js +27 -16
  26. package/dist/make.js.map +1 -1
  27. package/dist/operational-error-reporting.d.ts +15 -0
  28. package/dist/operational-error-reporting.d.ts.map +1 -0
  29. package/dist/operational-error-reporting.js +42 -0
  30. package/dist/operational-error-reporting.js.map +1 -0
  31. package/dist/outbox.d.ts.map +1 -1
  32. package/dist/outbox.js +73 -0
  33. package/dist/outbox.js.map +1 -1
  34. package/dist/provider-add.js +17 -17
  35. package/dist/provider-add.js.map +1 -1
  36. package/dist/provider-audit.d.ts.map +1 -1
  37. package/dist/provider-audit.js +11 -6
  38. package/dist/provider-audit.js.map +1 -1
  39. package/dist/schedule.d.ts.map +1 -1
  40. package/dist/schedule.js +28 -1
  41. package/dist/schedule.js.map +1 -1
  42. package/dist/task.d.ts.map +1 -1
  43. package/dist/task.js +37 -4
  44. package/dist/task.js.map +1 -1
  45. package/dist/templates/base.d.ts.map +1 -1
  46. package/dist/templates/base.js +6 -1
  47. package/dist/templates/base.js.map +1 -1
  48. package/dist/templates/db/index.d.ts +1 -0
  49. package/dist/templates/db/index.d.ts.map +1 -1
  50. package/dist/templates/db/index.js.map +1 -1
  51. package/dist/templates/db/mysql.d.ts.map +1 -1
  52. package/dist/templates/db/mysql.js +16 -0
  53. package/dist/templates/db/mysql.js.map +1 -1
  54. package/dist/templates/db/postgres.d.ts.map +1 -1
  55. package/dist/templates/db/postgres.js +16 -0
  56. package/dist/templates/db/postgres.js.map +1 -1
  57. package/dist/templates/db/sqlite.d.ts.map +1 -1
  58. package/dist/templates/db/sqlite.js +16 -0
  59. package/dist/templates/db/sqlite.js.map +1 -1
  60. package/dist/templates/index.d.ts.map +1 -1
  61. package/dist/templates/index.js +4 -3
  62. package/dist/templates/index.js.map +1 -1
  63. package/dist/templates/server.d.ts +2 -2
  64. package/dist/templates/server.d.ts.map +1 -1
  65. package/dist/templates/server.js +56 -40
  66. package/dist/templates/server.js.map +1 -1
  67. package/dist/templates/shared.js +3 -3
  68. package/dist/templates/shared.js.map +1 -1
  69. package/dist/templates/testing.js +1 -1
  70. package/dist/templates/todos.js +6 -6
  71. package/package.json +4 -4
  72. package/src/config.ts +66 -2
  73. package/src/inspect.ts +31 -19
  74. package/src/lint.ts +35 -4
  75. package/src/make/inbox.ts +7 -7
  76. package/src/make/payments.ts +23 -15
  77. package/src/make/shared.ts +56 -40
  78. package/src/make/tenancy.ts +8 -8
  79. package/src/make.ts +30 -16
  80. package/src/operational-error-reporting.ts +60 -0
  81. package/src/outbox.ts +77 -0
  82. package/src/provider-add.ts +21 -17
  83. package/src/provider-audit.ts +28 -11
  84. package/src/schedule.ts +32 -1
  85. package/src/task.ts +41 -4
  86. package/src/templates/base.ts +6 -1
  87. package/src/templates/db/index.ts +1 -0
  88. package/src/templates/db/mysql.ts +16 -0
  89. package/src/templates/db/postgres.ts +16 -0
  90. package/src/templates/db/sqlite.ts +16 -0
  91. package/src/templates/index.ts +4 -3
  92. package/src/templates/server.ts +56 -42
  93. package/src/templates/shared.ts +3 -3
  94. package/src/templates/testing.ts +1 -1
  95. package/src/templates/todos.ts +6 -6
@@ -273,7 +273,7 @@ export type FileChangeSet = {
273
273
  * A provider-backed app port a generator depends on.
274
274
  *
275
275
  * Generated code that publishes events or sends notifications needs the
276
- * matching port in `AppPorts`, the key deferred in `infra/app-ports.ts`, and a
276
+ * matching port in `AppPorts`, the key deferred in `infra/port-wiring.ts`, and a
277
277
  * dev-default provider registered in `server/providers.ts`. Wiring is
278
278
  * generation-time by design: the starter stays lean and each generator wires
279
279
  * its own dependencies.
@@ -434,8 +434,13 @@ export function wireListenersProviderSource(
434
434
  }
435
435
 
436
436
  if (!new RegExp(`\\bconst\\s+${providerName}\\b`).test(next)) {
437
+ next = addNamedImport(
438
+ next,
439
+ "tryReportException",
440
+ "@beignet/core/error-reporting",
441
+ );
437
442
  const providerDefinition = `const ${providerName} = createProvider<
438
- \tPick<AppPorts, "eventBus" | "logger">,
443
+ \tPick<AppPorts, "errorReporter" | "eventBus" | "logger">,
439
444
  \tAppContext,
440
445
  \tAppServiceContextInput
441
446
  >()({
@@ -451,6 +456,20 @@ export function wireListenersProviderSource(
451
456
  \t\t\t\t\terror,
452
457
  \t\t\t\t\tlistenerName: listener.name,
453
458
  \t\t\t\t});
459
+ \t\t\t\tvoid tryReportException({
460
+ \t\t\t\t\treporter: ports.errorReporter,
461
+ \t\t\t\t\terror,
462
+ \t\t\t\t\treportOptions: {
463
+ \t\t\t\t\t\tlevel: "error",
464
+ \t\t\t\t\t\tmechanism: "beignet.listener",
465
+ \t\t\t\t\t\thandled: false,
466
+ \t\t\t\t\t\ttags: {
467
+ \t\t\t\t\t\t\t"beignet.kind": "listener",
468
+ \t\t\t\t\t\t\t"beignet.listener": listener.name,
469
+ \t\t\t\t\t\t},
470
+ \t\t\t\t\t\tcontexts: { listener: { name: listener.name } },
471
+ \t\t\t\t\t},
472
+ \t\t\t\t});
454
473
  \t\t\t},
455
474
  \t\t});
456
475
 
@@ -496,7 +515,7 @@ export async function wirePortProviders(
496
515
  options: { dryRun: boolean },
497
516
  ): Promise<{ updatedFiles: string[] }> {
498
517
  const portsFile = config.paths.ports;
499
- const infrastructureFile = config.paths.infrastructurePorts;
518
+ const infrastructureFile = config.paths.portWiring;
500
519
  const providersFile = providersFilePath(config);
501
520
  const manualFor = (wiring: PortProviderWiring) =>
502
521
  `Wire the ${wiring.portKey} port manually: add ${wiring.portKey}: ${wiring.portType}; to AppPorts in ${portsFile}, defer "${wiring.portKey}" in ${infrastructureFile}, and register ${wiring.providerFactory}() from ${wiring.providerModule} in ${providersFile}.`;
@@ -536,7 +555,7 @@ export async function wirePortProviders(
536
555
  );
537
556
  if (infrastructureOriginal === undefined) {
538
557
  throw new Error(
539
- `Could not find the generated infrastructure ports file ${infrastructureFile}. ${manualFor(active[0])}`,
558
+ `Could not find the generated port wiring file ${infrastructureFile}. ${manualFor(active[0])}`,
540
559
  );
541
560
  }
542
561
 
@@ -548,7 +567,7 @@ export async function wirePortProviders(
548
567
  !new RegExp(`["']${wiring.portKey}["']`).test(infrastructureNext)
549
568
  ) {
550
569
  throw new Error(
551
- `Could not find the deferred port list in ${infrastructureFile}. ${manualFor(wiring)} Or restore the generated infrastructure ports file before running beignet ${wiring.command}.`,
570
+ `Could not find the deferred port list in ${infrastructureFile}. ${manualFor(wiring)} Or restore the generated port wiring file before running beignet ${wiring.command}.`,
552
571
  );
553
572
  }
554
573
  infrastructureNext = appended;
@@ -651,7 +670,7 @@ export async function assertStandardApp(
651
670
  ): Promise<void> {
652
671
  const requiredFiles = [
653
672
  config.paths.appContext,
654
- config.paths.infrastructurePorts,
673
+ config.paths.portWiring,
655
674
  config.paths.ports,
656
675
  config.paths.server,
657
676
  config.paths.useCaseBuilder,
@@ -688,7 +707,7 @@ export async function assertTestApp(
688
707
  ): Promise<void> {
689
708
  const requiredFiles = [
690
709
  config.paths.appContext,
691
- config.paths.infrastructurePorts,
710
+ config.paths.portWiring,
692
711
  useCaseFilePath(names, config),
693
712
  ];
694
713
 
@@ -707,7 +726,7 @@ export async function assertPortApp(
707
726
  targetDir: string,
708
727
  config: ResolvedBeignetConfig,
709
728
  ): Promise<void> {
710
- const requiredFiles = [config.paths.ports, config.paths.infrastructurePorts];
729
+ const requiredFiles = [config.paths.ports, config.paths.portWiring];
711
730
 
712
731
  for (const file of requiredFiles) {
713
732
  try {
@@ -725,10 +744,7 @@ export async function assertAdapterApp(
725
744
  names: PortNames,
726
745
  config: ResolvedBeignetConfig,
727
746
  ): Promise<void> {
728
- const requiredFiles = [
729
- config.paths.infrastructurePorts,
730
- portFilePath(names, config),
731
- ];
747
+ const requiredFiles = [config.paths.portWiring, portFilePath(names, config)];
732
748
 
733
749
  for (const file of requiredFiles) {
734
750
  try {
@@ -740,16 +756,16 @@ export async function assertAdapterApp(
740
756
  }
741
757
  }
742
758
 
743
- const infrastructurePorts = await readFile(
744
- path.join(targetDir, config.paths.infrastructurePorts),
759
+ const portWiring = await readFile(
760
+ path.join(targetDir, config.paths.portWiring),
745
761
  "utf8",
746
762
  );
747
763
  if (
748
- !infrastructurePorts.includes(portStubEntry(names)) &&
749
- !infrastructurePorts.includes(adapterEntry(names))
764
+ !portWiring.includes(portStubEntry(names)) &&
765
+ !portWiring.includes(adapterEntry(names))
750
766
  ) {
751
767
  throw new Error(
752
- `Could not find the generated ${names.interfaceName} infrastructure stub in ${config.paths.infrastructurePorts}. Wire ${names.adapterFactoryName}() manually or restore the generated stub before running make adapter.`,
768
+ `Could not find the generated ${names.interfaceName} infrastructure stub in ${config.paths.portWiring}. Wire ${names.adapterFactoryName}() manually or restore the generated stub before running make adapter.`,
753
769
  );
754
770
  }
755
771
  }
@@ -822,7 +838,7 @@ export async function assertServerRuntimeApp(
822
838
  const requiredFiles = [
823
839
  config.paths.appContext,
824
840
  config.paths.server,
825
- config.paths.infrastructurePorts,
841
+ config.paths.portWiring,
826
842
  ];
827
843
 
828
844
  for (const file of requiredFiles) {
@@ -1254,11 +1270,11 @@ export async function updateInfrastructurePortStub(
1254
1270
  config: ResolvedBeignetConfig,
1255
1271
  options: { dryRun: boolean },
1256
1272
  ): Promise<boolean> {
1257
- const filePath = path.join(targetDir, config.paths.infrastructurePorts);
1273
+ const filePath = path.join(targetDir, config.paths.portWiring);
1258
1274
  const original = await readFile(filePath, "utf8");
1259
1275
  let next = original;
1260
1276
  const portPath = portFilePath(names, config);
1261
- const importLine = `import type { ${names.interfaceName} } from "${relativeModule(config.paths.infrastructurePorts, portPath)}";`;
1277
+ const importLine = `import type { ${names.interfaceName} } from "${relativeModule(config.paths.portWiring, portPath)}";`;
1262
1278
 
1263
1279
  if (!next.includes(importLine)) {
1264
1280
  next = insertAfterImports(next, importLine);
@@ -1273,7 +1289,7 @@ export async function updateInfrastructurePortStub(
1273
1289
  next = insertDefinePortsProperty(
1274
1290
  next,
1275
1291
  entry.trim(),
1276
- `Could not find definePorts({ in ${config.paths.infrastructurePorts}. Add the ${names.interfaceName} stub manually, or restore the generated infrastructure ports file before running make port.`,
1292
+ `Could not find definePorts({ in ${config.paths.portWiring}. Add the ${names.interfaceName} stub manually, or restore the generated port wiring file before running make port.`,
1277
1293
  );
1278
1294
  }
1279
1295
 
@@ -1288,18 +1304,18 @@ export async function updateInfrastructureAdapterWiring(
1288
1304
  config: ResolvedBeignetConfig,
1289
1305
  options: { dryRun: boolean },
1290
1306
  ): Promise<boolean> {
1291
- const filePath = path.join(targetDir, config.paths.infrastructurePorts);
1307
+ const filePath = path.join(targetDir, config.paths.portWiring);
1292
1308
  const original = await readFile(filePath, "utf8");
1293
1309
  let next = original;
1294
1310
  const adapterPath = adapterFilePath(names, config);
1295
- const adapterImportLine = `import { ${names.adapterFactoryName} } from "${relativeModule(config.paths.infrastructurePorts, adapterPath)}";`;
1311
+ const adapterImportLine = `import { ${names.adapterFactoryName} } from "${relativeModule(config.paths.portWiring, adapterPath)}";`;
1296
1312
 
1297
1313
  if (!next.includes(adapterImportLine)) {
1298
1314
  next = insertAfterImports(next, adapterImportLine);
1299
1315
  }
1300
1316
 
1301
1317
  const portPath = portFilePath(names, config);
1302
- const portImportLine = `import type { ${names.interfaceName} } from "${relativeModule(config.paths.infrastructurePorts, portPath)}";`;
1318
+ const portImportLine = `import type { ${names.interfaceName} } from "${relativeModule(config.paths.portWiring, portPath)}";`;
1303
1319
  if (next.includes(portImportLine)) {
1304
1320
  next = next.replace(`${portImportLine}\n`, "");
1305
1321
  }
@@ -1310,7 +1326,7 @@ export async function updateInfrastructureAdapterWiring(
1310
1326
  next = next.replace(stub, adapter);
1311
1327
  } else if (!next.includes(adapter)) {
1312
1328
  throw new Error(
1313
- `Could not find the generated ${names.interfaceName} infrastructure stub in ${config.paths.infrastructurePorts}. Wire ${names.adapterFactoryName}() manually or restore the generated stub before running make adapter.`,
1329
+ `Could not find the generated ${names.interfaceName} infrastructure stub in ${config.paths.portWiring}. Wire ${names.adapterFactoryName}() manually or restore the generated stub before running make adapter.`,
1314
1330
  );
1315
1331
  }
1316
1332
 
@@ -1325,15 +1341,15 @@ export async function updateInfrastructurePorts(
1325
1341
  config: ResolvedBeignetConfig,
1326
1342
  options: { dryRun: boolean },
1327
1343
  ): Promise<boolean> {
1328
- const filePath = path.join(targetDir, config.paths.infrastructurePorts);
1344
+ const filePath = path.join(targetDir, config.paths.portWiring);
1329
1345
  const original = await readFile(filePath, "utf8");
1330
1346
  let next = original;
1331
1347
  const repositoryPath = path.join(
1332
- path.dirname(config.paths.infrastructurePorts),
1348
+ path.dirname(config.paths.portWiring),
1333
1349
  names.pluralKebab,
1334
1350
  `in-memory-${names.singularKebab}-repository.ts`,
1335
1351
  );
1336
- const importLine = `import { createInMemory${names.singularPascal}Repository } from "${relativeModule(config.paths.infrastructurePorts, repositoryPath)}";`;
1352
+ const importLine = `import { createInMemory${names.singularPascal}Repository } from "${relativeModule(config.paths.portWiring, repositoryPath)}";`;
1337
1353
 
1338
1354
  if (!next.includes(importLine)) {
1339
1355
  next = insertAfterImports(next, importLine);
@@ -1343,9 +1359,9 @@ export async function updateInfrastructurePorts(
1343
1359
  if (!next.includes(repositoryConst)) {
1344
1360
  next = replaceRequired(
1345
1361
  next,
1346
- /(\nexport const appPorts = definePorts(?:<[^>]+>)?\((?:\)\()?\{)/,
1362
+ /(\nexport const initialPorts = definePorts(?:<[^>]+>)?\((?:\)\()?\{)/,
1347
1363
  `\n${repositoryConst}\n$1`,
1348
- `Could not find definePorts({ in ${config.paths.infrastructurePorts}. Add ${repositoryConst} manually, or restore the generated infrastructure ports file before running make resource.`,
1364
+ `Could not find definePorts({ in ${config.paths.portWiring}. Add ${repositoryConst} manually, or restore the generated port wiring file before running make resource.`,
1349
1365
  );
1350
1366
  }
1351
1367
 
@@ -1358,7 +1374,7 @@ export async function updateInfrastructurePorts(
1358
1374
  next = insertDefinePortsProperty(
1359
1375
  next,
1360
1376
  entry.trim(),
1361
- `Could not find definePorts({ in ${config.paths.infrastructurePorts}. Add ${entry.trim()} to appPorts manually, or restore the generated infrastructure ports file before running make resource.`,
1377
+ `Could not find definePorts({ in ${config.paths.portWiring}. Add ${entry.trim()} to initialPorts manually, or restore the generated port wiring file before running make resource.`,
1362
1378
  );
1363
1379
  }
1364
1380
 
@@ -1371,7 +1387,7 @@ export async function updateInfrastructurePorts(
1371
1387
  next = insertNoopUnitOfWorkProperty(
1372
1388
  next,
1373
1389
  transactionEntry.trim(),
1374
- `Could not find the generated Unit of Work port list in ${config.paths.infrastructurePorts}. Add ${transactionEntry.trim()} manually, or restore the generated infrastructure ports file before running make resource.`,
1390
+ `Could not find the generated Unit of Work port list in ${config.paths.portWiring}. Add ${transactionEntry.trim()} manually, or restore the generated port wiring file before running make resource.`,
1375
1391
  );
1376
1392
  }
1377
1393
 
@@ -1386,7 +1402,7 @@ export async function updateInfrastructureGatePolicies(
1386
1402
  config: ResolvedBeignetConfig,
1387
1403
  options: { dryRun: boolean },
1388
1404
  ): Promise<boolean> {
1389
- const filePath = path.join(targetDir, config.paths.infrastructurePorts);
1405
+ const filePath = path.join(targetDir, config.paths.portWiring);
1390
1406
  const original = await readFile(filePath, "utf8");
1391
1407
  let next = original;
1392
1408
  const policyPath = path.join(
@@ -1394,7 +1410,7 @@ export async function updateInfrastructureGatePolicies(
1394
1410
  names.pluralKebab,
1395
1411
  "policy.ts",
1396
1412
  );
1397
- const importLine = `import { ${names.singularCamel}Policy } from "${relativeModule(config.paths.infrastructurePorts, policyPath)}";`;
1413
+ const importLine = `import { ${names.singularCamel}Policy } from "${relativeModule(config.paths.portWiring, policyPath)}";`;
1398
1414
 
1399
1415
  if (!next.includes(importLine)) {
1400
1416
  next = insertAfterImports(next, importLine);
@@ -1403,7 +1419,7 @@ export async function updateInfrastructureGatePolicies(
1403
1419
  next = appendCreateGatePolicy(
1404
1420
  next,
1405
1421
  `${names.singularCamel}Policy`,
1406
- `Could not find createGate({ policies: [...] }) in ${config.paths.infrastructurePorts}. Add ${names.singularCamel}Policy to the gate policies manually, or restore the generated infrastructure ports file before running make resource --auth.`,
1422
+ `Could not find createGate({ policies: [...] }) in ${config.paths.portWiring}. Add ${names.singularCamel}Policy to the gate policies manually, or restore the generated port wiring file before running make resource --auth.`,
1407
1423
  );
1408
1424
 
1409
1425
  if (next === original) return false;
@@ -1713,7 +1729,7 @@ export async function updateInfrastructureDeferredPorts(
1713
1729
  config: ResolvedBeignetConfig,
1714
1730
  options: { dryRun: boolean },
1715
1731
  ): Promise<boolean> {
1716
- const filePath = path.join(targetDir, config.paths.infrastructurePorts);
1732
+ const filePath = path.join(targetDir, config.paths.portWiring);
1717
1733
  const original = await readFile(filePath, "utf8");
1718
1734
  // Drizzle resources are provided by the app database provider at startup,
1719
1735
  // so deferred-form ports files defer the new repository key. Identity-form
@@ -1745,11 +1761,11 @@ export async function updateServerTransactionPorts(
1745
1761
 
1746
1762
  if (!/\bappPorts\b/.test(next)) {
1747
1763
  throw new Error(
1748
- `Could not find appPorts in ${config.paths.server}. Add ${names.pluralCamel}: appPorts.${names.pluralCamel} to createTransactionPorts manually, or restore the generated server file before running make resource.`,
1764
+ `Could not find initialPorts in ${config.paths.server}. Add ${names.pluralCamel}: initialPorts.${names.pluralCamel} to createTransactionPorts manually, or restore the generated server file before running make resource.`,
1749
1765
  );
1750
1766
  }
1751
1767
 
1752
- const entry = `\t\t\t\t\t\t${names.pluralCamel}: appPorts.${names.pluralCamel},`;
1768
+ const entry = `\t\t\t\t\t\t${names.pluralCamel}: initialPorts.${names.pluralCamel},`;
1753
1769
  next = insertDrizzleTransactionPortsProperty(
1754
1770
  next,
1755
1771
  entry.trim(),
@@ -3048,14 +3064,14 @@ export function adapterFilePath(
3048
3064
  config: ResolvedBeignetConfig,
3049
3065
  ): string {
3050
3066
  return path.join(
3051
- path.dirname(config.paths.infrastructurePorts),
3067
+ path.dirname(config.paths.portWiring),
3052
3068
  names.kebab,
3053
3069
  `${names.kebab}-adapter.ts`,
3054
3070
  );
3055
3071
  }
3056
3072
 
3057
3073
  export function infrastructureDir(config: ResolvedBeignetConfig): string {
3058
- return path.dirname(config.paths.infrastructurePorts);
3074
+ return path.dirname(config.paths.portWiring);
3059
3075
  }
3060
3076
 
3061
3077
  export function drizzleSchemaIndexPath(config: ResolvedBeignetConfig): string {
@@ -330,7 +330,7 @@ export async function updateTenancyWiring(
330
330
  if (
331
331
  await updateWorkspaceInfrastructurePorts(targetDir, config, editOptions)
332
332
  ) {
333
- updated.add(config.paths.infrastructurePorts);
333
+ updated.add(config.paths.portWiring);
334
334
  }
335
335
  if (
336
336
  await updateDrizzleSchemaIndexExports(
@@ -524,7 +524,7 @@ export async function updateWorkspaceInfrastructurePorts(
524
524
  config: ResolvedBeignetConfig,
525
525
  options: { dryRun: boolean },
526
526
  ): Promise<boolean> {
527
- const filePath = path.join(targetDir, config.paths.infrastructurePorts);
527
+ const filePath = path.join(targetDir, config.paths.portWiring);
528
528
  const original = await readFile(filePath, "utf8");
529
529
  let next = original;
530
530
 
@@ -532,21 +532,21 @@ export async function updateWorkspaceInfrastructurePorts(
532
532
  const appended = appendDeferredPortKey(next, portKey);
533
533
  if (appended === next && !new RegExp(`["']${portKey}["']`).test(next)) {
534
534
  throw new Error(
535
- `Could not find the deferred port list in ${config.paths.infrastructurePorts}. Add "${portKey}" manually, or restore the generated infrastructure ports file before running make tenancy.`,
535
+ `Could not find the deferred port list in ${config.paths.portWiring}. Add "${portKey}" manually, or restore the generated port wiring file before running make tenancy.`,
536
536
  );
537
537
  }
538
538
  next = appended;
539
539
  }
540
540
 
541
541
  const policyModule = relativeModule(
542
- config.paths.infrastructurePorts,
542
+ config.paths.portWiring,
543
543
  path.join(config.paths.features, "workspaces/policy.ts"),
544
544
  );
545
545
  next = addNamedImport(next, "workspacePolicy", policyModule);
546
546
  next = appendCreateGatePolicy(
547
547
  next,
548
548
  "workspacePolicy",
549
- `Could not find createGate({ policies: [...] }) in ${config.paths.infrastructurePorts}. Add workspacePolicy to the gate policies manually, or restore the generated infrastructure ports file before running make tenancy.`,
549
+ `Could not find createGate({ policies: [...] }) in ${config.paths.portWiring}. Add workspacePolicy to the gate policies manually, or restore the generated port wiring file before running make tenancy.`,
550
550
  );
551
551
 
552
552
  if (next === original) return false;
@@ -2274,7 +2274,7 @@ import type {
2274
2274
  \tWorkspaceMemberWithUser,
2275
2275
  \tWorkspaceRepository,
2276
2276
  } from "@/features/workspaces/ports";
2277
- import { appPorts } from "@/infra/app-ports";
2277
+ import { initialPorts } from "@/infra/port-wiring";
2278
2278
  import type { AppTransactionPorts } from "@/ports";
2279
2279
 
2280
2280
  export type MemoryWorkspacePorts = {
@@ -2468,9 +2468,9 @@ export type WorkspaceTestFixture = TestPortsFixture<
2468
2468
  export function createWorkspaceFixture(): WorkspaceTestFixture {
2469
2469
  \tconst repositories = createMemoryWorkspacePorts();
2470
2470
  \tconst fixture = createTestPorts<AppContext["ports"], AppTransactionPorts>({
2471
- \t\tbase: appPorts,
2471
+ \t\tbase: initialPorts,
2472
2472
  \t\toverrides: {
2473
- \t\t\tgate: appPorts.gate,
2473
+ \t\t\tgate: initialPorts.gate,
2474
2474
  \t\t\t...repositories,
2475
2475
  \t\t},
2476
2476
  \t});
package/src/make.ts CHANGED
@@ -911,7 +911,7 @@ export async function makePort(
911
911
  dryRun: Boolean(options.dryRun),
912
912
  })
913
913
  ) {
914
- updatedFiles.push(config.paths.infrastructurePorts);
914
+ updatedFiles.push(config.paths.portWiring);
915
915
  }
916
916
 
917
917
  return {
@@ -957,7 +957,7 @@ export async function makeAdapter(
957
957
  dryRun: Boolean(options.dryRun),
958
958
  })
959
959
  ) {
960
- updatedFiles.push(config.paths.infrastructurePorts);
960
+ updatedFiles.push(config.paths.portWiring);
961
961
  }
962
962
 
963
963
  return {
@@ -1034,6 +1034,9 @@ export async function makeEvent(
1034
1034
  const outboxDrainPlan = await planOutboxDrainWiring(targetDir, config, {
1035
1035
  force: Boolean(options.force),
1036
1036
  });
1037
+ await updateOutboxRegistry(targetDir, names, "events", config, {
1038
+ dryRun: true,
1039
+ });
1037
1040
 
1038
1041
  const result = await makeFeatureArtifact({
1039
1042
  targetDir,
@@ -1078,6 +1081,9 @@ export async function makeJob(options: MakeJobOptions): Promise<MakeJobResult> {
1078
1081
  const outboxDrainPlan = await planOutboxDrainWiring(targetDir, config, {
1079
1082
  force: Boolean(options.force),
1080
1083
  });
1084
+ await updateOutboxRegistry(targetDir, names, "jobs", config, {
1085
+ dryRun: true,
1086
+ });
1081
1087
 
1082
1088
  const result = await makeFeatureArtifact({
1083
1089
  targetDir,
@@ -1697,7 +1703,7 @@ async function updateOutboxPortWiring(
1697
1703
  changes.updatedFiles.push(config.paths.ports);
1698
1704
  }
1699
1705
  if (await updateOutboxInfrastructurePorts(targetDir, config, options)) {
1700
- changes.updatedFiles.push(config.paths.infrastructurePorts);
1706
+ changes.updatedFiles.push(config.paths.portWiring);
1701
1707
  }
1702
1708
  if (await updateOutboxDatabaseProvider(targetDir, config, options)) {
1703
1709
  changes.updatedFiles.push(
@@ -1805,7 +1811,7 @@ async function updateOutboxInfrastructurePorts(
1805
1811
  config: ResolvedBeignetConfig,
1806
1812
  options: { dryRun: boolean },
1807
1813
  ): Promise<boolean> {
1808
- const filePath = path.join(targetDir, config.paths.infrastructurePorts);
1814
+ const filePath = path.join(targetDir, config.paths.portWiring);
1809
1815
  const original = await readFile(filePath, "utf8");
1810
1816
  let next = appendDeferredPortKey(original, "outbox");
1811
1817
  next = appendDeferredPortKey(next, "outboxAdmin");
@@ -2166,7 +2172,7 @@ export async function makeOutbox(
2166
2172
  ...cronSecretPlan.files.map((file) => file.path),
2167
2173
  ...cronSecretPlan.skippedFiles,
2168
2174
  config.paths.ports,
2169
- config.paths.infrastructurePorts,
2175
+ config.paths.portWiring,
2170
2176
  path.join(infrastructureDir(config), "db/provider.ts"),
2171
2177
  drizzleRepositoriesPath(config),
2172
2178
  outboxDrizzleSchemaFilePath(config),
@@ -2361,19 +2367,19 @@ async function updateResourceWiring(
2361
2367
  persistence === "memory" &&
2362
2368
  (await updateInfrastructurePorts(targetDir, names, config, options))
2363
2369
  ) {
2364
- updated.add(config.paths.infrastructurePorts);
2370
+ updated.add(config.paths.portWiring);
2365
2371
  }
2366
2372
  if (
2367
2373
  generationOptions.auth &&
2368
2374
  (await updateInfrastructureGatePolicies(targetDir, names, config, options))
2369
2375
  ) {
2370
- updated.add(config.paths.infrastructurePorts);
2376
+ updated.add(config.paths.portWiring);
2371
2377
  }
2372
2378
  if (
2373
2379
  persistence === "drizzle" &&
2374
2380
  (await updateInfrastructureDeferredPorts(targetDir, names, config, options))
2375
2381
  ) {
2376
- updated.add(config.paths.infrastructurePorts);
2382
+ updated.add(config.paths.portWiring);
2377
2383
  }
2378
2384
  if (
2379
2385
  persistence === "drizzle" &&
@@ -3381,7 +3387,13 @@ async function updateOutboxRegistry(
3381
3387
  importLine,
3382
3388
  );
3383
3389
 
3384
- if (appended.kind === "missing" || appended.kind === "unchanged") {
3390
+ if (appended.kind === "missing") {
3391
+ throw new Error(
3392
+ `Could not find the ${kind} array in ${config.paths.outbox}. Register ${registryName} manually, or restore the generated outbox file before running this command.`,
3393
+ );
3394
+ }
3395
+
3396
+ if (appended.kind === "unchanged") {
3385
3397
  return "skipped";
3386
3398
  }
3387
3399
 
@@ -3988,6 +4000,7 @@ export const outboxMessages = mysqlTable(
3988
4000
  \t\tkind: varchar("kind", { length: 32 }).notNull(),
3989
4001
  \t\tname: varchar("name", { length: 255 }).notNull(),
3990
4002
  \t\tpayloadJson: longtext("payload_json").notNull(),
4003
+ \t\ttraceContextJson: longtext("trace_context_json"),
3991
4004
  \t\tstatus: varchar("status", { length: 32 }).notNull(),
3992
4005
  \t\tattempts: int("attempts").notNull().default(0),
3993
4006
  \t\tmaxAttempts: int("max_attempts").notNull().default(3),
@@ -4023,6 +4036,7 @@ export const outboxMessages = ${dialect.tableFunction}(
4023
4036
  \t\tkind: text("kind").notNull(),
4024
4037
  \t\tname: text("name").notNull(),
4025
4038
  \t\tpayloadJson: text("payload_json").notNull(),
4039
+ \t\ttraceContextJson: text("trace_context_json"),
4026
4040
  \t\tstatus: text("status").notNull(),
4027
4041
  \t\tattempts: integer("attempts").notNull().default(0),
4028
4042
  \t\tmaxAttempts: integer("max_attempts").notNull().default(3),
@@ -4503,15 +4517,15 @@ import { createInMemoryDevtools } from "@beignet/devtools";
4503
4517
  import { createTestContextFactory, createTestPorts } from "@beignet/core/testing";
4504
4518
  import { createTestAnonymousActor } from "@beignet/core/testing";
4505
4519
  import type { AppContext } from "${aliasModule(config.paths.appContext)}";
4506
- import { appPorts } from "${aliasModule(config.paths.infrastructurePorts)}";
4520
+ import { initialPorts } from "${aliasModule(config.paths.portWiring)}";
4507
4521
  import { ${names.exportName} } from "${relativeModule(filePath, useCaseFilePath(names, config))}";
4508
4522
 
4509
4523
  describe("${names.exportName}", () => {
4510
4524
  it("runs ${names.action.camel}", async () => {
4511
4525
  const testFixture = createTestPorts<AppContext["ports"]>({
4512
- base: appPorts,
4526
+ base: initialPorts,
4513
4527
  overrides: {
4514
- gate: appPorts.gate,
4528
+ gate: initialPorts.gate,
4515
4529
  devtools: createInMemoryDevtools(),
4516
4530
  },
4517
4531
  });
@@ -5276,7 +5290,7 @@ function testFile(
5276
5290
  options: ResourceGenerationOptions,
5277
5291
  ): string {
5278
5292
  const repositoryPath = path.join(
5279
- path.dirname(config.paths.infrastructurePorts),
5293
+ path.dirname(config.paths.portWiring),
5280
5294
  names.pluralKebab,
5281
5295
  `in-memory-${names.singularKebab}-repository.ts`,
5282
5296
  );
@@ -5294,7 +5308,7 @@ import { createInMemoryDevtools } from "@beignet/devtools";
5294
5308
  import { createTestContextFactory, createTestPorts } from "@beignet/core/testing";
5295
5309
  import { ${options.auth ? "createTestUserActor" : "createTestAnonymousActor"}${options.tenant ? ", createTestTenant" : ""} } from "@beignet/core/testing";
5296
5310
  import type { AppContext } from "${aliasModule(config.paths.appContext)}";
5297
- import { appPorts } from "${aliasModule(config.paths.infrastructurePorts)}";
5311
+ import { initialPorts } from "${aliasModule(config.paths.portWiring)}";
5298
5312
  import { appContext } from "${aliasModule(serverContextPath)}";
5299
5313
  import {
5300
5314
  create${names.singularPascal},
@@ -5334,7 +5348,7 @@ ${options.tenant ? `\t\t\t\ttenantId: "tenant_test",\n` : ""} name: "Gamma ${
5334
5348
  ${options.tenant ? `\t\t\t{\n\t\t\t\tid: "00000000-0000-4000-8000-000000000104",\n\t\t\t\ttenantId: "tenant_other",\n\t\t\t\tname: "Other Tenant ${names.singularPascal}",\n\t\t\t\tversion: 1,\n\t\t\t\tcreatedAt: "2024-01-04T00:00:00.000Z",\n\t\t\t\tupdatedAt: "2024-01-04T00:00:00.000Z",\n\t\t\t},\n` : ""} ];
5335
5349
  const ${names.pluralCamel} = createInMemory${names.singularPascal}Repository(seed${names.pluralPascal});
5336
5350
  const testFixture = createTestPorts<AppContext["ports"]>({
5337
- base: appPorts,
5351
+ base: initialPorts,
5338
5352
  overrides: {
5339
5353
  ${names.pluralCamel},
5340
5354
  ${
@@ -5345,7 +5359,7 @@ ${
5345
5359
  ${options.tenant ? `\t\t\t\t\t\tsession: { tenantId: "tenant_test" },\n` : ""}
5346
5360
  }),
5347
5361
  },
5348
- ${options.auth ? `\t\t\t\tgate: appPorts.gate,\n` : ""}
5362
+ ${options.auth ? `\t\t\t\tgate: initialPorts.gate,\n` : ""}
5349
5363
  `
5350
5364
  : `\t\t\t\tauth: {
5351
5365
  getSession: async () => null,
@@ -0,0 +1,60 @@
1
+ import type {
2
+ ErrorReporterPort,
3
+ ErrorReportOptions,
4
+ } from "@beignet/core/error-reporting";
5
+
6
+ export type OperationalErrorReportingContext = {
7
+ requestId?: string;
8
+ traceId?: string;
9
+ ports?: {
10
+ errorReporter?: ErrorReporterPort;
11
+ };
12
+ };
13
+
14
+ export async function reportOperationalFailure(args: {
15
+ ctx: OperationalErrorReportingContext;
16
+ error: unknown;
17
+ reportOptions: ErrorReportOptions;
18
+ }): Promise<void> {
19
+ const reporter = args.ctx.ports?.errorReporter;
20
+ if (!reporter) return;
21
+
22
+ try {
23
+ const { tryReportException } = await import(
24
+ "@beignet/core/error-reporting"
25
+ );
26
+ await tryReportException({
27
+ reporter,
28
+ error: args.error,
29
+ reportOptions: {
30
+ requestId: args.ctx.requestId,
31
+ traceId: args.ctx.traceId,
32
+ ...args.reportOptions,
33
+ },
34
+ });
35
+ } catch {
36
+ // Reporting must not replace the operational command failure.
37
+ }
38
+ }
39
+
40
+ export async function flushOperationalErrorReporter(
41
+ ctx: OperationalErrorReportingContext,
42
+ timeoutMs = 1000,
43
+ ): Promise<void> {
44
+ const reporter = ctx.ports?.errorReporter;
45
+ if (!reporter) return;
46
+
47
+ let timeout: ReturnType<typeof setTimeout> | undefined;
48
+ try {
49
+ await Promise.race([
50
+ reporter.flush({ timeoutMs }),
51
+ new Promise<boolean>((resolve) => {
52
+ timeout = setTimeout(() => resolve(false), timeoutMs);
53
+ }),
54
+ ]);
55
+ } catch {
56
+ // Reporting shutdown must not replace the operational command result.
57
+ } finally {
58
+ if (timeout !== undefined) clearTimeout(timeout);
59
+ }
60
+ }