@jskit-ai/shell-web 0.1.9 → 0.1.11

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.
@@ -1,7 +1,7 @@
1
1
  export default Object.freeze({
2
2
  packageVersion: 1,
3
3
  packageId: "@jskit-ai/shell-web",
4
- version: "0.1.9",
4
+ version: "0.1.11",
5
5
  description: "Web shell layout runtime with outlet-based placement contributions.",
6
6
  dependsOn: [],
7
7
  capabilities: {
@@ -85,7 +85,7 @@ export default Object.freeze({
85
85
  dependencies: {
86
86
  runtime: {
87
87
  "@tanstack/vue-query": "^5.90.5",
88
- "@jskit-ai/kernel": "0.1.9",
88
+ "@jskit-ai/kernel": "0.1.11",
89
89
  "vuetify": "^4.0.0"
90
90
  },
91
91
  dev: {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/shell-web",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "node --test"
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "@tanstack/vue-query": "^5.90.5",
20
- "@jskit-ai/kernel": "0.1.9",
20
+ "@jskit-ai/kernel": "0.1.11",
21
21
  "vuetify": "^4.0.0"
22
22
  }
23
23
  }
@@ -14,40 +14,49 @@ const DEFAULT_MENU_FALLBACK = Object.freeze({
14
14
  icon: "$menu"
15
15
  });
16
16
 
17
- function normalizeAction(action, fallback) {
18
- const source = normalizeObject(action);
17
+ function normalizeLabeledItem(item, fallback, buildItem) {
18
+ const source = normalizeObject(item);
19
19
  const fallbackSource = normalizeObject(fallback);
20
20
  const label = String(source.label || fallbackSource.label || "").trim();
21
21
  if (!label) {
22
22
  return null;
23
23
  }
24
24
 
25
- return {
26
- label,
27
- to: String(source.to || fallbackSource.to || "").trim(),
28
- variant: String(source.variant || fallbackSource.variant || "text").trim(),
29
- color: String(source.color || fallbackSource.color || "secondary").trim()
30
- };
31
- }
32
-
33
- function normalizeMenuItem(item, fallback) {
34
- const source = normalizeObject(item);
35
- const fallbackSource = normalizeObject(fallback);
36
- const label = String(source.label || fallbackSource.label || "").trim();
37
- if (!label) {
25
+ if (typeof buildItem !== "function") {
38
26
  return null;
39
27
  }
40
28
 
41
- return {
42
- label,
43
- to: String(source.to || fallbackSource.to || "").trim() || "/",
44
- icon: String(source.icon || fallbackSource.icon || "$menu").trim() || "$menu"
45
- };
29
+ return buildItem({
30
+ source,
31
+ fallbackSource,
32
+ label
33
+ });
34
+ }
35
+
36
+ function normalizeAction(action, fallback) {
37
+ return normalizeLabeledItem(action, fallback, ({ source, fallbackSource, label }) => {
38
+ return {
39
+ label,
40
+ to: String(source.to || fallbackSource.to || "").trim(),
41
+ variant: String(source.variant || fallbackSource.variant || "text").trim(),
42
+ color: String(source.color || fallbackSource.color || "secondary").trim()
43
+ };
44
+ });
45
+ }
46
+
47
+ function normalizeMenuItem(item, fallback) {
48
+ return normalizeLabeledItem(item, fallback, ({ source, fallbackSource, label }) => {
49
+ return {
50
+ label,
51
+ to: String(source.to || fallbackSource.to || "").trim() || "/",
52
+ icon: String(source.icon || fallbackSource.icon || "$menu").trim() || "$menu"
53
+ };
54
+ });
46
55
  }
47
56
 
48
57
  function normalizeActionList(actions) {
49
- const source = Array.isArray(actions) ? actions : [];
50
- return source
58
+ const sourceActions = Array.isArray(actions) ? actions : [];
59
+ return sourceActions
51
60
  .map((item) => normalizeAction(item, DEFAULT_ACTION_FALLBACK))
52
61
  .filter(Boolean);
53
62
  }
@@ -1,14 +1,15 @@
1
- import { isRecord } from "@jskit-ai/kernel/shared/support/normalize";
1
+ import {
2
+ isRecord,
3
+ normalizeText as normalizeKernelText
4
+ } from "@jskit-ai/kernel/shared/support/normalize";
2
5
 
3
6
  const ERROR_CHANNELS = Object.freeze(["snackbar", "banner", "dialog", "silent"]);
4
7
  const ERROR_SEVERITIES = Object.freeze(["info", "success", "warning", "error", "critical"]);
5
8
 
6
9
  function normalizeText(value, fallback = "") {
7
- const normalized = String(value || "").trim();
8
- if (normalized) {
9
- return normalized;
10
- }
11
- return String(fallback || "").trim();
10
+ return normalizeKernelText(value, {
11
+ fallback: String(fallback || "").trim()
12
+ });
12
13
  }
13
14
 
14
15
  function normalizeChannel(value, fallback = "") {
@@ -6,6 +6,7 @@ import {
6
6
  normalizeSeverity,
7
7
  normalizeText
8
8
  } from "./normalize.js";
9
+ import { createListenerSubscription } from "@jskit-ai/kernel/shared/support/listenerSet";
9
10
  import { createDefaultErrorPolicy } from "./policy.js";
10
11
 
11
12
  function createRuntimeLogger(logger = null) {
@@ -108,6 +109,7 @@ function createErrorRuntime({
108
109
  const runtimeLogger = createRuntimeLogger(logger);
109
110
  const byPresenterId = new Map();
110
111
  const listeners = new Set();
112
+ const subscribe = createListenerSubscription(listeners);
111
113
  const dedupeWindowByKey = new Map();
112
114
  let activePolicy = typeof policy === "function" ? policy : createDefaultErrorPolicy();
113
115
  let activeAppDefaultPresenterId = normalizeText(defaultPresenterId);
@@ -177,17 +179,6 @@ function createErrorRuntime({
177
179
  return activeAppDefaultPresenterId;
178
180
  }
179
181
 
180
- function subscribe(listener) {
181
- if (typeof listener !== "function") {
182
- return () => {};
183
- }
184
-
185
- listeners.add(listener);
186
- return () => {
187
- listeners.delete(listener);
188
- };
189
- }
190
-
191
182
  function notify(event = {}) {
192
183
  const payload = Object.freeze({ ...event });
193
184
  for (const listener of listeners) {
@@ -3,6 +3,7 @@ import {
3
3
  normalizeSeverity,
4
4
  normalizeText
5
5
  } from "./normalize.js";
6
+ import { createListenerSubscription } from "@jskit-ai/kernel/shared/support/listenerSet";
6
7
 
7
8
  const PRESENTATION_CHANNELS = Object.freeze(["snackbar", "banner", "dialog"]);
8
9
  const SINGLETON_CHANNELS = new Set(["banner"]);
@@ -33,6 +34,7 @@ function createErrorPresentationStore({
33
34
  now = () => Date.now()
34
35
  } = {}) {
35
36
  const listeners = new Set();
37
+ const subscribe = createListenerSubscription(listeners);
36
38
  const channelState = createEmptyChannelState();
37
39
  let sequence = 0;
38
40
  let revision = 0;
@@ -66,17 +68,6 @@ function createErrorPresentationStore({
66
68
  }
67
69
  }
68
70
 
69
- function subscribe(listener) {
70
- if (typeof listener !== "function") {
71
- return () => {};
72
- }
73
-
74
- listeners.add(listener);
75
- return () => {
76
- listeners.delete(listener);
77
- };
78
- }
79
-
80
71
  function present(channel, payload = {}) {
81
72
  const normalizedChannel = String(channel || "").trim();
82
73
  const entries = getChannelEntries(normalizedChannel);
@@ -3,6 +3,7 @@ import {
3
3
  WEB_PLACEMENT_SURFACE_ANY
4
4
  } from "./tokens.js";
5
5
  import { DEFAULT_DEBUG_DEPTH, explodePayload } from "./debug.js";
6
+ import { createListenerSubscription } from "@jskit-ai/kernel/shared/support/listenerSet";
6
7
  import { isRecord } from "@jskit-ai/kernel/shared/support/normalize";
7
8
  import {
8
9
  isRenderableComponent,
@@ -227,6 +228,7 @@ function createWebPlacementRuntime({ app, logger = null } = {}) {
227
228
  const invalidComponentTokens = new Set();
228
229
  const failedTokens = new Set();
229
230
  const listeners = new Set();
231
+ const subscribe = createListenerSubscription(listeners);
230
232
  let placementDefinitions = Object.freeze([]);
231
233
  let sharedContext = Object.freeze({});
232
234
  let revision = 0;
@@ -300,16 +302,6 @@ function createWebPlacementRuntime({ app, logger = null } = {}) {
300
302
  return sharedContext;
301
303
  }
302
304
 
303
- function subscribe(listener) {
304
- if (typeof listener !== "function") {
305
- return () => {};
306
- }
307
- listeners.add(listener);
308
- return () => {
309
- listeners.delete(listener);
310
- };
311
- }
312
-
313
305
  function getRevision() {
314
306
  return revision;
315
307
  }
@@ -9,6 +9,7 @@ import {
9
9
  shouldRetryTransientQueryFailure,
10
10
  transientQueryRetryDelay
11
11
  } from "@jskit-ai/kernel/shared/support";
12
+ import { createProviderLogger as createSharedProviderLogger } from "@jskit-ai/kernel/shared/support/providerLogger";
12
13
  import { QueryClient, VueQueryPlugin } from "@tanstack/vue-query";
13
14
  import {
14
15
  createDefaultErrorPolicy
@@ -60,25 +61,6 @@ function createShellWebQueryClient() {
60
61
  });
61
62
  }
62
63
 
63
- function createProviderLogger(app) {
64
- return Object.freeze({
65
- warn: (...args) => {
66
- if (isRecord(app) && typeof app.warn === "function") {
67
- app.warn(...args);
68
- return;
69
- }
70
- console.warn(...args);
71
- },
72
- error: (...args) => {
73
- if (isRecord(app) && typeof app.error === "function") {
74
- app.error(...args);
75
- return;
76
- }
77
- console.error(...args);
78
- }
79
- });
80
- }
81
-
82
64
  function isMissingDynamicModule(error, moduleSpecifier) {
83
65
  const message = String(error?.message || error || "");
84
66
  return (
@@ -270,7 +252,7 @@ class ShellWebClientProvider {
270
252
  throw new Error("ShellWebClientProvider requires application singleton().");
271
253
  }
272
254
 
273
- const logger = createProviderLogger(app);
255
+ const logger = createSharedProviderLogger(isRecord(app) ? app : null);
274
256
  app.singleton(WEB_PLACEMENT_RUNTIME_CLIENT_TOKEN, () => createWebPlacementRuntime({ app, logger }));
275
257
  app.singleton(SHELL_WEB_QUERY_CLIENT_TOKEN, () => createShellWebQueryClient());
276
258
  app.singleton(SHELL_WEB_ERROR_PRESENTATION_STORE_CLIENT_TOKEN, () => createErrorPresentationStore());
@@ -291,7 +273,7 @@ class ShellWebClientProvider {
291
273
  throw new Error("ShellWebClientProvider requires application make()/has().");
292
274
  }
293
275
 
294
- const logger = createProviderLogger(app);
276
+ const logger = createSharedProviderLogger(isRecord(app) ? app : null);
295
277
  const placementRuntime = app.make(WEB_PLACEMENT_RUNTIME_CLIENT_TOKEN);
296
278
  if (placementRuntime && typeof placementRuntime.replacePlacements === "function") {
297
279
  const placements = await loadAppPlacementDefinitions(logger);