@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
|
@@ -1,7 +1,70 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import fs from "node:fs";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import ts from "typescript-js";
|
|
4
5
|
import { toSnake } from "../utils.js";
|
|
6
|
+
import { extractCommandIdsFromSource } from "./module-registry.js";
|
|
7
|
+
import { MODULE_CODE_EXTENSIONS } from "./scanner.js";
|
|
8
|
+
import {
|
|
9
|
+
assertNoUnresolvedExtensionTargets,
|
|
10
|
+
correlateIncomingExtensions,
|
|
11
|
+
correlateModuleExtensionFacts,
|
|
12
|
+
extractActivationTargetOwners,
|
|
13
|
+
extractKnownApiRouteIds,
|
|
14
|
+
extractKnownCommandIds,
|
|
15
|
+
extractModuleExtensionFacts,
|
|
16
|
+
renderFrameworkExtensionPointsMarkdown,
|
|
17
|
+
withModuleExtensionFactExtractionCache
|
|
18
|
+
} from "./module-extension-facts.js";
|
|
19
|
+
import {
|
|
20
|
+
ALL_OVERRIDE_DOMAINS,
|
|
21
|
+
MODULE_OVERRIDE_MODES,
|
|
22
|
+
MODULE_OVERRIDE_TARGET_DIAGNOSTIC_CODES,
|
|
23
|
+
MODULE_OVERRIDE_TARGET_NOTES,
|
|
24
|
+
collectModuleOverrideTargets
|
|
25
|
+
} from "./module-override-targets.js";
|
|
26
|
+
import { buildFactSourceLookup } from "./module-fact-sources.js";
|
|
27
|
+
import { appendLocalReferenceModuleSource } from "./module-facts-discovery.js";
|
|
28
|
+
const MODULE_FACT_SOURCE_KINDS = [
|
|
29
|
+
"module-metadata",
|
|
30
|
+
"entity",
|
|
31
|
+
"event",
|
|
32
|
+
"acl-feature",
|
|
33
|
+
"api-route",
|
|
34
|
+
"di-registration",
|
|
35
|
+
"search",
|
|
36
|
+
"vector",
|
|
37
|
+
"notification",
|
|
38
|
+
"cli-command",
|
|
39
|
+
"backend-page",
|
|
40
|
+
"frontend-page",
|
|
41
|
+
"ai-tool",
|
|
42
|
+
"ai-agent",
|
|
43
|
+
"ai-extension",
|
|
44
|
+
"command",
|
|
45
|
+
"subscriber",
|
|
46
|
+
"worker",
|
|
47
|
+
"page-middleware",
|
|
48
|
+
"setup",
|
|
49
|
+
"encryption",
|
|
50
|
+
"custom-entity",
|
|
51
|
+
"integration",
|
|
52
|
+
"generator-plugin",
|
|
53
|
+
"extension-host",
|
|
54
|
+
"extension-contribution"
|
|
55
|
+
];
|
|
56
|
+
const MODULE_OWNED_CONTRACT_KINDS = [
|
|
57
|
+
"module-metadata",
|
|
58
|
+
"command",
|
|
59
|
+
"worker",
|
|
60
|
+
"page-middleware",
|
|
61
|
+
"setup",
|
|
62
|
+
"encryption",
|
|
63
|
+
"di-registration",
|
|
64
|
+
"custom-entity",
|
|
65
|
+
"ai-extension",
|
|
66
|
+
"generator-plugin"
|
|
67
|
+
];
|
|
5
68
|
function readSourceFile(filePath) {
|
|
6
69
|
if (!fs.existsSync(filePath)) return null;
|
|
7
70
|
const source = fs.readFileSync(filePath, "utf8");
|
|
@@ -62,6 +125,34 @@ function classHasUpdatedAtColumn(node) {
|
|
|
62
125
|
}
|
|
63
126
|
return false;
|
|
64
127
|
}
|
|
128
|
+
function readStaticAccessPath(candidate) {
|
|
129
|
+
const access = unwrapExpression(candidate);
|
|
130
|
+
if (ts.isIdentifier(access)) return [access.text];
|
|
131
|
+
if (ts.isPropertyAccessExpression(access)) {
|
|
132
|
+
const parent = readStaticAccessPath(access.expression);
|
|
133
|
+
return parent ? [...parent, access.name.text] : null;
|
|
134
|
+
}
|
|
135
|
+
if (ts.isElementAccessExpression(access) && access.argumentExpression) {
|
|
136
|
+
const parent = readStaticAccessPath(access.expression);
|
|
137
|
+
const key = unwrapExpression(access.argumentExpression);
|
|
138
|
+
return parent && ts.isStringLiteralLike(key) ? [...parent, key.text] : null;
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
function resolveEntityIdReference(expression, initializers, visitedIdentifiers = /* @__PURE__ */ new Set()) {
|
|
143
|
+
const current = unwrapExpression(expression);
|
|
144
|
+
if (ts.isStringLiteralLike(current)) return current.text;
|
|
145
|
+
const accessPath = readStaticAccessPath(current);
|
|
146
|
+
if (accessPath?.length === 3 && accessPath[0] === "E") {
|
|
147
|
+
return `${accessPath[1]}:${accessPath[2]}`;
|
|
148
|
+
}
|
|
149
|
+
if (initializers && ts.isIdentifier(current) && !visitedIdentifiers.has(current.text)) {
|
|
150
|
+
visitedIdentifiers.add(current.text);
|
|
151
|
+
const declaration = initializers.get(current.text);
|
|
152
|
+
if (declaration) return resolveEntityIdReference(declaration, initializers, visitedIdentifiers);
|
|
153
|
+
}
|
|
154
|
+
return void 0;
|
|
155
|
+
}
|
|
65
156
|
function collectCustomFieldEntityIds(ceFilePath) {
|
|
66
157
|
const result = /* @__PURE__ */ new Set();
|
|
67
158
|
if (!ceFilePath) return result;
|
|
@@ -69,8 +160,11 @@ function collectCustomFieldEntityIds(ceFilePath) {
|
|
|
69
160
|
if (!sourceFile) return result;
|
|
70
161
|
const visit = (node) => {
|
|
71
162
|
if (ts.isObjectLiteralExpression(node)) {
|
|
72
|
-
const
|
|
73
|
-
if (
|
|
163
|
+
const initializer = getObjectPropertyInitializer(node, "id");
|
|
164
|
+
if (initializer) {
|
|
165
|
+
const id = resolveEntityIdReference(initializer);
|
|
166
|
+
if (id && id.includes(":")) result.add(id);
|
|
167
|
+
}
|
|
74
168
|
}
|
|
75
169
|
node.forEachChild(visit);
|
|
76
170
|
};
|
|
@@ -118,8 +212,29 @@ function getPropertyName(property) {
|
|
|
118
212
|
if (!name) return void 0;
|
|
119
213
|
if (ts.isIdentifier(name)) return name.text;
|
|
120
214
|
if (ts.isStringLiteralLike(name)) return name.text;
|
|
215
|
+
if (ts.isComputedPropertyName(name)) return resolveComputedPropertyKey(name.expression);
|
|
121
216
|
return void 0;
|
|
122
217
|
}
|
|
218
|
+
function resolveComputedPropertyKey(expression) {
|
|
219
|
+
if (ts.isStringLiteralLike(expression)) return expression.text;
|
|
220
|
+
if (ts.isAsExpression(expression) || ts.isSatisfiesExpression(expression)) {
|
|
221
|
+
return resolveComputedPropertyKey(expression.expression);
|
|
222
|
+
}
|
|
223
|
+
if (ts.isParenthesizedExpression(expression)) return resolveComputedPropertyKey(expression.expression);
|
|
224
|
+
if (!ts.isIdentifier(expression)) return void 0;
|
|
225
|
+
const sourceFile = expression.getSourceFile();
|
|
226
|
+
if (!sourceFile) return void 0;
|
|
227
|
+
let resolved;
|
|
228
|
+
const visit = (node) => {
|
|
229
|
+
if (resolved) return;
|
|
230
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.name.text === expression.text) {
|
|
231
|
+
if (node.initializer) resolved = resolveComputedPropertyKey(node.initializer);
|
|
232
|
+
}
|
|
233
|
+
node.forEachChild(visit);
|
|
234
|
+
};
|
|
235
|
+
sourceFile.forEachChild(visit);
|
|
236
|
+
return resolved;
|
|
237
|
+
}
|
|
123
238
|
function getObjectPropertyInitializer(objectLiteral, propertyName) {
|
|
124
239
|
for (const property of objectLiteral.properties) {
|
|
125
240
|
if (!ts.isPropertyAssignment(property)) continue;
|
|
@@ -212,7 +327,11 @@ function extractEvents(eventsFilePath) {
|
|
|
212
327
|
category: category ?? null,
|
|
213
328
|
entity: entity ?? null
|
|
214
329
|
};
|
|
330
|
+
const clientBroadcast = readBooleanPropertyInitializer(element, "clientBroadcast");
|
|
331
|
+
const portalBroadcast = readBooleanPropertyInitializer(element, "portalBroadcast");
|
|
215
332
|
if (label !== void 0) fact.label = label;
|
|
333
|
+
if (clientBroadcast !== void 0) fact.clientBroadcast = clientBroadcast;
|
|
334
|
+
if (portalBroadcast !== void 0) fact.portalBroadcast = portalBroadcast;
|
|
216
335
|
facts.push(fact);
|
|
217
336
|
}
|
|
218
337
|
return facts;
|
|
@@ -302,9 +421,9 @@ function parseApiRouteEntry(entryLiteral) {
|
|
|
302
421
|
}
|
|
303
422
|
}
|
|
304
423
|
}
|
|
305
|
-
return { path: routePath, methods, auth };
|
|
424
|
+
return { path: routePath, methods, auth, sourcePath: null };
|
|
306
425
|
}
|
|
307
|
-
function extractApiRoutes(moduleId, registrySource, registryDescription, warnings) {
|
|
426
|
+
function extractApiRoutes(moduleId, registrySource, registryDescription, routeSourcePaths, warnings) {
|
|
308
427
|
if (registrySource == null) {
|
|
309
428
|
warnings.push(`[module-facts] module registry unavailable (${registryDescription}); API route auth omitted for ${moduleId}`);
|
|
310
429
|
return [];
|
|
@@ -328,7 +447,7 @@ function extractApiRoutes(moduleId, registrySource, registryDescription, warning
|
|
|
328
447
|
const route = parseApiRouteEntry(element);
|
|
329
448
|
if (route && !seenPaths.has(route.path)) {
|
|
330
449
|
seenPaths.add(route.path);
|
|
331
|
-
routes.push(route);
|
|
450
|
+
routes.push({ ...route, sourcePath: routeSourcePaths.get(route.path) ?? null });
|
|
332
451
|
}
|
|
333
452
|
}
|
|
334
453
|
}
|
|
@@ -405,15 +524,25 @@ function extractSearchEntities(searchFilePath, warnings) {
|
|
|
405
524
|
warnings.push(`[module-facts] searchConfig.entities is not an array literal: ${searchFilePath}`);
|
|
406
525
|
return [];
|
|
407
526
|
}
|
|
527
|
+
const initializers = buildVariableInitializerMap(sourceFile);
|
|
408
528
|
const entityIds = [];
|
|
409
529
|
const seen = /* @__PURE__ */ new Set();
|
|
410
530
|
for (const element of entitiesArray.elements) {
|
|
411
531
|
if (!ts.isObjectLiteralExpression(element)) continue;
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
532
|
+
const initializer = getObjectPropertyInitializer(element, "entityId");
|
|
533
|
+
const location = `${searchFilePath}:${nodeLine(sourceFile, element)}`;
|
|
534
|
+
if (!initializer) {
|
|
535
|
+
warnings.push(`[module-facts] searchConfig entity declares no entityId: ${location}`);
|
|
536
|
+
continue;
|
|
416
537
|
}
|
|
538
|
+
const entityId = resolveEntityIdReference(initializer, initializers);
|
|
539
|
+
if (!entityId) {
|
|
540
|
+
warnings.push(`[module-facts] searchConfig entityId is not statically resolvable: ${location}`);
|
|
541
|
+
continue;
|
|
542
|
+
}
|
|
543
|
+
if (seen.has(entityId)) continue;
|
|
544
|
+
seen.add(entityId);
|
|
545
|
+
entityIds.push(entityId);
|
|
417
546
|
}
|
|
418
547
|
return entityIds;
|
|
419
548
|
}
|
|
@@ -476,31 +605,204 @@ function extractCli(cliFilePath, warnings) {
|
|
|
476
605
|
function listSourceFilesRecursive(directory) {
|
|
477
606
|
const files = [];
|
|
478
607
|
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
479
|
-
if (entry.name === "__tests__" || entry.name === "node_modules") continue;
|
|
608
|
+
if (entry.name === "__tests__" || entry.name === "__mocks__" || entry.name === "node_modules") continue;
|
|
480
609
|
const fullPath = path.join(directory, entry.name);
|
|
481
610
|
if (entry.isDirectory()) {
|
|
482
611
|
files.push(...listSourceFilesRecursive(fullPath));
|
|
483
|
-
} else if (/\.tsx?$/.test(entry.name) && !entry.name.endsWith(".d.ts")) {
|
|
612
|
+
} else if (/\.tsx?$/.test(entry.name) && !entry.name.endsWith(".d.ts") && !/\.(?:test|spec)\.tsx?$/.test(entry.name)) {
|
|
484
613
|
files.push(fullPath);
|
|
485
614
|
}
|
|
486
615
|
}
|
|
487
616
|
return files;
|
|
488
617
|
}
|
|
489
|
-
function
|
|
490
|
-
|
|
618
|
+
function toPortableSourceRoot(moduleId, sourcePackage) {
|
|
619
|
+
return path.posix.join("node_modules", sourcePackage ?? "@open-mercato/core", "src", "modules", moduleId);
|
|
620
|
+
}
|
|
621
|
+
function assertPortableSourceRoot(moduleId, portableSourceRoot) {
|
|
622
|
+
const invalid = portableSourceRoot.length === 0 || path.posix.isAbsolute(portableSourceRoot) || /^[A-Za-z]:/.test(portableSourceRoot) || portableSourceRoot.includes("\\") || portableSourceRoot.split("/").includes("..");
|
|
623
|
+
if (invalid) {
|
|
624
|
+
throw new Error(
|
|
625
|
+
`[module-facts] portableSourceRoot for module "${moduleId}" must be a relative POSIX app path, received "${portableSourceRoot}"`
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
return portableSourceRoot;
|
|
629
|
+
}
|
|
630
|
+
function toPortableSourcePath(moduleRoot, sourceRoot, filePath) {
|
|
631
|
+
const relativePath = path.relative(moduleRoot, filePath).split(path.sep).join("/");
|
|
632
|
+
return path.posix.join(sourceRoot, relativePath);
|
|
633
|
+
}
|
|
634
|
+
function sourceHasDefaultExport(sourceFile) {
|
|
635
|
+
return sourceFile.statements.some((statement) => {
|
|
636
|
+
if (ts.isExportAssignment(statement) && !statement.isExportEquals) return true;
|
|
637
|
+
if (!ts.isFunctionDeclaration(statement) && !ts.isClassDeclaration(statement)) return false;
|
|
638
|
+
return statement.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword) ?? false;
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
function readRouteMetadataPath(sourceFile) {
|
|
642
|
+
const metadata = findObjectLiteralDeclaration(sourceFile, "metadata");
|
|
643
|
+
return metadata ? readStringPropertyInitializer(metadata, "path") ?? null : null;
|
|
644
|
+
}
|
|
645
|
+
function extractApiRouteSourcePaths(moduleId, moduleRoot, sourceRoot) {
|
|
646
|
+
const apiRoot = path.join(moduleRoot, "api");
|
|
647
|
+
const sources = /* @__PURE__ */ new Map();
|
|
648
|
+
if (!fs.existsSync(apiRoot)) return sources;
|
|
649
|
+
const methodDirectories = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete"]);
|
|
650
|
+
for (const filePath of listSourceFilesRecursive(apiRoot)) {
|
|
651
|
+
const relativePath = path.relative(apiRoot, filePath).split(path.sep).join("/");
|
|
652
|
+
const segments = relativePath.split("/");
|
|
653
|
+
const fileName = segments.pop();
|
|
654
|
+
const fileStem = fileName.replace(/\.tsx?$/, "");
|
|
655
|
+
let routeSegments;
|
|
656
|
+
if (fileStem === "route") {
|
|
657
|
+
routeSegments = segments;
|
|
658
|
+
} else if (segments[0] && methodDirectories.has(segments[0].toLowerCase())) {
|
|
659
|
+
routeSegments = [...segments.slice(1), fileStem];
|
|
660
|
+
} else {
|
|
661
|
+
routeSegments = [...segments, fileStem];
|
|
662
|
+
}
|
|
663
|
+
const defaultPath = `/${[moduleId, ...routeSegments].filter(Boolean).join("/")}`;
|
|
664
|
+
const sourceFile = readSourceFile(filePath);
|
|
665
|
+
const routePath = sourceFile ? readRouteMetadataPath(sourceFile) ?? defaultPath : defaultPath;
|
|
666
|
+
sources.set(routePath, toPortableSourcePath(moduleRoot, sourceRoot, filePath));
|
|
667
|
+
}
|
|
668
|
+
return sources;
|
|
669
|
+
}
|
|
670
|
+
function extractModulePages(moduleId, moduleRoot, sourceRoot, surface) {
|
|
671
|
+
const pageRoot = path.join(moduleRoot, surface);
|
|
672
|
+
if (!fs.existsSync(pageRoot)) return [];
|
|
673
|
+
const pages = [];
|
|
674
|
+
const seen = /* @__PURE__ */ new Set();
|
|
675
|
+
for (const filePath of listSourceFilesRecursive(pageRoot)) {
|
|
676
|
+
if (!filePath.endsWith(".tsx")) continue;
|
|
677
|
+
const relativePath = path.relative(pageRoot, filePath).split(path.sep).join("/");
|
|
678
|
+
const segments = relativePath.split("/");
|
|
679
|
+
const fileName = segments.pop();
|
|
680
|
+
const fileStem = fileName.replace(/\.tsx$/, "");
|
|
681
|
+
const isModernPage = fileStem === "page";
|
|
682
|
+
if (!isModernPage && (fileStem === "meta" || fileStem.endsWith(".meta") || /^[A-Z]/.test(fileStem))) continue;
|
|
683
|
+
const sourceFile = readSourceFile(filePath);
|
|
684
|
+
if (!sourceFile || !sourceHasDefaultExport(sourceFile)) continue;
|
|
685
|
+
const routeSegments = isModernPage ? segments : [...segments, fileStem];
|
|
686
|
+
const routePath = surface === "frontend" ? `/${routeSegments.filter(Boolean).join("/")}` : isModernPage ? `/backend/${routeSegments.join("/") || moduleId}` : `/backend/${routeSegments[0] === moduleId ? routeSegments.filter(Boolean).join("/") : [moduleId, ...routeSegments].filter(Boolean).join("/")}`;
|
|
687
|
+
if (seen.has(routePath)) continue;
|
|
688
|
+
seen.add(routePath);
|
|
689
|
+
const metadataFilePath = resolvePageMetadataFile(path.dirname(filePath));
|
|
690
|
+
const metadataSourceFile = metadataFilePath ? readSourceFile(metadataFilePath) : sourceFile;
|
|
691
|
+
const metadata = metadataSourceFile ? readPageMetadata(metadataSourceFile) : void 0;
|
|
692
|
+
pages.push({
|
|
693
|
+
path: routePath,
|
|
694
|
+
sourcePath: toPortableSourcePath(moduleRoot, sourceRoot, filePath),
|
|
695
|
+
...metadataFilePath ? { metadataSourcePath: toPortableSourcePath(moduleRoot, sourceRoot, metadataFilePath) } : {},
|
|
696
|
+
...metadata ? { metadata } : {}
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
return pages.sort((left, right) => left.path.localeCompare(right.path));
|
|
700
|
+
}
|
|
701
|
+
function findAncestorVariableDeclaration(node) {
|
|
702
|
+
let current = node.parent;
|
|
703
|
+
while (current) {
|
|
704
|
+
if (ts.isVariableDeclaration(current)) return current;
|
|
705
|
+
current = current.parent;
|
|
706
|
+
}
|
|
707
|
+
return null;
|
|
708
|
+
}
|
|
709
|
+
function findAncestorCallExpression(node) {
|
|
710
|
+
let current = node.parent;
|
|
711
|
+
while (current) {
|
|
712
|
+
if (ts.isCallExpression(current)) return current;
|
|
713
|
+
if (ts.isVariableDeclaration(current) || ts.isStatement(current)) return null;
|
|
714
|
+
current = current.parent;
|
|
715
|
+
}
|
|
716
|
+
return null;
|
|
717
|
+
}
|
|
718
|
+
function extractNamedAiFacts(filePaths, moduleRoot, sourceRoot, propertyName) {
|
|
719
|
+
const facts = [];
|
|
720
|
+
const seen = /* @__PURE__ */ new Set();
|
|
721
|
+
for (const filePath of filePaths) {
|
|
722
|
+
const sourceFile = readSourceFile(filePath);
|
|
723
|
+
if (!sourceFile) continue;
|
|
724
|
+
const initializers = buildVariableInitializerMap(sourceFile);
|
|
725
|
+
const readDefinitionValue = (objectLiteral) => {
|
|
726
|
+
const initializer = getObjectPropertyInitializer(objectLiteral, propertyName);
|
|
727
|
+
if (!initializer) return void 0;
|
|
728
|
+
const resolved = unwrapExpression(initializer);
|
|
729
|
+
if (ts.isStringLiteralLike(resolved)) return resolved.text;
|
|
730
|
+
if (ts.isIdentifier(resolved)) {
|
|
731
|
+
const declarationInitializer = initializers.get(resolved.text);
|
|
732
|
+
if (declarationInitializer) {
|
|
733
|
+
const declarationValue = unwrapExpression(declarationInitializer);
|
|
734
|
+
if (ts.isStringLiteralLike(declarationValue)) return declarationValue.text;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
return void 0;
|
|
738
|
+
};
|
|
739
|
+
const visit = (node) => {
|
|
740
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
741
|
+
const value = readDefinitionValue(node);
|
|
742
|
+
if (value && !seen.has(value)) {
|
|
743
|
+
const declaration = findAncestorVariableDeclaration(node);
|
|
744
|
+
const declarationName = declaration && ts.isIdentifier(declaration.name) ? declaration.name.text : "";
|
|
745
|
+
const declarationType = declaration?.type?.getText(sourceFile) ?? "";
|
|
746
|
+
const call = findAncestorCallExpression(node);
|
|
747
|
+
const callee = call?.expression.getText(sourceFile) ?? "";
|
|
748
|
+
const isDefinition = propertyName === "name" ? /AiTool/.test(declarationType) || /tool$/i.test(declarationName) || /AiTool/.test(callee) : /AiAgentDefinition/.test(declarationType) || /agent$/i.test(declarationName) || /defineAiAgent/.test(callee);
|
|
749
|
+
if (isDefinition) {
|
|
750
|
+
seen.add(value);
|
|
751
|
+
facts.push({
|
|
752
|
+
value,
|
|
753
|
+
sourcePath: toPortableSourcePath(moduleRoot, sourceRoot, filePath)
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
node.forEachChild(visit);
|
|
759
|
+
};
|
|
760
|
+
sourceFile.forEachChild(visit);
|
|
761
|
+
}
|
|
762
|
+
return facts.sort((left, right) => left.value.localeCompare(right.value));
|
|
763
|
+
}
|
|
764
|
+
function extractAiTools(moduleRoot, sourceRoot) {
|
|
765
|
+
const files = /* @__PURE__ */ new Set();
|
|
766
|
+
const rootFile = resolveConventionFile(moduleRoot, "ai-tools");
|
|
767
|
+
if (rootFile) files.add(rootFile);
|
|
768
|
+
const toolsDirectory = path.join(moduleRoot, "ai-tools");
|
|
769
|
+
if (fs.existsSync(toolsDirectory)) {
|
|
770
|
+
for (const filePath of listSourceFilesRecursive(toolsDirectory)) files.add(filePath);
|
|
771
|
+
}
|
|
772
|
+
return extractNamedAiFacts([...files], moduleRoot, sourceRoot, "name").map((fact) => ({ name: fact.value, sourcePath: fact.sourcePath }));
|
|
773
|
+
}
|
|
774
|
+
function extractAiAgents(moduleRoot, sourceRoot) {
|
|
775
|
+
const agentsFile = resolveConventionFile(moduleRoot, "ai-agents");
|
|
776
|
+
if (!agentsFile) return [];
|
|
777
|
+
return extractNamedAiFacts([agentsFile], moduleRoot, sourceRoot, "id").map((fact) => ({ id: fact.value, sourcePath: fact.sourcePath }));
|
|
778
|
+
}
|
|
779
|
+
function extractTableIds(moduleRoot) {
|
|
780
|
+
if (!fs.existsSync(moduleRoot)) return [];
|
|
491
781
|
const tableIds = [];
|
|
492
782
|
const seen = /* @__PURE__ */ new Set();
|
|
493
|
-
|
|
783
|
+
const collectLiteralValues = (expression) => {
|
|
784
|
+
const current = unwrapExpression(expression);
|
|
785
|
+
if (ts.isStringLiteralLike(current)) return [current.text];
|
|
786
|
+
if (ts.isConditionalExpression(current)) {
|
|
787
|
+
return [
|
|
788
|
+
...collectLiteralValues(current.whenTrue),
|
|
789
|
+
...collectLiteralValues(current.whenFalse)
|
|
790
|
+
];
|
|
791
|
+
}
|
|
792
|
+
return [];
|
|
793
|
+
};
|
|
794
|
+
for (const filePath of listSourceFilesRecursive(moduleRoot)) {
|
|
494
795
|
const sourceFile = readSourceFile(filePath);
|
|
495
796
|
if (!sourceFile) continue;
|
|
496
797
|
const visit = (node) => {
|
|
497
798
|
if (ts.isPropertyAssignment(node)) {
|
|
498
799
|
const propertyName = getPropertyName(node);
|
|
499
|
-
if (
|
|
500
|
-
const value
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
800
|
+
if (propertyName === "tableId" || propertyName === "extensionTableId") {
|
|
801
|
+
for (const value of collectLiteralValues(node.initializer)) {
|
|
802
|
+
if (value && !seen.has(value)) {
|
|
803
|
+
seen.add(value);
|
|
804
|
+
tableIds.push(value);
|
|
805
|
+
}
|
|
504
806
|
}
|
|
505
807
|
}
|
|
506
808
|
}
|
|
@@ -525,12 +827,587 @@ function extractModuleMeta(indexFilePath) {
|
|
|
525
827
|
description: readStringPropertyInitializer(metadata, "description") ?? null
|
|
526
828
|
};
|
|
527
829
|
}
|
|
830
|
+
function nodeLine(sourceFile, node) {
|
|
831
|
+
return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;
|
|
832
|
+
}
|
|
833
|
+
function readPropertyNameText(name) {
|
|
834
|
+
if (ts.isIdentifier(name) || ts.isStringLiteralLike(name)) return name.text;
|
|
835
|
+
return void 0;
|
|
836
|
+
}
|
|
837
|
+
function readMethodBearingPropertyNames(objectLiteral) {
|
|
838
|
+
const names = [];
|
|
839
|
+
for (const property of objectLiteral.properties) {
|
|
840
|
+
if (ts.isMethodDeclaration(property)) {
|
|
841
|
+
const name = readPropertyNameText(property.name);
|
|
842
|
+
if (name) names.push(name);
|
|
843
|
+
continue;
|
|
844
|
+
}
|
|
845
|
+
if (ts.isPropertyAssignment(property)) {
|
|
846
|
+
const name = readPropertyNameText(property.name);
|
|
847
|
+
if (!name) continue;
|
|
848
|
+
const initializer = unwrapExpression(property.initializer);
|
|
849
|
+
if (ts.isArrowFunction(initializer) || ts.isFunctionExpression(initializer)) names.push(name);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
return names.sort((left, right) => left.localeCompare(right));
|
|
853
|
+
}
|
|
854
|
+
function readObjectKeyNames(objectLiteral) {
|
|
855
|
+
const names = [];
|
|
856
|
+
for (const property of objectLiteral.properties) {
|
|
857
|
+
const name = getPropertyName(property);
|
|
858
|
+
if (name) names.push(name);
|
|
859
|
+
}
|
|
860
|
+
return names.sort((left, right) => left.localeCompare(right));
|
|
861
|
+
}
|
|
862
|
+
function mapDiRegistrationKind(base) {
|
|
863
|
+
switch (base) {
|
|
864
|
+
case "asFunction":
|
|
865
|
+
return "function";
|
|
866
|
+
case "asClass":
|
|
867
|
+
return "class";
|
|
868
|
+
case "asValue":
|
|
869
|
+
return "value";
|
|
870
|
+
case "aliasTo":
|
|
871
|
+
return "alias";
|
|
872
|
+
default:
|
|
873
|
+
return null;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
function readDiChainMetadata(initializer) {
|
|
877
|
+
const result = {};
|
|
878
|
+
let current = unwrapExpression(initializer);
|
|
879
|
+
while (ts.isCallExpression(current) && ts.isPropertyAccessExpression(current.expression)) {
|
|
880
|
+
const method = current.expression.name.text;
|
|
881
|
+
if ((method === "singleton" || method === "scoped" || method === "transient") && !result.lifetime) {
|
|
882
|
+
result.lifetime = method;
|
|
883
|
+
} else if (method === "proxy" && !result.injectionMode) {
|
|
884
|
+
result.injectionMode = "proxy";
|
|
885
|
+
} else if (method === "classic" && !result.injectionMode) {
|
|
886
|
+
result.injectionMode = "classic";
|
|
887
|
+
}
|
|
888
|
+
current = unwrapExpression(current.expression.expression);
|
|
889
|
+
}
|
|
890
|
+
return result;
|
|
891
|
+
}
|
|
892
|
+
function readDiProviderSymbol(initializer, kind) {
|
|
893
|
+
let current = unwrapExpression(initializer);
|
|
894
|
+
while (ts.isCallExpression(current) && ts.isPropertyAccessExpression(current.expression)) {
|
|
895
|
+
current = unwrapExpression(current.expression.expression);
|
|
896
|
+
}
|
|
897
|
+
if (!ts.isCallExpression(current)) return void 0;
|
|
898
|
+
const argument = current.arguments[0];
|
|
899
|
+
if (!argument) return void 0;
|
|
900
|
+
if (kind === "value") return void 0;
|
|
901
|
+
if (kind === "alias" && ts.isStringLiteralLike(argument)) return argument.text;
|
|
902
|
+
if ((kind === "function" || kind === "class") && ts.isIdentifier(argument)) return argument.text;
|
|
903
|
+
return void 0;
|
|
904
|
+
}
|
|
905
|
+
function extractDiRegistrationFacts(diFilePath, sourcePath) {
|
|
906
|
+
if (!diFilePath) return { facts: [], unresolvedTokens: [] };
|
|
907
|
+
const sourceFile = readSourceFile(diFilePath);
|
|
908
|
+
if (!sourceFile) return { facts: [], unresolvedTokens: [] };
|
|
909
|
+
const facts = [];
|
|
910
|
+
const unresolvedTokens = [];
|
|
911
|
+
const visit = (node) => {
|
|
912
|
+
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && node.expression.name.text === "register") {
|
|
913
|
+
const argument = node.arguments[0];
|
|
914
|
+
if (argument && ts.isObjectLiteralExpression(argument)) {
|
|
915
|
+
for (const property of argument.properties) {
|
|
916
|
+
if (!ts.isPropertyAssignment(property)) continue;
|
|
917
|
+
const token = getPropertyName(property);
|
|
918
|
+
if (!token) continue;
|
|
919
|
+
const kind = mapDiRegistrationKind(detectAwilixRegistrationKind(property.initializer));
|
|
920
|
+
if (!kind) {
|
|
921
|
+
if (!unresolvedTokens.includes(token)) unresolvedTokens.push(token);
|
|
922
|
+
continue;
|
|
923
|
+
}
|
|
924
|
+
const chain = readDiChainMetadata(property.initializer);
|
|
925
|
+
const providerSymbol = readDiProviderSymbol(property.initializer, kind);
|
|
926
|
+
facts.push({
|
|
927
|
+
token,
|
|
928
|
+
registrationKind: kind,
|
|
929
|
+
...providerSymbol ? { providerSymbol } : {},
|
|
930
|
+
...chain.lifetime ? { lifetime: chain.lifetime } : {},
|
|
931
|
+
...chain.injectionMode ? { injectionMode: chain.injectionMode } : {},
|
|
932
|
+
source: { sourcePath, exportName: token, line: nodeLine(sourceFile, property) }
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
node.forEachChild(visit);
|
|
938
|
+
};
|
|
939
|
+
sourceFile.forEachChild(visit);
|
|
940
|
+
return { facts, unresolvedTokens };
|
|
941
|
+
}
|
|
942
|
+
function deriveLegacyDiTokens(facts) {
|
|
943
|
+
const tokens = [];
|
|
944
|
+
const seen = /* @__PURE__ */ new Set();
|
|
945
|
+
for (const fact of facts) {
|
|
946
|
+
if (fact.registrationKind !== "function" && fact.registrationKind !== "class") continue;
|
|
947
|
+
if (seen.has(fact.token)) continue;
|
|
948
|
+
seen.add(fact.token);
|
|
949
|
+
tokens.push(fact.token);
|
|
950
|
+
}
|
|
951
|
+
return tokens;
|
|
952
|
+
}
|
|
953
|
+
function dedupeOwnedContractFacts(facts) {
|
|
954
|
+
const byId = /* @__PURE__ */ new Map();
|
|
955
|
+
for (const fact of facts) {
|
|
956
|
+
const existing = byId.get(fact.id);
|
|
957
|
+
if (!existing) {
|
|
958
|
+
byId.set(fact.id, fact);
|
|
959
|
+
continue;
|
|
960
|
+
}
|
|
961
|
+
if (fact.source.sourcePath.localeCompare(existing.source.sourcePath) < 0) byId.set(fact.id, fact);
|
|
962
|
+
}
|
|
963
|
+
return [...byId.values()].sort((left, right) => left.id.localeCompare(right.id));
|
|
964
|
+
}
|
|
965
|
+
const PAGE_CONTEXT_VALUES = /* @__PURE__ */ new Set(["main", "admin", "settings", "profile"]);
|
|
966
|
+
function readAliasedStringProperty(metadata, preferred, fallback) {
|
|
967
|
+
return readStringPropertyInitializer(metadata, preferred) ?? readStringPropertyInitializer(metadata, fallback);
|
|
968
|
+
}
|
|
969
|
+
function readNumericProperty(metadata, propertyName) {
|
|
970
|
+
const initializer = getObjectPropertyInitializer(metadata, propertyName);
|
|
971
|
+
if (!initializer || !ts.isNumericLiteral(initializer)) return void 0;
|
|
972
|
+
return Number(initializer.text);
|
|
973
|
+
}
|
|
974
|
+
function readPageMetadata(sourceFile) {
|
|
975
|
+
const metadata = findObjectLiteralDeclaration(sourceFile, "metadata");
|
|
976
|
+
if (!metadata) return void 0;
|
|
977
|
+
const result = {};
|
|
978
|
+
const requireAuth = readBooleanPropertyInitializer(metadata, "requireAuth");
|
|
979
|
+
if (requireAuth !== void 0) result.requireAuth = requireAuth;
|
|
980
|
+
const requireCustomerAuth = readBooleanPropertyInitializer(metadata, "requireCustomerAuth");
|
|
981
|
+
if (requireCustomerAuth !== void 0) result.requireCustomerAuth = requireCustomerAuth;
|
|
982
|
+
const requireFeatures = readStringArrayPropertyInitializer(metadata, "requireFeatures");
|
|
983
|
+
if (requireFeatures && requireFeatures.length > 0) result.requireFeatures = requireFeatures;
|
|
984
|
+
const requireCustomerFeatures = readStringArrayPropertyInitializer(metadata, "requireCustomerFeatures");
|
|
985
|
+
if (requireCustomerFeatures && requireCustomerFeatures.length > 0) {
|
|
986
|
+
result.requireCustomerFeatures = requireCustomerFeatures;
|
|
987
|
+
}
|
|
988
|
+
const navHidden = readBooleanPropertyInitializer(metadata, "navHidden");
|
|
989
|
+
if (navHidden !== void 0) result.navHidden = navHidden;
|
|
990
|
+
const navGroup = readAliasedStringProperty(metadata, "pageGroup", "group");
|
|
991
|
+
if (navGroup) result.navGroup = navGroup;
|
|
992
|
+
const navGroupKey = readAliasedStringProperty(metadata, "pageGroupKey", "groupKey");
|
|
993
|
+
if (navGroupKey) result.navGroupKey = navGroupKey;
|
|
994
|
+
const navOrder = readNumericProperty(metadata, "pageOrder") ?? readNumericProperty(metadata, "order");
|
|
995
|
+
if (navOrder !== void 0) result.navOrder = navOrder;
|
|
996
|
+
const pageContext = readStringPropertyInitializer(metadata, "pageContext");
|
|
997
|
+
if (pageContext && PAGE_CONTEXT_VALUES.has(pageContext)) {
|
|
998
|
+
result.pageContext = pageContext;
|
|
999
|
+
}
|
|
1000
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
1001
|
+
}
|
|
1002
|
+
function resolvePageMetadataFile(pageDirectory) {
|
|
1003
|
+
for (const baseName of ["page.meta", "meta"]) {
|
|
1004
|
+
for (const extension of MODULE_CODE_EXTENSIONS) {
|
|
1005
|
+
const candidate = path.join(pageDirectory, `${baseName}${extension}`);
|
|
1006
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
return null;
|
|
1010
|
+
}
|
|
1011
|
+
function extractModuleMetadataContract(indexFilePath, moduleId, sourcePath) {
|
|
1012
|
+
if (!indexFilePath || !sourcePath) return null;
|
|
1013
|
+
const sourceFile = readSourceFile(indexFilePath);
|
|
1014
|
+
if (!sourceFile) return null;
|
|
1015
|
+
const metadata = findObjectLiteralDeclaration(sourceFile, "metadata");
|
|
1016
|
+
if (!metadata) return null;
|
|
1017
|
+
const facts = {};
|
|
1018
|
+
const name = readStringPropertyInitializer(metadata, "name");
|
|
1019
|
+
if (name) facts.name = name;
|
|
1020
|
+
const version = readStringPropertyInitializer(metadata, "version");
|
|
1021
|
+
if (version) facts.version = version;
|
|
1022
|
+
const requires = readStringArrayPropertyInitializer(metadata, "requires");
|
|
1023
|
+
if (requires && requires.length > 0) facts.requires = requires;
|
|
1024
|
+
const ejectable = readBooleanPropertyInitializer(metadata, "ejectable");
|
|
1025
|
+
if (ejectable !== void 0) facts.ejectable = ejectable;
|
|
1026
|
+
return {
|
|
1027
|
+
kind: "module-metadata",
|
|
1028
|
+
id: moduleId,
|
|
1029
|
+
source: { sourcePath, exportName: "metadata", line: nodeLine(sourceFile, metadata) },
|
|
1030
|
+
...Object.keys(facts).length > 0 ? { metadata: facts } : {}
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
function extractCommandContractCandidates(moduleId, moduleRoot, sourceRoot) {
|
|
1034
|
+
const commandsRoot = path.join(moduleRoot, "commands");
|
|
1035
|
+
if (!fs.existsSync(commandsRoot)) return [];
|
|
1036
|
+
const candidates = [];
|
|
1037
|
+
for (const filePath of listSourceFilesRecursive(commandsRoot)) {
|
|
1038
|
+
if (path.basename(filePath).replace(/\.tsx?$/, "") === "interceptors") continue;
|
|
1039
|
+
const sourcePath = toPortableSourcePath(moduleRoot, sourceRoot, filePath);
|
|
1040
|
+
for (const id of extractCommandIdsFromSource(filePath)) candidates.push({ id, sourcePath });
|
|
1041
|
+
}
|
|
1042
|
+
return candidates;
|
|
1043
|
+
}
|
|
1044
|
+
function deriveWorkerId(moduleId, workersRoot, filePath) {
|
|
1045
|
+
const relativePath = path.relative(workersRoot, filePath).split(path.sep).join("/");
|
|
1046
|
+
const segments = relativePath.split("/");
|
|
1047
|
+
const fileName = segments.pop();
|
|
1048
|
+
const fileStem = fileName.replace(/\.tsx?$/, "");
|
|
1049
|
+
return [moduleId, "workers", ...segments, fileStem].filter(Boolean).join(":");
|
|
1050
|
+
}
|
|
1051
|
+
function readResolvedStringProperty(objectLiteral, propertyName, initializers) {
|
|
1052
|
+
const initializer = getObjectPropertyInitializer(objectLiteral, propertyName);
|
|
1053
|
+
if (!initializer) return void 0;
|
|
1054
|
+
const resolved = unwrapExpression(initializer);
|
|
1055
|
+
if (ts.isStringLiteralLike(resolved)) return resolved.text;
|
|
1056
|
+
if (ts.isIdentifier(resolved)) {
|
|
1057
|
+
const declaration = initializers.get(resolved.text);
|
|
1058
|
+
if (declaration) {
|
|
1059
|
+
const declarationValue = unwrapExpression(declaration);
|
|
1060
|
+
if (ts.isStringLiteralLike(declarationValue)) return declarationValue.text;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
return void 0;
|
|
1064
|
+
}
|
|
1065
|
+
function extractWorkerContracts(moduleId, moduleRoot, sourceRoot) {
|
|
1066
|
+
const workersRoot = path.join(moduleRoot, "workers");
|
|
1067
|
+
if (!fs.existsSync(workersRoot)) return { facts: [], unresolved: [] };
|
|
1068
|
+
const facts = [];
|
|
1069
|
+
const unresolved = [];
|
|
1070
|
+
for (const filePath of listSourceFilesRecursive(workersRoot)) {
|
|
1071
|
+
const sourceFile = readSourceFile(filePath);
|
|
1072
|
+
if (!sourceFile) continue;
|
|
1073
|
+
const metadata = findObjectLiteralDeclaration(sourceFile, "metadata");
|
|
1074
|
+
if (!metadata) continue;
|
|
1075
|
+
const sourcePath = toPortableSourcePath(moduleRoot, sourceRoot, filePath);
|
|
1076
|
+
const source = { sourcePath, exportName: "metadata", line: nodeLine(sourceFile, metadata) };
|
|
1077
|
+
const initializers = buildVariableInitializerMap(sourceFile);
|
|
1078
|
+
const workerId = readResolvedStringProperty(metadata, "id", initializers) ?? deriveWorkerId(moduleId, workersRoot, filePath);
|
|
1079
|
+
const hasQueueProperty = getObjectPropertyInitializer(metadata, "queue") !== void 0;
|
|
1080
|
+
const queue = readResolvedStringProperty(metadata, "queue", initializers);
|
|
1081
|
+
if (!hasQueueProperty) {
|
|
1082
|
+
unresolved.push({ code: "unresolved-static-contract", kind: "worker", id: workerId, source });
|
|
1083
|
+
continue;
|
|
1084
|
+
}
|
|
1085
|
+
const workerMetadata = {};
|
|
1086
|
+
if (queue) workerMetadata.queue = queue;
|
|
1087
|
+
else unresolved.push({ code: "unresolved-static-contract", kind: "worker", id: workerId, source });
|
|
1088
|
+
const name = readResolvedStringProperty(metadata, "name", initializers);
|
|
1089
|
+
if (name) workerMetadata.name = name;
|
|
1090
|
+
const concurrencyInitializer = getObjectPropertyInitializer(metadata, "concurrency");
|
|
1091
|
+
if (concurrencyInitializer && ts.isNumericLiteral(concurrencyInitializer)) {
|
|
1092
|
+
workerMetadata.concurrency = Number(concurrencyInitializer.text);
|
|
1093
|
+
}
|
|
1094
|
+
facts.push({
|
|
1095
|
+
kind: "worker",
|
|
1096
|
+
id: workerId,
|
|
1097
|
+
source,
|
|
1098
|
+
...Object.keys(workerMetadata).length > 0 ? { metadata: workerMetadata } : {}
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
return { facts: dedupeOwnedContractFacts(facts), unresolved };
|
|
1102
|
+
}
|
|
1103
|
+
function extractPageMiddlewareContracts(moduleRoot, sourceRoot) {
|
|
1104
|
+
const facts = [];
|
|
1105
|
+
const unresolved = [];
|
|
1106
|
+
for (const surface of ["backend", "frontend"]) {
|
|
1107
|
+
const filePath = resolveConventionFile(path.join(moduleRoot, surface), "middleware");
|
|
1108
|
+
if (!filePath) continue;
|
|
1109
|
+
const sourceFile = readSourceFile(filePath);
|
|
1110
|
+
if (!sourceFile) continue;
|
|
1111
|
+
const sourcePath = toPortableSourcePath(moduleRoot, sourceRoot, filePath);
|
|
1112
|
+
const middlewareArray = findArrayLiteralDeclaration(sourceFile, "middleware") ?? (() => {
|
|
1113
|
+
const defaultExport = findDefaultExportExpression(sourceFile);
|
|
1114
|
+
return defaultExport ? unwrapArrayLiteral(defaultExport) : null;
|
|
1115
|
+
})();
|
|
1116
|
+
if (!middlewareArray) continue;
|
|
1117
|
+
let index = 0;
|
|
1118
|
+
for (const element of middlewareArray.elements) {
|
|
1119
|
+
if (!ts.isObjectLiteralExpression(element)) continue;
|
|
1120
|
+
const middlewareId = readStringPropertyInitializer(element, "id");
|
|
1121
|
+
if (!middlewareId) {
|
|
1122
|
+
unresolved.push({
|
|
1123
|
+
code: "unresolved-static-contract",
|
|
1124
|
+
kind: "page-middleware",
|
|
1125
|
+
id: `${sourcePath}#${index}`,
|
|
1126
|
+
source: { sourcePath, line: nodeLine(sourceFile, element) }
|
|
1127
|
+
});
|
|
1128
|
+
index += 1;
|
|
1129
|
+
continue;
|
|
1130
|
+
}
|
|
1131
|
+
const metadata = { surface };
|
|
1132
|
+
const mode = readStringPropertyInitializer(element, "mode");
|
|
1133
|
+
if (mode) metadata.mode = mode;
|
|
1134
|
+
const priorityInitializer = getObjectPropertyInitializer(element, "priority");
|
|
1135
|
+
if (priorityInitializer && ts.isNumericLiteral(priorityInitializer)) {
|
|
1136
|
+
metadata.priority = Number(priorityInitializer.text);
|
|
1137
|
+
}
|
|
1138
|
+
facts.push({
|
|
1139
|
+
kind: "page-middleware",
|
|
1140
|
+
id: middlewareId,
|
|
1141
|
+
source: { sourcePath, exportName: "middleware", line: nodeLine(sourceFile, element) },
|
|
1142
|
+
metadata
|
|
1143
|
+
});
|
|
1144
|
+
index += 1;
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
return { facts: dedupeOwnedContractFacts(facts), unresolved };
|
|
1148
|
+
}
|
|
1149
|
+
function extractSetupContract(moduleId, setupFilePath, sourcePath) {
|
|
1150
|
+
if (!setupFilePath || !sourcePath) return null;
|
|
1151
|
+
const sourceFile = readSourceFile(setupFilePath);
|
|
1152
|
+
if (!sourceFile) return null;
|
|
1153
|
+
const setupObject = findObjectLiteralDeclaration(sourceFile, "setup") ?? (() => {
|
|
1154
|
+
const defaultExport = findDefaultExportExpression(sourceFile);
|
|
1155
|
+
if (!defaultExport) return null;
|
|
1156
|
+
const unwrapped = unwrapExpression(defaultExport);
|
|
1157
|
+
return ts.isObjectLiteralExpression(unwrapped) ? unwrapped : null;
|
|
1158
|
+
})();
|
|
1159
|
+
if (!setupObject) return null;
|
|
1160
|
+
const hooks = readMethodBearingPropertyNames(setupObject);
|
|
1161
|
+
const metadata = {};
|
|
1162
|
+
if (hooks.length > 0) metadata.hooks = hooks;
|
|
1163
|
+
const roleFeatures = getObjectPropertyInitializer(setupObject, "defaultRoleFeatures");
|
|
1164
|
+
if (roleFeatures && ts.isObjectLiteralExpression(roleFeatures)) {
|
|
1165
|
+
const roles = readObjectKeyNames(roleFeatures);
|
|
1166
|
+
if (roles.length > 0) metadata.roles = roles;
|
|
1167
|
+
}
|
|
1168
|
+
const customerRoleFeatures = getObjectPropertyInitializer(setupObject, "defaultCustomerRoleFeatures");
|
|
1169
|
+
if (customerRoleFeatures && ts.isObjectLiteralExpression(customerRoleFeatures)) {
|
|
1170
|
+
const customerRoles = readObjectKeyNames(customerRoleFeatures);
|
|
1171
|
+
if (customerRoles.length > 0) metadata.customerRoles = customerRoles;
|
|
1172
|
+
}
|
|
1173
|
+
return {
|
|
1174
|
+
kind: "setup",
|
|
1175
|
+
id: `${moduleId}:setup`,
|
|
1176
|
+
source: { sourcePath, exportName: "setup", line: nodeLine(sourceFile, setupObject) },
|
|
1177
|
+
...Object.keys(metadata).length > 0 ? { metadata } : {}
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
function extractEncryptionContracts(encryptionFilePath, sourcePath) {
|
|
1181
|
+
if (!encryptionFilePath || !sourcePath) return [];
|
|
1182
|
+
const sourceFile = readSourceFile(encryptionFilePath);
|
|
1183
|
+
if (!sourceFile) return [];
|
|
1184
|
+
const mapsArray = findArrayLiteralDeclaration(sourceFile, "defaultEncryptionMaps") ?? findArrayLiteralDeclaration(sourceFile, "encryptionMaps");
|
|
1185
|
+
if (!mapsArray) return [];
|
|
1186
|
+
const facts = [];
|
|
1187
|
+
for (const element of mapsArray.elements) {
|
|
1188
|
+
if (!ts.isObjectLiteralExpression(element)) continue;
|
|
1189
|
+
const entityId = readStringPropertyInitializer(element, "entityId");
|
|
1190
|
+
if (!entityId) continue;
|
|
1191
|
+
const fieldsInitializer = getObjectPropertyInitializer(element, "fields");
|
|
1192
|
+
const fields = [];
|
|
1193
|
+
if (fieldsInitializer) {
|
|
1194
|
+
const fieldsArray = unwrapArrayLiteral(fieldsInitializer);
|
|
1195
|
+
if (fieldsArray) {
|
|
1196
|
+
for (const fieldElement of fieldsArray.elements) {
|
|
1197
|
+
if (!ts.isObjectLiteralExpression(fieldElement)) continue;
|
|
1198
|
+
const field = readStringPropertyInitializer(fieldElement, "field");
|
|
1199
|
+
if (field) fields.push(field);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
facts.push({
|
|
1204
|
+
kind: "encryption",
|
|
1205
|
+
id: entityId,
|
|
1206
|
+
source: { sourcePath, exportName: "defaultEncryptionMaps", line: nodeLine(sourceFile, element) },
|
|
1207
|
+
...fields.length > 0 ? { metadata: { fields: fields.sort((left, right) => left.localeCompare(right)) } } : {}
|
|
1208
|
+
});
|
|
1209
|
+
}
|
|
1210
|
+
return dedupeOwnedContractFacts(facts);
|
|
1211
|
+
}
|
|
1212
|
+
function extractCustomEntityContracts(ceFilePath, sourcePath) {
|
|
1213
|
+
if (!ceFilePath || !sourcePath) return [];
|
|
1214
|
+
const entityIds = [...collectCustomFieldEntityIds(ceFilePath)].sort((left, right) => left.localeCompare(right));
|
|
1215
|
+
return entityIds.map((id) => ({
|
|
1216
|
+
kind: "custom-entity",
|
|
1217
|
+
id,
|
|
1218
|
+
source: { sourcePath, exportName: "default" }
|
|
1219
|
+
}));
|
|
1220
|
+
}
|
|
1221
|
+
const AI_OVERRIDE_EXPORTS = [
|
|
1222
|
+
{ exportName: "aiAgentOverrides", target: "agent" },
|
|
1223
|
+
{ exportName: "aiToolOverrides", target: "tool" }
|
|
1224
|
+
];
|
|
1225
|
+
function extractAiExtensionContracts(moduleRoot, sourceRoot) {
|
|
1226
|
+
const facts = [];
|
|
1227
|
+
const sources = [
|
|
1228
|
+
{ basename: "ai-agents", relativeDir: "." },
|
|
1229
|
+
{ basename: "ai-tools", relativeDir: "." },
|
|
1230
|
+
{ basename: "ai-agents.extensions", relativeDir: "." },
|
|
1231
|
+
{ basename: "ai-tools.extensions", relativeDir: "." }
|
|
1232
|
+
];
|
|
1233
|
+
for (const entry of sources) {
|
|
1234
|
+
const filePath = resolveConventionFile(path.join(moduleRoot, entry.relativeDir), entry.basename);
|
|
1235
|
+
if (!filePath) continue;
|
|
1236
|
+
const sourceFile = readSourceFile(filePath);
|
|
1237
|
+
if (!sourceFile) continue;
|
|
1238
|
+
const sourcePath = toPortableSourcePath(moduleRoot, sourceRoot, filePath);
|
|
1239
|
+
const extensionsArray = findArrayLiteralDeclaration(sourceFile, "aiAgentExtensions");
|
|
1240
|
+
if (extensionsArray) {
|
|
1241
|
+
for (const element of extensionsArray.elements) {
|
|
1242
|
+
const objectLiteral = ts.isObjectLiteralExpression(element) ? element : ts.isCallExpression(element) && element.arguments[0] && ts.isObjectLiteralExpression(element.arguments[0]) ? element.arguments[0] : null;
|
|
1243
|
+
if (!objectLiteral) continue;
|
|
1244
|
+
const targetAgentId = readStringPropertyInitializer(objectLiteral, "targetAgentId");
|
|
1245
|
+
if (!targetAgentId) continue;
|
|
1246
|
+
facts.push({
|
|
1247
|
+
kind: "ai-extension",
|
|
1248
|
+
id: `agent-extension:${targetAgentId}`,
|
|
1249
|
+
source: { sourcePath, exportName: "aiAgentExtensions", line: nodeLine(sourceFile, objectLiteral) },
|
|
1250
|
+
metadata: { target: "agent", mode: "extension", targetAgentId }
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
for (const { exportName, target } of AI_OVERRIDE_EXPORTS) {
|
|
1255
|
+
const overridesObject = findObjectLiteralDeclaration(sourceFile, exportName);
|
|
1256
|
+
if (!overridesObject) continue;
|
|
1257
|
+
for (const property of overridesObject.properties) {
|
|
1258
|
+
const overrideId = getPropertyName(property);
|
|
1259
|
+
if (!overrideId) continue;
|
|
1260
|
+
facts.push({
|
|
1261
|
+
kind: "ai-extension",
|
|
1262
|
+
id: `${target}-override:${overrideId}`,
|
|
1263
|
+
source: { sourcePath, exportName, line: nodeLine(sourceFile, property) },
|
|
1264
|
+
metadata: { target, mode: "override", overrideKey: overrideId }
|
|
1265
|
+
});
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
return dedupeOwnedContractFacts(facts);
|
|
1270
|
+
}
|
|
1271
|
+
function extractGeneratorPluginContracts(moduleRoot, sourceRoot) {
|
|
1272
|
+
const filePath = resolveConventionFile(moduleRoot, "generators");
|
|
1273
|
+
if (!filePath) return { facts: [], unresolved: [] };
|
|
1274
|
+
const sourceFile = readSourceFile(filePath);
|
|
1275
|
+
if (!sourceFile) return { facts: [], unresolved: [] };
|
|
1276
|
+
const pluginsArray = findArrayLiteralDeclaration(sourceFile, "generatorPlugins");
|
|
1277
|
+
if (!pluginsArray) return { facts: [], unresolved: [] };
|
|
1278
|
+
const sourcePath = toPortableSourcePath(moduleRoot, sourceRoot, filePath);
|
|
1279
|
+
const initializers = buildVariableInitializerMap(sourceFile);
|
|
1280
|
+
const facts = [];
|
|
1281
|
+
const unresolved = [];
|
|
1282
|
+
let index = 0;
|
|
1283
|
+
for (const element of pluginsArray.elements) {
|
|
1284
|
+
const objectLiteral = resolveToObjectLiteral(element, initializers);
|
|
1285
|
+
if (!objectLiteral) {
|
|
1286
|
+
index += 1;
|
|
1287
|
+
continue;
|
|
1288
|
+
}
|
|
1289
|
+
const pluginId = readStringPropertyInitializer(objectLiteral, "id");
|
|
1290
|
+
if (!pluginId) {
|
|
1291
|
+
unresolved.push({
|
|
1292
|
+
code: "unresolved-static-contract",
|
|
1293
|
+
kind: "generator-plugin",
|
|
1294
|
+
id: `${sourcePath}#${index}`,
|
|
1295
|
+
source: { sourcePath, exportName: "generatorPlugins", line: nodeLine(sourceFile, objectLiteral) }
|
|
1296
|
+
});
|
|
1297
|
+
index += 1;
|
|
1298
|
+
continue;
|
|
1299
|
+
}
|
|
1300
|
+
const metadata = {};
|
|
1301
|
+
const conventionFile = readStringPropertyInitializer(objectLiteral, "conventionFile");
|
|
1302
|
+
if (conventionFile) metadata.conventionFile = conventionFile;
|
|
1303
|
+
const outputFileName = readStringPropertyInitializer(objectLiteral, "outputFileName");
|
|
1304
|
+
if (outputFileName) metadata.outputFileName = outputFileName;
|
|
1305
|
+
facts.push({
|
|
1306
|
+
kind: "generator-plugin",
|
|
1307
|
+
id: pluginId,
|
|
1308
|
+
source: { sourcePath, exportName: "generatorPlugins", line: nodeLine(sourceFile, objectLiteral) },
|
|
1309
|
+
...Object.keys(metadata).length > 0 ? { metadata } : {}
|
|
1310
|
+
});
|
|
1311
|
+
index += 1;
|
|
1312
|
+
}
|
|
1313
|
+
return { facts: dedupeOwnedContractFacts(facts), unresolved };
|
|
1314
|
+
}
|
|
1315
|
+
function compareSourceRefs(left, right) {
|
|
1316
|
+
return left.sourcePath.localeCompare(right.sourcePath) || (left.line ?? 0) - (right.line ?? 0) || (left.exportName ?? "").localeCompare(right.exportName ?? "");
|
|
1317
|
+
}
|
|
1318
|
+
function sameSourceRef(left, right) {
|
|
1319
|
+
return left.sourcePath === right.sourcePath && (left.line ?? null) === (right.line ?? null) && (left.exportName ?? null) === (right.exportName ?? null);
|
|
1320
|
+
}
|
|
1321
|
+
function provenanceCandidateOrderKey(candidate) {
|
|
1322
|
+
if (candidate.source) {
|
|
1323
|
+
return JSON.stringify([
|
|
1324
|
+
0,
|
|
1325
|
+
candidate.source.sourcePath,
|
|
1326
|
+
candidate.source.line ?? 0,
|
|
1327
|
+
candidate.source.exportName ?? ""
|
|
1328
|
+
]);
|
|
1329
|
+
}
|
|
1330
|
+
return JSON.stringify([1, candidate.factRef?.factSection ?? "", candidate.factRef?.factKey ?? ""]);
|
|
1331
|
+
}
|
|
1332
|
+
function buildProvenanceIndex(candidates) {
|
|
1333
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
1334
|
+
for (const candidate of candidates) {
|
|
1335
|
+
const key = JSON.stringify([candidate.kind, candidate.id]);
|
|
1336
|
+
const group = grouped.get(key) ?? [];
|
|
1337
|
+
group.push(candidate);
|
|
1338
|
+
grouped.set(key, group);
|
|
1339
|
+
}
|
|
1340
|
+
const factSources = [];
|
|
1341
|
+
const diagnostics = [];
|
|
1342
|
+
for (const group of grouped.values()) {
|
|
1343
|
+
const sorted = [...group].sort(
|
|
1344
|
+
(left, right) => provenanceCandidateOrderKey(left).localeCompare(provenanceCandidateOrderKey(right))
|
|
1345
|
+
);
|
|
1346
|
+
const canonical = sorted[0];
|
|
1347
|
+
factSources.push(
|
|
1348
|
+
canonical.factRef ? {
|
|
1349
|
+
kind: canonical.kind,
|
|
1350
|
+
id: canonical.id,
|
|
1351
|
+
factRef: {
|
|
1352
|
+
factSection: canonical.factRef.factSection,
|
|
1353
|
+
...canonical.factRef.factKey === canonical.id ? {} : { factKey: canonical.factRef.factKey }
|
|
1354
|
+
}
|
|
1355
|
+
} : {
|
|
1356
|
+
kind: canonical.kind,
|
|
1357
|
+
id: canonical.id,
|
|
1358
|
+
source: { sourcePath: canonical.source.sourcePath }
|
|
1359
|
+
}
|
|
1360
|
+
);
|
|
1361
|
+
const canonicalKey = provenanceCandidateOrderKey(canonical);
|
|
1362
|
+
for (const rejected of sorted.slice(1)) {
|
|
1363
|
+
if (provenanceCandidateOrderKey(rejected) === canonicalKey) continue;
|
|
1364
|
+
diagnostics.push({
|
|
1365
|
+
code: "duplicate-source",
|
|
1366
|
+
kind: rejected.kind,
|
|
1367
|
+
id: rejected.id,
|
|
1368
|
+
...rejected.source ? { source: rejected.source } : {}
|
|
1369
|
+
});
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
factSources.sort(
|
|
1373
|
+
(left, right) => left.kind.localeCompare(right.kind) || left.id.localeCompare(right.id)
|
|
1374
|
+
);
|
|
1375
|
+
diagnostics.sort(
|
|
1376
|
+
(left, right) => left.code.localeCompare(right.code) || left.kind.localeCompare(right.kind) || left.id.localeCompare(right.id) || compareSourceRefs(left.source ?? { sourcePath: "" }, right.source ?? { sourcePath: "" })
|
|
1377
|
+
);
|
|
1378
|
+
return { factSources, diagnostics };
|
|
1379
|
+
}
|
|
1380
|
+
function buildOwnedContracts(families) {
|
|
1381
|
+
const result = {};
|
|
1382
|
+
for (const [kind, facts] of families) {
|
|
1383
|
+
if (facts.length === 0) continue;
|
|
1384
|
+
result[kind] = facts.map((fact) => ({
|
|
1385
|
+
...fact,
|
|
1386
|
+
source: {
|
|
1387
|
+
sourcePath: fact.source.sourcePath,
|
|
1388
|
+
...fact.source.line ? { line: fact.source.line } : {}
|
|
1389
|
+
}
|
|
1390
|
+
}));
|
|
1391
|
+
}
|
|
1392
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
1393
|
+
}
|
|
1394
|
+
const CONTRIBUTION_KIND_TO_SOURCE_KIND = {
|
|
1395
|
+
subscriber: "subscriber"
|
|
1396
|
+
};
|
|
1397
|
+
const REGISTRY_TO_SOURCE_KIND = {
|
|
1398
|
+
vector: "vector",
|
|
1399
|
+
integration: "integration",
|
|
1400
|
+
payment: "integration",
|
|
1401
|
+
shipping: "integration",
|
|
1402
|
+
currency: "integration"
|
|
1403
|
+
};
|
|
528
1404
|
function extractModuleFacts(options) {
|
|
529
|
-
const { moduleId, coreVersion = null } = options;
|
|
1405
|
+
const { moduleId, coreVersion = null, sourcePackage = null, sourceVersion = null } = options;
|
|
530
1406
|
const moduleRoot = options.moduleRoot ?? (options.coreSrcRoot ? path.join(options.coreSrcRoot, moduleId) : null);
|
|
531
1407
|
if (!moduleRoot) {
|
|
532
1408
|
throw new Error(`[internal] extractModuleFacts requires moduleRoot or coreSrcRoot for module "${moduleId}"`);
|
|
533
1409
|
}
|
|
1410
|
+
const sourceRoot = options.portableSourceRoot ? assertPortableSourceRoot(moduleId, options.portableSourceRoot) : toPortableSourceRoot(moduleId, sourcePackage);
|
|
534
1411
|
const entitiesFilePath = resolveConventionFile(path.join(moduleRoot, "data"), "entities") ?? resolveConventionFile(path.join(moduleRoot, "db"), "entities") ?? resolveConventionFile(path.join(moduleRoot, "data"), "schema");
|
|
535
1412
|
const ceFilePath = resolveConventionFile(moduleRoot, "ce");
|
|
536
1413
|
const eventsFilePath = resolveConventionFile(moduleRoot, "events");
|
|
@@ -540,7 +1417,9 @@ function extractModuleFacts(options) {
|
|
|
540
1417
|
const notificationsFilePath = resolveConventionFile(moduleRoot, "notifications");
|
|
541
1418
|
const cliFilePath = resolveConventionFile(moduleRoot, "cli");
|
|
542
1419
|
const indexFilePath = resolveConventionFile(moduleRoot, "index");
|
|
543
|
-
const
|
|
1420
|
+
const setupFilePath = resolveConventionFile(moduleRoot, "setup");
|
|
1421
|
+
const encryptionFilePath = resolveConventionFile(moduleRoot, "encryption");
|
|
1422
|
+
const portableOf = (filePath) => filePath ? toPortableSourcePath(moduleRoot, sourceRoot, filePath) : null;
|
|
544
1423
|
const warnings = [];
|
|
545
1424
|
const { title, description } = extractModuleMeta(indexFilePath);
|
|
546
1425
|
const customFieldEntityIds = collectCustomFieldEntityIds(ceFilePath);
|
|
@@ -548,20 +1427,225 @@ function extractModuleFacts(options) {
|
|
|
548
1427
|
const events = extractEvents(eventsFilePath);
|
|
549
1428
|
const aclFeatures = extractAclFeatures(aclFilePath);
|
|
550
1429
|
const { source: registrySource, description: registryDescription } = resolveRegistrySource(options);
|
|
551
|
-
const
|
|
552
|
-
const
|
|
1430
|
+
const apiRouteSourcePaths = extractApiRouteSourcePaths(moduleId, moduleRoot, sourceRoot);
|
|
1431
|
+
const apiRoutes = extractApiRoutes(moduleId, registrySource, registryDescription, apiRouteSourcePaths, warnings);
|
|
1432
|
+
const diRegistrations = extractDiRegistrationFacts(diFilePath, portableOf(diFilePath) ?? "");
|
|
1433
|
+
const diTokens = deriveLegacyDiTokens(diRegistrations.facts);
|
|
1434
|
+
for (const legacyToken of extractDiTokens(diFilePath)) {
|
|
1435
|
+
if (!diTokens.includes(legacyToken)) diTokens.push(legacyToken);
|
|
1436
|
+
}
|
|
553
1437
|
const searchEntities = extractSearchEntities(searchFilePath, warnings);
|
|
554
1438
|
const notifications = extractNotifications(notificationsFilePath, warnings);
|
|
555
1439
|
const cli = extractCli(cliFilePath, warnings);
|
|
1440
|
+
const cliCommands = cliFilePath ? cli.map((command) => ({
|
|
1441
|
+
command,
|
|
1442
|
+
sourcePath: toPortableSourcePath(moduleRoot, sourceRoot, cliFilePath)
|
|
1443
|
+
})) : [];
|
|
1444
|
+
const backendPages = extractModulePages(moduleId, moduleRoot, sourceRoot, "backend");
|
|
1445
|
+
const frontendPages = extractModulePages(moduleId, moduleRoot, sourceRoot, "frontend");
|
|
1446
|
+
const aiTools = extractAiTools(moduleRoot, sourceRoot);
|
|
1447
|
+
const aiAgents = extractAiAgents(moduleRoot, sourceRoot);
|
|
556
1448
|
const hostTokens = {
|
|
557
1449
|
entityIds: extractHostEntityIds(entities),
|
|
558
|
-
tableIds: extractTableIds(
|
|
1450
|
+
tableIds: extractTableIds(moduleRoot)
|
|
559
1451
|
};
|
|
560
|
-
|
|
1452
|
+
const extensionSurfaces = extractModuleExtensionFacts({
|
|
1453
|
+
moduleId,
|
|
1454
|
+
moduleRoot,
|
|
1455
|
+
sourceRoot,
|
|
1456
|
+
entities,
|
|
1457
|
+
events,
|
|
1458
|
+
apiRoutes,
|
|
1459
|
+
searchEntities,
|
|
1460
|
+
notifications,
|
|
1461
|
+
aiTools,
|
|
1462
|
+
aiAgents
|
|
1463
|
+
});
|
|
1464
|
+
const indexSourcePath = portableOf(indexFilePath);
|
|
1465
|
+
const diSourcePath = portableOf(diFilePath);
|
|
1466
|
+
const moduleMetadataFact = extractModuleMetadataContract(indexFilePath, moduleId, indexSourcePath);
|
|
1467
|
+
const commandCandidates = extractCommandContractCandidates(moduleId, moduleRoot, sourceRoot);
|
|
1468
|
+
const commandFacts = dedupeOwnedContractFacts(
|
|
1469
|
+
commandCandidates.map((candidate) => ({
|
|
1470
|
+
kind: "command",
|
|
1471
|
+
id: candidate.id,
|
|
1472
|
+
source: { sourcePath: candidate.sourcePath }
|
|
1473
|
+
}))
|
|
1474
|
+
);
|
|
1475
|
+
const workers = extractWorkerContracts(moduleId, moduleRoot, sourceRoot);
|
|
1476
|
+
const pageMiddleware = extractPageMiddlewareContracts(moduleRoot, sourceRoot);
|
|
1477
|
+
const setupFact = extractSetupContract(moduleId, setupFilePath, portableOf(setupFilePath));
|
|
1478
|
+
const encryptionFacts = extractEncryptionContracts(encryptionFilePath, portableOf(encryptionFilePath));
|
|
1479
|
+
const customEntityFacts = extractCustomEntityContracts(ceFilePath, portableOf(ceFilePath));
|
|
1480
|
+
const aiExtensionFacts = extractAiExtensionContracts(moduleRoot, sourceRoot);
|
|
1481
|
+
const generatorPlugins = extractGeneratorPluginContracts(moduleRoot, sourceRoot);
|
|
1482
|
+
const diContractFacts = dedupeOwnedContractFacts(
|
|
1483
|
+
diRegistrations.facts.map((fact) => ({
|
|
1484
|
+
kind: "di-registration",
|
|
1485
|
+
id: fact.token,
|
|
1486
|
+
source: fact.source,
|
|
1487
|
+
metadata: {
|
|
1488
|
+
registrationKind: fact.registrationKind,
|
|
1489
|
+
...fact.providerSymbol ? { providerSymbol: fact.providerSymbol } : {},
|
|
1490
|
+
...fact.lifetime ? { lifetime: fact.lifetime } : {},
|
|
1491
|
+
...fact.injectionMode ? { injectionMode: fact.injectionMode } : {}
|
|
1492
|
+
}
|
|
1493
|
+
}))
|
|
1494
|
+
);
|
|
1495
|
+
const ownedContracts = buildOwnedContracts([
|
|
1496
|
+
["module-metadata", moduleMetadataFact ? [moduleMetadataFact] : []],
|
|
1497
|
+
["command", commandFacts],
|
|
1498
|
+
["worker", workers.facts],
|
|
1499
|
+
["page-middleware", pageMiddleware.facts],
|
|
1500
|
+
["setup", setupFact ? [setupFact] : []],
|
|
1501
|
+
["encryption", encryptionFacts],
|
|
1502
|
+
["di-registration", diContractFacts],
|
|
1503
|
+
["custom-entity", customEntityFacts],
|
|
1504
|
+
["ai-extension", aiExtensionFacts],
|
|
1505
|
+
["generator-plugin", generatorPlugins.facts]
|
|
1506
|
+
]);
|
|
1507
|
+
const provenanceCandidates = [];
|
|
1508
|
+
const pushSource = (kind, id, source) => {
|
|
1509
|
+
provenanceCandidates.push({ kind, id, source });
|
|
1510
|
+
};
|
|
1511
|
+
const pushRef = (kind, id, source, factSection, factKey) => {
|
|
1512
|
+
provenanceCandidates.push({ kind, id, source, factRef: { factSection, factKey } });
|
|
1513
|
+
};
|
|
1514
|
+
const pushOwnedRefs = (kind, facts2) => {
|
|
1515
|
+
for (const fact of facts2) pushRef(kind, fact.id, fact.source, `ownedContracts.${kind}`, fact.id);
|
|
1516
|
+
};
|
|
1517
|
+
if (moduleMetadataFact) {
|
|
1518
|
+
pushRef(
|
|
1519
|
+
"module-metadata",
|
|
1520
|
+
moduleId,
|
|
1521
|
+
moduleMetadataFact.source,
|
|
1522
|
+
"ownedContracts.module-metadata",
|
|
1523
|
+
moduleMetadataFact.id
|
|
1524
|
+
);
|
|
1525
|
+
}
|
|
1526
|
+
if (entitiesFilePath) {
|
|
1527
|
+
const entitiesSourcePath = portableOf(entitiesFilePath);
|
|
1528
|
+
for (const entity of entities) {
|
|
1529
|
+
pushSource("entity", entity.id, { sourcePath: entitiesSourcePath, exportName: entity.class });
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
if (eventsFilePath) {
|
|
1533
|
+
const eventsSourcePath = portableOf(eventsFilePath);
|
|
1534
|
+
for (const event of events) pushSource("event", event.id, { sourcePath: eventsSourcePath, exportName: "events" });
|
|
1535
|
+
}
|
|
1536
|
+
if (aclFilePath) {
|
|
1537
|
+
const aclSourcePath = portableOf(aclFilePath);
|
|
1538
|
+
for (const feature of aclFeatures) pushSource("acl-feature", feature, { sourcePath: aclSourcePath, exportName: "features" });
|
|
1539
|
+
}
|
|
1540
|
+
for (const route of apiRoutes) {
|
|
1541
|
+
if (route.sourcePath) {
|
|
1542
|
+
pushRef("api-route", route.path, { sourcePath: route.sourcePath }, "apiRoutes", route.path);
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
if (searchFilePath) {
|
|
1546
|
+
const searchSourcePath = portableOf(searchFilePath);
|
|
1547
|
+
for (const entityId of searchEntities) pushSource("search", entityId, { sourcePath: searchSourcePath, exportName: "searchConfig" });
|
|
1548
|
+
}
|
|
1549
|
+
if (notificationsFilePath) {
|
|
1550
|
+
const notificationsSourcePath = portableOf(notificationsFilePath);
|
|
1551
|
+
for (const id of notifications) pushSource("notification", id, { sourcePath: notificationsSourcePath });
|
|
1552
|
+
}
|
|
1553
|
+
for (const command of cliCommands) {
|
|
1554
|
+
pushRef("cli-command", command.command, { sourcePath: command.sourcePath }, "cliCommands", command.command);
|
|
1555
|
+
}
|
|
1556
|
+
for (const page of backendPages) {
|
|
1557
|
+
pushRef("backend-page", page.path, { sourcePath: page.sourcePath }, "backendPages", page.path);
|
|
1558
|
+
}
|
|
1559
|
+
for (const page of frontendPages) {
|
|
1560
|
+
pushRef("frontend-page", page.path, { sourcePath: page.sourcePath }, "frontendPages", page.path);
|
|
1561
|
+
}
|
|
1562
|
+
for (const tool of aiTools) pushRef("ai-tool", tool.name, { sourcePath: tool.sourcePath }, "aiTools", tool.name);
|
|
1563
|
+
for (const agent of aiAgents) pushRef("ai-agent", agent.id, { sourcePath: agent.sourcePath }, "aiAgents", agent.id);
|
|
1564
|
+
for (const candidate of commandCandidates) {
|
|
1565
|
+
pushRef("command", candidate.id, { sourcePath: candidate.sourcePath }, "ownedContracts.command", candidate.id);
|
|
1566
|
+
}
|
|
1567
|
+
pushOwnedRefs("worker", workers.facts);
|
|
1568
|
+
pushOwnedRefs("page-middleware", pageMiddleware.facts);
|
|
1569
|
+
if (setupFact) pushOwnedRefs("setup", [setupFact]);
|
|
1570
|
+
pushOwnedRefs("encryption", encryptionFacts);
|
|
1571
|
+
for (const fact of diRegistrations.facts) {
|
|
1572
|
+
pushRef("di-registration", fact.token, fact.source, "ownedContracts.di-registration", fact.token);
|
|
1573
|
+
}
|
|
1574
|
+
pushOwnedRefs("custom-entity", customEntityFacts);
|
|
1575
|
+
pushOwnedRefs("ai-extension", aiExtensionFacts);
|
|
1576
|
+
pushOwnedRefs("generator-plugin", generatorPlugins.facts);
|
|
1577
|
+
const contributionRef = (contributionId) => ({
|
|
1578
|
+
factSection: "extensionSurfaces.contributions",
|
|
1579
|
+
factKey: contributionId
|
|
1580
|
+
});
|
|
1581
|
+
for (const contribution of extensionSurfaces.contributions) {
|
|
1582
|
+
const contributionSource = {
|
|
1583
|
+
sourcePath: contribution.source.path,
|
|
1584
|
+
...contribution.source.symbol ? { exportName: contribution.source.symbol } : {}
|
|
1585
|
+
};
|
|
1586
|
+
const ref = contributionRef(contribution.id);
|
|
1587
|
+
if (contribution.kind === "subscriber") {
|
|
1588
|
+
pushRef("subscriber", contribution.id, contributionSource, ref.factSection, ref.factKey);
|
|
1589
|
+
continue;
|
|
1590
|
+
}
|
|
1591
|
+
if (contribution.kind === "specialized-registry") {
|
|
1592
|
+
const details = contribution.details;
|
|
1593
|
+
const sourceKind = details.registry ? REGISTRY_TO_SOURCE_KIND[details.registry] : void 0;
|
|
1594
|
+
if (sourceKind) {
|
|
1595
|
+
pushRef(
|
|
1596
|
+
sourceKind,
|
|
1597
|
+
details.registryId ?? contribution.id,
|
|
1598
|
+
contributionSource,
|
|
1599
|
+
ref.factSection,
|
|
1600
|
+
ref.factKey
|
|
1601
|
+
);
|
|
1602
|
+
}
|
|
1603
|
+
continue;
|
|
1604
|
+
}
|
|
1605
|
+
const mapped = CONTRIBUTION_KIND_TO_SOURCE_KIND[contribution.kind];
|
|
1606
|
+
if (mapped) pushRef(mapped, contribution.id, contributionSource, ref.factSection, ref.factKey);
|
|
1607
|
+
}
|
|
1608
|
+
for (const host of extensionSurfaces.hosts) {
|
|
1609
|
+
provenanceCandidates.push({
|
|
1610
|
+
kind: "extension-host",
|
|
1611
|
+
id: host.key,
|
|
1612
|
+
...host.source.kind === "fact-ref" ? {} : { source: { sourcePath: host.source.path } },
|
|
1613
|
+
factRef: { factSection: "extensionSurfaces.hosts", factKey: host.key }
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1616
|
+
for (const contribution of extensionSurfaces.contributions) {
|
|
1617
|
+
pushRef(
|
|
1618
|
+
"extension-contribution",
|
|
1619
|
+
contribution.id,
|
|
1620
|
+
{ sourcePath: contribution.source.path },
|
|
1621
|
+
"extensionSurfaces.contributions",
|
|
1622
|
+
contribution.id
|
|
1623
|
+
);
|
|
1624
|
+
}
|
|
1625
|
+
const provenance = buildProvenanceIndex(provenanceCandidates);
|
|
1626
|
+
const factDiagnostics = [
|
|
1627
|
+
...provenance.diagnostics,
|
|
1628
|
+
...workers.unresolved,
|
|
1629
|
+
...pageMiddleware.unresolved,
|
|
1630
|
+
...generatorPlugins.unresolved,
|
|
1631
|
+
...diRegistrations.unresolvedTokens.map((token) => ({
|
|
1632
|
+
code: "unresolved-static-contract",
|
|
1633
|
+
kind: "di-registration",
|
|
1634
|
+
id: token,
|
|
1635
|
+
...diSourcePath ? { source: { sourcePath: diSourcePath, exportName: token } } : {}
|
|
1636
|
+
}))
|
|
1637
|
+
].sort(
|
|
1638
|
+
(left, right) => left.code.localeCompare(right.code) || left.kind.localeCompare(right.kind) || left.id.localeCompare(right.id)
|
|
1639
|
+
);
|
|
1640
|
+
const facts = {
|
|
561
1641
|
module: moduleId,
|
|
562
1642
|
title,
|
|
563
1643
|
description,
|
|
564
1644
|
coreVersion,
|
|
1645
|
+
sourcePackage,
|
|
1646
|
+
sourceVersion,
|
|
1647
|
+
sourceRoot,
|
|
1648
|
+
...options.sourceKind === "local-reference" ? { sourceKind: options.sourceKind } : {},
|
|
565
1649
|
entities,
|
|
566
1650
|
events,
|
|
567
1651
|
aclFeatures,
|
|
@@ -571,33 +1655,54 @@ function extractModuleFacts(options) {
|
|
|
571
1655
|
hostTokens,
|
|
572
1656
|
notifications,
|
|
573
1657
|
cli,
|
|
1658
|
+
backendPages,
|
|
1659
|
+
frontendPages,
|
|
1660
|
+
cliCommands,
|
|
1661
|
+
aiTools,
|
|
1662
|
+
aiAgents,
|
|
1663
|
+
extensionSurfaces,
|
|
1664
|
+
...provenance.factSources.length > 0 ? { factSources: provenance.factSources } : {},
|
|
1665
|
+
...ownedContracts ? { ownedContracts } : {},
|
|
1666
|
+
...factDiagnostics.length > 0 ? { factDiagnostics } : {},
|
|
574
1667
|
warnings
|
|
575
1668
|
};
|
|
1669
|
+
const overrides = collectModuleOverrideTargets(facts, buildFactSourceLookup(facts));
|
|
1670
|
+
if (overrides.overrideTargets.length > 0) facts.overrideTargets = overrides.overrideTargets;
|
|
1671
|
+
if (overrides.overrideTargetDiagnostics.length > 0) {
|
|
1672
|
+
facts.overrideTargetDiagnostics = overrides.overrideTargetDiagnostics;
|
|
1673
|
+
}
|
|
1674
|
+
return facts;
|
|
576
1675
|
}
|
|
577
1676
|
const EMPTY_SECTION_MARKER = "_none_";
|
|
578
|
-
function renderVersionStamp(coreVersion) {
|
|
1677
|
+
function renderVersionStamp(coreVersion, sourcePackage, sourceVersion) {
|
|
1678
|
+
if (sourcePackage) {
|
|
1679
|
+
return `<!-- generated from ${sourcePackage} ${sourceVersion || "<unknown>"}; core ${coreVersion || "<unknown>"} \u2014 R1 staleness stamp -->`;
|
|
1680
|
+
}
|
|
579
1681
|
const version = coreVersion && coreVersion.length > 0 ? coreVersion : "<unknown>";
|
|
580
1682
|
return `<!-- generated from @open-mercato/core ${version} \u2014 R1 staleness stamp -->`;
|
|
581
1683
|
}
|
|
582
|
-
function renderEntitiesSection(entities) {
|
|
1684
|
+
function renderEntitiesSection(entities, lookup) {
|
|
583
1685
|
if (entities.length === 0) return `## Entities
|
|
584
1686
|
|
|
585
1687
|
${EMPTY_SECTION_MARKER}`;
|
|
586
|
-
const header = "| Entity ID | Class | Table | Editable | CustomFields |";
|
|
587
|
-
const divider = "
|
|
1688
|
+
const header = "| Entity ID | Class | Table | Editable | CustomFields | Source |";
|
|
1689
|
+
const divider = "|---|---|---|---|---|---|";
|
|
588
1690
|
const rows = entities.map(
|
|
589
|
-
(entity) => `| ${entity.id} | ${entity.class} | ${entity.table} | ${entity.editable ? "yes" : "no"} | ${entity.customFields ? "yes" : "no"} |`
|
|
1691
|
+
(entity) => `| ${entity.id} | ${entity.class} | ${entity.table} | ${entity.editable ? "yes" : "no"} | ${entity.customFields ? "yes" : "no"} | ${renderLookedUpSource(lookup, "entity", entity.id)} |`
|
|
590
1692
|
);
|
|
591
1693
|
return ["## Entities", "", header, divider, ...rows].join("\n");
|
|
592
1694
|
}
|
|
593
|
-
function renderEventsSection(events) {
|
|
1695
|
+
function renderEventsSection(events, lookup) {
|
|
594
1696
|
const heading = `## Events (${events.length})`;
|
|
595
1697
|
if (events.length === 0) return `${heading}
|
|
596
1698
|
|
|
597
1699
|
${EMPTY_SECTION_MARKER}`;
|
|
598
|
-
const header = "| ID | Category | Entity |";
|
|
599
|
-
const divider = "
|
|
600
|
-
const rows = events.map((event) =>
|
|
1700
|
+
const header = "| ID | Category | Entity | Browser transport | Source |";
|
|
1701
|
+
const divider = "|---|---|---|---|---|";
|
|
1702
|
+
const rows = events.map((event) => {
|
|
1703
|
+
const transports = [event.clientBroadcast ? "client" : null, event.portalBroadcast ? "portal" : null].filter((value) => value !== null);
|
|
1704
|
+
return `| ${event.id} | ${event.category ?? "\u2014"} | ${event.entity ?? "\u2014"} | ${transports.join(", ") || "\u2014"} | ${renderLookedUpSource(lookup, "event", event.id)} |`;
|
|
1705
|
+
});
|
|
601
1706
|
return [heading, "", header, divider, ...rows].join("\n");
|
|
602
1707
|
}
|
|
603
1708
|
function renderInlineListSection(heading, values) {
|
|
@@ -608,6 +1713,62 @@ ${EMPTY_SECTION_MARKER}`;
|
|
|
608
1713
|
|
|
609
1714
|
${values.join(" \xB7 ")}`;
|
|
610
1715
|
}
|
|
1716
|
+
function renderSourceLinkedListSection(heading, values, lookup, kind, idColumnLabel) {
|
|
1717
|
+
if (values.length === 0) return `${heading}
|
|
1718
|
+
|
|
1719
|
+
${EMPTY_SECTION_MARKER}`;
|
|
1720
|
+
const rows = values.map((value) => `| ${value} | ${renderLookedUpSource(lookup, kind, value)} |`);
|
|
1721
|
+
return [heading, "", `| ${idColumnLabel} | Source |`, "|---|---|", ...rows].join("\n");
|
|
1722
|
+
}
|
|
1723
|
+
function isExactSourceFilePath(sourcePath) {
|
|
1724
|
+
return MODULE_CODE_EXTENSIONS.some((extension) => sourcePath.endsWith(extension));
|
|
1725
|
+
}
|
|
1726
|
+
function renderSourceLink(sourcePath) {
|
|
1727
|
+
if (!isExactSourceFilePath(sourcePath)) return sourcePath;
|
|
1728
|
+
return `[${sourcePath}](../../../${sourcePath})`;
|
|
1729
|
+
}
|
|
1730
|
+
function renderSourceRefLink(source) {
|
|
1731
|
+
const anchor = source.line ? `#L${source.line}` : "";
|
|
1732
|
+
const label = source.line ? `${source.sourcePath}:${source.line}` : source.sourcePath;
|
|
1733
|
+
if (!isExactSourceFilePath(source.sourcePath)) return label;
|
|
1734
|
+
return `[${label}](../../../${source.sourcePath}${anchor})`;
|
|
1735
|
+
}
|
|
1736
|
+
function renderLookedUpSource(lookup, kind, id) {
|
|
1737
|
+
const source = lookup(kind, id);
|
|
1738
|
+
return source ? renderSourceRefLink(source) : "\u2014";
|
|
1739
|
+
}
|
|
1740
|
+
function renderSafeScalar(value) {
|
|
1741
|
+
if (value === void 0 || value === null) return "\u2014";
|
|
1742
|
+
if (Array.isArray(value)) return value.length > 0 ? value.map((entry) => String(entry)).join(", ") : "\u2014";
|
|
1743
|
+
return String(value);
|
|
1744
|
+
}
|
|
1745
|
+
function renderOwnedContractMetadata(metadata) {
|
|
1746
|
+
if (!metadata) return "\u2014";
|
|
1747
|
+
const keys = Object.keys(metadata).sort((left, right) => left.localeCompare(right));
|
|
1748
|
+
if (keys.length === 0) return "\u2014";
|
|
1749
|
+
return keys.map((key) => `${key}=${renderSafeScalar(metadata[key])}`).join("; ");
|
|
1750
|
+
}
|
|
1751
|
+
function renderOwnedContractSection(heading, facts) {
|
|
1752
|
+
if (!facts || facts.length === 0) return "";
|
|
1753
|
+
const rows = facts.map(
|
|
1754
|
+
(fact) => `| ${fact.id} | ${renderOwnedContractMetadata(fact.metadata)} | ${renderSourceRefLink(fact.source)} |`
|
|
1755
|
+
);
|
|
1756
|
+
return [heading, "", "| ID | Metadata | Source |", "|---|---|---|", ...rows].join("\n");
|
|
1757
|
+
}
|
|
1758
|
+
function renderDiRegistrationsSection(facts) {
|
|
1759
|
+
if (!facts || facts.length === 0) return "";
|
|
1760
|
+
const rows = facts.map((fact) => {
|
|
1761
|
+
const metadata = fact.metadata ?? {};
|
|
1762
|
+
return `| ${fact.id} | ${renderSafeScalar(metadata.registrationKind)} | ${renderSafeScalar(metadata.lifetime)} | ${renderSafeScalar(metadata.injectionMode)} | ${renderSafeScalar(metadata.providerSymbol)} | ${renderSourceRefLink(fact.source)} |`;
|
|
1763
|
+
});
|
|
1764
|
+
return [
|
|
1765
|
+
"## DI registrations (rich)",
|
|
1766
|
+
"",
|
|
1767
|
+
"| Token | Kind | Lifetime | Injection | Provider | Source |",
|
|
1768
|
+
"|---|---|---|---|---|---|",
|
|
1769
|
+
...rows
|
|
1770
|
+
].join("\n");
|
|
1771
|
+
}
|
|
611
1772
|
function describeAuthRule(rule) {
|
|
612
1773
|
if (!rule) return "public";
|
|
613
1774
|
if (rule.requireFeatures && rule.requireFeatures.length > 0) return rule.requireFeatures.join(", ");
|
|
@@ -630,42 +1791,257 @@ function renderApiRoutesSection(routes) {
|
|
|
630
1791
|
if (routes.length === 0) return `## API routes
|
|
631
1792
|
|
|
632
1793
|
${EMPTY_SECTION_MARKER}`;
|
|
633
|
-
const header = "| Path | Methods | Auth (per-method requireFeatures) |";
|
|
634
|
-
const divider = "
|
|
1794
|
+
const header = "| Path | Methods | Auth (per-method requireFeatures) | Source |";
|
|
1795
|
+
const divider = "|---|---|---|---|";
|
|
635
1796
|
const rows = routes.map(
|
|
636
|
-
(route) => `| ${route.path} | ${route.methods.join(" ")} | ${renderApiRouteAuthCell(route)} |`
|
|
1797
|
+
(route) => `| ${route.path} | ${route.methods.join(" ")} | ${renderApiRouteAuthCell(route)} | ${route.sourcePath ? renderSourceLink(route.sourcePath) : "\u2014"} |`
|
|
637
1798
|
);
|
|
638
1799
|
return ["## API routes", "", header, divider, ...rows].join("\n");
|
|
639
1800
|
}
|
|
1801
|
+
function renderLinkedFactsSection(heading, facts) {
|
|
1802
|
+
if (facts.length === 0) return `${heading}
|
|
1803
|
+
|
|
1804
|
+
${EMPTY_SECTION_MARKER}`;
|
|
1805
|
+
const rows = facts.map((fact) => `| ${fact.label} | ${renderSourceLink(fact.sourcePath)} |`);
|
|
1806
|
+
return [heading, "", "| ID / path | Source |", "|---|---|", ...rows].join("\n");
|
|
1807
|
+
}
|
|
640
1808
|
function renderHostTokensSection(hostTokens) {
|
|
641
1809
|
const entityIdsLine = hostTokens.entityIds.length > 0 ? hostTokens.entityIds.join(" \xB7 ") : EMPTY_SECTION_MARKER;
|
|
642
1810
|
const tableIdsLine = hostTokens.tableIds.length > 0 ? hostTokens.tableIds.join(" \xB7 ") : EMPTY_SECTION_MARKER;
|
|
643
1811
|
return ["## Host extension points", "", `- Entity IDs: ${entityIdsLine}`, `- Table IDs: ${tableIdsLine}`].join("\n");
|
|
644
1812
|
}
|
|
645
|
-
function
|
|
646
|
-
|
|
1813
|
+
function renderExtensionHostContext(host) {
|
|
1814
|
+
return host.contextContract ?? host.runtimeContract ?? host.scopeContract ?? "\u2014";
|
|
1815
|
+
}
|
|
1816
|
+
function renderExtensionHostSource(host) {
|
|
1817
|
+
if (host.source.kind === "fact-ref") return `${host.source.factSection}:${host.source.factKey}`;
|
|
1818
|
+
return renderSourceRefLink({ sourcePath: host.source.path });
|
|
1819
|
+
}
|
|
1820
|
+
function renderExtensionHostsSection(extensionSurfaces) {
|
|
1821
|
+
const boundHosts = extensionSurfaces.hosts.filter((host) => host.bound);
|
|
1822
|
+
if (boundHosts.length === 0) return `## UMES hosts
|
|
1823
|
+
|
|
1824
|
+
${EMPTY_SECTION_MARKER}`;
|
|
1825
|
+
const rows = boundHosts.map(
|
|
1826
|
+
(host) => `| ${host.id} | ${host.family} | ${host.capabilities.join(", ") || "\u2014"} | ${renderExtensionHostContext(host)} | ${host.stability.toUpperCase()} | ${renderExtensionHostSource(host)} |`
|
|
1827
|
+
);
|
|
1828
|
+
return [
|
|
1829
|
+
"## UMES hosts",
|
|
1830
|
+
"",
|
|
1831
|
+
"| ID / pattern | Family | Supports | Context | Stability | Source |",
|
|
1832
|
+
"|---|---|---|---|---|---|",
|
|
1833
|
+
...rows
|
|
1834
|
+
].join("\n");
|
|
1835
|
+
}
|
|
1836
|
+
function compactContributionDetails(contribution) {
|
|
1837
|
+
const details = contribution.details;
|
|
1838
|
+
return Object.keys(details).sort((left, right) => left.localeCompare(right)).flatMap((key) => {
|
|
1839
|
+
const value = details[key];
|
|
1840
|
+
if (value === void 0) return [];
|
|
1841
|
+
if (Array.isArray(value)) return [`${key}=${value.join(",") || "none"}`];
|
|
1842
|
+
if (value && typeof value === "object") {
|
|
1843
|
+
const nested = Object.keys(value).sort((left, right) => left.localeCompare(right)).map((nestedKey) => {
|
|
1844
|
+
const nestedValue = value[nestedKey];
|
|
1845
|
+
return `${nestedKey}:${Array.isArray(nestedValue) ? nestedValue.join(",") : String(nestedValue)}`;
|
|
1846
|
+
});
|
|
1847
|
+
return [`${key}={${nested.join(";")}}`];
|
|
1848
|
+
}
|
|
1849
|
+
return [`${key}=${String(value)}`];
|
|
1850
|
+
}).join("; ");
|
|
1851
|
+
}
|
|
1852
|
+
function renderExtensionContributionsSection(extensionSurfaces) {
|
|
1853
|
+
if (extensionSurfaces.contributions.length === 0) return `## UMES contributions
|
|
1854
|
+
|
|
1855
|
+
${EMPTY_SECTION_MARKER}`;
|
|
1856
|
+
const rows = extensionSurfaces.contributions.map((contribution) => {
|
|
1857
|
+
const targets = contribution.targets.map((entry) => entry.id).join(", ");
|
|
1858
|
+
const resolution = contribution.targets.map((entry) => entry.resolution).join(", ");
|
|
1859
|
+
const phases = [...contribution.phases ?? [], ...contribution.operations ?? []].join(", ") || "\u2014";
|
|
1860
|
+
const source = renderSourceRefLink({ sourcePath: contribution.source.path });
|
|
1861
|
+
return `| ${contribution.id} | ${contribution.kind} | ${targets || "\u2014"} | ${phases} | ${compactContributionDetails(contribution)} | ${resolution || "\u2014"} | ${source} |`;
|
|
1862
|
+
});
|
|
1863
|
+
return [
|
|
1864
|
+
"## UMES contributions",
|
|
1865
|
+
"",
|
|
1866
|
+
"| ID | Kind | Target | Phase / operations | Contract | Resolution | Source |",
|
|
1867
|
+
"|---|---|---|---|---|---|---|",
|
|
1868
|
+
...rows
|
|
1869
|
+
].join("\n");
|
|
1870
|
+
}
|
|
1871
|
+
function renderExtensionDiagnosticsSection(extensionSurfaces) {
|
|
1872
|
+
const unbound = extensionSurfaces.hosts.filter((host) => !host.bound);
|
|
1873
|
+
if (unbound.length === 0 && extensionSurfaces.unresolved.length === 0) return "";
|
|
1874
|
+
const diagnostics = [
|
|
1875
|
+
...unbound.map((host) => `- unbound-helper: ${host.id}`),
|
|
1876
|
+
...extensionSurfaces.unresolved.map((entry) => `- ${entry.reason}: ${entry.key} (${entry.source.path})`)
|
|
1877
|
+
];
|
|
1878
|
+
return ["## UMES diagnostics", "", ...diagnostics].join("\n");
|
|
1879
|
+
}
|
|
1880
|
+
function renderExtensionTargetRef(target) {
|
|
1881
|
+
const method = target.method ? ` ${target.method}` : "";
|
|
1882
|
+
const owner = target.moduleId ? ` @${target.moduleId}` : "";
|
|
1883
|
+
return `${target.kind}:${target.id}${method}${owner}`;
|
|
1884
|
+
}
|
|
1885
|
+
function renderActivationBindingsSection(extensionSurfaces) {
|
|
1886
|
+
const activations = extensionSurfaces.activations ?? [];
|
|
1887
|
+
if (activations.length === 0) return `## Active extension bindings
|
|
1888
|
+
|
|
1889
|
+
${EMPTY_SECTION_MARKER}`;
|
|
1890
|
+
const rows = activations.map((activation) => {
|
|
1891
|
+
const phases = activation.phases && activation.phases.length > 0 ? activation.phases.join(", ") : "\u2014";
|
|
1892
|
+
const source = activation.source ? renderSourceRefLink(activation.source) : "\u2014";
|
|
1893
|
+
const bridge = activation.bridge ? `${activation.bridge.factSection}:${activation.bridge.factKey}` : "\u2014";
|
|
1894
|
+
return `| ${activation.id} | ${activation.kind} | ${renderExtensionTargetRef(activation.host)} | ${activation.contributionKinds.join(", ") || "\u2014"} | ${phases} | ${bridge} | ${source} |`;
|
|
1895
|
+
});
|
|
1896
|
+
return [
|
|
1897
|
+
"## Active extension bindings",
|
|
1898
|
+
"",
|
|
1899
|
+
"| Activation | Kind | Host | Contribution kinds | Phases | Bridge | Source |",
|
|
1900
|
+
"|---|---|---|---|---|---|---|",
|
|
1901
|
+
...rows
|
|
1902
|
+
].join("\n");
|
|
1903
|
+
}
|
|
1904
|
+
function renderIncomingContributionsSection(extensionSurfaces) {
|
|
1905
|
+
const incoming = extensionSurfaces.incoming ?? [];
|
|
1906
|
+
if (incoming.length === 0) return `## Incoming installed contributions
|
|
1907
|
+
|
|
1908
|
+
${EMPTY_SECTION_MARKER}`;
|
|
1909
|
+
const rows = incoming.map((entry) => {
|
|
1910
|
+
const activation = entry.activationId ?? "\u2014";
|
|
1911
|
+
const source = entry.source ? renderSourceRefLink(entry.source) : "\u2014";
|
|
1912
|
+
return `| ${entry.contributorModuleId} | ${entry.contributionKind} | ${renderExtensionTargetRef(entry.target)} | ${entry.resolution} | ${activation} | ${entry.contributionId} \xB7 ${source} |`;
|
|
1913
|
+
});
|
|
1914
|
+
return [
|
|
1915
|
+
"## Incoming installed contributions",
|
|
1916
|
+
"",
|
|
1917
|
+
"| Contributor | Kind | Target | Resolution | Activation | Contribution \xB7 Source |",
|
|
1918
|
+
"|---|---|---|---|---|---|",
|
|
1919
|
+
...rows
|
|
1920
|
+
].join("\n");
|
|
1921
|
+
}
|
|
1922
|
+
function renderContributionResolutionsSection(extensionSurfaces, lookup) {
|
|
1923
|
+
const resolutions = extensionSurfaces.contributionResolutions ?? [];
|
|
1924
|
+
if (resolutions.length === 0) return `## Contribution resolutions
|
|
1925
|
+
|
|
1926
|
+
${EMPTY_SECTION_MARKER}`;
|
|
1927
|
+
const rows = resolutions.map((resolution) => {
|
|
1928
|
+
const activations = resolution.activationIds.length > 0 ? resolution.activationIds.join(", ") : "\u2014";
|
|
1929
|
+
const source = renderLookedUpSource(lookup, "extension-contribution", resolution.contributionId);
|
|
1930
|
+
return `| ${resolution.contributionId} | ${renderExtensionTargetRef(resolution.target)} | ${resolution.resolution} | ${activations} | ${source} |`;
|
|
1931
|
+
});
|
|
1932
|
+
return [
|
|
1933
|
+
"## Contribution resolutions",
|
|
1934
|
+
"",
|
|
1935
|
+
"| Contribution | Target | Resolution | Activations | Source |",
|
|
1936
|
+
"|---|---|---|---|---|",
|
|
1937
|
+
...rows
|
|
1938
|
+
].join("\n");
|
|
1939
|
+
}
|
|
1940
|
+
function renderOverrideTargetPath(target) {
|
|
1941
|
+
let display = "overrides";
|
|
1942
|
+
for (let index = 0; index < target.path.length; index += 1) {
|
|
1943
|
+
const segment = target.path[index];
|
|
1944
|
+
const isKeyedTerminal = target.key !== void 0 && index === target.path.length - 1;
|
|
1945
|
+
display += isKeyedTerminal ? `[${JSON.stringify(segment)}]` : `.${segment}`;
|
|
1946
|
+
}
|
|
1947
|
+
return display;
|
|
1948
|
+
}
|
|
1949
|
+
function renderOverrideTargetsSection(targets) {
|
|
1950
|
+
if (!targets || targets.length === 0) return `## Exact override targets
|
|
1951
|
+
|
|
1952
|
+
${EMPTY_SECTION_MARKER}`;
|
|
1953
|
+
const header = "| Domain | Path / key | Supported modes | Referenced fact | Source |";
|
|
1954
|
+
const divider = "|---|---|---|---|---|";
|
|
1955
|
+
const rows = targets.map((target) => {
|
|
1956
|
+
const path2 = `\`${renderOverrideTargetPath(target)}\``;
|
|
1957
|
+
const modes = target.modes.join(", ") || "\u2014";
|
|
1958
|
+
const fact = `${target.factRef.factSection}:${target.factRef.factKey}`;
|
|
1959
|
+
return `| ${target.domain} | ${path2} | ${modes} | ${fact} | ${renderSourceRefLink(target.source)} |`;
|
|
1960
|
+
});
|
|
1961
|
+
return ["## Exact override targets", "", header, divider, ...rows].join("\n");
|
|
1962
|
+
}
|
|
1963
|
+
function renderReferenceProjectionHeader(facts, fingerprints) {
|
|
1964
|
+
return [
|
|
1965
|
+
`# ${facts.module} \u2014 module facts (generated reference projection, do not edit)`,
|
|
1966
|
+
`<!-- generated from local reference ${facts.sourceRoot}; source-fingerprint ${fingerprints.sourceFingerprint}; taxonomy-fingerprint ${fingerprints.taxonomyFingerprint} \u2014 R1 staleness stamp -->`,
|
|
1967
|
+
`> Reference projection: \`${facts.module}\` is not selected by the current runtime. Its activations and exact override targets describe behavior only after opt-in in \`src/modules.ts\`.`,
|
|
1968
|
+
`Source root: ${renderSourceLink(facts.sourceRoot)}`
|
|
1969
|
+
];
|
|
1970
|
+
}
|
|
1971
|
+
function renderModuleFactsMarkdown(facts, referenceProjection) {
|
|
1972
|
+
const extensionSurfaces = facts.extensionSurfaces ?? { hosts: [], contributions: [], unresolved: [] };
|
|
1973
|
+
const lookup = buildFactSourceLookup(facts);
|
|
1974
|
+
const header = facts.sourceKind === "local-reference" && referenceProjection ? renderReferenceProjectionHeader(facts, referenceProjection) : [
|
|
647
1975
|
`# ${facts.module} \u2014 module facts (generated, do not edit)`,
|
|
648
|
-
renderVersionStamp(facts.coreVersion),
|
|
1976
|
+
renderVersionStamp(facts.coreVersion, facts.sourcePackage, facts.sourceVersion),
|
|
1977
|
+
`Source root: ${renderSourceLink(facts.sourceRoot)}`
|
|
1978
|
+
];
|
|
1979
|
+
const sections = [
|
|
1980
|
+
...header,
|
|
649
1981
|
"",
|
|
650
|
-
renderEntitiesSection(facts.entities),
|
|
1982
|
+
renderEntitiesSection(facts.entities, lookup),
|
|
651
1983
|
"",
|
|
652
|
-
renderEventsSection(facts.events),
|
|
1984
|
+
renderEventsSection(facts.events, lookup),
|
|
653
1985
|
"",
|
|
654
|
-
|
|
1986
|
+
renderSourceLinkedListSection(
|
|
1987
|
+
`## ACL features (${facts.aclFeatures.length})`,
|
|
1988
|
+
facts.aclFeatures,
|
|
1989
|
+
lookup,
|
|
1990
|
+
"acl-feature",
|
|
1991
|
+
"Feature"
|
|
1992
|
+
),
|
|
655
1993
|
"",
|
|
656
1994
|
renderApiRoutesSection(facts.apiRoutes),
|
|
657
1995
|
"",
|
|
658
|
-
|
|
1996
|
+
renderLinkedFactsSection("## Backend pages", facts.backendPages.map((page) => ({ label: page.path, sourcePath: page.sourcePath }))),
|
|
1997
|
+
"",
|
|
1998
|
+
renderLinkedFactsSection("## Frontend pages", facts.frontendPages.map((page) => ({ label: page.path, sourcePath: page.sourcePath }))),
|
|
1999
|
+
"",
|
|
2000
|
+
renderSourceLinkedListSection("## DI service tokens", facts.diTokens, lookup, "di-registration", "Token"),
|
|
659
2001
|
"",
|
|
660
|
-
|
|
2002
|
+
renderSourceLinkedListSection("## Search entities", facts.searchEntities, lookup, "search", "Entity ID"),
|
|
661
2003
|
"",
|
|
662
2004
|
renderHostTokensSection(facts.hostTokens),
|
|
663
2005
|
"",
|
|
664
|
-
|
|
2006
|
+
renderExtensionHostsSection(extensionSurfaces),
|
|
2007
|
+
"",
|
|
2008
|
+
renderExtensionContributionsSection(extensionSurfaces),
|
|
2009
|
+
"",
|
|
2010
|
+
renderActivationBindingsSection(extensionSurfaces),
|
|
2011
|
+
"",
|
|
2012
|
+
renderIncomingContributionsSection(extensionSurfaces),
|
|
2013
|
+
"",
|
|
2014
|
+
renderContributionResolutionsSection(extensionSurfaces, lookup),
|
|
2015
|
+
"",
|
|
2016
|
+
renderExtensionDiagnosticsSection(extensionSurfaces),
|
|
2017
|
+
"",
|
|
2018
|
+
renderSourceLinkedListSection("## Notifications", facts.notifications, lookup, "notification", "Notification ID"),
|
|
665
2019
|
"",
|
|
666
|
-
|
|
2020
|
+
renderLinkedFactsSection("## CLI commands", facts.cliCommands.map((command) => ({ label: command.command, sourcePath: command.sourcePath }))),
|
|
2021
|
+
"",
|
|
2022
|
+
renderLinkedFactsSection("## AI tools / MCP capabilities", facts.aiTools.map((tool) => ({ label: tool.name, sourcePath: tool.sourcePath }))),
|
|
2023
|
+
"",
|
|
2024
|
+
renderLinkedFactsSection("## AI agents", facts.aiAgents.map((agent) => ({ label: agent.id, sourcePath: agent.sourcePath }))),
|
|
2025
|
+
"",
|
|
2026
|
+
renderOverrideTargetsSection(facts.overrideTargets),
|
|
667
2027
|
""
|
|
668
2028
|
];
|
|
2029
|
+
const owned = facts.ownedContracts ?? {};
|
|
2030
|
+
const ownedSections = [
|
|
2031
|
+
renderOwnedContractSection("## Owned contract \u2014 module metadata", owned["module-metadata"]),
|
|
2032
|
+
renderOwnedContractSection("## Domain commands", owned.command),
|
|
2033
|
+
renderOwnedContractSection("## Workers", owned.worker),
|
|
2034
|
+
renderOwnedContractSection("## Page middleware", owned["page-middleware"]),
|
|
2035
|
+
renderOwnedContractSection("## Setup", owned.setup),
|
|
2036
|
+
renderOwnedContractSection("## Encryption", owned.encryption),
|
|
2037
|
+
renderDiRegistrationsSection(owned["di-registration"]),
|
|
2038
|
+
renderOwnedContractSection("## Custom entities", owned["custom-entity"]),
|
|
2039
|
+
renderOwnedContractSection("## AI extensions", owned["ai-extension"]),
|
|
2040
|
+
renderOwnedContractSection("## Generator plugins", owned["generator-plugin"])
|
|
2041
|
+
].filter((section) => section.length > 0);
|
|
2042
|
+
for (const section of ownedSections) {
|
|
2043
|
+
sections.push(section, "");
|
|
2044
|
+
}
|
|
669
2045
|
return sections.join("\n");
|
|
670
2046
|
}
|
|
671
2047
|
function toModuleFactsJsonEntry(facts) {
|
|
@@ -673,15 +2049,36 @@ function toModuleFactsJsonEntry(facts) {
|
|
|
673
2049
|
title: facts.title,
|
|
674
2050
|
description: facts.description,
|
|
675
2051
|
coreVersion: facts.coreVersion,
|
|
2052
|
+
sourcePackage: facts.sourcePackage,
|
|
2053
|
+
sourceVersion: facts.sourceVersion,
|
|
2054
|
+
sourceRoot: facts.sourceRoot,
|
|
2055
|
+
...facts.sourceKind === "local-reference" ? { sourceKind: facts.sourceKind } : {},
|
|
676
2056
|
entities: facts.entities,
|
|
677
|
-
events: facts.events.map((event) => ({
|
|
2057
|
+
events: facts.events.map((event) => ({
|
|
2058
|
+
id: event.id,
|
|
2059
|
+
category: event.category,
|
|
2060
|
+
entity: event.entity,
|
|
2061
|
+
...event.clientBroadcast !== void 0 ? { clientBroadcast: event.clientBroadcast } : {},
|
|
2062
|
+
...event.portalBroadcast !== void 0 ? { portalBroadcast: event.portalBroadcast } : {}
|
|
2063
|
+
})),
|
|
678
2064
|
aclFeatures: facts.aclFeatures,
|
|
679
2065
|
apiRoutes: facts.apiRoutes,
|
|
680
2066
|
diTokens: facts.diTokens,
|
|
681
2067
|
searchEntities: facts.searchEntities,
|
|
682
2068
|
hostTokens: facts.hostTokens,
|
|
683
2069
|
notifications: facts.notifications,
|
|
684
|
-
cli: facts.cli
|
|
2070
|
+
cli: facts.cli,
|
|
2071
|
+
backendPages: facts.backendPages,
|
|
2072
|
+
frontendPages: facts.frontendPages,
|
|
2073
|
+
cliCommands: facts.cliCommands,
|
|
2074
|
+
aiTools: facts.aiTools,
|
|
2075
|
+
aiAgents: facts.aiAgents,
|
|
2076
|
+
...facts.extensionSurfaces ? { extensionSurfaces: facts.extensionSurfaces } : {},
|
|
2077
|
+
...facts.factSources && facts.factSources.length > 0 ? { factSources: facts.factSources } : {},
|
|
2078
|
+
...facts.ownedContracts && Object.keys(facts.ownedContracts).length > 0 ? { ownedContracts: facts.ownedContracts } : {},
|
|
2079
|
+
...facts.factDiagnostics && facts.factDiagnostics.length > 0 ? { factDiagnostics: facts.factDiagnostics } : {},
|
|
2080
|
+
...facts.overrideTargets && facts.overrideTargets.length > 0 ? { overrideTargets: facts.overrideTargets } : {},
|
|
2081
|
+
...facts.overrideTargetDiagnostics && facts.overrideTargetDiagnostics.length > 0 ? { overrideTargetDiagnostics: facts.overrideTargetDiagnostics } : {}
|
|
685
2082
|
};
|
|
686
2083
|
}
|
|
687
2084
|
function buildModuleFactsJsonObject(factsByModule) {
|
|
@@ -695,7 +2092,167 @@ function renderModuleFactsJson(factsByModule) {
|
|
|
695
2092
|
return `${JSON.stringify(buildModuleFactsJsonObject(factsByModule), null, 2)}
|
|
696
2093
|
`;
|
|
697
2094
|
}
|
|
2095
|
+
function assertPackageModuleFactsOnly(factsByModule) {
|
|
2096
|
+
const offenders = Object.values(factsByModule).filter((facts) => facts.sourceKind === "local-reference" || !facts.sourceRoot.startsWith("node_modules/")).map((facts) => `${facts.module} (${facts.sourceRoot})`).sort((left, right) => left.localeCompare(right));
|
|
2097
|
+
if (offenders.length > 0) {
|
|
2098
|
+
throw new Error(
|
|
2099
|
+
`[module-facts] normal package output must not contain app-local modules: ${offenders.join(", ")}`
|
|
2100
|
+
);
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
function sha256Hex(value) {
|
|
2104
|
+
return createHash("sha256").update(value).digest("hex");
|
|
2105
|
+
}
|
|
2106
|
+
function canonicalizeFingerprintValue(value) {
|
|
2107
|
+
if (Array.isArray(value)) {
|
|
2108
|
+
return value.map((item) => {
|
|
2109
|
+
const canonical = canonicalizeFingerprintValue(item);
|
|
2110
|
+
return { canonical, text: JSON.stringify(canonical) ?? "" };
|
|
2111
|
+
}).sort((left, right) => left.text < right.text ? -1 : left.text > right.text ? 1 : 0).map((item) => item.canonical);
|
|
2112
|
+
}
|
|
2113
|
+
if (value !== null && typeof value === "object") {
|
|
2114
|
+
const record = value;
|
|
2115
|
+
const result = {};
|
|
2116
|
+
for (const key of Object.keys(record).sort((left, right) => left < right ? -1 : left > right ? 1 : 0)) {
|
|
2117
|
+
if (record[key] === void 0) continue;
|
|
2118
|
+
result[key] = canonicalizeFingerprintValue(record[key]);
|
|
2119
|
+
}
|
|
2120
|
+
return result;
|
|
2121
|
+
}
|
|
2122
|
+
return value;
|
|
2123
|
+
}
|
|
2124
|
+
function canonicalFingerprintJson(value) {
|
|
2125
|
+
return JSON.stringify(canonicalizeFingerprintValue(value));
|
|
2126
|
+
}
|
|
2127
|
+
const REFERENCE_FINGERPRINT_SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["__tests__", "__integration__", "node_modules"]);
|
|
2128
|
+
function computeModuleSourceFingerprint(moduleRoot, portableSourceRoot) {
|
|
2129
|
+
assertPortableSourceRoot(path.basename(portableSourceRoot), portableSourceRoot);
|
|
2130
|
+
const records = [];
|
|
2131
|
+
const walk = (directory) => {
|
|
2132
|
+
const entries = fs.readdirSync(directory, { withFileTypes: true });
|
|
2133
|
+
for (const entry of entries) {
|
|
2134
|
+
const fullPath = path.join(directory, entry.name);
|
|
2135
|
+
if (entry.isDirectory()) {
|
|
2136
|
+
if (REFERENCE_FINGERPRINT_SKIPPED_DIRECTORIES.has(entry.name)) continue;
|
|
2137
|
+
walk(fullPath);
|
|
2138
|
+
continue;
|
|
2139
|
+
}
|
|
2140
|
+
if (!entry.isFile()) continue;
|
|
2141
|
+
records.push({
|
|
2142
|
+
path: toPortableSourcePath(moduleRoot, portableSourceRoot, fullPath),
|
|
2143
|
+
sha256: sha256Hex(fs.readFileSync(fullPath))
|
|
2144
|
+
});
|
|
2145
|
+
}
|
|
2146
|
+
};
|
|
2147
|
+
walk(moduleRoot);
|
|
2148
|
+
records.sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0);
|
|
2149
|
+
return sha256Hex(canonicalFingerprintJson(records));
|
|
2150
|
+
}
|
|
2151
|
+
const TAXONOMY_EXCLUDED_SECTIONS = /* @__PURE__ */ new Set([
|
|
2152
|
+
"title",
|
|
2153
|
+
"description",
|
|
2154
|
+
"coreVersion",
|
|
2155
|
+
"sourcePackage",
|
|
2156
|
+
"sourceVersion",
|
|
2157
|
+
"sourceRoot",
|
|
2158
|
+
"sourceKind"
|
|
2159
|
+
]);
|
|
2160
|
+
function collectTaxonomyRecords(setName, value, records) {
|
|
2161
|
+
if (value === void 0) return;
|
|
2162
|
+
if (Array.isArray(value)) {
|
|
2163
|
+
for (const item of value) records.push({ setName, value: canonicalizeFingerprintValue(item) });
|
|
2164
|
+
return;
|
|
2165
|
+
}
|
|
2166
|
+
if (value !== null && typeof value === "object") {
|
|
2167
|
+
for (const [key, child] of Object.entries(value)) {
|
|
2168
|
+
collectTaxonomyRecords(`${setName}.${key}`, child, records);
|
|
2169
|
+
}
|
|
2170
|
+
return;
|
|
2171
|
+
}
|
|
2172
|
+
records.push({ setName, value });
|
|
2173
|
+
}
|
|
2174
|
+
function computeModuleTaxonomyFingerprint(entry) {
|
|
2175
|
+
const records = [];
|
|
2176
|
+
for (const [setName, value] of Object.entries(entry)) {
|
|
2177
|
+
if (TAXONOMY_EXCLUDED_SECTIONS.has(setName)) continue;
|
|
2178
|
+
collectTaxonomyRecords(setName, value, records);
|
|
2179
|
+
}
|
|
2180
|
+
const decorated = records.map((record) => ({ record, text: JSON.stringify(record.value) ?? "" })).sort(
|
|
2181
|
+
(left, right) => left.record.setName < right.record.setName ? -1 : left.record.setName > right.record.setName ? 1 : left.text < right.text ? -1 : left.text > right.text ? 1 : 0
|
|
2182
|
+
);
|
|
2183
|
+
return sha256Hex(JSON.stringify(decorated.map((item) => item.record)));
|
|
2184
|
+
}
|
|
2185
|
+
function renderReferenceModuleFactsJson(bundle) {
|
|
2186
|
+
const ordered = {};
|
|
2187
|
+
for (const moduleId of Object.keys(bundle).sort((left, right) => left < right ? -1 : left > right ? 1 : 0)) {
|
|
2188
|
+
ordered[moduleId] = bundle[moduleId];
|
|
2189
|
+
}
|
|
2190
|
+
return `${JSON.stringify(ordered, null, 2)}
|
|
2191
|
+
`;
|
|
2192
|
+
}
|
|
2193
|
+
function extractLocalReferenceModuleFacts(options) {
|
|
2194
|
+
const { reference } = options;
|
|
2195
|
+
if (reference.sourceKind !== "local-reference") {
|
|
2196
|
+
throw new Error(
|
|
2197
|
+
`[module-facts] reference source "${reference.moduleId}" must declare sourceKind "local-reference"`
|
|
2198
|
+
);
|
|
2199
|
+
}
|
|
2200
|
+
const portableSourceRoot = reference.portableSourceRoot;
|
|
2201
|
+
if (!portableSourceRoot) {
|
|
2202
|
+
throw new Error(`[module-facts] reference source "${reference.moduleId}" must declare portableSourceRoot`);
|
|
2203
|
+
}
|
|
2204
|
+
assertPortableSourceRoot(reference.moduleId, portableSourceRoot);
|
|
2205
|
+
const sources = appendLocalReferenceModuleSource(options.packageSources, reference);
|
|
2206
|
+
const activated = extractAllModuleFacts({
|
|
2207
|
+
sources,
|
|
2208
|
+
registryPath: options.registryPath ?? null,
|
|
2209
|
+
registrySource: options.registrySource ?? null,
|
|
2210
|
+
coreVersion: options.coreVersion ?? null,
|
|
2211
|
+
// The disposable projection collects rather than throws: an app-local module can name
|
|
2212
|
+
// a target whose classification is still missing from the shared extension reader, and
|
|
2213
|
+
// aborting there would delete the whole reference bundle instead of publishing the
|
|
2214
|
+
// exact gap. Normal package output keeps the strict default.
|
|
2215
|
+
unresolvedFirstPartyTargets: "collect"
|
|
2216
|
+
});
|
|
2217
|
+
const facts = activated.factsByModule[reference.moduleId];
|
|
2218
|
+
if (!facts) {
|
|
2219
|
+
throw new Error(`[module-facts] reference module "${reference.moduleId}" produced no facts`);
|
|
2220
|
+
}
|
|
2221
|
+
if (facts.sourceRoot !== portableSourceRoot) {
|
|
2222
|
+
throw new Error(
|
|
2223
|
+
`[module-facts] reference module "${reference.moduleId}" emitted non-portable source root "${facts.sourceRoot}"`
|
|
2224
|
+
);
|
|
2225
|
+
}
|
|
2226
|
+
const jsonEntry = toModuleFactsJsonEntry(facts);
|
|
2227
|
+
const fingerprints = {
|
|
2228
|
+
sourceFingerprint: computeModuleSourceFingerprint(reference.moduleRoot, portableSourceRoot),
|
|
2229
|
+
taxonomyFingerprint: computeModuleTaxonomyFingerprint(jsonEntry)
|
|
2230
|
+
};
|
|
2231
|
+
return {
|
|
2232
|
+
entry: {
|
|
2233
|
+
moduleId: reference.moduleId,
|
|
2234
|
+
projectionKind: "activated-reference",
|
|
2235
|
+
sourceKind: "local-reference",
|
|
2236
|
+
runtimeSelected: false,
|
|
2237
|
+
...fingerprints,
|
|
2238
|
+
factCoverage: activated.factCoverage,
|
|
2239
|
+
facts: jsonEntry
|
|
2240
|
+
},
|
|
2241
|
+
markdown: renderModuleFactsMarkdown(facts, fingerprints),
|
|
2242
|
+
warnings: activated.warnings,
|
|
2243
|
+
unresolvedTargets: activated.unresolvedFirstPartyTargets
|
|
2244
|
+
};
|
|
2245
|
+
}
|
|
2246
|
+
function collectUnresolvedFirstPartyTargets(surfacesByModule) {
|
|
2247
|
+
return Object.entries(surfacesByModule).flatMap(
|
|
2248
|
+
([moduleId, surface]) => surface.unresolved.filter((entry) => entry.reason === "unresolved-first-party-target").map((entry) => `${moduleId}:${entry.key}`)
|
|
2249
|
+
).sort((left, right) => left.localeCompare(right));
|
|
2250
|
+
}
|
|
698
2251
|
function extractAllModuleFacts(options) {
|
|
2252
|
+
return withModuleExtensionFactExtractionCache(() => extractAllModuleFactsWithCache(options));
|
|
2253
|
+
}
|
|
2254
|
+
function extractAllModuleFactsWithCache(options) {
|
|
2255
|
+
const factCoverage = buildModuleFactCoverageLedger();
|
|
699
2256
|
const sources = options.sources ? [...options.sources] : options.coreSrcRoot ? (options.moduleIds ?? []).map((moduleId) => ({
|
|
700
2257
|
moduleId,
|
|
701
2258
|
moduleRoot: path.join(options.coreSrcRoot, moduleId)
|
|
@@ -708,21 +2265,325 @@ function extractAllModuleFacts(options) {
|
|
|
708
2265
|
moduleId: source.moduleId,
|
|
709
2266
|
moduleRoot: source.moduleRoot,
|
|
710
2267
|
coreVersion: options.coreVersion ?? null,
|
|
2268
|
+
sourcePackage: source.from ?? null,
|
|
2269
|
+
sourceVersion: source.packageVersion ?? null,
|
|
711
2270
|
registryPath: options.registryPath ?? null,
|
|
712
|
-
registrySource: options.registrySource ?? null
|
|
2271
|
+
registrySource: options.registrySource ?? null,
|
|
2272
|
+
...source.portableSourceRoot ? { portableSourceRoot: source.portableSourceRoot } : {},
|
|
2273
|
+
...source.sourceKind ? { sourceKind: source.sourceKind } : {}
|
|
713
2274
|
});
|
|
714
2275
|
factsByModule[source.moduleId] = facts;
|
|
715
|
-
markdownByModule[source.moduleId] = renderModuleFactsMarkdown(facts);
|
|
716
2276
|
warnings.push(...facts.warnings);
|
|
717
2277
|
}
|
|
718
|
-
|
|
2278
|
+
const surfacesByModule = Object.fromEntries(
|
|
2279
|
+
Object.entries(factsByModule).map(([moduleId, facts]) => [
|
|
2280
|
+
moduleId,
|
|
2281
|
+
facts.extensionSurfaces ?? { hosts: [], contributions: [], unresolved: [] }
|
|
2282
|
+
])
|
|
2283
|
+
);
|
|
2284
|
+
const correlated = correlateModuleExtensionFacts({
|
|
2285
|
+
surfacesByModule,
|
|
2286
|
+
entityIds: new Set(Object.values(factsByModule).flatMap((facts) => facts.entities.map((entity) => entity.id))),
|
|
2287
|
+
eventIds: new Set(Object.values(factsByModule).flatMap((facts) => facts.events.map((event) => event.id))),
|
|
2288
|
+
apiRoutes: /* @__PURE__ */ new Set([
|
|
2289
|
+
...Object.values(factsByModule).flatMap((facts) => facts.apiRoutes.map((route) => route.path)),
|
|
2290
|
+
...sources.flatMap((source) => extractKnownApiRouteIds(source.moduleId, source.moduleRoot))
|
|
2291
|
+
]),
|
|
2292
|
+
commandIds: new Set(sources.flatMap((source) => extractKnownCommandIds(source.moduleId, source.moduleRoot)))
|
|
2293
|
+
});
|
|
2294
|
+
const unresolvedFirstPartyTargets = collectUnresolvedFirstPartyTargets(correlated);
|
|
2295
|
+
if ((options.unresolvedFirstPartyTargets ?? "throw") === "throw") assertNoUnresolvedExtensionTargets(correlated);
|
|
2296
|
+
const apiRouteOwners = /* @__PURE__ */ new Map();
|
|
2297
|
+
const commandOwners = /* @__PURE__ */ new Map();
|
|
2298
|
+
for (const source of [...sources].sort((left, right) => left.moduleId.localeCompare(right.moduleId))) {
|
|
2299
|
+
const facts = factsByModule[source.moduleId];
|
|
2300
|
+
const sourceRoot = facts ? facts.sourceRoot : source.moduleId;
|
|
2301
|
+
const owners = extractActivationTargetOwners({
|
|
2302
|
+
moduleId: source.moduleId,
|
|
2303
|
+
moduleRoot: source.moduleRoot,
|
|
2304
|
+
sourceRoot
|
|
2305
|
+
});
|
|
2306
|
+
for (const route of owners.apiRoutes) {
|
|
2307
|
+
if (!apiRouteOwners.has(route.id)) {
|
|
2308
|
+
apiRouteOwners.set(route.id, { moduleId: source.moduleId, source: route.source, bridges: route.bridges });
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
for (const command of owners.commands) {
|
|
2312
|
+
if (!commandOwners.has(command.id)) commandOwners.set(command.id, { moduleId: source.moduleId, source: command.source });
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
const withIncoming = correlateIncomingExtensions({
|
|
2316
|
+
surfacesByModule: correlated,
|
|
2317
|
+
apiRouteOwners,
|
|
2318
|
+
commandOwners
|
|
2319
|
+
});
|
|
2320
|
+
for (const moduleId of Object.keys(factsByModule).sort((left, right) => left.localeCompare(right))) {
|
|
2321
|
+
factsByModule[moduleId].extensionSurfaces = withIncoming[moduleId];
|
|
2322
|
+
markdownByModule[moduleId] = renderModuleFactsMarkdown(factsByModule[moduleId]);
|
|
2323
|
+
warnings.push(...correlated[moduleId].unresolved.map(
|
|
2324
|
+
(entry) => `[module-facts] ${moduleId} ${entry.reason}: ${entry.key} (${entry.source.path})`
|
|
2325
|
+
));
|
|
2326
|
+
}
|
|
2327
|
+
return {
|
|
2328
|
+
factsByModule,
|
|
2329
|
+
markdownByModule,
|
|
2330
|
+
warnings,
|
|
2331
|
+
frameworkMarkdown: renderFrameworkExtensionPointsMarkdown(),
|
|
2332
|
+
unresolvedFirstPartyTargets,
|
|
2333
|
+
factCoverage
|
|
2334
|
+
};
|
|
2335
|
+
}
|
|
2336
|
+
const FACT_SOURCE_KIND_COVERAGE = {
|
|
2337
|
+
"module-metadata": { value: "module-metadata", status: "emitted-example", note: "example/index.ts ModuleInfo export" },
|
|
2338
|
+
entity: { value: "entity", status: "emitted-example", note: "example/data/entities.ts ORM entities" },
|
|
2339
|
+
event: { value: "event", status: "emitted-example", note: "example/events.ts typed event definitions" },
|
|
2340
|
+
"acl-feature": { value: "acl-feature", status: "emitted-example", note: "example/acl.ts RBAC features" },
|
|
2341
|
+
"api-route": {
|
|
2342
|
+
value: "api-route",
|
|
2343
|
+
status: "emitted-example",
|
|
2344
|
+
note: "example/api/**/route.ts; route facts are read from the generated module registry, not from module source",
|
|
2345
|
+
requiresGeneratedRegistry: true
|
|
2346
|
+
},
|
|
2347
|
+
"di-registration": { value: "di-registration", status: "emitted-example", note: "example/di.ts Awilix container.register" },
|
|
2348
|
+
search: { value: "search", status: "emitted-example", note: "example/search.ts indexed entity declaration" },
|
|
2349
|
+
vector: {
|
|
2350
|
+
value: "vector",
|
|
2351
|
+
status: "pending-emission",
|
|
2352
|
+
note: "REQUIRED: a vector-search identity for the Todo entity; the example declares none today",
|
|
2353
|
+
pendingEmission: "slice-H-missing-example-surfaces"
|
|
2354
|
+
},
|
|
2355
|
+
notification: { value: "notification", status: "emitted-example", note: "example/notifications.ts notification types" },
|
|
2356
|
+
"cli-command": { value: "cli-command", status: "emitted-example", note: "example/cli.ts module commands" },
|
|
2357
|
+
"backend-page": { value: "backend-page", status: "emitted-example", note: "example/backend/**/page.tsx admin pages" },
|
|
2358
|
+
"frontend-page": { value: "frontend-page", status: "emitted-example", note: "example/frontend/**/page.tsx portal pages" },
|
|
2359
|
+
"ai-tool": { value: "ai-tool", status: "emitted-example", note: "example/ai-tools.ts defineAiTool declarations" },
|
|
2360
|
+
"ai-agent": { value: "ai-agent", status: "emitted-example", note: "example/ai-agents.ts defineAiAgent declarations" },
|
|
2361
|
+
"ai-extension": { value: "ai-extension", status: "emitted-example", note: "example AI agent/tool override and extension records" },
|
|
2362
|
+
command: { value: "command", status: "emitted-example", note: "example/commands/todos.ts command handlers" },
|
|
2363
|
+
subscriber: { value: "subscriber", status: "emitted-example", note: "example/subscribers/*.ts event subscribers" },
|
|
2364
|
+
worker: { value: "worker", status: "emitted-example", note: "example/workers/*.ts queue workers" },
|
|
2365
|
+
"page-middleware": { value: "page-middleware", status: "emitted-example", note: "example/backend/middleware.ts page guard" },
|
|
2366
|
+
setup: { value: "setup", status: "emitted-example", note: "example/setup.ts tenant setup hooks" },
|
|
2367
|
+
encryption: { value: "encryption", status: "emitted-example", note: "example/encryption.ts field encryption map" },
|
|
2368
|
+
"custom-entity": { value: "custom-entity", status: "emitted-example", note: "example/ce.ts custom entity and field DSL" },
|
|
2369
|
+
integration: { value: "integration", status: "emitted-example", note: "example/lib/mock-*-adapter.ts integration registrations" },
|
|
2370
|
+
"generator-plugin": {
|
|
2371
|
+
value: "generator-plugin",
|
|
2372
|
+
status: "pending-emission",
|
|
2373
|
+
note: "REQUIRED: an example/generators.ts GeneratorPlugin declaration; the file does not exist yet",
|
|
2374
|
+
pendingEmission: "slice-H-missing-example-surfaces"
|
|
2375
|
+
},
|
|
2376
|
+
"extension-host": { value: "extension-host", status: "emitted-example", note: "example/extension-points.ts declared hosts" },
|
|
2377
|
+
"extension-contribution": {
|
|
2378
|
+
value: "extension-contribution",
|
|
2379
|
+
status: "emitted-example",
|
|
2380
|
+
note: "example widget, DataTable, CrudForm, interceptor and enricher contributions"
|
|
2381
|
+
}
|
|
2382
|
+
};
|
|
2383
|
+
const OWNED_CONTRACT_KIND_COVERAGE = {
|
|
2384
|
+
"module-metadata": { value: "module-metadata", status: "emitted-example", note: "ModuleInfo owned contract" },
|
|
2385
|
+
command: { value: "command", status: "emitted-example", note: "example/commands/todos.ts" },
|
|
2386
|
+
worker: { value: "worker", status: "emitted-example", note: "example/workers/*.ts" },
|
|
2387
|
+
"page-middleware": { value: "page-middleware", status: "emitted-example", note: "example/backend/middleware.ts" },
|
|
2388
|
+
setup: { value: "setup", status: "emitted-example", note: "example/setup.ts" },
|
|
2389
|
+
encryption: { value: "encryption", status: "emitted-example", note: "example/encryption.ts" },
|
|
2390
|
+
"di-registration": {
|
|
2391
|
+
value: "di-registration",
|
|
2392
|
+
status: "emitted-example",
|
|
2393
|
+
note: "example/di.ts scoped Todo summary cache service consumed by a route"
|
|
2394
|
+
},
|
|
2395
|
+
"custom-entity": { value: "custom-entity", status: "emitted-example", note: "example/ce.ts" },
|
|
2396
|
+
"ai-extension": { value: "ai-extension", status: "emitted-example", note: "example AI override/extension record" },
|
|
2397
|
+
"generator-plugin": {
|
|
2398
|
+
value: "generator-plugin",
|
|
2399
|
+
status: "pending-emission",
|
|
2400
|
+
note: "REQUIRED: an example/generators.ts GeneratorPlugin declaration; the file does not exist yet",
|
|
2401
|
+
pendingEmission: "slice-H-missing-example-surfaces"
|
|
2402
|
+
}
|
|
2403
|
+
};
|
|
2404
|
+
const OVERRIDE_DOMAIN_COVERAGE = {
|
|
2405
|
+
ai: { value: "ai", status: "emitted-example", note: "ai.agents, ai.tools and ai.extensions targets" },
|
|
2406
|
+
routes: { value: "routes", status: "emitted-example", note: "routes.api and routes.pages targets" },
|
|
2407
|
+
events: { value: "events", status: "emitted-example", note: "events.subscribers targets" },
|
|
2408
|
+
workers: { value: "workers", status: "emitted-example", note: "workers targets" },
|
|
2409
|
+
widgets: { value: "widgets", status: "emitted-example", note: "widgets.injection, .components and .dashboard targets" },
|
|
2410
|
+
notifications: { value: "notifications", status: "emitted-example", note: "notifications.types and notifications.handlers targets" },
|
|
2411
|
+
interceptors: { value: "interceptors", status: "emitted-example", note: "API interceptor targets" },
|
|
2412
|
+
commandInterceptors: { value: "commandInterceptors", status: "emitted-example", note: "command interceptor targets" },
|
|
2413
|
+
enrichers: { value: "enrichers", status: "emitted-example", note: "response enricher targets" },
|
|
2414
|
+
guards: { value: "guards", status: "emitted-example", note: "page-middleware guard targets" },
|
|
2415
|
+
cli: { value: "cli", status: "emitted-example", note: "CLI command targets" },
|
|
2416
|
+
setup: { value: "setup", status: "emitted-example", note: "setup hook and default-role targets" },
|
|
2417
|
+
acl: { value: "acl", status: "emitted-example", note: "acl.features targets" },
|
|
2418
|
+
di: { value: "di", status: "emitted-example", note: "DI token targets" },
|
|
2419
|
+
encryption: { value: "encryption", status: "emitted-example", note: "encryption.maps targets" },
|
|
2420
|
+
nav: {
|
|
2421
|
+
value: "nav",
|
|
2422
|
+
status: "framework-only",
|
|
2423
|
+
note: "nav.groupOrder is an app-level framework setting; no module target is ever emitted for it"
|
|
2424
|
+
}
|
|
2425
|
+
};
|
|
2426
|
+
const OVERRIDE_MODE_COVERAGE = {
|
|
2427
|
+
"disable-replace": {
|
|
2428
|
+
value: "disable-replace",
|
|
2429
|
+
status: "emitted-example",
|
|
2430
|
+
note: "default mode for most example override hosts"
|
|
2431
|
+
},
|
|
2432
|
+
replace: { value: "replace", status: "emitted-example", note: "the setup host replaces wholesale" },
|
|
2433
|
+
additive: { value: "additive", status: "emitted-example", note: "the ai.extensions host merges additively" }
|
|
2434
|
+
};
|
|
2435
|
+
const OVERRIDE_NOTE_COVERAGE = {
|
|
2436
|
+
"safe-metadata-only": {
|
|
2437
|
+
value: "safe-metadata-only",
|
|
2438
|
+
status: "emitted-example",
|
|
2439
|
+
note: "emitted on the metadata-only override target"
|
|
2440
|
+
},
|
|
2441
|
+
"page-middleware-not-mutation-guard": {
|
|
2442
|
+
value: "page-middleware-not-mutation-guard",
|
|
2443
|
+
status: "emitted-example",
|
|
2444
|
+
note: "emitted on the guards target so a page guard is not read as a mutation guard"
|
|
2445
|
+
}
|
|
2446
|
+
};
|
|
2447
|
+
const OVERRIDE_DIAGNOSTIC_COVERAGE = {
|
|
2448
|
+
"missing-owned-fact": {
|
|
2449
|
+
value: "missing-owned-fact",
|
|
2450
|
+
status: "currently-unbound",
|
|
2451
|
+
note: "reserved by the spec's closed diagnostic set; no adapter emits it today, so not even a broken fixture can produce it"
|
|
2452
|
+
},
|
|
2453
|
+
"missing-source": {
|
|
2454
|
+
value: "missing-source",
|
|
2455
|
+
status: "negative-fixture",
|
|
2456
|
+
note: "a fact whose provenance cannot be proven yields this and no target; activated canonical output emits none"
|
|
2457
|
+
},
|
|
2458
|
+
"unsupported-dynamic-key": {
|
|
2459
|
+
value: "unsupported-dynamic-key",
|
|
2460
|
+
status: "negative-fixture",
|
|
2461
|
+
note: "a key the runtime cannot address yields this and no target; activated canonical output emits none"
|
|
2462
|
+
},
|
|
2463
|
+
"unknown-framework-domain": {
|
|
2464
|
+
value: "unknown-framework-domain",
|
|
2465
|
+
status: "negative-fixture",
|
|
2466
|
+
note: "the framework catalog does not describe the dotted host at all"
|
|
2467
|
+
},
|
|
2468
|
+
"unknown-framework-mode": {
|
|
2469
|
+
value: "unknown-framework-mode",
|
|
2470
|
+
status: "negative-fixture",
|
|
2471
|
+
note: "the framework catalog describes the host but names an operation that is not a public mode"
|
|
2472
|
+
}
|
|
2473
|
+
};
|
|
2474
|
+
function buildCoverageFamily(family, enumSource, values, coverage) {
|
|
2475
|
+
const rows = [];
|
|
2476
|
+
const missing = [];
|
|
2477
|
+
for (const value of values) {
|
|
2478
|
+
const row = coverage[value];
|
|
2479
|
+
if (!row) {
|
|
2480
|
+
missing.push(value);
|
|
2481
|
+
continue;
|
|
2482
|
+
}
|
|
2483
|
+
rows.push(row);
|
|
2484
|
+
}
|
|
2485
|
+
if (missing.length > 0) {
|
|
2486
|
+
throw new Error(
|
|
2487
|
+
`[module-facts] factCoverage family "${family}" has no row for: ${missing.join(", ")}. Add an explicit classification instead of leaving the value unclassified.`
|
|
2488
|
+
);
|
|
2489
|
+
}
|
|
2490
|
+
const extra = Object.keys(coverage).filter((value) => !values.includes(value));
|
|
2491
|
+
if (extra.length > 0) {
|
|
2492
|
+
throw new Error(
|
|
2493
|
+
`[module-facts] factCoverage family "${family}" classifies values absent from the enum: ${extra.join(", ")}.`
|
|
2494
|
+
);
|
|
2495
|
+
}
|
|
2496
|
+
return { family, enumSource, rows };
|
|
2497
|
+
}
|
|
2498
|
+
function buildModuleFactCoverageLedger() {
|
|
2499
|
+
return [
|
|
2500
|
+
buildCoverageFamily(
|
|
2501
|
+
"ModuleFactSourceKind",
|
|
2502
|
+
"packages/cli/src/lib/generators/module-facts.ts#MODULE_FACT_SOURCE_KINDS",
|
|
2503
|
+
MODULE_FACT_SOURCE_KINDS,
|
|
2504
|
+
FACT_SOURCE_KIND_COVERAGE
|
|
2505
|
+
),
|
|
2506
|
+
buildCoverageFamily(
|
|
2507
|
+
"ModuleOwnedContractKind",
|
|
2508
|
+
"packages/cli/src/lib/generators/module-facts.ts#MODULE_OWNED_CONTRACT_KINDS",
|
|
2509
|
+
MODULE_OWNED_CONTRACT_KINDS,
|
|
2510
|
+
OWNED_CONTRACT_KIND_COVERAGE
|
|
2511
|
+
),
|
|
2512
|
+
buildCoverageFamily(
|
|
2513
|
+
"ModuleOverrideDomain",
|
|
2514
|
+
"packages/cli/src/lib/generators/module-override-targets.ts#ALL_OVERRIDE_DOMAINS",
|
|
2515
|
+
ALL_OVERRIDE_DOMAINS,
|
|
2516
|
+
OVERRIDE_DOMAIN_COVERAGE
|
|
2517
|
+
),
|
|
2518
|
+
buildCoverageFamily(
|
|
2519
|
+
"ModuleOverrideMode",
|
|
2520
|
+
"packages/cli/src/lib/generators/module-override-targets.ts#MODULE_OVERRIDE_MODES",
|
|
2521
|
+
MODULE_OVERRIDE_MODES,
|
|
2522
|
+
OVERRIDE_MODE_COVERAGE
|
|
2523
|
+
),
|
|
2524
|
+
buildCoverageFamily(
|
|
2525
|
+
"ModuleOverrideTargetNote",
|
|
2526
|
+
"packages/cli/src/lib/generators/module-override-targets.ts#MODULE_OVERRIDE_TARGET_NOTES",
|
|
2527
|
+
MODULE_OVERRIDE_TARGET_NOTES,
|
|
2528
|
+
OVERRIDE_NOTE_COVERAGE
|
|
2529
|
+
),
|
|
2530
|
+
buildCoverageFamily(
|
|
2531
|
+
"ModuleOverrideTargetDiagnosticCode",
|
|
2532
|
+
"packages/cli/src/lib/generators/module-override-targets.ts#MODULE_OVERRIDE_TARGET_DIAGNOSTIC_CODES",
|
|
2533
|
+
MODULE_OVERRIDE_TARGET_DIAGNOSTIC_CODES,
|
|
2534
|
+
OVERRIDE_DIAGNOSTIC_COVERAGE
|
|
2535
|
+
)
|
|
2536
|
+
];
|
|
2537
|
+
}
|
|
2538
|
+
function countModuleFactCoverage(facts) {
|
|
2539
|
+
const factSourceKind = {};
|
|
2540
|
+
for (const entry of facts.factSources ?? []) {
|
|
2541
|
+
factSourceKind[entry.kind] = (factSourceKind[entry.kind] ?? 0) + 1;
|
|
2542
|
+
}
|
|
2543
|
+
const ownedContractKind = {};
|
|
2544
|
+
for (const [kind, list] of Object.entries(facts.ownedContracts ?? {})) {
|
|
2545
|
+
ownedContractKind[kind] = (list ?? []).length;
|
|
2546
|
+
}
|
|
2547
|
+
const overrideDomain = {};
|
|
2548
|
+
const overrideMode = {};
|
|
2549
|
+
const overrideNote = {};
|
|
2550
|
+
for (const target of facts.overrideTargets ?? []) {
|
|
2551
|
+
overrideDomain[target.domain] = (overrideDomain[target.domain] ?? 0) + 1;
|
|
2552
|
+
for (const mode of target.modes) overrideMode[mode] = (overrideMode[mode] ?? 0) + 1;
|
|
2553
|
+
for (const note of target.notes ?? []) overrideNote[note] = (overrideNote[note] ?? 0) + 1;
|
|
2554
|
+
}
|
|
2555
|
+
const overrideDiagnosticCode = {};
|
|
2556
|
+
for (const diagnostic of facts.overrideTargetDiagnostics ?? []) {
|
|
2557
|
+
overrideDiagnosticCode[diagnostic.code] = (overrideDiagnosticCode[diagnostic.code] ?? 0) + 1;
|
|
2558
|
+
}
|
|
2559
|
+
return {
|
|
2560
|
+
ModuleFactSourceKind: factSourceKind,
|
|
2561
|
+
ModuleOwnedContractKind: ownedContractKind,
|
|
2562
|
+
ModuleOverrideDomain: overrideDomain,
|
|
2563
|
+
ModuleOverrideMode: overrideMode,
|
|
2564
|
+
ModuleOverrideTargetNote: overrideNote,
|
|
2565
|
+
ModuleOverrideTargetDiagnosticCode: overrideDiagnosticCode
|
|
2566
|
+
};
|
|
719
2567
|
}
|
|
720
2568
|
export {
|
|
2569
|
+
MODULE_FACT_SOURCE_KINDS,
|
|
2570
|
+
MODULE_OWNED_CONTRACT_KINDS,
|
|
2571
|
+
assertPackageModuleFactsOnly,
|
|
2572
|
+
assertPortableSourceRoot,
|
|
2573
|
+
buildCoverageFamily,
|
|
2574
|
+
buildModuleFactCoverageLedger,
|
|
721
2575
|
buildModuleFactsJsonObject,
|
|
2576
|
+
collectUnresolvedFirstPartyTargets,
|
|
2577
|
+
computeModuleSourceFingerprint,
|
|
2578
|
+
computeModuleTaxonomyFingerprint,
|
|
2579
|
+
countModuleFactCoverage,
|
|
722
2580
|
extractAllModuleFacts,
|
|
2581
|
+
extractLocalReferenceModuleFacts,
|
|
723
2582
|
extractModuleFacts,
|
|
2583
|
+
isExactSourceFilePath,
|
|
724
2584
|
renderModuleFactsJson,
|
|
725
2585
|
renderModuleFactsMarkdown,
|
|
2586
|
+
renderReferenceModuleFactsJson,
|
|
726
2587
|
toModuleFactsJsonEntry
|
|
727
2588
|
};
|
|
728
2589
|
//# sourceMappingURL=module-facts.js.map
|