@jskit-ai/users-core 0.1.48 → 0.1.50

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.
Files changed (87) hide show
  1. package/package.descriptor.mjs +7 -7
  2. package/package.json +7 -17
  3. package/src/server/common/services/authProfileSyncService.js +28 -7
  4. package/src/server/common/support/realtimeServiceEvents.js +1 -59
  5. package/src/server/profileSyncLifecycleContributorRegistry.js +56 -0
  6. package/src/server/registerUsersBootstrap.js +0 -1
  7. package/src/server/registerUsersCore.js +2 -14
  8. package/src/server/usersBootstrapContributor.js +2 -64
  9. package/src/shared/index.js +2 -99
  10. package/src/shared/settings.js +1 -119
  11. package/test/authProfileSyncService.test.js +19 -10
  12. package/test/registerServiceRealtimeEvents.test.js +0 -86
  13. package/test/registerUsersCore.test.js +6 -15
  14. package/test/repositoryContracts.test.js +1 -9
  15. package/test/resourcesCanonical.test.js +0 -16
  16. package/test/settingsFieldRegistriesSingleton.test.js +0 -5
  17. package/test/usersBootstrapContributor.test.js +2 -26
  18. package/test/usersRouteResources.test.js +0 -16
  19. package/src/server/UsersWorkspacesServiceProvider.js +0 -44
  20. package/src/server/common/contributors/workspaceActionContextContributor.js +0 -88
  21. package/src/server/common/contributors/workspaceAuthPolicyContextResolver.js +0 -34
  22. package/src/server/common/contributors/workspaceRouteVisibilityResolver.js +0 -78
  23. package/src/server/common/formatters/workspaceFormatter.js +0 -53
  24. package/src/server/common/repositories/workspaceInvitesRepository.js +0 -208
  25. package/src/server/common/repositories/workspaceMembershipsRepository.js +0 -190
  26. package/src/server/common/repositories/workspacesRepository.js +0 -202
  27. package/src/server/common/services/workspaceContextService.js +0 -281
  28. package/src/server/common/support/workspaceRoutePaths.js +0 -17
  29. package/src/server/common/validators/routeParamsValidator.js +0 -62
  30. package/src/server/registerWorkspaceBootstrap.js +0 -27
  31. package/src/server/registerWorkspaceCore.js +0 -73
  32. package/src/server/registerWorkspaceRepositories.js +0 -26
  33. package/src/server/support/resolveWorkspace.js +0 -16
  34. package/src/server/support/workspaceActionSurfaces.js +0 -118
  35. package/src/server/support/workspaceInvitationsPolicy.js +0 -45
  36. package/src/server/support/workspaceRouteInput.js +0 -22
  37. package/src/server/workspaceBootstrapContributor.js +0 -212
  38. package/src/server/workspaceDirectory/bootWorkspaceDirectoryRoutes.js +0 -133
  39. package/src/server/workspaceDirectory/registerWorkspaceDirectory.js +0 -19
  40. package/src/server/workspaceDirectory/workspaceDirectoryActions.js +0 -133
  41. package/src/server/workspaceMembers/bootWorkspaceMembers.js +0 -236
  42. package/src/server/workspaceMembers/registerWorkspaceMembers.js +0 -108
  43. package/src/server/workspaceMembers/workspaceMembersActions.js +0 -186
  44. package/src/server/workspaceMembers/workspaceMembersService.js +0 -222
  45. package/src/server/workspacePendingInvitations/bootWorkspacePendingInvitations.js +0 -62
  46. package/src/server/workspacePendingInvitations/registerWorkspacePendingInvitations.js +0 -119
  47. package/src/server/workspacePendingInvitations/workspacePendingInvitationsActions.js +0 -74
  48. package/src/server/workspacePendingInvitations/workspacePendingInvitationsService.js +0 -138
  49. package/src/server/workspaceSettings/bootWorkspaceSettings.js +0 -76
  50. package/src/server/workspaceSettings/registerWorkspaceSettings.js +0 -62
  51. package/src/server/workspaceSettings/workspaceSettingsActions.js +0 -72
  52. package/src/server/workspaceSettings/workspaceSettingsRepository.js +0 -154
  53. package/src/server/workspaceSettings/workspaceSettingsService.js +0 -66
  54. package/src/shared/resources/workspaceMembersResource.js +0 -354
  55. package/src/shared/resources/workspacePendingInvitationsResource.js +0 -82
  56. package/src/shared/resources/workspaceResource.js +0 -176
  57. package/src/shared/resources/workspaceSettingsFields.js +0 -59
  58. package/src/shared/resources/workspaceSettingsResource.js +0 -169
  59. package/src/shared/roles.js +0 -161
  60. package/src/shared/support/usersApiPaths.js +0 -43
  61. package/src/shared/support/usersVisibility.js +0 -42
  62. package/src/shared/support/workspacePathModel.js +0 -145
  63. package/src/shared/tenancyMode.js +0 -35
  64. package/src/shared/tenancyProfile.js +0 -73
  65. package/test/registerWorkspaceDirectory.test.js +0 -31
  66. package/test/registerWorkspaceSettings.test.js +0 -40
  67. package/test/roles.test.js +0 -159
  68. package/test/tenancyProfile.test.js +0 -67
  69. package/test/usersApiPaths.test.js +0 -49
  70. package/test/usersRouteValidators.test.js +0 -49
  71. package/test/usersVisibility.test.js +0 -27
  72. package/test/workspaceActionContextContributor.test.js +0 -344
  73. package/test/workspaceActionSurfaces.test.js +0 -85
  74. package/test/workspaceAuthPolicyContextResolver.test.js +0 -119
  75. package/test/workspaceBootstrapContributor.test.js +0 -154
  76. package/test/workspaceInvitationsPolicy.test.js +0 -71
  77. package/test/workspaceInvitesRepository.test.js +0 -111
  78. package/test/workspaceMembersService.test.js +0 -398
  79. package/test/workspacePathModel.test.js +0 -93
  80. package/test/workspacePendingInvitationsResource.test.js +0 -38
  81. package/test/workspacePendingInvitationsService.test.js +0 -151
  82. package/test/workspaceRouteVisibilityResolver.test.js +0 -83
  83. package/test/workspaceService.test.js +0 -546
  84. package/test/workspaceSettingsActions.test.js +0 -52
  85. package/test/workspaceSettingsRepository.test.js +0 -202
  86. package/test/workspaceSettingsResource.test.js +0 -169
  87. package/test/workspaceSettingsService.test.js +0 -140
@@ -1,154 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { createWorkspaceBootstrapContributor } from "../src/server/workspaceBootstrapContributor.js";
4
-
5
- function createAuthenticatedProfile(overrides = {}) {
6
- return {
7
- id: "7",
8
- authProvider: "local",
9
- authProviderUserSid: "user-7",
10
- username: "tester",
11
- displayName: "Test User",
12
- email: "test@example.com",
13
- ...overrides
14
- };
15
- }
16
-
17
- test("workspace bootstrap contributor passes actor context to pending invites service", async () => {
18
- const profile = createAuthenticatedProfile();
19
- const pendingServiceCalls = [];
20
- const contributor = createWorkspaceBootstrapContributor({
21
- workspaceService: {
22
- async listWorkspacesForUser() {
23
- return [];
24
- },
25
- async resolveWorkspaceContextForUserBySlug() {
26
- return null;
27
- }
28
- },
29
- workspacePendingInvitationsService: {
30
- async listPendingInvitesForUser(user, options = {}) {
31
- pendingServiceCalls.push({
32
- user,
33
- options
34
- });
35
- return [];
36
- }
37
- },
38
- usersRepository: {
39
- async findById() {
40
- return profile;
41
- }
42
- },
43
- workspaceInvitationsEnabled: true,
44
- appConfig: {
45
- tenancyMode: "workspaces"
46
- }
47
- });
48
-
49
- await contributor.contribute({
50
- payload: {
51
- session: {
52
- authenticated: true,
53
- userId: profile.id
54
- }
55
- }
56
- });
57
-
58
- assert.equal(pendingServiceCalls.length, 1);
59
- assert.equal(pendingServiceCalls[0].user.id, profile.id);
60
- assert.equal(pendingServiceCalls[0].options?.context?.actor?.id, profile.id);
61
- });
62
-
63
- test("workspace bootstrap contributor resolves workspace slug from bootstrap query", async () => {
64
- const profile = createAuthenticatedProfile();
65
- const calls = [];
66
- const contributor = createWorkspaceBootstrapContributor({
67
- workspaceService: {
68
- async listWorkspacesForUser() {
69
- return [];
70
- },
71
- async resolveWorkspaceContextForUserBySlug(_user, workspaceSlug) {
72
- calls.push(workspaceSlug);
73
- return null;
74
- }
75
- },
76
- workspacePendingInvitationsService: {
77
- async listPendingInvitesForUser() {
78
- return [];
79
- }
80
- },
81
- usersRepository: {
82
- async findById() {
83
- return profile;
84
- }
85
- },
86
- workspaceInvitationsEnabled: true,
87
- appConfig: {
88
- tenancyMode: "workspaces"
89
- }
90
- });
91
-
92
- const payload = await contributor.contribute({
93
- query: {
94
- workspaceSlug: " AcMe "
95
- },
96
- payload: {
97
- session: {
98
- authenticated: true,
99
- userId: profile.id
100
- }
101
- }
102
- });
103
-
104
- assert.deepEqual(calls, ["acme"]);
105
- assert.deepEqual(payload.requestedWorkspace, {
106
- slug: "acme",
107
- status: "resolved"
108
- });
109
- });
110
-
111
- test("workspace bootstrap contributor reports unauthenticated requested workspace without generic bootstrap work", async () => {
112
- const contributor = createWorkspaceBootstrapContributor({
113
- workspaceService: {
114
- async listWorkspacesForUser() {
115
- assert.fail("listWorkspacesForUser should not run for unauthenticated payloads");
116
- },
117
- async resolveWorkspaceContextForUserBySlug() {
118
- assert.fail("resolveWorkspaceContextForUserBySlug should not run for unauthenticated payloads");
119
- }
120
- },
121
- workspacePendingInvitationsService: {
122
- async listPendingInvitesForUser() {
123
- assert.fail("listPendingInvitesForUser should not run for unauthenticated payloads");
124
- }
125
- },
126
- usersRepository: {
127
- async findById() {
128
- assert.fail("findById should not run for unauthenticated payloads");
129
- }
130
- },
131
- workspaceInvitationsEnabled: true,
132
- appConfig: {
133
- tenancyMode: "workspaces"
134
- }
135
- });
136
-
137
- const payload = await contributor.contribute({
138
- query: {
139
- workspaceSlug: "AcMe"
140
- },
141
- payload: {
142
- session: {
143
- authenticated: false
144
- }
145
- }
146
- });
147
-
148
- assert.deepEqual(payload, {
149
- requestedWorkspace: {
150
- slug: "acme",
151
- status: "unauthenticated"
152
- }
153
- });
154
- });
@@ -1,71 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { resolveWorkspaceInvitationsPolicy } from "../src/server/support/workspaceInvitationsPolicy.js";
4
-
5
- test("workspace invitations policy enables invitations by default in personal mode", () => {
6
- const policy = resolveWorkspaceInvitationsPolicy({
7
- appConfig: {},
8
- tenancyProfile: {
9
- mode: "personal",
10
- workspace: {
11
- enabled: true
12
- }
13
- }
14
- });
15
-
16
- assert.equal(policy.enabled, true);
17
- assert.equal(policy.allowInPersonalMode, true);
18
- });
19
-
20
- test("workspace invitations policy disables invitations in personal mode when explicitly configured", () => {
21
- const policy = resolveWorkspaceInvitationsPolicy({
22
- appConfig: {
23
- workspaceInvitations: {
24
- allowInPersonalMode: false
25
- }
26
- },
27
- tenancyProfile: {
28
- mode: "personal",
29
- workspace: {
30
- enabled: true
31
- }
32
- }
33
- });
34
-
35
- assert.equal(policy.enabled, false);
36
- assert.equal(policy.allowInPersonalMode, false);
37
- });
38
-
39
- test("workspace invitations policy disables invitations when workspace mode is disabled", () => {
40
- const policy = resolveWorkspaceInvitationsPolicy({
41
- appConfig: {},
42
- tenancyProfile: {
43
- mode: "none",
44
- workspace: {
45
- enabled: false
46
- }
47
- }
48
- });
49
-
50
- assert.equal(policy.enabled, false);
51
- assert.equal(policy.workspaceEnabled, false);
52
- });
53
-
54
- test("workspace invitations policy disables invitations when app config disables feature", () => {
55
- const policy = resolveWorkspaceInvitationsPolicy({
56
- appConfig: {
57
- workspaceInvitations: {
58
- enabled: false,
59
- allowInPersonalMode: true
60
- }
61
- },
62
- tenancyProfile: {
63
- mode: "workspace",
64
- workspace: {
65
- enabled: true
66
- }
67
- }
68
- });
69
-
70
- assert.equal(policy.enabled, false);
71
- });
@@ -1,111 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { createRepository } from "../src/server/common/repositories/workspaceInvitesRepository.js";
4
-
5
- function createKnexStub() {
6
- const state = {
7
- insertPayload: null
8
- };
9
-
10
- const row = {
11
- id: 1,
12
- workspace_id: 1,
13
- email: "invitee@example.com",
14
- role_sid: "member",
15
- status: "pending",
16
- token_hash: "hash",
17
- invited_by_user_id: 1,
18
- expires_at: "2026-03-16 00:26:35.709",
19
- accepted_at: null,
20
- revoked_at: null,
21
- created_at: "2026-03-09 00:26:35.710",
22
- updated_at: "2026-03-09 00:26:35.710"
23
- };
24
-
25
- function tableBuilder(tableName) {
26
- assert.equal(tableName, "workspace_invites");
27
- return {
28
- insert(payload) {
29
- state.insertPayload = payload;
30
- return Promise.resolve([1]);
31
- },
32
- where(criteria) {
33
- assert.equal(typeof criteria, "object");
34
- return {
35
- first() {
36
- return Promise.resolve({ ...row });
37
- },
38
- orderBy() {
39
- return {
40
- first() {
41
- return Promise.resolve({ ...row });
42
- }
43
- };
44
- }
45
- };
46
- }
47
- };
48
- }
49
-
50
- return { knexStub: tableBuilder, state };
51
- }
52
-
53
- test("workspaceInvitesRepository.insert normalizes expiresAt ISO input to database datetime", async () => {
54
- const { knexStub, state } = createKnexStub();
55
- const repository = createRepository(knexStub);
56
-
57
- await repository.insert({
58
- workspaceId: "1",
59
- email: "invitee@example.com",
60
- roleSid: "member",
61
- status: "pending",
62
- tokenHash: "hash",
63
- invitedByUserId: "1",
64
- expiresAt: "2026-03-16T00:26:35.709Z"
65
- });
66
-
67
- assert.equal(state.insertPayload.expires_at, "2026-03-16 00:26:35.709");
68
- });
69
-
70
- test("workspaceInvitesRepository.findPendingByTokenHash reads from invites table without workspace join", async () => {
71
- const calls = {
72
- tableName: "",
73
- whereCriteria: null
74
- };
75
- const row = {
76
- id: 44,
77
- workspace_id: 9,
78
- email: "invitee@example.com",
79
- role_sid: "member",
80
- status: "pending",
81
- token_hash: "hash-token",
82
- invited_by_user_id: 1,
83
- expires_at: "2030-01-01 00:00:00.000",
84
- accepted_at: null,
85
- revoked_at: null,
86
- created_at: "2026-03-09 00:26:35.710",
87
- updated_at: "2026-03-09 00:26:35.710"
88
- };
89
-
90
- const repository = createRepository((tableName) => {
91
- calls.tableName = String(tableName || "");
92
- return {
93
- where(criteria) {
94
- calls.whereCriteria = criteria;
95
- return this;
96
- },
97
- first() {
98
- return Promise.resolve({ ...row });
99
- }
100
- };
101
- });
102
-
103
- const invite = await repository.findPendingByTokenHash("hash-token");
104
- assert.equal(calls.tableName, "workspace_invites");
105
- assert.deepEqual(calls.whereCriteria, {
106
- token_hash: "hash-token",
107
- status: "pending"
108
- });
109
- assert.equal(invite?.workspaceId, "9");
110
- assert.equal(invite?.workspaceSlug, undefined);
111
- });