@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
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
const DYNAMIC_SHARD_KEY = "__dynamic";
|
|
2
|
+
const ROOT_SHARD_KEY = "__root";
|
|
3
|
+
function normalizeRoutePath(kind, pathname) {
|
|
4
|
+
const segments = pathname.split("?")[0].split("/").filter(Boolean);
|
|
5
|
+
if (kind === "backend" && segments[0]?.toLowerCase() === "backend") {
|
|
6
|
+
segments.shift();
|
|
7
|
+
}
|
|
8
|
+
return segments;
|
|
9
|
+
}
|
|
10
|
+
function getRouteManifestShardKey(kind, pathname) {
|
|
11
|
+
const firstSegment = normalizeRoutePath(kind, pathname)[0];
|
|
12
|
+
if (!firstSegment) return ROOT_SHARD_KEY;
|
|
13
|
+
if (firstSegment.startsWith("[") || firstSegment.startsWith(":") || firstSegment === "*") {
|
|
14
|
+
return DYNAMIC_SHARD_KEY;
|
|
15
|
+
}
|
|
16
|
+
return firstSegment.toLowerCase();
|
|
17
|
+
}
|
|
18
|
+
function renderArray(entries) {
|
|
19
|
+
if (entries.length === 0) return "[]";
|
|
20
|
+
return `[
|
|
21
|
+
${entries.map((entry) => ` ${entry.declaration}`).join(",\n")}
|
|
22
|
+
]`;
|
|
23
|
+
}
|
|
24
|
+
function uniqueImports(entries) {
|
|
25
|
+
return Array.from(new Set(entries.flatMap((entry) => entry.imports ?? [])));
|
|
26
|
+
}
|
|
27
|
+
function shardStem(kind, index, key) {
|
|
28
|
+
const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
29
|
+
return `${kind}-route-shard.${String(index).padStart(3, "0")}.${safeKey}.generated`;
|
|
30
|
+
}
|
|
31
|
+
function renderShardFile(kind, entries) {
|
|
32
|
+
const typeName = kind === "api" ? "ApiRouteManifestEntry" : "BackendRouteManifestEntry";
|
|
33
|
+
const helperImport = kind === "backend" ? "resolvePageRouteMetadata, " : "";
|
|
34
|
+
const imports = uniqueImports(entries);
|
|
35
|
+
return `// AUTO-GENERATED by mercato generate registry (request-scoped route shard)
|
|
36
|
+
import { ${helperImport}type ${typeName} } from '@open-mercato/shared/modules/registry'
|
|
37
|
+
${imports.length ? `${imports.join("\n")}
|
|
38
|
+
` : ""}
|
|
39
|
+
export const routes: ${typeName}[] = ${renderArray(entries)}
|
|
40
|
+
export const routeOrders = [${entries.map((entry) => entry.order).join(", ")}]
|
|
41
|
+
|
|
42
|
+
export default routes
|
|
43
|
+
`;
|
|
44
|
+
}
|
|
45
|
+
function renderLoaderFile(kind, shards) {
|
|
46
|
+
const isApi = kind === "api";
|
|
47
|
+
const typeName = kind === "api" ? "ApiRouteManifestEntry" : "BackendRouteManifestEntry";
|
|
48
|
+
const functionName = kind === "api" ? "loadApiRouteManifestShard" : "loadBackendRouteManifestShard";
|
|
49
|
+
const ordinalFunctionName = isApi ? "loadApiRouteManifestByOrdinal" : "loadBackendRouteManifestByOrdinal";
|
|
50
|
+
const facadeExportName = isApi ? "apiRouteFacades" : "backendRouteFacades";
|
|
51
|
+
const metadataExportName = isApi ? "apiRouteMetadata" : "backendRouteMetadata";
|
|
52
|
+
const metadataFileName = isApi ? "api-route-metadata.generated" : "backend-route-metadata.generated";
|
|
53
|
+
const shardCases = shards.map(({ key, stem }) => ` case ${JSON.stringify(key)}:
|
|
54
|
+
return import('./${stem}').then((entry) => ({ routes: entry.routes, routeOrders: entry.routeOrders }))`).join("\n");
|
|
55
|
+
const hasDynamicShard = shards.some(({ key }) => key === DYNAMIC_SHARD_KEY);
|
|
56
|
+
const routeShardKeys = [];
|
|
57
|
+
for (const shard of shards) {
|
|
58
|
+
for (const order of shard.orders) routeShardKeys[order] = shard.key;
|
|
59
|
+
}
|
|
60
|
+
const prefixNormalization = kind === "backend" ? ` if (segments[0]?.toLowerCase() === 'backend') segments.shift()
|
|
61
|
+
` : "";
|
|
62
|
+
const loadDynamic = hasDynamicShard ? ` const dynamicShard = await loadShard(${JSON.stringify(DYNAMIC_SHARD_KEY)})
|
|
63
|
+
` : ` const dynamicShard: LoadedRouteShard = EMPTY_SHARD
|
|
64
|
+
`;
|
|
65
|
+
return `// AUTO-GENERATED by mercato generate registry (request-scoped route loader)
|
|
66
|
+
import type { ${typeName} } from '@open-mercato/shared/modules/registry'
|
|
67
|
+
import { ${metadataExportName} } from './${metadataFileName}'
|
|
68
|
+
|
|
69
|
+
type LoadedRouteShard = { routes: ${typeName}[]; routeOrders: number[] }
|
|
70
|
+
const EMPTY_SHARD: LoadedRouteShard = { routes: [], routeOrders: [] }
|
|
71
|
+
const ROUTE_SHARD_KEYS = ${JSON.stringify(routeShardKeys)}
|
|
72
|
+
|
|
73
|
+
function resolveShardKey(pathname: string): string {
|
|
74
|
+
const segments = pathname.split('?')[0].split('/').filter(Boolean)
|
|
75
|
+
${prefixNormalization} return segments[0]?.toLowerCase() ?? ${JSON.stringify(ROOT_SHARD_KEY)}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function loadShard(key: string): Promise<LoadedRouteShard> {
|
|
79
|
+
switch (key) {
|
|
80
|
+
${shardCases ? `${shardCases}
|
|
81
|
+
` : ""} default:
|
|
82
|
+
return EMPTY_SHARD
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function ${ordinalFunctionName}(ordinal: number): Promise<${typeName}> {
|
|
87
|
+
const shardKey = ROUTE_SHARD_KEYS[ordinal]
|
|
88
|
+
if (shardKey === undefined) throw new Error('Unknown generated route manifest ordinal: ' + ordinal)
|
|
89
|
+
const shard = await loadShard(shardKey)
|
|
90
|
+
const index = shard.routeOrders.indexOf(ordinal)
|
|
91
|
+
if (index < 0 || !shard.routes[index]) throw new Error('Generated route manifest ordinal is missing from its shard: ' + ordinal)
|
|
92
|
+
return shard.routes[index]
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const ${facadeExportName}: ${typeName}[] = ${metadataExportName}.map((metadata, ordinal) => ({
|
|
96
|
+
...metadata,
|
|
97
|
+
load: async () => {
|
|
98
|
+
const route = await ${ordinalFunctionName}(ordinal)
|
|
99
|
+
return route.load()
|
|
100
|
+
},
|
|
101
|
+
}))
|
|
102
|
+
|
|
103
|
+
export async function ${functionName}(pathname: string): Promise<${typeName}[]> {
|
|
104
|
+
const exactShard = await loadShard(resolveShardKey(pathname))
|
|
105
|
+
${loadDynamic} return [
|
|
106
|
+
...exactShard.routes.map((route, index) => ({ route, order: exactShard.routeOrders[index] })),
|
|
107
|
+
...dynamicShard.routes.map((route, index) => ({ route, order: dynamicShard.routeOrders[index] })),
|
|
108
|
+
].sort((a, b) => a.order - b.order).map(({ route }) => route)
|
|
109
|
+
}
|
|
110
|
+
`;
|
|
111
|
+
}
|
|
112
|
+
function renderRouteManifestShardOutputs(kind, entries) {
|
|
113
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
114
|
+
for (const [order, entry] of entries.entries()) {
|
|
115
|
+
const key = getRouteManifestShardKey(kind, entry.path);
|
|
116
|
+
const existing = grouped.get(key);
|
|
117
|
+
const indexedEntry = { ...entry, order };
|
|
118
|
+
if (existing) existing.push(indexedEntry);
|
|
119
|
+
else grouped.set(key, [indexedEntry]);
|
|
120
|
+
}
|
|
121
|
+
const sortedGroups = Array.from(grouped.entries()).sort(([a], [b]) => a.localeCompare(b));
|
|
122
|
+
const shards = sortedGroups.map(([key, shardEntries], index) => {
|
|
123
|
+
const stem = shardStem(kind, index, key);
|
|
124
|
+
return {
|
|
125
|
+
key,
|
|
126
|
+
stem,
|
|
127
|
+
orders: shardEntries.map((entry) => entry.order),
|
|
128
|
+
output: {
|
|
129
|
+
fileName: `${stem}.ts`,
|
|
130
|
+
content: renderShardFile(kind, shardEntries)
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
});
|
|
134
|
+
const loaderFileName = kind === "api" ? "api-route-shards.generated.ts" : "backend-route-shards.generated.ts";
|
|
135
|
+
return [
|
|
136
|
+
...shards.map(({ output }) => output),
|
|
137
|
+
{
|
|
138
|
+
fileName: loaderFileName,
|
|
139
|
+
content: renderLoaderFile(kind, shards)
|
|
140
|
+
}
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
function renderRouteMetadataOutput(kind, entries) {
|
|
144
|
+
const isApi = kind === "api";
|
|
145
|
+
const manifestType = isApi ? "ApiRouteManifestEntry" : "BackendRouteManifestEntry";
|
|
146
|
+
const metadataType = isApi ? "ApiRouteMetadataEntry" : "BackendRouteMetadataEntry";
|
|
147
|
+
const exportName = isApi ? "apiRouteMetadata" : "backendRouteMetadata";
|
|
148
|
+
const fileName = isApi ? "api-route-metadata.generated.ts" : "backend-route-metadata.generated.ts";
|
|
149
|
+
const helperImport = isApi ? "" : "resolvePageRouteMetadata, ";
|
|
150
|
+
const imports = uniqueImports(entries);
|
|
151
|
+
const content = `// AUTO-GENERATED by mercato generate registry (route metadata without component loaders)
|
|
152
|
+
import { ${helperImport}type ${manifestType} } from '@open-mercato/shared/modules/registry'
|
|
153
|
+
${imports.length ? `${imports.join("\n")}
|
|
154
|
+
` : ""}
|
|
155
|
+
export type ${metadataType} = Omit<${manifestType}, 'load'>
|
|
156
|
+
|
|
157
|
+
export const ${exportName}: ${metadataType}[] = ${renderArray(entries)}
|
|
158
|
+
|
|
159
|
+
export default ${exportName}
|
|
160
|
+
`;
|
|
161
|
+
return { fileName, content };
|
|
162
|
+
}
|
|
163
|
+
const routeManifestShardFilePattern = /^(?:api|backend)-route-shard\..+\.generated\.(?:ts|checksum)$/;
|
|
164
|
+
export {
|
|
165
|
+
getRouteManifestShardKey,
|
|
166
|
+
renderRouteManifestShardOutputs,
|
|
167
|
+
renderRouteMetadataOutput,
|
|
168
|
+
routeManifestShardFilePattern
|
|
169
|
+
};
|
|
170
|
+
//# sourceMappingURL=route-manifest-shards.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/lib/generators/route-manifest-shards.ts"],
|
|
4
|
+
"sourcesContent": ["export type RouteManifestShardKind = 'api' | 'backend'\n\nexport type RouteManifestShardEntry = {\n path: string\n declaration: string\n imports?: string[]\n}\n\nexport type RouteManifestShardOutput = {\n fileName: string\n content: string\n}\n\ntype IndexedRouteManifestShardEntry = RouteManifestShardEntry & {\n order: number\n}\n\nconst DYNAMIC_SHARD_KEY = '__dynamic'\nconst ROOT_SHARD_KEY = '__root'\n\nfunction normalizeRoutePath(kind: RouteManifestShardKind, pathname: string): string[] {\n const segments = pathname.split('?')[0].split('/').filter(Boolean)\n if (kind === 'backend' && segments[0]?.toLowerCase() === 'backend') {\n segments.shift()\n }\n return segments\n}\n\nexport function getRouteManifestShardKey(kind: RouteManifestShardKind, pathname: string): string {\n const firstSegment = normalizeRoutePath(kind, pathname)[0]\n if (!firstSegment) return ROOT_SHARD_KEY\n if (\n firstSegment.startsWith('[')\n || firstSegment.startsWith(':')\n || firstSegment === '*'\n ) {\n return DYNAMIC_SHARD_KEY\n }\n return firstSegment.toLowerCase()\n}\n\nfunction renderArray(entries: RouteManifestShardEntry[]): string {\n if (entries.length === 0) return '[]'\n return `[\\n${entries.map((entry) => ` ${entry.declaration}`).join(',\\n')}\\n]`\n}\n\nfunction uniqueImports(entries: RouteManifestShardEntry[]): string[] {\n return Array.from(new Set(entries.flatMap((entry) => entry.imports ?? [])))\n}\n\nfunction shardStem(kind: RouteManifestShardKind, index: number, key: string): string {\n const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, '_')\n return `${kind}-route-shard.${String(index).padStart(3, '0')}.${safeKey}.generated`\n}\n\nfunction renderShardFile(kind: RouteManifestShardKind, entries: IndexedRouteManifestShardEntry[]): string {\n const typeName = kind === 'api' ? 'ApiRouteManifestEntry' : 'BackendRouteManifestEntry'\n const helperImport = kind === 'backend' ? 'resolvePageRouteMetadata, ' : ''\n const imports = uniqueImports(entries)\n return `// AUTO-GENERATED by mercato generate registry (request-scoped route shard)\\nimport { ${helperImport}type ${typeName} } from '@open-mercato/shared/modules/registry'\\n${imports.length ? `${imports.join('\\n')}\\n` : ''}\\nexport const routes: ${typeName}[] = ${renderArray(entries)}\\nexport const routeOrders = [${entries.map((entry) => entry.order).join(', ')}]\\n\\nexport default routes\\n`\n}\n\nfunction renderLoaderFile(\n kind: RouteManifestShardKind,\n shards: Array<{ key: string; stem: string; orders: number[] }>,\n): string {\n const isApi = kind === 'api'\n const typeName = kind === 'api' ? 'ApiRouteManifestEntry' : 'BackendRouteManifestEntry'\n const functionName = kind === 'api' ? 'loadApiRouteManifestShard' : 'loadBackendRouteManifestShard'\n const ordinalFunctionName = isApi ? 'loadApiRouteManifestByOrdinal' : 'loadBackendRouteManifestByOrdinal'\n const facadeExportName = isApi ? 'apiRouteFacades' : 'backendRouteFacades'\n const metadataExportName = isApi ? 'apiRouteMetadata' : 'backendRouteMetadata'\n const metadataFileName = isApi ? 'api-route-metadata.generated' : 'backend-route-metadata.generated'\n const shardCases = shards\n .map(({ key, stem }) => ` case ${JSON.stringify(key)}:\\n return import('./${stem}').then((entry) => ({ routes: entry.routes, routeOrders: entry.routeOrders }))`)\n .join('\\n')\n const hasDynamicShard = shards.some(({ key }) => key === DYNAMIC_SHARD_KEY)\n const routeShardKeys: string[] = []\n for (const shard of shards) {\n for (const order of shard.orders) routeShardKeys[order] = shard.key\n }\n const prefixNormalization = kind === 'backend'\n ? ` if (segments[0]?.toLowerCase() === 'backend') segments.shift()\\n`\n : ''\n const loadDynamic = hasDynamicShard\n ? ` const dynamicShard = await loadShard(${JSON.stringify(DYNAMIC_SHARD_KEY)})\\n`\n : ` const dynamicShard: LoadedRouteShard = EMPTY_SHARD\\n`\n\n return `// AUTO-GENERATED by mercato generate registry (request-scoped route loader)\\nimport type { ${typeName} } from '@open-mercato/shared/modules/registry'\\nimport { ${metadataExportName} } from './${metadataFileName}'\\n\\ntype LoadedRouteShard = { routes: ${typeName}[]; routeOrders: number[] }\\nconst EMPTY_SHARD: LoadedRouteShard = { routes: [], routeOrders: [] }\\nconst ROUTE_SHARD_KEYS = ${JSON.stringify(routeShardKeys)}\\n\\nfunction resolveShardKey(pathname: string): string {\\n const segments = pathname.split('?')[0].split('/').filter(Boolean)\\n${prefixNormalization} return segments[0]?.toLowerCase() ?? ${JSON.stringify(ROOT_SHARD_KEY)}\\n}\\n\\nasync function loadShard(key: string): Promise<LoadedRouteShard> {\\n switch (key) {\\n${shardCases ? `${shardCases}\\n` : ''} default:\\n return EMPTY_SHARD\\n }\\n}\\n\\nexport async function ${ordinalFunctionName}(ordinal: number): Promise<${typeName}> {\\n const shardKey = ROUTE_SHARD_KEYS[ordinal]\\n if (shardKey === undefined) throw new Error('Unknown generated route manifest ordinal: ' + ordinal)\\n const shard = await loadShard(shardKey)\\n const index = shard.routeOrders.indexOf(ordinal)\\n if (index < 0 || !shard.routes[index]) throw new Error('Generated route manifest ordinal is missing from its shard: ' + ordinal)\\n return shard.routes[index]\\n}\\n\\nexport const ${facadeExportName}: ${typeName}[] = ${metadataExportName}.map((metadata, ordinal) => ({\\n ...metadata,\\n load: async () => {\\n const route = await ${ordinalFunctionName}(ordinal)\\n return route.load()\\n },\\n}))\\n\\nexport async function ${functionName}(pathname: string): Promise<${typeName}[]> {\\n const exactShard = await loadShard(resolveShardKey(pathname))\\n${loadDynamic} return [\\n ...exactShard.routes.map((route, index) => ({ route, order: exactShard.routeOrders[index] })),\\n ...dynamicShard.routes.map((route, index) => ({ route, order: dynamicShard.routeOrders[index] })),\\n ].sort((a, b) => a.order - b.order).map(({ route }) => route)\\n}\\n`\n}\n\nexport function renderRouteManifestShardOutputs(\n kind: RouteManifestShardKind,\n entries: RouteManifestShardEntry[],\n): RouteManifestShardOutput[] {\n const grouped = new Map<string, IndexedRouteManifestShardEntry[]>()\n for (const [order, entry] of entries.entries()) {\n const key = getRouteManifestShardKey(kind, entry.path)\n const existing = grouped.get(key)\n const indexedEntry = { ...entry, order }\n if (existing) existing.push(indexedEntry)\n else grouped.set(key, [indexedEntry])\n }\n\n const sortedGroups = Array.from(grouped.entries()).sort(([a], [b]) => a.localeCompare(b))\n const shards = sortedGroups.map(([key, shardEntries], index) => {\n const stem = shardStem(kind, index, key)\n return {\n key,\n stem,\n orders: shardEntries.map((entry) => entry.order),\n output: {\n fileName: `${stem}.ts`,\n content: renderShardFile(kind, shardEntries),\n },\n }\n })\n const loaderFileName = kind === 'api'\n ? 'api-route-shards.generated.ts'\n : 'backend-route-shards.generated.ts'\n\n return [\n ...shards.map(({ output }) => output),\n {\n fileName: loaderFileName,\n content: renderLoaderFile(kind, shards),\n },\n ]\n}\n\nexport function renderRouteMetadataOutput(\n kind: RouteManifestShardKind,\n entries: RouteManifestShardEntry[],\n): RouteManifestShardOutput {\n const isApi = kind === 'api'\n const manifestType = isApi ? 'ApiRouteManifestEntry' : 'BackendRouteManifestEntry'\n const metadataType = isApi ? 'ApiRouteMetadataEntry' : 'BackendRouteMetadataEntry'\n const exportName = isApi ? 'apiRouteMetadata' : 'backendRouteMetadata'\n const fileName = isApi ? 'api-route-metadata.generated.ts' : 'backend-route-metadata.generated.ts'\n const helperImport = isApi ? '' : 'resolvePageRouteMetadata, '\n const imports = uniqueImports(entries)\n const content = `// AUTO-GENERATED by mercato generate registry (route metadata without component loaders)\\nimport { ${helperImport}type ${manifestType} } from '@open-mercato/shared/modules/registry'\\n${imports.length ? `${imports.join('\\n')}\\n` : ''}\\nexport type ${metadataType} = Omit<${manifestType}, 'load'>\\n\\nexport const ${exportName}: ${metadataType}[] = ${renderArray(entries)}\\n\\nexport default ${exportName}\\n`\n return { fileName, content }\n}\n\nexport const routeManifestShardFilePattern = /^(?:api|backend)-route-shard\\..+\\.generated\\.(?:ts|checksum)$/\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -54,12 +54,21 @@ function startInProcessGenerateWatcher(options) {
|
|
|
54
54
|
if (stopping) return;
|
|
55
55
|
try {
|
|
56
56
|
const changeVersion = changeSignal?.currentVersion() ?? 0;
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
const eventGatedIdle = Boolean(
|
|
58
|
+
changeSignal && !changeSignal.usesPollingFallback() && changeVersion === observedChangeVersion
|
|
59
|
+
);
|
|
60
|
+
let refreshedSkippedTargets = false;
|
|
61
|
+
if (eventGatedIdle) {
|
|
62
|
+
if (!changeSignal?.hasSkippedTargets?.()) return;
|
|
63
|
+
await changeSignal.refresh();
|
|
64
|
+
refreshedSkippedTargets = true;
|
|
65
|
+
if (changeSignal.hasSkippedTargets?.()) return;
|
|
59
66
|
}
|
|
60
67
|
const nextChecksum = await computeStructureChecksum();
|
|
61
68
|
observedChangeVersion = changeVersion;
|
|
62
|
-
|
|
69
|
+
if (!refreshedSkippedTargets) {
|
|
70
|
+
await changeSignal?.refresh();
|
|
71
|
+
}
|
|
63
72
|
if (nextChecksum !== previousChecksum) {
|
|
64
73
|
previousChecksum = nextChecksum;
|
|
65
74
|
await runOnce("structure change");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/in-process-generate-watcher.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * In-process generate watcher.\n *\n * Coalesces filesystem events on a configurable interval, then verifies them\n * with the existing structural fingerprint before invoking the generator.\n * If filesystem watching is unavailable, it falls back to fingerprint polling.\n * The polling loop is identical to the legacy standalone watcher; the only\n * difference is that it now runs inside whatever process calls it\n * (typically `mercato server dev`), so the dev runtime no longer needs a\n * sidecar `mercato generate watch` Node process.\n *\n * Contract preserved from the prior standalone watcher:\n * - Default debounce/fallback poll interval 1000 ms (minimum 250 ms).\n * - One initial generator run unless `skipInitial` is true.\n * - Concurrent regeneration requests are coalesced (running + pending).\n * - Generator errors are logged but never crash the watcher.\n * - The polling timer uses `.unref()` so it never blocks process exit.\n */\n\nexport type GenerateWatcherLogger = Pick<Console, 'log' | 'error'>\n\nexport type GenerateWatcherChangeSignal = {\n /** Monotonic event generation. Changes indicate that a checksum may be stale. */\n currentVersion(): number\n /** Refresh filesystem subscriptions after module configuration changes. */\n refresh(): Promise<void> | void\n /** When true, preserve the legacy full-checksum-on-every-poll behavior. */\n usesPollingFallback(): boolean\n /** Close filesystem subscriptions. Idempotent. */\n close(): Promise<void> | void\n}\n\nexport type GenerateWatcherOptions = {\n /**\n * Function that returns the current structural fingerprint of the module\n * tree. The watcher re-runs `runGenerators` whenever this value changes.\n */\n computeStructureChecksum: () => Promise<string> | string\n /** Optional event signal that avoids full checksum work during idle polls. */\n changeSignal?: GenerateWatcherChangeSignal\n /**\n * Function that performs the actual regeneration work. Called once on\n * startup (unless `skipInitial`) and again whenever the checksum changes.\n * The `reason` argument is suitable for logging (`'initial'`,\n * `'structure change'`, `'queued change'`).\n */\n runGenerators: (reason: string) => Promise<void>\n /** Poll interval in milliseconds. Defaults to 1000. Clamped to >= 250. */\n pollMs?: number\n /** Skip the initial regeneration on startup. Defaults to false. */\n skipInitial?: boolean\n /** Suppress informational logs. Errors are always logged. */\n quiet?: boolean\n /** Logger override. Defaults to `console`. */\n logger?: GenerateWatcherLogger\n}\n\nexport type GenerateWatcherHandle = {\n /** Resolves when the watcher loop has stopped (after `close()`). */\n readonly done: Promise<void>\n /** Stop the polling loop and resolve `done`. Idempotent. */\n close(): Promise<void>\n}\n\nconst MIN_POLL_MS = 250\nconst DEFAULT_POLL_MS = 1000\n\nfunction resolvePollMs(value: number | undefined): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) return DEFAULT_POLL_MS\n const numeric = Math.floor(value)\n return numeric >= MIN_POLL_MS ? numeric : DEFAULT_POLL_MS\n}\n\n/**\n * Start an in-process generate watcher. The returned handle exposes a\n * `close()` to stop polling and a `done` promise that resolves once the\n * watcher loop has finished.\n */\nexport function startInProcessGenerateWatcher(\n options: GenerateWatcherOptions,\n): GenerateWatcherHandle {\n const logger = options.logger ?? console\n const quiet = options.quiet === true\n const pollMs = resolvePollMs(options.pollMs)\n const skipInitial = options.skipInitial === true\n const { changeSignal, computeStructureChecksum, runGenerators } = options\n\n let stopping = false\n let pollTimer: ReturnType<typeof setTimeout> | null = null\n let running = false\n let pendingReason: string | null = null\n let previousChecksum = ''\n let observedChangeVersion = -1\n let doneResolve: (() => void) | null = null\n const done = new Promise<void>((resolve) => {\n doneResolve = resolve\n })\n\n async function runOnce(reason: string): Promise<void> {\n if (running) {\n pendingReason = reason\n return\n }\n running = true\n try {\n if (!quiet) {\n logger.log(`[generate:watch] Regenerating (${reason})...`)\n }\n await runGenerators(reason)\n if (!quiet) {\n logger.log('[generate:watch] Generators completed.')\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n logger.error(`[generate:watch] Generation failed: ${message}`)\n } finally {\n running = false\n if (pendingReason && !stopping) {\n const queued = pendingReason\n pendingReason = null\n await runOnce(queued)\n }\n }\n }\n\n function scheduleNext(): void {\n if (stopping) return\n pollTimer = setTimeout(() => {\n void (async () => {\n if (stopping) return\n try {\n const changeVersion = changeSignal?.currentVersion() ?? 0\n
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["/**\n * In-process generate watcher.\n *\n * Coalesces filesystem events on a configurable interval, then verifies them\n * with the existing structural fingerprint before invoking the generator.\n * If filesystem watching is unavailable, it falls back to fingerprint polling.\n * The polling loop is identical to the legacy standalone watcher; the only\n * difference is that it now runs inside whatever process calls it\n * (typically `mercato server dev`), so the dev runtime no longer needs a\n * sidecar `mercato generate watch` Node process.\n *\n * Contract preserved from the prior standalone watcher:\n * - Default debounce/fallback poll interval 1000 ms (minimum 250 ms).\n * - One initial generator run unless `skipInitial` is true.\n * - Concurrent regeneration requests are coalesced (running + pending).\n * - Generator errors are logged but never crash the watcher.\n * - The polling timer uses `.unref()` so it never blocks process exit.\n */\n\nexport type GenerateWatcherLogger = Pick<Console, 'log' | 'error'>\n\nexport type GenerateWatcherChangeSignal = {\n /** Monotonic event generation. Changes indicate that a checksum may be stale. */\n currentVersion(): number\n /** Refresh filesystem subscriptions after module configuration changes. */\n refresh(): Promise<void> | void\n /** Whether configured roots are missing and should be retried on idle polls. */\n hasSkippedTargets?(): boolean\n /** When true, preserve the legacy full-checksum-on-every-poll behavior. */\n usesPollingFallback(): boolean\n /** Close filesystem subscriptions. Idempotent. */\n close(): Promise<void> | void\n}\n\nexport type GenerateWatcherOptions = {\n /**\n * Function that returns the current structural fingerprint of the module\n * tree. The watcher re-runs `runGenerators` whenever this value changes.\n */\n computeStructureChecksum: () => Promise<string> | string\n /** Optional event signal that avoids full checksum work during idle polls. */\n changeSignal?: GenerateWatcherChangeSignal\n /**\n * Function that performs the actual regeneration work. Called once on\n * startup (unless `skipInitial`) and again whenever the checksum changes.\n * The `reason` argument is suitable for logging (`'initial'`,\n * `'structure change'`, `'queued change'`).\n */\n runGenerators: (reason: string) => Promise<void>\n /** Poll interval in milliseconds. Defaults to 1000. Clamped to >= 250. */\n pollMs?: number\n /** Skip the initial regeneration on startup. Defaults to false. */\n skipInitial?: boolean\n /** Suppress informational logs. Errors are always logged. */\n quiet?: boolean\n /** Logger override. Defaults to `console`. */\n logger?: GenerateWatcherLogger\n}\n\nexport type GenerateWatcherHandle = {\n /** Resolves when the watcher loop has stopped (after `close()`). */\n readonly done: Promise<void>\n /** Stop the polling loop and resolve `done`. Idempotent. */\n close(): Promise<void>\n}\n\nconst MIN_POLL_MS = 250\nconst DEFAULT_POLL_MS = 1000\n\nfunction resolvePollMs(value: number | undefined): number {\n if (typeof value !== 'number' || !Number.isFinite(value)) return DEFAULT_POLL_MS\n const numeric = Math.floor(value)\n return numeric >= MIN_POLL_MS ? numeric : DEFAULT_POLL_MS\n}\n\n/**\n * Start an in-process generate watcher. The returned handle exposes a\n * `close()` to stop polling and a `done` promise that resolves once the\n * watcher loop has finished.\n */\nexport function startInProcessGenerateWatcher(\n options: GenerateWatcherOptions,\n): GenerateWatcherHandle {\n const logger = options.logger ?? console\n const quiet = options.quiet === true\n const pollMs = resolvePollMs(options.pollMs)\n const skipInitial = options.skipInitial === true\n const { changeSignal, computeStructureChecksum, runGenerators } = options\n\n let stopping = false\n let pollTimer: ReturnType<typeof setTimeout> | null = null\n let running = false\n let pendingReason: string | null = null\n let previousChecksum = ''\n let observedChangeVersion = -1\n let doneResolve: (() => void) | null = null\n const done = new Promise<void>((resolve) => {\n doneResolve = resolve\n })\n\n async function runOnce(reason: string): Promise<void> {\n if (running) {\n pendingReason = reason\n return\n }\n running = true\n try {\n if (!quiet) {\n logger.log(`[generate:watch] Regenerating (${reason})...`)\n }\n await runGenerators(reason)\n if (!quiet) {\n logger.log('[generate:watch] Generators completed.')\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n logger.error(`[generate:watch] Generation failed: ${message}`)\n } finally {\n running = false\n if (pendingReason && !stopping) {\n const queued = pendingReason\n pendingReason = null\n await runOnce(queued)\n }\n }\n }\n\n function scheduleNext(): void {\n if (stopping) return\n pollTimer = setTimeout(() => {\n void (async () => {\n if (stopping) return\n try {\n const changeVersion = changeSignal?.currentVersion() ?? 0\n const eventGatedIdle = Boolean(\n changeSignal\n && !changeSignal.usesPollingFallback()\n && changeVersion === observedChangeVersion\n )\n let refreshedSkippedTargets = false\n if (eventGatedIdle) {\n if (!changeSignal?.hasSkippedTargets?.()) return\n await changeSignal.refresh()\n refreshedSkippedTargets = true\n if (changeSignal.hasSkippedTargets?.()) return\n }\n const nextChecksum = await computeStructureChecksum()\n observedChangeVersion = changeVersion\n if (!refreshedSkippedTargets) {\n await changeSignal?.refresh()\n }\n if (nextChecksum !== previousChecksum) {\n previousChecksum = nextChecksum\n await runOnce('structure change')\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n logger.error(`[generate:watch] Poll cycle failed: ${message}`)\n } finally {\n if (!stopping) scheduleNext()\n }\n })()\n }, pollMs)\n pollTimer.unref?.()\n }\n\n void (async () => {\n try {\n await changeSignal?.refresh()\n const initialChangeVersion = changeSignal?.currentVersion() ?? 0\n if (!skipInitial) {\n await runOnce('initial')\n }\n previousChecksum = await computeStructureChecksum()\n observedChangeVersion = initialChangeVersion\n if (!quiet) {\n if (skipInitial) {\n logger.log('[generate:watch] Skipping initial regeneration and watching the current generated state.')\n }\n logger.log(`[generate:watch] Watching structural module files with a ${pollMs}ms debounce (in-process).`)\n }\n scheduleNext()\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n logger.error(`[generate:watch] Initial setup failed: ${message}`)\n // Even if the initial bootstrap fails, schedule polling so a later\n // checksum recovery still picks up the next change on disk.\n scheduleNext()\n }\n })()\n\n async function close(): Promise<void> {\n if (stopping) {\n await done\n return\n }\n stopping = true\n if (pollTimer) {\n clearTimeout(pollTimer)\n pollTimer = null\n }\n await changeSignal?.close()\n if (doneResolve) {\n doneResolve()\n doneResolve = null\n }\n }\n\n return {\n done,\n close,\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
async function loadAppEnv(options = {}) {
|
|
4
|
+
try {
|
|
5
|
+
const { createResolver } = await import("./resolver.js");
|
|
6
|
+
const appDir = createResolver(options.cwd).getAppDir();
|
|
7
|
+
const envPath = path.join(appDir, ".env");
|
|
8
|
+
if (fs.existsSync(envPath)) {
|
|
9
|
+
const dotenv = await import("dotenv");
|
|
10
|
+
dotenv.config({ path: envPath, quiet: true });
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
} catch {
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const dotenv = await import("dotenv");
|
|
17
|
+
dotenv.config({ quiet: true });
|
|
18
|
+
} catch {
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export {
|
|
22
|
+
loadAppEnv
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=load-env.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/lib/load-env.ts"],
|
|
4
|
+
"sourcesContent": ["import fs from 'node:fs'\nimport path from 'node:path'\n\n/**\n * Bootstrap-free `.env` loader for the CLI binary. `bin.ts` must load the app's\n * `.env` BEFORE `initTelemetry()` so `TELEMETRY_*` set only in `.env` resolves a\n * real backend for worker/scheduler processes \u2014 `run()`'s `ensureEnvLoaded()`\n * happens after telemetry init, which is too late. Deliberately read-only (no\n * `.env.example` copy \u2014 that UX stays in `ensureEnvLoaded`) and pulls in only the\n * resolver + dotenv, so nothing here loads `pg` and the OTEL instrumentation\n * load-order guarantee is preserved.\n */\nexport async function loadAppEnv(options: { cwd?: string } = {}): Promise<void> {\n try {\n const { createResolver } = await import('./resolver.js')\n const appDir = createResolver(options.cwd).getAppDir()\n const envPath = path.join(appDir, '.env')\n if (fs.existsSync(envPath)) {\n const dotenv = await import('dotenv')\n dotenv.config({ path: envPath, quiet: true })\n return\n }\n } catch {\n // Resolver can fail outside an app dir \u2014 fall through to plain cwd loading.\n }\n try {\n const dotenv = await import('dotenv')\n dotenv.config({ quiet: true })\n } catch {}\n}\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AAWjB,eAAsB,WAAW,UAA4B,CAAC,GAAkB;AAC9E,MAAI;AACF,UAAM,EAAE,eAAe,IAAI,MAAM,OAAO,eAAe;AACvD,UAAM,SAAS,eAAe,QAAQ,GAAG,EAAE,UAAU;AACrD,UAAM,UAAU,KAAK,KAAK,QAAQ,MAAM;AACxC,QAAI,GAAG,WAAW,OAAO,GAAG;AAC1B,YAAM,SAAS,MAAM,OAAO,QAAQ;AACpC,aAAO,OAAO,EAAE,MAAM,SAAS,OAAO,KAAK,CAAC;AAC5C;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AACA,MAAI;AACF,UAAM,SAAS,MAAM,OAAO,QAAQ;AACpC,WAAO,OAAO,EAAE,OAAO,KAAK,CAAC;AAAA,EAC/B,QAAQ;AAAA,EAAC;AACX;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/queue-worker-supervisor.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Lazy queue worker supervisor.\n *\n * Replaces `mercato queue worker --all` with a lightweight watcher that\n * starts a per-queue worker process only after the first ready job appears\n * on that queue. Discovered queues with no jobs stay completely idle.\n *\n * Design:\n * - Probes every queue on a configurable interval using\n * `getQueuePendingProbe()` from `@open-mercato/queue`.\n * - The first non-error probe with `ready > 0` triggers `spawn('node', [\n * mercatoBin, 'queue', 'worker', queueName ])`.\n * - Each spawned child inherits stdio so its logs flow through the\n * surrounding dev runtime.\n * - On unexpected exit while jobs are still pending, the supervisor\n * restarts the worker if `restartOnUnexpectedExit` is enabled.\n * - On `close()`, all active children receive SIGTERM and the supervisor\n * waits for them to exit before resolving.\n *\n * Probes never invoke handlers, never create BullMQ Workers, and never\n * import generated worker handler modules. The contract is preserved by\n * `getQueuePendingProbe()`.\n */\n\nimport { spawn as nodeSpawn } from 'node:child_process'\nimport type { ChildProcess, SpawnOptions } from 'node:child_process'\nimport {\n getQueuePendingProbe,\n type QueuePendingProbeResult,\n type QueueStrategyType,\n} from '@open-mercato/queue'\nimport type { ModuleWorker } from '@open-mercato/shared/modules/registry'\nimport type { LazyWorkerSpawnMode } from './auto-spawn-workers'\n\nexport type LazySupervisorSpawnFn = (\n command: string,\n args: readonly string[],\n options: SpawnOptions,\n) => ChildProcess\n\nexport type LazySupervisorProbeFn = (\n queueName: string,\n strategy: QueueStrategyType,\n) => Promise<QueuePendingProbeResult>\n\nexport type LazyWorkerSupervisorOptions = {\n mercatoBin: string\n appDir: string\n runtimeEnv: NodeJS.ProcessEnv\n workers: ModuleWorker[]\n pollMs: number\n restartOnUnexpectedExit: boolean\n spawnMode?: LazyWorkerSpawnMode\n strategy?: QueueStrategyType\n /** Start the shared worker even with no ready queue jobs (for an embedded scheduler). */\n shouldStartSharedWorker?: () => Promise<boolean>\n /** Restart the shared worker with no ready queue jobs after an unexpected exit. */\n shouldRestartSharedWorker?: () => Promise<boolean>\n /** Extra arguments for the shared worker command only. */\n sharedWorkerArgs?: readonly string[]\n /** Override for tests. Defaults to `child_process.spawn`. */\n spawnFn?: LazySupervisorSpawnFn\n /** Override for tests. Defaults to `getQueuePendingProbe`. */\n probeFn?: LazySupervisorProbeFn\n /** Logger override. Defaults to `console`. */\n logger?: Pick<Console, 'log' | 'warn' | 'error'>\n onSpawn?: (queueName: string, child: ChildProcess) => void\n onChildExit?: (\n queueName: string,\n code: number | null,\n signal: NodeJS.Signals | null,\n ) => void\n}\n\nexport type LazyWorkerSupervisorHandle = {\n /** Set of queue names whose worker has started at least once. */\n readonly startedQueues: ReadonlySet<string>\n /** Currently running child process per queue (if any). */\n getActiveChild(queueName: string): ChildProcess | undefined\n /** Stop polling, kill children, wait for exit. Idempotent. */\n close(): Promise<void>\n /** Resolves when the watcher loop has stopped (after `close()`). */\n done: Promise<void>\n}\n\ntype QueueGroup = {\n queueName: string\n concurrency: number\n workerCount: number\n}\n\nfunction groupWorkersByQueue(workers: ModuleWorker[]): QueueGroup[] {\n const groups = new Map<string, QueueGroup>()\n for (const worker of workers) {\n const existing = groups.get(worker.queue)\n if (existing) {\n existing.workerCount += 1\n if (worker.concurrency > existing.concurrency) {\n existing.concurrency = worker.concurrency\n }\n } else {\n groups.set(worker.queue, {\n queueName: worker.queue,\n concurrency: Math.max(worker.concurrency, 1),\n workerCount: 1,\n })\n }\n }\n return [...groups.values()].sort((a, b) => a.queueName.localeCompare(b.queueName))\n}\n\nfunction resolveStrategyFromEnv(env: NodeJS.ProcessEnv): QueueStrategyType {\n return env.QUEUE_STRATEGY === 'async' ? 'async' : 'local'\n}\n\nconst PROBE_ERROR_LOG_THROTTLE_MS = 60_000\n\nexport function startLazyWorkerSupervisor(\n options: LazyWorkerSupervisorOptions,\n): LazyWorkerSupervisorHandle {\n const logger = options.logger ?? console\n const strategy: QueueStrategyType = options.strategy ?? resolveStrategyFromEnv(options.runtimeEnv)\n const spawnMode: LazyWorkerSpawnMode = options.spawnMode ?? 'per-queue'\n const probe: LazySupervisorProbeFn = options.probeFn\n ?? ((queueName) => getQueuePendingProbe(queueName, strategy))\n const groups = groupWorkersByQueue(options.workers)\n\n const startedQueues = new Set<string>()\n const activeChildren = new Map<string, ChildProcess>()\n const startingQueues = new Set<string>()\n let activeSharedChild: ChildProcess | undefined\n let startingSharedWorker = false\n const probeErrorLastLoggedAt = new Map<string, number>()\n\n let stopping = false\n let pollTimer: ReturnType<typeof setTimeout> | null = null\n let watchPromiseResolve: (() => void) | null = null\n const done = new Promise<void>((resolve) => {\n watchPromiseResolve = resolve\n })\n\n if (groups.length === 0) {\n logger.warn(\n '[lazy-supervisor] No queues discovered. Idle until queues are registered or run `yarn generate`.',\n )\n } else {\n logger.log(\n `[lazy-supervisor] Watching ${groups.length} queue(s): ${groups\n .map((g) => g.queueName)\n .join(', ')}`,\n )\n }\n\n const spawnFn: LazySupervisorSpawnFn =\n options.spawnFn ?? ((command, args, opts) => nodeSpawn(command, args as string[], opts))\n\n const markSharedWorkerStarted = () => {\n for (const group of groups) {\n startedQueues.add(group.queueName)\n activeChildren.set(group.queueName, activeSharedChild as ChildProcess)\n }\n }\n\n const clearSharedWorker = () => {\n for (const group of groups) {\n if (activeChildren.get(group.queueName) === activeSharedChild) {\n activeChildren.delete(group.queueName)\n }\n }\n activeSharedChild = undefined\n }\n\n function logProbeError(queueName: string, message: string): void {\n const now = Date.now()\n const lastAt = probeErrorLastLoggedAt.get(queueName) ?? 0\n if (now - lastAt < PROBE_ERROR_LOG_THROTTLE_MS) return\n probeErrorLastLoggedAt.set(queueName, now)\n logger.warn(`[lazy-supervisor] Probe failed for queue \"${queueName}\": ${message}`)\n }\n\n function spawnQueueWorker(queueName: string): void {\n if (stopping) return\n if (activeChildren.has(queueName)) return\n if (startingQueues.has(queueName)) return\n startingQueues.add(queueName)\n\n logger.log(`[lazy-supervisor] Pending job detected \u2014 starting worker for queue \"${queueName}\"`)\n let child: ChildProcess\n try {\n child = spawnFn(\n 'node',\n [options.mercatoBin, 'queue', 'worker', queueName],\n {\n stdio: 'inherit',\n env: options.runtimeEnv,\n cwd: options.appDir,\n },\n )\n } catch (err) {\n startingQueues.delete(queueName)\n const message = err instanceof Error ? err.message : String(err)\n logger.error(`[lazy-supervisor] Failed to spawn worker for \"${queueName}\": ${message}`)\n return\n }\n\n activeChildren.set(queueName, child)\n startedQueues.add(queueName)\n startingQueues.delete(queueName)\n if (options.onSpawn) {\n try {\n options.onSpawn(queueName, child)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n logger.warn(`[lazy-supervisor] onSpawn callback threw for \"${queueName}\": ${message}`)\n }\n }\n\n child.on('exit', (code, signal) => {\n activeChildren.delete(queueName)\n if (options.onChildExit) {\n try {\n options.onChildExit(queueName, code, signal)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n logger.warn(`[lazy-supervisor] onChildExit callback threw for \"${queueName}\": ${message}`)\n }\n }\n if (stopping) return\n const expected = signal === 'SIGTERM' || signal === 'SIGINT'\n if (expected) return\n const reason = code !== null ? `code ${code}` : `signal ${signal ?? 'unknown'}`\n logger.warn(`[lazy-supervisor] Worker for \"${queueName}\" exited (${reason}).`)\n if (!options.restartOnUnexpectedExit) return\n void (async () => {\n const result = await safeProbe(queueName)\n if (!result || result.error) return\n if (result.ready > 0) {\n logger.warn(`[lazy-supervisor] Restarting worker for \"${queueName}\" because jobs remain pending.`)\n spawnQueueWorker(queueName)\n }\n })()\n })\n }\n\n function spawnSharedWorker(triggerQueueNames: string[], schedulerTriggered = false): void {\n if (stopping) return\n if (activeSharedChild) return\n if (startingSharedWorker) return\n startingSharedWorker = true\n\n if (schedulerTriggered) {\n logger.log('[lazy-supervisor] Enabled schedule detected \u2014 starting shared worker for all queues')\n } else {\n const triggerSummary = triggerQueueNames.length > 0\n ? ` in ${triggerQueueNames.join(', ')}`\n : ''\n logger.log(`[lazy-supervisor] Pending job detected${triggerSummary} \u2014 starting shared worker for all queues`)\n }\n let child: ChildProcess\n try {\n child = spawnFn(\n 'node',\n [options.mercatoBin, 'queue', 'worker', '--all', ...(options.sharedWorkerArgs ?? [])],\n {\n stdio: 'inherit',\n env: options.runtimeEnv,\n cwd: options.appDir,\n },\n )\n } catch (err) {\n startingSharedWorker = false\n const message = err instanceof Error ? err.message : String(err)\n logger.error(`[lazy-supervisor] Failed to spawn shared worker: ${message}`)\n return\n }\n\n activeSharedChild = child\n markSharedWorkerStarted()\n startingSharedWorker = false\n for (const group of groups) {\n if (options.onSpawn) {\n try {\n options.onSpawn(group.queueName, child)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n logger.warn(`[lazy-supervisor] onSpawn callback threw for \"${group.queueName}\": ${message}`)\n }\n }\n }\n\n child.on('exit', (code, signal) => {\n clearSharedWorker()\n for (const group of groups) {\n if (options.onChildExit) {\n try {\n options.onChildExit(group.queueName, code, signal)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n logger.warn(`[lazy-supervisor] onChildExit callback threw for \"${group.queueName}\": ${message}`)\n }\n }\n }\n if (stopping) return\n const expected = signal === 'SIGTERM' || signal === 'SIGINT'\n if (expected) return\n const reason = code !== null ? `code ${code}` : `signal ${signal ?? 'unknown'}`\n logger.warn(`[lazy-supervisor] Shared worker exited (${reason}).`)\n if (!options.restartOnUnexpectedExit) return\n void (async () => {\n const readyQueues: string[] = []\n for (const group of groups) {\n const result = await safeProbe(group.queueName)\n if (!result || result.error) continue\n if (result.ready > 0) readyQueues.push(group.queueName)\n }\n if (readyQueues.length > 0) {\n logger.warn('[lazy-supervisor] Restarting shared worker because jobs remain pending.')\n spawnSharedWorker(readyQueues)\n return\n }\n if (options.shouldRestartSharedWorker) {\n try {\n if (await options.shouldRestartSharedWorker()) {\n logger.warn('[lazy-supervisor] Restarting shared worker because an enabled schedule remains.')\n spawnSharedWorker([], true)\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n logger.warn(`[lazy-supervisor] Shared-worker restart condition failed: ${message}`)\n }\n }\n })()\n })\n }\n\n async function safeProbe(queueName: string): Promise<QueuePendingProbeResult | null> {\n try {\n return await probe(queueName, strategy)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n logProbeError(queueName, message)\n return null\n }\n }\n\n async function tickOnce(): Promise<void> {\n if (stopping) return\n if (groups.length === 0) return\n if (spawnMode === 'shared' && (activeSharedChild || startingSharedWorker)) return\n\n const toCheck = groups.filter((g) => !activeChildren.has(g.queueName) && !startingQueues.has(g.queueName))\n if (toCheck.length === 0) return\n\n const probes = await Promise.all(\n toCheck.map(async (group) => ({\n group,\n result: await safeProbe(group.queueName),\n })),\n )\n\n if (stopping) return\n\n const readySharedQueues: string[] = []\n for (const { group, result } of probes) {\n if (!result) continue\n if (result.error) {\n if (result.errorMessage) {\n logProbeError(group.queueName, result.errorMessage)\n }\n continue\n }\n if (result.ready > 0) {\n if (spawnMode === 'shared') {\n readySharedQueues.push(group.queueName)\n continue\n }\n spawnQueueWorker(group.queueName)\n }\n }\n if (spawnMode === 'shared') {\n if (readySharedQueues.length > 0) {\n spawnSharedWorker(readySharedQueues)\n return\n }\n if (options.shouldStartSharedWorker) {\n try {\n if (await options.shouldStartSharedWorker()) {\n spawnSharedWorker([], true)\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n logger.warn(`[lazy-supervisor] Shared-worker start condition failed: ${message}`)\n }\n }\n }\n }\n\n function scheduleNext(): void {\n if (stopping) return\n pollTimer = setTimeout(() => {\n void (async () => {\n try {\n await tickOnce()\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n logger.warn(`[lazy-supervisor] Poll cycle failed: ${message}`)\n } finally {\n if (!stopping) scheduleNext()\n }\n })()\n }, options.pollMs)\n pollTimer.unref?.()\n }\n\n // Kick off immediately so the first probe doesn't wait one full poll interval.\n void (async () => {\n try {\n await tickOnce()\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n logger.warn(`[lazy-supervisor] Initial poll failed: ${message}`)\n } finally {\n scheduleNext()\n }\n })()\n\n async function close(): Promise<void> {\n if (stopping) {\n await done\n return\n }\n stopping = true\n if (pollTimer) {\n clearTimeout(pollTimer)\n pollTimer = null\n }\n\n const exitWaiters: Promise<void>[] = []\n for (const [queueName, child] of activeChildren) {\n exitWaiters.push(\n new Promise<void>((resolve) => {\n if (child.exitCode !== null || child.signalCode !== null) return resolve()\n child.once('exit', () => resolve())\n }),\n )\n try {\n if (!child.killed) child.kill('SIGTERM')\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n logger.warn(`[lazy-supervisor] Failed to send SIGTERM to \"${queueName}\": ${message}`)\n }\n }\n await Promise.all(exitWaiters)\n activeChildren.clear()\n if (watchPromiseResolve) {\n watchPromiseResolve()\n watchPromiseResolve = null\n }\n }\n\n return {\n startedQueues,\n getActiveChild: (queueName) => activeChildren.get(queueName),\n close,\n done,\n }\n}\n"],
|
|
5
|
-
"mappings": "AAwBA,SAAS,SAAS,iBAAiB;AAEnC;AAAA,EACE;AAAA,OAGK;
|
|
4
|
+
"sourcesContent": ["/**\n * Lazy queue worker supervisor.\n *\n * Replaces `mercato queue worker --all` with a lightweight watcher that\n * starts a per-queue worker process only after the first ready job appears\n * on that queue. Discovered queues with no jobs stay completely idle.\n *\n * Design:\n * - Probes every queue on a configurable interval using\n * `getQueuePendingProbe()` from `@open-mercato/queue`.\n * - The first non-error probe with `ready > 0` triggers `spawn('node', [\n * mercatoBin, 'queue', 'worker', queueName ])`.\n * - Each spawned child inherits stdio so its logs flow through the\n * surrounding dev runtime.\n * - On unexpected exit while jobs are still pending, the supervisor\n * restarts the worker if `restartOnUnexpectedExit` is enabled.\n * - On `close()`, all active children receive SIGTERM and the supervisor\n * waits for them to exit before resolving.\n *\n * Probes never invoke handlers, never create BullMQ Workers, and never\n * import generated worker handler modules. The contract is preserved by\n * `getQueuePendingProbe()`.\n */\n\nimport { spawn as nodeSpawn } from 'node:child_process'\nimport type { ChildProcess, SpawnOptions } from 'node:child_process'\nimport {\n getQueuePendingProbe,\n type QueuePendingProbeResult,\n type QueueStrategyType,\n} from '@open-mercato/queue'\nimport type { ModuleWorker } from '@open-mercato/shared/modules/registry'\nimport type { LazyWorkerSpawnMode } from './auto-spawn-workers'\n\nexport type SupervisorWorkerDescriptor = Pick<\n ModuleWorker,\n 'id' | 'moduleId' | 'queue' | 'concurrency'\n>\n\nexport type LazySupervisorSpawnFn = (\n command: string,\n args: readonly string[],\n options: SpawnOptions,\n) => ChildProcess\n\nexport type LazySupervisorProbeFn = (\n queueName: string,\n strategy: QueueStrategyType,\n) => Promise<QueuePendingProbeResult>\n\nexport type LazyWorkerSupervisorOptions = {\n mercatoBin: string\n appDir: string\n runtimeEnv: NodeJS.ProcessEnv\n workers: readonly SupervisorWorkerDescriptor[]\n pollMs: number\n restartOnUnexpectedExit: boolean\n spawnMode?: LazyWorkerSpawnMode\n strategy?: QueueStrategyType\n /** Start the shared worker even with no ready queue jobs (for an embedded scheduler). */\n shouldStartSharedWorker?: () => Promise<boolean>\n /** Restart the shared worker with no ready queue jobs after an unexpected exit. */\n shouldRestartSharedWorker?: () => Promise<boolean>\n /** Extra arguments for the shared worker command only. */\n sharedWorkerArgs?: readonly string[]\n /** Override for tests. Defaults to `child_process.spawn`. */\n spawnFn?: LazySupervisorSpawnFn\n /** Override for tests. Defaults to `getQueuePendingProbe`. */\n probeFn?: LazySupervisorProbeFn\n /** Logger override. Defaults to `console`. */\n logger?: Pick<Console, 'log' | 'warn' | 'error'>\n onSpawn?: (queueName: string, child: ChildProcess) => void\n onChildExit?: (\n queueName: string,\n code: number | null,\n signal: NodeJS.Signals | null,\n ) => void\n}\n\nexport type LazyWorkerSupervisorHandle = {\n /** Set of queue names whose worker has started at least once. */\n readonly startedQueues: ReadonlySet<string>\n /** Currently running child process per queue (if any). */\n getActiveChild(queueName: string): ChildProcess | undefined\n /** Stop polling, kill children, wait for exit. Idempotent. */\n close(): Promise<void>\n /** Resolves when the watcher loop has stopped (after `close()`). */\n done: Promise<void>\n}\n\ntype QueueGroup = {\n queueName: string\n concurrency: number\n workerCount: number\n}\n\nfunction groupWorkersByQueue(workers: readonly SupervisorWorkerDescriptor[]): QueueGroup[] {\n const groups = new Map<string, QueueGroup>()\n for (const worker of workers) {\n const existing = groups.get(worker.queue)\n if (existing) {\n existing.workerCount += 1\n if (worker.concurrency > existing.concurrency) {\n existing.concurrency = worker.concurrency\n }\n } else {\n groups.set(worker.queue, {\n queueName: worker.queue,\n concurrency: Math.max(worker.concurrency, 1),\n workerCount: 1,\n })\n }\n }\n return [...groups.values()].sort((a, b) => a.queueName.localeCompare(b.queueName))\n}\n\nfunction resolveStrategyFromEnv(env: NodeJS.ProcessEnv): QueueStrategyType {\n return env.QUEUE_STRATEGY === 'async' ? 'async' : 'local'\n}\n\nconst PROBE_ERROR_LOG_THROTTLE_MS = 60_000\n\nexport function startLazyWorkerSupervisor(\n options: LazyWorkerSupervisorOptions,\n): LazyWorkerSupervisorHandle {\n const logger = options.logger ?? console\n const strategy: QueueStrategyType = options.strategy ?? resolveStrategyFromEnv(options.runtimeEnv)\n const spawnMode: LazyWorkerSpawnMode = options.spawnMode ?? 'per-queue'\n const probe: LazySupervisorProbeFn = options.probeFn\n ?? ((queueName) => getQueuePendingProbe(queueName, strategy))\n const groups = groupWorkersByQueue(options.workers)\n\n const startedQueues = new Set<string>()\n const activeChildren = new Map<string, ChildProcess>()\n const startingQueues = new Set<string>()\n let activeSharedChild: ChildProcess | undefined\n let startingSharedWorker = false\n const probeErrorLastLoggedAt = new Map<string, number>()\n\n let stopping = false\n let pollTimer: ReturnType<typeof setTimeout> | null = null\n let watchPromiseResolve: (() => void) | null = null\n const done = new Promise<void>((resolve) => {\n watchPromiseResolve = resolve\n })\n\n if (groups.length === 0) {\n logger.warn(\n '[lazy-supervisor] No queues discovered. Idle until queues are registered or run `yarn generate`.',\n )\n } else {\n logger.log(\n `[lazy-supervisor] Watching ${groups.length} queue(s): ${groups\n .map((g) => g.queueName)\n .join(', ')}`,\n )\n }\n\n const spawnFn: LazySupervisorSpawnFn =\n options.spawnFn ?? ((command, args, opts) => nodeSpawn(command, args as string[], opts))\n\n const markSharedWorkerStarted = () => {\n for (const group of groups) {\n startedQueues.add(group.queueName)\n activeChildren.set(group.queueName, activeSharedChild as ChildProcess)\n }\n }\n\n const clearSharedWorker = () => {\n for (const group of groups) {\n if (activeChildren.get(group.queueName) === activeSharedChild) {\n activeChildren.delete(group.queueName)\n }\n }\n activeSharedChild = undefined\n }\n\n function logProbeError(queueName: string, message: string): void {\n const now = Date.now()\n const lastAt = probeErrorLastLoggedAt.get(queueName) ?? 0\n if (now - lastAt < PROBE_ERROR_LOG_THROTTLE_MS) return\n probeErrorLastLoggedAt.set(queueName, now)\n logger.warn(`[lazy-supervisor] Probe failed for queue \"${queueName}\": ${message}`)\n }\n\n function spawnQueueWorker(queueName: string): void {\n if (stopping) return\n if (activeChildren.has(queueName)) return\n if (startingQueues.has(queueName)) return\n startingQueues.add(queueName)\n\n logger.log(`[lazy-supervisor] Pending job detected \u2014 starting worker for queue \"${queueName}\"`)\n let child: ChildProcess\n try {\n child = spawnFn(\n 'node',\n [options.mercatoBin, 'queue', 'worker', queueName],\n {\n stdio: 'inherit',\n env: options.runtimeEnv,\n cwd: options.appDir,\n },\n )\n } catch (err) {\n startingQueues.delete(queueName)\n const message = err instanceof Error ? err.message : String(err)\n logger.error(`[lazy-supervisor] Failed to spawn worker for \"${queueName}\": ${message}`)\n return\n }\n\n activeChildren.set(queueName, child)\n startedQueues.add(queueName)\n startingQueues.delete(queueName)\n if (options.onSpawn) {\n try {\n options.onSpawn(queueName, child)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n logger.warn(`[lazy-supervisor] onSpawn callback threw for \"${queueName}\": ${message}`)\n }\n }\n\n child.on('exit', (code, signal) => {\n activeChildren.delete(queueName)\n if (options.onChildExit) {\n try {\n options.onChildExit(queueName, code, signal)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n logger.warn(`[lazy-supervisor] onChildExit callback threw for \"${queueName}\": ${message}`)\n }\n }\n if (stopping) return\n const expected = signal === 'SIGTERM' || signal === 'SIGINT'\n if (expected) return\n const reason = code !== null ? `code ${code}` : `signal ${signal ?? 'unknown'}`\n logger.warn(`[lazy-supervisor] Worker for \"${queueName}\" exited (${reason}).`)\n if (!options.restartOnUnexpectedExit) return\n void (async () => {\n const result = await safeProbe(queueName)\n if (!result || result.error) return\n if (result.ready > 0) {\n logger.warn(`[lazy-supervisor] Restarting worker for \"${queueName}\" because jobs remain pending.`)\n spawnQueueWorker(queueName)\n }\n })()\n })\n }\n\n function spawnSharedWorker(triggerQueueNames: string[], schedulerTriggered = false): void {\n if (stopping) return\n if (activeSharedChild) return\n if (startingSharedWorker) return\n startingSharedWorker = true\n\n if (schedulerTriggered) {\n logger.log('[lazy-supervisor] Enabled schedule detected \u2014 starting shared worker for all queues')\n } else {\n const triggerSummary = triggerQueueNames.length > 0\n ? ` in ${triggerQueueNames.join(', ')}`\n : ''\n logger.log(`[lazy-supervisor] Pending job detected${triggerSummary} \u2014 starting shared worker for all queues`)\n }\n let child: ChildProcess\n try {\n child = spawnFn(\n 'node',\n [options.mercatoBin, 'queue', 'worker', '--all', ...(options.sharedWorkerArgs ?? [])],\n {\n stdio: 'inherit',\n env: options.runtimeEnv,\n cwd: options.appDir,\n },\n )\n } catch (err) {\n startingSharedWorker = false\n const message = err instanceof Error ? err.message : String(err)\n logger.error(`[lazy-supervisor] Failed to spawn shared worker: ${message}`)\n return\n }\n\n activeSharedChild = child\n markSharedWorkerStarted()\n startingSharedWorker = false\n for (const group of groups) {\n if (options.onSpawn) {\n try {\n options.onSpawn(group.queueName, child)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n logger.warn(`[lazy-supervisor] onSpawn callback threw for \"${group.queueName}\": ${message}`)\n }\n }\n }\n\n child.on('exit', (code, signal) => {\n clearSharedWorker()\n for (const group of groups) {\n if (options.onChildExit) {\n try {\n options.onChildExit(group.queueName, code, signal)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n logger.warn(`[lazy-supervisor] onChildExit callback threw for \"${group.queueName}\": ${message}`)\n }\n }\n }\n if (stopping) return\n const expected = signal === 'SIGTERM' || signal === 'SIGINT'\n if (expected) return\n const reason = code !== null ? `code ${code}` : `signal ${signal ?? 'unknown'}`\n logger.warn(`[lazy-supervisor] Shared worker exited (${reason}).`)\n if (!options.restartOnUnexpectedExit) return\n void (async () => {\n const readyQueues: string[] = []\n for (const group of groups) {\n const result = await safeProbe(group.queueName)\n if (!result || result.error) continue\n if (result.ready > 0) readyQueues.push(group.queueName)\n }\n if (readyQueues.length > 0) {\n logger.warn('[lazy-supervisor] Restarting shared worker because jobs remain pending.')\n spawnSharedWorker(readyQueues)\n return\n }\n if (options.shouldRestartSharedWorker) {\n try {\n if (await options.shouldRestartSharedWorker()) {\n logger.warn('[lazy-supervisor] Restarting shared worker because an enabled schedule remains.')\n spawnSharedWorker([], true)\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n logger.warn(`[lazy-supervisor] Shared-worker restart condition failed: ${message}`)\n }\n }\n })()\n })\n }\n\n async function safeProbe(queueName: string): Promise<QueuePendingProbeResult | null> {\n try {\n return await probe(queueName, strategy)\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n logProbeError(queueName, message)\n return null\n }\n }\n\n async function tickOnce(): Promise<void> {\n if (stopping) return\n if (groups.length === 0) return\n if (spawnMode === 'shared' && (activeSharedChild || startingSharedWorker)) return\n\n const toCheck = groups.filter((g) => !activeChildren.has(g.queueName) && !startingQueues.has(g.queueName))\n if (toCheck.length === 0) return\n\n const probes = await Promise.all(\n toCheck.map(async (group) => ({\n group,\n result: await safeProbe(group.queueName),\n })),\n )\n\n if (stopping) return\n\n const readySharedQueues: string[] = []\n for (const { group, result } of probes) {\n if (!result) continue\n if (result.error) {\n if (result.errorMessage) {\n logProbeError(group.queueName, result.errorMessage)\n }\n continue\n }\n if (result.ready > 0) {\n if (spawnMode === 'shared') {\n readySharedQueues.push(group.queueName)\n continue\n }\n spawnQueueWorker(group.queueName)\n }\n }\n if (spawnMode === 'shared') {\n if (readySharedQueues.length > 0) {\n spawnSharedWorker(readySharedQueues)\n return\n }\n if (options.shouldStartSharedWorker) {\n try {\n if (await options.shouldStartSharedWorker()) {\n spawnSharedWorker([], true)\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n logger.warn(`[lazy-supervisor] Shared-worker start condition failed: ${message}`)\n }\n }\n }\n }\n\n function scheduleNext(): void {\n if (stopping) return\n pollTimer = setTimeout(() => {\n void (async () => {\n try {\n await tickOnce()\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n logger.warn(`[lazy-supervisor] Poll cycle failed: ${message}`)\n } finally {\n if (!stopping) scheduleNext()\n }\n })()\n }, options.pollMs)\n pollTimer.unref?.()\n }\n\n // Kick off immediately so the first probe doesn't wait one full poll interval.\n void (async () => {\n try {\n await tickOnce()\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n logger.warn(`[lazy-supervisor] Initial poll failed: ${message}`)\n } finally {\n scheduleNext()\n }\n })()\n\n async function close(): Promise<void> {\n if (stopping) {\n await done\n return\n }\n stopping = true\n if (pollTimer) {\n clearTimeout(pollTimer)\n pollTimer = null\n }\n\n const exitWaiters: Promise<void>[] = []\n for (const [queueName, child] of activeChildren) {\n exitWaiters.push(\n new Promise<void>((resolve) => {\n if (child.exitCode !== null || child.signalCode !== null) return resolve()\n child.once('exit', () => resolve())\n }),\n )\n try {\n if (!child.killed) child.kill('SIGTERM')\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err)\n logger.warn(`[lazy-supervisor] Failed to send SIGTERM to \"${queueName}\": ${message}`)\n }\n }\n await Promise.all(exitWaiters)\n activeChildren.clear()\n if (watchPromiseResolve) {\n watchPromiseResolve()\n watchPromiseResolve = null\n }\n }\n\n return {\n startedQueues,\n getActiveChild: (queueName) => activeChildren.get(queueName),\n close,\n done,\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/lib/resolver.js
CHANGED
|
@@ -36,18 +36,41 @@ function pkgRootFor(rootDir, from, isMonorepo = true) {
|
|
|
36
36
|
function parseModuleEntryFromObjectLiteral(node) {
|
|
37
37
|
let id = null;
|
|
38
38
|
let from = null;
|
|
39
|
+
let devSupervisorRequiresFullBootstrap = false;
|
|
39
40
|
for (const property of node.properties) {
|
|
40
|
-
if (!ts.isPropertyAssignment(property)
|
|
41
|
-
const key = property.name.text;
|
|
41
|
+
if (!ts.isPropertyAssignment(property)) continue;
|
|
42
|
+
const key = ts.isIdentifier(property.name) || ts.isStringLiteralLike(property.name) ? property.name.text : null;
|
|
42
43
|
if (key === "id" && ts.isStringLiteralLike(property.initializer)) {
|
|
43
44
|
id = property.initializer.text;
|
|
44
45
|
}
|
|
45
46
|
if (key === "from" && ts.isStringLiteralLike(property.initializer)) {
|
|
46
47
|
from = property.initializer.text;
|
|
47
48
|
}
|
|
49
|
+
if (key === "overrides") {
|
|
50
|
+
if (!ts.isObjectLiteralExpression(property.initializer)) {
|
|
51
|
+
devSupervisorRequiresFullBootstrap = true;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
for (const overrideProperty of property.initializer.properties) {
|
|
55
|
+
if (ts.isSpreadAssignment(overrideProperty)) {
|
|
56
|
+
devSupervisorRequiresFullBootstrap = true;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
if (!ts.isPropertyAssignment(overrideProperty)) continue;
|
|
60
|
+
const overrideKey = ts.isIdentifier(overrideProperty.name) || ts.isStringLiteralLike(overrideProperty.name) ? overrideProperty.name.text : null;
|
|
61
|
+
if (overrideKey === "workers" || overrideKey === "cli") {
|
|
62
|
+
devSupervisorRequiresFullBootstrap = true;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
48
67
|
}
|
|
49
68
|
if (!id) return null;
|
|
50
|
-
return {
|
|
69
|
+
return {
|
|
70
|
+
id,
|
|
71
|
+
from: from ?? "@open-mercato/core",
|
|
72
|
+
...devSupervisorRequiresFullBootstrap ? { devSupervisorRequiresFullBootstrap: true } : {}
|
|
73
|
+
};
|
|
51
74
|
}
|
|
52
75
|
function parseProcessEnvAccess(node, env) {
|
|
53
76
|
if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.name)) {
|
|
@@ -238,6 +261,7 @@ function discoverPackagesInMonorepo(rootDir) {
|
|
|
238
261
|
if (fs.existsSync(modulesPath)) {
|
|
239
262
|
packages.push({
|
|
240
263
|
name: pkgJson.name || `@open-mercato/${entry.name}`,
|
|
264
|
+
version: typeof pkgJson.version === "string" ? pkgJson.version : null,
|
|
241
265
|
path: pkgPath,
|
|
242
266
|
modulesPath
|
|
243
267
|
});
|
|
@@ -265,6 +289,7 @@ function discoverPackagesInNodeModules(rootDir) {
|
|
|
265
289
|
if (fs.existsSync(modulesPath)) {
|
|
266
290
|
packages.push({
|
|
267
291
|
name: pkgJson.name || `@open-mercato/${entry.name}`,
|
|
292
|
+
version: typeof pkgJson.version === "string" ? pkgJson.version : null,
|
|
268
293
|
path: pkgPath,
|
|
269
294
|
modulesPath
|
|
270
295
|
});
|