@mettlecast/domain-cli 0.2.0

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 (301) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +133 -0
  3. package/dist/builder/build-registry.d.ts +20 -0
  4. package/dist/builder/build-registry.js +178 -0
  5. package/dist/builder/build-types.d.ts +10 -0
  6. package/dist/builder/build-types.js +32 -0
  7. package/dist/builder/load-domain-module-config.d.ts +12 -0
  8. package/dist/builder/load-domain-module-config.js +34 -0
  9. package/dist/builder/load-module.d.ts +26 -0
  10. package/dist/builder/load-module.js +114 -0
  11. package/dist/builder/zod-to-json.d.ts +10 -0
  12. package/dist/builder/zod-to-json.js +15 -0
  13. package/dist/cli.d.ts +2 -0
  14. package/dist/cli.js +338 -0
  15. package/dist/commands/add-api.d.ts +32 -0
  16. package/dist/commands/add-api.js +106 -0
  17. package/dist/commands/add-component.d.ts +15 -0
  18. package/dist/commands/add-component.js +78 -0
  19. package/dist/commands/add-domain.d.ts +21 -0
  20. package/dist/commands/add-domain.js +71 -0
  21. package/dist/commands/add-flow.d.ts +14 -0
  22. package/dist/commands/add-flow.js +108 -0
  23. package/dist/commands/add-hook.d.ts +15 -0
  24. package/dist/commands/add-hook.js +65 -0
  25. package/dist/commands/add-migration.d.ts +26 -0
  26. package/dist/commands/add-migration.js +93 -0
  27. package/dist/commands/add-module.d.ts +7 -0
  28. package/dist/commands/add-module.js +261 -0
  29. package/dist/commands/add-page.d.ts +13 -0
  30. package/dist/commands/add-page.js +170 -0
  31. package/dist/commands/add-seed-page.d.ts +5 -0
  32. package/dist/commands/add-seed-page.js +251 -0
  33. package/dist/commands/add-subscriber.d.ts +30 -0
  34. package/dist/commands/add-subscriber.js +87 -0
  35. package/dist/commands/add-test.d.ts +22 -0
  36. package/dist/commands/add-test.js +285 -0
  37. package/dist/commands/audit.d.ts +39 -0
  38. package/dist/commands/audit.js +171 -0
  39. package/dist/commands/build-catalog.d.ts +68 -0
  40. package/dist/commands/build-catalog.js +105 -0
  41. package/dist/commands/build-flows.d.ts +26 -0
  42. package/dist/commands/build-flows.js +174 -0
  43. package/dist/commands/build.d.ts +17 -0
  44. package/dist/commands/build.js +39 -0
  45. package/dist/commands/check-hashes.d.ts +38 -0
  46. package/dist/commands/check-hashes.js +97 -0
  47. package/dist/commands/create-project.d.ts +5 -0
  48. package/dist/commands/create-project.js +272 -0
  49. package/dist/commands/dev.d.ts +15 -0
  50. package/dist/commands/dev.js +27 -0
  51. package/dist/commands/doctor.d.ts +48 -0
  52. package/dist/commands/doctor.js +1301 -0
  53. package/dist/commands/explain.d.ts +34 -0
  54. package/dist/commands/explain.js +167 -0
  55. package/dist/commands/power-tune.d.ts +1 -0
  56. package/dist/commands/power-tune.js +87 -0
  57. package/dist/commands/reseed-page.d.ts +6 -0
  58. package/dist/commands/reseed-page.js +297 -0
  59. package/dist/commands/show-dns.d.ts +15 -0
  60. package/dist/commands/show-dns.js +101 -0
  61. package/dist/commands/show.d.ts +46 -0
  62. package/dist/commands/show.js +135 -0
  63. package/dist/commands/test.d.ts +18 -0
  64. package/dist/commands/test.js +56 -0
  65. package/dist/commands/upgrade-backend.d.ts +9 -0
  66. package/dist/commands/upgrade-backend.js +46 -0
  67. package/dist/commands/upgrade-event.d.ts +22 -0
  68. package/dist/commands/upgrade-event.js +111 -0
  69. package/dist/commands/upgrade-frontend.d.ts +5 -0
  70. package/dist/commands/upgrade-frontend.js +99 -0
  71. package/dist/commands/upgrade.d.ts +7 -0
  72. package/dist/commands/upgrade.js +495 -0
  73. package/dist/commands/validate.d.ts +30 -0
  74. package/dist/commands/validate.js +148 -0
  75. package/dist/runtime-stubs/hydrate-local-ctx.d.ts +49 -0
  76. package/dist/runtime-stubs/hydrate-local-ctx.js +45 -0
  77. package/dist/runtime-stubs/load-secrets.d.ts +11 -0
  78. package/dist/runtime-stubs/load-secrets.js +37 -0
  79. package/dist/server/api-server.d.ts +21 -0
  80. package/dist/server/api-server.js +14 -0
  81. package/dist/server/mount-routes.d.ts +19 -0
  82. package/dist/server/mount-routes.js +42 -0
  83. package/dist/templates/api-skeleton.d.ts +18 -0
  84. package/dist/templates/api-skeleton.js +69 -0
  85. package/dist/templates/claude-md.d.ts +4 -0
  86. package/dist/templates/claude-md.js +7 -0
  87. package/dist/templates/dashboard-pages/index.d.ts +101 -0
  88. package/dist/templates/dashboard-pages/index.js +22 -0
  89. package/dist/templates/domain-config.d.ts +10 -0
  90. package/dist/templates/domain-config.js +19 -0
  91. package/dist/templates/events-skeleton.d.ts +9 -0
  92. package/dist/templates/events-skeleton.js +23 -0
  93. package/dist/templates/patterns/api/create-with-event.d.ts +5 -0
  94. package/dist/templates/patterns/api/create-with-event.js +69 -0
  95. package/dist/templates/patterns/api/idempotent-mutation.d.ts +5 -0
  96. package/dist/templates/patterns/api/idempotent-mutation.js +99 -0
  97. package/dist/templates/patterns/api/paginated-list.d.ts +5 -0
  98. package/dist/templates/patterns/api/paginated-list.js +71 -0
  99. package/dist/templates/patterns/api/simple-crud.d.ts +6 -0
  100. package/dist/templates/patterns/api/simple-crud.js +95 -0
  101. package/dist/templates/patterns/api/system-admin.d.ts +5 -0
  102. package/dist/templates/patterns/api/system-admin.js +98 -0
  103. package/dist/templates/patterns/api/webhook-receiver-style.d.ts +5 -0
  104. package/dist/templates/patterns/api/webhook-receiver-style.js +125 -0
  105. package/dist/templates/patterns/component/index.d.ts +25 -0
  106. package/dist/templates/patterns/component/index.js +814 -0
  107. package/dist/templates/patterns/flow/autonomous-remediation.d.ts +5 -0
  108. package/dist/templates/patterns/flow/autonomous-remediation.js +98 -0
  109. package/dist/templates/patterns/flow/fan-out-reaction.d.ts +5 -0
  110. package/dist/templates/patterns/flow/fan-out-reaction.js +53 -0
  111. package/dist/templates/patterns/flow/human-in-loop.d.ts +5 -0
  112. package/dist/templates/patterns/flow/human-in-loop.js +90 -0
  113. package/dist/templates/patterns/flow/multi-step-orchestration.d.ts +5 -0
  114. package/dist/templates/patterns/flow/multi-step-orchestration.js +67 -0
  115. package/dist/templates/patterns/hook/index.d.ts +7 -0
  116. package/dist/templates/patterns/hook/index.js +378 -0
  117. package/dist/templates/patterns/migration/column-add.d.ts +5 -0
  118. package/dist/templates/patterns/migration/column-add.js +52 -0
  119. package/dist/templates/patterns/migration/event-store.d.ts +6 -0
  120. package/dist/templates/patterns/migration/event-store.js +97 -0
  121. package/dist/templates/patterns/migration/index.d.ts +5 -0
  122. package/dist/templates/patterns/migration/index.js +52 -0
  123. package/dist/templates/patterns/migration/rls-policy.d.ts +5 -0
  124. package/dist/templates/patterns/migration/rls-policy.js +78 -0
  125. package/dist/templates/patterns/migration/tenant-scoped-table.d.ts +6 -0
  126. package/dist/templates/patterns/migration/tenant-scoped-table.js +81 -0
  127. package/dist/templates/patterns/page/dashboard.d.ts +4 -0
  128. package/dist/templates/patterns/page/dashboard.js +164 -0
  129. package/dist/templates/patterns/page/detail.d.ts +4 -0
  130. package/dist/templates/patterns/page/detail.js +162 -0
  131. package/dist/templates/patterns/page/form.d.ts +4 -0
  132. package/dist/templates/patterns/page/form.js +192 -0
  133. package/dist/templates/patterns/page/index.d.ts +8 -0
  134. package/dist/templates/patterns/page/index.js +14 -0
  135. package/dist/templates/patterns/page/list.d.ts +4 -0
  136. package/dist/templates/patterns/page/list.js +138 -0
  137. package/dist/templates/patterns/page/settings.d.ts +4 -0
  138. package/dist/templates/patterns/page/settings.js +318 -0
  139. package/dist/templates/patterns/page/wizard.d.ts +4 -0
  140. package/dist/templates/patterns/page/wizard.js +293 -0
  141. package/dist/templates/patterns/subscriber/audit-relay.d.ts +5 -0
  142. package/dist/templates/patterns/subscriber/audit-relay.js +51 -0
  143. package/dist/templates/patterns/subscriber/cascade-deletion.d.ts +5 -0
  144. package/dist/templates/patterns/subscriber/cascade-deletion.js +60 -0
  145. package/dist/templates/patterns/subscriber/single-step-projection.d.ts +5 -0
  146. package/dist/templates/patterns/subscriber/single-step-projection.js +36 -0
  147. package/dist/templates/readme.d.ts +4 -0
  148. package/dist/templates/readme.js +7 -0
  149. package/dist/templates/subscriber-skeleton.d.ts +11 -0
  150. package/dist/templates/subscriber-skeleton.js +23 -0
  151. package/dist/utils/checksum.d.ts +18 -0
  152. package/dist/utils/checksum.js +39 -0
  153. package/dist/utils/file-helpers.d.ts +35 -0
  154. package/dist/utils/file-helpers.js +59 -0
  155. package/dist/utils/header-inject.d.ts +15 -0
  156. package/dist/utils/header-inject.js +93 -0
  157. package/dist/utils/install-file.d.ts +17 -0
  158. package/dist/utils/install-file.js +78 -0
  159. package/dist/utils/logger.d.ts +12 -0
  160. package/dist/utils/logger.js +24 -0
  161. package/dist/utils/manifest.d.ts +28 -0
  162. package/dist/utils/manifest.js +93 -0
  163. package/dist/utils/s3-fetch.d.ts +25 -0
  164. package/dist/utils/s3-fetch.js +52 -0
  165. package/dist/utils/scaffold-config.d.ts +95 -0
  166. package/dist/utils/scaffold-config.js +53 -0
  167. package/dist/utils/token-substitution.d.ts +35 -0
  168. package/dist/utils/token-substitution.js +52 -0
  169. package/dist/utils/zod-extractor.d.ts +10 -0
  170. package/dist/utils/zod-extractor.js +28 -0
  171. package/package.json +43 -0
  172. package/src/__tests__/build-registry.test.ts +115 -0
  173. package/src/__tests__/build-types.test.ts +100 -0
  174. package/src/__tests__/builder/walkDomainDir.test.ts +140 -0
  175. package/src/__tests__/commands/add-domain.test.ts +174 -0
  176. package/src/__tests__/commands/add-flow.test.ts +69 -0
  177. package/src/__tests__/commands/add-module.test.ts +207 -0
  178. package/src/__tests__/commands/build-flows.test.ts +154 -0
  179. package/src/__tests__/commands/check-hashes.test.ts +142 -0
  180. package/src/__tests__/commands/create-project.test.ts +712 -0
  181. package/src/__tests__/commands/upgrade.test.ts +762 -0
  182. package/src/__tests__/doctor.test.ts +291 -0
  183. package/src/__tests__/hydrate-local-ctx.test.ts +71 -0
  184. package/src/__tests__/load-domain-module-config.test.ts +10 -0
  185. package/src/__tests__/mount-routes.test.ts +100 -0
  186. package/src/__tests__/scaffold-src/part-a-layout.test.ts +46 -0
  187. package/src/__tests__/scripts/package-scaffold.test.ts +45 -0
  188. package/src/__tests__/utils/checksum.test.ts +90 -0
  189. package/src/__tests__/utils/header-inject.test.ts +89 -0
  190. package/src/__tests__/utils/install-file.test.ts +207 -0
  191. package/src/__tests__/utils/manifest.test.ts +229 -0
  192. package/src/__tests__/utils/token-substitution.test.ts +134 -0
  193. package/src/__tests__/validate.test.ts +18 -0
  194. package/src/builder/build-registry.ts +237 -0
  195. package/src/builder/build-types.ts +36 -0
  196. package/src/builder/load-domain-module-config.ts +43 -0
  197. package/src/builder/load-module.ts +133 -0
  198. package/src/builder/zod-to-json.ts +16 -0
  199. package/src/cli.ts +387 -0
  200. package/src/commands/add-api.ts +145 -0
  201. package/src/commands/add-component.ts +116 -0
  202. package/src/commands/add-domain.ts +100 -0
  203. package/src/commands/add-flow.ts +136 -0
  204. package/src/commands/add-hook.ts +100 -0
  205. package/src/commands/add-migration.ts +129 -0
  206. package/src/commands/add-module.ts +347 -0
  207. package/src/commands/add-page.ts +242 -0
  208. package/src/commands/add-seed-page.ts +338 -0
  209. package/src/commands/add-subscriber.ts +123 -0
  210. package/src/commands/add-test.ts +328 -0
  211. package/src/commands/audit.ts +217 -0
  212. package/src/commands/build-catalog.ts +181 -0
  213. package/src/commands/build-flows.ts +198 -0
  214. package/src/commands/build.ts +59 -0
  215. package/src/commands/check-hashes.ts +150 -0
  216. package/src/commands/create-project.ts +367 -0
  217. package/src/commands/dev.ts +47 -0
  218. package/src/commands/doctor.ts +1362 -0
  219. package/src/commands/explain.ts +209 -0
  220. package/src/commands/power-tune.ts +112 -0
  221. package/src/commands/reseed-page.ts +410 -0
  222. package/src/commands/show-dns.ts +139 -0
  223. package/src/commands/show.ts +179 -0
  224. package/src/commands/test.ts +91 -0
  225. package/src/commands/upgrade-backend.ts +72 -0
  226. package/src/commands/upgrade-event.ts +175 -0
  227. package/src/commands/upgrade-frontend.ts +135 -0
  228. package/src/commands/upgrade.ts +668 -0
  229. package/src/commands/validate.ts +208 -0
  230. package/src/runtime-stubs/hydrate-local-ctx.ts +102 -0
  231. package/src/runtime-stubs/load-secrets.ts +40 -0
  232. package/src/server/api-server.ts +34 -0
  233. package/src/server/mount-routes.ts +68 -0
  234. package/src/templates/api-skeleton.ts +72 -0
  235. package/src/templates/claude-md.ts +7 -0
  236. package/src/templates/dashboard-pages/account/api-keys.tsx +107 -0
  237. package/src/templates/dashboard-pages/account/audit-log.tsx +60 -0
  238. package/src/templates/dashboard-pages/account/index.ts +5 -0
  239. package/src/templates/dashboard-pages/account/members.tsx +107 -0
  240. package/src/templates/dashboard-pages/account/profile.tsx +53 -0
  241. package/src/templates/dashboard-pages/account/workspace-settings.tsx +64 -0
  242. package/src/templates/dashboard-pages/auth/accept-invitation.tsx +62 -0
  243. package/src/templates/dashboard-pages/auth/choose-org.tsx +66 -0
  244. package/src/templates/dashboard-pages/auth/forgot-password.tsx +69 -0
  245. package/src/templates/dashboard-pages/auth/index.ts +10 -0
  246. package/src/templates/dashboard-pages/auth/login.tsx +75 -0
  247. package/src/templates/dashboard-pages/auth/mfa-setup.tsx +56 -0
  248. package/src/templates/dashboard-pages/auth/mfa-verify.tsx +49 -0
  249. package/src/templates/dashboard-pages/auth/reset-password.tsx +64 -0
  250. package/src/templates/dashboard-pages/auth/sign-out.tsx +20 -0
  251. package/src/templates/dashboard-pages/auth/signup.tsx +71 -0
  252. package/src/templates/dashboard-pages/auth/verify-email.tsx +53 -0
  253. package/src/templates/dashboard-pages/index.ts +22 -0
  254. package/src/templates/dashboard-pages/shell/dashboard-home.tsx +41 -0
  255. package/src/templates/dashboard-pages/shell/forbidden.tsx +46 -0
  256. package/src/templates/dashboard-pages/shell/index.ts +5 -0
  257. package/src/templates/dashboard-pages/shell/maintenance.tsx +27 -0
  258. package/src/templates/dashboard-pages/shell/not-found.tsx +34 -0
  259. package/src/templates/dashboard-pages/shell/server-error.tsx +44 -0
  260. package/src/templates/domain-config.ts +20 -0
  261. package/src/templates/events-skeleton.ts +24 -0
  262. package/src/templates/patterns/api/create-with-event.ts +72 -0
  263. package/src/templates/patterns/api/idempotent-mutation.ts +102 -0
  264. package/src/templates/patterns/api/paginated-list.ts +74 -0
  265. package/src/templates/patterns/api/simple-crud.ts +100 -0
  266. package/src/templates/patterns/api/system-admin.ts +101 -0
  267. package/src/templates/patterns/api/webhook-receiver-style.ts +128 -0
  268. package/src/templates/patterns/component/index.ts +833 -0
  269. package/src/templates/patterns/flow/autonomous-remediation.ts +101 -0
  270. package/src/templates/patterns/flow/fan-out-reaction.ts +56 -0
  271. package/src/templates/patterns/flow/human-in-loop.ts +93 -0
  272. package/src/templates/patterns/flow/multi-step-orchestration.ts +70 -0
  273. package/src/templates/patterns/hook/index.ts +382 -0
  274. package/src/templates/patterns/migration/column-add.ts +54 -0
  275. package/src/templates/patterns/migration/event-store.ts +99 -0
  276. package/src/templates/patterns/migration/index.ts +54 -0
  277. package/src/templates/patterns/migration/rls-policy.ts +80 -0
  278. package/src/templates/patterns/migration/tenant-scoped-table.ts +83 -0
  279. package/src/templates/patterns/page/dashboard.ts +164 -0
  280. package/src/templates/patterns/page/detail.ts +162 -0
  281. package/src/templates/patterns/page/form.ts +192 -0
  282. package/src/templates/patterns/page/index.ts +17 -0
  283. package/src/templates/patterns/page/list.ts +138 -0
  284. package/src/templates/patterns/page/settings.ts +318 -0
  285. package/src/templates/patterns/page/wizard.ts +293 -0
  286. package/src/templates/patterns/subscriber/audit-relay.ts +54 -0
  287. package/src/templates/patterns/subscriber/cascade-deletion.ts +63 -0
  288. package/src/templates/patterns/subscriber/single-step-projection.ts +39 -0
  289. package/src/templates/readme.ts +7 -0
  290. package/src/templates/subscriber-skeleton.ts +28 -0
  291. package/src/utils/checksum.ts +43 -0
  292. package/src/utils/file-helpers.ts +91 -0
  293. package/src/utils/header-inject.ts +126 -0
  294. package/src/utils/install-file.ts +99 -0
  295. package/src/utils/logger.ts +26 -0
  296. package/src/utils/manifest.ts +137 -0
  297. package/src/utils/s3-fetch.ts +91 -0
  298. package/src/utils/scaffold-config.ts +172 -0
  299. package/src/utils/token-substitution.ts +80 -0
  300. package/src/utils/zod-extractor.ts +31 -0
  301. package/tsconfig.json +13 -0
@@ -0,0 +1,49 @@
1
+ import type { DomainContext } from '@mettlecast/domain-runtime';
2
+ /**
3
+ * Captured side-effects from a local-dev ctx for test inspection.
4
+ */
5
+ export interface LocalCtxCaptures {
6
+ /** Events published via ctx.publish during this invocation. */
7
+ events: Array<{
8
+ eventType: string;
9
+ data: unknown;
10
+ }>;
11
+ /** Jobs enqueued via ctx.jobs.enqueue during this invocation. */
12
+ jobs: Array<{
13
+ jobName: string;
14
+ input: unknown;
15
+ }>;
16
+ /** Flows started via ctx.flows.start during this invocation. */
17
+ executions: Array<{
18
+ flowName: string;
19
+ input: unknown;
20
+ }>;
21
+ }
22
+ /**
23
+ * Options for creating a local dev DomainContext.
24
+ */
25
+ export interface LocalCtxOptions {
26
+ /** Absolute path to the domain root (used to read .env.local). */
27
+ domainRoot: string;
28
+ /** Override tenant id. Defaults to a placeholder UUID. */
29
+ tenantId?: string;
30
+ /** Override actor sub. Defaults to 'local-dev'. */
31
+ actorSub?: string;
32
+ }
33
+ /**
34
+ * Result of hydrateLocalCtx — the context plus capture handles.
35
+ */
36
+ export interface LocalCtxResult {
37
+ /** The fully hydrated DomainContext with all stubs. */
38
+ ctx: DomainContext;
39
+ /** Captures for inspecting published events, enqueued jobs, started flows. */
40
+ captures: LocalCtxCaptures;
41
+ }
42
+ /**
43
+ * Hydrates a DomainContext with local-dev stubs — no AWS calls.
44
+ * Reads .env.local from domainRoot for the secrets stub.
45
+ * Uses capturing publishers/proxies so test output can inspect side effects.
46
+ * @param options - LocalCtxOptions controlling domain root and optional overrides.
47
+ * @returns LocalCtxResult with the hydrated ctx and capture handles.
48
+ */
49
+ export declare function hydrateLocalCtx(options: LocalCtxOptions): Promise<LocalCtxResult>;
@@ -0,0 +1,45 @@
1
+ import { hydrateCtx, noopTracer, createCapturingPublisher, createCapturingJobsProxy, createCapturingFlowsProxy, createNoopIdempotency, createCache, createFetch, createMockDb, createEmptyActionsProxy, createEmptyIntegrationsProxy, createEmptySecrets, } from '@mettlecast/domain-runtime';
2
+ import { createCliLogger } from '../utils/logger.js';
3
+ import { loadLocalSecrets } from './load-secrets.js';
4
+ /**
5
+ * Hydrates a DomainContext with local-dev stubs — no AWS calls.
6
+ * Reads .env.local from domainRoot for the secrets stub.
7
+ * Uses capturing publishers/proxies so test output can inspect side effects.
8
+ * @param options - LocalCtxOptions controlling domain root and optional overrides.
9
+ * @returns LocalCtxResult with the hydrated ctx and capture handles.
10
+ */
11
+ export async function hydrateLocalCtx(options) {
12
+ const { domainRoot, tenantId = '00000000-0000-7000-8000-000000000000', actorSub = 'local-dev', } = options;
13
+ const fileSecrets = await loadLocalSecrets(domainRoot);
14
+ // Use loaded secrets; fall back to empty secrets if none found
15
+ const secrets = fileSecrets && Object.keys(fileSecrets).length > 0
16
+ ? fileSecrets
17
+ : createEmptySecrets();
18
+ const publisherCapture = createCapturingPublisher();
19
+ const jobsCapture = createCapturingJobsProxy();
20
+ const flowsCapture = createCapturingFlowsProxy();
21
+ const ctx = await hydrateCtx({}, {
22
+ actor: { type: 'user', sub: actorSub, email: 'dev@local' },
23
+ tenant: { id: tenantId, workspaceId: tenantId },
24
+ logger: createCliLogger('domain-dev'),
25
+ tracer: noopTracer,
26
+ fetch: createFetch(),
27
+ cache: createCache(),
28
+ idempotency: createNoopIdempotency(actorSub),
29
+ secrets,
30
+ db: createMockDb(),
31
+ publish: publisherCapture.publish,
32
+ actions: createEmptyActionsProxy(),
33
+ jobs: jobsCapture.proxy,
34
+ flows: flowsCapture.proxy,
35
+ integrations: createEmptyIntegrationsProxy(),
36
+ });
37
+ return {
38
+ ctx,
39
+ captures: {
40
+ events: publisherCapture.events,
41
+ jobs: jobsCapture.jobs,
42
+ executions: flowsCapture.executions,
43
+ },
44
+ };
45
+ }
@@ -0,0 +1,11 @@
1
+ import type { SecretsProxy } from '@mettlecast/domain-runtime';
2
+ /**
3
+ * Read a `.env.local` file from the given domain root directory and return
4
+ * its entries as a SecretsProxy (Record<string, string>).
5
+ * Lines starting with `#` are treated as comments and skipped.
6
+ * Lines without `=` are skipped. Values may be quoted with single or double quotes.
7
+ * Returns an empty frozen object if the file does not exist.
8
+ * @param domainRoot - Absolute path to the domain directory.
9
+ * @returns A frozen SecretsProxy mapping env var names to string values.
10
+ */
11
+ export declare function loadLocalSecrets(domainRoot: string): Promise<SecretsProxy>;
@@ -0,0 +1,37 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ /**
4
+ * Read a `.env.local` file from the given domain root directory and return
5
+ * its entries as a SecretsProxy (Record<string, string>).
6
+ * Lines starting with `#` are treated as comments and skipped.
7
+ * Lines without `=` are skipped. Values may be quoted with single or double quotes.
8
+ * Returns an empty frozen object if the file does not exist.
9
+ * @param domainRoot - Absolute path to the domain directory.
10
+ * @returns A frozen SecretsProxy mapping env var names to string values.
11
+ */
12
+ export async function loadLocalSecrets(domainRoot) {
13
+ const filePath = join(domainRoot, '.env.local');
14
+ let raw;
15
+ try {
16
+ raw = await readFile(filePath, 'utf8');
17
+ }
18
+ catch {
19
+ return Object.freeze({});
20
+ }
21
+ const entries = raw
22
+ .split('\n')
23
+ .map(line => line.trim())
24
+ .filter(line => line.length > 0 && !line.startsWith('#') && line.includes('='))
25
+ .map(line => {
26
+ const idx = line.indexOf('=');
27
+ const key = line.slice(0, idx).trim();
28
+ let value = line.slice(idx + 1).trim();
29
+ if ((value.startsWith('"') && value.endsWith('"')) ||
30
+ (value.startsWith("'") && value.endsWith("'"))) {
31
+ value = value.slice(1, -1);
32
+ }
33
+ return [key, value];
34
+ })
35
+ .filter(([key]) => key.length > 0);
36
+ return Object.freeze(Object.fromEntries(entries));
37
+ }
@@ -0,0 +1,21 @@
1
+ import { type FastifyInstance } from 'fastify';
2
+ import type { DomainRegistry } from '@mettlecast/domain-cdk-packer';
3
+ /**
4
+ * Options for creating the local API server.
5
+ */
6
+ export interface ApiServerOptions {
7
+ /** Port to listen on. Defaults to 3000. */
8
+ port?: number;
9
+ /** Host to bind to. Defaults to '0.0.0.0'. */
10
+ host?: string;
11
+ /** The built DomainRegistry. */
12
+ registry: DomainRegistry;
13
+ /** Absolute path to the domain root. */
14
+ domainRoot: string;
15
+ }
16
+ /**
17
+ * Create and start the local Fastify API server, mounting all routes from the registry.
18
+ * @param options - ApiServerOptions.
19
+ * @returns The running FastifyInstance (caller can call server.close() to stop).
20
+ */
21
+ export declare function createApiServer(options: ApiServerOptions): Promise<FastifyInstance>;
@@ -0,0 +1,14 @@
1
+ import Fastify from 'fastify';
2
+ import { mountRoutes } from './mount-routes.js';
3
+ /**
4
+ * Create and start the local Fastify API server, mounting all routes from the registry.
5
+ * @param options - ApiServerOptions.
6
+ * @returns The running FastifyInstance (caller can call server.close() to stop).
7
+ */
8
+ export async function createApiServer(options) {
9
+ const { port = 3000, host = '0.0.0.0', registry, domainRoot } = options;
10
+ const server = Fastify({ logger: true });
11
+ await mountRoutes({ server, registry, domainRoot });
12
+ await server.listen({ port, host });
13
+ return server;
14
+ }
@@ -0,0 +1,19 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { DomainRegistry } from '@mettlecast/domain-cdk-packer';
3
+ /**
4
+ * Options for mountRoutes.
5
+ */
6
+ export interface MountRoutesOptions {
7
+ /** The Fastify server instance. */
8
+ server: FastifyInstance;
9
+ /** The built DomainRegistry. */
10
+ registry: DomainRegistry;
11
+ /** Absolute path to the domain root (needed for ctx hydration). */
12
+ domainRoot: string;
13
+ }
14
+ /**
15
+ * Mount all API routes from the DomainRegistry onto the Fastify server.
16
+ * Each route dynamically imports its handler file and hydrates a local dev ctx per request.
17
+ * @param options - MountRoutesOptions.
18
+ */
19
+ export declare function mountRoutes(options: MountRoutesOptions): Promise<void>;
@@ -0,0 +1,42 @@
1
+ import { join } from 'node:path';
2
+ import { hydrateLocalCtx } from '../runtime-stubs/hydrate-local-ctx.js';
3
+ function toFastifyPath(path) {
4
+ return path.replace(/\{(\w+)\}/g, ':$1');
5
+ }
6
+ /**
7
+ * Mount all API routes from the DomainRegistry onto the Fastify server.
8
+ * Each route dynamically imports its handler file and hydrates a local dev ctx per request.
9
+ * @param options - MountRoutesOptions.
10
+ */
11
+ export async function mountRoutes(options) {
12
+ const { server, registry, domainRoot } = options;
13
+ for (const api of registry.apis) {
14
+ const handlerAbsPath = join(domainRoot, api.handlerFile);
15
+ const fastifyPath = toFastifyPath(api.path);
16
+ const mod = await import(handlerAbsPath);
17
+ const def = Object.values(mod).find(v => v && typeof v === 'object' && v['id'] === api.id);
18
+ if (!def || typeof def['handler'] !== 'function') {
19
+ server.log.warn({ apiId: api.id, handlerAbsPath }, 'No handler function found, skipping route');
20
+ continue;
21
+ }
22
+ const handler = def['handler'];
23
+ server.route({
24
+ method: api.method === 'ANY' ? ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'] : [api.method],
25
+ url: fastifyPath,
26
+ handler: async (request, reply) => {
27
+ const { ctx } = await hydrateLocalCtx({ domainRoot });
28
+ const event = {
29
+ method: request.method,
30
+ path: request.url,
31
+ params: request.params,
32
+ query: request.query,
33
+ body: request.body,
34
+ headers: request.headers,
35
+ };
36
+ const result = await handler(event, ctx);
37
+ await reply.send(result);
38
+ },
39
+ });
40
+ server.log.info({ method: api.method, path: fastifyPath, apiId: api.id }, 'Route mounted');
41
+ }
42
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Template for generating API handler skeleton.
3
+ */
4
+ /**
5
+ * Generates an API handler skeleton with the given domain, API, and tenancy.
6
+ * @param domainId - Domain ID in kebab-case.
7
+ * @param apiId - API ID in kebab-case.
8
+ * @param tenancy - Tenancy mode: 'required', 'none', or 'system'.
9
+ * @returns TypeScript source code as a string.
10
+ */
11
+ export declare function apiSkeletonTemplate(domainId: string, apiId: string, tenancy: string): string;
12
+ /**
13
+ * Generates a fixture JSON skeleton for API testing.
14
+ * @param domainId - Domain ID in kebab-case.
15
+ * @param apiId - API ID in kebab-case.
16
+ * @returns JSON string with mock event and context for testing.
17
+ */
18
+ export declare function apiFixtureSkeleton(domainId: string, apiId: string): string;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Template for generating API handler skeleton.
3
+ */
4
+ /**
5
+ * Convert kebab-case to camelCase.
6
+ * @param s - String in kebab-case.
7
+ * @returns String in camelCase.
8
+ */
9
+ function camelCase(s) {
10
+ return s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
11
+ }
12
+ /**
13
+ * Generates an API handler skeleton with the given domain, API, and tenancy.
14
+ * @param domainId - Domain ID in kebab-case.
15
+ * @param apiId - API ID in kebab-case.
16
+ * @param tenancy - Tenancy mode: 'required', 'none', or 'system'.
17
+ * @returns TypeScript source code as a string.
18
+ */
19
+ export function apiSkeletonTemplate(domainId, apiId, tenancy) {
20
+ const tenantPath = tenancy === 'required' ? '/v1/tenants/{tenantId}' : '/v1';
21
+ return `import { z } from 'zod';
22
+ import { defineApi } from '../../../lib/domain-runtime';
23
+
24
+ export const ${camelCase(apiId)} = defineApi({
25
+ id: '${apiId}',
26
+ path: '${tenantPath}/${domainId}/${apiId}',
27
+ tenancy: '${tenancy}',
28
+ versions: {
29
+ v1: {
30
+ status: 'stable',
31
+ input: z.object({}).optional(),
32
+ output: z.object({ ok: z.boolean() }),
33
+ handler: async (_input, _ctx) => {
34
+ return { ok: true };
35
+ },
36
+ },
37
+ },
38
+ });
39
+ `;
40
+ }
41
+ /**
42
+ * Generates a fixture JSON skeleton for API testing.
43
+ * @param domainId - Domain ID in kebab-case.
44
+ * @param apiId - API ID in kebab-case.
45
+ * @returns JSON string with mock event and context for testing.
46
+ */
47
+ export function apiFixtureSkeleton(domainId, apiId) {
48
+ return JSON.stringify({
49
+ description: `Fixture for ${domainId}.${apiId}`,
50
+ event: {
51
+ version: '2.0',
52
+ requestContext: {
53
+ http: { method: 'GET', path: `/v1/${domainId}/${apiId}` },
54
+ authorizer: {
55
+ jwt: {
56
+ claims: {
57
+ sub: '00000000-0000-0000-0000-000000000000',
58
+ email: 'test@example.com',
59
+ 'custom:tenantId': '11111111-1111-1111-1111-111111111111',
60
+ },
61
+ },
62
+ },
63
+ },
64
+ pathParameters: {},
65
+ headers: { 'content-type': 'application/json', 'accept-version': '1' },
66
+ body: null,
67
+ },
68
+ }, null, 2) + '\n';
69
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Template for generating per-domain CLAUDE.md ownership file.
3
+ */
4
+ export declare function claudeMdTemplate(id: string): string;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Template for generating per-domain CLAUDE.md ownership file.
3
+ */
4
+ export function claudeMdTemplate(id) {
5
+ const titleName = id.replace(/-/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
6
+ return `# ${titleName} Domain — Ownership\n\nYou are the domain owner for \`${id}\`.\n\n## Responsibilities\n\n- All source files under \`domains/${id}/\` are owned by you.\n- API handlers must use \`defineApi\` from \`@mettlecast/domain-runtime\`.\n- Cross-domain calls must use \`ctx.actions.call()\`, never direct imports.\n- AWS SDK must never be imported directly — use \`ctx\` helpers.\n\n## Scaffold Rules\n\nSee the root \`CLAUDE.md\` for full scaffold rules, anti-patterns, and workflow commands.\n\n## Domain Config\n\nSee \`domains/${id}/domain.config.ts\` for the domain definition.\n`;
7
+ }
@@ -0,0 +1,101 @@
1
+ export declare const PAGE_REGISTRY: readonly [{
2
+ readonly id: "login";
3
+ readonly category: "auth";
4
+ readonly path: "auth/login.tsx";
5
+ readonly name: "LoginPage";
6
+ }, {
7
+ readonly id: "signup";
8
+ readonly category: "auth";
9
+ readonly path: "auth/signup.tsx";
10
+ readonly name: "SignupPage";
11
+ }, {
12
+ readonly id: "verify-email";
13
+ readonly category: "auth";
14
+ readonly path: "auth/verify-email.tsx";
15
+ readonly name: "VerifyEmailPage";
16
+ }, {
17
+ readonly id: "forgot-password";
18
+ readonly category: "auth";
19
+ readonly path: "auth/forgot-password.tsx";
20
+ readonly name: "ForgotPasswordPage";
21
+ }, {
22
+ readonly id: "reset-password";
23
+ readonly category: "auth";
24
+ readonly path: "auth/reset-password.tsx";
25
+ readonly name: "ResetPasswordPage";
26
+ }, {
27
+ readonly id: "mfa-setup";
28
+ readonly category: "auth";
29
+ readonly path: "auth/mfa-setup.tsx";
30
+ readonly name: "MfaSetupPage";
31
+ }, {
32
+ readonly id: "mfa-verify";
33
+ readonly category: "auth";
34
+ readonly path: "auth/mfa-verify.tsx";
35
+ readonly name: "MfaVerifyPage";
36
+ }, {
37
+ readonly id: "accept-invitation";
38
+ readonly category: "auth";
39
+ readonly path: "auth/accept-invitation.tsx";
40
+ readonly name: "AcceptInvitationPage";
41
+ }, {
42
+ readonly id: "choose-org";
43
+ readonly category: "auth";
44
+ readonly path: "auth/choose-org.tsx";
45
+ readonly name: "ChooseOrgPage";
46
+ }, {
47
+ readonly id: "sign-out";
48
+ readonly category: "auth";
49
+ readonly path: "auth/sign-out.tsx";
50
+ readonly name: "SignOutPage";
51
+ }, {
52
+ readonly id: "dashboard-home";
53
+ readonly category: "shell";
54
+ readonly path: "shell/dashboard-home.tsx";
55
+ readonly name: "DashboardHomePage";
56
+ }, {
57
+ readonly id: "not-found";
58
+ readonly category: "shell";
59
+ readonly path: "shell/not-found.tsx";
60
+ readonly name: "NotFoundPage";
61
+ }, {
62
+ readonly id: "forbidden";
63
+ readonly category: "shell";
64
+ readonly path: "shell/forbidden.tsx";
65
+ readonly name: "ForbiddenPage";
66
+ }, {
67
+ readonly id: "server-error";
68
+ readonly category: "shell";
69
+ readonly path: "shell/server-error.tsx";
70
+ readonly name: "ServerErrorPage";
71
+ }, {
72
+ readonly id: "maintenance";
73
+ readonly category: "shell";
74
+ readonly path: "shell/maintenance.tsx";
75
+ readonly name: "MaintenancePage";
76
+ }, {
77
+ readonly id: "profile";
78
+ readonly category: "account";
79
+ readonly path: "account/profile.tsx";
80
+ readonly name: "ProfilePage";
81
+ }, {
82
+ readonly id: "workspace-settings";
83
+ readonly category: "account";
84
+ readonly path: "account/workspace-settings.tsx";
85
+ readonly name: "WorkspaceSettingsPage";
86
+ }, {
87
+ readonly id: "members";
88
+ readonly category: "account";
89
+ readonly path: "account/members.tsx";
90
+ readonly name: "MembersPage";
91
+ }, {
92
+ readonly id: "api-keys";
93
+ readonly category: "account";
94
+ readonly path: "account/api-keys.tsx";
95
+ readonly name: "ApiKeysPage";
96
+ }, {
97
+ readonly id: "audit-log";
98
+ readonly category: "account";
99
+ readonly path: "account/audit-log.tsx";
100
+ readonly name: "AuditLogPage";
101
+ }];
@@ -0,0 +1,22 @@
1
+ export const PAGE_REGISTRY = [
2
+ { id: 'login', category: 'auth', path: 'auth/login.tsx', name: 'LoginPage' },
3
+ { id: 'signup', category: 'auth', path: 'auth/signup.tsx', name: 'SignupPage' },
4
+ { id: 'verify-email', category: 'auth', path: 'auth/verify-email.tsx', name: 'VerifyEmailPage' },
5
+ { id: 'forgot-password', category: 'auth', path: 'auth/forgot-password.tsx', name: 'ForgotPasswordPage' },
6
+ { id: 'reset-password', category: 'auth', path: 'auth/reset-password.tsx', name: 'ResetPasswordPage' },
7
+ { id: 'mfa-setup', category: 'auth', path: 'auth/mfa-setup.tsx', name: 'MfaSetupPage' },
8
+ { id: 'mfa-verify', category: 'auth', path: 'auth/mfa-verify.tsx', name: 'MfaVerifyPage' },
9
+ { id: 'accept-invitation', category: 'auth', path: 'auth/accept-invitation.tsx', name: 'AcceptInvitationPage' },
10
+ { id: 'choose-org', category: 'auth', path: 'auth/choose-org.tsx', name: 'ChooseOrgPage' },
11
+ { id: 'sign-out', category: 'auth', path: 'auth/sign-out.tsx', name: 'SignOutPage' },
12
+ { id: 'dashboard-home', category: 'shell', path: 'shell/dashboard-home.tsx', name: 'DashboardHomePage' },
13
+ { id: 'not-found', category: 'shell', path: 'shell/not-found.tsx', name: 'NotFoundPage' },
14
+ { id: 'forbidden', category: 'shell', path: 'shell/forbidden.tsx', name: 'ForbiddenPage' },
15
+ { id: 'server-error', category: 'shell', path: 'shell/server-error.tsx', name: 'ServerErrorPage' },
16
+ { id: 'maintenance', category: 'shell', path: 'shell/maintenance.tsx', name: 'MaintenancePage' },
17
+ { id: 'profile', category: 'account', path: 'account/profile.tsx', name: 'ProfilePage' },
18
+ { id: 'workspace-settings', category: 'account', path: 'account/workspace-settings.tsx', name: 'WorkspaceSettingsPage' },
19
+ { id: 'members', category: 'account', path: 'account/members.tsx', name: 'MembersPage' },
20
+ { id: 'api-keys', category: 'account', path: 'account/api-keys.tsx', name: 'ApiKeysPage' },
21
+ { id: 'audit-log', category: 'account', path: 'account/audit-log.tsx', name: 'AuditLogPage' },
22
+ ];
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Template for generating domain.config.ts skeleton.
3
+ */
4
+ /**
5
+ * Generates a domain.config.ts skeleton with the given ID and tenancy mode.
6
+ * @param id - Domain ID in kebab-case.
7
+ * @param tenancy - Tenancy mode: 'required', 'none', or 'system'.
8
+ * @returns TypeScript source code as a string.
9
+ */
10
+ export declare function domainConfigTemplate(id: string, tenancy: string): string;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Template for generating domain.config.ts skeleton.
3
+ */
4
+ /**
5
+ * Generates a domain.config.ts skeleton with the given ID and tenancy mode.
6
+ * @param id - Domain ID in kebab-case.
7
+ * @param tenancy - Tenancy mode: 'required', 'none', or 'system'.
8
+ * @returns TypeScript source code as a string.
9
+ */
10
+ export function domainConfigTemplate(id, tenancy) {
11
+ return `import { defineDomain } from '../../lib/domain-runtime';
12
+
13
+ export const domain = defineDomain({
14
+ id: '${id}',
15
+ name: '${id.replace(/-/g, ' ').replace(/\\b\\w/g, c => c.toUpperCase())}',
16
+ tenancy: '${tenancy}',
17
+ });
18
+ `;
19
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Template for generating events skeleton.
3
+ */
4
+ /**
5
+ * Generates a publishes/events.ts skeleton with an example event.
6
+ * @param domainId - Domain ID in kebab-case.
7
+ * @returns TypeScript source code as a string.
8
+ */
9
+ export declare function eventsSkeletonTemplate(domainId: string): string;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Template for generating events skeleton.
3
+ */
4
+ /**
5
+ * Generates a publishes/events.ts skeleton with an example event.
6
+ * @param domainId - Domain ID in kebab-case.
7
+ * @returns TypeScript source code as a string.
8
+ */
9
+ export function eventsSkeletonTemplate(domainId) {
10
+ return `import { z } from 'zod';
11
+ import { defineEvent } from '../../../lib/domain-runtime';
12
+
13
+ export const exampleEvent = defineEvent({
14
+ id: '${domainId}.example',
15
+ versions: [
16
+ {
17
+ version: 1,
18
+ schema: z.object({ tenantId: z.string().uuid() }),
19
+ },
20
+ ],
21
+ });
22
+ `;
23
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Create-with-event API pattern template.
3
+ * Produces: POST handler + publishes a domain event after mutation.
4
+ */
5
+ export declare function createWithEventTemplate(domain: string, id: string, tenancy: string): string;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Create-with-event API pattern template.
3
+ * Produces: POST handler + publishes a domain event after mutation.
4
+ */
5
+ function camelCase(s) {
6
+ return s.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
7
+ }
8
+ export function createWithEventTemplate(domain, id, tenancy) {
9
+ const varName = camelCase(id);
10
+ const tenantPath = tenancy === 'required' ? '/v1/tenants/{tenantId}' : '/v1';
11
+ return `import { z } from 'zod';
12
+ import { defineApi } from '@mettlecast/domain-runtime';
13
+
14
+ // ── Zod schemas ──────────────────────────────────────────────
15
+
16
+ const ${varName}Input = z.object({
17
+ name: z.string().min(1),
18
+ payload: z.record(z.unknown()).optional(),
19
+ });
20
+
21
+ const ${varName}Output = z.object({
22
+ id: z.string().uuid(),
23
+ status: z.enum(['created', 'pending']),
24
+ });
25
+
26
+ // ── API definition ───────────────────────────────────────────
27
+
28
+ export const ${varName} = defineApi({
29
+ id: '${id}',
30
+ path: '${tenantPath}/${domain}/${id}',
31
+ tenancy: '${tenancy}',
32
+ versions: {
33
+ v1: {
34
+ status: 'stable',
35
+ input: ${varName}Input,
36
+ output: ${varName}Output,
37
+ handler: async (input, ctx) => {
38
+ const entityId = crypto.randomUUID();
39
+
40
+ // ── Persist entity ────────────────────────────────────
41
+ // ctx.db.put({ ... })
42
+
43
+ // ── Publish domain event ──────────────────────────────
44
+ await ctx.publish(
45
+ '${domain}.${id}.created',
46
+ {
47
+ id: entityId,
48
+ name: input.name,
49
+ tenantId: ctx.tenantId,
50
+ },
51
+ 1,
52
+ );
53
+
54
+ await ctx.auditLog({
55
+ action: '${id}.create',
56
+ tenantId: ctx.tenantId,
57
+ entityId,
58
+ });
59
+
60
+ return {
61
+ id: entityId,
62
+ status: 'created',
63
+ };
64
+ },
65
+ },
66
+ },
67
+ });
68
+ `;
69
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Idempotent-mutation API pattern template.
3
+ * Produces: PUT with idempotency key middleware.
4
+ */
5
+ export declare function idempotentMutationTemplate(domain: string, id: string, tenancy: string): string;