@jskit-ai/users-core 0.1.47 → 0.1.49

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 (104) hide show
  1. package/package.descriptor.mjs +9 -46
  2. package/package.json +8 -19
  3. package/src/server/UsersCoreServiceProvider.js +0 -4
  4. package/src/server/common/registerCommonRepositories.js +0 -5
  5. package/src/server/common/services/authProfileSyncService.js +28 -7
  6. package/src/server/common/support/realtimeServiceEvents.js +1 -59
  7. package/src/server/profileSyncLifecycleContributorRegistry.js +56 -0
  8. package/src/server/registerUsersBootstrap.js +1 -3
  9. package/src/server/registerUsersCore.js +2 -14
  10. package/src/server/usersBootstrapContributor.js +10 -85
  11. package/src/shared/index.js +2 -99
  12. package/src/shared/settings.js +1 -119
  13. package/templates/migrations/users_core_generic_initial.cjs +0 -16
  14. package/test/authProfileSyncService.test.js +19 -10
  15. package/test/registerServiceRealtimeEvents.test.js +0 -94
  16. package/test/registerUsersCore.test.js +6 -19
  17. package/test/repositoryContracts.test.js +1 -11
  18. package/test/resourcesCanonical.test.js +1 -19
  19. package/test/settingsFieldRegistriesSingleton.test.js +0 -10
  20. package/test/usersBootstrapContributor.test.js +20 -38
  21. package/test/usersRouteRequestInputValidator.test.js +2 -43
  22. package/test/usersRouteResources.test.js +2 -20
  23. package/test-support/registerDefaultSettingsFields.js +0 -1
  24. package/src/server/UsersWorkspacesServiceProvider.js +0 -44
  25. package/src/server/common/contributors/workspaceActionContextContributor.js +0 -88
  26. package/src/server/common/contributors/workspaceAuthPolicyContextResolver.js +0 -34
  27. package/src/server/common/contributors/workspaceRouteVisibilityResolver.js +0 -78
  28. package/src/server/common/formatters/workspaceFormatter.js +0 -53
  29. package/src/server/common/repositories/workspaceInvitesRepository.js +0 -208
  30. package/src/server/common/repositories/workspaceMembershipsRepository.js +0 -190
  31. package/src/server/common/repositories/workspacesRepository.js +0 -202
  32. package/src/server/common/services/workspaceContextService.js +0 -281
  33. package/src/server/common/support/workspaceRoutePaths.js +0 -17
  34. package/src/server/common/validators/routeParamsValidator.js +0 -62
  35. package/src/server/consoleSettings/bootConsoleSettingsRoutes.js +0 -63
  36. package/src/server/consoleSettings/consoleService.js +0 -36
  37. package/src/server/consoleSettings/consoleSettingsActions.js +0 -55
  38. package/src/server/consoleSettings/consoleSettingsRepository.js +0 -115
  39. package/src/server/consoleSettings/consoleSettingsService.js +0 -40
  40. package/src/server/consoleSettings/registerConsoleSettings.js +0 -56
  41. package/src/server/registerWorkspaceBootstrap.js +0 -27
  42. package/src/server/registerWorkspaceCore.js +0 -73
  43. package/src/server/registerWorkspaceRepositories.js +0 -26
  44. package/src/server/support/resolveWorkspace.js +0 -16
  45. package/src/server/support/workspaceActionSurfaces.js +0 -135
  46. package/src/server/support/workspaceInvitationsPolicy.js +0 -45
  47. package/src/server/support/workspaceRouteInput.js +0 -22
  48. package/src/server/workspaceBootstrapContributor.js +0 -211
  49. package/src/server/workspaceDirectory/bootWorkspaceDirectoryRoutes.js +0 -133
  50. package/src/server/workspaceDirectory/registerWorkspaceDirectory.js +0 -19
  51. package/src/server/workspaceDirectory/workspaceDirectoryActions.js +0 -133
  52. package/src/server/workspaceMembers/bootWorkspaceMembers.js +0 -236
  53. package/src/server/workspaceMembers/registerWorkspaceMembers.js +0 -108
  54. package/src/server/workspaceMembers/workspaceMembersActions.js +0 -186
  55. package/src/server/workspaceMembers/workspaceMembersService.js +0 -222
  56. package/src/server/workspacePendingInvitations/bootWorkspacePendingInvitations.js +0 -62
  57. package/src/server/workspacePendingInvitations/registerWorkspacePendingInvitations.js +0 -119
  58. package/src/server/workspacePendingInvitations/workspacePendingInvitationsActions.js +0 -74
  59. package/src/server/workspacePendingInvitations/workspacePendingInvitationsService.js +0 -138
  60. package/src/server/workspaceSettings/bootWorkspaceSettings.js +0 -76
  61. package/src/server/workspaceSettings/registerWorkspaceSettings.js +0 -62
  62. package/src/server/workspaceSettings/workspaceSettingsActions.js +0 -72
  63. package/src/server/workspaceSettings/workspaceSettingsRepository.js +0 -154
  64. package/src/server/workspaceSettings/workspaceSettingsService.js +0 -66
  65. package/src/shared/resources/consoleSettingsFields.js +0 -54
  66. package/src/shared/resources/consoleSettingsResource.js +0 -119
  67. package/src/shared/resources/workspaceMembersResource.js +0 -354
  68. package/src/shared/resources/workspacePendingInvitationsResource.js +0 -82
  69. package/src/shared/resources/workspaceResource.js +0 -176
  70. package/src/shared/resources/workspaceSettingsFields.js +0 -59
  71. package/src/shared/resources/workspaceSettingsResource.js +0 -169
  72. package/src/shared/roles.js +0 -161
  73. package/src/shared/support/usersApiPaths.js +0 -43
  74. package/src/shared/support/usersVisibility.js +0 -42
  75. package/src/shared/support/workspacePathModel.js +0 -145
  76. package/src/shared/tenancyMode.js +0 -35
  77. package/src/shared/tenancyProfile.js +0 -73
  78. package/templates/migrations/users_core_console_owner.cjs +0 -37
  79. package/templates/packages/main/src/shared/resources/consoleSettingsFields.js +0 -11
  80. package/test/consoleService.test.js +0 -57
  81. package/test/consoleSettingsService.test.js +0 -86
  82. package/test/registerWorkspaceDirectory.test.js +0 -31
  83. package/test/registerWorkspaceSettings.test.js +0 -40
  84. package/test/roles.test.js +0 -159
  85. package/test/tenancyProfile.test.js +0 -67
  86. package/test/usersApiPaths.test.js +0 -49
  87. package/test/usersRouteValidators.test.js +0 -49
  88. package/test/usersVisibility.test.js +0 -27
  89. package/test/workspaceActionContextContributor.test.js +0 -344
  90. package/test/workspaceActionSurfaces.test.js +0 -105
  91. package/test/workspaceAuthPolicyContextResolver.test.js +0 -119
  92. package/test/workspaceBootstrapContributor.test.js +0 -154
  93. package/test/workspaceInvitationsPolicy.test.js +0 -71
  94. package/test/workspaceInvitesRepository.test.js +0 -111
  95. package/test/workspaceMembersService.test.js +0 -398
  96. package/test/workspacePathModel.test.js +0 -93
  97. package/test/workspacePendingInvitationsResource.test.js +0 -38
  98. package/test/workspacePendingInvitationsService.test.js +0 -151
  99. package/test/workspaceRouteVisibilityResolver.test.js +0 -83
  100. package/test/workspaceService.test.js +0 -546
  101. package/test/workspaceSettingsActions.test.js +0 -52
  102. package/test/workspaceSettingsRepository.test.js +0 -202
  103. package/test/workspaceSettingsResource.test.js +0 -169
  104. package/test/workspaceSettingsService.test.js +0 -140
@@ -1,119 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { createWorkspaceAuthPolicyContextResolver } from "../src/server/common/contributors/workspaceAuthPolicyContextResolver.js";
4
-
5
- test("workspace auth policy context resolver returns empty context when policy does not require workspace context", async () => {
6
- const resolver = createWorkspaceAuthPolicyContextResolver({
7
- workspaceService: {
8
- async resolveWorkspaceContextForUserBySlug() {
9
- throw new Error("must not be called");
10
- }
11
- }
12
- });
13
-
14
- const resolved = await resolver({
15
- request: {
16
- params: {
17
- workspaceSlug: "acme"
18
- }
19
- },
20
- actor: {
21
- id: 7
22
- },
23
- meta: {
24
- contextPolicy: "none",
25
- permission: ""
26
- }
27
- });
28
-
29
- assert.deepEqual(resolved, {});
30
- });
31
-
32
- test("workspace auth policy context resolver resolves workspace context from users workspace service", async () => {
33
- const calls = [];
34
- const resolver = createWorkspaceAuthPolicyContextResolver({
35
- workspaceService: {
36
- async resolveWorkspaceContextForUserBySlug(actor, workspaceSlug, options) {
37
- calls.push({
38
- actor,
39
- workspaceSlug,
40
- options
41
- });
42
- return {
43
- workspace: {
44
- id: 11,
45
- slug: workspaceSlug
46
- },
47
- membership: {
48
- roleSid: "owner"
49
- },
50
- permissions: ["projects.read"]
51
- };
52
- }
53
- }
54
- });
55
-
56
- const request = {
57
- params: {
58
- workspaceSlug: "ACME"
59
- }
60
- };
61
- const actor = {
62
- id: 7
63
- };
64
-
65
- const resolved = await resolver({
66
- request,
67
- actor,
68
- meta: {
69
- contextPolicy: "required"
70
- }
71
- });
72
-
73
- assert.deepEqual(calls, [
74
- {
75
- actor,
76
- workspaceSlug: "acme",
77
- options: {
78
- request
79
- }
80
- }
81
- ]);
82
- assert.deepEqual(resolved, {
83
- workspace: {
84
- id: 11,
85
- slug: "acme"
86
- },
87
- membership: {
88
- roleSid: "owner"
89
- },
90
- permissions: ["projects.read"]
91
- });
92
- });
93
-
94
- test("workspace auth policy context resolver skips workspace lookup when workspace slug is absent", async () => {
95
- let called = false;
96
- const resolver = createWorkspaceAuthPolicyContextResolver({
97
- workspaceService: {
98
- async resolveWorkspaceContextForUserBySlug() {
99
- called = true;
100
- return {};
101
- }
102
- }
103
- });
104
-
105
- const resolved = await resolver({
106
- request: {
107
- params: {}
108
- },
109
- actor: {
110
- id: 7
111
- },
112
- meta: {
113
- contextPolicy: "required"
114
- }
115
- });
116
-
117
- assert.equal(called, false);
118
- assert.deepEqual(resolved, {});
119
- });
@@ -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
- });