@jskit-ai/kernel 0.1.245 → 0.1.246

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 (101) hide show
  1. package/client/appConfig.js +2 -1
  2. package/client/asyncModuleRecovery.js +2 -4
  3. package/client/index.d.ts +13 -75
  4. package/client/index.js +1 -1
  5. package/client/logging.js +8 -2
  6. package/client/moduleBootstrap.js +41 -33
  7. package/client/moduleBootstrap.test.js +35 -0
  8. package/client/shellBootstrap.js +1 -8
  9. package/client/shellBootstrap.test.js +32 -5
  10. package/client/shellRouting.js +16 -61
  11. package/client/shellRouting.test.js +30 -1
  12. package/client/vite/clientBootstrapPlugin.js +0 -6
  13. package/client/vite/clientBootstrapPlugin.test.js +2 -3
  14. package/internal/node/installedPackages.js +14 -58
  15. package/package.json +2 -4
  16. package/server/actions/actionCatalogue.js +13 -34
  17. package/{shared → server}/actions/actionContributorHelpers.js +0 -5
  18. package/{shared → server}/actions/actionDefinitions.js +24 -131
  19. package/{shared → server}/actions/actionDefinitions.test.js +75 -21
  20. package/server/actions/actionEvents.js +97 -2
  21. package/server/actions/actionEvents.test.js +79 -0
  22. package/{shared → server}/actions/executionContext.js +3 -13
  23. package/server/actions/idempotency.js +24 -0
  24. package/server/actions/pipeline.js +87 -0
  25. package/{shared → server}/actions/policies.js +20 -33
  26. package/{shared → server}/actions/policies.test.js +6 -6
  27. package/{shared → server}/actions/registry.js +3 -22
  28. package/{shared → server}/actions/registry.test.js +70 -32
  29. package/server/actions/requestMeta.js +17 -0
  30. package/server/http/capabilityHttpRuntime.js +1 -1
  31. package/server/http/capabilityHttpRuntime.test.js +56 -0
  32. package/server/http/errorHandling.js +291 -0
  33. package/server/{runtime/fastifyBootstrap.test.js → http/errorHandling.test.js} +72 -120
  34. package/server/http/lib/routeRegistration.js +36 -150
  35. package/server/http/lib/routeTransport.js +2 -0
  36. package/server/http/lib/routeValidator.js +5 -47
  37. package/server/http/lib/routeValidator.test.js +15 -17
  38. package/server/http/lib/router.js +10 -94
  39. package/server/platform/providerRuntime/capabilityProviderLoader.js +2 -1
  40. package/server/platform/providerRuntime/helpers.js +1 -34
  41. package/server/runtime/integers.js +1 -5
  42. package/server/runtime/serviceAuthorization.js +4 -9
  43. package/server/support/appConfig.js +2 -1
  44. package/server/support/appConfigFiles.js +1 -38
  45. package/server/support/appConfigFiles.test.js +3 -39
  46. package/server/support/index.js +1 -20
  47. package/shared/capabilities/defineProvider.js +1 -1
  48. package/shared/capabilities/index.js +1 -1
  49. package/shared/index.js +1 -1
  50. package/shared/support/index.js +1 -4
  51. package/shared/support/mobileConfig.js +99 -0
  52. package/shared/support/mobileConfig.test.js +113 -0
  53. package/shared/support/normalize.js +0 -108
  54. package/shared/support/normalize.test.js +0 -134
  55. package/shared/{actions/actionContributorHelpers.test.js → support/permissions.test.js} +1 -1
  56. package/shared/support/returnToPath.js +1 -20
  57. package/shared/support/returnToPath.test.js +1 -36
  58. package/shared/support/shellLayoutTargets.js +2 -140
  59. package/shared/support/shellLayoutTargets.test.js +1 -110
  60. package/shared/support/sorting.js +1 -9
  61. package/shared/surface/apiPaths.js +1 -82
  62. package/shared/surface/paths.js +38 -171
  63. package/shared/surface/registry.js +2 -1
  64. package/shared/surface/runtime.js +18 -109
  65. package/shared/surface/runtime.test.js +57 -0
  66. package/shared/validators/index.js +1 -4
  67. package/shared/validators/jsonRestSchemaSupport.js +0 -9
  68. package/shared/validators/recordIdParamsValidator.js +0 -6
  69. package/shared/validators/schemaDefinitions.js +4 -30
  70. package/shared/validators/schemaPayloadValidation.js +2 -6
  71. package/test/barrelExposure.test.js +3 -7
  72. package/test/exportsContract.test.js +1 -3
  73. package/client/mobileLaunchRouting.js +0 -231
  74. package/client/mobileLaunchRouting.test.js +0 -230
  75. package/server/http/lib/controller.js +0 -183
  76. package/server/http/lib/controller.test.js +0 -143
  77. package/server/http/lib/index.js +0 -5
  78. package/server/runtime/canonicalJson.js +0 -74
  79. package/server/runtime/entityChangeEvents.js +0 -307
  80. package/server/runtime/entityChangeEvents.test.js +0 -274
  81. package/server/runtime/fastifyBootstrap.js +0 -553
  82. package/server/runtime/moduleConfig.js +0 -223
  83. package/server/runtime/moduleConfig.test.js +0 -165
  84. package/server/runtime/pagination.js +0 -13
  85. package/server/runtime/realtimeNormalization.js +0 -21
  86. package/server/runtime/routeUtils.js +0 -20
  87. package/server/runtime/securityAudit.js +0 -269
  88. package/server/runtime/securityAudit.test.js +0 -41
  89. package/server/support/pageTargets.js +0 -772
  90. package/server/support/pageTargets.test.js +0 -659
  91. package/server/support/shellOutlets.js +0 -576
  92. package/server/support/shellOutlets.test.js +0 -336
  93. package/server/surface/index.js +0 -18
  94. package/shared/actions/audit.js +0 -7
  95. package/shared/actions/idempotency.js +0 -62
  96. package/shared/actions/observability.js +0 -10
  97. package/shared/actions/pipeline.js +0 -309
  98. package/shared/actions/requestMeta.js +0 -36
  99. package/shared/actions/textNormalization.js +0 -3
  100. package/shared/actions/withActionDefaults.js +0 -34
  101. /package/{shared → server}/actions/executionContext.test.js +0 -0
@@ -3,7 +3,7 @@ import test from "node:test";
3
3
  import { createSchema } from "json-rest-schema";
4
4
 
5
5
  import { createRouter } from "./router.js";
6
- import { compileRouteValidator, defineRouteValidator, resolveRouteValidatorOptions } from "./routeValidator.js";
6
+ import { compileRouteValidator, resolveRouteValidatorOptions } from "./routeValidator.js";
7
7
 
8
8
  function stripJsonRestTransportExtensions(value) {
9
9
  if (Array.isArray(value)) {
@@ -45,7 +45,7 @@ function createMockJsonRestSchema() {
45
45
  });
46
46
  }
47
47
 
48
- test("defineRouteValidator compiles body/query/params and maps query schema to querystring", () => {
48
+ test("compileRouteValidator compiles body/query/params and maps query schema to querystring", () => {
49
49
  const bodySchema = createSchema({
50
50
  name: {
51
51
  type: "string",
@@ -82,7 +82,7 @@ test("defineRouteValidator compiles body/query/params and maps query schema to q
82
82
  type: "object"
83
83
  };
84
84
 
85
- const validator = defineRouteValidator({
85
+ const compiled = compileRouteValidator({
86
86
  meta: {
87
87
  tags: ["contacts", "intake"],
88
88
  summary: "Create contact intake"
@@ -104,8 +104,6 @@ test("defineRouteValidator compiles body/query/params and maps query schema to q
104
104
  }
105
105
  });
106
106
 
107
- const compiled = validator.toRouteOptions();
108
-
109
107
  assert.deepEqual(compiled.schema, {
110
108
  tags: ["contacts", "intake"],
111
109
  summary: "Create contact intake",
@@ -344,7 +342,7 @@ test("resolveRouteValidatorOptions rejects validator wrapper", () => {
344
342
  method: "POST",
345
343
  path: "/contacts",
346
344
  options: {
347
- validator: defineRouteValidator({}),
345
+ validator: {},
348
346
  middleware: ["api"]
349
347
  }
350
348
  }),
@@ -352,10 +350,10 @@ test("resolveRouteValidatorOptions rejects validator wrapper", () => {
352
350
  );
353
351
  });
354
352
 
355
- test("defineRouteValidator rejects unsupported advanced.jskitInput", () => {
353
+ test("compileRouteValidator rejects unsupported advanced.jskitInput", () => {
356
354
  assert.throws(
357
355
  () =>
358
- defineRouteValidator({
356
+ compileRouteValidator({
359
357
  advanced: {
360
358
  jskitInput: {}
361
359
  }
@@ -364,24 +362,24 @@ test("defineRouteValidator rejects unsupported advanced.jskitInput", () => {
364
362
  );
365
363
  });
366
364
 
367
- test("defineRouteValidator rejects unsupported top-level keys generically", () => {
365
+ test("compileRouteValidator rejects unsupported top-level keys generically", () => {
368
366
  assert.throws(
369
367
  () =>
370
- defineRouteValidator({
368
+ compileRouteValidator({
371
369
  unsupportedContract: {
372
370
  schema: {
373
371
  type: "object"
374
372
  }
375
373
  }
376
374
  }),
377
- /defineRouteValidator\(\)\.unsupportedContract is not supported/
375
+ /route validator\.unsupportedContract is not supported/
378
376
  );
379
377
  });
380
378
 
381
- test("defineRouteValidator validates meta fields", () => {
379
+ test("compileRouteValidator validates meta fields", () => {
382
380
  assert.throws(
383
381
  () =>
384
- defineRouteValidator({
382
+ compileRouteValidator({
385
383
  meta: {
386
384
  tags: ["ok", ""]
387
385
  }
@@ -391,7 +389,7 @@ test("defineRouteValidator validates meta fields", () => {
391
389
 
392
390
  assert.throws(
393
391
  () =>
394
- defineRouteValidator({
392
+ compileRouteValidator({
395
393
  meta: {
396
394
  summary: ""
397
395
  }
@@ -407,7 +405,7 @@ test("HttpRouter.register rejects validator wrapper options", () => {
407
405
  "POST",
408
406
  "/contacts",
409
407
  {
410
- validator: defineRouteValidator({})
408
+ validator: {}
411
409
  },
412
410
  async () => {}
413
411
  ),
@@ -425,7 +423,7 @@ test("HttpRouter.register rejects compiled route option payloads", () => {
425
423
  }
426
424
  });
427
425
 
428
- const validator = defineRouteValidator({
426
+ const compiled = compileRouteValidator({
429
427
  query: {
430
428
  schema: querySchema
431
429
  }
@@ -434,7 +432,7 @@ test("HttpRouter.register rejects compiled route option payloads", () => {
434
432
  assert.throws(
435
433
  () => router.get(
436
434
  "/contacts",
437
- validator.toRouteOptions(),
435
+ compiled,
438
436
  async () => {}
439
437
  ),
440
438
  /uses unsupported validator options: schema, input/
@@ -1,7 +1,7 @@
1
1
  import { ensureNonEmptyText, normalizeObject, normalizeText } from "../../../shared/support/normalize.js";
2
2
  import { RouteDefinitionError } from "./errors.js";
3
3
  import { resolveRouteValidatorOptions } from "./routeValidator.js";
4
- import { normalizeMiddlewareStack as normalizeSharedMiddlewareStack } from "./routeSupport.js";
4
+ import { normalizeMiddlewareStack as normalizeSharedMiddlewareStack, resolveRouteLabel } from "./routeSupport.js";
5
5
  import { normalizeRouteOutputTransform, normalizeRouteTransport } from "./routeTransport.js";
6
6
 
7
7
  function normalizeMethod(method) {
@@ -13,17 +13,7 @@ function normalizePath(pathname) {
13
13
  if (!value.startsWith("/")) {
14
14
  throw new RouteDefinitionError(`Route path must start with '/': ${value || "<empty>"}`);
15
15
  }
16
- return value.replace(/\/+/g, "/");
17
- }
18
-
19
- function joinPath(left, right) {
20
- const leftPath = normalizeText(left);
21
- const rightPath = normalizeText(right);
22
-
23
- const normalizedLeft = leftPath ? `/${leftPath.replace(/^\/+|\/+$/g, "")}` : "";
24
- const normalizedRight = rightPath ? `/${rightPath.replace(/^\/+|\/+$/g, "")}` : "";
25
- const joined = `${normalizedLeft}${normalizedRight}`.replace(/\/+/g, "/");
26
- return joined || "/";
16
+ return value.replace(/\/+/g, "/").replace(/(.)\/$/, "$1");
27
17
  }
28
18
 
29
19
  function normalizeRouterMiddlewareStack(value, { context = "middleware" } = {}) {
@@ -40,9 +30,7 @@ function normalizeRouteInternal(value, { method = "", path = "" } = {}) {
40
30
  return false;
41
31
  }
42
32
  if (typeof value !== "boolean") {
43
- throw new RouteDefinitionError(
44
- `Route ${String(method || "<unknown>")} ${String(path || "<unknown>")} internal must be a boolean.`
45
- );
33
+ throw new RouteDefinitionError(`Route ${resolveRouteLabel({ method, path })} internal must be a boolean.`);
46
34
  }
47
35
  return value;
48
36
  }
@@ -74,12 +62,8 @@ function normalizeRouteInput(method, path, optionsOrHandler, maybeHandler) {
74
62
  }
75
63
 
76
64
  class HttpRouter {
77
- constructor({ routes = null, prefix = "", middleware = [] } = {}) {
78
- this._routes = Array.isArray(routes) ? routes : [];
79
- this._prefix = normalizeText(prefix);
80
- this._middleware = normalizeRouterMiddlewareStack(middleware, {
81
- context: "router middleware"
82
- });
65
+ constructor() {
66
+ this._routes = [];
83
67
  }
84
68
 
85
69
  register(method, path, optionsOrHandler, maybeHandler) {
@@ -111,7 +95,7 @@ class HttpRouter {
111
95
  const route = Object.freeze({
112
96
  id: normalizeText(resolvedOptions.id),
113
97
  method: input.method,
114
- path: joinPath(this._prefix, input.path),
98
+ path: input.path,
115
99
  schema: resolvedOptions.schema,
116
100
  input: routeInput,
117
101
  output: routeOutput,
@@ -131,7 +115,7 @@ class HttpRouter {
131
115
  ownerResolver: resolvedOptions.ownerResolver,
132
116
  csrfProtection: resolvedOptions.csrfProtection,
133
117
  bodyLimit: resolvedOptions.bodyLimit,
134
- middleware: Object.freeze([...this._middleware, ...routeMiddleware]),
118
+ middleware: Object.freeze(routeMiddleware),
135
119
  handler: input.handler
136
120
  });
137
121
 
@@ -159,81 +143,13 @@ class HttpRouter {
159
143
  return this.register("DELETE", path, optionsOrHandler, maybeHandler);
160
144
  }
161
145
 
162
- group({ prefix = "", middleware = [] } = {}, defineRoutes = null) {
163
- if (typeof defineRoutes !== "function") {
164
- throw new RouteDefinitionError("group() requires a callback.");
165
- }
166
-
167
- const nestedRouter = new HttpRouter({
168
- routes: this._routes,
169
- prefix: joinPath(this._prefix, prefix || ""),
170
- middleware: [
171
- ...this._middleware,
172
- ...normalizeRouterMiddlewareStack(middleware, {
173
- context: "group middleware"
174
- })
175
- ]
176
- });
177
-
178
- defineRoutes(nestedRouter);
179
- return this;
180
- }
181
-
182
- resource(name, controller, options = {}) {
183
- this._resource(name, controller, {
184
- ...normalizeObject(options),
185
- apiOnly: false
186
- });
187
- return this;
188
- }
189
-
190
- apiResource(name, controller, options = {}) {
191
- this._resource(name, controller, {
192
- ...normalizeObject(options),
193
- apiOnly: true
194
- });
195
- return this;
196
- }
197
-
198
- _resource(name, controller, options = {}) {
199
- const resourceName = ensureNonEmptyText(name, "resource name");
200
- const idParam = normalizeText(options.idParam, { fallback: "id" });
201
- const basePath = `/${resourceName}`;
202
- const itemPath = `/${resourceName}/:${idParam}`;
203
-
204
- const methods = normalizeObject(controller);
205
- const routeOptions = {
206
- ...normalizeObject(options)
207
- };
208
- delete routeOptions.idParam;
209
- delete routeOptions.apiOnly;
210
-
211
- const requireMethod = (methodName) => {
212
- const handler = methods[methodName];
213
- if (typeof handler !== "function") {
214
- throw new RouteDefinitionError(`resource controller for ${resourceName} is missing method ${methodName}().`);
215
- }
216
- return handler;
217
- };
218
-
219
- this.get(basePath, routeOptions, requireMethod("index"));
220
- if (!options.apiOnly) {
221
- this.get(`${basePath}/create`, routeOptions, requireMethod("create"));
222
- this.get(`${itemPath}/edit`, routeOptions, requireMethod("edit"));
223
- }
224
- this.post(basePath, routeOptions, requireMethod("store"));
225
- this.get(itemPath, routeOptions, requireMethod("show"));
226
- this.put(itemPath, routeOptions, requireMethod("update"));
227
- this.delete(itemPath, routeOptions, requireMethod("destroy"));
228
- }
229
-
230
146
  list() {
231
147
  return Object.freeze([...this._routes]);
232
148
  }
233
149
  }
234
150
 
235
- function createRouter(options = {}) {
236
- return new HttpRouter(options);
151
+ function createRouter() {
152
+ return new HttpRouter();
237
153
  }
238
154
 
239
- export { HttpRouter, createRouter, joinPath };
155
+ export { HttpRouter, createRouter };
@@ -1,7 +1,8 @@
1
1
  import path from "node:path";
2
2
  import { pathToFileURL } from "node:url";
3
3
  import { defineProvider } from "../../../shared/capabilities/defineProvider.js";
4
- import { fileExists, isIdentifier, isInsidePackageRoot } from "./helpers.js";
4
+ import { fileExists } from "../../../internal/node/fileSystem.js";
5
+ import { isIdentifier, isInsidePackageRoot } from "./helpers.js";
5
6
 
6
7
  function declaredProviderEntrypoints(packageEntry) {
7
8
  const runtime = packageEntry?.packageMetadata?.runtime;
@@ -1,45 +1,12 @@
1
1
  import path from "node:path";
2
- import { escapeRegExp } from "../../../shared/surface/escapeRegExp.js";
3
- import { fileExists } from "../../../internal/node/fileSystem.js";
4
2
 
5
3
  function isIdentifier(value) {
6
4
  return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(String(value || ""));
7
5
  }
8
6
 
9
- function createWildcardMatcher(pattern) {
10
- const rawPattern = String(pattern || "").trim();
11
- if (!rawPattern) {
12
- return null;
13
- }
14
-
15
- const escapedPattern = escapeRegExp(rawPattern).replace(/\\\*/g, ".*");
16
- return new RegExp(`^${escapedPattern}$`);
17
- }
18
-
19
- function normalizeRelativePath(fromPath, targetPath) {
20
- const relative = path.relative(path.resolve(fromPath), path.resolve(targetPath));
21
- return relative.split(path.sep).join("/");
22
- }
23
-
24
7
  function isInsidePackageRoot(packageRoot, candidatePath) {
25
8
  const relative = path.relative(path.resolve(packageRoot), path.resolve(candidatePath));
26
9
  return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
27
10
  }
28
11
 
29
- function toAbsoluteSortedUniquePaths(values) {
30
- const source = Array.isArray(values) ? values : [];
31
- return Object.freeze(
32
- [...new Set(source.map((entry) => path.resolve(entry)).filter(Boolean))].sort((left, right) =>
33
- left.localeCompare(right)
34
- )
35
- );
36
- }
37
-
38
- export {
39
- isIdentifier,
40
- createWildcardMatcher,
41
- normalizeRelativePath,
42
- fileExists,
43
- isInsidePackageRoot,
44
- toAbsoluteSortedUniquePaths
45
- };
12
+ export { isIdentifier, isInsidePackageRoot };
@@ -6,8 +6,4 @@ function parsePositiveInteger(value) {
6
6
  });
7
7
  }
8
8
 
9
- function normalizeNullablePositiveInteger(value) {
10
- return parsePositiveInteger(value);
11
- }
12
-
13
- export { parsePositiveInteger, normalizeNullablePositiveInteger };
9
+ export { parsePositiveInteger };
@@ -26,7 +26,7 @@ function resolveRequireMode(value) {
26
26
  return mode;
27
27
  }
28
28
 
29
- function normalizeRequireAuthOptions(options = {}, { context = "requireAuth options" } = {}) {
29
+ function normalizeRequireAuthOptions(options = {}) {
30
30
  const source = normalizeObject(options);
31
31
  const mode = resolveRequireMode(source.require);
32
32
  const permissions = normalizePermissionList(source.permissions);
@@ -35,8 +35,7 @@ function normalizeRequireAuthOptions(options = {}, { context = "requireAuth opti
35
35
  require: mode,
36
36
  permissions: Object.freeze(permissions),
37
37
  message: normalizeText(source.message),
38
- code: normalizeText(source.code),
39
- context
38
+ code: normalizeText(source.code)
40
39
  });
41
40
  }
42
41
 
@@ -71,7 +70,7 @@ function requireAuth(source = {}, options = {}) {
71
70
  return actor;
72
71
  }
73
72
 
74
- const requiredPermissions = normalizePermissionList(settings.permissions);
73
+ const requiredPermissions = settings.permissions;
75
74
  if (requiredPermissions.length < 1) {
76
75
  return actor;
77
76
  }
@@ -106,8 +105,4 @@ function requireAuth(source = {}, options = {}) {
106
105
  return actor;
107
106
  }
108
107
 
109
- export {
110
- resolveServiceContext,
111
- hasPermission,
112
- requireAuth
113
- };
108
+ export { requireAuth };
@@ -1,4 +1,5 @@
1
- import { normalizeMobileConfig, normalizeObject, normalizeText } from "../../shared/support/normalize.js";
1
+ import { normalizeObject, normalizeText } from "../../shared/support/normalize.js";
2
+ import { normalizeMobileConfig } from "../../shared/support/mobileConfig.js";
2
3
  import { normalizeSurfaceId } from "../../shared/surface/registry.js";
3
4
 
4
5
  function resolveAppConfig(scope = null) {
@@ -1,5 +1,4 @@
1
1
  import path from "node:path";
2
- import { fileURLToPath } from "node:url";
3
2
  import { fileExists } from "../../internal/node/fileSystem.js";
4
3
  import { importFreshModuleFromAbsolutePath } from "./importFreshModuleFromAbsolutePath.js";
5
4
 
@@ -10,27 +9,6 @@ function normalizeConfigObject(value) {
10
9
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
11
10
  }
12
11
 
13
- async function resolveAppRootFromModuleUrl(
14
- moduleUrl,
15
- { publicConfigRelativePath = PUBLIC_CONFIG_RELATIVE_PATH } = {}
16
- ) {
17
- const moduleDirectory = path.dirname(fileURLToPath(moduleUrl));
18
- let currentDirectory = path.resolve(moduleDirectory);
19
-
20
- while (true) {
21
- const candidateConfigPath = path.join(currentDirectory, publicConfigRelativePath);
22
- if (await fileExists(candidateConfigPath)) {
23
- return currentDirectory;
24
- }
25
-
26
- const parentDirectory = path.dirname(currentDirectory);
27
- if (parentDirectory === currentDirectory) {
28
- throw new Error(`Unable to locate app root (missing ${publicConfigRelativePath}).`);
29
- }
30
- currentDirectory = parentDirectory;
31
- }
32
- }
33
-
34
12
  async function loadConfigModuleAtPath(absolutePath) {
35
13
  if (!(await fileExists(absolutePath))) {
36
14
  return {};
@@ -62,19 +40,4 @@ async function loadAppConfigFromAppRoot({
62
40
  });
63
41
  }
64
42
 
65
- async function loadAppConfigFromModuleUrl({
66
- moduleUrl = import.meta.url,
67
- publicConfigRelativePath = PUBLIC_CONFIG_RELATIVE_PATH,
68
- serverConfigRelativePath = SERVER_CONFIG_RELATIVE_PATH
69
- } = {}) {
70
- const appRoot = await resolveAppRootFromModuleUrl(moduleUrl, {
71
- publicConfigRelativePath
72
- });
73
- return loadAppConfigFromAppRoot({
74
- appRoot,
75
- publicConfigRelativePath,
76
- serverConfigRelativePath
77
- });
78
- }
79
-
80
- export { loadAppConfigFromAppRoot, loadAppConfigFromModuleUrl };
43
+ export { loadAppConfigFromAppRoot };
@@ -3,32 +3,7 @@ import { mkdtemp, mkdir, writeFile } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import os from "node:os";
5
5
  import test from "node:test";
6
- import { pathToFileURL } from "node:url";
7
- import { loadAppConfigFromAppRoot, loadAppConfigFromModuleUrl } from "./appConfigFiles.js";
8
-
9
- async function createModuleUrlAt(absolutePath) {
10
- await mkdir(path.dirname(absolutePath), { recursive: true });
11
- await writeFile(absolutePath, "export {};\n", "utf8");
12
- return pathToFileURL(absolutePath).href;
13
- }
14
-
15
- test("loadAppConfigFromModuleUrl merges public and server config", async () => {
16
- const tempRoot = await mkdtemp(path.join(os.tmpdir(), "kernel-app-config-"));
17
- const appRoot = path.join(tempRoot, "app");
18
- await mkdir(path.join(appRoot, "config"), { recursive: true });
19
- await writeFile(path.join(appRoot, "config", "public.js"), "export const config = { a: 1, shared: 'public' };", "utf8");
20
- await writeFile(path.join(appRoot, "config", "server.js"), "export const config = { b: 2, shared: 'server' };", "utf8");
21
- const moduleUrl = await createModuleUrlAt(path.join(appRoot, "packages", "main", "src", "server", "MainServiceProvider.js"));
22
-
23
- const loaded = await loadAppConfigFromModuleUrl({ moduleUrl });
24
-
25
- assert.deepEqual(loaded, {
26
- a: 1,
27
- b: 2,
28
- shared: "server"
29
- });
30
- assert.equal(Object.isFrozen(loaded), true);
31
- });
6
+ import { loadAppConfigFromAppRoot } from "./appConfigFiles.js";
32
7
 
33
8
  test("loadAppConfigFromAppRoot merges public and server config", async () => {
34
9
  const tempRoot = await mkdtemp(path.join(os.tmpdir(), "kernel-app-config-"));
@@ -69,26 +44,15 @@ test("loadAppConfigFromAppRoot requires an explicit appRoot", async () => {
69
44
  );
70
45
  });
71
46
 
72
- test("loadAppConfigFromModuleUrl tolerates missing server config", async () => {
47
+ test("loadAppConfigFromAppRoot tolerates missing server config", async () => {
73
48
  const tempRoot = await mkdtemp(path.join(os.tmpdir(), "kernel-app-config-"));
74
49
  const appRoot = path.join(tempRoot, "app");
75
50
  await mkdir(path.join(appRoot, "config"), { recursive: true });
76
51
  await writeFile(path.join(appRoot, "config", "public.js"), "export const config = { a: 1 };", "utf8");
77
- const moduleUrl = await createModuleUrlAt(path.join(appRoot, "packages", "main", "src", "server", "MainServiceProvider.js"));
78
52
 
79
- const loaded = await loadAppConfigFromModuleUrl({ moduleUrl });
53
+ const loaded = await loadAppConfigFromAppRoot({ appRoot });
80
54
 
81
55
  assert.deepEqual(loaded, {
82
56
  a: 1
83
57
  });
84
58
  });
85
-
86
- test("loadAppConfigFromModuleUrl throws when app root cannot be resolved", async () => {
87
- const tempRoot = await mkdtemp(path.join(os.tmpdir(), "kernel-app-config-"));
88
- const moduleUrl = await createModuleUrlAt(path.join(tempRoot, "missing", "packages", "main", "src", "server", "MainServiceProvider.js"));
89
-
90
- await assert.rejects(
91
- loadAppConfigFromModuleUrl({ moduleUrl }),
92
- /Unable to locate app root/
93
- );
94
- });
@@ -1,6 +1,6 @@
1
1
  export { symlinkSafeRequire } from "./symlinkSafeRequire.js";
2
2
  export { resolveAppConfig, resolveMobileConfig, resolveClientAssetMode, resolveMobileCallbackUrls } from "./appConfig.js";
3
- export { loadAppConfigFromAppRoot, loadAppConfigFromModuleUrl } from "./appConfigFiles.js";
3
+ export { loadAppConfigFromAppRoot } from "./appConfigFiles.js";
4
4
  export { importFreshModuleFromAbsolutePath } from "./importFreshModuleFromAbsolutePath.js";
5
5
  export {
6
6
  collectPackageDependencyIds,
@@ -9,23 +9,4 @@ export {
9
9
  discoverInstalledPackages
10
10
  } from "../../internal/node/installedPackages.js";
11
11
  export { resolveRequiredAppRoot, toPosixPath } from "./path.js";
12
- export {
13
- DEFAULT_PAGE_LINK_COMPONENT_TOKEN,
14
- DEFAULT_SUBPAGE_LINK_COMPONENT_TOKEN,
15
- listSurfacePageRoots,
16
- normalizePagesRelativeTargetFile,
17
- normalizePagesRelativeTargetRoot,
18
- resolveBestSurfaceMatchFromPageFile,
19
- resolvePageTargetDetails,
20
- deriveDefaultSubpagesHost,
21
- resolveNearestParentSubpagesHost,
22
- resolvePageLinkTargetDetails
23
- } from "./pageTargets.js";
24
- export {
25
- discoverPlacementTopologyFromApp,
26
- discoverShellOutletSourcePathsFromApp,
27
- discoverShellOutletTargetsFromApp,
28
- resolveSemanticPlacementTargetFromApp,
29
- resolveShellOutletPlacementTargetFromApp
30
- } from "./shellOutlets.js";
31
12
  export { tryAcquireExclusiveFileLock } from "./fileLock.js";
@@ -96,4 +96,4 @@ function defineProvider({
96
96
  });
97
97
  }
98
98
 
99
- export { defineProvider, normalizeArchitectureId };
99
+ export { ARCHITECTURE_ID_PATTERN, defineProvider, normalizeArchitectureId };
@@ -1,2 +1,2 @@
1
- export { defineProvider } from "./defineProvider.js";
1
+ export { ARCHITECTURE_ID_PATTERN, defineProvider } from "./defineProvider.js";
2
2
  export { createCapabilityRuntime } from "./runtime.js";
package/shared/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  export { resolveLinkPath } from "./support/linkPath.js";
2
- export { normalizePathname } from "./surface/apiPaths.js";
2
+ export { normalizePathname } from "./surface/paths.js";
@@ -3,10 +3,7 @@ export { pickOwnProperties } from "./pickOwnProperties.js";
3
3
  export { formatDateTime } from "./formatDateTime.js";
4
4
  export { appendQueryString, splitPathQueryAndHash } from "./queryPath.js";
5
5
  export { normalizePermissionList, hasPermission } from "./permissions.js";
6
- export {
7
- normalizeReturnToPath,
8
- resolveAllowedOriginsFromPlacementContext
9
- } from "./returnToPath.js";
6
+ export { normalizeReturnToPath } from "./returnToPath.js";
10
7
  export { toCamelCase, toSnakeCase } from "./stringCase.js";
11
8
  export {
12
9
  isTransientQueryError,
@@ -0,0 +1,99 @@
1
+ import {
2
+ hasValue,
3
+ normalizeBoolean,
4
+ normalizeLowerText,
5
+ normalizeObject,
6
+ normalizePositiveInteger,
7
+ normalizeText,
8
+ normalizeUniqueTextList
9
+ } from "./normalize.js";
10
+
11
+ const MOBILE_ASSET_MODES = Object.freeze(["bundled", "dev_server"]);
12
+ const MOBILE_STRATEGIES = Object.freeze(["capacitor"]);
13
+ const DEFAULT_MOBILE_AUTH_CONFIG = Object.freeze({
14
+ callbackPath: "/auth/login",
15
+ customScheme: "",
16
+ appLinkDomains: Object.freeze([])
17
+ });
18
+ const DEFAULT_MOBILE_ANDROID_CONFIG = Object.freeze({
19
+ packageName: "",
20
+ minSdk: 26,
21
+ targetSdk: 35,
22
+ versionCode: 1,
23
+ versionName: "1.0.0"
24
+ });
25
+
26
+ function normalizeMobileStrategy(value = "", { fallback = "" } = {}) {
27
+ const normalized = normalizeLowerText(value);
28
+ return MOBILE_STRATEGIES.includes(normalized) ? normalized : normalizeLowerText(fallback);
29
+ }
30
+
31
+ function normalizeMobileAssetMode(value = "", { fallback = "bundled" } = {}) {
32
+ const normalized = normalizeLowerText(value);
33
+ if (!normalized) {
34
+ return normalizeLowerText(fallback, {
35
+ fallback: "bundled"
36
+ });
37
+ }
38
+ if (!MOBILE_ASSET_MODES.includes(normalized)) {
39
+ throw new TypeError('config.mobile.assetMode must be "bundled" or "dev_server".');
40
+ }
41
+ return normalized;
42
+ }
43
+
44
+ function normalizeMobileCallbackPath(value = "", { fallback = "/auth/login" } = {}) {
45
+ const normalized = normalizeText(value, {
46
+ fallback
47
+ });
48
+ if (!normalized) {
49
+ return "";
50
+ }
51
+
52
+ return normalized.startsWith("/") ? normalized : `/${normalized}`;
53
+ }
54
+
55
+ function normalizeMobileConfig(source = {}) {
56
+ const mobile = normalizeObject(source);
57
+ const auth = normalizeObject(mobile.auth);
58
+ const android = normalizeObject(mobile.android);
59
+
60
+ return Object.freeze({
61
+ enabled: hasValue(mobile.enabled) ? normalizeBoolean(mobile.enabled) : false,
62
+ strategy: normalizeMobileStrategy(mobile.strategy),
63
+ appId: normalizeText(mobile.appId),
64
+ appName: normalizeText(mobile.appName),
65
+ assetMode: normalizeMobileAssetMode(mobile.assetMode),
66
+ devServerUrl: normalizeText(mobile.devServerUrl),
67
+ apiBaseUrl: normalizeText(mobile.apiBaseUrl),
68
+ auth: Object.freeze({
69
+ callbackPath: normalizeMobileCallbackPath(auth.callbackPath, {
70
+ fallback: DEFAULT_MOBILE_AUTH_CONFIG.callbackPath
71
+ }),
72
+ customScheme: normalizeLowerText(auth.customScheme),
73
+ appLinkDomains: Object.freeze([
74
+ ...new Set(
75
+ normalizeUniqueTextList(auth.appLinkDomains, {
76
+ acceptSingle: true
77
+ }).map((entry) => normalizeLowerText(entry))
78
+ )
79
+ ])
80
+ }),
81
+ android: Object.freeze({
82
+ packageName: normalizeText(android.packageName),
83
+ minSdk: normalizePositiveInteger(android.minSdk, {
84
+ fallback: DEFAULT_MOBILE_ANDROID_CONFIG.minSdk
85
+ }),
86
+ targetSdk: normalizePositiveInteger(android.targetSdk, {
87
+ fallback: DEFAULT_MOBILE_ANDROID_CONFIG.targetSdk
88
+ }),
89
+ versionCode: normalizePositiveInteger(android.versionCode, {
90
+ fallback: DEFAULT_MOBILE_ANDROID_CONFIG.versionCode
91
+ }),
92
+ versionName: normalizeText(android.versionName, {
93
+ fallback: DEFAULT_MOBILE_ANDROID_CONFIG.versionName
94
+ })
95
+ })
96
+ });
97
+ }
98
+
99
+ export { normalizeMobileCallbackPath, normalizeMobileConfig };