@jsenv/navi 0.29.325 → 0.29.326
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/dev/jsenv_navi.js +73 -9
- package/dist/dev/jsenv_navi.js.map +4 -4
- package/dist/jsenv_navi.js +40 -13
- package/dist/jsenv_navi.js.map +4 -4
- package/package.json +1 -1
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -3235,6 +3235,15 @@ const resolveActionProxies = (actionSet) => {
|
|
|
3235
3235
|
|
|
3236
3236
|
const NO_PARAMS = { __no_params__: true };
|
|
3237
3237
|
const mergeActionParams = (currentParams, newParams) => {
|
|
3238
|
+
if (newParams === null) {
|
|
3239
|
+
// `null` is a question that cannot be asked: a callback destructuring its
|
|
3240
|
+
// params throws on it. It is what a computed says when it has not got
|
|
3241
|
+
// enough to ask with, and "nothing to ask" is said with `undefined`
|
|
3242
|
+
// everywhere below (and by the run guards reading paramsSignal), so it
|
|
3243
|
+
// becomes that here, at the one door params coming from a binding go
|
|
3244
|
+
// through.
|
|
3245
|
+
newParams = undefined;
|
|
3246
|
+
}
|
|
3238
3247
|
// The order of these two checks is load-bearing. Checking `undefined` first
|
|
3239
3248
|
// looks symmetric — "no new params, keep whatever is there, NO_PARAMS
|
|
3240
3249
|
// included" — and breaks routing: merge(NO_PARAMS, undefined) must yield
|
|
@@ -3492,7 +3501,10 @@ const createAction = (callback, rootOptions = {}) => {
|
|
|
3492
3501
|
*
|
|
3493
3502
|
* @param {any} newParamsOrSignal - params, or a signal holding them (the
|
|
3494
3503
|
* result then retargets itself as the signal changes), or an object whose
|
|
3495
|
-
* values may be signals.
|
|
3504
|
+
* values may be signals. A signal with nothing to ask yet — a question
|
|
3505
|
+
* computed from a form still missing a field — holds `undefined` or
|
|
3506
|
+
* `null`: the instance then has no params, which is what
|
|
3507
|
+
* `useAsyncData(action, { run: true })` reads to start nothing.
|
|
3496
3508
|
* @param {object} [options]
|
|
3497
3509
|
* @param {number} [options.debounce] - milliseconds the params must stay
|
|
3498
3510
|
* stable before the instance follows them. What it buys is a screen that
|
|
@@ -17101,6 +17113,11 @@ const generateSignalId = () => {
|
|
|
17101
17113
|
* as the user moved. `"push"` is for a state whose values ARE places one came
|
|
17102
17114
|
* from (the photo one is looking at in a gallery). Whatever is said here, one
|
|
17103
17115
|
* write can say otherwise: `signal.set(value, { history })`.
|
|
17116
|
+
* Either way every write reaches the address, synchronously, and browsers
|
|
17117
|
+
* refuse an address written too often (Safari: 100 writes per 10 s, then a
|
|
17118
|
+
* SecurityError). A value one DRAGS is written at 60 Hz: keep it in the
|
|
17119
|
+
* gesture while the finger is down and write the state once, on release —
|
|
17120
|
+
* an address is not a recording of a gesture.
|
|
17104
17121
|
* @param {boolean} [options.debug=false] - Enable debug logging for this signal's operations
|
|
17105
17122
|
* @returns {import("@preact/signals").Signal} A signal that can be synchronized with a source signal and/or persisted in localStorage. The signal includes a `validity` property for validation state.
|
|
17106
17123
|
*
|
|
@@ -27294,11 +27311,7 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
27294
27311
|
[NAV_DEPTH_STATE_KEY]: getNavDepth(),
|
|
27295
27312
|
},
|
|
27296
27313
|
});
|
|
27297
|
-
|
|
27298
|
-
window.history.pushState(effectiveState, null, url);
|
|
27299
|
-
} else {
|
|
27300
|
-
window.history.replaceState(effectiveState, null, url);
|
|
27301
|
-
}
|
|
27314
|
+
writeHistoryEntry(navigationType, effectiveState, url);
|
|
27302
27315
|
rememberEntryIsOfThisDocument();
|
|
27303
27316
|
updateDocumentUrl(url);
|
|
27304
27317
|
updateDocumentState(effectiveState);
|
|
@@ -27318,14 +27331,14 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
27318
27331
|
[NAV_DEPTH_STATE_KEY]: getNavDepth(),
|
|
27319
27332
|
},
|
|
27320
27333
|
});
|
|
27321
|
-
|
|
27334
|
+
writeHistoryEntry("replace", state, url);
|
|
27322
27335
|
rememberEntryIsOfThisDocument();
|
|
27323
27336
|
} else if (redirected) {
|
|
27324
27337
|
// The entry the browser is on names an address that only sends
|
|
27325
27338
|
// elsewhere — a cold load on it, or a back into it. Written over where
|
|
27326
27339
|
// it stands (the entry keeps its place in the stack, hence its state
|
|
27327
27340
|
// and the depth in it): pressing back must not walk into it again.
|
|
27328
|
-
|
|
27341
|
+
writeHistoryEntry("replace", state, url);
|
|
27329
27342
|
rememberEntryIsOfThisDocument();
|
|
27330
27343
|
}
|
|
27331
27344
|
updateDocumentUrl(url);
|
|
@@ -27578,7 +27591,7 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
27578
27591
|
// The entry itself has to lose them too, not just the document state:
|
|
27579
27592
|
// getDocumentState() reads the entry back, and every state write copies
|
|
27580
27593
|
// what it finds there onto the next one.
|
|
27581
|
-
|
|
27594
|
+
writeHistoryEntry("replace", state, url);
|
|
27582
27595
|
}
|
|
27583
27596
|
handleRoutingTask(url, {
|
|
27584
27597
|
reason: "routing initialization",
|
|
@@ -27605,6 +27618,57 @@ const setupBrowserIntegrationViaHistory = ({
|
|
|
27605
27618
|
// replace onto the url already displayed. Never a traverse — the browser has
|
|
27606
27619
|
// already moved window.location.href when a popstate is handled, so the
|
|
27607
27620
|
// comparison would say "same" about every back and forward.
|
|
27621
|
+
// Browsers refuse an address written too often: WebKit throws a SecurityError
|
|
27622
|
+
// past 100 writes per 10 seconds, Chromium and Firefox drop the write. A state
|
|
27623
|
+
// bound to the url and written per frame gets there in under two seconds, and
|
|
27624
|
+
// a developer working in Chromium never sees it. Every write goes through here
|
|
27625
|
+
// so the rate is measured against the strictest budget before any browser
|
|
27626
|
+
// refuses, and so a refusal names its cause rather than `replaceState`.
|
|
27627
|
+
const HISTORY_WRITE_BUDGET = 100;
|
|
27628
|
+
const HISTORY_WRITE_WINDOW_MS = 10_000;
|
|
27629
|
+
const ADDRESS_WRITE_ADVICE = `An address is not a recording of a gesture: a value dragged per frame belongs to the gesture, and the state bound to the url is written once, on release.`;
|
|
27630
|
+
const writeHistoryEntry = (navigationType, state, url) => {
|
|
27631
|
+
noteHistoryWrite(url);
|
|
27632
|
+
try {
|
|
27633
|
+
if (navigationType === "push") {
|
|
27634
|
+
window.history.pushState(state, null, url);
|
|
27635
|
+
} else {
|
|
27636
|
+
window.history.replaceState(state, null, url);
|
|
27637
|
+
}
|
|
27638
|
+
} catch (e) {
|
|
27639
|
+
if (e.name !== "SecurityError") {
|
|
27640
|
+
throw e;
|
|
27641
|
+
}
|
|
27642
|
+
throw new Error(
|
|
27643
|
+
`The browser refused to write the address ${url}: ${e.message}. ${ADDRESS_WRITE_ADVICE}`,
|
|
27644
|
+
{ cause: e },
|
|
27645
|
+
);
|
|
27646
|
+
}
|
|
27647
|
+
};
|
|
27648
|
+
const noteHistoryWrite = (() => {
|
|
27649
|
+
const writeTimes = [];
|
|
27650
|
+
let warned = false;
|
|
27651
|
+
return (url) => {
|
|
27652
|
+
if (warned) {
|
|
27653
|
+
return;
|
|
27654
|
+
}
|
|
27655
|
+
const now = performance.now();
|
|
27656
|
+
writeTimes.push(now);
|
|
27657
|
+
while (writeTimes[0] < now - HISTORY_WRITE_WINDOW_MS) {
|
|
27658
|
+
writeTimes.shift();
|
|
27659
|
+
}
|
|
27660
|
+
if (writeTimes.length < HISTORY_WRITE_BUDGET) {
|
|
27661
|
+
return;
|
|
27662
|
+
}
|
|
27663
|
+
warned = true;
|
|
27664
|
+
const seconds = ((now - writeTimes[0]) / 1000).toFixed(1);
|
|
27665
|
+
console.warn(
|
|
27666
|
+
`[navi] the address was written ${HISTORY_WRITE_BUDGET} times in ${seconds}s (last: ${url}). Safari refuses more than ${HISTORY_WRITE_BUDGET} writes per ${HISTORY_WRITE_WINDOW_MS / 1000}s with a SecurityError, and the state then runs ahead of the address. ${ADDRESS_WRITE_ADVICE}`,
|
|
27667
|
+
);
|
|
27668
|
+
};
|
|
27669
|
+
})()
|
|
27670
|
+
;
|
|
27671
|
+
|
|
27608
27672
|
const isStateOnlyNavigation = (
|
|
27609
27673
|
url,
|
|
27610
27674
|
{ navigationType },
|