vouchington-tooling 0.29.0 → 0.31.0
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/README.md +69 -19
- package/bin/node-guard.mjs +9 -0
- package/bin/vouchington-mcp.mjs +20 -0
- package/bin/vouchington.mjs +18 -0
- package/dist/agent-blackboard/feedback-outbox-counts.d.mts +5 -0
- package/dist/agent-blackboard/feedback-outbox-counts.mjs +11 -0
- package/dist/agent-blackboard/feedback-outbox-directory.d.mts +2 -0
- package/dist/agent-blackboard/feedback-outbox-directory.mjs +16 -0
- package/dist/agent-blackboard/feedback-outbox.mjs +5 -7
- package/dist/agent-blackboard/index.d.mts +1 -0
- package/dist/agent-blackboard/index.mjs +1 -0
- package/dist/api-contract-discovery/header-contract-registry.mjs +2 -0
- package/dist/api-contract-discovery/protocol-body-status.d.mts +2 -0
- package/dist/api-contract-discovery/protocol-body-status.mjs +4 -0
- package/dist/api-contract-discovery/protocol-callback-argument-bindings.d.mts +7 -0
- package/dist/api-contract-discovery/protocol-callback-argument-bindings.mjs +33 -0
- package/dist/api-contract-discovery/protocol-callback-invocation.d.mts +5 -0
- package/dist/api-contract-discovery/protocol-callback-invocation.mjs +103 -0
- package/dist/api-contract-discovery/protocol-callback-mutations.d.mts +5 -0
- package/dist/api-contract-discovery/protocol-callback-mutations.mjs +42 -0
- package/dist/api-contract-discovery/protocol-callback-registration.d.mts +3 -0
- package/dist/api-contract-discovery/protocol-callback-registration.mjs +27 -0
- package/dist/api-contract-discovery/protocol-callback-values.d.mts +17 -0
- package/dist/api-contract-discovery/protocol-callback-values.mjs +114 -0
- package/dist/api-contract-discovery/protocol-context-alias.d.mts +3 -0
- package/dist/api-contract-discovery/protocol-context-alias.mjs +20 -0
- package/dist/api-contract-discovery/protocol-contract-extraction.d.mts +9 -0
- package/dist/api-contract-discovery/protocol-contract-extraction.mjs +87 -0
- package/dist/api-contract-discovery/protocol-contract-keys.d.mts +1 -0
- package/dist/api-contract-discovery/protocol-contract-keys.mjs +8 -0
- package/dist/api-contract-discovery/protocol-contract-registry.d.mts +5 -0
- package/dist/api-contract-discovery/protocol-contract-registry.mjs +133 -0
- package/dist/api-contract-discovery/protocol-executable-path.d.mts +3 -0
- package/dist/api-contract-discovery/protocol-executable-path.mjs +64 -0
- package/dist/api-contract-discovery/protocol-execution-path.d.mts +3 -0
- package/dist/api-contract-discovery/protocol-execution-path.mjs +16 -0
- package/dist/api-contract-discovery/protocol-http-association.d.mts +7 -0
- package/dist/api-contract-discovery/protocol-http-association.mjs +97 -0
- package/dist/api-contract-discovery/protocol-http-body-kinds.d.mts +3 -0
- package/dist/api-contract-discovery/protocol-http-body-kinds.mjs +19 -0
- package/dist/api-contract-discovery/protocol-http-context-callers.d.mts +6 -0
- package/dist/api-contract-discovery/protocol-http-context-callers.mjs +34 -0
- package/dist/api-contract-discovery/protocol-http-context.d.mts +9 -0
- package/dist/api-contract-discovery/protocol-http-context.mjs +108 -0
- package/dist/api-contract-discovery/protocol-http-emission.d.mts +3 -0
- package/dist/api-contract-discovery/protocol-http-emission.mjs +76 -0
- package/dist/api-contract-discovery/protocol-http-implicit-taint.d.mts +7 -0
- package/dist/api-contract-discovery/protocol-http-implicit-taint.mjs +26 -0
- package/dist/api-contract-discovery/protocol-http-registration.d.mts +12 -0
- package/dist/api-contract-discovery/protocol-http-registration.mjs +53 -0
- package/dist/api-contract-discovery/protocol-marker-analysis.d.mts +7 -0
- package/dist/api-contract-discovery/protocol-marker-analysis.mjs +50 -0
- package/dist/api-contract-discovery/protocol-opaque-callback.d.mts +3 -0
- package/dist/api-contract-discovery/protocol-opaque-callback.mjs +107 -0
- package/dist/api-contract-discovery/protocol-platform-callbacks.d.mts +5 -0
- package/dist/api-contract-discovery/protocol-platform-callbacks.mjs +72 -0
- package/dist/api-contract-discovery/protocol-platform-mutation-targets.d.mts +5 -0
- package/dist/api-contract-discovery/protocol-platform-mutation-targets.mjs +89 -0
- package/dist/api-contract-discovery/protocol-platform-writes.d.mts +3 -0
- package/dist/api-contract-discovery/protocol-platform-writes.mjs +92 -0
- package/dist/api-contract-discovery/protocol-requested-keys.d.mts +5 -0
- package/dist/api-contract-discovery/protocol-requested-keys.mjs +45 -0
- package/dist/api-contract-discovery/protocol-sse-callers.d.mts +7 -0
- package/dist/api-contract-discovery/protocol-sse-callers.mjs +67 -0
- package/dist/api-contract-discovery/protocol-sse-emission.d.mts +11 -0
- package/dist/api-contract-discovery/protocol-sse-emission.mjs +61 -0
- package/dist/api-contract-discovery/protocol-sse-feasible-status.d.mts +3 -0
- package/dist/api-contract-discovery/protocol-sse-feasible-status.mjs +24 -0
- package/dist/api-contract-discovery/protocol-sse-raw-writes.d.mts +9 -0
- package/dist/api-contract-discovery/protocol-sse-raw-writes.mjs +70 -0
- package/dist/api-contract-discovery/protocol-sse-status.d.mts +3 -0
- package/dist/api-contract-discovery/protocol-sse-status.mjs +84 -0
- package/dist/api-contract-discovery/protocol-sse-write-access.d.mts +9 -0
- package/dist/api-contract-discovery/protocol-sse-write-access.mjs +42 -0
- package/dist/api-contract-discovery/protocol-sse-write-helpers.d.mts +6 -0
- package/dist/api-contract-discovery/protocol-sse-write-helpers.mjs +66 -0
- package/dist/api-contract-discovery/protocol-sse-write-mutations.d.mts +9 -0
- package/dist/api-contract-discovery/protocol-sse-write-mutations.mjs +36 -0
- package/dist/api-contract-discovery/protocol-sse-write-resolution.d.mts +14 -0
- package/dist/api-contract-discovery/protocol-sse-write-resolution.mjs +79 -0
- package/dist/api-contract-discovery/protocol-status-dominance.d.mts +5 -0
- package/dist/api-contract-discovery/protocol-status-dominance.mjs +35 -0
- package/dist/api-contract-discovery/protocol-write-receiver.d.mts +9 -0
- package/dist/api-contract-discovery/protocol-write-receiver.mjs +79 -0
- package/dist/api-contract-discovery/query-contract-registry.mjs +2 -0
- package/dist/api-contract-discovery/registered-route-binding-writes.d.mts +3 -0
- package/dist/api-contract-discovery/registered-route-binding-writes.mjs +30 -0
- package/dist/api-contract-discovery/registered-route-catalog.mjs +11 -7
- package/dist/api-contract-discovery/registered-route-factory-returns.d.mts +3 -0
- package/dist/api-contract-discovery/registered-route-factory-returns.mjs +29 -0
- package/dist/api-contract-discovery/registered-route-handler-analysis.d.mts +5 -1
- package/dist/api-contract-discovery/registered-route-handler-analysis.mjs +79 -35
- package/dist/api-contract-discovery/registered-route-runtime-parameters.d.mts +3 -0
- package/dist/api-contract-discovery/registered-route-runtime-parameters.mjs +5 -0
- package/dist/api-contract-discovery/registered-route-terminal-error.d.mts +3 -0
- package/dist/api-contract-discovery/registered-route-terminal-error.mjs +98 -0
- package/dist/api-contract-discovery/request-contract-registry.mjs +2 -0
- package/dist/api-contract-discovery/response-contract-implicit.mjs +28 -10
- package/dist/api-contract-discovery/response-contract-registry.mjs +14 -1
- package/dist/api-contract-discovery/response-contract-route-analysis.d.mts +1 -1
- package/dist/api-contract-discovery/response-contract-route-analysis.mjs +22 -4
- package/dist/api-contract-discovery/response-contract-status.d.mts +1 -1
- package/dist/api-contract-discovery/response-contract-status.mjs +10 -8
- package/dist/api-contract-discovery/response-contract-types.d.mts +7 -0
- package/dist/cli/commands/agent-blackboard-flags.d.mts +4 -0
- package/dist/cli/commands/agent-blackboard-flags.mjs +34 -0
- package/dist/cli/commands/agent-blackboard.mjs +27 -35
- package/dist/cli/index.d.mts +2 -0
- package/dist/cli/index.mjs +12 -15
- package/dist/cli/parse-forwarded-commands.d.mts +0 -3
- package/dist/cli/parse-forwarded-commands.mjs +0 -2
- package/dist/cli/parse.d.mts +0 -3
- package/dist/cli/usage.d.mts +1 -1
- package/dist/cli/usage.mjs +2 -6
- package/dist/contract-schema/contract-schema-bounded-array.d.mts +1 -3
- package/dist/contract-schema/contract-schema-bounded-array.mjs +5 -0
- package/dist/contract-schema/contract-schema-object-tuple.mjs +7 -5
- package/dist/contract-schema/contract-schema-type-extractor.mjs +13 -0
- package/dist/contract-schema/contract-schema-validator.mjs +2 -2
- package/dist/index.d.mts +0 -2
- package/dist/index.mjs +0 -1
- package/dist/mcp-server/dispatch.d.mts +4 -1
- package/dist/mcp-server/dispatch.mjs +6 -4
- package/dist/mcp-server/handlers-outbox.mjs +2 -7
- package/dist/mcp-server/harness.test-helpers.d.mts +1 -0
- package/dist/mcp-server/harness.test-helpers.mjs +1 -1
- package/dist/mcp-server/main.d.mts +5 -0
- package/dist/mcp-server/main.mjs +8 -0
- package/dist/mcp-server/run.d.mts +2 -0
- package/dist/mcp-server/run.mjs +4 -1
- package/dist/mcp-server/sdk.d.mts +11 -2
- package/dist/mcp-server/sdk.mjs +37 -5
- package/dist/mcp-server/server.mjs +3 -2
- package/dist/mcp-server/tools.mjs +3 -2
- package/dist/mcp-server/worktree.d.mts +17 -10
- package/dist/mcp-server/worktree.mjs +48 -31
- package/dist/openapi-document/build-openapi-catalog-sse.d.mts +4 -0
- package/dist/openapi-document/build-openapi-catalog-sse.mjs +27 -0
- package/dist/openapi-document/build-openapi-document.mjs +5 -2
- package/dist/openapi-document/build-openapi-response.d.mts +2 -2
- package/dist/openapi-document/build-openapi-response.mjs +33 -3
- package/dist/openapi-document/build-openapi-sse-events.d.mts +11 -0
- package/dist/openapi-document/build-openapi-sse-events.mjs +46 -0
- package/dist/openapi-document/contract-schema-intersection-merge.mjs +11 -11
- package/dist/openapi-document/contract-schema-to-openapi.mjs +3 -3
- package/dist/openapi-document/openapi-route-helpers.mjs +1 -3
- package/dist/openapi-document/openapi-types.d.mts +3 -0
- package/dist/openapi-document/operation-types.d.mts +7 -0
- package/docs/mcp-server.md +83 -25
- package/package.json +11 -9
- package/scripts/build.mjs +2 -0
- package/skills/blackboard/SKILL.md +66 -4
- package/skills/manifest.json +19 -19
- package/dist/agent-harness-config/apply.d.mts +0 -4
- package/dist/agent-harness-config/apply.mjs +0 -72
- package/dist/agent-harness-config/cli.d.mts +0 -10
- package/dist/agent-harness-config/cli.mjs +0 -100
- package/dist/agent-harness-config/index.d.mts +0 -4
- package/dist/agent-harness-config/index.mjs +0 -3
- package/dist/agent-harness-config/merge-json.d.mts +0 -5
- package/dist/agent-harness-config/merge-json.mjs +0 -62
- package/dist/agent-harness-config/merge-toml.d.mts +0 -6
- package/dist/agent-harness-config/merge-toml.mjs +0 -132
- package/dist/agent-harness-config/paths.d.mts +0 -3
- package/dist/agent-harness-config/paths.mjs +0 -117
- package/dist/agent-harness-config/policy.d.mts +0 -14
- package/dist/agent-harness-config/policy.mjs +0 -51
- package/dist/agent-harness-config/prerequisites.d.mts +0 -2
- package/dist/agent-harness-config/prerequisites.mjs +0 -67
- package/dist/agent-harness-config/toml-value.d.mts +0 -6
- package/dist/agent-harness-config/toml-value.mjs +0 -69
- package/dist/agent-harness-config/types.d.mts +0 -90
- package/dist/agent-harness-config/types.mjs +0 -2
- package/docs/agent-harness-config.md +0 -134
package/README.md
CHANGED
|
@@ -34,9 +34,6 @@ vouchington runner-port-policy
|
|
|
34
34
|
vouchington runner-port-policy --file ./policy.json
|
|
35
35
|
vouchington runner-port-policy --reserved 2200
|
|
36
36
|
vouchington with-host-lock --name expensive-build --timeout-seconds 60 -- make build
|
|
37
|
-
vouchington agent-harness-config dump
|
|
38
|
-
vouchington agent-harness-config check --global
|
|
39
|
-
vouchington agent-harness-config apply --global --repo /path/to/checkout
|
|
40
37
|
vouchington gha-runtime-audit --pr-workflow CI --push-workflow '/^Main CI \\(.+\\)$/'
|
|
41
38
|
vouchington require-up-to-date --remote origin --branch main
|
|
42
39
|
vouchington gitleaks-directory-scan --config .gitleaks.toml
|
|
@@ -199,11 +196,6 @@ import {
|
|
|
199
196
|
import { initSqlAst, extractCreateTableMetadata } from 'vouchington-tooling/sql-ast'
|
|
200
197
|
import { splitSqlStatements, stripSqlComments } from 'vouchington-tooling/sql-scanner'
|
|
201
198
|
import { auditCiJobRuntime } from 'vouchington-tooling/gha-runtime-audit'
|
|
202
|
-
import {
|
|
203
|
-
applyHarnessConfig,
|
|
204
|
-
checkHarnessConfig,
|
|
205
|
-
dumpHarnessPolicy,
|
|
206
|
-
} from 'vouchington-tooling/agent-harness-config'
|
|
207
199
|
import {
|
|
208
200
|
inspectHarnessEnvironment,
|
|
209
201
|
selectHarnessSession,
|
|
@@ -374,14 +366,8 @@ summaries and quoted attributes; it is not a general HTML validator or sanitizer
|
|
|
374
366
|
the input or infers semantic relevance, cost, schema conformance, or failure causes. The existing
|
|
375
367
|
`markdownSectionBetweenHeadings` API retains its original boundary behavior.
|
|
376
368
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
home-directory configs; `--repo` updates a checkout. It does not copy allowlists, hooks, or plugins.
|
|
380
|
-
Grok uses `permission_mode = "auto"` plus `auto_mode.enabled` and `default_auto_mode` so the
|
|
381
|
-
classifier stays available in plan mode; its defined sandbox profile still requires
|
|
382
|
-
`--sandbox workspace-write` at launch. Cursor uses global `approvalMode = "auto-review"`;
|
|
383
|
-
`unrestricted` disables the sandbox. Repo checks surface user-level trust or mode prerequisites
|
|
384
|
-
instead of claiming that repo files alone activate them.
|
|
369
|
+
Machine-wide agent configuration now belongs to [vouchington-machines](https://github.com/vouchington/vouchington-machines/blob/main/docs/agent-config.md).
|
|
370
|
+
Use its `configure-agents.sh` and `diagnose-agents.sh`; project hooks and MCP configuration remain in their checkouts.
|
|
385
371
|
|
|
386
372
|
`agent-harness-identity` inspects the four harness environment signals without choosing a winner.
|
|
387
373
|
Callers supply their own precedence to `selectHarnessSession`, keeping product-specific identity
|
|
@@ -470,6 +456,68 @@ Version one recognizes literal-key `apiResponse`, `apiNoContent`, `apiOpenApiRaw
|
|
|
470
456
|
`ctx.setStatus`, and static `Content-Type` setters. The input source files and TypeScript checker
|
|
471
457
|
are the source representation; the adapter does not parse or discover a repository on its own.
|
|
472
458
|
|
|
459
|
+
Error-only 405 routes require a terminal throw on the handler's own context. Named handlers and
|
|
460
|
+
imported wrappers are followed through compiler declarations; conditional, caught, recursive,
|
|
461
|
+
fallthrough, and callback-only throws do not prove an error-only route. Empty `never[]` contracts
|
|
462
|
+
render as arrays with no items, and `Record<string, never>` renders as a closed empty object.
|
|
463
|
+
Standalone `never` and `any` remain unsupported contracts.
|
|
464
|
+
|
|
465
|
+
Caller-owned `apiSseFrame(key, { event, data })` markers expose literal event names and concrete
|
|
466
|
+
payloads from the frames actually written by a route, including callbacks passed to a handler
|
|
467
|
+
factory. SSE responses keep a string body schema and describe each payload under the media
|
|
468
|
+
type's `x-sse-events` map. Callback arguments require a concrete helper invocation path; ignored,
|
|
469
|
+
declaration-only, deferred, or rewritten callbacks fail closed. Callback options passed to a callee
|
|
470
|
+
without a concrete implementation also fail closed. Opaque callback escape checks include array and
|
|
471
|
+
tuple containers, nested object properties, and literal array spreads. Factory callbacks must execute
|
|
472
|
+
from the returned request handler; registration-time invocations do not prove a request emission. Compiler-resolved platform timers
|
|
473
|
+
and Promise executors supply invocation paths using the caller Program's actual standard-library
|
|
474
|
+
identities; shadowed, custom, or reassigned platform APIs do not. Mutation checks recognize Node's
|
|
475
|
+
compiler-declared `global` as well as `globalThis`. Fluent route registrations retain their method. Stable named
|
|
476
|
+
handlers and called local helpers are followed within their declaring source file. Ordinary response,
|
|
477
|
+
request, query, and header markers also require callback invocation before route attribution.
|
|
478
|
+
Broad names, unknown root payloads, executable unmarked frames through stream aliases, and
|
|
479
|
+
conditional or mutable-context status changes fail closed. Called SSE helpers inherit the proven
|
|
480
|
+
request context and its status at the invocation; mutually exclusive complete status branches retain
|
|
481
|
+
their separate responses. Explicit SSE statuses must be integers from 100 through 599.
|
|
482
|
+
Unconditional blocks preserve the last proven status setter; conditional or foreign overwrites fail closed.
|
|
483
|
+
Indirect `setStatus.call`, `apply`, and `bind` forms fail closed instead of assuming status 200.
|
|
484
|
+
Ambiguous caller contexts fail closed. A raw stream failure invalidates all
|
|
485
|
+
selected SSE rows, including when only one protocol variant was requested. Object-literal wrapper
|
|
486
|
+
receivers fail closed for marked frames because their properties can mutate; raw wrapper writes
|
|
487
|
+
are matched to the original stream, including proven helper-parameter forwarding, overloaded helper
|
|
488
|
+
implementations, and literal bracket
|
|
489
|
+
methods. Compiler-resolved `Reflect.apply` calls use their actual receiver and payload.
|
|
490
|
+
Unsupported computed methods on that receiver fail closed. Byte-bearing
|
|
491
|
+
stream `end` calls and streams used as pipe destinations are raw emissions; empty terminal cleanup
|
|
492
|
+
is preserved. Replacing a selected stream's `write` implementation invalidates its frame evidence.
|
|
493
|
+
Binding a selected stream's `write` or `end` method fails closed because the bound
|
|
494
|
+
method can escape the proven emission path. A callback that captures
|
|
495
|
+
the route's stream or HTTP context and escapes to a callee without a concrete implementation can
|
|
496
|
+
write undocumented bytes, so the affected response contracts fail closed. Uncalled local callbacks,
|
|
497
|
+
dead branches, and implemented helpers that ignore their callback preserve the proven contracts.
|
|
498
|
+
Cataloged SSE routes with only extracted non-SSE variants retain an unavailable event-stream
|
|
499
|
+
possibility alongside those responses until the extracted variants describe the stream.
|
|
500
|
+
|
|
501
|
+
Caller-owned `apiOpenApiHttpResponse(key, response)` markers bind a constant opaque response
|
|
502
|
+
carrying optional readonly `apiHttpResponseVariants` metadata. Each concrete union variant has
|
|
503
|
+
`status` and `bodyKind`; content variants also have `mediaType` and `body`. Discovery associates
|
|
504
|
+
the marked variable's status and body emissions without reading or replacing response bytes.
|
|
505
|
+
A status setter must dominate each body emission, including separate branches; constant context
|
|
506
|
+
aliases are followed, assigned mutable context aliases and object-literal context wrappers fail closed,
|
|
507
|
+
and helper parameters must resolve to the registered caller's unchanged response context.
|
|
508
|
+
Helper-owned contracts also account for response emissions in their proven caller scopes.
|
|
509
|
+
Indirect response `call`, `apply`, and `bind` forms fail closed, including borrowed context receivers.
|
|
510
|
+
SSE frames and branded content variants reject body-forbidden 1xx, 204, 205, and 304 statuses.
|
|
511
|
+
Literal bracket response methods have the same emission checks as property methods. Unrelated raw emissions
|
|
512
|
+
remain unavailable. Separate branded response branches are checked together, and the nearest
|
|
513
|
+
proven status setter for each body must belong to that response. Observed content and bodyless
|
|
514
|
+
emissions must match the complete metadata union, including an empty response in the `else` branch
|
|
515
|
+
of a positive body check. Exact requested protocol rows are selected before
|
|
516
|
+
other payloads, media types, or statuses are validated. In lenient mode, incomplete marker evidence
|
|
517
|
+
keeps every selected sibling unavailable. Explicit 400 content variants also include the framework's
|
|
518
|
+
JSON error schema independently of their own media type. Explicit bodyless 400 metadata also
|
|
519
|
+
retains that framework JSON possibility and fails closed on the conflicting body kinds.
|
|
520
|
+
|
|
473
521
|
Fork diagnostics use a caller-selected `recordDirectory` in both the worker setup file and the
|
|
474
522
|
main-process reporter. Call `registerForkExitSentinel({ recordDirectory })` once per fork, then
|
|
475
523
|
update `setCurrentForkExitModule` and `setCurrentForkExitProject` around tests. Construct
|
|
@@ -544,9 +592,11 @@ for minimizing summaries and excluding raw logs, transcripts, environment dumps,
|
|
|
544
592
|
secrets. A bound violation fails visibly without clipping findings.
|
|
545
593
|
|
|
546
594
|
`writeFeedback({identity, envelope, mode, ...})` accepts an exact caller-owned session identity and
|
|
547
|
-
never manufactures native agent identities. Interactive mode requires an absolute
|
|
548
|
-
directory.
|
|
549
|
-
|
|
595
|
+
never manufactures native agent identities. Interactive mode requires an absolute outbox
|
|
596
|
+
directory. Ancestor ownership and permissions are not checked; existing directory and record modes
|
|
597
|
+
are accepted. New directories and files default to `0700` and `0600`. The outbox itself must be a
|
|
598
|
+
real directory, and records must be regular files. The writer persists before transport with atomic
|
|
599
|
+
rename, file/directory fsync, and a stable record path. The outbox holds at most 128 records and 2 MB. Saturation,
|
|
550
600
|
corruption, or persistence failure blocks capture and preserves unsent records. Delivered records
|
|
551
601
|
are removed only after matching readback. Pending state includes count and a safe diagnostic;
|
|
552
602
|
interactive CLI exit zero means durable retention, and callers must inspect the printed status to
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Plain JavaScript on purpose: the bin shims import this first, and it must parse and run on old
|
|
2
|
+
// Node releases so the version check can report a clear error before any TypeScript output loads.
|
|
3
|
+
const REQUIRED_MAJOR = 24
|
|
4
|
+
|
|
5
|
+
/** One stderr line when `found` is older than the supported Node, otherwise undefined. */
|
|
6
|
+
export function unsupportedNodeMessage(found = process.versions.node) {
|
|
7
|
+
if (Number(found.split('.')[0]) >= REQUIRED_MAJOR) return undefined
|
|
8
|
+
return `vouchington requires Node >=${REQUIRED_MAJOR} (found ${found})\n`
|
|
9
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// The stable entry point of the MCP server: `<node> <install>/node_modules/vouchington-tooling/bin/
|
|
3
|
+
// vouchington-mcp.mjs`. It takes no arguments and skips CLI argument parsing, so a CLI refactor
|
|
4
|
+
// cannot move it. Plain JavaScript on purpose: the Node version check runs before anything else.
|
|
5
|
+
// ES2015 syntax only (no `??`, `?.`, or top-level await) so this file parses on every Node that can
|
|
6
|
+
// load a `.mjs` at all (12.17+), and the message prints there. Older Node cannot load `.mjs`.
|
|
7
|
+
import { unsupportedNodeMessage } from './node-guard.mjs'
|
|
8
|
+
|
|
9
|
+
const unsupported = unsupportedNodeMessage()
|
|
10
|
+
if (unsupported === undefined) {
|
|
11
|
+
import('../dist/mcp-server/main.mjs')
|
|
12
|
+
.then((main) => main.runMcpMain())
|
|
13
|
+
.catch((error) => {
|
|
14
|
+
process.stderr.write(String((error && error.stack) || error) + '\n')
|
|
15
|
+
process.exitCode = 1
|
|
16
|
+
})
|
|
17
|
+
} else {
|
|
18
|
+
process.stderr.write(unsupported)
|
|
19
|
+
process.exitCode = 1
|
|
20
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Plain JavaScript on purpose: the Node version check runs before any TypeScript output loads.
|
|
3
|
+
// ES2015 syntax only (no `??`, `?.`, or top-level await) so this file parses on every Node that can
|
|
4
|
+
// load a `.mjs` at all (12.17+), and the message prints there. Older Node cannot load `.mjs`.
|
|
5
|
+
import { unsupportedNodeMessage } from './node-guard.mjs'
|
|
6
|
+
|
|
7
|
+
const unsupported = unsupportedNodeMessage()
|
|
8
|
+
if (unsupported === undefined) {
|
|
9
|
+
import('../dist/cli/index.mjs')
|
|
10
|
+
.then((main) => main.runMain())
|
|
11
|
+
.catch((error) => {
|
|
12
|
+
process.stderr.write(String((error && error.stack) || error) + '\n')
|
|
13
|
+
process.exitCode = 1
|
|
14
|
+
})
|
|
15
|
+
} else {
|
|
16
|
+
process.stderr.write(unsupported)
|
|
17
|
+
process.exitCode = 1
|
|
18
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { assertSessionId } from './session-id.mjs';
|
|
2
|
+
import { readFeedbackOutbox } from './feedback-outbox.mjs';
|
|
3
|
+
/** Counts a session separately from all retry records in the shared worktree outbox. */
|
|
4
|
+
export function feedbackOutboxCounts(directory, sessionId) {
|
|
5
|
+
assertSessionId(sessionId);
|
|
6
|
+
const records = readFeedbackOutbox(directory);
|
|
7
|
+
return {
|
|
8
|
+
pendingCount: records.filter((record) => record.identity.sessionId === sessionId).length,
|
|
9
|
+
worktreePendingCount: records.length,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { lstatSync, mkdirSync } from 'node:fs';
|
|
2
|
+
/** The outbox is temporary retry state; ancestor ownership and modes do not govern it. */
|
|
3
|
+
export function ensureOutboxDirectory(directory, create) {
|
|
4
|
+
if (create)
|
|
5
|
+
mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
6
|
+
try {
|
|
7
|
+
if (!lstatSync(directory).isDirectory())
|
|
8
|
+
throw new Error(`feedback outbox must be a directory: ${directory}`);
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT')
|
|
13
|
+
return false;
|
|
14
|
+
throw error;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -2,7 +2,7 @@ import { canonicalFeedback, canonicalFeedbackEvent } from './feedback-canonical.
|
|
|
2
2
|
import { createHash, randomUUID } from 'node:crypto';
|
|
3
3
|
import { closeSync, constants, fsyncSync, fstatSync, openSync, readdirSync, renameSync, unlinkSync, } from 'node:fs';
|
|
4
4
|
import { isAbsolute, join, resolve } from 'node:path';
|
|
5
|
-
import {
|
|
5
|
+
import { ensureOutboxDirectory } from './feedback-outbox-directory.mjs';
|
|
6
6
|
import { withFileLock } from '../session-friction/lock.mjs';
|
|
7
7
|
import { openLogFile, readLogContent, writeAll } from '../session-friction/log-file.mjs';
|
|
8
8
|
import { validateFeedbackEnvelope } from './feedback-codec.mjs';
|
|
@@ -13,9 +13,9 @@ const FEEDBACK_OUTBOX_MAX_BYTES = 2_000_000;
|
|
|
13
13
|
const RECORD_MAX_BYTES = 18_000;
|
|
14
14
|
function outboxDirectory(directory, create) {
|
|
15
15
|
if (typeof directory !== 'string' || directory.length > 4096 || !isAbsolute(directory))
|
|
16
|
-
throw new Error('feedback outbox requires an absolute
|
|
16
|
+
throw new Error('feedback outbox requires an absolute directory');
|
|
17
17
|
const path = resolve(directory);
|
|
18
|
-
|
|
18
|
+
ensureOutboxDirectory(path, create);
|
|
19
19
|
return path;
|
|
20
20
|
}
|
|
21
21
|
function filename(record) {
|
|
@@ -27,8 +27,6 @@ function readRecord(path) {
|
|
|
27
27
|
const descriptor = openLogFile(path, constants.O_RDONLY);
|
|
28
28
|
try {
|
|
29
29
|
const status = fstatSync(descriptor);
|
|
30
|
-
if ((status.mode & 0o777) !== 0o600 || status.uid !== process.geteuid?.())
|
|
31
|
-
throw new Error('feedback outbox record must be private and owned by caller');
|
|
32
30
|
const bytes = status.size;
|
|
33
31
|
if (bytes > RECORD_MAX_BYTES)
|
|
34
32
|
throw new Error('feedback outbox record is too large');
|
|
@@ -72,7 +70,7 @@ function syncDirectory(directory) {
|
|
|
72
70
|
}
|
|
73
71
|
export function feedbackOutboxStatus(directory) {
|
|
74
72
|
const path = outboxDirectory(directory, false);
|
|
75
|
-
if (!
|
|
73
|
+
if (!ensureOutboxDirectory(path, false))
|
|
76
74
|
return { status: 'empty', pendingCount: 0 };
|
|
77
75
|
return withFileLock(join(path, '.records'), () => {
|
|
78
76
|
const pendingCount = records(path).length;
|
|
@@ -86,7 +84,7 @@ export function listFeedbackOutbox(directory) {
|
|
|
86
84
|
/** Reads the retained records without creating the outbox directory. */
|
|
87
85
|
export function readFeedbackOutbox(directory) {
|
|
88
86
|
const path = outboxDirectory(directory, false);
|
|
89
|
-
if (!
|
|
87
|
+
if (!ensureOutboxDirectory(path, false))
|
|
90
88
|
return [];
|
|
91
89
|
return withFileLock(join(path, '.records'), () => records(path).map((item) => item.record));
|
|
92
90
|
}
|
|
@@ -24,3 +24,4 @@ export { composeRetrospective } from './feedback-compose.mts';
|
|
|
24
24
|
export type { RetrospectiveCompositionInput, FeedbackAssessment } from './feedback-compose.mts';
|
|
25
25
|
export type { JournalAuditOptions } from './feedback-journal-audit.mts';
|
|
26
26
|
export type { JournalEntry, JournalLoader, JournalLoadResult } from '../session-friction/types.mts';
|
|
27
|
+
export { feedbackOutboxCounts } from './feedback-outbox-counts.mts';
|
|
@@ -63,3 +63,4 @@ export { createFeedbackEnvelope, validateFeedbackEnvelope } from './feedback-cod
|
|
|
63
63
|
export { writeFeedback, verifyFreshFeedback, flushFeedbackOutbox, FeedbackDeliveryError, } from './feedback-delivery.mjs';
|
|
64
64
|
export { feedbackOutboxStatus, readFeedbackOutbox } from './feedback-outbox.mjs';
|
|
65
65
|
export { composeRetrospective } from './feedback-compose.mjs';
|
|
66
|
+
export { feedbackOutboxCounts } from './feedback-outbox-counts.mjs';
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import ts from '../contract-schema/typescript-api.mjs';
|
|
2
|
+
import { registerPlatformCompilerLibraries } from './protocol-platform-callbacks.mjs';
|
|
2
3
|
import { contractError } from './response-contract-registration.mjs';
|
|
3
4
|
import { parseHeaderContract } from './header-contract-parser.mjs';
|
|
4
5
|
import { collectHandlerBindings, enclosingRouteBinding, visit, } from './response-contract-route-analysis.mjs';
|
|
5
6
|
export function discoverApiHeaderContracts(program, sourceFiles, knownResponseRoutes) {
|
|
7
|
+
registerPlatformCompilerLibraries(program);
|
|
6
8
|
const checker = program.getTypeChecker();
|
|
7
9
|
const bindings = collectHandlerBindings(sourceFiles, checker);
|
|
8
10
|
const contracts = new Map();
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import ts from '../contract-schema/typescript-api.mts';
|
|
2
|
+
import type { CallbackBindings, createProtocolCallbackValueResolver } from './protocol-callback-values.mts';
|
|
3
|
+
/** Binds literal callback parameters, declining unsupported destructuring shapes. */
|
|
4
|
+
export declare function callbackArgumentBindings(fn: ts.FunctionLikeDeclaration, call: ts.CallExpression | ts.NewExpression, env: CallbackBindings, captured: CallbackBindings, checker: ts.TypeChecker, resolver: ReturnType<typeof createProtocolCallbackValueResolver>): Map<ts.Symbol, {
|
|
5
|
+
node: ts.Node;
|
|
6
|
+
env: CallbackBindings;
|
|
7
|
+
}> | undefined;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import ts from '../contract-schema/typescript-api.mjs';
|
|
2
|
+
import { runtimeParameters } from './registered-route-runtime-parameters.mjs';
|
|
3
|
+
/** Binds literal callback parameters, declining unsupported destructuring shapes. */
|
|
4
|
+
export function callbackArgumentBindings(fn, call, env, captured, checker, resolver) {
|
|
5
|
+
const next = new Map(captured);
|
|
6
|
+
for (const [index, parameter] of runtimeParameters(fn).entries()) {
|
|
7
|
+
const argument = call.arguments?.[index];
|
|
8
|
+
if (!argument)
|
|
9
|
+
continue;
|
|
10
|
+
if (ts.isSpreadElement(argument))
|
|
11
|
+
return undefined;
|
|
12
|
+
if (ts.isIdentifier(parameter.name)) {
|
|
13
|
+
next.set(checker.getSymbolAtLocation(parameter.name), { node: argument, env });
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
if (!ts.isObjectBindingPattern(parameter.name))
|
|
17
|
+
return undefined;
|
|
18
|
+
const value = resolver.resolve(argument, env);
|
|
19
|
+
for (const element of parameter.name.elements) {
|
|
20
|
+
const key = element.propertyName ?? element.name;
|
|
21
|
+
if (!ts.isIdentifier(element.name) ||
|
|
22
|
+
element.dotDotDotToken ||
|
|
23
|
+
element.initializer ||
|
|
24
|
+
!(ts.isIdentifier(key) || ts.isStringLiteral(key)))
|
|
25
|
+
return undefined;
|
|
26
|
+
const bound = resolver.property(value, key.text, new Set());
|
|
27
|
+
if (bound)
|
|
28
|
+
next.set(checker.getSymbolAtLocation(element.name), bound);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return next;
|
|
33
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import ts from '../contract-schema/typescript-api.mts';
|
|
2
|
+
type FunctionNode = ts.FunctionLikeDeclaration;
|
|
3
|
+
/** Proves callback consumption through concrete helper bodies, without executing nested closures. */
|
|
4
|
+
export declare function isSupportedProtocolCallback(fn: FunctionNode, checker: ts.TypeChecker): boolean;
|
|
5
|
+
export {};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import ts from '../contract-schema/typescript-api.mjs';
|
|
2
|
+
import { runtimeParameters } from './registered-route-runtime-parameters.mjs';
|
|
3
|
+
import { invokedResult, registeredHandler } from './protocol-callback-registration.mjs';
|
|
4
|
+
import { platformCallbackArgument } from './protocol-platform-callbacks.mjs';
|
|
5
|
+
import { potentiallyExecuted } from './protocol-executable-path.mjs';
|
|
6
|
+
import { callbackBindingReplaced, callbackOptionsEscape } from './protocol-callback-mutations.mjs';
|
|
7
|
+
import { enclosingFunction } from './protocol-marker-analysis.mjs';
|
|
8
|
+
import { createProtocolCallbackValueResolver, isProtocolCallbackFunction as functionNode, protocolCallbackSourceCalls, protocolCallbackHasWrittenBindings, } from './protocol-callback-values.mjs';
|
|
9
|
+
/** Proves callback consumption through concrete helper bodies, without executing nested closures. */
|
|
10
|
+
export function isSupportedProtocolCallback(fn, checker) {
|
|
11
|
+
if (!fn.body || fn.asteriskToken || protocolCallbackHasWrittenBindings(fn, checker))
|
|
12
|
+
return false;
|
|
13
|
+
const resolver = createProtocolCallbackValueResolver(checker);
|
|
14
|
+
const { resolve, symbol } = resolver;
|
|
15
|
+
let invalidated = false;
|
|
16
|
+
function consumed(call, env, active, returned = false) {
|
|
17
|
+
const platform = platformCallbackArgument(call, checker);
|
|
18
|
+
const callee = resolve(platform ?? call.expression, env);
|
|
19
|
+
if (!callee || !functionNode(callee.node) || !callee.node.body || callee.node.asteriskToken) {
|
|
20
|
+
invalidated ||= callbackOptionsEscape(call, env, fn, resolver);
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
if (protocolCallbackHasWrittenBindings(callee.node, checker)) {
|
|
24
|
+
invalidated = true;
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
if (callee.node === fn)
|
|
28
|
+
return !returned;
|
|
29
|
+
if (active.has(callee.node))
|
|
30
|
+
return false;
|
|
31
|
+
const chain = new Set([...active, callee.node]);
|
|
32
|
+
if (platform)
|
|
33
|
+
return body(callee.node.body, callee.env, chain, false);
|
|
34
|
+
const next = new Map(callee.env);
|
|
35
|
+
runtimeParameters(callee.node).forEach((parameter, index) => {
|
|
36
|
+
const argument = call.arguments[index];
|
|
37
|
+
const target = symbol(parameter.name);
|
|
38
|
+
if (argument && !ts.isSpreadElement(argument))
|
|
39
|
+
next.set(target, { node: argument, env });
|
|
40
|
+
});
|
|
41
|
+
return body(callee.node.body, next, chain, returned || invokedResult(call));
|
|
42
|
+
}
|
|
43
|
+
function body(content, env, active, returned) {
|
|
44
|
+
let found = false;
|
|
45
|
+
let replaced = false;
|
|
46
|
+
function visit(node) {
|
|
47
|
+
if (found || replaced || !potentiallyExecuted(node))
|
|
48
|
+
return;
|
|
49
|
+
if (functionNode(node))
|
|
50
|
+
return;
|
|
51
|
+
if (callbackBindingReplaced(node, env, fn, resolver)) {
|
|
52
|
+
replaced = true;
|
|
53
|
+
invalidated = true;
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if ((ts.isCallExpression(node) || ts.isNewExpression(node)) &&
|
|
57
|
+
consumed(node, env, active, returned && ts.isReturnStatement(node.parent)) &&
|
|
58
|
+
(!returned || ts.isReturnStatement(node.parent))) {
|
|
59
|
+
found = true;
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (returned && ts.isReturnStatement(node) && node.expression) {
|
|
63
|
+
const value = resolve(node.expression, env);
|
|
64
|
+
if (value &&
|
|
65
|
+
functionNode(value.node) &&
|
|
66
|
+
value.node.body &&
|
|
67
|
+
!value.node.asteriskToken &&
|
|
68
|
+
!active.has(value.node) &&
|
|
69
|
+
body(value.node.body, value.env, new Set([...active, value.node]), false)) {
|
|
70
|
+
found = true;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
ts.forEachChild(node, visit);
|
|
75
|
+
}
|
|
76
|
+
visit(content);
|
|
77
|
+
return found && !invalidated;
|
|
78
|
+
}
|
|
79
|
+
let argument = fn;
|
|
80
|
+
while (ts.isPropertyAssignment(argument.parent) ||
|
|
81
|
+
ts.isObjectLiteralExpression(argument.parent) ||
|
|
82
|
+
ts.isParenthesizedExpression(argument.parent) ||
|
|
83
|
+
ts.isAsExpression(argument.parent) ||
|
|
84
|
+
ts.isNonNullExpression(argument.parent))
|
|
85
|
+
argument = argument.parent;
|
|
86
|
+
const call = argument.parent;
|
|
87
|
+
if (!ts.isCallExpression(call) && !ts.isNewExpression(call)) {
|
|
88
|
+
const owner = enclosingFunction(fn);
|
|
89
|
+
if (owner && isSupportedProtocolCallback(owner, checker))
|
|
90
|
+
return body(owner.body, new Map(), new Set([owner]), false);
|
|
91
|
+
return protocolCallbackSourceCalls(fn).some((candidate) => potentiallyExecuted(candidate) &&
|
|
92
|
+
((registeredHandler(candidate) &&
|
|
93
|
+
candidate.arguments.some((value) => resolve(value, new Map())?.node === fn)) ||
|
|
94
|
+
consumed(candidate, new Map(), new Set())));
|
|
95
|
+
}
|
|
96
|
+
if (!potentiallyExecuted(call))
|
|
97
|
+
return false;
|
|
98
|
+
if (ts.isCallExpression(call) && call.expression === argument)
|
|
99
|
+
return true;
|
|
100
|
+
if (argument === fn && ts.isCallExpression(call) && registeredHandler(call))
|
|
101
|
+
return true;
|
|
102
|
+
return (!!call.arguments?.includes(argument) && consumed(call, new Map(), new Set()));
|
|
103
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import ts from '../contract-schema/typescript-api.mts';
|
|
2
|
+
import type { CallbackBindings, createProtocolCallbackValueResolver } from './protocol-callback-values.mts';
|
|
3
|
+
export declare function callbackBindingReplaced(node: ts.Node, env: CallbackBindings, fn: ts.FunctionLikeDeclaration, resolver: ReturnType<typeof createProtocolCallbackValueResolver>): boolean;
|
|
4
|
+
/** An opaque callee may replace a callback stored in a forwarded options object. */
|
|
5
|
+
export declare function callbackOptionsEscape(node: ts.CallExpression | ts.NewExpression, env: CallbackBindings, fn: ts.FunctionLikeDeclaration, resolver: ReturnType<typeof createProtocolCallbackValueResolver>): boolean;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import ts from '../contract-schema/typescript-api.mjs';
|
|
2
|
+
import { unwrapExpression } from './protocol-marker-analysis.mjs';
|
|
3
|
+
export function callbackBindingReplaced(node, env, fn, resolver) {
|
|
4
|
+
const { resolve, symbol } = resolver;
|
|
5
|
+
const write = ts.isDeleteExpression(node)
|
|
6
|
+
? node.expression
|
|
7
|
+
: ts.isBinaryExpression(node) &&
|
|
8
|
+
node.operatorToken.kind >= ts.SyntaxKind.FirstAssignment &&
|
|
9
|
+
node.operatorToken.kind <= ts.SyntaxKind.LastAssignment
|
|
10
|
+
? node.left
|
|
11
|
+
: undefined;
|
|
12
|
+
if (!write)
|
|
13
|
+
return false;
|
|
14
|
+
let root = unwrapExpression(write);
|
|
15
|
+
while (ts.isPropertyAccessExpression(root) || ts.isElementAccessExpression(root))
|
|
16
|
+
root = unwrapExpression(root.expression);
|
|
17
|
+
return (ts.isIdentifier(root) && env.has(symbol(root))) || resolve(write, env)?.node === fn;
|
|
18
|
+
}
|
|
19
|
+
/** An opaque callee may replace a callback stored in a forwarded options object. */
|
|
20
|
+
export function callbackOptionsEscape(node, env, fn, resolver) {
|
|
21
|
+
function contains(value, bindings, seen = new Set()) {
|
|
22
|
+
const resolved = resolver.resolve(value, bindings);
|
|
23
|
+
if (!resolved || seen.has(resolved.node))
|
|
24
|
+
return false;
|
|
25
|
+
seen.add(resolved.node);
|
|
26
|
+
if (resolved.node === fn)
|
|
27
|
+
return true;
|
|
28
|
+
if (ts.isArrayLiteralExpression(resolved.node))
|
|
29
|
+
return resolved.node.elements.some((element) => contains(ts.isSpreadElement(element) ? element.expression : element, resolved.env, seen));
|
|
30
|
+
if (!ts.isObjectLiteralExpression(resolved.node))
|
|
31
|
+
return false;
|
|
32
|
+
return resolved.node.properties.some((member) => member === fn ||
|
|
33
|
+
(ts.isPropertyAssignment(member)
|
|
34
|
+
? contains(member.initializer, resolved.env, seen)
|
|
35
|
+
: ts.isShorthandPropertyAssignment(member) &&
|
|
36
|
+
(() => {
|
|
37
|
+
const target = resolver.property(resolved, member.name.text, new Set());
|
|
38
|
+
return !!target && contains(target.node, target.env, seen);
|
|
39
|
+
})()));
|
|
40
|
+
}
|
|
41
|
+
return !!node.arguments?.some((argument) => contains(argument, env));
|
|
42
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import ts from '../contract-schema/typescript-api.mjs';
|
|
2
|
+
import { unwrapExpression } from './protocol-marker-analysis.mjs';
|
|
3
|
+
export function registeredHandler(call) {
|
|
4
|
+
const method = unwrapExpression(call.expression);
|
|
5
|
+
if (!ts.isPropertyAccessExpression(method) ||
|
|
6
|
+
!['get', 'post', 'put', 'patch', 'delete', 'head', 'options'].includes(method.name.text))
|
|
7
|
+
return false;
|
|
8
|
+
let receiver = method.expression;
|
|
9
|
+
while (ts.isCallExpression(receiver) && ts.isPropertyAccessExpression(receiver.expression)) {
|
|
10
|
+
if (receiver.expression.name.text === 'route')
|
|
11
|
+
return true;
|
|
12
|
+
receiver = receiver.expression.expression;
|
|
13
|
+
}
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
export function invokedResult(call) {
|
|
17
|
+
let node = call;
|
|
18
|
+
while (ts.isParenthesizedExpression(node.parent) ||
|
|
19
|
+
ts.isAsExpression(node.parent) ||
|
|
20
|
+
ts.isNonNullExpression(node.parent) ||
|
|
21
|
+
ts.isAwaitExpression(node.parent))
|
|
22
|
+
node = node.parent;
|
|
23
|
+
const parent = node.parent;
|
|
24
|
+
return (ts.isCallExpression(parent) &&
|
|
25
|
+
(parent.expression === node ||
|
|
26
|
+
(registeredHandler(parent) && parent.arguments.includes(node))));
|
|
27
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import ts from '../contract-schema/typescript-api.mts';
|
|
2
|
+
type FunctionNode = ts.FunctionLikeDeclaration;
|
|
3
|
+
export type CallbackValue = {
|
|
4
|
+
node: ts.Node;
|
|
5
|
+
env: CallbackBindings;
|
|
6
|
+
};
|
|
7
|
+
export type CallbackBindings = ReadonlyMap<ts.Symbol, CallbackValue>;
|
|
8
|
+
export declare function isProtocolCallbackFunction(node: ts.Node): node is FunctionNode;
|
|
9
|
+
/** Resolves stable named uses in outer statements and directly registered inline handlers. */
|
|
10
|
+
export declare function protocolCallbackSourceCalls(fn: FunctionNode): ts.CallExpression[];
|
|
11
|
+
export declare function createProtocolCallbackValueResolver(checker: ts.TypeChecker): {
|
|
12
|
+
resolve: (node: ts.Node, env: CallbackBindings, seen?: Set<ts.Node>) => CallbackValue | undefined;
|
|
13
|
+
symbol: (node: ts.Node) => ts.Symbol | undefined;
|
|
14
|
+
property: (value: CallbackValue | undefined, name: string, seen: Set<ts.Node>) => CallbackValue | undefined;
|
|
15
|
+
};
|
|
16
|
+
export declare function protocolCallbackHasWrittenBindings(fn: FunctionNode, checker: ts.TypeChecker): boolean;
|
|
17
|
+
export {};
|