@jskit-ai/shell-web 0.1.153 → 0.1.156
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/shell-web",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.156",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
"./client/bootstrap": "./src/client/bootstrap/index.js",
|
|
15
15
|
"./server/support/localLinkItemScaffolds": "./src/server/support/localLinkItemScaffolds.js",
|
|
16
16
|
"./client/navigation/linkResolver": "./src/client/navigation/linkResolver.js",
|
|
17
|
+
"./client/navigation/usePaths": "./src/client/navigation/usePaths.js",
|
|
18
|
+
"./client/navigation/useSurfaceRouteContext": "./src/client/navigation/useSurfaceRouteContext.js",
|
|
17
19
|
"./client/components/ShellLayout": "./src/client/components/ShellLayout.vue",
|
|
18
20
|
"./client/components/ShellOutlet": "./src/client/components/ShellOutlet.vue",
|
|
19
21
|
"./client/components/ShellOutletMenuWidget": "./src/client/components/ShellOutletMenuWidget.vue",
|
|
@@ -28,7 +30,7 @@
|
|
|
28
30
|
},
|
|
29
31
|
"dependencies": {
|
|
30
32
|
"@mdi/js": "^7.4.47",
|
|
31
|
-
"@jskit-ai/kernel": "0.1.
|
|
33
|
+
"@jskit-ai/kernel": "0.1.152"
|
|
32
34
|
},
|
|
33
35
|
"peerDependencies": {
|
|
34
36
|
"pinia": "^3.0.4",
|
|
@@ -83,6 +85,14 @@
|
|
|
83
85
|
"subpath": "./client/placement",
|
|
84
86
|
"summary": "Exports placement registry, placement context access, runtime token, and surface path helpers."
|
|
85
87
|
},
|
|
88
|
+
{
|
|
89
|
+
"subpath": "./client/navigation/usePaths",
|
|
90
|
+
"summary": "Exports surface-aware page and API path resolution."
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"subpath": "./client/navigation/useSurfaceRouteContext",
|
|
94
|
+
"summary": "Exports the current route, placement context, and resolved surface id."
|
|
95
|
+
},
|
|
86
96
|
{
|
|
87
97
|
"subpath": "./client/error",
|
|
88
98
|
"summary": "Exports default error policy and runtime error reporter hook."
|
|
@@ -381,7 +391,7 @@
|
|
|
381
391
|
"dependencies": {
|
|
382
392
|
"runtime": {
|
|
383
393
|
"@mdi/js": "^7.4.47",
|
|
384
|
-
"@jskit-ai/kernel": "0.1.
|
|
394
|
+
"@jskit-ai/kernel": "0.1.152"
|
|
385
395
|
},
|
|
386
396
|
"dev": {
|
|
387
397
|
"@playwright/test": "1.61.1"
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { computed, unref } from "vue";
|
|
2
|
+
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
3
|
+
import { resolveScopedApiBasePath } from "@jskit-ai/kernel/shared/surface";
|
|
4
|
+
import { resolveSurfaceDefinitionFromPlacementContext } from "../placement/index.js";
|
|
5
|
+
import { useShellLinkResolver } from "./linkResolver.js";
|
|
6
|
+
import { useSurfaceRouteContext } from "./useSurfaceRouteContext.js";
|
|
7
|
+
|
|
8
|
+
function normalizePathSuffix(value = "") {
|
|
9
|
+
const raw = normalizeText(unref(value));
|
|
10
|
+
if (!raw) {
|
|
11
|
+
return "";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return raw.startsWith("/") ? raw : `/${raw}`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function resolveSurfaceId(value, fallback = "") {
|
|
18
|
+
const normalized = normalizeText(unref(value)).toLowerCase();
|
|
19
|
+
if (normalized && normalized !== "*") {
|
|
20
|
+
return normalized;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const normalizedFallback = normalizeText(unref(fallback)).toLowerCase();
|
|
24
|
+
if (normalizedFallback && normalizedFallback !== "*") {
|
|
25
|
+
return normalizedFallback;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return "";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function resolveDefaultSurfaceIdFromPlacementContext(placementContext = null) {
|
|
32
|
+
return resolveSurfaceId(placementContext?.surfaceConfig?.defaultSurfaceId, "");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function normalizeRouteParams(params = null) {
|
|
36
|
+
if (!params || typeof params !== "object" || Array.isArray(params)) {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const output = {};
|
|
41
|
+
for (const [rawKey, rawValue] of Object.entries(params)) {
|
|
42
|
+
const key = normalizeText(rawKey);
|
|
43
|
+
if (!key) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const value = Array.isArray(rawValue) ? rawValue[0] : rawValue;
|
|
48
|
+
const normalizedValue = normalizeText(value);
|
|
49
|
+
if (normalizedValue) {
|
|
50
|
+
output[key] = normalizedValue;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return output;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function resolveRouteParams(baseParams = {}, overrideParams = null) {
|
|
57
|
+
return {
|
|
58
|
+
...normalizeRouteParams(baseParams),
|
|
59
|
+
...normalizeRouteParams(overrideParams)
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function usePaths({ routeContext: sourceRouteContext = null } = {}) {
|
|
64
|
+
const routeContext = sourceRouteContext || useSurfaceRouteContext();
|
|
65
|
+
const shellLinkResolver = useShellLinkResolver();
|
|
66
|
+
const routeParams = computed(() => normalizeRouteParams(routeContext.route?.params));
|
|
67
|
+
|
|
68
|
+
function page(relativePath = "/", options = {}) {
|
|
69
|
+
const source = options && typeof options === "object" && !Array.isArray(options) ? options : {};
|
|
70
|
+
const surface =
|
|
71
|
+
resolveSurfaceId(source.surface, routeContext.currentSurfaceId.value) ||
|
|
72
|
+
resolveDefaultSurfaceIdFromPlacementContext(routeContext.placementContext.value);
|
|
73
|
+
if (!surface) {
|
|
74
|
+
return "";
|
|
75
|
+
}
|
|
76
|
+
return shellLinkResolver.resolve(relativePath, {
|
|
77
|
+
surface,
|
|
78
|
+
explicitTo: source.explicitTo,
|
|
79
|
+
surfaceRelativePath: source.surfaceRelativePath,
|
|
80
|
+
params: resolveRouteParams(routeParams.value, source.params),
|
|
81
|
+
strictParams: source.strictParams !== false
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function api(relativePath = "", options = {}) {
|
|
86
|
+
const source = options && typeof options === "object" && !Array.isArray(options) ? options : {};
|
|
87
|
+
const surface =
|
|
88
|
+
resolveSurfaceId(source.surface, routeContext.currentSurfaceId.value) ||
|
|
89
|
+
resolveDefaultSurfaceIdFromPlacementContext(routeContext.placementContext.value);
|
|
90
|
+
const suffix = normalizePathSuffix(relativePath);
|
|
91
|
+
|
|
92
|
+
if (!suffix) {
|
|
93
|
+
throw new TypeError("usePaths().api(relativePath) requires a non-empty relativePath.");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const routeBase = resolveSurfaceDefinitionFromPlacementContext(
|
|
97
|
+
routeContext.placementContext.value,
|
|
98
|
+
surface
|
|
99
|
+
)?.routeBase || "/";
|
|
100
|
+
|
|
101
|
+
return resolveScopedApiBasePath({
|
|
102
|
+
routeBase,
|
|
103
|
+
relativePath: suffix,
|
|
104
|
+
params: resolveRouteParams(routeParams.value, source.params),
|
|
105
|
+
strictParams: source.strictParams !== false
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return Object.freeze({
|
|
110
|
+
route: routeContext.route,
|
|
111
|
+
placementContext: routeContext.placementContext,
|
|
112
|
+
currentSurfaceId: routeContext.currentSurfaceId,
|
|
113
|
+
routeParams,
|
|
114
|
+
page,
|
|
115
|
+
api
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export { usePaths };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { computed } from "vue";
|
|
2
|
+
import { useRoute } from "vue-router";
|
|
3
|
+
import {
|
|
4
|
+
resolveRuntimePathname,
|
|
5
|
+
resolveSurfaceIdFromPlacementPathname,
|
|
6
|
+
useWebPlacementContext
|
|
7
|
+
} from "../placement/index.js";
|
|
8
|
+
|
|
9
|
+
function useSurfaceRouteContext() {
|
|
10
|
+
const route = useRoute();
|
|
11
|
+
const { context: placementContext, mergeContext: mergePlacementContext } = useWebPlacementContext();
|
|
12
|
+
const routePath = computed(() => resolveRuntimePathname(route?.path));
|
|
13
|
+
const currentSurfaceId = computed(() =>
|
|
14
|
+
resolveSurfaceIdFromPlacementPathname(placementContext.value, routePath.value)
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
return Object.freeze({
|
|
18
|
+
route,
|
|
19
|
+
routePath,
|
|
20
|
+
placementContext,
|
|
21
|
+
mergePlacementContext,
|
|
22
|
+
currentSurfaceId
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { useSurfaceRouteContext };
|
|
@@ -114,13 +114,6 @@ function normalizePlacementDefinition(value, { strict = false, source = "placeme
|
|
|
114
114
|
return null;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
if (Object.hasOwn(value, "host") || Object.hasOwn(value, "position")) {
|
|
118
|
-
if (strict) {
|
|
119
|
-
throw new TypeError(`${source} must use "target" only. Legacy "host" and "position" fields are not supported.`);
|
|
120
|
-
}
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
117
|
const id = normalizeText(value.id);
|
|
125
118
|
if (!id) {
|
|
126
119
|
if (strict) {
|
|
@@ -125,10 +125,6 @@ function resolveQueryMeta(query = null) {
|
|
|
125
125
|
|
|
126
126
|
function isRequestRecoveryDisabled(query = null) {
|
|
127
127
|
const meta = resolveQueryMeta(query);
|
|
128
|
-
if (meta.jskitRequestRecovery === false || meta.requestRecovery === false) {
|
|
129
|
-
return true;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
128
|
const jskitMeta = isRecord(meta.jskit) ? meta.jskit : {};
|
|
133
129
|
return jskitMeta.requestRecovery === false;
|
|
134
130
|
}
|
|
@@ -138,11 +134,7 @@ function resolveQueryRecoveryLabel(query = null) {
|
|
|
138
134
|
const jskitMeta = isRecord(meta.jskit) ? meta.jskit : {};
|
|
139
135
|
|
|
140
136
|
return normalizeText(
|
|
141
|
-
jskitMeta.requestRecoveryLabel
|
|
142
|
-
jskitMeta.label ||
|
|
143
|
-
meta.jskitRequestRecoveryLabel ||
|
|
144
|
-
meta.requestRecoveryLabel ||
|
|
145
|
-
meta.label,
|
|
137
|
+
jskitMeta.requestRecoveryLabel,
|
|
146
138
|
"Request"
|
|
147
139
|
);
|
|
148
140
|
}
|
|
@@ -152,9 +144,7 @@ function resolveQueryRecoverySource(query = null) {
|
|
|
152
144
|
const jskitMeta = isRecord(meta.jskit) ? meta.jskit : {};
|
|
153
145
|
|
|
154
146
|
return normalizeText(
|
|
155
|
-
jskitMeta.requestRecoverySource
|
|
156
|
-
meta.jskitRequestRecoverySource ||
|
|
157
|
-
meta.requestRecoverySource,
|
|
147
|
+
jskitMeta.requestRecoverySource,
|
|
158
148
|
"shell-web.request-recovery.query"
|
|
159
149
|
);
|
|
160
150
|
}
|
|
@@ -164,9 +154,7 @@ function resolveQueryRecoveryDedupeKey(query = null, fallback = "") {
|
|
|
164
154
|
const jskitMeta = isRecord(meta.jskit) ? meta.jskit : {};
|
|
165
155
|
|
|
166
156
|
return normalizeText(
|
|
167
|
-
jskitMeta.requestRecoveryDedupeKey
|
|
168
|
-
meta.jskitRequestRecoveryDedupeKey ||
|
|
169
|
-
meta.requestRecoveryDedupeKey,
|
|
157
|
+
jskitMeta.requestRecoveryDedupeKey,
|
|
170
158
|
fallback
|
|
171
159
|
);
|
|
172
160
|
}
|
|
@@ -174,10 +162,7 @@ function resolveQueryRecoveryDedupeKey(query = null, fallback = "") {
|
|
|
174
162
|
function resolveQueryRecoveryDedupeWindowMs(query = null) {
|
|
175
163
|
const meta = resolveQueryMeta(query);
|
|
176
164
|
const jskitMeta = isRecord(meta.jskit) ? meta.jskit : {};
|
|
177
|
-
const value =
|
|
178
|
-
jskitMeta.requestRecoveryDedupeWindowMs ??
|
|
179
|
-
meta.jskitRequestRecoveryDedupeWindowMs ??
|
|
180
|
-
meta.requestRecoveryDedupeWindowMs;
|
|
165
|
+
const value = jskitMeta.requestRecoveryDedupeWindowMs;
|
|
181
166
|
|
|
182
167
|
return Number.isFinite(Number(value)) ? Math.max(0, Number(value)) : null;
|
|
183
168
|
}
|
|
@@ -186,11 +171,7 @@ function resolveQueryRecoveryMethod(query = null) {
|
|
|
186
171
|
const meta = resolveQueryMeta(query);
|
|
187
172
|
const jskitMeta = isRecord(meta.jskit) ? meta.jskit : {};
|
|
188
173
|
|
|
189
|
-
return normalizeText(
|
|
190
|
-
jskitMeta.requestRecoveryMethod ||
|
|
191
|
-
meta.jskitRequestRecoveryMethod ||
|
|
192
|
-
meta.requestRecoveryMethod
|
|
193
|
-
).toUpperCase();
|
|
174
|
+
return normalizeText(jskitMeta.requestRecoveryMethod).toUpperCase();
|
|
194
175
|
}
|
|
195
176
|
|
|
196
177
|
function isSafeQueryRecoveryMethod(query = null) {
|
|
@@ -41,7 +41,7 @@ test("placement registry accepts explicit non-global surface ids", () => {
|
|
|
41
41
|
assert.equal(added, true);
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
-
test("placement registry
|
|
44
|
+
test("placement registry requires target", () => {
|
|
45
45
|
const registry = createPlacementRegistry();
|
|
46
46
|
|
|
47
47
|
assert.throws(
|
|
@@ -51,6 +51,6 @@ test("placement registry rejects split target fields", () => {
|
|
|
51
51
|
position: "top-right",
|
|
52
52
|
componentToken: "example.split.component"
|
|
53
53
|
}),
|
|
54
|
-
/
|
|
54
|
+
/requires semantic target/
|
|
55
55
|
);
|
|
56
56
|
});
|