@jskit-ai/shell-web 0.1.250 → 0.1.252

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 (47) hide show
  1. package/fixtures/adaptive-shell/src/main.js +6 -3
  2. package/package.json +2 -2
  3. package/patterns/application-shell/PATTERN.md +18 -4
  4. package/patterns/application-shell/example/src/pages/home/index.vue +15 -5
  5. package/patterns/application-shell/example/tests/e2e/adaptive-shell.spec.ts +1 -1
  6. package/src/architecture/inspectVueArchitecture.js +1092 -336
  7. package/src/client/asyncModuleRecovery/runtime.js +116 -0
  8. package/src/client/components/ShellLayout.vue +18 -288
  9. package/src/client/components/ShellMenuLinkItem.vue +3 -21
  10. package/src/client/components/ShellOutlet.vue +2 -24
  11. package/src/client/components/ShellRouteTransition.vue +16 -114
  12. package/src/client/components/ShellSurfaceAwareMenuLinkItem.vue +5 -100
  13. package/src/client/components/ShellTabLinkItem.vue +5 -101
  14. package/src/client/composables/useShellNavigationLink.js +85 -0
  15. package/src/client/composables/useShellPullToRefresh.js +260 -0
  16. package/src/client/error/index.js +1 -1
  17. package/src/client/error/inject.js +4 -1
  18. package/src/client/error/policy.js +4 -13
  19. package/src/client/error/presenters.js +4 -9
  20. package/src/client/error/store.js +1 -1
  21. package/src/client/navigation/linkResolver.js +5 -27
  22. package/src/client/navigation/usePaths.js +1 -21
  23. package/src/client/placement/index.js +2 -0
  24. package/src/client/placement/inject.js +4 -1
  25. package/src/client/placement/runtime.js +2 -4
  26. package/src/client/placement/surfaceContext.js +11 -8
  27. package/src/client/placement/validators.js +18 -65
  28. package/src/client/providers/ShellWebClientProvider.js +100 -356
  29. package/src/client/requestRecovery/runtime.js +29 -77
  30. package/src/client/runtime/bootstrapRuntime.js +5 -4
  31. package/src/client/runtime/refreshRuntime.js +122 -0
  32. package/src/client/support/gestureTargets.js +26 -0
  33. package/src/client/support/menuLinkTarget.js +35 -19
  34. package/src/client/support/routeParams.js +26 -0
  35. package/src/client/support/routeSwipeGesture.js +88 -0
  36. package/src/client/support/routeTransitionKey.js +1 -3
  37. package/src/client/support/shellLayoutClass.js +32 -0
  38. package/test/bootstrapRuntime.test.js +98 -0
  39. package/test/errorActions.test.js +2 -1
  40. package/test/errorRuntime.test.js +30 -0
  41. package/test/linkResolver.test.js +23 -1
  42. package/test/menuLinkTarget.test.js +77 -0
  43. package/test/routeSwipeGesture.test.js +49 -0
  44. package/test/settingsPlacementContract.test.js +16 -6
  45. package/test/shellGestureSupport.test.js +46 -0
  46. package/test/surfaceContext.test.js +53 -0
  47. package/test/useShellPullToRefresh.test.js +123 -0
@@ -1,13 +1,13 @@
1
1
  import { isRecord, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
2
2
  import {
3
+ WEB_PLACEMENT_SURFACE_ANY,
3
4
  normalizePlacementKind,
5
+ normalizePlacementSurfaceId,
6
+ normalizePlacementSurfaces as normalizeKernelPlacementSurfaces,
4
7
  normalizePlacementTopologyDefinition,
5
- normalizeSemanticPlacementId,
6
8
  resolvePlacementTargetReference
7
9
  } from "@jskit-ai/kernel/shared/support/shellLayoutTargets";
8
10
 
9
- const WEB_PLACEMENT_SURFACE_ANY = "*";
10
-
11
11
  function isRenderableComponent(value) {
12
12
  if (typeof value === "function") {
13
13
  return true;
@@ -16,41 +16,16 @@ function isRenderableComponent(value) {
16
16
  }
17
17
 
18
18
  function normalizeSurface(value) {
19
- const normalized = normalizeText(value).toLowerCase();
20
- if (!normalized) {
21
- return WEB_PLACEMENT_SURFACE_ANY;
22
- }
23
- if (normalized === WEB_PLACEMENT_SURFACE_ANY) {
24
- return WEB_PLACEMENT_SURFACE_ANY;
25
- }
26
- return normalized;
27
- }
28
-
29
- function isValidSurfaceIdToken(value = "") {
30
- return /^[a-z0-9]+(?:[._-][a-z0-9]+)*$/.test(value);
19
+ return normalizeText(value).toLowerCase() || WEB_PLACEMENT_SURFACE_ANY;
31
20
  }
32
21
 
33
22
  function normalizePlacementSurface(value, { strict = false, source = "placement" } = {}) {
34
- const normalized = normalizeText(value).toLowerCase();
35
- if (!normalized) {
36
- if (strict) {
37
- throw new TypeError(`${source} requires surface id or "*".`);
38
- }
39
- return "";
40
- }
41
-
42
- if (normalized === WEB_PLACEMENT_SURFACE_ANY) {
43
- return WEB_PLACEMENT_SURFACE_ANY;
44
- }
45
-
46
- if (isValidSurfaceIdToken(normalized)) {
47
- return normalized;
48
- }
49
-
50
- if (strict) {
51
- throw new TypeError(`${source} surface "${normalized}" is invalid.`);
23
+ const surface = normalizePlacementSurfaceId(value);
24
+ if (!surface && strict) {
25
+ const normalized = normalizeText(value).toLowerCase();
26
+ throw new TypeError(normalized ? `${source} surface "${normalized}" is invalid.` : `${source} requires surface id or "*".`);
52
27
  }
53
- return "";
28
+ return surface;
54
29
  }
55
30
 
56
31
  function toInteger(value, fallback = 1000) {
@@ -74,36 +49,16 @@ function normalizePlacementTarget(value, { strict = false, source = "placement"
74
49
  }
75
50
 
76
51
  function normalizePlacementSurfaces(value, { strict = false, source = "placement" } = {}) {
77
- const candidates = Array.isArray(value) ? value : value === undefined || value === null ? [] : [value];
78
- if (candidates.length < 1) {
79
- return Object.freeze([WEB_PLACEMENT_SURFACE_ANY]);
80
- }
81
-
82
- const normalized = [];
83
- const seen = new Set();
84
- for (const candidate of candidates) {
85
- const surface = normalizePlacementSurface(candidate, {
86
- strict,
87
- source
88
- });
89
- if (!surface || seen.has(surface)) {
90
- continue;
91
- }
92
- if (surface === WEB_PLACEMENT_SURFACE_ANY) {
93
- return Object.freeze([WEB_PLACEMENT_SURFACE_ANY]);
94
- }
95
- seen.add(surface);
96
- normalized.push(surface);
97
- }
98
-
99
- if (normalized.length < 1) {
100
- if (strict) {
101
- throw new TypeError(`${source} requires at least one valid surface id.`);
52
+ if (strict) {
53
+ // Strict callers fail on the first invalid surface the kernel would otherwise drop; "*" ends the list.
54
+ const candidates = Array.isArray(value) ? value : value === undefined || value === null ? [] : [value];
55
+ for (const candidate of candidates) {
56
+ if (normalizePlacementSurface(candidate, { strict, source }) === WEB_PLACEMENT_SURFACE_ANY) {
57
+ break;
58
+ }
102
59
  }
103
- return Object.freeze([WEB_PLACEMENT_SURFACE_ANY]);
104
60
  }
105
-
106
- return Object.freeze(normalized);
61
+ return normalizeKernelPlacementSurfaces(value);
107
62
  }
108
63
 
109
64
  function normalizePlacementDefinition(value, { strict = false, source = "placement" } = {}) {
@@ -186,7 +141,6 @@ function definePlacementTopology(value = {}) {
186
141
  }
187
142
 
188
143
  export {
189
- isRecord,
190
144
  isRenderableComponent,
191
145
  normalizeSurface,
192
146
  normalizePlacementSurface,
@@ -194,6 +148,5 @@ export {
194
148
  normalizePlacementSurfaces,
195
149
  normalizePlacementDefinition,
196
150
  definePlacement,
197
- definePlacementTopology,
198
- normalizeSemanticPlacementId
151
+ definePlacementTopology
199
152
  };
@@ -1,12 +1,6 @@
1
1
  import { getClientAppConfig } from "@jskit-ai/kernel/client";
2
2
  import { defineProvider } from "@jskit-ai/kernel/shared/capabilities";
3
- import {
4
- createAsyncModuleRecoveryState,
5
- guardedReloadApp,
6
- installAsyncModuleRecoveryHandlers,
7
- isDynamicImportError,
8
- notifyAsyncModuleLoadError
9
- } from "@jskit-ai/kernel/client/asyncModuleRecovery";
3
+ import { isDynamicImportError } from "@jskit-ai/kernel/client/asyncModuleRecovery";
10
4
  import {
11
5
  isMissingDynamicModule,
12
6
  notifyDynamicImportFailure
@@ -14,6 +8,7 @@ import {
14
8
  import {
15
9
  SHELL_ASYNC_MODULE_RECOVERY_RUNTIME_KEY
16
10
  } from "../asyncModuleRecovery/inject.js";
11
+ import { createShellAsyncModuleRecoveryRuntime } from "../asyncModuleRecovery/runtime.js";
17
12
  import {
18
13
  SHELL_REQUEST_RECOVERY_RUNTIME_KEY
19
14
  } from "../requestRecovery/inject.js";
@@ -33,85 +28,104 @@ import {
33
28
  createMaterialDialogPresenter,
34
29
  createMaterialSnackbarPresenter
35
30
  } from "../error/presenters.js";
31
+ import { SHELL_WEB_ERROR_RUNTIME_KEY } from "../error/inject.js";
36
32
  import {
37
33
  createErrorRuntime
38
34
  } from "../error/runtime.js";
39
35
  import {
40
36
  createErrorPresentationStore
41
37
  } from "../error/store.js";
38
+ import { SHELL_WEB_PLACEMENT_RUNTIME_KEY } from "../placement/inject.js";
42
39
  import { createWebPlacementRuntime } from "../placement/runtime.js";
43
40
  import { useShellErrorPresentationStore } from "../stores/useShellErrorPresentationStore.js";
44
41
  import { buildSurfaceConfigContext } from "../placement/surfaceContext.js";
45
42
  import { createShellBootstrapRuntime } from "../runtime/bootstrapRuntime.js";
43
+ import {
44
+ SHELL_WEB_REFRESH_RUNTIME_KEY,
45
+ createShellRefreshRuntime
46
+ } from "../runtime/refreshRuntime.js";
46
47
  import { createBootstrapPayloadHandlerRegistry } from "../bootstrap/bootstrapPayloadHandlerRegistry.js";
47
48
  import { resolveBootstrapErrorStatusCode } from "../bootstrap/bootstrapErrorStatus.js";
48
49
 
49
- // Keep this constant for diagnostics, but keep import() below as a literal string so Vite can statically analyze it.
50
+ // Keep these constants for diagnostics, but keep each import() below as a literal string so Vite can statically analyze it.
50
51
  const APP_PLACEMENT_MODULE_SPECIFIER = "/src/placement.js";
51
52
  const APP_PLACEMENT_TOPOLOGY_MODULE_SPECIFIER = "/src/placementTopology.js";
52
53
  const APP_ERROR_MODULE_SPECIFIER = "/src/error.js";
53
- const ASYNC_MODULE_RELOAD_FAILURE_MESSAGE =
54
- "The app cannot reload because the app server is not reachable. Restart the server, then click Reload.";
55
54
 
56
- async function loadAppPlacementDefinitions(logger, asyncModuleRecoveryRuntime = null) {
55
+ // Loads an optional app module's default export. `resolve` returns the usable value, or undefined when the
56
+ // export has the wrong shape. A missing module silently yields `fallback`; any other failure is reported.
57
+ async function loadAppModule({
58
+ importModule,
59
+ specifier,
60
+ label,
61
+ resolve,
62
+ invalidExportMessage = "",
63
+ loadFailureMessage,
64
+ fallback,
65
+ logger,
66
+ asyncModuleRecoveryRuntime = null
67
+ }) {
57
68
  try {
58
- const moduleNamespace = await import("/src/placement.js");
69
+ const moduleNamespace = await importModule();
59
70
  const exported = moduleNamespace?.default;
60
- const resolved = typeof exported === "function" ? exported() : exported;
61
- if (Array.isArray(resolved)) {
71
+ const resolved = await resolve(exported);
72
+ if (resolved !== undefined) {
62
73
  return resolved;
63
74
  }
64
75
 
65
76
  logger.warn(
66
77
  {
67
- module: APP_PLACEMENT_MODULE_SPECIFIER,
78
+ module: specifier,
68
79
  exportedType: typeof exported
69
80
  },
70
- "App placement module default export did not resolve to an array; using empty list."
81
+ invalidExportMessage
71
82
  );
72
83
  } catch (error) {
73
- if (isMissingDynamicModule(error, APP_PLACEMENT_MODULE_SPECIFIER)) {
74
- return [];
84
+ if (isMissingDynamicModule(error, specifier)) {
85
+ return fallback;
75
86
  }
76
- notifyDynamicImportFailure(asyncModuleRecoveryRuntime, error, {
77
- label: "Placement config"
78
- });
87
+ notifyDynamicImportFailure(asyncModuleRecoveryRuntime, error, { label });
79
88
 
80
89
  logger.warn(
81
90
  {
82
- module: APP_PLACEMENT_MODULE_SPECIFIER,
91
+ module: specifier,
83
92
  error: String(error?.message || error || "unknown error")
84
93
  },
85
- "Failed to load app placement module; using empty list."
94
+ loadFailureMessage
86
95
  );
87
96
  }
88
97
 
89
- return [];
98
+ return fallback;
90
99
  }
91
100
 
92
- async function loadAppPlacementTopology(logger, asyncModuleRecoveryRuntime = null) {
93
- try {
94
- const moduleNamespace = await import("/src/placementTopology.js");
95
- const exported = moduleNamespace?.default;
96
- return resolveAppPlacementTopologyExport(exported, logger);
97
- } catch (error) {
98
- if (isMissingDynamicModule(error, APP_PLACEMENT_TOPOLOGY_MODULE_SPECIFIER)) {
99
- return [];
100
- }
101
- notifyDynamicImportFailure(asyncModuleRecoveryRuntime, error, {
102
- label: "Placement topology"
103
- });
104
-
105
- logger.warn(
106
- {
107
- module: APP_PLACEMENT_TOPOLOGY_MODULE_SPECIFIER,
108
- error: String(error?.message || error || "unknown error")
109
- },
110
- "Failed to load app placement topology module; using empty topology."
111
- );
112
- }
101
+ function loadAppPlacementDefinitions(logger, asyncModuleRecoveryRuntime = null) {
102
+ return loadAppModule({
103
+ importModule: () => import("/src/placement.js"),
104
+ specifier: APP_PLACEMENT_MODULE_SPECIFIER,
105
+ label: "Placement config",
106
+ resolve(exported) {
107
+ const resolved = typeof exported === "function" ? exported() : exported;
108
+ return Array.isArray(resolved) ? resolved : undefined;
109
+ },
110
+ invalidExportMessage: "App placement module default export did not resolve to an array; using empty list.",
111
+ loadFailureMessage: "Failed to load app placement module; using empty list.",
112
+ fallback: [],
113
+ logger,
114
+ asyncModuleRecoveryRuntime
115
+ });
116
+ }
113
117
 
114
- return [];
118
+ function loadAppPlacementTopology(logger, asyncModuleRecoveryRuntime = null) {
119
+ return loadAppModule({
120
+ importModule: () => import("/src/placementTopology.js"),
121
+ specifier: APP_PLACEMENT_TOPOLOGY_MODULE_SPECIFIER,
122
+ label: "Placement topology",
123
+ resolve: (exported) => resolveAppPlacementTopologyExport(exported, logger),
124
+ loadFailureMessage: "Failed to load app placement topology module; using empty topology.",
125
+ fallback: [],
126
+ logger,
127
+ asyncModuleRecoveryRuntime
128
+ });
115
129
  }
116
130
 
117
131
  function resolveAppPlacementTopologyExport(exported, logger) {
@@ -146,178 +160,21 @@ function createErrorConfigToolkit(errorRuntime) {
146
160
  });
147
161
  }
148
162
 
149
- async function loadAppErrorConfig(logger, errorRuntime, asyncModuleRecoveryRuntime = null) {
150
- try {
151
- const moduleNamespace = await import("/src/error.js");
152
- const exported = moduleNamespace?.default;
153
- const toolkit = createErrorConfigToolkit(errorRuntime);
154
- const resolved = typeof exported === "function" ? await exported(toolkit) : exported;
155
-
156
- if (isRecord(resolved)) {
157
- return resolved;
158
- }
159
-
160
- logger.warn(
161
- {
162
- module: APP_ERROR_MODULE_SPECIFIER,
163
- exportedType: typeof exported
164
- },
165
- "App error module default export did not resolve to an object; using shell-web defaults."
166
- );
167
- } catch (error) {
168
- if (isMissingDynamicModule(error, APP_ERROR_MODULE_SPECIFIER)) {
169
- return {};
170
- }
171
- notifyDynamicImportFailure(asyncModuleRecoveryRuntime, error, {
172
- label: "Error config"
173
- });
174
-
175
- logger.warn(
176
- {
177
- module: APP_ERROR_MODULE_SPECIFIER,
178
- error: String(error?.message || error || "unknown error")
179
- },
180
- "Failed to load app error module; using shell-web defaults."
181
- );
182
- }
183
-
184
- return {};
185
- }
186
-
187
- function applyAppErrorConfig(errorRuntime, errorConfig = {}) {
188
- const source = isRecord(errorConfig) ? errorConfig : {};
189
- const configPayload = {};
190
-
191
- if (Array.isArray(source.presenters) && source.presenters.length > 0) {
192
- configPayload.presenters = source.presenters;
193
- }
194
-
195
- if (Object.prototype.hasOwnProperty.call(source, "policy")) {
196
- configPayload.policy = source.policy;
197
- }
198
-
199
- if (Object.prototype.hasOwnProperty.call(source, "defaultPresenterId")) {
200
- configPayload.defaultPresenterId = source.defaultPresenterId;
201
- }
202
-
203
- if (Object.keys(configPayload).length > 0) {
204
- errorRuntime.configure(configPayload);
205
- return;
206
- }
207
-
208
- errorRuntime.assertBootReady();
209
- }
210
-
211
- function isPullRefreshQuery(query = null) {
212
- const meta = isRecord(query?.meta) ? query.meta : {};
213
- if (meta.jskitRefresh === "pull") {
214
- return true;
215
- }
216
- if (meta.jskitRefresh === false) {
217
- return false;
218
- }
219
-
220
- const jskitMeta = isRecord(meta.jskit) ? meta.jskit : {};
221
- return jskitMeta.refreshOnPull !== false;
222
- }
223
-
224
- function createShellRefreshRuntime({
225
- bootstrapRuntime = null,
226
- queryClient = null,
227
- errorRuntime = null,
228
- logger = null
229
- } = {}) {
230
- const runtimeLogger = createSharedProviderLogger(logger);
231
- let refreshQueue = Promise.resolve(null);
232
-
233
- async function refreshBootstrap(reason) {
234
- if (!bootstrapRuntime || typeof bootstrapRuntime.refresh !== "function") {
235
- return false;
236
- }
237
-
238
- await bootstrapRuntime.refresh(reason);
239
- return true;
240
- }
241
-
242
- async function refetchPullQueries() {
243
- if (!queryClient || typeof queryClient.refetchQueries !== "function") {
244
- return false;
245
- }
246
-
247
- await queryClient.refetchQueries(
248
- {
249
- type: "active",
250
- predicate: isPullRefreshQuery
251
- },
252
- {
253
- throwOnError: false
254
- }
255
- );
256
- return true;
257
- }
258
-
259
- function reportRefreshFailure(error) {
260
- runtimeLogger.warn(
261
- {
262
- error: String(error?.message || error || "unknown error")
263
- },
264
- "Shell refresh failed."
265
- );
266
-
267
- if (!errorRuntime || typeof errorRuntime.report !== "function") {
268
- return;
269
- }
270
-
271
- errorRuntime.report({
272
- source: "shell-web.refresh",
273
- message: "Unable to refresh. Check the connection and try again.",
274
- intent: "app-recoverable",
275
- severity: "error",
276
- dedupeKey: "shell-web.refresh.failed",
277
- dedupeWindowMs: 2000,
278
- action: {
279
- label: "Retry",
280
- dismissOnRun: true,
281
- handler() {
282
- void refresh("retry");
283
- }
284
- }
285
- });
286
- }
287
-
288
- async function performRefresh(reason = "manual") {
289
- const normalizedReason = String(reason || "manual").trim() || "manual";
290
- try {
291
- const [bootstrapRefreshed, queriesRefetched] = await Promise.all([
292
- refreshBootstrap(normalizedReason),
293
- refetchPullQueries()
294
- ]);
295
-
296
- return Object.freeze({
297
- reason: normalizedReason,
298
- bootstrapRefreshed,
299
- queriesRefetched
300
- });
301
- } catch (error) {
302
- reportRefreshFailure(error);
303
- return Object.freeze({
304
- reason: normalizedReason,
305
- bootstrapRefreshed: false,
306
- queriesRefetched: false,
307
- error
308
- });
309
- }
310
- }
311
-
312
- function refresh(reason = "manual") {
313
- refreshQueue = refreshQueue
314
- .catch(() => null)
315
- .then(() => performRefresh(reason));
316
- return refreshQueue;
317
- }
318
-
319
- return Object.freeze({
320
- refresh
163
+ function loadAppErrorConfig(logger, errorRuntime, asyncModuleRecoveryRuntime = null) {
164
+ return loadAppModule({
165
+ importModule: () => import("/src/error.js"),
166
+ specifier: APP_ERROR_MODULE_SPECIFIER,
167
+ label: "Error config",
168
+ async resolve(exported) {
169
+ const toolkit = createErrorConfigToolkit(errorRuntime);
170
+ const resolved = typeof exported === "function" ? await exported(toolkit) : exported;
171
+ return isRecord(resolved) ? resolved : undefined;
172
+ },
173
+ invalidExportMessage: "App error module default export did not resolve to an object; using shell-web defaults.",
174
+ loadFailureMessage: "Failed to load app error module; using shell-web defaults.",
175
+ fallback: {},
176
+ logger,
177
+ asyncModuleRecoveryRuntime
321
178
  });
322
179
  }
323
180
 
@@ -388,109 +245,6 @@ function installRouterErrorBridge(router, errorRuntime, logger) {
388
245
  });
389
246
  }
390
247
 
391
- function createShellAsyncModuleRecoveryRuntime({
392
- router = null,
393
- errorRuntime = null,
394
- logger = null
395
- } = {}) {
396
- const runtimeLogger = createSharedProviderLogger(logger);
397
- const state = createAsyncModuleRecoveryState();
398
- let installedRecovery = null;
399
-
400
- function report(nextState = state) {
401
- if (!errorRuntime || typeof errorRuntime.report !== "function") {
402
- runtimeLogger.warn(
403
- {
404
- label: String(nextState?.label || "")
405
- },
406
- "Async module recovery could not report because the shell error runtime is unavailable."
407
- );
408
- return null;
409
- }
410
-
411
- try {
412
- return errorRuntime.report({
413
- source: "shell-web.async-module-recovery",
414
- message: String(nextState?.message || "A required app module could not load."),
415
- cause: nextState?.error || null,
416
- intent: "app-recoverable",
417
- severity: "warning",
418
- dedupeKey: `shell-web.async-module-recovery.${Number(nextState?.attempt || 0)}`,
419
- action: {
420
- label: "Reload",
421
- dismissOnRun: true,
422
- handler() {
423
- return reload();
424
- }
425
- }
426
- });
427
- } catch (error) {
428
- runtimeLogger.error(
429
- {
430
- label: String(nextState?.label || ""),
431
- error: String(error?.message || error || "unknown error")
432
- },
433
- "Async module recovery could not report through the shell error runtime."
434
- );
435
- return null;
436
- }
437
- }
438
-
439
- async function reload() {
440
- const reloaded = await guardedReloadApp({
441
- state,
442
- label: "App",
443
- message: ASYNC_MODULE_RELOAD_FAILURE_MESSAGE
444
- });
445
- if (!reloaded) {
446
- report(state);
447
- }
448
- return reloaded;
449
- }
450
-
451
- function notify(error = null, {
452
- label = "App module",
453
- message = "",
454
- stale = isDynamicImportError(error)
455
- } = {}) {
456
- notifyAsyncModuleLoadError(state, error, {
457
- label,
458
- message,
459
- retry: state.retry,
460
- stale
461
- });
462
- return report(state);
463
- }
464
-
465
- function install() {
466
- if (installedRecovery) {
467
- return installedRecovery;
468
- }
469
-
470
- installedRecovery = installAsyncModuleRecoveryHandlers({
471
- state,
472
- label: "App module",
473
- router,
474
- onNotify: report
475
- });
476
- return installedRecovery;
477
- }
478
-
479
- function dispose() {
480
- installedRecovery?.dispose?.();
481
- installedRecovery = null;
482
- }
483
-
484
- return Object.freeze({
485
- dispose,
486
- install,
487
- notify,
488
- reload,
489
- report,
490
- state
491
- });
492
- }
493
-
494
248
  const ShellWebClientProvider = defineProvider({
495
249
  id: "shell.web.client",
496
250
  requires: {
@@ -588,39 +342,33 @@ const ShellWebClientProvider = defineProvider({
588
342
  } = outputs.shell;
589
343
  asyncModuleRecoveryRuntime.install();
590
344
 
591
- if (placementRuntime && typeof placementRuntime.replacePlacements === "function") {
592
- const placementTopology = await loadAppPlacementTopology(logger, asyncModuleRecoveryRuntime);
593
- if (typeof placementRuntime.replacePlacementTopology === "function") {
594
- placementRuntime.replacePlacementTopology(placementTopology, { source: APP_PLACEMENT_TOPOLOGY_MODULE_SPECIFIER });
345
+ const placementTopology = await loadAppPlacementTopology(logger, asyncModuleRecoveryRuntime);
346
+ placementRuntime.replacePlacementTopology(placementTopology, { source: APP_PLACEMENT_TOPOLOGY_MODULE_SPECIFIER });
347
+ const placements = await loadAppPlacementDefinitions(logger, asyncModuleRecoveryRuntime);
348
+ placementRuntime.replacePlacements(placements, { source: APP_PLACEMENT_MODULE_SPECIFIER });
349
+ const appConfig = getClientAppConfig();
350
+ const surfaceConfig = buildSurfaceConfigContext(surfaceRuntime, {
351
+ tenancyMode: appConfig?.tenancyMode
352
+ });
353
+ const surfaceAccessPolicies =
354
+ appConfig?.surfaceAccessPolicies && typeof appConfig.surfaceAccessPolicies === "object"
355
+ ? appConfig.surfaceAccessPolicies
356
+ : {};
357
+ placementRuntime.setContext(
358
+ {
359
+ surfaceConfig,
360
+ surfaceAccessPolicies
361
+ },
362
+ {
363
+ source: "shell-web.surface-config"
595
364
  }
596
- const placements = await loadAppPlacementDefinitions(logger, asyncModuleRecoveryRuntime);
597
- placementRuntime.replacePlacements(placements, { source: APP_PLACEMENT_MODULE_SPECIFIER });
598
- const appConfig = getClientAppConfig();
599
- const surfaceConfig = buildSurfaceConfigContext(surfaceRuntime, {
600
- tenancyMode: appConfig?.tenancyMode
601
- });
602
- const surfaceAccessPolicies =
603
- appConfig?.surfaceAccessPolicies && typeof appConfig.surfaceAccessPolicies === "object"
604
- ? appConfig.surfaceAccessPolicies
605
- : {};
606
- placementRuntime.setContext(
607
- {
608
- surfaceConfig,
609
- surfaceAccessPolicies
610
- },
611
- {
612
- source: "shell-web.surface-config"
613
- }
614
- );
615
- }
365
+ );
616
366
 
617
367
  const errorConfig = await loadAppErrorConfig(logger, errorRuntime, asyncModuleRecoveryRuntime);
618
- applyAppErrorConfig(errorRuntime, errorConfig);
368
+ errorRuntime.configure(errorConfig);
619
369
  requestRecoveryRuntime.install();
620
370
 
621
- if (bootstrapRuntime && typeof bootstrapRuntime.initialize === "function") {
622
- await bootstrapRuntime.initialize();
623
- }
371
+ await bootstrapRuntime.initialize();
624
372
 
625
373
  if (!vueApp || typeof vueApp.provide !== "function" || typeof vueApp.use !== "function") {
626
374
  return;
@@ -630,15 +378,11 @@ const ShellWebClientProvider = defineProvider({
630
378
  }
631
379
  useShellErrorPresentationStore(pinia).attachRuntimeStore(errorPresentationStore);
632
380
 
633
- vueApp.provide("jskit.shell-web.runtime.web-placement.client", placementRuntime);
634
- vueApp.provide("jskit.shell-web.runtime.web-refresh.client", refreshRuntime);
635
- vueApp.provide("jskit.shell-web.runtime.web-error.client", errorRuntime);
381
+ vueApp.provide(SHELL_WEB_PLACEMENT_RUNTIME_KEY, placementRuntime);
382
+ vueApp.provide(SHELL_WEB_REFRESH_RUNTIME_KEY, refreshRuntime);
383
+ vueApp.provide(SHELL_WEB_ERROR_RUNTIME_KEY, errorRuntime);
636
384
  vueApp.provide(SHELL_ASYNC_MODULE_RECOVERY_RUNTIME_KEY, asyncModuleRecoveryRuntime);
637
385
  vueApp.provide(SHELL_REQUEST_RECOVERY_RUNTIME_KEY, requestRecoveryRuntime);
638
- vueApp.provide(
639
- "jskit.shell-web.runtime.web-error.presentation-store.client",
640
- errorPresentationStore
641
- );
642
386
 
643
387
  installVueErrorBridge(vueApp, errorRuntime, logger);
644
388
  installRouterErrorBridge(router, errorRuntime, logger);