sibujs 4.6.0 → 4.8.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/README.md +1 -1
- package/dist/browser.cjs +62 -8
- package/dist/browser.d.cts +23 -0
- package/dist/browser.d.ts +23 -0
- package/dist/browser.js +6 -5
- package/dist/build.cjs +2388 -1004
- package/dist/build.d.cts +147 -44
- package/dist/build.d.ts +147 -44
- package/dist/build.js +1410 -564
- package/dist/cdn.dev.global.js +14 -12
- package/dist/cdn.full.dev.global.js +15 -13
- package/dist/cdn.full.global.js +14 -12
- package/dist/cdn.global.js +12 -10
- package/dist/chunk-3ACAAQIN.js +224 -0
- package/dist/{chunk-7NCARGJW.js → chunk-44MTL7ED.js} +6 -6
- package/dist/{chunk-BAAG6ZTI.js → chunk-4WMSTZ7J.js} +8 -5
- package/dist/{chunk-H3SRKIYX.js → chunk-62BDYGAG.js} +8 -0
- package/dist/{chunk-DKGBBKOF.js → chunk-C5PEWEGH.js} +20 -5
- package/dist/{chunk-3ISI6ACU.js → chunk-C7AA2CZX.js} +1 -1
- package/dist/{chunk-UZQ6ALFS.js → chunk-CZPRSWSS.js} +1 -1
- package/dist/{chunk-D33YTSX5.js → chunk-DSID757X.js} +5 -4
- package/dist/{chunk-OXUY2A6L.js → chunk-EGUQLBHE.js} +22 -8
- package/dist/{chunk-2INLLLMZ.js → chunk-EXXSN3BW.js} +202 -26
- package/dist/{chunk-HURREPU2.js → chunk-FC6KCJSA.js} +87 -150
- package/dist/{chunk-HYXDKS4N.js → chunk-GSGNX35Q.js} +5 -5
- package/dist/{chunk-7GQCWFOE.js → chunk-J3YQ457H.js} +171 -85
- package/dist/{chunk-ORMZXBKQ.js → chunk-JUTFX2RM.js} +18 -10
- package/dist/{chunk-QE4TTDU3.js → chunk-KJ3UKABJ.js} +12 -11
- package/dist/{chunk-XRRZKZYX.js → chunk-LI4W3U6C.js} +1 -1
- package/dist/{chunk-47M47FOM.js → chunk-O6TRVMJI.js} +58 -6
- package/dist/{chunk-JSXPZCET.js → chunk-Q5J2LVKH.js} +1 -1
- package/dist/{chunk-5MT6SJ3P.js → chunk-QGMSESDD.js} +10 -5
- package/dist/{chunk-AIF3Z2T7.js → chunk-RGLPEBDE.js} +13 -10
- package/dist/{chunk-5Q4R7HCL.js → chunk-TQQUN4WR.js} +11 -2
- package/dist/{chunk-PK6FK2G2.js → chunk-UCNP3HVM.js} +183 -30
- package/dist/{chunk-XC4MEKGA.js → chunk-V3LHZSU5.js} +8 -2
- package/dist/{chunk-VVWPJ543.js → chunk-VWT3VXPL.js} +55 -20
- package/dist/{chunk-SXXVZMKZ.js → chunk-ZVLHNEEL.js} +17 -35
- package/dist/{contracts-DRIuclVT.d.cts → contracts-CLqzJnOV.d.ts} +28 -17
- package/dist/{contracts-DRIuclVT.d.ts → contracts-CTOJXu-x.d.cts} +28 -17
- package/dist/{customElement-MmInOW1U.d.ts → customElement-pYDKUzWl.d.cts} +11 -3
- package/dist/{customElement-MmInOW1U.d.cts → customElement-pYDKUzWl.d.ts} +11 -3
- package/dist/data.cjs +205 -39
- package/dist/data.d.cts +119 -3
- package/dist/data.d.ts +119 -3
- package/dist/data.js +6 -6
- package/dist/devtools.cjs +18 -5
- package/dist/devtools.js +5 -5
- package/dist/ecosystem.cjs +462 -58
- package/dist/ecosystem.d.cts +2 -1
- package/dist/ecosystem.d.ts +2 -1
- package/dist/ecosystem.js +9 -8
- package/dist/extras.cjs +852 -296
- package/dist/extras.d.cts +5 -4
- package/dist/extras.d.ts +5 -4
- package/dist/extras.js +24 -23
- package/dist/index.cjs +721 -176
- package/dist/index.d.cts +220 -169
- package/dist/index.d.ts +220 -169
- package/dist/index.js +16 -12
- package/dist/motion.cjs +12 -4
- package/dist/motion.js +3 -3
- package/dist/patterns.cjs +37 -21
- package/dist/patterns.d.cts +2 -1
- package/dist/patterns.d.ts +2 -1
- package/dist/patterns.js +6 -6
- package/dist/performance.cjs +48 -6
- package/dist/performance.d.cts +12 -2
- package/dist/performance.d.ts +12 -2
- package/dist/performance.js +8 -6
- package/dist/plugins.cjs +672 -261
- package/dist/plugins.d.cts +131 -13
- package/dist/plugins.d.ts +131 -13
- package/dist/plugins.js +83 -47
- package/dist/{ssr-XOTUASDO.js → ssr-5OWAH3U2.js} +4 -2
- package/dist/{ssr-Bli9XRW5.d.cts → ssr-C8hgIAw7.d.cts} +6 -0
- package/dist/{ssr-Bli9XRW5.d.ts → ssr-C8hgIAw7.d.ts} +6 -0
- package/dist/ssr.cjs +509 -142
- package/dist/ssr.d.cts +55 -5
- package/dist/ssr.d.ts +55 -5
- package/dist/ssr.js +11 -9
- package/dist/{tagFactory-DFstCLQV.d.cts → tagFactory-8qL9LCIx.d.cts} +58 -19
- package/dist/{tagFactory-DkaNVUNV.d.ts → tagFactory-BL2fymez.d.ts} +58 -19
- package/dist/testing.cjs +17 -4
- package/dist/testing.js +4 -4
- package/dist/types-CJFViL6Q.d.cts +26 -0
- package/dist/types-CJFViL6Q.d.ts +26 -0
- package/dist/ui.cjs +635 -254
- package/dist/ui.d.cts +3 -2
- package/dist/ui.d.ts +3 -2
- package/dist/ui.js +9 -8
- package/dist/widgets.cjs +22 -14
- package/dist/widgets.js +7 -7
- package/package.json +8 -5
package/dist/data.cjs
CHANGED
|
@@ -660,7 +660,7 @@ function forEachSubscriber(signal2, visit) {
|
|
|
660
660
|
}
|
|
661
661
|
|
|
662
662
|
// src/reactivity/track.ts
|
|
663
|
-
var _runtimeVersion = true ? "4.
|
|
663
|
+
var _runtimeVersion = true ? "4.8.0" : "dev";
|
|
664
664
|
var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
|
|
665
665
|
function resolveReactiveApi() {
|
|
666
666
|
const g = globalThis;
|
|
@@ -716,6 +716,14 @@ var getSubscriberCount2 = API.getSubscriberCount;
|
|
|
716
716
|
var getSubscriberDeps2 = API.getSubscriberDeps;
|
|
717
717
|
var forEachSubscriber2 = API.forEachSubscriber;
|
|
718
718
|
|
|
719
|
+
// src/core/devtoolsHook.ts
|
|
720
|
+
function emitDevtools(hook, event, payload) {
|
|
721
|
+
try {
|
|
722
|
+
hook.emit(event, payload);
|
|
723
|
+
} catch {
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
719
727
|
// src/core/signals/derived.ts
|
|
720
728
|
function derived(getter, options) {
|
|
721
729
|
devAssert(typeof getter === "function", "derived: argument must be a getter function.");
|
|
@@ -742,17 +750,17 @@ function derived(getter, options) {
|
|
|
742
750
|
cs._d = false;
|
|
743
751
|
cs._init = true;
|
|
744
752
|
};
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
try {
|
|
753
|
+
try {
|
|
754
|
+
track2(() => {
|
|
748
755
|
cs._v = getter();
|
|
749
756
|
cs._d = false;
|
|
750
757
|
cs._init = true;
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
758
|
+
}, markDirty);
|
|
759
|
+
} catch (err) {
|
|
760
|
+
disposed = true;
|
|
761
|
+
cleanup2(markDirty);
|
|
762
|
+
throw err;
|
|
763
|
+
}
|
|
756
764
|
const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
757
765
|
const validate = () => {
|
|
758
766
|
if (!cs._d || disposed || pendingError !== void 0) return;
|
|
@@ -771,7 +779,7 @@ function derived(getter, options) {
|
|
|
771
779
|
if (disposed) cleanup2(markDirty);
|
|
772
780
|
}
|
|
773
781
|
if (hook && !disposed && !Object.is(oldValue, cs._v)) {
|
|
774
|
-
hook
|
|
782
|
+
emitDevtools(hook, "computed:update", { signal: cs, oldValue, newValue: cs._v });
|
|
775
783
|
}
|
|
776
784
|
};
|
|
777
785
|
cs._validate = validate;
|
|
@@ -822,7 +830,7 @@ function derived(getter, options) {
|
|
|
822
830
|
}
|
|
823
831
|
}
|
|
824
832
|
};
|
|
825
|
-
if (hook) hook
|
|
833
|
+
if (hook) emitDevtools(hook, "computed:create", { signal: cs, name: debugName, getter: computedGetter });
|
|
826
834
|
return computedGetter;
|
|
827
835
|
}
|
|
828
836
|
|
|
@@ -991,12 +999,17 @@ function effect(effectFn, options) {
|
|
|
991
999
|
try {
|
|
992
1000
|
retrack2(ctx.bodyFn, ctx.subscriber);
|
|
993
1001
|
if (ctx.rerunPending) drainReruns(ctx);
|
|
1002
|
+
} catch (err) {
|
|
1003
|
+
ctx.disposed = true;
|
|
1004
|
+
if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
|
|
1005
|
+
cleanup2(ctx.subscriber);
|
|
1006
|
+
throw err;
|
|
994
1007
|
} finally {
|
|
995
1008
|
ctx.running = false;
|
|
996
1009
|
ctx.rerunPending = false;
|
|
997
1010
|
}
|
|
998
1011
|
const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
999
|
-
if (hook) hook
|
|
1012
|
+
if (hook) emitDevtools(hook, "effect:create", { effectFn });
|
|
1000
1013
|
return () => disposeEffect(ctx);
|
|
1001
1014
|
}
|
|
1002
1015
|
|
|
@@ -1083,7 +1096,7 @@ function signal(initial, options) {
|
|
|
1083
1096
|
state2.__v++;
|
|
1084
1097
|
if (DEV) {
|
|
1085
1098
|
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1086
|
-
if (hook) hook
|
|
1099
|
+
if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
|
|
1087
1100
|
}
|
|
1088
1101
|
if (!enqueueBatchedSignal(state2)) {
|
|
1089
1102
|
notifySubscribers2(state2);
|
|
@@ -1097,7 +1110,7 @@ function signal(initial, options) {
|
|
|
1097
1110
|
state2.value = newValue;
|
|
1098
1111
|
state2.__v++;
|
|
1099
1112
|
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1100
|
-
if (hook) hook
|
|
1113
|
+
if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
|
|
1101
1114
|
if (!enqueueBatchedSignal(state2)) {
|
|
1102
1115
|
notifySubscribers2(state2);
|
|
1103
1116
|
}
|
|
@@ -1116,7 +1129,7 @@ function signal(initial, options) {
|
|
|
1116
1129
|
}
|
|
1117
1130
|
if (DEV) {
|
|
1118
1131
|
const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1119
|
-
if (hook) hook
|
|
1132
|
+
if (hook) emitDevtools(hook, "signal:create", { signal: state2, name: debugName, getter: get, initial });
|
|
1120
1133
|
}
|
|
1121
1134
|
return [get, set];
|
|
1122
1135
|
}
|
|
@@ -1238,7 +1251,87 @@ function waitForRetryDelay(delay, signal2) {
|
|
|
1238
1251
|
});
|
|
1239
1252
|
}
|
|
1240
1253
|
|
|
1254
|
+
// src/data/structuralSharing.ts
|
|
1255
|
+
function isPlainObject(value) {
|
|
1256
|
+
if (value === null || typeof value !== "object") return false;
|
|
1257
|
+
const proto = Object.getPrototypeOf(value);
|
|
1258
|
+
return proto === Object.prototype || proto === null;
|
|
1259
|
+
}
|
|
1260
|
+
function isPlainArray(value) {
|
|
1261
|
+
return Array.isArray(value) && Object.getPrototypeOf(value) === Array.prototype;
|
|
1262
|
+
}
|
|
1263
|
+
function reconcile(prev, next, depth, walk, newRoot) {
|
|
1264
|
+
if (Object.is(prev, next)) return prev;
|
|
1265
|
+
const arrays = isPlainArray(prev) && isPlainArray(next);
|
|
1266
|
+
if (!arrays && !(isPlainObject(prev) && isPlainObject(next))) return next;
|
|
1267
|
+
if (Object.getPrototypeOf(prev) !== Object.getPrototypeOf(next)) return next;
|
|
1268
|
+
const nextObject = next;
|
|
1269
|
+
const seenAt = walk.path.get(nextObject);
|
|
1270
|
+
if (seenAt !== void 0) {
|
|
1271
|
+
walk.cycleTo = Math.min(walk.cycleTo, seenAt);
|
|
1272
|
+
return next;
|
|
1273
|
+
}
|
|
1274
|
+
const prevRecord = prev;
|
|
1275
|
+
const nextRecord = next;
|
|
1276
|
+
const nextKeys = arrays ? Array.from({ length: next.length }, (_, i) => i) : Object.keys(nextRecord);
|
|
1277
|
+
const prevSize = arrays ? prev.length : Object.keys(prevRecord).length;
|
|
1278
|
+
const copy = arrays ? new Array(nextKeys.length) : Object.create(Object.getPrototypeOf(next));
|
|
1279
|
+
let allFromPrev = prevSize === nextKeys.length;
|
|
1280
|
+
let allFromNext = true;
|
|
1281
|
+
const outerCycleTo = walk.cycleTo;
|
|
1282
|
+
walk.cycleTo = Number.POSITIVE_INFINITY;
|
|
1283
|
+
walk.path.set(nextObject, depth);
|
|
1284
|
+
for (const key of nextKeys) {
|
|
1285
|
+
if (arrays && !Object.hasOwn(nextRecord, key)) {
|
|
1286
|
+
if (Object.hasOwn(prevRecord, key)) allFromPrev = false;
|
|
1287
|
+
continue;
|
|
1288
|
+
}
|
|
1289
|
+
const nextChild = nextRecord[key];
|
|
1290
|
+
const hasPrev = Object.hasOwn(prevRecord, key);
|
|
1291
|
+
const shared = hasPrev ? reconcile(prevRecord[key], nextChild, depth + 1, walk, false) : nextChild;
|
|
1292
|
+
if (key === "__proto__") {
|
|
1293
|
+
Object.defineProperty(copy, key, { value: shared, writable: true, enumerable: true, configurable: true });
|
|
1294
|
+
} else {
|
|
1295
|
+
copy[key] = shared;
|
|
1296
|
+
}
|
|
1297
|
+
if (!hasPrev || !Object.is(shared, prevRecord[key])) allFromPrev = false;
|
|
1298
|
+
if (!Object.is(shared, nextChild)) allFromNext = false;
|
|
1299
|
+
}
|
|
1300
|
+
walk.path.delete(nextObject);
|
|
1301
|
+
const reached = walk.cycleTo;
|
|
1302
|
+
walk.cycleTo = Math.min(outerCycleTo, reached < depth ? reached : Number.POSITIVE_INFINITY);
|
|
1303
|
+
if (reached <= depth) return next;
|
|
1304
|
+
if (allFromPrev && !newRoot) return prev;
|
|
1305
|
+
if (allFromNext) return next;
|
|
1306
|
+
return copy;
|
|
1307
|
+
}
|
|
1308
|
+
function applyStructuralSharing(option, prev, next, explicit = false) {
|
|
1309
|
+
if (option === false || prev === void 0) return next;
|
|
1310
|
+
const forceNew = explicit && !Object.is(prev, next);
|
|
1311
|
+
if (typeof option === "function") {
|
|
1312
|
+
const shared = option(prev, next);
|
|
1313
|
+
return forceNew && Object.is(shared, prev) ? next : shared;
|
|
1314
|
+
}
|
|
1315
|
+
return reconcile(prev, next, 0, { path: /* @__PURE__ */ new Map(), cycleTo: Number.POSITIVE_INFINITY }, forceNew);
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1241
1318
|
// src/data/query.ts
|
|
1319
|
+
function shareSafely(option, prev, next, explicit) {
|
|
1320
|
+
const shared = untracked2(
|
|
1321
|
+
() => runSelect("query structuralSharing", () => applyStructuralSharing(option, prev, next, explicit))
|
|
1322
|
+
);
|
|
1323
|
+
return shared.ok ? shared.value : next;
|
|
1324
|
+
}
|
|
1325
|
+
function sharedView(entry) {
|
|
1326
|
+
const raw = entry.data;
|
|
1327
|
+
if (raw === void 0) return void 0;
|
|
1328
|
+
if (entry.hasShared && Object.is(entry.sharedFrom, raw)) return entry.shared;
|
|
1329
|
+
const value = shareSafely(true, entry.hasShared ? entry.shared : void 0, raw, entry.explicit);
|
|
1330
|
+
entry.shared = value;
|
|
1331
|
+
entry.sharedFrom = raw;
|
|
1332
|
+
entry.hasShared = true;
|
|
1333
|
+
return value;
|
|
1334
|
+
}
|
|
1242
1335
|
var globalQueryCache = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.query.cache.v1"), () => /* @__PURE__ */ new Map());
|
|
1243
1336
|
function getActiveQueryCache() {
|
|
1244
1337
|
return getRequestScopedCache("query") ?? globalQueryCache;
|
|
@@ -1264,7 +1357,11 @@ function getOrCreateEntry(cache, key, initialData) {
|
|
|
1264
1357
|
listeners: /* @__PURE__ */ new Set(),
|
|
1265
1358
|
refetchers: /* @__PURE__ */ new Set(),
|
|
1266
1359
|
controller: null,
|
|
1267
|
-
generation: 0
|
|
1360
|
+
generation: 0,
|
|
1361
|
+
explicit: false,
|
|
1362
|
+
shared: void 0,
|
|
1363
|
+
sharedFrom: void 0,
|
|
1364
|
+
hasShared: false
|
|
1268
1365
|
};
|
|
1269
1366
|
cache.set(key, entry);
|
|
1270
1367
|
}
|
|
@@ -1283,13 +1380,24 @@ function query(key, fetcher, options = {}) {
|
|
|
1283
1380
|
onSuccess,
|
|
1284
1381
|
onError,
|
|
1285
1382
|
onSettled,
|
|
1286
|
-
select
|
|
1383
|
+
select,
|
|
1384
|
+
structuralSharing = true
|
|
1287
1385
|
} = options;
|
|
1288
1386
|
const resolveKey = typeof key === "function" ? key : () => key;
|
|
1289
1387
|
const cache = getActiveQueryCache();
|
|
1290
1388
|
const [data, setData] = signal(initialData);
|
|
1291
1389
|
const [isFetching, setIsFetching] = signal(false);
|
|
1292
1390
|
const [error, setError] = signal(void 0);
|
|
1391
|
+
const [updatedAt, setUpdatedAt] = signal(0);
|
|
1392
|
+
const [base, setBase] = signal(void 0);
|
|
1393
|
+
let baseEntry = null;
|
|
1394
|
+
let baseValue;
|
|
1395
|
+
let baseExplicit = false;
|
|
1396
|
+
let customEntry = null;
|
|
1397
|
+
let customFrom;
|
|
1398
|
+
let customValue;
|
|
1399
|
+
let selectedEntry = null;
|
|
1400
|
+
let selectedValue;
|
|
1293
1401
|
let disposed = false;
|
|
1294
1402
|
let currentKey = null;
|
|
1295
1403
|
let attachedEntry = null;
|
|
@@ -1298,11 +1406,57 @@ function query(key, fetcher, options = {}) {
|
|
|
1298
1406
|
const loading = derived(() => isFetching() && data() === void 0);
|
|
1299
1407
|
const isStale = derived(() => {
|
|
1300
1408
|
data();
|
|
1409
|
+
updatedAt();
|
|
1301
1410
|
if (!currentKey) return true;
|
|
1302
1411
|
const entry = cache.get(currentKey);
|
|
1303
1412
|
if (!entry || entry.dataUpdatedAt === 0) return true;
|
|
1304
1413
|
return Date.now() - entry.dataUpdatedAt >= staleTime;
|
|
1305
1414
|
});
|
|
1415
|
+
function viewOf(entry) {
|
|
1416
|
+
const raw = entry.data;
|
|
1417
|
+
if (raw === void 0) return void 0;
|
|
1418
|
+
if (structuralSharing === false) return raw;
|
|
1419
|
+
if (typeof structuralSharing !== "function") return sharedView(entry);
|
|
1420
|
+
const sameEntry = customEntry === entry;
|
|
1421
|
+
if (sameEntry && Object.is(customFrom, raw)) return customValue;
|
|
1422
|
+
const value = shareSafely(structuralSharing, sameEntry ? customValue : void 0, raw, entry.explicit);
|
|
1423
|
+
customEntry = entry;
|
|
1424
|
+
customFrom = raw;
|
|
1425
|
+
customValue = value;
|
|
1426
|
+
return value;
|
|
1427
|
+
}
|
|
1428
|
+
function commitBase(entry, value) {
|
|
1429
|
+
const changed = !Object.is(value, baseValue);
|
|
1430
|
+
baseEntry = entry;
|
|
1431
|
+
baseValue = value;
|
|
1432
|
+
if (!select) {
|
|
1433
|
+
setData(value);
|
|
1434
|
+
return;
|
|
1435
|
+
}
|
|
1436
|
+
if (!changed) return;
|
|
1437
|
+
baseExplicit = entry?.explicit === true;
|
|
1438
|
+
setBase(value);
|
|
1439
|
+
}
|
|
1440
|
+
const stopSelect = select ? effect(() => {
|
|
1441
|
+
const raw = base();
|
|
1442
|
+
const entry = baseEntry;
|
|
1443
|
+
const explicit = baseExplicit;
|
|
1444
|
+
baseExplicit = false;
|
|
1445
|
+
if (entry === null && selectedEntry === null) return;
|
|
1446
|
+
if (raw === void 0) {
|
|
1447
|
+
selectedEntry = entry;
|
|
1448
|
+
selectedValue = void 0;
|
|
1449
|
+
setData(void 0);
|
|
1450
|
+
return;
|
|
1451
|
+
}
|
|
1452
|
+
const selected = runSelect("query select", () => select(raw));
|
|
1453
|
+
if (!selected.ok) return;
|
|
1454
|
+
const prev = selectedEntry === entry ? selectedValue : void 0;
|
|
1455
|
+
const value = shareSafely(structuralSharing, prev, selected.value, explicit);
|
|
1456
|
+
selectedEntry = entry;
|
|
1457
|
+
selectedValue = value;
|
|
1458
|
+
setData(value);
|
|
1459
|
+
}) : null;
|
|
1306
1460
|
function detachFromEntry() {
|
|
1307
1461
|
const entry = attachedEntry;
|
|
1308
1462
|
const key2 = attachedKey;
|
|
@@ -1361,7 +1515,7 @@ function query(key, fetcher, options = {}) {
|
|
|
1361
1515
|
const settledError = entry.error;
|
|
1362
1516
|
runCallback("query onError", () => onError?.(settledError));
|
|
1363
1517
|
} else if (entry.data !== void 0) {
|
|
1364
|
-
const settledData = entry
|
|
1518
|
+
const settledData = viewOf(entry);
|
|
1365
1519
|
runCallback("query onSuccess", () => onSuccess?.(settledData));
|
|
1366
1520
|
}
|
|
1367
1521
|
runCallback("query onSettled", () => onSettled?.());
|
|
@@ -1392,17 +1546,14 @@ function query(key, fetcher, options = {}) {
|
|
|
1392
1546
|
entry.promise = null;
|
|
1393
1547
|
entry.controller = null;
|
|
1394
1548
|
entry.data = result;
|
|
1549
|
+
entry.explicit = false;
|
|
1395
1550
|
entry.dataUpdatedAt = Date.now();
|
|
1396
1551
|
entry.error = void 0;
|
|
1397
1552
|
notifyListeners(entry.listeners, "query cache listener");
|
|
1398
1553
|
if (disposed || currentKey !== key2) return;
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
setIsFetching(false);
|
|
1403
|
-
setError(void 0);
|
|
1404
|
-
});
|
|
1405
|
-
runCallback("query onSuccess", () => onSuccess?.(result));
|
|
1554
|
+
if (!entry.listeners.has(onCacheUpdate)) onCacheUpdate();
|
|
1555
|
+
const committedResult = viewOf(entry);
|
|
1556
|
+
runCallback("query onSuccess", () => onSuccess?.(committedResult));
|
|
1406
1557
|
} catch (err) {
|
|
1407
1558
|
if (entry.generation !== generation) return;
|
|
1408
1559
|
entry.promise = null;
|
|
@@ -1432,18 +1583,19 @@ function query(key, fetcher, options = {}) {
|
|
|
1432
1583
|
const entry = cache.get(currentKey);
|
|
1433
1584
|
if (!entry) {
|
|
1434
1585
|
batch(() => {
|
|
1435
|
-
|
|
1586
|
+
commitBase(null, void 0);
|
|
1436
1587
|
setError(void 0);
|
|
1437
1588
|
setIsFetching(false);
|
|
1589
|
+
setUpdatedAt(0);
|
|
1438
1590
|
});
|
|
1439
1591
|
return;
|
|
1440
1592
|
}
|
|
1441
|
-
const
|
|
1442
|
-
const selected = raw !== void 0 && select ? runSelect("query select", () => select(raw)) : { ok: true, value: raw };
|
|
1593
|
+
const view = viewOf(entry);
|
|
1443
1594
|
batch(() => {
|
|
1444
|
-
|
|
1595
|
+
commitBase(entry, view);
|
|
1445
1596
|
setError(entry.error);
|
|
1446
1597
|
if (!entry.promise) setIsFetching(false);
|
|
1598
|
+
setUpdatedAt(entry.dataUpdatedAt);
|
|
1447
1599
|
});
|
|
1448
1600
|
}
|
|
1449
1601
|
const effectCleanup = effect(() => {
|
|
@@ -1453,11 +1605,11 @@ function query(key, fetcher, options = {}) {
|
|
|
1453
1605
|
const entry = getOrCreateEntry(cache, key2, initialData);
|
|
1454
1606
|
attachToEntry(entry, key2);
|
|
1455
1607
|
if (entry.data !== void 0) {
|
|
1456
|
-
const
|
|
1457
|
-
const selected = runSelect("query select", () => select ? select(raw) : raw);
|
|
1608
|
+
const view = viewOf(entry);
|
|
1458
1609
|
batch(() => {
|
|
1459
|
-
|
|
1610
|
+
commitBase(entry, view);
|
|
1460
1611
|
setError(entry.error);
|
|
1612
|
+
setUpdatedAt(entry.dataUpdatedAt);
|
|
1461
1613
|
});
|
|
1462
1614
|
}
|
|
1463
1615
|
if (!keyChanged && currentKey === key2 && entry.data !== void 0) {
|
|
@@ -1495,6 +1647,7 @@ function query(key, fetcher, options = {}) {
|
|
|
1495
1647
|
if (disposed) return;
|
|
1496
1648
|
disposed = true;
|
|
1497
1649
|
effectCleanup();
|
|
1650
|
+
stopSelect?.();
|
|
1498
1651
|
loading.dispose();
|
|
1499
1652
|
isStale.dispose();
|
|
1500
1653
|
if (intervalTimer) clearInterval(intervalTimer);
|
|
@@ -1526,13 +1679,15 @@ function invalidateQueries(keyOrPredicate) {
|
|
|
1526
1679
|
}
|
|
1527
1680
|
}
|
|
1528
1681
|
function getQueryData(key) {
|
|
1529
|
-
|
|
1682
|
+
const entry = getActiveQueryCache().get(key);
|
|
1683
|
+
return entry ? sharedView(entry) : void 0;
|
|
1530
1684
|
}
|
|
1531
1685
|
function setQueryData(key, data) {
|
|
1532
1686
|
const entry = getActiveQueryCache().get(key);
|
|
1533
1687
|
if (!entry) return;
|
|
1534
|
-
const newData = typeof data === "function" ? data(entry
|
|
1688
|
+
const newData = typeof data === "function" ? data(sharedView(entry)) : data;
|
|
1535
1689
|
entry.data = newData;
|
|
1690
|
+
entry.explicit = true;
|
|
1536
1691
|
entry.dataUpdatedAt = Date.now();
|
|
1537
1692
|
notifyListeners(entry.listeners, "query cache listener");
|
|
1538
1693
|
}
|
|
@@ -1916,6 +2071,15 @@ function resource(sourceOrFetcher, fetcherOrOptions, maybeOptions) {
|
|
|
1916
2071
|
const [loading, setLoading] = signal(false);
|
|
1917
2072
|
const [error, setError] = signal(void 0);
|
|
1918
2073
|
let currentData = options.initialValue;
|
|
2074
|
+
function commitData(next, explicit) {
|
|
2075
|
+
const sharing = options.structuralSharing ?? true;
|
|
2076
|
+
const shared = untracked2(
|
|
2077
|
+
() => runSelect("resource structuralSharing", () => applyStructuralSharing(sharing, currentData, next, explicit))
|
|
2078
|
+
);
|
|
2079
|
+
currentData = shared.ok ? shared.value : next;
|
|
2080
|
+
setData(currentData);
|
|
2081
|
+
return currentData;
|
|
2082
|
+
}
|
|
1919
2083
|
let abortController = null;
|
|
1920
2084
|
let disposed = false;
|
|
1921
2085
|
let effectCleanup = null;
|
|
@@ -1935,12 +2099,12 @@ function resource(sourceOrFetcher, fetcherOrOptions, maybeOptions) {
|
|
|
1935
2099
|
try {
|
|
1936
2100
|
const result = await withRetry(() => fetcher(sourceValue, { signal: signal2, prev }), options.retry, void 0, signal2);
|
|
1937
2101
|
if (version !== fetchVersion || disposed) return;
|
|
1938
|
-
|
|
2102
|
+
let committed = result;
|
|
1939
2103
|
batch(() => {
|
|
1940
|
-
|
|
2104
|
+
committed = commitData(result, false);
|
|
1941
2105
|
setLoading(false);
|
|
1942
2106
|
});
|
|
1943
|
-
runCallback("resource onSuccess", () => options.onSuccess?.(
|
|
2107
|
+
runCallback("resource onSuccess", () => options.onSuccess?.(committed));
|
|
1944
2108
|
} catch (err) {
|
|
1945
2109
|
if (version !== fetchVersion || disposed) return;
|
|
1946
2110
|
if (isAbortError(err)) {
|
|
@@ -1974,8 +2138,7 @@ function resource(sourceOrFetcher, fetcherOrOptions, maybeOptions) {
|
|
|
1974
2138
|
refetch: () => doFetch(source ? source() : void 0),
|
|
1975
2139
|
mutate: (value) => {
|
|
1976
2140
|
const newValue = typeof value === "function" ? value(currentData) : value;
|
|
1977
|
-
|
|
1978
|
-
setData(newValue);
|
|
2141
|
+
commitData(newValue, true);
|
|
1979
2142
|
},
|
|
1980
2143
|
abort: () => abortController?.abort(),
|
|
1981
2144
|
dispose: () => {
|
|
@@ -2501,6 +2664,9 @@ function sanitizeUrl(url) {
|
|
|
2501
2664
|
const probe = stripControlChars(url).trim();
|
|
2502
2665
|
if (!probe) return "";
|
|
2503
2666
|
const output = url.replace(OUTER_TRIM, "");
|
|
2667
|
+
if (probe.length >= 2 && (probe[0] === "\\" || probe[0] === "/") && (probe[1] === "\\" || probe[1] === "/") && (probe[0] === "\\" || probe[1] === "\\")) {
|
|
2668
|
+
return "";
|
|
2669
|
+
}
|
|
2504
2670
|
const lower = probe.toLowerCase();
|
|
2505
2671
|
let schemeEnd = -1;
|
|
2506
2672
|
for (let i = 0; i < lower.length; i++) {
|
package/dist/data.d.cts
CHANGED
|
@@ -57,6 +57,43 @@ declare function calculateDelay(attempt: number, strategy: "exponential" | "line
|
|
|
57
57
|
*/
|
|
58
58
|
declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions, onRetry?: (error: unknown, attempt: number, delay: number) => void, signal?: AbortSignal): Promise<T>;
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Structural sharing for the data layer.
|
|
62
|
+
*
|
|
63
|
+
* A refetch almost always produces a brand-new object graph, even when the
|
|
64
|
+
* server sent back exactly what it sent last time. Committing that graph to a
|
|
65
|
+
* signal notifies every subscriber — signals compare with `Object.is` — so a
|
|
66
|
+
* background refresh that changed nothing still re-ran every binding reading
|
|
67
|
+
* the data, and a `when(() => q.data(), …)` or a `() => q.data() && Form()`
|
|
68
|
+
* child rebuilt its subtree and threw away whatever the user was typing.
|
|
69
|
+
*
|
|
70
|
+
* `replaceEqualDeep(prev, next)` reconciles the new graph against the old one:
|
|
71
|
+
*
|
|
72
|
+
* - deeply equal → returns `prev` itself, so the signal write is a no-op and
|
|
73
|
+
* nothing is notified;
|
|
74
|
+
* - partially equal → returns a new container in which every unchanged
|
|
75
|
+
* subtree is the OLD reference, so an `each()` keyed by reference, a
|
|
76
|
+
* `derived` over one branch, or an `equals`-less signal holding a sub-object
|
|
77
|
+
* stays stable while only the changed path gets fresh identities.
|
|
78
|
+
*
|
|
79
|
+
* Only plain objects (prototype `Object.prototype` or `null`) and arrays are
|
|
80
|
+
* reconciled. Everything else — Date, Map, Set, class instances — is opaque
|
|
81
|
+
* and compared by identity: its enumerable keys say nothing about its state,
|
|
82
|
+
* and rebuilding it as a plain object would change its type.
|
|
83
|
+
*
|
|
84
|
+
* Internal to the data layer; not re-exported from `data.ts`.
|
|
85
|
+
*/
|
|
86
|
+
/**
|
|
87
|
+
* The public knob shared by `query()` and `resource()`.
|
|
88
|
+
*
|
|
89
|
+
* - `true` (default) — reconcile with {@link replaceEqualDeep}.
|
|
90
|
+
* - `false` — commit every result as-is (a fresh reference notifies).
|
|
91
|
+
* - a function — custom reconciliation. It receives the previously committed
|
|
92
|
+
* value and the new one and returns what to commit; returning `prev` means
|
|
93
|
+
* "unchanged" and notifies nobody.
|
|
94
|
+
*/
|
|
95
|
+
type StructuralSharingOption<T> = boolean | ((prev: T, next: T) => T);
|
|
96
|
+
|
|
60
97
|
/**
|
|
61
98
|
* ## Callback semantics
|
|
62
99
|
*
|
|
@@ -118,8 +155,52 @@ interface QueryOptions<T> {
|
|
|
118
155
|
onError?: (error: Error) => void;
|
|
119
156
|
/** Called on fetch settle (success or error) */
|
|
120
157
|
onSettled?: () => void;
|
|
121
|
-
/**
|
|
158
|
+
/**
|
|
159
|
+
* Transform fetched data before returning to consumers. Cache stores raw data.
|
|
160
|
+
*
|
|
161
|
+
* Runs in this observer's own reactive computation: it re-runs when the
|
|
162
|
+
* cached value changes, and when a signal it reads changes — without
|
|
163
|
+
* re-running the key effect, so such a signal never triggers a refetch. It
|
|
164
|
+
* runs once per fetch that changed the data, and its output is structurally
|
|
165
|
+
* shared against this observer's previous output for the same key, so an
|
|
166
|
+
* identical refetch notifies nobody.
|
|
167
|
+
*/
|
|
122
168
|
select?: (data: T) => T;
|
|
169
|
+
/**
|
|
170
|
+
* Keep data referentially stable across refetches. Default: `true`.
|
|
171
|
+
*
|
|
172
|
+
* Every refetch — `refetchInterval`, window focus, reconnect,
|
|
173
|
+
* `invalidateQueries` — produces a new object graph even when the server
|
|
174
|
+
* returned the same thing. With structural sharing on, the new result is
|
|
175
|
+
* reconciled against the previous value of the same key: if it is deeply
|
|
176
|
+
* equal the previous reference is kept and `data` subscribers are not
|
|
177
|
+
* notified at all; if only part of it changed, every unchanged nested object
|
|
178
|
+
* or array keeps its old reference, so `each()` rows and deriveds over
|
|
179
|
+
* untouched branches stay put. Only plain objects and arrays are compared —
|
|
180
|
+
* Date, Map, Set and class instances compare by identity. Cyclic data is
|
|
181
|
+
* safe: the containers on a cycle are taken as-is.
|
|
182
|
+
*
|
|
183
|
+
* Every observer of a key using the default holds the SAME reference.
|
|
184
|
+
*
|
|
185
|
+
* `setQueryData()` is an explicit write, not a fetch: when it hands over a
|
|
186
|
+
* new top-level reference, observers always receive a new top-level
|
|
187
|
+
* reference and are notified — even if every child is unchanged, as after
|
|
188
|
+
* `prev.items.push(x); return { ...prev }`. Unchanged nested subtrees are
|
|
189
|
+
* still reused beneath it.
|
|
190
|
+
*
|
|
191
|
+
* The option applies to this observer only; observers of one key may use
|
|
192
|
+
* different settings.
|
|
193
|
+
*
|
|
194
|
+
* - `false` commits every result as-is (each refetch notifies).
|
|
195
|
+
* - A function `(prev, next) => T` replaces the default reconciliation;
|
|
196
|
+
* `prev` is this observer's previous value for the same key. Return `prev`
|
|
197
|
+
* to report "unchanged" (ignored for an explicit write of a new
|
|
198
|
+
* reference). It must not throw — if it does, the error is reported and
|
|
199
|
+
* `next` is committed unshared.
|
|
200
|
+
*
|
|
201
|
+
* Also applied to this observer's `select` output.
|
|
202
|
+
*/
|
|
203
|
+
structuralSharing?: StructuralSharingOption<T>;
|
|
123
204
|
}
|
|
124
205
|
interface QueryResult<T> {
|
|
125
206
|
/** Reactive getter for the cached data */
|
|
@@ -143,9 +224,20 @@ declare function query<T>(key: string | (() => string), fetcher: (ctx: {
|
|
|
143
224
|
}) => Promise<T>, options?: QueryOptions<T>): QueryResult<T>;
|
|
144
225
|
/** Invalidate queries matching a key or predicate, triggering refetch for active subscribers */
|
|
145
226
|
declare function invalidateQueries(keyOrPredicate: string | ((key: string) => boolean)): void;
|
|
146
|
-
/**
|
|
227
|
+
/**
|
|
228
|
+
* Get cached data for a query key — the same reference every observer using
|
|
229
|
+
* the default `structuralSharing` holds.
|
|
230
|
+
*/
|
|
147
231
|
declare function getQueryData<T>(key: string): T | undefined;
|
|
148
|
-
/**
|
|
232
|
+
/**
|
|
233
|
+
* Set cached data for a query key, notifying subscribers.
|
|
234
|
+
*
|
|
235
|
+
* An explicit write: a value (or updater result) that is a new top-level
|
|
236
|
+
* reference reaches every observer as a new top-level reference, even when it
|
|
237
|
+
* is deeply equal to the previous value — so `prev.items.push(x); return
|
|
238
|
+
* { ...prev }` is never dropped. Unchanged nested subtrees are still reused.
|
|
239
|
+
* Returning `prev` itself changes nothing.
|
|
240
|
+
*/
|
|
149
241
|
declare function setQueryData<T>(key: string, data: T | ((prev: T | undefined) => T)): void;
|
|
150
242
|
/** Clear the entire query cache */
|
|
151
243
|
declare function clearQueryCache(): void;
|
|
@@ -354,6 +446,30 @@ interface ResourceOptions<T> {
|
|
|
354
446
|
onError?: (error: Error) => void;
|
|
355
447
|
/** Called on fetch settle (success or error) */
|
|
356
448
|
onSettled?: () => void;
|
|
449
|
+
/**
|
|
450
|
+
* Keep data referentially stable across refetches. Default: `true`.
|
|
451
|
+
*
|
|
452
|
+
* Each fetched result — from `refetch()` or a source change — is
|
|
453
|
+
* reconciled against the value already held: a deeply equal result keeps
|
|
454
|
+
* the previous reference and notifies nobody, and a partially changed one
|
|
455
|
+
* reuses every unchanged nested object or array. Only plain objects and
|
|
456
|
+
* arrays are compared; Date, Map, Set and class instances compare by
|
|
457
|
+
* identity. Cyclic data is safe: the containers on a cycle are taken as-is.
|
|
458
|
+
* Same contract as `QueryOptions.structuralSharing`.
|
|
459
|
+
*
|
|
460
|
+
* `mutate()` is an explicit write, not a fetch: when it hands over a new
|
|
461
|
+
* top-level reference, `data` always becomes a new top-level reference and
|
|
462
|
+
* notifies — even if every child is unchanged, as after
|
|
463
|
+
* `prev.items.push(x); return { ...prev }`. Unchanged nested subtrees are
|
|
464
|
+
* still reused beneath it.
|
|
465
|
+
*
|
|
466
|
+
* - `false` commits every result as-is.
|
|
467
|
+
* - A function `(prev, next) => T` replaces the default reconciliation;
|
|
468
|
+
* return `prev` to report "unchanged" (ignored for a `mutate()` of a new
|
|
469
|
+
* reference). If it throws, the error is reported and `next` is committed
|
|
470
|
+
* unshared.
|
|
471
|
+
*/
|
|
472
|
+
structuralSharing?: StructuralSharingOption<T>;
|
|
357
473
|
}
|
|
358
474
|
interface Resource<T> {
|
|
359
475
|
/** Reactive getter for the fetched data */
|