@mejazbese21/obsidian-clipper-cli 1.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (5) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +109 -0
  3. package/dist/api.mjs +5018 -0
  4. package/dist/cli.cjs +26611 -0
  5. package/package.json +70 -0
package/dist/api.mjs ADDED
@@ -0,0 +1,5018 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/api.ts
8
+ import DefuddleClass from "defuddle";
9
+ import { createMarkdownContent as createMarkdownContent2 } from "defuddle/full";
10
+
11
+ // src/utils/tokenizer.ts
12
+ var KEYWORDS = {
13
+ "if": "keyword_if",
14
+ "elseif": "keyword_elseif",
15
+ "else": "keyword_else",
16
+ "endif": "keyword_endif",
17
+ "for": "keyword_for",
18
+ "in": "keyword_in",
19
+ "endfor": "keyword_endfor",
20
+ "set": "keyword_set",
21
+ "and": "op_and",
22
+ "or": "op_or",
23
+ "not": "op_not",
24
+ "contains": "op_contains",
25
+ "true": "boolean",
26
+ "false": "boolean",
27
+ "null": "null"
28
+ };
29
+ function tokenize(input) {
30
+ const state = {
31
+ input,
32
+ pos: 0,
33
+ line: 1,
34
+ column: 1,
35
+ mode: "text",
36
+ tokens: [],
37
+ errors: []
38
+ };
39
+ while (state.pos < state.input.length) {
40
+ switch (state.mode) {
41
+ case "text":
42
+ tokenizeText(state);
43
+ break;
44
+ case "variable":
45
+ tokenizeVariable(state);
46
+ break;
47
+ case "tag":
48
+ tokenizeTag(state);
49
+ break;
50
+ }
51
+ }
52
+ state.tokens.push({
53
+ type: "eof",
54
+ value: "",
55
+ line: state.line,
56
+ column: state.column
57
+ });
58
+ return {
59
+ tokens: state.tokens,
60
+ errors: state.errors
61
+ };
62
+ }
63
+ function tokenizeText(state) {
64
+ const startPos = state.pos;
65
+ const startLine = state.line;
66
+ const startColumn = state.column;
67
+ while (state.pos < state.input.length) {
68
+ if (lookAhead(state, "{{")) {
69
+ if (state.pos > startPos) {
70
+ state.tokens.push({
71
+ type: "text",
72
+ value: state.input.slice(startPos, state.pos),
73
+ line: startLine,
74
+ column: startColumn
75
+ });
76
+ }
77
+ advance(state, 2);
78
+ state.tokens.push({
79
+ type: "variable_start",
80
+ value: "{{",
81
+ line: state.line,
82
+ column: state.column - 2,
83
+ trimLeft: false
84
+ // Variables preserve whitespace by default
85
+ });
86
+ state.mode = "variable";
87
+ return;
88
+ }
89
+ if (lookAhead(state, "{%")) {
90
+ if (state.pos > startPos) {
91
+ state.tokens.push({
92
+ type: "text",
93
+ value: state.input.slice(startPos, state.pos),
94
+ line: startLine,
95
+ column: startColumn
96
+ });
97
+ }
98
+ advance(state, 2);
99
+ state.tokens.push({
100
+ type: "tag_start",
101
+ value: "{%",
102
+ line: state.line,
103
+ column: state.column - 2,
104
+ trimLeft: false
105
+ // Preserve whitespace before tags
106
+ });
107
+ state.mode = "tag";
108
+ return;
109
+ }
110
+ advanceChar(state);
111
+ }
112
+ if (state.pos > startPos) {
113
+ state.tokens.push({
114
+ type: "text",
115
+ value: state.input.slice(startPos, state.pos),
116
+ line: startLine,
117
+ column: startColumn
118
+ });
119
+ }
120
+ }
121
+ function tokenizeVariable(state) {
122
+ skipWhitespace(state);
123
+ if (lookAhead(state, "}}")) {
124
+ state.tokens.push({
125
+ type: "variable_end",
126
+ value: "}}",
127
+ line: state.line,
128
+ column: state.column,
129
+ trimRight: false
130
+ // Variables preserve whitespace by default
131
+ });
132
+ advance(state, 2);
133
+ state.mode = "text";
134
+ return;
135
+ }
136
+ const nextChar = state.input[state.pos + 1];
137
+ if (state.input[state.pos] === "}" && nextChar !== "}") {
138
+ const validAfterBrace = ["|", ",", ")", "]", " ", " ", "\n", "\r"];
139
+ if (!validAfterBrace.includes(nextChar)) {
140
+ state.errors.push({
141
+ message: `Malformed variable: expected '}}' but found '}'. Did you forget a '}'?`,
142
+ line: state.line,
143
+ column: state.column
144
+ });
145
+ state.tokens.push({
146
+ type: "variable_end",
147
+ value: "}",
148
+ line: state.line,
149
+ column: state.column,
150
+ trimRight: false
151
+ });
152
+ advanceChar(state);
153
+ state.mode = "text";
154
+ return;
155
+ }
156
+ }
157
+ if (lookAhead(state, "{%") || lookAhead(state, "{{")) {
158
+ const varStartIndex = [...state.tokens].reverse().findIndex((t) => t.type === "variable_start");
159
+ const actualIndex = varStartIndex >= 0 ? state.tokens.length - 1 - varStartIndex : -1;
160
+ const varStartToken = actualIndex >= 0 ? state.tokens[actualIndex] : null;
161
+ const startLine = varStartToken?.line || state.line;
162
+ state.errors.push({
163
+ message: `Missing closing '}}' for variable`,
164
+ line: startLine,
165
+ column: varStartToken?.column || state.column
166
+ });
167
+ if (actualIndex >= 0) {
168
+ state.tokens.splice(actualIndex);
169
+ }
170
+ state.mode = "text";
171
+ return;
172
+ }
173
+ tokenizeExpression(state, "variable");
174
+ }
175
+ function tokenizeTag(state) {
176
+ skipWhitespace(state);
177
+ if (lookAhead(state, "%}")) {
178
+ state.tokens.push({
179
+ type: "tag_end",
180
+ value: "%}",
181
+ line: state.line,
182
+ column: state.column,
183
+ trimRight: true
184
+ // Tags always trim whitespace
185
+ });
186
+ advance(state, 2);
187
+ state.mode = "text";
188
+ return;
189
+ }
190
+ if (lookAhead(state, "-%}")) {
191
+ state.tokens.push({
192
+ type: "tag_end",
193
+ value: "-%}",
194
+ line: state.line,
195
+ column: state.column,
196
+ trimRight: true
197
+ });
198
+ advance(state, 3);
199
+ state.mode = "text";
200
+ return;
201
+ }
202
+ if (state.input[state.pos] === "}" && state.pos > 0 && state.input[state.pos - 1] !== "%") {
203
+ state.errors.push({
204
+ message: `Malformed tag: expected '%}' but found '}'. Did you forget the '%'?`,
205
+ line: state.line,
206
+ column: state.column
207
+ });
208
+ state.tokens.push({
209
+ type: "tag_end",
210
+ value: "}",
211
+ line: state.line,
212
+ column: state.column,
213
+ trimRight: true
214
+ });
215
+ advanceChar(state);
216
+ state.mode = "text";
217
+ return;
218
+ }
219
+ if (lookAhead(state, "{%") || lookAhead(state, "{{")) {
220
+ const tagStartIndex = [...state.tokens].reverse().findIndex((t) => t.type === "tag_start");
221
+ const actualIndex = tagStartIndex >= 0 ? state.tokens.length - 1 - tagStartIndex : -1;
222
+ const tagStartToken = actualIndex >= 0 ? state.tokens[actualIndex] : null;
223
+ const startLine = tagStartToken?.line || state.line;
224
+ state.errors.push({
225
+ message: `Missing closing '%}' for tag`,
226
+ line: startLine,
227
+ column: tagStartToken?.column || state.column
228
+ });
229
+ if (actualIndex >= 0) {
230
+ state.tokens.splice(actualIndex);
231
+ }
232
+ state.mode = "text";
233
+ return;
234
+ }
235
+ tokenizeExpression(state, "tag");
236
+ }
237
+ function tokenizeExpression(state, mode) {
238
+ skipWhitespace(state);
239
+ if (state.pos >= state.input.length) {
240
+ state.errors.push({
241
+ message: mode === "variable" ? `Unclosed variable - missing '}}'` : `Unclosed tag - missing '%}'`,
242
+ line: state.line,
243
+ column: state.column
244
+ });
245
+ return;
246
+ }
247
+ const char = state.input[state.pos];
248
+ const startLine = state.line;
249
+ const startColumn = state.column;
250
+ if (char === '"' || char === "'") {
251
+ tokenizeString(state);
252
+ return;
253
+ }
254
+ if (isDigit(char) || char === "-" && isDigit(state.input[state.pos + 1])) {
255
+ tokenizeNumber(state);
256
+ return;
257
+ }
258
+ if (lookAhead(state, "==")) {
259
+ state.tokens.push({ type: "op_eq", value: "==", line: startLine, column: startColumn });
260
+ advance(state, 2);
261
+ return;
262
+ }
263
+ if (lookAhead(state, "!=")) {
264
+ state.tokens.push({ type: "op_neq", value: "!=", line: startLine, column: startColumn });
265
+ advance(state, 2);
266
+ return;
267
+ }
268
+ if (lookAhead(state, ">=")) {
269
+ state.tokens.push({ type: "op_gte", value: ">=", line: startLine, column: startColumn });
270
+ advance(state, 2);
271
+ return;
272
+ }
273
+ if (lookAhead(state, "<=")) {
274
+ state.tokens.push({ type: "op_lte", value: "<=", line: startLine, column: startColumn });
275
+ advance(state, 2);
276
+ return;
277
+ }
278
+ if (lookAhead(state, "&&")) {
279
+ state.tokens.push({ type: "op_and", value: "&&", line: startLine, column: startColumn });
280
+ advance(state, 2);
281
+ return;
282
+ }
283
+ if (lookAhead(state, "||")) {
284
+ state.tokens.push({ type: "op_or", value: "||", line: startLine, column: startColumn });
285
+ advance(state, 2);
286
+ return;
287
+ }
288
+ if (lookAhead(state, "??")) {
289
+ state.tokens.push({ type: "op_nullish", value: "??", line: startLine, column: startColumn });
290
+ advance(state, 2);
291
+ return;
292
+ }
293
+ if (lookAhead(state, "=>")) {
294
+ state.tokens.push({ type: "arrow", value: "=>", line: startLine, column: startColumn });
295
+ advance(state, 2);
296
+ return;
297
+ }
298
+ switch (char) {
299
+ case ">":
300
+ state.tokens.push({ type: "op_gt", value: ">", line: startLine, column: startColumn });
301
+ advanceChar(state);
302
+ return;
303
+ case "<":
304
+ state.tokens.push({ type: "op_lt", value: "<", line: startLine, column: startColumn });
305
+ advanceChar(state);
306
+ return;
307
+ case "!":
308
+ state.tokens.push({ type: "op_not", value: "!", line: startLine, column: startColumn });
309
+ advanceChar(state);
310
+ return;
311
+ case "=":
312
+ state.tokens.push({ type: "op_assign", value: "=", line: startLine, column: startColumn });
313
+ advanceChar(state);
314
+ return;
315
+ case "|":
316
+ state.tokens.push({ type: "pipe", value: "|", line: startLine, column: startColumn });
317
+ advanceChar(state);
318
+ return;
319
+ case "(":
320
+ state.tokens.push({ type: "lparen", value: "(", line: startLine, column: startColumn });
321
+ advanceChar(state);
322
+ return;
323
+ case ")":
324
+ state.tokens.push({ type: "rparen", value: ")", line: startLine, column: startColumn });
325
+ advanceChar(state);
326
+ return;
327
+ case "[":
328
+ state.tokens.push({ type: "lbracket", value: "[", line: startLine, column: startColumn });
329
+ advanceChar(state);
330
+ return;
331
+ case "]":
332
+ state.tokens.push({ type: "rbracket", value: "]", line: startLine, column: startColumn });
333
+ advanceChar(state);
334
+ return;
335
+ case ":":
336
+ state.tokens.push({ type: "colon", value: ":", line: startLine, column: startColumn });
337
+ advanceChar(state);
338
+ return;
339
+ case ",":
340
+ state.tokens.push({ type: "comma", value: ",", line: startLine, column: startColumn });
341
+ advanceChar(state);
342
+ return;
343
+ case ".":
344
+ state.tokens.push({ type: "dot", value: ".", line: startLine, column: startColumn });
345
+ advanceChar(state);
346
+ return;
347
+ case "*":
348
+ state.tokens.push({ type: "star", value: "*", line: startLine, column: startColumn });
349
+ advanceChar(state);
350
+ return;
351
+ case "/":
352
+ state.tokens.push({ type: "slash", value: "/", line: startLine, column: startColumn });
353
+ advanceChar(state);
354
+ return;
355
+ case "{":
356
+ state.tokens.push({ type: "lbrace", value: "{", line: startLine, column: startColumn });
357
+ advanceChar(state);
358
+ return;
359
+ case "}":
360
+ state.tokens.push({ type: "rbrace", value: "}", line: startLine, column: startColumn });
361
+ advanceChar(state);
362
+ return;
363
+ case "$":
364
+ state.tokens.push({ type: "dollar", value: "$", line: startLine, column: startColumn });
365
+ advanceChar(state);
366
+ return;
367
+ }
368
+ if (isIdentifierStart(char)) {
369
+ tokenizeIdentifier(state);
370
+ return;
371
+ }
372
+ if (char === "\\") {
373
+ tokenizeEscapedArgument(state);
374
+ return;
375
+ }
376
+ state.errors.push({
377
+ message: `Unexpected character '${char}' in template`,
378
+ line: state.line,
379
+ column: state.column
380
+ });
381
+ advanceChar(state);
382
+ }
383
+ function tokenizeString(state) {
384
+ const quote = state.input[state.pos];
385
+ const startLine = state.line;
386
+ const startColumn = state.column;
387
+ let value = "";
388
+ advanceChar(state);
389
+ while (state.pos < state.input.length) {
390
+ const char = state.input[state.pos];
391
+ const nextChar = state.input[state.pos + 1] || "";
392
+ if (char === quote) {
393
+ advanceChar(state);
394
+ state.tokens.push({
395
+ type: "string",
396
+ value,
397
+ line: startLine,
398
+ column: startColumn
399
+ });
400
+ return;
401
+ }
402
+ if (char === "}" && nextChar === "}" || char === "%" && nextChar === "}") {
403
+ state.errors.push({
404
+ message: `Unclosed string - missing ${quote} before ${char}${nextChar}`,
405
+ line: startLine,
406
+ column: startColumn
407
+ });
408
+ state.tokens.push({
409
+ type: "string",
410
+ value,
411
+ line: startLine,
412
+ column: startColumn
413
+ });
414
+ return;
415
+ }
416
+ if (char === "\\" && state.pos + 1 < state.input.length) {
417
+ advanceChar(state);
418
+ const escaped = state.input[state.pos];
419
+ switch (escaped) {
420
+ case "n":
421
+ value += "\n";
422
+ break;
423
+ case "t":
424
+ value += " ";
425
+ break;
426
+ case "r":
427
+ value += "\r";
428
+ break;
429
+ case "\\":
430
+ value += "\\";
431
+ break;
432
+ case '"':
433
+ value += '"';
434
+ break;
435
+ case "'":
436
+ value += "'";
437
+ break;
438
+ default:
439
+ value += escaped;
440
+ }
441
+ advanceChar(state);
442
+ continue;
443
+ }
444
+ value += char;
445
+ advanceChar(state);
446
+ }
447
+ state.errors.push({
448
+ message: `Unclosed string - missing closing ${quote}`,
449
+ line: startLine,
450
+ column: startColumn
451
+ });
452
+ state.tokens.push({
453
+ type: "string",
454
+ value,
455
+ line: startLine,
456
+ column: startColumn
457
+ });
458
+ }
459
+ function tokenizeEscapedArgument(state) {
460
+ const startLine = state.line;
461
+ const startColumn = state.column;
462
+ let value = "";
463
+ while (state.pos < state.input.length) {
464
+ const char = state.input[state.pos];
465
+ const nextChar = state.input[state.pos + 1] || "";
466
+ if (char === "|" || char === "%" || char === "}" || char === ")") {
467
+ break;
468
+ }
469
+ if (char === "+" && (nextChar === "%" || nextChar === "}")) {
470
+ break;
471
+ }
472
+ if (char === "\\" && state.pos + 1 < state.input.length) {
473
+ const escaped = state.input[state.pos + 1];
474
+ switch (escaped) {
475
+ case '"':
476
+ value += '"';
477
+ break;
478
+ case "'":
479
+ value += "'";
480
+ break;
481
+ case "\\":
482
+ value += "\\";
483
+ break;
484
+ case "n":
485
+ value += "\n";
486
+ break;
487
+ case "t":
488
+ value += " ";
489
+ break;
490
+ case "r":
491
+ value += "\r";
492
+ break;
493
+ case ",":
494
+ value += ",";
495
+ break;
496
+ case "|":
497
+ value += "|";
498
+ break;
499
+ default:
500
+ value += escaped;
501
+ }
502
+ advanceChar(state);
503
+ advanceChar(state);
504
+ continue;
505
+ }
506
+ value += char;
507
+ advanceChar(state);
508
+ }
509
+ state.tokens.push({
510
+ type: "string",
511
+ value,
512
+ line: startLine,
513
+ column: startColumn
514
+ });
515
+ }
516
+ function tokenizeNumber(state) {
517
+ const startLine = state.line;
518
+ const startColumn = state.column;
519
+ let value = "";
520
+ if (state.input[state.pos] === "-") {
521
+ value += "-";
522
+ advanceChar(state);
523
+ }
524
+ while (state.pos < state.input.length && isDigit(state.input[state.pos])) {
525
+ value += state.input[state.pos];
526
+ advanceChar(state);
527
+ }
528
+ if (state.pos < state.input.length && state.input[state.pos] === ".") {
529
+ value += ".";
530
+ advanceChar(state);
531
+ while (state.pos < state.input.length && isDigit(state.input[state.pos])) {
532
+ value += state.input[state.pos];
533
+ advanceChar(state);
534
+ }
535
+ }
536
+ state.tokens.push({
537
+ type: "number",
538
+ value,
539
+ line: startLine,
540
+ column: startColumn
541
+ });
542
+ }
543
+ function tokenizeIdentifier(state) {
544
+ const startLine = state.line;
545
+ const startColumn = state.column;
546
+ let value = "";
547
+ while (state.pos < state.input.length && isIdentifierChar(state.input[state.pos])) {
548
+ value += state.input[state.pos];
549
+ advanceChar(state);
550
+ }
551
+ if ((value === "selector" || value === "selectorHtml") && state.pos < state.input.length && state.input[state.pos] === ":") {
552
+ value += ":";
553
+ advanceChar(state);
554
+ value = tokenizeCssSelector(state, value);
555
+ }
556
+ const lowerValue = value.toLowerCase();
557
+ const keywordType = KEYWORDS[lowerValue];
558
+ if (keywordType) {
559
+ state.tokens.push({
560
+ type: keywordType,
561
+ value,
562
+ line: startLine,
563
+ column: startColumn
564
+ });
565
+ } else {
566
+ state.tokens.push({
567
+ type: "identifier",
568
+ value,
569
+ line: startLine,
570
+ column: startColumn
571
+ });
572
+ }
573
+ }
574
+ function tokenizeCssSelector(state, value) {
575
+ let bracketDepth = 0;
576
+ let parenDepth = 0;
577
+ let inString = null;
578
+ while (state.pos < state.input.length) {
579
+ const char = state.input[state.pos];
580
+ const nextChar = state.input[state.pos + 1] || "";
581
+ if (!inString && bracketDepth === 0 && parenDepth === 0) {
582
+ if (char === "|") {
583
+ break;
584
+ }
585
+ if (char === "%" && nextChar === "}") {
586
+ break;
587
+ }
588
+ if (char === "}" && nextChar === "}") {
589
+ break;
590
+ }
591
+ if (char === "-" && nextChar === "%") {
592
+ break;
593
+ }
594
+ if (char === "-" && nextChar === "}") {
595
+ break;
596
+ }
597
+ if (char === "}" && nextChar !== "}") {
598
+ break;
599
+ }
600
+ }
601
+ if (char === "}" && nextChar === "}" || char === "%" && nextChar === "}") {
602
+ if (inString) {
603
+ state.errors.push({
604
+ message: `Unclosed string in selector - missing closing ${inString}`,
605
+ line: state.line,
606
+ column: state.column
607
+ });
608
+ break;
609
+ }
610
+ if (bracketDepth > 0) {
611
+ state.errors.push({
612
+ message: `Unclosed '[' in selector - missing ']'`,
613
+ line: state.line,
614
+ column: state.column
615
+ });
616
+ break;
617
+ }
618
+ if (parenDepth > 0) {
619
+ state.errors.push({
620
+ message: `Unclosed '(' in selector - missing ')'`,
621
+ line: state.line,
622
+ column: state.column
623
+ });
624
+ break;
625
+ }
626
+ }
627
+ if (!inString && char === "\\" && (nextChar === '"' || nextChar === "'")) {
628
+ value += char;
629
+ advanceChar(state);
630
+ value += state.input[state.pos];
631
+ advanceChar(state);
632
+ continue;
633
+ }
634
+ if (!inString && (char === '"' || char === "'")) {
635
+ inString = char;
636
+ value += char;
637
+ advanceChar(state);
638
+ continue;
639
+ }
640
+ if (inString && char === inString) {
641
+ inString = null;
642
+ value += char;
643
+ advanceChar(state);
644
+ continue;
645
+ }
646
+ if (inString && char === "\\" && state.pos + 1 < state.input.length) {
647
+ value += char;
648
+ advanceChar(state);
649
+ value += state.input[state.pos];
650
+ advanceChar(state);
651
+ continue;
652
+ }
653
+ if (!inString) {
654
+ if (char === "[") {
655
+ bracketDepth++;
656
+ } else if (char === "]") {
657
+ bracketDepth--;
658
+ if (bracketDepth < 0) {
659
+ state.errors.push({
660
+ message: `Extra ']' in selector - no matching '['`,
661
+ line: state.line,
662
+ column: state.column
663
+ });
664
+ bracketDepth = 0;
665
+ }
666
+ } else if (char === "(") {
667
+ parenDepth++;
668
+ } else if (char === ")") {
669
+ parenDepth--;
670
+ if (parenDepth < 0) {
671
+ state.errors.push({
672
+ message: `Extra ')' in selector - no matching '('`,
673
+ line: state.line,
674
+ column: state.column
675
+ });
676
+ parenDepth = 0;
677
+ }
678
+ }
679
+ }
680
+ value += char;
681
+ advanceChar(state);
682
+ }
683
+ return value.trimEnd();
684
+ }
685
+ function lookAhead(state, str) {
686
+ return state.input.slice(state.pos, state.pos + str.length) === str;
687
+ }
688
+ function advance(state, count) {
689
+ for (let i = 0; i < count; i++) {
690
+ advanceChar(state);
691
+ }
692
+ }
693
+ function advanceChar(state) {
694
+ if (state.pos < state.input.length) {
695
+ if (state.input[state.pos] === "\n") {
696
+ state.line++;
697
+ state.column = 1;
698
+ } else {
699
+ state.column++;
700
+ }
701
+ state.pos++;
702
+ }
703
+ }
704
+ function skipWhitespace(state) {
705
+ while (state.pos < state.input.length && isWhitespace(state.input[state.pos])) {
706
+ advanceChar(state);
707
+ }
708
+ }
709
+ function isWhitespace(char) {
710
+ return char === " " || char === " " || char === "\n" || char === "\r";
711
+ }
712
+ function isDigit(char) {
713
+ return char >= "0" && char <= "9";
714
+ }
715
+ function isIdentifierStart(char) {
716
+ return char >= "a" && char <= "z" || char >= "A" && char <= "Z" || char === "_" || char === "@";
717
+ }
718
+ function isIdentifierChar(char) {
719
+ return isIdentifierStart(char) || isDigit(char) || char === "-" || // For kebab-case
720
+ char === ".";
721
+ }
722
+
723
+ // src/utils/cli-stubs.ts
724
+ var cli_stubs_exports = {};
725
+ __export(cli_stubs_exports, {
726
+ default: () => cli_stubs_default,
727
+ generalSettings: () => generalSettings,
728
+ getLocalStorage: () => getLocalStorage,
729
+ incrementStat: () => incrementStat,
730
+ loadSettings: () => loadSettings,
731
+ saveSettings: () => saveSettings,
732
+ setLocalStorage: () => setLocalStorage
733
+ });
734
+ var cli_stubs_default = {};
735
+ var generalSettings = {
736
+ vaults: [],
737
+ betaFeatures: false,
738
+ legacyMode: false,
739
+ silentOpen: false,
740
+ openBehavior: "popup",
741
+ highlighterEnabled: false,
742
+ alwaysShowHighlights: false,
743
+ highlightBehavior: "no-highlights",
744
+ showMoreActionsButton: false,
745
+ interpreterModel: "",
746
+ models: [],
747
+ providers: [],
748
+ interpreterEnabled: false,
749
+ interpreterAutoRun: false,
750
+ defaultPromptContext: "",
751
+ propertyTypes: [],
752
+ readerSettings: {
753
+ fontSize: 16,
754
+ lineHeight: 1.5,
755
+ maxWidth: 700,
756
+ lightTheme: "default",
757
+ darkTheme: "same",
758
+ appearance: "auto",
759
+ fonts: [],
760
+ defaultFont: "",
761
+ blendImages: true,
762
+ colorLinks: false,
763
+ followLinks: true,
764
+ pinPlayer: true,
765
+ autoScroll: true,
766
+ highlightActiveLine: true,
767
+ customCss: ""
768
+ },
769
+ stats: {
770
+ addToObsidian: 0,
771
+ saveFile: 0,
772
+ copyToClipboard: 0,
773
+ share: 0
774
+ },
775
+ history: [],
776
+ ratings: [],
777
+ saveBehavior: "addToObsidian"
778
+ };
779
+ var loadSettings = async () => {
780
+ };
781
+ var saveSettings = async () => {
782
+ };
783
+ var incrementStat = async () => {
784
+ };
785
+ var getLocalStorage = async () => ({});
786
+ var setLocalStorage = async () => {
787
+ };
788
+
789
+ // src/utils/browser-polyfill.ts
790
+ var browser_polyfill_default = cli_stubs_exports;
791
+
792
+ // src/utils/debug.ts
793
+ if (false) {
794
+ browser_polyfill_default.storage.local.get("debugMode").then((result) => {
795
+ debugMode = result.debugMode ?? false;
796
+ console.log(`Debug mode initialized to: ${debugMode ? "ON" : "OFF"}`);
797
+ }).catch((error) => {
798
+ console.error("Error initializing debug mode:", error);
799
+ });
800
+ }
801
+ var debugLog = (filterName, ...args) => {
802
+ if (false) {
803
+ console.log(`[${filterName}]`, ...args);
804
+ }
805
+ };
806
+ if (false) {
807
+ globalThis.toggleDebug = toggleDebug;
808
+ }
809
+
810
+ // src/utils/parser-utils.ts
811
+ function createParserState(initialCurrent = "") {
812
+ return {
813
+ current: initialCurrent,
814
+ inQuote: false,
815
+ quoteType: "",
816
+ inRegex: false,
817
+ curlyDepth: 0,
818
+ parenDepth: 0,
819
+ escapeNext: false
820
+ };
821
+ }
822
+ function processCharacter(char, state) {
823
+ if (state.escapeNext) {
824
+ state.current += char;
825
+ state.escapeNext = false;
826
+ return;
827
+ }
828
+ if (char === "\\") {
829
+ state.current += char;
830
+ if (!state.inRegex) {
831
+ state.escapeNext = true;
832
+ }
833
+ return;
834
+ }
835
+ if ((char === '"' || char === "'") && !state.inRegex) {
836
+ state.inQuote = !state.inQuote;
837
+ state.quoteType = state.inQuote ? char : "";
838
+ state.current += char;
839
+ return;
840
+ }
841
+ if (char === "/" && !state.inQuote && !state.inRegex && (state.current.endsWith(":") || state.current.endsWith(","))) {
842
+ state.inRegex = true;
843
+ state.current += char;
844
+ return;
845
+ }
846
+ if (char === "/" && state.inRegex && !state.escapeNext) {
847
+ state.inRegex = false;
848
+ state.current += char;
849
+ return;
850
+ }
851
+ if (char === "{") {
852
+ state.curlyDepth++;
853
+ state.current += char;
854
+ return;
855
+ }
856
+ if (char === "}") {
857
+ state.curlyDepth--;
858
+ state.current += char;
859
+ return;
860
+ }
861
+ if (char === "(" && !state.inQuote) {
862
+ state.parenDepth++;
863
+ state.current += char;
864
+ return;
865
+ }
866
+ if (char === ")" && !state.inQuote) {
867
+ state.parenDepth--;
868
+ state.current += char;
869
+ return;
870
+ }
871
+ state.current += char;
872
+ }
873
+ function parseRegexPattern(pattern) {
874
+ const match = pattern.match(/^\/(.+)\/([gimsuy]*)$/);
875
+ if (!match) return null;
876
+ return {
877
+ pattern: match[1],
878
+ flags: match[2]
879
+ };
880
+ }
881
+
882
+ // src/utils/filters/blockquote.ts
883
+ var blockquote = (input) => {
884
+ const processBlockquote = (str, depth = 1) => {
885
+ const prefix = "> ".repeat(depth);
886
+ return str.split("\n").map((line) => `${prefix}${line}`).join("\n");
887
+ };
888
+ const processArray = (arr, depth = 1) => {
889
+ return arr.map((item) => {
890
+ if (Array.isArray(item)) {
891
+ return processArray(item, depth + 1);
892
+ }
893
+ return processBlockquote(String(item), depth);
894
+ }).join("\n");
895
+ };
896
+ try {
897
+ const parsedInput = JSON.parse(input);
898
+ if (Array.isArray(parsedInput)) {
899
+ return processArray(parsedInput);
900
+ }
901
+ if (typeof parsedInput === "object" && parsedInput !== null) {
902
+ return processBlockquote(JSON.stringify(parsedInput, null, 2));
903
+ }
904
+ return processBlockquote(String(parsedInput));
905
+ } catch (error) {
906
+ if (Array.isArray(input)) {
907
+ return processArray(input);
908
+ }
909
+ return processBlockquote(input);
910
+ }
911
+ };
912
+
913
+ // src/utils/filters/calc.ts
914
+ var validateCalcParams = (param) => {
915
+ if (!param) {
916
+ return { valid: false, error: 'requires an operation (e.g., calc:"+10", calc:"*2")' };
917
+ }
918
+ const operation = param.replace(/^['"](.*)['"]$/, "$1").trim();
919
+ if (!operation) {
920
+ return { valid: false, error: "operation cannot be empty" };
921
+ }
922
+ const validOperators = ["+", "-", "*", "/", "^", "**"];
923
+ const operator = operation.slice(0, 2) === "**" ? "**" : operation.charAt(0);
924
+ if (!validOperators.includes(operator)) {
925
+ return { valid: false, error: `invalid operator "${operator}". Use +, -, *, /, ^ or **` };
926
+ }
927
+ const valueStr = operation.slice(operator === "**" ? 2 : 1);
928
+ if (!valueStr || isNaN(Number(valueStr))) {
929
+ return { valid: false, error: 'requires a number after the operator (e.g., "+10")' };
930
+ }
931
+ return { valid: true };
932
+ };
933
+ var calc = (str, param) => {
934
+ if (!param) {
935
+ return str;
936
+ }
937
+ try {
938
+ const num = Number(str);
939
+ if (isNaN(num)) {
940
+ console.error("Input is not a number:", str);
941
+ return str;
942
+ }
943
+ const operation = param.replace(/^['"](.*)['"]$/, "$1").trim();
944
+ const operator = operation.slice(0, 2) === "**" ? "**" : operation.charAt(0);
945
+ const value = Number(operation.slice(operator === "**" ? 2 : 1));
946
+ if (isNaN(value)) {
947
+ console.error("Invalid calculation value:", operation);
948
+ return str;
949
+ }
950
+ let result;
951
+ switch (operator) {
952
+ case "+":
953
+ result = num + value;
954
+ break;
955
+ case "-":
956
+ result = num - value;
957
+ break;
958
+ case "*":
959
+ result = num * value;
960
+ break;
961
+ case "/":
962
+ result = num / value;
963
+ break;
964
+ case "**":
965
+ case "^":
966
+ result = Math.pow(num, value);
967
+ break;
968
+ default:
969
+ console.error("Invalid operator:", operator);
970
+ return str;
971
+ }
972
+ return Number(result.toFixed(10)).toString();
973
+ } catch (error) {
974
+ console.error("Error in calc filter:", error);
975
+ return str;
976
+ }
977
+ };
978
+
979
+ // src/utils/filters/callout.ts
980
+ var callout = (str, param) => {
981
+ let type = "info";
982
+ let title2 = "";
983
+ let foldState = null;
984
+ if (param) {
985
+ param = param.replace(/^\((.*)\)$/, "$1");
986
+ const params = param.split(/,(?=(?:(?:[^"']*["'][^"']*["'])*[^"']*$))/).map((p) => {
987
+ return p.trim().replace(/^(['"])([\s\S]*)\1$/, "$2");
988
+ });
989
+ if (params.length > 0) type = params[0] || type;
990
+ if (params.length > 1) title2 = params[1] || title2;
991
+ if (params.length > 2) {
992
+ if (params[2].toLowerCase() === "true") foldState = "-";
993
+ else if (params[2].toLowerCase() === "false") foldState = "+";
994
+ }
995
+ }
996
+ let calloutHeader = `> [!${type}]`;
997
+ if (foldState) calloutHeader += foldState;
998
+ if (title2) calloutHeader += ` ${title2}`;
999
+ return `${calloutHeader}
1000
+ ${str.split("\n").map((line) => `> ${line}`).join("\n")}`;
1001
+ };
1002
+
1003
+ // src/utils/filters/camel.ts
1004
+ var camel = (str) => str.replace(
1005
+ /(?:^\w|[A-Z]|\b\w)/g,
1006
+ (letter, index) => index === 0 ? letter.toLowerCase() : letter.toUpperCase()
1007
+ ).replace(/[\s_-]+/g, "");
1008
+
1009
+ // src/utils/filters/capitalize.ts
1010
+ var capitalize = (input) => {
1011
+ const capitalizeString = (str) => str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
1012
+ try {
1013
+ const parseAndCapitalize = (value) => {
1014
+ if (typeof value === "string") {
1015
+ return capitalizeString(value);
1016
+ } else if (Array.isArray(value)) {
1017
+ return value.map(parseAndCapitalize);
1018
+ } else if (typeof value === "object" && value !== null) {
1019
+ const result = {};
1020
+ for (const [key, val] of Object.entries(value)) {
1021
+ result[capitalizeString(key)] = parseAndCapitalize(val);
1022
+ }
1023
+ return result;
1024
+ }
1025
+ return value;
1026
+ };
1027
+ const parsed = JSON.parse(input);
1028
+ const capitalized = parseAndCapitalize(parsed);
1029
+ return JSON.stringify(capitalized);
1030
+ } catch (error) {
1031
+ return capitalizeString(input);
1032
+ }
1033
+ };
1034
+
1035
+ // src/utils/filters/date.ts
1036
+ import dayjs from "dayjs";
1037
+ import isoWeek from "dayjs/plugin/isoWeek";
1038
+ import weekOfYear from "dayjs/plugin/weekOfYear";
1039
+ import customParseFormat from "dayjs/plugin/customParseFormat";
1040
+ import advancedFormat from "dayjs/plugin/advancedFormat";
1041
+ dayjs.extend(customParseFormat);
1042
+ dayjs.extend(isoWeek);
1043
+ dayjs.extend(weekOfYear);
1044
+ dayjs.extend(advancedFormat);
1045
+ var date = (str, param) => {
1046
+ if (str === "") {
1047
+ return str;
1048
+ }
1049
+ const inputDate = str === "now" ? /* @__PURE__ */ new Date() : str;
1050
+ if (!param) {
1051
+ return dayjs(inputDate).format("YYYY-MM-DD");
1052
+ }
1053
+ param = param.replace(/^\((.*)\)$/, "$1");
1054
+ const params = param.split(/,(?=(?:(?:[^"']*["'][^"']*["'])*[^"']*$))/).map((p) => {
1055
+ return p.trim().replace(/^(['"])([\s\S]*)\1$/, "$2");
1056
+ });
1057
+ const [outputFormat, inputFormat] = params;
1058
+ let date2;
1059
+ if (inputFormat) {
1060
+ date2 = dayjs(inputDate, inputFormat, true);
1061
+ } else {
1062
+ date2 = dayjs(inputDate);
1063
+ }
1064
+ if (!date2.isValid()) {
1065
+ console.error("Invalid date for date filter:", str);
1066
+ return str;
1067
+ }
1068
+ return date2.format(outputFormat);
1069
+ };
1070
+
1071
+ // src/utils/filters/date_modify.ts
1072
+ import dayjs2 from "dayjs";
1073
+ import isoWeek2 from "dayjs/plugin/isoWeek";
1074
+ import weekOfYear2 from "dayjs/plugin/weekOfYear";
1075
+ import customParseFormat2 from "dayjs/plugin/customParseFormat";
1076
+ import advancedFormat2 from "dayjs/plugin/advancedFormat";
1077
+ dayjs2.extend(customParseFormat2);
1078
+ dayjs2.extend(isoWeek2);
1079
+ dayjs2.extend(weekOfYear2);
1080
+ dayjs2.extend(advancedFormat2);
1081
+ var validUnits = ["year", "years", "month", "months", "week", "weeks", "day", "days", "hour", "hours", "minute", "minutes", "second", "seconds"];
1082
+ var validateDateModifyParams = (param) => {
1083
+ if (!param) {
1084
+ return { valid: false, error: 'requires a modifier (e.g., date_modify:"+1 day", "-2 weeks")' };
1085
+ }
1086
+ let cleanParam = param.replace(/^\((.*)\)$/, "$1");
1087
+ cleanParam = cleanParam.replace(/^(['"])([\s\S]*)\1$/, "$2").trim();
1088
+ const regex = /^([+-])\s*(\d+)\s*(\w+)s?$/;
1089
+ const match = cleanParam.match(regex);
1090
+ if (!match) {
1091
+ return { valid: false, error: 'invalid format. Use "+1 day", "-2 weeks", etc.' };
1092
+ }
1093
+ const [, , , unit] = match;
1094
+ const normalizedUnit = unit.toLowerCase().replace(/s$/, "");
1095
+ if (!validUnits.some((u) => u.replace(/s$/, "") === normalizedUnit)) {
1096
+ return { valid: false, error: `invalid unit "${unit}". Use year, month, week, day, hour, minute, or second` };
1097
+ }
1098
+ return { valid: true };
1099
+ };
1100
+ var date_modify = (str, param) => {
1101
+ if (!param) {
1102
+ console.error("date_modify filter requires a parameter");
1103
+ return str;
1104
+ }
1105
+ if (str === "") {
1106
+ return str;
1107
+ }
1108
+ let date2 = dayjs2(str);
1109
+ if (!date2.isValid()) {
1110
+ console.error("Invalid date for date_modify filter:", str);
1111
+ return str;
1112
+ }
1113
+ param = param.replace(/^\((.*)\)$/, "$1");
1114
+ param = param.replace(/^(['"])([\s\S]*)\1$/, "$2").trim();
1115
+ const regex = /^([+-])\s*(\d+)\s*(\w+)s?$/;
1116
+ const match = param.match(regex);
1117
+ if (!match) {
1118
+ console.error("Invalid format for date_modify filter:", param);
1119
+ return str;
1120
+ }
1121
+ const [, operation, amount, unit] = match;
1122
+ const numericAmount = parseInt(amount, 10);
1123
+ if (operation === "+") {
1124
+ date2 = date2.add(numericAmount, unit);
1125
+ } else {
1126
+ date2 = date2.subtract(numericAmount, unit);
1127
+ }
1128
+ return date2.format("YYYY-MM-DD");
1129
+ };
1130
+
1131
+ // src/utils/filters/decode_uri.ts
1132
+ var decode_uri = (str) => {
1133
+ try {
1134
+ return decodeURIComponent(str);
1135
+ } catch {
1136
+ return str;
1137
+ }
1138
+ };
1139
+
1140
+ // src/utils/filters/first.ts
1141
+ var first = (str) => {
1142
+ if (str === "") {
1143
+ return str;
1144
+ }
1145
+ try {
1146
+ const array = JSON.parse(str);
1147
+ if (Array.isArray(array) && array.length > 0) {
1148
+ return array[0].toString();
1149
+ }
1150
+ } catch (error) {
1151
+ console.error("Error parsing JSON in first filter:", error);
1152
+ }
1153
+ return str;
1154
+ };
1155
+
1156
+ // src/utils/filters/footnote.ts
1157
+ var footnote = (str) => {
1158
+ if (str === "") {
1159
+ return str;
1160
+ }
1161
+ try {
1162
+ const data = JSON.parse(str);
1163
+ if (Array.isArray(data)) {
1164
+ return data.map((item, index) => `[^${index + 1}]: ${item}`).join("\n\n");
1165
+ } else if (typeof data === "object" && data !== null) {
1166
+ return Object.entries(data).map(([key, value]) => {
1167
+ const footnoteId = key.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
1168
+ return `[^${footnoteId}]: ${value}`;
1169
+ }).join("\n\n");
1170
+ }
1171
+ } catch (error) {
1172
+ console.error("Error parsing JSON in footnote filter:", error);
1173
+ }
1174
+ return str;
1175
+ };
1176
+
1177
+ // src/utils/filters/strip_md.ts
1178
+ var strip_md = (str) => {
1179
+ str = str.replace(/!\[([^\]]*)\]\([^\)]+\)/g, "");
1180
+ str = str.replace(/!\[\[([^\]]+)\]\]/g, "");
1181
+ str = str.replace(/\[([^\]]+)\]\([^\)]+\)/g, "$1");
1182
+ str = str.replace(/https?:\/\/\S+/g, "");
1183
+ str = str.replace(/(\*\*|__)(.*?)\1/g, "$2");
1184
+ str = str.replace(/(\*|_)(.*?)\1/g, "$2");
1185
+ str = str.replace(/==(.*?)==/g, "$1");
1186
+ str = str.replace(/^#+\s+/gm, "");
1187
+ str = str.replace(/`([^`]+)`/g, "$1");
1188
+ str = str.replace(/```[\s\S]*?```/g, "");
1189
+ str = str.replace(/~~(.*?)~~/g, "$1");
1190
+ str = str.replace(/^[-*+] (\[[x ]\] )?/gm, "");
1191
+ str = str.replace(/^([-*_]){3,}\s*$/gm, "");
1192
+ str = str.replace(/^>\s+/gm, "");
1193
+ str = str.replace(/\|.*\|/g, "");
1194
+ str = str.replace(/([~^])(\w+)\1/g, "$2");
1195
+ str = str.replace(/:[a-z_]+:/g, "");
1196
+ str = str.replace(/<[^>]+>/g, "");
1197
+ str = str.replace(/\[\s*\]/g, "");
1198
+ str = str.replace(/\[\^[^\]]+\]/g, "");
1199
+ str = str.replace(/^\*\[[^\]]+\]:.+$/gm, "");
1200
+ str = str.replace(/\[\[([^\]|]+)\|?([^\]]*)\]\]/g, (match, p1, p2) => p2 || p1);
1201
+ str = str.replace(/\n{3,}/g, "\n\n");
1202
+ str = str.trim();
1203
+ return str;
1204
+ };
1205
+
1206
+ // src/utils/filters/fragment_link.ts
1207
+ var fragment_link = (str, param) => {
1208
+ if (!param || !str.trim()) {
1209
+ return [str];
1210
+ }
1211
+ const match = param.match(/^(.*?):?((https?:\/\/|file:\/\/).*$)/);
1212
+ const linktext = String(
1213
+ match?.[1]?.trim().replace(/(['"])/g, "") || "link"
1214
+ );
1215
+ const currentUrl = String(match?.[2] || param);
1216
+ const extractTextFragmentParts = (selectedText) => {
1217
+ const text = strip_md(selectedText);
1218
+ const words = text.split(/\s+/).filter(Boolean);
1219
+ if (words.length > 10) {
1220
+ const start = words.slice(0, 5).join(" ");
1221
+ const end = words.slice(-5).join(" ");
1222
+ return { start, end };
1223
+ } else {
1224
+ const start = words.join(" ");
1225
+ return { start };
1226
+ }
1227
+ };
1228
+ const createTextFragmentUrl = (selectedText) => {
1229
+ const { start, end } = extractTextFragmentParts(selectedText);
1230
+ const encodedEnd = end ? "," + encodeURIComponent(end) : "";
1231
+ const textFragment = encodeURIComponent(start) + encodedEnd;
1232
+ return "#:~:text=" + textFragment;
1233
+ };
1234
+ try {
1235
+ const data = JSON.parse(str);
1236
+ if (Array.isArray(data)) {
1237
+ return data.map((item) => {
1238
+ if (typeof item === "object" && item !== null && "text" in item) {
1239
+ return {
1240
+ ...item,
1241
+ text: `${item.text} [${linktext}](${currentUrl}${createTextFragmentUrl(
1242
+ item.text
1243
+ )})`
1244
+ };
1245
+ } else {
1246
+ return `${item} [${linktext}](${currentUrl}${createTextFragmentUrl(
1247
+ String(item)
1248
+ )})`;
1249
+ }
1250
+ });
1251
+ } else if (typeof data === "object" && data !== null) {
1252
+ return Object.entries(data).map(
1253
+ ([key, value]) => `${value} [${linktext}](${currentUrl}${createTextFragmentUrl(
1254
+ String(value)
1255
+ )})`
1256
+ );
1257
+ } else if (typeof data === "string") {
1258
+ return [
1259
+ `${data} [${linktext}](${currentUrl}${createTextFragmentUrl(
1260
+ data
1261
+ )})`
1262
+ ];
1263
+ }
1264
+ } catch (error) {
1265
+ console.error("Fragment filter error:", error);
1266
+ }
1267
+ return [str];
1268
+ };
1269
+
1270
+ // src/utils/filters/html_to_json.ts
1271
+ var html_to_json = (input) => {
1272
+ const parseNode2 = (node) => {
1273
+ if (node.nodeType === Node.TEXT_NODE) {
1274
+ const text = node.textContent?.trim();
1275
+ return text ? { type: "text", content: text } : null;
1276
+ }
1277
+ if (node.nodeType === Node.ELEMENT_NODE) {
1278
+ const element = node;
1279
+ const result = {
1280
+ type: "element",
1281
+ tag: element.tagName.toLowerCase()
1282
+ };
1283
+ const attributes = Array.from(element.attributes).reduce((acc, attr) => {
1284
+ acc[attr.name] = attr.value;
1285
+ return acc;
1286
+ }, {});
1287
+ if (Object.keys(attributes).length > 0) {
1288
+ result.attributes = attributes;
1289
+ }
1290
+ const children = Array.from(element.childNodes).map(parseNode2).filter((child) => child !== null);
1291
+ if (children.length > 0) {
1292
+ result.children = children;
1293
+ }
1294
+ return result;
1295
+ }
1296
+ return null;
1297
+ };
1298
+ try {
1299
+ const parser = new DOMParser();
1300
+ const doc = parser.parseFromString(input, "text/html");
1301
+ const bodyChildren = Array.from(doc.body.childNodes).map(parseNode2).filter((child) => child !== null);
1302
+ const result = bodyChildren.length === 1 ? bodyChildren[0] : bodyChildren;
1303
+ return JSON.stringify(result);
1304
+ } catch (error) {
1305
+ console.error("Error in html_to_json filter:", error);
1306
+ return input;
1307
+ }
1308
+ };
1309
+
1310
+ // src/utils/string-utils.ts
1311
+ function escapeMarkdown(str) {
1312
+ return str.replace(/([[\]])/g, "\\$1");
1313
+ }
1314
+ function escapeDoubleQuotes(str) {
1315
+ return str.replace(/"/g, '\\"');
1316
+ }
1317
+ function sanitizeFileName(fileName) {
1318
+ const platform = navigator.userAgentData?.platform || navigator.platform || "";
1319
+ const isWindows = /win/i.test(platform);
1320
+ const isMac = /mac/i.test(platform);
1321
+ let sanitized = fileName.replace(/[#|\^\[\]]/g, "");
1322
+ if (isWindows) {
1323
+ sanitized = sanitized.replace(/[<>:"\/\\?*\x00-\x1F]/g, "").replace(/^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i, "_$1$2").replace(/[\s.]+$/, "");
1324
+ } else if (isMac) {
1325
+ sanitized = sanitized.replace(/[\/:\x00-\x1F]/g, "").replace(/^\./, "_");
1326
+ } else {
1327
+ sanitized = sanitized.replace(/[<>:"\/\\|?*\x00-\x1F]/g, "").replace(/^\./, "_");
1328
+ }
1329
+ sanitized = sanitized.replace(/^\.+/, "").trim().slice(0, 245);
1330
+ if (sanitized.length === 0) {
1331
+ sanitized = "Untitled";
1332
+ }
1333
+ return sanitized;
1334
+ }
1335
+ function getDomain(url) {
1336
+ try {
1337
+ const urlObj = new URL(url);
1338
+ const hostname = urlObj.hostname;
1339
+ if (hostname === "localhost" || hostname === "127.0.0.1" || hostname.match(/^(\d{1,3}\.){3}\d{1,3}$/)) {
1340
+ return hostname;
1341
+ }
1342
+ const hostParts = hostname.split(".");
1343
+ if (hostParts.length > 2) {
1344
+ const lastTwo = hostParts.slice(-2).join(".");
1345
+ if (lastTwo.match(/^(co|com|org|net|edu|gov|mil)\.[a-z]{2}$/)) {
1346
+ return hostParts.slice(-3).join(".");
1347
+ }
1348
+ }
1349
+ return hostParts.slice(-2).join(".");
1350
+ } catch (error) {
1351
+ console.warn("Invalid URL:", url);
1352
+ return "";
1353
+ }
1354
+ }
1355
+
1356
+ // src/utils/filters/image.ts
1357
+ var image = (str, param) => {
1358
+ if (!str.trim()) {
1359
+ return str;
1360
+ }
1361
+ let altText = "";
1362
+ if (param) {
1363
+ param = param.replace(/^\((.*)\)$/, "$1");
1364
+ altText = param.replace(/^(['"])([\s\S]*)\1$/, "$2");
1365
+ }
1366
+ try {
1367
+ const data = JSON.parse(str);
1368
+ const processObject = (obj) => {
1369
+ return Object.entries(obj).map(([key, value]) => {
1370
+ if (typeof value === "object" && value !== null) {
1371
+ return processObject(value);
1372
+ }
1373
+ return `![${escapeMarkdown(String(value))}](${escapeMarkdown(key)})`;
1374
+ }).flat();
1375
+ };
1376
+ if (Array.isArray(data)) {
1377
+ return data.map((item) => {
1378
+ if (typeof item === "object" && item !== null) {
1379
+ return processObject(item);
1380
+ }
1381
+ return item ? `![${altText}](${escapeMarkdown(String(item))})` : "";
1382
+ }).flat();
1383
+ } else if (typeof data === "object" && data !== null) {
1384
+ return processObject(data);
1385
+ }
1386
+ } catch (error) {
1387
+ return `![${altText}](${escapeMarkdown(str)})`;
1388
+ }
1389
+ return str;
1390
+ };
1391
+
1392
+ // src/utils/filters/join.ts
1393
+ var join = (str, param) => {
1394
+ if (!str || str === "undefined" || str === "null") {
1395
+ return "";
1396
+ }
1397
+ let array;
1398
+ try {
1399
+ array = JSON.parse(str);
1400
+ } catch (error) {
1401
+ console.error("Error parsing JSON in join filter:", error);
1402
+ return str;
1403
+ }
1404
+ if (!Array.isArray(array)) {
1405
+ return str;
1406
+ }
1407
+ let separator = ",";
1408
+ if (param) {
1409
+ separator = param.replace(/^(['"])([\s\S]*)\1$/, "$2");
1410
+ separator = separator.replace(/\\n/g, "\n");
1411
+ }
1412
+ return array.join(separator);
1413
+ };
1414
+
1415
+ // src/utils/filters/kebab.ts
1416
+ var kebab = (str) => str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
1417
+
1418
+ // src/utils/filters/last.ts
1419
+ var last = (str) => {
1420
+ if (str === "") {
1421
+ return str;
1422
+ }
1423
+ try {
1424
+ const array = JSON.parse(str);
1425
+ if (Array.isArray(array) && array.length > 0) {
1426
+ return array[array.length - 1].toString();
1427
+ }
1428
+ } catch (error) {
1429
+ console.error("Error parsing JSON in last filter:", error);
1430
+ }
1431
+ return str;
1432
+ };
1433
+
1434
+ // src/utils/filters/list.ts
1435
+ var validListTypes = ["numbered", "task", "numbered-task"];
1436
+ var validateListParams = (param) => {
1437
+ if (!param) {
1438
+ return { valid: true };
1439
+ }
1440
+ if (!validListTypes.includes(param)) {
1441
+ return {
1442
+ valid: false,
1443
+ error: `invalid list type "${param}". Use "numbered", "task", or "numbered-task"`
1444
+ };
1445
+ }
1446
+ return { valid: true };
1447
+ };
1448
+ var list = (input, param) => {
1449
+ if (input === "") {
1450
+ return input;
1451
+ }
1452
+ const processListItem = (item, type, depth = 0) => {
1453
+ const indent = " ".repeat(depth);
1454
+ let prefix;
1455
+ switch (type) {
1456
+ case "numbered":
1457
+ prefix = "1. ";
1458
+ break;
1459
+ case "task":
1460
+ prefix = "- [ ] ";
1461
+ break;
1462
+ case "numbered-task":
1463
+ prefix = "1. [ ] ";
1464
+ break;
1465
+ default:
1466
+ prefix = "- ";
1467
+ }
1468
+ if (Array.isArray(item)) {
1469
+ return processArray(item, type, depth + 1);
1470
+ }
1471
+ return `${indent}${prefix}${item}`;
1472
+ };
1473
+ const processArray = (arr, type, depth = 0) => {
1474
+ return arr.map((item, index) => {
1475
+ let itemType = type;
1476
+ if (type === "numbered" || type === "numbered-task") {
1477
+ const number = index + 1;
1478
+ return processListItem(item, itemType, depth).replace(/^\d+/, number.toString());
1479
+ }
1480
+ return processListItem(item, itemType, depth);
1481
+ }).join("\n");
1482
+ };
1483
+ const determineListType = (param2) => {
1484
+ switch (param2) {
1485
+ case "numbered":
1486
+ return "numbered";
1487
+ case "task":
1488
+ return "task";
1489
+ case "numbered-task":
1490
+ return "numbered-task";
1491
+ default:
1492
+ return "bullet";
1493
+ }
1494
+ };
1495
+ try {
1496
+ const parsedInput = typeof input === "string" ? JSON.parse(input) : input;
1497
+ if (Array.isArray(parsedInput)) {
1498
+ const listType = determineListType(param);
1499
+ return processArray(parsedInput, listType);
1500
+ }
1501
+ return processArray([parsedInput], determineListType(param));
1502
+ } catch (error) {
1503
+ console.error("Error processing list filter:", error);
1504
+ return processListItem(input, determineListType(param));
1505
+ }
1506
+ };
1507
+
1508
+ // src/utils/filters/link.ts
1509
+ var link = (str, param) => {
1510
+ if (!str.trim()) {
1511
+ return str;
1512
+ }
1513
+ let linkText = "link";
1514
+ if (param) {
1515
+ param = param.replace(/^\((.*)\)$/, "$1");
1516
+ linkText = param.replace(/^(['"])([\s\S]*)\1$/, "$2");
1517
+ }
1518
+ const encodeUrl = (url) => {
1519
+ return url.replace(/ /g, "%20");
1520
+ };
1521
+ try {
1522
+ const data = JSON.parse(str);
1523
+ const processObject = (obj) => {
1524
+ return Object.entries(obj).map(([key, value]) => {
1525
+ if (typeof value === "object" && value !== null) {
1526
+ return processObject(value);
1527
+ }
1528
+ return `[${escapeMarkdown(String(value))}](${encodeUrl(escapeMarkdown(key))})`;
1529
+ }).flat();
1530
+ };
1531
+ if (Array.isArray(data)) {
1532
+ const result = data.map((item) => {
1533
+ if (typeof item === "object" && item !== null) {
1534
+ return processObject(item);
1535
+ }
1536
+ return item ? `[${linkText}](${encodeUrl(escapeMarkdown(String(item)))})` : "";
1537
+ });
1538
+ return result.join("\n");
1539
+ } else if (typeof data === "object" && data !== null) {
1540
+ return processObject(data).join("\n");
1541
+ }
1542
+ } catch (error) {
1543
+ return `[${linkText}](${encodeUrl(escapeMarkdown(str))})`;
1544
+ }
1545
+ return str;
1546
+ };
1547
+
1548
+ // src/utils/filters/length.ts
1549
+ var length = (str) => {
1550
+ try {
1551
+ const parsed = JSON.parse(str);
1552
+ if (Array.isArray(parsed)) {
1553
+ return parsed.length.toString();
1554
+ } else if (typeof parsed === "object" && parsed !== null) {
1555
+ return Object.keys(parsed).length.toString();
1556
+ }
1557
+ return str.length.toString();
1558
+ } catch (error) {
1559
+ return str.length.toString();
1560
+ }
1561
+ };
1562
+
1563
+ // src/utils/filters/lower.ts
1564
+ var lower = (input) => {
1565
+ const toLowerCase = (str) => {
1566
+ return str.toLocaleLowerCase();
1567
+ };
1568
+ if (Array.isArray(input)) {
1569
+ return input.map(toLowerCase);
1570
+ } else {
1571
+ return toLowerCase(input);
1572
+ }
1573
+ };
1574
+
1575
+ // src/utils/filters/map.ts
1576
+ var validateMapParams = (param) => {
1577
+ if (!param) {
1578
+ return { valid: false, error: "requires an arrow function (e.g., map:x => x.name)" };
1579
+ }
1580
+ const match = param.match(/^\s*(\w+)\s*=>\s*(.+)$/);
1581
+ if (!match) {
1582
+ return { valid: false, error: "invalid syntax. Use arrow function format (e.g., x => x.name)" };
1583
+ }
1584
+ return { valid: true };
1585
+ };
1586
+ var map = (str, param) => {
1587
+ debugLog("Map", "map input:", str);
1588
+ debugLog("Map", "map param:", param);
1589
+ let array;
1590
+ try {
1591
+ array = JSON.parse(str);
1592
+ debugLog("Map", "Parsed array:", JSON.stringify(array, null, 2));
1593
+ } catch (error) {
1594
+ debugLog("Map", "Parsing failed, using input as single item");
1595
+ array = [str];
1596
+ }
1597
+ if (Array.isArray(array) && param) {
1598
+ const match = param.match(/^\s*(\w+)\s*=>\s*(.+)$/);
1599
+ if (!match) {
1600
+ debugLog("Map", "Invalid arrow function syntax");
1601
+ return str;
1602
+ }
1603
+ const [, argName, expression] = match;
1604
+ debugLog("Map", "Arrow function parsed:", { argName, expression });
1605
+ const mappedArray = array.map((item, index) => {
1606
+ debugLog("Map", `Processing item ${index}:`, JSON.stringify(item, null, 2));
1607
+ let expr = expression.trim();
1608
+ if (expr.startsWith("(") && expr.endsWith(")")) {
1609
+ expr = expr.slice(1, -1).trim();
1610
+ }
1611
+ if (expr.startsWith("{") && expr.endsWith("}") || expr.startsWith('"') && expr.endsWith('"') || expr.startsWith("'") && expr.endsWith("'")) {
1612
+ const mappedItem = {};
1613
+ if (expr.startsWith("{")) {
1614
+ const assignments = expr.match(/\{(.+)\}/)?.[1].split(",") || [];
1615
+ assignments.forEach((assignment) => {
1616
+ const [key, value] = assignment.split(":").map((s) => s.trim());
1617
+ const cleanKey = key.replace(/^['"](.+)['"]$/, "$1");
1618
+ debugLog("Map", "Processing assignment:", { cleanKey, value });
1619
+ const cleanValue = evaluateExpression(value, item, argName);
1620
+ debugLog("Map", "Cleaned value:", cleanValue);
1621
+ mappedItem[cleanKey] = cleanValue;
1622
+ debugLog("Map", `Assigned ${cleanKey}:`, mappedItem[cleanKey]);
1623
+ });
1624
+ } else {
1625
+ const stringLiteral = expr.slice(1, -1);
1626
+ return stringLiteral.replace(new RegExp(`\\$\\{${argName}\\}`, "g"), item);
1627
+ }
1628
+ debugLog("Map", "Mapped item:", mappedItem);
1629
+ return mappedItem;
1630
+ } else {
1631
+ return evaluateExpression(expression, item, argName);
1632
+ }
1633
+ });
1634
+ debugLog("Map", "Mapped array:", JSON.stringify(mappedArray, null, 2));
1635
+ return JSON.stringify(mappedArray);
1636
+ }
1637
+ debugLog("Map", "map output (unchanged):", str);
1638
+ return str;
1639
+ };
1640
+ function evaluateExpression(expression, item, argName) {
1641
+ if (typeof item === "string") {
1642
+ return item;
1643
+ }
1644
+ const result = expression.replace(new RegExp(`${argName}\\.([\\w.\\[\\]]+)`, "g"), (_, prop) => {
1645
+ const value = getNestedProperty(item, prop);
1646
+ debugLog("Map", `Replacing ${argName}.${prop} with:`, value);
1647
+ return JSON.stringify(value);
1648
+ });
1649
+ try {
1650
+ return JSON.parse(result);
1651
+ } catch {
1652
+ return result.replace(/^["'](.+)["']$/, "$1");
1653
+ }
1654
+ }
1655
+ function getNestedProperty(obj, path) {
1656
+ debugLog("Map", "Getting nested property:", { obj: JSON.stringify(obj), path });
1657
+ const result = path.split(/[\.\[\]]/).filter(Boolean).reduce((current, key) => {
1658
+ if (current && Array.isArray(current) && /^\d+$/.test(key)) {
1659
+ return current[parseInt(key, 10)];
1660
+ }
1661
+ return current && current[key] !== void 0 ? current[key] : void 0;
1662
+ }, obj);
1663
+ debugLog("Map", "Nested property result:", result);
1664
+ return result;
1665
+ }
1666
+
1667
+ // src/utils/filters/markdown.ts
1668
+ import { createMarkdownContent } from "defuddle/full";
1669
+ var markdown = (str, param) => {
1670
+ const baseUrl = param || "about:blank";
1671
+ try {
1672
+ return createMarkdownContent(str, baseUrl);
1673
+ } catch (error) {
1674
+ console.error("Error in createMarkdownContent:", error);
1675
+ return str;
1676
+ }
1677
+ };
1678
+
1679
+ // src/utils/filters/merge.ts
1680
+ var merge = (str, param) => {
1681
+ if (!str || str === "undefined" || str === "null") {
1682
+ return "[]";
1683
+ }
1684
+ let array;
1685
+ try {
1686
+ array = JSON.parse(str);
1687
+ } catch (error) {
1688
+ console.error("Error parsing JSON in merge filter:", error);
1689
+ return str;
1690
+ }
1691
+ if (!Array.isArray(array)) {
1692
+ array = [str];
1693
+ }
1694
+ if (!param) {
1695
+ return JSON.stringify(array);
1696
+ }
1697
+ param = param.replace(/^\((.*)\)$/, "$1");
1698
+ try {
1699
+ const additionalItems = param.match(/(?:[^,"']+|"[^"]*"|'[^']*')+/g) || [];
1700
+ const processedItems = additionalItems.map((item) => {
1701
+ item = item.trim();
1702
+ return item.replace(/^(['"])([\s\S]*)\1$/, "$2");
1703
+ });
1704
+ return JSON.stringify([...array, ...processedItems]);
1705
+ } catch (error) {
1706
+ console.error("Error processing parameters in merge filter:", error);
1707
+ return JSON.stringify(array);
1708
+ }
1709
+ };
1710
+
1711
+ // src/utils/filters/nth.ts
1712
+ var validateNthParams = (param) => {
1713
+ if (!param) {
1714
+ return { valid: true };
1715
+ }
1716
+ if (param.includes(":")) {
1717
+ const [positions, basis] = param.split(":").map((p) => p.trim());
1718
+ const nthValues = positions.split(",").map((n) => parseInt(n.trim(), 10));
1719
+ const basisSize = parseInt(basis, 10);
1720
+ if (nthValues.some((n) => isNaN(n) || n < 1)) {
1721
+ return { valid: false, error: "positions must be positive numbers (e.g., nth:1,2,3:7)" };
1722
+ }
1723
+ if (isNaN(basisSize) || basisSize < 1) {
1724
+ return { valid: false, error: "basis must be a positive number (e.g., nth:1,2,3:7)" };
1725
+ }
1726
+ return { valid: true };
1727
+ }
1728
+ const expr = param.trim();
1729
+ if (/^\d+$/.test(expr)) {
1730
+ return { valid: true };
1731
+ }
1732
+ if (/^\d+n$/.test(expr)) {
1733
+ return { valid: true };
1734
+ }
1735
+ if (/^n\+\d+$/.test(expr)) {
1736
+ return { valid: true };
1737
+ }
1738
+ return { valid: false, error: "invalid syntax. Use number (2), multiplier (5n), offset (n+7), or basis (1,2:5)" };
1739
+ };
1740
+ var nth = (str, params) => {
1741
+ if (!str || str === "undefined" || str === "null") {
1742
+ return str;
1743
+ }
1744
+ try {
1745
+ const data = JSON.parse(str);
1746
+ if (!Array.isArray(data)) {
1747
+ return str;
1748
+ }
1749
+ if (!params) {
1750
+ return JSON.stringify(data);
1751
+ }
1752
+ if (params.includes(":")) {
1753
+ const [positions, basis] = params.split(":").map((p) => p.trim());
1754
+ const nthValues = positions.split(",").map((n) => parseInt(n.trim(), 10)).filter((n) => !isNaN(n) && n > 0);
1755
+ const basisSize = parseInt(basis, 10);
1756
+ return JSON.stringify(data.filter((_, index) => {
1757
+ const positionInGroup = index % basisSize + 1;
1758
+ return nthValues.includes(positionInGroup);
1759
+ }));
1760
+ }
1761
+ const nthExpression = params.trim();
1762
+ if (/^\d+$/.test(nthExpression)) {
1763
+ const position = parseInt(nthExpression, 10);
1764
+ return JSON.stringify(data.filter((_, index) => index + 1 === position));
1765
+ }
1766
+ if (/^\d+n$/.test(nthExpression)) {
1767
+ const multiplier = parseInt(nthExpression, 10);
1768
+ return JSON.stringify(data.filter((_, index) => {
1769
+ const position = index + 1;
1770
+ return position % multiplier === 0;
1771
+ }));
1772
+ }
1773
+ const nPlusBMatch = nthExpression.match(/^n\+(\d+)$/);
1774
+ if (nPlusBMatch) {
1775
+ const offset = parseInt(nPlusBMatch[1], 10);
1776
+ return JSON.stringify(data.filter((_, index) => {
1777
+ const position = index + 1;
1778
+ return position >= offset;
1779
+ }));
1780
+ }
1781
+ console.error("Invalid nth filter syntax:", params);
1782
+ return str;
1783
+ } catch (error) {
1784
+ console.error("Error in nth filter:", error);
1785
+ return str;
1786
+ }
1787
+ };
1788
+
1789
+ // src/utils/filters/number_format.ts
1790
+ var number_format = (input, param) => {
1791
+ const formatNumber = (num, decimals, decPoint, thousandsSep) => {
1792
+ const parts = num.toFixed(decimals).split(".");
1793
+ parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandsSep);
1794
+ return parts.join(decPoint);
1795
+ };
1796
+ const processValue = (value, decimals, decPoint, thousandsSep) => {
1797
+ if (typeof value === "number" || typeof value === "string" && !isNaN(parseFloat(value))) {
1798
+ const num = typeof value === "string" ? parseFloat(value) : value;
1799
+ return formatNumber(num, decimals, decPoint, thousandsSep);
1800
+ } else if (Array.isArray(value)) {
1801
+ return value.map((item) => processValue(item, decimals, decPoint, thousandsSep));
1802
+ } else if (typeof value === "object" && value !== null) {
1803
+ const result = {};
1804
+ for (const [key, val] of Object.entries(value)) {
1805
+ result[key] = processValue(val, decimals, decPoint, thousandsSep);
1806
+ }
1807
+ return result;
1808
+ }
1809
+ return value;
1810
+ };
1811
+ const unescapeString = (str) => {
1812
+ return str.replace(/\\(.)/g, "$1");
1813
+ };
1814
+ try {
1815
+ let decimals = 0;
1816
+ let decPoint = ".";
1817
+ let thousandsSep = ",";
1818
+ if (param) {
1819
+ const cleanParam = param.replace(/^\((.*)\)$/, "$1");
1820
+ const params = [];
1821
+ let current = "";
1822
+ let inQuote = false;
1823
+ let escapeNext = false;
1824
+ for (let i = 0; i < cleanParam.length; i++) {
1825
+ const char = cleanParam[i];
1826
+ if (escapeNext) {
1827
+ current += char;
1828
+ escapeNext = false;
1829
+ } else if (char === "\\") {
1830
+ current += char;
1831
+ escapeNext = true;
1832
+ } else if (char === '"' && !inQuote) {
1833
+ inQuote = true;
1834
+ } else if (char === '"' && inQuote) {
1835
+ inQuote = false;
1836
+ } else if (char === "," && !inQuote) {
1837
+ params.push(current.trim());
1838
+ current = "";
1839
+ } else {
1840
+ current += char;
1841
+ }
1842
+ }
1843
+ if (current) {
1844
+ params.push(current.trim());
1845
+ }
1846
+ if (params.length >= 1) decimals = parseInt(params[0], 10);
1847
+ if (params.length >= 2) decPoint = unescapeString(params[1].replace(/^["'](.*)["']$/, "$1"));
1848
+ if (params.length >= 3) thousandsSep = unescapeString(params[2].replace(/^["'](.*)["']$/, "$1"));
1849
+ }
1850
+ if (isNaN(decimals)) decimals = 0;
1851
+ let parsedInput;
1852
+ try {
1853
+ parsedInput = JSON.parse(input);
1854
+ } catch {
1855
+ parsedInput = input;
1856
+ }
1857
+ const result = processValue(parsedInput, decimals, decPoint, thousandsSep);
1858
+ return typeof result === "string" ? result : JSON.stringify(result);
1859
+ } catch (error) {
1860
+ console.error("Error in number_format filter:", error);
1861
+ return input;
1862
+ }
1863
+ };
1864
+
1865
+ // src/utils/filters/object.ts
1866
+ var validObjectParams = ["array", "keys", "values"];
1867
+ var validateObjectParams = (param) => {
1868
+ if (!param) {
1869
+ return { valid: false, error: 'requires a parameter: "array", "keys", or "values"' };
1870
+ }
1871
+ if (!validObjectParams.includes(param)) {
1872
+ return {
1873
+ valid: false,
1874
+ error: `invalid parameter "${param}". Use "array", "keys", or "values"`
1875
+ };
1876
+ }
1877
+ return { valid: true };
1878
+ };
1879
+ var object = (str, param) => {
1880
+ try {
1881
+ const obj = JSON.parse(str);
1882
+ if (typeof obj === "object" && obj !== null) {
1883
+ switch (param) {
1884
+ case "array":
1885
+ return JSON.stringify(Object.entries(obj));
1886
+ case "keys":
1887
+ return JSON.stringify(Object.keys(obj));
1888
+ case "values":
1889
+ return JSON.stringify(Object.values(obj));
1890
+ default:
1891
+ return str;
1892
+ }
1893
+ }
1894
+ } catch (error) {
1895
+ console.error("Error parsing JSON for object filter:", error);
1896
+ }
1897
+ return str;
1898
+ };
1899
+
1900
+ // src/utils/filters/pascal.ts
1901
+ var pascal = (str) => str.replace(/[\s_-]+(.)/g, (_, c) => c.toUpperCase()).replace(/^(.)/, (c) => c.toUpperCase());
1902
+
1903
+ // src/utils/filters/reverse.ts
1904
+ var reverse = (str) => {
1905
+ if (!str || str === "undefined" || str === "null") {
1906
+ return "";
1907
+ }
1908
+ try {
1909
+ const value = JSON.parse(str);
1910
+ if (Array.isArray(value)) {
1911
+ return JSON.stringify(value.reverse());
1912
+ } else if (typeof value === "object" && value !== null) {
1913
+ const entries = Object.entries(value);
1914
+ const reversedEntries = entries.reverse();
1915
+ const reversedObject = Object.fromEntries(reversedEntries);
1916
+ return JSON.stringify(reversedObject);
1917
+ }
1918
+ } catch (error) {
1919
+ return str.split("").reverse().join("");
1920
+ }
1921
+ return str;
1922
+ };
1923
+
1924
+ // src/utils/filters/remove_attr.ts
1925
+ var remove_attr = (html, removeAttributes = "") => {
1926
+ if (!removeAttributes) {
1927
+ return html;
1928
+ }
1929
+ removeAttributes = removeAttributes.replace(/^\((.*)\)$/, "$1");
1930
+ removeAttributes = removeAttributes.replace(/^(['"])([\s\S]*)\1$/, "$2").replace(/\\(['"])/g, "$1");
1931
+ const removeAttributesList = removeAttributes.split(",").map((attr) => attr.trim().toLowerCase()).filter(Boolean);
1932
+ if (removeAttributesList.length === 0) {
1933
+ return html;
1934
+ }
1935
+ return html.replace(/<(\w+)\s+([^>]*?)>/g, (match, tag, attributesString) => {
1936
+ const attributeOrSlashRegex = /([a-zA-Z0-9_:-]+(?:\s*=\s*(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|[^'"\s>]+))?)|(\s*\/?\s*$)/g;
1937
+ const attributeNameExtractorRegex = /^([a-zA-Z0-9_:-]+)/;
1938
+ const elementsToKeep = [];
1939
+ const allMatches = Array.from(attributesString.matchAll(attributeOrSlashRegex));
1940
+ for (const currentMatch of allMatches) {
1941
+ const fullMatchedText = currentMatch[0];
1942
+ const attributePart = currentMatch[1];
1943
+ const slashPart = currentMatch[2];
1944
+ if (attributePart) {
1945
+ const nameMatch = attributePart.match(attributeNameExtractorRegex);
1946
+ if (nameMatch) {
1947
+ const attrName = nameMatch[0];
1948
+ const shouldRemove = removeAttributesList.includes(attrName.toLowerCase());
1949
+ if (!shouldRemove) {
1950
+ elementsToKeep.push(attributePart);
1951
+ }
1952
+ } else {
1953
+ elementsToKeep.push(attributePart);
1954
+ }
1955
+ } else if (slashPart && fullMatchedText && fullMatchedText.includes("/")) {
1956
+ elementsToKeep.push(fullMatchedText.trim());
1957
+ }
1958
+ }
1959
+ const cleanedAttributes = elementsToKeep.join(" ").trim();
1960
+ return cleanedAttributes ? `<${tag} ${cleanedAttributes}>` : `<${tag}>`;
1961
+ });
1962
+ };
1963
+
1964
+ // src/utils/filters/remove_html.ts
1965
+ var remove_html = (html, params = "") => {
1966
+ debugLog("RemoveHTML", "Input:", { html, params });
1967
+ params = params.replace(/^\((.*)\)$/, "$1");
1968
+ params = params.replace(/^(['"])([\s\S]*)\1$/, "$2").replace(/\\(['"])/g, "$1");
1969
+ const elementsToRemove = params.split(/,(?=(?:(?:[^"']*["'][^"']*["'])*[^"']*$))/).map((elem) => elem.trim()).filter(Boolean);
1970
+ debugLog("RemoveHTML", "Elements to remove:", elementsToRemove);
1971
+ if (elementsToRemove.length === 0) {
1972
+ return html;
1973
+ }
1974
+ const parser = new DOMParser();
1975
+ const doc = parser.parseFromString(html, "text/html");
1976
+ elementsToRemove.forEach((elem) => {
1977
+ let elements;
1978
+ if (elem.startsWith(".")) {
1979
+ elements = doc.querySelectorAll(`[class*="${elem.slice(1)}"]`);
1980
+ } else if (elem.startsWith("#")) {
1981
+ elements = doc.querySelectorAll(`[id="${elem.slice(1)}"]`);
1982
+ } else {
1983
+ elements = doc.getElementsByTagName(elem);
1984
+ }
1985
+ Array.from(elements).forEach((el) => el.parentNode?.removeChild(el));
1986
+ });
1987
+ const serializer = new XMLSerializer();
1988
+ let result = "";
1989
+ Array.from(doc.body.childNodes).forEach((node) => {
1990
+ if (node.nodeType === Node.ELEMENT_NODE) {
1991
+ result += serializer.serializeToString(node);
1992
+ } else if (node.nodeType === Node.TEXT_NODE) {
1993
+ result += node.textContent;
1994
+ }
1995
+ });
1996
+ debugLog("RemoveHTML", "Output:", result);
1997
+ return result;
1998
+ };
1999
+
2000
+ // src/utils/filters/remove_tags.ts
2001
+ var remove_tags = (html, removeTags = "") => {
2002
+ if (!removeTags) {
2003
+ return html;
2004
+ }
2005
+ removeTags = removeTags.replace(/^\((.*)\)$/, "$1");
2006
+ removeTags = removeTags.replace(/^(['"])([\s\S]*)\1$/, "$2").replace(/\\(['"])/g, "$1");
2007
+ const removeTagsList = removeTags.split(",").map((tag) => tag.trim()).filter(Boolean);
2008
+ if (removeTagsList.length === 0) {
2009
+ return html;
2010
+ }
2011
+ const escapedTags = removeTagsList.map((tag) => tag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
2012
+ const regex = new RegExp(`<\\/?(?:${escapedTags})\\b[^>]*>`, "gi");
2013
+ return html.replace(regex, "");
2014
+ };
2015
+
2016
+ // src/utils/filters/replace.ts
2017
+ var validateReplaceParams = (param) => {
2018
+ if (!param) {
2019
+ return { valid: false, error: 'requires search and replacement (e.g., replace:"old":"new")' };
2020
+ }
2021
+ const cleanParam = param.replace(/^\((.*)\)$/, "$1");
2022
+ const hasQuotedPair = /["'][^"']*["']\s*:\s*["'][^"']*["']/.test(cleanParam) || /["'][^"']*["']\s*:/.test(cleanParam) || // "search": with implicit empty replacement
2023
+ /\/[^/]+\/[gimsuy]*\s*:/.test(cleanParam);
2024
+ if (!hasQuotedPair) {
2025
+ return {
2026
+ valid: false,
2027
+ error: 'values must be quoted (e.g., replace:"old":"new" or replace:"text":"")'
2028
+ };
2029
+ }
2030
+ return { valid: true };
2031
+ };
2032
+ var replace = (str, param) => {
2033
+ if (!param) {
2034
+ return str;
2035
+ }
2036
+ param = param.replace(/^\((.*)\)$/, "$1");
2037
+ const replacements = [];
2038
+ const state = createParserState();
2039
+ for (let i = 0; i < param.length; i++) {
2040
+ const char = param[i];
2041
+ if (char === "," && !state.inQuote && !state.inRegex && state.curlyDepth === 0 && state.parenDepth === 0) {
2042
+ replacements.push(state.current.trim());
2043
+ state.current = "";
2044
+ } else {
2045
+ processCharacter(char, state);
2046
+ }
2047
+ }
2048
+ if (state.current) {
2049
+ replacements.push(state.current.trim());
2050
+ }
2051
+ return replacements.reduce((acc, replacement) => {
2052
+ let [search, replace2] = replacement.split(/(?<=[^\\]["']):(?=["'])/).map((p) => {
2053
+ return p.trim().replace(/^["']|["']$/g, "");
2054
+ });
2055
+ replace2 = replace2 || "";
2056
+ const regexInfo = parseRegexPattern(search);
2057
+ if (regexInfo) {
2058
+ try {
2059
+ replace2 = processEscapedCharacters(replace2);
2060
+ const regex = new RegExp(regexInfo.pattern, regexInfo.flags);
2061
+ return acc.replace(regex, replace2);
2062
+ } catch (error) {
2063
+ console.error("Invalid regex pattern:", error);
2064
+ return acc;
2065
+ }
2066
+ }
2067
+ search = processEscapedCharacters(search);
2068
+ replace2 = processEscapedCharacters(replace2);
2069
+ if (search === "|" || search === ":") {
2070
+ return acc.split(search).join(replace2);
2071
+ }
2072
+ if (search.includes("\n") || search.includes("\r") || search.includes(" ")) {
2073
+ return acc.split(search).join(replace2);
2074
+ }
2075
+ const searchRegex = new RegExp(search.replace(/([.*+?^${}()[\]\\])/g, "\\$1"), "g");
2076
+ return acc.replace(searchRegex, replace2);
2077
+ }, str);
2078
+ };
2079
+ function processEscapedCharacters(str) {
2080
+ return str.replace(/\\([nrt]|[^nrt])/g, (match, char) => {
2081
+ switch (char) {
2082
+ case "n":
2083
+ return "\n";
2084
+ case "r":
2085
+ return "\r";
2086
+ case "t":
2087
+ return " ";
2088
+ default:
2089
+ return char;
2090
+ }
2091
+ });
2092
+ }
2093
+
2094
+ // src/utils/filters/replace_tags.ts
2095
+ var replace_tags = (html, params = "") => {
2096
+ params = params.replace(/^\((.*)\)$/, "$1");
2097
+ params = params.replace(/^(['"])([\s\S]*)\1$/, "$2").replace(/\\(['"])/g, "$1");
2098
+ const transformations = params.split(/,(?=(?:(?:[^"']*["'][^"']*["'])*[^"']*$))/).map((transform) => transform.trim()).filter(Boolean);
2099
+ if (transformations.length === 0) {
2100
+ return html;
2101
+ }
2102
+ let result = html;
2103
+ transformations.forEach((transform) => {
2104
+ const [source, target] = transform.split(/(?<!\\)":"/).map((tag) => {
2105
+ return tag.trim().replace(/^["']|["']$/g, "").replace(/\\(.)/g, "$1");
2106
+ });
2107
+ if (!source) {
2108
+ return;
2109
+ }
2110
+ const openingPattern = new RegExp(`<${source}(\\s+[^>]*?)?>`, "g");
2111
+ const closingPattern = new RegExp(`</${source}>`, "g");
2112
+ result = result.replace(openingPattern, (match, attributes) => {
2113
+ return target ? `<${target}${attributes || ""}>` : "";
2114
+ }).replace(closingPattern, target ? `</${target}>` : "");
2115
+ });
2116
+ return result;
2117
+ };
2118
+
2119
+ // src/utils/filters/round.ts
2120
+ var validateRoundParams = (param) => {
2121
+ if (!param) {
2122
+ return { valid: true };
2123
+ }
2124
+ const num = parseInt(param, 10);
2125
+ if (isNaN(num)) {
2126
+ return { valid: false, error: "decimal places must be a number (e.g., round:2)" };
2127
+ }
2128
+ if (num < 0) {
2129
+ return { valid: false, error: "decimal places must be non-negative (e.g., round:2)" };
2130
+ }
2131
+ return { valid: true };
2132
+ };
2133
+ var round = (input, param) => {
2134
+ const roundNumber = (num, decimalPlaces) => {
2135
+ if (decimalPlaces === void 0) {
2136
+ return Math.round(num);
2137
+ }
2138
+ const factor = Math.pow(10, decimalPlaces);
2139
+ return Math.round(num * factor) / factor;
2140
+ };
2141
+ const processValue = (value, decimalPlaces) => {
2142
+ if (typeof value === "number") {
2143
+ return roundNumber(value, decimalPlaces);
2144
+ } else if (typeof value === "string") {
2145
+ const num = parseFloat(value);
2146
+ return isNaN(num) ? value : roundNumber(num, decimalPlaces).toString();
2147
+ } else if (Array.isArray(value)) {
2148
+ return value.map((item) => processValue(item, decimalPlaces));
2149
+ } else if (typeof value === "object" && value !== null) {
2150
+ const result = {};
2151
+ for (const [key, val] of Object.entries(value)) {
2152
+ result[key] = processValue(val, decimalPlaces);
2153
+ }
2154
+ return result;
2155
+ }
2156
+ return value;
2157
+ };
2158
+ try {
2159
+ const decimalPlaces = param ? parseInt(param, 10) : void 0;
2160
+ if (param !== void 0 && isNaN(Number(param))) {
2161
+ return input;
2162
+ }
2163
+ let parsedInput;
2164
+ try {
2165
+ parsedInput = JSON.parse(input);
2166
+ } catch {
2167
+ parsedInput = input;
2168
+ }
2169
+ const result = processValue(parsedInput, decimalPlaces);
2170
+ return typeof result === "string" ? result : JSON.stringify(result);
2171
+ } catch (error) {
2172
+ console.error("Error in round filter:", error);
2173
+ return input;
2174
+ }
2175
+ };
2176
+
2177
+ // src/utils/filters/safe_name.ts
2178
+ var validOsParams = ["windows", "mac", "linux"];
2179
+ var validateSafeNameParams = (param) => {
2180
+ if (!param) {
2181
+ return { valid: true };
2182
+ }
2183
+ if (!validOsParams.includes(param.toLowerCase().trim())) {
2184
+ return {
2185
+ valid: false,
2186
+ error: `invalid OS "${param}". Use "windows", "mac", or "linux"`
2187
+ };
2188
+ }
2189
+ return { valid: true };
2190
+ };
2191
+ var safe_name = (str, param) => {
2192
+ const os = param ? param.toLowerCase().trim() : "default";
2193
+ let sanitized = str;
2194
+ sanitized = sanitized.replace(/[#|\^\[\]]/g, "");
2195
+ switch (os) {
2196
+ case "windows":
2197
+ sanitized = sanitized.replace(/[<>:"\/\\|?*\x00-\x1F]/g, "").replace(/^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i, "_$1$2").replace(/[\s.]+$/, "");
2198
+ break;
2199
+ case "mac":
2200
+ sanitized = sanitized.replace(/[\/:\x00-\x1F]/g, "").replace(/^\./, "_");
2201
+ break;
2202
+ case "linux":
2203
+ sanitized = sanitized.replace(/[\/\x00-\x1F]/g, "").replace(/^\./, "_");
2204
+ break;
2205
+ default:
2206
+ sanitized = sanitized.replace(/[<>:"\/\\|?*:\x00-\x1F]/g, "").replace(/^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i, "_$1$2").replace(/[\s.]+$/, "").replace(/^\./, "_");
2207
+ break;
2208
+ }
2209
+ sanitized = sanitized.replace(/^\.+/, "").slice(0, 245);
2210
+ if (sanitized.length === 0) {
2211
+ sanitized = "Untitled";
2212
+ }
2213
+ return sanitized;
2214
+ };
2215
+
2216
+ // src/utils/filters/slice.ts
2217
+ var validateSliceParams = (param) => {
2218
+ if (!param) {
2219
+ return { valid: false, error: "requires at least a start index (e.g., slice:0,5)" };
2220
+ }
2221
+ const parts = param.split(",").map((p) => p.trim());
2222
+ if (parts.length > 2) {
2223
+ return { valid: false, error: "accepts at most 2 parameters: start and end" };
2224
+ }
2225
+ for (const part of parts) {
2226
+ if (part !== "" && isNaN(parseInt(part, 10))) {
2227
+ return { valid: false, error: `"${part}" is not a valid number` };
2228
+ }
2229
+ }
2230
+ return { valid: true };
2231
+ };
2232
+ var slice = (str, param) => {
2233
+ if (!param) {
2234
+ console.error("Slice filter requires parameters");
2235
+ return str;
2236
+ }
2237
+ if (str === "") {
2238
+ return str;
2239
+ }
2240
+ const [start, end] = param.split(",").map((p) => p.trim()).map((p) => {
2241
+ if (p === "") return void 0;
2242
+ const num = parseInt(p, 10);
2243
+ return isNaN(num) ? void 0 : num;
2244
+ });
2245
+ let value;
2246
+ try {
2247
+ value = JSON.parse(str);
2248
+ } catch (error) {
2249
+ if (str.startsWith("[") || str.startsWith("{")) {
2250
+ console.error("Error parsing JSON in slice filter:", error);
2251
+ }
2252
+ value = str;
2253
+ }
2254
+ if (Array.isArray(value)) {
2255
+ const slicedArray = value.slice(start, end);
2256
+ if (slicedArray.length === 1) {
2257
+ return slicedArray[0].toString();
2258
+ }
2259
+ return JSON.stringify(slicedArray);
2260
+ } else {
2261
+ const slicedString = str.slice(start, end);
2262
+ return slicedString;
2263
+ }
2264
+ };
2265
+
2266
+ // src/utils/filters/snake.ts
2267
+ var snake = (str) => str.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/[\s-]+/g, "_").toLowerCase();
2268
+
2269
+ // src/utils/filters/split.ts
2270
+ var split = (str, param) => {
2271
+ if (!param || param === "") {
2272
+ return JSON.stringify(str.split(""));
2273
+ }
2274
+ param = param.replace(/^\((.*)\)$/, "$1");
2275
+ param = param.replace(/^(['"])([\s\S]*)\1$/, "$2");
2276
+ const separator = param.length === 1 ? param : new RegExp(param);
2277
+ const result = str.split(separator);
2278
+ return JSON.stringify(result);
2279
+ };
2280
+
2281
+ // src/utils/filters/strip_attr.ts
2282
+ var strip_attr = (html, keepAttributes = "") => {
2283
+ keepAttributes = keepAttributes.replace(/^\((.*)\)$/, "$1");
2284
+ keepAttributes = keepAttributes.replace(/^(['"])([\s\S]*)\1$/, "$2").replace(/\\(['"])/g, "$1");
2285
+ const keepAttributesList = keepAttributes.split(",").map((attr) => attr.trim()).filter(Boolean);
2286
+ return html.replace(/<(\w+)\s+(?:[^>]*?)>/g, (match, tag) => {
2287
+ if (keepAttributesList.length === 0) {
2288
+ return `<${tag}>`;
2289
+ }
2290
+ const keepAttrs = keepAttributesList.map((attr) => {
2291
+ const escapedAttr = attr.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2292
+ const regex = new RegExp(`\\s${escapedAttr}\\s*=\\s*("[^"]*"|'[^']*')`, "i");
2293
+ const attrMatch = match.match(regex);
2294
+ return attrMatch ? attrMatch[0].trim() : "";
2295
+ }).filter(Boolean).join(" ");
2296
+ return keepAttrs ? `<${tag} ${keepAttrs}>` : `<${tag}>`;
2297
+ });
2298
+ };
2299
+
2300
+ // src/utils/filters/strip_tags.ts
2301
+ var strip_tags = (html, keepTags = "") => {
2302
+ keepTags = keepTags.replace(/^\((.*)\)$/, "$1");
2303
+ keepTags = keepTags.replace(/^(['"])([\s\S]*)\1$/, "$2").replace(/\\(['"])/g, "$1");
2304
+ const keepTagsList = keepTags.split(",").map((tag) => tag.trim()).filter(Boolean);
2305
+ let result;
2306
+ if (keepTagsList.length === 0) {
2307
+ result = html.replace(/<\/?[^>]+(>|$)/g, "");
2308
+ } else {
2309
+ const escapedTags = keepTagsList.map((tag) => tag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
2310
+ const regex = new RegExp(`<(?!\\/?(?:${escapedTags})\\b)[^>]+>`, "gi");
2311
+ result = html.replace(regex, "");
2312
+ }
2313
+ result = result.replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&ldquo;/g, '"').replace(/&rdquo;/g, '"').replace(/&lsquo;/g, "'").replace(/&rsquo;/g, "'").replace(/&mdash;/g, "\u2014").replace(/&ndash;/g, "\u2013").replace(/&hellip;/g, "\u2026").replace(/&#(\d+);/g, (match, dec) => String.fromCharCode(Number(dec))).replace(/&#x([0-9A-Fa-f]+);/g, (match, hex) => String.fromCharCode(parseInt(hex, 16)));
2314
+ result = result.replace(/\n{3,}/g, "\n\n");
2315
+ return result.trim();
2316
+ };
2317
+
2318
+ // src/utils/filters/table.ts
2319
+ var table = (str, params) => {
2320
+ if (!str || str === "undefined" || str === "null") {
2321
+ return str;
2322
+ }
2323
+ try {
2324
+ const data = JSON.parse(str);
2325
+ let customHeaders = [];
2326
+ if (params) {
2327
+ try {
2328
+ const headerStr = params.replace(/^\((.*)\)$/, "$1");
2329
+ customHeaders = headerStr.split(",").map(
2330
+ (header) => header.trim().replace(/^["'](.*)["']$/, "$1")
2331
+ );
2332
+ } catch (error) {
2333
+ console.error("Error parsing table headers:", error);
2334
+ }
2335
+ }
2336
+ const escapeCell = (cell) => cell.replace(/\|/g, "\\|");
2337
+ if (typeof data === "object" && data !== null && !Array.isArray(data)) {
2338
+ const entries = Object.entries(data);
2339
+ if (entries.length === 0) return str;
2340
+ const [[firstKey, firstValue], ...restEntries] = entries;
2341
+ let table2 = `| ${escapeCell(firstKey)} | ${escapeCell(String(firstValue))} |
2342
+ | - | - |
2343
+ `;
2344
+ restEntries.forEach(([key, value]) => {
2345
+ table2 += `| ${escapeCell(key)} | ${escapeCell(String(value))} |
2346
+ `;
2347
+ });
2348
+ return table2.trim();
2349
+ }
2350
+ if (Array.isArray(data) && data.length > 0 && typeof data[0] === "object" && data[0] !== null) {
2351
+ const headers = customHeaders.length > 0 ? customHeaders : Object.keys(data[0]);
2352
+ let table2 = `| ${headers.join(" | ")} |
2353
+ | ${headers.map(() => "-").join(" | ")} |
2354
+ `;
2355
+ data.forEach((row) => {
2356
+ table2 += `| ${headers.map((header) => escapeCell(String(row[header] || ""))).join(" | ")} |
2357
+ `;
2358
+ });
2359
+ return table2.trim();
2360
+ }
2361
+ if (Array.isArray(data) && data.length > 0 && Array.isArray(data[0])) {
2362
+ const maxColumns = Math.max(...data.map((row) => row.length));
2363
+ const headers = customHeaders.length > 0 ? customHeaders : Array(maxColumns).fill("");
2364
+ let table2 = `| ${headers.join(" | ")} |
2365
+ | ${headers.map(() => "-").join(" | ")} |
2366
+ `;
2367
+ data.forEach((row) => {
2368
+ const paddedRow = [...row, ...Array(maxColumns - row.length).fill("")];
2369
+ table2 += `| ${paddedRow.map((cell) => escapeCell(String(cell))).join(" | ")} |
2370
+ `;
2371
+ });
2372
+ return table2.trim();
2373
+ }
2374
+ if (Array.isArray(data)) {
2375
+ if (customHeaders.length > 0) {
2376
+ const numColumns = customHeaders.length;
2377
+ let table3 = `| ${customHeaders.join(" | ")} |
2378
+ | ${customHeaders.map(() => "-").join(" | ")} |
2379
+ `;
2380
+ for (let i = 0; i < data.length; i += numColumns) {
2381
+ const row = data.slice(i, i + numColumns);
2382
+ const paddedRow = [...row, ...Array(numColumns - row.length).fill("")];
2383
+ table3 += `| ${paddedRow.map((cell) => escapeCell(String(cell))).join(" | ")} |
2384
+ `;
2385
+ }
2386
+ return table3.trim();
2387
+ }
2388
+ let table2 = "| Value |\n| - |\n";
2389
+ data.forEach((item) => {
2390
+ table2 += `| ${escapeCell(String(item))} |
2391
+ `;
2392
+ });
2393
+ return table2.trim();
2394
+ }
2395
+ return str;
2396
+ } catch (error) {
2397
+ console.error("Error parsing JSON for table filter:", error);
2398
+ return str;
2399
+ }
2400
+ };
2401
+
2402
+ // src/utils/filters/template.ts
2403
+ var validateTemplateParams = (param) => {
2404
+ if (!param) {
2405
+ return { valid: false, error: 'requires a template string (e.g., template:"${name}")' };
2406
+ }
2407
+ return { valid: true };
2408
+ };
2409
+ var template = (input, param) => {
2410
+ debugLog("Template", "Template input:", input);
2411
+ debugLog("Template", "Template param:", param);
2412
+ if (!param) {
2413
+ debugLog("Template", "No param provided, returning input");
2414
+ return typeof input === "string" ? input : JSON.stringify(input);
2415
+ }
2416
+ param = param.replace(/^\((.*)\)$/, "$1");
2417
+ param = param.replace(/^(['"])([\s\S]*)\1$/, "$2");
2418
+ let obj = [];
2419
+ if (typeof input === "string") {
2420
+ try {
2421
+ obj = JSON.parse(input);
2422
+ debugLog("Template", "Parsed input:", obj);
2423
+ } catch (error) {
2424
+ debugLog("Template", "Parsing failed, using input as is");
2425
+ obj = [input];
2426
+ }
2427
+ } else {
2428
+ obj = input;
2429
+ }
2430
+ obj = Array.isArray(obj) ? obj : [obj];
2431
+ debugLog("Template", "Object to process:", obj);
2432
+ const result = obj.map((item) => replaceTemplateVariables(item, param)).join("\n\n");
2433
+ debugLog("Template", "Processing result:", result);
2434
+ return result;
2435
+ };
2436
+ function replaceTemplateVariables(obj, template2) {
2437
+ debugLog("Template", "Replacing template variables for:", obj);
2438
+ debugLog("Template", "Template:", template2);
2439
+ if (typeof obj === "string") {
2440
+ const strValue = obj;
2441
+ try {
2442
+ obj = parseObjectString(obj);
2443
+ debugLog("Template", "Parsed object:", obj);
2444
+ } catch (error) {
2445
+ debugLog("Template", "Failed to parse object string:", obj);
2446
+ }
2447
+ if (obj.str === void 0) {
2448
+ obj.str = strValue;
2449
+ }
2450
+ }
2451
+ let result = template2.replace(/\$\{([\w.]+)\}/g, (match, path) => {
2452
+ debugLog("Template", "Replacing:", match);
2453
+ const value = getNestedProperty2(obj, path);
2454
+ debugLog("Template", "Replaced with:", value);
2455
+ return value !== void 0 && value !== "undefined" ? value : "";
2456
+ });
2457
+ debugLog("Template", "Result after variable replacement:", result);
2458
+ result = result.replace(/\\n/g, "\n");
2459
+ debugLog("Template", "Result after newline replacement:", result);
2460
+ result = result.split("\n").filter((line) => line.trim() !== "").join("\n");
2461
+ debugLog("Template", "Result after empty line removal:", result);
2462
+ return result.trim();
2463
+ }
2464
+ function parseObjectString(str) {
2465
+ const obj = {};
2466
+ const regex = /(\w+):\s*("(?:\\.|[^"\\])*"|[^,}]+)/g;
2467
+ let match;
2468
+ while ((match = regex.exec(str)) !== null) {
2469
+ let [, key, value] = match;
2470
+ if (value.startsWith('"') && value.endsWith('"')) {
2471
+ value = value.slice(1, -1);
2472
+ }
2473
+ obj[key] = value === "undefined" ? void 0 : value;
2474
+ }
2475
+ return obj;
2476
+ }
2477
+ function getNestedProperty2(obj, path) {
2478
+ debugLog("Template", "Getting nested property:", { obj, path });
2479
+ const result = path.split(".").reduce((current, key) => {
2480
+ return current && typeof current === "object" ? current[key] : void 0;
2481
+ }, obj);
2482
+ debugLog("Template", "Nested property result:", result);
2483
+ return result;
2484
+ }
2485
+
2486
+ // src/utils/filters/title.ts
2487
+ var lowercaseWords = ["a", "an", "the", "and", "but", "or", "for", "nor", "on", "at", "to", "from", "by", "in", "of"];
2488
+ var title = (input, param) => {
2489
+ const toTitleCase = (str) => {
2490
+ return str.split(/\s+/).map((word, index) => {
2491
+ if (index !== 0 && lowercaseWords.includes(word.toLowerCase())) {
2492
+ return word.toLowerCase();
2493
+ }
2494
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
2495
+ }).join(" ");
2496
+ };
2497
+ const processValue = (value) => {
2498
+ if (typeof value === "string") {
2499
+ return toTitleCase(value);
2500
+ } else if (Array.isArray(value)) {
2501
+ return value.map(processValue);
2502
+ } else if (typeof value === "object" && value !== null) {
2503
+ const result = {};
2504
+ for (const [key, val] of Object.entries(value)) {
2505
+ result[toTitleCase(key)] = processValue(val);
2506
+ }
2507
+ return result;
2508
+ }
2509
+ return value;
2510
+ };
2511
+ try {
2512
+ const parsedInput = JSON.parse(input);
2513
+ const result = processValue(parsedInput);
2514
+ return JSON.stringify(result);
2515
+ } catch (error) {
2516
+ return processValue(input);
2517
+ }
2518
+ };
2519
+
2520
+ // src/utils/filters/trim.ts
2521
+ var trim = (str) => {
2522
+ return str.trim();
2523
+ };
2524
+
2525
+ // src/utils/filters/uncamel.ts
2526
+ var uncamel = (str) => {
2527
+ const spaced = str.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
2528
+ return spaced.replace(/([A-Z])([A-Z][a-z])/g, "$1 $2").toLowerCase();
2529
+ };
2530
+
2531
+ // src/utils/filters/unescape.ts
2532
+ var unescape = (str) => str.replace(/\\"/g, '"').replace(/\\n/g, "\n");
2533
+
2534
+ // src/utils/filters/unique.ts
2535
+ var unique = (input) => {
2536
+ try {
2537
+ const parsed = JSON.parse(input);
2538
+ if (Array.isArray(parsed)) {
2539
+ if (parsed.every((item) => typeof item !== "object")) {
2540
+ return JSON.stringify([...new Set(parsed)]);
2541
+ }
2542
+ const seen = /* @__PURE__ */ new Set();
2543
+ const uniqueArray = parsed.filter((item) => {
2544
+ const stringified = JSON.stringify(item);
2545
+ if (seen.has(stringified)) {
2546
+ return false;
2547
+ }
2548
+ seen.add(stringified);
2549
+ return true;
2550
+ });
2551
+ return JSON.stringify(uniqueArray);
2552
+ }
2553
+ if (typeof parsed === "object" && parsed !== null) {
2554
+ const reverseEntries = Object.entries(parsed).reverse();
2555
+ const seen = /* @__PURE__ */ new Set();
2556
+ const uniqueEntries = reverseEntries.filter(([_, value]) => {
2557
+ const stringified = JSON.stringify(value);
2558
+ if (seen.has(stringified)) {
2559
+ return false;
2560
+ }
2561
+ seen.add(stringified);
2562
+ return true;
2563
+ }).reverse();
2564
+ return JSON.stringify(Object.fromEntries(uniqueEntries));
2565
+ }
2566
+ return input;
2567
+ } catch {
2568
+ return input;
2569
+ }
2570
+ };
2571
+
2572
+ // src/utils/filters/upper.ts
2573
+ var upper = (input) => {
2574
+ const toUpperCase = (str) => {
2575
+ return str.toLocaleUpperCase();
2576
+ };
2577
+ if (Array.isArray(input)) {
2578
+ return input.map(toUpperCase);
2579
+ } else {
2580
+ return toUpperCase(input);
2581
+ }
2582
+ };
2583
+
2584
+ // src/utils/filters/wikilink.ts
2585
+ var wikilink = (str, param) => {
2586
+ if (!str.trim()) {
2587
+ return str;
2588
+ }
2589
+ let alias = "";
2590
+ if (param) {
2591
+ param = param.replace(/^\((.*)\)$/, "$1");
2592
+ alias = param.replace(/^(['"])([\s\S]*)\1$/, "$2");
2593
+ }
2594
+ try {
2595
+ const data = JSON.parse(str);
2596
+ const processObject = (obj) => {
2597
+ return Object.entries(obj).map(([key, value]) => {
2598
+ if (typeof value === "object" && value !== null) {
2599
+ return processObject(value);
2600
+ }
2601
+ return `[[${key}|${value}]]`;
2602
+ }).flat();
2603
+ };
2604
+ if (Array.isArray(data)) {
2605
+ const result = data.flatMap((item) => {
2606
+ if (typeof item === "object" && item !== null) {
2607
+ return processObject(item);
2608
+ }
2609
+ return item ? alias ? `[[${item}|${alias}]]` : `[[${item}]]` : "";
2610
+ });
2611
+ return JSON.stringify(result);
2612
+ } else if (typeof data === "object" && data !== null) {
2613
+ return JSON.stringify(processObject(data));
2614
+ }
2615
+ } catch (error) {
2616
+ return alias ? `[[${str}|${alias}]]` : `[[${str}]]`;
2617
+ }
2618
+ return str;
2619
+ };
2620
+
2621
+ // src/utils/filters/duration.ts
2622
+ import dayjs3 from "dayjs";
2623
+ import durationPlugin from "dayjs/plugin/duration";
2624
+ dayjs3.extend(durationPlugin);
2625
+ var duration = (str, param) => {
2626
+ if (!str) {
2627
+ return str;
2628
+ }
2629
+ try {
2630
+ str = str.replace(/^["'](.*)["']$/g, "$1");
2631
+ const matches = str.match(/^P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/);
2632
+ if (!matches) {
2633
+ const seconds2 = parseInt(str, 10);
2634
+ if (!isNaN(seconds2)) {
2635
+ return formatDuration(dayjs3.duration(seconds2, "seconds"), param);
2636
+ }
2637
+ return str;
2638
+ }
2639
+ const [, years, months, days, hours, minutes, seconds] = matches;
2640
+ const totalSeconds = (years ? parseInt(years) * 365 * 24 * 3600 : 0) + (months ? parseInt(months) * 30 * 24 * 3600 : 0) + (days ? parseInt(days) * 24 * 3600 : 0) + (hours ? parseInt(hours) * 3600 : 0) + (minutes ? parseInt(minutes) * 60 : 0) + (seconds ? parseInt(seconds) : 0);
2641
+ const dur = dayjs3.duration(totalSeconds, "seconds");
2642
+ return formatDuration(dur, param);
2643
+ } catch (error) {
2644
+ console.error("Error in duration filter:", error);
2645
+ return str;
2646
+ }
2647
+ };
2648
+ function formatDuration(dur, format) {
2649
+ if (!format) {
2650
+ if (dur.asHours() >= 1) {
2651
+ format = "HH:mm:ss";
2652
+ } else {
2653
+ format = "mm:ss";
2654
+ }
2655
+ }
2656
+ format = format.replace(/^["'(](.*)["')]$/g, "$1");
2657
+ const hours = Math.floor(dur.asHours());
2658
+ const minutes = dur.minutes();
2659
+ const seconds = dur.seconds();
2660
+ const parts = {
2661
+ "HH": padZero(hours),
2662
+ "H": hours.toString(),
2663
+ "mm": padZero(minutes),
2664
+ "m": minutes.toString(),
2665
+ "ss": padZero(seconds),
2666
+ "s": seconds.toString()
2667
+ };
2668
+ return format.replace(/HH|H|mm|m|ss|s/g, (match) => parts[match].toString());
2669
+ }
2670
+ function padZero(num) {
2671
+ return num.toString().padStart(2, "0");
2672
+ }
2673
+
2674
+ // src/utils/filters.ts
2675
+ var filterMetadata = {
2676
+ // Filters with validators
2677
+ calc: { example: 'calc:"+10"', validateParams: validateCalcParams },
2678
+ date_modify: { example: 'date_modify:"+1 day"', validateParams: validateDateModifyParams },
2679
+ map: { example: "map:x => x.name", validateParams: validateMapParams },
2680
+ replace: { example: 'replace:"old":"new"', validateParams: validateReplaceParams },
2681
+ slice: { example: "slice:0,5", validateParams: validateSliceParams },
2682
+ template: { example: 'template:"${name}"', validateParams: validateTemplateParams },
2683
+ // Filters with optional parameters (examples for documentation)
2684
+ blockquote: {},
2685
+ callout: { example: "callout:info" },
2686
+ camel: {},
2687
+ capitalize: {},
2688
+ date: { example: 'date:"YYYY-MM-DD"' },
2689
+ decode_uri: {},
2690
+ duration: {},
2691
+ first: {},
2692
+ footnote: {},
2693
+ fragment_link: {},
2694
+ html_to_json: {},
2695
+ image: {},
2696
+ join: { example: 'join:", "' },
2697
+ kebab: {},
2698
+ last: {},
2699
+ length: {},
2700
+ link: {},
2701
+ list: { example: "list:numbered", validateParams: validateListParams },
2702
+ lower: {},
2703
+ markdown: {},
2704
+ merge: {},
2705
+ nth: { example: "nth:2", validateParams: validateNthParams },
2706
+ number_format: {},
2707
+ object: { example: "object:keys", validateParams: validateObjectParams },
2708
+ pascal: {},
2709
+ remove_attr: {},
2710
+ remove_html: {},
2711
+ remove_tags: {},
2712
+ replace_tags: {},
2713
+ reverse: {},
2714
+ round: { example: "round:2", validateParams: validateRoundParams },
2715
+ safe_name: { example: "safe_name:windows", validateParams: validateSafeNameParams },
2716
+ snake: {},
2717
+ split: { example: 'split:","' },
2718
+ strip_attr: {},
2719
+ strip_md: {},
2720
+ strip_tags: {},
2721
+ stripmd: {},
2722
+ table: {},
2723
+ title: {},
2724
+ trim: {},
2725
+ uncamel: {},
2726
+ unescape: {},
2727
+ unique: {},
2728
+ upper: {},
2729
+ wikilink: {}
2730
+ };
2731
+ var validFilterNames = new Set(Object.keys(filterMetadata));
2732
+ var filters = {
2733
+ blockquote,
2734
+ calc,
2735
+ callout,
2736
+ camel,
2737
+ capitalize,
2738
+ date_modify,
2739
+ date,
2740
+ decode_uri,
2741
+ duration,
2742
+ first,
2743
+ footnote,
2744
+ fragment_link,
2745
+ html_to_json,
2746
+ image,
2747
+ join,
2748
+ kebab,
2749
+ last,
2750
+ length,
2751
+ link,
2752
+ list,
2753
+ lower,
2754
+ map,
2755
+ markdown,
2756
+ merge,
2757
+ number_format,
2758
+ nth,
2759
+ object,
2760
+ pascal,
2761
+ reverse,
2762
+ remove_attr,
2763
+ remove_html,
2764
+ remove_tags,
2765
+ replace,
2766
+ replace_tags,
2767
+ round,
2768
+ safe_name,
2769
+ slice,
2770
+ snake,
2771
+ split,
2772
+ strip_attr,
2773
+ strip_md,
2774
+ strip_tags,
2775
+ stripmd: strip_md,
2776
+ // an alias for strip_md
2777
+ table,
2778
+ template,
2779
+ title,
2780
+ trim,
2781
+ uncamel,
2782
+ unescape,
2783
+ unique,
2784
+ upper,
2785
+ wikilink
2786
+ };
2787
+ function splitFilterString(filterString) {
2788
+ const filters2 = [];
2789
+ const state = createParserState();
2790
+ filterString = filterString.replace(/\s*\|\s*(?=(?:[^"'()]*["'][^"'()]*["'])*[^"'()]*$)/g, "|");
2791
+ for (let i = 0; i < filterString.length; i++) {
2792
+ const char = filterString[i];
2793
+ if (char === "|" && !state.inQuote && !state.inRegex && state.curlyDepth === 0 && state.parenDepth === 0) {
2794
+ filters2.push(state.current.trim());
2795
+ state.current = "";
2796
+ } else {
2797
+ processCharacter(char, state);
2798
+ }
2799
+ }
2800
+ if (state.current) {
2801
+ filters2.push(state.current.trim());
2802
+ }
2803
+ return filters2;
2804
+ }
2805
+ function parseFilterString(filterString) {
2806
+ const parts = [];
2807
+ const state = createParserState();
2808
+ for (let i = 0; i < filterString.length; i++) {
2809
+ const char = filterString[i];
2810
+ if (char === ":" && !state.inQuote && !state.inRegex && state.parenDepth === 0 && parts.length === 0) {
2811
+ parts.push(state.current.trim());
2812
+ state.current = "";
2813
+ } else {
2814
+ processCharacter(char, state);
2815
+ }
2816
+ }
2817
+ if (state.current) {
2818
+ parts.push(state.current.trim());
2819
+ }
2820
+ return parts;
2821
+ }
2822
+ function applyFilterDirect(value, filterName, paramString, currentUrl) {
2823
+ debugLog("Filters", "applyFilterDirect called with:", { value, filterName, paramString, currentUrl });
2824
+ const filter = filters[filterName];
2825
+ if (!filter) {
2826
+ console.error(`Invalid filter: ${filterName}`);
2827
+ debugLog("Filters", `Available filters:`, Object.keys(filters));
2828
+ return typeof value === "string" ? value : JSON.stringify(value);
2829
+ }
2830
+ const stringInput = typeof value === "string" ? value : JSON.stringify(value);
2831
+ let params = paramString ? [paramString] : [];
2832
+ if (filterName === "markdown" && !paramString && currentUrl) {
2833
+ params = [currentUrl];
2834
+ }
2835
+ if (filterName === "fragment_link" && currentUrl) {
2836
+ params.push(currentUrl);
2837
+ }
2838
+ const output = filter(stringInput, params.join(":"));
2839
+ debugLog("Filters", `Filter ${filterName} output:`, output);
2840
+ if (typeof output === "string" && (output.startsWith("[") || output.startsWith("{"))) {
2841
+ try {
2842
+ const parsed = JSON.parse(output);
2843
+ return JSON.stringify(parsed);
2844
+ } catch {
2845
+ return output;
2846
+ }
2847
+ }
2848
+ return typeof output === "string" ? output : JSON.stringify(output);
2849
+ }
2850
+ function applyFilters(value, filterString, currentUrl) {
2851
+ debugLog("Filters", "applyFilters called with:", { value, filterString, currentUrl });
2852
+ if (!filterString) {
2853
+ debugLog("Filters", "Empty filter string, returning original value");
2854
+ return typeof value === "string" ? value : JSON.stringify(value);
2855
+ }
2856
+ let processedValue = value;
2857
+ const filterNames = splitFilterString(filterString);
2858
+ debugLog("Filters", "Split filter string:", filterNames);
2859
+ const result = filterNames.reduce((result2, filterName) => {
2860
+ const [name, ...params] = parseFilterString(filterName);
2861
+ debugLog("Filters", `Parsed filter: ${name}, Params:`, params);
2862
+ const filter = filters[name];
2863
+ if (filter) {
2864
+ const stringInput = typeof result2 === "string" ? result2 : JSON.stringify(result2);
2865
+ if (name === "markdown" && params.length === 0 && currentUrl) {
2866
+ params.push(currentUrl);
2867
+ }
2868
+ if (name === "fragment_link" && currentUrl) {
2869
+ params.push(currentUrl);
2870
+ }
2871
+ const output = filter(stringInput, params.join(":"));
2872
+ debugLog("Filters", `Filter ${name} output:`, output);
2873
+ if (typeof output === "string" && (output.startsWith("[") || output.startsWith("{"))) {
2874
+ try {
2875
+ return JSON.parse(output);
2876
+ } catch {
2877
+ return output;
2878
+ }
2879
+ }
2880
+ return output;
2881
+ } else {
2882
+ console.error(`Invalid filter: ${name}`);
2883
+ debugLog("Filters", `Available filters:`, Object.keys(filters));
2884
+ return result2;
2885
+ }
2886
+ }, processedValue);
2887
+ return typeof result === "string" ? result : JSON.stringify(result);
2888
+ }
2889
+
2890
+ // src/utils/parser.ts
2891
+ function parse(input) {
2892
+ const tokenizerResult = tokenize(input);
2893
+ const errors = tokenizerResult.errors.map((e) => ({
2894
+ message: e.message,
2895
+ line: e.line,
2896
+ column: e.column
2897
+ }));
2898
+ const state = {
2899
+ tokens: tokenizerResult.tokens,
2900
+ pos: 0,
2901
+ errors
2902
+ };
2903
+ const ast = parseTemplate(state);
2904
+ return { ast, errors: state.errors };
2905
+ }
2906
+ function parseTemplate(state) {
2907
+ const nodes = [];
2908
+ while (!isAtEnd(state)) {
2909
+ const node = parseNode(state);
2910
+ if (node) {
2911
+ nodes.push(node);
2912
+ }
2913
+ }
2914
+ return nodes;
2915
+ }
2916
+ function parseNode(state) {
2917
+ const token = peek(state);
2918
+ switch (token.type) {
2919
+ case "text":
2920
+ return parseText(state);
2921
+ case "variable_start":
2922
+ return parseVariable(state);
2923
+ case "tag_start":
2924
+ return parseTag(state);
2925
+ case "eof":
2926
+ advance2(state);
2927
+ return null;
2928
+ default:
2929
+ state.errors.push({
2930
+ message: `Unexpected "${token.value}" in template`,
2931
+ line: token.line,
2932
+ column: token.column
2933
+ });
2934
+ advance2(state);
2935
+ return null;
2936
+ }
2937
+ }
2938
+ function parseText(state) {
2939
+ const token = advance2(state);
2940
+ return {
2941
+ type: "text",
2942
+ value: token.value,
2943
+ line: token.line,
2944
+ column: token.column
2945
+ };
2946
+ }
2947
+ function parseVariable(state) {
2948
+ const startToken = advance2(state);
2949
+ const trimLeft = startToken.trimLeft || false;
2950
+ const expression = parseExpression(state);
2951
+ if (!expression) {
2952
+ state.errors.push({
2953
+ message: "Empty variable - add a variable name between {{ and }}",
2954
+ line: startToken.line,
2955
+ column: startToken.column
2956
+ });
2957
+ skipToEndOfVariable(state);
2958
+ return null;
2959
+ }
2960
+ if (check(state, "identifier")) {
2961
+ let extraWords = 0;
2962
+ const savedPos = state.pos;
2963
+ while (check(state, "identifier") && extraWords < 10) {
2964
+ advance2(state);
2965
+ extraWords++;
2966
+ }
2967
+ state.pos = savedPos;
2968
+ if (extraWords > 0) {
2969
+ state.errors.push({
2970
+ message: 'Unknown variable. If this is a prompt, wrap it in quotes: {{"your prompt here"}}',
2971
+ line: startToken.line,
2972
+ column: startToken.column
2973
+ });
2974
+ skipToEndOfVariable(state);
2975
+ return null;
2976
+ }
2977
+ }
2978
+ let trimRight = false;
2979
+ if (check(state, "variable_end")) {
2980
+ const endToken = advance2(state);
2981
+ trimRight = endToken.trimRight || false;
2982
+ } else {
2983
+ state.errors.push({
2984
+ message: "Missing closing }}",
2985
+ line: peek(state).line,
2986
+ column: peek(state).column
2987
+ });
2988
+ }
2989
+ return {
2990
+ type: "variable",
2991
+ expression,
2992
+ trimLeft,
2993
+ trimRight,
2994
+ line: startToken.line,
2995
+ column: startToken.column
2996
+ };
2997
+ }
2998
+ function parseTag(state) {
2999
+ const startToken = advance2(state);
3000
+ const trimLeft = startToken.trimLeft || false;
3001
+ const keywordToken = peek(state);
3002
+ switch (keywordToken.type) {
3003
+ case "keyword_if":
3004
+ return parseIfStatement(state, startToken, trimLeft);
3005
+ case "keyword_for":
3006
+ return parseForStatement(state, startToken, trimLeft);
3007
+ case "keyword_set":
3008
+ return parseSetStatement(state, startToken, trimLeft);
3009
+ case "keyword_else":
3010
+ case "keyword_elseif":
3011
+ case "keyword_endif":
3012
+ case "keyword_endfor":
3013
+ state.errors.push({
3014
+ message: `Unexpected {% ${keywordToken.value} %} - no matching opening tag`,
3015
+ line: keywordToken.line,
3016
+ column: keywordToken.column
3017
+ });
3018
+ skipToEndOfTag(state);
3019
+ return null;
3020
+ default:
3021
+ state.errors.push({
3022
+ message: `Unknown tag: {% ${keywordToken.value} %}`,
3023
+ line: keywordToken.line,
3024
+ column: keywordToken.column
3025
+ });
3026
+ skipToEndOfTag(state);
3027
+ return null;
3028
+ }
3029
+ }
3030
+ function parseIfStatement(state, startToken, trimLeft) {
3031
+ advance2(state);
3032
+ const condition = parseExpression(state);
3033
+ if (!condition) {
3034
+ state.errors.push({
3035
+ message: "{% if %} requires a condition",
3036
+ line: startToken.line,
3037
+ column: startToken.column
3038
+ });
3039
+ skipToEndOfTag(state);
3040
+ return null;
3041
+ }
3042
+ let trimRight = false;
3043
+ if (check(state, "tag_end")) {
3044
+ trimRight = advance2(state).trimRight || false;
3045
+ } else {
3046
+ state.errors.push({
3047
+ message: "Missing %} to close {% if %}",
3048
+ line: peek(state).line,
3049
+ column: peek(state).column
3050
+ });
3051
+ }
3052
+ const consequent = parseBody(state, ["keyword_elseif", "keyword_else", "keyword_endif"]);
3053
+ const elseifs = [];
3054
+ while (checkTagKeyword(state, "keyword_elseif")) {
3055
+ consumeTagStart(state);
3056
+ advance2(state);
3057
+ const elseifCondition = parseExpression(state);
3058
+ if (!elseifCondition) {
3059
+ state.errors.push({
3060
+ message: "{% elseif %} requires a condition",
3061
+ line: peek(state).line,
3062
+ column: peek(state).column
3063
+ });
3064
+ skipToEndOfTag(state);
3065
+ continue;
3066
+ }
3067
+ consumeTagEnd(state);
3068
+ const elseifBody = parseBody(state, ["keyword_elseif", "keyword_else", "keyword_endif"]);
3069
+ elseifs.push({ condition: elseifCondition, body: elseifBody });
3070
+ }
3071
+ let alternate = null;
3072
+ if (checkTagKeyword(state, "keyword_else")) {
3073
+ consumeTagStart(state);
3074
+ advance2(state);
3075
+ consumeTagEnd(state);
3076
+ alternate = parseBody(state, ["keyword_endif"]);
3077
+ }
3078
+ if (checkTagKeyword(state, "keyword_endif")) {
3079
+ consumeTagStart(state);
3080
+ advance2(state);
3081
+ consumeTagEnd(state);
3082
+ } else {
3083
+ state.errors.push({
3084
+ message: "Missing {% endif %} to close {% if %}",
3085
+ line: peek(state).line,
3086
+ column: peek(state).column
3087
+ });
3088
+ }
3089
+ return {
3090
+ type: "if",
3091
+ condition,
3092
+ consequent,
3093
+ elseifs,
3094
+ alternate,
3095
+ trimLeft,
3096
+ trimRight,
3097
+ line: startToken.line,
3098
+ column: startToken.column
3099
+ };
3100
+ }
3101
+ function parseForStatement(state, startToken, trimLeft) {
3102
+ advance2(state);
3103
+ if (!check(state, "identifier")) {
3104
+ state.errors.push({
3105
+ message: "{% for %} requires a variable name, e.g. {% for item in items %}",
3106
+ line: peek(state).line,
3107
+ column: peek(state).column
3108
+ });
3109
+ skipToEndOfTag(state);
3110
+ return null;
3111
+ }
3112
+ const iterator = advance2(state).value;
3113
+ if (!check(state, "keyword_in")) {
3114
+ state.errors.push({
3115
+ message: '{% for %} requires "in" keyword, e.g. {% for item in items %}',
3116
+ line: peek(state).line,
3117
+ column: peek(state).column
3118
+ });
3119
+ skipToEndOfTag(state);
3120
+ return null;
3121
+ }
3122
+ advance2(state);
3123
+ const iterable = parseExpression(state);
3124
+ if (!iterable) {
3125
+ state.errors.push({
3126
+ message: '{% for %} requires something to loop over after "in"',
3127
+ line: peek(state).line,
3128
+ column: peek(state).column
3129
+ });
3130
+ skipToEndOfTag(state);
3131
+ return null;
3132
+ }
3133
+ let trimRight = false;
3134
+ if (check(state, "tag_end")) {
3135
+ trimRight = advance2(state).trimRight || false;
3136
+ } else {
3137
+ state.errors.push({
3138
+ message: "Missing %} to close {% for %}",
3139
+ line: peek(state).line,
3140
+ column: peek(state).column
3141
+ });
3142
+ }
3143
+ const body = parseBody(state, ["keyword_endfor"]);
3144
+ if (checkTagKeyword(state, "keyword_endfor")) {
3145
+ consumeTagStart(state);
3146
+ advance2(state);
3147
+ consumeTagEnd(state);
3148
+ } else {
3149
+ state.errors.push({
3150
+ message: "Missing {% endfor %} to close {% for %}",
3151
+ line: peek(state).line,
3152
+ column: peek(state).column
3153
+ });
3154
+ }
3155
+ return {
3156
+ type: "for",
3157
+ iterator,
3158
+ iterable,
3159
+ body,
3160
+ trimLeft,
3161
+ trimRight,
3162
+ line: startToken.line,
3163
+ column: startToken.column
3164
+ };
3165
+ }
3166
+ function parseSetStatement(state, startToken, trimLeft) {
3167
+ advance2(state);
3168
+ if (!check(state, "identifier")) {
3169
+ state.errors.push({
3170
+ message: "{% set %} requires a variable name, e.g. {% set name = value %}",
3171
+ line: peek(state).line,
3172
+ column: peek(state).column
3173
+ });
3174
+ skipToEndOfTag(state);
3175
+ return null;
3176
+ }
3177
+ const variable = advance2(state).value;
3178
+ if (!check(state, "op_assign")) {
3179
+ state.errors.push({
3180
+ message: '{% set %} requires "=" after variable name',
3181
+ line: peek(state).line,
3182
+ column: peek(state).column
3183
+ });
3184
+ skipToEndOfTag(state);
3185
+ return null;
3186
+ }
3187
+ advance2(state);
3188
+ const value = parseExpression(state);
3189
+ if (!value) {
3190
+ state.errors.push({
3191
+ message: '{% set %} requires a value after "="',
3192
+ line: peek(state).line,
3193
+ column: peek(state).column
3194
+ });
3195
+ skipToEndOfTag(state);
3196
+ return null;
3197
+ }
3198
+ let trimRight = false;
3199
+ if (check(state, "tag_end")) {
3200
+ trimRight = advance2(state).trimRight || false;
3201
+ } else {
3202
+ state.errors.push({
3203
+ message: "Missing %} to close {% set %}",
3204
+ line: peek(state).line,
3205
+ column: peek(state).column
3206
+ });
3207
+ }
3208
+ return {
3209
+ type: "set",
3210
+ variable,
3211
+ value,
3212
+ trimLeft,
3213
+ trimRight,
3214
+ line: startToken.line,
3215
+ column: startToken.column
3216
+ };
3217
+ }
3218
+ function parseBody(state, stopKeywords) {
3219
+ const nodes = [];
3220
+ while (!isAtEnd(state)) {
3221
+ if (checkTagKeyword(state, ...stopKeywords)) {
3222
+ break;
3223
+ }
3224
+ const node = parseNode(state);
3225
+ if (node) {
3226
+ nodes.push(node);
3227
+ }
3228
+ }
3229
+ return nodes;
3230
+ }
3231
+ function parseExpression(state) {
3232
+ return parseNullishExpression(state);
3233
+ }
3234
+ function parseNullishExpression(state) {
3235
+ let left = parseFilterExpression(state);
3236
+ if (!left) return null;
3237
+ while (check(state, "op_nullish")) {
3238
+ const opToken = advance2(state);
3239
+ const right = parseFilterExpression(state);
3240
+ if (!right) {
3241
+ state.errors.push({
3242
+ message: "Missing fallback value after ??",
3243
+ line: opToken.line,
3244
+ column: opToken.column
3245
+ });
3246
+ break;
3247
+ }
3248
+ left = {
3249
+ type: "binary",
3250
+ operator: "??",
3251
+ left,
3252
+ right,
3253
+ line: opToken.line,
3254
+ column: opToken.column
3255
+ };
3256
+ }
3257
+ return left;
3258
+ }
3259
+ function parseFilterArgument(state) {
3260
+ const startToken = peek(state);
3261
+ if (check(state, "slash") || check(state, "star")) {
3262
+ const token = advance2(state);
3263
+ return {
3264
+ type: "literal",
3265
+ value: token.value,
3266
+ raw: token.value,
3267
+ line: token.line,
3268
+ column: token.column
3269
+ };
3270
+ }
3271
+ if (check(state, "lbracket")) {
3272
+ let value2 = "";
3273
+ let bracketDepth = 0;
3274
+ const startLine = peek(state).line;
3275
+ const startColumn = peek(state).column;
3276
+ while (!isAtEnd(state)) {
3277
+ const token = peek(state);
3278
+ if (token.type === "lbracket") {
3279
+ bracketDepth++;
3280
+ } else if (token.type === "rbracket") {
3281
+ bracketDepth--;
3282
+ if (bracketDepth === 0) {
3283
+ value2 += token.value;
3284
+ advance2(state);
3285
+ break;
3286
+ }
3287
+ }
3288
+ if (bracketDepth === 0 && (token.type === "pipe" || token.type === "variable_end" || token.type === "comma")) {
3289
+ break;
3290
+ }
3291
+ value2 += token.value;
3292
+ advance2(state);
3293
+ }
3294
+ return {
3295
+ type: "literal",
3296
+ value: value2,
3297
+ raw: value2,
3298
+ line: startLine,
3299
+ column: startColumn
3300
+ };
3301
+ }
3302
+ if (check(state, "identifier")) {
3303
+ const savedPos = state.pos;
3304
+ const idToken = advance2(state);
3305
+ if (check(state, "arrow")) {
3306
+ let value2 = idToken.value + " ";
3307
+ value2 += advance2(state).value + " ";
3308
+ let braceDepth = 0;
3309
+ let parenDepth = 0;
3310
+ while (!isAtEnd(state)) {
3311
+ const token = peek(state);
3312
+ if (braceDepth === 0 && parenDepth === 0) {
3313
+ if (token.type === "pipe" || token.type === "variable_end" || token.type === "tag_end") {
3314
+ break;
3315
+ }
3316
+ }
3317
+ if (token.type === "lbrace" || token.type === "lparen") {
3318
+ if (token.type === "lbrace") braceDepth++;
3319
+ else parenDepth++;
3320
+ } else if (token.type === "rbrace" || token.type === "rparen") {
3321
+ if (token.type === "rbrace") braceDepth--;
3322
+ else parenDepth--;
3323
+ if (braceDepth < 0 || parenDepth < 0) break;
3324
+ }
3325
+ if (token.type === "string") {
3326
+ value2 += `"${token.value}"`;
3327
+ } else {
3328
+ value2 += token.value;
3329
+ }
3330
+ advance2(state);
3331
+ }
3332
+ return {
3333
+ type: "literal",
3334
+ value: value2.trim(),
3335
+ raw: value2.trim(),
3336
+ line: startToken.line,
3337
+ column: startToken.column
3338
+ };
3339
+ }
3340
+ state.pos = savedPos;
3341
+ }
3342
+ const first2 = parsePrimaryExpression(state);
3343
+ if (!first2) return null;
3344
+ if (first2.type === "literal" && startToken.type === "string") {
3345
+ const formatString = (val) => `"${val}"`;
3346
+ let combined = formatString(first2.value);
3347
+ while (check(state, "colon")) {
3348
+ const savedPos = state.pos;
3349
+ advance2(state);
3350
+ if (check(state, "string")) {
3351
+ const next = parsePrimaryExpression(state);
3352
+ if (next && next.type === "literal") {
3353
+ combined += ":" + formatString(next.value);
3354
+ }
3355
+ } else {
3356
+ state.pos = savedPos;
3357
+ break;
3358
+ }
3359
+ }
3360
+ return {
3361
+ type: "literal",
3362
+ value: combined,
3363
+ raw: combined,
3364
+ line: first2.line,
3365
+ column: first2.column
3366
+ };
3367
+ }
3368
+ if (first2.type === "literal" && startToken.type === "number" && check(state, "identifier")) {
3369
+ const idToken = peek(state);
3370
+ if (idToken.value.length === 1 && /^[a-z]$/i.test(idToken.value)) {
3371
+ advance2(state);
3372
+ const combined = String(first2.value) + idToken.value;
3373
+ return {
3374
+ type: "literal",
3375
+ value: combined,
3376
+ raw: combined,
3377
+ line: first2.line,
3378
+ column: first2.column
3379
+ };
3380
+ }
3381
+ }
3382
+ if (!check(state, "colon")) {
3383
+ return first2;
3384
+ }
3385
+ let value = "";
3386
+ if (first2.type === "literal") {
3387
+ value = String(first2.value);
3388
+ } else if (first2.type === "identifier") {
3389
+ value = first2.name;
3390
+ } else {
3391
+ return first2;
3392
+ }
3393
+ while (check(state, "colon") && !isAtEnd(state)) {
3394
+ advance2(state);
3395
+ value += ":";
3396
+ const next = parsePrimaryExpression(state);
3397
+ if (next) {
3398
+ if (next.type === "literal") {
3399
+ value += String(next.value);
3400
+ } else if (next.type === "identifier") {
3401
+ value += next.name;
3402
+ }
3403
+ } else {
3404
+ break;
3405
+ }
3406
+ }
3407
+ return {
3408
+ type: "literal",
3409
+ value,
3410
+ raw: value,
3411
+ line: startToken.line,
3412
+ column: startToken.column
3413
+ };
3414
+ }
3415
+ function parseFilterExpression(state) {
3416
+ let left = parseOrExpression(state);
3417
+ if (!left) return null;
3418
+ while (check(state, "pipe")) {
3419
+ advance2(state);
3420
+ if (!check(state, "identifier")) {
3421
+ state.errors.push({
3422
+ message: "Missing filter name after |",
3423
+ line: peek(state).line,
3424
+ column: peek(state).column
3425
+ });
3426
+ break;
3427
+ }
3428
+ const filterToken = advance2(state);
3429
+ const args = [];
3430
+ if (check(state, "colon")) {
3431
+ advance2(state);
3432
+ if (check(state, "lparen")) {
3433
+ advance2(state);
3434
+ while (!check(state, "rparen") && !isAtEnd(state)) {
3435
+ const arg = parseOrExpression(state);
3436
+ if (!arg) break;
3437
+ if (arg.type === "literal" && typeof arg.value === "string" && check(state, "colon")) {
3438
+ const formatStr = (val) => `"${val}"`;
3439
+ let combined = formatStr(arg.value);
3440
+ while (check(state, "colon")) {
3441
+ advance2(state);
3442
+ const next = parseOrExpression(state);
3443
+ if (next && next.type === "literal" && typeof next.value === "string") {
3444
+ combined += ":" + formatStr(next.value);
3445
+ } else {
3446
+ break;
3447
+ }
3448
+ }
3449
+ args.push({
3450
+ type: "literal",
3451
+ value: combined,
3452
+ raw: combined,
3453
+ line: arg.line,
3454
+ column: arg.column
3455
+ });
3456
+ } else {
3457
+ args.push(arg);
3458
+ }
3459
+ if (check(state, "comma")) {
3460
+ advance2(state);
3461
+ } else {
3462
+ break;
3463
+ }
3464
+ }
3465
+ if (check(state, "rparen")) {
3466
+ advance2(state);
3467
+ }
3468
+ } else {
3469
+ const arg = parseFilterArgument(state);
3470
+ if (arg) args.push(arg);
3471
+ while (check(state, "comma")) {
3472
+ advance2(state);
3473
+ const nextArg = parseFilterArgument(state);
3474
+ if (nextArg) args.push(nextArg);
3475
+ }
3476
+ }
3477
+ }
3478
+ left = {
3479
+ type: "filter",
3480
+ value: left,
3481
+ name: filterToken.value,
3482
+ args,
3483
+ line: filterToken.line,
3484
+ column: filterToken.column
3485
+ };
3486
+ }
3487
+ return left;
3488
+ }
3489
+ function parseOrExpression(state) {
3490
+ let left = parseAndExpression(state);
3491
+ if (!left) return null;
3492
+ while (check(state, "op_or")) {
3493
+ const opToken = advance2(state);
3494
+ const right = parseAndExpression(state);
3495
+ if (!right) {
3496
+ state.errors.push({
3497
+ message: 'Missing value after "or"',
3498
+ line: opToken.line,
3499
+ column: opToken.column
3500
+ });
3501
+ break;
3502
+ }
3503
+ left = {
3504
+ type: "binary",
3505
+ operator: "or",
3506
+ left,
3507
+ right,
3508
+ line: opToken.line,
3509
+ column: opToken.column
3510
+ };
3511
+ }
3512
+ return left;
3513
+ }
3514
+ function parseAndExpression(state) {
3515
+ let left = parseNotExpression(state);
3516
+ if (!left) return null;
3517
+ while (check(state, "op_and")) {
3518
+ const opToken = advance2(state);
3519
+ const right = parseNotExpression(state);
3520
+ if (!right) {
3521
+ state.errors.push({
3522
+ message: 'Missing value after "and"',
3523
+ line: opToken.line,
3524
+ column: opToken.column
3525
+ });
3526
+ break;
3527
+ }
3528
+ left = {
3529
+ type: "binary",
3530
+ operator: "and",
3531
+ left,
3532
+ right,
3533
+ line: opToken.line,
3534
+ column: opToken.column
3535
+ };
3536
+ }
3537
+ return left;
3538
+ }
3539
+ function parseNotExpression(state) {
3540
+ if (check(state, "op_not")) {
3541
+ const opToken = advance2(state);
3542
+ const argument = parseNotExpression(state);
3543
+ if (!argument) {
3544
+ state.errors.push({
3545
+ message: 'Missing value after "not"',
3546
+ line: opToken.line,
3547
+ column: opToken.column
3548
+ });
3549
+ return null;
3550
+ }
3551
+ return {
3552
+ type: "unary",
3553
+ operator: "not",
3554
+ argument,
3555
+ line: opToken.line,
3556
+ column: opToken.column
3557
+ };
3558
+ }
3559
+ return parseComparisonExpression(state);
3560
+ }
3561
+ function parseComparisonExpression(state) {
3562
+ let left = parsePostfixExpression(state);
3563
+ if (!left) return null;
3564
+ const comparisonOps = ["op_eq", "op_neq", "op_gt", "op_lt", "op_gte", "op_lte", "op_contains"];
3565
+ if (comparisonOps.some((op) => check(state, op))) {
3566
+ const opToken = advance2(state);
3567
+ const right = parsePostfixExpression(state);
3568
+ if (!right) {
3569
+ state.errors.push({
3570
+ message: `Missing value after "${opToken.value}"`,
3571
+ line: opToken.line,
3572
+ column: opToken.column
3573
+ });
3574
+ return left;
3575
+ }
3576
+ const operatorMap = {
3577
+ "op_eq": "==",
3578
+ "op_neq": "!=",
3579
+ "op_gt": ">",
3580
+ "op_lt": "<",
3581
+ "op_gte": ">=",
3582
+ "op_lte": "<=",
3583
+ "op_contains": "contains"
3584
+ };
3585
+ return {
3586
+ type: "binary",
3587
+ operator: operatorMap[opToken.type] || opToken.value,
3588
+ left,
3589
+ right,
3590
+ line: opToken.line,
3591
+ column: opToken.column
3592
+ };
3593
+ }
3594
+ return left;
3595
+ }
3596
+ function parsePostfixExpression(state) {
3597
+ let left = parsePrimaryExpression(state);
3598
+ if (!left) return null;
3599
+ while (check(state, "lbracket")) {
3600
+ const bracketToken = advance2(state);
3601
+ const property = parseOrExpression(state);
3602
+ if (!property) {
3603
+ state.errors.push({
3604
+ message: "Empty brackets [] - add an index or key",
3605
+ line: bracketToken.line,
3606
+ column: bracketToken.column
3607
+ });
3608
+ break;
3609
+ }
3610
+ if (check(state, "rbracket")) {
3611
+ advance2(state);
3612
+ } else {
3613
+ state.errors.push({
3614
+ message: "Missing closing ]",
3615
+ line: peek(state).line,
3616
+ column: peek(state).column
3617
+ });
3618
+ }
3619
+ left = {
3620
+ type: "member",
3621
+ object: left,
3622
+ property,
3623
+ computed: true,
3624
+ line: bracketToken.line,
3625
+ column: bracketToken.column
3626
+ };
3627
+ }
3628
+ return left;
3629
+ }
3630
+ function parsePrimaryExpression(state) {
3631
+ const token = peek(state);
3632
+ if (check(state, "lparen")) {
3633
+ advance2(state);
3634
+ const expr = parseOrExpression(state);
3635
+ if (!expr) {
3636
+ state.errors.push({
3637
+ message: "Empty parentheses () - add an expression",
3638
+ line: token.line,
3639
+ column: token.column
3640
+ });
3641
+ return null;
3642
+ }
3643
+ if (check(state, "rparen")) {
3644
+ advance2(state);
3645
+ } else {
3646
+ state.errors.push({
3647
+ message: "Missing closing )",
3648
+ line: peek(state).line,
3649
+ column: peek(state).column
3650
+ });
3651
+ }
3652
+ return {
3653
+ type: "group",
3654
+ expression: expr,
3655
+ line: token.line,
3656
+ column: token.column
3657
+ };
3658
+ }
3659
+ if (check(state, "string")) {
3660
+ const strToken = advance2(state);
3661
+ return {
3662
+ type: "literal",
3663
+ value: strToken.value,
3664
+ raw: strToken.value,
3665
+ line: strToken.line,
3666
+ column: strToken.column
3667
+ };
3668
+ }
3669
+ if (check(state, "number")) {
3670
+ const numToken = advance2(state);
3671
+ return {
3672
+ type: "literal",
3673
+ value: parseFloat(numToken.value),
3674
+ raw: numToken.value,
3675
+ line: numToken.line,
3676
+ column: numToken.column
3677
+ };
3678
+ }
3679
+ if (check(state, "boolean")) {
3680
+ const boolToken = advance2(state);
3681
+ return {
3682
+ type: "literal",
3683
+ value: boolToken.value.toLowerCase() === "true",
3684
+ raw: boolToken.value,
3685
+ line: boolToken.line,
3686
+ column: boolToken.column
3687
+ };
3688
+ }
3689
+ if (check(state, "null")) {
3690
+ const nullToken = advance2(state);
3691
+ return {
3692
+ type: "literal",
3693
+ value: null,
3694
+ raw: "null",
3695
+ line: nullToken.line,
3696
+ column: nullToken.column
3697
+ };
3698
+ }
3699
+ if (check(state, "identifier")) {
3700
+ const idToken = advance2(state);
3701
+ let name = idToken.value;
3702
+ if (check(state, "colon")) {
3703
+ const colonToken = peek(state);
3704
+ advance2(state);
3705
+ let rest = "";
3706
+ while (check(state, "identifier") || check(state, "dot") || check(state, "colon") || check(state, "lbracket") || check(state, "rbracket") || check(state, "number") || check(state, "string") || check(state, "star")) {
3707
+ rest += advance2(state).value;
3708
+ }
3709
+ name = name + ":" + rest;
3710
+ }
3711
+ return {
3712
+ type: "identifier",
3713
+ name,
3714
+ line: idToken.line,
3715
+ column: idToken.column
3716
+ };
3717
+ }
3718
+ return null;
3719
+ }
3720
+ function peek(state) {
3721
+ return state.tokens[state.pos] || { type: "eof", value: "", line: 0, column: 0 };
3722
+ }
3723
+ function advance2(state) {
3724
+ const token = peek(state);
3725
+ if (!isAtEnd(state)) {
3726
+ state.pos++;
3727
+ }
3728
+ return token;
3729
+ }
3730
+ function check(state, type) {
3731
+ return peek(state).type === type;
3732
+ }
3733
+ function isAtEnd(state) {
3734
+ return peek(state).type === "eof";
3735
+ }
3736
+ function checkTagKeyword(state, ...keywords) {
3737
+ if (!check(state, "tag_start")) return false;
3738
+ const nextPos = state.pos + 1;
3739
+ if (nextPos >= state.tokens.length) return false;
3740
+ const nextToken = state.tokens[nextPos];
3741
+ return keywords.includes(nextToken.type);
3742
+ }
3743
+ function consumeTagStart(state) {
3744
+ if (check(state, "tag_start")) {
3745
+ return advance2(state);
3746
+ }
3747
+ return null;
3748
+ }
3749
+ function consumeTagEnd(state) {
3750
+ if (check(state, "tag_end")) {
3751
+ return advance2(state);
3752
+ }
3753
+ state.errors.push({
3754
+ message: "Missing closing %}",
3755
+ line: peek(state).line,
3756
+ column: peek(state).column
3757
+ });
3758
+ return null;
3759
+ }
3760
+ function skipToEndOfTag(state) {
3761
+ while (!isAtEnd(state) && !check(state, "tag_end")) {
3762
+ advance2(state);
3763
+ }
3764
+ if (check(state, "tag_end")) {
3765
+ advance2(state);
3766
+ }
3767
+ }
3768
+ function skipToEndOfVariable(state) {
3769
+ while (!isAtEnd(state) && !check(state, "variable_end")) {
3770
+ advance2(state);
3771
+ }
3772
+ if (check(state, "variable_end")) {
3773
+ advance2(state);
3774
+ }
3775
+ }
3776
+
3777
+ // src/utils/renderer.ts
3778
+ var defaultApplyFilterDirect = applyFilterDirect;
3779
+ async function render(template2, context, options = {}) {
3780
+ const parseResult = parse(template2);
3781
+ if (parseResult.errors.length > 0) {
3782
+ return {
3783
+ output: "",
3784
+ errors: parseResult.errors.map((e) => ({
3785
+ message: e.message,
3786
+ line: e.line,
3787
+ column: e.column
3788
+ })),
3789
+ hasDeferredVariables: false
3790
+ };
3791
+ }
3792
+ return renderAST(parseResult.ast, context, options);
3793
+ }
3794
+ async function renderAST(ast, context, options = {}) {
3795
+ const errors = [];
3796
+ const state = {
3797
+ context,
3798
+ errors,
3799
+ pendingTrimRight: false,
3800
+ hasDeferredVariables: false
3801
+ };
3802
+ let output = "";
3803
+ for (let i = 0; i < ast.length; i++) {
3804
+ const node = ast[i];
3805
+ const nodeOutput = await renderNode(node, state);
3806
+ output = appendNodeOutput(output, nodeOutput, node, state);
3807
+ }
3808
+ if (options.trimOutput) {
3809
+ output = output.trim();
3810
+ }
3811
+ return { output, errors, hasDeferredVariables: state.hasDeferredVariables };
3812
+ }
3813
+ async function renderNode(node, state) {
3814
+ switch (node.type) {
3815
+ case "text":
3816
+ return renderText(node, state);
3817
+ case "variable":
3818
+ return renderVariable(node, state);
3819
+ case "if":
3820
+ return renderIf(node, state);
3821
+ case "for":
3822
+ return renderFor(node, state);
3823
+ case "set":
3824
+ return renderSet(node, state);
3825
+ default:
3826
+ state.errors.push({
3827
+ message: `Unknown node type: ${node.type}`
3828
+ });
3829
+ return "";
3830
+ }
3831
+ }
3832
+ function renderText(node, state) {
3833
+ let text = node.value;
3834
+ if (state.pendingTrimRight) {
3835
+ text = trimLeadingWhitespace(text);
3836
+ state.pendingTrimRight = false;
3837
+ }
3838
+ return text;
3839
+ }
3840
+ async function renderVariable(node, state) {
3841
+ try {
3842
+ const promptInfo = getPromptBase(node.expression);
3843
+ if (promptInfo) {
3844
+ if (node.trimRight) {
3845
+ state.pendingTrimRight = true;
3846
+ }
3847
+ state.hasDeferredVariables = true;
3848
+ return reconstructPromptTemplate(node.expression);
3849
+ }
3850
+ const value = await evaluateExpression2(node.expression, state);
3851
+ const result = valueToString(value);
3852
+ if (node.trimRight) {
3853
+ state.pendingTrimRight = true;
3854
+ }
3855
+ return result;
3856
+ } catch (error) {
3857
+ state.errors.push({
3858
+ message: `Error evaluating variable: ${error}`,
3859
+ line: node.line,
3860
+ column: node.column
3861
+ });
3862
+ return "";
3863
+ }
3864
+ }
3865
+ function getPromptBase(expr) {
3866
+ if (expr.type === "literal" && typeof expr.value === "string") {
3867
+ return expr.value;
3868
+ }
3869
+ if (expr.type === "filter") {
3870
+ return getPromptBase(expr.value);
3871
+ }
3872
+ return null;
3873
+ }
3874
+ function formatFilterArgs(args) {
3875
+ const formatted = args.map((arg) => {
3876
+ if (arg.type === "literal") {
3877
+ const val = arg.value;
3878
+ if (typeof val === "string") {
3879
+ if (/^["'].*["']$/.test(val) || val.includes('":"') || val.includes("':'")) {
3880
+ return val;
3881
+ }
3882
+ return `"${val}"`;
3883
+ }
3884
+ return String(val);
3885
+ }
3886
+ return String(arg.value || arg.name || "");
3887
+ });
3888
+ if (formatted.length > 1) {
3889
+ return `(${formatted.join(",")})`;
3890
+ }
3891
+ return formatted[0] || "";
3892
+ }
3893
+ function reconstructPromptTemplate(expr) {
3894
+ return `{{${reconstructPromptTemplateInner(expr)}}}`;
3895
+ }
3896
+ function reconstructPromptTemplateInner(expr) {
3897
+ if (expr.type === "literal") {
3898
+ const value = expr.value;
3899
+ return typeof value === "string" ? `"${value}"` : String(value);
3900
+ }
3901
+ if (expr.type === "filter") {
3902
+ const filter = expr;
3903
+ const inner = reconstructPromptTemplateInner(filter.value);
3904
+ let filterStr = `${inner}|${filter.name}`;
3905
+ if (filter.args.length > 0) {
3906
+ filterStr += `:${formatFilterArgs(filter.args)}`;
3907
+ }
3908
+ return filterStr;
3909
+ }
3910
+ return String(expr);
3911
+ }
3912
+ async function renderIf(node, state) {
3913
+ try {
3914
+ const conditionValue = await evaluateExpression2(node.condition, state);
3915
+ if (isTruthy(conditionValue)) {
3916
+ const result = await renderNodes(node.consequent, state);
3917
+ if (node.trimRight) {
3918
+ state.pendingTrimRight = true;
3919
+ }
3920
+ return result;
3921
+ }
3922
+ for (const elseif of node.elseifs) {
3923
+ const elseifValue = await evaluateExpression2(elseif.condition, state);
3924
+ if (isTruthy(elseifValue)) {
3925
+ return renderNodes(elseif.body, state);
3926
+ }
3927
+ }
3928
+ if (node.alternate) {
3929
+ return renderNodes(node.alternate, state);
3930
+ }
3931
+ if (node.trimRight) {
3932
+ state.pendingTrimRight = true;
3933
+ }
3934
+ return "";
3935
+ } catch (error) {
3936
+ state.errors.push({
3937
+ message: `Error evaluating if condition: ${error}`,
3938
+ line: node.line,
3939
+ column: node.column
3940
+ });
3941
+ return "";
3942
+ }
3943
+ }
3944
+ async function renderFor(node, state) {
3945
+ try {
3946
+ const iterableValue = await evaluateExpression2(node.iterable, state);
3947
+ if (iterableValue === void 0 || iterableValue === null) {
3948
+ if (node.trimRight) {
3949
+ state.pendingTrimRight = true;
3950
+ }
3951
+ return "";
3952
+ }
3953
+ let iterableArray = iterableValue;
3954
+ if (!Array.isArray(iterableArray) && typeof iterableArray === "string") {
3955
+ try {
3956
+ const parsed = JSON.parse(iterableArray);
3957
+ if (Array.isArray(parsed)) {
3958
+ iterableArray = parsed;
3959
+ }
3960
+ } catch {
3961
+ }
3962
+ }
3963
+ if (!Array.isArray(iterableArray)) {
3964
+ state.errors.push({
3965
+ message: `For loop iterable is not an array: ${typeof iterableArray}`,
3966
+ line: node.line,
3967
+ column: node.column
3968
+ });
3969
+ if (node.trimRight) {
3970
+ state.pendingTrimRight = true;
3971
+ }
3972
+ return "";
3973
+ }
3974
+ const results = [];
3975
+ const length2 = iterableArray.length;
3976
+ for (let i = 0; i < length2; i++) {
3977
+ const item = iterableArray[i];
3978
+ const loop = {
3979
+ index: i + 1,
3980
+ // 1-indexed
3981
+ index0: i,
3982
+ // 0-indexed
3983
+ first: i === 0,
3984
+ last: i === length2 - 1,
3985
+ length: length2
3986
+ };
3987
+ const loopContext = {
3988
+ ...state.context,
3989
+ variables: {
3990
+ ...state.context.variables,
3991
+ [node.iterator]: item,
3992
+ [`${node.iterator}_index`]: i,
3993
+ // Keep for backwards compatibility
3994
+ loop
3995
+ }
3996
+ };
3997
+ const loopState = {
3998
+ ...state,
3999
+ context: loopContext
4000
+ };
4001
+ const itemResult = await renderNodes(node.body, loopState);
4002
+ results.push(itemResult.trim());
4003
+ }
4004
+ if (node.trimRight) {
4005
+ state.pendingTrimRight = true;
4006
+ }
4007
+ return results.join("\n");
4008
+ } catch (error) {
4009
+ state.errors.push({
4010
+ message: `Error in for loop: ${error}`,
4011
+ line: node.line,
4012
+ column: node.column
4013
+ });
4014
+ return "";
4015
+ }
4016
+ }
4017
+ async function renderSet(node, state) {
4018
+ try {
4019
+ const value = await evaluateExpression2(node.value, state);
4020
+ state.context.variables[node.variable] = value;
4021
+ if (node.trimRight) {
4022
+ state.pendingTrimRight = true;
4023
+ }
4024
+ return "";
4025
+ } catch (error) {
4026
+ state.errors.push({
4027
+ message: `Error in set: ${error}`,
4028
+ line: node.line,
4029
+ column: node.column
4030
+ });
4031
+ return "";
4032
+ }
4033
+ }
4034
+ async function renderNodes(nodes, state) {
4035
+ let output = "";
4036
+ for (const node of nodes) {
4037
+ const nodeOutput = await renderNode(node, state);
4038
+ output = appendNodeOutput(output, nodeOutput, node, state);
4039
+ }
4040
+ return output;
4041
+ }
4042
+ function appendNodeOutput(output, nodeOutput, node, state) {
4043
+ if ("trimLeft" in node && node.trimLeft && output.length > 0) {
4044
+ output = trimTrailingWhitespace(output);
4045
+ }
4046
+ if (state.pendingTrimRight && nodeOutput.length > 0) {
4047
+ output += trimLeadingWhitespace(nodeOutput);
4048
+ state.pendingTrimRight = false;
4049
+ } else {
4050
+ output += nodeOutput;
4051
+ }
4052
+ return output;
4053
+ }
4054
+ async function evaluateExpression2(expr, state) {
4055
+ switch (expr.type) {
4056
+ case "literal":
4057
+ return evaluateLiteral(expr);
4058
+ case "identifier":
4059
+ return evaluateIdentifier(expr, state);
4060
+ case "binary":
4061
+ return evaluateBinary(expr, state);
4062
+ case "unary":
4063
+ return evaluateUnary(expr, state);
4064
+ case "filter":
4065
+ return evaluateFilter(expr, state);
4066
+ case "group":
4067
+ return evaluateExpression2(expr.expression, state);
4068
+ case "member":
4069
+ return evaluateMember(expr, state);
4070
+ default:
4071
+ throw new Error(`Unknown expression type: ${expr.type}`);
4072
+ }
4073
+ }
4074
+ function evaluateLiteral(expr) {
4075
+ return expr.value;
4076
+ }
4077
+ async function evaluateIdentifier(expr, state) {
4078
+ const name = expr.name;
4079
+ if (name.startsWith("selector:") || name.startsWith("selectorHtml:")) {
4080
+ if (state.context.asyncResolver) {
4081
+ return state.context.asyncResolver(name, state.context);
4082
+ }
4083
+ state.hasDeferredVariables = true;
4084
+ return `{{${name}}}`;
4085
+ }
4086
+ if (name.startsWith("schema:")) {
4087
+ const value = resolveSchemaVariable(name, state.context.variables);
4088
+ return value;
4089
+ }
4090
+ if (name.startsWith("prompt:") || name.startsWith('"')) {
4091
+ state.hasDeferredVariables = true;
4092
+ return `{{${name}}}`;
4093
+ }
4094
+ return resolveVariable(name, state.context.variables);
4095
+ }
4096
+ async function evaluateMember(expr, state) {
4097
+ const object2 = await evaluateExpression2(expr.object, state);
4098
+ const property = await evaluateExpression2(expr.property, state);
4099
+ if (object2 === void 0 || object2 === null) {
4100
+ return void 0;
4101
+ }
4102
+ if (Array.isArray(object2) && typeof property === "number") {
4103
+ return object2[property];
4104
+ }
4105
+ if (Array.isArray(object2) && typeof property === "string" && /^\d+$/.test(property)) {
4106
+ return object2[parseInt(property, 10)];
4107
+ }
4108
+ if (typeof object2 === "object" && property !== void 0) {
4109
+ return object2[property];
4110
+ }
4111
+ return void 0;
4112
+ }
4113
+ async function evaluateBinary(expr, state) {
4114
+ if (expr.operator === "??") {
4115
+ const left2 = await evaluateExpression2(expr.left, state);
4116
+ if (isTruthy(left2)) {
4117
+ return left2;
4118
+ }
4119
+ return evaluateExpression2(expr.right, state);
4120
+ }
4121
+ const left = await evaluateExpression2(expr.left, state);
4122
+ const right = await evaluateExpression2(expr.right, state);
4123
+ switch (expr.operator) {
4124
+ case "==":
4125
+ return left == right;
4126
+ case "!=":
4127
+ return left != right;
4128
+ case ">":
4129
+ return left > right;
4130
+ case "<":
4131
+ return left < right;
4132
+ case ">=":
4133
+ return left >= right;
4134
+ case "<=":
4135
+ return left <= right;
4136
+ case "contains":
4137
+ return evaluateContains(left, right);
4138
+ case "and":
4139
+ return isTruthy(left) && isTruthy(right);
4140
+ case "or":
4141
+ return isTruthy(left) || isTruthy(right);
4142
+ default:
4143
+ throw new Error(`Unknown binary operator: ${expr.operator}`);
4144
+ }
4145
+ }
4146
+ async function evaluateUnary(expr, state) {
4147
+ const argument = await evaluateExpression2(expr.argument, state);
4148
+ switch (expr.operator) {
4149
+ case "not":
4150
+ return !isTruthy(argument);
4151
+ default:
4152
+ throw new Error(`Unknown unary operator: ${expr.operator}`);
4153
+ }
4154
+ }
4155
+ async function evaluateFilter(expr, state) {
4156
+ const value = await evaluateExpression2(expr.value, state);
4157
+ const args = [];
4158
+ for (const arg of expr.args) {
4159
+ let argValue = await evaluateExpression2(arg, state);
4160
+ if (argValue === void 0 && arg.type === "identifier") {
4161
+ argValue = arg.name;
4162
+ }
4163
+ args.push(argValue);
4164
+ }
4165
+ if (state.context.filters && state.context.filters[expr.name]) {
4166
+ return state.context.filters[expr.name](value, ...args);
4167
+ }
4168
+ const stringValue = valueToString(value);
4169
+ let paramString;
4170
+ if (args.length > 0) {
4171
+ const formattedArgs = args.map((a) => {
4172
+ if (typeof a === "string") {
4173
+ if (isQuotedString(a)) {
4174
+ return a;
4175
+ }
4176
+ if (/\s*\w+\s*=>/.test(a)) {
4177
+ return a;
4178
+ }
4179
+ if (/^[\w.:+\-*/]+$/.test(a)) {
4180
+ return a;
4181
+ }
4182
+ return `"${a}"`;
4183
+ }
4184
+ return String(a);
4185
+ });
4186
+ paramString = formattedArgs.join(",");
4187
+ }
4188
+ const applyFilterDirectFn = state.context.applyFilterDirect || defaultApplyFilterDirect;
4189
+ return applyFilterDirectFn(stringValue, expr.name, paramString, state.context.currentUrl);
4190
+ }
4191
+ function evaluateContains(left, right) {
4192
+ if (left === void 0 || left === null) return false;
4193
+ if (right === void 0 || right === null) return false;
4194
+ if (Array.isArray(left)) {
4195
+ return left.some((item) => {
4196
+ if (typeof item === "string" && typeof right === "string") {
4197
+ return item.toLowerCase() === right.toLowerCase();
4198
+ }
4199
+ return item == right;
4200
+ });
4201
+ }
4202
+ if (typeof left === "string") {
4203
+ const searchValue = typeof right === "string" ? right : String(right);
4204
+ return left.toLowerCase().includes(searchValue.toLowerCase());
4205
+ }
4206
+ return false;
4207
+ }
4208
+ function resolveSchemaVariable(name, variables) {
4209
+ const schemaKey = name.slice("schema:".length);
4210
+ const nestedArrayMatch = schemaKey.match(/^(.*?)\[(\*|\d+)\](\.(.*))?$/);
4211
+ if (nestedArrayMatch) {
4212
+ const [, arrayKey, indexOrStar, , propertyPath] = nestedArrayMatch;
4213
+ const arrayValue = resolveSchemaKey(arrayKey, variables);
4214
+ if (arrayValue === void 0) return void 0;
4215
+ const parsed = parseSchemaValue(arrayValue);
4216
+ if (!Array.isArray(parsed)) return void 0;
4217
+ if (indexOrStar === "*") {
4218
+ if (propertyPath) {
4219
+ return parsed.map((item) => getNestedValue(item, propertyPath)).filter((v) => v != null);
4220
+ }
4221
+ return parsed;
4222
+ } else {
4223
+ const index = parseInt(indexOrStar, 10);
4224
+ const item = parsed[index];
4225
+ if (item === void 0) return void 0;
4226
+ return propertyPath ? getNestedValue(item, propertyPath) : item;
4227
+ }
4228
+ }
4229
+ const rawValue = resolveSchemaKey(schemaKey, variables);
4230
+ if (rawValue === void 0) return void 0;
4231
+ return parseSchemaValue(rawValue);
4232
+ }
4233
+ function resolveSchemaKey(schemaKey, variables) {
4234
+ const name = `schema:${schemaKey}`;
4235
+ const exactValue = variables[`{{${name}}}`];
4236
+ if (exactValue !== void 0) {
4237
+ return exactValue;
4238
+ }
4239
+ if (variables[name] !== void 0) {
4240
+ return variables[name];
4241
+ }
4242
+ if (!schemaKey.includes("@")) {
4243
+ const matchingKey = Object.keys(variables).find(
4244
+ (key) => key.includes("@") && key.endsWith(`:${schemaKey}}}`)
4245
+ );
4246
+ if (matchingKey) {
4247
+ return variables[matchingKey];
4248
+ }
4249
+ }
4250
+ return void 0;
4251
+ }
4252
+ function parseSchemaValue(value) {
4253
+ if (typeof value === "string") {
4254
+ if (value.startsWith("[") || value.startsWith("{")) {
4255
+ try {
4256
+ return JSON.parse(value);
4257
+ } catch {
4258
+ return value;
4259
+ }
4260
+ }
4261
+ }
4262
+ return value;
4263
+ }
4264
+ function resolveVariable(name, variables) {
4265
+ const trimmed = name.trim();
4266
+ const wrappedValue = variables[`{{${trimmed}}}`];
4267
+ if (wrappedValue !== void 0) {
4268
+ return wrappedValue;
4269
+ }
4270
+ if (variables[trimmed] !== void 0) {
4271
+ return variables[trimmed];
4272
+ }
4273
+ if (trimmed.includes(".")) {
4274
+ return getNestedValue(variables, trimmed);
4275
+ }
4276
+ return void 0;
4277
+ }
4278
+ function getNestedValue(obj, path) {
4279
+ if (!path || !obj) return void 0;
4280
+ const keys = path.split(".");
4281
+ let value = obj;
4282
+ for (const key of keys) {
4283
+ if (value === void 0 || value === null) return void 0;
4284
+ if (key.includes("[") && key.includes("]")) {
4285
+ const match = key.match(/^([^\[]*)\[([^\]]+)\]/);
4286
+ if (match) {
4287
+ const [, arrayKey, indexStr] = match;
4288
+ const baseValue = arrayKey ? value[arrayKey] : value;
4289
+ if (Array.isArray(baseValue)) {
4290
+ const index = parseInt(indexStr, 10);
4291
+ value = baseValue[index];
4292
+ } else if (baseValue && typeof baseValue === "object") {
4293
+ value = baseValue[indexStr.replace(/^["']|["']$/g, "")];
4294
+ } else {
4295
+ return void 0;
4296
+ }
4297
+ continue;
4298
+ }
4299
+ }
4300
+ if (value[`{{${key}}}`] !== void 0) {
4301
+ value = value[`{{${key}}}`];
4302
+ } else {
4303
+ value = value[key];
4304
+ }
4305
+ }
4306
+ return value;
4307
+ }
4308
+ function trimTrailingWhitespace(str) {
4309
+ return str.replace(/[\t ]*\r?\n?$/, "");
4310
+ }
4311
+ function trimLeadingWhitespace(str) {
4312
+ return str.replace(/^[\t ]*\r?\n?/, "");
4313
+ }
4314
+ function isQuotedString(str) {
4315
+ return /^["'][\s\S]*["']$/.test(str) || str.includes('":"') || str.includes("':'");
4316
+ }
4317
+ function isTruthy(value) {
4318
+ if (value === void 0 || value === null) return false;
4319
+ if (value === "") return false;
4320
+ if (value === 0) return false;
4321
+ if (value === false) return false;
4322
+ if (Array.isArray(value) && value.length === 0) return false;
4323
+ return true;
4324
+ }
4325
+ function valueToString(value) {
4326
+ if (value === void 0 || value === null) {
4327
+ return "";
4328
+ }
4329
+ if (Array.isArray(value) && value.length === 1 && typeof value[0] !== "object") {
4330
+ return String(value[0]);
4331
+ }
4332
+ if (typeof value === "object") {
4333
+ return JSON.stringify(value);
4334
+ }
4335
+ return String(value);
4336
+ }
4337
+
4338
+ // src/utils/shared.ts
4339
+ import dayjs4 from "dayjs";
4340
+ function buildVariables(params) {
4341
+ const currentUrl = params.url.replace(/#:~:text=[^&]+(&|$)/, "");
4342
+ const noteName = sanitizeFileName(params.title);
4343
+ const timestamp = dayjs4().format("YYYY-MM-DDTHH:mm:ssZ");
4344
+ const variables = {
4345
+ "{{author}}": (params.author || "").trim(),
4346
+ "{{content}}": (params.content || "").trim(),
4347
+ "{{contentHtml}}": (params.contentHtml || "").trim(),
4348
+ "{{selection}}": (params.selection || "").trim(),
4349
+ "{{selectionHtml}}": (params.selectionHtml || "").trim(),
4350
+ "{{date}}": timestamp,
4351
+ "{{time}}": timestamp,
4352
+ "{{description}}": (params.description || "").trim(),
4353
+ "{{domain}}": getDomain(currentUrl),
4354
+ "{{favicon}}": params.favicon || "",
4355
+ "{{fullHtml}}": (params.fullHtml || "").trim(),
4356
+ "{{highlights}}": params.highlights || "",
4357
+ "{{image}}": params.image || "",
4358
+ "{{noteName}}": noteName.trim(),
4359
+ "{{published}}": (params.published || "").split(",")[0].trim(),
4360
+ "{{site}}": (params.site || "").trim(),
4361
+ "{{title}}": (params.title || "").trim(),
4362
+ "{{url}}": currentUrl.trim(),
4363
+ "{{language}}": (params.language || "").trim(),
4364
+ "{{words}}": (params.wordCount ?? 0).toString()
4365
+ };
4366
+ if (params.extractedContent) {
4367
+ for (const [key, value] of Object.entries(params.extractedContent)) {
4368
+ variables[`{{${key}}}`] = value;
4369
+ }
4370
+ }
4371
+ if (params.metaTags) {
4372
+ for (const meta of params.metaTags) {
4373
+ if (meta.name && meta.content) {
4374
+ variables[`{{meta:name:${meta.name}}}`] = meta.content;
4375
+ }
4376
+ if (meta.property && meta.content) {
4377
+ variables[`{{meta:property:${meta.property}}}`] = meta.content;
4378
+ }
4379
+ }
4380
+ }
4381
+ if (params.schemaOrgData) {
4382
+ addSchemaOrgDataToVariables(params.schemaOrgData, variables);
4383
+ }
4384
+ return variables;
4385
+ }
4386
+ function addSchemaOrgDataToVariables(schemaData, variables, prefix = "") {
4387
+ if (Array.isArray(schemaData)) {
4388
+ schemaData.forEach((item, index) => {
4389
+ if (!item || typeof item !== "object") return;
4390
+ if (item["@type"]) {
4391
+ if (Array.isArray(item["@type"])) {
4392
+ item["@type"].forEach((type) => {
4393
+ addSchemaOrgDataToVariables(item, variables, `@${type}:`);
4394
+ });
4395
+ } else {
4396
+ addSchemaOrgDataToVariables(item, variables, `@${item["@type"]}:`);
4397
+ }
4398
+ } else {
4399
+ addSchemaOrgDataToVariables(item, variables, `[${index}]:`);
4400
+ }
4401
+ });
4402
+ } else if (typeof schemaData === "object" && schemaData !== null) {
4403
+ const objectKey = `{{schema:${prefix.replace(/\.$/, "")}}}`;
4404
+ variables[objectKey] = JSON.stringify(schemaData);
4405
+ Object.entries(schemaData).forEach(([key, value]) => {
4406
+ if (key === "@type") return;
4407
+ const variableKey = `{{schema:${prefix}${key}}}`;
4408
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
4409
+ variables[variableKey] = String(value);
4410
+ } else if (Array.isArray(value)) {
4411
+ variables[variableKey] = JSON.stringify(value);
4412
+ value.forEach((item, index) => {
4413
+ addSchemaOrgDataToVariables(item, variables, `${prefix}${key}[${index}].`);
4414
+ });
4415
+ } else if (typeof value === "object" && value !== null) {
4416
+ addSchemaOrgDataToVariables(value, variables, `${prefix}${key}.`);
4417
+ }
4418
+ });
4419
+ }
4420
+ }
4421
+ function generateFrontmatter(properties, propertyTypes = {}) {
4422
+ let frontmatter = "---\n";
4423
+ for (const property of properties) {
4424
+ const trimmedName = property.name.trim();
4425
+ const needsQuotes = /[:\s\{\}\[\],&*#?|<>=!%@\\-]/.test(trimmedName) || /^\d/.test(trimmedName) || /^(true|false|null|yes|no|on|off)$/i.test(trimmedName);
4426
+ const propertyKey = needsQuotes ? property.name.includes('"') ? `'${property.name.replace(/'/g, "''")}'` : `"${property.name}"` : property.name;
4427
+ frontmatter += `${propertyKey}:`;
4428
+ const propertyType = propertyTypes[property.name] || "text";
4429
+ switch (propertyType) {
4430
+ case "multitext": {
4431
+ let items;
4432
+ if (property.value.trim().startsWith('["') && property.value.trim().endsWith('"]')) {
4433
+ try {
4434
+ items = JSON.parse(property.value);
4435
+ } catch {
4436
+ items = property.value.split(",").map((item) => item.trim());
4437
+ }
4438
+ } else {
4439
+ items = property.value.split(/,(?![^\[]*\]\])/).map((item) => item.trim());
4440
+ }
4441
+ items = items.filter((item) => item !== "");
4442
+ if (items.length > 0) {
4443
+ frontmatter += "\n";
4444
+ items.forEach((item) => {
4445
+ frontmatter += ` - "${escapeDoubleQuotes(item)}"
4446
+ `;
4447
+ });
4448
+ } else {
4449
+ frontmatter += "\n";
4450
+ }
4451
+ break;
4452
+ }
4453
+ case "number": {
4454
+ const numericValue = property.value.replace(/[^\d.-]/g, "");
4455
+ frontmatter += numericValue ? ` ${parseFloat(numericValue)}
4456
+ ` : "\n";
4457
+ break;
4458
+ }
4459
+ case "checkbox": {
4460
+ const isChecked = typeof property.value === "boolean" ? property.value : property.value === "true";
4461
+ frontmatter += ` ${isChecked}
4462
+ `;
4463
+ break;
4464
+ }
4465
+ case "date":
4466
+ case "datetime":
4467
+ frontmatter += property.value.trim() !== "" ? ` ${property.value}
4468
+ ` : "\n";
4469
+ break;
4470
+ default:
4471
+ frontmatter += property.value.trim() !== "" ? ` "${escapeDoubleQuotes(property.value)}"
4472
+ ` : "\n";
4473
+ }
4474
+ }
4475
+ frontmatter += "---\n";
4476
+ if (frontmatter.trim() === "---\n---") {
4477
+ return "";
4478
+ }
4479
+ return frontmatter;
4480
+ }
4481
+ function formatPropertyValue(value, type, templateValue) {
4482
+ switch (type) {
4483
+ case "number": {
4484
+ const numericValue = value.replace(/[^\d.-]/g, "");
4485
+ return numericValue ? parseFloat(numericValue).toString() : value;
4486
+ }
4487
+ case "checkbox":
4488
+ return (value.toLowerCase() === "true" || value === "1").toString();
4489
+ case "date":
4490
+ case "datetime": {
4491
+ if (!templateValue.includes("|date:")) {
4492
+ const d = dayjs4(value);
4493
+ if (d.isValid()) {
4494
+ return d.format(type === "date" ? "YYYY-MM-DD" : "YYYY-MM-DDTHH:mm:ssZ");
4495
+ }
4496
+ }
4497
+ return value;
4498
+ }
4499
+ default:
4500
+ return value;
4501
+ }
4502
+ }
4503
+ function extractContentBySelector(doc, selector, attribute, extractHtml = false) {
4504
+ try {
4505
+ const elements = doc.querySelectorAll(selector);
4506
+ if (elements.length === 0) {
4507
+ return "";
4508
+ }
4509
+ return Array.from(elements).map((el) => {
4510
+ if (attribute) {
4511
+ return el.getAttribute(attribute) || "";
4512
+ }
4513
+ return extractHtml ? el.outerHTML : el.textContent?.trim() || "";
4514
+ });
4515
+ } catch (error) {
4516
+ console.error("Error in extractContentBySelector:", error);
4517
+ return "";
4518
+ }
4519
+ }
4520
+ function selectorContentToString(content) {
4521
+ if (Array.isArray(content)) {
4522
+ return content.length === 1 ? String(content[0]) : JSON.stringify(content);
4523
+ }
4524
+ return content;
4525
+ }
4526
+
4527
+ // src/utils/variables/selector.ts
4528
+ async function sendExtractContent(tabId, selector, attribute, extractHtml) {
4529
+ const response = await browser_polyfill_default.runtime.sendMessage({
4530
+ action: "sendMessageToTab",
4531
+ tabId,
4532
+ message: {
4533
+ action: "extractContent",
4534
+ selector,
4535
+ attribute,
4536
+ extractHtml
4537
+ }
4538
+ });
4539
+ return response || void 0;
4540
+ }
4541
+ async function resolveSelector(tabId, selectorExpr) {
4542
+ const selectorRegex = /^(selector|selectorHtml):(.*?)(?:\?(.*))?$/;
4543
+ const matches = selectorExpr.match(selectorRegex);
4544
+ if (!matches) {
4545
+ console.error("Invalid selector format:", selectorExpr);
4546
+ return void 0;
4547
+ }
4548
+ const [, selectorType, rawSelector, attribute] = matches;
4549
+ const extractHtml = selectorType === "selectorHtml";
4550
+ const selector = rawSelector.replace(/\\"/g, '"').replace(/\s+/g, " ").trim();
4551
+ try {
4552
+ const response = await sendExtractContent(tabId, selector, attribute, extractHtml);
4553
+ return response ? response.content : void 0;
4554
+ } catch (error) {
4555
+ console.error("Error extracting content by selector:", error, { selector, attribute, extractHtml });
4556
+ return void 0;
4557
+ }
4558
+ }
4559
+ async function processSelector(tabId, match, currentUrl) {
4560
+ const selectorRegex = /{{(selector|selectorHtml):(.*?)(?:\?(.*?))?(?:\|(.*?))?}}/;
4561
+ const matches = match.match(selectorRegex);
4562
+ if (!matches) {
4563
+ console.error("Invalid selector format:", match);
4564
+ return match;
4565
+ }
4566
+ const [, selectorType, rawSelector, attribute, filtersString] = matches;
4567
+ const extractHtml = selectorType === "selectorHtml";
4568
+ const selector = rawSelector.replace(/\\"/g, '"').replace(/\s+/g, " ").trim();
4569
+ try {
4570
+ const response = await sendExtractContent(tabId, selector, attribute, extractHtml);
4571
+ let content = response ? response.content : "";
4572
+ const contentString = selectorContentToString(content);
4573
+ debugLog("ContentExtractor", "Applying filters:", { selector, filterString: filtersString });
4574
+ const filteredContent = applyFilters(contentString, filtersString, currentUrl);
4575
+ return filteredContent;
4576
+ } catch (error) {
4577
+ console.error("Error extracting content by selector:", error, { selector, attribute, extractHtml });
4578
+ return "";
4579
+ }
4580
+ }
4581
+
4582
+ // src/utils/resolver.ts
4583
+ function resolveVariable2(name, variables) {
4584
+ const trimmed = name.trim();
4585
+ if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
4586
+ return trimmed.slice(1, -1).replace(/\\(.)/g, "$1");
4587
+ }
4588
+ if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
4589
+ return parseFloat(trimmed);
4590
+ }
4591
+ if (trimmed === "true") return true;
4592
+ if (trimmed === "false") return false;
4593
+ if (trimmed === "null") return null;
4594
+ if (trimmed === "undefined") return void 0;
4595
+ if (trimmed.startsWith("schema:")) {
4596
+ return resolveSchemaVariable2(trimmed, variables);
4597
+ }
4598
+ if (!trimmed.includes(".") && !trimmed.includes("[")) {
4599
+ const wrappedValue = variables[`{{${trimmed}}}`];
4600
+ if (wrappedValue !== void 0) {
4601
+ return wrappedValue;
4602
+ }
4603
+ if (variables[trimmed] !== void 0) {
4604
+ return variables[trimmed];
4605
+ }
4606
+ }
4607
+ return getNestedValue2(variables, trimmed);
4608
+ }
4609
+ function resolveSchemaVariable2(schemaKey, variables) {
4610
+ let value = variables[`{{${schemaKey}}}`];
4611
+ if (value !== void 0) {
4612
+ return value;
4613
+ }
4614
+ const shortKey = schemaKey.replace("schema:", "");
4615
+ if (!shortKey.includes("@")) {
4616
+ const matchingKey = Object.keys(variables).find((key) => key.includes("@") && key.endsWith(`:${shortKey}}}`));
4617
+ if (matchingKey) {
4618
+ return variables[matchingKey];
4619
+ }
4620
+ }
4621
+ return void 0;
4622
+ }
4623
+ function getNestedValue2(obj, path) {
4624
+ if (!path || !obj) return void 0;
4625
+ const keys = path.split(".");
4626
+ return keys.reduce((value, key) => {
4627
+ if (value === void 0 || value === null) return void 0;
4628
+ if (key.includes("[") && key.includes("]")) {
4629
+ const match = key.match(/^([^\[]*)\[([^\]]+)\]/);
4630
+ if (match) {
4631
+ const [, arrayKey, indexStr] = match;
4632
+ const baseValue = arrayKey ? value[arrayKey] : value;
4633
+ if (Array.isArray(baseValue)) {
4634
+ const index = parseInt(indexStr, 10);
4635
+ return baseValue[index];
4636
+ }
4637
+ if (baseValue && typeof baseValue === "object") {
4638
+ return baseValue[indexStr.replace(/^["']|["']$/g, "")];
4639
+ }
4640
+ return void 0;
4641
+ }
4642
+ }
4643
+ return value[key];
4644
+ }, obj);
4645
+ }
4646
+ function valueToString2(value) {
4647
+ if (value === void 0 || value === null) {
4648
+ return "";
4649
+ }
4650
+ if (typeof value === "object") {
4651
+ return JSON.stringify(value);
4652
+ }
4653
+ return String(value);
4654
+ }
4655
+
4656
+ // src/utils/variables/simple.ts
4657
+ async function processSimpleVariable(variableString, variables, currentUrl) {
4658
+ const [variablePath, ...filterParts] = variableString.split("|").map((part) => part.trim());
4659
+ const value = resolveVariable2(variablePath, variables);
4660
+ const stringValue = valueToString2(value);
4661
+ const filtersString = filterParts.join("|");
4662
+ return applyFilters(stringValue, filtersString, currentUrl);
4663
+ }
4664
+
4665
+ // src/utils/variables/schema.ts
4666
+ function splitListString(str) {
4667
+ return str.split(/(?=\d+\.|[-*•]\s)/).map(
4668
+ (item) => item.replace(/^(?:\d+\.|[-*•])\s*/, "").trim()
4669
+ ).filter((item) => item.length > 0);
4670
+ }
4671
+ async function processSchema(match, variables, currentUrl) {
4672
+ const [, fullSchemaKey] = match.match(/{{schema:(.*?)}}/) || [];
4673
+ if (!fullSchemaKey) {
4674
+ return "";
4675
+ }
4676
+ const [schemaKey, ...filterParts] = fullSchemaKey.split("|");
4677
+ const filtersString = filterParts.join("|");
4678
+ let schemaValue = "";
4679
+ const nestedArrayMatch = schemaKey.match(/(.*?)\[(\*|\d+)\](.*)/);
4680
+ if (nestedArrayMatch) {
4681
+ const [, arrayKey, indexOrStar, propertyKey] = nestedArrayMatch;
4682
+ let fullArrayKey = arrayKey;
4683
+ if (!arrayKey.includes("@")) {
4684
+ const matchingKey = Object.keys(variables).find((key) => key.includes("@") && key.endsWith(`:${arrayKey}}}`));
4685
+ if (matchingKey) {
4686
+ fullArrayKey = matchingKey.replace("{{schema:", "").replace("}}", "");
4687
+ }
4688
+ }
4689
+ try {
4690
+ const rawValue = variables[`{{schema:${fullArrayKey}}}`] || "[]";
4691
+ if (rawValue.trim().match(/^(?:\d+\.|[-*•]\s)/m)) {
4692
+ const list2 = splitListString(rawValue);
4693
+ if (indexOrStar === "*") {
4694
+ schemaValue = JSON.stringify(list2);
4695
+ } else {
4696
+ const index = parseInt(indexOrStar, 10);
4697
+ schemaValue = list2[index] || "";
4698
+ }
4699
+ } else {
4700
+ const arrayValue = JSON.parse(rawValue);
4701
+ if (Array.isArray(arrayValue)) {
4702
+ if (indexOrStar === "*") {
4703
+ schemaValue = JSON.stringify(arrayValue.map((item) => getNestedProperty3(item, propertyKey.slice(1))).filter(Boolean));
4704
+ } else {
4705
+ const index = parseInt(indexOrStar, 10);
4706
+ schemaValue = arrayValue[index] ? getNestedProperty3(arrayValue[index], propertyKey.slice(1)) : "";
4707
+ }
4708
+ }
4709
+ }
4710
+ } catch (error) {
4711
+ console.error("Error processing schema array:", error);
4712
+ console.error("Raw value:", variables[`{{schema:${fullArrayKey}}}`]);
4713
+ return "";
4714
+ }
4715
+ } else {
4716
+ if (!schemaKey.includes("@")) {
4717
+ const matchingKey = Object.keys(variables).find((key) => key.includes("@") && key.endsWith(`:${schemaKey}}}`));
4718
+ if (matchingKey) {
4719
+ schemaValue = variables[matchingKey];
4720
+ }
4721
+ }
4722
+ if (!schemaValue) {
4723
+ schemaValue = variables[`{{schema:${schemaKey}}}`] || "";
4724
+ }
4725
+ }
4726
+ return applyFilters(schemaValue, filtersString, currentUrl);
4727
+ }
4728
+ function getNestedProperty3(obj, path) {
4729
+ return path.split(".").reduce((prev, curr) => prev && prev[curr], obj);
4730
+ }
4731
+
4732
+ // src/utils/storage-utils.ts
4733
+ var generalSettings2 = {
4734
+ vaults: [],
4735
+ betaFeatures: false,
4736
+ legacyMode: false,
4737
+ silentOpen: false,
4738
+ openBehavior: "popup",
4739
+ highlighterEnabled: true,
4740
+ alwaysShowHighlights: false,
4741
+ highlightBehavior: "highlight-inline",
4742
+ showMoreActionsButton: false,
4743
+ interpreterModel: "",
4744
+ models: [],
4745
+ providers: [],
4746
+ interpreterEnabled: false,
4747
+ interpreterAutoRun: false,
4748
+ defaultPromptContext: "",
4749
+ propertyTypes: [],
4750
+ readerSettings: {
4751
+ fontSize: 16,
4752
+ lineHeight: 1.6,
4753
+ maxWidth: 38,
4754
+ lightTheme: "default",
4755
+ darkTheme: "same",
4756
+ appearance: "auto",
4757
+ fonts: [],
4758
+ defaultFont: "",
4759
+ blendImages: true,
4760
+ colorLinks: false,
4761
+ followLinks: true,
4762
+ pinPlayer: true,
4763
+ autoScroll: true,
4764
+ highlightActiveLine: true,
4765
+ customCss: ""
4766
+ },
4767
+ stats: {
4768
+ addToObsidian: 0,
4769
+ saveFile: 0,
4770
+ copyToClipboard: 0,
4771
+ share: 0
4772
+ },
4773
+ history: [],
4774
+ ratings: [],
4775
+ saveBehavior: "addToObsidian"
4776
+ };
4777
+ if (typeof window !== "undefined") {
4778
+ window.debugStorage = (key) => {
4779
+ if (key) {
4780
+ return browser_polyfill_default.storage.sync.get(key).then((data) => {
4781
+ console.log(`Sync storage contents for key "${key}":`, data);
4782
+ return data;
4783
+ });
4784
+ }
4785
+ return browser_polyfill_default.storage.sync.get(null).then((data) => {
4786
+ console.log("Sync storage contents:", data);
4787
+ return data;
4788
+ });
4789
+ };
4790
+ }
4791
+
4792
+ // src/utils/variables/prompt.ts
4793
+ async function processPrompt(match, variables, currentUrl) {
4794
+ if (generalSettings2.interpreterEnabled) {
4795
+ const promptRegex = /{{(?:prompt:)?"(.*?)"(\|.*?)?}}/;
4796
+ const matches = match.match(promptRegex);
4797
+ if (!matches) {
4798
+ console.error("Invalid prompt format:", match);
4799
+ return match;
4800
+ }
4801
+ const [, promptText, filters2 = ""] = matches;
4802
+ return match;
4803
+ } else {
4804
+ return "";
4805
+ }
4806
+ }
4807
+
4808
+ // src/utils/template-compiler.ts
4809
+ async function compileTemplate(tabId, text, variables, currentUrl, customAsyncResolver, customSelectorProcessor) {
4810
+ currentUrl = currentUrl.replace(/#:~:text=[^&]+(&|$)/, "");
4811
+ const asyncResolver = customAsyncResolver ?? (async (name, ctx) => {
4812
+ if (name.startsWith("selector:") || name.startsWith("selectorHtml:")) {
4813
+ return resolveSelector(ctx.tabId, name);
4814
+ }
4815
+ return void 0;
4816
+ });
4817
+ const context = {
4818
+ variables,
4819
+ currentUrl,
4820
+ tabId,
4821
+ applyFilterDirect,
4822
+ asyncResolver
4823
+ };
4824
+ const result = await render(text, context);
4825
+ if (result.errors.length > 0) {
4826
+ console.error("Template compilation errors:", result.errors.map((e) => `Line ${e.line}: ${e.message}`).join("; "));
4827
+ }
4828
+ if (!result.hasDeferredVariables) {
4829
+ return result.output;
4830
+ }
4831
+ const processedText = await processVariables(tabId, result.output, variables, currentUrl, customSelectorProcessor);
4832
+ return processedText;
4833
+ }
4834
+ async function processVariables(tabId, text, variables, currentUrl, customSelectorProcessor) {
4835
+ const regex = /{{([\s\S]*?)}}/g;
4836
+ let result = text;
4837
+ let match;
4838
+ while ((match = regex.exec(result)) !== null) {
4839
+ const fullMatch = match[0];
4840
+ const trimmedMatch = match[1].trim();
4841
+ let replacement;
4842
+ if (trimmedMatch.startsWith("selector:") || trimmedMatch.startsWith("selectorHtml:")) {
4843
+ if (customSelectorProcessor) {
4844
+ replacement = await customSelectorProcessor(fullMatch, currentUrl);
4845
+ } else {
4846
+ replacement = await processSelector(tabId, fullMatch, currentUrl);
4847
+ }
4848
+ } else if (trimmedMatch.startsWith("schema:")) {
4849
+ replacement = await processSchema(fullMatch, variables, currentUrl);
4850
+ } else if (trimmedMatch.startsWith('"') || trimmedMatch.startsWith("prompt:")) {
4851
+ replacement = await processPrompt(fullMatch, variables, currentUrl);
4852
+ } else {
4853
+ replacement = await processSimpleVariable(trimmedMatch, variables, currentUrl);
4854
+ }
4855
+ result = result.substring(0, match.index) + replacement + result.substring(match.index + fullMatch.length);
4856
+ regex.lastIndex = match.index + replacement.length;
4857
+ }
4858
+ return result;
4859
+ }
4860
+
4861
+ // src/api.ts
4862
+ function createAsyncResolver(doc) {
4863
+ return async (name, _context) => {
4864
+ if (name.startsWith("selector:") || name.startsWith("selectorHtml:")) {
4865
+ const extractHtml = name.startsWith("selectorHtml:");
4866
+ const prefix = extractHtml ? "selectorHtml:" : "selector:";
4867
+ const selectorPart = name.slice(prefix.length);
4868
+ const attrMatch = selectorPart.match(/^(.+?)\?(.+)$/);
4869
+ const selector = attrMatch ? attrMatch[1] : selectorPart;
4870
+ const attribute = attrMatch ? attrMatch[2] : void 0;
4871
+ return extractContentBySelector(
4872
+ doc,
4873
+ selector.replace(/\\"/g, '"'),
4874
+ attribute,
4875
+ extractHtml
4876
+ );
4877
+ }
4878
+ return void 0;
4879
+ };
4880
+ }
4881
+ function createSelectorProcessor(doc) {
4882
+ return async (match, currentUrl) => {
4883
+ const selectorRegex = /{{(selector|selectorHtml):(.*?)(?:\?(.*?))?(?:\|(.*?))?}}/;
4884
+ const matches = match.match(selectorRegex);
4885
+ if (!matches) return match;
4886
+ const [, selectorType, rawSelector, attribute, filtersString] = matches;
4887
+ const extractHtml = selectorType === "selectorHtml";
4888
+ const selector = rawSelector.replace(/\\"/g, '"').replace(/\s+/g, " ").trim();
4889
+ const content = extractContentBySelector(doc, selector, attribute, extractHtml);
4890
+ const contentString = selectorContentToString(content);
4891
+ return filtersString ? applyFilters(contentString, filtersString, currentUrl) : contentString;
4892
+ };
4893
+ }
4894
+ function matchTriggerPattern(pattern, url) {
4895
+ if (pattern.startsWith("/") && pattern.endsWith("/")) {
4896
+ try {
4897
+ return new RegExp(pattern.slice(1, -1)).test(url);
4898
+ } catch {
4899
+ return false;
4900
+ }
4901
+ }
4902
+ return url.startsWith(pattern);
4903
+ }
4904
+ function matchSchemaPattern(pattern, schemaOrgData) {
4905
+ const match = pattern.match(/^schema:(@\w+)?(?:\.(.+?))?(?:=(.+))?$/);
4906
+ if (!match) return false;
4907
+ const [, schemaType, schemaKey, expectedValue] = match;
4908
+ if (!schemaType && !schemaKey) return false;
4909
+ const schemaArray = Array.isArray(schemaOrgData) ? schemaOrgData : [schemaOrgData];
4910
+ const flattened = schemaArray.flatMap((s) => Array.isArray(s) ? s : [s]);
4911
+ for (const schema of flattened) {
4912
+ if (!schema || typeof schema !== "object") continue;
4913
+ if (schemaType) {
4914
+ const types = Array.isArray(schema["@type"]) ? schema["@type"] : [schema["@type"]];
4915
+ if (!types.includes(schemaType.slice(1))) continue;
4916
+ }
4917
+ if (schemaKey) {
4918
+ const keys = schemaKey.split(".");
4919
+ let val = schema;
4920
+ for (const k of keys) {
4921
+ val = val && typeof val === "object" && k in val ? val[k] : void 0;
4922
+ }
4923
+ if (expectedValue) {
4924
+ if (Array.isArray(val) ? val.includes(expectedValue) : val === expectedValue) return true;
4925
+ } else if (val !== void 0) {
4926
+ return true;
4927
+ }
4928
+ } else {
4929
+ return true;
4930
+ }
4931
+ }
4932
+ return false;
4933
+ }
4934
+ function matchTemplate(templates, url, schemaOrgData) {
4935
+ for (const template2 of templates) {
4936
+ if (!template2.triggers) continue;
4937
+ for (const trigger of template2.triggers) {
4938
+ if (!trigger.startsWith("schema:") && matchTriggerPattern(trigger, url)) {
4939
+ return template2;
4940
+ }
4941
+ }
4942
+ }
4943
+ if (schemaOrgData) {
4944
+ for (const template2 of templates) {
4945
+ if (!template2.triggers) continue;
4946
+ for (const trigger of template2.triggers) {
4947
+ if (trigger.startsWith("schema:") && matchSchemaPattern(trigger, schemaOrgData)) {
4948
+ return template2;
4949
+ }
4950
+ }
4951
+ }
4952
+ }
4953
+ return void 0;
4954
+ }
4955
+ async function clip(options) {
4956
+ const { html, url, template: template2, documentParser, propertyTypes, parsedDocument } = options;
4957
+ const doc = parsedDocument ?? documentParser.parseFromString(html, "text/html");
4958
+ const defuddle = new DefuddleClass(doc, { url });
4959
+ const defuddleResult = defuddle.parse();
4960
+ const markdownContent = createMarkdownContent2(defuddleResult.content, url);
4961
+ const variables = buildVariables({
4962
+ title: defuddleResult.title,
4963
+ author: defuddleResult.author,
4964
+ content: markdownContent,
4965
+ contentHtml: defuddleResult.content,
4966
+ url,
4967
+ fullHtml: html,
4968
+ description: defuddleResult.description,
4969
+ favicon: defuddleResult.favicon,
4970
+ image: defuddleResult.image,
4971
+ published: defuddleResult.published,
4972
+ site: defuddleResult.site,
4973
+ language: defuddleResult.language,
4974
+ wordCount: defuddleResult.wordCount,
4975
+ schemaOrgData: defuddleResult.schemaOrgData,
4976
+ metaTags: defuddleResult.metaTags,
4977
+ extractedContent: defuddleResult.variables
4978
+ });
4979
+ const asyncResolver = createAsyncResolver(doc);
4980
+ const selectorProcessor = createSelectorProcessor(doc);
4981
+ const compile = (text) => compileTemplate(0, text, variables, url, asyncResolver, selectorProcessor);
4982
+ const compiledNoteName = await compile(template2.noteNameFormat);
4983
+ const noteName = sanitizeFileName(compiledNoteName) || "Untitled";
4984
+ const compiledProperties = await Promise.all(
4985
+ template2.properties.map(async (prop) => {
4986
+ let value = await compile(prop.value);
4987
+ const propType = prop.type || "text";
4988
+ value = formatPropertyValue(value, propType, prop.value);
4989
+ return { name: prop.name, value, type: prop.type };
4990
+ })
4991
+ );
4992
+ const typeMap = {};
4993
+ for (const prop of template2.properties) {
4994
+ if (prop.type) {
4995
+ typeMap[prop.name] = prop.type;
4996
+ }
4997
+ }
4998
+ if (propertyTypes) {
4999
+ Object.assign(typeMap, propertyTypes);
5000
+ }
5001
+ const frontmatter = generateFrontmatter(compiledProperties, typeMap);
5002
+ const content = await compile(template2.noteContentFormat);
5003
+ const fullContent = frontmatter ? frontmatter + content : content;
5004
+ return {
5005
+ noteName,
5006
+ frontmatter,
5007
+ content,
5008
+ fullContent,
5009
+ properties: compiledProperties,
5010
+ variables
5011
+ };
5012
+ }
5013
+ export {
5014
+ clip,
5015
+ createAsyncResolver,
5016
+ createSelectorProcessor,
5017
+ matchTemplate
5018
+ };