@owlmeans/client 0.1.18-rc.4 → 0.1.18-rc.41

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 (88) hide show
  1. package/README.md +231 -54
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/client/SKILL.md +282 -19
  4. package/build/consts.d.ts +8 -0
  5. package/build/consts.d.ts.map +1 -1
  6. package/build/consts.js +8 -0
  7. package/build/consts.js.map +1 -1
  8. package/build/context.d.ts.map +1 -1
  9. package/build/context.js +1 -3
  10. package/build/context.js.map +1 -1
  11. package/build/entrypoint.d.ts +3 -0
  12. package/build/entrypoint.d.ts.map +1 -0
  13. package/build/entrypoint.js +4 -0
  14. package/build/entrypoint.js.map +1 -0
  15. package/build/helper.js +3 -3
  16. package/build/helper.js.map +1 -1
  17. package/build/index.d.ts +3 -1
  18. package/build/index.d.ts.map +1 -1
  19. package/build/index.js +3 -1
  20. package/build/index.js.map +1 -1
  21. package/build/lazy-retry.d.ts +47 -0
  22. package/build/lazy-retry.d.ts.map +1 -0
  23. package/build/lazy-retry.js +128 -0
  24. package/build/lazy-retry.js.map +1 -0
  25. package/build/lazy.d.ts +32 -0
  26. package/build/lazy.d.ts.map +1 -0
  27. package/build/lazy.js +114 -0
  28. package/build/lazy.js.map +1 -0
  29. package/build/navigate.d.ts.map +1 -1
  30. package/build/navigate.js +15 -18
  31. package/build/navigate.js.map +1 -1
  32. package/build/router.d.ts.map +1 -1
  33. package/build/router.js +12 -6
  34. package/build/router.js.map +1 -1
  35. package/build/services/debug.js.map +1 -1
  36. package/build/store.d.ts +19 -3
  37. package/build/store.d.ts.map +1 -1
  38. package/build/store.js +57 -50
  39. package/build/store.js.map +1 -1
  40. package/build/types.d.ts +85 -7
  41. package/build/types.d.ts.map +1 -1
  42. package/build/utils/entrypoint.d.ts +3 -0
  43. package/build/utils/entrypoint.d.ts.map +1 -0
  44. package/build/utils/{module.js → entrypoint.js} +2 -2
  45. package/build/utils/entrypoint.js.map +1 -0
  46. package/build/utils/index.d.ts +1 -1
  47. package/build/utils/index.d.ts.map +1 -1
  48. package/build/utils/index.js +1 -1
  49. package/build/utils/index.js.map +1 -1
  50. package/build/utils/route.d.ts.map +1 -1
  51. package/build/utils/route.js +12 -6
  52. package/build/utils/route.js.map +1 -1
  53. package/build/utils/router.d.ts +4 -4
  54. package/build/utils/router.d.ts.map +1 -1
  55. package/build/utils/router.js +8 -8
  56. package/build/utils/router.js.map +1 -1
  57. package/package.json +21 -14
  58. package/src/consts.ts +10 -0
  59. package/src/context.ts +1 -4
  60. package/src/entrypoint.ts +6 -0
  61. package/src/helper.tsx +6 -6
  62. package/src/index.ts +3 -1
  63. package/src/lazy-retry.ts +141 -0
  64. package/src/lazy.tsx +161 -0
  65. package/src/navigate.ts +16 -19
  66. package/src/router.ts +14 -10
  67. package/src/services/debug.ts +2 -2
  68. package/src/store.ts +71 -54
  69. package/src/types.ts +95 -7
  70. package/src/utils/{module.ts → entrypoint.ts} +2 -2
  71. package/src/utils/index.ts +1 -1
  72. package/src/utils/route.tsx +15 -8
  73. package/src/utils/router.ts +11 -11
  74. package/tests/context.ts +33 -0
  75. package/tests/harness/index.html +11 -0
  76. package/tests/harness/mount.tsx +86 -0
  77. package/tests/harness/piece.tsx +4 -0
  78. package/tests/lazy-retry.spec.ts +199 -0
  79. package/tests/lazy.spec.ts +145 -0
  80. package/tsconfig.json +1 -1
  81. package/build/module.d.ts +0 -3
  82. package/build/module.d.ts.map +0 -1
  83. package/build/module.js +0 -4
  84. package/build/module.js.map +0 -1
  85. package/build/utils/module.d.ts +0 -3
  86. package/build/utils/module.d.ts.map +0 -1
  87. package/build/utils/module.js.map +0 -1
  88. package/src/module.ts +0 -6
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: client
3
- description: How to use @owlmeans/client — platform-agnostic React client framework (works with web and React Native) providing context, components, services, navigate, store. Auto-invoked when importing client framework primitives.
3
+ description: How to use @owlmeans/client — the platform-agnostic React client framework (web and native) — makeClientContext, App/Router, useNavigate/Navigator, useEntrypoint/RoutedComponent, useStoreModel/useStoreList, useValue, lazyComponent/lazyHandler code-splitting and chunk-failure recovery (retryImport, isChunkLoadError, recoverFromChunkError), the modal and debug services. Auto-invoked when importing client framework primitives, navigating between screens, or reading client state from React.
4
4
  user-invocable: false
5
5
  ---
6
6
  <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
@@ -8,37 +8,300 @@ user-invocable: false
8
8
  # @owlmeans/client
9
9
 
10
10
  **Layer:** Client
11
- **Install:** `"@owlmeans/client": "^0.1.18-rc.0"` in `dependencies`
11
+ **Install:** `"@owlmeans/client": "^0.1.18-rc.41"` in `dependencies`
12
+
13
+ The React substrate `@owlmeans/web-client` (browser) and the native equivalent are built on. A
14
+ cross-platform package imports from here; an application normally imports from the platform
15
+ package, which re-exports what it needs — **except the hooks below, which are only here**.
16
+
17
+ ## What lives where
18
+
19
+ | Import from `@owlmeans/client` | Import from `@owlmeans/web-client` |
20
+ |---|---|
21
+ | `useNavigate`, `useEntrypoint`, `useStoreModel`, `useStoreList`, `useValue`, `useToggle`, `useSetupModalNavigator` — none of these are re-exported | `renderApp`, `makeContext`, `useAuthenticated`, and protocol binding helpers from `@owlmeans/client-entrypoint` |
22
+ | `RoutedComponent`, `EntrypointContextParams`, `Navigator`, `NavRequest`, `ClientContext` | `AppConfig`, `AppContext` |
23
+ | `App`, `Router`, `makeClientContext` — the platform-agnostic mounts | `WebApp`, `renderApp` — the browser mounts that wrap them |
12
24
 
13
25
  ## Key Exports
14
26
 
15
27
  | Export | Description |
16
28
  |--------|-------------|
17
- | `App` / context helpers | Mount the React app, provide context |
18
- | `navigate` helpers | Programmatic navigation via the router service |
19
- | `module` helpers | Resolve modules by alias |
20
- | `store` helpers | Client store integration |
21
- | `components` | Cross-platform components (e.g. error boundaries) |
22
- | `value`, `debug` | Render-time helpers |
23
- | Errors | Client-side typed errors |
24
- | Constants | Default aliases |
29
+ | `makeClientContext(cfg)` | The React client context: `@owlmeans/client-context` plus the state resource, both config resources, the modal and debug services, the rerender hook and `context.router()` |
30
+ | `ClientContext<C>` | That context's interface — adds `router()`, `registerRerenderer(fn)`, `rerender()`, `modal()`, `debug()` |
31
+ | `App` / `AppProps` | Mount: provides the context and, unless `noRouter`, the router. `children` render inside the provider and before the router |
32
+ | `Context` / `ClientContextContainer` / `useContext()` | The React context container and the hook that reads it |
33
+ | `Router` / `RouterProps` / `RouterProvider` / `makeRouterModel()` | Route rendering. `provide` is optional — omitted, the active router plugin's `compile` is used |
34
+ | `useNavigate()` | The `Navigator` — programmatic navigation by entrypoint alias |
35
+ | `Navigator` / `NavRequest` | `navigate` `go` `press` `back` `pressBack` `location`; a request adds `replace` and `silent` to an `AbstractRequest` |
36
+ | `useEntrypoint<T>()` | The `EntrypointContextParams` of the screen currently rendering — `{ alias, path, params, context }` |
37
+ | `RoutedComponent<Extra>` | Type of a component bound to a frontend protocol |
38
+ | `handler(Component, preprender?)` | Wrap a React component as an entrypoint handler |
39
+ | `lazyComponent(load, exportName, opts?)` / `lazyHandler(load, exportName, opts?)` | A code-split component with a static `.preload()`; and `handler(lazyComponent(...))` with `.preload` carried through. Types `LazyComponent`, `LazyHandler`, `LazyComponentOptions`, `LazyErrorRenderer` — see Code-splitting |
40
+ | `retryImport(load, opts?)` / `isChunkLoadError(error)` | Run a dynamic `import()` again while it fails to FETCH; tell a fetch failure from a module that loaded and broke. `RetryImportOptions` — see Chunk failures |
41
+ | `reloadOnce(key, windowMs)` / `recoverFromChunkError()` | The guarded page reload, and the one every chunk-failure path in a tab shares — see Chunk failures |
42
+ | `useStoreModel` / `useStoreList` | React hooks over a `@owlmeans/state` resource — one record by id, or a live query |
43
+ | `useValue(loader, deps?, forceDefault?)` / `UseValueParams<T>` | Render an async result. The second argument is the **dependency list**, not a default — see Async values |
44
+ | `useToggle(opened?)` / `Toggleable` | An open/close/toggle handle, which is what a modal surface binds to |
45
+ | `appendModalService` / `createModalService` / `ModalService` / `ModalStackLayer` | The modal stack — `context.modal()`: `request` `response` `cancel` `error` `layer()` `link(toggle)` |
46
+ | `ModalBodyProps` / `useSetupModalNavigator()` | `{ modal?: ModalService }` — the props a modal body is rendered with; and the hook that lets a body navigate |
47
+ | `appendDebugService` / `createDebugService` / `appendStateDebug(ctx, alias)` / `DebugService` | The debug menu — `context.debug()` |
48
+ | `ClientError`, `ComponentError`, `ComponentPropError`, `ComponentPropUndefined` | The client error family, registered with `ResilientError` |
49
+ | `DEF_MODAL_ALIAS` (`modal`), `DEF_DEBUG_ALIAS` (`debug`), `DEBUGGER_FLAG` / `DEBUG_CONFIG_KEY` (`debugger`), `DEF_IMPORT_RETRY_ATTEMPTS` (2), `DEF_IMPORT_RETRY_DELAYS_MS` (500, 1500), `CHUNK_RELOAD_KEY` (`owlmeans:chunk-reload`), `CHUNK_RELOAD_WINDOW_MS` (60 s) | Constants |
25
50
 
26
51
  ## Subpath Exports
27
52
 
28
- - `./utils` — generic client utilities
53
+ - `./utils` — `buildEntrypointTree`, `visitEntrypointTree`, `initializeRouter`, `createRouteRenderer`,
54
+ `EntrypointContext`. What the router is assembled from; a package building its own routing surface
55
+ uses these, an application does not.
29
56
 
30
- ## Usage
57
+ ## Navigation
31
58
 
32
- This is the platform-agnostic substrate that `@owlmeans/web-client` (browser) and the native equivalent build on. Most apps import from `@owlmeans/web-client` directly; use `@owlmeans/client` only for cross-platform code.
59
+ Navigation addresses an ALIAS, never a URL — the path lives in the entrypoint declaration, so a
60
+ component never builds one. `useNavigate()` returns the `Navigator`, which asks the target
61
+ entrypoint for its `url(request)` and hands that to the active router plugin:
33
62
 
34
63
  ```typescript
35
- import { navigate } from '@owlmeans/client'
36
- const navigateTo = navigate(context)
37
- navigateTo('/projects')
64
+ import { useNavigate } from '@owlmeans/client'
65
+ import type { RoutedComponent } from '@owlmeans/client'
66
+
67
+ export const ProjectScreen: RoutedComponent = ({ params }) => {
68
+ const nav = useNavigate()
69
+
70
+ // `go` navigates; `press` returns the handler for an onClick. `params` fills the path
71
+ // parameters of the target entrypoint, `query` the query string, `replace` swaps the
72
+ // history entry instead of pushing one.
73
+ void nav.go(PROJECT_ITEM, { params: { id: params.id }, query: { tab: 'files' } })
74
+
75
+ return <a onClick={nav.press(PROJECT_LIST)}>Back to the list</a>
76
+ }
38
77
  ```
39
78
 
79
+ A URL that comes back starting with `http` belongs to another service, and the navigator assigns
80
+ `location.href` rather than pushing a history entry. `nav.navigate(entrypoint, request)` takes the
81
+ entrypoint itself when you already hold it; `nav.back()` / `nav.pressBack()` go one entry back, and
82
+ `nav.location()` reads the current one.
83
+
84
+ A bound screen receives `{ alias, path, params, context }` as props, and `useEntrypoint()`
85
+ reads the same values from anywhere below it — read path parameters from `params` and pass them
86
+ down; a nested component never resolves route parameters itself.
87
+
88
+ ## Routing and guards
89
+
90
+ `App` mounts `Router`, which resolves the frontend entrypoints the context holds into a nested
91
+ route tree. Only entrypoints whose route is `AppType.Frontend` are mounted, and among those only
92
+ the ones that name no service at all, name this app's service, or are `sticky`. Each node
93
+ contributes **only its own segment** — its ancestors already carry theirs, so a declaration nests
94
+ by naming a `parent`.
95
+
96
+ A screen's guards are its own plus every ancestor's, taken from `getGuards()`. An empty list is an
97
+ open screen; when the list is non-empty and no guard matches, the renderer throws
98
+ `AuthorizationError('frontend-guard')`.
99
+
100
+ ## Code-splitting a screen or component
101
+
102
+ `lazyComponent(load, exportName, opts?)` turns a dynamic `import()` into a component whose chunk
103
+ loads on first render, with the `Suspense` boundary INSIDE it — the fallback replaces only this
104
+ component and the layout around it stays mounted. `lazyHandler` is `handler(lazyComponent(...))`
105
+ with `.preload` carried through, so it binds exactly like `handler(Component)`. Both are
106
+ re-exported by `@owlmeans/web-client` and `@owlmeans/web-panel` next to `handler`; the
107
+ chunk-failure tools below by `@owlmeans/web-client`.
108
+
109
+ ```tsx
110
+ import { lazyComponent, lazyHandler } from '@owlmeans/client'
111
+
112
+ // Module scope — never inside a render, a hook or an entrypoint handler factory.
113
+ export const reportsScreen = lazyHandler(
114
+ () => import('./screens/reports.js'), 'ReportsScreen', { fallback: <Spinner /> }
115
+ )
116
+ const Chart = lazyComponent(() => import('./chart.js'), 'Chart', {
117
+ fallback: props => <Skeleton height={props.height} />,
118
+ error: (props, error, retry) => <ChartUnavailable onRetry={retry} />,
119
+ })
120
+
121
+ // Prefetch on intent: the screen then renders without its fallback.
122
+ <a onMouseEnter={() => void reportsScreen.preload()} onFocus={() => void reportsScreen.preload()}>
123
+ ```
124
+
125
+ - **Module scope only.** The route renderer (`utils/route.tsx`) wraps the resolved screen in a
126
+ fresh `memo(...)` on every render, so the route subtree remounts on each navigation. A lazy
127
+ object made at module scope is already resolved by then and renders synchronously; one created
128
+ during a render or inside a handler factory is a new `React.lazy` each time and re-suspends — the
129
+ fallback flashes on every visit.
130
+ - **`preload()`** starts or joins the load and resolves to the component. Once loaded, every later
131
+ render resolves in the same tick — no re-suspend.
132
+ - **`fallback`** is a node or `fallback(props)`. **`error`** is a node or a `LazyErrorRenderer`
133
+ `(props, error, retry) => ReactNode`; `retry()` resets the piece's boundary and renders the
134
+ recreated lazy, which loads the chunk again.
135
+ - **`retry`** — the load runs through `retryImport` by default; pass `RetryImportOptions` to tune
136
+ it or `false` to load once.
137
+ - **An `exportName` the module does not export** rejects with a `SyntaxError` — never retried.
138
+
139
+ ### Chunk failures
140
+
141
+ A lazy piece ALWAYS carries its own error boundary, so a failed chunk never unmounts what is around
142
+ it:
143
+
144
+ | The piece fails with | `error` given | `error` omitted |
145
+ |---|---|---|
146
+ | a chunk-load failure (`isChunkLoadError`), after `retryImport` gave up | with `reload` (default for `lazyHandler`): the guarded reload starts, `fallback` stays, and `error` renders once the guard refuses; without it: `error` renders in place | `recoverFromChunkError()` starts the guarded reload; `fallback` stays in place |
147
+ | anything else (a module that loaded and broke, its own render) | `error` renders in place | propagates to the nearest boundary above, as if the piece had none |
148
+
149
+ Give every piece a deliberate `error`: a leaf that has a plain rendering of the same content (a
150
+ formatter, a highlighter) degrades to it; anything else shows a notice with a retry. A whole screen
151
+ (`lazyHandler`) reloads once before its notice (`reload: true` by default) — it has nothing to
152
+ degrade to.
153
+
154
+ - **Retry scope.** `retryImport` covers a TRANSIENT fetch failure — a blip, an edge answering 404
155
+ or 5xx for a moment. Chromium keeps a failed module fetch for the document's lifetime and rejects
156
+ every later `import()` of that URL at once, so a retry imports the URL the error names with a
157
+ fresh `t` parameter (`chunkUrlOf` + `cacheBustedUrl`, `bustCache` on by default; same-origin
158
+ http(s) URLs only): a new URL, fetched again. That recovers a built chunk. It cannot recover a
159
+ DEV-served module: React Fast Refresh makes every module import itself by its own URL, so the
160
+ busted copy depends on the remembered failure — only a new document loads it, which is what
161
+ `reload` and the guarded reload are for. Safari names no URL; its retries repeat `load`.
162
+ - **A failed load stays failed for the instance that saw it** until its `retry()`: React re-renders
163
+ that instance while recovering from the error, and a fresh load there would suspend again
164
+ forever. A NEW mount — a navigation back, another place in the tree — takes the recreated lazy and
165
+ loads again; so does `preload()`.
166
+ - **`isChunkLoadError(error)`** is true for a browser's failed dynamic import (Chromium "Failed to
167
+ fetch dynamically imported module", Safari "Importing a module script failed", Firefox "error
168
+ loading dynamically imported module"), Vite's "Unable to preload CSS", webpack's `ChunkLoadError`,
169
+ a `vite:preloadError` event, and an element's `error` event. It is false for a `SyntaxError`
170
+ about a missing export and for a throw while the module evaluated — loading those again changes
171
+ nothing.
172
+ - **`retryImport(load, opts?)`** runs `load` again while `shouldRetry(error)` (default
173
+ `isChunkLoadError`) holds, `attempts` (2) more times at most, pausing `delaysMs[i]` before retry
174
+ `i` (500 ms, 1500 ms; the last entry repeats), and rethrows the last failure.
175
+ - **`reloadOnce(key, windowMs)`** reloads the page at most once per `windowMs` per tab, keeping the
176
+ time in `sessionStorage` under `key`; never while offline and never without storage (with no
177
+ guard kept, a failure that survives the reload would reload forever); a platform with no page to
178
+ reload does nothing. It answers whether a reload started.
179
+ - **`recoverFromChunkError()`** is `reloadOnce(CHUNK_RELOAD_KEY, CHUNK_RELOAD_WINDOW_MS)` — the ONE
180
+ guard a tab shares. An application that also reloads on `vite:preloadError` calls it rather than
181
+ keeping a guard of its own, so one failure never reloads twice.
182
+
183
+ Source of truth: `src/lazy.tsx` and `src/lazy-retry.ts` in this package.
184
+
185
+ ## Client state
186
+
187
+ State lives on the context as a `@owlmeans/state` resource; these hooks subscribe to it.
188
+
189
+ ```typescript
190
+ import { useStoreList, useStoreModel } from '@owlmeans/client'
191
+
192
+ const task = useStoreModel<Task>(id, TASKS) // one record
193
+ const open = useStoreList<Task>({ query: { status: 'open' }, resource: TASKS }) // live query
194
+ ```
195
+
196
+ `useStoreModel` returns a `StateModel` — read `model.record`, write with `model.update({ ... })`.
197
+ Never assign into `model.record` — it is not a snapshot. On a model the store backs, it IS the
198
+ object the store holds: a field assignment mutates what every other holder of that key reads, and
199
+ the next `update()`/`commit()` carries the mutation through, while nothing notifies and nothing
200
+ re-renders. The hook never throws for missing data either — an id the store knows nothing about
201
+ yields a model whose `empty` is true, and nothing is written into the store on the way.
202
+ `useStoreList` takes `{ query?, sort?, resource? }` and matches everything when `query` is omitted.
203
+ Full contract: [[state]].
204
+
205
+ Both hooks go through `useSyncExternalStore`, and the live subscription React installs is torn down
206
+ with the component. The FIRST snapshot is taken during render, by subscribing and unsubscribing
207
+ again in one statement — a state resource seeds its listener synchronously, so no render runs
208
+ without a value and that momentary subscription never outlives the call. The value is then cached
209
+ and the same reference is returned until something actually changes, which is what keeps React from
210
+ re-rendering forever.
211
+
212
+ ## Async values
213
+
214
+ `useValue(loader, deps?, forceDefault?)` runs an async loader in an effect and answers with the
215
+ default — `null` when there is none — until it resolves. Its second argument is overloaded, and
216
+ reading it as "a default" is the standard mistake:
217
+
218
+ ```typescript
219
+ useValue(async () => api.load(id), [id]) // a DependencyList — re-runs on id
220
+ useValue(async () => api.load(id), { default: EMPTY, deps: [id] }) // both, via UseValueParams
221
+ useValue(async () => api.load(id)) // no deps — the loader runs once
222
+ useValue(async () => api.count(), 0) // a bare non-array value IS the default
223
+ ```
224
+
225
+ The deps always come from the argument's SHAPE: an array is the dependency list itself, an object
226
+ with a `deps` key gives `deps ?? []`, and anything else gives `[]`. The default is read from the
227
+ same argument: `default` off a `UseValueParams` object, `null` when an array was passed, the value
228
+ itself otherwise. `forceDefault: true` changes only the second half — the argument is then taken as
229
+ the default whatever its shape, while still deciding the deps.
230
+
231
+ The loader is handed a `MutableRefObject<boolean>` cancel ref, which the effect's cleanup sets to
232
+ `true`, so a loader that awaits more than once checks `cancel.current` before it commits. A loader that resolves to a **function** is kept aside and returned as it is, rather than
233
+ being run as a state updater — which is what lets a component be an async value.
234
+
235
+ ## Modals
236
+
237
+ `context.modal()` owns a STACK of body components and one surface. The surface is a component the
238
+ app mounts once: it links a toggle to the service, reads the top layer through `layer()`, and
239
+ renders it with the service as a prop.
240
+
241
+ ```tsx
242
+ import { useContext, useSetupModalNavigator, useToggle, useValue } from '@owlmeans/client'
243
+ import type { ModalBodyProps } from '@owlmeans/client'
244
+ import { useEffect } from 'react'
245
+ import type { FC } from 'react'
246
+
247
+ export const Modal: FC = () => {
248
+ useSetupModalNavigator() // lets a body navigate; call it once
249
+ const context = useContext()
250
+ const toggle = useToggle(false)
251
+
252
+ useEffect(() => {
253
+ void context.waitForInitialized().then(() => context.modal().link(toggle))
254
+ }, [])
255
+
256
+ const Com = useValue<FC<ModalBodyProps> | undefined>(
257
+ async () => toggle.opened ? context.modal().layer()?.Com : undefined,
258
+ [toggle.opened]
259
+ )
260
+
261
+ return <Dialog open={toggle.opened} onOpenChange={toggle.set}>
262
+ {Com != null ? <Com modal={context.modal()} /> : undefined}
263
+ </Dialog>
264
+ }
265
+ ```
266
+
267
+ A **body is an `FC<ModalBodyProps>`** — it receives the service as an optional `modal` prop, and
268
+ that prop is how it answers. Nothing else reaches it, so whatever a body needs travels in the
269
+ closure of the component that requested it.
270
+
271
+ ```typescript
272
+ const result = await context.modal().request<Answer>(ConfirmBody) // null when cancelled
273
+ ```
274
+
275
+ `request` pushes the body onto the stack, opens the linked toggle, and resolves when the body calls
276
+ `modal.response(value)`, `modal.cancel()` (which resolves `null`) or `modal.error(e)` (which
277
+ rejects). All three settle the promise first, then pop the layer and close the surface — and
278
+ `request` pops a SECOND time when its own `await` resumes. One completed request therefore removes
279
+ two layers.
280
+
281
+ **Keep the stack one deep.** A lone body ends on an empty stack and the second pop costs nothing.
282
+ A body requested from inside another body takes its parent down with it: closing the child pops the
283
+ child, sees a layer still there and schedules the surface to reopen 500 ms later, and then the
284
+ child's continuation pops the parent in the microtask before that timer fires. The surface reopens
285
+ with `layer()` answering `undefined` and nothing to render, and the parent's own `request` — whose
286
+ deferred no one is left to settle — never resolves. Chain from the caller instead: await the first
287
+ request, then issue the next.
288
+
289
+ ## Debug menu
290
+
291
+ `appendDebugService` registers the menu only when `cfg.debug.all` or `cfg.debug.debugger` is set,
292
+ so `context.debug()` answers `undefined` in a normal build and a caller must handle that. A package
293
+ that owns a client resource calls `appendStateDebug(context, alias)` at wiring time to have it
294
+ listed under "Reset states"; "Reset app" erases the whole client DB.
295
+
40
296
  ## Depends On
41
297
 
42
- - `@owlmeans/client-context`, `@owlmeans/client-entrypoint`, `@owlmeans/client-route`
43
- - `@owlmeans/router`, `@owlmeans/auth-common`
44
- - `react` (peer)
298
+ - `@owlmeans/client-context`, `@owlmeans/client-entrypoint`, `@owlmeans/client-resource`
299
+ - `@owlmeans/router` (the routing plugin surface), `@owlmeans/state`, `@owlmeans/entrypoint`,
300
+ `@owlmeans/resource`, `@owlmeans/config`, `@owlmeans/context`, `@owlmeans/auth`, `@owlmeans/error`
301
+ - `react` and `@remix-run/router` (peer)
302
+
303
+ ## Related
304
+
305
+ - [[web-client]] — the browser layer built on this
306
+ - [[state]] — the store the two state hooks read
307
+ - [[router]] — the routing plugin `context.router()` resolves
package/build/consts.d.ts CHANGED
@@ -2,4 +2,12 @@ export declare const DEF_MODAL_ALIAS = "modal";
2
2
  export declare const DEF_DEBUG_ALIAS = "debug";
3
3
  export declare const DEBUGGER_FLAG = "debugger";
4
4
  export declare const DEBUG_CONFIG_KEY = "debugger";
5
+ /** How many times `retryImport` loads a chunk again after its first failure. */
6
+ export declare const DEF_IMPORT_RETRY_ATTEMPTS = 2;
7
+ /** The pause before each retry, in order; the last one repeats. */
8
+ export declare const DEF_IMPORT_RETRY_DELAYS_MS: readonly number[];
9
+ /** The `sessionStorage` key `recoverFromChunkError` keeps the time of its last reload under. */
10
+ export declare const CHUNK_RELOAD_KEY = "owlmeans:chunk-reload";
11
+ /** At most one chunk-recovery reload per this window, per tab. */
12
+ export declare const CHUNK_RELOAD_WINDOW_MS = 60000;
5
13
  //# sourceMappingURL=consts.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,eAAe,UAAU,CAAA;AACtC,eAAO,MAAM,eAAe,UAAU,CAAA;AAEtC,eAAO,MAAM,aAAa,aAAa,CAAA;AACvC,eAAO,MAAM,gBAAgB,aAAa,CAAA"}
1
+ {"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,eAAe,UAAU,CAAA;AACtC,eAAO,MAAM,eAAe,UAAU,CAAA;AAEtC,eAAO,MAAM,aAAa,aAAa,CAAA;AACvC,eAAO,MAAM,gBAAgB,aAAa,CAAA;AAE1C,gFAAgF;AAChF,eAAO,MAAM,yBAAyB,IAAI,CAAA;AAC1C,mEAAmE;AACnE,eAAO,MAAM,0BAA0B,EAAE,SAAS,MAAM,EAAgB,CAAA;AAExE,gGAAgG;AAChG,eAAO,MAAM,gBAAgB,0BAA0B,CAAA;AACvD,kEAAkE;AAClE,eAAO,MAAM,sBAAsB,QAAS,CAAA"}
package/build/consts.js CHANGED
@@ -2,4 +2,12 @@ export const DEF_MODAL_ALIAS = 'modal';
2
2
  export const DEF_DEBUG_ALIAS = 'debug';
3
3
  export const DEBUGGER_FLAG = 'debugger';
4
4
  export const DEBUG_CONFIG_KEY = 'debugger';
5
+ /** How many times `retryImport` loads a chunk again after its first failure. */
6
+ export const DEF_IMPORT_RETRY_ATTEMPTS = 2;
7
+ /** The pause before each retry, in order; the last one repeats. */
8
+ export const DEF_IMPORT_RETRY_DELAYS_MS = [500, 1500];
9
+ /** The `sessionStorage` key `recoverFromChunkError` keeps the time of its last reload under. */
10
+ export const CHUNK_RELOAD_KEY = 'owlmeans:chunk-reload';
11
+ /** At most one chunk-recovery reload per this window, per tab. */
12
+ export const CHUNK_RELOAD_WINDOW_MS = 60_000;
5
13
  //# sourceMappingURL=consts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAA;AACtC,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAA;AAEtC,MAAM,CAAC,MAAM,aAAa,GAAG,UAAU,CAAA;AACvC,MAAM,CAAC,MAAM,gBAAgB,GAAG,UAAU,CAAA"}
1
+ {"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAA;AACtC,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAA;AAEtC,MAAM,CAAC,MAAM,aAAa,GAAG,UAAU,CAAA;AACvC,MAAM,CAAC,MAAM,gBAAgB,GAAG,UAAU,CAAA;AAE1C,gFAAgF;AAChF,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAA;AAC1C,mEAAmE;AACnE,MAAM,CAAC,MAAM,0BAA0B,GAAsB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;AAExE,gGAAgG;AAChG,MAAM,CAAC,MAAM,gBAAgB,GAAG,uBAAuB,CAAA;AACvD,kEAAkE;AAClE,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,OAAO,CAAA;AAEpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAE5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAoB/C,eAAO,MAAM,iBAAiB,GAAI,CAAC,SAAS,YAAY,EAAE,CAAC,SAAS,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,KAAG,CA8BjH,CAAA;AAED,eAAO,MAAM,sBAAsB,2CAA+C,CAAA;AAElF,eAAO,MAAM,OAAO,uDAAkC,CAAA;AAEtD,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,YAAY,EAAE,CAAC,SAAS,aAAa,CAAC,CAAC,CAAC,QAE5E,CAAA"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,OAAO,CAAA;AAEpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAE5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAmB/C,eAAO,MAAM,iBAAiB,GAAI,CAAC,SAAS,YAAY,EAAE,CAAC,SAAS,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,KAAG,CA4BjH,CAAA;AAED,eAAO,MAAM,sBAAsB,2CAA+C,CAAA;AAElF,eAAO,MAAM,OAAO,uDAAkC,CAAA;AAEtD,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,YAAY,EAAE,CAAC,SAAS,aAAa,CAAC,CAAC,CAAC,QAE5E,CAAA"}
package/build/context.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createContext, useContext as useCtx } from 'react';
2
2
  import { makeClientContext as makeBasicContext, PLUGINS } from '@owlmeans/client-context';
3
- import { AppType, CONFIG_RECORD, Layer } from '@owlmeans/context';
3
+ import { AppType, CONFIG_RECORD } from '@owlmeans/context';
4
4
  import { appendStateResource } from '@owlmeans/state';
5
5
  import { appendModalService } from './components/modal.js';
6
6
  import { appendDebugService } from './services/debug.js';
@@ -9,7 +9,6 @@ import { ROUTER_SERVICE } from '@owlmeans/router';
9
9
  const defaultCfg = {
10
10
  services: {},
11
11
  brand: {},
12
- layer: Layer.Service,
13
12
  trusted: [],
14
13
  [CONFIG_RECORD]: [],
15
14
  ready: false,
@@ -40,7 +39,6 @@ export const makeClientContext = (cfg) => {
40
39
  };
41
40
  }
42
41
  context.router = () => context.service(ROUTER_SERVICE);
43
- context.makeContext = makeClientContext;
44
42
  return context;
45
43
  };
46
44
  export const ClientContextContainer = createContext(makeClientContext(defaultCfg));
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,UAAU,IAAI,MAAM,EAAE,MAAM,OAAO,CAAA;AAE3D,OAAO,EAAE,iBAAiB,IAAI,gBAAgB,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAA;AAEzF,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAEjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAEtE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAEjD,MAAM,UAAU,GAAiB;IAC/B,QAAQ,EAAE,EAAE;IACZ,KAAK,EAAE,EAAE;IACT,KAAK,EAAE,KAAK,CAAC,OAAO;IACpB,OAAO,EAAE,EAAE;IACX,CAAC,aAAa,CAAC,EAAE,EAAE;IACnB,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE,EAAE;IACX,KAAK,EAAE,EAAE;IACT,IAAI,EAAE,OAAO,CAAC,QAAQ;CACvB,CAAA;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAwE,GAAM,EAAK,EAAE;IACpH,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAM,CAAA;IAC1C,mBAAmB,CAAO,OAAO,CAAC,CAAA;IAClC,kBAAkB,CAAO,OAAO,CAAC,CAAA;IACjC,oBAAoB,CAAO,OAAO,CAAC,CAAA;IACnC,oBAAoB,CAAO,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,CAAA;IAC3D,kBAAkB,CAAO,OAAO,CAAC,CAAA;IAEjC,IAAI,OAAO,CAAC,kBAAkB,IAAI,IAAI,EAAE,CAAC;QACvC,MAAM,WAAW,GAAuB,EAAE,CAAA;QAE1C,OAAO,CAAC,kBAAkB,GAAG,QAAQ,CAAC,EAAE;YACtC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;YAC1B,OAAO,GAAG,EAAE;gBACV,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;gBAC3C,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;oBACf,WAAW,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;gBAC9B,CAAC;YACH,CAAC,CAAA;QACH,CAAC,CAAA;QACD,OAAO,CAAC,QAAQ,GAAG,GAAG,EAAE;YACtB,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC7C,CAAC,CAAA;IACH,CAAC;IAED,OAAO,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAgB,cAAc,CAAC,CAAA;IAErE,OAAO,CAAC,WAAW,GAAG,iBAA+C,CAAA;IAErE,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAG,aAAa,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAA;AAElF,MAAM,CAAC,MAAM,OAAO,GAAG,sBAAsB,CAAC,QAAQ,CAAA;AAEtD,MAAM,CAAC,MAAM,UAAU,GAAG,GAAuD,EAAE,CAAC,MAAM,CACxF,sBAAoD,CACrD,CAAA"}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,UAAU,IAAI,MAAM,EAAE,MAAM,OAAO,CAAA;AAE3D,OAAO,EAAE,iBAAiB,IAAI,gBAAgB,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAA;AAEzF,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAE1D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACxD,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAEtE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAEjD,MAAM,UAAU,GAAiB;IAC/B,QAAQ,EAAE,EAAE;IACZ,KAAK,EAAE,EAAE;IACT,OAAO,EAAE,EAAE;IACX,CAAC,aAAa,CAAC,EAAE,EAAE;IACnB,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE,EAAE;IACX,KAAK,EAAE,EAAE;IACT,IAAI,EAAE,OAAO,CAAC,QAAQ;CACvB,CAAA;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAwE,GAAM,EAAK,EAAE;IACpH,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAM,CAAA;IAC1C,mBAAmB,CAAO,OAAO,CAAC,CAAA;IAClC,kBAAkB,CAAO,OAAO,CAAC,CAAA;IACjC,oBAAoB,CAAO,OAAO,CAAC,CAAA;IACnC,oBAAoB,CAAO,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,CAAA;IAC3D,kBAAkB,CAAO,OAAO,CAAC,CAAA;IAEjC,IAAI,OAAO,CAAC,kBAAkB,IAAI,IAAI,EAAE,CAAC;QACvC,MAAM,WAAW,GAAuB,EAAE,CAAA;QAE1C,OAAO,CAAC,kBAAkB,GAAG,QAAQ,CAAC,EAAE;YACtC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;YAC1B,OAAO,GAAG,EAAE;gBACV,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;gBAC3C,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;oBACf,WAAW,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;gBAC9B,CAAC;YACH,CAAC,CAAA;QACH,CAAC,CAAA;QACD,OAAO,CAAC,QAAQ,GAAG,GAAG,EAAE;YACtB,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC7C,CAAC,CAAA;IACH,CAAC;IAED,OAAO,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAgB,cAAc,CAAC,CAAA;IAErE,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAG,aAAa,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAA;AAElF,MAAM,CAAC,MAAM,OAAO,GAAG,sBAAsB,CAAC,QAAQ,CAAA;AAEtD,MAAM,CAAC,MAAM,UAAU,GAAG,GAAuD,EAAE,CAAC,MAAM,CACxF,sBAAoD,CACrD,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { EntrypointContextParams } from './types.js';
2
+ export declare const useEntrypoint: <T extends {} = {}>() => EntrypointContextParams<T>;
3
+ //# sourceMappingURL=entrypoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entrypoint.d.ts","sourceRoot":"","sources":["../src/entrypoint.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAA;AAEzD,eAAO,MAAM,aAAa,GAAI,CAAC,SAAS,EAAE,GAAG,EAAE,OAAwC,uBAAuB,CAAC,CAAC,CAAC,CAAA"}
@@ -0,0 +1,4 @@
1
+ import { useContext } from 'react';
2
+ import { EntrypointContext } from './utils/index.js';
3
+ export const useEntrypoint = () => useContext(EntrypointContext);
4
+ //# sourceMappingURL=entrypoint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entrypoint.js","sourceRoot":"","sources":["../src/entrypoint.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAA;AAClC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAGpD,MAAM,CAAC,MAAM,aAAa,GAAG,GAAsB,EAAE,CAAC,UAAU,CAAC,iBAAiB,CAA+B,CAAA"}
package/build/helper.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { isValidElement } from 'react';
3
- import { ModuleContext } from './utils/module.js';
3
+ import { EntrypointContext } from './utils/entrypoint.js';
4
4
  import { assertContext } from '@owlmeans/context';
5
5
  export const handler = (Component, preprender) => ref => (req, res) => {
6
- const location = `client-handler:${ref.ref?.getAlias() ?? 'unknown'}`;
6
+ const location = `client-handler:${ref.ref?.alias ?? 'unknown'}`;
7
7
  if (ref.ref == null) {
8
8
  throw new SyntaxError('Module reference is not provided');
9
9
  }
@@ -23,7 +23,7 @@ export const handler = (Component, preprender) => ref => (req, res) => {
23
23
  }
24
24
  const Renderer = ({ children, ...props }) => {
25
25
  const Renderer = Component;
26
- return _jsx(ModuleContext.Provider, { value: props, children: _jsx(Renderer, { ...props, children: children }) });
26
+ return _jsx(EntrypointContext.Provider, { value: props, children: _jsx(Renderer, { ...props, children: children }) });
27
27
  };
28
28
  return Renderer;
29
29
  };
@@ -1 +1 @@
1
- {"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.tsx"],"names":[],"mappings":";AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAKjD,MAAM,CAAC,MAAM,OAAO,GAAG,CACrB,SAA6B,EAAE,UAAoB,EACxB,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAGrC,GAAM,EAAE,GAAM,EAAO,EAAE;IACvB,MAAM,QAAQ,GAAG,kBAAkB,GAAG,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,SAAS,EAAE,CAAA;IACrE,IAAI,GAAG,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC;QACpB,MAAM,IAAI,WAAW,CAAC,kCAAkC,CAAC,CAAA;IAC3D,CAAC;IACD,MAAM,GAAG,GAAG,aAAa,CAAkB,GAAG,CAAC,GAAG,CAAC,GAAc,EAAE,QAAQ,CAAC,CAAA;IAC5E,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;QAChB,MAAM,IAAI,WAAW,CAAC,gCAAgC,CAAC,CAAA;IACzD,CAAC;IACD,IAAI,cAAc,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;QACtB,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QACxB,MAAM,QAAQ,GAAG,SAAoC,CAAA;QACrD,MAAM,OAAO,GAAG,KAAC,QAAQ,OAAK,GAAG,EAAE,OAAO,EAAE,GAAG,GAAI,CAAA;QACnD,GAAG,CAAC,OAAO,CAAC,OAA6B,CAAC,CAAA;QAE1C,OAAO,OAAO,CAAA;IAChB,CAAC;IAED,MAAM,QAAQ,GAAoB,CAAC,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,EAAE;QAC3D,MAAM,QAAQ,GAAG,SAAoC,CAAA;QACrD,OAAO,KAAC,aAAa,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YACzC,KAAC,QAAQ,OAAK,KAAK,YAAG,QAAQ,GAAY,GACnB,CAAA;IAC3B,CAAC,CAAA;IAED,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA"}
1
+ {"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.tsx"],"names":[],"mappings":";AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAEzD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAKjD,MAAM,CAAC,MAAM,OAAO,GAAG,CACrB,SAA6B,EAAE,UAAoB,EACxB,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAGrC,GAAM,EAAE,GAAM,EAAO,EAAE;IACvB,MAAM,QAAQ,GAAG,kBAAkB,GAAG,CAAC,GAAG,EAAE,KAAK,IAAI,SAAS,EAAE,CAAA;IAChE,IAAI,GAAG,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC;QACpB,MAAM,IAAI,WAAW,CAAC,kCAAkC,CAAC,CAAA;IAC3D,CAAC;IACD,MAAM,GAAG,GAAG,aAAa,CAAkB,GAAG,CAAC,GAAG,CAAC,GAAc,EAAE,QAAQ,CAAC,CAAA;IAC5E,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;QAChB,MAAM,IAAI,WAAW,CAAC,gCAAgC,CAAC,CAAA;IACzD,CAAC;IACD,IAAI,cAAc,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;QACtB,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QACxB,MAAM,QAAQ,GAAG,SAAoC,CAAA;QACrD,MAAM,OAAO,GAAG,KAAC,QAAQ,OAAK,GAAG,EAAE,OAAO,EAAE,GAAG,GAAI,CAAA;QACnD,GAAG,CAAC,OAAO,CAAC,OAA6B,CAAC,CAAA;QAE1C,OAAO,OAAO,CAAA;IAChB,CAAC;IAED,MAAM,QAAQ,GAAoB,CAAC,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,EAAE;QAC3D,MAAM,QAAQ,GAAG,SAAoC,CAAA;QACrD,OAAO,KAAC,iBAAiB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAC7C,KAAC,QAAQ,OAAK,KAAK,YAAG,QAAQ,GAAY,GACf,CAAA;IAC/B,CAAC,CAAA;IAED,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA"}
package/build/index.d.ts CHANGED
@@ -2,8 +2,10 @@ export * from './context.js';
2
2
  export * from './components/index.js';
3
3
  export * from './services/index.js';
4
4
  export * from './helper.js';
5
+ export * from './lazy.js';
6
+ export * from './lazy-retry.js';
5
7
  export * from './navigate.js';
6
- export * from './module.js';
8
+ export * from './entrypoint.js';
7
9
  export * from './router.js';
8
10
  export * from './types.js';
9
11
  export * from './store.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,cAAc,CAAA;AAC5B,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,UAAU,CAAA;AACxB,cAAc,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,cAAc,CAAA;AAC5B,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,UAAU,CAAA;AACxB,cAAc,aAAa,CAAA"}
package/build/index.js CHANGED
@@ -2,8 +2,10 @@ export * from './context.js';
2
2
  export * from './components/index.js';
3
3
  export * from './services/index.js';
4
4
  export * from './helper.js';
5
+ export * from './lazy.js';
6
+ export * from './lazy-retry.js';
5
7
  export * from './navigate.js';
6
- export * from './module.js';
8
+ export * from './entrypoint.js';
7
9
  export * from './router.js';
8
10
  export * from './types.js';
9
11
  export * from './store.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,cAAc,CAAA;AAC5B,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,UAAU,CAAA;AACxB,cAAc,aAAa,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,cAAc,CAAA;AAC5B,cAAc,uBAAuB,CAAA;AACrC,cAAc,qBAAqB,CAAA;AACnC,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,UAAU,CAAA;AACxB,cAAc,aAAa,CAAA"}
@@ -0,0 +1,47 @@
1
+ import type { RetryImportOptions } from './types.js';
2
+ /**
3
+ * Whether `error` is a chunk that could not be FETCHED: a browser's failed dynamic import, Vite's
4
+ * stylesheet preload failure, webpack's `ChunkLoadError`, Vite's `vite:preloadError` event, or
5
+ * the `error` event of the element a loader fetched through. A module that was fetched and then
6
+ * failed — a missing export, a throw while it evaluated — is not one: loading it again changes
7
+ * nothing.
8
+ */
9
+ export declare const isChunkLoadError: (error: unknown) => boolean;
10
+ /**
11
+ * The URL of the chunk a failed dynamic import tried to fetch, when the browser's error names it
12
+ * and it is an http(s) URL of this page's origin — never an arbitrary URL out of a message. `null`
13
+ * otherwise (Safari, a non-browser platform, another origin).
14
+ */
15
+ export declare const chunkUrlOf: (error: unknown) => string | null;
16
+ /**
17
+ * `href` with a fresh `t` parameter: a URL the browser has not seen, so it fetches the module
18
+ * again. `t` is the parameter Vite's dev server already gives module URLs; a static host ignores it.
19
+ */
20
+ export declare const cacheBustedUrl: (href: string, now?: number) => string;
21
+ /**
22
+ * Run `load` — a dynamic `import()` — and, while it rejects with a chunk-load failure, run it again
23
+ * after a pause, `attempts` more times at most. Any other rejection is rethrown at once.
24
+ *
25
+ * This covers a TRANSIENT failure — a network blip, an edge answering 404 or 5xx for a moment.
26
+ * A browser remembers a failed module fetch for the document's lifetime (Chromium does) and
27
+ * rejects every later `import()` of that URL at once, so a retry imports the URL the error names
28
+ * with a cache-busting parameter instead (`bustCache`, default on): a new URL, fetched again. Where
29
+ * the error names no URL (Safari), `load` runs again as is; what always recovers is a new
30
+ * document, which `recoverFromChunkError` starts, once and guarded.
31
+ */
32
+ export declare const retryImport: <M>(load: () => Promise<M>, opts?: RetryImportOptions) => Promise<M>;
33
+ /**
34
+ * Reload the page, at most once per `windowMs` in this tab — the time of the last reload is kept
35
+ * in `sessionStorage` under `key`. Never while offline (the reload would land on the browser's own
36
+ * error page), and never without storage: with nowhere to keep the guard, a failure that survives
37
+ * the reload would reload forever. A platform with no page to reload does nothing. Answers whether
38
+ * a reload was started.
39
+ */
40
+ export declare const reloadOnce: (key: string, windowMs: number) => boolean;
41
+ /**
42
+ * The guarded reload a chunk that failed for good ends in: `reloadOnce` under one key every caller
43
+ * in the tab shares, so a lazy boundary and an application's own `vite:preloadError` listener
44
+ * never reload twice for one failure.
45
+ */
46
+ export declare const recoverFromChunkError: () => boolean;
47
+ //# sourceMappingURL=lazy-retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lazy-retry.d.ts","sourceRoot":"","sources":["../src/lazy-retry.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAQpD;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,UAAW,OAAO,KAAG,OAUjD,CAAA;AAOD;;;;GAIG;AACH,eAAO,MAAM,UAAU,UAAW,OAAO,KAAG,MAAM,GAAG,IAgBpD,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,SAAU,MAAM,QAAO,MAAM,KAAgB,MAIvE,CAAA;AAKD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,WAAW,GAAU,CAAC,QAAQ,MAAM,OAAO,CAAC,CAAC,CAAC,SAAS,kBAAkB,KAAG,OAAO,CAAC,CAAC,CAsBjG,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,QAAS,MAAM,YAAY,MAAM,KAAG,OAoB1D,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,QAAO,OAA+D,CAAA"}