@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
@@ -317,3 +317,60 @@ test("filterRoutesBySurface hoists cross-surface nested route wrappers to top-le
317
317
  ["", "members"]
318
318
  );
319
319
  });
320
+
321
+ test("createSurfaceRuntime reads every definition from its one surface registry", () => {
322
+ const runtime = createSurfaceRuntime({
323
+ defaultSurfaceId: "APP",
324
+ surfaces: {
325
+ app: { pagesRoot: "", requiresAuth: false },
326
+ admin: { pagesRoot: "w/[workspaceSlug]/admin", requiresAuth: true },
327
+ console: { pagesRoot: "console", enabled: false }
328
+ }
329
+ });
330
+
331
+ const admin = runtime.getSurfaceDefinition(" Admin ");
332
+ assert.equal(admin.routeBase, "/w/:workspaceSlug/admin");
333
+ assert.equal(admin.enabled, true);
334
+ assert.equal(admin.requiresAuth, true);
335
+ assert.equal(Object.isFrozen(admin), true);
336
+ assert.equal(runtime.getSurfaceDefinition("console").enabled, false);
337
+ assert.equal(runtime.DEFAULT_SURFACE, runtime.getSurfaceDefinition("app"));
338
+ assert.deepEqual(runtime.SURFACE_IDS, ["app", "admin", "console"]);
339
+ assert.deepEqual(runtime.listSurfaceDefinitions().map((entry) => entry.id), ["app", "admin", "console"]);
340
+ assert.throws(
341
+ () => createSurfaceRuntime({ surfaces: { app: {} } }),
342
+ /Surface "app" requires pagesRoot/
343
+ );
344
+ });
345
+
346
+ test("createSurfaceRuntime resolves API pathnames by the surface-relative path", () => {
347
+ const runtime = createSurfaceRuntime({
348
+ defaultSurfaceId: "app",
349
+ surfaces: {
350
+ app: { pagesRoot: "" },
351
+ admin: { pagesRoot: "admin" }
352
+ }
353
+ });
354
+
355
+ assert.equal(runtime.resolveSurfaceFromPathname("/api/admin/users?x=1"), "admin");
356
+ assert.equal(runtime.resolveSurfaceFromPathname("/api/v2/admin/users"), "admin");
357
+ assert.equal(runtime.resolveSurfaceFromPathname("/api"), "app");
358
+ assert.equal(runtime.resolveSurfaceFromPathname("/apiary/admin"), "app");
359
+ });
360
+
361
+ test("filterRoutesBySurface keeps optional route params when hoisting a surface boundary", () => {
362
+ const runtime = createSurfaceRuntime({
363
+ defaultSurfaceId: "app",
364
+ surfaces: {
365
+ app: { pagesRoot: "" },
366
+ admin: { pagesRoot: "admin" }
367
+ }
368
+ });
369
+
370
+ const routes = filterRoutesBySurface(
371
+ [{ path: "/", children: [{ path: "admin/users/:id?", meta: { jskit: { surface: "admin" } } }] }],
372
+ { surfaceRuntime: runtime, surfaceMode: "all" }
373
+ );
374
+
375
+ assert.deepEqual(routes.map((route) => route.path), ["/", "/admin/users/:id?"]);
376
+ });
@@ -8,12 +8,11 @@ export {
8
8
  NULLABLE_HTML_TIME_STRING_SCHEMA
9
9
  } from "./htmlTimeSchemas.js";
10
10
  export { mergeObjectSchemas } from "./mergeObjectSchemas.js";
11
+ export { isJsonRestSchemaInstance } from "./jsonRestSchemaSupport.js";
11
12
  export {
12
13
  hasJsonRestSchemaDefinition,
13
14
  normalizeSingleSchemaDefinition,
14
- normalizeSchemaDefinition,
15
15
  resolveSchemaTransportSchemaDefinition,
16
- resolveStructuredSchemaTransportSchema,
17
16
  executeJsonRestSchemaDefinition
18
17
  } from "./schemaDefinitions.js";
19
18
  export {
@@ -23,9 +22,7 @@ export {
23
22
  export {
24
23
  RECORD_ID_PATTERN,
25
24
  recordIdSchema,
26
- recordIdInputSchema,
27
25
  nullableRecordIdSchema,
28
- nullableRecordIdInputSchema,
29
26
  recordIdParamsValidator
30
27
  } from "./recordIdParamsValidator.js";
31
28
  export {
@@ -64,14 +64,6 @@ function resolveSchemaDefinitionTransportSchema(schemaDefinition = null, options
64
64
  return schema.toJsonSchema({ mode });
65
65
  }
66
66
 
67
- function executeSchemaDefinition(schemaDefinition = null, payload, options = {}) {
68
- const schema = requireJsonRestSchemaInstance(schemaDefinition, {
69
- context: `${options?.context || "schema definition"}.schema`
70
- });
71
- const mode = resolveSchemaDefinitionMode(schemaDefinition, options);
72
- return schema[mode](payload);
73
- }
74
-
75
67
  function resolveJsonRestSchemaFieldMessages(schemaDefinition = null, fieldName = "") {
76
68
  const normalizedFieldName = normalizeText(fieldName);
77
69
  if (!normalizedFieldName) {
@@ -147,6 +139,5 @@ export {
147
139
  isJsonRestSchemaInstance,
148
140
  resolveSchemaDefinitionMode,
149
141
  resolveSchemaDefinitionTransportSchema,
150
- executeSchemaDefinition,
151
142
  normalizeJsonRestSchemaFieldErrors
152
143
  };
@@ -9,15 +9,11 @@ const recordIdSchema = deepFreeze({
9
9
  pattern: RECORD_ID_PATTERN
10
10
  });
11
11
 
12
- const recordIdInputSchema = recordIdSchema;
13
-
14
12
  const nullableRecordIdSchema = deepFreeze({
15
13
  ...recordIdSchema,
16
14
  nullable: true
17
15
  });
18
16
 
19
- const nullableRecordIdInputSchema = nullableRecordIdSchema;
20
-
21
17
  const recordIdParamsValidator = deepFreeze({
22
18
  schema: createSchema({
23
19
  recordId: {
@@ -34,8 +30,6 @@ const recordIdParamsValidator = deepFreeze({
34
30
  export {
35
31
  RECORD_ID_PATTERN,
36
32
  recordIdSchema,
37
- recordIdInputSchema,
38
33
  nullableRecordIdSchema,
39
- nullableRecordIdInputSchema,
40
34
  recordIdParamsValidator
41
35
  };
@@ -1,6 +1,5 @@
1
1
  import { normalizeObject, normalizeText } from "../support/normalize.js";
2
2
  import {
3
- executeSchemaDefinition,
4
3
  isJsonRestSchemaInstance,
5
4
  normalizeJsonRestSchemaFieldErrors,
6
5
  resolveSchemaDefinitionMode,
@@ -49,21 +48,11 @@ function normalizeSingleSchemaDefinition(value, { context = "schema definition",
49
48
  return Object.freeze(normalized);
50
49
  }
51
50
 
52
- function normalizeSchemaDefinition(value, {
53
- context = "schema definition",
54
- defaultMode = ""
55
- } = {}) {
56
- return normalizeSingleSchemaDefinition(value, {
57
- context,
58
- defaultMode
59
- });
60
- }
61
-
62
51
  function resolveSchemaTransportSchemaDefinition(value, {
63
52
  context = "schema definition",
64
53
  defaultMode = ""
65
54
  } = {}) {
66
- const normalized = normalizeSchemaDefinition(value, {
55
+ const normalized = normalizeSingleSchemaDefinition(value, {
67
56
  context,
68
57
  defaultMode
69
58
  });
@@ -78,19 +67,9 @@ function resolveSchemaTransportSchemaDefinition(value, {
78
67
  });
79
68
  }
80
69
 
81
- function resolveStructuredSchemaTransportSchema(value, {
82
- context = "schema definition",
83
- defaultMode = ""
84
- } = {}) {
85
- return resolveSchemaTransportSchemaDefinition(value, {
86
- context,
87
- defaultMode
88
- });
89
- }
90
-
91
70
  function hasJsonRestSchemaDefinition(value) {
92
71
  try {
93
- return Boolean(normalizeSchemaDefinition(value));
72
+ return Boolean(normalizeSingleSchemaDefinition(value));
94
73
  } catch {
95
74
  return false;
96
75
  }
@@ -100,7 +79,7 @@ function executeJsonRestSchemaDefinition(value, payload, {
100
79
  context = "schema definition",
101
80
  defaultMode = ""
102
81
  } = {}) {
103
- const normalized = normalizeSchemaDefinition(value, {
82
+ const normalized = normalizeSingleSchemaDefinition(value, {
104
83
  context,
105
84
  defaultMode
106
85
  });
@@ -109,18 +88,13 @@ function executeJsonRestSchemaDefinition(value, payload, {
109
88
  return null;
110
89
  }
111
90
 
112
- return executeSchemaDefinition(normalized, payload, {
113
- defaultMode: defaultMode || "patch",
114
- context: `${context}.mode`
115
- });
91
+ return normalized.schema[normalized.mode](payload);
116
92
  }
117
93
 
118
94
  export {
119
95
  hasJsonRestSchemaDefinition,
120
96
  normalizeSingleSchemaDefinition,
121
- normalizeSchemaDefinition,
122
97
  resolveSchemaTransportSchemaDefinition,
123
- resolveStructuredSchemaTransportSchema,
124
98
  executeJsonRestSchemaDefinition,
125
99
  normalizeJsonRestSchemaFieldErrors
126
100
  };
@@ -44,11 +44,7 @@ function validateSchemaPayload(schemaDefinition, payload, {
44
44
  context
45
45
  });
46
46
 
47
- if (!result) {
48
- throw new TypeError(`${context}.schema must be a json-rest-schema schema instance.`);
49
- }
50
-
51
- const fieldErrors = normalizeJsonRestSchemaFieldErrors(result?.errors, schemaDefinition);
47
+ const fieldErrors = normalizeJsonRestSchemaFieldErrors(result.errors, schemaDefinition);
52
48
  if (Object.keys(fieldErrors).length > 0) {
53
49
  throw buildSchemaValidationError({
54
50
  fieldErrors,
@@ -56,7 +52,7 @@ function validateSchemaPayload(schemaDefinition, payload, {
56
52
  });
57
53
  }
58
54
 
59
- return result?.validatedObject ?? payload;
55
+ return result.validatedObject ?? payload;
60
56
  }
61
57
 
62
58
  export {
@@ -27,6 +27,7 @@ const BARREL_EXPECTATIONS = Object.freeze([
27
27
  Object.freeze({
28
28
  filePath: path.join(REPO_ROOT, "packages", "kernel", "shared", "capabilities", "index.js"),
29
29
  expectedExports: Object.freeze([
30
+ "ARCHITECTURE_ID_PATTERN",
30
31
  "createCapabilityRuntime",
31
32
  "defineProvider"
32
33
  ])
@@ -61,7 +62,6 @@ const BARREL_EXPECTATIONS = Object.freeze([
61
62
  "normalizePermissionList",
62
63
  "normalizeReturnToPath",
63
64
  "pickOwnProperties",
64
- "resolveAllowedOriginsFromPlacementContext",
65
65
  "splitPathQueryAndHash",
66
66
  "toCamelCase",
67
67
  "toSnakeCase",
@@ -84,18 +84,15 @@ const BARREL_EXPECTATIONS = Object.freeze([
84
84
  "deriveResourceRequiredMetadata",
85
85
  "executeJsonRestSchemaDefinition",
86
86
  "hasJsonRestSchemaDefinition",
87
+ "isJsonRestSchemaInstance",
87
88
  "mergeObjectSchemas",
88
89
  "normalizeObjectInput",
89
90
  "normalizeRequiredFieldList",
90
- "normalizeSchemaDefinition",
91
91
  "normalizeSingleSchemaDefinition",
92
- "nullableRecordIdInputSchema",
93
92
  "nullableRecordIdSchema",
94
- "recordIdInputSchema",
95
93
  "recordIdParamsValidator",
96
94
  "recordIdSchema",
97
95
  "resolveSchemaTransportSchemaDefinition",
98
- "resolveStructuredSchemaTransportSchema",
99
96
  "validateSchemaPayload"
100
97
  ])
101
98
  }),
@@ -106,6 +103,7 @@ const BARREL_EXPECTATIONS = Object.freeze([
106
103
  "createComponentInteractionEmitter",
107
104
  "createAsyncModuleRecoveryState",
108
105
  "createShellRouter",
106
+ "DEFAULT_GUARD_EVALUATOR_KEY",
109
107
  "dismissAsyncModuleRecovery",
110
108
  "dynamicImportErrorMessage",
111
109
  "resolveClientAssetMode",
@@ -113,9 +111,7 @@ const BARREL_EXPECTATIONS = Object.freeze([
113
111
  "getClientAppConfig",
114
112
  "installAsyncModuleRecoveryHandlers",
115
113
  "isDynamicImportError",
116
- "normalizeIncomingAppUrl",
117
114
  "notifyAsyncModuleLoadError",
118
- "registerMobileLaunchRouting",
119
115
  "resolveMobileConfig",
120
116
  "resolveClientBootstrapDebugEnabled",
121
117
  "shellScrollBehavior"
@@ -30,9 +30,7 @@ const EXPORTED_UNUSED_ALLOWLIST = new Set([
30
30
  // defineFeature(), while infrastructure providers use it directly.
31
31
  "./shared/capabilities",
32
32
  // Public HTTP composition provider used when assembling a server runtime.
33
- "./server/http",
34
- // Public helper API for packages that publish entity-change realtime events.
35
- "./server/runtime/entityChangeEvents"
33
+ "./server/http"
36
34
  ]);
37
35
 
38
36
  function normalizeSlash(value) {
@@ -1,231 +0,0 @@
1
- import { normalizePathname } from "../shared/surface/paths.js";
2
- import { normalizeText } from "../shared/support/normalize.js";
3
- import { resolveMobileConfig } from "./appConfig.js";
4
-
5
- function buildNormalizedRoutePath(pathname = "/", search = "", hash = "") {
6
- const normalizedPathname = normalizePathname(pathname);
7
- const normalizedSearch = String(search || "").trim().replace(/^\?+/, "");
8
- const normalizedHash = String(hash || "").trim();
9
-
10
- return `${normalizedPathname}${normalizedSearch ? `?${normalizedSearch}` : ""}${normalizedHash}`;
11
- }
12
-
13
- function normalizeResolvedRoutePath(value = "", fallback = "") {
14
- const normalizedValue = normalizeText(value);
15
- if (!normalizedValue) {
16
- return fallback;
17
- }
18
-
19
- try {
20
- const parsed = new URL(normalizedValue, "https://jskit.invalid");
21
- return buildNormalizedRoutePath(parsed.pathname, parsed.search, parsed.hash);
22
- } catch {
23
- return fallback;
24
- }
25
- }
26
-
27
- function normalizeAllowedHttpOrigins({ mobileConfig = {}, currentOrigin = "", allowedHttpOrigins = [] } = {}) {
28
- const origins = new Set();
29
-
30
- const maybeAddOrigin = (value = "") => {
31
- const normalizedValue = normalizeText(value);
32
- if (!normalizedValue) {
33
- return;
34
- }
35
-
36
- try {
37
- const parsed = new URL(normalizedValue);
38
- const protocol = String(parsed.protocol || "").toLowerCase();
39
- if (protocol !== "http:" && protocol !== "https:") {
40
- return;
41
- }
42
- origins.add(String(parsed.origin || "").trim().toLowerCase());
43
- } catch {}
44
- };
45
-
46
- maybeAddOrigin(currentOrigin);
47
-
48
- const explicitOrigins = Array.isArray(allowedHttpOrigins) ? allowedHttpOrigins : [allowedHttpOrigins];
49
- for (const entry of explicitOrigins) {
50
- maybeAddOrigin(entry);
51
- }
52
-
53
- const appLinkDomains = Array.isArray(mobileConfig?.auth?.appLinkDomains) ? mobileConfig.auth.appLinkDomains : [];
54
- for (const domain of appLinkDomains) {
55
- const normalizedDomain = normalizeText(domain).toLowerCase();
56
- if (!normalizedDomain) {
57
- continue;
58
- }
59
- maybeAddOrigin(`https://${normalizedDomain}`);
60
- }
61
-
62
- return origins;
63
- }
64
-
65
- function normalizeCustomSchemeRoutePath(parsedUrl) {
66
- const host = normalizeText(parsedUrl?.host);
67
- const pathname = normalizePathname(parsedUrl?.pathname || "/");
68
-
69
- if (!host) {
70
- return pathname;
71
- }
72
-
73
- if (pathname === "/") {
74
- return `/${host}`;
75
- }
76
-
77
- return normalizePathname(`/${host}${pathname}`);
78
- }
79
-
80
- function normalizeIncomingAppUrl(url = "", mobileConfig = {}, { currentOrigin = "", allowedHttpOrigins = [] } = {}) {
81
- const normalizedUrl = normalizeText(url);
82
- if (!normalizedUrl) {
83
- return "";
84
- }
85
-
86
- if (normalizedUrl.startsWith("/")) {
87
- try {
88
- const parsed = new URL(normalizedUrl, "https://jskit.invalid");
89
- return buildNormalizedRoutePath(parsed.pathname, parsed.search, parsed.hash);
90
- } catch {
91
- return "";
92
- }
93
- }
94
-
95
- let parsedUrl;
96
- try {
97
- parsedUrl = new URL(normalizedUrl);
98
- } catch {
99
- return "";
100
- }
101
-
102
- const resolvedMobileConfig = resolveMobileConfig({
103
- mobile: mobileConfig
104
- });
105
- const allowedOrigins = normalizeAllowedHttpOrigins({
106
- mobileConfig: resolvedMobileConfig,
107
- currentOrigin,
108
- allowedHttpOrigins
109
- });
110
- const protocol = String(parsedUrl.protocol || "").toLowerCase();
111
- const customScheme = normalizeText(resolvedMobileConfig.auth.customScheme).toLowerCase();
112
-
113
- if (customScheme && protocol === `${customScheme}:`) {
114
- return buildNormalizedRoutePath(
115
- normalizeCustomSchemeRoutePath(parsedUrl),
116
- parsedUrl.search,
117
- parsedUrl.hash
118
- );
119
- }
120
-
121
- if ((protocol === "http:" || protocol === "https:") && allowedOrigins.has(String(parsedUrl.origin || "").toLowerCase())) {
122
- return buildNormalizedRoutePath(parsedUrl.pathname, parsedUrl.search, parsedUrl.hash);
123
- }
124
-
125
- return "";
126
- }
127
-
128
- function registerMobileLaunchRouting({
129
- router,
130
- mobileConfig = {},
131
- getInitialLaunchUrl = async () => "",
132
- subscribeToLaunchUrls = () => () => {},
133
- resolveTargetPath = null,
134
- currentOrigin = typeof window === "object" && window?.location ? String(window.location.origin || "") : "",
135
- allowedHttpOrigins = [],
136
- logger = null
137
- } = {}) {
138
- if (!router || typeof router.replace !== "function") {
139
- throw new TypeError("registerMobileLaunchRouting requires router.replace().");
140
- }
141
-
142
- const resolvedMobileConfig = resolveMobileConfig({
143
- mobile: mobileConfig
144
- });
145
- const runtimeLogger =
146
- logger && typeof logger === "object"
147
- ? logger
148
- : {
149
- info() {},
150
- warn() {},
151
- error() {}
152
- };
153
-
154
- async function applyIncomingUrl(url = "", reason = "manual") {
155
- const normalizedTargetPath = normalizeIncomingAppUrl(url, resolvedMobileConfig, {
156
- currentOrigin,
157
- allowedHttpOrigins
158
- });
159
- if (!normalizedTargetPath) {
160
- return "";
161
- }
162
-
163
- let resolvedTargetPath = normalizedTargetPath;
164
- if (typeof resolveTargetPath === "function") {
165
- const nextTargetPath = await resolveTargetPath(
166
- Object.freeze({
167
- originalUrl: String(url || ""),
168
- normalizedTargetPath,
169
- reason,
170
- mobileConfig: resolvedMobileConfig,
171
- router
172
- })
173
- );
174
- resolvedTargetPath = normalizeResolvedRoutePath(nextTargetPath, normalizedTargetPath);
175
- }
176
-
177
- const currentFullPath = String(router.currentRoute?.value?.fullPath || "").trim();
178
- if (currentFullPath === resolvedTargetPath) {
179
- return resolvedTargetPath;
180
- }
181
-
182
- await router.replace(resolvedTargetPath);
183
- if (typeof runtimeLogger.info === "function") {
184
- runtimeLogger.info(
185
- {
186
- reason,
187
- targetPath: resolvedTargetPath
188
- },
189
- "Mobile launch routing applied incoming app URL."
190
- );
191
- }
192
- return resolvedTargetPath;
193
- }
194
-
195
- async function initialize() {
196
- if (resolvedMobileConfig.enabled !== true) {
197
- return "";
198
- }
199
-
200
- const initialLaunchUrl = await getInitialLaunchUrl();
201
- return applyIncomingUrl(initialLaunchUrl, "initial-launch");
202
- }
203
-
204
- const unsubscribe =
205
- resolvedMobileConfig.enabled === true
206
- ? subscribeToLaunchUrls((nextUrl) => {
207
- Promise.resolve(applyIncomingUrl(nextUrl, "launch-event")).catch((error) => {
208
- if (typeof runtimeLogger.warn === "function") {
209
- runtimeLogger.warn(
210
- {
211
- error: String(error?.message || error || "unknown error")
212
- },
213
- "Mobile launch routing failed to apply incoming app URL."
214
- );
215
- }
216
- });
217
- })
218
- : () => {};
219
-
220
- return Object.freeze({
221
- initialize,
222
- dispose() {
223
- if (typeof unsubscribe === "function") {
224
- unsubscribe();
225
- }
226
- },
227
- applyIncomingUrl
228
- });
229
- }
230
-
231
- export { normalizeIncomingAppUrl, registerMobileLaunchRouting };