@jskit-ai/workspaces-core 0.1.137 → 0.1.138
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 +38 -238
- package/patterns/workspace-server/PATTERN.md +60 -0
- package/src/server/WorkspacesFeature.js +226 -0
- package/src/server/WorkspacesIntegrationsProvider.js +74 -0
- package/src/server/common/contributors/workspaceActionContextContributor.js +18 -6
- package/src/server/common/contributors/workspaceAuthPolicyContextResolver.js +25 -4
- package/src/server/common/contributors/workspaceRouteVisibilityResolver.js +22 -4
- package/src/server/common/services/workspaceContextService.js +7 -2
- package/src/server/common/support/realtimeServiceEvents.js +102 -61
- package/src/server/support/workspaceActionSurfaces.js +18 -13
- package/src/server/workspaceDirectory/bootWorkspaceDirectoryRoutes.js +5 -11
- package/src/server/workspaceDirectory/workspaceDirectoryActions.js +24 -14
- package/src/server/workspaceMembers/bootWorkspaceMembers.js +5 -12
- package/src/server/workspaceMembers/workspaceMembersActions.js +63 -23
- package/src/server/workspacePendingInvitations/bootWorkspacePendingInvitations.js +4 -6
- package/src/server/workspacePendingInvitations/workspacePendingInvitationsActions.js +31 -12
- package/src/server/workspaceSettings/bootWorkspaceSettings.js +5 -8
- package/src/server/workspaceSettings/workspaceSettingsActions.js +26 -8
- package/src/server/workspaceSettings/workspaceSettingsRepository.js +4 -4
- package/test/exportsContract.test.js +2 -1
- package/test/featureRuntime.test.js +116 -0
- package/test/packageMetadata.test.js +20 -54
- package/test/workspaceActionContextContributor.test.js +49 -8
- package/test/workspaceActionSurfaces.test.js +36 -1
- package/test/workspaceAuthPolicyContextResolver.test.js +61 -0
- package/test/workspaceRouteVisibilityResolver.test.js +31 -0
- package/test/workspaceService.test.js +62 -0
- package/test/workspaceSettingsActions.test.js +102 -16
- package/test/workspacesRouteRequestInputValidator.test.js +14 -65
- package/src/server/WorkspacesCoreServiceProvider.js +0 -64
- package/src/server/registerWorkspaceBootstrap.js +0 -27
- package/src/server/registerWorkspaceCore.js +0 -101
- package/src/server/registerWorkspaceRepositories.js +0 -30
- package/src/server/support/workspaceServerScopeSupport.js +0 -18
- package/src/server/workspaceDirectory/registerWorkspaceDirectory.js +0 -19
- package/src/server/workspaceMembers/registerWorkspaceMembers.js +0 -137
- package/src/server/workspacePendingInvitations/registerWorkspacePendingInvitations.js +0 -130
- package/src/server/workspaceSettings/registerWorkspaceSettings.js +0 -66
- package/test/registerServiceRealtimeEvents.test.js +0 -116
- package/test/registerWorkspaceBootstrap.test.js +0 -86
- package/test/registerWorkspaceDirectory.test.js +0 -31
- package/test/registerWorkspaceMembers.test.js +0 -140
- package/test/registerWorkspaceSettings.test.js +0 -40
- package/test/workspaceServerScopeSupport.test.js +0 -131
- /package/{templates/migrations → migrations}/workspaces_core_initial.cjs +0 -0
- /package/{templates/migrations → migrations}/workspaces_core_workspace_settings_single_name_source.cjs +0 -0
- /package/{templates/migrations → migrations}/workspaces_core_workspaces_drop_color.cjs +0 -0
- /package/{templates → patterns/workspace-server/example}/config/roles.js +0 -0
- /package/{templates → patterns/workspace-server/example}/packages/main/src/server/email/workspaceInviteEmail.js +0 -0
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import { registerWorkspaceMembers } from "../src/server/workspaceMembers/registerWorkspaceMembers.js";
|
|
4
|
-
import { registerWorkspacePendingInvitations } from "../src/server/workspacePendingInvitations/registerWorkspacePendingInvitations.js";
|
|
5
|
-
|
|
6
|
-
function createAppDouble() {
|
|
7
|
-
const serviceCalls = [];
|
|
8
|
-
|
|
9
|
-
return {
|
|
10
|
-
serviceCalls,
|
|
11
|
-
app: {
|
|
12
|
-
singleton() {
|
|
13
|
-
return this;
|
|
14
|
-
},
|
|
15
|
-
service(token, factory, metadata) {
|
|
16
|
-
serviceCalls.push({
|
|
17
|
-
token,
|
|
18
|
-
factory,
|
|
19
|
-
metadata
|
|
20
|
-
});
|
|
21
|
-
return this;
|
|
22
|
-
},
|
|
23
|
-
actions() {
|
|
24
|
-
return this;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function findServiceCall(serviceCalls, token) {
|
|
31
|
-
return serviceCalls.find((entry) => entry.token === token) || null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
test("workspace register functions publish members/invites/workspace-list realtime events", async () => {
|
|
35
|
-
const membersApp = createAppDouble();
|
|
36
|
-
registerWorkspaceMembers(membersApp.app);
|
|
37
|
-
const members = findServiceCall(membersApp.serviceCalls, "workspaces.members.service");
|
|
38
|
-
assert.equal(members?.metadata?.events?.updateMemberRole?.[0]?.realtime?.event, "workspace.members.changed");
|
|
39
|
-
assert.equal(members?.metadata?.events?.updateMemberRole?.[1]?.realtime?.event, "users.bootstrap.changed");
|
|
40
|
-
assert.equal(members?.metadata?.events?.removeMember?.[0]?.realtime?.event, "workspace.members.changed");
|
|
41
|
-
assert.equal(members?.metadata?.events?.removeMember?.[1]?.realtime?.event, "users.bootstrap.changed");
|
|
42
|
-
assert.equal(members?.metadata?.events?.createInvite?.[0]?.realtime?.event, "workspace.invites.changed");
|
|
43
|
-
assert.equal(members?.metadata?.events?.createInvite?.[1]?.realtime?.event, "users.bootstrap.changed");
|
|
44
|
-
assert.equal(members?.metadata?.events?.createInvite?.[1]?.entityId?.({ result: { createdInviteId: "91" } }), "91");
|
|
45
|
-
assert.equal(members?.metadata?.events?.createInvite?.[1]?.realtime?.audience?.preset, "event_scope");
|
|
46
|
-
assert.equal(typeof members?.metadata?.events?.createInvite?.[1]?.realtime?.audience?.userQuery, "function");
|
|
47
|
-
const createInviteAudienceQueryResult = await members?.metadata?.events?.createInvite?.[1]?.realtime?.audience?.userQuery({
|
|
48
|
-
knex() {
|
|
49
|
-
return {
|
|
50
|
-
join() {
|
|
51
|
-
return this;
|
|
52
|
-
},
|
|
53
|
-
where(field, value) {
|
|
54
|
-
assert.equal(field, "wi.id");
|
|
55
|
-
assert.equal(value, "91");
|
|
56
|
-
return this;
|
|
57
|
-
},
|
|
58
|
-
async first() {
|
|
59
|
-
return {
|
|
60
|
-
user_id: 55
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
},
|
|
65
|
-
event: {
|
|
66
|
-
entityId: "91"
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
assert.deepEqual(createInviteAudienceQueryResult, [{ userId: "55" }]);
|
|
70
|
-
assert.equal(members?.metadata?.events?.revokeInvite?.[0]?.realtime?.event, "workspace.invites.changed");
|
|
71
|
-
assert.equal(members?.metadata?.events?.revokeInvite?.[1]?.realtime?.event, "users.bootstrap.changed");
|
|
72
|
-
assert.equal(members?.metadata?.events?.revokeInvite?.[1]?.entityId?.({ result: { revokedInviteId: "19" } }), "19");
|
|
73
|
-
assert.equal(members?.metadata?.events?.revokeInvite?.[1]?.realtime?.audience?.preset, "event_scope");
|
|
74
|
-
assert.equal(typeof members?.metadata?.events?.revokeInvite?.[1]?.realtime?.audience?.userQuery, "function");
|
|
75
|
-
|
|
76
|
-
const pendingApp = createAppDouble();
|
|
77
|
-
registerWorkspacePendingInvitations(pendingApp.app);
|
|
78
|
-
const pending = findServiceCall(pendingApp.serviceCalls, "workspaces.pending-invitations.service");
|
|
79
|
-
const acceptInviteEvents = Array.isArray(pending?.metadata?.events?.acceptInviteByToken)
|
|
80
|
-
? pending.metadata.events.acceptInviteByToken
|
|
81
|
-
: [];
|
|
82
|
-
const acceptInviteRealtimeEvents = acceptInviteEvents.map((entry) => entry?.realtime?.event).filter(Boolean);
|
|
83
|
-
assert.ok(acceptInviteRealtimeEvents.includes("workspace.invitations.pending.changed"));
|
|
84
|
-
assert.ok(acceptInviteRealtimeEvents.includes("users.bootstrap.changed"));
|
|
85
|
-
assert.ok(acceptInviteRealtimeEvents.includes("workspaces.changed"));
|
|
86
|
-
assert.ok(acceptInviteRealtimeEvents.includes("workspace.members.changed"));
|
|
87
|
-
assert.ok(acceptInviteRealtimeEvents.includes("workspace.invites.changed"));
|
|
88
|
-
|
|
89
|
-
const acceptedMembersChange = acceptInviteEvents.find(
|
|
90
|
-
(entry) => entry?.realtime?.event === "workspace.members.changed"
|
|
91
|
-
);
|
|
92
|
-
assert.equal(acceptedMembersChange?.entityId?.({ result: { workspaceId: "9" } }), "9");
|
|
93
|
-
assert.deepEqual(
|
|
94
|
-
acceptedMembersChange?.realtime?.audience?.({
|
|
95
|
-
event: {
|
|
96
|
-
entityId: "9"
|
|
97
|
-
}
|
|
98
|
-
}),
|
|
99
|
-
{
|
|
100
|
-
workspaceId: "9"
|
|
101
|
-
}
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
const acceptedInvitesChange = acceptInviteEvents.find(
|
|
105
|
-
(entry) => entry?.realtime?.event === "workspace.invites.changed"
|
|
106
|
-
);
|
|
107
|
-
assert.equal(acceptedInvitesChange?.entityId?.({ result: { workspaceId: "9" } }), "9");
|
|
108
|
-
|
|
109
|
-
const refuseInviteEvents = Array.isArray(pending?.metadata?.events?.refuseInviteByToken)
|
|
110
|
-
? pending.metadata.events.refuseInviteByToken
|
|
111
|
-
: [];
|
|
112
|
-
const refuseInviteRealtimeEvents = refuseInviteEvents.map((entry) => entry?.realtime?.event).filter(Boolean);
|
|
113
|
-
assert.ok(refuseInviteRealtimeEvents.includes("workspace.invitations.pending.changed"));
|
|
114
|
-
assert.ok(refuseInviteRealtimeEvents.includes("users.bootstrap.changed"));
|
|
115
|
-
assert.ok(refuseInviteRealtimeEvents.includes("workspace.invites.changed"));
|
|
116
|
-
});
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import { registerWorkspaceBootstrap } from "../src/server/registerWorkspaceBootstrap.js";
|
|
4
|
-
|
|
5
|
-
function createAppDouble() {
|
|
6
|
-
const singletons = new Map();
|
|
7
|
-
const tags = [];
|
|
8
|
-
|
|
9
|
-
return {
|
|
10
|
-
singletons,
|
|
11
|
-
tags,
|
|
12
|
-
app: {
|
|
13
|
-
singleton(token, factory) {
|
|
14
|
-
singletons.set(token, factory);
|
|
15
|
-
return this;
|
|
16
|
-
},
|
|
17
|
-
tag(token, name) {
|
|
18
|
-
tags.push({ token, name });
|
|
19
|
-
return this;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
test("registerWorkspaceBootstrap resolves the canonical pending invitations service token when enabled", () => {
|
|
26
|
-
const { app, singletons, tags } = createAppDouble();
|
|
27
|
-
registerWorkspaceBootstrap(app);
|
|
28
|
-
|
|
29
|
-
assert.deepEqual(tags, [
|
|
30
|
-
{
|
|
31
|
-
token: "workspaces.core.bootstrap.payloadContributor",
|
|
32
|
-
name: "jskit.runtime.bootstrap.payloadContributors"
|
|
33
|
-
}
|
|
34
|
-
]);
|
|
35
|
-
|
|
36
|
-
const factory = singletons.get("workspaces.core.bootstrap.payloadContributor");
|
|
37
|
-
assert.equal(typeof factory, "function");
|
|
38
|
-
|
|
39
|
-
const resolvedTokens = [];
|
|
40
|
-
factory({
|
|
41
|
-
make(token) {
|
|
42
|
-
resolvedTokens.push(token);
|
|
43
|
-
|
|
44
|
-
if (token === "workspaces.invitations.enabled") {
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
if (token === "workspaces.service") {
|
|
48
|
-
return {
|
|
49
|
-
listWorkspacesForUser() {},
|
|
50
|
-
resolveWorkspaceContextForUserBySlug() {}
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
if (token === "workspaces.pending-invitations.service") {
|
|
54
|
-
return {
|
|
55
|
-
listPendingInvitesForUser() {}
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
if (token === "internal.repository.user-profiles") {
|
|
59
|
-
return {
|
|
60
|
-
findById() {}
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
if (token === "config") {
|
|
64
|
-
return {
|
|
65
|
-
tenancyMode: "workspaces"
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
if (token === "workspaces.tenancy.profile") {
|
|
69
|
-
return {
|
|
70
|
-
mode: "workspaces",
|
|
71
|
-
workspace: {
|
|
72
|
-
enabled: true,
|
|
73
|
-
autoProvision: false,
|
|
74
|
-
allowSelfCreate: false,
|
|
75
|
-
slugPolicy: "user_selected"
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
throw new Error(`Unexpected token: ${token}`);
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
assert.ok(resolvedTokens.includes("workspaces.pending-invitations.service"));
|
|
85
|
-
assert.ok(!resolvedTokens.includes("users.workspace.pending-invitations.service"));
|
|
86
|
-
});
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import { registerWorkspaceDirectory } from "../src/server/workspaceDirectory/registerWorkspaceDirectory.js";
|
|
4
|
-
|
|
5
|
-
function createAppDouble() {
|
|
6
|
-
const actionBatches = [];
|
|
7
|
-
|
|
8
|
-
return {
|
|
9
|
-
actionBatches,
|
|
10
|
-
singleton() {},
|
|
11
|
-
actions(entries) {
|
|
12
|
-
actionBatches.push(Array.isArray(entries) ? entries : [entries]);
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function listActionIds(app) {
|
|
18
|
-
return app.actionBatches.flat().map((entry) => String(entry?.id || ""));
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
test("registerWorkspaceDirectory registers workspace directory actions without resolving runtime tenancy tokens", () => {
|
|
22
|
-
const app = createAppDouble();
|
|
23
|
-
|
|
24
|
-
registerWorkspaceDirectory(app);
|
|
25
|
-
assert.deepEqual(listActionIds(app), [
|
|
26
|
-
"workspace.workspaces.create",
|
|
27
|
-
"workspace.workspaces.list",
|
|
28
|
-
"workspace.workspaces.read",
|
|
29
|
-
"workspace.workspaces.update"
|
|
30
|
-
]);
|
|
31
|
-
});
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import { registerWorkspaceMembers } from "../src/server/workspaceMembers/registerWorkspaceMembers.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 createScopeDouble(bindings = {}) {
|
|
30
|
-
return {
|
|
31
|
-
has(token) {
|
|
32
|
-
return Object.hasOwn(bindings, token);
|
|
33
|
-
},
|
|
34
|
-
make(token) {
|
|
35
|
-
if (!Object.hasOwn(bindings, token)) {
|
|
36
|
-
throw new Error(`Missing test binding: ${token}`);
|
|
37
|
-
}
|
|
38
|
-
const binding = bindings[token];
|
|
39
|
-
return typeof binding === "function" && token !== "appConfig" ? binding(this) : binding;
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
test("registerWorkspaceMembers uses app-owned workspace invite email template from appConfig", async () => {
|
|
45
|
-
const templateCalls = [];
|
|
46
|
-
const mailerCalls = [];
|
|
47
|
-
const fixture = createAppDouble();
|
|
48
|
-
registerWorkspaceMembers(fixture.app);
|
|
49
|
-
const serviceCall = fixture.serviceCalls.find((entry) => entry.token === "workspaces.members.service");
|
|
50
|
-
|
|
51
|
-
const service = serviceCall.factory(createScopeDouble({
|
|
52
|
-
appConfig: {
|
|
53
|
-
workspaceMembers: {
|
|
54
|
-
defaults: {
|
|
55
|
-
inviteExpiresInMs: 60_000
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
roleCatalog: {
|
|
59
|
-
workspace: {
|
|
60
|
-
defaultInviteRole: "member"
|
|
61
|
-
},
|
|
62
|
-
roles: {
|
|
63
|
-
owner: {
|
|
64
|
-
assignable: false,
|
|
65
|
-
permissions: ["*"]
|
|
66
|
-
},
|
|
67
|
-
member: {
|
|
68
|
-
assignable: true,
|
|
69
|
-
permissions: ["workspace.members.view"]
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
workspaceInviteEmailTemplate(payload) {
|
|
74
|
-
templateCalls.push(payload);
|
|
75
|
-
return {
|
|
76
|
-
subject: "Custom invite",
|
|
77
|
-
text: payload.inviteUrl,
|
|
78
|
-
html: null
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
"jskit.env": {
|
|
83
|
-
APP_PUBLIC_URL: "https://app.example.test"
|
|
84
|
-
},
|
|
85
|
-
"workspaces.invitations.enabled": true,
|
|
86
|
-
"workspaces.invite.mailer": {
|
|
87
|
-
async sendWorkspaceInvite(payload) {
|
|
88
|
-
mailerCalls.push(payload);
|
|
89
|
-
return {
|
|
90
|
-
status: "sent",
|
|
91
|
-
message: "sent",
|
|
92
|
-
providerMessageId: "msg-custom"
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
"internal.repository.workspace-memberships": {
|
|
97
|
-
async listActiveByWorkspaceId() {
|
|
98
|
-
return [];
|
|
99
|
-
}
|
|
100
|
-
},
|
|
101
|
-
"internal.repository.workspace-invites": {
|
|
102
|
-
async listPendingByWorkspaceIdWithWorkspace() {
|
|
103
|
-
return [];
|
|
104
|
-
},
|
|
105
|
-
async expirePendingByWorkspaceIdAndEmail() {},
|
|
106
|
-
async insert(payload) {
|
|
107
|
-
return {
|
|
108
|
-
id: "91",
|
|
109
|
-
expiresAt: payload.expiresAt
|
|
110
|
-
};
|
|
111
|
-
},
|
|
112
|
-
async findPendingByIdForWorkspace() {
|
|
113
|
-
return null;
|
|
114
|
-
},
|
|
115
|
-
async revokeById() {}
|
|
116
|
-
}
|
|
117
|
-
}));
|
|
118
|
-
|
|
119
|
-
const result = await service.createInvite(
|
|
120
|
-
{
|
|
121
|
-
id: "7",
|
|
122
|
-
slug: "acme",
|
|
123
|
-
name: "Acme",
|
|
124
|
-
ownerUserId: "9",
|
|
125
|
-
avatarUrl: ""
|
|
126
|
-
},
|
|
127
|
-
{ id: "9", email: "owner@example.com" },
|
|
128
|
-
{
|
|
129
|
-
email: "invitee@example.com",
|
|
130
|
-
roleSid: "member"
|
|
131
|
-
}
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
assert.equal(result.inviteDelivery.providerMessageId, "msg-custom");
|
|
135
|
-
assert.equal(templateCalls.length, 1);
|
|
136
|
-
assert.equal(templateCalls[0].workspace.name, "Acme");
|
|
137
|
-
assert.equal(mailerCalls.length, 1);
|
|
138
|
-
assert.equal(mailerCalls[0].message.subject, "Custom invite");
|
|
139
|
-
assert.match(mailerCalls[0].inviteUrl, /^https:\/\/app\.example\.test\/invite\//);
|
|
140
|
-
});
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import { registerWorkspaceSettings } from "../src/server/workspaceSettings/registerWorkspaceSettings.js";
|
|
4
|
-
|
|
5
|
-
test("registerWorkspaceSettings registers workspace settings service realtime event metadata", () => {
|
|
6
|
-
const singletonBindings = new Map();
|
|
7
|
-
const actionCalls = [];
|
|
8
|
-
const serviceCalls = [];
|
|
9
|
-
|
|
10
|
-
const app = {
|
|
11
|
-
singleton(token, factory) {
|
|
12
|
-
singletonBindings.set(token, factory);
|
|
13
|
-
return this;
|
|
14
|
-
},
|
|
15
|
-
service(token, factory, metadata) {
|
|
16
|
-
serviceCalls.push({
|
|
17
|
-
token,
|
|
18
|
-
factory,
|
|
19
|
-
metadata
|
|
20
|
-
});
|
|
21
|
-
return this;
|
|
22
|
-
},
|
|
23
|
-
actions(definitions) {
|
|
24
|
-
actionCalls.push(definitions);
|
|
25
|
-
return this;
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
registerWorkspaceSettings(app);
|
|
30
|
-
|
|
31
|
-
assert.equal(singletonBindings.has("internal.repository.workspace-settings"), true);
|
|
32
|
-
assert.equal(serviceCalls.length, 1);
|
|
33
|
-
assert.equal(serviceCalls[0].token, "workspaces.settings.service");
|
|
34
|
-
assert.equal(typeof serviceCalls[0].factory, "function");
|
|
35
|
-
assert.equal(serviceCalls[0].metadata?.events?.updateWorkspaceSettings?.[0]?.realtime?.event, "workspace.settings.changed");
|
|
36
|
-
assert.equal(serviceCalls[0].metadata?.events?.updateWorkspaceSettings?.[0]?.realtime?.audience, "event_scope");
|
|
37
|
-
assert.equal(serviceCalls[0].metadata?.events?.updateWorkspaceSettings?.[1]?.realtime?.event, "users.bootstrap.changed");
|
|
38
|
-
assert.equal(serviceCalls[0].metadata?.events?.updateWorkspaceSettings?.[1]?.realtime?.audience, "event_scope");
|
|
39
|
-
assert.equal(actionCalls.length, 1);
|
|
40
|
-
});
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import { AUTH_POLICY_CONTEXT_RESOLVER_TAG } from "@jskit-ai/auth-core/server/authPolicyContextResolverRegistry";
|
|
4
|
-
import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
|
|
5
|
-
import { PROFILE_SYNC_LIFECYCLE_CONTRIBUTOR_TAG } from "@jskit-ai/users-core/server/profileSyncLifecycleContributorRegistry";
|
|
6
|
-
import { createWorkspaceServerScopeSupport } from "../src/server/support/workspaceServerScopeSupport.js";
|
|
7
|
-
import { registerWorkspaceCore } from "../src/server/registerWorkspaceCore.js";
|
|
8
|
-
|
|
9
|
-
test("workspace server scope support exposes the canonical workspace helper surface", async () => {
|
|
10
|
-
const support = createWorkspaceServerScopeSupport();
|
|
11
|
-
|
|
12
|
-
assert.equal(support.available, true);
|
|
13
|
-
assert.equal(typeof support.params?.schema, "object");
|
|
14
|
-
assert.equal(support.params?.mode, "patch");
|
|
15
|
-
assert.deepEqual(
|
|
16
|
-
await validateSchemaPayload(support.params, { workspaceSlug: " ACME " }, {
|
|
17
|
-
phase: "input",
|
|
18
|
-
context: "workspaceServerScopeSupport.params"
|
|
19
|
-
}),
|
|
20
|
-
{
|
|
21
|
-
workspaceSlug: "acme"
|
|
22
|
-
}
|
|
23
|
-
);
|
|
24
|
-
assert.deepEqual(support.buildInputFromRouteParams({ workspaceSlug: " ACME " }), {
|
|
25
|
-
workspaceSlug: "acme"
|
|
26
|
-
});
|
|
27
|
-
assert.deepEqual(
|
|
28
|
-
support.resolveWorkspace(
|
|
29
|
-
{
|
|
30
|
-
requestMeta: {
|
|
31
|
-
resolvedWorkspaceContext: {
|
|
32
|
-
workspace: {
|
|
33
|
-
id: 7,
|
|
34
|
-
slug: "acme"
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
{}
|
|
40
|
-
),
|
|
41
|
-
{
|
|
42
|
-
id: 7,
|
|
43
|
-
slug: "acme"
|
|
44
|
-
}
|
|
45
|
-
);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test("registerWorkspaceCore registers the workspace server scope support token", () => {
|
|
49
|
-
const singletons = new Map();
|
|
50
|
-
const tags = new Map();
|
|
51
|
-
const app = {
|
|
52
|
-
singleton(token, factory) {
|
|
53
|
-
singletons.set(token, factory);
|
|
54
|
-
return this;
|
|
55
|
-
},
|
|
56
|
-
tag(token, tagName) {
|
|
57
|
-
const key = String(tagName || "");
|
|
58
|
-
const list = tags.get(key) || [];
|
|
59
|
-
list.push(String(token || ""));
|
|
60
|
-
tags.set(key, list);
|
|
61
|
-
return this;
|
|
62
|
-
},
|
|
63
|
-
has() {
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
registerWorkspaceCore(app);
|
|
69
|
-
|
|
70
|
-
assert.equal(singletons.has("workspaces.server.scope-support"), true);
|
|
71
|
-
assert.deepEqual(tags.get(AUTH_POLICY_CONTEXT_RESOLVER_TAG), ["workspaces.core.authPolicyContextResolver"]);
|
|
72
|
-
const support = singletons.get("workspaces.server.scope-support")();
|
|
73
|
-
assert.equal(support.available, true);
|
|
74
|
-
assert.equal(typeof support.buildInputFromRouteParams, "function");
|
|
75
|
-
assert.equal(typeof support.resolveWorkspace, "function");
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
test("registerWorkspaceCore ensures personal workspace provisioning on every authenticated profile sync", async () => {
|
|
79
|
-
const singletons = new Map();
|
|
80
|
-
const tags = new Map();
|
|
81
|
-
const app = {
|
|
82
|
-
singleton(token, factory) {
|
|
83
|
-
singletons.set(token, factory);
|
|
84
|
-
return this;
|
|
85
|
-
},
|
|
86
|
-
tag(token, tagName) {
|
|
87
|
-
const key = String(tagName || "");
|
|
88
|
-
const list = tags.get(key) || [];
|
|
89
|
-
list.push(String(token || ""));
|
|
90
|
-
tags.set(key, list);
|
|
91
|
-
return this;
|
|
92
|
-
},
|
|
93
|
-
has() {
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
registerWorkspaceCore(app);
|
|
99
|
-
|
|
100
|
-
assert.deepEqual(tags.get(PROFILE_SYNC_LIFECYCLE_CONTRIBUTOR_TAG), ["workspaces.core.profileSyncLifecycleContributor"]);
|
|
101
|
-
|
|
102
|
-
const lifecycleFactory = singletons.get("workspaces.core.profileSyncLifecycleContributor");
|
|
103
|
-
assert.equal(typeof lifecycleFactory, "function");
|
|
104
|
-
|
|
105
|
-
const calls = [];
|
|
106
|
-
const contributor = lifecycleFactory({
|
|
107
|
-
make(token) {
|
|
108
|
-
if (token === "workspaces.service") {
|
|
109
|
-
return {
|
|
110
|
-
async ensureProvisionedWorkspaceForAuthenticatedUser(profile, options = {}) {
|
|
111
|
-
calls.push({ profile, options });
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
throw new Error(`Unexpected token: ${token}`);
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
await contributor.afterIdentityProfileSynced({
|
|
120
|
-
profile: { id: "7", username: "tonymobily" },
|
|
121
|
-
created: false,
|
|
122
|
-
options: { trx: { id: "tx-1" } }
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
assert.deepEqual(calls, [
|
|
126
|
-
{
|
|
127
|
-
profile: { id: "7", username: "tonymobily" },
|
|
128
|
-
options: { trx: { id: "tx-1" } }
|
|
129
|
-
}
|
|
130
|
-
]);
|
|
131
|
-
});
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|