@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 +1 -1
- package/src/new.mjs +20 -13
- package/src/validate.mjs +44 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zaaxch/tailframe",
|
|
3
|
-
"version": "4.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
|
|
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
|
-
" - .:/
|
|
1009
|
-
" - /
|
|
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
|
|
1035
|
-
|
|
1036
|
-
|
|
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"]${
|
|
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"]${
|
|
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"${
|
|
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
|
}
|