@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jxsuite/runtime",
3
- "version": "0.19.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.34"
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 {any} [options]
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 {any}
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<any>}
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 {any} [options]
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 {any} val
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 {any} el
495
+ * @param {HTMLElement} el
496
496
  * @param {string} key
497
- * @param {any} val
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
- el[key] = resolveRef(val.$ref, state);
504
+ target[key] = /** @type {string} */ (
505
+ resolveRef(/** @type {{ $ref: string }} */ (val).$ref, state)
506
+ );
504
507
  return;
505
508
  }
506
509
  effect(() => {
507
- el[key] = resolveRef(val.$ref, state);
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
- el[key] = evaluateTemplate(val, state);
518
+ target[key] = evaluateTemplate(/** @type {string} */ (val), state);
516
519
  });
517
520
  return;
518
521
  }
519
522
 
520
- el[key] = val;
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 {any} */ (el.style)[prop] = evaluateTemplate(val, state);
554
+ /** @type {Record<string, string>} */ (/** @type {unknown} */ (el.style))[prop] =
555
+ evaluateTemplate(val, state);
552
556
  });
553
- else /** @type {any} */ (el.style)[prop] = val;
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 {any} [options]
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") items = items.filter(fn);
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") items = [...items].sort(fn);
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 {any} [options]
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 {any} */ e) =>
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<any>}
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<any>} */
776
+ /** @type {import("@vue/reactivity").Ref<unknown>} */
771
777
  const s = ref(null);
772
778
  const debounceMs = def.debounce ?? 0;
773
- /** @type {any} */
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 {any} */ e) => {
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 {any} */
906
- const db = /** @type {any} */ (e.target)?.result;
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 {any} */
914
- const db = /** @type {any} */ (e.target)?.result;
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 {string} */ mode = "readwrite") =>
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<any>}
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<any>}
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 {any} */ newVal) => {
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<any>}
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 {any} */ newVal) => {
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 {any}
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
- /** @type {any} */
1142
- const typedField = field;
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) /** @type {any} */ (this)[propName] = config[id];
1166
+ if (config[id] !== undefined)
1167
+ /** @type {Record<string, unknown>} */ (this)[propName] = config[id];
1146
1168
  else if (typedField.initializer !== undefined)
1147
- /** @type {any} */ (this)[propName] = typedField.initializer;
1169
+ /** @type {Record<string, unknown>} */ (this)[propName] = typedField.initializer;
1148
1170
  else if (typedField.default !== undefined)
1149
- /** @type {any} */ (this)[propName] = structuredClone(typedField.default);
1150
- else /** @type {any} */ (this)[propName] = null;
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
- /** @type {any} */
1161
- const typedMethod = method;
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 = (typedMethod.parameters ?? []).map((/** @type {any} */ p) => {
1164
- if (p.$ref) return p.$ref.split("/").pop();
1165
- return p.identifier ?? p.name ?? "arg";
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 {any} */ (new Function(typedMethod.getter.body));
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 {any} */ p) => p.$ref?.split("/").pop() ?? "v",
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 {any} */ (DynClass)[name] = new Function(...params, bodyStr);
1233
+ /** @type {DynamicClass} */ (DynClass)[name] = new Function(...params, bodyStr);
1185
1234
  } else {
1186
- /** @type {any} */ (DynClass.prototype)[name] = new Function(...params, bodyStr);
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
- return DynClass;
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<any>}
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 {any} */ v) => isTemplateString(v));
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 {any} */ value) => {
1294
+ .then((/** @type {unknown} */ value) => {
1243
1295
  s.value = value;
1244
1296
  })
1245
- .catch((/** @type {any} */ e) => console.error("Jx dev proxy:", e));
1297
+ .catch((/** @type {unknown} */ e) => console.error("Jx dev proxy:", e));
1246
1298
  });
1247
1299
  } else {
1248
1300
  doResolve(config)
1249
- .then((/** @type {any} */ value) => {
1301
+ .then((/** @type {unknown} */ value) => {
1250
1302
  s.value = value;
1251
1303
  })
1252
- .catch((/** @type {any} */ e) => console.error("Jx dev proxy:", e));
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<any>}
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 {any} */ v) => isRefObj(v));
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 {any} */ (v).$ref, state) : v;
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 {any} */ result) => {
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<any>}
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 {any} */ v) => isRefObj(v));
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 {any} */ (v).$ref, state) : v;
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 {any} */ result) => {
1430
+ .then((/** @type {unknown} */ result) => {
1379
1431
  s.value = result;
1380
1432
  })
1381
- .catch((/** @type {any} */ e) => console.error("Jx server proxy:", e));
1433
+ .catch((/** @type {unknown} */ e) => console.error("Jx server proxy:", e));
1382
1434
  });
1383
1435
  } else {
1384
1436
  doResolve(resolveArgs())
1385
- .then((/** @type {any} */ result) => {
1437
+ .then((/** @type {unknown} */ result) => {
1386
1438
  s.value = result;
1387
1439
  })
1388
- .catch((/** @type {any} */ e) => console.error("Jx server proxy:", e));
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 {any}
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 {any} v
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 {any} v
1489
+ * @param {unknown} v
1438
1490
  * @returns {boolean}
1439
1491
  */
1440
1492
  function isRefObj(v) {
1441
- return v !== null && typeof v === "object" && typeof v.$ref === "string";
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 {any} obj
1509
+ * @param {unknown} obj
1454
1510
  * @param {string} path
1455
- * @returns {any}
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, any>} rules
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 {any} */ (this)[key] !== undefined) {
1634
- state[key] = /** @type {any} */ (this)[key];
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 {any} */ v) => {
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 {any} */ (this)._state = state;
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 {any} [options]
1724
- * @param {any[]} [path]
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