@jskit-ai/console-core 0.1.17 → 0.1.19
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.descriptor.mjs +10 -25
- package/package.json +8 -9
- package/src/server/consoleAuthServiceDecorator.js +80 -0
- package/src/server/consoleBootstrapContributor.js +3 -7
- package/src/server/consoleSettings/bootConsoleSettingsRoutes.js +6 -8
- package/src/server/consoleSettings/consoleService.js +15 -0
- package/src/server/consoleSettings/consoleSettingsActions.js +6 -8
- package/src/server/consoleSettings/consoleSettingsRepository.js +1 -29
- package/src/server/registerConsoleCore.js +8 -0
- package/src/shared/resources/consoleSettingsResource.js +15 -110
- package/test/bootstrapPayloadIntegration.test.js +2 -5
- package/test/consoleAuthServiceDecorator.test.js +65 -0
- package/test/consoleBootstrapContributor.test.js +4 -7
- package/test/consoleRouteRequestInputValidator.test.js +1 -3
- package/test/consoleRouteResources.test.js +32 -14
- package/test/consoleService.test.js +12 -0
- package/test/registerConsoleCore.test.js +17 -0
- package/src/shared/resources/consoleSettingsFields.js +0 -56
- package/src/shared/resources/resolveGlobalArrayRegistry.js +0 -6
- package/templates/packages/main/src/shared/resources/consoleSettingsFields.js +0 -11
- package/test/settingsFieldRegistriesSingleton.test.js +0 -14
- package/test-support/registerDefaultSettingsFields.js +0 -1
package/package.descriptor.mjs
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
export default Object.freeze({
|
|
2
2
|
packageVersion: 1,
|
|
3
3
|
packageId: "@jskit-ai/console-core",
|
|
4
|
-
version: "0.1.
|
|
4
|
+
version: "0.1.19",
|
|
5
5
|
kind: "runtime",
|
|
6
6
|
description: "Console runtime: console settings schema, bootstrap flags, actions, and HTTP routes.",
|
|
7
7
|
dependsOn: [
|
|
8
|
+
"@jskit-ai/auth-core",
|
|
8
9
|
"@jskit-ai/database-runtime",
|
|
9
10
|
"@jskit-ai/http-runtime",
|
|
11
|
+
"@jskit-ai/resource-crud-core",
|
|
10
12
|
"@jskit-ai/users-core"
|
|
11
13
|
],
|
|
12
14
|
capabilities: {
|
|
@@ -42,7 +44,7 @@ export default Object.freeze({
|
|
|
42
44
|
},
|
|
43
45
|
{
|
|
44
46
|
subpath: "./shared",
|
|
45
|
-
summary: "Exports shared console settings resource
|
|
47
|
+
summary: "Exports the shared console settings resource contract."
|
|
46
48
|
},
|
|
47
49
|
{
|
|
48
50
|
subpath: "./client",
|
|
@@ -72,11 +74,12 @@ export default Object.freeze({
|
|
|
72
74
|
mutations: {
|
|
73
75
|
dependencies: {
|
|
74
76
|
runtime: {
|
|
75
|
-
"@jskit-ai/
|
|
76
|
-
"@jskit-ai/
|
|
77
|
-
"@jskit-ai/
|
|
78
|
-
"@jskit-ai/
|
|
79
|
-
"
|
|
77
|
+
"@jskit-ai/auth-core": "0.1.55",
|
|
78
|
+
"@jskit-ai/database-runtime": "0.1.56",
|
|
79
|
+
"@jskit-ai/http-runtime": "0.1.55",
|
|
80
|
+
"@jskit-ai/kernel": "0.1.56",
|
|
81
|
+
"@jskit-ai/resource-crud-core": "0.1.1",
|
|
82
|
+
"@jskit-ai/users-core": "0.1.66"
|
|
80
83
|
},
|
|
81
84
|
dev: {}
|
|
82
85
|
},
|
|
@@ -93,27 +96,9 @@ export default Object.freeze({
|
|
|
93
96
|
reason: "Install console settings schema migration.",
|
|
94
97
|
category: "migration",
|
|
95
98
|
id: "console-core-generic-initial-schema"
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
from: "templates/packages/main/src/shared/resources/consoleSettingsFields.js",
|
|
99
|
-
to: "packages/main/src/shared/resources/consoleSettingsFields.js",
|
|
100
|
-
preserveOnRemove: true,
|
|
101
|
-
reason: "Install app-owned console settings field definitions.",
|
|
102
|
-
category: "console-core",
|
|
103
|
-
id: "console-core-app-owned-console-settings-fields"
|
|
104
99
|
}
|
|
105
100
|
],
|
|
106
101
|
text: [
|
|
107
|
-
{
|
|
108
|
-
op: "append-text",
|
|
109
|
-
file: "packages/main/src/shared/index.js",
|
|
110
|
-
position: "top",
|
|
111
|
-
skipIfContains: "import \"./resources/consoleSettingsFields.js\";",
|
|
112
|
-
value: "import \"./resources/consoleSettingsFields.js\";\n",
|
|
113
|
-
reason: "Load app-owned console settings field definitions inside the main shared module.",
|
|
114
|
-
category: "console-core",
|
|
115
|
-
id: "console-core-main-shared-console-settings-field-import"
|
|
116
|
-
}
|
|
117
102
|
]
|
|
118
103
|
}
|
|
119
104
|
});
|
package/package.json
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/console-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.19",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
7
7
|
},
|
|
8
|
-
"exports": {
|
|
9
|
-
"./shared/resources/consoleSettingsFields": "./src/shared/resources/consoleSettingsFields.js"
|
|
10
|
-
},
|
|
11
8
|
"dependencies": {
|
|
12
|
-
"@jskit-ai/
|
|
13
|
-
"@jskit-ai/
|
|
14
|
-
"@jskit-ai/
|
|
15
|
-
"@jskit-ai/
|
|
16
|
-
"
|
|
9
|
+
"@jskit-ai/auth-core": "0.1.55",
|
|
10
|
+
"@jskit-ai/database-runtime": "0.1.56",
|
|
11
|
+
"@jskit-ai/http-runtime": "0.1.55",
|
|
12
|
+
"@jskit-ai/kernel": "0.1.56",
|
|
13
|
+
"@jskit-ai/resource-crud-core": "0.1.1",
|
|
14
|
+
"@jskit-ai/users-core": "0.1.66",
|
|
15
|
+
"json-rest-schema": "1.x.x"
|
|
17
16
|
}
|
|
18
17
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { AUTH_PATHS } from "@jskit-ai/auth-core/shared/authPaths";
|
|
2
|
+
import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
3
|
+
|
|
4
|
+
function normalizeRequestPathname(request = null) {
|
|
5
|
+
const candidates = [
|
|
6
|
+
request?.routeOptions?.url,
|
|
7
|
+
request?.routerPath,
|
|
8
|
+
request?.url,
|
|
9
|
+
request?.raw?.url
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
for (const candidate of candidates) {
|
|
13
|
+
const raw = String(candidate || "").trim();
|
|
14
|
+
if (!raw) {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
const withoutQuery = raw.split("?")[0];
|
|
18
|
+
const pathname = withoutQuery.split("#")[0];
|
|
19
|
+
if (pathname) {
|
|
20
|
+
return pathname;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return "";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function createConsoleAuthServiceDecorator({ consoleService } = {}) {
|
|
28
|
+
if (!consoleService || typeof consoleService.ensureInitialConsoleMember !== "function") {
|
|
29
|
+
throw new Error("createConsoleAuthServiceDecorator requires consoleService.ensureInitialConsoleMember().");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let consoleOwnerInitialized = false;
|
|
33
|
+
|
|
34
|
+
return Object.freeze({
|
|
35
|
+
decoratorId: "console.core.authServiceDecorator",
|
|
36
|
+
order: 0,
|
|
37
|
+
decorateAuthService(authService) {
|
|
38
|
+
if (!authService || typeof authService.authenticateRequest !== "function") {
|
|
39
|
+
return authService;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return Object.freeze(
|
|
43
|
+
Object.assign(Object.create(authService), {
|
|
44
|
+
async authenticateRequest(request, ...args) {
|
|
45
|
+
const authResult = await authService.authenticateRequest(request, ...args);
|
|
46
|
+
if (consoleOwnerInitialized) {
|
|
47
|
+
return authResult;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const requestPathname = normalizeRequestPathname(request);
|
|
51
|
+
if (requestPathname !== AUTH_PATHS.SESSION) {
|
|
52
|
+
return authResult;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const authenticatedUserId =
|
|
56
|
+
authResult?.authenticated === true
|
|
57
|
+
? normalizeRecordId(authResult?.profile?.id, { fallback: null })
|
|
58
|
+
: null;
|
|
59
|
+
|
|
60
|
+
if (!authenticatedUserId) {
|
|
61
|
+
return authResult;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const ownerUserId = normalizeRecordId(
|
|
65
|
+
await consoleService.ensureInitialConsoleMember(authenticatedUserId),
|
|
66
|
+
{ fallback: null }
|
|
67
|
+
);
|
|
68
|
+
if (ownerUserId) {
|
|
69
|
+
consoleOwnerInitialized = true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return authResult;
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export { createConsoleAuthServiceDecorator };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { normalizeObject, normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
2
2
|
|
|
3
3
|
function createConsoleBootstrapContributor({ consoleService } = {}) {
|
|
4
|
-
if (!consoleService || typeof consoleService.
|
|
5
|
-
throw new Error("createConsoleBootstrapContributor requires consoleService.
|
|
4
|
+
if (!consoleService || typeof consoleService.isConsoleOwnerUserId !== "function") {
|
|
5
|
+
throw new Error("createConsoleBootstrapContributor requires consoleService.isConsoleOwnerUserId().");
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
return Object.freeze({
|
|
@@ -17,11 +17,7 @@ function createConsoleBootstrapContributor({ consoleService } = {}) {
|
|
|
17
17
|
|
|
18
18
|
let consoleOwner = false;
|
|
19
19
|
if (authenticatedUserId) {
|
|
20
|
-
|
|
21
|
-
await consoleService.ensureInitialConsoleMember(authenticatedUserId),
|
|
22
|
-
{ fallback: null }
|
|
23
|
-
);
|
|
24
|
-
consoleOwner = Boolean(seededConsoleOwnerUserId) && seededConsoleOwnerUserId === authenticatedUserId;
|
|
20
|
+
consoleOwner = await consoleService.isConsoleOwnerUserId(authenticatedUserId);
|
|
25
21
|
}
|
|
26
22
|
|
|
27
23
|
return {
|
|
@@ -18,8 +18,8 @@ function bootConsoleSettingsRoutes(app) {
|
|
|
18
18
|
tags: ["console", "settings"],
|
|
19
19
|
summary: "Get console settings"
|
|
20
20
|
},
|
|
21
|
-
|
|
22
|
-
200: consoleSettingsResource.operations.view.
|
|
21
|
+
responses: withStandardErrorResponses({
|
|
22
|
+
200: consoleSettingsResource.operations.view.output
|
|
23
23
|
})
|
|
24
24
|
},
|
|
25
25
|
async function (request, reply) {
|
|
@@ -40,10 +40,10 @@ function bootConsoleSettingsRoutes(app) {
|
|
|
40
40
|
tags: ["console", "settings"],
|
|
41
41
|
summary: "Update console settings"
|
|
42
42
|
},
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
body: consoleSettingsResource.operations.replace.body,
|
|
44
|
+
responses: withStandardErrorResponses(
|
|
45
45
|
{
|
|
46
|
-
200: consoleSettingsResource.operations.view.
|
|
46
|
+
200: consoleSettingsResource.operations.view.output
|
|
47
47
|
},
|
|
48
48
|
{ includeValidation400: true }
|
|
49
49
|
)
|
|
@@ -51,9 +51,7 @@ function bootConsoleSettingsRoutes(app) {
|
|
|
51
51
|
async function (request, reply) {
|
|
52
52
|
const response = await request.executeAction({
|
|
53
53
|
actionId: "console.settings.update",
|
|
54
|
-
input:
|
|
55
|
-
payload: request.input.body
|
|
56
|
-
}
|
|
54
|
+
input: request.input.body
|
|
57
55
|
});
|
|
58
56
|
reply.code(200).send(response);
|
|
59
57
|
}
|
|
@@ -5,6 +5,9 @@ function createService({ consoleSettingsRepository } = {}) {
|
|
|
5
5
|
if (!consoleSettingsRepository || typeof consoleSettingsRepository.ensureOwnerUserId !== "function") {
|
|
6
6
|
throw new Error("consoleService requires consoleSettingsRepository.ensureOwnerUserId().");
|
|
7
7
|
}
|
|
8
|
+
if (!consoleSettingsRepository || typeof consoleSettingsRepository.getSingleton !== "function") {
|
|
9
|
+
throw new Error("consoleService requires consoleSettingsRepository.getSingleton().");
|
|
10
|
+
}
|
|
8
11
|
|
|
9
12
|
async function ensureInitialConsoleMember(userId, options = {}) {
|
|
10
13
|
const normalizedUserId = normalizeRecordId(userId, { fallback: null });
|
|
@@ -15,6 +18,17 @@ function createService({ consoleSettingsRepository } = {}) {
|
|
|
15
18
|
return consoleSettingsRepository.ensureOwnerUserId(normalizedUserId, options);
|
|
16
19
|
}
|
|
17
20
|
|
|
21
|
+
async function isConsoleOwnerUserId(userId, options = {}) {
|
|
22
|
+
const normalizedUserId = normalizeRecordId(userId, { fallback: null });
|
|
23
|
+
if (!normalizedUserId) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const settings = await consoleSettingsRepository.getSingleton(options);
|
|
28
|
+
const ownerUserId = normalizeRecordId(settings?.ownerUserId, { fallback: null });
|
|
29
|
+
return Boolean(ownerUserId) && ownerUserId === normalizedUserId;
|
|
30
|
+
}
|
|
31
|
+
|
|
18
32
|
async function requireConsoleOwner(context = {}, options = {}) {
|
|
19
33
|
const actorUserId = normalizeRecordId(context?.actor?.id, { fallback: null });
|
|
20
34
|
if (!actorUserId) {
|
|
@@ -29,6 +43,7 @@ function createService({ consoleSettingsRepository } = {}) {
|
|
|
29
43
|
|
|
30
44
|
return Object.freeze({
|
|
31
45
|
ensureInitialConsoleMember,
|
|
46
|
+
isConsoleOwnerUserId,
|
|
32
47
|
requireConsoleOwner
|
|
33
48
|
});
|
|
34
49
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
emptyInputValidator
|
|
3
3
|
} from "@jskit-ai/kernel/shared/actions/actionContributorHelpers";
|
|
4
4
|
import { consoleSettingsResource } from "../../shared/resources/consoleSettingsResource.js";
|
|
5
5
|
|
|
@@ -13,8 +13,8 @@ const consoleSettingsActions = Object.freeze([
|
|
|
13
13
|
permission: {
|
|
14
14
|
require: "authenticated"
|
|
15
15
|
},
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
input: emptyInputValidator,
|
|
17
|
+
output: consoleSettingsResource.operations.view.output,
|
|
18
18
|
idempotency: "none",
|
|
19
19
|
audit: {
|
|
20
20
|
actionName: "console.settings.read"
|
|
@@ -35,17 +35,15 @@ const consoleSettingsActions = Object.freeze([
|
|
|
35
35
|
permission: {
|
|
36
36
|
require: "authenticated"
|
|
37
37
|
},
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
},
|
|
41
|
-
outputValidator: consoleSettingsResource.operations.replace.outputValidator,
|
|
38
|
+
input: consoleSettingsResource.operations.replace.body,
|
|
39
|
+
output: consoleSettingsResource.operations.replace.output,
|
|
42
40
|
idempotency: "optional",
|
|
43
41
|
audit: {
|
|
44
42
|
actionName: "console.settings.update"
|
|
45
43
|
},
|
|
46
44
|
observability: {},
|
|
47
45
|
async execute(input, context, deps) {
|
|
48
|
-
return deps.consoleSettingsService.updateSettings(input
|
|
46
|
+
return deps.consoleSettingsService.updateSettings(input, {
|
|
49
47
|
context
|
|
50
48
|
});
|
|
51
49
|
}
|
|
@@ -4,30 +4,12 @@ import {
|
|
|
4
4
|
createWithTransaction
|
|
5
5
|
} from "@jskit-ai/database-runtime/shared";
|
|
6
6
|
import { toInsertDateTime } from "@jskit-ai/database-runtime/shared";
|
|
7
|
-
import { normalizeObjectInput } from "@jskit-ai/kernel/shared/validators/inputNormalization";
|
|
8
7
|
import { normalizeRecordId as normalizeKernelRecordId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
9
|
-
import { consoleSettingsFields } from "../../shared/resources/consoleSettingsFields.js";
|
|
10
8
|
|
|
11
9
|
function nowDb() {
|
|
12
10
|
return toInsertDateTime();
|
|
13
11
|
}
|
|
14
12
|
|
|
15
|
-
function mapSettings(row = {}) {
|
|
16
|
-
const settings = {};
|
|
17
|
-
for (const field of consoleSettingsFields) {
|
|
18
|
-
const repositoryColumn = field?.repository?.column;
|
|
19
|
-
const rawValue = Object.hasOwn(row, repositoryColumn)
|
|
20
|
-
? row[repositoryColumn]
|
|
21
|
-
: field.resolveDefault({
|
|
22
|
-
settings: row
|
|
23
|
-
});
|
|
24
|
-
settings[field.key] = field.normalizeOutput(rawValue, {
|
|
25
|
-
settings: row
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
return settings;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
13
|
function mapSingletonRow(row) {
|
|
32
14
|
if (!row) {
|
|
33
15
|
throw new Error("console_settings singleton row is missing.");
|
|
@@ -37,7 +19,7 @@ function mapSingletonRow(row) {
|
|
|
37
19
|
return {
|
|
38
20
|
id: normalizeDbRecordId(row.id, { fallback: "1" }),
|
|
39
21
|
ownerUserId,
|
|
40
|
-
settings:
|
|
22
|
+
settings: {},
|
|
41
23
|
createdAt: toIsoString(row.created_at),
|
|
42
24
|
updatedAt: toIsoString(row.updated_at)
|
|
43
25
|
};
|
|
@@ -88,20 +70,10 @@ function createRepository(knex) {
|
|
|
88
70
|
|
|
89
71
|
async function updateSingleton(patch, options = {}) {
|
|
90
72
|
const client = options?.trx || knex;
|
|
91
|
-
const source = normalizeObjectInput(patch);
|
|
92
73
|
const dbPatch = {
|
|
93
74
|
updated_at: nowDb()
|
|
94
75
|
};
|
|
95
76
|
|
|
96
|
-
for (const field of consoleSettingsFields) {
|
|
97
|
-
if (!Object.hasOwn(source, field.key)) {
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
dbPatch[field.repository.column] = field.normalizeInput(source[field.key], {
|
|
101
|
-
payload: source
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
77
|
await client("console_settings")
|
|
106
78
|
.where({ id: 1 })
|
|
107
79
|
.update(dbPatch);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { registerAuthServiceDecorator } from "@jskit-ai/auth-core/server/authServiceDecoratorRegistry";
|
|
2
|
+
import { createConsoleAuthServiceDecorator } from "./consoleAuthServiceDecorator.js";
|
|
1
3
|
import { createRepository as createConsoleSettingsRepository } from "./consoleSettings/consoleSettingsRepository.js";
|
|
2
4
|
import { registerConsoleCoreActionSurfaceSources } from "./support/consoleActionSurfaces.js";
|
|
3
5
|
|
|
@@ -8,6 +10,12 @@ function registerConsoleCore(app) {
|
|
|
8
10
|
|
|
9
11
|
registerConsoleCoreActionSurfaceSources(app);
|
|
10
12
|
|
|
13
|
+
registerAuthServiceDecorator(app, "console.core.authServiceDecorator", (scope) =>
|
|
14
|
+
createConsoleAuthServiceDecorator({
|
|
15
|
+
consoleService: scope.make("consoleService")
|
|
16
|
+
})
|
|
17
|
+
);
|
|
18
|
+
|
|
11
19
|
app.singleton("consoleSettingsRepository", (scope) => {
|
|
12
20
|
const knex = scope.make("jskit.database.knex");
|
|
13
21
|
return createConsoleSettingsRepository(knex);
|
|
@@ -1,119 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createSchema } from "json-rest-schema";
|
|
2
|
+
import { defineCrudResource } from "@jskit-ai/resource-crud-core/shared/crudResource";
|
|
2
3
|
import { createOperationMessages } from "../operationMessages.js";
|
|
3
|
-
import {
|
|
4
|
-
createCursorListValidator,
|
|
5
|
-
normalizeObjectInput,
|
|
6
|
-
normalizeSettingsFieldInput,
|
|
7
|
-
normalizeSettingsFieldOutput
|
|
8
|
-
} from "@jskit-ai/kernel/shared/validators";
|
|
9
|
-
import { consoleSettingsFields } from "./consoleSettingsFields.js";
|
|
10
4
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function buildOutputSchema() {
|
|
20
|
-
const properties = {};
|
|
21
|
-
for (const field of consoleSettingsFields) {
|
|
22
|
-
properties[field.key] = field.outputSchema;
|
|
23
|
-
}
|
|
24
|
-
return Type.Object(properties, { additionalProperties: false });
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function buildConsoleSettingsRecordSchema() {
|
|
28
|
-
return Type.Object(
|
|
29
|
-
{
|
|
30
|
-
settings: buildOutputSchema()
|
|
31
|
-
},
|
|
32
|
-
{ additionalProperties: false }
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function buildConsoleSettingsCreateSchema() {
|
|
37
|
-
return buildCreateSchema();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function buildConsoleSettingsReplaceSchema() {
|
|
41
|
-
return buildConsoleSettingsCreateSchema();
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function buildConsoleSettingsPatchSchema() {
|
|
45
|
-
return Type.Partial(buildConsoleSettingsCreateSchema(), {
|
|
46
|
-
additionalProperties: false
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function normalizeConsoleSettingsInput(payload = {}) {
|
|
51
|
-
return normalizeSettingsFieldInput(payload, consoleSettingsFields);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const consoleSettingsOutputValidator = Object.freeze({
|
|
55
|
-
get schema() {
|
|
56
|
-
return buildConsoleSettingsRecordSchema();
|
|
57
|
-
},
|
|
58
|
-
normalize(payload = {}) {
|
|
59
|
-
const source = normalizeObjectInput(payload);
|
|
60
|
-
const settingsSource = normalizeObjectInput(source.settings);
|
|
61
|
-
|
|
62
|
-
return {
|
|
63
|
-
settings: normalizeSettingsFieldOutput(settingsSource, consoleSettingsFields)
|
|
64
|
-
};
|
|
5
|
+
const consoleSettingsBodySchema = createSchema({});
|
|
6
|
+
const consoleSettingsOutputSchema = createSchema({
|
|
7
|
+
settings: {
|
|
8
|
+
type: "object",
|
|
9
|
+
required: true,
|
|
10
|
+
schema: createSchema({})
|
|
65
11
|
}
|
|
66
12
|
});
|
|
67
13
|
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
const consoleSettingsResource = Object.freeze({
|
|
14
|
+
const consoleSettingsResource = defineCrudResource({
|
|
71
15
|
namespace: "consoleSettings",
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
list: Object.freeze({
|
|
79
|
-
method: "GET",
|
|
80
|
-
messages: CONSOLE_SETTINGS_OPERATION_MESSAGES,
|
|
81
|
-
outputValidator: createCursorListValidator(consoleSettingsOutputValidator)
|
|
82
|
-
}),
|
|
83
|
-
create: Object.freeze({
|
|
84
|
-
method: "POST",
|
|
85
|
-
messages: CONSOLE_SETTINGS_OPERATION_MESSAGES,
|
|
86
|
-
bodyValidator: Object.freeze({
|
|
87
|
-
get schema() {
|
|
88
|
-
return buildConsoleSettingsCreateSchema();
|
|
89
|
-
},
|
|
90
|
-
normalize: normalizeConsoleSettingsInput
|
|
91
|
-
}),
|
|
92
|
-
outputValidator: consoleSettingsOutputValidator
|
|
93
|
-
}),
|
|
94
|
-
replace: Object.freeze({
|
|
95
|
-
method: "PUT",
|
|
96
|
-
messages: CONSOLE_SETTINGS_OPERATION_MESSAGES,
|
|
97
|
-
bodyValidator: Object.freeze({
|
|
98
|
-
get schema() {
|
|
99
|
-
return buildConsoleSettingsReplaceSchema();
|
|
100
|
-
},
|
|
101
|
-
normalize: normalizeConsoleSettingsInput
|
|
102
|
-
}),
|
|
103
|
-
outputValidator: consoleSettingsOutputValidator
|
|
104
|
-
}),
|
|
105
|
-
patch: Object.freeze({
|
|
106
|
-
method: "PATCH",
|
|
107
|
-
messages: CONSOLE_SETTINGS_OPERATION_MESSAGES,
|
|
108
|
-
bodyValidator: Object.freeze({
|
|
109
|
-
get schema() {
|
|
110
|
-
return buildConsoleSettingsPatchSchema();
|
|
111
|
-
},
|
|
112
|
-
normalize: normalizeConsoleSettingsInput
|
|
113
|
-
}),
|
|
114
|
-
outputValidator: consoleSettingsOutputValidator
|
|
115
|
-
})
|
|
116
|
-
})
|
|
16
|
+
messages: createOperationMessages(),
|
|
17
|
+
crudOperations: ["view", "list", "create", "replace", "patch"],
|
|
18
|
+
crud: {
|
|
19
|
+
output: consoleSettingsOutputSchema,
|
|
20
|
+
body: consoleSettingsBodySchema
|
|
21
|
+
}
|
|
117
22
|
});
|
|
118
23
|
|
|
119
24
|
export { consoleSettingsResource };
|
|
@@ -19,7 +19,6 @@ function createAuthenticatedProfile(overrides = {}) {
|
|
|
19
19
|
|
|
20
20
|
test("bootstrap payload preserves consoleowner for authenticated users after users bootstrap runs", async () => {
|
|
21
21
|
const profile = createAuthenticatedProfile();
|
|
22
|
-
const ownerSeeds = [];
|
|
23
22
|
const app = createContainer();
|
|
24
23
|
|
|
25
24
|
app.instance("internal.repository.user-profiles", {
|
|
@@ -43,9 +42,8 @@ test("bootstrap payload preserves consoleowner for authenticated users after use
|
|
|
43
42
|
clearSessionCookies() {}
|
|
44
43
|
});
|
|
45
44
|
app.instance("consoleService", {
|
|
46
|
-
async
|
|
47
|
-
|
|
48
|
-
return String(userId || "");
|
|
45
|
+
async isConsoleOwnerUserId(userId) {
|
|
46
|
+
return String(userId || "") === profile.id;
|
|
49
47
|
}
|
|
50
48
|
});
|
|
51
49
|
|
|
@@ -68,7 +66,6 @@ test("bootstrap payload preserves consoleowner for authenticated users after use
|
|
|
68
66
|
reply: {}
|
|
69
67
|
});
|
|
70
68
|
|
|
71
|
-
assert.deepEqual(ownerSeeds, ["12"]);
|
|
72
69
|
assert.equal(payload.session.authenticated, true);
|
|
73
70
|
assert.equal(payload.session.userId, "12");
|
|
74
71
|
assert.deepEqual(payload.surfaceAccess, {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { createConsoleAuthServiceDecorator } from "../src/server/consoleAuthServiceDecorator.js";
|
|
4
|
+
|
|
5
|
+
test("console auth service decorator seeds first owner during session reads only", async () => {
|
|
6
|
+
const ownerSeeds = [];
|
|
7
|
+
const decorator = createConsoleAuthServiceDecorator({
|
|
8
|
+
consoleService: {
|
|
9
|
+
async ensureInitialConsoleMember(userId) {
|
|
10
|
+
ownerSeeds.push(String(userId || ""));
|
|
11
|
+
return String(userId || "");
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const authService = decorator.decorateAuthService({
|
|
17
|
+
async authenticateRequest(request) {
|
|
18
|
+
return {
|
|
19
|
+
authenticated: true,
|
|
20
|
+
profile: {
|
|
21
|
+
id: request.profileId
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
await authService.authenticateRequest({
|
|
28
|
+
profileId: "12",
|
|
29
|
+
url: "/api/bootstrap"
|
|
30
|
+
});
|
|
31
|
+
await authService.authenticateRequest({
|
|
32
|
+
profileId: "12",
|
|
33
|
+
url: "/api/session"
|
|
34
|
+
});
|
|
35
|
+
await authService.authenticateRequest({
|
|
36
|
+
profileId: "99",
|
|
37
|
+
url: "/api/session"
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
assert.deepEqual(ownerSeeds, ["12"]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("console auth service decorator leaves unauthenticated session reads alone", async () => {
|
|
44
|
+
const decorator = createConsoleAuthServiceDecorator({
|
|
45
|
+
consoleService: {
|
|
46
|
+
async ensureInitialConsoleMember() {
|
|
47
|
+
throw new Error("should not seed owner for anonymous session reads");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const authService = decorator.decorateAuthService({
|
|
53
|
+
async authenticateRequest() {
|
|
54
|
+
return {
|
|
55
|
+
authenticated: false
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const result = await authService.authenticateRequest({
|
|
61
|
+
url: "/api/session"
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
assert.equal(result.authenticated, false);
|
|
65
|
+
});
|
|
@@ -2,13 +2,11 @@ import assert from "node:assert/strict";
|
|
|
2
2
|
import test from "node:test";
|
|
3
3
|
import { createConsoleBootstrapContributor } from "../src/server/consoleBootstrapContributor.js";
|
|
4
4
|
|
|
5
|
-
test("console bootstrap contributor
|
|
6
|
-
const ownerSeeds = [];
|
|
5
|
+
test("console bootstrap contributor exposes consoleowner when the authenticated user already owns the console", async () => {
|
|
7
6
|
const contributor = createConsoleBootstrapContributor({
|
|
8
7
|
consoleService: {
|
|
9
|
-
async
|
|
10
|
-
|
|
11
|
-
return String(userId || "");
|
|
8
|
+
async isConsoleOwnerUserId(userId) {
|
|
9
|
+
return String(userId || "") === "12";
|
|
12
10
|
}
|
|
13
11
|
}
|
|
14
12
|
});
|
|
@@ -26,7 +24,6 @@ test("console bootstrap contributor seeds the initial console owner into the exi
|
|
|
26
24
|
}
|
|
27
25
|
});
|
|
28
26
|
|
|
29
|
-
assert.deepEqual(ownerSeeds, ["12"]);
|
|
30
27
|
assert.deepEqual(contribution, {
|
|
31
28
|
surfaceAccess: {
|
|
32
29
|
existing: true,
|
|
@@ -38,7 +35,7 @@ test("console bootstrap contributor seeds the initial console owner into the exi
|
|
|
38
35
|
test("console bootstrap contributor exposes a false consoleowner flag for anonymous bootstrap", async () => {
|
|
39
36
|
const contributor = createConsoleBootstrapContributor({
|
|
40
37
|
consoleService: {
|
|
41
|
-
async
|
|
38
|
+
async isConsoleOwnerUserId() {
|
|
42
39
|
throw new Error("should not be called for anonymous payload");
|
|
43
40
|
}
|
|
44
41
|
}
|
|
@@ -112,8 +112,6 @@ test("console settings route handlers use request.input payloads", async () => {
|
|
|
112
112
|
assert.equal(calls[0].actionId, "console.settings.read");
|
|
113
113
|
assert.deepEqual(calls[1], {
|
|
114
114
|
actionId: "console.settings.update",
|
|
115
|
-
input: {
|
|
116
|
-
payload: {}
|
|
117
|
-
}
|
|
115
|
+
input: {}
|
|
118
116
|
});
|
|
119
117
|
});
|
|
@@ -4,7 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { deriveResourceRequiredMetadata } from "@jskit-ai/kernel/_testable";
|
|
7
|
-
import "
|
|
7
|
+
import { resolveStructuredSchemaTransportSchema } from "@jskit-ai/kernel/shared/validators";
|
|
8
8
|
import { consoleSettingsResource } from "../src/shared/resources/consoleSettingsResource.js";
|
|
9
9
|
|
|
10
10
|
function assertResourceShape(resource, label) {
|
|
@@ -26,15 +26,27 @@ function assertResourceShape(resource, label) {
|
|
|
26
26
|
`${label}.operations.${operationName} must resolve messages from operation.messages or resource.messages.`
|
|
27
27
|
);
|
|
28
28
|
assert.equal(
|
|
29
|
-
typeof operation.
|
|
29
|
+
typeof resolveStructuredSchemaTransportSchema(operation.output, {
|
|
30
|
+
context: `${label}.operations.${operationName}.output`,
|
|
31
|
+
defaultMode: "replace"
|
|
32
|
+
}),
|
|
30
33
|
"object",
|
|
31
34
|
`${label}.operations.${operationName} payload schema is required.`
|
|
32
35
|
);
|
|
33
36
|
}
|
|
34
37
|
|
|
35
|
-
assert.equal(typeof resource.operations.create.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
assert.equal(typeof resolveStructuredSchemaTransportSchema(resource.operations.create.body, {
|
|
39
|
+
context: `${label}.operations.create.body`,
|
|
40
|
+
defaultMode: "create"
|
|
41
|
+
}), "object", `${label}.operations.create.body.schema is required.`);
|
|
42
|
+
assert.equal(typeof resolveStructuredSchemaTransportSchema(resource.operations.replace.body, {
|
|
43
|
+
context: `${label}.operations.replace.body`,
|
|
44
|
+
defaultMode: "replace"
|
|
45
|
+
}), "object", `${label}.operations.replace.body.schema is required.`);
|
|
46
|
+
assert.equal(typeof resolveStructuredSchemaTransportSchema(resource.operations.patch.body, {
|
|
47
|
+
context: `${label}.operations.patch.body`,
|
|
48
|
+
defaultMode: "patch"
|
|
49
|
+
}), "object", `${label}.operations.patch.body.schema is required.`);
|
|
38
50
|
|
|
39
51
|
const requiredMetadata = deriveResourceRequiredMetadata(resource);
|
|
40
52
|
assert.ok(Array.isArray(requiredMetadata.create), `${label}.derivedRequired.create must be an array.`);
|
|
@@ -50,23 +62,29 @@ test("console settings operations expose canonical validators", () => {
|
|
|
50
62
|
for (const operationName of ["view", "list", "create", "replace", "patch"]) {
|
|
51
63
|
const operation = consoleSettingsResource.operations?.[operationName];
|
|
52
64
|
assert.equal(typeof operation?.method, "string", `${operationName}.method must exist.`);
|
|
53
|
-
assert.equal(typeof operation?.
|
|
54
|
-
|
|
55
|
-
|
|
65
|
+
assert.equal(typeof resolveStructuredSchemaTransportSchema(operation?.output, {
|
|
66
|
+
context: `${operationName}.output`,
|
|
67
|
+
defaultMode: "replace"
|
|
68
|
+
}), "object", `${operationName}.output.schema must exist.`);
|
|
69
|
+
if (operation?.body) {
|
|
70
|
+
assert.equal(typeof resolveStructuredSchemaTransportSchema(operation.body, {
|
|
71
|
+
context: `${operationName}.body`,
|
|
72
|
+
defaultMode: operationName === "create" ? "create" : operationName === "replace" ? "replace" : "patch"
|
|
73
|
+
}), "object", `${operationName}.body.schema must exist.`);
|
|
56
74
|
}
|
|
57
75
|
}
|
|
58
76
|
});
|
|
59
77
|
|
|
60
|
-
test("console-core
|
|
78
|
+
test("console-core does not use workspaceRoutes.js helper path", () => {
|
|
61
79
|
const testFilePath = fileURLToPath(import.meta.url);
|
|
62
80
|
const packageRoot = path.resolve(path.dirname(testFilePath), "..");
|
|
63
|
-
const
|
|
64
|
-
assert.equal(existsSync(
|
|
81
|
+
const workspaceRoutesFilePath = path.join(packageRoot, "src", "server", "common", "routes", "workspaceRoutes.js");
|
|
82
|
+
assert.equal(existsSync(workspaceRoutesFilePath), false, "workspaceRoutes.js must not exist.");
|
|
65
83
|
});
|
|
66
84
|
|
|
67
|
-
test("console-core route validators do not live under
|
|
85
|
+
test("console-core route validators do not live under src/shared/schema", () => {
|
|
68
86
|
const testFilePath = fileURLToPath(import.meta.url);
|
|
69
87
|
const packageRoot = path.resolve(path.dirname(testFilePath), "..");
|
|
70
|
-
const
|
|
71
|
-
assert.equal(existsSync(
|
|
88
|
+
const sharedSchemaDirPath = path.join(packageRoot, "src", "shared", "schema");
|
|
89
|
+
assert.equal(existsSync(sharedSchemaDirPath), false, "src/shared/schema must not exist.");
|
|
72
90
|
});
|
|
@@ -9,6 +9,11 @@ function createFixture(initialOwnerUserId = null) {
|
|
|
9
9
|
|
|
10
10
|
const service = createService({
|
|
11
11
|
consoleSettingsRepository: {
|
|
12
|
+
async getSingleton() {
|
|
13
|
+
return {
|
|
14
|
+
ownerUserId: state.ownerUserId
|
|
15
|
+
};
|
|
16
|
+
},
|
|
12
17
|
async ensureOwnerUserId(userId) {
|
|
13
18
|
const normalizedUserId = String(userId || "");
|
|
14
19
|
if (!state.ownerUserId) {
|
|
@@ -33,6 +38,13 @@ test("consoleService seeds the first authenticated user as console owner", async
|
|
|
33
38
|
assert.equal(state.ownerUserId, "7");
|
|
34
39
|
});
|
|
35
40
|
|
|
41
|
+
test("consoleService.isConsoleOwnerUserId reports current ownership without reseeding", async () => {
|
|
42
|
+
const { service } = createFixture("7");
|
|
43
|
+
|
|
44
|
+
assert.equal(await service.isConsoleOwnerUserId("7"), true);
|
|
45
|
+
assert.equal(await service.isConsoleOwnerUserId("9"), false);
|
|
46
|
+
});
|
|
47
|
+
|
|
36
48
|
test("consoleService.requireConsoleOwner denies authenticated non-owners", async () => {
|
|
37
49
|
const { service } = createFixture("7");
|
|
38
50
|
|
|
@@ -4,10 +4,18 @@ import { registerConsoleCore } from "../src/server/registerConsoleCore.js";
|
|
|
4
4
|
|
|
5
5
|
test("registerConsoleCore registers the console action surface alias when action runtime is available", () => {
|
|
6
6
|
const calls = [];
|
|
7
|
+
const tags = [];
|
|
7
8
|
const app = {
|
|
8
9
|
singleton() {
|
|
9
10
|
return this;
|
|
10
11
|
},
|
|
12
|
+
tag(token, tagName) {
|
|
13
|
+
tags.push({
|
|
14
|
+
token: String(token || ""),
|
|
15
|
+
tagName: String(tagName || "")
|
|
16
|
+
});
|
|
17
|
+
return this;
|
|
18
|
+
},
|
|
11
19
|
actionSurfaceSource(sourceName, resolver) {
|
|
12
20
|
calls.push({
|
|
13
21
|
sourceName: String(sourceName || ""),
|
|
@@ -25,12 +33,21 @@ test("registerConsoleCore registers the console action surface alias when action
|
|
|
25
33
|
resolverType: "function"
|
|
26
34
|
}
|
|
27
35
|
]);
|
|
36
|
+
assert.deepEqual(tags, [
|
|
37
|
+
{
|
|
38
|
+
token: "console.core.authServiceDecorator",
|
|
39
|
+
tagName: "jskit.auth.service.decorators"
|
|
40
|
+
}
|
|
41
|
+
]);
|
|
28
42
|
});
|
|
29
43
|
|
|
30
44
|
test("registerConsoleCore still works when action runtime has not installed actionSurfaceSource yet", () => {
|
|
31
45
|
const app = {
|
|
32
46
|
singleton() {
|
|
33
47
|
return this;
|
|
48
|
+
},
|
|
49
|
+
tag() {
|
|
50
|
+
return this;
|
|
34
51
|
}
|
|
35
52
|
};
|
|
36
53
|
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { normalizeText } from "@jskit-ai/kernel/shared/actions/textNormalization";
|
|
2
|
-
import { resolveGlobalArrayRegistry } from "./resolveGlobalArrayRegistry.js";
|
|
3
|
-
|
|
4
|
-
const consoleSettingsFields = resolveGlobalArrayRegistry("jskit.console-core.consoleSettingsFields");
|
|
5
|
-
|
|
6
|
-
function defineField(field = {}) {
|
|
7
|
-
const key = normalizeText(field.key);
|
|
8
|
-
if (!key) {
|
|
9
|
-
throw new TypeError("consoleSettingsFields.defineField requires field.key.");
|
|
10
|
-
}
|
|
11
|
-
if (consoleSettingsFields.some((entry) => entry.key === key)) {
|
|
12
|
-
throw new Error(`consoleSettingsFields.defineField duplicate key: ${key}`);
|
|
13
|
-
}
|
|
14
|
-
if (!field.inputSchema || typeof field.inputSchema !== "object") {
|
|
15
|
-
throw new TypeError(`consoleSettingsFields.defineField("${key}") requires inputSchema.`);
|
|
16
|
-
}
|
|
17
|
-
if (!field.outputSchema || typeof field.outputSchema !== "object") {
|
|
18
|
-
throw new TypeError(`consoleSettingsFields.defineField("${key}") requires outputSchema.`);
|
|
19
|
-
}
|
|
20
|
-
const repositoryColumn = normalizeText(field?.repository?.column);
|
|
21
|
-
if (!repositoryColumn) {
|
|
22
|
-
throw new TypeError(`consoleSettingsFields.defineField("${key}") requires repository.column.`);
|
|
23
|
-
}
|
|
24
|
-
if (typeof field.normalizeInput !== "function") {
|
|
25
|
-
throw new TypeError(`consoleSettingsFields.defineField("${key}") requires normalizeInput.`);
|
|
26
|
-
}
|
|
27
|
-
if (typeof field.normalizeOutput !== "function") {
|
|
28
|
-
throw new TypeError(`consoleSettingsFields.defineField("${key}") requires normalizeOutput.`);
|
|
29
|
-
}
|
|
30
|
-
if (typeof field.resolveDefault !== "function") {
|
|
31
|
-
throw new TypeError(`consoleSettingsFields.defineField("${key}") requires resolveDefault.`);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
consoleSettingsFields.push({
|
|
35
|
-
key,
|
|
36
|
-
repository: Object.freeze({
|
|
37
|
-
column: repositoryColumn
|
|
38
|
-
}),
|
|
39
|
-
required: field.required !== false,
|
|
40
|
-
inputSchema: field.inputSchema,
|
|
41
|
-
outputSchema: field.outputSchema,
|
|
42
|
-
normalizeInput: field.normalizeInput,
|
|
43
|
-
normalizeOutput: field.normalizeOutput,
|
|
44
|
-
resolveDefault: field.resolveDefault
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function resetConsoleSettingsFields() {
|
|
49
|
-
consoleSettingsFields.splice(0, consoleSettingsFields.length);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export {
|
|
53
|
-
defineField,
|
|
54
|
-
resetConsoleSettingsFields,
|
|
55
|
-
consoleSettingsFields
|
|
56
|
-
};
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// @jskit-contract console.settings-fields.v1
|
|
2
|
-
// Append-only settings field registrations for console settings.
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
defineField,
|
|
6
|
-
resetConsoleSettingsFields
|
|
7
|
-
} from "@jskit-ai/console-core/shared/resources/consoleSettingsFields";
|
|
8
|
-
|
|
9
|
-
resetConsoleSettingsFields();
|
|
10
|
-
|
|
11
|
-
void defineField;
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
|
|
4
|
-
async function importWithIdentity(url, identity) {
|
|
5
|
-
return import(`${url.href}?identity=${identity}`);
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
test("settings field registries stay shared across module identities", async () => {
|
|
9
|
-
const consoleModuleUrl = new URL("../src/shared/resources/consoleSettingsFields.js", import.meta.url);
|
|
10
|
-
|
|
11
|
-
const consoleA = await importWithIdentity(consoleModuleUrl, "console-a");
|
|
12
|
-
const consoleB = await importWithIdentity(consoleModuleUrl, "console-b");
|
|
13
|
-
assert.equal(consoleA.consoleSettingsFields, consoleB.consoleSettingsFields);
|
|
14
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import "../templates/packages/main/src/shared/resources/consoleSettingsFields.js";
|