woml-cli 1.0.1
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/LICENSE +88 -0
- package/NOTICE.md +20 -0
- package/README.md +124 -0
- package/dist/cli.js +36429 -0
- package/dist/cli.js.map +177 -0
- package/dist/custom-notification-provider-host.js +7801 -0
- package/dist/custom-notification-provider-host.js.map +97 -0
- package/dist/custom-notification-provider-worker.js +172 -0
- package/dist/custom-notification-provider-worker.js.map +10 -0
- package/dist/notification-provider-host.js +27015 -0
- package/dist/notification-provider-host.js.map +160 -0
- package/dist/script-host-worker.js +1616 -0
- package/dist/script-host-worker.js.map +11 -0
- package/dist/script-host.js +9346 -0
- package/dist/script-host.js.map +93 -0
- package/package.json +53 -0
- package/slack/README.md +132 -0
- package/slack/manifest.json +39 -0
|
@@ -0,0 +1,1616 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
function __accessProp(key) {
|
|
8
|
+
return this[key];
|
|
9
|
+
}
|
|
10
|
+
var __toESMCache_node;
|
|
11
|
+
var __toESMCache_esm;
|
|
12
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
+
var canCache = mod != null && typeof mod === "object";
|
|
14
|
+
if (canCache) {
|
|
15
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
+
var cached = cache.get(mod);
|
|
17
|
+
if (cached)
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
20
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
21
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
22
|
+
for (let key of __getOwnPropNames(mod))
|
|
23
|
+
if (!__hasOwnProp.call(to, key))
|
|
24
|
+
__defProp(to, key, {
|
|
25
|
+
get: __accessProp.bind(mod, key),
|
|
26
|
+
enumerable: true
|
|
27
|
+
});
|
|
28
|
+
if (canCache)
|
|
29
|
+
cache.set(mod, to);
|
|
30
|
+
return to;
|
|
31
|
+
};
|
|
32
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
|
+
|
|
34
|
+
// src/script-host/json.ts
|
|
35
|
+
function inspectJsonValue(value, path, ancestors) {
|
|
36
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (typeof value === "number") {
|
|
40
|
+
return Number.isFinite(value) ? undefined : { path, reason: "numbers must be finite" };
|
|
41
|
+
}
|
|
42
|
+
if (typeof value !== "object") {
|
|
43
|
+
return { path, reason: `${typeof value} is not a JSON value` };
|
|
44
|
+
}
|
|
45
|
+
if (ancestors.has(value)) {
|
|
46
|
+
return { path, reason: "circular references are not JSON values" };
|
|
47
|
+
}
|
|
48
|
+
ancestors.add(value);
|
|
49
|
+
try {
|
|
50
|
+
if (Array.isArray(value)) {
|
|
51
|
+
const allowedKeys = new Set(["length"]);
|
|
52
|
+
for (let index = 0;index < value.length; index += 1) {
|
|
53
|
+
allowedKeys.add(String(index));
|
|
54
|
+
if (!Object.prototype.hasOwnProperty.call(value, index)) {
|
|
55
|
+
return {
|
|
56
|
+
path: `${path}[${index}]`,
|
|
57
|
+
reason: "array holes are not JSON values"
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const issue = inspectJsonValue(value[index], `${path}[${index}]`, ancestors);
|
|
61
|
+
if (issue !== undefined)
|
|
62
|
+
return issue;
|
|
63
|
+
}
|
|
64
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
65
|
+
if (typeof key !== "string" || !allowedKeys.has(key)) {
|
|
66
|
+
return { path, reason: "arrays must not contain custom properties" };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const prototype = Object.getPrototypeOf(value);
|
|
72
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
73
|
+
return { path, reason: "objects must be plain JSON objects" };
|
|
74
|
+
}
|
|
75
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
76
|
+
if (typeof key !== "string") {
|
|
77
|
+
return { path, reason: "symbol keys are not JSON object keys" };
|
|
78
|
+
}
|
|
79
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
80
|
+
if (descriptor === undefined || descriptor.enumerable !== true || "get" in descriptor || "set" in descriptor) {
|
|
81
|
+
return {
|
|
82
|
+
path: `${path}.${key}`,
|
|
83
|
+
reason: "JSON fields must be enumerable data properties"
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
const issue = inspectJsonValue(descriptor.value, `${path}.${key}`, ancestors);
|
|
87
|
+
if (issue !== undefined)
|
|
88
|
+
return issue;
|
|
89
|
+
}
|
|
90
|
+
return;
|
|
91
|
+
} finally {
|
|
92
|
+
ancestors.delete(value);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function findJsonViolation(value) {
|
|
96
|
+
return inspectJsonValue(value, "$", new Set);
|
|
97
|
+
}
|
|
98
|
+
function deepFreezeJson(value) {
|
|
99
|
+
if (typeof value !== "object" || value === null)
|
|
100
|
+
return value;
|
|
101
|
+
if (Array.isArray(value)) {
|
|
102
|
+
for (const item of value)
|
|
103
|
+
deepFreezeJson(item);
|
|
104
|
+
} else {
|
|
105
|
+
for (const item of Object.values(value))
|
|
106
|
+
deepFreezeJson(item);
|
|
107
|
+
}
|
|
108
|
+
return Object.isFrozen(value) ? value : Object.freeze(value);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/script-host/worker.ts
|
|
112
|
+
var AsyncFunction = Object.getPrototypeOf(async function emptyAsyncFunction() {}).constructor;
|
|
113
|
+
function redactKnownSecrets(value, secrets) {
|
|
114
|
+
let redacted = value;
|
|
115
|
+
for (const secret of secrets) {
|
|
116
|
+
if (secret.length > 0)
|
|
117
|
+
redacted = redacted.split(secret).join("[REDACTED]");
|
|
118
|
+
}
|
|
119
|
+
return redacted.slice(0, 1024);
|
|
120
|
+
}
|
|
121
|
+
function safeSourceMapSources(sourceMap) {
|
|
122
|
+
try {
|
|
123
|
+
const decoded = JSON.parse(sourceMap);
|
|
124
|
+
if (!Array.isArray(decoded.sources))
|
|
125
|
+
return [];
|
|
126
|
+
return decoded.sources.filter((source) => typeof source === "string" && source.length > 0 && source.length <= 512 && !source.startsWith("/") && !source.includes("\\") && !source.split("/").includes(".."));
|
|
127
|
+
} catch {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function executableModuleBundle(module) {
|
|
132
|
+
if (module.sourceMap === undefined)
|
|
133
|
+
return module.bundle;
|
|
134
|
+
const sourceMapUrl = Buffer.from(module.sourceMap, "utf8").toString("base64");
|
|
135
|
+
return `${module.bundle.replace(/\n?\/\/# sourceMappingURL=.*$/m, "")}
|
|
136
|
+
//# sourceMappingURL=data:application/json;base64,${sourceMapUrl}`;
|
|
137
|
+
}
|
|
138
|
+
function safeModuleFrame(stack, modules) {
|
|
139
|
+
if (stack === undefined || modules === undefined)
|
|
140
|
+
return;
|
|
141
|
+
const sources = new Set;
|
|
142
|
+
for (const module of modules) {
|
|
143
|
+
if (module.sourceMap === undefined)
|
|
144
|
+
continue;
|
|
145
|
+
for (const source of safeSourceMapSources(module.sourceMap))
|
|
146
|
+
sources.add(source);
|
|
147
|
+
}
|
|
148
|
+
for (const line of stack.split(`
|
|
149
|
+
`)) {
|
|
150
|
+
for (const source of sources) {
|
|
151
|
+
const offset = line.indexOf(source);
|
|
152
|
+
if (offset < 0)
|
|
153
|
+
continue;
|
|
154
|
+
const suffix = line.slice(offset + source.length);
|
|
155
|
+
const location = /^:(\d+):(\d+)/.exec(suffix);
|
|
156
|
+
if (location !== null)
|
|
157
|
+
return `${source}:${location[1]}:${location[2]}`;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
for (const module of modules) {
|
|
161
|
+
if (module.sourceMap === undefined)
|
|
162
|
+
continue;
|
|
163
|
+
const source = safeSourceMapSources(module.sourceMap)[0];
|
|
164
|
+
if (source === undefined)
|
|
165
|
+
continue;
|
|
166
|
+
const encoded = Buffer.from(executableModuleBundle(module), "utf8").toString("base64");
|
|
167
|
+
const offset = stack.indexOf(`data:text/javascript;base64,${encoded}`);
|
|
168
|
+
if (offset < 0)
|
|
169
|
+
continue;
|
|
170
|
+
const suffix = stack.slice(offset + `data:text/javascript;base64,${encoded}`.length);
|
|
171
|
+
const location = /^:(\d+):(\d+)/.exec(suffix);
|
|
172
|
+
if (location !== null)
|
|
173
|
+
return `${source}:${location[1]}:${location[2]}`;
|
|
174
|
+
}
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
function serializeError(error, secrets = [], modules) {
|
|
178
|
+
if (error instanceof NativeFetchTrackingError) {
|
|
179
|
+
return {
|
|
180
|
+
ok: false,
|
|
181
|
+
error: {
|
|
182
|
+
kind: "service",
|
|
183
|
+
name: error.name,
|
|
184
|
+
message: redactKnownSecrets(error.message, secrets),
|
|
185
|
+
capability: "http",
|
|
186
|
+
operation: "fetch",
|
|
187
|
+
callId: error.callId,
|
|
188
|
+
cause: error.cause
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
if (typeof error === "object" && error !== null || typeof error === "function") {
|
|
193
|
+
const nativeFetch = nativeFetchFailures.get(error);
|
|
194
|
+
if (nativeFetch !== undefined) {
|
|
195
|
+
return {
|
|
196
|
+
ok: false,
|
|
197
|
+
error: {
|
|
198
|
+
kind: "service",
|
|
199
|
+
name: error instanceof Error ? error.name : "Error",
|
|
200
|
+
message: redactKnownSecrets(nativeFetch.cause.message, secrets),
|
|
201
|
+
capability: "http",
|
|
202
|
+
operation: "fetch",
|
|
203
|
+
callId: nativeFetch.callId,
|
|
204
|
+
cause: nativeFetch.cause
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (error instanceof ServiceCallError) {
|
|
210
|
+
return {
|
|
211
|
+
ok: false,
|
|
212
|
+
error: {
|
|
213
|
+
kind: "service",
|
|
214
|
+
name: error.name,
|
|
215
|
+
message: redactKnownSecrets(error.message, secrets),
|
|
216
|
+
capability: error.capability,
|
|
217
|
+
operation: error.operation,
|
|
218
|
+
callId: error.callId,
|
|
219
|
+
cause: error.cause
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
if (error instanceof Error) {
|
|
224
|
+
return {
|
|
225
|
+
ok: false,
|
|
226
|
+
error: {
|
|
227
|
+
kind: "script",
|
|
228
|
+
name: error.name,
|
|
229
|
+
message: redactKnownSecrets(error.message, secrets),
|
|
230
|
+
...error.stack === undefined ? {} : { stack: redactKnownSecrets(error.stack, secrets) },
|
|
231
|
+
...safeModuleFrame(error.stack, modules) === undefined ? {} : { moduleFrame: safeModuleFrame(error.stack, modules) }
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
ok: false,
|
|
237
|
+
error: {
|
|
238
|
+
kind: "script",
|
|
239
|
+
name: "Error",
|
|
240
|
+
message: redactKnownSecrets(String(error), secrets)
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
class ServiceCallError extends Error {
|
|
246
|
+
code;
|
|
247
|
+
service;
|
|
248
|
+
capability;
|
|
249
|
+
operation;
|
|
250
|
+
callId;
|
|
251
|
+
retryable;
|
|
252
|
+
ambiguous;
|
|
253
|
+
details;
|
|
254
|
+
cause;
|
|
255
|
+
constructor(capability, operation, callId, cause) {
|
|
256
|
+
super(cause.message);
|
|
257
|
+
this.name = "WomlServiceError";
|
|
258
|
+
this.code = cause.code;
|
|
259
|
+
this.service = capability;
|
|
260
|
+
this.capability = capability;
|
|
261
|
+
this.operation = operation;
|
|
262
|
+
this.callId = callId;
|
|
263
|
+
this.retryable = cause.retryable;
|
|
264
|
+
this.ambiguous = cause.ambiguous;
|
|
265
|
+
this.details = cause.details;
|
|
266
|
+
this.cause = cause;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
var pendingCalls = new Map;
|
|
270
|
+
var pendingFetchAcks = new Map;
|
|
271
|
+
var operationSequences = new Map;
|
|
272
|
+
var automaticEffectfulCalls = new Map;
|
|
273
|
+
var workflowTargetIdentityModes = new Map;
|
|
274
|
+
var fetchSequence = 0;
|
|
275
|
+
var nativeFetch = globalThis.fetch.bind(globalThis);
|
|
276
|
+
var nativeFetchFailures = new WeakMap;
|
|
277
|
+
|
|
278
|
+
class NativeFetchTrackingError extends Error {
|
|
279
|
+
callId;
|
|
280
|
+
cause;
|
|
281
|
+
code;
|
|
282
|
+
retryable;
|
|
283
|
+
ambiguous;
|
|
284
|
+
constructor(callId, failure) {
|
|
285
|
+
super(failure.message);
|
|
286
|
+
this.name = "WomlFetchTrackingError";
|
|
287
|
+
this.callId = callId;
|
|
288
|
+
this.cause = failure;
|
|
289
|
+
this.code = failure.code;
|
|
290
|
+
this.retryable = failure.retryable;
|
|
291
|
+
this.ambiguous = failure.ambiguous;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function requestBodyBytes(body) {
|
|
295
|
+
if (body === undefined || body === null)
|
|
296
|
+
return;
|
|
297
|
+
if (typeof body === "string")
|
|
298
|
+
return Buffer.byteLength(body, "utf8");
|
|
299
|
+
if (body instanceof URLSearchParams) {
|
|
300
|
+
return Buffer.byteLength(body.toString(), "utf8");
|
|
301
|
+
}
|
|
302
|
+
if (body instanceof Blob)
|
|
303
|
+
return body.size;
|
|
304
|
+
if (body instanceof ArrayBuffer)
|
|
305
|
+
return body.byteLength;
|
|
306
|
+
if (ArrayBuffer.isView(body))
|
|
307
|
+
return body.byteLength;
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
function fetchStartObservation(request, requestId, input, init) {
|
|
311
|
+
const rawUrl = input instanceof Request ? input.url : input instanceof URL ? input.href : String(input);
|
|
312
|
+
const url = new URL(rawUrl);
|
|
313
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const method = String(init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase();
|
|
317
|
+
const bytes = requestBodyBytes(init?.body);
|
|
318
|
+
return {
|
|
319
|
+
contract: "woml.native-fetch-observation",
|
|
320
|
+
contractVersion: 1,
|
|
321
|
+
observationType: "started",
|
|
322
|
+
invocationId: request.invocationId,
|
|
323
|
+
requestId,
|
|
324
|
+
method,
|
|
325
|
+
origin: url.origin,
|
|
326
|
+
path: url.pathname,
|
|
327
|
+
...bytes === undefined ? {} : { requestBodyBytes: bytes },
|
|
328
|
+
startedAt: new Date().toISOString()
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
async function observeFetch(observation) {
|
|
332
|
+
if (pendingFetchAcks.has(observation.requestId)) {
|
|
333
|
+
throw new Error("Native Fetch reused an active request ID.");
|
|
334
|
+
}
|
|
335
|
+
await new Promise((resolve, reject) => {
|
|
336
|
+
pendingFetchAcks.set(observation.requestId, { resolve, reject });
|
|
337
|
+
self.postMessage({
|
|
338
|
+
messageType: "fetch_observation",
|
|
339
|
+
observation
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
function trackedNativeFetch(request) {
|
|
344
|
+
const tracked = async (input, init) => {
|
|
345
|
+
const requestId = `fetch_${++fetchSequence}_${crypto.randomUUID().replaceAll("-", "")}`;
|
|
346
|
+
const startedAt = performance.now();
|
|
347
|
+
const start = fetchStartObservation(request, requestId, input, init);
|
|
348
|
+
if (start === undefined)
|
|
349
|
+
return nativeFetch(input, init);
|
|
350
|
+
await observeFetch(start);
|
|
351
|
+
let response;
|
|
352
|
+
try {
|
|
353
|
+
response = await nativeFetch(input, init);
|
|
354
|
+
} catch (error) {
|
|
355
|
+
const name = error instanceof Error ? error.name : "";
|
|
356
|
+
const kind = name === "AbortError" ? "cancelled" : name === "TimeoutError" ? "timed_out" : "fetch_rejected";
|
|
357
|
+
const failure = {
|
|
358
|
+
kind: kind === "cancelled" ? "cancelled" : kind === "timed_out" ? "timed_out" : "transport_failed",
|
|
359
|
+
code: kind === "cancelled" ? "WOML_NATIVE_FETCH_CANCELLED" : kind === "timed_out" ? "WOML_NATIVE_FETCH_TIMED_OUT" : "WOML_NATIVE_FETCH_REJECTED",
|
|
360
|
+
message: kind === "cancelled" ? "Bun Fetch was cancelled." : kind === "timed_out" ? "Bun Fetch exceeded its deadline." : "Bun Fetch rejected the request.",
|
|
361
|
+
retryable: false,
|
|
362
|
+
ambiguous: true
|
|
363
|
+
};
|
|
364
|
+
await observeFetch({
|
|
365
|
+
contract: "woml.native-fetch-observation",
|
|
366
|
+
contractVersion: 1,
|
|
367
|
+
observationType: "failed",
|
|
368
|
+
invocationId: request.invocationId,
|
|
369
|
+
requestId,
|
|
370
|
+
durationMs: Math.max(0, performance.now() - startedAt),
|
|
371
|
+
failedAt: new Date().toISOString(),
|
|
372
|
+
error: {
|
|
373
|
+
kind,
|
|
374
|
+
code: failure.code,
|
|
375
|
+
message: failure.message
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
if (typeof error === "object" && error !== null || typeof error === "function") {
|
|
379
|
+
nativeFetchFailures.set(error, { callId: requestId, cause: failure });
|
|
380
|
+
}
|
|
381
|
+
throw error;
|
|
382
|
+
}
|
|
383
|
+
await observeFetch({
|
|
384
|
+
contract: "woml.native-fetch-observation",
|
|
385
|
+
contractVersion: 1,
|
|
386
|
+
observationType: "completed",
|
|
387
|
+
invocationId: request.invocationId,
|
|
388
|
+
requestId,
|
|
389
|
+
status: response.status,
|
|
390
|
+
responseBodyBytes: null,
|
|
391
|
+
durationMs: Math.max(0, performance.now() - startedAt),
|
|
392
|
+
completedAt: new Date().toISOString()
|
|
393
|
+
});
|
|
394
|
+
return response;
|
|
395
|
+
};
|
|
396
|
+
return tracked;
|
|
397
|
+
}
|
|
398
|
+
async function operationKey(stepIdempotencyKey, operationName) {
|
|
399
|
+
const bytes = new TextEncoder().encode(`woml.capability-operation\x00v1\x00${stepIdempotencyKey}\x00${operationName}`);
|
|
400
|
+
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", bytes));
|
|
401
|
+
return `sha256:${[...digest].map((byte) => byte.toString(16).padStart(2, "0")).join("")}`;
|
|
402
|
+
}
|
|
403
|
+
function callId() {
|
|
404
|
+
return `call_${crypto.randomUUID().replaceAll("-", "")}`;
|
|
405
|
+
}
|
|
406
|
+
function plainObject(value) {
|
|
407
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
408
|
+
}
|
|
409
|
+
function httpTimeoutMilliseconds(input) {
|
|
410
|
+
if (input.timeout !== undefined && input.timeoutMs !== undefined) {
|
|
411
|
+
throw new TypeError("Managed HTTP accepts timeout or timeoutMs, not both.");
|
|
412
|
+
}
|
|
413
|
+
const value = input.timeout ?? input.timeoutMs ?? 30000;
|
|
414
|
+
if (typeof value === "number" && Number.isSafeInteger(value))
|
|
415
|
+
return value;
|
|
416
|
+
if (typeof value !== "string") {
|
|
417
|
+
throw new TypeError("Managed HTTP timeout must be milliseconds or a duration string.");
|
|
418
|
+
}
|
|
419
|
+
const match = value.match(/^(\d+)(ms|s|m|h)$/);
|
|
420
|
+
if (match === null) {
|
|
421
|
+
throw new TypeError("Managed HTTP timeout must look like 500ms, 10s, 2m, or 1h.");
|
|
422
|
+
}
|
|
423
|
+
const multiplier = match[2] === "ms" ? 1 : match[2] === "s" ? 1000 : match[2] === "m" ? 60000 : 3600000;
|
|
424
|
+
return Number(match[1]) * multiplier;
|
|
425
|
+
}
|
|
426
|
+
function normalizeHttpRequest(input) {
|
|
427
|
+
const object = plainObject(input);
|
|
428
|
+
if (object === undefined) {
|
|
429
|
+
throw new TypeError("services.http.request() requires a request object.");
|
|
430
|
+
}
|
|
431
|
+
const allowed = new Set([
|
|
432
|
+
"url",
|
|
433
|
+
"method",
|
|
434
|
+
"headers",
|
|
435
|
+
"query",
|
|
436
|
+
"json",
|
|
437
|
+
"text",
|
|
438
|
+
"bytesBase64",
|
|
439
|
+
"responseType",
|
|
440
|
+
"storage",
|
|
441
|
+
"timeout",
|
|
442
|
+
"timeoutMs",
|
|
443
|
+
"acceptedStatus",
|
|
444
|
+
"redirect",
|
|
445
|
+
"maximumRedirects",
|
|
446
|
+
"idempotency"
|
|
447
|
+
]);
|
|
448
|
+
const unknown = Object.keys(object).find((key) => !allowed.has(key));
|
|
449
|
+
if (unknown !== undefined) {
|
|
450
|
+
throw new TypeError(`Unknown managed HTTP option "${unknown}".`);
|
|
451
|
+
}
|
|
452
|
+
if (typeof object.url !== "string" || object.url.length === 0) {
|
|
453
|
+
throw new TypeError("Managed HTTP requires a URL string.");
|
|
454
|
+
}
|
|
455
|
+
const method = String(object.method ?? "GET").toUpperCase();
|
|
456
|
+
const headers = plainObject(object.headers ?? {});
|
|
457
|
+
if (headers === undefined || Object.values(headers).some((value) => typeof value !== "string")) {
|
|
458
|
+
throw new TypeError("Managed HTTP headers must contain string values.");
|
|
459
|
+
}
|
|
460
|
+
const timeoutMs = httpTimeoutMilliseconds(object);
|
|
461
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 86400000) {
|
|
462
|
+
throw new TypeError("Managed HTTP timeout must be between 1 ms and 24 hours.");
|
|
463
|
+
}
|
|
464
|
+
const responseType = object.responseType ?? "json";
|
|
465
|
+
const storage = plainObject(object.storage);
|
|
466
|
+
if (responseType === "storage") {
|
|
467
|
+
if (storage === undefined) {
|
|
468
|
+
throw new TypeError('Managed HTTP responseType "storage" requires a storage target.');
|
|
469
|
+
}
|
|
470
|
+
const unknownStorageOption = Object.keys(storage).find((key) => !["key", "contentType", "overwrite", "ifVersion"].includes(key));
|
|
471
|
+
if (unknownStorageOption !== undefined) {
|
|
472
|
+
throw new TypeError(`Unknown managed HTTP storage option "${unknownStorageOption}".`);
|
|
473
|
+
}
|
|
474
|
+
if (typeof storage.key !== "string" || storage.key.length === 0) {
|
|
475
|
+
throw new TypeError("Managed HTTP storage requires a key string.");
|
|
476
|
+
}
|
|
477
|
+
if (storage.contentType !== undefined && typeof storage.contentType !== "string") {
|
|
478
|
+
throw new TypeError("Managed HTTP storage contentType must be a string.");
|
|
479
|
+
}
|
|
480
|
+
if (storage.overwrite !== undefined && typeof storage.overwrite !== "boolean") {
|
|
481
|
+
throw new TypeError("Managed HTTP storage overwrite must be a Boolean.");
|
|
482
|
+
}
|
|
483
|
+
if (storage.ifVersion !== undefined && typeof storage.ifVersion !== "string") {
|
|
484
|
+
throw new TypeError("Managed HTTP storage ifVersion must be a string.");
|
|
485
|
+
}
|
|
486
|
+
if (storage.overwrite !== undefined && storage.ifVersion !== undefined) {
|
|
487
|
+
throw new TypeError("Managed HTTP storage overwrite and ifVersion are mutually exclusive.");
|
|
488
|
+
}
|
|
489
|
+
} else if (object.storage !== undefined) {
|
|
490
|
+
throw new TypeError('Managed HTTP storage is valid only with responseType "storage".');
|
|
491
|
+
}
|
|
492
|
+
const normalized = {
|
|
493
|
+
contract: "woml.managed-http",
|
|
494
|
+
contractVersion: 1,
|
|
495
|
+
kind: "request",
|
|
496
|
+
method,
|
|
497
|
+
url: object.url,
|
|
498
|
+
headers,
|
|
499
|
+
responseType,
|
|
500
|
+
timeoutMs,
|
|
501
|
+
acceptedStatus: object.acceptedStatus ?? { minimum: 200, maximum: 299 },
|
|
502
|
+
redirect: object.redirect ?? "follow",
|
|
503
|
+
maximumRedirects: object.maximumRedirects ?? 10
|
|
504
|
+
};
|
|
505
|
+
for (const field of [
|
|
506
|
+
"query",
|
|
507
|
+
"json",
|
|
508
|
+
"text",
|
|
509
|
+
"bytesBase64",
|
|
510
|
+
"idempotency",
|
|
511
|
+
"storage"
|
|
512
|
+
]) {
|
|
513
|
+
if (object[field] !== undefined)
|
|
514
|
+
normalized[field] = object[field];
|
|
515
|
+
}
|
|
516
|
+
return normalized;
|
|
517
|
+
}
|
|
518
|
+
function normalizeTelegramSend(input) {
|
|
519
|
+
const object = plainObject(input);
|
|
520
|
+
if (object === undefined) {
|
|
521
|
+
throw new TypeError("services.telegram.send() requires a request object.");
|
|
522
|
+
}
|
|
523
|
+
const allowed = new Set([
|
|
524
|
+
"botToken",
|
|
525
|
+
"conversationId",
|
|
526
|
+
"text",
|
|
527
|
+
"replyToMessageId"
|
|
528
|
+
]);
|
|
529
|
+
const unknown = Object.keys(object).find((key) => !allowed.has(key));
|
|
530
|
+
if (unknown !== undefined) {
|
|
531
|
+
throw new TypeError(`Unknown Telegram send option "${unknown}".`);
|
|
532
|
+
}
|
|
533
|
+
for (const required of ["botToken", "conversationId", "text"]) {
|
|
534
|
+
if (typeof object[required] !== "string" || object[required].length === 0) {
|
|
535
|
+
throw new TypeError(`Telegram send requires a non-empty ${required} string.`);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
if (object.replyToMessageId !== undefined && (typeof object.replyToMessageId !== "string" || object.replyToMessageId.length === 0)) {
|
|
539
|
+
throw new TypeError("Telegram replyToMessageId must be a non-empty string.");
|
|
540
|
+
}
|
|
541
|
+
return {
|
|
542
|
+
contract: "woml.telegram-message",
|
|
543
|
+
contractVersion: 1,
|
|
544
|
+
kind: "send",
|
|
545
|
+
botToken: object.botToken,
|
|
546
|
+
conversationId: object.conversationId,
|
|
547
|
+
text: object.text,
|
|
548
|
+
...object.replyToMessageId === undefined ? {} : { replyToMessageId: object.replyToMessageId }
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
function publicTelegramResult(result) {
|
|
552
|
+
const object = plainObject(result);
|
|
553
|
+
if (object === undefined || object.provider !== "telegram" || typeof object.conversationId !== "string" || typeof object.messageId !== "string" || typeof object.acceptedAt !== "string") {
|
|
554
|
+
throw new TypeError("Telegram returned an invalid managed result.");
|
|
555
|
+
}
|
|
556
|
+
return Object.freeze({
|
|
557
|
+
provider: "telegram",
|
|
558
|
+
conversationId: object.conversationId,
|
|
559
|
+
messageId: object.messageId,
|
|
560
|
+
acceptedAt: object.acceptedAt
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
function normalizeDiscordSend(input) {
|
|
564
|
+
const object = plainObject(input);
|
|
565
|
+
if (object === undefined) {
|
|
566
|
+
throw new TypeError("services.discord.send() requires a request object.");
|
|
567
|
+
}
|
|
568
|
+
const allowed = new Set([
|
|
569
|
+
"botToken",
|
|
570
|
+
"conversationId",
|
|
571
|
+
"text",
|
|
572
|
+
"replyToMessageId"
|
|
573
|
+
]);
|
|
574
|
+
const unknown = Object.keys(object).find((key) => !allowed.has(key));
|
|
575
|
+
if (unknown !== undefined) {
|
|
576
|
+
throw new TypeError(`Unknown Discord send option "${unknown}".`);
|
|
577
|
+
}
|
|
578
|
+
for (const required of ["botToken", "conversationId", "text"]) {
|
|
579
|
+
if (typeof object[required] !== "string" || object[required].length === 0) {
|
|
580
|
+
throw new TypeError(`Discord send requires a non-empty ${required} string.`);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
if (!/^[0-9]{17,20}$/.test(object.conversationId)) {
|
|
584
|
+
throw new TypeError("Discord conversationId must be a numeric snowflake.");
|
|
585
|
+
}
|
|
586
|
+
if (object.text.length > 2000) {
|
|
587
|
+
throw new TypeError("Discord text may contain at most 2000 characters.");
|
|
588
|
+
}
|
|
589
|
+
if (object.replyToMessageId !== undefined && (typeof object.replyToMessageId !== "string" || !/^[0-9]{17,20}$/.test(object.replyToMessageId))) {
|
|
590
|
+
throw new TypeError("Discord replyToMessageId must be a numeric snowflake.");
|
|
591
|
+
}
|
|
592
|
+
return {
|
|
593
|
+
contract: "woml.discord-message",
|
|
594
|
+
contractVersion: 1,
|
|
595
|
+
kind: "send",
|
|
596
|
+
botToken: object.botToken,
|
|
597
|
+
conversationId: object.conversationId,
|
|
598
|
+
text: object.text,
|
|
599
|
+
...object.replyToMessageId === undefined ? {} : { replyToMessageId: object.replyToMessageId }
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
function publicDiscordResult(result) {
|
|
603
|
+
const object = plainObject(result);
|
|
604
|
+
if (object === undefined || object.provider !== "discord" || typeof object.conversationId !== "string" || typeof object.messageId !== "string" || typeof object.acceptedAt !== "string") {
|
|
605
|
+
throw new TypeError("Discord returned an invalid managed result.");
|
|
606
|
+
}
|
|
607
|
+
return Object.freeze({
|
|
608
|
+
provider: "discord",
|
|
609
|
+
conversationId: object.conversationId,
|
|
610
|
+
messageId: object.messageId,
|
|
611
|
+
acceptedAt: object.acceptedAt
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
function normalizeWhatsAppSend(input) {
|
|
615
|
+
const object = plainObject(input);
|
|
616
|
+
if (object === undefined) {
|
|
617
|
+
throw new TypeError("services.whatsapp.send() requires a request object.");
|
|
618
|
+
}
|
|
619
|
+
const allowed = new Set([
|
|
620
|
+
"accessToken",
|
|
621
|
+
"phoneNumberId",
|
|
622
|
+
"conversationId",
|
|
623
|
+
"template"
|
|
624
|
+
]);
|
|
625
|
+
const unknown = Object.keys(object).find((key) => !allowed.has(key));
|
|
626
|
+
if (unknown !== undefined) {
|
|
627
|
+
throw new TypeError(`Unknown WhatsApp send option "${unknown}".`);
|
|
628
|
+
}
|
|
629
|
+
for (const required of [
|
|
630
|
+
"accessToken",
|
|
631
|
+
"phoneNumberId",
|
|
632
|
+
"conversationId"
|
|
633
|
+
]) {
|
|
634
|
+
if (typeof object[required] !== "string" || object[required].length === 0) {
|
|
635
|
+
throw new TypeError(`WhatsApp send requires a non-empty ${required} string.`);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
if (!/^[0-9]{6,32}$/.test(object.phoneNumberId)) {
|
|
639
|
+
throw new TypeError("WhatsApp phoneNumberId must be a numeric Meta Phone Number ID.");
|
|
640
|
+
}
|
|
641
|
+
if (!/^[0-9]{8,16}$/.test(object.conversationId)) {
|
|
642
|
+
throw new TypeError("WhatsApp conversationId must contain 8 to 16 digits without a plus sign.");
|
|
643
|
+
}
|
|
644
|
+
const template = plainObject(object.template);
|
|
645
|
+
if (template === undefined || Object.keys(template).some((key) => key !== "name" && key !== "language" && key !== "parameters") || typeof template.name !== "string" || !/^[a-z][a-z0-9_]{0,511}$/.test(template.name) || typeof template.language !== "string" || !/^[a-z]{2,3}(?:_[A-Z]{2})?$/.test(template.language) || !Array.isArray(template.parameters) || template.parameters.length > 32 || template.parameters.some((value) => typeof value !== "string" || value.length > 1024)) {
|
|
646
|
+
throw new TypeError("WhatsApp template must contain a valid name, language, and string parameters array.");
|
|
647
|
+
}
|
|
648
|
+
return {
|
|
649
|
+
contract: "woml.whatsapp-message",
|
|
650
|
+
contractVersion: 1,
|
|
651
|
+
kind: "send",
|
|
652
|
+
accessToken: object.accessToken,
|
|
653
|
+
phoneNumberId: object.phoneNumberId,
|
|
654
|
+
conversationId: object.conversationId,
|
|
655
|
+
template: {
|
|
656
|
+
name: template.name,
|
|
657
|
+
language: template.language,
|
|
658
|
+
parameters: template.parameters
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
function publicWhatsAppResult(result) {
|
|
663
|
+
const object = plainObject(result);
|
|
664
|
+
if (object === undefined || object.provider !== "whatsapp" || typeof object.conversationId !== "string" || typeof object.messageId !== "string" || typeof object.acceptedAt !== "string") {
|
|
665
|
+
throw new TypeError("WhatsApp returned an invalid managed result.");
|
|
666
|
+
}
|
|
667
|
+
return Object.freeze({
|
|
668
|
+
provider: "whatsapp",
|
|
669
|
+
conversationId: object.conversationId,
|
|
670
|
+
messageId: object.messageId,
|
|
671
|
+
acceptedAt: object.acceptedAt
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
function namedOperation(capability, operation, options) {
|
|
675
|
+
if (options === undefined) {
|
|
676
|
+
const key = `${capability}.${operation}`;
|
|
677
|
+
const sequence = (operationSequences.get(key) ?? 0) + 1;
|
|
678
|
+
operationSequences.set(key, sequence);
|
|
679
|
+
return {
|
|
680
|
+
mode: "automatic",
|
|
681
|
+
name: sequence === 1 ? key : `${key}.${sequence}`
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
const object = plainObject(options);
|
|
685
|
+
const name = object?.name;
|
|
686
|
+
if (object === undefined || Object.keys(object).length !== 1 || typeof name !== "string" || !/^[a-z][a-z0-9._-]{0,127}$/.test(name)) {
|
|
687
|
+
throw new TypeError('Service call options must be exactly { name: "stable-operation-name" }.');
|
|
688
|
+
}
|
|
689
|
+
return { mode: "named", name: `${capability}.${operation}.${name}` };
|
|
690
|
+
}
|
|
691
|
+
function publicHttpResult(result) {
|
|
692
|
+
const object = plainObject(result);
|
|
693
|
+
if (object?.contract !== "woml.managed-http" || object.contractVersion !== 1 || object.kind !== "result") {
|
|
694
|
+
throw new TypeError("Rust returned an invalid Managed HTTP v1 result.");
|
|
695
|
+
}
|
|
696
|
+
return {
|
|
697
|
+
status: object.status,
|
|
698
|
+
ok: object.ok,
|
|
699
|
+
headers: object.headers,
|
|
700
|
+
data: object.data,
|
|
701
|
+
url: object.url,
|
|
702
|
+
redirected: object.redirected
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
var databaseOperations = new Set([
|
|
706
|
+
"query",
|
|
707
|
+
"execute",
|
|
708
|
+
"read",
|
|
709
|
+
"insert",
|
|
710
|
+
"update",
|
|
711
|
+
"delete",
|
|
712
|
+
"transaction"
|
|
713
|
+
]);
|
|
714
|
+
function normalizeDatabaseConfig(input) {
|
|
715
|
+
const object = plainObject(input);
|
|
716
|
+
if (object === undefined || Object.keys(object).some((key) => key !== "driver" && key !== "connection") || object.driver !== "sqlite" && object.driver !== "postgres" || typeof object.connection !== "string" || object.connection.length === 0) {
|
|
717
|
+
throw new TypeError('services.db() requires exactly { driver: "sqlite" | "postgres", connection: "database connection" }.');
|
|
718
|
+
}
|
|
719
|
+
return { driver: object.driver, connection: object.connection };
|
|
720
|
+
}
|
|
721
|
+
function normalizeDatabaseRequest(config, operation, input) {
|
|
722
|
+
if (!databaseOperations.has(operation)) {
|
|
723
|
+
throw new TypeError(`Unknown Database v1 operation "${operation}".`);
|
|
724
|
+
}
|
|
725
|
+
return {
|
|
726
|
+
contract: "woml.database",
|
|
727
|
+
contractVersion: 1,
|
|
728
|
+
kind: "request",
|
|
729
|
+
driver: config.driver,
|
|
730
|
+
connection: config.connection,
|
|
731
|
+
operation,
|
|
732
|
+
input
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
function publicDatabaseResult(result, operation) {
|
|
736
|
+
const object = plainObject(result);
|
|
737
|
+
if (object?.contract !== "woml.database" || object.contractVersion !== 1 || object.kind !== "result" || object.operation !== operation || object.data === undefined) {
|
|
738
|
+
throw new TypeError("Rust returned an invalid Database v1 result.");
|
|
739
|
+
}
|
|
740
|
+
return object.data;
|
|
741
|
+
}
|
|
742
|
+
var storageOperations = new Set(["put", "get", "head", "list", "delete"]);
|
|
743
|
+
var cacheOperations = new Set([
|
|
744
|
+
"get",
|
|
745
|
+
"set",
|
|
746
|
+
"delete",
|
|
747
|
+
"has",
|
|
748
|
+
"increment",
|
|
749
|
+
"setIfAbsent"
|
|
750
|
+
]);
|
|
751
|
+
var stateOperations = new Set([
|
|
752
|
+
"get",
|
|
753
|
+
"has",
|
|
754
|
+
"set",
|
|
755
|
+
"delete",
|
|
756
|
+
"increment",
|
|
757
|
+
"setIfAbsent"
|
|
758
|
+
]);
|
|
759
|
+
var stateWireOperations = new Set([
|
|
760
|
+
"get",
|
|
761
|
+
"has",
|
|
762
|
+
"set",
|
|
763
|
+
"delete",
|
|
764
|
+
"increment",
|
|
765
|
+
"set_if_absent"
|
|
766
|
+
]);
|
|
767
|
+
var cacheWireOperations = new Set([
|
|
768
|
+
"get",
|
|
769
|
+
"set",
|
|
770
|
+
"delete",
|
|
771
|
+
"has",
|
|
772
|
+
"increment",
|
|
773
|
+
"set_if_absent"
|
|
774
|
+
]);
|
|
775
|
+
var eventOperations = new Set(["emit"]);
|
|
776
|
+
function normalizeStorageInput(operation, rawInput) {
|
|
777
|
+
const object = plainObject(rawInput);
|
|
778
|
+
if (object === undefined) {
|
|
779
|
+
throw new TypeError(`services.storage.${operation}() requires an object.`);
|
|
780
|
+
}
|
|
781
|
+
const allowedByOperation = {
|
|
782
|
+
put: [
|
|
783
|
+
"key",
|
|
784
|
+
"value",
|
|
785
|
+
"text",
|
|
786
|
+
"bytesBase64",
|
|
787
|
+
"contentType",
|
|
788
|
+
"overwrite",
|
|
789
|
+
"ifVersion"
|
|
790
|
+
],
|
|
791
|
+
get: ["key", "responseType", "ifVersion"],
|
|
792
|
+
head: ["key"],
|
|
793
|
+
list: ["prefix", "limit", "cursor"],
|
|
794
|
+
delete: ["key", "ifVersion"]
|
|
795
|
+
};
|
|
796
|
+
const allowed = allowedByOperation[operation];
|
|
797
|
+
if (allowed === undefined) {
|
|
798
|
+
throw new TypeError(`Unknown Storage v1 operation "${operation}".`);
|
|
799
|
+
}
|
|
800
|
+
const unknown = Object.keys(object).find((key) => !allowed.includes(key));
|
|
801
|
+
if (unknown !== undefined) {
|
|
802
|
+
throw new TypeError(`Unknown Storage v1 ${operation} option "${unknown}".`);
|
|
803
|
+
}
|
|
804
|
+
if (operation === "get") {
|
|
805
|
+
return { ...object, responseType: object.responseType ?? "json" };
|
|
806
|
+
}
|
|
807
|
+
if (operation === "list") {
|
|
808
|
+
return { prefix: "", limit: 100, ...object };
|
|
809
|
+
}
|
|
810
|
+
return object;
|
|
811
|
+
}
|
|
812
|
+
function normalizeStorageRequest(operation, input) {
|
|
813
|
+
if (!storageOperations.has(operation)) {
|
|
814
|
+
throw new TypeError(`Unknown Storage v1 operation "${operation}".`);
|
|
815
|
+
}
|
|
816
|
+
return {
|
|
817
|
+
contract: "woml.storage",
|
|
818
|
+
contractVersion: 1,
|
|
819
|
+
kind: "request",
|
|
820
|
+
operation,
|
|
821
|
+
input: normalizeStorageInput(operation, input)
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
function publicStorageResult(result, operation) {
|
|
825
|
+
const object = plainObject(result);
|
|
826
|
+
if (object?.contract !== "woml.storage" || object.contractVersion !== 1 || object.kind !== "result" || object.operation !== operation || object.data === undefined) {
|
|
827
|
+
throw new TypeError("Rust returned an invalid Storage v1 result.");
|
|
828
|
+
}
|
|
829
|
+
return object.data;
|
|
830
|
+
}
|
|
831
|
+
function cacheTtlMilliseconds(value) {
|
|
832
|
+
if (value === undefined)
|
|
833
|
+
return 300000;
|
|
834
|
+
if (typeof value === "number" && Number.isSafeInteger(value)) {
|
|
835
|
+
if (value >= 1 && value <= 2592000000)
|
|
836
|
+
return value;
|
|
837
|
+
} else if (typeof value === "string") {
|
|
838
|
+
const match = value.match(/^(\d+)(ms|s|m|h|d)$/);
|
|
839
|
+
if (match !== null) {
|
|
840
|
+
const multiplier = match[2] === "ms" ? 1 : match[2] === "s" ? 1000 : match[2] === "m" ? 60000 : match[2] === "h" ? 3600000 : 86400000;
|
|
841
|
+
const ttl = Number(match[1]) * multiplier;
|
|
842
|
+
if (Number.isSafeInteger(ttl) && ttl >= 1 && ttl <= 2592000000) {
|
|
843
|
+
return ttl;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
throw new TypeError("Cache ttl must be milliseconds or a whole duration from 1ms through 30d.");
|
|
848
|
+
}
|
|
849
|
+
function normalizeCacheOptions(value, acceptsTtl) {
|
|
850
|
+
if (value === undefined) {
|
|
851
|
+
return { ttlMs: 300000 };
|
|
852
|
+
}
|
|
853
|
+
const object = plainObject(value);
|
|
854
|
+
const allowed = acceptsTtl ? ["ttl", "name"] : ["name"];
|
|
855
|
+
if (object === undefined || Object.keys(object).some((key) => !allowed.includes(key)) || object.name !== undefined && typeof object.name !== "string") {
|
|
856
|
+
throw new TypeError(acceptsTtl ? "Cache options accept only ttl and a stable name." : "Cache options accept only a stable name.");
|
|
857
|
+
}
|
|
858
|
+
return {
|
|
859
|
+
ttlMs: cacheTtlMilliseconds(object.ttl),
|
|
860
|
+
...object.name === undefined ? {} : { callOptions: { name: object.name } }
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
function normalizeCacheRequest(operation, args) {
|
|
864
|
+
if (!cacheOperations.has(operation)) {
|
|
865
|
+
throw new TypeError(`Unknown Cache v1 operation "${operation}".`);
|
|
866
|
+
}
|
|
867
|
+
const key = args[0];
|
|
868
|
+
if (typeof key !== "string") {
|
|
869
|
+
throw new TypeError(`services.cache.${operation}() requires a string key.`);
|
|
870
|
+
}
|
|
871
|
+
let input;
|
|
872
|
+
let callOptions;
|
|
873
|
+
const wireOperation = operation === "setIfAbsent" ? "set_if_absent" : operation;
|
|
874
|
+
if (operation === "get" || operation === "has") {
|
|
875
|
+
if (args.length !== 1) {
|
|
876
|
+
throw new TypeError(`services.cache.${operation}() accepts only a key.`);
|
|
877
|
+
}
|
|
878
|
+
input = { key };
|
|
879
|
+
} else if (operation === "delete") {
|
|
880
|
+
if (args.length > 2) {
|
|
881
|
+
throw new TypeError("services.cache.delete() accepts key and options.");
|
|
882
|
+
}
|
|
883
|
+
const normalized = normalizeCacheOptions(args[1], false);
|
|
884
|
+
input = { key };
|
|
885
|
+
callOptions = normalized.callOptions;
|
|
886
|
+
} else if (operation === "set" || operation === "setIfAbsent") {
|
|
887
|
+
if (args.length < 2 || args.length > 3) {
|
|
888
|
+
throw new TypeError(`services.cache.${operation}() requires key, value, and optional options.`);
|
|
889
|
+
}
|
|
890
|
+
const normalized = normalizeCacheOptions(args[2], true);
|
|
891
|
+
input = { key, value: args[1], ttlMs: normalized.ttlMs };
|
|
892
|
+
callOptions = normalized.callOptions;
|
|
893
|
+
} else {
|
|
894
|
+
if (args.length > 3) {
|
|
895
|
+
throw new TypeError("services.cache.increment() accepts key, optional amount, and options.");
|
|
896
|
+
}
|
|
897
|
+
const secondIsOptions = plainObject(args[1]) !== undefined;
|
|
898
|
+
const amount = args[1] === undefined || secondIsOptions ? 1 : args[1];
|
|
899
|
+
if (typeof amount !== "number" || !Number.isSafeInteger(amount)) {
|
|
900
|
+
throw new TypeError("Cache increment amount must be a safe integer.");
|
|
901
|
+
}
|
|
902
|
+
const normalized = normalizeCacheOptions(secondIsOptions ? args[1] : args[2], true);
|
|
903
|
+
input = { key, amount, ttlMs: normalized.ttlMs };
|
|
904
|
+
callOptions = normalized.callOptions;
|
|
905
|
+
}
|
|
906
|
+
return {
|
|
907
|
+
request: {
|
|
908
|
+
contract: "woml.cache",
|
|
909
|
+
contractVersion: 1,
|
|
910
|
+
kind: "request",
|
|
911
|
+
operation: wireOperation,
|
|
912
|
+
input
|
|
913
|
+
},
|
|
914
|
+
...callOptions === undefined ? {} : { callOptions }
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
function publicCacheResult(result, operation) {
|
|
918
|
+
const object = plainObject(result);
|
|
919
|
+
if (object?.contract !== "woml.cache" || object.contractVersion !== 1 || object.kind !== "result" || object.operation !== operation || object.data === undefined) {
|
|
920
|
+
throw new TypeError("Rust returned an invalid Cache v1 result.");
|
|
921
|
+
}
|
|
922
|
+
return object.data;
|
|
923
|
+
}
|
|
924
|
+
function stateKey(value, operation) {
|
|
925
|
+
if (typeof value !== "string" || value.length === 0 || Buffer.byteLength(value, "utf8") > 256) {
|
|
926
|
+
throw new TypeError(`services.state.${operation}() requires a non-empty key up to 256 UTF-8 bytes.`);
|
|
927
|
+
}
|
|
928
|
+
return value;
|
|
929
|
+
}
|
|
930
|
+
function stateMutationOptions(value, acceptsVersion) {
|
|
931
|
+
const object = plainObject(value);
|
|
932
|
+
const allowed = acceptsVersion ? ["name", "ifVersion"] : ["name"];
|
|
933
|
+
if (object === undefined || Object.keys(object).some((key) => !allowed.includes(key)) || typeof object.name !== "string" || !/^[a-z][a-z0-9._-]{0,127}$/.test(object.name) || object.ifVersion !== undefined && (!Number.isSafeInteger(object.ifVersion) || Number(object.ifVersion) < 0)) {
|
|
934
|
+
throw new TypeError(acceptsVersion ? "State mutation options require a stable name and optional non-negative ifVersion." : "State mutation options require exactly one stable name.");
|
|
935
|
+
}
|
|
936
|
+
return {
|
|
937
|
+
callOptions: { name: object.name },
|
|
938
|
+
...object.ifVersion === undefined ? {} : { ifVersion: Number(object.ifVersion) }
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
function stateValue(value) {
|
|
942
|
+
const violation = findJsonViolation(value);
|
|
943
|
+
if (violation !== undefined) {
|
|
944
|
+
throw new TypeError(`State value ${violation.path}: ${violation.reason}`);
|
|
945
|
+
}
|
|
946
|
+
const encoded = JSON.stringify(value);
|
|
947
|
+
if (Buffer.byteLength(encoded, "utf8") > 262144) {
|
|
948
|
+
throw new TypeError("State values must not exceed 256 KiB of canonical JSON.");
|
|
949
|
+
}
|
|
950
|
+
return value;
|
|
951
|
+
}
|
|
952
|
+
function normalizeStateRequest(operation, args) {
|
|
953
|
+
if (!stateOperations.has(operation)) {
|
|
954
|
+
throw new TypeError(`Unknown Durable User State v1 operation "${operation}".`);
|
|
955
|
+
}
|
|
956
|
+
const key = stateKey(args[0], operation);
|
|
957
|
+
let input;
|
|
958
|
+
let callOptions;
|
|
959
|
+
const wireOperation = operation === "setIfAbsent" ? "set_if_absent" : operation;
|
|
960
|
+
if (operation === "get" || operation === "has") {
|
|
961
|
+
if (args.length !== 1) {
|
|
962
|
+
throw new TypeError(`services.state.${operation}() accepts only a key.`);
|
|
963
|
+
}
|
|
964
|
+
input = { key };
|
|
965
|
+
} else if (operation === "delete") {
|
|
966
|
+
if (args.length !== 2) {
|
|
967
|
+
throw new TypeError("services.state.delete() requires key and mutation options.");
|
|
968
|
+
}
|
|
969
|
+
const options = stateMutationOptions(args[1], true);
|
|
970
|
+
input = { key, ...options.ifVersion === undefined ? {} : { ifVersion: options.ifVersion } };
|
|
971
|
+
callOptions = options.callOptions;
|
|
972
|
+
} else if (operation === "set" || operation === "setIfAbsent") {
|
|
973
|
+
if (args.length !== 3) {
|
|
974
|
+
throw new TypeError(`services.state.${operation}() requires key, JSON value, and mutation options.`);
|
|
975
|
+
}
|
|
976
|
+
const options = stateMutationOptions(args[2], operation === "set");
|
|
977
|
+
input = {
|
|
978
|
+
key,
|
|
979
|
+
value: stateValue(args[1]),
|
|
980
|
+
...options.ifVersion === undefined ? {} : { ifVersion: options.ifVersion }
|
|
981
|
+
};
|
|
982
|
+
callOptions = options.callOptions;
|
|
983
|
+
} else {
|
|
984
|
+
if (args.length !== 3 || typeof args[1] !== "number" || !Number.isSafeInteger(args[1])) {
|
|
985
|
+
throw new TypeError("services.state.increment() requires key, safe-integer amount, and mutation options.");
|
|
986
|
+
}
|
|
987
|
+
const options = stateMutationOptions(args[2], true);
|
|
988
|
+
input = {
|
|
989
|
+
key,
|
|
990
|
+
amount: args[1],
|
|
991
|
+
...options.ifVersion === undefined ? {} : { ifVersion: options.ifVersion }
|
|
992
|
+
};
|
|
993
|
+
callOptions = options.callOptions;
|
|
994
|
+
}
|
|
995
|
+
return {
|
|
996
|
+
request: {
|
|
997
|
+
contract: "woml.state",
|
|
998
|
+
contractVersion: 1,
|
|
999
|
+
kind: "request",
|
|
1000
|
+
operation: wireOperation,
|
|
1001
|
+
input
|
|
1002
|
+
},
|
|
1003
|
+
...callOptions === undefined ? {} : { callOptions }
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
function publicStateResult(result, operation) {
|
|
1007
|
+
const object = plainObject(result);
|
|
1008
|
+
const data = plainObject(object?.data);
|
|
1009
|
+
const exact = (required, optional = []) => data !== undefined && required.every((key) => Object.hasOwn(data, key)) && Object.keys(data).every((key) => required.includes(key) || optional.includes(key));
|
|
1010
|
+
const version = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 1;
|
|
1011
|
+
const instant = (value) => typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
1012
|
+
const validData = operation === "get" ? exact(["found"]) && data?.found === false || exact(["found", "value", "version", "updatedAt"]) && data?.found === true && version(data.version) && instant(data.updatedAt) : operation === "has" ? exact(["present"]) && data?.present === false || exact(["present", "version"]) && data?.present === true && version(data.version) : operation === "set" ? exact(["stored", "version", "updatedAt"]) && data?.stored === true && version(data.version) && instant(data.updatedAt) : operation === "delete" ? exact(["deleted"]) && typeof data?.deleted === "boolean" : operation === "increment" ? exact(["value", "version", "updatedAt"]) && typeof data?.value === "number" && Number.isSafeInteger(data.value) && version(data.version) && instant(data.updatedAt) : operation === "set_if_absent" ? exact(["stored", "value", "version", "updatedAt"]) && typeof data?.stored === "boolean" && version(data.version) && instant(data.updatedAt) : false;
|
|
1013
|
+
if (object?.contract !== "woml.state" || object.contractVersion !== 1 || object.kind !== "result" || object.operation !== operation || !validData) {
|
|
1014
|
+
throw new TypeError("Rust returned an invalid Durable User State v1 result.");
|
|
1015
|
+
}
|
|
1016
|
+
return deepFreezeJson(data);
|
|
1017
|
+
}
|
|
1018
|
+
function normalizeEventEmit(args) {
|
|
1019
|
+
if (args.length < 1 || args.length > 3) {
|
|
1020
|
+
throw new TypeError("services.events.emit() requires an event name, optional payload, and optional options.");
|
|
1021
|
+
}
|
|
1022
|
+
const name = args[0];
|
|
1023
|
+
if (typeof name !== "string" || !/^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)+$/.test(name) || Buffer.byteLength(name, "utf8") > 256) {
|
|
1024
|
+
throw new TypeError("services.events.emit() requires a valid lowercase dotted event name.");
|
|
1025
|
+
}
|
|
1026
|
+
const payload = args[1] ?? {};
|
|
1027
|
+
if (plainObject(payload) === undefined) {
|
|
1028
|
+
throw new TypeError("Event payload must be a top-level JSON object.");
|
|
1029
|
+
}
|
|
1030
|
+
const options = args[2];
|
|
1031
|
+
if (options !== undefined) {
|
|
1032
|
+
namedOperation("events", "emit", options);
|
|
1033
|
+
}
|
|
1034
|
+
return {
|
|
1035
|
+
request: {
|
|
1036
|
+
contract: "woml.events",
|
|
1037
|
+
contractVersion: 1,
|
|
1038
|
+
kind: "request",
|
|
1039
|
+
operation: "emit",
|
|
1040
|
+
input: { name, payload }
|
|
1041
|
+
},
|
|
1042
|
+
...options === undefined ? {} : { callOptions: options }
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
function publicEventResult(result) {
|
|
1046
|
+
const object = plainObject(result);
|
|
1047
|
+
if (object?.contract !== "woml.events" || object.contractVersion !== 1 || object.kind !== "result" || object.operation !== "emit" || object.data === undefined) {
|
|
1048
|
+
throw new TypeError("Rust returned an invalid Events Service v1 result.");
|
|
1049
|
+
}
|
|
1050
|
+
return object.data;
|
|
1051
|
+
}
|
|
1052
|
+
function workflowCallTimeoutMilliseconds(value) {
|
|
1053
|
+
if (typeof value === "number" && Number.isSafeInteger(value))
|
|
1054
|
+
return value;
|
|
1055
|
+
if (typeof value !== "string") {
|
|
1056
|
+
throw new TypeError("Workflow Call timeout must be milliseconds or a duration string.");
|
|
1057
|
+
}
|
|
1058
|
+
const match = value.match(/^(\d+)(ms|s|m|h)$/);
|
|
1059
|
+
if (match === null) {
|
|
1060
|
+
throw new TypeError("Workflow Call timeout must look like 500ms, 10s, 2m, or 1h.");
|
|
1061
|
+
}
|
|
1062
|
+
const multiplier = match[2] === "ms" ? 1 : match[2] === "s" ? 1000 : match[2] === "m" ? 60000 : 3600000;
|
|
1063
|
+
return Number(match[1]) * multiplier;
|
|
1064
|
+
}
|
|
1065
|
+
function normalizeWorkflowOperation(operation, args, remainingTimeoutMs) {
|
|
1066
|
+
if (args.length < 2 || args.length > 3) {
|
|
1067
|
+
throw new TypeError(`services.workflows.${operation}() requires workflowId, payload, and optional options.`);
|
|
1068
|
+
}
|
|
1069
|
+
const [workflowId, payload, rawOptions] = args;
|
|
1070
|
+
if (typeof workflowId !== "string" || !/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/.test(workflowId) || workflowId.length > 256) {
|
|
1071
|
+
throw new TypeError(`Workflow ${operation} workflowId must use lowercase kebab-case.`);
|
|
1072
|
+
}
|
|
1073
|
+
const payloadObject = plainObject(payload);
|
|
1074
|
+
if (payloadObject === undefined) {
|
|
1075
|
+
throw new TypeError(`Workflow ${operation} payload must be a JSON object.`);
|
|
1076
|
+
}
|
|
1077
|
+
const options = rawOptions === undefined ? {} : plainObject(rawOptions);
|
|
1078
|
+
if (options === undefined) {
|
|
1079
|
+
throw new TypeError(`Workflow ${operation} options must be an object.`);
|
|
1080
|
+
}
|
|
1081
|
+
const unknown = Object.keys(options).find((key) => key !== "name" && (operation !== "call" || key !== "timeout"));
|
|
1082
|
+
if (unknown !== undefined) {
|
|
1083
|
+
throw new TypeError(`Unknown services.workflows.${operation}() option "${unknown}".`);
|
|
1084
|
+
}
|
|
1085
|
+
const name = options.name;
|
|
1086
|
+
if (name !== undefined && (typeof name !== "string" || !/^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/.test(name) || name.length > 128)) {
|
|
1087
|
+
throw new TypeError(`Workflow ${operation} name is invalid.`);
|
|
1088
|
+
}
|
|
1089
|
+
const timeoutMs = operation === "call" ? options.timeout === undefined ? remainingTimeoutMs : workflowCallTimeoutMilliseconds(options.timeout) : undefined;
|
|
1090
|
+
if (timeoutMs !== undefined && (timeoutMs < 1 || timeoutMs > 86400000)) {
|
|
1091
|
+
throw new TypeError("Workflow Call timeout must be between 1 ms and 24 hours.");
|
|
1092
|
+
}
|
|
1093
|
+
if (timeoutMs !== undefined && timeoutMs > remainingTimeoutMs) {
|
|
1094
|
+
throw new TypeError("Workflow Call timeout cannot exceed the calling step remaining timeout.");
|
|
1095
|
+
}
|
|
1096
|
+
const identityMode = name === undefined ? "automatic" : "named";
|
|
1097
|
+
const targetIdentityKey = `${operation}:${workflowId}`;
|
|
1098
|
+
const previousIdentityMode = workflowTargetIdentityModes.get(targetIdentityKey);
|
|
1099
|
+
if (previousIdentityMode !== undefined && (previousIdentityMode === "automatic" || identityMode === "automatic")) {
|
|
1100
|
+
throw new TypeError(`Multiple services.workflows.${operation}() operations for workflow "${workflowId}" in one step require stable names, for example { name: "primary-operation" }.`);
|
|
1101
|
+
}
|
|
1102
|
+
workflowTargetIdentityModes.set(targetIdentityKey, identityMode);
|
|
1103
|
+
return {
|
|
1104
|
+
request: {
|
|
1105
|
+
contract: operation === "call" ? "woml.workflow-call" : "woml.workflow-start",
|
|
1106
|
+
contractVersion: 1,
|
|
1107
|
+
kind: "request",
|
|
1108
|
+
workflowId,
|
|
1109
|
+
payload: payloadObject,
|
|
1110
|
+
options: {
|
|
1111
|
+
...name === undefined ? {} : { name },
|
|
1112
|
+
...timeoutMs === undefined ? {} : { timeoutMs }
|
|
1113
|
+
}
|
|
1114
|
+
},
|
|
1115
|
+
...name === undefined ? {} : { callOptions: { name } }
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
function publicWorkflowResult(result, operation) {
|
|
1119
|
+
const object = plainObject(result);
|
|
1120
|
+
if (operation === "start") {
|
|
1121
|
+
if (object?.contract !== "woml.workflow-start" || object.contractVersion !== 1 || object.kind !== "started" || typeof object.workflowId !== "string" || typeof object.runId !== "string" || typeof object.duplicate !== "boolean") {
|
|
1122
|
+
throw new TypeError("Rust returned an invalid Workflow Start v1 result.");
|
|
1123
|
+
}
|
|
1124
|
+
return Object.freeze({
|
|
1125
|
+
workflowId: object.workflowId,
|
|
1126
|
+
runId: object.runId,
|
|
1127
|
+
duplicate: object.duplicate
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
if (object?.contract !== "woml.workflow-call" || object.contractVersion !== 1 || object.kind !== "succeeded" || typeof object.workflowId !== "string" || typeof object.definitionHash !== "string" || typeof object.childRunId !== "string" || !Object.hasOwn(object, "result")) {
|
|
1131
|
+
throw new TypeError("Rust returned an invalid Workflow Call v1 result.");
|
|
1132
|
+
}
|
|
1133
|
+
return object.result;
|
|
1134
|
+
}
|
|
1135
|
+
function deeplyReadonlyServiceFacade(request, executionDeadline) {
|
|
1136
|
+
if (request.attempt === undefined || request.bindings === undefined) {
|
|
1137
|
+
return Object.freeze({});
|
|
1138
|
+
}
|
|
1139
|
+
const attempt = request.attempt;
|
|
1140
|
+
const capabilityCache = new Map;
|
|
1141
|
+
const invokeCapability = async (capability, operation, input, callOptions) => {
|
|
1142
|
+
const managedHttp = capability === "http" && operation === "request";
|
|
1143
|
+
const managedDatabase = capability === "db" && databaseOperations.has(operation);
|
|
1144
|
+
const managedStorage = capability === "storage" && storageOperations.has(operation);
|
|
1145
|
+
const managedCache = capability === "cache" && cacheWireOperations.has(operation);
|
|
1146
|
+
const managedState = capability === "state" && stateWireOperations.has(operation);
|
|
1147
|
+
const managedEvents = capability === "events" && eventOperations.has(operation);
|
|
1148
|
+
const managedWorkflows = capability === "workflows" && (operation === "call" || operation === "start");
|
|
1149
|
+
const managedTelegram = capability === "telegram" && operation === "send";
|
|
1150
|
+
const managedDiscord = capability === "discord" && operation === "send";
|
|
1151
|
+
const managedWhatsApp = capability === "whatsapp" && operation === "send";
|
|
1152
|
+
if (!managedHttp && !managedDatabase && !managedStorage && !managedCache && !managedState && !managedEvents && !managedWorkflows && !managedTelegram && !managedDiscord && !managedWhatsApp && callOptions !== undefined) {
|
|
1153
|
+
throw new TypeError("Named service-call options are supported by managed WOML services only.");
|
|
1154
|
+
}
|
|
1155
|
+
const violation = findJsonViolation(input);
|
|
1156
|
+
if (violation !== undefined) {
|
|
1157
|
+
throw new TypeError(`${violation.path}: ${violation.reason}`);
|
|
1158
|
+
}
|
|
1159
|
+
const inputBytes = Buffer.byteLength(JSON.stringify(input), "utf8");
|
|
1160
|
+
const inputLimit = managedWorkflows ? 1052672 : 1048576;
|
|
1161
|
+
if (inputBytes > inputLimit) {
|
|
1162
|
+
const id2 = callId();
|
|
1163
|
+
throw new ServiceCallError(capability, operation, id2, {
|
|
1164
|
+
kind: "input_too_large",
|
|
1165
|
+
code: "WOML_CAPABILITY_INPUT_TOO_LARGE",
|
|
1166
|
+
message: "The capability input exceeds its configured byte limit.",
|
|
1167
|
+
retryable: false,
|
|
1168
|
+
ambiguous: false,
|
|
1169
|
+
details: { actualBytes: inputBytes, limitBytes: inputLimit }
|
|
1170
|
+
});
|
|
1171
|
+
}
|
|
1172
|
+
const identity = namedOperation(capability, operation, callOptions);
|
|
1173
|
+
const method = managedHttp ? String(input.method) : undefined;
|
|
1174
|
+
const effectful = managedHttp ? input.responseType === "storage" || method !== "GET" && method !== "HEAD" && method !== "OPTIONS" : managedDatabase ? operation !== "query" && operation !== "read" : managedStorage && (operation === "put" || operation === "delete");
|
|
1175
|
+
const cacheEffectful = managedCache && operation !== "get" && operation !== "has";
|
|
1176
|
+
const stateEffectful = managedState && operation !== "get" && operation !== "has";
|
|
1177
|
+
if ((effectful || cacheEffectful || stateEffectful || managedEvents || managedTelegram || managedDiscord || managedWhatsApp) && identity.mode === "automatic") {
|
|
1178
|
+
const key = `${capability}.${operation}`;
|
|
1179
|
+
const count = (automaticEffectfulCalls.get(key) ?? 0) + 1;
|
|
1180
|
+
automaticEffectfulCalls.set(key, count);
|
|
1181
|
+
if (count > 1) {
|
|
1182
|
+
throw new TypeError(`Multiple effectful services.${capability}.${operation}() calls in one step require stable names, for example { name: "write-customer" }.`);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
const httpIdempotency = managedHttp ? plainObject(input.idempotency) : undefined;
|
|
1186
|
+
const providerIdempotencyKey = typeof httpIdempotency?.value === "string" ? httpIdempotency.value : undefined;
|
|
1187
|
+
const timeoutMs = managedHttp ? Math.min(86400000, Number(input.timeoutMs) + 1000) : managedWorkflows ? Math.min(86400000, Number(plainObject(input.options)?.timeoutMs ?? 30000) + 1000) : 30000;
|
|
1188
|
+
const id = callId();
|
|
1189
|
+
const call = {
|
|
1190
|
+
contract: "woml.capability-call",
|
|
1191
|
+
contractVersion: 1,
|
|
1192
|
+
messageType: "request",
|
|
1193
|
+
invocationId: request.invocationId,
|
|
1194
|
+
callId: id,
|
|
1195
|
+
runId: request.runId,
|
|
1196
|
+
nodeId: request.nodeId,
|
|
1197
|
+
attemptNumber: attempt.number,
|
|
1198
|
+
capability,
|
|
1199
|
+
operation,
|
|
1200
|
+
inputContractVersion: 1,
|
|
1201
|
+
resultContractVersion: 1,
|
|
1202
|
+
identity: {
|
|
1203
|
+
mode: identity.mode,
|
|
1204
|
+
stepIdempotencyKey: attempt.idempotencyKey,
|
|
1205
|
+
operationName: identity.name,
|
|
1206
|
+
operationKey: await operationKey(attempt.idempotencyKey, identity.name),
|
|
1207
|
+
...providerIdempotencyKey === undefined ? {} : { providerIdempotencyKey }
|
|
1208
|
+
},
|
|
1209
|
+
limits: {
|
|
1210
|
+
inputBytes: inputLimit,
|
|
1211
|
+
resultBytes: managedWorkflows ? 4198400 : 4194304,
|
|
1212
|
+
timeoutMs
|
|
1213
|
+
},
|
|
1214
|
+
input
|
|
1215
|
+
};
|
|
1216
|
+
const result = await new Promise((resolve, reject) => {
|
|
1217
|
+
pendingCalls.set(id, { capability, operation, resolve, reject });
|
|
1218
|
+
self.postMessage({
|
|
1219
|
+
messageType: "capability_call",
|
|
1220
|
+
call
|
|
1221
|
+
});
|
|
1222
|
+
});
|
|
1223
|
+
return managedHttp ? publicHttpResult(result) : managedDatabase ? publicDatabaseResult(result, operation) : managedStorage ? publicStorageResult(result, operation) : managedCache ? publicCacheResult(result, operation) : managedState ? publicStateResult(result, operation) : managedEvents ? publicEventResult(result) : managedWorkflows ? publicWorkflowResult(result, operation) : managedTelegram ? publicTelegramResult(result) : managedDiscord ? publicDiscordResult(result) : managedWhatsApp ? publicWhatsAppResult(result) : result;
|
|
1224
|
+
};
|
|
1225
|
+
return new Proxy(Object.freeze({}), {
|
|
1226
|
+
get(_target, capabilityProperty) {
|
|
1227
|
+
if (typeof capabilityProperty !== "string")
|
|
1228
|
+
return;
|
|
1229
|
+
const cached = capabilityCache.get(capabilityProperty);
|
|
1230
|
+
if (cached !== undefined)
|
|
1231
|
+
return cached;
|
|
1232
|
+
if (capabilityProperty === "db") {
|
|
1233
|
+
const database = (rawConfig) => {
|
|
1234
|
+
const config = Object.freeze(normalizeDatabaseConfig(rawConfig));
|
|
1235
|
+
const operationCache2 = new Map;
|
|
1236
|
+
return new Proxy(Object.freeze({}), {
|
|
1237
|
+
get(_databaseTarget, operationProperty) {
|
|
1238
|
+
if (typeof operationProperty !== "string")
|
|
1239
|
+
return;
|
|
1240
|
+
const known = operationCache2.get(operationProperty);
|
|
1241
|
+
if (known !== undefined)
|
|
1242
|
+
return known;
|
|
1243
|
+
if (!databaseOperations.has(operationProperty)) {
|
|
1244
|
+
return;
|
|
1245
|
+
}
|
|
1246
|
+
const invoke = async (rawInput = null, callOptions) => invokeCapability("db", operationProperty, normalizeDatabaseRequest(config, operationProperty, rawInput), callOptions);
|
|
1247
|
+
Object.freeze(invoke);
|
|
1248
|
+
operationCache2.set(operationProperty, invoke);
|
|
1249
|
+
return invoke;
|
|
1250
|
+
},
|
|
1251
|
+
set: () => false,
|
|
1252
|
+
defineProperty: () => false,
|
|
1253
|
+
deleteProperty: () => false
|
|
1254
|
+
});
|
|
1255
|
+
};
|
|
1256
|
+
Object.freeze(database);
|
|
1257
|
+
capabilityCache.set(capabilityProperty, database);
|
|
1258
|
+
return database;
|
|
1259
|
+
}
|
|
1260
|
+
if (capabilityProperty === "storage") {
|
|
1261
|
+
const operationCache2 = new Map;
|
|
1262
|
+
const storage = new Proxy(Object.freeze({}), {
|
|
1263
|
+
get(_storageTarget, operationProperty) {
|
|
1264
|
+
if (typeof operationProperty !== "string")
|
|
1265
|
+
return;
|
|
1266
|
+
const known = operationCache2.get(operationProperty);
|
|
1267
|
+
if (known !== undefined)
|
|
1268
|
+
return known;
|
|
1269
|
+
if (!storageOperations.has(operationProperty))
|
|
1270
|
+
return;
|
|
1271
|
+
const invoke = async (rawInput = {}, callOptions) => invokeCapability("storage", operationProperty, normalizeStorageRequest(operationProperty, rawInput), callOptions);
|
|
1272
|
+
Object.freeze(invoke);
|
|
1273
|
+
operationCache2.set(operationProperty, invoke);
|
|
1274
|
+
return invoke;
|
|
1275
|
+
},
|
|
1276
|
+
set: () => false,
|
|
1277
|
+
defineProperty: () => false,
|
|
1278
|
+
deleteProperty: () => false
|
|
1279
|
+
});
|
|
1280
|
+
capabilityCache.set(capabilityProperty, storage);
|
|
1281
|
+
return storage;
|
|
1282
|
+
}
|
|
1283
|
+
if (capabilityProperty === "cache") {
|
|
1284
|
+
const operationCache2 = new Map;
|
|
1285
|
+
const cache = new Proxy(Object.freeze({}), {
|
|
1286
|
+
get(_cacheTarget, operationProperty) {
|
|
1287
|
+
if (typeof operationProperty !== "string")
|
|
1288
|
+
return;
|
|
1289
|
+
const known = operationCache2.get(operationProperty);
|
|
1290
|
+
if (known !== undefined)
|
|
1291
|
+
return known;
|
|
1292
|
+
if (!cacheOperations.has(operationProperty))
|
|
1293
|
+
return;
|
|
1294
|
+
const invoke = async (...args) => {
|
|
1295
|
+
const normalized = normalizeCacheRequest(operationProperty, args);
|
|
1296
|
+
const wireOperation = String(normalized.request.operation);
|
|
1297
|
+
return invokeCapability("cache", wireOperation, normalized.request, normalized.callOptions);
|
|
1298
|
+
};
|
|
1299
|
+
Object.freeze(invoke);
|
|
1300
|
+
operationCache2.set(operationProperty, invoke);
|
|
1301
|
+
return invoke;
|
|
1302
|
+
},
|
|
1303
|
+
set: () => false,
|
|
1304
|
+
defineProperty: () => false,
|
|
1305
|
+
deleteProperty: () => false
|
|
1306
|
+
});
|
|
1307
|
+
capabilityCache.set(capabilityProperty, cache);
|
|
1308
|
+
return cache;
|
|
1309
|
+
}
|
|
1310
|
+
if (capabilityProperty === "state") {
|
|
1311
|
+
const operationCache2 = new Map;
|
|
1312
|
+
const state = new Proxy(Object.freeze({}), {
|
|
1313
|
+
get(_stateTarget, operationProperty) {
|
|
1314
|
+
if (typeof operationProperty !== "string")
|
|
1315
|
+
return;
|
|
1316
|
+
const known = operationCache2.get(operationProperty);
|
|
1317
|
+
if (known !== undefined)
|
|
1318
|
+
return known;
|
|
1319
|
+
if (!stateOperations.has(operationProperty))
|
|
1320
|
+
return;
|
|
1321
|
+
const invoke = async (...args) => {
|
|
1322
|
+
const normalized = normalizeStateRequest(operationProperty, args);
|
|
1323
|
+
const wireOperation = String(normalized.request.operation);
|
|
1324
|
+
return invokeCapability("state", wireOperation, normalized.request, normalized.callOptions);
|
|
1325
|
+
};
|
|
1326
|
+
Object.freeze(invoke);
|
|
1327
|
+
operationCache2.set(operationProperty, invoke);
|
|
1328
|
+
return invoke;
|
|
1329
|
+
},
|
|
1330
|
+
set: () => false,
|
|
1331
|
+
defineProperty: () => false,
|
|
1332
|
+
deleteProperty: () => false
|
|
1333
|
+
});
|
|
1334
|
+
capabilityCache.set(capabilityProperty, state);
|
|
1335
|
+
return state;
|
|
1336
|
+
}
|
|
1337
|
+
if (capabilityProperty === "events") {
|
|
1338
|
+
const emit = async (...args) => {
|
|
1339
|
+
const normalized = normalizeEventEmit(args);
|
|
1340
|
+
return invokeCapability("events", "emit", normalized.request, normalized.callOptions);
|
|
1341
|
+
};
|
|
1342
|
+
Object.freeze(emit);
|
|
1343
|
+
const events = Object.freeze({ emit });
|
|
1344
|
+
capabilityCache.set(capabilityProperty, events);
|
|
1345
|
+
return events;
|
|
1346
|
+
}
|
|
1347
|
+
if (capabilityProperty === "workflows") {
|
|
1348
|
+
const call = async (...args) => {
|
|
1349
|
+
const remainingTimeoutMs = Math.max(1, Math.floor(executionDeadline - performance.now()));
|
|
1350
|
+
const normalized = normalizeWorkflowOperation("call", args, remainingTimeoutMs);
|
|
1351
|
+
return invokeCapability("workflows", "call", normalized.request, normalized.callOptions);
|
|
1352
|
+
};
|
|
1353
|
+
const start = async (...args) => {
|
|
1354
|
+
const remainingTimeoutMs = Math.max(1, Math.floor(executionDeadline - performance.now()));
|
|
1355
|
+
const normalized = normalizeWorkflowOperation("start", args, remainingTimeoutMs);
|
|
1356
|
+
return invokeCapability("workflows", "start", normalized.request, normalized.callOptions);
|
|
1357
|
+
};
|
|
1358
|
+
Object.freeze(call);
|
|
1359
|
+
Object.freeze(start);
|
|
1360
|
+
const workflows = Object.freeze({ call, start });
|
|
1361
|
+
capabilityCache.set(capabilityProperty, workflows);
|
|
1362
|
+
return workflows;
|
|
1363
|
+
}
|
|
1364
|
+
const operationCache = new Map;
|
|
1365
|
+
const capability = new Proxy(Object.freeze({}), {
|
|
1366
|
+
get(_capabilityTarget, operationProperty) {
|
|
1367
|
+
if (typeof operationProperty !== "string")
|
|
1368
|
+
return;
|
|
1369
|
+
const known = operationCache.get(operationProperty);
|
|
1370
|
+
if (known !== undefined)
|
|
1371
|
+
return known;
|
|
1372
|
+
const invoke = async (rawInput = null, callOptions) => invokeCapability(capabilityProperty, operationProperty, capabilityProperty === "http" && operationProperty === "request" ? normalizeHttpRequest(rawInput) : capabilityProperty === "telegram" && operationProperty === "send" ? normalizeTelegramSend(rawInput) : capabilityProperty === "discord" && operationProperty === "send" ? normalizeDiscordSend(rawInput) : capabilityProperty === "whatsapp" && operationProperty === "send" ? normalizeWhatsAppSend(rawInput) : rawInput, callOptions);
|
|
1373
|
+
Object.freeze(invoke);
|
|
1374
|
+
operationCache.set(operationProperty, invoke);
|
|
1375
|
+
return invoke;
|
|
1376
|
+
},
|
|
1377
|
+
set: () => false,
|
|
1378
|
+
defineProperty: () => false,
|
|
1379
|
+
deleteProperty: () => false
|
|
1380
|
+
});
|
|
1381
|
+
capabilityCache.set(capabilityProperty, capability);
|
|
1382
|
+
return capability;
|
|
1383
|
+
},
|
|
1384
|
+
set: () => false,
|
|
1385
|
+
defineProperty: () => false,
|
|
1386
|
+
deleteProperty: () => false
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
class ModuleInitializationEffectError extends Error {
|
|
1391
|
+
constructor() {
|
|
1392
|
+
super("Fetch and managed services cannot be used while a WOML module is initializing.");
|
|
1393
|
+
this.name = "ModuleInitializationEffectError";
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
function servicePath(root, path) {
|
|
1397
|
+
let value = root;
|
|
1398
|
+
for (const part of path)
|
|
1399
|
+
value = Reflect.get(value, part);
|
|
1400
|
+
return value;
|
|
1401
|
+
}
|
|
1402
|
+
function guardedModuleServices(currentServices, invocationActive, path = []) {
|
|
1403
|
+
const callable = function guardedWomlModuleService() {};
|
|
1404
|
+
return new Proxy(callable, {
|
|
1405
|
+
get(_target, property) {
|
|
1406
|
+
return guardedModuleServices(currentServices, invocationActive, [
|
|
1407
|
+
...path,
|
|
1408
|
+
property
|
|
1409
|
+
]);
|
|
1410
|
+
},
|
|
1411
|
+
apply(_target, thisArgument, argumentsList) {
|
|
1412
|
+
if (!invocationActive())
|
|
1413
|
+
throw new ModuleInitializationEffectError;
|
|
1414
|
+
const value = servicePath(currentServices(), path);
|
|
1415
|
+
if (typeof value !== "function") {
|
|
1416
|
+
throw new TypeError(`services.${path.map(String).join(".")} is not callable.`);
|
|
1417
|
+
}
|
|
1418
|
+
return Reflect.apply(value, thisArgument, argumentsList);
|
|
1419
|
+
},
|
|
1420
|
+
set: () => false,
|
|
1421
|
+
defineProperty: () => false,
|
|
1422
|
+
deleteProperty: () => false
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1425
|
+
function mergedServiceFacade(builtIns, modules) {
|
|
1426
|
+
return new Proxy(Object.freeze({}), {
|
|
1427
|
+
get(_target, property) {
|
|
1428
|
+
if (typeof property === "string" && Object.hasOwn(modules, property)) {
|
|
1429
|
+
return modules[property];
|
|
1430
|
+
}
|
|
1431
|
+
return Reflect.get(builtIns, property);
|
|
1432
|
+
},
|
|
1433
|
+
set: () => false,
|
|
1434
|
+
defineProperty: () => false,
|
|
1435
|
+
deleteProperty: () => false
|
|
1436
|
+
});
|
|
1437
|
+
}
|
|
1438
|
+
async function loadRuntimeModules(request, builtIns, trackedFetch) {
|
|
1439
|
+
const imported = {};
|
|
1440
|
+
let moduleInvocationDepth = 0;
|
|
1441
|
+
let activeServices = builtIns;
|
|
1442
|
+
const invocationActive = () => moduleInvocationDepth > 0;
|
|
1443
|
+
const moduleServices = guardedModuleServices(() => activeServices, invocationActive);
|
|
1444
|
+
const moduleFetch = (...args) => {
|
|
1445
|
+
if (!invocationActive())
|
|
1446
|
+
throw new ModuleInitializationEffectError;
|
|
1447
|
+
return trackedFetch(...args);
|
|
1448
|
+
};
|
|
1449
|
+
Object.defineProperty(globalThis, "services", {
|
|
1450
|
+
configurable: false,
|
|
1451
|
+
enumerable: false,
|
|
1452
|
+
writable: false,
|
|
1453
|
+
value: moduleServices
|
|
1454
|
+
});
|
|
1455
|
+
Object.defineProperty(globalThis, "fetch", {
|
|
1456
|
+
configurable: false,
|
|
1457
|
+
enumerable: true,
|
|
1458
|
+
writable: false,
|
|
1459
|
+
value: moduleFetch
|
|
1460
|
+
});
|
|
1461
|
+
for (const module of request.modules ?? []) {
|
|
1462
|
+
const actualDigest = `sha256:${new Bun.CryptoHasher("sha256").update(module.bundle).digest("hex")}`;
|
|
1463
|
+
if (actualDigest !== module.bundleDigest) {
|
|
1464
|
+
throw new Error(`Module ${module.name} failed its Worker digest check.`);
|
|
1465
|
+
}
|
|
1466
|
+
if (module.sourceMap !== undefined) {
|
|
1467
|
+
const actualSourceMapDigest = `sha256:${new Bun.CryptoHasher("sha256").update(module.sourceMap).digest("hex")}`;
|
|
1468
|
+
if (actualSourceMapDigest !== module.sourceMapDigest) {
|
|
1469
|
+
throw new Error(`Module ${module.name} failed its source-map digest check.`);
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
const executableBundle = executableModuleBundle(module);
|
|
1473
|
+
const encoded = Buffer.from(executableBundle, "utf8").toString("base64");
|
|
1474
|
+
const namespace = await import(`data:text/javascript;base64,${encoded}`);
|
|
1475
|
+
const exposed = {};
|
|
1476
|
+
for (const exportName of module.exports) {
|
|
1477
|
+
const implementation = namespace[exportName];
|
|
1478
|
+
if (typeof implementation !== "function") {
|
|
1479
|
+
throw new TypeError(`Module ${module.name} export ${exportName} is not a function.`);
|
|
1480
|
+
}
|
|
1481
|
+
const wrapped = function womlImportedModuleFunction(...args) {
|
|
1482
|
+
moduleInvocationDepth += 1;
|
|
1483
|
+
try {
|
|
1484
|
+
const result = Reflect.apply(implementation, undefined, args);
|
|
1485
|
+
if (typeof result === "object" && result !== null || typeof result === "function") {
|
|
1486
|
+
const then = Reflect.get(result, "then");
|
|
1487
|
+
if (typeof then === "function") {
|
|
1488
|
+
return Promise.resolve(result).finally(() => {
|
|
1489
|
+
moduleInvocationDepth -= 1;
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
moduleInvocationDepth -= 1;
|
|
1494
|
+
return result;
|
|
1495
|
+
} catch (error) {
|
|
1496
|
+
moduleInvocationDepth -= 1;
|
|
1497
|
+
throw error;
|
|
1498
|
+
}
|
|
1499
|
+
};
|
|
1500
|
+
Object.freeze(wrapped);
|
|
1501
|
+
exposed[exportName] = wrapped;
|
|
1502
|
+
}
|
|
1503
|
+
imported[module.name] = Object.freeze(exposed);
|
|
1504
|
+
}
|
|
1505
|
+
activeServices = mergedServiceFacade(builtIns, imported);
|
|
1506
|
+
return activeServices;
|
|
1507
|
+
}
|
|
1508
|
+
async function execute(request) {
|
|
1509
|
+
let secretValues = [];
|
|
1510
|
+
try {
|
|
1511
|
+
const executionDeadline = performance.now() + request.timeoutMs;
|
|
1512
|
+
operationSequences.clear();
|
|
1513
|
+
automaticEffectfulCalls.clear();
|
|
1514
|
+
workflowTargetIdentityModes.clear();
|
|
1515
|
+
const context = deepFreezeJson({
|
|
1516
|
+
...request.context,
|
|
1517
|
+
payload: request.context.trigger
|
|
1518
|
+
});
|
|
1519
|
+
const attempt = request.attempt === undefined ? undefined : deepFreezeJson(request.attempt);
|
|
1520
|
+
const secrets = deepFreezeJson(request.bindings?.secrets ?? {});
|
|
1521
|
+
const lifecycle = request.lifecycle === undefined ? undefined : deepFreezeJson(request.lifecycle);
|
|
1522
|
+
const reusable = request.reusable === undefined ? undefined : deepFreezeJson(request.reusable);
|
|
1523
|
+
const reusableLifecycle = request.reusableLifecycle === undefined ? undefined : deepFreezeJson(request.reusableLifecycle);
|
|
1524
|
+
secretValues = Object.values(request.bindings?.secrets ?? {});
|
|
1525
|
+
const safeConsole = Object.freeze({
|
|
1526
|
+
log: (...values) => globalThis.console.error(redactKnownSecrets(values.map((value) => String(value)).join(" "), secretValues)),
|
|
1527
|
+
info: (...values) => globalThis.console.error(redactKnownSecrets(values.map((value) => String(value)).join(" "), secretValues)),
|
|
1528
|
+
warn: (...values) => globalThis.console.error(redactKnownSecrets(values.map((value) => String(value)).join(" "), secretValues)),
|
|
1529
|
+
error: (...values) => globalThis.console.error(redactKnownSecrets(values.map((value) => String(value)).join(" "), secretValues))
|
|
1530
|
+
});
|
|
1531
|
+
const builtInServices = deeplyReadonlyServiceFacade(request, executionDeadline);
|
|
1532
|
+
const nativeFetch2 = trackedNativeFetch(request);
|
|
1533
|
+
const services = request.modules === undefined ? builtInServices : await loadRuntimeModules(request, builtInServices, nativeFetch2);
|
|
1534
|
+
if (request.bindings !== undefined && request.modules === undefined) {
|
|
1535
|
+
Object.defineProperty(globalThis, "fetch", {
|
|
1536
|
+
configurable: false,
|
|
1537
|
+
enumerable: true,
|
|
1538
|
+
writable: false,
|
|
1539
|
+
value: nativeFetch2
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
const safeNodeId = request.nodeId.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
1543
|
+
const body = `"use strict";
|
|
1544
|
+
${request.source}
|
|
1545
|
+
//# sourceURL=woml-${request.mode === "lifecycle" ? "lifecycle" : "step"}-${safeNodeId}.js`;
|
|
1546
|
+
const script = request.bindings === undefined ? new AsyncFunction("context", "attempt", "console", body) : reusable !== undefined && reusableLifecycle !== undefined ? reusable.definition.kind === "step" ? new AsyncFunction("props", "context", "lifecycle", "services", "fetch", "console", body) : new AsyncFunction("props", "lifecycle", "services", "fetch", "console", body) : reusable !== undefined ? new AsyncFunction("props", "context", "attempt", "services", "fetch", "console", body) : request.mode === "lifecycle" ? new AsyncFunction("context", "lifecycle", "attempt", "services", "secrets", "fetch", "console", body) : new AsyncFunction("context", "attempt", "services", "secrets", "fetch", "console", body);
|
|
1547
|
+
let result = request.bindings === undefined ? await script(context, attempt, safeConsole) : reusable !== undefined && reusableLifecycle !== undefined ? reusable.definition.kind === "step" ? await script(reusable.props, context, reusableLifecycle, services, nativeFetch2, safeConsole) : await script(reusable.props, reusableLifecycle, services, nativeFetch2, safeConsole) : reusable !== undefined ? await script(reusable.props, context, attempt, services, nativeFetch2, safeConsole) : request.mode === "lifecycle" ? await script(context, lifecycle, attempt, services, secrets, nativeFetch2, safeConsole) : await script(context, attempt, services, secrets, nativeFetch2, safeConsole);
|
|
1548
|
+
if ((request.mode === "lifecycle" || reusableLifecycle !== undefined) && result === undefined) {
|
|
1549
|
+
result = null;
|
|
1550
|
+
}
|
|
1551
|
+
const violation = findJsonViolation(result);
|
|
1552
|
+
if (violation !== undefined) {
|
|
1553
|
+
self.postMessage({
|
|
1554
|
+
messageType: "completed",
|
|
1555
|
+
response: {
|
|
1556
|
+
ok: false,
|
|
1557
|
+
error: {
|
|
1558
|
+
kind: "non-json",
|
|
1559
|
+
name: "NonJsonResult",
|
|
1560
|
+
message: `${violation.path}: ${violation.reason}`
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
});
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
self.postMessage({
|
|
1567
|
+
messageType: "completed",
|
|
1568
|
+
response: { ok: true, result }
|
|
1569
|
+
});
|
|
1570
|
+
} catch (error) {
|
|
1571
|
+
self.postMessage({
|
|
1572
|
+
messageType: "completed",
|
|
1573
|
+
response: serializeError(error, secretValues, request.modules)
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
self.onmessage = (event) => {
|
|
1578
|
+
const message = event.data;
|
|
1579
|
+
if (message.messageType === "execute") {
|
|
1580
|
+
execute(message.request);
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1583
|
+
if (message.messageType === "fetch_observation_ack") {
|
|
1584
|
+
const pending2 = pendingFetchAcks.get(message.requestId);
|
|
1585
|
+
if (pending2 === undefined)
|
|
1586
|
+
return;
|
|
1587
|
+
pendingFetchAcks.delete(message.requestId);
|
|
1588
|
+
if (message.ack.accepted)
|
|
1589
|
+
pending2.resolve();
|
|
1590
|
+
else
|
|
1591
|
+
pending2.reject(new NativeFetchTrackingError(message.requestId, message.ack.error));
|
|
1592
|
+
return;
|
|
1593
|
+
}
|
|
1594
|
+
const pending = pendingCalls.get(message.callId);
|
|
1595
|
+
if (pending === undefined)
|
|
1596
|
+
return;
|
|
1597
|
+
pendingCalls.delete(message.callId);
|
|
1598
|
+
if (message.result.outcome === "succeeded") {
|
|
1599
|
+
const actualBytes = Buffer.byteLength(JSON.stringify(message.result.result), "utf8");
|
|
1600
|
+
if (actualBytes > 4194304 || actualBytes !== message.result.resultBytes) {
|
|
1601
|
+
pending.reject(new ServiceCallError(pending.capability, pending.operation, message.callId, {
|
|
1602
|
+
kind: "invalid_result",
|
|
1603
|
+
code: "WOML_CAPABILITY_RESULT_INVALID",
|
|
1604
|
+
message: "The capability result failed its byte-size contract.",
|
|
1605
|
+
retryable: false,
|
|
1606
|
+
ambiguous: false
|
|
1607
|
+
}));
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
pending.resolve(message.result.result);
|
|
1611
|
+
} else {
|
|
1612
|
+
pending.reject(new ServiceCallError(pending.capability, pending.operation, message.callId, message.result.error));
|
|
1613
|
+
}
|
|
1614
|
+
};
|
|
1615
|
+
|
|
1616
|
+
//# debugId=3B98B459CBEA2E9664756E2164756E21
|