vouchington-tooling 0.33.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 (172) hide show
  1. package/README.md +181 -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-context-callers.d.mts +2 -1
  13. package/dist/api-contract-discovery/protocol-http-context-callers.mjs +13 -7
  14. package/dist/api-contract-discovery/query-contract-extraction.mjs +20 -3
  15. package/dist/api-contract-discovery/query-contract-literals.d.mts +5 -0
  16. package/dist/api-contract-discovery/query-contract-literals.mjs +29 -0
  17. package/dist/api-contract-discovery/registered-route-binding-writes.d.mts +2 -1
  18. package/dist/api-contract-discovery/registered-route-binding-writes.mjs +20 -11
  19. package/dist/api-contract-discovery/registered-route-handler-analysis.d.mts +7 -1
  20. package/dist/api-contract-discovery/registered-route-handler-analysis.mjs +27 -26
  21. package/dist/api-contract-discovery/registered-route-handler-helpers.d.mts +3 -0
  22. package/dist/api-contract-discovery/registered-route-handler-helpers.mjs +16 -0
  23. package/dist/api-contract-discovery/request-validation-callbacks.d.mts +9 -0
  24. package/dist/api-contract-discovery/request-validation-callbacks.mjs +27 -0
  25. package/dist/api-contract-discovery/request-validation-checker.d.mts +7 -0
  26. package/dist/api-contract-discovery/request-validation-checker.mjs +25 -0
  27. package/dist/api-contract-discovery/request-validation-conditional.d.mts +6 -0
  28. package/dist/api-contract-discovery/request-validation-conditional.mjs +31 -0
  29. package/dist/api-contract-discovery/request-validation-factory-calls.d.mts +11 -0
  30. package/dist/api-contract-discovery/request-validation-factory-calls.mjs +23 -0
  31. package/dist/api-contract-discovery/request-validation-facts.d.mts +16 -0
  32. package/dist/api-contract-discovery/request-validation-facts.mjs +64 -0
  33. package/dist/api-contract-discovery/request-validation-follow.d.mts +24 -0
  34. package/dist/api-contract-discovery/request-validation-follow.mjs +38 -0
  35. package/dist/api-contract-discovery/request-validation-handler-state.d.mts +8 -0
  36. package/dist/api-contract-discovery/request-validation-handler-state.mjs +35 -0
  37. package/dist/api-contract-discovery/request-validation-input.d.mts +12 -0
  38. package/dist/api-contract-discovery/request-validation-input.mjs +43 -0
  39. package/dist/api-contract-discovery/request-validation-keys.d.mts +25 -0
  40. package/dist/api-contract-discovery/request-validation-keys.mjs +119 -0
  41. package/dist/api-contract-discovery/request-validation-match.d.mts +11 -0
  42. package/dist/api-contract-discovery/request-validation-match.mjs +47 -0
  43. package/dist/api-contract-discovery/request-validation-mutation.d.mts +7 -0
  44. package/dist/api-contract-discovery/request-validation-mutation.mjs +80 -0
  45. package/dist/api-contract-discovery/request-validation-origin.d.mts +20 -0
  46. package/dist/api-contract-discovery/request-validation-origin.mjs +71 -0
  47. package/dist/api-contract-discovery/request-validation-reads.d.mts +13 -0
  48. package/dist/api-contract-discovery/request-validation-reads.mjs +88 -0
  49. package/dist/api-contract-discovery/request-validation-registration.d.mts +9 -0
  50. package/dist/api-contract-discovery/request-validation-registration.mjs +32 -0
  51. package/dist/api-contract-discovery/request-validation-report.d.mts +3 -0
  52. package/dist/api-contract-discovery/request-validation-report.mjs +21 -0
  53. package/dist/api-contract-discovery/request-validation-request-origin.d.mts +5 -0
  54. package/dist/api-contract-discovery/request-validation-request-origin.mjs +13 -0
  55. package/dist/api-contract-discovery/request-validation-select.d.mts +9 -0
  56. package/dist/api-contract-discovery/request-validation-select.mjs +36 -0
  57. package/dist/api-contract-discovery/request-validation-sites.d.mts +9 -0
  58. package/dist/api-contract-discovery/request-validation-sites.mjs +77 -0
  59. package/dist/api-contract-discovery/request-validation-state-key.d.mts +13 -0
  60. package/dist/api-contract-discovery/request-validation-state-key.mjs +30 -0
  61. package/dist/api-contract-discovery/request-validation-trace-helpers.d.mts +14 -0
  62. package/dist/api-contract-discovery/request-validation-trace-helpers.mjs +80 -0
  63. package/dist/api-contract-discovery/request-validation-trace-select.d.mts +23 -0
  64. package/dist/api-contract-discovery/request-validation-trace-select.mjs +45 -0
  65. package/dist/api-contract-discovery/request-validation-trace.d.mts +8 -0
  66. package/dist/api-contract-discovery/request-validation-trace.mjs +130 -0
  67. package/dist/api-contract-discovery/request-validation-types.d.mts +84 -0
  68. package/dist/api-contract-discovery/request-validation-types.mjs +1 -0
  69. package/dist/api-contract-discovery/request-validation-walker.d.mts +26 -0
  70. package/dist/api-contract-discovery/request-validation-walker.mjs +143 -0
  71. package/dist/api-contract-discovery/request-validation.test-helpers.d.mts +39 -0
  72. package/dist/api-contract-discovery/request-validation.test-helpers.mjs +108 -0
  73. package/dist/api-contract-discovery/response-contract-attribution-facts.d.mts +2 -0
  74. package/dist/api-contract-discovery/response-contract-attribution-facts.mjs +18 -0
  75. package/dist/api-contract-discovery/response-contract-attribution.d.mts +3 -0
  76. package/dist/api-contract-discovery/response-contract-attribution.mjs +23 -0
  77. package/dist/api-contract-discovery/response-contract-call-classification.d.mts +3 -0
  78. package/dist/api-contract-discovery/response-contract-call-classification.mjs +37 -0
  79. package/dist/api-contract-discovery/response-contract-error-branch.d.mts +1 -1
  80. package/dist/api-contract-discovery/response-contract-error-branch.mjs +109 -6
  81. package/dist/api-contract-discovery/response-contract-handler-bindings.d.mts +3 -0
  82. package/dist/api-contract-discovery/response-contract-handler-bindings.mjs +126 -0
  83. package/dist/api-contract-discovery/response-contract-handler-calls.d.mts +3 -0
  84. package/dist/api-contract-discovery/response-contract-handler-calls.mjs +81 -0
  85. package/dist/api-contract-discovery/response-contract-implicit-variants.d.mts +2 -0
  86. package/dist/api-contract-discovery/response-contract-implicit-variants.mjs +6 -0
  87. package/dist/api-contract-discovery/response-contract-implicit.d.mts +2 -1
  88. package/dist/api-contract-discovery/response-contract-implicit.mjs +9 -17
  89. package/dist/api-contract-discovery/response-contract-lenient.d.mts +18 -0
  90. package/dist/api-contract-discovery/response-contract-object-symbols.d.mts +3 -0
  91. package/dist/api-contract-discovery/response-contract-object-symbols.mjs +80 -0
  92. package/dist/api-contract-discovery/response-contract-property-mutations.d.mts +5 -0
  93. package/dist/api-contract-discovery/response-contract-property-mutations.mjs +84 -0
  94. package/dist/api-contract-discovery/response-contract-registration.d.mts +1 -1
  95. package/dist/api-contract-discovery/response-contract-registration.mjs +7 -3
  96. package/dist/api-contract-discovery/response-contract-registry.mjs +43 -8
  97. package/dist/api-contract-discovery/response-contract-route-analysis.d.mts +3 -4
  98. package/dist/api-contract-discovery/response-contract-route-analysis.mjs +4 -101
  99. package/dist/api-contract-discovery/response-contract-route-syntax.d.mts +7 -0
  100. package/dist/api-contract-discovery/response-contract-route-syntax.mjs +41 -0
  101. package/dist/api-contract-discovery/response-contract-symbols.d.mts +5 -0
  102. package/dist/api-contract-discovery/response-contract-symbols.mjs +82 -0
  103. package/dist/compiler-build/index.d.mts +2 -0
  104. package/dist/compiler-build/index.mjs +1 -0
  105. package/dist/compiler-build/program-configuration.d.mts +9 -0
  106. package/dist/compiler-build/program-configuration.mjs +28 -0
  107. package/dist/compiler-build/program-types.d.mts +29 -0
  108. package/dist/compiler-build/program-types.mjs +1 -0
  109. package/dist/compiler-build/program.d.mts +7 -0
  110. package/dist/compiler-build/program.mjs +78 -0
  111. package/dist/contract-schema/index.d.mts +4 -0
  112. package/dist/contract-schema/index.mjs +2 -0
  113. package/dist/contract-schema/type-query-api.d.mts +44 -0
  114. package/dist/contract-schema/type-query-api.mjs +10 -0
  115. package/dist/contract-schema/type-query-call.d.mts +16 -0
  116. package/dist/contract-schema/type-query-call.mjs +52 -0
  117. package/dist/contract-schema/type-query-export.d.mts +10 -0
  118. package/dist/contract-schema/type-query-export.mjs +91 -0
  119. package/dist/contract-schema/type-query.d.mts +22 -0
  120. package/dist/contract-schema/type-query.mjs +39 -0
  121. package/dist/finite-enum-ripple/ast.d.mts +9 -0
  122. package/dist/finite-enum-ripple/ast.mjs +94 -0
  123. package/dist/finite-enum-ripple/check.d.mts +3 -0
  124. package/dist/finite-enum-ripple/check.mjs +62 -0
  125. package/dist/finite-enum-ripple/compare.d.mts +20 -0
  126. package/dist/finite-enum-ripple/compare.mjs +90 -0
  127. package/dist/finite-enum-ripple/component-paths.d.mts +8 -0
  128. package/dist/finite-enum-ripple/component-paths.mjs +30 -0
  129. package/dist/finite-enum-ripple/configured-object-mutations.d.mts +1 -0
  130. package/dist/finite-enum-ripple/configured-object-mutations.mjs +78 -0
  131. package/dist/finite-enum-ripple/create-page-jsx.d.mts +3 -0
  132. package/dist/finite-enum-ripple/create-page-jsx.mjs +28 -0
  133. package/dist/finite-enum-ripple/create-page-literals.d.mts +2 -0
  134. package/dist/finite-enum-ripple/create-page-literals.mjs +83 -0
  135. package/dist/finite-enum-ripple/create-page-object-visitor.d.mts +2 -0
  136. package/dist/finite-enum-ripple/create-page-object-visitor.mjs +57 -0
  137. package/dist/finite-enum-ripple/create-page-properties.d.mts +4 -0
  138. package/dist/finite-enum-ripple/create-page-properties.mjs +35 -0
  139. package/dist/finite-enum-ripple/index.d.mts +2 -0
  140. package/dist/finite-enum-ripple/index.mjs +1 -0
  141. package/dist/finite-enum-ripple/model.d.mts +74 -0
  142. package/dist/finite-enum-ripple/model.mjs +1 -0
  143. package/dist/finite-enum-ripple/parser-support.d.mts +11 -0
  144. package/dist/finite-enum-ripple/parser-support.mjs +86 -0
  145. package/dist/finite-enum-ripple/parsers.d.mts +32 -0
  146. package/dist/finite-enum-ripple/parsers.mjs +113 -0
  147. package/dist/finite-enum-ripple/pattern-captures.d.mts +2 -0
  148. package/dist/finite-enum-ripple/pattern-captures.mjs +45 -0
  149. package/dist/finite-enum-ripple/structured-route-check.d.mts +3 -0
  150. package/dist/finite-enum-ripple/structured-route-check.mjs +94 -0
  151. package/dist/finite-enum-ripple/types.d.mts +2 -0
  152. package/dist/finite-enum-ripple/types.mjs +60 -0
  153. package/dist/finite-enum-ripple/union-factory-check.d.mts +2 -0
  154. package/dist/finite-enum-ripple/union-factory-check.mjs +34 -0
  155. package/dist/finite-enum-ripple/union-types.d.mts +2 -0
  156. package/dist/finite-enum-ripple/union-types.mjs +129 -0
  157. package/dist/openapi-document/build-openapi-query.mjs +1 -1
  158. package/dist/openapi-document/openapi-types.d.mts +1 -1
  159. package/dist/openapi-document/query-types.d.mts +8 -2
  160. package/dist/post-publication-inventory/index.d.mts +5 -0
  161. package/dist/post-publication-inventory/index.mjs +2 -0
  162. package/dist/post-publication-inventory/readers/sql-executors.d.mts +3 -0
  163. package/dist/post-publication-inventory/readers/sql-executors.mjs +32 -0
  164. package/dist/post-publication-inventory/readers/sql-source.d.mts +7 -0
  165. package/dist/post-publication-inventory/readers/sql-source.mjs +87 -0
  166. package/dist/post-publication-inventory/readers/sql-template.d.mts +12 -0
  167. package/dist/post-publication-inventory/readers/sql-template.mjs +119 -0
  168. package/dist/post-publication-inventory/writers/source.d.mts +9 -0
  169. package/dist/post-publication-inventory/writers/source.mjs +128 -0
  170. package/dist/post-publication-inventory/writers/types.d.mts +19 -0
  171. package/dist/post-publication-inventory/writers/types.mjs +1 -0
  172. package/package.json +13 -2
@@ -0,0 +1,83 @@
1
+ import ts from '@typescript/typescript6';
2
+ import { getStringLiteralValue, unwrapExpression } from './ast.mjs';
3
+ import { jsxRuntimeStringValue } from './create-page-jsx.mjs';
4
+ import { getConfiguredPropertyName, isConfiguredPropertyName } from './create-page-properties.mjs';
5
+ import { visitCreatePageObject } from './create-page-object-visitor.mjs';
6
+ /** Inspect syntax nodes so comments and strings containing example code are ignored. */
7
+ export function collectCreatePageLiterals(content, file, properties) {
8
+ const scriptKind = file.endsWith('.tsx') || file.endsWith('.jsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS;
9
+ const source = ts.createSourceFile(file, content, ts.ScriptTarget.Latest, true, scriptKind);
10
+ const names = new Set(properties);
11
+ const values = [];
12
+ const assignedPropertyName = (target) => {
13
+ const left = unwrapExpression(target);
14
+ return ts.isIdentifier(left)
15
+ ? left.text
16
+ : ts.isPropertyAccessExpression(left)
17
+ ? left.name.text
18
+ : ts.isElementAccessExpression(left)
19
+ ? getStringLiteralValue(left.argumentExpression)
20
+ : undefined;
21
+ };
22
+ const visit = (node, ignoredProperties = new Set()) => {
23
+ if (ts.isObjectLiteralExpression(node) || ts.isJsxAttributes(node)) {
24
+ visitCreatePageObject(node, names, ignoredProperties, file, visit);
25
+ return;
26
+ }
27
+ if (ts.isPropertyAssignment(node)) {
28
+ const name = getConfiguredPropertyName(node, names);
29
+ if (isConfiguredPropertyName(name, names) && !ignoredProperties.has(name)) {
30
+ const value = getStringLiteralValue(node.initializer);
31
+ if (value === undefined)
32
+ throw new Error(`${file}: create page ${name} must be a string literal`);
33
+ values.push(value);
34
+ }
35
+ ts.forEachChild(node, (child) => visit(child, new Set()));
36
+ return;
37
+ }
38
+ else if (ts.isShorthandPropertyAssignment(node) && names.has(node.name.text)) {
39
+ throw new Error(`${file}: create page ${node.name.text} must be a string literal`);
40
+ }
41
+ else if (ts.isJsxAttribute(node) && ts.isIdentifier(node.name) && names.has(node.name.text)) {
42
+ const value = node.initializer && ts.isJsxExpression(node.initializer)
43
+ ? getStringLiteralValue(node.initializer.expression)
44
+ : node.initializer && ts.isStringLiteral(node.initializer)
45
+ ? jsxRuntimeStringValue(node.initializer, source, file)
46
+ : getStringLiteralValue(node.initializer);
47
+ if (value === undefined)
48
+ throw new Error(`${file}: create page ${node.name.text} must be a string literal`);
49
+ values.push(value);
50
+ }
51
+ else if ((ts.isPrefixUnaryExpression(node) || ts.isPostfixUnaryExpression(node)) &&
52
+ (node.operator === ts.SyntaxKind.PlusPlusToken ||
53
+ node.operator === ts.SyntaxKind.MinusMinusToken)) {
54
+ const name = assignedPropertyName(node.operand);
55
+ if (isConfiguredPropertyName(name, names))
56
+ throw new Error(`${file}: unary mutation of create page ${name} is uninspectable`);
57
+ }
58
+ else if (ts.isDeleteExpression(node)) {
59
+ const name = assignedPropertyName(node.expression);
60
+ if (isConfiguredPropertyName(name, names))
61
+ throw new Error(`${file}: deletion of create page ${name} is uninspectable`);
62
+ }
63
+ else if (ts.isBinaryExpression(node)) {
64
+ const name = assignedPropertyName(node.left);
65
+ if (node.operatorToken.kind >= ts.SyntaxKind.FirstCompoundAssignment &&
66
+ node.operatorToken.kind <= ts.SyntaxKind.LastCompoundAssignment &&
67
+ isConfiguredPropertyName(name, names))
68
+ throw new Error(`${file}: compound assignment to create page ${name} is uninspectable`);
69
+ if (node.operatorToken.kind !== ts.SyntaxKind.EqualsToken) {
70
+ ts.forEachChild(node, (child) => visit(child));
71
+ return;
72
+ }
73
+ const value = getStringLiteralValue(node.right);
74
+ if (isConfiguredPropertyName(name, names) && value === undefined)
75
+ throw new Error(`${file}: create page ${name} must be a string literal`);
76
+ if (isConfiguredPropertyName(name, names) && value !== undefined)
77
+ values.push(value);
78
+ }
79
+ ts.forEachChild(node, (child) => visit(child));
80
+ };
81
+ visit(source);
82
+ return values;
83
+ }
@@ -0,0 +1,2 @@
1
+ import ts from '@typescript/typescript6';
2
+ export declare function visitCreatePageObject(node: ts.ObjectLiteralExpression | ts.JsxAttributes, names: ReadonlySet<string>, ignoredProperties: ReadonlySet<string>, file: string, visit: (node: ts.Node, ignoredProperties?: ReadonlySet<string>) => void): void;
@@ -0,0 +1,57 @@
1
+ import ts from '@typescript/typescript6';
2
+ import { getStringLiteralValue, unwrapExpression } from './ast.mjs';
3
+ import { getConfiguredPropertyName, isConfiguredPropertyName, spreadMayOverrideConfiguredProperty, } from './create-page-properties.mjs';
4
+ export function visitCreatePageObject(node, names, ignoredProperties, file, visit) {
5
+ let configuredBeforeSpread = false;
6
+ for (const member of node.properties) {
7
+ if (ts.isSpreadAssignment(member) || ts.isJsxSpreadAttribute(member)) {
8
+ if (configuredBeforeSpread && spreadMayOverrideConfiguredProperty(member, names))
9
+ throw new Error(`${file}: create page type can be overridden by a trailing spread`);
10
+ }
11
+ else if (ts.isPropertyAssignment(member) &&
12
+ getConfiguredPropertyName(member, names) === '__proto__' &&
13
+ !ts.isComputedPropertyName(member.name)) {
14
+ throw new Error(`${file}: create page __proto__ prototype setter is uninspectable`);
15
+ }
16
+ else if (getConfiguredPropertyName(member, names) !== undefined) {
17
+ configuredBeforeSpread = true;
18
+ }
19
+ else if (ts.isPropertyAssignment(member) &&
20
+ ts.isComputedPropertyName(member.name) &&
21
+ getStringLiteralValue(member.name.expression) === undefined &&
22
+ configuredBeforeSpread) {
23
+ throw new Error(`${file}: create page type can be overridden by a trailing computed property`);
24
+ }
25
+ }
26
+ for (let index = 0; index < node.properties.length; index += 1) {
27
+ const member = node.properties[index];
28
+ if (ts.isSpreadAssignment(member) || ts.isJsxSpreadAttribute(member)) {
29
+ const overridden = new Set();
30
+ const expression = unwrapExpression(member.expression);
31
+ if (!ts.isObjectLiteralExpression(expression) &&
32
+ spreadMayOverrideConfiguredProperty(member, names)) {
33
+ for (const later of node.properties.slice(index + 1)) {
34
+ const name = getConfiguredPropertyName(later, names);
35
+ if (isConfiguredPropertyName(name, names))
36
+ overridden.add(name);
37
+ }
38
+ if ([...names].some((name) => !overridden.has(name)))
39
+ throw new Error(`${file}: create page type cannot be inspected through a leading spread`);
40
+ }
41
+ if (ts.isObjectLiteralExpression(expression)) {
42
+ for (const later of node.properties.slice(index + 1)) {
43
+ const name = getConfiguredPropertyName(later, names);
44
+ if (isConfiguredPropertyName(name, names))
45
+ overridden.add(name);
46
+ }
47
+ }
48
+ visit(expression, overridden);
49
+ }
50
+ else {
51
+ const name = getConfiguredPropertyName(member, names);
52
+ if (isConfiguredPropertyName(name, names) && ignoredProperties.has(name))
53
+ continue;
54
+ visit(member, ignoredProperties);
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,4 @@
1
+ import ts from '@typescript/typescript6';
2
+ export declare function getConfiguredPropertyName(node: ts.Node, names: ReadonlySet<string>): string | undefined;
3
+ export declare function isConfiguredPropertyName(name: string | undefined, names: ReadonlySet<string>): name is string;
4
+ export declare function spreadMayOverrideConfiguredProperty(member: ts.SpreadAssignment | ts.JsxSpreadAttribute, names: ReadonlySet<string>): boolean;
@@ -0,0 +1,35 @@
1
+ import ts from '@typescript/typescript6';
2
+ import { getPropertyNameText, getStringLiteralValue, unwrapExpression } from './ast.mjs';
3
+ export function getConfiguredPropertyName(node, names) {
4
+ let name;
5
+ if (ts.isPropertyAssignment(node)) {
6
+ name = getPropertyNameText(node.name);
7
+ if (name === undefined && ts.isComputedPropertyName(node.name))
8
+ name = getStringLiteralValue(node.name.expression);
9
+ }
10
+ else if (ts.isShorthandPropertyAssignment(node))
11
+ name = node.name.text;
12
+ else if (ts.isJsxAttribute(node) && ts.isIdentifier(node.name))
13
+ name = node.name.text;
14
+ return name !== undefined && names.has(name) ? name : undefined;
15
+ }
16
+ export function isConfiguredPropertyName(name, names) {
17
+ return name !== undefined && names.has(name);
18
+ }
19
+ export function spreadMayOverrideConfiguredProperty(member, names) {
20
+ return expressionMayOverride(unwrapExpression(member.expression), names);
21
+ }
22
+ function expressionMayOverride(expression, names) {
23
+ if (!ts.isObjectLiteralExpression(expression))
24
+ return true;
25
+ return expression.properties.some((property) => {
26
+ if (ts.isSpreadAssignment(property))
27
+ return expressionMayOverride(unwrapExpression(property.expression), names);
28
+ if (ts.isComputedPropertyName(property.name)) {
29
+ const name = getStringLiteralValue(property.name.expression);
30
+ return name === undefined || names.has(name);
31
+ }
32
+ const name = getPropertyNameText(property.name);
33
+ return name !== undefined && names.has(name);
34
+ });
35
+ }
@@ -0,0 +1,2 @@
1
+ export { checkFiniteEnumRipple } from './check.mts';
2
+ export type { FiniteEnumFiles, FiniteEnumRippleConfig } from './model.mts';
@@ -0,0 +1 @@
1
+ export { checkFiniteEnumRipple } from './check.mjs';
@@ -0,0 +1,74 @@
1
+ export type ReadTrackedFile = (file: string) => string;
2
+ export interface RoutedPage {
3
+ file: string;
4
+ isTopLevel: boolean;
5
+ slug: string;
6
+ }
7
+ export interface FiniteEnumFiles {
8
+ existingFileSet: ReadonlySet<string>;
9
+ unionCollectionPages: RoutedPage[];
10
+ unionCreatePages: RoutedPage[];
11
+ unionDetailPages: RoutedPage[];
12
+ structuredCollectionPages: RoutedPage[];
13
+ structuredComponentFiles: {
14
+ file: string;
15
+ slug: string;
16
+ }[];
17
+ structuredDetailPages: RoutedPage[];
18
+ }
19
+ /** Selection and diagnostics are supplied by the consumer, never inferred from its layout. */
20
+ export interface FiniteEnumRippleConfig {
21
+ files: FiniteEnumFiles;
22
+ /** Appended to every diagnostic; use for a consumer-owned policy guide. */
23
+ diagnosticSuffix?: string;
24
+ structured?: {
25
+ backendPath: string;
26
+ webPath: string;
27
+ routeConfigsPath: string;
28
+ typeObject: string;
29
+ routeConfigObject: string;
30
+ typeArrayProperty: string;
31
+ pluralPathProperty: string;
32
+ singularPathProperty: string;
33
+ routeExemptionProperty: string;
34
+ slugProperty: string;
35
+ slugPluralProperty: string;
36
+ factoryCallPattern: RegExp;
37
+ routeConfigExceptions: readonly string[];
38
+ /** Declared values intentionally without a collection route. */
39
+ collectionRouteExclusions: readonly string[];
40
+ routeLabels: {
41
+ detailTop: string;
42
+ detail: string;
43
+ collectionTop: string;
44
+ collection: string;
45
+ };
46
+ collectionLabel: string;
47
+ ignoredNavigationPaths: readonly string[];
48
+ collectionPathLiteralPattern: RegExp;
49
+ navigationPathLiteralPattern: RegExp;
50
+ };
51
+ union?: {
52
+ typesPath: string;
53
+ routeConfigsPath: string;
54
+ typeAlias: string;
55
+ slugMapObject: string;
56
+ routeConfigObject: string;
57
+ typeArrayProperty: string;
58
+ pluralPathProperty: string;
59
+ singularPathProperty: string;
60
+ factoryCallPattern: RegExp;
61
+ internalTypes: readonly string[];
62
+ routeConfigExceptions: readonly string[];
63
+ routeLabels: {
64
+ detailTop: string;
65
+ detail: string;
66
+ collectionTop: string;
67
+ collection: string;
68
+ factory: string;
69
+ };
70
+ collectionLabel: string;
71
+ createPageTypeProperties: readonly string[];
72
+ collectionPathLiteralPattern: RegExp;
73
+ };
74
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ import ts from '@typescript/typescript6';
2
+ export declare function parseRouteConfigEntries(content: string, name: string, file: string, pluralPathProperty: string, singularPathProperty: string): {
3
+ key: string;
4
+ body: ts.ObjectLiteralExpression;
5
+ pluralPath: string;
6
+ singularPath: string;
7
+ }[];
8
+ export declare function parseStringArray(content: ts.ObjectLiteralExpression, property: string, file: string): string[];
9
+ export declare function collectStringLiteralsFromType(type: ts.TypeNode): string[];
10
+ export declare function getStringProperty(object: ts.ObjectLiteralExpression, property: string): string | undefined;
11
+ export declare function hasTrueProperty(object: ts.ObjectLiteralExpression, property: string): boolean;
@@ -0,0 +1,86 @@
1
+ import ts from '@typescript/typescript6';
2
+ import { findConstObjectLiteral, getPropertyNameText, getStringLiteralValue, unwrapExpression, } from './ast.mjs';
3
+ import { hasPostDeclarationConfiguredObjectMutation } from './configured-object-mutations.mjs';
4
+ export function parseRouteConfigEntries(content, name, file, pluralPathProperty, singularPathProperty) {
5
+ const object = findConstObjectLiteral(content, name, file);
6
+ if (hasPostDeclarationConfiguredObjectMutation(content, file, name, object.end))
7
+ throw new Error(`${file}: ${name} has a post-declaration property mutation`);
8
+ const entries = [];
9
+ for (const property of object.properties) {
10
+ if (!ts.isPropertyAssignment(property))
11
+ throw new Error(`${file}: ${name} contains an uninspectable route member`);
12
+ const key = ts.isComputedPropertyName(property.name)
13
+ ? getStringLiteralValue(property.name.expression)
14
+ : getPropertyNameText(property.name);
15
+ if (!key)
16
+ throw new Error(`${file}: ${name} contains an uninspectable route key`);
17
+ if (key === '__proto__' && !ts.isComputedPropertyName(property.name))
18
+ throw new Error(`${file}: ${name} contains unsupported __proto__ prototype setter`);
19
+ const body = unwrapExpression(property.initializer);
20
+ if (!ts.isObjectLiteralExpression(body))
21
+ throw new Error(`${file}: ${name}.${key} must be an object literal`);
22
+ const names = new Set();
23
+ for (const member of body.properties) {
24
+ if (!ts.isPropertyAssignment(member))
25
+ throw new Error(`${file}: ${name}.${key} contains an uninspectable member`);
26
+ const memberName = getPropertyNameText(member.name);
27
+ if (!memberName || names.has(memberName))
28
+ throw new Error(`${file}: ${name}.${key} contains an uninspectable or duplicate key`);
29
+ names.add(memberName);
30
+ }
31
+ const pluralPath = getStringProperty(body, pluralPathProperty);
32
+ const singularPath = getStringProperty(body, singularPathProperty);
33
+ if (!pluralPath)
34
+ throw new Error(`${file}: ${name}.${key} is missing ${pluralPathProperty}`);
35
+ if (!singularPath)
36
+ throw new Error(`${file}: ${name}.${key} is missing ${singularPathProperty}`);
37
+ entries.push({ key, body, pluralPath, singularPath });
38
+ }
39
+ if (entries.length === 0)
40
+ throw new Error(`${file}: could not parse ${name} entries`);
41
+ return entries;
42
+ }
43
+ export function parseStringArray(content, property, file) {
44
+ const array = getPropertyValue(content, property);
45
+ if (!array)
46
+ return [];
47
+ if (!ts.isArrayLiteralExpression(array))
48
+ throw new Error(`${file}: ${property} must be a literal string array`);
49
+ return array.elements.map((element) => {
50
+ const value = getStringLiteralValue(element);
51
+ if (value === undefined)
52
+ throw new Error(`${file}: ${property} contains a non-string literal element`);
53
+ return value;
54
+ });
55
+ }
56
+ export function collectStringLiteralsFromType(type) {
57
+ const node = unwrapTypeNode(type);
58
+ if (ts.isUnionTypeNode(node))
59
+ return node.types.flatMap((item) => collectStringLiteralsFromType(item));
60
+ const value = ts.isLiteralTypeNode(node) ? getStringLiteralValue(node.literal) : undefined;
61
+ if (value === undefined)
62
+ throw new Error('union contains a non-string literal constituent');
63
+ return [value];
64
+ }
65
+ function getPropertyValue(object, property) {
66
+ for (const member of object.properties) {
67
+ if (!ts.isPropertyAssignment(member))
68
+ continue;
69
+ const key = getPropertyNameText(member.name);
70
+ if (key === property)
71
+ return unwrapExpression(member.initializer);
72
+ }
73
+ return undefined;
74
+ }
75
+ export function getStringProperty(object, property) {
76
+ return getStringLiteralValue(getPropertyValue(object, property));
77
+ }
78
+ export function hasTrueProperty(object, property) {
79
+ return getPropertyValue(object, property)?.kind === ts.SyntaxKind.TrueKeyword;
80
+ }
81
+ function unwrapTypeNode(type) {
82
+ let current = type;
83
+ while (ts.isParenthesizedTypeNode(current))
84
+ current = current.type;
85
+ return current;
86
+ }
@@ -0,0 +1,32 @@
1
+ export interface StructuredTypeEntry {
2
+ value: string;
3
+ slug: string;
4
+ slugPlural: string;
5
+ }
6
+ export interface UnionRouteConfigEntry {
7
+ key: string;
8
+ unionTypes: string[];
9
+ pluralPath: string;
10
+ singularPath: string;
11
+ }
12
+ export interface StructuredRouteConfigEntry {
13
+ key: string;
14
+ structuredTypes: string[];
15
+ pluralPath: string;
16
+ singularPath: string;
17
+ routeExempt: boolean;
18
+ }
19
+ export interface UnionDetailRouteFactoryArgs {
20
+ unionType: string;
21
+ slug: string;
22
+ }
23
+ export interface StructuredRouteFactoryArgs {
24
+ slug: string;
25
+ }
26
+ export declare function parseStructuredTypeEntries(content: string, file: string, name: string, slugProperty: string, slugPluralProperty: string): StructuredTypeEntry[];
27
+ export declare function parseUnionSlugToType(content: string, file: string, name: string): Map<string, string>;
28
+ export declare function parseUnionTypeUnion(content: string, file: string, name: string): string[];
29
+ export declare function parseUnionRouteConfigEntries(content: string, file: string, name: string, typeProperty: string, pluralPathProperty: string, singularPathProperty: string): UnionRouteConfigEntry[];
30
+ export declare function parseStructuredRouteConfigEntries(content: string, file: string, name: string, typeProperty: string, exemptionProperty: string, pluralPathProperty: string, singularPathProperty: string): StructuredRouteConfigEntry[];
31
+ export declare function parseUnionDetailRouteFactoryArgs(content: string, file: string, callPattern: RegExp): UnionDetailRouteFactoryArgs[];
32
+ export declare function parseStructuredRouteFactoryArgs(content: string, file: string, callPattern: RegExp): StructuredRouteFactoryArgs[];
@@ -0,0 +1,113 @@
1
+ import ts from '@typescript/typescript6';
2
+ import { collectCallExpressions, findConstObjectLiteral, findTypeAliasDeclaration, getCallExpressionName, getPropertyNameText, getStringLiteralValue, unwrapExpression, } from './ast.mjs';
3
+ import { collectStringLiteralsFromType, getStringProperty, hasTrueProperty, parseRouteConfigEntries, parseStringArray, } from './parser-support.mjs';
4
+ import { hasPostDeclarationConfiguredObjectMutation } from './configured-object-mutations.mjs';
5
+ export function parseStructuredTypeEntries(content, file, name, slugProperty, slugPluralProperty) {
6
+ const object = findConstObjectLiteral(content, name, file);
7
+ if (hasPostDeclarationConfiguredObjectMutation(content, file, name, object.end))
8
+ throw new Error(`${file}: ${name} has a post-declaration property mutation`);
9
+ const entries = [];
10
+ for (const property of object.properties) {
11
+ if (ts.isSpreadAssignment(property))
12
+ throw new Error(`${file}: ${name} contains an uninspectable spread`);
13
+ if (ts.isShorthandPropertyAssignment(property))
14
+ throw new Error(`${file}: ${name}.${property.name.text} must be an object literal`);
15
+ if (!ts.isPropertyAssignment(property))
16
+ continue;
17
+ const value = getPropertyNameText(property.name);
18
+ if (value === '__proto__' && !ts.isComputedPropertyName(property.name))
19
+ throw new Error(`${file}: ${name} contains unsupported __proto__ prototype setter`);
20
+ if (value === undefined)
21
+ continue;
22
+ const body = unwrapExpression(property.initializer);
23
+ if (!ts.isObjectLiteralExpression(body))
24
+ throw new Error(`${file}: ${name}.${value} must be an object literal`);
25
+ const names = new Set();
26
+ for (const member of body.properties) {
27
+ if (!ts.isPropertyAssignment(member))
28
+ throw new Error(`${file}: ${name}.${value} contains an uninspectable member`);
29
+ const memberName = getPropertyNameText(member.name);
30
+ if (!memberName || names.has(memberName))
31
+ throw new Error(`${file}: ${name}.${value} contains an uninspectable or duplicate key`);
32
+ names.add(memberName);
33
+ }
34
+ const slug = getStringProperty(body, slugProperty);
35
+ const slugPlural = getStringProperty(body, slugPluralProperty);
36
+ if (!slug)
37
+ throw new Error(`${file}: ${name}.${value} is missing ${slugProperty}`);
38
+ if (!slugPlural)
39
+ throw new Error(`${file}: ${name}.${value} is missing ${slugPluralProperty}`);
40
+ entries.push({ value, slug, slugPlural });
41
+ }
42
+ if (entries.length === 0)
43
+ throw new Error(`${file}: could not parse ${name} entries`);
44
+ return entries;
45
+ }
46
+ export function parseUnionSlugToType(content, file, name) {
47
+ const object = findConstObjectLiteral(content, name, file);
48
+ if (hasPostDeclarationConfiguredObjectMutation(content, file, name, object.end))
49
+ throw new Error(`${file}: ${name} has a post-declaration property mutation`);
50
+ const entries = new Map();
51
+ for (const property of object.properties) {
52
+ if (!ts.isPropertyAssignment(property))
53
+ throw new Error(`${file}: ${name} contains an uninspectable member`);
54
+ const key = getPropertyNameText(property.name);
55
+ if (!key)
56
+ throw new Error(`${file}: ${name} contains an uninspectable key`);
57
+ if (key === '__proto__' && !ts.isComputedPropertyName(property.name))
58
+ throw new Error(`${file}: ${name} contains unsupported __proto__ prototype setter`);
59
+ const value = getStringLiteralValue(property.initializer);
60
+ if (value === undefined)
61
+ throw new Error(`${file}: ${name}.${key} must be a string literal`);
62
+ entries.set(key, value);
63
+ }
64
+ if (entries.size === 0)
65
+ throw new Error(`${file}: could not parse ${name} entries`);
66
+ return entries;
67
+ }
68
+ export function parseUnionTypeUnion(content, file, name) {
69
+ const typeAlias = findTypeAliasDeclaration(content, name, file);
70
+ return collectStringLiteralsFromType(typeAlias.type);
71
+ }
72
+ export function parseUnionRouteConfigEntries(content, file, name, typeProperty, pluralPathProperty, singularPathProperty) {
73
+ return parseRouteConfigEntries(content, name, file, pluralPathProperty, singularPathProperty).map((entry) => ({
74
+ ...entry,
75
+ unionTypes: parseStringArray(entry.body, typeProperty, file),
76
+ }));
77
+ }
78
+ export function parseStructuredRouteConfigEntries(content, file, name, typeProperty, exemptionProperty, pluralPathProperty, singularPathProperty) {
79
+ return parseRouteConfigEntries(content, name, file, pluralPathProperty, singularPathProperty).map((entry) => ({
80
+ ...entry,
81
+ structuredTypes: parseStringArray(entry.body, typeProperty, file),
82
+ routeExempt: hasTrueProperty(entry.body, exemptionProperty),
83
+ }));
84
+ }
85
+ export function parseUnionDetailRouteFactoryArgs(content, file, callPattern) {
86
+ const args = [];
87
+ const matcher = new RegExp(callPattern.source, callPattern.flags);
88
+ for (const call of collectCallExpressions(content, file)) {
89
+ matcher.lastIndex = 0;
90
+ if (!matcher.test(getCallExpressionName(call.expression) ?? ''))
91
+ continue;
92
+ const unionType = getStringLiteralValue(call.arguments[0]);
93
+ const slug = getStringLiteralValue(call.arguments[1]);
94
+ if (unionType === undefined || slug === undefined)
95
+ throw new Error(`${file}: configured route factory call needs literal type and slug`);
96
+ args.push({ unionType, slug });
97
+ }
98
+ return args;
99
+ }
100
+ export function parseStructuredRouteFactoryArgs(content, file, callPattern) {
101
+ const args = [];
102
+ const matcher = new RegExp(callPattern.source, callPattern.flags);
103
+ for (const call of collectCallExpressions(content, file)) {
104
+ matcher.lastIndex = 0;
105
+ if (!matcher.test(getCallExpressionName(call.expression) ?? ''))
106
+ continue;
107
+ const slug = getStringLiteralValue(call.arguments[0]);
108
+ if (!slug)
109
+ throw new Error(`${file}: configured route factory call needs a string literal slug`);
110
+ args.push({ slug });
111
+ }
112
+ return args;
113
+ }
@@ -0,0 +1,2 @@
1
+ /** Keep caller-supplied patterns while ignoring matches that begin in inactive source text. */
2
+ export declare function collectActivePatternCaptures(content: string, pattern: RegExp, file: string): string[];
@@ -0,0 +1,45 @@
1
+ import ts from '@typescript/typescript6';
2
+ /** Keep caller-supplied patterns while ignoring matches that begin in inactive source text. */
3
+ export function collectActivePatternCaptures(content, pattern, file) {
4
+ const astInactive = [];
5
+ const astLiterals = [];
6
+ const commentRanges = new Map();
7
+ const source = ts.createSourceFile(file, content, ts.ScriptTarget.Latest, true, file.endsWith('.tsx') || file.endsWith('.jsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
8
+ const collectAstInactive = (node) => {
9
+ if (ts.isJsxText(node) || ts.isRegularExpressionLiteral(node))
10
+ astInactive.push({ start: node.getStart(source), end: node.end });
11
+ else if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node))
12
+ astLiterals.push({ start: node.getStart(source), end: node.end });
13
+ else if (node.kind === ts.SyntaxKind.TemplateHead ||
14
+ node.kind === ts.SyntaxKind.TemplateMiddle ||
15
+ node.kind === ts.SyntaxKind.TemplateTail)
16
+ astInactive.push({ start: node.getStart(source), end: node.end });
17
+ for (const range of [
18
+ ...(ts.getLeadingCommentRanges(content, node.getFullStart()) ?? []),
19
+ ...(ts.getTrailingCommentRanges(content, node.end) ?? []),
20
+ ])
21
+ commentRanges.set(`${range.pos}:${range.end}`, { start: range.pos, end: range.end });
22
+ ts.forEachChild(node, collectAstInactive);
23
+ };
24
+ collectAstInactive(source);
25
+ const inactive = [
26
+ ...[...commentRanges.values()]
27
+ .filter(({ start, end }) => !astInactive.some((span) => start < span.end && end > span.start))
28
+ .map(({ start, end }) => ({ start, end, literal: false })),
29
+ ...astInactive.map(({ start, end }) => ({ start, end, literal: false })),
30
+ ...astLiterals.map(({ start, end }) => ({ start, end, literal: true })),
31
+ ];
32
+ const flags = pattern.global ? pattern.flags : `${pattern.flags}g`;
33
+ const globalPattern = new RegExp(pattern.source, flags);
34
+ return [...content.matchAll(globalPattern)].flatMap((match) => {
35
+ if (inactive.some(({ start, end, literal }) => match.index >= start &&
36
+ match.index < end &&
37
+ !(literal && match.index === start) &&
38
+ !(literal &&
39
+ match.index === start + 1 &&
40
+ content[match.index] === '/' &&
41
+ match.index + match[0].length === end - 1)))
42
+ return [];
43
+ return match[1] === undefined ? [] : [match[1]];
44
+ });
45
+ }
@@ -0,0 +1,3 @@
1
+ import type { FiniteEnumFiles, FiniteEnumRippleConfig, ReadTrackedFile } from './model.mts';
2
+ import type { StructuredTypeEntry } from './parsers.mts';
3
+ export declare function checkStructuredRouteConfigs(errors: string[], files: FiniteEnumFiles, readTracked: ReadTrackedFile, config: NonNullable<FiniteEnumRippleConfig['structured']>, backendEntries: StructuredTypeEntry[], backendSlugByValue: Map<string, string>): void;