@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
package/libs/instance-factories/applications/templates/react-starter/dashboard-body-composer.ts
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard-view body composer for ReactAppStarter
|
|
3
|
+
*
|
|
4
|
+
* Minimal starter-kit dashboard: metric cards derived from the list
|
|
5
|
+
* query (total count; enum-field breakdowns if the model has a status-
|
|
6
|
+
* like attribute), plus a compact preview of recent records.
|
|
7
|
+
*
|
|
8
|
+
* Aggregation / charts are deferred — they need backend endpoints
|
|
9
|
+
* (`/api/posts/metrics`, `/api/posts/by-month`, …) that the starter
|
|
10
|
+
* kit doesn't assume. TODO comments flag the extension points.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { METADATA_FIELDS } from '@specverse/runtime/views/core';
|
|
14
|
+
import type { EmitContext, ModelSpec } from './view-emitter.js';
|
|
15
|
+
import { buildFKMap } from './belongs-to.js';
|
|
16
|
+
|
|
17
|
+
const METADATA_FIELD_NAMES = new Set(METADATA_FIELDS);
|
|
18
|
+
|
|
19
|
+
export function composeDashboardBody(context: EmitContext): string {
|
|
20
|
+
const modelName = context.model.name;
|
|
21
|
+
const previewColumns = inferPreviewColumns(context.model);
|
|
22
|
+
const enumFields = inferEnumFields(context.model);
|
|
23
|
+
const fkMap = buildFKMap(context.model);
|
|
24
|
+
|
|
25
|
+
const lines: string[] = [];
|
|
26
|
+
|
|
27
|
+
// ── Metrics row ────────────────────────────────────────────────────
|
|
28
|
+
lines.push('<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">');
|
|
29
|
+
lines.push(...renderTotalCard(modelName));
|
|
30
|
+
for (const enumField of enumFields) {
|
|
31
|
+
lines.push(...renderEnumBreakdownCard(enumField));
|
|
32
|
+
}
|
|
33
|
+
if (enumFields.length === 0) {
|
|
34
|
+
lines.push(...renderPlaceholderCard());
|
|
35
|
+
}
|
|
36
|
+
lines.push('</div>');
|
|
37
|
+
|
|
38
|
+
// ── TODO: aggregation metrics ──────────────────────────────────────
|
|
39
|
+
lines.push('');
|
|
40
|
+
lines.push('{/* TODO: add aggregation metrics (averages / sums / time series)');
|
|
41
|
+
lines.push(' by adding backend endpoints and per-metric hooks. Wire them in');
|
|
42
|
+
lines.push(' alongside the count cards above. */}');
|
|
43
|
+
|
|
44
|
+
// ── Recent-items preview ───────────────────────────────────────────
|
|
45
|
+
lines.push('');
|
|
46
|
+
lines.push('<div className="rounded-lg border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-900">');
|
|
47
|
+
lines.push(' <div className="border-b border-gray-200 dark:border-gray-700 px-6 py-3">');
|
|
48
|
+
lines.push(' <h3 className="text-sm font-semibold text-gray-700 dark:text-gray-200 uppercase tracking-wider">');
|
|
49
|
+
lines.push(` Recent ${humanize(modelName)}s`);
|
|
50
|
+
lines.push(' </h3>');
|
|
51
|
+
lines.push(' </div>');
|
|
52
|
+
|
|
53
|
+
if (previewColumns.length === 0) {
|
|
54
|
+
lines.push(' <div className="px-6 py-4 text-sm text-gray-400">No displayable fields.</div>');
|
|
55
|
+
} else {
|
|
56
|
+
lines.push(' <table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">');
|
|
57
|
+
lines.push(' <thead>');
|
|
58
|
+
lines.push(' <tr>');
|
|
59
|
+
for (const col of previewColumns) {
|
|
60
|
+
const fk = fkMap.get(col);
|
|
61
|
+
const headerLabel = humanize(fk ? fk.name : col);
|
|
62
|
+
lines.push(
|
|
63
|
+
` <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">` +
|
|
64
|
+
headerLabel +
|
|
65
|
+
`</th>`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
lines.push(' </tr>');
|
|
69
|
+
lines.push(' </thead>');
|
|
70
|
+
lines.push(' <tbody className="divide-y divide-gray-200 dark:divide-gray-700">');
|
|
71
|
+
lines.push(' {preview.map((item, idx) => (');
|
|
72
|
+
lines.push(' <tr');
|
|
73
|
+
lines.push(' key={idx}');
|
|
74
|
+
lines.push(' onClick={() => onSelect?.(item)}');
|
|
75
|
+
lines.push(' className="hover:bg-gray-50 dark:hover:bg-gray-800 cursor-pointer"');
|
|
76
|
+
lines.push(' >');
|
|
77
|
+
for (const col of previewColumns) {
|
|
78
|
+
const fk = fkMap.get(col);
|
|
79
|
+
const expr = fk
|
|
80
|
+
? `{resolveEntityDisplayName((item as any).${col}, ${fk.name}Options)}`
|
|
81
|
+
: `{String((item as any).${col} ?? '')}`;
|
|
82
|
+
lines.push(
|
|
83
|
+
` <td className="px-6 py-4 text-sm text-gray-700 dark:text-gray-300">` +
|
|
84
|
+
`${expr}</td>`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
lines.push(' </tr>');
|
|
88
|
+
lines.push(' ))}');
|
|
89
|
+
lines.push(' {preview.length === 0 && (');
|
|
90
|
+
lines.push(` <tr><td colSpan={${previewColumns.length}} className="px-6 py-4 text-sm text-gray-400">No records yet.</td></tr>`);
|
|
91
|
+
lines.push(' )}');
|
|
92
|
+
lines.push(' </tbody>');
|
|
93
|
+
lines.push(' </table>');
|
|
94
|
+
}
|
|
95
|
+
lines.push('</div>');
|
|
96
|
+
|
|
97
|
+
return lines.join('\n');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
101
|
+
// Card rendering
|
|
102
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
function renderTotalCard(modelName: string): string[] {
|
|
105
|
+
const pluralLower = humanize(modelName).toLowerCase() + 's';
|
|
106
|
+
return [
|
|
107
|
+
' <div className="rounded-lg border border-gray-200 bg-white p-6 shadow-sm dark:border-gray-700 dark:bg-gray-900">',
|
|
108
|
+
' <p className="text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400">',
|
|
109
|
+
` Total ${pluralLower}`,
|
|
110
|
+
' </p>',
|
|
111
|
+
' <p className="mt-2 text-3xl font-semibold text-gray-900 dark:text-gray-100">',
|
|
112
|
+
' {items.length}',
|
|
113
|
+
' </p>',
|
|
114
|
+
' </div>',
|
|
115
|
+
];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function renderEnumBreakdownCard(field: EnumField): string[] {
|
|
119
|
+
// One card per enum value — shows count of records where the field
|
|
120
|
+
// equals that value.
|
|
121
|
+
const cards: string[] = [];
|
|
122
|
+
for (const value of field.values) {
|
|
123
|
+
cards.push(
|
|
124
|
+
' <div className="rounded-lg border border-gray-200 bg-white p-6 shadow-sm dark:border-gray-700 dark:bg-gray-900">',
|
|
125
|
+
' <p className="text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400">',
|
|
126
|
+
` ${humanize(field.name)}: ${humanize(value)}`,
|
|
127
|
+
' </p>',
|
|
128
|
+
' <p className="mt-2 text-3xl font-semibold text-gray-900 dark:text-gray-100">',
|
|
129
|
+
` {items.filter((i: any) => i.${field.name} === ${JSON.stringify(value)}).length}`,
|
|
130
|
+
' </p>',
|
|
131
|
+
' </div>',
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
return cards;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function renderPlaceholderCard(): string[] {
|
|
138
|
+
return [
|
|
139
|
+
' <div className="rounded-lg border border-dashed border-gray-300 p-6 text-sm text-gray-400 dark:border-gray-600">',
|
|
140
|
+
' Add a metric here — e.g. a sum, average, or time-windowed count.',
|
|
141
|
+
' </div>',
|
|
142
|
+
];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
146
|
+
// Field inference
|
|
147
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
interface EnumField {
|
|
150
|
+
name: string;
|
|
151
|
+
values: string[];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function inferEnumFields(model: ModelSpec): EnumField[] {
|
|
155
|
+
const out: EnumField[] = [];
|
|
156
|
+
const attrs = model.attributes ?? {};
|
|
157
|
+
for (const [name, rawDef] of Object.entries(attrs)) {
|
|
158
|
+
const def = rawDef as { values?: string[] };
|
|
159
|
+
const values = def?.values;
|
|
160
|
+
if (Array.isArray(values) && values.length > 0 && values.length <= 6) {
|
|
161
|
+
out.push({ name, values });
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Cap: one enum breakdown in the card row to avoid card sprawl. The
|
|
165
|
+
// remaining enum fields stay available in the list view; users can
|
|
166
|
+
// promote more breakdowns manually.
|
|
167
|
+
return out.slice(0, 1);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function inferPreviewColumns(model: ModelSpec): string[] {
|
|
171
|
+
const attrs = model.attributes ?? {};
|
|
172
|
+
const attrCols = Object.keys(attrs).filter(n => !METADATA_FIELD_NAMES.has(n));
|
|
173
|
+
|
|
174
|
+
// Include belongsTo FK columns so the preview shows "who does this
|
|
175
|
+
// belong to" at a glance. Matches the list-view convention.
|
|
176
|
+
const fkCols = [...buildFKMap(model).keys()].filter(k => !attrCols.includes(k));
|
|
177
|
+
|
|
178
|
+
// FKs are always useful at-a-glance, so keep them all. Cap the
|
|
179
|
+
// combined set at 5 to keep the preview table readable without
|
|
180
|
+
// crowding out the "who" columns.
|
|
181
|
+
return [...fkCols, ...attrCols].slice(0, Math.max(5, fkCols.length + 1));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function humanize(name: string): string {
|
|
185
|
+
return name
|
|
186
|
+
.replace(/([A-Z])/g, ' $1')
|
|
187
|
+
.replace(/^./, c => c.toUpperCase())
|
|
188
|
+
.trim();
|
|
189
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detail-view body composer for ReactAppStarter
|
|
3
|
+
*
|
|
4
|
+
* Renders the interior of a detail view as a JSX-safe definition list:
|
|
5
|
+
* business fields first (prominent), belongsTo relationships resolved
|
|
6
|
+
* to display names in a dedicated section, metadata fields last (muted).
|
|
7
|
+
* The outer card + actions live in `skeletons/detail.tsx.template`.
|
|
8
|
+
*
|
|
9
|
+
* belongsTo FK columns are resolved via `resolveEntityDisplayName`
|
|
10
|
+
* using option arrays (${relName}Options) wired in by view-emitter's
|
|
11
|
+
* RELATED_HOOKS substitution.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { METADATA_FIELDS } from '@specverse/runtime/views/core';
|
|
15
|
+
import type { EmitContext, ModelSpec } from './view-emitter.js';
|
|
16
|
+
import { extractBelongsToTargets, type BelongsToRel } from './belongs-to.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Metadata attribute names rendered in a muted style. Sourced from
|
|
20
|
+
* the canonical pattern library so both this composer and the rest
|
|
21
|
+
* of the system stay in sync automatically.
|
|
22
|
+
*/
|
|
23
|
+
const METADATA_FIELD_NAMES = new Set(METADATA_FIELDS);
|
|
24
|
+
|
|
25
|
+
export function composeDetailBody(context: EmitContext): string {
|
|
26
|
+
// FK columns are excluded from business so we don't emit the raw
|
|
27
|
+
// UUID next to the resolved name. They surface in the belongsTo
|
|
28
|
+
// section instead, rendered via resolveEntityDisplayName.
|
|
29
|
+
const belongsTo = extractBelongsToTargets(context.model);
|
|
30
|
+
const fkExcludes = new Set(belongsTo.map(r => `${r.name}Id`));
|
|
31
|
+
const { business, metadata } = partitionFields(context.model, fkExcludes);
|
|
32
|
+
|
|
33
|
+
const lines: string[] = [];
|
|
34
|
+
lines.push('<div className="rounded-lg border border-gray-200 bg-white p-6 dark:border-gray-700 dark:bg-gray-900">');
|
|
35
|
+
|
|
36
|
+
if (business.length > 0) {
|
|
37
|
+
lines.push(' <dl className="grid grid-cols-1 gap-4 sm:grid-cols-2">');
|
|
38
|
+
for (const field of business) {
|
|
39
|
+
lines.push(...renderField(field, { muted: false }));
|
|
40
|
+
}
|
|
41
|
+
lines.push(' </dl>');
|
|
42
|
+
} else if (belongsTo.length === 0) {
|
|
43
|
+
lines.push(' <p className="text-sm text-gray-400">No business fields defined for this model.</p>');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (belongsTo.length > 0) {
|
|
47
|
+
lines.push('');
|
|
48
|
+
lines.push(' <dl className="mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 border-t border-gray-200 dark:border-gray-700 pt-4">');
|
|
49
|
+
for (const rel of belongsTo) {
|
|
50
|
+
lines.push(...renderRelationshipField(rel));
|
|
51
|
+
}
|
|
52
|
+
lines.push(' </dl>');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (metadata.length > 0) {
|
|
56
|
+
lines.push('');
|
|
57
|
+
lines.push(' <dl className="mt-6 grid grid-cols-1 gap-2 sm:grid-cols-2 border-t border-gray-200 dark:border-gray-700 pt-4 text-xs text-gray-500 dark:text-gray-400">');
|
|
58
|
+
for (const field of metadata) {
|
|
59
|
+
lines.push(...renderField(field, { muted: true }));
|
|
60
|
+
}
|
|
61
|
+
lines.push(' </dl>');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
lines.push('</div>');
|
|
65
|
+
return lines.join('\n');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface Field {
|
|
69
|
+
name: string;
|
|
70
|
+
/** Display label; defaults to humanize(name). */
|
|
71
|
+
label?: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function partitionFields(
|
|
75
|
+
model: ModelSpec,
|
|
76
|
+
exclude: Set<string> = new Set(),
|
|
77
|
+
): { business: Field[]; metadata: Field[] } {
|
|
78
|
+
const attrs = Object.keys(model.attributes ?? {});
|
|
79
|
+
const business: Field[] = [];
|
|
80
|
+
const metadata: Field[] = [];
|
|
81
|
+
for (const name of attrs) {
|
|
82
|
+
if (exclude.has(name)) continue;
|
|
83
|
+
const field = { name, label: humanize(name) };
|
|
84
|
+
if (METADATA_FIELD_NAMES.has(name)) {
|
|
85
|
+
metadata.push(field);
|
|
86
|
+
} else {
|
|
87
|
+
business.push(field);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return { business, metadata };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Render a belongsTo row as a label + resolved display name. The
|
|
95
|
+
* options array (${relName}Options) is populated by the hook call
|
|
96
|
+
* wired into the detail skeleton via view-emitter's RELATED_HOOKS.
|
|
97
|
+
*/
|
|
98
|
+
function renderRelationshipField(rel: BelongsToRel): string[] {
|
|
99
|
+
const label = humanize(rel.name);
|
|
100
|
+
const fkName = `${rel.name}Id`;
|
|
101
|
+
const optionsVar = `${rel.name}Options`;
|
|
102
|
+
return [
|
|
103
|
+
' <div>',
|
|
104
|
+
` <dt className="text-sm font-medium text-gray-500 dark:text-gray-400">${label}</dt>`,
|
|
105
|
+
` <dd className="mt-1 text-sm text-gray-900 dark:text-gray-100 break-words">` +
|
|
106
|
+
`{resolveEntityDisplayName((item as any).${fkName}, ${optionsVar})}</dd>`,
|
|
107
|
+
' </div>',
|
|
108
|
+
];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function renderField(field: Field, opts: { muted: boolean }): string[] {
|
|
112
|
+
const label = field.label ?? humanize(field.name);
|
|
113
|
+
const labelCls = opts.muted
|
|
114
|
+
? 'font-medium uppercase tracking-wide text-gray-400 dark:text-gray-500'
|
|
115
|
+
: 'text-sm font-medium text-gray-500 dark:text-gray-400';
|
|
116
|
+
const valueCls = opts.muted
|
|
117
|
+
? 'text-gray-500 dark:text-gray-400'
|
|
118
|
+
: 'mt-1 text-sm text-gray-900 dark:text-gray-100 break-words';
|
|
119
|
+
|
|
120
|
+
return [
|
|
121
|
+
' <div>',
|
|
122
|
+
` <dt className="${labelCls}">${label}</dt>`,
|
|
123
|
+
` <dd className="${valueCls}">` +
|
|
124
|
+
`{String((item as any).${field.name} ?? '')}` +
|
|
125
|
+
'</dd>',
|
|
126
|
+
' </div>',
|
|
127
|
+
];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function humanize(name: string): string {
|
|
131
|
+
return name
|
|
132
|
+
.replace(/([A-Z])/g, ' $1')
|
|
133
|
+
.replace(/^./, c => c.toUpperCase())
|
|
134
|
+
.trim();
|
|
135
|
+
}
|