@jaredwray/fumanchu 4.6.0 → 4.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3023 @@
1
+ import { CacheableMemory } from "@cacheable/memory";
2
+ import HandlebarsLib from "handlebars";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { parseDate } from "chrono-node";
6
+ import dayjs from "dayjs";
7
+ import isBetween from "dayjs/plugin/isBetween.js";
8
+ import relativeTime from "dayjs/plugin/relativeTime.js";
9
+ import timezone from "dayjs/plugin/timezone.js";
10
+ import utc from "dayjs/plugin/utc.js";
11
+ import micromatch from "micromatch";
12
+ import process$1 from "node:process";
13
+ import ent from "ent";
14
+ import MarkdownIt from "markdown-it";
15
+ import { escape } from "node:querystring";
16
+ import { format, parse, resolve } from "node:url";
17
+ //#region src/helper-registry-base.ts
18
+ /**
19
+ * Base class for helper registries. Holds all state-management logic and
20
+ * leaves `init()` empty so environment-specific subclasses decide which
21
+ * helper sets to register. This lets the browser registry avoid importing
22
+ * any Node-only helper files.
23
+ */
24
+ var HelperRegistryBase = class {
25
+ _helpers = [];
26
+ constructor() {
27
+ this.init();
28
+ }
29
+ /**
30
+ * Get all registered helpers.
31
+ */
32
+ get helpers() {
33
+ return this._helpers;
34
+ }
35
+ /**
36
+ * Initialize the helper registry. Overridden by subclasses.
37
+ */
38
+ init() {}
39
+ /**
40
+ * Register a helper. Returns true if the helper was added, false if a
41
+ * helper with the same name was already present.
42
+ */
43
+ register(helper) {
44
+ if (this.has(helper.name)) return false;
45
+ this._helpers.push(helper);
46
+ return true;
47
+ }
48
+ /**
49
+ * Register multiple helpers.
50
+ */
51
+ registerHelpers(helpers) {
52
+ for (const helper of helpers) this.register(helper);
53
+ }
54
+ /**
55
+ * Check if a helper is registered.
56
+ */
57
+ has(name) {
58
+ return this._helpers.some((helper) => helper.name === name);
59
+ }
60
+ /**
61
+ * Filter helpers by the given criteria.
62
+ */
63
+ filter(filter) {
64
+ let result = this._helpers;
65
+ if (filter.names) {
66
+ const names = filter.names;
67
+ result = result.filter((helper) => names.includes(helper.name));
68
+ }
69
+ if (filter.categories) {
70
+ const categories = filter.categories;
71
+ result = result.filter((helper) => categories.includes(helper.category));
72
+ }
73
+ if (filter.compatibility) {
74
+ const compatibility = filter.compatibility;
75
+ result = result.filter((helper) => helper.compatibility?.some((c) => compatibility.includes(c)));
76
+ }
77
+ return result;
78
+ }
79
+ /**
80
+ * Load Handlebars helpers.
81
+ */
82
+ load(handlebars, filters) {
83
+ let helpers = this._helpers;
84
+ if (filters) helpers = this.filter(filters);
85
+ for (const helper of helpers) handlebars.registerHelper(helper.name, helper.fn);
86
+ }
87
+ /**
88
+ * Swap Handlebars helpers. This is used when you want to swap out legacy
89
+ * helpers for fumanchu helpers.
90
+ */
91
+ swap(handlebars) {
92
+ for (const helper of this._helpers) handlebars.registerHelper(helper.name, helper.fn);
93
+ }
94
+ };
95
+ //#endregion
96
+ //#region src/helpers/array.ts
97
+ const after = (array, n) => {
98
+ if (!Array.isArray(array)) return [];
99
+ return array.slice(n);
100
+ };
101
+ const before = (array, n) => {
102
+ if (!Array.isArray(array)) return [];
103
+ return array.slice(0, -n);
104
+ };
105
+ const arrayify = (value) => {
106
+ if (value == null) return [];
107
+ return Array.isArray(value) ? value : [value];
108
+ };
109
+ const first = (array, n) => {
110
+ if (!Array.isArray(array)) return void 0;
111
+ if (typeof n !== "number") return array[0];
112
+ return array.slice(0, n);
113
+ };
114
+ const last = (value, n) => {
115
+ if (!Array.isArray(value) && typeof value !== "string") return;
116
+ if (typeof n !== "number") {
117
+ const array = value;
118
+ return array[array.length - 1];
119
+ }
120
+ const start = Math.abs(n);
121
+ if (typeof value === "string") return value.slice(-start);
122
+ return value.slice(-start);
123
+ };
124
+ const length = (value) => {
125
+ if (value && typeof value === "object" && !Array.isArray(value)) return Object.keys(value).length;
126
+ if (typeof value === "string" || Array.isArray(value)) return value.length;
127
+ return 0;
128
+ };
129
+ const join = (array, separator = ", ") => {
130
+ if (typeof array === "string") return array;
131
+ if (!Array.isArray(array)) return "";
132
+ return array.join(separator);
133
+ };
134
+ const forEach = function(collection, options) {
135
+ if (!Array.isArray(collection) || !options || typeof options.fn !== "function") return options?.inverse ? options.inverse(this) ?? "" : "";
136
+ if (collection.length === 0) return options.inverse ? options.inverse(this) ?? "" : "";
137
+ const total = collection.length;
138
+ const hash = options.hash ?? {};
139
+ const baseData = options.data ? { ...options.data } : void 0;
140
+ let result = "";
141
+ for (let index = 0; index < total; index++) {
142
+ const value = collection[index];
143
+ const meta = {
144
+ index,
145
+ total,
146
+ isFirst: index === 0,
147
+ isLast: index === total - 1
148
+ };
149
+ const data = {
150
+ ...baseData ? { ...baseData } : {},
151
+ ...hash,
152
+ ...meta
153
+ };
154
+ let context;
155
+ if (value != null && typeof value === "object") context = {
156
+ ...value,
157
+ ...hash,
158
+ ...meta
159
+ };
160
+ else context = {
161
+ value,
162
+ ...hash,
163
+ ...meta
164
+ };
165
+ result += options.fn(context, { data });
166
+ }
167
+ return result;
168
+ };
169
+ const inArray = function(array, value, options) {
170
+ if (!Array.isArray(array) || !options) return "";
171
+ const found = array.includes(value);
172
+ if (found && options.fn) return options.fn(this);
173
+ if (!found && options.inverse) return options.inverse(this);
174
+ return "";
175
+ };
176
+ const isArray = (value) => {
177
+ return Array.isArray(value);
178
+ };
179
+ const itemAt = (array, idx) => {
180
+ if (!Array.isArray(array)) return void 0;
181
+ return array[idx];
182
+ };
183
+ const equalsLength = function(value, targetLength, options) {
184
+ const isEqual = length(value) === targetLength;
185
+ if (options && (options.fn || options.inverse)) {
186
+ if (isEqual && options.fn) return options.fn(this);
187
+ if (!isEqual && options.inverse) return options.inverse(this);
188
+ return "";
189
+ }
190
+ return isEqual;
191
+ };
192
+ const some = function(array, iter, options) {
193
+ /* v8 ignore next -- @preserve */
194
+ if (!Array.isArray(array) || typeof iter !== "function" || !options) return options?.inverse ? options.inverse(this) ?? "" : "";
195
+ const hasSome = array.some(iter);
196
+ if (hasSome && options.fn) return options.fn(this);
197
+ if (!hasSome && options.inverse) return options.inverse(this);
198
+ return "";
199
+ };
200
+ const eachIndex = function(array, options) {
201
+ if (!Array.isArray(array) || !options || typeof options.fn !== "function") return options?.inverse ? options.inverse(this) ?? "" : "";
202
+ /* v8 ignore next -- @preserve */
203
+ if (array.length === 0) return options.inverse ? options.inverse(this) ?? "" : "";
204
+ let result = "";
205
+ for (let index = 0; index < array.length; index++) {
206
+ const context = {
207
+ item: array[index],
208
+ index
209
+ };
210
+ result += options.fn(context);
211
+ }
212
+ return result;
213
+ };
214
+ const withAfter = function(array, idx, options) {
215
+ if (!Array.isArray(array) || !options || typeof options.fn !== "function") return "";
216
+ const sliced = array.slice(idx + 1);
217
+ /* v8 ignore next -- @preserve */
218
+ if (sliced.length === 0) return options.inverse ? options.inverse(this) ?? "" : "";
219
+ let result = "";
220
+ for (const item of sliced) result += options.fn(item);
221
+ return result;
222
+ };
223
+ const withBefore = function(array, idx, options) {
224
+ if (!Array.isArray(array) || !options || typeof options.fn !== "function") return "";
225
+ const sliced = array.slice(0, idx);
226
+ /* v8 ignore next -- @preserve */
227
+ if (sliced.length === 0) return options.inverse ? options.inverse(this) ?? "" : "";
228
+ let result = "";
229
+ for (const item of sliced) result += options.fn(item);
230
+ return result;
231
+ };
232
+ const withFirst = function(array, n, options) {
233
+ let count = 1;
234
+ let opts = options;
235
+ if (typeof n === "object" && n !== null && !Array.isArray(n)) opts = n;
236
+ else if (typeof n === "number") count = n;
237
+ if (!Array.isArray(array) || !opts || typeof opts.fn !== "function") return "";
238
+ /* v8 ignore next -- @preserve */
239
+ if (array.length === 0) return opts.inverse ? opts.inverse(this) ?? "" : "";
240
+ const items = array.slice(0, count);
241
+ let result = "";
242
+ for (const item of items) result += opts.fn(item);
243
+ return result;
244
+ };
245
+ const withLast = function(array, n, options) {
246
+ let count = 1;
247
+ let opts = options;
248
+ if (typeof n === "object" && n !== null && !Array.isArray(n)) opts = n;
249
+ else if (typeof n === "number") count = n;
250
+ if (!Array.isArray(array) || !opts || typeof opts.fn !== "function") return "";
251
+ /* v8 ignore next -- @preserve */
252
+ if (array.length === 0) return opts.inverse ? opts.inverse(this) ?? "" : "";
253
+ const items = array.slice(-count);
254
+ let result = "";
255
+ for (const item of items) result += opts.fn(item);
256
+ return result;
257
+ };
258
+ const withGroup = function(array, size, options) {
259
+ if (!Array.isArray(array) || typeof size !== "number" || size <= 0 || !options || typeof options.fn !== "function") return "";
260
+ /* v8 ignore next -- @preserve */
261
+ if (array.length === 0) return options.inverse ? options.inverse(this) ?? "" : "";
262
+ let result = "";
263
+ const groups = [];
264
+ for (let i = 0; i < array.length; i += size) groups.push(array.slice(i, i + size));
265
+ for (const group of groups) result += options.fn(group);
266
+ return result;
267
+ };
268
+ const withSort = function(array, prop, options) {
269
+ let sortProp;
270
+ let opts = options;
271
+ if (typeof prop === "object" && prop !== null && !Array.isArray(prop)) opts = prop;
272
+ else if (typeof prop === "string") sortProp = prop;
273
+ if (!Array.isArray(array) || !opts || typeof opts.fn !== "function") return "";
274
+ /* v8 ignore next -- @preserve */
275
+ if (array.length === 0) return opts.inverse ? opts.inverse(this) ?? "" : "";
276
+ const sorted = [...array];
277
+ if (sortProp) sorted.sort((a, b) => {
278
+ const aVal = a?.[sortProp];
279
+ const bVal = b?.[sortProp];
280
+ /* v8 ignore next -- @preserve */
281
+ if (aVal === bVal) return 0;
282
+ if (aVal == null) return 1;
283
+ if (bVal == null) return -1;
284
+ /* v8 ignore next -- @preserve */
285
+ if (typeof aVal === "string" && typeof bVal === "string") return aVal.localeCompare(bVal);
286
+ /* v8 ignore next -- @preserve */
287
+ return aVal < bVal ? -1 : 1;
288
+ });
289
+ else sorted.sort((a, b) => {
290
+ /* v8 ignore next -- @preserve */
291
+ if (a === b) return 0;
292
+ if (a == null) return 1;
293
+ if (b == null) return -1;
294
+ if (typeof a === "string" && typeof b === "string") return a.localeCompare(b);
295
+ return a < b ? -1 : 1;
296
+ });
297
+ if (opts.hash?.reverse === true || opts.hash?.reverse === "true") sorted.reverse();
298
+ let result = "";
299
+ for (const item of sorted) result += opts.fn(item);
300
+ return result;
301
+ };
302
+ const filter = function(array, value, options) {
303
+ /* v8 ignore next -- @preserve */
304
+ if (!Array.isArray(array) || !options) return options?.inverse ? options.inverse(this) ?? "" : "";
305
+ const filtered = array.filter((item) => item === value);
306
+ if (filtered.length > 0 && options.fn) return options.fn(this);
307
+ /* v8 ignore next -- @preserve */
308
+ if (filtered.length === 0 && options.inverse) return options.inverse(this);
309
+ /* v8 ignore next -- @preserve */
310
+ return "";
311
+ };
312
+ const map = (array, fn) => {
313
+ if (!Array.isArray(array) || typeof fn !== "function") return [];
314
+ return array.map(fn);
315
+ };
316
+ const pluck = (collection, prop) => {
317
+ if (!Array.isArray(collection) || typeof prop !== "string") return [];
318
+ return collection.map((item) => {
319
+ if (item == null || typeof item !== "object") return;
320
+ const parts = prop.split(".");
321
+ let value = item;
322
+ for (const part of parts) {
323
+ /* v8 ignore next -- @preserve */
324
+ if (value == null || typeof value !== "object") return;
325
+ value = value[part];
326
+ }
327
+ return value;
328
+ });
329
+ };
330
+ const reverse$1 = (value) => {
331
+ if (typeof value === "string") return value.split("").reverse().join("");
332
+ if (Array.isArray(value)) return [...value].reverse();
333
+ };
334
+ const sort = (array, key) => {
335
+ if (!Array.isArray(array)) return [];
336
+ const sorted = [...array];
337
+ if (typeof key === "function") sorted.sort(key);
338
+ else if (typeof key === "string")
339
+ /* v8 ignore next -- @preserve */
340
+ sorted.sort((a, b) => {
341
+ const aVal = a?.[key];
342
+ const bVal = b?.[key];
343
+ /* v8 ignore next -- @preserve */
344
+ if (aVal === bVal) return 0;
345
+ /* v8 ignore next -- @preserve */
346
+ if (aVal == null) return 1;
347
+ if (bVal == null) return -1;
348
+ if (typeof aVal === "string" && typeof bVal === "string") return aVal.localeCompare(bVal);
349
+ /* v8 ignore next -- @preserve */
350
+ return aVal < bVal ? -1 : 1;
351
+ });
352
+ else sorted.sort((a, b) => {
353
+ /* v8 ignore next -- @preserve */
354
+ if (a === b) return 0;
355
+ if (a == null) return 1;
356
+ if (b == null) return -1;
357
+ if (typeof a === "string" && typeof b === "string") return a.localeCompare(b);
358
+ return a < b ? -1 : 1;
359
+ });
360
+ return sorted;
361
+ };
362
+ const sortBy = (array, ...props) => {
363
+ if (!Array.isArray(array) || props.length === 0) return [];
364
+ const sorted = [...array];
365
+ sorted.sort((a, b) => {
366
+ for (const prop of props) {
367
+ const aVal = a?.[prop];
368
+ const bVal = b?.[prop];
369
+ if (aVal === bVal) continue;
370
+ if (aVal == null) return 1;
371
+ if (bVal == null) return -1;
372
+ if (typeof aVal === "string" && typeof bVal === "string") {
373
+ const result = aVal.localeCompare(bVal);
374
+ /* v8 ignore next -- @preserve */
375
+ if (result !== 0) return result;
376
+ } else return aVal < bVal ? -1 : 1;
377
+ }
378
+ /* v8 ignore next -- @preserve */
379
+ return 0;
380
+ });
381
+ return sorted;
382
+ };
383
+ const unique = function(array, options) {
384
+ if (!Array.isArray(array)) {
385
+ /* v8 ignore next -- @preserve */
386
+ if (!options) return [];
387
+ /* v8 ignore next -- @preserve */
388
+ return options.inverse ? options.inverse(this) ?? "" : "";
389
+ }
390
+ const uniqueArray = [...new Set(array)];
391
+ if (!options) return uniqueArray;
392
+ if (uniqueArray.length === 0 && options.inverse) return options.inverse(this);
393
+ if (uniqueArray.length > 0 && options.fn) {
394
+ let result = "";
395
+ for (const item of uniqueArray) result += options.fn(item);
396
+ return result;
397
+ }
398
+ return "";
399
+ };
400
+ const helpers$23 = [
401
+ {
402
+ name: "after",
403
+ category: "array",
404
+ compatibility: ["browser", "nodejs"],
405
+ fn: after
406
+ },
407
+ {
408
+ name: "before",
409
+ category: "array",
410
+ compatibility: ["browser", "nodejs"],
411
+ fn: before
412
+ },
413
+ {
414
+ name: "arrayify",
415
+ category: "array",
416
+ compatibility: ["browser", "nodejs"],
417
+ fn: arrayify
418
+ },
419
+ {
420
+ name: "first",
421
+ category: "array",
422
+ compatibility: ["browser", "nodejs"],
423
+ fn: first
424
+ },
425
+ {
426
+ name: "last",
427
+ category: "array",
428
+ compatibility: ["browser", "nodejs"],
429
+ fn: last
430
+ },
431
+ {
432
+ name: "length",
433
+ category: "array",
434
+ compatibility: ["browser", "nodejs"],
435
+ fn: length
436
+ },
437
+ {
438
+ name: "join",
439
+ category: "array",
440
+ compatibility: ["browser", "nodejs"],
441
+ fn: join
442
+ },
443
+ {
444
+ name: "forEach",
445
+ category: "array",
446
+ compatibility: ["browser", "nodejs"],
447
+ fn: forEach
448
+ },
449
+ {
450
+ name: "inArray",
451
+ category: "array",
452
+ compatibility: ["browser", "nodejs"],
453
+ fn: inArray
454
+ },
455
+ {
456
+ name: "isArray",
457
+ category: "array",
458
+ compatibility: ["browser", "nodejs"],
459
+ fn: isArray
460
+ },
461
+ {
462
+ name: "itemAt",
463
+ category: "array",
464
+ compatibility: ["browser", "nodejs"],
465
+ fn: itemAt
466
+ },
467
+ {
468
+ name: "equalsLength",
469
+ category: "array",
470
+ compatibility: ["browser", "nodejs"],
471
+ fn: equalsLength
472
+ },
473
+ {
474
+ name: "some",
475
+ category: "array",
476
+ compatibility: ["browser", "nodejs"],
477
+ fn: some
478
+ },
479
+ {
480
+ name: "eachIndex",
481
+ category: "array",
482
+ compatibility: ["browser", "nodejs"],
483
+ fn: eachIndex
484
+ },
485
+ {
486
+ name: "withAfter",
487
+ category: "array",
488
+ compatibility: ["browser", "nodejs"],
489
+ fn: withAfter
490
+ },
491
+ {
492
+ name: "withBefore",
493
+ category: "array",
494
+ compatibility: ["browser", "nodejs"],
495
+ fn: withBefore
496
+ },
497
+ {
498
+ name: "withFirst",
499
+ category: "array",
500
+ compatibility: ["browser", "nodejs"],
501
+ fn: withFirst
502
+ },
503
+ {
504
+ name: "withLast",
505
+ category: "array",
506
+ compatibility: ["browser", "nodejs"],
507
+ fn: withLast
508
+ },
509
+ {
510
+ name: "withGroup",
511
+ category: "array",
512
+ compatibility: ["browser", "nodejs"],
513
+ fn: withGroup
514
+ },
515
+ {
516
+ name: "withSort",
517
+ category: "array",
518
+ compatibility: ["browser", "nodejs"],
519
+ fn: withSort
520
+ },
521
+ {
522
+ name: "filter",
523
+ category: "array",
524
+ compatibility: ["browser", "nodejs"],
525
+ fn: filter
526
+ },
527
+ {
528
+ name: "map",
529
+ category: "array",
530
+ compatibility: ["browser", "nodejs"],
531
+ fn: map
532
+ },
533
+ {
534
+ name: "pluck",
535
+ category: "array",
536
+ compatibility: ["browser", "nodejs"],
537
+ fn: pluck
538
+ },
539
+ {
540
+ name: "reverse",
541
+ category: "array",
542
+ compatibility: ["browser", "nodejs"],
543
+ fn: reverse$1
544
+ },
545
+ {
546
+ name: "sort",
547
+ category: "array",
548
+ compatibility: ["browser", "nodejs"],
549
+ fn: sort
550
+ },
551
+ {
552
+ name: "sortBy",
553
+ category: "array",
554
+ compatibility: ["browser", "nodejs"],
555
+ fn: sortBy
556
+ },
557
+ {
558
+ name: "unique",
559
+ category: "array",
560
+ compatibility: ["browser", "nodejs"],
561
+ fn: unique
562
+ }
563
+ ];
564
+ //#endregion
565
+ //#region src/helpers/html.ts
566
+ const isTagStart = (ch) => ch !== void 0 && (ch === "/" || ch === "!" || ch === "?" || /[a-zA-Z]/.test(ch));
567
+ const stripTags = (str) => {
568
+ let result = "";
569
+ let inTag = false;
570
+ let inComment = false;
571
+ let quote = null;
572
+ for (let i = 0; i < str.length; i++) {
573
+ const ch = str[i];
574
+ if (inComment) {
575
+ if (ch === "-" && str[i + 1] === "-" && str[i + 2] === ">") {
576
+ inComment = false;
577
+ i += 2;
578
+ }
579
+ continue;
580
+ }
581
+ if (inTag) {
582
+ if (quote) {
583
+ if (ch === quote) quote = null;
584
+ } else if (ch === "\"" || ch === "'") quote = ch;
585
+ else if (ch === ">") inTag = false;
586
+ continue;
587
+ }
588
+ if (ch === "<") {
589
+ if (str[i + 1] === "!" && str[i + 2] === "-" && str[i + 3] === "-") {
590
+ inComment = true;
591
+ i += 3;
592
+ continue;
593
+ }
594
+ if (isTagStart(str[i + 1])) {
595
+ inTag = true;
596
+ continue;
597
+ }
598
+ }
599
+ result += ch;
600
+ }
601
+ return result;
602
+ };
603
+ const VOID_ELEMENTS = new Set([
604
+ "area",
605
+ "base",
606
+ "br",
607
+ "col",
608
+ "command",
609
+ "embed",
610
+ "hr",
611
+ "img",
612
+ "input",
613
+ "keygen",
614
+ "link",
615
+ "meta",
616
+ "param",
617
+ "source",
618
+ "track",
619
+ "wbr",
620
+ "circle",
621
+ "ellipse",
622
+ "line",
623
+ "path",
624
+ "polygon",
625
+ "polyline",
626
+ "rect",
627
+ "stop",
628
+ "use"
629
+ ]);
630
+ const tag = (name, attribs = {}, text = "") => {
631
+ let html = `<${name}`;
632
+ for (const [key, val] of Object.entries(attribs)) if (val === true) html += ` ${key}`;
633
+ else if (typeof val === "string" || typeof val === "number") html += ` ${key}="${escapeAttr(String(val))}"`;
634
+ if (VOID_ELEMENTS.has(name.toLowerCase())) return `${html}>`;
635
+ return `${html}>${escapeText(text)}</${name}>`;
636
+ };
637
+ const escapeAttr = (str) => str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
638
+ const escapeText = (str) => str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
639
+ const parseAttributes = (hash = {}) => Object.keys(hash).map((key) => `${key}="${String(hash[key]).replace(/^['"]|["']$/g, "")}"`).join(" ");
640
+ const attr = (options) => {
641
+ const val = parseAttributes(options?.hash ?? {});
642
+ return val.trim() ? ` ${val}` : "";
643
+ };
644
+ const sanitize = (str) => {
645
+ if (typeof str !== "string") return "";
646
+ return stripTags(str).trim();
647
+ };
648
+ const ul = (context, options) => {
649
+ const attrs = parseAttributes(options.hash ?? {});
650
+ const items = context.map((item) => typeof item === "string" ? item : options.fn(item));
651
+ return `${attrs ? `<ul ${attrs}>` : "<ul>"}${items.map((i) => `<li>${i}</li>`).join("\n")}</ul>`;
652
+ };
653
+ const ol = (context, options) => {
654
+ const attrs = parseAttributes(options.hash ?? {});
655
+ const items = context.map((item) => typeof item === "string" ? item : options.fn(item));
656
+ return `${attrs ? `<ol ${attrs}>` : "<ol>"}${items.map((i) => `<li>${i}</li>`).join("\n")}</ol>`;
657
+ };
658
+ const thumbnailImage = (context) => {
659
+ let figure = "";
660
+ let image = "";
661
+ const link = context.full || false;
662
+ const imageAttributes = {
663
+ alt: context.alt,
664
+ src: context.thumbnail,
665
+ width: context.size.width,
666
+ height: context.size.height
667
+ };
668
+ const figureAttributes = { id: `image-${context.id}` };
669
+ const linkAttributes = {
670
+ href: link,
671
+ rel: "thumbnail"
672
+ };
673
+ /* c8 ignore next -- @preserve */
674
+ if (context.classes) {
675
+ if (context.classes.image) imageAttributes.class = context.classes.image.join(" ");
676
+ if (context.classes.figure) figureAttributes.class = context.classes.figure.join(" ");
677
+ if (context.classes.link) linkAttributes.class = context.classes.link.join(" ");
678
+ }
679
+ figure += `<figure ${parseAttributes(figureAttributes)}>\n`;
680
+ image += `<img ${parseAttributes(imageAttributes)}>\n`;
681
+ if (link) figure += `<a ${parseAttributes(linkAttributes)}>\n${image}</a>\n`;
682
+ else figure += image;
683
+ /* c8 ignore next -- @preserve */
684
+ if (context.caption) figure += `<figcaption>${context.caption}</figcaption>\n`;
685
+ figure += "</figure>";
686
+ return figure;
687
+ };
688
+ const helpers$22 = [
689
+ {
690
+ name: "attr",
691
+ category: "html",
692
+ compatibility: ["browser", "nodejs"],
693
+ fn: attr
694
+ },
695
+ {
696
+ name: "sanitize",
697
+ category: "html",
698
+ compatibility: ["browser", "nodejs"],
699
+ fn: sanitize
700
+ },
701
+ {
702
+ name: "ul",
703
+ category: "html",
704
+ compatibility: ["browser", "nodejs"],
705
+ fn: ul
706
+ },
707
+ {
708
+ name: "ol",
709
+ category: "html",
710
+ compatibility: ["browser", "nodejs"],
711
+ fn: ol
712
+ },
713
+ {
714
+ name: "thumbnailImage",
715
+ category: "html",
716
+ compatibility: ["browser", "nodejs"],
717
+ fn: thumbnailImage
718
+ }
719
+ ];
720
+ //#endregion
721
+ //#region src/helpers/code.ts
722
+ const gist = (id) => {
723
+ return tag("script", { src: `https://gist.github.com/${id}.js` });
724
+ };
725
+ const jsfiddle = (options) => {
726
+ if (!options?.id) throw new Error("jsfiddle helper expects an `id`");
727
+ const { id, width = "100%", height = "300", skin = "/presentation/", tabs = "result,js,html,css", allowfullscreen = "allowfullscreen", frameborder = "0" } = options;
728
+ return tag("iframe", {
729
+ width,
730
+ height,
731
+ src: `http://jsfiddle.net/${id}/embedded/${tabs}${skin}`,
732
+ allowfullscreen,
733
+ frameborder
734
+ });
735
+ };
736
+ const helpers$21 = [{
737
+ name: "gist",
738
+ category: "code",
739
+ compatibility: ["browser", "nodejs"],
740
+ fn: gist
741
+ }, {
742
+ name: "jsfiddle",
743
+ category: "code",
744
+ compatibility: ["browser", "nodejs"],
745
+ fn: jsfiddle
746
+ }];
747
+ //#endregion
748
+ //#region src/helpers/code.node.ts
749
+ const codeBlock = (code, lang) => {
750
+ return `\`\`\`${lang}\n${code}\n\`\`\`\n`;
751
+ };
752
+ const embed = (filepath, language) => {
753
+ let ext = typeof language === "string" ? language : path.extname(filepath).slice(1);
754
+ let code = fs.readFileSync(filepath, "utf8");
755
+ if (ext === "markdown" || ext === "md") {
756
+ ext = "markdown";
757
+ code = code.split("`").join("&#x60");
758
+ }
759
+ return `${codeBlock(code, ext).trim()}\n`;
760
+ };
761
+ const helpers$20 = [{
762
+ name: "embed",
763
+ category: "code",
764
+ compatibility: ["nodejs"],
765
+ fn: embed
766
+ }];
767
+ //#endregion
768
+ //#region src/helpers/collection.ts
769
+ const isEmpty = (collection) => {
770
+ if (collection == null) return true;
771
+ if (Array.isArray(collection)) return collection.length === 0;
772
+ if (typeof collection === "object") return Object.keys(collection).length === 0;
773
+ return false;
774
+ };
775
+ const iterate = (collection, fn, inverse) => {
776
+ if (Array.isArray(collection)) {
777
+ let result = "";
778
+ for (let i = 0; i < collection.length; i++) result += fn(collection[i], i);
779
+ return result;
780
+ }
781
+ if (collection && typeof collection === "object") {
782
+ let result = "";
783
+ for (const key of Object.keys(collection)) result += fn(collection[key], key);
784
+ return result;
785
+ }
786
+ return inverse ? inverse() : "";
787
+ };
788
+ const helpers$19 = [{
789
+ name: "isEmpty",
790
+ category: "collection",
791
+ compatibility: ["browser", "nodejs"],
792
+ fn: isEmpty
793
+ }, {
794
+ name: "iterate",
795
+ category: "collection",
796
+ compatibility: ["browser", "nodejs"],
797
+ fn: iterate
798
+ }];
799
+ //#endregion
800
+ //#region src/helpers/comparison.ts
801
+ const and = (...values) => values.every(Boolean);
802
+ const compare = (a, operator, b) => {
803
+ switch (operator) {
804
+ case "==": return a == b;
805
+ case "===": return a === b;
806
+ case "!=": return a != b;
807
+ case "!==": return a !== b;
808
+ case "<": return a < b;
809
+ case ">": return a > b;
810
+ case "<=": return a <= b;
811
+ case ">=": return a >= b;
812
+ case "typeof": return typeof a === b;
813
+ default: throw new Error(`helper {{compare}}: invalid operator: '${operator}'`);
814
+ }
815
+ };
816
+ const contains = (collection, value, startIndex = 0) => {
817
+ if (collection == null) return false;
818
+ if (typeof collection === "string" || Array.isArray(collection)) return collection.indexOf(value, startIndex) > -1;
819
+ if (typeof collection === "object") return value in collection;
820
+ return false;
821
+ };
822
+ const defaultHelper = (...values) => {
823
+ for (const val of values) if (val != null) return val;
824
+ return "";
825
+ };
826
+ const eq = (a, b) => a === b;
827
+ const gt = (a, b) => a > b;
828
+ const gte = (a, b) => a >= b;
829
+ const has = (value, pattern) => {
830
+ if (value == null || pattern == null) return false;
831
+ if (Array.isArray(value) || typeof value === "string") return value.indexOf(pattern) > -1;
832
+ if (typeof value === "object") return pattern in value;
833
+ return false;
834
+ };
835
+ const falsey = (val, keywords) => {
836
+ if (!val) return true;
837
+ const words = Array.isArray(keywords) ? keywords : keywords != null ? [keywords] : [
838
+ "0",
839
+ "false",
840
+ "nada",
841
+ "nil",
842
+ "nay",
843
+ "nah",
844
+ "negative",
845
+ "no",
846
+ "none",
847
+ "nope",
848
+ "nul",
849
+ "null",
850
+ "nix",
851
+ "nyet",
852
+ "uh-uh",
853
+ "veto",
854
+ "zero"
855
+ ];
856
+ const lower = typeof val === "string" ? val.toLowerCase() : null;
857
+ for (const word of words) {
858
+ if (word === val) return true;
859
+ if (lower != null && word === lower) return true;
860
+ }
861
+ return false;
862
+ };
863
+ const isFalsey = (val) => falsey(val);
864
+ const isTruthy = (val) => !falsey(val);
865
+ const ifEven = (num) => typeof num === "number" && num % 2 === 0;
866
+ const ifNth = (a, b) => typeof a === "number" && typeof b === "number" && b % a === 0;
867
+ const ifOdd = (val) => typeof val === "number" && Math.abs(val % 2) === 1;
868
+ const is = (a, b) => a == b;
869
+ const isnt = (a, b) => a != b;
870
+ const lt = (a, b) => a < b;
871
+ const lte = (a, b) => a <= b;
872
+ const neither = (a, b) => !a && !b;
873
+ const not = (val) => !val;
874
+ const or = (...values) => values.some(Boolean);
875
+ const unlessEq = (a, b) => a !== b;
876
+ const unlessGt = (a, b) => a <= b;
877
+ const unlessLt = (a, b) => a >= b;
878
+ const unlessGteq = (a, b) => a < b;
879
+ const unlessLteq = (a, b) => a > b;
880
+ const helpers$18 = [
881
+ {
882
+ name: "and",
883
+ category: "comparison",
884
+ compatibility: ["browser", "nodejs"],
885
+ fn: and
886
+ },
887
+ {
888
+ name: "compare",
889
+ category: "comparison",
890
+ compatibility: ["browser", "nodejs"],
891
+ fn: compare
892
+ },
893
+ {
894
+ name: "contains",
895
+ category: "comparison",
896
+ compatibility: ["browser", "nodejs"],
897
+ fn: contains
898
+ },
899
+ {
900
+ name: "default",
901
+ category: "comparison",
902
+ compatibility: ["browser", "nodejs"],
903
+ fn: defaultHelper
904
+ },
905
+ {
906
+ name: "eq",
907
+ category: "comparison",
908
+ compatibility: ["browser", "nodejs"],
909
+ fn: eq
910
+ },
911
+ {
912
+ name: "gt",
913
+ category: "comparison",
914
+ compatibility: ["browser", "nodejs"],
915
+ fn: gt
916
+ },
917
+ {
918
+ name: "gte",
919
+ category: "comparison",
920
+ compatibility: ["browser", "nodejs"],
921
+ fn: gte
922
+ },
923
+ {
924
+ name: "has",
925
+ category: "comparison",
926
+ compatibility: ["browser", "nodejs"],
927
+ fn: has
928
+ },
929
+ {
930
+ name: "isFalsey",
931
+ category: "comparison",
932
+ compatibility: ["browser", "nodejs"],
933
+ fn: isFalsey
934
+ },
935
+ {
936
+ name: "isTruthy",
937
+ category: "comparison",
938
+ compatibility: ["browser", "nodejs"],
939
+ fn: isTruthy
940
+ },
941
+ {
942
+ name: "ifEven",
943
+ category: "comparison",
944
+ compatibility: ["browser", "nodejs"],
945
+ fn: ifEven
946
+ },
947
+ {
948
+ name: "ifNth",
949
+ category: "comparison",
950
+ compatibility: ["browser", "nodejs"],
951
+ fn: ifNth
952
+ },
953
+ {
954
+ name: "ifOdd",
955
+ category: "comparison",
956
+ compatibility: ["browser", "nodejs"],
957
+ fn: ifOdd
958
+ },
959
+ {
960
+ name: "is",
961
+ category: "comparison",
962
+ compatibility: ["browser", "nodejs"],
963
+ fn: is
964
+ },
965
+ {
966
+ name: "isnt",
967
+ category: "comparison",
968
+ compatibility: ["browser", "nodejs"],
969
+ fn: isnt
970
+ },
971
+ {
972
+ name: "lt",
973
+ category: "comparison",
974
+ compatibility: ["browser", "nodejs"],
975
+ fn: lt
976
+ },
977
+ {
978
+ name: "lte",
979
+ category: "comparison",
980
+ compatibility: ["browser", "nodejs"],
981
+ fn: lte
982
+ },
983
+ {
984
+ name: "neither",
985
+ category: "comparison",
986
+ compatibility: ["browser", "nodejs"],
987
+ fn: neither
988
+ },
989
+ {
990
+ name: "not",
991
+ category: "comparison",
992
+ compatibility: ["browser", "nodejs"],
993
+ fn: not
994
+ },
995
+ {
996
+ name: "or",
997
+ category: "comparison",
998
+ compatibility: ["browser", "nodejs"],
999
+ fn: or
1000
+ },
1001
+ {
1002
+ name: "unlessEq",
1003
+ category: "comparison",
1004
+ compatibility: ["browser", "nodejs"],
1005
+ fn: unlessEq
1006
+ },
1007
+ {
1008
+ name: "unlessGt",
1009
+ category: "comparison",
1010
+ compatibility: ["browser", "nodejs"],
1011
+ fn: unlessGt
1012
+ },
1013
+ {
1014
+ name: "unlessLt",
1015
+ category: "comparison",
1016
+ compatibility: ["browser", "nodejs"],
1017
+ fn: unlessLt
1018
+ },
1019
+ {
1020
+ name: "unlessGteq",
1021
+ category: "comparison",
1022
+ compatibility: ["browser", "nodejs"],
1023
+ fn: unlessGteq
1024
+ },
1025
+ {
1026
+ name: "unlessLteq",
1027
+ category: "comparison",
1028
+ compatibility: ["browser", "nodejs"],
1029
+ fn: unlessLteq
1030
+ }
1031
+ ];
1032
+ //#endregion
1033
+ //#region src/helpers/date.ts
1034
+ dayjs.extend(relativeTime);
1035
+ dayjs.extend(isBetween);
1036
+ dayjs.extend(utc);
1037
+ dayjs.extend(timezone);
1038
+ const parseDateInput = (input) => {
1039
+ if (!input) return dayjs();
1040
+ if (typeof input === "string") {
1041
+ const parsed = parseDate(input);
1042
+ if (parsed) return dayjs(parsed);
1043
+ return dayjs(input);
1044
+ }
1045
+ return dayjs(input);
1046
+ };
1047
+ const year = () => (/* @__PURE__ */ new Date()).getFullYear().toString();
1048
+ const convertFormatString = (format) => {
1049
+ return format.replace(/yyyy/g, "YYYY").replace(/yy/g, "YY").replace(/dd/g, "DD").replace(/mm/g, "MM").replace(/hh/g, "HH");
1050
+ };
1051
+ const date = (humanReadableDate, formatString) => {
1052
+ const format = formatString || "YYYY-MM-DD";
1053
+ const parsedDate = parseDateInput(humanReadableDate);
1054
+ /* v8 ignore next -- @preserve */
1055
+ if (!parsedDate.isValid()) throw new Error(`Unable to parse date: ${humanReadableDate}`);
1056
+ const convertedFormat = convertFormatString(format);
1057
+ return parsedDate.format(convertedFormat);
1058
+ };
1059
+ const timestamp = () => {
1060
+ return Date.now().toString();
1061
+ };
1062
+ const now = (formatString) => {
1063
+ const convertedFormat = convertFormatString(formatString || "YYYY-MM-DD HH:mm:ss");
1064
+ return dayjs().format(convertedFormat);
1065
+ };
1066
+ const fromNow = (dateInput) => {
1067
+ const parsed = parseDateInput(dateInput);
1068
+ /* v8 ignore next -- @preserve */
1069
+ if (!parsed.isValid()) throw new Error(`Unable to parse date: ${dateInput}`);
1070
+ return parsed.fromNow();
1071
+ };
1072
+ const toNow = (dateInput) => {
1073
+ const parsed = parseDateInput(dateInput);
1074
+ /* v8 ignore next -- @preserve */
1075
+ if (!parsed.isValid()) throw new Error(`Unable to parse date: ${dateInput}`);
1076
+ return parsed.toNow();
1077
+ };
1078
+ const ago = (dateInput) => {
1079
+ return fromNow(dateInput);
1080
+ };
1081
+ const dateAdd = (dateInput, amount, unit) => {
1082
+ const parsed = parseDateInput(dateInput);
1083
+ /* v8 ignore next -- @preserve */
1084
+ if (!parsed.isValid()) throw new Error(`Unable to parse date: ${dateInput}`);
1085
+ return parsed.add(amount, unit).format("YYYY-MM-DD HH:mm:ss");
1086
+ };
1087
+ const dateSubtract = (dateInput, amount, unit) => {
1088
+ const parsed = parseDateInput(dateInput);
1089
+ /* v8 ignore next -- @preserve */
1090
+ if (!parsed.isValid()) throw new Error(`Unable to parse date: ${dateInput}`);
1091
+ return parsed.subtract(amount, unit).format("YYYY-MM-DD HH:mm:ss");
1092
+ };
1093
+ const startOf = (dateInput, unit) => {
1094
+ const parsed = parseDateInput(dateInput);
1095
+ /* v8 ignore next -- @preserve */
1096
+ if (!parsed.isValid()) throw new Error(`Unable to parse date: ${dateInput}`);
1097
+ return parsed.startOf(unit).format("YYYY-MM-DD HH:mm:ss");
1098
+ };
1099
+ const endOf = (dateInput, unit) => {
1100
+ const parsed = parseDateInput(dateInput);
1101
+ /* v8 ignore next -- @preserve */
1102
+ if (!parsed.isValid()) throw new Error(`Unable to parse date: ${dateInput}`);
1103
+ return parsed.endOf(unit).format("YYYY-MM-DD HH:mm:ss");
1104
+ };
1105
+ const isBefore = (date1, date2) => {
1106
+ const parsed1 = parseDateInput(date1);
1107
+ const parsed2 = parseDateInput(date2);
1108
+ /* v8 ignore next -- @preserve */
1109
+ if (!parsed1.isValid() || !parsed2.isValid()) throw new Error(`Unable to parse dates: ${date1}, ${date2}`);
1110
+ return parsed1.isBefore(parsed2);
1111
+ };
1112
+ const isAfter = (date1, date2) => {
1113
+ const parsed1 = parseDateInput(date1);
1114
+ const parsed2 = parseDateInput(date2);
1115
+ /* v8 ignore next -- @preserve */
1116
+ if (!parsed1.isValid() || !parsed2.isValid()) throw new Error(`Unable to parse dates: ${date1}, ${date2}`);
1117
+ return parsed1.isAfter(parsed2);
1118
+ };
1119
+ const isSame = (date1, date2, unit) => {
1120
+ const parsed1 = parseDateInput(date1);
1121
+ const parsed2 = parseDateInput(date2);
1122
+ /* v8 ignore next -- @preserve */
1123
+ if (!parsed1.isValid() || !parsed2.isValid()) throw new Error(`Unable to parse dates: ${date1}, ${date2}`);
1124
+ return parsed1.isSame(parsed2, unit);
1125
+ };
1126
+ const isBetweenDates = (dateInput, startDate, endDate) => {
1127
+ const parsed = parseDateInput(dateInput);
1128
+ const parsedStart = parseDateInput(startDate);
1129
+ const parsedEnd = parseDateInput(endDate);
1130
+ /* v8 ignore next -- @preserve */
1131
+ if (!parsed.isValid() || !parsedStart.isValid() || !parsedEnd.isValid()) throw new Error(`Unable to parse dates: ${dateInput}, ${startDate}, ${endDate}`);
1132
+ return parsed.isBetween(parsedStart, parsedEnd, null, "[]");
1133
+ };
1134
+ const diff = (date1, date2, unit) => {
1135
+ const parsed1 = parseDateInput(date1);
1136
+ const parsed2 = parseDateInput(date2);
1137
+ /* v8 ignore next -- @preserve */
1138
+ if (!parsed1.isValid() || !parsed2.isValid()) throw new Error(`Unable to parse dates: ${date1}, ${date2}`);
1139
+ return parsed1.diff(parsed2, unit);
1140
+ };
1141
+ const toISOString = (dateInput) => {
1142
+ const parsed = parseDateInput(dateInput);
1143
+ /* v8 ignore next -- @preserve */
1144
+ if (!parsed.isValid()) throw new Error(`Unable to parse date: ${dateInput}`);
1145
+ return parsed.toISOString();
1146
+ };
1147
+ const dateTimezone = (dateInput, timezoneStr, formatString) => {
1148
+ const convertedFormat = convertFormatString(formatString || "YYYY-MM-DD HH:mm:ss");
1149
+ const parsed = parseDateInput(dateInput);
1150
+ /* v8 ignore next -- @preserve */
1151
+ if (!parsed.isValid()) throw new Error(`Unable to parse date: ${dateInput}`);
1152
+ return parsed.tz(timezoneStr).format(convertedFormat);
1153
+ };
1154
+ const dateLocale = (dateInput, locale, formatString) => {
1155
+ const convertedFormat = convertFormatString(formatString || "YYYY-MM-DD HH:mm:ss");
1156
+ const parsed = parseDateInput(dateInput);
1157
+ /* v8 ignore next -- @preserve */
1158
+ if (!parsed.isValid()) throw new Error(`Unable to parse date: ${dateInput}`);
1159
+ return parsed.locale(locale).format(convertedFormat);
1160
+ };
1161
+ const helpers$17 = [
1162
+ {
1163
+ name: "year",
1164
+ category: "date",
1165
+ compatibility: ["browser", "nodejs"],
1166
+ fn: year
1167
+ },
1168
+ {
1169
+ name: "date",
1170
+ category: "date",
1171
+ compatibility: ["browser", "nodejs"],
1172
+ fn: date
1173
+ },
1174
+ {
1175
+ name: "moment",
1176
+ category: "date",
1177
+ compatibility: ["browser", "nodejs"],
1178
+ fn: date
1179
+ },
1180
+ {
1181
+ name: "timestamp",
1182
+ category: "date",
1183
+ compatibility: ["browser", "nodejs"],
1184
+ fn: timestamp
1185
+ },
1186
+ {
1187
+ name: "now",
1188
+ category: "date",
1189
+ compatibility: ["browser", "nodejs"],
1190
+ fn: now
1191
+ },
1192
+ {
1193
+ name: "fromNow",
1194
+ category: "date",
1195
+ compatibility: ["browser", "nodejs"],
1196
+ fn: fromNow
1197
+ },
1198
+ {
1199
+ name: "toNow",
1200
+ category: "date",
1201
+ compatibility: ["browser", "nodejs"],
1202
+ fn: toNow
1203
+ },
1204
+ {
1205
+ name: "ago",
1206
+ category: "date",
1207
+ compatibility: ["browser", "nodejs"],
1208
+ fn: ago
1209
+ },
1210
+ {
1211
+ name: "dateAdd",
1212
+ category: "date",
1213
+ compatibility: ["browser", "nodejs"],
1214
+ fn: dateAdd
1215
+ },
1216
+ {
1217
+ name: "dateSubtract",
1218
+ category: "date",
1219
+ compatibility: ["browser", "nodejs"],
1220
+ fn: dateSubtract
1221
+ },
1222
+ {
1223
+ name: "startOf",
1224
+ category: "date",
1225
+ compatibility: ["browser", "nodejs"],
1226
+ fn: startOf
1227
+ },
1228
+ {
1229
+ name: "endOf",
1230
+ category: "date",
1231
+ compatibility: ["browser", "nodejs"],
1232
+ fn: endOf
1233
+ },
1234
+ {
1235
+ name: "isBefore",
1236
+ category: "date",
1237
+ compatibility: ["browser", "nodejs"],
1238
+ fn: isBefore
1239
+ },
1240
+ {
1241
+ name: "isAfter",
1242
+ category: "date",
1243
+ compatibility: ["browser", "nodejs"],
1244
+ fn: isAfter
1245
+ },
1246
+ {
1247
+ name: "isSame",
1248
+ category: "date",
1249
+ compatibility: ["browser", "nodejs"],
1250
+ fn: isSame
1251
+ },
1252
+ {
1253
+ name: "isBetween",
1254
+ category: "date",
1255
+ compatibility: ["browser", "nodejs"],
1256
+ fn: isBetweenDates
1257
+ },
1258
+ {
1259
+ name: "diff",
1260
+ category: "date",
1261
+ compatibility: ["browser", "nodejs"],
1262
+ fn: diff
1263
+ },
1264
+ {
1265
+ name: "toISOString",
1266
+ category: "date",
1267
+ compatibility: ["browser", "nodejs"],
1268
+ fn: toISOString
1269
+ },
1270
+ {
1271
+ name: "dateTimezone",
1272
+ category: "date",
1273
+ compatibility: ["browser", "nodejs"],
1274
+ fn: dateTimezone
1275
+ },
1276
+ {
1277
+ name: "dateLocale",
1278
+ category: "date",
1279
+ compatibility: ["browser", "nodejs"],
1280
+ fn: dateLocale
1281
+ }
1282
+ ];
1283
+ //#endregion
1284
+ //#region src/helpers/fs.ts
1285
+ const ESCAPE_SEQUENCE = /\\[\s\S]/g;
1286
+ const GLOB_PATTERN = /^!|[*?]|\[[^\]]+\]|\{[^}]+\}|[@?!+*]\([^)]*\)|\([^)]*\|[^)]*\)/;
1287
+ const isGlob = (value) => GLOB_PATTERN.test(value.replace(ESCAPE_SEQUENCE, ""));
1288
+ const fileSize = (value, precision) => {
1289
+ if (value == null) return "0 B";
1290
+ const number = typeof value === "number" ? value : value.length ?? 0;
1291
+ if (!number) return "0 B";
1292
+ const precisionNumber = typeof precision === "number" ? precision : 2;
1293
+ const abbr = [
1294
+ "B",
1295
+ "kB",
1296
+ "MB",
1297
+ "GB",
1298
+ "TB",
1299
+ "PB",
1300
+ "EB",
1301
+ "ZB",
1302
+ "YB"
1303
+ ];
1304
+ const factor = 10 ** precisionNumber;
1305
+ let i = abbr.length - 1;
1306
+ do {
1307
+ const size = 10 ** (i * 3);
1308
+ if (size <= number + 1) return `${Math.round(number * factor / size) / factor} ${abbr[i]}`;
1309
+ } while (--i >= 0);
1310
+ /* c8 ignore next -- @preserve */
1311
+ return `${number} B`;
1312
+ };
1313
+ const read = (filepath) => {
1314
+ return fs.readFileSync(filepath, "utf8");
1315
+ };
1316
+ const isOptions$1 = (value) => {
1317
+ return Boolean(value && typeof value === "object" && "hash" in value);
1318
+ };
1319
+ const readdir = (dir, filter) => {
1320
+ const files = fs.readdirSync(dir).map((fp) => path.join(dir, fp));
1321
+ const filterArg = isOptions$1(filter) ? void 0 : filter;
1322
+ if (typeof filterArg === "function") return filterArg(files);
1323
+ if (filterArg instanceof RegExp) return files.filter((fp) => filterArg.test(fp));
1324
+ if (typeof filterArg === "string") {
1325
+ if (isGlob(filterArg)) return micromatch(files, filterArg);
1326
+ if (filterArg === "isFile" || filterArg === "isDirectory") return files.filter((fp) => {
1327
+ const stat = fs.statSync(fp);
1328
+ return filterArg === "isFile" ? stat.isFile() : stat.isDirectory();
1329
+ });
1330
+ }
1331
+ return files;
1332
+ };
1333
+ const helpers$16 = [
1334
+ {
1335
+ name: "fileSize",
1336
+ category: "fs",
1337
+ compatibility: ["nodejs"],
1338
+ fn: fileSize
1339
+ },
1340
+ {
1341
+ name: "read",
1342
+ category: "fs",
1343
+ compatibility: ["nodejs"],
1344
+ fn: read
1345
+ },
1346
+ {
1347
+ name: "readdir",
1348
+ category: "fs",
1349
+ compatibility: ["nodejs"],
1350
+ fn: readdir
1351
+ }
1352
+ ];
1353
+ //#endregion
1354
+ //#region src/helpers/html.node.ts
1355
+ const css = function(list, options) {
1356
+ if (arguments.length < 2) {
1357
+ options = list;
1358
+ list = [];
1359
+ }
1360
+ let styles = arrayify(list ?? []);
1361
+ let assets = "";
1362
+ if (this && this.options) assets = this.options.assets || "";
1363
+ if (options?.hash?.href) styles = arrayify(options.hash.href);
1364
+ return styles.map((item) => {
1365
+ const ext = path.extname(item);
1366
+ let fp = item;
1367
+ /* c8 ignore next -- @preserve */
1368
+ if (!/(^\/\/)|(:\/\/)/.test(item)) fp = path.posix.join(assets, item);
1369
+ if (ext === ".less") return `<link type="text/css" rel="stylesheet/less" href="${fp}">`;
1370
+ return `<link type="text/css" rel="stylesheet" href="${fp}">`;
1371
+ }).join("\n");
1372
+ };
1373
+ const js = (context) => {
1374
+ if (context && typeof context === "object" && !Array.isArray(context)) {
1375
+ const attrStr = parseAttributes(context.hash ?? {});
1376
+ return `<script${attrStr ? ` ${attrStr}` : ""}><\/script>`;
1377
+ }
1378
+ if (typeof context === "string") return `<script src="${context}"><\/script>`;
1379
+ return arrayify(context).map((fp) => path.extname(fp) === ".coffee" ? tag("script", {
1380
+ type: "text/coffeescript",
1381
+ src: fp
1382
+ }) : tag("script", { src: fp })).join("\n");
1383
+ };
1384
+ const helpers$15 = [{
1385
+ name: "css",
1386
+ category: "html",
1387
+ compatibility: ["nodejs"],
1388
+ fn: css
1389
+ }, {
1390
+ name: "js",
1391
+ category: "html",
1392
+ compatibility: ["nodejs"],
1393
+ fn: js
1394
+ }];
1395
+ //#endregion
1396
+ //#region src/utils.ts
1397
+ /**
1398
+ * Native implementation to replace get-value module
1399
+ * Safely gets nested object properties using dot notation or array paths
1400
+ * @param object - The object to traverse
1401
+ * @param path - Dot notation string or array of keys
1402
+ * @returns The value at the path or undefined if not found
1403
+ */
1404
+ const get = (object, path) => {
1405
+ if (!object || typeof object !== "object") return void 0;
1406
+ const keys = Array.isArray(path) ? path : path.split(".");
1407
+ let result = object;
1408
+ for (const key of keys) {
1409
+ if (result == null || typeof result !== "object") return void 0;
1410
+ result = result[key];
1411
+ }
1412
+ return result;
1413
+ };
1414
+ /**
1415
+ * Native implementation to replace get-object module
1416
+ * Returns an object containing the key-value pair at the specified path
1417
+ * @param object - The object to traverse
1418
+ * @param path - Dot notation string or array of keys
1419
+ * @returns An object containing the key-value pair, or undefined if not found
1420
+ */
1421
+ const getObject = (object, path) => {
1422
+ if (!object || typeof object !== "object") return void 0;
1423
+ const keys = Array.isArray(path) ? path : path.split(".");
1424
+ if (keys.length === 0) return void 0;
1425
+ let current = object;
1426
+ for (let i = 0; i < keys.length - 1; i++) {
1427
+ if (current == null || typeof current !== "object") return void 0;
1428
+ current = current[keys[i]];
1429
+ }
1430
+ const finalKey = keys[keys.length - 1];
1431
+ if (current == null || typeof current !== "object" || !(finalKey in current)) return;
1432
+ return { [finalKey]: current[finalKey] };
1433
+ };
1434
+ /**
1435
+ * Merges options from multiple sources for Handlebars helpers
1436
+ * Replaces handlebars-utils util.options()
1437
+ * @param thisArg - The context (this) from the helper, may have an options property
1438
+ * @param locals - Local options or a Handlebars options object with hash
1439
+ * @param opts - Additional options or a Handlebars options object with hash
1440
+ * @returns Merged options object
1441
+ */
1442
+ const options = (thisArg, locals, opts) => {
1443
+ const result = {};
1444
+ if (thisArg && typeof thisArg === "object" && thisArg.options) Object.assign(result, thisArg.options);
1445
+ if (locals && typeof locals === "object") if ("hash" in locals) Object.assign(result, locals.hash);
1446
+ else Object.assign(result, locals);
1447
+ if (opts && typeof opts === "object") if ("hash" in opts) Object.assign(result, opts.hash);
1448
+ else Object.assign(result, opts);
1449
+ return result;
1450
+ };
1451
+ //#endregion
1452
+ //#region src/helpers/i18n.ts
1453
+ const i18n = function(prop, locals, options) {
1454
+ if (locals && typeof locals === "object" && "hash" in locals && options === void 0) {
1455
+ options = locals;
1456
+ locals = {};
1457
+ }
1458
+ if (typeof prop !== "string") throw new Error("{{i18n}} helper expected \"key\" to be a string");
1459
+ const context = {
1460
+ ...this,
1461
+ ...locals,
1462
+ ...options?.hash ?? {}
1463
+ };
1464
+ const lang = context.language || context.lang;
1465
+ if (typeof lang !== "string") throw new TypeError("{{i18n}} helper expected \"language\" to be a string");
1466
+ const cache = context[lang];
1467
+ if (typeof cache === "undefined") throw new Error(`{{i18n}} helper cannot find language "${lang}"`);
1468
+ const result = get(cache, prop);
1469
+ if (typeof result === "undefined") throw new Error("{{i18n}} helper cannot find property \"" + String(prop) + "\" for language \"" + lang + "\"");
1470
+ return String(result);
1471
+ };
1472
+ const helpers$14 = [{
1473
+ name: "i18n",
1474
+ category: "i18n",
1475
+ compatibility: ["browser", "nodejs"],
1476
+ fn: i18n
1477
+ }];
1478
+ //#endregion
1479
+ //#region src/helpers/inflection.ts
1480
+ const inflect = (count, singular, plural, includeCount) => {
1481
+ const word = count > 1 || count === 0 ? plural : singular;
1482
+ return includeCount ? `${count} ${word}` : word;
1483
+ };
1484
+ const ordinalize = (value) => {
1485
+ const num = Math.abs(Math.round(Number(value)));
1486
+ const str = String(value);
1487
+ const res = num % 100;
1488
+ if ([
1489
+ 11,
1490
+ 12,
1491
+ 13
1492
+ ].includes(res)) return `${str}th`;
1493
+ switch (num % 10) {
1494
+ case 1: return `${str}st`;
1495
+ case 2: return `${str}nd`;
1496
+ case 3: return `${str}rd`;
1497
+ default: return `${str}th`;
1498
+ }
1499
+ };
1500
+ const helpers$13 = [{
1501
+ name: "inflect",
1502
+ category: "inflection",
1503
+ compatibility: ["browser", "nodejs"],
1504
+ fn: inflect
1505
+ }, {
1506
+ name: "ordinalize",
1507
+ category: "inflection",
1508
+ compatibility: ["browser", "nodejs"],
1509
+ fn: ordinalize
1510
+ }];
1511
+ //#endregion
1512
+ //#region src/helpers/logging.ts
1513
+ /**
1514
+ * ANSI color codes for terminal output
1515
+ */
1516
+ const colors = {
1517
+ green: (str) => `\x1b[32m${str}\x1b[0m`,
1518
+ cyan: (str) => `\x1b[36m${str}\x1b[0m`,
1519
+ yellow: (str) => `\x1b[33m${str}\x1b[0m`,
1520
+ red: (str) => `\x1b[31m${str}\x1b[0m`,
1521
+ bold: (str) => `\x1b[1m${str}\x1b[0m`
1522
+ };
1523
+ /**
1524
+ * Remove handlebars options from arguments if present
1525
+ */
1526
+ function cleanArgs(args) {
1527
+ const last = args[args.length - 1];
1528
+ if (args.length > 1 && typeof last === "object" && last !== null && "hash" in last) return args.slice(0, -1);
1529
+ return args;
1530
+ }
1531
+ /**
1532
+ * Helper for logging an unstyled message to the terminal.
1533
+ */
1534
+ function log(...args) {
1535
+ const cleanedArgs = cleanArgs(args);
1536
+ console.log(...cleanedArgs);
1537
+ return "";
1538
+ }
1539
+ /**
1540
+ * Helper for logging a green colored "ok" message preceded by a checkmark
1541
+ * to the terminal.
1542
+ */
1543
+ function ok(...args) {
1544
+ const message = cleanArgs(args).join(" ");
1545
+ console.log(colors.green(`✓ ${message}`));
1546
+ return "";
1547
+ }
1548
+ /**
1549
+ * Helper for logging a green colored "success" message to the terminal.
1550
+ */
1551
+ function success(...args) {
1552
+ const message = cleanArgs(args).map((arg) => String(arg)).join(" ");
1553
+ console.log(colors.green(message));
1554
+ return "";
1555
+ }
1556
+ /**
1557
+ * Helper for logging a cyan colored "informational" message to the terminal.
1558
+ */
1559
+ function info(...args) {
1560
+ const message = cleanArgs(args).map((arg) => String(arg)).join(" ");
1561
+ console.log(colors.cyan(message));
1562
+ return "";
1563
+ }
1564
+ /**
1565
+ * Helper for logging a yellow colored "warning" message to the terminal.
1566
+ */
1567
+ function warning(...args) {
1568
+ const message = cleanArgs(args).map((arg) => String(arg)).join(" ");
1569
+ console.error(colors.yellow(message));
1570
+ return "";
1571
+ }
1572
+ /**
1573
+ * Alias for warning helper.
1574
+ */
1575
+ const warn = warning;
1576
+ /**
1577
+ * Helper for logging a red colored "error" message to the terminal.
1578
+ */
1579
+ function error(...args) {
1580
+ const message = cleanArgs(args).map((arg) => String(arg)).join(" ");
1581
+ console.error(colors.red(message));
1582
+ return "";
1583
+ }
1584
+ /**
1585
+ * Alias for error helper.
1586
+ */
1587
+ const danger = error;
1588
+ /**
1589
+ * Helper for logging a bold formatted message to the terminal.
1590
+ */
1591
+ function bold(...args) {
1592
+ const message = cleanArgs(args).map((arg) => String(arg)).join(" ");
1593
+ console.error(colors.bold(message));
1594
+ return "";
1595
+ }
1596
+ /**
1597
+ * Helper for outputting a debug statement with the current context
1598
+ */
1599
+ function _debug(...args) {
1600
+ const separator = "─".repeat(50);
1601
+ console.error(colors.cyan(separator));
1602
+ if (args.length > 0) {
1603
+ const cleanedArgs = cleanArgs(args);
1604
+ console.error(colors.cyan("VALUE:"), cleanedArgs[0]);
1605
+ }
1606
+ if (this && Object.keys(this).length > 0) console.error(colors.cyan("CONTEXT:"), this);
1607
+ console.error(colors.cyan(separator));
1608
+ return "";
1609
+ }
1610
+ /**
1611
+ * Helper for inspecting and formatting values as JSON
1612
+ */
1613
+ function _inspect(context, options) {
1614
+ const val = JSON.stringify(context, null, 2);
1615
+ return switchOutput(options?.hash?.type || "html", val);
1616
+ }
1617
+ /**
1618
+ * Generate output based on the given type
1619
+ */
1620
+ function switchOutput(type, json) {
1621
+ if (type[0] === ".") type = type.slice(1);
1622
+ switch (type) {
1623
+ case "md": return `\n\`\`\`json\n${json}\n\`\`\`\n`;
1624
+ case "html": return `<div class="highlight highlight-json">\n<pre><code>\n${json}</code></pre></div>`;
1625
+ default: return json;
1626
+ }
1627
+ }
1628
+ const helpers$12 = [
1629
+ {
1630
+ name: "log",
1631
+ category: "logging",
1632
+ compatibility: ["nodejs"],
1633
+ fn: log
1634
+ },
1635
+ {
1636
+ name: "ok",
1637
+ category: "logging",
1638
+ compatibility: ["nodejs"],
1639
+ fn: ok
1640
+ },
1641
+ {
1642
+ name: "success",
1643
+ category: "logging",
1644
+ compatibility: ["nodejs"],
1645
+ fn: success
1646
+ },
1647
+ {
1648
+ name: "info",
1649
+ category: "logging",
1650
+ compatibility: ["nodejs"],
1651
+ fn: info
1652
+ },
1653
+ {
1654
+ name: "warning",
1655
+ category: "logging",
1656
+ compatibility: ["nodejs"],
1657
+ fn: warning
1658
+ },
1659
+ {
1660
+ name: "warn",
1661
+ category: "logging",
1662
+ compatibility: ["nodejs"],
1663
+ fn: warn
1664
+ },
1665
+ {
1666
+ name: "error",
1667
+ category: "logging",
1668
+ compatibility: ["nodejs"],
1669
+ fn: error
1670
+ },
1671
+ {
1672
+ name: "danger",
1673
+ category: "logging",
1674
+ compatibility: ["nodejs"],
1675
+ fn: danger
1676
+ },
1677
+ {
1678
+ name: "bold",
1679
+ category: "logging",
1680
+ compatibility: ["nodejs"],
1681
+ fn: bold
1682
+ },
1683
+ {
1684
+ name: "_debug",
1685
+ category: "logging",
1686
+ compatibility: ["nodejs"],
1687
+ fn: _debug
1688
+ },
1689
+ {
1690
+ name: "_inspect",
1691
+ category: "logging",
1692
+ compatibility: ["nodejs"],
1693
+ fn: _inspect
1694
+ }
1695
+ ];
1696
+ //#endregion
1697
+ //#region src/helpers/match.ts
1698
+ const match = (files, patterns, options) => {
1699
+ const pats = typeof patterns === "string" ? patterns.split(/, */) : patterns;
1700
+ return micromatch(typeof files === "string" ? [files] : files, pats, options);
1701
+ };
1702
+ const isMatch = (filepath, pattern, options) => {
1703
+ return micromatch.isMatch(filepath, pattern, options);
1704
+ };
1705
+ const mm = (...args) => {
1706
+ console.log("the {{mm}} helper is depcrecated and will be removed");
1707
+ console.log("in handlebars-helpers v1.0.0, please use the {{match}}");
1708
+ console.log("helper instead.");
1709
+ return match(...args);
1710
+ };
1711
+ const helpers$11 = [
1712
+ {
1713
+ name: "match",
1714
+ category: "match",
1715
+ compatibility: ["browser", "nodejs"],
1716
+ fn: match
1717
+ },
1718
+ {
1719
+ name: "isMatch",
1720
+ category: "match",
1721
+ compatibility: ["browser", "nodejs"],
1722
+ fn: isMatch
1723
+ },
1724
+ {
1725
+ name: "mm",
1726
+ category: "match",
1727
+ compatibility: ["browser", "nodejs"],
1728
+ fn: mm
1729
+ }
1730
+ ];
1731
+ //#endregion
1732
+ //#region src/helpers/math.ts
1733
+ const isNumeric$1 = (value) => typeof value === "number" && !Number.isNaN(value) || typeof value === "string" && value.trim() !== "" && !Number.isNaN(Number(value));
1734
+ const toNumber = (value, message = "expected a number") => {
1735
+ if (isNumeric$1(value)) return Number(value);
1736
+ throw new TypeError(message);
1737
+ };
1738
+ const abs = (value) => {
1739
+ const num = toNumber(value);
1740
+ return Math.abs(num);
1741
+ };
1742
+ const add = (a, b) => {
1743
+ if (isNumeric$1(a) && isNumeric$1(b)) return Number(a) + Number(b);
1744
+ return "";
1745
+ };
1746
+ const avg = (...values) => {
1747
+ const flat = values.flat().map((n) => Number(n));
1748
+ return sum(flat) / flat.length;
1749
+ };
1750
+ const ceil = (value) => {
1751
+ const num = toNumber(value);
1752
+ return Math.ceil(num);
1753
+ };
1754
+ const divide = (a, b) => {
1755
+ return toNumber(a, "expected the first argument to be a number") / toNumber(b, "expected the second argument to be a number");
1756
+ };
1757
+ const floor = (value) => {
1758
+ const num = toNumber(value);
1759
+ return Math.floor(num);
1760
+ };
1761
+ const minus = (a, b) => {
1762
+ return toNumber(a, "expected the first argument to be a number") - toNumber(b, "expected the second argument to be a number");
1763
+ };
1764
+ const modulo = (a, b) => {
1765
+ return toNumber(a, "expected the first argument to be a number") % toNumber(b, "expected the second argument to be a number");
1766
+ };
1767
+ const multiply = (a, b) => {
1768
+ return toNumber(a, "expected the first argument to be a number") * toNumber(b, "expected the second argument to be a number");
1769
+ };
1770
+ const plus = (a, b) => {
1771
+ return toNumber(a, "expected the first argument to be a number") + toNumber(b, "expected the second argument to be a number");
1772
+ };
1773
+ const random = (min, max) => {
1774
+ const minNum = toNumber(min, "expected minimum to be a number");
1775
+ const maxNum = toNumber(max, "expected maximum to be a number");
1776
+ return minNum + Math.floor(Math.random() * (maxNum - minNum + 1));
1777
+ };
1778
+ const remainder = (a, b) => a % b;
1779
+ const round = (value) => {
1780
+ const num = toNumber(value);
1781
+ return Math.round(num);
1782
+ };
1783
+ const subtract = (a, b) => {
1784
+ return toNumber(a, "expected the first argument to be a number") - toNumber(b, "expected the second argument to be a number");
1785
+ };
1786
+ const sum = (...values) => {
1787
+ const flat = values.flat();
1788
+ let total = 0;
1789
+ for (const value of flat) if (isNumeric$1(value)) total += Number(value);
1790
+ return total;
1791
+ };
1792
+ const times = (a, b) => multiply(a, b);
1793
+ const helpers$10 = [
1794
+ {
1795
+ name: "abs",
1796
+ category: "math",
1797
+ compatibility: ["browser", "nodejs"],
1798
+ fn: abs
1799
+ },
1800
+ {
1801
+ name: "add",
1802
+ category: "math",
1803
+ compatibility: ["browser", "nodejs"],
1804
+ fn: add
1805
+ },
1806
+ {
1807
+ name: "avg",
1808
+ category: "math",
1809
+ compatibility: ["browser", "nodejs"],
1810
+ fn: avg
1811
+ },
1812
+ {
1813
+ name: "ceil",
1814
+ category: "math",
1815
+ compatibility: ["browser", "nodejs"],
1816
+ fn: ceil
1817
+ },
1818
+ {
1819
+ name: "divide",
1820
+ category: "math",
1821
+ compatibility: ["browser", "nodejs"],
1822
+ fn: divide
1823
+ },
1824
+ {
1825
+ name: "floor",
1826
+ category: "math",
1827
+ compatibility: ["browser", "nodejs"],
1828
+ fn: floor
1829
+ },
1830
+ {
1831
+ name: "minus",
1832
+ category: "math",
1833
+ compatibility: ["browser", "nodejs"],
1834
+ fn: minus
1835
+ },
1836
+ {
1837
+ name: "modulo",
1838
+ category: "math",
1839
+ compatibility: ["browser", "nodejs"],
1840
+ fn: modulo
1841
+ },
1842
+ {
1843
+ name: "multiply",
1844
+ category: "math",
1845
+ compatibility: ["browser", "nodejs"],
1846
+ fn: multiply
1847
+ },
1848
+ {
1849
+ name: "plus",
1850
+ category: "math",
1851
+ compatibility: ["browser", "nodejs"],
1852
+ fn: plus
1853
+ },
1854
+ {
1855
+ name: "random",
1856
+ category: "math",
1857
+ compatibility: ["browser", "nodejs"],
1858
+ fn: random
1859
+ },
1860
+ {
1861
+ name: "remainder",
1862
+ category: "math",
1863
+ compatibility: ["browser", "nodejs"],
1864
+ fn: remainder
1865
+ },
1866
+ {
1867
+ name: "round",
1868
+ category: "math",
1869
+ compatibility: ["browser", "nodejs"],
1870
+ fn: round
1871
+ },
1872
+ {
1873
+ name: "subtract",
1874
+ category: "math",
1875
+ compatibility: ["browser", "nodejs"],
1876
+ fn: subtract
1877
+ },
1878
+ {
1879
+ name: "sum",
1880
+ category: "math",
1881
+ compatibility: ["browser", "nodejs"],
1882
+ fn: sum
1883
+ },
1884
+ {
1885
+ name: "times",
1886
+ category: "math",
1887
+ compatibility: ["browser", "nodejs"],
1888
+ fn: times
1889
+ }
1890
+ ];
1891
+ //#endregion
1892
+ //#region src/helpers/md.ts
1893
+ const md = new MarkdownIt({
1894
+ breaks: true,
1895
+ html: true,
1896
+ langPrefix: "lang-",
1897
+ typographer: false,
1898
+ xhtmlOut: false
1899
+ });
1900
+ function renderString(input) {
1901
+ return new HandlebarsLib.SafeString(ent.decode(md.render(input)));
1902
+ }
1903
+ //#endregion
1904
+ //#region src/helpers/md.node.ts
1905
+ const renderMarkdown = function(input, options) {
1906
+ if (typeof input === "object" && input && "fn" in input && typeof input.fn === "function") {
1907
+ options = input;
1908
+ input = input.fn(this);
1909
+ } else if (typeof input !== "string") input = "";
1910
+ const options_ = {
1911
+ cwd: process$1.cwd(),
1912
+ ...options
1913
+ };
1914
+ let string_ = input;
1915
+ if (string_.length > 0) {
1916
+ const filepath = path.resolve(options_.cwd, string_);
1917
+ if (fs.existsSync(filepath) && fs.statSync(filepath).isFile()) string_ = fs.readFileSync(filepath, "utf8");
1918
+ }
1919
+ return renderString(string_);
1920
+ };
1921
+ const helpers$9 = [{
1922
+ name: "md",
1923
+ category: "markdown",
1924
+ compatibility: ["nodejs"],
1925
+ fn: renderMarkdown
1926
+ }, {
1927
+ name: "markdown",
1928
+ category: "markdown",
1929
+ compatibility: ["nodejs"],
1930
+ fn: renderMarkdown
1931
+ }];
1932
+ //#endregion
1933
+ //#region src/helpers/misc.ts
1934
+ const objectToString = Object.prototype.toString;
1935
+ function ctorName(val) {
1936
+ return typeof val.constructor === "function" ? val.constructor.name : null;
1937
+ }
1938
+ function isGeneratorFn(val) {
1939
+ return ctorName(val) === "GeneratorFunction";
1940
+ }
1941
+ function isGeneratorObj(val) {
1942
+ return typeof val.throw === "function" && typeof val.return === "function" && typeof val.next === "function";
1943
+ }
1944
+ function isDateLike(val) {
1945
+ return typeof val.toDateString === "function" && typeof val.getDate === "function" && typeof val.setDate === "function";
1946
+ }
1947
+ function isErrorLike(val) {
1948
+ return typeof val.message === "string" && val.constructor && typeof val.constructor.stackTraceLimit === "number";
1949
+ }
1950
+ function isRegexpLike(val) {
1951
+ return typeof val.flags === "string" && typeof val.ignoreCase === "boolean" && typeof val.multiline === "boolean" && typeof val.global === "boolean";
1952
+ }
1953
+ function kindOf(val) {
1954
+ if (val === void 0) return "undefined";
1955
+ if (val === null) return "null";
1956
+ const type = typeof val;
1957
+ if (type === "boolean") return "boolean";
1958
+ if (type === "string") return "string";
1959
+ if (type === "number") return "number";
1960
+ if (type === "symbol") return "symbol";
1961
+ if (type === "bigint") return "bigint";
1962
+ if (type === "function") return isGeneratorFn(val) ? "generatorfunction" : "function";
1963
+ if (Array.isArray(val)) return "array";
1964
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer?.(val)) return "buffer";
1965
+ if (typeof val.length === "number" && objectToString.call(val) === "[object Arguments]") return "arguments";
1966
+ if (val instanceof Date || isDateLike(val)) return "date";
1967
+ if (val instanceof Error || isErrorLike(val)) return "error";
1968
+ if (val instanceof RegExp || isRegexpLike(val)) return "regexp";
1969
+ switch (ctorName(val)) {
1970
+ case "Promise": return "promise";
1971
+ case "WeakMap": return "weakmap";
1972
+ case "WeakSet": return "weakset";
1973
+ case "Map": return "map";
1974
+ case "Set": return "set";
1975
+ case "Int8Array": return "int8array";
1976
+ case "Uint8Array": return "uint8array";
1977
+ case "Uint8ClampedArray": return "uint8clampedarray";
1978
+ case "Int16Array": return "int16array";
1979
+ case "Uint16Array": return "uint16array";
1980
+ case "Int32Array": return "int32array";
1981
+ case "Uint32Array": return "uint32array";
1982
+ case "Float32Array": return "float32array";
1983
+ case "Float64Array": return "float64array";
1984
+ }
1985
+ if (isGeneratorObj(val)) return "generator";
1986
+ const tag = objectToString.call(val);
1987
+ switch (tag) {
1988
+ case "[object Object]": return "object";
1989
+ case "[object Map Iterator]": return "mapiterator";
1990
+ case "[object Set Iterator]": return "setiterator";
1991
+ case "[object String Iterator]": return "stringiterator";
1992
+ case "[object Array Iterator]": return "arrayiterator";
1993
+ }
1994
+ return tag.slice(8, -1).toLowerCase().replace(/\s/g, "");
1995
+ }
1996
+ /**
1997
+ * Block helper for exposing private `@` variables on the context
1998
+ * Replaces the external handlebars-helper-create-frame package
1999
+ */
2000
+ function frame(context, options) {
2001
+ if (typeof context === "object" && context !== null && "hash" in context) {
2002
+ options = context;
2003
+ context = options.data;
2004
+ }
2005
+ const data = HandlebarsLib.createFrame(context || {});
2006
+ if (typeof options !== "object" || options === null) options = {};
2007
+ if (options.hash && typeof options.hash === "object") Object.assign(data, options.hash);
2008
+ return options.fn(this, { data });
2009
+ }
2010
+ function option(prop, locals, opts) {
2011
+ return get(options(this, locals, opts), prop);
2012
+ }
2013
+ function noop(options) {
2014
+ return options.fn(this);
2015
+ }
2016
+ const typeOf = kindOf;
2017
+ function withHash(options) {
2018
+ if (options.hash && Object.keys(options.hash).length) return options.fn(options.hash);
2019
+ return options.inverse(this);
2020
+ }
2021
+ const helpers$8 = [
2022
+ {
2023
+ name: "frame",
2024
+ category: "misc",
2025
+ compatibility: ["browser", "nodejs"],
2026
+ fn: frame
2027
+ },
2028
+ {
2029
+ name: "option",
2030
+ category: "misc",
2031
+ compatibility: ["browser", "nodejs"],
2032
+ fn: option
2033
+ },
2034
+ {
2035
+ name: "noop",
2036
+ category: "misc",
2037
+ compatibility: ["browser", "nodejs"],
2038
+ fn: noop
2039
+ },
2040
+ {
2041
+ name: "typeOf",
2042
+ category: "misc",
2043
+ compatibility: ["browser", "nodejs"],
2044
+ fn: typeOf
2045
+ },
2046
+ {
2047
+ name: "withHash",
2048
+ category: "misc",
2049
+ compatibility: ["browser", "nodejs"],
2050
+ fn: withHash
2051
+ }
2052
+ ];
2053
+ //#endregion
2054
+ //#region src/helpers/number.ts
2055
+ const isNumeric = (value) => typeof value === "number" && !Number.isNaN(value);
2056
+ const bytes = (value, precision) => {
2057
+ if (value == null) return "0 B";
2058
+ let num;
2059
+ if (typeof value === "string") {
2060
+ num = value.length;
2061
+ if (!num) return "0 B";
2062
+ } else if (isNumeric(value)) num = Number(value);
2063
+ else if (typeof value.length === "number") {
2064
+ num = value.length;
2065
+ if (!num) return "0 B";
2066
+ } else return "0 B";
2067
+ const prec = typeof precision === "number" && !Number.isNaN(precision) ? precision : 2;
2068
+ const abbr = [
2069
+ "B",
2070
+ "kB",
2071
+ "MB",
2072
+ "GB",
2073
+ "TB",
2074
+ "PB",
2075
+ "EB",
2076
+ "ZB",
2077
+ "YB"
2078
+ ];
2079
+ const factor = 10 ** prec;
2080
+ for (let i = abbr.length - 1; i >= 0; i--) {
2081
+ const size = 10 ** (i * 3);
2082
+ if (size <= num + 1) return `${Math.round(num * factor / size) / factor} ${abbr[i]}`;
2083
+ }
2084
+ return `${num}`;
2085
+ };
2086
+ const addCommas = (num) => num.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");
2087
+ const phoneNumber = (num) => {
2088
+ const str = num.toString();
2089
+ return `(${str.substr(0, 3)}) ${str.substr(3, 3)}-${str.substr(6, 4)}`;
2090
+ };
2091
+ const toAbbr = (value, precision) => {
2092
+ const num = isNumeric(value) ? Number(value) : 0;
2093
+ const factor = 10 ** (typeof precision === "number" && !Number.isNaN(precision) ? precision : 2);
2094
+ const abbr = [
2095
+ "k",
2096
+ "m",
2097
+ "b",
2098
+ "t",
2099
+ "q"
2100
+ ];
2101
+ for (let i = abbr.length - 1; i >= 0; i--) {
2102
+ const size = 10 ** ((i + 1) * 3);
2103
+ if (size <= num + 1) return `${Math.round(num * factor / size) / factor}${abbr[i]}`;
2104
+ }
2105
+ return `${num}`;
2106
+ };
2107
+ const toExponential = (value, digits) => {
2108
+ const num = isNumeric(value) ? Number(value) : 0;
2109
+ const d = typeof digits === "number" && !Number.isNaN(digits) ? digits : 0;
2110
+ return Number(num).toExponential(d);
2111
+ };
2112
+ const toFixed = (value, digits) => {
2113
+ const num = isNumeric(value) ? Number(value) : 0;
2114
+ const d = typeof digits === "number" && !Number.isNaN(digits) ? digits : 0;
2115
+ return Number(num).toFixed(d);
2116
+ };
2117
+ const toFloat = (value) => parseFloat(String(value));
2118
+ const toInt = (value) => parseInt(String(value), 10);
2119
+ const toPrecision = (value, precision) => {
2120
+ const num = isNumeric(value) ? Number(value) : 0;
2121
+ const prec = typeof precision === "number" && !Number.isNaN(precision) ? precision : 1;
2122
+ return Number(num).toPrecision(prec);
2123
+ };
2124
+ const helpers$7 = [
2125
+ {
2126
+ name: "bytes",
2127
+ category: "number",
2128
+ compatibility: ["browser", "nodejs"],
2129
+ fn: bytes
2130
+ },
2131
+ {
2132
+ name: "addCommas",
2133
+ category: "number",
2134
+ compatibility: ["browser", "nodejs"],
2135
+ fn: addCommas
2136
+ },
2137
+ {
2138
+ name: "phoneNumber",
2139
+ category: "number",
2140
+ compatibility: ["browser", "nodejs"],
2141
+ fn: phoneNumber
2142
+ },
2143
+ {
2144
+ name: "toAbbr",
2145
+ category: "number",
2146
+ compatibility: ["browser", "nodejs"],
2147
+ fn: toAbbr
2148
+ },
2149
+ {
2150
+ name: "toExponential",
2151
+ category: "number",
2152
+ compatibility: ["browser", "nodejs"],
2153
+ fn: toExponential
2154
+ },
2155
+ {
2156
+ name: "toFixed",
2157
+ category: "number",
2158
+ compatibility: ["browser", "nodejs"],
2159
+ fn: toFixed
2160
+ },
2161
+ {
2162
+ name: "toFloat",
2163
+ category: "number",
2164
+ compatibility: ["browser", "nodejs"],
2165
+ fn: toFloat
2166
+ },
2167
+ {
2168
+ name: "toInt",
2169
+ category: "number",
2170
+ compatibility: ["browser", "nodejs"],
2171
+ fn: toInt
2172
+ },
2173
+ {
2174
+ name: "toPrecision",
2175
+ category: "number",
2176
+ compatibility: ["browser", "nodejs"],
2177
+ fn: toPrecision
2178
+ }
2179
+ ];
2180
+ //#endregion
2181
+ //#region src/helpers/object.ts
2182
+ const isOptions = (value) => {
2183
+ return Boolean(value && typeof value === "object" && "hash" in value);
2184
+ };
2185
+ const createFrame = (options, hash) => ({
2186
+ ...options?.data || {},
2187
+ ...hash
2188
+ });
2189
+ const extend = (...objects) => {
2190
+ const args = [...objects];
2191
+ if (args.length && isOptions(args[args.length - 1])) {
2192
+ const opts = args.pop();
2193
+ args.push(opts.hash);
2194
+ }
2195
+ const result = {};
2196
+ for (const obj of args) if (obj && typeof obj === "object" && !Array.isArray(obj)) Object.assign(result, obj);
2197
+ return result;
2198
+ };
2199
+ const forIn = (obj, options) => {
2200
+ if (!isOptions(options)) return options?.inverse?.(obj) ?? "";
2201
+ const data = createFrame(options, options.hash || {});
2202
+ let result = "";
2203
+ for (const key in obj) {
2204
+ data.key = key;
2205
+ result += options.fn(obj[key], { data });
2206
+ }
2207
+ return result;
2208
+ };
2209
+ const forOwn = (obj, options) => {
2210
+ if (!isOptions(options)) return options?.inverse?.(obj) ?? "";
2211
+ const data = createFrame(options, options.hash || {});
2212
+ let result = "";
2213
+ for (const key in obj) if (Object.hasOwn(obj, key)) {
2214
+ data.key = key;
2215
+ result += options.fn(obj[key], { data });
2216
+ }
2217
+ return result;
2218
+ };
2219
+ const toPath = (...prop) => {
2220
+ const parts = [];
2221
+ for (const p of prop) if (typeof p === "string" || typeof p === "number") parts.push(p);
2222
+ return parts.join(".");
2223
+ };
2224
+ const getHelper = (prop, context, options) => {
2225
+ const val = get(context, prop);
2226
+ if (options && typeof options.fn === "function") return val ? options.fn(val) : options.inverse?.(context);
2227
+ return val;
2228
+ };
2229
+ const getObjectHelper = (prop, context) => {
2230
+ return getObject(context, prop);
2231
+ };
2232
+ const hasOwn = (context, key) => {
2233
+ return Object.hasOwn(context, key);
2234
+ };
2235
+ const isObject = (value) => {
2236
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2237
+ };
2238
+ const JSONparse = (str) => {
2239
+ return JSON.parse(str);
2240
+ };
2241
+ const JSONstringify = (obj, indent) => {
2242
+ return JSON.stringify(obj, null, typeof indent === "number" ? indent : 0);
2243
+ };
2244
+ const merge = (context, ...objects) => {
2245
+ const args = [context, ...objects];
2246
+ if (args.length && isOptions(args[args.length - 1])) {
2247
+ const opts = args.pop();
2248
+ args.push(opts.hash);
2249
+ }
2250
+ return Object.assign(args[0], ...args.slice(1));
2251
+ };
2252
+ const parseJSON = JSONparse;
2253
+ const stringify = JSONstringify;
2254
+ const pick = (props, context, options) => {
2255
+ const keys = arrayify(props);
2256
+ let result = {};
2257
+ for (const key of keys) result = Object.assign({}, result, getObject(context, key));
2258
+ if (options && typeof options.fn === "function") {
2259
+ if (Object.keys(result).length) return options.fn(result);
2260
+ return options.inverse?.(context);
2261
+ }
2262
+ return result;
2263
+ };
2264
+ const helpers$6 = [
2265
+ {
2266
+ name: "extend",
2267
+ category: "object",
2268
+ compatibility: ["browser", "nodejs"],
2269
+ fn: extend
2270
+ },
2271
+ {
2272
+ name: "forIn",
2273
+ category: "object",
2274
+ compatibility: ["browser", "nodejs"],
2275
+ fn: forIn
2276
+ },
2277
+ {
2278
+ name: "forOwn",
2279
+ category: "object",
2280
+ compatibility: ["browser", "nodejs"],
2281
+ fn: forOwn
2282
+ },
2283
+ {
2284
+ name: "toPath",
2285
+ category: "object",
2286
+ compatibility: ["browser", "nodejs"],
2287
+ fn: toPath
2288
+ },
2289
+ {
2290
+ name: "get",
2291
+ category: "object",
2292
+ compatibility: ["browser", "nodejs"],
2293
+ fn: getHelper
2294
+ },
2295
+ {
2296
+ name: "getObject",
2297
+ category: "object",
2298
+ compatibility: ["browser", "nodejs"],
2299
+ fn: getObjectHelper
2300
+ },
2301
+ {
2302
+ name: "hasOwn",
2303
+ category: "object",
2304
+ compatibility: ["browser", "nodejs"],
2305
+ fn: hasOwn
2306
+ },
2307
+ {
2308
+ name: "isObject",
2309
+ category: "object",
2310
+ compatibility: ["browser", "nodejs"],
2311
+ fn: isObject
2312
+ },
2313
+ {
2314
+ name: "JSONparse",
2315
+ category: "object",
2316
+ compatibility: ["browser", "nodejs"],
2317
+ fn: JSONparse
2318
+ },
2319
+ {
2320
+ name: "JSONstringify",
2321
+ category: "object",
2322
+ compatibility: ["browser", "nodejs"],
2323
+ fn: JSONstringify
2324
+ },
2325
+ {
2326
+ name: "merge",
2327
+ category: "object",
2328
+ compatibility: ["browser", "nodejs"],
2329
+ fn: merge
2330
+ },
2331
+ {
2332
+ name: "parseJSON",
2333
+ category: "object",
2334
+ compatibility: ["browser", "nodejs"],
2335
+ fn: parseJSON
2336
+ },
2337
+ {
2338
+ name: "pick",
2339
+ category: "object",
2340
+ compatibility: ["browser", "nodejs"],
2341
+ fn: pick
2342
+ },
2343
+ {
2344
+ name: "stringify",
2345
+ category: "object",
2346
+ compatibility: ["browser", "nodejs"],
2347
+ fn: stringify
2348
+ }
2349
+ ];
2350
+ //#endregion
2351
+ //#region src/helpers/path.ts
2352
+ const expectedType = (variableName, expectedType, variableValue) => {
2353
+ return `Expected ${variableName} to be of type ${expectedType}, but got ${Object.prototype.toString.call(variableValue).slice(8, -1).toLowerCase()}`;
2354
+ };
2355
+ const assertString = (name, value) => {
2356
+ if (typeof value !== "string") throw new TypeError(expectedType(name, "string", value));
2357
+ return value;
2358
+ };
2359
+ const absolute = function(filepath, options) {
2360
+ const fp = assertString("filepath", filepath);
2361
+ const cwd = options?.data?.root?.cwd ?? this?.cwd ?? process.cwd();
2362
+ return path.resolve(cwd, fp);
2363
+ };
2364
+ const dirname = (filepath) => {
2365
+ const fp = assertString("filepath", filepath);
2366
+ return path.dirname(fp);
2367
+ };
2368
+ const relative = (a, b) => {
2369
+ const from = assertString("first path", a);
2370
+ const to = assertString("second path", b);
2371
+ return path.relative(path.dirname(from), to);
2372
+ };
2373
+ const basename = (filepath) => {
2374
+ const fp = assertString("filepath", filepath);
2375
+ return path.basename(fp);
2376
+ };
2377
+ const stem = (filepath) => {
2378
+ const fp = assertString("filepath", filepath);
2379
+ return path.basename(fp, path.extname(fp));
2380
+ };
2381
+ const extname = (filepath) => {
2382
+ const fp = assertString("filepath", filepath);
2383
+ return path.extname(fp);
2384
+ };
2385
+ const resolveFn = function(filepath, ...paths) {
2386
+ let options;
2387
+ const last = paths[paths.length - 1];
2388
+ if (typeof last === "object" && last && "data" in last) options = paths.pop();
2389
+ const all = [
2390
+ options?.data?.root?.cwd ?? this?.cwd ?? process.cwd(),
2391
+ assertString("filepath", filepath),
2392
+ ...paths.map((p, i) => assertString(`path${i}`, p))
2393
+ ];
2394
+ return path.resolve(...all);
2395
+ };
2396
+ const segments = (filepath, a, b) => {
2397
+ const fp = assertString("filepath", filepath);
2398
+ const start = Number(a);
2399
+ const end = Number(b);
2400
+ return fp.split(/[\\/]+/).slice(start, end).join("/");
2401
+ };
2402
+ const helpers$5 = [
2403
+ {
2404
+ name: "absolute",
2405
+ category: "path",
2406
+ compatibility: ["nodejs"],
2407
+ fn: absolute
2408
+ },
2409
+ {
2410
+ name: "dirname",
2411
+ category: "path",
2412
+ compatibility: ["nodejs"],
2413
+ fn: dirname
2414
+ },
2415
+ {
2416
+ name: "relative",
2417
+ category: "path",
2418
+ compatibility: ["nodejs"],
2419
+ fn: relative
2420
+ },
2421
+ {
2422
+ name: "basename",
2423
+ category: "path",
2424
+ compatibility: ["nodejs"],
2425
+ fn: basename
2426
+ },
2427
+ {
2428
+ name: "stem",
2429
+ category: "path",
2430
+ compatibility: ["nodejs"],
2431
+ fn: stem
2432
+ },
2433
+ {
2434
+ name: "extname",
2435
+ category: "path",
2436
+ compatibility: ["nodejs"],
2437
+ fn: extname
2438
+ },
2439
+ {
2440
+ name: "resolve",
2441
+ category: "path",
2442
+ compatibility: ["nodejs"],
2443
+ fn: resolveFn
2444
+ },
2445
+ {
2446
+ name: "segments",
2447
+ category: "path",
2448
+ compatibility: ["nodejs"],
2449
+ fn: segments
2450
+ }
2451
+ ];
2452
+ //#endregion
2453
+ //#region src/helpers/regex.ts
2454
+ const toRegex = (str, locals, opts) => {
2455
+ const mergedOpts = options({}, locals, opts);
2456
+ return new RegExp(str, mergedOpts.flags);
2457
+ };
2458
+ const test = (str, regex) => {
2459
+ if (typeof str !== "string") return false;
2460
+ if (!(regex instanceof RegExp)) throw new TypeError("expected a regular expression");
2461
+ return regex.test(str);
2462
+ };
2463
+ const helpers$4 = [{
2464
+ name: "toRegex",
2465
+ category: "regex",
2466
+ compatibility: ["browser", "nodejs"],
2467
+ fn: toRegex
2468
+ }, {
2469
+ name: "test",
2470
+ category: "regex",
2471
+ compatibility: ["browser", "nodejs"],
2472
+ fn: test
2473
+ }];
2474
+ //#endregion
2475
+ //#region src/helpers/string.ts
2476
+ const chop = (str) => str.trim().replace(/^[-_.\W\s]+|[-_.\W\s]+$/g, "");
2477
+ const changecase = (str, fn) => {
2478
+ if (str.length === 1) return str.toLowerCase();
2479
+ return chop(str).toLowerCase().replace(/[-_.\W\s]+(\w|$)/g, (_, ch) => fn(ch));
2480
+ };
2481
+ const append = (str, suffix) => {
2482
+ if (typeof str === "string" && typeof suffix === "string") return str + suffix;
2483
+ return str;
2484
+ };
2485
+ const camelcase = (str) => typeof str === "string" ? changecase(str, (ch) => ch.toUpperCase()) : "";
2486
+ const capitalize = (str) => {
2487
+ if (typeof str !== "string" || str.length === 0) return "";
2488
+ return str.charAt(0).toUpperCase() + str.slice(1);
2489
+ };
2490
+ const capitalizeAll = (str) => {
2491
+ if (typeof str !== "string") return "";
2492
+ return str.replace(/\w\S*/g, (word) => capitalize(word));
2493
+ };
2494
+ const center = (str, spaces) => {
2495
+ if (typeof str !== "string") return "";
2496
+ let space = "";
2497
+ for (let i = 0; i < spaces; i++) space += "&nbsp;";
2498
+ return space + str + space;
2499
+ };
2500
+ const chopStr = (str) => typeof str === "string" ? chop(str) : "";
2501
+ const dashcase = (str) => typeof str === "string" ? changecase(str, (ch) => `-${ch}`) : "";
2502
+ const dotcase = (str) => typeof str === "string" ? changecase(str, (ch) => `.${ch}`) : "";
2503
+ const truncate = (str, limit, suffix) => {
2504
+ if (typeof str === "string") {
2505
+ if (typeof suffix !== "string") suffix = "";
2506
+ if (str.length > limit) return str.slice(0, limit - suffix.length) + suffix;
2507
+ return str;
2508
+ }
2509
+ return "";
2510
+ };
2511
+ const ellipsis = (str, limit) => {
2512
+ if (typeof str === "string") {
2513
+ if (str.length <= limit) return str;
2514
+ return `${truncate(str, limit)}…`;
2515
+ }
2516
+ return "";
2517
+ };
2518
+ const hyphenate = (str) => typeof str === "string" ? str.split(" ").join("-") : "";
2519
+ const isString = (value) => typeof value === "string";
2520
+ const lowercase = (str) => {
2521
+ if (typeof str === "object" && str !== null && "fn" in str && typeof str.fn === "function") return String(str.fn()).toLowerCase();
2522
+ if (typeof str !== "string") return "";
2523
+ return str.toLowerCase();
2524
+ };
2525
+ const downcase = (str) => lowercase(str);
2526
+ const occurrences = (str, substring) => {
2527
+ if (typeof str !== "string") return "";
2528
+ const len = substring.length;
2529
+ let pos = str.indexOf(substring, 0);
2530
+ let n = 0;
2531
+ while (pos > -1) {
2532
+ n++;
2533
+ pos = str.indexOf(substring, pos + len);
2534
+ }
2535
+ return n;
2536
+ };
2537
+ const pascalcase = (str) => {
2538
+ if (typeof str !== "string") return "";
2539
+ const res = changecase(str, (ch) => ch.toUpperCase());
2540
+ return res.charAt(0).toUpperCase() + res.slice(1);
2541
+ };
2542
+ const pathcase = (str) => typeof str === "string" ? changecase(str, (ch) => `/${ch}`) : "";
2543
+ const plusify = (str, ch) => {
2544
+ if (typeof str !== "string") return "";
2545
+ if (typeof ch !== "string") ch = " ";
2546
+ return str.split(ch).join("+");
2547
+ };
2548
+ const prepend = (str, prefix) => {
2549
+ return typeof str === "string" && typeof prefix === "string" ? prefix + str : str;
2550
+ };
2551
+ const remove = (str, ch) => {
2552
+ if (typeof str !== "string") return "";
2553
+ if (typeof ch !== "string") return str;
2554
+ return str.split(ch).join("");
2555
+ };
2556
+ const removeFirst = (str, ch) => {
2557
+ if (typeof str !== "string") return "";
2558
+ if (typeof ch !== "string") return str;
2559
+ return str.replace(ch, "");
2560
+ };
2561
+ const replace = (str, a, b) => {
2562
+ if (typeof str !== "string") return "";
2563
+ if (typeof a !== "string") return str;
2564
+ if (typeof b !== "string") b = "";
2565
+ return str.split(a).join(b);
2566
+ };
2567
+ const replaceFirst = (str, a, b) => {
2568
+ if (typeof str !== "string") return "";
2569
+ if (typeof a !== "string") return str;
2570
+ if (typeof b !== "string") b = "";
2571
+ return str.replace(a, b);
2572
+ };
2573
+ const reverse = (str) => typeof str === "string" ? str.split("").reverse().join("") : "";
2574
+ const sentence = (str) => {
2575
+ if (typeof str !== "string") return "";
2576
+ return str.replace(/((?:\S[^.?!]*)[.?!]*)/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase());
2577
+ };
2578
+ const snakecase = (str) => typeof str === "string" ? changecase(str, (ch) => `_${ch}`) : "";
2579
+ const trim = (str) => typeof str === "string" ? str.trim() : "";
2580
+ const trimLeft = (str) => typeof str === "string" ? str.replace(/^\s+/, "") : "";
2581
+ const trimRight = (str) => typeof str === "string" ? str.replace(/\s+$/, "") : "";
2582
+ const truncateWords = (str, count, suffix) => {
2583
+ if (typeof str === "string" && typeof count === "number" && Number.isFinite(count)) {
2584
+ /* c8 ignore next -- @preserve */
2585
+ if (typeof suffix !== "string") suffix = "...";
2586
+ const num = Math.trunc(count);
2587
+ let arr = str.split(" ");
2588
+ /* c8 ignore next -- @preserve */
2589
+ if (num < arr.length) arr = arr.slice(0, num);
2590
+ return arr.join(" ").trim() + suffix;
2591
+ }
2592
+ return "";
2593
+ };
2594
+ const truncateWordsAlias = truncateWords;
2595
+ const uppercase = (str) => {
2596
+ if (typeof str === "object" && str !== null && "fn" in str && typeof str.fn === "function") return String(str.fn()).toUpperCase();
2597
+ if (typeof str !== "string") return "";
2598
+ return str.toUpperCase();
2599
+ };
2600
+ const upcase = (str) => uppercase(str);
2601
+ const split = (str, character) => {
2602
+ if (typeof str !== "string") return "";
2603
+ if (typeof character !== "string") character = "";
2604
+ return str.split(character);
2605
+ };
2606
+ const startsWith = (prefix, testString, _options) => {
2607
+ if (typeof prefix !== "string" || typeof testString !== "string") return false;
2608
+ return testString.startsWith(prefix);
2609
+ };
2610
+ const titleize = (str) => {
2611
+ if (typeof str !== "string") return "";
2612
+ return str.replace(/\w\S*/g, (word) => {
2613
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
2614
+ });
2615
+ };
2616
+ const raw = (options) => {
2617
+ if (options && typeof options.fn === "function") return options.fn();
2618
+ return "";
2619
+ };
2620
+ const helpers$3 = [
2621
+ {
2622
+ name: "append",
2623
+ category: "string",
2624
+ compatibility: ["browser", "nodejs"],
2625
+ fn: append
2626
+ },
2627
+ {
2628
+ name: "camelcase",
2629
+ category: "string",
2630
+ compatibility: ["browser", "nodejs"],
2631
+ fn: camelcase
2632
+ },
2633
+ {
2634
+ name: "capitalize",
2635
+ category: "string",
2636
+ compatibility: ["browser", "nodejs"],
2637
+ fn: capitalize
2638
+ },
2639
+ {
2640
+ name: "capitalizeAll",
2641
+ category: "string",
2642
+ compatibility: ["browser", "nodejs"],
2643
+ fn: capitalizeAll
2644
+ },
2645
+ {
2646
+ name: "center",
2647
+ category: "string",
2648
+ compatibility: ["browser", "nodejs"],
2649
+ fn: center
2650
+ },
2651
+ {
2652
+ name: "chop",
2653
+ category: "string",
2654
+ compatibility: ["browser", "nodejs"],
2655
+ fn: chopStr
2656
+ },
2657
+ {
2658
+ name: "dashcase",
2659
+ category: "string",
2660
+ compatibility: ["browser", "nodejs"],
2661
+ fn: dashcase
2662
+ },
2663
+ {
2664
+ name: "dotcase",
2665
+ category: "string",
2666
+ compatibility: ["browser", "nodejs"],
2667
+ fn: dotcase
2668
+ },
2669
+ {
2670
+ name: "ellipsis",
2671
+ category: "string",
2672
+ compatibility: ["browser", "nodejs"],
2673
+ fn: ellipsis
2674
+ },
2675
+ {
2676
+ name: "hyphenate",
2677
+ category: "string",
2678
+ compatibility: ["browser", "nodejs"],
2679
+ fn: hyphenate
2680
+ },
2681
+ {
2682
+ name: "isString",
2683
+ category: "string",
2684
+ compatibility: ["browser", "nodejs"],
2685
+ fn: isString
2686
+ },
2687
+ {
2688
+ name: "lowercase",
2689
+ category: "string",
2690
+ compatibility: ["browser", "nodejs"],
2691
+ fn: lowercase
2692
+ },
2693
+ {
2694
+ name: "downcase",
2695
+ category: "string",
2696
+ compatibility: ["browser", "nodejs"],
2697
+ fn: downcase
2698
+ },
2699
+ {
2700
+ name: "occurrences",
2701
+ category: "string",
2702
+ compatibility: ["browser", "nodejs"],
2703
+ fn: occurrences
2704
+ },
2705
+ {
2706
+ name: "pascalcase",
2707
+ category: "string",
2708
+ compatibility: ["browser", "nodejs"],
2709
+ fn: pascalcase
2710
+ },
2711
+ {
2712
+ name: "pathcase",
2713
+ category: "string",
2714
+ compatibility: ["browser", "nodejs"],
2715
+ fn: pathcase
2716
+ },
2717
+ {
2718
+ name: "plusify",
2719
+ category: "string",
2720
+ compatibility: ["browser", "nodejs"],
2721
+ fn: plusify
2722
+ },
2723
+ {
2724
+ name: "prepend",
2725
+ category: "string",
2726
+ compatibility: ["browser", "nodejs"],
2727
+ fn: prepend
2728
+ },
2729
+ {
2730
+ name: "remove",
2731
+ category: "string",
2732
+ compatibility: ["browser", "nodejs"],
2733
+ fn: remove
2734
+ },
2735
+ {
2736
+ name: "removeFirst",
2737
+ category: "string",
2738
+ compatibility: ["browser", "nodejs"],
2739
+ fn: removeFirst
2740
+ },
2741
+ {
2742
+ name: "replace",
2743
+ category: "string",
2744
+ compatibility: ["browser", "nodejs"],
2745
+ fn: replace
2746
+ },
2747
+ {
2748
+ name: "replaceFirst",
2749
+ category: "string",
2750
+ compatibility: ["browser", "nodejs"],
2751
+ fn: replaceFirst
2752
+ },
2753
+ {
2754
+ name: "reverse",
2755
+ category: "string",
2756
+ compatibility: ["browser", "nodejs"],
2757
+ fn: reverse
2758
+ },
2759
+ {
2760
+ name: "sentence",
2761
+ category: "string",
2762
+ compatibility: ["browser", "nodejs"],
2763
+ fn: sentence
2764
+ },
2765
+ {
2766
+ name: "snakecase",
2767
+ category: "string",
2768
+ compatibility: ["browser", "nodejs"],
2769
+ fn: snakecase
2770
+ },
2771
+ {
2772
+ name: "trim",
2773
+ category: "string",
2774
+ compatibility: ["browser", "nodejs"],
2775
+ fn: trim
2776
+ },
2777
+ {
2778
+ name: "trimLeft",
2779
+ category: "string",
2780
+ compatibility: ["browser", "nodejs"],
2781
+ fn: trimLeft
2782
+ },
2783
+ {
2784
+ name: "trimRight",
2785
+ category: "string",
2786
+ compatibility: ["browser", "nodejs"],
2787
+ fn: trimRight
2788
+ },
2789
+ {
2790
+ name: "truncate",
2791
+ category: "string",
2792
+ compatibility: ["browser", "nodejs"],
2793
+ fn: truncate
2794
+ },
2795
+ {
2796
+ name: "truncateWords",
2797
+ category: "string",
2798
+ compatibility: ["browser", "nodejs"],
2799
+ fn: truncateWordsAlias
2800
+ },
2801
+ {
2802
+ name: "uppercase",
2803
+ category: "string",
2804
+ compatibility: ["browser", "nodejs"],
2805
+ fn: uppercase
2806
+ },
2807
+ {
2808
+ name: "upcase",
2809
+ category: "string",
2810
+ compatibility: ["browser", "nodejs"],
2811
+ fn: upcase
2812
+ },
2813
+ {
2814
+ name: "split",
2815
+ category: "string",
2816
+ compatibility: ["browser", "nodejs"],
2817
+ fn: split
2818
+ },
2819
+ {
2820
+ name: "startsWith",
2821
+ category: "string",
2822
+ compatibility: ["browser", "nodejs"],
2823
+ fn: startsWith
2824
+ },
2825
+ {
2826
+ name: "titleize",
2827
+ category: "string",
2828
+ compatibility: ["browser", "nodejs"],
2829
+ fn: titleize
2830
+ },
2831
+ {
2832
+ name: "raw",
2833
+ category: "string",
2834
+ compatibility: ["browser", "nodejs"],
2835
+ fn: raw
2836
+ }
2837
+ ];
2838
+ //#endregion
2839
+ //#region src/helpers/url.ts
2840
+ const all = ["browser", "nodejs"];
2841
+ const encodeUri = (str) => {
2842
+ if (typeof str === "string") return encodeURIComponent(str);
2843
+ };
2844
+ const decodeUri = (str) => {
2845
+ if (typeof str === "string") return decodeURIComponent(str);
2846
+ };
2847
+ function urlEncode(...args) {
2848
+ return encodeUri.apply(this, args);
2849
+ }
2850
+ function urlDecode(...args) {
2851
+ return decodeUri.apply(this, args);
2852
+ }
2853
+ const stripQuerystring = (str) => {
2854
+ if (typeof str === "string") return str.split("?")[0];
2855
+ };
2856
+ const helpers$2 = [
2857
+ {
2858
+ name: "encodeURI",
2859
+ category: "url",
2860
+ compatibility: all,
2861
+ fn: encodeUri
2862
+ },
2863
+ {
2864
+ name: "decodeURI",
2865
+ category: "url",
2866
+ compatibility: all,
2867
+ fn: decodeUri
2868
+ },
2869
+ {
2870
+ name: "url_encode",
2871
+ category: "url",
2872
+ compatibility: all,
2873
+ fn: urlEncode
2874
+ },
2875
+ {
2876
+ name: "url_decode",
2877
+ category: "url",
2878
+ compatibility: all,
2879
+ fn: urlDecode
2880
+ },
2881
+ {
2882
+ name: "stripQuerystring",
2883
+ category: "url",
2884
+ compatibility: all,
2885
+ fn: stripQuerystring
2886
+ }
2887
+ ];
2888
+ //#endregion
2889
+ //#region src/helpers/url.node.ts
2890
+ const escapeFn = (str) => {
2891
+ if (typeof str === "string") return escape(str);
2892
+ };
2893
+ const urlResolve = (base, href) => resolve(String(base), String(href));
2894
+ const urlParse = (str) => parse(String(str));
2895
+ const stripProtocol = (str) => {
2896
+ if (typeof str === "string") {
2897
+ const parsed = parse(str);
2898
+ parsed.protocol = "";
2899
+ return format(parsed);
2900
+ }
2901
+ };
2902
+ const helpers$1 = [
2903
+ {
2904
+ name: "escape",
2905
+ category: "url",
2906
+ compatibility: ["nodejs"],
2907
+ fn: escapeFn
2908
+ },
2909
+ {
2910
+ name: "urlResolve",
2911
+ category: "url",
2912
+ compatibility: ["nodejs"],
2913
+ fn: urlResolve
2914
+ },
2915
+ {
2916
+ name: "urlParse",
2917
+ category: "url",
2918
+ compatibility: ["nodejs"],
2919
+ fn: urlParse
2920
+ },
2921
+ {
2922
+ name: "stripProtocol",
2923
+ category: "url",
2924
+ compatibility: ["nodejs"],
2925
+ fn: stripProtocol
2926
+ }
2927
+ ];
2928
+ //#endregion
2929
+ //#region src/helper-registry.ts
2930
+ var HelperRegistry = class extends HelperRegistryBase {
2931
+ init() {
2932
+ this.registerHelpers(helpers$23);
2933
+ this.registerHelpers(helpers$19);
2934
+ this.registerHelpers(helpers$17);
2935
+ this.registerHelpers(helpers$16);
2936
+ this.registerHelpers(helpers$21);
2937
+ this.registerHelpers(helpers$20);
2938
+ this.registerHelpers(helpers$9);
2939
+ this.registerHelpers(helpers$22);
2940
+ this.registerHelpers(helpers$15);
2941
+ this.registerHelpers(helpers$18);
2942
+ this.registerHelpers(helpers$14);
2943
+ this.registerHelpers(helpers$13);
2944
+ this.registerHelpers(helpers$12);
2945
+ this.registerHelpers(helpers$11);
2946
+ this.registerHelpers(helpers$10);
2947
+ this.registerHelpers(helpers$8);
2948
+ this.registerHelpers(helpers$7);
2949
+ this.registerHelpers(helpers$5);
2950
+ this.registerHelpers(helpers$4);
2951
+ this.registerHelpers(helpers$3);
2952
+ this.registerHelpers(helpers$2);
2953
+ this.registerHelpers(helpers$1);
2954
+ this.registerHelpers(helpers$6);
2955
+ }
2956
+ };
2957
+ //#endregion
2958
+ //#region src/index.node.ts
2959
+ /**
2960
+ * Handlebars library not initiated with helpers
2961
+ * @type {Handlebars}
2962
+ */
2963
+ const Handlebars = HandlebarsLib;
2964
+ /**
2965
+ * Fumanchu Handlebars instance not initiated with helpers
2966
+ * @type {Handlebars}
2967
+ */
2968
+ const handlebars = HandlebarsLib.create();
2969
+ /**
2970
+ * Register Handlebars helpers
2971
+ * @param {HelpersOptions} options - Options for Fumanchu helpers
2972
+ */
2973
+ function helpers(options) {
2974
+ new HelperRegistry().load(options.handlebars ?? options.hbs);
2975
+ }
2976
+ /**
2977
+ * Create a new Handlebars instance with Fumanchu helpers
2978
+ * @returns {Promise<Handlebars>}
2979
+ * @deprecated Will be deprecated in future versions, use `fumanchu()` instead.
2980
+ */
2981
+ async function createHandlebars() {
2982
+ const registry = new HelperRegistry();
2983
+ const handlebars = HandlebarsLib.create();
2984
+ if (Object.keys(HandlebarsLib.partials).length > 0) handlebars.registerPartial(HandlebarsLib.partials);
2985
+ registry.load(handlebars);
2986
+ /* c8 ignore next -- @preserve */
2987
+ if (process.env.NODE_ENV === "development") console.warn("createHandlebars will be deprecated in future versions, use `fumanchu` instead.");
2988
+ return handlebars;
2989
+ }
2990
+ /**
2991
+ * Will return a Handlebars instance with Fumanchu helpers (experimental)
2992
+ * @param {FumanchuOptions} [options] - Options for Fumanchu
2993
+ * @returns {Handlebars} Handlebars instance with helpers
2994
+ */
2995
+ function fumanchu(options) {
2996
+ const registry = new HelperRegistry();
2997
+ let handlebars = HandlebarsLib.create();
2998
+ /* c8 ignore next -- @preserve */
2999
+ if (options?.handlebars) handlebars = options.handlebars;
3000
+ else if (Object.keys(HandlebarsLib.partials).length > 0) handlebars.registerPartial(HandlebarsLib.partials);
3001
+ registry.load(handlebars);
3002
+ if (options?.caching) {
3003
+ let cache;
3004
+ if (options.caching instanceof CacheableMemory) cache = options.caching;
3005
+ else if (typeof options.caching === "object") cache = new CacheableMemory({
3006
+ useClone: false,
3007
+ ...options.caching
3008
+ });
3009
+ else cache = new CacheableMemory({ useClone: false });
3010
+ const originalCompile = handlebars.compile.bind(handlebars);
3011
+ handlebars.compile = (input, compileOptions) => {
3012
+ const key = compileOptions ? `${input}\0${JSON.stringify(compileOptions)}` : input;
3013
+ const cached = cache.get(key);
3014
+ if (cached) return cached;
3015
+ const compiled = originalCompile(input, compileOptions);
3016
+ cache.set(key, compiled);
3017
+ return compiled;
3018
+ };
3019
+ }
3020
+ return handlebars;
3021
+ }
3022
+ //#endregion
3023
+ export { CacheableMemory, Handlebars, HelperRegistry, createHandlebars, fumanchu, handlebars, helpers };