@jskit-ai/kernel 0.1.8 → 0.1.10
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/package.json +5 -1
- package/server/runtime/moduleConfig.js +1 -17
- package/shared/actions/executionContext.js +3 -14
- package/shared/support/deepFreeze.js +18 -0
- package/shared/support/listenerSet.js +26 -0
- package/shared/support/normalize.js +6 -0
- package/shared/support/normalize.test.js +13 -1
- package/shared/support/providerLogger.js +42 -0
- package/shared/support/visibility.js +2 -2
- package/shared/surface/registry.js +3 -1
- package/shared/validators/index.js +1 -0
- package/shared/validators/settingsFieldNormalization.js +40 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/kernel",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"typebox": "^1.0.81"
|
|
@@ -32,6 +32,10 @@
|
|
|
32
32
|
"./shared/support": "./shared/support/index.js",
|
|
33
33
|
"./shared/support/tokens": "./shared/support/tokens.js",
|
|
34
34
|
"./shared/support/normalize": "./shared/support/normalize.js",
|
|
35
|
+
"./shared/support/permissions": "./shared/support/permissions.js",
|
|
36
|
+
"./shared/support/deepFreeze": "./shared/support/deepFreeze.js",
|
|
37
|
+
"./shared/support/listenerSet": "./shared/support/listenerSet.js",
|
|
38
|
+
"./shared/support/providerLogger": "./shared/support/providerLogger.js",
|
|
35
39
|
"./shared/support/returnToPath": "./shared/support/returnToPath.js",
|
|
36
40
|
"./shared/support/visibility": "./shared/support/visibility.js",
|
|
37
41
|
"./shared/support/linkPath": "./shared/support/linkPath.js",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Check, Errors, Parse } from "typebox/value";
|
|
2
|
+
import { deepFreeze } from "../../shared/support/deepFreeze.js";
|
|
2
3
|
import { normalizeObject, normalizeText } from "../../shared/support/normalize.js";
|
|
3
4
|
|
|
4
5
|
function normalizeModuleId(value) {
|
|
@@ -71,23 +72,6 @@ function buildInvalidConfigMessage(moduleId, issues = []) {
|
|
|
71
72
|
return `Invalid config for module "${moduleId}": ${summary}`;
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
function deepFreeze(value, seen = new WeakSet()) {
|
|
75
|
-
if (!value || typeof value !== "object") {
|
|
76
|
-
return value;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (seen.has(value)) {
|
|
80
|
-
return value;
|
|
81
|
-
}
|
|
82
|
-
seen.add(value);
|
|
83
|
-
|
|
84
|
-
for (const key of Object.getOwnPropertyNames(value)) {
|
|
85
|
-
deepFreeze(value[key], seen);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return Object.freeze(value);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
75
|
function normalizeCustomValidationIssues(result) {
|
|
92
76
|
if (result == null || result === true) {
|
|
93
77
|
return [];
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { normalizeRequestMeta } from "./requestMeta.js";
|
|
2
2
|
import { normalizeLowerText, normalizeText } from "./textNormalization.js";
|
|
3
|
-
|
|
4
|
-
function normalizePermissions(value) {
|
|
5
|
-
const source = Array.isArray(value) ? value : [];
|
|
6
|
-
return Array.from(new Set(source.map((entry) => normalizeText(entry)).filter(Boolean)));
|
|
7
|
-
}
|
|
3
|
+
import { normalizePermissionList } from "../support/permissions.js";
|
|
8
4
|
|
|
9
5
|
function copyRecord(value) {
|
|
10
6
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -31,10 +27,6 @@ function normalizeActor(actor) {
|
|
|
31
27
|
return source;
|
|
32
28
|
}
|
|
33
29
|
|
|
34
|
-
function normalizeMembership(membership) {
|
|
35
|
-
return copyRecord(membership);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
30
|
function normalizeTimeMeta(timeMeta) {
|
|
39
31
|
const source = timeMeta && typeof timeMeta === "object" ? timeMeta : {};
|
|
40
32
|
const nowValue = source.now instanceof Date ? source.now : new Date();
|
|
@@ -48,7 +40,6 @@ function normalizeTimeMeta(timeMeta) {
|
|
|
48
40
|
|
|
49
41
|
const RESERVED_CONTEXT_KEYS = new Set([
|
|
50
42
|
"actor",
|
|
51
|
-
"membership",
|
|
52
43
|
"permissions",
|
|
53
44
|
"surface",
|
|
54
45
|
"channel",
|
|
@@ -74,8 +65,7 @@ function normalizeExecutionContext(context = {}) {
|
|
|
74
65
|
return Object.freeze({
|
|
75
66
|
...passthrough,
|
|
76
67
|
actor: normalizeActor(source.actor),
|
|
77
|
-
|
|
78
|
-
permissions: normalizePermissions(source.permissions),
|
|
68
|
+
permissions: normalizePermissionList(source.permissions),
|
|
79
69
|
surface: normalizeLowerText(source.surface),
|
|
80
70
|
channel: normalizeLowerText(source.channel) || "internal",
|
|
81
71
|
requestMeta: normalizeRequestMeta(source.requestMeta),
|
|
@@ -86,10 +76,9 @@ function normalizeExecutionContext(context = {}) {
|
|
|
86
76
|
const __testables = {
|
|
87
77
|
normalizeText,
|
|
88
78
|
normalizeLowerText,
|
|
89
|
-
normalizePermissions,
|
|
79
|
+
normalizePermissions: normalizePermissionList,
|
|
90
80
|
copyRecord,
|
|
91
81
|
normalizeActor,
|
|
92
|
-
normalizeMembership,
|
|
93
82
|
normalizeRequestMeta,
|
|
94
83
|
normalizeTimeMeta
|
|
95
84
|
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
function deepFreeze(value, seen = new WeakSet()) {
|
|
2
|
+
if (!value || typeof value !== "object") {
|
|
3
|
+
return value;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
if (seen.has(value)) {
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
seen.add(value);
|
|
10
|
+
|
|
11
|
+
for (const key of Object.getOwnPropertyNames(value)) {
|
|
12
|
+
deepFreeze(value[key], seen);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return Object.freeze(value);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export { deepFreeze };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
function subscribeListener(listeners, listener) {
|
|
2
|
+
if (!(listeners instanceof Set)) {
|
|
3
|
+
throw new TypeError("subscribeListener requires a Set.");
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
if (typeof listener !== "function") {
|
|
7
|
+
return () => {};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
listeners.add(listener);
|
|
11
|
+
return () => {
|
|
12
|
+
listeners.delete(listener);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function createListenerSubscription(listeners) {
|
|
17
|
+
if (!(listeners instanceof Set)) {
|
|
18
|
+
throw new TypeError("createListenerSubscription requires a Set.");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return function subscribe(listener) {
|
|
22
|
+
return subscribeListener(listeners, listener);
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { subscribeListener, createListenerSubscription };
|
|
@@ -29,6 +29,11 @@ function normalizeArray(value) {
|
|
|
29
29
|
return Array.isArray(value) ? value : [];
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
function normalizeUniqueTextList(value, { acceptSingle = false } = {}) {
|
|
33
|
+
const source = Array.isArray(value) ? value : acceptSingle ? [value] : [];
|
|
34
|
+
return Array.from(new Set(source.map((entry) => normalizeText(entry)).filter(Boolean)));
|
|
35
|
+
}
|
|
36
|
+
|
|
32
37
|
function normalizeInteger(value, { fallback = 0, min = null, max = null } = {}) {
|
|
33
38
|
const numeric = Number(value);
|
|
34
39
|
if (!Number.isFinite(numeric)) {
|
|
@@ -108,6 +113,7 @@ export {
|
|
|
108
113
|
normalizeObject,
|
|
109
114
|
isRecord,
|
|
110
115
|
normalizeArray,
|
|
116
|
+
normalizeUniqueTextList,
|
|
111
117
|
normalizeInteger,
|
|
112
118
|
normalizePositiveInteger,
|
|
113
119
|
normalizeOpaqueId,
|
|
@@ -4,7 +4,8 @@ import {
|
|
|
4
4
|
normalizeOpaqueId,
|
|
5
5
|
normalizePositiveInteger,
|
|
6
6
|
normalizeOneOf,
|
|
7
|
-
normalizeQueryToken
|
|
7
|
+
normalizeQueryToken,
|
|
8
|
+
normalizeUniqueTextList
|
|
8
9
|
} from "./normalize.js";
|
|
9
10
|
|
|
10
11
|
test("normalizeQueryToken trims, lowercases, and falls back when empty", () => {
|
|
@@ -46,3 +47,14 @@ test("normalizeOpaqueId preserves opaque identifiers", () => {
|
|
|
46
47
|
assert.equal(normalizeOpaqueId(""), null);
|
|
47
48
|
assert.equal(normalizeOpaqueId(null), null);
|
|
48
49
|
});
|
|
50
|
+
|
|
51
|
+
test("normalizeUniqueTextList trims, dedupes, and supports optional single values", () => {
|
|
52
|
+
assert.deepEqual(normalizeUniqueTextList([" one ", "two", "one", "", null]), ["one", "two"]);
|
|
53
|
+
assert.deepEqual(normalizeUniqueTextList("one"), []);
|
|
54
|
+
assert.deepEqual(
|
|
55
|
+
normalizeUniqueTextList(" one ", {
|
|
56
|
+
acceptSingle: true
|
|
57
|
+
}),
|
|
58
|
+
["one"]
|
|
59
|
+
);
|
|
60
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
function asLogger(value) {
|
|
2
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
3
|
+
return null;
|
|
4
|
+
}
|
|
5
|
+
return value;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function callLoggerMethod(logger, methodName, fallbackMethodName, args) {
|
|
9
|
+
if (logger && typeof logger[methodName] === "function") {
|
|
10
|
+
logger[methodName](...args);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (typeof console[fallbackMethodName] === "function") {
|
|
15
|
+
console[fallbackMethodName](...args);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function createProviderLogger(loggerLike = null, { debugEnabled = false, debugMethod = "info" } = {}) {
|
|
20
|
+
const logger = asLogger(loggerLike);
|
|
21
|
+
const resolvedDebugMethod = String(debugMethod || "info").trim() || "info";
|
|
22
|
+
|
|
23
|
+
return Object.freeze({
|
|
24
|
+
debug: (...args) => {
|
|
25
|
+
if (debugEnabled !== true) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
callLoggerMethod(logger, resolvedDebugMethod, "info", args);
|
|
29
|
+
},
|
|
30
|
+
info: (...args) => {
|
|
31
|
+
callLoggerMethod(logger, "info", "info", args);
|
|
32
|
+
},
|
|
33
|
+
warn: (...args) => {
|
|
34
|
+
callLoggerMethod(logger, "warn", "warn", args);
|
|
35
|
+
},
|
|
36
|
+
error: (...args) => {
|
|
37
|
+
callLoggerMethod(logger, "error", "error", args);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export { createProviderLogger };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { normalizeOpaqueId, normalizeText } from "./normalize.js";
|
|
1
|
+
import { normalizeObject, normalizeOpaqueId, normalizeText } from "./normalize.js";
|
|
2
2
|
import { ROUTE_VISIBILITY_PUBLIC, ROUTE_VISIBILITY_USER } from "./policies.js";
|
|
3
3
|
|
|
4
4
|
const ROUTE_VISIBILITY_LEVELS = Object.freeze([ROUTE_VISIBILITY_PUBLIC, ROUTE_VISIBILITY_USER]);
|
|
@@ -40,7 +40,7 @@ function normalizeVisibilityScopeKind(value) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
function normalizeVisibilityContext(value = {}) {
|
|
43
|
-
const source =
|
|
43
|
+
const source = normalizeObject(value);
|
|
44
44
|
const normalizedVisibility = normalizeRouteVisibilityToken(source.visibility);
|
|
45
45
|
const normalizedScopeKind = normalizeVisibilityScopeKind(source.scopeKind);
|
|
46
46
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { normalizeObject } from "../support/normalize.js";
|
|
2
|
+
|
|
1
3
|
function normalizeSurfaceId(value) {
|
|
2
4
|
return String(value || "")
|
|
3
5
|
.trim()
|
|
@@ -59,7 +61,7 @@ function createSurfaceRegistry(options = {}) {
|
|
|
59
61
|
|
|
60
62
|
const normalizedEntries = Object.entries(rawSurfaces)
|
|
61
63
|
.map(([key, value]) => {
|
|
62
|
-
const sourceDefinition =
|
|
64
|
+
const sourceDefinition = normalizeObject(value);
|
|
63
65
|
const normalizedId = normalizeSurfaceId(sourceDefinition.id || key);
|
|
64
66
|
if (!normalizedId) {
|
|
65
67
|
return null;
|
|
@@ -5,6 +5,7 @@ export { mergeObjectSchemas } from "./mergeObjectSchemas.js";
|
|
|
5
5
|
export { mergeValidators } from "./mergeValidators.js";
|
|
6
6
|
export { nestValidator } from "./nestValidator.js";
|
|
7
7
|
export { recordIdParamsValidator, positiveIntegerValidator } from "./recordIdParamsValidator.js";
|
|
8
|
+
export { normalizeSettingsFieldInput, normalizeSettingsFieldOutput } from "./settingsFieldNormalization.js";
|
|
8
9
|
export {
|
|
9
10
|
normalizeRequiredFieldList,
|
|
10
11
|
deriveRequiredFieldsFromSchema,
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { normalizeObjectInput } from "./inputNormalization.js";
|
|
2
|
+
|
|
3
|
+
function normalizeSettingsFieldInput(payload = {}, fields = []) {
|
|
4
|
+
const source = normalizeObjectInput(payload);
|
|
5
|
+
const normalized = {};
|
|
6
|
+
|
|
7
|
+
for (const field of Array.isArray(fields) ? fields : []) {
|
|
8
|
+
if (!Object.hasOwn(source, field.key)) {
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
normalized[field.key] = field.normalizeInput(source[field.key], {
|
|
12
|
+
payload: source
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return normalized;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function normalizeSettingsFieldOutput(payload = {}, fields = []) {
|
|
20
|
+
const settingsSource = normalizeObjectInput(payload);
|
|
21
|
+
const normalized = {};
|
|
22
|
+
|
|
23
|
+
for (const field of Array.isArray(fields) ? fields : []) {
|
|
24
|
+
const rawValue = Object.hasOwn(settingsSource, field.key)
|
|
25
|
+
? settingsSource[field.key]
|
|
26
|
+
: field.resolveDefault({
|
|
27
|
+
settings: settingsSource
|
|
28
|
+
});
|
|
29
|
+
normalized[field.key] = field.normalizeOutput(rawValue, {
|
|
30
|
+
settings: settingsSource
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return normalized;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export {
|
|
38
|
+
normalizeSettingsFieldInput,
|
|
39
|
+
normalizeSettingsFieldOutput
|
|
40
|
+
};
|