@softprobe/softprobe-js 2.0.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 (328) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +202 -0
  3. package/bin/softprobe +15 -0
  4. package/dist/api/baggage.d.ts +13 -0
  5. package/dist/api/baggage.d.ts.map +1 -0
  6. package/dist/api/baggage.js +32 -0
  7. package/dist/api/baggage.js.map +1 -0
  8. package/dist/api/compare.d.ts +16 -0
  9. package/dist/api/compare.d.ts.map +1 -0
  10. package/dist/api/compare.js +77 -0
  11. package/dist/api/compare.js.map +1 -0
  12. package/dist/api.d.ts +63 -0
  13. package/dist/api.d.ts.map +1 -0
  14. package/dist/api.js +104 -0
  15. package/dist/api.js.map +1 -0
  16. package/dist/bindings/http-span.d.ts +6 -0
  17. package/dist/bindings/http-span.d.ts.map +1 -0
  18. package/dist/bindings/http-span.js +11 -0
  19. package/dist/bindings/http-span.js.map +1 -0
  20. package/dist/bindings/postgres-span.d.ts +6 -0
  21. package/dist/bindings/postgres-span.d.ts.map +1 -0
  22. package/dist/bindings/postgres-span.js +11 -0
  23. package/dist/bindings/postgres-span.js.map +1 -0
  24. package/dist/bindings/redis-span.d.ts +6 -0
  25. package/dist/bindings/redis-span.d.ts.map +1 -0
  26. package/dist/bindings/redis-span.js +11 -0
  27. package/dist/bindings/redis-span.js.map +1 -0
  28. package/dist/bindings/test-span.d.ts +6 -0
  29. package/dist/bindings/test-span.d.ts.map +1 -0
  30. package/dist/bindings/test-span.js +9 -0
  31. package/dist/bindings/test-span.js.map +1 -0
  32. package/dist/bootstrap/otel/framework-mutator.d.ts +20 -0
  33. package/dist/bootstrap/otel/framework-mutator.d.ts.map +1 -0
  34. package/dist/bootstrap/otel/framework-mutator.js +144 -0
  35. package/dist/bootstrap/otel/framework-mutator.js.map +1 -0
  36. package/dist/bootstrap/otel/inject.d.ts +17 -0
  37. package/dist/bootstrap/otel/inject.d.ts.map +1 -0
  38. package/dist/bootstrap/otel/inject.js +28 -0
  39. package/dist/bootstrap/otel/inject.js.map +1 -0
  40. package/dist/bootstrap/otel/mutator.d.ts +17 -0
  41. package/dist/bootstrap/otel/mutator.d.ts.map +1 -0
  42. package/dist/bootstrap/otel/mutator.js +51 -0
  43. package/dist/bootstrap/otel/mutator.js.map +1 -0
  44. package/dist/capture/express.d.ts +6 -0
  45. package/dist/capture/express.d.ts.map +1 -0
  46. package/dist/capture/express.js +11 -0
  47. package/dist/capture/express.js.map +1 -0
  48. package/dist/capture/fastify.d.ts +5 -0
  49. package/dist/capture/fastify.d.ts.map +1 -0
  50. package/dist/capture/fastify.js +9 -0
  51. package/dist/capture/fastify.js.map +1 -0
  52. package/dist/capture/framework-mutator.d.ts +20 -0
  53. package/dist/capture/framework-mutator.d.ts.map +1 -0
  54. package/dist/capture/framework-mutator.js +144 -0
  55. package/dist/capture/framework-mutator.js.map +1 -0
  56. package/dist/capture/http-inbound.d.ts +28 -0
  57. package/dist/capture/http-inbound.d.ts.map +1 -0
  58. package/dist/capture/http-inbound.js +40 -0
  59. package/dist/capture/http-inbound.js.map +1 -0
  60. package/dist/capture/inject.d.ts +17 -0
  61. package/dist/capture/inject.d.ts.map +1 -0
  62. package/dist/capture/inject.js +28 -0
  63. package/dist/capture/inject.js.map +1 -0
  64. package/dist/capture/mutator.d.ts +17 -0
  65. package/dist/capture/mutator.d.ts.map +1 -0
  66. package/dist/capture/mutator.js +51 -0
  67. package/dist/capture/mutator.js.map +1 -0
  68. package/dist/capture/postgres.d.ts +6 -0
  69. package/dist/capture/postgres.d.ts.map +1 -0
  70. package/dist/capture/postgres.js +11 -0
  71. package/dist/capture/postgres.js.map +1 -0
  72. package/dist/capture/redis.d.ts +5 -0
  73. package/dist/capture/redis.d.ts.map +1 -0
  74. package/dist/capture/redis.js +10 -0
  75. package/dist/capture/redis.js.map +1 -0
  76. package/dist/capture/store-accessor.d.ts +11 -0
  77. package/dist/capture/store-accessor.d.ts.map +1 -0
  78. package/dist/capture/store-accessor.js +19 -0
  79. package/dist/capture/store-accessor.js.map +1 -0
  80. package/dist/capture/stream-tap.d.ts +5 -0
  81. package/dist/capture/stream-tap.d.ts.map +1 -0
  82. package/dist/capture/stream-tap.js +9 -0
  83. package/dist/capture/stream-tap.js.map +1 -0
  84. package/dist/cli/diff-reporter.d.ts +26 -0
  85. package/dist/cli/diff-reporter.d.ts.map +1 -0
  86. package/dist/cli/diff-reporter.js +163 -0
  87. package/dist/cli/diff-reporter.js.map +1 -0
  88. package/dist/cli/diff.d.ts +18 -0
  89. package/dist/cli/diff.d.ts.map +1 -0
  90. package/dist/cli/diff.js +105 -0
  91. package/dist/cli/diff.js.map +1 -0
  92. package/dist/cli.d.ts +7 -0
  93. package/dist/cli.d.ts.map +1 -0
  94. package/dist/cli.js +84 -0
  95. package/dist/cli.js.map +1 -0
  96. package/dist/config/config-manager.d.ts +20 -0
  97. package/dist/config/config-manager.d.ts.map +1 -0
  98. package/dist/config/config-manager.js +46 -0
  99. package/dist/config/config-manager.js.map +1 -0
  100. package/dist/context.d.ts +98 -0
  101. package/dist/context.d.ts.map +1 -0
  102. package/dist/context.js +198 -0
  103. package/dist/context.js.map +1 -0
  104. package/dist/core/bindings/http-span.d.ts +39 -0
  105. package/dist/core/bindings/http-span.d.ts.map +1 -0
  106. package/dist/core/bindings/http-span.js +55 -0
  107. package/dist/core/bindings/http-span.js.map +1 -0
  108. package/dist/core/bindings/index.d.ts +12 -0
  109. package/dist/core/bindings/index.d.ts.map +1 -0
  110. package/dist/core/bindings/index.js +21 -0
  111. package/dist/core/bindings/index.js.map +1 -0
  112. package/dist/core/bindings/postgres-span.d.ts +41 -0
  113. package/dist/core/bindings/postgres-span.d.ts.map +1 -0
  114. package/dist/core/bindings/postgres-span.js +57 -0
  115. package/dist/core/bindings/postgres-span.js.map +1 -0
  116. package/dist/core/bindings/redis-span.d.ts +40 -0
  117. package/dist/core/bindings/redis-span.d.ts.map +1 -0
  118. package/dist/core/bindings/redis-span.js +65 -0
  119. package/dist/core/bindings/redis-span.js.map +1 -0
  120. package/dist/core/bindings/test-span.d.ts +15 -0
  121. package/dist/core/bindings/test-span.d.ts.map +1 -0
  122. package/dist/core/bindings/test-span.js +24 -0
  123. package/dist/core/bindings/test-span.js.map +1 -0
  124. package/dist/core/cassette/capture-store-accessor.d.ts +11 -0
  125. package/dist/core/cassette/capture-store-accessor.d.ts.map +1 -0
  126. package/dist/core/cassette/capture-store-accessor.js +19 -0
  127. package/dist/core/cassette/capture-store-accessor.js.map +1 -0
  128. package/dist/core/cassette/context-request-storage.d.ts +14 -0
  129. package/dist/core/cassette/context-request-storage.d.ts.map +1 -0
  130. package/dist/core/cassette/context-request-storage.js +24 -0
  131. package/dist/core/cassette/context-request-storage.js.map +1 -0
  132. package/dist/core/cassette/index.d.ts +8 -0
  133. package/dist/core/cassette/index.d.ts.map +1 -0
  134. package/dist/core/cassette/index.js +16 -0
  135. package/dist/core/cassette/index.js.map +1 -0
  136. package/dist/core/cassette/ndjson-cassette.d.ts +22 -0
  137. package/dist/core/cassette/ndjson-cassette.d.ts.map +1 -0
  138. package/dist/core/cassette/ndjson-cassette.js +56 -0
  139. package/dist/core/cassette/ndjson-cassette.js.map +1 -0
  140. package/dist/core/cassette/request-storage.d.ts +16 -0
  141. package/dist/core/cassette/request-storage.d.ts.map +1 -0
  142. package/dist/core/cassette/request-storage.js +22 -0
  143. package/dist/core/cassette/request-storage.js.map +1 -0
  144. package/dist/core/context/index.d.ts +5 -0
  145. package/dist/core/context/index.d.ts.map +1 -0
  146. package/dist/core/context/index.js +9 -0
  147. package/dist/core/context/index.js.map +1 -0
  148. package/dist/core/contracts/index.d.ts +5 -0
  149. package/dist/core/contracts/index.d.ts.map +1 -0
  150. package/dist/core/contracts/index.js +3 -0
  151. package/dist/core/contracts/index.js.map +1 -0
  152. package/dist/core/identifier.d.ts +20 -0
  153. package/dist/core/identifier.d.ts.map +1 -0
  154. package/dist/core/identifier.js +31 -0
  155. package/dist/core/identifier.js.map +1 -0
  156. package/dist/core/index.d.ts +11 -0
  157. package/dist/core/index.d.ts.map +1 -0
  158. package/dist/core/index.js +27 -0
  159. package/dist/core/index.js.map +1 -0
  160. package/dist/core/matcher/extract-key.d.ts +41 -0
  161. package/dist/core/matcher/extract-key.d.ts.map +1 -0
  162. package/dist/core/matcher/extract-key.js +80 -0
  163. package/dist/core/matcher/extract-key.js.map +1 -0
  164. package/dist/core/matcher/index.d.ts +9 -0
  165. package/dist/core/matcher/index.d.ts.map +1 -0
  166. package/dist/core/matcher/index.js +24 -0
  167. package/dist/core/matcher/index.js.map +1 -0
  168. package/dist/core/matcher/matcher.d.ts +25 -0
  169. package/dist/core/matcher/matcher.d.ts.map +1 -0
  170. package/dist/core/matcher/matcher.js +83 -0
  171. package/dist/core/matcher/matcher.js.map +1 -0
  172. package/dist/core/matcher/softprobe-matcher.d.ts +41 -0
  173. package/dist/core/matcher/softprobe-matcher.d.ts.map +1 -0
  174. package/dist/core/matcher/softprobe-matcher.js +92 -0
  175. package/dist/core/matcher/softprobe-matcher.js.map +1 -0
  176. package/dist/core/matcher/store-accessor.d.ts +14 -0
  177. package/dist/core/matcher/store-accessor.d.ts.map +1 -0
  178. package/dist/core/matcher/store-accessor.js +25 -0
  179. package/dist/core/matcher/store-accessor.js.map +1 -0
  180. package/dist/core/matcher/topology.d.ts +37 -0
  181. package/dist/core/matcher/topology.d.ts.map +1 -0
  182. package/dist/core/matcher/topology.js +72 -0
  183. package/dist/core/matcher/topology.js.map +1 -0
  184. package/dist/core/runtime/architecture-guard.d.ts +9 -0
  185. package/dist/core/runtime/architecture-guard.d.ts.map +1 -0
  186. package/dist/core/runtime/architecture-guard.js +131 -0
  187. package/dist/core/runtime/architecture-guard.js.map +1 -0
  188. package/dist/core/runtime/index.d.ts +6 -0
  189. package/dist/core/runtime/index.d.ts.map +1 -0
  190. package/dist/core/runtime/index.js +11 -0
  191. package/dist/core/runtime/index.js.map +1 -0
  192. package/dist/identifier.d.ts +5 -0
  193. package/dist/identifier.d.ts.map +1 -0
  194. package/dist/identifier.js +11 -0
  195. package/dist/identifier.js.map +1 -0
  196. package/dist/index.d.ts +5 -0
  197. package/dist/index.d.ts.map +1 -0
  198. package/dist/index.js +21 -0
  199. package/dist/index.js.map +1 -0
  200. package/dist/init.d.ts +21 -0
  201. package/dist/init.d.ts.map +1 -0
  202. package/dist/init.js +51 -0
  203. package/dist/init.js.map +1 -0
  204. package/dist/instrumentations/common/http/context-headers.d.ts +9 -0
  205. package/dist/instrumentations/common/http/context-headers.d.ts.map +1 -0
  206. package/dist/instrumentations/common/http/context-headers.js +16 -0
  207. package/dist/instrumentations/common/http/context-headers.js.map +1 -0
  208. package/dist/instrumentations/common/http/inbound-capture.d.ts +17 -0
  209. package/dist/instrumentations/common/http/inbound-capture.d.ts.map +1 -0
  210. package/dist/instrumentations/common/http/inbound-capture.js +41 -0
  211. package/dist/instrumentations/common/http/inbound-capture.js.map +1 -0
  212. package/dist/instrumentations/common/http/inbound-record.d.ts +28 -0
  213. package/dist/instrumentations/common/http/inbound-record.d.ts.map +1 -0
  214. package/dist/instrumentations/common/http/inbound-record.js +40 -0
  215. package/dist/instrumentations/common/http/inbound-record.js.map +1 -0
  216. package/dist/instrumentations/common/http/span-adapter.d.ts +6 -0
  217. package/dist/instrumentations/common/http/span-adapter.d.ts.map +1 -0
  218. package/dist/instrumentations/common/http/span-adapter.js +12 -0
  219. package/dist/instrumentations/common/http/span-adapter.js.map +1 -0
  220. package/dist/instrumentations/common/http/stream-tap.d.ts +28 -0
  221. package/dist/instrumentations/common/http/stream-tap.d.ts.map +1 -0
  222. package/dist/instrumentations/common/http/stream-tap.js +61 -0
  223. package/dist/instrumentations/common/http/stream-tap.js.map +1 -0
  224. package/dist/instrumentations/express/capture.d.ts +26 -0
  225. package/dist/instrumentations/express/capture.d.ts.map +1 -0
  226. package/dist/instrumentations/express/capture.js +80 -0
  227. package/dist/instrumentations/express/capture.js.map +1 -0
  228. package/dist/instrumentations/express/index.d.ts +7 -0
  229. package/dist/instrumentations/express/index.d.ts.map +1 -0
  230. package/dist/instrumentations/express/index.js +15 -0
  231. package/dist/instrumentations/express/index.js.map +1 -0
  232. package/dist/instrumentations/express/replay.d.ts +10 -0
  233. package/dist/instrumentations/express/replay.d.ts.map +1 -0
  234. package/dist/instrumentations/express/replay.js +16 -0
  235. package/dist/instrumentations/express/replay.js.map +1 -0
  236. package/dist/instrumentations/fastify/capture.d.ts +13 -0
  237. package/dist/instrumentations/fastify/capture.d.ts.map +1 -0
  238. package/dist/instrumentations/fastify/capture.js +65 -0
  239. package/dist/instrumentations/fastify/capture.js.map +1 -0
  240. package/dist/instrumentations/fastify/index.d.ts +7 -0
  241. package/dist/instrumentations/fastify/index.d.ts.map +1 -0
  242. package/dist/instrumentations/fastify/index.js +13 -0
  243. package/dist/instrumentations/fastify/index.js.map +1 -0
  244. package/dist/instrumentations/fastify/replay.d.ts +13 -0
  245. package/dist/instrumentations/fastify/replay.d.ts.map +1 -0
  246. package/dist/instrumentations/fastify/replay.js +24 -0
  247. package/dist/instrumentations/fastify/replay.js.map +1 -0
  248. package/dist/instrumentations/fetch/index.d.ts +5 -0
  249. package/dist/instrumentations/fetch/index.d.ts.map +1 -0
  250. package/dist/instrumentations/fetch/index.js +10 -0
  251. package/dist/instrumentations/fetch/index.js.map +1 -0
  252. package/dist/instrumentations/fetch/replay.d.ts +19 -0
  253. package/dist/instrumentations/fetch/replay.d.ts.map +1 -0
  254. package/dist/instrumentations/fetch/replay.js +259 -0
  255. package/dist/instrumentations/fetch/replay.js.map +1 -0
  256. package/dist/instrumentations/postgres/capture.d.ts +46 -0
  257. package/dist/instrumentations/postgres/capture.d.ts.map +1 -0
  258. package/dist/instrumentations/postgres/capture.js +68 -0
  259. package/dist/instrumentations/postgres/capture.js.map +1 -0
  260. package/dist/instrumentations/postgres/index.d.ts +6 -0
  261. package/dist/instrumentations/postgres/index.d.ts.map +1 -0
  262. package/dist/instrumentations/postgres/index.js +14 -0
  263. package/dist/instrumentations/postgres/index.js.map +1 -0
  264. package/dist/instrumentations/postgres/replay.d.ts +20 -0
  265. package/dist/instrumentations/postgres/replay.d.ts.map +1 -0
  266. package/dist/instrumentations/postgres/replay.js +163 -0
  267. package/dist/instrumentations/postgres/replay.js.map +1 -0
  268. package/dist/instrumentations/redis/capture.d.ts +20 -0
  269. package/dist/instrumentations/redis/capture.d.ts.map +1 -0
  270. package/dist/instrumentations/redis/capture.js +61 -0
  271. package/dist/instrumentations/redis/capture.js.map +1 -0
  272. package/dist/instrumentations/redis/index.d.ts +6 -0
  273. package/dist/instrumentations/redis/index.d.ts.map +1 -0
  274. package/dist/instrumentations/redis/index.js +13 -0
  275. package/dist/instrumentations/redis/index.js.map +1 -0
  276. package/dist/instrumentations/redis/replay.d.ts +24 -0
  277. package/dist/instrumentations/redis/replay.d.ts.map +1 -0
  278. package/dist/instrumentations/redis/replay.js +173 -0
  279. package/dist/instrumentations/redis/replay.js.map +1 -0
  280. package/dist/replay/express.d.ts +5 -0
  281. package/dist/replay/express.d.ts.map +1 -0
  282. package/dist/replay/express.js +9 -0
  283. package/dist/replay/express.js.map +1 -0
  284. package/dist/replay/extract-key.d.ts +41 -0
  285. package/dist/replay/extract-key.d.ts.map +1 -0
  286. package/dist/replay/extract-key.js +80 -0
  287. package/dist/replay/extract-key.js.map +1 -0
  288. package/dist/replay/fastify.d.ts +5 -0
  289. package/dist/replay/fastify.d.ts.map +1 -0
  290. package/dist/replay/fastify.js +9 -0
  291. package/dist/replay/fastify.js.map +1 -0
  292. package/dist/replay/http.d.ts +5 -0
  293. package/dist/replay/http.d.ts.map +1 -0
  294. package/dist/replay/http.js +10 -0
  295. package/dist/replay/http.js.map +1 -0
  296. package/dist/replay/matcher.d.ts +25 -0
  297. package/dist/replay/matcher.d.ts.map +1 -0
  298. package/dist/replay/matcher.js +83 -0
  299. package/dist/replay/matcher.js.map +1 -0
  300. package/dist/replay/postgres.d.ts +5 -0
  301. package/dist/replay/postgres.d.ts.map +1 -0
  302. package/dist/replay/postgres.js +10 -0
  303. package/dist/replay/postgres.js.map +1 -0
  304. package/dist/replay/redis.d.ts +5 -0
  305. package/dist/replay/redis.d.ts.map +1 -0
  306. package/dist/replay/redis.js +10 -0
  307. package/dist/replay/redis.js.map +1 -0
  308. package/dist/replay/softprobe-matcher.d.ts +41 -0
  309. package/dist/replay/softprobe-matcher.d.ts.map +1 -0
  310. package/dist/replay/softprobe-matcher.js +92 -0
  311. package/dist/replay/softprobe-matcher.js.map +1 -0
  312. package/dist/replay/store-accessor.d.ts +14 -0
  313. package/dist/replay/store-accessor.d.ts.map +1 -0
  314. package/dist/replay/store-accessor.js +26 -0
  315. package/dist/replay/store-accessor.js.map +1 -0
  316. package/dist/replay/topology.d.ts +37 -0
  317. package/dist/replay/topology.d.ts.map +1 -0
  318. package/dist/replay/topology.js +72 -0
  319. package/dist/replay/topology.js.map +1 -0
  320. package/dist/store/cassette-store.d.ts +36 -0
  321. package/dist/store/cassette-store.d.ts.map +1 -0
  322. package/dist/store/cassette-store.js +66 -0
  323. package/dist/store/cassette-store.js.map +1 -0
  324. package/dist/types/schema.d.ts +114 -0
  325. package/dist/types/schema.d.ts.map +1 -0
  326. package/dist/types/schema.js +13 -0
  327. package/dist/types/schema.js.map +1 -0
  328. package/package.json +88 -0
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Pure identifier builders for capture/replay matching.
3
+ * Keys must be built identically in capture and replay (design §6.2).
4
+ */
5
+ /**
6
+ * HTTP identifier: "METHOD url" (method uppercased).
7
+ * @example httpIdentifier('POST', 'https://a/b') => 'POST https://a/b'
8
+ */
9
+ export declare function httpIdentifier(method: string, url: string): string;
10
+ /**
11
+ * Redis identifier: "CMD arg1 arg2 ..." (cmd uppercased, args joined by space).
12
+ * @example redisIdentifier('get', ['k']) => 'GET k'
13
+ */
14
+ export declare function redisIdentifier(cmd: string, args: string[]): string;
15
+ /**
16
+ * Postgres identifier: pass-through of SQL string (normalization deferred).
17
+ * Must be consistent in capture and replay; optional normalizeSql later.
18
+ */
19
+ export declare function pgIdentifier(sql: string): string;
20
+ //# sourceMappingURL=identifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifier.d.ts","sourceRoot":"","sources":["../../src/core/identifier.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAElE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAEnE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD"}
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ /**
3
+ * Pure identifier builders for capture/replay matching.
4
+ * Keys must be built identically in capture and replay (design §6.2).
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.httpIdentifier = httpIdentifier;
8
+ exports.redisIdentifier = redisIdentifier;
9
+ exports.pgIdentifier = pgIdentifier;
10
+ /**
11
+ * HTTP identifier: "METHOD url" (method uppercased).
12
+ * @example httpIdentifier('POST', 'https://a/b') => 'POST https://a/b'
13
+ */
14
+ function httpIdentifier(method, url) {
15
+ return `${method.toUpperCase()} ${url}`;
16
+ }
17
+ /**
18
+ * Redis identifier: "CMD arg1 arg2 ..." (cmd uppercased, args joined by space).
19
+ * @example redisIdentifier('get', ['k']) => 'GET k'
20
+ */
21
+ function redisIdentifier(cmd, args) {
22
+ return `${cmd.toUpperCase()} ${args.join(' ')}`.trim();
23
+ }
24
+ /**
25
+ * Postgres identifier: pass-through of SQL string (normalization deferred).
26
+ * Must be consistent in capture and replay; optional normalizeSql later.
27
+ */
28
+ function pgIdentifier(sql) {
29
+ return sql;
30
+ }
31
+ //# sourceMappingURL=identifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifier.js","sourceRoot":"","sources":["../../src/core/identifier.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAMH,wCAEC;AAMD,0CAEC;AAMD,oCAEC;AAtBD;;;GAGG;AACH,SAAgB,cAAc,CAAC,MAAc,EAAE,GAAW;IACxD,OAAO,GAAG,MAAM,CAAC,WAAW,EAAE,IAAI,GAAG,EAAE,CAAC;AAC1C,CAAC;AAED;;;GAGG;AACH,SAAgB,eAAe,CAAC,GAAW,EAAE,IAAc;IACzD,OAAO,GAAG,GAAG,CAAC,WAAW,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;AACzD,CAAC;AAED;;;GAGG;AACH,SAAgB,YAAY,CAAC,GAAW;IACtC,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Foundation package surface.
3
+ */
4
+ export * from './contracts';
5
+ export * from './runtime';
6
+ export * from './context';
7
+ export * from './cassette';
8
+ export * from './bindings';
9
+ export * from './identifier';
10
+ export * from './matcher';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC"}
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ /**
18
+ * Foundation package surface.
19
+ */
20
+ __exportStar(require("./contracts"), exports);
21
+ __exportStar(require("./runtime"), exports);
22
+ __exportStar(require("./context"), exports);
23
+ __exportStar(require("./cassette"), exports);
24
+ __exportStar(require("./bindings"), exports);
25
+ __exportStar(require("./identifier"), exports);
26
+ __exportStar(require("./matcher"), exports);
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;GAEG;AACH,8CAA4B;AAC5B,4CAA0B;AAC1B,4CAA0B;AAC1B,6CAA2B;AAC3B,6CAA2B;AAC3B,+CAA6B;AAC7B,4CAA0B"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Key extraction and candidate filtering for default matcher (design §7.3).
3
+ * Derives { protocol, identifier } from a span; filters outbound records by key.
4
+ */
5
+ import type { MatcherFn, SoftprobeCassetteRecord } from '../../types/schema';
6
+ /** Key used for flat matching: protocol + identifier. */
7
+ export type SpanKey = {
8
+ protocol: 'postgres' | 'redis' | 'http';
9
+ identifier: string;
10
+ };
11
+ /** Span-like with readable attributes (OTel Span or test span). */
12
+ type ReadableSpan = {
13
+ attributes?: Record<string, unknown>;
14
+ } | undefined;
15
+ /**
16
+ * Extracts matching key from a span using PostgresSpan, RedisSpan, and HttpSpan.
17
+ * Returns the first non-null fromSpan result as { protocol, identifier }; unknown span yields null.
18
+ */
19
+ export declare function extractKeyFromSpan(span: ReadableSpan): SpanKey | null;
20
+ /**
21
+ * Returns only outbound records whose protocol and identifier match the key.
22
+ */
23
+ export declare function filterOutboundCandidates(records: SoftprobeCassetteRecord[], key: SpanKey): SoftprobeCassetteRecord[];
24
+ /**
25
+ * Per-key call sequence for default matcher: tracks the next candidate index
26
+ * per (protocol, identifier). getAndIncrement returns the current index and advances.
27
+ */
28
+ export declare class CallSeq {
29
+ private next;
30
+ /**
31
+ * Returns the current index for this key and increments for the next call.
32
+ */
33
+ getAndIncrement(key: SpanKey): number;
34
+ }
35
+ /**
36
+ * Returns a MatcherFn that uses extractKeyFromSpan, filterOutboundCandidates,
37
+ * and CallSeq to pick an outbound record and return MOCK with its responsePayload.
38
+ */
39
+ export declare function createDefaultMatcher(): MatcherFn;
40
+ export {};
41
+ //# sourceMappingURL=extract-key.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-key.d.ts","sourceRoot":"","sources":["../../../src/core/matcher/extract-key.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAEV,SAAS,EACT,uBAAuB,EACxB,MAAM,oBAAoB,CAAC;AAK5B,yDAAyD;AACzD,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,EAAE,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;IACxC,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,mEAAmE;AACnE,KAAK,YAAY,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG,SAAS,CAAC;AAEzE;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,GAAG,IAAI,CAWrE;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,uBAAuB,EAAE,EAClC,GAAG,EAAE,OAAO,GACX,uBAAuB,EAAE,CAO3B;AAMD;;;GAGG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,IAAI,CAA6B;IAEzC;;OAEG;IACH,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM;CAMtC;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,SAAS,CAYhD"}
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ /**
3
+ * Key extraction and candidate filtering for default matcher (design §7.3).
4
+ * Derives { protocol, identifier } from a span; filters outbound records by key.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.CallSeq = void 0;
8
+ exports.extractKeyFromSpan = extractKeyFromSpan;
9
+ exports.filterOutboundCandidates = filterOutboundCandidates;
10
+ exports.createDefaultMatcher = createDefaultMatcher;
11
+ const postgres_span_1 = require("../bindings/postgres-span");
12
+ const redis_span_1 = require("../bindings/redis-span");
13
+ const http_span_1 = require("../bindings/http-span");
14
+ /**
15
+ * Extracts matching key from a span using PostgresSpan, RedisSpan, and HttpSpan.
16
+ * Returns the first non-null fromSpan result as { protocol, identifier }; unknown span yields null.
17
+ */
18
+ function extractKeyFromSpan(span) {
19
+ const pg = postgres_span_1.PostgresSpan.fromSpan(span);
20
+ if (pg)
21
+ return { protocol: 'postgres', identifier: pg.identifier };
22
+ const redis = redis_span_1.RedisSpan.fromSpan(span);
23
+ if (redis)
24
+ return { protocol: 'redis', identifier: redis.identifier };
25
+ const http = http_span_1.HttpSpan.fromSpan(span);
26
+ if (http)
27
+ return { protocol: 'http', identifier: http.identifier };
28
+ return null;
29
+ }
30
+ /**
31
+ * Returns only outbound records whose protocol and identifier match the key.
32
+ */
33
+ function filterOutboundCandidates(records, key) {
34
+ return records.filter((r) => r.type === 'outbound' &&
35
+ r.protocol === key.protocol &&
36
+ r.identifier === key.identifier);
37
+ }
38
+ function seqKey(key) {
39
+ return `${key.protocol}::${key.identifier}`;
40
+ }
41
+ /**
42
+ * Per-key call sequence for default matcher: tracks the next candidate index
43
+ * per (protocol, identifier). getAndIncrement returns the current index and advances.
44
+ */
45
+ class CallSeq {
46
+ constructor() {
47
+ this.next = new Map();
48
+ }
49
+ /**
50
+ * Returns the current index for this key and increments for the next call.
51
+ */
52
+ getAndIncrement(key) {
53
+ const k = seqKey(key);
54
+ const idx = this.next.get(k) ?? 0;
55
+ this.next.set(k, idx + 1);
56
+ return idx;
57
+ }
58
+ }
59
+ exports.CallSeq = CallSeq;
60
+ /**
61
+ * Returns a MatcherFn that uses extractKeyFromSpan, filterOutboundCandidates,
62
+ * and CallSeq to pick an outbound record and return MOCK with its responsePayload.
63
+ */
64
+ function createDefaultMatcher() {
65
+ const callSeq = new CallSeq();
66
+ return (span, records) => {
67
+ const key = extractKeyFromSpan(span);
68
+ if (!key)
69
+ return { action: 'CONTINUE' };
70
+ const candidates = filterOutboundCandidates(records, key);
71
+ if (candidates.length === 0)
72
+ return { action: 'CONTINUE' };
73
+ const idx = callSeq.getAndIncrement(key);
74
+ if (idx >= candidates.length)
75
+ return { action: 'CONTINUE' };
76
+ const record = candidates[idx];
77
+ return { action: 'MOCK', payload: record?.responsePayload, traceId: record?.traceId };
78
+ };
79
+ }
80
+ //# sourceMappingURL=extract-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract-key.js","sourceRoot":"","sources":["../../../src/core/matcher/extract-key.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAwBH,gDAWC;AAKD,4DAUC;AA4BD,oDAYC;AAnFD,6DAAyD;AACzD,uDAAmD;AACnD,qDAAiD;AAWjD;;;GAGG;AACH,SAAgB,kBAAkB,CAAC,IAAkB;IACnD,MAAM,EAAE,GAAG,4BAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,EAAE;QAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,EAAE,CAAC,UAAU,EAAE,CAAC;IAEnE,MAAM,KAAK,GAAG,sBAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,KAAK;QAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC;IAEtE,MAAM,IAAI,GAAG,oBAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,IAAI;QAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC;IAEnE,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,SAAgB,wBAAwB,CACtC,OAAkC,EAClC,GAAY;IAEZ,OAAO,OAAO,CAAC,MAAM,CACnB,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,IAAI,KAAK,UAAU;QACrB,CAAC,CAAC,QAAQ,KAAK,GAAG,CAAC,QAAQ;QAC3B,CAAC,CAAC,UAAU,KAAK,GAAG,CAAC,UAAU,CAClC,CAAC;AACJ,CAAC;AAED,SAAS,MAAM,CAAC,GAAY;IAC1B,OAAO,GAAG,GAAG,CAAC,QAAQ,KAAK,GAAG,CAAC,UAAU,EAAE,CAAC;AAC9C,CAAC;AAED;;;GAGG;AACH,MAAa,OAAO;IAApB;QACU,SAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IAW3C,CAAC;IATC;;OAEG;IACH,eAAe,CAAC,GAAY;QAC1B,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QACtB,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC;QAC1B,OAAO,GAAG,CAAC;IACb,CAAC;CACF;AAZD,0BAYC;AAED;;;GAGG;AACH,SAAgB,oBAAoB;IAClC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAC9B,OAAO,CAAC,IAAI,EAAE,OAAO,EAAiB,EAAE;QACtC,MAAM,GAAG,GAAG,kBAAkB,CAAC,IAAoB,CAAC,CAAC;QACrD,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;QACxC,MAAM,UAAU,GAAG,wBAAwB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC1D,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;QAC3D,MAAM,GAAG,GAAG,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QACzC,IAAI,GAAG,IAAI,UAAU,CAAC,MAAM;YAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;QAC5D,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;QAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IACxF,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Shared matcher contracts and implementations.
3
+ */
4
+ export { SemanticMatcher } from './matcher';
5
+ export { SoftprobeMatcher } from './softprobe-matcher';
6
+ export { CallSeq, createDefaultMatcher, extractKeyFromSpan, filterOutboundCandidates, type SpanKey, } from './extract-key';
7
+ export { buildBySpanIdIndex, createTopologyMatcher, filterCandidatesByKey, getLiveParentName, selectLineagePool, } from './topology';
8
+ export { getRecordsForTrace } from './store-accessor';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/matcher/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EACL,OAAO,EACP,oBAAoB,EACpB,kBAAkB,EAClB,wBAAwB,EACxB,KAAK,OAAO,GACb,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getRecordsForTrace = exports.selectLineagePool = exports.getLiveParentName = exports.filterCandidatesByKey = exports.createTopologyMatcher = exports.buildBySpanIdIndex = exports.filterOutboundCandidates = exports.extractKeyFromSpan = exports.createDefaultMatcher = exports.CallSeq = exports.SoftprobeMatcher = exports.SemanticMatcher = void 0;
4
+ /**
5
+ * Shared matcher contracts and implementations.
6
+ */
7
+ var matcher_1 = require("./matcher");
8
+ Object.defineProperty(exports, "SemanticMatcher", { enumerable: true, get: function () { return matcher_1.SemanticMatcher; } });
9
+ var softprobe_matcher_1 = require("./softprobe-matcher");
10
+ Object.defineProperty(exports, "SoftprobeMatcher", { enumerable: true, get: function () { return softprobe_matcher_1.SoftprobeMatcher; } });
11
+ var extract_key_1 = require("./extract-key");
12
+ Object.defineProperty(exports, "CallSeq", { enumerable: true, get: function () { return extract_key_1.CallSeq; } });
13
+ Object.defineProperty(exports, "createDefaultMatcher", { enumerable: true, get: function () { return extract_key_1.createDefaultMatcher; } });
14
+ Object.defineProperty(exports, "extractKeyFromSpan", { enumerable: true, get: function () { return extract_key_1.extractKeyFromSpan; } });
15
+ Object.defineProperty(exports, "filterOutboundCandidates", { enumerable: true, get: function () { return extract_key_1.filterOutboundCandidates; } });
16
+ var topology_1 = require("./topology");
17
+ Object.defineProperty(exports, "buildBySpanIdIndex", { enumerable: true, get: function () { return topology_1.buildBySpanIdIndex; } });
18
+ Object.defineProperty(exports, "createTopologyMatcher", { enumerable: true, get: function () { return topology_1.createTopologyMatcher; } });
19
+ Object.defineProperty(exports, "filterCandidatesByKey", { enumerable: true, get: function () { return topology_1.filterCandidatesByKey; } });
20
+ Object.defineProperty(exports, "getLiveParentName", { enumerable: true, get: function () { return topology_1.getLiveParentName; } });
21
+ Object.defineProperty(exports, "selectLineagePool", { enumerable: true, get: function () { return topology_1.selectLineagePool; } });
22
+ var store_accessor_1 = require("./store-accessor");
23
+ Object.defineProperty(exports, "getRecordsForTrace", { enumerable: true, get: function () { return store_accessor_1.getRecordsForTrace; } });
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/matcher/index.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACH,qCAA4C;AAAnC,0GAAA,eAAe,OAAA;AACxB,yDAAuD;AAA9C,qHAAA,gBAAgB,OAAA;AACzB,6CAMuB;AALrB,sGAAA,OAAO,OAAA;AACP,mHAAA,oBAAoB,OAAA;AACpB,iHAAA,kBAAkB,OAAA;AAClB,uHAAA,wBAAwB,OAAA;AAG1B,uCAMoB;AALlB,8GAAA,kBAAkB,OAAA;AAClB,iHAAA,qBAAqB,OAAA;AACrB,iHAAA,qBAAqB,OAAA;AACrB,6GAAA,iBAAiB,OAAA;AACjB,6GAAA,iBAAiB,OAAA;AAEnB,mDAAsD;AAA7C,oHAAA,kBAAkB,OAAA"}
@@ -0,0 +1,25 @@
1
+ import type { ReadableSpan } from '@opentelemetry/sdk-trace-base';
2
+ import type { MatchRequest, CustomMatcherFn } from '../../types/schema';
3
+ export declare class SemanticMatcher {
4
+ private readonly recordedSpans;
5
+ /** Tracks call count per (protocol, identifier, liveParentName) for sequential N+1 resolution. */
6
+ private readonly callSequenceMap;
7
+ /** User-registered matchers evaluated before the default tree-matching algorithm. */
8
+ private readonly customMatchers;
9
+ constructor(recordedSpans: ReadableSpan[]);
10
+ /**
11
+ * Registers a custom matcher. Custom matchers are run before the default tree-matching logic.
12
+ * - MOCK: return the given payload (no tree matching, no network).
13
+ * - CONTINUE: fall through to tree matching; the call is still replayed from the recording.
14
+ * - PASSTHROUGH: request live network for this call; in strict mode throws (not allowed).
15
+ */
16
+ addMatcher(fn: CustomMatcherFn): void;
17
+ /**
18
+ * Finds a recorded span that matches the live request by protocol and identifier,
19
+ * and optionally by lineage: the recorded span's parent name matches the current
20
+ * active OpenTelemetry span's name (semantic tree matching).
21
+ * Custom matchers are evaluated first; if any returns MOCK, that payload is returned; if any returns PASSTHROUGH, throws.
22
+ */
23
+ findMatch(request: MatchRequest): unknown;
24
+ }
25
+ //# sourceMappingURL=matcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matcher.d.ts","sourceRoot":"","sources":["../../../src/core/matcher/matcher.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAQxE,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiB;IAC/C,kGAAkG;IAClG,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA6B;IAC7D,qFAAqF;IACrF,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAyB;gBAE5C,aAAa,EAAE,YAAY,EAAE;IAIzC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,EAAE,eAAe,GAAG,IAAI;IAIrC;;;;;OAKG;IACH,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO;CA0D1C"}
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SemanticMatcher = void 0;
4
+ const api_1 = require("@opentelemetry/api");
5
+ const RESPONSE_BODY_KEY = 'softprobe.response.body';
6
+ const PROTOCOL_KEY = 'softprobe.protocol';
7
+ const IDENTIFIER_KEY = 'softprobe.identifier';
8
+ // This is obsolete, we should use the new matcher instead
9
+ // Deprecated: use SoftprobeMatcher instead
10
+ class SemanticMatcher {
11
+ constructor(recordedSpans) {
12
+ /** Tracks call count per (protocol, identifier, liveParentName) for sequential N+1 resolution. */
13
+ this.callSequenceMap = new Map();
14
+ /** User-registered matchers evaluated before the default tree-matching algorithm. */
15
+ this.customMatchers = [];
16
+ this.recordedSpans = recordedSpans;
17
+ }
18
+ /**
19
+ * Registers a custom matcher. Custom matchers are run before the default tree-matching logic.
20
+ * - MOCK: return the given payload (no tree matching, no network).
21
+ * - CONTINUE: fall through to tree matching; the call is still replayed from the recording.
22
+ * - PASSTHROUGH: request live network for this call; in strict mode throws (not allowed).
23
+ */
24
+ addMatcher(fn) {
25
+ this.customMatchers.push(fn);
26
+ }
27
+ /**
28
+ * Finds a recorded span that matches the live request by protocol and identifier,
29
+ * and optionally by lineage: the recorded span's parent name matches the current
30
+ * active OpenTelemetry span's name (semantic tree matching).
31
+ * Custom matchers are evaluated first; if any returns MOCK, that payload is returned; if any returns PASSTHROUGH, throws.
32
+ */
33
+ findMatch(request) {
34
+ for (const matcher of this.customMatchers) {
35
+ const result = matcher(request, this.recordedSpans);
36
+ if (result.action === 'MOCK')
37
+ return result.payload;
38
+ if (result.action === 'PASSTHROUGH') {
39
+ throw new Error('[Softprobe] Network Passthrough not allowed in strict mode');
40
+ }
41
+ }
42
+ const candidates = this.recordedSpans.filter((span) => span.attributes[PROTOCOL_KEY] === request.protocol &&
43
+ span.attributes[IDENTIFIER_KEY] === request.identifier);
44
+ if (candidates.length === 0) {
45
+ throw new Error(`[Softprobe] No recorded traces found for ${request.protocol}: ${request.identifier}`);
46
+ }
47
+ const liveSpan = api_1.trace.getActiveSpan();
48
+ const liveParentName = liveSpan && typeof liveSpan.name === 'string'
49
+ ? liveSpan.name
50
+ : 'root';
51
+ const lineageMatches = candidates.filter((candidate) => {
52
+ const parentSpanId = candidate.parentSpanId;
53
+ if (parentSpanId == null)
54
+ return liveParentName === 'root';
55
+ const candidateParent = this.recordedSpans.find((s) => s.spanContext().spanId === parentSpanId);
56
+ return candidateParent != null && candidateParent.name === liveParentName;
57
+ });
58
+ let matched;
59
+ if (lineageMatches.length > 0) {
60
+ const sequenceKey = `${request.protocol}-${request.identifier}-${liveParentName}`;
61
+ const currentCount = this.callSequenceMap.get(sequenceKey) ?? 0;
62
+ // Wrap-around: if call count exceeds number of lineage matches, reuse the first match
63
+ matched = lineageMatches[currentCount] ?? lineageMatches[0];
64
+ this.callSequenceMap.set(sequenceKey, currentCount + 1);
65
+ }
66
+ else {
67
+ // Flat match: no lineage context, so we do not update callSequenceMap; always return first candidate.
68
+ matched = candidates[0];
69
+ }
70
+ const raw = matched.attributes[RESPONSE_BODY_KEY];
71
+ if (typeof raw !== 'string') {
72
+ throw new Error('[Softprobe] Missing or invalid softprobe.response.body on matched span');
73
+ }
74
+ try {
75
+ return JSON.parse(raw);
76
+ }
77
+ catch {
78
+ throw new Error('[Softprobe] Invalid or non-JSON softprobe.response.body on matched span');
79
+ }
80
+ }
81
+ }
82
+ exports.SemanticMatcher = SemanticMatcher;
83
+ //# sourceMappingURL=matcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matcher.js","sourceRoot":"","sources":["../../../src/core/matcher/matcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import type { MatcherAction, MatcherFn, SoftprobeCassetteRecord } from '../../types/schema';
2
+ export declare class SoftprobeMatcher {
3
+ /** Matcher functions run in registration order; first non-CONTINUE wins. */
4
+ private fns;
5
+ /** Cassette records for this replay context (one trace). Set by SoftprobeContext.run(REPLAY). */
6
+ private records;
7
+ /** Appends a matcher function. Fns are run in registration order. */
8
+ use(fn: MatcherFn): void;
9
+ /** Removes all registered matcher fns. */
10
+ clear(): void;
11
+ /**
12
+ * Sets the cassette records for this context. Called by SoftprobeContext.run(REPLAY)
13
+ * after loading from storage so each matcher fn receives them in match().
14
+ */
15
+ _setRecords(records: SoftprobeCassetteRecord[]): void;
16
+ /**
17
+ * Returns the records for this replay context (context-scoped).
18
+ * Used by getRecordsForTrace when the active context has a matcher, so HTTP replay
19
+ * (MSW interceptor) reads from context instead of a global cache.
20
+ */
21
+ _getRecords(): SoftprobeCassetteRecord[];
22
+ /**
23
+ * Returns the trace id for the current request (from first record).
24
+ * Used when context/span do not propagate to fetch so the interceptor can identify the trace.
25
+ */
26
+ _getTraceId(): string | undefined;
27
+ /**
28
+ * Returns the inbound record's responsePayload.body, with .http extracted when present.
29
+ * Used by HTTP replay (MSW interceptor) so the mock response body matches the recorded inbound and diff passes.
30
+ */
31
+ _getInboundHttpBody(): unknown;
32
+ /**
33
+ * Runs registered matcher fns in order against the current active span (or spanOverride).
34
+ * Returns the first non-CONTINUE action (MOCK or PASSTHROUGH), or CONTINUE if all fns continue.
35
+ * Each fn receives the span and this context's records (design §7.1).
36
+ */
37
+ match(spanOverride?: import('@opentelemetry/api').Span | {
38
+ attributes?: Record<string, unknown>;
39
+ }): MatcherAction;
40
+ }
41
+ //# sourceMappingURL=softprobe-matcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"softprobe-matcher.d.ts","sourceRoot":"","sources":["../../../src/core/matcher/softprobe-matcher.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAE5F,qBAAa,gBAAgB;IAC3B,4EAA4E;IAC5E,OAAO,CAAC,GAAG,CAAmB;IAC9B,iGAAiG;IACjG,OAAO,CAAC,OAAO,CAAiC;IAEhD,qEAAqE;IACrE,GAAG,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI;IAIxB,0CAA0C;IAC1C,KAAK,IAAI,IAAI;IAIb;;;OAGG;IACH,WAAW,CAAC,OAAO,EAAE,uBAAuB,EAAE,GAAG,IAAI;IAIrD;;;;OAIG;IACH,WAAW,IAAI,uBAAuB,EAAE;IAIxC;;;OAGG;IACH,WAAW,IAAI,MAAM,GAAG,SAAS;IAIjC;;;OAGG;IACH,mBAAmB,IAAI,OAAO;IAoB9B;;;;OAIG;IACH,KAAK,CAAC,YAAY,CAAC,EAAE,OAAO,oBAAoB,EAAE,IAAI,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAAG,aAAa;CAQlH"}
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SoftprobeMatcher = void 0;
4
+ /**
5
+ * SoftprobeMatcher: per-context replay matcher for outbound calls.
6
+ *
7
+ * One instance is created per SoftprobeContext.run(REPLAY) and stored in the OTel context
8
+ * for that request. It holds the cassette records for that trace and runs a list of MatcherFn
9
+ * in order; match() returns the first non-CONTINUE action (MOCK or PASSTHROUGH).
10
+ *
11
+ * Design §7.1: use(fn) appends; matchers do not execute passthrough. Records are context-scoped
12
+ * (loaded from the cassette for this run's traceId).
13
+ */
14
+ const api_1 = require("@opentelemetry/api");
15
+ class SoftprobeMatcher {
16
+ constructor() {
17
+ /** Matcher functions run in registration order; first non-CONTINUE wins. */
18
+ this.fns = [];
19
+ /** Cassette records for this replay context (one trace). Set by SoftprobeContext.run(REPLAY). */
20
+ this.records = [];
21
+ }
22
+ /** Appends a matcher function. Fns are run in registration order. */
23
+ use(fn) {
24
+ this.fns.push(fn);
25
+ }
26
+ /** Removes all registered matcher fns. */
27
+ clear() {
28
+ this.fns = [];
29
+ }
30
+ /**
31
+ * Sets the cassette records for this context. Called by SoftprobeContext.run(REPLAY)
32
+ * after loading from storage so each matcher fn receives them in match().
33
+ */
34
+ _setRecords(records) {
35
+ this.records = records;
36
+ }
37
+ /**
38
+ * Returns the records for this replay context (context-scoped).
39
+ * Used by getRecordsForTrace when the active context has a matcher, so HTTP replay
40
+ * (MSW interceptor) reads from context instead of a global cache.
41
+ */
42
+ _getRecords() {
43
+ return this.records;
44
+ }
45
+ /**
46
+ * Returns the trace id for the current request (from first record).
47
+ * Used when context/span do not propagate to fetch so the interceptor can identify the trace.
48
+ */
49
+ _getTraceId() {
50
+ return this.records[0]?.traceId;
51
+ }
52
+ /**
53
+ * Returns the inbound record's responsePayload.body, with .http extracted when present.
54
+ * Used by HTTP replay (MSW interceptor) so the mock response body matches the recorded inbound and diff passes.
55
+ */
56
+ _getInboundHttpBody() {
57
+ const inbound = this.records.find((r) => r.type === 'inbound');
58
+ const payload = inbound?.responsePayload;
59
+ const body = payload?.body;
60
+ if (body == null)
61
+ return undefined;
62
+ const parsed = typeof body === 'string'
63
+ ? (() => {
64
+ try {
65
+ return JSON.parse(body);
66
+ }
67
+ catch {
68
+ return undefined;
69
+ }
70
+ })()
71
+ : body;
72
+ return parsed && typeof parsed === 'object' && 'http' in parsed
73
+ ? parsed.http
74
+ : undefined;
75
+ }
76
+ /**
77
+ * Runs registered matcher fns in order against the current active span (or spanOverride).
78
+ * Returns the first non-CONTINUE action (MOCK or PASSTHROUGH), or CONTINUE if all fns continue.
79
+ * Each fn receives the span and this context's records (design §7.1).
80
+ */
81
+ match(spanOverride) {
82
+ const span = (api_1.trace.getActiveSpan() ?? spanOverride);
83
+ for (const fn of this.fns) {
84
+ const r = fn(span, this.records);
85
+ if (r.action !== 'CONTINUE')
86
+ return r;
87
+ }
88
+ return { action: 'CONTINUE' };
89
+ }
90
+ }
91
+ exports.SoftprobeMatcher = SoftprobeMatcher;
92
+ //# sourceMappingURL=softprobe-matcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"softprobe-matcher.js","sourceRoot":"","sources":["../../../src/core/matcher/softprobe-matcher.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;GASG;AACH,4CAA2C;AAG3C,MAAa,gBAAgB;IAA7B;QACE,4EAA4E;QACpE,QAAG,GAAgB,EAAE,CAAC;QAC9B,iGAAiG;QACzF,YAAO,GAA8B,EAAE,CAAC;IA0ElD,CAAC;IAxEC,qEAAqE;IACrE,GAAG,CAAC,EAAa;QACf,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpB,CAAC;IAED,0CAA0C;IAC1C,KAAK;QACH,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;IAChB,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,OAAkC;QAC5C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED;;;;OAIG;IACH,WAAW;QACT,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED;;;OAGG;IACH,WAAW;QACT,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC;IAClC,CAAC;IAED;;;OAGG;IACH,mBAAmB;QACjB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC;QAC/D,MAAM,OAAO,GAAG,OAAO,EAAE,eAAiD,CAAC;QAC3E,MAAM,IAAI,GAAG,OAAO,EAAE,IAAI,CAAC;QAC3B,IAAI,IAAI,IAAI,IAAI;YAAE,OAAO,SAAS,CAAC;QACnC,MAAM,MAAM,GACV,OAAO,IAAI,KAAK,QAAQ;YACtB,CAAC,CAAC,CAAC,GAAG,EAAE;gBACJ,IAAI,CAAC;oBACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC1B,CAAC;gBAAC,MAAM,CAAC;oBACP,OAAO,SAAS,CAAC;gBACnB,CAAC;YACH,CAAC,CAAC,EAAE;YACN,CAAC,CAAC,IAAI,CAAC;QACX,OAAO,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,IAAI,MAAM;YAC7D,CAAC,CAAE,MAA4B,CAAC,IAAI;YACpC,CAAC,CAAC,SAAS,CAAC;IAChB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,YAA2F;QAC/F,MAAM,IAAI,GAAG,CAAC,WAAK,CAAC,aAAa,EAAE,IAAI,YAAY,CAAkD,CAAC;QACtG,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;YAC1B,MAAM,CAAC,GAAG,EAAE,CAAC,IAAyC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YACtE,IAAI,CAAC,CAAC,MAAM,KAAK,UAAU;gBAAE,OAAO,CAAC,CAAC;QACxC,CAAC;QACD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAChC,CAAC;CACF;AA9ED,4CA8EC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Replay-mode record store accessor. Records are context-scoped only: the matcher
3
+ * in the active OTel context holds the loaded records for that request (created by
4
+ * SoftprobeContext.run(REPLAY)). No global cache. Task 15.3.1.
5
+ * Task 13.10: Load via Cassette (getOrCreateCassette) only; no loadNdjson.
6
+ */
7
+ import type { SoftprobeCassetteRecord } from '../../types/schema';
8
+ /**
9
+ * Returns recorded cassette records for the given traceId from the active context only.
10
+ * The matcher is created and seeded by SoftprobeContext.run(REPLAY); only the framework creates matchers.
11
+ * Returns [] when there is no active matcher with records. Compares traceIds in lowercase (W3C traceparent).
12
+ */
13
+ export declare function getRecordsForTrace(traceId: string): SoftprobeCassetteRecord[];
14
+ //# sourceMappingURL=store-accessor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-accessor.d.ts","sourceRoot":"","sources":["../../../src/core/matcher/store-accessor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAIlE;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,uBAAuB,EAAE,CAQ7E"}
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ /**
3
+ * Replay-mode record store accessor. Records are context-scoped only: the matcher
4
+ * in the active OTel context holds the loaded records for that request (created by
5
+ * SoftprobeContext.run(REPLAY)). No global cache. Task 15.3.1.
6
+ * Task 13.10: Load via Cassette (getOrCreateCassette) only; no loadNdjson.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.getRecordsForTrace = getRecordsForTrace;
10
+ const context_1 = require("../../context");
11
+ /**
12
+ * Returns recorded cassette records for the given traceId from the active context only.
13
+ * The matcher is created and seeded by SoftprobeContext.run(REPLAY); only the framework creates matchers.
14
+ * Returns [] when there is no active matcher with records. Compares traceIds in lowercase (W3C traceparent).
15
+ */
16
+ function getRecordsForTrace(traceId) {
17
+ const normalized = traceId.toLowerCase();
18
+ const matcher = context_1.SoftprobeContext.getMatcher();
19
+ if (matcher && '_getRecords' in matcher && typeof matcher._getRecords === 'function') {
20
+ const contextRecords = matcher._getRecords();
21
+ return contextRecords.filter((r) => (r.traceId ?? '').toLowerCase() === normalized);
22
+ }
23
+ return [];
24
+ }
25
+ //# sourceMappingURL=store-accessor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-accessor.js","sourceRoot":"","sources":["../../../src/core/matcher/store-accessor.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAYH,gDAQC;AAhBD,2CAAiD;AAGjD;;;;GAIG;AACH,SAAgB,kBAAkB,CAAC,OAAe;IAChD,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG,0BAAgB,CAAC,UAAU,EAAE,CAAC;IAC9C,IAAI,OAAO,IAAI,aAAa,IAAI,OAAO,IAAI,OAAQ,OAA4B,CAAC,WAAW,KAAK,UAAU,EAAE,CAAC;QAC3G,MAAM,cAAc,GAAI,OAA4B,CAAC,WAAW,EAAE,CAAC;QACnE,OAAO,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,UAAU,CAAC,CAAC;IACtF,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC"}