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
@@ -0,0 +1,94 @@
1
+ import { hasConfiguredObjectDeclaration } from './ast.mjs';
2
+ import { parseStructuredRouteConfigEntries } from './parsers.mjs';
3
+ import { checkCollectionPagePathLiterals, compareSets, finiteEnumError, hasAllFiles, routePageSlugs, uniqueSorted, } from './compare.mjs';
4
+ import { checkStructuredCollectionComponentPathLiterals } from './component-paths.mjs';
5
+ export function checkStructuredRouteConfigs(errors, files, readTracked, config, backendEntries, backendSlugByValue) {
6
+ const routeConfigsPath = config.routeConfigsPath;
7
+ const backendStructuredsPath = config.backendPath;
8
+ if (hasAllFiles(files, [routeConfigsPath])) {
9
+ const routeConfigContent = readTracked(routeConfigsPath);
10
+ if (hasConfiguredObjectDeclaration(routeConfigContent, config.routeConfigObject, routeConfigsPath)) {
11
+ const routeConfigs = parseStructuredRouteConfigEntries(routeConfigContent, routeConfigsPath, config.routeConfigObject, config.typeArrayProperty, config.routeExemptionProperty, config.pluralPathProperty, config.singularPathProperty);
12
+ const slugPluralByValue = new Map(backendEntries.map((entry) => [entry.value, entry.slugPlural]));
13
+ const valueBySlug = new Map(backendEntries.map((entry) => [entry.slug, entry.value]));
14
+ const typedRouteConfigs = routeConfigs.map((routeConfig) => ({
15
+ ...routeConfig,
16
+ inferredStructuredType: valueBySlug.get(routeConfig.singularPath),
17
+ }));
18
+ for (const routeConfig of typedRouteConfigs) {
19
+ if (routeConfig.structuredTypes.length > 0 ||
20
+ routeConfig.routeExempt ||
21
+ routeConfig.inferredStructuredType === undefined ||
22
+ config.routeConfigExceptions.includes(routeConfig.key)) {
23
+ continue;
24
+ }
25
+ errors.push(finiteEnumError(routeConfigsPath, `${config.routeConfigObject}.${routeConfig.key} has ${config.singularPathProperty} "${routeConfig.singularPath}" but does not declare ${config.typeArrayProperty}`));
26
+ }
27
+ const enumRouteConfigs = typedRouteConfigs.filter((routeConfig) => routeConfig.structuredTypes.length > 0);
28
+ for (const routeConfig of routeConfigs) {
29
+ if (routeConfig.key === routeConfig.pluralPath)
30
+ continue;
31
+ errors.push(finiteEnumError(routeConfigsPath, `${config.routeConfigObject}.${routeConfig.key} has ${config.pluralPathProperty} "${routeConfig.pluralPath}" but the route config key is "${routeConfig.key}"`));
32
+ }
33
+ for (const routeConfig of enumRouteConfigs) {
34
+ const structuredType = routeConfig.structuredTypes[0];
35
+ const expectedSlug = backendSlugByValue.get(structuredType);
36
+ const expectedSlugPlural = slugPluralByValue.get(structuredType);
37
+ if (routeConfig.structuredTypes.length === 1 &&
38
+ routeConfig.singularPath === expectedSlug &&
39
+ routeConfig.pluralPath === expectedSlugPlural) {
40
+ continue;
41
+ }
42
+ errors.push(finiteEnumError(routeConfigsPath, `${config.routeConfigObject}.${routeConfig.key} maps ${config.singularPathProperty} "${routeConfig.singularPath}" and ${config.pluralPathProperty} "${routeConfig.pluralPath}" to [${routeConfig.structuredTypes.join(', ')}] but ${config.typeObject} expects "${expectedSlug ?? 'missing'}" and "${expectedSlugPlural ?? 'missing'}"`));
43
+ }
44
+ compareSets(errors, {
45
+ label: `${config.collectionLabel} route config values`,
46
+ actualLabel: `${routeConfigsPath} ${config.routeConfigObject}`,
47
+ actualFile: routeConfigsPath,
48
+ actualValues: typedRouteConfigs.flatMap((routeConfig) => routeConfig.structuredTypes.length > 0
49
+ ? routeConfig.structuredTypes
50
+ : routeConfig.inferredStructuredType !== undefined &&
51
+ (routeConfig.routeExempt || config.routeConfigExceptions.includes(routeConfig.key))
52
+ ? [routeConfig.inferredStructuredType]
53
+ : []),
54
+ expectedLabel: backendStructuredsPath,
55
+ expectedFile: backendStructuredsPath,
56
+ expectedValues: backendEntries
57
+ .filter((entry) => !config.collectionRouteExclusions.includes(entry.value))
58
+ .map((entry) => entry.value),
59
+ });
60
+ compareSets(errors, {
61
+ label: `${config.collectionLabel} collection route config paths`,
62
+ actualLabel: `${routeConfigsPath} ${config.routeConfigObject}`,
63
+ actualFile: routeConfigsPath,
64
+ actualValues: enumRouteConfigs.map((routeConfig) => routeConfig.pluralPath),
65
+ expectedLabel: `${backendStructuredsPath} ${config.typeObject} ${config.slugPluralProperty}`,
66
+ expectedFile: backendStructuredsPath,
67
+ expectedValues: enumRouteConfigs.flatMap((routeConfig) => {
68
+ const slugPlural = slugPluralByValue.get(routeConfig.structuredTypes[0]);
69
+ return slugPlural ? [slugPlural] : [];
70
+ }),
71
+ });
72
+ compareSets(errors, {
73
+ label: `${config.collectionLabel} top-level collection route directories`,
74
+ actualLabel: config.routeLabels.collectionTop,
75
+ actualFile: files.structuredCollectionPages[0]?.file ?? routeConfigsPath,
76
+ actualValues: routePageSlugs(files.structuredCollectionPages, true),
77
+ expectedLabel: `${routeConfigsPath} ${config.routeConfigObject} ${config.pluralPathProperty}`,
78
+ expectedFile: routeConfigsPath,
79
+ expectedValues: routeConfigs.map((routeConfig) => routeConfig.pluralPath),
80
+ });
81
+ compareSets(errors, {
82
+ label: `${config.collectionLabel} collection routed pages`,
83
+ actualLabel: config.routeLabels.collection,
84
+ actualFile: files.structuredCollectionPages[0]?.file ?? routeConfigsPath,
85
+ actualValues: uniqueSorted(files.structuredCollectionPages.map((page) => page.slug)),
86
+ expectedLabel: `${routeConfigsPath} ${config.routeConfigObject} ${config.pluralPathProperty}`,
87
+ expectedFile: routeConfigsPath,
88
+ expectedValues: routeConfigs.map((routeConfig) => routeConfig.pluralPath),
89
+ });
90
+ checkCollectionPagePathLiterals(files.structuredCollectionPages, errors, config.collectionLabel, new Set(routeConfigs.map((routeConfig) => routeConfig.pluralPath)), readTracked, config.collectionPathLiteralPattern);
91
+ checkStructuredCollectionComponentPathLiterals(errors, routeConfigs, files.structuredComponentFiles, new Set(config.ignoredNavigationPaths), readTracked, config.collectionLabel, config.navigationPathLiteralPattern);
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,2 @@
1
+ import type { FiniteEnumFiles, FiniteEnumRippleConfig, ReadTrackedFile } from './model.mts';
2
+ export declare function checkStructuredTypes(errors: string[], files: FiniteEnumFiles, readTracked: ReadTrackedFile, config: NonNullable<FiniteEnumRippleConfig['structured']>): void;
@@ -0,0 +1,60 @@
1
+ import { parseStructuredRouteFactoryArgs, parseStructuredTypeEntries } from './parsers.mjs';
2
+ import { compareSets, finiteEnumError, hasAllFiles, routePageSlugs } from './compare.mjs';
3
+ import { checkStructuredRouteConfigs } from './structured-route-check.mjs';
4
+ export function checkStructuredTypes(errors, files, readTracked, config) {
5
+ const backendStructuredsPath = config.backendPath;
6
+ const webStructuredsPath = config.webPath;
7
+ if (!hasAllFiles(files, [backendStructuredsPath, webStructuredsPath]))
8
+ return;
9
+ const backendEntries = parseStructuredTypeEntries(readTracked(backendStructuredsPath), backendStructuredsPath, config.typeObject, config.slugProperty, config.slugPluralProperty);
10
+ const webEntries = parseStructuredTypeEntries(readTracked(webStructuredsPath), webStructuredsPath, config.typeObject, config.slugProperty, config.slugPluralProperty);
11
+ const backendSlugByValue = new Map(backendEntries.map((entry) => [entry.value, entry.slug]));
12
+ const backendSlugPluralByValue = new Map(backendEntries.map((entry) => [entry.value, entry.slugPlural]));
13
+ compareSets(errors, {
14
+ label: `${config.collectionLabel} values`,
15
+ actualLabel: `${webStructuredsPath} ${config.typeObject}`,
16
+ actualFile: webStructuredsPath,
17
+ actualValues: webEntries.map((entry) => entry.value),
18
+ expectedLabel: `${backendStructuredsPath} ${config.typeObject}`,
19
+ expectedFile: backendStructuredsPath,
20
+ expectedValues: backendEntries.map((entry) => entry.value),
21
+ });
22
+ for (const webEntry of webEntries) {
23
+ const backendSlug = backendSlugByValue.get(webEntry.value);
24
+ if (backendSlug && backendSlug !== webEntry.slug) {
25
+ errors.push(finiteEnumError(webStructuredsPath, `${config.typeObject}.${webEntry.value}.${config.slugProperty} is "${webEntry.slug}" but ${backendStructuredsPath} uses "${backendSlug}"`));
26
+ }
27
+ const backendSlugPlural = backendSlugPluralByValue.get(webEntry.value);
28
+ if (backendSlugPlural && backendSlugPlural !== webEntry.slugPlural) {
29
+ errors.push(finiteEnumError(webStructuredsPath, `${config.typeObject}.${webEntry.value}.${config.slugPluralProperty} is "${webEntry.slugPlural}" but ${backendStructuredsPath} uses "${backendSlugPlural}"`));
30
+ }
31
+ }
32
+ const topRouteSlugs = routePageSlugs(files.structuredDetailPages, true);
33
+ const routeSlugs = routePageSlugs(files.structuredDetailPages);
34
+ compareSets(errors, {
35
+ label: `${config.collectionLabel} route directories`,
36
+ actualLabel: config.routeLabels.detailTop,
37
+ actualFile: files.structuredDetailPages[0]?.file ?? backendStructuredsPath,
38
+ actualValues: topRouteSlugs,
39
+ expectedLabel: `${backendStructuredsPath} ${config.typeObject} slugs`,
40
+ expectedFile: backendStructuredsPath,
41
+ expectedValues: backendEntries.map((entry) => entry.slug),
42
+ });
43
+ compareSets(errors, {
44
+ label: `${config.collectionLabel} routed pages`,
45
+ actualLabel: config.routeLabels.detail,
46
+ actualFile: files.structuredDetailPages[0]?.file ?? backendStructuredsPath,
47
+ actualValues: routeSlugs,
48
+ expectedLabel: `${backendStructuredsPath} ${config.typeObject} slugs`,
49
+ expectedFile: backendStructuredsPath,
50
+ expectedValues: backendEntries.map((entry) => entry.slug),
51
+ });
52
+ checkStructuredRouteConfigs(errors, files, readTracked, config, backendEntries, backendSlugByValue);
53
+ for (const page of files.structuredDetailPages) {
54
+ for (const args of parseStructuredRouteFactoryArgs(readTracked(page.file), page.file, config.factoryCallPattern)) {
55
+ if (page.slug === args.slug)
56
+ continue;
57
+ errors.push(finiteEnumError(page.file, `${config.collectionLabel} route factory slug mismatch; route directory is "${page.slug}" but factory uses "${args.slug}"`));
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,2 @@
1
+ import type { FiniteEnumRippleConfig, ReadTrackedFile, RoutedPage } from './model.mts';
2
+ export declare function checkUnionFactories(errors: string[], pages: readonly RoutedPage[], readTracked: ReadTrackedFile, config: NonNullable<FiniteEnumRippleConfig['union']>, slugToType: ReadonlyMap<string, string>, routeSlugs: readonly string[]): void;
@@ -0,0 +1,34 @@
1
+ import { compareSets, finiteEnumError, uniqueSorted } from './compare.mjs';
2
+ import { parseUnionDetailRouteFactoryArgs } from './parsers.mjs';
3
+ export function checkUnionFactories(errors, pages, readTracked, config, slugToType, routeSlugs) {
4
+ const routeConfigsPath = config.routeConfigsPath;
5
+ const detailFactoryArgs = pages.flatMap((page) => parseUnionDetailRouteFactoryArgs(readTracked(page.file), page.file, config.factoryCallPattern).map((args) => ({
6
+ ...args,
7
+ file: page.file,
8
+ routeSlug: page.slug,
9
+ })));
10
+ compareSets(errors, {
11
+ label: `${config.collectionLabel} route factory slugs`,
12
+ actualLabel: config.routeLabels.factory,
13
+ actualFile: pages[0]?.file ?? routeConfigsPath,
14
+ actualValues: uniqueSorted(detailFactoryArgs.map((args) => args.slug)),
15
+ expectedLabel: config.routeLabels.detail,
16
+ expectedFile: pages[0]?.file ?? routeConfigsPath,
17
+ expectedValues: routeSlugs,
18
+ });
19
+ compareSets(errors, {
20
+ label: `${config.collectionLabel} route factory values`,
21
+ actualLabel: config.routeLabels.factory,
22
+ actualFile: pages[0]?.file ?? routeConfigsPath,
23
+ actualValues: uniqueSorted(detailFactoryArgs.map((args) => args.unionType)),
24
+ expectedLabel: `${routeConfigsPath} ${config.slugMapObject} values`,
25
+ expectedFile: routeConfigsPath,
26
+ expectedValues: [...slugToType.values()],
27
+ });
28
+ for (const args of detailFactoryArgs) {
29
+ const expectedType = slugToType.get(args.routeSlug);
30
+ if (args.slug === args.routeSlug && expectedType === args.unionType)
31
+ continue;
32
+ errors.push(finiteEnumError(args.file, `${config.collectionLabel} route factory args mismatch; route directory is "${args.routeSlug}" and expected type is "${expectedType ?? 'missing'}" but factory uses "${args.unionType}", "${args.slug}"`));
33
+ }
34
+ }
@@ -0,0 +1,2 @@
1
+ import type { FiniteEnumFiles, FiniteEnumRippleConfig, ReadTrackedFile } from './model.mts';
2
+ export declare function checkUnionTypes(errors: string[], files: FiniteEnumFiles, readTracked: ReadTrackedFile, config: NonNullable<FiniteEnumRippleConfig['union']>): void;
@@ -0,0 +1,129 @@
1
+ import { hasConfiguredObjectDeclaration } from './ast.mjs';
2
+ import { checkUnionFactories } from './union-factory-check.mjs';
3
+ import { checkCollectionPagePathLiterals, checkUnionCreatePageTypes, compareSets, finiteEnumError, hasAllFiles, routePageSlugs, uniqueSorted, } from './compare.mjs';
4
+ import { parseUnionRouteConfigEntries, parseUnionSlugToType, parseUnionTypeUnion, } from './parsers.mjs';
5
+ export function checkUnionTypes(errors, files, readTracked, config) {
6
+ const unionTypesPath = config.typesPath;
7
+ const routeConfigsPath = config.routeConfigsPath;
8
+ if (!hasAllFiles(files, [unionTypesPath, routeConfigsPath]))
9
+ return;
10
+ const unionTypes = parseUnionTypeUnion(readTracked(unionTypesPath), unionTypesPath, config.typeAlias);
11
+ const publicUnionTypes = unionTypes.filter((value) => !config.internalTypes.includes(value));
12
+ const routeConfigContent = readTracked(routeConfigsPath);
13
+ const slugToType = parseUnionSlugToType(routeConfigContent, routeConfigsPath, config.slugMapObject);
14
+ const topRouteSlugs = routePageSlugs(files.unionDetailPages, true);
15
+ const routeSlugs = routePageSlugs(files.unionDetailPages);
16
+ compareSets(errors, {
17
+ label: `${config.collectionLabel} route config values`,
18
+ actualLabel: `${routeConfigsPath} ${config.slugMapObject}`,
19
+ actualFile: routeConfigsPath,
20
+ actualValues: [...slugToType.values()],
21
+ expectedLabel: `${unionTypesPath} public ${config.typeAlias} values`,
22
+ expectedFile: unionTypesPath,
23
+ expectedValues: publicUnionTypes,
24
+ });
25
+ compareSets(errors, {
26
+ label: `${config.collectionLabel} route directories`,
27
+ actualLabel: config.routeLabels.detailTop,
28
+ actualFile: files.unionDetailPages[0]?.file ?? routeConfigsPath,
29
+ actualValues: topRouteSlugs,
30
+ expectedLabel: `${routeConfigsPath} ${config.slugMapObject} slugs`,
31
+ expectedFile: routeConfigsPath,
32
+ expectedValues: [...slugToType.keys()],
33
+ });
34
+ compareSets(errors, {
35
+ label: `${config.collectionLabel} routed pages`,
36
+ actualLabel: config.routeLabels.detail,
37
+ actualFile: files.unionDetailPages[0]?.file ?? routeConfigsPath,
38
+ actualValues: routeSlugs,
39
+ expectedLabel: `${routeConfigsPath} ${config.slugMapObject} slugs`,
40
+ expectedFile: routeConfigsPath,
41
+ expectedValues: [...slugToType.keys()],
42
+ });
43
+ if (hasConfiguredObjectDeclaration(routeConfigContent, config.routeConfigObject, routeConfigsPath)) {
44
+ const routeConfigs = parseUnionRouteConfigEntries(routeConfigContent, routeConfigsPath, config.routeConfigObject, config.typeArrayProperty, config.pluralPathProperty, config.singularPathProperty);
45
+ const typedRouteConfigs = routeConfigs.filter((routeConfig) => routeConfig.unionTypes.length > 0);
46
+ const effectiveSingleTypeRouteConfigs = routeConfigs.flatMap((routeConfig) => {
47
+ if (routeConfig.unionTypes.length === 1)
48
+ return [routeConfig];
49
+ if (!config.routeConfigExceptions.includes(routeConfig.key))
50
+ return [];
51
+ const inferredType = slugToType.get(routeConfig.singularPath);
52
+ return inferredType === undefined ? [] : [{ ...routeConfig, unionTypes: [inferredType] }];
53
+ });
54
+ for (const routeConfig of routeConfigs) {
55
+ if (routeConfig.unionTypes.length > 0 ||
56
+ config.routeConfigExceptions.includes(routeConfig.key))
57
+ continue;
58
+ errors.push(finiteEnumError(routeConfigsPath, `${config.routeConfigObject}.${routeConfig.key} has ${config.singularPathProperty} "${routeConfig.singularPath}" but does not declare ${config.typeArrayProperty}`));
59
+ }
60
+ for (const routeConfig of routeConfigs) {
61
+ if (routeConfig.key === routeConfig.pluralPath)
62
+ continue;
63
+ errors.push(finiteEnumError(routeConfigsPath, `${config.routeConfigObject}.${routeConfig.key} has ${config.pluralPathProperty} "${routeConfig.pluralPath}" but the route config key is "${routeConfig.key}"`));
64
+ }
65
+ compareSets(errors, {
66
+ label: `${config.collectionLabel} collection route config values`,
67
+ actualLabel: `${routeConfigsPath} ${config.routeConfigObject}`,
68
+ actualFile: routeConfigsPath,
69
+ actualValues: routeConfigs.flatMap((routeConfig) => {
70
+ if (routeConfig.unionTypes.length > 0)
71
+ return routeConfig.unionTypes;
72
+ if (!config.routeConfigExceptions.includes(routeConfig.key))
73
+ return [];
74
+ const inferredType = slugToType.get(routeConfig.singularPath);
75
+ return inferredType === undefined ? [] : [inferredType];
76
+ }),
77
+ expectedLabel: `${unionTypesPath} public ${config.typeAlias} values`,
78
+ expectedFile: unionTypesPath,
79
+ expectedValues: publicUnionTypes,
80
+ });
81
+ compareSets(errors, {
82
+ label: `${config.collectionLabel} collection route config singular paths`,
83
+ actualLabel: `${routeConfigsPath} ${config.routeConfigObject}`,
84
+ actualFile: routeConfigsPath,
85
+ actualValues: routeConfigs.flatMap((routeConfig) => {
86
+ if (routeConfig.unionTypes.length > 0)
87
+ return [routeConfig.singularPath];
88
+ if (config.routeConfigExceptions.includes(routeConfig.key) &&
89
+ slugToType.has(routeConfig.singularPath)) {
90
+ return [routeConfig.singularPath];
91
+ }
92
+ return [];
93
+ }),
94
+ expectedLabel: `${routeConfigsPath} ${config.slugMapObject} slugs`,
95
+ expectedFile: routeConfigsPath,
96
+ expectedValues: [...slugToType.keys()],
97
+ });
98
+ for (const routeConfig of typedRouteConfigs) {
99
+ const expectedType = slugToType.get(routeConfig.singularPath);
100
+ if (expectedType !== undefined &&
101
+ routeConfig.unionTypes.length === 1 &&
102
+ routeConfig.unionTypes[0] === expectedType) {
103
+ continue;
104
+ }
105
+ errors.push(finiteEnumError(routeConfigsPath, `${config.routeConfigObject}.${routeConfig.key} maps ${config.singularPathProperty} "${routeConfig.singularPath}" to [${routeConfig.unionTypes.join(', ')}] but ${config.slugMapObject} expects "${expectedType ?? 'missing'}"`));
106
+ }
107
+ compareSets(errors, {
108
+ label: `${config.collectionLabel} top-level collection route directories`,
109
+ actualLabel: config.routeLabels.collectionTop,
110
+ actualFile: files.unionCollectionPages[0]?.file ?? routeConfigsPath,
111
+ actualValues: routePageSlugs(files.unionCollectionPages, true),
112
+ expectedLabel: `${routeConfigsPath} ${config.routeConfigObject} ${config.pluralPathProperty}`,
113
+ expectedFile: routeConfigsPath,
114
+ expectedValues: routeConfigs.map((routeConfig) => routeConfig.pluralPath),
115
+ });
116
+ compareSets(errors, {
117
+ label: `${config.collectionLabel} collection routed pages`,
118
+ actualLabel: config.routeLabels.collection,
119
+ actualFile: files.unionCollectionPages[0]?.file ?? routeConfigsPath,
120
+ actualValues: uniqueSorted(files.unionCollectionPages.map((page) => page.slug)),
121
+ expectedLabel: `${routeConfigsPath} ${config.routeConfigObject} ${config.pluralPathProperty}`,
122
+ expectedFile: routeConfigsPath,
123
+ expectedValues: routeConfigs.map((routeConfig) => routeConfig.pluralPath),
124
+ });
125
+ checkUnionCreatePageTypes(errors, effectiveSingleTypeRouteConfigs, files.unionCreatePages, readTracked, config.createPageTypeProperties, config.collectionLabel);
126
+ checkCollectionPagePathLiterals(files.unionCollectionPages, errors, config.collectionLabel, new Set(effectiveSingleTypeRouteConfigs.map((routeConfig) => routeConfig.pluralPath)), readTracked, config.collectionPathLiteralPattern);
127
+ }
128
+ checkUnionFactories(errors, files.unionDetailPages, readTracked, config, slugToType, routeSlugs);
129
+ }
@@ -0,0 +1,6 @@
1
+ import type { MarkdownSourceSpan } from './types.mts';
2
+ /**
3
+ * Returns source spans for fenced/indented code, inline code, and raw HTML. Offsets are JavaScript
4
+ * string offsets; positions retain the parser's 1-indexed line and column coordinates.
5
+ */
6
+ export declare function markdownLiteralSpans(markdown: string): MarkdownSourceSpan[];
@@ -0,0 +1,18 @@
1
+ import { parseGfmMarkdown, walkMarkdown } from './ast.mjs';
2
+ /**
3
+ * Returns source spans for fenced/indented code, inline code, and raw HTML. Offsets are JavaScript
4
+ * string offsets; positions retain the parser's 1-indexed line and column coordinates.
5
+ */
6
+ export function markdownLiteralSpans(markdown) {
7
+ const spans = [];
8
+ walkMarkdown(parseGfmMarkdown(markdown), (node) => {
9
+ if (node.type !== 'code' && node.type !== 'inlineCode' && node.type !== 'html')
10
+ return;
11
+ spans.push({
12
+ kind: node.type === 'inlineCode' ? 'inline-code' : node.type,
13
+ position: node.position,
14
+ value: node.value,
15
+ });
16
+ });
17
+ return spans;
18
+ }
@@ -0,0 +1,5 @@
1
+ import type { MarkdownHtmlFact } from './types.mts';
2
+ /** Returns source-backed HTML facts, classifying inline occurrences separately from block nodes. */
3
+ export declare function markdownHtmlFacts(markdown: string): MarkdownHtmlFact[];
4
+ /** Returns block HTML facts for CommonMark types 1, 6, and 7, preserving their parser bounds. */
5
+ export declare function markdownHtmlBlocks(markdown: string): MarkdownHtmlFact[];
@@ -0,0 +1,51 @@
1
+ import { parseGfmMarkdown } from './ast.mjs';
2
+ const BLOCK_CONTAINERS = new Set(['root', 'blockquote', 'list', 'listItem', 'footnoteDefinition']);
3
+ const TYPE1 = /^ {0,3}<(script|style|pre|textarea)(?:[\s>]|$)/i;
4
+ const TYPE6_NAMES = 'address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|' +
5
+ 'dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|' +
6
+ 'hr|html|iframe|legend|li|link|main|menu|menuitem|nav|noframes|ol|optgroup|option|p|param|' +
7
+ 'search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul';
8
+ const TYPE6 = new RegExp(`^ {0,3}</?(?:${TYPE6_NAMES})(?:[\\s>]|/>|$)`, 'i');
9
+ function htmlType(value) {
10
+ if (TYPE1.test(value))
11
+ return 1;
12
+ if (/^ {0,3}<!--/.test(value))
13
+ return 2;
14
+ if (/^ {0,3}<\?/.test(value))
15
+ return 3;
16
+ if (/^ {0,3}<![A-Za-z]/.test(value))
17
+ return 4;
18
+ if (/^ {0,3}<!\[CDATA\[/i.test(value))
19
+ return 5;
20
+ if (TYPE6.test(value))
21
+ return 6;
22
+ // Parsed block HTML outside types 1–6 is CommonMark type 7 by definition.
23
+ return 7;
24
+ }
25
+ /** Returns source-backed HTML facts, classifying inline occurrences separately from block nodes. */
26
+ export function markdownHtmlFacts(markdown) {
27
+ const facts = [];
28
+ const visit = (node, inBlockContainer) => {
29
+ if (node.type === 'html') {
30
+ const kind = inBlockContainer ? 'block' : 'inline';
31
+ facts.push({
32
+ kind,
33
+ commonMarkType: kind === 'block' ? htmlType(node.value) : null,
34
+ position: node.position,
35
+ value: node.value,
36
+ });
37
+ }
38
+ if (!('children' in node))
39
+ return;
40
+ const childIsBlockContainer = BLOCK_CONTAINERS.has(node.type);
41
+ for (const child of node.children)
42
+ visit(child, childIsBlockContainer);
43
+ };
44
+ visit(parseGfmMarkdown(markdown), true);
45
+ return facts;
46
+ }
47
+ /** Returns block HTML facts for CommonMark types 1, 6, and 7, preserving their parser bounds. */
48
+ export function markdownHtmlBlocks(markdown) {
49
+ return markdownHtmlFacts(markdown).filter((fact) => fact.kind === 'block' &&
50
+ (fact.commonMarkType === 1 || fact.commonMarkType === 6 || fact.commonMarkType === 7));
51
+ }
@@ -1,4 +1,6 @@
1
1
  export { findMarkdownNode, findMarkdownNodes, parseGfmMarkdown, walkMarkdown } from './ast.mts';
2
+ export { markdownHtmlBlocks, markdownHtmlFacts } from './html-blocks.mts';
3
+ export { markdownLiteralSpans } from './content-facts.mts';
2
4
  export { extractLooseMarkdownTableRows } from './loose-pipe-rows.mts';
3
5
  export { parseMarkdownSections } from './document-sections.mts';
4
6
  export { validateMarkdownSections } from './validate-sections.mts';
@@ -6,4 +8,5 @@ export type { MarkdownVisibleSection, MarkdownSectionsDiagnostic, MarkdownSectio
6
8
  export { markdownSectionBetweenHeadings } from './sections.mts';
7
9
  export { extractMarkdownTables, parseMarkdownTables } from './tables.mts';
8
10
  export { markdownNodeText } from './text.mts';
9
- export type { LooseMarkdownTableRow, LooseMarkdownTableRowsOptions, MarkdownNode, MarkdownSection, MarkdownSectionOptions, MarkdownTableRow, MarkdownTextOptions, ParseMarkdownTablesOptions, PositionedMarkdownTable, } from './types.mts';
11
+ export { hasUncheckedMarkdownTask } from './tasks.mts';
12
+ export type { LooseMarkdownTableRow, LooseMarkdownTableRowsOptions, MarkdownNode, MarkdownHtmlBlockType, MarkdownHtmlFact, MarkdownSourcePosition, MarkdownSourceSpan, MarkdownSection, MarkdownSectionOptions, MarkdownTableRow, MarkdownTextOptions, ParseMarkdownTablesOptions, PositionedMarkdownTable, } from './types.mts';
@@ -1,7 +1,10 @@
1
1
  export { findMarkdownNode, findMarkdownNodes, parseGfmMarkdown, walkMarkdown } from './ast.mjs';
2
+ export { markdownHtmlBlocks, markdownHtmlFacts } from './html-blocks.mjs';
3
+ export { markdownLiteralSpans } from './content-facts.mjs';
2
4
  export { extractLooseMarkdownTableRows } from './loose-pipe-rows.mjs';
3
5
  export { parseMarkdownSections } from './document-sections.mjs';
4
6
  export { validateMarkdownSections } from './validate-sections.mjs';
5
7
  export { markdownSectionBetweenHeadings } from './sections.mjs';
6
8
  export { extractMarkdownTables, parseMarkdownTables } from './tables.mjs';
7
9
  export { markdownNodeText } from './text.mjs';
10
+ export { hasUncheckedMarkdownTask } from './tasks.mjs';
@@ -0,0 +1,2 @@
1
+ /** True when the GFM parse contains at least one unchecked task-list item. */
2
+ export declare function hasUncheckedMarkdownTask(markdown: string): boolean;
@@ -0,0 +1,10 @@
1
+ import { parseGfmMarkdown, walkMarkdown } from './ast.mjs';
2
+ /** True when the GFM parse contains at least one unchecked task-list item. */
3
+ export function hasUncheckedMarkdownTask(markdown) {
4
+ let unchecked = false;
5
+ walkMarkdown(parseGfmMarkdown(markdown), (node) => {
6
+ if (node.type === 'listItem' && node.checked === false)
7
+ unchecked = true;
8
+ });
9
+ return unchecked;
10
+ }
@@ -1,6 +1,33 @@
1
1
  import type { Heading, Root, RootContent } from 'mdast';
2
2
  import type { Position } from 'unist';
3
3
  export type MarkdownNode = Root | RootContent;
4
+ /** A source range measured in JavaScript string offsets and 1-indexed source coordinates. */
5
+ export type MarkdownSourcePosition = {
6
+ start: {
7
+ line: number;
8
+ column: number;
9
+ offset: number;
10
+ };
11
+ end: {
12
+ line: number;
13
+ column: number;
14
+ offset: number;
15
+ };
16
+ };
17
+ /** A source-backed Markdown span with no dependency on mdast node types. */
18
+ export type MarkdownSourceSpan = {
19
+ kind: 'code' | 'inline-code' | 'html';
20
+ position: MarkdownSourcePosition;
21
+ value: string;
22
+ };
23
+ export type MarkdownHtmlBlockType = 1 | 2 | 3 | 4 | 5 | 6 | 7;
24
+ /** HTML parser facts distinguish block parsing from inline HTML in a phrasing container. */
25
+ export type MarkdownHtmlFact = {
26
+ commonMarkType: MarkdownHtmlBlockType | null;
27
+ kind: 'block' | 'inline';
28
+ position: MarkdownSourcePosition;
29
+ value: string;
30
+ };
4
31
  export type MarkdownTableRow = {
5
32
  cells: string[];
6
33
  line: number;
@@ -9,7 +9,7 @@ function queryParameter(name, descriptor) {
9
9
  return {
10
10
  name,
11
11
  in: 'query',
12
- required: false,
12
+ required: descriptor.required === true,
13
13
  ...(descriptor.description ? { description: descriptor.description } : {}),
14
14
  ...(descriptor.kind === 'csv-array'
15
15
  ? { style: descriptor.style, explode: descriptor.explode }
@@ -29,7 +29,7 @@ export type OpenApiParameter = {
29
29
  } | {
30
30
  name: string;
31
31
  in: 'query';
32
- required: false;
32
+ required: boolean;
33
33
  description?: string;
34
34
  style?: 'form';
35
35
  explode?: false;
@@ -1,4 +1,4 @@
1
- export type OpenApiQueryParameter = {
1
+ export type OpenApiQueryParameterShape = {
2
2
  kind: 'string';
3
3
  format?: 'uuid' | 'uri';
4
4
  description?: string;
@@ -27,9 +27,15 @@ export type OpenApiQueryParameter = {
27
27
  description?: string;
28
28
  } | {
29
29
  kind: 'csv-array';
30
- items: OpenApiQueryParameter;
30
+ items: OpenApiQueryParameterShape & {
31
+ required?: never;
32
+ };
31
33
  style: 'form';
32
34
  explode: false;
33
35
  description?: string;
34
36
  };
37
+ /** Top-level parameters may be `required: true`; array `items` never are. */
38
+ export type OpenApiQueryParameter = OpenApiQueryParameterShape & {
39
+ required?: true;
40
+ };
35
41
  export type OpenApiQueryContract = Readonly<Record<string, OpenApiQueryParameter>>;
@@ -0,0 +1,5 @@
1
+ export { analyzePostPublicationWriterSource } from './writers/source.mts';
2
+ export type { PostPublicationWriterSourceAnalysis } from './writers/source.mts';
3
+ export type { PostPublicationWriterSourceOptions } from './writers/types.mts';
4
+ export { extractStaticSqlTemplateQuasis } from './readers/sql-source.mts';
5
+ export type { ReaderSqlTemplateOptions } from './readers/sql-template.mts';
@@ -0,0 +1,2 @@
1
+ export { analyzePostPublicationWriterSource } from './writers/source.mjs';
2
+ export { extractStaticSqlTemplateQuasis } from './readers/sql-source.mjs';
@@ -0,0 +1,3 @@
1
+ import ts from '../../contract-schema/typescript-api.mts';
2
+ import { type ReaderSqlTemplateOptions } from './sql-template.mts';
3
+ export declare function terminalSqlExecutorBindings(ast: ts.SourceFile, options: ReaderSqlTemplateOptions): Set<string>;
@@ -0,0 +1,32 @@
1
+ import ts from '../../contract-schema/typescript-api.mjs';
2
+ import { identifierName, unparenthesized, walk, } from './sql-template.mjs';
3
+ export function terminalSqlExecutorBindings(ast, options) {
4
+ const bindings = new Set();
5
+ walk(ast, (node) => {
6
+ if (!ts.isImportDeclaration(node) || !ts.isStringLiteral(node.moduleSpecifier))
7
+ return;
8
+ const names = options.executorImports.get(node.moduleSpecifier.text);
9
+ const importClause = node.importClause;
10
+ const specifiers = importClause?.namedBindings;
11
+ if (importClause?.isTypeOnly)
12
+ return;
13
+ if (!names || !specifiers || !ts.isNamedImports(specifiers))
14
+ return;
15
+ for (const specifier of specifiers.elements) {
16
+ if (!specifier.isTypeOnly && names.has((specifier.propertyName ?? specifier.name).text))
17
+ bindings.add(specifier.name.text);
18
+ }
19
+ });
20
+ walk(ast, (node) => {
21
+ if (!ts.isVariableDeclaration(node) || !ts.isIdentifier(node.name))
22
+ return;
23
+ const initializer = node.initializer && unparenthesized(node.initializer);
24
+ if (!initializer || !ts.isConditionalExpression(initializer))
25
+ return;
26
+ const consequent = identifierName(initializer.whenTrue);
27
+ const alternate = identifierName(initializer.whenFalse);
28
+ if ((consequent && bindings.has(consequent)) || (alternate && bindings.has(alternate)))
29
+ bindings.add(node.name.text);
30
+ });
31
+ return bindings;
32
+ }
@@ -0,0 +1,7 @@
1
+ import { type ReaderSqlTemplateOptions } from './sql-template.mts';
2
+ /**
3
+ * Collect static SQL fragments returned, exported, or passed to configured executors.
4
+ * Interpolations become numbered placeholders; local append chains are reassembled.
5
+ * This preserves name-based collection heuristics, not binding or control-flow proof.
6
+ */
7
+ export declare function extractStaticSqlTemplateQuasis(content: string, options: ReaderSqlTemplateOptions): string[];