@hostwebhook/template-engine 1.0.0 → 1.1.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.
- package/dist/index.d.mts +7 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.js +72 -5
- package/dist/index.mjs +72 -5
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -74,9 +74,14 @@ declare function tryNullCoalesce(expr: string, ctx: TemplateContext): string | n
|
|
|
74
74
|
*/
|
|
75
75
|
declare function tryTernary(expr: string, ctx: TemplateContext): string | null;
|
|
76
76
|
/**
|
|
77
|
-
* Apply a pipe transform
|
|
77
|
+
* Apply a pipe transform.
|
|
78
|
+
*
|
|
79
|
+
* String: | json, | upper, | lower, | trim
|
|
80
|
+
* Date: | formatDate, | formatTime, | formatDateTime, | toISO, | toEpoch
|
|
81
|
+
* | diffDays(otherPath), | diffHours(otherPath)
|
|
82
|
+
* Number: | currency, | percent, | round(decimals)
|
|
78
83
|
*/
|
|
79
|
-
declare function applyPipe(pipe: string, val: unknown): string;
|
|
84
|
+
declare function applyPipe(pipe: string, val: unknown, arg?: string, ctx?: TemplateContext): string;
|
|
80
85
|
/**
|
|
81
86
|
* Evaluate a single {{...}} expression and return a string.
|
|
82
87
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -74,9 +74,14 @@ declare function tryNullCoalesce(expr: string, ctx: TemplateContext): string | n
|
|
|
74
74
|
*/
|
|
75
75
|
declare function tryTernary(expr: string, ctx: TemplateContext): string | null;
|
|
76
76
|
/**
|
|
77
|
-
* Apply a pipe transform
|
|
77
|
+
* Apply a pipe transform.
|
|
78
|
+
*
|
|
79
|
+
* String: | json, | upper, | lower, | trim
|
|
80
|
+
* Date: | formatDate, | formatTime, | formatDateTime, | toISO, | toEpoch
|
|
81
|
+
* | diffDays(otherPath), | diffHours(otherPath)
|
|
82
|
+
* Number: | currency, | percent, | round(decimals)
|
|
78
83
|
*/
|
|
79
|
-
declare function applyPipe(pipe: string, val: unknown): string;
|
|
84
|
+
declare function applyPipe(pipe: string, val: unknown, arg?: string, ctx?: TemplateContext): string;
|
|
80
85
|
/**
|
|
81
86
|
* Evaluate a single {{...}} expression and return a string.
|
|
82
87
|
*/
|
package/dist/index.js
CHANGED
|
@@ -218,7 +218,8 @@ var TRUTHY_RE = new RegExp(
|
|
|
218
218
|
`^(!?)(.+?)\\s*\\?\\s*(${BRANCH})\\s*:\\s*(${BRANCH})$`
|
|
219
219
|
);
|
|
220
220
|
var NULL_COALESCE_RE = /^(.+?)\s*\?\?\s*("(?:[^"]*)"$|'(?:[^']*)'$|\S+$)/;
|
|
221
|
-
var
|
|
221
|
+
var PIPE_NAMES = "json|upper|lower|trim|diffDays|diffHours|formatDate|formatTime|formatDateTime|toISO|toEpoch|currency|percent|round";
|
|
222
|
+
var PIPE_RE = new RegExp(`^(.+?)\\s*\\|\\s*(${PIPE_NAMES})(?:\\((.+?)\\))?$`);
|
|
222
223
|
function tryNullCoalesce(expr, ctx) {
|
|
223
224
|
const m = expr.match(NULL_COALESCE_RE);
|
|
224
225
|
if (!m) return null;
|
|
@@ -245,8 +246,21 @@ function tryTernary(expr, ctx) {
|
|
|
245
246
|
if (negate === "!") truthy = !truthy;
|
|
246
247
|
return truthy ? resolveBranch(trueRaw, ctx) : resolveBranch(falseRaw, ctx);
|
|
247
248
|
}
|
|
248
|
-
function
|
|
249
|
+
function toDate(val) {
|
|
250
|
+
if (val instanceof Date) return val;
|
|
251
|
+
if (typeof val === "number") return new Date(val);
|
|
252
|
+
if (typeof val === "string") {
|
|
253
|
+
const d = new Date(val);
|
|
254
|
+
return isNaN(d.getTime()) ? null : d;
|
|
255
|
+
}
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
function pad2(n) {
|
|
259
|
+
return n < 10 ? `0${n}` : String(n);
|
|
260
|
+
}
|
|
261
|
+
function applyPipe(pipe, val, arg, ctx) {
|
|
249
262
|
switch (pipe) {
|
|
263
|
+
// ── String ───────────────────────────────────────────────
|
|
250
264
|
case "json":
|
|
251
265
|
return JSON.stringify(val, null, 2);
|
|
252
266
|
case "upper":
|
|
@@ -255,6 +269,59 @@ function applyPipe(pipe, val) {
|
|
|
255
269
|
return String(val ?? "").toLowerCase();
|
|
256
270
|
case "trim":
|
|
257
271
|
return String(val ?? "").trim();
|
|
272
|
+
// ── Date formatting ──────────────────────────────────────
|
|
273
|
+
case "formatDate": {
|
|
274
|
+
const d = toDate(val);
|
|
275
|
+
if (!d) return "";
|
|
276
|
+
return `${d.getUTCFullYear()}-${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())}`;
|
|
277
|
+
}
|
|
278
|
+
case "formatTime": {
|
|
279
|
+
const d = toDate(val);
|
|
280
|
+
if (!d) return "";
|
|
281
|
+
return `${pad2(d.getUTCHours())}:${pad2(d.getUTCMinutes())}:${pad2(d.getUTCSeconds())}`;
|
|
282
|
+
}
|
|
283
|
+
case "formatDateTime": {
|
|
284
|
+
const d = toDate(val);
|
|
285
|
+
if (!d) return "";
|
|
286
|
+
return `${d.getUTCFullYear()}-${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())} ${pad2(d.getUTCHours())}:${pad2(d.getUTCMinutes())}:${pad2(d.getUTCSeconds())}`;
|
|
287
|
+
}
|
|
288
|
+
case "toISO": {
|
|
289
|
+
const d = toDate(val);
|
|
290
|
+
return d ? d.toISOString() : "";
|
|
291
|
+
}
|
|
292
|
+
case "toEpoch": {
|
|
293
|
+
const d = toDate(val);
|
|
294
|
+
return d ? String(d.getTime()) : "";
|
|
295
|
+
}
|
|
296
|
+
// ── Date diff ────────────────────────────────────────────
|
|
297
|
+
case "diffDays":
|
|
298
|
+
case "diffHours": {
|
|
299
|
+
const d1 = toDate(val);
|
|
300
|
+
if (!d1 || !arg || !ctx) return "";
|
|
301
|
+
const otherVal = resolvePath(arg.trim(), ctx);
|
|
302
|
+
const d2 = toDate(otherVal);
|
|
303
|
+
if (!d2) return "";
|
|
304
|
+
const diffMs = Math.abs(d1.getTime() - d2.getTime());
|
|
305
|
+
if (pipe === "diffDays") return String(Math.floor(diffMs / 864e5));
|
|
306
|
+
return String(Math.floor(diffMs / 36e5));
|
|
307
|
+
}
|
|
308
|
+
// ── Number ───────────────────────────────────────────────
|
|
309
|
+
case "currency": {
|
|
310
|
+
const n = Number(val);
|
|
311
|
+
if (isNaN(n)) return "";
|
|
312
|
+
return `$${n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
313
|
+
}
|
|
314
|
+
case "percent": {
|
|
315
|
+
const n = Number(val);
|
|
316
|
+
if (isNaN(n)) return "";
|
|
317
|
+
return `${n}%`;
|
|
318
|
+
}
|
|
319
|
+
case "round": {
|
|
320
|
+
const n = Number(val);
|
|
321
|
+
if (isNaN(n)) return "";
|
|
322
|
+
const decimals = arg ? parseInt(arg, 10) : 0;
|
|
323
|
+
return n.toFixed(isNaN(decimals) ? 0 : decimals);
|
|
324
|
+
}
|
|
258
325
|
default:
|
|
259
326
|
return valueToString(val);
|
|
260
327
|
}
|
|
@@ -271,9 +338,9 @@ function evaluate(expr, ctx, opts) {
|
|
|
271
338
|
}
|
|
272
339
|
const pipeM = trimmed.match(PIPE_RE);
|
|
273
340
|
if (pipeM) {
|
|
274
|
-
const [, path, pipe] = pipeM;
|
|
341
|
+
const [, path, pipe, arg] = pipeM;
|
|
275
342
|
const val = resolvePath(path.trim(), ctx);
|
|
276
|
-
return applyPipe(pipe, val);
|
|
343
|
+
return applyPipe(pipe, val, arg, ctx);
|
|
277
344
|
}
|
|
278
345
|
const ncResult = tryNullCoalesce(trimmed, ctx);
|
|
279
346
|
if (ncResult !== null) return ncResult;
|
|
@@ -309,7 +376,7 @@ function resolveJsonValue(value, ctx, opts) {
|
|
|
309
376
|
const val = resolvePath(expr.slice(1).trim(), ctx);
|
|
310
377
|
return !val;
|
|
311
378
|
}
|
|
312
|
-
if (/\|\s
|
|
379
|
+
if (/\|\s*\w+/.test(expr)) {
|
|
313
380
|
return evaluate(expr, ctx, opts);
|
|
314
381
|
}
|
|
315
382
|
const resolved = resolvePath(expr, ctx);
|
package/dist/index.mjs
CHANGED
|
@@ -176,7 +176,8 @@ var TRUTHY_RE = new RegExp(
|
|
|
176
176
|
`^(!?)(.+?)\\s*\\?\\s*(${BRANCH})\\s*:\\s*(${BRANCH})$`
|
|
177
177
|
);
|
|
178
178
|
var NULL_COALESCE_RE = /^(.+?)\s*\?\?\s*("(?:[^"]*)"$|'(?:[^']*)'$|\S+$)/;
|
|
179
|
-
var
|
|
179
|
+
var PIPE_NAMES = "json|upper|lower|trim|diffDays|diffHours|formatDate|formatTime|formatDateTime|toISO|toEpoch|currency|percent|round";
|
|
180
|
+
var PIPE_RE = new RegExp(`^(.+?)\\s*\\|\\s*(${PIPE_NAMES})(?:\\((.+?)\\))?$`);
|
|
180
181
|
function tryNullCoalesce(expr, ctx) {
|
|
181
182
|
const m = expr.match(NULL_COALESCE_RE);
|
|
182
183
|
if (!m) return null;
|
|
@@ -203,8 +204,21 @@ function tryTernary(expr, ctx) {
|
|
|
203
204
|
if (negate === "!") truthy = !truthy;
|
|
204
205
|
return truthy ? resolveBranch(trueRaw, ctx) : resolveBranch(falseRaw, ctx);
|
|
205
206
|
}
|
|
206
|
-
function
|
|
207
|
+
function toDate(val) {
|
|
208
|
+
if (val instanceof Date) return val;
|
|
209
|
+
if (typeof val === "number") return new Date(val);
|
|
210
|
+
if (typeof val === "string") {
|
|
211
|
+
const d = new Date(val);
|
|
212
|
+
return isNaN(d.getTime()) ? null : d;
|
|
213
|
+
}
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
function pad2(n) {
|
|
217
|
+
return n < 10 ? `0${n}` : String(n);
|
|
218
|
+
}
|
|
219
|
+
function applyPipe(pipe, val, arg, ctx) {
|
|
207
220
|
switch (pipe) {
|
|
221
|
+
// ── String ───────────────────────────────────────────────
|
|
208
222
|
case "json":
|
|
209
223
|
return JSON.stringify(val, null, 2);
|
|
210
224
|
case "upper":
|
|
@@ -213,6 +227,59 @@ function applyPipe(pipe, val) {
|
|
|
213
227
|
return String(val ?? "").toLowerCase();
|
|
214
228
|
case "trim":
|
|
215
229
|
return String(val ?? "").trim();
|
|
230
|
+
// ── Date formatting ──────────────────────────────────────
|
|
231
|
+
case "formatDate": {
|
|
232
|
+
const d = toDate(val);
|
|
233
|
+
if (!d) return "";
|
|
234
|
+
return `${d.getUTCFullYear()}-${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())}`;
|
|
235
|
+
}
|
|
236
|
+
case "formatTime": {
|
|
237
|
+
const d = toDate(val);
|
|
238
|
+
if (!d) return "";
|
|
239
|
+
return `${pad2(d.getUTCHours())}:${pad2(d.getUTCMinutes())}:${pad2(d.getUTCSeconds())}`;
|
|
240
|
+
}
|
|
241
|
+
case "formatDateTime": {
|
|
242
|
+
const d = toDate(val);
|
|
243
|
+
if (!d) return "";
|
|
244
|
+
return `${d.getUTCFullYear()}-${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())} ${pad2(d.getUTCHours())}:${pad2(d.getUTCMinutes())}:${pad2(d.getUTCSeconds())}`;
|
|
245
|
+
}
|
|
246
|
+
case "toISO": {
|
|
247
|
+
const d = toDate(val);
|
|
248
|
+
return d ? d.toISOString() : "";
|
|
249
|
+
}
|
|
250
|
+
case "toEpoch": {
|
|
251
|
+
const d = toDate(val);
|
|
252
|
+
return d ? String(d.getTime()) : "";
|
|
253
|
+
}
|
|
254
|
+
// ── Date diff ────────────────────────────────────────────
|
|
255
|
+
case "diffDays":
|
|
256
|
+
case "diffHours": {
|
|
257
|
+
const d1 = toDate(val);
|
|
258
|
+
if (!d1 || !arg || !ctx) return "";
|
|
259
|
+
const otherVal = resolvePath(arg.trim(), ctx);
|
|
260
|
+
const d2 = toDate(otherVal);
|
|
261
|
+
if (!d2) return "";
|
|
262
|
+
const diffMs = Math.abs(d1.getTime() - d2.getTime());
|
|
263
|
+
if (pipe === "diffDays") return String(Math.floor(diffMs / 864e5));
|
|
264
|
+
return String(Math.floor(diffMs / 36e5));
|
|
265
|
+
}
|
|
266
|
+
// ── Number ───────────────────────────────────────────────
|
|
267
|
+
case "currency": {
|
|
268
|
+
const n = Number(val);
|
|
269
|
+
if (isNaN(n)) return "";
|
|
270
|
+
return `$${n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
271
|
+
}
|
|
272
|
+
case "percent": {
|
|
273
|
+
const n = Number(val);
|
|
274
|
+
if (isNaN(n)) return "";
|
|
275
|
+
return `${n}%`;
|
|
276
|
+
}
|
|
277
|
+
case "round": {
|
|
278
|
+
const n = Number(val);
|
|
279
|
+
if (isNaN(n)) return "";
|
|
280
|
+
const decimals = arg ? parseInt(arg, 10) : 0;
|
|
281
|
+
return n.toFixed(isNaN(decimals) ? 0 : decimals);
|
|
282
|
+
}
|
|
216
283
|
default:
|
|
217
284
|
return valueToString(val);
|
|
218
285
|
}
|
|
@@ -229,9 +296,9 @@ function evaluate(expr, ctx, opts) {
|
|
|
229
296
|
}
|
|
230
297
|
const pipeM = trimmed.match(PIPE_RE);
|
|
231
298
|
if (pipeM) {
|
|
232
|
-
const [, path, pipe] = pipeM;
|
|
299
|
+
const [, path, pipe, arg] = pipeM;
|
|
233
300
|
const val = resolvePath(path.trim(), ctx);
|
|
234
|
-
return applyPipe(pipe, val);
|
|
301
|
+
return applyPipe(pipe, val, arg, ctx);
|
|
235
302
|
}
|
|
236
303
|
const ncResult = tryNullCoalesce(trimmed, ctx);
|
|
237
304
|
if (ncResult !== null) return ncResult;
|
|
@@ -267,7 +334,7 @@ function resolveJsonValue(value, ctx, opts) {
|
|
|
267
334
|
const val = resolvePath(expr.slice(1).trim(), ctx);
|
|
268
335
|
return !val;
|
|
269
336
|
}
|
|
270
|
-
if (/\|\s
|
|
337
|
+
if (/\|\s*\w+/.test(expr)) {
|
|
271
338
|
return evaluate(expr, ctx, opts);
|
|
272
339
|
}
|
|
273
340
|
const resolved = resolvePath(expr, ctx);
|