@thymian/plugin-sampler 0.3.0-canary.20261007-e6cb8dc → 0.3.0-canary.20261007-cdbe1d3

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 (264) hide show
  1. package/dist/cli/check-result.d.ts +88 -0
  2. package/dist/cli/check-result.d.ts.map +1 -0
  3. package/dist/cli/check-result.js +160 -0
  4. package/dist/cli/check-result.js.map +1 -0
  5. package/dist/cli/commands/sampler/check.d.ts +32 -4
  6. package/dist/cli/commands/sampler/check.d.ts.map +1 -1
  7. package/dist/cli/commands/sampler/check.js +126 -88
  8. package/dist/cli/commands/sampler/check.js.map +1 -1
  9. package/dist/cli/commands/sampler/init.d.ts +4 -6
  10. package/dist/cli/commands/sampler/init.d.ts.map +1 -1
  11. package/dist/cli/commands/sampler/init.js +33 -49
  12. package/dist/cli/commands/sampler/init.js.map +1 -1
  13. package/dist/cli/commands/sampler/show.d.ts +11 -0
  14. package/dist/cli/commands/sampler/show.d.ts.map +1 -0
  15. package/dist/cli/commands/sampler/show.js +36 -0
  16. package/dist/cli/commands/sampler/show.js.map +1 -0
  17. package/dist/cli/commands/sampler/sync.d.ts +11 -0
  18. package/dist/cli/commands/sampler/sync.d.ts.map +1 -0
  19. package/dist/cli/commands/sampler/sync.js +65 -0
  20. package/dist/cli/commands/sampler/sync.js.map +1 -0
  21. package/dist/cli/commands/sampler/validate.d.ts +3 -10
  22. package/dist/cli/commands/sampler/validate.d.ts.map +1 -1
  23. package/dist/cli/commands/sampler/validate.js +57 -126
  24. package/dist/cli/commands/sampler/validate.js.map +1 -1
  25. package/dist/generation/closed-schema.d.ts +22 -0
  26. package/dist/generation/closed-schema.d.ts.map +1 -0
  27. package/dist/generation/closed-schema.js +45 -0
  28. package/dist/generation/closed-schema.js.map +1 -0
  29. package/dist/generation/content-type-strategies/json.content-type-strategy.d.ts.map +1 -1
  30. package/dist/generation/content-type-strategies/json.content-type-strategy.js +3 -1
  31. package/dist/generation/content-type-strategies/json.content-type-strategy.js.map +1 -1
  32. package/dist/generation/content-type-strategies/xml.content-type-strategy.d.ts.map +1 -1
  33. package/dist/generation/content-type-strategies/xml.content-type-strategy.js +3 -1
  34. package/dist/generation/content-type-strategies/xml.content-type-strategy.js.map +1 -1
  35. package/dist/generation/generate-request-sample.d.ts +12 -0
  36. package/dist/generation/generate-request-sample.d.ts.map +1 -0
  37. package/dist/generation/{generate-samples.js → generate-request-sample.js} +11 -4
  38. package/dist/generation/generate-request-sample.js.map +1 -0
  39. package/dist/generation/request-generators/default-request-generator.d.ts +14 -0
  40. package/dist/generation/request-generators/default-request-generator.d.ts.map +1 -1
  41. package/dist/generation/request-generators/default-request-generator.js +18 -1
  42. package/dist/generation/request-generators/default-request-generator.js.map +1 -1
  43. package/dist/generation/types/declaration-set.d.ts +124 -0
  44. package/dist/generation/types/declaration-set.d.ts.map +1 -0
  45. package/dist/generation/types/declaration-set.js +389 -0
  46. package/dist/generation/types/declaration-set.js.map +1 -0
  47. package/dist/generation/types/example-reflection.d.ts +21 -0
  48. package/dist/generation/types/example-reflection.d.ts.map +1 -0
  49. package/dist/generation/types/example-reflection.js +135 -0
  50. package/dist/generation/types/example-reflection.js.map +1 -0
  51. package/dist/generation/types/generate-type-surface.d.ts +23 -0
  52. package/dist/generation/types/generate-type-surface.d.ts.map +1 -0
  53. package/dist/generation/types/generate-type-surface.js +671 -0
  54. package/dist/generation/types/generate-type-surface.js.map +1 -0
  55. package/dist/generation/types/schema-type.d.ts +20 -0
  56. package/dist/generation/types/schema-type.d.ts.map +1 -0
  57. package/dist/generation/types/schema-type.js +203 -0
  58. package/dist/generation/types/schema-type.js.map +1 -0
  59. package/dist/generation/types/self-check-surface.d.ts +32 -0
  60. package/dist/generation/types/self-check-surface.d.ts.map +1 -0
  61. package/dist/generation/types/self-check-surface.js +92 -0
  62. package/dist/generation/types/self-check-surface.js.map +1 -0
  63. package/dist/generation/types/type-names.d.ts +135 -0
  64. package/dist/generation/types/type-names.d.ts.map +1 -0
  65. package/dist/generation/types/type-names.js +213 -0
  66. package/dist/generation/types/type-names.js.map +1 -0
  67. package/dist/generation/types/write-type-surface.d.ts +58 -0
  68. package/dist/generation/types/write-type-surface.d.ts.map +1 -0
  69. package/dist/generation/types/write-type-surface.js +119 -0
  70. package/dist/generation/types/write-type-surface.js.map +1 -0
  71. package/dist/hooks/create-hook-utils.d.ts +22 -6
  72. package/dist/hooks/create-hook-utils.d.ts.map +1 -1
  73. package/dist/hooks/create-hook-utils.js +74 -96
  74. package/dist/hooks/create-hook-utils.js.map +1 -1
  75. package/dist/hooks/hook-api.d.ts +49 -0
  76. package/dist/hooks/hook-api.d.ts.map +1 -0
  77. package/dist/hooks/hook-api.js +105 -0
  78. package/dist/hooks/hook-api.js.map +1 -0
  79. package/dist/hooks/hook-diagnostics.d.ts +45 -0
  80. package/dist/hooks/hook-diagnostics.d.ts.map +1 -0
  81. package/dist/hooks/hook-diagnostics.js +80 -0
  82. package/dist/hooks/hook-diagnostics.js.map +1 -0
  83. package/dist/hooks/hook-errors.d.ts +33 -0
  84. package/dist/hooks/hook-errors.d.ts.map +1 -1
  85. package/dist/hooks/hook-errors.js +46 -0
  86. package/dist/hooks/hook-errors.js.map +1 -1
  87. package/dist/hooks/hook-registration.d.ts +108 -0
  88. package/dist/hooks/hook-registration.d.ts.map +1 -0
  89. package/dist/hooks/hook-registration.js +69 -0
  90. package/dist/hooks/hook-registration.js.map +1 -0
  91. package/dist/hooks/hook-runner.d.ts +111 -11
  92. package/dist/hooks/hook-runner.d.ts.map +1 -1
  93. package/dist/hooks/hook-runner.js +309 -142
  94. package/dist/hooks/hook-runner.js.map +1 -1
  95. package/dist/hooks/hook-utils-factory.d.ts +19 -0
  96. package/dist/hooks/hook-utils-factory.d.ts.map +1 -0
  97. package/dist/hooks/hook-utils-factory.js +2 -0
  98. package/dist/hooks/hook-utils-factory.js.map +1 -0
  99. package/dist/hooks/hook-utils.d.ts +80 -12
  100. package/dist/hooks/hook-utils.d.ts.map +1 -1
  101. package/dist/hooks/invoke-hook.d.ts +71 -0
  102. package/dist/hooks/invoke-hook.d.ts.map +1 -0
  103. package/dist/hooks/invoke-hook.js +161 -0
  104. package/dist/hooks/invoke-hook.js.map +1 -0
  105. package/dist/hooks/load-user-hooks.d.ts +96 -0
  106. package/dist/hooks/load-user-hooks.d.ts.map +1 -0
  107. package/dist/hooks/load-user-hooks.js +432 -0
  108. package/dist/hooks/load-user-hooks.js.map +1 -0
  109. package/dist/hooks/nested-request.d.ts +21 -0
  110. package/dist/hooks/nested-request.d.ts.map +1 -0
  111. package/dist/hooks/nested-request.js +24 -0
  112. package/dist/hooks/nested-request.js.map +1 -0
  113. package/dist/hooks/overlay-args.d.ts +40 -0
  114. package/dist/hooks/overlay-args.d.ts.map +1 -0
  115. package/dist/hooks/overlay-args.js +84 -0
  116. package/dist/hooks/overlay-args.js.map +1 -0
  117. package/dist/hooks/resolve-targeting.d.ts +16 -0
  118. package/dist/hooks/resolve-targeting.d.ts.map +1 -0
  119. package/dist/hooks/resolve-targeting.js +168 -0
  120. package/dist/hooks/resolve-targeting.js.map +1 -0
  121. package/dist/hooks/run-scoped-hooks.d.ts +83 -0
  122. package/dist/hooks/run-scoped-hooks.d.ts.map +1 -0
  123. package/dist/hooks/run-scoped-hooks.js +153 -0
  124. package/dist/hooks/run-scoped-hooks.js.map +1 -0
  125. package/dist/index.d.ts +42 -19
  126. package/dist/index.d.ts.map +1 -1
  127. package/dist/index.js +152 -165
  128. package/dist/index.js.map +1 -1
  129. package/dist/request-sampler.d.ts +40 -11
  130. package/dist/request-sampler.d.ts.map +1 -1
  131. package/dist/request-sampler.js +51 -76
  132. package/dist/request-sampler.js.map +1 -1
  133. package/dist/sampler-paths.d.ts +19 -0
  134. package/dist/sampler-paths.d.ts.map +1 -0
  135. package/dist/sampler-paths.js +22 -0
  136. package/dist/sampler-paths.js.map +1 -0
  137. package/dist/selectors/nearest-paths.d.ts +14 -0
  138. package/dist/selectors/nearest-paths.d.ts.map +1 -0
  139. package/dist/selectors/nearest-paths.js +32 -0
  140. package/dist/selectors/nearest-paths.js.map +1 -0
  141. package/dist/selectors/path-glob.d.ts +94 -0
  142. package/dist/selectors/path-glob.d.ts.map +1 -0
  143. package/dist/selectors/path-glob.js +136 -0
  144. package/dist/selectors/path-glob.js.map +1 -0
  145. package/dist/selectors/selector-errors.d.ts +17 -0
  146. package/dist/selectors/selector-errors.d.ts.map +1 -0
  147. package/dist/selectors/selector-errors.js +139 -0
  148. package/dist/selectors/selector-errors.js.map +1 -0
  149. package/dist/selectors/selector.d.ts +55 -0
  150. package/dist/selectors/selector.d.ts.map +1 -0
  151. package/dist/selectors/selector.js +305 -0
  152. package/dist/selectors/selector.js.map +1 -0
  153. package/dist/selectors/transaction-catalog.d.ts +96 -0
  154. package/dist/selectors/transaction-catalog.d.ts.map +1 -0
  155. package/dist/selectors/transaction-catalog.js +193 -0
  156. package/dist/selectors/transaction-catalog.js.map +1 -0
  157. package/dist/selectors/transaction-filter.d.ts +119 -0
  158. package/dist/selectors/transaction-filter.d.ts.map +1 -0
  159. package/dist/selectors/transaction-filter.js +191 -0
  160. package/dist/selectors/transaction-filter.js.map +1 -0
  161. package/dist/ts-path.d.ts +36 -0
  162. package/dist/ts-path.d.ts.map +1 -0
  163. package/dist/ts-path.js +46 -0
  164. package/dist/ts-path.js.map +1 -0
  165. package/dist/utils.d.ts +1 -2
  166. package/dist/utils.d.ts.map +1 -1
  167. package/dist/utils.js +2 -20
  168. package/dist/utils.js.map +1 -1
  169. package/dist/validation/canonicalize.d.ts +17 -0
  170. package/dist/validation/canonicalize.d.ts.map +1 -0
  171. package/dist/validation/canonicalize.js +29 -0
  172. package/dist/validation/canonicalize.js.map +1 -0
  173. package/dist/validation/shared-compiler-host.d.ts +17 -0
  174. package/dist/validation/shared-compiler-host.d.ts.map +1 -0
  175. package/dist/validation/shared-compiler-host.js +58 -0
  176. package/dist/validation/shared-compiler-host.js.map +1 -0
  177. package/dist/validation/typecheck-hooks.d.ts +37 -0
  178. package/dist/validation/typecheck-hooks.d.ts.map +1 -0
  179. package/dist/validation/typecheck-hooks.js +169 -0
  180. package/dist/validation/typecheck-hooks.js.map +1 -0
  181. package/dist/validation/validate-sampler.d.ts +79 -0
  182. package/dist/validation/validate-sampler.d.ts.map +1 -0
  183. package/dist/validation/validate-sampler.js +89 -0
  184. package/dist/validation/validate-sampler.js.map +1 -0
  185. package/package.json +7 -11
  186. package/dist/cli/commands/sampler/generate/hook.d.ts +0 -10
  187. package/dist/cli/commands/sampler/generate/hook.d.ts.map +0 -1
  188. package/dist/cli/commands/sampler/generate/hook.js +0 -19
  189. package/dist/cli/commands/sampler/generate/hook.js.map +0 -1
  190. package/dist/cli/generate-hook.d.ts +0 -4
  191. package/dist/cli/generate-hook.d.ts.map +0 -1
  192. package/dist/cli/generate-hook.js +0 -96
  193. package/dist/cli/generate-hook.js.map +0 -1
  194. package/dist/cli/templates.d.ts +0 -4
  195. package/dist/cli/templates.d.ts.map +0 -1
  196. package/dist/cli/templates.js +0 -28
  197. package/dist/cli/templates.js.map +0 -1
  198. package/dist/constants.d.ts +0 -12
  199. package/dist/constants.d.ts.map +0 -1
  200. package/dist/constants.js +0 -16
  201. package/dist/constants.js.map +0 -1
  202. package/dist/generation/generate-samples-for-thymian-format.d.ts +0 -4
  203. package/dist/generation/generate-samples-for-thymian-format.d.ts.map +0 -1
  204. package/dist/generation/generate-samples-for-thymian-format.js +0 -18
  205. package/dist/generation/generate-samples-for-thymian-format.js.map +0 -1
  206. package/dist/generation/generate-samples.d.ts +0 -4
  207. package/dist/generation/generate-samples.d.ts.map +0 -1
  208. package/dist/generation/generate-samples.js.map +0 -1
  209. package/dist/hooks/generate-request-types.d.ts +0 -34
  210. package/dist/hooks/generate-request-types.d.ts.map +0 -1
  211. package/dist/hooks/generate-request-types.js +0 -458
  212. package/dist/hooks/generate-request-types.js.map +0 -1
  213. package/dist/hooks/hook-types.d.ts +0 -7
  214. package/dist/hooks/hook-types.d.ts.map +0 -1
  215. package/dist/hooks/hook-types.js +0 -2
  216. package/dist/hooks/hook-types.js.map +0 -1
  217. package/dist/hooks/load-hooks-from-samples.d.ts +0 -3
  218. package/dist/hooks/load-hooks-from-samples.d.ts.map +0 -1
  219. package/dist/hooks/load-hooks-from-samples.js +0 -22
  220. package/dist/hooks/load-hooks-from-samples.js.map +0 -1
  221. package/dist/hooks/ts-config.d.ts +0 -10
  222. package/dist/hooks/ts-config.d.ts.map +0 -1
  223. package/dist/hooks/ts-config.js +0 -10
  224. package/dist/hooks/ts-config.js.map +0 -1
  225. package/dist/samples-structure/file-request-sample.d.ts +0 -20
  226. package/dist/samples-structure/file-request-sample.d.ts.map +0 -1
  227. package/dist/samples-structure/file-request-sample.js +0 -5
  228. package/dist/samples-structure/file-request-sample.js.map +0 -1
  229. package/dist/samples-structure/get-path-transaction-id.d.ts +0 -3
  230. package/dist/samples-structure/get-path-transaction-id.d.ts.map +0 -1
  231. package/dist/samples-structure/get-path-transaction-id.js +0 -22
  232. package/dist/samples-structure/get-path-transaction-id.js.map +0 -1
  233. package/dist/samples-structure/merge-tree.d.ts +0 -5
  234. package/dist/samples-structure/merge-tree.d.ts.map +0 -1
  235. package/dist/samples-structure/merge-tree.js +0 -118
  236. package/dist/samples-structure/merge-tree.js.map +0 -1
  237. package/dist/samples-structure/read-samples-from-dir.d.ts +0 -13
  238. package/dist/samples-structure/read-samples-from-dir.d.ts.map +0 -1
  239. package/dist/samples-structure/read-samples-from-dir.js +0 -205
  240. package/dist/samples-structure/read-samples-from-dir.js.map +0 -1
  241. package/dist/samples-structure/samples-from-transactions.d.ts +0 -5
  242. package/dist/samples-structure/samples-from-transactions.d.ts.map +0 -1
  243. package/dist/samples-structure/samples-from-transactions.js +0 -120
  244. package/dist/samples-structure/samples-from-transactions.js.map +0 -1
  245. package/dist/samples-structure/samples-tree-structure.d.ts +0 -106
  246. package/dist/samples-structure/samples-tree-structure.d.ts.map +0 -1
  247. package/dist/samples-structure/samples-tree-structure.js +0 -26
  248. package/dist/samples-structure/samples-tree-structure.js.map +0 -1
  249. package/dist/samples-structure/structure-meta-on-disc.d.ts +0 -11
  250. package/dist/samples-structure/structure-meta-on-disc.d.ts.map +0 -1
  251. package/dist/samples-structure/structure-meta-on-disc.js +0 -2
  252. package/dist/samples-structure/structure-meta-on-disc.js.map +0 -1
  253. package/dist/samples-structure/traverse.d.ts +0 -4
  254. package/dist/samples-structure/traverse.d.ts.map +0 -1
  255. package/dist/samples-structure/traverse.js +0 -13
  256. package/dist/samples-structure/traverse.js.map +0 -1
  257. package/dist/samples-structure/write-samples-to-dir.d.ts +0 -37
  258. package/dist/samples-structure/write-samples-to-dir.d.ts.map +0 -1
  259. package/dist/samples-structure/write-samples-to-dir.js +0 -183
  260. package/dist/samples-structure/write-samples-to-dir.js.map +0 -1
  261. package/dist/validation/validate-sampler-output.d.ts +0 -38
  262. package/dist/validation/validate-sampler-output.d.ts.map +0 -1
  263. package/dist/validation/validate-sampler-output.js +0 -288
  264. package/dist/validation/validate-sampler-output.js.map +0 -1
@@ -0,0 +1,19 @@
1
+ export type SamplerPaths = {
2
+ /** The per-sampler root, `<cwd>/.thymian/sampler` by default. */
3
+ root: string;
4
+ /** The committed type surface. Regenerated wholesale. */
5
+ generatedDir: string;
6
+ /** The recursive hook tree the loader scans. */
7
+ hooksDir: string;
8
+ /** The tsconfig `init` scaffolds once and thereafter never touches. */
9
+ tsconfigPath: string;
10
+ };
11
+ /**
12
+ * The single source of truth for every sampler path.
13
+ *
14
+ * `path` is the plugin option. It named the v1 samples tree, which no longer
15
+ * exists; it now names the sampler root, so a project that kept its sampler
16
+ * somewhere other than `.thymian/sampler` keeps one option to say so.
17
+ */
18
+ export declare function resolveSamplerPaths(cwd: string, path?: string): SamplerPaths;
19
+ //# sourceMappingURL=sampler-paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sampler-paths.d.ts","sourceRoot":"","sources":["../src/sampler-paths.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,YAAY,GAAG;IACzB,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,YAAY,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,YAAY,CAa5E"}
@@ -0,0 +1,22 @@
1
+ import { isAbsolute, join } from 'node:path';
2
+ /**
3
+ * The single source of truth for every sampler path.
4
+ *
5
+ * `path` is the plugin option. It named the v1 samples tree, which no longer
6
+ * exists; it now names the sampler root, so a project that kept its sampler
7
+ * somewhere other than `.thymian/sampler` keeps one option to say so.
8
+ */
9
+ export function resolveSamplerPaths(cwd, path) {
10
+ const root = path
11
+ ? isAbsolute(path)
12
+ ? path
13
+ : join(cwd, path)
14
+ : join(cwd, '.thymian', 'sampler');
15
+ return {
16
+ root,
17
+ generatedDir: join(root, 'generated'),
18
+ hooksDir: join(root, 'hooks'),
19
+ tsconfigPath: join(root, 'tsconfig.json'),
20
+ };
21
+ }
22
+ //# sourceMappingURL=sampler-paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sampler-paths.js","sourceRoot":"","sources":["../src/sampler-paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAa7C;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW,EAAE,IAAa;IAC5D,MAAM,IAAI,GAAG,IAAI;QACf,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC;YAChB,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC;QACnB,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;IAErC,OAAO;QACL,IAAI;QACJ,YAAY,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC;QACrC,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC;QAC7B,YAAY,EAAE,IAAI,CAAC,IAAI,EAAE,eAAe,CAAC;KAC1C,CAAC;AACJ,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Paths that share `value`'s longest literal prefix, as message lines.
3
+ *
4
+ * Walks the prefix from the longest down to the shortest, so the reader is
5
+ * pointed at the narrowest subtree that actually has anything in it rather
6
+ * than at the whole description. No fuzzy matching: it would add a
7
+ * dependency and make the ordering unexplainable.
8
+ *
9
+ * Shared between a vacuous filter path value and a Selector whose path is a
10
+ * typo — the same prefix walk, so the two diagnostics agree on what "near"
11
+ * means instead of carrying two guesses that could disagree.
12
+ */
13
+ export declare function nearestPathHints(value: string, paths: readonly string[]): string[];
14
+ //# sourceMappingURL=nearest-paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nearest-paths.d.ts","sourceRoot":"","sources":["../../src/selectors/nearest-paths.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,MAAM,EAAE,CAoBV"}
@@ -0,0 +1,32 @@
1
+ const MAX_NEAR_PATHS = 5;
2
+ /**
3
+ * Paths that share `value`'s longest literal prefix, as message lines.
4
+ *
5
+ * Walks the prefix from the longest down to the shortest, so the reader is
6
+ * pointed at the narrowest subtree that actually has anything in it rather
7
+ * than at the whole description. No fuzzy matching: it would add a
8
+ * dependency and make the ordering unexplainable.
9
+ *
10
+ * Shared between a vacuous filter path value and a Selector whose path is a
11
+ * typo — the same prefix walk, so the two diagnostics agree on what "near"
12
+ * means instead of carrying two guesses that could disagree.
13
+ */
14
+ export function nearestPathHints(value, paths) {
15
+ const segments = value.split('/');
16
+ for (let depth = segments.length - 1; depth > 0; depth--) {
17
+ const prefix = `${segments.slice(0, depth).join('/')}/`;
18
+ const candidates = paths
19
+ .filter((path) => path.startsWith(prefix))
20
+ .slice(0, MAX_NEAR_PATHS);
21
+ if (candidates.length > 0) {
22
+ return [
23
+ `Paths under "${prefix}" are:`,
24
+ ...candidates.map((path) => `"${path}"`),
25
+ ];
26
+ }
27
+ }
28
+ return [
29
+ `No path in the loaded API description begins with "${segments[1] ? `/${segments[1]}` : value}".`,
30
+ ];
31
+ }
32
+ //# sourceMappingURL=nearest-paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nearest-paths.js","sourceRoot":"","sources":["../../src/selectors/nearest-paths.ts"],"names":[],"mappings":"AAAA,MAAM,cAAc,GAAG,CAAC,CAAC;AAEzB;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAC9B,KAAa,EACb,KAAwB;IAExB,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAElC,KAAK,IAAI,KAAK,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QACzD,MAAM,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;QACxD,MAAM,UAAU,GAAG,KAAK;aACrB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;aACzC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC;QAE5B,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,OAAO;gBACL,gBAAgB,MAAM,QAAQ;gBAC9B,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,GAAG,CAAC;aACzC,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO;QACL,sDAAsD,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI;KAClG,CAAC;AACJ,CAAC"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * # The path-glob grammar — FROZEN
3
+ *
4
+ * A glob is a string compared against a path as the transaction catalog spells
5
+ * it: beginning with `/`, already carrying the API description's server base
6
+ * path, with `{param}` in template form. Both sides are compared **segment by
7
+ * segment**, where a segment is a maximal run of characters containing no `/`.
8
+ *
9
+ * ```ebnf
10
+ * glob = segment , { "/" , segment } , [ "/" , deep-wildcard ] ;
11
+ * segment = wildcard | literal-segment ;
12
+ * wildcard = "*" ; (* the WHOLE segment, never part *)
13
+ * deep-wildcard = "**" ; (* final position only *)
14
+ * literal-segment = { literal-char } ; (* matched byte-for-byte *)
15
+ * literal-char = ? any character except "/" ? ;
16
+ * ```
17
+ *
18
+ * ## The decisions (all of them explicit, all of them frozen)
19
+ *
20
+ * 1. **`*` matches exactly one whole segment.** It never matches part of a
21
+ * segment. `/foo*` is therefore **not a legal glob** — it is a segment whose
22
+ * literal text happens to be `foo*`, which matches no real path, and which
23
+ * `validate` reports as "matches nothing". It is deliberately *not* a
24
+ * separate "malformed glob" diagnostic: one diagnostic for "this names
25
+ * nothing" is the whole promise, and a second class of error would have to
26
+ * be kept in sync with the first.
27
+ * 2. **`*` matches exactly one segment — never zero.** `/users/*` does not
28
+ * match `/users`, and does not match `/users/` (whose trailing segment is
29
+ * the empty string).
30
+ * 3. **`*` never crosses `/`.** `/users/*` does not match `/users/1/orders`.
31
+ * 4. **A trailing `**` matches one *or more* segments.** `/admin/**` matches
32
+ * `/admin/users` and `/admin/users/42/roles`, and does **not** match
33
+ * `/admin` itself. A user who wants both writes two entries. This is the
34
+ * decision most likely to be argued later, so: it is one-or-more, and the
35
+ * reason is that `/admin/**` reads as "everything *under* /admin", and a
36
+ * zero-or-more reading makes `{ path: '/admin/**' }` silently a superset of
37
+ * `{ path: '/admin' }` — two different intents spelled the same way.
38
+ * 5. **`**` is legal only as the final segment.** Leading and mid-path `**`
39
+ * are out; mid-path `**` is deferred. A `**` anywhere else is, again, just a
40
+ * literal segment that matches nothing.
41
+ * 6. **`{id}` — and any other brace text — is a literal.** Real paths contain
42
+ * `{id}`, `{launchId}` and so on, because the loader keeps the template
43
+ * form. A glob spells them exactly as the path does. Braces are **not**
44
+ * metacharacters and never will be in this grammar.
45
+ * 7. **Matching is case-sensitive and exact.** `/Users/*` does not match
46
+ * `/users/1`. No normalisation, no trailing-slash tolerance, no percent
47
+ * decoding.
48
+ * 8. **A glob containing no metacharacter is just an exact path.** It has to
49
+ * equal a path the catalog holds, and it is validated by the same rule as
50
+ * every other glob: does it name at least one path.
51
+ * 9. **Anchoring.** A glob is matched against the whole path string, anchored
52
+ * at both ends — there is no implicit prefix or suffix wildcard. The string
53
+ * it is anchored against carries the server base path, so on a description
54
+ * whose server is `https://api.example.com/v1`, the glob for the spec's own
55
+ * `/admin/**` example is `/v1/admin/**`.
56
+ *
57
+ * ## Deliberately excluded — this list is closed
58
+ *
59
+ * `?` (single character); character classes (`[a-z]`); brace alternation
60
+ * (`{a,b}`); regular expressions of any kind; leading or mid-path `**`;
61
+ * negation inside a glob (exclusion is `not:`, a filter-level feature);
62
+ * case-insensitive matching; escaping (there is no escape character, because
63
+ * there is nothing to escape: `*` in a literal position is already just a
64
+ * character that matches nothing); and **globbing on any filter field other
65
+ * than `path`** — `method`, `status`, `statusClass`, `requestMediaType` and
66
+ * `responseMediaType` stay closed unions.
67
+ *
68
+ * Anything not on the "in" list is out.
69
+ *
70
+ * The semantics above are stated as data in `bench/glob-corpus.ts`, and
71
+ * {@link matchesPathGlob} is checked against that corpus and nothing else.
72
+ */
73
+ /**
74
+ * Whether `glob` names `path`.
75
+ *
76
+ * Written directly from the decision list above, iteratively rather than
77
+ * recursively, so that reading it against the grammar is a one-pass job.
78
+ */
79
+ export declare function matchesPathGlob(glob: string, path: string): boolean;
80
+ /**
81
+ * Whether a path value carries a wildcard at all (decision 8).
82
+ *
83
+ * Only used for the wording of a diagnostic: an exact path that names nothing
84
+ * and a glob that names nothing are the same fault, but "no path is spelled
85
+ * that way" and "that glob matches nothing" are not the same sentence.
86
+ *
87
+ * Any `*` counts, not only a whole-segment one. `/v1/launch*` is a glob the
88
+ * user wrote — `PathGlob` accepts it and the grammar says so — it is just a
89
+ * glob that names nothing, because decision 1 makes `launch*` a literal
90
+ * segment. Testing for a whole-segment `*` instead told that user no path was
91
+ * *spelled* `/v1/launch*`, which is true and unhelpful.
92
+ */
93
+ export declare function hasWildcard(value: string): boolean;
94
+ //# sourceMappingURL=path-glob.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-glob.d.ts","sourceRoot":"","sources":["../../src/selectors/path-glob.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuEG;AAEH;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAkDnE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAElD"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * # The path-glob grammar — FROZEN
3
+ *
4
+ * A glob is a string compared against a path as the transaction catalog spells
5
+ * it: beginning with `/`, already carrying the API description's server base
6
+ * path, with `{param}` in template form. Both sides are compared **segment by
7
+ * segment**, where a segment is a maximal run of characters containing no `/`.
8
+ *
9
+ * ```ebnf
10
+ * glob = segment , { "/" , segment } , [ "/" , deep-wildcard ] ;
11
+ * segment = wildcard | literal-segment ;
12
+ * wildcard = "*" ; (* the WHOLE segment, never part *)
13
+ * deep-wildcard = "**" ; (* final position only *)
14
+ * literal-segment = { literal-char } ; (* matched byte-for-byte *)
15
+ * literal-char = ? any character except "/" ? ;
16
+ * ```
17
+ *
18
+ * ## The decisions (all of them explicit, all of them frozen)
19
+ *
20
+ * 1. **`*` matches exactly one whole segment.** It never matches part of a
21
+ * segment. `/foo*` is therefore **not a legal glob** — it is a segment whose
22
+ * literal text happens to be `foo*`, which matches no real path, and which
23
+ * `validate` reports as "matches nothing". It is deliberately *not* a
24
+ * separate "malformed glob" diagnostic: one diagnostic for "this names
25
+ * nothing" is the whole promise, and a second class of error would have to
26
+ * be kept in sync with the first.
27
+ * 2. **`*` matches exactly one segment — never zero.** `/users/*` does not
28
+ * match `/users`, and does not match `/users/` (whose trailing segment is
29
+ * the empty string).
30
+ * 3. **`*` never crosses `/`.** `/users/*` does not match `/users/1/orders`.
31
+ * 4. **A trailing `**` matches one *or more* segments.** `/admin/**` matches
32
+ * `/admin/users` and `/admin/users/42/roles`, and does **not** match
33
+ * `/admin` itself. A user who wants both writes two entries. This is the
34
+ * decision most likely to be argued later, so: it is one-or-more, and the
35
+ * reason is that `/admin/**` reads as "everything *under* /admin", and a
36
+ * zero-or-more reading makes `{ path: '/admin/**' }` silently a superset of
37
+ * `{ path: '/admin' }` — two different intents spelled the same way.
38
+ * 5. **`**` is legal only as the final segment.** Leading and mid-path `**`
39
+ * are out; mid-path `**` is deferred. A `**` anywhere else is, again, just a
40
+ * literal segment that matches nothing.
41
+ * 6. **`{id}` — and any other brace text — is a literal.** Real paths contain
42
+ * `{id}`, `{launchId}` and so on, because the loader keeps the template
43
+ * form. A glob spells them exactly as the path does. Braces are **not**
44
+ * metacharacters and never will be in this grammar.
45
+ * 7. **Matching is case-sensitive and exact.** `/Users/*` does not match
46
+ * `/users/1`. No normalisation, no trailing-slash tolerance, no percent
47
+ * decoding.
48
+ * 8. **A glob containing no metacharacter is just an exact path.** It has to
49
+ * equal a path the catalog holds, and it is validated by the same rule as
50
+ * every other glob: does it name at least one path.
51
+ * 9. **Anchoring.** A glob is matched against the whole path string, anchored
52
+ * at both ends — there is no implicit prefix or suffix wildcard. The string
53
+ * it is anchored against carries the server base path, so on a description
54
+ * whose server is `https://api.example.com/v1`, the glob for the spec's own
55
+ * `/admin/**` example is `/v1/admin/**`.
56
+ *
57
+ * ## Deliberately excluded — this list is closed
58
+ *
59
+ * `?` (single character); character classes (`[a-z]`); brace alternation
60
+ * (`{a,b}`); regular expressions of any kind; leading or mid-path `**`;
61
+ * negation inside a glob (exclusion is `not:`, a filter-level feature);
62
+ * case-insensitive matching; escaping (there is no escape character, because
63
+ * there is nothing to escape: `*` in a literal position is already just a
64
+ * character that matches nothing); and **globbing on any filter field other
65
+ * than `path`** — `method`, `status`, `statusClass`, `requestMediaType` and
66
+ * `responseMediaType` stay closed unions.
67
+ *
68
+ * Anything not on the "in" list is out.
69
+ *
70
+ * The semantics above are stated as data in `bench/glob-corpus.ts`, and
71
+ * {@link matchesPathGlob} is checked against that corpus and nothing else.
72
+ */
73
+ /**
74
+ * Whether `glob` names `path`.
75
+ *
76
+ * Written directly from the decision list above, iteratively rather than
77
+ * recursively, so that reading it against the grammar is a one-pass job.
78
+ */
79
+ export function matchesPathGlob(glob, path) {
80
+ const globSegments = glob.split('/');
81
+ const pathSegments = path.split('/');
82
+ const deep = globSegments.at(-1) === '**';
83
+ // Decision 5: `**` is legal only as the final segment. Anywhere else it is a
84
+ // literal segment, which is what the equality branch below gives it.
85
+ const fixed = deep ? globSegments.slice(0, -1) : globSegments;
86
+ // Decision 4: a trailing `**` needs one or more segments to consume, so the
87
+ // path must be strictly longer than the fixed prefix. Without a trailing
88
+ // `**` the two must have the same number of segments — decisions 2 and 3.
89
+ if (deep
90
+ ? pathSegments.length <= fixed.length
91
+ : pathSegments.length !== fixed.length) {
92
+ return false;
93
+ }
94
+ for (const [index, globSegment] of fixed.entries()) {
95
+ const pathSegment = pathSegments[index] ?? '';
96
+ if (globSegment === '*') {
97
+ // Decision 2: exactly one segment, never zero. An empty segment is not a
98
+ // segment.
99
+ if (pathSegment === '') {
100
+ return false;
101
+ }
102
+ continue;
103
+ }
104
+ // Decisions 1, 6, 7, 8: everything else is literal, compared exactly.
105
+ if (globSegment !== pathSegment) {
106
+ return false;
107
+ }
108
+ }
109
+ // Decision 4 again, for what the tail actually holds. The length test above
110
+ // proves the tail is non-empty as a list, not that it holds a segment: a
111
+ // path written with a trailing slash splits to one final empty string, so
112
+ // `/admin/**` matched `/admin/` and `/**` matched `/`. `*` already refuses an
113
+ // empty segment above, and "one or more segments" has to mean the same thing
114
+ // in both spellings.
115
+ if (deep && pathSegments.slice(fixed.length).every((s) => s === '')) {
116
+ return false;
117
+ }
118
+ return true;
119
+ }
120
+ /**
121
+ * Whether a path value carries a wildcard at all (decision 8).
122
+ *
123
+ * Only used for the wording of a diagnostic: an exact path that names nothing
124
+ * and a glob that names nothing are the same fault, but "no path is spelled
125
+ * that way" and "that glob matches nothing" are not the same sentence.
126
+ *
127
+ * Any `*` counts, not only a whole-segment one. `/v1/launch*` is a glob the
128
+ * user wrote — `PathGlob` accepts it and the grammar says so — it is just a
129
+ * glob that names nothing, because decision 1 makes `launch*` a literal
130
+ * segment. Testing for a whole-segment `*` instead told that user no path was
131
+ * *spelled* `/v1/launch*`, which is true and unhelpful.
132
+ */
133
+ export function hasWildcard(value) {
134
+ return value.includes('*');
135
+ }
136
+ //# sourceMappingURL=path-glob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-glob.js","sourceRoot":"","sources":["../../src/selectors/path-glob.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuEG;AAEH;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,IAAY;IACxD,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrC,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAE1C,6EAA6E;IAC7E,qEAAqE;IACrE,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;IAE9D,4EAA4E;IAC5E,yEAAyE;IACzE,0EAA0E;IAC1E,IACE,IAAI;QACF,CAAC,CAAC,YAAY,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM;QACrC,CAAC,CAAC,YAAY,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EACxC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QACnD,MAAM,WAAW,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAE9C,IAAI,WAAW,KAAK,GAAG,EAAE,CAAC;YACxB,yEAAyE;YACzE,WAAW;YACX,IAAI,WAAW,KAAK,EAAE,EAAE,CAAC;gBACvB,OAAO,KAAK,CAAC;YACf,CAAC;YAED,SAAS;QACX,CAAC;QAED,sEAAsE;QACtE,IAAI,WAAW,KAAK,WAAW,EAAE,CAAC;YAChC,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,yEAAyE;IACzE,0EAA0E;IAC1E,8EAA8E;IAC9E,6EAA6E;IAC7E,qBAAqB;IACrB,IAAI,IAAI,IAAI,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC;QACpE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,OAAO,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { ThymianBaseError, type ThymianHttpTransaction } from '@thymian/core';
2
+ /**
3
+ * Two transactions in the loaded format render the same selector. Fail-fast:
4
+ * nothing is dropped, overwritten or resolved "last wins". This is the one hard
5
+ * error at catalog build — rendering itself never rejects.
6
+ */
7
+ export declare function selectorCollisionError(selector: string, first: ThymianHttpTransaction, second: ThymianHttpTransaction): ThymianBaseError;
8
+ /**
9
+ * A well-formed selector that names no transaction in the loaded format. The
10
+ * near-miss candidates are supplied by the catalog, which is the only thing that
11
+ * knows what is loaded. A dangling selector is never auto-rebound to a
12
+ * "close enough" transaction.
13
+ */
14
+ export declare function unknownSelectorError(selector: string, suggestions: string[]): ThymianBaseError;
15
+ /** The value is not a selector at all. */
16
+ export declare function malformedSelectorError(value: string, hints?: string[]): ThymianBaseError;
17
+ //# sourceMappingURL=selector-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector-errors.d.ts","sourceRoot":"","sources":["../../src/selectors/selector-errors.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAGhB,KAAK,sBAAsB,EAE5B,MAAM,eAAe,CAAC;AA2HvB;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,sBAAsB,EAC7B,MAAM,EAAE,sBAAsB,GAC7B,gBAAgB,CAalB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EAAE,GACpB,gBAAgB,CASlB;AAED,0CAA0C;AAC1C,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAM,EAAO,GACnB,gBAAgB,CAalB"}
@@ -0,0 +1,139 @@
1
+ import { ThymianBaseError, thymianRequestToOrigin, } from '@thymian/core';
2
+ /**
3
+ * The selector grammar, written the way the reference pages write it. Kept next
4
+ * to the errors so the diagnostic and the parser cannot drift apart in wording.
5
+ */
6
+ const SELECTOR_GRAMMAR = 'METHOD SP path [ SP "(" requestMediaType ")" ] SP "->" SP status [ SP "(" responseMediaType ")" ]';
7
+ const SELECTOR_EXAMPLE = 'POST /astronauts (application/json) -> 201 (application/json)';
8
+ function locationToString(location) {
9
+ if (!location) {
10
+ return undefined;
11
+ }
12
+ const file = 'path' in location ? location.path : location.uri;
13
+ if (!location.position) {
14
+ return file;
15
+ }
16
+ return `${file}:${location.position.line}:${location.position.column}`;
17
+ }
18
+ /**
19
+ * `thymianRequestToOrigin` runs the origin through `normalizeUrl`, which throws
20
+ * when `protocol://host:port` is not a URL — an OpenAPI `servers` entry like
21
+ * `file:///tmp/api` leaves `host` empty. That failure must never replace the
22
+ * diagnostic the user actually needs to read.
23
+ *
24
+ * `normalizeUrl` returns `url.toString()`, which appends the empty path as a
25
+ * trailing slash. These sentences end in a period, so the slash is dropped: an
26
+ * origin has none.
27
+ */
28
+ function originOf(req) {
29
+ try {
30
+ const origin = thymianRequestToOrigin(req);
31
+ return origin.endsWith('/') ? origin.slice(0, -1) : origin;
32
+ }
33
+ catch {
34
+ return undefined;
35
+ }
36
+ }
37
+ /**
38
+ * The source pointer a diagnostic prints.
39
+ *
40
+ * `sourceName` is typed `string` on every node and edge but is not one at
41
+ * runtime: `plugin-openapi` defaults it to `document.info.title`, the config
42
+ * never requires an explicit name, and a missing `title` only warns. So it can
43
+ * arrive `''` or `undefined`, and an unnamed source is named as such — it must
44
+ * never render as `Source ""`, and it must never turn the diagnostic the user
45
+ * needs into a `TypeError`.
46
+ */
47
+ function describeSource(sourceName, origin, location) {
48
+ const where = locationToString(location);
49
+ return ((sourceName
50
+ ? `Source "${sourceName}" describes it`
51
+ : 'An unnamed source describes it') +
52
+ (origin ? ` at ${origin}` : '') +
53
+ (where ? ` (${where}).` : '.'));
54
+ }
55
+ /**
56
+ * Names a transaction from its RESPONSE node, and takes the location from the
57
+ * same producer — never from a mixture.
58
+ *
59
+ * Of the three carriers of a `sourceName`, the response node is the only one
60
+ * that belongs to the description that actually declared this transaction. A
61
+ * request node is deduped *across* sources, because `sourceName` sits in core's
62
+ * ignored hash properties, and the transaction edge then inherits that deduped
63
+ * name. A response node cannot be shared while a collision exists — its id is
64
+ * derived from the request id and the response's semantic hash, so two sources
65
+ * share it only when the whole transaction is identical, and then it merges and
66
+ * there is nothing to collide.
67
+ *
68
+ * Mixing carriers is what prints source A's name over source B's file: core
69
+ * rewrites an edge's `sourceName` but leaves the producer's `sourceLocation`
70
+ * alone. The edge is consulted for the location only, where it is the same
71
+ * producer's as the response node's.
72
+ */
73
+ function describeTransaction(transaction) {
74
+ return describeSource(transaction.thymianRes.sourceName, originOf(transaction.thymianReq), transaction.thymianRes.sourceLocation ??
75
+ transaction.transaction.sourceLocation);
76
+ }
77
+ /**
78
+ * The advice every collision gets, whichever kind it is.
79
+ *
80
+ * There is deliberately **no** same-source/cross-source classification. Telling
81
+ * the two apart needs a per-specification identity, and the graph carries only a
82
+ * source *name*: it defaults to `document.info.title` and is never required, so
83
+ * a staging and a production description of one API — the flagship cross-source
84
+ * cause — are indistinguishable by any name-based test, an unnamed description
85
+ * is indistinguishable from every other unnamed one, and a request node deduped
86
+ * across sources hands its name to an edge that never declared it.
87
+ *
88
+ * So the advice names both remedies without asserting which applies. The two
89
+ * lines above it already print each transaction's source and, where the
90
+ * description carries one, its file and position — the evidence a classifier
91
+ * never had, handed to the one reader who can use it.
92
+ */
93
+ const COLLISION_ADVICE = [
94
+ 'A selector is host-stripped and carries no query parameters or headers, so two transactions collide whenever they agree on method, path, status and media types — whether they come from one description or two.',
95
+ 'If the two lines above point at different documents, load those sources separately — a source-discriminator syntax does not exist.',
96
+ 'If they point at one document, give the two operations distinct paths, methods, statuses or media types.',
97
+ ];
98
+ /**
99
+ * Two transactions in the loaded format render the same selector. Fail-fast:
100
+ * nothing is dropped, overwritten or resolved "last wins". This is the one hard
101
+ * error at catalog build — rendering itself never rejects.
102
+ */
103
+ export function selectorCollisionError(selector, first, second) {
104
+ return new ThymianBaseError(`Two transactions resolve to the same selector "${selector}".`, {
105
+ name: 'SelectorCollisionError',
106
+ ref: 'https://thymian.dev/references/errors/selector-collision-error/',
107
+ suggestions: [
108
+ describeTransaction(first),
109
+ describeTransaction(second),
110
+ ...COLLISION_ADVICE,
111
+ ],
112
+ });
113
+ }
114
+ /**
115
+ * A well-formed selector that names no transaction in the loaded format. The
116
+ * near-miss candidates are supplied by the catalog, which is the only thing that
117
+ * knows what is loaded. A dangling selector is never auto-rebound to a
118
+ * "close enough" transaction.
119
+ */
120
+ export function unknownSelectorError(selector, suggestions) {
121
+ return new ThymianBaseError(`No transaction matches the selector "${selector}".`, {
122
+ name: 'UnknownSelectorError',
123
+ ref: 'https://thymian.dev/references/errors/unknown-selector-error/',
124
+ suggestions,
125
+ });
126
+ }
127
+ /** The value is not a selector at all. */
128
+ export function malformedSelectorError(value, hints = []) {
129
+ return new ThymianBaseError(`"${value}" is not a valid transaction selector.`, {
130
+ name: 'MalformedSelectorError',
131
+ ref: 'https://thymian.dev/references/errors/malformed-selector-error/',
132
+ suggestions: [
133
+ ...hints,
134
+ `Write a selector as ${SELECTOR_GRAMMAR}.`,
135
+ `For example: "${SELECTOR_EXAMPLE}".`,
136
+ ],
137
+ });
138
+ }
139
+ //# sourceMappingURL=selector-errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector-errors.js","sourceRoot":"","sources":["../../src/selectors/selector-errors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import { canonicalPath, encodeMediaType, encodeMethod, encodePath, formatRequestSelector, formatResponseSelector, formatSelector, type Selector, selectorForTransaction } from '@thymian/core';
2
+ /**
3
+ * The selector grammar, plugin side: everything that reads a selector.
4
+ *
5
+ * **Rendering lives in `@thymian/core`** (`selector/render-selector.ts`) and is
6
+ * re-exported here, because a selector is two things at once — the sampler's
7
+ * *address* of a Transaction and the application's *label* for one — and every
8
+ * surface core prints has to render the same grammar the sampler resolves
9
+ * (ADR-0022). What stays here is what only an address needs: parsing back,
10
+ * near-miss diagnostics and the catalog's ordering.
11
+ *
12
+ * The reader below is the inverse of core's encoders. The two are one grammar
13
+ * split across two packages, so the round-trip property — every rendered
14
+ * selector parses, and parses back to what it was rendered from — is the test
15
+ * that keeps them honest.
16
+ */
17
+ export { canonicalPath, encodeMediaType, encodeMethod, encodePath, formatRequestSelector, formatResponseSelector, formatSelector, type Selector, selectorForTransaction, };
18
+ export type SelectorParts = {
19
+ method: string;
20
+ path: string;
21
+ requestMediaType?: string;
22
+ status: number;
23
+ responseMediaType?: string;
24
+ };
25
+ /**
26
+ * Total order on selectors, by UTF-16 code unit.
27
+ *
28
+ * Deliberately not `localeCompare`: the catalog's order is the order of the
29
+ * generated type surface, and a locale-sensitive comparison would make the
30
+ * committed types depend on the machine that generated them.
31
+ */
32
+ export declare function compareSelectors(a: Selector, b: Selector): number;
33
+ /**
34
+ * Parses a selector back into its components.
35
+ *
36
+ * For DIAGNOSTICS ONLY. Resolution is a map lookup
37
+ * (`TransactionCatalog.resolve`); a parse happens only after a miss, to decide
38
+ * whether the value was malformed or merely unknown. Do not move a parse onto
39
+ * the happy path.
40
+ */
41
+ export declare function parseSelector(value: string): SelectorParts;
42
+ /** Whether `value` is a syntactically well-formed selector. */
43
+ export declare function isSelector(value: string): boolean;
44
+ /**
45
+ * The suggestions a malformed selector's diagnostic carries — the grammar,
46
+ * an example, and a canonical-form guess when the value is close. Never
47
+ * throws.
48
+ *
49
+ * Reads `malformedSelectorError`'s own message composition rather than
50
+ * duplicating it, so a hook-target diagnostic for a malformed Selector and
51
+ * the error `catalog.resolve` throws for one typed on the CLI say exactly the
52
+ * same thing — one grammar, one set of hints, two call sites.
53
+ */
54
+ export declare function malformedSelectorHints(value: string): string[];
55
+ //# sourceMappingURL=selector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector.d.ts","sourceRoot":"","sources":["../../src/selectors/selector.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,eAAe,EACf,YAAY,EACZ,UAAU,EAEV,qBAAqB,EACrB,sBAAsB,EACtB,cAAc,EACd,KAAK,QAAQ,EAEb,sBAAsB,EACvB,MAAM,eAAe,CAAC;AAIvB;;;;;;;;;;;;;;GAcG;AACH,OAAO,EACL,aAAa,EACb,eAAe,EACf,YAAY,EACZ,UAAU,EACV,qBAAqB,EACrB,sBAAsB,EACtB,cAAc,EACd,KAAK,QAAQ,EACb,sBAAsB,GACvB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAwGF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAEjE;AA2HD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,CAQ1D;AAED,+DAA+D;AAC/D,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAI9D"}