@jskit-ai/console-core 0.1.1
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 +119 -0
- package/package.json +18 -0
- package/src/server/ConsoleCoreServiceProvider.js +22 -0
- package/src/server/consoleBootstrapContributor.js +37 -0
- package/src/server/consoleSettings/bootConsoleSettingsRoutes.js +63 -0
- package/src/server/consoleSettings/consoleService.js +36 -0
- package/src/server/consoleSettings/consoleSettingsActions.js +55 -0
- package/src/server/consoleSettings/consoleSettingsRepository.js +119 -0
- package/src/server/consoleSettings/consoleSettingsService.js +40 -0
- package/src/server/consoleSettings/registerConsoleSettings.js +56 -0
- package/src/server/registerConsoleBootstrap.js +16 -0
- package/src/server/registerConsoleCore.js +17 -0
- package/src/server/support/consoleActionSurfaces.js +62 -0
- package/src/shared/operationMessages.js +16 -0
- package/src/shared/resources/consoleSettingsFields.js +54 -0
- package/src/shared/resources/consoleSettingsResource.js +119 -0
- package/src/shared/resources/resolveGlobalArrayRegistry.js +6 -0
- package/templates/migrations/console_core_generic_initial.cjs +27 -0
- package/templates/packages/main/src/shared/resources/consoleSettingsFields.js +11 -0
- package/test/bootstrapPayloadIntegration.test.js +86 -0
- package/test/consoleActionSurfaces.test.js +24 -0
- package/test/consoleBootstrapContributor.test.js +64 -0
- package/test/consoleRouteRequestInputValidator.test.js +119 -0
- package/test/consoleRouteResources.test.js +72 -0
- package/test/consoleService.test.js +57 -0
- package/test/consoleSettingsService.test.js +86 -0
- package/test/exportsContract.test.js +26 -0
- package/test/registerConsoleCore.test.js +38 -0
- package/test/registerServiceRealtimeEvents.test.js +38 -0
- package/test/repositoryContracts.test.js +28 -0
- package/test/settingsFieldRegistriesSingleton.test.js +14 -0
- package/test-support/registerDefaultSettingsFields.js +1 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { createService } from "../src/server/consoleSettings/consoleSettingsService.js";
|
|
4
|
+
|
|
5
|
+
function createFixture({ deny = false } = {}) {
|
|
6
|
+
const calls = {
|
|
7
|
+
requireConsoleOwner: [],
|
|
8
|
+
updateSingleton: []
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const service = createService({
|
|
12
|
+
consoleService: {
|
|
13
|
+
async requireConsoleOwner(context) {
|
|
14
|
+
calls.requireConsoleOwner.push(context || null);
|
|
15
|
+
if (deny) {
|
|
16
|
+
const error = new Error("Forbidden.");
|
|
17
|
+
error.status = 403;
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
consoleSettingsRepository: {
|
|
23
|
+
async getSingleton() {
|
|
24
|
+
return {};
|
|
25
|
+
},
|
|
26
|
+
async updateSingleton(patch = {}) {
|
|
27
|
+
calls.updateSingleton.push({ ...patch });
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return { service, calls };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
test("consoleSettingsService.getSettings requires owner access and returns normalized payload", async () => {
|
|
37
|
+
const { service, calls } = createFixture();
|
|
38
|
+
const context = {
|
|
39
|
+
actor: {
|
|
40
|
+
id: 7
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const response = await service.getSettings({ context });
|
|
45
|
+
|
|
46
|
+
assert.deepEqual(calls.requireConsoleOwner, [context]);
|
|
47
|
+
assert.deepEqual(response, {
|
|
48
|
+
settings: {}
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("consoleSettingsService.updateSettings requires owner access before writing", async () => {
|
|
53
|
+
const { service, calls } = createFixture();
|
|
54
|
+
const context = {
|
|
55
|
+
actor: {
|
|
56
|
+
id: 7
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const response = await service.updateSettings(
|
|
61
|
+
{},
|
|
62
|
+
{ context }
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
assert.deepEqual(calls.requireConsoleOwner, [context]);
|
|
66
|
+
assert.deepEqual(calls.updateSingleton, [{}]);
|
|
67
|
+
assert.deepEqual(response, {
|
|
68
|
+
settings: {}
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("consoleSettingsService denies access when owner validation fails", async () => {
|
|
73
|
+
const { service } = createFixture({ deny: true });
|
|
74
|
+
|
|
75
|
+
await assert.rejects(
|
|
76
|
+
() =>
|
|
77
|
+
service.getSettings({
|
|
78
|
+
context: {
|
|
79
|
+
actor: {
|
|
80
|
+
id: 9
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}),
|
|
84
|
+
(error) => error?.status === 403
|
|
85
|
+
);
|
|
86
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import test from "node:test";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { evaluatePackageExportsContract } from "../../../tooling/test-support/exportsContract.mjs";
|
|
6
|
+
|
|
7
|
+
const TEST_DIRECTORY = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const REPO_ROOT = path.resolve(TEST_DIRECTORY, "..", "..", "..");
|
|
9
|
+
const PACKAGE_DIR = path.join(REPO_ROOT, "packages", "console-core");
|
|
10
|
+
|
|
11
|
+
test("console-core exports are explicit and aligned with production/template usage", () => {
|
|
12
|
+
const result = evaluatePackageExportsContract({
|
|
13
|
+
repoRoot: REPO_ROOT,
|
|
14
|
+
packageDir: PACKAGE_DIR,
|
|
15
|
+
packageId: "@jskit-ai/console-core"
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
assert.deepEqual(result.wildcardExports, [], `console-core exports must be explicit. Remove wildcard keys: ${result.wildcardExports.join(", ")}`);
|
|
19
|
+
assert.deepEqual(
|
|
20
|
+
result.missingRequiredExports,
|
|
21
|
+
[],
|
|
22
|
+
`console-core required exports missing: ${result.missingRequiredExports.join(", ")}`
|
|
23
|
+
);
|
|
24
|
+
assert.deepEqual(result.missingExports, [], `console-core imports missing from package exports:\n${result.missingExports.join("\n")}`);
|
|
25
|
+
assert.deepEqual(result.staleExports, [], `Stale console-core exports found. Remove stale keys: ${result.staleExports.join(", ")}`);
|
|
26
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { registerConsoleCore } from "../src/server/registerConsoleCore.js";
|
|
4
|
+
|
|
5
|
+
test("registerConsoleCore registers the console action surface alias when action runtime is available", () => {
|
|
6
|
+
const calls = [];
|
|
7
|
+
const app = {
|
|
8
|
+
singleton() {
|
|
9
|
+
return this;
|
|
10
|
+
},
|
|
11
|
+
actionSurfaceSource(sourceName, resolver) {
|
|
12
|
+
calls.push({
|
|
13
|
+
sourceName: String(sourceName || ""),
|
|
14
|
+
resolverType: typeof resolver
|
|
15
|
+
});
|
|
16
|
+
return this;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
registerConsoleCore(app);
|
|
21
|
+
|
|
22
|
+
assert.deepEqual(calls, [
|
|
23
|
+
{
|
|
24
|
+
sourceName: "console",
|
|
25
|
+
resolverType: "function"
|
|
26
|
+
}
|
|
27
|
+
]);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("registerConsoleCore still works when action runtime has not installed actionSurfaceSource yet", () => {
|
|
31
|
+
const app = {
|
|
32
|
+
singleton() {
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
assert.doesNotThrow(() => registerConsoleCore(app));
|
|
38
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { registerConsoleSettings } from "../src/server/consoleSettings/registerConsoleSettings.js";
|
|
4
|
+
|
|
5
|
+
function createAppDouble() {
|
|
6
|
+
const serviceCalls = [];
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
serviceCalls,
|
|
10
|
+
app: {
|
|
11
|
+
singleton() {
|
|
12
|
+
return this;
|
|
13
|
+
},
|
|
14
|
+
service(token, factory, metadata) {
|
|
15
|
+
serviceCalls.push({
|
|
16
|
+
token,
|
|
17
|
+
factory,
|
|
18
|
+
metadata
|
|
19
|
+
});
|
|
20
|
+
return this;
|
|
21
|
+
},
|
|
22
|
+
actions() {
|
|
23
|
+
return this;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function findServiceCall(serviceCalls, token) {
|
|
30
|
+
return serviceCalls.find((entry) => entry.token === token) || null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
test("console settings register publishes console.settings.changed", () => {
|
|
34
|
+
const payload = createAppDouble();
|
|
35
|
+
registerConsoleSettings(payload.app);
|
|
36
|
+
const consoleSettings = findServiceCall(payload.serviceCalls, "console.settings.service");
|
|
37
|
+
assert.equal(consoleSettings?.metadata?.events?.updateSettings?.[0]?.realtime?.event, "console.settings.changed");
|
|
38
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { createRepository as createConsoleSettingsRepository } from "../src/server/consoleSettings/consoleSettingsRepository.js";
|
|
4
|
+
|
|
5
|
+
function createKnexStub() {
|
|
6
|
+
const knex = Object.assign(() => {
|
|
7
|
+
throw new Error("query execution not expected");
|
|
8
|
+
}, {
|
|
9
|
+
async transaction(work) {
|
|
10
|
+
return work({ trxId: "trx-1" });
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
return knex;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
test("console-core repositories expose withTransaction", async () => {
|
|
18
|
+
const knex = createKnexStub();
|
|
19
|
+
const repositories = [
|
|
20
|
+
createConsoleSettingsRepository(knex)
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
for (const repository of repositories) {
|
|
24
|
+
assert.equal(typeof repository.withTransaction, "function");
|
|
25
|
+
const result = await repository.withTransaction(async (trx) => ({ id: trx.trxId }));
|
|
26
|
+
assert.deepEqual(result, { id: "trx-1" });
|
|
27
|
+
}
|
|
28
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
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
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "../templates/packages/main/src/shared/resources/consoleSettingsFields.js";
|