@starci/hfs 4.0.8 → 4.0.10
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/LICENSE +21 -0
- package/README.md +81 -39
- package/emit/contracts.mjs +56 -9
- package/emit/db-types.mjs +169 -0
- package/emit/events.mjs +41 -0
- package/emit/operations-worker.mjs +1 -1
- package/emit/operations.mjs +2 -2
- package/emit/schema-worker.mjs +1 -1
- package/lint/bound-sys.cjs +50 -0
- package/lint/run.mjs +131 -24
- package/package.json +16 -10
- package/report/sonar.mjs +8 -8
- package/runtime/engine/admission.mjs +308 -0
- package/runtime/engine/canonical-json.mjs +10 -0
- package/runtime/engine/config.mjs +486 -0
- package/runtime/engine/db/blob.mjs +315 -0
- package/runtime/engine/db/ledger-paths.mjs +83 -0
- package/runtime/engine/db/ledger.mjs +1118 -0
- package/runtime/engine/db/machine-connection.mjs +135 -0
- package/runtime/engine/db/machine-schema.mjs +84 -0
- package/runtime/engine/db/machine.mjs +1367 -0
- package/runtime/engine/db/migrations/machine/0001-init.sql +924 -0
- package/runtime/engine/db/migrations/runtime/0001-init.sql +1108 -0
- package/runtime/engine/db/provider-reservations.mjs +101 -0
- package/runtime/engine/digest.mjs +16 -0
- package/runtime/engine/invalid-config.mjs +15 -0
- package/runtime/engine/orca-config.mjs +27 -0
- package/runtime/engine/refuse.mjs +11 -0
- package/runtime/engine/secrets.mjs +130 -0
- package/runtime/knowledge/hfs/canon-pins.yaml +162 -31
- package/runtime/knowledge/hfs/peer-integrations.yaml +2 -2
- package/runtime/knowledge/hfs/rules.yaml +2059 -0
- package/runtime/knowledge/hfs/slots.yaml +912 -120
- package/runtime/knowledge/patterns/be/api.yaml +369 -0
- package/runtime/knowledge/patterns/be/cli.yaml +297 -0
- package/runtime/knowledge/patterns/be/domain.yaml +113 -0
- package/runtime/knowledge/patterns/be/event-bus.yaml +285 -0
- package/runtime/knowledge/patterns/be/jobs.yaml +261 -0
- package/runtime/knowledge/patterns/be/projections.yaml +178 -0
- package/runtime/knowledge/patterns/be/queues.yaml +236 -0
- package/runtime/knowledge/patterns/be/reactors.yaml +140 -0
- package/runtime/knowledge/patterns/be/realtime.yaml +222 -0
- package/runtime/knowledge/patterns/be/saga.yaml +248 -0
- package/runtime/knowledge/patterns/be/webhooks.yaml +221 -0
- package/runtime/knowledge/patterns/fe/folder.yaml +131 -25
- package/runtime/knowledge/sonar-gate.yaml +52 -18
- package/runtime/modules/kernel/failure-codes.yaml +611 -51
- package/runtime/modules/models/registry.yaml +553 -0
- package/runtime/modules/models/runtimes.yaml +695 -0
- package/runtime/modules/ops/_labels.yaml +53 -0
- package/runtime/scripts/api/fs/forbidden-root.mjs +51 -0
- package/runtime/scripts/api/fs/is-link-like.mjs +25 -0
- package/runtime/scripts/api/fs/lib.mjs +16 -2
- package/runtime/scripts/api/fs/safe-remove.mjs +12 -132
- package/runtime/scripts/api/git/config-get.mjs +10 -0
- package/runtime/scripts/api/git/diff.mjs +5 -0
- package/runtime/scripts/api/git/lib.mjs +18 -26
- package/runtime/scripts/api/git/log.mjs +5 -0
- package/runtime/scripts/api/git/ls-files.mjs +6 -0
- package/runtime/scripts/api/git/ls-tree.mjs +5 -0
- package/runtime/scripts/api/git/merge-base.mjs +9 -0
- package/runtime/scripts/api/git/remote.mjs +5 -0
- package/runtime/scripts/api/git/rev-parse-query.mjs +5 -0
- package/runtime/scripts/api/git/show.mjs +6 -0
- package/runtime/scripts/api/node/lib.mjs +14 -0
- package/runtime/scripts/api/node/spawn-node.mjs +6 -0
- package/runtime/scripts/api/process/lib.mjs +111 -0
- package/runtime/scripts/api/process/owned-process.mjs +78 -0
- package/runtime/scripts/api/process/resolve-real-tool.mjs +46 -0
- package/runtime/scripts/api/process/run-program.mjs +7 -0
- package/runtime/scripts/api/process/stop-owned-process.mjs +9 -0
- package/runtime/scripts/api/sops/decrypt.mjs +9 -12
- package/runtime/scripts/api/sops/lib.mjs +255 -0
- package/runtime/scripts/api/sops/seal.mjs +22 -0
- package/runtime/scripts/connectors/lib.mjs +488 -0
- package/runtime/scripts/hfs/architecture/ast-walks.mjs +220 -0
- package/runtime/scripts/hfs/architecture/backend.mjs +16 -23
- package/runtime/scripts/hfs/architecture/background-unowned.mjs +16 -34
- package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +3 -1
- package/runtime/scripts/hfs/architecture/clones.mjs +4 -4
- package/runtime/scripts/hfs/architecture/config.mjs +9 -6
- package/runtime/scripts/hfs/architecture/connection-map.mjs +20 -8
- package/runtime/scripts/hfs/architecture/context-coupling.mjs +88 -0
- package/runtime/scripts/hfs/architecture/context-map.mjs +237 -0
- package/runtime/scripts/hfs/architecture/context-owner.mjs +82 -0
- package/runtime/scripts/hfs/architecture/context-platform-tables.mjs +47 -0
- package/runtime/scripts/hfs/architecture/context-transaction.mjs +60 -0
- package/runtime/scripts/hfs/architecture/contract-fixture-guard.mjs +4 -10
- package/runtime/scripts/hfs/architecture/contracts.mjs +33 -162
- package/runtime/scripts/hfs/architecture/dead-exports.mjs +58 -29
- package/runtime/scripts/hfs/architecture/doc-language.mjs +1 -1
- package/runtime/scripts/hfs/architecture/entrypoint.mjs +2 -1
- package/runtime/scripts/hfs/architecture/fe-slot-allows.mjs +2 -2
- package/runtime/scripts/hfs/architecture/feature-shape.mjs +5 -2
- package/runtime/scripts/hfs/architecture/framework-pinned.mjs +1 -1
- package/runtime/scripts/hfs/architecture/frontend.mjs +17 -43
- package/runtime/scripts/hfs/architecture/hfs.mjs +24 -39
- package/runtime/scripts/hfs/architecture/i18n-keys.mjs +1 -1
- package/runtime/scripts/hfs/architecture/index.mjs +17 -4
- package/runtime/scripts/hfs/architecture/machine-ast.mjs +44 -11
- package/runtime/scripts/hfs/architecture/managed-scripts.mjs +27 -0
- package/runtime/scripts/hfs/architecture/module-per-transport.mjs +30 -6
- package/runtime/scripts/hfs/architecture/next-data.mjs +22 -63
- package/runtime/scripts/hfs/architecture/owners.mjs +35 -15
- package/runtime/scripts/hfs/architecture/register-once.mjs +2 -1
- package/runtime/scripts/hfs/architecture/registration.mjs +23 -81
- package/runtime/scripts/hfs/architecture/required-files.mjs +20 -12
- package/runtime/scripts/hfs/architecture/schema-owner.mjs +17 -114
- package/runtime/scripts/hfs/architecture/server-action.mjs +24 -0
- package/runtime/scripts/hfs/architecture/side-boundary.mjs +16 -0
- package/runtime/scripts/hfs/architecture/source-names.mjs +27 -147
- package/runtime/scripts/hfs/architecture/sql-owner.mjs +59 -18
- package/runtime/scripts/hfs/architecture/sql-returning.mjs +64 -0
- package/runtime/scripts/hfs/architecture/supabase-ast.mjs +111 -0
- package/runtime/scripts/hfs/architecture/supabase-be.mjs +131 -0
- package/runtime/scripts/hfs/architecture/supabase-tables.mjs +42 -0
- package/runtime/scripts/hfs/architecture/supabase.mjs +434 -0
- package/runtime/scripts/hfs/architecture/surface.mjs +9 -3
- package/runtime/scripts/hfs/architecture/test-world-files.mjs +9 -19
- package/runtime/scripts/hfs/architecture/tiers.mjs +47 -5
- package/runtime/scripts/hfs/architecture/transport-owner.mjs +3 -3
- package/runtime/scripts/hfs/architecture/typescript.mjs +12 -26
- package/runtime/scripts/hfs/architecture.mjs +4 -4
- package/runtime/scripts/hfs/check.mjs +75 -72
- package/runtime/scripts/hfs/coverage-scope.mjs +162 -0
- package/runtime/scripts/hfs/declaration-shape.mjs +62 -0
- package/runtime/scripts/hfs/declaration-slots.mjs +75 -0
- package/runtime/scripts/hfs/edition-slots.mjs +66 -0
- package/runtime/scripts/hfs/manifest-shape.mjs +109 -32
- package/runtime/scripts/hfs/param-names.mjs +18 -0
- package/runtime/scripts/hfs/path-findings.mjs +1 -1
- package/runtime/scripts/hfs/repo-identity.mjs +9 -6
- package/runtime/scripts/hfs/rule-params-shape.mjs +50 -0
- package/runtime/scripts/hfs/rules/app-root.mjs +51 -0
- package/runtime/scripts/hfs/rules/cli.mjs +37 -0
- package/runtime/scripts/hfs/rules/contract-compat.mjs +71 -0
- package/runtime/scripts/hfs/rules/contract.mjs +10 -9
- package/runtime/scripts/hfs/rules/database-config.mjs +102 -0
- package/runtime/scripts/hfs/rules/database-constants.mjs +13 -0
- package/runtime/scripts/hfs/rules/database-migrations.mjs +111 -0
- package/runtime/scripts/hfs/rules/database-plpgsql.mjs +233 -0
- package/runtime/scripts/hfs/rules/database-sql.mjs +336 -0
- package/runtime/scripts/hfs/rules/database.mjs +68 -0
- package/runtime/scripts/hfs/rules/deps.mjs +1 -1
- package/runtime/scripts/hfs/rules/docker.mjs +259 -0
- package/runtime/scripts/hfs/rules/edition.mjs +111 -0
- package/runtime/scripts/hfs/rules/event-bus.mjs +153 -0
- package/runtime/scripts/hfs/rules/fe-contract-documents.mjs +1 -1
- package/runtime/scripts/hfs/rules/fe-no-tests.mjs +6 -5
- package/runtime/scripts/hfs/rules/frontend-tree.mjs +9 -9
- package/runtime/scripts/hfs/rules/integration-specs.mjs +3 -6
- package/runtime/scripts/hfs/rules/kinds.mjs +84 -0
- package/runtime/scripts/hfs/rules/lint-suppression.mjs +1 -1
- package/runtime/scripts/hfs/rules/monorepo.mjs +179 -0
- package/runtime/scripts/hfs/rules/peer-integrations.mjs +3 -3
- package/runtime/scripts/hfs/rules/pipeline.mjs +11 -21
- package/runtime/scripts/hfs/rules/proof-commands.mjs +2 -2
- package/runtime/scripts/hfs/rules/read.mjs +1 -1
- package/runtime/scripts/hfs/rules/repo-local-checks.mjs +2 -2
- package/runtime/scripts/hfs/rules/saga.mjs +170 -0
- package/runtime/scripts/hfs/rules/secrets.mjs +3 -3
- package/runtime/scripts/hfs/rules/services.mjs +220 -0
- package/runtime/scripts/hfs/rules/spec-placement.mjs +1 -1
- package/runtime/scripts/hfs/rules/stacks.mjs +3 -3
- package/runtime/scripts/hfs/rules/supabase-secrets.mjs +89 -0
- package/runtime/scripts/hfs/rules/test-topology.mjs +2 -2
- package/runtime/scripts/hfs/secret.mjs +209 -0
- package/runtime/scripts/hfs/slots.mjs +78 -108
- package/runtime/scripts/hfs/sql/pg-parse.mjs +46 -0
- package/runtime/scripts/hfs/test-world-slot.mjs +6 -0
- package/runtime/scripts/hfs/tree.mjs +5 -4
- package/runtime/scripts/hfs/typescript-programs.mjs +0 -5
- package/runtime/scripts/hfs/view.mjs +3 -2
- package/runtime/scripts/lib/clip.mjs +19 -0
- package/runtime/scripts/lib/display-names.mjs +259 -0
- package/runtime/scripts/lib/dockerfile.mjs +124 -0
- package/runtime/scripts/lib/env.mjs +10 -0
- package/runtime/scripts/lib/event-contract.mjs +103 -0
- package/runtime/scripts/lib/example-refs.mjs +158 -0
- package/runtime/scripts/lib/fs-kind.mjs +14 -4
- package/runtime/scripts/lib/git.mjs +36 -0
- package/runtime/scripts/lib/graphql-contract.mjs +1 -1
- package/runtime/scripts/lib/i18n.mjs +86 -0
- package/runtime/scripts/lib/json-schema.mjs +52 -0
- package/runtime/scripts/lib/json.mjs +13 -3
- package/runtime/scripts/lib/language.mjs +24 -5
- package/runtime/scripts/lib/list.mjs +20 -0
- package/runtime/scripts/lib/mutation-fence.mjs +15 -0
- package/runtime/scripts/lib/package-at.mjs +37 -0
- package/runtime/scripts/lib/path-key.mjs +57 -2
- package/runtime/scripts/lib/process-identity.mjs +6 -0
- package/runtime/scripts/lib/read-text.mjs +34 -0
- package/runtime/scripts/lib/read-yaml.mjs +18 -0
- package/runtime/scripts/lib/redact.mjs +161 -0
- package/runtime/scripts/lib/regex.mjs +4 -0
- package/runtime/scripts/lib/same-text.mjs +5 -0
- package/runtime/scripts/lib/secret-patterns.mjs +4 -3
- package/runtime/scripts/lib/sleep-sync.mjs +2 -1
- package/runtime/scripts/lib/sleep.mjs +7 -0
- package/runtime/scripts/lib/sops-envelope.mjs +112 -0
- package/runtime/scripts/lib/source-phrases.mjs +34 -0
- package/runtime/scripts/lib/sqlite.mjs +21 -0
- package/runtime/scripts/lib/stack-services.mjs +7 -31
- package/runtime/scripts/lib/ts-ast.mjs +35 -0
- package/runtime/scripts/lib/walk.mjs +4 -3
- package/scaffold/add-app.mjs +174 -0
- package/scaffold/add-cli-lite.mjs +257 -0
- package/scaffold/add-inbox-lite.mjs +314 -0
- package/scaffold/add-table.mjs +344 -0
- package/scaffold/add.mjs +299 -0
- package/scaffold/app.mjs +358 -69
- package/scaffold/edition-gate.mjs +25 -0
- package/scaffold/image.mjs +19 -0
- package/scaffold/lite-exports.mjs +186 -0
- package/scaffold/service.mjs +32 -26
- package/scaffold/supabase-ports.mjs +26 -0
- package/src/main.mjs +327 -0
- package/sync/cli.mjs +6 -9
- package/sync/format.mjs +1 -1
- package/sync/hygiene.mjs +10 -10
- package/sync/index.mjs +167 -104
- package/sync/managed.mjs +19 -11
- package/sync/sonar-key.mjs +5 -0
- package/templates/app/ci-workflows/github/workflows/ci.yml +14 -14
- package/templates/app/ci-workflows/github/workflows/images.yml +37 -0
- package/templates/app/ci-workflows/sonar-steps.yml +9 -0
- package/templates/app/ci-workflows-lite/github/workflows/ci.yml +66 -0
- package/templates/app/ci-workflows-lite/github/workflows/db-deploy.yml +31 -0
- package/templates/app/ci-workflows-lite/github/workflows/images.yml +37 -0
- package/templates/app/codegen-lite/scripts/codegen.mjs +32 -0
- package/templates/app/docker-ignore/dockerignore +50 -0
- package/templates/app/gitignore-lite +35 -0
- package/templates/app/hooks/husky/pre-commit +3 -8
- package/templates/app/hooks/husky/pre-push +45 -6
- package/templates/app/package-scripts/core.json +8 -0
- package/templates/app/package-scripts/package.json +3 -10
- package/templates/app/package-scripts-lite/package.json +15 -0
- package/templates/app/quality-config/codecov.yml +15 -3
- package/templates/app/quality-config/sonar-base.properties +4 -0
- package/templates/app/quality-config/sonar-project.properties +1 -4
- package/templates/app/quality-config-lite/sonar-project.properties +5 -0
- package/templates/app/skeleton/.starcistacks/dev/seeds/primary-notes.sql +5 -0
- package/templates/app/skeleton/.starciwork/features/system-health/index.yaml +4 -0
- package/templates/app/skeleton/.starciwork/{features/index.yaml → index.yaml} +2 -2
- package/templates/app/skeleton/README.md +7 -5
- package/templates/app/skeleton-lite/.sops.yaml +3 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/environment.json +5 -0
- package/templates/app/skeleton-lite/.starcistacks/dev/secrets/.gitkeep +1 -0
- package/templates/app/skeleton-lite/.starciwork/shell/index.yaml +55 -0
- package/templates/app/skeleton-lite/README.md +40 -0
- package/templates/app/skeleton-lite/supabase/config.toml +41 -0
- package/templates/app/skeleton-lite/supabase/migrations/__timestamp___baseline.sql +56 -0
- package/templates/app/skeleton-lite/supabase/seed.sql +0 -0
- package/templates/app/task-graph/turbo.json +23 -0
- package/templates/app/tool-config-lite/prettierignore +2 -0
- package/templates/app/tool-config-lite/prettierrc +1 -0
- package/templates/app/upgrade-full/.starcistacks/application-stacks.yaml +38 -0
- package/templates/app/upgrade-full/.starcistacks/dev/README.md +10 -0
- package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/api.yaml +13 -0
- package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/compose.yaml +3 -0
- package/templates/app/upgrade-full/.starcistacks/dev/infra/compose/postgres.yaml +12 -0
- package/templates/app/upgrade-full/.starcistacks/dev/infra/metadata.json +7 -0
- package/templates/be/common/main-listen.ts.partial +14 -0
- package/templates/be/common/sql-ident.ts.partial +7 -0
- package/templates/be/common/sql-tag.ts.partial +5 -0
- package/templates/be/image/api/Dockerfile +38 -0
- package/templates/be/image/cli/Dockerfile +38 -0
- package/templates/be/image/worker/Dockerfile +36 -0
- package/templates/be/inbox/database-client.ts.tpl +50 -0
- package/templates/be/inbox/index.ts.tpl +2 -0
- package/templates/be/inbox/migration.sql.tpl +29 -0
- package/templates/be/inbox/module-definition.ts.tpl +6 -0
- package/templates/be/inbox/module.ts.tpl +14 -0
- package/templates/be/inbox/options.ts.tpl +2 -0
- package/templates/be/inbox/service.ts.tpl +33 -0
- package/templates/be/inbox/sql.ts.tpl +16 -0
- package/templates/be/inbox/webhook-door.ts.tpl +36 -0
- package/templates/be/patterns/api/application.command.http.ts.tpl +10 -0
- package/templates/be/patterns/api/application.command.ts.tpl +10 -0
- package/templates/be/patterns/api/application.contracts.ts.tpl +11 -0
- package/templates/be/patterns/api/application.handler.http.ts.tpl +22 -0
- package/templates/be/patterns/api/application.handler.ts.tpl +22 -0
- package/templates/be/patterns/api/feature.index.http.ts.tpl +1 -0
- package/templates/be/patterns/api/feature.index.ts.tpl +1 -0
- package/templates/be/patterns/api/feature.module.ts.tpl +6 -0
- package/templates/be/patterns/api/graphql.input.ts.tpl +13 -0
- package/templates/be/patterns/api/graphql.mapper.ts.tpl +9 -0
- package/templates/be/patterns/api/graphql.module.ts.tpl +7 -0
- package/templates/be/patterns/api/graphql.resolver.ts.tpl +27 -0
- package/templates/be/patterns/api/graphql.type.ts.tpl +9 -0
- package/templates/be/patterns/api/http.controller.ts.tpl +27 -0
- package/templates/be/patterns/api/http.mapper.ts.tpl +9 -0
- package/templates/be/patterns/api/http.module.ts.tpl +7 -0
- package/templates/be/patterns/api/http.request.ts.tpl +10 -0
- package/templates/be/patterns/api/http.response.ts.tpl +5 -0
- package/templates/be/patterns/cli/group.cli.spec.ts.tpl +15 -0
- package/templates/be/patterns/cli/group.cli.ts.tpl +11 -0
- package/templates/be/patterns/cli/group.module.ts.tpl +7 -0
- package/templates/be/patterns/cli/sub.run.cli.spec.ts.tpl +17 -0
- package/templates/be/patterns/cli/sub.run.cli.ts.tpl +15 -0
- package/templates/be/patterns/event-bus/platform/errors/event-bus.error.ts.tpl +22 -0
- package/templates/be/patterns/event-bus/platform/event-bus.config.ts.tpl +19 -0
- package/templates/be/patterns/event-bus/platform/event-bus.contracts.ts.tpl +58 -0
- package/templates/be/patterns/event-bus/platform/event-bus.decorators.ts.tpl +46 -0
- package/templates/be/patterns/event-bus/platform/event-bus.log-events.ts.tpl +12 -0
- package/templates/be/patterns/event-bus/platform/event-bus.module-definition.ts.tpl +10 -0
- package/templates/be/patterns/event-bus/platform/event-bus.module.ts.tpl +52 -0
- package/templates/be/patterns/event-bus/platform/event-bus.options.ts.tpl +19 -0
- package/templates/be/patterns/event-bus/platform/event-bus.port.ts.tpl +28 -0
- package/templates/be/patterns/event-bus/platform/event-bus.service.spec.ts.tpl +165 -0
- package/templates/be/patterns/event-bus/platform/event-bus.service.ts.tpl +93 -0
- package/templates/be/patterns/event-bus/platform/event-envelope.policy.spec.ts.tpl +90 -0
- package/templates/be/patterns/event-bus/platform/event-envelope.policy.ts.tpl +58 -0
- package/templates/be/patterns/event-bus/platform/event-relay.service.spec.ts.tpl +136 -0
- package/templates/be/patterns/event-bus/platform/event-relay.service.ts.tpl +56 -0
- package/templates/be/patterns/event-bus/platform/event-runner.service.spec.ts.tpl +272 -0
- package/templates/be/patterns/event-bus/platform/event-runner.service.ts.tpl +128 -0
- package/templates/be/patterns/event-bus/platform/event-transport.port.ts.tpl +180 -0
- package/templates/be/patterns/event-bus/platform/event.policy.spec.ts.tpl +67 -0
- package/templates/be/patterns/event-bus/platform/event.policy.ts.tpl +38 -0
- package/templates/be/patterns/event-bus/platform/index.ts.tpl +11 -0
- package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.spec.ts.tpl +348 -0
- package/templates/be/patterns/event-bus/platform/kafka-event-transport.client.ts.tpl +241 -0
- package/templates/be/patterns/event-bus/platform/messages/event-bus.messages.ts.tpl +15 -0
- package/templates/be/patterns/event-bus/platform/persistence/connection.ts.tpl +8 -0
- package/templates/be/patterns/event-bus/platform/persistence/entities/event-outbox.entity.ts.tpl +37 -0
- package/templates/be/patterns/event-bus/platform/persistence/event-bus.rows.ts.tpl +11 -0
- package/templates/be/patterns/event-bus/platform/persistence/event-bus.sql.ts.tpl +12 -0
- package/templates/be/patterns/event-bus/platform/persistence/migrations/1789800009000-create-event-outbox.ts.tpl +26 -0
- package/templates/be/patterns/jobs/job.index.ts.tpl +1 -0
- package/templates/be/patterns/jobs/job.module.ts.tpl +6 -0
- package/templates/be/patterns/jobs/job.processor.ts.tpl +20 -0
- package/templates/be/patterns/jobs/job.queue.module.ts.tpl +20 -0
- package/templates/be/patterns/jobs/job.step.ts.tpl +19 -0
- package/templates/be/patterns/jobs/platform/fenced.processor.spec.ts.tpl +31 -0
- package/templates/be/patterns/jobs/platform/fenced.processor.ts.tpl +13 -0
- package/templates/be/patterns/jobs/platform/job-claim.service.spec.ts.tpl +150 -0
- package/templates/be/patterns/jobs/platform/job-claim.service.ts.tpl +100 -0
- package/templates/be/patterns/jobs/platform/job-runner.service.spec.ts.tpl +138 -0
- package/templates/be/patterns/jobs/platform/job-runner.service.ts.tpl +69 -0
- package/templates/be/patterns/jobs/platform/jobs.config.ts.tpl +11 -0
- package/templates/be/patterns/jobs/platform/jobs.contracts.ts.tpl +62 -0
- package/templates/be/patterns/jobs/platform/jobs.decorators.ts.tpl +31 -0
- package/templates/be/patterns/jobs/platform/jobs.error.ts.tpl +22 -0
- package/templates/be/patterns/jobs/platform/jobs.index.ts.tpl +11 -0
- package/templates/be/patterns/jobs/platform/jobs.log-events.ts.tpl +9 -0
- package/templates/be/patterns/jobs/platform/jobs.messages.ts.tpl +15 -0
- package/templates/be/patterns/jobs/platform/jobs.module-definition.ts.tpl +10 -0
- package/templates/be/patterns/jobs/platform/jobs.module.ts.tpl +32 -0
- package/templates/be/patterns/jobs/platform/jobs.options.ts.tpl +11 -0
- package/templates/be/patterns/jobs/platform/jobs.port.ts.tpl +36 -0
- package/templates/be/patterns/jobs/platform/persistence.connection.ts.tpl +8 -0
- package/templates/be/patterns/jobs/platform/persistence.job.entity.ts.tpl +53 -0
- package/templates/be/patterns/jobs/platform/persistence.jobs.rows.ts.tpl +13 -0
- package/templates/be/patterns/jobs/platform/persistence.jobs.sql.ts.tpl +33 -0
- package/templates/be/patterns/jobs/platform/persistence.migration.ts.tpl +29 -0
- package/templates/be/patterns/outbox/platform/index.ts.tpl +2 -0
- package/templates/be/patterns/outbox/platform/outbox-relay.policy.spec.ts.tpl +96 -0
- package/templates/be/patterns/outbox/platform/outbox-relay.policy.ts.tpl +78 -0
- package/templates/be/patterns/outbox/platform/outbox.log-events.ts.tpl +5 -0
- package/templates/be/patterns/projections/migration.ts.tpl +18 -0
- package/templates/be/patterns/projections/persistence.connection.ts.tpl +8 -0
- package/templates/be/patterns/projections/projection.entity.ts.tpl +9 -0
- package/templates/be/patterns/projections/projection.index.ts.tpl +3 -0
- package/templates/be/patterns/projections/projection.module-definition.ts.tpl +7 -0
- package/templates/be/patterns/projections/projection.module.ts.tpl +18 -0
- package/templates/be/patterns/projections/projection.options.ts.tpl +2 -0
- package/templates/be/patterns/projections/projection.projection.spec.ts.tpl +55 -0
- package/templates/be/patterns/projections/projection.projection.ts.tpl +21 -0
- package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.spec.ts.tpl +142 -0
- package/templates/be/patterns/queues/platform/bullmq-queue-transport.client.ts.tpl +81 -0
- package/templates/be/patterns/queues/platform/persistence.connection.ts.tpl +8 -0
- package/templates/be/patterns/queues/platform/persistence.migration.ts.tpl +25 -0
- package/templates/be/patterns/queues/platform/persistence.queue-outbox.entity.ts.tpl +25 -0
- package/templates/be/patterns/queues/platform/persistence.queue.rows.ts.tpl +9 -0
- package/templates/be/patterns/queues/platform/persistence.queue.sql.ts.tpl +12 -0
- package/templates/be/patterns/queues/platform/queue-outbox.service.spec.ts.tpl +48 -0
- package/templates/be/patterns/queues/platform/queue-outbox.service.ts.tpl +17 -0
- package/templates/be/patterns/queues/platform/queue-relay.service.spec.ts.tpl +128 -0
- package/templates/be/patterns/queues/platform/queue-relay.service.ts.tpl +48 -0
- package/templates/be/patterns/queues/platform/queue-transport.port.ts.tpl +115 -0
- package/templates/be/patterns/queues/platform/queue-worker.service.spec.ts.tpl +91 -0
- package/templates/be/patterns/queues/platform/queue-worker.service.ts.tpl +45 -0
- package/templates/be/patterns/queues/platform/queue.config.ts.tpl +15 -0
- package/templates/be/patterns/queues/platform/queue.contracts.ts.tpl +31 -0
- package/templates/be/patterns/queues/platform/queue.decorators.ts.tpl +45 -0
- package/templates/be/patterns/queues/platform/queue.index.ts.tpl +7 -0
- package/templates/be/patterns/queues/platform/queue.log-events.ts.tpl +5 -0
- package/templates/be/patterns/queues/platform/queue.module-definition.ts.tpl +10 -0
- package/templates/be/patterns/queues/platform/queue.module.ts.tpl +62 -0
- package/templates/be/patterns/queues/platform/queue.options.ts.tpl +22 -0
- package/templates/be/patterns/queues/platform/queue.policy.spec.ts.tpl +19 -0
- package/templates/be/patterns/queues/platform/queue.policy.ts.tpl +11 -0
- package/templates/be/patterns/queues/platform/queue.port.ts.tpl +14 -0
- package/templates/be/patterns/queues/queue.index.ts.tpl +3 -0
- package/templates/be/patterns/queues/queue.module.ts.tpl +6 -0
- package/templates/be/patterns/queues/queue.queue.ts.tpl +23 -0
- package/templates/be/patterns/reactors/application.command.ts.tpl +10 -0
- package/templates/be/patterns/reactors/application.contracts.ts.tpl +12 -0
- package/templates/be/patterns/reactors/application.handler.ts.tpl +22 -0
- package/templates/be/patterns/reactors/consumer.ts.tpl +19 -0
- package/templates/be/patterns/reactors/message.module.ts.tpl +20 -0
- package/templates/be/patterns/reactors/reactor.index.ts.tpl +1 -0
- package/templates/be/patterns/reactors/reactor.module.ts.tpl +6 -0
- package/templates/be/patterns/realtime/channel.graphql.module.ts.tpl +6 -0
- package/templates/be/patterns/realtime/channel.index.ts.tpl +1 -0
- package/templates/be/patterns/realtime/channel.input.ts.tpl +12 -0
- package/templates/be/patterns/realtime/channel.subscription.spec.ts.tpl +51 -0
- package/templates/be/patterns/realtime/channel.subscription.ts.tpl +32 -0
- package/templates/be/patterns/realtime/channel.type.ts.tpl +9 -0
- package/templates/be/patterns/saga/saga.compensate.command.ts.tpl +11 -0
- package/templates/be/patterns/saga/saga.compensate.contracts.ts.tpl +7 -0
- package/templates/be/patterns/saga/saga.compensate.handler.ts.tpl +22 -0
- package/templates/be/patterns/saga/saga.compensation.ts.tpl +21 -0
- package/templates/be/patterns/saga/saga.complete.command.ts.tpl +11 -0
- package/templates/be/patterns/saga/saga.complete.contracts.ts.tpl +7 -0
- package/templates/be/patterns/saga/saga.complete.handler.ts.tpl +22 -0
- package/templates/be/patterns/saga/saga.done.consumer.ts.tpl +21 -0
- package/templates/be/patterns/saga/saga.failed.consumer.ts.tpl +21 -0
- package/templates/be/patterns/saga/saga.index.ts.tpl +1 -0
- package/templates/be/patterns/saga/saga.message.module.ts.tpl +23 -0
- package/templates/be/patterns/saga/saga.module.ts.tpl +20 -0
- package/templates/be/patterns/saga/saga.service.spec.ts.tpl +70 -0
- package/templates/be/patterns/saga/saga.service.ts.tpl +44 -0
- package/templates/be/patterns/saga/saga.state.ts.tpl +14 -0
- package/templates/be/patterns/saga/saga.step.ts.tpl +11 -0
- package/templates/be/patterns/saga/saga.undo.command.ts.tpl +10 -0
- package/templates/be/patterns/saga/saga.undo.contracts.ts.tpl +5 -0
- package/templates/be/patterns/saga/saga.undo.handler.ts.tpl +21 -0
- package/templates/be/patterns/webhooks/webhook.door.spec.ts.tpl +72 -0
- package/templates/be/patterns/webhooks/webhook.door.ts.tpl +36 -0
- package/templates/be/patterns/webhooks/webhook.http.module.ts.tpl +6 -0
- package/templates/be/patterns/webhooks/webhook.index.ts.tpl +1 -0
- package/templates/be/patterns/webhooks/webhook.request.ts.tpl +9 -0
- package/templates/be/skeleton/apps/__app__/src/__app__.options.ts +3 -0
- package/templates/be/skeleton/apps/__app__/src/app.module.ts +15 -2
- package/templates/be/skeleton/apps/__app__/src/main.ts +7 -15
- package/templates/be/skeleton/apps/cli/src/app.module.ts +30 -0
- package/templates/be/skeleton/apps/cli/src/cli.options.ts +22 -0
- package/templates/be/skeleton/apps/cli/src/main.ts +24 -0
- package/templates/be/skeleton/src/features/cli/cli.module.ts +7 -0
- package/templates/be/skeleton/src/features/cli/index.ts +1 -0
- package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.spec.ts +17 -0
- package/templates/be/skeleton/src/features/cli/migrate/migrate.cli.ts +15 -0
- package/templates/be/skeleton/src/features/cli/migrate/migrate.module.ts +7 -0
- package/templates/be/skeleton/src/features/cli/migrate/subs/run.cli.spec.ts +17 -0
- package/templates/be/skeleton/src/features/cli/migrate/subs/run.cli.ts +18 -0
- package/templates/be/skeleton/src/features/cli/seed/seed.cli.spec.ts +17 -0
- package/templates/be/skeleton/src/features/cli/seed/seed.cli.ts +15 -0
- package/templates/be/skeleton/src/features/cli/seed/seed.module.ts +7 -0
- package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.spec.ts +33 -0
- package/templates/be/skeleton/src/features/cli/seed/subs/run.cli.ts +34 -0
- package/templates/be/skeleton/src/modules/domain/identity/admission.policy.spec.ts +13 -0
- package/templates/be/skeleton/src/modules/domain/identity/auth.guard.spec.ts +40 -0
- package/templates/be/skeleton/src/modules/domain/identity/messages/identity.messages.ts +1 -1
- package/templates/be/skeleton/src/modules/domain/note/errors/note.error.ts +16 -0
- package/templates/be/skeleton/src/modules/domain/note/index.ts +3 -0
- package/templates/be/skeleton/src/modules/domain/note/note.contracts.ts +19 -0
- package/templates/be/skeleton/src/modules/domain/note/note.decorators.ts +9 -0
- package/templates/be/skeleton/src/modules/domain/note/note.module-definition.ts +7 -0
- package/templates/be/skeleton/src/modules/domain/note/note.module.ts +19 -0
- package/templates/be/skeleton/src/modules/domain/note/note.options.ts +2 -0
- package/templates/be/skeleton/src/modules/domain/note/note.service.spec.ts +76 -0
- package/templates/be/skeleton/src/modules/domain/note/note.service.ts +37 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/connection.ts +8 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/entities/note.entity.ts +17 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/migrations/1790000000000-init-note.ts +22 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/note.rows.ts +17 -0
- package/templates/be/skeleton/src/modules/domain/note/persistence/note.sql.ts +4 -0
- package/templates/be/skeleton/src/modules/platform/config/env-source.config.ts +20 -0
- package/templates/be/skeleton/src/modules/platform/config/index.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/cqrs/cqrs.contracts.ts +18 -0
- package/templates/be/skeleton/src/modules/platform/database/connection-source.client.spec.ts +41 -0
- package/templates/be/skeleton/src/modules/platform/database/connection-source.client.ts +17 -0
- package/templates/be/skeleton/src/modules/platform/database/database.module-definition.ts +13 -0
- package/templates/be/skeleton/src/modules/platform/database/database.module.ts +77 -0
- package/templates/be/skeleton/src/modules/platform/database/database.options.ts +30 -0
- package/templates/be/skeleton/src/modules/platform/database/database.port.ts +65 -0
- package/templates/be/skeleton/src/modules/platform/database/database.sql.ts +47 -0
- package/templates/be/skeleton/src/modules/platform/database/errors/database.error.ts +16 -0
- package/templates/be/skeleton/src/modules/platform/database/index.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/database/migrate-connections.client.ts +30 -0
- package/templates/be/skeleton/src/modules/platform/database/migration-runner.service.spec.ts +84 -0
- package/templates/be/skeleton/src/modules/platform/database/migration-runner.service.ts +23 -0
- package/templates/be/skeleton/src/modules/platform/database/primary.config.ts +9 -0
- package/templates/be/skeleton/src/modules/platform/database/primary.connection.ts +2 -0
- package/templates/be/skeleton/src/modules/platform/database/primary.decorators.ts +10 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.spec.ts +108 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-connections.client.ts +75 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-runner.service.spec.ts +88 -0
- package/templates/be/skeleton/src/modules/platform/database/seed-runner.service.ts +29 -0
- package/templates/be/skeleton/src/modules/platform/errors/errors.filter.spec.ts +48 -0
- package/templates/be/skeleton/src/modules/platform/errors/messages/errors.messages.ts +1 -1
- package/templates/be/skeleton/src/modules/platform/http-security/errors/http-security.error.ts +12 -0
- package/templates/be/skeleton/src/modules/platform/http-security/execution-request.mapper.spec.ts +16 -0
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.config.ts +8 -1
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.decorators.ts +8 -0
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.module.ts +14 -1
- package/templates/be/skeleton/src/modules/platform/http-security/http-security.options.ts +12 -0
- package/templates/be/skeleton/src/modules/platform/http-security/messages/http-security.messages.ts +10 -2
- package/templates/be/skeleton/src/modules/platform/http-security/origin.guard.spec.ts +63 -0
- package/templates/be/skeleton/src/modules/platform/http-security/rate-limit.guard.spec.ts +108 -0
- package/templates/be/skeleton/src/modules/platform/http-security/request-validation.service.spec.ts +39 -0
- package/templates/be/skeleton/src/modules/platform/http-security/request-validation.service.ts +25 -0
- package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.spec.ts +172 -0
- package/templates/be/skeleton/src/modules/platform/http-security/webhook-signature.service.ts +66 -0
- package/templates/be/skeleton/src/modules/platform/logging/logging.log-events.ts +3 -1
- package/templates/be/skeleton/src/modules/platform/primitives/outcome.mapper.spec.ts +52 -0
- package/templates/be/skeleton-lite/apps/api/Dockerfile +37 -0
- package/templates/be/skeleton-lite/apps/api/src/api.options.ts +16 -0
- package/templates/be/skeleton-lite/apps/api/src/app.module.ts +81 -0
- package/templates/be/skeleton-lite/apps/api/src/main.ts +34 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/admission.policy.ts +14 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/auth.guard.ts +50 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.contracts.ts +26 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.decorators.ts +19 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.module.ts +26 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/identity.options.ts +7 -0
- package/templates/be/skeleton-lite/src/modules/domain/identity/index.ts +6 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/errors/supabase.error.ts +19 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/index.ts +10 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.client.ts +24 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.config.ts +8 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.decorators.ts +17 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.jwks.ts +32 -0
- package/templates/be/skeleton-lite/src/modules/integrations/supabase/supabase.options.ts +24 -0
- package/templates/be/skeleton-lite/src/modules/platform/cqrs/index.ts +4 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.module.ts +54 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.options.ts +23 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.port.ts +10 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/database.sql.ts +49 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/index.ts +4 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/migration-runner.service.ts +21 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/primary.config.ts +11 -0
- package/templates/be/skeleton-lite/src/modules/platform/database/seed-runner.service.ts +28 -0
- package/templates/be/skeleton-lite/src/modules/platform/http-security/http-security.module.ts +25 -0
- package/templates/be/skeleton-lite/src/modules/platform/http-security/index.ts +9 -0
- package/templates/be/skeleton-lite/src/modules/platform/i18n/index.ts +4 -0
- package/templates/be/skeleton-lite/src/modules/platform/logging/index.ts +5 -0
- package/templates/be/table/error.ts.tpl +16 -0
- package/templates/be/table/index.ts.tpl +2 -0
- package/templates/be/table/migration.sql.tpl +51 -0
- package/templates/be/table/module-definition.ts.tpl +7 -0
- package/templates/be/table/module.ts.tpl +14 -0
- package/templates/be/table/options.ts.tpl +2 -0
- package/templates/be/table/rows.ts.tpl +4 -0
- package/templates/be/table/service.ts.tpl +23 -0
- package/templates/be/table/sql.ts.tpl +4 -0
- package/templates/be/tool-config/jest.config.js +3 -1
- package/templates/be/upgrade-full/apps/cli/src/app.module.ts +14 -0
- package/templates/be/upgrade-full/apps/cli/src/main.ts +26 -0
- package/templates/be/upgrade-full/src/features/cli/cli.module.ts +6 -0
- package/templates/be/upgrade-full/src/features/cli/migrate/subs/run.cli.ts +23 -0
- package/templates/common/gitignore.base +1 -2
- package/templates/fe/app/package.json.tpl +1 -0
- package/templates/fe/app/tsconfig.json +12 -0
- package/templates/fe/common/app-layout.tsx.partial +8 -0
- package/templates/fe/common/site-shell.tsx.partial +9 -0
- package/templates/fe/image/next/Dockerfile +42 -0
- package/templates/fe/skeleton/apps/app/next.config.ts +23 -0
- package/templates/fe/skeleton/apps/app/postcss.config.mjs +7 -0
- package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/error.tsx +2 -2
- package/templates/fe/skeleton/apps/app/src/app/[locale]/layout.tsx +22 -0
- package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/loading.tsx +2 -2
- package/templates/fe/skeleton/apps/{__app__ → app}/src/app/[locale]/not-found.tsx +2 -2
- package/templates/fe/skeleton/apps/app/src/app/[locale]/page.tsx +9 -0
- package/templates/fe/skeleton/apps/{__app__ → app}/src/app/global-error.tsx +2 -2
- package/templates/fe/skeleton/apps/app/src/app/globals.css +10 -0
- package/templates/fe/skeleton/apps/app/src/features/layouts/AppLayout/index.tsx +18 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppErrorPage/component.tsx +19 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppErrorPage/index.tsx +11 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppGlobalErrorPage/index.tsx +16 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/component.tsx +10 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppHomePage/index.tsx +15 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppLoadingPage/index.tsx +8 -0
- package/templates/fe/skeleton/apps/app/src/features/pages/AppNotFoundPage/index.tsx +9 -0
- package/templates/fe/skeleton/apps/app/src/modules/i18n/index.ts +4 -0
- package/templates/fe/skeleton/apps/app/src/modules/i18n/messages/vi.json +28 -0
- package/templates/fe/skeleton/apps/app/src/modules/i18n/request.ts +4 -0
- package/templates/fe/skeleton/apps/app/src/modules/routes/index.ts +4 -0
- package/templates/fe/skeleton/apps/app/src/proxy.ts +1 -0
- package/templates/fe/skeleton/apps/{__app__ → landing}/next.config.ts +4 -2
- package/templates/fe/skeleton/apps/landing/postcss.config.mjs +7 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/error.tsx +10 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/layout.tsx +22 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/loading.tsx +6 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/not-found.tsx +6 -0
- package/templates/fe/skeleton/apps/landing/src/app/[locale]/page.tsx +9 -0
- package/templates/fe/skeleton/apps/landing/src/app/global-error.tsx +10 -0
- package/templates/fe/skeleton/apps/landing/src/app/globals.css +10 -0
- package/templates/fe/skeleton/apps/landing/src/features/layouts/LandingLayout/index.tsx +24 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingErrorPage/component.tsx +18 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingErrorPage/index.tsx +11 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingGlobalErrorPage/index.tsx +16 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingLoadingPage/index.tsx +8 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingNotFoundPage/index.tsx +9 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingPage/component.tsx +24 -0
- package/templates/fe/skeleton/apps/landing/src/features/pages/LandingPage/index.tsx +16 -0
- package/templates/fe/skeleton/apps/landing/src/modules/config/index.ts +12 -0
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/index.ts +7 -0
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/messages/vi.json +31 -0
- package/templates/fe/skeleton/apps/landing/src/modules/i18n/request.ts +4 -0
- package/templates/fe/skeleton/apps/landing/src/modules/routes/index.ts +4 -0
- package/templates/fe/skeleton/apps/landing/src/proxy.ts +1 -0
- package/templates/fe/skeleton/packages/__project__-i18n/src/index.ts +49 -0
- package/templates/fe/skeleton/packages/__project__-i18n/src/proxy.ts +24 -0
- package/templates/fe/skeleton/packages/__project__-i18n/src/routing.ts +20 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/branches/Providers/index.tsx +23 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/ErrorNotice/index.tsx +14 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/GlobalErrorNotice/index.tsx +25 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/LoadingNotice/index.tsx +9 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/LocaleShell/index.tsx +21 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/NotFoundNotice/index.tsx +19 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/SiteShell/classNames.ts +4 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/composites/SiteShell/index.tsx +19 -0
- package/templates/fe/skeleton/packages/__project__-ui/src/index.ts +6 -0
- package/templates/fe/skeleton-lite/apps/web/next.config.ts +22 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/error.tsx +10 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/layout.tsx +37 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/loading.tsx +6 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/not-found.tsx +6 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/page.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/[locale]/providers.tsx +26 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/auth/callback/route.ts +24 -0
- package/templates/fe/skeleton-lite/apps/web/src/app/global-error.tsx +10 -0
- package/templates/fe/{skeleton/apps/__app__ → skeleton-lite/apps/web}/src/app/globals.css +1 -1
- package/templates/fe/skeleton-lite/apps/web/src/app/health/live/route.ts +5 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/component.tsx +29 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignInForm/index.tsx +41 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignOutButton/component.tsx +17 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/blocks/SignOutButton/index.tsx +38 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/ErrorNotice/index.tsx +13 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/GlobalErrorNotice/index.tsx +24 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/LoadingNotice/index.tsx +7 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/NotFoundNotice/index.tsx +18 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/SiteShell/classNames.ts +4 -0
- package/templates/fe/skeleton-lite/apps/web/src/components/composites/SiteShell/index.tsx +13 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/layouts/AppLayout/index.tsx +31 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppErrorPage/component.tsx +12 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppErrorPage/index.tsx +10 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppGlobalErrorPage/index.tsx +15 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/component.tsx +14 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppHomePage/index.tsx +30 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppLoadingPage/index.tsx +8 -0
- package/templates/fe/skeleton-lite/apps/web/src/features/pages/AppNotFoundPage/index.tsx +9 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/brand/brand.css +20 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/brand/index.ts +2 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/config/index.ts +20 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/read-session.ts +25 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-in.ts +25 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/auth/write-sign-out.ts +17 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/browser.ts +14 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/index.ts +3 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/outcome.ts +59 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/principal.ts +29 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/server.ts +70 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/db/validation/validation.mapper.ts +50 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/index.ts +8 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/messages/vi.json +48 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/request.ts +16 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/i18n/routing.ts +14 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/routes/index.ts +7 -0
- package/templates/fe/skeleton-lite/apps/web/src/modules/routes/safe-next-path.ts +26 -0
- package/templates/fe/skeleton-lite/apps/web/src/proxy.ts +43 -0
- package/templates/fe/table/read.ts.tpl +16 -0
- package/templates/fe/table/write.ts.tpl +16 -0
- package/upgrade/check.mjs +137 -0
- package/upgrade/index.mjs +524 -0
- package/bin/hfs.mjs +0 -233
- package/runtime/scripts/api/fs/rmdir-link.mjs +0 -12
- package/runtime/scripts/lib/test-secrets.mjs +0 -116
- package/templates/fe/skeleton/apps/__app__/src/app/[locale]/layout.tsx +0 -20
- package/templates/fe/skeleton/apps/__app__/src/app/[locale]/page.tsx +0 -9
- package/templates/fe/skeleton/apps/__app__/src/components/composites/FailureScreen/index.tsx +0 -28
- package/templates/fe/skeleton/apps/__app__/src/features/layouts/LocaleShell/index.tsx +0 -35
- package/templates/fe/skeleton/apps/__app__/src/features/pages/ErrorPage/component.tsx +0 -19
- package/templates/fe/skeleton/apps/__app__/src/features/pages/ErrorPage/index.tsx +0 -11
- package/templates/fe/skeleton/apps/__app__/src/features/pages/GlobalErrorPage/component.tsx +0 -24
- package/templates/fe/skeleton/apps/__app__/src/features/pages/GlobalErrorPage/index.tsx +0 -14
- package/templates/fe/skeleton/apps/__app__/src/features/pages/HomePage/component.tsx +0 -21
- package/templates/fe/skeleton/apps/__app__/src/features/pages/HomePage/index.tsx +0 -15
- package/templates/fe/skeleton/apps/__app__/src/features/pages/LoadingPage/component.tsx +0 -23
- package/templates/fe/skeleton/apps/__app__/src/features/pages/LoadingPage/index.tsx +0 -8
- package/templates/fe/skeleton/apps/__app__/src/features/pages/NotFoundPage/component.tsx +0 -28
- package/templates/fe/skeleton/apps/__app__/src/features/pages/NotFoundPage/index.tsx +0 -8
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/config.ts +0 -8
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/index.ts +0 -2
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/messages/vi.json +0 -25
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/request.ts +0 -19
- package/templates/fe/skeleton/apps/__app__/src/modules/i18n/routing.ts +0 -9
- package/templates/fe/skeleton/apps/__app__/src/modules/routes/index.ts +0 -4
- package/templates/fe/skeleton/apps/__app__/src/proxy.ts +0 -10
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.contracts.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.handler.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/application/check-liveness.query.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/index.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/system-health.module.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/transport/http/live.controller.ts +0 -0
- /package/templates/be/skeleton/src/features/{system-health → api/system-health}/transport/http/system-health-http.module.ts +0 -0
- /package/templates/fe/skeleton/apps/{__app__ → app}/src/app/health/live/route.ts +0 -0
- /package/templates/fe/skeleton/apps/{__app__ → app}/src/modules/config/index.ts +0 -0
- /package/templates/fe/{skeleton/apps/__app__ → skeleton-lite/apps/web}/postcss.config.mjs +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# _labels.yaml — the human name of every operation, one shared map.
|
|
2
|
+
# The underscore prefix keeps this file out of the op catalog scan, like
|
|
3
|
+
# _common.yaml: scripts/route/build-ops-registry.mjs and route-op.mjs skip
|
|
4
|
+
# modules/ops/*.yaml files whose names start with "_".
|
|
5
|
+
#
|
|
6
|
+
# Owner request 2026-09-27: kernels, workflows and op workers carry names a person understands. The
|
|
7
|
+
# op id (interface.draw) stays the key everywhere — the ledger, the api, job ids, contracts; this map
|
|
8
|
+
# only says what a human reads for it. Every reader goes through scripts/lib/display-names.mjs
|
|
9
|
+
# (opLabel): Orca [Op] worker titles, starci kernel status legs/nextActions `label`, the supervisor digest and
|
|
10
|
+
# progress report, Telegram notices and the harness UI (ui/server.mjs sends the map in the snapshot
|
|
11
|
+
# as `opLabels`, so the UI shows exactly these words). tests/lib/display-names.spec.mjs keeps one entry per
|
|
12
|
+
# modules/ops/ops/*.yaml. `vi` is what the owner reads; `en` is the UI's English rendering of it.
|
|
13
|
+
schema: starci/op-labels@1
|
|
14
|
+
|
|
15
|
+
labels:
|
|
16
|
+
request.analyze: { vi: Phân tích yêu cầu, en: Analyze request }
|
|
17
|
+
task.execute: { vi: Chọn luồng việc, en: Select workflow }
|
|
18
|
+
scope.define: { vi: Xác định phạm vi, en: Define scope }
|
|
19
|
+
scope.finish: { vi: Kết thúc phạm vi, en: Finish scope }
|
|
20
|
+
goal.revise: { vi: Sửa mục tiêu, en: Revise goal }
|
|
21
|
+
business.decide: { vi: Chốt nghiệp vụ, en: Decide business rules }
|
|
22
|
+
architecture.decide: { vi: Thiết kế kiến trúc, en: Design architecture }
|
|
23
|
+
brand.decide: { vi: Chốt thương hiệu, en: Decide brand }
|
|
24
|
+
decision.prepare: { vi: Chuẩn bị quyết định, en: Prepare decision }
|
|
25
|
+
provision.ask: { vi: Xin thông tin, en: Request information }
|
|
26
|
+
work.author: { vi: Chia việc chi tiết, en: Plan work }
|
|
27
|
+
workspace.manage: { vi: Dựng workspace, en: Manage workspace }
|
|
28
|
+
knowledge.repair: { vi: Sửa tri thức, en: Repair knowledge }
|
|
29
|
+
grammar.update: { vi: Bổ sung Grammar, en: Update grammar }
|
|
30
|
+
interface.draw: { vi: Vẽ giao diện, en: Draw interface }
|
|
31
|
+
interface.asset: { vi: Làm hình ảnh, en: Make assets }
|
|
32
|
+
interface.scaffold: { vi: Dựng khung giao diện, en: Scaffold interface }
|
|
33
|
+
interface.implement: { vi: Code giao diện, en: Implement interface }
|
|
34
|
+
interface.audit: { vi: Soát giao diện, en: Audit interface }
|
|
35
|
+
backend.scaffold: { vi: Dựng khung backend, en: Scaffold backend }
|
|
36
|
+
backend.implement: { vi: Code backend, en: Implement backend }
|
|
37
|
+
package.scaffold: { vi: Dựng khung package, en: Scaffold package }
|
|
38
|
+
code.refactor: { vi: Chỉnh sửa mã nguồn, en: Refactor code }
|
|
39
|
+
content.generate: { vi: Soạn nội dung, en: Generate content }
|
|
40
|
+
docs.author: { vi: Viết tài liệu, en: Write docs }
|
|
41
|
+
test.author: { vi: Viết test, en: Write tests }
|
|
42
|
+
integration.verify: { vi: Kiểm thử tích hợp, en: Integration test }
|
|
43
|
+
e2e.verify: { vi: Kiểm thử đầu cuối, en: End-to-end test }
|
|
44
|
+
perf.verify: { vi: Kiểm thử hiệu năng, en: Performance test }
|
|
45
|
+
unit.verify: { vi: Chạy toàn bộ unit test, en: Full unit test run }
|
|
46
|
+
security.verify: { vi: Soát bảo mật, en: Security review }
|
|
47
|
+
uat.verify: { vi: Kiểm thử UAT, en: UAT test }
|
|
48
|
+
uat.assisted.prepare: { vi: Chuẩn bị UAT có người, en: Prepare assisted UAT }
|
|
49
|
+
uat.assisted.verify: { vi: Xác minh UAT có người, en: Verify assisted UAT }
|
|
50
|
+
review.verify: { vi: Review cuối, en: Final review }
|
|
51
|
+
handover.review: { vi: Bàn giao, en: Handover }
|
|
52
|
+
release.deliver: { vi: Phát hành, en: Release }
|
|
53
|
+
runtime.operate: { vi: Vận hành môi trường, en: Operate runtime }
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// forbidden-root.mjs — the paths the runtime must never remove whole (safe-remove.mjs safeRemove refuses them before it
|
|
2
|
+
// deletes anything; scripts/machine/worktree-git.mjs asks it before a worktree removal).
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
import { samePath } from '../../lib/path-key.mjs';
|
|
8
|
+
import { realpathOr } from '../../lib/fs-kind.mjs';
|
|
9
|
+
|
|
10
|
+
const SKILL_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..', '..');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* True when `p` sits strictly below `root` by real path: no link on the way, not `root` itself. The one place a
|
|
14
|
+
* primary checkout may be removed is a disposable fixture a caller names by its temp root (hk-tmp).
|
|
15
|
+
*/
|
|
16
|
+
function strictlyInsideReal(p, root) {
|
|
17
|
+
if (!root) return false;
|
|
18
|
+
const rel = path.relative(path.resolve(root), path.resolve(p));
|
|
19
|
+
if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return false;
|
|
20
|
+
const real = realpathOr(p);
|
|
21
|
+
const rootReal = realpathOr(root);
|
|
22
|
+
// Its real path must be the same relative place under the root's real path: no link between them.
|
|
23
|
+
return Boolean(real && rootReal) && samePath(real, path.join(rootReal, rel));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A path the runtime must never remove whole: a filesystem root, the home or temp directory, the runtime, the
|
|
28
|
+
* repository that hosts it, the repositories root, or a primary git checkout (its .git is a directory; the
|
|
29
|
+
* scratch trees the runtime removes are temp directories and linked worktrees, whose .git is a file).
|
|
30
|
+
* `checkoutsUnder` names a disposable root (hk-tmp's temp root): a checkout strictly inside it by real path
|
|
31
|
+
* is a spec fixture, not a live repository, and is not refused for its .git. Every other refusal stands.
|
|
32
|
+
* `hold(path)` is the caller's artifact-hold check (scripts/machine/artifact-hold.mjs artifactHoldReason: a tree holding
|
|
33
|
+
* an indexed job artifact, or inside an evidence directory holding one, whatever the workflow's phase), its refusal line
|
|
34
|
+
* or null. It is required: without one every path is refused (fail closed); a caller that removes only a scratch tree
|
|
35
|
+
* it made itself, outside every ledger's repository, says so with `hold: () => null`.
|
|
36
|
+
*/
|
|
37
|
+
export function forbiddenRoot(p, { checkoutsUnder = null, hold } = {}) {
|
|
38
|
+
const resolved = path.resolve(p);
|
|
39
|
+
if (path.parse(resolved).root === resolved || samePath(path.dirname(resolved), resolved)) return 'a filesystem root';
|
|
40
|
+
for (const [name, dir] of [['the home directory', os.homedir()], ['the temp directory', os.tmpdir()], ['the runtime', SKILL_ROOT],
|
|
41
|
+
['the repository hosting the runtime', path.dirname(SKILL_ROOT)], ['the repositories root', path.dirname(path.dirname(SKILL_ROOT))]]) {
|
|
42
|
+
if (dir && samePath(path.resolve(dir), resolved)) return name;
|
|
43
|
+
}
|
|
44
|
+
if (typeof hold !== 'function') return 'a tree no artifact-hold check cleared (pass hold)';
|
|
45
|
+
const held = hold(resolved);
|
|
46
|
+
if (held) return held;
|
|
47
|
+
let checkout = false;
|
|
48
|
+
try { checkout = fs.lstatSync(path.join(resolved, '.git')).isDirectory(); } catch { /* no .git directory */ }
|
|
49
|
+
if (checkout && !strictlyInsideReal(resolved, checkoutsUnder)) return 'a git checkout';
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// is-link-like.mjs — whether a path is a link of any kind (a symlink, a junction, or another reparse point that
|
|
2
|
+
// redirects it), read with lstat, readlink and real paths. Every tree walk of the runtime asks it before it enters a
|
|
3
|
+
// directory (safe-remove.mjs, links-under.mjs, the housekeeping collectors), so no walk ever follows a link
|
|
4
|
+
// (inc-c8fbf76aa499).
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { samePath } from '../../lib/path-key.mjs';
|
|
8
|
+
import { realpathOr } from '../../lib/fs-kind.mjs';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* True when `p` is a link of any kind: a symlink, a junction, or another reparse point that redirects it.
|
|
12
|
+
* `parentReal` (the real path of p's parent, when the caller walked to it through real directories) lets the
|
|
13
|
+
* real-path test catch a reparse point lstat and readlink do not report. A missing path is not a link.
|
|
14
|
+
*/
|
|
15
|
+
export function isLinkLike(p, { parentReal = null, stat = null } = {}) {
|
|
16
|
+
let st = stat;
|
|
17
|
+
if (!st) { try { st = fs.lstatSync(p); } catch { return false; } }
|
|
18
|
+
if (st.isSymbolicLink()) return true;
|
|
19
|
+
if (!st.isDirectory()) return false;
|
|
20
|
+
try { fs.readlinkSync(p); return true; } catch { /* not a link readlink can read */ }
|
|
21
|
+
const parent = parentReal ?? realpathOr(path.dirname(p));
|
|
22
|
+
const real = realpathOr(p);
|
|
23
|
+
if (!parent || !real) return true; // cannot prove it is a plain directory: treat it as a link
|
|
24
|
+
return !samePath(real, path.join(parent, path.basename(p)));
|
|
25
|
+
}
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
// scripts/api/fs/lib.mjs — what the fs call files beside it share: the error codes Windows returns while another
|
|
2
|
-
// process holds a path open. The call files (safe-remove.mjs,
|
|
3
|
-
// name one filesystem use.
|
|
2
|
+
// process holds a path open, and the verified unlink of one link. The call files (safe-remove.mjs, remove-links-under.mjs,
|
|
3
|
+
// rename-over.mjs, zip-write.mjs, ...) each name one filesystem use.
|
|
4
|
+
import fs from 'node:fs';
|
|
4
5
|
|
|
5
6
|
/** The rename and unlink errors Windows returns while another process holds the target open: retried, never final. */
|
|
6
7
|
export const FS_BUSY = Object.freeze(['EPERM', 'EBUSY', 'EACCES']);
|
|
8
|
+
|
|
9
|
+
/** Remove a link itself, never its target. True when nothing is left at `p`. */
|
|
10
|
+
export function unlinkOnly(p) {
|
|
11
|
+
try { fs.lstatSync(p); } catch (error) { return error?.code === 'ENOENT'; }
|
|
12
|
+
try { fs.unlinkSync(p); } catch { try { fs.rmdirSync(p); } catch { /* verified below */ } }
|
|
13
|
+
try { fs.lstatSync(p); return false; } catch (error) { return error?.code === 'ENOENT'; }
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Construct an archive-reader refusal with its existing error code. */
|
|
17
|
+
export const zipRefuse = (message, code = 'zip-corrupt') => Object.assign(new Error(message), { code });
|
|
18
|
+
|
|
19
|
+
/** Relative ZIP entry names exclude traversal, drive roots and control characters. */
|
|
20
|
+
export const validZipName=name=>Boolean(name)&&!name.startsWith('/')&&!/^[A-Za-z]:/.test(name)&&!/[\0\r\n\\]/.test(name)&&!name.split('/').includes('..');
|
|
@@ -1,75 +1,35 @@
|
|
|
1
|
-
// safe-remove.mjs — the ONE way the runtime deletes a directory tree
|
|
1
|
+
// safe-remove.mjs — the ONE way the runtime deletes a directory tree.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
3
|
+
// A product repository once lost 674 tracked working-tree files and its node_modules: a
|
|
4
4
|
// recursive delete of a scratch tree followed directory links (Windows junctions) into the live repository.
|
|
5
5
|
// Git for Windows' `git worktree remove --force` follows a junction inside the worktree and empties its target
|
|
6
6
|
// (proven on this host, git 2.52), and a PowerShell/cmd/third-party recursive delete may do the same. A tree
|
|
7
7
|
// that holds links to live checkouts is exactly what the runtime makes (node_modules junctions in land and
|
|
8
8
|
// worker staging checkouts, push-mains scratch worktrees, dependency mirrors).
|
|
9
9
|
//
|
|
10
|
-
//
|
|
10
|
+
// safeRemove walks the tree itself with lstat and NEVER descends into a link: every symlink, junction or
|
|
11
11
|
// other reparse point is unlinked as a link (the link only, never what it points at) and verified gone. A
|
|
12
12
|
// directory counts as a link when lstat says so, when readlink resolves it, or when its real path is not
|
|
13
13
|
// its own path under its parent's real path (any name-surrogate reparse point). A link that cannot be
|
|
14
14
|
// unlinked stops the removal of everything above it; nothing is ever deleted through it.
|
|
15
15
|
//
|
|
16
16
|
// A worktree's removal composes this with git (scripts/machine/worktree-git.mjs safeRemoveWorktree, the Orca home in
|
|
17
|
-
// scripts/machine/worktree-orca.mjs): every link removed as a link first (
|
|
18
|
-
// or Orca.
|
|
19
|
-
// artifactHoldReason), taken before
|
|
17
|
+
// scripts/machine/worktree-orca.mjs): every link removed as a link first (remove-links-under.mjs), zero asserted, only then
|
|
18
|
+
// git or Orca. The link test is is-link-like.mjs, the refused roots forbidden-root.mjs. Whether a tree holds an indexed
|
|
19
|
+
// job artifact is the caller's decision (`hold`: scripts/machine/artifact-hold.mjs artifactHoldReason), taken before
|
|
20
|
+
// anything is deleted.
|
|
20
21
|
import fs from 'node:fs';
|
|
21
|
-
import os from 'node:os';
|
|
22
22
|
import path from 'node:path';
|
|
23
|
-
import { fileURLToPath } from 'node:url';
|
|
24
23
|
import { sleepSync } from '../../lib/sleep-sync.mjs';
|
|
25
24
|
import { samePath } from '../../lib/path-key.mjs';
|
|
26
|
-
import { rmdirLink } from './rmdir-link.mjs';
|
|
27
|
-
import { FS_BUSY } from './lib.mjs';
|
|
28
25
|
import { realpathOr } from '../../lib/fs-kind.mjs';
|
|
26
|
+
import { FS_BUSY, unlinkOnly } from './lib.mjs';
|
|
27
|
+
import { isLinkLike } from './is-link-like.mjs';
|
|
28
|
+
import { forbiddenRoot } from './forbidden-root.mjs';
|
|
29
29
|
|
|
30
30
|
const WIN = process.platform === 'win32';
|
|
31
31
|
const same = samePath;
|
|
32
32
|
|
|
33
|
-
/**
|
|
34
|
-
* True when `p` is a link of any kind: a symlink, a junction, or another reparse point that redirects it.
|
|
35
|
-
* `parentReal` (the real path of p's parent, when the caller walked to it through real directories) lets the
|
|
36
|
-
* real-path test catch a reparse point lstat and readlink do not report. A missing path is not a link.
|
|
37
|
-
*/
|
|
38
|
-
export function isLinkLike(p, { parentReal = null, stat = null } = {}) {
|
|
39
|
-
let st = stat;
|
|
40
|
-
if (!st) { try { st = fs.lstatSync(p); } catch { return false; } }
|
|
41
|
-
if (st.isSymbolicLink()) return true;
|
|
42
|
-
if (!st.isDirectory()) return false;
|
|
43
|
-
try { fs.readlinkSync(p); return true; } catch { /* not a link readlink can read */ }
|
|
44
|
-
const parent = parentReal ?? realpathOr(path.dirname(p));
|
|
45
|
-
const real = realpathOr(p);
|
|
46
|
-
if (!parent || !real) return true; // cannot prove it is a plain directory: treat it as a link
|
|
47
|
-
return !same(real, path.join(parent, path.basename(p)));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** Remove a link itself, never its target. True when nothing is left at `p`. */
|
|
51
|
-
/** Every link (symlink, junction, other reparse point) under root, found with lstat; the walk never enters one. */
|
|
52
|
-
export function linksUnder(root) {
|
|
53
|
-
const found = [];
|
|
54
|
-
const visit = (p, parentReal) => {
|
|
55
|
-
let stat;
|
|
56
|
-
try { stat = fs.lstatSync(p); } catch { return; }
|
|
57
|
-
if (isLinkLike(p, { parentReal, stat })) { found.push(p); return; }
|
|
58
|
-
if (!stat.isDirectory()) return;
|
|
59
|
-
let real, names;
|
|
60
|
-
try { real = fs.realpathSync.native(p); names = fs.readdirSync(p); } catch { return; }
|
|
61
|
-
for (const name of names) visit(path.join(p, name), real);
|
|
62
|
-
};
|
|
63
|
-
visit(path.resolve(root), null);
|
|
64
|
-
return found;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export function unlinkOnly(p) {
|
|
68
|
-
try { fs.lstatSync(p); } catch (error) { return error?.code === 'ENOENT'; }
|
|
69
|
-
try { fs.unlinkSync(p); } catch { try { fs.rmdirSync(p); } catch { /* verified below */ } }
|
|
70
|
-
try { fs.lstatSync(p); return false; } catch (error) { return error?.code === 'ENOENT'; }
|
|
71
|
-
}
|
|
72
|
-
|
|
73
33
|
const retrying = (fn, retries) => {
|
|
74
34
|
for (let attempt = 0; ; attempt += 1) {
|
|
75
35
|
try { fn(); return null; } catch (error) {
|
|
@@ -85,54 +45,12 @@ const removeFile = (p, retries) => retrying(() => {
|
|
|
85
45
|
}
|
|
86
46
|
}, retries);
|
|
87
47
|
|
|
88
|
-
const SKILL_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..', '..');
|
|
89
|
-
/**
|
|
90
|
-
* True when `p` sits strictly below `root` by real path: no link on the way, not `root` itself. The one place a
|
|
91
|
-
* primary checkout may be removed is a disposable fixture a caller names by its temp root (hk-tmp).
|
|
92
|
-
*/
|
|
93
|
-
export function strictlyInsideReal(p, root) {
|
|
94
|
-
if (!root) return false;
|
|
95
|
-
const rel = path.relative(path.resolve(root), path.resolve(p));
|
|
96
|
-
if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return false;
|
|
97
|
-
const real = realpathOr(p);
|
|
98
|
-
const rootReal = realpathOr(root);
|
|
99
|
-
// Its real path must be the same relative place under the root's real path: no link between them.
|
|
100
|
-
return Boolean(real && rootReal) && same(real, path.join(rootReal, rel));
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* A path the runtime must never remove whole: a filesystem root, the home or temp directory, the runtime, the
|
|
105
|
-
* repository that hosts it, the repositories root, or a primary git checkout (its .git is a directory; the
|
|
106
|
-
* scratch trees the runtime removes are temp directories and linked worktrees, whose .git is a file).
|
|
107
|
-
* `checkoutsUnder` names a disposable root (hk-tmp's temp root): a checkout strictly inside it by real path
|
|
108
|
-
* is a spec fixture, not a live repository, and is not refused for its .git. Every other refusal stands.
|
|
109
|
-
* `hold(path)` is the caller's artifact-hold check (scripts/machine/artifact-hold.mjs artifactHoldReason: a tree holding
|
|
110
|
-
* an indexed job artifact, or inside an evidence directory holding one, whatever the workflow's phase), its refusal line
|
|
111
|
-
* or null. It is required: without one every path is refused (fail closed); a caller that removes only a scratch tree
|
|
112
|
-
* it made itself, outside every ledger's repository, says so with `hold: () => null`.
|
|
113
|
-
*/
|
|
114
|
-
export function forbiddenRoot(p, { checkoutsUnder = null, hold } = {}) {
|
|
115
|
-
const resolved = path.resolve(p);
|
|
116
|
-
if (path.parse(resolved).root === resolved || same(path.dirname(resolved), resolved)) return 'a filesystem root';
|
|
117
|
-
for (const [name, dir] of [['the home directory', os.homedir()], ['the temp directory', os.tmpdir()], ['the runtime', SKILL_ROOT],
|
|
118
|
-
['the repository hosting the runtime', path.dirname(SKILL_ROOT)], ['the repositories root', path.dirname(path.dirname(SKILL_ROOT))]]) {
|
|
119
|
-
if (dir && same(path.resolve(dir), resolved)) return name;
|
|
120
|
-
}
|
|
121
|
-
if (typeof hold !== 'function') return 'a tree no artifact-hold check cleared (pass hold)';
|
|
122
|
-
const held = hold(resolved);
|
|
123
|
-
if (held) return held;
|
|
124
|
-
let checkout = false;
|
|
125
|
-
try { checkout = fs.lstatSync(path.join(resolved, '.git')).isDirectory(); } catch { /* no .git directory */ }
|
|
126
|
-
if (checkout && !strictlyInsideReal(resolved, checkoutsUnder)) return 'a git checkout';
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
48
|
/**
|
|
131
49
|
* Remove `root` and everything under it without ever following a link. Links are unlinked (the link only);
|
|
132
|
-
* plain files and directories are deleted bottom-up. `checkoutsUnder` and `hold` (required) are forbiddenRoot's. Returns {ok, root, removed: {files, dirs, links},
|
|
50
|
+
* plain files and directories are deleted bottom-up. `checkoutsUnder` and `hold` (required) are forbidden-root.mjs forbiddenRoot's. Returns {ok, root, removed: {files, dirs, links},
|
|
133
51
|
* errors: [{path, code, message}]}; ok is true only when `root` is gone. A missing root is ok.
|
|
134
52
|
*/
|
|
135
|
-
export function
|
|
53
|
+
export function safeRemove(root, { retries = 5, checkoutsUnder = null, hold } = {}) {
|
|
136
54
|
const target = path.resolve(String(root ?? ''));
|
|
137
55
|
const out = { ok: false, root: target, removed: { files: 0, dirs: 0, links: 0 }, errors: [] };
|
|
138
56
|
const fail = (p, error) => { out.errors.push({ path: p, code: error?.code ?? 'ERROR', message: String(error?.message ?? error) }); };
|
|
@@ -184,41 +102,3 @@ export function safeRemoveTree(root, { retries = 5, checkoutsUnder = null, hold
|
|
|
184
102
|
out.ok = !fs.existsSync(target) && (() => { try { fs.lstatSync(target); return false; } catch { return true; } })();
|
|
185
103
|
return out;
|
|
186
104
|
}
|
|
187
|
-
|
|
188
|
-
/** Remove one link as a link, never its target: `cmd /c rmdir <link>` on Windows (no /s), then unlinkOnly. */
|
|
189
|
-
export function removeLink(p) {
|
|
190
|
-
if (WIN) {
|
|
191
|
-
let st = null;
|
|
192
|
-
try { st = fs.lstatSync(p); } catch { return true; }
|
|
193
|
-
if (st.isDirectory() || st.isSymbolicLink()) rmdirLink(p);
|
|
194
|
-
}
|
|
195
|
-
return unlinkOnly(p);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* The link step of every worktree removal (scripts/machine/worktree-git.mjs safeRemoveWorktree, worktree-orca.mjs removeOrcaWorktree): every link
|
|
200
|
-
* under `target` found WITHOUT following one (linksUnder), each removed as a link (removeLink: `cmd /c rmdir <link>`, never
|
|
201
|
-
* /s), outermost first, then a re-scan that must find ZERO. {ok, links, errors: [{path, code, message}]}; ok false: a link
|
|
202
|
-
* is stuck and the caller removes nothing.
|
|
203
|
-
*/
|
|
204
|
-
export function removeLinksUnder(target) {
|
|
205
|
-
const out = { ok: false, links: 0, errors: [] };
|
|
206
|
-
if (!fs.existsSync(target)) { out.ok = true; return out; }
|
|
207
|
-
for (const link of linksUnder(target)) { if (removeLink(link)) out.links += 1; else out.errors.push({ path: link, code: 'LINK_STUCK', message: 'a link could not be removed' }); }
|
|
208
|
-
for (const l of linksUnder(target)) if (!out.errors.some((e) => e.path === l)) out.errors.push({ path: l, code: 'LINK_STUCK', message: 'a link is still there after removal' });
|
|
209
|
-
out.ok = out.errors.length === 0;
|
|
210
|
-
return out;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Unlink `<dir>/node_modules` when it is a link (one an older runtime made; no runtime code makes one, RT_NODE_MODULES_LINK).
|
|
215
|
-
* true when no link is left there; a real directory is left alone (true: it is the checkout's own).
|
|
216
|
-
*/
|
|
217
|
-
export function unlinkNodeModulesLink(dir) {
|
|
218
|
-
const nm = path.join(dir, 'node_modules');
|
|
219
|
-
let st;
|
|
220
|
-
try { st = fs.lstatSync(nm); } catch { return true; }
|
|
221
|
-
if (!st.isSymbolicLink()) return true;
|
|
222
|
-
try { fs.unlinkSync(nm); } catch { try { fs.rmdirSync(nm); } catch { /* checked below */ } }
|
|
223
|
-
try { fs.lstatSync(nm); return false; } catch { return true; }
|
|
224
|
-
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// config-get.mjs — `git config [--local] --get <key>`: one configuration value of a checkout (empty when unset).
|
|
2
|
+
import { gitRunner } from './lib.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* {ok, stdout, stderr}; `local` reads the repository's own config only; `env` replaces the process env (a caller drops
|
|
6
|
+
* git's repository-local variables). git: the caller's runner or null.
|
|
7
|
+
*/
|
|
8
|
+
export function configGet(cwd, key, { local = false, env = undefined, git = null } = {}) {
|
|
9
|
+
return gitRunner(git)(['config', ...(local ? ['--local'] : []), '--get', key], env ? { cwd, env } : { cwd });
|
|
10
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// diff.mjs — `git diff <args>`: the changes between commits, the index and the working tree.
|
|
2
|
+
import { runGit } from './lib.mjs';
|
|
3
|
+
|
|
4
|
+
/** The spawn result {status, stdout, stderr, error} of `git diff ...args`; options are runGit's (cwd or dir, config, timeout, maxBuffer, input, encoding, env, git). */
|
|
5
|
+
export const diff = (args = [], options = {}) => runGit(['diff', ...args], options);
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
// scripts/api/git/lib.mjs — the one place the runtime spawns git (RT_EXTERNAL_OWNER of scripts/hfs/runtime-rules/external-owner.mjs enforces it).
|
|
2
2
|
//
|
|
3
3
|
// Every caller spelt the same options by hand - encoding:'utf8', windowsHide:true, sometimes a timeout - with two shapes:
|
|
4
|
-
// `git args` in a cwd and `git -C dir args`.
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
4
|
+
// `git args` in a cwd and `git -C dir args`. gitSpawn keeps the spawnSync(file, args, options) signature so an injected
|
|
5
|
+
// runner or a spec's fake takes the same three arguments; runGit is the `-C` / `-c` convenience; gitRunner folds any
|
|
6
|
+
// caller's runner into {ok, stdout, stderr}. Only the call files beside this one import it: each names one git verb
|
|
7
|
+
// (diff.mjs, ls-files.mjs, worktree-*.mjs, ...), and a caller folds the spawn result it gets back with the pure
|
|
8
|
+
// helpers of scripts/lib/git.mjs (gitResultOf, gitOutputOf).
|
|
9
|
+
import { assertMutationFence } from '../../lib/mutation-fence.mjs';
|
|
9
10
|
import { spawnSync } from 'node:child_process';
|
|
11
|
+
import { withoutGitLocalEnv } from '../../lib/git.mjs';
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Spawn `file` (the git binary) once with `args`: utf8 text, a hidden window, never a shell.
|
|
@@ -15,32 +17,22 @@ import { spawnSync } from 'node:child_process';
|
|
|
15
17
|
* ERR_UNKNOWN_ENCODING, so it reaches spawnSync as encoding:null (Buffer output either way).
|
|
16
18
|
*/
|
|
17
19
|
export const gitSpawn = (file, args, options = {}) => {
|
|
20
|
+
assertMutationFence({ kind: 'git-effect', args });
|
|
18
21
|
const spawn = { encoding: 'utf8', windowsHide: true, ...options };
|
|
19
22
|
if (spawn.encoding === 'buffer') spawn.encoding = null;
|
|
20
23
|
return spawnSync(file, args, spawn);
|
|
21
24
|
};
|
|
22
25
|
|
|
23
|
-
/**
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
export
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** runGit's result as {ok, stdout, error}: ok only on a clean exit, error from stderr or the spawn. */
|
|
36
|
-
export function gitResult(args, options = {}) {
|
|
37
|
-
const r = runGit(args, options);
|
|
38
|
-
return {
|
|
39
|
-
ok: !r.error && r.status === 0,
|
|
40
|
-
stdout: r.stdout ?? '',
|
|
41
|
-
error: (r.stderr ?? '').trim() || String(r.error?.message ?? (r.status == null ? 'timed out' : `exit ${r.status}`)),
|
|
42
|
-
};
|
|
43
|
-
}
|
|
26
|
+
/**
|
|
27
|
+
* `git args` in `cwd`, or `git -C dir args` when `dir` is given. `git` overrides the binary; `config` ({key: value})
|
|
28
|
+
* prefixes one `-c key=value` per entry (core.quotepath=off, a committer identity). Scoped calls discard ambient
|
|
29
|
+
* hook repository variables; an explicit env remains authoritative, including an intentional temporary index.
|
|
30
|
+
*/
|
|
31
|
+
export const runGit = (args, { cwd = null, dir = null, git = 'git', config = null, ...options } = {}) =>
|
|
32
|
+
gitSpawn(git, [...(dir ? ['-C', dir] : []), ...Object.entries(config ?? {}).flatMap(([key, value]) => ['-c', `${key}=${value}`]), ...args], {
|
|
33
|
+
...(cwd ? { cwd } : {}), ...options,
|
|
34
|
+
...((cwd || dir) && options.env === undefined ? { env: withoutGitLocalEnv(process.env) } : {}),
|
|
35
|
+
});
|
|
44
36
|
|
|
45
37
|
/**
|
|
46
38
|
* A caller's git runner folded to (args, opts) -> {ok, stdout, stderr}. `git`: the caller's runner (args, {cwd}) ->
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// log.mjs — `git log <args>`: the commit history in a caller-chosen format.
|
|
2
|
+
import { runGit } from './lib.mjs';
|
|
3
|
+
|
|
4
|
+
/** The spawn result {status, stdout, stderr, error} of `git log ...args`; options are runGit's (cwd or dir, config, timeout, maxBuffer, input, encoding, env, git). */
|
|
5
|
+
export const log = (args = [], options = {}) => runGit(['log', ...args], options);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// ls-files.mjs — `git ls-files <args>`: the paths the index tracks (--cached), or the untracked / deleted / ignored ones
|
|
2
|
+
// the flags ask for (-z keeps any file name intact).
|
|
3
|
+
import { runGit } from './lib.mjs';
|
|
4
|
+
|
|
5
|
+
/** The spawn result {status, stdout, stderr, error} of `git ls-files ...args`; options are runGit's (cwd or dir, config, timeout, maxBuffer, input, encoding, env, git). */
|
|
6
|
+
export const lsFiles = (args = [], options = {}) => runGit(['ls-files', ...args], options);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// ls-tree.mjs — `git ls-tree <args>`: the entries of a tree object.
|
|
2
|
+
import { runGit } from './lib.mjs';
|
|
3
|
+
|
|
4
|
+
/** The spawn result {status, stdout, stderr, error} of `git ls-tree ...args`; options are runGit's (cwd or dir, config, timeout, maxBuffer, input, encoding, env, git). */
|
|
5
|
+
export const lsTree = (args = [], options = {}) => runGit(['ls-tree', ...args], options);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// merge-base.mjs — `git merge-base <a> <b>`: the best common ancestor of two commits. Whether one commit is in another
|
|
2
|
+
// is is-ancestor.mjs.
|
|
3
|
+
import { gitRunner } from './lib.mjs';
|
|
4
|
+
|
|
5
|
+
/** The best common ancestor of commits `a` and `b` (a sha), or null when they share none or a ref does not resolve. */
|
|
6
|
+
export const mergeBase = (cwd, a, b, { git = null } = {}) => {
|
|
7
|
+
const r = gitRunner(git)(['merge-base', a, b], { cwd });
|
|
8
|
+
return r.ok && r.stdout ? r.stdout : null;
|
|
9
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// remote.mjs — `git remote <args>`: the configured remotes (get-url <name>).
|
|
2
|
+
import { runGit } from './lib.mjs';
|
|
3
|
+
|
|
4
|
+
/** The spawn result {status, stdout, stderr, error} of `git remote ...args`; options are runGit's (cwd or dir, config, timeout, maxBuffer, input, encoding, env, git). */
|
|
5
|
+
export const remote = (args = [], options = {}) => runGit(['remote', ...args], options);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// rev-parse-query.mjs — `git rev-parse <args>`: one rev-parse query (--show-toplevel, --git-common-dir, --git-path, a ref): the text it prints.
|
|
2
|
+
import { runGit } from './lib.mjs';
|
|
3
|
+
|
|
4
|
+
/** The spawn result {status, stdout, stderr, error} of `git rev-parse ...args`; options are runGit's (cwd or dir, config, timeout, maxBuffer, input, encoding, env, git). */
|
|
5
|
+
export const revParseQuery = (args = [], options = {}) => runGit(['rev-parse', ...args], options);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// show.mjs — `git show <args>`: an object of a repository - a file at a revision (`<rev>:<path>`), or a commit's
|
|
2
|
+
// header and message (-s --format=...).
|
|
3
|
+
import { runGit } from './lib.mjs';
|
|
4
|
+
|
|
5
|
+
/** The spawn result {status, stdout, stderr, error} of `git show ...args`; options are runGit's (cwd or dir, config, timeout, maxBuffer, input, encoding, env, git). */
|
|
6
|
+
export const show = (args = [], options = {}) => runGit(['show', ...args], options);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// scripts/api/node/lib.mjs — the runner of the runtime's own scripts as child processes: the node binary that runs this
|
|
2
|
+
// process (process.execPath), a hidden window, utf8 text. The call files beside it (run-script.mjs, syntax-check.mjs,
|
|
3
|
+
// run-node.mjs, spawn-node.mjs, exec-node.mjs) each name one use; nothing outside scripts/api/node imports this runner.
|
|
4
|
+
import { assertMutationFence } from '../../lib/mutation-fence.mjs';
|
|
5
|
+
import { execFile, spawn, spawnSync } from 'node:child_process';
|
|
6
|
+
|
|
7
|
+
/** `node <args>` with this process's node binary, waited for; options pass through last (cwd, stdio, env, timeout, maxBuffer, input). */
|
|
8
|
+
export const nodeSpawn = (args, options = {}) => { assertMutationFence({ kind: 'node-effect', args }); return spawnSync(process.execPath, args, { encoding: 'utf8', windowsHide: true, ...options }); };
|
|
9
|
+
|
|
10
|
+
/** `node <args>` with this process's node binary, started and not waited for: the ChildProcess (detached, stdio, env pass through). */
|
|
11
|
+
export const nodeStart = (args, options = {}) => { assertMutationFence({ kind: 'node-effect', args }); return spawn(process.execPath, args, { windowsHide: true, ...options }); };
|
|
12
|
+
|
|
13
|
+
/** `node <args>` with this process's node binary, waited for without blocking the thread: callback(error, stdout, stderr). */
|
|
14
|
+
export const nodeExecFile = (args, options, callback) => { assertMutationFence({ kind: 'node-effect', args }); return execFile(process.execPath, args, { encoding: 'utf8', windowsHide: true, ...options }, callback); };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// spawn-node.mjs — `node <args>` with this process's node binary, started and not waited for: a long-running runtime
|
|
2
|
+
// process (a server, an engine, a detached helper). Returns the ChildProcess.
|
|
3
|
+
import { nodeStart } from './lib.mjs';
|
|
4
|
+
|
|
5
|
+
/** Starts `node <args>`; options (cwd, env, stdio, detached) pass through. */
|
|
6
|
+
export const spawnNode = (args, options = {}) => nodeStart(args, options);
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// scripts/api/process/lib.mjs — what the process call files beside it share: the spawn options every child of the
|
|
2
|
+
// runtime carries. The call files (process-list.mjs, kill-tree.mjs, run-shell.mjs, spawn-detached.mjs, set-priority.mjs,
|
|
3
|
+
// hide-child-windows.mjs, ...) each name one use of the host's process table or a child process.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* `list` with windowsHide: true added to its options argument. `hasArgv`:
|
|
7
|
+
* the call's second argument may be an argv array (spawn, execFile) rather
|
|
8
|
+
* than options (exec). An explicit windowsHide (true or false) is kept.
|
|
9
|
+
*/
|
|
10
|
+
export function withWindowsHide(list, hasArgv) {
|
|
11
|
+
const out = [...list];
|
|
12
|
+
let slot = 1;
|
|
13
|
+
if (hasArgv && (Array.isArray(out[1]) || (out[1] == null && out.length > 2))) slot = 2;
|
|
14
|
+
const options = out[slot];
|
|
15
|
+
if (options == null) out.splice(slot, out.length > slot ? 1 : 0, { windowsHide: true });
|
|
16
|
+
else if (typeof options === 'function') out.splice(slot, 0, { windowsHide: true });
|
|
17
|
+
else if (typeof options === 'object' && !('windowsHide' in options)) out[slot] = { ...options, windowsHide: true };
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ---- the host process table (process-list.mjs, process-list-async.mjs) ------------------------------------------
|
|
22
|
+
|
|
23
|
+
const psQuote = (s) => String(s).replace(/'/g, "''");
|
|
24
|
+
|
|
25
|
+
/** The PowerShell script of one process-table read: Win32_Process rows (optionally filtered), with CPU when asked. */
|
|
26
|
+
export function processListScript({ where = null, cmdMax = 4000, cpu = false } = {}) {
|
|
27
|
+
const filter = where ? ` -Filter '${psQuote(where)}'` : '';
|
|
28
|
+
return [
|
|
29
|
+
...(cpu ? ['$perf = @{}', 'Get-CimInstance Win32_PerfFormattedData_PerfProc_Process | ForEach-Object { if ($_.IDProcess -gt 0) { $perf[[int]$_.IDProcess] = [double]$_.PercentProcessorTime } }'] : []),
|
|
30
|
+
`Get-CimInstance Win32_Process${filter} | ForEach-Object { $c = [string]$_.CommandLine; [pscustomobject]@{ pid = [int]$_.ProcessId; ppid = [int]$_.ParentProcessId; name = [string]$_.Name; exe = [string]$_.ExecutablePath;`,
|
|
31
|
+
` cmd = $c.Substring(0, [Math]::Min(${Number(cmdMax) || 4000}, $c.Length)); ws = [int64]$_.WorkingSetSize;`,
|
|
32
|
+
` created = $(if ($_.CreationDate) { ([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds() } else { 0 })${cpu ? '; cpu = $perf[[int]$_.ProcessId]' : ''} } } | ConvertTo-Json -Compress`,
|
|
33
|
+
].join('\n');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The rows of the PowerShell read's JSON (one object or an array). */
|
|
37
|
+
export const processRowsOfJson = (text) => {
|
|
38
|
+
const t = String(text ?? '').trim();
|
|
39
|
+
const v = t ? JSON.parse(t) : [];
|
|
40
|
+
return (Array.isArray(v) ? v : [v]).map((p) => ({ ...p, pid: Number(p.pid), ppid: Number(p.ppid), cmd: String(p.cmd ?? '') }));
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/** The rows of `ps -eo pid=,ppid=,comm=,args=` (exe/created/ws are null there). */
|
|
44
|
+
export const processRowsOfPs = (text, cmdMax = 4000) => String(text ?? '').split(/\r?\n/).map((line) => /^\s*(\d+)\s+(\d+)\s+(\S+)\s+(.*)$/.exec(line)).filter(Boolean)
|
|
45
|
+
.map((m) => ({ pid: Number(m[1]), ppid: Number(m[2]), name: m[3].split('/').pop(), exe: null, cmd: m[4].slice(0, cmdMax), created: null, ws: null }));
|
|
46
|
+
|
|
47
|
+
// ---- the environment of other processes (process-env.mjs) -------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
// Windows keeps a process's environment block in its PEB: PEB.ProcessParameters (+0x20) -> Environment (+0x80) and
|
|
50
|
+
// EnvironmentSize (+0x3F0), x64 only. A same-user process opens with PROCESS_QUERY_INFORMATION | PROCESS_VM_READ (0x0410).
|
|
51
|
+
export const PROCESS_ENV_NATIVE = `
|
|
52
|
+
using System;
|
|
53
|
+
using System.Collections.Generic;
|
|
54
|
+
using System.Runtime.InteropServices;
|
|
55
|
+
using System.Text;
|
|
56
|
+
public static class StarciProcessEnv {
|
|
57
|
+
[StructLayout(LayoutKind.Sequential)] struct PBI { public IntPtr Reserved1; public IntPtr PebBaseAddress; public IntPtr Reserved2a; public IntPtr Reserved2b; public IntPtr UniqueProcessId; public IntPtr Reserved3; }
|
|
58
|
+
[DllImport("ntdll.dll")] static extern int NtQueryInformationProcess(IntPtr h, int cls, ref PBI pbi, int len, out int ret);
|
|
59
|
+
[DllImport("kernel32.dll")] static extern bool ReadProcessMemory(IntPtr h, IntPtr addr, byte[] buf, IntPtr size, out IntPtr read);
|
|
60
|
+
[DllImport("kernel32.dll")] static extern IntPtr OpenProcess(int access, bool inherit, int pid);
|
|
61
|
+
[DllImport("kernel32.dll")] static extern bool CloseHandle(IntPtr h);
|
|
62
|
+
[DllImport("kernel32.dll")] static extern bool IsWow64Process(IntPtr h, out bool wow);
|
|
63
|
+
static byte[] Read(IntPtr h, IntPtr addr, int size) {
|
|
64
|
+
var buf = new byte[size]; IntPtr n;
|
|
65
|
+
if (!ReadProcessMemory(h, addr, buf, (IntPtr)size, out n) || (int)n != size) return null;
|
|
66
|
+
return buf;
|
|
67
|
+
}
|
|
68
|
+
public static Dictionary<string, string> Env(int pid) {
|
|
69
|
+
var h = OpenProcess(0x0410, false, pid);
|
|
70
|
+
if (h == IntPtr.Zero) return null;
|
|
71
|
+
try { return EnvHandle(h); } finally { CloseHandle(h); }
|
|
72
|
+
}
|
|
73
|
+
public static Dictionary<string, string> EnvHandle(IntPtr h) {
|
|
74
|
+
bool wow; if (IsWow64Process(h, out wow) && wow) return null;
|
|
75
|
+
var pbi = new PBI(); int ret;
|
|
76
|
+
if (NtQueryInformationProcess(h, 0, ref pbi, Marshal.SizeOf(pbi), out ret) != 0) return null;
|
|
77
|
+
var peb = Read(h, pbi.PebBaseAddress + 0x20, 8); if (peb == null) return null;
|
|
78
|
+
var parameters = (IntPtr)BitConverter.ToInt64(peb, 0);
|
|
79
|
+
var envPtr = Read(h, parameters + 0x80, 8); if (envPtr == null) return null;
|
|
80
|
+
var sizeBuf = Read(h, parameters + 0x3F0, 8); if (sizeBuf == null) return null;
|
|
81
|
+
long size = BitConverter.ToInt64(sizeBuf, 0);
|
|
82
|
+
if (size <= 0 || size > 4 * 1024 * 1024) return null;
|
|
83
|
+
var block = Read(h, (IntPtr)BitConverter.ToInt64(envPtr, 0), (int)size); if (block == null) return null;
|
|
84
|
+
var d = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
85
|
+
foreach (var entry in Encoding.Unicode.GetString(block).Split('\\0')) {
|
|
86
|
+
if (entry.Length == 0) continue;
|
|
87
|
+
int i = entry.IndexOf('=', 1); if (i < 0) continue;
|
|
88
|
+
d[entry.Substring(0, i)] = entry.Substring(i + 1);
|
|
89
|
+
}
|
|
90
|
+
return d;
|
|
91
|
+
}
|
|
92
|
+
}`;
|
|
93
|
+
|
|
94
|
+
/** The PowerShell script reading `names` from the environment of `pids` (every process when null): JSON [{pid, readable, values}]. */
|
|
95
|
+
export function processEnvScript({ names, pids = null }) {
|
|
96
|
+
const wanted = JSON.stringify([...names].map(String));
|
|
97
|
+
const ids = pids ? `@(${[...pids].map((p) => Number(p)).filter(Number.isInteger).join(',')})` : '(Get-Process).Id';
|
|
98
|
+
return [
|
|
99
|
+
`Add-Type -TypeDefinition @'${PROCESS_ENV_NATIVE}\n'@ -Language CSharp`,
|
|
100
|
+
`$names = '${psQuote(wanted)}' | ConvertFrom-Json`,
|
|
101
|
+
`$rows = foreach ($id in ${ids}) { $e = [StarciProcessEnv]::Env([int]$id); $v = [ordered]@{}; foreach ($n in $names) { $v[$n] = if ($null -ne $e -and $e.ContainsKey($n)) { $e[$n] } else { $null } }; [pscustomobject]@{ pid = [int]$id; readable = ($null -ne $e); values = $v } }`,
|
|
102
|
+
'@($rows) | ConvertTo-Json -Compress -Depth 4',
|
|
103
|
+
].join('\n');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** The rows of that script's JSON. */
|
|
107
|
+
export const processEnvRowsOfJson = (text) => {
|
|
108
|
+
const t = String(text ?? '').trim();
|
|
109
|
+
const v = t ? JSON.parse(t) : [];
|
|
110
|
+
return (Array.isArray(v) ? v : [v]).map((r) => ({ pid: Number(r.pid), readable: r.readable === true, values: r.values ?? {} }));
|
|
111
|
+
};
|