wield-mcp 0.0.0-stage → 0.1.2

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.
@@ -0,0 +1,4228 @@
1
+ "use strict";
2
+ (() => {
3
+ // packages/recipe/src/errors.ts
4
+ var RecipeError = class extends Error {
5
+ code;
6
+ step;
7
+ constructor(code, message, step) {
8
+ super(message);
9
+ this.name = "RecipeError";
10
+ this.code = code;
11
+ this.step = step;
12
+ }
13
+ };
14
+ var TemplateError = class extends Error {
15
+ constructor(message) {
16
+ super(message);
17
+ this.name = "TemplateError";
18
+ }
19
+ };
20
+
21
+ // node_modules/.pnpm/@jmespath-community+jmespath@1.5.0/node_modules/@jmespath-community/jmespath/dist/index.esm.js
22
+ var __defProp = Object.defineProperty;
23
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
24
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
25
+ var CHILD_PROPS = ["left", "right", "condition", "child", "operand", "trueExpr", "falseExpr", "expression"];
26
+ function pushChildren(node, stack) {
27
+ const n = node;
28
+ for (const prop of CHILD_PROPS) {
29
+ const val = n[prop];
30
+ if (val && typeof val === "object" && "type" in val) {
31
+ stack.push(val);
32
+ }
33
+ }
34
+ if (Array.isArray(n.children)) {
35
+ for (const item of n.children) {
36
+ if (!item) {
37
+ continue;
38
+ }
39
+ stack.push(item);
40
+ if (item.type === "KeyValuePair" && item.value) {
41
+ stack.push(item.value);
42
+ }
43
+ }
44
+ }
45
+ if (Array.isArray(n.bindings)) {
46
+ for (const item of n.bindings) {
47
+ if (!item) {
48
+ continue;
49
+ }
50
+ stack.push(item);
51
+ if (item.reference) {
52
+ stack.push(item.reference);
53
+ }
54
+ }
55
+ }
56
+ }
57
+ function forEachNode(root, visitor) {
58
+ const stack = [root];
59
+ while (stack.length > 0) {
60
+ const current = stack.pop();
61
+ if (!current) {
62
+ break;
63
+ }
64
+ if (visitor(current) === false) {
65
+ return;
66
+ }
67
+ pushChildren(current, stack);
68
+ }
69
+ }
70
+ function countNodes(root) {
71
+ let count = 0;
72
+ forEachNode(root, () => {
73
+ count += 1;
74
+ return true;
75
+ });
76
+ return count;
77
+ }
78
+ var isObject = (obj) => {
79
+ return obj !== null && Object.prototype.toString.call(obj) === "[object Object]";
80
+ };
81
+ var strictDeepEqual = (first, second) => {
82
+ if (first === second) {
83
+ return true;
84
+ }
85
+ if (typeof first !== typeof second) {
86
+ return false;
87
+ }
88
+ if (Array.isArray(first) && Array.isArray(second)) {
89
+ if (first.length !== second.length) {
90
+ return false;
91
+ }
92
+ for (let i = 0; i < first.length; i += 1) {
93
+ if (!strictDeepEqual(first[i], second[i])) {
94
+ return false;
95
+ }
96
+ }
97
+ return true;
98
+ }
99
+ if (isObject(first) && isObject(second)) {
100
+ const keys = Object.keys(first);
101
+ if (keys.length !== Object.keys(second).length) {
102
+ return false;
103
+ }
104
+ return keys.every(
105
+ (key) => Object.prototype.hasOwnProperty.call(second, key) && strictDeepEqual(first[key], second[key])
106
+ );
107
+ }
108
+ return false;
109
+ };
110
+ var isFalse = (obj) => {
111
+ if (obj == null || obj === false) {
112
+ return true;
113
+ }
114
+ if (typeof obj === "string") {
115
+ return obj === "";
116
+ }
117
+ if (typeof obj === "object") {
118
+ return Array.isArray(obj) ? obj.length === 0 : Object.keys(obj).length === 0;
119
+ }
120
+ return false;
121
+ };
122
+ var isAlpha = (ch) => {
123
+ return ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch === "_";
124
+ };
125
+ var isNum = (ch) => {
126
+ return ch >= "0" && ch <= "9" || ch === "-";
127
+ };
128
+ var isAlphaNum = (ch) => {
129
+ return isAlpha(ch) || ch >= "0" && ch <= "9";
130
+ };
131
+ var ensureInteger = (value) => {
132
+ if (!(typeof value === "number") || Math.floor(value) !== value) {
133
+ throw new Error("invalid-value: expecting an integer.");
134
+ }
135
+ return value;
136
+ };
137
+ var ensurePositiveInteger = (value) => {
138
+ if (!(typeof value === "number") || value < 0 || Math.floor(value) !== value) {
139
+ throw new Error("invalid-value: expecting a non-negative integer.");
140
+ }
141
+ return value;
142
+ };
143
+ var ensureNumbers = (...operands) => {
144
+ for (let i = 0; i < operands.length; i++) {
145
+ if (operands[i] === null || operands[i] === void 0) {
146
+ throw new Error("not-a-number: undefined");
147
+ }
148
+ if (typeof operands[i] !== "number") {
149
+ throw new Error("not-a-number");
150
+ }
151
+ }
152
+ };
153
+ var notZero = (n) => {
154
+ n = +n;
155
+ if (!n) {
156
+ throw new Error("not-a-number: divide by zero");
157
+ }
158
+ return n;
159
+ };
160
+ var binOp = (fn) => (left, right) => {
161
+ ensureNumbers(left, right);
162
+ return fn(left, right);
163
+ };
164
+ var add = binOp((a, b) => a + b);
165
+ var sub = binOp((a, b) => a - b);
166
+ var mul = binOp((a, b) => a * b);
167
+ var divide = binOp((a, b) => a / notZero(b));
168
+ var div = binOp((a, b) => Math.floor(a / notZero(b)));
169
+ var mod = binOp((a, b) => a % notZero(b));
170
+ var basicTokens = {
171
+ "(": "Lparen",
172
+ ")": "Rparen",
173
+ "*": "Star",
174
+ ",": "Comma",
175
+ ".": "Dot",
176
+ ":": "Colon",
177
+ "@": "Current",
178
+ "]": "Rbracket",
179
+ "{": "Lbrace",
180
+ "}": "Rbrace",
181
+ "+": "Plus",
182
+ "%": "Modulo",
183
+ "?": "Question",
184
+ "\u2212": "Minus",
185
+ "\xD7": "Multiply",
186
+ "\xF7": "Divide"
187
+ /* TOK_DIVIDE */
188
+ };
189
+ var operatorStartToken = {
190
+ "!": true,
191
+ "<": true,
192
+ "=": true,
193
+ ">": true,
194
+ "&": true,
195
+ "|": true,
196
+ "/": true
197
+ };
198
+ var skipChars = {
199
+ " ": true,
200
+ "\n": true,
201
+ "\r": true,
202
+ " ": true
203
+ };
204
+ var StreamLexer = class {
205
+ constructor() {
206
+ __publicField(this, "_current", 0);
207
+ __publicField(this, "_enable_legacy_literals", false);
208
+ }
209
+ tokenize(stream, options) {
210
+ const tokens = [];
211
+ this._current = 0;
212
+ this._enable_legacy_literals = options?.enable_legacy_literals || false;
213
+ let start;
214
+ let identifier;
215
+ let token;
216
+ while (this._current < stream.length) {
217
+ if (isAlpha(stream[this._current])) {
218
+ start = this._current;
219
+ identifier = this.consumeUnquotedIdentifier(stream);
220
+ tokens.push({
221
+ start,
222
+ type: "UnquotedIdentifier",
223
+ value: identifier
224
+ });
225
+ } else if (basicTokens[stream[this._current]] !== void 0) {
226
+ tokens.push({
227
+ start: this._current,
228
+ type: basicTokens[stream[this._current]],
229
+ value: stream[this._current]
230
+ });
231
+ this._current += 1;
232
+ } else if (stream[this._current] === "$") {
233
+ start = this._current;
234
+ if (this._current + 1 < stream.length && isAlpha(stream[this._current + 1])) {
235
+ this._current += 1;
236
+ identifier = this.consumeUnquotedIdentifier(stream);
237
+ tokens.push({
238
+ start,
239
+ type: "Variable",
240
+ value: identifier
241
+ });
242
+ } else {
243
+ tokens.push({
244
+ start,
245
+ type: "Root",
246
+ value: stream[this._current]
247
+ });
248
+ this._current += 1;
249
+ }
250
+ } else if (stream[this._current] === "-") {
251
+ if (this._current + 1 < stream.length && isNum(stream[this._current + 1])) {
252
+ const token2 = this.consumeNumber(stream);
253
+ token2 && tokens.push(token2);
254
+ } else {
255
+ const token2 = {
256
+ start: this._current,
257
+ type: "Minus",
258
+ value: "-"
259
+ };
260
+ tokens.push(token2);
261
+ this._current += 1;
262
+ }
263
+ } else if (isNum(stream[this._current])) {
264
+ token = this.consumeNumber(stream);
265
+ tokens.push(token);
266
+ } else if (stream[this._current] === "[") {
267
+ token = this.consumeLBracket(stream);
268
+ tokens.push(token);
269
+ } else if (stream[this._current] === '"') {
270
+ start = this._current;
271
+ identifier = this.consumeQuotedIdentifier(stream);
272
+ tokens.push({
273
+ start,
274
+ type: "QuotedIdentifier",
275
+ value: identifier
276
+ });
277
+ } else if (stream[this._current] === `'`) {
278
+ start = this._current;
279
+ identifier = this.consumeRawStringLiteral(stream);
280
+ tokens.push({
281
+ start,
282
+ type: "Literal",
283
+ value: identifier
284
+ });
285
+ } else if (stream[this._current] === "`") {
286
+ start = this._current;
287
+ const literal = this.consumeLiteral(stream);
288
+ tokens.push({
289
+ start,
290
+ type: "Literal",
291
+ value: literal
292
+ });
293
+ } else if (operatorStartToken[stream[this._current]] !== void 0) {
294
+ token = this.consumeOperator(stream);
295
+ token && tokens.push(token);
296
+ } else if (skipChars[stream[this._current]] !== void 0) {
297
+ this._current += 1;
298
+ } else {
299
+ const error = new Error(`Syntax error: unknown character: ${stream[this._current]}`);
300
+ error.name = "LexerError";
301
+ throw error;
302
+ }
303
+ }
304
+ return tokens;
305
+ }
306
+ consumeUnquotedIdentifier(stream) {
307
+ const start = this._current;
308
+ this._current += 1;
309
+ while (this._current < stream.length && isAlphaNum(stream[this._current])) {
310
+ this._current += 1;
311
+ }
312
+ return stream.slice(start, this._current);
313
+ }
314
+ consumeUntil(stream, delimiter) {
315
+ const start = this._current;
316
+ this._current += 1;
317
+ const len = stream.length;
318
+ while (this._current < len && stream[this._current] !== delimiter) {
319
+ if (stream[this._current] === "\\" && (stream[this._current + 1] === "\\" || stream[this._current + 1] === delimiter)) {
320
+ this._current += 2;
321
+ } else {
322
+ this._current += 1;
323
+ }
324
+ }
325
+ if (this._current < len) {
326
+ this._current += 1;
327
+ }
328
+ return stream.slice(start, this._current);
329
+ }
330
+ consumeQuotedIdentifier(stream) {
331
+ const raw = this.consumeUntil(stream, '"');
332
+ const [value, ok] = this.parseJSON(raw);
333
+ if (!ok) {
334
+ const error = new Error("syntax: unexpected end of JSON input");
335
+ error.name = "LexerError";
336
+ throw error;
337
+ }
338
+ return value;
339
+ }
340
+ consumeRawStringLiteral(stream) {
341
+ const raw = this.consumeUntil(stream, "'");
342
+ if (!raw.endsWith("'") || raw.length < 2) {
343
+ const error = new Error("Syntax error: unclosed single quote");
344
+ error.name = "LexerError";
345
+ throw error;
346
+ }
347
+ const content = raw.slice(1, -1);
348
+ return content.replace(/\\(['\\])/g, "$1");
349
+ }
350
+ consumeNumber(stream) {
351
+ const start = this._current;
352
+ this._current += 1;
353
+ const maxLength = stream.length;
354
+ while (isNum(stream[this._current]) && this._current < maxLength) {
355
+ this._current += 1;
356
+ }
357
+ const value = parseInt(stream.slice(start, this._current), 10);
358
+ return {
359
+ start,
360
+ value,
361
+ type: "Number"
362
+ /* TOK_NUMBER */
363
+ };
364
+ }
365
+ consumeLBracket(stream) {
366
+ const start = this._current;
367
+ this._current += 1;
368
+ if (stream[this._current] === "?") {
369
+ this._current += 1;
370
+ return { start, type: "Filter", value: "[?" };
371
+ }
372
+ if (stream[this._current] === "]") {
373
+ this._current += 1;
374
+ return { start, type: "Flatten", value: "[]" };
375
+ }
376
+ return { start, type: "Lbracket", value: "[" };
377
+ }
378
+ consumeOrElse(stream, peek, token, orElse) {
379
+ const start = this._current;
380
+ this._current += 1;
381
+ if (this._current < stream.length && stream[this._current] === peek) {
382
+ this._current += 1;
383
+ return {
384
+ start,
385
+ type: orElse,
386
+ value: stream.slice(start, this._current)
387
+ };
388
+ }
389
+ return { start, type: token, value: stream[start] };
390
+ }
391
+ consumeOperator(stream) {
392
+ const start = this._current;
393
+ const startingChar = stream[start];
394
+ switch (startingChar) {
395
+ case "!":
396
+ return this.consumeOrElse(
397
+ stream,
398
+ "=",
399
+ "Not",
400
+ "NE"
401
+ /* TOK_NE */
402
+ );
403
+ case "<":
404
+ return this.consumeOrElse(
405
+ stream,
406
+ "=",
407
+ "LT",
408
+ "LTE"
409
+ /* TOK_LTE */
410
+ );
411
+ case ">":
412
+ return this.consumeOrElse(
413
+ stream,
414
+ "=",
415
+ "GT",
416
+ "GTE"
417
+ /* TOK_GTE */
418
+ );
419
+ case "=":
420
+ return this.consumeOrElse(
421
+ stream,
422
+ "=",
423
+ "Assign",
424
+ "EQ"
425
+ /* TOK_EQ */
426
+ );
427
+ case "&":
428
+ return this.consumeOrElse(
429
+ stream,
430
+ "&",
431
+ "Expref",
432
+ "And"
433
+ /* TOK_AND */
434
+ );
435
+ case "|":
436
+ return this.consumeOrElse(
437
+ stream,
438
+ "|",
439
+ "Pipe",
440
+ "Or"
441
+ /* TOK_OR */
442
+ );
443
+ case "/":
444
+ return this.consumeOrElse(
445
+ stream,
446
+ "/",
447
+ "Divide",
448
+ "Div"
449
+ /* TOK_DIV */
450
+ );
451
+ }
452
+ }
453
+ consumeLiteral(stream) {
454
+ const raw = this.consumeUntil(stream, "`");
455
+ const inner = raw.endsWith("`") && raw.length > 1 ? raw.slice(1, -1) : raw.slice(1);
456
+ const literalString = inner.trimStart().replace("\\`", "`");
457
+ let [literal, ok] = this.parseJSON(literalString);
458
+ if (!ok && this._enable_legacy_literals) {
459
+ [literal, ok] = this.parseJSON(`"${literalString}"`);
460
+ }
461
+ if (!ok) {
462
+ const error = new Error(
463
+ `Syntax error: unexpected end of JSON input or invalid format for a JSON literal: ${stream[this._current - 1] ?? ""}`
464
+ );
465
+ error.name = "LexerError";
466
+ throw error;
467
+ }
468
+ return literal;
469
+ }
470
+ parseJSON(text) {
471
+ try {
472
+ const json = JSON.parse(text);
473
+ return [json, true];
474
+ } catch {
475
+ return [null, false];
476
+ }
477
+ }
478
+ };
479
+ var Lexer = new StreamLexer();
480
+ var Lexer_default = Lexer;
481
+ var bindingPower = {
482
+ [
483
+ "EOF"
484
+ /* TOK_EOF */
485
+ ]: 0,
486
+ [
487
+ "Variable"
488
+ /* TOK_VARIABLE */
489
+ ]: 0,
490
+ [
491
+ "UnquotedIdentifier"
492
+ /* TOK_UNQUOTEDIDENTIFIER */
493
+ ]: 0,
494
+ [
495
+ "QuotedIdentifier"
496
+ /* TOK_QUOTEDIDENTIFIER */
497
+ ]: 0,
498
+ [
499
+ "Rbracket"
500
+ /* TOK_RBRACKET */
501
+ ]: 0,
502
+ [
503
+ "Rparen"
504
+ /* TOK_RPAREN */
505
+ ]: 0,
506
+ [
507
+ "Comma"
508
+ /* TOK_COMMA */
509
+ ]: 0,
510
+ [
511
+ "Rbrace"
512
+ /* TOK_RBRACE */
513
+ ]: 0,
514
+ [
515
+ "Number"
516
+ /* TOK_NUMBER */
517
+ ]: 0,
518
+ [
519
+ "Current"
520
+ /* TOK_CURRENT */
521
+ ]: 0,
522
+ [
523
+ "Expref"
524
+ /* TOK_EXPREF */
525
+ ]: 0,
526
+ [
527
+ "Root"
528
+ /* TOK_ROOT */
529
+ ]: 0,
530
+ [
531
+ "Assign"
532
+ /* TOK_ASSIGN */
533
+ ]: 1,
534
+ [
535
+ "Pipe"
536
+ /* TOK_PIPE */
537
+ ]: 1,
538
+ [
539
+ "Question"
540
+ /* TOK_QUESTION */
541
+ ]: 2,
542
+ [
543
+ "Or"
544
+ /* TOK_OR */
545
+ ]: 3,
546
+ [
547
+ "And"
548
+ /* TOK_AND */
549
+ ]: 4,
550
+ [
551
+ "EQ"
552
+ /* TOK_EQ */
553
+ ]: 5,
554
+ [
555
+ "GT"
556
+ /* TOK_GT */
557
+ ]: 5,
558
+ [
559
+ "LT"
560
+ /* TOK_LT */
561
+ ]: 5,
562
+ [
563
+ "GTE"
564
+ /* TOK_GTE */
565
+ ]: 5,
566
+ [
567
+ "LTE"
568
+ /* TOK_LTE */
569
+ ]: 5,
570
+ [
571
+ "NE"
572
+ /* TOK_NE */
573
+ ]: 5,
574
+ [
575
+ "Minus"
576
+ /* TOK_MINUS */
577
+ ]: 6,
578
+ [
579
+ "Plus"
580
+ /* TOK_PLUS */
581
+ ]: 6,
582
+ [
583
+ "Div"
584
+ /* TOK_DIV */
585
+ ]: 7,
586
+ [
587
+ "Divide"
588
+ /* TOK_DIVIDE */
589
+ ]: 7,
590
+ [
591
+ "Modulo"
592
+ /* TOK_MODULO */
593
+ ]: 7,
594
+ [
595
+ "Multiply"
596
+ /* TOK_MULTIPLY */
597
+ ]: 7,
598
+ [
599
+ "Flatten"
600
+ /* TOK_FLATTEN */
601
+ ]: 9,
602
+ [
603
+ "Star"
604
+ /* TOK_STAR */
605
+ ]: 20,
606
+ [
607
+ "Filter"
608
+ /* TOK_FILTER */
609
+ ]: 21,
610
+ [
611
+ "Dot"
612
+ /* TOK_DOT */
613
+ ]: 40,
614
+ [
615
+ "Not"
616
+ /* TOK_NOT */
617
+ ]: 45,
618
+ [
619
+ "Lbrace"
620
+ /* TOK_LBRACE */
621
+ ]: 50,
622
+ [
623
+ "Lbracket"
624
+ /* TOK_LBRACKET */
625
+ ]: 55,
626
+ [
627
+ "Lparen"
628
+ /* TOK_LPAREN */
629
+ ]: 60
630
+ };
631
+ var DEFAULT_MAX_DEPTH = 1e3;
632
+ var TokenParser = class _TokenParser {
633
+ constructor() {
634
+ __publicField(this, "index", 0);
635
+ __publicField(this, "tokens", []);
636
+ __publicField(this, "depth", 0);
637
+ __publicField(this, "maxDepth", DEFAULT_MAX_DEPTH);
638
+ __publicField(this, "nodeCount", 0);
639
+ __publicField(this, "maxNodes");
640
+ }
641
+ parse(expression, options) {
642
+ this.loadTokens(expression, options || { enable_legacy_literals: false });
643
+ this.index = 0;
644
+ this.depth = 0;
645
+ this.nodeCount = 0;
646
+ this.maxDepth = options?.maxDepth ?? DEFAULT_MAX_DEPTH;
647
+ this.maxNodes = options?.maxNodes;
648
+ const ast = this.expression(0);
649
+ if (this.lookahead(0) !== "EOF") {
650
+ const token = this.lookaheadToken(0);
651
+ this.errorToken(token, `Syntax error: unexpected token type: ${token.type}, value: ${token.value}`);
652
+ }
653
+ if (this.maxNodes !== void 0 && countNodes(ast) > this.maxNodes) {
654
+ throw new Error(`Expression exceeds maximum allowed node count of ${this.maxNodes}`);
655
+ }
656
+ return ast;
657
+ }
658
+ loadTokens(expression, options) {
659
+ this.tokens = Lexer_default.tokenize(expression, options);
660
+ this.tokens.push({ type: "EOF", value: "", start: expression.length });
661
+ }
662
+ expression(rbp) {
663
+ if (this.depth >= this.maxDepth) {
664
+ throw new Error(`Expression exceeds maximum allowed depth of ${this.maxDepth}`);
665
+ }
666
+ this.depth += 1;
667
+ this.trackNode();
668
+ try {
669
+ const leftToken = this.lookaheadToken(0);
670
+ this.advance();
671
+ let left = this.nud(leftToken);
672
+ let currentTokenType = this.lookahead(0);
673
+ while (rbp < bindingPower[currentTokenType]) {
674
+ this.trackNode();
675
+ this.advance();
676
+ left = this.led(currentTokenType, left);
677
+ currentTokenType = this.lookahead(0);
678
+ }
679
+ return left;
680
+ } finally {
681
+ this.depth -= 1;
682
+ }
683
+ }
684
+ trackNode() {
685
+ this.nodeCount += 1;
686
+ if (this.maxNodes !== void 0 && this.nodeCount > this.maxNodes) {
687
+ throw new Error(`Expression exceeds maximum allowed node count of ${this.maxNodes}`);
688
+ }
689
+ }
690
+ lookahead(offset) {
691
+ return this.tokens[this.index + offset].type;
692
+ }
693
+ lookaheadToken(offset) {
694
+ return this.tokens[this.index + offset];
695
+ }
696
+ advance() {
697
+ this.index += 1;
698
+ }
699
+ nud(token) {
700
+ switch (token.type) {
701
+ case "Variable":
702
+ return { type: "Variable", name: token.value };
703
+ case "Literal":
704
+ return { type: "Literal", value: token.value };
705
+ case "UnquotedIdentifier": {
706
+ if (_TokenParser.isKeyword(token, "let") && this.lookahead(0) === "Variable") {
707
+ return this.parseLetExpression();
708
+ } else {
709
+ return { type: "Field", name: token.value };
710
+ }
711
+ }
712
+ case "QuotedIdentifier":
713
+ if (this.lookahead(0) === "Lparen") {
714
+ throw new Error("Syntax error: quoted identifier not allowed for function names.");
715
+ } else {
716
+ return { type: "Field", name: token.value };
717
+ }
718
+ case "Not": {
719
+ const child = this.expression(bindingPower.Not);
720
+ return { type: "NotExpression", child };
721
+ }
722
+ case "Minus": {
723
+ const child = this.expression(bindingPower.Minus);
724
+ return {
725
+ type: "Unary",
726
+ operator: token.type,
727
+ operand: child
728
+ };
729
+ }
730
+ case "Plus": {
731
+ const child = this.expression(bindingPower.Plus);
732
+ return {
733
+ type: "Unary",
734
+ operator: token.type,
735
+ operand: child
736
+ };
737
+ }
738
+ case "Star": {
739
+ const left = { type: "Identity" };
740
+ return { type: "ValueProjection", left, right: this.parseProjectionRHS(bindingPower.Star) };
741
+ }
742
+ case "Filter":
743
+ return this.led(token.type, { type: "Identity" });
744
+ case "Lbrace":
745
+ return this.parseMultiselectHash();
746
+ case "Flatten": {
747
+ const left = {
748
+ type: "Flatten",
749
+ child: { type: "Identity" }
750
+ };
751
+ const right = this.parseProjectionRHS(bindingPower.Flatten);
752
+ return { type: "Projection", left, right };
753
+ }
754
+ case "Lbracket": {
755
+ if (this.lookahead(0) === "Number" || this.lookahead(0) === "Colon") {
756
+ const right = this.parseIndexExpression();
757
+ return this.projectIfSlice({ type: "Identity" }, right);
758
+ }
759
+ if (this.lookahead(0) === "Star" && this.lookahead(1) === "Rbracket") {
760
+ this.advance();
761
+ this.advance();
762
+ const right = this.parseProjectionRHS(bindingPower.Star);
763
+ return {
764
+ left: { type: "Identity" },
765
+ right,
766
+ type: "Projection"
767
+ };
768
+ }
769
+ return this.parseMultiselectList();
770
+ }
771
+ case "Current":
772
+ return {
773
+ type: "Current"
774
+ /* TOK_CURRENT */
775
+ };
776
+ case "Root":
777
+ return {
778
+ type: "Root"
779
+ /* TOK_ROOT */
780
+ };
781
+ case "Expref": {
782
+ const child = this.expression(bindingPower.Expref);
783
+ return { type: "ExpressionReference", child };
784
+ }
785
+ case "Lparen": {
786
+ const expression = this.expression(0);
787
+ this.match(
788
+ "Rparen"
789
+ /* TOK_RPAREN */
790
+ );
791
+ return expression;
792
+ }
793
+ default:
794
+ this.errorToken(token);
795
+ }
796
+ }
797
+ led(tokenName, left) {
798
+ switch (tokenName) {
799
+ case "Question": {
800
+ const trueExpr = this.expression(0);
801
+ this.match(
802
+ "Colon"
803
+ /* TOK_COLON */
804
+ );
805
+ const falseExpr = this.expression(0);
806
+ return {
807
+ type: "Ternary",
808
+ condition: left,
809
+ trueExpr,
810
+ falseExpr
811
+ };
812
+ }
813
+ case "Dot": {
814
+ const rbp = bindingPower.Dot;
815
+ if (this.lookahead(0) !== "Star") {
816
+ const right2 = this.parseDotRHS(rbp);
817
+ return { type: "Subexpression", left, right: right2 };
818
+ }
819
+ this.advance();
820
+ const right = this.parseProjectionRHS(rbp);
821
+ return { type: "ValueProjection", left, right };
822
+ }
823
+ case "Pipe": {
824
+ const right = this.expression(bindingPower.Pipe);
825
+ return { type: "Pipe", left, right };
826
+ }
827
+ case "Or": {
828
+ const right = this.expression(bindingPower.Or);
829
+ return { type: "OrExpression", left, right };
830
+ }
831
+ case "And": {
832
+ const right = this.expression(bindingPower.And);
833
+ return { type: "AndExpression", left, right };
834
+ }
835
+ case "Lparen": {
836
+ if (left.type !== "Field") {
837
+ throw new Error("Syntax error: expected a Field node");
838
+ }
839
+ const name = left.name;
840
+ const args = this.parseCommaSeparatedExpressionsUntilToken(
841
+ "Rparen"
842
+ /* TOK_RPAREN */
843
+ );
844
+ const node = { name, type: "Function", children: args };
845
+ return node;
846
+ }
847
+ case "Filter": {
848
+ const condition = this.expression(0);
849
+ this.match(
850
+ "Rbracket"
851
+ /* TOK_RBRACKET */
852
+ );
853
+ const right = this.lookahead(0) === "Flatten" ? { type: "Identity" } : this.parseProjectionRHS(bindingPower.Filter);
854
+ return { type: "FilterProjection", left, right, condition };
855
+ }
856
+ case "Flatten": {
857
+ const leftNode = { type: "Flatten", child: left };
858
+ const right = this.parseProjectionRHS(bindingPower.Flatten);
859
+ return { type: "Projection", left: leftNode, right };
860
+ }
861
+ case "Assign": {
862
+ const leftNode = left;
863
+ const right = this.expression(0);
864
+ return {
865
+ type: "Binding",
866
+ variable: leftNode.name,
867
+ reference: right
868
+ };
869
+ }
870
+ case "EQ":
871
+ case "NE":
872
+ case "GT":
873
+ case "GTE":
874
+ case "LT":
875
+ case "LTE":
876
+ return this.parseComparator(left, tokenName);
877
+ case "Plus":
878
+ case "Minus":
879
+ case "Multiply":
880
+ case "Star":
881
+ case "Divide":
882
+ case "Modulo":
883
+ case "Div":
884
+ return this.parseArithmetic(left, tokenName);
885
+ case "Lbracket": {
886
+ const token = this.lookaheadToken(0);
887
+ if (token.type === "Number" || token.type === "Colon") {
888
+ const right2 = this.parseIndexExpression();
889
+ return this.projectIfSlice(left, right2);
890
+ }
891
+ this.match(
892
+ "Star"
893
+ /* TOK_STAR */
894
+ );
895
+ this.match(
896
+ "Rbracket"
897
+ /* TOK_RBRACKET */
898
+ );
899
+ const right = this.parseProjectionRHS(bindingPower.Star);
900
+ return { type: "Projection", left, right };
901
+ }
902
+ default:
903
+ return this.errorToken(this.lookaheadToken(0));
904
+ }
905
+ }
906
+ static isKeyword(token, keyword) {
907
+ return token.type === "UnquotedIdentifier" && token.value === keyword;
908
+ }
909
+ match(tokenType) {
910
+ if (this.lookahead(0) === tokenType) {
911
+ this.advance();
912
+ return;
913
+ } else {
914
+ const token = this.lookaheadToken(0);
915
+ this.errorToken(token, `Syntax error: expected ${tokenType}, got: ${token.type}`);
916
+ }
917
+ }
918
+ errorToken(token, message = "") {
919
+ const error = new Error(message || `Syntax error: invalid token (${token.type}): "${token.value}"`);
920
+ error.name = "ParserError";
921
+ throw error;
922
+ }
923
+ parseIndexExpression() {
924
+ if (this.lookahead(0) === "Colon" || this.lookahead(1) === "Colon") {
925
+ return this.parseSliceExpression();
926
+ }
927
+ const value = Number(this.lookaheadToken(0).value);
928
+ this.advance();
929
+ this.match(
930
+ "Rbracket"
931
+ /* TOK_RBRACKET */
932
+ );
933
+ return { type: "Index", value };
934
+ }
935
+ projectIfSlice(left, right) {
936
+ const indexExpr = {
937
+ type: "IndexExpression",
938
+ left,
939
+ right
940
+ };
941
+ if (right.type === "Slice") {
942
+ const rhs = this.parseProjectionRHS(bindingPower.Star);
943
+ if (rhs.type === "Identity") {
944
+ return indexExpr;
945
+ }
946
+ return {
947
+ left: indexExpr,
948
+ right: rhs,
949
+ type: "Projection"
950
+ };
951
+ }
952
+ return indexExpr;
953
+ }
954
+ parseSliceExpression() {
955
+ const parts = [null, null, null];
956
+ let index = 0;
957
+ let current = this.lookaheadToken(0);
958
+ while (current.type != "Rbracket" && index < 3) {
959
+ if (current.type === "Colon") {
960
+ index++;
961
+ if (index === 3) {
962
+ this.errorToken(this.lookaheadToken(0), "Syntax error, too many colons in slice expression");
963
+ }
964
+ this.advance();
965
+ } else if (current.type === "Number" && parts[index] === null) {
966
+ const part = this.lookaheadToken(0).value;
967
+ parts[index] = part;
968
+ this.advance();
969
+ } else {
970
+ const next = this.lookaheadToken(0);
971
+ this.errorToken(next, `Syntax error, unexpected token: ${next.value}(${next.type})`);
972
+ }
973
+ current = this.lookaheadToken(0);
974
+ }
975
+ this.match(
976
+ "Rbracket"
977
+ /* TOK_RBRACKET */
978
+ );
979
+ const [start, stop, step] = parts;
980
+ return { type: "Slice", start, stop, step };
981
+ }
982
+ parseLetExpression() {
983
+ const separated = this.parseCommaSeparatedExpressionsUntilKeyword("in");
984
+ const expression = this.expression(0);
985
+ const bindings = separated.map((binding) => binding);
986
+ return {
987
+ type: "LetExpression",
988
+ bindings,
989
+ expression
990
+ };
991
+ }
992
+ parseCommaSeparatedExpressionsUntilKeyword(keyword) {
993
+ return this.parseCommaSeparatedExpressionsUntil(
994
+ () => {
995
+ return _TokenParser.isKeyword(this.lookaheadToken(0), keyword);
996
+ },
997
+ () => {
998
+ this.advance();
999
+ }
1000
+ );
1001
+ }
1002
+ parseCommaSeparatedExpressionsUntilToken(token) {
1003
+ return this.parseCommaSeparatedExpressionsUntil(
1004
+ () => {
1005
+ return this.lookahead(0) === token;
1006
+ },
1007
+ () => {
1008
+ return this.match(token);
1009
+ }
1010
+ );
1011
+ }
1012
+ parseCommaSeparatedExpressionsUntil(isEndToken, matchEndToken) {
1013
+ const args = [];
1014
+ let expression;
1015
+ while (!isEndToken()) {
1016
+ expression = this.expression(0);
1017
+ if (this.lookahead(0) === "Comma") {
1018
+ this.match(
1019
+ "Comma"
1020
+ /* TOK_COMMA */
1021
+ );
1022
+ }
1023
+ args.push(expression);
1024
+ }
1025
+ matchEndToken();
1026
+ return args;
1027
+ }
1028
+ parseComparator(left, comparator) {
1029
+ const right = this.expression(bindingPower[comparator]);
1030
+ return { type: "Comparator", name: comparator, left, right };
1031
+ }
1032
+ parseArithmetic(left, operator) {
1033
+ const right = this.expression(bindingPower[operator]);
1034
+ return { type: "Arithmetic", operator, left, right };
1035
+ }
1036
+ parseDotRHS(rbp) {
1037
+ const lookahead = this.lookahead(0);
1038
+ const exprTokens = [
1039
+ "UnquotedIdentifier",
1040
+ "QuotedIdentifier",
1041
+ "Star"
1042
+ /* TOK_STAR */
1043
+ ];
1044
+ if (exprTokens.includes(lookahead)) {
1045
+ return this.expression(rbp);
1046
+ }
1047
+ if (lookahead === "Lbracket") {
1048
+ this.match(
1049
+ "Lbracket"
1050
+ /* TOK_LBRACKET */
1051
+ );
1052
+ return this.parseMultiselectList();
1053
+ }
1054
+ if (lookahead === "Lbrace") {
1055
+ this.match(
1056
+ "Lbrace"
1057
+ /* TOK_LBRACE */
1058
+ );
1059
+ return this.parseMultiselectHash();
1060
+ }
1061
+ const token = this.lookaheadToken(0);
1062
+ this.errorToken(token, `Syntax error, unexpected token: ${token.value}(${token.type})`);
1063
+ }
1064
+ parseProjectionRHS(rbp) {
1065
+ if (bindingPower[this.lookahead(0)] < 10) {
1066
+ return { type: "Identity" };
1067
+ }
1068
+ if (this.lookahead(0) === "Lbracket") {
1069
+ return this.expression(rbp);
1070
+ }
1071
+ if (this.lookahead(0) === "Filter") {
1072
+ return this.expression(rbp);
1073
+ }
1074
+ if (this.lookahead(0) === "Dot") {
1075
+ this.match(
1076
+ "Dot"
1077
+ /* TOK_DOT */
1078
+ );
1079
+ return this.parseDotRHS(rbp);
1080
+ }
1081
+ const token = this.lookaheadToken(0);
1082
+ this.errorToken(token, `Syntax error, unexpected token: ${token.value}(${token.type})`);
1083
+ }
1084
+ parseMultiselectList() {
1085
+ const expressions = [];
1086
+ while (this.lookahead(0) !== "Rbracket") {
1087
+ const expression = this.expression(0);
1088
+ expressions.push(expression);
1089
+ if (this.lookahead(0) === "Comma") {
1090
+ this.match(
1091
+ "Comma"
1092
+ /* TOK_COMMA */
1093
+ );
1094
+ if (this.lookahead(0) === "Rbracket") {
1095
+ throw new Error("Syntax error: unexpected token Rbracket");
1096
+ }
1097
+ } else if (this.lookahead(0) !== "Rbracket") {
1098
+ const token = this.lookaheadToken(0);
1099
+ this.errorToken(token, `Syntax error: unexpected token: ${token.value}(${token.type})`);
1100
+ }
1101
+ }
1102
+ this.match(
1103
+ "Rbracket"
1104
+ /* TOK_RBRACKET */
1105
+ );
1106
+ return { type: "MultiSelectList", children: expressions };
1107
+ }
1108
+ parseMultiselectHash() {
1109
+ const pairs = [];
1110
+ const identifierTypes = [
1111
+ "UnquotedIdentifier",
1112
+ "QuotedIdentifier"
1113
+ /* TOK_QUOTEDIDENTIFIER */
1114
+ ];
1115
+ let keyToken;
1116
+ let keyName;
1117
+ let value;
1118
+ for (; ; ) {
1119
+ keyToken = this.lookaheadToken(0);
1120
+ if (!identifierTypes.includes(keyToken.type)) {
1121
+ throw new Error(`Syntax error: expecting an identifier token, got: ${keyToken.type}`);
1122
+ }
1123
+ keyName = keyToken.value;
1124
+ this.advance();
1125
+ this.match(
1126
+ "Colon"
1127
+ /* TOK_COLON */
1128
+ );
1129
+ value = this.expression(0);
1130
+ pairs.push({ value, type: "KeyValuePair", name: keyName });
1131
+ if (this.lookahead(0) === "Comma") {
1132
+ this.match(
1133
+ "Comma"
1134
+ /* TOK_COMMA */
1135
+ );
1136
+ } else if (this.lookahead(0) === "Rbrace") {
1137
+ this.match(
1138
+ "Rbrace"
1139
+ /* TOK_RBRACE */
1140
+ );
1141
+ break;
1142
+ } else {
1143
+ const token = this.lookaheadToken(0);
1144
+ this.errorToken(token, `Syntax error: unexpected token: ${token.value}(${token.type})`);
1145
+ }
1146
+ }
1147
+ return { type: "MultiSelectHash", children: pairs };
1148
+ }
1149
+ };
1150
+ var Parser = new TokenParser();
1151
+ var Parser_default = Parser;
1152
+ var normalizeBounds = (len, start, end) => {
1153
+ let s2 = ensureInteger(start ?? 0);
1154
+ if (s2 < 0) {
1155
+ s2 = Math.max(0, len + s2);
1156
+ }
1157
+ let e = ensureInteger(end ?? len);
1158
+ if (e < 0) {
1159
+ e = Math.max(0, len + e);
1160
+ }
1161
+ return [s2, Math.min(len, e)];
1162
+ };
1163
+ var findFirst = (subject, sub2, start, end) => {
1164
+ if (!subject || !sub2) {
1165
+ return null;
1166
+ }
1167
+ const chars = [...subject];
1168
+ const [s2, e] = normalizeBounds(chars.length, start, end);
1169
+ const target = chars.slice(s2, e).join("");
1170
+ const idx = target.indexOf(sub2);
1171
+ return idx === -1 ? null : [...target.slice(0, idx)].length + s2;
1172
+ };
1173
+ var findLast = (subject, sub2, start, end) => {
1174
+ if (!subject || !sub2) {
1175
+ return null;
1176
+ }
1177
+ const chars = [...subject];
1178
+ const [s2, e] = normalizeBounds(chars.length, start, end);
1179
+ const target = chars.slice(s2, e).join("");
1180
+ const idx = target.lastIndexOf(sub2);
1181
+ return idx === -1 ? null : [...target.slice(0, idx)].length + s2;
1182
+ };
1183
+ var createPad = (isLeft, name) => (subject, width, padding = " ") => {
1184
+ if ([...padding].length !== 1) {
1185
+ throw new Error(
1186
+ `invalid value, ${name} expects its 'pad' parameter to be a valid string with a single codepoint`
1187
+ );
1188
+ }
1189
+ ensurePositiveInteger(width);
1190
+ if (!subject) {
1191
+ return "";
1192
+ }
1193
+ const padLen = width - [...subject].length;
1194
+ if (padLen <= 0) {
1195
+ return subject;
1196
+ }
1197
+ const pad2 = padding.repeat(padLen);
1198
+ return isLeft ? pad2 + subject : subject + pad2;
1199
+ };
1200
+ var padLeft = createPad(true, "pad_left");
1201
+ var padRight = createPad(false, "pad_right");
1202
+ var replace = (subject, string, by, count) => {
1203
+ if (count === 0) {
1204
+ return subject;
1205
+ }
1206
+ const parts = subject.split(string);
1207
+ if (count === null || count === void 0) {
1208
+ return parts.join(by);
1209
+ }
1210
+ ensurePositiveInteger(count);
1211
+ const head = parts.slice(0, count + 1).join(by);
1212
+ const tail = parts.slice(count + 1).join(string);
1213
+ return tail.length > 0 ? head + string + tail : head;
1214
+ };
1215
+ var split = (subject, search2, count) => {
1216
+ if (subject.length === 0 && search2.length === 0) {
1217
+ return [];
1218
+ }
1219
+ if (count !== null && count !== void 0) {
1220
+ ensurePositiveInteger(count);
1221
+ if (count === 0) {
1222
+ return [subject];
1223
+ }
1224
+ }
1225
+ const parts = search2 === "" ? [...subject] : subject.split(search2);
1226
+ if (count === null || count === void 0) {
1227
+ return parts;
1228
+ }
1229
+ return [...parts.slice(0, count), parts.slice(count).join(search2)];
1230
+ };
1231
+ var trimImpl = (subject, pattern, chars) => {
1232
+ const charClass = chars ? chars.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&") : "\\s\x85";
1233
+ return subject.replace(new RegExp(pattern.replace("%s", charClass), "u"), "");
1234
+ };
1235
+ var trimLeft = (subject, chars) => trimImpl(subject, "^[%s]+", chars);
1236
+ var trimRight = (subject, chars) => trimImpl(subject, "[%s]+$", chars);
1237
+ var trim = (subject, chars) => trimLeft(trimRight(subject, chars), chars);
1238
+ var compareStrings = (a, b) => {
1239
+ if (a === b) {
1240
+ return 0;
1241
+ }
1242
+ const itA = a[Symbol.iterator]();
1243
+ const itB = b[Symbol.iterator]();
1244
+ while (true) {
1245
+ const nextA = itA.next();
1246
+ const nextB = itB.next();
1247
+ if (nextA.done || nextB.done) {
1248
+ return nextA.done && nextB.done ? 0 : nextA.done ? -1 : 1;
1249
+ }
1250
+ if (nextA.value !== nextB.value) {
1251
+ return nextA.value.codePointAt(0) > nextB.value.codePointAt(0) ? 1 : -1;
1252
+ }
1253
+ }
1254
+ };
1255
+ var Text = class {
1256
+ constructor(string) {
1257
+ this.string = string;
1258
+ }
1259
+ get length() {
1260
+ return [...this.string].length;
1261
+ }
1262
+ compareTo(other) {
1263
+ return compareStrings(this.string, other);
1264
+ }
1265
+ };
1266
+ __publicField(Text, "comparer", compareStrings);
1267
+ __publicField(Text, "compareStrings", compareStrings);
1268
+ var InputArgument = /* @__PURE__ */ ((InputArgument2) => {
1269
+ InputArgument2[InputArgument2["TYPE_NUMBER"] = 0] = "TYPE_NUMBER";
1270
+ InputArgument2[InputArgument2["TYPE_ANY"] = 1] = "TYPE_ANY";
1271
+ InputArgument2[InputArgument2["TYPE_STRING"] = 2] = "TYPE_STRING";
1272
+ InputArgument2[InputArgument2["TYPE_ARRAY"] = 3] = "TYPE_ARRAY";
1273
+ InputArgument2[InputArgument2["TYPE_OBJECT"] = 4] = "TYPE_OBJECT";
1274
+ InputArgument2[InputArgument2["TYPE_BOOLEAN"] = 5] = "TYPE_BOOLEAN";
1275
+ InputArgument2[InputArgument2["TYPE_EXPREF"] = 6] = "TYPE_EXPREF";
1276
+ InputArgument2[InputArgument2["TYPE_NULL"] = 7] = "TYPE_NULL";
1277
+ InputArgument2[InputArgument2["TYPE_ARRAY_NUMBER"] = 8] = "TYPE_ARRAY_NUMBER";
1278
+ InputArgument2[InputArgument2["TYPE_ARRAY_STRING"] = 9] = "TYPE_ARRAY_STRING";
1279
+ InputArgument2[InputArgument2["TYPE_ARRAY_OBJECT"] = 10] = "TYPE_ARRAY_OBJECT";
1280
+ InputArgument2[InputArgument2["TYPE_ARRAY_ARRAY"] = 11] = "TYPE_ARRAY_ARRAY";
1281
+ return InputArgument2;
1282
+ })(InputArgument || {});
1283
+ var ARRAY_SUBTYPES = {
1284
+ [
1285
+ 8
1286
+ /* TYPE_ARRAY_NUMBER */
1287
+ ]: 0,
1288
+ [
1289
+ 10
1290
+ /* TYPE_ARRAY_OBJECT */
1291
+ ]: 4,
1292
+ [
1293
+ 9
1294
+ /* TYPE_ARRAY_STRING */
1295
+ ]: 2,
1296
+ [
1297
+ 11
1298
+ /* TYPE_ARRAY_ARRAY */
1299
+ ]: 3
1300
+ /* TYPE_ARRAY */
1301
+ };
1302
+ var JSON_NUMBER_REGEX = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
1303
+ var s = (...types) => types.map((t) => ({ types: Array.isArray(t) ? t : [t] }));
1304
+ var opt = (...types) => types.map((t) => ({ types: Array.isArray(t) ? t : [t], optional: true }));
1305
+ var variadic = (...types) => types.map((t) => ({ types: Array.isArray(t) ? t : [t], variadic: true }));
1306
+ var optVariadic = (...types) => types.map((t) => ({ types: Array.isArray(t) ? t : [t], variadic: true, optional: true }));
1307
+ var {
1308
+ TYPE_NUMBER: NUM,
1309
+ TYPE_ANY: ANY,
1310
+ TYPE_STRING: STR,
1311
+ TYPE_ARRAY: ARR,
1312
+ TYPE_OBJECT: OBJ,
1313
+ TYPE_EXPREF: EXP,
1314
+ TYPE_ARRAY_NUMBER: ARR_NUM,
1315
+ TYPE_ARRAY_STRING: ARR_STR,
1316
+ TYPE_ARRAY_ARRAY: ARR_ARR
1317
+ } = InputArgument;
1318
+ var Runtime = class {
1319
+ constructor(interpreter) {
1320
+ __publicField(this, "_interpreter");
1321
+ __publicField(this, "_functionTable");
1322
+ __publicField(this, "_customFunctions", /* @__PURE__ */ new Set());
1323
+ __publicField(this, "TYPE_NAME_TABLE", Object.freeze([
1324
+ "number",
1325
+ "any",
1326
+ "string",
1327
+ "array",
1328
+ "object",
1329
+ "boolean",
1330
+ "expression",
1331
+ "null",
1332
+ "Array<number>",
1333
+ "Array<string>",
1334
+ "Array<object>",
1335
+ "Array<Array<any>>"
1336
+ ]));
1337
+ __publicField(this, "functionContains", ([
1338
+ searchable,
1339
+ searchValue
1340
+ ]) => {
1341
+ if (Array.isArray(searchable)) {
1342
+ const array = searchable;
1343
+ return array.includes(searchValue);
1344
+ }
1345
+ if (typeof searchable === "string") {
1346
+ const text = searchable;
1347
+ if (typeof searchValue === "string") {
1348
+ return text.includes(searchValue);
1349
+ }
1350
+ return false;
1351
+ }
1352
+ return null;
1353
+ });
1354
+ __publicField(this, "functionFromItems", ([array]) => {
1355
+ for (const pair of array) {
1356
+ if (pair.length !== 2 || typeof pair[0] !== "string") {
1357
+ throw new Error("invalid value, each array must contain two elements, a pair of string and value");
1358
+ }
1359
+ }
1360
+ return Object.fromEntries(array);
1361
+ });
1362
+ __publicField(this, "functionGroupBy", ([array, exprefNode]) => {
1363
+ const interpreter2 = this._interpreter;
1364
+ if (!interpreter2) {
1365
+ return {};
1366
+ }
1367
+ const groups = /* @__PURE__ */ new Map();
1368
+ for (const item of array) {
1369
+ const raw = interpreter2.visit(exprefNode, item);
1370
+ if (raw === null || raw === void 0) {
1371
+ continue;
1372
+ }
1373
+ if (typeof raw !== "string") {
1374
+ const type = this.getTypeName(raw);
1375
+ throw new Error(
1376
+ `Invalid type: expected one of (${this.TYPE_NAME_TABLE[
1377
+ 2
1378
+ /* TYPE_STRING */
1379
+ ]}), received ${this.TYPE_NAME_TABLE[type]}`
1380
+ );
1381
+ }
1382
+ let group = groups.get(raw);
1383
+ if (!group) {
1384
+ group = [];
1385
+ groups.set(raw, group);
1386
+ }
1387
+ group.push(item);
1388
+ }
1389
+ return Object.fromEntries(groups);
1390
+ });
1391
+ __publicField(this, "functionMap", ([exprefNode, elements]) => {
1392
+ if (!this._interpreter) {
1393
+ return [];
1394
+ }
1395
+ const mapped = [];
1396
+ const interpreter2 = this._interpreter;
1397
+ for (let i = 0; i < elements.length; i += 1) {
1398
+ mapped.push(interpreter2.visit(exprefNode, elements[i]));
1399
+ }
1400
+ return mapped;
1401
+ });
1402
+ __publicField(this, "functionSort", ([inputValue]) => inputValue.length <= 1 ? inputValue : typeof inputValue[0] === "string" ? [...inputValue].sort(Text.comparer) : [...inputValue].sort((a, b) => a - b));
1403
+ __publicField(this, "functionSortBy", (resolvedArgs) => {
1404
+ const sortedArray = [...resolvedArgs[0]];
1405
+ if (sortedArray.length === 0) {
1406
+ return sortedArray;
1407
+ }
1408
+ const interpreter2 = this._interpreter;
1409
+ const exprefNode = resolvedArgs[1];
1410
+ const requiredType = this.getTypeName(interpreter2.visit(exprefNode, sortedArray[0]));
1411
+ if (requiredType === void 0 || ![
1412
+ 0,
1413
+ 2
1414
+ /* TYPE_STRING */
1415
+ ].includes(requiredType)) {
1416
+ const typeName = requiredType !== void 0 ? this.TYPE_NAME_TABLE[requiredType] : "undefined";
1417
+ throw new Error(`Invalid type: unexpected type (${typeName})`);
1418
+ }
1419
+ if (sortedArray.length === 1) {
1420
+ return sortedArray;
1421
+ }
1422
+ function throwInvalidTypeError(rt, item) {
1423
+ throw new Error(
1424
+ `Invalid type: expected (${rt.TYPE_NAME_TABLE[requiredType]}), received ${rt.TYPE_NAME_TABLE[rt.getTypeName(item)]}`
1425
+ );
1426
+ }
1427
+ return sortedArray.sort((a, b) => {
1428
+ const exprA = interpreter2.visit(exprefNode, a);
1429
+ const exprB = interpreter2.visit(exprefNode, b);
1430
+ if (this.getTypeName(exprA) !== requiredType) {
1431
+ throwInvalidTypeError(this, exprA);
1432
+ } else if (this.getTypeName(exprB) !== requiredType) {
1433
+ throwInvalidTypeError(this, exprB);
1434
+ }
1435
+ if (requiredType === 2) {
1436
+ return Text.comparer(exprA, exprB);
1437
+ }
1438
+ return exprA - exprB;
1439
+ });
1440
+ });
1441
+ __publicField(this, "functionToNumber", ([inputValue]) => {
1442
+ const typeName = this.getTypeName(inputValue);
1443
+ if (typeName === 0) {
1444
+ return inputValue;
1445
+ }
1446
+ if (typeName === 2) {
1447
+ const str = inputValue;
1448
+ if (JSON_NUMBER_REGEX.test(str)) {
1449
+ const convertedValue = Number(str);
1450
+ if (!isNaN(convertedValue)) {
1451
+ return convertedValue;
1452
+ }
1453
+ }
1454
+ }
1455
+ return null;
1456
+ });
1457
+ __publicField(this, "functionType", ([inputValue]) => {
1458
+ const type = this.getTypeName(inputValue);
1459
+ const name = type !== void 0 ? this.TYPE_NAME_TABLE[type] : void 0;
1460
+ if (!name) {
1461
+ throw new Error("invalid-type");
1462
+ }
1463
+ return name;
1464
+ });
1465
+ __publicField(this, "functionZip", (array) => {
1466
+ const length = Math.min(...array.map((arr) => arr.length));
1467
+ return Array.from({ length }, (_, index) => array.map((arr) => arr[index]));
1468
+ });
1469
+ this._interpreter = interpreter;
1470
+ this._functionTable = this.buildFunctionTable();
1471
+ }
1472
+ buildFunctionTable() {
1473
+ return {
1474
+ abs: { _func: ([v]) => Math.abs(v), _signature: s(NUM) },
1475
+ ceil: { _func: ([v]) => Math.ceil(v), _signature: s(NUM) },
1476
+ floor: { _func: ([v]) => Math.floor(v), _signature: s(NUM) },
1477
+ lower: { _func: ([s2]) => s2.toLowerCase(), _signature: s(STR) },
1478
+ upper: { _func: ([s2]) => s2.toUpperCase(), _signature: s(STR) },
1479
+ keys: { _func: ([o]) => Object.keys(o), _signature: s(OBJ) },
1480
+ values: { _func: ([o]) => Object.values(o), _signature: s(OBJ) },
1481
+ avg: {
1482
+ _func: ([arr]) => arr && arr.length ? arr.reduce((a, b) => a + b, 0) / arr.length : null,
1483
+ _signature: s(ARR_NUM)
1484
+ },
1485
+ contains: { _func: this.functionContains, _signature: s([STR, ARR], ANY) },
1486
+ ends_with: { _func: ([str, suf]) => str.endsWith(suf), _signature: s(STR, STR) },
1487
+ find_first: {
1488
+ _func: ([s2, sub2, st, en]) => findFirst(s2, sub2, st, en),
1489
+ _signature: [...s(STR, STR), ...opt(NUM, NUM)]
1490
+ },
1491
+ find_last: {
1492
+ _func: ([s2, sub2, st, en]) => findLast(s2, sub2, st, en),
1493
+ _signature: [...s(STR, STR), ...opt(NUM, NUM)]
1494
+ },
1495
+ from_items: { _func: this.functionFromItems, _signature: s(ARR_ARR) },
1496
+ group_by: { _func: this.functionGroupBy, _signature: s(ARR, EXP) },
1497
+ items: { _func: ([o]) => Object.entries(o), _signature: s(OBJ) },
1498
+ join: { _func: ([sep, arr]) => arr.join(sep), _signature: s(STR, ARR_STR) },
1499
+ length: {
1500
+ _func: ([v]) => typeof v === "string" ? [...v].length : Array.isArray(v) ? v.length : Object.keys(v).length,
1501
+ _signature: s([STR, ARR, OBJ])
1502
+ },
1503
+ map: { _func: this.functionMap, _signature: s(EXP, ARR) },
1504
+ max: {
1505
+ _func: ([v]) => this.findExtreme(v, (cmp) => cmp > 0, Math.max),
1506
+ _signature: s([ARR_NUM, ARR_STR])
1507
+ },
1508
+ max_by: {
1509
+ _func: (args) => this.findExtremeBy(args, (cmp) => cmp > 0),
1510
+ _signature: s(ARR, EXP)
1511
+ },
1512
+ merge: { _func: (args) => Object.assign({}, ...args), _signature: optVariadic(OBJ) },
1513
+ min: {
1514
+ _func: ([v]) => this.findExtreme(v, (cmp) => cmp < 0, Math.min),
1515
+ _signature: s([ARR_NUM, ARR_STR])
1516
+ },
1517
+ min_by: {
1518
+ _func: (args) => this.findExtremeBy(args, (cmp) => cmp < 0),
1519
+ _signature: s(ARR, EXP)
1520
+ },
1521
+ not_null: {
1522
+ _func: (args) => args.find((arg) => arg !== null) ?? null,
1523
+ _signature: variadic(ANY)
1524
+ },
1525
+ pad_left: {
1526
+ _func: ([s2, w, p]) => padLeft(s2, w, p),
1527
+ _signature: [...s(STR, NUM), ...opt(STR)]
1528
+ },
1529
+ pad_right: {
1530
+ _func: ([s2, w, p]) => padRight(s2, w, p),
1531
+ _signature: [...s(STR, NUM), ...opt(STR)]
1532
+ },
1533
+ replace: {
1534
+ _func: ([s2, str, by, c]) => replace(s2, str, by, c),
1535
+ _signature: [...s(STR, STR, STR), ...opt(NUM)]
1536
+ },
1537
+ reverse: {
1538
+ _func: ([v]) => typeof v === "string" ? [...v].reverse().join("") : [...v].reverse(),
1539
+ _signature: s([STR, ARR])
1540
+ },
1541
+ sort: { _func: this.functionSort, _signature: s([ARR_STR, ARR_NUM]) },
1542
+ sort_by: { _func: this.functionSortBy, _signature: s(ARR, EXP) },
1543
+ split: {
1544
+ _func: ([s2, sub2, c]) => split(s2, sub2, c),
1545
+ _signature: [...s(STR, STR), ...opt(NUM)]
1546
+ },
1547
+ starts_with: { _func: ([str, pre]) => str.startsWith(pre), _signature: s(STR, STR) },
1548
+ sum: { _func: ([arr]) => arr.reduce((x, y) => x + y, 0), _signature: s(ARR_NUM) },
1549
+ to_array: { _func: ([v]) => Array.isArray(v) ? v : [v], _signature: s(ANY) },
1550
+ to_number: { _func: this.functionToNumber, _signature: s(ANY) },
1551
+ to_string: {
1552
+ _func: ([v]) => typeof v === "string" ? v : JSON.stringify(v),
1553
+ _signature: s(ANY)
1554
+ },
1555
+ trim: {
1556
+ _func: ([s2, c]) => trim(s2, c),
1557
+ _signature: [...s(STR), ...opt(STR)]
1558
+ },
1559
+ trim_left: {
1560
+ _func: ([s2, c]) => trimLeft(s2, c),
1561
+ _signature: [...s(STR), ...opt(STR)]
1562
+ },
1563
+ trim_right: {
1564
+ _func: ([s2, c]) => trimRight(s2, c),
1565
+ _signature: [...s(STR), ...opt(STR)]
1566
+ },
1567
+ type: { _func: this.functionType, _signature: s(ANY) },
1568
+ zip: { _func: this.functionZip, _signature: variadic(ARR) }
1569
+ };
1570
+ }
1571
+ /**
1572
+ * Enhanced registerFunction with backward compatibility and new options
1573
+ * @deprecated Use register() method for enhanced functionality
1574
+ */
1575
+ registerFunction(name, customFunction, signature, options) {
1576
+ const result = this._registerInternal(name, customFunction, signature, options);
1577
+ if (!result.success) {
1578
+ throw new Error(result.message);
1579
+ }
1580
+ }
1581
+ /**
1582
+ * Internal registration method that bypasses TypeScript type checking
1583
+ */
1584
+ _registerInternal(name, customFunction, signature, options = {}) {
1585
+ if (!name || typeof name !== "string" || name.trim() === "") {
1586
+ return {
1587
+ success: false,
1588
+ reason: "invalid-name",
1589
+ message: "Function name must be a non-empty string"
1590
+ };
1591
+ }
1592
+ try {
1593
+ this.validateInputSignatures(name, signature);
1594
+ } catch (error) {
1595
+ return {
1596
+ success: false,
1597
+ reason: "invalid-signature",
1598
+ message: error instanceof Error ? error.message : "Invalid function signature"
1599
+ };
1600
+ }
1601
+ const { override = false, warn = false } = options;
1602
+ const exists = name in this._functionTable;
1603
+ if (exists && !override) {
1604
+ return {
1605
+ success: false,
1606
+ reason: "already-exists",
1607
+ message: `Function already defined: ${name}(). Use { override: true } to replace it.`
1608
+ };
1609
+ }
1610
+ if (exists && override && warn) {
1611
+ console.warn(`Warning: Overriding existing function: ${name}()`);
1612
+ }
1613
+ this._functionTable[name] = {
1614
+ _func: customFunction.bind(this),
1615
+ _signature: signature
1616
+ };
1617
+ this._customFunctions.add(name);
1618
+ const message = exists ? `Function ${name}() overridden successfully` : `Function ${name}() registered successfully`;
1619
+ return { success: true, message };
1620
+ }
1621
+ /**
1622
+ * Register a new function with enhanced options and type safety
1623
+ */
1624
+ register(name, customFunction, signature, options = {}) {
1625
+ return this._registerInternal(name, customFunction, signature, options);
1626
+ }
1627
+ /**
1628
+ * Unregister a custom function (built-in functions cannot be unregistered)
1629
+ */
1630
+ unregister(name) {
1631
+ if (!this._customFunctions.has(name)) {
1632
+ return false;
1633
+ }
1634
+ delete this._functionTable[name];
1635
+ this._customFunctions.delete(name);
1636
+ return true;
1637
+ }
1638
+ /**
1639
+ * Check if a function is registered
1640
+ */
1641
+ isRegistered(name) {
1642
+ return name in this._functionTable;
1643
+ }
1644
+ /**
1645
+ * Get list of all registered function names
1646
+ */
1647
+ getRegistered() {
1648
+ return Object.keys(this._functionTable);
1649
+ }
1650
+ /**
1651
+ * Get list of custom (non-built-in) function names
1652
+ */
1653
+ getCustomFunctions() {
1654
+ return Array.from(this._customFunctions);
1655
+ }
1656
+ /**
1657
+ * Clear all custom functions (built-in functions remain)
1658
+ */
1659
+ clearCustomFunctions() {
1660
+ for (const name of this._customFunctions) {
1661
+ delete this._functionTable[name];
1662
+ }
1663
+ this._customFunctions.clear();
1664
+ }
1665
+ callFunction(name, resolvedArgs) {
1666
+ const functionEntry = this._functionTable[name];
1667
+ if (functionEntry === void 0) {
1668
+ throw new Error(`Unknown function: ${name}()`);
1669
+ }
1670
+ this.validateArgs(name, resolvedArgs, functionEntry._signature);
1671
+ return functionEntry._func.call(this, resolvedArgs);
1672
+ }
1673
+ validateInputSignatures(name, signature) {
1674
+ for (let i = 0; i < signature.length; i += 1) {
1675
+ if ("variadic" in signature[i] && i !== signature.length - 1) {
1676
+ throw new Error(`Invalid arity: ${name}() 'variadic' argument ${i + 1} must occur last`);
1677
+ }
1678
+ }
1679
+ }
1680
+ validateArgs(name, args, signature) {
1681
+ this.validateInputSignatures(name, signature);
1682
+ this.validateArity(name, args, signature);
1683
+ this.validateTypes(name, args, signature);
1684
+ }
1685
+ validateArity(name, args, signature) {
1686
+ const numberOfRequiredArgs = signature.filter((argSignature) => !(argSignature.optional ?? false)).length;
1687
+ const lastArgIsVariadic = signature[signature.length - 1]?.variadic ?? false;
1688
+ const tooFewArgs = args.length < numberOfRequiredArgs;
1689
+ const tooManyArgs = args.length > signature.length;
1690
+ if (lastArgIsVariadic && tooFewArgs || !lastArgIsVariadic && (tooFewArgs || tooManyArgs)) {
1691
+ const tooFewModifier = tooFewArgs && (!lastArgIsVariadic && numberOfRequiredArgs > 1 || lastArgIsVariadic) ? "at least " : "";
1692
+ const pluralized = signature.length > 1;
1693
+ throw new Error(
1694
+ `Invalid arity: ${name}() takes ${tooFewModifier}${numberOfRequiredArgs} argument${pluralized && "s" || ""} but received ${args.length}`
1695
+ );
1696
+ }
1697
+ }
1698
+ validateTypes(name, args, signature) {
1699
+ const lastSigIndex = signature.length - 1;
1700
+ const isVariadic = signature[lastSigIndex]?.variadic ?? false;
1701
+ for (let i = 0; i < args.length; i += 1) {
1702
+ const sig = i < signature.length ? signature[i] : isVariadic ? signature[lastSigIndex] : void 0;
1703
+ if (!sig) {
1704
+ continue;
1705
+ }
1706
+ const currentSpec = sig.types;
1707
+ const actualType = this.getTypeName(args[i]);
1708
+ if (actualType === void 0) {
1709
+ continue;
1710
+ }
1711
+ const typeMatched = currentSpec.some((expectedType) => this.typeMatches(actualType, expectedType, args[i]));
1712
+ if (!typeMatched) {
1713
+ const expected = currentSpec.map((typeId) => this.TYPE_NAME_TABLE[typeId]).join(" | ");
1714
+ throw new Error(
1715
+ `Invalid type: ${name}() expected argument ${i + 1} to be type (${expected}) but received type ${this.TYPE_NAME_TABLE[actualType]} instead.`
1716
+ );
1717
+ }
1718
+ }
1719
+ }
1720
+ typeMatches(actual, expected, argValue) {
1721
+ if (expected === 1) {
1722
+ return true;
1723
+ }
1724
+ const subtype = ARRAY_SUBTYPES[expected];
1725
+ if (subtype !== void 0) {
1726
+ if (actual !== 3) {
1727
+ return false;
1728
+ }
1729
+ const array = argValue;
1730
+ return array.every((item) => {
1731
+ const typeName = this.getTypeName(item);
1732
+ return typeName === void 0 || this.typeMatches(typeName, subtype, item);
1733
+ });
1734
+ }
1735
+ return actual === expected;
1736
+ }
1737
+ getTypeName(obj) {
1738
+ if (obj === null) {
1739
+ return 7;
1740
+ }
1741
+ if (typeof obj === "string") {
1742
+ return 2;
1743
+ }
1744
+ if (typeof obj === "number") {
1745
+ return 0;
1746
+ }
1747
+ if (typeof obj === "boolean") {
1748
+ return 5;
1749
+ }
1750
+ if (Array.isArray(obj)) {
1751
+ return 3;
1752
+ }
1753
+ if (typeof obj === "object") {
1754
+ if (obj.expref) {
1755
+ return 6;
1756
+ }
1757
+ return 4;
1758
+ }
1759
+ return;
1760
+ }
1761
+ createKeyFunction(exprefNode, allowedTypes) {
1762
+ const interpreter = this._interpreter;
1763
+ const keyFunc = (x) => {
1764
+ const current = interpreter.visit(exprefNode, x);
1765
+ if (!allowedTypes.includes(this.getTypeName(current))) {
1766
+ const msg = `Invalid type: expected one of (${allowedTypes.map((t) => this.TYPE_NAME_TABLE[t]).join(" | ")}), received ${this.TYPE_NAME_TABLE[this.getTypeName(current)]}`;
1767
+ throw new Error(msg);
1768
+ }
1769
+ return current;
1770
+ };
1771
+ return keyFunc;
1772
+ }
1773
+ findExtreme(inputValue, isBetter, mathFn) {
1774
+ if (!inputValue.length) {
1775
+ return null;
1776
+ }
1777
+ if (this.getTypeName(inputValue[0]) === 0) {
1778
+ return mathFn(...inputValue);
1779
+ }
1780
+ const elements = inputValue;
1781
+ let extreme = elements[0];
1782
+ for (let i = 1; i < elements.length; i += 1) {
1783
+ if (isBetter(Text.comparer(elements[i], extreme))) {
1784
+ extreme = elements[i];
1785
+ }
1786
+ }
1787
+ return extreme;
1788
+ }
1789
+ findExtremeBy(resolvedArgs, isBetter) {
1790
+ const resolvedArray = resolvedArgs[0];
1791
+ if (!resolvedArray.length) {
1792
+ return null;
1793
+ }
1794
+ const exprefNode = resolvedArgs[1];
1795
+ const interpreter = this._interpreter;
1796
+ if (!interpreter) {
1797
+ return null;
1798
+ }
1799
+ const firstKey = interpreter.visit(exprefNode, resolvedArray[0]);
1800
+ const firstType = this.getTypeName(firstKey);
1801
+ if (firstType !== 0 && firstType !== 2) {
1802
+ throw new Error(
1803
+ `Invalid type: expected one of (${this.TYPE_NAME_TABLE[
1804
+ 0
1805
+ /* TYPE_NUMBER */
1806
+ ]} | ${this.TYPE_NAME_TABLE[
1807
+ 2
1808
+ /* TYPE_STRING */
1809
+ ]}), received ${this.TYPE_NAME_TABLE[firstType]}`
1810
+ );
1811
+ }
1812
+ let extremeRecord = resolvedArray[0];
1813
+ let extremeKey = firstKey;
1814
+ for (let i = 1; i < resolvedArray.length; i += 1) {
1815
+ const currentKey = interpreter.visit(exprefNode, resolvedArray[i]);
1816
+ const currentType = this.getTypeName(currentKey);
1817
+ if (currentType !== firstType) {
1818
+ throw new Error(
1819
+ `Invalid type: expected (${this.TYPE_NAME_TABLE[firstType]}), received ${this.TYPE_NAME_TABLE[currentType]}`
1820
+ );
1821
+ }
1822
+ const cmp = firstType === 0 ? currentKey - extremeKey : Text.comparer(currentKey, extremeKey);
1823
+ if (isBetter(cmp)) {
1824
+ extremeKey = currentKey;
1825
+ extremeRecord = resolvedArray[i];
1826
+ }
1827
+ }
1828
+ return extremeRecord;
1829
+ }
1830
+ };
1831
+ var ScopeChain = class _ScopeChain {
1832
+ constructor() {
1833
+ __publicField(this, "inner");
1834
+ __publicField(this, "data", {});
1835
+ }
1836
+ get currentScopeData() {
1837
+ return this.data;
1838
+ }
1839
+ withScope(data) {
1840
+ const outer = new _ScopeChain();
1841
+ outer.inner = this;
1842
+ outer.data = data;
1843
+ return outer;
1844
+ }
1845
+ hasValue(identifier) {
1846
+ if (Object.prototype.hasOwnProperty.call(this.data, identifier)) {
1847
+ return true;
1848
+ }
1849
+ if (this.inner) {
1850
+ return this.inner.hasValue(identifier);
1851
+ }
1852
+ return false;
1853
+ }
1854
+ getValue(identifier) {
1855
+ if (Object.prototype.hasOwnProperty.call(this.data, identifier)) {
1856
+ return this.data[identifier];
1857
+ }
1858
+ if (this.inner) {
1859
+ return this.inner.getValue(identifier);
1860
+ }
1861
+ return null;
1862
+ }
1863
+ };
1864
+ var emptyScopeChain = new ScopeChain();
1865
+ var TreeInterpreter = class _TreeInterpreter {
1866
+ constructor(options) {
1867
+ __publicField(this, "runtime");
1868
+ __publicField(this, "_rootValue", null);
1869
+ __publicField(this, "_scope");
1870
+ __publicField(this, "_options");
1871
+ __publicField(this, "_depth", 0);
1872
+ __publicField(this, "_maxDepth", DEFAULT_MAX_DEPTH);
1873
+ __publicField(this, "_deadline", 0);
1874
+ this.runtime = new Runtime(this);
1875
+ this._scope = new ScopeChain();
1876
+ this._maxDepth = options?.maxDepth ?? DEFAULT_MAX_DEPTH;
1877
+ if (options) {
1878
+ this._options = options;
1879
+ this._deadline = options.timeoutMs !== void 0 ? Date.now() + options.timeoutMs : 0;
1880
+ }
1881
+ }
1882
+ withScope(scope) {
1883
+ const interpreter = new _TreeInterpreter(this._options);
1884
+ interpreter.runtime = this.runtime;
1885
+ interpreter._rootValue = this._rootValue;
1886
+ interpreter._scope = this._scope.withScope(scope);
1887
+ interpreter._depth = this._depth;
1888
+ interpreter._maxDepth = this._maxDepth;
1889
+ interpreter._deadline = this._deadline;
1890
+ return interpreter;
1891
+ }
1892
+ search(node, value, options) {
1893
+ if (options?.maxNodes !== void 0 && countNodes(node) > options.maxNodes) {
1894
+ throw new Error(`Expression exceeds maximum allowed node count of ${options.maxNodes}`);
1895
+ }
1896
+ this._rootValue = value;
1897
+ this._scope = emptyScopeChain;
1898
+ this._options = options;
1899
+ this._maxDepth = options?.maxDepth ?? DEFAULT_MAX_DEPTH;
1900
+ this._depth = 0;
1901
+ this._deadline = options?.timeoutMs !== void 0 ? Date.now() + options.timeoutMs : 0;
1902
+ const result = this.visit(node, value);
1903
+ this.checkOutputSize(result);
1904
+ return result;
1905
+ }
1906
+ checkOutputSize(val) {
1907
+ const max = this._options?.maxOutputSize;
1908
+ if (max === void 0 || val == null) {
1909
+ return;
1910
+ }
1911
+ const size = Array.isArray(val) || typeof val === "string" ? val.length : typeof val === "object" ? Object.keys(val).length : 0;
1912
+ if (size > max) {
1913
+ throw new Error(`Output size exceeds maximum allowed size of ${max}`);
1914
+ }
1915
+ }
1916
+ collect(target, item) {
1917
+ target.push(item);
1918
+ if (this._options?.maxOutputSize !== void 0 && target.length > this._options.maxOutputSize) {
1919
+ throw new Error(`Output size exceeds maximum allowed size of ${this._options.maxOutputSize}`);
1920
+ }
1921
+ }
1922
+ evaluateProjectionItems(items, right) {
1923
+ const collected = [];
1924
+ if (right.type === "Field") {
1925
+ const key = right.name;
1926
+ for (let i = 0; i < items.length; i++) {
1927
+ if (this._deadline > 0 && Date.now() >= this._deadline) {
1928
+ throw new Error(`Query execution timed out after ${this._options?.timeoutMs}ms`);
1929
+ }
1930
+ const elem = items[i];
1931
+ if (!elem || typeof elem !== "object" || Array.isArray(elem)) {
1932
+ continue;
1933
+ }
1934
+ if (!Object.prototype.hasOwnProperty.call(elem, key)) {
1935
+ continue;
1936
+ }
1937
+ const val = elem[key];
1938
+ if (val !== void 0 && val !== null) {
1939
+ this.collect(collected, val);
1940
+ }
1941
+ }
1942
+ return collected;
1943
+ }
1944
+ if (right.type === "Identity") {
1945
+ for (let i = 0; i < items.length; i++) {
1946
+ const elem = items[i];
1947
+ if (elem !== null) {
1948
+ this.collect(collected, elem);
1949
+ }
1950
+ }
1951
+ return collected;
1952
+ }
1953
+ for (const elem of items) {
1954
+ const current = this.visit(right, elem);
1955
+ if (current !== null) {
1956
+ this.collect(collected, current);
1957
+ }
1958
+ }
1959
+ return collected;
1960
+ }
1961
+ visit(node, value) {
1962
+ if (this._deadline > 0 && Date.now() >= this._deadline) {
1963
+ throw new Error(`Query execution timed out after ${this._options?.timeoutMs}ms`);
1964
+ }
1965
+ if (this._depth >= this._maxDepth) {
1966
+ throw new Error(`Evaluation exceeds maximum allowed depth of ${this._maxDepth}`);
1967
+ }
1968
+ this._depth += 1;
1969
+ try {
1970
+ return this.evaluateNode(node, value);
1971
+ } finally {
1972
+ this._depth -= 1;
1973
+ }
1974
+ }
1975
+ evaluateNode(node, value) {
1976
+ switch (node.type) {
1977
+ case "Ternary": {
1978
+ const condition = this.visit(node.condition, value);
1979
+ if (!isFalse(condition)) {
1980
+ return this.visit(node.trueExpr, value);
1981
+ }
1982
+ return this.visit(node.falseExpr, value);
1983
+ }
1984
+ case "Field":
1985
+ const identifier = node.name;
1986
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
1987
+ return null;
1988
+ }
1989
+ return Object.prototype.hasOwnProperty.call(value, identifier) ? value[identifier] ?? null : null;
1990
+ case "LetExpression": {
1991
+ const scope = {};
1992
+ for (const binding of node.bindings) {
1993
+ const bound = this.visit(binding, value);
1994
+ for (const key of Object.keys(bound)) {
1995
+ Object.defineProperty(scope, key, {
1996
+ value: bound[key],
1997
+ enumerable: true,
1998
+ writable: true,
1999
+ configurable: true
2000
+ });
2001
+ }
2002
+ }
2003
+ return this.withScope(scope).visit(node.expression, value);
2004
+ }
2005
+ case "Binding": {
2006
+ const { variable, reference } = node;
2007
+ const result = this.visit(reference, value);
2008
+ const binding = {};
2009
+ Object.defineProperty(binding, variable, {
2010
+ value: result,
2011
+ enumerable: true,
2012
+ writable: true,
2013
+ configurable: true
2014
+ });
2015
+ return binding;
2016
+ }
2017
+ case "Variable": {
2018
+ const variable = node.name;
2019
+ if (!this._scope.hasValue(variable)) {
2020
+ throw new Error(`Error referencing undefined variable ${variable}`);
2021
+ }
2022
+ return this._scope.getValue(variable);
2023
+ }
2024
+ case "IndexExpression":
2025
+ return this.visit(node.right, this.visit(node.left, value));
2026
+ case "Subexpression": {
2027
+ const result = this.visit(node.left, value);
2028
+ return result != null ? this.visit(node.right, result) ?? null : null;
2029
+ }
2030
+ case "Index": {
2031
+ if (!Array.isArray(value)) {
2032
+ return null;
2033
+ }
2034
+ const index = node.value < 0 ? value.length + node.value : node.value;
2035
+ return value[index] ?? null;
2036
+ }
2037
+ case "Slice": {
2038
+ if (!Array.isArray(value) && typeof value !== "string") {
2039
+ return null;
2040
+ }
2041
+ const target = typeof value === "string" ? [...value] : value;
2042
+ const { start, stop, step } = this.computeSliceParams(target.length, node);
2043
+ const sliced = this.slice(target, start, stop, step);
2044
+ const res = typeof value === "string" ? sliced.join("") : sliced;
2045
+ this.checkOutputSize(res);
2046
+ return res;
2047
+ }
2048
+ case "Projection": {
2049
+ const { left, right } = node;
2050
+ const allowString = left.type === "IndexExpression" && left.right.type === "Slice";
2051
+ const base = this.visit(left, value);
2052
+ if (allowString && typeof base === "string") {
2053
+ return this.visit(right, base);
2054
+ }
2055
+ return Array.isArray(base) ? this.evaluateProjectionItems(base, right) : null;
2056
+ }
2057
+ case "ValueProjection": {
2058
+ const base = this.visit(node.left, value);
2059
+ if (base === null || typeof base !== "object" || Array.isArray(base)) {
2060
+ return null;
2061
+ }
2062
+ return this.evaluateProjectionItems(Object.values(base), node.right);
2063
+ }
2064
+ case "FilterProjection": {
2065
+ const { left, right, condition } = node;
2066
+ const base = this.visit(left, value);
2067
+ if (!Array.isArray(base)) {
2068
+ return null;
2069
+ }
2070
+ const results = [];
2071
+ if (right.type === "Field") {
2072
+ const key = right.name;
2073
+ for (let i = 0; i < base.length; i++) {
2074
+ const elem = base[i];
2075
+ if (isFalse(this.visit(condition, elem))) {
2076
+ continue;
2077
+ }
2078
+ if (!elem || typeof elem !== "object" || Array.isArray(elem)) {
2079
+ continue;
2080
+ }
2081
+ if (!Object.prototype.hasOwnProperty.call(elem, key)) {
2082
+ continue;
2083
+ }
2084
+ const val = elem[key];
2085
+ if (val !== void 0 && val !== null) {
2086
+ this.collect(results, val);
2087
+ }
2088
+ }
2089
+ return results;
2090
+ }
2091
+ if (right.type === "Identity") {
2092
+ for (let i = 0; i < base.length; i++) {
2093
+ const elem = base[i];
2094
+ if (isFalse(this.visit(condition, elem))) {
2095
+ continue;
2096
+ }
2097
+ if (elem !== null) {
2098
+ this.collect(results, elem);
2099
+ }
2100
+ }
2101
+ return results;
2102
+ }
2103
+ for (const elem of base) {
2104
+ if (isFalse(this.visit(condition, elem))) {
2105
+ continue;
2106
+ }
2107
+ const result = this.visit(right, elem);
2108
+ if (result !== null) {
2109
+ this.collect(results, result);
2110
+ }
2111
+ }
2112
+ return results;
2113
+ }
2114
+ case "Arithmetic": {
2115
+ const first = this.visit(node.left, value);
2116
+ const second = this.visit(node.right, value);
2117
+ switch (node.operator) {
2118
+ case "Plus":
2119
+ return add(first, second);
2120
+ case "Minus":
2121
+ return sub(first, second);
2122
+ case "Multiply":
2123
+ case "Star":
2124
+ return mul(first, second);
2125
+ case "Divide":
2126
+ return divide(first, second);
2127
+ case "Modulo":
2128
+ return mod(first, second);
2129
+ case "Div":
2130
+ return div(first, second);
2131
+ default:
2132
+ throw new Error(`Syntax error: unknown arithmetic operator: ${node.operator}`);
2133
+ }
2134
+ }
2135
+ case "Unary": {
2136
+ const operand = this.visit(node.operand, value);
2137
+ switch (node.operator) {
2138
+ case "Plus":
2139
+ ensureNumbers(operand);
2140
+ return operand;
2141
+ case "Minus":
2142
+ ensureNumbers(operand);
2143
+ return -operand;
2144
+ default:
2145
+ throw new Error(`Syntax error: unknown arithmetic operator: ${node.operator}`);
2146
+ }
2147
+ }
2148
+ case "Comparator": {
2149
+ const first = this.visit(node.left, value);
2150
+ const second = this.visit(node.right, value);
2151
+ switch (node.name) {
2152
+ case "EQ":
2153
+ return strictDeepEqual(first, second);
2154
+ case "NE":
2155
+ return !strictDeepEqual(first, second);
2156
+ }
2157
+ if (typeof first !== "number" || typeof second !== "number") {
2158
+ return null;
2159
+ }
2160
+ switch (node.name) {
2161
+ case "GT":
2162
+ return first > second;
2163
+ case "GTE":
2164
+ return first >= second;
2165
+ case "LT":
2166
+ return first < second;
2167
+ case "LTE":
2168
+ return first <= second;
2169
+ }
2170
+ }
2171
+ case "Flatten": {
2172
+ const original = this.visit(node.child, value);
2173
+ const res = Array.isArray(original) ? original.flat() : null;
2174
+ this.checkOutputSize(res);
2175
+ return res;
2176
+ }
2177
+ case "Root":
2178
+ return this._rootValue;
2179
+ case "MultiSelectList": {
2180
+ const collected = [];
2181
+ for (const child of node.children) {
2182
+ collected.push(this.visit(child, value));
2183
+ }
2184
+ this.checkOutputSize(collected);
2185
+ return collected;
2186
+ }
2187
+ case "MultiSelectHash": {
2188
+ const collected = {};
2189
+ for (const child of node.children) {
2190
+ Object.defineProperty(collected, child.name, {
2191
+ value: this.visit(child.value, value),
2192
+ enumerable: true,
2193
+ writable: true,
2194
+ configurable: true
2195
+ });
2196
+ }
2197
+ this.checkOutputSize(collected);
2198
+ return collected;
2199
+ }
2200
+ case "OrExpression": {
2201
+ const result = this.visit(node.left, value);
2202
+ if (isFalse(result)) {
2203
+ return this.visit(node.right, value);
2204
+ }
2205
+ return result;
2206
+ }
2207
+ case "AndExpression": {
2208
+ const result = this.visit(node.left, value);
2209
+ if (isFalse(result)) {
2210
+ return result;
2211
+ }
2212
+ return this.visit(node.right, value);
2213
+ }
2214
+ case "NotExpression":
2215
+ return isFalse(this.visit(node.child, value));
2216
+ case "Literal":
2217
+ return node.value;
2218
+ case "Pipe":
2219
+ return this.visit(node.right, this.visit(node.left, value));
2220
+ case "Function": {
2221
+ if (this._options?.allowedFunctions && !this._options.allowedFunctions.includes(node.name)) {
2222
+ throw new Error(`Function '${node.name}' is not allowed`);
2223
+ }
2224
+ if (this._options?.disallowedFunctions && this._options.disallowedFunctions.includes(node.name)) {
2225
+ throw new Error(`Function '${node.name}' is disallowed`);
2226
+ }
2227
+ const args = [];
2228
+ for (const child of node.children) {
2229
+ args.push(this.visit(child, value));
2230
+ }
2231
+ const prevInterpreter = this.runtime._interpreter;
2232
+ this.runtime._interpreter = this;
2233
+ try {
2234
+ const res = this.runtime.callFunction(node.name, args);
2235
+ this.checkOutputSize(res);
2236
+ return res;
2237
+ } finally {
2238
+ this.runtime._interpreter = prevInterpreter;
2239
+ }
2240
+ }
2241
+ case "ExpressionReference":
2242
+ return {
2243
+ expref: true,
2244
+ ...node.child
2245
+ };
2246
+ case "Current":
2247
+ case "Identity":
2248
+ return value;
2249
+ }
2250
+ }
2251
+ computeSliceParams(arrayLength, sliceNode) {
2252
+ let { start, stop, step } = sliceNode;
2253
+ if (step === null) {
2254
+ step = 1;
2255
+ } else if (step === 0) {
2256
+ const error = new Error("Invalid value: slice step cannot be 0");
2257
+ error.name = "RuntimeError";
2258
+ throw error;
2259
+ }
2260
+ start = start === null ? step < 0 ? arrayLength - 1 : 0 : this.capSliceRange(arrayLength, start, step);
2261
+ stop = stop === null ? step < 0 ? -1 : arrayLength : this.capSliceRange(arrayLength, stop, step);
2262
+ return { start, stop, step };
2263
+ }
2264
+ capSliceRange(arrayLength, actualValue, step) {
2265
+ let nextActualValue = actualValue;
2266
+ if (nextActualValue < 0) {
2267
+ nextActualValue += arrayLength;
2268
+ if (nextActualValue < 0) {
2269
+ nextActualValue = step < 0 ? -1 : 0;
2270
+ }
2271
+ } else if (nextActualValue >= arrayLength) {
2272
+ nextActualValue = step < 0 ? arrayLength - 1 : arrayLength;
2273
+ }
2274
+ return nextActualValue;
2275
+ }
2276
+ slice(collection, start, end, step) {
2277
+ if (step === 1) {
2278
+ return collection.slice(start, end);
2279
+ }
2280
+ const len = Math.max(0, Math.ceil((end - start) / step));
2281
+ const result = new Array(len);
2282
+ let idx = 0;
2283
+ if (step > 0) {
2284
+ for (let i = start; i < end; i += step) {
2285
+ result[idx++] = collection[i];
2286
+ }
2287
+ } else {
2288
+ for (let i = start; i > end; i += step) {
2289
+ result[idx++] = collection[i];
2290
+ }
2291
+ }
2292
+ return result;
2293
+ }
2294
+ };
2295
+ var TreeInterpreterInstance = new TreeInterpreter();
2296
+ var TreeInterpreter_default = TreeInterpreterInstance;
2297
+ function compile(expression, options) {
2298
+ return Parser_default.parse(expression, options);
2299
+ }
2300
+ var TreeInterpreter2 = TreeInterpreter_default;
2301
+
2302
+ // packages/recipe/src/jmes.ts
2303
+ var cache = /* @__PURE__ */ new Map();
2304
+ function compileJmes(expr) {
2305
+ let ast = cache.get(expr);
2306
+ if (!ast) {
2307
+ ast = compile(expr);
2308
+ if (cache.size > 2e3) cache.clear();
2309
+ cache.set(expr, ast);
2310
+ }
2311
+ return ast;
2312
+ }
2313
+ function jmes(data, expr) {
2314
+ const v = TreeInterpreter2.search(compileJmes(expr), data ?? null);
2315
+ return v === void 0 ? null : v;
2316
+ }
2317
+ function jmesCtx(ctx, expr) {
2318
+ return jmes(toJson(ctx), expr);
2319
+ }
2320
+ function truthy(v) {
2321
+ if (v === null || v === void 0 || v === false || v === "") return false;
2322
+ if (Array.isArray(v)) return v.length > 0;
2323
+ if (typeof v === "object") return Object.keys(v).length > 0;
2324
+ return true;
2325
+ }
2326
+ function toJson(v) {
2327
+ if (v === void 0) return null;
2328
+ if (v instanceof Date) return v.toISOString();
2329
+ if (Array.isArray(v)) return v.map((x) => x === void 0 ? null : toJson(x));
2330
+ if (v && typeof v === "object") {
2331
+ const out = {};
2332
+ for (const [k, x] of Object.entries(v)) if (x !== void 0) out[k] = toJson(x);
2333
+ return out;
2334
+ }
2335
+ return v;
2336
+ }
2337
+
2338
+ // packages/recipe/src/html.ts
2339
+ var SELF = /* @__PURE__ */ new Set(["", ".", ":scope"]);
2340
+ var NOT_TEXT = /* @__PURE__ */ new Set(["STYLE", "SCRIPT", "NOSCRIPT", "TEMPLATE"]);
2341
+ function visibleText(node) {
2342
+ if (!node.childNodes || NOT_TEXT.has((node.nodeName ?? "").toUpperCase())) return node.textContent ?? "";
2343
+ let out = "";
2344
+ const walk2 = (n) => {
2345
+ const kids = n.childNodes;
2346
+ if (!kids) return;
2347
+ for (let i = 0; i < kids.length; i++) {
2348
+ const c = kids[i];
2349
+ if (c.nodeType === 3 || c.nodeType === 4) out += c.nodeValue ?? c.textContent ?? "";
2350
+ else if (c.nodeType === 1 && !NOT_TEXT.has((c.nodeName ?? "").toUpperCase())) walk2(c);
2351
+ }
2352
+ };
2353
+ walk2(node);
2354
+ return out;
2355
+ }
2356
+ function readNode(node, attr) {
2357
+ if (attr === "textContent") return (node.textContent ?? "").replace(/\s+/g, " ").trim();
2358
+ if (attr && attr !== "text") return node.getAttribute(attr);
2359
+ return visibleText(node).replace(/\s+/g, " ").trim();
2360
+ }
2361
+ var MAX_JSON_VALUE = 256 * 1024;
2362
+ function readValue(node, f) {
2363
+ if (f.parse !== "json") return readNode(node, f.attr);
2364
+ const raw = f.attr && f.attr !== "text" && f.attr !== "textContent" ? node.getAttribute(f.attr) : node.textContent ?? "";
2365
+ if (raw === null || raw.length > MAX_JSON_VALUE) return null;
2366
+ let data;
2367
+ try {
2368
+ data = JSON.parse(raw);
2369
+ } catch {
2370
+ return null;
2371
+ }
2372
+ if (!f.path) return data;
2373
+ try {
2374
+ return jmes(data, f.path) ?? null;
2375
+ } catch {
2376
+ return null;
2377
+ }
2378
+ }
2379
+ function select(root, selector) {
2380
+ if (SELF.has(selector.trim())) return root;
2381
+ return root.querySelector(selector);
2382
+ }
2383
+ function extractHtml(doc, spec) {
2384
+ if (spec.all) {
2385
+ return Array.from(doc.querySelectorAll(spec.selector)).map((n) => readValue(n, spec));
2386
+ }
2387
+ const node = doc.querySelector(spec.selector);
2388
+ return node ? readValue(node, spec) : null;
2389
+ }
2390
+ function extractHtmlItems(doc, spec) {
2391
+ const max = Math.min(spec.max ?? 50, 100);
2392
+ const nodes = Array.from(doc.querySelectorAll(spec.selector)).slice(0, max);
2393
+ return nodes.map((item) => {
2394
+ const row = {};
2395
+ for (const [name, f] of Object.entries(spec.fields)) {
2396
+ const n = select(item, f.selector);
2397
+ row[name] = n ? readValue(n, f) : null;
2398
+ }
2399
+ return row;
2400
+ });
2401
+ }
2402
+ function extractEmbeddedJson(doc, spec, step) {
2403
+ const node = doc.querySelector(spec.selector);
2404
+ if (!node) {
2405
+ if (spec.required === false) return Object.fromEntries(Object.keys(spec.vars).map((k) => [k, null]));
2406
+ throw new RecipeError("expect_failed", `embedded_json: no element matches ${spec.selector}`, step);
2407
+ }
2408
+ let raw = (spec.attr ? node.getAttribute(spec.attr) : node.textContent) ?? "";
2409
+ if (spec.strip_js || spec.strip_js_after !== void 0) {
2410
+ let from = raw;
2411
+ if (spec.strip_js_after !== void 0) {
2412
+ const at = raw.indexOf(spec.strip_js_after);
2413
+ if (at < 0) {
2414
+ if (spec.required === false) return Object.fromEntries(Object.keys(spec.vars).map((k) => [k, null]));
2415
+ throw new RecipeError("expect_failed", `embedded_json: ${spec.selector} does not contain ${JSON.stringify(spec.strip_js_after.slice(0, 60))}`, step);
2416
+ }
2417
+ from = raw.slice(at + spec.strip_js_after.length);
2418
+ }
2419
+ const cut = firstBalancedJson(from);
2420
+ if (cut === null) throw new RecipeError("expect_failed", `embedded_json: no JSON object or array found in ${spec.selector}`, step);
2421
+ raw = cut;
2422
+ }
2423
+ let data;
2424
+ try {
2425
+ data = JSON.parse(raw);
2426
+ } catch {
2427
+ throw new RecipeError("expect_failed", `embedded_json: ${spec.selector}${spec.attr ? `[${spec.attr}]` : ""} is not valid JSON`, step);
2428
+ }
2429
+ if (spec.decode === "astro_props") data = decodeAstroProps(data);
2430
+ const out = {};
2431
+ for (const [name, expr] of Object.entries(spec.vars)) out[name] = jmes(data, expr);
2432
+ return out;
2433
+ }
2434
+ function firstBalancedJson(text) {
2435
+ const start = text.search(/[{[]/);
2436
+ if (start < 0) return null;
2437
+ let depth = 0;
2438
+ let quote = null;
2439
+ for (let i = start; i < text.length; i++) {
2440
+ const c = text[i];
2441
+ if (quote) {
2442
+ if (c === "\\") i++;
2443
+ else if (c === quote) quote = null;
2444
+ continue;
2445
+ }
2446
+ if (c === '"' || c === "'" || c === "`") quote = c;
2447
+ else if (c === "{" || c === "[") depth++;
2448
+ else if (c === "}" || c === "]") {
2449
+ depth--;
2450
+ if (depth === 0) return text.slice(start, i + 1);
2451
+ }
2452
+ }
2453
+ return null;
2454
+ }
2455
+ function decodeAstroProps(props) {
2456
+ if (!props || typeof props !== "object" || Array.isArray(props)) return decodeAstroValue(props);
2457
+ const out = {};
2458
+ for (const [k, v] of Object.entries(props)) out[k] = decodeAstroValue(v);
2459
+ return out;
2460
+ }
2461
+ function decodeAstroValue(v) {
2462
+ if (!Array.isArray(v) || v.length !== 2 || typeof v[0] !== "number") return v;
2463
+ const [type, value] = v;
2464
+ switch (type) {
2465
+ case 0:
2466
+ if (value && typeof value === "object" && !Array.isArray(value)) {
2467
+ const out = {};
2468
+ for (const [k, x] of Object.entries(value)) out[k] = decodeAstroValue(x);
2469
+ return out;
2470
+ }
2471
+ return value;
2472
+ case 1:
2473
+ return Array.isArray(value) ? value.map(decodeAstroValue) : value;
2474
+ case 3:
2475
+ return typeof value === "string" ? value : String(value);
2476
+ default:
2477
+ return value;
2478
+ }
2479
+ }
2480
+
2481
+ // packages/recipe/src/params.ts
2482
+ function prepareParams(schema, input) {
2483
+ if (input === void 0 || input === null) input = {};
2484
+ if (typeof input !== "object" || Array.isArray(input)) {
2485
+ throw new RecipeError("invalid_params", "params must be an object");
2486
+ }
2487
+ const params = { ...input };
2488
+ const props = schema.properties ?? {};
2489
+ const problems = [];
2490
+ for (const key of Object.keys(params)) {
2491
+ if (!(key in props)) {
2492
+ if (schema.additionalProperties === false) problems.push(`unknown param "${key}"`);
2493
+ }
2494
+ }
2495
+ for (const [key, prop] of Object.entries(props)) {
2496
+ if (params[key] === void 0 && prop.default !== void 0) params[key] = structuredCloneSafe(prop.default);
2497
+ }
2498
+ for (const key of schema.required ?? []) {
2499
+ if (params[key] === void 0 || params[key] === null) problems.push(`missing required param "${key}"`);
2500
+ }
2501
+ for (const [key, prop] of Object.entries(props)) {
2502
+ const v = params[key];
2503
+ if (v === void 0) continue;
2504
+ const p = checkValue(key, v, prop);
2505
+ if (p) problems.push(p);
2506
+ }
2507
+ if (problems.length) throw new RecipeError("invalid_params", problems.join("; "));
2508
+ return params;
2509
+ }
2510
+ function typeOf(v) {
2511
+ if (v === null) return "null";
2512
+ if (Array.isArray(v)) return "array";
2513
+ if (typeof v === "number") return Number.isInteger(v) ? "integer" : "number";
2514
+ return typeof v;
2515
+ }
2516
+ function typeMatches(actual, expected) {
2517
+ if (expected === actual) return true;
2518
+ if (expected === "number" && actual === "integer") return true;
2519
+ return false;
2520
+ }
2521
+ function checkValue(key, v, s2) {
2522
+ const actual = typeOf(v);
2523
+ if (s2.type) {
2524
+ const types = Array.isArray(s2.type) ? s2.type : [s2.type];
2525
+ if (!types.some((t) => typeMatches(actual, t))) return `param "${key}" must be ${types.join(" or ")}`;
2526
+ }
2527
+ if (s2.enum && !s2.enum.some((e) => e === v)) return `param "${key}" must be one of ${s2.enum.map((e) => JSON.stringify(e)).join(", ")}`;
2528
+ if (typeof v === "number") {
2529
+ if (typeof s2.minimum === "number" && v < s2.minimum) return `param "${key}" must be \u2265 ${s2.minimum}`;
2530
+ if (typeof s2.maximum === "number" && v > s2.maximum) return `param "${key}" must be \u2264 ${s2.maximum}`;
2531
+ }
2532
+ if (typeof v === "string") {
2533
+ if (typeof s2.minLength === "number" && v.length < s2.minLength) return `param "${key}" is too short`;
2534
+ if (typeof s2.maxLength === "number" && v.length > s2.maxLength) return `param "${key}" is too long`;
2535
+ if (typeof s2.pattern === "string") {
2536
+ let re = null;
2537
+ try {
2538
+ re = new RegExp(s2.pattern, "u");
2539
+ } catch {
2540
+ re = null;
2541
+ }
2542
+ if (re && !re.test(v)) return `param "${key}" does not match the expected format`;
2543
+ }
2544
+ }
2545
+ return null;
2546
+ }
2547
+ function structuredCloneSafe(v) {
2548
+ return v === null || typeof v !== "object" ? v : JSON.parse(JSON.stringify(v));
2549
+ }
2550
+
2551
+ // packages/recipe/src/redact.ts
2552
+ var REDACTED = "[redacted]";
2553
+ function secretStrings(v, acc = []) {
2554
+ if (v === null || v === void 0) return acc;
2555
+ if (typeof v === "string") {
2556
+ if (v.length >= 4) {
2557
+ acc.push(v);
2558
+ const enc2 = encodeURIComponent(v);
2559
+ if (enc2 !== v) acc.push(enc2);
2560
+ }
2561
+ } else if (typeof v === "number" || typeof v === "boolean") {
2562
+ const s2 = String(v);
2563
+ if (s2.length >= 6) acc.push(s2);
2564
+ } else if (Array.isArray(v)) v.forEach((x) => secretStrings(x, acc));
2565
+ else if (typeof v === "object") {
2566
+ Object.values(v).forEach((x) => secretStrings(x, acc));
2567
+ }
2568
+ return acc;
2569
+ }
2570
+ function redactString(s2, secrets) {
2571
+ let out = s2;
2572
+ for (const sec of secrets) if (out.includes(sec)) out = out.split(sec).join(REDACTED);
2573
+ return out;
2574
+ }
2575
+ function redactDeep(v, secrets) {
2576
+ if (!secrets.length) return v;
2577
+ return walk(v, secrets);
2578
+ }
2579
+ function walk(v, secrets) {
2580
+ if (typeof v === "string") return redactString(v, secrets);
2581
+ if (typeof v === "number" || typeof v === "boolean") {
2582
+ const s2 = String(v);
2583
+ return secrets.includes(s2) ? REDACTED : v;
2584
+ }
2585
+ if (Array.isArray(v)) return v.map((x) => walk(x, secrets));
2586
+ if (v && typeof v === "object") {
2587
+ const out = {};
2588
+ for (const [k, x] of Object.entries(v)) out[redactString(k, secrets)] = walk(x, secrets);
2589
+ return out;
2590
+ }
2591
+ return v;
2592
+ }
2593
+
2594
+ // packages/recipe/src/template.ts
2595
+ var FILTERS = [
2596
+ "urlencode",
2597
+ "json",
2598
+ "lower",
2599
+ "upper",
2600
+ "trim",
2601
+ "string",
2602
+ "number",
2603
+ "default",
2604
+ "date",
2605
+ "add_days",
2606
+ "join",
2607
+ "first"
2608
+ ];
2609
+ var cache2 = /* @__PURE__ */ new Map();
2610
+ function parseTemplate(src) {
2611
+ const hit = cache2.get(src);
2612
+ if (hit) return hit;
2613
+ const parts = [];
2614
+ let i = 0;
2615
+ while (i < src.length) {
2616
+ const open = src.indexOf("{{", i);
2617
+ if (open < 0) {
2618
+ parts.push({ kind: "text", text: src.slice(i) });
2619
+ break;
2620
+ }
2621
+ if (open > i) parts.push({ kind: "text", text: src.slice(i, open) });
2622
+ const close = findClose(src, open + 2);
2623
+ if (close < 0) throw new TemplateError(`unclosed {{ in template: ${truncate(src)}`);
2624
+ parts.push({ kind: "expr", expr: parseExpr(src.slice(open + 2, close)) });
2625
+ i = close + 2;
2626
+ }
2627
+ const parsed = {
2628
+ parts,
2629
+ single: parts.length === 1 && parts[0].kind === "expr"
2630
+ };
2631
+ if (cache2.size > 2e3) cache2.clear();
2632
+ cache2.set(src, parsed);
2633
+ return parsed;
2634
+ }
2635
+ function findClose(src, from) {
2636
+ let quote = null;
2637
+ for (let j = from; j < src.length; j++) {
2638
+ const c = src[j];
2639
+ if (quote) {
2640
+ if (c === "\\") j++;
2641
+ else if (c === quote) quote = null;
2642
+ continue;
2643
+ }
2644
+ if (c === "'" || c === '"') quote = c;
2645
+ else if (c === "}" && src[j + 1] === "}") return j;
2646
+ }
2647
+ return -1;
2648
+ }
2649
+ function splitPipes(s2) {
2650
+ const out = [];
2651
+ let quote = null;
2652
+ let cur = "";
2653
+ for (let j = 0; j < s2.length; j++) {
2654
+ const c = s2[j];
2655
+ if (quote) {
2656
+ cur += c;
2657
+ if (c === "\\" && j + 1 < s2.length) cur += s2[++j];
2658
+ else if (c === quote) quote = null;
2659
+ continue;
2660
+ }
2661
+ if (c === "'" || c === '"') {
2662
+ quote = c;
2663
+ cur += c;
2664
+ } else if (c === "|") {
2665
+ out.push(cur);
2666
+ cur = "";
2667
+ } else cur += c;
2668
+ }
2669
+ out.push(cur);
2670
+ return out;
2671
+ }
2672
+ function parseExpr(inner) {
2673
+ const [pathRaw, ...filterRaws] = splitPipes(inner);
2674
+ const pathText = (pathRaw ?? "").trim();
2675
+ if (!pathText) throw new TemplateError(`empty expression in {{${inner}}}`);
2676
+ const path = parsePath(pathText);
2677
+ const filters = filterRaws.map((f) => parseFilter(f.trim(), inner));
2678
+ return { path, pathText, filters };
2679
+ }
2680
+ var IDENT = /^[A-Za-z_$][A-Za-z0-9_$-]*$/;
2681
+ function parsePath(text) {
2682
+ const segs = [];
2683
+ for (const raw of text.split(".")) {
2684
+ const m = /^([^[\]]*)((?:\[\d+\])*)$/.exec(raw);
2685
+ if (!m) throw new TemplateError(`bad path "${text}"`);
2686
+ const name = m[1];
2687
+ if (name) {
2688
+ if (!IDENT.test(name) && !/^\d+$/.test(name)) throw new TemplateError(`bad path segment "${name}" in "${text}"`);
2689
+ segs.push(/^\d+$/.test(name) && segs.length > 0 ? Number(name) : name);
2690
+ } else if (!m[2]) {
2691
+ throw new TemplateError(`bad path "${text}"`);
2692
+ }
2693
+ for (const idx of m[2].matchAll(/\[(\d+)\]/g)) segs.push(Number(idx[1]));
2694
+ }
2695
+ if (typeof segs[0] !== "string") throw new TemplateError(`path must start with a name: "${text}"`);
2696
+ return segs;
2697
+ }
2698
+ function parseFilter(raw, inner) {
2699
+ const colon = raw.indexOf(":");
2700
+ const name = (colon < 0 ? raw : raw.slice(0, colon)).trim();
2701
+ if (!FILTERS.includes(name)) {
2702
+ throw new TemplateError(`unknown filter "${name}" in {{${inner}}}`);
2703
+ }
2704
+ if (colon < 0) {
2705
+ if (name === "default" || name === "date" || name === "add_days") {
2706
+ throw new TemplateError(`filter "${name}" needs an argument in {{${inner}}}`);
2707
+ }
2708
+ return { name, hasArg: false };
2709
+ }
2710
+ const arg = parseLiteral(raw.slice(colon + 1).trim());
2711
+ if (name === "add_days" && typeof arg !== "number") {
2712
+ throw new TemplateError(`add_days needs a number in {{${inner}}}`);
2713
+ }
2714
+ return { name, arg, hasArg: true };
2715
+ }
2716
+ function parseLiteral(s2) {
2717
+ if (s2.length >= 2 && (s2[0] === "'" || s2[0] === '"') && s2[s2.length - 1] === s2[0]) {
2718
+ return s2.slice(1, -1).replace(/\\(.)/g, "$1");
2719
+ }
2720
+ if (s2 === "true") return true;
2721
+ if (s2 === "false") return false;
2722
+ if (s2 === "null") return null;
2723
+ if (/^-?\d+(\.\d+)?$/.test(s2)) return Number(s2);
2724
+ return s2;
2725
+ }
2726
+ function lookup(ctx, path) {
2727
+ let cur = ctx;
2728
+ for (const seg of path) {
2729
+ if (cur === null || cur === void 0) return void 0;
2730
+ if (typeof seg === "number") {
2731
+ cur = Array.isArray(cur) ? cur[seg] : cur[String(seg)];
2732
+ } else {
2733
+ if (typeof cur !== "object") return void 0;
2734
+ if (!Object.prototype.hasOwnProperty.call(cur, seg)) return void 0;
2735
+ cur = cur[seg];
2736
+ }
2737
+ }
2738
+ return cur;
2739
+ }
2740
+ function evalExpr(expr, ctx) {
2741
+ let v = lookup(ctx, expr.path);
2742
+ for (const f of expr.filters) v = applyFilter(f, v);
2743
+ return v;
2744
+ }
2745
+ function applyFilter(f, v) {
2746
+ switch (f.name) {
2747
+ case "urlencode":
2748
+ return encodeURIComponent(toText(v));
2749
+ case "json":
2750
+ return JSON.stringify(normalizeValue(v) ?? null);
2751
+ case "lower":
2752
+ return v == null ? v : toText(v).toLowerCase();
2753
+ case "upper":
2754
+ return v == null ? v : toText(v).toUpperCase();
2755
+ case "trim":
2756
+ return v == null ? v : toText(v).trim();
2757
+ case "string":
2758
+ return v == null ? "" : toText(v);
2759
+ case "number": {
2760
+ if (v == null || v === "") return v;
2761
+ const n = typeof v === "number" ? v : Number(toText(v).trim());
2762
+ if (Number.isNaN(n)) throw new TemplateError(`number filter: not a number`);
2763
+ return n;
2764
+ }
2765
+ case "default":
2766
+ return v === void 0 || v === null || v === "" ? f.arg : v;
2767
+ case "date":
2768
+ return v == null ? v : formatDate(toDate(v), String(f.arg));
2769
+ case "add_days":
2770
+ return v == null ? v : addDays(v, f.arg);
2771
+ case "join": {
2772
+ const sep = f.hasArg ? String(f.arg) : ",";
2773
+ return Array.isArray(v) ? v.map((x) => toText(x)).join(sep) : v;
2774
+ }
2775
+ case "first":
2776
+ return Array.isArray(v) ? v[0] : v;
2777
+ }
2778
+ }
2779
+ function renderString(src, ctx) {
2780
+ if (!src.includes("{{")) return src;
2781
+ const t = parseTemplate(src);
2782
+ if (t.single) return normalizeValue(evalExpr(t.parts[0].expr, ctx));
2783
+ let out = "";
2784
+ for (const p of t.parts) out += p.kind === "text" ? p.text : toText(evalExpr(p.expr, ctx));
2785
+ return out;
2786
+ }
2787
+ function renderValue(value, ctx) {
2788
+ if (typeof value === "string") return renderString(value, ctx);
2789
+ if (Array.isArray(value)) return value.map((v) => renderValue(v, ctx));
2790
+ if (value && typeof value === "object") {
2791
+ const out = {};
2792
+ for (const [k, v] of Object.entries(value)) {
2793
+ const r = renderValue(v, ctx);
2794
+ if (r !== void 0) out[k] = r;
2795
+ }
2796
+ return out;
2797
+ }
2798
+ return value;
2799
+ }
2800
+ function toText(v) {
2801
+ if (v === void 0 || v === null) return "";
2802
+ if (typeof v === "string") return v;
2803
+ if (v instanceof Date) return localIso(v);
2804
+ if (typeof v === "object") return JSON.stringify(normalizeValue(v));
2805
+ return String(v);
2806
+ }
2807
+ function normalizeValue(v) {
2808
+ if (v instanceof Date) return localIso(v);
2809
+ if (Array.isArray(v)) return v.map(normalizeValue);
2810
+ if (v && typeof v === "object") {
2811
+ let changed = false;
2812
+ const out = {};
2813
+ for (const [k, x] of Object.entries(v)) {
2814
+ const n = normalizeValue(x);
2815
+ if (n !== x) changed = true;
2816
+ out[k] = n;
2817
+ }
2818
+ return changed ? out : v;
2819
+ }
2820
+ return v;
2821
+ }
2822
+ var DATE_ONLY = /^(\d{4})-(\d{2})-(\d{2})$/;
2823
+ function toDate(v) {
2824
+ if (v instanceof Date) return v;
2825
+ if (typeof v === "number") return new Date(v);
2826
+ const s2 = String(v).trim();
2827
+ const m = DATE_ONLY.exec(s2);
2828
+ if (m) return new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
2829
+ const d = new Date(s2);
2830
+ if (Number.isNaN(d.getTime())) throw new TemplateError(`not a date: "${truncate(s2)}"`);
2831
+ return d;
2832
+ }
2833
+ function addDays(v, n) {
2834
+ const dateOnly = typeof v === "string" && DATE_ONLY.test(v.trim());
2835
+ const d = new Date(toDate(v).getTime());
2836
+ d.setDate(d.getDate() + n);
2837
+ return dateOnly ? formatDate(d, "YYYY-MM-DD") : d;
2838
+ }
2839
+ var pad = (n, w = 2) => String(n).padStart(w, "0");
2840
+ function localIso(d) {
2841
+ return formatDate(d, "YYYY-MM-DDTHH:mm:ss");
2842
+ }
2843
+ function formatDate(d, fmt) {
2844
+ return fmt.replace(/YYYY|SSS|MM|DD|HH|mm|ss|ZZ/g, (tok) => {
2845
+ switch (tok) {
2846
+ case "ZZ": {
2847
+ const off = -d.getTimezoneOffset();
2848
+ const a = Math.abs(off);
2849
+ return `${off < 0 ? "-" : "+"}${pad(Math.floor(a / 60))}:${pad(a % 60)}`;
2850
+ }
2851
+ case "YYYY":
2852
+ return pad(d.getFullYear(), 4);
2853
+ case "MM":
2854
+ return pad(d.getMonth() + 1);
2855
+ case "DD":
2856
+ return pad(d.getDate());
2857
+ case "HH":
2858
+ return pad(d.getHours());
2859
+ case "mm":
2860
+ return pad(d.getMinutes());
2861
+ case "ss":
2862
+ return pad(d.getSeconds());
2863
+ case "SSS":
2864
+ return pad(d.getMilliseconds(), 3);
2865
+ default:
2866
+ return tok;
2867
+ }
2868
+ });
2869
+ }
2870
+ function truncate(s2, n = 80) {
2871
+ return s2.length > n ? s2.slice(0, n) + "\u2026" : s2;
2872
+ }
2873
+
2874
+ // packages/recipe/src/decoders.ts
2875
+ function decodeTurboStream(text) {
2876
+ const firstLine = text.split("\n", 1)[0] ?? "";
2877
+ const values = JSON.parse(firstLine);
2878
+ if (!Array.isArray(values)) throw new Error("turbo-stream: root is not an array");
2879
+ const done = /* @__PURE__ */ new Map();
2880
+ const hydrate = (index) => {
2881
+ switch (index) {
2882
+ case -1:
2883
+ case -7:
2884
+ return null;
2885
+ // hole / undefined
2886
+ case -2:
2887
+ return null;
2888
+ // NaN is not JSON
2889
+ case -3:
2890
+ return null;
2891
+ // -Infinity
2892
+ case -4:
2893
+ return 0;
2894
+ // -0
2895
+ case -5:
2896
+ return null;
2897
+ case -6:
2898
+ return null;
2899
+ }
2900
+ if (typeof index !== "number" || index < 0 || index >= values.length) return null;
2901
+ if (done.has(index)) return done.get(index);
2902
+ const v = values[index];
2903
+ if (v === null || typeof v !== "object") {
2904
+ done.set(index, v);
2905
+ return v;
2906
+ }
2907
+ if (Array.isArray(v)) {
2908
+ if (v.length && typeof v[0] === "string") return tagged(index, v);
2909
+ const out2 = [];
2910
+ done.set(index, out2);
2911
+ for (const i of v) out2.push(hydrate(i));
2912
+ return out2;
2913
+ }
2914
+ const out = {};
2915
+ done.set(index, out);
2916
+ for (const [k, vi] of Object.entries(v)) {
2917
+ const key = /^_\d+$/.test(k) ? String(values[Number(k.slice(1))]) : k;
2918
+ out[key] = hydrate(vi);
2919
+ }
2920
+ return out;
2921
+ };
2922
+ const tagged = (index, v) => {
2923
+ const tag = v[0];
2924
+ let out;
2925
+ switch (tag) {
2926
+ case "D": {
2927
+ const d = new Date(v[1]);
2928
+ out = Number.isNaN(d.getTime()) ? null : d.toISOString();
2929
+ break;
2930
+ }
2931
+ case "B":
2932
+ // BigInt
2933
+ case "U":
2934
+ // URL
2935
+ case "R":
2936
+ // RegExp
2937
+ case "Y":
2938
+ out = String(v[1]);
2939
+ break;
2940
+ case "S": {
2941
+ const arr = [];
2942
+ done.set(index, arr);
2943
+ for (let i = 1; i < v.length; i++) arr.push(hydrate(v[i]));
2944
+ return arr;
2945
+ }
2946
+ case "M": {
2947
+ const obj = {};
2948
+ done.set(index, obj);
2949
+ for (let i = 1; i + 1 < v.length; i += 2) obj[String(hydrate(v[i]))] = hydrate(v[i + 1]);
2950
+ return obj;
2951
+ }
2952
+ case "N": {
2953
+ const obj = {};
2954
+ done.set(index, obj);
2955
+ for (let i = 1; i + 1 < v.length; i += 2) obj[String(values[v[i]])] = hydrate(v[i + 1]);
2956
+ return obj;
2957
+ }
2958
+ case "E":
2959
+ out = { error: String(v[1] ?? "") };
2960
+ break;
2961
+ case "P":
2962
+ // unresolved promise (deferred data)
2963
+ default:
2964
+ out = null;
2965
+ }
2966
+ done.set(index, out);
2967
+ return out;
2968
+ };
2969
+ return hydrate(0);
2970
+ }
2971
+
2972
+ // packages/recipe/src/blocked.ts
2973
+ var VENDOR_MARKERS = [
2974
+ [/cf-chl|cf_chl_opt|challenge-platform|Just a moment\.\.\.|Attention Required! \| Cloudflare|cf-turnstile/i, "Cloudflare"],
2975
+ [/OPS_BLOCKED|errors\.edgesuite\.net|Reference #\d+\.[0-9a-f]+|akamai|_abck|bm-verify|sec-cpt|sec-if-cpt|\/_sec\/cp_challenge|ak_bmsc/i, "Akamai"],
2976
+ [/captcha-delivery\.com|datadome/i, "DataDome"],
2977
+ [/px-captcha|_pxCaptcha|perimeterx|Press & Hold/i, "PerimeterX"],
2978
+ [/awswaf|aws-waf-token|AwsWafIntegration|challenge\.js.*token/i, "AWS WAF"],
2979
+ [/Incapsula incident|_Incapsula_Resource|imperva|Pardon Our Interruption/i, "Imperva"],
2980
+ [/hcaptcha\.com|recaptcha|g-recaptcha|geetest|captcha/i, "a CAPTCHA"]
2981
+ ];
2982
+ function vendorFrom(text, headers) {
2983
+ if ((headers("cf-mitigated") ?? "").toLowerCase() === "challenge") return "Cloudflare";
2984
+ if (headers("x-datadome") || headers("x-dd-b")) return "DataDome";
2985
+ if (/akamaighost/i.test(headers("server") ?? "")) return "Akamai";
2986
+ const sample = text.length > 2e5 ? text.slice(0, 2e5) : text;
2987
+ for (const [re, name] of VENDOR_MARKERS) if (re.test(sample)) return name;
2988
+ return void 0;
2989
+ }
2990
+ function detectBlock(status, text, headers, unexpectedBody) {
2991
+ if (status === 429) return { kind: "rate_limited", vendor: vendorFrom(text, headers) };
2992
+ const vendor = vendorFrom(text, headers);
2993
+ if (status === 403) return { kind: "challenge", vendor };
2994
+ if ((status === 503 || status === 401 || status === 202 || status === 200 || status === 405) && vendor && (unexpectedBody || status !== 200)) {
2995
+ return { kind: "challenge", vendor };
2996
+ }
2997
+ return null;
2998
+ }
2999
+ function blockMessage(site, status, b) {
3000
+ if (b.kind === "rate_limited") {
3001
+ return `${site.name} is rate-limiting requests (HTTP 429${b.vendor ? `, ${b.vendor}` : ""}). The Wield executor is working; wait a minute, then retry once.`;
3002
+ }
3003
+ return `${site.name} blocked the request with its bot protection (HTTP ${status}${b.vendor ? `, ${b.vendor}` : ""}). The Wield executor is online and working. Ask the user to open ${site.origin} in Chrome, use the site once (click a link or run a search, and solve a check if one appears), then retry.`;
3004
+ }
3005
+
3006
+ // packages/recipe/src/interpreter.ts
3007
+ var LIMITS = {
3008
+ steps: 12,
3009
+ foreach: 20,
3010
+ requests: 40,
3011
+ responseBytes: 5 * 1024 * 1024,
3012
+ runMs: 6e4,
3013
+ redirects: 3,
3014
+ confirmMs: 13e4
3015
+ };
3016
+ var FORBIDDEN_HEADERS = /* @__PURE__ */ new Set(["cookie", "cookie2", "host", "set-cookie"]);
3017
+ function stepDependencies(steps) {
3018
+ const ids = steps.map((s2) => s2.id);
3019
+ const esc = (x) => x.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3020
+ const barrier = (s2) => !!s2.write || s2.request.method !== "GET";
3021
+ return steps.map((s2, i) => {
3022
+ const all = Array.from({ length: i }, (_, j) => j);
3023
+ if (barrier(s2)) return all;
3024
+ const { id: _id, ...rest } = s2;
3025
+ const text = JSON.stringify(rest);
3026
+ if (/\bsteps\b(?!\.[A-Za-z0-9_])/.test(text)) return all;
3027
+ const deps = /* @__PURE__ */ new Set();
3028
+ ids.slice(0, i).forEach((x, j) => {
3029
+ if (new RegExp(`\\bsteps\\.${esc(x)}\\b`).test(text)) deps.add(j);
3030
+ });
3031
+ for (let j = i - 1; j >= 0; j--) {
3032
+ if (barrier(steps[j])) {
3033
+ deps.add(j);
3034
+ break;
3035
+ }
3036
+ }
3037
+ return [...deps].sort((a, b) => a - b);
3038
+ });
3039
+ }
3040
+ async function runRecipe(recipe, params, adapters, opts = {}) {
3041
+ return new Run(recipe, adapters, opts).execute(params);
3042
+ }
3043
+ async function checkLogin(target, adapters, opts = {}) {
3044
+ const recipe = {
3045
+ schema: "https://wield.run/schema/recipe/v1.json",
3046
+ id: target.id ?? `${target.site.id}/login-check`,
3047
+ version: 1,
3048
+ site: target.site,
3049
+ task: "Check login",
3050
+ effect: "read",
3051
+ requires_login: true,
3052
+ login_check: target.login_check,
3053
+ params: { type: "object", properties: {} },
3054
+ auth: target.auth,
3055
+ steps: [],
3056
+ output: { schema: {}, value: "`true`" },
3057
+ verify: { example_params: {}, checks: ["`true`"] }
3058
+ };
3059
+ const res = await new Run(recipe, adapters, { ...opts, timeoutMs: Math.min(opts.timeoutMs ?? 15e3, 15e3) }).execute({}, true);
3060
+ if (res.ok) return true;
3061
+ if (res.error.code === "login_required") return false;
3062
+ return null;
3063
+ }
3064
+ function pageOf(base) {
3065
+ try {
3066
+ const u = new URL(base);
3067
+ return { origin: u.origin, host: u.host };
3068
+ } catch {
3069
+ return { origin: "", host: "" };
3070
+ }
3071
+ }
3072
+ var Run = class {
3073
+ constructor(recipe, adapters, opts) {
3074
+ this.recipe = recipe;
3075
+ this.adapters = adapters;
3076
+ this.opts = opts;
3077
+ const l = { ...LIMITS };
3078
+ for (const [k, v] of Object.entries(opts.limits ?? {})) {
3079
+ const key = k;
3080
+ if (typeof v === "number" && v < l[key]) l[key] = v;
3081
+ }
3082
+ this.limits = l;
3083
+ this.allowed = /* @__PURE__ */ new Set();
3084
+ for (const o of recipe.site?.origins ?? []) {
3085
+ try {
3086
+ const u = new URL(o);
3087
+ if (u.protocol === "https:") this.allowed.add(u.origin);
3088
+ } catch {
3089
+ }
3090
+ }
3091
+ this.t0 = Date.now();
3092
+ this.remainingMs = Math.min(opts.timeoutMs ?? l.runMs, l.runMs);
3093
+ this.base = recipe.site?.origin ?? "";
3094
+ }
3095
+ limits;
3096
+ allowed;
3097
+ t0;
3098
+ ac = new AbortController();
3099
+ abortCode = null;
3100
+ timer = null;
3101
+ remainingMs;
3102
+ armedAt = 0;
3103
+ requests = 0;
3104
+ secrets = [];
3105
+ confirmed = false;
3106
+ currentStep;
3107
+ /** What relative request URLs resolve against: site.origin, or the listed language host the tab landed on. */
3108
+ base;
3109
+ /**
3110
+ * English-first browsers get www.zalando.de redirected to en.zalando.de; the recipe's relative requests must then go
3111
+ * to en.zalando.de (same-origin from the tab), not www (cross-origin, rejected). Only a host the recipe lists
3112
+ * (site.locale_hosts, or an origin in site.origins) under the site's own domain qualifies. Never a wildcard.
3113
+ */
3114
+ async resolveBase() {
3115
+ const site = this.recipe.site;
3116
+ if (!this.adapters.pageUrl) return;
3117
+ let page;
3118
+ try {
3119
+ page = new URL(await this.adapters.pageUrl());
3120
+ } catch {
3121
+ return;
3122
+ }
3123
+ if (page.protocol !== "https:" || page.origin === new URL(site.origin).origin) return;
3124
+ const listed = (site.locale_hosts ?? []).includes(page.hostname) || this.allowed.has(page.origin);
3125
+ const sameSite = page.hostname === site.id || page.hostname.endsWith(`.${site.id}`);
3126
+ if (!listed || !sameSite) return;
3127
+ this.base = page.origin;
3128
+ this.allowed.add(page.origin);
3129
+ this.emit("tab", "info", `${site.name} answered from ${page.host} (a listed language host); same-site requests go there`);
3130
+ }
3131
+ // -- lifecycle ------------------------------------------------------------
3132
+ async execute(rawParams, loginOnly = false) {
3133
+ const onCancel = () => this.abort("cancelled");
3134
+ if (this.opts.signal?.aborted) this.abort("cancelled");
3135
+ this.opts.signal?.addEventListener("abort", onCancel);
3136
+ this.arm();
3137
+ try {
3138
+ this.staticChecks();
3139
+ await this.resolveBase();
3140
+ const params = prepareParams(this.recipe.params, rawParams);
3141
+ const ctx = { params, vars: {}, steps: {}, now: this.opts.now ? this.opts.now() : /* @__PURE__ */ new Date(), page: pageOf(this.base) };
3142
+ await this.derive(ctx);
3143
+ if (this.recipe.login_check && (this.recipe.requires_login || loginOnly)) await this.loginCheck(ctx);
3144
+ await this.runSteps(ctx);
3145
+ this.currentStep = void 0;
3146
+ let output = this.q({ params: ctx.params, steps: ctx.steps, now: localIsoNow(ctx.now), page: ctx.page }, this.recipe.output.value, "output");
3147
+ output = redactDeep(normalizeValue(output), this.secrets);
3148
+ const duration_ms = Date.now() - this.t0;
3149
+ this.emit("done", "info", `ok in ${duration_ms} ms (${this.requests} requests)`);
3150
+ return { ok: true, output: output ?? null, duration_ms, requests: this.requests };
3151
+ } catch (e) {
3152
+ const err = this.toRecipeError(e);
3153
+ const duration_ms = Date.now() - this.t0;
3154
+ const message = redactString(err.message, this.secrets);
3155
+ this.emit("done", "error", `failed: ${err.code}${err.step ? ` at ${err.step}` : ""}`);
3156
+ return {
3157
+ ok: false,
3158
+ error: { code: err.code, message, ...err.step ? { step: err.step } : {} },
3159
+ duration_ms,
3160
+ requests: this.requests
3161
+ };
3162
+ } finally {
3163
+ this.disarm();
3164
+ this.opts.signal?.removeEventListener("abort", onCancel);
3165
+ }
3166
+ }
3167
+ toRecipeError(e) {
3168
+ if (this.abortCode) return new RecipeError(this.abortCode, this.abortCode === "timeout" ? `run exceeded ${this.limits.runMs / 1e3} s` : "run cancelled", this.currentStep);
3169
+ if (e instanceof RecipeError) {
3170
+ if (!e.step && this.currentStep) e.step = this.currentStep;
3171
+ return e;
3172
+ }
3173
+ if (e instanceof TemplateError) return new RecipeError("recipe_invalid", `template: ${e.message}`, this.currentStep);
3174
+ const msg = e instanceof Error ? e.message : String(e);
3175
+ return new RecipeError("expect_failed", msg, this.currentStep);
3176
+ }
3177
+ // -- time limit (paused while waiting for the user's Allow) ---------------
3178
+ arm() {
3179
+ this.armedAt = Date.now();
3180
+ this.timer = setTimeout(() => this.abort("timeout"), Math.max(0, this.remainingMs));
3181
+ }
3182
+ disarm() {
3183
+ if (this.timer) {
3184
+ clearTimeout(this.timer);
3185
+ this.timer = null;
3186
+ this.remainingMs -= Date.now() - this.armedAt;
3187
+ }
3188
+ }
3189
+ abort(code) {
3190
+ if (this.abortCode) return;
3191
+ this.abortCode = code;
3192
+ this.ac.abort();
3193
+ }
3194
+ checkAbort() {
3195
+ if (this.abortCode) throw new RecipeError(this.abortCode, this.abortCode, this.currentStep);
3196
+ }
3197
+ /** Race a promise against timeout/cancel. */
3198
+ guard(p) {
3199
+ if (this.abortCode) return Promise.reject(new RecipeError(this.abortCode, this.abortCode));
3200
+ return new Promise((resolve, reject) => {
3201
+ const onAbort = () => reject(new RecipeError(this.abortCode ?? "cancelled", this.abortCode ?? "cancelled"));
3202
+ this.ac.signal.addEventListener("abort", onAbort, { once: true });
3203
+ Promise.resolve(p).then(
3204
+ (v) => {
3205
+ this.ac.signal.removeEventListener("abort", onAbort);
3206
+ resolve(v);
3207
+ },
3208
+ (err) => {
3209
+ this.ac.signal.removeEventListener("abort", onAbort);
3210
+ reject(err);
3211
+ }
3212
+ );
3213
+ });
3214
+ }
3215
+ sleep(ms) {
3216
+ return this.guard(new Promise((r) => setTimeout(r, ms)));
3217
+ }
3218
+ // -- static rules -----------------------------------------------------------
3219
+ staticChecks() {
3220
+ const r = this.recipe;
3221
+ if (!r || typeof r !== "object" || !Array.isArray(r.steps) || !r.site || !r.output) {
3222
+ throw new RecipeError("recipe_invalid", "not a recipe");
3223
+ }
3224
+ if (r.steps.length > this.limits.steps) {
3225
+ throw new RecipeError("limit_exceeded", `recipe has ${r.steps.length} steps (max ${this.limits.steps})`);
3226
+ }
3227
+ if (this.allowed.size === 0) throw new RecipeError("recipe_invalid", "site.origins has no valid https origin");
3228
+ for (const h of r.site.locale_hosts ?? []) {
3229
+ if (typeof h !== "string" || !/^[a-z0-9-]+(\.[a-z0-9-]+)+$/.test(h) || !(h === r.site.id || h.endsWith(`.${r.site.id}`))) {
3230
+ throw new RecipeError("recipe_invalid", `site.locale_hosts: "${String(h)}" must be an exact host name under ${r.site.id} (no wildcards)`);
3231
+ }
3232
+ }
3233
+ const all = r.login_check ? [r.login_check, ...r.steps] : r.steps;
3234
+ const ids = /* @__PURE__ */ new Set();
3235
+ for (const s2 of all) {
3236
+ if (s2 !== r.login_check) {
3237
+ if (ids.has(s2.id)) throw new RecipeError("recipe_invalid", `duplicate step id "${s2.id}"`, s2.id);
3238
+ ids.add(s2.id);
3239
+ }
3240
+ for (const h of Object.keys(s2.request?.headers ?? {})) {
3241
+ if (FORBIDDEN_HEADERS.has(h.toLowerCase())) {
3242
+ throw new RecipeError("recipe_invalid", `header "${h}" is not allowed in recipes`, s2.id);
3243
+ }
3244
+ }
3245
+ if (s2.write && r.effect !== "write") {
3246
+ throw new RecipeError("recipe_invalid", `write step "${s2.id}" in a recipe with effect "read"`, s2.id);
3247
+ }
3248
+ if (s2 === r.login_check && s2.write) throw new RecipeError("recipe_invalid", "login_check cannot write", s2.id);
3249
+ if (s2 === r.login_check && this.opts.trust !== "registry" && s2.request?.method !== "GET") {
3250
+ throw new RecipeError("recipe_invalid", "an unsigned recipe's login_check must be a GET request", s2.id);
3251
+ }
3252
+ }
3253
+ }
3254
+ /** A step that changes data: marked write, or (unless registry-signed) any non-GET request. */
3255
+ isWrite(s2) {
3256
+ return s2.write === true || this.opts.trust !== "registry" && s2.request?.method !== "GET";
3257
+ }
3258
+ // -- auth.derive ------------------------------------------------------------
3259
+ async derive(ctx) {
3260
+ const list = this.recipe.auth?.derive ?? [];
3261
+ for (const d of list) {
3262
+ let raw = await this.guard(this.readDerive(d));
3263
+ if (d.regex) raw = await this.guard(this.applyDeriveRegex(d, raw));
3264
+ let value = raw ?? null;
3265
+ if (value !== null && d.jmespath) {
3266
+ try {
3267
+ value = jmes(JSON.parse(String(raw)), d.jmespath);
3268
+ } catch {
3269
+ value = null;
3270
+ }
3271
+ }
3272
+ this.secrets.push(...secretStrings(value));
3273
+ this.secrets.sort((a, b) => b.length - a.length);
3274
+ if ((value === null || value === "") && d.required !== false) {
3275
+ throw new RecipeError(
3276
+ this.recipe.requires_login ? "login_required" : "expect_failed",
3277
+ `could not derive "${d.name}" from ${d.from}${this.recipe.requires_login ? " (are you logged in?)" : ""}`
3278
+ );
3279
+ }
3280
+ ctx.vars[d.name] = value;
3281
+ }
3282
+ }
3283
+ compileRegex(src, stepId) {
3284
+ try {
3285
+ return new RegExp(src);
3286
+ } catch {
3287
+ throw new RecipeError("recipe_invalid", `invalid regex in recipe`, stepId);
3288
+ }
3289
+ }
3290
+ /** First capture group (or the whole match). For from=dom, the first element whose text matches wins. */
3291
+ async applyDeriveRegex(d, raw) {
3292
+ const re = this.compileRegex(d.regex);
3293
+ const MAX = 2 * 1024 * 1024;
3294
+ const pick = (s2) => {
3295
+ if (s2 === null || s2 === void 0) return null;
3296
+ const m = re.exec(s2.length > MAX ? s2.slice(0, MAX) : s2);
3297
+ return m ? m[1] ?? m[0] : null;
3298
+ };
3299
+ if (d.from === "dom" && d.selector && this.adapters.queryDomAll) {
3300
+ const all = await this.adapters.queryDomAll(d.selector, d.attr);
3301
+ for (const s2 of all ?? []) {
3302
+ const v = pick(s2);
3303
+ if (v !== null) return v;
3304
+ }
3305
+ return null;
3306
+ }
3307
+ return pick(raw);
3308
+ }
3309
+ async readDerive(d) {
3310
+ const a = this.adapters;
3311
+ switch (d.from) {
3312
+ case "cookie":
3313
+ return d.key && a.getCookie ? a.getCookie(d.key) : null;
3314
+ case "local_storage":
3315
+ return d.key && a.getStorage ? a.getStorage("local", d.key) : null;
3316
+ case "session_storage":
3317
+ return d.key && a.getStorage ? a.getStorage("session", d.key) : null;
3318
+ case "meta":
3319
+ return d.selector && a.queryMeta ? a.queryMeta(d.selector, d.attr ?? "content") : null;
3320
+ case "dom":
3321
+ return d.selector && a.queryDom ? a.queryDom(d.selector, d.attr) : null;
3322
+ case "page_url": {
3323
+ if (!a.pageUrl) return null;
3324
+ const u = await a.pageUrl();
3325
+ if (!d.key) return u;
3326
+ try {
3327
+ return new URL(u).searchParams.get(d.key);
3328
+ } catch {
3329
+ return null;
3330
+ }
3331
+ }
3332
+ default:
3333
+ return null;
3334
+ }
3335
+ }
3336
+ // -- login check ----------------------------------------------------------
3337
+ async loginCheck(ctx) {
3338
+ const step = this.recipe.login_check;
3339
+ this.currentStep = step.id;
3340
+ try {
3341
+ await this.execRequest(step, ctx);
3342
+ } catch (e) {
3343
+ if (e instanceof RecipeError && (e.code === "expect_failed" || e.code === "login_required")) {
3344
+ throw new RecipeError("login_required", `not logged in to ${this.recipe.site.name}`, step.id);
3345
+ }
3346
+ throw e;
3347
+ }
3348
+ this.emit("step", "info", `login check ok`);
3349
+ }
3350
+ // -- steps ------------------------------------------------------------------
3351
+ async runSteps(ctx) {
3352
+ const steps = this.recipe.steps;
3353
+ if (this.opts.parallel === false) {
3354
+ for (const step of steps) {
3355
+ this.checkAbort();
3356
+ await this.runStep(step, ctx);
3357
+ }
3358
+ return;
3359
+ }
3360
+ const deps = stepDependencies(steps);
3361
+ let first = null;
3362
+ const fail = (e, stepId) => {
3363
+ if (first) return;
3364
+ if (e instanceof RecipeError && !e.step) e.step = stepId;
3365
+ first = e instanceof RecipeError || this.abortCode ? e : Object.assign(this.toRecipeError(e), { step: stepId });
3366
+ if (!this.abortCode) this.ac.abort();
3367
+ };
3368
+ const done = [];
3369
+ steps.forEach((step, i) => {
3370
+ done[i] = Promise.all(deps[i].map((j) => done[j])).then(async () => {
3371
+ if (first) throw first;
3372
+ this.checkAbort();
3373
+ try {
3374
+ await this.runStep(step, ctx);
3375
+ } catch (e) {
3376
+ fail(e, step.id);
3377
+ throw e;
3378
+ }
3379
+ });
3380
+ done[i].catch(() => {
3381
+ });
3382
+ });
3383
+ await Promise.allSettled(done);
3384
+ if (first) throw first;
3385
+ }
3386
+ async runStep(step, ctx) {
3387
+ this.currentStep = step.id;
3388
+ if (step.when !== void 0 && !truthy(this.q(ctx, step.when, step.id))) {
3389
+ ctx.steps[step.id] = null;
3390
+ this.emit("step", "info", `step ${step.id} skipped`);
3391
+ return;
3392
+ }
3393
+ if (this.isWrite(step) && !this.confirmed) await this.confirmWrites(step, ctx);
3394
+ if (step.foreach) {
3395
+ const fe = step.foreach;
3396
+ let list = this.q(ctx, fe.in, step.id);
3397
+ if (list === null) list = [];
3398
+ if (!Array.isArray(list)) throw new RecipeError("expect_failed", `foreach.in did not produce an array`, step.id);
3399
+ const max = Math.min(fe.max ?? 10, this.limits.foreach);
3400
+ if (list.length > max) this.emit("step", "warn", `step ${step.id}: foreach capped at ${max} of ${list.length} items`);
3401
+ const results = [];
3402
+ for (const item of list.slice(0, max)) {
3403
+ this.checkAbort();
3404
+ const local = { ...ctx, item, [fe.as]: item };
3405
+ const extracted = await this.execRequest(step, local);
3406
+ results.push({ [fe.as]: item, ...extracted });
3407
+ }
3408
+ ctx.steps[step.id] = results;
3409
+ } else {
3410
+ ctx.steps[step.id] = await this.execRequest(step, ctx);
3411
+ }
3412
+ this.emit("step", "info", `step ${step.id} ok`);
3413
+ }
3414
+ async confirmWrites(first, ctx) {
3415
+ const steps = this.recipe.steps;
3416
+ const writes = [];
3417
+ for (const s2 of steps.slice(steps.indexOf(first))) {
3418
+ if (!this.isWrite(s2)) continue;
3419
+ writes.push(this.describeWrite(s2, ctx));
3420
+ }
3421
+ let summary = this.recipe.task;
3422
+ if (this.recipe.confirm?.summary) {
3423
+ try {
3424
+ summary = toText(renderString(this.recipe.confirm.summary, { params: ctx.params, steps: ctx.steps, now: ctx.now, page: ctx.page }));
3425
+ } catch {
3426
+ summary = this.recipe.confirm.summary;
3427
+ }
3428
+ }
3429
+ summary = redactString(summary, this.secrets);
3430
+ const req = {
3431
+ recipe_id: this.recipe.id,
3432
+ site: { id: this.recipe.site.id, name: this.recipe.site.name, origin: this.recipe.site.origin },
3433
+ task: this.recipe.task,
3434
+ summary,
3435
+ writes
3436
+ };
3437
+ this.emit("confirm", "info", `waiting for Allow: ${writes.length} write request(s)`);
3438
+ let ok = false;
3439
+ this.disarm();
3440
+ try {
3441
+ if (this.adapters.confirm) {
3442
+ ok = await this.guard(
3443
+ Promise.race([
3444
+ this.adapters.confirm(req),
3445
+ new Promise((r) => setTimeout(() => r(false), this.limits.confirmMs))
3446
+ ])
3447
+ );
3448
+ }
3449
+ } finally {
3450
+ if (!this.abortCode) this.arm();
3451
+ }
3452
+ this.checkAbort();
3453
+ this.emit("confirm", ok ? "info" : "warn", ok ? "allowed by user" : "denied");
3454
+ if (ok !== true) throw new RecipeError("confirmation_denied", "the user did not allow this write", first.id);
3455
+ this.confirmed = true;
3456
+ }
3457
+ describeWrite(s2, ctx) {
3458
+ try {
3459
+ let local = ctx;
3460
+ if (s2.foreach) {
3461
+ const list = this.q(ctx, s2.foreach.in, s2.id);
3462
+ const item = Array.isArray(list) ? list[0] : void 0;
3463
+ local = { ...ctx, item, [s2.foreach.as]: item };
3464
+ }
3465
+ const b = this.build(s2.request, local, s2.id);
3466
+ const preview = b.body !== void 0 ? redactString(b.body, this.secrets) : void 0;
3467
+ return {
3468
+ step: s2.id,
3469
+ method: b.method,
3470
+ origin: b.url.origin,
3471
+ path: redactString(b.url.pathname, this.secrets),
3472
+ body_fields: b.bodyFields,
3473
+ ...preview !== void 0 ? { body_preview: preview.length > 4e3 ? `${preview.slice(0, 4e3)}\u2026` : preview } : {}
3474
+ };
3475
+ } catch {
3476
+ const raw = s2.request.url.split("?")[0] ?? s2.request.url;
3477
+ const v = s2.request.body?.value;
3478
+ return {
3479
+ step: s2.id,
3480
+ method: s2.request.method,
3481
+ origin: this.recipe.site.origin,
3482
+ path: raw,
3483
+ body_fields: v && typeof v === "object" && !Array.isArray(v) ? Object.keys(v) : []
3484
+ };
3485
+ }
3486
+ }
3487
+ // -- one request ------------------------------------------------------------
3488
+ build(r, ctx, stepId) {
3489
+ const urlText = toText(renderString(r.url, ctx));
3490
+ let url;
3491
+ try {
3492
+ url = new URL(urlText, this.base);
3493
+ } catch {
3494
+ throw new RecipeError("recipe_invalid", `invalid request URL`, stepId);
3495
+ }
3496
+ for (const [k, v] of Object.entries(r.query ?? {})) {
3497
+ const rv = renderValue(v, ctx);
3498
+ if (rv === void 0 || rv === null) continue;
3499
+ if (Array.isArray(rv)) {
3500
+ for (const x of rv) if (x !== void 0 && x !== null) url.searchParams.append(k, toText(x));
3501
+ } else url.searchParams.append(k, toText(rv));
3502
+ }
3503
+ const headers = {};
3504
+ for (const [k, v] of Object.entries(r.headers ?? {})) {
3505
+ if (FORBIDDEN_HEADERS.has(k.toLowerCase())) throw new RecipeError("recipe_invalid", `header "${k}" is not allowed in recipes`, stepId);
3506
+ const rv = renderString(v, ctx);
3507
+ if (rv === void 0 || rv === null) continue;
3508
+ const text = toText(rv);
3509
+ if (/[\r\n]/.test(text)) throw new RecipeError("recipe_invalid", `header "${k}" contains a line break`, stepId);
3510
+ headers[k] = text;
3511
+ }
3512
+ const hasHeader = (name) => Object.keys(headers).some((h) => h.toLowerCase() === name);
3513
+ let body;
3514
+ let bodyFields = [];
3515
+ if (r.body) {
3516
+ if (r.method === "GET") throw new RecipeError("recipe_invalid", "GET requests cannot have a body", stepId);
3517
+ const value = normalizeValue(renderValue(r.body.value, ctx));
3518
+ const tv = r.body.value;
3519
+ if (tv && typeof tv === "object" && !Array.isArray(tv)) bodyFields = Object.keys(tv);
3520
+ if (r.body.type === "json") {
3521
+ body = JSON.stringify(value ?? null);
3522
+ if (!hasHeader("content-type")) headers["Content-Type"] = "application/json";
3523
+ } else if (r.body.type === "form") {
3524
+ const form = new URLSearchParams();
3525
+ if (value && typeof value === "object" && !Array.isArray(value)) {
3526
+ for (const [k, v] of Object.entries(value)) {
3527
+ if (v === void 0 || v === null) continue;
3528
+ if (Array.isArray(v)) v.forEach((x) => x !== null && x !== void 0 && form.append(k, toText(x)));
3529
+ else form.append(k, toText(v));
3530
+ }
3531
+ } else if (typeof value === "string") {
3532
+ body = value;
3533
+ }
3534
+ body ??= form.toString();
3535
+ if (!hasHeader("content-type")) headers["Content-Type"] = "application/x-www-form-urlencoded;charset=UTF-8";
3536
+ } else {
3537
+ body = toText(value);
3538
+ if (!hasHeader("content-type")) headers["Content-Type"] = "text/plain;charset=UTF-8";
3539
+ }
3540
+ }
3541
+ return { url, method: r.method, headers, body, bodyFields, credentials: r.credentials ?? "include" };
3542
+ }
3543
+ checkOrigin(url, stepId, what = "request") {
3544
+ if (url.protocol !== "https:" || !this.allowed.has(url.origin) || url.username || url.password) {
3545
+ throw new RecipeError("origin_blocked", `${what} to ${url.protocol}//${url.host} is not in site.origins`, stepId);
3546
+ }
3547
+ }
3548
+ async send(b, stepId) {
3549
+ let cur = b;
3550
+ for (let hop = 0; ; hop++) {
3551
+ this.checkOrigin(cur.url, stepId, hop ? "redirect" : "request");
3552
+ this.checkAbort();
3553
+ if (++this.requests > this.limits.requests) {
3554
+ throw new RecipeError("limit_exceeded", `more than ${this.limits.requests} requests in one run`, stepId);
3555
+ }
3556
+ const started = Date.now();
3557
+ let resp;
3558
+ try {
3559
+ resp = await this.guard(
3560
+ this.adapters.fetch({
3561
+ url: cur.url.toString(),
3562
+ method: cur.method,
3563
+ headers: cur.headers,
3564
+ body: cur.body,
3565
+ credentials: cur.credentials,
3566
+ redirect: "manual",
3567
+ signal: this.ac.signal,
3568
+ maxBytes: this.limits.responseBytes
3569
+ })
3570
+ );
3571
+ } catch (e) {
3572
+ if (this.abortCode || e instanceof RecipeError) throw e;
3573
+ this.emitRequest(cur, 0, Date.now() - started, 0, "error");
3574
+ throw new RecipeError("expect_failed", `${cur.method} ${cur.url.host}${cur.url.pathname} failed (network or CORS error)`, stepId);
3575
+ }
3576
+ const ms = Date.now() - started;
3577
+ const bytes = typeof resp.bytes === "number" ? resp.bytes : utf8Length(resp.text ?? "");
3578
+ this.emitRequest(cur, resp.status, ms, bytes);
3579
+ if (bytes > this.limits.responseBytes) {
3580
+ throw new RecipeError("limit_exceeded", `response larger than ${Math.round(this.limits.responseBytes / 1024 / 1024)} MB`, stepId);
3581
+ }
3582
+ const location2 = header(resp, "location");
3583
+ const isRedirect = resp.status >= 300 && resp.status < 400 && resp.status !== 304;
3584
+ if (resp.type === "opaqueredirect" || !isRedirect || !location2) return resp;
3585
+ let target;
3586
+ try {
3587
+ target = new URL(location2, cur.url);
3588
+ } catch {
3589
+ return resp;
3590
+ }
3591
+ if (hop >= this.limits.redirects) throw new RecipeError("expect_failed", "too many redirects", stepId);
3592
+ this.checkOrigin(target, stepId, "redirect");
3593
+ const keep = resp.status === 307 || resp.status === 308;
3594
+ cur = keep ? { ...cur, url: target } : { ...cur, url: target, method: "GET", body: void 0 };
3595
+ }
3596
+ }
3597
+ async execRequest(step, ctx) {
3598
+ if (step.delay_ms) await this.sleep(Math.min(step.delay_ms, 3e3));
3599
+ const built = this.build(step.request, ctx, step.id);
3600
+ const resp = await this.send(built, step.id);
3601
+ const exp = step.expect ?? {};
3602
+ const ex = step.extract ?? {};
3603
+ const ctHeader = (header(resp, "content-type") ?? "").toLowerCase();
3604
+ const parse = step.parse && step.parse !== "auto" ? step.parse : void 0;
3605
+ const ct = parse ?? exp.content_type ?? (ctHeader.includes("json") ? "json" : ctHeader.includes("html") ? "html" : ex.json ? "json" : ex.html || ex.html_items || ex.embedded_json ? "html" : "text");
3606
+ const text = resp.text ?? "";
3607
+ let body = text;
3608
+ let notJson = false;
3609
+ if (ct === "json") {
3610
+ try {
3611
+ body = text.trim() === "" ? null : JSON.parse(text);
3612
+ } catch {
3613
+ notJson = true;
3614
+ }
3615
+ } else if (ct === "turbo_stream") {
3616
+ try {
3617
+ body = decodeTurboStream(text);
3618
+ } catch {
3619
+ notJson = true;
3620
+ }
3621
+ }
3622
+ const opaque = resp.type === "opaqueredirect";
3623
+ const status = resp.status;
3624
+ const lr = exp.login_required_if;
3625
+ if (lr) {
3626
+ const statusHit = lr.status?.includes(status) || opaque && lr.status?.some((s2) => s2 >= 300 && s2 < 400);
3627
+ if (statusHit) throw new RecipeError("login_required", `${this.recipe.site.name} asked for a login (HTTP ${opaque ? "redirect" : status})`, step.id);
3628
+ if (lr.assert && this.safeTruthy({ body, status }, lr.assert, step.id)) {
3629
+ throw new RecipeError("login_required", `${this.recipe.site.name} asked for a login`, step.id);
3630
+ }
3631
+ }
3632
+ if (opaque) {
3633
+ throw new RecipeError("expect_failed", `the site redirected (target hidden by the browser; not followed)`, step.id);
3634
+ }
3635
+ const okStatus = exp.status ?? [200];
3636
+ const badStatus = !okStatus.includes(status);
3637
+ if (badStatus || notJson) {
3638
+ const block = detectBlock(status, text, (n) => header(resp, n), notJson);
3639
+ if (block) throw new RecipeError("site_challenge", blockMessage(this.recipe.site, status, block), step.id);
3640
+ }
3641
+ if (badStatus) {
3642
+ throw new RecipeError("expect_failed", `${this.recipe.site.name} answered HTTP ${status} (expected ${okStatus.join("/")}). The executor is online; the site's API may have changed.`, step.id);
3643
+ }
3644
+ if (notJson) throw new RecipeError("expect_failed", `${this.recipe.site.name} did not return ${ct === "turbo_stream" ? "a turbo-stream" : "JSON"}. The executor is online; the site's API may have changed.`, step.id);
3645
+ const blockedPage = (e) => {
3646
+ if (e instanceof RecipeError && e.code === "expect_failed" && ct !== "json" && ct !== "turbo_stream" && text.length < 6e4) {
3647
+ const block = detectBlock(status, text, (n) => header(resp, n), true);
3648
+ if (block) throw new RecipeError("site_challenge", blockMessage(this.recipe.site, status, block), step.id);
3649
+ }
3650
+ throw e;
3651
+ };
3652
+ if (exp.assert && !truthy(this.q({ body, status }, exp.assert, step.id))) {
3653
+ blockedPage(new RecipeError("expect_failed", `assert failed: ${exp.assert}`, step.id));
3654
+ }
3655
+ const out = {};
3656
+ if (ex.json) {
3657
+ let data = body;
3658
+ if (ct !== "json" && ct !== "turbo_stream") {
3659
+ try {
3660
+ data = JSON.parse(text);
3661
+ } catch {
3662
+ const block = detectBlock(status, text, (n) => header(resp, n), true);
3663
+ if (block) throw new RecipeError("site_challenge", blockMessage(this.recipe.site, status, block), step.id);
3664
+ throw new RecipeError("expect_failed", `${this.recipe.site.name} did not return JSON. The executor is online; the site's API may have changed.`, step.id);
3665
+ }
3666
+ }
3667
+ for (const [name, expr] of Object.entries(ex.json)) out[name] = this.q(data, expr, step.id, true);
3668
+ }
3669
+ if (ex.html || ex.html_items || ex.embedded_json) {
3670
+ try {
3671
+ const doc = this.parseHtml(text, step.id);
3672
+ for (const [name, spec] of Object.entries(ex.html ?? {})) out[name] = extractHtml(doc, spec);
3673
+ for (const [name, spec] of Object.entries(ex.html_items ?? {})) out[name] = extractHtmlItems(doc, spec);
3674
+ if (ex.embedded_json) Object.assign(out, extractEmbeddedJson(doc, ex.embedded_json, step.id));
3675
+ } catch (e) {
3676
+ blockedPage(e);
3677
+ }
3678
+ }
3679
+ return out;
3680
+ }
3681
+ parseHtml(text, stepId) {
3682
+ if (!this.adapters.parseHtml) throw new RecipeError("tab_error", "no HTML parser available", stepId);
3683
+ return this.adapters.parseHtml(text);
3684
+ }
3685
+ // -- JMESPath helpers -------------------------------------------------------
3686
+ /** Syntax errors are recipe bugs (recipe_invalid); runtime type errors mean the data changed (expect_failed). */
3687
+ q(data, expr, stepId, isJson = false) {
3688
+ try {
3689
+ compileJmes(expr);
3690
+ } catch (e) {
3691
+ throw new RecipeError("recipe_invalid", `JMESPath syntax: ${expr} (${e.message})`, stepId);
3692
+ }
3693
+ try {
3694
+ return isJson ? jmes(data, expr) : jmesCtx(data, expr);
3695
+ } catch (e) {
3696
+ throw new RecipeError("expect_failed", `JMESPath ${expr}: ${e.message}`, stepId);
3697
+ }
3698
+ }
3699
+ safeTruthy(data, expr, stepId) {
3700
+ try {
3701
+ return truthy(this.q(data, expr, stepId));
3702
+ } catch (e) {
3703
+ if (e instanceof RecipeError && e.code === "recipe_invalid") throw e;
3704
+ return false;
3705
+ }
3706
+ }
3707
+ // -- events -----------------------------------------------------------------
3708
+ emit(kind, level, msg, data) {
3709
+ if (!this.adapters.emit) return;
3710
+ const ev = {
3711
+ type: "event",
3712
+ run_id: this.opts.runId ?? "local",
3713
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
3714
+ level,
3715
+ kind,
3716
+ msg: redactString(msg, this.secrets)
3717
+ };
3718
+ if (data) ev.data = data;
3719
+ try {
3720
+ this.adapters.emit(ev);
3721
+ } catch {
3722
+ }
3723
+ }
3724
+ emitRequest(b, status, ms, bytes, level = "info") {
3725
+ const path = redactString(b.url.pathname, this.secrets);
3726
+ const kb = bytes >= 1024 ? `${Math.round(bytes / 1024)} KB` : `${bytes} B`;
3727
+ const lvl = level === "error" ? "error" : status >= 400 || status === 0 ? "warn" : "info";
3728
+ this.emit("request", lvl, `${b.method} ${b.url.host}${path} \u2192 ${status || (level === "error" ? "network error" : "redirect")} (${ms} ms, ${kb})`, {
3729
+ method: b.method,
3730
+ origin: b.url.origin,
3731
+ path,
3732
+ status,
3733
+ ms,
3734
+ bytes
3735
+ });
3736
+ }
3737
+ };
3738
+ function localIsoNow(d) {
3739
+ const off = -d.getTimezoneOffset();
3740
+ const sign = off >= 0 ? "+" : "-";
3741
+ const a = Math.abs(off);
3742
+ return `${localIso(d)}${sign}${String(Math.floor(a / 60)).padStart(2, "0")}:${String(a % 60).padStart(2, "0")}`;
3743
+ }
3744
+ function header(resp, name) {
3745
+ const h = resp.headers;
3746
+ if (!h) return null;
3747
+ if (typeof h.get === "function") return h.get(name);
3748
+ const rec = h;
3749
+ for (const k of Object.keys(rec)) if (k.toLowerCase() === name) return rec[k] ?? null;
3750
+ return null;
3751
+ }
3752
+ function utf8Length(s2) {
3753
+ if (typeof TextEncoder !== "undefined") return new TextEncoder().encode(s2).length;
3754
+ return s2.length;
3755
+ }
3756
+
3757
+ // packages/recipe/src/lint.ts
3758
+ var OPERAND = String.raw`(?<![\w$.@])(§S|params\s*\.\s*[A-Za-z_]\w*|\$[A-Za-z_]\w*)`;
3759
+ var COMPARISON = new RegExp(String.raw`${OPERAND}\s*(<=|>=|<|>)|(<=|>=|<|>)\s*${OPERAND}`, "g");
3760
+
3761
+ // packages/recipe/src/signature.ts
3762
+ var enc = new TextEncoder();
3763
+
3764
+ // apps/extension/src/shared/types.ts
3765
+ var BUILD_ID = "0.1.15+20261107220223.8c74";
3766
+ var RELOAD_HINT = "Reload the Wield extension once: open chrome://extensions (vivaldi://extensions in Vivaldi) and click the reload arrow on the Wield card, then try again.";
3767
+ var LIMITS2 = {
3768
+ steps: 12,
3769
+ foreach: 20,
3770
+ requests: 40,
3771
+ responseBytes: 5 * 1024 * 1024,
3772
+ runMs: 6e4,
3773
+ confirmMs: 12e4,
3774
+ /** How long the "quick human check" note waits for a click in the site tab. */
3775
+ humanCheckMs: 6e4,
3776
+ tabLoadMs: 3e4,
3777
+ /** Settle time after a tab we opened reaches `complete`, so anti-bot sensor scripts can post (tests/e2e/SPIKE.md). */
3778
+ tabSettleMs: 1500
3779
+ };
3780
+
3781
+ // apps/extension/src/content/index.ts
3782
+ async function readCapped(res, maxBytes) {
3783
+ if (!res.body) return { text: "", bytes: 0 };
3784
+ const reader = res.body.getReader();
3785
+ const chunks = [];
3786
+ let bytes = 0;
3787
+ for (; ; ) {
3788
+ const { done, value } = await reader.read();
3789
+ if (done) break;
3790
+ bytes += value.byteLength;
3791
+ if (bytes > maxBytes) {
3792
+ await reader.cancel().catch(() => {
3793
+ });
3794
+ return { text: "", bytes };
3795
+ }
3796
+ chunks.push(value);
3797
+ }
3798
+ const all = new Uint8Array(bytes);
3799
+ let off = 0;
3800
+ for (const c of chunks) {
3801
+ all.set(c, off);
3802
+ off += c.byteLength;
3803
+ }
3804
+ const charset = /charset=([^;]+)/i.exec(res.headers.get("content-type") ?? "")?.[1]?.trim() ?? "utf-8";
3805
+ let decoder;
3806
+ try {
3807
+ decoder = new TextDecoder(charset);
3808
+ } catch {
3809
+ decoder = new TextDecoder("utf-8");
3810
+ }
3811
+ return { text: decoder.decode(all), bytes };
3812
+ }
3813
+ function makeFetch(hooks) {
3814
+ return async (req) => {
3815
+ const url = new URL(req.url);
3816
+ await hooks.pace(url.origin);
3817
+ const res = await fetch(req.url, {
3818
+ method: req.method,
3819
+ headers: req.headers,
3820
+ body: req.body,
3821
+ credentials: req.credentials,
3822
+ redirect: "manual",
3823
+ signal: req.signal
3824
+ // No `cache` option on purpose: cache: 'no-store' makes Chrome add Pragma and
3825
+ // Cache-Control headers, and the request would no longer match the site's own XHR.
3826
+ });
3827
+ const headers = {};
3828
+ res.headers.forEach((v, k) => headers[k] = v);
3829
+ if (res.type === "opaqueredirect") return { status: 0, type: res.type, headers, text: "", bytes: 0 };
3830
+ const { text, bytes } = await readCapped(res, req.maxBytes);
3831
+ const challenge = detectChallenge(res.status, headers, text, url.host);
3832
+ if (challenge) hooks.onChallenge(challenge);
3833
+ return { status: res.status, type: res.type, headers, text, bytes };
3834
+ };
3835
+ }
3836
+ function detectChallenge(status, headers, text, host) {
3837
+ const h = (k) => headers[k] ?? "";
3838
+ const head = text.slice(0, 4e3);
3839
+ let vendor = null;
3840
+ if (h("cf-mitigated") === "challenge" || (status === 403 || status === 503) && /Just a moment|cf-chl|challenge-platform/i.test(head)) vendor = "Cloudflare";
3841
+ else if (h("x-amzn-waf-action") || (status === 401 || status === 403 || status === 405) && /Ich bin kein Roboter|awswaf|aws-waf-token/i.test(head)) vendor = "AWS WAF";
3842
+ else if (h("x-datadome") || /captcha-delivery\.com/i.test(head)) vendor = status >= 400 ? "DataDome" : null;
3843
+ else if (status === 403 && (/OPS_BLOCKED/.test(head) || /AkamaiGHost/i.test(h("server")) || h("akamai-grn"))) vendor = "Akamai";
3844
+ else if (status === 429) vendor = "unknown";
3845
+ if (!vendor) return null;
3846
+ const info = { vendor, status, host };
3847
+ if (vendor === "Akamai") {
3848
+ const abck = readCookie("_abck");
3849
+ info.sensor = abck == null ? "absent" : /~0~/.test(abck) ? "validated" : "not validated";
3850
+ }
3851
+ return info;
3852
+ }
3853
+ function readCookie(name) {
3854
+ for (const part of document.cookie.split(";")) {
3855
+ const i = part.indexOf("=");
3856
+ if (i < 0) continue;
3857
+ if (part.slice(0, i).trim() === name) {
3858
+ const v = part.slice(i + 1).trim();
3859
+ try {
3860
+ return decodeURIComponent(v);
3861
+ } catch {
3862
+ return v;
3863
+ }
3864
+ }
3865
+ }
3866
+ return null;
3867
+ }
3868
+ function readNode2(el, attr) {
3869
+ if (!el) return null;
3870
+ return attr ? el.getAttribute(attr) : (el.textContent ?? "").trim();
3871
+ }
3872
+ function onRunPort(port) {
3873
+ const ac = new AbortController();
3874
+ const confirms = /* @__PURE__ */ new Map();
3875
+ const paces = /* @__PURE__ */ new Map();
3876
+ let challenge = null;
3877
+ const post = (m) => {
3878
+ try {
3879
+ port.postMessage(m);
3880
+ } catch {
3881
+ }
3882
+ };
3883
+ port.onDisconnect.addListener(() => {
3884
+ ac.abort();
3885
+ for (const done of confirms.values()) done(false);
3886
+ for (const done of paces.values()) done({ ok: false, message: "run ended" });
3887
+ });
3888
+ const hooks = {
3889
+ pace: (origin) => new Promise((resolve, reject) => {
3890
+ const id = crypto.randomUUID();
3891
+ const backstop = setTimeout(() => paces.get(id)?.({ ok: false, message: `The Wield service worker did not answer. ${RELOAD_HINT}` }), 3e4);
3892
+ paces.set(id, (r) => {
3893
+ clearTimeout(backstop);
3894
+ paces.delete(id);
3895
+ if (r.ok) resolve();
3896
+ else if (r.message?.startsWith("The Wield service worker")) reject(new RecipeError("tab_error", r.message));
3897
+ else reject(new RecipeError("limit_exceeded", r.message ?? "paused by pacing"));
3898
+ });
3899
+ post({ type: "pace", id, origin });
3900
+ }),
3901
+ onChallenge: (c) => {
3902
+ if (challenge) return;
3903
+ challenge = c;
3904
+ const sensor = c.sensor ? `; Akamai sensor cookie: ${c.sensor}` : "";
3905
+ post({ type: "event", level: "warn", kind: "step", msg: `bot check from ${c.vendor} on ${c.host} (HTTP ${c.status})${sensor}` });
3906
+ }
3907
+ };
3908
+ const adapters = {
3909
+ fetch: makeFetch(hooks),
3910
+ getCookie: (name) => readCookie(name),
3911
+ getStorage: (area, key) => (area === "local" ? localStorage : sessionStorage).getItem(key),
3912
+ queryMeta: (selector, attr) => readNode2(document.querySelector(selector), attr || "content"),
3913
+ queryDom: (selector, attr) => readNode2(document.querySelector(selector), attr),
3914
+ queryDomAll: (selector, attr) => Array.from(document.querySelectorAll(selector), (el) => attr ? el.getAttribute(attr) : el.textContent ?? ""),
3915
+ pageUrl: () => location.href,
3916
+ parseHtml: (html) => new DOMParser().parseFromString(html, "text/html"),
3917
+ confirm: (info) => new Promise((resolve) => {
3918
+ const id = crypto.randomUUID();
3919
+ confirms.set(id, (allow) => {
3920
+ confirms.delete(id);
3921
+ resolve(allow);
3922
+ });
3923
+ post({ type: "confirm", id, info });
3924
+ }),
3925
+ emit: (ev) => post({ type: "event", level: ev.level, kind: ev.kind, msg: ev.msg, ...ev.data ? { data: ev.data } : {} })
3926
+ };
3927
+ port.onMessage.addListener(async (m) => {
3928
+ if (m.type === "cancel") {
3929
+ ac.abort();
3930
+ } else if (m.type === "confirm_result") {
3931
+ confirms.get(m.id)?.(m.allow === true);
3932
+ } else if (m.type === "pace_result") {
3933
+ paces.get(m.id)?.(m);
3934
+ } else if (m.type === "run") {
3935
+ post({ type: "ack", build: BUILD_ID });
3936
+ if (m.build !== BUILD_ID) {
3937
+ post({ type: "result", ok: false, requests: 0, error: { code: "tab_error", message: `Wield's files changed on disk but the extension was not reloaded (worker ${m.build ?? "older"}, page script ${BUILD_ID}). ${RELOAD_HINT}` } });
3938
+ return;
3939
+ }
3940
+ const opts = { runId: m.run_id, signal: ac.signal, timeoutMs: m.timeout_ms, ...m.trust === "registry" ? { trust: "registry" } : {} };
3941
+ try {
3942
+ if (m.mode === "login_check") {
3943
+ const r = m.recipe;
3944
+ const logged_in = await checkLogin({ id: r.id, site: r.site, login_check: r.login_check, auth: r.auth }, adapters, opts);
3945
+ post({ type: "result", ok: true, output: logged_in, requests: 0 });
3946
+ } else {
3947
+ const res = await runRecipe(m.recipe, m.params, adapters, opts);
3948
+ if (res.ok) {
3949
+ post({ type: "result", ok: true, output: res.output, requests: res.requests });
3950
+ } else if (challenge && ["expect_failed", "login_required", "site_challenge"].includes(res.error.code)) {
3951
+ const c = challenge;
3952
+ const message = res.error.code === "site_challenge" ? res.error.message : `${c.host} answered with a bot check (${c.vendor}, HTTP ${c.status}) instead of data.`;
3953
+ post({ type: "result", ok: false, requests: res.requests, challenge: c, error: { code: "site_challenge", message, ...res.error.step ? { step: res.error.step } : {} } });
3954
+ } else {
3955
+ post({ type: "result", ok: false, error: res.error, requests: res.requests });
3956
+ }
3957
+ }
3958
+ } catch (e) {
3959
+ post({ type: "result", ok: false, error: { code: "tab_error", message: String(e?.message ?? e).slice(0, 300) }, requests: 0 });
3960
+ }
3961
+ }
3962
+ });
3963
+ }
3964
+ var CHECK_CSS = `
3965
+ :host { all: initial; }
3966
+ .card { position: fixed; right: 20px; bottom: 20px; z-index: 2147483647; width: 340px; box-sizing: border-box;
3967
+ padding: 16px 16px 12px; border-radius: 10px; background: #fff; color: #3b3c38;
3968
+ font: 14px/1.5 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
3969
+ box-shadow: 0 0 0 1px rgba(18,19,17,.08), 0 2px 2px rgba(18,19,17,.03), 0 12px 24px -6px rgba(18,19,17,.10), 0 32px 56px -20px rgba(18,19,17,.16); }
3970
+ .brand { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; color: #121311; font: 600 13px/1 ui-sans-serif, system-ui, sans-serif; letter-spacing: -0.03em; }
3971
+ .brand svg { width: 14px; height: 14px; }
3972
+ h2 { margin: 0 0 4px; color: #121311; font-size: 15px; font-weight: 560; line-height: 1.3; }
3973
+ p { margin: 0; }
3974
+ .foot { display: flex; align-items: center; justify-content: space-between; margin-top: 12px; padding-top: 10px; border-top: 1px solid #e2e2dd; }
3975
+ .left { color: #5f605a; font-size: 12px; font-variant-numeric: tabular-nums; }
3976
+ button { all: unset; cursor: pointer; padding: 6px 10px; border-radius: 6px; border: 1px solid #d1d1cb; color: #121311;
3977
+ font: 11.5px/1 ui-monospace, "SF Mono", Menlo, monospace; text-transform: uppercase; letter-spacing: .06em; }
3978
+ button:hover { background: #fafaf8; }
3979
+ button:focus-visible { outline: 2px solid #121311; outline-offset: 2px; }
3980
+ @media (prefers-color-scheme: dark) {
3981
+ .card { background: #161715; color: #c8c9c2; box-shadow: 0 0 0 1px rgba(255,255,255,.09), 0 12px 24px -6px rgba(0,0,0,.55); }
3982
+ .brand, h2, button { color: #f0f0ec; } .foot { border-color: #272825; } .left { color: #9a9b94; } button { border-color: #363733; } button:hover { background: #1b1c1a; }
3983
+ }`;
3984
+ function onCheckPort(port) {
3985
+ let host = null;
3986
+ let done = false;
3987
+ let timer;
3988
+ const send = (m) => {
3989
+ try {
3990
+ port.postMessage(m);
3991
+ } catch {
3992
+ }
3993
+ };
3994
+ const cleanup = () => {
3995
+ clearInterval(timer);
3996
+ window.removeEventListener("pointerdown", onInput, true);
3997
+ window.removeEventListener("keydown", onInput, true);
3998
+ window.removeEventListener("wheel", onInput, true);
3999
+ host?.remove();
4000
+ host = null;
4001
+ };
4002
+ const onInput = (e) => {
4003
+ if (done || !e.isTrusted || host && e.composedPath().includes(host)) return;
4004
+ done = true;
4005
+ send({ type: "interacted" });
4006
+ cleanup();
4007
+ };
4008
+ port.onDisconnect.addListener(cleanup);
4009
+ port.onMessage.addListener((m) => {
4010
+ if (m.type === "hide") return cleanup();
4011
+ if (m.type !== "show" || host) return;
4012
+ host = document.createElement("wield-check");
4013
+ const root = host.attachShadow({ mode: "open" });
4014
+ root.innerHTML = `<style>${CHECK_CSS}</style>
4015
+ <div class="card" role="alertdialog" aria-labelledby="t" aria-describedby="d">
4016
+ <div class="brand"><svg viewBox="0 0 24 24" aria-hidden="true"><defs><linearGradient id="wm-hd" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#ffd85a"/><stop offset=".5" stop-color="#ff9a24"/><stop offset="1" stop-color="#ff5200"/></linearGradient><linearGradient id="wm-ht" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#ff6a0a"/><stop offset="1" stop-color="currentColor"/></linearGradient></defs><g transform="translate(0 -2.45)"><path d="M7.0 11.6 2.4 16.6 7.0 21.6M17.0 11.6 21.6 16.6 17.0 21.6" fill="none" stroke="currentColor" stroke-width="2.7" stroke-linecap="round" stroke-linejoin="round"/><path d="M11.0 21.6 12.6 10.28" stroke="currentColor" stroke-width="2.3" stroke-linecap="round"/><g transform="translate(12.6 10.28) rotate(8.045051779050041)"><rect x="-1.15" y="-.2" width="2.3" height="2.4" fill="url(#wm-ht)"/></g><g transform="translate(12.908 8.102) rotate(8.05) scale(0.84)"><path d="M-4.6 -1.9 -2.9 -1.9 -2.4 -1.5 1.3 -1.5 4.4 -0.55 4.6 -0.2 4.6 0.2 4.4 0.55 1.3 1.5 -2.4 1.5 -2.9 1.9 -4.6 1.9Z" fill="url(#wm-hd)" stroke="url(#wm-hd)" stroke-width=".35" stroke-linejoin="round"/><path d="M-4.2 -1.25H-2.9M-1.9 -.95H1.0" stroke="#fff1b8" stroke-width=".55" stroke-linecap="round" opacity=".85"/><rect x="-1.4" y="1.35" width="2.8" height=".8" rx=".3" fill="currentColor"/></g><rect x="7.01" y="5.50" width="1.3" height="1.3" fill="#ffc23d"/><rect x="5.41" y="7.20" width=".9" height=".9" fill="#ff8a1f"/></g></svg>wield</div>
4017
+ <h2 id="t"></h2>
4018
+ <p id="d"></p>
4019
+ <div class="foot"><span class="left" id="left"></span><button type="button" id="no">Not now</button></div>
4020
+ </div>`;
4021
+ root.getElementById("t").textContent = `${m.site_name} wants a quick check that a person is here.`;
4022
+ root.getElementById("d").textContent = "Click anywhere on the page, or scroll a little. Your agent\u2019s request is then retried once.";
4023
+ root.getElementById("no").addEventListener("click", () => {
4024
+ if (done) return;
4025
+ done = true;
4026
+ send({ type: "dismissed" });
4027
+ cleanup();
4028
+ });
4029
+ document.documentElement.append(host);
4030
+ const until = Date.now() + m.timeout_ms;
4031
+ const tick = () => {
4032
+ const left = Math.max(0, Math.round((until - Date.now()) / 1e3));
4033
+ const el = host?.shadowRoot?.getElementById("left");
4034
+ if (el) el.textContent = `Waiting ${Math.floor(left / 60)}:${String(left % 60).padStart(2, "0")}`;
4035
+ };
4036
+ tick();
4037
+ timer = setInterval(tick, 1e3);
4038
+ window.addEventListener("pointerdown", onInput, true);
4039
+ window.addEventListener("keydown", onInput, true);
4040
+ window.addEventListener("wheel", onInput, true);
4041
+ });
4042
+ }
4043
+ var REC_CSS = `
4044
+ :host { all: initial; }
4045
+ .bar { position: fixed; top: 12px; right: 12px; z-index: 2147483647; display: flex; align-items: center; gap: 10px;
4046
+ padding: 8px 8px 8px 12px; border-radius: 10px; background: #16110c; color: #f7f3ec;
4047
+ font: 13px/1.3 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
4048
+ box-shadow: 0 0 0 1px rgba(255,255,255,.08), 0 12px 32px -12px rgba(0,0,0,.6); }
4049
+ .dot { width: 8px; height: 8px; border-radius: 50%; background: #ff7d70; box-shadow: 0 0 0 3px rgba(255,125,112,.25); }
4050
+ .t { font-variant-numeric: tabular-nums; color: #aeafa8; }
4051
+ b { font-weight: 600; color: #fff; }
4052
+ button { all: unset; cursor: pointer; padding: 6px 10px; border-radius: 6px; font: 11.5px/1 ui-monospace, "SF Mono", Menlo, monospace;
4053
+ text-transform: uppercase; letter-spacing: .06em; }
4054
+ .done { background: #ffc23d; color: #16110c; }
4055
+ .cancel { color: #ecece8; border: 1px solid #363733; }
4056
+ button:focus-visible { outline: 2px solid #ffc23d; outline-offset: 2px; }
4057
+ @media (prefers-reduced-motion: no-preference) { .dot { animation: p 1.6s ease-in-out infinite; } @keyframes p { 50% { opacity: .35; } } }`;
4058
+ function labelFor(el) {
4059
+ const aria = el.getAttribute("aria-label");
4060
+ if (aria) return aria.trim();
4061
+ const id = el.id;
4062
+ if (id) {
4063
+ const root = el.getRootNode();
4064
+ const l = root.querySelector?.(`label[for="${CSS.escape(id)}"]`) ?? document.querySelector(`label[for="${CSS.escape(id)}"]`);
4065
+ if (l?.textContent?.trim()) return l.textContent.trim();
4066
+ }
4067
+ const wrap = el.closest("label");
4068
+ if (wrap?.textContent?.trim()) return wrap.textContent.trim();
4069
+ return (el.getAttribute("placeholder") || el.getAttribute("name") || el.id || el.tagName.toLowerCase()).trim();
4070
+ }
4071
+ function onRecordPort(port) {
4072
+ let host = null;
4073
+ let timer;
4074
+ const typed = /* @__PURE__ */ new Map();
4075
+ const page = Math.random().toString(36).slice(2, 10);
4076
+ const send = (m) => {
4077
+ try {
4078
+ port.postMessage(m);
4079
+ } catch {
4080
+ }
4081
+ };
4082
+ const isField = (el) => el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement || el instanceof HTMLSelectElement;
4083
+ const NOT_TEXT2 = /* @__PURE__ */ new Set(["password", "hidden", "submit", "button", "reset", "image", "file", "checkbox", "radio"]);
4084
+ const note = (el, prefilledOk) => {
4085
+ const type = String(el.type || el.tagName).toLowerCase();
4086
+ if (type === "password" || type === "hidden" || el.getAttribute("autocomplete")?.includes("password")) return false;
4087
+ if (prefilledOk && NOT_TEXT2.has(type)) return false;
4088
+ let value = String(el.value ?? "");
4089
+ if (el instanceof HTMLSelectElement) value = el.selectedOptions[0]?.textContent?.trim() || value;
4090
+ if (!value.trim()) return false;
4091
+ const prev = typed.get(el);
4092
+ const next = { label: labelFor(el).slice(0, 80), ...el.getAttribute("name") ? { name: el.getAttribute("name") } : {}, input_type: type, value: value.slice(0, 200) };
4093
+ if (prev && prev.value === next.value) return false;
4094
+ typed.set(el, next);
4095
+ return true;
4096
+ };
4097
+ const flushTyped = () => send({ type: "typed", page, typed: [...typed.values()] });
4098
+ const onInput = (e) => {
4099
+ if (!e.isTrusted) return;
4100
+ const path = e.composedPath();
4101
+ const el = path[0];
4102
+ if (!isField(el) || host && path.includes(host)) return;
4103
+ if (note(el, false)) flushTyped();
4104
+ };
4105
+ const fieldsNear = (start) => {
4106
+ const form = start.form ?? start.closest("form");
4107
+ if (form) return Array.from(form.elements).filter(isField);
4108
+ const root = start.getRootNode();
4109
+ return root instanceof ShadowRoot ? Array.from(root.querySelectorAll("input, textarea, select")).filter(isField) : [];
4110
+ };
4111
+ const onCommit = (e) => {
4112
+ if (!e.isTrusted) return;
4113
+ const path = e.composedPath();
4114
+ if (host && path.includes(host)) return;
4115
+ const first = path[0];
4116
+ let start = null;
4117
+ if (e.type === "keydown") {
4118
+ if (e.key !== "Enter" || !isField(first)) return;
4119
+ start = first;
4120
+ } else if (e.type === "submit") {
4121
+ start = first instanceof HTMLFormElement ? first : null;
4122
+ } else {
4123
+ start = path.find((n) => n instanceof HTMLButtonElement || n instanceof HTMLInputElement && /^(submit|image)$/.test(n.type)) ?? null;
4124
+ }
4125
+ if (!start) return;
4126
+ let changed = isField(start) ? note(start, true) : false;
4127
+ for (const f of fieldsNear(start)) changed = note(f, true) || changed;
4128
+ if (changed) flushTyped();
4129
+ };
4130
+ let nonce = "";
4131
+ const vouched = /* @__PURE__ */ new Set();
4132
+ let documentSeen = false;
4133
+ const verified = (en) => {
4134
+ if (typeof en.url !== "string") return false;
4135
+ if (en.kind === "document") {
4136
+ const nav = performance.getEntriesByType("navigation")[0];
4137
+ if (documentSeen || new URL(en.url, location.href).origin !== location.origin || en.url !== nav?.name && en.url !== location.href) return false;
4138
+ documentSeen = true;
4139
+ return true;
4140
+ }
4141
+ const started = Number(en.started);
4142
+ for (const r of performance.getEntriesByType("resource")) {
4143
+ if (r.name !== en.url || r.initiatorType !== "fetch" && r.initiatorType !== "xmlhttprequest") continue;
4144
+ const at = performance.timeOrigin + r.startTime;
4145
+ const key = `${r.name}|${r.startTime}`;
4146
+ if (at < started - 1e3 || at > started + 15e3 || vouched.has(key)) continue;
4147
+ vouched.add(key);
4148
+ return true;
4149
+ }
4150
+ return false;
4151
+ };
4152
+ const relay = (en, tries = 0) => {
4153
+ if (!nonce) return;
4154
+ if (verified(en)) return send({ type: "entry", entry: en });
4155
+ if (tries < 4) setTimeout(() => relay(en, tries + 1), [300, 1e3, 2500, 5e3][tries]);
4156
+ };
4157
+ const onPageMessage = (e) => {
4158
+ const d = e.data;
4159
+ if (e.source !== window || !d || !nonce || d.nonce !== nonce) return;
4160
+ if (d.source === "wield-rec-hello") window.postMessage({ source: "wield-rec-ctl", nonce, cmd: "ready" }, location.origin);
4161
+ else if (d.source === "wield-rec" && d.entry && typeof d.entry === "object") relay(d.entry);
4162
+ };
4163
+ const cleanup = () => {
4164
+ clearInterval(timer);
4165
+ document.removeEventListener("input", onInput, true);
4166
+ document.removeEventListener("change", onInput, true);
4167
+ document.removeEventListener("keydown", onCommit, true);
4168
+ document.removeEventListener("click", onCommit, true);
4169
+ document.removeEventListener("submit", onCommit, true);
4170
+ window.removeEventListener("message", onPageMessage);
4171
+ host?.remove();
4172
+ host = null;
4173
+ };
4174
+ port.onDisconnect.addListener(cleanup);
4175
+ port.onMessage.addListener((m) => {
4176
+ if (m.type === "hide") return cleanup();
4177
+ if (m.type !== "show" || host) return;
4178
+ if (typeof m.nonce === "string" && m.nonce) {
4179
+ nonce = m.nonce;
4180
+ try {
4181
+ performance.setResourceTimingBufferSize(1e3);
4182
+ } catch {
4183
+ }
4184
+ window.addEventListener("message", onPageMessage);
4185
+ window.postMessage({ source: "wield-rec-ctl", nonce, cmd: "ready" }, location.origin);
4186
+ }
4187
+ host = document.createElement("wield-recorder");
4188
+ const root = host.attachShadow({ mode: "open" });
4189
+ root.innerHTML = `<style>${REC_CSS}</style><div class="bar" role="status" aria-live="polite"><span class="dot" aria-hidden="true"></span>
4190
+ <span>Recording on <b id="h"></b> \xB7 <span class="t" id="t">0:00</span></span>
4191
+ <button class="cancel" id="c" type="button">Cancel</button><button class="done" id="d" type="button">Done</button></div>`;
4192
+ root.getElementById("h").textContent = m.host ?? location.host;
4193
+ root.getElementById("d").addEventListener("click", () => send({ type: "done" }));
4194
+ root.getElementById("c").addEventListener("click", () => send({ type: "cancel" }));
4195
+ document.documentElement.append(host);
4196
+ const t0 = m.started_at ?? Date.now();
4197
+ const tick = () => {
4198
+ const s2 = Math.floor((Date.now() - t0) / 1e3);
4199
+ const el = host?.shadowRoot?.getElementById("t");
4200
+ if (el) el.textContent = `${Math.floor(s2 / 60)}:${String(s2 % 60).padStart(2, "0")}`;
4201
+ };
4202
+ tick();
4203
+ let n = 0;
4204
+ timer = setInterval(() => {
4205
+ tick();
4206
+ if (++n % 15 === 0) send({ type: "ping" });
4207
+ }, 1e3);
4208
+ document.addEventListener("input", onInput, true);
4209
+ document.addEventListener("change", onInput, true);
4210
+ document.addEventListener("keydown", onCommit, true);
4211
+ document.addEventListener("click", onCommit, true);
4212
+ document.addEventListener("submit", onCommit, true);
4213
+ });
4214
+ }
4215
+ function onPort(port) {
4216
+ if (port.name.startsWith("wield-run:")) onRunPort(port);
4217
+ else if (port.name.startsWith("wield-check:")) onCheckPort(port);
4218
+ else if (port.name.startsWith("wield-rec:")) onRecordPort(port);
4219
+ }
4220
+ if (globalThis.__wieldListener) {
4221
+ try {
4222
+ chrome.runtime.onConnect.removeListener(globalThis.__wieldListener);
4223
+ } catch {
4224
+ }
4225
+ }
4226
+ globalThis.__wieldListener = onPort;
4227
+ chrome.runtime.onConnect.addListener(onPort);
4228
+ })();