pipework 0.8.1 → 0.8.4
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 +14 -0
- package/dist/async/jobs/execute.d.ts +2 -0
- package/dist/async/jobs/execute.d.ts.map +1 -1
- package/dist/async/jobs/execute.js +17 -2
- package/dist/async/jobs/execute.js.map +1 -1
- package/dist/audit/query/cursor.d.ts +7 -0
- package/dist/audit/query/cursor.d.ts.map +1 -0
- package/dist/audit/query/cursor.js +25 -0
- package/dist/audit/query/cursor.js.map +1 -0
- package/dist/audit/query/index.d.ts +5 -0
- package/dist/audit/query/index.d.ts.map +1 -0
- package/dist/audit/query/index.js +3 -0
- package/dist/audit/query/index.js.map +1 -0
- package/dist/audit/query/query.d.ts +49 -0
- package/dist/audit/query/query.d.ts.map +1 -0
- package/dist/audit/query/query.js +119 -0
- package/dist/audit/query/query.js.map +1 -0
- package/dist/audit/schema/audit-record-fields.d.ts +15 -0
- package/dist/audit/schema/audit-record-fields.d.ts.map +1 -0
- package/dist/audit/schema/audit-record-fields.js +50 -0
- package/dist/audit/schema/audit-record-fields.js.map +1 -0
- package/dist/audit/schema/audit-record.d.ts +15 -0
- package/dist/audit/schema/audit-record.d.ts.map +1 -0
- package/dist/audit/schema/audit-record.js +12 -0
- package/dist/audit/schema/audit-record.js.map +1 -0
- package/dist/auth/tenant/scope.js +1 -1
- package/dist/auth/tenant/scope.js.map +1 -1
- package/dist/auth/tenant/scoped-db.d.ts.map +1 -1
- package/dist/auth/tenant/scoped-db.js +1 -2
- package/dist/auth/tenant/scoped-db.js.map +1 -1
- package/dist/cli/commands/check.d.ts +4 -0
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +187 -15
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/codemod.d.ts +6 -0
- package/dist/cli/commands/codemod.d.ts.map +1 -0
- package/dist/cli/commands/codemod.js +63 -0
- package/dist/cli/commands/codemod.js.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +9 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/config/load.d.ts +23 -0
- package/dist/core/config/load.d.ts.map +1 -1
- package/dist/core/config/load.js +8 -0
- package/dist/core/config/load.js.map +1 -1
- package/dist/core/config/namespace.d.ts +19 -0
- package/dist/core/config/namespace.d.ts.map +1 -1
- package/dist/core/config/schema.d.ts +41 -0
- package/dist/core/config/schema.d.ts.map +1 -1
- package/dist/core/config/schema.js +27 -0
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/logging/logger.d.ts +3 -1
- package/dist/core/logging/logger.d.ts.map +1 -1
- package/dist/core/logging/logger.js +2 -2
- package/dist/core/logging/logger.js.map +1 -1
- package/dist/core/pipework.d.ts +2 -2
- package/dist/core/pipework.d.ts.map +1 -1
- package/dist/data/db/bulk-set.js +1 -1
- package/dist/data/db/bulk-set.js.map +1 -1
- package/dist/data/db/composed.js +1 -1
- package/dist/data/db/composed.js.map +1 -1
- package/dist/data/db/db.d.ts +10 -0
- package/dist/data/db/db.d.ts.map +1 -1
- package/dist/data/db/db.js +12 -0
- package/dist/data/db/db.js.map +1 -1
- package/dist/data/db/filter.d.ts +42 -43
- package/dist/data/db/filter.d.ts.map +1 -1
- package/dist/data/db/filter.js +7 -6
- package/dist/data/db/filter.js.map +1 -1
- package/dist/data/db/namespace.d.ts +90 -82
- package/dist/data/db/namespace.d.ts.map +1 -1
- package/dist/data/db/namespace.js +21 -5
- package/dist/data/db/namespace.js.map +1 -1
- package/dist/data/migrate/post-process.d.ts +2 -1
- package/dist/data/migrate/post-process.d.ts.map +1 -1
- package/dist/data/migrate/post-process.js +2 -1
- package/dist/data/migrate/post-process.js.map +1 -1
- package/dist/data/temporal/definition-queries.js +1 -1
- package/dist/data/temporal/definition-queries.js.map +1 -1
- package/dist/data/temporal/query.js +1 -1
- package/dist/data/temporal/query.js.map +1 -1
- package/dist/data/temporal/resolve.js +1 -1
- package/dist/data/temporal/resolve.js.map +1 -1
- package/dist/data/write/audit/actor.d.ts +3 -0
- package/dist/data/write/audit/actor.d.ts.map +1 -0
- package/dist/data/write/audit/actor.js +15 -0
- package/dist/data/write/audit/actor.js.map +1 -0
- package/dist/data/write/audit/audit-columns.d.ts +6 -0
- package/dist/data/write/audit/audit-columns.d.ts.map +1 -0
- package/dist/data/write/audit/audit-columns.js +15 -0
- package/dist/data/write/audit/audit-columns.js.map +1 -0
- package/dist/data/write/audit/audit-select.d.ts +28 -0
- package/dist/data/write/audit/audit-select.d.ts.map +1 -0
- package/dist/data/write/audit/audit-select.js +49 -0
- package/dist/data/write/audit/audit-select.js.map +1 -0
- package/dist/data/write/audit/cte-compile.d.ts +7 -0
- package/dist/data/write/audit/cte-compile.d.ts.map +1 -0
- package/dist/data/write/audit/cte-compile.js +23 -0
- package/dist/data/write/audit/cte-compile.js.map +1 -0
- package/dist/data/write/audit/final-select.d.ts +4 -0
- package/dist/data/write/audit/final-select.d.ts.map +1 -0
- package/dist/data/write/audit/final-select.js +28 -0
- package/dist/data/write/audit/final-select.js.map +1 -0
- package/dist/data/write/audit/primary-key.d.ts +7 -0
- package/dist/data/write/audit/primary-key.d.ts.map +1 -0
- package/dist/data/write/audit/primary-key.js +32 -0
- package/dist/data/write/audit/primary-key.js.map +1 -0
- package/dist/data/write/audit/projection.d.ts +11 -0
- package/dist/data/write/audit/projection.d.ts.map +1 -0
- package/dist/data/write/audit/projection.js +37 -0
- package/dist/data/write/audit/projection.js.map +1 -0
- package/dist/data/write/audit/read-touches.d.ts +10 -0
- package/dist/data/write/audit/read-touches.d.ts.map +1 -0
- package/dist/data/write/audit/read-touches.js +63 -0
- package/dist/data/write/audit/read-touches.js.map +1 -0
- package/dist/data/write/audit/verb-delete.d.ts +7 -0
- package/dist/data/write/audit/verb-delete.d.ts.map +1 -0
- package/dist/data/write/audit/verb-delete.js +53 -0
- package/dist/data/write/audit/verb-delete.js.map +1 -0
- package/dist/data/write/audit/verb-insert.d.ts +7 -0
- package/dist/data/write/audit/verb-insert.d.ts.map +1 -0
- package/dist/data/write/audit/verb-insert.js +28 -0
- package/dist/data/write/audit/verb-insert.js.map +1 -0
- package/dist/data/write/audit/verb-update.d.ts +7 -0
- package/dist/data/write/audit/verb-update.d.ts.map +1 -0
- package/dist/data/write/audit/verb-update.js +52 -0
- package/dist/data/write/audit/verb-update.js.map +1 -0
- package/dist/data/write/audit/verb-upsert.d.ts +7 -0
- package/dist/data/write/audit/verb-upsert.d.ts.map +1 -0
- package/dist/data/write/audit/verb-upsert.js +81 -0
- package/dist/data/write/audit/verb-upsert.js.map +1 -0
- package/dist/data/write/delete.d.ts +15 -0
- package/dist/data/write/delete.d.ts.map +1 -0
- package/dist/data/write/delete.js +25 -0
- package/dist/data/write/delete.js.map +1 -0
- package/dist/data/write/execute.d.ts +3 -0
- package/dist/data/write/execute.d.ts.map +1 -0
- package/dist/data/write/execute.js +162 -0
- package/dist/data/write/execute.js.map +1 -0
- package/dist/data/write/index.d.ts +6 -0
- package/dist/data/write/index.d.ts.map +1 -0
- package/dist/data/write/index.js +10 -0
- package/dist/data/write/index.js.map +1 -0
- package/dist/data/write/insert.d.ts +18 -0
- package/dist/data/write/insert.d.ts.map +1 -0
- package/dist/data/write/insert.js +29 -0
- package/dist/data/write/insert.js.map +1 -0
- package/dist/data/write/state.d.ts +29 -0
- package/dist/data/write/state.d.ts.map +1 -0
- package/dist/data/write/state.js +9 -0
- package/dist/data/write/state.js.map +1 -0
- package/dist/data/write/update.d.ts +17 -0
- package/dist/data/write/update.d.ts.map +1 -0
- package/dist/data/write/update.js +39 -0
- package/dist/data/write/update.js.map +1 -0
- package/dist/data/write/upsert.d.ts +21 -0
- package/dist/data/write/upsert.d.ts.map +1 -0
- package/dist/data/write/upsert.js +42 -0
- package/dist/data/write/upsert.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/infra/audit/namespace.d.ts +3 -0
- package/dist/infra/audit/namespace.d.ts.map +1 -1
- package/dist/infra/audit/namespace.js +3 -0
- package/dist/infra/audit/namespace.js.map +1 -1
- package/dist/lint/config.d.ts +4 -2
- package/dist/lint/config.d.ts.map +1 -1
- package/dist/lint/config.js +9 -5
- package/dist/lint/config.js.map +1 -1
- package/dist/lint/index.d.ts +2 -1
- package/dist/lint/index.d.ts.map +1 -1
- package/dist/lint/index.js +1 -0
- package/dist/lint/index.js.map +1 -1
- package/dist/lint/resolver.d.ts +16 -0
- package/dist/lint/resolver.d.ts.map +1 -0
- package/dist/lint/resolver.js +61 -0
- package/dist/lint/resolver.js.map +1 -0
- package/dist/request/context/create.d.ts +4 -0
- package/dist/request/context/create.d.ts.map +1 -1
- package/dist/request/context/create.js +3 -0
- package/dist/request/context/create.js.map +1 -1
- package/dist/request/context/types.d.ts +3 -0
- package/dist/request/context/types.d.ts.map +1 -1
- package/dist/request/http/middleware.d.ts.map +1 -1
- package/dist/request/http/middleware.js +30 -0
- package/dist/request/http/middleware.js.map +1 -1
- package/dist/request/http/server.d.ts.map +1 -1
- package/dist/request/http/server.js +6 -1
- package/dist/request/http/server.js.map +1 -1
- package/dist/test/context.d.ts.map +1 -1
- package/dist/test/context.js +43 -2
- package/dist/test/context.js.map +1 -1
- package/dist/test/index.d.ts +1 -1
- package/dist/test/index.d.ts.map +1 -1
- package/dist/test/index.js +1 -1
- package/dist/test/index.js.map +1 -1
- package/dist/test/vitest-workspace.d.ts +15 -2
- package/dist/test/vitest-workspace.d.ts.map +1 -1
- package/dist/test/vitest-workspace.js +60 -7
- package/dist/test/vitest-workspace.js.map +1 -1
- package/dist/trace/cardinality/analyze.d.ts +26 -0
- package/dist/trace/cardinality/analyze.d.ts.map +1 -0
- package/dist/trace/cardinality/analyze.js +129 -0
- package/dist/trace/cardinality/analyze.js.map +1 -0
- package/dist/trace/cardinality/branch-context.d.ts +13 -0
- package/dist/trace/cardinality/branch-context.d.ts.map +1 -0
- package/dist/trace/cardinality/branch-context.js +182 -0
- package/dist/trace/cardinality/branch-context.js.map +1 -0
- package/dist/trace/cardinality/check.d.ts +4 -0
- package/dist/trace/cardinality/check.d.ts.map +1 -0
- package/dist/trace/cardinality/check.js +29 -0
- package/dist/trace/cardinality/check.js.map +1 -0
- package/dist/trace/cardinality/classify-edge.d.ts +31 -0
- package/dist/trace/cardinality/classify-edge.d.ts.map +1 -0
- package/dist/trace/cardinality/classify-edge.js +27 -0
- package/dist/trace/cardinality/classify-edge.js.map +1 -0
- package/dist/trace/cardinality/structural-max.d.ts +14 -0
- package/dist/trace/cardinality/structural-max.d.ts.map +1 -0
- package/dist/trace/cardinality/structural-max.js +83 -0
- package/dist/trace/cardinality/structural-max.js.map +1 -0
- package/dist/trace/check/coverage-check.d.ts +20 -0
- package/dist/trace/check/coverage-check.d.ts.map +1 -0
- package/dist/trace/check/coverage-check.js +146 -0
- package/dist/trace/check/coverage-check.js.map +1 -0
- package/dist/trace/check/flow-check.d.ts +13 -0
- package/dist/trace/check/flow-check.d.ts.map +1 -0
- package/dist/trace/check/flow-check.js +82 -0
- package/dist/trace/check/flow-check.js.map +1 -0
- package/dist/trace/check/flow-step-site.d.ts +62 -0
- package/dist/trace/check/flow-step-site.d.ts.map +1 -0
- package/dist/trace/check/flow-step-site.js +142 -0
- package/dist/trace/check/flow-step-site.js.map +1 -0
- package/dist/trace/check/io-pattern.d.ts +14 -0
- package/dist/trace/check/io-pattern.d.ts.map +1 -0
- package/dist/trace/check/io-pattern.js +113 -0
- package/dist/trace/check/io-pattern.js.map +1 -0
- package/dist/trace/codemod/add-cardinality.d.ts +38 -0
- package/dist/trace/codemod/add-cardinality.d.ts.map +1 -0
- package/dist/trace/codemod/add-cardinality.js +106 -0
- package/dist/trace/codemod/add-cardinality.js.map +1 -0
- package/dist/trace/codemod/run.d.ts +25 -0
- package/dist/trace/codemod/run.d.ts.map +1 -0
- package/dist/trace/codemod/run.js +47 -0
- package/dist/trace/codemod/run.js.map +1 -0
- package/dist/trace/entry/close-trace.d.ts +7 -0
- package/dist/trace/entry/close-trace.d.ts.map +1 -0
- package/dist/trace/entry/close-trace.js +54 -0
- package/dist/trace/entry/close-trace.js.map +1 -0
- package/dist/trace/entry/finalize-trace.d.ts +5 -0
- package/dist/trace/entry/finalize-trace.d.ts.map +1 -0
- package/dist/trace/entry/finalize-trace.js +23 -0
- package/dist/trace/entry/finalize-trace.js.map +1 -0
- package/dist/trace/entry/open-trace.d.ts +26 -0
- package/dist/trace/entry/open-trace.d.ts.map +1 -0
- package/dist/trace/entry/open-trace.js +75 -0
- package/dist/trace/entry/open-trace.js.map +1 -0
- package/dist/trace/entry/parent-trace.d.ts +4 -0
- package/dist/trace/entry/parent-trace.d.ts.map +1 -0
- package/dist/trace/entry/parent-trace.js +10 -0
- package/dist/trace/entry/parent-trace.js.map +1 -0
- package/dist/trace/entry/parse-traceparent.d.ts +8 -0
- package/dist/trace/entry/parse-traceparent.d.ts.map +1 -0
- package/dist/trace/entry/parse-traceparent.js +37 -0
- package/dist/trace/entry/parse-traceparent.js.map +1 -0
- package/dist/trace/entry/trace-context.d.ts +19 -0
- package/dist/trace/entry/trace-context.d.ts.map +1 -0
- package/dist/trace/entry/trace-context.js +5 -0
- package/dist/trace/entry/trace-context.js.map +1 -0
- package/dist/trace/entry/tracing-enabled.d.ts +4 -0
- package/dist/trace/entry/tracing-enabled.d.ts.map +1 -0
- package/dist/trace/entry/tracing-enabled.js +8 -0
- package/dist/trace/entry/tracing-enabled.js.map +1 -0
- package/dist/trace/partition/plan.d.ts +2 -0
- package/dist/trace/partition/plan.d.ts.map +1 -1
- package/dist/trace/partition/plan.js +8 -1
- package/dist/trace/partition/plan.js.map +1 -1
- package/dist/trace/partition/schedule.d.ts.map +1 -1
- package/dist/trace/partition/schedule.js +6 -4
- package/dist/trace/partition/schedule.js.map +1 -1
- package/dist/trace/partition/startup-hook.d.ts.map +1 -1
- package/dist/trace/partition/startup-hook.js +6 -2
- package/dist/trace/partition/startup-hook.js.map +1 -1
- package/dist/trace/partition/tables.d.ts +1 -1
- package/dist/trace/partition/tables.d.ts.map +1 -1
- package/dist/trace/partition/tables.js +13 -5
- package/dist/trace/partition/tables.js.map +1 -1
- package/dist/trace/runtime/expire-stale-traces.d.ts +10 -0
- package/dist/trace/runtime/expire-stale-traces.d.ts.map +1 -0
- package/dist/trace/runtime/expire-stale-traces.js +36 -0
- package/dist/trace/runtime/expire-stale-traces.js.map +1 -0
- package/dist/trace/runtime/flow-step.d.ts +6 -0
- package/dist/trace/runtime/flow-step.d.ts.map +1 -0
- package/dist/trace/runtime/flow-step.js +122 -0
- package/dist/trace/runtime/flow-step.js.map +1 -0
- package/dist/trace/runtime/flush-steps.d.ts +5 -0
- package/dist/trace/runtime/flush-steps.d.ts.map +1 -0
- package/dist/trace/runtime/flush-steps.js +23 -0
- package/dist/trace/runtime/flush-steps.js.map +1 -0
- package/dist/trace/runtime/step-buffer.d.ts +139 -0
- package/dist/trace/runtime/step-buffer.d.ts.map +1 -0
- package/dist/trace/runtime/step-buffer.js +140 -0
- package/dist/trace/runtime/step-buffer.js.map +1 -0
- package/dist/trace/runtime/step-error.d.ts +8 -0
- package/dist/trace/runtime/step-error.d.ts.map +1 -0
- package/dist/trace/runtime/step-error.js +13 -0
- package/dist/trace/runtime/step-error.js.map +1 -0
- package/dist/trace/runtime/step-meta.d.ts +48 -0
- package/dist/trace/runtime/step-meta.d.ts.map +1 -0
- package/dist/trace/runtime/step-meta.js +6 -0
- package/dist/trace/runtime/step-meta.js.map +1 -0
- package/dist/trace/runtime/thenable.d.ts +3 -0
- package/dist/trace/runtime/thenable.d.ts.map +1 -0
- package/dist/trace/runtime/thenable.js +12 -0
- package/dist/trace/runtime/thenable.js.map +1 -0
- package/dist/trace/runtime/truncate-trace.d.ts +5 -0
- package/dist/trace/runtime/truncate-trace.d.ts.map +1 -0
- package/dist/trace/runtime/truncate-trace.js +44 -0
- package/dist/trace/runtime/truncate-trace.js.map +1 -0
- package/dist/trace/schema/trace-fields.d.ts +1 -1
- package/dist/trace/schema/trace-retained.d.ts +1 -1
- package/dist/trace/schema/trace.d.ts +1 -1
- package/dist/trace/transformer/build-meta-literal.d.ts +3 -0
- package/dist/trace/transformer/build-meta-literal.d.ts.map +1 -0
- package/dist/trace/transformer/build-meta-literal.js +8 -0
- package/dist/trace/transformer/build-meta-literal.js.map +1 -0
- package/dist/trace/transformer/find-flow-step-sites.d.ts +24 -0
- package/dist/trace/transformer/find-flow-step-sites.d.ts.map +1 -0
- package/dist/trace/transformer/find-flow-step-sites.js +69 -0
- package/dist/trace/transformer/find-flow-step-sites.js.map +1 -0
- package/dist/trace/transformer/inject-flow-step-import.d.ts +3 -0
- package/dist/trace/transformer/inject-flow-step-import.d.ts.map +1 -0
- package/dist/trace/transformer/inject-flow-step-import.js +28 -0
- package/dist/trace/transformer/inject-flow-step-import.js.map +1 -0
- package/dist/trace/transformer/transform-error.d.ts +8 -0
- package/dist/trace/transformer/transform-error.d.ts.map +1 -0
- package/dist/trace/transformer/transform-error.js +5 -0
- package/dist/trace/transformer/transform-error.js.map +1 -0
- package/dist/trace/transformer/transform-flow-steps.d.ts +12 -0
- package/dist/trace/transformer/transform-flow-steps.d.ts.map +1 -0
- package/dist/trace/transformer/transform-flow-steps.js +85 -0
- package/dist/trace/transformer/transform-flow-steps.js.map +1 -0
- package/dist/trace/transformer/wrap-function-declaration.d.ts +3 -0
- package/dist/trace/transformer/wrap-function-declaration.d.ts.map +1 -0
- package/dist/trace/transformer/wrap-function-declaration.js +28 -0
- package/dist/trace/transformer/wrap-function-declaration.js.map +1 -0
- package/dist/trace/transformer/wrap-variable-declaration.d.ts +3 -0
- package/dist/trace/transformer/wrap-variable-declaration.d.ts.map +1 -0
- package/dist/trace/transformer/wrap-variable-declaration.js +30 -0
- package/dist/trace/transformer/wrap-variable-declaration.js.map +1 -0
- package/dist/workspace/dependency-graph.d.ts.map +1 -1
- package/dist/workspace/dependency-graph.js +2 -3
- package/dist/workspace/dependency-graph.js.map +1 -1
- package/dist/workspace/generate.d.ts.map +1 -1
- package/dist/workspace/generate.js +63 -10
- package/dist/workspace/generate.js.map +1 -1
- package/dist/workspace/module-package-name.d.ts +3 -0
- package/dist/workspace/module-package-name.d.ts.map +1 -0
- package/dist/workspace/module-package-name.js +5 -0
- package/dist/workspace/module-package-name.js.map +1 -0
- package/package.json +21 -6
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Structural cardinality verdict for one declared `@downstream` edge: locate
|
|
2
|
+
// the edge's call sites in the flow-step body and decide whether the edge is a
|
|
3
|
+
// plain `step`, a `branch` (a single call site reached through a loop, an
|
|
4
|
+
// iteration method, a fan-out, or recursion), or `ambiguous` — reached through
|
|
5
|
+
// indirection the analyzer cannot follow. See PLAN.tracing.md, Q1.
|
|
6
|
+
import { findCallSites } from './branch-context.js';
|
|
7
|
+
/** Classifies one `@downstream` edge of `site` against the flow-step body. */
|
|
8
|
+
export function classifyEdge(file, site, edge) {
|
|
9
|
+
const from = site.subject.name;
|
|
10
|
+
const base = { file, from, edge };
|
|
11
|
+
const body = site.subject.body;
|
|
12
|
+
if (body === null) {
|
|
13
|
+
// An orphan annotation — no function body to walk. The `flow` check owns
|
|
14
|
+
// the diagnostic; here the edge is simply unresolvable.
|
|
15
|
+
return { ...base, structural: 'ambiguous', branchKind: null, line: null };
|
|
16
|
+
}
|
|
17
|
+
const sites = findCallSites(body, from, edge.symbol);
|
|
18
|
+
if (sites.length === 0) {
|
|
19
|
+
return { ...base, structural: 'ambiguous', branchKind: null, line: null };
|
|
20
|
+
}
|
|
21
|
+
const branching = sites.find(s => s.branchKind !== null);
|
|
22
|
+
if (branching !== undefined) {
|
|
23
|
+
return { ...base, structural: 'branch', branchKind: branching.branchKind, line: branching.line };
|
|
24
|
+
}
|
|
25
|
+
return { ...base, structural: 'step', branchKind: null, line: sites[0].line };
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=classify-edge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify-edge.js","sourceRoot":"","sources":["../../../src/trace/cardinality/classify-edge.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,+EAA+E;AAC/E,0EAA0E;AAC1E,+EAA+E;AAC/E,mEAAmE;AAInE,OAAO,EAAE,aAAa,EAAmB,MAAM,qBAAqB,CAAA;AA6BpE,8EAA8E;AAC9E,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,IAAkB,EAClB,IAAoB;IAEpB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAA;IAC9B,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;IAEjC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAA;IAC9B,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,yEAAyE;QACzE,wDAAwD;QACxD,OAAO,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;IAC3E,CAAC;IAED,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;IACpD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;IAC3E,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,IAAI,CAAC,CAAA;IACxD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,CAAA;IAClG,CAAC;IACD,OAAO,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAA;AAChF,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** One `step` edge of the flow graph, both endpoints as `path:symbol` node ids. */
|
|
2
|
+
export interface StepEdge {
|
|
3
|
+
readonly from: string;
|
|
4
|
+
readonly to: string;
|
|
5
|
+
}
|
|
6
|
+
export interface StructuralMax {
|
|
7
|
+
/** Longest chain of `step` edges through the graph, counted in nodes. */
|
|
8
|
+
readonly value: number;
|
|
9
|
+
/** Every `step`-edge cycle found — each a ring of `path:symbol` node ids. */
|
|
10
|
+
readonly cycles: readonly (readonly string[])[];
|
|
11
|
+
}
|
|
12
|
+
/** Longest `step`-edge chain across the flow graph, plus any `step`-edge cycles. */
|
|
13
|
+
export declare function computeStructuralMax(nodeIds: readonly string[], edges: readonly StepEdge[]): StructuralMax;
|
|
14
|
+
//# sourceMappingURL=structural-max.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structural-max.d.ts","sourceRoot":"","sources":["../../../src/trace/cardinality/structural-max.ts"],"names":[],"mappings":"AAWA,mFAAmF;AACnF,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,yEAAyE;IACzE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAA;CAChD;AAED,oFAAoF;AACpF,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,KAAK,EAAE,SAAS,QAAQ,EAAE,GACzB,aAAa,CAqCf"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// The Q7 step-cap derivation. Once batching is forced to branch, a single
|
|
2
|
+
// trace's step count is bounded by the static flow graph: a `branch` edge
|
|
3
|
+
// opens a *child* trace, so only `step` edges extend the current one.
|
|
4
|
+
// `structuralMax` is the longest chain of `step` edges, counted in nodes.
|
|
5
|
+
//
|
|
6
|
+
// A cycle of `step` edges is unbounded — and a bug. A direct self-call is
|
|
7
|
+
// caught as recursion by branch-context, but *mutual* recursion (A step-calls
|
|
8
|
+
// B, B step-calls A) is invisible to single-function analysis; it surfaces
|
|
9
|
+
// here as a `step`-edge cycle. One of the edges that close the loop should
|
|
10
|
+
// have been a branch. See PLAN.tracing.md, Q7.
|
|
11
|
+
/** Longest `step`-edge chain across the flow graph, plus any `step`-edge cycles. */
|
|
12
|
+
export function computeStructuralMax(nodeIds, edges) {
|
|
13
|
+
const adjacency = buildAdjacency(nodeIds, edges);
|
|
14
|
+
const longest = new Map();
|
|
15
|
+
const visiting = new Set();
|
|
16
|
+
const cycles = [];
|
|
17
|
+
function dfs(node, stack) {
|
|
18
|
+
const cached = longest.get(node);
|
|
19
|
+
if (cached !== undefined)
|
|
20
|
+
return cached;
|
|
21
|
+
if (visiting.has(node)) {
|
|
22
|
+
// Back-edge: record the ring and treat this edge as contributing nothing,
|
|
23
|
+
// so the walk terminates. The node's own length is left uncached.
|
|
24
|
+
cycles.push([...stack.slice(stack.indexOf(node)), node]);
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
27
|
+
visiting.add(node);
|
|
28
|
+
stack.push(node);
|
|
29
|
+
let best = 0;
|
|
30
|
+
// `node` is always an adjacency key — it came from `keys()` or from a
|
|
31
|
+
// neighbour set, and `buildAdjacency` ensures every endpoint is a key.
|
|
32
|
+
for (const target of adjacency.get(node)) {
|
|
33
|
+
best = Math.max(best, dfs(target, stack));
|
|
34
|
+
}
|
|
35
|
+
stack.pop();
|
|
36
|
+
visiting.delete(node);
|
|
37
|
+
const result = 1 + best;
|
|
38
|
+
longest.set(node, result);
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
let value = 0;
|
|
42
|
+
for (const node of adjacency.keys()) {
|
|
43
|
+
value = Math.max(value, dfs(node, []));
|
|
44
|
+
}
|
|
45
|
+
return { value, cycles: dedupeCycles(cycles) };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The `step`-edge adjacency of the flow graph. Every analyzed flow step is a
|
|
49
|
+
* node; a `step` edge to a target that is not itself an analyzed flow step
|
|
50
|
+
* still adds that target as a terminal node, so the cap stays an upper bound.
|
|
51
|
+
*/
|
|
52
|
+
function buildAdjacency(nodeIds, edges) {
|
|
53
|
+
const adjacency = new Map();
|
|
54
|
+
const ensure = (id) => {
|
|
55
|
+
let targets = adjacency.get(id);
|
|
56
|
+
if (targets === undefined) {
|
|
57
|
+
targets = new Set();
|
|
58
|
+
adjacency.set(id, targets);
|
|
59
|
+
}
|
|
60
|
+
return targets;
|
|
61
|
+
};
|
|
62
|
+
for (const id of nodeIds)
|
|
63
|
+
ensure(id);
|
|
64
|
+
for (const { from, to } of edges) {
|
|
65
|
+
ensure(to);
|
|
66
|
+
ensure(from).add(to);
|
|
67
|
+
}
|
|
68
|
+
return adjacency;
|
|
69
|
+
}
|
|
70
|
+
/** Collapses rotations and re-discoveries of the same ring to one entry each. */
|
|
71
|
+
function dedupeCycles(cycles) {
|
|
72
|
+
const seen = new Map();
|
|
73
|
+
for (const cycle of cycles) {
|
|
74
|
+
// Drop the repeated closing node, then rotate so the smallest id leads —
|
|
75
|
+
// a canonical form shared by every rotation and rediscovery of the ring.
|
|
76
|
+
const ring = cycle.slice(0, -1);
|
|
77
|
+
const pivot = ring.indexOf([...ring].sort()[0]);
|
|
78
|
+
const canonical = [...ring.slice(pivot), ...ring.slice(0, pivot)];
|
|
79
|
+
seen.set(canonical.join(' '), canonical);
|
|
80
|
+
}
|
|
81
|
+
return [...seen.values()];
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=structural-max.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structural-max.js","sourceRoot":"","sources":["../../../src/trace/cardinality/structural-max.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type IoKind } from './io-pattern.js';
|
|
2
|
+
export interface CoverageViolation {
|
|
3
|
+
readonly rule: string;
|
|
4
|
+
readonly file: string;
|
|
5
|
+
readonly line: number;
|
|
6
|
+
readonly message: string;
|
|
7
|
+
}
|
|
8
|
+
export interface CoverageCheckResult {
|
|
9
|
+
readonly violations: readonly CoverageViolation[];
|
|
10
|
+
readonly scanned: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Tier-2 coverage check across `sourceFiles` (paths relative to `cwd`).
|
|
14
|
+
*
|
|
15
|
+
* `extras` extends the stock pipework I/O registry with consumer-declared
|
|
16
|
+
* patterns from `config.trace.io`. Defaults to empty — the stock registry
|
|
17
|
+
* (`pipe.*` writes, `pipe(...)`-chain reads) is always live.
|
|
18
|
+
*/
|
|
19
|
+
export declare function checkCoverage(cwd: string, sourceFiles: readonly string[], extras?: Readonly<Record<string, IoKind>>): CoverageCheckResult;
|
|
20
|
+
//# sourceMappingURL=coverage-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverage-check.d.ts","sourceRoot":"","sources":["../../../src/trace/check/coverage-check.ts"],"names":[],"mappings":"AA2BA,OAAO,EAAc,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAEzD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,UAAU,EAAE,SAAS,iBAAiB,EAAE,CAAA;IACjD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAUD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,MAAM,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,GAC5C,mBAAmB,CAiErB"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// Tier-2 coverage check (Q9): every I/O call site is inside a `@flow_step`,
|
|
2
|
+
// and a flow_step performs at most one I/O — at most one write, in particular.
|
|
3
|
+
// Without this, the trace is gap-prone by construction: an unannotated read or
|
|
4
|
+
// write goes silently untraced and the build doesn't notice.
|
|
5
|
+
//
|
|
6
|
+
// Ships as warning (Phase 6f): violations are surfaced but do not fail
|
|
7
|
+
// `pipework check`. Once the consumer corpus has been migrated, Phase 7a
|
|
8
|
+
// flips it to an error.
|
|
9
|
+
//
|
|
10
|
+
// Two rules:
|
|
11
|
+
// - flow/io-outside-flow-step — an I/O call appears outside any annotated
|
|
12
|
+
// flow_step function. The runtime can't open a `trace_step` for it.
|
|
13
|
+
// - flow/multi-io-per-flow-step — a flow_step body contains more than one
|
|
14
|
+
// I/O call. Split it so each I/O is its own step. The stricter variant,
|
|
15
|
+
// more than one *write* in the same step, is reported as
|
|
16
|
+
// flow/multi-write-per-flow-step — surfaces the Q8 one-write-per-step
|
|
17
|
+
// rule with its own diagnostic.
|
|
18
|
+
//
|
|
19
|
+
// Lexical: I/O inside a nested closure inside a flow_step body still counts
|
|
20
|
+
// as "inside the flow_step" — otherwise writes hide inside callbacks. The
|
|
21
|
+
// cardinality analyzer's "don't follow nested closures" convention is the
|
|
22
|
+
// opposite default, by design (Q1 vs Q9).
|
|
23
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
24
|
+
import { join } from 'node:path';
|
|
25
|
+
import { parse } from '@typescript-eslint/parser';
|
|
26
|
+
import { collectFlowStepSites, isNode } from './flow-step-site.js';
|
|
27
|
+
import { classifyIo } from './io-pattern.js';
|
|
28
|
+
/**
|
|
29
|
+
* Tier-2 coverage check across `sourceFiles` (paths relative to `cwd`).
|
|
30
|
+
*
|
|
31
|
+
* `extras` extends the stock pipework I/O registry with consumer-declared
|
|
32
|
+
* patterns from `config.trace.io`. Defaults to empty — the stock registry
|
|
33
|
+
* (`pipe.*` writes, `pipe(...)`-chain reads) is always live.
|
|
34
|
+
*/
|
|
35
|
+
export function checkCoverage(cwd, sourceFiles, extras = {}) {
|
|
36
|
+
const violations = [];
|
|
37
|
+
let scanned = 0;
|
|
38
|
+
for (const file of sourceFiles) {
|
|
39
|
+
const abs = join(cwd, file);
|
|
40
|
+
if (!existsSync(abs))
|
|
41
|
+
continue;
|
|
42
|
+
let program = null;
|
|
43
|
+
let sites = [];
|
|
44
|
+
try {
|
|
45
|
+
program = parseProgram(readFileSync(abs, 'utf-8'));
|
|
46
|
+
sites = collectFlowStepSites(readFileSync(abs, 'utf-8'));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Unparseable — leave syntax errors to the type checker.
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
scanned++;
|
|
53
|
+
const ioSites = collectIoSites(program, extras);
|
|
54
|
+
const flowStepRanges = collectFlowStepRanges(sites);
|
|
55
|
+
for (const io of ioSites) {
|
|
56
|
+
const enclosing = flowStepRanges.find(r => io.start >= r.bodyStart && io.start < r.bodyEnd);
|
|
57
|
+
if (enclosing === undefined) {
|
|
58
|
+
violations.push({
|
|
59
|
+
rule: 'flow/io-outside-flow-step',
|
|
60
|
+
file,
|
|
61
|
+
line: io.line,
|
|
62
|
+
message: `I/O ${io.kind} is outside any @flow_step. Wrap it in a standalone function and ` +
|
|
63
|
+
`annotate that function with @flow_step so the runtime can open a trace_step for it.`,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
for (const range of flowStepRanges) {
|
|
68
|
+
const inside = ioSites.filter(s => s.start >= range.bodyStart && s.start < range.bodyEnd);
|
|
69
|
+
if (inside.length <= 1)
|
|
70
|
+
continue;
|
|
71
|
+
const writes = inside.filter(s => s.kind === 'write');
|
|
72
|
+
if (writes.length > 1) {
|
|
73
|
+
violations.push({
|
|
74
|
+
rule: 'flow/multi-write-per-flow-step',
|
|
75
|
+
file,
|
|
76
|
+
line: range.line,
|
|
77
|
+
message: `@flow_step "${range.name}" performs ${String(writes.length)} writes. A flow_step ` +
|
|
78
|
+
`may perform at most one write (Q8). Split each write into its own flow_step.`,
|
|
79
|
+
});
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
violations.push({
|
|
83
|
+
rule: 'flow/multi-io-per-flow-step',
|
|
84
|
+
file,
|
|
85
|
+
line: range.line,
|
|
86
|
+
message: `@flow_step "${range.name}" performs ${String(inside.length)} I/O calls. A flow_step ` +
|
|
87
|
+
`may perform at most one I/O call. Split each call into its own flow_step.`,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return { violations, scanned };
|
|
92
|
+
}
|
|
93
|
+
function collectFlowStepRanges(sites) {
|
|
94
|
+
const ranges = [];
|
|
95
|
+
for (const site of sites) {
|
|
96
|
+
const body = site.subject.body;
|
|
97
|
+
if (body === null)
|
|
98
|
+
continue;
|
|
99
|
+
ranges.push({
|
|
100
|
+
name: site.subject.name,
|
|
101
|
+
line: site.subject.line,
|
|
102
|
+
bodyStart: body.range[0],
|
|
103
|
+
bodyEnd: body.range[1],
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return ranges;
|
|
107
|
+
}
|
|
108
|
+
function collectIoSites(program, extras) {
|
|
109
|
+
const out = [];
|
|
110
|
+
walk(program, node => {
|
|
111
|
+
if (node.type !== 'CallExpression')
|
|
112
|
+
return;
|
|
113
|
+
const kind = classifyIo(node, extras);
|
|
114
|
+
if (kind === null)
|
|
115
|
+
return;
|
|
116
|
+
out.push({ kind, line: node.loc.start.line, start: node.range[0] });
|
|
117
|
+
});
|
|
118
|
+
return out;
|
|
119
|
+
}
|
|
120
|
+
const SKIP_KEYS = new Set(['loc', 'range', 'parent']);
|
|
121
|
+
function walk(node, visit) {
|
|
122
|
+
visit(node);
|
|
123
|
+
for (const key of Object.keys(node)) {
|
|
124
|
+
if (SKIP_KEYS.has(key))
|
|
125
|
+
continue;
|
|
126
|
+
const value = node[key];
|
|
127
|
+
if (Array.isArray(value)) {
|
|
128
|
+
for (const item of value) {
|
|
129
|
+
if (isNode(item))
|
|
130
|
+
walk(item, visit);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else if (isNode(value)) {
|
|
134
|
+
walk(value, visit);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function parseProgram(code) {
|
|
139
|
+
return parse(code, {
|
|
140
|
+
range: true,
|
|
141
|
+
loc: true,
|
|
142
|
+
comment: true,
|
|
143
|
+
sourceType: 'module',
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=coverage-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverage-check.js","sourceRoot":"","sources":["../../../src/trace/check/coverage-check.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,+EAA+E;AAC/E,+EAA+E;AAC/E,6DAA6D;AAC7D,EAAE;AACF,uEAAuE;AACvE,yEAAyE;AACzE,wBAAwB;AACxB,EAAE;AACF,aAAa;AACb,4EAA4E;AAC5E,wEAAwE;AACxE,4EAA4E;AAC5E,4EAA4E;AAC5E,6DAA6D;AAC7D,0EAA0E;AAC1E,oCAAoC;AACpC,EAAE;AACF,4EAA4E;AAC5E,0EAA0E;AAC1E,0EAA0E;AAC1E,0CAA0C;AAE1C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAA;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,EAAmC,MAAM,qBAAqB,CAAA;AACnG,OAAO,EAAE,UAAU,EAAe,MAAM,iBAAiB,CAAA;AAsBzD;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC3B,GAAW,EACX,WAA8B,EAC9B,SAA2C,EAAE;IAE7C,MAAM,UAAU,GAAwB,EAAE,CAAA;IAC1C,IAAI,OAAO,GAAG,CAAC,CAAA;IAEf,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;QAC3B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAQ;QAE9B,IAAI,OAAO,GAAmB,IAAI,CAAA;QAClC,IAAI,KAAK,GAA4B,EAAE,CAAA;QACvC,IAAI,CAAC;YACH,OAAO,GAAG,YAAY,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAA;YAClD,KAAK,GAAG,oBAAoB,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAA;QAC1D,CAAC;QAAC,MAAM,CAAC;YACP,yDAAyD;YACzD,SAAQ;QACV,CAAC;QACD,OAAO,EAAE,CAAA;QAET,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QAC/C,MAAM,cAAc,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAA;QAEnD,KAAK,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;YACzB,MAAM,SAAS,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,OAAO,CAAC,CAAA;YAC3F,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC5B,UAAU,CAAC,IAAI,CAAC;oBACd,IAAI,EAAE,2BAA2B;oBACjC,IAAI;oBACJ,IAAI,EAAE,EAAE,CAAC,IAAI;oBACb,OAAO,EACL,OAAO,EAAE,CAAC,IAAI,mEAAmE;wBACjF,qFAAqF;iBACxF,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE,CAAC;YACnC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,KAAK,CAAC,SAAS,IAAI,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,CAAA;YACzF,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC;gBAAE,SAAQ;YAEhC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAA;YACrD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACtB,UAAU,CAAC,IAAI,CAAC;oBACd,IAAI,EAAE,gCAAgC;oBACtC,IAAI;oBACJ,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,OAAO,EACL,eAAe,KAAK,CAAC,IAAI,cAAc,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,uBAAuB;wBACnF,8EAA8E;iBACjF,CAAC,CAAA;gBACF,SAAQ;YACV,CAAC;YAED,UAAU,CAAC,IAAI,CAAC;gBACd,IAAI,EAAE,6BAA6B;gBACnC,IAAI;gBACJ,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,OAAO,EACL,eAAe,KAAK,CAAC,IAAI,cAAc,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,0BAA0B;oBACtF,2EAA2E;aAC9E,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,CAAA;AAChC,CAAC;AASD,SAAS,qBAAqB,CAAC,KAA8B;IAC3D,MAAM,MAAM,GAAoB,EAAE,CAAA;IAClC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAA;QAC9B,IAAI,IAAI,KAAK,IAAI;YAAE,SAAQ;QAC3B,MAAM,CAAC,IAAI,CAAC;YACV,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI;YACvB,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI;YACvB,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YACxB,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;SACvB,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,cAAc,CAAC,OAAgB,EAAE,MAAwC;IAChF,MAAM,GAAG,GAAa,EAAE,CAAA;IACxB,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE;QACnB,IAAI,IAAI,CAAC,IAAI,KAAK,gBAAgB;YAAE,OAAM;QAC1C,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QACrC,IAAI,IAAI,KAAK,IAAI;YAAE,OAAM;QACzB,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IACrE,CAAC,CAAC,CAAA;IACF,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAA;AAErD,SAAS,IAAI,CAAC,IAAa,EAAE,KAA2B;IACtD,KAAK,CAAC,IAAI,CAAC,CAAA;IACX,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,IAAI,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAQ;QAChC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAA;QACvB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,MAAM,CAAC,IAAI,CAAC;oBAAE,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;YACrC,CAAC;QACH,CAAC;aAAM,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;QACpB,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,IAAY;IAChC,OAAO,KAAK,CAAC,IAAI,EAAE;QACjB,KAAK,EAAE,IAAI;QACX,GAAG,EAAE,IAAI;QACT,OAAO,EAAE,IAAI;QACb,UAAU,EAAE,QAAQ;KACrB,CAAuB,CAAA;AAC1B,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface FlowViolation {
|
|
2
|
+
readonly rule: string;
|
|
3
|
+
readonly file: string;
|
|
4
|
+
readonly line: number;
|
|
5
|
+
readonly message: string;
|
|
6
|
+
}
|
|
7
|
+
export interface FlowCheckResult {
|
|
8
|
+
readonly violations: readonly FlowViolation[];
|
|
9
|
+
readonly scanned: number;
|
|
10
|
+
}
|
|
11
|
+
/** Validates every `@flow_step` annotation in `sourceFiles` (paths relative to `cwd`). */
|
|
12
|
+
export declare function checkFlowAnnotations(cwd: string, sourceFiles: readonly string[]): FlowCheckResult;
|
|
13
|
+
//# sourceMappingURL=flow-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-check.d.ts","sourceRoot":"","sources":["../../../src/trace/check/flow-check.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,UAAU,EAAE,SAAS,aAAa,EAAE,CAAA;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED,0FAA0F;AAC1F,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,eAAe,CAuBjG"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// The `flow` check: validates `@flow_step` annotations across a set of source
|
|
2
|
+
// files. This is the syntactic tier — it rejects what is locally wrong about an
|
|
3
|
+
// annotation without consulting the call graph. Three rules:
|
|
4
|
+
//
|
|
5
|
+
// - a `@flow_step` block must document a standalone function, not a method;
|
|
6
|
+
// - every `@downstream` must declare its cardinality (`step` | `branch`);
|
|
7
|
+
// - the annotation must parse.
|
|
8
|
+
//
|
|
9
|
+
// Cardinality is *correct* only relative to the call graph — that is the
|
|
10
|
+
// cardinality analyzer's job, not this one. See PLAN.tracing.md, component 2.
|
|
11
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
import { collectFlowStepSites } from './flow-step-site.js';
|
|
14
|
+
/** Validates every `@flow_step` annotation in `sourceFiles` (paths relative to `cwd`). */
|
|
15
|
+
export function checkFlowAnnotations(cwd, sourceFiles) {
|
|
16
|
+
const violations = [];
|
|
17
|
+
let scanned = 0;
|
|
18
|
+
for (const file of sourceFiles) {
|
|
19
|
+
const abs = join(cwd, file);
|
|
20
|
+
if (!existsSync(abs))
|
|
21
|
+
continue;
|
|
22
|
+
let sites;
|
|
23
|
+
try {
|
|
24
|
+
sites = collectFlowStepSites(readFileSync(abs, 'utf-8'));
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Unparseable file — leave syntax errors to the type checker.
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
scanned++;
|
|
31
|
+
for (const site of sites) {
|
|
32
|
+
collectSiteViolations(file, site, violations);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return { violations, scanned };
|
|
36
|
+
}
|
|
37
|
+
function collectSiteViolations(file, site, out) {
|
|
38
|
+
const { subject, annotation, commentLine } = site;
|
|
39
|
+
if (subject.shape === 'orphan') {
|
|
40
|
+
out.push({
|
|
41
|
+
rule: 'flow/orphan-annotation',
|
|
42
|
+
file,
|
|
43
|
+
line: commentLine,
|
|
44
|
+
message: '@flow_step JSDoc is not attached to a function. A flow step must document a '
|
|
45
|
+
+ 'standalone function declaration so it has a stable path:symbol identity.',
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
else if (subject.shape === 'method') {
|
|
49
|
+
out.push({
|
|
50
|
+
rule: 'flow/no-method-flow-step',
|
|
51
|
+
file,
|
|
52
|
+
line: subject.line,
|
|
53
|
+
message: `@flow_step on method "${subject.name}" — a method cannot be a flow step. It carries `
|
|
54
|
+
+ `hidden "this" state, so the trace boundary is untrustworthy. Extract "${subject.name}" `
|
|
55
|
+
+ 'into a standalone function.',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
if (annotation.kind === 'invalid') {
|
|
59
|
+
for (const err of annotation.errors) {
|
|
60
|
+
out.push({
|
|
61
|
+
rule: 'flow/invalid-annotation',
|
|
62
|
+
file,
|
|
63
|
+
line: commentLine + err.line - 1,
|
|
64
|
+
message: err.message,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
for (const edge of annotation.annotation.downstream) {
|
|
70
|
+
if (edge.cardinality === null) {
|
|
71
|
+
out.push({
|
|
72
|
+
rule: 'flow/missing-cardinality',
|
|
73
|
+
file,
|
|
74
|
+
line: commentLine,
|
|
75
|
+
message: `@downstream ${edge.path}:${edge.symbol} is missing a cardinality. Add "step" or `
|
|
76
|
+
+ `"branch": @downstream ${edge.path}:${edge.symbol} branch. A branch is an edge `
|
|
77
|
+
+ 'traversed more than once (loop, batch, recursion, fan-out).',
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=flow-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-check.js","sourceRoot":"","sources":["../../../src/trace/check/flow-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type FlowStepParseResult } from '@pipework/flow-step-parser';
|
|
2
|
+
interface Position {
|
|
3
|
+
readonly line: number;
|
|
4
|
+
readonly column: number;
|
|
5
|
+
}
|
|
6
|
+
interface SourceLocation {
|
|
7
|
+
readonly start: Position;
|
|
8
|
+
readonly end: Position;
|
|
9
|
+
}
|
|
10
|
+
/** A structurally-typed ESTree node — enough to walk without a parser type dependency. */
|
|
11
|
+
export interface AstNode {
|
|
12
|
+
readonly type: string;
|
|
13
|
+
readonly range: readonly [number, number];
|
|
14
|
+
readonly loc: SourceLocation;
|
|
15
|
+
readonly [key: string]: unknown;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* How a `@flow_step`-annotated declaration is shaped.
|
|
19
|
+
*
|
|
20
|
+
* - `function` — a standalone function (declaration or a `const` bound to an
|
|
21
|
+
* arrow / function expression). The only legal shape.
|
|
22
|
+
* - `method` — a class method, class field bound to a function, or object
|
|
23
|
+
* literal method. Banned: a method carries hidden `this` state, so the trace
|
|
24
|
+
* boundary is untrustworthy. See PLAN.tracing.md, Q3.
|
|
25
|
+
* - `orphan` — the JSDoc block documents no declaration the analyzer can see.
|
|
26
|
+
*/
|
|
27
|
+
export type FlowStepShape = 'function' | 'method' | 'orphan';
|
|
28
|
+
export interface FlowStepSubject {
|
|
29
|
+
readonly shape: FlowStepShape;
|
|
30
|
+
/** Declared name, or `<anonymous>` / `<computed>` when none can be read. */
|
|
31
|
+
readonly name: string;
|
|
32
|
+
/** 1-based line of the declaration, or of the JSDoc block when orphaned. */
|
|
33
|
+
readonly line: number;
|
|
34
|
+
/** The declaration node, or `null` when orphaned. */
|
|
35
|
+
readonly node: AstNode | null;
|
|
36
|
+
/**
|
|
37
|
+
* The documented function's executable body — what the cardinality analyzer
|
|
38
|
+
* walks. `null` exactly when `node` is `null` (an orphan): every `function`
|
|
39
|
+
* and `method` subject resolves to a function with a body.
|
|
40
|
+
*/
|
|
41
|
+
readonly body: AstNode | null;
|
|
42
|
+
}
|
|
43
|
+
export interface FlowStepSite {
|
|
44
|
+
/** Parsed content of the `@flow_step` JSDoc block — never `not-a-flow-step`. */
|
|
45
|
+
readonly annotation: Exclude<FlowStepParseResult, {
|
|
46
|
+
kind: 'not-a-flow-step';
|
|
47
|
+
}>;
|
|
48
|
+
/** The declaration the block documents, and how it is shaped. */
|
|
49
|
+
readonly subject: FlowStepSubject;
|
|
50
|
+
/** 1-based line where the `@flow_step` JSDoc block opens. */
|
|
51
|
+
readonly commentLine: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Parse `code` and return every `@flow_step` site it contains, in source order.
|
|
55
|
+
*
|
|
56
|
+
* Throws if `code` is not parseable — callers scanning many files should catch
|
|
57
|
+
* and skip, leaving syntax errors to the type checker.
|
|
58
|
+
*/
|
|
59
|
+
export declare function collectFlowStepSites(code: string): FlowStepSite[];
|
|
60
|
+
export declare function isNode(value: unknown): value is AstNode;
|
|
61
|
+
export {};
|
|
62
|
+
//# sourceMappingURL=flow-step-site.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-step-site.d.ts","sourceRoot":"","sources":["../../../src/trace/check/flow-step-site.ts"],"names":[],"mappings":"AAOA,OAAO,EAA2B,KAAK,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAE9F,UAAU,QAAQ;IAChB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED,UAAU,cAAc;IACtB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAA;IACxB,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAA;CACvB;AAED,0FAA0F;AAC1F,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACzC,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAA;IAC5B,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAChC;AAaD;;;;;;;;;GASG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAE5D,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAA;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,qDAAqD;IACrD,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAA;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAA;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,gFAAgF;IAChF,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,mBAAmB,EAAE;QAAE,IAAI,EAAE,iBAAiB,CAAA;KAAE,CAAC,CAAA;IAC9E,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAA;IACjC,6DAA6D;IAC7D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AASD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,EAAE,CA2BjE;AAgGD,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,CAIvD"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// Locates `@flow_step` annotation sites in a TypeScript source file: each site
|
|
2
|
+
// pairs a parsed annotation with the declaration it documents and how that
|
|
3
|
+
// declaration is shaped — a standalone function, a method, or nothing at all.
|
|
4
|
+
// The `flow` check and the cardinality analyzer both build on this; it is the
|
|
5
|
+
// one place that answers "where are the flow steps in this file".
|
|
6
|
+
import { parse } from '@typescript-eslint/parser';
|
|
7
|
+
import { parseFlowStepAnnotation } from '@pipework/flow-step-parser';
|
|
8
|
+
/**
|
|
9
|
+
* Parse `code` and return every `@flow_step` site it contains, in source order.
|
|
10
|
+
*
|
|
11
|
+
* Throws if `code` is not parseable — callers scanning many files should catch
|
|
12
|
+
* and skip, leaving syntax errors to the type checker.
|
|
13
|
+
*/
|
|
14
|
+
export function collectFlowStepSites(code) {
|
|
15
|
+
const program = parse(code, {
|
|
16
|
+
range: true,
|
|
17
|
+
loc: true,
|
|
18
|
+
comment: true,
|
|
19
|
+
sourceType: 'module',
|
|
20
|
+
});
|
|
21
|
+
const candidates = [];
|
|
22
|
+
walk(program, node => {
|
|
23
|
+
const candidate = classify(node);
|
|
24
|
+
if (candidate !== null)
|
|
25
|
+
candidates.push(candidate);
|
|
26
|
+
});
|
|
27
|
+
candidates.sort((a, b) => a.node.range[0] - b.node.range[0]);
|
|
28
|
+
const sites = [];
|
|
29
|
+
for (const comment of program.comments) {
|
|
30
|
+
if (comment.type !== 'Block')
|
|
31
|
+
continue;
|
|
32
|
+
const annotation = parseFlowStepAnnotation(`/*${comment.value}*/`);
|
|
33
|
+
if (annotation.kind === 'not-a-flow-step')
|
|
34
|
+
continue;
|
|
35
|
+
sites.push({
|
|
36
|
+
annotation,
|
|
37
|
+
subject: subjectFor(comment, candidates),
|
|
38
|
+
commentLine: comment.loc.start.line,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return sites;
|
|
42
|
+
}
|
|
43
|
+
/** The declaration a `@flow_step` block documents: the nearest one that follows it. */
|
|
44
|
+
function subjectFor(comment, candidates) {
|
|
45
|
+
const documented = candidates.find(c => c.node.range[0] >= comment.range[1]);
|
|
46
|
+
if (documented === undefined) {
|
|
47
|
+
return { shape: 'orphan', name: '<unknown>', line: comment.loc.start.line, node: null, body: null };
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
shape: documented.shape,
|
|
51
|
+
name: documented.name,
|
|
52
|
+
line: documented.node.loc.start.line,
|
|
53
|
+
node: documented.node,
|
|
54
|
+
body: documented.body,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** Classifies a node as a flow-step-able declaration, or `null` if it is not one. */
|
|
58
|
+
function classify(node) {
|
|
59
|
+
switch (node.type) {
|
|
60
|
+
case 'FunctionDeclaration':
|
|
61
|
+
return { node, shape: 'function', name: readName(node['id']), body: bodyOf(node) };
|
|
62
|
+
case 'VariableDeclaration': {
|
|
63
|
+
const bound = functionBoundDeclarator(node);
|
|
64
|
+
if (bound === null)
|
|
65
|
+
return null;
|
|
66
|
+
return { node, shape: 'function', name: readName(bound['id']), body: bodyOf(bound['init']) };
|
|
67
|
+
}
|
|
68
|
+
case 'MethodDefinition':
|
|
69
|
+
case 'TSAbstractMethodDefinition':
|
|
70
|
+
return { node, shape: 'method', name: readName(node['key']), body: bodyOf(node['value']) };
|
|
71
|
+
case 'PropertyDefinition':
|
|
72
|
+
case 'TSAbstractPropertyDefinition':
|
|
73
|
+
case 'Property':
|
|
74
|
+
return isFunctionExpression(node['value'])
|
|
75
|
+
? { node, shape: 'method', name: readName(node['key']), body: bodyOf(node['value']) }
|
|
76
|
+
: null;
|
|
77
|
+
default:
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** The executable `body` of a function node — a `BlockStatement` or, for a
|
|
82
|
+
* concise arrow, the returned expression. `null` when `fn` is not a function. */
|
|
83
|
+
function bodyOf(fn) {
|
|
84
|
+
if (!isNode(fn))
|
|
85
|
+
return null;
|
|
86
|
+
const body = fn['body'];
|
|
87
|
+
return isNode(body) ? body : null;
|
|
88
|
+
}
|
|
89
|
+
/** The first declarator in a `VariableDeclaration` bound to a function, or `null`. */
|
|
90
|
+
function functionBoundDeclarator(node) {
|
|
91
|
+
const declarations = node['declarations'];
|
|
92
|
+
if (!Array.isArray(declarations))
|
|
93
|
+
return null;
|
|
94
|
+
for (const declarator of declarations) {
|
|
95
|
+
if (isNode(declarator) && isFunctionExpression(declarator['init']))
|
|
96
|
+
return declarator;
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
function isFunctionExpression(value) {
|
|
101
|
+
return isNode(value)
|
|
102
|
+
&& (value.type === 'ArrowFunctionExpression' || value.type === 'FunctionExpression');
|
|
103
|
+
}
|
|
104
|
+
function readName(keyOrId) {
|
|
105
|
+
if (!isNode(keyOrId))
|
|
106
|
+
return '<anonymous>';
|
|
107
|
+
switch (keyOrId.type) {
|
|
108
|
+
case 'Identifier':
|
|
109
|
+
return String(keyOrId['name']);
|
|
110
|
+
case 'PrivateIdentifier':
|
|
111
|
+
return `#${String(keyOrId['name'])}`;
|
|
112
|
+
case 'Literal':
|
|
113
|
+
return String(keyOrId['value']);
|
|
114
|
+
default:
|
|
115
|
+
return '<computed>';
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const SKIP_KEYS = new Set(['loc', 'range', 'parent']);
|
|
119
|
+
/** Depth-first visit of every node reachable from `node`, parents before children. */
|
|
120
|
+
function walk(node, visit) {
|
|
121
|
+
visit(node);
|
|
122
|
+
for (const key of Object.keys(node)) {
|
|
123
|
+
if (SKIP_KEYS.has(key))
|
|
124
|
+
continue;
|
|
125
|
+
const value = node[key];
|
|
126
|
+
if (Array.isArray(value)) {
|
|
127
|
+
for (const item of value) {
|
|
128
|
+
if (isNode(item))
|
|
129
|
+
walk(item, visit);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else if (isNode(value)) {
|
|
133
|
+
walk(value, visit);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
export function isNode(value) {
|
|
138
|
+
return typeof value === 'object'
|
|
139
|
+
&& value !== null
|
|
140
|
+
&& typeof value.type === 'string';
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=flow-step-site.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-step-site.js","sourceRoot":"","sources":["../../../src/trace/check/flow-step-site.ts"],"names":[],"mappings":"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"}
|