@jskit-ai/kernel 0.1.245 → 0.1.247
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/appConfig.js +2 -1
- package/client/asyncModuleRecovery.js +2 -4
- package/client/index.d.ts +13 -75
- package/client/index.js +1 -1
- package/client/logging.js +8 -2
- package/client/moduleBootstrap.js +41 -33
- package/client/moduleBootstrap.test.js +35 -0
- package/client/shellBootstrap.js +1 -8
- package/client/shellBootstrap.test.js +32 -5
- package/client/shellRouting.js +16 -61
- package/client/shellRouting.test.js +30 -1
- package/client/vite/clientBootstrapPlugin.js +0 -6
- package/client/vite/clientBootstrapPlugin.test.js +2 -3
- package/internal/node/installedPackages.js +19 -62
- package/internal/node/installedPackages.test.js +33 -0
- package/package.json +2 -4
- package/server/actions/actionCatalogue.js +13 -34
- package/{shared → server}/actions/actionContributorHelpers.js +0 -5
- package/{shared → server}/actions/actionDefinitions.js +24 -131
- package/{shared → server}/actions/actionDefinitions.test.js +75 -21
- package/server/actions/actionEvents.js +97 -2
- package/server/actions/actionEvents.test.js +79 -0
- package/{shared → server}/actions/executionContext.js +3 -13
- package/server/actions/idempotency.js +24 -0
- package/server/actions/pipeline.js +87 -0
- package/{shared → server}/actions/policies.js +20 -33
- package/{shared → server}/actions/policies.test.js +6 -6
- package/{shared → server}/actions/registry.js +3 -22
- package/{shared → server}/actions/registry.test.js +70 -32
- package/server/actions/requestMeta.js +17 -0
- package/server/http/capabilityHttpRuntime.js +1 -1
- package/server/http/capabilityHttpRuntime.test.js +56 -0
- package/server/http/errorHandling.js +291 -0
- package/server/{runtime/fastifyBootstrap.test.js → http/errorHandling.test.js} +72 -120
- package/server/http/lib/routeRegistration.js +36 -150
- package/server/http/lib/routeTransport.js +2 -0
- package/server/http/lib/routeValidator.js +5 -47
- package/server/http/lib/routeValidator.test.js +15 -17
- package/server/http/lib/router.js +10 -94
- package/server/platform/providerRuntime/capabilityProviderLoader.js +2 -1
- package/server/platform/providerRuntime/helpers.js +1 -34
- package/server/runtime/integers.js +1 -5
- package/server/runtime/serviceAuthorization.js +4 -9
- package/server/support/appConfig.js +2 -1
- package/server/support/appConfigFiles.js +1 -38
- package/server/support/appConfigFiles.test.js +3 -39
- package/server/support/index.js +1 -20
- package/shared/capabilities/defineProvider.js +1 -1
- package/shared/capabilities/index.js +1 -1
- package/shared/index.js +1 -1
- package/shared/support/index.js +1 -4
- package/shared/support/mobileConfig.js +99 -0
- package/shared/support/mobileConfig.test.js +113 -0
- package/shared/support/normalize.js +0 -108
- package/shared/support/normalize.test.js +0 -134
- package/shared/{actions/actionContributorHelpers.test.js → support/permissions.test.js} +1 -1
- package/shared/support/returnToPath.js +1 -20
- package/shared/support/returnToPath.test.js +1 -36
- package/shared/support/shellLayoutTargets.js +2 -140
- package/shared/support/shellLayoutTargets.test.js +1 -110
- package/shared/support/sorting.js +1 -9
- package/shared/surface/apiPaths.js +1 -82
- package/shared/surface/paths.js +38 -171
- package/shared/surface/registry.js +2 -1
- package/shared/surface/runtime.js +18 -109
- package/shared/surface/runtime.test.js +57 -0
- package/shared/validators/index.js +1 -4
- package/shared/validators/jsonRestSchemaSupport.js +0 -9
- package/shared/validators/recordIdParamsValidator.js +0 -6
- package/shared/validators/schemaDefinitions.js +4 -30
- package/shared/validators/schemaPayloadValidation.js +2 -6
- package/test/barrelExposure.test.js +3 -7
- package/test/exportsContract.test.js +1 -3
- package/client/mobileLaunchRouting.js +0 -231
- package/client/mobileLaunchRouting.test.js +0 -230
- package/server/http/lib/controller.js +0 -183
- package/server/http/lib/controller.test.js +0 -143
- package/server/http/lib/index.js +0 -5
- package/server/runtime/canonicalJson.js +0 -74
- package/server/runtime/entityChangeEvents.js +0 -307
- package/server/runtime/entityChangeEvents.test.js +0 -274
- package/server/runtime/fastifyBootstrap.js +0 -553
- package/server/runtime/moduleConfig.js +0 -223
- package/server/runtime/moduleConfig.test.js +0 -165
- package/server/runtime/pagination.js +0 -13
- package/server/runtime/realtimeNormalization.js +0 -21
- package/server/runtime/routeUtils.js +0 -20
- package/server/runtime/securityAudit.js +0 -269
- package/server/runtime/securityAudit.test.js +0 -41
- package/server/support/pageTargets.js +0 -772
- package/server/support/pageTargets.test.js +0 -659
- package/server/support/shellOutlets.js +0 -576
- package/server/support/shellOutlets.test.js +0 -336
- package/server/surface/index.js +0 -18
- package/shared/actions/audit.js +0 -7
- package/shared/actions/idempotency.js +0 -62
- package/shared/actions/observability.js +0 -10
- package/shared/actions/pipeline.js +0 -309
- package/shared/actions/requestMeta.js +0 -36
- package/shared/actions/textNormalization.js +0 -3
- package/shared/actions/withActionDefaults.js +0 -34
- /package/{shared → server}/actions/executionContext.test.js +0 -0
|
@@ -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 =
|
|
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(
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
|
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 };
|