@jskit-ai/kernel 0.1.4
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 +24 -0
- package/_testable/index.js +4 -0
- package/client/appConfig.js +33 -0
- package/client/componentInteraction.js +51 -0
- package/client/componentInteraction.test.js +111 -0
- package/client/descriptorSections.js +75 -0
- package/client/index.d.ts +70 -0
- package/client/index.js +3 -0
- package/client/logging.js +38 -0
- package/client/moduleBootstrap.js +670 -0
- package/client/moduleBootstrap.test.js +403 -0
- package/client/shellBootstrap.js +233 -0
- package/client/shellBootstrap.test.js +185 -0
- package/client/shellRouting.js +321 -0
- package/client/shellRouting.test.js +113 -0
- package/client/vite/clientBootstrapPlugin.js +259 -0
- package/client/vite/clientBootstrapPlugin.test.js +563 -0
- package/client/vite/index.js +3 -0
- package/internal/node/fileSystem.js +21 -0
- package/internal/node/installedPackageDescriptor.js +104 -0
- package/package.json +43 -0
- package/server/actions/ActionRuntimeServiceProvider.js +309 -0
- package/server/actions/ActionRuntimeServiceProvider.test.js +551 -0
- package/server/actions/index.js +8 -0
- package/server/container/ContainerCoreServiceProvider.js +27 -0
- package/server/container/index.js +10 -0
- package/server/exportPolicy.test.js +68 -0
- package/server/http/HttpFastifyServiceProvider.js +25 -0
- package/server/http/_testable/index.js +2 -0
- package/server/http/index.js +1 -0
- package/server/http/lib/controller.js +183 -0
- package/server/http/lib/controller.test.js +143 -0
- package/server/http/lib/errors.js +12 -0
- package/server/http/lib/httpRuntime.js +82 -0
- package/server/http/lib/index.js +18 -0
- package/server/http/lib/kernel.js +15 -0
- package/server/http/lib/kernel.test.js +880 -0
- package/server/http/lib/middlewareRuntime.js +149 -0
- package/server/http/lib/requestActionExecutor.js +258 -0
- package/server/http/lib/requestScope.js +59 -0
- package/server/http/lib/routeRegistration.js +165 -0
- package/server/http/lib/routeSupport.js +45 -0
- package/server/http/lib/routeValidator.js +469 -0
- package/server/http/lib/routeValidator.test.js +474 -0
- package/server/http/lib/router.js +206 -0
- package/server/kernel/KernelCoreServiceProvider.js +27 -0
- package/server/kernel/index.js +10 -0
- package/server/platform/PlatformServerRuntimeServiceProvider.js +30 -0
- package/server/platform/index.js +5 -0
- package/server/platform/providerRuntime/descriptorCatalog.js +170 -0
- package/server/platform/providerRuntime/helpers.js +45 -0
- package/server/platform/providerRuntime/lockfile.js +27 -0
- package/server/platform/providerRuntime/providerLoader.js +283 -0
- package/server/platform/providerRuntime.js +142 -0
- package/server/platform/providerRuntime.test.js +217 -0
- package/server/platform/runtime.js +40 -0
- package/server/platform/surfaceRuntime.js +150 -0
- package/server/platform/surfaceRuntime.test.js +136 -0
- package/server/registries/actionSurfaceSourceRegistry.js +150 -0
- package/server/registries/bootstrapPayloadContributorRegistry.js +41 -0
- package/server/registries/domainEventListenerRegistry.js +61 -0
- package/server/registries/index.js +36 -0
- package/server/registries/primitives.js +63 -0
- package/server/registries/routeVisibilityResolverRegistry.js +87 -0
- package/server/registries/serviceRegistrationRegistry.js +431 -0
- package/server/runtime/ServerRuntimeCoreServiceProvider.js +65 -0
- package/server/runtime/ServerRuntimeCoreServiceProvider.test.js +53 -0
- package/server/runtime/apiRoutePolicyParity.test.js +109 -0
- package/server/runtime/apiRouteRegistration.js +65 -0
- package/server/runtime/bootBootstrapRoutes.js +46 -0
- package/server/runtime/bootBootstrapRoutes.test.js +79 -0
- package/server/runtime/bootstrapContributors.test.js +114 -0
- package/server/runtime/canonicalJson.js +74 -0
- package/server/runtime/composition.js +142 -0
- package/server/runtime/domainEvents.test.js +114 -0
- package/server/runtime/domainRules.js +50 -0
- package/server/runtime/domainRules.test.js +87 -0
- package/server/runtime/entityChangeEvents.js +182 -0
- package/server/runtime/entityChangeEvents.test.js +211 -0
- package/server/runtime/errors.js +68 -0
- package/server/runtime/errors.test.js +73 -0
- package/server/runtime/fastifyBootstrap.js +372 -0
- package/server/runtime/fastifyBootstrap.test.js +194 -0
- package/server/runtime/index.js +6 -0
- package/server/runtime/integers.js +13 -0
- package/server/runtime/moduleConfig.js +269 -0
- package/server/runtime/moduleConfig.test.js +141 -0
- package/server/runtime/pagination.js +13 -0
- package/server/runtime/realtimeNormalization.js +21 -0
- package/server/runtime/requestUrl.js +38 -0
- package/server/runtime/routeUtils.js +20 -0
- package/server/runtime/runtimeAssembly.js +113 -0
- package/server/runtime/runtimeKernel.js +55 -0
- package/server/runtime/securityAudit.js +269 -0
- package/server/runtime/securityAudit.test.js +41 -0
- package/server/runtime/serviceAuthorization.js +113 -0
- package/server/runtime/serviceAuthorization.test.js +100 -0
- package/server/runtime/serviceRegistration.test.js +197 -0
- package/server/support/SupportCoreServiceProvider.js +25 -0
- package/server/support/appConfig.js +37 -0
- package/server/support/appConfig.test.js +94 -0
- package/server/support/defaultMissingHandler.js +7 -0
- package/server/support/index.js +2 -0
- package/server/support/routePolicyConfig.js +51 -0
- package/server/support/symlinkSafeRequire.js +78 -0
- package/server/support/symlinkSafeRequire.test.js +27 -0
- package/server/surface/SurfaceRoutingServiceProvider.js +27 -0
- package/server/surface/index.js +19 -0
- package/shared/actions/actionContributorHelpers.js +34 -0
- package/shared/actions/actionContributorHelpers.test.js +16 -0
- package/shared/actions/actionDefinitions.js +488 -0
- package/shared/actions/actionDefinitions.test.js +212 -0
- package/shared/actions/audit.js +7 -0
- package/shared/actions/executionContext.js +97 -0
- package/shared/actions/executionContext.test.js +66 -0
- package/shared/actions/idempotency.js +62 -0
- package/shared/actions/index.js +2 -0
- package/shared/actions/observability.js +10 -0
- package/shared/actions/pipeline.js +287 -0
- package/shared/actions/policies.js +342 -0
- package/shared/actions/policies.test.js +233 -0
- package/shared/actions/registry.js +187 -0
- package/shared/actions/registry.test.js +381 -0
- package/shared/actions/requestMeta.js +36 -0
- package/shared/actions/textNormalization.js +3 -0
- package/shared/actions/withActionDefaults.js +34 -0
- package/shared/index.js +2 -0
- package/shared/runtime/application.js +323 -0
- package/shared/runtime/container.js +261 -0
- package/shared/runtime/containerErrors.js +22 -0
- package/shared/runtime/index.js +18 -0
- package/shared/runtime/kernelErrors.js +20 -0
- package/shared/runtime/serviceProvider.js +13 -0
- package/shared/support/formatDateTime.js +10 -0
- package/shared/support/formatDateTime.test.js +15 -0
- package/shared/support/index.js +14 -0
- package/shared/support/linkPath.js +67 -0
- package/shared/support/linkPath.test.js +35 -0
- package/shared/support/normalize.js +116 -0
- package/shared/support/normalize.test.js +48 -0
- package/shared/support/packageDescriptor.test.js +121 -0
- package/shared/support/permissions.js +50 -0
- package/shared/support/pickOwnProperties.js +17 -0
- package/shared/support/pickOwnProperties.test.js +25 -0
- package/shared/support/policies.js +11 -0
- package/shared/support/queryPath.js +33 -0
- package/shared/support/queryPath.test.js +19 -0
- package/shared/support/queryResilience.js +34 -0
- package/shared/support/queryResilience.test.js +33 -0
- package/shared/support/returnToPath.js +153 -0
- package/shared/support/returnToPath.test.js +123 -0
- package/shared/support/sorting.js +15 -0
- package/shared/support/tokens.js +23 -0
- package/shared/support/tokens.test.js +17 -0
- package/shared/support/visibility.js +56 -0
- package/shared/support/visibility.test.js +45 -0
- package/shared/surface/apiPaths.js +84 -0
- package/shared/surface/escapeRegExp.js +5 -0
- package/shared/surface/index.js +6 -0
- package/shared/surface/paths.js +273 -0
- package/shared/surface/registry.js +135 -0
- package/shared/surface/registry.test.js +44 -0
- package/shared/surface/runtime.js +357 -0
- package/shared/surface/runtime.test.js +319 -0
- package/shared/validators/createCursorListValidator.js +42 -0
- package/shared/validators/createCursorListValidator.test.js +34 -0
- package/shared/validators/cursorPaginationQueryValidator.js +31 -0
- package/shared/validators/cursorPaginationQueryValidator.test.js +21 -0
- package/shared/validators/index.js +12 -0
- package/shared/validators/inputNormalization.js +13 -0
- package/shared/validators/mergeObjectSchemas.js +31 -0
- package/shared/validators/mergeObjectSchemas.test.js +67 -0
- package/shared/validators/mergeValidators.js +89 -0
- package/shared/validators/mergeValidators.test.js +116 -0
- package/shared/validators/nestValidator.js +53 -0
- package/shared/validators/nestValidator.test.js +60 -0
- package/shared/validators/recordIdParamsValidator.js +36 -0
- package/shared/validators/recordIdParamsValidator.test.js +20 -0
- package/shared/validators/resourceRequiredMetadata.js +41 -0
- package/shared/validators/resourceRequiredMetadata.test.js +49 -0
- package/test/barrelExposure.test.js +106 -0
- package/test/dynamicImportPolicy.test.js +89 -0
- package/test/exportsContract.test.js +168 -0
- package/test/routeInputContractGuard.test.js +78 -0
- package/test/surfaceIndependence.test.js +109 -0
|
@@ -0,0 +1,670 @@
|
|
|
1
|
+
import { createApplication } from "../shared/runtime/application.js";
|
|
2
|
+
import { filterRoutesBySurface } from "../shared/surface/runtime.js";
|
|
3
|
+
import { isRecord } from "../shared/support/normalize.js";
|
|
4
|
+
import { normalizeDescriptorClientProviders, normalizeDescriptorUiRoutes } from "./descriptorSections.js";
|
|
5
|
+
import { createStructuredLogger, summarizeRouterRoutes } from "./logging.js";
|
|
6
|
+
|
|
7
|
+
const CLIENT_MODULE_RUNTIME_APP_TOKEN = Symbol.for("jskit.client.runtime.app");
|
|
8
|
+
const CLIENT_MODULE_ROUTER_TOKEN = Symbol.for("jskit.client.router");
|
|
9
|
+
const CLIENT_MODULE_VUE_APP_TOKEN = Symbol.for("jskit.client.vue.app");
|
|
10
|
+
const CLIENT_MODULE_ENV_TOKEN = Symbol.for("jskit.client.env");
|
|
11
|
+
const CLIENT_MODULE_SURFACE_RUNTIME_TOKEN = Symbol.for("jskit.client.surface.runtime");
|
|
12
|
+
const CLIENT_MODULE_SURFACE_MODE_TOKEN = Symbol.for("jskit.client.surface.mode");
|
|
13
|
+
const CLIENT_MODULE_LOGGER_TOKEN = Symbol.for("jskit.client.logger");
|
|
14
|
+
|
|
15
|
+
function normalizePackageId(value) {
|
|
16
|
+
return String(value || "").trim();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function toRouteSnapshot(route) {
|
|
20
|
+
const metaJskit = isRecord(route?.meta?.jskit) ? route.meta.jskit : {};
|
|
21
|
+
return Object.freeze({
|
|
22
|
+
id: String(route?.id || "").trim(),
|
|
23
|
+
name: String(route?.name || "").trim(),
|
|
24
|
+
path: String(route?.path || "").trim(),
|
|
25
|
+
scope: String(route?.scope || "").trim(),
|
|
26
|
+
surface: String(route?.surface || "").trim(),
|
|
27
|
+
metaScope: String(metaJskit.scope || "").trim(),
|
|
28
|
+
metaSurface: String(metaJskit.surface || "").trim()
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isRouteComponent(value) {
|
|
33
|
+
if (typeof value === "function") {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function normalizeRoute(route, { packageId, index }) {
|
|
40
|
+
if (!isRecord(route)) {
|
|
41
|
+
throw new TypeError(`Client route #${index} from ${packageId} must be an object.`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const id = String(route.id || "").trim();
|
|
45
|
+
const path = String(route.path || "").trim();
|
|
46
|
+
if (!id) {
|
|
47
|
+
throw new Error(`Client route #${index} from ${packageId} requires id.`);
|
|
48
|
+
}
|
|
49
|
+
if (!path || !path.startsWith("/") || path.startsWith("//")) {
|
|
50
|
+
throw new Error(`Client route "${id}" from ${packageId} must use an absolute path.`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!isRouteComponent(route.component)) {
|
|
54
|
+
throw new Error(`Client route "${id}" from ${packageId} requires component.`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const scope = String(route.scope || "surface")
|
|
58
|
+
.trim()
|
|
59
|
+
.toLowerCase();
|
|
60
|
+
if (scope !== "global" && scope !== "surface") {
|
|
61
|
+
throw new Error(`Client route "${id}" from ${packageId} has invalid scope "${scope}".`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const surface = String(route.surface || "")
|
|
65
|
+
.trim()
|
|
66
|
+
.toLowerCase();
|
|
67
|
+
const baseMeta = isRecord(route.meta) ? route.meta : {};
|
|
68
|
+
const baseMetaJskit = isRecord(baseMeta.jskit) ? baseMeta.jskit : {};
|
|
69
|
+
|
|
70
|
+
return Object.freeze({
|
|
71
|
+
...route,
|
|
72
|
+
id,
|
|
73
|
+
path,
|
|
74
|
+
scope,
|
|
75
|
+
...(surface ? { surface } : {}),
|
|
76
|
+
meta: {
|
|
77
|
+
...baseMeta,
|
|
78
|
+
jskit: {
|
|
79
|
+
...baseMetaJskit,
|
|
80
|
+
packageId,
|
|
81
|
+
routeId: id,
|
|
82
|
+
scope,
|
|
83
|
+
...(surface ? { surface } : {})
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function normalizeRouteList(routes, { packageId }) {
|
|
90
|
+
const entries = Array.isArray(routes) ? routes : [];
|
|
91
|
+
return Object.freeze(
|
|
92
|
+
entries.map((route, index) => normalizeRoute(route, { packageId, index: index + 1 }))
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function toVueRouteRecord(route) {
|
|
97
|
+
const nextMeta = isRecord(route.meta) ? { ...route.meta } : undefined;
|
|
98
|
+
const routeRecord = {
|
|
99
|
+
path: route.path,
|
|
100
|
+
component: route.component,
|
|
101
|
+
...(route.name ? { name: route.name } : {}),
|
|
102
|
+
...(route.props !== undefined ? { props: route.props } : {}),
|
|
103
|
+
...(route.redirect !== undefined ? { redirect: route.redirect } : {}),
|
|
104
|
+
...(route.children !== undefined ? { children: route.children } : {}),
|
|
105
|
+
...(nextMeta ? { meta: nextMeta } : {})
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
return Object.freeze(routeRecord);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function registerClientModuleRoutes({
|
|
112
|
+
packageId,
|
|
113
|
+
routes = [],
|
|
114
|
+
router,
|
|
115
|
+
surfaceRuntime,
|
|
116
|
+
surfaceMode,
|
|
117
|
+
seenRoutePaths,
|
|
118
|
+
seenRouteNames,
|
|
119
|
+
logger = null,
|
|
120
|
+
source = "module",
|
|
121
|
+
descriptorRouteDeclarations = null
|
|
122
|
+
} = {}) {
|
|
123
|
+
const normalizedPackageId = normalizePackageId(packageId);
|
|
124
|
+
if (!normalizedPackageId) {
|
|
125
|
+
throw new TypeError("registerClientModuleRoutes requires packageId.");
|
|
126
|
+
}
|
|
127
|
+
if (!router || typeof router.addRoute !== "function") {
|
|
128
|
+
throw new TypeError("registerClientModuleRoutes requires router.addRoute().");
|
|
129
|
+
}
|
|
130
|
+
if (!surfaceRuntime || typeof surfaceRuntime !== "object") {
|
|
131
|
+
throw new TypeError("registerClientModuleRoutes requires surfaceRuntime.");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const normalizedRoutes = normalizeRouteList(routes, { packageId: normalizedPackageId });
|
|
135
|
+
assertRoutesDeclaredInDescriptor({
|
|
136
|
+
packageId: normalizedPackageId,
|
|
137
|
+
source,
|
|
138
|
+
normalizedRoutes,
|
|
139
|
+
descriptorRouteDeclarations
|
|
140
|
+
});
|
|
141
|
+
const activeRoutes = filterRoutesBySurface(normalizedRoutes, {
|
|
142
|
+
surfaceRuntime,
|
|
143
|
+
surfaceMode
|
|
144
|
+
});
|
|
145
|
+
const log = createStructuredLogger(logger);
|
|
146
|
+
log.debug(
|
|
147
|
+
{
|
|
148
|
+
packageId: normalizedPackageId,
|
|
149
|
+
source,
|
|
150
|
+
surfaceMode: String(surfaceMode || "").trim(),
|
|
151
|
+
declaredRoutes: normalizedRoutes.map(toRouteSnapshot),
|
|
152
|
+
activeRoutes: activeRoutes.map(toRouteSnapshot)
|
|
153
|
+
},
|
|
154
|
+
"Client route registration analysis."
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
const declaredPaths = normalizedRoutes.map((route) => route.path);
|
|
158
|
+
const activePaths = activeRoutes.map((route) => route.path);
|
|
159
|
+
|
|
160
|
+
let registeredCount = 0;
|
|
161
|
+
for (const route of activeRoutes) {
|
|
162
|
+
const normalizedPath = String(route.path || "").trim();
|
|
163
|
+
if (seenRoutePaths.has(normalizedPath)) {
|
|
164
|
+
throw new Error(`Client route path "${normalizedPath}" is duplicated (package ${normalizedPackageId}).`);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const normalizedName = String(route.name || "").trim();
|
|
168
|
+
if (normalizedName) {
|
|
169
|
+
if (seenRouteNames.has(normalizedName)) {
|
|
170
|
+
throw new Error(`Client route name "${normalizedName}" is duplicated (package ${normalizedPackageId}).`);
|
|
171
|
+
}
|
|
172
|
+
seenRouteNames.add(normalizedName);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
seenRoutePaths.add(normalizedPath);
|
|
176
|
+
router.addRoute(toVueRouteRecord(route));
|
|
177
|
+
log.debug(
|
|
178
|
+
{
|
|
179
|
+
packageId: normalizedPackageId,
|
|
180
|
+
source,
|
|
181
|
+
route: toRouteSnapshot(route)
|
|
182
|
+
},
|
|
183
|
+
"Added client route to router."
|
|
184
|
+
);
|
|
185
|
+
registeredCount += 1;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return Object.freeze({
|
|
189
|
+
packageId: normalizedPackageId,
|
|
190
|
+
source,
|
|
191
|
+
declaredCount: normalizedRoutes.length,
|
|
192
|
+
registeredCount,
|
|
193
|
+
declaredPaths: Object.freeze(declaredPaths),
|
|
194
|
+
activePaths: Object.freeze(activePaths)
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function isProviderClass(candidate) {
|
|
199
|
+
if (typeof candidate !== "function") {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const providerId = String(candidate.id || "").trim();
|
|
204
|
+
if (!providerId) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const prototype = candidate.prototype;
|
|
209
|
+
if (!prototype || typeof prototype !== "object") {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return (
|
|
214
|
+
typeof prototype.register === "function" ||
|
|
215
|
+
typeof prototype.boot === "function" ||
|
|
216
|
+
typeof prototype.shutdown === "function"
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function normalizeExplicitProviderClasses(value, packageId) {
|
|
221
|
+
if (!Array.isArray(value)) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const providers = [];
|
|
226
|
+
for (const candidate of value) {
|
|
227
|
+
if (!isProviderClass(candidate)) {
|
|
228
|
+
throw new TypeError(`Client module ${packageId} exports invalid clientProviders entry.`);
|
|
229
|
+
}
|
|
230
|
+
providers.push(candidate);
|
|
231
|
+
}
|
|
232
|
+
return providers;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function resolveDescriptorProviderClasses(moduleNamespace, packageId, descriptorClientProviders = []) {
|
|
236
|
+
const providers = [];
|
|
237
|
+
const seenProviderIds = new Set();
|
|
238
|
+
|
|
239
|
+
for (const providerDeclaration of descriptorClientProviders) {
|
|
240
|
+
const exportName = String(providerDeclaration?.export || "").trim();
|
|
241
|
+
if (!exportName) {
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const providerClass = moduleNamespace?.[exportName];
|
|
246
|
+
if (!isProviderClass(providerClass)) {
|
|
247
|
+
throw new TypeError(
|
|
248
|
+
`Client module ${packageId} descriptor provider export "${exportName}" is missing or invalid in "${packageId}/client".`
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const providerId = String(providerClass.id || "").trim();
|
|
253
|
+
if (!providerId) {
|
|
254
|
+
throw new TypeError(`Client module ${packageId} descriptor provider "${exportName}" requires static id.`);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (seenProviderIds.has(providerId)) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
seenProviderIds.add(providerId);
|
|
261
|
+
providers.push(providerClass);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return providers;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function resolveModuleProviderClasses(moduleNamespace, packageId, descriptorClientProviders = []) {
|
|
268
|
+
if (!isRecord(moduleNamespace)) {
|
|
269
|
+
return [];
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const explicitProviders = normalizeExplicitProviderClasses(moduleNamespace.clientProviders, packageId);
|
|
273
|
+
if (explicitProviders) {
|
|
274
|
+
return explicitProviders;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (Array.isArray(descriptorClientProviders) && descriptorClientProviders.length > 0) {
|
|
278
|
+
return resolveDescriptorProviderClasses(moduleNamespace, packageId, descriptorClientProviders);
|
|
279
|
+
}
|
|
280
|
+
return [];
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function buildDescriptorRouteDeclarationIndex({ packageId, descriptorUiRoutes = [] } = {}) {
|
|
284
|
+
const normalizedPackageId = normalizePackageId(packageId);
|
|
285
|
+
const descriptorRoutes = normalizeDescriptorUiRoutes(descriptorUiRoutes);
|
|
286
|
+
const byId = new Map();
|
|
287
|
+
|
|
288
|
+
for (const descriptorRoute of descriptorRoutes) {
|
|
289
|
+
const routeId = String(descriptorRoute.id || "").trim();
|
|
290
|
+
const routePath = String(descriptorRoute.path || "").trim();
|
|
291
|
+
const routeScope = String(descriptorRoute.scope || "surface")
|
|
292
|
+
.trim()
|
|
293
|
+
.toLowerCase();
|
|
294
|
+
if (!routeId || !routePath) {
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (byId.has(routeId)) {
|
|
299
|
+
const existingRoute = byId.get(routeId);
|
|
300
|
+
if (existingRoute.path !== routePath || existingRoute.scope !== routeScope) {
|
|
301
|
+
throw new Error(
|
|
302
|
+
`Descriptor ui routes for ${normalizedPackageId} define duplicate id "${routeId}" with conflicting declarations.`
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
byId.set(
|
|
309
|
+
routeId,
|
|
310
|
+
Object.freeze({
|
|
311
|
+
id: routeId,
|
|
312
|
+
path: routePath,
|
|
313
|
+
scope: routeScope
|
|
314
|
+
})
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return Object.freeze({ byId });
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function assertRoutesDeclaredInDescriptor({
|
|
322
|
+
packageId,
|
|
323
|
+
source,
|
|
324
|
+
normalizedRoutes = [],
|
|
325
|
+
descriptorRouteDeclarations = null
|
|
326
|
+
} = {}) {
|
|
327
|
+
const normalizedSource = String(source || "").trim();
|
|
328
|
+
if (normalizedSource !== "clientRoutes") {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const byId = descriptorRouteDeclarations?.byId instanceof Map ? descriptorRouteDeclarations.byId : new Map();
|
|
333
|
+
const normalizedPackageId = normalizePackageId(packageId);
|
|
334
|
+
|
|
335
|
+
for (const route of normalizedRoutes) {
|
|
336
|
+
const routeId = String(route?.id || "").trim();
|
|
337
|
+
const routePath = String(route?.path || "").trim();
|
|
338
|
+
const routeScope = String(route?.scope || "surface")
|
|
339
|
+
.trim()
|
|
340
|
+
.toLowerCase();
|
|
341
|
+
if (routeScope !== "global") {
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const declaredRoute = byId.get(routeId);
|
|
346
|
+
if (!declaredRoute) {
|
|
347
|
+
throw new Error(
|
|
348
|
+
`Global client route "${routeId}" from ${normalizedPackageId} (${source}) must be declared in metadata.ui.routes (id "${routeId}", path "${routePath}") with scope:"global" and autoRegister:false.`
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
if (String(declaredRoute.path || "").trim() !== routePath) {
|
|
352
|
+
throw new Error(
|
|
353
|
+
`Global client route "${routeId}" from ${normalizedPackageId} (${source}) path "${routePath}" does not match descriptor metadata.ui.routes path "${declaredRoute.path}".`
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
if (String(declaredRoute.scope || "").trim().toLowerCase() !== "global") {
|
|
357
|
+
throw new Error(
|
|
358
|
+
`Global client route "${routeId}" from ${normalizedPackageId} (${source}) must be declared with scope:"global" in metadata.ui.routes.`
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function resolveDescriptorClientRoutes({
|
|
365
|
+
packageId,
|
|
366
|
+
descriptorUiRoutes = [],
|
|
367
|
+
routeComponents = {},
|
|
368
|
+
logger = null
|
|
369
|
+
} = {}) {
|
|
370
|
+
const normalizedPackageId = normalizePackageId(packageId);
|
|
371
|
+
const descriptorRoutes = normalizeDescriptorUiRoutes(descriptorUiRoutes);
|
|
372
|
+
if (descriptorRoutes.length < 1) {
|
|
373
|
+
return Object.freeze([]);
|
|
374
|
+
}
|
|
375
|
+
if (!isRecord(routeComponents)) {
|
|
376
|
+
throw new TypeError(
|
|
377
|
+
`Client module ${normalizedPackageId} declares descriptor ui routes but does not export a routeComponents map.`
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const log = createStructuredLogger(logger);
|
|
382
|
+
const routes = [];
|
|
383
|
+
const skippedRoutes = [];
|
|
384
|
+
for (const descriptorRoute of descriptorRoutes) {
|
|
385
|
+
const routeId = String(descriptorRoute.id || "").trim();
|
|
386
|
+
const routePath = String(descriptorRoute.path || "").trim();
|
|
387
|
+
const autoRegister = descriptorRoute.autoRegister !== false;
|
|
388
|
+
if (!autoRegister) {
|
|
389
|
+
skippedRoutes.push(
|
|
390
|
+
Object.freeze({
|
|
391
|
+
id: routeId,
|
|
392
|
+
path: routePath,
|
|
393
|
+
reason: "autoRegister=false"
|
|
394
|
+
})
|
|
395
|
+
);
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (!routeId || !routePath) {
|
|
400
|
+
throw new Error(
|
|
401
|
+
`Descriptor ui route from ${normalizedPackageId} requires id and path when autoRegister is enabled.`
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const componentKey = String(descriptorRoute.componentKey || "").trim();
|
|
406
|
+
if (!componentKey) {
|
|
407
|
+
throw new Error(
|
|
408
|
+
`Descriptor ui route "${routeId}" from ${normalizedPackageId} requires componentKey when autoRegister is enabled.`
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const routeComponent = routeComponents[componentKey];
|
|
413
|
+
if (!isRouteComponent(routeComponent)) {
|
|
414
|
+
throw new Error(
|
|
415
|
+
`Descriptor ui route "${routeId}" from ${normalizedPackageId} references unknown routeComponents key "${componentKey}".`
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const scope = String(descriptorRoute.scope || "surface")
|
|
420
|
+
.trim()
|
|
421
|
+
.toLowerCase();
|
|
422
|
+
const surface = String(descriptorRoute.surface || "")
|
|
423
|
+
.trim()
|
|
424
|
+
.toLowerCase();
|
|
425
|
+
const guard = isRecord(descriptorRoute.guard) ? { ...descriptorRoute.guard } : {};
|
|
426
|
+
const baseMeta = isRecord(descriptorRoute.meta) ? { ...descriptorRoute.meta } : {};
|
|
427
|
+
const baseMetaJskit = isRecord(baseMeta.jskit) ? { ...baseMeta.jskit } : {};
|
|
428
|
+
|
|
429
|
+
routes.push(
|
|
430
|
+
Object.freeze({
|
|
431
|
+
id: routeId,
|
|
432
|
+
path: routePath,
|
|
433
|
+
scope,
|
|
434
|
+
...(surface ? { surface } : {}),
|
|
435
|
+
...(String(descriptorRoute.name || "").trim() ? { name: String(descriptorRoute.name || "").trim() } : {}),
|
|
436
|
+
component: routeComponent,
|
|
437
|
+
meta: {
|
|
438
|
+
...baseMeta,
|
|
439
|
+
...(Object.keys(guard).length > 0 ? { guard } : {}),
|
|
440
|
+
jskit: {
|
|
441
|
+
...baseMetaJskit,
|
|
442
|
+
packageId: normalizedPackageId,
|
|
443
|
+
routeId,
|
|
444
|
+
scope,
|
|
445
|
+
componentKey,
|
|
446
|
+
source: "descriptor.ui.routes",
|
|
447
|
+
...(surface ? { surface } : {})
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
})
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
log.debug(
|
|
455
|
+
{
|
|
456
|
+
packageId: normalizedPackageId,
|
|
457
|
+
descriptorRouteCount: descriptorRoutes.length,
|
|
458
|
+
autoRegisterRouteCount: routes.length,
|
|
459
|
+
skippedRoutes
|
|
460
|
+
},
|
|
461
|
+
"Processed descriptor ui routes."
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
return Object.freeze(routes);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function normalizeClientModuleEntries(clientModules) {
|
|
468
|
+
if (!Array.isArray(clientModules)) {
|
|
469
|
+
return [];
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return clientModules
|
|
473
|
+
.map((entry) => {
|
|
474
|
+
const packageId = normalizePackageId(entry?.packageId);
|
|
475
|
+
const moduleNamespace = isRecord(entry?.module) ? entry.module : null;
|
|
476
|
+
if (!packageId || !moduleNamespace) {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
return Object.freeze({
|
|
480
|
+
packageId,
|
|
481
|
+
module: moduleNamespace,
|
|
482
|
+
descriptorUiRoutes: normalizeDescriptorUiRoutes(entry?.descriptorUiRoutes),
|
|
483
|
+
descriptorClientProviders: normalizeDescriptorClientProviders(entry?.descriptorClientProviders)
|
|
484
|
+
});
|
|
485
|
+
})
|
|
486
|
+
.filter(Boolean)
|
|
487
|
+
.sort((left, right) => left.packageId.localeCompare(right.packageId));
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function createClientRuntimeApp({
|
|
491
|
+
profile = "client",
|
|
492
|
+
app,
|
|
493
|
+
router,
|
|
494
|
+
env,
|
|
495
|
+
logger,
|
|
496
|
+
surfaceRuntime,
|
|
497
|
+
surfaceMode
|
|
498
|
+
} = {}) {
|
|
499
|
+
const runtimeApp = createApplication({
|
|
500
|
+
profile,
|
|
501
|
+
strict: true
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
runtimeApp.instance(CLIENT_MODULE_RUNTIME_APP_TOKEN, runtimeApp);
|
|
505
|
+
runtimeApp.instance(CLIENT_MODULE_ROUTER_TOKEN, router || null);
|
|
506
|
+
runtimeApp.instance(CLIENT_MODULE_VUE_APP_TOKEN, app || null);
|
|
507
|
+
runtimeApp.instance(CLIENT_MODULE_ENV_TOKEN, isRecord(env) ? { ...env } : {});
|
|
508
|
+
runtimeApp.instance(CLIENT_MODULE_SURFACE_RUNTIME_TOKEN, surfaceRuntime || null);
|
|
509
|
+
runtimeApp.instance(CLIENT_MODULE_SURFACE_MODE_TOKEN, String(surfaceMode || "").trim());
|
|
510
|
+
runtimeApp.instance(CLIENT_MODULE_LOGGER_TOKEN, logger);
|
|
511
|
+
|
|
512
|
+
return runtimeApp;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
async function bootClientModules({
|
|
516
|
+
clientModules = [],
|
|
517
|
+
app,
|
|
518
|
+
router,
|
|
519
|
+
surfaceRuntime,
|
|
520
|
+
surfaceMode,
|
|
521
|
+
env,
|
|
522
|
+
logger = console
|
|
523
|
+
} = {}) {
|
|
524
|
+
if (!router || typeof router.addRoute !== "function") {
|
|
525
|
+
throw new TypeError("bootClientModules requires router.addRoute().");
|
|
526
|
+
}
|
|
527
|
+
if (!surfaceRuntime || typeof surfaceRuntime.normalizeSurfaceMode !== "function") {
|
|
528
|
+
throw new TypeError("bootClientModules requires surfaceRuntime.normalizeSurfaceMode().");
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
const log = createStructuredLogger(logger);
|
|
532
|
+
const moduleEntries = normalizeClientModuleEntries(clientModules);
|
|
533
|
+
const runtimeApp = createClientRuntimeApp({
|
|
534
|
+
profile: String(surfaceRuntime.normalizeSurfaceMode(surfaceMode) || "client"),
|
|
535
|
+
app,
|
|
536
|
+
router,
|
|
537
|
+
env,
|
|
538
|
+
logger: log,
|
|
539
|
+
surfaceRuntime,
|
|
540
|
+
surfaceMode
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
const providerClasses = [];
|
|
544
|
+
const seenProviderIds = new Set();
|
|
545
|
+
log.debug(
|
|
546
|
+
{
|
|
547
|
+
surfaceMode: String(surfaceMode || "").trim(),
|
|
548
|
+
normalizedSurfaceMode: String(surfaceRuntime.normalizeSurfaceMode(surfaceMode) || "").trim(),
|
|
549
|
+
moduleCount: moduleEntries.length,
|
|
550
|
+
modules: moduleEntries.map((entry) => entry.packageId)
|
|
551
|
+
},
|
|
552
|
+
"Starting JSKIT client module bootstrap."
|
|
553
|
+
);
|
|
554
|
+
for (const entry of moduleEntries) {
|
|
555
|
+
const providers = resolveModuleProviderClasses(entry.module, entry.packageId, entry.descriptorClientProviders);
|
|
556
|
+
log.debug(
|
|
557
|
+
{
|
|
558
|
+
packageId: entry.packageId,
|
|
559
|
+
providerExports: providers.map((providerClass) => String(providerClass.id || providerClass.name || "").trim()),
|
|
560
|
+
hasClientRoutes: Array.isArray(entry.module.clientRoutes) && entry.module.clientRoutes.length > 0
|
|
561
|
+
},
|
|
562
|
+
"Discovered client module capabilities."
|
|
563
|
+
);
|
|
564
|
+
for (const providerClass of providers) {
|
|
565
|
+
const providerId = String(providerClass.id || "").trim();
|
|
566
|
+
if (seenProviderIds.has(providerId)) {
|
|
567
|
+
throw new Error(`Client provider id "${providerId}" is duplicated.`);
|
|
568
|
+
}
|
|
569
|
+
seenProviderIds.add(providerId);
|
|
570
|
+
providerClasses.push(providerClass);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
if (providerClasses.length > 0) {
|
|
575
|
+
await runtimeApp.start({ providers: providerClasses });
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const seenRoutePaths = new Set();
|
|
579
|
+
const seenRouteNames = new Set();
|
|
580
|
+
const routeResults = [];
|
|
581
|
+
const registerRoutesForEntry = (routeList, packageId, source = "module", descriptorRouteDeclarations = null) => {
|
|
582
|
+
if (!routeList || routeList.length === 0) {
|
|
583
|
+
return null;
|
|
584
|
+
}
|
|
585
|
+
const result = registerClientModuleRoutes({
|
|
586
|
+
packageId,
|
|
587
|
+
routes: routeList,
|
|
588
|
+
router,
|
|
589
|
+
surfaceRuntime,
|
|
590
|
+
surfaceMode,
|
|
591
|
+
seenRoutePaths,
|
|
592
|
+
seenRouteNames,
|
|
593
|
+
logger: log,
|
|
594
|
+
source,
|
|
595
|
+
descriptorRouteDeclarations
|
|
596
|
+
});
|
|
597
|
+
log.debug(
|
|
598
|
+
{
|
|
599
|
+
packageId,
|
|
600
|
+
source,
|
|
601
|
+
declaredPaths: result.declaredPaths,
|
|
602
|
+
activePaths: result.activePaths,
|
|
603
|
+
registeredCount: result.registeredCount
|
|
604
|
+
},
|
|
605
|
+
"Registered client module routes."
|
|
606
|
+
);
|
|
607
|
+
log.debug(
|
|
608
|
+
{
|
|
609
|
+
packageId,
|
|
610
|
+
source,
|
|
611
|
+
routerRoutes: summarizeRouterRoutes(router)
|
|
612
|
+
},
|
|
613
|
+
"Router route table after client route registration."
|
|
614
|
+
);
|
|
615
|
+
routeResults.push(result);
|
|
616
|
+
return result;
|
|
617
|
+
};
|
|
618
|
+
for (const entry of moduleEntries) {
|
|
619
|
+
const descriptorRouteDeclarations = buildDescriptorRouteDeclarationIndex({
|
|
620
|
+
packageId: entry.packageId,
|
|
621
|
+
descriptorUiRoutes: entry.descriptorUiRoutes
|
|
622
|
+
});
|
|
623
|
+
const descriptorRoutes = resolveDescriptorClientRoutes({
|
|
624
|
+
packageId: entry.packageId,
|
|
625
|
+
descriptorUiRoutes: entry.descriptorUiRoutes,
|
|
626
|
+
routeComponents: entry.module.routeComponents,
|
|
627
|
+
logger: log
|
|
628
|
+
});
|
|
629
|
+
registerRoutesForEntry(descriptorRoutes, entry.packageId, "descriptor.ui.routes", descriptorRouteDeclarations);
|
|
630
|
+
|
|
631
|
+
const moduleRoutes = Array.isArray(entry.module.clientRoutes) ? entry.module.clientRoutes : [];
|
|
632
|
+
registerRoutesForEntry(moduleRoutes, entry.packageId, "clientRoutes", descriptorRouteDeclarations);
|
|
633
|
+
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
const registeredRouteCount = routeResults.reduce((sum, result) => sum + result.registeredCount, 0);
|
|
637
|
+
if (moduleEntries.length > 0) {
|
|
638
|
+
log.debug(
|
|
639
|
+
{
|
|
640
|
+
modules: moduleEntries.map((entry) => entry.packageId),
|
|
641
|
+
providerCount: providerClasses.length,
|
|
642
|
+
routeCount: registeredRouteCount
|
|
643
|
+
},
|
|
644
|
+
"Booted JSKIT client modules."
|
|
645
|
+
);
|
|
646
|
+
log.debug(
|
|
647
|
+
{
|
|
648
|
+
routerRoutes: summarizeRouterRoutes(router),
|
|
649
|
+
currentPath: typeof window !== "undefined" ? String(window.location?.pathname || "") : ""
|
|
650
|
+
},
|
|
651
|
+
"JSKIT client bootstrap final route table."
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
return Object.freeze({
|
|
656
|
+
runtimeApp,
|
|
657
|
+
modules: Object.freeze(moduleEntries.map((entry) => entry.packageId)),
|
|
658
|
+
providerCount: providerClasses.length,
|
|
659
|
+
routeResults: Object.freeze(routeResults),
|
|
660
|
+
routeCount: registeredRouteCount
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
export {
|
|
665
|
+
CLIENT_MODULE_ROUTER_TOKEN,
|
|
666
|
+
CLIENT_MODULE_VUE_APP_TOKEN,
|
|
667
|
+
CLIENT_MODULE_ENV_TOKEN,
|
|
668
|
+
CLIENT_MODULE_SURFACE_RUNTIME_TOKEN,
|
|
669
|
+
bootClientModules
|
|
670
|
+
};
|