@specverse/engines 4.1.30 → 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/examples/manifests/frontend-only.yaml +3 -6
- package/assets/examples/manifests/fullstack-app.yaml +5 -7
- package/assets/examples/manifests/fullstack-monorepo.yaml +3 -6
- package/assets/templates/default/specs/main.specly +65 -0
- package/dist/inference/comprehensive-engine.d.ts.map +1 -1
- package/dist/inference/comprehensive-engine.js +3 -19
- package/dist/inference/comprehensive-engine.js.map +1 -1
- package/dist/inference/core/rule-engine.d.ts +31 -0
- package/dist/inference/core/rule-engine.d.ts.map +1 -1
- package/dist/inference/core/rule-engine.js +117 -33
- package/dist/inference/core/rule-engine.js.map +1 -1
- package/dist/inference/core/rule-file-types.d.ts +0 -2
- package/dist/inference/core/rule-file-types.d.ts.map +1 -1
- package/dist/inference/core/rule-file-types.js +3 -6
- package/dist/inference/core/rule-file-types.js.map +1 -1
- package/dist/inference/core/rule-loader.d.ts +5 -15
- package/dist/inference/core/rule-loader.d.ts.map +1 -1
- package/dist/inference/core/rule-loader.js +43 -132
- package/dist/inference/core/rule-loader.js.map +1 -1
- package/dist/inference/core/types.d.ts +0 -6
- package/dist/inference/core/types.d.ts.map +1 -1
- package/dist/inference/core/types.js +0 -4
- package/dist/inference/core/types.js.map +1 -1
- package/dist/inference/logical/generators/component-type-resolver.d.ts +0 -26
- package/dist/inference/logical/generators/component-type-resolver.d.ts.map +1 -1
- package/dist/inference/logical/generators/component-type-resolver.js +0 -19
- package/dist/inference/logical/generators/component-type-resolver.js.map +1 -1
- package/dist/inference/logical/generators/specialist-view-expander.d.ts +1 -17
- package/dist/inference/logical/generators/specialist-view-expander.d.ts.map +1 -1
- package/dist/inference/logical/generators/specialist-view-expander.js +0 -15
- package/dist/inference/logical/generators/specialist-view-expander.js.map +1 -1
- package/dist/inference/logical/generators/view-generator.d.ts +4 -14
- package/dist/inference/logical/generators/view-generator.d.ts.map +1 -1
- package/dist/inference/logical/generators/view-generator.js +6 -26
- package/dist/inference/logical/generators/view-generator.js.map +1 -1
- package/dist/inference/logical/index.d.ts +2 -2
- package/dist/inference/logical/index.d.ts.map +1 -1
- package/dist/inference/logical/logical-engine.d.ts.map +1 -1
- package/dist/inference/logical/logical-engine.js +17 -80
- package/dist/inference/logical/logical-engine.js.map +1 -1
- package/dist/inference/quint-transpiler.d.ts +5 -3
- package/dist/inference/quint-transpiler.d.ts.map +1 -1
- package/dist/inference/quint-transpiler.js +11 -6
- package/dist/inference/quint-transpiler.js.map +1 -1
- 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/{libs/instance-factories/applications/react-app.yaml → dist/libs/instance-factories/applications/react-app-runtime.yaml} +30 -77
- 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 +110 -0
- 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 +129 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/detail-body-composer.js +80 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/form-body-composer.js +217 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/helpers-emitter.js +51 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/html-to-jsx.js +192 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/list-body-composer.js +56 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/orchestrator.js +41 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/package-json-generator.js +38 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/regen-safety.js +89 -0
- 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 +81 -0
- package/dist/libs/instance-factories/applications/templates/react-starter/views-generator.js +66 -0
- 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 +63 -12
- 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/dist/realize/index.d.ts.map +1 -1
- package/dist/realize/index.js +15 -22
- package/dist/realize/index.js.map +1 -1
- package/dist/registry/utils/manifest-adapter.d.ts +8 -1
- package/dist/registry/utils/manifest-adapter.d.ts.map +1 -1
- package/dist/registry/utils/manifest-adapter.js +8 -1
- package/dist/registry/utils/manifest-adapter.js.map +1 -1
- package/libs/instance-factories/applications/react-app-starter.yaml +143 -0
- 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/README.md +211 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/dashboard-body-composer.test.ts +153 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/detail-body-composer.test.ts +146 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/form-body-composer.test.ts +188 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/helpers-emitter.test.ts +55 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/html-to-jsx.test.ts +140 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/list-body-composer.test.ts +146 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/orchestrator.test.ts +184 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/parity-p2-factory-imports.test.ts +116 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/parity-p3-rendered-output.test.ts +183 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/regen-safety.test.ts +144 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/starter-generators.test.ts +114 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/view-emitter.test.ts +107 -0
- package/libs/instance-factories/applications/templates/react-starter/__tests__/views-generator.test.ts +139 -0
- 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 +141 -0
- 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 +189 -0
- package/libs/instance-factories/applications/templates/react-starter/detail-body-composer.ts +135 -0
- package/libs/instance-factories/applications/templates/react-starter/form-body-composer.ts +383 -0
- package/libs/instance-factories/applications/templates/react-starter/helpers-emitter.ts +66 -0
- package/libs/instance-factories/applications/templates/react-starter/html-to-jsx.ts +334 -0
- package/libs/instance-factories/applications/templates/react-starter/list-body-composer.ts +146 -0
- package/libs/instance-factories/applications/templates/react-starter/orchestrator.ts +95 -0
- package/libs/instance-factories/applications/templates/react-starter/package-json-generator.ts +57 -0
- package/libs/instance-factories/applications/templates/react-starter/regen-safety.ts +157 -0
- package/libs/instance-factories/applications/templates/react-starter/skeletons/dashboard.tsx.template +49 -0
- package/libs/instance-factories/applications/templates/react-starter/skeletons/detail.tsx.template +96 -0
- package/libs/instance-factories/applications/templates/react-starter/skeletons/form.tsx.template +116 -0
- package/libs/instance-factories/applications/templates/react-starter/skeletons/list.tsx.template +74 -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 +209 -0
- package/libs/instance-factories/applications/templates/react-starter/views-generator.ts +137 -0
- package/libs/instance-factories/cli/templates/commander/command-generator.ts +63 -12
- 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 +3 -3
- package/dist/libs/instance-factories/applications/templates/react/_view-components-source.js +0 -530
- package/dist/libs/instance-factories/applications/templates/react/app-tsx-generator.js +0 -73
- package/dist/libs/instance-factories/applications/templates/react/field-helpers-generator.js +0 -99
- package/dist/libs/instance-factories/applications/templates/react/package-json-generator.js +0 -49
- package/dist/libs/instance-factories/applications/templates/react/pattern-adapter-generator.js +0 -156
- package/dist/libs/instance-factories/applications/templates/react/react-pattern-adapter.js +0 -935
- package/dist/libs/instance-factories/applications/templates/react/relationship-field-generator.js +0 -143
- package/dist/libs/instance-factories/applications/templates/react/tailwind-adapter-generator.js +0 -646
- package/dist/libs/instance-factories/applications/templates/react/tailwind-adapter-wrapper-generator.js +0 -65
- package/dist/libs/instance-factories/applications/templates/react/view-dashboard-generator.js +0 -143
- package/dist/libs/instance-factories/applications/templates/react/view-detail-generator.js +0 -143
- package/dist/libs/instance-factories/applications/templates/react/view-form-generator.js +0 -355
- package/dist/libs/instance-factories/applications/templates/react/view-list-generator.js +0 -91
- package/dist/libs/instance-factories/applications/templates/react/view-router-generator.js +0 -79
- package/dist/libs/instance-factories/views/index.js +0 -48
- package/dist/libs/instance-factories/views/templates/react/adapters/antd-adapter.js +0 -742
- package/dist/libs/instance-factories/views/templates/react/adapters/mui-adapter.js +0 -824
- package/dist/libs/instance-factories/views/templates/react/adapters/shadcn-adapter.js +0 -719
- package/dist/libs/instance-factories/views/templates/react/app-generator.js +0 -45
- package/dist/libs/instance-factories/views/templates/react/components-generator.js +0 -820
- package/dist/libs/instance-factories/views/templates/react/forms-generator.js +0 -275
- package/dist/libs/instance-factories/views/templates/react/frontend-package-json-generator.js +0 -46
- package/dist/libs/instance-factories/views/templates/react/hooks-generator.js +0 -81
- package/dist/libs/instance-factories/views/templates/react/index-css-generator.js +0 -9
- package/dist/libs/instance-factories/views/templates/react/index-html-generator.js +0 -23
- package/dist/libs/instance-factories/views/templates/react/main-tsx-generator.js +0 -21
- package/dist/libs/instance-factories/views/templates/react/react-component-generator.js +0 -299
- package/dist/libs/instance-factories/views/templates/react/router-generator.js +0 -136
- package/dist/libs/instance-factories/views/templates/react/router-generic-generator.js +0 -107
- package/dist/libs/instance-factories/views/templates/react/shared-utils-generator.js +0 -187
- package/dist/libs/instance-factories/views/templates/react/spec-json-generator.js +0 -7
- package/dist/libs/instance-factories/views/templates/react/types-generator.js +0 -56
- package/dist/libs/instance-factories/views/templates/react/views-metadata-generator.js +0 -27
- package/dist/libs/instance-factories/views/templates/react/vite-config-generator.js +0 -29
- package/dist/libs/instance-factories/views/templates/runtime/runtime-view-renderer.js +0 -261
- package/dist/libs/instance-factories/views/templates/shared/adapter-types.js +0 -34
- package/dist/libs/instance-factories/views/templates/shared/atomic-components-registry.js +0 -800
- package/dist/libs/instance-factories/views/templates/shared/base-generator.js +0 -305
- package/dist/libs/instance-factories/views/templates/shared/component-metadata.js +0 -517
- package/dist/libs/instance-factories/views/templates/shared/composite-pattern-types.js +0 -0
- package/dist/libs/instance-factories/views/templates/shared/composite-patterns.js +0 -445
- package/dist/libs/instance-factories/views/templates/shared/index.js +0 -80
- package/dist/libs/instance-factories/views/templates/shared/pattern-validator.js +0 -210
- package/dist/libs/instance-factories/views/templates/shared/property-mapper.js +0 -492
- package/dist/libs/instance-factories/views/templates/shared/syntax-mapper.js +0 -321
- package/dist/realize/index.js.bak +0 -758
- package/libs/instance-factories/applications/templates/react/_view-components-source.ts +0 -555
- package/libs/instance-factories/applications/templates/react/app-tsx-generator.ts +0 -94
- package/libs/instance-factories/applications/templates/react/field-helpers-generator.ts +0 -106
- package/libs/instance-factories/applications/templates/react/package-json-generator.ts +0 -57
- package/libs/instance-factories/applications/templates/react/pattern-adapter-generator.ts +0 -179
- package/libs/instance-factories/applications/templates/react/react-pattern-adapter.tsx +0 -1347
- package/libs/instance-factories/applications/templates/react/relationship-field-generator.ts +0 -150
- package/libs/instance-factories/applications/templates/react/tailwind-adapter-generator.ts +0 -704
- package/libs/instance-factories/applications/templates/react/tailwind-adapter-wrapper-generator.ts +0 -84
- package/libs/instance-factories/applications/templates/react/view-dashboard-generator.ts +0 -150
- package/libs/instance-factories/applications/templates/react/view-detail-generator.ts +0 -150
- package/libs/instance-factories/applications/templates/react/view-form-generator.ts +0 -362
- package/libs/instance-factories/applications/templates/react/view-list-generator.ts +0 -98
- package/libs/instance-factories/applications/templates/react/view-router-generator.ts +0 -89
- package/libs/instance-factories/views/README.md +0 -62
- package/libs/instance-factories/views/index.d.ts +0 -13
- package/libs/instance-factories/views/index.d.ts.map +0 -1
- package/libs/instance-factories/views/index.js +0 -18
- package/libs/instance-factories/views/index.js.map +0 -1
- package/libs/instance-factories/views/index.ts +0 -45
- package/libs/instance-factories/views/react-components.yaml +0 -129
- package/libs/instance-factories/views/templates/ARCHITECTURE.md +0 -198
- package/libs/instance-factories/views/templates/react/adapters/antd-adapter.ts +0 -869
- package/libs/instance-factories/views/templates/react/adapters/mui-adapter.ts +0 -953
- package/libs/instance-factories/views/templates/react/adapters/shadcn-adapter.ts +0 -806
- package/libs/instance-factories/views/templates/react/app-generator.ts +0 -55
- package/libs/instance-factories/views/templates/react/components-generator.ts +0 -938
- package/libs/instance-factories/views/templates/react/forms-generator.ts +0 -325
- package/libs/instance-factories/views/templates/react/frontend-package-json-generator.ts +0 -57
- package/libs/instance-factories/views/templates/react/hooks-generator.ts +0 -106
- package/libs/instance-factories/views/templates/react/index-css-generator.ts +0 -14
- package/libs/instance-factories/views/templates/react/index-html-generator.ts +0 -34
- package/libs/instance-factories/views/templates/react/main-tsx-generator.ts +0 -29
- package/libs/instance-factories/views/templates/react/react-component-generator.d.ts +0 -152
- package/libs/instance-factories/views/templates/react/react-component-generator.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/react/react-component-generator.js +0 -398
- package/libs/instance-factories/views/templates/react/react-component-generator.js.map +0 -1
- package/libs/instance-factories/views/templates/react/react-component-generator.ts +0 -533
- package/libs/instance-factories/views/templates/react/router-generator.ts +0 -197
- package/libs/instance-factories/views/templates/react/router-generic-generator.ts +0 -132
- package/libs/instance-factories/views/templates/react/shared-utils-generator.ts +0 -196
- package/libs/instance-factories/views/templates/react/spec-json-generator.ts +0 -17
- package/libs/instance-factories/views/templates/react/types-generator.ts +0 -76
- package/libs/instance-factories/views/templates/react/views-metadata-generator.ts +0 -42
- package/libs/instance-factories/views/templates/react/vite-config-generator.ts +0 -38
- package/libs/instance-factories/views/templates/runtime/runtime-view-renderer.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/runtime/runtime-view-renderer.js.map +0 -1
- package/libs/instance-factories/views/templates/runtime/runtime-view-renderer.ts +0 -474
- package/libs/instance-factories/views/templates/shared/__tests__/composite-patterns.test.ts +0 -242
- package/libs/instance-factories/views/templates/shared/adapter-types.d.ts +0 -77
- package/libs/instance-factories/views/templates/shared/adapter-types.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/shared/adapter-types.js +0 -47
- package/libs/instance-factories/views/templates/shared/adapter-types.js.map +0 -1
- package/libs/instance-factories/views/templates/shared/adapter-types.ts +0 -142
- package/libs/instance-factories/views/templates/shared/atomic-components-registry.d.ts +0 -63
- package/libs/instance-factories/views/templates/shared/atomic-components-registry.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/shared/atomic-components-registry.js +0 -822
- package/libs/instance-factories/views/templates/shared/atomic-components-registry.js.map +0 -1
- package/libs/instance-factories/views/templates/shared/atomic-components-registry.ts +0 -908
- package/libs/instance-factories/views/templates/shared/base-generator.d.ts +0 -247
- package/libs/instance-factories/views/templates/shared/base-generator.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/shared/base-generator.js +0 -363
- package/libs/instance-factories/views/templates/shared/base-generator.js.map +0 -1
- package/libs/instance-factories/views/templates/shared/base-generator.ts +0 -608
- package/libs/instance-factories/views/templates/shared/component-metadata.d.ts +0 -254
- package/libs/instance-factories/views/templates/shared/component-metadata.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/shared/component-metadata.js +0 -602
- package/libs/instance-factories/views/templates/shared/component-metadata.js.map +0 -1
- package/libs/instance-factories/views/templates/shared/component-metadata.ts +0 -803
- package/libs/instance-factories/views/templates/shared/composite-pattern-types.ts +0 -250
- package/libs/instance-factories/views/templates/shared/composite-patterns.ts +0 -535
- package/libs/instance-factories/views/templates/shared/index.ts +0 -68
- package/libs/instance-factories/views/templates/shared/pattern-validator.ts +0 -279
- package/libs/instance-factories/views/templates/shared/property-mapper.d.ts +0 -149
- package/libs/instance-factories/views/templates/shared/property-mapper.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/shared/property-mapper.js +0 -580
- package/libs/instance-factories/views/templates/shared/property-mapper.js.map +0 -1
- package/libs/instance-factories/views/templates/shared/property-mapper.ts +0 -700
- package/libs/instance-factories/views/templates/shared/syntax-mapper.d.ts +0 -143
- package/libs/instance-factories/views/templates/shared/syntax-mapper.d.ts.map +0 -1
- package/libs/instance-factories/views/templates/shared/syntax-mapper.js +0 -420
- package/libs/instance-factories/views/templates/shared/syntax-mapper.js.map +0 -1
- package/libs/instance-factories/views/templates/shared/syntax-mapper.ts +0 -539
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* {{MODEL_NAME}}ListView — generated by @specverse/realize (ReactAppStarter)
|
|
3
|
+
*
|
|
4
|
+
* Safe to edit. Edits are preserved across regeneration via content
|
|
5
|
+
* hashing (see .specverse-gen/hashes.json). To accept an upstream
|
|
6
|
+
* regeneration of this file, delete it first, then run `spv realize`.
|
|
7
|
+
*/
|
|
8
|
+
import { useState, useMemo } from 'react';
|
|
9
|
+
import { use{{PLURAL_MODEL}}Query, useDelete{{MODEL_NAME}}Mutation } from '../hooks/useApi';
|
|
10
|
+
{{RELATED_IMPORTS}}
|
|
11
|
+
import type { {{MODEL_NAME}} } from '../types/api';
|
|
12
|
+
|
|
13
|
+
interface {{MODEL_NAME}}ListViewProps {
|
|
14
|
+
onSelect?: (item: {{MODEL_NAME}}) => void;
|
|
15
|
+
onCreate?: () => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function {{MODEL_NAME}}ListView({ onSelect, onCreate }: {{MODEL_NAME}}ListViewProps) {
|
|
19
|
+
const { data: items = [], isLoading, error } = use{{PLURAL_MODEL}}Query();
|
|
20
|
+
const deleteItem = useDelete{{MODEL_NAME}}Mutation();
|
|
21
|
+
{{RELATED_HOOKS}}
|
|
22
|
+
const [searchTerm, setSearchTerm] = useState('');
|
|
23
|
+
|
|
24
|
+
const filtered = useMemo(
|
|
25
|
+
() =>
|
|
26
|
+
items.filter((item: {{MODEL_NAME}}) =>
|
|
27
|
+
// Cast to `any` inside JSX context — `Record<string, unknown>`
|
|
28
|
+
// looks like a JSX opening tag to the TSX parser when this
|
|
29
|
+
// expression ends up inside JSX. `any` is safe here: we only
|
|
30
|
+
// read values for substring-matching against the search term.
|
|
31
|
+
Object.values(item as any).some(v =>
|
|
32
|
+
String(v ?? '').toLowerCase().includes(searchTerm.toLowerCase())
|
|
33
|
+
)
|
|
34
|
+
),
|
|
35
|
+
[items, searchTerm]
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
if (isLoading) return <div className="p-4 text-gray-500">Loading {{PLURAL_LOWER}}…</div>;
|
|
39
|
+
if (error) return <div className="p-4 text-red-600">Error loading {{PLURAL_LOWER}}: {String(error)}</div>;
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className="p-6 space-y-4">
|
|
43
|
+
<div className="flex items-center justify-between">
|
|
44
|
+
<input
|
|
45
|
+
type="search"
|
|
46
|
+
placeholder="Search {{PLURAL_LOWER}}…"
|
|
47
|
+
value={searchTerm}
|
|
48
|
+
onChange={e => setSearchTerm(e.target.value)}
|
|
49
|
+
className="w-64 rounded border border-gray-300 px-3 py-2 text-sm"
|
|
50
|
+
/>
|
|
51
|
+
<button
|
|
52
|
+
type="button"
|
|
53
|
+
onClick={onCreate}
|
|
54
|
+
className="rounded bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700"
|
|
55
|
+
>
|
|
56
|
+
+ New {{MODEL_NAME}}
|
|
57
|
+
</button>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
{/* Pattern-rendered list body (table + rows). Edit freely. */}
|
|
61
|
+
{{BODY}}
|
|
62
|
+
|
|
63
|
+
{filtered.length === 0 && (
|
|
64
|
+
<div className="p-8 text-center text-gray-400">No {{PLURAL_LOWER}} yet.</div>
|
|
65
|
+
)}
|
|
66
|
+
|
|
67
|
+
{deleteItem.isError && (
|
|
68
|
+
<div className="p-2 text-sm text-red-600">
|
|
69
|
+
Delete failed: {String(deleteItem.error)}
|
|
70
|
+
</div>
|
|
71
|
+
)}
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
function pluralize(s) {
|
|
2
|
+
if (/y$/.test(s) && !/[aeiou]y$/.test(s)) return s.replace(/y$/, "ies");
|
|
3
|
+
if (/(s|x|z|ch|sh)$/.test(s)) return s + "es";
|
|
4
|
+
return s + "s";
|
|
5
|
+
}
|
|
6
|
+
async function generate(context) {
|
|
7
|
+
const models = Object.keys(context.spec.models ?? {});
|
|
8
|
+
const importsAndTypes = `/**
|
|
9
|
+
* useApi \u2014 typed per-model React Query hooks (ReactAppStarter)
|
|
10
|
+
*
|
|
11
|
+
* Safe to edit. Users who want to reshape their API client or add
|
|
12
|
+
* request/response interceptors should start here. Each hook calls
|
|
13
|
+
* the sibling \`apiClient\` which does the actual fetch; edit
|
|
14
|
+
* \`apiClient.ts\` to change transport concerns (headers, auth, etc.).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
18
|
+
${models.map((m) => `import type { ${m} } from '../types/api';`).join("\n")}
|
|
19
|
+
|
|
20
|
+
const API_BASE = import.meta.env.VITE_API_BASE_URL || '';
|
|
21
|
+
|
|
22
|
+
async function fetchJSON<T = unknown>(url: string, init?: RequestInit): Promise<T> {
|
|
23
|
+
const res = await fetch(url, {
|
|
24
|
+
headers: { 'Content-Type': 'application/json' },
|
|
25
|
+
...init,
|
|
26
|
+
});
|
|
27
|
+
if (!res.ok) {
|
|
28
|
+
throw new Error(\`\${res.status} \${res.statusText} \u2014 \${url}\`);
|
|
29
|
+
}
|
|
30
|
+
if (res.status === 204) return undefined as T;
|
|
31
|
+
return (await res.json()) as T;
|
|
32
|
+
}
|
|
33
|
+
`;
|
|
34
|
+
const hookBlocks = models.map((m) => generateModelHooks(m)).join("\n\n");
|
|
35
|
+
return importsAndTypes + "\n" + hookBlocks + "\n";
|
|
36
|
+
}
|
|
37
|
+
function generateModelHooks(model) {
|
|
38
|
+
const plural = pluralize(model);
|
|
39
|
+
const resource = plural.toLowerCase();
|
|
40
|
+
const modelLower = model.toLowerCase();
|
|
41
|
+
return `// \u2500\u2500\u2500 ${model} \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
42
|
+
|
|
43
|
+
export function use${plural}Query() {
|
|
44
|
+
return useQuery({
|
|
45
|
+
queryKey: ['${resource}'],
|
|
46
|
+
queryFn: () => fetchJSON<${model}[]>(\`\${API_BASE}/api/${resource}\`),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function use${model}Query(id: string | number | undefined) {
|
|
51
|
+
return useQuery({
|
|
52
|
+
queryKey: ['${resource}', id],
|
|
53
|
+
queryFn: () => fetchJSON<${model}>(\`\${API_BASE}/api/${resource}/\${id}\`),
|
|
54
|
+
enabled: id !== undefined && id !== null,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function useCreate${model}Mutation() {
|
|
59
|
+
const qc = useQueryClient();
|
|
60
|
+
return useMutation({
|
|
61
|
+
mutationFn: (data: Partial<${model}>) =>
|
|
62
|
+
fetchJSON<${model}>(\`\${API_BASE}/api/${resource}\`, {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
body: JSON.stringify(data),
|
|
65
|
+
}),
|
|
66
|
+
onSuccess: () => qc.invalidateQueries({ queryKey: ['${resource}'] }),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function useUpdate${model}Mutation() {
|
|
71
|
+
const qc = useQueryClient();
|
|
72
|
+
return useMutation({
|
|
73
|
+
mutationFn: ({ id, data }: { id: string | number; data: Partial<${model}> }) =>
|
|
74
|
+
fetchJSON<${model}>(\`\${API_BASE}/api/${resource}/\${id}\`, {
|
|
75
|
+
method: 'PATCH',
|
|
76
|
+
body: JSON.stringify(data),
|
|
77
|
+
}),
|
|
78
|
+
onSuccess: () => qc.invalidateQueries({ queryKey: ['${resource}'] }),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function useDelete${model}Mutation() {
|
|
83
|
+
const qc = useQueryClient();
|
|
84
|
+
return useMutation({
|
|
85
|
+
mutationFn: (id: string | number) =>
|
|
86
|
+
fetchJSON<void>(\`\${API_BASE}/api/${resource}/\${id}\`, { method: 'DELETE' }),
|
|
87
|
+
onSuccess: () => qc.invalidateQueries({ queryKey: ['${resource}'] }),
|
|
88
|
+
});
|
|
89
|
+
}`;
|
|
90
|
+
}
|
|
91
|
+
var stdin_default = generate;
|
|
92
|
+
export {
|
|
93
|
+
stdin_default as default,
|
|
94
|
+
generate
|
|
95
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
import { join, dirname } from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { extractBelongsToTargets, pluralize as pluralizeShared } from "./belongs-to.js";
|
|
5
|
+
function emitView(context) {
|
|
6
|
+
const skeleton = loadSkeleton(context.view.type);
|
|
7
|
+
const bodyJsx = context.renderBody(context);
|
|
8
|
+
const substitutions = buildSubstitutions(context, bodyJsx);
|
|
9
|
+
return applySubstitutions(skeleton, substitutions);
|
|
10
|
+
}
|
|
11
|
+
const SKELETON_BY_VIEW_TYPE = {
|
|
12
|
+
list: "list.tsx.template",
|
|
13
|
+
detail: "detail.tsx.template",
|
|
14
|
+
form: "form.tsx.template",
|
|
15
|
+
dashboard: "dashboard.tsx.template"
|
|
16
|
+
// Specialist types (board, timeline, calendar, analytics, workflow,
|
|
17
|
+
// wizard, comparison, settings, map, feed, profile) come online in
|
|
18
|
+
// Phase 2e.
|
|
19
|
+
};
|
|
20
|
+
function loadSkeleton(viewType) {
|
|
21
|
+
const filename = SKELETON_BY_VIEW_TYPE[viewType.toLowerCase()];
|
|
22
|
+
if (!filename) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`No skeleton registered for view type "${viewType}". Known types: ${Object.keys(SKELETON_BY_VIEW_TYPE).join(", ")}.`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
28
|
+
const path = join(here, "skeletons", filename);
|
|
29
|
+
return readFileSync(path, "utf8");
|
|
30
|
+
}
|
|
31
|
+
function buildSubstitutions(context, body) {
|
|
32
|
+
const modelName = context.model.name;
|
|
33
|
+
const pluralModel = pluralize(modelName);
|
|
34
|
+
const { imports, hooks } = buildBelongsToWiring(context);
|
|
35
|
+
return {
|
|
36
|
+
MODEL_NAME: modelName,
|
|
37
|
+
PLURAL_MODEL: pluralModel,
|
|
38
|
+
PLURAL_LOWER: pluralModel.toLowerCase(),
|
|
39
|
+
SINGULAR_LOWER: modelName.toLowerCase(),
|
|
40
|
+
BODY: body,
|
|
41
|
+
RELATED_IMPORTS: imports,
|
|
42
|
+
RELATED_HOOKS: hooks
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function buildBelongsToWiring(context) {
|
|
46
|
+
const rels = extractBelongsToTargets(context.model);
|
|
47
|
+
if (rels.length === 0) return { imports: "", hooks: "" };
|
|
48
|
+
const hookImportNames = /* @__PURE__ */ new Set();
|
|
49
|
+
for (const rel of rels) {
|
|
50
|
+
hookImportNames.add(`use${pluralizeShared(rel.target)}Query`);
|
|
51
|
+
}
|
|
52
|
+
const helperImport = context.view.type.toLowerCase() === "form" ? "getEntityDisplayName" : "resolveEntityDisplayName";
|
|
53
|
+
const importLines = [];
|
|
54
|
+
importLines.push(
|
|
55
|
+
`import { ${[...hookImportNames].join(", ")} } from '../hooks/useApi';`
|
|
56
|
+
);
|
|
57
|
+
importLines.push(`import { ${helperImport} } from '../lib/entity-display';`);
|
|
58
|
+
const hookLines = rels.map(
|
|
59
|
+
(rel) => ` const { data: ${rel.name}Options = [] } = use${pluralizeShared(rel.target)}Query();`
|
|
60
|
+
);
|
|
61
|
+
return {
|
|
62
|
+
imports: importLines.join("\n"),
|
|
63
|
+
hooks: hookLines.join("\n")
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function applySubstitutions(template, subs) {
|
|
67
|
+
let out = template;
|
|
68
|
+
for (const [key, value] of Object.entries(subs)) {
|
|
69
|
+
const pattern = new RegExp(`\\{\\{${key}\\}\\}`, "g");
|
|
70
|
+
out = out.replace(pattern, value);
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
function pluralize(s) {
|
|
75
|
+
if (/[^aeiou]y$/i.test(s)) return s.slice(0, -1) + "ies";
|
|
76
|
+
if (/(s|x|z|ch|sh)$/i.test(s)) return s + "es";
|
|
77
|
+
return s + "s";
|
|
78
|
+
}
|
|
79
|
+
export {
|
|
80
|
+
emitView
|
|
81
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { emitView } from "./view-emitter.js";
|
|
2
|
+
import { composeListBody } from "./list-body-composer.js";
|
|
3
|
+
import { composeDetailBody } from "./detail-body-composer.js";
|
|
4
|
+
import { composeFormBody } from "./form-body-composer.js";
|
|
5
|
+
import { composeDashboardBody } from "./dashboard-body-composer.js";
|
|
6
|
+
import { emitEntityDisplay } from "./helpers-emitter.js";
|
|
7
|
+
const PRIMARY_VIEW_TYPES = ["list", "detail", "form", "dashboard"];
|
|
8
|
+
const COMPOSER_BY_TYPE = {
|
|
9
|
+
list: composeListBody,
|
|
10
|
+
detail: composeDetailBody,
|
|
11
|
+
form: composeFormBody,
|
|
12
|
+
dashboard: composeDashboardBody
|
|
13
|
+
};
|
|
14
|
+
async function generate(context) {
|
|
15
|
+
const files = {};
|
|
16
|
+
const spec = context.spec;
|
|
17
|
+
const models = spec.models ?? {};
|
|
18
|
+
const viewsBySpec = spec.views ?? {};
|
|
19
|
+
const viewIndex = indexViews(viewsBySpec);
|
|
20
|
+
const skippedSpecialists = [];
|
|
21
|
+
for (const [modelName, model] of Object.entries(models)) {
|
|
22
|
+
for (const viewType of PRIMARY_VIEW_TYPES) {
|
|
23
|
+
const viewName = `${modelName}${capitalize(viewType)}View`;
|
|
24
|
+
const view = viewIndex.get(viewName) ?? synthesizeDefault(modelName, viewType);
|
|
25
|
+
const source = emitView({
|
|
26
|
+
view,
|
|
27
|
+
viewName,
|
|
28
|
+
model,
|
|
29
|
+
modelSchemas: models,
|
|
30
|
+
renderBody: COMPOSER_BY_TYPE[viewType]
|
|
31
|
+
});
|
|
32
|
+
files[`src/views/${viewName}.tsx`] = source;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
for (const [name, view] of Object.entries(viewsBySpec)) {
|
|
36
|
+
if (!PRIMARY_VIEW_TYPES.includes(view.type)) {
|
|
37
|
+
skippedSpecialists.push(`${name} (type=${view.type})`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (skippedSpecialists.length > 0) {
|
|
41
|
+
console.warn(
|
|
42
|
+
`[ReactAppStarter] Skipped ${skippedSpecialists.length} specialist view(s) \u2014 implement composers for: ${[...new Set(Object.values(viewsBySpec).filter((v) => !PRIMARY_VIEW_TYPES.includes(v.type)).map((v) => v.type))].join(", ")}. Skipped: ${skippedSpecialists.join(", ")}`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
files["src/lib/entity-display.ts"] = emitEntityDisplay();
|
|
46
|
+
return files;
|
|
47
|
+
}
|
|
48
|
+
function indexViews(views) {
|
|
49
|
+
const out = /* @__PURE__ */ new Map();
|
|
50
|
+
for (const [name, view] of Object.entries(views)) {
|
|
51
|
+
out.set(name, view);
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
function synthesizeDefault(modelName, viewType) {
|
|
56
|
+
return {
|
|
57
|
+
type: viewType,
|
|
58
|
+
model: modelName
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function capitalize(s) {
|
|
62
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
63
|
+
}
|
|
64
|
+
export {
|
|
65
|
+
generate
|
|
66
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
name: FastifyPrismaAPI
|
|
2
|
+
version: "1.0.0"
|
|
3
|
+
category: controller
|
|
4
|
+
description: "Fastify server with Prisma ORM, Zod validation, and JWT authentication"
|
|
5
|
+
|
|
6
|
+
metadata:
|
|
7
|
+
author: "SpecVerse Team"
|
|
8
|
+
license: "MIT"
|
|
9
|
+
tags: [fastify, prisma, typescript, rest-api]
|
|
10
|
+
|
|
11
|
+
compatibility:
|
|
12
|
+
specverse: ">=3.3.0"
|
|
13
|
+
node: ">=18.0.0"
|
|
14
|
+
|
|
15
|
+
capabilities:
|
|
16
|
+
provides:
|
|
17
|
+
- "api.rest"
|
|
18
|
+
- "api.rest.crud"
|
|
19
|
+
- "api.websocket"
|
|
20
|
+
requires: []
|
|
21
|
+
|
|
22
|
+
technology:
|
|
23
|
+
runtime: "node"
|
|
24
|
+
language: "typescript"
|
|
25
|
+
framework: "fastify"
|
|
26
|
+
orm: "prisma"
|
|
27
|
+
validation: "zod"
|
|
28
|
+
authentication: "jwt"
|
|
29
|
+
|
|
30
|
+
dependencies:
|
|
31
|
+
runtime:
|
|
32
|
+
- name: "fastify"
|
|
33
|
+
version: "^4.24.0"
|
|
34
|
+
- name: "@fastify/cors"
|
|
35
|
+
version: "^8.4.0"
|
|
36
|
+
- name: "@fastify/jwt"
|
|
37
|
+
version: "^7.2.0"
|
|
38
|
+
- name: "@fastify/helmet"
|
|
39
|
+
version: "^11.1.0"
|
|
40
|
+
- name: "@fastify/rate-limit"
|
|
41
|
+
version: "^9.0.0"
|
|
42
|
+
- name: "@prisma/client"
|
|
43
|
+
version: "^5.5.0"
|
|
44
|
+
- name: "zod"
|
|
45
|
+
version: "^3.22.0"
|
|
46
|
+
|
|
47
|
+
dev:
|
|
48
|
+
- name: "prisma"
|
|
49
|
+
version: "^5.5.0"
|
|
50
|
+
- name: "@types/node"
|
|
51
|
+
version: "^20.8.0"
|
|
52
|
+
- name: "typescript"
|
|
53
|
+
version: "^5.2.0"
|
|
54
|
+
- name: "tsx"
|
|
55
|
+
version: "^4.0.0"
|
|
56
|
+
|
|
57
|
+
codeTemplates:
|
|
58
|
+
routes:
|
|
59
|
+
engine: typescript
|
|
60
|
+
generator: "libs/instance-factories/backend/templates/fastify/routes-generator.ts"
|
|
61
|
+
outputPattern: "routes/{controller}.ts"
|
|
62
|
+
|
|
63
|
+
services:
|
|
64
|
+
engine: typescript
|
|
65
|
+
generator: "libs/instance-factories/backend/templates/prisma/services-generator.ts"
|
|
66
|
+
outputPattern: "services/{model}.service.ts"
|
|
67
|
+
|
|
68
|
+
schema:
|
|
69
|
+
engine: typescript
|
|
70
|
+
generator: "libs/instance-factories/backend/templates/prisma/schema-generator.ts"
|
|
71
|
+
outputPattern: "prisma/schema.prisma"
|
|
72
|
+
|
|
73
|
+
configuration:
|
|
74
|
+
server:
|
|
75
|
+
port: 3000
|
|
76
|
+
host: "0.0.0.0"
|
|
77
|
+
trustProxy: true
|
|
78
|
+
requestIdLogLabel: "reqId"
|
|
79
|
+
logger:
|
|
80
|
+
level: "info"
|
|
81
|
+
prettyPrint: true
|
|
82
|
+
|
|
83
|
+
orm:
|
|
84
|
+
provider: "postgresql"
|
|
85
|
+
relationMode: "foreignKeys"
|
|
86
|
+
log: ["query", "error", "warn"]
|
|
87
|
+
|
|
88
|
+
middleware:
|
|
89
|
+
cors:
|
|
90
|
+
origin: true
|
|
91
|
+
credentials: true
|
|
92
|
+
helmet:
|
|
93
|
+
enabled: true
|
|
94
|
+
rateLimit:
|
|
95
|
+
max: 100
|
|
96
|
+
timeWindow: "1 minute"
|
|
97
|
+
jwt:
|
|
98
|
+
secret: "${JWT_SECRET}"
|
|
99
|
+
sign:
|
|
100
|
+
expiresIn: "7d"
|
|
101
|
+
|
|
102
|
+
validation:
|
|
103
|
+
stripUnknown: true
|
|
104
|
+
abortEarly: false
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# CLI Factory
|
|
2
|
+
|
|
3
|
+
Generates Commander.js CLI applications from spec command definitions.
|
|
4
|
+
|
|
5
|
+
## Definition
|
|
6
|
+
|
|
7
|
+
| File | Description |
|
|
8
|
+
|------|-------------|
|
|
9
|
+
| `commander-js.yaml` | Commander.js v12 CLI with argument parsing, help text, and command routing |
|
|
10
|
+
|
|
11
|
+
## Generated Output
|
|
12
|
+
|
|
13
|
+
| Generator | Output | Purpose |
|
|
14
|
+
|-----------|--------|---------|
|
|
15
|
+
| `cli-entry-generator` | `backend/src/cli/index.ts` | Main CLI entry point — registers all commands from spec |
|
|
16
|
+
| `command-generator` | `backend/src/cli/commands/{command}.ts` | Individual command files (one per spec command) |
|
|
17
|
+
|
|
18
|
+
The entry generator reads the spec's `commands` section and registers each as a
|
|
19
|
+
Commander.js command with arguments, flags, and help text. Each command file
|
|
20
|
+
delegates to its corresponding service for business logic.
|
|
21
|
+
|
|
22
|
+
## Technology
|
|
23
|
+
|
|
24
|
+
- **Framework**: Commander.js ^12.0.0
|
|
25
|
+
- **Runtime**: Node.js with tsx for development
|
|
26
|
+
- **Language**: TypeScript
|
|
27
|
+
|
|
28
|
+
## Capabilities
|
|
29
|
+
|
|
30
|
+
| Provides | Requires |
|
|
31
|
+
|----------|----------|
|
|
32
|
+
| `cli.commands` | `service.controller` |
|
|
33
|
+
| `cli.parser` | |
|
|
34
|
+
| `cli.help` | |
|
|
35
|
+
|
|
36
|
+
## Spec Integration
|
|
37
|
+
|
|
38
|
+
Commands are defined in the spec with:
|
|
39
|
+
- `arguments` — positional parameters
|
|
40
|
+
- `flags` — optional switches
|
|
41
|
+
- `returns` — output format
|
|
42
|
+
- `exitCodes` — process exit codes
|
|
43
|
+
- `subcommands` — nested command trees
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
name: CommanderJS
|
|
2
|
+
version: "1.0.0"
|
|
3
|
+
category: cli
|
|
4
|
+
description: "Node.js CLI generation using Commander.js"
|
|
5
|
+
|
|
6
|
+
metadata:
|
|
7
|
+
author: "SpecVerse Team"
|
|
8
|
+
license: "MIT"
|
|
9
|
+
tags: [cli, commander, node, typescript]
|
|
10
|
+
|
|
11
|
+
compatibility:
|
|
12
|
+
specverse: ">=3.3.0"
|
|
13
|
+
node: ">=18.0.0"
|
|
14
|
+
|
|
15
|
+
capabilities:
|
|
16
|
+
provides:
|
|
17
|
+
- "cli.commands"
|
|
18
|
+
- "cli.parser"
|
|
19
|
+
- "cli.help"
|
|
20
|
+
requires:
|
|
21
|
+
- "service.controller"
|
|
22
|
+
|
|
23
|
+
technology:
|
|
24
|
+
runtime: "node"
|
|
25
|
+
language: "typescript"
|
|
26
|
+
framework: "commander"
|
|
27
|
+
version: "^12.0.0"
|
|
28
|
+
|
|
29
|
+
dependencies:
|
|
30
|
+
runtime:
|
|
31
|
+
- name: "commander"
|
|
32
|
+
version: "^12.0.0"
|
|
33
|
+
|
|
34
|
+
dev:
|
|
35
|
+
- name: "@types/node"
|
|
36
|
+
version: "^20.8.0"
|
|
37
|
+
- name: "typescript"
|
|
38
|
+
version: "^5.2.0"
|
|
39
|
+
- name: "tsx"
|
|
40
|
+
version: "^4.0.0"
|
|
41
|
+
|
|
42
|
+
codeTemplates:
|
|
43
|
+
cli-entry:
|
|
44
|
+
engine: typescript
|
|
45
|
+
generator: "libs/instance-factories/cli/templates/commander/cli-entry-generator.ts"
|
|
46
|
+
outputPattern: "{backendDir}/src/cli/index.ts"
|
|
47
|
+
|
|
48
|
+
cli-commands:
|
|
49
|
+
engine: typescript
|
|
50
|
+
generator: "libs/instance-factories/cli/templates/commander/command-generator.ts"
|
|
51
|
+
outputPattern: "{backendDir}/src/cli/commands/{command}.ts"
|
|
52
|
+
|
|
53
|
+
configuration:
|
|
54
|
+
outputStructure: "monorepo"
|
|
55
|
+
backendDir: "backend"
|
|
@@ -338,22 +338,25 @@ import type { ParserEngine, InferenceEngine, RealizeEngine } from '@specverse/ty
|
|
|
338
338
|
process.exit(1);
|
|
339
339
|
}
|
|
340
340
|
|
|
341
|
-
//
|
|
341
|
+
// --static ejects to a standalone starter kit (ReactAppStarter).
|
|
342
|
+
// Default: whatever the manifest declares (ReactAppRuntime in the
|
|
343
|
+
// shipped templates). The swap is a convenience only \u2014 users can
|
|
344
|
+
// achieve the same by editing their manifest directly.
|
|
342
345
|
let effectiveManifestPath = manifestPath;
|
|
343
|
-
if (
|
|
346
|
+
if (options.static) {
|
|
344
347
|
const manifestContent = readFileSync(manifestPath, 'utf8');
|
|
345
|
-
if (/instanceFactory:\\s*["']?
|
|
348
|
+
if (/instanceFactory:\\s*["']?ReactAppRuntime["']?\\s*$/m.test(manifestContent)) {
|
|
346
349
|
const { tmpdir } = await import('os');
|
|
347
|
-
const
|
|
348
|
-
/instanceFactory:\\s*["']?
|
|
349
|
-
'instanceFactory: "
|
|
350
|
+
const staticManifest = manifestContent.replace(
|
|
351
|
+
/instanceFactory:\\s*["']?ReactAppRuntime["']?\\s*$/gm,
|
|
352
|
+
'instanceFactory: "ReactAppStarter"'
|
|
350
353
|
);
|
|
351
|
-
effectiveManifestPath = join(tmpdir(), 'specverse-
|
|
352
|
-
writeFileSync(effectiveManifestPath,
|
|
353
|
-
console.log(' Using
|
|
354
|
+
effectiveManifestPath = join(tmpdir(), 'specverse-static-manifest.yaml');
|
|
355
|
+
writeFileSync(effectiveManifestPath, staticManifest, 'utf8');
|
|
356
|
+
console.log(' Using static mode (standalone starter kit \u2014 ReactAppStarter)');
|
|
357
|
+
} else {
|
|
358
|
+
console.log(' Using static mode (manifest factory unchanged)');
|
|
354
359
|
}
|
|
355
|
-
} else {
|
|
356
|
-
console.log(' Using static mode (full frontend generation)');
|
|
357
360
|
}
|
|
358
361
|
|
|
359
362
|
const realizeEngine = registry.getEngineForCapability('realize') as RealizeEngine;
|
|
@@ -502,8 +505,56 @@ import { fileURLToPath } from 'url';`,
|
|
|
502
505
|
}
|
|
503
506
|
|
|
504
507
|
copyDir(templateDir, destDir);
|
|
508
|
+
|
|
509
|
+
// If the copied template didn't ship a specs/main.specly, load
|
|
510
|
+
// the canonical default spec from @specverse/engines/assets \u2014
|
|
511
|
+
// same file app-demo's Server Manager "new spec" action uses,
|
|
512
|
+
// so both entry points start a user on the same footing. The
|
|
513
|
+
// template can opt out of this by shipping its own spec.
|
|
514
|
+
const specDestPath = join(destDir, 'specs', 'main.specly');
|
|
515
|
+
if (!existsSync(specDestPath)) {
|
|
516
|
+
try {
|
|
517
|
+
const { createRequire } = await import('module');
|
|
518
|
+
const require = createRequire(import.meta.url);
|
|
519
|
+
const enginesPkg = require.resolve('@specverse/engines/package.json');
|
|
520
|
+
const canonicalSpec = join(
|
|
521
|
+
dirname(enginesPkg),
|
|
522
|
+
'assets', 'templates', 'default', 'specs', 'main.specly'
|
|
523
|
+
);
|
|
524
|
+
if (existsSync(canonicalSpec)) {
|
|
525
|
+
let content = readFileSync(canonicalSpec, 'utf8');
|
|
526
|
+
for (const [key, val] of Object.entries(vars)) {
|
|
527
|
+
content = content.split(key).join(val);
|
|
528
|
+
}
|
|
529
|
+
mkdirSync(join(destDir, 'specs'), { recursive: true });
|
|
530
|
+
writeFileSync(specDestPath, content);
|
|
531
|
+
}
|
|
532
|
+
} catch { /* engines not installed or template missing \u2014 proceed */ }
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// --static: flip any ReactAppRuntime mappings in the copied
|
|
536
|
+
// manifests to ReactAppStarter so the user gets standalone-starter
|
|
537
|
+
// output on first \`spv realize\` without having to re-pass the
|
|
538
|
+
// flag. Same semantic as \`spv realize --static\` \u2014 just applied
|
|
539
|
+
// at init time. Idempotent; no-op for templates without a frontend.
|
|
540
|
+
if (options.static) {
|
|
541
|
+
const manifestsDir = join(destDir, 'manifests');
|
|
542
|
+
if (existsSync(manifestsDir)) {
|
|
543
|
+
for (const f of readdirSync(manifestsDir)) {
|
|
544
|
+
if (!f.endsWith('.yaml') && !f.endsWith('.yml')) continue;
|
|
545
|
+
const p = join(manifestsDir, f);
|
|
546
|
+
const before = readFileSync(p, 'utf8');
|
|
547
|
+
const after = before.replace(
|
|
548
|
+
/instanceFactory:\\s*["']?ReactAppRuntime["']?/g,
|
|
549
|
+
'instanceFactory: "ReactAppStarter"'
|
|
550
|
+
);
|
|
551
|
+
if (after !== before) writeFileSync(p, after, 'utf8');
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
505
556
|
console.log('Project created: ' + destDir);
|
|
506
|
-
console.log('Template: ' + templateName);
|
|
557
|
+
console.log('Template: ' + templateName + (options.static ? ' (static / ReactAppStarter)' : ''));
|
|
507
558
|
console.log('');
|
|
508
559
|
|
|
509
560
|
// Build the "Next steps" hint from the actual scripts the
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Communication Factory
|
|
2
|
+
|
|
3
|
+
Event-driven messaging infrastructure with publisher/subscriber generation.
|
|
4
|
+
|
|
5
|
+
## Definitions
|
|
6
|
+
|
|
7
|
+
| File | Description |
|
|
8
|
+
|------|-------------|
|
|
9
|
+
| `event-emitter.yaml` | In-memory event bus using EventEmitter3 (development/testing) |
|
|
10
|
+
| `rabbitmq-events.yaml` | RabbitMQ AMQP bus for production pub/sub messaging |
|
|
11
|
+
|
|
12
|
+
## Generated Output
|
|
13
|
+
|
|
14
|
+
### EventEmitter (`eventemitter/`)
|
|
15
|
+
|
|
16
|
+
| Generator | Output | Purpose |
|
|
17
|
+
|-----------|--------|---------|
|
|
18
|
+
| `bus-generator` | `src/events/eventBus.ts` | Singleton event bus with typed event names |
|
|
19
|
+
| `publisher-generator` | `src/events/publishers/{event}Publisher.ts` | Per-event publisher (one per spec event) |
|
|
20
|
+
| `subscriber-generator` | `src/events/subscribers/{event}Subscriber.ts` | Per-event subscriber with handler logic |
|
|
21
|
+
|
|
22
|
+
### RabbitMQ (`rabbitmq/` — defined, templates pending)
|
|
23
|
+
|
|
24
|
+
| Generator | Output | Purpose |
|
|
25
|
+
|-----------|--------|---------|
|
|
26
|
+
| `publisher-generator` | `src/events/publishers/{event}Publisher.ts` | AMQP publisher with confirms |
|
|
27
|
+
| `subscriber-generator` | `src/events/subscribers/{event}Subscriber.ts` | AMQP consumer with ack/nack |
|
|
28
|
+
| `connection-generator` | `src/events/connection.ts` | RabbitMQ connection management |
|
|
29
|
+
|
|
30
|
+
## Technology
|
|
31
|
+
|
|
32
|
+
- **EventEmitter**: eventemitter3 ^5.0.0 (zero-dependency, in-process)
|
|
33
|
+
- **RabbitMQ**: amqplib ^0.10.3 (topic exchange, durable queues, prefetch)
|
|
34
|
+
|
|
35
|
+
## Capabilities
|
|
36
|
+
|
|
37
|
+
| Provides | Requires |
|
|
38
|
+
|----------|----------|
|
|
39
|
+
| `messaging.pubsub` | (none) |
|
|
40
|
+
| `messaging.events` | |
|
|
41
|
+
| `messaging.inmemory` (EventEmitter) | |
|
|
42
|
+
| `messaging.queue` (RabbitMQ) | |
|
|
43
|
+
|
|
44
|
+
## Configuration
|
|
45
|
+
|
|
46
|
+
EventEmitter: `maxListeners: 100`, `captureRejections: true`
|
|
47
|
+
RabbitMQ: topic exchange, durable queues, prefetch 10, persistent messages, confirm-select
|