@specverse/engines 4.2.0 → 4.2.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/assets/templates/default/specs/main.specly +65 -0
- package/dist/libs/instance-factories/CURVED-INTERFACE.md +278 -0
- package/dist/libs/instance-factories/README.md +73 -0
- package/dist/libs/instance-factories/applications/README.md +51 -0
- package/dist/libs/instance-factories/applications/generic-app.yaml +52 -0
- package/dist/libs/instance-factories/applications/react-app-runtime.yaml +139 -0
- package/dist/libs/instance-factories/applications/react-app-starter.yaml +143 -0
- package/dist/libs/instance-factories/applications/templates/react/env-example-generator.js +24 -2
- package/dist/libs/instance-factories/applications/templates/react/vite-config-generator.js +54 -33
- package/dist/libs/instance-factories/applications/templates/react-starter/README.md +211 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/api-types-starter-generator.js +69 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/app-tsx-generator.js +1 -1
- package/dist/libs/instance-factories/applications/templates/react-starter/belongs-to.js +40 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/dashboard-body-composer.js +11 -3
- package/dist/libs/instance-factories/applications/templates/react-starter/detail-body-composer.js +18 -16
- package/dist/libs/instance-factories/applications/templates/react-starter/form-body-composer.js +50 -23
- package/dist/libs/instance-factories/applications/templates/react-starter/helpers-emitter.js +9 -3
- package/dist/libs/instance-factories/applications/templates/react-starter/list-body-composer.js +17 -7
- package/dist/libs/instance-factories/applications/templates/react-starter/orchestrator.js +16 -5
- package/dist/libs/instance-factories/applications/templates/react-starter/skeletons/dashboard.tsx.template +49 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/skeletons/detail.tsx.template +96 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/skeletons/form.tsx.template +116 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/skeletons/list.tsx.template +74 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/use-api-hooks-starter-generator.js +95 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/view-emitter.js +26 -1
- package/dist/libs/instance-factories/archived/fastify-prisma.yaml +104 -0
- package/dist/libs/instance-factories/cli/README.md +43 -0
- package/dist/libs/instance-factories/cli/commander-js.yaml +55 -0
- package/dist/libs/instance-factories/cli/templates/commander/command-generator.js +49 -1
- package/dist/libs/instance-factories/communication/README.md +47 -0
- package/dist/libs/instance-factories/communication/event-emitter.yaml +60 -0
- package/dist/libs/instance-factories/communication/rabbitmq-events.yaml +87 -0
- package/dist/libs/instance-factories/controllers/README.md +42 -0
- package/dist/libs/instance-factories/controllers/fastify.yaml +139 -0
- package/dist/libs/instance-factories/controllers/templates/fastify/server-generator.js +29 -2
- package/dist/libs/instance-factories/infrastructure/README.md +29 -0
- package/dist/libs/instance-factories/infrastructure/docker-k8s.yaml +61 -0
- package/dist/libs/instance-factories/orms/README.md +54 -0
- package/dist/libs/instance-factories/orms/prisma.yaml +89 -0
- package/dist/libs/instance-factories/orms/templates/prisma/schema-generator.js +2 -2
- package/dist/libs/instance-factories/scaffolding/README.md +49 -0
- package/dist/libs/instance-factories/scaffolding/generic-scaffold.yaml +65 -0
- package/dist/libs/instance-factories/sdks/README.md +28 -0
- package/dist/libs/instance-factories/sdks/python-sdk.yaml +66 -0
- package/dist/libs/instance-factories/sdks/typescript-sdk.yaml +59 -0
- package/dist/libs/instance-factories/services/README.md +55 -0
- package/dist/libs/instance-factories/services/prisma-services.yaml +71 -0
- package/dist/libs/instance-factories/storage/README.md +34 -0
- package/dist/libs/instance-factories/storage/mongodb.yaml +79 -0
- package/dist/libs/instance-factories/storage/postgresql.yaml +75 -0
- package/dist/libs/instance-factories/storage/redis.yaml +79 -0
- package/dist/libs/instance-factories/testing/README.md +40 -0
- package/dist/libs/instance-factories/testing/vitest-tests.yaml +63 -0
- package/dist/libs/instance-factories/tools/README.md +70 -0
- package/dist/libs/instance-factories/tools/mcp.yaml +36 -0
- package/dist/libs/instance-factories/tools/vscode.yaml +35 -0
- package/dist/libs/instance-factories/validation/README.md +38 -0
- package/dist/libs/instance-factories/validation/zod.yaml +56 -0
- package/dist/realize/engines/code-generator.d.ts.map +1 -1
- package/dist/realize/engines/code-generator.js +3 -0
- package/dist/realize/engines/code-generator.js.map +1 -1
- package/libs/instance-factories/applications/react-app-starter.yaml +10 -17
- package/libs/instance-factories/applications/templates/react/env-example-generator.ts +24 -2
- package/libs/instance-factories/applications/templates/react/vite-config-generator.ts +54 -33
- package/libs/instance-factories/applications/templates/react-starter/__tests__/detail-body-composer.test.ts +5 -4
- package/libs/instance-factories/applications/templates/react-starter/__tests__/form-body-composer.test.ts +18 -5
- package/libs/instance-factories/applications/templates/react-starter/__tests__/orchestrator.test.ts +83 -62
- package/libs/instance-factories/applications/templates/react-starter/api-types-starter-generator.ts +98 -0
- package/libs/instance-factories/applications/templates/react-starter/app-tsx-generator.ts +1 -1
- package/libs/instance-factories/applications/templates/react-starter/belongs-to.ts +82 -0
- package/libs/instance-factories/applications/templates/react-starter/dashboard-body-composer.ts +20 -5
- package/libs/instance-factories/applications/templates/react-starter/detail-body-composer.ts +33 -33
- package/libs/instance-factories/applications/templates/react-starter/form-body-composer.ts +107 -30
- package/libs/instance-factories/applications/templates/react-starter/helpers-emitter.ts +9 -3
- package/libs/instance-factories/applications/templates/react-starter/list-body-composer.ts +34 -8
- package/libs/instance-factories/applications/templates/react-starter/orchestrator.ts +41 -26
- package/libs/instance-factories/applications/templates/react-starter/skeletons/dashboard.tsx.template +2 -0
- package/libs/instance-factories/applications/templates/react-starter/skeletons/detail.tsx.template +2 -0
- package/libs/instance-factories/applications/templates/react-starter/skeletons/form.tsx.template +2 -0
- package/libs/instance-factories/applications/templates/react-starter/skeletons/list.tsx.template +2 -0
- package/libs/instance-factories/applications/templates/react-starter/use-api-hooks-starter-generator.ts +124 -0
- package/libs/instance-factories/applications/templates/react-starter/view-emitter.ts +58 -0
- package/libs/instance-factories/cli/templates/commander/command-generator.ts +49 -1
- package/libs/instance-factories/controllers/fastify.yaml +7 -0
- package/libs/instance-factories/controllers/templates/fastify/server-generator.ts +36 -2
- package/libs/instance-factories/orms/templates/prisma/schema-generator.ts +11 -4
- package/package.json +1 -1
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: VSCodeExtension
|
|
2
|
+
version: "4.0.0"
|
|
3
|
+
category: tools
|
|
4
|
+
description: "VSCode extension with language support, commands, themes, and schema validation"
|
|
5
|
+
|
|
6
|
+
metadata:
|
|
7
|
+
author: "SpecVerse Team"
|
|
8
|
+
license: "MIT"
|
|
9
|
+
tags: [vscode, ide, extension, language-support]
|
|
10
|
+
|
|
11
|
+
compatibility:
|
|
12
|
+
specverse: "^4.0.0"
|
|
13
|
+
node: ">=18.0.0"
|
|
14
|
+
|
|
15
|
+
capabilities:
|
|
16
|
+
provides:
|
|
17
|
+
- "tools.vscode"
|
|
18
|
+
- "tools.ide"
|
|
19
|
+
requires: []
|
|
20
|
+
|
|
21
|
+
technology:
|
|
22
|
+
runtime: "node"
|
|
23
|
+
language: "typescript"
|
|
24
|
+
framework: "vscode"
|
|
25
|
+
|
|
26
|
+
dependencies:
|
|
27
|
+
runtime:
|
|
28
|
+
- name: "vscode"
|
|
29
|
+
version: "^1.85.0"
|
|
30
|
+
|
|
31
|
+
codeTemplates:
|
|
32
|
+
vscode-extension:
|
|
33
|
+
engine: typescript
|
|
34
|
+
generator: "libs/instance-factories/tools/templates/vscode/vscode-extension-generator.ts"
|
|
35
|
+
outputPattern: "tools/vscode-extension/"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Validation Instance Factory
|
|
2
|
+
|
|
3
|
+
Generates Zod runtime validation schemas from SpecVerse model definitions, with optional JSON Schema output for Fastify integration.
|
|
4
|
+
|
|
5
|
+
## Definition
|
|
6
|
+
|
|
7
|
+
- **`zod.yaml`** -- Single definition (category: `service`, no external dependencies required).
|
|
8
|
+
|
|
9
|
+
## Generator
|
|
10
|
+
|
|
11
|
+
- `templates/zod/validation-generator.ts` -- Wraps `generate-validation.js`. Produces per-model validation files.
|
|
12
|
+
|
|
13
|
+
## Capabilities
|
|
14
|
+
|
|
15
|
+
| Capability | Description |
|
|
16
|
+
|---|---|
|
|
17
|
+
| `validation.runtime` | Zod schemas for runtime type checking |
|
|
18
|
+
| `validation.zod` | Zod-specific schema generation |
|
|
19
|
+
| `validation.jsonschema` | JSON Schema output (for Fastify route validation) |
|
|
20
|
+
|
|
21
|
+
## Output Pattern
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
src/validation/{model}.validation.ts
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Each file contains Zod schema definitions with inferred TypeScript types, plus optional JSON Schema equivalents.
|
|
28
|
+
|
|
29
|
+
## Default Configuration
|
|
30
|
+
|
|
31
|
+
- **Framework**: Zod 3.22+
|
|
32
|
+
- **JSON Schema generation**: Enabled
|
|
33
|
+
- **Strip unknown keys**: Enabled
|
|
34
|
+
- **Abort early**: Disabled (collect all errors)
|
|
35
|
+
|
|
36
|
+
## Status
|
|
37
|
+
|
|
38
|
+
Wrapper implementation. Future TODO to split into dedicated generators for Zod schemas, JSON Schemas, and validation helper functions.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: ZodValidation
|
|
2
|
+
version: "1.0.0"
|
|
3
|
+
category: service
|
|
4
|
+
description: "Zod schema generator for runtime validation with type inference"
|
|
5
|
+
|
|
6
|
+
metadata:
|
|
7
|
+
author: "SpecVerse Team"
|
|
8
|
+
license: "MIT"
|
|
9
|
+
tags: [zod, validation, typescript, schema]
|
|
10
|
+
|
|
11
|
+
compatibility:
|
|
12
|
+
specverse: ">=3.3.0"
|
|
13
|
+
node: ">=18.0.0"
|
|
14
|
+
|
|
15
|
+
capabilities:
|
|
16
|
+
provides:
|
|
17
|
+
- "validation.runtime"
|
|
18
|
+
- "validation.zod"
|
|
19
|
+
- "validation.jsonschema"
|
|
20
|
+
requires: [] # Fixed: removed incorrect api.rest dependency
|
|
21
|
+
|
|
22
|
+
technology:
|
|
23
|
+
runtime: "node"
|
|
24
|
+
language: "typescript"
|
|
25
|
+
framework: "zod"
|
|
26
|
+
validation: "zod"
|
|
27
|
+
|
|
28
|
+
dependencies:
|
|
29
|
+
runtime:
|
|
30
|
+
- name: "zod"
|
|
31
|
+
version: "^3.22.0"
|
|
32
|
+
dev:
|
|
33
|
+
- name: "@types/node"
|
|
34
|
+
version: "^20.8.0"
|
|
35
|
+
- name: "typescript"
|
|
36
|
+
version: "^5.2.0"
|
|
37
|
+
|
|
38
|
+
codeTemplates:
|
|
39
|
+
validation:
|
|
40
|
+
engine: typescript
|
|
41
|
+
generator: "libs/instance-factories/validation/templates/zod/validation-generator.ts"
|
|
42
|
+
outputPattern: "src/validation/{model}.validation.ts"
|
|
43
|
+
|
|
44
|
+
configuration:
|
|
45
|
+
framework: "zod"
|
|
46
|
+
generateJsonSchema: true
|
|
47
|
+
stripUnknown: true
|
|
48
|
+
abortEarly: false
|
|
49
|
+
|
|
50
|
+
# NOTE: This implementation type wraps the existing generate-validation.js script
|
|
51
|
+
# TODO: Future enhancement - Convert generate-validation.js to native TypeScript template generator
|
|
52
|
+
# The current script generates Zod schemas, JSON schemas, and validation functions.
|
|
53
|
+
# Converting to native templates would require breaking it into multiple template files:
|
|
54
|
+
# - zod-schema-generator.ts (Zod schema definitions)
|
|
55
|
+
# - json-schema-generator.ts (JSON Schema for Fastify)
|
|
56
|
+
# - validation-function-generator.ts (validation helper functions)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-generator.d.ts","sourceRoot":"","sources":["../../../src/realize/engines/code-generator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,sBAAsB,EAAyB,MAAM,sBAAsB,CAAC;AACrF,OAAO,KAAK,EACV,sBAAsB,EACtB,eAAe,EAEhB,MAAM,mBAAmB,CAAC;AAE3B;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IAEb,uBAAuB;IACvB,QAAQ,EAAE,MAAM,CAAC;IAEjB,6BAA6B;IAC7B,YAAY,EAAE,MAAM,CAAC;IAErB,qCAAqC;IACrC,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,oDAAoD;IACpD,cAAc,CAAC,EAAE,sBAAsB,CAAC;IAExC,oDAAoD;IACpD,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,kCAAkC;IAClC,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACzC;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,SAAS,CAAS;gBAEd,OAAO,GAAE,qBAA0B;IAK/C;;OAEG;IACG,oBAAoB,CACxB,QAAQ,EAAE,sBAAsB,EAChC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,EACjC,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"code-generator.d.ts","sourceRoot":"","sources":["../../../src/realize/engines/code-generator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,sBAAsB,EAAyB,MAAM,sBAAsB,CAAC;AACrF,OAAO,KAAK,EACV,sBAAsB,EACtB,eAAe,EAEhB,MAAM,mBAAmB,CAAC;AAE3B;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IAEb,uBAAuB;IACvB,QAAQ,EAAE,MAAM,CAAC;IAEjB,6BAA6B;IAC7B,YAAY,EAAE,MAAM,CAAC;IAErB,qCAAqC;IACrC,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,oDAAoD;IACpD,cAAc,CAAC,EAAE,sBAAsB,CAAC;IAExC,oDAAoD;IACpD,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,kCAAkC;IAClC,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACzC;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,SAAS,CAAS;gBAEd,OAAO,GAAE,qBAA0B;IAK/C;;OAEG;IACG,oBAAoB,CACxB,QAAQ,EAAE,sBAAsB,EAChC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,EACjC,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,eAAe,CAAC;IA+C3B;;OAEG;IACG,WAAW,CACf,QAAQ,EAAE,sBAAsB,EAChC,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,EACjC,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,eAAe,EAAE,CAAC;IAwB7B;;OAEG;IACG,qBAAqB,CACzB,QAAQ,EAAE,sBAAsB,EAAE,EAClC,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,EACjC,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,eAAe,EAAE,CAAC,CAAC;IAW1C;;;;;;;;;OASG;IACH,OAAO,CAAC,iBAAiB;IAwCzB;;OAEG;IACH,OAAO,CAAC,eAAe;IAevB;;OAEG;IACH,iBAAiB,IAAI,sBAAsB;IAI3C;;OAEG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAI/B;;OAEG;IACH,YAAY,IAAI,MAAM;CAGvB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,aAAa,CAElF"}
|
|
@@ -37,6 +37,9 @@ export class CodeGenerator {
|
|
|
37
37
|
workspaceRoot: process.cwd(),
|
|
38
38
|
backendDir: 'backend',
|
|
39
39
|
frontendDir: 'frontend',
|
|
40
|
+
// Realize-time output root, so multi-file generators (orchestrators
|
|
41
|
+
// that write their own files) know where the project is on disk.
|
|
42
|
+
outputDir: options.outputDir,
|
|
40
43
|
...(resolved.configuration || {}),
|
|
41
44
|
...(options.additionalContext || {}),
|
|
42
45
|
...context
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-generator.js","sourceRoot":"","sources":["../../../src/realize/engines/code-generator.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"code-generator.js","sourceRoot":"","sources":["../../../src/realize/engines/code-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,9 +18,9 @@ capabilities:
|
|
|
18
18
|
- "app.frontend"
|
|
19
19
|
- "ui.scaffolding"
|
|
20
20
|
- "ui.router"
|
|
21
|
-
- "ui.
|
|
22
|
-
- "ui.
|
|
23
|
-
- "ui.
|
|
21
|
+
- "ui.staticviews"
|
|
22
|
+
- "ui.staticforms"
|
|
23
|
+
- "ui.staticcomponents"
|
|
24
24
|
requires: []
|
|
25
25
|
|
|
26
26
|
technology:
|
|
@@ -99,21 +99,19 @@ codeTemplates:
|
|
|
99
99
|
generator: "libs/instance-factories/applications/templates/react/env-example-generator.ts"
|
|
100
100
|
outputPattern: "{frontendDir}/.env.example"
|
|
101
101
|
|
|
102
|
-
# API wiring —
|
|
103
|
-
#
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
outputPattern: "{frontendDir}/src/lib/apiClient.ts"
|
|
108
|
-
|
|
102
|
+
# API wiring — starter-specific. The starter's hooks inline their
|
|
103
|
+
# own fetch transport so there is NO apiClient.ts (the runtime
|
|
104
|
+
# apiClient imports runtime-only types that this factory doesn't
|
|
105
|
+
# emit). Users wanting a shared transport layer can add one by
|
|
106
|
+
# hand; the hooks file is intentionally small enough to edit.
|
|
109
107
|
use-api-hooks:
|
|
110
108
|
engine: typescript
|
|
111
|
-
generator: "libs/instance-factories/applications/templates/react/use-api-hooks-generator.ts"
|
|
109
|
+
generator: "libs/instance-factories/applications/templates/react-starter/use-api-hooks-starter-generator.ts"
|
|
112
110
|
outputPattern: "{frontendDir}/src/hooks/useApi.ts"
|
|
113
111
|
|
|
114
112
|
api-types:
|
|
115
113
|
engine: typescript
|
|
116
|
-
generator: "libs/instance-factories/applications/templates/react/api-types-generator.ts"
|
|
114
|
+
generator: "libs/instance-factories/applications/templates/react-starter/api-types-starter-generator.ts"
|
|
117
115
|
outputPattern: "{frontendDir}/src/types/api.ts"
|
|
118
116
|
|
|
119
117
|
# === Starter-specific: emitted view components + helpers + App + package.json ===
|
|
@@ -143,8 +141,3 @@ configuration:
|
|
|
143
141
|
host: "localhost"
|
|
144
142
|
proxy:
|
|
145
143
|
"/api": "http://localhost:3000"
|
|
146
|
-
|
|
147
|
-
notes:
|
|
148
|
-
- "Generated code is a starter — user edits are preserved across regeneration via content-hashing in .specverse-gen/hashes.json."
|
|
149
|
-
- "To accept an upstream regeneration for a file you've edited: delete the file first, then re-run `spv realize`."
|
|
150
|
-
- "No @specverse/runtime dep — the generated project is fully forkable; delete the .specverse-gen directory and rely only on the emitted source."
|
|
@@ -11,8 +11,30 @@ export default function generateEnvExample(context: TemplateContext): string {
|
|
|
11
11
|
const pathConfig = getPathConfig(context);
|
|
12
12
|
const defaultApiUrl = getApiBaseUrl(pathConfig);
|
|
13
13
|
|
|
14
|
-
return `#
|
|
15
|
-
#
|
|
14
|
+
return `# ─── Ports ────────────────────────────────────────────────────
|
|
15
|
+
# Running more than one SpecVerse project at once? Each project reads
|
|
16
|
+
# its own .env — copy this file to .env and bump the numbers so the
|
|
17
|
+
# projects don't collide.
|
|
18
|
+
#
|
|
19
|
+
# Suggested ranges (shared across the ecosystem):
|
|
20
|
+
# 3000-3049 : generated project backends (one per project)
|
|
21
|
+
# 3050-3099 : app-demo per-spec runtime backends
|
|
22
|
+
# 5173-5199 : vite frontends (one per project)
|
|
23
|
+
# 9000+ : app-demo Server Manager
|
|
24
|
+
|
|
25
|
+
# Backend (Fastify) port — read by \`npm run dev:backend\`.
|
|
26
|
+
PORT=3000
|
|
27
|
+
|
|
28
|
+
# Frontend (vite) port — read by \`npm run dev:frontend\`.
|
|
29
|
+
VITE_PORT=5173
|
|
30
|
+
|
|
31
|
+
# Frontend host — '0.0.0.0' if you want LAN access.
|
|
32
|
+
VITE_HOST=localhost
|
|
33
|
+
|
|
34
|
+
# ─── API wiring ────────────────────────────────────────────────
|
|
35
|
+
# Base URL for backend API. Used by the generated apiClient AND by
|
|
36
|
+
# vite's dev-server proxy (so /api and /ws requests from the browser
|
|
37
|
+
# are forwarded to your backend). Keep this in sync with PORT.
|
|
16
38
|
VITE_API_BASE_URL=${defaultApiUrl}
|
|
17
39
|
|
|
18
40
|
# API Path Prefix
|
|
@@ -16,45 +16,66 @@ export default function generateViteConfig(context: TemplateContext): string {
|
|
|
16
16
|
const apiBaseUrl = getApiBaseUrl(pathConfig);
|
|
17
17
|
const apiProxy = configuration?.vite?.proxy?.['/api'] || apiBaseUrl;
|
|
18
18
|
|
|
19
|
-
return `import { defineConfig } from 'vite';
|
|
19
|
+
return `import { defineConfig, loadEnv } from 'vite';
|
|
20
20
|
import react from '@vitejs/plugin-react';
|
|
21
21
|
import path from 'path';
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Ports and proxy targets are env-overridable via this project's
|
|
25
|
+
* .env (and .env.local). Lets you run several SpecVerse projects
|
|
26
|
+
* side-by-side without port collisions:
|
|
27
|
+
*
|
|
28
|
+
* # project-a/.env
|
|
29
|
+
* PORT=3001
|
|
30
|
+
* VITE_PORT=5174
|
|
31
|
+
* VITE_API_BASE_URL=http://localhost:3001
|
|
32
|
+
*
|
|
33
|
+
* # project-b/.env
|
|
34
|
+
* PORT=3002
|
|
35
|
+
* VITE_PORT=5175
|
|
36
|
+
* VITE_API_BASE_URL=http://localhost:3002
|
|
37
|
+
*/
|
|
38
|
+
export default defineConfig(({ mode }) => {
|
|
39
|
+
const env = loadEnv(mode, process.cwd(), '');
|
|
40
|
+
const port = Number(env.VITE_PORT ?? ${vitePort});
|
|
41
|
+
const host = env.VITE_HOST ?? '${viteHost}';
|
|
42
|
+
const apiTarget = env.VITE_API_BASE_URL ?? '${apiProxy}';
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
plugins: [react()],
|
|
46
|
+
server: {
|
|
47
|
+
port,
|
|
48
|
+
host,
|
|
49
|
+
proxy: {
|
|
50
|
+
'/api': {
|
|
51
|
+
target: apiTarget,
|
|
52
|
+
changeOrigin: true,
|
|
53
|
+
},
|
|
54
|
+
// WebSocket proxy — the runtime's useEntitySync hook opens
|
|
55
|
+
// ws://<host>/ws to receive entity mutation events from the
|
|
56
|
+
// backend event bus. Without this proxy the socket tries to
|
|
57
|
+
// connect to the vite dev server on /ws and fails, forcing
|
|
58
|
+
// state-sync to fall back to React Query's refetchInterval
|
|
59
|
+
// polling (usable but slow).
|
|
60
|
+
'/ws': {
|
|
61
|
+
target: apiTarget.replace(/^http/, 'ws'),
|
|
62
|
+
ws: true,
|
|
63
|
+
changeOrigin: true,
|
|
64
|
+
},
|
|
44
65
|
},
|
|
45
66
|
},
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
67
|
+
build: {
|
|
68
|
+
outDir: 'dist',
|
|
69
|
+
sourcemap: true,
|
|
70
|
+
},
|
|
71
|
+
resolve: {
|
|
72
|
+
alias: {
|
|
73
|
+
'@': path.resolve(__dirname, './src'),
|
|
74
|
+
},
|
|
75
|
+
// Prevent React duplication when using local packages
|
|
76
|
+
dedupe: ['react', 'react-dom'],
|
|
54
77
|
},
|
|
55
|
-
|
|
56
|
-
dedupe: ['react', 'react-dom'],
|
|
57
|
-
},
|
|
78
|
+
};
|
|
58
79
|
});
|
|
59
80
|
`;
|
|
60
81
|
}
|
|
@@ -57,10 +57,11 @@ describe('composeDetailBody — direct output', () => {
|
|
|
57
57
|
expect(body).toContain('>Title<');
|
|
58
58
|
expect(body).toContain('>Body<');
|
|
59
59
|
|
|
60
|
-
// belongsTo section — surfaces
|
|
60
|
+
// belongsTo section — surfaces author as a resolved name via
|
|
61
|
+
// resolveEntityDisplayName against the authorOptions array wired
|
|
62
|
+
// in by view-emitter's RELATED_HOOKS substitution.
|
|
61
63
|
expect(body).toContain('>Author<');
|
|
62
|
-
expect(body).toContain('.authorId
|
|
63
|
-
expect(body).toContain('TODO: resolve FK ids');
|
|
64
|
+
expect(body).toContain('resolveEntityDisplayName((item as any).authorId, authorOptions)');
|
|
64
65
|
|
|
65
66
|
// Metadata section (id / createdAt / publishedAt) — muted styling
|
|
66
67
|
expect(body).toContain('>Id<');
|
|
@@ -113,7 +114,7 @@ describe('composeDetailBody — direct output', () => {
|
|
|
113
114
|
viewName: 'SoloDetailView',
|
|
114
115
|
modelSchemas: { Solo: solo },
|
|
115
116
|
}));
|
|
116
|
-
expect(body).not.toContain('
|
|
117
|
+
expect(body).not.toContain('resolveEntityDisplayName');
|
|
117
118
|
expect(body).toContain('>Name<');
|
|
118
119
|
});
|
|
119
120
|
});
|
|
@@ -15,6 +15,7 @@ function makeContext(overrides: Partial<EmitContext> = {}): EmitContext {
|
|
|
15
15
|
email: { type: 'Email', required: false },
|
|
16
16
|
status: { type: 'String', required: false, values: ['draft', 'live', 'archived'] },
|
|
17
17
|
authorId: { type: 'UUID', required: true }, // belongsTo FK
|
|
18
|
+
externalId: { type: 'UUID', required: false }, // non-FK UUID
|
|
18
19
|
createdAt: { type: 'DateTime', required: false }, // metadata
|
|
19
20
|
},
|
|
20
21
|
relationships: {
|
|
@@ -121,8 +122,11 @@ describe('composeFormBody — input type per attribute', () => {
|
|
|
121
122
|
});
|
|
122
123
|
|
|
123
124
|
it('UUID → text input (treated like string)', () => {
|
|
125
|
+
// externalId is a plain UUID attribute — no belongsTo relationship
|
|
126
|
+
// shadows it, so it renders as a text input. (FK UUIDs like
|
|
127
|
+
// authorId render as a <select> instead; see the belongsTo suite.)
|
|
124
128
|
const body = composeFormBody(makeContext());
|
|
125
|
-
expect(body).toMatch(/id="
|
|
129
|
+
expect(body).toMatch(/id="externalId"[\s\S]*type="text"/);
|
|
126
130
|
});
|
|
127
131
|
});
|
|
128
132
|
|
|
@@ -145,11 +149,20 @@ describe('composeFormBody — required markers', () => {
|
|
|
145
149
|
});
|
|
146
150
|
|
|
147
151
|
describe('composeFormBody — belongsTo section', () => {
|
|
148
|
-
it('
|
|
152
|
+
it('emits a <select> for each belongsTo, iterating ${relName}Options', () => {
|
|
149
153
|
const body = composeFormBody(makeContext());
|
|
150
|
-
|
|
151
|
-
//
|
|
152
|
-
expect(body).toMatch(
|
|
154
|
+
// Author belongsTo User — author is the relationship name, so the
|
|
155
|
+
// options array is authorOptions and the FK column is authorId.
|
|
156
|
+
expect(body).toMatch(/<select[\s\S]*?id="authorId"/);
|
|
157
|
+
expect(body).toContain('authorOptions.map');
|
|
158
|
+
expect(body).toContain('getEntityDisplayName(opt)');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('does not emit plain text inputs for FK columns shadowed by a belongsTo', () => {
|
|
162
|
+
// authorId is shadowed by the author belongsTo relationship — it
|
|
163
|
+
// should only render once (as the <select>), never as a text input.
|
|
164
|
+
const body = composeFormBody(makeContext());
|
|
165
|
+
expect(body).not.toMatch(/<input[\s\S]*?id="authorId"[\s\S]*?type="text"/);
|
|
153
166
|
});
|
|
154
167
|
});
|
|
155
168
|
|
package/libs/instance-factories/applications/templates/react-starter/__tests__/orchestrator.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
-
import { mkdtempSync, readFileSync, rmSync, writeFileSync,
|
|
2
|
+
import { mkdtempSync, readFileSync, rmSync, writeFileSync, existsSync } from 'fs';
|
|
3
3
|
import { tmpdir } from 'os';
|
|
4
4
|
import { join } from 'path';
|
|
5
5
|
import * as ts from 'typescript';
|
|
@@ -55,22 +55,36 @@ function assertValidTsx(source: string, label: string): void {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
58
|
+
* Drive the orchestrator with the per-test tmp dir as the effective
|
|
59
|
+
* frontend root. `frontendDir='.'` collapses the
|
|
60
|
+
* `${outputDir}/${frontendDir}` join so files land directly under
|
|
61
|
+
* `projectRoot`, which keeps test assertions terse.
|
|
60
62
|
*/
|
|
61
|
-
function
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
async function runGenerate(spec = makeSpec()) {
|
|
64
|
+
return generate({ spec, outputDir: projectRoot, frontendDir: '.' });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** List every file the orchestrator wrote under projectRoot. */
|
|
68
|
+
function walk(dir: string, prefix = ''): string[] {
|
|
69
|
+
const { readdirSync, statSync } = require('fs');
|
|
70
|
+
const out: string[] = [];
|
|
71
|
+
for (const entry of readdirSync(dir)) {
|
|
72
|
+
const abs = join(dir, entry);
|
|
73
|
+
const rel = prefix ? `${prefix}/${entry}` : entry;
|
|
74
|
+
if (statSync(abs).isDirectory()) out.push(...walk(abs, rel));
|
|
75
|
+
else out.push(rel);
|
|
66
76
|
}
|
|
77
|
+
return out.sort();
|
|
67
78
|
}
|
|
68
79
|
|
|
69
80
|
describe('orchestrator — first-run (empty project)', () => {
|
|
70
|
-
it('
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
expect(
|
|
81
|
+
it('writes the full set of files: views + helpers + App.tsx + package.json + hash manifest', async () => {
|
|
82
|
+
const ret = await runGenerate();
|
|
83
|
+
// Contract: returns '' (realize's single-file writeOutput skips it).
|
|
84
|
+
expect(ret).toBe('');
|
|
85
|
+
|
|
86
|
+
const written = walk(projectRoot);
|
|
87
|
+
expect(written).toEqual([
|
|
74
88
|
`${HASHES_DIR}/${HASHES_FILE}`,
|
|
75
89
|
'package.json',
|
|
76
90
|
'src/App.tsx',
|
|
@@ -82,82 +96,89 @@ describe('orchestrator — first-run (empty project)', () => {
|
|
|
82
96
|
]);
|
|
83
97
|
});
|
|
84
98
|
|
|
85
|
-
it('does not write to disk (that is realize\'s job)', async () => {
|
|
86
|
-
await generate({ spec: makeSpec(), projectRoot });
|
|
87
|
-
// Orchestrator is pure — nothing on disk yet.
|
|
88
|
-
expect(existsSync(join(projectRoot, 'package.json'))).toBe(false);
|
|
89
|
-
expect(existsSync(join(projectRoot, 'src/App.tsx'))).toBe(false);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
99
|
it('every emitted .tsx parses as valid TSX', async () => {
|
|
93
|
-
|
|
94
|
-
for (const
|
|
95
|
-
if (path.endsWith('.tsx'))
|
|
100
|
+
await runGenerate();
|
|
101
|
+
for (const path of walk(projectRoot)) {
|
|
102
|
+
if (path.endsWith('.tsx')) {
|
|
103
|
+
const source = readFileSync(join(projectRoot, path), 'utf8');
|
|
104
|
+
assertValidTsx(source, path);
|
|
105
|
+
}
|
|
96
106
|
}
|
|
97
107
|
});
|
|
98
108
|
|
|
99
109
|
it('package.json has no @specverse/runtime dependency', async () => {
|
|
100
|
-
|
|
101
|
-
const pkg = JSON.parse(
|
|
110
|
+
await runGenerate();
|
|
111
|
+
const pkg = JSON.parse(readFileSync(join(projectRoot, 'package.json'), 'utf8'));
|
|
102
112
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
103
113
|
expect(deps['@specverse/runtime']).toBeUndefined();
|
|
104
114
|
});
|
|
105
115
|
|
|
106
116
|
it('the emitted hash manifest records every approved file', async () => {
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
const manifest = JSON.parse(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
expect(manifest[path]).toBe(sha256(
|
|
117
|
+
await runGenerate();
|
|
118
|
+
const manifestPath = join(projectRoot, HASHES_DIR, HASHES_FILE);
|
|
119
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
120
|
+
for (const path of walk(projectRoot)) {
|
|
121
|
+
if (path === `${HASHES_DIR}/${HASHES_FILE}`) continue;
|
|
122
|
+
const content = readFileSync(join(projectRoot, path), 'utf8');
|
|
123
|
+
expect(manifest[path]).toBe(sha256(content));
|
|
114
124
|
}
|
|
115
125
|
});
|
|
116
126
|
});
|
|
117
127
|
|
|
118
128
|
describe('orchestrator — regeneration safety', () => {
|
|
119
129
|
it('re-approves pristine files on a second run', async () => {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
130
|
+
await runGenerate();
|
|
131
|
+
|
|
132
|
+
// Record the first-run hash of one of the view files.
|
|
133
|
+
const listPath = 'src/views/PostListView.tsx';
|
|
134
|
+
const listAbs = join(projectRoot, listPath);
|
|
135
|
+
const firstContent = readFileSync(listAbs, 'utf8');
|
|
136
|
+
|
|
137
|
+
// Second run. Pristine files should be rewritten (the orchestrator
|
|
138
|
+
// writes them, possibly with identical bytes).
|
|
139
|
+
await runGenerate();
|
|
140
|
+
expect(existsSync(listAbs)).toBe(true);
|
|
141
|
+
expect(readFileSync(listAbs, 'utf8')).toBe(firstContent);
|
|
128
142
|
});
|
|
129
143
|
|
|
130
|
-
it('
|
|
131
|
-
|
|
132
|
-
applyOutput(firstOutput, projectRoot);
|
|
144
|
+
it('preserves a user-edited file across regeneration', async () => {
|
|
145
|
+
await runGenerate();
|
|
133
146
|
|
|
134
|
-
// User edits one of the files
|
|
135
147
|
const editedPath = 'src/views/PostListView.tsx';
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
//
|
|
143
|
-
expect(
|
|
144
|
-
// Manifest
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
+
const editedAbs = join(projectRoot, editedPath);
|
|
149
|
+
const userEdit = '/* user edit */';
|
|
150
|
+
writeFileSync(editedAbs, userEdit, 'utf8');
|
|
151
|
+
|
|
152
|
+
await runGenerate();
|
|
153
|
+
|
|
154
|
+
// File on disk is unchanged — user edit survived.
|
|
155
|
+
expect(readFileSync(editedAbs, 'utf8')).toBe(userEdit);
|
|
156
|
+
// Manifest still records the ORIGINAL (pre-edit) hash so a future
|
|
157
|
+
// un-edit can re-sync cleanly.
|
|
158
|
+
const manifest = JSON.parse(
|
|
159
|
+
readFileSync(join(projectRoot, HASHES_DIR, HASHES_FILE), 'utf8')
|
|
160
|
+
);
|
|
161
|
+
expect(manifest[editedPath]).toBeDefined();
|
|
162
|
+
expect(manifest[editedPath]).not.toBe(sha256(userEdit));
|
|
148
163
|
});
|
|
149
164
|
|
|
150
165
|
it('is cautious when the user deletes the hash manifest', async () => {
|
|
151
|
-
|
|
152
|
-
applyOutput(firstOutput, projectRoot);
|
|
166
|
+
await runGenerate();
|
|
153
167
|
|
|
154
|
-
// User deletes the hash manifest
|
|
168
|
+
// User deletes the hash manifest but keeps the view files.
|
|
155
169
|
rmSync(join(projectRoot, HASHES_DIR), { recursive: true, force: true });
|
|
156
170
|
|
|
157
|
-
|
|
171
|
+
// Record the existing file contents (may be identical to what the
|
|
172
|
+
// orchestrator would emit, but we're treating them as unknown origin).
|
|
173
|
+
const listPath = join(projectRoot, 'src/views/PostListView.tsx');
|
|
174
|
+
const before = readFileSync(listPath, 'utf8');
|
|
175
|
+
|
|
176
|
+
await runGenerate();
|
|
158
177
|
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
expect(
|
|
178
|
+
// No hash manifest means we can't tell user edits from originals, so
|
|
179
|
+
// existing files are skipped. Content is unchanged.
|
|
180
|
+
expect(readFileSync(listPath, 'utf8')).toBe(before);
|
|
181
|
+
// A fresh hash manifest was written.
|
|
182
|
+
expect(existsSync(join(projectRoot, HASHES_DIR, HASHES_FILE))).toBe(true);
|
|
162
183
|
});
|
|
163
184
|
});
|