vouchington-tooling 0.32.0 → 0.34.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 +219 -1
- package/dist/api-contract-discovery/index.d.mts +4 -1
- package/dist/api-contract-discovery/index.mjs +1 -0
- package/dist/api-contract-discovery/protocol-analysis-cache.d.mts +21 -0
- package/dist/api-contract-discovery/protocol-analysis-cache.mjs +7 -0
- package/dist/api-contract-discovery/protocol-callback-invocation.d.mts +2 -1
- package/dist/api-contract-discovery/protocol-callback-invocation.mjs +12 -4
- package/dist/api-contract-discovery/protocol-callback-values.d.mts +2 -1
- package/dist/api-contract-discovery/protocol-callback-values.mjs +3 -3
- package/dist/api-contract-discovery/protocol-execution-path.d.mts +2 -1
- package/dist/api-contract-discovery/protocol-execution-path.mjs +2 -2
- package/dist/api-contract-discovery/protocol-http-association.mjs +8 -0
- package/dist/api-contract-discovery/protocol-http-context-callers.d.mts +2 -1
- package/dist/api-contract-discovery/protocol-http-context-callers.mjs +13 -7
- package/dist/api-contract-discovery/protocol-http-context-escapes.d.mts +3 -0
- package/dist/api-contract-discovery/protocol-http-context-escapes.mjs +95 -0
- package/dist/api-contract-discovery/protocol-http-context.d.mts +1 -2
- package/dist/api-contract-discovery/protocol-http-context.mjs +6 -29
- package/dist/api-contract-discovery/protocol-http-method-access.d.mts +9 -0
- package/dist/api-contract-discovery/protocol-http-method-access.mjs +34 -0
- package/dist/api-contract-discovery/protocol-http-reflect.d.mts +5 -0
- package/dist/api-contract-discovery/protocol-http-reflect.mjs +45 -0
- package/dist/api-contract-discovery/protocol-http-response-mutations.d.mts +3 -0
- package/dist/api-contract-discovery/protocol-http-response-mutations.mjs +31 -0
- package/dist/api-contract-discovery/protocol-platform-callbacks.d.mts +2 -0
- package/dist/api-contract-discovery/protocol-platform-callbacks.mjs +4 -0
- package/dist/api-contract-discovery/protocol-sse-raw-writes.mjs +36 -7
- package/dist/api-contract-discovery/protocol-sse-write-helpers.d.mts +1 -0
- package/dist/api-contract-discovery/protocol-sse-write-helpers.mjs +21 -5
- package/dist/api-contract-discovery/protocol-sse-write-mutations.d.mts +1 -1
- package/dist/api-contract-discovery/protocol-sse-write-mutations.mjs +8 -2
- package/dist/api-contract-discovery/query-contract-extraction.mjs +20 -3
- package/dist/api-contract-discovery/query-contract-literals.d.mts +5 -0
- package/dist/api-contract-discovery/query-contract-literals.mjs +29 -0
- package/dist/api-contract-discovery/registered-route-binding-writes.d.mts +2 -1
- package/dist/api-contract-discovery/registered-route-binding-writes.mjs +20 -11
- package/dist/api-contract-discovery/registered-route-handler-analysis.d.mts +7 -1
- package/dist/api-contract-discovery/registered-route-handler-analysis.mjs +27 -26
- package/dist/api-contract-discovery/registered-route-handler-helpers.d.mts +3 -0
- package/dist/api-contract-discovery/registered-route-handler-helpers.mjs +16 -0
- package/dist/api-contract-discovery/request-validation-callbacks.d.mts +9 -0
- package/dist/api-contract-discovery/request-validation-callbacks.mjs +27 -0
- package/dist/api-contract-discovery/request-validation-checker.d.mts +7 -0
- package/dist/api-contract-discovery/request-validation-checker.mjs +25 -0
- package/dist/api-contract-discovery/request-validation-conditional.d.mts +6 -0
- package/dist/api-contract-discovery/request-validation-conditional.mjs +31 -0
- package/dist/api-contract-discovery/request-validation-factory-calls.d.mts +11 -0
- package/dist/api-contract-discovery/request-validation-factory-calls.mjs +23 -0
- package/dist/api-contract-discovery/request-validation-facts.d.mts +16 -0
- package/dist/api-contract-discovery/request-validation-facts.mjs +64 -0
- package/dist/api-contract-discovery/request-validation-follow.d.mts +24 -0
- package/dist/api-contract-discovery/request-validation-follow.mjs +38 -0
- package/dist/api-contract-discovery/request-validation-handler-state.d.mts +8 -0
- package/dist/api-contract-discovery/request-validation-handler-state.mjs +35 -0
- package/dist/api-contract-discovery/request-validation-input.d.mts +12 -0
- package/dist/api-contract-discovery/request-validation-input.mjs +43 -0
- package/dist/api-contract-discovery/request-validation-keys.d.mts +25 -0
- package/dist/api-contract-discovery/request-validation-keys.mjs +119 -0
- package/dist/api-contract-discovery/request-validation-match.d.mts +11 -0
- package/dist/api-contract-discovery/request-validation-match.mjs +47 -0
- package/dist/api-contract-discovery/request-validation-mutation.d.mts +7 -0
- package/dist/api-contract-discovery/request-validation-mutation.mjs +80 -0
- package/dist/api-contract-discovery/request-validation-origin.d.mts +20 -0
- package/dist/api-contract-discovery/request-validation-origin.mjs +71 -0
- package/dist/api-contract-discovery/request-validation-reads.d.mts +13 -0
- package/dist/api-contract-discovery/request-validation-reads.mjs +88 -0
- package/dist/api-contract-discovery/request-validation-registration.d.mts +9 -0
- package/dist/api-contract-discovery/request-validation-registration.mjs +32 -0
- package/dist/api-contract-discovery/request-validation-report.d.mts +3 -0
- package/dist/api-contract-discovery/request-validation-report.mjs +21 -0
- package/dist/api-contract-discovery/request-validation-request-origin.d.mts +5 -0
- package/dist/api-contract-discovery/request-validation-request-origin.mjs +13 -0
- package/dist/api-contract-discovery/request-validation-select.d.mts +9 -0
- package/dist/api-contract-discovery/request-validation-select.mjs +36 -0
- package/dist/api-contract-discovery/request-validation-sites.d.mts +9 -0
- package/dist/api-contract-discovery/request-validation-sites.mjs +77 -0
- package/dist/api-contract-discovery/request-validation-state-key.d.mts +13 -0
- package/dist/api-contract-discovery/request-validation-state-key.mjs +30 -0
- package/dist/api-contract-discovery/request-validation-trace-helpers.d.mts +14 -0
- package/dist/api-contract-discovery/request-validation-trace-helpers.mjs +80 -0
- package/dist/api-contract-discovery/request-validation-trace-select.d.mts +23 -0
- package/dist/api-contract-discovery/request-validation-trace-select.mjs +45 -0
- package/dist/api-contract-discovery/request-validation-trace.d.mts +8 -0
- package/dist/api-contract-discovery/request-validation-trace.mjs +130 -0
- package/dist/api-contract-discovery/request-validation-types.d.mts +84 -0
- package/dist/api-contract-discovery/request-validation-types.mjs +1 -0
- package/dist/api-contract-discovery/request-validation-walker.d.mts +26 -0
- package/dist/api-contract-discovery/request-validation-walker.mjs +143 -0
- package/dist/api-contract-discovery/request-validation.test-helpers.d.mts +39 -0
- package/dist/api-contract-discovery/request-validation.test-helpers.mjs +108 -0
- package/dist/api-contract-discovery/response-contract-attribution-facts.d.mts +2 -0
- package/dist/api-contract-discovery/response-contract-attribution-facts.mjs +18 -0
- package/dist/api-contract-discovery/response-contract-attribution.d.mts +3 -0
- package/dist/api-contract-discovery/response-contract-attribution.mjs +23 -0
- package/dist/api-contract-discovery/response-contract-call-classification.d.mts +3 -0
- package/dist/api-contract-discovery/response-contract-call-classification.mjs +37 -0
- package/dist/api-contract-discovery/response-contract-error-branch.d.mts +1 -1
- package/dist/api-contract-discovery/response-contract-error-branch.mjs +109 -6
- package/dist/api-contract-discovery/response-contract-handler-bindings.d.mts +3 -0
- package/dist/api-contract-discovery/response-contract-handler-bindings.mjs +126 -0
- package/dist/api-contract-discovery/response-contract-handler-calls.d.mts +3 -0
- package/dist/api-contract-discovery/response-contract-handler-calls.mjs +81 -0
- package/dist/api-contract-discovery/response-contract-implicit-variants.d.mts +2 -0
- package/dist/api-contract-discovery/response-contract-implicit-variants.mjs +6 -0
- package/dist/api-contract-discovery/response-contract-implicit.d.mts +2 -1
- package/dist/api-contract-discovery/response-contract-implicit.mjs +9 -17
- package/dist/api-contract-discovery/response-contract-lenient.d.mts +18 -0
- package/dist/api-contract-discovery/response-contract-object-symbols.d.mts +3 -0
- package/dist/api-contract-discovery/response-contract-object-symbols.mjs +80 -0
- package/dist/api-contract-discovery/response-contract-property-mutations.d.mts +5 -0
- package/dist/api-contract-discovery/response-contract-property-mutations.mjs +84 -0
- package/dist/api-contract-discovery/response-contract-registration.d.mts +1 -1
- package/dist/api-contract-discovery/response-contract-registration.mjs +7 -3
- package/dist/api-contract-discovery/response-contract-registry.mjs +43 -8
- package/dist/api-contract-discovery/response-contract-route-analysis.d.mts +3 -4
- package/dist/api-contract-discovery/response-contract-route-analysis.mjs +4 -101
- package/dist/api-contract-discovery/response-contract-route-syntax.d.mts +7 -0
- package/dist/api-contract-discovery/response-contract-route-syntax.mjs +41 -0
- package/dist/api-contract-discovery/response-contract-symbols.d.mts +5 -0
- package/dist/api-contract-discovery/response-contract-symbols.mjs +82 -0
- package/dist/basic-auth-doc-sync/index.d.mts +3 -0
- package/dist/basic-auth-doc-sync/index.mjs +2 -0
- package/dist/basic-auth-doc-sync/methods.d.mts +1 -0
- package/dist/basic-auth-doc-sync/methods.mjs +4 -0
- package/dist/basic-auth-doc-sync/runbook.d.mts +5 -0
- package/dist/basic-auth-doc-sync/runbook.mjs +59 -0
- package/dist/basic-auth-doc-sync/source.d.mts +2 -0
- package/dist/basic-auth-doc-sync/source.mjs +71 -0
- package/dist/compiler-build/index.d.mts +2 -0
- package/dist/compiler-build/index.mjs +1 -0
- package/dist/compiler-build/program-configuration.d.mts +9 -0
- package/dist/compiler-build/program-configuration.mjs +28 -0
- package/dist/compiler-build/program-types.d.mts +29 -0
- package/dist/compiler-build/program-types.mjs +1 -0
- package/dist/compiler-build/program.d.mts +7 -0
- package/dist/compiler-build/program.mjs +78 -0
- package/dist/contract-schema/index.d.mts +4 -0
- package/dist/contract-schema/index.mjs +2 -0
- package/dist/contract-schema/type-query-api.d.mts +44 -0
- package/dist/contract-schema/type-query-api.mjs +10 -0
- package/dist/contract-schema/type-query-call.d.mts +16 -0
- package/dist/contract-schema/type-query-call.mjs +52 -0
- package/dist/contract-schema/type-query-export.d.mts +10 -0
- package/dist/contract-schema/type-query-export.mjs +91 -0
- package/dist/contract-schema/type-query.d.mts +22 -0
- package/dist/contract-schema/type-query.mjs +39 -0
- package/dist/finite-enum-ripple/ast.d.mts +9 -0
- package/dist/finite-enum-ripple/ast.mjs +94 -0
- package/dist/finite-enum-ripple/check.d.mts +3 -0
- package/dist/finite-enum-ripple/check.mjs +62 -0
- package/dist/finite-enum-ripple/compare.d.mts +20 -0
- package/dist/finite-enum-ripple/compare.mjs +90 -0
- package/dist/finite-enum-ripple/component-paths.d.mts +8 -0
- package/dist/finite-enum-ripple/component-paths.mjs +30 -0
- package/dist/finite-enum-ripple/configured-object-mutations.d.mts +1 -0
- package/dist/finite-enum-ripple/configured-object-mutations.mjs +78 -0
- package/dist/finite-enum-ripple/create-page-jsx.d.mts +3 -0
- package/dist/finite-enum-ripple/create-page-jsx.mjs +28 -0
- package/dist/finite-enum-ripple/create-page-literals.d.mts +2 -0
- package/dist/finite-enum-ripple/create-page-literals.mjs +83 -0
- package/dist/finite-enum-ripple/create-page-object-visitor.d.mts +2 -0
- package/dist/finite-enum-ripple/create-page-object-visitor.mjs +57 -0
- package/dist/finite-enum-ripple/create-page-properties.d.mts +4 -0
- package/dist/finite-enum-ripple/create-page-properties.mjs +35 -0
- package/dist/finite-enum-ripple/index.d.mts +2 -0
- package/dist/finite-enum-ripple/index.mjs +1 -0
- package/dist/finite-enum-ripple/model.d.mts +74 -0
- package/dist/finite-enum-ripple/model.mjs +1 -0
- package/dist/finite-enum-ripple/parser-support.d.mts +11 -0
- package/dist/finite-enum-ripple/parser-support.mjs +86 -0
- package/dist/finite-enum-ripple/parsers.d.mts +32 -0
- package/dist/finite-enum-ripple/parsers.mjs +113 -0
- package/dist/finite-enum-ripple/pattern-captures.d.mts +2 -0
- package/dist/finite-enum-ripple/pattern-captures.mjs +45 -0
- package/dist/finite-enum-ripple/structured-route-check.d.mts +3 -0
- package/dist/finite-enum-ripple/structured-route-check.mjs +94 -0
- package/dist/finite-enum-ripple/types.d.mts +2 -0
- package/dist/finite-enum-ripple/types.mjs +60 -0
- package/dist/finite-enum-ripple/union-factory-check.d.mts +2 -0
- package/dist/finite-enum-ripple/union-factory-check.mjs +34 -0
- package/dist/finite-enum-ripple/union-types.d.mts +2 -0
- package/dist/finite-enum-ripple/union-types.mjs +129 -0
- package/dist/markdown/content-facts.d.mts +6 -0
- package/dist/markdown/content-facts.mjs +18 -0
- package/dist/markdown/html-blocks.d.mts +5 -0
- package/dist/markdown/html-blocks.mjs +51 -0
- package/dist/markdown/index.d.mts +4 -1
- package/dist/markdown/index.mjs +3 -0
- package/dist/markdown/tasks.d.mts +2 -0
- package/dist/markdown/tasks.mjs +10 -0
- package/dist/markdown/types.d.mts +27 -0
- package/dist/openapi-document/build-openapi-query.mjs +1 -1
- package/dist/openapi-document/openapi-types.d.mts +1 -1
- package/dist/openapi-document/query-types.d.mts +8 -2
- package/dist/post-publication-inventory/index.d.mts +5 -0
- package/dist/post-publication-inventory/index.mjs +2 -0
- package/dist/post-publication-inventory/readers/sql-executors.d.mts +3 -0
- package/dist/post-publication-inventory/readers/sql-executors.mjs +32 -0
- package/dist/post-publication-inventory/readers/sql-source.d.mts +7 -0
- package/dist/post-publication-inventory/readers/sql-source.mjs +87 -0
- package/dist/post-publication-inventory/readers/sql-template.d.mts +12 -0
- package/dist/post-publication-inventory/readers/sql-template.mjs +119 -0
- package/dist/post-publication-inventory/writers/source.d.mts +9 -0
- package/dist/post-publication-inventory/writers/source.mjs +128 -0
- package/dist/post-publication-inventory/writers/types.d.mts +19 -0
- package/dist/post-publication-inventory/writers/types.mjs +1 -0
- package/docs/basic-auth-doc-sync.md +37 -0
- package/package.json +24 -3
|
@@ -4,6 +4,9 @@ import { enclosingFunction } from './protocol-marker-analysis.mjs';
|
|
|
4
4
|
import { expressionReceiver } from './protocol-write-receiver.mjs';
|
|
5
5
|
import { hasBindingWrite } from './registered-route-binding-writes.mjs';
|
|
6
6
|
import { runtimeParameters } from './registered-route-runtime-parameters.mjs';
|
|
7
|
+
import { contextResponseMethod, methodAccess } from './protocol-http-method-access.mjs';
|
|
8
|
+
import { reflectHttpResponseMethod } from './protocol-http-reflect.mjs';
|
|
9
|
+
export { contextResponseMethod } from './protocol-http-method-access.mjs';
|
|
7
10
|
/** A helper parameter is a response context only when every executable caller passes one. */
|
|
8
11
|
export function httpHandlerContext(fn, checker, active = new Set()) {
|
|
9
12
|
if (active.has(fn))
|
|
@@ -31,35 +34,6 @@ export function httpHandlerContext(fn, checker, active = new Set()) {
|
|
|
31
34
|
const result = valid ? context : undefined;
|
|
32
35
|
return result;
|
|
33
36
|
}
|
|
34
|
-
/** Literal bracket access is equivalent to property access for response methods. */
|
|
35
|
-
export function contextResponseMethod(expression, context, checker, allowMutable = false) {
|
|
36
|
-
const access = methodAccess(expression);
|
|
37
|
-
if (!access)
|
|
38
|
-
return undefined;
|
|
39
|
-
const receiver = expressionReceiver(access.receiver, checker) ??
|
|
40
|
-
bracketResponseReceiver(access.receiver, checker);
|
|
41
|
-
if (receiver?.root !== context || (receiver.mutableAlias && !allowMutable))
|
|
42
|
-
return undefined;
|
|
43
|
-
if (receiver.path.length === 0)
|
|
44
|
-
return access.name;
|
|
45
|
-
if (receiver.path.length === 1 && receiver.path[0] === 'response')
|
|
46
|
-
return `response.${access.name}`;
|
|
47
|
-
return undefined;
|
|
48
|
-
}
|
|
49
|
-
function methodAccess(expression) {
|
|
50
|
-
if (ts.isPropertyAccessExpression(expression))
|
|
51
|
-
return { receiver: expression.expression, name: expression.name.text };
|
|
52
|
-
if (ts.isElementAccessExpression(expression) && ts.isStringLiteral(expression.argumentExpression))
|
|
53
|
-
return { receiver: expression.expression, name: expression.argumentExpression.text };
|
|
54
|
-
return undefined;
|
|
55
|
-
}
|
|
56
|
-
function bracketResponseReceiver(expression, checker) {
|
|
57
|
-
const access = methodAccess(expression);
|
|
58
|
-
if (!access || access.name !== 'response')
|
|
59
|
-
return undefined;
|
|
60
|
-
const receiver = expressionReceiver(access.receiver, checker);
|
|
61
|
-
return receiver && { ...receiver, path: [...receiver.path, 'response'] };
|
|
62
|
-
}
|
|
63
37
|
/** Follow only caller contexts already admitted by the actual-argument proof. */
|
|
64
38
|
export function httpContextScopes(fn, checker) {
|
|
65
39
|
const scopes = new Map();
|
|
@@ -84,6 +58,9 @@ export function httpContextScopes(fn, checker) {
|
|
|
84
58
|
}
|
|
85
59
|
/** Indirect methods cannot prove the declared status or body, including borrowed this receivers. */
|
|
86
60
|
export function indirectHttpResponseMethod(call, context, checker) {
|
|
61
|
+
const reflected = reflectHttpResponseMethod(call, context, checker);
|
|
62
|
+
if (reflected)
|
|
63
|
+
return reflected;
|
|
87
64
|
const outer = methodAccess(call.expression);
|
|
88
65
|
if (!outer || !['call', 'apply', 'bind'].includes(outer.name))
|
|
89
66
|
return undefined;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import ts from '../contract-schema/typescript-api.mts';
|
|
2
|
+
/** Literal bracket access is equivalent to property access for response methods. */
|
|
3
|
+
export declare function contextResponseMethod(expression: ts.Expression, context: ts.Symbol, checker: ts.TypeChecker, allowMutable?: boolean): string | undefined;
|
|
4
|
+
export declare function methodAccess(expression: ts.Expression): {
|
|
5
|
+
receiver: ts.LeftHandSideExpression;
|
|
6
|
+
name: string;
|
|
7
|
+
} | undefined;
|
|
8
|
+
/** Uses the existing canonical receiver plus literal response-property access. */
|
|
9
|
+
export declare function httpContextReceiver(expression: ts.Expression, checker: ts.TypeChecker): import("./protocol-write-receiver.mts").WriteReceiver | undefined;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import ts from '../contract-schema/typescript-api.mjs';
|
|
2
|
+
import { expressionReceiver } from './protocol-write-receiver.mjs';
|
|
3
|
+
/** Literal bracket access is equivalent to property access for response methods. */
|
|
4
|
+
export function contextResponseMethod(expression, context, checker, allowMutable = false) {
|
|
5
|
+
const access = methodAccess(expression);
|
|
6
|
+
if (!access)
|
|
7
|
+
return undefined;
|
|
8
|
+
const receiver = httpContextReceiver(access.receiver, checker);
|
|
9
|
+
if (receiver?.root !== context || (receiver.mutableAlias && !allowMutable))
|
|
10
|
+
return undefined;
|
|
11
|
+
if (receiver.path.length === 0)
|
|
12
|
+
return access.name;
|
|
13
|
+
if (receiver.path.length === 1 && receiver.path[0] === 'response')
|
|
14
|
+
return `response.${access.name}`;
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
export function methodAccess(expression) {
|
|
18
|
+
if (ts.isPropertyAccessExpression(expression))
|
|
19
|
+
return { receiver: expression.expression, name: expression.name.text };
|
|
20
|
+
if (ts.isElementAccessExpression(expression) && ts.isStringLiteral(expression.argumentExpression))
|
|
21
|
+
return { receiver: expression.expression, name: expression.argumentExpression.text };
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
/** Uses the existing canonical receiver plus literal response-property access. */
|
|
25
|
+
export function httpContextReceiver(expression, checker) {
|
|
26
|
+
const receiver = expressionReceiver(expression, checker);
|
|
27
|
+
if (receiver)
|
|
28
|
+
return receiver;
|
|
29
|
+
const access = methodAccess(expression);
|
|
30
|
+
if (!access || access.name !== 'response')
|
|
31
|
+
return undefined;
|
|
32
|
+
const parent = expressionReceiver(access.receiver, checker);
|
|
33
|
+
return parent && { ...parent, path: [...parent.path, 'response'] };
|
|
34
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import ts from '../contract-schema/typescript-api.mts';
|
|
2
|
+
/** Both the actual invoked value and signature must belong to the compiler library. */
|
|
3
|
+
export declare function standardReflectApply(call: ts.CallExpression, checker: ts.TypeChecker): boolean;
|
|
4
|
+
/** Reflected context emissions cannot prove the marked status or response body. */
|
|
5
|
+
export declare function reflectHttpResponseMethod(call: ts.CallExpression, context: ts.Symbol, checker: ts.TypeChecker): string | undefined;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import ts from '../contract-schema/typescript-api.mjs';
|
|
2
|
+
import { standardCompilerDeclaration } from './protocol-platform-callbacks.mjs';
|
|
3
|
+
import { expressionReceiver } from './protocol-write-receiver.mjs';
|
|
4
|
+
import { unwrapExpression } from './protocol-marker-analysis.mjs';
|
|
5
|
+
import { contextResponseMethod, httpContextReceiver, methodAccess, } from './protocol-http-method-access.mjs';
|
|
6
|
+
/** Both the actual invoked value and signature must belong to the compiler library. */
|
|
7
|
+
export function standardReflectApply(call, checker) {
|
|
8
|
+
const access = methodAccess(unwrapExpression(call.expression));
|
|
9
|
+
const receiver = expressionReceiver(access?.receiver ?? call.expression, checker);
|
|
10
|
+
if (!receiver)
|
|
11
|
+
return false;
|
|
12
|
+
const path = [...receiver.path, ...(access ? [access.name] : [])];
|
|
13
|
+
if (path.join('.') !== 'apply')
|
|
14
|
+
return false;
|
|
15
|
+
if (!receiver.root.declarations?.every((value) => standardCompilerDeclaration(value, checker)))
|
|
16
|
+
return false;
|
|
17
|
+
const declaration = checker.getResolvedSignature(call)?.declaration;
|
|
18
|
+
if (!declaration || !ts.isFunctionDeclaration(declaration) || declaration.name?.text !== 'apply')
|
|
19
|
+
return false;
|
|
20
|
+
const namespace = declaration.parent.parent;
|
|
21
|
+
return (ts.isModuleDeclaration(namespace) &&
|
|
22
|
+
namespace.name.text === 'Reflect' &&
|
|
23
|
+
standardCompilerDeclaration(declaration, checker));
|
|
24
|
+
}
|
|
25
|
+
/** Reflected context emissions cannot prove the marked status or response body. */
|
|
26
|
+
export function reflectHttpResponseMethod(call, context, checker) {
|
|
27
|
+
if (!standardReflectApply(call, checker))
|
|
28
|
+
return undefined;
|
|
29
|
+
const target = call.arguments[0];
|
|
30
|
+
const direct = target && contextResponseMethod(target, context, checker, true);
|
|
31
|
+
if (direct)
|
|
32
|
+
return direct;
|
|
33
|
+
const method = target && expressionReceiver(target, checker);
|
|
34
|
+
if (method?.root === context)
|
|
35
|
+
return method.path[0] === 'response' ? method.path.slice(0, 2).join('.') : method.path[0];
|
|
36
|
+
const receiver = call.arguments[1] && httpContextReceiver(call.arguments[1], checker);
|
|
37
|
+
const name = (target && methodAccess(target)?.name) ?? method?.path.at(-1);
|
|
38
|
+
if (receiver?.root !== context || !name)
|
|
39
|
+
return undefined;
|
|
40
|
+
return receiver.path.length === 0
|
|
41
|
+
? name
|
|
42
|
+
: receiver.path.length === 1 && receiver.path[0] === 'response'
|
|
43
|
+
? `response.${name}`
|
|
44
|
+
: undefined;
|
|
45
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import ts from '../contract-schema/typescript-api.mjs';
|
|
2
|
+
import { contextResponseMethod } from './protocol-http-context.mjs';
|
|
3
|
+
import { unwrapExpression } from './protocol-marker-analysis.mjs';
|
|
4
|
+
import { expressionReceiver } from './protocol-write-receiver.mjs';
|
|
5
|
+
import { executableProtocolPath } from './protocol-execution-path.mjs';
|
|
6
|
+
import { opaqueProtocolCallbackPath } from './protocol-opaque-callback.mjs';
|
|
7
|
+
/** Branded metadata requires the dispatched response's body and status to remain intact. */
|
|
8
|
+
export function mutatedHttpResponse(node, response, checker) {
|
|
9
|
+
const target = mutationTarget(node);
|
|
10
|
+
if (!target)
|
|
11
|
+
return false;
|
|
12
|
+
const receiver = expressionReceiver(target, checker);
|
|
13
|
+
const field = receiver?.root === response
|
|
14
|
+
? receiver.path[0]
|
|
15
|
+
: contextResponseMethod(unwrapExpression(target), response, checker, true);
|
|
16
|
+
return ((field === 'body' || field === 'status') &&
|
|
17
|
+
(executableProtocolPath(node, checker) || opaqueProtocolCallbackPath(node, checker)));
|
|
18
|
+
}
|
|
19
|
+
function mutationTarget(node) {
|
|
20
|
+
if (ts.isBinaryExpression(node) &&
|
|
21
|
+
node.operatorToken.kind >= ts.SyntaxKind.FirstAssignment &&
|
|
22
|
+
node.operatorToken.kind <= ts.SyntaxKind.LastAssignment)
|
|
23
|
+
return node.left;
|
|
24
|
+
if (ts.isDeleteExpression(node))
|
|
25
|
+
return node.expression;
|
|
26
|
+
if ((ts.isPrefixUnaryExpression(node) || ts.isPostfixUnaryExpression(node)) &&
|
|
27
|
+
(node.operator === ts.SyntaxKind.PlusPlusToken ||
|
|
28
|
+
node.operator === ts.SyntaxKind.MinusMinusToken))
|
|
29
|
+
return node.operand;
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import ts from '../contract-schema/typescript-api.mts';
|
|
2
2
|
/** Records the actual caller compiler's library identities, including another installation. */
|
|
3
3
|
export declare function registerPlatformCompilerLibraries(program: ts.Program): void;
|
|
4
|
+
/** Shares actual compiler-library identity with other standard API guards. */
|
|
5
|
+
export declare function standardCompilerDeclaration(declaration: ts.Declaration, checker: ts.TypeChecker): boolean;
|
|
4
6
|
/** Recognizes only callback APIs supplied by the compiler's standard libraries or Node typings. */
|
|
5
7
|
export declare function platformCallbackArgument(node: ts.CallExpression | ts.NewExpression, checker: ts.TypeChecker): ts.Expression | undefined;
|
|
@@ -27,6 +27,10 @@ function standardDeclaration(declaration, timer, checker) {
|
|
|
27
27
|
return (timer &&
|
|
28
28
|
/\/node_modules\/@types\/node\/(?:web-globals\/)?timers\.d\.ts$/.test(file.replaceAll('\\', '/')));
|
|
29
29
|
}
|
|
30
|
+
/** Shares actual compiler-library identity with other standard API guards. */
|
|
31
|
+
export function standardCompilerDeclaration(declaration, checker) {
|
|
32
|
+
return standardDeclaration(declaration, false, checker);
|
|
33
|
+
}
|
|
30
34
|
function platformSymbol(expression, checker, seen = new Set()) {
|
|
31
35
|
const value = unwrapExpression(expression);
|
|
32
36
|
if (!ts.isIdentifier(value))
|
|
@@ -6,7 +6,8 @@ import { enclosingRouteBinding, visit, } from './response-contract-route-analysi
|
|
|
6
6
|
import { expressionReceiver, sameWriteReceiver, } from './protocol-write-receiver.mjs';
|
|
7
7
|
import { sseWriteInvocation } from './protocol-sse-write-access.mjs';
|
|
8
8
|
import { isSourceLevelMutation, mutationAffectsSelectedStream, sseWriteMutation, } from './protocol-sse-write-mutations.mjs';
|
|
9
|
-
import { actualReceivers, helperBindings, routeKey } from './protocol-sse-write-helpers.mjs';
|
|
9
|
+
import { actualReceivers, helperBindings, opaqueCallReceivesSelectedStream, routeKey, } from './protocol-sse-write-helpers.mjs';
|
|
10
|
+
import { writeAccess } from './protocol-sse-write-resolution.mjs';
|
|
10
11
|
/** Reports unmarked writes on executable or opaque escaping paths to a selected frame's stream. */
|
|
11
12
|
export function rejectRawSseWrites(files, checker, bindings, framedWrites, routes, reject) {
|
|
12
13
|
const calls = [];
|
|
@@ -30,8 +31,14 @@ export function rejectRawSseWrites(files, checker, bindings, framedWrites, route
|
|
|
30
31
|
if (mutationNode.getSourceFile() === source && isSourceLevelMutation(mutationNode))
|
|
31
32
|
return true;
|
|
32
33
|
const candidate = enclosingRouteBinding(mutationNode, checker, bindings, false);
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
if (candidate && routeKey(candidate) === routeKey(binding))
|
|
35
|
+
return true;
|
|
36
|
+
if (!candidate &&
|
|
37
|
+
mutationNode.getSourceFile() === source &&
|
|
38
|
+
opaqueProtocolCallbackPath(mutationNode, checker))
|
|
39
|
+
return true;
|
|
40
|
+
return helperBindings(mutationNode, calls, checker, bindings).some((helper) => routeKey(helper) === routeKey(binding));
|
|
41
|
+
}, (mutationNode) => helperBindings(mutationNode, calls, checker, bindings).length > 0, (expression) => {
|
|
35
42
|
const receiver = expressionReceiver(expression, checker);
|
|
36
43
|
return receiver && actualReceivers(receiver, binding, calls, checker, bindings);
|
|
37
44
|
}, (receiver) => actualReceivers(receiver, binding, calls, checker, bindings)))
|
|
@@ -39,7 +46,7 @@ export function rejectRawSseWrites(files, checker, bindings, framedWrites, route
|
|
|
39
46
|
continue;
|
|
40
47
|
}
|
|
41
48
|
const access = sseWriteInvocation(node, checker);
|
|
42
|
-
if (!
|
|
49
|
+
if (!potentiallyExecuted(node))
|
|
43
50
|
continue;
|
|
44
51
|
const proven = executableProtocolPath(node, checker) || opaqueProtocolCallbackPath(node, checker);
|
|
45
52
|
const helpers = helperBindings(node, calls, checker, bindings);
|
|
@@ -51,17 +58,39 @@ export function rejectRawSseWrites(files, checker, bindings, framedWrites, route
|
|
|
51
58
|
const route = routes.get(routeKey(candidate));
|
|
52
59
|
if (!route)
|
|
53
60
|
continue;
|
|
61
|
+
const receiver = access?.receiver && expressionReceiver(access.receiver, checker);
|
|
62
|
+
const actual = receiver ? actualReceivers(receiver, candidate, calls, checker, bindings) : [];
|
|
63
|
+
const framed = route.receivers.flatMap((value) => actualReceivers(value, candidate, calls, checker, bindings));
|
|
64
|
+
const bound = writeAccess(node.expression);
|
|
65
|
+
const boundReceiver = bound?.method === 'bind' && expressionReceiver(bound.receiver, checker);
|
|
66
|
+
const boundActual = boundReceiver
|
|
67
|
+
? actualReceivers(boundReceiver, candidate, calls, checker, bindings)
|
|
68
|
+
: [];
|
|
69
|
+
const accessTargetsSelected = (access?.method === 'write' || access?.method === 'end') &&
|
|
70
|
+
actual.some((value) => value !== undefined &&
|
|
71
|
+
framed.some((frame) => frame !== undefined && sameWriteReceiver(frame, value)));
|
|
72
|
+
const bindsSelectedMember = bound?.method === 'bind' &&
|
|
73
|
+
boundActual.some((value) => value !== undefined &&
|
|
74
|
+
framed.some((frame) => frame !== undefined &&
|
|
75
|
+
frame.root === value.root &&
|
|
76
|
+
value.path.length > frame.path.length &&
|
|
77
|
+
value.path.slice(0, frame.path.length).join('.') === frame.path.join('.')));
|
|
78
|
+
if (!accessTargetsSelected &&
|
|
79
|
+
!bindsSelectedMember &&
|
|
80
|
+
opaqueCallReceivesSelectedStream(node, route.receivers, candidate, calls, checker, bindings)) {
|
|
81
|
+
reject(node, candidate, route.keys);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (!access || !access.rawBytes)
|
|
85
|
+
continue;
|
|
54
86
|
if (!access.receiver) {
|
|
55
87
|
reject(node, candidate, route.keys);
|
|
56
88
|
continue;
|
|
57
89
|
}
|
|
58
|
-
const receiver = expressionReceiver(access.receiver, checker);
|
|
59
90
|
if (!receiver) {
|
|
60
91
|
reject(node, candidate, route.keys);
|
|
61
92
|
continue;
|
|
62
93
|
}
|
|
63
|
-
const actual = actualReceivers(receiver, candidate, calls, checker, bindings);
|
|
64
|
-
const framed = route.receivers.flatMap((value) => actualReceivers(value, candidate, calls, checker, bindings));
|
|
65
94
|
if (actual.some((value) => value === undefined ||
|
|
66
95
|
framed.some((frame) => frame === undefined || sameWriteReceiver(frame, value))))
|
|
67
96
|
reject(node, candidate, route.keys);
|
|
@@ -2,5 +2,6 @@ import ts from '../contract-schema/typescript-api.mts';
|
|
|
2
2
|
import { type HandlerBindings, type RouteBinding } from './response-contract-route-analysis.mts';
|
|
3
3
|
import { type WriteReceiver } from './protocol-write-receiver.mts';
|
|
4
4
|
export declare function routeKey(binding: RouteBinding): string;
|
|
5
|
+
export declare function opaqueCallReceivesSelectedStream(call: ts.CallExpression, selectedReceivers: readonly WriteReceiver[], binding: RouteBinding, calls: readonly ts.CallExpression[], checker: ts.TypeChecker, bindings: HandlerBindings): boolean;
|
|
5
6
|
export declare function helperBindings(node: ts.Node, calls: readonly ts.CallExpression[], checker: ts.TypeChecker, bindings: HandlerBindings): RouteBinding[];
|
|
6
7
|
export declare function actualReceivers(receiver: WriteReceiver, binding: RouteBinding, calls: readonly ts.CallExpression[], checker: ts.TypeChecker, bindings: HandlerBindings, active?: Set<ts.Symbol>): (WriteReceiver | undefined)[];
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import ts from '../contract-schema/typescript-api.mjs';
|
|
2
2
|
import { hasBindingWrite } from './registered-route-binding-writes.mjs';
|
|
3
|
+
import { createProtocolCallbackValueResolver } from './protocol-callback-values.mjs';
|
|
3
4
|
import { runtimeParameters } from './registered-route-runtime-parameters.mjs';
|
|
4
5
|
import { enclosingFunction } from './protocol-marker-analysis.mjs';
|
|
5
6
|
import { executableProtocolPath } from './protocol-execution-path.mjs';
|
|
6
7
|
import { enclosingRouteBinding, } from './response-contract-route-analysis.mjs';
|
|
7
|
-
import { expressionReceiver } from './protocol-write-receiver.mjs';
|
|
8
|
+
import { expressionReceiver, sameWriteReceiver, } from './protocol-write-receiver.mjs';
|
|
8
9
|
export function routeKey(binding) {
|
|
9
10
|
return `${binding.method}:${binding.routeTemplate}`;
|
|
10
11
|
}
|
|
@@ -22,13 +23,29 @@ function implementationDeclaration(declaration, checker) {
|
|
|
22
23
|
}
|
|
23
24
|
return undefined;
|
|
24
25
|
}
|
|
26
|
+
function implementationCall(call, checker) {
|
|
27
|
+
return (implementationDeclaration(checker.getResolvedSignature(call)?.declaration, checker) ??
|
|
28
|
+
createProtocolCallbackValueResolver(checker).resolve(call.expression, new Map())?.node);
|
|
29
|
+
}
|
|
30
|
+
export function opaqueCallReceivesSelectedStream(call, selectedReceivers, binding, calls, checker, bindings) {
|
|
31
|
+
const implementation = implementationCall(call, checker);
|
|
32
|
+
if (implementation && ts.isFunctionLike(implementation) && 'body' in implementation)
|
|
33
|
+
return false;
|
|
34
|
+
const framed = selectedReceivers.flatMap((receiver) => actualReceivers(receiver, binding, calls, checker, bindings));
|
|
35
|
+
return call.arguments.some((argument) => {
|
|
36
|
+
const receiver = expressionReceiver(argument, checker);
|
|
37
|
+
if (!receiver)
|
|
38
|
+
return false;
|
|
39
|
+
return actualReceivers(receiver, binding, calls, checker, bindings).some((value) => value === undefined ||
|
|
40
|
+
framed.some((frame) => frame === undefined || sameWriteReceiver(frame, value)));
|
|
41
|
+
});
|
|
42
|
+
}
|
|
25
43
|
export function helperBindings(node, calls, checker, bindings) {
|
|
26
44
|
const fn = enclosingFunction(node);
|
|
27
45
|
if (!fn || fn.asteriskToken)
|
|
28
46
|
return [];
|
|
29
47
|
return calls.flatMap((call) => {
|
|
30
|
-
if (
|
|
31
|
-
!executableProtocolPath(call, checker))
|
|
48
|
+
if (implementationCall(call, checker) !== fn || !executableProtocolPath(call, checker))
|
|
32
49
|
return [];
|
|
33
50
|
const binding = enclosingRouteBinding(call, checker, bindings, false);
|
|
34
51
|
return binding ? [binding] : [];
|
|
@@ -50,8 +67,7 @@ export function actualReceivers(receiver, binding, calls, checker, bindings, act
|
|
|
50
67
|
return [undefined];
|
|
51
68
|
const index = runtimeParameters(fn).indexOf(declaration);
|
|
52
69
|
const actuals = calls.flatMap((call) => {
|
|
53
|
-
if (
|
|
54
|
-
!executableProtocolPath(call, checker))
|
|
70
|
+
if (implementationCall(call, checker) !== fn || !executableProtocolPath(call, checker))
|
|
55
71
|
return [];
|
|
56
72
|
const callBinding = enclosingRouteBinding(call, checker, bindings, false);
|
|
57
73
|
if (!callBinding || routeKey(callBinding) !== routeKey(binding))
|
|
@@ -5,5 +5,5 @@ export type SseWriteMutation = {
|
|
|
5
5
|
receiver: ts.Expression;
|
|
6
6
|
};
|
|
7
7
|
export declare function sseWriteMutation(node: ts.Node): SseWriteMutation | undefined;
|
|
8
|
-
export declare function mutationAffectsSelectedStream(mutations: readonly SseWriteMutation[], frameReceivers: readonly WriteReceiver[], checker: ts.TypeChecker, isSameRoute: (node: ts.Node) => boolean, resolveActual: (expression: ts.Expression) => readonly (WriteReceiver | undefined)[] | undefined, resolveFrame: (receiver: WriteReceiver) => readonly (WriteReceiver | undefined)[]): boolean;
|
|
8
|
+
export declare function mutationAffectsSelectedStream(mutations: readonly SseWriteMutation[], frameReceivers: readonly WriteReceiver[], checker: ts.TypeChecker, isSameRoute: (node: ts.Node) => boolean, isInvokedHelper: (node: ts.Node) => boolean, resolveActual: (expression: ts.Expression) => readonly (WriteReceiver | undefined)[] | undefined, resolveFrame: (receiver: WriteReceiver) => readonly (WriteReceiver | undefined)[]): boolean;
|
|
9
9
|
export declare function isSourceLevelMutation(node: ts.Node): boolean;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import ts from '../contract-schema/typescript-api.mjs';
|
|
2
|
+
import { potentiallyExecuted } from './protocol-executable-path.mjs';
|
|
2
3
|
import { executableProtocolPath } from './protocol-execution-path.mjs';
|
|
4
|
+
import { opaqueProtocolCallbackPath } from './protocol-opaque-callback.mjs';
|
|
3
5
|
import { sameWriteReceiver } from './protocol-write-receiver.mjs';
|
|
4
6
|
import { writeAccess } from './protocol-sse-write-resolution.mjs';
|
|
5
7
|
export function sseWriteMutation(node) {
|
|
@@ -18,10 +20,14 @@ export function sseWriteMutation(node) {
|
|
|
18
20
|
function isAssignment(kind) {
|
|
19
21
|
return kind >= ts.SyntaxKind.FirstAssignment && kind <= ts.SyntaxKind.LastAssignment;
|
|
20
22
|
}
|
|
21
|
-
export function mutationAffectsSelectedStream(mutations, frameReceivers, checker, isSameRoute, resolveActual, resolveFrame) {
|
|
23
|
+
export function mutationAffectsSelectedStream(mutations, frameReceivers, checker, isSameRoute, isInvokedHelper, resolveActual, resolveFrame) {
|
|
22
24
|
const framed = frameReceivers.flatMap(resolveFrame);
|
|
23
25
|
return mutations.some(({ node, receiver }) => {
|
|
24
|
-
if (
|
|
26
|
+
if (!potentiallyExecuted(node) ||
|
|
27
|
+
(!isSourceLevelMutation(node) &&
|
|
28
|
+
!executableProtocolPath(node, checker) &&
|
|
29
|
+
!opaqueProtocolCallbackPath(node, checker) &&
|
|
30
|
+
!isInvokedHelper(node)) ||
|
|
25
31
|
!isSameRoute(node))
|
|
26
32
|
return false;
|
|
27
33
|
const actual = resolveActual(receiver);
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
import ts from '../contract-schema/typescript-api.mjs';
|
|
2
2
|
import { contractError } from './response-contract-registration.mjs';
|
|
3
|
-
import { optionalNumberLiteral, optionalStringLiteral, requiredBooleanLiteral, requiredNumberLiteral, requiredPropertyType, requiredStringLiteral, stringTuple, } from './query-contract-literals.mjs';
|
|
3
|
+
import { optionalNumberLiteral, declaresProperty, optionalTrueLiteral, optionalStringLiteral, requiredBooleanLiteral, requiredNumberLiteral, requiredPropertyType, rejectUnresolvedType, requiredStringLiteral, stringTuple, } from './query-contract-literals.mjs';
|
|
4
4
|
export function extractQueryParameterDescriptor(type, checker, sourceFile, node, parameterName) {
|
|
5
|
-
|
|
5
|
+
rejectUnresolvedType(type, failure(sourceFile, node, parameterName));
|
|
6
|
+
const descriptor = extractDescriptorShape(type, checker, sourceFile, node, parameterName);
|
|
7
|
+
if (!optionalTrueLiteral(type, 'required', checker, failure(sourceFile, node, parameterName))) {
|
|
8
|
+
return descriptor;
|
|
9
|
+
}
|
|
10
|
+
if (declaresProperty(type, 'default')) {
|
|
11
|
+
failure(sourceFile, node, parameterName)('required cannot be combined with default');
|
|
12
|
+
}
|
|
13
|
+
return { ...descriptor, required: true };
|
|
14
|
+
}
|
|
15
|
+
function failure(sourceFile, node, parameterName) {
|
|
16
|
+
return (detail) => {
|
|
6
17
|
throw contractError(sourceFile, node, `Malformed query parameter "${parameterName}": ${detail}`);
|
|
7
18
|
};
|
|
19
|
+
}
|
|
20
|
+
function extractDescriptorShape(type, checker, sourceFile, node, parameterName) {
|
|
21
|
+
const fail = failure(sourceFile, node, parameterName);
|
|
8
22
|
const kind = requiredStringLiteral(type, 'kind', checker, fail);
|
|
9
23
|
const description = optionalStringLiteral(type, 'description', checker, node, fail);
|
|
10
24
|
const options = description === undefined ? {} : { description };
|
|
@@ -55,7 +69,10 @@ export function extractQueryParameterDescriptor(type, checker, sourceFile, node,
|
|
|
55
69
|
if (requiredBooleanLiteral(type, 'explode', checker, fail) !== false)
|
|
56
70
|
fail('explode must be false');
|
|
57
71
|
const itemType = requiredPropertyType(type, 'items', checker, fail);
|
|
58
|
-
|
|
72
|
+
rejectUnresolvedType(itemType, fail);
|
|
73
|
+
if (declaresProperty(itemType, 'required'))
|
|
74
|
+
fail('array items cannot be required');
|
|
75
|
+
const items = extractDescriptorShape(itemType, checker, sourceFile, node, `${parameterName}[]`);
|
|
59
76
|
if (items.kind === 'string' || items.kind === 'enum') {
|
|
60
77
|
return { kind, items, style: 'form', explode: false, ...options };
|
|
61
78
|
}
|
|
@@ -5,4 +5,9 @@ export declare function requiredNumberLiteral(type: ts.Type, name: string, check
|
|
|
5
5
|
export declare function requiredBooleanLiteral(type: ts.Type, name: string, checker: ts.TypeChecker, fail: (detail: string) => never): boolean;
|
|
6
6
|
export declare function optionalStringLiteral(type: ts.Type, name: string, checker: ts.TypeChecker, node: ts.Node, fail: (detail: string) => never): string | undefined;
|
|
7
7
|
export declare function optionalNumberLiteral(type: ts.Type, name: string, checker: ts.TypeChecker, node: ts.Node, fail: (detail: string) => never): number | undefined;
|
|
8
|
+
export declare function optionalTrueLiteral(type: ts.Type, name: string, checker: ts.TypeChecker, fail: (detail: string) => never): boolean;
|
|
9
|
+
/** True when the type, or any member of a union type, declares the property. */
|
|
10
|
+
export declare function declaresProperty(type: ts.Type, name: string): boolean;
|
|
8
11
|
export declare function stringTuple(type: ts.Type, name: string, checker: ts.TypeChecker, fail: (detail: string) => never): readonly string[];
|
|
12
|
+
/** Fails when the type, or any union/intersection constituent, is conditional or generic. */
|
|
13
|
+
export declare function rejectUnresolvedType(type: ts.Type, fail: (detail: string) => never): void;
|
|
@@ -42,6 +42,25 @@ export function optionalNumberLiteral(type, name, checker, node, fail) {
|
|
|
42
42
|
const value = checker.getTypeOfSymbolAtLocation(property, location);
|
|
43
43
|
return value.isNumberLiteral() ? value.value : fail(`requires literal ${name}`);
|
|
44
44
|
}
|
|
45
|
+
export function optionalTrueLiteral(type, name, checker, fail) {
|
|
46
|
+
const property = type.getProperty(name);
|
|
47
|
+
if (!property) {
|
|
48
|
+
if (declaresProperty(type, name))
|
|
49
|
+
return fail(`${name} must be present on every union member`);
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
if (property.flags & ts.SymbolFlags.Optional)
|
|
53
|
+
return fail(`${name} must be literal when present`);
|
|
54
|
+
const value = requiredPropertyType(type, name, checker, fail);
|
|
55
|
+
const isTrue = value.flags & ts.TypeFlags.BooleanLiteral && checker.typeToString(value) === 'true';
|
|
56
|
+
return isTrue ? true : fail(`${name} must be the literal true`);
|
|
57
|
+
}
|
|
58
|
+
/** True when the type, or any member of a union type, declares the property. */
|
|
59
|
+
export function declaresProperty(type, name) {
|
|
60
|
+
if (type.getProperty(name))
|
|
61
|
+
return true;
|
|
62
|
+
return type.isUnion() && type.types.some((member) => member.getProperty(name) !== undefined);
|
|
63
|
+
}
|
|
45
64
|
export function stringTuple(type, name, checker, fail) {
|
|
46
65
|
const value = requiredPropertyType(type, name, checker, fail);
|
|
47
66
|
if (!checker.isTupleType(value))
|
|
@@ -52,3 +71,13 @@ export function stringTuple(type, name, checker, fail) {
|
|
|
52
71
|
}
|
|
53
72
|
return values.map((item) => item.value);
|
|
54
73
|
}
|
|
74
|
+
/** Fails when the type, or any union/intersection constituent, is conditional or generic. */
|
|
75
|
+
export function rejectUnresolvedType(type, fail) {
|
|
76
|
+
if (type.flags & (ts.TypeFlags.Conditional | ts.TypeFlags.Instantiable)) {
|
|
77
|
+
fail('descriptor must not contain unresolved conditional or generic types');
|
|
78
|
+
}
|
|
79
|
+
if (type.isUnionOrIntersection()) {
|
|
80
|
+
for (const member of type.types)
|
|
81
|
+
rejectUnresolvedType(member, fail);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import ts from '../contract-schema/typescript-api.mts';
|
|
2
|
+
import type { ProtocolCache } from './protocol-analysis-cache.mts';
|
|
2
3
|
/** Helper and factory parameter bindings must remain unchanged in their declaring source. */
|
|
3
|
-
export declare function hasBindingWrite(declaration: ts.FunctionDeclaration | ts.ParameterDeclaration, checker: ts.TypeChecker): boolean;
|
|
4
|
+
export declare function hasBindingWrite(declaration: ts.FunctionDeclaration | ts.ParameterDeclaration, checker: ts.TypeChecker, cache?: ProtocolCache): boolean;
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import ts from '../contract-schema/typescript-api.mjs';
|
|
2
|
-
/**
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
return false;
|
|
6
|
-
if (!ts.isIdentifier(declaration.name))
|
|
7
|
-
return true;
|
|
8
|
-
const binding = checker.getSymbolAtLocation(declaration.name);
|
|
9
|
-
let written = false;
|
|
2
|
+
/** Every symbol assigned, updated, or looped over in the source file, found in one scan. */
|
|
3
|
+
function writtenSymbols(sourceFile, checker) {
|
|
4
|
+
const written = new Set();
|
|
10
5
|
const referencesBinding = (node) => {
|
|
11
6
|
// Index expressions and computed property names read their keys, not write them.
|
|
12
7
|
// inspect still visits these expressions separately to detect nested side-effect writes.
|
|
@@ -16,8 +11,8 @@ export function hasBindingWrite(declaration, checker) {
|
|
|
16
11
|
referencesBinding(node.expression);
|
|
17
12
|
return;
|
|
18
13
|
}
|
|
19
|
-
if (ts.isIdentifier(node)
|
|
20
|
-
written
|
|
14
|
+
if (ts.isIdentifier(node))
|
|
15
|
+
written.add(checker.getSymbolAtLocation(node));
|
|
21
16
|
node.forEachChild(referencesBinding);
|
|
22
17
|
};
|
|
23
18
|
const inspect = (node) => {
|
|
@@ -33,6 +28,20 @@ export function hasBindingWrite(declaration, checker) {
|
|
|
33
28
|
referencesBinding(node.initializer);
|
|
34
29
|
node.forEachChild(inspect);
|
|
35
30
|
};
|
|
36
|
-
inspect(
|
|
31
|
+
inspect(sourceFile);
|
|
37
32
|
return written;
|
|
38
33
|
}
|
|
34
|
+
/** Helper and factory parameter bindings must remain unchanged in their declaring source. */
|
|
35
|
+
export function hasBindingWrite(declaration, checker, cache) {
|
|
36
|
+
if (!declaration.name)
|
|
37
|
+
return false;
|
|
38
|
+
if (!ts.isIdentifier(declaration.name))
|
|
39
|
+
return true;
|
|
40
|
+
const sourceFile = declaration.getSourceFile();
|
|
41
|
+
let written = cache?.writtenSymbols.get(sourceFile);
|
|
42
|
+
if (!written) {
|
|
43
|
+
written = writtenSymbols(sourceFile, checker);
|
|
44
|
+
cache?.writtenSymbols.set(sourceFile, written);
|
|
45
|
+
}
|
|
46
|
+
return written.has(checker.getSymbolAtLocation(declaration.name));
|
|
47
|
+
}
|
|
@@ -2,5 +2,11 @@ import ts from '../contract-schema/typescript-api.mts';
|
|
|
2
2
|
export type HandlerProof = {
|
|
3
3
|
node: ts.FunctionLikeDeclaration;
|
|
4
4
|
bindings: Map<ts.Symbol, ts.Expression>;
|
|
5
|
+
/** Set under `executableReturns` when a runtime branch selects the handler. */
|
|
6
|
+
conditional?: boolean;
|
|
5
7
|
};
|
|
6
|
-
export declare function handlerNodes(argument: ts.Expression, checker: ts.TypeChecker, parameterBindings?: Map<ts.Symbol, ts.Expression>, active?: Set<ts.Node>, staticProof?: boolean, proofs?: HandlerProof[]
|
|
8
|
+
export declare function handlerNodes(argument: ts.Expression, checker: ts.TypeChecker, parameterBindings?: Map<ts.Symbol, ts.Expression>, active?: Set<ts.Node>, staticProof?: boolean, proofs?: HandlerProof[],
|
|
9
|
+
/** Ignore `let`, `var` and reassigned bindings; always on under `staticProof`. */
|
|
10
|
+
stableBindings?: boolean,
|
|
11
|
+
/** Ignore returned values on statically dead paths, and mark runtime-branch ones conditional. */
|
|
12
|
+
executableReturns?: boolean): ts.Node[];
|