@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.
- package/CHANGELOG.md +37 -0
- package/LICENSE +16 -0
- package/README.md +842 -0
- package/dist/browser/index.d.ts +8 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +4493 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/openai-compatible.d.ts +44 -0
- package/dist/providers/openai-compatible.d.ts.map +1 -0
- package/dist/providers/openai-compatible.js +305 -0
- package/dist/providers/openai-compatible.js.map +1 -0
- package/dist/runtime/broadcast.d.ts +18 -0
- package/dist/runtime/broadcast.d.ts.map +1 -0
- package/dist/runtime/broadcast.js +335 -0
- package/dist/runtime/broadcast.js.map +1 -0
- package/dist/runtime/cancellation.d.ts +6 -0
- package/dist/runtime/cancellation.d.ts.map +1 -0
- package/dist/runtime/cancellation.js +35 -0
- package/dist/runtime/cancellation.js.map +1 -0
- package/dist/runtime/coordinator.d.ts +18 -0
- package/dist/runtime/coordinator.d.ts.map +1 -0
- package/dist/runtime/coordinator.js +434 -0
- package/dist/runtime/coordinator.js.map +1 -0
- package/dist/runtime/decisions.d.ts +5 -0
- package/dist/runtime/decisions.d.ts.map +1 -0
- package/dist/runtime/decisions.js +31 -0
- package/dist/runtime/decisions.js.map +1 -0
- package/dist/runtime/defaults.d.ts +63 -0
- package/dist/runtime/defaults.d.ts.map +1 -0
- package/dist/runtime/defaults.js +426 -0
- package/dist/runtime/defaults.js.map +1 -0
- package/dist/runtime/engine.d.ts +79 -0
- package/dist/runtime/engine.d.ts.map +1 -0
- package/dist/runtime/engine.js +723 -0
- package/dist/runtime/engine.js.map +1 -0
- package/dist/runtime/model.d.ts +14 -0
- package/dist/runtime/model.d.ts.map +1 -0
- package/dist/runtime/model.js +82 -0
- package/dist/runtime/model.js.map +1 -0
- package/dist/runtime/sequential.d.ts +18 -0
- package/dist/runtime/sequential.d.ts.map +1 -0
- package/dist/runtime/sequential.js +277 -0
- package/dist/runtime/sequential.js.map +1 -0
- package/dist/runtime/shared.d.ts +18 -0
- package/dist/runtime/shared.d.ts.map +1 -0
- package/dist/runtime/shared.js +288 -0
- package/dist/runtime/shared.js.map +1 -0
- package/dist/runtime/termination.d.ts +77 -0
- package/dist/runtime/termination.d.ts.map +1 -0
- package/dist/runtime/termination.js +355 -0
- package/dist/runtime/termination.js.map +1 -0
- package/dist/runtime/tools.d.ts +314 -0
- package/dist/runtime/tools.d.ts.map +1 -0
- package/dist/runtime/tools.js +969 -0
- package/dist/runtime/tools.js.map +1 -0
- package/dist/runtime/validation.d.ts +23 -0
- package/dist/runtime/validation.d.ts.map +1 -0
- package/dist/runtime/validation.js +656 -0
- package/dist/runtime/validation.js.map +1 -0
- package/dist/types.d.ts +2434 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +81 -0
- package/dist/types.js.map +1 -0
- package/package.json +157 -0
- package/src/browser/index.ts +7 -0
- package/src/index.ts +195 -0
- package/src/providers/openai-compatible.ts +406 -0
- package/src/runtime/broadcast.test.ts +355 -0
- package/src/runtime/broadcast.ts +428 -0
- package/src/runtime/cancellation.ts +40 -0
- package/src/runtime/coordinator.test.ts +468 -0
- package/src/runtime/coordinator.ts +581 -0
- package/src/runtime/decisions.ts +38 -0
- package/src/runtime/defaults.ts +547 -0
- package/src/runtime/engine.ts +880 -0
- package/src/runtime/model.ts +117 -0
- package/src/runtime/sequential.test.ts +262 -0
- package/src/runtime/sequential.ts +357 -0
- package/src/runtime/shared.test.ts +265 -0
- package/src/runtime/shared.ts +367 -0
- package/src/runtime/termination.ts +463 -0
- package/src/runtime/tools.ts +1518 -0
- package/src/runtime/validation.ts +771 -0
- 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
|