@beignet/cli 0.0.37 → 0.0.39

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 (113) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +138 -101
  3. package/dist/choices.d.ts +17 -21
  4. package/dist/choices.d.ts.map +1 -1
  5. package/dist/choices.js +21 -67
  6. package/dist/choices.js.map +1 -1
  7. package/dist/config.d.ts +1 -1
  8. package/dist/config.d.ts.map +1 -1
  9. package/dist/config.js +1 -1
  10. package/dist/config.js.map +1 -1
  11. package/dist/create-prompts.d.ts +5 -6
  12. package/dist/create-prompts.d.ts.map +1 -1
  13. package/dist/create-prompts.js +15 -15
  14. package/dist/create-prompts.js.map +1 -1
  15. package/dist/create.d.ts +11 -2
  16. package/dist/create.d.ts.map +1 -1
  17. package/dist/create.js +38 -28
  18. package/dist/create.js.map +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +149 -47
  21. package/dist/index.js.map +1 -1
  22. package/dist/inspect.js +69 -16
  23. package/dist/inspect.js.map +1 -1
  24. package/dist/lib.d.ts +1 -1
  25. package/dist/lib.d.ts.map +1 -1
  26. package/dist/lint.d.ts.map +1 -1
  27. package/dist/lint.js +27 -6
  28. package/dist/lint.js.map +1 -1
  29. package/dist/make/inbox.js +10 -10
  30. package/dist/make/inbox.js.map +1 -1
  31. package/dist/make/payments.d.ts +2 -1
  32. package/dist/make/payments.d.ts.map +1 -1
  33. package/dist/make/payments.js +18 -14
  34. package/dist/make/payments.js.map +1 -1
  35. package/dist/make/shared.d.ts +11 -4
  36. package/dist/make/shared.d.ts.map +1 -1
  37. package/dist/make/shared.js +56 -45
  38. package/dist/make/shared.js.map +1 -1
  39. package/dist/make/tenancy.js +8 -8
  40. package/dist/make/tenancy.js.map +1 -1
  41. package/dist/make.d.ts +3 -2
  42. package/dist/make.d.ts.map +1 -1
  43. package/dist/make.js +481 -200
  44. package/dist/make.js.map +1 -1
  45. package/dist/mcp.d.ts.map +1 -1
  46. package/dist/mcp.js +15 -11
  47. package/dist/mcp.js.map +1 -1
  48. package/dist/operational-error-reporting.d.ts +15 -0
  49. package/dist/operational-error-reporting.d.ts.map +1 -0
  50. package/dist/operational-error-reporting.js +42 -0
  51. package/dist/operational-error-reporting.js.map +1 -0
  52. package/dist/outbox.d.ts.map +1 -1
  53. package/dist/outbox.js +73 -0
  54. package/dist/outbox.js.map +1 -1
  55. package/dist/provider-add.d.ts +21 -3
  56. package/dist/provider-add.d.ts.map +1 -1
  57. package/dist/provider-add.js +117 -81
  58. package/dist/provider-add.js.map +1 -1
  59. package/dist/provider-audit.js +3 -3
  60. package/dist/provider-audit.js.map +1 -1
  61. package/dist/schedule.d.ts.map +1 -1
  62. package/dist/schedule.js +28 -1
  63. package/dist/schedule.js.map +1 -1
  64. package/dist/task.d.ts.map +1 -1
  65. package/dist/task.js +38 -5
  66. package/dist/task.js.map +1 -1
  67. package/dist/templates/base.d.ts +1 -1
  68. package/dist/templates/base.d.ts.map +1 -1
  69. package/dist/templates/base.js +15 -15
  70. package/dist/templates/base.js.map +1 -1
  71. package/dist/templates/index.d.ts +2 -2
  72. package/dist/templates/index.d.ts.map +1 -1
  73. package/dist/templates/index.js +7 -7
  74. package/dist/templates/index.js.map +1 -1
  75. package/dist/templates/server.d.ts +1 -1
  76. package/dist/templates/server.d.ts.map +1 -1
  77. package/dist/templates/server.js +22 -18
  78. package/dist/templates/server.js.map +1 -1
  79. package/dist/templates/shared.d.ts +4 -4
  80. package/dist/templates/shared.d.ts.map +1 -1
  81. package/dist/templates/shared.js +6 -6
  82. package/dist/templates/shared.js.map +1 -1
  83. package/dist/templates/testing.js +1 -1
  84. package/dist/templates/todos.js +6 -6
  85. package/package.json +4 -4
  86. package/skills/app-structure/SKILL.md +13 -8
  87. package/src/choices.ts +38 -88
  88. package/src/config.ts +2 -2
  89. package/src/create-prompts.ts +20 -21
  90. package/src/create.ts +54 -36
  91. package/src/index.ts +193 -65
  92. package/src/inspect.ts +116 -18
  93. package/src/lib.ts +1 -1
  94. package/src/lint.ts +35 -4
  95. package/src/make/inbox.ts +10 -10
  96. package/src/make/payments.ts +26 -18
  97. package/src/make/shared.ts +72 -50
  98. package/src/make/tenancy.ts +8 -8
  99. package/src/make.ts +607 -191
  100. package/src/mcp.ts +20 -13
  101. package/src/operational-error-reporting.ts +60 -0
  102. package/src/outbox.ts +77 -0
  103. package/src/provider-add.ts +179 -95
  104. package/src/provider-audit.ts +3 -3
  105. package/src/schedule.ts +32 -1
  106. package/src/task.ts +42 -5
  107. package/src/templates/base.ts +16 -16
  108. package/src/templates/index.ts +8 -8
  109. package/src/templates/server.ts +22 -18
  110. package/src/templates/shared.ts +10 -10
  111. package/src/templates/testing.ts +1 -1
  112. package/src/templates/todos.ts +6 -6
  113. package/src/test-helpers/generated-app.ts +1 -0
package/src/mcp.ts CHANGED
@@ -78,13 +78,13 @@ const makeInputSchema = {
78
78
  .string()
79
79
  .optional()
80
80
  .describe(
81
- "Artifact name. Required except for inbox, outbox, payments, and tenancy. Feature-owned artifacts use feature-scoped paths such as posts/backfill.",
81
+ "Artifact name. Required except for inbox, outbox, payments, and tenancy. Feature-owned artifacts use identities such as posts.backfill.",
82
82
  ),
83
83
  with: z
84
84
  .array(z.enum(makeFeatureAddonChoices))
85
85
  .optional()
86
86
  .describe(
87
- "feature only: add feature-owned artifacts such as policy, factories, seeds, events, listeners, jobs, notifications, schedules, tasks, ui, and uploads.",
87
+ "feature only: add feature-owned artifacts such as policy, factory, seed, event, listener, job, notification, schedule, task, ui, and upload.",
88
88
  ),
89
89
  recipe: z
90
90
  .enum(makeFeatureRecipeChoices)
@@ -94,7 +94,7 @@ const makeInputSchema = {
94
94
  .string()
95
95
  .optional()
96
96
  .describe(
97
- "listener only (required): event to listen to, for example posts/published.",
97
+ "listener only (required): event to listen to, for example posts.published.",
98
98
  ),
99
99
  cron: z
100
100
  .string()
@@ -108,13 +108,19 @@ const makeInputSchema = {
108
108
  .boolean()
109
109
  .optional()
110
110
  .describe("schedule only: generate a Next.js cron route for the schedule."),
111
- auth: z
111
+ ui: z
112
+ .boolean()
113
+ .optional()
114
+ .describe(
115
+ "upload only: generate a typed React upload client and component.",
116
+ ),
117
+ authorization: z
112
118
  .boolean()
113
119
  .optional()
114
120
  .describe(
115
121
  "resource only: generate policy metadata and use-case authorization checks for mutating routes.",
116
122
  ),
117
- tenant: z
123
+ tenantScoped: z
118
124
  .boolean()
119
125
  .optional()
120
126
  .describe(
@@ -163,8 +169,9 @@ type MakeToolInput = {
163
169
  cron?: string;
164
170
  timezone?: string;
165
171
  route?: boolean;
166
- auth?: boolean;
167
- tenant?: boolean;
172
+ ui?: boolean;
173
+ authorization?: boolean;
174
+ tenantScoped?: boolean;
168
175
  events?: boolean;
169
176
  softDelete?: boolean;
170
177
  force?: boolean;
@@ -268,7 +275,7 @@ async function runMakeTool(
268
275
  case "listener":
269
276
  if (!input.event) {
270
277
  throw new Error(
271
- "make listener requires an event, for example event: posts/published.",
278
+ "make listener requires an event, for example event: posts.published.",
272
279
  );
273
280
  }
274
281
  return makeListener({ ...base, event: input.event });
@@ -281,8 +288,8 @@ async function runMakeTool(
281
288
  case "resource":
282
289
  return makeResource({
283
290
  ...base,
284
- auth: input.auth,
285
- tenant: input.tenant,
291
+ authorization: input.authorization,
292
+ tenantScoped: input.tenantScoped,
286
293
  events: input.events,
287
294
  softDelete: input.softDelete,
288
295
  });
@@ -300,7 +307,7 @@ async function runMakeTool(
300
307
  case "test":
301
308
  return makeTest(base);
302
309
  case "upload":
303
- return makeUpload(base);
310
+ return makeUpload({ ...base, ui: input.ui });
304
311
  case "use-case":
305
312
  return makeUseCase(base);
306
313
  }
@@ -418,7 +425,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
418
425
  "make",
419
426
  {
420
427
  description:
421
- "Generate Beignet app files for one artifact: adapter, contract, event, factory, feature, inbox, job, listener, notification, outbox, payments, policy, port, resource, schedule, seed, task, tenancy, test, upload, or use-case. Generators auto-register artifacts, so doctor stays clean. Options by artifact: feature accepts with (addons); resource accepts auth, tenant, events, softDelete; listener requires event; schedule accepts cron, timezone, route. All artifacts accept force and dryRun. Returns created, updated, and skipped files as JSON.",
428
+ "Generate Beignet app files for one artifact: adapter, contract, event, factory, feature, inbox, job, listener, notification, outbox, payments, policy, port, resource, schedule, seed, task, tenancy, test, upload, or use-case. Generators auto-register artifacts, so doctor stays clean. Options by artifact: feature accepts with (addons); resource accepts authorization, tenantScoped, events, softDelete; listener requires event; schedule accepts cron, timezone, route; upload accepts ui. All artifacts accept force and dryRun. Returns created, updated, and skipped files as JSON.",
422
429
  inputSchema: makeInputSchema,
423
430
  },
424
431
  async (input) =>
@@ -430,7 +437,7 @@ export function buildBeignetMcpServer(options: McpServerOptions): McpServer {
430
437
  server.registerTool(
431
438
  "provider_add",
432
439
  {
433
- description: `Add a Beignet provider setup preset to this app. Updates dependencies, provider registration, app port wiring, .env.example, and docs/integrations.md. Presets: ${providerPresetDescription}. Supports dryRun.`,
440
+ description: `Add a Beignet provider setup preset to this app. Updates dependencies, provider registration, app port wiring, .env.example, and docs/providers.md. Presets: ${providerPresetDescription}. Supports dryRun.`,
434
441
  inputSchema: providerAddInputSchema,
435
442
  },
436
443
  async (input) =>
@@ -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
+ }
package/src/outbox.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { readFile, stat } from "node:fs/promises";
2
2
  import path from "node:path";
3
+ import type { ErrorReporterPort } from "@beignet/core/error-reporting";
3
4
  import type {
4
5
  DrainOutboxOptions,
5
6
  DrainOutboxResult,
@@ -11,6 +12,10 @@ import type {
11
12
  } from "@beignet/core/outbox";
12
13
  import { createJiti } from "jiti";
13
14
  import { loadBeignetConfig, normalizePath } from "./config.js";
15
+ import {
16
+ flushOperationalErrorReporter,
17
+ reportOperationalFailure,
18
+ } from "./operational-error-reporting.js";
14
19
 
15
20
  /**
16
21
  * Options for running one bounded outbox drain pass.
@@ -159,6 +164,7 @@ type OutboxDrainPorts = {
159
164
  logger?: OutboxDrainLogger;
160
165
  devtools?: OutboxDrainInstrumentation;
161
166
  instrumentation?: OutboxDrainInstrumentation;
167
+ errorReporter?: ErrorReporterPort;
162
168
  };
163
169
 
164
170
  type OutboxDrainContext = {
@@ -240,6 +246,60 @@ export async function runOutboxDrain(
240
246
  attempts: message.attempts,
241
247
  });
242
248
  },
249
+ onDeadLetter(error, message) {
250
+ return reportOperationalFailure({
251
+ ctx,
252
+ error,
253
+ reportOptions: {
254
+ level: "error",
255
+ mechanism: "beignet.outbox.cli",
256
+ handled: false,
257
+ tags: {
258
+ "beignet.kind": "outbox",
259
+ "beignet.outbox.kind": message.kind,
260
+ "beignet.outbox.name": message.name,
261
+ "beignet.outbox.outcome": "deadLettered",
262
+ },
263
+ contexts: {
264
+ outbox: {
265
+ messageId: message.id,
266
+ kind: message.kind,
267
+ name: message.name,
268
+ attempts: message.attempts,
269
+ maxAttempts: message.maxAttempts,
270
+ outcome: "deadLettered",
271
+ },
272
+ },
273
+ },
274
+ });
275
+ },
276
+ onSettlementError(settlementError, message) {
277
+ return reportOperationalFailure({
278
+ ctx,
279
+ error: settlementError,
280
+ reportOptions: {
281
+ level: "error",
282
+ mechanism: "beignet.outbox.cli",
283
+ handled: false,
284
+ tags: {
285
+ "beignet.kind": "outbox",
286
+ "beignet.outbox.kind": message.kind,
287
+ "beignet.outbox.name": message.name,
288
+ "beignet.outbox.outcome": "settlementFailed",
289
+ },
290
+ contexts: {
291
+ outbox: {
292
+ messageId: message.id,
293
+ kind: message.kind,
294
+ name: message.name,
295
+ attempts: message.attempts,
296
+ maxAttempts: message.maxAttempts,
297
+ outcome: "settlementFailed",
298
+ },
299
+ },
300
+ },
301
+ });
302
+ },
243
303
  });
244
304
 
245
305
  await recordOutboxDrain(ctx, result);
@@ -251,7 +311,24 @@ export async function runOutboxDrain(
251
311
  result,
252
312
  durationMs: Math.round(performance.now() - startedAt),
253
313
  };
314
+ } catch (error) {
315
+ await reportOperationalFailure({
316
+ ctx,
317
+ error,
318
+ reportOptions: {
319
+ level: "error",
320
+ mechanism: "beignet.outbox.cli",
321
+ handled: false,
322
+ tags: {
323
+ "beignet.kind": "outbox",
324
+ "beignet.outbox.outcome": "drainFailed",
325
+ },
326
+ contexts: { outbox: { outcome: "drainFailed" } },
327
+ },
328
+ });
329
+ throw error;
254
330
  } finally {
331
+ await flushOperationalErrorReporter(ctx);
255
332
  await outboxModule.stopOutboxDrainContext?.(ctx, contextArgs);
256
333
  }
257
334
  }