@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,107 +1,10 @@
1
- import {
2
- OWNER_ROLE_ID,
3
- ADMIN_ROLE_ID,
4
- MEMBER_ROLE_ID,
5
- resolveRolePermissions,
6
- listRoleDescriptors,
7
- hasPermission
8
- } from "./roles.js";
9
-
10
- import {
11
- DEFAULT_WORKSPACE_DARK_PALETTE,
12
- DEFAULT_WORKSPACE_LIGHT_PALETTE,
13
- DEFAULT_WORKSPACE_COLOR,
14
- DEFAULT_USER_SETTINGS,
15
- coerceWorkspaceColor,
16
- coerceWorkspaceThemeColor,
17
- coerceWorkspaceSecondaryColor,
18
- coerceWorkspaceSurfaceColor,
19
- coerceWorkspaceSurfaceVariantColor,
20
- normalizeWorkspaceHexColor,
21
- normalizeWorkspaceThemeMode,
22
- resolveWorkspaceThemeDefaultPalette,
23
- resolveWorkspaceThemePalettes,
24
- WORKSPACE_THEME_MODE_DARK,
25
- WORKSPACE_THEME_MODE_LIGHT,
26
- resolveWorkspaceThemePalette
27
- } from "./settings.js";
28
- import {
29
- TENANCY_MODE_NONE,
30
- TENANCY_MODE_PERSONAL,
31
- TENANCY_MODE_WORKSPACES,
32
- normalizeTenancyMode,
33
- WORKSPACE_SLUG_POLICY_NONE,
34
- WORKSPACE_SLUG_POLICY_IMMUTABLE_USERNAME,
35
- WORKSPACE_SLUG_POLICY_USER_SELECTED,
36
- resolveTenancyProfile,
37
- isWorkspacesTenancyMode
38
- } from "./tenancyProfile.js";
1
+ import { DEFAULT_USER_SETTINGS } from "./settings.js";
39
2
 
40
3
  const USERS_SHARED_API = Object.freeze({
41
- OWNER_ROLE_ID,
42
- ADMIN_ROLE_ID,
43
- MEMBER_ROLE_ID,
44
- resolveRolePermissions,
45
- listRoleDescriptors,
46
- hasPermission,
47
- DEFAULT_WORKSPACE_LIGHT_PALETTE,
48
- DEFAULT_WORKSPACE_DARK_PALETTE,
49
- DEFAULT_WORKSPACE_COLOR,
50
- DEFAULT_USER_SETTINGS,
51
- coerceWorkspaceColor,
52
- coerceWorkspaceThemeColor,
53
- coerceWorkspaceSecondaryColor,
54
- coerceWorkspaceSurfaceColor,
55
- coerceWorkspaceSurfaceVariantColor,
56
- WORKSPACE_THEME_MODE_LIGHT,
57
- WORKSPACE_THEME_MODE_DARK,
58
- normalizeWorkspaceThemeMode,
59
- resolveWorkspaceThemeDefaultPalette,
60
- resolveWorkspaceThemePalettes,
61
- normalizeWorkspaceHexColor,
62
- resolveWorkspaceThemePalette,
63
- TENANCY_MODE_NONE,
64
- TENANCY_MODE_PERSONAL,
65
- TENANCY_MODE_WORKSPACES,
66
- normalizeTenancyMode,
67
- WORKSPACE_SLUG_POLICY_NONE,
68
- WORKSPACE_SLUG_POLICY_IMMUTABLE_USERNAME,
69
- WORKSPACE_SLUG_POLICY_USER_SELECTED,
70
- resolveTenancyProfile,
71
- isWorkspacesTenancyMode
4
+ DEFAULT_USER_SETTINGS
72
5
  });
73
6
 
74
7
  export {
75
- OWNER_ROLE_ID,
76
- ADMIN_ROLE_ID,
77
- MEMBER_ROLE_ID,
78
- resolveRolePermissions,
79
- listRoleDescriptors,
80
- hasPermission,
81
- DEFAULT_WORKSPACE_LIGHT_PALETTE,
82
- DEFAULT_WORKSPACE_DARK_PALETTE,
83
- DEFAULT_WORKSPACE_COLOR,
84
8
  DEFAULT_USER_SETTINGS,
85
- coerceWorkspaceColor,
86
- coerceWorkspaceThemeColor,
87
- coerceWorkspaceSecondaryColor,
88
- coerceWorkspaceSurfaceColor,
89
- coerceWorkspaceSurfaceVariantColor,
90
- WORKSPACE_THEME_MODE_LIGHT,
91
- WORKSPACE_THEME_MODE_DARK,
92
- normalizeWorkspaceThemeMode,
93
- resolveWorkspaceThemeDefaultPalette,
94
- resolveWorkspaceThemePalettes,
95
- normalizeWorkspaceHexColor,
96
- resolveWorkspaceThemePalette,
97
- TENANCY_MODE_NONE,
98
- TENANCY_MODE_PERSONAL,
99
- TENANCY_MODE_WORKSPACES,
100
- normalizeTenancyMode,
101
- WORKSPACE_SLUG_POLICY_NONE,
102
- WORKSPACE_SLUG_POLICY_IMMUTABLE_USERNAME,
103
- WORKSPACE_SLUG_POLICY_USER_SELECTED,
104
- resolveTenancyProfile,
105
- isWorkspacesTenancyMode,
106
9
  USERS_SHARED_API
107
10
  };
@@ -1,23 +1,3 @@
1
- const WORKSPACE_THEME_MODE_LIGHT = "light";
2
- const WORKSPACE_THEME_MODE_DARK = "dark";
3
- const HEX_COLOR_PATTERN = /^#[0-9A-Fa-f]{6}$/;
4
-
5
- const DEFAULT_WORKSPACE_LIGHT_PALETTE = Object.freeze({
6
- color: "#1867C0",
7
- secondaryColor: "#48A9A6",
8
- surfaceColor: "#FFFFFF",
9
- surfaceVariantColor: "#424242"
10
- });
11
-
12
- const DEFAULT_WORKSPACE_DARK_PALETTE = Object.freeze({
13
- color: "#2196F3",
14
- secondaryColor: "#54B6B2",
15
- surfaceColor: "#212121",
16
- surfaceVariantColor: "#C8C8C8"
17
- });
18
-
19
- const DEFAULT_WORKSPACE_COLOR = DEFAULT_WORKSPACE_LIGHT_PALETTE.color;
20
-
21
1
  const DEFAULT_USER_SETTINGS = Object.freeze({
22
2
  theme: "system",
23
3
  locale: "en",
@@ -32,104 +12,6 @@ const DEFAULT_USER_SETTINGS = Object.freeze({
32
12
  passwordSignInEnabled: true,
33
13
  passwordSetupRequired: false
34
14
  });
35
-
36
- function normalizeWorkspaceHexColor(value) {
37
- const normalized = String(value || "").trim();
38
- if (!HEX_COLOR_PATTERN.test(normalized)) {
39
- return "";
40
- }
41
- return normalized.toUpperCase();
42
- }
43
-
44
- function coerceWorkspaceColor(value) {
45
- return normalizeWorkspaceHexColor(value) || DEFAULT_WORKSPACE_COLOR;
46
- }
47
-
48
- function normalizeWorkspaceThemeMode(value = "") {
49
- const normalized = String(value || "").trim().toLowerCase();
50
- if (normalized === WORKSPACE_THEME_MODE_DARK) {
51
- return WORKSPACE_THEME_MODE_DARK;
52
- }
53
- return WORKSPACE_THEME_MODE_LIGHT;
54
- }
55
-
56
- function resolveWorkspaceThemeDefaultPalette(mode = WORKSPACE_THEME_MODE_LIGHT) {
57
- const normalizedMode = normalizeWorkspaceThemeMode(mode);
58
- return normalizedMode === WORKSPACE_THEME_MODE_DARK
59
- ? DEFAULT_WORKSPACE_DARK_PALETTE
60
- : DEFAULT_WORKSPACE_LIGHT_PALETTE;
61
- }
62
-
63
- function coerceWorkspaceThemeColor(value, fallbackColor = DEFAULT_WORKSPACE_COLOR) {
64
- return normalizeWorkspaceHexColor(value) || normalizeWorkspaceHexColor(fallbackColor) || DEFAULT_WORKSPACE_COLOR;
65
- }
66
-
67
- function coerceWorkspaceSecondaryColor(value, { mode = WORKSPACE_THEME_MODE_LIGHT } = {}) {
68
- return coerceWorkspaceThemeColor(value, resolveWorkspaceThemeDefaultPalette(mode).secondaryColor);
69
- }
70
-
71
- function coerceWorkspaceSurfaceColor(value, { mode = WORKSPACE_THEME_MODE_LIGHT } = {}) {
72
- return coerceWorkspaceThemeColor(value, resolveWorkspaceThemeDefaultPalette(mode).surfaceColor);
73
- }
74
-
75
- function coerceWorkspaceSurfaceVariantColor(value, { mode = WORKSPACE_THEME_MODE_LIGHT } = {}) {
76
- return coerceWorkspaceThemeColor(value, resolveWorkspaceThemeDefaultPalette(mode).surfaceVariantColor);
77
- }
78
-
79
- function resolveWorkspaceThemePalette(input = {}, { mode = WORKSPACE_THEME_MODE_LIGHT } = {}) {
80
- const source = input && typeof input === "object" ? input : {};
81
- const normalizedMode = normalizeWorkspaceThemeMode(mode);
82
- const paletteDefaults = resolveWorkspaceThemeDefaultPalette(normalizedMode);
83
-
84
- if (normalizedMode === WORKSPACE_THEME_MODE_DARK) {
85
- return Object.freeze({
86
- color: coerceWorkspaceThemeColor(source.darkPrimaryColor, paletteDefaults.color),
87
- secondaryColor: coerceWorkspaceThemeColor(source.darkSecondaryColor, paletteDefaults.secondaryColor),
88
- surfaceColor: coerceWorkspaceThemeColor(source.darkSurfaceColor, paletteDefaults.surfaceColor),
89
- surfaceVariantColor: coerceWorkspaceThemeColor(
90
- source.darkSurfaceVariantColor,
91
- paletteDefaults.surfaceVariantColor
92
- )
93
- });
94
- }
95
-
96
- return Object.freeze({
97
- color: coerceWorkspaceThemeColor(source.lightPrimaryColor, paletteDefaults.color),
98
- secondaryColor: coerceWorkspaceThemeColor(source.lightSecondaryColor, paletteDefaults.secondaryColor),
99
- surfaceColor: coerceWorkspaceThemeColor(source.lightSurfaceColor, paletteDefaults.surfaceColor),
100
- surfaceVariantColor: coerceWorkspaceThemeColor(
101
- source.lightSurfaceVariantColor,
102
- paletteDefaults.surfaceVariantColor
103
- )
104
- });
105
- }
106
-
107
- function resolveWorkspaceThemePalettes(input = {}) {
108
- return Object.freeze({
109
- light: resolveWorkspaceThemePalette(input, {
110
- mode: WORKSPACE_THEME_MODE_LIGHT
111
- }),
112
- dark: resolveWorkspaceThemePalette(input, {
113
- mode: WORKSPACE_THEME_MODE_DARK
114
- })
115
- });
116
- }
117
-
118
15
  export {
119
- DEFAULT_WORKSPACE_DARK_PALETTE,
120
- DEFAULT_WORKSPACE_LIGHT_PALETTE,
121
- DEFAULT_WORKSPACE_COLOR,
122
- DEFAULT_USER_SETTINGS,
123
- coerceWorkspaceColor,
124
- coerceWorkspaceThemeColor,
125
- coerceWorkspaceSecondaryColor,
126
- coerceWorkspaceSurfaceColor,
127
- coerceWorkspaceSurfaceVariantColor,
128
- normalizeWorkspaceHexColor,
129
- normalizeWorkspaceThemeMode,
130
- resolveWorkspaceThemeDefaultPalette,
131
- resolveWorkspaceThemePalettes,
132
- WORKSPACE_THEME_MODE_DARK,
133
- WORKSPACE_THEME_MODE_LIGHT,
134
- resolveWorkspaceThemePalette
16
+ DEFAULT_USER_SETTINGS
135
17
  };
@@ -42,28 +42,12 @@ exports.up = async function up(knex) {
42
42
  });
43
43
  }
44
44
 
45
- const hasConsoleSettingsTable = await knex.schema.hasTable("console_settings");
46
- if (!hasConsoleSettingsTable) {
47
- await knex.schema.createTable("console_settings", (table) => {
48
- table.bigInteger("id").primary();
49
- table.bigInteger("owner_user_id").unsigned().nullable().references("id").inTable("users").onDelete("SET NULL");
50
- table.timestamp("created_at", { useTz: false }).notNullable().defaultTo(knex.fn.now());
51
- table.timestamp("updated_at", { useTz: false }).notNullable().defaultTo(knex.fn.now());
52
- });
53
-
54
- await knex("console_settings").insert({
55
- id: 1,
56
- created_at: knex.fn.now(),
57
- updated_at: knex.fn.now()
58
- });
59
- }
60
45
  };
61
46
 
62
47
  /**
63
48
  * @param {import('knex').Knex} knex
64
49
  */
65
50
  exports.down = async function down(knex) {
66
- await knex.schema.dropTableIfExists("console_settings");
67
51
  await knex.schema.dropTableIfExists("user_settings");
68
52
  await knex.schema.dropTableIfExists("users");
69
53
  };
@@ -2,7 +2,7 @@ import assert from "node:assert/strict";
2
2
  import test from "node:test";
3
3
  import { createService } from "../src/server/common/services/authProfileSyncService.js";
4
4
 
5
- test("authProfileSyncService.syncIdentityProfile uses shared transaction for profile upsert and provisioning", async () => {
5
+ test("authProfileSyncService.syncIdentityProfile uses shared transaction for profile upsert and lifecycle contributors", async () => {
6
6
  const calls = [];
7
7
  const transaction = { trxId: "tx-1" };
8
8
 
@@ -33,11 +33,14 @@ test("authProfileSyncService.syncIdentityProfile uses shared transaction for pro
33
33
  return { userId: Number(userId) };
34
34
  }
35
35
  },
36
- workspaceProvisioningService: {
37
- async provisionWorkspaceForNewUser(_profile, options = {}) {
38
- calls.push({ step: "provision", trx: options.trx || null });
36
+ lifecycleContributors: [
37
+ {
38
+ contributorId: "test.lifecycle",
39
+ async afterIdentityProfileSynced({ created, options = {} } = {}) {
40
+ calls.push({ step: "lifecycle", created, trx: options.trx || null });
41
+ }
39
42
  }
40
- }
43
+ ]
41
44
  });
42
45
 
43
46
  const profile = await service.syncIdentityProfile({
@@ -52,7 +55,8 @@ test("authProfileSyncService.syncIdentityProfile uses shared transaction for pro
52
55
  assert.equal(calls[1].step, "find");
53
56
  assert.equal(calls[2].step, "upsert");
54
57
  assert.equal(calls[3].step, "ensureUserSettings");
55
- assert.equal(calls[4].step, "provision");
58
+ assert.equal(calls[4].step, "lifecycle");
59
+ assert.equal(calls[4].created, true);
56
60
  assert.equal(calls[1].trx, transaction);
57
61
  assert.equal(calls[2].trx, transaction);
58
62
  assert.equal(calls[3].trx, transaction);
@@ -87,11 +91,16 @@ test("authProfileSyncService.syncIdentityProfile skips write path when profile i
87
91
  return { userId: "7" };
88
92
  }
89
93
  },
90
- workspaceProvisioningService: {
91
- async provisionWorkspaceForNewUser() {
92
- provisionCalls += 1;
94
+ lifecycleContributors: [
95
+ {
96
+ contributorId: "test.lifecycle",
97
+ async afterIdentityProfileSynced({ created } = {}) {
98
+ if (created) {
99
+ provisionCalls += 1;
100
+ }
101
+ }
93
102
  }
94
- }
103
+ ]
95
104
  });
96
105
 
97
106
  const profile = await service.syncIdentityProfile({
@@ -3,9 +3,6 @@ import test from "node:test";
3
3
  import { registerAccountProfile } from "../src/server/accountProfile/registerAccountProfile.js";
4
4
  import { registerAccountPreferences } from "../src/server/accountPreferences/registerAccountPreferences.js";
5
5
  import { registerAccountNotifications } from "../src/server/accountNotifications/registerAccountNotifications.js";
6
- import { registerConsoleSettings } from "../src/server/consoleSettings/registerConsoleSettings.js";
7
- import { registerWorkspaceMembers } from "../src/server/workspaceMembers/registerWorkspaceMembers.js";
8
- import { registerWorkspacePendingInvitations } from "../src/server/workspacePendingInvitations/registerWorkspacePendingInvitations.js";
9
6
 
10
7
  function createAppDouble() {
11
8
  const serviceCalls = [];
@@ -58,94 +55,3 @@ test("account register functions publish account.settings.changed for update ope
58
55
  assert.equal(notifications?.metadata?.events?.updateNotifications?.[0]?.realtime?.event, "account.settings.changed");
59
56
  assert.equal(notifications?.metadata?.events?.updateNotifications?.[1]?.realtime?.event, "users.bootstrap.changed");
60
57
  });
61
-
62
- test("console settings register publishes console.settings.changed", () => {
63
- const payload = createAppDouble();
64
- registerConsoleSettings(payload.app);
65
- const consoleSettings = findServiceCall(payload.serviceCalls, "users.console.settings.service");
66
- assert.equal(consoleSettings?.metadata?.events?.updateSettings?.[0]?.realtime?.event, "console.settings.changed");
67
- });
68
-
69
- test("workspace register functions publish members/invites/workspace-list realtime events", async () => {
70
- const membersApp = createAppDouble();
71
- registerWorkspaceMembers(membersApp.app);
72
- const members = findServiceCall(membersApp.serviceCalls, "users.workspace.members.service");
73
- assert.equal(members?.metadata?.events?.updateMemberRole?.[0]?.realtime?.event, "workspace.members.changed");
74
- assert.equal(members?.metadata?.events?.updateMemberRole?.[1]?.realtime?.event, "users.bootstrap.changed");
75
- assert.equal(members?.metadata?.events?.removeMember?.[0]?.realtime?.event, "workspace.members.changed");
76
- assert.equal(members?.metadata?.events?.removeMember?.[1]?.realtime?.event, "users.bootstrap.changed");
77
- assert.equal(members?.metadata?.events?.createInvite?.[0]?.realtime?.event, "workspace.invites.changed");
78
- assert.equal(members?.metadata?.events?.createInvite?.[1]?.realtime?.event, "users.bootstrap.changed");
79
- assert.equal(members?.metadata?.events?.createInvite?.[1]?.entityId?.({ result: { createdInviteId: "91" } }), "91");
80
- assert.equal(members?.metadata?.events?.createInvite?.[1]?.realtime?.audience?.preset, "event_scope");
81
- assert.equal(typeof members?.metadata?.events?.createInvite?.[1]?.realtime?.audience?.userQuery, "function");
82
- const createInviteAudienceQueryResult = await members?.metadata?.events?.createInvite?.[1]?.realtime?.audience?.userQuery({
83
- knex() {
84
- return {
85
- join() {
86
- return this;
87
- },
88
- where(field, value) {
89
- assert.equal(field, "wi.id");
90
- assert.equal(value, "91");
91
- return this;
92
- },
93
- async first() {
94
- return {
95
- user_id: 55
96
- };
97
- }
98
- };
99
- },
100
- event: {
101
- entityId: "91"
102
- }
103
- });
104
- assert.deepEqual(createInviteAudienceQueryResult, [{ userId: "55" }]);
105
- assert.equal(members?.metadata?.events?.revokeInvite?.[0]?.realtime?.event, "workspace.invites.changed");
106
- assert.equal(members?.metadata?.events?.revokeInvite?.[1]?.realtime?.event, "users.bootstrap.changed");
107
- assert.equal(members?.metadata?.events?.revokeInvite?.[1]?.entityId?.({ result: { revokedInviteId: "19" } }), "19");
108
- assert.equal(members?.metadata?.events?.revokeInvite?.[1]?.realtime?.audience?.preset, "event_scope");
109
- assert.equal(typeof members?.metadata?.events?.revokeInvite?.[1]?.realtime?.audience?.userQuery, "function");
110
-
111
- const pendingApp = createAppDouble();
112
- registerWorkspacePendingInvitations(pendingApp.app);
113
- const pending = findServiceCall(pendingApp.serviceCalls, "users.workspace.pending-invitations.service");
114
- const acceptInviteEvents = Array.isArray(pending?.metadata?.events?.acceptInviteByToken)
115
- ? pending.metadata.events.acceptInviteByToken
116
- : [];
117
- const acceptInviteRealtimeEvents = acceptInviteEvents.map((entry) => entry?.realtime?.event).filter(Boolean);
118
- assert.ok(acceptInviteRealtimeEvents.includes("workspace.invitations.pending.changed"));
119
- assert.ok(acceptInviteRealtimeEvents.includes("users.bootstrap.changed"));
120
- assert.ok(acceptInviteRealtimeEvents.includes("workspaces.changed"));
121
- assert.ok(acceptInviteRealtimeEvents.includes("workspace.members.changed"));
122
- assert.ok(acceptInviteRealtimeEvents.includes("workspace.invites.changed"));
123
-
124
- const acceptedMembersChange = acceptInviteEvents.find(
125
- (entry) => entry?.realtime?.event === "workspace.members.changed"
126
- );
127
- assert.equal(acceptedMembersChange?.entityId?.({ result: { workspaceId: "9" } }), "9");
128
- assert.deepEqual(
129
- acceptedMembersChange?.realtime?.audience?.({
130
- event: {
131
- entityId: "9"
132
- }
133
- }),
134
- {
135
- workspaceId: "9"
136
- }
137
- );
138
-
139
- const acceptedInvitesChange = acceptInviteEvents.find(
140
- (entry) => entry?.realtime?.event === "workspace.invites.changed"
141
- );
142
- assert.equal(acceptedInvitesChange?.entityId?.({ result: { workspaceId: "9" } }), "9");
143
-
144
- const refuseInviteEvents = Array.isArray(pending?.metadata?.events?.refuseInviteByToken)
145
- ? pending.metadata.events.refuseInviteByToken
146
- : [];
147
- const refuseInviteRealtimeEvents = refuseInviteEvents.map((entry) => entry?.realtime?.event).filter(Boolean);
148
- assert.ok(refuseInviteRealtimeEvents.includes("workspace.invitations.pending.changed"));
149
- assert.ok(refuseInviteRealtimeEvents.includes("users.bootstrap.changed"));
150
- assert.ok(refuseInviteRealtimeEvents.includes("workspace.invites.changed"));
151
- });
@@ -2,32 +2,19 @@ import assert from "node:assert/strict";
2
2
  import test from "node:test";
3
3
  import { registerUsersCore } from "../src/server/registerUsersCore.js";
4
4
 
5
- test("registerUsersCore registers console and workspace action surface aliases when action runtime is available", () => {
6
- const calls = [];
5
+ test("registerUsersCore only binds users-core singletons", () => {
6
+ const singletonTokens = [];
7
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
- });
8
+ singleton(token) {
9
+ singletonTokens.push(String(token || ""));
16
10
  return this;
17
11
  }
18
12
  };
19
13
 
20
14
  registerUsersCore(app);
21
15
 
22
- assert.deepEqual(calls, [
23
- {
24
- sourceName: "workspace",
25
- resolverType: "function"
26
- },
27
- {
28
- sourceName: "console",
29
- resolverType: "function"
30
- }
16
+ assert.deepEqual(singletonTokens.sort(), [
17
+ "users.profile.sync.service"
31
18
  ]);
32
19
  });
33
20
 
@@ -2,11 +2,6 @@ import assert from "node:assert/strict";
2
2
  import test from "node:test";
3
3
  import { createRepository as createUsersRepository } from "../src/server/common/repositories/usersRepository.js";
4
4
  import { createRepository as createUserSettingsRepository } from "../src/server/common/repositories/userSettingsRepository.js";
5
- import { createRepository as createWorkspaceInvitesRepository } from "../src/server/common/repositories/workspaceInvitesRepository.js";
6
- import { createRepository as createWorkspaceMembershipsRepository } from "../src/server/common/repositories/workspaceMembershipsRepository.js";
7
- import { createRepository as createWorkspacesRepository } from "../src/server/common/repositories/workspacesRepository.js";
8
- import { createRepository as createConsoleSettingsRepository } from "../src/server/consoleSettings/consoleSettingsRepository.js";
9
- import { createRepository as createWorkspaceSettingsRepository } from "../src/server/workspaceSettings/workspaceSettingsRepository.js";
10
5
 
11
6
  function createKnexStub() {
12
7
  const knex = Object.assign(() => {
@@ -24,12 +19,7 @@ test("users-core repositories expose withTransaction", async () => {
24
19
  const knex = createKnexStub();
25
20
  const repositories = [
26
21
  createUsersRepository(knex),
27
- createUserSettingsRepository(knex),
28
- createWorkspaceInvitesRepository(knex),
29
- createWorkspaceMembershipsRepository(knex),
30
- createWorkspacesRepository(knex),
31
- createConsoleSettingsRepository(knex),
32
- createWorkspaceSettingsRepository(knex)
22
+ createUserSettingsRepository(knex)
33
23
  ];
34
24
 
35
25
  for (const repository of repositories) {
@@ -4,12 +4,8 @@ import path from "node:path";
4
4
  import { existsSync } from "node:fs";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import "../test-support/registerDefaultSettingsFields.js";
7
- import { workspaceMembersResource } from "../src/shared/resources/workspaceMembersResource.js";
8
- import { workspaceResource } from "../src/shared/resources/workspaceResource.js";
9
- import { workspaceSettingsResource } from "../src/shared/resources/workspaceSettingsResource.js";
10
7
  import { userProfileResource } from "../src/shared/resources/userProfileResource.js";
11
8
  import { userSettingsResource } from "../src/shared/resources/userSettingsResource.js";
12
- import { consoleSettingsResource } from "../src/shared/resources/consoleSettingsResource.js";
13
9
 
14
10
  function assertResourceOperationMessages(resource, operationName, label) {
15
11
  const operation = resource?.operations?.[operationName];
@@ -31,11 +27,8 @@ function assertResourceOperationMessages(resource, operationName, label) {
31
27
 
32
28
  test("users-core resources expose messages for all operations", () => {
33
29
  const resources = {
34
- workspace: workspaceResource,
35
- workspaceSettings: workspaceSettingsResource,
36
30
  userProfile: userProfileResource,
37
- userSettings: userSettingsResource,
38
- consoleSettings: consoleSettingsResource
31
+ userSettings: userSettingsResource
39
32
  };
40
33
 
41
34
  for (const [label, resource] of Object.entries(resources)) {
@@ -46,18 +39,7 @@ test("users-core resources expose messages for all operations", () => {
46
39
  });
47
40
 
48
41
  test("users-core specialized resource operations expose messages and validators", () => {
49
- const workspaceMembersOperationSpecs = [
50
- { label: "workspaceMembers.rolesList", operation: workspaceMembersResource.operations.rolesList },
51
- { label: "workspaceMembers.membersList", operation: workspaceMembersResource.operations.membersList },
52
- { label: "workspaceMembers.updateMemberRole", operation: workspaceMembersResource.operations.updateMemberRole },
53
- { label: "workspaceMembers.removeMember", operation: workspaceMembersResource.operations.removeMember },
54
- { label: "workspaceMembers.invitesList", operation: workspaceMembersResource.operations.invitesList },
55
- { label: "workspaceMembers.createInvite", operation: workspaceMembersResource.operations.createInvite },
56
- { label: "workspaceMembers.revokeInvite", operation: workspaceMembersResource.operations.revokeInvite },
57
- { label: "workspaceMembers.redeemInvite", operation: workspaceMembersResource.operations.redeemInvite }
58
- ];
59
42
  const operationSpecs = [
60
- ...workspaceMembersOperationSpecs,
61
43
  { label: "userProfile.avatarUpload", operation: userProfileResource.operations.avatarUpload },
62
44
  { label: "userProfile.avatarDelete", operation: userProfileResource.operations.avatarDelete },
63
45
  { label: "userSettings.passwordChange", operation: userSettingsResource.operations.passwordChange },
@@ -6,19 +6,9 @@ async function importWithIdentity(url, identity) {
6
6
  }
7
7
 
8
8
  test("settings field registries stay shared across module identities", async () => {
9
- const workspaceModuleUrl = new URL("../src/shared/resources/workspaceSettingsFields.js", import.meta.url);
10
9
  const userModuleUrl = new URL("../src/shared/resources/userSettingsFields.js", import.meta.url);
11
- const consoleModuleUrl = new URL("../src/shared/resources/consoleSettingsFields.js", import.meta.url);
12
-
13
- const workspaceA = await importWithIdentity(workspaceModuleUrl, "workspace-a");
14
- const workspaceB = await importWithIdentity(workspaceModuleUrl, "workspace-b");
15
- assert.equal(workspaceA.workspaceSettingsFields, workspaceB.workspaceSettingsFields);
16
10
 
17
11
  const userA = await importWithIdentity(userModuleUrl, "user-a");
18
12
  const userB = await importWithIdentity(userModuleUrl, "user-b");
19
13
  assert.equal(userA.userSettingsFields, userB.userSettingsFields);
20
-
21
- const consoleA = await importWithIdentity(consoleModuleUrl, "console-a");
22
- const consoleB = await importWithIdentity(consoleModuleUrl, "console-b");
23
- assert.equal(consoleA.consoleSettingsFields, consoleB.consoleSettingsFields);
24
14
  });