@owlmeans/web-client 0.1.18-rc.50 → 0.1.18-rc.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,7 +11,7 @@ is the legacy MUI layer — start nothing new there. React Native apps use the `
11
11
  ## Installation
12
12
 
13
13
  ```bash
14
- bun add @owlmeans/web-client@^0.1.18-rc.50
14
+ bun add @owlmeans/web-client@^0.1.18-rc.52
15
15
  ```
16
16
 
17
17
  ## Concepts
@@ -289,7 +289,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
289
289
  your project's skill store (`.agents/skills/`):
290
290
 
291
291
  ```sh
292
- npx @owlmeans/agent-skills@^0.1.18-rc.38
292
+ npx @owlmeans/agent-skills@^0.1.18-rc.39
293
293
  ```
294
294
 
295
295
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/web-client",
4
- "version": "0.1.18-rc.50",
5
- "generatedAt": "2026-09-23T19:57:40.685Z",
4
+ "version": "0.1.18-rc.52",
5
+ "generatedAt": "2026-09-25T12:22:06.199Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  name: web-client
3
- description: Bind OwlMeans shared entrypoint protocols in a browser application. Use when registering client routes, attaching React screens, calling API protocols, or configuring the browser context.
3
+ description: Bind OwlMeans shared entrypoint protocols in a browser application. Use when registering client routes, attaching React screens (including lazily-loaded ones), calling API protocols, or configuring the browser context.
4
4
  ---
5
5
  <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
6
6
 
7
7
  # Browser protocol entrypoints
8
8
 
9
- **Install:** `bun add @owlmeans/web-client@^0.1.18-rc.50`
9
+ **Install:** `bun add @owlmeans/web-client@^0.1.18-rc.52`
10
10
 
11
11
  Shared protocol declarations are immutable. Bind a complete protocol tree for callable API routes,
12
12
  then bind frontend declarations to screens.
@@ -31,11 +31,53 @@ const project = await context.entrypoint(projectProtocols.get).call({
31
31
  })
32
32
  ```
33
33
 
34
+ ## Lazily-loaded screens
35
+
36
+ `lazyHandler` and `lazyComponent` (from `@owlmeans/client`) are re-exported here and by
37
+ `@owlmeans/web-panel`, next to `handler`; so are, here only, the chunk-failure tools
38
+ `retryImport`, `isChunkLoadError`, `reloadOnce` and `recoverFromChunkError` (types
39
+ `LazyErrorRenderer`, `RetryImportOptions`). A `lazyHandler(...)` result binds exactly like
40
+ `handler(Component)`; declare it at module scope, where the bindings live — never inside a render.
41
+
42
+ ```tsx
43
+ import { bindScreen, lazyHandler } from '@owlmeans/web-client'
44
+
45
+ const reportsScreen = lazyHandler(() => import('./screens/reports.js'), 'ReportsScreen', {
46
+ fallback: <Spinner />,
47
+ })
48
+
49
+ export const clientBindings = [
50
+ bindScreen(webProtocols.reports, reportsScreen),
51
+ ]
52
+ // reportsScreen.preload() on hover/focus of a link renders the screen without its fallback.
53
+ ```
54
+
55
+ The fallback renders inside the screen's own `Suspense` boundary, so the layout around it stays
56
+ mounted while the chunk loads, and the screen's own error boundary keeps a failed chunk from
57
+ unmounting it: a fetch failure is retried, then `error` renders — or, without one, the guarded
58
+ reload starts. Rules and options: the `client` skill, Code-splitting and Chunk failures.
59
+
60
+ ## Rendering after an async boot
61
+
62
+ If your app awaits something (e.g. `prepareI18n`) before calling `render()`, this is handled
63
+ correctly — the render helper checks `document.readyState` rather than unconditionally waiting for
64
+ an event that may already have fired. It waits for `DOMContentLoaded` only while the document is
65
+ still `loading` and mounts at once otherwise; `renderApp` and `@owlmeans/web-panel`'s `render` both
66
+ go through it.
67
+
68
+ ```ts
69
+ import { prepareI18n } from '@owlmeans/client-i18n'
70
+
71
+ await prepareI18n(context.cfg)
72
+ renderApp(context) // or @owlmeans/web-panel's render(context)
73
+ ```
74
+
34
75
  ## Rules
35
76
 
36
77
  - Declare paths, guards, gates and contracts once in the shared protocol tree.
37
78
  - Bind every API declaration the browser calls, including route parents.
38
- - Use `bindScreen(protocol, handler(Component))` only for frontend route declarations.
79
+ - Use `bindScreen(protocol, handler(Component))` — or `lazyHandler(...)` — only for frontend route
80
+ declarations.
39
81
  - Do not replace request/response typing at a call site; update the shared contract instead.
40
82
  - Keep framework entrypoints and application entrypoints in one registered array, without mutating a
41
83
  shared declaration collection.
@@ -1,4 +1,5 @@
1
- export { handler } from '@owlmeans/client';
1
+ export { handler, lazyHandler, lazyComponent, retryImport, isChunkLoadError, reloadOnce, recoverFromChunkError, chunkUrlOf, cacheBustedUrl, } from '@owlmeans/client';
2
+ export type { LazyComponent, LazyComponentOptions, LazyErrorRenderer, LazyHandler, RetryImportOptions } from '@owlmeans/client';
2
3
  export { config } from '@owlmeans/client-context';
3
4
  export { service } from '@owlmeans/config';
4
5
  export { AppType, HOME, ROOT, BASE, GUEST } from '@owlmeans/context';
@@ -1 +1 @@
1
- {"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAA;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACpE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAA;AAC7F,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AACjD,YAAY,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AACnE,YAAY,EAAE,gBAAgB,IAAI,MAAM,EAAE,MAAM,6BAA6B,CAAA;AAC7E,YAAY,EAAE,WAAW,IAAI,KAAK,EAAE,MAAM,wBAAwB,CAAA"}
1
+ {"version":3,"file":"exports.d.ts","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"AACA,OAAO,EACL,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,UAAU,EAAE,qBAAqB,EACrG,UAAU,EAAE,cAAc,GAC3B,MAAM,kBAAkB,CAAA;AACzB,YAAY,EACV,aAAa,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,WAAW,EAAE,kBAAkB,EACxF,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAA;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACpE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAA;AAC7F,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AACjD,YAAY,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AACnE,YAAY,EAAE,gBAAgB,IAAI,MAAM,EAAE,MAAM,6BAA6B,CAAA;AAC7E,YAAY,EAAE,WAAW,IAAI,KAAK,EAAE,MAAM,wBAAwB,CAAA"}
package/build/exports.js CHANGED
@@ -1,4 +1,4 @@
1
- export { handler } from '@owlmeans/client';
1
+ export { handler, lazyHandler, lazyComponent, retryImport, isChunkLoadError, reloadOnce, recoverFromChunkError, chunkUrlOf, cacheBustedUrl, } from '@owlmeans/client';
2
2
  export { config } from '@owlmeans/client-context';
3
3
  export { service } from '@owlmeans/config';
4
4
  export { AppType, HOME, ROOT, BASE, GUEST } from '@owlmeans/context';
@@ -1 +1 @@
1
- {"version":3,"file":"exports.js","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAA;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACpE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAA;AAC7F,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA"}
1
+ {"version":3,"file":"exports.js","sourceRoot":"","sources":["../src/exports.ts"],"names":[],"mappings":"AACA,OAAO,EACL,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,UAAU,EAAE,qBAAqB,EACrG,UAAU,EAAE,cAAc,GAC3B,MAAM,kBAAkB,CAAA;AAIzB,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAA;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACpE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAA;AAC7F,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA"}
@@ -7,9 +7,33 @@ import type { FC } from 'react';
7
7
  * application, with its navigation, inside itself. This screen has no continuation at all — it
8
8
  * either hands something back and closes, or it says what it is waiting for.
9
9
  *
10
- * It also never runs the authorization machine. When there is already a session here, that is the
11
- * answer, and it goes back to the opener immediately — no provider round trip, no PKCE exchange.
12
- * When there is not, it forwards to the dispatcher (`next`), which owns that flow.
10
+ * It also never runs the authorization machine itself: it forwards to the dispatcher (`next`),
11
+ * which owns that flow, and hands back what the dispatcher issued — see {@link surrogateLoginStep}
12
+ * for why a session already stored in this window is not simply handed back.
13
13
  */
14
+ /** What a surrogate window opened to SIGN IN does with the session it may already hold. */
15
+ export declare enum SurrogateLoginStep {
16
+ /** Drop the stored session and authenticate afresh through the dispatcher. */
17
+ Forget = "forget",
18
+ /** Hand the stored session to the opener — there is no dispatcher to authenticate through. */
19
+ Resume = "resume",
20
+ /** Nothing stored: authenticate through the dispatcher. */
21
+ Authenticate = "authenticate"
22
+ }
23
+ /**
24
+ * A sign-in request never reuses the session this window happens to hold.
25
+ *
26
+ * The opener asks for a sign-in only when it has no session its server accepts — and the window it
27
+ * opens does not share its storage: an embedded application's storage is partitioned by the top
28
+ * level site, the popup's is first-party. So the popup can hold a session the server has already
29
+ * REFUSED (its record gone after a restart, revoked, fenced). Handing that back closes the popup
30
+ * at once — a blink — and the opener fails its next request exactly as before, forever, because
31
+ * nothing in the opener can reach the popup's storage to clear it. A fresh round trip costs only
32
+ * redirects while the provider still has a session, and always yields a session the server holds.
33
+ *
34
+ * Only without a dispatcher address is the stored session handed back as it is: there is nothing
35
+ * to authenticate through, and the opener's own 401 handling drops it if it is refused.
36
+ */
37
+ export declare const surrogateLoginStep: (token: string | null | undefined, next: string | null) => SurrogateLoginStep;
14
38
  export declare const SurrogateScreen: FC;
15
39
  //# sourceMappingURL=screen.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"screen.d.ts","sourceRoot":"","sources":["../../src/login/screen.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;AAU/B;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,EAAE,EA+F7B,CAAA"}
1
+ {"version":3,"file":"screen.d.ts","sourceRoot":"","sources":["../../src/login/screen.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;AAW/B;;;;;;;;;;;GAWG;AACH,2FAA2F;AAC3F,oBAAY,kBAAkB;IAC5B,8EAA8E;IAC9E,MAAM,WAAW;IACjB,8FAA8F;IAC9F,MAAM,WAAW;IACjB,2DAA2D;IAC3D,YAAY,iBAAiB;CAC9B;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB,UAAW,MAAM,GAAG,IAAI,GAAG,SAAS,QAAQ,MAAM,GAAG,IAAI,KAAG,kBAO1F,CAAA;AAED,eAAO,MAAM,eAAe,EAAE,EAyG7B,CAAA"}
@@ -3,6 +3,7 @@ import { useCallback, useEffect, useState } from 'react';
3
3
  import { useContext } from '@owlmeans/client';
4
4
  import { useI18nLib } from '@owlmeans/client-i18n';
5
5
  import { LoginIntent, LoginOutcome, LOGIN_INTENT_QUERY, LOGIN_METHOD_QUERY, LOGIN_NEXT_QUERY, } from '@owlmeans/client-auth/login';
6
+ import { USER_ID } from '@owlmeans/client-auth';
6
7
  import { LoginSurrogateView, SurrogateStage } from './view.js';
7
8
  /**
8
9
  * The login window an embedded application opens one level up.
@@ -12,10 +13,41 @@ import { LoginSurrogateView, SurrogateStage } from './view.js';
12
13
  * application, with its navigation, inside itself. This screen has no continuation at all — it
13
14
  * either hands something back and closes, or it says what it is waiting for.
14
15
  *
15
- * It also never runs the authorization machine. When there is already a session here, that is the
16
- * answer, and it goes back to the opener immediately — no provider round trip, no PKCE exchange.
17
- * When there is not, it forwards to the dispatcher (`next`), which owns that flow.
16
+ * It also never runs the authorization machine itself: it forwards to the dispatcher (`next`),
17
+ * which owns that flow, and hands back what the dispatcher issued — see {@link surrogateLoginStep}
18
+ * for why a session already stored in this window is not simply handed back.
18
19
  */
20
+ /** What a surrogate window opened to SIGN IN does with the session it may already hold. */
21
+ export var SurrogateLoginStep;
22
+ (function (SurrogateLoginStep) {
23
+ /** Drop the stored session and authenticate afresh through the dispatcher. */
24
+ SurrogateLoginStep["Forget"] = "forget";
25
+ /** Hand the stored session to the opener — there is no dispatcher to authenticate through. */
26
+ SurrogateLoginStep["Resume"] = "resume";
27
+ /** Nothing stored: authenticate through the dispatcher. */
28
+ SurrogateLoginStep["Authenticate"] = "authenticate";
29
+ })(SurrogateLoginStep || (SurrogateLoginStep = {}));
30
+ /**
31
+ * A sign-in request never reuses the session this window happens to hold.
32
+ *
33
+ * The opener asks for a sign-in only when it has no session its server accepts — and the window it
34
+ * opens does not share its storage: an embedded application's storage is partitioned by the top
35
+ * level site, the popup's is first-party. So the popup can hold a session the server has already
36
+ * REFUSED (its record gone after a restart, revoked, fenced). Handing that back closes the popup
37
+ * at once — a blink — and the opener fails its next request exactly as before, forever, because
38
+ * nothing in the opener can reach the popup's storage to clear it. A fresh round trip costs only
39
+ * redirects while the provider still has a session, and always yields a session the server holds.
40
+ *
41
+ * Only without a dispatcher address is the stored session handed back as it is: there is nothing
42
+ * to authenticate through, and the opener's own 401 handling drops it if it is refused.
43
+ */
44
+ export const surrogateLoginStep = (token, next) => {
45
+ const stored = token != null && token !== '';
46
+ if (!stored) {
47
+ return SurrogateLoginStep.Authenticate;
48
+ }
49
+ return next != null && next !== '' ? SurrogateLoginStep.Forget : SurrogateLoginStep.Resume;
50
+ };
19
51
  export const SurrogateScreen = () => {
20
52
  const context = useContext();
21
53
  const [query] = context.router().useSearchParams();
@@ -63,19 +95,27 @@ export const SurrogateScreen = () => {
63
95
  : SurrogateStage.Failed);
64
96
  return;
65
97
  }
66
- if (token != null && token !== '') {
67
- // Already signed in here. The framed application is the one that asked, so the session
68
- // goes to it rather than being displayed to this window.
98
+ const next = query.get(LOGIN_NEXT_QUERY);
99
+ const step = surrogateLoginStep(token, next);
100
+ if (step === SurrogateLoginStep.Forget) {
101
+ // Dropped from this window's storage only — the web auth service's own clearing path
102
+ // navigates to the dispatcher, and `next` below is exactly where this window goes anyway.
103
+ const auth = context.auth();
104
+ await auth.store().delete(USER_ID);
105
+ auth.token = undefined;
106
+ auth.auth = undefined;
107
+ }
108
+ if (step === SurrogateLoginStep.Resume && token != null) {
109
+ // Signed in here and nowhere to authenticate afresh: hand what there is to the opener.
69
110
  const outcome = await login.resume(token);
70
111
  setStage(outcome === LoginOutcome.Handled ? SurrogateStage.Handing
71
112
  : outcome === LoginOutcome.Orphaned ? SurrogateStage.Orphaned
72
113
  : SurrogateStage.Failed);
73
114
  return;
74
115
  }
75
- // Nothing here yet — the dispatcher owns the authorization round trip, and the provider's
76
- // callback lands there rather than on this route. The method the user already chose travels
77
- // with it, so the dispatcher does not ask a second time in a window with no one to ask.
78
- const next = query.get(LOGIN_NEXT_QUERY);
116
+ // The dispatcher owns the authorization round trip, and the provider's callback lands there
117
+ // rather than on this route. The method the user already chose travels with it, so the
118
+ // dispatcher does not ask a second time in a window with no one to ask.
79
119
  if (next == null || next === '') {
80
120
  setStage(SurrogateStage.Gesture);
81
121
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"screen.js","sourceRoot":"","sources":["../../src/login/screen.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"screen.js","sourceRoot":"","sources":["../../src/login/screen.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAGtE,eAAO,MAAM,MAAM,SAAU,SAAS,SAAS,aAAa,SAkB3D,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,SAAS,EAAE,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,WAC3D,CAAC,SAAS,aAAa,aAAa,SAAS,SACQ,CAAA"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAGtE,eAAO,MAAM,MAAM,SAAU,SAAS,SAAS,aAAa,SAyB3D,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,SAAS,EAAE,CAAC,SAAS,UAAU,CAAC,CAAC,CAAC,WAC3D,CAAC,SAAS,aAAa,aAAa,SAAS,SACQ,CAAA"}
package/build/main.js CHANGED
@@ -19,7 +19,15 @@ export const render = (node, opts) => {
19
19
  createRoot(root).render(node);
20
20
  }
21
21
  };
22
- opts?.onReady ?? true ? window.addEventListener('DOMContentLoaded', _callback) : _callback();
22
+ // Wait for the DOM only while it is still loading: a caller that awaits something first
23
+ // (`await prepareI18n(cfg); render(...)`) can run after `DOMContentLoaded` has already fired,
24
+ // and a listener added then never runs — the app would never mount.
25
+ if ((opts?.onReady ?? true) && document.readyState === 'loading') {
26
+ window.addEventListener('DOMContentLoaded', _callback, { once: true });
27
+ }
28
+ else {
29
+ _callback();
30
+ }
23
31
  };
24
32
  /**
25
33
  * Mount the application.
package/build/main.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAE9C,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,IAAe,EAAE,IAAoB,EAAE,EAAE;IAC9D,MAAM,SAAS,GAAG,GAAG,EAAE;QACrB,MAAM,GAAG,GAAG,IAAI,EAAE,KAAK,IAAI,YAAY,CAAA;QACvC,MAAM,IAAI,GAAG,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,CAAA;QACzC,IAAI,IAAI,EAAE,KAAK,EAAE,CAAC;YAChB,OAAO,CAAC,KAAK,CAAC,uBAAuB,GAAG,EAAE,CAAC,CAAA;QAC7C,CAAC;QACD,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,mCAAmC,GAAG,EAAE,CAAC,CAAA;QAC3D,CAAC;QACD,IAAI,IAAI,EAAE,OAAO,KAAK,IAAI,EAAE,CAAC;YAC3B,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QACzB,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAC/B,CAAC;IACH,CAAC,CAAA;IAED,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAA;AAC9F,CAAC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,OAAU,EAAE,IAAoB,EAAE,QAAoB,EACtD,EAAE,CAAC,MAAM,CAAC,KAAC,MAAM,IAAC,OAAO,EAAE,OAAO,YAAG,QAAQ,GAAU,EAAE,IAAI,CAAC,CAAA"}
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAA;AAE9C,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,IAAe,EAAE,IAAoB,EAAE,EAAE;IAC9D,MAAM,SAAS,GAAG,GAAG,EAAE;QACrB,MAAM,GAAG,GAAG,IAAI,EAAE,KAAK,IAAI,YAAY,CAAA;QACvC,MAAM,IAAI,GAAG,QAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,CAAA;QACzC,IAAI,IAAI,EAAE,KAAK,EAAE,CAAC;YAChB,OAAO,CAAC,KAAK,CAAC,uBAAuB,GAAG,EAAE,CAAC,CAAA;QAC7C,CAAC;QACD,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,mCAAmC,GAAG,EAAE,CAAC,CAAA;QAC3D,CAAC;QACD,IAAI,IAAI,EAAE,OAAO,KAAK,IAAI,EAAE,CAAC;YAC3B,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QACzB,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAC/B,CAAC;IACH,CAAC,CAAA;IAED,wFAAwF;IACxF,8FAA8F;IAC9F,oEAAoE;IACpE,IAAI,CAAC,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC,IAAI,QAAQ,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QACjE,MAAM,CAAC,gBAAgB,CAAC,kBAAkB,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;IACxE,CAAC;SAAM,CAAC;QACN,SAAS,EAAE,CAAA;IACb,CAAC;AACH,CAAC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,OAAU,EAAE,IAAoB,EAAE,QAAoB,EACtD,EAAE,CAAC,MAAM,CAAC,KAAC,MAAM,IAAC,OAAO,EAAE,OAAO,YAAG,QAAQ,GAAU,EAAE,IAAI,CAAC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/web-client",
3
- "version": "0.1.18-rc.50",
3
+ "version": "0.1.18-rc.52",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -21,22 +21,22 @@
21
21
  }
22
22
  },
23
23
  "dependencies": {
24
- "@owlmeans/auth": "^0.1.18-rc.35",
25
- "@owlmeans/auth-common": "^0.1.18-rc.37",
26
- "@owlmeans/client": "^0.1.18-rc.40",
27
- "@owlmeans/client-auth": "^0.1.18-rc.47",
28
- "@owlmeans/client-context": "^0.1.18-rc.38",
29
- "@owlmeans/client-i18n": "^0.1.18-rc.39",
30
- "@owlmeans/client-entrypoint": "^0.1.18-rc.37",
31
- "@owlmeans/client-resource": "^0.1.18-rc.37",
32
- "@owlmeans/client-route": "^0.1.18-rc.38",
33
- "@owlmeans/config": "^0.1.18-rc.37",
24
+ "@owlmeans/auth": "^0.1.18-rc.36",
25
+ "@owlmeans/auth-common": "^0.1.18-rc.38",
26
+ "@owlmeans/client": "^0.1.18-rc.42",
27
+ "@owlmeans/client-auth": "^0.1.18-rc.49",
28
+ "@owlmeans/client-context": "^0.1.18-rc.39",
29
+ "@owlmeans/client-i18n": "^0.1.18-rc.41",
30
+ "@owlmeans/client-entrypoint": "^0.1.18-rc.38",
31
+ "@owlmeans/client-resource": "^0.1.18-rc.38",
32
+ "@owlmeans/client-route": "^0.1.18-rc.39",
33
+ "@owlmeans/config": "^0.1.18-rc.38",
34
34
  "@owlmeans/context": "^0.1.18-rc.32",
35
- "@owlmeans/error": "^0.1.18-rc.35",
36
- "@owlmeans/i18n": "^0.1.18-rc.36",
35
+ "@owlmeans/error": "^0.1.18-rc.36",
36
+ "@owlmeans/i18n": "^0.1.18-rc.37",
37
37
  "@owlmeans/route": "^0.1.18-rc.33",
38
- "@owlmeans/web-db": "^0.1.18-rc.38",
39
- "@owlmeans/web-router": "^0.1.18-rc.38",
38
+ "@owlmeans/web-db": "^0.1.18-rc.39",
39
+ "@owlmeans/web-router": "^0.1.18-rc.40",
40
40
  "react-dropzone": "^14.3.5"
41
41
  },
42
42
  "peerDependencies": {
package/src/exports.ts CHANGED
@@ -1,5 +1,11 @@
1
1
 
2
- export { handler } from '@owlmeans/client'
2
+ export {
3
+ handler, lazyHandler, lazyComponent, retryImport, isChunkLoadError, reloadOnce, recoverFromChunkError,
4
+ chunkUrlOf, cacheBustedUrl,
5
+ } from '@owlmeans/client'
6
+ export type {
7
+ LazyComponent, LazyComponentOptions, LazyErrorRenderer, LazyHandler, RetryImportOptions
8
+ } from '@owlmeans/client'
3
9
  export { config } from '@owlmeans/client-context'
4
10
  export { service } from '@owlmeans/config'
5
11
  export { AppType, HOME, ROOT, BASE, GUEST } from '@owlmeans/context'
@@ -6,6 +6,7 @@ import {
6
6
  LoginIntent, LoginOutcome, LOGIN_INTENT_QUERY, LOGIN_METHOD_QUERY, LOGIN_NEXT_QUERY,
7
7
  } from '@owlmeans/client-auth/login'
8
8
  import type { LoginService } from '@owlmeans/client-auth/login'
9
+ import { USER_ID } from '@owlmeans/client-auth'
9
10
  import type { AppContext } from '../types.js'
10
11
  import { LoginSurrogateView, SurrogateStage } from './view.js'
11
12
 
@@ -17,10 +18,43 @@ import { LoginSurrogateView, SurrogateStage } from './view.js'
17
18
  * application, with its navigation, inside itself. This screen has no continuation at all — it
18
19
  * either hands something back and closes, or it says what it is waiting for.
19
20
  *
20
- * It also never runs the authorization machine. When there is already a session here, that is the
21
- * answer, and it goes back to the opener immediately — no provider round trip, no PKCE exchange.
22
- * When there is not, it forwards to the dispatcher (`next`), which owns that flow.
21
+ * It also never runs the authorization machine itself: it forwards to the dispatcher (`next`),
22
+ * which owns that flow, and hands back what the dispatcher issued — see {@link surrogateLoginStep}
23
+ * for why a session already stored in this window is not simply handed back.
23
24
  */
25
+ /** What a surrogate window opened to SIGN IN does with the session it may already hold. */
26
+ export enum SurrogateLoginStep {
27
+ /** Drop the stored session and authenticate afresh through the dispatcher. */
28
+ Forget = 'forget',
29
+ /** Hand the stored session to the opener — there is no dispatcher to authenticate through. */
30
+ Resume = 'resume',
31
+ /** Nothing stored: authenticate through the dispatcher. */
32
+ Authenticate = 'authenticate',
33
+ }
34
+
35
+ /**
36
+ * A sign-in request never reuses the session this window happens to hold.
37
+ *
38
+ * The opener asks for a sign-in only when it has no session its server accepts — and the window it
39
+ * opens does not share its storage: an embedded application's storage is partitioned by the top
40
+ * level site, the popup's is first-party. So the popup can hold a session the server has already
41
+ * REFUSED (its record gone after a restart, revoked, fenced). Handing that back closes the popup
42
+ * at once — a blink — and the opener fails its next request exactly as before, forever, because
43
+ * nothing in the opener can reach the popup's storage to clear it. A fresh round trip costs only
44
+ * redirects while the provider still has a session, and always yields a session the server holds.
45
+ *
46
+ * Only without a dispatcher address is the stored session handed back as it is: there is nothing
47
+ * to authenticate through, and the opener's own 401 handling drops it if it is refused.
48
+ */
49
+ export const surrogateLoginStep = (token: string | null | undefined, next: string | null): SurrogateLoginStep => {
50
+ const stored = token != null && token !== ''
51
+ if (!stored) {
52
+ return SurrogateLoginStep.Authenticate
53
+ }
54
+
55
+ return next != null && next !== '' ? SurrogateLoginStep.Forget : SurrogateLoginStep.Resume
56
+ }
57
+
24
58
  export const SurrogateScreen: FC = () => {
25
59
  const context = useContext() as unknown as AppContext
26
60
  const [query] = context.router().useSearchParams()
@@ -78,9 +112,20 @@ export const SurrogateScreen: FC = () => {
78
112
  return
79
113
  }
80
114
 
81
- if (token != null && token !== '') {
82
- // Already signed in here. The framed application is the one that asked, so the session
83
- // goes to it rather than being displayed to this window.
115
+ const next = query.get(LOGIN_NEXT_QUERY)
116
+ const step = surrogateLoginStep(token, next)
117
+
118
+ if (step === SurrogateLoginStep.Forget) {
119
+ // Dropped from this window's storage only — the web auth service's own clearing path
120
+ // navigates to the dispatcher, and `next` below is exactly where this window goes anyway.
121
+ const auth = context.auth()
122
+ await auth.store().delete(USER_ID)
123
+ auth.token = undefined
124
+ auth.auth = undefined
125
+ }
126
+
127
+ if (step === SurrogateLoginStep.Resume && token != null) {
128
+ // Signed in here and nowhere to authenticate afresh: hand what there is to the opener.
84
129
  const outcome = await login.resume(token)
85
130
  setStage(
86
131
  outcome === LoginOutcome.Handled ? SurrogateStage.Handing
@@ -90,10 +135,9 @@ export const SurrogateScreen: FC = () => {
90
135
  return
91
136
  }
92
137
 
93
- // Nothing here yet — the dispatcher owns the authorization round trip, and the provider's
94
- // callback lands there rather than on this route. The method the user already chose travels
95
- // with it, so the dispatcher does not ask a second time in a window with no one to ask.
96
- const next = query.get(LOGIN_NEXT_QUERY)
138
+ // The dispatcher owns the authorization round trip, and the provider's callback lands there
139
+ // rather than on this route. The method the user already chose travels with it, so the
140
+ // dispatcher does not ask a second time in a window with no one to ask.
97
141
  if (next == null || next === '') {
98
142
  setStage(SurrogateStage.Gesture)
99
143
  return
package/src/main.tsx CHANGED
@@ -22,7 +22,14 @@ export const render = (node: ReactNode, opts?: RenderOptions) => {
22
22
  }
23
23
  }
24
24
 
25
- opts?.onReady ?? true ? window.addEventListener('DOMContentLoaded', _callback) : _callback()
25
+ // Wait for the DOM only while it is still loading: a caller that awaits something first
26
+ // (`await prepareI18n(cfg); render(...)`) can run after `DOMContentLoaded` has already fired,
27
+ // and a listener added then never runs — the app would never mount.
28
+ if ((opts?.onReady ?? true) && document.readyState === 'loading') {
29
+ window.addEventListener('DOMContentLoaded', _callback, { once: true })
30
+ } else {
31
+ _callback()
32
+ }
26
33
  }
27
34
 
28
35
  /**
@@ -50,3 +50,22 @@ describe('centeredPopupFeatures', () => {
50
50
  expect(centeredPopupFeatures(520, 760)).toBe('popup=yes,width=520,height=760,left=0,top=0')
51
51
  })
52
52
  })
53
+
54
+ describe('surrogateLoginStep', () => {
55
+ test('a stored session is forgotten when there is a dispatcher to authenticate through', async () => {
56
+ const { surrogateLoginStep, SurrogateLoginStep } = await import('../src/login/screen.js')
57
+ expect(surrogateLoginStep('OIDC-WRAPPED-TOKEN abc', '/dispatcher?x=1')).toBe(SurrogateLoginStep.Forget)
58
+ })
59
+
60
+ test('a stored session is handed back only when there is no dispatcher address', async () => {
61
+ const { surrogateLoginStep, SurrogateLoginStep } = await import('../src/login/screen.js')
62
+ expect(surrogateLoginStep('OIDC-WRAPPED-TOKEN abc', null)).toBe(SurrogateLoginStep.Resume)
63
+ expect(surrogateLoginStep('OIDC-WRAPPED-TOKEN abc', '')).toBe(SurrogateLoginStep.Resume)
64
+ })
65
+
66
+ test('nothing stored authenticates, with or without a dispatcher address', async () => {
67
+ const { surrogateLoginStep, SurrogateLoginStep } = await import('../src/login/screen.js')
68
+ expect(surrogateLoginStep(null, '/dispatcher')).toBe(SurrogateLoginStep.Authenticate)
69
+ expect(surrogateLoginStep('', null)).toBe(SurrogateLoginStep.Authenticate)
70
+ })
71
+ })