@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.
@@ -3092,6 +3092,15 @@ const resolveActionProxies = (actionSet) => {
3092
3092
 
3093
3093
  const NO_PARAMS = { __no_params__: true };
3094
3094
  const mergeActionParams = (currentParams, newParams) => {
3095
+ if (newParams === null) {
3096
+ // `null` is a question that cannot be asked: a callback destructuring its
3097
+ // params throws on it. It is what a computed says when it has not got
3098
+ // enough to ask with, and "nothing to ask" is said with `undefined`
3099
+ // everywhere below (and by the run guards reading paramsSignal), so it
3100
+ // becomes that here, at the one door params coming from a binding go
3101
+ // through.
3102
+ newParams = undefined;
3103
+ }
3095
3104
  // The order of these two checks is load-bearing. Checking `undefined` first
3096
3105
  // looks symmetric — "no new params, keep whatever is there, NO_PARAMS
3097
3106
  // included" — and breaks routing: merge(NO_PARAMS, undefined) must yield
@@ -3349,7 +3358,10 @@ const createAction = (callback, rootOptions = {}) => {
3349
3358
  *
3350
3359
  * @param {any} newParamsOrSignal - params, or a signal holding them (the
3351
3360
  * result then retargets itself as the signal changes), or an object whose
3352
- * values may be signals.
3361
+ * values may be signals. A signal with nothing to ask yet — a question
3362
+ * computed from a form still missing a field — holds `undefined` or
3363
+ * `null`: the instance then has no params, which is what
3364
+ * `useAsyncData(action, { run: true })` reads to start nothing.
3353
3365
  * @param {object} [options]
3354
3366
  * @param {number} [options.debounce] - milliseconds the params must stay
3355
3367
  * stable before the instance follows them. What it buys is a screen that
@@ -16509,6 +16521,11 @@ const generateSignalId = () => {
16509
16521
  * as the user moved. `"push"` is for a state whose values ARE places one came
16510
16522
  * from (the photo one is looking at in a gallery). Whatever is said here, one
16511
16523
  * write can say otherwise: `signal.set(value, { history })`.
16524
+ * Either way every write reaches the address, synchronously, and browsers
16525
+ * refuse an address written too often (Safari: 100 writes per 10 s, then a
16526
+ * SecurityError). A value one DRAGS is written at 60 Hz: keep it in the
16527
+ * gesture while the finger is down and write the state once, on release —
16528
+ * an address is not a recording of a gesture.
16512
16529
  * @param {boolean} [options.debug=false] - Enable debug logging for this signal's operations
16513
16530
  * @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.
16514
16531
  *
@@ -26544,11 +26561,7 @@ const setupBrowserIntegrationViaHistory = ({
26544
26561
  [NAV_DEPTH_STATE_KEY]: getNavDepth(),
26545
26562
  },
26546
26563
  });
26547
- if (navigationType === "push") {
26548
- window.history.pushState(effectiveState, null, url);
26549
- } else {
26550
- window.history.replaceState(effectiveState, null, url);
26551
- }
26564
+ writeHistoryEntry(navigationType, effectiveState, url);
26552
26565
  rememberEntryIsOfThisDocument();
26553
26566
  updateDocumentUrl(url);
26554
26567
  updateDocumentState(effectiveState);
@@ -26568,14 +26581,14 @@ const setupBrowserIntegrationViaHistory = ({
26568
26581
  [NAV_DEPTH_STATE_KEY]: getNavDepth(),
26569
26582
  },
26570
26583
  });
26571
- window.history.replaceState(state, null, url);
26584
+ writeHistoryEntry("replace", state, url);
26572
26585
  rememberEntryIsOfThisDocument();
26573
26586
  } else if (redirected) {
26574
26587
  // The entry the browser is on names an address that only sends
26575
26588
  // elsewhere — a cold load on it, or a back into it. Written over where
26576
26589
  // it stands (the entry keeps its place in the stack, hence its state
26577
26590
  // and the depth in it): pressing back must not walk into it again.
26578
- window.history.replaceState(state, null, url);
26591
+ writeHistoryEntry("replace", state, url);
26579
26592
  rememberEntryIsOfThisDocument();
26580
26593
  }
26581
26594
  updateDocumentUrl(url);
@@ -26828,7 +26841,7 @@ const setupBrowserIntegrationViaHistory = ({
26828
26841
  // The entry itself has to lose them too, not just the document state:
26829
26842
  // getDocumentState() reads the entry back, and every state write copies
26830
26843
  // what it finds there onto the next one.
26831
- window.history.replaceState(state, null, url);
26844
+ writeHistoryEntry("replace", state, url);
26832
26845
  }
26833
26846
  handleRoutingTask(url, {
26834
26847
  reason: "routing initialization",
@@ -26850,11 +26863,25 @@ const setupBrowserIntegrationViaHistory = ({
26850
26863
  visitedUrlsSignal,
26851
26864
  };
26852
26865
  };
26866
+ 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.`;
26867
+ const writeHistoryEntry = (navigationType, state, url) => {
26868
+ try {
26869
+ if (navigationType === "push") {
26870
+ window.history.pushState(state, null, url);
26871
+ } else {
26872
+ window.history.replaceState(state, null, url);
26873
+ }
26874
+ } catch (e) {
26875
+ if (e.name !== "SecurityError") {
26876
+ throw e;
26877
+ }
26878
+ throw new Error(
26879
+ `The browser refused to write the address ${url}: ${e.message}. ${ADDRESS_WRITE_ADVICE}`,
26880
+ { cause: e },
26881
+ );
26882
+ }
26883
+ };
26853
26884
 
26854
- // Read before the history is written, in both places that ask: a push or a
26855
- // replace onto the url already displayed. Never a traverse — the browser has
26856
- // already moved window.location.href when a popstate is handled, so the
26857
- // comparison would say "same" about every back and forward.
26858
26885
  const isStateOnlyNavigation = (
26859
26886
  url,
26860
26887
  { navigationType },