@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.
Files changed (185) hide show
  1. package/README.md +24 -0
  2. package/_testable/index.js +4 -0
  3. package/client/appConfig.js +33 -0
  4. package/client/componentInteraction.js +51 -0
  5. package/client/componentInteraction.test.js +111 -0
  6. package/client/descriptorSections.js +75 -0
  7. package/client/index.d.ts +70 -0
  8. package/client/index.js +3 -0
  9. package/client/logging.js +38 -0
  10. package/client/moduleBootstrap.js +670 -0
  11. package/client/moduleBootstrap.test.js +403 -0
  12. package/client/shellBootstrap.js +233 -0
  13. package/client/shellBootstrap.test.js +185 -0
  14. package/client/shellRouting.js +321 -0
  15. package/client/shellRouting.test.js +113 -0
  16. package/client/vite/clientBootstrapPlugin.js +259 -0
  17. package/client/vite/clientBootstrapPlugin.test.js +563 -0
  18. package/client/vite/index.js +3 -0
  19. package/internal/node/fileSystem.js +21 -0
  20. package/internal/node/installedPackageDescriptor.js +104 -0
  21. package/package.json +43 -0
  22. package/server/actions/ActionRuntimeServiceProvider.js +309 -0
  23. package/server/actions/ActionRuntimeServiceProvider.test.js +551 -0
  24. package/server/actions/index.js +8 -0
  25. package/server/container/ContainerCoreServiceProvider.js +27 -0
  26. package/server/container/index.js +10 -0
  27. package/server/exportPolicy.test.js +68 -0
  28. package/server/http/HttpFastifyServiceProvider.js +25 -0
  29. package/server/http/_testable/index.js +2 -0
  30. package/server/http/index.js +1 -0
  31. package/server/http/lib/controller.js +183 -0
  32. package/server/http/lib/controller.test.js +143 -0
  33. package/server/http/lib/errors.js +12 -0
  34. package/server/http/lib/httpRuntime.js +82 -0
  35. package/server/http/lib/index.js +18 -0
  36. package/server/http/lib/kernel.js +15 -0
  37. package/server/http/lib/kernel.test.js +880 -0
  38. package/server/http/lib/middlewareRuntime.js +149 -0
  39. package/server/http/lib/requestActionExecutor.js +258 -0
  40. package/server/http/lib/requestScope.js +59 -0
  41. package/server/http/lib/routeRegistration.js +165 -0
  42. package/server/http/lib/routeSupport.js +45 -0
  43. package/server/http/lib/routeValidator.js +469 -0
  44. package/server/http/lib/routeValidator.test.js +474 -0
  45. package/server/http/lib/router.js +206 -0
  46. package/server/kernel/KernelCoreServiceProvider.js +27 -0
  47. package/server/kernel/index.js +10 -0
  48. package/server/platform/PlatformServerRuntimeServiceProvider.js +30 -0
  49. package/server/platform/index.js +5 -0
  50. package/server/platform/providerRuntime/descriptorCatalog.js +170 -0
  51. package/server/platform/providerRuntime/helpers.js +45 -0
  52. package/server/platform/providerRuntime/lockfile.js +27 -0
  53. package/server/platform/providerRuntime/providerLoader.js +283 -0
  54. package/server/platform/providerRuntime.js +142 -0
  55. package/server/platform/providerRuntime.test.js +217 -0
  56. package/server/platform/runtime.js +40 -0
  57. package/server/platform/surfaceRuntime.js +150 -0
  58. package/server/platform/surfaceRuntime.test.js +136 -0
  59. package/server/registries/actionSurfaceSourceRegistry.js +150 -0
  60. package/server/registries/bootstrapPayloadContributorRegistry.js +41 -0
  61. package/server/registries/domainEventListenerRegistry.js +61 -0
  62. package/server/registries/index.js +36 -0
  63. package/server/registries/primitives.js +63 -0
  64. package/server/registries/routeVisibilityResolverRegistry.js +87 -0
  65. package/server/registries/serviceRegistrationRegistry.js +431 -0
  66. package/server/runtime/ServerRuntimeCoreServiceProvider.js +65 -0
  67. package/server/runtime/ServerRuntimeCoreServiceProvider.test.js +53 -0
  68. package/server/runtime/apiRoutePolicyParity.test.js +109 -0
  69. package/server/runtime/apiRouteRegistration.js +65 -0
  70. package/server/runtime/bootBootstrapRoutes.js +46 -0
  71. package/server/runtime/bootBootstrapRoutes.test.js +79 -0
  72. package/server/runtime/bootstrapContributors.test.js +114 -0
  73. package/server/runtime/canonicalJson.js +74 -0
  74. package/server/runtime/composition.js +142 -0
  75. package/server/runtime/domainEvents.test.js +114 -0
  76. package/server/runtime/domainRules.js +50 -0
  77. package/server/runtime/domainRules.test.js +87 -0
  78. package/server/runtime/entityChangeEvents.js +182 -0
  79. package/server/runtime/entityChangeEvents.test.js +211 -0
  80. package/server/runtime/errors.js +68 -0
  81. package/server/runtime/errors.test.js +73 -0
  82. package/server/runtime/fastifyBootstrap.js +372 -0
  83. package/server/runtime/fastifyBootstrap.test.js +194 -0
  84. package/server/runtime/index.js +6 -0
  85. package/server/runtime/integers.js +13 -0
  86. package/server/runtime/moduleConfig.js +269 -0
  87. package/server/runtime/moduleConfig.test.js +141 -0
  88. package/server/runtime/pagination.js +13 -0
  89. package/server/runtime/realtimeNormalization.js +21 -0
  90. package/server/runtime/requestUrl.js +38 -0
  91. package/server/runtime/routeUtils.js +20 -0
  92. package/server/runtime/runtimeAssembly.js +113 -0
  93. package/server/runtime/runtimeKernel.js +55 -0
  94. package/server/runtime/securityAudit.js +269 -0
  95. package/server/runtime/securityAudit.test.js +41 -0
  96. package/server/runtime/serviceAuthorization.js +113 -0
  97. package/server/runtime/serviceAuthorization.test.js +100 -0
  98. package/server/runtime/serviceRegistration.test.js +197 -0
  99. package/server/support/SupportCoreServiceProvider.js +25 -0
  100. package/server/support/appConfig.js +37 -0
  101. package/server/support/appConfig.test.js +94 -0
  102. package/server/support/defaultMissingHandler.js +7 -0
  103. package/server/support/index.js +2 -0
  104. package/server/support/routePolicyConfig.js +51 -0
  105. package/server/support/symlinkSafeRequire.js +78 -0
  106. package/server/support/symlinkSafeRequire.test.js +27 -0
  107. package/server/surface/SurfaceRoutingServiceProvider.js +27 -0
  108. package/server/surface/index.js +19 -0
  109. package/shared/actions/actionContributorHelpers.js +34 -0
  110. package/shared/actions/actionContributorHelpers.test.js +16 -0
  111. package/shared/actions/actionDefinitions.js +488 -0
  112. package/shared/actions/actionDefinitions.test.js +212 -0
  113. package/shared/actions/audit.js +7 -0
  114. package/shared/actions/executionContext.js +97 -0
  115. package/shared/actions/executionContext.test.js +66 -0
  116. package/shared/actions/idempotency.js +62 -0
  117. package/shared/actions/index.js +2 -0
  118. package/shared/actions/observability.js +10 -0
  119. package/shared/actions/pipeline.js +287 -0
  120. package/shared/actions/policies.js +342 -0
  121. package/shared/actions/policies.test.js +233 -0
  122. package/shared/actions/registry.js +187 -0
  123. package/shared/actions/registry.test.js +381 -0
  124. package/shared/actions/requestMeta.js +36 -0
  125. package/shared/actions/textNormalization.js +3 -0
  126. package/shared/actions/withActionDefaults.js +34 -0
  127. package/shared/index.js +2 -0
  128. package/shared/runtime/application.js +323 -0
  129. package/shared/runtime/container.js +261 -0
  130. package/shared/runtime/containerErrors.js +22 -0
  131. package/shared/runtime/index.js +18 -0
  132. package/shared/runtime/kernelErrors.js +20 -0
  133. package/shared/runtime/serviceProvider.js +13 -0
  134. package/shared/support/formatDateTime.js +10 -0
  135. package/shared/support/formatDateTime.test.js +15 -0
  136. package/shared/support/index.js +14 -0
  137. package/shared/support/linkPath.js +67 -0
  138. package/shared/support/linkPath.test.js +35 -0
  139. package/shared/support/normalize.js +116 -0
  140. package/shared/support/normalize.test.js +48 -0
  141. package/shared/support/packageDescriptor.test.js +121 -0
  142. package/shared/support/permissions.js +50 -0
  143. package/shared/support/pickOwnProperties.js +17 -0
  144. package/shared/support/pickOwnProperties.test.js +25 -0
  145. package/shared/support/policies.js +11 -0
  146. package/shared/support/queryPath.js +33 -0
  147. package/shared/support/queryPath.test.js +19 -0
  148. package/shared/support/queryResilience.js +34 -0
  149. package/shared/support/queryResilience.test.js +33 -0
  150. package/shared/support/returnToPath.js +153 -0
  151. package/shared/support/returnToPath.test.js +123 -0
  152. package/shared/support/sorting.js +15 -0
  153. package/shared/support/tokens.js +23 -0
  154. package/shared/support/tokens.test.js +17 -0
  155. package/shared/support/visibility.js +56 -0
  156. package/shared/support/visibility.test.js +45 -0
  157. package/shared/surface/apiPaths.js +84 -0
  158. package/shared/surface/escapeRegExp.js +5 -0
  159. package/shared/surface/index.js +6 -0
  160. package/shared/surface/paths.js +273 -0
  161. package/shared/surface/registry.js +135 -0
  162. package/shared/surface/registry.test.js +44 -0
  163. package/shared/surface/runtime.js +357 -0
  164. package/shared/surface/runtime.test.js +319 -0
  165. package/shared/validators/createCursorListValidator.js +42 -0
  166. package/shared/validators/createCursorListValidator.test.js +34 -0
  167. package/shared/validators/cursorPaginationQueryValidator.js +31 -0
  168. package/shared/validators/cursorPaginationQueryValidator.test.js +21 -0
  169. package/shared/validators/index.js +12 -0
  170. package/shared/validators/inputNormalization.js +13 -0
  171. package/shared/validators/mergeObjectSchemas.js +31 -0
  172. package/shared/validators/mergeObjectSchemas.test.js +67 -0
  173. package/shared/validators/mergeValidators.js +89 -0
  174. package/shared/validators/mergeValidators.test.js +116 -0
  175. package/shared/validators/nestValidator.js +53 -0
  176. package/shared/validators/nestValidator.test.js +60 -0
  177. package/shared/validators/recordIdParamsValidator.js +36 -0
  178. package/shared/validators/recordIdParamsValidator.test.js +20 -0
  179. package/shared/validators/resourceRequiredMetadata.js +41 -0
  180. package/shared/validators/resourceRequiredMetadata.test.js +49 -0
  181. package/test/barrelExposure.test.js +106 -0
  182. package/test/dynamicImportPolicy.test.js +89 -0
  183. package/test/exportsContract.test.js +168 -0
  184. package/test/routeInputContractGuard.test.js +78 -0
  185. package/test/surfaceIndependence.test.js +109 -0
@@ -0,0 +1,403 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { createSurfaceRuntime } from "../shared/surface/runtime.js";
4
+ import { bootClientModules } from "./moduleBootstrap.js";
5
+
6
+ function createRouterStub() {
7
+ const routes = [];
8
+ return {
9
+ routes,
10
+ addRoute(route) {
11
+ routes.push(route);
12
+ }
13
+ };
14
+ }
15
+
16
+ function createSurfaceRuntimeFixture() {
17
+ return createSurfaceRuntime({
18
+ allMode: "all",
19
+ surfaces: {
20
+ app: { id: "app", pagesRoot: "app", enabled: true },
21
+ admin: { id: "admin", pagesRoot: "admin", enabled: true }
22
+ },
23
+ defaultSurfaceId: "app"
24
+ });
25
+ }
26
+
27
+ test("bootClientModules registers global routes regardless of surface mode", async () => {
28
+ const router = createRouterStub();
29
+ const surfaceRuntime = createSurfaceRuntimeFixture();
30
+
31
+ const result = await bootClientModules({
32
+ clientModules: [
33
+ {
34
+ packageId: "@example/auth",
35
+ descriptorUiRoutes: [
36
+ {
37
+ id: "auth.login",
38
+ path: "/auth/login",
39
+ scope: "global",
40
+ autoRegister: false
41
+ }
42
+ ],
43
+ module: {
44
+ clientRoutes: [
45
+ {
46
+ id: "auth.login",
47
+ name: "auth-login",
48
+ path: "/auth/login",
49
+ scope: "global",
50
+ component: {}
51
+ }
52
+ ]
53
+ }
54
+ }
55
+ ],
56
+ router,
57
+ surfaceRuntime,
58
+ surfaceMode: "admin",
59
+ logger: { info() {}, warn() {}, error() {} }
60
+ });
61
+
62
+ assert.equal(result.routeResults.length, 1);
63
+ assert.equal(result.routeResults[0].declaredCount, 1);
64
+ assert.equal(result.routeResults[0].registeredCount, 1);
65
+ assert.equal(router.routes.length, 1);
66
+ assert.equal(router.routes[0].path, "/auth/login");
67
+ });
68
+
69
+ test("bootClientModules filters surface routes by mode", async () => {
70
+ const router = createRouterStub();
71
+ const surfaceRuntime = createSurfaceRuntimeFixture();
72
+
73
+ const result = await bootClientModules({
74
+ clientModules: [
75
+ {
76
+ packageId: "@example/admin",
77
+ module: {
78
+ clientRoutes: [
79
+ {
80
+ id: "admin.dashboard",
81
+ name: "admin-dashboard",
82
+ path: "/admin/dashboard",
83
+ scope: "surface",
84
+ component: {}
85
+ },
86
+ {
87
+ id: "app.dashboard",
88
+ name: "app-dashboard",
89
+ path: "/app/dashboard",
90
+ scope: "surface",
91
+ component: {}
92
+ }
93
+ ]
94
+ }
95
+ }
96
+ ],
97
+ router,
98
+ surfaceRuntime,
99
+ surfaceMode: "admin",
100
+ logger: { info() {}, warn() {}, error() {} }
101
+ });
102
+
103
+ assert.equal(result.routeResults.length, 1);
104
+ assert.equal(result.routeResults[0].declaredCount, 2);
105
+ assert.equal(result.routeResults[0].registeredCount, 1);
106
+ assert.equal(router.routes.length, 1);
107
+ assert.equal(router.routes[0].path, "/admin/dashboard");
108
+ });
109
+
110
+ test("bootClientModules registers descriptor and clientRoutes with providers only", async () => {
111
+ const router = createRouterStub();
112
+ const surfaceRuntime = createSurfaceRuntimeFixture();
113
+ const events = [];
114
+ const loginComponent = {};
115
+ class ExampleClientProvider {
116
+ static id = "example.client";
117
+ register(app) {
118
+ events.push("register");
119
+ app.instance("example.value", 42);
120
+ }
121
+ boot() {
122
+ events.push("boot");
123
+ }
124
+ }
125
+
126
+ const result = await bootClientModules({
127
+ clientModules: [
128
+ {
129
+ packageId: "@example/alpha",
130
+ module: {
131
+ clientProviders: [ExampleClientProvider],
132
+ ExampleClientProvider
133
+ }
134
+ },
135
+ {
136
+ packageId: "@example/zeta",
137
+ descriptorUiRoutes: [
138
+ {
139
+ id: "auth.default-login-2",
140
+ name: "auth-default-login-2",
141
+ path: "/auth/default-login-2",
142
+ scope: "global",
143
+ componentKey: "auth-login",
144
+ autoRegister: true
145
+ },
146
+ {
147
+ id: "auth.skipped",
148
+ path: "/auth/skipped",
149
+ scope: "global",
150
+ componentKey: "auth-login",
151
+ autoRegister: false
152
+ },
153
+ {
154
+ id: "auth.login",
155
+ path: "/auth/login",
156
+ scope: "global",
157
+ autoRegister: false
158
+ },
159
+ {
160
+ id: "auth.callback",
161
+ path: "/auth/callback",
162
+ scope: "global",
163
+ autoRegister: false
164
+ }
165
+ ],
166
+ module: {
167
+ routeComponents: {
168
+ "auth-login": loginComponent
169
+ },
170
+ clientRoutes: [
171
+ {
172
+ id: "auth.login",
173
+ name: "auth-login",
174
+ path: "/auth/login",
175
+ scope: "global",
176
+ component: loginComponent
177
+ }
178
+ ]
179
+ }
180
+ }
181
+ ],
182
+ router,
183
+ surfaceRuntime,
184
+ surfaceMode: "all",
185
+ logger: { info() {}, warn() {}, error() {} }
186
+ });
187
+
188
+ assert.deepEqual(events, ["register", "boot"]);
189
+ assert.equal(result.providerCount, 1);
190
+ assert.equal(result.routeCount, 2);
191
+ assert.equal(router.routes.length, 2);
192
+ assert.equal(router.routes[0].path, "/auth/default-login-2");
193
+ assert.equal(router.routes[0].component, loginComponent);
194
+ assert.equal(router.routes[1].path, "/auth/login");
195
+ assert.equal(router.routes[1].component, loginComponent);
196
+ assert.equal(result.runtimeApp.make("example.value"), 42);
197
+ });
198
+
199
+ test("bootClientModules does not auto-discover providers from module exports", async () => {
200
+ const router = createRouterStub();
201
+ const surfaceRuntime = createSurfaceRuntimeFixture();
202
+ const events = [];
203
+
204
+ class ExampleClientProvider {
205
+ static id = "example.client";
206
+ register() {
207
+ events.push("register");
208
+ }
209
+ boot() {
210
+ events.push("boot");
211
+ }
212
+ }
213
+
214
+ const result = await bootClientModules({
215
+ clientModules: [
216
+ {
217
+ packageId: "@example/no-provider-declaration",
218
+ module: {
219
+ ExampleClientProvider
220
+ }
221
+ }
222
+ ],
223
+ router,
224
+ surfaceRuntime,
225
+ surfaceMode: "all",
226
+ logger: { info() {}, warn() {}, error() {} }
227
+ });
228
+
229
+ assert.equal(result.providerCount, 0);
230
+ assert.deepEqual(events, []);
231
+ });
232
+
233
+ test("bootClientModules rejects clientRoutes without components", async () => {
234
+ const router = createRouterStub();
235
+ const surfaceRuntime = createSurfaceRuntimeFixture();
236
+
237
+ await assert.rejects(
238
+ bootClientModules({
239
+ clientModules: [
240
+ {
241
+ packageId: "@example/missing",
242
+ module: {
243
+ clientRoutes: [
244
+ {
245
+ id: "auth.login",
246
+ path: "/auth/login",
247
+ scope: "global",
248
+ component: null
249
+ }
250
+ ]
251
+ }
252
+ }
253
+ ],
254
+ router,
255
+ surfaceRuntime,
256
+ surfaceMode: "all",
257
+ logger: { info() {}, warn() {}, error() {} }
258
+ }),
259
+ /Client route "auth.login" from @example\/missing requires component\./
260
+ );
261
+ });
262
+
263
+ test("bootClientModules rejects non-declared global clientRoutes", async () => {
264
+ const router = createRouterStub();
265
+ const surfaceRuntime = createSurfaceRuntimeFixture();
266
+ const loginComponent = {};
267
+
268
+ await assert.rejects(
269
+ bootClientModules({
270
+ clientModules: [
271
+ {
272
+ packageId: "@example/strict",
273
+ descriptorUiRoutes: [
274
+ {
275
+ id: "auth.default-login-2",
276
+ path: "/auth/default-login-2",
277
+ scope: "global",
278
+ componentKey: "auth-login",
279
+ autoRegister: true
280
+ }
281
+ ],
282
+ module: {
283
+ routeComponents: {
284
+ "auth-login": loginComponent
285
+ },
286
+ clientRoutes: [
287
+ {
288
+ id: "auth.callback",
289
+ name: "auth-callback",
290
+ path: "/auth/callback",
291
+ scope: "global",
292
+ component: loginComponent
293
+ }
294
+ ]
295
+ }
296
+ }
297
+ ],
298
+ router,
299
+ surfaceRuntime,
300
+ surfaceMode: "all",
301
+ logger: { info() {}, warn() {}, error() {} }
302
+ }),
303
+ /must be declared in metadata\.ui\.routes/
304
+ );
305
+ });
306
+
307
+ test("bootClientModules allows non-declared surface clientRoutes", async () => {
308
+ const router = createRouterStub();
309
+ const surfaceRuntime = createSurfaceRuntimeFixture();
310
+ const dashboardComponent = {};
311
+
312
+ const result = await bootClientModules({
313
+ clientModules: [
314
+ {
315
+ packageId: "@example/surface-programmatic",
316
+ descriptorUiRoutes: [],
317
+ module: {
318
+ clientRoutes: [
319
+ {
320
+ id: "admin.projects",
321
+ name: "admin-projects",
322
+ path: "/admin/projects",
323
+ scope: "surface",
324
+ component: dashboardComponent
325
+ }
326
+ ]
327
+ }
328
+ }
329
+ ],
330
+ router,
331
+ surfaceRuntime,
332
+ surfaceMode: "admin",
333
+ logger: { info() {}, warn() {}, error() {} }
334
+ });
335
+
336
+ assert.equal(result.routeCount, 1);
337
+ assert.equal(router.routes.length, 1);
338
+ assert.equal(router.routes[0].path, "/admin/projects");
339
+ });
340
+
341
+ test("bootClientModules loads client providers declared in descriptorClientProviders", async () => {
342
+ const router = createRouterStub();
343
+ const surfaceRuntime = createSurfaceRuntimeFixture();
344
+ const events = [];
345
+
346
+ await bootClientModules({
347
+ clientModules: [
348
+ {
349
+ packageId: "@example/descriptor-providers",
350
+ descriptorClientProviders: [
351
+ {
352
+ entrypoint: "src/client/providers/ExampleProvider.js",
353
+ export: "ExampleProvider"
354
+ }
355
+ ],
356
+ module: {
357
+ ExampleProvider: class {
358
+ static id = "example.descriptor.provider";
359
+ register() {
360
+ events.push("register");
361
+ }
362
+ boot() {
363
+ events.push("boot");
364
+ }
365
+ }
366
+ }
367
+ }
368
+ ],
369
+ router,
370
+ surfaceRuntime,
371
+ surfaceMode: "all",
372
+ logger: { info() {}, warn() {}, error() {} }
373
+ });
374
+
375
+ assert.deepEqual(events, ["register", "boot"]);
376
+ });
377
+
378
+ test("bootClientModules throws when descriptorClientProviders export is missing", async () => {
379
+ const router = createRouterStub();
380
+ const surfaceRuntime = createSurfaceRuntimeFixture();
381
+
382
+ await assert.rejects(
383
+ bootClientModules({
384
+ clientModules: [
385
+ {
386
+ packageId: "@example/missing-provider-export",
387
+ descriptorClientProviders: [
388
+ {
389
+ entrypoint: "src/client/providers/MissingProvider.js",
390
+ export: "MissingProvider"
391
+ }
392
+ ],
393
+ module: {}
394
+ }
395
+ ],
396
+ router,
397
+ surfaceRuntime,
398
+ surfaceMode: "all",
399
+ logger: { info() {}, warn() {}, error() {} }
400
+ }),
401
+ /descriptor provider export "MissingProvider" is missing or invalid/
402
+ );
403
+ });
@@ -0,0 +1,233 @@
1
+ import { buildSurfaceAwareRoutes, createFallbackNotFoundRoute, createShellBeforeEachGuard } from "./shellRouting.js";
2
+ import { isRecord } from "../shared/support/normalize.js";
3
+ import { setClientAppConfig } from "./appConfig.js";
4
+ import { createStructuredLogger, summarizeRouterRoutes } from "./logging.js";
5
+
6
+ function resolveClientBootstrapDebugEnabled({
7
+ env = {},
8
+ debugEnabled = undefined,
9
+ debugEnvKey = "VITE_JSKIT_CLIENT_DEBUG"
10
+ } = {}) {
11
+ if (typeof debugEnabled === "boolean") {
12
+ return debugEnabled;
13
+ }
14
+ if (!isRecord(env)) {
15
+ return false;
16
+ }
17
+ return String(env[debugEnvKey] || "").trim() === "1";
18
+ }
19
+
20
+ function createClientBootstrapLogger({
21
+ env = {},
22
+ logger = console,
23
+ debugEnabled = undefined,
24
+ debugEnvKey = "VITE_JSKIT_CLIENT_DEBUG"
25
+ } = {}) {
26
+ const baseLogger = createStructuredLogger(logger);
27
+ const isDebugEnabled = resolveClientBootstrapDebugEnabled({
28
+ env,
29
+ debugEnabled,
30
+ debugEnvKey
31
+ });
32
+ return Object.freeze({
33
+ info: baseLogger.info,
34
+ warn: baseLogger.warn,
35
+ error: baseLogger.error,
36
+ debug: isDebugEnabled ? baseLogger.info : () => {},
37
+ isDebugEnabled
38
+ });
39
+ }
40
+
41
+ function installAppPlugins(app, appPlugins = []) {
42
+ const plugins = Array.isArray(appPlugins) ? appPlugins : [];
43
+ for (const pluginEntry of plugins) {
44
+ if (!pluginEntry) {
45
+ continue;
46
+ }
47
+ if (Array.isArray(pluginEntry)) {
48
+ const [plugin, ...pluginArgs] = pluginEntry;
49
+ if (!plugin) {
50
+ continue;
51
+ }
52
+ app.use(plugin, ...pluginArgs);
53
+ continue;
54
+ }
55
+ app.use(pluginEntry);
56
+ }
57
+ }
58
+
59
+ function createSurfaceShellRouter({
60
+ createRouter,
61
+ history,
62
+ routes = [],
63
+ surfaceRuntime,
64
+ surfaceMode,
65
+ fallbackRoute = null,
66
+ notFoundComponent = null,
67
+ guard = false
68
+ } = {}) {
69
+ if (typeof createRouter !== "function") {
70
+ throw new TypeError("createSurfaceShellRouter requires createRouter().");
71
+ }
72
+
73
+ const effectiveFallbackRoute = fallbackRoute || (notFoundComponent ? createFallbackNotFoundRoute(notFoundComponent) : null);
74
+ const activeRoutes = buildSurfaceAwareRoutes({
75
+ routes,
76
+ surfaceRuntime,
77
+ surfaceMode,
78
+ fallbackRoute: effectiveFallbackRoute
79
+ });
80
+ const router = createRouter({
81
+ history,
82
+ routes: activeRoutes
83
+ });
84
+
85
+ if (guard !== false) {
86
+ if (!router || typeof router.beforeEach !== "function") {
87
+ throw new TypeError("createSurfaceShellRouter requires router.beforeEach() when guard is enabled.");
88
+ }
89
+ const beforeEachGuard =
90
+ typeof guard === "function"
91
+ ? guard
92
+ : createShellBeforeEachGuard({
93
+ surfaceRuntime,
94
+ ...(isRecord(guard) ? guard : {})
95
+ });
96
+ router.beforeEach(beforeEachGuard);
97
+ }
98
+
99
+ return Object.freeze({
100
+ router,
101
+ activeRoutes: Object.freeze([...activeRoutes]),
102
+ fallbackRoute: effectiveFallbackRoute
103
+ });
104
+ }
105
+
106
+ async function bootstrapClientShellApp({
107
+ createApp,
108
+ rootComponent,
109
+ appConfig = {},
110
+ appPlugins = [],
111
+ router,
112
+ bootClientModules,
113
+ surfaceRuntime,
114
+ surfaceMode,
115
+ env = {},
116
+ fallbackRoute = null,
117
+ logger = console,
118
+ createBootstrapLogger = null,
119
+ debugEnabled = undefined,
120
+ debugEnvKey = "VITE_JSKIT_CLIENT_DEBUG",
121
+ debugMessage = "Client modules bootstrapped before router install.",
122
+ onAfterModulesBootstrapped = null,
123
+ mountSelector = "#app"
124
+ } = {}) {
125
+ if (typeof createApp !== "function") {
126
+ throw new TypeError("bootstrapClientShellApp requires createApp().");
127
+ }
128
+ if (!router || typeof router.addRoute !== "function") {
129
+ throw new TypeError("bootstrapClientShellApp requires router.addRoute().");
130
+ }
131
+ if (typeof bootClientModules !== "function") {
132
+ throw new TypeError("bootstrapClientShellApp requires bootClientModules().");
133
+ }
134
+
135
+ const app = createApp(rootComponent);
136
+ if (!app || typeof app.use !== "function" || typeof app.mount !== "function") {
137
+ throw new TypeError("bootstrapClientShellApp requires createApp() to return a Vue app with use() and mount().");
138
+ }
139
+
140
+ installAppPlugins(app, appPlugins);
141
+ setClientAppConfig(appConfig);
142
+
143
+ const resolvedLogger =
144
+ typeof createBootstrapLogger === "function"
145
+ ? createBootstrapLogger({
146
+ env,
147
+ logger,
148
+ debugEnabled,
149
+ debugEnvKey
150
+ })
151
+ : createClientBootstrapLogger({
152
+ env,
153
+ logger,
154
+ debugEnabled,
155
+ debugEnvKey
156
+ });
157
+ const bootstrapLogger = createStructuredLogger(resolvedLogger);
158
+
159
+ const clientBootstrap = await bootClientModules({
160
+ app,
161
+ router,
162
+ surfaceRuntime,
163
+ surfaceMode,
164
+ env,
165
+ logger: bootstrapLogger
166
+ });
167
+
168
+ const isDebugEnabled =
169
+ typeof resolvedLogger?.isDebugEnabled === "boolean"
170
+ ? resolvedLogger.isDebugEnabled
171
+ : resolveClientBootstrapDebugEnabled({
172
+ env,
173
+ debugEnabled,
174
+ debugEnvKey
175
+ });
176
+ if (isDebugEnabled) {
177
+ bootstrapLogger.info(
178
+ {
179
+ bootstrap: {
180
+ modules: clientBootstrap?.modules || [],
181
+ providerCount: Number(clientBootstrap?.providerCount || 0),
182
+ routeCount: Number(clientBootstrap?.routeCount || 0)
183
+ },
184
+ routerRoutesBeforeInstall: summarizeRouterRoutes(router),
185
+ currentPath: typeof window !== "undefined" ? String(window.location?.pathname || "") : ""
186
+ },
187
+ String(debugMessage || "").trim() || "Client modules bootstrapped before router install."
188
+ );
189
+ }
190
+
191
+ if (fallbackRoute?.name && typeof router.addRoute === "function") {
192
+ if (typeof router.hasRoute === "function" && router.hasRoute(fallbackRoute.name) && typeof router.removeRoute === "function") {
193
+ router.removeRoute(fallbackRoute.name);
194
+ }
195
+ router.addRoute(fallbackRoute);
196
+ }
197
+
198
+ if (typeof onAfterModulesBootstrapped === "function") {
199
+ await onAfterModulesBootstrapped(
200
+ Object.freeze({
201
+ app,
202
+ router,
203
+ clientBootstrap,
204
+ surfaceRuntime,
205
+ surfaceMode,
206
+ env: isRecord(env) ? { ...env } : {},
207
+ logger: bootstrapLogger,
208
+ debugEnabled: isDebugEnabled
209
+ })
210
+ );
211
+ }
212
+
213
+ app.use(router);
214
+ if (typeof router.isReady === "function") {
215
+ await router.isReady();
216
+ }
217
+ app.mount(mountSelector);
218
+
219
+ return Object.freeze({
220
+ app,
221
+ router,
222
+ clientBootstrap,
223
+ logger: bootstrapLogger,
224
+ debugEnabled: isDebugEnabled
225
+ });
226
+ }
227
+
228
+ export {
229
+ resolveClientBootstrapDebugEnabled,
230
+ createClientBootstrapLogger,
231
+ createSurfaceShellRouter,
232
+ bootstrapClientShellApp
233
+ };