@oxygen-agent/cli 1.45.4

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 (73) hide show
  1. package/README.md +25 -0
  2. package/dist/browser-login.d.ts +8 -0
  3. package/dist/browser-login.d.ts.map +1 -0
  4. package/dist/browser-login.js +181 -0
  5. package/dist/browser-login.js.map +1 -0
  6. package/dist/credentials.d.ts +28 -0
  7. package/dist/credentials.d.ts.map +1 -0
  8. package/dist/credentials.js +470 -0
  9. package/dist/credentials.js.map +1 -0
  10. package/dist/http-client.d.ts +12 -0
  11. package/dist/http-client.d.ts.map +1 -0
  12. package/dist/http-client.js +218 -0
  13. package/dist/http-client.js.map +1 -0
  14. package/dist/index.d.ts +4 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +3523 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/local-custom-http-column.d.ts +9 -0
  19. package/dist/local-custom-http-column.d.ts.map +1 -0
  20. package/dist/local-custom-http-column.js +571 -0
  21. package/dist/local-custom-http-column.js.map +1 -0
  22. package/dist/session.d.ts +57 -0
  23. package/dist/session.d.ts.map +1 -0
  24. package/dist/session.js +194 -0
  25. package/dist/session.js.map +1 -0
  26. package/node_modules/@oxygen/recipe-sdk/dist/index.d.ts +136 -0
  27. package/node_modules/@oxygen/recipe-sdk/dist/index.d.ts.map +1 -0
  28. package/node_modules/@oxygen/recipe-sdk/dist/index.js +43 -0
  29. package/node_modules/@oxygen/recipe-sdk/dist/index.js.map +1 -0
  30. package/node_modules/@oxygen/recipe-sdk/package.json +15 -0
  31. package/node_modules/@oxygen/shared/dist/billing.d.ts +124 -0
  32. package/node_modules/@oxygen/shared/dist/billing.d.ts.map +1 -0
  33. package/node_modules/@oxygen/shared/dist/billing.js +296 -0
  34. package/node_modules/@oxygen/shared/dist/billing.js.map +1 -0
  35. package/node_modules/@oxygen/shared/dist/column-types.d.ts +55 -0
  36. package/node_modules/@oxygen/shared/dist/column-types.d.ts.map +1 -0
  37. package/node_modules/@oxygen/shared/dist/column-types.js +161 -0
  38. package/node_modules/@oxygen/shared/dist/column-types.js.map +1 -0
  39. package/node_modules/@oxygen/shared/dist/credit-guidance.d.ts +15 -0
  40. package/node_modules/@oxygen/shared/dist/credit-guidance.d.ts.map +1 -0
  41. package/node_modules/@oxygen/shared/dist/credit-guidance.js +60 -0
  42. package/node_modules/@oxygen/shared/dist/credit-guidance.js.map +1 -0
  43. package/node_modules/@oxygen/shared/dist/file-import.d.ts +22 -0
  44. package/node_modules/@oxygen/shared/dist/file-import.d.ts.map +1 -0
  45. package/node_modules/@oxygen/shared/dist/file-import.js +232 -0
  46. package/node_modules/@oxygen/shared/dist/file-import.js.map +1 -0
  47. package/node_modules/@oxygen/shared/dist/index.d.ts +45 -0
  48. package/node_modules/@oxygen/shared/dist/index.d.ts.map +1 -0
  49. package/node_modules/@oxygen/shared/dist/index.js +49 -0
  50. package/node_modules/@oxygen/shared/dist/index.js.map +1 -0
  51. package/node_modules/@oxygen/shared/dist/log.d.ts +22 -0
  52. package/node_modules/@oxygen/shared/dist/log.d.ts.map +1 -0
  53. package/node_modules/@oxygen/shared/dist/log.js +58 -0
  54. package/node_modules/@oxygen/shared/dist/log.js.map +1 -0
  55. package/node_modules/@oxygen/shared/dist/redaction.d.ts +4 -0
  56. package/node_modules/@oxygen/shared/dist/redaction.d.ts.map +1 -0
  57. package/node_modules/@oxygen/shared/dist/redaction.js +106 -0
  58. package/node_modules/@oxygen/shared/dist/redaction.js.map +1 -0
  59. package/node_modules/@oxygen/shared/dist/telemetry.d.ts +9 -0
  60. package/node_modules/@oxygen/shared/dist/telemetry.d.ts.map +1 -0
  61. package/node_modules/@oxygen/shared/dist/telemetry.js +98 -0
  62. package/node_modules/@oxygen/shared/dist/telemetry.js.map +1 -0
  63. package/node_modules/@oxygen/shared/dist/version.d.ts +2 -0
  64. package/node_modules/@oxygen/shared/dist/version.d.ts.map +1 -0
  65. package/node_modules/@oxygen/shared/dist/version.js +2 -0
  66. package/node_modules/@oxygen/shared/dist/version.js.map +1 -0
  67. package/node_modules/@oxygen/shared/package.json +21 -0
  68. package/node_modules/@oxygen/workflows/dist/index.d.ts +680 -0
  69. package/node_modules/@oxygen/workflows/dist/index.d.ts.map +1 -0
  70. package/node_modules/@oxygen/workflows/dist/index.js +983 -0
  71. package/node_modules/@oxygen/workflows/dist/index.js.map +1 -0
  72. package/node_modules/@oxygen/workflows/package.json +15 -0
  73. package/package.json +44 -0
@@ -0,0 +1,983 @@
1
+ import { createHash } from "node:crypto";
2
+ import * as vm from "node:vm";
3
+ export const WORKFLOW_MANIFEST_VERSION = 1;
4
+ export const WORKFLOW_COMPILER_VERSION = "oxygen-workflows-v1";
5
+ export const DURABLE_RECIPE_COMPILER_VERSION = "oxygen-recipes-v2";
6
+ export const MAX_RECIPE_BUNDLE_BYTES = 2_000_000;
7
+ export const DEFAULT_WORKFLOW_CRON_TIMEZONE = "UTC";
8
+ // Compatibility and determinism lint only. The Vercel sandbox process,
9
+ // denied network policy, and runtime global guards are the security boundary.
10
+ const UNSAFE_RECIPE_BUNDLE_PATTERNS = [
11
+ { token: "globalThis", pattern: /\bglobalThis\b/ },
12
+ { token: "global", pattern: /\bglobal\b/ },
13
+ { token: "process", pattern: /\bprocess\b/ },
14
+ { token: "fetch", pattern: /\bfetch\b/ },
15
+ { token: "XMLHttpRequest", pattern: /\bXMLHttpRequest\b/ },
16
+ { token: "WebSocket", pattern: /\bWebSocket\b/ },
17
+ { token: "EventSource", pattern: /\bEventSource\b/ },
18
+ { token: "require", pattern: /\brequire\b/ },
19
+ { token: "dynamic import", pattern: /\bimport(?:\s|\/\*[\s\S]*?\*\/)*\(/ },
20
+ { token: "node module import", pattern: /\b(?:node:)?(?:fs|child_process|net|tls|http|https|dns|dgram|worker_threads)\b/ },
21
+ { token: "eval", pattern: /\beval\b/ },
22
+ { token: "Function", pattern: /\bFunction\b/ },
23
+ { token: "constructor", pattern: /\bconstructor\b/ },
24
+ { token: "Date.now", pattern: /\bDate\s*\.\s*now\b/ },
25
+ { token: "new Date", pattern: /\bnew\s+Date\b/ },
26
+ { token: "Math.random", pattern: /\bMath\s*\.\s*random\b/ },
27
+ { token: "crypto.randomUUID", pattern: /\bcrypto\s*\.\s*randomUUID\b/ },
28
+ { token: "setTimeout", pattern: /\bsetTimeout\b/ },
29
+ { token: "setInterval", pattern: /\bsetInterval\b/ },
30
+ ];
31
+ export function defineWorkflow(input) {
32
+ return {
33
+ __oxygen_workflow_definition: true,
34
+ id: input.id,
35
+ name: input.name,
36
+ ...(input.status ? { status: input.status } : {}),
37
+ ...(input.specification ? { specification: input.specification } : {}),
38
+ ...(input.trigger ? { trigger: input.trigger } : {}),
39
+ ...(input.inputSchema ? { inputSchema: input.inputSchema } : {}),
40
+ steps: input.steps,
41
+ };
42
+ }
43
+ export function apiTrigger(input = {}) {
44
+ return {
45
+ type: "api",
46
+ ...(input.status ? { status: input.status } : {}),
47
+ };
48
+ }
49
+ export function webhookTrigger(input) {
50
+ return {
51
+ type: "webhook",
52
+ trigger_id: input.id,
53
+ ...(input.name !== undefined ? { trigger_name: input.name } : {}),
54
+ secret_required: input.secret ?? true,
55
+ ...(input.idempotencyKeyPath !== undefined
56
+ ? { idempotency_key_path: input.idempotencyKeyPath }
57
+ : {}),
58
+ ...(input.status ? { status: input.status } : {}),
59
+ };
60
+ }
61
+ export function cronTrigger(input) {
62
+ return {
63
+ type: "cron",
64
+ cron: input.cron,
65
+ ...(input.timezone !== undefined ? { timezone: input.timezone } : {}),
66
+ ...(input.status ? { status: input.status } : {}),
67
+ };
68
+ }
69
+ export function eventTrigger(input) {
70
+ return {
71
+ type: "event",
72
+ source: input.source,
73
+ event: input.event,
74
+ ...(input.filters ? { filters: input.filters } : {}),
75
+ ...(input.idempotencyKeyPath !== undefined
76
+ ? { idempotency_key_path: input.idempotencyKeyPath }
77
+ : {}),
78
+ ...(input.status ? { status: input.status } : {}),
79
+ };
80
+ }
81
+ export function nextCronRunAfter(input) {
82
+ const schedule = parseCronSchedule(input.cron);
83
+ const timezone = input.timezone?.trim() || DEFAULT_WORKFLOW_CRON_TIMEZONE;
84
+ let candidate = roundUpToNextMinute(input.after ?? new Date());
85
+ const deadline = candidate.getTime() + 370 * 24 * 60 * 60 * 1000;
86
+ while (candidate.getTime() <= deadline) {
87
+ if (matchesCronSchedule(schedule, zonedDateParts(candidate, timezone))) {
88
+ return candidate;
89
+ }
90
+ candidate = new Date(candidate.getTime() + 60_000);
91
+ }
92
+ throw new Error("Cron expression has no matching run time in the next 370 days.");
93
+ }
94
+ export function transformStep(input) {
95
+ return {
96
+ __oxygen_workflow_step: true,
97
+ kind: "transform",
98
+ id: input.id,
99
+ ...(input.description ? { description: input.description } : {}),
100
+ run: input.run,
101
+ };
102
+ }
103
+ export function toolStep(input) {
104
+ return {
105
+ __oxygen_workflow_step: true,
106
+ kind: "tool",
107
+ id: input.id,
108
+ ...(input.description ? { description: input.description } : {}),
109
+ tool: input.tool,
110
+ ...(input.effect ? { effect: input.effect } : {}),
111
+ ...(input.mode ? { mode: input.mode } : {}),
112
+ payload: input.payload,
113
+ };
114
+ }
115
+ export function isWorkflowDefinition(value) {
116
+ return isRecord(value) && value.__oxygen_workflow_definition === true;
117
+ }
118
+ export function isWorkflowManifest(value) {
119
+ return isRecord(value)
120
+ && value.manifest_version === WORKFLOW_MANIFEST_VERSION
121
+ && Array.isArray(value.steps);
122
+ }
123
+ export function isRecipeManifest(value) {
124
+ return isRecord(value)
125
+ && value.manifest_version === WORKFLOW_MANIFEST_VERSION
126
+ && value.compiler_version === DURABLE_RECIPE_COMPILER_VERSION;
127
+ }
128
+ export function isDurableRecipeManifest(value) {
129
+ return isRecipeManifest(value);
130
+ }
131
+ export function isAnyWorkflowManifest(value) {
132
+ return isWorkflowManifest(value) || isRecipeManifest(value);
133
+ }
134
+ export function compileWorkflowDefinition(definition, options = {}) {
135
+ const sourceHash = options.sourceHash
136
+ ?? hashWorkflowSource(options.source ?? JSON.stringify(definition, workflowJsonReplacer));
137
+ const manifest = {
138
+ manifest_version: WORKFLOW_MANIFEST_VERSION,
139
+ workflow: {
140
+ id: definition.id,
141
+ name: definition.name,
142
+ ...(definition.status ? { status: definition.status } : {}),
143
+ },
144
+ ...(definition.specification ? { specification: definition.specification } : {}),
145
+ ...(definition.trigger ? { trigger: definition.trigger } : {}),
146
+ ...(definition.inputSchema ? { input_schema: definition.inputSchema } : {}),
147
+ steps: definition.steps.map((step) => {
148
+ if (step.kind === "transform") {
149
+ return {
150
+ kind: "transform",
151
+ id: step.id,
152
+ ...(step.description ? { description: step.description } : {}),
153
+ run_source: serializeWorkflowFunction(step.run, `steps.${step.id}.run`),
154
+ };
155
+ }
156
+ return {
157
+ kind: "tool",
158
+ id: step.id,
159
+ ...(step.description ? { description: step.description } : {}),
160
+ tool: step.tool,
161
+ effect: step.effect ?? "external_read",
162
+ ...(step.mode ? { mode: step.mode } : {}),
163
+ payload_source: serializeWorkflowFunction(step.payload, `steps.${step.id}.payload`),
164
+ };
165
+ }),
166
+ source_hash: sourceHash,
167
+ compiler_version: WORKFLOW_COMPILER_VERSION,
168
+ created_at: (options.createdAt ?? new Date()).toISOString(),
169
+ };
170
+ assertWorkflowManifest(manifest);
171
+ return manifest;
172
+ }
173
+ export function buildRecipeManifest(input) {
174
+ const bundle = input.bundle;
175
+ if (typeof bundle !== "string" || bundle.length === 0) {
176
+ throw new Error("Recipe bundle source is required.");
177
+ }
178
+ const sourceHash = input.sourceHash ?? hashWorkflowSource(bundle);
179
+ const manifest = {
180
+ manifest_version: WORKFLOW_MANIFEST_VERSION,
181
+ workflow: {
182
+ id: input.workflowId,
183
+ name: input.workflowName,
184
+ ...(input.status ? { status: input.status } : {}),
185
+ },
186
+ ...(input.trigger ? { trigger: input.trigger } : {}),
187
+ ...(input.inputSchema ? { input_schema: input.inputSchema } : {}),
188
+ runtime: "durable",
189
+ bundle,
190
+ bundle_format: "esm",
191
+ tools_used: Array.from(new Set((input.toolsUsed ?? []).filter(Boolean))).sort(),
192
+ source_hash: sourceHash,
193
+ compiler_version: DURABLE_RECIPE_COMPILER_VERSION,
194
+ created_at: (input.createdAt ?? new Date()).toISOString(),
195
+ };
196
+ return manifest;
197
+ }
198
+ export function lintWorkflowManifest(value, options = {}) {
199
+ if (isRecord(value)
200
+ && typeof value.compiler_version === "string"
201
+ && value.compiler_version.startsWith("oxygen-recipes-")) {
202
+ return lintRecipeManifest(value, options);
203
+ }
204
+ const issues = [];
205
+ const add = (path, code, message) => issues.push({ path, code, message });
206
+ if (!isRecord(value)) {
207
+ add("$", "invalid_manifest", "Workflow manifest must be an object.");
208
+ return { ok: false, issues };
209
+ }
210
+ if (value.manifest_version !== WORKFLOW_MANIFEST_VERSION) {
211
+ add("$.manifest_version", "invalid_manifest_version", "Workflow manifest version must be 1.");
212
+ }
213
+ const workflow = value.workflow;
214
+ if (!isRecord(workflow)) {
215
+ add("$.workflow", "missing_workflow", "Workflow metadata is required.");
216
+ }
217
+ else {
218
+ if (!isNonEmptyString(workflow.id))
219
+ add("$.workflow.id", "invalid_workflow_id", "Workflow id is required.");
220
+ if (!isNonEmptyString(workflow.name))
221
+ add("$.workflow.name", "invalid_workflow_name", "Workflow name is required.");
222
+ if (workflow.status !== undefined && workflow.status !== "active" && workflow.status !== "disabled") {
223
+ add("$.workflow.status", "invalid_workflow_status", "Workflow status must be active or disabled.");
224
+ }
225
+ }
226
+ if (value.trigger !== undefined)
227
+ validateTrigger(value.trigger, "$.trigger", add);
228
+ if (!Array.isArray(value.steps) || value.steps.length === 0) {
229
+ add("$.steps", "missing_steps", "At least one workflow step is required.");
230
+ }
231
+ else {
232
+ const ids = new Set();
233
+ value.steps.forEach((step, index) => {
234
+ const path = `$.steps.${index}`;
235
+ if (!isRecord(step)) {
236
+ add(path, "invalid_step", "Workflow step must be an object.");
237
+ return;
238
+ }
239
+ if (!isNonEmptyString(step.id)) {
240
+ add(`${path}.id`, "invalid_step_id", "Step id is required.");
241
+ }
242
+ else if (ids.has(step.id)) {
243
+ add(`${path}.id`, "duplicate_step_id", `Step id '${step.id}' is duplicated.`);
244
+ }
245
+ else {
246
+ ids.add(step.id);
247
+ }
248
+ if (step.kind === "transform") {
249
+ if (!isNonEmptyString(step.run_source)) {
250
+ add(`${path}.run_source`, "missing_step_code", "Transform step run_source is required.");
251
+ }
252
+ else {
253
+ validatePureFunctionSource(step.run_source, `${path}.run_source`, add);
254
+ }
255
+ return;
256
+ }
257
+ if (step.kind === "tool") {
258
+ if (!isNonEmptyString(step.tool)) {
259
+ add(`${path}.tool`, "missing_tool", "Tool step tool id is required.");
260
+ }
261
+ else if (isWorkflowToolRejected(step.tool)) {
262
+ add(`${path}.tool`, "unsupported_workflow_tool", `Tool '${step.tool}' is not allowed in workflow automations.`);
263
+ }
264
+ else if (options.isToolAllowed && !options.isToolAllowed(step.tool)) {
265
+ add(`${path}.tool`, "tool_not_allowed", `Tool '${step.tool}' is not available for workflow automations.`);
266
+ }
267
+ if (!isNonEmptyString(step.payload_source)) {
268
+ add(`${path}.payload_source`, "missing_step_code", "Tool step payload_source is required.");
269
+ }
270
+ else {
271
+ validatePureFunctionSource(step.payload_source, `${path}.payload_source`, add);
272
+ }
273
+ if (step.effect !== "none"
274
+ && step.effect !== "external_read"
275
+ && step.effect !== "external_write") {
276
+ add(`${path}.effect`, "invalid_step_effect", "Step effect is invalid.");
277
+ }
278
+ if (step.mode !== undefined
279
+ && step.mode !== "dry_run"
280
+ && step.mode !== "live"
281
+ && step.mode !== "smoke_test") {
282
+ add(`${path}.mode`, "invalid_step_mode", "Step mode is invalid.");
283
+ }
284
+ return;
285
+ }
286
+ add(`${path}.kind`, "invalid_step_kind", "Step kind must be transform or tool.");
287
+ });
288
+ }
289
+ if (!isNonEmptyString(value.source_hash)) {
290
+ add("$.source_hash", "missing_source_hash", "Manifest source_hash is required.");
291
+ }
292
+ if (!isNonEmptyString(value.compiler_version)) {
293
+ add("$.compiler_version", "missing_compiler_version", "Manifest compiler_version is required.");
294
+ }
295
+ return { ok: issues.length === 0, issues };
296
+ }
297
+ export function lintRecipeManifest(value, options = {}) {
298
+ const issues = [];
299
+ const add = (path, code, message) => issues.push({ path, code, message });
300
+ if (!isRecord(value)) {
301
+ add("$", "invalid_manifest", "Recipe manifest must be an object.");
302
+ return { ok: false, issues };
303
+ }
304
+ if (value.manifest_version !== WORKFLOW_MANIFEST_VERSION) {
305
+ add("$.manifest_version", "invalid_manifest_version", "Recipe manifest version must be 1.");
306
+ }
307
+ if (value.compiler_version !== DURABLE_RECIPE_COMPILER_VERSION) {
308
+ add("$.compiler_version", "invalid_compiler_version", `Recipe manifest compiler_version must be '${DURABLE_RECIPE_COMPILER_VERSION}'.`);
309
+ }
310
+ const workflow = value.workflow;
311
+ if (!isRecord(workflow)) {
312
+ add("$.workflow", "missing_workflow", "Recipe metadata is required.");
313
+ }
314
+ else {
315
+ if (!isNonEmptyString(workflow.id))
316
+ add("$.workflow.id", "invalid_workflow_id", "Recipe id is required.");
317
+ if (!isNonEmptyString(workflow.name))
318
+ add("$.workflow.name", "invalid_workflow_name", "Recipe name is required.");
319
+ if (workflow.status !== undefined && workflow.status !== "active" && workflow.status !== "disabled") {
320
+ add("$.workflow.status", "invalid_workflow_status", "Recipe status must be active or disabled.");
321
+ }
322
+ }
323
+ if (value.trigger !== undefined)
324
+ validateTrigger(value.trigger, "$.trigger", add);
325
+ if (!isNonEmptyString(value.bundle)) {
326
+ add("$.bundle", "missing_bundle", "Recipe bundle is required.");
327
+ }
328
+ else if (Buffer.byteLength(value.bundle, "utf8") > MAX_RECIPE_BUNDLE_BYTES) {
329
+ add("$.bundle", "bundle_too_large", "Recipe bundle exceeds the maximum allowed size.");
330
+ }
331
+ else {
332
+ validateRecipeBundleSafety(value.bundle, "$.bundle", add);
333
+ }
334
+ if (value.bundle_format !== "esm") {
335
+ add("$.bundle_format", "invalid_bundle_format", "Recipe bundle_format must be 'esm'.");
336
+ }
337
+ if (value.runtime !== "durable") {
338
+ add("$.runtime", "invalid_recipe_runtime", "Durable recipe manifests must set runtime to durable.");
339
+ }
340
+ if (Array.isArray(value.tools_used)) {
341
+ if (value.tools_used.length === 0) {
342
+ add("$.tools_used", "missing_recipe_tools", "Durable recipes must declare at least one allowed tool.");
343
+ }
344
+ value.tools_used.forEach((toolId, index) => {
345
+ if (typeof toolId !== "string" || !toolId.trim()) {
346
+ add(`$.tools_used.${index}`, "invalid_tool_id", "Recipe tools_used entries must be non-empty strings.");
347
+ }
348
+ else if (isWorkflowToolRejected(toolId)) {
349
+ add(`$.tools_used.${index}`, "unsupported_workflow_tool", `Tool '${toolId}' is not allowed in durable recipes.`);
350
+ }
351
+ else if (options.isToolAllowed && !options.isToolAllowed(toolId)) {
352
+ add(`$.tools_used.${index}`, "tool_not_allowed", `Tool '${toolId}' is not available for durable recipes.`);
353
+ }
354
+ });
355
+ }
356
+ else if (value.tools_used !== undefined) {
357
+ add("$.tools_used", "invalid_tools_used", "Recipe tools_used must be an array of tool ids.");
358
+ }
359
+ else {
360
+ add("$.tools_used", "missing_recipe_tools", "Durable recipes must declare at least one allowed tool.");
361
+ }
362
+ if (!isNonEmptyString(value.source_hash)) {
363
+ add("$.source_hash", "missing_source_hash", "Recipe manifest source_hash is required.");
364
+ }
365
+ return { ok: issues.length === 0, issues };
366
+ }
367
+ export function assertRecipeManifest(value, options = {}) {
368
+ const result = lintRecipeManifest(value, options);
369
+ if (result.ok)
370
+ return;
371
+ const first = result.issues[0];
372
+ throw new Error(first ? `${first.code}: ${first.message}` : "Invalid recipe manifest.");
373
+ }
374
+ export function assertWorkflowManifest(value, options = {}) {
375
+ const result = lintWorkflowManifest(value, options);
376
+ if (result.ok)
377
+ return;
378
+ const first = result.issues[0];
379
+ throw new Error(first ? `${first.code}: ${first.message}` : "Invalid workflow manifest.");
380
+ }
381
+ export function validateJsonSchemaValue(value, schema, path = "$") {
382
+ if (!schema || typeof schema !== "object" || Array.isArray(schema))
383
+ return [];
384
+ const issues = [];
385
+ validateJsonSchemaValueInto(value, schema, path, issues);
386
+ return issues;
387
+ }
388
+ export function assertRecipeBundleSafe(bundle) {
389
+ const issues = lintRecipeBundleSafety(bundle, "$.bundle");
390
+ const first = issues[0];
391
+ if (first)
392
+ throw new Error(`${first.code}: ${first.message}`);
393
+ }
394
+ export function lintRecipeBundleSafety(bundle, path = "$.bundle") {
395
+ const issues = [];
396
+ validateRecipeBundleSafety(bundle, path, (issuePath, code, message) => {
397
+ issues.push({ path: issuePath, code, message });
398
+ });
399
+ return issues;
400
+ }
401
+ export async function runPureWorkflowFunction(input) {
402
+ const timeoutMs = Math.max(10, Math.min(input.timeoutMs ?? 1_000, 10_000));
403
+ const maxOutputBytes = Math.max(1_000, Math.min(input.maxOutputBytes ?? 250_000, 2_000_000));
404
+ const issues = [];
405
+ validatePureFunctionSource(input.source, "$.source", (path, code, message) => {
406
+ issues.push({ path, code, message });
407
+ });
408
+ if (issues.length > 0) {
409
+ const first = issues[0];
410
+ throw new Error(first ? `${first.code}: ${first.message}` : "Invalid workflow function source.");
411
+ }
412
+ const context = toJsonValue(input.context, "context");
413
+ const sandbox = Object.create(null);
414
+ sandbox.__oxygen_context = context;
415
+ const script = new vm.Script(`"use strict";\nconst __oxygen_fn = (${input.source});\n__oxygen_fn(__oxygen_context);`);
416
+ const result = script.runInNewContext(sandbox, { timeout: timeoutMs });
417
+ const resolved = isPromiseLike(result)
418
+ ? await withTimeout(result, timeoutMs)
419
+ : result;
420
+ return enforceJsonOutput(resolved, maxOutputBytes);
421
+ }
422
+ export function getWorkflowApplySchema() {
423
+ return workflowApplySchema;
424
+ }
425
+ export function getWorkflowSchema(subject = "apply") {
426
+ const schemas = {
427
+ apply: workflowApplySchema,
428
+ call: workflowCallSchema,
429
+ event: workflowEventEmitSchema,
430
+ trigger: workflowTriggerSchema,
431
+ manifest: workflowManifestSchema,
432
+ };
433
+ if (subject === "all")
434
+ return { schemas };
435
+ return schemas[subject];
436
+ }
437
+ export const workflowApplySchema = {
438
+ $schema: "https://json-schema.org/draft/2020-12/schema",
439
+ title: "OXYGEN Workflow Apply Input",
440
+ type: "object",
441
+ additionalProperties: false,
442
+ properties: {
443
+ manifest: { $ref: "#/$defs/manifest" },
444
+ },
445
+ required: ["manifest"],
446
+ $defs: {
447
+ manifest: { type: "object", additionalProperties: true },
448
+ },
449
+ };
450
+ export const workflowCallSchema = {
451
+ $schema: "https://json-schema.org/draft/2020-12/schema",
452
+ title: "OXYGEN Workflow Call Input",
453
+ type: "object",
454
+ additionalProperties: false,
455
+ properties: {
456
+ workflow_id: { type: "string" },
457
+ workflow_name: { type: "string" },
458
+ input: { type: "object", additionalProperties: true },
459
+ mode: { enum: ["dry_run", "live", "smoke_test"] },
460
+ idempotency_key: { type: "string" },
461
+ },
462
+ };
463
+ export const workflowEventEmitSchema = {
464
+ $schema: "https://json-schema.org/draft/2020-12/schema",
465
+ title: "OXYGEN Workflow Event Emit Input",
466
+ type: "object",
467
+ additionalProperties: false,
468
+ properties: {
469
+ source: { type: "string" },
470
+ event: { type: "string" },
471
+ payload: { type: "object", additionalProperties: true },
472
+ raw_payload: { type: "object", additionalProperties: true },
473
+ headers: { type: "object", additionalProperties: true },
474
+ external_event_id: { type: "string" },
475
+ idempotency_key: { type: "string" },
476
+ mode: { enum: ["dry_run", "live", "smoke_test"] },
477
+ },
478
+ required: ["source", "event", "payload"],
479
+ };
480
+ export const workflowTriggerSchema = {
481
+ $schema: "https://json-schema.org/draft/2020-12/schema",
482
+ title: "OXYGEN Workflow Trigger",
483
+ type: "object",
484
+ additionalProperties: false,
485
+ properties: {
486
+ type: { enum: ["api", "webhook", "cron"] },
487
+ trigger_id: { type: "string" },
488
+ trigger_name: { type: ["string", "null"] },
489
+ secret_required: { type: "boolean" },
490
+ idempotency_key_path: { type: ["string", "null"] },
491
+ cron: { type: "string" },
492
+ timezone: { type: ["string", "null"] },
493
+ source: { type: "string" },
494
+ event: { type: "string" },
495
+ filters: {
496
+ type: "array",
497
+ items: {
498
+ type: "object",
499
+ additionalProperties: false,
500
+ properties: {
501
+ path: { type: "string" },
502
+ op: { enum: ["eq", "neq", "exists", "not_exists"] },
503
+ value: {},
504
+ },
505
+ required: ["path", "op"],
506
+ },
507
+ },
508
+ status: { enum: ["active", "disabled"] },
509
+ },
510
+ required: ["type"],
511
+ };
512
+ export const workflowManifestSchema = {
513
+ $schema: "https://json-schema.org/draft/2020-12/schema",
514
+ title: "OXYGEN Workflow Manifest",
515
+ type: "object",
516
+ additionalProperties: true,
517
+ required: ["manifest_version", "workflow", "steps", "source_hash", "compiler_version"],
518
+ };
519
+ function validateJsonSchemaValueInto(value, schema, path, issues) {
520
+ const startCount = issues.length;
521
+ if (schema.const !== undefined && !jsonEqual(value, schema.const)) {
522
+ issues.push({
523
+ path,
524
+ code: "schema_const_mismatch",
525
+ message: `${path} must equal the schema const value.`,
526
+ });
527
+ return;
528
+ }
529
+ if (Array.isArray(schema.enum) && schema.enum.length > 0 && !schema.enum.some((entry) => jsonEqual(value, entry))) {
530
+ issues.push({
531
+ path,
532
+ code: "schema_enum_mismatch",
533
+ message: `${path} must be one of the allowed values.`,
534
+ });
535
+ return;
536
+ }
537
+ if (Array.isArray(schema.anyOf) && schema.anyOf.length > 0) {
538
+ const matched = schema.anyOf.some((candidate) => {
539
+ const candidateIssues = [];
540
+ validateJsonSchemaValueInto(value, candidate, path, candidateIssues);
541
+ return candidateIssues.length === 0;
542
+ });
543
+ if (!matched) {
544
+ issues.push({
545
+ path,
546
+ code: "schema_any_of_mismatch",
547
+ message: `${path} must match at least one allowed schema.`,
548
+ });
549
+ }
550
+ return;
551
+ }
552
+ const expectedTypes = normalizeSchemaTypes(schema);
553
+ if (expectedTypes.length > 0 && !expectedTypes.some((type) => matchesJsonSchemaType(value, type))) {
554
+ issues.push({
555
+ path,
556
+ code: "schema_type_mismatch",
557
+ message: `${path} must be ${formatSchemaTypes(expectedTypes)}.`,
558
+ });
559
+ return;
560
+ }
561
+ const shouldValidateObject = expectedTypes.includes("object")
562
+ || Boolean(schema.properties)
563
+ || Array.isArray(schema.required)
564
+ || schema.additionalProperties !== undefined;
565
+ if (shouldValidateObject && isRecord(value)) {
566
+ const properties = schema.properties ?? {};
567
+ for (const requiredKey of schema.required ?? []) {
568
+ if (!Object.hasOwn(value, requiredKey) || value[requiredKey] === undefined) {
569
+ issues.push({
570
+ path: `${path}.${requiredKey}`,
571
+ code: "schema_required",
572
+ message: `${path}.${requiredKey} is required.`,
573
+ });
574
+ }
575
+ }
576
+ for (const [key, propertyValue] of Object.entries(value)) {
577
+ const propertySchema = properties[key];
578
+ if (propertySchema) {
579
+ validateJsonSchemaValueInto(propertyValue, propertySchema, `${path}.${key}`, issues);
580
+ }
581
+ else if (schema.additionalProperties === false) {
582
+ issues.push({
583
+ path: `${path}.${key}`,
584
+ code: "schema_additional_property",
585
+ message: `${path}.${key} is not allowed.`,
586
+ });
587
+ }
588
+ else if (isRecord(schema.additionalProperties)) {
589
+ validateJsonSchemaValueInto(propertyValue, schema.additionalProperties, `${path}.${key}`, issues);
590
+ }
591
+ }
592
+ }
593
+ const shouldValidateArray = expectedTypes.includes("array") || Boolean(schema.items);
594
+ if (shouldValidateArray && Array.isArray(value) && schema.items) {
595
+ value.forEach((entry, index) => {
596
+ validateJsonSchemaValueInto(entry, schema.items, `${path}.${index}`, issues);
597
+ });
598
+ }
599
+ if (issues.length > startCount)
600
+ return;
601
+ }
602
+ function normalizeSchemaTypes(schema) {
603
+ if (Array.isArray(schema.type))
604
+ return schema.type.filter((entry) => typeof entry === "string");
605
+ if (typeof schema.type === "string")
606
+ return [schema.type];
607
+ if (schema.properties || schema.required || schema.additionalProperties !== undefined)
608
+ return ["object"];
609
+ if (schema.items)
610
+ return ["array"];
611
+ return [];
612
+ }
613
+ function matchesJsonSchemaType(value, type) {
614
+ switch (type) {
615
+ case "array":
616
+ return Array.isArray(value);
617
+ case "boolean":
618
+ return typeof value === "boolean";
619
+ case "integer":
620
+ return typeof value === "number" && Number.isInteger(value);
621
+ case "null":
622
+ return value === null;
623
+ case "number":
624
+ return typeof value === "number" && Number.isFinite(value);
625
+ case "object":
626
+ return isRecord(value);
627
+ case "string":
628
+ return typeof value === "string";
629
+ default:
630
+ return true;
631
+ }
632
+ }
633
+ function formatSchemaTypes(types) {
634
+ if (types.length === 1)
635
+ return `a ${types[0]}`;
636
+ return types.map((type) => type === "null" ? "null" : `a ${type}`).join(" or ");
637
+ }
638
+ function jsonEqual(left, right) {
639
+ return JSON.stringify(left) === JSON.stringify(right);
640
+ }
641
+ function validateTrigger(value, path, add) {
642
+ if (!isRecord(value)) {
643
+ add(path, "invalid_trigger", "Workflow trigger must be an object.");
644
+ return;
645
+ }
646
+ if (value.status !== undefined && value.status !== "active" && value.status !== "disabled") {
647
+ add(`${path}.status`, "invalid_trigger_status", "Trigger status must be active or disabled.");
648
+ }
649
+ if (value.type === "api")
650
+ return;
651
+ if (value.type === "webhook") {
652
+ if (!isNonEmptyString(value.trigger_id)) {
653
+ add(`${path}.trigger_id`, "invalid_trigger_id", "Webhook trigger_id is required.");
654
+ }
655
+ if (value.idempotency_key_path !== undefined
656
+ && value.idempotency_key_path !== null
657
+ && !isNonEmptyString(value.idempotency_key_path)) {
658
+ add(`${path}.idempotency_key_path`, "invalid_idempotency_key_path", "Webhook idempotency path is invalid.");
659
+ }
660
+ return;
661
+ }
662
+ if (value.type === "cron") {
663
+ if (!isNonEmptyString(value.cron)) {
664
+ add(`${path}.cron`, "invalid_cron", "Cron trigger cron expression is required.");
665
+ }
666
+ else {
667
+ try {
668
+ parseCronSchedule(value.cron);
669
+ }
670
+ catch (error) {
671
+ add(`${path}.cron`, "invalid_cron", error instanceof Error ? error.message : "Cron expression is invalid.");
672
+ }
673
+ }
674
+ return;
675
+ }
676
+ if (value.type === "event") {
677
+ if (!isNonEmptyString(value.source)) {
678
+ add(`${path}.source`, "invalid_event_source", "Event trigger source is required.");
679
+ }
680
+ if (!isNonEmptyString(value.event)) {
681
+ add(`${path}.event`, "invalid_event_type", "Event trigger event is required.");
682
+ }
683
+ if (value.idempotency_key_path !== undefined
684
+ && value.idempotency_key_path !== null
685
+ && !isNonEmptyString(value.idempotency_key_path)) {
686
+ add(`${path}.idempotency_key_path`, "invalid_idempotency_key_path", "Event idempotency path is invalid.");
687
+ }
688
+ if (value.filters !== undefined)
689
+ validateEventFilters(value.filters, `${path}.filters`, add);
690
+ return;
691
+ }
692
+ add(`${path}.type`, "invalid_trigger_type", "Trigger type must be api, webhook, cron, or event.");
693
+ }
694
+ function validateEventFilters(value, path, add) {
695
+ if (!Array.isArray(value)) {
696
+ add(path, "invalid_event_filters", "Event trigger filters must be an array.");
697
+ return;
698
+ }
699
+ value.forEach((filter, index) => {
700
+ const filterPath = `${path}.${index}`;
701
+ if (!isRecord(filter)) {
702
+ add(filterPath, "invalid_event_filter", "Event trigger filter must be an object.");
703
+ return;
704
+ }
705
+ if (!isNonEmptyString(filter.path)) {
706
+ add(`${filterPath}.path`, "invalid_event_filter_path", "Event filter path is required.");
707
+ }
708
+ if (filter.op !== "eq"
709
+ && filter.op !== "neq"
710
+ && filter.op !== "exists"
711
+ && filter.op !== "not_exists") {
712
+ add(`${filterPath}.op`, "invalid_event_filter_op", "Event filter op is invalid.");
713
+ }
714
+ if ((filter.op === "eq" || filter.op === "neq") && !Object.hasOwn(filter, "value")) {
715
+ add(`${filterPath}.value`, "missing_event_filter_value", "Event eq/neq filters require a value.");
716
+ }
717
+ });
718
+ }
719
+ function serializeWorkflowFunction(fn, path) {
720
+ if (typeof fn !== "function") {
721
+ throw new Error(`${path} must be a function.`);
722
+ }
723
+ const source = Function.prototype.toString.call(fn);
724
+ const issues = [];
725
+ validatePureFunctionSource(source, path, (issuePath, code, message) => {
726
+ issues.push({ path: issuePath, code, message });
727
+ });
728
+ if (issues.length > 0) {
729
+ const first = issues[0];
730
+ throw new Error(first ? `${first.code}: ${first.message}` : `${path} is invalid.`);
731
+ }
732
+ return source;
733
+ }
734
+ function validatePureFunctionSource(source, path, add) {
735
+ if (!source.trim()) {
736
+ add(path, "empty_function_source", "Workflow function source cannot be empty.");
737
+ return;
738
+ }
739
+ const forbidden = [
740
+ "import",
741
+ "require",
742
+ "process",
743
+ "global",
744
+ "globalThis",
745
+ "fetch",
746
+ "XMLHttpRequest",
747
+ "Function",
748
+ "eval",
749
+ "constructor",
750
+ "setTimeout",
751
+ "setInterval",
752
+ ];
753
+ for (const token of forbidden) {
754
+ const pattern = new RegExp(`\\b${escapeRegExp(token)}\\b`);
755
+ if (pattern.test(source)) {
756
+ add(path, "impure_workflow_function", `Workflow function source cannot reference '${token}'.`);
757
+ return;
758
+ }
759
+ }
760
+ }
761
+ function validateRecipeBundleSafety(source, path, add) {
762
+ if (!source.trim()) {
763
+ add(path, "empty_recipe_bundle", "Recipe bundle cannot be empty.");
764
+ return;
765
+ }
766
+ for (const entry of UNSAFE_RECIPE_BUNDLE_PATTERNS) {
767
+ if (entry.pattern.test(source)) {
768
+ add(path, "unsafe_recipe_bundle", `Durable recipe bundle cannot reference '${entry.token}'. Use ctx.tools.run(), ctx.step(), ctx.now(), or ctx.uuid() instead.`);
769
+ return;
770
+ }
771
+ }
772
+ }
773
+ function isWorkflowToolRejected(toolId) {
774
+ return toolId === "run_javascript" || toolId === "custom_http" || toolId.includes("custom_http");
775
+ }
776
+ const CRON_FIELD_LIMITS = [
777
+ { name: "minute", min: 0, max: 59 },
778
+ { name: "hour", min: 0, max: 23 },
779
+ { name: "day-of-month", min: 1, max: 31 },
780
+ { name: "month", min: 1, max: 12 },
781
+ { name: "day-of-week", min: 0, max: 7 },
782
+ ];
783
+ const zonedFormatters = new Map();
784
+ function parseCronSchedule(value) {
785
+ const parts = value.trim().split(/\s+/);
786
+ if (parts.length !== 5) {
787
+ throw new Error("Cron expression must have five space-separated parts.");
788
+ }
789
+ const [minute, hour, dayOfMonth, month, dayOfWeek] = parts;
790
+ if (!minute || !hour || !dayOfMonth || !month || !dayOfWeek) {
791
+ throw new Error("Cron expression must have five space-separated parts.");
792
+ }
793
+ const minutes = parseCronField(minute, CRON_FIELD_LIMITS[0]);
794
+ const hours = parseCronField(hour, CRON_FIELD_LIMITS[1]);
795
+ const daysOfMonth = parseCronField(dayOfMonth, CRON_FIELD_LIMITS[2]);
796
+ const months = parseCronField(month, CRON_FIELD_LIMITS[3]);
797
+ const daysOfWeek = normalizeDaysOfWeek(parseCronField(dayOfWeek, CRON_FIELD_LIMITS[4]));
798
+ return {
799
+ minutes,
800
+ hours,
801
+ daysOfMonth,
802
+ months,
803
+ daysOfWeek,
804
+ dayOfMonthRestricted: dayOfMonth !== "*",
805
+ dayOfWeekRestricted: dayOfWeek !== "*",
806
+ };
807
+ }
808
+ function parseCronField(expression, limits) {
809
+ const values = new Set();
810
+ for (const rawToken of expression.split(",")) {
811
+ const token = rawToken.trim();
812
+ if (!token)
813
+ throw new Error(`Cron ${limits.name} field contains an empty item.`);
814
+ const parts = token.split("/");
815
+ const rangePart = parts[0];
816
+ const stepPart = parts[1];
817
+ if (!rangePart || parts.length > 2) {
818
+ throw new Error(`Cron ${limits.name} field is invalid.`);
819
+ }
820
+ const step = stepPart === undefined ? 1 : Number(stepPart);
821
+ if (!Number.isInteger(step) || step < 1) {
822
+ throw new Error(`Cron ${limits.name} step must be a positive integer.`);
823
+ }
824
+ const [start, end] = parseCronRange(rangePart, limits);
825
+ for (let current = start; current <= end; current += step) {
826
+ values.add(current);
827
+ }
828
+ }
829
+ if (values.size === 0)
830
+ throw new Error(`Cron ${limits.name} field is empty.`);
831
+ return values;
832
+ }
833
+ function parseCronRange(value, limits) {
834
+ if (value === "*")
835
+ return [limits.min, limits.max];
836
+ const parts = value.split("-");
837
+ const startRaw = parts[0];
838
+ const endRaw = parts[1];
839
+ if (!startRaw || parts.length > 2) {
840
+ throw new Error(`Cron ${limits.name} range is invalid.`);
841
+ }
842
+ const start = Number(startRaw);
843
+ const end = endRaw === undefined ? start : Number(endRaw);
844
+ if (!Number.isInteger(start) || !Number.isInteger(end)) {
845
+ throw new Error(`Cron ${limits.name} values must be integers.`);
846
+ }
847
+ if (start < limits.min || end > limits.max || start > end) {
848
+ throw new Error(`Cron ${limits.name} values must be between ${limits.min} and ${limits.max}.`);
849
+ }
850
+ return [start, end];
851
+ }
852
+ function normalizeDaysOfWeek(values) {
853
+ const normalized = new Set();
854
+ for (const value of values) {
855
+ normalized.add(value === 7 ? 0 : value);
856
+ }
857
+ return normalized;
858
+ }
859
+ function matchesCronSchedule(schedule, parts) {
860
+ if (!schedule.minutes.has(parts.minute))
861
+ return false;
862
+ if (!schedule.hours.has(parts.hour))
863
+ return false;
864
+ if (!schedule.months.has(parts.month))
865
+ return false;
866
+ const dayOfMonthMatches = schedule.daysOfMonth.has(parts.dayOfMonth);
867
+ const dayOfWeekMatches = schedule.daysOfWeek.has(parts.dayOfWeek);
868
+ if (schedule.dayOfMonthRestricted && schedule.dayOfWeekRestricted) {
869
+ return dayOfMonthMatches || dayOfWeekMatches;
870
+ }
871
+ return dayOfMonthMatches && dayOfWeekMatches;
872
+ }
873
+ function roundUpToNextMinute(value) {
874
+ const result = new Date(value.getTime());
875
+ result.setUTCSeconds(0, 0);
876
+ if (result.getTime() <= value.getTime()) {
877
+ result.setUTCMinutes(result.getUTCMinutes() + 1);
878
+ }
879
+ return result;
880
+ }
881
+ function zonedDateParts(date, timezone) {
882
+ const formatter = getZonedFormatter(timezone);
883
+ const parts = new Map();
884
+ for (const part of formatter.formatToParts(date)) {
885
+ if (part.type !== "literal")
886
+ parts.set(part.type, part.value);
887
+ }
888
+ return {
889
+ minute: Number(parts.get("minute")),
890
+ hour: Number(parts.get("hour")),
891
+ dayOfMonth: Number(parts.get("day")),
892
+ month: Number(parts.get("month")),
893
+ dayOfWeek: weekdayToNumber(parts.get("weekday") ?? ""),
894
+ };
895
+ }
896
+ function getZonedFormatter(timezone) {
897
+ const cached = zonedFormatters.get(timezone);
898
+ if (cached)
899
+ return cached;
900
+ const formatter = new Intl.DateTimeFormat("en-US", {
901
+ timeZone: timezone,
902
+ weekday: "short",
903
+ year: "numeric",
904
+ month: "numeric",
905
+ day: "numeric",
906
+ hour: "numeric",
907
+ minute: "numeric",
908
+ hourCycle: "h23",
909
+ });
910
+ zonedFormatters.set(timezone, formatter);
911
+ return formatter;
912
+ }
913
+ function weekdayToNumber(value) {
914
+ switch (value.toLowerCase()) {
915
+ case "sun":
916
+ return 0;
917
+ case "mon":
918
+ return 1;
919
+ case "tue":
920
+ return 2;
921
+ case "wed":
922
+ return 3;
923
+ case "thu":
924
+ return 4;
925
+ case "fri":
926
+ return 5;
927
+ case "sat":
928
+ return 6;
929
+ default:
930
+ throw new Error(`Unsupported weekday '${value}'.`);
931
+ }
932
+ }
933
+ function hashWorkflowSource(source) {
934
+ return createHash("sha256").update(source).digest("hex");
935
+ }
936
+ function workflowJsonReplacer(_key, value) {
937
+ return typeof value === "function" ? Function.prototype.toString.call(value) : value;
938
+ }
939
+ function enforceJsonOutput(value, maxOutputBytes) {
940
+ const json = JSON.stringify(value);
941
+ if (json === undefined)
942
+ return null;
943
+ if (Buffer.byteLength(json) > maxOutputBytes) {
944
+ throw new Error("workflow_output_too_large: Workflow function output is too large.");
945
+ }
946
+ return JSON.parse(json);
947
+ }
948
+ function toJsonValue(value, label) {
949
+ const json = JSON.stringify(value);
950
+ if (json === undefined) {
951
+ throw new Error(`${label} must be JSON serializable.`);
952
+ }
953
+ return JSON.parse(json);
954
+ }
955
+ function isPromiseLike(value) {
956
+ return value !== null && typeof value === "object" && "then" in value
957
+ && typeof value.then === "function";
958
+ }
959
+ async function withTimeout(promise, timeoutMs) {
960
+ let timer = null;
961
+ try {
962
+ return await Promise.race([
963
+ promise,
964
+ new Promise((_, reject) => {
965
+ timer = setTimeout(() => reject(new Error("workflow_function_timeout: Workflow function timed out.")), timeoutMs);
966
+ }),
967
+ ]);
968
+ }
969
+ finally {
970
+ if (timer)
971
+ clearTimeout(timer);
972
+ }
973
+ }
974
+ function isRecord(value) {
975
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
976
+ }
977
+ function isNonEmptyString(value) {
978
+ return typeof value === "string" && value.trim().length > 0;
979
+ }
980
+ function escapeRegExp(value) {
981
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
982
+ }
983
+ //# sourceMappingURL=index.js.map