@jsenv/navi 0.29.437 → 0.29.438

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.
@@ -2975,6 +2975,41 @@ const setActionPrivateProperties = (action, properties) => {
2975
2975
  actionPrivatePropertiesWeakMap.set(action, properties);
2976
2976
  };
2977
2977
 
2978
+ /*
2979
+ * Where an action that declares no provisionalValue of its own finds one: the
2980
+ * value the page kept from the previous document, for a route action that is
2981
+ * not a resource read (see keepPageOnScreen in nav/page_kept.js). routeAction
2982
+ * adds the source when it binds the action; a run asks for it at its start,
2983
+ * like it asks provisionalValue (actions.js).
2984
+ *
2985
+ * A registry beside actions.js rather than an import of the kept reads: those
2986
+ * know about stores and params keys, which import actions.js.
2987
+ */
2988
+
2989
+ const keptValueSourcesWeakMap = new WeakMap();
2990
+ const addKeptValueSource = (rootAction, source) => {
2991
+ let sources = keptValueSourcesWeakMap.get(rootAction);
2992
+ if (!sources) {
2993
+ sources = [];
2994
+ keptValueSourcesWeakMap.set(rootAction, sources);
2995
+ }
2996
+ sources.push(source);
2997
+ };
2998
+ const takeKeptValue = (action, params) => {
2999
+ const rootAction = action.rootAction || action;
3000
+ const sources = keptValueSourcesWeakMap.get(rootAction);
3001
+ if (!sources) {
3002
+ return undefined;
3003
+ }
3004
+ for (const source of sources) {
3005
+ const value = source(params);
3006
+ if (value !== undefined) {
3007
+ return value;
3008
+ }
3009
+ }
3010
+ return undefined;
3011
+ };
3012
+
2978
3013
  /*
2979
3014
  * Actions: async callbacks wrapped in reactive state.
2980
3015
  *
@@ -4210,8 +4245,12 @@ const createAction = (callback, rootOptions = {}) => {
4210
4245
  actionAbortMap.set(action, abort);
4211
4246
 
4212
4247
  batch(() => {
4213
- if (provisionalValue && valueSignal.peek() === undefined) {
4214
- const provisional = provisionalValue(params, action);
4248
+ if (valueSignal.peek() === undefined) {
4249
+ // An action saying nothing of its own may still have a value the
4250
+ // page kept from the previous document (see kept_values.js).
4251
+ const provisional = provisionalValue
4252
+ ? provisionalValue(params, action)
4253
+ : takeKeptValue(action, params);
4215
4254
  if (provisional !== undefined) {
4216
4255
  valueSignal.value = provisional;
4217
4256
  }
@@ -26749,10 +26788,10 @@ const isOwnerReadBy = (childResource, childAction, ownerGetAction) => {
26749
26788
  * out again, after a reset say, it would write the previous document's rows
26750
26789
  * over a fresher answer.
26751
26790
  *
26752
- * This module is the half the resources see: `resource()` and the range reader
26753
- * take from it, and say which of their reads can be kept at all. Which reads
26754
- * are on the page on screen, and what the slot is written into, is
26755
- * page_kept.js.
26791
+ * This module is the half the reads see: `resource()` and the range reader
26792
+ * take from it and say which of their reads are kept as rows, routeAction takes
26793
+ * from it for any other action (through action/kept_values.js). Which reads are
26794
+ * on the page on screen, and what the slot is written into, is page_kept.js.
26756
26795
  */
26757
26796
 
26758
26797
  // key → what the read answered in the previous document, not handed out yet
@@ -26775,9 +26814,10 @@ const takeKeptRead = (key) => {
26775
26814
  return entry;
26776
26815
  };
26777
26816
 
26778
- // The root actions whose answer is rows of a store, written and read back the
26779
- // same way: a resource's GET and GET_MANY. Anything else a route runs (a page's
26780
- // code, a computation) has nothing a later document could draw.
26817
+ // The root actions whose answer is rows of a store: a resource's GET and
26818
+ // GET_MANY. They are kept as rows, since their value (ids) names rows a new
26819
+ // document's store does not hold. Any other route action is kept by its value
26820
+ // (see page_kept.js).
26781
26821
  const keepableReadWeakSet = new WeakSet();
26782
26822
  const markKeepableRead = (rootAction) => {
26783
26823
  keepableReadWeakSet.add(rootAction);
@@ -39211,6 +39251,15 @@ const writeCover = (edge, element, size) => {
39211
39251
  // Every route action, with whether it asks something of the address at the
39212
39252
  // moment: its route matches and its params getter returns something.
39213
39253
  const routeActionEntriesSignal = signal([]);
39254
+ // What a route action is called in the page kept for the next document (see
39255
+ // keepPageOnScreen): its place among the route actions declared, which the
39256
+ // next document of the same build declares in the same order. A resource read
39257
+ // has a name of its own and is kept by it; an action made from a callback is
39258
+ // most often "anonymous", and several of them read the same page.
39259
+ const keptNameWeakMap = new WeakMap();
39260
+ const getRouteActionKeptName = (routeAction) => {
39261
+ return keptNameWeakMap.get(routeAction);
39262
+ };
39214
39263
  let activeRouteActionsPrevious = new Set();
39215
39264
  /**
39216
39265
  * The route actions asking something for the page on screen: their route
@@ -39295,7 +39344,16 @@ const routeAction = (
39295
39344
  }
39296
39345
  return params;
39297
39346
  };
39347
+ const rootAction = rootActionOf(action);
39348
+ const keptName = `route_action#${routeActionEntriesSignal.peek().length} ${rootAction.name}`;
39349
+ if (!isKeepableRead(rootAction)) {
39350
+ // Before the effect below: it may run the action at once.
39351
+ addKeptValueSource(rootAction, (params) =>
39352
+ takeKeptRead(getKeptReadKey(keptName, params)),
39353
+ );
39354
+ }
39298
39355
  const actionBoundToRoute = actionRunEffect(action, readParamsAsked, options);
39356
+ keptNameWeakMap.set(actionBoundToRoute, keptName);
39299
39357
  routeActionEntriesSignal.value = [
39300
39358
  ...routeActionEntriesSignal.peek(),
39301
39359
  {
@@ -39386,6 +39444,13 @@ const anyMatchingRouteSignal = (routes) => {
39386
39444
  return anyMatchingSignal;
39387
39445
  };
39388
39446
 
39447
+ const rootActionOf = (action) => {
39448
+ if (action.isProxy) {
39449
+ return rootActionOf(getActionPrivateProperties(action).currentAction);
39450
+ }
39451
+ return action.rootAction || action;
39452
+ };
39453
+
39389
39454
  const haveSameMembers = (setA, setB) => {
39390
39455
  if (setA.size !== setB.size) {
39391
39456
  return false;
@@ -39413,6 +39478,15 @@ const haveSameMembers = (setA, setB) => {
39413
39478
  * (shownRangeReadersSignal). A search typed on the page is neither, and is not
39414
39479
  * kept.
39415
39480
  *
39481
+ * What a read is made of on the way out depends on what it holds. A resource
39482
+ * read holds ids, which name rows a new document's store does not have: its
39483
+ * rows are written, and re-enter the store. Any other route action holds its
39484
+ * value, written as it is when JSON brings it back as it was — a page's code
39485
+ * (a function), a Date built by the callback, a Map, are not, and are left
39486
+ * out. Such an action is recognized in the next document by its place among
39487
+ * the route actions declared (see getRouteActionKeptName), not by a name the
39488
+ * app would have to give every action made from a callback.
39489
+ *
39416
39490
  * A read is written once it has answered in this document. Before that it
39417
39491
  * keeps what the previous document kept for it, never what it shows meanwhile:
39418
39492
  * an answer handed over from other params (a list standing in for the next
@@ -39439,17 +39513,19 @@ let stopKeepingCurrent = null;
39439
39513
  * draws it again while its reads go out, rather than skeletons.
39440
39514
  *
39441
39515
  * What the page reads is what navi runs for it: the route actions asking
39442
- * something for it (see `activeRouteActionsSignal`) whose action is a resource
39443
- * `GET` or `GET_MANY`, and the `GET_RANGE` compositions the `<List.Items>` on
39444
- * screen read, around the window they draw. A navigation replaces the slot with
39445
- * the next page's reads, and the page left is dropped. In the next document,
39446
- * the first run of the same read with the same params draws the kept answer as
39447
- * its provisional value: `data` set while `loading` is `true`, the answer
39448
- * replacing it. Under a network policy answering reads from the store, the
39449
- * kept answer answers the read. The copy follows the store: a `PUT` on a row
39450
- * rewrites it.
39451
- *
39452
- * Call it before the routes start: the first runs are the ones that look.
39516
+ * something for it (see `activeRouteActionsSignal`), and the `GET_RANGE`
39517
+ * compositions the `<List.Items>` on screen read, around the window they draw.
39518
+ * A resource read keeps its rows; any other route action keeps its value when
39519
+ * JSON brings it back as it was. A navigation replaces the slot with the next
39520
+ * page's reads, and the page left is dropped. In the next document, the first
39521
+ * run of the same read with the same params draws the kept answer as its
39522
+ * provisional value: `data` set while `loading` is `true`, the answer replacing
39523
+ * it. Under a network policy answering reads from the store, a resource read
39524
+ * completes with it. The copy follows the store: a `PUT` on a row rewrites it.
39525
+ *
39526
+ * Call it before the routes start: the first runs are the ones that look. And
39527
+ * declare the route actions where the app starts: one that is not a resource
39528
+ * read is recognized by its place among them.
39453
39529
  *
39454
39530
  * @param {object} options
39455
39531
  * @param {import("@preact/signals").Signal} options.signal - where the slot is
@@ -39572,7 +39648,7 @@ const keepPageOnScreen = ({
39572
39648
  });
39573
39649
 
39574
39650
  const actionReadWeakMap = new WeakMap();
39575
- const actionReadOf = (action) => {
39651
+ const actionReadOf = (action, keptName, readEntryOf) => {
39576
39652
  let read = actionReadWeakMap.get(action);
39577
39653
  if (read) {
39578
39654
  return read;
@@ -39586,16 +39662,10 @@ const keepPageOnScreen = ({
39586
39662
  answeredActionWeakSet.add(action);
39587
39663
  }
39588
39664
  return {
39589
- key: getKeptReadKey(action.name, action.params),
39665
+ key: getKeptReadKey(keptName, action.params),
39590
39666
  answered: answeredActionWeakSet.has(action),
39591
39667
  landed,
39592
- readEntry: () => {
39593
- const data = action.dataSignal.value;
39594
- if (data === undefined || data === null) {
39595
- return undefined;
39596
- }
39597
- return serializeData(data);
39598
- },
39668
+ readEntry: () => readEntryOf(action),
39599
39669
  };
39600
39670
  };
39601
39671
  actionReadWeakMap.set(action, read);
@@ -39634,10 +39704,20 @@ const keepPageOnScreen = ({
39634
39704
  // eslint-disable-next-line no-unused-expressions
39635
39705
  routeAction.callSource;
39636
39706
  const action = getActionPrivateProperties(routeAction).currentAction;
39637
- if (action.params === NO_PARAMS || !isKeepableRead(action)) {
39707
+ if (action.params === NO_PARAMS) {
39638
39708
  continue;
39639
39709
  }
39640
- reads.push(actionReadOf(action));
39710
+ if (isKeepableRead(action)) {
39711
+ reads.push(actionReadOf(action, action.name, readRows));
39712
+ } else {
39713
+ reads.push(
39714
+ actionReadOf(
39715
+ action,
39716
+ getRouteActionKeptName(routeAction),
39717
+ readPlainValue,
39718
+ ),
39719
+ );
39720
+ }
39641
39721
  }
39642
39722
  for (const reader of shownRangeReadersSignal.value.keys()) {
39643
39723
  reads.push(rangeReadOf(reader));
@@ -39723,6 +39803,57 @@ const keepPageOnScreen = ({
39723
39803
  return stop;
39724
39804
  };
39725
39805
 
39806
+ const readRows = (action) => {
39807
+ const data = action.dataSignal.value;
39808
+ if (data === undefined || data === null) {
39809
+ return undefined;
39810
+ }
39811
+ return serializeData(data);
39812
+ };
39813
+ const readPlainValue = (action) => {
39814
+ const value = action.valueSignal.value;
39815
+ if (value === undefined || !isPlainData(value, new Set())) {
39816
+ return undefined;
39817
+ }
39818
+ return value;
39819
+ };
39820
+ // What JSON writes and reads back as it was. `undefined` inside is accepted:
39821
+ // JSON drops the key, which reads the same.
39822
+ const isPlainData = (value, ancestorSet) => {
39823
+ if (value === null || value === undefined) {
39824
+ return true;
39825
+ }
39826
+ const type = typeof value;
39827
+ if (type === "string" || type === "boolean") {
39828
+ return true;
39829
+ }
39830
+ if (type === "number") {
39831
+ return Number.isFinite(value);
39832
+ }
39833
+ if (type !== "object") {
39834
+ return false;
39835
+ }
39836
+ if (ancestorSet.has(value)) {
39837
+ return false;
39838
+ }
39839
+ const isArray = Array.isArray(value);
39840
+ if (!isArray) {
39841
+ const proto = Object.getPrototypeOf(value);
39842
+ if (proto !== Object.prototype && proto !== null) {
39843
+ return false;
39844
+ }
39845
+ }
39846
+ ancestorSet.add(value);
39847
+ const entries = isArray ? value : Object.values(value);
39848
+ for (const entry of entries) {
39849
+ if (!isPlainData(entry, ancestorSet)) {
39850
+ return false;
39851
+ }
39852
+ }
39853
+ ancestorSet.delete(value);
39854
+ return true;
39855
+ };
39856
+
39726
39857
  /**
39727
39858
  * A popup that is the anchor, brought to the front.
39728
39859
  *