@stackwright-services/capabilities 0.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/dist/index.d.mts +750 -0
- package/dist/index.d.ts +750 -0
- package/dist/index.js +1431 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1365 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +43 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1431 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
CapabilityRegistry: () => CapabilityRegistry,
|
|
24
|
+
CollectionAggregateInput: () => CollectionAggregateInput,
|
|
25
|
+
CollectionAggregateOutput: () => CollectionAggregateOutput,
|
|
26
|
+
CollectionFilterInput: () => CollectionFilterInput,
|
|
27
|
+
CollectionFilterOutput: () => CollectionFilterOutput,
|
|
28
|
+
CollectionJoinInput: () => CollectionJoinInput,
|
|
29
|
+
CollectionJoinOutput: () => CollectionJoinOutput,
|
|
30
|
+
DateShiftInput: () => DateShiftInput,
|
|
31
|
+
DateShiftOutput: () => DateShiftOutput,
|
|
32
|
+
EventsFilterInput: () => EventsFilterInput,
|
|
33
|
+
EventsFilterOutput: () => EventsFilterOutput,
|
|
34
|
+
EventsPublishInput: () => EventsPublishInput,
|
|
35
|
+
EventsPublishOutput: () => EventsPublishOutput,
|
|
36
|
+
NotificationChannel: () => NotificationChannel,
|
|
37
|
+
NotifyUserInput: () => NotifyUserInput,
|
|
38
|
+
NotifyUserOutput: () => NotifyUserOutput,
|
|
39
|
+
ServiceCallInput: () => ServiceCallInput,
|
|
40
|
+
ServiceCallOutput: () => ServiceCallOutput,
|
|
41
|
+
TextFormatInput: () => TextFormatInput,
|
|
42
|
+
TextFormatOutput: () => TextFormatOutput,
|
|
43
|
+
UnitsConvertInput: () => UnitsConvertInput,
|
|
44
|
+
UnitsConvertOutput: () => UnitsConvertOutput,
|
|
45
|
+
collectionAggregateHandler: () => collectionAggregateHandler,
|
|
46
|
+
collectionFilterHandler: () => collectionFilterHandler,
|
|
47
|
+
collectionJoinHandler: () => collectionJoinHandler,
|
|
48
|
+
dateShiftHandler: () => dateShiftHandler,
|
|
49
|
+
defaultRegistry: () => defaultRegistry,
|
|
50
|
+
evaluatePredicate: () => evaluatePredicate,
|
|
51
|
+
eventsFilterHandler: () => eventsFilterHandler,
|
|
52
|
+
eventsPublishDefinition: () => eventsPublishDefinition,
|
|
53
|
+
eventsPublishHandler: () => eventsPublishHandler,
|
|
54
|
+
notifyUserDefinition: () => notifyUserDefinition,
|
|
55
|
+
notifyUserHandler: () => notifyUserHandler,
|
|
56
|
+
registerAllCapabilities: () => registerAllCapabilities,
|
|
57
|
+
registerEffects: () => registerEffects,
|
|
58
|
+
registerTransforms: () => registerTransforms,
|
|
59
|
+
serviceCallHandler: () => serviceCallHandler,
|
|
60
|
+
stripBusPrefix: () => stripBusPrefix,
|
|
61
|
+
textFormatHandler: () => textFormatHandler,
|
|
62
|
+
unitsConvertHandler: () => unitsConvertHandler
|
|
63
|
+
});
|
|
64
|
+
module.exports = __toCommonJS(index_exports);
|
|
65
|
+
|
|
66
|
+
// src/registry.ts
|
|
67
|
+
var CapabilityRegistry = class {
|
|
68
|
+
capabilities = /* @__PURE__ */ new Map();
|
|
69
|
+
/**
|
|
70
|
+
* Register a capability. This is a security-relevant operation --
|
|
71
|
+
* every registration should be audited.
|
|
72
|
+
*
|
|
73
|
+
* @throws if a capability with the same name is already registered
|
|
74
|
+
*/
|
|
75
|
+
register(definition, handler, inputSchema, outputSchema) {
|
|
76
|
+
if (this.capabilities.has(definition.name)) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
`Capability "${definition.name}" is already registered. Duplicate registration is not allowed -- this is the audit chokepoint.`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
this.capabilities.set(definition.name, {
|
|
82
|
+
definition,
|
|
83
|
+
handler,
|
|
84
|
+
inputSchema,
|
|
85
|
+
outputSchema
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Look up a capability by name.
|
|
90
|
+
* @returns the registered capability, or undefined if not found
|
|
91
|
+
*/
|
|
92
|
+
lookup(name) {
|
|
93
|
+
return this.capabilities.get(name);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Get a capability by name, throwing if not found.
|
|
97
|
+
* @throws if the capability is not registered
|
|
98
|
+
*/
|
|
99
|
+
get(name) {
|
|
100
|
+
const capability = this.capabilities.get(name);
|
|
101
|
+
if (!capability) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`Capability "${name}" is not registered. Available capabilities: ${this.listNames().join(", ") || "(none)"}`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
return capability;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* List all registered capabilities.
|
|
110
|
+
*/
|
|
111
|
+
listAll() {
|
|
112
|
+
return Array.from(this.capabilities.values());
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* List all registered capability names.
|
|
116
|
+
*/
|
|
117
|
+
listNames() {
|
|
118
|
+
return Array.from(this.capabilities.keys());
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Filter capabilities by kind (transform or effect).
|
|
122
|
+
*/
|
|
123
|
+
filterByKind(kind) {
|
|
124
|
+
return this.listAll().filter((cap) => cap.definition.kind === kind);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Check if a capability is registered.
|
|
128
|
+
*/
|
|
129
|
+
has(name) {
|
|
130
|
+
return this.capabilities.has(name);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get the count of registered capabilities.
|
|
134
|
+
*/
|
|
135
|
+
get size() {
|
|
136
|
+
return this.capabilities.size;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Clear all registrations. Primarily for testing.
|
|
140
|
+
*/
|
|
141
|
+
clear() {
|
|
142
|
+
this.capabilities.clear();
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
var defaultRegistry = new CapabilityRegistry();
|
|
146
|
+
|
|
147
|
+
// src/transforms/collection-aggregate.ts
|
|
148
|
+
var import_zod = require("zod");
|
|
149
|
+
var AggregateOperation = import_zod.z.enum(["count", "sum", "avg", "min", "max"]);
|
|
150
|
+
var CollectionAggregateInput = import_zod.z.object({
|
|
151
|
+
items: import_zod.z.array(import_zod.z.unknown()),
|
|
152
|
+
field: import_zod.z.string().min(1),
|
|
153
|
+
operation: AggregateOperation
|
|
154
|
+
});
|
|
155
|
+
var CollectionAggregateOutput = import_zod.z.object({
|
|
156
|
+
result: import_zod.z.number()
|
|
157
|
+
});
|
|
158
|
+
function resolveFieldPath(item, path) {
|
|
159
|
+
const parts = path.split(".");
|
|
160
|
+
let current = item;
|
|
161
|
+
for (const part of parts) {
|
|
162
|
+
if (current === null || current === void 0 || typeof current !== "object") {
|
|
163
|
+
return void 0;
|
|
164
|
+
}
|
|
165
|
+
current = current[part];
|
|
166
|
+
}
|
|
167
|
+
return current;
|
|
168
|
+
}
|
|
169
|
+
function extractNumericValues(items, field) {
|
|
170
|
+
return items.map((item, index) => {
|
|
171
|
+
const value = resolveFieldPath(item, field);
|
|
172
|
+
if (typeof value !== "number") {
|
|
173
|
+
throw new Error(
|
|
174
|
+
`Field "${field}" at index ${index} is not a number (got ${typeof value}). Numeric aggregation requires all values to be numbers.`
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
return value;
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
var collectionAggregateHandler = async (input) => {
|
|
181
|
+
const { items, field, operation } = input;
|
|
182
|
+
if (operation === "count") {
|
|
183
|
+
return { result: items.length };
|
|
184
|
+
}
|
|
185
|
+
if (items.length === 0) {
|
|
186
|
+
throw new Error(`Cannot compute ${operation} on an empty collection`);
|
|
187
|
+
}
|
|
188
|
+
const values = extractNumericValues(items, field);
|
|
189
|
+
let result;
|
|
190
|
+
switch (operation) {
|
|
191
|
+
case "sum":
|
|
192
|
+
result = values.reduce((acc, v) => acc + v, 0);
|
|
193
|
+
break;
|
|
194
|
+
case "avg":
|
|
195
|
+
result = values.reduce((acc, v) => acc + v, 0) / values.length;
|
|
196
|
+
break;
|
|
197
|
+
case "min":
|
|
198
|
+
result = Math.min(...values);
|
|
199
|
+
break;
|
|
200
|
+
case "max":
|
|
201
|
+
result = Math.max(...values);
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
return { result };
|
|
205
|
+
};
|
|
206
|
+
var collectionAggregateDefinition = {
|
|
207
|
+
kind: "transform",
|
|
208
|
+
name: "collection.aggregate",
|
|
209
|
+
description: "Aggregate operations (count, sum, avg, min, max) on a field in an array",
|
|
210
|
+
inputSchema: "CollectionAggregateInput",
|
|
211
|
+
outputSchema: "CollectionAggregateOutput"
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// src/transforms/collection-filter.ts
|
|
215
|
+
var import_zod2 = require("zod");
|
|
216
|
+
var CollectionFilterInput = import_zod2.z.object({
|
|
217
|
+
items: import_zod2.z.array(import_zod2.z.unknown()),
|
|
218
|
+
conditions: import_zod2.z.array(
|
|
219
|
+
import_zod2.z.object({
|
|
220
|
+
field: import_zod2.z.string().min(1),
|
|
221
|
+
op: import_zod2.z.string().min(1),
|
|
222
|
+
value: import_zod2.z.unknown().optional(),
|
|
223
|
+
value_field: import_zod2.z.string().min(1).optional()
|
|
224
|
+
})
|
|
225
|
+
)
|
|
226
|
+
});
|
|
227
|
+
var CollectionFilterOutput = import_zod2.z.object({
|
|
228
|
+
items: import_zod2.z.array(import_zod2.z.unknown()),
|
|
229
|
+
count: import_zod2.z.number()
|
|
230
|
+
});
|
|
231
|
+
function resolveFieldPath2(item, path) {
|
|
232
|
+
const parts = path.split(".");
|
|
233
|
+
let current = item;
|
|
234
|
+
for (const part of parts) {
|
|
235
|
+
if (current === null || current === void 0 || typeof current !== "object") {
|
|
236
|
+
return void 0;
|
|
237
|
+
}
|
|
238
|
+
current = current[part];
|
|
239
|
+
}
|
|
240
|
+
return current;
|
|
241
|
+
}
|
|
242
|
+
function evaluatePredicate(item, condition) {
|
|
243
|
+
const fieldValue = resolveFieldPath2(item, condition.field);
|
|
244
|
+
const op = condition.op;
|
|
245
|
+
const target = condition.value;
|
|
246
|
+
switch (op) {
|
|
247
|
+
case "equals":
|
|
248
|
+
return fieldValue === target;
|
|
249
|
+
case "not_equals":
|
|
250
|
+
return fieldValue !== target;
|
|
251
|
+
case "in":
|
|
252
|
+
if (!Array.isArray(target)) return false;
|
|
253
|
+
return target.includes(fieldValue);
|
|
254
|
+
case "not_in":
|
|
255
|
+
if (!Array.isArray(target)) return true;
|
|
256
|
+
return !target.includes(fieldValue);
|
|
257
|
+
case "less_than":
|
|
258
|
+
return typeof fieldValue === "number" && typeof target === "number" ? fieldValue < target : false;
|
|
259
|
+
case "greater_than":
|
|
260
|
+
return typeof fieldValue === "number" && typeof target === "number" ? fieldValue > target : false;
|
|
261
|
+
case "less_than_or_equal":
|
|
262
|
+
return typeof fieldValue === "number" && typeof target === "number" ? fieldValue <= target : false;
|
|
263
|
+
case "greater_than_or_equal":
|
|
264
|
+
return typeof fieldValue === "number" && typeof target === "number" ? fieldValue >= target : false;
|
|
265
|
+
case "matches_prefix":
|
|
266
|
+
return typeof fieldValue === "string" && typeof target === "string" ? fieldValue.startsWith(target) : false;
|
|
267
|
+
case "contains":
|
|
268
|
+
return typeof fieldValue === "string" && typeof target === "string" ? fieldValue.includes(target) : false;
|
|
269
|
+
case "less_than_field": {
|
|
270
|
+
if (!condition.value_field) return false;
|
|
271
|
+
const otherValue = resolveFieldPath2(item, condition.value_field);
|
|
272
|
+
return typeof fieldValue === "number" && typeof otherValue === "number" ? fieldValue < otherValue : false;
|
|
273
|
+
}
|
|
274
|
+
case "greater_than_field": {
|
|
275
|
+
if (!condition.value_field) return false;
|
|
276
|
+
const otherValue = resolveFieldPath2(item, condition.value_field);
|
|
277
|
+
return typeof fieldValue === "number" && typeof otherValue === "number" ? fieldValue > otherValue : false;
|
|
278
|
+
}
|
|
279
|
+
case "equals_field": {
|
|
280
|
+
if (!condition.value_field) return false;
|
|
281
|
+
const otherValue = resolveFieldPath2(item, condition.value_field);
|
|
282
|
+
return fieldValue === otherValue;
|
|
283
|
+
}
|
|
284
|
+
default: {
|
|
285
|
+
const _exhaustive = op;
|
|
286
|
+
throw new Error(`Unsupported predicate operator: ${_exhaustive}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
var collectionFilterHandler = async (input) => {
|
|
291
|
+
const { items, conditions } = input;
|
|
292
|
+
const filtered = items.filter(
|
|
293
|
+
(item) => conditions.every((condition) => evaluatePredicate(item, condition))
|
|
294
|
+
);
|
|
295
|
+
return { items: filtered, count: filtered.length };
|
|
296
|
+
};
|
|
297
|
+
var collectionFilterDefinition = {
|
|
298
|
+
kind: "transform",
|
|
299
|
+
name: "collection.filter",
|
|
300
|
+
description: "Filter an array of objects using typed predicate conditions",
|
|
301
|
+
inputSchema: "CollectionFilterInput",
|
|
302
|
+
outputSchema: "CollectionFilterOutput"
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
// src/transforms/collection-join.ts
|
|
306
|
+
var import_zod3 = require("zod");
|
|
307
|
+
var CollectionJoinInput = import_zod3.z.object({
|
|
308
|
+
/** Left-side items array */
|
|
309
|
+
leftItems: import_zod3.z.array(import_zod3.z.unknown()),
|
|
310
|
+
/** Right-side items array */
|
|
311
|
+
rightItems: import_zod3.z.array(import_zod3.z.unknown()),
|
|
312
|
+
/** Field path on left items to join on */
|
|
313
|
+
leftField: import_zod3.z.string().min(1),
|
|
314
|
+
/** Field path on right items to join on */
|
|
315
|
+
rightField: import_zod3.z.string().min(1)
|
|
316
|
+
});
|
|
317
|
+
var CollectionJoinOutput = import_zod3.z.object({
|
|
318
|
+
/** Merged items where left[leftField] === right[rightField] */
|
|
319
|
+
items: import_zod3.z.array(
|
|
320
|
+
import_zod3.z.object({
|
|
321
|
+
left: import_zod3.z.unknown(),
|
|
322
|
+
right: import_zod3.z.unknown()
|
|
323
|
+
})
|
|
324
|
+
),
|
|
325
|
+
/** Count of matched items */
|
|
326
|
+
count: import_zod3.z.number()
|
|
327
|
+
});
|
|
328
|
+
function resolveFieldPath3(item, path) {
|
|
329
|
+
const parts = path.split(".");
|
|
330
|
+
let current = item;
|
|
331
|
+
for (const part of parts) {
|
|
332
|
+
if (current === null || current === void 0 || typeof current !== "object") {
|
|
333
|
+
return void 0;
|
|
334
|
+
}
|
|
335
|
+
current = current[part];
|
|
336
|
+
}
|
|
337
|
+
return current;
|
|
338
|
+
}
|
|
339
|
+
var collectionJoinHandler = async (input) => {
|
|
340
|
+
const { leftItems, rightItems, leftField, rightField } = input;
|
|
341
|
+
const rightIndex = /* @__PURE__ */ new Map();
|
|
342
|
+
for (const rightItem of rightItems) {
|
|
343
|
+
const key = resolveFieldPath3(rightItem, rightField);
|
|
344
|
+
if (key !== void 0) {
|
|
345
|
+
const existing = rightIndex.get(key) ?? [];
|
|
346
|
+
existing.push(rightItem);
|
|
347
|
+
rightIndex.set(key, existing);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
const items = [];
|
|
351
|
+
for (const leftItem of leftItems) {
|
|
352
|
+
const key = resolveFieldPath3(leftItem, leftField);
|
|
353
|
+
if (key !== void 0) {
|
|
354
|
+
const matches = rightIndex.get(key);
|
|
355
|
+
if (matches) {
|
|
356
|
+
for (const rightItem of matches) {
|
|
357
|
+
items.push({ left: leftItem, right: rightItem });
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return { items, count: items.length };
|
|
363
|
+
};
|
|
364
|
+
var collectionJoinDefinition = {
|
|
365
|
+
kind: "transform",
|
|
366
|
+
name: "collection.join",
|
|
367
|
+
description: "Join two arrays on matching field values for cross-domain data correlation",
|
|
368
|
+
inputSchema: "CollectionJoinInput",
|
|
369
|
+
outputSchema: "CollectionJoinOutput"
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
// src/transforms/collection-paginate.ts
|
|
373
|
+
var import_zod4 = require("zod");
|
|
374
|
+
var CollectionPaginateInput = import_zod4.z.object({
|
|
375
|
+
items: import_zod4.z.array(import_zod4.z.unknown()),
|
|
376
|
+
page: import_zod4.z.number().int().min(1),
|
|
377
|
+
pageSize: import_zod4.z.number().int().min(1).max(1e3)
|
|
378
|
+
});
|
|
379
|
+
var CollectionPaginateOutput = import_zod4.z.object({
|
|
380
|
+
items: import_zod4.z.array(import_zod4.z.unknown()),
|
|
381
|
+
total: import_zod4.z.number(),
|
|
382
|
+
page: import_zod4.z.number(),
|
|
383
|
+
pageSize: import_zod4.z.number(),
|
|
384
|
+
hasNext: import_zod4.z.boolean(),
|
|
385
|
+
hasPrevious: import_zod4.z.boolean()
|
|
386
|
+
});
|
|
387
|
+
var collectionPaginateHandler = async (input) => {
|
|
388
|
+
const { items, page, pageSize } = input;
|
|
389
|
+
const total = items.length;
|
|
390
|
+
const startIndex = (page - 1) * pageSize;
|
|
391
|
+
const pageItems = items.slice(startIndex, startIndex + pageSize);
|
|
392
|
+
return {
|
|
393
|
+
items: pageItems,
|
|
394
|
+
total,
|
|
395
|
+
page,
|
|
396
|
+
pageSize,
|
|
397
|
+
hasNext: startIndex + pageSize < total,
|
|
398
|
+
hasPrevious: page > 1
|
|
399
|
+
};
|
|
400
|
+
};
|
|
401
|
+
var collectionPaginateDefinition = {
|
|
402
|
+
kind: "transform",
|
|
403
|
+
name: "collection.paginate",
|
|
404
|
+
description: "Extract a page from an array with pagination metadata",
|
|
405
|
+
inputSchema: "CollectionPaginateInput",
|
|
406
|
+
outputSchema: "CollectionPaginateOutput"
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
// src/transforms/math-compute.ts
|
|
410
|
+
var import_zod5 = require("zod");
|
|
411
|
+
var MathOperation = import_zod5.z.object({
|
|
412
|
+
op: import_zod5.z.enum(["add", "subtract", "multiply", "divide", "modulo", "round", "ceil", "floor"]),
|
|
413
|
+
fields: import_zod5.z.array(import_zod5.z.string().min(1)).min(1),
|
|
414
|
+
// field paths to read operands from
|
|
415
|
+
result_field: import_zod5.z.string().min(1)
|
|
416
|
+
// dot-path where the result is written
|
|
417
|
+
});
|
|
418
|
+
var MathComputeInput = import_zod5.z.object({
|
|
419
|
+
data: import_zod5.z.record(import_zod5.z.unknown()),
|
|
420
|
+
operations: import_zod5.z.array(MathOperation).min(1)
|
|
421
|
+
});
|
|
422
|
+
var MathComputeOutput = import_zod5.z.object({
|
|
423
|
+
data: import_zod5.z.record(import_zod5.z.unknown()),
|
|
424
|
+
computed_fields: import_zod5.z.array(import_zod5.z.string())
|
|
425
|
+
});
|
|
426
|
+
function resolveFieldPath4(obj, path) {
|
|
427
|
+
const parts = path.split(".");
|
|
428
|
+
let current = obj;
|
|
429
|
+
for (const part of parts) {
|
|
430
|
+
if (current === null || current === void 0 || typeof current !== "object") {
|
|
431
|
+
return void 0;
|
|
432
|
+
}
|
|
433
|
+
current = current[part];
|
|
434
|
+
}
|
|
435
|
+
return current;
|
|
436
|
+
}
|
|
437
|
+
function setFieldPath(obj, path, value) {
|
|
438
|
+
const parts = path.split(".");
|
|
439
|
+
const lastKey = parts.pop();
|
|
440
|
+
if (lastKey === void 0) return;
|
|
441
|
+
let current = obj;
|
|
442
|
+
for (const key of parts) {
|
|
443
|
+
if (current[key] === null || current[key] === void 0 || typeof current[key] !== "object") {
|
|
444
|
+
current[key] = {};
|
|
445
|
+
}
|
|
446
|
+
current = current[key];
|
|
447
|
+
}
|
|
448
|
+
current[lastKey] = value;
|
|
449
|
+
}
|
|
450
|
+
function resolveNumber(data, field) {
|
|
451
|
+
const value = resolveFieldPath4(data, field);
|
|
452
|
+
return typeof value === "number" ? value : null;
|
|
453
|
+
}
|
|
454
|
+
var mathComputeHandler = async (input) => {
|
|
455
|
+
const { data, operations } = input;
|
|
456
|
+
const result = structuredClone(data);
|
|
457
|
+
const computedFields = [];
|
|
458
|
+
for (const { op, fields, result_field } of operations) {
|
|
459
|
+
const [firstField, secondField] = fields;
|
|
460
|
+
const a = firstField !== void 0 ? resolveNumber(result, firstField) : null;
|
|
461
|
+
let computed = null;
|
|
462
|
+
if (op === "round") {
|
|
463
|
+
computed = a !== null ? Math.round(a) : null;
|
|
464
|
+
} else if (op === "ceil") {
|
|
465
|
+
computed = a !== null ? Math.ceil(a) : null;
|
|
466
|
+
} else if (op === "floor") {
|
|
467
|
+
computed = a !== null ? Math.floor(a) : null;
|
|
468
|
+
} else {
|
|
469
|
+
const b = secondField !== void 0 ? resolveNumber(result, secondField) : null;
|
|
470
|
+
if (a !== null && b !== null) {
|
|
471
|
+
if (op === "add") computed = a + b;
|
|
472
|
+
else if (op === "subtract") computed = a - b;
|
|
473
|
+
else if (op === "multiply") computed = a * b;
|
|
474
|
+
else if (op === "divide") computed = b !== 0 ? a / b : null;
|
|
475
|
+
else if (op === "modulo") computed = b !== 0 ? a % b : null;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
setFieldPath(result, result_field, computed);
|
|
479
|
+
computedFields.push(result_field);
|
|
480
|
+
}
|
|
481
|
+
return { data: result, computed_fields: computedFields };
|
|
482
|
+
};
|
|
483
|
+
var mathComputeDefinition = {
|
|
484
|
+
kind: "transform",
|
|
485
|
+
name: "math.compute",
|
|
486
|
+
description: "Safe arithmetic operations on typed numeric fields. Operations: add, subtract, multiply, divide, modulo, round, ceil, floor.",
|
|
487
|
+
inputSchema: "MathComputeInput",
|
|
488
|
+
outputSchema: "MathComputeOutput"
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
// src/transforms/collection-sort.ts
|
|
492
|
+
var import_zod6 = require("zod");
|
|
493
|
+
var SortKey = import_zod6.z.object({
|
|
494
|
+
field: import_zod6.z.string().min(1),
|
|
495
|
+
// Explicit direction required — no silent defaults. Explicit is better than implicit.
|
|
496
|
+
direction: import_zod6.z.enum(["asc", "desc"])
|
|
497
|
+
});
|
|
498
|
+
var CollectionSortInput = import_zod6.z.object({
|
|
499
|
+
items: import_zod6.z.array(import_zod6.z.unknown()),
|
|
500
|
+
sortBy: import_zod6.z.array(SortKey).min(1)
|
|
501
|
+
});
|
|
502
|
+
var CollectionSortOutput = import_zod6.z.object({
|
|
503
|
+
items: import_zod6.z.array(import_zod6.z.unknown()),
|
|
504
|
+
count: import_zod6.z.number()
|
|
505
|
+
});
|
|
506
|
+
function resolveFieldPath5(item, path) {
|
|
507
|
+
const parts = path.split(".");
|
|
508
|
+
let current = item;
|
|
509
|
+
for (const part of parts) {
|
|
510
|
+
if (current === null || current === void 0 || typeof current !== "object") {
|
|
511
|
+
return void 0;
|
|
512
|
+
}
|
|
513
|
+
current = current[part];
|
|
514
|
+
}
|
|
515
|
+
return current;
|
|
516
|
+
}
|
|
517
|
+
function compareNonNilValues(a, b) {
|
|
518
|
+
if (typeof a === "number" && typeof b === "number") {
|
|
519
|
+
return a - b;
|
|
520
|
+
}
|
|
521
|
+
if (typeof a === "string" && typeof b === "string") {
|
|
522
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
523
|
+
}
|
|
524
|
+
if (typeof a === "number") return -1;
|
|
525
|
+
if (typeof b === "number") return 1;
|
|
526
|
+
const sa = String(a);
|
|
527
|
+
const sb = String(b);
|
|
528
|
+
return sa < sb ? -1 : sa > sb ? 1 : 0;
|
|
529
|
+
}
|
|
530
|
+
var collectionSortHandler = async (input) => {
|
|
531
|
+
const { items, sortBy } = input;
|
|
532
|
+
const sorted = [...items].sort((a, b) => {
|
|
533
|
+
for (const { field, direction } of sortBy) {
|
|
534
|
+
const aVal = resolveFieldPath5(a, field);
|
|
535
|
+
const bVal = resolveFieldPath5(b, field);
|
|
536
|
+
const aIsNil = aVal === null || aVal === void 0;
|
|
537
|
+
const bIsNil = bVal === null || bVal === void 0;
|
|
538
|
+
if (aIsNil && bIsNil) continue;
|
|
539
|
+
if (aIsNil) return 1;
|
|
540
|
+
if (bIsNil) return -1;
|
|
541
|
+
const cmp = compareNonNilValues(aVal, bVal);
|
|
542
|
+
if (cmp !== 0) {
|
|
543
|
+
return direction === "desc" ? -cmp : cmp;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
return 0;
|
|
547
|
+
});
|
|
548
|
+
return { items: sorted, count: sorted.length };
|
|
549
|
+
};
|
|
550
|
+
var collectionSortDefinition = {
|
|
551
|
+
kind: "transform",
|
|
552
|
+
name: "collection.sort",
|
|
553
|
+
description: "Sort arrays by one or more fields with typed direction (asc/desc)",
|
|
554
|
+
inputSchema: "CollectionSortInput",
|
|
555
|
+
outputSchema: "CollectionSortOutput"
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
// src/transforms/date-shift.ts
|
|
559
|
+
var import_zod7 = require("zod");
|
|
560
|
+
var DateShiftInput = import_zod7.z.object({
|
|
561
|
+
date: import_zod7.z.string().min(1),
|
|
562
|
+
duration: import_zod7.z.string().regex(
|
|
563
|
+
/^P(?:\d+D)?(?:T(?:\d+H)?(?:\d+M)?(?:\d+S)?)?$/,
|
|
564
|
+
'Duration must be ISO 8601 format (e.g. "P3D", "PT1H30M")'
|
|
565
|
+
),
|
|
566
|
+
direction: import_zod7.z.enum(["add", "subtract"])
|
|
567
|
+
});
|
|
568
|
+
var DateShiftOutput = import_zod7.z.object({
|
|
569
|
+
result: import_zod7.z.string()
|
|
570
|
+
});
|
|
571
|
+
function parseDuration(duration) {
|
|
572
|
+
const result = { days: 0, hours: 0, minutes: 0, seconds: 0 };
|
|
573
|
+
const dayMatch = duration.match(/(\d+)D/);
|
|
574
|
+
if (dayMatch?.[1]) result.days = parseInt(dayMatch[1], 10);
|
|
575
|
+
const timeSection = duration.match(/T(.+)$/);
|
|
576
|
+
if (timeSection?.[1]) {
|
|
577
|
+
const timePart = timeSection[1];
|
|
578
|
+
const hourMatch = timePart.match(/(\d+)H/);
|
|
579
|
+
const minMatch = timePart.match(/(\d+)M/);
|
|
580
|
+
const secMatch = timePart.match(/(\d+)S/);
|
|
581
|
+
if (hourMatch?.[1]) result.hours = parseInt(hourMatch[1], 10);
|
|
582
|
+
if (minMatch?.[1]) result.minutes = parseInt(minMatch[1], 10);
|
|
583
|
+
if (secMatch?.[1]) result.seconds = parseInt(secMatch[1], 10);
|
|
584
|
+
}
|
|
585
|
+
return result;
|
|
586
|
+
}
|
|
587
|
+
var dateShiftHandler = async (input) => {
|
|
588
|
+
const { date, duration, direction } = input;
|
|
589
|
+
const parsed = parseDuration(duration);
|
|
590
|
+
const totalMs = (parsed.days * 86400 + parsed.hours * 3600 + parsed.minutes * 60 + parsed.seconds) * 1e3;
|
|
591
|
+
const dateObj = new Date(date);
|
|
592
|
+
if (isNaN(dateObj.getTime())) {
|
|
593
|
+
throw new Error(`Invalid date: "${date}" is not a valid ISO 8601 date`);
|
|
594
|
+
}
|
|
595
|
+
const shift = direction === "add" ? totalMs : -totalMs;
|
|
596
|
+
const resultDate = new Date(dateObj.getTime() + shift);
|
|
597
|
+
return { result: resultDate.toISOString() };
|
|
598
|
+
};
|
|
599
|
+
var dateShiftDefinition = {
|
|
600
|
+
kind: "transform",
|
|
601
|
+
name: "date.shift",
|
|
602
|
+
description: "Add or subtract an ISO 8601 duration from a date",
|
|
603
|
+
inputSchema: "DateShiftInput",
|
|
604
|
+
outputSchema: "DateShiftOutput"
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
// src/transforms/events-filter.ts
|
|
608
|
+
var import_zod8 = require("zod");
|
|
609
|
+
var EventsFilterInput = import_zod8.z.object({
|
|
610
|
+
/** The event payload to evaluate */
|
|
611
|
+
event: import_zod8.z.unknown(),
|
|
612
|
+
/** Typed predicate conditions -- all must pass for the event to pass through */
|
|
613
|
+
conditions: import_zod8.z.array(
|
|
614
|
+
import_zod8.z.object({
|
|
615
|
+
field: import_zod8.z.string().min(1),
|
|
616
|
+
op: import_zod8.z.string().min(1),
|
|
617
|
+
value: import_zod8.z.unknown()
|
|
618
|
+
})
|
|
619
|
+
).min(1)
|
|
620
|
+
});
|
|
621
|
+
var EventsFilterOutput = import_zod8.z.object({
|
|
622
|
+
/** The event if it passed all conditions, or null if filtered out */
|
|
623
|
+
event: import_zod8.z.unknown().nullable(),
|
|
624
|
+
/** Whether the event passed all conditions */
|
|
625
|
+
passed: import_zod8.z.boolean()
|
|
626
|
+
});
|
|
627
|
+
var eventsFilterHandler = async (input) => {
|
|
628
|
+
const { event, conditions } = input;
|
|
629
|
+
const passed = conditions.every(
|
|
630
|
+
(condition) => evaluatePredicate(event, condition)
|
|
631
|
+
);
|
|
632
|
+
return {
|
|
633
|
+
event: passed ? event : null,
|
|
634
|
+
passed
|
|
635
|
+
};
|
|
636
|
+
};
|
|
637
|
+
var eventsFilterDefinition = {
|
|
638
|
+
kind: "transform",
|
|
639
|
+
name: "events.filter",
|
|
640
|
+
description: "Filter a single event payload against typed predicate conditions. Returns the event if all conditions pass, null if filtered out.",
|
|
641
|
+
inputSchema: "EventsFilterInput",
|
|
642
|
+
outputSchema: "EventsFilterOutput"
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
// src/transforms/text-format.ts
|
|
646
|
+
var import_zod9 = require("zod");
|
|
647
|
+
var TextFormatInput = import_zod9.z.object({
|
|
648
|
+
template: import_zod9.z.string(),
|
|
649
|
+
params: import_zod9.z.record(import_zod9.z.string(), import_zod9.z.union([import_zod9.z.string(), import_zod9.z.number(), import_zod9.z.boolean()]))
|
|
650
|
+
});
|
|
651
|
+
var TextFormatOutput = import_zod9.z.object({
|
|
652
|
+
result: import_zod9.z.string()
|
|
653
|
+
});
|
|
654
|
+
var textFormatHandler = async (input) => {
|
|
655
|
+
const { template, params } = input;
|
|
656
|
+
const result = template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
|
|
657
|
+
const value = params[key];
|
|
658
|
+
if (value === void 0) {
|
|
659
|
+
throw new Error(
|
|
660
|
+
`Missing template parameter: "${key}" (referenced in template but not provided)`
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
return String(value);
|
|
664
|
+
});
|
|
665
|
+
return { result };
|
|
666
|
+
};
|
|
667
|
+
var textFormatDefinition = {
|
|
668
|
+
kind: "transform",
|
|
669
|
+
name: "text.format",
|
|
670
|
+
description: "Template interpolation: replaces {{key}} placeholders with typed parameter values",
|
|
671
|
+
inputSchema: "TextFormatInput",
|
|
672
|
+
outputSchema: "TextFormatOutput"
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
// src/transforms/units-convert.ts
|
|
676
|
+
var import_zod10 = require("zod");
|
|
677
|
+
var UnitCategory = import_zod10.z.enum(["length", "weight", "temperature", "volume"]);
|
|
678
|
+
var UnitsConvertInput = import_zod10.z.object({
|
|
679
|
+
value: import_zod10.z.number(),
|
|
680
|
+
from: import_zod10.z.string().min(1),
|
|
681
|
+
to: import_zod10.z.string().min(1),
|
|
682
|
+
category: UnitCategory
|
|
683
|
+
});
|
|
684
|
+
var UnitsConvertOutput = import_zod10.z.object({
|
|
685
|
+
result: import_zod10.z.number(),
|
|
686
|
+
from: import_zod10.z.string(),
|
|
687
|
+
to: import_zod10.z.string()
|
|
688
|
+
});
|
|
689
|
+
var CONVERSION_FACTORS = {
|
|
690
|
+
length: {
|
|
691
|
+
m: 1,
|
|
692
|
+
km: 1e3,
|
|
693
|
+
cm: 0.01,
|
|
694
|
+
mm: 1e-3,
|
|
695
|
+
mi: 1609.344,
|
|
696
|
+
yd: 0.9144,
|
|
697
|
+
ft: 0.3048,
|
|
698
|
+
in: 0.0254,
|
|
699
|
+
nm: 1852
|
|
700
|
+
},
|
|
701
|
+
weight: {
|
|
702
|
+
kg: 1,
|
|
703
|
+
g: 1e-3,
|
|
704
|
+
mg: 1e-6,
|
|
705
|
+
lb: 0.453592,
|
|
706
|
+
oz: 0.0283495,
|
|
707
|
+
ton: 1e3
|
|
708
|
+
},
|
|
709
|
+
volume: {
|
|
710
|
+
l: 1,
|
|
711
|
+
ml: 1e-3,
|
|
712
|
+
gal: 3.78541,
|
|
713
|
+
qt: 0.946353,
|
|
714
|
+
pt: 0.473176,
|
|
715
|
+
cup: 0.236588,
|
|
716
|
+
floz: 0.0295735
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
function convertTemperature(value, from, to) {
|
|
720
|
+
let celsius;
|
|
721
|
+
switch (from) {
|
|
722
|
+
case "C":
|
|
723
|
+
celsius = value;
|
|
724
|
+
break;
|
|
725
|
+
case "F":
|
|
726
|
+
celsius = (value - 32) * 5 / 9;
|
|
727
|
+
break;
|
|
728
|
+
case "K":
|
|
729
|
+
celsius = value - 273.15;
|
|
730
|
+
break;
|
|
731
|
+
default:
|
|
732
|
+
throw new Error(`Unknown temperature unit: ${from}`);
|
|
733
|
+
}
|
|
734
|
+
switch (to) {
|
|
735
|
+
case "C":
|
|
736
|
+
return celsius;
|
|
737
|
+
case "F":
|
|
738
|
+
return celsius * 9 / 5 + 32;
|
|
739
|
+
case "K":
|
|
740
|
+
return celsius + 273.15;
|
|
741
|
+
default:
|
|
742
|
+
throw new Error(`Unknown temperature unit: ${to}`);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
var unitsConvertHandler = async (input) => {
|
|
746
|
+
const { value, from, to, category } = input;
|
|
747
|
+
let result;
|
|
748
|
+
if (category === "temperature") {
|
|
749
|
+
result = convertTemperature(value, from, to);
|
|
750
|
+
} else {
|
|
751
|
+
const factors = CONVERSION_FACTORS[category];
|
|
752
|
+
if (!factors) throw new Error(`Unknown unit category: ${category}`);
|
|
753
|
+
const fromFactor = factors[from];
|
|
754
|
+
const toFactor = factors[to];
|
|
755
|
+
if (fromFactor === void 0) throw new Error(`Unknown ${category} unit: ${from}`);
|
|
756
|
+
if (toFactor === void 0) throw new Error(`Unknown ${category} unit: ${to}`);
|
|
757
|
+
result = value * fromFactor / toFactor;
|
|
758
|
+
}
|
|
759
|
+
return { result, from, to };
|
|
760
|
+
};
|
|
761
|
+
var unitsConvertDefinition = {
|
|
762
|
+
kind: "transform",
|
|
763
|
+
name: "units.convert",
|
|
764
|
+
description: "Convert between measurement units (length, weight, temperature, volume)",
|
|
765
|
+
inputSchema: "UnitsConvertInput",
|
|
766
|
+
outputSchema: "UnitsConvertOutput"
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
// src/transforms/validation-check.ts
|
|
770
|
+
var import_types = require("@stackwright-services/types");
|
|
771
|
+
var import_zod11 = require("zod");
|
|
772
|
+
var ValidationRule = import_zod11.z.object({
|
|
773
|
+
/** The field path to validate (dot notation supported: "address.zip") */
|
|
774
|
+
field: import_zod11.z.string().min(1),
|
|
775
|
+
/** Predicate operator to apply */
|
|
776
|
+
op: import_zod11.z.string(),
|
|
777
|
+
/** Expected value for the predicate (literal value or field name for *_field ops) */
|
|
778
|
+
value: import_zod11.z.unknown(),
|
|
779
|
+
/** Optional human-readable error message (defaults to auto-generated) */
|
|
780
|
+
message: import_zod11.z.string().optional()
|
|
781
|
+
});
|
|
782
|
+
var ValidationCheckInput = import_zod11.z.object({
|
|
783
|
+
/** Data object to validate */
|
|
784
|
+
data: import_zod11.z.record(import_zod11.z.unknown()),
|
|
785
|
+
/** Validation rules to apply */
|
|
786
|
+
rules: import_zod11.z.array(ValidationRule).min(1)
|
|
787
|
+
});
|
|
788
|
+
var ValidationError = import_zod11.z.object({
|
|
789
|
+
/** Field that failed validation */
|
|
790
|
+
field: import_zod11.z.string(),
|
|
791
|
+
/** Operator that was applied */
|
|
792
|
+
rule: import_zod11.z.string(),
|
|
793
|
+
/** Human-readable error message */
|
|
794
|
+
message: import_zod11.z.string(),
|
|
795
|
+
/** The actual value found at the field */
|
|
796
|
+
actual: import_zod11.z.unknown().optional()
|
|
797
|
+
});
|
|
798
|
+
var ValidationCheckOutput = import_zod11.z.object({
|
|
799
|
+
/** Whether all rules passed */
|
|
800
|
+
valid: import_zod11.z.boolean(),
|
|
801
|
+
/** Number of rules that passed */
|
|
802
|
+
passed_count: import_zod11.z.number().int().min(0),
|
|
803
|
+
/** Number of rules that failed */
|
|
804
|
+
failed_count: import_zod11.z.number().int().min(0),
|
|
805
|
+
/** Detailed errors for each failed rule */
|
|
806
|
+
errors: import_zod11.z.array(ValidationError)
|
|
807
|
+
});
|
|
808
|
+
var validationCheckDefinition = {
|
|
809
|
+
kind: "transform",
|
|
810
|
+
name: "validation.check",
|
|
811
|
+
description: "Run typed predicates against data fields and return structured validation results. Pure transform that composes with all 13 predicate operators. Powers form validation.",
|
|
812
|
+
inputSchema: "ValidationCheckInput",
|
|
813
|
+
outputSchema: "ValidationCheckOutput"
|
|
814
|
+
};
|
|
815
|
+
function getNestedField(obj, path) {
|
|
816
|
+
const parts = path.split(".");
|
|
817
|
+
let current = obj;
|
|
818
|
+
for (const part of parts) {
|
|
819
|
+
if (current === null || current === void 0 || typeof current !== "object") {
|
|
820
|
+
return void 0;
|
|
821
|
+
}
|
|
822
|
+
current = current[part];
|
|
823
|
+
}
|
|
824
|
+
return current;
|
|
825
|
+
}
|
|
826
|
+
function defaultMessage(field, op, value) {
|
|
827
|
+
const valueStr = typeof value === "string" ? `"${value}"` : Array.isArray(value) ? `[${value.map((v) => JSON.stringify(v)).join(", ")}]` : JSON.stringify(value);
|
|
828
|
+
return `Field "${field}" failed ${op} check (expected: ${valueStr})`;
|
|
829
|
+
}
|
|
830
|
+
function toPredicateCondition(rule) {
|
|
831
|
+
const isFieldOp = import_types.FIELD_COMPARISON_OPERATORS.includes(rule.op);
|
|
832
|
+
if (isFieldOp) {
|
|
833
|
+
return {
|
|
834
|
+
field: rule.field,
|
|
835
|
+
op: rule.op,
|
|
836
|
+
value_field: String(rule.value)
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
return {
|
|
840
|
+
field: rule.field,
|
|
841
|
+
op: rule.op,
|
|
842
|
+
value: rule.value
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
var validationCheckHandler = async (input) => {
|
|
846
|
+
const { data, rules } = ValidationCheckInput.parse(input);
|
|
847
|
+
const errors = [];
|
|
848
|
+
for (const rule of rules) {
|
|
849
|
+
const fieldValue = getNestedField(data, rule.field);
|
|
850
|
+
const condition = toPredicateCondition(rule);
|
|
851
|
+
const passed = evaluatePredicate(data, condition);
|
|
852
|
+
if (!passed) {
|
|
853
|
+
errors.push({
|
|
854
|
+
field: rule.field,
|
|
855
|
+
rule: rule.op,
|
|
856
|
+
message: rule.message ?? defaultMessage(rule.field, rule.op, rule.value),
|
|
857
|
+
actual: fieldValue
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
return {
|
|
862
|
+
valid: errors.length === 0,
|
|
863
|
+
passed_count: rules.length - errors.length,
|
|
864
|
+
failed_count: errors.length,
|
|
865
|
+
errors
|
|
866
|
+
};
|
|
867
|
+
};
|
|
868
|
+
|
|
869
|
+
// src/transforms/index.ts
|
|
870
|
+
function registerTransforms(registry) {
|
|
871
|
+
registry.register(
|
|
872
|
+
unitsConvertDefinition,
|
|
873
|
+
unitsConvertHandler,
|
|
874
|
+
UnitsConvertInput,
|
|
875
|
+
UnitsConvertOutput
|
|
876
|
+
);
|
|
877
|
+
registry.register(textFormatDefinition, textFormatHandler, TextFormatInput, TextFormatOutput);
|
|
878
|
+
registry.register(
|
|
879
|
+
collectionFilterDefinition,
|
|
880
|
+
collectionFilterHandler,
|
|
881
|
+
CollectionFilterInput,
|
|
882
|
+
CollectionFilterOutput
|
|
883
|
+
);
|
|
884
|
+
registry.register(
|
|
885
|
+
collectionAggregateDefinition,
|
|
886
|
+
collectionAggregateHandler,
|
|
887
|
+
CollectionAggregateInput,
|
|
888
|
+
CollectionAggregateOutput
|
|
889
|
+
);
|
|
890
|
+
registry.register(
|
|
891
|
+
collectionJoinDefinition,
|
|
892
|
+
collectionJoinHandler,
|
|
893
|
+
CollectionJoinInput,
|
|
894
|
+
CollectionJoinOutput
|
|
895
|
+
);
|
|
896
|
+
registry.register(
|
|
897
|
+
collectionPaginateDefinition,
|
|
898
|
+
collectionPaginateHandler,
|
|
899
|
+
CollectionPaginateInput,
|
|
900
|
+
CollectionPaginateOutput
|
|
901
|
+
);
|
|
902
|
+
registry.register(mathComputeDefinition, mathComputeHandler, MathComputeInput, MathComputeOutput);
|
|
903
|
+
registry.register(
|
|
904
|
+
collectionSortDefinition,
|
|
905
|
+
collectionSortHandler,
|
|
906
|
+
CollectionSortInput,
|
|
907
|
+
CollectionSortOutput
|
|
908
|
+
);
|
|
909
|
+
registry.register(dateShiftDefinition, dateShiftHandler, DateShiftInput, DateShiftOutput);
|
|
910
|
+
registry.register(
|
|
911
|
+
eventsFilterDefinition,
|
|
912
|
+
eventsFilterHandler,
|
|
913
|
+
EventsFilterInput,
|
|
914
|
+
EventsFilterOutput
|
|
915
|
+
);
|
|
916
|
+
registry.register(
|
|
917
|
+
validationCheckDefinition,
|
|
918
|
+
validationCheckHandler,
|
|
919
|
+
ValidationCheckInput,
|
|
920
|
+
ValidationCheckOutput
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// src/effects/service-call.ts
|
|
925
|
+
var import_zod12 = require("zod");
|
|
926
|
+
var ServiceCallInput = import_zod12.z.object({
|
|
927
|
+
/** The URL to call (raw HTTP fallback — always required so the effect works without a provider) */
|
|
928
|
+
url: import_zod12.z.string().url(),
|
|
929
|
+
/** HTTP method */
|
|
930
|
+
method: import_zod12.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]),
|
|
931
|
+
/**
|
|
932
|
+
* Data source ID for provider delegation (e.g. "equipment-api").
|
|
933
|
+
* When provided alongside `operation`, and a DataSourceProvider in context can resolve
|
|
934
|
+
* this source, the call is delegated to the provider instead of raw HTTP.
|
|
935
|
+
* Falls back to `url`+`method` when no provider is installed or source is not known.
|
|
936
|
+
*/
|
|
937
|
+
source: import_zod12.z.string().optional(),
|
|
938
|
+
/**
|
|
939
|
+
* Operation name for provider delegation (e.g. "listUnits").
|
|
940
|
+
* Must be provided together with `source` to trigger provider delegation.
|
|
941
|
+
*/
|
|
942
|
+
operation: import_zod12.z.string().optional(),
|
|
943
|
+
/** Request headers */
|
|
944
|
+
headers: import_zod12.z.record(import_zod12.z.string(), import_zod12.z.string()).optional(),
|
|
945
|
+
/** Request body (for POST/PUT/PATCH, or provider operation params when delegating) */
|
|
946
|
+
body: import_zod12.z.unknown().optional(),
|
|
947
|
+
/** Expected response type (defaults to 'json') */
|
|
948
|
+
responseType: import_zod12.z.enum(["json", "text"]).optional(),
|
|
949
|
+
/** Timeout in milliseconds (defaults to 30000) */
|
|
950
|
+
timeout: import_zod12.z.number().positive().optional()
|
|
951
|
+
});
|
|
952
|
+
var ServiceCallOutput = import_zod12.z.object({
|
|
953
|
+
/** HTTP status code */
|
|
954
|
+
status: import_zod12.z.number(),
|
|
955
|
+
/** Response headers */
|
|
956
|
+
headers: import_zod12.z.record(import_zod12.z.string(), import_zod12.z.string()),
|
|
957
|
+
/** Response body */
|
|
958
|
+
body: import_zod12.z.unknown(),
|
|
959
|
+
/** Whether the call was successful (2xx status) */
|
|
960
|
+
ok: import_zod12.z.boolean()
|
|
961
|
+
});
|
|
962
|
+
var serviceCallHandler = async (input, context) => {
|
|
963
|
+
const { url, method, headers, body, source, operation } = input;
|
|
964
|
+
const responseType = input.responseType ?? "json";
|
|
965
|
+
const timeout = input.timeout ?? 3e4;
|
|
966
|
+
if (source !== void 0 && operation !== void 0) {
|
|
967
|
+
const provider = context.dataSourceProvider;
|
|
968
|
+
if (provider !== void 0) {
|
|
969
|
+
const knownSources = provider.listSources();
|
|
970
|
+
const canResolve = knownSources.some((s) => s.id === source);
|
|
971
|
+
if (canResolve) {
|
|
972
|
+
context.logger.info(
|
|
973
|
+
`service.call: delegating ${source}/${operation} to DataSourceProvider`,
|
|
974
|
+
{ source, operation }
|
|
975
|
+
);
|
|
976
|
+
const result = await provider.execute(source, operation, body);
|
|
977
|
+
return {
|
|
978
|
+
status: 200,
|
|
979
|
+
headers: {},
|
|
980
|
+
body: result.data,
|
|
981
|
+
ok: true
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
context.logger.info(
|
|
985
|
+
`service.call: source "${source}" not known to provider, falling back to HTTP`,
|
|
986
|
+
{ source, operation, url }
|
|
987
|
+
);
|
|
988
|
+
} else {
|
|
989
|
+
context.logger.info(`service.call: no DataSourceProvider in context, falling back to HTTP`, {
|
|
990
|
+
source,
|
|
991
|
+
operation,
|
|
992
|
+
url
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
context.logger.info(`service.call: ${method} ${url}`, { method, url });
|
|
997
|
+
const controller = new AbortController();
|
|
998
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
999
|
+
try {
|
|
1000
|
+
const requestHeaders = new Headers(headers);
|
|
1001
|
+
const fetchOptions = {
|
|
1002
|
+
method,
|
|
1003
|
+
headers: requestHeaders,
|
|
1004
|
+
signal: controller.signal
|
|
1005
|
+
};
|
|
1006
|
+
if (body !== void 0 && ["POST", "PUT", "PATCH"].includes(method)) {
|
|
1007
|
+
fetchOptions.body = JSON.stringify(body);
|
|
1008
|
+
if (!requestHeaders.has("Content-Type")) {
|
|
1009
|
+
requestHeaders.set("Content-Type", "application/json");
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
const response = await fetch(url, fetchOptions);
|
|
1013
|
+
const responseHeaders = {};
|
|
1014
|
+
response.headers.forEach((value, key) => {
|
|
1015
|
+
responseHeaders[key] = value;
|
|
1016
|
+
});
|
|
1017
|
+
let responseBody;
|
|
1018
|
+
if (responseType === "json") {
|
|
1019
|
+
try {
|
|
1020
|
+
responseBody = await response.json();
|
|
1021
|
+
} catch {
|
|
1022
|
+
responseBody = await response.text();
|
|
1023
|
+
}
|
|
1024
|
+
} else {
|
|
1025
|
+
responseBody = await response.text();
|
|
1026
|
+
}
|
|
1027
|
+
context.logger.info(`service.call: ${method} ${url} -> ${response.status}`, {
|
|
1028
|
+
status: response.status,
|
|
1029
|
+
ok: response.ok
|
|
1030
|
+
});
|
|
1031
|
+
return {
|
|
1032
|
+
status: response.status,
|
|
1033
|
+
headers: responseHeaders,
|
|
1034
|
+
body: responseBody,
|
|
1035
|
+
ok: response.ok
|
|
1036
|
+
};
|
|
1037
|
+
} catch (error) {
|
|
1038
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
1039
|
+
context.logger.error(`service.call: ${method} ${url} timed out after ${timeout}ms`);
|
|
1040
|
+
return {
|
|
1041
|
+
status: 408,
|
|
1042
|
+
headers: {},
|
|
1043
|
+
body: { error: `Request timed out after ${timeout}ms` },
|
|
1044
|
+
ok: false
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
throw error;
|
|
1048
|
+
} finally {
|
|
1049
|
+
clearTimeout(timeoutId);
|
|
1050
|
+
}
|
|
1051
|
+
};
|
|
1052
|
+
var serviceCallDefinition = {
|
|
1053
|
+
kind: "effect",
|
|
1054
|
+
name: "service.call",
|
|
1055
|
+
description: "Invoke an HTTP endpoint, or delegate to a DataSourceProvider when one is installed. Raw HTTP is the fallback escape hatch when no typed provider can resolve the target.",
|
|
1056
|
+
inputSchema: "ServiceCallInput",
|
|
1057
|
+
outputSchema: "ServiceCallOutput",
|
|
1058
|
+
requiredPermissions: [
|
|
1059
|
+
{
|
|
1060
|
+
resource: "endpoint:*",
|
|
1061
|
+
action: "invoke"
|
|
1062
|
+
}
|
|
1063
|
+
]
|
|
1064
|
+
};
|
|
1065
|
+
|
|
1066
|
+
// src/effects/events-publish.ts
|
|
1067
|
+
var import_zod13 = require("zod");
|
|
1068
|
+
var EventsPublishInput = import_zod13.z.object({
|
|
1069
|
+
/** Topic to publish to (e.g. "bus:equipment-status" or "equipment-status") */
|
|
1070
|
+
topic: import_zod13.z.string().min(1),
|
|
1071
|
+
/** Message payload to publish */
|
|
1072
|
+
payload: import_zod13.z.unknown(),
|
|
1073
|
+
/** Optional metadata to include with the message */
|
|
1074
|
+
metadata: import_zod13.z.object({
|
|
1075
|
+
/** Logical source identifier */
|
|
1076
|
+
source: import_zod13.z.string().optional(),
|
|
1077
|
+
/** Correlation ID for request-scoped grouping */
|
|
1078
|
+
correlationId: import_zod13.z.string().optional()
|
|
1079
|
+
}).optional()
|
|
1080
|
+
});
|
|
1081
|
+
var EventsPublishOutput = import_zod13.z.object({
|
|
1082
|
+
/** Provider-assigned message ID */
|
|
1083
|
+
messageId: import_zod13.z.string(),
|
|
1084
|
+
/** Topic the message was published to (without bus: prefix) */
|
|
1085
|
+
topic: import_zod13.z.string(),
|
|
1086
|
+
/** Whether publishing succeeded */
|
|
1087
|
+
success: import_zod13.z.boolean()
|
|
1088
|
+
});
|
|
1089
|
+
function stripBusPrefix(topic) {
|
|
1090
|
+
return topic.startsWith("bus:") ? topic.slice(4) : topic;
|
|
1091
|
+
}
|
|
1092
|
+
var eventsPublishHandler = async (input, context) => {
|
|
1093
|
+
const { payload, metadata } = input;
|
|
1094
|
+
const topic = stripBusPrefix(input.topic);
|
|
1095
|
+
if (!context.messageBus) {
|
|
1096
|
+
throw new Error(
|
|
1097
|
+
"events.publish: No MessageBusProvider available in context. Ensure the runtime injects a MessageBusProvider into CapabilityContext.messageBus. Set MESSAGE_BUS_PROVIDER environment variable or pass a provider at startup."
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
context.logger.info(`events.publish: publishing to "${topic}"`, { topic });
|
|
1101
|
+
const result = await context.messageBus.publish(topic, {
|
|
1102
|
+
payload,
|
|
1103
|
+
traceContext: { traceId: context.traceId },
|
|
1104
|
+
metadata: {
|
|
1105
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1106
|
+
...metadata?.source !== void 0 ? { source: metadata.source } : {},
|
|
1107
|
+
...metadata?.correlationId !== void 0 ? { correlationId: metadata.correlationId } : {}
|
|
1108
|
+
}
|
|
1109
|
+
});
|
|
1110
|
+
context.logger.info(`events.publish: published to "${topic}" -> ${result.messageId}`, {
|
|
1111
|
+
topic,
|
|
1112
|
+
messageId: result.messageId,
|
|
1113
|
+
success: result.success
|
|
1114
|
+
});
|
|
1115
|
+
return {
|
|
1116
|
+
messageId: result.messageId,
|
|
1117
|
+
topic: result.topic,
|
|
1118
|
+
success: result.success
|
|
1119
|
+
};
|
|
1120
|
+
};
|
|
1121
|
+
var eventsPublishDefinition = {
|
|
1122
|
+
kind: "effect",
|
|
1123
|
+
name: "events.publish",
|
|
1124
|
+
description: "Publish a message to a message bus topic. Declares bus publish permissions for least-privilege derivation.",
|
|
1125
|
+
inputSchema: "EventsPublishInput",
|
|
1126
|
+
outputSchema: "EventsPublishOutput",
|
|
1127
|
+
requiredPermissions: [
|
|
1128
|
+
{
|
|
1129
|
+
resource: "bus:*",
|
|
1130
|
+
action: "publish"
|
|
1131
|
+
}
|
|
1132
|
+
]
|
|
1133
|
+
};
|
|
1134
|
+
|
|
1135
|
+
// src/effects/notify-user.ts
|
|
1136
|
+
var import_zod14 = require("zod");
|
|
1137
|
+
var NotificationChannel = import_zod14.z.enum(["email", "sms", "push", "in-app"]);
|
|
1138
|
+
var NotifyUserInput = import_zod14.z.object({
|
|
1139
|
+
/** Recipient user identifier — the provider resolves this to a delivery address */
|
|
1140
|
+
recipient: import_zod14.z.string().min(1),
|
|
1141
|
+
/** Delivery channel */
|
|
1142
|
+
channel: NotificationChannel,
|
|
1143
|
+
/** Template name for the notification content */
|
|
1144
|
+
template: import_zod14.z.string().min(1),
|
|
1145
|
+
/** Typed payload for template interpolation */
|
|
1146
|
+
payload: import_zod14.z.unknown(),
|
|
1147
|
+
/** Optional subject line (for email, push) */
|
|
1148
|
+
subject: import_zod14.z.string().optional(),
|
|
1149
|
+
/** Optional metadata to include with the notification */
|
|
1150
|
+
metadata: import_zod14.z.object({
|
|
1151
|
+
/** Logical source identifier */
|
|
1152
|
+
source: import_zod14.z.string().optional(),
|
|
1153
|
+
/** Correlation ID for request-scoped grouping */
|
|
1154
|
+
correlationId: import_zod14.z.string().optional()
|
|
1155
|
+
}).optional()
|
|
1156
|
+
});
|
|
1157
|
+
var NotifyUserOutput = import_zod14.z.object({
|
|
1158
|
+
/** Provider-assigned notification ID */
|
|
1159
|
+
notificationId: import_zod14.z.string(),
|
|
1160
|
+
/** Channel the notification was sent through */
|
|
1161
|
+
channel: import_zod14.z.string(),
|
|
1162
|
+
/** Whether sending succeeded */
|
|
1163
|
+
success: import_zod14.z.boolean()
|
|
1164
|
+
});
|
|
1165
|
+
var notifyUserHandler = async (input, context) => {
|
|
1166
|
+
const { recipient, channel, template, payload, subject, metadata } = input;
|
|
1167
|
+
if (!context.notificationProvider) {
|
|
1168
|
+
throw new Error(
|
|
1169
|
+
"notify.user: No NotificationProvider available in context. Ensure the runtime injects a NotificationProvider into CapabilityContext.notificationProvider. Set NOTIFICATION_PROVIDER environment variable or pass a provider at startup."
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
1172
|
+
context.logger.info(`notify.user: sending "${template}" via ${channel} to "${recipient}"`, {
|
|
1173
|
+
recipient,
|
|
1174
|
+
channel,
|
|
1175
|
+
template
|
|
1176
|
+
});
|
|
1177
|
+
const result = await context.notificationProvider.send({
|
|
1178
|
+
recipient,
|
|
1179
|
+
channel,
|
|
1180
|
+
template,
|
|
1181
|
+
payload,
|
|
1182
|
+
...subject !== void 0 ? { subject } : {},
|
|
1183
|
+
traceContext: { traceId: context.traceId },
|
|
1184
|
+
metadata: {
|
|
1185
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1186
|
+
...metadata?.source !== void 0 ? { source: metadata.source } : {},
|
|
1187
|
+
...metadata?.correlationId !== void 0 ? { correlationId: metadata.correlationId } : {}
|
|
1188
|
+
}
|
|
1189
|
+
});
|
|
1190
|
+
context.logger.info(
|
|
1191
|
+
`notify.user: sent "${template}" via ${channel} to "${recipient}" -> ${result.notificationId}`,
|
|
1192
|
+
{
|
|
1193
|
+
notificationId: result.notificationId,
|
|
1194
|
+
channel: result.channel,
|
|
1195
|
+
success: result.success
|
|
1196
|
+
}
|
|
1197
|
+
);
|
|
1198
|
+
return {
|
|
1199
|
+
notificationId: result.notificationId,
|
|
1200
|
+
channel: result.channel,
|
|
1201
|
+
success: result.success
|
|
1202
|
+
};
|
|
1203
|
+
};
|
|
1204
|
+
var notifyUserDefinition = {
|
|
1205
|
+
kind: "effect",
|
|
1206
|
+
name: "notify.user",
|
|
1207
|
+
description: "Send a notification to a user via email, SMS, push, or in-app channel. Template-based with typed payload. Declares notification permissions for least-privilege derivation.",
|
|
1208
|
+
inputSchema: "NotifyUserInput",
|
|
1209
|
+
outputSchema: "NotifyUserOutput",
|
|
1210
|
+
requiredPermissions: [
|
|
1211
|
+
{
|
|
1212
|
+
resource: "notification:*",
|
|
1213
|
+
action: "send"
|
|
1214
|
+
}
|
|
1215
|
+
]
|
|
1216
|
+
};
|
|
1217
|
+
|
|
1218
|
+
// src/effects/http-webhook.ts
|
|
1219
|
+
var import_zod15 = require("zod");
|
|
1220
|
+
var HttpWebhookInput = import_zod15.z.object({
|
|
1221
|
+
/** Target URL -- must be declared statically in flow YAML */
|
|
1222
|
+
url: import_zod15.z.string().url(),
|
|
1223
|
+
/** HTTP method -- only POST or PUT for webhooks (defaults to POST) */
|
|
1224
|
+
method: import_zod15.z.enum(["POST", "PUT"]).optional(),
|
|
1225
|
+
/** Payload to send -- shape is flow-defined */
|
|
1226
|
+
payload: import_zod15.z.unknown(),
|
|
1227
|
+
/** Additional headers to include */
|
|
1228
|
+
headers: import_zod15.z.record(import_zod15.z.string()).optional(),
|
|
1229
|
+
/** Request timeout in milliseconds (100ms - 30s, defaults to 5000) */
|
|
1230
|
+
timeout_ms: import_zod15.z.number().int().min(100).max(3e4).optional(),
|
|
1231
|
+
/** Number of retry attempts on failure (0-3, defaults to 1) */
|
|
1232
|
+
retry_count: import_zod15.z.number().int().min(0).max(3).optional(),
|
|
1233
|
+
/** Optional HMAC-SHA256 secret for request signing */
|
|
1234
|
+
signature_secret: import_zod15.z.string().optional()
|
|
1235
|
+
});
|
|
1236
|
+
var HttpWebhookOutput = import_zod15.z.object({
|
|
1237
|
+
/** Whether the webhook was delivered successfully */
|
|
1238
|
+
success: import_zod15.z.boolean(),
|
|
1239
|
+
/** HTTP status code from the target */
|
|
1240
|
+
status_code: import_zod15.z.number().int(),
|
|
1241
|
+
/** Response body (truncated to 4KB for safety) */
|
|
1242
|
+
response_body: import_zod15.z.string().optional(),
|
|
1243
|
+
/** Number of attempts made (1 = first try succeeded) */
|
|
1244
|
+
attempts: import_zod15.z.number().int().min(1),
|
|
1245
|
+
/** Total elapsed time in milliseconds */
|
|
1246
|
+
elapsed_ms: import_zod15.z.number()
|
|
1247
|
+
});
|
|
1248
|
+
var httpWebhookDefinition = {
|
|
1249
|
+
kind: "effect",
|
|
1250
|
+
name: "http.webhook",
|
|
1251
|
+
description: "Invoke an outbound webhook with retry, timeout, and optional HMAC signature verification. More constrained than service.call -- URL is static and declared at compile time.",
|
|
1252
|
+
inputSchema: "HttpWebhookInput",
|
|
1253
|
+
outputSchema: "HttpWebhookOutput",
|
|
1254
|
+
requiredPermissions: [{ resource: "webhook:$url", action: "invoke" }]
|
|
1255
|
+
};
|
|
1256
|
+
async function computeHmacSignature(payload, secret) {
|
|
1257
|
+
const encoder = new TextEncoder();
|
|
1258
|
+
const key = await crypto.subtle.importKey(
|
|
1259
|
+
"raw",
|
|
1260
|
+
encoder.encode(secret),
|
|
1261
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
1262
|
+
false,
|
|
1263
|
+
["sign"]
|
|
1264
|
+
);
|
|
1265
|
+
const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(payload));
|
|
1266
|
+
return Array.from(new Uint8Array(signature)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1267
|
+
}
|
|
1268
|
+
var httpWebhookHandler = async (input, context) => {
|
|
1269
|
+
const parsed = HttpWebhookInput.parse(input);
|
|
1270
|
+
const url = parsed.url;
|
|
1271
|
+
const method = parsed.method ?? "POST";
|
|
1272
|
+
const payload = parsed.payload;
|
|
1273
|
+
const headers = parsed.headers;
|
|
1274
|
+
const timeout_ms = parsed.timeout_ms ?? 5e3;
|
|
1275
|
+
const retry_count = parsed.retry_count ?? 1;
|
|
1276
|
+
const signature_secret = parsed.signature_secret;
|
|
1277
|
+
const body = typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
1278
|
+
const requestHeaders = {
|
|
1279
|
+
"Content-Type": "application/json",
|
|
1280
|
+
"User-Agent": "stackwright-services/webhook",
|
|
1281
|
+
...headers
|
|
1282
|
+
};
|
|
1283
|
+
if (signature_secret) {
|
|
1284
|
+
const signature = await computeHmacSignature(body, signature_secret);
|
|
1285
|
+
requestHeaders["X-Webhook-Signature"] = `sha256=${signature}`;
|
|
1286
|
+
}
|
|
1287
|
+
if (context.traceId) {
|
|
1288
|
+
requestHeaders["X-Trace-Id"] = context.traceId;
|
|
1289
|
+
}
|
|
1290
|
+
context.logger.info(`http.webhook: ${method} ${url}`, { method, url });
|
|
1291
|
+
let lastError;
|
|
1292
|
+
let lastStatusCode = 0;
|
|
1293
|
+
let attempts = 0;
|
|
1294
|
+
const startTime = Date.now();
|
|
1295
|
+
for (let attempt = 0; attempt <= retry_count; attempt++) {
|
|
1296
|
+
attempts = attempt + 1;
|
|
1297
|
+
try {
|
|
1298
|
+
const controller = new AbortController();
|
|
1299
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout_ms);
|
|
1300
|
+
const response = await fetch(url, {
|
|
1301
|
+
method,
|
|
1302
|
+
headers: requestHeaders,
|
|
1303
|
+
body,
|
|
1304
|
+
signal: controller.signal
|
|
1305
|
+
});
|
|
1306
|
+
clearTimeout(timeoutId);
|
|
1307
|
+
const elapsed_ms2 = Date.now() - startTime;
|
|
1308
|
+
const responseText = await response.text();
|
|
1309
|
+
const response_body = responseText.length > 4096 ? responseText.slice(0, 4096) + "...[truncated]" : responseText;
|
|
1310
|
+
if (response.ok) {
|
|
1311
|
+
context.logger.info(`http.webhook: delivered successfully`, {
|
|
1312
|
+
status: response.status,
|
|
1313
|
+
attempts,
|
|
1314
|
+
elapsed_ms: elapsed_ms2
|
|
1315
|
+
});
|
|
1316
|
+
return {
|
|
1317
|
+
success: true,
|
|
1318
|
+
status_code: response.status,
|
|
1319
|
+
response_body,
|
|
1320
|
+
attempts,
|
|
1321
|
+
elapsed_ms: elapsed_ms2
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
|
|
1325
|
+
context.logger.warn(`http.webhook: non-retryable failure`, {
|
|
1326
|
+
status: response.status,
|
|
1327
|
+
attempts
|
|
1328
|
+
});
|
|
1329
|
+
return {
|
|
1330
|
+
success: false,
|
|
1331
|
+
status_code: response.status,
|
|
1332
|
+
response_body,
|
|
1333
|
+
attempts,
|
|
1334
|
+
elapsed_ms: elapsed_ms2
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
lastStatusCode = response.status;
|
|
1338
|
+
lastError = new Error(`HTTP ${response.status}: ${response_body}`);
|
|
1339
|
+
context.logger.warn(`http.webhook: retryable failure (attempt ${attempts})`, {
|
|
1340
|
+
status: response.status
|
|
1341
|
+
});
|
|
1342
|
+
} catch (error) {
|
|
1343
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
1344
|
+
if (lastError.name !== "AbortError" && lastError.name !== "TypeError") {
|
|
1345
|
+
break;
|
|
1346
|
+
}
|
|
1347
|
+
context.logger.warn(`http.webhook: network error (attempt ${attempts})`, {
|
|
1348
|
+
error: lastError.message
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
if (attempt < retry_count) {
|
|
1352
|
+
await new Promise((resolve) => setTimeout(resolve, 100 * Math.pow(2, attempt)));
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
const elapsed_ms = Date.now() - startTime;
|
|
1356
|
+
context.logger.error(`http.webhook: all retries exhausted`, {
|
|
1357
|
+
attempts,
|
|
1358
|
+
elapsed_ms,
|
|
1359
|
+
lastError: lastError?.message
|
|
1360
|
+
});
|
|
1361
|
+
return {
|
|
1362
|
+
success: false,
|
|
1363
|
+
status_code: lastStatusCode,
|
|
1364
|
+
response_body: lastError?.message ?? "Unknown error",
|
|
1365
|
+
attempts,
|
|
1366
|
+
elapsed_ms
|
|
1367
|
+
};
|
|
1368
|
+
};
|
|
1369
|
+
|
|
1370
|
+
// src/effects/index.ts
|
|
1371
|
+
function registerEffects(registry) {
|
|
1372
|
+
registry.register(serviceCallDefinition, serviceCallHandler, ServiceCallInput, ServiceCallOutput);
|
|
1373
|
+
registry.register(
|
|
1374
|
+
eventsPublishDefinition,
|
|
1375
|
+
eventsPublishHandler,
|
|
1376
|
+
EventsPublishInput,
|
|
1377
|
+
EventsPublishOutput
|
|
1378
|
+
);
|
|
1379
|
+
registry.register(notifyUserDefinition, notifyUserHandler, NotifyUserInput, NotifyUserOutput);
|
|
1380
|
+
registry.register(httpWebhookDefinition, httpWebhookHandler, HttpWebhookInput, HttpWebhookOutput);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
// src/index.ts
|
|
1384
|
+
function registerAllCapabilities(registry) {
|
|
1385
|
+
registerTransforms(registry);
|
|
1386
|
+
registerEffects(registry);
|
|
1387
|
+
}
|
|
1388
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1389
|
+
0 && (module.exports = {
|
|
1390
|
+
CapabilityRegistry,
|
|
1391
|
+
CollectionAggregateInput,
|
|
1392
|
+
CollectionAggregateOutput,
|
|
1393
|
+
CollectionFilterInput,
|
|
1394
|
+
CollectionFilterOutput,
|
|
1395
|
+
CollectionJoinInput,
|
|
1396
|
+
CollectionJoinOutput,
|
|
1397
|
+
DateShiftInput,
|
|
1398
|
+
DateShiftOutput,
|
|
1399
|
+
EventsFilterInput,
|
|
1400
|
+
EventsFilterOutput,
|
|
1401
|
+
EventsPublishInput,
|
|
1402
|
+
EventsPublishOutput,
|
|
1403
|
+
NotificationChannel,
|
|
1404
|
+
NotifyUserInput,
|
|
1405
|
+
NotifyUserOutput,
|
|
1406
|
+
ServiceCallInput,
|
|
1407
|
+
ServiceCallOutput,
|
|
1408
|
+
TextFormatInput,
|
|
1409
|
+
TextFormatOutput,
|
|
1410
|
+
UnitsConvertInput,
|
|
1411
|
+
UnitsConvertOutput,
|
|
1412
|
+
collectionAggregateHandler,
|
|
1413
|
+
collectionFilterHandler,
|
|
1414
|
+
collectionJoinHandler,
|
|
1415
|
+
dateShiftHandler,
|
|
1416
|
+
defaultRegistry,
|
|
1417
|
+
evaluatePredicate,
|
|
1418
|
+
eventsFilterHandler,
|
|
1419
|
+
eventsPublishDefinition,
|
|
1420
|
+
eventsPublishHandler,
|
|
1421
|
+
notifyUserDefinition,
|
|
1422
|
+
notifyUserHandler,
|
|
1423
|
+
registerAllCapabilities,
|
|
1424
|
+
registerEffects,
|
|
1425
|
+
registerTransforms,
|
|
1426
|
+
serviceCallHandler,
|
|
1427
|
+
stripBusPrefix,
|
|
1428
|
+
textFormatHandler,
|
|
1429
|
+
unitsConvertHandler
|
|
1430
|
+
});
|
|
1431
|
+
//# sourceMappingURL=index.js.map
|