@starci/hfs 4.7.32 → 4.7.33
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/CHANGELOG.md +9 -0
- package/README.md +13 -6
- package/emit/contracts.mjs +4 -3
- package/emit/form-bounds-reader.ts.partial +58 -0
- package/emit/form-bounds.mjs +155 -0
- package/emit/graphql-input-bounds.mjs +97 -0
- package/emit/input-bounds.mjs +44 -0
- package/emit/operation-schemas.mjs +137 -0
- package/emit/operations-worker.mjs +2 -2
- package/emit/operations.mjs +52 -16
- package/emit/schema-worker.mjs +17 -4
- package/emit/type-schema.mjs +17 -5
- package/package.json +9 -2
- package/release/gate.mjs +18 -23
- package/release/index.mjs +1 -1
- package/release/push.mjs +4 -5
- package/runtime/engine/admission.mjs +331 -0
- package/runtime/engine/canonical-json.mjs +14 -0
- package/runtime/engine/db/blob.mjs +380 -0
- package/runtime/engine/db/event-compact.mjs +73 -0
- package/runtime/engine/db/ledger-open.mjs +38 -0
- package/runtime/engine/db/ledger-paths.mjs +87 -0
- package/runtime/engine/db/ledger.mjs +1068 -0
- package/runtime/engine/db/machine-connection.mjs +150 -0
- package/runtime/engine/db/machine-open.mjs +60 -0
- package/runtime/engine/db/machine-schema.mjs +105 -0
- package/runtime/engine/db/machine.mjs +1317 -0
- package/runtime/engine/db/provider-reservations.mjs +127 -0
- package/runtime/engine/db/provision-authority.mjs +76 -0
- package/runtime/engine/db/provision-lock.mjs +29 -0
- package/runtime/engine/db/provision-operations.mjs +146 -0
- package/runtime/engine/db/ref-value.mjs +67 -0
- package/runtime/engine/db/schema/machine.sql +920 -0
- package/runtime/engine/db/schema/runtime.sql +1113 -0
- package/runtime/engine/refuse.mjs +11 -0
- package/runtime/knowledge/hfs/canon-pins.yaml +65 -9
- package/runtime/knowledge/hfs/design-truths.yaml +149 -0
- package/runtime/knowledge/hfs/fe-stack.yaml +23 -5
- package/runtime/knowledge/hfs/rules.yaml +269 -48
- package/runtime/knowledge/hfs/slots.yaml +113 -53
- package/runtime/knowledge/patterns/be/api.yaml +46 -20
- package/runtime/knowledge/patterns/be/event-bus.yaml +19 -12
- package/runtime/knowledge/patterns/be/idempotency.yaml +265 -0
- package/runtime/knowledge/patterns/be/jobs.yaml +17 -12
- package/runtime/knowledge/patterns/be/projections.yaml +2 -2
- package/runtime/knowledge/patterns/be/queues.yaml +57 -22
- package/runtime/knowledge/patterns/be/reactors.yaml +1 -1
- package/runtime/knowledge/patterns/be/realtime.yaml +326 -27
- package/runtime/knowledge/patterns/be/saga.yaml +2 -2
- package/runtime/knowledge/sonar-gate.yaml +1 -1
- package/runtime/modules/kernel/command-policy.yaml +249 -0
- package/runtime/modules/kernel/failure-codes.yaml +211 -52
- package/runtime/modules/kernel/hook-input.yaml +6 -0
- package/runtime/modules/kernel/protected-zone.yaml +81 -0
- package/runtime/modules/kernel/removed-vocabulary.yaml +62 -0
- package/runtime/modules/kernel/roles.yaml +405 -0
- package/runtime/modules/models/registry.yaml +3 -1
- package/runtime/modules/models/runtimes.yaml +2 -0
- package/runtime/modules/schemas/application-stacks.schema.yaml +10 -2
- package/runtime/packages/cli/src/catalog.generated.mjs +46 -59
- package/runtime/packages/grammar/scripts/build-stamp.mjs +98 -0
- package/runtime/scripts/agent/codex-app-server.mjs +79 -0
- package/runtime/scripts/agent/codex-mirror-source.mjs +34 -0
- package/runtime/scripts/agent/host-agents.mjs +80 -0
- package/runtime/scripts/agent/launch-trust-policy.mjs +53 -0
- package/runtime/scripts/agent/trust-launch.mjs +191 -0
- package/runtime/scripts/agent/trust.mjs +523 -0
- package/runtime/scripts/api/fs/rename-over.mjs +21 -0
- package/runtime/scripts/api/fs/temp-path.mjs +8 -0
- package/runtime/scripts/api/fs/write-json-file.mjs +10 -0
- package/runtime/scripts/api/git/diff-names.mjs +8 -0
- package/runtime/scripts/api/git/head-time.mjs +10 -0
- package/runtime/scripts/api/git/index-lock.mjs +78 -0
- package/runtime/scripts/api/git/is-ancestor.mjs +5 -0
- package/runtime/scripts/api/git/log.mjs +5 -0
- package/runtime/scripts/api/git/merge-base-query.mjs +5 -0
- package/runtime/scripts/api/git/merge-tree.mjs +5 -0
- package/runtime/scripts/api/git/rev-list.mjs +5 -0
- package/runtime/scripts/api/git/rev-parse.mjs +13 -0
- package/runtime/scripts/api/git/status-query.mjs +5 -0
- package/runtime/scripts/api/git/worktree-list-query.mjs +5 -0
- package/runtime/scripts/api/node/spawn-node.mjs +6 -0
- package/runtime/scripts/api/npm/dist-tag.mjs +8 -0
- package/runtime/scripts/api/npm/lib.mjs +32 -0
- package/runtime/scripts/api/npm/pack-dry-run.mjs +9 -0
- package/runtime/scripts/api/npm/run-npm.mjs +6 -0
- package/runtime/scripts/api/npm/view.mjs +30 -0
- package/runtime/scripts/api/npm/whoami.mjs +8 -0
- package/runtime/scripts/api/process/hide-child-windows.mjs +31 -0
- package/runtime/scripts/api/process/kill-tree.mjs +22 -0
- package/runtime/scripts/api/process/lib.mjs +125 -0
- package/runtime/scripts/api/process/probe-guard-command.mjs +74 -0
- package/runtime/scripts/api/process/process-list.mjs +28 -0
- package/runtime/scripts/api/process/system-tool.mjs +37 -0
- package/runtime/scripts/gates/canon-digest.mjs +81 -0
- package/runtime/scripts/gates/canon-pins.mjs +16 -0
- package/runtime/scripts/gates/commit-proof.mjs +53 -0
- package/runtime/scripts/gates/gate-input.mjs +98 -0
- package/runtime/scripts/gates/gate.mjs +669 -0
- package/runtime/scripts/gates/grammar-dist.mjs +206 -0
- package/runtime/scripts/gates/runtime-package-selection.mjs +27 -0
- package/runtime/scripts/gates/test-world-run.mjs +254 -0
- package/runtime/scripts/gates/type-impact.mjs +277 -0
- package/runtime/scripts/gates/type-program.mjs +113 -0
- package/runtime/scripts/guards/assigned-command.mjs +12 -0
- package/runtime/scripts/guards/call-rights.mjs +47 -0
- package/runtime/scripts/guards/command-guard.mjs +468 -0
- package/runtime/scripts/guards/command-policy.mjs +342 -0
- package/runtime/scripts/guards/critic-reach.mjs +82 -0
- package/runtime/scripts/guards/deps-guard.mjs +150 -0
- package/runtime/scripts/guards/env-dump-verdict.mjs +89 -0
- package/runtime/scripts/guards/git-pathspec.mjs +225 -0
- package/runtime/scripts/guards/git-policy.mjs +355 -0
- package/runtime/scripts/guards/guards-root.mjs +27 -0
- package/runtime/scripts/guards/hook-install.mjs +343 -0
- package/runtime/scripts/guards/hook-io.mjs +76 -0
- package/runtime/scripts/guards/hook-verdict.mjs +12 -0
- package/runtime/scripts/guards/install-verdict.mjs +41 -0
- package/runtime/scripts/guards/kernel-seat.mjs +63 -0
- package/runtime/scripts/guards/launch-verdict.mjs +74 -0
- package/runtime/scripts/guards/op-starci.mjs +16 -0
- package/runtime/scripts/guards/op-write-scope.mjs +27 -0
- package/runtime/scripts/guards/orca-self-lifecycle.mjs +39 -0
- package/runtime/scripts/guards/process-kill-verdict.mjs +106 -0
- package/runtime/scripts/guards/protected-zone.mjs +56 -0
- package/runtime/scripts/guards/refusals.mjs +27 -0
- package/runtime/scripts/guards/release-definition.mjs +185 -0
- package/runtime/scripts/guards/release-push-gate.mjs +72 -0
- package/runtime/scripts/guards/release-record.mjs +58 -0
- package/runtime/scripts/guards/release-suite-mode.mjs +15 -0
- package/runtime/scripts/guards/rights.mjs +268 -0
- package/runtime/scripts/guards/seat-tools.mjs +47 -0
- package/runtime/scripts/guards/shell-commands.mjs +328 -0
- package/runtime/scripts/guards/supervisor-seat.mjs +80 -0
- package/runtime/scripts/guards/verdict-process.mjs +81 -0
- package/runtime/scripts/hfs/architecture/dead-exports.mjs +10 -10
- package/runtime/scripts/hfs/architecture/function-spec-providers.mjs +1 -1
- package/runtime/scripts/hfs/architecture/operation-contract-roots.mjs +53 -0
- package/runtime/scripts/hfs/architecture/reachability.mjs +1 -1
- package/runtime/scripts/hfs/architecture/sql-owner.mjs +17 -6
- package/runtime/scripts/hfs/architecture/sql-token-forms.mjs +105 -0
- package/runtime/scripts/hfs/architecture/sql-tokens.mjs +38 -52
- package/runtime/scripts/hfs/architecture/supabase-keys.mjs +261 -0
- package/runtime/scripts/hfs/architecture/transport-owner.mjs +49 -3
- package/runtime/scripts/hfs/architecture/typescript.mjs +1 -1
- package/runtime/scripts/hfs/check.mjs +6 -4
- package/runtime/scripts/hfs/coverage-scope.mjs +28 -1
- package/runtime/scripts/hfs/db-restamp-verb.mjs +54 -14
- package/runtime/scripts/hfs/declaration-shape.mjs +4 -4
- package/runtime/scripts/hfs/declaration-slots.mjs +14 -3
- package/runtime/scripts/hfs/manifest-shape.mjs +10 -6
- package/runtime/scripts/hfs/rule-params-shape.mjs +18 -6
- package/runtime/scripts/hfs/rules/a15-findings.mjs +4 -1
- package/runtime/scripts/hfs/rules/bypass-markers.mjs +22 -7
- package/runtime/scripts/hfs/rules/database-migrations.mjs +69 -14
- package/runtime/scripts/hfs/rules/database-queue-sources.mjs +9 -7
- package/runtime/scripts/hfs/rules/database-queues.mjs +68 -18
- package/runtime/scripts/hfs/rules/edition.mjs +56 -12
- package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +1 -10
- package/runtime/scripts/hfs/rules/fe-stack.mjs +12 -7
- package/runtime/scripts/hfs/rules/lite-proof-lane.mjs +1 -1
- package/runtime/scripts/hfs/rules/stacks-lite-backing.mjs +75 -9
- package/runtime/scripts/hfs/rules/stacks-runtime.mjs +1 -1
- package/runtime/scripts/hfs/rules/stacks-swarm.mjs +15 -4
- package/runtime/scripts/hfs/rules/text-integrity.mjs +73 -0
- package/runtime/scripts/hfs/runtime-rules/hook-shape.mjs +74 -0
- package/runtime/scripts/hfs/runtime-rules/release-notes.mjs +44 -0
- package/runtime/scripts/hfs/sealed-members.mjs +3 -3
- package/runtime/scripts/hfs/slot-side-problems.mjs +3 -3
- package/runtime/scripts/hfs/slots.mjs +2 -1
- package/runtime/scripts/hfs/stack-deploy-config.mjs +22 -5
- package/runtime/scripts/hfs/stacks-gate.mjs +28 -7
- package/runtime/scripts/hfs/view.mjs +2 -1
- package/runtime/scripts/lib/app-data.mjs +11 -0
- package/runtime/scripts/lib/bypass-markers.mjs +272 -75
- package/runtime/scripts/lib/check-format.mjs +7 -0
- package/runtime/scripts/lib/cli-arg.mjs +120 -0
- package/runtime/scripts/lib/css-rules.mjs +220 -0
- package/runtime/scripts/lib/dom-tree.mjs +4 -0
- package/runtime/scripts/lib/escape.mjs +16 -0
- package/runtime/scripts/lib/event-items.mjs +22 -0
- package/runtime/scripts/lib/failure-code-template-errors.mjs +161 -0
- package/runtime/scripts/lib/git-dir.mjs +120 -0
- package/runtime/scripts/lib/grammar-vendor-parity.mjs +377 -0
- package/runtime/scripts/lib/guard-command.mjs +24 -0
- package/runtime/scripts/lib/hook-limits.mjs +5 -0
- package/runtime/scripts/lib/interrupt-cleanup.mjs +38 -0
- package/runtime/scripts/lib/js-lex.mjs +200 -0
- package/runtime/scripts/lib/json-formats.mjs +0 -1
- package/runtime/scripts/lib/language.mjs +94 -1
- package/runtime/scripts/lib/loosening-budgets.mjs +44 -0
- package/runtime/scripts/lib/loosening-config.mjs +361 -0
- package/runtime/scripts/lib/loosening-state.mjs +223 -0
- package/runtime/scripts/lib/menu-parts.mjs +11 -0
- package/runtime/scripts/lib/normalize.mjs +25 -0
- package/runtime/scripts/lib/op-gate.mjs +15 -0
- package/runtime/scripts/lib/redact.mjs +208 -0
- package/runtime/scripts/lib/runtime-location.mjs +21 -0
- package/runtime/scripts/lib/runtime-package-inventory.mjs +21 -0
- package/runtime/scripts/lib/runtime-package-manifests.mjs +84 -0
- package/runtime/scripts/lib/runtime-publication-tags.mjs +99 -0
- package/runtime/scripts/lib/seat-env.mjs +43 -0
- package/runtime/scripts/lib/secret-patterns.mjs +58 -9
- package/runtime/scripts/lib/shell-source.mjs +4 -0
- package/runtime/scripts/lib/source-phrases.mjs +34 -0
- package/runtime/scripts/lib/sqlite.mjs +21 -0
- package/runtime/scripts/lib/swarm-stack.mjs +2 -1
- package/runtime/scripts/lib/tar-files.mjs +39 -0
- package/runtime/scripts/lib/ts-analysis.mjs +10 -0
- package/runtime/scripts/lib/worktree-exclude.mjs +35 -0
- package/runtime/scripts/lib/worktree-record.mjs +14 -0
- package/runtime/scripts/machine/artifact-hold.mjs +52 -0
- package/runtime/scripts/machine/ask-receipts.mjs +113 -0
- package/runtime/scripts/machine/evidence-store.mjs +223 -0
- package/runtime/scripts/machine/home.mjs +216 -0
- package/runtime/scripts/machine/host-lock.mjs +184 -0
- package/runtime/scripts/machine/lock-recovery.mjs +55 -0
- package/runtime/scripts/machine/roles-contract.mjs +90 -0
- package/runtime/scripts/machine/runtime-change.mjs +20 -0
- package/runtime/scripts/machine/workflow-tree.mjs +122 -0
- package/runtime/scripts/reconciler/checklist-items.mjs +7 -0
- package/runtime/scripts/reconciler/guard-row.mjs +25 -0
- package/runtime/scripts/work/asset-slot.mjs +284 -0
- package/runtime/scripts/work/brand/brand-colour.mjs +125 -0
- package/runtime/scripts/work/brand/brand-tokens.mjs +167 -0
- package/runtime/scripts/work/brand/brand-value-source.mjs +106 -0
- package/runtime/scripts/work/brand/brand.mjs +892 -0
- package/runtime/scripts/work/draw/draw-dna-anatomy.mjs +60 -0
- package/runtime/scripts/work/draw/draw-dna-findings.mjs +256 -0
- package/runtime/scripts/work/draw/draw-dna-selector.mjs +38 -0
- package/runtime/scripts/work/draw/draw-dna.mjs +483 -0
- package/runtime/scripts/work/draw/draw-layer.mjs +305 -0
- package/runtime/scripts/work/draw/draw-rationale-page.mjs +237 -0
- package/runtime/scripts/work/draw/draw-rationale-ref.mjs +14 -0
- package/runtime/scripts/work/draw/draw-rationale.mjs +353 -0
- package/runtime/scripts/work/draw/draw-source.mjs +412 -0
- package/runtime/scripts/work/draw/draw-taste.mjs +278 -0
- package/runtime/scripts/work/draw/draw-typecheck.mjs +72 -0
- package/runtime/scripts/work/draw/rationale-findings.mjs +205 -0
- package/runtime/scripts/work/draw-grammar.mjs +175 -0
- package/runtime/scripts/work/draw-render-page.mjs +188 -0
- package/runtime/scripts/work/draw-render.mjs +626 -0
- package/runtime/scripts/work/grammar-proposal.mjs +279 -0
- package/runtime/scripts/work/png.mjs +237 -0
- package/runtime/scripts/work/render-tools.mjs +64 -0
- package/runtime/scripts/work/work-io.mjs +119 -0
- package/scaffold/add-inbox-lite.mjs +2 -2
- package/scaffold/add-plan.mjs +40 -13
- package/scaffold/add-queue-lite.mjs +372 -0
- package/scaffold/add.mjs +5 -2
- package/scaffold/app.mjs +1 -2
- package/scaffold/edition-gate.mjs +11 -7
- package/scaffold/lite-exports.mjs +10 -0
- package/scaffold/pattern-file-slots.mjs +33 -0
- package/scaffold/test-world.mjs +1 -1
- package/scaffold/yaml-block-edit.mjs +117 -0
- package/src/main.mjs +5 -4
- package/stack/deploy.mjs +7 -3
- package/stack/post-deploy.mjs +48 -0
- package/sync/index.mjs +10 -0
- package/templates/app/ci-workflows/github/workflows/stack-deploy.yml +1 -1
- package/templates/app/ci-workflows-lite/github/workflows/stack-deploy.yml +1 -1
- package/templates/app/codegen-graphql.mjs.partial +59 -12
- package/templates/app/hooks/husky/pre-commit +2 -2
- package/templates/app/hooks/husky/pre-push +10 -3
- package/templates/app/package-scripts/redis-dev.json +3 -0
- package/templates/app/package-scripts-lite/package.json +1 -0
- package/templates/app/skeleton/.starcistacks/application-stacks.yaml +36 -8
- package/templates/app/skeleton/.starcistacks/dev/README.md +1 -1
- package/templates/app/skeleton/.starcistacks/production/README.md +10 -9
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/postgres-init.sh +21 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/recovery/restore-proof.md +1 -1
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/releases/images.lock.yaml +1 -0
- package/templates/app/skeleton/.starcistacks/production/infra/swarm/stack.yaml +47 -5
- package/templates/app/skeleton/.starcistacks/production/runtime/env/KEYS.md +5 -3
- package/templates/app/skeleton/README.md +2 -0
- package/templates/app/skeleton-lite/.starcistacks/application-stacks.yaml +5 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/README.md +2 -2
- package/templates/app/skeleton-lite/.starcistacks/production/README.md +1 -1
- package/templates/app/skeleton-lite/README.md +1 -1
- package/templates/be/inbox/platform/client-operation-outcomes.sql.tpl +18 -0
- package/templates/be/inbox/platform/event-bus/errors/event-bus.error.ts.tpl +16 -0
- package/templates/be/inbox/platform/event-bus/event-bus.contracts.ts.tpl +10 -0
- package/templates/be/inbox/platform/event-bus/event-bus.port.ts.tpl +1 -1
- package/templates/be/inbox/platform/event-bus/event-bus.service.spec.ts.tpl +15 -3
- package/templates/be/inbox/platform/event-bus/event-bus.service.ts.tpl +11 -3
- package/templates/be/inbox/platform/event-bus/index.ts.tpl +2 -0
- package/templates/be/inbox/platform/event-bus/messages/event-bus.messages.ts.tpl +7 -0
- package/templates/be/inbox/platform/event-bus/persistence/event-bus.sql.ts.tpl +2 -2
- package/templates/be/inbox/platform/idempotency/errors/idempotency.error.ts.tpl +19 -0
- package/templates/be/inbox/platform/idempotency/index.ts.tpl +2 -0
- package/templates/be/inbox/platform/idempotency/messages/idempotency.messages.ts.tpl +13 -0
- package/templates/be/inbox/platform/inbox/client-operation.policy.spec.ts.tpl +32 -0
- package/templates/be/inbox/platform/inbox/client-operation.policy.ts.tpl +17 -0
- package/templates/be/inbox/platform/inbox/errors/inbox.error.ts.tpl +22 -0
- package/templates/be/inbox/platform/inbox/inbox.port.ts.tpl +21 -0
- package/templates/be/inbox/platform/inbox/inbox.service.spec.ts.tpl +334 -11
- package/templates/be/inbox/platform/inbox/inbox.service.ts.tpl +55 -4
- package/templates/be/inbox/platform/inbox/index.ts.tpl +2 -0
- package/templates/be/inbox/platform/inbox/messages/inbox.messages.ts.tpl +15 -0
- package/templates/be/inbox/platform/inbox/persistence/inbox.rows.ts.tpl +33 -0
- package/templates/be/inbox/platform/inbox/persistence/inbox.sql.ts.tpl +26 -0
- package/templates/be/inbox/platform/inbox/request-fingerprint.policy.spec.ts.tpl +76 -0
- package/templates/be/inbox/platform/inbox/request-fingerprint.policy.ts.tpl +36 -0
- package/templates/be/inbox/platform/migration.sql.tpl +1 -0
- package/templates/be/inbox/service.spec.ts.tpl +4 -4
- package/templates/be/inbox/service.ts.tpl +3 -1
- package/templates/be/inbox/webhook-door.spec.ts.tpl +3 -2
- package/templates/be/inbox/webhook-door.ts.tpl +3 -2
- package/templates/be/patterns/event-bus/platform/errors/event-bus.error.ts.tpl +3 -0
- package/templates/be/patterns/event-bus/platform/event-bus.contracts.ts.tpl +10 -0
- package/templates/be/patterns/event-bus/platform/event-bus.port.ts.tpl +2 -2
- package/templates/be/patterns/event-bus/platform/event-bus.service.spec.ts.tpl +16 -3
- package/templates/be/patterns/event-bus/platform/event-bus.service.ts.tpl +11 -3
- package/templates/be/patterns/event-bus/platform/event-envelope.policy.spec.ts.tpl +139 -50
- package/templates/be/patterns/event-bus/platform/event-envelope.policy.ts.tpl +25 -2
- package/templates/be/patterns/event-bus/platform/event-relay.service.spec.ts.tpl +139 -78
- package/templates/be/patterns/event-bus/platform/event-relay.service.ts.tpl +10 -6
- package/templates/be/patterns/event-bus/platform/event-runner.service.spec.ts.tpl +262 -159
- package/templates/be/patterns/event-bus/platform/event-runner.service.ts.tpl +32 -5
- package/templates/be/patterns/event-bus/platform/messages/event-bus.messages.ts.tpl +2 -0
- package/templates/be/patterns/event-bus/platform/persistence/entities/event-outbox.entity.ts.tpl +5 -1
- package/templates/be/patterns/event-bus/platform/persistence/event-bus.rows.ts.tpl +2 -0
- package/templates/be/patterns/event-bus/platform/persistence/event-bus.sql.ts.tpl +4 -4
- package/templates/be/patterns/event-bus/platform/persistence/migrations/1789800009000-create-event-outbox.ts.tpl +1 -0
- package/templates/be/patterns/idempotency/platform/idempotency/errors/idempotency.error.ts.tpl +19 -0
- package/templates/be/patterns/idempotency/platform/idempotency/index.ts.tpl +2 -0
- package/templates/be/patterns/idempotency/platform/idempotency/messages/idempotency.messages.ts.tpl +13 -0
- package/templates/be/patterns/idempotency/platform/inbox/client-operation.policy.spec.ts.tpl +32 -0
- package/templates/be/patterns/idempotency/platform/inbox/client-operation.policy.ts.tpl +17 -0
- package/templates/be/patterns/idempotency/platform/inbox/client-outcome-backfill.client.spec.ts.tpl +66 -0
- package/templates/be/patterns/idempotency/platform/inbox/client-outcome-backfill.client.ts.tpl +24 -0
- package/templates/be/patterns/idempotency/platform/inbox/delivery-claim-backfill.client.spec.ts.tpl +60 -0
- package/templates/be/patterns/idempotency/platform/inbox/delivery-claim-backfill.client.ts.tpl +12 -0
- package/templates/be/patterns/idempotency/platform/inbox/delivery-claim-release.client.spec.ts.tpl +49 -0
- package/templates/be/patterns/idempotency/platform/inbox/delivery-claim-release.client.ts.tpl +12 -0
- package/templates/be/patterns/idempotency/platform/inbox/effectful.decorators.ts.tpl +7 -0
- package/templates/be/patterns/idempotency/platform/inbox/errors/inbox.error.ts.tpl +22 -0
- package/templates/be/patterns/idempotency/platform/inbox/inbox.decorators.ts.tpl +17 -0
- package/templates/be/patterns/idempotency/platform/inbox/inbox.module-definition.ts.tpl +7 -0
- package/templates/be/patterns/idempotency/platform/inbox/inbox.module.ts.tpl +28 -0
- package/templates/be/patterns/idempotency/platform/inbox/inbox.options.ts.tpl +7 -0
- package/templates/be/patterns/idempotency/platform/inbox/inbox.port.ts.tpl +44 -0
- package/templates/be/patterns/idempotency/platform/inbox/inbox.service.spec.ts.tpl +388 -0
- package/templates/be/patterns/idempotency/platform/inbox/inbox.service.ts.tpl +77 -0
- package/templates/be/patterns/idempotency/platform/inbox/index.ts.tpl +10 -0
- package/templates/be/patterns/idempotency/platform/inbox/messages/inbox.messages.ts.tpl +15 -0
- package/templates/be/patterns/idempotency/platform/inbox/persistence/connection.ts.tpl +9 -0
- package/templates/be/patterns/idempotency/platform/inbox/persistence/entities/inbox-claim.entity.ts.tpl +25 -0
- package/templates/be/patterns/idempotency/platform/inbox/persistence/inbox.rows.ts.tpl +33 -0
- package/templates/be/patterns/idempotency/platform/inbox/persistence/inbox.sql.ts.tpl +39 -0
- package/templates/be/patterns/idempotency/platform/inbox/persistence/migrations/1789800005000-create-inbox-claims.ts.tpl +21 -0
- package/templates/be/patterns/idempotency/platform/inbox/persistence/migrations/1792800000000-client-operation-outcomes.ts.tpl +21 -0
- package/templates/be/patterns/idempotency/platform/inbox/request-fingerprint.policy.spec.ts.tpl +76 -0
- package/templates/be/patterns/idempotency/platform/inbox/request-fingerprint.policy.ts.tpl +36 -0
- package/templates/be/patterns/jobs/platform/fenced.processor.spec.ts.tpl +1 -0
- package/templates/be/patterns/jobs/platform/job-claim.service.spec.ts.tpl +4 -3
- package/templates/be/patterns/jobs/platform/job-claim.service.ts.tpl +2 -0
- package/templates/be/patterns/jobs/platform/job-runner.service.spec.ts.tpl +20 -2
- package/templates/be/patterns/jobs/platform/job-runner.service.ts.tpl +1 -0
- package/templates/be/patterns/jobs/platform/jobs.contracts.ts.tpl +4 -0
- package/templates/be/patterns/jobs/platform-lite/jobs.index.ts.tpl +10 -0
- package/templates/be/patterns/jobs/platform-lite/jobs.sql.tpl +44 -0
- package/templates/be/patterns/jobs/platform-lite/persistence.jobs.sql.ts.tpl +33 -0
- package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.spec.ts.tpl +203 -18
- package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.ts.tpl +78 -22
- package/templates/be/patterns/queues/platform/errors/queue.error.ts.tpl +19 -0
- package/templates/be/patterns/queues/platform/messages/queue.messages.ts.tpl +13 -0
- package/templates/be/patterns/queues/platform/persistence.migration.ts.tpl +1 -0
- package/templates/be/patterns/queues/platform/persistence.queue-outbox.entity.ts.tpl +4 -0
- package/templates/be/patterns/queues/platform/persistence.queue.rows.ts.tpl +2 -0
- package/templates/be/patterns/queues/platform/persistence.queue.sql.ts.tpl +4 -4
- package/templates/be/patterns/queues/platform/queue-outbox.service.spec.ts.tpl +14 -2
- package/templates/be/patterns/queues/platform/queue-outbox.service.ts.tpl +5 -2
- package/templates/be/patterns/queues/platform/queue-relay.service.spec.ts.tpl +72 -74
- package/templates/be/patterns/queues/platform/queue-relay.service.ts.tpl +1 -1
- package/templates/be/patterns/queues/platform/queue-transport.port.ts.tpl +23 -8
- package/templates/be/patterns/queues/platform/queue-worker.service.spec.ts.tpl +29 -8
- package/templates/be/patterns/queues/platform/queue-worker.service.ts.tpl +7 -3
- package/templates/be/patterns/queues/platform/queue.contracts.ts.tpl +5 -0
- package/templates/be/patterns/queues/platform/queue.index.ts.tpl +2 -0
- package/templates/be/patterns/queues/platform/queue.log-events.ts.tpl +4 -0
- package/templates/be/patterns/queues/platform/queue.module.ts.tpl +2 -1
- package/templates/be/patterns/queues/platform/queue.port.ts.tpl +1 -1
- package/templates/be/patterns/queues/platform-lite/bullmq-queue-transport.client.spec.ts.tpl +417 -0
- package/templates/be/patterns/queues/platform-lite/bullmq-queue-transport.client.ts.tpl +172 -0
- package/templates/be/patterns/queues/platform-lite/persistence.queue.sql.ts.tpl +12 -0
- package/templates/be/patterns/queues/platform-lite/queue-outbox.sql.tpl +32 -0
- package/templates/be/patterns/queues/platform-lite/queue-relay.service.spec.ts.tpl +148 -0
- package/templates/be/patterns/queues/platform-lite/queue-relay.service.ts.tpl +51 -0
- package/templates/be/patterns/queues/platform-lite/queue-transport.port.ts.tpl +163 -0
- package/templates/be/patterns/queues/platform-lite/queue.config.ts.tpl +17 -0
- package/templates/be/patterns/queues/platform-lite/queue.error.ts.tpl +25 -0
- package/templates/be/patterns/queues/platform-lite/queue.index.ts.tpl +8 -0
- package/templates/be/patterns/queues/platform-lite/queue.messages.ts.tpl +21 -0
- package/templates/be/patterns/queues/platform-lite/queue.module.ts.tpl +74 -0
- package/templates/be/patterns/queues/platform-lite/queue.options.ts.tpl +38 -0
- package/templates/be/patterns/queues/queue.queue.ts.tpl +2 -2
- package/templates/be/patterns/reactors/application.contracts.ts.tpl +2 -0
- package/templates/be/patterns/reactors/consumer.ts.tpl +5 -1
- package/templates/be/platform/graphql/graphql.contracts.ts +2 -1
- package/templates/be/platform/graphql/localize-errors.mapper.spec.ts +1 -1
- package/templates/be/skeleton/apps/__app__/src/compose/platform.ts +6 -1
- package/templates/be/skeleton/apps/__app__/src/main.ts +7 -4
- package/templates/be/skeleton/apps/cli/src/compose/platform.ts +4 -0
- package/templates/be/skeleton/apps/cli/src/main.ts +7 -4
- package/templates/be/skeleton/src/modules/platform/database/index.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/http/errors/http.error.ts +22 -0
- package/templates/be/skeleton/src/modules/platform/http/fetch-http-client.client.spec.ts +235 -0
- package/templates/be/skeleton/src/modules/platform/http/fetch-http-client.client.ts +85 -0
- package/templates/be/skeleton/src/modules/platform/http/graphql-call.mapper.spec.ts +92 -0
- package/templates/be/skeleton/src/modules/platform/http/graphql-call.mapper.ts +52 -0
- package/templates/be/skeleton/src/modules/platform/http/http.decorators.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/http/http.module-definition.ts +7 -0
- package/templates/be/skeleton/src/modules/platform/http/http.module.ts +19 -0
- package/templates/be/skeleton/src/modules/platform/http/http.options.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/http/http.port.ts +40 -0
- package/templates/be/skeleton/src/modules/platform/http/index.ts +7 -0
- package/templates/be/skeleton/src/modules/platform/http/messages/http.messages.ts +15 -0
- package/templates/be/skeleton/src/modules/platform/ids/ids.decorators.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/ids/ids.module-definition.ts +7 -0
- package/templates/be/skeleton/src/modules/platform/ids/ids.module.ts +19 -0
- package/templates/be/skeleton/src/modules/platform/ids/ids.options.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/ids/ids.port.ts +5 -0
- package/templates/be/skeleton/src/modules/platform/ids/index.ts +4 -0
- package/templates/be/skeleton/src/modules/platform/ids/system-ids.service.spec.ts +15 -0
- package/templates/be/skeleton/src/modules/platform/ids/system-ids.service.ts +10 -0
- package/templates/be/skeleton/src/modules/platform/logging/correlation-request.policy.spec.ts +59 -0
- package/templates/be/skeleton/src/modules/platform/logging/correlation-request.policy.ts +21 -0
- package/templates/be/skeleton/src/modules/platform/logging/correlation.contracts.ts +16 -0
- package/templates/be/skeleton/src/modules/platform/logging/correlation.policy.spec.ts +150 -0
- package/templates/be/skeleton/src/modules/platform/logging/correlation.policy.ts +42 -0
- package/templates/be/skeleton/src/modules/platform/logging/correlation.port.ts +15 -0
- package/templates/be/skeleton/src/modules/platform/logging/correlation.service.spec.ts +23 -0
- package/templates/be/skeleton/src/modules/platform/logging/correlation.service.ts +28 -0
- package/templates/be/skeleton/src/modules/platform/logging/index.ts +14 -1
- package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.spec.ts +39 -6
- package/templates/be/skeleton/src/modules/platform/logging/json-logger.service.ts +15 -3
- package/templates/be/skeleton/src/modules/platform/logging/logging.decorators.ts +17 -2
- package/templates/be/skeleton/src/modules/platform/logging/logging.log-events.ts +13 -1
- package/templates/be/skeleton/src/modules/platform/logging/logging.module.ts +13 -3
- package/templates/be/skeleton/src/modules/platform/logging/nest-logger.service.spec.ts +69 -0
- package/templates/be/skeleton/src/modules/platform/logging/nest-logger.service.ts +48 -0
- package/templates/be/skeleton/src/modules/platform/primitives/index.ts +1 -0
- package/templates/be/skeleton/src/modules/platform/primitives/record.policy.spec.ts +12 -0
- package/templates/be/skeleton/src/modules/platform/primitives/record.policy.ts +3 -0
- package/templates/be/skeleton-lite/apps/api/src/compose/platform.ts +6 -1
- package/templates/be/skeleton-lite/apps/cli/src/compose/platform.ts +4 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +1 -0
- package/templates/be/skeleton-lite/src/modules/platform/logging/index.ts +14 -1
- package/templates/be/skeleton-lite/src/modules/platform/logging/logging.log-events.ts +12 -0
- package/templates/be/skeleton-lite/src/modules/platform/primitives/index.ts +2 -0
- package/templates/be/test-world/world/test-apps.options.ts.tpl +2 -0
- package/templates/be/test-world/world/test-world.config.ts.tpl +1 -0
- package/upgrade/check.mjs +12 -135
- package/upgrade/file-state.mjs +1 -16
- package/upgrade/index.mjs +34 -361
- package/runtime/scripts/lib/check-code-loosening.mjs +0 -128
- package/runtime/scripts/lib/gate-loosening.mjs +0 -292
- package/templates/app/upgrade-full/.starcistacks/application-stacks.yaml +0 -89
- package/templates/app/upgrade-full/.starcistacks/dev/README.md +0 -16
- package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/api.yaml +0 -13
- package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/compose.yaml +0 -3
- package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/postgres.yaml +0 -12
- package/templates/app/upgrade-full/.starcistacks/dev/infra/metadata.json +0 -7
- package/templates/be/skeleton-lite/apps/api/src/main.ts +0 -26
- package/templates/be/upgrade-full/apps/cli/src/app.module.ts +0 -12
- package/templates/be/upgrade-full/apps/cli/src/cli.env.ts +0 -7
- package/templates/be/upgrade-full/apps/cli/src/compose/cli.ts +0 -5
- package/templates/be/upgrade-full/apps/cli/src/compose/platform.ts +0 -9
- package/templates/be/upgrade-full/apps/cli/src/main.ts +0 -29
- package/templates/be/upgrade-full/src/features/cli/cli.module.ts +0 -7
- package/templates/be/upgrade-full/src/features/cli/migrate/subs/run.cli.ts +0 -23
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// draw-grammar.mjs — which @starci/grammar a drawing renders against (owner ruling 2026-09-27: the draw must render
|
|
3
|
+
// against the grammar version the product will SHIP).
|
|
4
|
+
//
|
|
5
|
+
// Candidates, in order:
|
|
6
|
+
// product the grammar the product has installed (resolved from the product app dir, as its bundler would)
|
|
7
|
+
// claude-dist the runtime-built package (.claude/packages/grammar/dist, the grammar main carries - possibly
|
|
8
|
+
// newer than the product's install, e.g. Meter segments 0.5.2 against an installed 0.5.0)
|
|
9
|
+
// The product's install wins when it SATISFIES what the draw needs, and "satisfies" is decided by TypeScript: the
|
|
10
|
+
// draw file type-checks against that candidate's types (scripts/work/draw/draw-source.mjs typecheckDraw). When only
|
|
11
|
+
// claude-dist satisfies, the drawing renders against it through an alias in the draw bundle ONLY (draw-render.mjs
|
|
12
|
+
// --grammar-root), the provenance says so (grammarSource claude-dist@<v>) and an upgrade of the product is OWED
|
|
13
|
+
// (upgradeOwed {status: owed, from, to, range, inRange}) - interface.implement bumps the product's dependency; the
|
|
14
|
+
// draw never touches the product's package.json. When neither satisfies: DRAW_TYPECHECK_FAILED.
|
|
15
|
+
//
|
|
16
|
+
// starci work draw-grammar --product <app dir> [--file <X.draw.tsx>] [--grammar auto|product|claude-dist]
|
|
17
|
+
// [--grammar-dist <package root>] (default .claude/packages/grammar; env STARCI_GRAMMAR_DIST; a lane checkout
|
|
18
|
+
// without a built dist points it at the live checkout's)
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
|
+
import { revParseQuery } from '../api/git/rev-parse-query.mjs';
|
|
22
|
+
import { findPackage } from '../lib/package-at.mjs';
|
|
23
|
+
import { readJsonFile } from '../lib/json.mjs';
|
|
24
|
+
import { GRAMMAR_PACKAGE, typecheckDraw } from './draw/draw-typecheck.mjs';
|
|
25
|
+
import { grammarDistStatus } from '../gates/grammar-dist.mjs';
|
|
26
|
+
import { isFile } from './work-io.mjs';
|
|
27
|
+
import { isMain } from '../lib/is-main.mjs';
|
|
28
|
+
|
|
29
|
+
const GRAMMAR_SOURCES = Object.freeze(['product', 'claude-dist']);
|
|
30
|
+
export const PREFERENCES = Object.freeze(['auto', ...GRAMMAR_SOURCES]);
|
|
31
|
+
const SKILL_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
32
|
+
|
|
33
|
+
/** The product's declared range for the grammar (dependencies/devDependencies/peerDependencies), or null. */
|
|
34
|
+
function productRangeOf(productDir) {
|
|
35
|
+
const pkg = readJsonFile(path.join(productDir, 'package.json'));
|
|
36
|
+
return pkg?.dependencies?.[GRAMMAR_PACKAGE] ?? pkg?.devDependencies?.[GRAMMAR_PACKAGE] ?? pkg?.peerDependencies?.[GRAMMAR_PACKAGE] ?? null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** A built grammar package root: its package.json plus dist/<family>/index.d.ts and .js. */
|
|
40
|
+
const builtGrammar = (root) => isFile(path.join(root, 'package.json')) && isFile(path.join(root, 'dist', 'core', 'index.d.ts')) && isFile(path.join(root, 'dist', 'core', 'index.js'));
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The main worktree of the git checkout `dir` lives in (a lane worktree's live runtime), or null: a lane checkout never
|
|
44
|
+
* builds packages/grammar/dist (dist/ is untracked), so its drawings resolve the live runtime's build.
|
|
45
|
+
*/
|
|
46
|
+
function mainWorktreeOf(dir) {
|
|
47
|
+
try {
|
|
48
|
+
const r = revParseQuery(['--path-format=absolute', '--git-common-dir'], { dir, timeout: 10_000 });
|
|
49
|
+
const common = r.status === 0 ? r.stdout.trim() : '';
|
|
50
|
+
if (!common || path.basename(common) !== '.git') return null;
|
|
51
|
+
const main = path.dirname(common);
|
|
52
|
+
return path.resolve(main) === path.resolve(dir) ? null : main;
|
|
53
|
+
} catch { return null; }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The claude-dist roots, best first: --grammar-dist / STARCI_GRAMMAR_DIST when given (used as is), else the runtime's
|
|
58
|
+
* packages/grammar when its build is FRESH (grammar-dist.mjs: stamped from the current source), else the live main
|
|
59
|
+
* worktree's fresh build (a lane checkout), else the runtime's built but stale dist - named stale, never silently.
|
|
60
|
+
*/
|
|
61
|
+
function claudeDistRoots({ skillRoot = SKILL_ROOT, grammarDist = null, env = process.env, status = grammarDistStatus, mainOf = mainWorktreeOf } = {}) {
|
|
62
|
+
const pinned = grammarDist ?? env.STARCI_GRAMMAR_DIST ?? null;
|
|
63
|
+
if (pinned) return [{ root: path.resolve(pinned), status: null, via: grammarDist ? '--grammar-dist' : 'STARCI_GRAMMAR_DIST' }];
|
|
64
|
+
const own = path.join(skillRoot, 'packages', 'grammar');
|
|
65
|
+
const main = mainOf(skillRoot);
|
|
66
|
+
const tried = [{ root: own, via: 'runtime' }, ...(main ? [{ root: path.join(main, 'packages', 'grammar'), via: 'main-worktree' }] : [])]
|
|
67
|
+
.filter((c) => builtGrammar(c.root)).map((c) => ({ ...c, status: status(c.root) }));
|
|
68
|
+
const fresh = tried.filter((c) => c.status?.ok);
|
|
69
|
+
return fresh.length ? [fresh[0]] : tried.slice(0, 1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function grammarCandidates({ productDir, skillRoot = SKILL_ROOT, grammarDist = null, claudeRoots = null }) {
|
|
73
|
+
const out = [];
|
|
74
|
+
const installed = productDir ? findPackage([productDir], [GRAMMAR_PACKAGE]) : null;
|
|
75
|
+
if (installed && builtGrammar(installed.root)) out.push({ source: 'product', root: installed.root, version: installed.version });
|
|
76
|
+
for (const c of claudeRoots ?? claudeDistRoots({ skillRoot, grammarDist })) {
|
|
77
|
+
if (!builtGrammar(c.root)) continue;
|
|
78
|
+
out.push({ source: 'claude-dist', root: c.root, version: readJsonFile(path.join(c.root, 'package.json'))?.version ?? null, via: c.via,
|
|
79
|
+
...(c.status ? { dist: { state: c.status.state, ok: c.status.ok, detail: c.status.detail } } : {}) });
|
|
80
|
+
}
|
|
81
|
+
return out;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const parse = (v) => { const m = /^(\d+)\.(\d+)\.(\d+)/.exec(String(v ?? '').trim()); return m ? m.slice(1, 4).map(Number) : null; };
|
|
85
|
+
const cmp = (a, b) => (a[0] - b[0]) || (a[1] - b[1]) || (a[2] - b[2]);
|
|
86
|
+
/** Minimal semver range test: exact, ^, ~, >=, * (enough for a dependency line); null when unreadable. */
|
|
87
|
+
function satisfiesRange(version, range) {
|
|
88
|
+
const v = parse(version);
|
|
89
|
+
if (!v || range == null) return null;
|
|
90
|
+
const r = String(range).trim();
|
|
91
|
+
if (r === '*' || r === 'latest' || r === '') return true;
|
|
92
|
+
const m = /^(\^|~|>=|=)?\s*v?(\d+\.\d+\.\d+)/.exec(r);
|
|
93
|
+
if (!m) return null;
|
|
94
|
+
const base = parse(m[2]);
|
|
95
|
+
if (cmp(v, base) < 0) return false;
|
|
96
|
+
if (m[1] === '>=') return true;
|
|
97
|
+
if (!m[1] || m[1] === '=') return cmp(v, base) === 0;
|
|
98
|
+
if (m[1] === '~') return v[0] === base[0] && v[1] === base[1];
|
|
99
|
+
// caret: the left-most non-zero part is fixed
|
|
100
|
+
if (base[0] > 0) return v[0] === base[0];
|
|
101
|
+
if (base[1] > 0) return v[0] === 0 && v[1] === base[1];
|
|
102
|
+
return v[0] === 0 && v[1] === 0 && v[2] === base[2];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function tryGrammarCandidates(candidates, { file, productDir, typecheck }) {
|
|
106
|
+
const attempts = [];
|
|
107
|
+
let pick = null;
|
|
108
|
+
for (const c of candidates) {
|
|
109
|
+
if (!file) { pick = c; break; }
|
|
110
|
+
const r = typecheck({ file, productDir, grammarRoot: c.root });
|
|
111
|
+
attempts.push({ source: c.source, version: c.version, root: c.root, ok: r.ok, errors: r.errors.slice(0, 20), typescript: r.typescript ?? null, ...(c.dist ? { dist: c.dist } : {}) });
|
|
112
|
+
if (r.ok) { pick = c; break; }
|
|
113
|
+
}
|
|
114
|
+
return { attempts, pick };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function upgradeOwedFor(pick, product, productRange) {
|
|
118
|
+
return pick?.source === 'claude-dist' ? { status: 'owed', package: GRAMMAR_PACKAGE, from: product?.version ?? null, to: pick.version, range: productRange,
|
|
119
|
+
inRange: satisfiesRange(pick.version, productRange), why: product ? `the product's installed ${GRAMMAR_PACKAGE}@${product.version} does not satisfy the drawing (it fails to type-check); ${pick.version} does` : `the product has no built ${GRAMMAR_PACKAGE} install` } : null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function missingGrammarError(candidates, skillRoot) {
|
|
123
|
+
return candidates.length
|
|
124
|
+
? `no grammar candidate type-checks the drawing (${candidates.map((candidate) => candidate.source + '@' + candidate.version + (candidate.dist && !candidate.dist.ok ? ' - its dist is ' + candidate.dist.state + ': ' + candidate.dist.detail + '; run npm run build in packages/grammar' : '')).join(', ')})`
|
|
125
|
+
: `no built ${GRAMMAR_PACKAGE} (product install, ${path.join(skillRoot, 'packages', 'grammar', 'dist')} or the main worktree's; run npm run build in packages/grammar, or pass --grammar-dist)`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Resolve the grammar a draw file renders against. `typecheck` is injectable (tests). Without `file` the first
|
|
130
|
+
* candidate of the preference wins untested. Returns {ok, pick, grammarSource, productVersion, productRange,
|
|
131
|
+
* upgradeOwed, attempts:[{source, version, root, ok, errors}], error?}.
|
|
132
|
+
*/
|
|
133
|
+
export function resolveDrawGrammar({ file = null, productDir, skillRoot = SKILL_ROOT, grammarDist = null, prefer = 'auto', typecheck = typecheckDraw, claudeRoots = null }) {
|
|
134
|
+
if (!PREFERENCES.includes(prefer)) throw new Error(`--grammar must be one of ${PREFERENCES.join('|')}`);
|
|
135
|
+
const all = grammarCandidates({ productDir, skillRoot, grammarDist, claudeRoots });
|
|
136
|
+
const product = all.find((c) => c.source === 'product') ?? null;
|
|
137
|
+
const productRange = productRangeOf(productDir);
|
|
138
|
+
const candidates = prefer === 'auto' ? all : all.filter((c) => c.source === prefer);
|
|
139
|
+
const { attempts, pick } = tryGrammarCandidates(candidates, { file, productDir, typecheck });
|
|
140
|
+
const upgradeOwed = upgradeOwedFor(pick, product, productRange);
|
|
141
|
+
return {
|
|
142
|
+
ok: Boolean(pick), pick, grammarSource: pick ? `${pick.source}@${pick.version}` : null, productVersion: product?.version ?? null, productRange, upgradeOwed, attempts,
|
|
143
|
+
...(pick ? {} : { error: missingGrammarError(candidates, skillRoot) }),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** The file a grammar subpath import resolves to inside a grammar root (package exports; `import` for JS). */
|
|
148
|
+
export function grammarEntry(root, subpath = '') {
|
|
149
|
+
const pkg = readJsonFile(path.join(root, 'package.json'));
|
|
150
|
+
const key = subpath ? `./${subpath.replace(/^\//, '')}` : '.';
|
|
151
|
+
const mapped = pkg?.exports?.[key];
|
|
152
|
+
const target = typeof mapped === 'string' ? mapped : mapped?.import ?? mapped?.default ?? mapped?.style ?? null;
|
|
153
|
+
if (target) return path.join(root, target);
|
|
154
|
+
if (!subpath) return path.join(root, 'dist', 'core', 'index.js');
|
|
155
|
+
return path.join(root, subpath);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function main(argv) {
|
|
159
|
+
const val = (k) => { const i = argv.indexOf(k); return i >= 0 ? argv[i + 1] : null; };
|
|
160
|
+
const productDir = val('--product');
|
|
161
|
+
if (!productDir) { process.stderr.write('use: starci work draw-grammar --product <app dir> [--file <X.draw.tsx>] [--grammar auto|product|claude-dist] [--json]\n'); return 2; }
|
|
162
|
+
const r = resolveDrawGrammar({ file: val('--file') ? path.resolve(val('--file')) : null, productDir: path.resolve(productDir), prefer: val('--grammar') ?? 'auto', grammarDist: val('--grammar-dist') });
|
|
163
|
+
if (argv.includes('--json')) process.stdout.write(`${JSON.stringify(r, null, 2)}\n`);
|
|
164
|
+
else {
|
|
165
|
+
const summary = r.ok ? 'grammar ' + r.grammarSource : 'UNRESOLVED: ' + r.error;
|
|
166
|
+
const upgrade = r.upgradeOwed ? `; product upgrade owed ${r.upgradeOwed.from} -> ${r.upgradeOwed.to} (range ${r.upgradeOwed.range}, in range ${r.upgradeOwed.inRange})` : '';
|
|
167
|
+
const attempts = r.attempts.map((a) => ' ' + a.source + '@' + a.version + ': ' + (a.ok ? 'type-checks' : a.errors.length + ' error(s): ' + a.errors.slice(0, 3).map((e) => e.code + ' ' + e.message).join(' | '))).join('\n');
|
|
168
|
+
process.stdout.write(`${summary}${upgrade}\n${attempts}\n`);
|
|
169
|
+
}
|
|
170
|
+
return r.ok ? 0 : 1;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (isMain(import.meta.url)) {
|
|
174
|
+
try { process.exitCode = await main(process.argv.slice(2)); } catch (e) { process.stderr.write(`draw-grammar: ${e?.stack ?? e}\n`); process.exitCode = 2; }
|
|
175
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
// draw-render-page.mjs - the in-page half of draw-render.mjs. A page function is serialized to a string, so it cannot
|
|
2
|
+
// reach the module scope: measureInPage assembles one script from the source of every function below (each is a
|
|
3
|
+
// top-level declaration that reads only the page's globals and its own parameters) and runs it in the page.
|
|
4
|
+
|
|
5
|
+
function measurePage({ generic, exemptSelector, layoutAttr = 'data-draw-layout' }) {
|
|
6
|
+
const faces = [...document.fonts].map((face) => ({ family: face.family.replace(/^(["'])(.*)\1$/, '$2'), weight: face.weight, style: face.style, status: face.status }));
|
|
7
|
+
const fontData = measureFonts(generic);
|
|
8
|
+
const de = document.documentElement;
|
|
9
|
+
const pageWidth = de.clientWidth;
|
|
10
|
+
const scrollWidth = Math.max(de.scrollWidth, document.body?.scrollWidth ?? 0);
|
|
11
|
+
const overflowing = measureOverflow(pageWidth, scrollWidth);
|
|
12
|
+
const root = document.getElementById('root');
|
|
13
|
+
const accentExempt = measureAccentExempt(exemptSelector);
|
|
14
|
+
const artwork = measureArtwork();
|
|
15
|
+
const anatomy = measureAnatomy();
|
|
16
|
+
const { ownership, dom } = measureOwnership(root, layoutAttr);
|
|
17
|
+
return { ownership, dom, anatomy, accentExempt, artwork, faces, stacks: [...fontData.stacks], local: fontData.local, pageWidth, innerWidth, scrollWidth, overflowing, documentHeight: de.scrollHeight,
|
|
18
|
+
rendered: document.documentElement.dataset.drawHarness === 'component' ? Boolean(root && root.childElementCount) : null };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function visible(el) {
|
|
22
|
+
const style = getComputedStyle(el);
|
|
23
|
+
return style.display !== 'none' && style.visibility !== 'hidden';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function measureFonts(genericFamilies) {
|
|
27
|
+
const stacks = new Set();
|
|
28
|
+
const walker = document.createTreeWalker(document.body ?? document.documentElement, NodeFilter.SHOW_TEXT);
|
|
29
|
+
for (let node = walker.nextNode(); node; node = walker.nextNode()) {
|
|
30
|
+
const el = node.parentElement;
|
|
31
|
+
if (el && node.textContent.trim() && !['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEMPLATE'].includes(el.tagName) && visible(el)) { stacks.add(getComputedStyle(el).fontFamily); }
|
|
32
|
+
}
|
|
33
|
+
for (const el of document.querySelectorAll('input,textarea,select,button')) {
|
|
34
|
+
if (visible(el)) { stacks.add(getComputedStyle(el).fontFamily); }
|
|
35
|
+
}
|
|
36
|
+
const ctx = document.createElement('canvas').getContext('2d');
|
|
37
|
+
const sample = 'mmmmmmmmmmlli10WQ@#';
|
|
38
|
+
const width = (font) => { ctx.font = `72px ${font}`; return ctx.measureText(sample).width; };
|
|
39
|
+
const families = new Set([...stacks].flatMap((stack) => stack.split(',').map((family) => family.trim().replace(/^(["'])(.*)\1$/, '$2'))).filter((family) => family && !genericFamilies.includes(family.toLowerCase())));
|
|
40
|
+
const local = [...families].filter((family) => ['monospace', 'serif', 'sans-serif'].some((base) => width(`"${family}", ${base}`) !== width(base)));
|
|
41
|
+
return { stacks, local };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function measureOverflow(width, scroll) {
|
|
45
|
+
if (scroll > width) {
|
|
46
|
+
return [...document.querySelectorAll('body *')].filter((el) => el.getBoundingClientRect().right > width + 0.5)
|
|
47
|
+
.slice(0, 10).map((el) => el.tagName.toLowerCase() + (el.id ? '#' + el.id : '') + (typeof el.className === 'string' && el.className ? '.' + el.className.trim().split(/\s+/).slice(0, 3).join('.') : ''));
|
|
48
|
+
}
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function measureAccentExempt(exemptSelector) {
|
|
53
|
+
try {
|
|
54
|
+
return [...document.querySelectorAll(exemptSelector)].map((el) => el.getBoundingClientRect())
|
|
55
|
+
.filter((rect) => rect.width > 0 && rect.height > 0).map((rect) => ({ x: rect.left + scrollX, y: rect.top + scrollY, width: rect.width, height: rect.height }));
|
|
56
|
+
} catch { return []; }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function measureArtwork() {
|
|
60
|
+
try {
|
|
61
|
+
return [...document.querySelectorAll('img')].filter((el) => visible(el) && (el.currentSrc || el.src)).map((el) => {
|
|
62
|
+
const rect = el.getBoundingClientRect();
|
|
63
|
+
let x0 = rect.left, y0 = rect.top, x1 = rect.right, y1 = rect.bottom;
|
|
64
|
+
for (let ancestor = el.parentElement; ancestor; ancestor = ancestor.parentElement) {
|
|
65
|
+
const style = getComputedStyle(ancestor);
|
|
66
|
+
if (style.overflowX !== 'visible' || style.overflowY !== 'visible') {
|
|
67
|
+
const clip = ancestor.getBoundingClientRect();
|
|
68
|
+
x0 = Math.max(x0, clip.left); y0 = Math.max(y0, clip.top); x1 = Math.min(x1, clip.right); y1 = Math.min(y1, clip.bottom);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return { src: el.currentSrc || el.src, slot: el.closest('[data-artwork-slot]')?.dataset.artworkSlot ?? null,
|
|
72
|
+
x: x0 + scrollX, y: y0 + scrollY, width: Math.max(0, x1 - x0), height: Math.max(0, y1 - y0) };
|
|
73
|
+
}).filter((art) => art.width > 0 && art.height > 0);
|
|
74
|
+
} catch { return []; }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function tagOf(el) {
|
|
78
|
+
return el.tagName.toLowerCase() + (el.id ? '#' + el.id : '');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function partIn(rootEl, names) {
|
|
82
|
+
return [...rootEl.querySelectorAll('[data-grammar-part]')].find((part) => names.includes(part.dataset.grammarPart.trim()));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function measureAnatomy() {
|
|
86
|
+
const anatomy = { alerts: [], meters: [] };
|
|
87
|
+
try {
|
|
88
|
+
const probe = document.createElement('div');
|
|
89
|
+
probe.style.cssText = 'position:absolute;visibility:hidden;background-color:var(--surface)';
|
|
90
|
+
document.body.appendChild(probe);
|
|
91
|
+
const surface = getComputedStyle(probe).backgroundColor;
|
|
92
|
+
probe.remove();
|
|
93
|
+
for (const el of document.querySelectorAll('[data-grammar-component="Alert"],[data-component="Alert"]')) {
|
|
94
|
+
const alert = measureAlert(el, surface);
|
|
95
|
+
if (alert) { anatomy.alerts.push(alert); }
|
|
96
|
+
}
|
|
97
|
+
for (const el of document.querySelectorAll('[data-grammar-component="Meter"],[data-component="Meter"]')) {
|
|
98
|
+
const meter = measureMeter(el);
|
|
99
|
+
if (meter) { anatomy.meters.push(meter); }
|
|
100
|
+
}
|
|
101
|
+
} catch { /* the anatomy is advisory measurement; the static gate still runs */ }
|
|
102
|
+
return anatomy;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function measureAlert(el, surface) {
|
|
106
|
+
if ('grammarPart' in el.dataset) { return null; }
|
|
107
|
+
const indicator = partIn(el, ['alert-indicator', 'indicator']) ?? el.querySelector('.alert__indicator');
|
|
108
|
+
const title = partIn(el, ['alert-title', 'title']) ?? el.querySelector('.alert__title');
|
|
109
|
+
const box = indicator?.getBoundingClientRect();
|
|
110
|
+
return { desc: tagOf(el), background: getComputedStyle(el).backgroundColor, surface,
|
|
111
|
+
indicator: box ? { width: box.width, height: box.height } : null, tile: Boolean(el.querySelector('[data-grammar-component="IconTile"],[data-component="IconTile"]')),
|
|
112
|
+
indicatorColor: indicator ? getComputedStyle(indicator.querySelector('svg') ?? indicator).color : null, titleColor: title ? getComputedStyle(title).color : null };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function measureMeter(el) {
|
|
116
|
+
if ('grammarPart' in el.dataset) { return null; }
|
|
117
|
+
const track = partIn(el, ['meter-track', 'track']) ?? el.querySelector('.meter__track');
|
|
118
|
+
const band = el.parentElement;
|
|
119
|
+
if (!track || !band) { return null; }
|
|
120
|
+
const style = getComputedStyle(band);
|
|
121
|
+
const trackRect = track.getBoundingClientRect();
|
|
122
|
+
const segments = [...el.querySelectorAll('[data-grammar-part*="segment"]:not([data-grammar-part$="segments"]), .starci-core-meter-segment, [data-grammar-meter-segment]')]
|
|
123
|
+
.map((part) => part.getBoundingClientRect()).map((rect) => ({ x: rect.left, width: rect.width }));
|
|
124
|
+
const segmented = 'grammarMeterSegments' in el.dataset || 'segments' in el.dataset || segments.length > 1;
|
|
125
|
+
return { desc: tagOf(el), segmented, track: { width: trackRect.width, height: trackRect.height },
|
|
126
|
+
band: { width: band.clientWidth - Number.parseFloat(style.paddingLeft || '0') - Number.parseFloat(style.paddingRight || '0') }, segments };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function marked(el) {
|
|
130
|
+
return 'component' in el.dataset || [...el.attributes].some((attr) => attr.name.startsWith('data-grammar-')) || [...el.classList].some((name) => name.startsWith('starci-core-'));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function paints(el) {
|
|
134
|
+
const style = getComputedStyle(el);
|
|
135
|
+
if (style.display === 'none' || style.visibility === 'hidden') { return false; }
|
|
136
|
+
if (['img', 'svg', 'canvas', 'video', 'picture'].includes(el.tagName.toLowerCase())) { return true; }
|
|
137
|
+
if ([...el.childNodes].some((node) => node.nodeType === 3 && node.textContent.trim())) { return true; }
|
|
138
|
+
if (style.backgroundColor && !/^(?:transparent|rgba\(0, 0, 0, 0\))$/.test(style.backgroundColor)) { return true; }
|
|
139
|
+
if (style.backgroundImage && style.backgroundImage !== 'none') { return true; }
|
|
140
|
+
if (['Top', 'Right', 'Bottom', 'Left'].some((side) => Number.parseFloat(style[`border${side}Width`]) > 0 && style[`border${side}Style`] !== 'none' && !/rgba\(0, 0, 0, 0\)/.test(style[`border${side}Color`]))) { return true; }
|
|
141
|
+
return Boolean(style.boxShadow && style.boxShadow !== 'none');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function measureOwnership(rootEl, layoutAttribute) {
|
|
145
|
+
let ownership = null, dom = null;
|
|
146
|
+
if (document.documentElement.dataset.drawHarness !== 'component' || !rootEl) { return { ownership, dom }; }
|
|
147
|
+
const components = new Set();
|
|
148
|
+
const unowned = [];
|
|
149
|
+
let layoutElements = 0;
|
|
150
|
+
for (const el of rootEl.querySelectorAll('*')) {
|
|
151
|
+
if ('component' in el.dataset) { components.add(el.dataset.component); }
|
|
152
|
+
layoutElements += Number(el.hasAttribute(layoutAttribute));
|
|
153
|
+
if ((el.closest('svg') && el.tagName.toLowerCase() !== 'svg') || !paints(el)) { continue; }
|
|
154
|
+
const owner = ownerOf(el, rootEl, layoutAttribute);
|
|
155
|
+
if (owner !== 'grammar') { unowned.push(ownershipLabel(el, owner)); }
|
|
156
|
+
}
|
|
157
|
+
ownership = { components: [...components].sort((left, right) => { if (left < right) { return -1; } if (left > right) { return 1; } return 0; }), layoutElements, unownedCount: unowned.length, unowned: unowned.slice(0, 20) };
|
|
158
|
+
const clone = document.documentElement.cloneNode(true);
|
|
159
|
+
for (const el of clone.querySelectorAll('[data-component]')) {
|
|
160
|
+
if (!('grammarComponent' in el.dataset)) { el.dataset.grammarComponent = el.dataset.component; }
|
|
161
|
+
}
|
|
162
|
+
for (const el of clone.querySelectorAll('script')) { el.remove(); }
|
|
163
|
+
dom = `<!doctype html>
|
|
164
|
+
${clone.outerHTML}`;
|
|
165
|
+
return { ownership, dom };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function ownerOf(el, rootEl, layoutAttribute) {
|
|
169
|
+
for (let ancestor = el; ancestor && ancestor !== rootEl; ancestor = ancestor.parentElement) {
|
|
170
|
+
if (marked(ancestor)) { return 'grammar'; }
|
|
171
|
+
if (ancestor.hasAttribute(layoutAttribute)) { return 'layout'; }
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function ownershipLabel(el, owner) {
|
|
177
|
+
return el.tagName.toLowerCase() + (typeof el.className === 'string' && el.className ? '.' + el.className.trim().split(/\s+/).slice(0, 3).join('.') : '') + (el.textContent.trim() ? ' "' + el.textContent.trim().slice(0, 40) + '"' : '') + ` (${owner ?? 'no owner'})`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** The expression that declares `functions` and returns what `entry` makes of `arg` (JSON data) in the page. */
|
|
181
|
+
function pageScript(entry, functions, arg) {
|
|
182
|
+
return `(() => {\n${functions.map(String).join('\n')}\nreturn ${entry.name}(${JSON.stringify(arg)});\n})()`;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** What the page measures: `arg` is {generic, exemptSelector, layoutAttr}. */
|
|
186
|
+
export function measureInPage(page, arg) {
|
|
187
|
+
return page.evaluate(pageScript(measurePage, [measurePage, visible, measureFonts, measureOverflow, measureAccentExempt, measureArtwork, tagOf, partIn, measureAnatomy, measureAlert, measureMeter, marked, paints, measureOwnership, ownerOf, ownershipLabel], arg));
|
|
188
|
+
}
|