@jskit-ai/kernel 0.1.245 → 0.1.247
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/client/appConfig.js +2 -1
- package/client/asyncModuleRecovery.js +2 -4
- package/client/index.d.ts +13 -75
- package/client/index.js +1 -1
- package/client/logging.js +8 -2
- package/client/moduleBootstrap.js +41 -33
- package/client/moduleBootstrap.test.js +35 -0
- package/client/shellBootstrap.js +1 -8
- package/client/shellBootstrap.test.js +32 -5
- package/client/shellRouting.js +16 -61
- package/client/shellRouting.test.js +30 -1
- package/client/vite/clientBootstrapPlugin.js +0 -6
- package/client/vite/clientBootstrapPlugin.test.js +2 -3
- package/internal/node/installedPackages.js +19 -62
- package/internal/node/installedPackages.test.js +33 -0
- package/package.json +2 -4
- package/server/actions/actionCatalogue.js +13 -34
- package/{shared → server}/actions/actionContributorHelpers.js +0 -5
- package/{shared → server}/actions/actionDefinitions.js +24 -131
- package/{shared → server}/actions/actionDefinitions.test.js +75 -21
- package/server/actions/actionEvents.js +97 -2
- package/server/actions/actionEvents.test.js +79 -0
- package/{shared → server}/actions/executionContext.js +3 -13
- package/server/actions/idempotency.js +24 -0
- package/server/actions/pipeline.js +87 -0
- package/{shared → server}/actions/policies.js +20 -33
- package/{shared → server}/actions/policies.test.js +6 -6
- package/{shared → server}/actions/registry.js +3 -22
- package/{shared → server}/actions/registry.test.js +70 -32
- package/server/actions/requestMeta.js +17 -0
- package/server/http/capabilityHttpRuntime.js +1 -1
- package/server/http/capabilityHttpRuntime.test.js +56 -0
- package/server/http/errorHandling.js +291 -0
- package/server/{runtime/fastifyBootstrap.test.js → http/errorHandling.test.js} +72 -120
- package/server/http/lib/routeRegistration.js +36 -150
- package/server/http/lib/routeTransport.js +2 -0
- package/server/http/lib/routeValidator.js +5 -47
- package/server/http/lib/routeValidator.test.js +15 -17
- package/server/http/lib/router.js +10 -94
- package/server/platform/providerRuntime/capabilityProviderLoader.js +2 -1
- package/server/platform/providerRuntime/helpers.js +1 -34
- package/server/runtime/integers.js +1 -5
- package/server/runtime/serviceAuthorization.js +4 -9
- package/server/support/appConfig.js +2 -1
- package/server/support/appConfigFiles.js +1 -38
- package/server/support/appConfigFiles.test.js +3 -39
- package/server/support/index.js +1 -20
- package/shared/capabilities/defineProvider.js +1 -1
- package/shared/capabilities/index.js +1 -1
- package/shared/index.js +1 -1
- package/shared/support/index.js +1 -4
- package/shared/support/mobileConfig.js +99 -0
- package/shared/support/mobileConfig.test.js +113 -0
- package/shared/support/normalize.js +0 -108
- package/shared/support/normalize.test.js +0 -134
- package/shared/{actions/actionContributorHelpers.test.js → support/permissions.test.js} +1 -1
- package/shared/support/returnToPath.js +1 -20
- package/shared/support/returnToPath.test.js +1 -36
- package/shared/support/shellLayoutTargets.js +2 -140
- package/shared/support/shellLayoutTargets.test.js +1 -110
- package/shared/support/sorting.js +1 -9
- package/shared/surface/apiPaths.js +1 -82
- package/shared/surface/paths.js +38 -171
- package/shared/surface/registry.js +2 -1
- package/shared/surface/runtime.js +18 -109
- package/shared/surface/runtime.test.js +57 -0
- package/shared/validators/index.js +1 -4
- package/shared/validators/jsonRestSchemaSupport.js +0 -9
- package/shared/validators/recordIdParamsValidator.js +0 -6
- package/shared/validators/schemaDefinitions.js +4 -30
- package/shared/validators/schemaPayloadValidation.js +2 -6
- package/test/barrelExposure.test.js +3 -7
- package/test/exportsContract.test.js +1 -3
- package/client/mobileLaunchRouting.js +0 -231
- package/client/mobileLaunchRouting.test.js +0 -230
- package/server/http/lib/controller.js +0 -183
- package/server/http/lib/controller.test.js +0 -143
- package/server/http/lib/index.js +0 -5
- package/server/runtime/canonicalJson.js +0 -74
- package/server/runtime/entityChangeEvents.js +0 -307
- package/server/runtime/entityChangeEvents.test.js +0 -274
- package/server/runtime/fastifyBootstrap.js +0 -553
- package/server/runtime/moduleConfig.js +0 -223
- package/server/runtime/moduleConfig.test.js +0 -165
- package/server/runtime/pagination.js +0 -13
- package/server/runtime/realtimeNormalization.js +0 -21
- package/server/runtime/routeUtils.js +0 -20
- package/server/runtime/securityAudit.js +0 -269
- package/server/runtime/securityAudit.test.js +0 -41
- package/server/support/pageTargets.js +0 -772
- package/server/support/pageTargets.test.js +0 -659
- package/server/support/shellOutlets.js +0 -576
- package/server/support/shellOutlets.test.js +0 -336
- package/server/surface/index.js +0 -18
- package/shared/actions/audit.js +0 -7
- package/shared/actions/idempotency.js +0 -62
- package/shared/actions/observability.js +0 -10
- package/shared/actions/pipeline.js +0 -309
- package/shared/actions/requestMeta.js +0 -36
- package/shared/actions/textNormalization.js +0 -3
- package/shared/actions/withActionDefaults.js +0 -34
- /package/{shared → server}/actions/executionContext.test.js +0 -0
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import test from "node:test";
|
|
3
|
-
import {
|
|
4
|
-
describeShellOutletTargets,
|
|
5
|
-
discoverShellOutletTargetsFromVueSource,
|
|
6
|
-
findShellOutletTargetById,
|
|
7
|
-
normalizeShellOutletTargetId
|
|
8
|
-
} from "./shellLayoutTargets.js";
|
|
3
|
+
import { normalizeShellOutletTargetId } from "./shellLayoutTargets.js";
|
|
9
4
|
|
|
10
5
|
test("normalizeShellOutletTargetId validates host:position tokens", () => {
|
|
11
6
|
assert.equal(normalizeShellOutletTargetId("shell-layout:primary-menu"), "shell-layout:primary-menu");
|
|
@@ -14,107 +9,3 @@ test("normalizeShellOutletTargetId validates host:position tokens", () => {
|
|
|
14
9
|
assert.equal(normalizeShellOutletTargetId("shell-layout"), "");
|
|
15
10
|
assert.equal(normalizeShellOutletTargetId("shell-layout:"), "");
|
|
16
11
|
});
|
|
17
|
-
|
|
18
|
-
test("discoverShellOutletTargetsFromVueSource resolves legal targets and one default", () => {
|
|
19
|
-
const source = `
|
|
20
|
-
<template>
|
|
21
|
-
<ShellOutlet target="shell-layout:top-left" />
|
|
22
|
-
<ShellOutlet
|
|
23
|
-
target="shell-layout:primary-menu"
|
|
24
|
-
default
|
|
25
|
-
/>
|
|
26
|
-
<ShellOutlet target="shell-layout:secondary-menu" />
|
|
27
|
-
</template>
|
|
28
|
-
`;
|
|
29
|
-
|
|
30
|
-
const discovered = discoverShellOutletTargetsFromVueSource(source, {
|
|
31
|
-
context: "ShellLayout.vue"
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
assert.equal(discovered.defaultTargetId, "shell-layout:primary-menu");
|
|
35
|
-
assert.deepEqual(
|
|
36
|
-
discovered.targets.map((entry) => entry.id),
|
|
37
|
-
["shell-layout:top-left", "shell-layout:primary-menu", "shell-layout:secondary-menu"]
|
|
38
|
-
);
|
|
39
|
-
assert.equal(
|
|
40
|
-
describeShellOutletTargets(discovered.targets),
|
|
41
|
-
"shell-layout:top-left, shell-layout:primary-menu, shell-layout:secondary-menu"
|
|
42
|
-
);
|
|
43
|
-
assert.deepEqual(
|
|
44
|
-
findShellOutletTargetById(discovered.targets, " shell-layout : primary-menu "),
|
|
45
|
-
discovered.targets[1]
|
|
46
|
-
);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test("discoverShellOutletTargetsFromVueSource throws for duplicate targets", () => {
|
|
50
|
-
const source = `
|
|
51
|
-
<template>
|
|
52
|
-
<ShellOutlet target="shell-layout:top-right" />
|
|
53
|
-
<ShellOutlet target="shell-layout:top-right" />
|
|
54
|
-
</template>
|
|
55
|
-
`;
|
|
56
|
-
|
|
57
|
-
assert.throws(
|
|
58
|
-
() => discoverShellOutletTargetsFromVueSource(source, { context: "ShellLayout.vue" }),
|
|
59
|
-
/duplicate ShellOutlet target/
|
|
60
|
-
);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test("discoverShellOutletTargetsFromVueSource throws for multiple defaults", () => {
|
|
64
|
-
const source = `
|
|
65
|
-
<template>
|
|
66
|
-
<ShellOutlet target="shell-layout:primary-menu" default />
|
|
67
|
-
<ShellOutlet target="shell-layout:secondary-menu" default />
|
|
68
|
-
</template>
|
|
69
|
-
`;
|
|
70
|
-
|
|
71
|
-
assert.throws(
|
|
72
|
-
() => discoverShellOutletTargetsFromVueSource(source, { context: "ShellLayout.vue" }),
|
|
73
|
-
/multiple default ShellOutlet targets/
|
|
74
|
-
);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test("discoverShellOutletTargetsFromVueSource can collect source hints without enforcing one default", () => {
|
|
78
|
-
const source = `
|
|
79
|
-
<template>
|
|
80
|
-
<ShellOutlet target="shell-layout:primary-menu" default />
|
|
81
|
-
<ShellOutlet target="shell-layout:primary-bottom-nav" default />
|
|
82
|
-
</template>
|
|
83
|
-
`;
|
|
84
|
-
|
|
85
|
-
const discovered = discoverShellOutletTargetsFromVueSource(source, {
|
|
86
|
-
context: "ShellLayout.vue",
|
|
87
|
-
enforceSingleDefault: false
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
assert.equal(discovered.defaultTargetId, "shell-layout:primary-menu");
|
|
91
|
-
assert.deepEqual(
|
|
92
|
-
discovered.targets.map((entry) => entry.id),
|
|
93
|
-
["shell-layout:primary-menu", "shell-layout:primary-bottom-nav"]
|
|
94
|
-
);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
test("discoverShellOutletTargetsFromVueSource ignores disabled default markers", () => {
|
|
98
|
-
const source = `
|
|
99
|
-
<template>
|
|
100
|
-
<ShellOutlet target="shell-layout:primary-menu" default="false" />
|
|
101
|
-
<ShellOutlet target="shell-layout:secondary-menu" />
|
|
102
|
-
</template>
|
|
103
|
-
`;
|
|
104
|
-
|
|
105
|
-
const discovered = discoverShellOutletTargetsFromVueSource(source, { context: "ShellLayout.vue" });
|
|
106
|
-
assert.equal(discovered.defaultTargetId, "");
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
test("discoverShellOutletTargetsFromVueSource reads the target attribute", () => {
|
|
110
|
-
const source = `
|
|
111
|
-
<template>
|
|
112
|
-
<ShellOutlet target="shell-layout:primary-menu" host="other-host" position="primary-menu" />
|
|
113
|
-
</template>
|
|
114
|
-
`;
|
|
115
|
-
|
|
116
|
-
assert.deepEqual(
|
|
117
|
-
discoverShellOutletTargetsFromVueSource(source, { context: "ShellLayout.vue" }).targets,
|
|
118
|
-
[{ id: "shell-layout:primary-menu", default: false }]
|
|
119
|
-
);
|
|
120
|
-
});
|
|
@@ -4,12 +4,4 @@ function sortStrings(values = []) {
|
|
|
4
4
|
);
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
return [...(Array.isArray(items) ? items : [])].sort((left, right) => {
|
|
9
|
-
const leftId = String(idSelector(left) || "").trim();
|
|
10
|
-
const rightId = String(idSelector(right) || "").trim();
|
|
11
|
-
return leftId.localeCompare(rightId);
|
|
12
|
-
});
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export { sortStrings, sortById };
|
|
7
|
+
export { sortStrings };
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
const API_BASE_PATH = "/api";
|
|
4
|
-
const API_PREFIX = "/api";
|
|
5
|
-
const API_PREFIX_SLASH = `${API_PREFIX}/`;
|
|
6
|
-
const API_DOCS_PATH = `${API_PREFIX}/docs`;
|
|
7
|
-
const API_REALTIME_PATH = `${API_PREFIX}/realtime`;
|
|
8
|
-
const VERSIONED_API_PATH_PATTERN = /^\/api\/v[0-9]+(?:$|\/)/;
|
|
1
|
+
import { API_BASE_PATH, normalizePathname } from "./paths.js";
|
|
9
2
|
|
|
10
3
|
function normalizeRouteTemplateParams(params = null) {
|
|
11
4
|
if (!params || typeof params !== "object" || Array.isArray(params)) {
|
|
@@ -77,81 +70,7 @@ function resolveScopedApiBasePath({
|
|
|
77
70
|
return `${basePath}${normalizedRelativePath}`;
|
|
78
71
|
}
|
|
79
72
|
|
|
80
|
-
function isApiPath(pathname) {
|
|
81
|
-
return matchesPathPrefix(pathname, API_BASE_PATH);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function isVersionedApiPath(pathname) {
|
|
85
|
-
return VERSIONED_API_PATH_PATTERN.test(normalizePathname(pathname));
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function toVersionedApiPath(pathname) {
|
|
89
|
-
const normalizedPathname = normalizePathname(pathname);
|
|
90
|
-
if (!isApiPath(normalizedPathname)) {
|
|
91
|
-
return normalizedPathname;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (isVersionedApiPath(normalizedPathname)) {
|
|
95
|
-
return normalizedPathname;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (normalizedPathname === API_BASE_PATH) {
|
|
99
|
-
return API_PREFIX;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return `${API_PREFIX}${normalizedPathname.slice(API_BASE_PATH.length)}`;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function toVersionedApiPrefix(pathnameOrPrefix) {
|
|
106
|
-
const versionedPath = toVersionedApiPath(pathnameOrPrefix || API_BASE_PATH);
|
|
107
|
-
if (!isApiPath(versionedPath)) {
|
|
108
|
-
return normalizePathname(versionedPath);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (versionedPath === API_PREFIX) {
|
|
112
|
-
return API_PREFIX_SLASH;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return `${versionedPath}/`;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function buildVersionedApiPath(suffix) {
|
|
119
|
-
const rawSuffix = String(suffix || "").trim();
|
|
120
|
-
if (!rawSuffix || rawSuffix === "/") {
|
|
121
|
-
return API_PREFIX;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const normalizedSuffix = normalizePathname(rawSuffix.startsWith("/") ? rawSuffix : `/${rawSuffix}`);
|
|
125
|
-
if (isApiPath(normalizedSuffix)) {
|
|
126
|
-
return toVersionedApiPath(normalizedSuffix);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (normalizedSuffix === "/") {
|
|
130
|
-
return API_PREFIX;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return `${API_PREFIX}${normalizedSuffix}`;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function isVersionedApiPrefixMatch(pathname) {
|
|
137
|
-
const normalizedPathname = normalizePathname(pathname);
|
|
138
|
-
return normalizedPathname === API_PREFIX || normalizedPathname.startsWith(API_PREFIX_SLASH);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
73
|
export {
|
|
142
|
-
API_BASE_PATH,
|
|
143
|
-
API_PREFIX,
|
|
144
|
-
API_PREFIX_SLASH,
|
|
145
|
-
API_DOCS_PATH,
|
|
146
|
-
API_REALTIME_PATH,
|
|
147
|
-
materializeRouteTemplate,
|
|
148
|
-
normalizePathname,
|
|
149
|
-
isApiPath,
|
|
150
|
-
isVersionedApiPath,
|
|
151
|
-
toVersionedApiPath,
|
|
152
|
-
toVersionedApiPrefix,
|
|
153
|
-
buildVersionedApiPath,
|
|
154
|
-
isVersionedApiPrefixMatch,
|
|
155
74
|
resolveScopedRouteBase,
|
|
156
75
|
resolveScopedApiBasePath
|
|
157
76
|
};
|
package/shared/surface/paths.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { escapeRegExp } from "./escapeRegExp.js";
|
|
2
|
+
import { normalizeSurfaceId } from "./registry.js";
|
|
3
|
+
|
|
4
|
+
const API_BASE_PATH = "/api";
|
|
2
5
|
|
|
3
6
|
function normalizePathname(pathname) {
|
|
4
7
|
const rawValue = String(pathname || "/").trim();
|
|
@@ -73,201 +76,65 @@ function compareSurfaceRouteSpecificity(left, right) {
|
|
|
73
76
|
return String(left.id || "").localeCompare(String(right.id || ""));
|
|
74
77
|
}
|
|
75
78
|
|
|
76
|
-
function createSurfacePathHelpers(
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
const listSurfaceDefinitions = options?.listSurfaceDefinitions;
|
|
80
|
-
|
|
81
|
-
if (typeof normalizeSurfaceId !== "function") {
|
|
82
|
-
throw new Error("createSurfacePathHelpers requires normalizeSurfaceId.");
|
|
83
|
-
}
|
|
84
|
-
if (typeof resolveSurfaceRouteBaseFromRegistry !== "function") {
|
|
85
|
-
throw new Error("createSurfacePathHelpers requires resolveSurfaceRouteBase.");
|
|
86
|
-
}
|
|
87
|
-
if (typeof listSurfaceDefinitions !== "function") {
|
|
88
|
-
throw new Error("createSurfacePathHelpers requires listSurfaceDefinitions.");
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const defaultSurfaceId = normalizeSurfaceId(options?.defaultSurfaceId);
|
|
92
|
-
if (!defaultSurfaceId) {
|
|
79
|
+
function createSurfacePathHelpers({ defaultSurfaceId, surfaceDefinitions, apiBasePath = API_BASE_PATH } = {}) {
|
|
80
|
+
const normalizedDefaultSurfaceId = normalizeSurfaceId(defaultSurfaceId);
|
|
81
|
+
if (!normalizedDefaultSurfaceId) {
|
|
93
82
|
throw new Error("createSurfacePathHelpers requires defaultSurfaceId.");
|
|
94
83
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const routeConfig = options?.routes && typeof options.routes === "object" && !Array.isArray(options.routes) ? options.routes : {};
|
|
98
|
-
|
|
99
|
-
function normalizeSurface(surface) {
|
|
100
|
-
return normalizeSurfaceId(surface);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function resolveSurfaceRouteBase(surface) {
|
|
104
|
-
return normalizePathname(resolveSurfaceRouteBaseFromRegistry(surface));
|
|
84
|
+
if (!Array.isArray(surfaceDefinitions)) {
|
|
85
|
+
throw new Error("createSurfacePathHelpers requires a surfaceDefinitions array.");
|
|
105
86
|
}
|
|
106
87
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
88
|
+
const normalizedApiBasePath = normalizePathname(apiBasePath);
|
|
89
|
+
const routedSurfaces = surfaceDefinitions
|
|
90
|
+
.map((definition) => {
|
|
91
|
+
const surfaceId = normalizeSurfaceId(definition?.id);
|
|
92
|
+
if (!surfaceId) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
112
95
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
96
|
+
const matcher = compileSurfaceRouteMatcher(definition.routeBase || "/");
|
|
97
|
+
return {
|
|
98
|
+
id: surfaceId,
|
|
99
|
+
routeBase: matcher.routeBase,
|
|
100
|
+
segmentCount: matcher.segmentCount,
|
|
101
|
+
staticSegmentCount: matcher.staticSegmentCount,
|
|
102
|
+
matcher
|
|
103
|
+
};
|
|
104
|
+
})
|
|
105
|
+
.filter(Boolean)
|
|
106
|
+
.sort(compareSurfaceRouteSpecificity);
|
|
120
107
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
return {
|
|
124
|
-
id: surfaceId,
|
|
125
|
-
routeBase,
|
|
126
|
-
segmentCount: matcher.segmentCount,
|
|
127
|
-
staticSegmentCount: matcher.staticSegmentCount,
|
|
128
|
-
matcher
|
|
129
|
-
};
|
|
130
|
-
})
|
|
131
|
-
.filter(Boolean)
|
|
132
|
-
.sort(compareSurfaceRouteSpecificity);
|
|
108
|
+
function matchSurface(pathname) {
|
|
109
|
+
return routedSurfaces.find((surface) => surface.matcher.test(pathname))?.id || normalizedDefaultSurfaceId;
|
|
133
110
|
}
|
|
134
111
|
|
|
135
112
|
function resolveApiNamespace(pathname) {
|
|
136
|
-
|
|
137
|
-
if (!matchesPathPrefix(normalizedPathname, apiBasePath)) {
|
|
113
|
+
if (!matchesPathPrefix(pathname, normalizedApiBasePath)) {
|
|
138
114
|
return "";
|
|
139
115
|
}
|
|
140
116
|
|
|
141
|
-
|
|
142
|
-
return apiBasePath;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
const remainder = normalizedPathname.slice(apiBasePath.length);
|
|
146
|
-
if (!remainder || remainder === "/") {
|
|
147
|
-
return apiBasePath;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const versionMatch = remainder.match(/^\/v[0-9]+(?:$|\/)/);
|
|
117
|
+
const versionMatch = pathname.slice(normalizedApiBasePath.length).match(/^\/v[0-9]+(?:$|\/)/);
|
|
151
118
|
if (!versionMatch) {
|
|
152
|
-
return
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const versionPath = String(versionMatch[0]).replace(/\/$/, "");
|
|
156
|
-
return normalizePathname(`${apiBasePath}${versionPath}`);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function resolveSurfaceFromApiPathname(pathname) {
|
|
160
|
-
const apiNamespace = resolveApiNamespace(pathname);
|
|
161
|
-
if (!apiNamespace) {
|
|
162
|
-
return "";
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const normalizedPathname = normalizePathname(pathname);
|
|
166
|
-
const remainder = normalizedPathname.slice(apiNamespace.length) || "/";
|
|
167
|
-
const apiRelativePath = normalizePathname(remainder.startsWith("/") ? remainder : `/${remainder}`);
|
|
168
|
-
|
|
169
|
-
for (const surface of routedSurfaceDefinitions()) {
|
|
170
|
-
if (surface.matcher.test(apiRelativePath)) {
|
|
171
|
-
return surface.id;
|
|
172
|
-
}
|
|
119
|
+
return normalizedApiBasePath;
|
|
173
120
|
}
|
|
174
121
|
|
|
175
|
-
return
|
|
122
|
+
return normalizePathname(`${normalizedApiBasePath}${versionMatch[0]}`);
|
|
176
123
|
}
|
|
177
124
|
|
|
178
125
|
function resolveSurfaceFromPathname(pathname) {
|
|
179
126
|
const normalizedPathname = normalizePathname(pathname);
|
|
180
|
-
const
|
|
181
|
-
if (
|
|
182
|
-
return
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
for (const surface of routedSurfaceDefinitions()) {
|
|
186
|
-
if (surface.matcher.test(normalizedPathname)) {
|
|
187
|
-
return surface.id;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return defaultSurfaceId;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function withSurfaceRouteBase(surface, path) {
|
|
195
|
-
const normalizedPath = normalizePathname(path);
|
|
196
|
-
const routeBase = resolveSurfaceRouteBase(surface);
|
|
197
|
-
if (normalizedPath === "/") {
|
|
198
|
-
return routeBase || "/";
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (routeBase === "/") {
|
|
202
|
-
return normalizedPath;
|
|
203
|
-
}
|
|
204
|
-
return normalizePathname(`${routeBase}${normalizedPath}`);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
function normalizeOptionalRoutePath(path) {
|
|
208
|
-
const normalizedPath = String(path || "").trim();
|
|
209
|
-
if (!normalizedPath) {
|
|
210
|
-
return "";
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return normalizePathname(normalizedPath);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function createSurfacePaths(surface) {
|
|
217
|
-
const normalizedSurface = normalizeSurface(surface);
|
|
218
|
-
const routeBase = resolveSurfaceRouteBase(normalizedSurface);
|
|
219
|
-
|
|
220
|
-
const rootPath = routeBase || "/";
|
|
221
|
-
const namedPaths = {};
|
|
222
|
-
for (const [routeName, routePath] of Object.entries(routeConfig)) {
|
|
223
|
-
const normalizedRouteName = String(routeName || "").trim();
|
|
224
|
-
if (!normalizedRouteName) {
|
|
225
|
-
continue;
|
|
226
|
-
}
|
|
227
|
-
const normalizedRoutePath = normalizeOptionalRoutePath(routePath);
|
|
228
|
-
if (!normalizedRoutePath) {
|
|
229
|
-
continue;
|
|
230
|
-
}
|
|
231
|
-
namedPaths[normalizedRouteName] = withSurfaceRouteBase(normalizedSurface, normalizedRoutePath);
|
|
127
|
+
const apiNamespace = resolveApiNamespace(normalizedPathname);
|
|
128
|
+
if (!apiNamespace) {
|
|
129
|
+
return matchSurface(normalizedPathname);
|
|
232
130
|
}
|
|
233
131
|
|
|
234
|
-
|
|
235
|
-
...namedPaths
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
return {
|
|
239
|
-
surface: normalizedSurface,
|
|
240
|
-
routeBase,
|
|
241
|
-
rootPath,
|
|
242
|
-
namedPaths: frozenNamedPaths,
|
|
243
|
-
isNamedPath(routeName, pathname) {
|
|
244
|
-
const normalizedRouteName = String(routeName || "").trim();
|
|
245
|
-
if (!normalizedRouteName || !Object.hasOwn(frozenNamedPaths, normalizedRouteName)) {
|
|
246
|
-
return false;
|
|
247
|
-
}
|
|
248
|
-
return normalizePathname(pathname) === frozenNamedPaths[normalizedRouteName];
|
|
249
|
-
}
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
function resolveSurfacePaths(pathname) {
|
|
254
|
-
const browserPathname =
|
|
255
|
-
typeof window !== "undefined" && window?.location?.pathname ? String(window.location.pathname) : "";
|
|
256
|
-
const effectivePathname = browserPathname || pathname;
|
|
257
|
-
return createSurfacePaths(resolveSurfaceFromPathname(effectivePathname));
|
|
132
|
+
return matchSurface(normalizePathname(normalizedPathname.slice(apiNamespace.length)));
|
|
258
133
|
}
|
|
259
134
|
|
|
260
135
|
return Object.freeze({
|
|
261
|
-
|
|
262
|
-
matchesPathPrefix,
|
|
263
|
-
compileSurfaceRouteMatcher,
|
|
264
|
-
resolveSurfaceFromApiPathname,
|
|
265
|
-
resolveSurfaceFromPathname,
|
|
266
|
-
resolveSurfaceRouteBase,
|
|
267
|
-
withSurfaceRouteBase,
|
|
268
|
-
createSurfacePaths,
|
|
269
|
-
resolveSurfacePaths
|
|
136
|
+
resolveSurfaceFromPathname
|
|
270
137
|
});
|
|
271
138
|
}
|
|
272
139
|
|
|
273
|
-
export { createSurfacePathHelpers, normalizePathname, matchesPathPrefix };
|
|
140
|
+
export { API_BASE_PATH, createSurfacePathHelpers, normalizePathname, matchesPathPrefix };
|
|
@@ -81,7 +81,8 @@ function createSurfaceRegistry(options = {}) {
|
|
|
81
81
|
...sourceDefinition,
|
|
82
82
|
id: normalizedId,
|
|
83
83
|
pagesRoot,
|
|
84
|
-
routeBase: deriveSurfaceRouteBaseFromPagesRoot(pagesRoot)
|
|
84
|
+
routeBase: deriveSurfaceRouteBaseFromPagesRoot(pagesRoot),
|
|
85
|
+
enabled: sourceDefinition.enabled !== false
|
|
85
86
|
})
|
|
86
87
|
];
|
|
87
88
|
})
|
|
@@ -1,41 +1,9 @@
|
|
|
1
1
|
import { createSurfacePathHelpers } from "./paths.js";
|
|
2
|
-
import {
|
|
3
|
-
createSurfaceRegistry,
|
|
4
|
-
deriveSurfaceRouteBaseFromPagesRoot,
|
|
5
|
-
normalizeSurfaceId,
|
|
6
|
-
normalizeSurfacePagesRoot
|
|
7
|
-
} from "./registry.js";
|
|
2
|
+
import { createSurfaceRegistry, normalizeSurfaceId } from "./registry.js";
|
|
8
3
|
import { isRecord } from "../support/normalize.js";
|
|
9
4
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const ordered = [];
|
|
13
|
-
for (const id of ids) {
|
|
14
|
-
const normalizedId = normalizeSurfaceId(id);
|
|
15
|
-
if (!normalizedId || seen.has(normalizedId)) {
|
|
16
|
-
continue;
|
|
17
|
-
}
|
|
18
|
-
seen.add(normalizedId);
|
|
19
|
-
ordered.push(normalizedId);
|
|
20
|
-
}
|
|
21
|
-
return ordered;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function resolveSurfaceIds({ surfaces = {} } = {}) {
|
|
25
|
-
const surfaceCandidates = [];
|
|
26
|
-
for (const [key, value] of Object.entries(isRecord(surfaces) ? surfaces : {})) {
|
|
27
|
-
const record = isRecord(value) ? value : {};
|
|
28
|
-
surfaceCandidates.push(record.id || key);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const fromSurfaces = uniqueSurfaceIds(surfaceCandidates);
|
|
32
|
-
if (fromSurfaces.length > 0) {
|
|
33
|
-
return fromSurfaces;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
throw new Error("createSurfaceRuntime requires at least one surface id.");
|
|
37
|
-
}
|
|
38
|
-
|
|
5
|
+
// Unlike normalizePathname, this keeps "?" so vue-router optional params
|
|
6
|
+
// such as "/users/:id?" survive when a route is hoisted to an absolute path.
|
|
39
7
|
function normalizeRoutePath(pathLike = "") {
|
|
40
8
|
const rawPath = String(pathLike || "").trim();
|
|
41
9
|
if (!rawPath || rawPath === "/") {
|
|
@@ -44,9 +12,6 @@ function normalizeRoutePath(pathLike = "") {
|
|
|
44
12
|
|
|
45
13
|
const withLeadingSlash = rawPath.startsWith("/") ? rawPath : `/${rawPath}`;
|
|
46
14
|
const compacted = withLeadingSlash.replace(/\/{2,}/g, "/");
|
|
47
|
-
if (compacted === "/") {
|
|
48
|
-
return "/";
|
|
49
|
-
}
|
|
50
15
|
return compacted.replace(/\/+$/, "") || "/";
|
|
51
16
|
}
|
|
52
17
|
|
|
@@ -99,16 +64,7 @@ function resolveRouteSurface(route, inheritedSurfaceId = "", { surfaceRuntime, a
|
|
|
99
64
|
return normalizedInheritedSurface;
|
|
100
65
|
}
|
|
101
66
|
|
|
102
|
-
|
|
103
|
-
if (routePath && routePath.startsWith("/")) {
|
|
104
|
-
return surfaceRuntime.resolveSurfaceFromPathname(routePath);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (absolutePath) {
|
|
108
|
-
return surfaceRuntime.resolveSurfaceFromPathname(absolutePath);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return surfaceRuntime.DEFAULT_SURFACE_ID;
|
|
67
|
+
return surfaceRuntime.resolveSurfaceFromPathname(absolutePath);
|
|
112
68
|
}
|
|
113
69
|
|
|
114
70
|
function normalizeRoutesBySurfaceBoundaries(routeList = [], { surfaceRuntime } = {}) {
|
|
@@ -186,48 +142,19 @@ function normalizeRoutesBySurfaceBoundaries(routeList = [], { surfaceRuntime } =
|
|
|
186
142
|
|
|
187
143
|
function createSurfaceRuntime(options = {}) {
|
|
188
144
|
const allMode = normalizeSurfaceId(options?.allMode || "all") || "all";
|
|
189
|
-
const sourceSurfaces = isRecord(options?.surfaces) ? options.surfaces : {};
|
|
190
|
-
const surfaceIds = resolveSurfaceIds({
|
|
191
|
-
surfaces: sourceSurfaces
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
const normalizedSurfaces = {};
|
|
195
|
-
for (const surfaceId of surfaceIds) {
|
|
196
|
-
const source = isRecord(sourceSurfaces[surfaceId]) ? sourceSurfaces[surfaceId] : {};
|
|
197
|
-
if (!Object.prototype.hasOwnProperty.call(source, "pagesRoot")) {
|
|
198
|
-
throw new Error(`Surface "${surfaceId}" requires pagesRoot (use "" for root).`);
|
|
199
|
-
}
|
|
200
|
-
const pagesRoot = normalizeSurfacePagesRoot(source.pagesRoot);
|
|
201
|
-
normalizedSurfaces[surfaceId] = {
|
|
202
|
-
...source,
|
|
203
|
-
id: surfaceId,
|
|
204
|
-
pagesRoot,
|
|
205
|
-
routeBase: deriveSurfaceRouteBaseFromPagesRoot(pagesRoot),
|
|
206
|
-
enabled: source.enabled !== false
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
const defaultSurfaceId = normalizeSurfaceId(options?.defaultSurfaceId) || surfaceIds[0];
|
|
211
145
|
const registry = createSurfaceRegistry({
|
|
212
|
-
surfaces:
|
|
213
|
-
defaultSurfaceId
|
|
146
|
+
surfaces: isRecord(options?.surfaces) ? options.surfaces : {},
|
|
147
|
+
defaultSurfaceId: options?.defaultSurfaceId
|
|
214
148
|
});
|
|
149
|
+
const { SURFACE_REGISTRY, DEFAULT_SURFACE_ID } = registry;
|
|
150
|
+
const surfaceDefinitions = registry.listSurfaceDefinitions();
|
|
151
|
+
const surfaceIds = surfaceDefinitions.map((definition) => definition.id);
|
|
152
|
+
const enabledSurfaceIds = surfaceDefinitions.filter((definition) => definition.enabled).map((definition) => definition.id);
|
|
215
153
|
|
|
216
154
|
const pathHelpers = createSurfacePathHelpers({
|
|
217
|
-
apiBasePath:
|
|
218
|
-
defaultSurfaceId:
|
|
219
|
-
|
|
220
|
-
resolveSurfaceRouteBase: registry.resolveSurfaceRouteBase,
|
|
221
|
-
listSurfaceDefinitions: registry.listSurfaceDefinitions,
|
|
222
|
-
routes: options?.routes
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
const enabledSurfaceIds = surfaceIds.filter((surfaceId) => normalizedSurfaces[surfaceId]?.enabled !== false);
|
|
226
|
-
const defaultSurfaceSource = isRecord(normalizedSurfaces[registry.DEFAULT_SURFACE_ID])
|
|
227
|
-
? normalizedSurfaces[registry.DEFAULT_SURFACE_ID]
|
|
228
|
-
: {};
|
|
229
|
-
const defaultSurfaceDefinition = Object.freeze({
|
|
230
|
-
...defaultSurfaceSource
|
|
155
|
+
apiBasePath: options?.apiBasePath,
|
|
156
|
+
defaultSurfaceId: DEFAULT_SURFACE_ID,
|
|
157
|
+
surfaceDefinitions
|
|
231
158
|
});
|
|
232
159
|
|
|
233
160
|
function normalizeSurfaceMode(value) {
|
|
@@ -236,7 +163,7 @@ function createSurfaceRuntime(options = {}) {
|
|
|
236
163
|
return allMode;
|
|
237
164
|
}
|
|
238
165
|
|
|
239
|
-
return
|
|
166
|
+
return SURFACE_REGISTRY[normalized] ? normalized : allMode;
|
|
240
167
|
}
|
|
241
168
|
|
|
242
169
|
function listEnabledSurfaceIds() {
|
|
@@ -244,29 +171,11 @@ function createSurfaceRuntime(options = {}) {
|
|
|
244
171
|
}
|
|
245
172
|
|
|
246
173
|
function getSurfaceDefinition(surfaceId) {
|
|
247
|
-
|
|
248
|
-
const definition = normalizedSurfaces[normalizedSurfaceId];
|
|
249
|
-
if (!definition) {
|
|
250
|
-
return null;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
return Object.freeze({
|
|
254
|
-
...definition
|
|
255
|
-
});
|
|
174
|
+
return SURFACE_REGISTRY[normalizeSurfaceId(surfaceId)] || null;
|
|
256
175
|
}
|
|
257
176
|
|
|
258
177
|
function listSurfaceDefinitions({ enabledOnly = false } = {}) {
|
|
259
|
-
|
|
260
|
-
const definitions = [];
|
|
261
|
-
for (const surfaceId of ids) {
|
|
262
|
-
const source = isRecord(normalizedSurfaces[surfaceId]) ? normalizedSurfaces[surfaceId] : {};
|
|
263
|
-
definitions.push(
|
|
264
|
-
Object.freeze({
|
|
265
|
-
...source
|
|
266
|
-
})
|
|
267
|
-
);
|
|
268
|
-
}
|
|
269
|
-
return definitions;
|
|
178
|
+
return enabledOnly ? surfaceDefinitions.filter((definition) => definition.enabled) : [...surfaceDefinitions];
|
|
270
179
|
}
|
|
271
180
|
|
|
272
181
|
function isSurfaceEnabled(surfaceId) {
|
|
@@ -281,8 +190,8 @@ function createSurfaceRuntime(options = {}) {
|
|
|
281
190
|
return {
|
|
282
191
|
SURFACE_MODE_ALL: allMode,
|
|
283
192
|
SURFACE_IDS: [...surfaceIds],
|
|
284
|
-
DEFAULT_SURFACE_ID
|
|
285
|
-
DEFAULT_SURFACE:
|
|
193
|
+
DEFAULT_SURFACE_ID,
|
|
194
|
+
DEFAULT_SURFACE: SURFACE_REGISTRY[DEFAULT_SURFACE_ID],
|
|
286
195
|
normalizeSurfaceMode,
|
|
287
196
|
resolveSurfaceFromPathname: pathHelpers.resolveSurfaceFromPathname,
|
|
288
197
|
getSurfaceDefinition,
|