@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,3 +1,5 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import os from 'node:os'
|
|
1
3
|
import path from 'node:path'
|
|
2
4
|
import {
|
|
3
5
|
createGenerateWatchChangeSignal,
|
|
@@ -26,6 +28,7 @@ function createWatchHarness(initialTargets: GenerateWatchTarget[]) {
|
|
|
26
28
|
const signal = createGenerateWatchChangeSignal({
|
|
27
29
|
getWatchTargets: () => targets,
|
|
28
30
|
watchDirectory,
|
|
31
|
+
directoryExists: () => true,
|
|
29
32
|
})
|
|
30
33
|
|
|
31
34
|
return {
|
|
@@ -37,6 +40,78 @@ function createWatchHarness(initialTargets: GenerateWatchTarget[]) {
|
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
describe('createGenerateWatchChangeSignal', () => {
|
|
43
|
+
it('skips missing directories without falling back and retries them on refresh', async () => {
|
|
44
|
+
const present = path.resolve('./modules-present')
|
|
45
|
+
const delayed = path.resolve('./modules-delayed')
|
|
46
|
+
const existing = new Set([present])
|
|
47
|
+
const registered: GenerateWatchTarget[] = []
|
|
48
|
+
const signal = createGenerateWatchChangeSignal({
|
|
49
|
+
getWatchTargets: () => [
|
|
50
|
+
{ directory: present, recursive: true },
|
|
51
|
+
{ directory: delayed, recursive: true },
|
|
52
|
+
],
|
|
53
|
+
directoryExists: (directory) => existing.has(directory),
|
|
54
|
+
watchDirectory: (target) => {
|
|
55
|
+
registered.push(target)
|
|
56
|
+
return { close: jest.fn() }
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
await signal.refresh()
|
|
61
|
+
expect(registered.map((target) => target.directory)).toEqual([present])
|
|
62
|
+
expect(signal.hasSkippedTargets?.()).toBe(true)
|
|
63
|
+
expect(signal.usesPollingFallback()).toBe(false)
|
|
64
|
+
|
|
65
|
+
existing.add(delayed)
|
|
66
|
+
await signal.refresh()
|
|
67
|
+
expect(registered.map((target) => target.directory)).toEqual([present, delayed])
|
|
68
|
+
expect(signal.hasSkippedTargets?.()).toBe(false)
|
|
69
|
+
expect(signal.usesPollingFallback()).toBe(false)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('reports a missing directory once while it remains skipped', async () => {
|
|
73
|
+
const missing = path.resolve('./modules-missing')
|
|
74
|
+
const onSkippedDirectory = jest.fn()
|
|
75
|
+
const options = {
|
|
76
|
+
getWatchTargets: () => [{ directory: missing, recursive: true }],
|
|
77
|
+
directoryExists: () => false,
|
|
78
|
+
watchDirectory: jest.fn(() => ({ close: jest.fn() })),
|
|
79
|
+
onSkippedDirectory,
|
|
80
|
+
}
|
|
81
|
+
const signal = createGenerateWatchChangeSignal(options)
|
|
82
|
+
|
|
83
|
+
await signal.refresh()
|
|
84
|
+
await signal.refresh()
|
|
85
|
+
|
|
86
|
+
expect(onSkippedDirectory).toHaveBeenCalledTimes(1)
|
|
87
|
+
expect(onSkippedDirectory).toHaveBeenCalledWith(missing)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('uses filesystem existence checks by default', async () => {
|
|
91
|
+
const present = fs.mkdtempSync(path.join(os.tmpdir(), 'mercato-generate-watch-'))
|
|
92
|
+
const missing = path.join(present, 'missing')
|
|
93
|
+
const registered: string[] = []
|
|
94
|
+
const signal = createGenerateWatchChangeSignal({
|
|
95
|
+
getWatchTargets: () => [
|
|
96
|
+
{ directory: present, recursive: true },
|
|
97
|
+
{ directory: missing, recursive: true },
|
|
98
|
+
],
|
|
99
|
+
watchDirectory: (target) => {
|
|
100
|
+
registered.push(target.directory)
|
|
101
|
+
return { close: jest.fn() }
|
|
102
|
+
},
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
await signal.refresh()
|
|
107
|
+
expect(registered).toEqual([present])
|
|
108
|
+
expect(signal.usesPollingFallback()).toBe(false)
|
|
109
|
+
} finally {
|
|
110
|
+
await signal.close()
|
|
111
|
+
fs.rmSync(present, { recursive: true, force: true })
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
|
|
40
115
|
it('deduplicates targets and increments its version on filesystem events', async () => {
|
|
41
116
|
const target = { directory: './modules', recursive: true }
|
|
42
117
|
const { signal, registered, watchDirectory } = createWatchHarness([target, target])
|
|
@@ -80,6 +155,7 @@ describe('createGenerateWatchChangeSignal', () => {
|
|
|
80
155
|
{ directory: './modules-b', recursive: true },
|
|
81
156
|
],
|
|
82
157
|
watchDirectory,
|
|
158
|
+
directoryExists: () => true,
|
|
83
159
|
})
|
|
84
160
|
|
|
85
161
|
await signal.refresh()
|
|
@@ -14,15 +14,26 @@ async function flushAsync(times = 5): Promise<void> {
|
|
|
14
14
|
function createFakeChangeSignal() {
|
|
15
15
|
let version = 0
|
|
16
16
|
let fallback = false
|
|
17
|
+
let skippedTargets = false
|
|
18
|
+
let discoverSkippedTargets = false
|
|
19
|
+
const refresh = jest.fn(async () => {
|
|
20
|
+
if (discoverSkippedTargets) {
|
|
21
|
+
skippedTargets = false
|
|
22
|
+
discoverSkippedTargets = false
|
|
23
|
+
}
|
|
24
|
+
})
|
|
17
25
|
const signal: GenerateWatcherChangeSignal = {
|
|
18
26
|
currentVersion: () => version,
|
|
19
|
-
refresh
|
|
27
|
+
refresh,
|
|
20
28
|
usesPollingFallback: () => fallback,
|
|
29
|
+
hasSkippedTargets: () => skippedTargets,
|
|
21
30
|
close: jest.fn(async () => undefined),
|
|
22
31
|
}
|
|
23
32
|
return {
|
|
24
33
|
signal,
|
|
25
34
|
markChanged: () => { version += 1 },
|
|
35
|
+
markSkippedTarget: () => { skippedTargets = true },
|
|
36
|
+
discoverSkippedTargetOnNextRefresh: () => { discoverSkippedTargets = true },
|
|
26
37
|
usePollingFallback: () => { fallback = true },
|
|
27
38
|
}
|
|
28
39
|
}
|
|
@@ -142,6 +153,46 @@ describe('startInProcessGenerateWatcher', () => {
|
|
|
142
153
|
await handle.close()
|
|
143
154
|
})
|
|
144
155
|
|
|
156
|
+
it('discovers skipped roots during event-gated idle polling', async () => {
|
|
157
|
+
const runGenerators = jest.fn(async () => undefined)
|
|
158
|
+
const computeStructureChecksum = jest.fn()
|
|
159
|
+
.mockResolvedValueOnce('before')
|
|
160
|
+
.mockResolvedValue('after')
|
|
161
|
+
const {
|
|
162
|
+
signal,
|
|
163
|
+
markSkippedTarget,
|
|
164
|
+
discoverSkippedTargetOnNextRefresh,
|
|
165
|
+
} = createFakeChangeSignal()
|
|
166
|
+
markSkippedTarget()
|
|
167
|
+
const handle = startInProcessGenerateWatcher({
|
|
168
|
+
pollMs: 1000,
|
|
169
|
+
skipInitial: true,
|
|
170
|
+
quiet: true,
|
|
171
|
+
logger: silentLogger,
|
|
172
|
+
changeSignal: signal,
|
|
173
|
+
computeStructureChecksum,
|
|
174
|
+
runGenerators,
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
await flushAsync(8)
|
|
178
|
+
expect(computeStructureChecksum).toHaveBeenCalledTimes(1)
|
|
179
|
+
|
|
180
|
+
jest.advanceTimersByTime(1000)
|
|
181
|
+
await flushAsync(12)
|
|
182
|
+
|
|
183
|
+
expect(signal.refresh).toHaveBeenCalledTimes(2)
|
|
184
|
+
expect(computeStructureChecksum).toHaveBeenCalledTimes(1)
|
|
185
|
+
discoverSkippedTargetOnNextRefresh()
|
|
186
|
+
|
|
187
|
+
jest.advanceTimersByTime(1000)
|
|
188
|
+
await flushAsync(12)
|
|
189
|
+
|
|
190
|
+
expect(signal.refresh).toHaveBeenCalledTimes(3)
|
|
191
|
+
expect(computeStructureChecksum).toHaveBeenCalledTimes(2)
|
|
192
|
+
expect(runGenerators).toHaveBeenCalledWith('structure change')
|
|
193
|
+
await handle.close()
|
|
194
|
+
})
|
|
195
|
+
|
|
145
196
|
it('coalesces filesystem event bursts into one checksum and regeneration', async () => {
|
|
146
197
|
let currentChecksum = 'before'
|
|
147
198
|
const runGenerators = jest.fn(async () => undefined)
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import os from 'node:os'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { createInjectionWidgetsExtension } from '../generators/extensions/injection-widgets'
|
|
5
|
+
import type { ModuleScanContext } from '../generators/extension'
|
|
6
|
+
|
|
7
|
+
// A widget's `metadata.id` and the `widgetId` that mounts it are two hand-typed literals in two
|
|
8
|
+
// different files. Nothing linked them, so a typo in either failed silently — the widget was never
|
|
9
|
+
// mounted, or the table entry resolved to nothing. These cases drive the real scan → generate seam
|
|
10
|
+
// (real filesystem fixtures, real AST extraction) and assert the reconciliation warnings.
|
|
11
|
+
|
|
12
|
+
let tmpRoot: string
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'om-injection-widgets-'))
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
fs.rmSync(tmpRoot, { recursive: true, force: true })
|
|
20
|
+
jest.restoreAllMocks()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
function widgetSource(id: string): string {
|
|
24
|
+
return [
|
|
25
|
+
"import type { InjectionWidgetModule } from '@open-mercato/shared/modules/widgets/injection'",
|
|
26
|
+
"import Impl from './widget.client'",
|
|
27
|
+
'',
|
|
28
|
+
'const widget: InjectionWidgetModule = {',
|
|
29
|
+
' metadata: {',
|
|
30
|
+
` id: '${id}',`,
|
|
31
|
+
" title: 'Fixture widget',",
|
|
32
|
+
' },',
|
|
33
|
+
' Widget: Impl,',
|
|
34
|
+
'}',
|
|
35
|
+
'',
|
|
36
|
+
'export default widget',
|
|
37
|
+
].join('\n')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function tableSource(widgetIds: string[]): string {
|
|
41
|
+
const entries = widgetIds
|
|
42
|
+
.map((id, index) => ` 'detail:fixture:tab${index}': [{ widgetId: '${id}', kind: 'tab' }],`)
|
|
43
|
+
.join('\n')
|
|
44
|
+
return [
|
|
45
|
+
"import type { ModuleInjectionTable } from '@open-mercato/shared/modules/widgets/injection'",
|
|
46
|
+
'',
|
|
47
|
+
'export const injectionTable: ModuleInjectionTable = {',
|
|
48
|
+
entries,
|
|
49
|
+
'}',
|
|
50
|
+
'',
|
|
51
|
+
'export default injectionTable',
|
|
52
|
+
].join('\n')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Lays out one module on disk in the shape the scanner expects and returns a scan context for it.
|
|
57
|
+
* `declaredIds` each get `widgets/injection/<name>/widget.ts`; `referencedIds` go into the module's
|
|
58
|
+
* `widgets/injection-table.ts` (omitted entirely when the list is empty).
|
|
59
|
+
*/
|
|
60
|
+
function makeModule(moduleId: string, declaredIds: string[], referencedIds: string[]): ModuleScanContext {
|
|
61
|
+
const base = path.join(tmpRoot, moduleId)
|
|
62
|
+
const injectionDir = path.join(base, 'widgets', 'injection')
|
|
63
|
+
fs.mkdirSync(injectionDir, { recursive: true })
|
|
64
|
+
|
|
65
|
+
declaredIds.forEach((id, index) => {
|
|
66
|
+
const dir = path.join(injectionDir, `w${index}`)
|
|
67
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
68
|
+
fs.writeFileSync(path.join(dir, 'widget.ts'), widgetSource(id))
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const tablePath = path.join(base, 'widgets', 'injection-table.ts')
|
|
72
|
+
if (referencedIds.length > 0) fs.writeFileSync(tablePath, tableSource(referencedIds))
|
|
73
|
+
|
|
74
|
+
const roots = { appBase: base, pkgBase: base }
|
|
75
|
+
return {
|
|
76
|
+
moduleId,
|
|
77
|
+
roots,
|
|
78
|
+
imps: { appBase: `@fixture/${moduleId}`, pkgBase: `@fixture/${moduleId}` },
|
|
79
|
+
importIdRef: { value: 0 },
|
|
80
|
+
sharedImports: [],
|
|
81
|
+
resolveModuleFile: (() =>
|
|
82
|
+
referencedIds.length > 0
|
|
83
|
+
? { absolutePath: tablePath, importPath: `@fixture/${moduleId}/widgets/injection-table` }
|
|
84
|
+
: null) as unknown as ModuleScanContext['resolveModuleFile'],
|
|
85
|
+
resolveFirstModuleFile: (() => null) as unknown as ModuleScanContext['resolveFirstModuleFile'],
|
|
86
|
+
processStandaloneConfig: () => null,
|
|
87
|
+
sanitizeGeneratedModuleSpecifier: (importPath: string) => importPath,
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function runGenerate(contexts: ModuleScanContext[]): string[] {
|
|
92
|
+
const warn = jest.spyOn(console, 'warn').mockImplementation(() => {})
|
|
93
|
+
const extension = createInjectionWidgetsExtension()
|
|
94
|
+
for (const ctx of contexts) extension.scanModule(ctx)
|
|
95
|
+
extension.generateOutput()
|
|
96
|
+
return warn.mock.calls.map((call) => String(call[0]))
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
describe('injection widget id reconciliation at generate time', () => {
|
|
100
|
+
it('stays silent when every declared widget is referenced and vice versa', () => {
|
|
101
|
+
const warnings = runGenerate([makeModule('alpha', ['alpha.one', 'alpha.two'], ['alpha.one', 'alpha.two'])])
|
|
102
|
+
|
|
103
|
+
expect(warnings).toEqual([])
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('warns when an injection table references a widget id nothing declares', () => {
|
|
107
|
+
const warnings = runGenerate([makeModule('alpha', ['alpha.one'], ['alpha.one', 'alpha.typo'])])
|
|
108
|
+
|
|
109
|
+
expect(warnings).toHaveLength(1)
|
|
110
|
+
expect(warnings[0]).toContain('alpha.typo')
|
|
111
|
+
expect(warnings[0]).toContain('references widget id')
|
|
112
|
+
expect(warnings[0]).toContain('alpha')
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('stays silent about a declared widget that no table references', () => {
|
|
116
|
+
// The mirror check is deliberately not implemented: `extractInjectionTableWidgetIds` reads only
|
|
117
|
+
// statically-analyzable literals, so a table built by composition or computation exposes no ids
|
|
118
|
+
// and every widget in such a module would be reported as unreferenced. Pinned so the omission is
|
|
119
|
+
// a decision rather than an oversight.
|
|
120
|
+
const warnings = runGenerate([makeModule('alpha', ['alpha.one', 'alpha.orphan'], ['alpha.one'])])
|
|
121
|
+
|
|
122
|
+
expect(warnings).toEqual([])
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('reports only the unresolvable reference when the two sides disagree entirely', () => {
|
|
126
|
+
const warnings = runGenerate([makeModule('alpha', ['alpha.declared'], ['alpha.referenced'])])
|
|
127
|
+
|
|
128
|
+
expect(warnings).toHaveLength(1)
|
|
129
|
+
expect(warnings[0]).toContain('alpha.referenced')
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('does not warn about a table assembled by composition, whose ids are invisible statically', () => {
|
|
133
|
+
// Mirrors the real `example` module: `cond ? { ...a, ...b } : a`. The extractor sees no ids, so
|
|
134
|
+
// there is nothing to reconcile — and critically, no false warning.
|
|
135
|
+
const base = path.join(tmpRoot, 'composed')
|
|
136
|
+
fs.mkdirSync(path.join(base, 'widgets', 'injection', 'w0'), { recursive: true })
|
|
137
|
+
fs.writeFileSync(path.join(base, 'widgets', 'injection', 'w0', 'widget.ts'), widgetSource('composed.one'))
|
|
138
|
+
const tablePath = path.join(base, 'widgets', 'injection-table.ts')
|
|
139
|
+
fs.writeFileSync(
|
|
140
|
+
tablePath,
|
|
141
|
+
[
|
|
142
|
+
"import type { ModuleInjectionTable } from '@open-mercato/shared/modules/widgets/injection'",
|
|
143
|
+
"const a: ModuleInjectionTable = { 'detail:x:tab': [{ widgetId: 'composed.one' }] }",
|
|
144
|
+
"const b: ModuleInjectionTable = { 'detail:y:tab': [{ widgetId: 'composed.two' }] }",
|
|
145
|
+
'export const injectionTable: ModuleInjectionTable = process.env.FLAG ? { ...a, ...b } : a',
|
|
146
|
+
'export default injectionTable',
|
|
147
|
+
].join('\n'),
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
const ctx: ModuleScanContext = {
|
|
151
|
+
...makeModule('composed', [], []),
|
|
152
|
+
resolveModuleFile: (() => ({
|
|
153
|
+
absolutePath: tablePath,
|
|
154
|
+
importPath: '@fixture/composed/widgets/injection-table',
|
|
155
|
+
})) as unknown as ModuleScanContext['resolveModuleFile'],
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
expect(runGenerate([ctx])).toEqual([])
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
it('accepts a table in one module referencing a widget declared in another', () => {
|
|
162
|
+
const warnings = runGenerate([
|
|
163
|
+
makeModule('alpha', ['alpha.shared'], []),
|
|
164
|
+
makeModule('beta', [], ['alpha.shared']),
|
|
165
|
+
])
|
|
166
|
+
|
|
167
|
+
expect(warnings).toEqual([])
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it('does not warn for the single-widget id inference', () => {
|
|
171
|
+
// A lone widget whose metadata.id cannot be extracted adopts the table's single id; that
|
|
172
|
+
// convenience path must not then report itself as a mismatch.
|
|
173
|
+
const ctx = makeModule('gamma', [], ['gamma.inferred'])
|
|
174
|
+
const dir = path.join(tmpRoot, 'gamma', 'widgets', 'injection', 'solo')
|
|
175
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
176
|
+
fs.writeFileSync(path.join(dir, 'widget.ts'), 'export default { Widget: () => null }\n')
|
|
177
|
+
|
|
178
|
+
expect(runGenerate([ctx])).toEqual([])
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('stays silent for a module with no injection widgets and no table', () => {
|
|
182
|
+
expect(runGenerate([makeModule('delta', [], [])])).toEqual([])
|
|
183
|
+
})
|
|
184
|
+
})
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import os from 'node:os'
|
|
4
|
+
import { loadAppEnv } from '../load-env'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Regression: bin.ts must load the app's `.env` before initTelemetry(), so
|
|
8
|
+
* TELEMETRY_BACKEND set only in `.env` reaches worker and scheduler processes
|
|
9
|
+
* (ensureEnvLoaded in run() happens after init — too late).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
describe('loadAppEnv', () => {
|
|
13
|
+
const TEST_KEY = 'OM_LOAD_ENV_TEST_BACKEND'
|
|
14
|
+
let tmpDir: string
|
|
15
|
+
let cwd: string
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'load-env-'))
|
|
19
|
+
cwd = process.cwd()
|
|
20
|
+
process.chdir(tmpDir)
|
|
21
|
+
delete process.env[TEST_KEY]
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
process.chdir(cwd)
|
|
26
|
+
delete process.env[TEST_KEY]
|
|
27
|
+
fs.rmSync(tmpDir, { recursive: true, force: true })
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('loads .env from the app directory into process.env', async () => {
|
|
31
|
+
fs.writeFileSync(path.join(tmpDir, 'package.json'), '{"name":"env-test-app"}\n')
|
|
32
|
+
fs.mkdirSync(path.join(tmpDir, 'src'))
|
|
33
|
+
fs.writeFileSync(path.join(tmpDir, 'src', 'modules.ts'), 'export const enabledModules = []\n')
|
|
34
|
+
fs.writeFileSync(path.join(tmpDir, '.env'), `${TEST_KEY}=console\n`)
|
|
35
|
+
|
|
36
|
+
await loadAppEnv()
|
|
37
|
+
|
|
38
|
+
expect(process.env[TEST_KEY]).toBe('console')
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('does not override values already set in the real process env', async () => {
|
|
42
|
+
fs.writeFileSync(path.join(tmpDir, 'package.json'), '{"name":"env-test-app"}\n')
|
|
43
|
+
fs.writeFileSync(path.join(tmpDir, '.env'), `${TEST_KEY}=from-dotenv\n`)
|
|
44
|
+
process.env[TEST_KEY] = 'from-shell'
|
|
45
|
+
|
|
46
|
+
await loadAppEnv()
|
|
47
|
+
|
|
48
|
+
expect(process.env[TEST_KEY]).toBe('from-shell')
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('is a safe no-op when no .env exists', async () => {
|
|
52
|
+
fs.writeFileSync(path.join(tmpDir, 'package.json'), '{"name":"env-test-app"}\n')
|
|
53
|
+
await expect(loadAppEnv()).resolves.toBeUndefined()
|
|
54
|
+
expect(process.env[TEST_KEY]).toBeUndefined()
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('bin.ts awaits loadAppEnv() before initTelemetry() (the guarded ordering)', () => {
|
|
58
|
+
const binSource = fs.readFileSync(path.resolve(__dirname, '../../bin.ts'), 'utf8')
|
|
59
|
+
const loadEnvCall = binSource.indexOf('await loadAppEnv()')
|
|
60
|
+
const initTelemetryCall = binSource.indexOf('await initTelemetry()')
|
|
61
|
+
expect(loadEnvCall).toBeGreaterThan(-1)
|
|
62
|
+
expect(initTelemetryCall).toBeGreaterThan(-1)
|
|
63
|
+
expect(loadEnvCall).toBeLessThan(initTelemetryCall)
|
|
64
|
+
})
|
|
65
|
+
})
|
|
@@ -65,6 +65,39 @@ async function flushAsync(times = 5): Promise<void> {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
describe('startLazyWorkerSupervisor', () => {
|
|
68
|
+
it('accepts lightweight worker descriptors without importing handlers', async () => {
|
|
69
|
+
const spawnFn = jest.fn() as unknown as jest.MockedFunction<LazySupervisorSpawnFn>
|
|
70
|
+
const probeFn = jest.fn(async (queueName, strategy) =>
|
|
71
|
+
emptyProbe(queueName, strategy),
|
|
72
|
+
) as jest.MockedFunction<LazySupervisorProbeFn>
|
|
73
|
+
|
|
74
|
+
const handle = startLazyWorkerSupervisor({
|
|
75
|
+
mercatoBin: '/tmp/mercato',
|
|
76
|
+
appDir: '/tmp/app',
|
|
77
|
+
runtimeEnv: { ...process.env },
|
|
78
|
+
workers: [
|
|
79
|
+
{
|
|
80
|
+
id: 'events:workers:dispatch',
|
|
81
|
+
moduleId: 'events',
|
|
82
|
+
queue: 'events',
|
|
83
|
+
concurrency: 1,
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
pollMs: 250,
|
|
87
|
+
restartOnUnexpectedExit: true,
|
|
88
|
+
strategy: 'local',
|
|
89
|
+
spawnFn,
|
|
90
|
+
probeFn,
|
|
91
|
+
logger: silentLogger,
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
await flushAsync(20)
|
|
95
|
+
expect(probeFn).toHaveBeenCalledWith('events', 'local')
|
|
96
|
+
expect(spawnFn).not.toHaveBeenCalled()
|
|
97
|
+
|
|
98
|
+
await handle.close()
|
|
99
|
+
})
|
|
100
|
+
|
|
68
101
|
it('does not spawn any worker while every queue is idle', async () => {
|
|
69
102
|
const spawnFn = jest.fn() as unknown as jest.MockedFunction<LazySupervisorSpawnFn>
|
|
70
103
|
const probeFn = jest.fn(async (queueName, strategy) =>
|
|
@@ -67,6 +67,33 @@ if (enabledModules.some((entry) => entry.id === 'example')) {
|
|
|
67
67
|
)
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
function writeModulesConfigWithSupervisorOverrides(rootDir: string) {
|
|
71
|
+
const srcDir = path.join(rootDir, 'src')
|
|
72
|
+
fs.mkdirSync(srcDir, { recursive: true })
|
|
73
|
+
fs.writeFileSync(
|
|
74
|
+
path.join(srcDir, 'modules.ts'),
|
|
75
|
+
`
|
|
76
|
+
const supervisorPolicy = { workers: { 'events:dispatch': null } }
|
|
77
|
+
export const enabledModules = [
|
|
78
|
+
{
|
|
79
|
+
id: 'events',
|
|
80
|
+
from: '@open-mercato/events',
|
|
81
|
+
overrides: {
|
|
82
|
+
workers: {
|
|
83
|
+
'events:dispatch': null,
|
|
84
|
+
'events:replacement': { id: 'events:replacement', queue: 'events-v2', concurrency: 2, handler() {} },
|
|
85
|
+
},
|
|
86
|
+
cli: { start: null },
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
{ id: 'scheduler', overrides: supervisorPolicy },
|
|
90
|
+
{ id: 'queue', overrides: { ...supervisorPolicy } },
|
|
91
|
+
]
|
|
92
|
+
`,
|
|
93
|
+
'utf8',
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
70
97
|
describe('resolver enterprise module toggle', () => {
|
|
71
98
|
const originalEnv = process.env.OM_ENABLE_ENTERPRISE_MODULES
|
|
72
99
|
const originalResolverMarker = (globalThis as Record<string, unknown>).__resolver_evaluated__
|
|
@@ -140,4 +167,27 @@ describe('resolver enterprise module toggle', () => {
|
|
|
140
167
|
]),
|
|
141
168
|
)
|
|
142
169
|
})
|
|
170
|
+
|
|
171
|
+
it('marks inline worker and CLI overrides for the full dev bootstrap', () => {
|
|
172
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'resolver-supervisor-overrides-'))
|
|
173
|
+
writeModulesConfigWithSupervisorOverrides(tempDir)
|
|
174
|
+
|
|
175
|
+
expect(createResolver(tempDir).loadEnabledModules()).toEqual([
|
|
176
|
+
{
|
|
177
|
+
id: 'events',
|
|
178
|
+
from: '@open-mercato/events',
|
|
179
|
+
devSupervisorRequiresFullBootstrap: true,
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
id: 'scheduler',
|
|
183
|
+
from: '@open-mercato/core',
|
|
184
|
+
devSupervisorRequiresFullBootstrap: true,
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
id: 'queue',
|
|
188
|
+
from: '@open-mercato/core',
|
|
189
|
+
devSupervisorRequiresFullBootstrap: true,
|
|
190
|
+
},
|
|
191
|
+
])
|
|
192
|
+
})
|
|
143
193
|
})
|