@jxsuite/runtime 0.19.0 → 0.20.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/runtime.js +20 -17
- package/dist/runtime.js.map +6 -6
- package/package.json +2 -2
- package/src/runtime.js +152 -97
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "JSON-native reactive web component runtime",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"upgrade": "bunx npm-check-updates -u && bun install"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@vue/reactivity": "^3.5.
|
|
28
|
+
"@vue/reactivity": "^3.5.35"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@happy-dom/global-registrator": "^20.9.0"
|
package/src/runtime.js
CHANGED
|
@@ -25,7 +25,7 @@ import { reactive, ref, computed, effect, isRef, onEffectCleanup } from "@vue/re
|
|
|
25
25
|
*
|
|
26
26
|
* @param {string | Record<string, any>} source - Path to .json file, URL, or raw document object
|
|
27
27
|
* @param {HTMLElement} [target] Default is `document.body`
|
|
28
|
-
* @param {
|
|
28
|
+
* @param {JxRenderOptions} [options]
|
|
29
29
|
* @returns {Promise<Record<string, any>>} Resolves with the live component scope (state reactive
|
|
30
30
|
* proxy)
|
|
31
31
|
*/
|
|
@@ -232,7 +232,7 @@ export { hasSchemaKeywords };
|
|
|
232
232
|
*
|
|
233
233
|
* @param {string} str
|
|
234
234
|
* @param {Record<string, any>} state
|
|
235
|
-
* @returns {
|
|
235
|
+
* @returns {string}
|
|
236
236
|
*/
|
|
237
237
|
function evaluateTemplate(str, state) {
|
|
238
238
|
const fn = new Function("state", "$map", `return \`${str}\``);
|
|
@@ -254,7 +254,7 @@ const _moduleCache = new Map();
|
|
|
254
254
|
* @param {Record<string, any>} state - Reactive scope proxy
|
|
255
255
|
* @param {string} key - Def key name
|
|
256
256
|
* @param {string} [base] - Base URL for resolving $src imports
|
|
257
|
-
* @returns {Promise<
|
|
257
|
+
* @returns {Promise<unknown>}
|
|
258
258
|
*/
|
|
259
259
|
async function resolveFunction(def, state, key, base) {
|
|
260
260
|
if (def.body && def.$src) {
|
|
@@ -385,7 +385,7 @@ export const RESERVED_KEYS = new Set([
|
|
|
385
385
|
*
|
|
386
386
|
* @param {Record<string, any>} def
|
|
387
387
|
* @param {Record<string, any>} state - Reactive scope proxy (or child scope via Object.create)
|
|
388
|
-
* @param {
|
|
388
|
+
* @param {JxRenderOptions} [options]
|
|
389
389
|
* @returns {HTMLElement | Text}
|
|
390
390
|
*/
|
|
391
391
|
export function renderNode(def, state, options) {
|
|
@@ -448,7 +448,7 @@ export function renderNode(def, state, options) {
|
|
|
448
448
|
/**
|
|
449
449
|
* Check if a value is a template string (contains ${}).
|
|
450
450
|
*
|
|
451
|
-
* @param {
|
|
451
|
+
* @param {unknown} val
|
|
452
452
|
* @returns {boolean}
|
|
453
453
|
*/
|
|
454
454
|
function isTemplateString(val) {
|
|
@@ -492,19 +492,22 @@ function applyProperties(el, def, state) {
|
|
|
492
492
|
}
|
|
493
493
|
|
|
494
494
|
/**
|
|
495
|
-
* @param {
|
|
495
|
+
* @param {HTMLElement} el
|
|
496
496
|
* @param {string} key
|
|
497
|
-
* @param {
|
|
497
|
+
* @param {unknown} val
|
|
498
498
|
* @param {Record<string, any>} state
|
|
499
499
|
*/
|
|
500
500
|
function bindProperty(el, key, val, state) {
|
|
501
|
+
const target = /** @type {Record<string, unknown>} */ (/** @type {unknown} */ (el));
|
|
501
502
|
if (isRefObj(val)) {
|
|
502
503
|
if (key === "id") {
|
|
503
|
-
|
|
504
|
+
target[key] = /** @type {string} */ (
|
|
505
|
+
resolveRef(/** @type {{ $ref: string }} */ (val).$ref, state)
|
|
506
|
+
);
|
|
504
507
|
return;
|
|
505
508
|
}
|
|
506
509
|
effect(() => {
|
|
507
|
-
|
|
510
|
+
target[key] = resolveRef(/** @type {{ $ref: string }} */ (val).$ref, state);
|
|
508
511
|
});
|
|
509
512
|
return;
|
|
510
513
|
}
|
|
@@ -512,12 +515,12 @@ function bindProperty(el, key, val, state) {
|
|
|
512
515
|
// Universal ${} reactivity — template strings in element properties
|
|
513
516
|
if (isTemplateString(val)) {
|
|
514
517
|
effect(() => {
|
|
515
|
-
|
|
518
|
+
target[key] = evaluateTemplate(/** @type {string} */ (val), state);
|
|
516
519
|
});
|
|
517
520
|
return;
|
|
518
521
|
}
|
|
519
522
|
|
|
520
|
-
|
|
523
|
+
target[key] = val;
|
|
521
524
|
}
|
|
522
525
|
|
|
523
526
|
/**
|
|
@@ -548,9 +551,10 @@ export function applyStyle(el, styleDef, mediaQueries = {}, state = {}) {
|
|
|
548
551
|
else el.style.setProperty(prop, val);
|
|
549
552
|
} else if (isTemplateString(val))
|
|
550
553
|
effect(() => {
|
|
551
|
-
/** @type {
|
|
554
|
+
/** @type {Record<string, string>} */ (/** @type {unknown} */ (el.style))[prop] =
|
|
555
|
+
evaluateTemplate(val, state);
|
|
552
556
|
});
|
|
553
|
-
else /** @type {
|
|
557
|
+
else /** @type {Record<string, string>} */ (/** @type {unknown} */ (el.style))[prop] = val;
|
|
554
558
|
}
|
|
555
559
|
|
|
556
560
|
const hasNested = Object.keys(nested).length > 0;
|
|
@@ -647,7 +651,7 @@ function applyAttributes(el, attrs, state) {
|
|
|
647
651
|
/**
|
|
648
652
|
* @param {Record<string, any>} def
|
|
649
653
|
* @param {Record<string, any>} state
|
|
650
|
-
* @param {
|
|
654
|
+
* @param {JxRenderOptions} [options]
|
|
651
655
|
* @returns {HTMLElement}
|
|
652
656
|
*/
|
|
653
657
|
function renderMappedArray(def, state, options) {
|
|
@@ -672,11 +676,13 @@ function renderMappedArray(def, state, options) {
|
|
|
672
676
|
if (!Array.isArray(items)) return;
|
|
673
677
|
if (filterRef) {
|
|
674
678
|
const fn = resolveRef(filterRef.$ref, state);
|
|
675
|
-
if (typeof fn === "function")
|
|
679
|
+
if (typeof fn === "function")
|
|
680
|
+
items = items.filter(/** @type {(v: unknown) => boolean} */ (fn));
|
|
676
681
|
}
|
|
677
682
|
if (sortRef) {
|
|
678
683
|
const fn = resolveRef(sortRef.$ref, state);
|
|
679
|
-
if (typeof fn === "function")
|
|
684
|
+
if (typeof fn === "function")
|
|
685
|
+
items = [...items].sort(/** @type {(a: unknown, b: unknown) => number} */ (fn));
|
|
680
686
|
}
|
|
681
687
|
|
|
682
688
|
items.forEach((item, index) => {
|
|
@@ -699,7 +705,7 @@ function renderMappedArray(def, state, options) {
|
|
|
699
705
|
/**
|
|
700
706
|
* @param {Record<string, any>} def
|
|
701
707
|
* @param {Record<string, any>} state
|
|
702
|
-
* @param {
|
|
708
|
+
* @param {JxRenderOptions} [options]
|
|
703
709
|
* @returns {HTMLElement}
|
|
704
710
|
*/
|
|
705
711
|
function renderSwitch(def, state, options) {
|
|
@@ -715,7 +721,7 @@ function renderSwitch(def, state, options) {
|
|
|
715
721
|
|
|
716
722
|
effect(() => {
|
|
717
723
|
container.innerHTML = "";
|
|
718
|
-
const key = resolveRef(def.$switch.$ref, state);
|
|
724
|
+
const key = /** @type {string} */ (resolveRef(def.$switch.$ref, state));
|
|
719
725
|
const caseDef = def.cases?.[key];
|
|
720
726
|
if (!caseDef) return;
|
|
721
727
|
|
|
@@ -732,7 +738,7 @@ function renderSwitch(def, state, options) {
|
|
|
732
738
|
const childOpts = options ? { ...options, _path: [...path, "cases", key] } : undefined;
|
|
733
739
|
container.appendChild(renderNode(doc, childScope, childOpts));
|
|
734
740
|
})
|
|
735
|
-
.catch((/** @type {
|
|
741
|
+
.catch((/** @type {unknown} */ e) =>
|
|
736
742
|
console.error("Jx $switch: failed to load external case", caseDef.$ref, e),
|
|
737
743
|
);
|
|
738
744
|
return;
|
|
@@ -757,7 +763,7 @@ function renderSwitch(def, state, options) {
|
|
|
757
763
|
* @param {Record<string, any>} state - Reactive scope proxy
|
|
758
764
|
* @param {string} key - Def key (for diagnostics)
|
|
759
765
|
* @param {string} [base] - Base URL for resolving $src imports
|
|
760
|
-
* @returns {Promise<
|
|
766
|
+
* @returns {Promise<unknown>}
|
|
761
767
|
*/
|
|
762
768
|
export async function resolvePrototype(def, state, key, base) {
|
|
763
769
|
// ── External class via $src ─────────────────────────────────────────────────
|
|
@@ -767,10 +773,10 @@ export async function resolvePrototype(def, state, key, base) {
|
|
|
767
773
|
|
|
768
774
|
switch (def.$prototype) {
|
|
769
775
|
case "Request": {
|
|
770
|
-
/** @type {import("@vue/reactivity").Ref<
|
|
776
|
+
/** @type {import("@vue/reactivity").Ref<unknown>} */
|
|
771
777
|
const s = ref(null);
|
|
772
778
|
const debounceMs = def.debounce ?? 0;
|
|
773
|
-
/** @type {
|
|
779
|
+
/** @type {ReturnType<typeof setTimeout> | null} */
|
|
774
780
|
let debounceTimer = null;
|
|
775
781
|
|
|
776
782
|
if (!def.manual) {
|
|
@@ -786,7 +792,7 @@ export async function resolvePrototype(def, state, key, base) {
|
|
|
786
792
|
const controller = new AbortController();
|
|
787
793
|
onEffectCleanup(() => {
|
|
788
794
|
controller.abort();
|
|
789
|
-
clearTimeout(debounceTimer);
|
|
795
|
+
if (debounceTimer !== null) clearTimeout(debounceTimer);
|
|
790
796
|
});
|
|
791
797
|
|
|
792
798
|
const doFetch = () =>
|
|
@@ -802,8 +808,8 @@ export async function resolvePrototype(def, state, key, base) {
|
|
|
802
808
|
.then((d) => {
|
|
803
809
|
s.value = d;
|
|
804
810
|
})
|
|
805
|
-
.catch((/** @type {
|
|
806
|
-
if (e.name !== "AbortError") s.value = { error: String(e) };
|
|
811
|
+
.catch((/** @type {unknown} */ e) => {
|
|
812
|
+
if (/** @type {Error} */ (e).name !== "AbortError") s.value = { error: String(e) };
|
|
807
813
|
});
|
|
808
814
|
|
|
809
815
|
if (debounceMs > 0) {
|
|
@@ -902,22 +908,22 @@ export async function resolvePrototype(def, state, key, base) {
|
|
|
902
908
|
} = def;
|
|
903
909
|
const req = indexedDB.open(database, version);
|
|
904
910
|
req.onupgradeneeded = (e) => {
|
|
905
|
-
/** @type {
|
|
906
|
-
const db = /** @type {
|
|
911
|
+
/** @type {IDBDatabase} */
|
|
912
|
+
const db = /** @type {IDBOpenDBRequest} */ (e.target).result;
|
|
907
913
|
if (!db.objectStoreNames.contains(store)) {
|
|
908
914
|
const os = db.createObjectStore(store, { keyPath, autoIncrement });
|
|
909
915
|
for (const i of indexes) os.createIndex(i.name, i.keyPath, { unique: i.unique ?? false });
|
|
910
916
|
}
|
|
911
917
|
};
|
|
912
918
|
req.onsuccess = (e) => {
|
|
913
|
-
/** @type {
|
|
914
|
-
const db = /** @type {
|
|
919
|
+
/** @type {IDBDatabase} */
|
|
920
|
+
const db = /** @type {IDBOpenDBRequest} */ (e.target).result;
|
|
915
921
|
idbState.value = {
|
|
916
922
|
database,
|
|
917
923
|
store,
|
|
918
924
|
version,
|
|
919
925
|
isReady: true,
|
|
920
|
-
getStore: (/** @type {
|
|
926
|
+
getStore: (/** @type {IDBTransactionMode} */ mode = "readwrite") =>
|
|
921
927
|
Promise.resolve(db.transaction(store, mode).objectStore(store)),
|
|
922
928
|
};
|
|
923
929
|
};
|
|
@@ -976,7 +982,7 @@ const EXTERNAL_RESERVED = new Set([
|
|
|
976
982
|
* @param {Record<string, any>} state
|
|
977
983
|
* @param {string} key
|
|
978
984
|
* @param {string} [base]
|
|
979
|
-
* @returns {Promise<
|
|
985
|
+
* @returns {Promise<unknown>}
|
|
980
986
|
*/
|
|
981
987
|
async function resolveExternalPrototype(def, state, key, base) {
|
|
982
988
|
const src = def.$src;
|
|
@@ -1000,7 +1006,7 @@ async function resolveExternalPrototype(def, state, key, base) {
|
|
|
1000
1006
|
* @param {string} src - JS module URL to import
|
|
1001
1007
|
* @param {string} exportName - Export name to look up
|
|
1002
1008
|
* @param {string} [base] - Base URL for resolution
|
|
1003
|
-
* @returns {Promise<
|
|
1009
|
+
* @returns {Promise<unknown>}
|
|
1004
1010
|
*/
|
|
1005
1011
|
async function importAndInstantiate(def, src, exportName, base) {
|
|
1006
1012
|
let mod;
|
|
@@ -1049,7 +1055,7 @@ async function importAndInstantiate(def, src, exportName, base) {
|
|
|
1049
1055
|
/** @type {import("@vue/reactivity").Ref<any>} */
|
|
1050
1056
|
const s = ref(value);
|
|
1051
1057
|
if (typeof instance.subscribe === "function") {
|
|
1052
|
-
instance.subscribe((/** @type {
|
|
1058
|
+
instance.subscribe((/** @type {unknown} */ newVal) => {
|
|
1053
1059
|
s.value = newVal;
|
|
1054
1060
|
});
|
|
1055
1061
|
}
|
|
@@ -1064,7 +1070,7 @@ async function importAndInstantiate(def, src, exportName, base) {
|
|
|
1064
1070
|
* @param {Record<string, any>} state
|
|
1065
1071
|
* @param {string} key
|
|
1066
1072
|
* @param {string} [base]
|
|
1067
|
-
* @returns {Promise<
|
|
1073
|
+
* @returns {Promise<unknown>}
|
|
1068
1074
|
*/
|
|
1069
1075
|
async function resolveClassJson(def, state, key, base) {
|
|
1070
1076
|
const src = def.$src;
|
|
@@ -1116,7 +1122,7 @@ async function resolveClassJson(def, state, key, base) {
|
|
|
1116
1122
|
/** @type {import("@vue/reactivity").Ref<any>} */
|
|
1117
1123
|
const s = ref(value);
|
|
1118
1124
|
if (typeof instance.subscribe === "function") {
|
|
1119
|
-
instance.subscribe((/** @type {
|
|
1125
|
+
instance.subscribe((/** @type {unknown} */ newVal) => {
|
|
1120
1126
|
s.value = newVal;
|
|
1121
1127
|
});
|
|
1122
1128
|
}
|
|
@@ -1128,7 +1134,7 @@ async function resolveClassJson(def, state, key, base) {
|
|
|
1128
1134
|
* fields to _-prefixed public fields.
|
|
1129
1135
|
*
|
|
1130
1136
|
* @param {Record<string, any>} classDef
|
|
1131
|
-
* @returns {
|
|
1137
|
+
* @returns {DynamicClass}
|
|
1132
1138
|
*/
|
|
1133
1139
|
function classFromSchema(classDef) {
|
|
1134
1140
|
const fields = classDef.$defs?.fields ?? {};
|
|
@@ -1138,16 +1144,34 @@ function classFromSchema(classDef) {
|
|
|
1138
1144
|
class DynClass {
|
|
1139
1145
|
constructor(/** @type {Record<string, any>} */ config = {}) {
|
|
1140
1146
|
for (const [key, field] of Object.entries(fields)) {
|
|
1141
|
-
/**
|
|
1142
|
-
|
|
1147
|
+
/**
|
|
1148
|
+
* @type {{
|
|
1149
|
+
* identifier?: string;
|
|
1150
|
+
* access?: string;
|
|
1151
|
+
* initializer?: unknown;
|
|
1152
|
+
* default?: unknown;
|
|
1153
|
+
* }}
|
|
1154
|
+
*/
|
|
1155
|
+
const typedField =
|
|
1156
|
+
/**
|
|
1157
|
+
* @type {{
|
|
1158
|
+
* identifier?: string;
|
|
1159
|
+
* access?: string;
|
|
1160
|
+
* initializer?: unknown;
|
|
1161
|
+
* default?: unknown;
|
|
1162
|
+
* }}
|
|
1163
|
+
*/ (field);
|
|
1143
1164
|
const id = typedField.identifier ?? key;
|
|
1144
1165
|
const propName = typedField.access === "private" ? `_${id}` : id;
|
|
1145
|
-
if (config[id] !== undefined)
|
|
1166
|
+
if (config[id] !== undefined)
|
|
1167
|
+
/** @type {Record<string, unknown>} */ (this)[propName] = config[id];
|
|
1146
1168
|
else if (typedField.initializer !== undefined)
|
|
1147
|
-
/** @type {
|
|
1169
|
+
/** @type {Record<string, unknown>} */ (this)[propName] = typedField.initializer;
|
|
1148
1170
|
else if (typedField.default !== undefined)
|
|
1149
|
-
/** @type {
|
|
1150
|
-
|
|
1171
|
+
/** @type {Record<string, unknown>} */ (this)[propName] = structuredClone(
|
|
1172
|
+
typedField.default,
|
|
1173
|
+
);
|
|
1174
|
+
else /** @type {Record<string, unknown>} */ (this)[propName] = null;
|
|
1151
1175
|
}
|
|
1152
1176
|
if (ctor?.body) {
|
|
1153
1177
|
const bodyStr = Array.isArray(ctor.body) ? ctor.body.join("\n") : ctor.body;
|
|
@@ -1157,13 +1181,35 @@ function classFromSchema(classDef) {
|
|
|
1157
1181
|
}
|
|
1158
1182
|
|
|
1159
1183
|
for (const [key, method] of Object.entries(methods)) {
|
|
1160
|
-
/**
|
|
1161
|
-
|
|
1184
|
+
/**
|
|
1185
|
+
* @type {{
|
|
1186
|
+
* identifier?: string;
|
|
1187
|
+
* parameters?: Record<string, unknown>[];
|
|
1188
|
+
* body?: string | string[];
|
|
1189
|
+
* role?: string;
|
|
1190
|
+
* scope?: string;
|
|
1191
|
+
* getter?: { body: string };
|
|
1192
|
+
* setter?: { body: string; parameters?: Record<string, unknown>[] };
|
|
1193
|
+
* }}
|
|
1194
|
+
*/
|
|
1195
|
+
const typedMethod = /**
|
|
1196
|
+
* @type {{
|
|
1197
|
+
* identifier?: string;
|
|
1198
|
+
* parameters?: Record<string, unknown>[];
|
|
1199
|
+
* body?: string | string[];
|
|
1200
|
+
* role?: string;
|
|
1201
|
+
* scope?: string;
|
|
1202
|
+
* getter?: { body: string };
|
|
1203
|
+
* setter?: { body: string; parameters?: Record<string, unknown>[] };
|
|
1204
|
+
* }}
|
|
1205
|
+
*/ (method);
|
|
1162
1206
|
const name = typedMethod.identifier ?? key;
|
|
1163
|
-
const params =
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1207
|
+
const params = /** @type {string[]} */ (
|
|
1208
|
+
(typedMethod.parameters ?? []).map((/** @type {Record<string, unknown>} */ p) => {
|
|
1209
|
+
if (p.$ref) return /** @type {string} */ (/** @type {string} */ (p.$ref).split("/").pop());
|
|
1210
|
+
return /** @type {string} */ (p.identifier ?? p.name ?? "arg");
|
|
1211
|
+
})
|
|
1212
|
+
);
|
|
1167
1213
|
const bodyStr = Array.isArray(typedMethod.body)
|
|
1168
1214
|
? typedMethod.body.join("\n")
|
|
1169
1215
|
: (typedMethod.body ?? "");
|
|
@@ -1172,23 +1218,27 @@ function classFromSchema(classDef) {
|
|
|
1172
1218
|
/** @type {PropertyDescriptor} */
|
|
1173
1219
|
const descriptor = {};
|
|
1174
1220
|
if (typedMethod.getter)
|
|
1175
|
-
descriptor.get = /** @type {
|
|
1221
|
+
descriptor.get = /** @type {() => unknown} */ (new Function(typedMethod.getter.body));
|
|
1176
1222
|
if (typedMethod.setter) {
|
|
1177
1223
|
const sp = (typedMethod.setter.parameters ?? []).map(
|
|
1178
|
-
(/** @type {
|
|
1224
|
+
(/** @type {Record<string, unknown>} */ p) =>
|
|
1225
|
+
/** @type {string} */ (p.$ref)?.split("/").pop() ?? "v",
|
|
1226
|
+
);
|
|
1227
|
+
descriptor.set = /** @type {(v: unknown) => void} */ (
|
|
1228
|
+
new Function(...sp, typedMethod.setter.body)
|
|
1179
1229
|
);
|
|
1180
|
-
descriptor.set = /** @type {any} */ (new Function(...sp, typedMethod.setter.body));
|
|
1181
1230
|
}
|
|
1182
1231
|
Object.defineProperty(DynClass.prototype, name, { ...descriptor, configurable: true });
|
|
1183
1232
|
} else if (typedMethod.scope === "static") {
|
|
1184
|
-
/** @type {
|
|
1233
|
+
/** @type {DynamicClass} */ (DynClass)[name] = new Function(...params, bodyStr);
|
|
1185
1234
|
} else {
|
|
1186
|
-
/** @type {
|
|
1235
|
+
/** @type {DynamicClass} */ (DynClass).prototype[name] = new Function(...params, bodyStr);
|
|
1187
1236
|
}
|
|
1188
1237
|
}
|
|
1189
1238
|
|
|
1190
1239
|
Object.defineProperty(DynClass, "name", { value: classDef.title, configurable: true });
|
|
1191
|
-
|
|
1240
|
+
const dynCtor = /** @type {DynamicClass} */ (/** @type {unknown} */ (DynClass));
|
|
1241
|
+
return dynCtor;
|
|
1192
1242
|
}
|
|
1193
1243
|
|
|
1194
1244
|
/**
|
|
@@ -1200,7 +1250,7 @@ function classFromSchema(classDef) {
|
|
|
1200
1250
|
* @param {Record<string, any>} state
|
|
1201
1251
|
* @param {string} key
|
|
1202
1252
|
* @param {string} [base]
|
|
1203
|
-
* @returns {Promise<
|
|
1253
|
+
* @returns {Promise<unknown>}
|
|
1204
1254
|
*/
|
|
1205
1255
|
async function resolveViaDevProxy(def, state, key, base) {
|
|
1206
1256
|
/** @type {Record<string, any>} */
|
|
@@ -1209,7 +1259,9 @@ async function resolveViaDevProxy(def, state, key, base) {
|
|
|
1209
1259
|
if (!EXTERNAL_RESERVED.has(k)) config[k] = v;
|
|
1210
1260
|
}
|
|
1211
1261
|
|
|
1212
|
-
const hasTemplates = Object.values(config).some((/** @type {
|
|
1262
|
+
const hasTemplates = Object.values(config).some((/** @type {unknown} */ v) =>
|
|
1263
|
+
isTemplateString(v),
|
|
1264
|
+
);
|
|
1213
1265
|
|
|
1214
1266
|
/** @param {Record<string, any>} resolvedConfig */
|
|
1215
1267
|
const doResolve = (resolvedConfig) =>
|
|
@@ -1239,17 +1291,17 @@ async function resolveViaDevProxy(def, state, key, base) {
|
|
|
1239
1291
|
resolvedConfig[k] = isTemplateString(v) ? evaluateTemplate(v, state) : v;
|
|
1240
1292
|
}
|
|
1241
1293
|
doResolve(resolvedConfig)
|
|
1242
|
-
.then((/** @type {
|
|
1294
|
+
.then((/** @type {unknown} */ value) => {
|
|
1243
1295
|
s.value = value;
|
|
1244
1296
|
})
|
|
1245
|
-
.catch((/** @type {
|
|
1297
|
+
.catch((/** @type {unknown} */ e) => console.error("Jx dev proxy:", e));
|
|
1246
1298
|
});
|
|
1247
1299
|
} else {
|
|
1248
1300
|
doResolve(config)
|
|
1249
|
-
.then((/** @type {
|
|
1301
|
+
.then((/** @type {unknown} */ value) => {
|
|
1250
1302
|
s.value = value;
|
|
1251
1303
|
})
|
|
1252
|
-
.catch((/** @type {
|
|
1304
|
+
.catch((/** @type {unknown} */ e) => console.error("Jx dev proxy:", e));
|
|
1253
1305
|
}
|
|
1254
1306
|
return s;
|
|
1255
1307
|
}
|
|
@@ -1264,7 +1316,7 @@ async function resolveViaDevProxy(def, state, key, base) {
|
|
|
1264
1316
|
* @param {Record<string, any>} state
|
|
1265
1317
|
* @param {string} key
|
|
1266
1318
|
* @param {string} [base]
|
|
1267
|
-
* @returns {Promise<
|
|
1319
|
+
* @returns {Promise<unknown>}
|
|
1268
1320
|
*/
|
|
1269
1321
|
async function resolveServerFunction(def, state, key, base) {
|
|
1270
1322
|
const src = def.$src;
|
|
@@ -1298,12 +1350,12 @@ async function resolveServerFunction(def, state, key, base) {
|
|
|
1298
1350
|
throw new Error(`Jx: "${exportName}" from "${src}" is not a function`);
|
|
1299
1351
|
|
|
1300
1352
|
const rawArgs = def.arguments ?? {};
|
|
1301
|
-
const hasReactiveArg = Object.values(rawArgs).some((/** @type {
|
|
1353
|
+
const hasReactiveArg = Object.values(rawArgs).some((/** @type {unknown} */ v) => isRefObj(v));
|
|
1302
1354
|
const resolveArgs = () => {
|
|
1303
1355
|
/** @type {Record<string, any>} */
|
|
1304
1356
|
const args = {};
|
|
1305
1357
|
for (const [k, v] of Object.entries(rawArgs)) {
|
|
1306
|
-
args[k] = isRefObj(v) ? resolveRef(/** @type {
|
|
1358
|
+
args[k] = isRefObj(v) ? resolveRef(/** @type {{ $ref: string }} */ (v).$ref, state) : v;
|
|
1307
1359
|
}
|
|
1308
1360
|
return args;
|
|
1309
1361
|
};
|
|
@@ -1316,7 +1368,7 @@ async function resolveServerFunction(def, state, key, base) {
|
|
|
1316
1368
|
const args = resolveArgs();
|
|
1317
1369
|
onEffectCleanup(() => {});
|
|
1318
1370
|
fn(args)
|
|
1319
|
-
.then((/** @type {
|
|
1371
|
+
.then((/** @type {unknown} */ result) => {
|
|
1320
1372
|
s.value = result;
|
|
1321
1373
|
})
|
|
1322
1374
|
.catch(() => {});
|
|
@@ -1336,17 +1388,17 @@ async function resolveServerFunction(def, state, key, base) {
|
|
|
1336
1388
|
* @param {Record<string, any>} state
|
|
1337
1389
|
* @param {string} key
|
|
1338
1390
|
* @param {string} [base]
|
|
1339
|
-
* @returns {Promise<
|
|
1391
|
+
* @returns {Promise<unknown>}
|
|
1340
1392
|
*/
|
|
1341
1393
|
async function resolveServerFunctionViaProxy(def, state, key, base) {
|
|
1342
1394
|
const rawArgs = def.arguments ?? {};
|
|
1343
|
-
const hasReactiveArg = Object.values(rawArgs).some((/** @type {
|
|
1395
|
+
const hasReactiveArg = Object.values(rawArgs).some((/** @type {unknown} */ v) => isRefObj(v));
|
|
1344
1396
|
|
|
1345
1397
|
const resolveArgs = () => {
|
|
1346
1398
|
/** @type {Record<string, any>} */
|
|
1347
1399
|
const args = {};
|
|
1348
1400
|
for (const [k, v] of Object.entries(rawArgs)) {
|
|
1349
|
-
args[k] = isRefObj(v) ? resolveRef(/** @type {
|
|
1401
|
+
args[k] = isRefObj(v) ? resolveRef(/** @type {{ $ref: string }} */ (v).$ref, state) : v;
|
|
1350
1402
|
}
|
|
1351
1403
|
return args;
|
|
1352
1404
|
};
|
|
@@ -1375,17 +1427,17 @@ async function resolveServerFunctionViaProxy(def, state, key, base) {
|
|
|
1375
1427
|
const args = resolveArgs();
|
|
1376
1428
|
onEffectCleanup(() => {});
|
|
1377
1429
|
doResolve(args)
|
|
1378
|
-
.then((/** @type {
|
|
1430
|
+
.then((/** @type {unknown} */ result) => {
|
|
1379
1431
|
s.value = result;
|
|
1380
1432
|
})
|
|
1381
|
-
.catch((/** @type {
|
|
1433
|
+
.catch((/** @type {unknown} */ e) => console.error("Jx server proxy:", e));
|
|
1382
1434
|
});
|
|
1383
1435
|
} else {
|
|
1384
1436
|
doResolve(resolveArgs())
|
|
1385
|
-
.then((/** @type {
|
|
1437
|
+
.then((/** @type {unknown} */ result) => {
|
|
1386
1438
|
s.value = result;
|
|
1387
1439
|
})
|
|
1388
|
-
.catch((/** @type {
|
|
1440
|
+
.catch((/** @type {unknown} */ e) => console.error("Jx server proxy:", e));
|
|
1389
1441
|
}
|
|
1390
1442
|
return s;
|
|
1391
1443
|
}
|
|
@@ -1398,7 +1450,7 @@ async function resolveServerFunctionViaProxy(def, state, key, base) {
|
|
|
1398
1450
|
*
|
|
1399
1451
|
* @param {string} ref
|
|
1400
1452
|
* @param {Record<string, any>} state - Reactive scope proxy (or child scope)
|
|
1401
|
-
* @returns {
|
|
1453
|
+
* @returns {unknown}
|
|
1402
1454
|
*/
|
|
1403
1455
|
export function resolveRef(ref, state) {
|
|
1404
1456
|
if (typeof ref !== "string") return ref;
|
|
@@ -1426,7 +1478,7 @@ export function resolveRef(ref, state) {
|
|
|
1426
1478
|
/**
|
|
1427
1479
|
* Check if v is a Vue ref (including computed).
|
|
1428
1480
|
*
|
|
1429
|
-
* @param {
|
|
1481
|
+
* @param {unknown} v
|
|
1430
1482
|
* @returns {boolean}
|
|
1431
1483
|
*/
|
|
1432
1484
|
export function isSignal(v) {
|
|
@@ -1434,11 +1486,15 @@ export function isSignal(v) {
|
|
|
1434
1486
|
}
|
|
1435
1487
|
|
|
1436
1488
|
/**
|
|
1437
|
-
* @param {
|
|
1489
|
+
* @param {unknown} v
|
|
1438
1490
|
* @returns {boolean}
|
|
1439
1491
|
*/
|
|
1440
1492
|
function isRefObj(v) {
|
|
1441
|
-
return
|
|
1493
|
+
return (
|
|
1494
|
+
v !== null &&
|
|
1495
|
+
typeof v === "object" &&
|
|
1496
|
+
typeof (/** @type {Record<string, unknown>} */ (v).$ref) === "string"
|
|
1497
|
+
);
|
|
1442
1498
|
}
|
|
1443
1499
|
|
|
1444
1500
|
/**
|
|
@@ -1450,12 +1506,12 @@ function isNestedSelector(k) {
|
|
|
1450
1506
|
}
|
|
1451
1507
|
|
|
1452
1508
|
/**
|
|
1453
|
-
* @param {
|
|
1509
|
+
* @param {unknown} obj
|
|
1454
1510
|
* @param {string} path
|
|
1455
|
-
* @returns {
|
|
1511
|
+
* @returns {unknown}
|
|
1456
1512
|
*/
|
|
1457
1513
|
function getPath(obj, path) {
|
|
1458
|
-
return path.split(/[./]/).reduce((o, k) => o?.[k], obj);
|
|
1514
|
+
return path.split(/[./]/).reduce((o, k) => /** @type {Record<string, unknown>} */ (o)?.[k], obj);
|
|
1459
1515
|
}
|
|
1460
1516
|
|
|
1461
1517
|
/**
|
|
@@ -1484,7 +1540,7 @@ export function camelToKebab(s) {
|
|
|
1484
1540
|
/**
|
|
1485
1541
|
* Convert a style rules object to a CSS text string (skipping nested selectors).
|
|
1486
1542
|
*
|
|
1487
|
-
* @param {Record<string,
|
|
1543
|
+
* @param {Record<string, unknown> | object} rules
|
|
1488
1544
|
* @returns {string}
|
|
1489
1545
|
*/
|
|
1490
1546
|
export function toCSSText(rules) {
|
|
@@ -1630,8 +1686,8 @@ export async function defineElement(source, base) {
|
|
|
1630
1686
|
|
|
1631
1687
|
// Merge $props set as JS properties by parent before connection
|
|
1632
1688
|
for (const key of Object.keys(def.state ?? {})) {
|
|
1633
|
-
if (key in this && /** @type {
|
|
1634
|
-
state[key] = /** @type {
|
|
1689
|
+
if (key in this && /** @type {Record<string, unknown>} */ (this)[key] !== undefined) {
|
|
1690
|
+
state[key] = /** @type {Record<string, unknown>} */ (this)[key];
|
|
1635
1691
|
}
|
|
1636
1692
|
}
|
|
1637
1693
|
// Set up property getters/setters that forward into reactive state
|
|
@@ -1639,7 +1695,7 @@ export async function defineElement(source, base) {
|
|
|
1639
1695
|
if (!(key in HTMLElement.prototype)) {
|
|
1640
1696
|
Object.defineProperty(this, key, {
|
|
1641
1697
|
get: () => state[key],
|
|
1642
|
-
set: (/** @type {
|
|
1698
|
+
set: (/** @type {unknown} */ v) => {
|
|
1643
1699
|
state[key] = v;
|
|
1644
1700
|
},
|
|
1645
1701
|
configurable: true,
|
|
@@ -1647,7 +1703,7 @@ export async function defineElement(source, base) {
|
|
|
1647
1703
|
}
|
|
1648
1704
|
}
|
|
1649
1705
|
|
|
1650
|
-
/** @type {
|
|
1706
|
+
/** @type {Record<string, unknown>} */ (this)._state = state;
|
|
1651
1707
|
|
|
1652
1708
|
// Capture light DOM children (for slot distribution) before rendering
|
|
1653
1709
|
const slottedChildren = Array.from(this.childNodes);
|
|
@@ -1677,16 +1733,16 @@ export async function defineElement(source, base) {
|
|
|
1677
1733
|
}
|
|
1678
1734
|
|
|
1679
1735
|
disconnectedCallback() {
|
|
1680
|
-
/** @type {any} */
|
|
1681
|
-
const self = this;
|
|
1736
|
+
/** @type {{ _state?: Record<string, any> }} */
|
|
1737
|
+
const self = /** @type {{ _state?: Record<string, any> }} */ (/** @type {unknown} */ (this));
|
|
1682
1738
|
if (typeof self._state?.onUnmount === "function") {
|
|
1683
1739
|
self._state.onUnmount(self._state);
|
|
1684
1740
|
}
|
|
1685
1741
|
}
|
|
1686
1742
|
|
|
1687
1743
|
adoptedCallback() {
|
|
1688
|
-
/** @type {any} */
|
|
1689
|
-
const self = this;
|
|
1744
|
+
/** @type {{ _state?: Record<string, any> }} */
|
|
1745
|
+
const self = /** @type {{ _state?: Record<string, any> }} */ (/** @type {unknown} */ (this));
|
|
1690
1746
|
if (typeof self._state?.onAdopted === "function") {
|
|
1691
1747
|
self._state.onAdopted(self._state);
|
|
1692
1748
|
}
|
|
@@ -1697,8 +1753,8 @@ export async function defineElement(source, base) {
|
|
|
1697
1753
|
/** @type {string | null} */ oldVal,
|
|
1698
1754
|
/** @type {string | null} */ newVal,
|
|
1699
1755
|
) {
|
|
1700
|
-
/** @type {any} */
|
|
1701
|
-
const self = this;
|
|
1756
|
+
/** @type {{ _state?: Record<string, any> }} */
|
|
1757
|
+
const self = /** @type {{ _state?: Record<string, any> }} */ (/** @type {unknown} */ (this));
|
|
1702
1758
|
if (!self._state || oldVal === newVal) return;
|
|
1703
1759
|
const camelKey = name.replace(
|
|
1704
1760
|
/-([a-z])/g,
|
|
@@ -1720,31 +1776,30 @@ export async function defineElement(source, base) {
|
|
|
1720
1776
|
*
|
|
1721
1777
|
* @param {Record<string, any>} def
|
|
1722
1778
|
* @param {Record<string, any>} state
|
|
1723
|
-
* @param {
|
|
1724
|
-
* @param {
|
|
1779
|
+
* @param {JxRenderOptions} [options]
|
|
1780
|
+
* @param {JxPath} [path]
|
|
1725
1781
|
* @returns {HTMLElement}
|
|
1726
1782
|
*/
|
|
1727
1783
|
function renderCustomElementWithProps(def, state, options, path) {
|
|
1728
|
-
/** @type {any} */
|
|
1729
1784
|
const el = document.createElement(def.tagName);
|
|
1730
1785
|
|
|
1731
|
-
if (options?.onNodeCreated) options.onNodeCreated(el, path, def);
|
|
1786
|
+
if (options?.onNodeCreated) options.onNodeCreated(el, path ?? [], def);
|
|
1732
1787
|
|
|
1733
1788
|
// Set JS properties from $props (before connection)
|
|
1734
1789
|
for (const [key, val] of Object.entries(def.$props ?? {})) {
|
|
1735
1790
|
if (isRefObj(val)) {
|
|
1736
1791
|
const resolved = resolveRef(val.$ref, state);
|
|
1737
|
-
el[key] = resolved;
|
|
1792
|
+
/** @type {Record<string, unknown>} */ (el)[key] = resolved;
|
|
1738
1793
|
// Reactive forwarding: re-set the property when the source changes
|
|
1739
1794
|
effect(() => {
|
|
1740
|
-
el[key] = resolveRef(val.$ref, state);
|
|
1795
|
+
/** @type {Record<string, unknown>} */ (el)[key] = resolveRef(val.$ref, state);
|
|
1741
1796
|
});
|
|
1742
1797
|
} else if (isTemplateString(val)) {
|
|
1743
1798
|
effect(() => {
|
|
1744
|
-
el[key] = evaluateTemplate(val, state);
|
|
1799
|
+
/** @type {Record<string, unknown>} */ (el)[key] = evaluateTemplate(val, state);
|
|
1745
1800
|
});
|
|
1746
1801
|
} else {
|
|
1747
|
-
el[key] = val;
|
|
1802
|
+
/** @type {Record<string, unknown>} */ (el)[key] = val;
|
|
1748
1803
|
}
|
|
1749
1804
|
}
|
|
1750
1805
|
|