@empire-builder-kit/nx 1.0.0-beta.4

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 (185) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +5 -0
  3. package/README.md +25 -0
  4. package/executors.json +54 -0
  5. package/generators.json +35 -0
  6. package/migrations/standard-pre-v1-to-v1.md +11 -0
  7. package/package.json +66 -0
  8. package/src/blueprints/execution.d.ts +31 -0
  9. package/src/blueprints/execution.js +166 -0
  10. package/src/blueprints/execution.js.map +1 -0
  11. package/src/blueprints/index.d.ts +5 -0
  12. package/src/blueprints/index.js +9 -0
  13. package/src/blueprints/index.js.map +1 -0
  14. package/src/blueprints/inputs.d.ts +43 -0
  15. package/src/blueprints/inputs.js +103 -0
  16. package/src/blueprints/inputs.js.map +1 -0
  17. package/src/blueprints/manifest.d.ts +241 -0
  18. package/src/blueprints/manifest.js +369 -0
  19. package/src/blueprints/manifest.js.map +1 -0
  20. package/src/blueprints/resolver.d.ts +28 -0
  21. package/src/blueprints/resolver.js +214 -0
  22. package/src/blueprints/resolver.js.map +1 -0
  23. package/src/blueprints/transitions.d.ts +20 -0
  24. package/src/blueprints/transitions.js +61 -0
  25. package/src/blueprints/transitions.js.map +1 -0
  26. package/src/executors/deploy/deploy.d.ts +4 -0
  27. package/src/executors/deploy/deploy.js +6 -0
  28. package/src/executors/deploy/deploy.js.map +1 -0
  29. package/src/executors/dev/dev.d.ts +4 -0
  30. package/src/executors/dev/dev.js +6 -0
  31. package/src/executors/dev/dev.js.map +1 -0
  32. package/src/executors/dev-doctor/dev-doctor.d.ts +18 -0
  33. package/src/executors/dev-doctor/dev-doctor.js +145 -0
  34. package/src/executors/dev-doctor/dev-doctor.js.map +1 -0
  35. package/src/executors/dev-doctor/schema.json +17 -0
  36. package/src/executors/dev-setup/dev-setup.d.ts +11 -0
  37. package/src/executors/dev-setup/dev-setup.js +454 -0
  38. package/src/executors/dev-setup/dev-setup.js.map +1 -0
  39. package/src/executors/dev-setup/schema.json +16 -0
  40. package/src/executors/fleet/fleet.d.ts +51 -0
  41. package/src/executors/fleet/fleet.js +529 -0
  42. package/src/executors/fleet/fleet.js.map +1 -0
  43. package/src/executors/fleet/schema.json +25 -0
  44. package/src/executors/foundation-preflight/foundation-preflight.d.ts +15 -0
  45. package/src/executors/foundation-preflight/foundation-preflight.js +94 -0
  46. package/src/executors/foundation-preflight/foundation-preflight.js.map +1 -0
  47. package/src/executors/foundation-preflight/schema.json +31 -0
  48. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.d.ts +13 -0
  49. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js +69 -0
  50. package/src/executors/foundation-remove-preflight/foundation-remove-preflight.js.map +1 -0
  51. package/src/executors/foundation-remove-preflight/schema.json +17 -0
  52. package/src/executors/refresh/refresh.d.ts +4 -0
  53. package/src/executors/refresh/refresh.js +6 -0
  54. package/src/executors/refresh/refresh.js.map +1 -0
  55. package/src/executors/remove/remove.d.ts +4 -0
  56. package/src/executors/remove/remove.js +6 -0
  57. package/src/executors/remove/remove.js.map +1 -0
  58. package/src/executors/sst-lifecycle/run-sst-command.d.ts +28 -0
  59. package/src/executors/sst-lifecycle/run-sst-command.js +154 -0
  60. package/src/executors/sst-lifecycle/run-sst-command.js.map +1 -0
  61. package/src/executors/sst-lifecycle/schema.json +45 -0
  62. package/src/executors/unlock/unlock.d.ts +4 -0
  63. package/src/executors/unlock/unlock.js +6 -0
  64. package/src/executors/unlock/unlock.js.map +1 -0
  65. package/src/foundation/aurora-logical-database.d.ts +76 -0
  66. package/src/foundation/aurora-logical-database.js +419 -0
  67. package/src/foundation/aurora-logical-database.js.map +1 -0
  68. package/src/foundation/discovery.d.ts +55 -0
  69. package/src/foundation/discovery.js +55 -0
  70. package/src/foundation/discovery.js.map +1 -0
  71. package/src/foundation/index.d.ts +6 -0
  72. package/src/foundation/index.js +10 -0
  73. package/src/foundation/index.js.map +1 -0
  74. package/src/foundation/local-database-contract.d.ts +30 -0
  75. package/src/foundation/local-database-contract.js +75 -0
  76. package/src/foundation/local-database-contract.js.map +1 -0
  77. package/src/foundation/preflight.d.ts +31 -0
  78. package/src/foundation/preflight.js +114 -0
  79. package/src/foundation/preflight.js.map +1 -0
  80. package/src/foundation/provision-database-bindings.d.ts +30 -0
  81. package/src/foundation/provision-database-bindings.js +113 -0
  82. package/src/foundation/provision-database-bindings.js.map +1 -0
  83. package/src/foundation/provision-logical-database.d.ts +26 -0
  84. package/src/foundation/provision-logical-database.js +162 -0
  85. package/src/foundation/provision-logical-database.js.map +1 -0
  86. package/src/foundation/requirements.d.ts +37 -0
  87. package/src/foundation/requirements.js +141 -0
  88. package/src/foundation/requirements.js.map +1 -0
  89. package/src/foundation/stages.d.ts +14 -0
  90. package/src/foundation/stages.js +51 -0
  91. package/src/foundation/stages.js.map +1 -0
  92. package/src/generators/function/function.d.ts +13 -0
  93. package/src/generators/function/function.js +71 -0
  94. package/src/generators/function/function.js.map +1 -0
  95. package/src/generators/function/schema.d.ts +5 -0
  96. package/src/generators/function/schema.json +20 -0
  97. package/src/generators/job/job.d.ts +4 -0
  98. package/src/generators/job/job.js +5 -0
  99. package/src/generators/job/job.js.map +1 -0
  100. package/src/generators/preset/generator.d.ts +5 -0
  101. package/src/generators/preset/generator.js +41 -0
  102. package/src/generators/preset/generator.js.map +1 -0
  103. package/src/generators/preset/schema.d.ts +4 -0
  104. package/src/generators/preset/schema.json +22 -0
  105. package/src/generators/preset/workspace-files.d.ts +18 -0
  106. package/src/generators/preset/workspace-files.js +895 -0
  107. package/src/generators/preset/workspace-files.js.map +1 -0
  108. package/src/generators/service/service.d.ts +4 -0
  109. package/src/generators/service/service.js +5 -0
  110. package/src/generators/service/service.js.map +1 -0
  111. package/src/generators/slice/schema.d.ts +7 -0
  112. package/src/generators/slice/schema.json +38 -0
  113. package/src/generators/slice/slice.d.ts +26 -0
  114. package/src/generators/slice/slice.js +1676 -0
  115. package/src/generators/slice/slice.js.map +1 -0
  116. package/src/generators/upgrade/schema.d.ts +8 -0
  117. package/src/generators/upgrade/schema.json +31 -0
  118. package/src/generators/upgrade/upgrade.d.ts +27 -0
  119. package/src/generators/upgrade/upgrade.js +666 -0
  120. package/src/generators/upgrade/upgrade.js.map +1 -0
  121. package/src/generators/workload/schema.d.ts +6 -0
  122. package/src/generators/workload/schema.json +21 -0
  123. package/src/generators/workload/workload.d.ts +16 -0
  124. package/src/generators/workload/workload.js +157 -0
  125. package/src/generators/workload/workload.js.map +1 -0
  126. package/src/index.d.ts +5 -0
  127. package/src/index.js +9 -0
  128. package/src/index.js.map +1 -0
  129. package/src/local-dev/contracts.d.ts +31 -0
  130. package/src/local-dev/contracts.js +126 -0
  131. package/src/local-dev/contracts.js.map +1 -0
  132. package/src/local-dev/gateway-daemon.d.ts +4 -0
  133. package/src/local-dev/gateway-daemon.js +39 -0
  134. package/src/local-dev/gateway-daemon.js.map +1 -0
  135. package/src/local-dev/gateway.d.ts +60 -0
  136. package/src/local-dev/gateway.js +368 -0
  137. package/src/local-dev/gateway.js.map +1 -0
  138. package/src/local-dev/identity.d.ts +16 -0
  139. package/src/local-dev/identity.js +101 -0
  140. package/src/local-dev/identity.js.map +1 -0
  141. package/src/local-dev/index.d.ts +7 -0
  142. package/src/local-dev/index.js +11 -0
  143. package/src/local-dev/index.js.map +1 -0
  144. package/src/local-dev/portless.d.ts +15 -0
  145. package/src/local-dev/portless.js +115 -0
  146. package/src/local-dev/portless.js.map +1 -0
  147. package/src/local-dev/ports.d.ts +10 -0
  148. package/src/local-dev/ports.js +39 -0
  149. package/src/local-dev/ports.js.map +1 -0
  150. package/src/local-dev/proxy.d.ts +37 -0
  151. package/src/local-dev/proxy.js +271 -0
  152. package/src/local-dev/proxy.js.map +1 -0
  153. package/src/local-dev/tls.d.ts +24 -0
  154. package/src/local-dev/tls.js +294 -0
  155. package/src/local-dev/tls.js.map +1 -0
  156. package/src/ownership/composition.d.ts +16 -0
  157. package/src/ownership/composition.js +125 -0
  158. package/src/ownership/composition.js.map +1 -0
  159. package/src/ownership/foundation-record.d.ts +66 -0
  160. package/src/ownership/foundation-record.js +108 -0
  161. package/src/ownership/foundation-record.js.map +1 -0
  162. package/src/ownership/hash.d.ts +2 -0
  163. package/src/ownership/hash.js +10 -0
  164. package/src/ownership/hash.js.map +1 -0
  165. package/src/ownership/index.d.ts +6 -0
  166. package/src/ownership/index.js +10 -0
  167. package/src/ownership/index.js.map +1 -0
  168. package/src/ownership/json-ownership.d.ts +13 -0
  169. package/src/ownership/json-ownership.js +147 -0
  170. package/src/ownership/json-ownership.js.map +1 -0
  171. package/src/ownership/record.d.ts +186 -0
  172. package/src/ownership/record.js +336 -0
  173. package/src/ownership/record.js.map +1 -0
  174. package/src/ownership/workspace-record.d.ts +66 -0
  175. package/src/ownership/workspace-record.js +108 -0
  176. package/src/ownership/workspace-record.js.map +1 -0
  177. package/src/schemas/blueprint-input-schema-v1.json +23 -0
  178. package/src/schemas/blueprint-v1.json +170 -0
  179. package/src/schemas/foundation-ownership-v2.json +42 -0
  180. package/src/schemas/foundation-requirement-v1.json +32 -0
  181. package/src/schemas/generated-ownership-v2.json +117 -0
  182. package/src/schemas/workspace-ownership-v2.json +42 -0
  183. package/src/validation.d.ts +14 -0
  184. package/src/validation.js +14 -0
  185. package/src/validation.js.map +1 -0
@@ -0,0 +1,895 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WORKSPACE_PACKAGE_MANAGER_VERSION = exports.WORKSPACE_PROJECT_MANAGED_JSON_POINTERS = void 0;
4
+ exports.renderWorkspaceFiles = renderWorkspaceFiles;
5
+ exports.renderWorkspaceRootFilePlan = renderWorkspaceRootFilePlan;
6
+ exports.renderFoundationFilePlan = renderFoundationFilePlan;
7
+ const index_js_1 = require("../../ownership/index.js");
8
+ const TOOL_VERSIONS = {
9
+ eslint: '^9.8.0',
10
+ eslintJs: '^9.8.0',
11
+ globals: '^16.0.0',
12
+ nodeTypes: '25.5.0',
13
+ nx: '23.1.0',
14
+ pnpm: '10.30.1',
15
+ prettier: '^3.8.2',
16
+ sst: '4.11.0',
17
+ typescript: '~5.9.2',
18
+ typescriptEslint: '^8.40.0',
19
+ };
20
+ function json(value) {
21
+ return `${JSON.stringify(value, null, 2)}\n`;
22
+ }
23
+ const FOUNDATION_ROOT = 'packages/foundation/';
24
+ const FOUNDATION_OWNERSHIP_PATH = `${FOUNDATION_ROOT}.ebk/ownership.json`;
25
+ const FOUNDATION_TOOL_OWNED_PATHS = new Set(['sst-env.d.ts']);
26
+ const WORKSPACE_OWNERSHIP_PATH = '.ebk/ownership.json';
27
+ const WORKSPACE_SEEDED_PATHS = new Set([
28
+ 'README.md',
29
+ 'docs/architecture.md',
30
+ 'package.json',
31
+ ]);
32
+ exports.WORKSPACE_PROJECT_MANAGED_JSON_POINTERS = [
33
+ '/name',
34
+ '/$schema',
35
+ '/projectType',
36
+ '/tags',
37
+ '/targets/dev',
38
+ '/targets/dev-doctor',
39
+ '/targets/dev-setup',
40
+ '/targets/verify',
41
+ '/targets/workspace-health',
42
+ ];
43
+ const FOUNDATION_SEEDED_PATHS = new Set([
44
+ 'README.md',
45
+ 'docs/costs.md',
46
+ 'docs/runbooks.md',
47
+ 'docs/security.md',
48
+ ]);
49
+ function createFoundationOwnership(files, frameworkVersion) {
50
+ const paths = Object.keys(files)
51
+ .filter((path) => path.startsWith(FOUNDATION_ROOT) &&
52
+ path !== FOUNDATION_OWNERSHIP_PATH &&
53
+ !FOUNDATION_TOOL_OWNED_PATHS.has(path.slice(FOUNDATION_ROOT.length)))
54
+ .map((path) => {
55
+ const relativePath = path.slice(FOUNDATION_ROOT.length);
56
+ return {
57
+ mode: FOUNDATION_SEEDED_PATHS.has(relativePath)
58
+ ? 'seeded'
59
+ : 'managed',
60
+ origin: 'framework',
61
+ path: relativePath,
62
+ sha256: (0, index_js_1.hashGeneratedContent)(files[path] ?? ''),
63
+ };
64
+ });
65
+ paths.sort((left, right) => left.path.localeCompare(right.path));
66
+ return {
67
+ frameworkVersion,
68
+ kind: 'foundation',
69
+ paths,
70
+ schemaVersion: index_js_1.FOUNDATION_OWNERSHIP_SCHEMA_VERSION,
71
+ };
72
+ }
73
+ function createWorkspaceOwnership(files, frameworkVersion) {
74
+ const paths = Object.keys(files)
75
+ .filter((path) => !path.startsWith(FOUNDATION_ROOT) && path !== WORKSPACE_OWNERSHIP_PATH)
76
+ .map((path) => {
77
+ const ownership = {
78
+ ...(path === 'project.json'
79
+ ? {
80
+ managedJsonPointers: [...exports.WORKSPACE_PROJECT_MANAGED_JSON_POINTERS],
81
+ }
82
+ : {}),
83
+ mode: WORKSPACE_SEEDED_PATHS.has(path)
84
+ ? 'seeded'
85
+ : 'managed',
86
+ origin: 'framework',
87
+ path,
88
+ };
89
+ return {
90
+ ...ownership,
91
+ sha256: (0, index_js_1.hashOwnedContent)(files[path] ?? '', ownership),
92
+ };
93
+ });
94
+ paths.sort((left, right) => left.path.localeCompare(right.path));
95
+ return {
96
+ frameworkVersion,
97
+ kind: 'workspace',
98
+ paths,
99
+ schemaVersion: index_js_1.WORKSPACE_OWNERSHIP_SCHEMA_VERSION,
100
+ };
101
+ }
102
+ function renderWorkspaceFiles(options) {
103
+ const { frameworkVersion, name } = options;
104
+ const files = {
105
+ '.github/workflows/ci.yml': `name: Verify
106
+
107
+ on:
108
+ pull_request:
109
+ push:
110
+ branches: [main]
111
+
112
+ concurrency:
113
+ group: verify-\${{ github.ref }}
114
+ cancel-in-progress: true
115
+
116
+ permissions:
117
+ contents: read
118
+
119
+ jobs:
120
+ verify:
121
+ runs-on: ubuntu-latest
122
+ steps:
123
+ - uses: actions/checkout@v6
124
+ - uses: pnpm/action-setup@v5
125
+ - uses: actions/setup-node@v6
126
+ with:
127
+ node-version: 22
128
+ cache: pnpm
129
+ - run: pnpm install --frozen-lockfile
130
+ - run: pnpm nx format:check
131
+ - run: pnpm nx run-many -t verify --parallel=3
132
+ `,
133
+ '.github/workflows/deploy.yml': `name: Deploy
134
+
135
+ on:
136
+ workflow_dispatch:
137
+ inputs:
138
+ stage:
139
+ description: Deployment stage
140
+ required: true
141
+ default: development
142
+ type: choice
143
+ options: [development, staging, production]
144
+ action:
145
+ description: Lifecycle action
146
+ required: true
147
+ default: deploy
148
+ type: choice
149
+ options: [deploy, refresh, unlock, remove]
150
+
151
+ concurrency:
152
+ group: deploy-\${{ inputs.stage }}
153
+ cancel-in-progress: false
154
+
155
+ permissions:
156
+ contents: read
157
+ id-token: write
158
+
159
+ jobs:
160
+ deploy:
161
+ if: github.ref == 'refs/heads/main'
162
+ environment: \${{ inputs.stage }}
163
+ env:
164
+ EBK_ALLOW_PRODUCTION_REMOVE: \${{ vars.EBK_ALLOW_PRODUCTION_REMOVE || 'false' }}
165
+ runs-on: ubuntu-latest
166
+ steps:
167
+ - uses: actions/checkout@v6
168
+ - uses: aws-actions/configure-aws-credentials@v6
169
+ with:
170
+ role-to-assume: \${{ secrets.AWS_DEPLOY_ROLE_ARN }}
171
+ aws-region: \${{ vars.AWS_REGION || 'us-east-1' }}
172
+ - uses: pnpm/action-setup@v5
173
+ - uses: actions/setup-node@v6
174
+ with:
175
+ node-version: 22
176
+ cache: pnpm
177
+ - run: pnpm install --frozen-lockfile
178
+ - run: pnpm nx run-many -t \${{ inputs.action }} --stage=\${{ inputs.stage }} --parallel=1
179
+ `,
180
+ '.gitignore': `.nx/cache
181
+ .nx/workspace-data
182
+ .ebk/cache
183
+ .next
184
+ .open-next
185
+ dist
186
+ *.tsbuildinfo
187
+ node_modules
188
+ .sst
189
+ .env
190
+ .env.*
191
+ !.env.example
192
+ `,
193
+ '.prettierignore': `node_modules
194
+ dist
195
+ .nx
196
+ .next
197
+ .open-next
198
+ .sst
199
+ **/next-env.d.ts
200
+ pnpm-lock.yaml
201
+ `,
202
+ '.prettierrc.json': json({ singleQuote: true }),
203
+ 'eslint.config.mjs': `import eslint from '@eslint/js';
204
+ import globals from 'globals';
205
+ import tseslint from 'typescript-eslint';
206
+
207
+ export default tseslint.config(
208
+ { ignores: ['**/.next/**', '**/.open-next/**', '**/.sst/**', '**/dist/**', '**/node_modules/**'] },
209
+ eslint.configs.recommended,
210
+ ...tseslint.configs.recommended,
211
+ {
212
+ languageOptions: { globals: globals.node },
213
+ rules: { '@typescript-eslint/no-explicit-any': 'off' },
214
+ },
215
+ );
216
+ `,
217
+ 'README.md': `# ${name}
218
+
219
+ This workspace was created with Empire Builder Kit v1.
220
+
221
+ ## Start here
222
+
223
+ 1. Review \`docs/architecture.md\`.
224
+ 2. Review the generated Foundation before deploying it.
225
+ 3. Add product slices through the Empire Builder Kit Nx plugin.
226
+
227
+ For one-origin fleet development, first run \`pnpm nx run ${name}:dev-setup\`, inspect the
228
+ generated root certificate, then explicitly approve \`--trust --portless\`. Diagnose without
229
+ mutation through \`pnpm nx run ${name}:dev-doctor --stage=<personal-stage>\`. Start selected
230
+ slices with \`pnpm nx run ${name}:dev --slices=billing,accounts --stage=<personal-stage>\`.
231
+
232
+ Workspace creation only writes source files. It does not create or change cloud resources.
233
+ `,
234
+ 'docs/architecture.md': `# Workspace architecture
235
+
236
+ This workspace uses three public Empire Builder Kit packages:
237
+
238
+ - \`@empire-builder-kit/create\` creates the workspace source tree.
239
+ - \`@empire-builder-kit/nx\` owns generators and infrastructure lifecycle executors.
240
+ - \`@empire-builder-kit/runtime\` owns application-facing runtime contracts.
241
+
242
+ The Standard Foundation lives at \`packages/foundation\`. Product slices live beside it under
243
+ \`packages/<slice-name>\` and consume versioned Foundation contracts instead of importing
244
+ Foundation implementation files.
245
+
246
+ Canonical consumer stages use the matching \`development\`, \`staging\`, or \`production\`
247
+ Foundation. Personal and preview consumer stages may use only the \`development\` Foundation.
248
+ `,
249
+ 'nx.json': json({
250
+ $schema: './node_modules/nx/schemas/nx-schema.json',
251
+ defaultBase: 'main',
252
+ namedInputs: {
253
+ default: ['{projectRoot}/**/*', 'sharedGlobals'],
254
+ production: [
255
+ 'default',
256
+ '!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)',
257
+ '!{projectRoot}/tsconfig.spec.json',
258
+ ],
259
+ sharedGlobals: [],
260
+ },
261
+ targetDefaults: {
262
+ build: {
263
+ cache: true,
264
+ dependsOn: ['^build'],
265
+ inputs: ['production', '^production'],
266
+ },
267
+ lint: {
268
+ cache: true,
269
+ inputs: ['default', '^production'],
270
+ },
271
+ test: {
272
+ cache: true,
273
+ dependsOn: ['^build'],
274
+ inputs: ['default', '^production'],
275
+ },
276
+ typecheck: {
277
+ cache: true,
278
+ dependsOn: ['^build'],
279
+ inputs: ['default', '^production'],
280
+ },
281
+ },
282
+ neverConnectToCloud: true,
283
+ }),
284
+ 'package.json': json({
285
+ name,
286
+ version: '0.0.0',
287
+ private: true,
288
+ packageManager: `pnpm@${TOOL_VERSIONS.pnpm}`,
289
+ engines: {
290
+ node: '>=22.12.0',
291
+ pnpm: '>=10.0.0',
292
+ },
293
+ scripts: {
294
+ build: `nx run-many -t build --exclude=${name}`,
295
+ format: 'nx format:write',
296
+ 'format:check': 'nx format:check',
297
+ graph: 'nx graph',
298
+ health: `nx run ${name}:workspace-health`,
299
+ lint: `nx run-many -t lint --exclude=${name}`,
300
+ test: `nx run-many -t test --exclude=${name}`,
301
+ typecheck: `nx run-many -t typecheck --exclude=${name}`,
302
+ verify: `nx run ${name}:workspace-health && nx run-many -t verify --exclude=${name}`,
303
+ dev: `nx run ${name}:dev`,
304
+ },
305
+ dependencies: {
306
+ '@empire-builder-kit/runtime': frameworkVersion,
307
+ },
308
+ devDependencies: {
309
+ '@eslint/js': TOOL_VERSIONS.eslintJs,
310
+ '@empire-builder-kit/nx': frameworkVersion,
311
+ '@types/node': TOOL_VERSIONS.nodeTypes,
312
+ eslint: TOOL_VERSIONS.eslint,
313
+ globals: TOOL_VERSIONS.globals,
314
+ nx: TOOL_VERSIONS.nx,
315
+ prettier: TOOL_VERSIONS.prettier,
316
+ sst: TOOL_VERSIONS.sst,
317
+ typescript: TOOL_VERSIONS.typescript,
318
+ 'typescript-eslint': TOOL_VERSIONS.typescriptEslint,
319
+ },
320
+ }),
321
+ 'project.json': json({
322
+ name,
323
+ $schema: './node_modules/nx/schemas/project-schema.json',
324
+ projectType: 'application',
325
+ tags: ['scope:workspace', 'layer:governance'],
326
+ targets: {
327
+ dev: {
328
+ continuous: true,
329
+ executor: '@empire-builder-kit/nx:fleet',
330
+ options: {
331
+ stage: 'personal',
332
+ },
333
+ },
334
+ 'dev-doctor': {
335
+ executor: '@empire-builder-kit/nx:dev-doctor',
336
+ options: {},
337
+ },
338
+ 'dev-setup': {
339
+ executor: '@empire-builder-kit/nx:dev-setup',
340
+ options: {},
341
+ },
342
+ verify: {
343
+ executor: 'nx:run-commands',
344
+ options: { command: 'node tools/workspace-health.mjs' },
345
+ },
346
+ 'workspace-health': {
347
+ executor: 'nx:run-commands',
348
+ options: { command: 'node tools/workspace-health.mjs' },
349
+ },
350
+ },
351
+ }),
352
+ 'packages/foundation/README.md': `# Standard Foundation
353
+
354
+ This project owns the workspace-wide AWS platform for each canonical \`development\`,
355
+ \`staging\`, or \`production\` stage. It is a separate SST state boundary from product slices.
356
+ Never deploy it at a personal or preview stage; those consumers map to \`development\`.
357
+
358
+ It provides the v1 network, routing, container compute, Postgres, identity, events,
359
+ observability, configuration, and deployment capabilities. Product slices consume its
360
+ versioned contract; they do not import files from \`infra/\`.
361
+
362
+ Workspace creation does not deploy this project. Review \`docs/costs.md\`,
363
+ \`docs/security.md\`, and \`docs/runbooks.md\` before the first deployment.
364
+
365
+ Set \`EBK_ROOT_DOMAIN\` before deploying when Route 53 in the target account owns a public
366
+ domain. Without it, the shared Router uses its functional provider URL.
367
+ `,
368
+ 'packages/foundation/contracts/foundation.json': json({
369
+ contractVersion: '1.0.0',
370
+ capabilities: [
371
+ 'network',
372
+ 'routing',
373
+ 'container-compute',
374
+ 'postgres',
375
+ 'identity',
376
+ 'events',
377
+ 'observability',
378
+ 'configuration',
379
+ 'deployment',
380
+ ],
381
+ }),
382
+ 'packages/foundation/contracts/costs.json': json({
383
+ schemaVersion: 1,
384
+ stages: {
385
+ development: {
386
+ aurora: 'scale-to-zero',
387
+ dataRetention: 'retained',
388
+ nat: 'ec2',
389
+ },
390
+ staging: {
391
+ aurora: 'scale-to-zero',
392
+ dataRetention: 'retained',
393
+ nat: 'ec2',
394
+ },
395
+ production: {
396
+ aurora: 'always-warm',
397
+ dataRetention: 'retained-and-protected',
398
+ nat: 'managed',
399
+ },
400
+ },
401
+ usagePriced: [
402
+ 'cloudwatch',
403
+ 'cognito',
404
+ 'ecs-workloads',
405
+ 'eventbridge',
406
+ 'sns',
407
+ 'ssm',
408
+ ],
409
+ }),
410
+ 'packages/foundation/docs/costs.md': `# Foundation cost contract
411
+
412
+ The VPC NAT path and Aurora capacity are the main fixed-cost decisions.
413
+ The machine-readable stage inventory is \`../contracts/costs.json\`; keep this
414
+ seeded decision record aligned with organization-specific budgets and alerts.
415
+
416
+ - Development uses EC2 NAT, a development bastion, and Aurora scale-to-zero.
417
+ - Staging uses EC2 NAT and Aurora scale-to-zero by default.
418
+ - Production uses managed NAT and an always-warm Aurora baseline.
419
+ - ECS, EventBridge, Cognito, SNS, SSM, and CloudWatch are primarily usage-priced; logs,
420
+ metrics, alarms, and retained data still require active cost review.
421
+
422
+ Run \`foundation:remove\` only for disposable stages. Production is retained and protected by
423
+ the SST configuration.
424
+ `,
425
+ 'packages/foundation/docs/runbooks.md': `# Foundation runbooks
426
+
427
+ ## Deployment
428
+
429
+ Use Nx for every supported lifecycle action:
430
+
431
+ - \`pnpm nx run foundation:deploy --stage=development\`
432
+ - \`pnpm nx run foundation:refresh --stage=development\`
433
+ - \`pnpm nx run foundation:unlock --stage=development\`
434
+ - \`pnpm nx run foundation:remove --stage=development\`
435
+
436
+ Do not invoke SST lifecycle commands directly. Production deployment belongs in the trusted
437
+ CI path.
438
+
439
+ Foundation stages are canonical. Run a Product Slice with \`--stage=<personal-name>\` to map
440
+ that consumer to the development Foundation. The slice preflight reports both stages; it
441
+ does not deploy a personal Foundation. Full mapped personal-stage deploys fail closed.
442
+
443
+ ## Database restore
444
+
445
+ Production Aurora retains 30 days of automated backups; non-production retains 7 days.
446
+ Restore into a new cluster, validate application migrations and data integrity, then cut over
447
+ consumers deliberately. Never expose the Foundation master credential to a product slice.
448
+ `,
449
+ 'packages/foundation/docs/security.md': `# Foundation security contract
450
+
451
+ - Production resources are retained and protected.
452
+ - The shared Aurora master credential is Foundation-internal.
453
+ - Product slices receive scoped logical-database credentials through the provisioning contract.
454
+ - Cognito owns authentication; product slices own authorization policy and app clients.
455
+ - Lambda and server-side web workloads use resource linking. Containers use the documented
456
+ stage-scoped SSM/secret path and least-privilege IAM.
457
+ - Public domains are configured explicitly through \`EBK_ROOT_DOMAIN\`; generated source never
458
+ contains credentials or customer domain assumptions.
459
+ `,
460
+ 'packages/foundation/ebk-globals.d.ts': `// SST and Pulumi inject these globals while evaluating sst.config.ts.
461
+ declare const sst: any;
462
+ declare const aws: any;
463
+ declare const $config: any;
464
+ declare const $app: {
465
+ name: string;
466
+ stage: string;
467
+ removal: 'remove' | 'retain' | 'retain-all';
468
+ protect: boolean;
469
+ };
470
+ `,
471
+ 'packages/foundation/infra/foundation.ts': `import {
472
+ FOUNDATION_SSM_SCHEMA_VERSION,
473
+ STANDARD_FOUNDATION_CAPABILITIES,
474
+ foundationSsmPath,
475
+ type FoundationSsmKey,
476
+ } from '@empire-builder-kit/nx/foundation';
477
+
478
+ function publishFoundation(values: Record<FoundationSsmKey, any>) {
479
+ return Object.fromEntries(
480
+ Object.entries(values).map(([key, value]) => [
481
+ key,
482
+ new aws.ssm.Parameter('FoundationSsm_' + key, {
483
+ name: foundationSsmPath($app.stage, key as FoundationSsmKey),
484
+ type: 'String',
485
+ value,
486
+ }),
487
+ ]),
488
+ );
489
+ }
490
+
491
+ export function foundation() {
492
+ const isProduction = $app.stage === 'production';
493
+ const isStaging = $app.stage === 'staging';
494
+ const rootDomain = process.env.EBK_ROOT_DOMAIN?.trim();
495
+
496
+ const vpc = new sst.aws.Vpc('FoundationVpc', {
497
+ az: 2,
498
+ bastion: $app.stage === 'development',
499
+ nat: isProduction ? 'managed' : 'ec2',
500
+ });
501
+ const cluster = new sst.aws.Cluster('FoundationCluster', { vpc });
502
+ const router = new sst.aws.Router('FoundationRouter', {
503
+ ...(rootDomain
504
+ ? {
505
+ domain: {
506
+ name: rootDomain,
507
+ aliases: ['*.' + rootDomain],
508
+ dns: sst.aws.dns(),
509
+ },
510
+ }
511
+ : {}),
512
+ });
513
+ const database = new sst.aws.Aurora('FoundationDatabase', {
514
+ dataApi: true,
515
+ engine: 'postgres',
516
+ scaling: isProduction
517
+ ? { min: '2 ACU', max: '16 ACU' }
518
+ : isStaging
519
+ ? { min: '0 ACU', max: '4 ACU', pauseAfter: '5 minutes' }
520
+ : { min: '0 ACU', max: '2 ACU', pauseAfter: '5 minutes' },
521
+ transform: {
522
+ cluster: {
523
+ backupRetentionPeriod: isProduction ? 30 : 7,
524
+ deletionProtection: isProduction,
525
+ },
526
+ },
527
+ vpc,
528
+ });
529
+ const userPool = new sst.aws.CognitoUserPool('FoundationIdentity', {
530
+ usernames: ['email'],
531
+ });
532
+ const eventBus = new sst.aws.Bus('FoundationEventBus');
533
+ const alertTopic = new sst.aws.SnsTopic('FoundationAlerts');
534
+ const dashboard = new aws.cloudwatch.Dashboard('FoundationDashboard', {
535
+ dashboardName: 'ebk-' + $app.stage + '-foundation',
536
+ dashboardBody: JSON.stringify({
537
+ widgets: [
538
+ {
539
+ type: 'text',
540
+ x: 0,
541
+ y: 0,
542
+ width: 24,
543
+ height: 3,
544
+ properties: {
545
+ markdown: '# Empire Builder Kit Foundation\\nStage: ' + $app.stage,
546
+ },
547
+ },
548
+ ],
549
+ }),
550
+ });
551
+
552
+ if (new Set(['development', 'staging', 'production']).has($app.stage)) {
553
+ new aws.cloudwatch.EventArchive('FoundationEventArchive', {
554
+ eventSourceArn: eventBus.arn,
555
+ retentionDays: 30,
556
+ });
557
+ }
558
+
559
+ publishFoundation({
560
+ alertTopicArn: alertTopic.arn,
561
+ capabilities: STANDARD_FOUNDATION_CAPABILITIES.join(','),
562
+ clusterId: cluster.id,
563
+ contractVersion: '1.0.0',
564
+ databaseClusterArn: database.clusterArn,
565
+ databaseClusterId: database.id,
566
+ databaseMasterSecretArn: database.secretArn,
567
+ eventBusArn: eventBus.arn,
568
+ eventBusName: eventBus.name,
569
+ hasPublicDomain: rootDomain ? 'true' : 'false',
570
+ identityUserPoolArn: userPool.arn,
571
+ identityUserPoolId: userPool.id,
572
+ rootDomain: rootDomain ?? 'none',
573
+ routerDistributionId: router.distributionID,
574
+ routerUrl: router.url,
575
+ schemaVersion: String(FOUNDATION_SSM_SCHEMA_VERSION),
576
+ vpcId: vpc.id,
577
+ });
578
+
579
+ return {
580
+ alertTopicArn: alertTopic.arn,
581
+ capabilities: STANDARD_FOUNDATION_CAPABILITIES,
582
+ clusterId: cluster.id,
583
+ contractVersion: '1.0.0',
584
+ dashboardName: dashboard.dashboardName,
585
+ databaseClusterId: database.id,
586
+ eventBusName: eventBus.name,
587
+ identityUserPoolId: userPool.id,
588
+ name: 'foundation',
589
+ rootDomain,
590
+ routerUrl: router.url,
591
+ vpcId: vpc.id,
592
+ };
593
+ }
594
+ `,
595
+ 'packages/foundation/infra/index.ts': `export { foundation as app } from './foundation.js';
596
+ `,
597
+ 'packages/foundation/package.json': json({
598
+ name: `@${name}/foundation`,
599
+ version: '0.0.0',
600
+ private: true,
601
+ }),
602
+ 'packages/foundation/project.json': json({
603
+ name: 'foundation',
604
+ $schema: '../../node_modules/nx/schemas/project-schema.json',
605
+ projectType: 'application',
606
+ sourceRoot: 'packages/foundation/infra',
607
+ tags: [
608
+ 'scope:foundation',
609
+ 'type:sst',
610
+ 'layer:platform',
611
+ 'visibility:internal',
612
+ ],
613
+ targets: {
614
+ deploy: {
615
+ executor: '@empire-builder-kit/nx:deploy',
616
+ dependsOn: ['verify'],
617
+ options: {
618
+ cwd: 'packages/foundation',
619
+ stage: 'development',
620
+ stageMode: 'foundation',
621
+ },
622
+ },
623
+ dev: {
624
+ executor: '@empire-builder-kit/nx:dev',
625
+ dependsOn: ['verify'],
626
+ options: {
627
+ cwd: 'packages/foundation',
628
+ stage: 'development',
629
+ stageMode: 'foundation',
630
+ },
631
+ },
632
+ lint: {
633
+ executor: 'nx:run-commands',
634
+ options: {
635
+ command: 'eslint .',
636
+ cwd: 'packages/foundation',
637
+ },
638
+ },
639
+ refresh: {
640
+ executor: '@empire-builder-kit/nx:refresh',
641
+ options: {
642
+ cwd: 'packages/foundation',
643
+ stage: 'development',
644
+ stageMode: 'foundation',
645
+ },
646
+ },
647
+ remove: {
648
+ executor: '@empire-builder-kit/nx:remove',
649
+ dependsOn: [{ target: 'remove-preflight', params: 'forward' }],
650
+ options: {
651
+ cwd: 'packages/foundation',
652
+ stage: 'development',
653
+ stageMode: 'foundation',
654
+ },
655
+ },
656
+ 'remove-preflight': {
657
+ executor: '@empire-builder-kit/nx:foundation-remove-preflight',
658
+ options: { stage: 'development' },
659
+ },
660
+ typecheck: {
661
+ executor: 'nx:run-commands',
662
+ options: {
663
+ command: 'tsc --noEmit -p tsconfig.json',
664
+ cwd: 'packages/foundation',
665
+ },
666
+ },
667
+ unlock: {
668
+ executor: '@empire-builder-kit/nx:unlock',
669
+ options: {
670
+ cwd: 'packages/foundation',
671
+ stage: 'development',
672
+ stageMode: 'foundation',
673
+ },
674
+ },
675
+ verify: {
676
+ executor: 'nx:run-commands',
677
+ dependsOn: ['lint', 'typecheck'],
678
+ options: {
679
+ command: 'node scripts/verify-foundation.mjs',
680
+ cwd: 'packages/foundation',
681
+ },
682
+ },
683
+ },
684
+ }),
685
+ 'packages/foundation/scripts/verify-foundation.mjs': `import { readFileSync } from 'node:fs';
686
+
687
+ const descriptor = JSON.parse(
688
+ readFileSync(new URL('../contracts/foundation.json', import.meta.url), 'utf8'),
689
+ );
690
+ const costs = JSON.parse(
691
+ readFileSync(new URL('../contracts/costs.json', import.meta.url), 'utf8'),
692
+ );
693
+ const expectedCapabilities = [
694
+ 'network',
695
+ 'routing',
696
+ 'container-compute',
697
+ 'postgres',
698
+ 'identity',
699
+ 'events',
700
+ 'observability',
701
+ 'configuration',
702
+ 'deployment',
703
+ ];
704
+
705
+ if (descriptor.contractVersion !== '1.0.0') {
706
+ throw new Error('Foundation contractVersion must be 1.0.0.');
707
+ }
708
+ if (JSON.stringify(descriptor.capabilities) !== JSON.stringify(expectedCapabilities)) {
709
+ throw new Error('Foundation capability descriptor does not match the v1 standard.');
710
+ }
711
+ if (
712
+ costs.schemaVersion !== 1 ||
713
+ !costs.stages?.development ||
714
+ !costs.stages?.staging ||
715
+ costs.stages?.production?.dataRetention !== 'retained-and-protected'
716
+ ) {
717
+ throw new Error('Foundation cost manifest does not match the v1 stage contract.');
718
+ }
719
+
720
+ process.stdout.write('Foundation source contract verified.\\n');
721
+ `,
722
+ 'packages/foundation/sst-env.d.ts': `// Bootstrap placeholder. SST overwrites this file after install, dev, or deploy.
723
+ export {};
724
+ `,
725
+ 'packages/foundation/sst.config.ts': `// SST/Pulumi ambient globals are declared in ebk-globals.d.ts.
726
+ export default $config({
727
+ app(input: { stage?: string } | undefined) {
728
+ const stage = input?.stage ?? 'development';
729
+ if (!['development', 'staging', 'production'].includes(stage)) {
730
+ throw new Error(
731
+ 'Foundation stage ' + stage + ' is not canonical. Use development, staging, or production.',
732
+ );
733
+ }
734
+ const production = stage === 'production';
735
+ return {
736
+ name: 'foundation',
737
+ removal: production ? 'retain' : 'remove',
738
+ protect: production,
739
+ home: 'aws',
740
+ providers: {
741
+ aws: {
742
+ region: process.env.AWS_REGION ?? 'us-east-1',
743
+ },
744
+ },
745
+ };
746
+ },
747
+ async run() {
748
+ const { app } = await import('./infra/index.js');
749
+ return app();
750
+ },
751
+ });
752
+ `,
753
+ 'packages/foundation/tsconfig.json': json({
754
+ extends: '../../tsconfig.base.json',
755
+ compilerOptions: { types: ['node'] },
756
+ include: [
757
+ 'infra/**/*.ts',
758
+ 'sst.config.ts',
759
+ 'sst-env.d.ts',
760
+ 'ebk-globals.d.ts',
761
+ ],
762
+ exclude: ['.sst', 'dist', 'node_modules'],
763
+ }),
764
+ 'pnpm-workspace.yaml': `packages:
765
+ - 'packages/*'
766
+ - 'packages/*/apps/*'
767
+ - 'packages/*/jobs/*'
768
+ - 'packages/*/libs/*'
769
+ - 'packages/*/services/*'
770
+ `,
771
+ 'tsconfig.base.json': json({
772
+ compilerOptions: {
773
+ composite: true,
774
+ declarationMap: true,
775
+ forceConsistentCasingInFileNames: true,
776
+ isolatedModules: true,
777
+ lib: ['es2022'],
778
+ module: 'nodenext',
779
+ moduleResolution: 'nodenext',
780
+ noEmitOnError: true,
781
+ noFallthroughCasesInSwitch: true,
782
+ noImplicitOverride: true,
783
+ noImplicitReturns: true,
784
+ noUnusedLocals: true,
785
+ skipLibCheck: true,
786
+ strict: true,
787
+ target: 'es2022',
788
+ },
789
+ }),
790
+ 'tsconfig.json': json({
791
+ extends: './tsconfig.base.json',
792
+ files: [],
793
+ references: [],
794
+ }),
795
+ 'tools/workspace-health.mjs': `import { createHash } from 'node:crypto';
796
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
797
+ import { join } from 'node:path';
798
+
799
+ const parseJson = (contents, label) => {
800
+ try {
801
+ return JSON.parse(contents);
802
+ } catch (error) {
803
+ throw new Error(label + ' is not valid JSON: ' + (error instanceof Error ? error.message : String(error)));
804
+ }
805
+ };
806
+ const readJson = (path) => parseJson(readFileSync(path, 'utf8'), path);
807
+ const sha256 = (contents) => createHash('sha256').update(contents, 'utf8').digest('hex');
808
+ const stableValue = (value) => {
809
+ if (Array.isArray(value)) return value.map(stableValue);
810
+ if (!value || typeof value !== 'object') return value;
811
+ return Object.fromEntries(Object.keys(value).sort().map((key) => [key, stableValue(value[key])]));
812
+ };
813
+ const pointerValue = (document, pointer) => {
814
+ let current = document;
815
+ for (const segment of pointer.slice(1).split('/').map((part) => part.replaceAll('~1', '/').replaceAll('~0', '~'))) {
816
+ if (!current || typeof current !== 'object' || !Object.hasOwn(current, segment)) {
817
+ return { exists: false };
818
+ }
819
+ current = current[segment];
820
+ }
821
+ return { exists: true, value: current };
822
+ };
823
+ const ownedSha256 = (contents, entry) => {
824
+ if (!entry.managedJsonPointers) return sha256(contents);
825
+ const document = parseJson(contents, 'Managed JSON file ' + entry.path);
826
+ const projection = [...entry.managedJsonPointers].sort().map((pointer) => {
827
+ const resolved = pointerValue(document, pointer);
828
+ return { exists: resolved.exists, pointer, ...(resolved.exists ? { value: resolved.value } : {}) };
829
+ });
830
+ return sha256(JSON.stringify(stableValue(projection)));
831
+ };
832
+ const validateOwnership = (root, record, label) => {
833
+ for (const entry of record.paths ?? []) {
834
+ if (entry.mode !== 'managed') continue;
835
+ const path = join(root, entry.path);
836
+ if (!existsSync(path)) throw new Error(label + ' managed path is missing: ' + entry.path);
837
+ if (ownedSha256(readFileSync(path, 'utf8'), entry) !== entry.sha256) {
838
+ throw new Error(label + ' managed path has drifted: ' + entry.path);
839
+ }
840
+ }
841
+ };
842
+
843
+ const workspace = readJson('.ebk/ownership.json');
844
+ const foundation = readJson('packages/foundation/.ebk/ownership.json');
845
+ if (
846
+ workspace.schemaVersion !== ${index_js_1.WORKSPACE_OWNERSHIP_SCHEMA_VERSION} ||
847
+ workspace.kind !== 'workspace' ||
848
+ foundation.schemaVersion !== ${index_js_1.FOUNDATION_OWNERSHIP_SCHEMA_VERSION} ||
849
+ foundation.kind !== 'foundation'
850
+ ) {
851
+ throw new Error('EBK workspace or Foundation ownership metadata is incompatible.');
852
+ }
853
+ validateOwnership('.', workspace, 'Workspace');
854
+ validateOwnership('packages/foundation', foundation, 'Foundation');
855
+
856
+ for (const name of readdirSync('packages')) {
857
+ if (name === 'foundation') continue;
858
+ const ownershipPath = join('packages', name, '.ebk/ownership.json');
859
+ if (!existsSync(ownershipPath)) continue;
860
+ const ownership = readJson(ownershipPath);
861
+ if (ownership.schemaVersion !== ${index_js_1.GENERATED_OWNERSHIP_SCHEMA_VERSION}) {
862
+ throw new Error('Product Slice ' + name + ' ownership metadata is incompatible.');
863
+ }
864
+ validateOwnership(join('packages', name), ownership, 'Product Slice ' + name);
865
+ }
866
+ process.stdout.write('Empire Builder Kit workspace source health is green.\\n');
867
+ `,
868
+ };
869
+ files[FOUNDATION_OWNERSHIP_PATH] = json(createFoundationOwnership(files, frameworkVersion));
870
+ files[WORKSPACE_OWNERSHIP_PATH] = json(createWorkspaceOwnership(files, frameworkVersion));
871
+ return files;
872
+ }
873
+ function renderWorkspaceRootFilePlan(options) {
874
+ const workspaceFiles = renderWorkspaceFiles(options);
875
+ const files = Object.fromEntries(Object.entries(workspaceFiles).filter(([path]) => !path.startsWith(FOUNDATION_ROOT)));
876
+ return {
877
+ files,
878
+ ownership: JSON.parse(workspaceFiles[WORKSPACE_OWNERSHIP_PATH]),
879
+ };
880
+ }
881
+ function renderFoundationFilePlan(options) {
882
+ const workspaceFiles = renderWorkspaceFiles(options);
883
+ const files = Object.fromEntries(Object.entries(workspaceFiles)
884
+ .filter(([path]) => path.startsWith(FOUNDATION_ROOT))
885
+ .map(([path, contents]) => [
886
+ path.slice(FOUNDATION_ROOT.length),
887
+ contents,
888
+ ]));
889
+ return {
890
+ files,
891
+ ownership: JSON.parse(workspaceFiles[FOUNDATION_OWNERSHIP_PATH]),
892
+ };
893
+ }
894
+ exports.WORKSPACE_PACKAGE_MANAGER_VERSION = TOOL_VERSIONS.pnpm;
895
+ //# sourceMappingURL=workspace-files.js.map