@mettlecast/domain-cli 0.2.59 → 0.2.61

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 (119) hide show
  1. package/dist/builder/build-registry.d.ts +1 -1
  2. package/dist/builder/build-registry.js +129 -49
  3. package/dist/builder/build-types.d.ts +1 -1
  4. package/dist/builder/load-module.d.ts +1 -1
  5. package/dist/builder/load-module.js +3 -3
  6. package/dist/cli.js +3 -3
  7. package/dist/commands/add-api.js +2 -2
  8. package/dist/commands/add-domain.js +4 -4
  9. package/dist/commands/add-fixture-factory.js +5 -6
  10. package/dist/commands/build-catalog.d.ts +8 -24
  11. package/dist/commands/build-catalog.js +12 -23
  12. package/dist/commands/build-flows.js +1 -1
  13. package/dist/commands/build.js +7 -6
  14. package/dist/commands/check-hashes.js +2 -2
  15. package/dist/commands/create-project.js +1 -1
  16. package/dist/commands/dev.js +1 -1
  17. package/dist/commands/doctor.d.ts +5 -7
  18. package/dist/commands/doctor.js +110 -202
  19. package/dist/commands/explain.js +13 -13
  20. package/dist/commands/generate-openapi.d.ts +10 -1
  21. package/dist/commands/generate-openapi.js +19 -33
  22. package/dist/commands/power-tune.js +2 -2
  23. package/dist/commands/show-dns.d.ts +1 -1
  24. package/dist/commands/show-dns.js +5 -5
  25. package/dist/commands/show.d.ts +2 -3
  26. package/dist/commands/show.js +0 -2
  27. package/dist/commands/test.js +0 -1
  28. package/dist/commands/upgrade-backend.js +3 -3
  29. package/dist/commands/upgrade.js +17 -11
  30. package/dist/commands/validate.js +144 -39
  31. package/dist/server/api-server.d.ts +1 -1
  32. package/dist/server/mount-routes.d.ts +11 -2
  33. package/dist/server/mount-routes.js +20 -8
  34. package/dist/templates/api-skeleton.d.ts +5 -0
  35. package/dist/templates/api-skeleton.js +28 -27
  36. package/dist/templates/claude-md.js +1 -1
  37. package/dist/templates/patterns/api/create-with-event.d.ts +4 -0
  38. package/dist/templates/patterns/api/create-with-event.js +38 -32
  39. package/dist/templates/patterns/api/idempotent-mutation.d.ts +4 -0
  40. package/dist/templates/patterns/api/idempotent-mutation.js +47 -41
  41. package/dist/templates/patterns/api/paginated-list.d.ts +4 -0
  42. package/dist/templates/patterns/api/paginated-list.js +30 -24
  43. package/dist/templates/patterns/api/simple-crud.d.ts +4 -0
  44. package/dist/templates/patterns/api/simple-crud.js +46 -35
  45. package/dist/templates/patterns/api/streaming-list.d.ts +4 -0
  46. package/dist/templates/patterns/api/streaming-list.js +46 -41
  47. package/dist/templates/patterns/api/system-admin.d.ts +4 -0
  48. package/dist/templates/patterns/api/system-admin.js +59 -52
  49. package/dist/templates/patterns/api/webhook-receiver-style.d.ts +4 -0
  50. package/dist/templates/patterns/api/webhook-receiver-style.js +43 -35
  51. package/dist/types.d.ts +100 -0
  52. package/dist/types.js +1 -0
  53. package/dist/utils/file-helpers.d.ts +0 -2
  54. package/dist/utils/file-helpers.js +2 -3
  55. package/dist/utils/header-inject.js +2 -2
  56. package/dist/utils/install-file.d.ts +1 -1
  57. package/dist/utils/install-file.js +1 -1
  58. package/dist/utils/manifest.js +1 -2
  59. package/dist/utils/scaffold-config.d.ts +8 -2
  60. package/dist/utils/scaffold-config.js +3 -1
  61. package/package.json +1 -1
  62. package/src/__tests__/build-registry.test.ts +43 -20
  63. package/src/__tests__/build-types.test.ts +4 -7
  64. package/src/__tests__/builder/walkDomainDir.test.ts +19 -21
  65. package/src/__tests__/commands/add-api.test.ts +12 -10
  66. package/src/__tests__/commands/add-domain.test.ts +8 -5
  67. package/src/__tests__/commands/check-hashes.test.ts +9 -9
  68. package/src/__tests__/commands/create-project.test.ts +5 -5
  69. package/src/__tests__/commands/dev.test.ts +0 -1
  70. package/src/__tests__/commands/upgrade.test.ts +7 -7
  71. package/src/__tests__/doctor.test.ts +60 -67
  72. package/src/__tests__/mount-routes.test.ts +64 -23
  73. package/src/__tests__/package-freshness.test.ts +94 -0
  74. package/src/__tests__/scaffold-src/part-a-layout.test.ts +10 -10
  75. package/src/__tests__/scripts/package-scaffold.test.ts +5 -5
  76. package/src/__tests__/smoke/scaffold.test.ts +13 -15
  77. package/src/__tests__/utils/install-file.test.ts +2 -2
  78. package/src/__tests__/utils/manifest.test.ts +2 -2
  79. package/src/__tests__/validate.test.ts +570 -1
  80. package/src/builder/build-registry.ts +154 -59
  81. package/src/builder/build-types.ts +1 -1
  82. package/src/builder/load-module.ts +3 -3
  83. package/src/cli.ts +4 -4
  84. package/src/commands/add-api.ts +2 -2
  85. package/src/commands/add-domain.ts +4 -4
  86. package/src/commands/add-fixture-factory.ts +5 -6
  87. package/src/commands/build-catalog.ts +18 -40
  88. package/src/commands/build-flows.ts +1 -1
  89. package/src/commands/build.ts +8 -7
  90. package/src/commands/check-hashes.ts +2 -2
  91. package/src/commands/create-project.ts +1 -1
  92. package/src/commands/dev.ts +1 -1
  93. package/src/commands/doctor.ts +120 -218
  94. package/src/commands/explain.ts +13 -13
  95. package/src/commands/generate-openapi.ts +30 -52
  96. package/src/commands/power-tune.ts +2 -2
  97. package/src/commands/show-dns.ts +5 -5
  98. package/src/commands/show.ts +2 -5
  99. package/src/commands/test.ts +0 -1
  100. package/src/commands/upgrade-backend.ts +3 -3
  101. package/src/commands/upgrade.ts +16 -10
  102. package/src/commands/validate.ts +180 -40
  103. package/src/server/api-server.ts +1 -1
  104. package/src/server/mount-routes.ts +21 -10
  105. package/src/templates/api-skeleton.ts +29 -28
  106. package/src/templates/claude-md.ts +1 -1
  107. package/src/templates/patterns/api/create-with-event.ts +39 -33
  108. package/src/templates/patterns/api/idempotent-mutation.ts +48 -42
  109. package/src/templates/patterns/api/paginated-list.ts +31 -25
  110. package/src/templates/patterns/api/simple-crud.ts +47 -36
  111. package/src/templates/patterns/api/streaming-list.ts +47 -42
  112. package/src/templates/patterns/api/system-admin.ts +60 -53
  113. package/src/templates/patterns/api/webhook-receiver-style.ts +48 -40
  114. package/src/types.ts +128 -0
  115. package/src/utils/file-helpers.ts +2 -5
  116. package/src/utils/header-inject.ts +2 -2
  117. package/src/utils/install-file.ts +1 -1
  118. package/src/utils/manifest.ts +1 -2
  119. package/src/utils/scaffold-config.ts +12 -3
@@ -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
 
6
10
  function camelCase(s: string): string {
@@ -11,8 +15,7 @@ export function systemAdminTemplate(domain: string, id: string, tenancy: string)
11
15
  const varName = camelCase(id);
12
16
 
13
17
  return `import { z } from 'zod';
14
- import { defineApi } from '@mettlecast/domain-runtime';
15
- import { createKyClient, initOtel } from '@mettlecast/domain-runtime';
18
+ import { defineAction, initOtel } from '@mettlecast/domain-runtime';
16
19
  import type { Result, AppError } from '@mettlecast/domain-runtime';
17
20
  import { ok, err, notFound } from '@mettlecast/domain-runtime';
18
21
 
@@ -30,7 +33,7 @@ const ${varName}Output = z.object({
30
33
  data: z.record(z.unknown()).optional(),
31
34
  }).default({ ok: true });
32
35
 
33
- // ── System health input ──────────────────────────────────────
36
+ // ── System health output ─────────────────────────────────────
34
37
 
35
38
  const SystemHealthOutput = z.object({
36
39
  status: z.enum(['healthy', 'degraded', 'down']),
@@ -41,64 +44,68 @@ const SystemHealthOutput = z.object({
41
44
  })),
42
45
  }).default({ status: 'healthy', uptime: 0, domains: [] });
43
46
 
44
- // ── API definitions ──────────────────────────────────────────
47
+ // ── Action definitions (API exposure) ────────────────────────
45
48
 
46
- export const ${varName} = defineApi({
49
+ export const ${varName} = defineAction({
47
50
  id: '${id}',
48
- path: '/system/${domain}/${id}',
49
- tenancy: '${tenancy}',
51
+ backendAccess: 'platform',
52
+ exposure: {
53
+ type: 'api',
54
+ path: '/system/${domain}/${id}',
55
+ method: 'POST',
56
+ auth: 'service',
57
+ tenancy: '${tenancy}',
58
+ },
50
59
  outboundAccess: 'internal',
51
- versions: {
52
- v1: {
53
- status: 'stable',
54
- input: ${varName}Input,
55
- output: ${varName}Output,
56
- handler: async (input, ctx) => {
57
- await ctx.audit.log('${id}.system', 'system', { command: input.command });
60
+ idempotent: false,
61
+ input: ${varName}Input,
62
+ output: ${varName}Output,
63
+ handler: async (input, ctx) => {
64
+ await ctx.audit.log('${id}.system', 'system', { command: input.command });
58
65
 
59
- switch (input.command) {
60
- case 'status':
61
- return {
62
- ok: true,
63
- data: { status: 'healthy', domains: [] },
64
- };
65
- case 'metrics':
66
- return {
67
- ok: true,
68
- data: {
69
- requestCount: 0,
70
- errorCount: 0,
71
- p99LatencyMs: 0,
72
- },
73
- };
74
- case 'health':
75
- return { ok: true, data: { status: 'healthy' } };
76
- default:
77
- return { ok: false };
78
- }
79
- },
80
- },
66
+ switch (input.command) {
67
+ case 'status':
68
+ return {
69
+ ok: true,
70
+ data: { status: 'healthy', domains: [] },
71
+ };
72
+ case 'metrics':
73
+ return {
74
+ ok: true,
75
+ data: {
76
+ requestCount: 0,
77
+ errorCount: 0,
78
+ p99LatencyMs: 0,
79
+ },
80
+ };
81
+ case 'health':
82
+ return { ok: true, data: { status: 'healthy' } };
83
+ default:
84
+ return { ok: false };
85
+ }
81
86
  },
82
87
  });
83
88
 
84
- export const ${varName}Health = defineApi({
89
+ export const ${varName}Health = defineAction({
85
90
  id: '${id}-health',
86
- path: '/system/${domain}/${id}-health',
87
- tenancy: '${tenancy}',
91
+ backendAccess: 'platform',
92
+ exposure: {
93
+ type: 'api',
94
+ path: '/system/${domain}/${id}-health',
95
+ method: 'GET',
96
+ auth: 'service',
97
+ tenancy: '${tenancy}',
98
+ },
88
99
  outboundAccess: 'internal',
89
- versions: {
90
- v1: {
91
- status: 'stable',
92
- input: z.object({}).default({}),
93
- output: SystemHealthOutput,
94
- handler: async (_input, _ctx) => {
95
- return {
96
- status: 'healthy',
97
- uptime: process.uptime(),
98
- domains: [],
99
- };
100
- },
101
- },
100
+ idempotent: true,
101
+ input: z.object({}).default({}),
102
+ output: SystemHealthOutput,
103
+ handler: async (_input, _ctx) => {
104
+ return {
105
+ status: 'healthy',
106
+ uptime: process.uptime(),
107
+ domains: [],
108
+ };
102
109
  },
103
110
  });
104
111
  `;
@@ -108,4 +115,4 @@ export const ${varName}Health = defineApi({
108
115
  * Example body string used as the event body in the add-api fixture for the
109
116
  * system-admin pattern. Mirrors the default shape of the input schema.
110
117
  */
111
- export const systemAdminExampleBody = JSON.stringify({ command: 'status' });
118
+ export const systemAdminExampleBody = JSON.stringify({ command: 'status' });
@@ -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
 
6
10
  function camelCase(s: string): string {
@@ -9,10 +13,12 @@ function camelCase(s: string): string {
9
13
 
10
14
  export function webhookReceiverStyleTemplate(domain: string, id: string, tenancy: string): string {
11
15
  const varName = camelCase(id);
16
+ // Webhook receivers are typically public; pair `auth: 'none'` with a
17
+ // securityException.reason so the validator does not flag the route.
18
+ const securityException = `\n securityException: { reason: 'webhook receiver; signature verification handled by authorizer' },`;
12
19
 
13
20
  return `import { z } from 'zod';
14
- import { defineApi } from '@mettlecast/domain-runtime';
15
- import { createKyClient, initOtel } from '@mettlecast/domain-runtime';
21
+ import { defineAction, initOtel } from '@mettlecast/domain-runtime';
16
22
  import type { Result, AppError } from '@mettlecast/domain-runtime';
17
23
  import { ok, err, notFound } from '@mettlecast/domain-runtime';
18
24
 
@@ -50,46 +56,48 @@ const ${varName}Output = z.object({
50
56
  provider: 'stripe',
51
57
  });
52
58
 
53
- // ── API definition ───────────────────────────────────────────
59
+ // ── Action definition (API exposure) ─────────────────────────
54
60
 
55
- export const ${varName} = defineApi({
61
+ export const ${varName} = defineAction({
56
62
  id: '${id}',
57
- path: '/webhooks/${domain}/${id}',
58
- tenancy: '${tenancy}',
63
+ backendAccess: 'domain',
64
+ exposure: {
65
+ type: 'api',
66
+ path: '/webhooks/${domain}/${id}',
67
+ method: 'POST',
68
+ auth: 'none',${securityException}
69
+ tenancy: '${tenancy}',
70
+ },
59
71
  outboundAccess: 'internal',
60
- versions: {
61
- v1: {
62
- status: 'stable',
63
- input: ${varName}Input,
64
- output: ${varName}Output,
65
- handler: async (input, ctx) => {
66
- // Signature verification belongs in a Lambda authorizer (x-webhook-signature header
67
- // is not accessible here). Wire an HttpLambdaAuthorizer in DomainStack for production.
68
-
69
- const eventId = crypto.randomUUID();
70
-
71
- // ── Publish event for downstream processing ───────────
72
- await ctx.publish('${domain}.webhook.received', {
73
- eventId,
74
- provider: input.provider,
75
- eventType: input.eventType,
76
- payload: input.payload,
77
- tenantId: ctx.tenant.id,
78
- }, 1);
79
-
80
- await ctx.audit.log('${id}.webhook.received', ctx.tenant.id, {
81
- provider: input.provider,
82
- eventType: input.eventType,
83
- eventId,
84
- });
85
-
86
- return {
87
- received: true,
88
- eventId,
89
- provider: input.provider,
90
- };
91
- },
92
- },
72
+ idempotent: false,
73
+ input: ${varName}Input,
74
+ output: ${varName}Output,
75
+ handler: async (input, ctx) => {
76
+ // Signature verification belongs in a Lambda authorizer (x-webhook-signature header
77
+ // is not accessible here). Wire an HttpLambdaAuthorizer in DomainStack for production.
78
+
79
+ const eventId = crypto.randomUUID();
80
+
81
+ // ── Publish event for downstream processing ───────────
82
+ await ctx.publish('${domain}.webhook.received', {
83
+ eventId,
84
+ provider: input.provider,
85
+ eventType: input.eventType,
86
+ payload: input.payload,
87
+ tenantId: ctx.tenant.id,
88
+ }, 1);
89
+
90
+ await ctx.audit.log('${id}.webhook.received', ctx.tenant.id, {
91
+ provider: input.provider,
92
+ eventType: input.eventType,
93
+ eventId,
94
+ });
95
+
96
+ return {
97
+ received: true,
98
+ eventId,
99
+ provider: input.provider,
100
+ };
93
101
  },
94
102
  });
95
103
  `;
@@ -103,4 +111,4 @@ export const webhookReceiverStyleExampleBody = JSON.stringify({
103
111
  provider: 'stripe',
104
112
  eventType: 'example.event',
105
113
  payload: {},
106
- });
114
+ });
package/src/types.ts ADDED
@@ -0,0 +1,128 @@
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 {
20
+ DomainRegistry as PackerDomainRegistry,
21
+ ActionRegistryEntry as PackerActionRegistryEntry,
22
+ SchemaSnapshot,
23
+ WebhookRegistryEntry,
24
+ SubscriberRegistryEntry,
25
+ ScheduleRegistryEntry,
26
+ JobRegistryEntry,
27
+ IntegrationRegistryEntry,
28
+ EventRegistryEntry,
29
+ DomainRegistryEntry,
30
+ SerialDeploymentConfig,
31
+ BaseRegistryEntry,
32
+ } from '@mettlecast/domain-cdk-packer';
33
+
34
+ // Re-export the unchanged registry entry types so callers can import
35
+ // everything from a single local module.
36
+ export type {
37
+ SchemaSnapshot,
38
+ WebhookRegistryEntry,
39
+ SubscriberRegistryEntry,
40
+ ScheduleRegistryEntry,
41
+ JobRegistryEntry,
42
+ IntegrationRegistryEntry,
43
+ EventRegistryEntry,
44
+ DomainRegistryEntry,
45
+ SerialDeploymentConfig,
46
+ BaseRegistryEntry,
47
+ };
48
+
49
+ /**
50
+ * Backend invocation permission for an action. Re-declared locally
51
+ * because the legacy published packer does not export this union yet
52
+ * (it was introduced as part of the action-first migration #4619).
53
+ */
54
+ export type ActionBackendAccess = 'private' | 'domain' | 'platform';
55
+
56
+ /**
57
+ * API-exposure metadata for an action whose `exposure.type === 'api'`.
58
+ * Carries the route, method, auth, tenancy, and any documented security
59
+ * exception that downstream CDK/contract generation needs to wire the
60
+ * route safely. Mirrors the canonical `ActionApiExposure` shape from
61
+ * `@mettlecast/domain-cdk-packer`.
62
+ */
63
+ export interface ActionApiExposure {
64
+ /** Discriminant. */
65
+ type: 'api';
66
+ /** HTTP route path, e.g. '/v1/tenants/{tenantId}/billing/invoices'. */
67
+ path: string;
68
+ /** HTTP method. One of: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS. */
69
+ method: string;
70
+ /** Authentication requirement for the route. */
71
+ auth: 'required' | 'none' | 'service';
72
+ /** Tenancy requirement for the route. */
73
+ tenancy: 'required' | 'none' | 'system';
74
+ /** Required role(s) for `auth: 'required'` routes (system tenancy requires this). */
75
+ roles?: string[];
76
+ /** Documented exception when auth or tenancy is intentionally not required. */
77
+ securityException?: { reason: string };
78
+ /**
79
+ * True if the source code explicitly declared `auth`. False if the
80
+ * builder fell back to the safe default (`'required'`). Validation-only.
81
+ */
82
+ authDeclared?: boolean;
83
+ /**
84
+ * True if the source code explicitly declared `tenancy`. False if the
85
+ * builder fell back to the safe default (`'required'`). Validation-only.
86
+ */
87
+ tenancyDeclared?: boolean;
88
+ }
89
+
90
+ /**
91
+ * Discriminated union for an action's external exposure shape.
92
+ * `internal` actions are reachable only through `ctx.actions`. `api`
93
+ * actions are exposed via API Gateway and carry the route + auth
94
+ * metadata needed for safe route generation.
95
+ */
96
+ export type ActionExposure = ActionApiExposure | { type: 'internal' };
97
+
98
+ /**
99
+ * Local `DomainRegistry` for the CLI. Drops the legacy `apis` slot
100
+ * that was removed in issue #4689's action-first migration, and pins
101
+ * the `actions` array to the local `ActionRegistryEntry` shape (which
102
+ * carries the action-first `exposure` field). The CLI builds an object
103
+ * literal that satisfies this type and then serialises it to
104
+ * `.mc/<domain>-registry.json`; downstream consumers (CDK packer,
105
+ * OpenAPI generator, dev server) only ever see the JSON.
106
+ */
107
+ export type DomainRegistry = Omit<PackerDomainRegistry, 'apis' | 'actions'> & {
108
+ /** All callable actions (local shape with action-first `exposure`). */
109
+ actions: ActionRegistryEntry[];
110
+ };
111
+
112
+ /**
113
+ * Local `ActionRegistryEntry` for the CLI. Augments the packer's shape
114
+ * with the `exposure` field and its `exposureDeclared` flag from the
115
+ * action-first migration (#4619) so the build/dev/mount-routes
116
+ * pipeline can read these properties regardless of which packer
117
+ * version is linked. The `backendAccess` field is also surfaced here
118
+ * (the CLI is the canonical writer of this field) even when the
119
+ * linked packer only exposes the legacy `visibility` alias.
120
+ */
121
+ export type ActionRegistryEntry = PackerActionRegistryEntry & {
122
+ /** External exposure shape for the action (see ActionExposure). */
123
+ exposure: ActionExposure;
124
+ /** True if the source code explicitly declared `exposure`. */
125
+ exposureDeclared?: boolean;
126
+ /** Backend invocation permission for the action. */
127
+ backendAccess?: ActionBackendAccess;
128
+ };
@@ -10,8 +10,6 @@ import { join } from 'node:path';
10
10
  export interface DomainFilePaths {
11
11
  /** Path to domain.config.ts if present. */
12
12
  domain: string | undefined;
13
- /** API handler file paths under api/. */
14
- apis: string[];
15
13
  /** Webhook handler file paths under webhooks/. */
16
14
  webhooks: string[];
17
15
  /** Subscriber handler file paths under subscribers/. */
@@ -74,10 +72,9 @@ export async function walkDomainDir(domainRoot: string): Promise<DomainFilePaths
74
72
  }
75
73
  }
76
74
 
77
- const [domain, apis, webhooks, subscribers, actions, schedules, jobs, integrations, publishes] =
75
+ const [domain, webhooks, subscribers, actions, schedules, jobs, integrations, publishes] =
78
76
  await Promise.all([
79
77
  singleTs(join(domainRoot, 'domain.config.ts')),
80
- tsFiles(join(domainRoot, 'api')),
81
78
  tsFiles(join(domainRoot, 'webhooks')),
82
79
  tsFiles(join(domainRoot, 'subscribers')),
83
80
  tsFiles(join(domainRoot, 'actions')),
@@ -87,5 +84,5 @@ export async function walkDomainDir(domainRoot: string): Promise<DomainFilePaths
87
84
  singleTs(join(domainRoot, 'publishes', 'events.ts')),
88
85
  ]);
89
86
 
90
- return { domain, apis, webhooks, subscribers, actions, schedules, jobs, integrations, publishes };
87
+ return { domain, webhooks, subscribers, actions, schedules, jobs, integrations, publishes };
91
88
  }
@@ -68,7 +68,7 @@ function injectLineCommentHeader(
68
68
  ): string {
69
69
  const header = [
70
70
  `${commentChar} @mc-scaffold: ${moduleId}@${version}`,
71
- `${commentChar} This file is managed by TIB scaffold. Manual edits will be flagged during \`tib upgrade\`.`,
71
+ `${commentChar} This file is managed by TIB scaffold. Manual edits will be flagged during \`npx mc-domain-module upgrade\`.`,
72
72
  `${commentChar} To opt out of upgrade management for this file, remove these header lines.`,
73
73
  '',
74
74
  ].join('\n');
@@ -92,7 +92,7 @@ function injectBlockCommentHeader(
92
92
  ): string {
93
93
  const header = [
94
94
  `${open} @mc-scaffold: ${moduleId}@${version} ${close}`,
95
- `${open} This file is managed by TIB scaffold. Manual edits will be flagged during \`tib upgrade\`. ${close}`,
95
+ `${open} This file is managed by TIB scaffold. Manual edits will be flagged during \`npx mc-domain-module upgrade\`. ${close}`,
96
96
  '',
97
97
  ].join('\n');
98
98
  return header + content;
@@ -14,7 +14,7 @@ export interface InstallResult {
14
14
  *
15
15
  * - managed: always overwrite. Return 'added' if new, 'updated' if existed.
16
16
  * - editable: check sha256. If same → 'unchanged'. If different → 'update-available'
17
- * (does not write .tib-upgrade or overwrite — user opts in via Updates tab).
17
+ * (does not write .mc-upgrade or overwrite — user opts in via Updates tab).
18
18
  * - seed: if file exists → 'skipped'. Else write → 'added'.
19
19
  */
20
20
  export async function installScaffoldFile(
@@ -29,10 +29,9 @@ const MANIFEST_PATH = '.mc/manifest.json';
29
29
  const SCHEMA_URL = 'https://mc-scaffold.s3.amazonaws.com/schema/manifest.v3.json';
30
30
 
31
31
  export function inferPolicyFromPath(filePath: string): FilePolicy {
32
- // owned: infra/, .mc/infra/, mc-deploy.yml, .github/workflows/
32
+ // owned: infra/, mc-deploy.yml, .github/workflows/
33
33
  if (
34
34
  filePath.match(/^infra\//) ||
35
- filePath.match(/^\.tib\/infra\//) ||
36
35
  filePath === 'mc-deploy.yml' ||
37
36
  filePath.match(/^\.github\/workflows\//)
38
37
  ) {
@@ -50,10 +50,15 @@ export interface CostOptions {
50
50
  reservedConcurrencyPerDomain?: Record<string, number>;
51
51
  }
52
52
 
53
+ /** NAT instance type options for cost control. */
54
+ export type NatInstanceType = 't4g.nano' | 't4g.micro' | 't4g.small';
55
+
53
56
  /** Monitoring / observability feature opt-ins. */
54
57
  export interface MonitoringOptions {
55
58
  /** Replace basic MonitoringStack with full ObservabilityStack (Grafana + Cost Explorer + CloudWatch). ~$9–18/month */
56
59
  enhanced: boolean;
60
+ /** Disable auto-generated per-domain CloudWatch dashboards. Removes existing dashboards on next deploy. */
61
+ disableCloudWatchDashboards?: boolean;
57
62
  }
58
63
 
59
64
  /** Lifecycle tunables — Phase B. Schema only in Phase A. */
@@ -92,9 +97,9 @@ export interface ScaffoldConfig {
92
97
  awsAccountId: string;
93
98
  /** Admin email collected at scaffold time (Cognito seed user). Present only when auth module is enabled. */
94
99
  adminEmail?: string;
95
- /** Domain IDs added via `tib add-domain` — NOT scaffold-owned, user-managed */
100
+ /** Domain IDs added via `mc-domain-module add-domain` — NOT scaffold-owned, user-managed */
96
101
  domainIds: string[];
97
- /** Flow IDs added via `tib add-flow` — user-managed */
102
+ /** Flow IDs added via `mc-domain-module add-flow` — user-managed */
98
103
  flowIds: string[];
99
104
  /** S3 bucket used for scaffold fetches (defaults to public TIB bucket) */
100
105
  scaffoldBucket: string;
@@ -114,6 +119,8 @@ export interface ScaffoldConfig {
114
119
  environments?: EnvironmentsConfig;
115
120
  /** When true, CORS responses include Allow-Credentials: true (requires non-wildcard origins). */
116
121
  allowCredentials?: boolean;
122
+ /** NAT instance type for the shared NAT instance. Defaults to the smallest: t4g.nano. */
123
+ natInstanceType?: NatInstanceType;
117
124
  }
118
125
 
119
126
  const DEFAULT_SCAFFOLD_CONFIG: Omit<ScaffoldConfig, 'domainIds' | 'flowIds'> = {
@@ -134,7 +141,9 @@ const DEFAULT_SCAFFOLD_CONFIG: Omit<ScaffoldConfig, 'domainIds' | 'flowIds'> = {
134
141
  logRetentionDays: 30,
135
142
  monitoringOptions: {
136
143
  enhanced: false,
144
+ disableCloudWatchDashboards: false,
137
145
  },
146
+ natInstanceType: 't4g.nano',
138
147
  };
139
148
 
140
149
  const CONFIG_PATH = '.mc/scaffold-config.json';
@@ -148,7 +157,7 @@ export async function readScaffoldConfig(projectRoot: string): Promise<ScaffoldC
148
157
  if (!raw.scaffoldVersion) {
149
158
  // eslint-disable-next-line no-console
150
159
  console.warn(
151
- '[tib] scaffold-config.json is missing scaffoldVersion — project was created before S3 scaffold migration. Run `tib upgrade` to backfill.'
160
+ '[mc-domain-module] scaffold-config.json is missing scaffoldVersion — project was created before S3 scaffold migration. Run `npx mc-domain-module upgrade` to backfill.'
152
161
  );
153
162
  }
154
163