@zaaxch/tailframe 2.0.0 → 2.1.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/package.json +1 -1
- package/src/new.mjs +27 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zaaxch/tailframe",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Tailframe architecture toolkit: validates the Tailframe structure, import-boundary, and file-convention contracts. The package version is the contract version.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/new.mjs
CHANGED
|
@@ -77,9 +77,7 @@ const prettierConfig = {
|
|
|
77
77
|
endOfLine: "lf",
|
|
78
78
|
singleQuote: false
|
|
79
79
|
};
|
|
80
|
-
|
|
81
|
-
add(".prettierrc.json", JSON.stringify(prettierConfig, null, "\t"));
|
|
82
|
-
add(".gitattributes", `* text=auto eol=lf
|
|
80
|
+
const gitAttributes = `* text=auto eol=lf
|
|
83
81
|
|
|
84
82
|
*.png binary
|
|
85
83
|
*.jpg binary
|
|
@@ -87,7 +85,10 @@ add(".gitattributes", `* text=auto eol=lf
|
|
|
87
85
|
*.gif binary
|
|
88
86
|
*.ico binary
|
|
89
87
|
*.pdf binary
|
|
90
|
-
|
|
88
|
+
`;
|
|
89
|
+
|
|
90
|
+
add(".prettierrc.json", JSON.stringify(prettierConfig, null, "\t"));
|
|
91
|
+
add(".gitattributes", gitAttributes);
|
|
91
92
|
|
|
92
93
|
const repos = [[svc, "Express/TypeScript API and background runtime."]];
|
|
93
94
|
if (options.ui) repos.push([ui, "Vue/Vite customer interface."]);
|
|
@@ -203,7 +204,7 @@ add(`${svc}/package.json`, JSON.stringify({
|
|
|
203
204
|
...(options.worker ? { "dev:worker": "nodemon --legacy-watch --exec ts-node -r tsconfig-paths/register src/worker.ts" } : {}),
|
|
204
205
|
build: "tsc && tsc-alias", start: "node dist/server.js",
|
|
205
206
|
...(options.worker ? { worker: "node dist/worker.js" } : {}),
|
|
206
|
-
test: "npm run test:unit && npm run test:integration", "test:unit": "jest --selectProjects unit --runInBand", "test:integration": "jest --selectProjects integration --runInBand", "validate:architecture": "tailframe validate --kind service .", format: "prettier --write src/",
|
|
207
|
+
test: "npm run test:unit && npm run test:integration", "test:unit": "jest --selectProjects unit --runInBand", "test:integration": "jest --selectProjects integration --runInBand", "validate:architecture": "tailframe validate --kind service .", format: "prettier --write src/", "format:check": "prettier --check src/",
|
|
207
208
|
"test:db:up": "docker compose -f docker-compose.test.yml up -d", "test:db:down": "docker compose -f docker-compose.test.yml down -v",
|
|
208
209
|
"docker:dev": "docker compose -f docker-compose.dev.yml up",
|
|
209
210
|
"docker:prod": "docker compose -f docker-compose.yml up -d",
|
|
@@ -220,6 +221,7 @@ module.exports = { projects: [
|
|
|
220
221
|
{ ...base, displayName: "integration", testMatch: ["**/*.integration.test.ts"] }
|
|
221
222
|
] };`);
|
|
222
223
|
add(`${svc}/.prettierrc.json`, JSON.stringify(prettierConfig, null, "\t"));
|
|
224
|
+
add(`${svc}/.gitattributes`, gitAttributes);
|
|
223
225
|
add(`${svc}/.gitignore`, `node_modules/\ndist/\n.env*\n!.env.example\n*.log\nfirebase-service-account*.json\n`);
|
|
224
226
|
add(`${svc}/.dockerignore`, `node_modules\ndist\n.git\n.env*\n!.env.example\n*.log\ndata\nfirebase-service-account*.json\n`);
|
|
225
227
|
if (options.ui) add(".dockerignore", `**/node_modules\n**/dist\n**/.git\n**/.env*\n**/*.log\n**/data\n**/firebase-service-account*.json\n`);
|
|
@@ -444,15 +446,19 @@ This service uses MongoDB. Module-owned MongoDB adapters own collection access,
|
|
|
444
446
|
- Register routes, workers, and jobs explicitly. Shutdown paths are awaitable and close resources without forcing \`process.exit\`.
|
|
445
447
|
${options.ui ? "- In production, serve the compiled UI from `dist/public` with a non-API SPA fallback. Never return the SPA for `/api` requests." : ""}
|
|
446
448
|
|
|
449
|
+
## Production image
|
|
450
|
+
The ECR build targets \`linux/amd64\`. ${options.ui ? "Its default immutable tag is `<service-sha>_<ui-sha>` and production Compose requires that exact tag." : "Its default immutable tag is `<service-sha>` and production Compose requires that exact tag."}${options.ui && options.auth === "firebase" ? ` The build requires \`${ui}/.env.production\` and mounts it as a BuildKit secret only while Vite compiles the browser bundle; it is not copied into the final image.` : ""}
|
|
451
|
+
|
|
447
452
|
## Tests and validation
|
|
448
|
-
Run \`npm run validate:architecture\` after changing files or imports. The generated service also includes unit tests and Mongo-backed integration tests. Run \`npm run test:db:up\`, \`npm test\`, and \`npm run test:db:down\` for the full service check. Test use cases, policies, Mongo repository adapters, and HTTP boundaries where behavior lives. Every public operation needs success and error-envelope coverage; authenticated operations need trusted-identity coverage; persisted capabilities need Mongo ownership/query coverage; UI-enabled services must verify that \`/api\` paths never fall through to the SPA. All AWS/ECR and Docker commands, including \`npm run docker:push\`, must run outside the sandbox from the first attempt.
|
|
453
|
+
Run \`npm run validate:architecture\` and \`npm run format:check\` after changing files or imports. The generated service also includes unit tests and Mongo-backed integration tests. Run \`npm run test:db:up\`, \`npm test\`, and \`npm run test:db:down\` for the full service check. Test use cases, policies, Mongo repository adapters, and HTTP boundaries where behavior lives. Every public operation needs success and error-envelope coverage; authenticated operations need trusted-identity coverage; persisted capabilities need Mongo ownership/query coverage; UI-enabled services must verify that \`/api\` paths never fall through to the SPA. All AWS/ECR and Docker commands, including \`npm run docker:push\`, must run outside the sandbox from the first attempt.
|
|
449
454
|
`);
|
|
450
455
|
|
|
451
456
|
if (options.ui) {
|
|
452
457
|
const uiDeps = { "@tailwindcss/vite": "^4.1.18", "@vueuse/core": "^14.3.0", axios: "^1.9.0", pinia: "^3.0.1", primevue: "^4.2.5", vue: "^3.5.13", "vue-router": "^4.5.0", ...(options.auth === "firebase" ? { firebase: "^11.0.0" } : {}) };
|
|
453
458
|
const uiDevDeps = { "@tsconfig/node22": "^22.0.1", "@zaaxch/tailframe": contractVersion, "@types/node": "^22.13.14", "@vitejs/plugin-vue": "^5.2.3", "@vue/test-utils": "^2.4.10", "@vue/tsconfig": "^0.7.0", jsdom: "^29.1.1", "npm-run-all2": "^7.0.2", prettier: "3.5.3", typescript: "~5.8.0", vite: "^6.2.4", vitest: "^4.1.7", "vue-tsc": "^2.2.8" };
|
|
454
|
-
add(`${ui}/package.json`, JSON.stringify({ name: ui, version: "0.1.0", private: true, type: "module", scripts: { dev: "vite --host 0.0.0.0", build: "run-p type-check \"build-only {@}\" --", "build-only": "vite build", "type-check": "vue-tsc --build", test: "vitest", "validate:architecture": "tailframe validate --kind ui .", format: "prettier --write src/" }, dependencies: uiDeps, devDependencies: uiDevDeps }, null, "\t"));
|
|
459
|
+
add(`${ui}/package.json`, JSON.stringify({ name: ui, version: "0.1.0", private: true, type: "module", scripts: { dev: "vite --host 0.0.0.0", build: "run-p type-check \"build-only {@}\" --", "build-only": "vite build", "type-check": "vue-tsc --build", test: "vitest run", "test:watch": "vitest", "validate:architecture": "tailframe validate --kind ui .", format: "prettier --write src/", "format:check": "prettier --check src/" }, dependencies: uiDeps, devDependencies: uiDevDeps }, null, "\t"));
|
|
455
460
|
add(`${ui}/.prettierrc.json`, JSON.stringify(prettierConfig, null, "\t"));
|
|
461
|
+
add(`${ui}/.gitattributes`, gitAttributes);
|
|
456
462
|
add(`${ui}/tsconfig.json`, JSON.stringify({ files: [], references: [{ path: "./tsconfig.app.json" }, { path: "./tsconfig.node.json" }] }, null, "\t"));
|
|
457
463
|
add(`${ui}/tsconfig.app.json`, JSON.stringify({ extends: "@vue/tsconfig/tsconfig.dom.json", include: ["env.d.ts", "src/**/*", "src/**/*.vue"], compilerOptions: { composite: true, baseUrl: ".", paths: { "@/*": ["./src/*"] } } }, null, "\t"));
|
|
458
464
|
add(`${ui}/tsconfig.node.json`, JSON.stringify({ extends: "@tsconfig/node22/tsconfig.json", include: ["vite.config.*"], compilerOptions: { composite: true, types: ["node"] } }, null, "\t"));
|
|
@@ -587,13 +593,13 @@ Reuse Vue, PrimeVue, Tailwind, loading, error, and accessibility conventions. Pr
|
|
|
587
593
|
- Keep \`src/app/styles.css\` limited to Tailwind imports, theme tokens, and true global base behavior.
|
|
588
594
|
|
|
589
595
|
## Production packaging
|
|
590
|
-
The UI runs through Vite in development but has no production container. The service's multi-stage Dockerfile builds this repository and copies its output into \`dist/public\`; Express serves it with SPA fallback. AWS ECR stores the combined production image.
|
|
596
|
+
The UI runs through Vite in development but has no production container. The service's multi-stage Dockerfile builds this repository and copies its output into \`dist/public\`; Express serves it with SPA fallback. AWS ECR stores the combined \`linux/amd64\` production image under the default immutable \`<service-sha>_<ui-sha>\` tag, and production Compose requires that exact tag.${options.auth === "firebase" ? ` Provide \`${ui}/.env.production\` before a production build; the service build mounts it as a required BuildKit secret only while Vite compiles the browser bundle and does not copy it into the final image.` : ""}
|
|
591
597
|
|
|
592
598
|
## Product language
|
|
593
599
|
The product domain is undefined. Do not invent entities, workflows, roles, claims, navigation, or customer-facing promises.
|
|
594
600
|
|
|
595
601
|
## Validation
|
|
596
|
-
Run \`npm run validate:architecture\`, then add focused Vitest coverage and run type-check, build, and
|
|
602
|
+
Run \`npm run validate:architecture\` and \`npm run format:check\`, then add focused Vitest coverage and run type-check, build, and \`npm test\` as applicable. Use \`npm run test:watch\` only for interactive development.
|
|
597
603
|
`);
|
|
598
604
|
}
|
|
599
605
|
|
|
@@ -609,7 +615,7 @@ WORKDIR /app/ui
|
|
|
609
615
|
COPY ${ui}/package*.json ./
|
|
610
616
|
RUN npm ci
|
|
611
617
|
COPY ${ui}/ ./
|
|
612
|
-
RUN npm run build
|
|
618
|
+
${options.auth === "firebase" ? "RUN --mount=type=secret,id=ui_env,target=/app/ui/.env.production,required=true npm run build" : "RUN npm run build"}
|
|
613
619
|
|
|
614
620
|
FROM node:22.13-bookworm-slim
|
|
615
621
|
WORKDIR /app
|
|
@@ -650,18 +656,23 @@ set -euo pipefail
|
|
|
650
656
|
: "\${AWS_ACCOUNT_ID:?Set AWS_ACCOUNT_ID}"
|
|
651
657
|
: "\${AWS_REGION:?Set AWS_REGION}"
|
|
652
658
|
|
|
659
|
+
SVC_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
|
|
660
|
+
BUILD_CONTEXT="${options.ui ? `$(cd "\${SVC_DIR}/.." && pwd)` : `\${SVC_DIR}`}"
|
|
661
|
+
${options.ui ? `UI_DIR="\${BUILD_CONTEXT}/${ui}"
|
|
662
|
+
SVC_SHA="$(git -C "\${SVC_DIR}" rev-parse --short HEAD)"
|
|
663
|
+
UI_SHA="$(git -C "\${UI_DIR}" rev-parse --short HEAD)"` : `SVC_SHA="$(git -C "\${SVC_DIR}" rev-parse --short HEAD)"`}
|
|
664
|
+
|
|
653
665
|
ECR_REPOSITORY="\${ECR_REPOSITORY:-${options.name}}"
|
|
654
|
-
IMAGE_TAG="\${IMAGE_TAG
|
|
666
|
+
IMAGE_TAG="\${IMAGE_TAG:-${options.ui ? `\${SVC_SHA}_\${UI_SHA}` : `\${SVC_SHA}`}}"
|
|
655
667
|
REGISTRY="\${AWS_ACCOUNT_ID}.dkr.ecr.\${AWS_REGION}.amazonaws.com"
|
|
656
668
|
IMAGE="\${REGISTRY}/\${ECR_REPOSITORY}:\${IMAGE_TAG}"
|
|
657
|
-
SVC_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
|
|
658
|
-
BUILD_CONTEXT="${options.ui ? `$(cd "\${SVC_DIR}/.." && pwd)` : `\${SVC_DIR}`}"
|
|
659
669
|
DOCKERFILE="\${SVC_DIR}/Dockerfile"
|
|
670
|
+
${options.ui && options.auth === "firebase" ? `UI_ENV_FILE="\${UI_ENV_FILE:-\${UI_DIR}/.env.production}"` : ""}
|
|
660
671
|
|
|
661
672
|
aws ecr get-login-password --region "\${AWS_REGION}" |
|
|
662
673
|
docker login --username AWS --password-stdin "\${REGISTRY}"
|
|
663
674
|
|
|
664
|
-
docker build --file "\${DOCKERFILE}" --tag "\${IMAGE}" "\${BUILD_CONTEXT}"
|
|
675
|
+
docker build --platform linux/amd64${options.ui && options.auth === "firebase" ? ` --secret "id=ui_env,src=\${UI_ENV_FILE}"` : ""} --file "\${DOCKERFILE}" --tag "\${IMAGE}" "\${BUILD_CONTEXT}"
|
|
665
676
|
docker push "\${IMAGE}"
|
|
666
677
|
|
|
667
678
|
echo "Pushed \${IMAGE}"
|
|
@@ -683,7 +694,7 @@ const dependsBlock = `\n depends_on:\n${dependencies.map(([dependency, condit
|
|
|
683
694
|
const databaseEnvironmentBlock = `\n environment:\n MONGODB_URI: "mongodb://mongodb:27017/?replicaSet=rs0&directConnection=true"`;
|
|
684
695
|
const volumeBlock = serviceVolumes.length ? `\n volumes:\n${serviceVolumes.join("\n")}` : "";
|
|
685
696
|
const workerService = options.worker ? `\n worker:
|
|
686
|
-
image: \${ECR_IMAGE:?Set ECR_IMAGE}:\${IMAGE_TAG
|
|
697
|
+
image: \${ECR_IMAGE:?Set ECR_IMAGE}:\${IMAGE_TAG:?Set IMAGE_TAG}
|
|
687
698
|
env_file:
|
|
688
699
|
- .env.production
|
|
689
700
|
command: ["node", "dist/worker.js"]${databaseEnvironmentBlock}${volumeBlock}${dependsBlock}
|
|
@@ -728,7 +739,7 @@ const namedVolumes = [
|
|
|
728
739
|
add(`${svc}/docker-compose.yml`, `name: ${options.name}-prod
|
|
729
740
|
services:
|
|
730
741
|
${options.name}:
|
|
731
|
-
image: \${ECR_IMAGE:?Set ECR_IMAGE}:\${IMAGE_TAG
|
|
742
|
+
image: \${ECR_IMAGE:?Set ECR_IMAGE}:\${IMAGE_TAG:?Set IMAGE_TAG}
|
|
732
743
|
env_file:
|
|
733
744
|
- .env.production
|
|
734
745
|
ports:
|