@mettlecast/domain-cli 0.2.60 → 0.2.62

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 (107) hide show
  1. package/dist/builder/build-registry.d.ts +1 -1
  2. package/dist/builder/build-registry.js +1 -36
  3. package/dist/builder/build-types.d.ts +1 -1
  4. package/dist/builder/load-module.d.ts +1 -1
  5. package/dist/cli.js +21 -3
  6. package/dist/commands/add-api.js +2 -2
  7. package/dist/commands/add-domain.js +4 -4
  8. package/dist/commands/add-fixture-factory.js +5 -6
  9. package/dist/commands/build-catalog.d.ts +6 -22
  10. package/dist/commands/build-catalog.js +7 -18
  11. package/dist/commands/build.js +2 -1
  12. package/dist/commands/check-hashes.d.ts +2 -0
  13. package/dist/commands/check-hashes.js +8 -0
  14. package/dist/commands/dev.js +1 -1
  15. package/dist/commands/doctor.js +70 -41
  16. package/dist/commands/explain.js +13 -13
  17. package/dist/commands/generate-openapi.d.ts +10 -1
  18. package/dist/commands/generate-openapi.js +19 -33
  19. package/dist/commands/regenerate-modules-hashes.d.ts +25 -0
  20. package/dist/commands/regenerate-modules-hashes.js +58 -0
  21. package/dist/commands/show.d.ts +2 -3
  22. package/dist/commands/show.js +0 -2
  23. package/dist/commands/test.js +0 -1
  24. package/dist/commands/update-all.d.ts +21 -0
  25. package/dist/commands/update-all.js +62 -0
  26. package/dist/commands/upgrade-backend.js +3 -3
  27. package/dist/commands/validate.js +12 -90
  28. package/dist/server/api-server.d.ts +1 -1
  29. package/dist/server/mount-routes.d.ts +11 -2
  30. package/dist/server/mount-routes.js +20 -8
  31. package/dist/templates/api-skeleton.d.ts +5 -0
  32. package/dist/templates/api-skeleton.js +28 -27
  33. package/dist/templates/claude-md.js +1 -1
  34. package/dist/templates/patterns/api/create-with-event.d.ts +4 -0
  35. package/dist/templates/patterns/api/create-with-event.js +38 -32
  36. package/dist/templates/patterns/api/idempotent-mutation.d.ts +4 -0
  37. package/dist/templates/patterns/api/idempotent-mutation.js +47 -41
  38. package/dist/templates/patterns/api/paginated-list.d.ts +4 -0
  39. package/dist/templates/patterns/api/paginated-list.js +30 -24
  40. package/dist/templates/patterns/api/simple-crud.d.ts +4 -0
  41. package/dist/templates/patterns/api/simple-crud.js +46 -35
  42. package/dist/templates/patterns/api/streaming-list.d.ts +4 -0
  43. package/dist/templates/patterns/api/streaming-list.js +46 -41
  44. package/dist/templates/patterns/api/system-admin.d.ts +4 -0
  45. package/dist/templates/patterns/api/system-admin.js +59 -52
  46. package/dist/templates/patterns/api/webhook-receiver-style.d.ts +4 -0
  47. package/dist/templates/patterns/api/webhook-receiver-style.js +43 -35
  48. package/dist/types.d.ts +100 -0
  49. package/dist/types.js +1 -0
  50. package/dist/utils/file-helpers.d.ts +0 -2
  51. package/dist/utils/file-helpers.js +2 -3
  52. package/dist/utils/manifest.d.ts +1 -0
  53. package/dist/utils/manifest.js +16 -1
  54. package/dist/utils/scaffold-config.d.ts +6 -0
  55. package/dist/utils/scaffold-config.js +2 -0
  56. package/package.json +1 -1
  57. package/src/__tests__/build-registry.test.ts +43 -20
  58. package/src/__tests__/build-types.test.ts +4 -7
  59. package/src/__tests__/builder/walkDomainDir.test.ts +19 -21
  60. package/src/__tests__/commands/add-api.test.ts +12 -10
  61. package/src/__tests__/commands/add-domain.test.ts +8 -5
  62. package/src/__tests__/commands/build-flows.test.ts +55 -0
  63. package/src/__tests__/commands/check-hashes.test.ts +31 -0
  64. package/src/__tests__/commands/create-project.test.ts +5 -5
  65. package/src/__tests__/commands/dev.test.ts +0 -1
  66. package/src/__tests__/commands/regenerate-modules-hashes.test.ts +170 -0
  67. package/src/__tests__/commands/update-all.test.ts +322 -0
  68. package/src/__tests__/doctor.test.ts +73 -0
  69. package/src/__tests__/mount-routes.test.ts +64 -23
  70. package/src/__tests__/package-freshness.test.ts +1 -21
  71. package/src/__tests__/smoke/scaffold.test.ts +13 -15
  72. package/src/__tests__/utils/manifest.test.ts +128 -0
  73. package/src/__tests__/validate.test.ts +21 -103
  74. package/src/builder/build-registry.ts +7 -44
  75. package/src/builder/build-types.ts +1 -1
  76. package/src/cli.ts +23 -3
  77. package/src/commands/add-api.ts +2 -2
  78. package/src/commands/add-domain.ts +4 -4
  79. package/src/commands/add-fixture-factory.ts +5 -6
  80. package/src/commands/build-catalog.ts +13 -35
  81. package/src/commands/build.ts +3 -2
  82. package/src/commands/check-hashes.ts +12 -0
  83. package/src/commands/dev.ts +1 -1
  84. package/src/commands/doctor.ts +72 -41
  85. package/src/commands/explain.ts +13 -13
  86. package/src/commands/generate-openapi.ts +30 -52
  87. package/src/commands/regenerate-modules-hashes.ts +89 -0
  88. package/src/commands/show.ts +2 -5
  89. package/src/commands/test.ts +0 -1
  90. package/src/commands/update-all.ts +79 -0
  91. package/src/commands/upgrade-backend.ts +3 -3
  92. package/src/commands/validate.ts +11 -96
  93. package/src/server/api-server.ts +1 -1
  94. package/src/server/mount-routes.ts +21 -10
  95. package/src/templates/api-skeleton.ts +29 -28
  96. package/src/templates/claude-md.ts +1 -1
  97. package/src/templates/patterns/api/create-with-event.ts +39 -33
  98. package/src/templates/patterns/api/idempotent-mutation.ts +48 -42
  99. package/src/templates/patterns/api/paginated-list.ts +31 -25
  100. package/src/templates/patterns/api/simple-crud.ts +47 -36
  101. package/src/templates/patterns/api/streaming-list.ts +47 -42
  102. package/src/templates/patterns/api/system-admin.ts +60 -53
  103. package/src/templates/patterns/api/webhook-receiver-style.ts +48 -40
  104. package/src/types.ts +128 -0
  105. package/src/utils/file-helpers.ts +2 -5
  106. package/src/utils/manifest.ts +16 -1
  107. package/src/utils/scaffold-config.ts +9 -0
@@ -19,6 +19,10 @@
19
19
  * - API Gateway HTTP API with 2.0 payload format (not REST API)
20
20
  * - Lambda runtime v20+
21
21
  *
22
+ * Action-first: HTTP endpoints are declared with `defineAction` +
23
+ * `exposure: { type: 'api', ... }`. The legacy `defineApi` factory was
24
+ * removed in #4689.
25
+ *
22
26
  * @param domain - Domain ID in kebab-case
23
27
  * @param apiId - API ID in kebab-case
24
28
  * @param itemName - Singular item name (e.g. 'ticket', 'member')
@@ -26,7 +30,7 @@
26
30
  */
27
31
  export function streamingListPattern(domain, apiId, itemName) {
28
32
  return `import { z } from 'zod';
29
- import { defineApi } from '@mettlecast/domain-runtime';
33
+ import { defineAction } from '@mettlecast/domain-runtime';
30
34
  import type { Result, AppError } from '@mettlecast/domain-runtime';
31
35
 
32
36
  const ${itemName}Schema = z.object({ id: z.string() });
@@ -39,53 +43,54 @@ const outputSchema = z.object({
39
43
  nextCursor: z.string().optional(),
40
44
  }).default({ items: [] });
41
45
 
42
- export const ${apiId.replace(/-/g, '_')} = defineApi({
46
+ export const ${apiId.replace(/-/g, '_')} = defineAction({
43
47
  id: '${apiId}',
44
- path: '/v1/${domain}/${apiId}',
45
- method: 'GET',
46
- tenancy: 'required',
48
+ backendAccess: 'domain',
49
+ exposure: {
50
+ type: 'api',
51
+ path: '/v1/tenants/{tenantId}/${domain}/${apiId}',
52
+ method: 'GET',
53
+ auth: 'required',
54
+ tenancy: 'required',
55
+ },
47
56
  outboundAccess: 'internal',
48
- versions: {
49
- v1: {
50
- status: 'stable',
51
- input: inputSchema,
52
- output: outputSchema,
53
- handler: async (input, ctx): Promise<Result<z.infer<typeof outputSchema>, AppError>> => {
54
- // Streaming response: each item is yielded as a JSON line
55
- // before the full query completes. The runtime handler
56
- // wrapper converts this generator into a chunked response.
57
- //
58
- // GET /v1/${domain}/${apiId}?limit=100
59
- // Transfer-Encoding: chunked
60
- // Content-Type: application/x-ndjson
61
- //
62
- // {"id":"1"}\\n
63
- // {"id":"2"}\\n
64
- // {"id":"3"}\\n
57
+ idempotent: true,
58
+ input: inputSchema,
59
+ output: outputSchema,
60
+ handler: async (input, ctx): Promise<Result<z.infer<typeof outputSchema>, AppError>> => {
61
+ // Streaming response: each item is yielded as a JSON line
62
+ // before the full query completes. The runtime handler
63
+ // wrapper converts this generator into a chunked response.
64
+ //
65
+ // GET /v1/${domain}/${apiId}?limit=100
66
+ // Transfer-Encoding: chunked
67
+ // Content-Type: application/x-ndjson
68
+ //
69
+ // {"id":"1"}\\n
70
+ // {"id":"2"}\\n
71
+ // {"id":"3"}\\n
65
72
 
66
- let nextCursor: string | undefined;
67
- const items: unknown[] = [];
73
+ let nextCursor: string | undefined;
74
+ const items: unknown[] = [];
68
75
 
69
- // Replace this query with your actual data source.
70
- const rows = await ctx.db.query(
71
- '${domain}',
72
- 'SELECT id FROM ${domain}s LIMIT $1 OFFSET $2',
73
- [input.limit + 1, input.cursor ? parseInt(input.cursor, 10) : 0],
74
- );
76
+ // Replace this query with your actual data source.
77
+ const rows = await ctx.db.query(
78
+ '${domain}',
79
+ 'SELECT id FROM ${domain}s LIMIT $1 OFFSET $2',
80
+ [input.limit + 1, input.cursor ? parseInt(input.cursor, 10) : 0],
81
+ );
75
82
 
76
- for (let i = 0; i < Math.min(rows.length, input.limit); i++) {
77
- items.push(rows[i]);
78
- }
83
+ for (let i = 0; i < Math.min(rows.length, input.limit); i++) {
84
+ items.push(rows[i]);
85
+ }
79
86
 
80
- if (rows.length > input.limit) {
81
- nextCursor = String(
82
- (input.cursor ? parseInt(input.cursor, 10) : 0) + input.limit,
83
- );
84
- }
87
+ if (rows.length > input.limit) {
88
+ nextCursor = String(
89
+ (input.cursor ? parseInt(input.cursor, 10) : 0) + input.limit,
90
+ );
91
+ }
85
92
 
86
- return { ok: true, value: { items, nextCursor } };
87
- },
88
- },
93
+ return { ok: true, value: { items, nextCursor } };
89
94
  },
90
95
  });
91
96
  `;
@@ -1,6 +1,10 @@
1
1
  /**
2
2
  * System-admin API pattern template.
3
3
  * Produces: GET/POST with tenancy: 'system', no tenant context.
4
+ *
5
+ * Action-first: HTTP endpoints are declared with `defineAction` +
6
+ * `exposure: { type: 'api', ... }`. The legacy `defineApi` factory was
7
+ * removed in #4689.
4
8
  */
5
9
  export declare function systemAdminTemplate(domain: string, id: string, tenancy: string): string;
6
10
  /**
@@ -1,6 +1,10 @@
1
1
  /**
2
2
  * System-admin API pattern template.
3
3
  * Produces: GET/POST with tenancy: 'system', no tenant context.
4
+ *
5
+ * Action-first: HTTP endpoints are declared with `defineAction` +
6
+ * `exposure: { type: 'api', ... }`. The legacy `defineApi` factory was
7
+ * removed in #4689.
4
8
  */
5
9
  function camelCase(s) {
6
10
  return s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
@@ -8,8 +12,7 @@ function camelCase(s) {
8
12
  export function systemAdminTemplate(domain, id, tenancy) {
9
13
  const varName = camelCase(id);
10
14
  return `import { z } from 'zod';
11
- import { defineApi } from '@mettlecast/domain-runtime';
12
- import { createKyClient, initOtel } from '@mettlecast/domain-runtime';
15
+ import { defineAction, initOtel } from '@mettlecast/domain-runtime';
13
16
  import type { Result, AppError } from '@mettlecast/domain-runtime';
14
17
  import { ok, err, notFound } from '@mettlecast/domain-runtime';
15
18
 
@@ -27,7 +30,7 @@ const ${varName}Output = z.object({
27
30
  data: z.record(z.unknown()).optional(),
28
31
  }).default({ ok: true });
29
32
 
30
- // ── System health input ──────────────────────────────────────
33
+ // ── System health output ─────────────────────────────────────
31
34
 
32
35
  const SystemHealthOutput = z.object({
33
36
  status: z.enum(['healthy', 'degraded', 'down']),
@@ -38,64 +41,68 @@ const SystemHealthOutput = z.object({
38
41
  })),
39
42
  }).default({ status: 'healthy', uptime: 0, domains: [] });
40
43
 
41
- // ── API definitions ──────────────────────────────────────────
44
+ // ── Action definitions (API exposure) ────────────────────────
42
45
 
43
- export const ${varName} = defineApi({
46
+ export const ${varName} = defineAction({
44
47
  id: '${id}',
45
- path: '/system/${domain}/${id}',
46
- tenancy: '${tenancy}',
48
+ backendAccess: 'platform',
49
+ exposure: {
50
+ type: 'api',
51
+ path: '/system/${domain}/${id}',
52
+ method: 'POST',
53
+ auth: 'service',
54
+ tenancy: '${tenancy}',
55
+ },
47
56
  outboundAccess: 'internal',
48
- versions: {
49
- v1: {
50
- status: 'stable',
51
- input: ${varName}Input,
52
- output: ${varName}Output,
53
- handler: async (input, ctx) => {
54
- await ctx.audit.log('${id}.system', 'system', { command: input.command });
57
+ idempotent: false,
58
+ input: ${varName}Input,
59
+ output: ${varName}Output,
60
+ handler: async (input, ctx) => {
61
+ await ctx.audit.log('${id}.system', 'system', { command: input.command });
55
62
 
56
- switch (input.command) {
57
- case 'status':
58
- return {
59
- ok: true,
60
- data: { status: 'healthy', domains: [] },
61
- };
62
- case 'metrics':
63
- return {
64
- ok: true,
65
- data: {
66
- requestCount: 0,
67
- errorCount: 0,
68
- p99LatencyMs: 0,
69
- },
70
- };
71
- case 'health':
72
- return { ok: true, data: { status: 'healthy' } };
73
- default:
74
- return { ok: false };
75
- }
76
- },
77
- },
63
+ switch (input.command) {
64
+ case 'status':
65
+ return {
66
+ ok: true,
67
+ data: { status: 'healthy', domains: [] },
68
+ };
69
+ case 'metrics':
70
+ return {
71
+ ok: true,
72
+ data: {
73
+ requestCount: 0,
74
+ errorCount: 0,
75
+ p99LatencyMs: 0,
76
+ },
77
+ };
78
+ case 'health':
79
+ return { ok: true, data: { status: 'healthy' } };
80
+ default:
81
+ return { ok: false };
82
+ }
78
83
  },
79
84
  });
80
85
 
81
- export const ${varName}Health = defineApi({
86
+ export const ${varName}Health = defineAction({
82
87
  id: '${id}-health',
83
- path: '/system/${domain}/${id}-health',
84
- tenancy: '${tenancy}',
88
+ backendAccess: 'platform',
89
+ exposure: {
90
+ type: 'api',
91
+ path: '/system/${domain}/${id}-health',
92
+ method: 'GET',
93
+ auth: 'service',
94
+ tenancy: '${tenancy}',
95
+ },
85
96
  outboundAccess: 'internal',
86
- versions: {
87
- v1: {
88
- status: 'stable',
89
- input: z.object({}).default({}),
90
- output: SystemHealthOutput,
91
- handler: async (_input, _ctx) => {
92
- return {
93
- status: 'healthy',
94
- uptime: process.uptime(),
95
- domains: [],
96
- };
97
- },
98
- },
97
+ idempotent: true,
98
+ input: z.object({}).default({}),
99
+ output: SystemHealthOutput,
100
+ handler: async (_input, _ctx) => {
101
+ return {
102
+ status: 'healthy',
103
+ uptime: process.uptime(),
104
+ domains: [],
105
+ };
99
106
  },
100
107
  });
101
108
  `;
@@ -1,6 +1,10 @@
1
1
  /**
2
2
  * Webhook-receiver-style API pattern template.
3
3
  * Produces: POST with signature verification, provider enum.
4
+ *
5
+ * Action-first: HTTP endpoints are declared with `defineAction` +
6
+ * `exposure: { type: 'api', ... }`. The legacy `defineApi` factory was
7
+ * removed in #4689.
4
8
  */
5
9
  export declare function webhookReceiverStyleTemplate(domain: string, id: string, tenancy: string): string;
6
10
  /**
@@ -1,15 +1,21 @@
1
1
  /**
2
2
  * Webhook-receiver-style API pattern template.
3
3
  * Produces: POST with signature verification, provider enum.
4
+ *
5
+ * Action-first: HTTP endpoints are declared with `defineAction` +
6
+ * `exposure: { type: 'api', ... }`. The legacy `defineApi` factory was
7
+ * removed in #4689.
4
8
  */
5
9
  function camelCase(s) {
6
10
  return s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
7
11
  }
8
12
  export function webhookReceiverStyleTemplate(domain, id, tenancy) {
9
13
  const varName = camelCase(id);
14
+ // Webhook receivers are typically public; pair `auth: 'none'` with a
15
+ // securityException.reason so the validator does not flag the route.
16
+ const securityException = `\n securityException: { reason: 'webhook receiver; signature verification handled by authorizer' },`;
10
17
  return `import { z } from 'zod';
11
- import { defineApi } from '@mettlecast/domain-runtime';
12
- import { createKyClient, initOtel } from '@mettlecast/domain-runtime';
18
+ import { defineAction, initOtel } from '@mettlecast/domain-runtime';
13
19
  import type { Result, AppError } from '@mettlecast/domain-runtime';
14
20
  import { ok, err, notFound } from '@mettlecast/domain-runtime';
15
21
 
@@ -47,46 +53,48 @@ const ${varName}Output = z.object({
47
53
  provider: 'stripe',
48
54
  });
49
55
 
50
- // ── API definition ───────────────────────────────────────────
56
+ // ── Action definition (API exposure) ─────────────────────────
51
57
 
52
- export const ${varName} = defineApi({
58
+ export const ${varName} = defineAction({
53
59
  id: '${id}',
54
- path: '/webhooks/${domain}/${id}',
55
- tenancy: '${tenancy}',
60
+ backendAccess: 'domain',
61
+ exposure: {
62
+ type: 'api',
63
+ path: '/webhooks/${domain}/${id}',
64
+ method: 'POST',
65
+ auth: 'none',${securityException}
66
+ tenancy: '${tenancy}',
67
+ },
56
68
  outboundAccess: 'internal',
57
- versions: {
58
- v1: {
59
- status: 'stable',
60
- input: ${varName}Input,
61
- output: ${varName}Output,
62
- handler: async (input, ctx) => {
63
- // Signature verification belongs in a Lambda authorizer (x-webhook-signature header
64
- // is not accessible here). Wire an HttpLambdaAuthorizer in DomainStack for production.
69
+ idempotent: false,
70
+ input: ${varName}Input,
71
+ output: ${varName}Output,
72
+ handler: async (input, ctx) => {
73
+ // Signature verification belongs in a Lambda authorizer (x-webhook-signature header
74
+ // is not accessible here). Wire an HttpLambdaAuthorizer in DomainStack for production.
65
75
 
66
- const eventId = crypto.randomUUID();
76
+ const eventId = crypto.randomUUID();
67
77
 
68
- // ── Publish event for downstream processing ───────────
69
- await ctx.publish('${domain}.webhook.received', {
70
- eventId,
71
- provider: input.provider,
72
- eventType: input.eventType,
73
- payload: input.payload,
74
- tenantId: ctx.tenant.id,
75
- }, 1);
78
+ // ── Publish event for downstream processing ───────────
79
+ await ctx.publish('${domain}.webhook.received', {
80
+ eventId,
81
+ provider: input.provider,
82
+ eventType: input.eventType,
83
+ payload: input.payload,
84
+ tenantId: ctx.tenant.id,
85
+ }, 1);
76
86
 
77
- await ctx.audit.log('${id}.webhook.received', ctx.tenant.id, {
78
- provider: input.provider,
79
- eventType: input.eventType,
80
- eventId,
81
- });
87
+ await ctx.audit.log('${id}.webhook.received', ctx.tenant.id, {
88
+ provider: input.provider,
89
+ eventType: input.eventType,
90
+ eventId,
91
+ });
82
92
 
83
- return {
84
- received: true,
85
- eventId,
86
- provider: input.provider,
87
- };
88
- },
89
- },
93
+ return {
94
+ received: true,
95
+ eventId,
96
+ provider: input.provider,
97
+ };
90
98
  },
91
99
  });
92
100
  `;
@@ -0,0 +1,100 @@
1
+ /**
2
+ * packages/domain-cli/src/types.ts
3
+ *
4
+ * Local re-declaration of registry types used by the CLI's
5
+ * build/dev/mount-routes pipeline.
6
+ *
7
+ * Issue #4689 removed the legacy `defineApi` primitive and the
8
+ * `registry.apis` field in favour of the action-first model (#4619).
9
+ * The CLI's own source defines the action-first shape here so the
10
+ * pipeline can compile even when the published
11
+ * `@mettlecast/domain-cdk-packer` on npm still carries the legacy
12
+ * shape (i.e. `DomainRegistry` still requires `apis`, and
13
+ * `ActionRegistryEntry` lacks the `exposure` field).
14
+ *
15
+ * Once the npm-published packer catches up, the two local types below
16
+ * can collapse back to a single re-export from
17
+ * `@mettlecast/domain-cdk-packer`.
18
+ */
19
+ import type { DomainRegistry as PackerDomainRegistry, ActionRegistryEntry as PackerActionRegistryEntry, SchemaSnapshot, WebhookRegistryEntry, SubscriberRegistryEntry, ScheduleRegistryEntry, JobRegistryEntry, IntegrationRegistryEntry, EventRegistryEntry, DomainRegistryEntry, SerialDeploymentConfig, BaseRegistryEntry } from '@mettlecast/domain-cdk-packer';
20
+ export type { SchemaSnapshot, WebhookRegistryEntry, SubscriberRegistryEntry, ScheduleRegistryEntry, JobRegistryEntry, IntegrationRegistryEntry, EventRegistryEntry, DomainRegistryEntry, SerialDeploymentConfig, BaseRegistryEntry, };
21
+ /**
22
+ * Backend invocation permission for an action. Re-declared locally
23
+ * because the legacy published packer does not export this union yet
24
+ * (it was introduced as part of the action-first migration #4619).
25
+ */
26
+ export type ActionBackendAccess = 'private' | 'domain' | 'platform';
27
+ /**
28
+ * API-exposure metadata for an action whose `exposure.type === 'api'`.
29
+ * Carries the route, method, auth, tenancy, and any documented security
30
+ * exception that downstream CDK/contract generation needs to wire the
31
+ * route safely. Mirrors the canonical `ActionApiExposure` shape from
32
+ * `@mettlecast/domain-cdk-packer`.
33
+ */
34
+ export interface ActionApiExposure {
35
+ /** Discriminant. */
36
+ type: 'api';
37
+ /** HTTP route path, e.g. '/v1/tenants/{tenantId}/billing/invoices'. */
38
+ path: string;
39
+ /** HTTP method. One of: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS. */
40
+ method: string;
41
+ /** Authentication requirement for the route. */
42
+ auth: 'required' | 'none' | 'service';
43
+ /** Tenancy requirement for the route. */
44
+ tenancy: 'required' | 'none' | 'system';
45
+ /** Required role(s) for `auth: 'required'` routes (system tenancy requires this). */
46
+ roles?: string[];
47
+ /** Documented exception when auth or tenancy is intentionally not required. */
48
+ securityException?: {
49
+ reason: string;
50
+ };
51
+ /**
52
+ * True if the source code explicitly declared `auth`. False if the
53
+ * builder fell back to the safe default (`'required'`). Validation-only.
54
+ */
55
+ authDeclared?: boolean;
56
+ /**
57
+ * True if the source code explicitly declared `tenancy`. False if the
58
+ * builder fell back to the safe default (`'required'`). Validation-only.
59
+ */
60
+ tenancyDeclared?: boolean;
61
+ }
62
+ /**
63
+ * Discriminated union for an action's external exposure shape.
64
+ * `internal` actions are reachable only through `ctx.actions`. `api`
65
+ * actions are exposed via API Gateway and carry the route + auth
66
+ * metadata needed for safe route generation.
67
+ */
68
+ export type ActionExposure = ActionApiExposure | {
69
+ type: 'internal';
70
+ };
71
+ /**
72
+ * Local `DomainRegistry` for the CLI. Drops the legacy `apis` slot
73
+ * that was removed in issue #4689's action-first migration, and pins
74
+ * the `actions` array to the local `ActionRegistryEntry` shape (which
75
+ * carries the action-first `exposure` field). The CLI builds an object
76
+ * literal that satisfies this type and then serialises it to
77
+ * `.mc/<domain>-registry.json`; downstream consumers (CDK packer,
78
+ * OpenAPI generator, dev server) only ever see the JSON.
79
+ */
80
+ export type DomainRegistry = Omit<PackerDomainRegistry, 'apis' | 'actions'> & {
81
+ /** All callable actions (local shape with action-first `exposure`). */
82
+ actions: ActionRegistryEntry[];
83
+ };
84
+ /**
85
+ * Local `ActionRegistryEntry` for the CLI. Augments the packer's shape
86
+ * with the `exposure` field and its `exposureDeclared` flag from the
87
+ * action-first migration (#4619) so the build/dev/mount-routes
88
+ * pipeline can read these properties regardless of which packer
89
+ * version is linked. The `backendAccess` field is also surfaced here
90
+ * (the CLI is the canonical writer of this field) even when the
91
+ * linked packer only exposes the legacy `visibility` alias.
92
+ */
93
+ export type ActionRegistryEntry = PackerActionRegistryEntry & {
94
+ /** External exposure shape for the action (see ActionExposure). */
95
+ exposure: ActionExposure;
96
+ /** True if the source code explicitly declared `exposure`. */
97
+ exposureDeclared?: boolean;
98
+ /** Backend invocation permission for the action. */
99
+ backendAccess?: ActionBackendAccess;
100
+ };
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -7,8 +7,6 @@
7
7
  export interface DomainFilePaths {
8
8
  /** Path to domain.config.ts if present. */
9
9
  domain: string | undefined;
10
- /** API handler file paths under api/. */
11
- apis: string[];
12
10
  /** Webhook handler file paths under webhooks/. */
13
11
  webhooks: string[];
14
12
  /** Subscriber handler file paths under subscribers/. */
@@ -44,9 +44,8 @@ export async function walkDomainDir(domainRoot) {
44
44
  return undefined;
45
45
  }
46
46
  }
47
- const [domain, apis, webhooks, subscribers, actions, schedules, jobs, integrations, publishes] = await Promise.all([
47
+ const [domain, webhooks, subscribers, actions, schedules, jobs, integrations, publishes] = await Promise.all([
48
48
  singleTs(join(domainRoot, 'domain.config.ts')),
49
- tsFiles(join(domainRoot, 'api')),
50
49
  tsFiles(join(domainRoot, 'webhooks')),
51
50
  tsFiles(join(domainRoot, 'subscribers')),
52
51
  tsFiles(join(domainRoot, 'actions')),
@@ -55,5 +54,5 @@ export async function walkDomainDir(domainRoot) {
55
54
  tsFiles(join(domainRoot, 'integrations')),
56
55
  singleTs(join(domainRoot, 'publishes', 'events.ts')),
57
56
  ]);
58
- return { domain, apis, webhooks, subscribers, actions, schedules, jobs, integrations, publishes };
57
+ return { domain, webhooks, subscribers, actions, schedules, jobs, integrations, publishes };
59
58
  }
@@ -26,3 +26,4 @@ export declare function upsertManifestFile(manifest: TibManifest, entry: Manifes
26
26
  export declare function removeManifestFile(manifest: TibManifest, filePath: string): void;
27
27
  export declare function getManifestFile(manifest: TibManifest, filePath: string): ManifestFileEntry | undefined;
28
28
  export declare function isScaffoldManaged(manifest: TibManifest, filePath: string): boolean;
29
+ export declare function pruneManifest(projectRoot: string, manifest: TibManifest): Promise<number>;
@@ -1,4 +1,4 @@
1
- import { readFile, writeFile, mkdir } from 'fs/promises';
1
+ import { readFile, writeFile, mkdir, access } from 'fs/promises';
2
2
  import path from 'path';
3
3
  import { cliLogger } from './logger.js';
4
4
  const MANIFEST_PATH = '.mc/manifest.json';
@@ -132,3 +132,18 @@ export function isScaffoldManaged(manifest, filePath) {
132
132
  // Returns true only when the file is in the manifest with policy 'managed'
133
133
  return entry != null && entry.policy === 'managed';
134
134
  }
135
+ export async function pruneManifest(projectRoot, manifest) {
136
+ let pruned = 0;
137
+ const survivors = [];
138
+ for (const entry of manifest.files) {
139
+ try {
140
+ await access(path.join(projectRoot, entry.path));
141
+ survivors.push(entry);
142
+ }
143
+ catch {
144
+ pruned++;
145
+ }
146
+ }
147
+ manifest.files = survivors;
148
+ return pruned;
149
+ }
@@ -29,10 +29,14 @@ export interface CostOptions {
29
29
  xRaySamplingRate?: number;
30
30
  reservedConcurrencyPerDomain?: Record<string, number>;
31
31
  }
32
+ /** NAT instance type options for cost control. */
33
+ export type NatInstanceType = 't4g.nano' | 't4g.micro' | 't4g.small';
32
34
  /** Monitoring / observability feature opt-ins. */
33
35
  export interface MonitoringOptions {
34
36
  /** Replace basic MonitoringStack with full ObservabilityStack (Grafana + Cost Explorer + CloudWatch). ~$9–18/month */
35
37
  enhanced: boolean;
38
+ /** Disable auto-generated per-domain CloudWatch dashboards. Removes existing dashboards on next deploy. */
39
+ disableCloudWatchDashboards?: boolean;
36
40
  }
37
41
  /** Lifecycle tunables — Phase B. Schema only in Phase A. */
38
42
  export interface LifecycleOptions {
@@ -92,6 +96,8 @@ export interface ScaffoldConfig {
92
96
  environments?: EnvironmentsConfig;
93
97
  /** When true, CORS responses include Allow-Credentials: true (requires non-wildcard origins). */
94
98
  allowCredentials?: boolean;
99
+ /** NAT instance type for the shared NAT instance. Defaults to the smallest: t4g.nano. */
100
+ natInstanceType?: NatInstanceType;
95
101
  }
96
102
  export declare function readScaffoldConfig(projectRoot: string): Promise<ScaffoldConfig>;
97
103
  export declare function writeScaffoldConfig(projectRoot: string, config: ScaffoldConfig): Promise<void>;
@@ -18,7 +18,9 @@ const DEFAULT_SCAFFOLD_CONFIG = {
18
18
  logRetentionDays: 30,
19
19
  monitoringOptions: {
20
20
  enhanced: false,
21
+ disableCloudWatchDashboards: false,
21
22
  },
23
+ natInstanceType: 't4g.nano',
22
24
  };
23
25
  const CONFIG_PATH = '.mc/scaffold-config.json';
24
26
  export async function readScaffoldConfig(projectRoot) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mettlecast/domain-cli",
3
- "version": "0.2.60",
3
+ "version": "0.2.62",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org",