@probemesh/sdk 0.1.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 (83) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +160 -0
  3. package/dist/chunk-257Y7LN2.js +827 -0
  4. package/dist/chunk-257Y7LN2.js.map +1 -0
  5. package/dist/chunk-2ASMVLG4.js +56 -0
  6. package/dist/chunk-2ASMVLG4.js.map +1 -0
  7. package/dist/chunk-2KWGHJYP.js +285 -0
  8. package/dist/chunk-2KWGHJYP.js.map +1 -0
  9. package/dist/chunk-3H7UGVI6.js +1117 -0
  10. package/dist/chunk-3H7UGVI6.js.map +1 -0
  11. package/dist/chunk-5Q3PDYIA.js +657 -0
  12. package/dist/chunk-5Q3PDYIA.js.map +1 -0
  13. package/dist/chunk-CXZOO3U4.js +550 -0
  14. package/dist/chunk-CXZOO3U4.js.map +1 -0
  15. package/dist/chunk-FRFBK4SY.js +270 -0
  16. package/dist/chunk-FRFBK4SY.js.map +1 -0
  17. package/dist/chunk-HJK52QJF.js +994 -0
  18. package/dist/chunk-HJK52QJF.js.map +1 -0
  19. package/dist/chunk-HNBDX7IU.js +705 -0
  20. package/dist/chunk-HNBDX7IU.js.map +1 -0
  21. package/dist/chunk-NYTV263W.js +116 -0
  22. package/dist/chunk-NYTV263W.js.map +1 -0
  23. package/dist/chunk-PXG54XOG.js +595 -0
  24. package/dist/chunk-PXG54XOG.js.map +1 -0
  25. package/dist/chunk-TDOBAMYM.js +607 -0
  26. package/dist/chunk-TDOBAMYM.js.map +1 -0
  27. package/dist/chunk-TV42EZSI.js +2157 -0
  28. package/dist/chunk-TV42EZSI.js.map +1 -0
  29. package/dist/chunk-UU2ZG7P7.js +408 -0
  30. package/dist/chunk-UU2ZG7P7.js.map +1 -0
  31. package/dist/chunk-WKN7QOCA.js +977 -0
  32. package/dist/chunk-WKN7QOCA.js.map +1 -0
  33. package/dist/chunk-ZJOLPBJJ.js +1091 -0
  34. package/dist/chunk-ZJOLPBJJ.js.map +1 -0
  35. package/dist/cli/audit-trail-export.cjs +1193 -0
  36. package/dist/cli/audit-trail-export.cjs.map +1 -0
  37. package/dist/cli/audit-trail-export.d.cts +1 -0
  38. package/dist/cli/audit-trail-export.d.ts +1 -0
  39. package/dist/cli/audit-trail-export.js +24 -0
  40. package/dist/cli/audit-trail-export.js.map +1 -0
  41. package/dist/cli/catalog-check.cjs +2687 -0
  42. package/dist/cli/catalog-check.cjs.map +1 -0
  43. package/dist/cli/catalog-check.d.cts +1 -0
  44. package/dist/cli/catalog-check.d.ts +1 -0
  45. package/dist/cli/catalog-check.js +26 -0
  46. package/dist/cli/catalog-check.js.map +1 -0
  47. package/dist/cli/probemesh-init.cjs +1049 -0
  48. package/dist/cli/probemesh-init.cjs.map +1 -0
  49. package/dist/cli/probemesh-init.d.cts +1 -0
  50. package/dist/cli/probemesh-init.d.ts +1 -0
  51. package/dist/cli/probemesh-init.js +22 -0
  52. package/dist/cli/probemesh-init.js.map +1 -0
  53. package/dist/cli/provider-conformance.cjs +6180 -0
  54. package/dist/cli/provider-conformance.cjs.map +1 -0
  55. package/dist/cli/provider-conformance.d.cts +1 -0
  56. package/dist/cli/provider-conformance.d.ts +1 -0
  57. package/dist/cli/provider-conformance.js +29 -0
  58. package/dist/cli/provider-conformance.js.map +1 -0
  59. package/dist/cli/provider-dossier-check.cjs +2978 -0
  60. package/dist/cli/provider-dossier-check.cjs.map +1 -0
  61. package/dist/cli/provider-dossier-check.d.cts +1 -0
  62. package/dist/cli/provider-dossier-check.d.ts +1 -0
  63. package/dist/cli/provider-dossier-check.js +27 -0
  64. package/dist/cli/provider-dossier-check.js.map +1 -0
  65. package/dist/cli/provider-dossier.cjs +1753 -0
  66. package/dist/cli/provider-dossier.cjs.map +1 -0
  67. package/dist/cli/provider-dossier.d.cts +1 -0
  68. package/dist/cli/provider-dossier.d.ts +1 -0
  69. package/dist/cli/provider-dossier.js +26 -0
  70. package/dist/cli/provider-dossier.js.map +1 -0
  71. package/dist/cli/x402-accept.cjs +6009 -0
  72. package/dist/cli/x402-accept.cjs.map +1 -0
  73. package/dist/cli/x402-accept.d.cts +1 -0
  74. package/dist/cli/x402-accept.d.ts +1 -0
  75. package/dist/cli/x402-accept.js +28 -0
  76. package/dist/cli/x402-accept.js.map +1 -0
  77. package/dist/index.cjs +13671 -0
  78. package/dist/index.cjs.map +1 -0
  79. package/dist/index.d.cts +2026 -0
  80. package/dist/index.d.ts +2026 -0
  81. package/dist/index.js +2560 -0
  82. package/dist/index.js.map +1 -0
  83. package/package.json +111 -0
@@ -0,0 +1,2687 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // src/cli/providerCatalogPolicyCli.ts
5
+ var import_promises2 = require("fs/promises");
6
+ var import_node_path2 = require("path");
7
+
8
+ // src/cli/configLoader.ts
9
+ var import_promises = require("fs/promises");
10
+ var import_node_path = require("path");
11
+ var import_node_url = require("url");
12
+ async function loadCliConfigDefault(configPath, cwd) {
13
+ const resolvedPath = (0, import_node_path.resolve)(cwd, configPath);
14
+ const moduleUrl = (0, import_node_url.pathToFileURL)(resolvedPath);
15
+ try {
16
+ const loadedModule = await import(
17
+ /* @vite-ignore */
18
+ moduleUrl.href
19
+ );
20
+ return loadedModule.default;
21
+ } catch (error) {
22
+ return loadSelfContainedConfigFallback(resolvedPath, error);
23
+ }
24
+ }
25
+ async function loadSelfContainedConfigFallback(resolvedPath, importError) {
26
+ const source = await (0, import_promises.readFile)(resolvedPath, "utf8");
27
+ if (/\bimport\s/.test(source) || !/\bexport\s+default\b/.test(source)) {
28
+ throw importError;
29
+ }
30
+ const transformedSource = source.replace(
31
+ /\bexport\s+default\b/,
32
+ "const __probemeshDefault ="
33
+ );
34
+ try {
35
+ const factory = new Function(
36
+ `${transformedSource}
37
+ return __probemeshDefault;`
38
+ );
39
+ return factory();
40
+ } catch {
41
+ throw importError;
42
+ }
43
+ }
44
+
45
+ // src/errors.ts
46
+ var ProbeMeshError = class extends Error {
47
+ code;
48
+ status = "failed";
49
+ capability;
50
+ provider;
51
+ callId;
52
+ receiptRefs;
53
+ safety;
54
+ timeline;
55
+ retrySafety;
56
+ attempt;
57
+ route;
58
+ cause;
59
+ constructor(options) {
60
+ super(options.message);
61
+ this.name = "ProbeMeshError";
62
+ this.code = options.code;
63
+ this.capability = options.capability;
64
+ this.provider = options.provider;
65
+ this.callId = options.callId;
66
+ this.receiptRefs = options.receiptRefs;
67
+ this.safety = options.safety;
68
+ this.timeline = options.timeline;
69
+ this.retrySafety = options.retrySafety;
70
+ this.attempt = options.attempt;
71
+ this.route = options.route;
72
+ this.cause = options.cause;
73
+ }
74
+ toJSON() {
75
+ return {
76
+ name: this.name,
77
+ code: this.code,
78
+ message: this.message,
79
+ status: this.status,
80
+ capability: this.capability,
81
+ provider: this.provider,
82
+ callId: this.callId,
83
+ receiptRefs: this.receiptRefs,
84
+ safety: this.safety,
85
+ timeline: this.timeline,
86
+ retrySafety: this.retrySafety,
87
+ attempt: this.attempt,
88
+ route: this.route
89
+ };
90
+ }
91
+ };
92
+
93
+ // src/paymentOptions.ts
94
+ function resolveProviderPaymentOption(input) {
95
+ const options = getProviderPaymentOptions(input);
96
+ const candidates = options.filter((option) => optionSupportsCapability(option, input.capability)).map((option, index) => ({
97
+ option,
98
+ index,
99
+ cost: pricingToCost(resolvePaymentOptionPricing(option, input.capability) ?? resolvePricing(input.pricing, input.capability)),
100
+ receiptTypes: option.receiptTypes ?? []
101
+ }));
102
+ const filtered = candidates.filter(
103
+ ({ option, receiptTypes }) => optionMatchesPaymentPreferences(option, receiptTypes, input.paymentPreferences)
104
+ );
105
+ if (filtered.length === 0) {
106
+ return {
107
+ options,
108
+ reason: createNoPaymentOptionReason(input, candidates.length)
109
+ };
110
+ }
111
+ const selected = choosePaymentOption(
112
+ filtered,
113
+ input.paymentPreferences,
114
+ input.paymentStrategy
115
+ );
116
+ return {
117
+ options,
118
+ selection: {
119
+ providerId: input.providerId,
120
+ optionId: selected.option.id,
121
+ protocolMode: selected.option.protocolMode,
122
+ cost: selected.cost,
123
+ receiptTypes: [...selected.receiptTypes],
124
+ score: selected.score,
125
+ scoreReasons: [...selected.scoreReasons],
126
+ metadata: cloneSerializable(selected.option.metadata)
127
+ }
128
+ };
129
+ }
130
+ function getProviderPaymentOptions(input) {
131
+ if (input.paymentOptions && input.paymentOptions.length > 0) {
132
+ return cloneSerializable(input.paymentOptions);
133
+ }
134
+ return [
135
+ {
136
+ id: "default",
137
+ protocolMode: input.providerMode ?? "local",
138
+ capabilities: [...input.capabilities],
139
+ ...input.pricing !== void 0 ? { pricing: cloneSerializable(input.pricing) } : {},
140
+ receiptTypes: [...input.defaultReceiptTypes ?? []]
141
+ }
142
+ ];
143
+ }
144
+ function validateProviderPaymentOptions(paymentOptions, path, capabilities, errors) {
145
+ if (paymentOptions === void 0) {
146
+ return;
147
+ }
148
+ if (!Array.isArray(paymentOptions) || paymentOptions.length === 0) {
149
+ errors.push({
150
+ path,
151
+ message: "paymentOptions must be a non-empty array when provided."
152
+ });
153
+ return;
154
+ }
155
+ const seen = /* @__PURE__ */ new Set();
156
+ paymentOptions.forEach((option, index) => {
157
+ const optionPath = `${path}.${index}`;
158
+ if (!isRecord(option)) {
159
+ errors.push({
160
+ path: optionPath,
161
+ message: "payment option must be an object."
162
+ });
163
+ return;
164
+ }
165
+ validateNonEmptyString(option.id, `${optionPath}.id`, errors);
166
+ validateNonEmptyString(option.protocolMode, `${optionPath}.protocolMode`, errors);
167
+ if (typeof option.id === "string") {
168
+ if (seen.has(option.id)) {
169
+ errors.push({
170
+ path: `${optionPath}.id`,
171
+ message: `duplicate payment option "${option.id}".`
172
+ });
173
+ }
174
+ seen.add(option.id);
175
+ }
176
+ if (option.capabilities !== void 0) {
177
+ validateStringArray(option.capabilities, `${optionPath}.capabilities`, errors);
178
+ if (Array.isArray(option.capabilities)) {
179
+ for (const capability of option.capabilities) {
180
+ if (typeof capability === "string" && capability.length > 0 && !capabilities.includes(capability)) {
181
+ errors.push({
182
+ path: `${optionPath}.capabilities`,
183
+ message: `payment option capability "${capability}" is not declared by the provider.`
184
+ });
185
+ }
186
+ }
187
+ }
188
+ }
189
+ if (option.pricing !== void 0) {
190
+ validatePricingDeclaration(option.pricing, `${optionPath}.pricing`, errors);
191
+ }
192
+ if (option.receiptTypes !== void 0) {
193
+ if (!Array.isArray(option.receiptTypes) || option.receiptTypes.some((receiptType) => !isReceiptType(receiptType))) {
194
+ errors.push({
195
+ path: `${optionPath}.receiptTypes`,
196
+ message: "payment option receiptTypes must contain known receipt types."
197
+ });
198
+ }
199
+ }
200
+ if (option.metadata !== void 0 && !isRecord(option.metadata)) {
201
+ errors.push({
202
+ path: `${optionPath}.metadata`,
203
+ message: "payment option metadata must be an object when provided."
204
+ });
205
+ }
206
+ });
207
+ }
208
+ function validatePaymentPreferences(paymentPreferences, capability) {
209
+ if (paymentPreferences === void 0) {
210
+ return;
211
+ }
212
+ if (!isRecord(paymentPreferences)) {
213
+ throw new ProbeMeshError({
214
+ code: "invalid_request",
215
+ message: "paymentPreferences must be an object when provided.",
216
+ capability
217
+ });
218
+ }
219
+ validatePreferenceStringArray(
220
+ paymentPreferences.allowedProtocols,
221
+ "allowedProtocols",
222
+ capability
223
+ );
224
+ validatePreferenceStringArray(
225
+ paymentPreferences.preferredProtocols,
226
+ "preferredProtocols",
227
+ capability
228
+ );
229
+ validatePreferenceStringArray(
230
+ paymentPreferences.blockedProtocols,
231
+ "blockedProtocols",
232
+ capability
233
+ );
234
+ validatePreferenceStringArray(
235
+ paymentPreferences.allowedPaymentOptions,
236
+ "allowedPaymentOptions",
237
+ capability
238
+ );
239
+ validatePreferenceStringArray(
240
+ paymentPreferences.preferredPaymentOptions,
241
+ "preferredPaymentOptions",
242
+ capability
243
+ );
244
+ validatePreferenceStringArray(
245
+ paymentPreferences.blockedPaymentOptions,
246
+ "blockedPaymentOptions",
247
+ capability
248
+ );
249
+ if (paymentPreferences.requiredReceiptTypes !== void 0 && (!Array.isArray(paymentPreferences.requiredReceiptTypes) || paymentPreferences.requiredReceiptTypes.some((receiptType) => !isReceiptType(receiptType)))) {
250
+ throw new ProbeMeshError({
251
+ code: "invalid_request",
252
+ message: "paymentPreferences.requiredReceiptTypes must contain known receipt types.",
253
+ capability
254
+ });
255
+ }
256
+ }
257
+ function validatePaymentStrategy(paymentStrategy, capability) {
258
+ if (paymentStrategy === void 0) {
259
+ return;
260
+ }
261
+ if (!isRecord(paymentStrategy)) {
262
+ throw new ProbeMeshError({
263
+ code: "invalid_request",
264
+ message: "paymentStrategy must be an object when provided.",
265
+ capability
266
+ });
267
+ }
268
+ if (!isPaymentStrategyMode(paymentStrategy.mode)) {
269
+ throw new ProbeMeshError({
270
+ code: "invalid_request",
271
+ message: 'paymentStrategy.mode must be one of "provider_order", "lowest_cost", "preferred_protocol", or "receipt_coverage".',
272
+ capability
273
+ });
274
+ }
275
+ if (paymentStrategy.preferredReceiptTypes !== void 0 && (!Array.isArray(paymentStrategy.preferredReceiptTypes) || paymentStrategy.preferredReceiptTypes.some(
276
+ (receiptType) => !isReceiptType(receiptType)
277
+ ))) {
278
+ throw new ProbeMeshError({
279
+ code: "invalid_request",
280
+ message: "paymentStrategy.preferredReceiptTypes must contain known receipt types.",
281
+ capability
282
+ });
283
+ }
284
+ }
285
+ function mergePaymentPreferences(first, second) {
286
+ if (!first && !second) {
287
+ return void 0;
288
+ }
289
+ return compactPaymentPreferences({
290
+ allowedProtocols: intersectArrays(first?.allowedProtocols, second?.allowedProtocols),
291
+ preferredProtocols: second?.preferredProtocols ?? first?.preferredProtocols,
292
+ blockedProtocols: unionArrays(first?.blockedProtocols, second?.blockedProtocols),
293
+ allowedPaymentOptions: intersectArrays(
294
+ first?.allowedPaymentOptions,
295
+ second?.allowedPaymentOptions
296
+ ),
297
+ preferredPaymentOptions: second?.preferredPaymentOptions ?? first?.preferredPaymentOptions,
298
+ blockedPaymentOptions: unionArrays(
299
+ first?.blockedPaymentOptions,
300
+ second?.blockedPaymentOptions
301
+ ),
302
+ requiredReceiptTypes: unionArrays(
303
+ first?.requiredReceiptTypes,
304
+ second?.requiredReceiptTypes
305
+ )
306
+ });
307
+ }
308
+ function optionSupportsCapability(option, capability) {
309
+ return !option.capabilities || option.capabilities.includes(capability);
310
+ }
311
+ function optionMatchesPaymentPreferences(option, receiptTypes, preferences) {
312
+ if (!preferences) {
313
+ return true;
314
+ }
315
+ if (preferences.allowedProtocols && !preferences.allowedProtocols.includes(option.protocolMode)) {
316
+ return false;
317
+ }
318
+ if (preferences.blockedProtocols?.includes(option.protocolMode)) {
319
+ return false;
320
+ }
321
+ if (preferences.allowedPaymentOptions && !preferences.allowedPaymentOptions.includes(option.id)) {
322
+ return false;
323
+ }
324
+ if (preferences.blockedPaymentOptions?.includes(option.id)) {
325
+ return false;
326
+ }
327
+ const receiptSet = new Set(receiptTypes);
328
+ return (preferences.requiredReceiptTypes ?? []).every(
329
+ (receiptType) => receiptSet.has(receiptType)
330
+ );
331
+ }
332
+ function choosePaymentOption(candidates, preferences, strategy) {
333
+ const mode = strategy?.mode ?? "provider_order";
334
+ const ranked = [...candidates].sort(
335
+ (left, right) => comparePaymentOptions(left, right, preferences, strategy)
336
+ );
337
+ return ranked.map((candidate, rank) => ({
338
+ ...candidate,
339
+ score: rank,
340
+ scoreReasons: createScoreReasons(candidate, preferences, strategy, mode)
341
+ }))[0];
342
+ }
343
+ function preferenceIndex(preferences, value) {
344
+ const index = preferences.indexOf(value);
345
+ return index === -1 ? Number.MAX_SAFE_INTEGER : index;
346
+ }
347
+ function comparePaymentOptions(left, right, preferences, strategy) {
348
+ switch (strategy?.mode ?? "provider_order") {
349
+ case "lowest_cost":
350
+ return compareCost(left, right) || left.index - right.index;
351
+ case "preferred_protocol":
352
+ return comparePreferredProtocol(left, right, preferences) || compareCost(left, right) || left.index - right.index;
353
+ case "receipt_coverage":
354
+ return compareReceiptCoverage(left, right, preferences, strategy) || comparePreferredProtocol(left, right, preferences) || compareCost(left, right) || left.index - right.index;
355
+ case "provider_order":
356
+ return compareProviderOrder(left, right, preferences);
357
+ }
358
+ }
359
+ function compareProviderOrder(left, right, preferences) {
360
+ const optionPreference = preferences?.preferredPaymentOptions ?? [];
361
+ const protocolPreference = preferences?.preferredProtocols ?? [];
362
+ const optionPreferenceDelta = preferenceIndex(optionPreference, left.option.id) - preferenceIndex(optionPreference, right.option.id);
363
+ if (optionPreferenceDelta !== 0) {
364
+ return optionPreferenceDelta;
365
+ }
366
+ const protocolPreferenceDelta = preferenceIndex(protocolPreference, left.option.protocolMode) - preferenceIndex(protocolPreference, right.option.protocolMode);
367
+ if (protocolPreferenceDelta !== 0) {
368
+ return protocolPreferenceDelta;
369
+ }
370
+ return left.index - right.index;
371
+ }
372
+ function comparePreferredProtocol(left, right, preferences) {
373
+ const protocolPreference = preferences?.preferredProtocols ?? [];
374
+ return preferenceIndex(protocolPreference, left.option.protocolMode) - preferenceIndex(protocolPreference, right.option.protocolMode);
375
+ }
376
+ function compareCost(left, right) {
377
+ const leftCost = costSortValue(left.cost);
378
+ const rightCost = costSortValue(right.cost);
379
+ if (leftCost === rightCost) {
380
+ return 0;
381
+ }
382
+ return leftCost < rightCost ? -1 : 1;
383
+ }
384
+ function compareReceiptCoverage(left, right, preferences, strategy) {
385
+ return receiptCoverage(right, preferences, strategy) - receiptCoverage(left, preferences, strategy);
386
+ }
387
+ function receiptCoverage(candidate, preferences, strategy) {
388
+ const desiredReceiptTypes = strategy?.preferredReceiptTypes ?? preferences?.requiredReceiptTypes ?? [];
389
+ const receiptSet = new Set(candidate.receiptTypes);
390
+ if (desiredReceiptTypes.length > 0) {
391
+ return desiredReceiptTypes.filter((receiptType) => receiptSet.has(receiptType)).length;
392
+ }
393
+ return receiptSet.size;
394
+ }
395
+ function costSortValue(cost) {
396
+ return cost?.amountUsd ?? Number.POSITIVE_INFINITY;
397
+ }
398
+ function createScoreReasons(candidate, preferences, strategy, mode) {
399
+ const reasons = [`strategy:${mode}`];
400
+ if (mode === "provider_order") {
401
+ const preferredOptionIndex = preferenceIndex(
402
+ preferences?.preferredPaymentOptions ?? [],
403
+ candidate.option.id
404
+ );
405
+ const preferredProtocolIndex = preferenceIndex(
406
+ preferences?.preferredProtocols ?? [],
407
+ candidate.option.protocolMode
408
+ );
409
+ if (preferredOptionIndex !== Number.MAX_SAFE_INTEGER) {
410
+ reasons.push(`preferred_option:${candidate.option.id}`);
411
+ } else if (preferredProtocolIndex !== Number.MAX_SAFE_INTEGER) {
412
+ reasons.push(`preferred_protocol:${candidate.option.protocolMode}`);
413
+ } else {
414
+ reasons.push(`provider_order:${candidate.index}`);
415
+ }
416
+ }
417
+ if (mode === "lowest_cost" || mode === "preferred_protocol" || mode === "receipt_coverage") {
418
+ reasons.push(
419
+ candidate.cost ? `cost:${candidate.cost.amountUsd}` : "cost:unknown"
420
+ );
421
+ }
422
+ if (mode === "preferred_protocol") {
423
+ reasons.push(
424
+ isPreferredProtocol(candidate, preferences) ? `preferred_protocol:${candidate.option.protocolMode}` : `protocol:${candidate.option.protocolMode}`
425
+ );
426
+ }
427
+ if (mode === "receipt_coverage") {
428
+ const desiredReceiptTypes = strategy?.preferredReceiptTypes ?? preferences?.requiredReceiptTypes ?? [];
429
+ reasons.push(
430
+ desiredReceiptTypes.length > 0 ? `receipt_coverage:${receiptCoverage(candidate, preferences, strategy)}/${desiredReceiptTypes.length}` : `receipt_coverage:${candidate.receiptTypes.length}`
431
+ );
432
+ if (isPreferredProtocol(candidate, preferences)) {
433
+ reasons.push(`preferred_protocol:${candidate.option.protocolMode}`);
434
+ }
435
+ }
436
+ return reasons;
437
+ }
438
+ function isPreferredProtocol(candidate, preferences) {
439
+ return preferences?.preferredProtocols?.includes(candidate.option.protocolMode) ?? false;
440
+ }
441
+ function createNoPaymentOptionReason(input, capabilityCandidateCount) {
442
+ if (capabilityCandidateCount === 0) {
443
+ return `Provider "${input.providerId}" has no payment option for capability "${input.capability}".`;
444
+ }
445
+ return `Provider "${input.providerId}" has no payment option matching paymentPreferences for capability "${input.capability}".`;
446
+ }
447
+ function resolvePaymentOptionPricing(option, capability) {
448
+ return resolvePricing(option.pricing, capability);
449
+ }
450
+ function resolvePricing(pricing, capability) {
451
+ if (!pricing) {
452
+ return void 0;
453
+ }
454
+ if (isPricing(pricing)) {
455
+ return pricing;
456
+ }
457
+ const capabilityPricing = pricing[capability];
458
+ return isPricing(capabilityPricing) ? capabilityPricing : void 0;
459
+ }
460
+ function pricingToCost(pricing) {
461
+ if (!pricing) {
462
+ return void 0;
463
+ }
464
+ return {
465
+ amountUsd: pricing.amountUsd,
466
+ currency: pricing.currency ?? "USD",
467
+ unit: pricing.unit
468
+ };
469
+ }
470
+ function validatePreferenceStringArray(value, field, capability) {
471
+ if (value !== void 0 && (!Array.isArray(value) || value.some((entry) => typeof entry !== "string" || entry.length === 0))) {
472
+ throw new ProbeMeshError({
473
+ code: "invalid_request",
474
+ message: `paymentPreferences.${field} must be an array of non-empty strings when provided.`,
475
+ capability
476
+ });
477
+ }
478
+ }
479
+ function validatePricingDeclaration(pricing, path, errors) {
480
+ if (!isRecord(pricing)) {
481
+ errors.push({
482
+ path,
483
+ message: "payment option pricing must be an object."
484
+ });
485
+ return;
486
+ }
487
+ if (isPricing(pricing)) {
488
+ validatePricing(pricing, path, errors);
489
+ return;
490
+ }
491
+ for (const [capability, capabilityPricing] of Object.entries(pricing)) {
492
+ validatePricing(capabilityPricing, `${path}.${capability}`, errors);
493
+ }
494
+ }
495
+ function validatePricing(pricing, path, errors) {
496
+ if (!isRecord(pricing)) {
497
+ errors.push({
498
+ path,
499
+ message: "payment option pricing entry must be an object."
500
+ });
501
+ return;
502
+ }
503
+ validateNonEmptyString(pricing.unit, `${path}.unit`, errors);
504
+ if (typeof pricing.amountUsd !== "number" || !Number.isFinite(pricing.amountUsd) || pricing.amountUsd < 0) {
505
+ errors.push({
506
+ path: `${path}.amountUsd`,
507
+ message: "amountUsd must be a non-negative number."
508
+ });
509
+ }
510
+ if (pricing.currency !== void 0 && typeof pricing.currency !== "string") {
511
+ errors.push({
512
+ path: `${path}.currency`,
513
+ message: "currency must be a string when provided."
514
+ });
515
+ }
516
+ }
517
+ function validateStringArray(value, path, errors) {
518
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string" || entry.length === 0)) {
519
+ errors.push({
520
+ path,
521
+ message: `${path} must be an array of non-empty strings.`
522
+ });
523
+ }
524
+ }
525
+ function validateNonEmptyString(value, path, errors) {
526
+ if (typeof value !== "string" || value.length === 0) {
527
+ errors.push({
528
+ path,
529
+ message: `${path} must be a non-empty string.`
530
+ });
531
+ }
532
+ }
533
+ function isPricing(value) {
534
+ return isRecord(value) && typeof value.unit === "string" && value.unit.length > 0 && typeof value.amountUsd === "number" && Number.isFinite(value.amountUsd) && value.amountUsd >= 0 && (value.currency === void 0 || typeof value.currency === "string");
535
+ }
536
+ function isReceiptType(value) {
537
+ return value === "payment_proof" || value === "settlement_confirmation" || value === "provider_delivery" || value === "provider_response_evidence" || value === "authorization_decision";
538
+ }
539
+ function isPaymentStrategyMode(value) {
540
+ return value === "provider_order" || value === "lowest_cost" || value === "preferred_protocol" || value === "receipt_coverage";
541
+ }
542
+ function compactPaymentPreferences(preferences) {
543
+ const entries = Object.entries(preferences).filter(
544
+ ([, value]) => value !== void 0 && (!Array.isArray(value) || value.length > 0)
545
+ );
546
+ return entries.length > 0 ? Object.fromEntries(entries) : void 0;
547
+ }
548
+ function intersectArrays(first, second) {
549
+ if (first === void 0) {
550
+ return second === void 0 ? void 0 : [...second];
551
+ }
552
+ if (second === void 0) {
553
+ return [...first];
554
+ }
555
+ const secondValues = new Set(second);
556
+ return first.filter((value) => secondValues.has(value));
557
+ }
558
+ function unionArrays(first, second) {
559
+ const values = [...first ?? [], ...second ?? []];
560
+ return values.length > 0 ? [...new Set(values)] : void 0;
561
+ }
562
+ function isRecord(value) {
563
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
564
+ }
565
+ function cloneSerializable(value) {
566
+ if (value === void 0) {
567
+ return value;
568
+ }
569
+ return JSON.parse(JSON.stringify(value));
570
+ }
571
+
572
+ // src/providerKit/manifest.ts
573
+ var JSON_SCHEMA_HINT_KEYS = /* @__PURE__ */ new Set([
574
+ "$schema",
575
+ "$id",
576
+ "type",
577
+ "properties",
578
+ "required",
579
+ "items",
580
+ "enum",
581
+ "description",
582
+ "additionalProperties",
583
+ "oneOf",
584
+ "anyOf",
585
+ "allOf"
586
+ ]);
587
+ function validateProviderManifest(manifest) {
588
+ const errors = [];
589
+ if (!isRecord2(manifest)) {
590
+ return {
591
+ valid: false,
592
+ errors: [
593
+ {
594
+ path: "$",
595
+ message: "Provider manifest must be an object."
596
+ }
597
+ ]
598
+ };
599
+ }
600
+ validateNonEmptyString2(manifest.id, "id", errors);
601
+ validateNonEmptyString2(manifest.displayName, "displayName", errors);
602
+ validateCapabilityList(manifest.capabilities, errors);
603
+ validateNonEmptyString2(manifest.protocolMode, "protocolMode", errors);
604
+ validateProviderPaymentOptions(
605
+ manifest.paymentOptions,
606
+ "paymentOptions",
607
+ Array.isArray(manifest.capabilities) ? manifest.capabilities : [],
608
+ errors
609
+ );
610
+ validatePricingDeclaration2(
611
+ manifest.pricing,
612
+ "pricing",
613
+ manifest.capabilities,
614
+ errors
615
+ );
616
+ validateSchemaDeclaration(
617
+ manifest.inputSchema,
618
+ "inputSchema",
619
+ manifest.capabilities,
620
+ errors
621
+ );
622
+ validateSchemaDeclaration(
623
+ manifest.outputSchema,
624
+ "outputSchema",
625
+ manifest.capabilities,
626
+ errors
627
+ );
628
+ validateReceipts(manifest.receipts, errors);
629
+ if (manifest.limits !== void 0) {
630
+ validateLimits(manifest.limits, errors);
631
+ }
632
+ return {
633
+ valid: errors.length === 0,
634
+ errors
635
+ };
636
+ }
637
+ function isSharedJsonSchemaLike(value) {
638
+ if (!isRecord2(value)) {
639
+ return false;
640
+ }
641
+ return Object.keys(value).some((key) => JSON_SCHEMA_HINT_KEYS.has(key));
642
+ }
643
+ function isSharedPricing(value) {
644
+ return isRecord2(value) && typeof value.unit === "string" && value.unit.length > 0 && typeof value.amountUsd === "number";
645
+ }
646
+ function validateCapabilityList(capabilities, errors) {
647
+ if (!Array.isArray(capabilities) || capabilities.length === 0) {
648
+ errors.push({
649
+ path: "capabilities",
650
+ message: "capabilities must be a non-empty array."
651
+ });
652
+ return;
653
+ }
654
+ const seen = /* @__PURE__ */ new Set();
655
+ capabilities.forEach((capability, index) => {
656
+ if (typeof capability !== "string" || capability.length === 0) {
657
+ errors.push({
658
+ path: `capabilities.${index}`,
659
+ message: "capability ids must be non-empty strings."
660
+ });
661
+ return;
662
+ }
663
+ if (seen.has(capability)) {
664
+ errors.push({
665
+ path: `capabilities.${index}`,
666
+ message: `duplicate capability "${capability}".`
667
+ });
668
+ }
669
+ seen.add(capability);
670
+ });
671
+ }
672
+ function validatePricingDeclaration2(pricing, path, capabilities, errors) {
673
+ if (!isRecord2(pricing)) {
674
+ errors.push({
675
+ path,
676
+ message: "pricing must be an object."
677
+ });
678
+ return;
679
+ }
680
+ if (isSharedPricing(pricing)) {
681
+ validatePricing2(pricing, path, errors);
682
+ return;
683
+ }
684
+ if (!Array.isArray(capabilities)) {
685
+ return;
686
+ }
687
+ for (const capability of capabilities) {
688
+ if (typeof capability !== "string" || capability.length === 0) {
689
+ continue;
690
+ }
691
+ validatePricing2(pricing[capability], `${path}.${capability}`, errors);
692
+ }
693
+ }
694
+ function validatePricing2(pricing, path, errors) {
695
+ if (!isRecord2(pricing)) {
696
+ errors.push({
697
+ path,
698
+ message: "pricing entry must be an object."
699
+ });
700
+ return;
701
+ }
702
+ validateNonEmptyString2(pricing.unit, `${path}.unit`, errors);
703
+ if (typeof pricing.amountUsd !== "number" || !Number.isFinite(pricing.amountUsd) || pricing.amountUsd < 0) {
704
+ errors.push({
705
+ path: `${path}.amountUsd`,
706
+ message: "amountUsd must be a non-negative number."
707
+ });
708
+ }
709
+ if (pricing.currency !== void 0 && typeof pricing.currency !== "string") {
710
+ errors.push({
711
+ path: `${path}.currency`,
712
+ message: "currency must be a string when provided."
713
+ });
714
+ }
715
+ }
716
+ function validateSchemaDeclaration(schema, path, capabilities, errors) {
717
+ if (!isRecord2(schema)) {
718
+ errors.push({
719
+ path,
720
+ message: `${path} must be an object.`
721
+ });
722
+ return;
723
+ }
724
+ if (isSharedJsonSchemaLike(schema)) {
725
+ validateSchemaLike(schema, path, errors);
726
+ return;
727
+ }
728
+ if (!Array.isArray(capabilities)) {
729
+ return;
730
+ }
731
+ for (const capability of capabilities) {
732
+ if (typeof capability !== "string" || capability.length === 0) {
733
+ continue;
734
+ }
735
+ validateSchemaLike(schema[capability], `${path}.${capability}`, errors);
736
+ }
737
+ }
738
+ function validateSchemaLike(schema, path, errors) {
739
+ if (!isRecord2(schema)) {
740
+ errors.push({
741
+ path,
742
+ message: "schema entry must be an object."
743
+ });
744
+ return;
745
+ }
746
+ if (schema.type !== void 0 && typeof schema.type !== "string") {
747
+ errors.push({
748
+ path: `${path}.type`,
749
+ message: "schema type must be a string when provided."
750
+ });
751
+ }
752
+ if (schema.properties !== void 0 && !isRecord2(schema.properties)) {
753
+ errors.push({
754
+ path: `${path}.properties`,
755
+ message: "schema properties must be an object when provided."
756
+ });
757
+ }
758
+ if (schema.required !== void 0 && (!Array.isArray(schema.required) || schema.required.some((field) => typeof field !== "string"))) {
759
+ errors.push({
760
+ path: `${path}.required`,
761
+ message: "schema required must be an array of strings when provided."
762
+ });
763
+ }
764
+ }
765
+ function validateReceipts(receipts, errors) {
766
+ if (!isRecord2(receipts)) {
767
+ errors.push({
768
+ path: "receipts",
769
+ message: "receipts must be an object."
770
+ });
771
+ return;
772
+ }
773
+ validateBoolean(receipts.payment, "receipts.payment", errors);
774
+ validateBoolean(receipts.delivery, "receipts.delivery", errors);
775
+ validateBoolean(
776
+ receipts.responseEvidence,
777
+ "receipts.responseEvidence",
778
+ errors
779
+ );
780
+ }
781
+ function validateLimits(limits, errors) {
782
+ if (!isRecord2(limits)) {
783
+ errors.push({
784
+ path: "limits",
785
+ message: "limits must be an object when provided."
786
+ });
787
+ return;
788
+ }
789
+ const typedLimits = limits;
790
+ if (typedLimits.maxRequestsPerMinute !== void 0 && (!Number.isFinite(typedLimits.maxRequestsPerMinute) || typedLimits.maxRequestsPerMinute <= 0)) {
791
+ errors.push({
792
+ path: "limits.maxRequestsPerMinute",
793
+ message: "maxRequestsPerMinute must be a positive number when provided."
794
+ });
795
+ }
796
+ }
797
+ function validateNonEmptyString2(value, path, errors) {
798
+ if (typeof value !== "string" || value.length === 0) {
799
+ errors.push({
800
+ path,
801
+ message: `${path} must be a non-empty string.`
802
+ });
803
+ }
804
+ }
805
+ function validateBoolean(value, path, errors) {
806
+ if (typeof value !== "boolean") {
807
+ errors.push({
808
+ path,
809
+ message: `${path} must be a boolean.`
810
+ });
811
+ }
812
+ }
813
+ function isRecord2(value) {
814
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
815
+ }
816
+
817
+ // src/providerCatalogArtifact.ts
818
+ var PROVIDER_CATALOG_ARTIFACT_SCHEMA_VERSION = "probemesh.provider-catalog.v1";
819
+ function validateProviderCatalogArtifact(artifact) {
820
+ const errors = [];
821
+ if (!isRecord3(artifact)) {
822
+ return {
823
+ valid: false,
824
+ errors: [
825
+ {
826
+ path: "$",
827
+ message: "Provider catalog artifact must be an object."
828
+ }
829
+ ]
830
+ };
831
+ }
832
+ if (artifact.schemaVersion !== PROVIDER_CATALOG_ARTIFACT_SCHEMA_VERSION) {
833
+ errors.push({
834
+ path: "schemaVersion",
835
+ message: `schemaVersion must be "${PROVIDER_CATALOG_ARTIFACT_SCHEMA_VERSION}".`
836
+ });
837
+ }
838
+ validateNonEmptyString3(artifact.artifactId, "artifactId", errors);
839
+ validateIsoTimestamp(artifact.generatedAt, "generatedAt", errors);
840
+ if (artifact.status !== "ready" && artifact.status !== "rejected") {
841
+ errors.push({
842
+ path: "status",
843
+ message: 'status must be "ready" or "rejected".'
844
+ });
845
+ }
846
+ const manifestValidation = validateProviderManifest(artifact.provider);
847
+ for (const manifestError of manifestValidation.errors) {
848
+ errors.push({
849
+ path: `provider.${manifestError.path}`,
850
+ message: manifestError.message
851
+ });
852
+ }
853
+ validateAcceptanceArtifact(artifact.acceptance, errors);
854
+ return {
855
+ valid: errors.length === 0,
856
+ errors
857
+ };
858
+ }
859
+ function validateAcceptanceArtifact(acceptance, errors) {
860
+ if (!isRecord3(acceptance)) {
861
+ errors.push({
862
+ path: "acceptance",
863
+ message: "acceptance must be an object."
864
+ });
865
+ return;
866
+ }
867
+ validateNonEmptyString3(acceptance.title, "acceptance.title", errors);
868
+ if (acceptance.status !== "passed" && acceptance.status !== "failed") {
869
+ errors.push({
870
+ path: "acceptance.status",
871
+ message: 'acceptance.status must be "passed" or "failed".'
872
+ });
873
+ }
874
+ if (!isRecord3(acceptance.gate)) {
875
+ errors.push({
876
+ path: "acceptance.gate",
877
+ message: "acceptance.gate must be an object."
878
+ });
879
+ } else {
880
+ if (typeof acceptance.gate.accepted !== "boolean") {
881
+ errors.push({
882
+ path: "acceptance.gate.accepted",
883
+ message: "acceptance.gate.accepted must be a boolean."
884
+ });
885
+ }
886
+ if (acceptance.gate.status !== "accepted" && acceptance.gate.status !== "rejected") {
887
+ errors.push({
888
+ path: "acceptance.gate.status",
889
+ message: 'acceptance.gate.status must be "accepted" or "rejected".'
890
+ });
891
+ }
892
+ }
893
+ validateStringArray2(
894
+ acceptance.failedChecks,
895
+ "acceptance.failedChecks",
896
+ errors
897
+ );
898
+ validateReceiptSummary(acceptance.receipts, errors);
899
+ validateStringArray2(
900
+ acceptance.timelineEventTypes,
901
+ "acceptance.timelineEventTypes",
902
+ errors
903
+ );
904
+ validateStringArray2(
905
+ acceptance.telemetryEventTypes,
906
+ "acceptance.telemetryEventTypes",
907
+ errors
908
+ );
909
+ }
910
+ function validateReceiptSummary(receipts, errors) {
911
+ if (!isRecord3(receipts)) {
912
+ errors.push({
913
+ path: "acceptance.receipts",
914
+ message: "acceptance.receipts must be an object."
915
+ });
916
+ return;
917
+ }
918
+ if (typeof receipts.total !== "number" || !Number.isFinite(receipts.total)) {
919
+ errors.push({
920
+ path: "acceptance.receipts.total",
921
+ message: "acceptance.receipts.total must be a number."
922
+ });
923
+ }
924
+ validateStringArray2(receipts.types, "acceptance.receipts.types", errors);
925
+ validateStringArray2(
926
+ receipts.providers,
927
+ "acceptance.receipts.providers",
928
+ errors
929
+ );
930
+ for (const key of [
931
+ "hasPaymentProof",
932
+ "hasSettlementConfirmation",
933
+ "hasProviderDelivery"
934
+ ]) {
935
+ if (typeof receipts[key] !== "boolean") {
936
+ errors.push({
937
+ path: `acceptance.receipts.${key}`,
938
+ message: `acceptance.receipts.${key} must be a boolean.`
939
+ });
940
+ }
941
+ }
942
+ }
943
+ function validateStringArray2(value, path, errors) {
944
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
945
+ errors.push({
946
+ path,
947
+ message: `${path} must be an array of strings.`
948
+ });
949
+ }
950
+ }
951
+ function validateNonEmptyString3(value, path, errors) {
952
+ if (typeof value !== "string" || value.length === 0) {
953
+ errors.push({
954
+ path,
955
+ message: `${path} must be a non-empty string.`
956
+ });
957
+ }
958
+ }
959
+ function validateIsoTimestamp(value, path, errors) {
960
+ if (typeof value !== "string" || Number.isNaN(Date.parse(value))) {
961
+ errors.push({
962
+ path,
963
+ message: `${path} must be an ISO timestamp string.`
964
+ });
965
+ }
966
+ }
967
+ function isRecord3(value) {
968
+ return !!value && typeof value === "object" && !Array.isArray(value);
969
+ }
970
+
971
+ // src/providerCatalog.ts
972
+ var PROVIDER_CATALOG_SCHEMA_VERSION = "probemesh.provider-catalog.index.v1";
973
+ function validateProviderCatalog(catalog) {
974
+ const errors = [];
975
+ if (!isRecord4(catalog)) {
976
+ return {
977
+ valid: false,
978
+ errors: [
979
+ {
980
+ path: "$",
981
+ message: "Provider catalog must be an object."
982
+ }
983
+ ]
984
+ };
985
+ }
986
+ if (catalog.schemaVersion !== PROVIDER_CATALOG_SCHEMA_VERSION) {
987
+ errors.push({
988
+ path: "schemaVersion",
989
+ message: `schemaVersion must be "${PROVIDER_CATALOG_SCHEMA_VERSION}".`
990
+ });
991
+ }
992
+ validateIsoTimestamp2(catalog.generatedAt, "generatedAt", errors);
993
+ if (!Array.isArray(catalog.entries)) {
994
+ errors.push({
995
+ path: "entries",
996
+ message: "entries must be an array."
997
+ });
998
+ } else {
999
+ catalog.entries.forEach(
1000
+ (entry, index) => validateProviderCatalogEntry(entry, `entries.${index}`, errors)
1001
+ );
1002
+ }
1003
+ return {
1004
+ valid: errors.length === 0,
1005
+ errors
1006
+ };
1007
+ }
1008
+ function assertProviderCatalog(catalog) {
1009
+ const result = validateProviderCatalog(catalog);
1010
+ if (result.valid) {
1011
+ return;
1012
+ }
1013
+ throw new ProbeMeshError({
1014
+ code: "invalid_request",
1015
+ message: `Invalid provider catalog: ${formatValidationErrors(result.errors)}`
1016
+ });
1017
+ }
1018
+ function createProviderCatalog(options) {
1019
+ assertProviderCatalogOptions(options);
1020
+ const entries = options.artifacts.map((artifact, index) => {
1021
+ const validation = validateProviderCatalogArtifact(artifact);
1022
+ if (!validation.valid) {
1023
+ throw new ProbeMeshError({
1024
+ code: "invalid_request",
1025
+ message: `Invalid provider catalog artifact at index ${index}: ${formatValidationErrors(
1026
+ validation.errors
1027
+ )}`
1028
+ });
1029
+ }
1030
+ return createCatalogEntryFromArtifact(artifact);
1031
+ });
1032
+ const catalog = {
1033
+ schemaVersion: PROVIDER_CATALOG_SCHEMA_VERSION,
1034
+ generatedAt: options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1035
+ entries
1036
+ };
1037
+ assertProviderCatalog(catalog);
1038
+ return catalog;
1039
+ }
1040
+ function findProviderCatalogMatches(catalog, query) {
1041
+ assertProviderCatalog(catalog);
1042
+ assertProviderCatalogQuery(query);
1043
+ const requiredStatus = query.status ?? "ready";
1044
+ const requiredReceiptTypes = query.requiredReceiptTypes ?? [];
1045
+ const providerIds = query.providerIds;
1046
+ const blockedProviderIds = new Set(query.blockedProviderIds ?? []);
1047
+ return catalog.entries.flatMap((entry) => {
1048
+ const providerId = entry.provider.id;
1049
+ if (entry.status !== requiredStatus) {
1050
+ return [];
1051
+ }
1052
+ if (!entry.provider.capabilities.includes(query.capability)) {
1053
+ return [];
1054
+ }
1055
+ if (query.protocolMode !== void 0 && entry.provider.protocolMode !== query.protocolMode) {
1056
+ return [];
1057
+ }
1058
+ if (providerIds !== void 0 && !providerIds.includes(providerId)) {
1059
+ return [];
1060
+ }
1061
+ if (blockedProviderIds.has(providerId)) {
1062
+ return [];
1063
+ }
1064
+ const paymentPreferences = mergePaymentPreferences(
1065
+ query.protocolMode ? {
1066
+ allowedProtocols: [query.protocolMode]
1067
+ } : void 0,
1068
+ query.paymentPreferences
1069
+ );
1070
+ const paymentResolution = resolveProviderPaymentOption({
1071
+ providerId,
1072
+ providerMode: entry.provider.protocolMode,
1073
+ capabilities: entry.provider.capabilities,
1074
+ capability: query.capability,
1075
+ pricing: entry.provider.pricing,
1076
+ paymentOptions: entry.provider.paymentOptions,
1077
+ paymentPreferences,
1078
+ paymentStrategy: query.paymentStrategy,
1079
+ defaultReceiptTypes: entry.acceptance.receipts.types
1080
+ });
1081
+ const cost = paymentSelectionCost(paymentResolution.selection) ?? resolveCatalogCost(entry, query.capability);
1082
+ if (query.maxCostUsd !== void 0 && (cost === void 0 || cost.amountUsd > query.maxCostUsd)) {
1083
+ return [];
1084
+ }
1085
+ if (!hasRequiredReceiptTypes(entry, requiredReceiptTypes)) {
1086
+ return [];
1087
+ }
1088
+ if (!paymentResolution.selection) {
1089
+ return [];
1090
+ }
1091
+ return [
1092
+ {
1093
+ entry,
1094
+ providerId,
1095
+ capability: query.capability,
1096
+ status: entry.status,
1097
+ cost,
1098
+ paymentSelection: paymentResolution.selection,
1099
+ reasons: createMatchReasons(
1100
+ entry,
1101
+ query,
1102
+ cost,
1103
+ paymentResolution.selection
1104
+ )
1105
+ }
1106
+ ];
1107
+ });
1108
+ }
1109
+ function summarizeProviderCatalog(catalog) {
1110
+ assertProviderCatalog(catalog);
1111
+ const providers = /* @__PURE__ */ new Set();
1112
+ const capabilities = /* @__PURE__ */ new Set();
1113
+ const protocolModes = /* @__PURE__ */ new Set();
1114
+ let readyEntries = 0;
1115
+ let rejectedEntries = 0;
1116
+ for (const entry of catalog.entries) {
1117
+ providers.add(entry.provider.id);
1118
+ protocolModes.add(entry.provider.protocolMode);
1119
+ for (const capability of entry.provider.capabilities) {
1120
+ capabilities.add(capability);
1121
+ }
1122
+ if (entry.status === "ready") {
1123
+ readyEntries += 1;
1124
+ } else {
1125
+ rejectedEntries += 1;
1126
+ }
1127
+ }
1128
+ return {
1129
+ schemaVersion: catalog.schemaVersion,
1130
+ generatedAt: catalog.generatedAt,
1131
+ totalEntries: catalog.entries.length,
1132
+ readyEntries,
1133
+ rejectedEntries,
1134
+ providers: [...providers],
1135
+ capabilities: [...capabilities],
1136
+ protocolModes: [...protocolModes]
1137
+ };
1138
+ }
1139
+ function assertProviderCatalogOptions(options) {
1140
+ if (!isRecord4(options)) {
1141
+ throw new ProbeMeshError({
1142
+ code: "invalid_request",
1143
+ message: "Provider catalog options must be an object."
1144
+ });
1145
+ }
1146
+ if (!Array.isArray(options.artifacts)) {
1147
+ throw new ProbeMeshError({
1148
+ code: "invalid_request",
1149
+ message: "Provider catalog options require an artifacts array."
1150
+ });
1151
+ }
1152
+ if (options.generatedAt !== void 0 && (typeof options.generatedAt !== "string" || Number.isNaN(Date.parse(options.generatedAt)))) {
1153
+ throw new ProbeMeshError({
1154
+ code: "invalid_request",
1155
+ message: "Provider catalog generatedAt must be an ISO timestamp."
1156
+ });
1157
+ }
1158
+ }
1159
+ function assertProviderCatalogQuery(query) {
1160
+ if (!isRecord4(query)) {
1161
+ throw new ProbeMeshError({
1162
+ code: "invalid_request",
1163
+ message: "Provider catalog query must be an object."
1164
+ });
1165
+ }
1166
+ if (typeof query.capability !== "string" || query.capability.length === 0) {
1167
+ throw new ProbeMeshError({
1168
+ code: "invalid_request",
1169
+ message: "Provider catalog query capability must be a non-empty string."
1170
+ });
1171
+ }
1172
+ if (query.protocolMode !== void 0 && (typeof query.protocolMode !== "string" || query.protocolMode.length === 0)) {
1173
+ throw new ProbeMeshError({
1174
+ code: "invalid_request",
1175
+ message: "Provider catalog query protocolMode must be a non-empty string."
1176
+ });
1177
+ }
1178
+ validatePaymentPreferences(query.paymentPreferences, query.capability);
1179
+ validatePaymentStrategy(query.paymentStrategy, query.capability);
1180
+ if (query.maxCostUsd !== void 0 && (typeof query.maxCostUsd !== "number" || !Number.isFinite(query.maxCostUsd) || query.maxCostUsd < 0)) {
1181
+ throw new ProbeMeshError({
1182
+ code: "invalid_request",
1183
+ message: "Provider catalog query maxCostUsd must be a non-negative finite number."
1184
+ });
1185
+ }
1186
+ if (query.status !== void 0 && query.status !== "ready" && query.status !== "rejected") {
1187
+ throw new ProbeMeshError({
1188
+ code: "invalid_request",
1189
+ message: 'Provider catalog query status must be "ready" or "rejected".'
1190
+ });
1191
+ }
1192
+ validateQueryStringArray(query.providerIds, "providerIds");
1193
+ validateQueryStringArray(query.blockedProviderIds, "blockedProviderIds");
1194
+ if (query.requiredReceiptTypes !== void 0 && (!Array.isArray(query.requiredReceiptTypes) || query.requiredReceiptTypes.some((receiptType) => !isReceiptType2(receiptType)))) {
1195
+ throw new ProbeMeshError({
1196
+ code: "invalid_request",
1197
+ message: "Provider catalog query requiredReceiptTypes must contain known receipt types."
1198
+ });
1199
+ }
1200
+ }
1201
+ function createCatalogEntryFromArtifact(artifact) {
1202
+ return {
1203
+ artifactId: artifact.artifactId,
1204
+ generatedAt: artifact.generatedAt,
1205
+ status: artifact.status,
1206
+ provider: {
1207
+ id: artifact.provider.id,
1208
+ displayName: artifact.provider.displayName,
1209
+ capabilities: [...artifact.provider.capabilities],
1210
+ protocolMode: artifact.provider.protocolMode,
1211
+ paymentOptions: cloneSerializable2(artifact.provider.paymentOptions),
1212
+ pricing: cloneSerializable2(artifact.provider.pricing),
1213
+ receipts: cloneSerializable2(artifact.provider.receipts),
1214
+ limits: cloneSerializable2(artifact.provider.limits)
1215
+ },
1216
+ acceptance: {
1217
+ gate: cloneSerializable2(artifact.acceptance.gate),
1218
+ failedChecks: [...artifact.acceptance.failedChecks],
1219
+ receipts: cloneSerializable2(artifact.acceptance.receipts),
1220
+ safety: cloneSerializable2(artifact.acceptance.safety),
1221
+ retrySafety: cloneSerializable2(artifact.acceptance.retrySafety),
1222
+ route: cloneSerializable2(artifact.acceptance.route),
1223
+ timelineEventTypes: [...artifact.acceptance.timelineEventTypes],
1224
+ telemetryEventTypes: [...artifact.acceptance.telemetryEventTypes],
1225
+ errorCode: artifact.acceptance.errorCode
1226
+ }
1227
+ };
1228
+ }
1229
+ function resolveCatalogCost(entry, capability) {
1230
+ const pricing = entry.provider.pricing;
1231
+ if (isProbeMeshPricing(pricing)) {
1232
+ return pricingToCost2(pricing);
1233
+ }
1234
+ const capabilityPricing = pricing[capability];
1235
+ if (!isProbeMeshPricing(capabilityPricing)) {
1236
+ return void 0;
1237
+ }
1238
+ return pricingToCost2(capabilityPricing);
1239
+ }
1240
+ function pricingToCost2(pricing) {
1241
+ return {
1242
+ amountUsd: pricing.amountUsd,
1243
+ currency: pricing.currency,
1244
+ unit: pricing.unit
1245
+ };
1246
+ }
1247
+ function hasRequiredReceiptTypes(entry, requiredReceiptTypes) {
1248
+ const receiptTypes = new Set(entry.acceptance.receipts.types);
1249
+ return requiredReceiptTypes.every(
1250
+ (receiptType) => receiptTypes.has(receiptType)
1251
+ );
1252
+ }
1253
+ function createMatchReasons(entry, query, cost, paymentSelection) {
1254
+ const reasons = [
1255
+ `status:${entry.status}`,
1256
+ `capability:${query.capability}`,
1257
+ `protocol:${entry.provider.protocolMode}`
1258
+ ];
1259
+ if (query.paymentPreferences !== void 0) {
1260
+ reasons.push("payment_preferences:matched");
1261
+ }
1262
+ if (query.paymentStrategy !== void 0) {
1263
+ reasons.push(`payment_strategy:${query.paymentStrategy.mode}`);
1264
+ }
1265
+ if (paymentSelection?.score !== void 0) {
1266
+ reasons.push(`payment_score:${paymentSelection.score}`);
1267
+ }
1268
+ for (const scoreReason of paymentSelection?.scoreReasons ?? []) {
1269
+ reasons.push(`payment_score_reason:${scoreReason}`);
1270
+ }
1271
+ if (query.providerIds !== void 0) {
1272
+ reasons.push("provider:allowed");
1273
+ }
1274
+ if (query.maxCostUsd !== void 0 && cost !== void 0) {
1275
+ reasons.push(`cost:${cost.amountUsd}<=${query.maxCostUsd}`);
1276
+ }
1277
+ if ((query.requiredReceiptTypes ?? []).length > 0) {
1278
+ reasons.push(`receipts:${query.requiredReceiptTypes?.join(",")}`);
1279
+ }
1280
+ return reasons;
1281
+ }
1282
+ function paymentSelectionCost(selection) {
1283
+ return selection?.cost;
1284
+ }
1285
+ function validateQueryStringArray(value, name) {
1286
+ if (value !== void 0 && (!Array.isArray(value) || value.some((entry) => typeof entry !== "string" || entry.length === 0))) {
1287
+ throw new ProbeMeshError({
1288
+ code: "invalid_request",
1289
+ message: `Provider catalog query ${name} must be an array of non-empty strings.`
1290
+ });
1291
+ }
1292
+ }
1293
+ function isProbeMeshPricing(value) {
1294
+ return isRecord4(value) && typeof value.unit === "string" && typeof value.amountUsd === "number" && Number.isFinite(value.amountUsd) && value.amountUsd >= 0 && (value.currency === void 0 || typeof value.currency === "string");
1295
+ }
1296
+ function isReceiptType2(value) {
1297
+ return value === "payment_proof" || value === "settlement_confirmation" || value === "provider_delivery" || value === "provider_response_evidence" || value === "authorization_decision";
1298
+ }
1299
+ function cloneSerializable2(value) {
1300
+ if (value === void 0) {
1301
+ return value;
1302
+ }
1303
+ return JSON.parse(JSON.stringify(value));
1304
+ }
1305
+ function validateProviderCatalogEntry(entry, path, errors) {
1306
+ if (!isRecord4(entry)) {
1307
+ errors.push({
1308
+ path,
1309
+ message: "catalog entry must be an object."
1310
+ });
1311
+ return;
1312
+ }
1313
+ validateNonEmptyString4(entry.artifactId, `${path}.artifactId`, errors);
1314
+ validateIsoTimestamp2(entry.generatedAt, `${path}.generatedAt`, errors);
1315
+ if (entry.status !== "ready" && entry.status !== "rejected") {
1316
+ errors.push({
1317
+ path: `${path}.status`,
1318
+ message: 'status must be "ready" or "rejected".'
1319
+ });
1320
+ }
1321
+ validateEntryProvider(entry.provider, `${path}.provider`, errors);
1322
+ validateEntryAcceptance(entry.acceptance, `${path}.acceptance`, errors);
1323
+ }
1324
+ function validateEntryProvider(provider, path, errors) {
1325
+ if (!isRecord4(provider)) {
1326
+ errors.push({
1327
+ path,
1328
+ message: "provider must be an object."
1329
+ });
1330
+ return;
1331
+ }
1332
+ validateNonEmptyString4(provider.id, `${path}.id`, errors);
1333
+ validateNonEmptyString4(provider.displayName, `${path}.displayName`, errors);
1334
+ validateStringArray3(provider.capabilities, `${path}.capabilities`, errors);
1335
+ validateNonEmptyString4(provider.protocolMode, `${path}.protocolMode`, errors);
1336
+ validateProviderPaymentOptions(
1337
+ provider.paymentOptions,
1338
+ `${path}.paymentOptions`,
1339
+ Array.isArray(provider.capabilities) ? provider.capabilities : [],
1340
+ errors
1341
+ );
1342
+ validatePricingDeclaration3(provider.pricing, `${path}.pricing`, errors);
1343
+ validateReceiptSupport(provider.receipts, `${path}.receipts`, errors);
1344
+ if (provider.limits !== void 0 && !isRecord4(provider.limits)) {
1345
+ errors.push({
1346
+ path: `${path}.limits`,
1347
+ message: "limits must be an object when provided."
1348
+ });
1349
+ }
1350
+ }
1351
+ function validateEntryAcceptance(acceptance, path, errors) {
1352
+ if (!isRecord4(acceptance)) {
1353
+ errors.push({
1354
+ path,
1355
+ message: "acceptance must be an object."
1356
+ });
1357
+ return;
1358
+ }
1359
+ if (!isRecord4(acceptance.gate)) {
1360
+ errors.push({
1361
+ path: `${path}.gate`,
1362
+ message: "gate must be an object."
1363
+ });
1364
+ } else {
1365
+ if (typeof acceptance.gate.accepted !== "boolean") {
1366
+ errors.push({
1367
+ path: `${path}.gate.accepted`,
1368
+ message: "gate.accepted must be a boolean."
1369
+ });
1370
+ }
1371
+ if (acceptance.gate.status !== "accepted" && acceptance.gate.status !== "rejected") {
1372
+ errors.push({
1373
+ path: `${path}.gate.status`,
1374
+ message: 'gate.status must be "accepted" or "rejected".'
1375
+ });
1376
+ }
1377
+ }
1378
+ validateStringArray3(acceptance.failedChecks, `${path}.failedChecks`, errors);
1379
+ validateReceiptSummary2(acceptance.receipts, `${path}.receipts`, errors);
1380
+ validateStringArray3(
1381
+ acceptance.timelineEventTypes,
1382
+ `${path}.timelineEventTypes`,
1383
+ errors
1384
+ );
1385
+ validateStringArray3(
1386
+ acceptance.telemetryEventTypes,
1387
+ `${path}.telemetryEventTypes`,
1388
+ errors
1389
+ );
1390
+ }
1391
+ function validatePricingDeclaration3(pricing, path, errors) {
1392
+ if (!isRecord4(pricing)) {
1393
+ errors.push({
1394
+ path,
1395
+ message: "pricing must be an object."
1396
+ });
1397
+ return;
1398
+ }
1399
+ if (isPricingObject(pricing)) {
1400
+ validatePricingObject(pricing, path, errors);
1401
+ return;
1402
+ }
1403
+ for (const [capability, capabilityPricing] of Object.entries(pricing)) {
1404
+ validateNonEmptyString4(capability, `${path}.${capability}`, errors);
1405
+ validatePricingObject(capabilityPricing, `${path}.${capability}`, errors);
1406
+ }
1407
+ }
1408
+ function validatePricingObject(pricing, path, errors) {
1409
+ if (!isRecord4(pricing)) {
1410
+ errors.push({
1411
+ path,
1412
+ message: "pricing entry must be an object."
1413
+ });
1414
+ return;
1415
+ }
1416
+ validateNonEmptyString4(pricing.unit, `${path}.unit`, errors);
1417
+ if (typeof pricing.amountUsd !== "number" || !Number.isFinite(pricing.amountUsd) || pricing.amountUsd < 0) {
1418
+ errors.push({
1419
+ path: `${path}.amountUsd`,
1420
+ message: "amountUsd must be a non-negative finite number."
1421
+ });
1422
+ }
1423
+ if (pricing.currency !== void 0 && typeof pricing.currency !== "string") {
1424
+ errors.push({
1425
+ path: `${path}.currency`,
1426
+ message: "currency must be a string when provided."
1427
+ });
1428
+ }
1429
+ }
1430
+ function validateReceiptSupport(receipts, path, errors) {
1431
+ if (!isRecord4(receipts)) {
1432
+ errors.push({
1433
+ path,
1434
+ message: "receipts must be an object."
1435
+ });
1436
+ return;
1437
+ }
1438
+ for (const key of ["payment", "delivery", "responseEvidence"]) {
1439
+ if (typeof receipts[key] !== "boolean") {
1440
+ errors.push({
1441
+ path: `${path}.${key}`,
1442
+ message: `${key} must be a boolean.`
1443
+ });
1444
+ }
1445
+ }
1446
+ }
1447
+ function validateReceiptSummary2(receipts, path, errors) {
1448
+ if (!isRecord4(receipts)) {
1449
+ errors.push({
1450
+ path,
1451
+ message: "receipts must be an object."
1452
+ });
1453
+ return;
1454
+ }
1455
+ if (typeof receipts.total !== "number" || !Number.isFinite(receipts.total)) {
1456
+ errors.push({
1457
+ path: `${path}.total`,
1458
+ message: "total must be a finite number."
1459
+ });
1460
+ }
1461
+ validateStringArray3(receipts.types, `${path}.types`, errors);
1462
+ validateStringArray3(receipts.providers, `${path}.providers`, errors);
1463
+ for (const key of [
1464
+ "hasPaymentProof",
1465
+ "hasSettlementConfirmation",
1466
+ "hasProviderDelivery"
1467
+ ]) {
1468
+ if (typeof receipts[key] !== "boolean") {
1469
+ errors.push({
1470
+ path: `${path}.${key}`,
1471
+ message: `${key} must be a boolean.`
1472
+ });
1473
+ }
1474
+ }
1475
+ }
1476
+ function isPricingObject(value) {
1477
+ return "unit" in value || "amountUsd" in value || "currency" in value;
1478
+ }
1479
+ function validateStringArray3(value, path, errors) {
1480
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string" || entry.length === 0)) {
1481
+ errors.push({
1482
+ path,
1483
+ message: `${path} must be an array of non-empty strings.`
1484
+ });
1485
+ }
1486
+ }
1487
+ function validateNonEmptyString4(value, path, errors) {
1488
+ if (typeof value !== "string" || value.length === 0) {
1489
+ errors.push({
1490
+ path,
1491
+ message: `${path} must be a non-empty string.`
1492
+ });
1493
+ }
1494
+ }
1495
+ function validateIsoTimestamp2(value, path, errors) {
1496
+ if (typeof value !== "string" || Number.isNaN(Date.parse(value))) {
1497
+ errors.push({
1498
+ path,
1499
+ message: `${path} must be an ISO timestamp string.`
1500
+ });
1501
+ }
1502
+ }
1503
+ function formatValidationErrors(errors) {
1504
+ return errors.map((error) => `${error.path}: ${error.message}`).join("; ");
1505
+ }
1506
+ function isRecord4(value) {
1507
+ return !!value && typeof value === "object" && !Array.isArray(value);
1508
+ }
1509
+
1510
+ // src/providerCatalogPolicy.ts
1511
+ function evaluateProviderCatalogPolicy(options) {
1512
+ assertEvaluationOptions(options);
1513
+ const catalogPolicy = resolveCatalogPolicy(options.catalogPolicy);
1514
+ const adapterInventory = options.adapterInventory;
1515
+ const adapterIds = new Set(adapterInventory.map((adapter) => adapter.id));
1516
+ const catalogSummary = summarizeProviderCatalog(options.catalog);
1517
+ const checks = [
1518
+ {
1519
+ name: "catalog_valid",
1520
+ status: "passed",
1521
+ message: "Provider catalog is valid."
1522
+ },
1523
+ {
1524
+ name: "adapter_inventory_valid",
1525
+ status: "passed",
1526
+ message: "Adapter inventory is valid."
1527
+ }
1528
+ ];
1529
+ const queryResults = options.queries.map(
1530
+ (query) => evaluateQuery({
1531
+ catalog: options.catalog,
1532
+ catalogPolicy,
1533
+ query,
1534
+ adapterInventory,
1535
+ adapterIds,
1536
+ checks
1537
+ })
1538
+ );
1539
+ const failedChecks = checks.filter((check) => check.status === "failed").length;
1540
+ const routeableMatches = queryResults.reduce(
1541
+ (total, result) => total + result.routeableMatches.length,
1542
+ 0
1543
+ );
1544
+ const missingAdapterMatches = queryResults.reduce(
1545
+ (total, result) => total + result.missingAdapterMatches.length,
1546
+ 0
1547
+ );
1548
+ return {
1549
+ status: failedChecks === 0 ? "passed" : "failed",
1550
+ checks,
1551
+ summary: {
1552
+ ...catalogSummary,
1553
+ totalQueries: options.queries.length,
1554
+ passedChecks: checks.length - failedChecks,
1555
+ failedChecks,
1556
+ routeableMatches,
1557
+ missingAdapterMatches
1558
+ },
1559
+ queryResults
1560
+ };
1561
+ }
1562
+ function createProviderCatalogPolicyReport(result, options = {}) {
1563
+ const report = {
1564
+ title: options.title ?? "ProbeMesh Catalog Policy Report",
1565
+ generatedAt: options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1566
+ status: result.status,
1567
+ summary: cloneSerializable3(result.summary),
1568
+ checks: cloneSerializable3(result.checks),
1569
+ queryResults: cloneSerializable3(result.queryResults)
1570
+ };
1571
+ return sanitizeReport(report, options.forbiddenOutputValues ?? []);
1572
+ }
1573
+ function formatProviderCatalogPolicyReport(report, options = {}) {
1574
+ if ((options.format ?? "json") === "markdown") {
1575
+ return formatMarkdownReport(report);
1576
+ }
1577
+ return JSON.stringify(report, null, 2);
1578
+ }
1579
+ function assertEvaluationOptions(options) {
1580
+ if (!isRecord5(options)) {
1581
+ throw new ProbeMeshError({
1582
+ code: "invalid_request",
1583
+ message: "Provider catalog policy evaluation options must be an object."
1584
+ });
1585
+ }
1586
+ assertProviderCatalog(options.catalog);
1587
+ assertAdapterInventory(options.adapterInventory);
1588
+ if (!Array.isArray(options.queries) || options.queries.length === 0) {
1589
+ throw new ProbeMeshError({
1590
+ code: "invalid_request",
1591
+ message: "Provider catalog policy evaluation requires at least one query."
1592
+ });
1593
+ }
1594
+ options.queries.forEach(
1595
+ (query, index) => assertExpectedQuery(query, `queries.${index}`)
1596
+ );
1597
+ assertCatalogPolicyOptions(options.catalogPolicy, "catalogPolicy");
1598
+ }
1599
+ function assertAdapterInventory(adapterInventory) {
1600
+ if (!Array.isArray(adapterInventory)) {
1601
+ throw new ProbeMeshError({
1602
+ code: "invalid_request",
1603
+ message: "Provider catalog policy adapterInventory must be an array."
1604
+ });
1605
+ }
1606
+ const seen = /* @__PURE__ */ new Set();
1607
+ adapterInventory.forEach((adapter, index) => {
1608
+ const path = `adapterInventory.${index}`;
1609
+ if (!isRecord5(adapter)) {
1610
+ throw new ProbeMeshError({
1611
+ code: "invalid_request",
1612
+ message: `${path} must be an object.`
1613
+ });
1614
+ }
1615
+ if (typeof adapter.id !== "string" || adapter.id.length === 0) {
1616
+ throw new ProbeMeshError({
1617
+ code: "invalid_request",
1618
+ message: `${path}.id must be a non-empty string.`
1619
+ });
1620
+ }
1621
+ if (seen.has(adapter.id)) {
1622
+ throw new ProbeMeshError({
1623
+ code: "invalid_request",
1624
+ message: `${path}.id duplicates adapter "${adapter.id}".`
1625
+ });
1626
+ }
1627
+ seen.add(adapter.id);
1628
+ if (!Array.isArray(adapter.capabilities) || adapter.capabilities.some(
1629
+ (capability) => typeof capability !== "string" || capability.length === 0
1630
+ )) {
1631
+ throw new ProbeMeshError({
1632
+ code: "invalid_request",
1633
+ message: `${path}.capabilities must be an array of non-empty strings.`
1634
+ });
1635
+ }
1636
+ if (adapter.paymentOptions !== void 0 && (!Array.isArray(adapter.paymentOptions) || adapter.paymentOptions.some(
1637
+ (option) => !isRecord5(option) || typeof option.id !== "string" || option.id.length === 0 || typeof option.protocolMode !== "string" || option.protocolMode.length === 0
1638
+ ))) {
1639
+ throw new ProbeMeshError({
1640
+ code: "invalid_request",
1641
+ message: `${path}.paymentOptions must be an array of payment option objects when provided.`
1642
+ });
1643
+ }
1644
+ });
1645
+ }
1646
+ function assertExpectedQuery(query, path) {
1647
+ if (!isRecord5(query)) {
1648
+ throw new ProbeMeshError({
1649
+ code: "invalid_request",
1650
+ message: `${path} must be an object.`
1651
+ });
1652
+ }
1653
+ if (typeof query.capability !== "string" || query.capability.length === 0) {
1654
+ throw new ProbeMeshError({
1655
+ code: "invalid_request",
1656
+ message: `${path}.capability must be a non-empty string.`
1657
+ });
1658
+ }
1659
+ const minMatches = query.minMatches;
1660
+ if (minMatches !== void 0 && (typeof minMatches !== "number" || !Number.isInteger(minMatches) || minMatches < 0)) {
1661
+ throw new ProbeMeshError({
1662
+ code: "invalid_request",
1663
+ message: `${path}.minMatches must be a non-negative integer.`
1664
+ });
1665
+ }
1666
+ assertCatalogPolicyOptions(query, path);
1667
+ }
1668
+ function assertCatalogPolicyOptions(policy, path) {
1669
+ if (policy === void 0) {
1670
+ return;
1671
+ }
1672
+ if (!isRecord5(policy)) {
1673
+ throw new ProbeMeshError({
1674
+ code: "invalid_request",
1675
+ message: `${path} must be an object when provided.`
1676
+ });
1677
+ }
1678
+ if (policy.status !== void 0 && policy.status !== "ready" && policy.status !== "rejected") {
1679
+ throw new ProbeMeshError({
1680
+ code: "invalid_request",
1681
+ message: `${path}.status must be "ready" or "rejected".`
1682
+ });
1683
+ }
1684
+ if (policy.protocolMode !== void 0 && (typeof policy.protocolMode !== "string" || policy.protocolMode.length === 0)) {
1685
+ throw new ProbeMeshError({
1686
+ code: "invalid_request",
1687
+ message: `${path}.protocolMode must be a non-empty string.`
1688
+ });
1689
+ }
1690
+ validatePaymentPreferences(policy.paymentPreferences, `${path}.paymentPreferences`);
1691
+ validatePaymentStrategy(policy.paymentStrategy, `${path}.paymentStrategy`);
1692
+ if (policy.maxCostUsd !== void 0 && (typeof policy.maxCostUsd !== "number" || !Number.isFinite(policy.maxCostUsd) || policy.maxCostUsd < 0)) {
1693
+ throw new ProbeMeshError({
1694
+ code: "invalid_request",
1695
+ message: `${path}.maxCostUsd must be a non-negative finite number.`
1696
+ });
1697
+ }
1698
+ validateStringArray4(policy.providerIds, `${path}.providerIds`);
1699
+ validateStringArray4(policy.blockedProviderIds, `${path}.blockedProviderIds`);
1700
+ if (policy.requiredReceiptTypes !== void 0 && (!Array.isArray(policy.requiredReceiptTypes) || policy.requiredReceiptTypes.some((receiptType) => !isReceiptType3(receiptType)))) {
1701
+ throw new ProbeMeshError({
1702
+ code: "invalid_request",
1703
+ message: `${path}.requiredReceiptTypes must contain known receipt types.`
1704
+ });
1705
+ }
1706
+ }
1707
+ function evaluateQuery(options) {
1708
+ const resolvedQuery = resolveQuery(options.catalogPolicy, options.query);
1709
+ const matches = findProviderCatalogMatches(options.catalog, resolvedQuery);
1710
+ const routeableMatches = matches.filter(
1711
+ (match) => hasRouteableAdapter(match, options.adapterInventory)
1712
+ );
1713
+ const missingAdapterMatches = matches.filter((match) => !options.adapterIds.has(match.providerId)).map((match) => ({
1714
+ providerId: match.providerId,
1715
+ capability: match.capability,
1716
+ catalogArtifactId: match.entry.artifactId,
1717
+ catalogStatus: match.entry.status,
1718
+ reasons: [
1719
+ ...match.reasons,
1720
+ `missing_adapter:${match.providerId}`
1721
+ ]
1722
+ }));
1723
+ const skippedProviders = createSkippedProviders(
1724
+ options.catalog.entries,
1725
+ options.query.capability,
1726
+ matches,
1727
+ resolvedQuery,
1728
+ options.adapterInventory
1729
+ );
1730
+ const minMatches = options.query.minMatches ?? 1;
1731
+ pushCheck(options.checks, {
1732
+ name: "ready_matches",
1733
+ status: matches.length >= minMatches ? "passed" : "failed",
1734
+ capability: options.query.capability,
1735
+ message: matches.length >= minMatches ? `Found ${matches.length} catalog match(es) for "${options.query.capability}".` : `Expected at least ${minMatches} catalog match(es) for "${options.query.capability}", found ${matches.length}.`
1736
+ });
1737
+ pushCheck(options.checks, {
1738
+ name: "routeable_matches",
1739
+ status: routeableMatches.length >= minMatches ? "passed" : "failed",
1740
+ capability: options.query.capability,
1741
+ message: routeableMatches.length >= minMatches ? `Found ${routeableMatches.length} routeable adapter-backed match(es) for "${options.query.capability}".` : `Expected at least ${minMatches} routeable adapter-backed match(es) for "${options.query.capability}", found ${routeableMatches.length}.`
1742
+ });
1743
+ for (const missingMatch of missingAdapterMatches) {
1744
+ pushCheck(options.checks, {
1745
+ name: "missing_adapter",
1746
+ status: routeableMatches.length >= minMatches ? "passed" : "failed",
1747
+ capability: missingMatch.capability,
1748
+ providerId: missingMatch.providerId,
1749
+ message: routeableMatches.length >= minMatches ? `Catalog provider "${missingMatch.providerId}" matches policy but is missing from adapter inventory; minimum routeable matches are still satisfied.` : `Catalog provider "${missingMatch.providerId}" matches policy but is missing from adapter inventory.`
1750
+ });
1751
+ }
1752
+ return {
1753
+ query: options.query,
1754
+ matches,
1755
+ routeableMatches,
1756
+ missingAdapterMatches,
1757
+ skippedProviders
1758
+ };
1759
+ }
1760
+ function resolveCatalogPolicy(policy) {
1761
+ return {
1762
+ enabled: policy?.enabled ?? true,
1763
+ status: policy?.status ?? "ready",
1764
+ protocolMode: policy?.protocolMode,
1765
+ paymentPreferences: policy?.paymentPreferences,
1766
+ paymentStrategy: policy?.paymentStrategy,
1767
+ maxCostUsd: policy?.maxCostUsd,
1768
+ requiredReceiptTypes: policy?.requiredReceiptTypes,
1769
+ providerIds: policy?.providerIds,
1770
+ blockedProviderIds: policy?.blockedProviderIds
1771
+ };
1772
+ }
1773
+ function resolveQuery(policy, query) {
1774
+ const queryPolicy = resolveCatalogPolicy(query);
1775
+ const activePolicy = policy.enabled ? policy : resolveCatalogPolicy(void 0);
1776
+ return {
1777
+ capability: query.capability,
1778
+ protocolMode: queryPolicy.protocolMode ?? activePolicy.protocolMode,
1779
+ paymentPreferences: mergePaymentPreferences(
1780
+ activePolicy.paymentPreferences,
1781
+ queryPolicy.paymentPreferences
1782
+ ),
1783
+ paymentStrategy: queryPolicy.paymentStrategy ?? activePolicy.paymentStrategy,
1784
+ maxCostUsd: strictestMaxCostUsd(
1785
+ queryPolicy.maxCostUsd,
1786
+ activePolicy.maxCostUsd
1787
+ ),
1788
+ requiredReceiptTypes: unionProviderIds(
1789
+ queryPolicy.requiredReceiptTypes,
1790
+ activePolicy.requiredReceiptTypes
1791
+ ),
1792
+ providerIds: intersectProviderIds(
1793
+ queryPolicy.providerIds,
1794
+ activePolicy.providerIds
1795
+ ),
1796
+ blockedProviderIds: unionProviderIds(
1797
+ queryPolicy.blockedProviderIds,
1798
+ activePolicy.blockedProviderIds
1799
+ ),
1800
+ status: queryPolicy.status ?? activePolicy.status
1801
+ };
1802
+ }
1803
+ function hasRouteableAdapter(match, adapterInventory) {
1804
+ return adapterInventory.some(
1805
+ (adapter) => adapter.id === match.providerId && adapter.capabilities.includes(match.capability) && (adapter.paymentOptions === void 0 || match.paymentSelection === void 0 || adapter.paymentOptions.some(
1806
+ (option) => option.id === match.paymentSelection?.optionId
1807
+ ))
1808
+ );
1809
+ }
1810
+ function createSkippedProviders(entries, capability, matches, query, adapterInventory) {
1811
+ const matchIds = new Set(matches.map((match) => match.providerId));
1812
+ const adapterIds = new Set(adapterInventory.map((adapter) => adapter.id));
1813
+ const catalogProviderIds = new Set(entries.map((entry) => entry.provider.id));
1814
+ const skippedCatalogEntries = entries.filter((entry) => entry.provider.capabilities.includes(capability)).filter((entry) => !matchIds.has(entry.provider.id)).map((entry) => ({
1815
+ providerId: entry.provider.id,
1816
+ capability,
1817
+ catalogArtifactId: entry.artifactId,
1818
+ catalogStatus: entry.status,
1819
+ reasons: explainSkippedEntry(entry, query, adapterIds)
1820
+ }));
1821
+ const inventoryOnlyAdapters = adapterInventory.filter((adapter) => adapter.capabilities.includes(capability)).filter((adapter) => !catalogProviderIds.has(adapter.id)).map((adapter) => ({
1822
+ providerId: adapter.id,
1823
+ capability,
1824
+ inventoryOnly: true,
1825
+ reasons: ["adapter:not_in_catalog"]
1826
+ }));
1827
+ return [...skippedCatalogEntries, ...inventoryOnlyAdapters];
1828
+ }
1829
+ function explainSkippedEntry(entry, query, adapterIds) {
1830
+ const reasons = [];
1831
+ const providerId = entry.provider.id;
1832
+ if (entry.status !== (query.status ?? "ready")) {
1833
+ reasons.push(`status:${entry.status}`);
1834
+ }
1835
+ if (query.protocolMode && entry.provider.protocolMode !== query.protocolMode) {
1836
+ reasons.push(`protocol:${entry.provider.protocolMode}`);
1837
+ }
1838
+ if (query.paymentPreferences) {
1839
+ const paymentMatch = findProviderCatalogMatches(
1840
+ {
1841
+ schemaVersion: "probemesh.provider-catalog.index.v1",
1842
+ generatedAt: "1970-01-01T00:00:00.000Z",
1843
+ entries: [entry]
1844
+ },
1845
+ {
1846
+ capability: query.capability,
1847
+ paymentPreferences: query.paymentPreferences,
1848
+ status: entry.status
1849
+ }
1850
+ );
1851
+ if (paymentMatch.length === 0) {
1852
+ reasons.push("payment_preferences:not_matched");
1853
+ }
1854
+ }
1855
+ if (query.providerIds && !query.providerIds.includes(providerId)) {
1856
+ reasons.push("provider:not_allowed");
1857
+ }
1858
+ if (query.blockedProviderIds?.includes(providerId)) {
1859
+ reasons.push("provider:blocked");
1860
+ }
1861
+ const cost = resolveCatalogCost2(entry, query.capability);
1862
+ if (query.maxCostUsd !== void 0 && (cost === void 0 || cost.amountUsd > query.maxCostUsd)) {
1863
+ reasons.push(`cost:${cost?.amountUsd ?? "unknown"}>${query.maxCostUsd}`);
1864
+ }
1865
+ const receiptTypes = new Set(entry.acceptance.receipts.types);
1866
+ for (const receiptType of query.requiredReceiptTypes ?? []) {
1867
+ if (!receiptTypes.has(receiptType)) {
1868
+ reasons.push(`missing_receipt:${receiptType}`);
1869
+ }
1870
+ }
1871
+ if (!adapterIds.has(providerId)) {
1872
+ reasons.push(`missing_adapter:${providerId}`);
1873
+ }
1874
+ return reasons.length > 0 ? reasons : ["not_matched"];
1875
+ }
1876
+ function resolveCatalogCost2(entry, capability) {
1877
+ const pricing = entry.provider.pricing;
1878
+ if (isPricing2(pricing)) {
1879
+ return pricing;
1880
+ }
1881
+ const capabilityPricing = pricing[capability];
1882
+ return isPricing2(capabilityPricing) ? capabilityPricing : void 0;
1883
+ }
1884
+ function pushCheck(checks, check) {
1885
+ checks.push(check);
1886
+ }
1887
+ function strictestMaxCostUsd(first, second) {
1888
+ if (first === void 0) {
1889
+ return second;
1890
+ }
1891
+ if (second === void 0) {
1892
+ return first;
1893
+ }
1894
+ return Math.min(first, second);
1895
+ }
1896
+ function intersectProviderIds(first, second) {
1897
+ if (!first) {
1898
+ return second;
1899
+ }
1900
+ if (!second) {
1901
+ return first;
1902
+ }
1903
+ return first.filter((providerId) => second.includes(providerId));
1904
+ }
1905
+ function unionProviderIds(first, second) {
1906
+ return [.../* @__PURE__ */ new Set([...first ?? [], ...second ?? []])];
1907
+ }
1908
+ function formatMarkdownReport(report) {
1909
+ const lines = [
1910
+ `# ${report.title}`,
1911
+ "",
1912
+ `Status: ${report.status}`,
1913
+ `Generated: ${report.generatedAt}`,
1914
+ "",
1915
+ "## Summary",
1916
+ `- Total entries: ${report.summary.totalEntries}`,
1917
+ `- Ready entries: ${report.summary.readyEntries}`,
1918
+ `- Rejected entries: ${report.summary.rejectedEntries}`,
1919
+ `- Queries: ${report.summary.totalQueries}`,
1920
+ `- Routeable matches: ${report.summary.routeableMatches}`,
1921
+ `- Missing adapter matches: ${report.summary.missingAdapterMatches}`,
1922
+ `- Failed checks: ${report.summary.failedChecks}`,
1923
+ "",
1924
+ "## Checks",
1925
+ ...report.checks.map(
1926
+ (check) => `- ${check.status === "passed" ? "[x]" : "[ ]"} ${check.name}: ${check.message}`
1927
+ ),
1928
+ "",
1929
+ "## Queries",
1930
+ ...report.queryResults.flatMap((result) => [
1931
+ `### ${result.query.capability}`,
1932
+ `- Matches: ${formatProviderIds(result.matches.map((match) => match.providerId))}`,
1933
+ `- Payment options: ${formatMatchPaymentOptions(result.routeableMatches)}`,
1934
+ `- Routeable: ${formatProviderIds(
1935
+ result.routeableMatches.map((match) => match.providerId)
1936
+ )}`,
1937
+ `- Missing adapters: ${formatProviderIds(
1938
+ result.missingAdapterMatches.map((match) => match.providerId)
1939
+ )}`,
1940
+ `- Skipped: ${formatProviderIds(
1941
+ result.skippedProviders.map((provider) => provider.providerId)
1942
+ )}`,
1943
+ ""
1944
+ ])
1945
+ ];
1946
+ return `${lines.join("\n")}
1947
+ `;
1948
+ }
1949
+ function formatProviderIds(providerIds) {
1950
+ return providerIds.length > 0 ? providerIds.join(", ") : "none";
1951
+ }
1952
+ function formatMatchPaymentOptions(matches) {
1953
+ if (matches.length === 0) {
1954
+ return "none";
1955
+ }
1956
+ return matches.map((match) => {
1957
+ const selection = match.paymentSelection;
1958
+ if (!selection) {
1959
+ return `${match.providerId}: none`;
1960
+ }
1961
+ const score = selection.score !== void 0 ? ` score=${selection.score}` : "";
1962
+ const reasons = selection.scoreReasons && selection.scoreReasons.length > 0 ? ` (${selection.scoreReasons.join("; ")})` : "";
1963
+ return `${match.providerId}: ${selection.optionId}/${selection.protocolMode}${score}${reasons}`;
1964
+ }).join(", ");
1965
+ }
1966
+ function sanitizeReport(value, forbiddenOutputValues) {
1967
+ return replaceForbiddenValues(
1968
+ cloneSerializable3(value),
1969
+ forbiddenOutputValues.filter((entry) => entry.length > 0)
1970
+ );
1971
+ }
1972
+ function replaceForbiddenValues(value, forbiddenValues) {
1973
+ if (typeof value === "string") {
1974
+ return forbiddenValues.reduce(
1975
+ (current, forbidden) => current.split(forbidden).join("[REDACTED]"),
1976
+ value
1977
+ );
1978
+ }
1979
+ if (value === null || typeof value !== "object" || value instanceof Date) {
1980
+ return value;
1981
+ }
1982
+ if (Array.isArray(value)) {
1983
+ return value.map((entry) => replaceForbiddenValues(entry, forbiddenValues));
1984
+ }
1985
+ return Object.fromEntries(
1986
+ Object.entries(value).map(([key, entry]) => [
1987
+ key,
1988
+ replaceForbiddenValues(entry, forbiddenValues)
1989
+ ])
1990
+ );
1991
+ }
1992
+ function cloneSerializable3(value) {
1993
+ if (value === void 0) {
1994
+ return value;
1995
+ }
1996
+ return JSON.parse(JSON.stringify(value));
1997
+ }
1998
+ function validateStringArray4(value, path) {
1999
+ if (value !== void 0 && (!Array.isArray(value) || value.some((entry) => typeof entry !== "string" || entry.length === 0))) {
2000
+ throw new ProbeMeshError({
2001
+ code: "invalid_request",
2002
+ message: `${path} must be an array of non-empty strings.`
2003
+ });
2004
+ }
2005
+ }
2006
+ function isPricing2(value) {
2007
+ return isRecord5(value) && typeof value.unit === "string" && typeof value.amountUsd === "number" && Number.isFinite(value.amountUsd) && value.amountUsd >= 0 && (value.currency === void 0 || typeof value.currency === "string");
2008
+ }
2009
+ function isReceiptType3(value) {
2010
+ return value === "payment_proof" || value === "settlement_confirmation" || value === "provider_delivery" || value === "provider_response_evidence" || value === "authorization_decision";
2011
+ }
2012
+ function isRecord5(value) {
2013
+ return !!value && typeof value === "object" && !Array.isArray(value);
2014
+ }
2015
+
2016
+ // src/providerCatalogPolicyBundle.ts
2017
+ var import_node_crypto = require("crypto");
2018
+ var PROVIDER_CATALOG_POLICY_BUNDLE_SCHEMA_VERSION = "probemesh.catalog-policy-bundle.v1";
2019
+ function createProviderCatalogPolicyBundle(options) {
2020
+ assertBundleOptions(options);
2021
+ const evaluation = options.evaluation ?? evaluateProviderCatalogPolicy({
2022
+ catalog: options.catalog,
2023
+ catalogPolicy: options.catalogPolicy,
2024
+ adapterInventory: options.adapterInventory,
2025
+ queries: options.queries
2026
+ });
2027
+ const hashesWithoutBundle = {
2028
+ catalog: hashStableJson(options.catalog),
2029
+ catalogPolicy: hashStableJson(options.catalogPolicy ?? null),
2030
+ adapterInventory: hashStableJson(options.adapterInventory),
2031
+ queries: hashStableJson(options.queries),
2032
+ evaluation: hashStableJson(evaluation),
2033
+ bundle: ""
2034
+ };
2035
+ const bundleBase = {
2036
+ schemaVersion: PROVIDER_CATALOG_POLICY_BUNDLE_SCHEMA_VERSION,
2037
+ bundleId: options.bundleId ?? `catalog_policy_bundle_${hashStableJson({
2038
+ catalog: hashesWithoutBundle.catalog,
2039
+ catalogPolicy: hashesWithoutBundle.catalogPolicy,
2040
+ adapterInventory: hashesWithoutBundle.adapterInventory,
2041
+ queries: hashesWithoutBundle.queries
2042
+ }).slice(0, 16)}`,
2043
+ generatedAt: options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
2044
+ status: evaluation.status,
2045
+ hashAlgorithm: "sha256",
2046
+ hashes: hashesWithoutBundle,
2047
+ catalogSummary: summarizeProviderCatalog(options.catalog),
2048
+ evaluationSummary: cloneSerializable4(evaluation.summary),
2049
+ queryCoverage: evaluation.queryResults.map(createQueryCoverage),
2050
+ checks: cloneSerializable4(evaluation.checks),
2051
+ failedChecks: evaluation.checks.filter((check) => check.status === "failed").map((check) => check.name),
2052
+ queries: cloneSerializable4(options.queries)
2053
+ };
2054
+ const bundle = {
2055
+ ...bundleBase,
2056
+ hashes: {
2057
+ ...hashesWithoutBundle,
2058
+ bundle: hashBundleContent(bundleBase)
2059
+ }
2060
+ };
2061
+ const sanitizedBundle = sanitizeBundleOutput(
2062
+ bundle,
2063
+ options.forbiddenOutputValues ?? []
2064
+ );
2065
+ assertProviderCatalogPolicyBundle(sanitizedBundle);
2066
+ return sanitizedBundle;
2067
+ }
2068
+ function validateProviderCatalogPolicyBundle(bundle) {
2069
+ const errors = [];
2070
+ if (!isRecord6(bundle)) {
2071
+ return {
2072
+ valid: false,
2073
+ errors: [
2074
+ {
2075
+ path: "$",
2076
+ message: "Provider catalog policy bundle must be an object."
2077
+ }
2078
+ ]
2079
+ };
2080
+ }
2081
+ if (bundle.schemaVersion !== PROVIDER_CATALOG_POLICY_BUNDLE_SCHEMA_VERSION) {
2082
+ errors.push({
2083
+ path: "schemaVersion",
2084
+ message: `schemaVersion must be "${PROVIDER_CATALOG_POLICY_BUNDLE_SCHEMA_VERSION}".`
2085
+ });
2086
+ }
2087
+ validateNonEmptyString5(bundle.bundleId, "bundleId", errors);
2088
+ validateIsoTimestamp3(bundle.generatedAt, "generatedAt", errors);
2089
+ if (bundle.status !== "passed" && bundle.status !== "failed") {
2090
+ errors.push({
2091
+ path: "status",
2092
+ message: 'status must be "passed" or "failed".'
2093
+ });
2094
+ }
2095
+ if (bundle.hashAlgorithm !== "sha256") {
2096
+ errors.push({
2097
+ path: "hashAlgorithm",
2098
+ message: 'hashAlgorithm must be "sha256".'
2099
+ });
2100
+ }
2101
+ validateHashes(bundle.hashes, errors);
2102
+ validateObject(bundle.catalogSummary, "catalogSummary", errors);
2103
+ validateObject(bundle.evaluationSummary, "evaluationSummary", errors);
2104
+ validateQueryCoverage(bundle.queryCoverage, errors);
2105
+ validateCheckArray(bundle.checks, "checks", errors);
2106
+ validateStringArray5(bundle.failedChecks, "failedChecks", errors);
2107
+ if (!Array.isArray(bundle.queries) || bundle.queries.length === 0) {
2108
+ errors.push({
2109
+ path: "queries",
2110
+ message: "queries must be a non-empty array."
2111
+ });
2112
+ }
2113
+ if (errors.length === 0) {
2114
+ const expectedBundleHash = hashBundleContent(
2115
+ bundle
2116
+ );
2117
+ if (bundle.hashes.bundle !== expectedBundleHash) {
2118
+ errors.push({
2119
+ path: "hashes.bundle",
2120
+ message: "bundle hash does not match bundle content."
2121
+ });
2122
+ }
2123
+ }
2124
+ return {
2125
+ valid: errors.length === 0,
2126
+ errors
2127
+ };
2128
+ }
2129
+ function assertProviderCatalogPolicyBundle(bundle) {
2130
+ const result = validateProviderCatalogPolicyBundle(bundle);
2131
+ if (result.valid) {
2132
+ return;
2133
+ }
2134
+ throw new ProbeMeshError({
2135
+ code: "invalid_request",
2136
+ message: `Invalid provider catalog policy bundle: ${formatValidationErrors2(
2137
+ result.errors
2138
+ )}`
2139
+ });
2140
+ }
2141
+ function formatProviderCatalogPolicyBundle(bundle, options = {}) {
2142
+ assertProviderCatalogPolicyBundle(bundle);
2143
+ if ((options.format ?? "json") === "markdown") {
2144
+ return formatBundleMarkdown(bundle);
2145
+ }
2146
+ return JSON.stringify(bundle, null, 2);
2147
+ }
2148
+ function assertBundleOptions(options) {
2149
+ if (!isRecord6(options)) {
2150
+ throw new ProbeMeshError({
2151
+ code: "invalid_request",
2152
+ message: "Provider catalog policy bundle options must be an object."
2153
+ });
2154
+ }
2155
+ assertProviderCatalog(options.catalog);
2156
+ if (!Array.isArray(options.adapterInventory)) {
2157
+ throw new ProbeMeshError({
2158
+ code: "invalid_request",
2159
+ message: "Provider catalog policy bundle adapterInventory must be an array."
2160
+ });
2161
+ }
2162
+ if (!Array.isArray(options.queries) || options.queries.length === 0) {
2163
+ throw new ProbeMeshError({
2164
+ code: "invalid_request",
2165
+ message: "Provider catalog policy bundle requires at least one query."
2166
+ });
2167
+ }
2168
+ if (options.bundleId !== void 0 && (typeof options.bundleId !== "string" || options.bundleId.length === 0)) {
2169
+ throw new ProbeMeshError({
2170
+ code: "invalid_request",
2171
+ message: "Provider catalog policy bundleId must be a non-empty string."
2172
+ });
2173
+ }
2174
+ if (options.generatedAt !== void 0 && (typeof options.generatedAt !== "string" || Number.isNaN(Date.parse(options.generatedAt)))) {
2175
+ throw new ProbeMeshError({
2176
+ code: "invalid_request",
2177
+ message: "Provider catalog policy bundle generatedAt must be an ISO timestamp."
2178
+ });
2179
+ }
2180
+ }
2181
+ function createQueryCoverage(result) {
2182
+ return {
2183
+ capability: result.query.capability,
2184
+ minMatches: result.query.minMatches ?? 1,
2185
+ matches: result.matches.length,
2186
+ routeableMatches: result.routeableMatches.length,
2187
+ missingAdapterMatches: result.missingAdapterMatches.length,
2188
+ providerIds: result.routeableMatches.map((match) => match.providerId)
2189
+ };
2190
+ }
2191
+ function hashStableJson(value) {
2192
+ return (0, import_node_crypto.createHash)("sha256").update(stableStringify(value)).digest("hex");
2193
+ }
2194
+ function hashBundleContent(bundle) {
2195
+ return hashStableJson({
2196
+ ...bundle,
2197
+ hashes: {
2198
+ ...bundle.hashes,
2199
+ bundle: ""
2200
+ }
2201
+ });
2202
+ }
2203
+ function stableStringify(value) {
2204
+ return JSON.stringify(sortSerializable(value));
2205
+ }
2206
+ function sortSerializable(value) {
2207
+ if (Array.isArray(value)) {
2208
+ return value.map(sortSerializable);
2209
+ }
2210
+ if (!isRecord6(value)) {
2211
+ return value;
2212
+ }
2213
+ return Object.fromEntries(
2214
+ Object.keys(value).sort().filter((key) => value[key] !== void 0).map((key) => [key, sortSerializable(value[key])])
2215
+ );
2216
+ }
2217
+ function formatBundleMarkdown(bundle) {
2218
+ const lines = [
2219
+ `# Provider Catalog Policy Bundle ${bundle.bundleId}`,
2220
+ "",
2221
+ `Status: ${bundle.status}`,
2222
+ `Generated: ${bundle.generatedAt}`,
2223
+ `Bundle hash: ${bundle.hashes.bundle}`,
2224
+ "",
2225
+ "## Summary",
2226
+ `- Catalog entries: ${bundle.catalogSummary.totalEntries}`,
2227
+ `- Ready entries: ${bundle.catalogSummary.readyEntries}`,
2228
+ `- Queries: ${bundle.queryCoverage.length}`,
2229
+ `- Routeable matches: ${bundle.evaluationSummary.routeableMatches}`,
2230
+ `- Failed checks: ${bundle.evaluationSummary.failedChecks}`,
2231
+ "",
2232
+ "## Query Coverage",
2233
+ ...bundle.queryCoverage.map(
2234
+ (coverage) => `- ${coverage.capability}: ${coverage.routeableMatches}/${coverage.minMatches} routeable (${formatInlineList(
2235
+ coverage.providerIds
2236
+ )})`
2237
+ ),
2238
+ "",
2239
+ "## Checks",
2240
+ ...bundle.checks.map(
2241
+ (check) => `- ${check.status === "passed" ? "[x]" : "[ ]"} ${check.name}: ${check.message}`
2242
+ )
2243
+ ];
2244
+ return `${lines.join("\n")}
2245
+ `;
2246
+ }
2247
+ function validateHashes(value, errors) {
2248
+ if (!isRecord6(value)) {
2249
+ errors.push({
2250
+ path: "hashes",
2251
+ message: "hashes must be an object."
2252
+ });
2253
+ return;
2254
+ }
2255
+ for (const key of [
2256
+ "catalog",
2257
+ "catalogPolicy",
2258
+ "adapterInventory",
2259
+ "queries",
2260
+ "evaluation",
2261
+ "bundle"
2262
+ ]) {
2263
+ validateSha256(value[key], `hashes.${key}`, errors);
2264
+ }
2265
+ }
2266
+ function validateQueryCoverage(value, errors) {
2267
+ if (!Array.isArray(value)) {
2268
+ errors.push({
2269
+ path: "queryCoverage",
2270
+ message: "queryCoverage must be an array."
2271
+ });
2272
+ return;
2273
+ }
2274
+ value.forEach((coverage, index) => {
2275
+ if (!isRecord6(coverage)) {
2276
+ errors.push({
2277
+ path: `queryCoverage.${index}`,
2278
+ message: "query coverage entry must be an object."
2279
+ });
2280
+ return;
2281
+ }
2282
+ validateNonEmptyString5(coverage.capability, `queryCoverage.${index}.capability`, errors);
2283
+ validateNonNegativeInteger(
2284
+ coverage.minMatches,
2285
+ `queryCoverage.${index}.minMatches`,
2286
+ errors
2287
+ );
2288
+ validateNonNegativeInteger(
2289
+ coverage.matches,
2290
+ `queryCoverage.${index}.matches`,
2291
+ errors
2292
+ );
2293
+ validateNonNegativeInteger(
2294
+ coverage.routeableMatches,
2295
+ `queryCoverage.${index}.routeableMatches`,
2296
+ errors
2297
+ );
2298
+ validateNonNegativeInteger(
2299
+ coverage.missingAdapterMatches,
2300
+ `queryCoverage.${index}.missingAdapterMatches`,
2301
+ errors
2302
+ );
2303
+ validateStringArray5(coverage.providerIds, `queryCoverage.${index}.providerIds`, errors);
2304
+ });
2305
+ }
2306
+ function validateCheckArray(value, path, errors) {
2307
+ if (!Array.isArray(value)) {
2308
+ errors.push({
2309
+ path,
2310
+ message: `${path} must be an array.`
2311
+ });
2312
+ return;
2313
+ }
2314
+ value.forEach((check, index) => {
2315
+ if (!isRecord6(check)) {
2316
+ errors.push({
2317
+ path: `${path}.${index}`,
2318
+ message: "check must be an object."
2319
+ });
2320
+ return;
2321
+ }
2322
+ validateNonEmptyString5(check.name, `${path}.${index}.name`, errors);
2323
+ if (check.status !== "passed" && check.status !== "failed") {
2324
+ errors.push({
2325
+ path: `${path}.${index}.status`,
2326
+ message: 'check status must be "passed" or "failed".'
2327
+ });
2328
+ }
2329
+ validateNonEmptyString5(check.message, `${path}.${index}.message`, errors);
2330
+ });
2331
+ }
2332
+ function validateObject(value, path, errors) {
2333
+ if (!isRecord6(value)) {
2334
+ errors.push({
2335
+ path,
2336
+ message: `${path} must be an object.`
2337
+ });
2338
+ }
2339
+ }
2340
+ function validateSha256(value, path, errors) {
2341
+ if (typeof value !== "string" || !/^[a-f0-9]{64}$/.test(value)) {
2342
+ errors.push({
2343
+ path,
2344
+ message: `${path} must be a sha256 hex string.`
2345
+ });
2346
+ }
2347
+ }
2348
+ function validateNonEmptyString5(value, path, errors) {
2349
+ if (typeof value !== "string" || value.length === 0) {
2350
+ errors.push({
2351
+ path,
2352
+ message: `${path} must be a non-empty string.`
2353
+ });
2354
+ }
2355
+ }
2356
+ function validateStringArray5(value, path, errors) {
2357
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string" || entry.length === 0)) {
2358
+ errors.push({
2359
+ path,
2360
+ message: `${path} must be an array of non-empty strings.`
2361
+ });
2362
+ }
2363
+ }
2364
+ function validateNonNegativeInteger(value, path, errors) {
2365
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
2366
+ errors.push({
2367
+ path,
2368
+ message: `${path} must be a non-negative integer.`
2369
+ });
2370
+ }
2371
+ }
2372
+ function validateIsoTimestamp3(value, path, errors) {
2373
+ if (typeof value !== "string" || Number.isNaN(Date.parse(value))) {
2374
+ errors.push({
2375
+ path,
2376
+ message: `${path} must be an ISO timestamp.`
2377
+ });
2378
+ }
2379
+ }
2380
+ function sanitizeBundleOutput(value, forbiddenOutputValues) {
2381
+ return replaceForbiddenValues2(
2382
+ cloneSerializable4(value),
2383
+ forbiddenOutputValues.filter((entry) => entry.length > 0)
2384
+ );
2385
+ }
2386
+ function replaceForbiddenValues2(value, forbiddenValues) {
2387
+ if (typeof value === "string") {
2388
+ return forbiddenValues.reduce(
2389
+ (current, forbidden) => current.split(forbidden).join("[REDACTED]"),
2390
+ value
2391
+ );
2392
+ }
2393
+ if (value === null || typeof value !== "object" || value instanceof Date) {
2394
+ return value;
2395
+ }
2396
+ if (Array.isArray(value)) {
2397
+ return value.map((entry) => replaceForbiddenValues2(entry, forbiddenValues));
2398
+ }
2399
+ return Object.fromEntries(
2400
+ Object.entries(value).map(([key, entry]) => [
2401
+ key,
2402
+ replaceForbiddenValues2(entry, forbiddenValues)
2403
+ ])
2404
+ );
2405
+ }
2406
+ function cloneSerializable4(value) {
2407
+ if (value === void 0) {
2408
+ return value;
2409
+ }
2410
+ return JSON.parse(JSON.stringify(value));
2411
+ }
2412
+ function formatValidationErrors2(errors) {
2413
+ return errors.map((error) => `${error.path}: ${error.message}`).join("; ");
2414
+ }
2415
+ function formatInlineList(values) {
2416
+ return values.length > 0 ? values.join(", ") : "none";
2417
+ }
2418
+ function isRecord6(value) {
2419
+ return !!value && typeof value === "object" && !Array.isArray(value);
2420
+ }
2421
+
2422
+ // src/cli/providerCatalogPolicyCli.ts
2423
+ var USAGE = "Usage: probemesh-catalog-check --config <path> [--format markdown|json] [--out-json <path>] [--out-md <path>] [--out-bundle <path>] [--title <text>]";
2424
+ async function runProviderCatalogPolicyCli(options = {}) {
2425
+ const argv = options.argv ?? [];
2426
+ const cwd = options.cwd ?? process.cwd();
2427
+ try {
2428
+ const parsedArgs = parseCliArgs(argv);
2429
+ if (parsedArgs.help) {
2430
+ return {
2431
+ exitCode: 0,
2432
+ skipped: true,
2433
+ stdout: `${USAGE}
2434
+ `
2435
+ };
2436
+ }
2437
+ if (!parsedArgs.configPath) {
2438
+ return cliFailure("--config <path> is required.");
2439
+ }
2440
+ const configExport = await loadCliConfig(parsedArgs.configPath, {
2441
+ argv,
2442
+ cwd
2443
+ });
2444
+ if (isCliSkip(configExport)) {
2445
+ return {
2446
+ exitCode: 0,
2447
+ skipped: true,
2448
+ skip: configExport,
2449
+ stdout: `provider catalog policy check skipped: ${configExport.reason}
2450
+ `
2451
+ };
2452
+ }
2453
+ assertCliConfig(configExport);
2454
+ const catalog = resolveCatalog(configExport);
2455
+ const evaluation = evaluateProviderCatalogPolicy({
2456
+ catalog,
2457
+ catalogPolicy: configExport.catalogPolicy,
2458
+ adapterInventory: configExport.adapterInventory,
2459
+ queries: configExport.queries
2460
+ });
2461
+ const report = createProviderCatalogPolicyReport(evaluation, {
2462
+ ...configExport.report ?? {},
2463
+ title: parsedArgs.title ?? configExport.report?.title
2464
+ });
2465
+ const selectedFormat = parsedArgs.format ?? configExport.report?.format ?? "markdown";
2466
+ const stdout = formatProviderCatalogPolicyReport(report, {
2467
+ format: selectedFormat
2468
+ });
2469
+ const bundle = createOptionalBundle({
2470
+ catalog,
2471
+ config: configExport,
2472
+ evaluation,
2473
+ requestedBundleOutput: parsedArgs.outBundle
2474
+ });
2475
+ const artifactPaths = await writeArtifacts({
2476
+ cwd,
2477
+ report,
2478
+ bundle,
2479
+ outputs: {
2480
+ json: parsedArgs.outJson ?? configExport.outputs?.json,
2481
+ markdown: parsedArgs.outMarkdown ?? configExport.outputs?.markdown,
2482
+ bundle: parsedArgs.outBundle ?? configExport.outputs?.bundle
2483
+ }
2484
+ });
2485
+ return {
2486
+ exitCode: evaluation.status === "passed" ? 0 : 1,
2487
+ skipped: false,
2488
+ evaluation,
2489
+ report,
2490
+ bundle,
2491
+ stdout,
2492
+ artifactPaths
2493
+ };
2494
+ } catch (error) {
2495
+ return cliFailure(
2496
+ error instanceof Error ? error.message : "Unknown catalog policy CLI failure."
2497
+ );
2498
+ }
2499
+ }
2500
+ function parseCliArgs(argv) {
2501
+ const parsedArgs = {};
2502
+ for (let index = 0; index < argv.length; index += 1) {
2503
+ const arg = argv[index];
2504
+ if (arg === "--") {
2505
+ continue;
2506
+ }
2507
+ if (arg === "--help" || arg === "-h") {
2508
+ parsedArgs.help = true;
2509
+ continue;
2510
+ }
2511
+ if (arg === "--config") {
2512
+ parsedArgs.configPath = requireValue(argv, index, arg);
2513
+ index += 1;
2514
+ continue;
2515
+ }
2516
+ if (arg === "--format") {
2517
+ const format = requireValue(argv, index, arg);
2518
+ if (format !== "markdown" && format !== "json") {
2519
+ throw new Error("--format must be markdown or json.");
2520
+ }
2521
+ parsedArgs.format = format;
2522
+ index += 1;
2523
+ continue;
2524
+ }
2525
+ if (arg === "--out-json") {
2526
+ parsedArgs.outJson = requireValue(argv, index, arg);
2527
+ index += 1;
2528
+ continue;
2529
+ }
2530
+ if (arg === "--out-md") {
2531
+ parsedArgs.outMarkdown = requireValue(argv, index, arg);
2532
+ index += 1;
2533
+ continue;
2534
+ }
2535
+ if (arg === "--out-bundle") {
2536
+ parsedArgs.outBundle = requireValue(argv, index, arg);
2537
+ index += 1;
2538
+ continue;
2539
+ }
2540
+ if (arg === "--title") {
2541
+ parsedArgs.title = requireValue(argv, index, arg);
2542
+ index += 1;
2543
+ continue;
2544
+ }
2545
+ throw new Error(`Unknown argument "${arg}".`);
2546
+ }
2547
+ return parsedArgs;
2548
+ }
2549
+ function requireValue(argv, index, flag) {
2550
+ const value = argv[index + 1];
2551
+ if (!value || value.startsWith("--")) {
2552
+ throw new Error(`${flag} requires a value.`);
2553
+ }
2554
+ return value;
2555
+ }
2556
+ async function loadCliConfig(configPath, context) {
2557
+ const configExport = await loadCliConfigDefault(configPath, context.cwd);
2558
+ if (typeof configExport === "function") {
2559
+ return configExport(context);
2560
+ }
2561
+ return configExport;
2562
+ }
2563
+ function isCliSkip(value) {
2564
+ return !!value && typeof value === "object" && !Array.isArray(value) && value.skip === true && typeof value.reason === "string";
2565
+ }
2566
+ function assertCliConfig(value) {
2567
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
2568
+ throw new Error("Catalog policy CLI config must export an object.");
2569
+ }
2570
+ const config = value;
2571
+ if (!config.catalog && !config.artifacts) {
2572
+ throw new Error("Catalog policy CLI config requires catalog or artifacts.");
2573
+ }
2574
+ if (config.catalog && config.artifacts) {
2575
+ throw new Error("Catalog policy CLI config must not provide both catalog and artifacts.");
2576
+ }
2577
+ if (config.artifacts !== void 0 && !Array.isArray(config.artifacts)) {
2578
+ throw new Error("Catalog policy CLI artifacts must be an array.");
2579
+ }
2580
+ if (!Array.isArray(config.adapterInventory)) {
2581
+ throw new Error("Catalog policy CLI config requires adapterInventory array.");
2582
+ }
2583
+ if (!Array.isArray(config.queries) || config.queries.length === 0) {
2584
+ throw new Error("Catalog policy CLI config requires at least one query.");
2585
+ }
2586
+ const outputs = config.outputs;
2587
+ if (outputs !== void 0 && (!outputs || typeof outputs !== "object" || Array.isArray(outputs) || outputs.json !== void 0 && typeof outputs.json !== "string" || outputs.markdown !== void 0 && typeof outputs.markdown !== "string" || outputs.bundle !== void 0 && typeof outputs.bundle !== "string")) {
2588
+ throw new Error("Catalog policy CLI outputs must contain string paths.");
2589
+ }
2590
+ const bundle = config.bundle;
2591
+ if (bundle !== void 0 && (!bundle || typeof bundle !== "object" || Array.isArray(bundle) || bundle.bundleId !== void 0 && typeof bundle.bundleId !== "string" || bundle.generatedAt !== void 0 && typeof bundle.generatedAt !== "string")) {
2592
+ throw new Error("Catalog policy CLI bundle config must contain string values.");
2593
+ }
2594
+ }
2595
+ function resolveCatalog(config) {
2596
+ if (config.catalog) {
2597
+ return config.catalog;
2598
+ }
2599
+ return createProviderCatalog({
2600
+ artifacts: config.artifacts ?? []
2601
+ });
2602
+ }
2603
+ function createOptionalBundle(options) {
2604
+ if (!options.config.bundle && !options.requestedBundleOutput && !options.config.outputs?.bundle) {
2605
+ return void 0;
2606
+ }
2607
+ return createProviderCatalogPolicyBundle({
2608
+ catalog: options.catalog,
2609
+ catalogPolicy: options.config.catalogPolicy,
2610
+ adapterInventory: options.config.adapterInventory,
2611
+ queries: options.config.queries,
2612
+ evaluation: options.evaluation,
2613
+ bundleId: options.config.bundle?.bundleId,
2614
+ generatedAt: options.config.bundle?.generatedAt,
2615
+ forbiddenOutputValues: options.config.report?.forbiddenOutputValues
2616
+ });
2617
+ }
2618
+ async function writeArtifacts(options) {
2619
+ const artifactPaths = {};
2620
+ if (options.outputs.json) {
2621
+ const outputPath = (0, import_node_path2.resolve)(options.cwd, options.outputs.json);
2622
+ await writeArtifact(
2623
+ outputPath,
2624
+ formatProviderCatalogPolicyReport(options.report, {
2625
+ format: "json"
2626
+ })
2627
+ );
2628
+ artifactPaths.json = outputPath;
2629
+ }
2630
+ if (options.outputs.markdown) {
2631
+ const outputPath = (0, import_node_path2.resolve)(options.cwd, options.outputs.markdown);
2632
+ await writeArtifact(
2633
+ outputPath,
2634
+ formatProviderCatalogPolicyReport(options.report, {
2635
+ format: "markdown"
2636
+ })
2637
+ );
2638
+ artifactPaths.markdown = outputPath;
2639
+ }
2640
+ if (options.outputs.bundle) {
2641
+ if (!options.bundle) {
2642
+ throw new Error("Catalog policy CLI bundle output requires bundle config.");
2643
+ }
2644
+ const outputPath = (0, import_node_path2.resolve)(options.cwd, options.outputs.bundle);
2645
+ await writeArtifact(
2646
+ outputPath,
2647
+ formatProviderCatalogPolicyBundle(options.bundle, {
2648
+ format: "json"
2649
+ })
2650
+ );
2651
+ artifactPaths.bundle = outputPath;
2652
+ }
2653
+ return artifactPaths;
2654
+ }
2655
+ async function writeArtifact(outputPath, body) {
2656
+ await (0, import_promises2.mkdir)((0, import_node_path2.dirname)(outputPath), {
2657
+ recursive: true
2658
+ });
2659
+ await (0, import_promises2.writeFile)(outputPath, body, "utf8");
2660
+ }
2661
+ function cliFailure(message) {
2662
+ return {
2663
+ exitCode: 2,
2664
+ skipped: false,
2665
+ stdout: "",
2666
+ stderr: `${message}
2667
+ ${USAGE}
2668
+ `
2669
+ };
2670
+ }
2671
+
2672
+ // src/cli/catalog-check.ts
2673
+ void main();
2674
+ async function main() {
2675
+ const result = await runProviderCatalogPolicyCli({
2676
+ argv: process.argv.slice(2),
2677
+ cwd: process.cwd()
2678
+ });
2679
+ if (result.stdout) {
2680
+ process.stdout.write(result.stdout);
2681
+ }
2682
+ if (result.stderr) {
2683
+ process.stderr.write(result.stderr);
2684
+ }
2685
+ process.exitCode = result.exitCode;
2686
+ }
2687
+ //# sourceMappingURL=catalog-check.cjs.map