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.
Files changed (208) hide show
  1. package/README.md +219 -1
  2. package/dist/api-contract-discovery/index.d.mts +4 -1
  3. package/dist/api-contract-discovery/index.mjs +1 -0
  4. package/dist/api-contract-discovery/protocol-analysis-cache.d.mts +21 -0
  5. package/dist/api-contract-discovery/protocol-analysis-cache.mjs +7 -0
  6. package/dist/api-contract-discovery/protocol-callback-invocation.d.mts +2 -1
  7. package/dist/api-contract-discovery/protocol-callback-invocation.mjs +12 -4
  8. package/dist/api-contract-discovery/protocol-callback-values.d.mts +2 -1
  9. package/dist/api-contract-discovery/protocol-callback-values.mjs +3 -3
  10. package/dist/api-contract-discovery/protocol-execution-path.d.mts +2 -1
  11. package/dist/api-contract-discovery/protocol-execution-path.mjs +2 -2
  12. package/dist/api-contract-discovery/protocol-http-association.mjs +8 -0
  13. package/dist/api-contract-discovery/protocol-http-context-callers.d.mts +2 -1
  14. package/dist/api-contract-discovery/protocol-http-context-callers.mjs +13 -7
  15. package/dist/api-contract-discovery/protocol-http-context-escapes.d.mts +3 -0
  16. package/dist/api-contract-discovery/protocol-http-context-escapes.mjs +95 -0
  17. package/dist/api-contract-discovery/protocol-http-context.d.mts +1 -2
  18. package/dist/api-contract-discovery/protocol-http-context.mjs +6 -29
  19. package/dist/api-contract-discovery/protocol-http-method-access.d.mts +9 -0
  20. package/dist/api-contract-discovery/protocol-http-method-access.mjs +34 -0
  21. package/dist/api-contract-discovery/protocol-http-reflect.d.mts +5 -0
  22. package/dist/api-contract-discovery/protocol-http-reflect.mjs +45 -0
  23. package/dist/api-contract-discovery/protocol-http-response-mutations.d.mts +3 -0
  24. package/dist/api-contract-discovery/protocol-http-response-mutations.mjs +31 -0
  25. package/dist/api-contract-discovery/protocol-platform-callbacks.d.mts +2 -0
  26. package/dist/api-contract-discovery/protocol-platform-callbacks.mjs +4 -0
  27. package/dist/api-contract-discovery/protocol-sse-raw-writes.mjs +36 -7
  28. package/dist/api-contract-discovery/protocol-sse-write-helpers.d.mts +1 -0
  29. package/dist/api-contract-discovery/protocol-sse-write-helpers.mjs +21 -5
  30. package/dist/api-contract-discovery/protocol-sse-write-mutations.d.mts +1 -1
  31. package/dist/api-contract-discovery/protocol-sse-write-mutations.mjs +8 -2
  32. package/dist/api-contract-discovery/query-contract-extraction.mjs +20 -3
  33. package/dist/api-contract-discovery/query-contract-literals.d.mts +5 -0
  34. package/dist/api-contract-discovery/query-contract-literals.mjs +29 -0
  35. package/dist/api-contract-discovery/registered-route-binding-writes.d.mts +2 -1
  36. package/dist/api-contract-discovery/registered-route-binding-writes.mjs +20 -11
  37. package/dist/api-contract-discovery/registered-route-handler-analysis.d.mts +7 -1
  38. package/dist/api-contract-discovery/registered-route-handler-analysis.mjs +27 -26
  39. package/dist/api-contract-discovery/registered-route-handler-helpers.d.mts +3 -0
  40. package/dist/api-contract-discovery/registered-route-handler-helpers.mjs +16 -0
  41. package/dist/api-contract-discovery/request-validation-callbacks.d.mts +9 -0
  42. package/dist/api-contract-discovery/request-validation-callbacks.mjs +27 -0
  43. package/dist/api-contract-discovery/request-validation-checker.d.mts +7 -0
  44. package/dist/api-contract-discovery/request-validation-checker.mjs +25 -0
  45. package/dist/api-contract-discovery/request-validation-conditional.d.mts +6 -0
  46. package/dist/api-contract-discovery/request-validation-conditional.mjs +31 -0
  47. package/dist/api-contract-discovery/request-validation-factory-calls.d.mts +11 -0
  48. package/dist/api-contract-discovery/request-validation-factory-calls.mjs +23 -0
  49. package/dist/api-contract-discovery/request-validation-facts.d.mts +16 -0
  50. package/dist/api-contract-discovery/request-validation-facts.mjs +64 -0
  51. package/dist/api-contract-discovery/request-validation-follow.d.mts +24 -0
  52. package/dist/api-contract-discovery/request-validation-follow.mjs +38 -0
  53. package/dist/api-contract-discovery/request-validation-handler-state.d.mts +8 -0
  54. package/dist/api-contract-discovery/request-validation-handler-state.mjs +35 -0
  55. package/dist/api-contract-discovery/request-validation-input.d.mts +12 -0
  56. package/dist/api-contract-discovery/request-validation-input.mjs +43 -0
  57. package/dist/api-contract-discovery/request-validation-keys.d.mts +25 -0
  58. package/dist/api-contract-discovery/request-validation-keys.mjs +119 -0
  59. package/dist/api-contract-discovery/request-validation-match.d.mts +11 -0
  60. package/dist/api-contract-discovery/request-validation-match.mjs +47 -0
  61. package/dist/api-contract-discovery/request-validation-mutation.d.mts +7 -0
  62. package/dist/api-contract-discovery/request-validation-mutation.mjs +80 -0
  63. package/dist/api-contract-discovery/request-validation-origin.d.mts +20 -0
  64. package/dist/api-contract-discovery/request-validation-origin.mjs +71 -0
  65. package/dist/api-contract-discovery/request-validation-reads.d.mts +13 -0
  66. package/dist/api-contract-discovery/request-validation-reads.mjs +88 -0
  67. package/dist/api-contract-discovery/request-validation-registration.d.mts +9 -0
  68. package/dist/api-contract-discovery/request-validation-registration.mjs +32 -0
  69. package/dist/api-contract-discovery/request-validation-report.d.mts +3 -0
  70. package/dist/api-contract-discovery/request-validation-report.mjs +21 -0
  71. package/dist/api-contract-discovery/request-validation-request-origin.d.mts +5 -0
  72. package/dist/api-contract-discovery/request-validation-request-origin.mjs +13 -0
  73. package/dist/api-contract-discovery/request-validation-select.d.mts +9 -0
  74. package/dist/api-contract-discovery/request-validation-select.mjs +36 -0
  75. package/dist/api-contract-discovery/request-validation-sites.d.mts +9 -0
  76. package/dist/api-contract-discovery/request-validation-sites.mjs +77 -0
  77. package/dist/api-contract-discovery/request-validation-state-key.d.mts +13 -0
  78. package/dist/api-contract-discovery/request-validation-state-key.mjs +30 -0
  79. package/dist/api-contract-discovery/request-validation-trace-helpers.d.mts +14 -0
  80. package/dist/api-contract-discovery/request-validation-trace-helpers.mjs +80 -0
  81. package/dist/api-contract-discovery/request-validation-trace-select.d.mts +23 -0
  82. package/dist/api-contract-discovery/request-validation-trace-select.mjs +45 -0
  83. package/dist/api-contract-discovery/request-validation-trace.d.mts +8 -0
  84. package/dist/api-contract-discovery/request-validation-trace.mjs +130 -0
  85. package/dist/api-contract-discovery/request-validation-types.d.mts +84 -0
  86. package/dist/api-contract-discovery/request-validation-types.mjs +1 -0
  87. package/dist/api-contract-discovery/request-validation-walker.d.mts +26 -0
  88. package/dist/api-contract-discovery/request-validation-walker.mjs +143 -0
  89. package/dist/api-contract-discovery/request-validation.test-helpers.d.mts +39 -0
  90. package/dist/api-contract-discovery/request-validation.test-helpers.mjs +108 -0
  91. package/dist/api-contract-discovery/response-contract-attribution-facts.d.mts +2 -0
  92. package/dist/api-contract-discovery/response-contract-attribution-facts.mjs +18 -0
  93. package/dist/api-contract-discovery/response-contract-attribution.d.mts +3 -0
  94. package/dist/api-contract-discovery/response-contract-attribution.mjs +23 -0
  95. package/dist/api-contract-discovery/response-contract-call-classification.d.mts +3 -0
  96. package/dist/api-contract-discovery/response-contract-call-classification.mjs +37 -0
  97. package/dist/api-contract-discovery/response-contract-error-branch.d.mts +1 -1
  98. package/dist/api-contract-discovery/response-contract-error-branch.mjs +109 -6
  99. package/dist/api-contract-discovery/response-contract-handler-bindings.d.mts +3 -0
  100. package/dist/api-contract-discovery/response-contract-handler-bindings.mjs +126 -0
  101. package/dist/api-contract-discovery/response-contract-handler-calls.d.mts +3 -0
  102. package/dist/api-contract-discovery/response-contract-handler-calls.mjs +81 -0
  103. package/dist/api-contract-discovery/response-contract-implicit-variants.d.mts +2 -0
  104. package/dist/api-contract-discovery/response-contract-implicit-variants.mjs +6 -0
  105. package/dist/api-contract-discovery/response-contract-implicit.d.mts +2 -1
  106. package/dist/api-contract-discovery/response-contract-implicit.mjs +9 -17
  107. package/dist/api-contract-discovery/response-contract-lenient.d.mts +18 -0
  108. package/dist/api-contract-discovery/response-contract-object-symbols.d.mts +3 -0
  109. package/dist/api-contract-discovery/response-contract-object-symbols.mjs +80 -0
  110. package/dist/api-contract-discovery/response-contract-property-mutations.d.mts +5 -0
  111. package/dist/api-contract-discovery/response-contract-property-mutations.mjs +84 -0
  112. package/dist/api-contract-discovery/response-contract-registration.d.mts +1 -1
  113. package/dist/api-contract-discovery/response-contract-registration.mjs +7 -3
  114. package/dist/api-contract-discovery/response-contract-registry.mjs +43 -8
  115. package/dist/api-contract-discovery/response-contract-route-analysis.d.mts +3 -4
  116. package/dist/api-contract-discovery/response-contract-route-analysis.mjs +4 -101
  117. package/dist/api-contract-discovery/response-contract-route-syntax.d.mts +7 -0
  118. package/dist/api-contract-discovery/response-contract-route-syntax.mjs +41 -0
  119. package/dist/api-contract-discovery/response-contract-symbols.d.mts +5 -0
  120. package/dist/api-contract-discovery/response-contract-symbols.mjs +82 -0
  121. package/dist/basic-auth-doc-sync/index.d.mts +3 -0
  122. package/dist/basic-auth-doc-sync/index.mjs +2 -0
  123. package/dist/basic-auth-doc-sync/methods.d.mts +1 -0
  124. package/dist/basic-auth-doc-sync/methods.mjs +4 -0
  125. package/dist/basic-auth-doc-sync/runbook.d.mts +5 -0
  126. package/dist/basic-auth-doc-sync/runbook.mjs +59 -0
  127. package/dist/basic-auth-doc-sync/source.d.mts +2 -0
  128. package/dist/basic-auth-doc-sync/source.mjs +71 -0
  129. package/dist/compiler-build/index.d.mts +2 -0
  130. package/dist/compiler-build/index.mjs +1 -0
  131. package/dist/compiler-build/program-configuration.d.mts +9 -0
  132. package/dist/compiler-build/program-configuration.mjs +28 -0
  133. package/dist/compiler-build/program-types.d.mts +29 -0
  134. package/dist/compiler-build/program-types.mjs +1 -0
  135. package/dist/compiler-build/program.d.mts +7 -0
  136. package/dist/compiler-build/program.mjs +78 -0
  137. package/dist/contract-schema/index.d.mts +4 -0
  138. package/dist/contract-schema/index.mjs +2 -0
  139. package/dist/contract-schema/type-query-api.d.mts +44 -0
  140. package/dist/contract-schema/type-query-api.mjs +10 -0
  141. package/dist/contract-schema/type-query-call.d.mts +16 -0
  142. package/dist/contract-schema/type-query-call.mjs +52 -0
  143. package/dist/contract-schema/type-query-export.d.mts +10 -0
  144. package/dist/contract-schema/type-query-export.mjs +91 -0
  145. package/dist/contract-schema/type-query.d.mts +22 -0
  146. package/dist/contract-schema/type-query.mjs +39 -0
  147. package/dist/finite-enum-ripple/ast.d.mts +9 -0
  148. package/dist/finite-enum-ripple/ast.mjs +94 -0
  149. package/dist/finite-enum-ripple/check.d.mts +3 -0
  150. package/dist/finite-enum-ripple/check.mjs +62 -0
  151. package/dist/finite-enum-ripple/compare.d.mts +20 -0
  152. package/dist/finite-enum-ripple/compare.mjs +90 -0
  153. package/dist/finite-enum-ripple/component-paths.d.mts +8 -0
  154. package/dist/finite-enum-ripple/component-paths.mjs +30 -0
  155. package/dist/finite-enum-ripple/configured-object-mutations.d.mts +1 -0
  156. package/dist/finite-enum-ripple/configured-object-mutations.mjs +78 -0
  157. package/dist/finite-enum-ripple/create-page-jsx.d.mts +3 -0
  158. package/dist/finite-enum-ripple/create-page-jsx.mjs +28 -0
  159. package/dist/finite-enum-ripple/create-page-literals.d.mts +2 -0
  160. package/dist/finite-enum-ripple/create-page-literals.mjs +83 -0
  161. package/dist/finite-enum-ripple/create-page-object-visitor.d.mts +2 -0
  162. package/dist/finite-enum-ripple/create-page-object-visitor.mjs +57 -0
  163. package/dist/finite-enum-ripple/create-page-properties.d.mts +4 -0
  164. package/dist/finite-enum-ripple/create-page-properties.mjs +35 -0
  165. package/dist/finite-enum-ripple/index.d.mts +2 -0
  166. package/dist/finite-enum-ripple/index.mjs +1 -0
  167. package/dist/finite-enum-ripple/model.d.mts +74 -0
  168. package/dist/finite-enum-ripple/model.mjs +1 -0
  169. package/dist/finite-enum-ripple/parser-support.d.mts +11 -0
  170. package/dist/finite-enum-ripple/parser-support.mjs +86 -0
  171. package/dist/finite-enum-ripple/parsers.d.mts +32 -0
  172. package/dist/finite-enum-ripple/parsers.mjs +113 -0
  173. package/dist/finite-enum-ripple/pattern-captures.d.mts +2 -0
  174. package/dist/finite-enum-ripple/pattern-captures.mjs +45 -0
  175. package/dist/finite-enum-ripple/structured-route-check.d.mts +3 -0
  176. package/dist/finite-enum-ripple/structured-route-check.mjs +94 -0
  177. package/dist/finite-enum-ripple/types.d.mts +2 -0
  178. package/dist/finite-enum-ripple/types.mjs +60 -0
  179. package/dist/finite-enum-ripple/union-factory-check.d.mts +2 -0
  180. package/dist/finite-enum-ripple/union-factory-check.mjs +34 -0
  181. package/dist/finite-enum-ripple/union-types.d.mts +2 -0
  182. package/dist/finite-enum-ripple/union-types.mjs +129 -0
  183. package/dist/markdown/content-facts.d.mts +6 -0
  184. package/dist/markdown/content-facts.mjs +18 -0
  185. package/dist/markdown/html-blocks.d.mts +5 -0
  186. package/dist/markdown/html-blocks.mjs +51 -0
  187. package/dist/markdown/index.d.mts +4 -1
  188. package/dist/markdown/index.mjs +3 -0
  189. package/dist/markdown/tasks.d.mts +2 -0
  190. package/dist/markdown/tasks.mjs +10 -0
  191. package/dist/markdown/types.d.mts +27 -0
  192. package/dist/openapi-document/build-openapi-query.mjs +1 -1
  193. package/dist/openapi-document/openapi-types.d.mts +1 -1
  194. package/dist/openapi-document/query-types.d.mts +8 -2
  195. package/dist/post-publication-inventory/index.d.mts +5 -0
  196. package/dist/post-publication-inventory/index.mjs +2 -0
  197. package/dist/post-publication-inventory/readers/sql-executors.d.mts +3 -0
  198. package/dist/post-publication-inventory/readers/sql-executors.mjs +32 -0
  199. package/dist/post-publication-inventory/readers/sql-source.d.mts +7 -0
  200. package/dist/post-publication-inventory/readers/sql-source.mjs +87 -0
  201. package/dist/post-publication-inventory/readers/sql-template.d.mts +12 -0
  202. package/dist/post-publication-inventory/readers/sql-template.mjs +119 -0
  203. package/dist/post-publication-inventory/writers/source.d.mts +9 -0
  204. package/dist/post-publication-inventory/writers/source.mjs +128 -0
  205. package/dist/post-publication-inventory/writers/types.d.mts +19 -0
  206. package/dist/post-publication-inventory/writers/types.mjs +1 -0
  207. package/docs/basic-auth-doc-sync.md +37 -0
  208. package/package.json +24 -3
@@ -1,7 +1,10 @@
1
1
  import ts from '../contract-schema/typescript-api.mjs';
2
+ import { functionSymbol, resolveSymbol } from './response-contract-symbols.mjs';
2
3
  import { executableProtocolPath } from './protocol-execution-path.mjs';
4
+ import { HTTP_METHODS, propertyName, routeTemplateFromExpression, } from './response-contract-route-syntax.mjs';
5
+ export { collectHandlerBindings } from './response-contract-handler-bindings.mjs';
6
+ export { propertyName, routeTemplateFromExpression, visit, } from './response-contract-route-syntax.mjs';
3
7
  export { isContextMethod, isContextResponseBufferCall, isContextResponseEmptyCall, responseMarker, } from './response-contract-ctx-analysis.mjs';
4
- const HTTP_METHODS = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']);
5
8
  export function requestedKeyForBinding(binding, requestedKeys) {
6
9
  if (!requestedKeys)
7
10
  return `${binding.method}:${binding.routeTemplate}`;
@@ -57,106 +60,6 @@ export function enclosingRouteBinding(node, checker, handlerBindings, proveCallb
57
60
  }
58
61
  return undefined;
59
62
  }
60
- export function collectHandlerBindings(sourceFiles, checker) {
61
- const bindingsBySymbol = new Map();
62
- for (const sourceFile of sourceFiles) {
63
- visit(sourceFile, (node) => {
64
- if (!ts.isCallExpression(node))
65
- return;
66
- const method = propertyName(node.expression)?.toUpperCase();
67
- if (!method || !HTTP_METHODS.has(method))
68
- return;
69
- const routeTemplate = routeTemplateFromExpression(node.expression);
70
- if (!routeTemplate)
71
- return;
72
- const binding = { method, routeTemplate };
73
- for (const symbol of handlerArgumentSymbols(node, checker)) {
74
- const resolved = resolveSymbol(symbol, checker);
75
- const candidates = bindingsBySymbol.get(resolved) ?? [];
76
- candidates.push(binding);
77
- bindingsBySymbol.set(resolved, candidates);
78
- }
79
- });
80
- }
81
- const bindings = new Map();
82
- for (const [symbol, candidates] of bindingsBySymbol) {
83
- const [first] = candidates;
84
- const unambiguous = candidates.every((candidate) => candidate.method === first.method && candidate.routeTemplate === first.routeTemplate);
85
- // A symbol bound to more than one distinct route (a genuinely shared handler function) can't
86
- // be attributed to either one — recording an arbitrary winner would misdocument whichever
87
- // route lost.
88
- if (unambiguous)
89
- bindings.set(symbol, first);
90
- }
91
- return bindings;
92
- }
93
- /**
94
- * Symbols a route registration call directly implicates: identifiers passed by reference
95
- * (`app.route(...).post(handleThing)`), and — for an inline handler argument — every plain named
96
- * function it calls directly, e.g. `app.route(...).post(async ctx => { await handleThing(ctx) })`.
97
- * `enclosingRouteBinding`'s lexical walk can't see through the latter case on its own, since
98
- * `handleThing`'s body isn't lexically nested inside the route registration at all; recording its
99
- * symbol here lets the same `handlerBindings` lookup resolve it.
100
- */
101
- function handlerArgumentSymbols(node, checker) {
102
- const symbols = [];
103
- for (const argument of node.arguments) {
104
- if (ts.isIdentifier(argument)) {
105
- const symbol = checker.getSymbolAtLocation(argument);
106
- if (symbol)
107
- symbols.push(symbol);
108
- continue;
109
- }
110
- if (isFunctionLike(argument)) {
111
- visit(argument, (child) => {
112
- if (!ts.isCallExpression(child) ||
113
- !ts.isIdentifier(child.expression) ||
114
- !executableProtocolPath(child, checker))
115
- return;
116
- const symbol = checker.getSymbolAtLocation(child.expression);
117
- if (symbol)
118
- symbols.push(symbol);
119
- });
120
- }
121
- }
122
- return symbols;
123
- }
124
- function functionSymbol(node, checker) {
125
- if (ts.isFunctionDeclaration(node) && node.name)
126
- return checker.getSymbolAtLocation(node.name);
127
- if ((ts.isArrowFunction(node) || ts.isFunctionExpression(node)) &&
128
- ts.isVariableDeclaration(node.parent) &&
129
- ts.isIdentifier(node.parent.name)) {
130
- return checker.getSymbolAtLocation(node.parent.name);
131
- }
132
- return undefined;
133
- }
134
- function resolveSymbol(symbol, checker) {
135
- return symbol.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
136
- }
137
- export function routeTemplateFromExpression(expression) {
138
- if (!ts.isPropertyAccessExpression(expression))
139
- return undefined;
140
- return findRouteCall(expression.expression);
141
- }
142
- function findRouteCall(expression) {
143
- if (!ts.isCallExpression(expression))
144
- return undefined;
145
- if (!ts.isPropertyAccessExpression(expression.expression))
146
- return undefined;
147
- if (expression.expression.name.text === 'route') {
148
- const route = expression.arguments[0];
149
- return route && ts.isStringLiteral(route) ? route.text : undefined;
150
- }
151
- return findRouteCall(expression.expression.expression);
152
- }
153
- export function propertyName(expression) {
154
- return ts.isPropertyAccessExpression(expression) ? expression.name.text : undefined;
155
- }
156
63
  function isFunctionLike(node) {
157
64
  return ts.isArrowFunction(node) || ts.isFunctionExpression(node);
158
65
  }
159
- export function visit(node, callback) {
160
- callback(node);
161
- node.forEachChild((child) => visit(child, callback));
162
- }
@@ -0,0 +1,7 @@
1
+ import ts from '../contract-schema/typescript-api.mts';
2
+ export declare const HTTP_METHODS: Set<string>;
3
+ export declare function isInlineRouteHandler(node: ts.Node): boolean;
4
+ export declare function routeTemplateFromExpression(expression: ts.Expression): string | undefined;
5
+ export declare function propertyName(expression: ts.Expression): string | undefined;
6
+ export declare function unwrapTransparentExpression(expression: ts.Expression): ts.Expression;
7
+ export declare function visit(node: ts.Node, callback: (node: ts.Node) => void): void;
@@ -0,0 +1,41 @@
1
+ import ts from '../contract-schema/typescript-api.mjs';
2
+ export const HTTP_METHODS = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']);
3
+ export function isInlineRouteHandler(node) {
4
+ if ((!ts.isArrowFunction(node) && !ts.isFunctionExpression(node)) ||
5
+ !ts.isCallExpression(node.parent))
6
+ return false;
7
+ const method = propertyName(node.parent.expression)?.toUpperCase();
8
+ return (!!method && HTTP_METHODS.has(method) && !!routeTemplateFromExpression(node.parent.expression));
9
+ }
10
+ export function routeTemplateFromExpression(expression) {
11
+ if (!ts.isPropertyAccessExpression(expression))
12
+ return undefined;
13
+ return findRouteCall(expression.expression);
14
+ }
15
+ function findRouteCall(expression) {
16
+ if (!ts.isCallExpression(expression))
17
+ return undefined;
18
+ if (!ts.isPropertyAccessExpression(expression.expression))
19
+ return undefined;
20
+ if (expression.expression.name.text === 'route') {
21
+ const route = expression.arguments[0];
22
+ return route && ts.isStringLiteral(route) ? route.text : undefined;
23
+ }
24
+ return findRouteCall(expression.expression.expression);
25
+ }
26
+ export function propertyName(expression) {
27
+ return ts.isPropertyAccessExpression(expression) ? expression.name.text : undefined;
28
+ }
29
+ export function unwrapTransparentExpression(expression) {
30
+ while (ts.isParenthesizedExpression(expression) ||
31
+ ts.isAsExpression(expression) ||
32
+ ts.isTypeAssertionExpression(expression) ||
33
+ ts.isSatisfiesExpression(expression) ||
34
+ ts.isNonNullExpression(expression))
35
+ expression = expression.expression;
36
+ return expression;
37
+ }
38
+ export function visit(node, callback) {
39
+ callback(node);
40
+ node.forEachChild((child) => visit(child, callback));
41
+ }
@@ -0,0 +1,5 @@
1
+ import ts from '../contract-schema/typescript-api.mts';
2
+ export declare function functionSymbol(node: ts.Node, checker: ts.TypeChecker): ts.Symbol | undefined;
3
+ export declare function attributionFunctionSymbol(node: ts.Node, checker: ts.TypeChecker): ts.Symbol | undefined;
4
+ export declare function resolveSymbol(symbol: ts.Symbol, checker: ts.TypeChecker): ts.Symbol;
5
+ export declare function attributionSymbol(symbol: ts.Symbol, checker: ts.TypeChecker): ts.Symbol;
@@ -0,0 +1,82 @@
1
+ import ts from '../contract-schema/typescript-api.mjs';
2
+ export function functionSymbol(node, checker) {
3
+ if (ts.isFunctionDeclaration(node) && node.name)
4
+ return checker.getSymbolAtLocation(node.name);
5
+ if ((ts.isArrowFunction(node) || ts.isFunctionExpression(node)) &&
6
+ ts.isVariableDeclaration(node.parent) &&
7
+ ts.isIdentifier(node.parent.name)) {
8
+ return checker.getSymbolAtLocation(node.parent.name);
9
+ }
10
+ return undefined;
11
+ }
12
+ export function attributionFunctionSymbol(node, checker) {
13
+ const existing = functionSymbol(node, checker);
14
+ if (existing)
15
+ return existing;
16
+ if (ts.isMethodDeclaration(node) && node.name)
17
+ return checker.getSymbolAtLocation(node.name);
18
+ if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
19
+ const owner = wrappedFunctionOwner(node);
20
+ if (owner &&
21
+ (ts.isVariableDeclaration(owner) ||
22
+ ts.isPropertyAssignment(owner) ||
23
+ ts.isPropertyDeclaration(owner))) {
24
+ return checker.getSymbolAtLocation(owner.name);
25
+ }
26
+ }
27
+ if (ts.isFunctionDeclaration(node) &&
28
+ !node.name &&
29
+ ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Default)
30
+ return defaultExportSymbol(node.getSourceFile(), checker);
31
+ if ((ts.isArrowFunction(node) || ts.isFunctionExpression(node)) &&
32
+ isDefaultExportExpression(node))
33
+ return defaultExportSymbol(node.getSourceFile(), checker);
34
+ return undefined;
35
+ }
36
+ function wrappedFunctionOwner(node) {
37
+ let current = node;
38
+ let parent = current.parent;
39
+ while (ts.isParenthesizedExpression(parent) ||
40
+ ts.isAsExpression(parent) ||
41
+ ts.isTypeAssertionExpression(parent) ||
42
+ ts.isSatisfiesExpression(parent) ||
43
+ ts.isNonNullExpression(parent)) {
44
+ current = parent;
45
+ parent = current.parent;
46
+ }
47
+ if ((ts.isVariableDeclaration(parent) ||
48
+ ts.isPropertyAssignment(parent) ||
49
+ ts.isPropertyDeclaration(parent)) &&
50
+ parent.initializer === current)
51
+ return parent;
52
+ return undefined;
53
+ }
54
+ function isDefaultExportExpression(node) {
55
+ let current = node;
56
+ let parent = current.parent;
57
+ while ((ts.isParenthesizedExpression(parent) ||
58
+ ts.isAsExpression(parent) ||
59
+ ts.isTypeAssertionExpression(parent) ||
60
+ ts.isSatisfiesExpression(parent) ||
61
+ ts.isNonNullExpression(parent)) &&
62
+ parent.expression === current) {
63
+ current = parent;
64
+ parent = current.parent;
65
+ }
66
+ return ts.isExportAssignment(parent) && !parent.isExportEquals && parent.expression === current;
67
+ }
68
+ function defaultExportSymbol(sourceFile, checker) {
69
+ const moduleSymbol = checker.getSymbolAtLocation(sourceFile);
70
+ return (moduleSymbol &&
71
+ checker.getExportsOfModule(moduleSymbol).find((symbol) => symbol.getName() === 'default'));
72
+ }
73
+ export function resolveSymbol(symbol, checker) {
74
+ return symbol.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
75
+ }
76
+ export function attributionSymbol(symbol, checker) {
77
+ const resolved = resolveSymbol(symbol, checker);
78
+ const name = resolved.valueDeclaration?.name;
79
+ if (!name || !ts.isIdentifier(name))
80
+ return resolved;
81
+ return checker.getSymbolAtLocation(name) ?? resolved;
82
+ }
@@ -0,0 +1,3 @@
1
+ export { findBasicAuthExemptPaths, findBasicAuthExemptMethodsByPath } from './source.mts';
2
+ export { findRunbookExemptRoutes } from './runbook.mts';
3
+ export type { ExemptRoute } from './runbook.mts';
@@ -0,0 +1,2 @@
1
+ export { findBasicAuthExemptPaths, findBasicAuthExemptMethodsByPath } from './source.mjs';
2
+ export { findRunbookExemptRoutes } from './runbook.mjs';
@@ -0,0 +1 @@
1
+ export declare function isCanonicalHttpMethod(method: string): boolean;
@@ -0,0 +1,4 @@
1
+ const HTTP_METHOD_TOKEN_RE = /^[!#$%&'*+\-.^_`|~0-9A-Z]+$/;
2
+ export function isCanonicalHttpMethod(method) {
3
+ return method === method.toUpperCase() && HTTP_METHOD_TOKEN_RE.test(method);
4
+ }
@@ -0,0 +1,5 @@
1
+ export type ExemptRoute = {
2
+ methods: string[];
3
+ path: string;
4
+ };
5
+ export declare function findRunbookExemptRoutes(markdown: string, heading: string): ExemptRoute[] | null;
@@ -0,0 +1,59 @@
1
+ import { parseGfmMarkdown } from '../markdown/ast.mjs';
2
+ import { markdownNodeText } from '../markdown/text.mjs';
3
+ import { parseMarkdownTables } from '../markdown/tables.mjs';
4
+ import { isCanonicalHttpMethod } from './methods.mjs';
5
+ const METHODS_CELL_RE = /^`[^`]+`(?:\s*,\s*`[^`]+`)*$/;
6
+ function codeText(cell) {
7
+ const match = /^`([^`]+)`$/.exec(cell);
8
+ return match?.[1] ?? null;
9
+ }
10
+ function methodTexts(cell) {
11
+ if (!METHODS_CELL_RE.test(cell))
12
+ return null;
13
+ const methods = Array.from(cell.matchAll(/`([^`]+)`/g), (match) => match[1]).filter((value) => value !== undefined);
14
+ return methods.every(isCanonicalHttpMethod) ? methods : null;
15
+ }
16
+ function tableHeaderColumns(cells) {
17
+ const columns = new Map();
18
+ for (const [index, cell] of cells.entries()) {
19
+ const name = cell.toLowerCase();
20
+ if ((name === 'path' || name === 'methods') && columns.has(name))
21
+ return null;
22
+ columns.set(name, index);
23
+ }
24
+ return columns;
25
+ }
26
+ export function findRunbookExemptRoutes(markdown, heading) {
27
+ const children = parseGfmMarkdown(markdown).children;
28
+ const headingIndex = children.findIndex((node) => node.type === 'heading' &&
29
+ node.depth === 3 &&
30
+ markdownNodeText(node, { whitespace: 'collapse' }).toLowerCase() === heading.toLowerCase());
31
+ if (headingIndex === -1)
32
+ return null;
33
+ const endIndex = children.findIndex((node, index) => index > headingIndex && node.type === 'heading' && node.depth <= 3);
34
+ const startOffset = children[headingIndex].position.end.offset;
35
+ const endOffset = endIndex === -1 ? markdown.length : children[endIndex].position.start.offset;
36
+ const table = parseMarkdownTables(markdown.slice(startOffset, endOffset), {
37
+ preserveInlineCodeMarkers: true,
38
+ })[0];
39
+ if (!table || table.length < 2)
40
+ return null;
41
+ const routes = [];
42
+ const columns = tableHeaderColumns(table[0].cells);
43
+ if (!columns)
44
+ return null;
45
+ const pathColumn = columns.get('path') ?? -1;
46
+ const methodsColumn = columns.get('methods') ?? -1;
47
+ if (pathColumn === -1 || methodsColumn === -1)
48
+ return null;
49
+ for (const row of table.slice(1)) {
50
+ const path = codeText(row.cells[pathColumn] ?? '');
51
+ const methods = methodTexts(row.cells[methodsColumn] ?? '');
52
+ if (!path)
53
+ return null;
54
+ if (!methods || methods.length === 0)
55
+ return null;
56
+ routes.push({ methods, path });
57
+ }
58
+ return routes;
59
+ }
@@ -0,0 +1,2 @@
1
+ export declare function findBasicAuthExemptPaths(code: string, variableName: string): string[] | null;
2
+ export declare function findBasicAuthExemptMethodsByPath(code: string, variableName: string): Map<string, string[]> | null;
@@ -0,0 +1,71 @@
1
+ import ts from '../contract-schema/typescript-api.mjs';
2
+ import { isCanonicalHttpMethod } from './methods.mjs';
3
+ export function findBasicAuthExemptPaths(code, variableName) {
4
+ const declaration = findTopLevelConstInitializer(code, variableName);
5
+ if (!declaration)
6
+ return null;
7
+ return stringSetValues(declaration);
8
+ }
9
+ export function findBasicAuthExemptMethodsByPath(code, variableName) {
10
+ const declaration = findTopLevelConstInitializer(code, variableName);
11
+ if (!declaration || !isNewCollectionExpression(declaration, 'Map'))
12
+ return null;
13
+ const [entriesArg] = declaration.arguments ?? [];
14
+ if (!entriesArg || !ts.isArrayLiteralExpression(entriesArg))
15
+ return null;
16
+ const entries = new Map();
17
+ for (const entry of entriesArg.elements) {
18
+ if (!ts.isArrayLiteralExpression(entry) || entry.elements.length !== 2)
19
+ return null;
20
+ const path = stringLiteralValue(entry.elements[0]);
21
+ const methods = stringSetValues(entry.elements[1]);
22
+ if (!path || !methods || methods.length === 0)
23
+ return null;
24
+ if (!methods.every(isCanonicalHttpMethod))
25
+ return null;
26
+ entries.set(path, methods);
27
+ }
28
+ return entries.size > 0 ? entries : null;
29
+ }
30
+ function findTopLevelConstInitializer(code, name) {
31
+ const source = ts.createSourceFile('basic-auth.mts', code, ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
32
+ for (const statement of source.statements) {
33
+ if (!ts.isVariableStatement(statement))
34
+ continue;
35
+ if ((statement.declarationList.flags & ts.NodeFlags.Const) === 0)
36
+ continue;
37
+ for (const declaration of statement.declarationList.declarations) {
38
+ if (!ts.isIdentifier(declaration.name) || declaration.name.text !== name)
39
+ continue;
40
+ return declaration.initializer ?? null;
41
+ }
42
+ }
43
+ return null;
44
+ }
45
+ function stringSetValues(expression) {
46
+ if (!expression || !isNewCollectionExpression(expression, 'Set'))
47
+ return null;
48
+ const [valuesArg] = expression.arguments ?? [];
49
+ if (!valuesArg || !ts.isArrayLiteralExpression(valuesArg))
50
+ return null;
51
+ const values = [];
52
+ for (const element of valuesArg.elements) {
53
+ const value = stringLiteralValue(element);
54
+ if (!value)
55
+ return null;
56
+ values.push(value);
57
+ }
58
+ return values.length > 0 ? values : null;
59
+ }
60
+ function stringLiteralValue(expression) {
61
+ if (expression &&
62
+ (ts.isStringLiteral(expression) || ts.isNoSubstitutionTemplateLiteral(expression))) {
63
+ return expression.text;
64
+ }
65
+ return null;
66
+ }
67
+ function isNewCollectionExpression(expression, collectionName) {
68
+ return (ts.isNewExpression(expression) &&
69
+ ts.isIdentifier(expression.expression) &&
70
+ expression.expression.text === collectionName);
71
+ }
@@ -1,2 +1,4 @@
1
1
  export { compilerHostProbesAreFresh, trackCompilerHost, type CompilerFilesystemHost, type CompilerHostProbeSnapshot, type CompilerHostTrackingOptions, } from './freshness.mts';
2
2
  export { settleBuild, type SettlementCallbacks, type SettlementConfirmation, } from './settlement.mts';
3
+ export { createCompilerProgramCache } from './program.mts';
4
+ export type { CompilerProgramCache, CompilerProgramCacheOptions, CompilerProgramGeneration, CompilerProgramSnapshot, } from './program-types.mts';
@@ -1,2 +1,3 @@
1
1
  export { compilerHostProbesAreFresh, trackCompilerHost, } from './freshness.mjs';
2
2
  export { settleBuild, } from './settlement.mjs';
3
+ export { createCompilerProgramCache } from './program.mjs';
@@ -0,0 +1,9 @@
1
+ import type ts from '@typescript/typescript6';
2
+ import type { CompilerProgramCacheOptions } from './program-types.mts';
3
+ export interface CompilerProgramConfiguration {
4
+ parsed: ts.ParsedCommandLine;
5
+ roots: string[];
6
+ selectedSources: Set<string> | undefined;
7
+ signature: string;
8
+ }
9
+ export declare function readProgramConfiguration(api: typeof ts, input: CompilerProgramCacheOptions): CompilerProgramConfiguration;
@@ -0,0 +1,28 @@
1
+ import { dirname, resolve } from 'node:path';
2
+ export function readProgramConfiguration(api, input) {
3
+ const configPath = resolve(input.configPath);
4
+ const directory = dirname(configPath);
5
+ const config = api.readConfigFile(configPath, (file) => api.sys.readFile(file));
6
+ const format = (diagnostics) => api.formatDiagnosticsWithColorAndContext(diagnostics, {
7
+ getCanonicalFileName: (file) => file,
8
+ getCurrentDirectory: () => directory,
9
+ getNewLine: () => '\n',
10
+ });
11
+ if (config.error)
12
+ throw new Error(format([config.error]));
13
+ const parsed = api.parseJsonConfigFileContent(config.config, api.sys, directory, undefined, configPath);
14
+ if (parsed.errors.length > 0)
15
+ throw new Error(format(parsed.errors));
16
+ const rootNames = typeof input.rootNames === 'function' ? input.rootNames(parsed) : input.rootNames;
17
+ const roots = (rootNames ?? parsed.fileNames).map((file) => resolve(directory, file));
18
+ const names = typeof input.sourceFilePaths === 'function' ? input.sourceFilePaths() : input.sourceFilePaths;
19
+ const selectedSources = names && new Set(names.map((file) => resolve(directory, file)));
20
+ const signature = JSON.stringify({
21
+ config: config.config,
22
+ options: parsed.options,
23
+ projectReferences: parsed.projectReferences?.map((reference) => reference.path),
24
+ roots,
25
+ sources: selectedSources && [...selectedSources].toSorted(),
26
+ });
27
+ return { parsed, roots, selectedSources, signature };
28
+ }
@@ -0,0 +1,29 @@
1
+ import type ts from '@typescript/typescript6';
2
+ import type { CompilerHostTrackingOptions } from './freshness.mts';
3
+ declare const generationBrand: unique symbol;
4
+ /** Identity changes on each rebuild, including after clear(). */
5
+ export type CompilerProgramGeneration = {
6
+ readonly [generationBrand]: true;
7
+ };
8
+ export interface CompilerProgramSnapshot {
9
+ readonly generation: CompilerProgramGeneration;
10
+ readonly program: ts.Program;
11
+ readonly sourceFiles: readonly ts.SourceFile[];
12
+ }
13
+ export interface CompilerProgramCache {
14
+ load(): CompilerProgramSnapshot;
15
+ clear(): void;
16
+ readonly buildCount: number;
17
+ }
18
+ export interface CompilerProgramCacheOptions {
19
+ /** Inject a compiler; otherwise load the optional peer when constructing this cache. */
20
+ ts?: typeof ts;
21
+ configPath: string;
22
+ /** Relative names resolve beside configPath; the callback selects parsed roots on each load. */
23
+ rootNames?: readonly string[] | ((configuration: ts.ParsedCommandLine) => readonly string[]);
24
+ /** Omit to return all sources; relative paths resolve beside configPath. */
25
+ sourceFilePaths?: readonly string[] | (() => readonly string[]);
26
+ maximumAttempts?: number;
27
+ tracking?: CompilerHostTrackingOptions;
28
+ }
29
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import type { CompilerProgramCache, CompilerProgramCacheOptions } from './program-types.mts';
2
+ /**
3
+ * Reuses a real Program while configuration, roots and recorded filesystem probes are unchanged.
4
+ * Rebuilds release the cache's old generation before allocating another Program. Callers retaining
5
+ * old snapshots still own those references; clear() cannot release caller data.
6
+ */
7
+ export declare function createCompilerProgramCache(input: CompilerProgramCacheOptions): CompilerProgramCache;
@@ -0,0 +1,78 @@
1
+ import { createRequire } from 'node:module';
2
+ import { resolve } from 'node:path';
3
+ import { compilerHostProbesAreFresh, trackCompilerHost, } from './freshness.mjs';
4
+ import { readProgramConfiguration } from './program-configuration.mjs';
5
+ import { settleBuild } from './settlement.mjs';
6
+ /**
7
+ * Reuses a real Program while configuration, roots and recorded filesystem probes are unchanged.
8
+ * Rebuilds release the cache's old generation before allocating another Program. Callers retaining
9
+ * old snapshots still own those references; clear() cannot release caller data.
10
+ */
11
+ export function createCompilerProgramCache(input) {
12
+ const api = input.ts ?? createRequire(import.meta.url)('@typescript/typescript6');
13
+ const maximumAttempts = input.maximumAttempts ?? 3;
14
+ let cached;
15
+ let buildCount = 0;
16
+ const readConfiguration = () => {
17
+ try {
18
+ return readProgramConfiguration(api, input);
19
+ }
20
+ catch (error) {
21
+ cached = undefined;
22
+ throw error;
23
+ }
24
+ };
25
+ return {
26
+ get buildCount() {
27
+ return buildCount;
28
+ },
29
+ clear() {
30
+ cached = undefined;
31
+ },
32
+ load() {
33
+ const configuration = readConfiguration();
34
+ if (cached?.signature === configuration.signature &&
35
+ compilerHostProbesAreFresh(cached.probes()))
36
+ return cached;
37
+ cached = undefined;
38
+ const settled = settleBuild(configuration, maximumAttempts, {
39
+ buildAttempt(current) {
40
+ const tracker = trackCompilerHost(api.createCompilerHost(current.parsed.options, true), {
41
+ readFileReplay: 'when-filesystem-metadata-stable',
42
+ ...input.tracking,
43
+ });
44
+ buildCount += 1;
45
+ const program = api.createProgram({
46
+ host: tracker.host,
47
+ options: current.parsed.options,
48
+ rootNames: current.roots,
49
+ ...(current.parsed.projectReferences && {
50
+ projectReferences: current.parsed.projectReferences,
51
+ }),
52
+ });
53
+ // Compiler consumers may issue more host reads after construction; retain their probes.
54
+ return { configuration: current, probes: () => tracker.snapshot(), program };
55
+ },
56
+ confirmAttempt(current, value) {
57
+ const confirmed = readConfiguration();
58
+ return {
59
+ configuration: confirmed,
60
+ settled: current.signature === confirmed.signature &&
61
+ compilerHostProbesAreFresh(value.probes()),
62
+ };
63
+ },
64
+ });
65
+ const selected = settled.configuration.selectedSources;
66
+ cached = {
67
+ generation: Object.freeze({}),
68
+ program: settled.program,
69
+ sourceFiles: settled.program
70
+ .getSourceFiles()
71
+ .filter((file) => !selected || selected.has(resolve(file.fileName))),
72
+ signature: settled.configuration.signature,
73
+ probes: settled.probes,
74
+ };
75
+ return cached;
76
+ },
77
+ };
78
+ }
@@ -7,3 +7,7 @@ export { validateResponseContract } from './contract-schema-validator.mts';
7
7
  export type { ContractValidationIssue, ExtractContractSchemaOptions, ExtractedResponseContract, } from './types.mts';
8
8
  export { buildVirtualProgramMatrix, virtualProgramBuildCountForTest } from './virtual-program.mts';
9
9
  export type { VirtualProgramMatrix } from './virtual-program.mts';
10
+ export { getExportedTypeFacts } from './type-query.mts';
11
+ export type { ExportedTypeFactsRequest, TypeFacts, TypeFactsRequest, ExportedTypeSelector, TypeScriptApi, TypeScriptProgram, } from './type-query.mts';
12
+ export { getCallRowTypeFacts } from './type-query-call.mts';
13
+ export type { CallRowTypeFacts, CallRowTypeFactsRequest } from './type-query-call.mts';
@@ -3,3 +3,5 @@ export { extractResponseContracts } from './contract-schema-extractor.mjs';
3
3
  export { extractContractSchema } from './contract-schema-type-extractor.mjs';
4
4
  export { validateResponseContract } from './contract-schema-validator.mjs';
5
5
  export { buildVirtualProgramMatrix, virtualProgramBuildCountForTest } from './virtual-program.mjs';
6
+ export { getExportedTypeFacts } from './type-query.mjs';
7
+ export { getCallRowTypeFacts } from './type-query-call.mjs';
@@ -0,0 +1,44 @@
1
+ import type ts from './typescript-api.mts';
2
+ type NodePredicate<Node extends ts.Node> = {
3
+ check(node: ts.Node): node is Node;
4
+ }['check'];
5
+ type ForEachChild = {
6
+ visit(node: ts.Node, callback: (child: ts.Node) => void): void;
7
+ }['visit'];
8
+ export interface TypeScriptApi {
9
+ readonly IndexKind: {
10
+ readonly Number: number;
11
+ readonly String: number;
12
+ };
13
+ readonly SymbolFlags: {
14
+ readonly Alias: number;
15
+ readonly Type: number;
16
+ readonly Value: number;
17
+ };
18
+ readonly SyntaxKind: {
19
+ readonly SourceFile: number;
20
+ };
21
+ readonly TypeFlags: {
22
+ readonly Any: number;
23
+ readonly Never: number;
24
+ };
25
+ readonly TypeFormatFlags: {
26
+ readonly NoTruncation: ts.TypeFormatFlags.NoTruncation;
27
+ };
28
+ readonly forEachChild: ForEachChild;
29
+ readonly isClassDeclaration: NodePredicate<ts.ClassDeclaration>;
30
+ readonly isCallExpression: NodePredicate<ts.CallExpression>;
31
+ readonly isFunctionDeclaration: NodePredicate<ts.FunctionDeclaration>;
32
+ readonly isInterfaceDeclaration: NodePredicate<ts.InterfaceDeclaration>;
33
+ readonly isIdentifier: NodePredicate<ts.Identifier>;
34
+ readonly isSourceFile: NodePredicate<ts.SourceFile>;
35
+ readonly isTypeReferenceNode: NodePredicate<ts.TypeReferenceNode>;
36
+ readonly isTypeAliasDeclaration: NodePredicate<ts.TypeAliasDeclaration>;
37
+ }
38
+ export interface TypeScriptProgram {
39
+ getSourceFile(fileName: string): unknown;
40
+ getTypeChecker(): unknown;
41
+ }
42
+ export declare function programForQuery(program: TypeScriptProgram): ts.Program;
43
+ export declare function validateTypeScriptApi(typescript: TypeScriptApi, program: TypeScriptProgram, sourceFile: ts.SourceFile): void;
44
+ export {};