@zaaxch/tailframe 4.0.0 → 4.0.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zaaxch/tailframe",
3
- "version": "4.0.0",
3
+ "version": "4.0.1",
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
@@ -131,6 +131,8 @@ add("package.json", JSON.stringify({
131
131
  add("pnpm-workspace.yaml", `packages:
132
132
  - "apps/*"
133
133
  injectWorkspacePackages: true
134
+ minimumReleaseAgeExclude:
135
+ - "@zaaxch/tailframe@${contractVersion}"
134
136
  allowBuilds:
135
137
  "@firebase/util": true
136
138
  esbuild: true
@@ -564,8 +566,8 @@ add(`${ui}/package.json`, JSON.stringify({ name: uiPackage, version: "0.1.0", pr
564
566
  add(`${ui}/.prettierrc.json`, JSON.stringify(prettierConfig, null, "\t"));
565
567
  add(`${ui}/.gitattributes`, gitAttributes);
566
568
  add(`${ui}/tsconfig.json`, JSON.stringify({ files: [], references: [{ path: "./tsconfig.app.json" }, { path: "./tsconfig.node.json" }] }, null, "\t"));
567
- 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"));
568
- add(`${ui}/tsconfig.node.json`, JSON.stringify({ extends: "@tsconfig/node22/tsconfig.json", include: ["vite.config.*"], compilerOptions: { composite: true, types: ["node"] } }, null, "\t"));
569
+ add(`${ui}/tsconfig.app.json`, JSON.stringify({ extends: "@vue/tsconfig/tsconfig.dom.json", include: ["env.d.ts", "src/**/*", "src/**/*.vue"], compilerOptions: { composite: true, noEmit: true, tsBuildInfoFile: "./.tmp/tsconfig.app.tsbuildinfo", types: ["vitest/globals"], baseUrl: ".", paths: { "@/*": ["./src/*"] } } }, null, "\t"));
570
+ add(`${ui}/tsconfig.node.json`, JSON.stringify({ extends: "@tsconfig/node22/tsconfig.json", include: ["vite.config.ts"], compilerOptions: { composite: true, noEmit: true, tsBuildInfoFile: "./.tmp/tsconfig.node.tsbuildinfo", types: ["node"] } }, null, "\t"));
569
571
  add(`${ui}/vite.config.ts`, `import { fileURLToPath, URL } from "node:url";\nimport { defineConfig } from "vitest/config";\nimport vue from "@vitejs/plugin-vue";\nimport tailwindcss from "@tailwindcss/vite";\nexport default defineConfig({ plugins: [vue(), tailwindcss()], resolve: { alias: { "@": fileURLToPath(new URL("./src", import.meta.url)) } }, server: { proxy: { "/api": { target: "http://localhost:3000", changeOrigin: true } } }, test: { environment: "jsdom", globals: true } });`);
570
572
  add(`${ui}/env.d.ts`, `/// <reference types="vite/client" />`);
571
573
  add(`${ui}/index.html`, `<!doctype html>
@@ -581,7 +583,7 @@ add(`${ui}/index.html`, `<!doctype html>
581
583
  </body>
582
584
  </html>
583
585
  `);
584
- add(`${ui}/.gitignore`, `node_modules/\ndist/\n.env*\n!.env.example\ncerts/\n*.log\n`);
586
+ add(`${ui}/.gitignore`, `node_modules/\ndist/\n.env*\n!.env.example\ncerts/\n*.log\n.tmp/\n`);
585
587
  if (options.auth === "firebase") add(`${ui}/.env.example`, `VITE_FIREBASE_API_KEY=\nVITE_FIREBASE_AUTH_DOMAIN=\nVITE_FIREBASE_PROJECT_ID=\nVITE_FIREBASE_APP_ID=\n`);
586
588
  add(`${ui}/src/core/errors.ts`, uiErrorsSource);
587
589
  add(`${ui}/src/core/rpc.ts`, uiRpcSource);
@@ -719,7 +721,7 @@ Run \`pnpm validate:architecture\` and \`pnpm format:check\`, then add focused V
719
721
  }
720
722
 
721
723
  add(`${svc}/Dockerfile`, `FROM node:22.13-bookworm AS build
722
- RUN corepack enable
724
+ RUN npm install --global corepack@0.34.5 && corepack enable
723
725
  WORKDIR /workspace
724
726
  COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./
725
727
  COPY apps/service/package.json apps/service/package.json
@@ -737,7 +739,7 @@ EXPOSE 3000
737
739
  CMD ["node", "dist/server.js"]
738
740
  `);
739
741
  add(`${svc}/Dockerfile.dev`, `FROM node:22.13-alpine
740
- RUN corepack enable
742
+ RUN npm install --global corepack@0.34.5 && corepack enable
741
743
  WORKDIR /workspace
742
744
  COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./
743
745
  COPY apps/service/package.json apps/service/package.json
@@ -1005,8 +1007,9 @@ const serviceVolumes = [
1005
1007
  ...(options.auth === "firebase" ? [" - ${SECRETS_DIR:?Set SECRETS_DIR}/firebase-service-account.json:/app/secrets/firebase-service-account.json:ro"] : [])
1006
1008
  ];
1007
1009
  const devVolumes = [
1008
- " - .:/app",
1009
- " - /app/node_modules",
1010
+ " - .:/workspace/apps/service",
1011
+ " - /workspace/node_modules",
1012
+ " - /workspace/apps/service/node_modules",
1010
1013
  ...(options.auth === "firebase" ? [" - ./firebase-service-account.development.json:/app/firebase-service-account.development.json:ro"] : [])
1011
1014
  ];
1012
1015
  const productionDependencies = [
@@ -1031,9 +1034,13 @@ const productionEnvironment = [
1031
1034
  ...(options.redis ? [" REDIS_URL: \"redis://:\${REDIS_PASSWORD:?Set REDIS_PASSWORD}@redis:6379\""] : [])
1032
1035
  ];
1033
1036
  const productionEnvironmentBlock = `\n environment:\n${productionEnvironment.join("\n")}`;
1034
- const databaseEnvironmentBlock = postgres
1035
- ? `\n environment:\n POSTGRES_URI: "postgresql://${options.name}-app:development@postgres:5432/${databaseName}"`
1036
- : `\n environment:\n MONGODB_URI: "mongodb://mongodb:27017/?replicaSet=rs0&directConnection=true"`;
1037
+ const developmentEnvironment = [
1038
+ postgres
1039
+ ? ` POSTGRES_URI: "postgresql://${options.name}-app:development@postgres:5432/${databaseName}"`
1040
+ : ` MONGODB_URI: "mongodb://mongodb:27017/?replicaSet=rs0&directConnection=true"`,
1041
+ ...(options.auth === "firebase" ? [" FIREBASE_SERVICE_ACCOUNT_PATH: /app/firebase-service-account.development.json"] : [])
1042
+ ];
1043
+ const developmentEnvironmentBlock = `\n environment:\n${developmentEnvironment.join("\n")}`;
1037
1044
  const volumeBlock = serviceVolumes.length ? `\n volumes:\n${serviceVolumes.join("\n")}` : "";
1038
1045
  const workerService = options.worker ? `\n worker:
1039
1046
  image: \${ECR_IMAGE:?Set ECR_IMAGE}:\${IMAGE_TAG:?Set IMAGE_TAG}
@@ -1206,7 +1213,7 @@ const devWorker = options.worker ? `\n worker:
1206
1213
  dockerfile: Dockerfile.dev
1207
1214
  env_file:
1208
1215
  - .env.development
1209
- command: ["npm", "run", "dev:worker"]${databaseEnvironmentBlock}
1216
+ command: ["npm", "run", "dev:worker"]${developmentEnvironmentBlock}
1210
1217
  volumes:
1211
1218
  ${devVolumes.join("\n")}${devDependsBlock}` : "";
1212
1219
  const devDatabase = postgres ? `\n postgres:
@@ -1270,7 +1277,7 @@ services:
1270
1277
  dockerfile: Dockerfile.dev
1271
1278
  env_file:
1272
1279
  - .env.development
1273
- command: ["npm", "run", "schema:apply:dev"]${databaseEnvironmentBlock}
1280
+ command: ["npm", "run", "schema:apply:dev"]${developmentEnvironmentBlock}
1274
1281
  volumes:
1275
1282
  ${devVolumes.join("\n")}
1276
1283
  depends_on:
@@ -1283,7 +1290,7 @@ ${devVolumes.join("\n")}
1283
1290
  env_file:
1284
1291
  - .env.development
1285
1292
  ports:
1286
- - "3000:3000"${databaseEnvironmentBlock}
1293
+ - "3000:3000"${developmentEnvironmentBlock}
1287
1294
  volumes:
1288
1295
  ${devVolumes.join("\n")}${devDependsBlock}${devWorker}${devDatabase}${devRedis}
1289
1296
  volumes:
package/src/validate.mjs CHANGED
@@ -65,6 +65,50 @@ function validateAppMetadata(productRoot, app) {
65
65
  const errors = [];
66
66
  const root = appRoot(productRoot, app);
67
67
  if (!fs.existsSync(root)) return [`Missing configured application root: ${app.path}`];
68
+ if (app.kind === "service") {
69
+ const composeFile = path.join(root, "docker-compose.dev.yml");
70
+ if (!fs.existsSync(composeFile)) errors.push(`${app.path}/docker-compose.dev.yml is required`);
71
+ else {
72
+ const compose = fs.readFileSync(composeFile, "utf8");
73
+ const generatedProcessCount = app.profiles.includes("worker") ? 3 : 2;
74
+ const count = (term) => compose.split(term).length - 1;
75
+ for (const term of [
76
+ " - .:/workspace/apps/service",
77
+ " - /workspace/node_modules",
78
+ " - /workspace/apps/service/node_modules"
79
+ ]) {
80
+ if (count(term) < generatedProcessCount) {
81
+ errors.push(`${app.path}/docker-compose.dev.yml must mount every generated service process at the image workspace: ${term.trim()}`);
82
+ }
83
+ }
84
+ if (compose.includes(" - .:/app\n")) {
85
+ errors.push(`${app.path}/docker-compose.dev.yml uses the obsolete /app service source mount`);
86
+ }
87
+ if (app.profiles.includes("firebase")) {
88
+ for (const term of [
89
+ " FIREBASE_SERVICE_ACCOUNT_PATH: /app/firebase-service-account.development.json",
90
+ " - ./firebase-service-account.development.json:/app/firebase-service-account.development.json:ro"
91
+ ]) {
92
+ if (count(term) < generatedProcessCount) {
93
+ errors.push(`${app.path}/docker-compose.dev.yml must use the canonical Firebase development credential path for every generated service process: ${term.trim()}`);
94
+ }
95
+ }
96
+ }
97
+ }
98
+ }
99
+ if (app.kind === "ui") {
100
+ const generatedArtifacts = fs.readdirSync(root, { withFileTypes: true })
101
+ .filter(
102
+ (entry) =>
103
+ entry.isFile() &&
104
+ (entry.name.endsWith(".tsbuildinfo") || ["vite.config.js", "vite.config.d.ts"].includes(entry.name))
105
+ )
106
+ .map((entry) => entry.name)
107
+ .sort();
108
+ for (const artifact of generatedArtifacts) {
109
+ errors.push(`${app.path}/${artifact} is generated compiler output; keep incremental state under apps/ui/.tmp`);
110
+ }
111
+ }
68
112
  if (fs.existsSync(path.join(root, "tailframe.json"))) {
69
113
  errors.push(`${app.path}/tailframe.json is forbidden; the product root owns Tailframe metadata`);
70
114
  }