@open-mercato/cli 0.6.7-develop.6863.1.61001cf9a8 → 0.6.7
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 +4 -4
- package/build.mjs +7 -24
- package/dist/agentic/claude-code/hooks/entity-migration-check.ts +88 -32
- package/dist/agentic/codex/enforcement-rules.md +16 -2
- package/dist/agentic/cursor/hooks/entity-migration-check.mjs +50 -19
- package/dist/agentic/cursor/rules/entity-guard.mdc +21 -9
- package/dist/agentic/cursor/rules/generated-guard.mdc +5 -3
- package/dist/agentic/cursor/rules/open-mercato.mdc +29 -3
- package/dist/agentic/guides/cache.md +45 -0
- package/dist/agentic/guides/core.md +453 -0
- package/dist/agentic/guides/events.md +117 -0
- package/dist/agentic/guides/module-facts.json +3833 -115419
- package/dist/agentic/guides/module-system.md +127 -12
- package/dist/agentic/guides/modules/ai_assistant.md +28 -227
- package/dist/agentic/guides/modules/api_docs.md +2 -58
- package/dist/agentic/guides/modules/api_keys.md +7 -92
- package/dist/agentic/guides/modules/attachments.md +14 -119
- package/dist/agentic/guides/modules/audit_logs.md +10 -131
- package/dist/agentic/guides/modules/auth.md +33 -276
- package/dist/agentic/guides/modules/business_rules.md +14 -149
- package/dist/agentic/guides/modules/catalog.md +43 -475
- package/dist/agentic/guides/modules/channel_gmail.md +3 -82
- package/dist/agentic/guides/modules/channel_imap.md +3 -82
- package/dist/agentic/guides/modules/checkout.md +27 -299
- package/dist/agentic/guides/modules/communication_channels.md +33 -315
- package/dist/agentic/guides/modules/configs.md +8 -104
- package/dist/agentic/guides/modules/content.md +2 -57
- package/dist/agentic/guides/modules/currencies.md +17 -152
- package/dist/agentic/guides/modules/customer_accounts.md +45 -387
- package/dist/agentic/guides/modules/customers.md +85 -746
- package/dist/agentic/guides/modules/dashboards.md +10 -155
- package/dist/agentic/guides/modules/data_sync.md +16 -149
- package/dist/agentic/guides/modules/dictionaries.md +12 -107
- package/dist/agentic/guides/modules/directory.md +15 -148
- package/dist/agentic/guides/modules/entities.md +13 -145
- package/dist/agentic/guides/modules/events.md +2 -59
- package/dist/agentic/guides/modules/feature_toggles.md +9 -119
- package/dist/agentic/guides/modules/gateway_stripe.md +4 -92
- package/dist/agentic/guides/modules/generators.md +2 -45
- package/dist/agentic/guides/modules/inbox_ops.md +27 -207
- package/dist/agentic/guides/modules/integrations.md +16 -146
- package/dist/agentic/guides/modules/messages.md +24 -223
- package/dist/agentic/guides/modules/notifications.md +8 -96
- package/dist/agentic/guides/modules/onboarding.md +6 -77
- package/dist/agentic/guides/modules/payment_gateways.md +21 -164
- package/dist/agentic/guides/modules/perspectives.md +7 -77
- package/dist/agentic/guides/modules/planner.md +18 -136
- package/dist/agentic/guides/modules/portal.md +2 -93
- package/dist/agentic/guides/modules/progress.md +14 -104
- package/dist/agentic/guides/modules/query_index.md +15 -154
- package/dist/agentic/guides/modules/record_locks.md +21 -206
- package/dist/agentic/guides/modules/resources.md +30 -197
- package/dist/agentic/guides/modules/sales.md +84 -678
- package/dist/agentic/guides/modules/scheduler.md +14 -133
- package/dist/agentic/guides/modules/search.md +6 -170
- package/dist/agentic/guides/modules/security.md +35 -294
- package/dist/agentic/guides/modules/shipping_carriers.md +18 -134
- package/dist/agentic/guides/modules/sso.md +26 -151
- package/dist/agentic/guides/modules/staff.md +57 -369
- package/dist/agentic/guides/modules/storage_s3.md +5 -95
- package/dist/agentic/guides/modules/sync_akeneo.md +3 -85
- package/dist/agentic/guides/modules/sync_excel.md +6 -75
- package/dist/agentic/guides/modules/system_status_overlays.md +2 -60
- package/dist/agentic/guides/modules/translations.md +10 -104
- package/dist/agentic/guides/modules/webhooks.md +24 -203
- package/dist/agentic/guides/modules/wms.md +46 -441
- package/dist/agentic/guides/modules/workflows.md +43 -302
- package/dist/agentic/guides/queue.md +53 -0
- package/dist/agentic/guides/search.md +115 -0
- package/dist/agentic/guides/shared.md +108 -0
- package/dist/agentic/guides/ui.md +347 -0
- package/dist/agentic/shared/AGENTS.md.template +311 -124
- package/dist/agentic/shared/ai/agentic.config.json +0 -2
- package/dist/agentic/shared/ai/lessons.md +3 -26
- package/dist/agentic/shared/ai/skills/om-auto-continue-pr-loop/SKILL.md +27 -0
- package/dist/agentic/shared/ai/skills/om-auto-create-pr-loop/SKILL.md +27 -0
- package/dist/agentic/shared/ai/skills/om-auto-review-pr/SKILL.md +0 -4
- package/dist/agentic/shared/ai/skills/om-backend-ui-design/SKILL.md +241 -20
- package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/ui-components.md +260 -0
- package/dist/agentic/shared/ai/skills/om-data-model-design/SKILL.md +601 -16
- package/dist/agentic/shared/ai/skills/om-data-model-design/references/mikro-orm-cheatsheet.md +146 -0
- package/dist/agentic/shared/ai/skills/om-eject-and-customize/SKILL.md +310 -14
- package/dist/agentic/shared/ai/skills/om-help/SKILL.md +97 -15
- package/dist/agentic/shared/ai/skills/om-help/references/skills-catalog.md +84 -0
- package/dist/agentic/shared/ai/skills/om-help/references/workflow-sequences.md +193 -0
- package/dist/agentic/shared/ai/skills/om-implement-spec/SKILL.md +152 -33
- package/dist/agentic/shared/ai/skills/om-integration-builder/SKILL.md +708 -24
- package/dist/agentic/shared/ai/skills/om-integration-builder/STANDALONE.md +34 -0
- package/dist/agentic/shared/ai/skills/om-integration-builder/references/adapter-contracts.md +762 -0
- package/dist/agentic/shared/ai/skills/om-module-scaffold/SKILL.md +820 -20
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/naming-conventions.md +50 -0
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/navigation-patterns.md +97 -0
- package/dist/agentic/shared/ai/skills/om-system-extension/SKILL.md +843 -19
- package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-contracts.md +271 -0
- package/dist/agentic/shared/ai/skills/om-trim-unused-modules/SKILL.md +63 -15
- package/dist/agentic/shared/ai/skills/om-troubleshooter/SKILL.md +467 -16
- package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnostic-commands.md +101 -0
- package/dist/agentic/shared/ai/skills/tiers.json +21 -143
- package/dist/agentic/shared/ai/skills/tiers.schema.json +40 -52
- package/dist/agentic/shared/ai/specs/README.md +3 -4
- package/dist/agentic/shared/ai/specs/SPEC-000-template.md +10 -226
- package/dist/agentic/shared/ai/trackers/github.md +4 -108
- package/dist/agentic/shared/scripts/install-skills.sh +694 -4
- package/dist/bin.js +1 -13
- package/dist/bin.js.map +2 -2
- package/dist/lib/__integration__/TC-INT-008.spec.js +8 -88
- package/dist/lib/__integration__/TC-INT-008.spec.js.map +2 -2
- package/dist/lib/agentic-init.js +3 -13
- package/dist/lib/agentic-init.js.map +2 -2
- package/dist/lib/agentic-setup.js +186 -418
- package/dist/lib/agentic-setup.js.map +2 -2
- package/dist/lib/generators/extensions/injection-widgets.js +0 -20
- package/dist/lib/generators/extensions/injection-widgets.js.map +2 -2
- package/dist/lib/generators/module-facts-discovery.js +7 -41
- package/dist/lib/generators/module-facts-discovery.js.map +2 -2
- package/dist/lib/generators/module-facts.js +45 -1387
- package/dist/lib/generators/module-facts.js.map +3 -3
- package/dist/lib/generators/module-registry.js +5 -11
- package/dist/lib/generators/module-registry.js.map +2 -2
- package/dist/lib/resolver.js +0 -2
- package/dist/lib/resolver.js.map +2 -2
- package/dist/lib/testing/integration.js +0 -5
- package/dist/lib/testing/integration.js.map +2 -2
- package/dist/mercato.js +1 -23
- package/dist/mercato.js.map +2 -2
- package/package.json +11 -13
- package/src/bin.ts +1 -33
- package/src/lib/__integration__/TC-INT-008.spec.ts +9 -107
- package/src/lib/__tests__/agentic-init.test.ts +5 -41
- package/src/lib/agentic-init.ts +3 -15
- package/src/lib/agentic-setup.ts +224 -530
- package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +2 -88
- package/src/lib/generators/__tests__/module-facts.customers.fixture.test.ts +0 -1
- package/src/lib/generators/__tests__/module-facts.discovery.test.ts +10 -54
- package/src/lib/generators/__tests__/module-subset.test.ts +0 -18
- package/src/lib/generators/extensions/injection-widgets.ts +0 -51
- package/src/lib/generators/module-facts-discovery.ts +7 -58
- package/src/lib/generators/module-facts.ts +46 -1851
- package/src/lib/generators/module-registry.ts +6 -16
- package/src/lib/resolver.ts +0 -3
- package/src/lib/testing/__tests__/integration.test.ts +0 -4
- package/src/lib/testing/integration.ts +0 -5
- package/src/mercato.ts +1 -26
- package/dist/agentic/guides/ai-workflows.md +0 -76
- package/dist/agentic/guides/architecture.md +0 -107
- package/dist/agentic/guides/backend-ui.md +0 -80
- package/dist/agentic/guides/contracts.md +0 -92
- package/dist/agentic/guides/extensions.md +0 -121
- package/dist/agentic/guides/framework-extension-points.md +0 -36
- package/dist/agentic/guides/integrations.md +0 -81
- package/dist/agentic/guides/modules/design_system.md +0 -102
- package/dist/agentic/guides/spec-delivery.md +0 -22
- package/dist/agentic/guides/testing-debugging.md +0 -80
- package/dist/agentic/guides/upstream/AGENTS.md +0 -298
- package/dist/agentic/guides/upstream/BACKWARD_COMPATIBILITY.md +0 -316
- package/dist/agentic/guides/upstream/manifest.json +0 -8
- package/dist/agentic/shared/ai/harness/README.md +0 -50
- package/dist/agentic/shared/ai/harness/RELEASE.md +0 -57
- package/dist/agentic/shared/ai/harness/cases.json +0 -13680
- package/dist/agentic/shared/ai/harness/cases.schema.json +0 -133
- package/dist/agentic/shared/ai/harness/fixtures/index.json +0 -49
- package/dist/agentic/shared/ai/harness/fixtures/seeds.json +0 -162
- package/dist/agentic/shared/ai/harness/generated-code-review-policy.md +0 -21
- package/dist/agentic/shared/ai/harness/generated-code-review-response.schema.json +0 -96
- package/dist/agentic/shared/ai/harness/generated-code-review-result.schema.json +0 -172
- package/dist/agentic/shared/ai/harness/release-matrix.json +0 -158
- package/dist/agentic/shared/ai/harness/release-result.schema.json +0 -219
- package/dist/agentic/shared/ai/harness/result.schema.json +0 -76
- package/dist/agentic/shared/ai/harness/routing-response.schema.json +0 -12
- package/dist/agentic/shared/ai/harness/target-validation-result.schema.json +0 -73
- package/dist/agentic/shared/ai/harness/validators.json +0 -78
- package/dist/agentic/shared/ai/harness/writable-ast-oracles.mjs +0 -2121
- package/dist/agentic/shared/ai/harness/writable-behavior-oracles.mjs +0 -1096
- package/dist/agentic/shared/ai/lessons/_template.md +0 -16
- package/dist/agentic/shared/ai/review-checklist.md +0 -36
- package/dist/agentic/shared/ai/skills/om-auto-implement-spec/SKILL.md +0 -21
- package/dist/agentic/shared/ai/skills/om-auto-upgrade-0.6.6-to-0.6.7/SKILL.md +0 -98
- package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/crud-surfaces.md +0 -16
- package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/frontend-and-design-system.md +0 -53
- package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/page-and-navigation.md +0 -13
- package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/quality-states.md +0 -11
- package/dist/agentic/shared/ai/skills/om-build-workflow/SKILL.md +0 -30
- package/dist/agentic/shared/ai/skills/om-build-workflow/references/activity-contracts.md +0 -12
- package/dist/agentic/shared/ai/skills/om-build-workflow/references/durability-and-progress.md +0 -13
- package/dist/agentic/shared/ai/skills/om-build-workflow/references/workflow-design.md +0 -14
- package/dist/agentic/shared/ai/skills/om-create-ai-agent/SKILL.md +0 -28
- package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/attachments-and-overrides.md +0 -12
- package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/module-agents-and-tools.md +0 -22
- package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/orchestrator-agents.md +0 -12
- package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/surface-selector.md +0 -19
- package/dist/agentic/shared/ai/skills/om-data-model-design/references/integrity-and-concurrency.md +0 -19
- package/dist/agentic/shared/ai/skills/om-data-model-design/references/migration-workflow.md +0 -13
- package/dist/agentic/shared/ai/skills/om-data-model-design/references/schema-design.md +0 -14
- package/dist/agentic/shared/ai/skills/om-data-model-design/references/sensitive-data.md +0 -10
- package/dist/agentic/shared/ai/skills/om-eject-and-customize/references/decision-and-procedure.md +0 -17
- package/dist/agentic/shared/ai/skills/om-evolve-harness/SKILL.md +0 -29
- package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-template.md +0 -100
- package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-workflow.md +0 -16
- package/dist/agentic/shared/ai/skills/om-evolve-harness/references/owner-selection.md +0 -17
- package/dist/agentic/shared/ai/skills/om-framework-context/SKILL.md +0 -25
- package/dist/agentic/shared/ai/skills/om-framework-context/references/resolver-procedure.md +0 -11
- package/dist/agentic/shared/ai/skills/om-framework-context/references/skew-and-escalation.md +0 -13
- package/dist/agentic/shared/ai/skills/om-help/references/delivery-workflows.md +0 -18
- package/dist/agentic/shared/ai/skills/om-help/references/task-families.md +0 -20
- package/dist/agentic/shared/ai/skills/om-implement-spec/references/phases-and-gates.md +0 -33
- package/dist/agentic/shared/ai/skills/om-implement-spec/references/planning-and-progress.md +0 -40
- package/dist/agentic/shared/ai/skills/om-implement-spec/references/report-templates.md +0 -27
- package/dist/agentic/shared/ai/skills/om-implement-spec/references/spec-resolution.md +0 -24
- package/dist/agentic/shared/ai/skills/om-integration-builder/references/package-and-activation.md +0 -16
- package/dist/agentic/shared/ai/skills/om-integration-builder/references/provider-families.md +0 -14
- package/dist/agentic/shared/ai/skills/om-integration-builder/references/security-and-reliability.md +0 -15
- package/dist/agentic/shared/ai/skills/om-integration-builder/references/sync-and-files.md +0 -12
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/SKILL.md +0 -34
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/agents/openai.yaml +0 -4
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/agentic-setup.md +0 -11
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/input-normalization.md +0 -31
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/judge-workflow.md +0 -39
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/report-template.md +0 -43
- package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/rules.md +0 -11
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/api-and-domain.md +0 -19
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/business-one-shot-blueprints.md +0 -172
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/data-and-migrations.md +0 -12
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/discovery-surface-catalog.md +0 -83
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/module-surfaces.md +0 -21
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/planning.md +0 -11
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/runtime-cache-and-queues.md +0 -22
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/verification.md +0 -13
- package/dist/agentic/shared/ai/skills/om-share-this-session/SKILL.md +0 -41
- package/dist/agentic/shared/ai/skills/om-share-this-session/references/agentic-setup.md +0 -19
- package/dist/agentic/shared/ai/skills/om-share-this-session/references/bundle-preparation.md +0 -44
- package/dist/agentic/shared/ai/skills/om-share-this-session/references/consent-and-review.md +0 -39
- package/dist/agentic/shared/ai/skills/om-share-this-session/references/publication.md +0 -20
- package/dist/agentic/shared/ai/skills/om-share-this-session/references/report-templates.md +0 -57
- package/dist/agentic/shared/ai/skills/om-share-this-session/references/rules.md +0 -11
- package/dist/agentic/shared/ai/skills/om-share-this-session/scripts/prepare-share-bundle.mjs +0 -486
- package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-branches.md +0 -18
- package/dist/agentic/shared/ai/skills/om-system-extension/references/mechanism-selector.md +0 -27
- package/dist/agentic/shared/ai/skills/om-system-extension/references/read-write-roundtrip.md +0 -13
- package/dist/agentic/shared/ai/skills/om-system-extension/references/unified-overrides.md +0 -41
- package/dist/agentic/shared/ai/skills/om-system-extension/references/verification.md +0 -11
- package/dist/agentic/shared/ai/skills/om-trim-unused-modules/references/dependency-and-redirect.md +0 -15
- package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnosis-map.md +0 -16
- package/dist/agentic/shared/ai/skills/om-troubleshooter/references/regression-oracles.md +0 -14
- package/dist/agentic/shared/scripts/agent-harness-tool-server.mjs +0 -187
- package/dist/agentic/shared/scripts/check-lessons.mjs +0 -178
- package/dist/agentic/shared/scripts/evaluate-agent-harness.mjs +0 -2958
- package/dist/agentic/shared/scripts/execution-sandbox.mjs +0 -329
- package/dist/agentic/shared/scripts/framework-context.mjs +0 -888
- package/dist/agentic/shared/scripts/install-skills.mjs +0 -1029
- package/dist/agentic/shared/scripts/prepare-agent-harness-fixture.mjs +0 -222
- package/dist/agentic/shared/scripts/run-agent-harness-release.mjs +0 -1774
- package/dist/lib/generators/module-extension-facts.js +0 -1840
- package/dist/lib/generators/module-extension-facts.js.map +0 -7
- package/dist/lib/generators/module-fact-sources.js +0 -70
- package/dist/lib/generators/module-fact-sources.js.map +0 -7
- package/dist/lib/generators/module-override-targets.js +0 -688
- package/dist/lib/generators/module-override-targets.js.map +0 -7
- package/dist/lib/load-env.js +0 -24
- package/dist/lib/load-env.js.map +0 -7
- package/dist/lib/telemetry-init.js +0 -358
- package/dist/lib/telemetry-init.js.map +0 -7
- package/src/lib/__tests__/agentic-setup.ownership.test.ts +0 -386
- package/src/lib/__tests__/injection-widget-id-validation.test.ts +0 -184
- package/src/lib/__tests__/load-env.test.ts +0 -65
- package/src/lib/__tests__/telemetry-init.test.ts +0 -298
- package/src/lib/generators/__tests__/activation-adapter-coverage.test.ts +0 -92
- package/src/lib/generators/__tests__/module-extension-activations.test.ts +0 -762
- package/src/lib/generators/__tests__/module-extension-facts.test.ts +0 -404
- package/src/lib/generators/__tests__/module-facts.extension-hosts.test.ts +0 -108
- package/src/lib/generators/__tests__/module-facts.owned-contracts.test.ts +0 -561
- package/src/lib/generators/__tests__/module-facts.source-links.test.ts +0 -119
- package/src/lib/generators/__tests__/module-override-targets.roundtrip.test.ts +0 -149
- package/src/lib/generators/__tests__/module-override-targets.test.ts +0 -304
- package/src/lib/generators/module-extension-facts.ts +0 -2365
- package/src/lib/generators/module-fact-sources.ts +0 -108
- package/src/lib/generators/module-override-targets.ts +0 -863
- package/src/lib/load-env.ts +0 -30
- package/src/lib/telemetry-init.ts +0 -461
|
@@ -1,561 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs'
|
|
2
|
-
import os from 'node:os'
|
|
3
|
-
import path from 'node:path'
|
|
4
|
-
import { extractModuleFacts, renderModuleFactsMarkdown, toModuleFactsJsonEntry } from '../module-facts'
|
|
5
|
-
import type { ModuleFacts, ModuleOwnedContractFact } from '../module-facts'
|
|
6
|
-
import { buildFactSourceLookup } from '../module-fact-sources'
|
|
7
|
-
|
|
8
|
-
function writeFixture(root: string, relativePath: string, source: string): void {
|
|
9
|
-
const filePath = path.join(root, relativePath)
|
|
10
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
|
11
|
-
fs.writeFileSync(filePath, source)
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function ownedById(
|
|
15
|
-
facts: ModuleFacts,
|
|
16
|
-
kind: keyof NonNullable<ModuleFacts['ownedContracts']>,
|
|
17
|
-
id: string,
|
|
18
|
-
): ModuleOwnedContractFact | undefined {
|
|
19
|
-
return facts.ownedContracts?.[kind]?.find((fact) => fact.id === id)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
describe('module-facts owned contracts + provenance (Spec 1)', () => {
|
|
23
|
-
let temporaryRoot: string
|
|
24
|
-
let moduleRoot: string
|
|
25
|
-
let facts: ModuleFacts
|
|
26
|
-
|
|
27
|
-
beforeAll(() => {
|
|
28
|
-
temporaryRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'module-facts-owned-'))
|
|
29
|
-
moduleRoot = path.join(temporaryRoot, 'facts')
|
|
30
|
-
|
|
31
|
-
writeFixture(
|
|
32
|
-
moduleRoot,
|
|
33
|
-
'index.ts',
|
|
34
|
-
"export const metadata = { name: 'facts', title: 'Facts', version: '1.2.3', requires: ['auth', 'catalog'], ejectable: true }\n",
|
|
35
|
-
)
|
|
36
|
-
writeFixture(
|
|
37
|
-
moduleRoot,
|
|
38
|
-
'data/entities.ts',
|
|
39
|
-
"import { Entity, Property } from '@mikro-orm/core'\n"
|
|
40
|
-
+ "@Entity({ tableName: 'facts_widget' })\nexport class FactsWidget { updatedAt!: Date }\n",
|
|
41
|
-
)
|
|
42
|
-
writeFixture(
|
|
43
|
-
moduleRoot,
|
|
44
|
-
'events.ts',
|
|
45
|
-
"export const events = [{ id: 'facts.widget.created', category: 'facts' }]\n",
|
|
46
|
-
)
|
|
47
|
-
writeFixture(
|
|
48
|
-
moduleRoot,
|
|
49
|
-
'acl.ts',
|
|
50
|
-
"export const features = [{ id: 'facts.view' }, { id: 'facts.manage' }]\n",
|
|
51
|
-
)
|
|
52
|
-
writeFixture(
|
|
53
|
-
moduleRoot,
|
|
54
|
-
'search.ts',
|
|
55
|
-
"export const searchConfig = { entities: [{ entityId: 'facts:facts_widget' }] }\n",
|
|
56
|
-
)
|
|
57
|
-
writeFixture(
|
|
58
|
-
moduleRoot,
|
|
59
|
-
'notifications.ts',
|
|
60
|
-
"export const notificationTypes = [{ type: 'facts.alert' }]\n",
|
|
61
|
-
)
|
|
62
|
-
writeFixture(
|
|
63
|
-
moduleRoot,
|
|
64
|
-
'ce.ts',
|
|
65
|
-
"const systemEntities = [{ id: 'facts:facts_widget', label: 'Widget', fields: [] }]\nexport default systemEntities\n",
|
|
66
|
-
)
|
|
67
|
-
writeFixture(
|
|
68
|
-
moduleRoot,
|
|
69
|
-
'encryption.ts',
|
|
70
|
-
"export const defaultEncryptionMaps = [{ entityId: 'facts:facts_widget', fields: [{ field: 'secret_snapshot' }, { field: 'notes' }] }]\n",
|
|
71
|
-
)
|
|
72
|
-
writeFixture(
|
|
73
|
-
moduleRoot,
|
|
74
|
-
'setup.ts',
|
|
75
|
-
"export const setup = {\n"
|
|
76
|
-
+ " defaultRoleFeatures: { admin: ['facts.manage'], employee: ['facts.view'] },\n"
|
|
77
|
-
+ " async onTenantCreated() {},\n"
|
|
78
|
-
+ " seedDefaults: async () => {},\n"
|
|
79
|
-
+ "}\n",
|
|
80
|
-
)
|
|
81
|
-
writeFixture(
|
|
82
|
-
moduleRoot,
|
|
83
|
-
'backend/middleware.ts',
|
|
84
|
-
"export const middleware = [{ id: 'facts.backend.redirect', mode: 'backend', priority: 10, run() {} }]\n",
|
|
85
|
-
)
|
|
86
|
-
writeFixture(
|
|
87
|
-
moduleRoot,
|
|
88
|
-
'frontend/middleware.ts',
|
|
89
|
-
"export const middleware = [{ id: 'facts.frontend.guard', mode: 'frontend', run() {} }, { mode: 'frontend', run() {} }]\n",
|
|
90
|
-
)
|
|
91
|
-
writeFixture(
|
|
92
|
-
moduleRoot,
|
|
93
|
-
'backend/page.tsx',
|
|
94
|
-
"export const metadata = { requireAuth: true, requireFeatures: ['facts.view'], group: 'crm' }\nexport default function Page() { return null }\n",
|
|
95
|
-
)
|
|
96
|
-
// Commands: recursive discovery, plus a duplicate id declared in two files.
|
|
97
|
-
writeFixture(
|
|
98
|
-
moduleRoot,
|
|
99
|
-
'commands/root.ts',
|
|
100
|
-
"import { registerCommand } from '@open-mercato/shared'\nregisterCommand({ id: 'facts.sync' })\n",
|
|
101
|
-
)
|
|
102
|
-
writeFixture(
|
|
103
|
-
moduleRoot,
|
|
104
|
-
'commands/nested/deep.ts',
|
|
105
|
-
"import { registerCommand } from '@open-mercato/shared'\nregisterCommand({ id: 'facts.reindex' })\nregisterCommand({ id: 'facts.sync' })\n",
|
|
106
|
-
)
|
|
107
|
-
// Workers: recursive discovery, declared and derived ids, local-constant queues,
|
|
108
|
-
// a dynamic queue, and a worker runtime skips because it declares no queue.
|
|
109
|
-
writeFixture(
|
|
110
|
-
moduleRoot,
|
|
111
|
-
'workers/sync.ts',
|
|
112
|
-
"export const metadata = { id: 'facts:sync', queue: 'facts-sync', name: 'Sync', concurrency: 2 }\nexport default async function handle() {}\n",
|
|
113
|
-
)
|
|
114
|
-
writeFixture(
|
|
115
|
-
moduleRoot,
|
|
116
|
-
'workers/nested/deep.ts',
|
|
117
|
-
"export const metadata = { id: 'facts:deep', queue: 'facts-deep' }\nexport default async function handle() {}\n",
|
|
118
|
-
)
|
|
119
|
-
writeFixture(
|
|
120
|
-
moduleRoot,
|
|
121
|
-
'workers/reindex.ts',
|
|
122
|
-
"const REINDEX_QUEUE = 'facts-reindex'\nexport const metadata = { queue: REINDEX_QUEUE, concurrency: 4 }\nexport default async function handle() {}\n",
|
|
123
|
-
)
|
|
124
|
-
writeFixture(
|
|
125
|
-
moduleRoot,
|
|
126
|
-
'workers/nested/cleanup.ts',
|
|
127
|
-
"const CLEANUP_QUEUE = 'facts-cleanup'\nexport const metadata = { queue: CLEANUP_QUEUE }\nexport default async function handle() {}\n",
|
|
128
|
-
)
|
|
129
|
-
writeFixture(
|
|
130
|
-
moduleRoot,
|
|
131
|
-
'workers/queueless.ts',
|
|
132
|
-
"export const metadata = { id: 'facts:queueless' }\nexport default async function handle() {}\n",
|
|
133
|
-
)
|
|
134
|
-
writeFixture(
|
|
135
|
-
moduleRoot,
|
|
136
|
-
'workers/dynamic.ts',
|
|
137
|
-
"const QUEUE = computeQueue()\nexport const metadata = { queue: QUEUE }\nexport default async function handle() {}\n",
|
|
138
|
-
)
|
|
139
|
-
// Pages: companion metadata files (`page.meta.*`, then `meta.*`) win over the
|
|
140
|
-
// page component, exactly as runtime discovery resolves them.
|
|
141
|
-
writeFixture(
|
|
142
|
-
moduleRoot,
|
|
143
|
-
'backend/facts/config/page.tsx',
|
|
144
|
-
"export const metadata = { requireAuth: false, group: 'ignored' }\nexport default function ConfigPage() { return null }\n",
|
|
145
|
-
)
|
|
146
|
-
writeFixture(
|
|
147
|
-
moduleRoot,
|
|
148
|
-
'backend/facts/config/page.meta.ts',
|
|
149
|
-
"export const metadata = { requireAuth: true, requireFeatures: ['facts.configure'], pageGroup: 'settings-group', pageGroupKey: 'facts.settings', pageOrder: 30, pageContext: 'settings', navHidden: true }\n",
|
|
150
|
-
)
|
|
151
|
-
writeFixture(
|
|
152
|
-
moduleRoot,
|
|
153
|
-
'backend/facts/reports/page.tsx',
|
|
154
|
-
"export default function ReportsPage() { return null }\n",
|
|
155
|
-
)
|
|
156
|
-
writeFixture(
|
|
157
|
-
moduleRoot,
|
|
158
|
-
'backend/facts/reports/meta.ts',
|
|
159
|
-
"export const metadata = { requireAuth: true, order: 5, pageContext: 'admin', pageContextId: 'not-a-contract-key' }\n",
|
|
160
|
-
)
|
|
161
|
-
// Subscribers: nested directory must be discovered like runtime.
|
|
162
|
-
writeFixture(
|
|
163
|
-
moduleRoot,
|
|
164
|
-
'subscribers/nested/on-created.ts',
|
|
165
|
-
"export const metadata = { id: 'facts:on-created', event: 'facts.widget.created', persistent: false }\nexport default async function handle() {}\n",
|
|
166
|
-
)
|
|
167
|
-
// DI: cover function/class/value/alias/lifetime/injectionMode/spread/unresolved.
|
|
168
|
-
writeFixture(
|
|
169
|
-
moduleRoot,
|
|
170
|
-
'di.ts',
|
|
171
|
-
"import { asFunction, asClass, asValue, aliasTo } from 'awilix'\n"
|
|
172
|
-
+ "import { RealService } from './lib/realService'\n"
|
|
173
|
-
+ "const SENSITIVE_CONFIG = { apiKey: 'super-secret-value-9000' }\n"
|
|
174
|
-
+ "const baseRegistrations = {}\n"
|
|
175
|
-
+ "export function register(container) {\n"
|
|
176
|
-
+ " container.register({\n"
|
|
177
|
-
+ " ...baseRegistrations,\n"
|
|
178
|
-
+ " fnService: asFunction(makeService).singleton(),\n"
|
|
179
|
-
+ " ClassService: asClass(RealService).scoped().proxy(),\n"
|
|
180
|
-
+ " ValueToken: asValue(SENSITIVE_CONFIG),\n"
|
|
181
|
-
+ " aliasToken: aliasTo('ClassService'),\n"
|
|
182
|
-
+ " spreadFn: asFunction(() => 1).transient().classic(),\n"
|
|
183
|
-
+ " dynamicOne: someDynamicFactory(),\n"
|
|
184
|
-
+ " })\n"
|
|
185
|
-
+ "}\n",
|
|
186
|
-
)
|
|
187
|
-
// Generator plugins.
|
|
188
|
-
writeFixture(
|
|
189
|
-
moduleRoot,
|
|
190
|
-
'generators.ts',
|
|
191
|
-
"const pluginA = { id: 'facts.registry', conventionFile: 'facts.items.ts', outputFileName: 'facts-items.generated.ts' }\nexport const generatorPlugins = [pluginA]\n",
|
|
192
|
-
)
|
|
193
|
-
// AI agent extensions (file overrides).
|
|
194
|
-
writeFixture(
|
|
195
|
-
moduleRoot,
|
|
196
|
-
'ai-agents.ts',
|
|
197
|
-
"type AiAgentDefinition = { id: string }\nconst agent: AiAgentDefinition = { id: 'facts.assistant' }\nexport const aiAgents = [agent]\nexport const aiAgentExtensions = [defineAiAgentExtension({ targetAgentId: 'catalog.catalog_assistant', appendAllowedTools: ['facts.stats'] })]\nexport const aiAgentOverrides = { 'catalog.merchandising_assistant': replacementAgent, 'catalog.catalog_assistant': null }\nexport const aiToolOverrides = { 'legacy_tool': null }\nexport default aiAgents\n",
|
|
198
|
-
)
|
|
199
|
-
|
|
200
|
-
facts = extractModuleFacts({
|
|
201
|
-
moduleId: 'facts',
|
|
202
|
-
moduleRoot,
|
|
203
|
-
sourcePackage: '@open-mercato/example',
|
|
204
|
-
sourceVersion: '1.0.0',
|
|
205
|
-
})
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
afterAll(() => {
|
|
209
|
-
fs.rmSync(temporaryRoot, { recursive: true, force: true })
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
it('extracts module-metadata owned contract with only safe scalar fields', () => {
|
|
213
|
-
const fact = ownedById(facts, 'module-metadata', 'facts')
|
|
214
|
-
expect(fact).toBeDefined()
|
|
215
|
-
expect(fact?.metadata).toEqual({ name: 'facts', version: '1.2.3', requires: ['auth', 'catalog'], ejectable: true })
|
|
216
|
-
expect(fact?.source.sourcePath).toBe('node_modules/@open-mercato/example/src/modules/facts/index.ts')
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
it('discovers recursive domain commands and selects a canonical duplicate source with a diagnostic', () => {
|
|
220
|
-
const ids = (facts.ownedContracts?.command ?? []).map((fact) => fact.id)
|
|
221
|
-
expect(ids).toEqual(['facts.reindex', 'facts.sync'])
|
|
222
|
-
// facts.sync is declared in both commands/root.ts and commands/nested/deep.ts.
|
|
223
|
-
const sync = ownedById(facts, 'command', 'facts.sync')
|
|
224
|
-
expect(sync?.source.sourcePath).toBe('node_modules/@open-mercato/example/src/modules/facts/commands/nested/deep.ts')
|
|
225
|
-
const duplicate = facts.factDiagnostics?.find(
|
|
226
|
-
(diagnostic) => diagnostic.code === 'duplicate-source' && diagnostic.kind === 'command' && diagnostic.id === 'facts.sync',
|
|
227
|
-
)
|
|
228
|
-
expect(duplicate?.source?.sourcePath).toBe('node_modules/@open-mercato/example/src/modules/facts/commands/root.ts')
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
it('matches runtime worker identity for declared, derived, and id-less workers', () => {
|
|
232
|
-
const sync = ownedById(facts, 'worker', 'facts:sync')
|
|
233
|
-
expect(sync?.metadata).toEqual({ queue: 'facts-sync', name: 'Sync', concurrency: 2 })
|
|
234
|
-
expect(ownedById(facts, 'worker', 'facts:deep')).toBeDefined()
|
|
235
|
-
|
|
236
|
-
// Runtime falls back to `<module>:workers:<...path>:<file>` when metadata.id is absent,
|
|
237
|
-
// at the root and in nested directories, and resolves a local-constant queue.
|
|
238
|
-
expect(ownedById(facts, 'worker', 'facts:workers:reindex')?.metadata).toEqual({
|
|
239
|
-
queue: 'facts-reindex',
|
|
240
|
-
concurrency: 4,
|
|
241
|
-
})
|
|
242
|
-
expect(ownedById(facts, 'worker', 'facts:workers:nested:cleanup')?.metadata).toEqual({
|
|
243
|
-
queue: 'facts-cleanup',
|
|
244
|
-
})
|
|
245
|
-
})
|
|
246
|
-
|
|
247
|
-
it('reports workers whose required queue is missing or dynamic', () => {
|
|
248
|
-
const workerDiagnostics = (facts.factDiagnostics ?? []).filter(
|
|
249
|
-
(diagnostic) => diagnostic.code === 'unresolved-static-contract' && diagnostic.kind === 'worker',
|
|
250
|
-
)
|
|
251
|
-
expect(workerDiagnostics.map((diagnostic) => diagnostic.id).sort()).toEqual([
|
|
252
|
-
'facts:queueless',
|
|
253
|
-
'facts:workers:dynamic',
|
|
254
|
-
])
|
|
255
|
-
// A worker with no queue is never registered by runtime, so it is not a contract.
|
|
256
|
-
expect(ownedById(facts, 'worker', 'facts:queueless')).toBeUndefined()
|
|
257
|
-
// A dynamic queue still yields a worker contract, minus the unresolvable value.
|
|
258
|
-
expect(ownedById(facts, 'worker', 'facts:workers:dynamic')).toBeDefined()
|
|
259
|
-
expect(JSON.stringify(facts.ownedContracts?.worker)).not.toContain('QUEUE')
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
it('extracts page middleware for both surfaces and diagnoses id-less entries', () => {
|
|
263
|
-
expect(ownedById(facts, 'page-middleware', 'facts.backend.redirect')?.metadata).toEqual({
|
|
264
|
-
surface: 'backend',
|
|
265
|
-
mode: 'backend',
|
|
266
|
-
priority: 10,
|
|
267
|
-
})
|
|
268
|
-
expect(ownedById(facts, 'page-middleware', 'facts.frontend.guard')?.metadata).toMatchObject({ surface: 'frontend' })
|
|
269
|
-
expect(
|
|
270
|
-
facts.factDiagnostics?.some((diagnostic) => diagnostic.kind === 'page-middleware' && diagnostic.code === 'unresolved-static-contract'),
|
|
271
|
-
).toBe(true)
|
|
272
|
-
})
|
|
273
|
-
|
|
274
|
-
it('extracts setup hooks and role identifiers only', () => {
|
|
275
|
-
const setup = ownedById(facts, 'setup', 'facts:setup')
|
|
276
|
-
expect(setup?.metadata).toEqual({ hooks: ['onTenantCreated', 'seedDefaults'], roles: ['admin', 'employee'] })
|
|
277
|
-
})
|
|
278
|
-
|
|
279
|
-
it('extracts encryption entity ids with declared field names', () => {
|
|
280
|
-
const encryption = ownedById(facts, 'encryption', 'facts:facts_widget')
|
|
281
|
-
expect(encryption?.metadata).toEqual({ fields: ['notes', 'secret_snapshot'] })
|
|
282
|
-
})
|
|
283
|
-
|
|
284
|
-
it('extracts custom entity ownership from ce.ts', () => {
|
|
285
|
-
expect((facts.ownedContracts?.['custom-entity'] ?? []).map((fact) => fact.id)).toEqual(['facts:facts_widget'])
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
it('separates additive agent extensions from keyed agent and tool overrides', () => {
|
|
289
|
-
const aiFacts = facts.ownedContracts?.['ai-extension'] ?? []
|
|
290
|
-
expect(aiFacts.map((fact) => fact.id).sort()).toEqual([
|
|
291
|
-
'agent-extension:catalog.catalog_assistant',
|
|
292
|
-
'agent-override:catalog.catalog_assistant',
|
|
293
|
-
'agent-override:catalog.merchandising_assistant',
|
|
294
|
-
'tool-override:legacy_tool',
|
|
295
|
-
])
|
|
296
|
-
const byId = new Map(aiFacts.map((fact) => [fact.id, fact.metadata]))
|
|
297
|
-
expect(byId.get('agent-extension:catalog.catalog_assistant')).toEqual({
|
|
298
|
-
target: 'agent',
|
|
299
|
-
mode: 'extension',
|
|
300
|
-
targetAgentId: 'catalog.catalog_assistant',
|
|
301
|
-
})
|
|
302
|
-
expect(byId.get('agent-override:catalog.merchandising_assistant')).toEqual({
|
|
303
|
-
target: 'agent',
|
|
304
|
-
mode: 'override',
|
|
305
|
-
overrideKey: 'catalog.merchandising_assistant',
|
|
306
|
-
})
|
|
307
|
-
expect(byId.get('tool-override:legacy_tool')).toEqual({
|
|
308
|
-
target: 'tool',
|
|
309
|
-
mode: 'override',
|
|
310
|
-
overrideKey: 'legacy_tool',
|
|
311
|
-
})
|
|
312
|
-
|
|
313
|
-
// Override values (replacement definitions and `null` disables) are never serialized.
|
|
314
|
-
const serialized = JSON.stringify(aiFacts)
|
|
315
|
-
expect(serialized).not.toContain('replacementAgent')
|
|
316
|
-
expect(serialized).not.toContain('appendAllowedTools')
|
|
317
|
-
})
|
|
318
|
-
|
|
319
|
-
it('extracts generator plugin ownership with safe metadata', () => {
|
|
320
|
-
expect(ownedById(facts, 'generator-plugin', 'facts.registry')?.metadata).toEqual({
|
|
321
|
-
conventionFile: 'facts.items.ts',
|
|
322
|
-
outputFileName: 'facts-items.generated.ts',
|
|
323
|
-
})
|
|
324
|
-
})
|
|
325
|
-
|
|
326
|
-
it('classifies every supported DI registration form without leaking values', () => {
|
|
327
|
-
const di = facts.ownedContracts?.['di-registration'] ?? []
|
|
328
|
-
const byToken = new Map(di.map((fact) => [fact.id, fact.metadata]))
|
|
329
|
-
expect(byToken.get('fnService')).toEqual({ registrationKind: 'function', providerSymbol: 'makeService', lifetime: 'singleton' })
|
|
330
|
-
expect(byToken.get('ClassService')).toEqual({
|
|
331
|
-
registrationKind: 'class',
|
|
332
|
-
providerSymbol: 'RealService',
|
|
333
|
-
lifetime: 'scoped',
|
|
334
|
-
injectionMode: 'proxy',
|
|
335
|
-
})
|
|
336
|
-
expect(byToken.get('ValueToken')).toEqual({ registrationKind: 'value' })
|
|
337
|
-
expect(byToken.get('aliasToken')).toEqual({ registrationKind: 'alias', providerSymbol: 'ClassService' })
|
|
338
|
-
expect(byToken.get('spreadFn')).toEqual({ registrationKind: 'function', lifetime: 'transient', injectionMode: 'classic' })
|
|
339
|
-
// Dynamic registration is diagnosed, not fabricated.
|
|
340
|
-
expect(byToken.has('dynamicOne')).toBe(false)
|
|
341
|
-
expect(
|
|
342
|
-
facts.factDiagnostics?.some((diagnostic) => diagnostic.kind === 'di-registration' && diagnostic.id === 'dynamicOne'),
|
|
343
|
-
).toBe(true)
|
|
344
|
-
})
|
|
345
|
-
|
|
346
|
-
it('derives the legacy diTokens list from function/class registrations only, preserving order', () => {
|
|
347
|
-
expect(facts.diTokens).toEqual(['fnService', 'ClassService', 'spreadFn'])
|
|
348
|
-
})
|
|
349
|
-
|
|
350
|
-
it('never leaks asValue payloads, secrets, config bodies, or absolute paths', () => {
|
|
351
|
-
const serialized = JSON.stringify(toModuleFactsJsonEntry(facts))
|
|
352
|
-
expect(serialized).not.toContain('SENSITIVE_CONFIG')
|
|
353
|
-
expect(serialized).not.toContain('super-secret-value-9000')
|
|
354
|
-
expect(serialized).not.toContain('apiKey')
|
|
355
|
-
expect(serialized).not.toContain('makeService(')
|
|
356
|
-
expect(serialized).not.toContain(temporaryRoot)
|
|
357
|
-
expect(serialized).not.toMatch(/"sourcePath":\s*"\//)
|
|
358
|
-
})
|
|
359
|
-
|
|
360
|
-
it('resolves every proven fact through the uniform provenance index', () => {
|
|
361
|
-
const resolve = buildFactSourceLookup(facts)
|
|
362
|
-
expect(resolve('entity', 'facts:facts_widget')?.sourcePath).toBe(
|
|
363
|
-
'node_modules/@open-mercato/example/src/modules/facts/data/entities.ts',
|
|
364
|
-
)
|
|
365
|
-
expect(resolve('event', 'facts.widget.created')?.sourcePath).toBe(
|
|
366
|
-
'node_modules/@open-mercato/example/src/modules/facts/events.ts',
|
|
367
|
-
)
|
|
368
|
-
expect(resolve('acl-feature', 'facts.view')?.sourcePath).toBe(
|
|
369
|
-
'node_modules/@open-mercato/example/src/modules/facts/acl.ts',
|
|
370
|
-
)
|
|
371
|
-
expect(resolve('search', 'facts:facts_widget')?.sourcePath).toBe(
|
|
372
|
-
'node_modules/@open-mercato/example/src/modules/facts/search.ts',
|
|
373
|
-
)
|
|
374
|
-
expect(resolve('notification', 'facts.alert')?.sourcePath).toBe(
|
|
375
|
-
'node_modules/@open-mercato/example/src/modules/facts/notifications.ts',
|
|
376
|
-
)
|
|
377
|
-
expect(resolve('subscriber', 'facts:on-created')?.sourcePath).toBe(
|
|
378
|
-
'node_modules/@open-mercato/example/src/modules/facts/subscribers/nested/on-created.ts',
|
|
379
|
-
)
|
|
380
|
-
|
|
381
|
-
// Kinds whose declaration site is already serialized inline are indexed as typed
|
|
382
|
-
// pointers, and still resolve to a real source through the same lookup.
|
|
383
|
-
const commandEntry = facts.factSources?.find(
|
|
384
|
-
(entry) => entry.kind === 'command' && entry.id === 'facts.sync',
|
|
385
|
-
)
|
|
386
|
-
expect(commandEntry?.source).toBeUndefined()
|
|
387
|
-
expect(commandEntry?.factRef).toEqual({ factSection: 'ownedContracts.command' })
|
|
388
|
-
expect(resolve('command', 'facts.sync')?.sourcePath).toBe(
|
|
389
|
-
'node_modules/@open-mercato/example/src/modules/facts/commands/nested/deep.ts',
|
|
390
|
-
)
|
|
391
|
-
|
|
392
|
-
const diEntry = facts.factSources?.find((entry) => entry.kind === 'di-registration')
|
|
393
|
-
expect(diEntry?.factRef?.factSection).toBe('ownedContracts.di-registration')
|
|
394
|
-
expect(resolve('di-registration', diEntry?.id as string)?.sourcePath).toBe(
|
|
395
|
-
'node_modules/@open-mercato/example/src/modules/facts/di.ts',
|
|
396
|
-
)
|
|
397
|
-
|
|
398
|
-
// Every emitted entry carries exactly one projection and resolves.
|
|
399
|
-
for (const entry of facts.factSources ?? []) {
|
|
400
|
-
expect(Boolean(entry.source) !== Boolean(entry.factRef)).toBe(true)
|
|
401
|
-
expect(resolve(entry.kind, entry.id)).not.toBeNull()
|
|
402
|
-
}
|
|
403
|
-
})
|
|
404
|
-
|
|
405
|
-
it('indexes every proven contract kind exactly once', () => {
|
|
406
|
-
const indexed = new Set((facts.factSources ?? []).map((entry) => `${entry.kind}:${entry.id}`))
|
|
407
|
-
expect(indexed.size).toBe((facts.factSources ?? []).length)
|
|
408
|
-
for (const route of facts.apiRoutes) expect(indexed.has(`api-route:${route.path}`)).toBe(true)
|
|
409
|
-
for (const page of facts.backendPages) expect(indexed.has(`backend-page:${page.path}`)).toBe(true)
|
|
410
|
-
for (const command of facts.cliCommands) expect(indexed.has(`cli-command:${command.command}`)).toBe(true)
|
|
411
|
-
for (const tool of facts.aiTools) expect(indexed.has(`ai-tool:${tool.name}`)).toBe(true)
|
|
412
|
-
for (const [kind, ownedFacts] of Object.entries(facts.ownedContracts ?? {})) {
|
|
413
|
-
for (const owned of ownedFacts ?? []) expect(indexed.has(`${kind}:${owned.id}`)).toBe(true)
|
|
414
|
-
}
|
|
415
|
-
for (const host of facts.extensionSurfaces?.hosts ?? []) {
|
|
416
|
-
expect(indexed.has(`extension-host:${host.key}`)).toBe(true)
|
|
417
|
-
}
|
|
418
|
-
for (const contribution of facts.extensionSurfaces?.contributions ?? []) {
|
|
419
|
-
expect(indexed.has(`extension-contribution:${contribution.id}`)).toBe(true)
|
|
420
|
-
}
|
|
421
|
-
})
|
|
422
|
-
|
|
423
|
-
it('keeps subscriber discovery aligned with runtime recursion', () => {
|
|
424
|
-
const subscriber = facts.extensionSurfaces?.contributions.find((contribution) => contribution.id === 'facts:on-created')
|
|
425
|
-
expect(subscriber?.kind).toBe('subscriber')
|
|
426
|
-
expect(subscriber?.source.path).toBe('node_modules/@open-mercato/example/src/modules/facts/subscribers/nested/on-created.ts')
|
|
427
|
-
})
|
|
428
|
-
|
|
429
|
-
it('attaches only statically-declared safe page metadata', () => {
|
|
430
|
-
const page = facts.backendPages.find((entry) => entry.path === '/backend/facts')
|
|
431
|
-
expect(page?.metadata).toEqual({ requireAuth: true, requireFeatures: ['facts.view'], navGroup: 'crm' })
|
|
432
|
-
expect(page?.metadataSourcePath).toBeUndefined()
|
|
433
|
-
})
|
|
434
|
-
|
|
435
|
-
it('reads page metadata from the runtime companion file convention', () => {
|
|
436
|
-
const companionPage = facts.backendPages.find((entry) => entry.path === '/backend/facts/config')
|
|
437
|
-
expect(companionPage?.metadataSourcePath).toBe(
|
|
438
|
-
'node_modules/@open-mercato/example/src/modules/facts/backend/facts/config/page.meta.ts',
|
|
439
|
-
)
|
|
440
|
-
expect(companionPage?.metadata).toEqual({
|
|
441
|
-
requireAuth: true,
|
|
442
|
-
requireFeatures: ['facts.configure'],
|
|
443
|
-
navGroup: 'settings-group',
|
|
444
|
-
navGroupKey: 'facts.settings',
|
|
445
|
-
navOrder: 30,
|
|
446
|
-
pageContext: 'settings',
|
|
447
|
-
navHidden: true,
|
|
448
|
-
})
|
|
449
|
-
|
|
450
|
-
// `meta.*` is the documented fallback name, and page-component metadata is
|
|
451
|
-
// ignored entirely when a companion exists (runtime imports only the companion).
|
|
452
|
-
const fallbackPage = facts.backendPages.find((entry) => entry.path === '/backend/facts/reports')
|
|
453
|
-
expect(fallbackPage?.metadataSourcePath).toBe(
|
|
454
|
-
'node_modules/@open-mercato/example/src/modules/facts/backend/facts/reports/meta.ts',
|
|
455
|
-
)
|
|
456
|
-
expect(fallbackPage?.metadata).toEqual({ requireAuth: true, navOrder: 5, pageContext: 'admin' })
|
|
457
|
-
})
|
|
458
|
-
|
|
459
|
-
it('emits portable POSIX source paths and is deterministic across runs', () => {
|
|
460
|
-
const resolve = buildFactSourceLookup(facts)
|
|
461
|
-
for (const entry of facts.factSources ?? []) {
|
|
462
|
-
const source = resolve(entry.kind, entry.id)
|
|
463
|
-
expect(source?.sourcePath.startsWith('node_modules/@open-mercato/example/')).toBe(true)
|
|
464
|
-
expect(source?.sourcePath.includes('\\')).toBe(false)
|
|
465
|
-
}
|
|
466
|
-
const rerun = extractModuleFacts({
|
|
467
|
-
moduleId: 'facts',
|
|
468
|
-
moduleRoot,
|
|
469
|
-
sourcePackage: '@open-mercato/example',
|
|
470
|
-
sourceVersion: '1.0.0',
|
|
471
|
-
})
|
|
472
|
-
expect(JSON.stringify(toModuleFactsJsonEntry(rerun))).toBe(JSON.stringify(toModuleFactsJsonEntry(facts)))
|
|
473
|
-
})
|
|
474
|
-
|
|
475
|
-
it('renders new owned-contract markdown sections with clickable source links', () => {
|
|
476
|
-
const markdown = renderModuleFactsMarkdown(facts)
|
|
477
|
-
expect(markdown).toContain('## Domain commands')
|
|
478
|
-
expect(markdown).toContain('## Workers')
|
|
479
|
-
expect(markdown).toContain('## Page middleware')
|
|
480
|
-
expect(markdown).toContain('## Setup')
|
|
481
|
-
expect(markdown).toContain('## Encryption')
|
|
482
|
-
expect(markdown).toContain('## DI registrations (rich)')
|
|
483
|
-
expect(markdown).toContain('## Generator plugins')
|
|
484
|
-
expect(markdown).toContain('(../../../node_modules/@open-mercato/example/src/modules/facts/di.ts')
|
|
485
|
-
// Empty-content sections are omitted rather than rendered blank.
|
|
486
|
-
expect(markdown).not.toMatch(/## Domain commands\n\n_none_/)
|
|
487
|
-
})
|
|
488
|
-
|
|
489
|
-
it('renders a resolved source link for every represented scalar fact', () => {
|
|
490
|
-
const markdown = renderModuleFactsMarkdown(facts)
|
|
491
|
-
const rowFor = (identifier: string): string => {
|
|
492
|
-
const row = markdown.split('\n').find((line) => line.startsWith(`| ${identifier} |`))
|
|
493
|
-
return row ?? ''
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
expect(markdown).toContain('| Entity ID | Class | Table | Editable | CustomFields | Source |')
|
|
497
|
-
expect(rowFor('facts:facts_widget')).toContain(
|
|
498
|
-
'(../../../node_modules/@open-mercato/example/src/modules/facts/data/entities.ts)',
|
|
499
|
-
)
|
|
500
|
-
|
|
501
|
-
expect(markdown).toContain('| ID | Category | Entity | Browser transport | Source |')
|
|
502
|
-
expect(rowFor('facts.widget.created')).toContain(
|
|
503
|
-
'(../../../node_modules/@open-mercato/example/src/modules/facts/events.ts)',
|
|
504
|
-
)
|
|
505
|
-
|
|
506
|
-
expect(markdown).toContain('| Feature | Source |')
|
|
507
|
-
expect(rowFor('facts.view')).toContain('(../../../node_modules/@open-mercato/example/src/modules/facts/acl.ts)')
|
|
508
|
-
|
|
509
|
-
expect(markdown).toContain('| Token | Source |')
|
|
510
|
-
expect(markdown).toContain('| Entity ID | Source |')
|
|
511
|
-
expect(markdown).toContain('| Notification ID | Source |')
|
|
512
|
-
expect(rowFor('facts.alert')).toContain(
|
|
513
|
-
'(../../../node_modules/@open-mercato/example/src/modules/facts/notifications.ts)',
|
|
514
|
-
)
|
|
515
|
-
|
|
516
|
-
// No represented scalar fact renders an unresolved source cell.
|
|
517
|
-
const scalarSections = ['## ACL features', '## DI service tokens', '## Search entities', '## Notifications']
|
|
518
|
-
for (const heading of scalarSections) {
|
|
519
|
-
const section = markdown.slice(markdown.indexOf(heading)).split('\n\n')[1] ?? ''
|
|
520
|
-
expect(section).not.toContain('| — |')
|
|
521
|
-
}
|
|
522
|
-
})
|
|
523
|
-
|
|
524
|
-
it('renders source columns on the extension-surface sections', () => {
|
|
525
|
-
const markdown = renderModuleFactsMarkdown(facts)
|
|
526
|
-
expect(markdown).toContain('## Contribution resolutions')
|
|
527
|
-
expect(markdown).toContain('| ID / pattern | Family | Supports | Context | Stability | Source |')
|
|
528
|
-
expect(markdown).toContain('| ID | Kind | Target | Phase / operations | Contract | Resolution | Source |')
|
|
529
|
-
})
|
|
530
|
-
})
|
|
531
|
-
|
|
532
|
-
describe('module-facts integration array/bundle coverage (Spec 1)', () => {
|
|
533
|
-
let temporaryRoot: string
|
|
534
|
-
|
|
535
|
-
afterAll(() => {
|
|
536
|
-
if (temporaryRoot) fs.rmSync(temporaryRoot, { recursive: true, force: true })
|
|
537
|
-
})
|
|
538
|
-
|
|
539
|
-
it('reads singular, array, and bundle integration declarations', () => {
|
|
540
|
-
temporaryRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'module-facts-integration-'))
|
|
541
|
-
const moduleRoot = path.join(temporaryRoot, 'shipments')
|
|
542
|
-
writeFixture(moduleRoot, 'index.ts', "export const metadata = { name: 'shipments' }\n")
|
|
543
|
-
writeFixture(
|
|
544
|
-
moduleRoot,
|
|
545
|
-
'integration.ts',
|
|
546
|
-
"export const integration = { id: 'shipments.primary' }\n"
|
|
547
|
-
+ "export const integrations = [{ id: 'shipments.alt' }, { id: 'shipments.legacy' }]\n"
|
|
548
|
-
+ "export const integrationBundle = { id: 'carriers', integrations: [{ id: 'shipments.bundled' }] }\n",
|
|
549
|
-
)
|
|
550
|
-
const facts = extractModuleFacts({
|
|
551
|
-
moduleId: 'shipments',
|
|
552
|
-
moduleRoot,
|
|
553
|
-
sourcePackage: '@open-mercato/example',
|
|
554
|
-
sourceVersion: '1.0.0',
|
|
555
|
-
})
|
|
556
|
-
const integrationIds = new Set(
|
|
557
|
-
(facts.factSources ?? []).filter((entry) => entry.kind === 'integration').map((entry) => entry.id),
|
|
558
|
-
)
|
|
559
|
-
expect(integrationIds).toEqual(new Set(['shipments.primary', 'shipments.alt', 'shipments.legacy', 'shipments.bundled']))
|
|
560
|
-
})
|
|
561
|
-
})
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs'
|
|
2
|
-
import os from 'node:os'
|
|
3
|
-
import path from 'node:path'
|
|
4
|
-
import { extractModuleFacts, renderModuleFactsMarkdown } from '../module-facts'
|
|
5
|
-
|
|
6
|
-
function writeFixture(root: string, relativePath: string, source: string): void {
|
|
7
|
-
const filePath = path.join(root, relativePath)
|
|
8
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true })
|
|
9
|
-
fs.writeFileSync(filePath, source)
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
describe('module-facts source-linked extension surfaces', () => {
|
|
13
|
-
let temporaryRoot: string
|
|
14
|
-
let moduleRoot: string
|
|
15
|
-
|
|
16
|
-
beforeAll(() => {
|
|
17
|
-
temporaryRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'module-facts-source-links-'))
|
|
18
|
-
moduleRoot = path.join(temporaryRoot, 'facts')
|
|
19
|
-
writeFixture(moduleRoot, 'index.ts', "export const metadata = { title: 'Facts' }\n")
|
|
20
|
-
writeFixture(
|
|
21
|
-
moduleRoot,
|
|
22
|
-
'api/records/route.ts',
|
|
23
|
-
"export const metadata = { path: '/facts/records-custom' }\nexport async function GET() {}\n",
|
|
24
|
-
)
|
|
25
|
-
writeFixture(moduleRoot, 'backend/page.tsx', 'export default function FactsPage() { return null }\n')
|
|
26
|
-
writeFixture(
|
|
27
|
-
moduleRoot,
|
|
28
|
-
'backend/facts/items/[id]/page.tsx',
|
|
29
|
-
'export default function FactItemPage() { return null }\n',
|
|
30
|
-
)
|
|
31
|
-
writeFixture(
|
|
32
|
-
moduleRoot,
|
|
33
|
-
'frontend/facts/portal/[id]/page.tsx',
|
|
34
|
-
'export default function FactPortalPage() { return null }\n',
|
|
35
|
-
)
|
|
36
|
-
writeFixture(
|
|
37
|
-
moduleRoot,
|
|
38
|
-
'cli.ts',
|
|
39
|
-
"const commands = [{ command: 'facts:sync' }, { command: 'facts:check' }]\nexport default commands\n",
|
|
40
|
-
)
|
|
41
|
-
writeFixture(
|
|
42
|
-
moduleRoot,
|
|
43
|
-
'ai-tools/tools-pack.ts',
|
|
44
|
-
"type FactsAiToolDefinition = { name: string; description: string }\nconst inspectTool: FactsAiToolDefinition = { name: 'facts.inspect', description: 'Inspect facts' }\nexport default [inspectTool]\n",
|
|
45
|
-
)
|
|
46
|
-
writeFixture(
|
|
47
|
-
moduleRoot,
|
|
48
|
-
'ai-tools.ts',
|
|
49
|
-
"type FactsAiToolDefinition = { name: string; description: string }\nconst listTool: FactsAiToolDefinition = { name: 'facts.list', description: 'List facts' }\nexport const aiTools = [listTool]\nexport default aiTools\n",
|
|
50
|
-
)
|
|
51
|
-
writeFixture(
|
|
52
|
-
moduleRoot,
|
|
53
|
-
'ai-agents.ts',
|
|
54
|
-
"type AiAgentDefinition = { id: string; label: string }\nconst AGENT_ID = 'facts.assistant'\nconst agent: AiAgentDefinition = { id: AGENT_ID, label: 'Facts assistant' }\nexport const aiAgents = [agent]\nexport default aiAgents\n",
|
|
55
|
-
)
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
afterAll(() => {
|
|
59
|
-
fs.rmSync(temporaryRoot, { recursive: true, force: true })
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
it('emits portable source links for routes, pages, commands, tools, and agents', () => {
|
|
63
|
-
const registrySource = `export const modules = [{
|
|
64
|
-
id: 'facts',
|
|
65
|
-
apis: [{
|
|
66
|
-
path: '/facts/records-custom',
|
|
67
|
-
handlers: { GET: async () => undefined },
|
|
68
|
-
metadata: { GET: { requireFeatures: ['facts.view'] } },
|
|
69
|
-
}],
|
|
70
|
-
}]`
|
|
71
|
-
const facts = extractModuleFacts({
|
|
72
|
-
moduleId: 'facts',
|
|
73
|
-
moduleRoot,
|
|
74
|
-
sourcePackage: '@open-mercato/example',
|
|
75
|
-
sourceVersion: '1.2.3',
|
|
76
|
-
registrySource,
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
expect(facts.sourceRoot).toBe('node_modules/@open-mercato/example/src/modules/facts')
|
|
80
|
-
expect(facts.apiRoutes).toEqual([
|
|
81
|
-
{
|
|
82
|
-
path: '/facts/records-custom',
|
|
83
|
-
methods: ['GET'],
|
|
84
|
-
auth: { GET: { requireFeatures: ['facts.view'] } },
|
|
85
|
-
sourcePath: 'node_modules/@open-mercato/example/src/modules/facts/api/records/route.ts',
|
|
86
|
-
},
|
|
87
|
-
])
|
|
88
|
-
expect(facts.backendPages).toEqual([
|
|
89
|
-
{
|
|
90
|
-
path: '/backend/facts',
|
|
91
|
-
sourcePath: 'node_modules/@open-mercato/example/src/modules/facts/backend/page.tsx',
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
path: '/backend/facts/items/[id]',
|
|
95
|
-
sourcePath: 'node_modules/@open-mercato/example/src/modules/facts/backend/facts/items/[id]/page.tsx',
|
|
96
|
-
},
|
|
97
|
-
])
|
|
98
|
-
expect(facts.frontendPages).toEqual([
|
|
99
|
-
{
|
|
100
|
-
path: '/facts/portal/[id]',
|
|
101
|
-
sourcePath: 'node_modules/@open-mercato/example/src/modules/facts/frontend/facts/portal/[id]/page.tsx',
|
|
102
|
-
},
|
|
103
|
-
])
|
|
104
|
-
expect(facts.cli).toEqual(['facts:sync', 'facts:check'])
|
|
105
|
-
expect(facts.cliCommands.map((command) => command.command)).toEqual(['facts:sync', 'facts:check'])
|
|
106
|
-
expect(facts.aiTools.map((tool) => tool.name)).toEqual(['facts.inspect', 'facts.list'])
|
|
107
|
-
expect(facts.aiAgents.map((agentFact) => agentFact.id)).toEqual(['facts.assistant'])
|
|
108
|
-
|
|
109
|
-
const markdown = renderModuleFactsMarkdown(facts)
|
|
110
|
-
expect(markdown).toContain('## Backend pages')
|
|
111
|
-
expect(markdown).toContain('## Frontend pages')
|
|
112
|
-
expect(markdown).toContain('## CLI commands')
|
|
113
|
-
expect(markdown).toContain('## AI tools / MCP capabilities')
|
|
114
|
-
expect(markdown).toContain('## AI agents')
|
|
115
|
-
expect(markdown).toContain(
|
|
116
|
-
'(../../../node_modules/@open-mercato/example/src/modules/facts/api/records/route.ts)',
|
|
117
|
-
)
|
|
118
|
-
})
|
|
119
|
-
})
|