jtlt 0.13.0 → 0.14.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/CHANGES.md +10 -0
- package/README.md +99 -85
- package/demo/calltemplate-params-demo.js +13 -22
- package/demo/vendor/jhtml/src/SAJJ/SAJJ.ObjectArrayDelegator.js +17 -17
- package/demo/vendor/jhtml/src/SAJJ/SAJJ.js +60 -61
- package/demo/vendor/jhtml/src/jhtml-browser.js +1 -0
- package/demo/vendor/jhtml/src/jhtml-node.js +1 -0
- package/demo/vendor/jhtml/src/jhtml.js +18 -15
- package/dist/AbstractJoiningTransformer.d.ts +5 -5
- package/dist/AbstractJoiningTransformer.d.ts.map +1 -1
- package/dist/JSONPathTransformer.d.ts +13 -12
- package/dist/JSONPathTransformer.d.ts.map +1 -1
- package/dist/JSONPathTransformerContext.d.ts +50 -20
- package/dist/JSONPathTransformerContext.d.ts.map +1 -1
- package/dist/XPathTransformer.d.ts +9 -2
- package/dist/XPathTransformer.d.ts.map +1 -1
- package/dist/XPathTransformerContext.d.ts +46 -9
- package/dist/XPathTransformerContext.d.ts.map +1 -1
- package/dist/index.d.ts +104 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/indexedDB.d.ts +107 -0
- package/dist/indexedDB.d.ts.map +1 -0
- package/dist/maybeAsync.d.ts +15 -0
- package/dist/maybeAsync.d.ts.map +1 -0
- package/docs/API.expanded.md +70 -20
- package/docs/API.md +34 -4
- package/docs/TO-DO.md +2 -2
- package/eslint.config.js +11 -3
- package/package.json +7 -5
- package/pnpm-workspace.yaml +9 -0
- package/src/AbstractJoiningTransformer.js +3 -3
- package/src/JSONPathTransformer.js +45 -7
- package/src/JSONPathTransformerContext.js +157 -22
- package/src/XPathTransformer.js +33 -1
- package/src/XPathTransformerContext.js +136 -16
- package/src/index.js +85 -8
- package/src/indexedDB.js +530 -0
- package/src/maybeAsync.js +44 -0
|
@@ -2,6 +2,12 @@ import xpath2 from 'xpath2.js'; // Runtime JS import; ambient types declared
|
|
|
2
2
|
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
3
3
|
// xpathVersion: 1 => browser/native XPathEvaluator API; 2 => xpath2.js, 3 => fontoxpath
|
|
4
4
|
import fontoxpath from 'fontoxpath';
|
|
5
|
+
import {maybeAsyncLoop} from './maybeAsync.js';
|
|
6
|
+
import {
|
|
7
|
+
queryIndexedDB,
|
|
8
|
+
xpathExpressionUsesIndexedDB,
|
|
9
|
+
evaluateXPathWithIndexedDB
|
|
10
|
+
} from './indexedDB.js';
|
|
5
11
|
// import xsdValidator from 'xsd-validator';
|
|
6
12
|
|
|
7
13
|
/**
|
|
@@ -19,6 +25,10 @@ const escapeRegexReplacement = (string) => {
|
|
|
19
25
|
* @property {import('./index.js').
|
|
20
26
|
* JoiningTransformer} joiningTransformer Joiner
|
|
21
27
|
* @property {boolean} [errorOnEqualPriority]
|
|
28
|
+
* @property {boolean} [sync] - When true, throw if a template returns a
|
|
29
|
+
* Promise instead of awaiting it (disables `indexedDB()`)
|
|
30
|
+
* @property {boolean} [preventEval] - Whether to prevent eval in the JSONPath
|
|
31
|
+
* trailing segment of an `indexedDB(...)` expression
|
|
22
32
|
* @property {(path: string) => number} [specificityPriorityResolver]
|
|
23
33
|
*/
|
|
24
34
|
|
|
@@ -126,9 +136,14 @@ class XPathTransformerContext {
|
|
|
126
136
|
this._stripSpaceElements = [];
|
|
127
137
|
}
|
|
128
138
|
|
|
129
|
-
/** @returns {import('./index.js').
|
|
139
|
+
/** @returns {import('./index.js').BuiltinJoiningTransformer} */
|
|
130
140
|
_getJoiningTransformer () {
|
|
131
|
-
|
|
141
|
+
// Engine internals only ever run against a built-in joiner (they touch
|
|
142
|
+
// concrete members such as `_modeConfig`); a custom stub is the caller's
|
|
143
|
+
// responsibility.
|
|
144
|
+
return /** @type {import('./index.js').BuiltinJoiningTransformer} */ (
|
|
145
|
+
this._config.joiningTransformer
|
|
146
|
+
);
|
|
132
147
|
}
|
|
133
148
|
|
|
134
149
|
/**
|
|
@@ -398,9 +413,13 @@ class XPathTransformerContext {
|
|
|
398
413
|
});
|
|
399
414
|
|
|
400
415
|
const preApplyContext = this._contextNode;
|
|
416
|
+
const isSync = Boolean(/** @type {any} */ (this._config).sync);
|
|
401
417
|
|
|
402
|
-
// Process each node
|
|
403
|
-
|
|
418
|
+
// Process each node. `maybeAsyncLoop` keeps this a plain synchronous
|
|
419
|
+
// loop unless a template returns a Promise (e.g. via
|
|
420
|
+
// `await this.indexedDB(...)`), in which case it chains and the whole
|
|
421
|
+
// call becomes asynchronous.
|
|
422
|
+
const loopResult = maybeAsyncLoop(nodes, (node) => {
|
|
404
423
|
// Path resolution simplified (could track full XPath if needed)
|
|
405
424
|
const pathMatchedTemplates = modeMatched.filter((t) => {
|
|
406
425
|
// Basic matching: template.path is XPath tested for existence
|
|
@@ -438,7 +457,7 @@ class XPathTransformerContext {
|
|
|
438
457
|
}
|
|
439
458
|
if (onNoMatch === 'deep-skip') {
|
|
440
459
|
// Skip this node entirely
|
|
441
|
-
|
|
460
|
+
return undefined;
|
|
442
461
|
}
|
|
443
462
|
if (onNoMatch === 'shallow-copy') {
|
|
444
463
|
// Output the node without processing children
|
|
@@ -451,7 +470,7 @@ class XPathTransformerContext {
|
|
|
451
470
|
} else if (node.nodeType === 3 && node.nodeValue) { // Text
|
|
452
471
|
joiner.text(node.nodeValue);
|
|
453
472
|
}
|
|
454
|
-
|
|
473
|
+
return undefined;
|
|
455
474
|
}
|
|
456
475
|
if (onNoMatch === 'deep-copy') {
|
|
457
476
|
// Output the node and all descendants
|
|
@@ -462,7 +481,7 @@ class XPathTransformerContext {
|
|
|
462
481
|
} else if (node.nodeType === 3 && node.nodeValue) { // Text
|
|
463
482
|
joiner.text(node.nodeValue);
|
|
464
483
|
}
|
|
465
|
-
|
|
484
|
+
return undefined;
|
|
466
485
|
}
|
|
467
486
|
if (onNoMatch === 'text-only-copy') {
|
|
468
487
|
// Output only text content
|
|
@@ -474,7 +493,7 @@ class XPathTransformerContext {
|
|
|
474
493
|
joiner.text(textContent);
|
|
475
494
|
}
|
|
476
495
|
}
|
|
477
|
-
|
|
496
|
+
return undefined;
|
|
478
497
|
}
|
|
479
498
|
// 'apply-templates', 'shallow-skip', or other:
|
|
480
499
|
// use default template rules
|
|
@@ -569,11 +588,45 @@ class XPathTransformerContext {
|
|
|
569
588
|
const prevTemplateParams = this._params;
|
|
570
589
|
this._params = {0: node};
|
|
571
590
|
|
|
591
|
+
/**
|
|
592
|
+
* The template may return synchronously or return a Promise (e.g. from
|
|
593
|
+
* `await this.indexedDB(...)`), which is awaited unless `config.sync`.
|
|
594
|
+
* @type {any}
|
|
595
|
+
*/
|
|
572
596
|
const ret = templateObj.template.call(this, node, {mode});
|
|
573
597
|
|
|
574
598
|
// Restore previous parameter context
|
|
575
599
|
this._params = prevTemplateParams;
|
|
576
600
|
|
|
601
|
+
if (ret !== null && typeof ret !== 'undefined' &&
|
|
602
|
+
typeof ret.then === 'function') {
|
|
603
|
+
if (isSync) {
|
|
604
|
+
throw new Error(
|
|
605
|
+
'A template returned a Promise but JTLT is configured with ' +
|
|
606
|
+
'`sync: true`.'
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
610
|
+
// eslint-disable-next-line promise/prefer-await-to-then -- intentional dynamic sync/async
|
|
611
|
+
return ret.then((/** @type {any} */ resolvedRet) => {
|
|
612
|
+
if (typeof resolvedRet !== 'undefined') {
|
|
613
|
+
const joiner = this._getJoiningTransformer();
|
|
614
|
+
/* c8 ignore start -- _openTagState only on
|
|
615
|
+
StringJoiningTransformer; string-output defensive check */
|
|
616
|
+
// @ts-expect-error -- _openTagState: StringJoiningTransformer only
|
|
617
|
+
if (joiner._openTagState) {
|
|
618
|
+
joiner.append('>');
|
|
619
|
+
// @ts-expect-error -- _openTagState: StringJoiningTransformer
|
|
620
|
+
joiner._openTagState = false;
|
|
621
|
+
}
|
|
622
|
+
/* c8 ignore stop */
|
|
623
|
+
joiner.append(resolvedRet);
|
|
624
|
+
}
|
|
625
|
+
this._contextNode = node;
|
|
626
|
+
return undefined;
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
|
|
577
630
|
if (typeof ret !== 'undefined') {
|
|
578
631
|
const joiner = this._getJoiningTransformer();
|
|
579
632
|
// Close any open tag before appending template return value
|
|
@@ -586,6 +639,18 @@ class XPathTransformerContext {
|
|
|
586
639
|
joiner.append(ret);
|
|
587
640
|
}
|
|
588
641
|
this._contextNode = node; // Restore (placeholder for more complex state)
|
|
642
|
+
return undefined;
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
if (typeof loopResult?.then === 'function') {
|
|
646
|
+
return /** @type {any} */ (
|
|
647
|
+
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
648
|
+
// eslint-disable-next-line promise/prefer-await-to-then -- intentional dynamic sync/async
|
|
649
|
+
loopResult.then(() => {
|
|
650
|
+
this._contextNode = preApplyContext;
|
|
651
|
+
return this;
|
|
652
|
+
})
|
|
653
|
+
);
|
|
589
654
|
}
|
|
590
655
|
|
|
591
656
|
this._contextNode = preApplyContext;
|
|
@@ -691,7 +756,10 @@ class XPathTransformerContext {
|
|
|
691
756
|
* @param {string} [options.groupEndingWith] - Ends group when expression
|
|
692
757
|
* matches
|
|
693
758
|
* @param {(
|
|
694
|
-
* this: XPathTransformerContext,
|
|
759
|
+
* this: XPathTransformerContext,
|
|
760
|
+
* key: unknown,
|
|
761
|
+
* items: Node[],
|
|
762
|
+
* ctx: XPathTransformerContext
|
|
695
763
|
* ) => void} cb - Callback receives (groupingKey, groupItems, context)
|
|
696
764
|
* @returns {this}
|
|
697
765
|
*/
|
|
@@ -919,25 +987,77 @@ class XPathTransformerContext {
|
|
|
919
987
|
|
|
920
988
|
/**
|
|
921
989
|
* Returns the current grouping key (for use within forEachGroup callback).
|
|
922
|
-
* @returns {
|
|
990
|
+
* @returns {unknown}
|
|
923
991
|
*/
|
|
924
992
|
currentGroupingKey () {
|
|
925
993
|
return /** @type {any} */ (this)._currentGroupingKey;
|
|
926
994
|
}
|
|
927
995
|
|
|
996
|
+
/**
|
|
997
|
+
* Directly query IndexedDB from within a template, e.g.
|
|
998
|
+
* `await this.indexedDB('myDB', 'myStore', {index: 'byAge'})`.
|
|
999
|
+
*
|
|
1000
|
+
* Since IndexedDB access is asynchronous, this is unavailable when JTLT is
|
|
1001
|
+
* configured with `sync: true`.
|
|
1002
|
+
* @param {string} dbName - Database name
|
|
1003
|
+
* @param {string} storeName - Object store name
|
|
1004
|
+
* @param {import('./indexedDB.js').QueryOptions} [options] - Query options
|
|
1005
|
+
* @returns {Promise<unknown[]>} The matching records
|
|
1006
|
+
*/
|
|
1007
|
+
indexedDB (dbName, storeName, options) {
|
|
1008
|
+
if (/** @type {any} */ (this._config).sync) {
|
|
1009
|
+
throw new Error(
|
|
1010
|
+
'The `indexedDB()` API is unavailable when JTLT is configured with ' +
|
|
1011
|
+
'`sync: true`.'
|
|
1012
|
+
);
|
|
1013
|
+
}
|
|
1014
|
+
return queryIndexedDB(dbName, storeName, options);
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
/**
|
|
1018
|
+
* Evaluate an XPath selector that calls the `jtlt:indexedDB(...)` function,
|
|
1019
|
+
* awaiting the underlying IndexedDB reads, then append the string result.
|
|
1020
|
+
* Used by {@link valueOf}. Callers reject `config.sync`.
|
|
1021
|
+
* @param {string} selectStr
|
|
1022
|
+
* @returns {Promise<XPathTransformerContext>}
|
|
1023
|
+
*/
|
|
1024
|
+
async _appendIndexedDBValue (selectStr) {
|
|
1025
|
+
const value = await evaluateXPathWithIndexedDB(
|
|
1026
|
+
selectStr, this._contextNode
|
|
1027
|
+
);
|
|
1028
|
+
this._getJoiningTransformer().text(value);
|
|
1029
|
+
return this;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
928
1032
|
/**
|
|
929
1033
|
* Append the value from an XPath expression or the context node text.
|
|
930
1034
|
* @param {string|object} [select]
|
|
931
1035
|
* @returns {XPathTransformerContext}
|
|
932
1036
|
*/
|
|
933
1037
|
valueOf (select) {
|
|
934
|
-
const jt = this._getJoiningTransformer();
|
|
935
|
-
let val;
|
|
936
|
-
|
|
937
1038
|
const selectStr = typeof select === 'object'
|
|
938
1039
|
? /** @type {{select?: string}} */ (select).select
|
|
939
1040
|
: select;
|
|
940
1041
|
|
|
1042
|
+
// `jtlt:indexedDB(...)` is a registered XPath function backed by async
|
|
1043
|
+
// IndexedDB reads. When it appears in the selector, evaluate the whole
|
|
1044
|
+
// expression asynchronously (fontoxpath itself stays synchronous) and
|
|
1045
|
+
// return a Promise so templates can `await this.valueOf(...)`.
|
|
1046
|
+
if (xpathExpressionUsesIndexedDB(selectStr)) {
|
|
1047
|
+
if (/** @type {any} */ (this._config).sync) {
|
|
1048
|
+
throw new Error(
|
|
1049
|
+
'The `indexedDB()` XPath function is unavailable when JTLT is ' +
|
|
1050
|
+
'configured with `sync: true`.'
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
return /** @type {any} */ (
|
|
1054
|
+
this._appendIndexedDBValue(/** @type {string} */ (selectStr))
|
|
1055
|
+
);
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
const jt = this._getJoiningTransformer();
|
|
1059
|
+
let val;
|
|
1060
|
+
|
|
941
1061
|
// Check if select is a custom function call: prefix:name(...)
|
|
942
1062
|
// Try to parse and invoke registered function
|
|
943
1063
|
if (selectStr && (/^[^:]+:[^\(]+\(/v).test(selectStr)) {
|
|
@@ -1039,7 +1159,7 @@ class XPathTransformerContext {
|
|
|
1039
1159
|
const resultToAppend = Array.isArray(result)
|
|
1040
1160
|
? result.join(' ')
|
|
1041
1161
|
: result;
|
|
1042
|
-
jt.append(resultToAppend);
|
|
1162
|
+
jt.append(/** @type {string | Node} */ (resultToAppend));
|
|
1043
1163
|
return this;
|
|
1044
1164
|
}
|
|
1045
1165
|
/* c8 ignore start -- Error handler for malformed function calls */
|
|
@@ -2002,8 +2122,8 @@ class XPathTransformerContext {
|
|
|
2002
2122
|
/**
|
|
2003
2123
|
* Invoke a registered stylesheet function with positional arguments.
|
|
2004
2124
|
* @param {string} name - Function name (with namespace)
|
|
2005
|
-
* @param {
|
|
2006
|
-
* @returns {
|
|
2125
|
+
* @param {unknown[]} args - Positional arguments
|
|
2126
|
+
* @returns {unknown} Function return value
|
|
2007
2127
|
*/
|
|
2008
2128
|
invokeFunctionByArity (name, args = []) {
|
|
2009
2129
|
return this._getJoiningTransformer().invokeFunctionByArity(name, args);
|
package/src/index.js
CHANGED
|
@@ -51,7 +51,7 @@ export const setWindow = (win) => {
|
|
|
51
51
|
* @typedef {(this: TCtx,
|
|
52
52
|
* value: ResultType<U>,
|
|
53
53
|
* cfg?: {mode?: string}
|
|
54
|
-
* ) => ResultType<T>|void} TemplateFunction
|
|
54
|
+
* ) => ResultType<T>|void|Promise<ResultType<T>|void>} TemplateFunction
|
|
55
55
|
*/
|
|
56
56
|
|
|
57
57
|
/**
|
|
@@ -80,11 +80,71 @@ export const setWindow = (win) => {
|
|
|
80
80
|
*/
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
|
+
* The output-sink surface a custom `joiningTransformer` may provide. Only
|
|
84
|
+
* `append` and `get` are required; the rest are optional because the engine
|
|
85
|
+
* guards each call. The built-in joiners' `append`/`string`/… signatures
|
|
86
|
+
* diverge (e.g. the DOM joiner also accepts `Node`), so under
|
|
87
|
+
* `strictFunctionTypes` no single structural type is a supertype of all
|
|
88
|
+
* three; this contract lists the surface with `unknown` parameters, and
|
|
89
|
+
* {@link JoiningTransformer} unions it with the concrete classes so real
|
|
90
|
+
* joiners still type precisely.
|
|
91
|
+
* @typedef {object} JoiningTransformerContract
|
|
92
|
+
* @property {(item: unknown) => unknown} append
|
|
93
|
+
* @property {() => unknown} get
|
|
94
|
+
* @property {(txt: string) => unknown} [text]
|
|
95
|
+
* @property {(str: unknown, cb?: () => void) => unknown} [string]
|
|
96
|
+
* @property {(num: unknown) => unknown} [number]
|
|
97
|
+
* @property {(
|
|
98
|
+
* obj: unknown, cb?: unknown, usePropertySets?: unknown, propSets?: unknown
|
|
99
|
+
* ) => unknown} [object]
|
|
100
|
+
* @property {(arr: unknown, cb?: unknown) => unknown} [array]
|
|
101
|
+
* @property {(
|
|
102
|
+
* name: string, atts?: unknown, children?: unknown,
|
|
103
|
+
* cb?: unknown, useAttributeSets?: unknown
|
|
104
|
+
* ) => unknown} [element]
|
|
105
|
+
* @property {(
|
|
106
|
+
* name: string, val: unknown, avoidAttEscape?: unknown
|
|
107
|
+
* ) => unknown} [attribute]
|
|
108
|
+
* @property {(text: string) => unknown} [comment]
|
|
109
|
+
* @property {(
|
|
110
|
+
* target: string, data: string
|
|
111
|
+
* ) => unknown} [processingInstruction]
|
|
112
|
+
* @property {(str: unknown) => unknown} [plainText]
|
|
113
|
+
* @property {(prop: unknown, val: unknown) => unknown} [propValue]
|
|
114
|
+
* @property {(item: unknown) => unknown} [rawAppend]
|
|
115
|
+
* @property {(prefix: string, namespaceURI: string) => unknown} [namespace]
|
|
116
|
+
* @property {(context: unknown) => unknown} [setContext]
|
|
117
|
+
* @property {(cfg: unknown) => unknown} [output]
|
|
118
|
+
* @property {(cfg: unknown) => unknown} [mode]
|
|
119
|
+
* @property {(cfg: unknown) => unknown} [stylesheet]
|
|
120
|
+
* @property {(cfg: unknown) => unknown} [function]
|
|
121
|
+
* @property {(
|
|
122
|
+
* name: string, args?: unknown[]
|
|
123
|
+
* ) => unknown} [invokeFunctionByArity]
|
|
124
|
+
* @property {(
|
|
125
|
+
* name: string, outputCharacters: unknown
|
|
126
|
+
* ) => unknown} [characterMap]
|
|
127
|
+
* @property {(name: string, attributes: unknown) => unknown} [attributeSet]
|
|
128
|
+
* @property {(
|
|
129
|
+
* stylesheetPrefix: string, resultPrefix: string
|
|
130
|
+
* ) => unknown} [namespaceAlias]
|
|
131
|
+
*/
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* One of the three built-in joiners. Used where engine internals rely on
|
|
135
|
+
* concrete members (e.g. `_modeConfig`).
|
|
83
136
|
* @typedef {(
|
|
84
137
|
* StringJoiningTransformer|
|
|
85
138
|
* DOMJoiningTransformer|
|
|
86
139
|
* JSONJoiningTransformer
|
|
87
|
-
* )}
|
|
140
|
+
* )} BuiltinJoiningTransformer
|
|
141
|
+
*/
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* The type accepted for a config `joiningTransformer`: a built-in joiner or
|
|
145
|
+
* any object implementing {@link JoiningTransformerContract}.
|
|
146
|
+
* @typedef {BuiltinJoiningTransformer | JoiningTransformerContract
|
|
147
|
+
* } JoiningTransformer
|
|
88
148
|
*/
|
|
89
149
|
|
|
90
150
|
/**
|
|
@@ -95,7 +155,7 @@ export const setWindow = (win) => {
|
|
|
95
155
|
* @template T
|
|
96
156
|
* @template {boolean|undefined} [E=false]
|
|
97
157
|
* @typedef {T extends "json" ?
|
|
98
|
-
* (E extends true ?
|
|
158
|
+
* (E extends true ? unknown[] : unknown) :
|
|
99
159
|
* T extends "string" ?
|
|
100
160
|
* (E extends true ? string[] : string) :
|
|
101
161
|
* (E extends true ? XMLDocument[] :
|
|
@@ -107,9 +167,12 @@ export const setWindow = (win) => {
|
|
|
107
167
|
* @template T
|
|
108
168
|
* @template {boolean|undefined} [E=false]
|
|
109
169
|
* @typedef {object} BaseJTLTOptions
|
|
170
|
+
* @property {boolean} [sync] Off by default: the engine awaits any Promise a
|
|
171
|
+
* template returns (e.g. from `await this.indexedDB(...)`). Set `true` to
|
|
172
|
+
* forbid asynchrony — a template that returns a Promise then throws.
|
|
110
173
|
* @property {(
|
|
111
174
|
* result: ResultType<T, E>
|
|
112
|
-
* ) => ResultType<T, E>|void} success A callback supplied
|
|
175
|
+
* ) => ResultType<T, E>|void} [success] A callback supplied
|
|
113
176
|
* with a single argument that is the result of this instance's
|
|
114
177
|
* transform() method. When used in TypeScript, this can be made
|
|
115
178
|
* generic as `success<T>(result: T): void`.
|
|
@@ -199,7 +262,7 @@ export const setWindow = (win) => {
|
|
|
199
262
|
*/
|
|
200
263
|
|
|
201
264
|
/**
|
|
202
|
-
* @template {boolean|undefined} [E=
|
|
265
|
+
* @template {boolean|undefined} [E=boolean|undefined]
|
|
203
266
|
* @typedef {JSONPathJTLTOptions<"json", E> |
|
|
204
267
|
* JSONPathJTLTOptions<"string", E> |
|
|
205
268
|
* JSONPathJTLTOptions<"dom", E> |
|
|
@@ -556,7 +619,8 @@ class JTLT {
|
|
|
556
619
|
}
|
|
557
620
|
throw new Error('You must wait until the ajax file is retrieved');
|
|
558
621
|
}
|
|
559
|
-
|
|
622
|
+
const {success} = this.config;
|
|
623
|
+
if (typeof success !== 'function') {
|
|
560
624
|
throw new TypeError("You must supply a 'success' callback");
|
|
561
625
|
}
|
|
562
626
|
|
|
@@ -585,7 +649,20 @@ class JTLT {
|
|
|
585
649
|
);
|
|
586
650
|
// The engine returns ResultType<T>. We cast through never to bypass
|
|
587
651
|
// the impossible intersection type that TypeScript infers for the union.
|
|
588
|
-
|
|
652
|
+
// The engine returns a Promise instead when a template ran asynchronously
|
|
653
|
+
// (e.g. it performed an `await this.indexedDB(...)` fetch); under
|
|
654
|
+
// `config.sync` the engine throws rather than returning one.
|
|
655
|
+
const maybePromise = /** @type {{then?: unknown}} */ (result);
|
|
656
|
+
if (maybePromise && typeof maybePromise.then === 'function') {
|
|
657
|
+
return /** @type {any} */ (
|
|
658
|
+
// eslint-disable-next-line @stylistic/max-len -- Long
|
|
659
|
+
// eslint-disable-next-line promise/prefer-await-to-then -- intentional dynamic sync/async
|
|
660
|
+
/** @type {Promise<never>} */ (result).then((res) => {
|
|
661
|
+
return /** @type {any} */ (success(res));
|
|
662
|
+
})
|
|
663
|
+
);
|
|
664
|
+
}
|
|
665
|
+
const ret = success(
|
|
589
666
|
/** @type {never} */ (result)
|
|
590
667
|
);
|
|
591
668
|
return /** @type {any} */ (ret);
|
|
@@ -657,7 +734,7 @@ class JTLT {
|
|
|
657
734
|
*/
|
|
658
735
|
/**
|
|
659
736
|
* @param {Omit<JTLTOptions, "success">} cfg Options
|
|
660
|
-
* @returns {Promise<
|
|
737
|
+
* @returns {Promise<unknown>}
|
|
661
738
|
*/
|
|
662
739
|
export function jtlt (cfg) {
|
|
663
740
|
// eslint-disable-next-line promise/avoid-new -- Own API
|