@marktoflow/core 2.0.0-alpha.14 → 2.0.0-alpha.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/expression-helpers.d.ts +309 -0
- package/dist/expression-helpers.d.ts.map +1 -0
- package/dist/expression-helpers.js +697 -0
- package/dist/expression-helpers.js.map +1 -0
- package/dist/pipeline-parser.d.ts +38 -0
- package/dist/pipeline-parser.d.ts.map +1 -0
- package/dist/pipeline-parser.js +219 -0
- package/dist/pipeline-parser.js.map +1 -0
- package/dist/regex-operators.d.ts +86 -0
- package/dist/regex-operators.d.ts.map +1 -0
- package/dist/regex-operators.js +383 -0
- package/dist/regex-operators.js.map +1 -0
- package/dist/tools/mcp-tool.js +9 -9
- package/dist/tools/mcp-tool.js.map +1 -1
- package/dist/version.d.ts +8 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +8 -0
- package/dist/version.js.map +1 -0
- package/package.json +5 -2
|
@@ -0,0 +1,697 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expression Helper Library for marktoflow
|
|
3
|
+
*
|
|
4
|
+
* Provides pipeline-style filters for template expressions:
|
|
5
|
+
* {{ value | filter1 | filter2(arg) | filter3 }}
|
|
6
|
+
*
|
|
7
|
+
* Categories:
|
|
8
|
+
* - String helpers: split, join, trim, upper, lower, slugify, prefix, suffix
|
|
9
|
+
* - Array helpers: first, last, nth, map, filter, unique, count, sum
|
|
10
|
+
* - Object helpers: path, keys, values, merge, pick, omit
|
|
11
|
+
* - Date helpers: now, format_date, add_days, subtract_days
|
|
12
|
+
* - Logic helpers: default, or, and, not
|
|
13
|
+
* - Validation helpers: is_array, is_object, is_empty, is_null
|
|
14
|
+
*/
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// String Helpers
|
|
17
|
+
// ============================================================================
|
|
18
|
+
/**
|
|
19
|
+
* Split string by delimiter
|
|
20
|
+
* Usage: {{ "a,b,c" | split(',') }} → ['a', 'b', 'c']
|
|
21
|
+
*/
|
|
22
|
+
export function split(value, delimiter = ',') {
|
|
23
|
+
const str = String(value);
|
|
24
|
+
return str.split(delimiter);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Join array elements
|
|
28
|
+
* Usage: {{ ['a', 'b', 'c'] | join(', ') }} → "a, b, c"
|
|
29
|
+
*/
|
|
30
|
+
export function join(value, separator = ',') {
|
|
31
|
+
if (!Array.isArray(value)) {
|
|
32
|
+
return String(value);
|
|
33
|
+
}
|
|
34
|
+
return value.join(separator);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Trim whitespace
|
|
38
|
+
* Usage: {{ " hello " | trim }} → "hello"
|
|
39
|
+
*/
|
|
40
|
+
export function trim(value) {
|
|
41
|
+
return String(value).trim();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Convert to uppercase
|
|
45
|
+
* Usage: {{ "hello" | upper }} → "HELLO"
|
|
46
|
+
*/
|
|
47
|
+
export function upper(value) {
|
|
48
|
+
return String(value).toUpperCase();
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Convert to lowercase
|
|
52
|
+
* Usage: {{ "HELLO" | lower }} → "hello"
|
|
53
|
+
*/
|
|
54
|
+
export function lower(value) {
|
|
55
|
+
return String(value).toLowerCase();
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Convert to title case
|
|
59
|
+
* Usage: {{ "hello world" | title }} → "Hello World"
|
|
60
|
+
*/
|
|
61
|
+
export function title(value) {
|
|
62
|
+
const str = String(value);
|
|
63
|
+
return str.replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.slice(1).toLowerCase());
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Capitalize first letter
|
|
67
|
+
* Usage: {{ "hello" | capitalize }} → "Hello"
|
|
68
|
+
*/
|
|
69
|
+
export function capitalize(value) {
|
|
70
|
+
const str = String(value);
|
|
71
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Convert to URL-friendly slug
|
|
75
|
+
* Usage: {{ "Hello World!" | slugify }} → "hello-world"
|
|
76
|
+
*/
|
|
77
|
+
export function slugify(value) {
|
|
78
|
+
return String(value)
|
|
79
|
+
.toLowerCase()
|
|
80
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
81
|
+
.replace(/^-|-$/g, '');
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Add prefix to string
|
|
85
|
+
* Usage: {{ "hello" | prefix('@') }} → "@hello"
|
|
86
|
+
*/
|
|
87
|
+
export function prefix(value, prefixStr) {
|
|
88
|
+
return prefixStr + String(value);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Add suffix to string
|
|
92
|
+
* Usage: {{ "hello" | suffix('!') }} → "hello!"
|
|
93
|
+
*/
|
|
94
|
+
export function suffix(value, suffixStr) {
|
|
95
|
+
return String(value) + suffixStr;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Replace all occurrences
|
|
99
|
+
* Usage: {{ "hello world" | replace('world', 'there') }} → "hello there"
|
|
100
|
+
*/
|
|
101
|
+
export function replace(value, search, replaceWith) {
|
|
102
|
+
return String(value).replace(new RegExp(search, 'g'), replaceWith);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Truncate string to length
|
|
106
|
+
* Usage: {{ "hello world" | truncate(5) }} → "hello..."
|
|
107
|
+
*/
|
|
108
|
+
export function truncate(value, length, ellipsis = '...') {
|
|
109
|
+
const str = String(value);
|
|
110
|
+
if (str.length <= length)
|
|
111
|
+
return str;
|
|
112
|
+
return str.slice(0, length) + ellipsis;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Extract substring
|
|
116
|
+
* Usage: {{ "hello world" | substring(0, 5) }} → "hello"
|
|
117
|
+
*/
|
|
118
|
+
export function substring(value, start, end) {
|
|
119
|
+
return String(value).substring(start, end);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Check if string/array contains value
|
|
123
|
+
* Usage: {{ "hello world" | contains('world') }} → true
|
|
124
|
+
*/
|
|
125
|
+
export function contains(value, search) {
|
|
126
|
+
if (typeof value === 'string') {
|
|
127
|
+
return value.includes(String(search));
|
|
128
|
+
}
|
|
129
|
+
if (Array.isArray(value)) {
|
|
130
|
+
return value.includes(search);
|
|
131
|
+
}
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
// ============================================================================
|
|
135
|
+
// Array Helpers
|
|
136
|
+
// ============================================================================
|
|
137
|
+
/**
|
|
138
|
+
* Get first element
|
|
139
|
+
* Usage: {{ [1, 2, 3] | first }} → 1
|
|
140
|
+
*/
|
|
141
|
+
export function first(value) {
|
|
142
|
+
if (Array.isArray(value)) {
|
|
143
|
+
return value[0];
|
|
144
|
+
}
|
|
145
|
+
return value;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Get last element
|
|
149
|
+
* Usage: {{ [1, 2, 3] | last }} → 3
|
|
150
|
+
*/
|
|
151
|
+
export function last(value) {
|
|
152
|
+
if (Array.isArray(value)) {
|
|
153
|
+
return value[value.length - 1];
|
|
154
|
+
}
|
|
155
|
+
return value;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get nth element (0-indexed)
|
|
159
|
+
* Usage: {{ [1, 2, 3] | nth(1) }} → 2
|
|
160
|
+
*/
|
|
161
|
+
export function nth(value, index) {
|
|
162
|
+
if (Array.isArray(value)) {
|
|
163
|
+
return value[index];
|
|
164
|
+
}
|
|
165
|
+
return value;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Get array length or object property count
|
|
169
|
+
* Usage: {{ [1, 2, 3] | count }} → 3
|
|
170
|
+
*/
|
|
171
|
+
export function count(value) {
|
|
172
|
+
if (Array.isArray(value)) {
|
|
173
|
+
return value.length;
|
|
174
|
+
}
|
|
175
|
+
if (typeof value === 'string') {
|
|
176
|
+
return value.length;
|
|
177
|
+
}
|
|
178
|
+
if (typeof value === 'object' && value !== null) {
|
|
179
|
+
// Check for common count properties
|
|
180
|
+
const obj = value;
|
|
181
|
+
if ('length' in obj && typeof obj.length === 'number')
|
|
182
|
+
return obj.length;
|
|
183
|
+
if ('total' in obj && typeof obj.total === 'number')
|
|
184
|
+
return obj.total;
|
|
185
|
+
if ('count' in obj && typeof obj.count === 'number')
|
|
186
|
+
return obj.count;
|
|
187
|
+
return Object.keys(value).length;
|
|
188
|
+
}
|
|
189
|
+
return 0;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Sum array of numbers
|
|
193
|
+
* Usage: {{ [1, 2, 3] | sum }} → 6
|
|
194
|
+
*/
|
|
195
|
+
export function sum(value) {
|
|
196
|
+
if (!Array.isArray(value))
|
|
197
|
+
return 0;
|
|
198
|
+
return value.reduce((acc, val) => acc + Number(val), 0);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Get unique values
|
|
202
|
+
* Usage: {{ [1, 2, 2, 3] | unique }} → [1, 2, 3]
|
|
203
|
+
*/
|
|
204
|
+
export function unique(value) {
|
|
205
|
+
if (!Array.isArray(value))
|
|
206
|
+
return [value];
|
|
207
|
+
return Array.from(new Set(value));
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Flatten array one level
|
|
211
|
+
* Usage: {{ [[1, 2], [3, 4]] | flatten }} → [1, 2, 3, 4]
|
|
212
|
+
*/
|
|
213
|
+
export function flatten(value) {
|
|
214
|
+
if (!Array.isArray(value))
|
|
215
|
+
return [value];
|
|
216
|
+
return value.flat(1);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Reverse array
|
|
220
|
+
* Usage: {{ [1, 2, 3] | reverse }} → [3, 2, 1]
|
|
221
|
+
*/
|
|
222
|
+
export function reverse(value) {
|
|
223
|
+
if (!Array.isArray(value))
|
|
224
|
+
return [value];
|
|
225
|
+
return [...value].reverse();
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Sort array
|
|
229
|
+
* Usage: {{ [3, 1, 2] | sort }} → [1, 2, 3]
|
|
230
|
+
* Usage: {{ [3, 1, 2] | sort(true) }} → [3, 2, 1]
|
|
231
|
+
*/
|
|
232
|
+
export function sort(value, reverse = false) {
|
|
233
|
+
if (!Array.isArray(value))
|
|
234
|
+
return [value];
|
|
235
|
+
const sorted = [...value].sort((a, b) => {
|
|
236
|
+
if (typeof a === 'number' && typeof b === 'number')
|
|
237
|
+
return a - b;
|
|
238
|
+
return String(a).localeCompare(String(b));
|
|
239
|
+
});
|
|
240
|
+
return reverse ? sorted.reverse() : sorted;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Get array slice
|
|
244
|
+
* Usage: {{ [1, 2, 3, 4, 5] | slice(1, 3) }} → [2, 3]
|
|
245
|
+
*/
|
|
246
|
+
export function slice(value, start, end) {
|
|
247
|
+
if (!Array.isArray(value))
|
|
248
|
+
return [value];
|
|
249
|
+
return value.slice(start, end);
|
|
250
|
+
}
|
|
251
|
+
// ============================================================================
|
|
252
|
+
// Object Helpers
|
|
253
|
+
// ============================================================================
|
|
254
|
+
/**
|
|
255
|
+
* Get value at path
|
|
256
|
+
* Usage: {{ obj | path('user.name') }} → obj.user.name
|
|
257
|
+
*/
|
|
258
|
+
export function path(value, pathStr) {
|
|
259
|
+
if (typeof value !== 'object' || value === null) {
|
|
260
|
+
return undefined;
|
|
261
|
+
}
|
|
262
|
+
const keys = pathStr.split('.');
|
|
263
|
+
let result = value;
|
|
264
|
+
for (const key of keys) {
|
|
265
|
+
if (typeof result !== 'object' || result === null) {
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
268
|
+
result = result[key];
|
|
269
|
+
}
|
|
270
|
+
return result;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Get object keys
|
|
274
|
+
* Usage: {{ {a: 1, b: 2} | keys }} → ['a', 'b']
|
|
275
|
+
*/
|
|
276
|
+
export function keys(value) {
|
|
277
|
+
if (typeof value !== 'object' || value === null) {
|
|
278
|
+
return [];
|
|
279
|
+
}
|
|
280
|
+
return Object.keys(value);
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Get object values
|
|
284
|
+
* Usage: {{ {a: 1, b: 2} | values }} → [1, 2]
|
|
285
|
+
*/
|
|
286
|
+
export function values(value) {
|
|
287
|
+
if (typeof value !== 'object' || value === null) {
|
|
288
|
+
return [];
|
|
289
|
+
}
|
|
290
|
+
return Object.values(value);
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Get object entries (key-value pairs)
|
|
294
|
+
* Usage: {{ {a: 1, b: 2} | entries }} → [['a', 1], ['b', 2]]
|
|
295
|
+
*/
|
|
296
|
+
export function entries(value) {
|
|
297
|
+
if (typeof value !== 'object' || value === null) {
|
|
298
|
+
return [];
|
|
299
|
+
}
|
|
300
|
+
return Object.entries(value);
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Pick specific keys from object
|
|
304
|
+
* Usage: {{ {a: 1, b: 2, c: 3} | pick('a', 'c') }} → {a: 1, c: 3}
|
|
305
|
+
*/
|
|
306
|
+
export function pick(value, ...keysToPick) {
|
|
307
|
+
if (typeof value !== 'object' || value === null) {
|
|
308
|
+
return {};
|
|
309
|
+
}
|
|
310
|
+
const obj = value;
|
|
311
|
+
const result = {};
|
|
312
|
+
for (const key of keysToPick) {
|
|
313
|
+
if (key in obj) {
|
|
314
|
+
result[key] = obj[key];
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return result;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Omit specific keys from object
|
|
321
|
+
* Usage: {{ {a: 1, b: 2, c: 3} | omit('b') }} → {a: 1, c: 3}
|
|
322
|
+
*/
|
|
323
|
+
export function omit(value, ...keysToOmit) {
|
|
324
|
+
if (typeof value !== 'object' || value === null) {
|
|
325
|
+
return {};
|
|
326
|
+
}
|
|
327
|
+
const obj = value;
|
|
328
|
+
const result = {};
|
|
329
|
+
const omitSet = new Set(keysToOmit);
|
|
330
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
331
|
+
if (!omitSet.has(key)) {
|
|
332
|
+
result[key] = val;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return result;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Merge objects
|
|
339
|
+
* Usage: {{ {a: 1} | merge({b: 2}) }} → {a: 1, b: 2}
|
|
340
|
+
*/
|
|
341
|
+
export function merge(value, ...objects) {
|
|
342
|
+
if (typeof value !== 'object' || value === null) {
|
|
343
|
+
return {};
|
|
344
|
+
}
|
|
345
|
+
let result = { ...value };
|
|
346
|
+
for (const obj of objects) {
|
|
347
|
+
if (typeof obj === 'object' && obj !== null) {
|
|
348
|
+
result = { ...result, ...obj };
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return result;
|
|
352
|
+
}
|
|
353
|
+
// ============================================================================
|
|
354
|
+
// Date Helpers
|
|
355
|
+
// ============================================================================
|
|
356
|
+
/**
|
|
357
|
+
* Get current timestamp
|
|
358
|
+
* Usage: {{ now() }} → 1706745600000
|
|
359
|
+
*/
|
|
360
|
+
export function now() {
|
|
361
|
+
return Date.now();
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Format date
|
|
365
|
+
* Usage: {{ timestamp | format_date('YYYY-MM-DD') }} → "2025-01-31"
|
|
366
|
+
*/
|
|
367
|
+
export function format_date(value, format = 'YYYY-MM-DD') {
|
|
368
|
+
let date;
|
|
369
|
+
if (value instanceof Date) {
|
|
370
|
+
date = value;
|
|
371
|
+
}
|
|
372
|
+
else if (typeof value === 'string' || typeof value === 'number') {
|
|
373
|
+
date = new Date(value);
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
date = new Date();
|
|
377
|
+
}
|
|
378
|
+
if (isNaN(date.getTime())) {
|
|
379
|
+
return 'Invalid Date';
|
|
380
|
+
}
|
|
381
|
+
// Simple date formatting
|
|
382
|
+
let formatted = format;
|
|
383
|
+
formatted = formatted.replace('YYYY', date.getFullYear().toString());
|
|
384
|
+
formatted = formatted.replace('MM', String(date.getMonth() + 1).padStart(2, '0'));
|
|
385
|
+
formatted = formatted.replace('DD', String(date.getDate()).padStart(2, '0'));
|
|
386
|
+
formatted = formatted.replace('HH', String(date.getHours()).padStart(2, '0'));
|
|
387
|
+
formatted = formatted.replace('mm', String(date.getMinutes()).padStart(2, '0'));
|
|
388
|
+
formatted = formatted.replace('ss', String(date.getSeconds()).padStart(2, '0'));
|
|
389
|
+
return formatted;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Add days to date
|
|
393
|
+
* Usage: {{ timestamp | add_days(7) }} → timestamp + 7 days
|
|
394
|
+
*/
|
|
395
|
+
export function add_days(value, days) {
|
|
396
|
+
const date = new Date(value);
|
|
397
|
+
if (isNaN(date.getTime()))
|
|
398
|
+
return 0;
|
|
399
|
+
date.setDate(date.getDate() + days);
|
|
400
|
+
return date.getTime();
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Subtract days from date
|
|
404
|
+
* Usage: {{ timestamp | subtract_days(7) }} → timestamp - 7 days
|
|
405
|
+
*/
|
|
406
|
+
export function subtract_days(value, days) {
|
|
407
|
+
const date = new Date(value);
|
|
408
|
+
if (isNaN(date.getTime()))
|
|
409
|
+
return 0;
|
|
410
|
+
date.setDate(date.getDate() - days);
|
|
411
|
+
return date.getTime();
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Get date difference in days (date1 - date2)
|
|
415
|
+
* Usage: {{ date1 | diff_days(date2) }} → number of days
|
|
416
|
+
*/
|
|
417
|
+
export function diff_days(value, compareDate) {
|
|
418
|
+
const date1 = new Date(value);
|
|
419
|
+
const date2 = new Date(compareDate);
|
|
420
|
+
if (isNaN(date1.getTime()) || isNaN(date2.getTime()))
|
|
421
|
+
return 0;
|
|
422
|
+
const diffMs = date1.getTime() - date2.getTime();
|
|
423
|
+
return Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
424
|
+
}
|
|
425
|
+
// ============================================================================
|
|
426
|
+
// Logic Helpers
|
|
427
|
+
// ============================================================================
|
|
428
|
+
/**
|
|
429
|
+
* Return default value if input is null/undefined
|
|
430
|
+
* Usage: {{ null | default('N/A') }} → "N/A"
|
|
431
|
+
*/
|
|
432
|
+
export function defaultValue(value, defaultVal) {
|
|
433
|
+
if (value === null || value === undefined) {
|
|
434
|
+
return defaultVal;
|
|
435
|
+
}
|
|
436
|
+
return value;
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Return first truthy value
|
|
440
|
+
* Usage: {{ null | or('fallback') }} → "fallback"
|
|
441
|
+
*/
|
|
442
|
+
export function or(value, ...alternatives) {
|
|
443
|
+
if (value)
|
|
444
|
+
return value;
|
|
445
|
+
for (const alt of alternatives) {
|
|
446
|
+
if (alt)
|
|
447
|
+
return alt;
|
|
448
|
+
}
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Check if all values are truthy
|
|
453
|
+
* Usage: {{ true | and(1, 'value') }} → true
|
|
454
|
+
*/
|
|
455
|
+
export function and(value, ...values) {
|
|
456
|
+
if (!value)
|
|
457
|
+
return false;
|
|
458
|
+
for (const v of values) {
|
|
459
|
+
if (!v)
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
return true;
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Negate boolean value
|
|
466
|
+
* Usage: {{ true | not }} → false
|
|
467
|
+
*/
|
|
468
|
+
export function not(value) {
|
|
469
|
+
return !value;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Ternary operator
|
|
473
|
+
* Usage: {{ condition | ternary('yes', 'no') }}
|
|
474
|
+
*/
|
|
475
|
+
export function ternary(condition, trueVal, falseVal) {
|
|
476
|
+
return condition ? trueVal : falseVal;
|
|
477
|
+
}
|
|
478
|
+
// ============================================================================
|
|
479
|
+
// Validation Helpers
|
|
480
|
+
// ============================================================================
|
|
481
|
+
/**
|
|
482
|
+
* Check if value is array
|
|
483
|
+
* Usage: {{ value | is_array }} → true/false
|
|
484
|
+
*/
|
|
485
|
+
export function is_array(value) {
|
|
486
|
+
return Array.isArray(value);
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Check if value is object
|
|
490
|
+
* Usage: {{ value | is_object }} → true/false
|
|
491
|
+
*/
|
|
492
|
+
export function is_object(value) {
|
|
493
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Check if value is string
|
|
497
|
+
* Usage: {{ value | is_string }} → true/false
|
|
498
|
+
*/
|
|
499
|
+
export function is_string(value) {
|
|
500
|
+
return typeof value === 'string';
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Check if value is number
|
|
504
|
+
* Usage: {{ value | is_number }} → true/false
|
|
505
|
+
*/
|
|
506
|
+
export function is_number(value) {
|
|
507
|
+
return typeof value === 'number' && !isNaN(value);
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* Check if value is empty
|
|
511
|
+
* Usage: {{ value | is_empty }} → true/false
|
|
512
|
+
*/
|
|
513
|
+
export function is_empty(value) {
|
|
514
|
+
if (value === null || value === undefined)
|
|
515
|
+
return true;
|
|
516
|
+
if (typeof value === 'string')
|
|
517
|
+
return value.length === 0;
|
|
518
|
+
if (Array.isArray(value))
|
|
519
|
+
return value.length === 0;
|
|
520
|
+
if (typeof value === 'object')
|
|
521
|
+
return Object.keys(value).length === 0;
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Check if value is null
|
|
526
|
+
* Usage: {{ value | is_null }} → true/false
|
|
527
|
+
*/
|
|
528
|
+
export function is_null(value) {
|
|
529
|
+
return value === null;
|
|
530
|
+
}
|
|
531
|
+
// ============================================================================
|
|
532
|
+
// JSON Helpers
|
|
533
|
+
// ============================================================================
|
|
534
|
+
/**
|
|
535
|
+
* Parse JSON string
|
|
536
|
+
* Usage: {{ '{"a":1}' | parse_json }} → {a: 1}
|
|
537
|
+
*/
|
|
538
|
+
export function parse_json(value) {
|
|
539
|
+
try {
|
|
540
|
+
return JSON.parse(String(value));
|
|
541
|
+
}
|
|
542
|
+
catch {
|
|
543
|
+
return null;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Stringify to JSON
|
|
548
|
+
* Usage: {{ {a: 1} | to_json }} → '{"a":1}'
|
|
549
|
+
*/
|
|
550
|
+
export function to_json(value, pretty = false) {
|
|
551
|
+
return JSON.stringify(value, null, pretty ? 2 : 0);
|
|
552
|
+
}
|
|
553
|
+
// ============================================================================
|
|
554
|
+
// Math Helpers
|
|
555
|
+
// ============================================================================
|
|
556
|
+
/**
|
|
557
|
+
* Absolute value
|
|
558
|
+
* Usage: {{ -5 | abs }} → 5
|
|
559
|
+
*/
|
|
560
|
+
export function abs(value) {
|
|
561
|
+
return Math.abs(Number(value));
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Round to nearest integer
|
|
565
|
+
* Usage: {{ 3.7 | round }} → 4
|
|
566
|
+
*/
|
|
567
|
+
export function round(value, decimals = 0) {
|
|
568
|
+
const num = Number(value);
|
|
569
|
+
const multiplier = Math.pow(10, decimals);
|
|
570
|
+
return Math.round(num * multiplier) / multiplier;
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Round down
|
|
574
|
+
* Usage: {{ 3.7 | floor }} → 3
|
|
575
|
+
*/
|
|
576
|
+
export function floor(value) {
|
|
577
|
+
return Math.floor(Number(value));
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Round up
|
|
581
|
+
* Usage: {{ 3.1 | ceil }} → 4
|
|
582
|
+
*/
|
|
583
|
+
export function ceil(value) {
|
|
584
|
+
return Math.ceil(Number(value));
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Get minimum value
|
|
588
|
+
* Usage: {{ [1, 2, 3] | min }} → 1
|
|
589
|
+
* Usage: {{ 5 | min(3) }} → 3
|
|
590
|
+
*/
|
|
591
|
+
export function min(value, ...values) {
|
|
592
|
+
if (Array.isArray(value)) {
|
|
593
|
+
return Math.min(...value.map(v => Number(v)));
|
|
594
|
+
}
|
|
595
|
+
const nums = [Number(value), ...values.map(v => Number(v))];
|
|
596
|
+
return Math.min(...nums);
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Get maximum value
|
|
600
|
+
* Usage: {{ [1, 2, 3] | max }} → 3
|
|
601
|
+
* Usage: {{ 3 | max(5) }} → 5
|
|
602
|
+
*/
|
|
603
|
+
export function max(value, ...values) {
|
|
604
|
+
if (Array.isArray(value)) {
|
|
605
|
+
return Math.max(...value.map(v => Number(v)));
|
|
606
|
+
}
|
|
607
|
+
const nums = [Number(value), ...values.map(v => Number(v))];
|
|
608
|
+
return Math.max(...nums);
|
|
609
|
+
}
|
|
610
|
+
// ============================================================================
|
|
611
|
+
// Helper Registry
|
|
612
|
+
// ============================================================================
|
|
613
|
+
/**
|
|
614
|
+
* Global registry of all helper functions
|
|
615
|
+
*/
|
|
616
|
+
export const HELPER_REGISTRY = {
|
|
617
|
+
// String helpers
|
|
618
|
+
split: split,
|
|
619
|
+
join: join,
|
|
620
|
+
trim: trim,
|
|
621
|
+
upper: upper,
|
|
622
|
+
lower: lower,
|
|
623
|
+
title: title,
|
|
624
|
+
capitalize: capitalize,
|
|
625
|
+
slugify: slugify,
|
|
626
|
+
prefix: prefix,
|
|
627
|
+
suffix: suffix,
|
|
628
|
+
replace: replace,
|
|
629
|
+
truncate: truncate,
|
|
630
|
+
substring: substring,
|
|
631
|
+
contains: contains,
|
|
632
|
+
// Array helpers
|
|
633
|
+
first: first,
|
|
634
|
+
last: last,
|
|
635
|
+
nth: nth,
|
|
636
|
+
count: count,
|
|
637
|
+
sum: sum,
|
|
638
|
+
unique: unique,
|
|
639
|
+
flatten: flatten,
|
|
640
|
+
reverse: reverse,
|
|
641
|
+
sort: sort,
|
|
642
|
+
slice: slice,
|
|
643
|
+
// Object helpers
|
|
644
|
+
path: path,
|
|
645
|
+
keys: keys,
|
|
646
|
+
values: values,
|
|
647
|
+
entries: entries,
|
|
648
|
+
pick: pick,
|
|
649
|
+
omit: omit,
|
|
650
|
+
merge: merge,
|
|
651
|
+
// Date helpers
|
|
652
|
+
now: now,
|
|
653
|
+
format_date: format_date,
|
|
654
|
+
add_days: add_days,
|
|
655
|
+
subtract_days: subtract_days,
|
|
656
|
+
diff_days: diff_days,
|
|
657
|
+
// Logic helpers
|
|
658
|
+
default: defaultValue,
|
|
659
|
+
or: or,
|
|
660
|
+
and: and,
|
|
661
|
+
not: not,
|
|
662
|
+
ternary: ternary,
|
|
663
|
+
// Validation helpers
|
|
664
|
+
is_array: is_array,
|
|
665
|
+
is_object: is_object,
|
|
666
|
+
is_string: is_string,
|
|
667
|
+
is_number: is_number,
|
|
668
|
+
is_empty: is_empty,
|
|
669
|
+
is_null: is_null,
|
|
670
|
+
// JSON helpers
|
|
671
|
+
parse_json: parse_json,
|
|
672
|
+
to_json: to_json,
|
|
673
|
+
// Math helpers
|
|
674
|
+
abs: abs,
|
|
675
|
+
round: round,
|
|
676
|
+
floor: floor,
|
|
677
|
+
ceil: ceil,
|
|
678
|
+
min: min,
|
|
679
|
+
max: max,
|
|
680
|
+
};
|
|
681
|
+
/**
|
|
682
|
+
* Apply a helper function to a value
|
|
683
|
+
*/
|
|
684
|
+
export function applyHelper(helperName, value, args) {
|
|
685
|
+
const helper = HELPER_REGISTRY[helperName];
|
|
686
|
+
if (!helper) {
|
|
687
|
+
throw new Error(`Unknown helper function: ${helperName}`);
|
|
688
|
+
}
|
|
689
|
+
return helper(value, ...args);
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Check if a helper exists
|
|
693
|
+
*/
|
|
694
|
+
export function hasHelper(helperName) {
|
|
695
|
+
return helperName in HELPER_REGISTRY;
|
|
696
|
+
}
|
|
697
|
+
//# sourceMappingURL=expression-helpers.js.map
|