@dogpile/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 (88) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/LICENSE +16 -0
  3. package/README.md +842 -0
  4. package/dist/browser/index.d.ts +8 -0
  5. package/dist/browser/index.d.ts.map +1 -0
  6. package/dist/browser/index.js +4493 -0
  7. package/dist/browser/index.js.map +1 -0
  8. package/dist/index.d.ts +17 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +14 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/providers/openai-compatible.d.ts +44 -0
  13. package/dist/providers/openai-compatible.d.ts.map +1 -0
  14. package/dist/providers/openai-compatible.js +305 -0
  15. package/dist/providers/openai-compatible.js.map +1 -0
  16. package/dist/runtime/broadcast.d.ts +18 -0
  17. package/dist/runtime/broadcast.d.ts.map +1 -0
  18. package/dist/runtime/broadcast.js +335 -0
  19. package/dist/runtime/broadcast.js.map +1 -0
  20. package/dist/runtime/cancellation.d.ts +6 -0
  21. package/dist/runtime/cancellation.d.ts.map +1 -0
  22. package/dist/runtime/cancellation.js +35 -0
  23. package/dist/runtime/cancellation.js.map +1 -0
  24. package/dist/runtime/coordinator.d.ts +18 -0
  25. package/dist/runtime/coordinator.d.ts.map +1 -0
  26. package/dist/runtime/coordinator.js +434 -0
  27. package/dist/runtime/coordinator.js.map +1 -0
  28. package/dist/runtime/decisions.d.ts +5 -0
  29. package/dist/runtime/decisions.d.ts.map +1 -0
  30. package/dist/runtime/decisions.js +31 -0
  31. package/dist/runtime/decisions.js.map +1 -0
  32. package/dist/runtime/defaults.d.ts +63 -0
  33. package/dist/runtime/defaults.d.ts.map +1 -0
  34. package/dist/runtime/defaults.js +426 -0
  35. package/dist/runtime/defaults.js.map +1 -0
  36. package/dist/runtime/engine.d.ts +79 -0
  37. package/dist/runtime/engine.d.ts.map +1 -0
  38. package/dist/runtime/engine.js +723 -0
  39. package/dist/runtime/engine.js.map +1 -0
  40. package/dist/runtime/model.d.ts +14 -0
  41. package/dist/runtime/model.d.ts.map +1 -0
  42. package/dist/runtime/model.js +82 -0
  43. package/dist/runtime/model.js.map +1 -0
  44. package/dist/runtime/sequential.d.ts +18 -0
  45. package/dist/runtime/sequential.d.ts.map +1 -0
  46. package/dist/runtime/sequential.js +277 -0
  47. package/dist/runtime/sequential.js.map +1 -0
  48. package/dist/runtime/shared.d.ts +18 -0
  49. package/dist/runtime/shared.d.ts.map +1 -0
  50. package/dist/runtime/shared.js +288 -0
  51. package/dist/runtime/shared.js.map +1 -0
  52. package/dist/runtime/termination.d.ts +77 -0
  53. package/dist/runtime/termination.d.ts.map +1 -0
  54. package/dist/runtime/termination.js +355 -0
  55. package/dist/runtime/termination.js.map +1 -0
  56. package/dist/runtime/tools.d.ts +314 -0
  57. package/dist/runtime/tools.d.ts.map +1 -0
  58. package/dist/runtime/tools.js +969 -0
  59. package/dist/runtime/tools.js.map +1 -0
  60. package/dist/runtime/validation.d.ts +23 -0
  61. package/dist/runtime/validation.d.ts.map +1 -0
  62. package/dist/runtime/validation.js +656 -0
  63. package/dist/runtime/validation.js.map +1 -0
  64. package/dist/types.d.ts +2434 -0
  65. package/dist/types.d.ts.map +1 -0
  66. package/dist/types.js +81 -0
  67. package/dist/types.js.map +1 -0
  68. package/package.json +157 -0
  69. package/src/browser/index.ts +7 -0
  70. package/src/index.ts +195 -0
  71. package/src/providers/openai-compatible.ts +406 -0
  72. package/src/runtime/broadcast.test.ts +355 -0
  73. package/src/runtime/broadcast.ts +428 -0
  74. package/src/runtime/cancellation.ts +40 -0
  75. package/src/runtime/coordinator.test.ts +468 -0
  76. package/src/runtime/coordinator.ts +581 -0
  77. package/src/runtime/decisions.ts +38 -0
  78. package/src/runtime/defaults.ts +547 -0
  79. package/src/runtime/engine.ts +880 -0
  80. package/src/runtime/model.ts +117 -0
  81. package/src/runtime/sequential.test.ts +262 -0
  82. package/src/runtime/sequential.ts +357 -0
  83. package/src/runtime/shared.test.ts +265 -0
  84. package/src/runtime/shared.ts +367 -0
  85. package/src/runtime/termination.ts +463 -0
  86. package/src/runtime/tools.ts +1518 -0
  87. package/src/runtime/validation.ts +771 -0
  88. package/src/types.ts +2729 -0
@@ -0,0 +1,656 @@
1
+ import { DogpileError } from "../types.js";
2
+ const protocolNames = ["coordinator", "sequential", "broadcast", "shared"];
3
+ const budgetTiers = ["fast", "balanced", "quality"];
4
+ /**
5
+ * Validate high-level caller options before any protocol execution starts.
6
+ */
7
+ export function validateDogpileOptions(options) {
8
+ requireRecord(options, "options");
9
+ validateMissionIntent(options.intent);
10
+ if (options.protocol !== undefined) {
11
+ validateProtocolSelection(options.protocol, "protocol");
12
+ }
13
+ if (options.tier !== undefined) {
14
+ validateBudgetTier(options.tier, "tier");
15
+ }
16
+ validateModelProviderRegistration(options.model, "model");
17
+ validateOptionalAgents(options.agents, "agents");
18
+ validateOptionalRuntimeTools(options.tools, "tools");
19
+ validateOptionalTemperature(options.temperature, "temperature");
20
+ validateOptionalBudgetCaps(options.budget, "budget");
21
+ validateOptionalTerminationCondition(options.terminate, "terminate");
22
+ validateOptionalFunction(options.evaluate, "evaluate");
23
+ validateOptionalSeed(options.seed, "seed");
24
+ validateOptionalAbortSignal(options.signal, "signal");
25
+ }
26
+ export function validateMissionIntent(intent, path = "intent") {
27
+ validateNonEmptyString(intent, path, "intent is required.");
28
+ }
29
+ /**
30
+ * Validate low-level engine configuration before normalizing reusable controls.
31
+ */
32
+ export function validateEngineOptions(options) {
33
+ requireRecord(options, "options");
34
+ validateProtocolSelection(options.protocol, "protocol");
35
+ validateBudgetTier(options.tier, "tier");
36
+ validateModelProviderRegistration(options.model, "model");
37
+ validateOptionalAgents(options.agents, "agents");
38
+ validateOptionalRuntimeTools(options.tools, "tools");
39
+ validateOptionalTemperature(options.temperature, "temperature");
40
+ validateOptionalBudgetCaps(options.budget, "budget");
41
+ validateOptionalTerminationCondition(options.terminate, "terminate");
42
+ validateOptionalFunction(options.evaluate, "evaluate");
43
+ validateOptionalSeed(options.seed, "seed");
44
+ validateOptionalAbortSignal(options.signal, "signal");
45
+ }
46
+ /**
47
+ * Validate Vercel AI adapter factory options at construction time.
48
+ */
49
+ export function validateVercelAIProviderOptions(options) {
50
+ const record = requireRecord(options, "options");
51
+ if (record.model === undefined) {
52
+ invalidConfiguration({
53
+ path: "model",
54
+ rule: "required",
55
+ message: "model is required.",
56
+ expected: "a Vercel AI language model",
57
+ actual: record.model
58
+ });
59
+ }
60
+ if (typeof record.model === "string") {
61
+ validateNonEmptyString(record.model, "model", "model must not be empty.");
62
+ }
63
+ else {
64
+ validateVercelAILanguageModel(record.model, "model");
65
+ }
66
+ validateOptionalNonEmptyString(record.id, "id");
67
+ validateOptionalBoolean(record.streaming, "streaming");
68
+ validateOptionalFunction(record.generateText, "generateText");
69
+ validateOptionalFunction(record.streamText, "streamText");
70
+ validateOptionalFunction(record.costEstimator, "costEstimator");
71
+ validateOptionalPositiveInteger(record.maxOutputTokens, "maxOutputTokens");
72
+ validateOptionalNumberInRange(record.topP, "topP", 0, 1);
73
+ validateOptionalPositiveInteger(record.topK, "topK");
74
+ validateOptionalNumberInRange(record.presencePenalty, "presencePenalty", -2, 2);
75
+ validateOptionalNumberInRange(record.frequencyPenalty, "frequencyPenalty", -2, 2);
76
+ validateOptionalStringArray(record.stopSequences, "stopSequences");
77
+ validateOptionalInteger(record.seed, "seed");
78
+ validateOptionalNonNegativeInteger(record.maxRetries, "maxRetries");
79
+ validateOptionalAbortSignal(record.abortSignal, "abortSignal");
80
+ validateOptionalHeaders(record.headers, "headers");
81
+ validateOptionalProviderOptions(record.providerOptions, "providerOptions");
82
+ validateOptionalArray(record.activeTools, "activeTools");
83
+ validateOptionalFunction(record.runtimeToolIdForName, "runtimeToolIdForName");
84
+ }
85
+ function validateProtocolSelection(value, path) {
86
+ if (typeof value === "string") {
87
+ if (!isProtocolName(value)) {
88
+ invalidConfiguration({
89
+ path,
90
+ rule: "enum",
91
+ message: "protocol must be one of coordinator, sequential, broadcast, or shared.",
92
+ expected: protocolNames.join(" | "),
93
+ actual: value
94
+ });
95
+ }
96
+ return;
97
+ }
98
+ validateProtocolConfig(value, path);
99
+ }
100
+ function validateProtocolConfig(value, path) {
101
+ const record = requireRecord(value, path);
102
+ const kind = record.kind;
103
+ if (!isProtocolName(kind)) {
104
+ invalidConfiguration({
105
+ path: `${path}.kind`,
106
+ rule: "enum",
107
+ message: "protocol config kind must be one of coordinator, sequential, broadcast, or shared.",
108
+ expected: protocolNames.join(" | "),
109
+ actual: kind
110
+ });
111
+ }
112
+ switch (kind) {
113
+ case "coordinator":
114
+ case "sequential":
115
+ case "shared":
116
+ validateOptionalPositiveInteger(record.maxTurns, `${path}.maxTurns`);
117
+ if (kind === "shared") {
118
+ validateOptionalString(record.organizationalMemory, `${path}.organizationalMemory`);
119
+ }
120
+ return;
121
+ case "broadcast":
122
+ validateOptionalPositiveInteger(record.maxRounds, `${path}.maxRounds`);
123
+ return;
124
+ }
125
+ }
126
+ function validateBudgetTier(value, path) {
127
+ if (!isBudgetTier(value)) {
128
+ invalidConfiguration({
129
+ path,
130
+ rule: "enum",
131
+ message: "tier must be one of fast, balanced, or quality.",
132
+ expected: budgetTiers.join(" | "),
133
+ actual: value
134
+ });
135
+ }
136
+ }
137
+ /**
138
+ * Validate configured model provider definitions at registration boundaries.
139
+ */
140
+ export function validateModelProviderRegistration(value, path = "model") {
141
+ const record = requireRecord(value, path);
142
+ validateNonEmptyString(record.id, `${path}.id`, "model.id is required.");
143
+ validateFunction(record.generate, `${path}.generate`);
144
+ validateOptionalFunction(record.stream, `${path}.stream`);
145
+ }
146
+ function validateVercelAILanguageModel(value, path) {
147
+ const record = requireRecord(value, path);
148
+ if (record.specificationVersion !== "v2" && record.specificationVersion !== "v3") {
149
+ invalidConfiguration({
150
+ path: `${path}.specificationVersion`,
151
+ rule: "model-provider",
152
+ message: "model.specificationVersion must be v2 or v3.",
153
+ expected: "v2 | v3",
154
+ actual: record.specificationVersion
155
+ });
156
+ }
157
+ validateNonEmptyString(record.provider, `${path}.provider`, "model.provider is required.");
158
+ validateNonEmptyString(record.modelId, `${path}.modelId`, "model.modelId is required.");
159
+ validateFunction(record.doGenerate, `${path}.doGenerate`);
160
+ validateFunction(record.doStream, `${path}.doStream`);
161
+ }
162
+ function validateOptionalAgents(value, path) {
163
+ if (value === undefined) {
164
+ return;
165
+ }
166
+ if (!Array.isArray(value)) {
167
+ invalidConfiguration({
168
+ path,
169
+ rule: "array",
170
+ message: "agents must be an array when provided.",
171
+ expected: "readonly AgentSpec[]",
172
+ actual: value
173
+ });
174
+ }
175
+ if (value.length === 0) {
176
+ invalidConfiguration({
177
+ path,
178
+ rule: "array",
179
+ message: "agents must contain at least one participant when provided.",
180
+ expected: "non-empty readonly AgentSpec[]",
181
+ actual: value
182
+ });
183
+ }
184
+ value.forEach((agent, index) => {
185
+ const agentPath = `${path}[${index}]`;
186
+ const record = requireRecord(agent, agentPath);
187
+ validateNonEmptyString(record.id, `${agentPath}.id`, "agent.id is required.");
188
+ validateNonEmptyString(record.role, `${agentPath}.role`, "agent.role is required.");
189
+ validateOptionalString(record.instructions, `${agentPath}.instructions`);
190
+ });
191
+ }
192
+ function validateOptionalRuntimeTools(value, path) {
193
+ if (value === undefined) {
194
+ return;
195
+ }
196
+ validateRuntimeToolRegistrations(value, path);
197
+ }
198
+ /**
199
+ * Validate runtime tool definitions at registration boundaries.
200
+ */
201
+ export function validateRuntimeToolRegistrations(value, path = "tools") {
202
+ if (!Array.isArray(value)) {
203
+ invalidConfiguration({
204
+ path,
205
+ rule: "array",
206
+ message: "tools must be an array when provided.",
207
+ expected: "readonly RuntimeTool[]",
208
+ actual: value
209
+ });
210
+ }
211
+ value.forEach((tool, index) => validateRuntimeTool(tool, `${path}[${index}]`));
212
+ }
213
+ function validateRuntimeTool(value, path) {
214
+ const record = requireRecord(value, path);
215
+ const identity = requireRecord(record.identity, `${path}.identity`);
216
+ validateNonEmptyString(identity.id, `${path}.identity.id`, "tool identity id is required.");
217
+ validateNonEmptyString(identity.name, `${path}.identity.name`, "tool identity name is required.");
218
+ validateOptionalString(identity.namespace, `${path}.identity.namespace`);
219
+ validateOptionalString(identity.version, `${path}.identity.version`);
220
+ validateOptionalString(identity.description, `${path}.identity.description`);
221
+ const inputSchema = requireRecord(record.inputSchema, `${path}.inputSchema`);
222
+ if (inputSchema.kind !== "json-schema") {
223
+ invalidConfiguration({
224
+ path: `${path}.inputSchema.kind`,
225
+ rule: "runtime-tool",
226
+ message: "tool inputSchema.kind must be json-schema.",
227
+ expected: "json-schema",
228
+ actual: inputSchema.kind
229
+ });
230
+ }
231
+ validateJsonObject(inputSchema.schema, `${path}.inputSchema.schema`);
232
+ validateOptionalString(inputSchema.description, `${path}.inputSchema.description`);
233
+ validateOptionalArray(record.permissions, `${path}.permissions`);
234
+ validateOptionalFunction(record.validateInput, `${path}.validateInput`);
235
+ validateFunction(record.execute, `${path}.execute`);
236
+ }
237
+ function validateOptionalBudgetCaps(value, path) {
238
+ if (value === undefined) {
239
+ return;
240
+ }
241
+ validateBudgetCaps(value, path);
242
+ }
243
+ function validateBudgetCaps(value, path) {
244
+ const record = requireRecord(value, path);
245
+ validateOptionalNonNegativeNumber(record.maxUsd, `${path}.maxUsd`);
246
+ validateOptionalNonNegativeInteger(record.maxTokens, `${path}.maxTokens`);
247
+ validateOptionalNonNegativeInteger(record.maxIterations, `${path}.maxIterations`);
248
+ validateOptionalNonNegativeInteger(record.timeoutMs, `${path}.timeoutMs`);
249
+ validateOptionalNumberInRange(record.qualityWeight, `${path}.qualityWeight`, 0, 1);
250
+ }
251
+ function validateOptionalTerminationCondition(value, path) {
252
+ if (value === undefined) {
253
+ return;
254
+ }
255
+ validateTerminationCondition(value, path, new Set());
256
+ }
257
+ function validateTerminationCondition(value, path, stack) {
258
+ const record = requireRecord(value, path);
259
+ if (stack.has(record)) {
260
+ invalidConfiguration({
261
+ path,
262
+ rule: "termination-condition",
263
+ message: "termination conditions must not contain cycles.",
264
+ expected: "acyclic termination condition",
265
+ actual: value
266
+ });
267
+ }
268
+ stack.add(record);
269
+ try {
270
+ switch (record.kind) {
271
+ case "budget":
272
+ validateBudgetCaps(record, path);
273
+ return;
274
+ case "convergence":
275
+ validatePositiveInteger(record.stableTurns, `${path}.stableTurns`);
276
+ validateNumberInRange(record.minSimilarity, `${path}.minSimilarity`, 0, 1);
277
+ return;
278
+ case "judge":
279
+ validateJudgeRubric(record.rubric, `${path}.rubric`);
280
+ validateOptionalNumberInRange(record.minScore, `${path}.minScore`, 0, 1);
281
+ return;
282
+ case "firstOf":
283
+ validateFirstOfConditions(record.conditions, `${path}.conditions`, stack);
284
+ return;
285
+ default:
286
+ invalidConfiguration({
287
+ path: `${path}.kind`,
288
+ rule: "termination-condition",
289
+ message: "termination condition kind must be budget, convergence, judge, or firstOf.",
290
+ expected: "budget | convergence | judge | firstOf",
291
+ actual: record.kind
292
+ });
293
+ }
294
+ }
295
+ finally {
296
+ stack.delete(record);
297
+ }
298
+ }
299
+ function validateFirstOfConditions(value, path, stack) {
300
+ if (!Array.isArray(value)) {
301
+ invalidConfiguration({
302
+ path,
303
+ rule: "array",
304
+ message: "firstOf conditions must be a non-empty array.",
305
+ expected: "non-empty termination condition array",
306
+ actual: value
307
+ });
308
+ }
309
+ if (value.length === 0) {
310
+ invalidConfiguration({
311
+ path,
312
+ rule: "array",
313
+ message: "firstOf conditions must contain at least one condition.",
314
+ expected: "non-empty termination condition array",
315
+ actual: value
316
+ });
317
+ }
318
+ value.forEach((condition, index) => {
319
+ validateTerminationCondition(condition, `${path}[${index}]`, stack);
320
+ });
321
+ }
322
+ function validateJudgeRubric(value, path) {
323
+ if (typeof value === "string") {
324
+ validateNonEmptyString(value, path, "judge rubric must not be empty.");
325
+ return;
326
+ }
327
+ validateJsonObject(value, path);
328
+ }
329
+ function validateOptionalTemperature(value, path) {
330
+ validateOptionalNumberInRange(value, path, 0, 2);
331
+ }
332
+ function validateOptionalSeed(value, path) {
333
+ if (value === undefined) {
334
+ return;
335
+ }
336
+ if (typeof value === "string") {
337
+ return;
338
+ }
339
+ if (typeof value === "number" && Number.isFinite(value)) {
340
+ return;
341
+ }
342
+ invalidConfiguration({
343
+ path,
344
+ rule: "finite-number",
345
+ message: "seed must be a string or finite number when provided.",
346
+ expected: "string or finite number",
347
+ actual: value
348
+ });
349
+ }
350
+ function validateOptionalHeaders(value, path) {
351
+ if (value === undefined) {
352
+ return;
353
+ }
354
+ const record = requireRecord(value, path);
355
+ for (const [key, headerValue] of Object.entries(record)) {
356
+ if (headerValue !== undefined && typeof headerValue !== "string") {
357
+ invalidConfiguration({
358
+ path: `${path}.${key}`,
359
+ rule: "non-empty-string",
360
+ message: "headers values must be strings or undefined.",
361
+ expected: "string | undefined",
362
+ actual: headerValue
363
+ });
364
+ }
365
+ }
366
+ }
367
+ function validateOptionalProviderOptions(value, path) {
368
+ if (value === undefined) {
369
+ return;
370
+ }
371
+ const record = requireRecord(value, path);
372
+ for (const [key, providerOptions] of Object.entries(record)) {
373
+ validateJsonObject(providerOptions, `${path}.${key}`);
374
+ }
375
+ }
376
+ function validateJsonObject(value, path) {
377
+ if (!isJsonValue(value, new Set()) || !isRecord(value)) {
378
+ invalidConfiguration({
379
+ path,
380
+ rule: "json-object",
381
+ message: "value must be a JSON-compatible object.",
382
+ expected: "JSON-compatible object",
383
+ actual: value
384
+ });
385
+ }
386
+ }
387
+ function isJsonValue(value, stack) {
388
+ if (value === null || typeof value === "string" || typeof value === "boolean") {
389
+ return true;
390
+ }
391
+ if (typeof value === "number") {
392
+ return Number.isFinite(value);
393
+ }
394
+ if (Array.isArray(value)) {
395
+ if (stack.has(value)) {
396
+ return false;
397
+ }
398
+ stack.add(value);
399
+ const valid = value.every((child) => isJsonValue(child, stack));
400
+ stack.delete(value);
401
+ return valid;
402
+ }
403
+ if (isRecord(value)) {
404
+ if (stack.has(value)) {
405
+ return false;
406
+ }
407
+ stack.add(value);
408
+ const valid = Object.values(value).every((child) => isJsonValue(child, stack));
409
+ stack.delete(value);
410
+ return valid;
411
+ }
412
+ return false;
413
+ }
414
+ function validateOptionalString(value, path) {
415
+ if (value === undefined) {
416
+ return;
417
+ }
418
+ if (typeof value !== "string") {
419
+ invalidConfiguration({
420
+ path,
421
+ rule: "non-empty-string",
422
+ message: "value must be a string when provided.",
423
+ expected: "string",
424
+ actual: value
425
+ });
426
+ }
427
+ }
428
+ function validateOptionalNonEmptyString(value, path) {
429
+ if (value === undefined) {
430
+ return;
431
+ }
432
+ validateNonEmptyString(value, path, `${path} must not be empty.`);
433
+ }
434
+ function validateNonEmptyString(value, path, message) {
435
+ if (typeof value !== "string" || value.trim().length === 0) {
436
+ invalidConfiguration({
437
+ path,
438
+ rule: "non-empty-string",
439
+ message,
440
+ expected: "non-empty string",
441
+ actual: value
442
+ });
443
+ }
444
+ }
445
+ function validateOptionalBoolean(value, path) {
446
+ if (value === undefined) {
447
+ return;
448
+ }
449
+ if (typeof value !== "boolean") {
450
+ invalidConfiguration({
451
+ path,
452
+ rule: "boolean",
453
+ message: "value must be a boolean when provided.",
454
+ expected: "boolean",
455
+ actual: value
456
+ });
457
+ }
458
+ }
459
+ function validateOptionalFunction(value, path) {
460
+ if (value === undefined) {
461
+ return;
462
+ }
463
+ validateFunction(value, path);
464
+ }
465
+ function validateFunction(value, path) {
466
+ if (typeof value !== "function") {
467
+ invalidConfiguration({
468
+ path,
469
+ rule: "function",
470
+ message: "value must be a function.",
471
+ expected: "function",
472
+ actual: value
473
+ });
474
+ }
475
+ }
476
+ function validateOptionalArray(value, path) {
477
+ if (value === undefined) {
478
+ return;
479
+ }
480
+ if (!Array.isArray(value)) {
481
+ invalidConfiguration({
482
+ path,
483
+ rule: "array",
484
+ message: "value must be an array when provided.",
485
+ expected: "array",
486
+ actual: value
487
+ });
488
+ }
489
+ }
490
+ function validateOptionalStringArray(value, path) {
491
+ if (value === undefined) {
492
+ return;
493
+ }
494
+ if (!Array.isArray(value)) {
495
+ invalidConfiguration({
496
+ path,
497
+ rule: "array",
498
+ message: "value must be an array of strings when provided.",
499
+ expected: "string[]",
500
+ actual: value
501
+ });
502
+ }
503
+ value.forEach((item, index) => validateNonEmptyString(item, `${path}[${index}]`, "array item must be a string."));
504
+ }
505
+ function validateOptionalAbortSignal(value, path) {
506
+ if (value === undefined) {
507
+ return;
508
+ }
509
+ if (!isAbortSignalLike(value)) {
510
+ invalidConfiguration({
511
+ path,
512
+ rule: "abort-signal",
513
+ message: "value must be an AbortSignal when provided.",
514
+ expected: "AbortSignal",
515
+ actual: value
516
+ });
517
+ }
518
+ }
519
+ function isAbortSignalLike(value) {
520
+ if (!isRecord(value)) {
521
+ return false;
522
+ }
523
+ return (typeof value.aborted === "boolean" &&
524
+ typeof value.addEventListener === "function" &&
525
+ typeof value.removeEventListener === "function");
526
+ }
527
+ function validateOptionalInteger(value, path) {
528
+ if (value === undefined) {
529
+ return;
530
+ }
531
+ validateInteger(value, path);
532
+ }
533
+ function validateInteger(value, path) {
534
+ if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value)) {
535
+ invalidConfiguration({
536
+ path,
537
+ rule: "finite-number",
538
+ message: "value must be a finite integer.",
539
+ expected: "finite integer",
540
+ actual: value
541
+ });
542
+ }
543
+ }
544
+ function validateOptionalPositiveInteger(value, path) {
545
+ if (value === undefined) {
546
+ return;
547
+ }
548
+ validatePositiveInteger(value, path);
549
+ }
550
+ function validatePositiveInteger(value, path) {
551
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
552
+ invalidConfiguration({
553
+ path,
554
+ rule: "positive-integer",
555
+ message: "value must be a positive integer.",
556
+ expected: "integer >= 1",
557
+ actual: value
558
+ });
559
+ }
560
+ }
561
+ function validateOptionalNonNegativeInteger(value, path) {
562
+ if (value === undefined) {
563
+ return;
564
+ }
565
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
566
+ invalidConfiguration({
567
+ path,
568
+ rule: "non-negative-integer",
569
+ message: "value must be a non-negative integer.",
570
+ expected: "integer >= 0",
571
+ actual: value
572
+ });
573
+ }
574
+ }
575
+ function validateOptionalNonNegativeNumber(value, path) {
576
+ if (value === undefined) {
577
+ return;
578
+ }
579
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
580
+ invalidConfiguration({
581
+ path,
582
+ rule: "non-negative-number",
583
+ message: "value must be a non-negative finite number.",
584
+ expected: "finite number >= 0",
585
+ actual: value
586
+ });
587
+ }
588
+ }
589
+ function validateOptionalNumberInRange(value, path, min, max) {
590
+ if (value === undefined) {
591
+ return;
592
+ }
593
+ validateNumberInRange(value, path, min, max);
594
+ }
595
+ function validateNumberInRange(value, path, min, max) {
596
+ if (typeof value !== "number" || !Number.isFinite(value) || value < min || value > max) {
597
+ invalidConfiguration({
598
+ path,
599
+ rule: "range",
600
+ message: `value must be a finite number in the inclusive range ${min}..${max}.`,
601
+ expected: `finite number in ${min}..${max}`,
602
+ actual: value
603
+ });
604
+ }
605
+ }
606
+ function requireRecord(value, path) {
607
+ if (!isRecord(value)) {
608
+ invalidConfiguration({
609
+ path,
610
+ rule: "object",
611
+ message: "value must be an object.",
612
+ expected: "object",
613
+ actual: value
614
+ });
615
+ }
616
+ return value;
617
+ }
618
+ function invalidConfiguration(options) {
619
+ throw new DogpileError({
620
+ code: "invalid-configuration",
621
+ message: `Invalid Dogpile configuration at ${options.path}: ${options.message}`,
622
+ retryable: false,
623
+ detail: {
624
+ kind: "configuration-validation",
625
+ path: options.path,
626
+ rule: options.rule,
627
+ expected: options.expected,
628
+ received: describeValue(options.actual)
629
+ }
630
+ });
631
+ }
632
+ function isProtocolName(value) {
633
+ return typeof value === "string" && protocolNames.includes(value);
634
+ }
635
+ function isBudgetTier(value) {
636
+ return typeof value === "string" && budgetTiers.includes(value);
637
+ }
638
+ function isRecord(value) {
639
+ return typeof value === "object" && value !== null && !Array.isArray(value);
640
+ }
641
+ function describeValue(value) {
642
+ if (value === null) {
643
+ return "null";
644
+ }
645
+ if (value === undefined) {
646
+ return "undefined";
647
+ }
648
+ if (Array.isArray(value)) {
649
+ return "array";
650
+ }
651
+ if (typeof value === "number" && !Number.isFinite(value)) {
652
+ return String(value);
653
+ }
654
+ return typeof value;
655
+ }
656
+ //# sourceMappingURL=validation.js.map