@open-mercato/cli 0.6.7 → 0.6.8-canary.320.1.bf889f49e4
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/.turbo/turbo-build.log +77 -4
- package/AGENTS.md +6 -1
- package/build.mjs +68 -11
- package/dist/agentic/claude-code/hooks/entity-migration-check.ts +32 -88
- package/dist/agentic/codex/enforcement-rules.md +2 -16
- package/dist/agentic/cursor/hooks/entity-migration-check.mjs +19 -50
- package/dist/agentic/cursor/rules/entity-guard.mdc +9 -21
- package/dist/agentic/cursor/rules/generated-guard.mdc +3 -5
- package/dist/agentic/cursor/rules/open-mercato.mdc +3 -29
- package/dist/agentic/guides/ai-workflows.md +76 -0
- package/dist/agentic/guides/architecture.md +107 -0
- package/dist/agentic/guides/backend-ui.md +86 -0
- package/dist/agentic/guides/contracts.md +92 -0
- package/dist/agentic/guides/extensions.md +121 -0
- package/dist/agentic/guides/framework-extension-points.md +36 -0
- package/dist/agentic/guides/integrations.md +81 -0
- package/dist/agentic/guides/module-facts.json +116757 -3951
- package/dist/agentic/guides/module-system.md +12 -127
- package/dist/agentic/guides/modules/ai_assistant.md +227 -28
- package/dist/agentic/guides/modules/api_docs.md +58 -2
- package/dist/agentic/guides/modules/api_keys.md +92 -7
- package/dist/agentic/guides/modules/attachments.md +119 -14
- package/dist/agentic/guides/modules/audit_logs.md +131 -10
- package/dist/agentic/guides/modules/auth.md +276 -33
- package/dist/agentic/guides/modules/business_rules.md +149 -14
- package/dist/agentic/guides/modules/catalog.md +482 -43
- package/dist/agentic/guides/modules/channel_gmail.md +82 -3
- package/dist/agentic/guides/modules/channel_imap.md +82 -3
- package/dist/agentic/guides/modules/checkout.md +305 -27
- package/dist/agentic/guides/modules/communication_channels.md +315 -33
- package/dist/agentic/guides/modules/configs.md +104 -8
- package/dist/agentic/guides/modules/content.md +57 -2
- package/dist/agentic/guides/modules/currencies.md +152 -17
- package/dist/agentic/guides/modules/customer_accounts.md +387 -45
- package/dist/agentic/guides/modules/customers.md +746 -85
- package/dist/agentic/guides/modules/dashboards.md +155 -10
- package/dist/agentic/guides/modules/data_sync.md +149 -16
- package/dist/agentic/guides/modules/design_system.md +102 -0
- package/dist/agentic/guides/modules/dictionaries.md +107 -12
- package/dist/agentic/guides/modules/directory.md +148 -15
- package/dist/agentic/guides/modules/entities.md +145 -13
- package/dist/agentic/guides/modules/events.md +59 -2
- package/dist/agentic/guides/modules/feature_toggles.md +119 -9
- package/dist/agentic/guides/modules/gateway_stripe.md +92 -4
- package/dist/agentic/guides/modules/generators.md +45 -2
- package/dist/agentic/guides/modules/inbox_ops.md +207 -27
- package/dist/agentic/guides/modules/integrations.md +149 -16
- package/dist/agentic/guides/modules/messages.md +223 -24
- package/dist/agentic/guides/modules/notifications.md +96 -8
- package/dist/agentic/guides/modules/onboarding.md +77 -6
- package/dist/agentic/guides/modules/payment_gateways.md +168 -21
- package/dist/agentic/guides/modules/perspectives.md +77 -7
- package/dist/agentic/guides/modules/planner.md +136 -18
- package/dist/agentic/guides/modules/portal.md +93 -2
- package/dist/agentic/guides/modules/progress.md +104 -14
- package/dist/agentic/guides/modules/query_index.md +154 -15
- package/dist/agentic/guides/modules/record_locks.md +206 -21
- package/dist/agentic/guides/modules/resources.md +197 -30
- package/dist/agentic/guides/modules/sales.md +687 -84
- package/dist/agentic/guides/modules/scheduler.md +133 -14
- package/dist/agentic/guides/modules/search.md +170 -6
- package/dist/agentic/guides/modules/security.md +294 -35
- package/dist/agentic/guides/modules/shipping_carriers.md +134 -18
- package/dist/agentic/guides/modules/sso.md +151 -26
- package/dist/agentic/guides/modules/staff.md +372 -57
- package/dist/agentic/guides/modules/storage_s3.md +95 -5
- package/dist/agentic/guides/modules/sync_akeneo.md +85 -3
- package/dist/agentic/guides/modules/sync_excel.md +75 -6
- package/dist/agentic/guides/modules/system_status_overlays.md +60 -2
- package/dist/agentic/guides/modules/translations.md +104 -10
- package/dist/agentic/guides/modules/webhooks.md +203 -24
- package/dist/agentic/guides/modules/wms.md +469 -47
- package/dist/agentic/guides/modules/workflows.md +302 -43
- package/dist/agentic/guides/reference-module-facts.json +6942 -0
- package/dist/agentic/guides/reference-modules/example.md +462 -0
- package/dist/agentic/guides/spec-delivery.md +56 -0
- package/dist/agentic/guides/testing-debugging.md +80 -0
- package/dist/agentic/guides/upstream/AGENTS.md +300 -0
- package/dist/agentic/guides/upstream/BACKWARD_COMPATIBILITY.md +334 -0
- package/dist/agentic/guides/upstream/manifest.json +8 -0
- package/dist/agentic/shared/AGENTS.md.template +120 -311
- package/dist/agentic/shared/ai/agentic.config.json +2 -0
- package/dist/agentic/shared/ai/harness/README.md +50 -0
- package/dist/agentic/shared/ai/harness/RELEASE.md +59 -0
- package/dist/agentic/shared/ai/harness/cases.json +20596 -0
- package/dist/agentic/shared/ai/harness/cases.schema.json +196 -0
- package/dist/agentic/shared/ai/harness/design-system-inventory.json +6326 -0
- package/dist/agentic/shared/ai/harness/fixtures/index.json +51 -0
- package/dist/agentic/shared/ai/harness/fixtures/seeds.json +166 -0
- package/dist/agentic/shared/ai/harness/generated-code-review-policy.md +21 -0
- package/dist/agentic/shared/ai/harness/generated-code-review-response.schema.json +96 -0
- package/dist/agentic/shared/ai/harness/generated-code-review-result.schema.json +172 -0
- package/dist/agentic/shared/ai/harness/knowledge-change.schema.json +228 -0
- package/dist/agentic/shared/ai/harness/release-matrix.json +163 -0
- package/dist/agentic/shared/ai/harness/release-result.schema.json +219 -0
- package/dist/agentic/shared/ai/harness/result.schema.json +136 -0
- package/dist/agentic/shared/ai/harness/routing-response.schema.json +22 -0
- package/dist/agentic/shared/ai/harness/source-link-inventory.json +1706 -0
- package/dist/agentic/shared/ai/harness/target-validation-result.schema.json +73 -0
- package/dist/agentic/shared/ai/harness/validators.json +82 -0
- package/dist/agentic/shared/ai/harness/writable-ast-oracles.mjs +2121 -0
- package/dist/agentic/shared/ai/harness/writable-behavior-oracles.mjs +1096 -0
- package/dist/agentic/shared/ai/harness/writable-spec-oracles.mjs +602 -0
- package/dist/agentic/shared/ai/lessons/_template.md +16 -0
- package/dist/agentic/shared/ai/lessons.md +26 -3
- package/dist/agentic/shared/ai/review-checklist.md +36 -0
- package/dist/agentic/shared/ai/skills/om-auto-implement-spec/SKILL.md +21 -0
- package/dist/agentic/shared/ai/skills/om-auto-review-pr/SKILL.md +4 -0
- package/dist/agentic/shared/ai/skills/om-auto-upgrade-0.6.6-to-0.6.7/SKILL.md +98 -0
- package/dist/agentic/shared/ai/skills/om-backend-ui-design/SKILL.md +21 -241
- package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/crud-surfaces.md +31 -0
- package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/frontend-and-design-system.md +53 -0
- package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/page-and-navigation.md +15 -0
- package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/quality-states.md +11 -0
- package/dist/agentic/shared/ai/skills/om-build-workflow/SKILL.md +30 -0
- package/dist/agentic/shared/ai/skills/om-build-workflow/references/activity-contracts.md +12 -0
- package/dist/agentic/shared/ai/skills/om-build-workflow/references/durability-and-progress.md +13 -0
- package/dist/agentic/shared/ai/skills/om-build-workflow/references/workflow-design.md +14 -0
- package/dist/agentic/shared/ai/skills/om-create-ai-agent/SKILL.md +28 -0
- package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/attachments-and-overrides.md +12 -0
- package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/module-agents-and-tools.md +22 -0
- package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/orchestrator-agents.md +12 -0
- package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/surface-selector.md +19 -0
- package/dist/agentic/shared/ai/skills/om-data-model-design/SKILL.md +17 -601
- package/dist/agentic/shared/ai/skills/om-data-model-design/references/integrity-and-concurrency.md +21 -0
- package/dist/agentic/shared/ai/skills/om-data-model-design/references/migration-workflow.md +15 -0
- package/dist/agentic/shared/ai/skills/om-data-model-design/references/schema-design.md +16 -0
- package/dist/agentic/shared/ai/skills/om-data-model-design/references/sensitive-data.md +10 -0
- package/dist/agentic/shared/ai/skills/om-eject-and-customize/SKILL.md +15 -310
- package/dist/agentic/shared/ai/skills/om-eject-and-customize/references/decision-and-procedure.md +19 -0
- package/dist/agentic/shared/ai/skills/om-evolve-harness/SKILL.md +31 -0
- package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-template.md +100 -0
- package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-workflow.md +16 -0
- package/dist/agentic/shared/ai/skills/om-evolve-harness/references/knowledge-change.md +133 -0
- package/dist/agentic/shared/ai/skills/om-evolve-harness/references/owner-selection.md +17 -0
- package/dist/agentic/shared/ai/skills/om-framework-context/SKILL.md +25 -0
- package/dist/agentic/shared/ai/skills/om-framework-context/references/resolver-procedure.md +11 -0
- package/dist/agentic/shared/ai/skills/om-framework-context/references/skew-and-escalation.md +13 -0
- package/dist/agentic/shared/ai/skills/om-help/SKILL.md +15 -97
- package/dist/agentic/shared/ai/skills/om-help/references/delivery-workflows.md +18 -0
- package/dist/agentic/shared/ai/skills/om-help/references/task-families.md +20 -0
- package/dist/agentic/shared/ai/skills/om-implement-spec/SKILL.md +33 -152
- package/dist/agentic/shared/ai/skills/om-implement-spec/references/phases-and-gates.md +33 -0
- package/dist/agentic/shared/ai/skills/om-implement-spec/references/planning-and-progress.md +40 -0
- package/dist/agentic/shared/ai/skills/om-implement-spec/references/report-templates.md +27 -0
- package/dist/agentic/shared/ai/skills/om-implement-spec/references/spec-resolution.md +24 -0
- package/dist/agentic/shared/ai/skills/om-integration-builder/SKILL.md +25 -708
- package/dist/agentic/shared/ai/skills/om-integration-builder/references/package-and-activation.md +16 -0
- package/dist/agentic/shared/ai/skills/om-integration-builder/references/provider-families.md +25 -0
- package/dist/agentic/shared/ai/skills/om-integration-builder/references/security-and-reliability.md +15 -0
- package/dist/agentic/shared/ai/skills/om-integration-builder/references/sync-and-files.md +12 -0
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/SKILL.md +34 -0
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/agents/openai.yaml +4 -0
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/agentic-setup.md +11 -0
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/input-normalization.md +31 -0
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/judge-workflow.md +39 -0
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/report-template.md +43 -0
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/rules.md +11 -0
- package/dist/agentic/shared/ai/skills/om-module-scaffold/SKILL.md +20 -820
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/api-and-domain.md +32 -0
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/business-one-shot-blueprints.md +172 -0
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/data-and-migrations.md +14 -0
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/discovery-surface-catalog.md +83 -0
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/module-surfaces.md +40 -0
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/planning.md +11 -0
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/runtime-cache-and-queues.md +22 -0
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/verification.md +13 -0
- package/dist/agentic/shared/ai/skills/om-share-this-session/SKILL.md +41 -0
- package/dist/agentic/shared/ai/skills/om-share-this-session/references/agentic-setup.md +19 -0
- package/dist/agentic/shared/ai/skills/om-share-this-session/references/bundle-preparation.md +44 -0
- package/dist/agentic/shared/ai/skills/om-share-this-session/references/consent-and-review.md +39 -0
- package/dist/agentic/shared/ai/skills/om-share-this-session/references/publication.md +20 -0
- package/dist/agentic/shared/ai/skills/om-share-this-session/references/report-templates.md +57 -0
- package/dist/agentic/shared/ai/skills/om-share-this-session/references/rules.md +11 -0
- package/dist/agentic/shared/ai/skills/om-share-this-session/scripts/prepare-share-bundle.mjs +486 -0
- package/dist/agentic/shared/ai/skills/om-system-extension/SKILL.md +20 -843
- package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-branches.md +42 -0
- package/dist/agentic/shared/ai/skills/om-system-extension/references/mechanism-selector.md +27 -0
- package/dist/agentic/shared/ai/skills/om-system-extension/references/read-write-roundtrip.md +13 -0
- package/dist/agentic/shared/ai/skills/om-system-extension/references/unified-overrides.md +43 -0
- package/dist/agentic/shared/ai/skills/om-system-extension/references/verification.md +11 -0
- package/dist/agentic/shared/ai/skills/om-trim-unused-modules/SKILL.md +15 -63
- package/dist/agentic/shared/ai/skills/om-trim-unused-modules/references/dependency-and-redirect.md +15 -0
- package/dist/agentic/shared/ai/skills/om-troubleshooter/SKILL.md +17 -467
- package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnosis-map.md +29 -0
- package/dist/agentic/shared/ai/skills/om-troubleshooter/references/regression-oracles.md +14 -0
- package/dist/agentic/shared/ai/skills/tiers.json +143 -21
- package/dist/agentic/shared/ai/skills/tiers.schema.json +52 -40
- package/dist/agentic/shared/ai/specs/2026-08-06-reference-module-activation.md +248 -0
- package/dist/agentic/shared/ai/specs/README.md +4 -3
- package/dist/agentic/shared/ai/specs/SPEC-000-template.md +226 -10
- package/dist/agentic/shared/ai/trackers/github.md +108 -4
- package/dist/agentic/shared/scripts/agent-harness-tool-server.mjs +224 -0
- package/dist/agentic/shared/scripts/check-lessons.mjs +178 -0
- package/dist/agentic/shared/scripts/evaluate-agent-harness.mjs +3812 -0
- package/dist/agentic/shared/scripts/execution-sandbox.mjs +329 -0
- package/dist/agentic/shared/scripts/framework-context.mjs +888 -0
- package/dist/agentic/shared/scripts/install-skills.mjs +1029 -0
- package/dist/agentic/shared/scripts/install-skills.sh +4 -694
- package/dist/agentic/shared/scripts/prepare-agent-harness-fixture.mjs +222 -0
- package/dist/agentic/shared/scripts/run-agent-harness-release.mjs +1774 -0
- package/dist/agentic/shared/scripts/validate-knowledge-change.mjs +1172 -0
- package/dist/bin.js +32 -34
- package/dist/bin.js.map +2 -2
- package/dist/lib/__integration__/TC-INT-008.spec.js +88 -8
- package/dist/lib/__integration__/TC-INT-008.spec.js.map +2 -2
- package/dist/lib/agentic-init.js +13 -3
- package/dist/lib/agentic-init.js.map +2 -2
- package/dist/lib/agentic-setup.js +434 -186
- package/dist/lib/agentic-setup.js.map +2 -2
- package/dist/lib/cli-bootstrap-mode.js +32 -0
- package/dist/lib/cli-bootstrap-mode.js.map +7 -0
- package/dist/lib/dev-supervisor-manifest.js +105 -0
- package/dist/lib/dev-supervisor-manifest.js.map +7 -0
- package/dist/lib/generate-watch-events.js +15 -0
- package/dist/lib/generate-watch-events.js.map +2 -2
- package/dist/lib/generators/entity-ids.js +11 -24
- package/dist/lib/generators/entity-ids.js.map +2 -2
- package/dist/lib/generators/extensions/injection-widgets.js +20 -0
- package/dist/lib/generators/extensions/injection-widgets.js.map +2 -2
- package/dist/lib/generators/module-di.js +10 -23
- package/dist/lib/generators/module-di.js.map +2 -2
- package/dist/lib/generators/module-entities.js +10 -23
- package/dist/lib/generators/module-entities.js.map +2 -2
- package/dist/lib/generators/module-extension-facts.js +1882 -0
- package/dist/lib/generators/module-extension-facts.js.map +7 -0
- package/dist/lib/generators/module-fact-sources.js +70 -0
- package/dist/lib/generators/module-fact-sources.js.map +7 -0
- package/dist/lib/generators/module-facts-discovery.js +64 -7
- package/dist/lib/generators/module-facts-discovery.js.map +2 -2
- package/dist/lib/generators/module-facts.js +1913 -52
- package/dist/lib/generators/module-facts.js.map +3 -3
- package/dist/lib/generators/module-override-targets.js +712 -0
- package/dist/lib/generators/module-override-targets.js.map +7 -0
- package/dist/lib/generators/module-registry.js +405 -36
- package/dist/lib/generators/module-registry.js.map +2 -2
- package/dist/lib/generators/route-manifest-shards.js +170 -0
- package/dist/lib/generators/route-manifest-shards.js.map +7 -0
- package/dist/lib/in-process-generate-watcher.js +12 -3
- package/dist/lib/in-process-generate-watcher.js.map +2 -2
- package/dist/lib/load-env.js +24 -0
- package/dist/lib/load-env.js.map +7 -0
- package/dist/lib/queue-worker-supervisor.js.map +2 -2
- package/dist/lib/resolver.js +28 -3
- package/dist/lib/resolver.js.map +2 -2
- package/dist/lib/telemetry-init.js +358 -0
- package/dist/lib/telemetry-init.js.map +7 -0
- package/dist/lib/testing/integration.js +5 -2
- package/dist/lib/testing/integration.js.map +2 -2
- package/dist/lib/utils.js +15 -8
- package/dist/lib/utils.js.map +2 -2
- package/dist/mercato.js +35 -9
- package/dist/mercato.js.map +2 -2
- package/package.json +13 -11
- package/src/__tests__/mercato.test.ts +144 -0
- package/src/bin.ts +60 -42
- package/src/lib/__integration__/TC-INT-008.spec.ts +107 -9
- package/src/lib/__tests__/agentic-init.test.ts +41 -5
- package/src/lib/__tests__/agentic-setup.ownership.test.ts +386 -0
- package/src/lib/__tests__/cli-bootstrap-mode.test.ts +22 -0
- package/src/lib/__tests__/content-stable-writes.test.ts +74 -0
- package/src/lib/__tests__/dev-supervisor-manifest.test.ts +131 -0
- package/src/lib/__tests__/generate-watch-events.test.ts +76 -0
- package/src/lib/__tests__/in-process-generate-watcher.test.ts +52 -1
- package/src/lib/__tests__/injection-widget-id-validation.test.ts +184 -0
- package/src/lib/__tests__/load-env.test.ts +65 -0
- package/src/lib/__tests__/queue-worker-supervisor.test.ts +33 -0
- package/src/lib/__tests__/resolver.enterprise.test.ts +50 -0
- package/src/lib/__tests__/telemetry-init.test.ts +298 -0
- package/src/lib/agentic-init.ts +15 -3
- package/src/lib/agentic-setup.ts +573 -222
- package/src/lib/cli-bootstrap-mode.ts +31 -0
- package/src/lib/dev-supervisor-manifest.ts +142 -0
- package/src/lib/generate-watch-events.ts +14 -0
- package/src/lib/generators/__tests__/activation-adapter-coverage.test.ts +92 -0
- package/src/lib/generators/__tests__/entity-ids.test.ts +32 -0
- package/src/lib/generators/__tests__/helpers/override-target-diagnostic-fixtures.ts +150 -0
- package/src/lib/generators/__tests__/module-extension-activations.test.ts +762 -0
- package/src/lib/generators/__tests__/module-extension-facts.test.ts +603 -0
- package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +139 -2
- package/src/lib/generators/__tests__/module-facts.computed-di-token.test.ts +88 -0
- package/src/lib/generators/__tests__/module-facts.customers.fixture.test.ts +1 -0
- package/src/lib/generators/__tests__/module-facts.discovery.test.ts +54 -10
- package/src/lib/generators/__tests__/module-facts.example-fact-coverage.test.ts +414 -0
- package/src/lib/generators/__tests__/module-facts.extension-hosts.test.ts +108 -0
- package/src/lib/generators/__tests__/module-facts.local-reference.test.ts +393 -0
- package/src/lib/generators/__tests__/module-facts.owned-contracts.test.ts +561 -0
- package/src/lib/generators/__tests__/module-facts.search-entity-id.test.ts +151 -0
- package/src/lib/generators/__tests__/module-facts.source-links.test.ts +160 -0
- package/src/lib/generators/__tests__/module-override-targets.diagnostic-wiring.test.ts +92 -0
- package/src/lib/generators/__tests__/module-override-targets.roundtrip.test.ts +149 -0
- package/src/lib/generators/__tests__/module-override-targets.test.ts +328 -0
- package/src/lib/generators/__tests__/module-override-targets.unknown-mode.test.ts +117 -0
- package/src/lib/generators/__tests__/module-subset.test.ts +266 -0
- package/src/lib/generators/__tests__/output-snapshots.test.ts +3 -3
- package/src/lib/generators/__tests__/route-manifest-shards.test.ts +62 -0
- package/src/lib/generators/entity-ids.ts +10 -26
- package/src/lib/generators/extensions/injection-widgets.ts +51 -0
- package/src/lib/generators/module-di.ts +9 -25
- package/src/lib/generators/module-entities.ts +9 -25
- package/src/lib/generators/module-extension-facts.ts +2472 -0
- package/src/lib/generators/module-fact-sources.ts +108 -0
- package/src/lib/generators/module-facts-discovery.ts +104 -7
- package/src/lib/generators/module-facts.ts +2747 -138
- package/src/lib/generators/module-override-targets.ts +907 -0
- package/src/lib/generators/module-registry.ts +448 -35
- package/src/lib/generators/route-manifest-shards.ts +146 -0
- package/src/lib/in-process-generate-watcher.ts +13 -4
- package/src/lib/load-env.ts +30 -0
- package/src/lib/queue-worker-supervisor.ts +7 -2
- package/src/lib/resolver.ts +34 -3
- package/src/lib/telemetry-init.ts +461 -0
- package/src/lib/testing/__tests__/integration.test.ts +4 -0
- package/src/lib/testing/integration.ts +5 -2
- package/src/lib/utils.ts +24 -16
- package/src/mercato.ts +46 -9
- package/dist/agentic/guides/cache.md +0 -45
- package/dist/agentic/guides/core.md +0 -453
- package/dist/agentic/guides/events.md +0 -117
- package/dist/agentic/guides/queue.md +0 -53
- package/dist/agentic/guides/search.md +0 -115
- package/dist/agentic/guides/shared.md +0 -108
- package/dist/agentic/guides/ui.md +0 -347
- package/dist/agentic/shared/ai/skills/om-auto-continue-pr-loop/SKILL.md +0 -27
- package/dist/agentic/shared/ai/skills/om-auto-create-pr-loop/SKILL.md +0 -27
- package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/ui-components.md +0 -260
- package/dist/agentic/shared/ai/skills/om-data-model-design/references/mikro-orm-cheatsheet.md +0 -146
- package/dist/agentic/shared/ai/skills/om-help/references/skills-catalog.md +0 -84
- package/dist/agentic/shared/ai/skills/om-help/references/workflow-sequences.md +0 -193
- package/dist/agentic/shared/ai/skills/om-integration-builder/STANDALONE.md +0 -34
- package/dist/agentic/shared/ai/skills/om-integration-builder/references/adapter-contracts.md +0 -762
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/naming-conventions.md +0 -50
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/navigation-patterns.md +0 -97
- package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-contracts.md +0 -271
- package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnostic-commands.md +0 -101
|
@@ -578,6 +578,66 @@ export { POST, PUT, DELETE }
|
|
|
578
578
|
expect(apiRoutes).toContain('methods: ["GET", "POST", "PUT", "DELETE"]')
|
|
579
579
|
})
|
|
580
580
|
|
|
581
|
+
it('partitions API and backend loader graphs while preserving legacy manifests', async () => {
|
|
582
|
+
scaffoldModule(tmpDir, 'customers', 'pkg', ['api/people/route.ts', 'backend/people/page.tsx'])
|
|
583
|
+
scaffoldModule(tmpDir, 'sales', 'pkg', ['api/orders/route.ts', 'backend/orders/page.tsx'])
|
|
584
|
+
touchFile(
|
|
585
|
+
path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'customers', 'api', 'people', 'route.ts'),
|
|
586
|
+
`export const metadata = { path: '/customers/people' }\nexport function GET() { return null }\n`,
|
|
587
|
+
)
|
|
588
|
+
touchFile(
|
|
589
|
+
path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'customers', 'backend', 'people', 'page.tsx'),
|
|
590
|
+
`export const metadata = { pageTitle: 'People' }\nexport default function Page() { return null }\n`,
|
|
591
|
+
)
|
|
592
|
+
touchFile(
|
|
593
|
+
path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'sales', 'api', 'orders', 'route.ts'),
|
|
594
|
+
`export const metadata = { path: '/sales/orders' }\nexport function POST() { return null }\n`,
|
|
595
|
+
)
|
|
596
|
+
touchFile(
|
|
597
|
+
path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'sales', 'backend', 'orders', 'page.tsx'),
|
|
598
|
+
`export const metadata = { pageTitle: 'Orders' }\nexport default function Page() { return null }\n`,
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
const resolver = createMockResolver(tmpDir, [
|
|
602
|
+
{ id: 'customers', from: '@open-mercato/core' },
|
|
603
|
+
{ id: 'sales', from: '@open-mercato/core' },
|
|
604
|
+
])
|
|
605
|
+
await generateModuleRegistry({ resolver, quiet: true })
|
|
606
|
+
|
|
607
|
+
const outputDir = path.join(tmpDir, 'output', 'generated')
|
|
608
|
+
const generatedNames = fs.readdirSync(outputDir)
|
|
609
|
+
const apiShardNames = generatedNames.filter((name) => /^api-route-shard\..+\.generated\.ts$/.test(name))
|
|
610
|
+
const backendShardNames = generatedNames.filter((name) => /^backend-route-shard\..+\.generated\.ts$/.test(name))
|
|
611
|
+
const apiShards = apiShardNames.map((name) => fs.readFileSync(path.join(outputDir, name), 'utf8'))
|
|
612
|
+
const backendShards = backendShardNames.map((name) => fs.readFileSync(path.join(outputDir, name), 'utf8'))
|
|
613
|
+
|
|
614
|
+
expect(apiShardNames).toHaveLength(2)
|
|
615
|
+
expect(backendShardNames).toHaveLength(2)
|
|
616
|
+
expect(apiShards.filter((source) => source.includes('/customers/people'))).toHaveLength(1)
|
|
617
|
+
expect(apiShards.filter((source) => source.includes('/sales/orders'))).toHaveLength(1)
|
|
618
|
+
expect(backendShards.filter((source) => source.includes('/backend/people'))).toHaveLength(1)
|
|
619
|
+
expect(backendShards.filter((source) => source.includes('/backend/orders'))).toHaveLength(1)
|
|
620
|
+
|
|
621
|
+
const apiLoader = readGenerated(tmpDir, 'api-route-shards.generated.ts')!
|
|
622
|
+
const backendLoader = readGenerated(tmpDir, 'backend-route-shards.generated.ts')!
|
|
623
|
+
expect(apiLoader).not.toContain('@open-mercato/core/modules/customers/api')
|
|
624
|
+
expect(backendLoader).not.toContain('@open-mercato/core/modules/customers/backend')
|
|
625
|
+
|
|
626
|
+
const apiMetadata = readGenerated(tmpDir, 'api-route-metadata.generated.ts')!
|
|
627
|
+
const backendMetadata = readGenerated(tmpDir, 'backend-route-metadata.generated.ts')!
|
|
628
|
+
expect(apiMetadata).not.toContain('load: async')
|
|
629
|
+
expect(backendMetadata).not.toContain('load: async')
|
|
630
|
+
expect(readGenerated(tmpDir, 'api-routes.generated.ts')).toContain('/customers/people')
|
|
631
|
+
expect(readGenerated(tmpDir, 'backend-routes.generated.ts')).toContain('/backend/orders')
|
|
632
|
+
|
|
633
|
+
const customersOnlyResolver = createMockResolver(tmpDir, [
|
|
634
|
+
{ id: 'customers', from: '@open-mercato/core' },
|
|
635
|
+
])
|
|
636
|
+
await generateModuleRegistry({ resolver: customersOnlyResolver, quiet: true })
|
|
637
|
+
const remainingApiShards = fs.readdirSync(outputDir).filter((name) => /^api-route-shard\..+\.generated\.ts$/.test(name))
|
|
638
|
+
expect(remainingApiShards).toHaveLength(1)
|
|
639
|
+
})
|
|
640
|
+
|
|
581
641
|
it('keeps all HTTP methods for route.ts files that destructure-export crud handlers', async () => {
|
|
582
642
|
scaffoldModule(tmpDir, 'example', 'pkg', ['api/todos/route.ts'])
|
|
583
643
|
touchFile(
|
|
@@ -702,6 +762,24 @@ export function GET() {
|
|
|
702
762
|
expect(output).toContain('features:')
|
|
703
763
|
})
|
|
704
764
|
|
|
765
|
+
it('normalizes singular app-owned integration exports without requiring plural properties', async () => {
|
|
766
|
+
touchFile(
|
|
767
|
+
path.join(tmpDir, 'app', 'src', 'modules', 'mailbox', 'integration.ts'),
|
|
768
|
+
"export const integration = { id: 'mailbox' }\nexport default integration\n",
|
|
769
|
+
)
|
|
770
|
+
const resolver = createMockResolver(tmpDir, [{ id: 'mailbox', from: '@app' }])
|
|
771
|
+
const result = await generateModuleRegistry({ resolver, quiet: true })
|
|
772
|
+
|
|
773
|
+
expect(result.errors).toEqual([])
|
|
774
|
+
for (const file of ['modules.generated.ts', 'modules.runtime.generated.ts']) {
|
|
775
|
+
const output = readGenerated(tmpDir, file)!
|
|
776
|
+
expect(output).toContain('as unknown as { integrations?:')
|
|
777
|
+
expect(output).toContain('.integration ? [')
|
|
778
|
+
expect(output).not.toMatch(/INTEGRATION_[A-Za-z0-9_]+\.integrations/)
|
|
779
|
+
expect(output).not.toMatch(/INTEGRATION_[A-Za-z0-9_]+\.bundles/)
|
|
780
|
+
}
|
|
781
|
+
})
|
|
782
|
+
|
|
705
783
|
it('escapes generated module specifiers for lazy imports', async () => {
|
|
706
784
|
touchFile(
|
|
707
785
|
path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'safe_mod', 'subscribers', `o'hare.ts`),
|
|
@@ -1044,6 +1122,85 @@ export default async function handle(): Promise<void> {}
|
|
|
1044
1122
|
expect(output).toContain("workers:")
|
|
1045
1123
|
})
|
|
1046
1124
|
|
|
1125
|
+
it('writes a content-stable primitive dev supervisor manifest', async () => {
|
|
1126
|
+
scaffoldModule(tmpDir, 'events', 'pkg', ['workers/dispatch.ts'])
|
|
1127
|
+
scaffoldModule(tmpDir, 'scheduler', 'pkg', ['cli.ts'])
|
|
1128
|
+
touchFile(
|
|
1129
|
+
path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'events', 'workers', 'dispatch.ts'),
|
|
1130
|
+
`export const metadata = { id: 'events:dispatch', queue: 'events', concurrency: 3 }\nexport default async function dispatch() {}\n`,
|
|
1131
|
+
)
|
|
1132
|
+
touchFile(
|
|
1133
|
+
path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'scheduler', 'cli.ts'),
|
|
1134
|
+
`export default [{ command: 'start', run() {} }]\n`,
|
|
1135
|
+
)
|
|
1136
|
+
const resolver = createMockResolver(tmpDir, [
|
|
1137
|
+
{ id: 'events', from: '@open-mercato/core' },
|
|
1138
|
+
{ id: 'scheduler', from: '@open-mercato/core' },
|
|
1139
|
+
])
|
|
1140
|
+
|
|
1141
|
+
const first = await generateModuleRegistryCli({ resolver, quiet: true })
|
|
1142
|
+
const manifestPath = path.join(tmpDir, 'output', 'generated', 'dev-supervisor.generated.json')
|
|
1143
|
+
expect(JSON.parse(fs.readFileSync(manifestPath, 'utf8'))).toEqual({
|
|
1144
|
+
version: 1,
|
|
1145
|
+
workers: [{
|
|
1146
|
+
id: 'events:dispatch',
|
|
1147
|
+
moduleId: 'events',
|
|
1148
|
+
queue: 'events',
|
|
1149
|
+
concurrency: 3,
|
|
1150
|
+
}],
|
|
1151
|
+
schedulerStartStatus: 'ok',
|
|
1152
|
+
requiresFullBootstrap: false,
|
|
1153
|
+
})
|
|
1154
|
+
expect(first.filesWritten).toContain(manifestPath)
|
|
1155
|
+
|
|
1156
|
+
const second = await generateModuleRegistryCli({ resolver, quiet: true })
|
|
1157
|
+
expect(second.filesUnchanged).toContain(manifestPath)
|
|
1158
|
+
expect(second.filesWritten).not.toContain(manifestPath)
|
|
1159
|
+
})
|
|
1160
|
+
|
|
1161
|
+
it('falls back to full bootstrap for worker overrides and computed scheduler commands', async () => {
|
|
1162
|
+
scaffoldModule(tmpDir, 'events', 'pkg', ['workers/dispatch.ts'])
|
|
1163
|
+
scaffoldModule(tmpDir, 'scheduler', 'pkg', ['cli.ts'])
|
|
1164
|
+
touchFile(
|
|
1165
|
+
path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'events', 'workers', 'dispatch.ts'),
|
|
1166
|
+
`export const metadata = { id: 'events:dispatch', queue: 'events', concurrency: 3 }\nexport default async function dispatch() {}\n`,
|
|
1167
|
+
)
|
|
1168
|
+
touchFile(
|
|
1169
|
+
path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'scheduler', 'cli.ts'),
|
|
1170
|
+
`const command = 'start'\nexport default [{ command, run() {} }]\n`,
|
|
1171
|
+
)
|
|
1172
|
+
const resolver = createMockResolver(tmpDir, [
|
|
1173
|
+
{ id: 'events', from: '@open-mercato/core', devSupervisorRequiresFullBootstrap: true },
|
|
1174
|
+
{ id: 'scheduler', from: '@open-mercato/core' },
|
|
1175
|
+
])
|
|
1176
|
+
|
|
1177
|
+
await generateModuleRegistryCli({ resolver, quiet: true })
|
|
1178
|
+
const manifestPath = path.join(tmpDir, 'output', 'generated', 'dev-supervisor.generated.json')
|
|
1179
|
+
expect(JSON.parse(fs.readFileSync(manifestPath, 'utf8'))).toMatchObject({
|
|
1180
|
+
schedulerStartStatus: 'missing-command',
|
|
1181
|
+
requiresFullBootstrap: true,
|
|
1182
|
+
})
|
|
1183
|
+
})
|
|
1184
|
+
|
|
1185
|
+
it('falls back to full bootstrap when app code registers programmatic worker overrides', async () => {
|
|
1186
|
+
scaffoldModule(tmpDir, 'events', 'pkg', ['workers/dispatch.ts'])
|
|
1187
|
+
touchFile(
|
|
1188
|
+
path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'events', 'workers', 'dispatch.ts'),
|
|
1189
|
+
`export const metadata = { id: 'events:dispatch', queue: 'events', concurrency: 3 }\nexport default async function dispatch() {}\n`,
|
|
1190
|
+
)
|
|
1191
|
+
touchFile(
|
|
1192
|
+
path.join(tmpDir, 'app', 'src', 'bootstrap-overrides.ts'),
|
|
1193
|
+
`import { applyWorkerOverrides as overrideWorkers } from '@open-mercato/shared/modules/overrides'\noverrideWorkers({ 'events:dispatch': null })\n`,
|
|
1194
|
+
)
|
|
1195
|
+
const resolver = createMockResolver(tmpDir, [
|
|
1196
|
+
{ id: 'events', from: '@open-mercato/core' },
|
|
1197
|
+
])
|
|
1198
|
+
|
|
1199
|
+
await generateModuleRegistryCli({ resolver, quiet: true })
|
|
1200
|
+
const manifestPath = path.join(tmpDir, 'output', 'generated', 'dev-supervisor.generated.json')
|
|
1201
|
+
expect(JSON.parse(fs.readFileSync(manifestPath, 'utf8')).requiresFullBootstrap).toBe(true)
|
|
1202
|
+
})
|
|
1203
|
+
|
|
1047
1204
|
it('handles disabling a module that was previously enabled', async () => {
|
|
1048
1205
|
scaffoldModule(tmpDir, 'keep_mod', 'pkg', [
|
|
1049
1206
|
'subscribers/handler.ts',
|
|
@@ -1462,6 +1619,115 @@ describe('generateModuleRegistryApp with module subsets', () => {
|
|
|
1462
1619
|
const bootstrapOutput = readGenerated(tmpDir, 'modules.bootstrap.generated.ts')
|
|
1463
1620
|
expect(bootstrapOutput).not.toBeNull()
|
|
1464
1621
|
expect(bootstrapOutput).toContain('export const modules: Module[] = [')
|
|
1622
|
+
|
|
1623
|
+
const i18nLoadersOutput = readGenerated(tmpDir, 'modules.i18n.loaders.generated.ts')
|
|
1624
|
+
expect(i18nLoadersOutput).toContain('export async function loadI18nModules')
|
|
1625
|
+
expect(i18nLoadersOutput).toContain('default:')
|
|
1626
|
+
expect(i18nLoadersOutput).toContain('return []')
|
|
1627
|
+
})
|
|
1628
|
+
|
|
1629
|
+
it('keeps the legacy i18n registry while emitting deterministic locale-specific shards', async () => {
|
|
1630
|
+
touchFile(
|
|
1631
|
+
path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'localized', 'i18n', 'en.json'),
|
|
1632
|
+
JSON.stringify({ localized: { title: 'Core title' } }),
|
|
1633
|
+
)
|
|
1634
|
+
touchFile(
|
|
1635
|
+
path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'localized', 'i18n', 'pl.json'),
|
|
1636
|
+
JSON.stringify({ localized: { title: 'Tytuł' } }),
|
|
1637
|
+
)
|
|
1638
|
+
touchFile(
|
|
1639
|
+
path.join(tmpDir, 'app', 'src', 'modules', 'localized', 'i18n', 'en.json'),
|
|
1640
|
+
JSON.stringify({ localized: { title: 'App title' } }),
|
|
1641
|
+
)
|
|
1642
|
+
touchFile(
|
|
1643
|
+
path.join(tmpDir, 'packages', 'core', 'src', 'modules', 'localized', 'i18n', '.hardcoded-allowlist.json'),
|
|
1644
|
+
JSON.stringify({ allow: ['framework chrome'] }),
|
|
1645
|
+
)
|
|
1646
|
+
|
|
1647
|
+
const resolver = createMockResolver(tmpDir, [
|
|
1648
|
+
{ id: 'localized', from: '@open-mercato/core' },
|
|
1649
|
+
])
|
|
1650
|
+
const result = await generateModuleRegistryApp({ resolver, quiet: true })
|
|
1651
|
+
|
|
1652
|
+
expect(result.errors).toEqual([])
|
|
1653
|
+
|
|
1654
|
+
const legacyI18nOutput = readGenerated(tmpDir, 'modules.i18n.generated.ts')!
|
|
1655
|
+
expect(legacyI18nOutput).toContain('@open-mercato/core/modules/localized/i18n/en.json')
|
|
1656
|
+
expect(legacyI18nOutput).toContain('@/modules/localized/i18n/en.json')
|
|
1657
|
+
expect(legacyI18nOutput).toContain('@open-mercato/core/modules/localized/i18n/pl.json')
|
|
1658
|
+
expect(legacyI18nOutput).toContain('export const modules: Module[] = [')
|
|
1659
|
+
|
|
1660
|
+
const bootstrapOutput = readGenerated(tmpDir, 'modules.bootstrap.generated.ts')!
|
|
1661
|
+
expect(bootstrapOutput).not.toContain('/i18n/')
|
|
1662
|
+
expect(bootstrapOutput).not.toContain('translations:')
|
|
1663
|
+
|
|
1664
|
+
const loadersOutput = readGenerated(tmpDir, 'modules.i18n.loaders.generated.ts')!
|
|
1665
|
+
expect(loadersOutput).not.toContain('.json')
|
|
1666
|
+
expect(loadersOutput).toContain('case "en":')
|
|
1667
|
+
expect(loadersOutput).toContain('import("./modules.i18n.en.generated")')
|
|
1668
|
+
expect(loadersOutput).toContain('case "pl":')
|
|
1669
|
+
expect(loadersOutput).not.toContain('hardcoded-allowlist')
|
|
1670
|
+
expect(loadersOutput.indexOf('case "en":')).toBeLessThan(loadersOutput.indexOf('case "pl":'))
|
|
1671
|
+
|
|
1672
|
+
const enShard = readGenerated(tmpDir, 'modules.i18n.en.generated.ts')!
|
|
1673
|
+
expect(enShard).toContain('@open-mercato/core/modules/localized/i18n/en.json')
|
|
1674
|
+
expect(enShard).toContain('@/modules/localized/i18n/en.json')
|
|
1675
|
+
expect(enShard).not.toContain('/i18n/pl.json')
|
|
1676
|
+
expect(enShard).toContain('id: "localized"')
|
|
1677
|
+
expect(enShard).toContain('translations:')
|
|
1678
|
+
|
|
1679
|
+
const plShard = readGenerated(tmpDir, 'modules.i18n.pl.generated.ts')!
|
|
1680
|
+
expect(plShard).toContain('@open-mercato/core/modules/localized/i18n/pl.json')
|
|
1681
|
+
expect(plShard).not.toContain('/i18n/en.json')
|
|
1682
|
+
expect(plShard).not.toContain('@/modules/localized/i18n/pl.json')
|
|
1683
|
+
expect(readGenerated(tmpDir, 'modules.i18n._hardcoded-allowlist.generated.ts')).toBeNull()
|
|
1684
|
+
|
|
1685
|
+
const secondResult = await generateModuleRegistryApp({ resolver, quiet: true })
|
|
1686
|
+
expect(secondResult.filesWritten).toEqual([])
|
|
1687
|
+
|
|
1688
|
+
fs.rmSync(path.join(
|
|
1689
|
+
tmpDir,
|
|
1690
|
+
'packages',
|
|
1691
|
+
'core',
|
|
1692
|
+
'src',
|
|
1693
|
+
'modules',
|
|
1694
|
+
'localized',
|
|
1695
|
+
'i18n',
|
|
1696
|
+
'pl.json',
|
|
1697
|
+
))
|
|
1698
|
+
await generateModuleRegistryApp({ resolver, quiet: true })
|
|
1699
|
+
expect(readGenerated(tmpDir, 'modules.i18n.pl.generated.ts')).toBeNull()
|
|
1700
|
+
expect(fs.existsSync(path.join(
|
|
1701
|
+
tmpDir,
|
|
1702
|
+
'output',
|
|
1703
|
+
'generated',
|
|
1704
|
+
'modules.i18n.pl.generated.checksum',
|
|
1705
|
+
))).toBe(false)
|
|
1706
|
+
expect(readGenerated(tmpDir, 'modules.i18n.loaders.generated.ts')).not.toContain('case "pl":')
|
|
1707
|
+
})
|
|
1708
|
+
|
|
1709
|
+
it('uses standalone source-mirror translations in locale-specific shards', async () => {
|
|
1710
|
+
touchFile(
|
|
1711
|
+
path.join(tmpDir, 'node_modules', 'pkg', 'src', 'modules', 'customers', 'i18n', 'en.json'),
|
|
1712
|
+
JSON.stringify({ customers: { people: { title: 'People' } } }),
|
|
1713
|
+
)
|
|
1714
|
+
touchFile(
|
|
1715
|
+
path.join(tmpDir, 'node_modules', 'pkg', 'dist', 'modules', 'customers', 'index.js'),
|
|
1716
|
+
'export const metadata = { id: "customers" }\n',
|
|
1717
|
+
)
|
|
1718
|
+
|
|
1719
|
+
const resolver = createStandaloneMockResolver(tmpDir, [
|
|
1720
|
+
{ id: 'customers', from: '@open-mercato/core' },
|
|
1721
|
+
])
|
|
1722
|
+
const result = await generateModuleRegistryApp({ resolver, quiet: true })
|
|
1723
|
+
|
|
1724
|
+
expect(result.errors).toEqual([])
|
|
1725
|
+
const shard = fs.readFileSync(
|
|
1726
|
+
path.join(tmpDir, '.mercato', 'generated', 'modules.i18n.en.generated.ts'),
|
|
1727
|
+
'utf8',
|
|
1728
|
+
)
|
|
1729
|
+
expect(shard).toContain('@open-mercato/core/modules/customers/i18n/en.json')
|
|
1730
|
+
expect(shard).toContain('id: "customers"')
|
|
1465
1731
|
})
|
|
1466
1732
|
|
|
1467
1733
|
it('app output excludes CLI commands and keeps lazy workers/subscribers', async () => {
|
|
@@ -829,12 +829,12 @@ describe('generator output compatibility', () => {
|
|
|
829
829
|
|
|
830
830
|
const content = readGenerated('bootstrap-registrations.generated.ts')
|
|
831
831
|
expect(content).not.toBeNull()
|
|
832
|
-
expect(content).toContain(`import {
|
|
832
|
+
expect(content).toContain(`import { backendRouteFacades } from "./backend-route-shards.generated"`)
|
|
833
833
|
expect(content).toContain(`import { frontendRoutes } from "./frontend-routes.generated"`)
|
|
834
834
|
expect(content).toContain(
|
|
835
835
|
`import { registerBackendRouteManifests, registerFrontendRouteManifests } from '@open-mercato/shared/modules/registry'`,
|
|
836
836
|
)
|
|
837
|
-
expect(content).toMatch(/export function runBootstrapRegistrations\(\): void \{[\s\S]*registerBackendRouteManifests\(
|
|
837
|
+
expect(content).toMatch(/export function runBootstrapRegistrations\(\): void \{[\s\S]*registerBackendRouteManifests\(backendRouteFacades\)[\s\S]*\}/)
|
|
838
838
|
expect(content).toMatch(/export function runBootstrapRegistrations\(\): void \{[\s\S]*registerFrontendRouteManifests\(frontendRoutes\)[\s\S]*\}/)
|
|
839
839
|
})
|
|
840
840
|
|
|
@@ -846,6 +846,6 @@ describe('generator output compatibility', () => {
|
|
|
846
846
|
|
|
847
847
|
const content = readGenerated('bootstrap-registrations.generated.ts')
|
|
848
848
|
expect(content).not.toBeNull()
|
|
849
|
-
expect(content).toContain(`registerBackendRouteManifests(
|
|
849
|
+
expect(content).toContain(`registerBackendRouteManifests(backendRouteFacades)`)
|
|
850
850
|
})
|
|
851
851
|
})
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getRouteManifestShardKey,
|
|
3
|
+
renderRouteManifestShardOutputs,
|
|
4
|
+
renderRouteMetadataOutput,
|
|
5
|
+
} from '../route-manifest-shards'
|
|
6
|
+
|
|
7
|
+
describe('route manifest shards', () => {
|
|
8
|
+
it('groups static request prefixes and preserves dynamic-prefix fallbacks', () => {
|
|
9
|
+
expect(getRouteManifestShardKey('api', '/customers/[id]')).toBe('customers')
|
|
10
|
+
expect(getRouteManifestShardKey('backend', '/backend/sales/orders')).toBe('sales')
|
|
11
|
+
expect(getRouteManifestShardKey('backend', '/backend/[tenant]/settings')).toBe('__dynamic')
|
|
12
|
+
expect(getRouteManifestShardKey('api', '/')).toBe('__root')
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('renders a small loader graph with exact and dynamic shards', () => {
|
|
16
|
+
const outputs = renderRouteManifestShardOutputs('api', [
|
|
17
|
+
{ path: '/customers', declaration: `{ moduleId: 'customers' } as any` },
|
|
18
|
+
{ path: '/sales/orders', declaration: `{ moduleId: 'sales' } as any` },
|
|
19
|
+
{ path: '/[module]/health', declaration: `{ moduleId: 'dynamic' } as any` },
|
|
20
|
+
])
|
|
21
|
+
const loader = outputs.find((entry) => entry.fileName === 'api-route-shards.generated.ts')?.content ?? ''
|
|
22
|
+
|
|
23
|
+
expect(outputs.filter((entry) => entry.fileName.includes('route-shard.'))).toHaveLength(3)
|
|
24
|
+
expect(loader).toContain(`case "customers":`)
|
|
25
|
+
expect(loader).toContain(`case "sales":`)
|
|
26
|
+
expect(loader).toContain(`const dynamicShard = await loadShard("__dynamic")`)
|
|
27
|
+
expect(loader).toContain(`export const apiRouteFacades`)
|
|
28
|
+
expect(loader).toContain(`loadApiRouteManifestByOrdinal(ordinal)`)
|
|
29
|
+
expect(loader).not.toContain("moduleId: 'customers'")
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('retains global route order when exact and dynamic entries interleave', () => {
|
|
33
|
+
const outputs = renderRouteManifestShardOutputs('api', [
|
|
34
|
+
{ path: '/customers/new', declaration: `{ path: '/customers/new' } as any` },
|
|
35
|
+
{ path: '/[module]/health', declaration: `{ path: '/[module]/health' } as any` },
|
|
36
|
+
{ path: '/customers/[id]', declaration: `{ path: '/customers/[id]' } as any` },
|
|
37
|
+
{ path: '/', declaration: `{ path: '/' } as any` },
|
|
38
|
+
])
|
|
39
|
+
const customerShard = outputs.find((entry) => entry.content.includes(`/customers/new`))?.content ?? ''
|
|
40
|
+
const dynamicShard = outputs.find((entry) => entry.content.includes(`/[module]/health`))?.content ?? ''
|
|
41
|
+
const loader = outputs.find((entry) => entry.fileName === 'api-route-shards.generated.ts')?.content ?? ''
|
|
42
|
+
|
|
43
|
+
expect(customerShard).toContain('export const routeOrders = [0, 2]')
|
|
44
|
+
expect(dynamicShard).toContain('export const routeOrders = [1]')
|
|
45
|
+
expect(loader).toContain(`case "__root":`)
|
|
46
|
+
expect(loader).toContain(`.sort((a, b) => a.order - b.order)`)
|
|
47
|
+
expect(loader).toContain(`const ROUTE_SHARD_KEYS = ["customers","__dynamic","customers","__root"]`)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('renders metadata without load functions while retaining metadata imports', () => {
|
|
51
|
+
const output = renderRouteMetadataOutput('backend', [{
|
|
52
|
+
path: '/backend/settings',
|
|
53
|
+
declaration: `{ moduleId: 'settings', ...resolvePageRouteMetadata('/backend/settings', META.metadata) }`,
|
|
54
|
+
imports: [`import * as META from '@app/settings/meta'`],
|
|
55
|
+
}])
|
|
56
|
+
|
|
57
|
+
expect(output.fileName).toBe('backend-route-metadata.generated.ts')
|
|
58
|
+
expect(output.content).toContain(`import * as META from '@app/settings/meta'`)
|
|
59
|
+
expect(output.content).toContain(`Omit<BackendRouteManifestEntry, 'load'>`)
|
|
60
|
+
expect(output.content).not.toContain('load: async')
|
|
61
|
+
})
|
|
62
|
+
})
|
|
@@ -5,17 +5,13 @@ import { VariableDeclarationKind } from 'ts-morph'
|
|
|
5
5
|
import type { PackageResolver, ModuleEntry } from '../resolver'
|
|
6
6
|
import { MODULE_CODE_EXTENSIONS } from './scanner'
|
|
7
7
|
import {
|
|
8
|
-
calculateChecksum,
|
|
9
|
-
readChecksumRecord,
|
|
10
|
-
writeChecksumRecord,
|
|
11
|
-
ensureDir,
|
|
12
8
|
toVar,
|
|
13
9
|
toSnake,
|
|
14
10
|
rimrafDir,
|
|
15
|
-
logGenerationResult,
|
|
16
|
-
writeIfChanged,
|
|
17
11
|
type GeneratorResult,
|
|
18
12
|
createGeneratorResult,
|
|
13
|
+
writeGeneratedFile,
|
|
14
|
+
writeIfChanged,
|
|
19
15
|
} from '../utils'
|
|
20
16
|
import { addAutoGeneratedComment, createGeneratedSourceFile, elementAccess, getSourceText, identifier, returnStatement } from './ast'
|
|
21
17
|
|
|
@@ -438,26 +434,14 @@ export async function generateEntityIds(options: EntityIdsOptions): Promise<Gene
|
|
|
438
434
|
})
|
|
439
435
|
const consolidatedSrc = getSourceText(consolidatedSourceFile)
|
|
440
436
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
if (shouldWrite) {
|
|
451
|
-
ensureDir(outFile)
|
|
452
|
-
fs.writeFileSync(outFile, consolidatedSrc)
|
|
453
|
-
writeChecksumRecord(checksumFile, { content: newChecksum, structure: '' })
|
|
454
|
-
result.filesWritten.push(outFile)
|
|
455
|
-
if (!quiet) {
|
|
456
|
-
logGenerationResult(path.relative(process.cwd(), outFile), true)
|
|
457
|
-
}
|
|
458
|
-
} else {
|
|
459
|
-
result.filesUnchanged.push(outFile)
|
|
460
|
-
}
|
|
437
|
+
writeGeneratedFile({
|
|
438
|
+
outFile,
|
|
439
|
+
checksumFile,
|
|
440
|
+
content: consolidatedSrc,
|
|
441
|
+
structureChecksum: '',
|
|
442
|
+
result,
|
|
443
|
+
quiet,
|
|
444
|
+
})
|
|
461
445
|
|
|
462
446
|
// Write per-group outputs
|
|
463
447
|
const groups = Object.keys(grouped) as GroupKey[]
|
|
@@ -183,9 +183,52 @@ function resolveScannedWidgetPath(roots: ModuleRoots, fromApp: boolean, relPath:
|
|
|
183
183
|
return path.join(base, 'widgets', 'injection', ...relPath.split('/'))
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
+
/**
|
|
187
|
+
* A widget's `metadata.id` and the injection-table entry that mounts it are two hand-typed string
|
|
188
|
+
* literals in two different files, and nothing links them. A typo in either fails silently: the spot
|
|
189
|
+
* resolves to no widget and nothing is logged, so it only surfaces as "why isn't my widget
|
|
190
|
+
* rendering" during manual QA.
|
|
191
|
+
*
|
|
192
|
+
* Reports table entries whose `widgetId` no scanned widget declares. Diagnostics only — generated
|
|
193
|
+
* output is unchanged and the build is not failed, since a table entry may legitimately point at a
|
|
194
|
+
* widget supplied by a module that is not enabled in this app.
|
|
195
|
+
*
|
|
196
|
+
* Deliberately one-directional. The mirror check ("a widget nothing references is never mounted")
|
|
197
|
+
* cannot be done reliably yet: `extractInjectionTableWidgetIds` only reads statically-analyzable
|
|
198
|
+
* object literals, so a table assembled by composition or computation contributes **no** visible
|
|
199
|
+
* ids — `example` exports `cond ? { ...a, ...b } : a` and `translations` exports
|
|
200
|
+
* `buildInjectionTable()`, and both would have every one of their widgets reported as unreferenced.
|
|
201
|
+
* Under-reporting is harmless in the direction implemented here (a reference the extractor never saw
|
|
202
|
+
* cannot produce a warning) but fatal in the other. Teaching the extractor to resolve spreads,
|
|
203
|
+
* conditionals and call expressions is the prerequisite for adding it.
|
|
204
|
+
*/
|
|
205
|
+
function reportUnresolvableWidgetReferences(
|
|
206
|
+
widgetEntries: Map<string, InjectionWidgetEntry>,
|
|
207
|
+
tableWidgetRefs: Map<string, Set<string>>,
|
|
208
|
+
): void {
|
|
209
|
+
const declaredIds = new Set<string>()
|
|
210
|
+
for (const entry of widgetEntries.values()) {
|
|
211
|
+
if (entry.widgetId) declaredIds.add(entry.widgetId)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
for (const [widgetId, modules] of Array.from(tableWidgetRefs.entries()).sort(([a], [b]) => a.localeCompare(b))) {
|
|
215
|
+
if (declaredIds.has(widgetId)) continue
|
|
216
|
+
const owners = Array.from(modules).sort((left, right) => left.localeCompare(right)).join(', ')
|
|
217
|
+
console.warn(
|
|
218
|
+
`[generate] ⚠ Injection table in "${owners}" references widget id "${widgetId}", but no scanned ` +
|
|
219
|
+
'widget declares it — that spot will render nothing. Check the id against the widget\'s ' +
|
|
220
|
+
'metadata.id, and that the widget lives in a file the scanner reads ' +
|
|
221
|
+
'(widgets/injection/**/widget.ts|tsx — note that widget.client.tsx alone is not scanned).',
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
186
226
|
export function createInjectionWidgetsExtension(): GeneratorExtension {
|
|
187
227
|
const widgetEntries = new Map<string, InjectionWidgetEntry>()
|
|
188
228
|
const tableEntries: InjectionTableEntry[] = []
|
|
229
|
+
// widgetId -> module ids whose injection table references it. Collected across every module so a
|
|
230
|
+
// table may legitimately reference a widget another module owns.
|
|
231
|
+
const tableWidgetRefs = new Map<string, Set<string>>()
|
|
189
232
|
|
|
190
233
|
return {
|
|
191
234
|
id: 'registry.injection-widgets',
|
|
@@ -217,6 +260,12 @@ export function createInjectionWidgetsExtension(): GeneratorExtension {
|
|
|
217
260
|
moduleWidgetEntries[0].widgetId = tableWidgetIds[0]
|
|
218
261
|
}
|
|
219
262
|
|
|
263
|
+
for (const widgetId of tableWidgetIds) {
|
|
264
|
+
const modules = tableWidgetRefs.get(widgetId) ?? new Set<string>()
|
|
265
|
+
modules.add(ctx.moduleId)
|
|
266
|
+
tableWidgetRefs.set(widgetId, modules)
|
|
267
|
+
}
|
|
268
|
+
|
|
220
269
|
for (const entry of moduleWidgetEntries) {
|
|
221
270
|
const existing = widgetEntries.get(entry.key)
|
|
222
271
|
if (!existing || (existing.source !== 'app' && entry.source === 'app')) {
|
|
@@ -233,6 +282,8 @@ export function createInjectionWidgetsExtension(): GeneratorExtension {
|
|
|
233
282
|
}
|
|
234
283
|
},
|
|
235
284
|
generateOutput() {
|
|
285
|
+
reportUnresolvableWidgetReferences(widgetEntries, tableWidgetRefs)
|
|
286
|
+
|
|
236
287
|
const widgetDecls = Array.from(widgetEntries.entries())
|
|
237
288
|
.sort(([left], [right]) => left.localeCompare(right))
|
|
238
289
|
.map(([, entry]) =>
|
|
@@ -4,14 +4,10 @@ import { StructureKind, VariableDeclarationKind } from 'ts-morph'
|
|
|
4
4
|
import type { PackageResolver } from '../resolver'
|
|
5
5
|
import { MODULE_CODE_EXTENSIONS } from './scanner'
|
|
6
6
|
import {
|
|
7
|
-
calculateChecksum,
|
|
8
|
-
readChecksumRecord,
|
|
9
|
-
writeChecksumRecord,
|
|
10
|
-
ensureDir,
|
|
11
7
|
toVar,
|
|
12
|
-
logGenerationResult,
|
|
13
8
|
type GeneratorResult,
|
|
14
9
|
createGeneratorResult,
|
|
10
|
+
writeGeneratedFile,
|
|
15
11
|
} from '../utils'
|
|
16
12
|
import {
|
|
17
13
|
addAutoGeneratedComment,
|
|
@@ -96,26 +92,14 @@ export async function generateModuleDi(options: ModuleDiOptions): Promise<Genera
|
|
|
96
92
|
})
|
|
97
93
|
const output = getSourceText(sourceFile)
|
|
98
94
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (shouldWrite) {
|
|
109
|
-
ensureDir(outFile)
|
|
110
|
-
fs.writeFileSync(outFile, output)
|
|
111
|
-
writeChecksumRecord(checksumFile, { content: newChecksum, structure: '' })
|
|
112
|
-
result.filesWritten.push(outFile)
|
|
113
|
-
if (!quiet) {
|
|
114
|
-
logGenerationResult(path.relative(process.cwd(), outFile), true)
|
|
115
|
-
}
|
|
116
|
-
} else {
|
|
117
|
-
result.filesUnchanged.push(outFile)
|
|
118
|
-
}
|
|
95
|
+
writeGeneratedFile({
|
|
96
|
+
outFile,
|
|
97
|
+
checksumFile,
|
|
98
|
+
content: output,
|
|
99
|
+
structureChecksum: '',
|
|
100
|
+
result,
|
|
101
|
+
quiet,
|
|
102
|
+
})
|
|
119
103
|
|
|
120
104
|
return result
|
|
121
105
|
}
|
|
@@ -4,14 +4,10 @@ import { StructureKind, VariableDeclarationKind } from 'ts-morph'
|
|
|
4
4
|
import type { PackageResolver } from '../resolver'
|
|
5
5
|
import { MODULE_CODE_EXTENSIONS, stripModuleCodeExtension } from './scanner'
|
|
6
6
|
import {
|
|
7
|
-
calculateChecksum,
|
|
8
|
-
readChecksumRecord,
|
|
9
|
-
writeChecksumRecord,
|
|
10
|
-
ensureDir,
|
|
11
7
|
toVar,
|
|
12
|
-
logGenerationResult,
|
|
13
8
|
type GeneratorResult,
|
|
14
9
|
createGeneratorResult,
|
|
10
|
+
writeGeneratedFile,
|
|
15
11
|
} from '../utils'
|
|
16
12
|
import {
|
|
17
13
|
addAutoGeneratedComment,
|
|
@@ -206,26 +202,14 @@ export async function generateModuleEntities(options: ModuleEntitiesOptions): Pr
|
|
|
206
202
|
})
|
|
207
203
|
const output = getSourceText(sourceFile)
|
|
208
204
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if (shouldWrite) {
|
|
219
|
-
ensureDir(outFile)
|
|
220
|
-
fs.writeFileSync(outFile, output)
|
|
221
|
-
writeChecksumRecord(checksumFile, { content: newChecksum, structure: '' })
|
|
222
|
-
result.filesWritten.push(outFile)
|
|
223
|
-
if (!quiet) {
|
|
224
|
-
logGenerationResult(path.relative(process.cwd(), outFile), true)
|
|
225
|
-
}
|
|
226
|
-
} else {
|
|
227
|
-
result.filesUnchanged.push(outFile)
|
|
228
|
-
}
|
|
205
|
+
writeGeneratedFile({
|
|
206
|
+
outFile,
|
|
207
|
+
checksumFile,
|
|
208
|
+
content: output,
|
|
209
|
+
structureChecksum: '',
|
|
210
|
+
result,
|
|
211
|
+
quiet,
|
|
212
|
+
})
|
|
229
213
|
|
|
230
214
|
return result
|
|
231
215
|
}
|