@askrjs/askr 0.3.0 → 0.4.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.
Files changed (123) hide show
  1. package/CHANGELOG.md +874 -0
  2. package/README.md +61 -29
  3. package/capabilities.json +19 -9
  4. package/dist/actions/index.js +56 -12
  5. package/dist/benchmark.js +10 -15
  6. package/dist/boot/index.d.ts +3 -2
  7. package/dist/boot/index.js +1 -1
  8. package/dist/boot-DaoTYoCw.js +3989 -0
  9. package/dist/browser-activity-CB8e-V4f.js +220 -0
  10. package/dist/components/index.d.ts +8 -1
  11. package/dist/components/index.js +1 -43
  12. package/dist/{aria-hKjThHcp.js → compose-ref-DWzdbxsD.js} +41 -4
  13. package/dist/control/index.d.ts +2 -0
  14. package/dist/control/index.js +1 -1
  15. package/dist/control-nfpT2fbK.js +209 -0
  16. package/dist/core.d.ts +5 -9
  17. package/dist/csp-nonce-DwGw1A6Q.js +35 -0
  18. package/dist/data/index.js +3 -3
  19. package/dist/{data-2edW0jhF.js → data-BCwClJiF.js} +283 -94
  20. package/dist/data-runtime-C1bFAlba.js +242 -0
  21. package/dist/{deferred-CbgNhf67.js → deferred-bEEHHvic.js} +42 -20
  22. package/dist/domains/actions.d.ts +8 -3
  23. package/dist/domains/component.d.ts +1 -107
  24. package/dist/domains/context.d.ts +0 -15
  25. package/dist/domains/control.d.ts +36 -249
  26. package/dist/domains/data.d.ts +31 -13
  27. package/dist/domains/diagnostics.d.ts +1 -2
  28. package/dist/domains/lifecycle.d.ts +2 -5
  29. package/dist/domains/refs.d.ts +0 -2
  30. package/dist/domains/routing.d.ts +43 -8
  31. package/dist/domains/scheduler.d.ts +1 -76
  32. package/dist/domains/server.d.ts +5 -2
  33. package/dist/domains/state.d.ts +2 -5
  34. package/dist/domains/telemetry.d.ts +0 -2
  35. package/dist/elements.d.ts +214 -51
  36. package/dist/error-boundary-Bpwofwps.js +293 -0
  37. package/dist/foundations/icon/index.d.ts +0 -58
  38. package/dist/foundations/icon/index.js +2 -2
  39. package/dist/foundations/index.js +2 -1
  40. package/dist/foundations/interactions/index.js +1 -1
  41. package/dist/foundations/state/index.d.ts +3 -3
  42. package/dist/foundations/state/index.js +5 -3
  43. package/dist/foundations/structures/index.js +2 -1
  44. package/dist/foundations/utilities/index.d.ts +2 -0
  45. package/dist/foundations/utilities/index.js +1 -2
  46. package/dist/fx/index.d.ts +31 -58
  47. package/dist/fx/index.js +313 -195
  48. package/dist/hooks-BimKStr4.js +202 -0
  49. package/dist/index.d.ts +11 -74
  50. package/dist/index.js +8 -447
  51. package/dist/instance-BAnfmXOR.js +988 -0
  52. package/dist/{interactions-CTeACdT_.js → interactions-D2BoREb-.js} +3 -2
  53. package/dist/jsx-dev-runtime.d.ts +41 -18
  54. package/dist/jsx-dev-runtime.js +1 -6
  55. package/dist/{jsx-runtime-CPJXAOBF.js → jsx-runtime-C4idqHY0.js} +18 -7
  56. package/dist/jsx-runtime.js +1 -1
  57. package/dist/jsx.d.ts +61 -37
  58. package/dist/{manifest-D1P3UNWV.js → manifest-CW9gyNBY.js} +3 -3
  59. package/dist/{navigate-prC5V8XV.js → navigate-CP_0Zy5U.js} +454 -87
  60. package/dist/navigation.d.ts +12 -3
  61. package/dist/peer-types.d.ts +8 -0
  62. package/dist/portal-DuOKfnLa.js +349 -0
  63. package/dist/promise-Dz8iIHAm.js +16 -0
  64. package/dist/query-registry-WMCN0BJL.js +320 -0
  65. package/dist/readable-DFWOsTac.js +21 -0
  66. package/dist/{render-context-B7ohlh6t.js → render-context-DGHnEveC.js} +27 -11
  67. package/dist/render-sync-IC5lMFpq.js +1240 -0
  68. package/dist/resolution-Cx82-xjW.js +1244 -0
  69. package/dist/resource-CLjB7RBQ.js +377 -0
  70. package/dist/resources/index.d.ts +21 -11
  71. package/dist/resources/index.js +45 -244
  72. package/dist/router/index.d.ts +14 -5
  73. package/dist/router/index.js +22 -53
  74. package/dist/scope-CwpVSHVf.js +55 -0
  75. package/dist/{shared-kU8yvryp.js → shared-O2ZnmYAf.js} +1 -7
  76. package/dist/{snapshot-source-An4iKajG.js → snapshot-Cjx4N988.js} +6 -1
  77. package/dist/ssg/index.d.ts +1 -2
  78. package/dist/ssg/index.js +5 -13
  79. package/dist/ssr/index.d.ts +26 -28
  80. package/dist/ssr/index.js +4 -4
  81. package/dist/{ssr-CiNm6Vzl.js → ssr-BJ8ug7zH.js} +133 -122
  82. package/dist/ssr-DoV-vJ-5.js +229 -0
  83. package/dist/state-DWO1KzkX.js +232 -0
  84. package/dist/structures-D4dDMqHX.js +231 -0
  85. package/dist/testing/index.d.ts +8 -4
  86. package/dist/testing/index.js +29 -34
  87. package/dist/url-CnbfpX0K.js +185 -0
  88. package/dist/utilities.d.ts +48 -2
  89. package/dist/verify-hydration-DpVaDnqz.js +152 -0
  90. package/package.json +19 -9
  91. package/dist/access-CEA2pOry.js +0 -1046
  92. package/dist/activity-oy2YFTFF.js +0 -201
  93. package/dist/boot-BMBMWUGI.js +0 -1084
  94. package/dist/capabilities-Bbf0mlp9.js +0 -42
  95. package/dist/compose-ref-v0qGlHoN.js +0 -26
  96. package/dist/control-BR_mrX__.js +0 -173
  97. package/dist/control-C-ApnFs1.js +0 -20
  98. package/dist/csp-nonce-BfFWcd0w.js +0 -173
  99. package/dist/data-runtime-CxoY98lX.js +0 -149
  100. package/dist/default-portal-runtime-DvMvWJVp.js +0 -20
  101. package/dist/dom-renderer.d.ts +0 -77
  102. package/dist/domains/renderer.d.ts +0 -127
  103. package/dist/eager-control.d.ts +0 -8
  104. package/dist/effect-C3Zsc1k6.js +0 -292
  105. package/dist/for-state-BDykeOJL.js +0 -1753
  106. package/dist/instance-VAFYVJOa.js +0 -1027
  107. package/dist/notify-DxxuDPeY.js +0 -266
  108. package/dist/portal-w3oQ6oQU.js +0 -29
  109. package/dist/query-registry-LrYpQsDJ.js +0 -83
  110. package/dist/render-resolved-BC0W7_5b.js +0 -1123
  111. package/dist/renderer-CT0HeY58.js +0 -8623
  112. package/dist/resolution-3DskUkjG.js +0 -577
  113. package/dist/resource-18-sFRl9.js +0 -344
  114. package/dist/route-matching-CUzZ31gT.js +0 -551
  115. package/dist/router-internal-7nuNEHfG.js +0 -36
  116. package/dist/scope-Cq_iYbTR.js +0 -403
  117. package/dist/selector-BSPVkjKP.js +0 -311
  118. package/dist/ssr-EPjwJH5R.js +0 -29
  119. package/dist/state-BXJfudq1.js +0 -91
  120. package/dist/structures-CjEzfj4h.js +0 -654
  121. package/dist/timer-C7uqnJVt.js +0 -85
  122. package/dist/verify-hydration-ikaxotf7.js +0 -27
  123. package/dist/vnode-D24p6IW2.js +0 -7
package/README.md CHANGED
@@ -1,24 +1,46 @@
1
1
  # Askr
2
2
 
3
- [![CI](https://github.com/askrjs/askr/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/askrjs/askr/actions/workflows/ci.yml)
3
+ [![CI](https://github.com/askrjs/askr/actions/workflows/ci.yml/badge.svg?branch=develop)](https://github.com/askrjs/askr/actions/workflows/ci.yml)
4
4
  [![npm version](https://img.shields.io/npm/v/%40askrjs%2Faskr.svg)](https://www.npmjs.com/package/@askrjs/askr)
5
5
 
6
- Askr is a lane-scheduled UI runtime for TypeScript applications. Its
7
- single-threaded scheduler serializes queued framework work; it is not a
8
- message-passing actor system. Askr provides explicit reactivity, routed
9
- application startup, server-side rendering, and static-site generation
10
- entrypoints.
6
+ A TypeScript UI runtime with getter-based state, fine-grained bindings,
7
+ transactional commits with rollback, and one route graph for SPA, SSR, and SSG.
8
+
9
+ ## How Askr works
10
+
11
+ - **Getter-based state with automatic tracking.** `state()` gives you a getter
12
+ and a setter. Calling the getter while a component renders subscribes that
13
+ component; you do not list dependencies for state or `derive()`.
14
+ - **Component re-render plus fine-grained bindings.** A state change re-runs
15
+ the components that read it. A function child or prop, such as
16
+ `{() => count()}`, is a binding: it updates its own DOM node without
17
+ re-running the component around it.
18
+ - **Transactional commits with rollback.** Each render is prepared and applied
19
+ as one transaction. If a component throws or the DOM commit fails, Askr
20
+ restores the last committed DOM, and the listeners, refs, resources, and
21
+ subscriptions created by the failed render never become live. The error goes
22
+ to the nearest `ErrorBoundary`, or is rethrown when no boundary catches it.
23
+ Bindings keep showing current state across a rollback.
24
+ - **One route graph.** The registry from `createRouteRegistry()` drives browser
25
+ navigation (`createSPA`), hydration (`hydrateSPA`), server rendering
26
+ (`renderRouteRequest`), and static generation (`createStaticGen`).
27
+
28
+ Writes made in one event handler are batched into one scheduled render, and
29
+ queued work runs in a fixed order. See [Update ordering](docs/concepts/determinism.md)
30
+ for exactly what Askr guarantees and what it does not.
11
31
 
12
32
  ## Quick Start
13
33
 
14
- ```ts
34
+ ```tsx
15
35
  import { state } from '@askrjs/askr';
16
36
  import { createIsland } from '@askrjs/askr/boot';
17
37
 
18
38
  function Counter() {
19
39
  const [count, setCount] = state(0);
20
40
 
21
- return <button onClick={() => setCount((value) => value + 1)}>{count()}</button>;
41
+ return (
42
+ <button onClick={() => setCount((value) => value + 1)}>{count()}</button>
43
+ );
22
44
  }
23
45
 
24
46
  createIsland({ root: document.body, component: Counter });
@@ -35,23 +57,34 @@ The tuple form above and the direct callable form below are equivalent; see
35
57
  `selector()`, `defineScope()`, `readScope()`, `getSignal()`, and the JSX
36
58
  runtime exports.
37
59
 
38
- Public APIs prefer functions and closures over classes. Lexical ownership uses
39
- `defineScope()` and `readScope()`; there are no compatibility aliases for the
40
- clean-break vocabulary.
60
+ Public APIs are mostly functions and closures. Error classes such as
61
+ `RouteDataLoadError` and `SSRDataMissingError` live on their subpaths.
62
+ Lexical ownership uses `defineScope()` and `readScope()`;
63
+ there are no compatibility aliases for the clean-break vocabulary.
41
64
 
42
65
  App startup, routing, async resources, data helpers, and error boundaries live
43
66
  on their own subpaths.
44
67
 
45
- ### Explicit reactivity
68
+ ### Getter-based state
46
69
 
47
70
  State is read through getter functions and updated through setter functions.
71
+ Reads are explicit calls, but dependency tracking is automatic: a component
72
+ re-renders when state it read during its last committed render changes.
73
+
74
+ `state()`, `derive()`, `selector()`, `resource()`, and the other render-scoped
75
+ primitives are hooks in the React sense. Each call is matched to its slot by
76
+ call position, so call them unconditionally and in the same order on every
77
+ render; the runtime throws when the order changes (see
78
+ [Runtime Enforcement](docs/concepts/runtime-enforcement.md)). `resource()`
79
+ takes a dependency array that decides when its loader re-runs.
48
80
 
49
81
  #### State usage forms
50
82
 
51
83
  `state()` returns one callable state cell that is also iterable. Destructuring
52
- it gives the cell as the getter and its `.set()` method as the setter:
84
+ it gives the cell as the getter and its `.set()` method as the setter. Call
85
+ `state()` inside a component function:
53
86
 
54
- ```ts
87
+ ```tsx
55
88
  const [count, setCount] = state(0);
56
89
  console.log(count());
57
90
  setCount(1);
@@ -94,27 +127,26 @@ createSPA({
94
127
 
95
128
  `resource()` manages async work with cancellation support.
96
129
 
97
- ```ts
130
+ ```tsx
98
131
  import { resource } from '@askrjs/askr/resources';
99
132
 
100
133
  function Data({ id }: { id: string }) {
101
- const data = resource(async ({ signal }) => {
102
- const response = await fetch(`/api/${id}`, { signal });
103
- return response.json();
104
- }, [id]);
134
+ const data = resource(
135
+ async ({ signal }) => {
136
+ const response = await fetch(`/api/${id}`, { signal });
137
+ return response.json();
138
+ },
139
+ [id]
140
+ );
105
141
 
106
142
  if (data.pending) return <div>Loading...</div>;
107
143
  if (data.error) return <div>Failed to load</div>;
108
- return <div>{data.value.name}</div>;
144
+ return <div>{data.value?.name}</div>;
109
145
  }
110
146
  ```
111
147
 
112
- `@askrjs/askr/data` is the canonical entrypoint for all query, mutation,
113
- invalidation, and data-runtime helpers. The root package retains query creation
114
- and collection, definition and serving, prefetch, and hydration exports for
115
- compatibility with existing applications. Mutation, invalidation, and
116
- data-runtime control remain subpath-only. New code should import the whole data surface from
117
- `@askrjs/askr/data` rather than split related imports across entrypoints.
148
+ `@askrjs/askr/data` is the only entrypoint for query, mutation, invalidation,
149
+ and data-runtime helpers; the root package does not re-export them.
118
150
 
119
151
  ### Developer error boundaries
120
152
 
@@ -125,7 +157,7 @@ a `resetKey` tied to your app state. The boundary protects both initial mount
125
157
  and scheduled post-mount updates. Portal content follows its logical writer
126
158
  boundary and can also recover through a boundary around its host.
127
159
 
128
- ```ts
160
+ ```tsx
129
161
  import { ErrorBoundary } from '@askrjs/askr/components';
130
162
 
131
163
  function App() {
@@ -139,7 +171,7 @@ function App() {
139
171
 
140
172
  ## Documentation
141
173
 
142
- - [Documentation Index](docs/index.md)
174
+ - [Documentation Index](docs/README.md)
143
175
  - [Installation](docs/getting-started/installation.md)
144
176
  - [Quick Start](docs/getting-started/quick-start.md)
145
177
  - [State Guide](docs/guides/state.md)
@@ -147,7 +179,7 @@ function App() {
147
179
  - [Resources Guide](docs/guides/resources.md)
148
180
  - [SSG Guide](docs/guides/ssg.md)
149
181
  - [Runtime Enforcement](docs/concepts/runtime-enforcement.md)
150
- - [Determinism](docs/concepts/determinism.md)
182
+ - [Update Ordering](docs/concepts/determinism.md)
151
183
  - [API Reference](docs/reference/api.md)
152
184
 
153
185
  ## Release Notes
package/capabilities.json CHANGED
@@ -6,12 +6,22 @@
6
6
  "intent": "reactive state and derived views",
7
7
  "package": "@askrjs/askr",
8
8
  "import": "@askrjs/askr",
9
- "exports": ["state", "derive", "selector", "For"],
9
+ "exports": ["state", "derive", "selector"],
10
10
  "constraints": ["component render scope", "stable call order"],
11
- "stability": "stable",
11
+ "stability": "preview",
12
12
  "docs": "https://github.com/askrjs/askr/blob/main/docs/guides/state.md",
13
13
  "docsPath": "docs/guides/state.md"
14
14
  },
15
+ {
16
+ "intent": "declarative control flow",
17
+ "package": "@askrjs/askr",
18
+ "import": "@askrjs/askr/control",
19
+ "exports": ["For", "Show", "Case", "Match"],
20
+ "constraints": ["control boundaries keep their rendered ranges"],
21
+ "stability": "preview",
22
+ "docs": "https://github.com/askrjs/askr/blob/main/docs/guides/control-flow.md",
23
+ "docsPath": "docs/guides/control-flow.md"
24
+ },
15
25
  {
16
26
  "intent": "data queries and mutations",
17
27
  "package": "@askrjs/askr",
@@ -28,7 +38,7 @@
28
38
  "fetches are cancellable",
29
39
  "query collections require component render scope"
30
40
  ],
31
- "stability": "stable",
41
+ "stability": "preview",
32
42
  "docs": "https://github.com/askrjs/askr/blob/main/docs/core/data.md",
33
43
  "docsPath": "docs/core/data.md"
34
44
  },
@@ -40,7 +50,7 @@
40
50
  "constraints": [
41
51
  "fixtures are test-only and do not perform network requests"
42
52
  ],
43
- "stability": "stable",
53
+ "stability": "preview",
44
54
  "docs": "https://github.com/askrjs/askr/blob/main/docs/contributing/testing.md",
45
55
  "docsPath": "docs/contributing/testing.md"
46
56
  },
@@ -50,7 +60,7 @@
50
60
  "import": "@askrjs/askr/router",
51
61
  "exports": ["route", "Link", "navigate", "createRouteRegistry"],
52
62
  "constraints": ["route registry owns navigation"],
53
- "stability": "stable",
63
+ "stability": "preview",
54
64
  "docs": "https://github.com/askrjs/askr/blob/main/docs/guides/router.md",
55
65
  "docsPath": "docs/guides/router.md"
56
66
  },
@@ -60,7 +70,7 @@
60
70
  "import": "@askrjs/askr/ssr",
61
71
  "exports": ["renderToString"],
62
72
  "constraints": ["browser globals require lazy client targets"],
63
- "stability": "stable",
73
+ "stability": "preview",
64
74
  "docs": "https://github.com/askrjs/askr/blob/main/docs/guides/ssr.md",
65
75
  "docsPath": "docs/guides/ssr.md"
66
76
  },
@@ -70,7 +80,7 @@
70
80
  "import": "@askrjs/askr/ssg",
71
81
  "exports": ["createStaticGen"],
72
82
  "constraints": ["routes must be deterministic"],
73
- "stability": "stable",
83
+ "stability": "preview",
74
84
  "docs": "https://github.com/askrjs/askr/blob/main/docs/guides/ssg.md",
75
85
  "docsPath": "docs/guides/ssg.md"
76
86
  },
@@ -80,7 +90,7 @@
80
90
  "import": "@askrjs/askr/components",
81
91
  "exports": ["ErrorBoundary"],
82
92
  "constraints": ["portal hosts are finalized during render"],
83
- "stability": "stable",
93
+ "stability": "preview",
84
94
  "docs": "https://github.com/askrjs/askr/blob/main/docs/reference/api.md",
85
95
  "docsPath": "docs/reference/api.md"
86
96
  },
@@ -90,7 +100,7 @@
90
100
  "import": "@askrjs/askr/foundations/structures",
91
101
  "exports": ["Portal", "DefaultPortal"],
92
102
  "constraints": ["portal hosts are finalized during render"],
93
- "stability": "stable",
103
+ "stability": "preview",
94
104
  "docs": "https://github.com/askrjs/askr/blob/main/docs/reference/api.md",
95
105
  "docsPath": "docs/reference/api.md"
96
106
  }
@@ -1,14 +1,15 @@
1
- import { d as getCurrentAppRenderRuntime } from "../scope-Cq_iYbTR.js";
2
- import { r as getCurrentRenderData } from "../render-context-B7ohlh6t.js";
3
- import { t as state } from "../state-BXJfudq1.js";
4
- import { r as jsxs, t as jsx } from "../jsx-runtime-CPJXAOBF.js";
5
- import { c as resolveDataRuntimeState, s as invalidateQueriesForRuntime } from "../data-runtime-CxoY98lX.js";
6
- import { r as isCurrentAsyncGeneration } from "../shared-kU8yvryp.js";
1
+ import { a as state } from "../state-DWO1KzkX.js";
2
+ import { r as currentAppRuntime } from "../hooks-BimKStr4.js";
3
+ import { r as getCurrentRenderData } from "../render-context-DGHnEveC.js";
4
+ import { i as jsxs, n as jsx } from "../jsx-runtime-C4idqHY0.js";
5
+ import { c as invalidateQueriesForRuntime, u as resolveDataRuntimeState } from "../data-runtime-C1bFAlba.js";
6
+ import { c as resolveNavigationUrl } from "../url-CnbfpX0K.js";
7
+ import { n as isCurrentAsyncGeneration } from "../shared-O2ZnmYAf.js";
7
8
  //#region src/actions/runtime.ts
8
9
  const emptyFramework = Object.freeze({});
9
10
  /** @internal Read Askr-owned state from the current application root. */
10
11
  function readActionFramework() {
11
- return getCurrentAppRenderRuntime()?.framework ?? emptyFramework;
12
+ return currentAppRuntime()?.framework ?? emptyFramework;
12
13
  }
13
14
  //#endregion
14
15
  //#region src/actions/index.tsx
@@ -62,13 +63,46 @@ function initialStatus(actionId) {
62
63
  error: replay
63
64
  } : { pending: false };
64
65
  }
66
+ /**
67
+ * Validate an action redirect and return its absolute URL. Path-like targets
68
+ * that leave the origin, including dot segments that collapse to `//host`,
69
+ * are refused by {@link resolveNavigationUrl}; explicit URLs on another origin
70
+ * are refused here. The absolute form keeps `location.assign()` on the origin.
71
+ */
65
72
  function normalizeActionRedirect(value) {
66
73
  const current = new URL(location.href);
67
- const target = new URL(value, current);
74
+ const target = resolveNavigationUrl(value, current.href);
68
75
  if (target.protocol !== "http:" && target.protocol !== "https:" || target.origin !== current.origin) throw new TypeError("Action redirects must stay on the current origin.");
69
- return `${target.pathname}${target.search}${target.hash}`;
76
+ return target.href;
70
77
  }
71
- /** Returns a command handle, rather than a hook. */
78
+ /**
79
+ * The `Error` an action submission rejects with for a failed or unreadable
80
+ * response. The message always carries the HTTP status, plus a string
81
+ * error, or the `detail`, `message`, or `title` of an error object or RFC 7807
82
+ * problem body. The server value, or the body parse failure, is kept as
83
+ * `cause`.
84
+ */
85
+ function actionFailure(status, cause) {
86
+ let detail = typeof cause === "string" ? cause : void 0;
87
+ if (cause && typeof cause === "object" && !(cause instanceof Error)) {
88
+ const fields = cause;
89
+ detail = [
90
+ fields.detail,
91
+ fields.message,
92
+ fields.title
93
+ ].find((field) => typeof field === "string");
94
+ }
95
+ const error = /* @__PURE__ */ new Error(detail ? `Action failed (${status}): ${detail}` : `Action failed (${status}).`);
96
+ if (cause !== void 0) error.cause = cause;
97
+ return error;
98
+ }
99
+ /**
100
+ * Bind an action descriptor to the calling component and return its
101
+ * `{ state, submit }` handle. `action()` allocates its status with `state()`,
102
+ * so it follows the same rule: call it during component render, at the top
103
+ * level and in the same order on every render. `submit()` itself may be
104
+ * called from event handlers or other async work.
105
+ */
72
106
  function action(descriptor) {
73
107
  const token = csrfToken();
74
108
  const runtime = resolveDataRuntimeState();
@@ -94,8 +128,18 @@ function action(descriptor) {
94
128
  },
95
129
  body: JSON.stringify(input)
96
130
  });
97
- const envelope = await response.json();
98
- if (!response.ok) throw envelope.error ?? validationError(envelope, descriptor.id) ?? /* @__PURE__ */ new Error(`Action failed (${response.status}).`);
131
+ let parsed;
132
+ try {
133
+ const body = await response.text();
134
+ parsed = response.ok && body === "" ? void 0 : JSON.parse(body);
135
+ } catch (cause) {
136
+ throw actionFailure(response.status, cause);
137
+ }
138
+ const envelope = parsed ?? {};
139
+ if (!response.ok) {
140
+ if (envelope.error != null) throw actionFailure(response.status, envelope.error);
141
+ throw validationError(envelope, descriptor.id) ?? actionFailure(response.status, parsed ?? void 0);
142
+ }
99
143
  const redirect = typeof envelope.redirect === "string" ? normalizeActionRedirect(envelope.redirect) : void 0;
100
144
  const invalidates = Array.isArray(envelope.invalidates) ? envelope.invalidates.filter((prefix) => typeof prefix === "string") : descriptor.invalidates;
101
145
  for (const prefix of invalidates) invalidateQueriesForRuntime(runtime, prefix, true);
package/dist/benchmark.js CHANGED
@@ -1,9 +1,8 @@
1
- import { r as flushRuntimeScheduler } from "./access-CEA2pOry.js";
2
- import { t as state } from "./state-BXJfudq1.js";
3
- import { t as selector } from "./selector-BSPVkjKP.js";
4
- import { i as For } from "./control-BR_mrX__.js";
5
- import { r as jsxs, t as jsx } from "./jsx-runtime-CPJXAOBF.js";
6
- import { r as createIsland, s as installRendererBridge } from "./boot-BMBMWUGI.js";
1
+ import { y as flushSync } from "./instance-BAnfmXOR.js";
2
+ import { a as state, i as selector } from "./state-DWO1KzkX.js";
3
+ import { i as jsxs, n as jsx } from "./jsx-runtime-C4idqHY0.js";
4
+ import { r as createIsland } from "./boot-DaoTYoCw.js";
5
+ import { n as For } from "./control-nfpT2fbK.js";
7
6
  //#region src/bench/components/benchmark-row.tsx
8
7
  function BenchmarkRow({ item, isSelected, onSelect, onRemove }) {
9
8
  return /* @__PURE__ */ jsxs("tr", {
@@ -57,13 +56,10 @@ function BenchmarkTable({ rows, isSelected, onSelect, onRemove }) {
57
56
  }) })
58
57
  });
59
58
  }
60
- //#endregion
61
- //#region src/bench/benchmark-entry.tsx
62
- installRendererBridge();
63
59
  const benchmarkMetadata = {
64
60
  packageName: "@askrjs/askr",
65
- packageVersion: "0.3.0",
66
- buildLabel: "0.3.0-local"
61
+ packageVersion: "0.4.0",
62
+ buildLabel: "0.4.0-local"
67
63
  };
68
64
  function getBenchmarkMetadata() {
69
65
  return { ...benchmarkMetadata };
@@ -75,7 +71,6 @@ function mountBenchmark(root, initialRows) {
75
71
  const App = () => {
76
72
  dataState = state(initialRowsTyped);
77
73
  selectedState = state(null);
78
- dataState._hasBeenRead = true;
79
74
  const isSelected = selector(selectedState);
80
75
  const select = (id) => selectedState.set(id);
81
76
  const remove = (id) => {
@@ -96,15 +91,15 @@ function mountBenchmark(root, initialRows) {
96
91
  root,
97
92
  component: App
98
93
  });
99
- flushRuntimeScheduler();
94
+ flushSync();
100
95
  return {
101
96
  setRows(rows) {
102
97
  dataState.set(rows);
103
- flushRuntimeScheduler();
98
+ flushSync();
104
99
  },
105
100
  setSelected(id) {
106
101
  selectedState.set(id);
107
- flushRuntimeScheduler();
102
+ flushSync();
108
103
  },
109
104
  cleanup() {}
110
105
  };
@@ -21,6 +21,7 @@ type IslandsConfig = {
21
21
  cspNonce?: string;
22
22
  };
23
23
  type BootRouteSource = {
24
+ /** Pass a route registry built via `createRouteRegistry()`; hydration uses the one the server rendered with. */
24
25
  registry: RouteRegistry;
25
26
  };
26
27
  /** Configuration for {@link createSPA}. */
@@ -29,7 +30,7 @@ type SPAConfig = BootRouteSource & {
29
30
  cspNonce?: string;
30
31
  /** Optional data runtime, primarily for routed test fixtures. */
31
32
  dataRuntime?: DataRuntime;
32
- /** Pass a route registry built via `createRouteRegistry(() => { ... })`. */
33
+ /** Auth resolution and redirect options; defaults to the registry's `auth`. */
33
34
  auth?: RouteAuthOptions;
34
35
  scrollRestoration?: boolean | ScrollRestorationOptions;
35
36
  cleanupStrict?: boolean;
@@ -40,7 +41,7 @@ type HydrateSPAConfig = BootRouteSource & {
40
41
  root: Element | string;
41
42
  cspNonce?: string;
42
43
  dataRuntime?: DataRuntime;
43
- /** Pass the same explicit route registry used for the server render. */
44
+ /** Auth resolution and redirect options; defaults to the registry's `auth`. */
44
45
  auth?: RouteAuthOptions;
45
46
  scrollRestoration?: boolean | ScrollRestorationOptions;
46
47
  cleanupStrict?: boolean;
@@ -1,2 +1,2 @@
1
- import { a as cleanupApp, i as createIslands, n as createSPA, o as hasApp, r as createIsland, t as hydrateSPA } from "../boot-BMBMWUGI.js";
1
+ import { a as cleanupApp, i as createIslands, n as createSPA, o as hasApp, r as createIsland, t as hydrateSPA } from "../boot-DaoTYoCw.js";
2
2
  export { cleanupApp, createIsland, createIslands, createSPA, hasApp, hydrateSPA };