@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.
- package/CHANGELOG.md +18 -0
- package/README.md +133 -0
- package/dist/builder/build-registry.d.ts +20 -0
- package/dist/builder/build-registry.js +178 -0
- package/dist/builder/build-types.d.ts +10 -0
- package/dist/builder/build-types.js +32 -0
- package/dist/builder/load-domain-module-config.d.ts +12 -0
- package/dist/builder/load-domain-module-config.js +34 -0
- package/dist/builder/load-module.d.ts +26 -0
- package/dist/builder/load-module.js +114 -0
- package/dist/builder/zod-to-json.d.ts +10 -0
- package/dist/builder/zod-to-json.js +15 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +338 -0
- package/dist/commands/add-api.d.ts +32 -0
- package/dist/commands/add-api.js +106 -0
- package/dist/commands/add-component.d.ts +15 -0
- package/dist/commands/add-component.js +78 -0
- package/dist/commands/add-domain.d.ts +21 -0
- package/dist/commands/add-domain.js +71 -0
- package/dist/commands/add-flow.d.ts +14 -0
- package/dist/commands/add-flow.js +108 -0
- package/dist/commands/add-hook.d.ts +15 -0
- package/dist/commands/add-hook.js +65 -0
- package/dist/commands/add-migration.d.ts +26 -0
- package/dist/commands/add-migration.js +93 -0
- package/dist/commands/add-module.d.ts +7 -0
- package/dist/commands/add-module.js +261 -0
- package/dist/commands/add-page.d.ts +13 -0
- package/dist/commands/add-page.js +170 -0
- package/dist/commands/add-seed-page.d.ts +5 -0
- package/dist/commands/add-seed-page.js +251 -0
- package/dist/commands/add-subscriber.d.ts +30 -0
- package/dist/commands/add-subscriber.js +87 -0
- package/dist/commands/add-test.d.ts +22 -0
- package/dist/commands/add-test.js +285 -0
- package/dist/commands/audit.d.ts +39 -0
- package/dist/commands/audit.js +171 -0
- package/dist/commands/build-catalog.d.ts +68 -0
- package/dist/commands/build-catalog.js +105 -0
- package/dist/commands/build-flows.d.ts +26 -0
- package/dist/commands/build-flows.js +174 -0
- package/dist/commands/build.d.ts +17 -0
- package/dist/commands/build.js +39 -0
- package/dist/commands/check-hashes.d.ts +38 -0
- package/dist/commands/check-hashes.js +97 -0
- package/dist/commands/create-project.d.ts +5 -0
- package/dist/commands/create-project.js +272 -0
- package/dist/commands/dev.d.ts +15 -0
- package/dist/commands/dev.js +27 -0
- package/dist/commands/doctor.d.ts +48 -0
- package/dist/commands/doctor.js +1301 -0
- package/dist/commands/explain.d.ts +34 -0
- package/dist/commands/explain.js +167 -0
- package/dist/commands/power-tune.d.ts +1 -0
- package/dist/commands/power-tune.js +87 -0
- package/dist/commands/reseed-page.d.ts +6 -0
- package/dist/commands/reseed-page.js +297 -0
- package/dist/commands/show-dns.d.ts +15 -0
- package/dist/commands/show-dns.js +101 -0
- package/dist/commands/show.d.ts +46 -0
- package/dist/commands/show.js +135 -0
- package/dist/commands/test.d.ts +18 -0
- package/dist/commands/test.js +56 -0
- package/dist/commands/upgrade-backend.d.ts +9 -0
- package/dist/commands/upgrade-backend.js +46 -0
- package/dist/commands/upgrade-event.d.ts +22 -0
- package/dist/commands/upgrade-event.js +111 -0
- package/dist/commands/upgrade-frontend.d.ts +5 -0
- package/dist/commands/upgrade-frontend.js +99 -0
- package/dist/commands/upgrade.d.ts +7 -0
- package/dist/commands/upgrade.js +495 -0
- package/dist/commands/validate.d.ts +30 -0
- package/dist/commands/validate.js +148 -0
- package/dist/runtime-stubs/hydrate-local-ctx.d.ts +49 -0
- package/dist/runtime-stubs/hydrate-local-ctx.js +45 -0
- package/dist/runtime-stubs/load-secrets.d.ts +11 -0
- package/dist/runtime-stubs/load-secrets.js +37 -0
- package/dist/server/api-server.d.ts +21 -0
- package/dist/server/api-server.js +14 -0
- package/dist/server/mount-routes.d.ts +19 -0
- package/dist/server/mount-routes.js +42 -0
- package/dist/templates/api-skeleton.d.ts +18 -0
- package/dist/templates/api-skeleton.js +69 -0
- package/dist/templates/claude-md.d.ts +4 -0
- package/dist/templates/claude-md.js +7 -0
- package/dist/templates/dashboard-pages/index.d.ts +101 -0
- package/dist/templates/dashboard-pages/index.js +22 -0
- package/dist/templates/domain-config.d.ts +10 -0
- package/dist/templates/domain-config.js +19 -0
- package/dist/templates/events-skeleton.d.ts +9 -0
- package/dist/templates/events-skeleton.js +23 -0
- package/dist/templates/patterns/api/create-with-event.d.ts +5 -0
- package/dist/templates/patterns/api/create-with-event.js +69 -0
- package/dist/templates/patterns/api/idempotent-mutation.d.ts +5 -0
- package/dist/templates/patterns/api/idempotent-mutation.js +99 -0
- package/dist/templates/patterns/api/paginated-list.d.ts +5 -0
- package/dist/templates/patterns/api/paginated-list.js +71 -0
- package/dist/templates/patterns/api/simple-crud.d.ts +6 -0
- package/dist/templates/patterns/api/simple-crud.js +95 -0
- package/dist/templates/patterns/api/system-admin.d.ts +5 -0
- package/dist/templates/patterns/api/system-admin.js +98 -0
- package/dist/templates/patterns/api/webhook-receiver-style.d.ts +5 -0
- package/dist/templates/patterns/api/webhook-receiver-style.js +125 -0
- package/dist/templates/patterns/component/index.d.ts +25 -0
- package/dist/templates/patterns/component/index.js +814 -0
- package/dist/templates/patterns/flow/autonomous-remediation.d.ts +5 -0
- package/dist/templates/patterns/flow/autonomous-remediation.js +98 -0
- package/dist/templates/patterns/flow/fan-out-reaction.d.ts +5 -0
- package/dist/templates/patterns/flow/fan-out-reaction.js +53 -0
- package/dist/templates/patterns/flow/human-in-loop.d.ts +5 -0
- package/dist/templates/patterns/flow/human-in-loop.js +90 -0
- package/dist/templates/patterns/flow/multi-step-orchestration.d.ts +5 -0
- package/dist/templates/patterns/flow/multi-step-orchestration.js +67 -0
- package/dist/templates/patterns/hook/index.d.ts +7 -0
- package/dist/templates/patterns/hook/index.js +378 -0
- package/dist/templates/patterns/migration/column-add.d.ts +5 -0
- package/dist/templates/patterns/migration/column-add.js +52 -0
- package/dist/templates/patterns/migration/event-store.d.ts +6 -0
- package/dist/templates/patterns/migration/event-store.js +97 -0
- package/dist/templates/patterns/migration/index.d.ts +5 -0
- package/dist/templates/patterns/migration/index.js +52 -0
- package/dist/templates/patterns/migration/rls-policy.d.ts +5 -0
- package/dist/templates/patterns/migration/rls-policy.js +78 -0
- package/dist/templates/patterns/migration/tenant-scoped-table.d.ts +6 -0
- package/dist/templates/patterns/migration/tenant-scoped-table.js +81 -0
- package/dist/templates/patterns/page/dashboard.d.ts +4 -0
- package/dist/templates/patterns/page/dashboard.js +164 -0
- package/dist/templates/patterns/page/detail.d.ts +4 -0
- package/dist/templates/patterns/page/detail.js +162 -0
- package/dist/templates/patterns/page/form.d.ts +4 -0
- package/dist/templates/patterns/page/form.js +192 -0
- package/dist/templates/patterns/page/index.d.ts +8 -0
- package/dist/templates/patterns/page/index.js +14 -0
- package/dist/templates/patterns/page/list.d.ts +4 -0
- package/dist/templates/patterns/page/list.js +138 -0
- package/dist/templates/patterns/page/settings.d.ts +4 -0
- package/dist/templates/patterns/page/settings.js +318 -0
- package/dist/templates/patterns/page/wizard.d.ts +4 -0
- package/dist/templates/patterns/page/wizard.js +293 -0
- package/dist/templates/patterns/subscriber/audit-relay.d.ts +5 -0
- package/dist/templates/patterns/subscriber/audit-relay.js +51 -0
- package/dist/templates/patterns/subscriber/cascade-deletion.d.ts +5 -0
- package/dist/templates/patterns/subscriber/cascade-deletion.js +60 -0
- package/dist/templates/patterns/subscriber/single-step-projection.d.ts +5 -0
- package/dist/templates/patterns/subscriber/single-step-projection.js +36 -0
- package/dist/templates/readme.d.ts +4 -0
- package/dist/templates/readme.js +7 -0
- package/dist/templates/subscriber-skeleton.d.ts +11 -0
- package/dist/templates/subscriber-skeleton.js +23 -0
- package/dist/utils/checksum.d.ts +18 -0
- package/dist/utils/checksum.js +39 -0
- package/dist/utils/file-helpers.d.ts +35 -0
- package/dist/utils/file-helpers.js +59 -0
- package/dist/utils/header-inject.d.ts +15 -0
- package/dist/utils/header-inject.js +93 -0
- package/dist/utils/install-file.d.ts +17 -0
- package/dist/utils/install-file.js +78 -0
- package/dist/utils/logger.d.ts +12 -0
- package/dist/utils/logger.js +24 -0
- package/dist/utils/manifest.d.ts +28 -0
- package/dist/utils/manifest.js +93 -0
- package/dist/utils/s3-fetch.d.ts +25 -0
- package/dist/utils/s3-fetch.js +52 -0
- package/dist/utils/scaffold-config.d.ts +95 -0
- package/dist/utils/scaffold-config.js +53 -0
- package/dist/utils/token-substitution.d.ts +35 -0
- package/dist/utils/token-substitution.js +52 -0
- package/dist/utils/zod-extractor.d.ts +10 -0
- package/dist/utils/zod-extractor.js +28 -0
- package/package.json +43 -0
- package/src/__tests__/build-registry.test.ts +115 -0
- package/src/__tests__/build-types.test.ts +100 -0
- package/src/__tests__/builder/walkDomainDir.test.ts +140 -0
- package/src/__tests__/commands/add-domain.test.ts +174 -0
- package/src/__tests__/commands/add-flow.test.ts +69 -0
- package/src/__tests__/commands/add-module.test.ts +207 -0
- package/src/__tests__/commands/build-flows.test.ts +154 -0
- package/src/__tests__/commands/check-hashes.test.ts +142 -0
- package/src/__tests__/commands/create-project.test.ts +712 -0
- package/src/__tests__/commands/upgrade.test.ts +762 -0
- package/src/__tests__/doctor.test.ts +291 -0
- package/src/__tests__/hydrate-local-ctx.test.ts +71 -0
- package/src/__tests__/load-domain-module-config.test.ts +10 -0
- package/src/__tests__/mount-routes.test.ts +100 -0
- package/src/__tests__/scaffold-src/part-a-layout.test.ts +46 -0
- package/src/__tests__/scripts/package-scaffold.test.ts +45 -0
- package/src/__tests__/utils/checksum.test.ts +90 -0
- package/src/__tests__/utils/header-inject.test.ts +89 -0
- package/src/__tests__/utils/install-file.test.ts +207 -0
- package/src/__tests__/utils/manifest.test.ts +229 -0
- package/src/__tests__/utils/token-substitution.test.ts +134 -0
- package/src/__tests__/validate.test.ts +18 -0
- package/src/builder/build-registry.ts +237 -0
- package/src/builder/build-types.ts +36 -0
- package/src/builder/load-domain-module-config.ts +43 -0
- package/src/builder/load-module.ts +133 -0
- package/src/builder/zod-to-json.ts +16 -0
- package/src/cli.ts +387 -0
- package/src/commands/add-api.ts +145 -0
- package/src/commands/add-component.ts +116 -0
- package/src/commands/add-domain.ts +100 -0
- package/src/commands/add-flow.ts +136 -0
- package/src/commands/add-hook.ts +100 -0
- package/src/commands/add-migration.ts +129 -0
- package/src/commands/add-module.ts +347 -0
- package/src/commands/add-page.ts +242 -0
- package/src/commands/add-seed-page.ts +338 -0
- package/src/commands/add-subscriber.ts +123 -0
- package/src/commands/add-test.ts +328 -0
- package/src/commands/audit.ts +217 -0
- package/src/commands/build-catalog.ts +181 -0
- package/src/commands/build-flows.ts +198 -0
- package/src/commands/build.ts +59 -0
- package/src/commands/check-hashes.ts +150 -0
- package/src/commands/create-project.ts +367 -0
- package/src/commands/dev.ts +47 -0
- package/src/commands/doctor.ts +1362 -0
- package/src/commands/explain.ts +209 -0
- package/src/commands/power-tune.ts +112 -0
- package/src/commands/reseed-page.ts +410 -0
- package/src/commands/show-dns.ts +139 -0
- package/src/commands/show.ts +179 -0
- package/src/commands/test.ts +91 -0
- package/src/commands/upgrade-backend.ts +72 -0
- package/src/commands/upgrade-event.ts +175 -0
- package/src/commands/upgrade-frontend.ts +135 -0
- package/src/commands/upgrade.ts +668 -0
- package/src/commands/validate.ts +208 -0
- package/src/runtime-stubs/hydrate-local-ctx.ts +102 -0
- package/src/runtime-stubs/load-secrets.ts +40 -0
- package/src/server/api-server.ts +34 -0
- package/src/server/mount-routes.ts +68 -0
- package/src/templates/api-skeleton.ts +72 -0
- package/src/templates/claude-md.ts +7 -0
- package/src/templates/dashboard-pages/account/api-keys.tsx +107 -0
- package/src/templates/dashboard-pages/account/audit-log.tsx +60 -0
- package/src/templates/dashboard-pages/account/index.ts +5 -0
- package/src/templates/dashboard-pages/account/members.tsx +107 -0
- package/src/templates/dashboard-pages/account/profile.tsx +53 -0
- package/src/templates/dashboard-pages/account/workspace-settings.tsx +64 -0
- package/src/templates/dashboard-pages/auth/accept-invitation.tsx +62 -0
- package/src/templates/dashboard-pages/auth/choose-org.tsx +66 -0
- package/src/templates/dashboard-pages/auth/forgot-password.tsx +69 -0
- package/src/templates/dashboard-pages/auth/index.ts +10 -0
- package/src/templates/dashboard-pages/auth/login.tsx +75 -0
- package/src/templates/dashboard-pages/auth/mfa-setup.tsx +56 -0
- package/src/templates/dashboard-pages/auth/mfa-verify.tsx +49 -0
- package/src/templates/dashboard-pages/auth/reset-password.tsx +64 -0
- package/src/templates/dashboard-pages/auth/sign-out.tsx +20 -0
- package/src/templates/dashboard-pages/auth/signup.tsx +71 -0
- package/src/templates/dashboard-pages/auth/verify-email.tsx +53 -0
- package/src/templates/dashboard-pages/index.ts +22 -0
- package/src/templates/dashboard-pages/shell/dashboard-home.tsx +41 -0
- package/src/templates/dashboard-pages/shell/forbidden.tsx +46 -0
- package/src/templates/dashboard-pages/shell/index.ts +5 -0
- package/src/templates/dashboard-pages/shell/maintenance.tsx +27 -0
- package/src/templates/dashboard-pages/shell/not-found.tsx +34 -0
- package/src/templates/dashboard-pages/shell/server-error.tsx +44 -0
- package/src/templates/domain-config.ts +20 -0
- package/src/templates/events-skeleton.ts +24 -0
- package/src/templates/patterns/api/create-with-event.ts +72 -0
- package/src/templates/patterns/api/idempotent-mutation.ts +102 -0
- package/src/templates/patterns/api/paginated-list.ts +74 -0
- package/src/templates/patterns/api/simple-crud.ts +100 -0
- package/src/templates/patterns/api/system-admin.ts +101 -0
- package/src/templates/patterns/api/webhook-receiver-style.ts +128 -0
- package/src/templates/patterns/component/index.ts +833 -0
- package/src/templates/patterns/flow/autonomous-remediation.ts +101 -0
- package/src/templates/patterns/flow/fan-out-reaction.ts +56 -0
- package/src/templates/patterns/flow/human-in-loop.ts +93 -0
- package/src/templates/patterns/flow/multi-step-orchestration.ts +70 -0
- package/src/templates/patterns/hook/index.ts +382 -0
- package/src/templates/patterns/migration/column-add.ts +54 -0
- package/src/templates/patterns/migration/event-store.ts +99 -0
- package/src/templates/patterns/migration/index.ts +54 -0
- package/src/templates/patterns/migration/rls-policy.ts +80 -0
- package/src/templates/patterns/migration/tenant-scoped-table.ts +83 -0
- package/src/templates/patterns/page/dashboard.ts +164 -0
- package/src/templates/patterns/page/detail.ts +162 -0
- package/src/templates/patterns/page/form.ts +192 -0
- package/src/templates/patterns/page/index.ts +17 -0
- package/src/templates/patterns/page/list.ts +138 -0
- package/src/templates/patterns/page/settings.ts +318 -0
- package/src/templates/patterns/page/wizard.ts +293 -0
- package/src/templates/patterns/subscriber/audit-relay.ts +54 -0
- package/src/templates/patterns/subscriber/cascade-deletion.ts +63 -0
- package/src/templates/patterns/subscriber/single-step-projection.ts +39 -0
- package/src/templates/readme.ts +7 -0
- package/src/templates/subscriber-skeleton.ts +28 -0
- package/src/utils/checksum.ts +43 -0
- package/src/utils/file-helpers.ts +91 -0
- package/src/utils/header-inject.ts +126 -0
- package/src/utils/install-file.ts +99 -0
- package/src/utils/logger.ts +26 -0
- package/src/utils/manifest.ts +137 -0
- package/src/utils/s3-fetch.ts +91 -0
- package/src/utils/scaffold-config.ts +172 -0
- package/src/utils/token-substitution.ts +80 -0
- package/src/utils/zod-extractor.ts +31 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inject the @mc-scaffold ownership header into file content.
|
|
3
|
+
* The header format depends on the file type.
|
|
4
|
+
* For JSON files in the skip list, returns content unchanged.
|
|
5
|
+
*
|
|
6
|
+
* @param content The file content (post-token-substitution)
|
|
7
|
+
* @param filename The destination filename (used to determine type)
|
|
8
|
+
* @param moduleId The scaffold module id (e.g. 'backend-lambda')
|
|
9
|
+
* @param version The scaffold module version (e.g. '1.0.0')
|
|
10
|
+
*/
|
|
11
|
+
export declare function injectHeader(content: string, filename: string, moduleId: string, version: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Strip the @mc-scaffold header from file content (used during upgrade conflict detection).
|
|
14
|
+
*/
|
|
15
|
+
export declare function stripHeader(content: string, filename: string): string;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
// Files where adding a key/comment would break schema validation — tracked via manifest only
|
|
3
|
+
const JSON_SKIP_LIST = new Set([
|
|
4
|
+
'package.json',
|
|
5
|
+
'tsconfig.json',
|
|
6
|
+
'tsconfig.base.json',
|
|
7
|
+
]);
|
|
8
|
+
/**
|
|
9
|
+
* Inject the @mc-scaffold ownership header into file content.
|
|
10
|
+
* The header format depends on the file type.
|
|
11
|
+
* For JSON files in the skip list, returns content unchanged.
|
|
12
|
+
*
|
|
13
|
+
* @param content The file content (post-token-substitution)
|
|
14
|
+
* @param filename The destination filename (used to determine type)
|
|
15
|
+
* @param moduleId The scaffold module id (e.g. 'backend-lambda')
|
|
16
|
+
* @param version The scaffold module version (e.g. '1.0.0')
|
|
17
|
+
*/
|
|
18
|
+
export function injectHeader(content, filename, moduleId, version) {
|
|
19
|
+
const basename = path.basename(filename);
|
|
20
|
+
const ext = path.extname(filename).toLowerCase();
|
|
21
|
+
if (basename.endsWith('.fixture.json')) {
|
|
22
|
+
return content;
|
|
23
|
+
}
|
|
24
|
+
if (ext === '.json') {
|
|
25
|
+
if (JSON_SKIP_LIST.has(basename)) {
|
|
26
|
+
return content;
|
|
27
|
+
}
|
|
28
|
+
return injectJsonHeader(content, moduleId, version);
|
|
29
|
+
}
|
|
30
|
+
if (ext === '.ts' || ext === '.tsx' || ext === '.js' || ext === '.jsx' || ext === '.mjs') {
|
|
31
|
+
return injectLineCommentHeader(content, '//', moduleId, version);
|
|
32
|
+
}
|
|
33
|
+
if (ext === '.yml' || ext === '.yaml') {
|
|
34
|
+
return injectLineCommentHeader(content, '#', moduleId, version);
|
|
35
|
+
}
|
|
36
|
+
if (ext === '.css' || ext === '.scss') {
|
|
37
|
+
return injectBlockCommentHeader(content, '/*', '*/', moduleId, version);
|
|
38
|
+
}
|
|
39
|
+
if (ext === '.html') {
|
|
40
|
+
return injectBlockCommentHeader(content, '<!--', '-->', moduleId, version);
|
|
41
|
+
}
|
|
42
|
+
if (ext === '.md') {
|
|
43
|
+
return content;
|
|
44
|
+
}
|
|
45
|
+
return content;
|
|
46
|
+
}
|
|
47
|
+
function injectLineCommentHeader(content, commentChar, moduleId, version) {
|
|
48
|
+
const header = [
|
|
49
|
+
`${commentChar} @mc-scaffold: ${moduleId}@${version}`,
|
|
50
|
+
`${commentChar} This file is managed by TIB scaffold. Manual edits will be flagged during \`tib upgrade\`.`,
|
|
51
|
+
`${commentChar} To opt out of upgrade management for this file, remove these header lines.`,
|
|
52
|
+
'',
|
|
53
|
+
].join('\n');
|
|
54
|
+
// Preserve shebang line if present
|
|
55
|
+
if (content.startsWith('#!')) {
|
|
56
|
+
const newlineIdx = content.indexOf('\n');
|
|
57
|
+
if (newlineIdx === -1)
|
|
58
|
+
return content + '\n' + header;
|
|
59
|
+
return content.slice(0, newlineIdx + 1) + header + content.slice(newlineIdx + 1);
|
|
60
|
+
}
|
|
61
|
+
return header + content;
|
|
62
|
+
}
|
|
63
|
+
function injectBlockCommentHeader(content, open, close, moduleId, version) {
|
|
64
|
+
const header = [
|
|
65
|
+
`${open} @mc-scaffold: ${moduleId}@${version} ${close}`,
|
|
66
|
+
`${open} This file is managed by TIB scaffold. Manual edits will be flagged during \`tib upgrade\`. ${close}`,
|
|
67
|
+
'',
|
|
68
|
+
].join('\n');
|
|
69
|
+
return header + content;
|
|
70
|
+
}
|
|
71
|
+
function injectJsonHeader(content, moduleId, version) {
|
|
72
|
+
try {
|
|
73
|
+
const parsed = JSON.parse(content);
|
|
74
|
+
const withHeader = { _tibScaffold: `${moduleId}@${version}`, ...parsed };
|
|
75
|
+
return JSON.stringify(withHeader, null, 2);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return content;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Strip the @mc-scaffold header from file content (used during upgrade conflict detection).
|
|
83
|
+
*/
|
|
84
|
+
export function stripHeader(content, filename) {
|
|
85
|
+
const ext = path.extname(filename).toLowerCase();
|
|
86
|
+
if (ext === '.ts' || ext === '.tsx' || ext === '.js' || ext === '.jsx' || ext === '.mjs') {
|
|
87
|
+
return content.replace(/^\/\/ @mc-scaffold:.*\n\/\/ This file is.*\n\/\/ To opt out.*\n\n?/, '');
|
|
88
|
+
}
|
|
89
|
+
if (ext === '.yml' || ext === '.yaml') {
|
|
90
|
+
return content.replace(/^# @mc-scaffold:.*\n# This file is.*\n\n?/, '');
|
|
91
|
+
}
|
|
92
|
+
return content;
|
|
93
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ManifestFileEntry } from './manifest.js';
|
|
2
|
+
export type FilePolicy = 'owned' | 'tracked' | 'seed';
|
|
3
|
+
export interface InstallResult {
|
|
4
|
+
status: 'added' | 'updated' | 'conflict' | 'skipped' | 'unchanged';
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Install a scaffold file according to its policy.
|
|
8
|
+
*
|
|
9
|
+
* - owned: always overwrite. Return 'added' if new, 'updated' if existed.
|
|
10
|
+
* - tracked: check sha256. If same → 'unchanged'. If different, check disk for drift.
|
|
11
|
+
* If drifted → write .tib-upgrade file → 'conflict'. If not drifted → overwrite → 'updated'.
|
|
12
|
+
* If no currentEntry → write new → 'added'.
|
|
13
|
+
* - seed: if file exists → 'skipped'. Else write → 'added'.
|
|
14
|
+
*/
|
|
15
|
+
export declare function installScaffoldFile(absPath: string, content: string, policy: FilePolicy, currentEntry: ManifestFileEntry | undefined, opts?: {
|
|
16
|
+
dryRun?: boolean;
|
|
17
|
+
}): Promise<InstallResult>;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { writeFile, mkdir, access } from 'node:fs/promises';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
import { computeChecksumString, computeChecksumFile } from './checksum.js';
|
|
4
|
+
/**
|
|
5
|
+
* Install a scaffold file according to its policy.
|
|
6
|
+
*
|
|
7
|
+
* - owned: always overwrite. Return 'added' if new, 'updated' if existed.
|
|
8
|
+
* - tracked: check sha256. If same → 'unchanged'. If different, check disk for drift.
|
|
9
|
+
* If drifted → write .tib-upgrade file → 'conflict'. If not drifted → overwrite → 'updated'.
|
|
10
|
+
* If no currentEntry → write new → 'added'.
|
|
11
|
+
* - seed: if file exists → 'skipped'. Else write → 'added'.
|
|
12
|
+
*/
|
|
13
|
+
export async function installScaffoldFile(absPath, content, policy, currentEntry, opts) {
|
|
14
|
+
const dryRun = opts?.dryRun ?? false;
|
|
15
|
+
const newChecksum = computeChecksumString(content);
|
|
16
|
+
// Helper to check if file exists
|
|
17
|
+
const fileExists = async (path) => {
|
|
18
|
+
try {
|
|
19
|
+
await access(path);
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
if (policy === 'owned') {
|
|
27
|
+
// owned: always overwrite, no sha256 check
|
|
28
|
+
const exists = await fileExists(absPath);
|
|
29
|
+
if (!dryRun) {
|
|
30
|
+
await mkdir(dirname(absPath), { recursive: true });
|
|
31
|
+
await writeFile(absPath, content, 'utf-8');
|
|
32
|
+
}
|
|
33
|
+
return { status: exists ? 'updated' : 'added' };
|
|
34
|
+
}
|
|
35
|
+
if (policy === 'seed') {
|
|
36
|
+
// seed: never overwrite existing file
|
|
37
|
+
const exists = await fileExists(absPath);
|
|
38
|
+
if (exists) {
|
|
39
|
+
return { status: 'skipped' };
|
|
40
|
+
}
|
|
41
|
+
if (!dryRun) {
|
|
42
|
+
await mkdir(dirname(absPath), { recursive: true });
|
|
43
|
+
await writeFile(absPath, content, 'utf-8');
|
|
44
|
+
}
|
|
45
|
+
return { status: 'added' };
|
|
46
|
+
}
|
|
47
|
+
// policy === 'tracked'
|
|
48
|
+
if (!currentEntry) {
|
|
49
|
+
// New file: write it
|
|
50
|
+
if (!dryRun) {
|
|
51
|
+
await mkdir(dirname(absPath), { recursive: true });
|
|
52
|
+
await writeFile(absPath, content, 'utf-8');
|
|
53
|
+
}
|
|
54
|
+
return { status: 'added' };
|
|
55
|
+
}
|
|
56
|
+
// File exists in manifest: check if content changed
|
|
57
|
+
if (newChecksum === currentEntry.sha256) {
|
|
58
|
+
// Content unchanged
|
|
59
|
+
return { status: 'unchanged' };
|
|
60
|
+
}
|
|
61
|
+
// Content changed: check for drift
|
|
62
|
+
const diskChecksum = await computeChecksumFile(absPath);
|
|
63
|
+
const drifted = diskChecksum !== null && diskChecksum !== currentEntry.sha256;
|
|
64
|
+
if (drifted) {
|
|
65
|
+
// Drift detected: write .tib-upgrade file, don't touch original
|
|
66
|
+
if (!dryRun) {
|
|
67
|
+
await mkdir(dirname(absPath), { recursive: true });
|
|
68
|
+
await writeFile(`${absPath}.tib-upgrade`, content, 'utf-8');
|
|
69
|
+
}
|
|
70
|
+
return { status: 'conflict' };
|
|
71
|
+
}
|
|
72
|
+
// No drift: safe to overwrite
|
|
73
|
+
if (!dryRun) {
|
|
74
|
+
await mkdir(dirname(absPath), { recursive: true });
|
|
75
|
+
await writeFile(absPath, content, 'utf-8');
|
|
76
|
+
}
|
|
77
|
+
return { status: 'updated' };
|
|
78
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type Logger } from 'pino';
|
|
2
|
+
/**
|
|
3
|
+
* Factory that creates a pino logger with pino-pretty transport for CLI output.
|
|
4
|
+
* @param name - Optional logger name shown in pino-pretty output. Defaults to 'tib-domain'.
|
|
5
|
+
* @returns A pino Logger instance configured for human-readable terminal output.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createCliLogger(name?: string): Logger;
|
|
8
|
+
/**
|
|
9
|
+
* Default singleton CLI logger used by all domain-cli commands.
|
|
10
|
+
* Pretty-printed with timestamps to stdout.
|
|
11
|
+
*/
|
|
12
|
+
export declare const cliLogger: Logger;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import pino from 'pino';
|
|
2
|
+
/**
|
|
3
|
+
* Factory that creates a pino logger with pino-pretty transport for CLI output.
|
|
4
|
+
* @param name - Optional logger name shown in pino-pretty output. Defaults to 'tib-domain'.
|
|
5
|
+
* @returns A pino Logger instance configured for human-readable terminal output.
|
|
6
|
+
*/
|
|
7
|
+
export function createCliLogger(name = 'tib-domain') {
|
|
8
|
+
return pino({
|
|
9
|
+
name,
|
|
10
|
+
transport: {
|
|
11
|
+
target: 'pino-pretty',
|
|
12
|
+
options: {
|
|
13
|
+
colorize: true,
|
|
14
|
+
translateTime: 'SYS:HH:MM:ss',
|
|
15
|
+
ignore: 'pid,hostname',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Default singleton CLI logger used by all domain-cli commands.
|
|
22
|
+
* Pretty-printed with timestamps to stdout.
|
|
23
|
+
*/
|
|
24
|
+
export const cliLogger = createCliLogger();
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type FilePolicy = 'owned' | 'tracked' | 'seed';
|
|
2
|
+
export interface ManifestFileEntry {
|
|
3
|
+
path: string;
|
|
4
|
+
module: string;
|
|
5
|
+
moduleVersion: string;
|
|
6
|
+
sha256: string;
|
|
7
|
+
wasTemplate: boolean;
|
|
8
|
+
installedAt: string;
|
|
9
|
+
policy: FilePolicy;
|
|
10
|
+
}
|
|
11
|
+
export interface TibManifest {
|
|
12
|
+
$schema: string;
|
|
13
|
+
scaffoldVersion: string;
|
|
14
|
+
createdAt: string;
|
|
15
|
+
updatedAt: string;
|
|
16
|
+
projectName: string;
|
|
17
|
+
awsRegion: string;
|
|
18
|
+
enabledModules: string[];
|
|
19
|
+
files: ManifestFileEntry[];
|
|
20
|
+
}
|
|
21
|
+
export declare function inferPolicyFromPath(filePath: string): FilePolicy;
|
|
22
|
+
export declare function readManifest(projectRoot: string): Promise<TibManifest | null>;
|
|
23
|
+
export declare function writeManifest(projectRoot: string, manifest: TibManifest): Promise<void>;
|
|
24
|
+
export declare function createManifest(scaffoldVersion: string, projectName: string, awsRegion: string, enabledModules: string[]): TibManifest;
|
|
25
|
+
export declare function upsertManifestFile(manifest: TibManifest, entry: ManifestFileEntry): void;
|
|
26
|
+
export declare function removeManifestFile(manifest: TibManifest, filePath: string): void;
|
|
27
|
+
export declare function getManifestFile(manifest: TibManifest, filePath: string): ManifestFileEntry | undefined;
|
|
28
|
+
export declare function isScaffoldOwned(manifest: TibManifest, filePath: string): boolean;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { cliLogger } from './logger.js';
|
|
4
|
+
const MANIFEST_PATH = '.mc/manifest.json';
|
|
5
|
+
const SCHEMA_URL = 'https://mc-scaffold.s3.amazonaws.com/schema/manifest.v3.json';
|
|
6
|
+
export function inferPolicyFromPath(filePath) {
|
|
7
|
+
// owned: infra/, .mc/infra/, mc-deploy.yml, .github/workflows/
|
|
8
|
+
if (filePath.match(/^infra\//) ||
|
|
9
|
+
filePath.match(/^\.tib\/infra\//) ||
|
|
10
|
+
filePath === 'mc-deploy.yml' ||
|
|
11
|
+
filePath.match(/^\.github\/workflows\//)) {
|
|
12
|
+
return 'owned';
|
|
13
|
+
}
|
|
14
|
+
// seed: specific frontend paths
|
|
15
|
+
const seedPatterns = [
|
|
16
|
+
/^frontend\/src\/pages\/auth\//,
|
|
17
|
+
/^frontend\/src\/pages\/account\//,
|
|
18
|
+
/^frontend\/src\/pages\/shell\//,
|
|
19
|
+
/^frontend\/src\/pages\/_errors\//,
|
|
20
|
+
/^frontend\/src\/layouts\//,
|
|
21
|
+
/^frontend\/src\/styles\/brand\.css$/,
|
|
22
|
+
];
|
|
23
|
+
if (seedPatterns.some((pattern) => pattern.test(filePath))) {
|
|
24
|
+
return 'seed';
|
|
25
|
+
}
|
|
26
|
+
// tracked: everything else
|
|
27
|
+
return 'tracked';
|
|
28
|
+
}
|
|
29
|
+
export async function readManifest(projectRoot) {
|
|
30
|
+
const manifestPath = path.join(projectRoot, MANIFEST_PATH);
|
|
31
|
+
try {
|
|
32
|
+
const content = await readFile(manifestPath, 'utf-8');
|
|
33
|
+
const manifest = JSON.parse(content);
|
|
34
|
+
// v1→v2 migration: infer policy for entries missing it
|
|
35
|
+
let needsMigration = false;
|
|
36
|
+
for (const entry of manifest.files) {
|
|
37
|
+
if (!entry['policy']) {
|
|
38
|
+
entry['policy'] = inferPolicyFromPath(entry['path']);
|
|
39
|
+
needsMigration = true;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (needsMigration) {
|
|
43
|
+
cliLogger.info('manifest: migrating v1→v2 with inferred policies');
|
|
44
|
+
await writeManifest(projectRoot, manifest);
|
|
45
|
+
}
|
|
46
|
+
return manifest;
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
if (err.code === 'ENOENT')
|
|
50
|
+
return null;
|
|
51
|
+
throw err;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export async function writeManifest(projectRoot, manifest) {
|
|
55
|
+
const manifestPath = path.join(projectRoot, MANIFEST_PATH);
|
|
56
|
+
await mkdir(path.dirname(manifestPath), { recursive: true });
|
|
57
|
+
manifest.$schema = SCHEMA_URL;
|
|
58
|
+
manifest.updatedAt = new Date().toISOString();
|
|
59
|
+
await writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
|
|
60
|
+
}
|
|
61
|
+
export function createManifest(scaffoldVersion, projectName, awsRegion, enabledModules) {
|
|
62
|
+
const now = new Date().toISOString();
|
|
63
|
+
return {
|
|
64
|
+
$schema: SCHEMA_URL,
|
|
65
|
+
scaffoldVersion,
|
|
66
|
+
createdAt: now,
|
|
67
|
+
updatedAt: now,
|
|
68
|
+
projectName,
|
|
69
|
+
awsRegion,
|
|
70
|
+
enabledModules,
|
|
71
|
+
files: [],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function upsertManifestFile(manifest, entry) {
|
|
75
|
+
const idx = manifest.files.findIndex((f) => f.path === entry.path);
|
|
76
|
+
if (idx === -1) {
|
|
77
|
+
manifest.files.push(entry);
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
manifest.files[idx] = entry;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
export function removeManifestFile(manifest, filePath) {
|
|
84
|
+
manifest.files = manifest.files.filter((f) => f.path !== filePath);
|
|
85
|
+
}
|
|
86
|
+
export function getManifestFile(manifest, filePath) {
|
|
87
|
+
return manifest.files.find((f) => f.path === filePath);
|
|
88
|
+
}
|
|
89
|
+
export function isScaffoldOwned(manifest, filePath) {
|
|
90
|
+
const entry = manifest.files.find((f) => f.path === filePath);
|
|
91
|
+
// Scaffold-managed means in manifest AND policy is not 'seed'
|
|
92
|
+
return entry != null && entry.policy !== 'seed';
|
|
93
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface ModulesJson {
|
|
2
|
+
scaffoldVersion: string;
|
|
3
|
+
releasedAt: string;
|
|
4
|
+
modules: S3ModuleEntry[];
|
|
5
|
+
compatibility: {
|
|
6
|
+
cliMinVersion: string;
|
|
7
|
+
nodeMinVersion: string;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export interface S3ModuleEntry {
|
|
11
|
+
id: string;
|
|
12
|
+
version: string;
|
|
13
|
+
description: string;
|
|
14
|
+
dependencies: string[];
|
|
15
|
+
tarball: string;
|
|
16
|
+
sha256: string;
|
|
17
|
+
}
|
|
18
|
+
export interface VersionsJson {
|
|
19
|
+
latest: string;
|
|
20
|
+
versions: string[];
|
|
21
|
+
}
|
|
22
|
+
export declare function fetchVersionsJson(bucket: string): Promise<VersionsJson>;
|
|
23
|
+
export declare function fetchModulesJson(bucket: string, version: string): Promise<ModulesJson>;
|
|
24
|
+
export declare function fetchModuleTarball(bucket: string, version: string, tarball: string, expectedSha256: string): Promise<Buffer>;
|
|
25
|
+
export declare function fetchScaffoldFile(bucket: string, version: string, path: string): Promise<string | null>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
|
2
|
+
import { createHash } from 'crypto';
|
|
3
|
+
export async function fetchVersionsJson(bucket) {
|
|
4
|
+
const client = new S3Client({});
|
|
5
|
+
const cmd = new GetObjectCommand({ Bucket: bucket, Key: 'versions.json' });
|
|
6
|
+
const res = await client.send(cmd);
|
|
7
|
+
const body = await streamToBuffer(res.Body);
|
|
8
|
+
return JSON.parse(body.toString('utf-8'));
|
|
9
|
+
}
|
|
10
|
+
export async function fetchModulesJson(bucket, version) {
|
|
11
|
+
const client = new S3Client({});
|
|
12
|
+
const cmd = new GetObjectCommand({
|
|
13
|
+
Bucket: bucket,
|
|
14
|
+
Key: `versions/${version}/modules.json`,
|
|
15
|
+
});
|
|
16
|
+
const res = await client.send(cmd);
|
|
17
|
+
const body = await streamToBuffer(res.Body);
|
|
18
|
+
return JSON.parse(body.toString('utf-8'));
|
|
19
|
+
}
|
|
20
|
+
export async function fetchModuleTarball(bucket, version, tarball, expectedSha256) {
|
|
21
|
+
const client = new S3Client({});
|
|
22
|
+
const key = `versions/${version}/${tarball}`;
|
|
23
|
+
const cmd = new GetObjectCommand({ Bucket: bucket, Key: key });
|
|
24
|
+
const res = await client.send(cmd);
|
|
25
|
+
const buf = await streamToBuffer(res.Body);
|
|
26
|
+
const actual = createHash('sha256').update(buf).digest('hex');
|
|
27
|
+
if (actual !== expectedSha256) {
|
|
28
|
+
throw new Error(`Checksum mismatch for ${tarball}: expected ${expectedSha256}, got ${actual}`);
|
|
29
|
+
}
|
|
30
|
+
return buf;
|
|
31
|
+
}
|
|
32
|
+
export async function fetchScaffoldFile(bucket, version, path) {
|
|
33
|
+
const client = new S3Client({});
|
|
34
|
+
const cmd = new GetObjectCommand({ Bucket: bucket, Key: `versions/${version}/${path}` });
|
|
35
|
+
try {
|
|
36
|
+
const res = await client.send(cmd);
|
|
37
|
+
const body = await streamToBuffer(res.Body);
|
|
38
|
+
return body.toString('utf-8');
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
if (err.name === 'NoSuchKey')
|
|
42
|
+
return null;
|
|
43
|
+
throw err;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function streamToBuffer(stream) {
|
|
47
|
+
const chunks = [];
|
|
48
|
+
for await (const chunk of stream) {
|
|
49
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
50
|
+
}
|
|
51
|
+
return Buffer.concat(chunks);
|
|
52
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
export type ModuleId = 'core' | 'backend-lambda' | 'db-aurora' | 'auth-cognito' | 'frontend' | 'flows' | 'data-management';
|
|
2
|
+
export type LogRetentionDays = 30 | 90 | 365 | 2557;
|
|
3
|
+
export interface SecurityOptions {
|
|
4
|
+
/** Enable CMK (KMS customer-managed keys) for DynamoDB, S3, SQS. ~$10–20/month */
|
|
5
|
+
cmk: boolean;
|
|
6
|
+
/** Enable GuardDuty threat detection with S3 protection. ~$15–25/month */
|
|
7
|
+
guardDuty: boolean;
|
|
8
|
+
/** Enable WAF WebACL on all domain API Gateways. ~$25–60/month */
|
|
9
|
+
waf: boolean;
|
|
10
|
+
/** Enable VPC interface endpoints for SM, SQS, EventBridge, Lambda. ~$85–100/month */
|
|
11
|
+
vpcInterfaceEndpoints: boolean;
|
|
12
|
+
/** Enable CloudWatch alarms and DLQ depth alarms per domain. ~$10–15/month */
|
|
13
|
+
cloudWatchAlarms: boolean;
|
|
14
|
+
/** Enable Cognito advanced security (adaptive auth + compromised-credential detection). Phase B. */
|
|
15
|
+
cognitoAdvancedSecurity?: boolean;
|
|
16
|
+
/** Cognito MFA enforcement level. Phase B. */
|
|
17
|
+
cognitoMfaEnforcement?: 'off' | 'optional' | 'required';
|
|
18
|
+
/** Secrets Manager auto-rotation cadence in days. 0 = disabled. Phase B. */
|
|
19
|
+
secretsRotationDays?: 0 | 30 | 90 | 365;
|
|
20
|
+
/** API Gateway minimum TLS version. Phase B. */
|
|
21
|
+
tlsMinimumVersion?: '1.2' | '1.3';
|
|
22
|
+
}
|
|
23
|
+
/** Cost / performance tunables — Phase B. Schema only in Phase A. */
|
|
24
|
+
export interface CostOptions {
|
|
25
|
+
auroraMinAcuNonProd?: 0 | 0.5 | 1;
|
|
26
|
+
auroraPerformanceInsights?: boolean;
|
|
27
|
+
apiGatewayAccessLogSamplingRate?: 0 | 0.1 | 1.0;
|
|
28
|
+
lambdaLogLevelDefault?: 'INFO' | 'WARN' | 'ERROR';
|
|
29
|
+
xRaySamplingRate?: number;
|
|
30
|
+
reservedConcurrencyPerDomain?: Record<string, number>;
|
|
31
|
+
}
|
|
32
|
+
/** Lifecycle tunables — Phase B. Schema only in Phase A. */
|
|
33
|
+
export interface LifecycleOptions {
|
|
34
|
+
dlqRetentionDays?: 7 | 14;
|
|
35
|
+
auroraBackupRetentionDays?: 7 | 14 | 35;
|
|
36
|
+
auditLogArchiveToS3?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/** Per-environment URL configuration. */
|
|
39
|
+
export interface EnvironmentConfig {
|
|
40
|
+
/**
|
|
41
|
+
* Bare domain for the dashboard in this environment (no scheme, no path, no port).
|
|
42
|
+
* Example: "app.acme.com". When set, an ACM cert is provisioned and CloudFront is
|
|
43
|
+
* configured with this domain name. DNS records are surfaced in the Setup tab.
|
|
44
|
+
*/
|
|
45
|
+
dashboardUrl?: string;
|
|
46
|
+
}
|
|
47
|
+
/** Optional per-environment URL overrides. */
|
|
48
|
+
export interface EnvironmentsConfig {
|
|
49
|
+
dev?: EnvironmentConfig;
|
|
50
|
+
staging?: EnvironmentConfig;
|
|
51
|
+
prod?: EnvironmentConfig;
|
|
52
|
+
}
|
|
53
|
+
export interface ScaffoldConfig {
|
|
54
|
+
$schema?: string;
|
|
55
|
+
/** Scaffold version this project was created with / last upgraded to */
|
|
56
|
+
scaffoldVersion: string;
|
|
57
|
+
/** Modules installed in this project */
|
|
58
|
+
enabledModules: ModuleId[];
|
|
59
|
+
/** Project identity — used for token substitution and resource naming */
|
|
60
|
+
projectName: string;
|
|
61
|
+
awsRegion: string;
|
|
62
|
+
awsAccountId: string;
|
|
63
|
+
/** Admin email collected at scaffold time (Cognito seed user). Present only when auth-cognito module is enabled. */
|
|
64
|
+
adminEmail?: string;
|
|
65
|
+
/** Domain IDs added via `tib add-domain` — NOT scaffold-owned, user-managed */
|
|
66
|
+
domainIds: string[];
|
|
67
|
+
/** Flow IDs added via `tib add-flow` — user-managed */
|
|
68
|
+
flowIds: string[];
|
|
69
|
+
/** S3 bucket used for scaffold fetches (defaults to public TIB bucket) */
|
|
70
|
+
scaffoldBucket: string;
|
|
71
|
+
/** Security feature opt-ins. All default false. */
|
|
72
|
+
securityOptions?: SecurityOptions;
|
|
73
|
+
/** ISO timestamp when scaffold was first deployed per environment. CMK and VPC endpoints are locked after first deploy. */
|
|
74
|
+
firstDeployedAt?: {
|
|
75
|
+
dev?: string;
|
|
76
|
+
prod?: string;
|
|
77
|
+
};
|
|
78
|
+
/** CloudWatch log retention in days. Default: 30. */
|
|
79
|
+
logRetentionDays?: LogRetentionDays;
|
|
80
|
+
/** Cost / performance tunables. Phase B — schema present, CDK not yet wired. */
|
|
81
|
+
costOptions?: CostOptions;
|
|
82
|
+
/** Lifecycle tunables. Phase B — schema present, CDK not yet wired. */
|
|
83
|
+
lifecycleOptions?: LifecycleOptions;
|
|
84
|
+
/** Per-environment URL configuration for custom domains. */
|
|
85
|
+
environments?: EnvironmentsConfig;
|
|
86
|
+
/** When true, CORS responses include Allow-Credentials: true (requires non-wildcard origins). */
|
|
87
|
+
allowCredentials?: boolean;
|
|
88
|
+
}
|
|
89
|
+
export declare function readScaffoldConfig(projectRoot: string): Promise<ScaffoldConfig>;
|
|
90
|
+
export declare function writeScaffoldConfig(projectRoot: string, config: ScaffoldConfig): Promise<void>;
|
|
91
|
+
export declare function updateScaffoldConfig(config: ScaffoldConfig, updates: Partial<ScaffoldConfig>): ScaffoldConfig;
|
|
92
|
+
/**
|
|
93
|
+
* Add a domain ID to the scaffold config file on disk.
|
|
94
|
+
*/
|
|
95
|
+
export declare function addDomainToScaffoldConfig(domainId: string, projectRoot: string): Promise<void>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
const DEFAULT_SCAFFOLD_CONFIG = {
|
|
4
|
+
scaffoldVersion: '0.0.0',
|
|
5
|
+
enabledModules: ['core', 'backend-lambda'],
|
|
6
|
+
projectName: 'unknown',
|
|
7
|
+
awsRegion: 'eu-north-1',
|
|
8
|
+
awsAccountId: '',
|
|
9
|
+
scaffoldBucket: 'mc-scaffold',
|
|
10
|
+
securityOptions: {
|
|
11
|
+
cmk: false,
|
|
12
|
+
guardDuty: false,
|
|
13
|
+
waf: false,
|
|
14
|
+
vpcInterfaceEndpoints: false,
|
|
15
|
+
cloudWatchAlarms: false,
|
|
16
|
+
},
|
|
17
|
+
firstDeployedAt: {},
|
|
18
|
+
logRetentionDays: 30,
|
|
19
|
+
};
|
|
20
|
+
const CONFIG_PATH = '.mc/scaffold-config.json';
|
|
21
|
+
export async function readScaffoldConfig(projectRoot) {
|
|
22
|
+
const configPath = path.join(projectRoot, CONFIG_PATH);
|
|
23
|
+
const content = await readFile(configPath, 'utf-8');
|
|
24
|
+
const raw = JSON.parse(content);
|
|
25
|
+
// Backward compatibility: old files may only have domainIds/flowIds
|
|
26
|
+
if (!raw.scaffoldVersion) {
|
|
27
|
+
// eslint-disable-next-line no-console
|
|
28
|
+
console.warn('[tib] scaffold-config.json is missing scaffoldVersion — project was created before S3 scaffold migration. Run `tib upgrade` to backfill.');
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
...DEFAULT_SCAFFOLD_CONFIG,
|
|
32
|
+
domainIds: [],
|
|
33
|
+
flowIds: [],
|
|
34
|
+
...raw,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export async function writeScaffoldConfig(projectRoot, config) {
|
|
38
|
+
const configPath = path.join(projectRoot, CONFIG_PATH);
|
|
39
|
+
await writeFile(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
40
|
+
}
|
|
41
|
+
export function updateScaffoldConfig(config, updates) {
|
|
42
|
+
return { ...config, ...updates };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Add a domain ID to the scaffold config file on disk.
|
|
46
|
+
*/
|
|
47
|
+
export async function addDomainToScaffoldConfig(domainId, projectRoot) {
|
|
48
|
+
const config = await readScaffoldConfig(projectRoot);
|
|
49
|
+
if (!config.domainIds.includes(domainId)) {
|
|
50
|
+
config.domainIds.push(domainId);
|
|
51
|
+
await writeScaffoldConfig(projectRoot, config);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface TokenContext {
|
|
2
|
+
PROJECT_NAME: string;
|
|
3
|
+
PROJECT_NAME_PASCAL: string;
|
|
4
|
+
PROJECT_NAME_UPPER: string;
|
|
5
|
+
AWS_REGION: string;
|
|
6
|
+
AWS_ACCOUNT_ID: string;
|
|
7
|
+
ADMIN_EMAIL: string;
|
|
8
|
+
SCAFFOLD_VERSION: string;
|
|
9
|
+
SCAFFOLD_DATE: string;
|
|
10
|
+
TIB_CLI_VERSION: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ScaffoldConfigForTokens {
|
|
13
|
+
projectName: string;
|
|
14
|
+
awsRegion: string;
|
|
15
|
+
awsAccountId: string;
|
|
16
|
+
adminEmail?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Replace all {{TOKEN}} placeholders in a template string with values from ctx.
|
|
20
|
+
* Throws if an unknown token is encountered.
|
|
21
|
+
*/
|
|
22
|
+
export declare function applyTokens(template: string, ctx: TokenContext): string;
|
|
23
|
+
/**
|
|
24
|
+
* Build a TokenContext from a scaffold config and resolved version.
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildTokenContext(config: ScaffoldConfigForTokens, scaffoldVersion: string, cliVersion: string): TokenContext;
|
|
27
|
+
/**
|
|
28
|
+
* Returns true if the given file extension indicates the file should receive token substitution.
|
|
29
|
+
* Only .tpl files are substituted.
|
|
30
|
+
*/
|
|
31
|
+
export declare function isTemplateFile(filename: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Strip the .tpl extension from a filename to get the install path.
|
|
34
|
+
*/
|
|
35
|
+
export declare function stripTplExtension(filename: string): string;
|