@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,9 +1,9 @@
1
1
  export default Object.freeze({
2
2
  packageVersion: 1,
3
3
  packageId: "@jskit-ai/users-core",
4
- version: "0.1.47",
4
+ version: "0.1.49",
5
5
  kind: "runtime",
6
- description: "Users/account runtime plus HTTP routes for account and console features.",
6
+ description: "Users/account runtime plus HTTP routes for account features.",
7
7
  dependsOn: [
8
8
  "@jskit-ai/auth-core",
9
9
  "@jskit-ai/database-runtime",
@@ -44,11 +44,11 @@ export default Object.freeze({
44
44
  {
45
45
  subpath: "./server",
46
46
  summary:
47
- "Exports UsersCoreServiceProvider, users/console repositories and services, account feature route registration modules, and action definitions."
47
+ "Exports UsersCoreServiceProvider plus account feature route registration modules and action definitions."
48
48
  },
49
49
  {
50
50
  subpath: "./shared",
51
- summary: "Exports shared users settings and tenancy utilities."
51
+ summary: "Exports shared users settings resources and defaults."
52
52
  },
53
53
  {
54
54
  subpath: "./client",
@@ -121,16 +121,6 @@ export default Object.freeze({
121
121
  method: "POST",
122
122
  path: "/api/settings/security/logout-others",
123
123
  summary: "Sign out from other active sessions."
124
- },
125
- {
126
- method: "GET",
127
- path: "/api/console/settings",
128
- summary: "Get console settings."
129
- },
130
- {
131
- method: "PATCH",
132
- path: "/api/console/settings",
133
- summary: "Update console settings."
134
124
  }
135
125
  ]
136
126
  }
@@ -138,11 +128,11 @@ export default Object.freeze({
138
128
  mutations: {
139
129
  dependencies: {
140
130
  runtime: {
141
- "@jskit-ai/auth-core": "0.1.36",
142
- "@jskit-ai/database-runtime": "0.1.37",
143
- "@jskit-ai/http-runtime": "0.1.36",
144
- "@jskit-ai/kernel": "0.1.37",
145
- "@jskit-ai/uploads-runtime": "0.1.15",
131
+ "@jskit-ai/auth-core": "0.1.38",
132
+ "@jskit-ai/database-runtime": "0.1.39",
133
+ "@jskit-ai/http-runtime": "0.1.38",
134
+ "@jskit-ai/kernel": "0.1.39",
135
+ "@jskit-ai/uploads-runtime": "0.1.17",
146
136
  "@fastify/type-provider-typebox": "^6.1.0",
147
137
  typebox: "^1.0.81"
148
138
  },
@@ -171,23 +161,6 @@ export default Object.freeze({
171
161
  category: "migration",
172
162
  id: "users-core-profile-username-schema"
173
163
  },
174
- {
175
- op: "install-migration",
176
- from: "templates/migrations/users_core_console_owner.cjs",
177
- toDir: "migrations",
178
- extension: ".cjs",
179
- reason: "Install console owner migration.",
180
- category: "migration",
181
- id: "users-core-console-owner-schema"
182
- },
183
- {
184
- from: "templates/packages/main/src/shared/resources/consoleSettingsFields.js",
185
- to: "packages/main/src/shared/resources/consoleSettingsFields.js",
186
- preserveOnRemove: true,
187
- reason: "Install app-owned console settings field definitions.",
188
- category: "users-core",
189
- id: "users-core-app-owned-console-settings-fields"
190
- },
191
164
  {
192
165
  from: "templates/packages/main/src/shared/resources/userSettingsFields.js",
193
166
  to: "packages/main/src/shared/resources/userSettingsFields.js",
@@ -207,16 +180,6 @@ export default Object.freeze({
207
180
  category: "runtime-config",
208
181
  id: "users-core-auth-profile-mode"
209
182
  },
210
- {
211
- op: "append-text",
212
- file: "packages/main/src/shared/index.js",
213
- position: "top",
214
- skipIfContains: "import \"./resources/consoleSettingsFields.js\";",
215
- value: "import \"./resources/consoleSettingsFields.js\";\n",
216
- reason: "Load app-owned console settings field definitions inside the main shared module.",
217
- category: "users-core",
218
- id: "users-core-main-shared-console-settings-field-import"
219
- },
220
183
  {
221
184
  op: "append-text",
222
185
  file: "packages/main/src/shared/index.js",
package/package.json CHANGED
@@ -1,34 +1,23 @@
1
1
  {
2
2
  "name": "@jskit-ai/users-core",
3
- "version": "0.1.47",
3
+ "version": "0.1.49",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "node --test"
7
7
  },
8
8
  "exports": {
9
- "./server/UsersWorkspacesServiceProvider": "./src/server/UsersWorkspacesServiceProvider.js",
10
- "./server/support/workspaceRouteInput": "./src/server/support/workspaceRouteInput.js",
11
- "./server/support/resolveWorkspace": "./src/server/support/resolveWorkspace.js",
12
- "./server/validators/routeParamsValidator": "./src/server/common/validators/routeParamsValidator.js",
9
+ "./server/profileSyncLifecycleContributorRegistry": "./src/server/profileSyncLifecycleContributorRegistry.js",
13
10
  "./shared/settings": "./src/shared/settings.js",
14
- "./shared/tenancyProfile": "./src/shared/tenancyProfile.js",
15
- "./shared/support/usersVisibility": "./src/shared/support/usersVisibility.js",
16
- "./shared/support/workspacePathModel": "./src/shared/support/workspacePathModel.js",
17
- "./shared/support/usersApiPaths": "./src/shared/support/usersApiPaths.js",
18
- "./shared/resources/workspaceResource": "./src/shared/resources/workspaceResource.js",
19
- "./shared/resources/workspaceSettingsResource": "./src/shared/resources/workspaceSettingsResource.js",
20
- "./shared/resources/workspaceSettingsFields": "./src/shared/resources/workspaceSettingsFields.js",
21
11
  "./shared/resources/userProfileResource": "./src/shared/resources/userProfileResource.js",
22
12
  "./shared/resources/userSettingsFields": "./src/shared/resources/userSettingsFields.js",
23
- "./shared/resources/userSettingsResource": "./src/shared/resources/userSettingsResource.js",
24
- "./shared/resources/consoleSettingsFields": "./src/shared/resources/consoleSettingsFields.js"
13
+ "./shared/resources/userSettingsResource": "./src/shared/resources/userSettingsResource.js"
25
14
  },
26
15
  "dependencies": {
27
- "@jskit-ai/auth-core": "0.1.36",
28
- "@jskit-ai/database-runtime": "0.1.37",
29
- "@jskit-ai/http-runtime": "0.1.36",
30
- "@jskit-ai/kernel": "0.1.37",
31
- "@jskit-ai/uploads-runtime": "0.1.15",
16
+ "@jskit-ai/auth-core": "0.1.38",
17
+ "@jskit-ai/database-runtime": "0.1.39",
18
+ "@jskit-ai/http-runtime": "0.1.38",
19
+ "@jskit-ai/kernel": "0.1.39",
20
+ "@jskit-ai/uploads-runtime": "0.1.17",
32
21
  "@fastify/type-provider-typebox": "^6.1.0",
33
22
  "typebox": "^1.0.81"
34
23
  }
@@ -3,7 +3,6 @@ import { bootAccountProfileRoutes } from "./accountProfile/bootAccountProfileRou
3
3
  import { bootAccountPreferencesRoutes } from "./accountPreferences/bootAccountPreferencesRoutes.js";
4
4
  import { bootAccountNotificationsRoutes } from "./accountNotifications/bootAccountNotificationsRoutes.js";
5
5
  import { bootAccountSecurityRoutes } from "./accountSecurity/bootAccountSecurityRoutes.js";
6
- import { bootConsoleSettingsRoutes } from "./consoleSettings/bootConsoleSettingsRoutes.js";
7
6
  import { registerSharedApi } from "./common/registerSharedApi.js";
8
7
  import { registerCommonRepositories } from "./common/registerCommonRepositories.js";
9
8
  import { registerUsersCore } from "./registerUsersCore.js";
@@ -12,7 +11,6 @@ import { registerAccountPreferences } from "./accountPreferences/registerAccount
12
11
  import { registerAccountNotifications } from "./accountNotifications/registerAccountNotifications.js";
13
12
  import { registerAccountProfile } from "./accountProfile/registerAccountProfile.js";
14
13
  import { registerAccountSecurity } from "./accountSecurity/registerAccountSecurity.js";
15
- import { registerConsoleSettings } from "./consoleSettings/registerConsoleSettings.js";
16
14
 
17
15
  class UsersCoreServiceProvider {
18
16
  static id = "users.core";
@@ -29,7 +27,6 @@ class UsersCoreServiceProvider {
29
27
  registerAccountPreferences(app);
30
28
  registerAccountNotifications(app);
31
29
  registerAccountSecurity(app);
32
- registerConsoleSettings(app);
33
30
  }
34
31
 
35
32
  async boot(app) {
@@ -37,7 +34,6 @@ class UsersCoreServiceProvider {
37
34
  bootAccountPreferencesRoutes(app);
38
35
  bootAccountNotificationsRoutes(app);
39
36
  bootAccountSecurityRoutes(app);
40
- bootConsoleSettingsRoutes(app);
41
37
  }
42
38
  }
43
39
 
@@ -1,6 +1,5 @@
1
1
  import { createRepository as createUsersRepository } from "./repositories/usersRepository.js";
2
2
  import { createRepository as createUserSettingsRepository } from "./repositories/userSettingsRepository.js";
3
- import { createRepository as createConsoleSettingsRepository } from "../consoleSettings/consoleSettingsRepository.js";
4
3
 
5
4
  function registerCommonRepositories(app) {
6
5
  if (!app || typeof app.singleton !== "function") {
@@ -16,10 +15,6 @@ function registerCommonRepositories(app) {
16
15
  const knex = scope.make("jskit.database.knex");
17
16
  return createUserSettingsRepository(knex);
18
17
  });
19
- app.singleton("consoleSettingsRepository", (scope) => {
20
- const knex = scope.make("jskit.database.knex");
21
- return createConsoleSettingsRepository(knex);
22
- });
23
18
  }
24
19
 
25
20
  export { registerCommonRepositories };
@@ -54,7 +54,17 @@ function requireSynchronizedProfile(profile) {
54
54
  throw new Error("Profile synchronization failed.");
55
55
  }
56
56
 
57
- function createService({ usersRepository, workspaceProvisioningService = null, userSettingsRepository = null } = {}) {
57
+ function normalizeLifecycleContributors(entries = []) {
58
+ if (!Array.isArray(entries)) {
59
+ return [];
60
+ }
61
+
62
+ return entries.filter(
63
+ (entry) => entry && typeof entry === "object" && typeof entry.afterIdentityProfileSynced === "function"
64
+ );
65
+ }
66
+
67
+ function createService({ usersRepository, lifecycleContributors = [], userSettingsRepository = null } = {}) {
58
68
  if (!usersRepository || typeof usersRepository.findByIdentity !== "function") {
59
69
  throw new Error("authProfileSyncService requires usersRepository.findByIdentity().");
60
70
  }
@@ -68,6 +78,8 @@ function createService({ usersRepository, workspaceProvisioningService = null, u
68
78
  throw new Error("authProfileSyncService requires userSettingsRepository.ensureForUserId().");
69
79
  }
70
80
 
81
+ const normalizedLifecycleContributors = normalizeLifecycleContributors(lifecycleContributors);
82
+
71
83
  async function findByIdentity(identityLike, options = {}) {
72
84
  const normalized = buildNormalizedIdentityKey(identityLike);
73
85
  return usersRepository.findByIdentity(
@@ -99,21 +111,30 @@ function createService({ usersRepository, workspaceProvisioningService = null, u
99
111
  const runSync = async (trx = null) => {
100
112
  const operationOptions = trx ? { ...options, trx } : options;
101
113
  const existing = await findByIdentity(normalized, operationOptions);
114
+ let created = false;
102
115
  if (!profileNeedsUpdate(existing, normalized)) {
103
116
  const synchronizedProfile = requireSynchronizedProfile(existing);
104
117
  await userSettingsRepository.ensureForUserId(synchronizedProfile.id, operationOptions);
118
+ for (const contributor of normalizedLifecycleContributors) {
119
+ await contributor.afterIdentityProfileSynced({
120
+ profile: synchronizedProfile,
121
+ created,
122
+ options: operationOptions
123
+ });
124
+ }
105
125
  return synchronizedProfile;
106
126
  }
107
127
 
108
128
  const upserted = await upsertByIdentity(normalized, operationOptions);
109
129
  const synchronizedProfile = requireSynchronizedProfile(upserted);
110
130
  await userSettingsRepository.ensureForUserId(synchronizedProfile.id, operationOptions);
111
- if (
112
- !existing &&
113
- workspaceProvisioningService &&
114
- typeof workspaceProvisioningService.provisionWorkspaceForNewUser === "function"
115
- ) {
116
- await workspaceProvisioningService.provisionWorkspaceForNewUser(synchronizedProfile, operationOptions);
131
+ created = !existing;
132
+ for (const contributor of normalizedLifecycleContributors) {
133
+ await contributor.afterIdentityProfileSynced({
134
+ profile: synchronizedProfile,
135
+ created,
136
+ options: operationOptions
137
+ });
117
138
  }
118
139
 
119
140
  return synchronizedProfile;
@@ -5,12 +5,6 @@ function resolveActorScopedEntityId({ options } = {}) {
5
5
  return normalizeRecordId(options?.context?.actor?.id, { fallback: "" });
6
6
  }
7
7
 
8
- function resolveWorkspaceSlugPayload({ args } = {}) {
9
- return {
10
- workspaceSlug: String(args?.[0]?.slug || "").trim()
11
- };
12
- }
13
-
14
8
  const ACCOUNT_SETTINGS_AND_BOOTSTRAP_EVENTS = deepFreeze([
15
9
  {
16
10
  type: "entity.changed",
@@ -36,56 +30,4 @@ const ACCOUNT_SETTINGS_AND_BOOTSTRAP_EVENTS = deepFreeze([
36
30
  }
37
31
  ]);
38
32
 
39
- function createWorkspaceEntityAndBootstrapEvents({
40
- workspaceEntity,
41
- workspaceOperation,
42
- workspaceRealtimeEvent,
43
- workspaceEntityId = ({ args }) => args?.[0]?.id,
44
- bootstrapEntityId = ({ args }) => args?.[0]?.id,
45
- bootstrapAudience = "event_scope"
46
- } = {}) {
47
- const normalizedWorkspaceEntity = String(workspaceEntity || "").trim();
48
- const normalizedWorkspaceOperation = String(workspaceOperation || "")
49
- .trim()
50
- .toLowerCase();
51
- const normalizedWorkspaceRealtimeEvent = String(workspaceRealtimeEvent || "").trim();
52
- if (!normalizedWorkspaceEntity || !normalizedWorkspaceOperation || !normalizedWorkspaceRealtimeEvent) {
53
- throw new Error(
54
- "createWorkspaceEntityAndBootstrapEvents requires workspaceEntity, workspaceOperation, and workspaceRealtimeEvent."
55
- );
56
- }
57
- if (typeof workspaceEntityId !== "function") {
58
- throw new Error("createWorkspaceEntityAndBootstrapEvents requires workspaceEntityId to be a function.");
59
- }
60
- if (typeof bootstrapEntityId !== "function") {
61
- throw new Error("createWorkspaceEntityAndBootstrapEvents requires bootstrapEntityId to be a function.");
62
- }
63
-
64
- return deepFreeze([
65
- {
66
- type: "entity.changed",
67
- source: "workspace",
68
- entity: normalizedWorkspaceEntity,
69
- operation: normalizedWorkspaceOperation,
70
- entityId: (payload = {}) => normalizeRecordId(workspaceEntityId(payload), { fallback: "" }),
71
- realtime: {
72
- event: normalizedWorkspaceRealtimeEvent,
73
- payload: resolveWorkspaceSlugPayload,
74
- audience: "event_scope"
75
- }
76
- },
77
- {
78
- type: "entity.changed",
79
- source: "users",
80
- entity: "bootstrap",
81
- operation: "updated",
82
- entityId: (payload = {}) => normalizeRecordId(bootstrapEntityId(payload), { fallback: "" }),
83
- realtime: {
84
- event: "users.bootstrap.changed",
85
- audience: bootstrapAudience
86
- }
87
- }
88
- ]);
89
- }
90
-
91
- export { ACCOUNT_SETTINGS_AND_BOOTSTRAP_EVENTS, createWorkspaceEntityAndBootstrapEvents };
33
+ export { ACCOUNT_SETTINGS_AND_BOOTSTRAP_EVENTS };
@@ -0,0 +1,56 @@
1
+ import { registerTaggedSingleton, resolveTaggedEntries } from "@jskit-ai/kernel/server/registries";
2
+
3
+ const PROFILE_SYNC_LIFECYCLE_CONTRIBUTOR_TAG = "jskit.users.profileSync.lifecycleContributors";
4
+
5
+ function normalizeProfileSyncLifecycleContributor(entry) {
6
+ if (typeof entry === "function") {
7
+ return Object.freeze({
8
+ contributorId: String(entry.name || "anonymous"),
9
+ order: 0,
10
+ afterIdentityProfileSynced: entry
11
+ });
12
+ }
13
+
14
+ if (!entry || typeof entry !== "object" || typeof entry.afterIdentityProfileSynced !== "function") {
15
+ return null;
16
+ }
17
+
18
+ const contributorId = String(entry.contributorId || "anonymous");
19
+ const order = Number.isFinite(entry.order) ? Number(entry.order) : 0;
20
+
21
+ return Object.freeze({
22
+ ...entry,
23
+ contributorId,
24
+ order,
25
+ afterIdentityProfileSynced: entry.afterIdentityProfileSynced
26
+ });
27
+ }
28
+
29
+ function registerProfileSyncLifecycleContributor(app, token, factory) {
30
+ registerTaggedSingleton(app, token, factory, PROFILE_SYNC_LIFECYCLE_CONTRIBUTOR_TAG, {
31
+ context: "registerProfileSyncLifecycleContributor"
32
+ });
33
+ }
34
+
35
+ function resolveProfileSyncLifecycleContributors(scope) {
36
+ return resolveTaggedEntries(scope, PROFILE_SYNC_LIFECYCLE_CONTRIBUTOR_TAG)
37
+ .map((entry, index) => ({
38
+ contributor: normalizeProfileSyncLifecycleContributor(entry),
39
+ index
40
+ }))
41
+ .filter((entry) => Boolean(entry.contributor))
42
+ .sort((left, right) => {
43
+ if (left.contributor.order !== right.contributor.order) {
44
+ return left.contributor.order - right.contributor.order;
45
+ }
46
+
47
+ return left.index - right.index;
48
+ })
49
+ .map((entry) => entry.contributor);
50
+ }
51
+
52
+ export {
53
+ PROFILE_SYNC_LIFECYCLE_CONTRIBUTOR_TAG,
54
+ registerProfileSyncLifecycleContributor,
55
+ resolveProfileSyncLifecycleContributors
56
+ };
@@ -12,9 +12,7 @@ function registerUsersBootstrap(app) {
12
12
  usersRepository: scope.make("usersRepository"),
13
13
  userSettingsRepository: scope.make("userSettingsRepository"),
14
14
  appConfig: resolveAppConfig(scope),
15
- tenancyProfile: scope.make("users.tenancy.profile"),
16
- authService: scope.make("authService"),
17
- consoleService: scope.has("consoleService") ? scope.make("consoleService") : null
15
+ authService: scope.make("authService")
18
16
  });
19
17
  });
20
18
  }
@@ -1,30 +1,18 @@
1
- import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
2
- import { resolveTenancyProfile } from "../shared/tenancyProfile.js";
3
1
  import { createService as createAuthProfileSyncService } from "./common/services/authProfileSyncService.js";
4
- import { registerUsersCoreActionSurfaceSources } from "./support/workspaceActionSurfaces.js";
2
+ import { resolveProfileSyncLifecycleContributors } from "./profileSyncLifecycleContributorRegistry.js";
5
3
 
6
4
  function registerUsersCore(app) {
7
5
  if (!app || typeof app.singleton !== "function") {
8
6
  throw new Error("registerUsersCore requires application singleton().");
9
7
  }
10
8
 
11
- registerUsersCoreActionSurfaceSources(app);
12
-
13
9
  app.singleton("users.profile.sync.service", (scope) => {
14
10
  return createAuthProfileSyncService({
15
11
  usersRepository: scope.make("usersRepository"),
16
12
  userSettingsRepository: scope.make("userSettingsRepository"),
17
- workspaceProvisioningService:
18
- typeof scope.has === "function" && scope.has("users.workspace.service")
19
- ? scope.make("users.workspace.service")
20
- : null
13
+ lifecycleContributors: resolveProfileSyncLifecycleContributors(scope)
21
14
  });
22
15
  });
23
-
24
- app.singleton("users.tenancy.profile", (scope) => {
25
- const appConfig = resolveAppConfig(scope);
26
- return resolveTenancyProfile(appConfig);
27
- });
28
16
  }
29
17
 
30
18
  export { registerUsersCore };
@@ -1,19 +1,10 @@
1
1
  import { AppError } from "@jskit-ai/kernel/server/runtime";
2
2
  import { requireServiceMethod } from "@jskit-ai/kernel/shared/actions/actionContributorHelpers";
3
3
  import { normalizeLowerText, normalizeText } from "@jskit-ai/kernel/shared/actions/textNormalization";
4
- import {
5
- TENANCY_MODE_NONE,
6
- TENANCY_MODE_PERSONAL,
7
- TENANCY_MODE_WORKSPACES,
8
- WORKSPACE_SLUG_POLICY_NONE,
9
- WORKSPACE_SLUG_POLICY_IMMUTABLE_USERNAME,
10
- WORKSPACE_SLUG_POLICY_USER_SELECTED,
11
- resolveTenancyProfile
12
- } from "../shared/tenancyProfile.js";
4
+ import { normalizeObject } from "@jskit-ai/kernel/shared/support/normalize";
13
5
  import { accountAvatarFormatter } from "./common/formatters/accountAvatarFormatter.js";
14
6
  import { authenticatedUserValidator } from "./common/validators/authenticatedUserValidator.js";
15
7
  import { userSettingsFields } from "../shared/resources/userSettingsFields.js";
16
- import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
17
8
 
18
9
  function getOAuthProviderCatalogPayload(authService) {
19
10
  if (!authService || typeof authService.getOAuthProviderCatalog !== "function") {
@@ -56,10 +47,8 @@ function normalizeBoolean(value, fallback) {
56
47
  return fallback;
57
48
  }
58
49
 
59
- function resolveAppState(appConfig = {}, { workspaceInvitationsEnabled = false } = {}) {
50
+ function resolveAppState(appConfig = {}) {
60
51
  const features = {
61
- workspaceSwitching: normalizeBoolean(appConfig.workspaceSwitching, false),
62
- workspaceInvites: workspaceInvitationsEnabled === true,
63
52
  assistantEnabled: normalizeBoolean(appConfig.assistantEnabled, false),
64
53
  assistantRequiredPermission: normalizeText(appConfig.assistantRequiredPermission),
65
54
  socialEnabled: normalizeBoolean(appConfig.socialEnabled, false),
@@ -71,56 +60,14 @@ function resolveAppState(appConfig = {}, { workspaceInvitationsEnabled = false }
71
60
  };
72
61
  }
73
62
 
74
- function normalizeSlugPolicy(value = "") {
75
- const normalizedValue = normalizeLowerText(value);
76
- if (
77
- normalizedValue === WORKSPACE_SLUG_POLICY_IMMUTABLE_USERNAME ||
78
- normalizedValue === WORKSPACE_SLUG_POLICY_USER_SELECTED
79
- ) {
80
- return normalizedValue;
81
- }
82
- return WORKSPACE_SLUG_POLICY_NONE;
83
- }
84
-
85
- function isSupportedTenancyMode(value = "") {
86
- return value === TENANCY_MODE_NONE || value === TENANCY_MODE_PERSONAL || value === TENANCY_MODE_WORKSPACES;
87
- }
88
-
89
- function resolveBootstrapTenancyProfile(tenancyProfile = null, appConfig = {}) {
90
- const fallback = resolveTenancyProfile(appConfig);
91
- const source = tenancyProfile && typeof tenancyProfile === "object" ? tenancyProfile : fallback;
92
- const mode = isSupportedTenancyMode(source?.mode) ? source.mode : fallback.mode;
93
- const workspace = source?.workspace && typeof source.workspace === "object" ? source.workspace : fallback.workspace;
94
-
95
- return Object.freeze({
96
- mode,
97
- workspace: Object.freeze({
98
- enabled: workspace.enabled === true,
99
- autoProvision: workspace.autoProvision === true,
100
- allowSelfCreate: workspace.allowSelfCreate === true,
101
- slugPolicy: normalizeSlugPolicy(workspace.slugPolicy)
102
- })
103
- });
104
- }
105
-
106
- function createAnonymousBootstrapPayload({ appState, tenancyProfile }) {
63
+ function createAnonymousBootstrapPayload({ appState, surfaceAccess = {} }) {
107
64
  return {
108
65
  session: {
109
66
  authenticated: false
110
67
  },
111
68
  profile: null,
112
- tenancy: tenancyProfile,
113
69
  app: appState,
114
- workspaces: [],
115
- pendingInvites: [],
116
- activeWorkspace: null,
117
- membership: null,
118
- requestedWorkspace: null,
119
- permissions: [],
120
- surfaceAccess: {
121
- consoleowner: false
122
- },
123
- workspaceSettings: null,
70
+ surfaceAccess: normalizeObject(surfaceAccess),
124
71
  userSettings: null,
125
72
  requestMeta: {
126
73
  hasRequest: false
@@ -153,13 +100,10 @@ function createUsersBootstrapContributor({
153
100
  usersRepository,
154
101
  userSettingsRepository,
155
102
  appConfig = {},
156
- tenancyProfile = null,
157
- authService,
158
- consoleService = null
103
+ authService
159
104
  } = {}) {
160
105
  const contributorId = "users.bootstrap";
161
106
  const appState = resolveAppState(appConfig);
162
- const resolvedTenancyProfile = resolveBootstrapTenancyProfile(tenancyProfile, appConfig);
163
107
 
164
108
  requireServiceMethod(usersRepository, "findById", contributorId, {
165
109
  serviceLabel: "usersRepository"
@@ -170,7 +114,8 @@ function createUsersBootstrapContributor({
170
114
 
171
115
  return Object.freeze({
172
116
  contributorId,
173
- async contribute({ request = null, reply = null } = {}) {
117
+ order: 100,
118
+ async contribute({ request = null, reply = null, payload: existingPayload = {} } = {}) {
174
119
  const authResult = await request.executeAction({
175
120
  actionId: "auth.session.read"
176
121
  });
@@ -186,26 +131,16 @@ function createUsersBootstrapContributor({
186
131
  }
187
132
 
188
133
  const normalizedUser = authenticatedUserValidator.normalize(authResult?.authenticated ? authResult?.profile : null);
134
+ const inheritedSurfaceAccess = normalizeObject(existingPayload?.surfaceAccess);
189
135
  let payload = createAnonymousBootstrapPayload({
190
136
  appState,
191
- tenancyProfile: resolvedTenancyProfile
137
+ surfaceAccess: inheritedSurfaceAccess
192
138
  });
193
139
 
194
140
  if (normalizedUser) {
195
141
  const latestProfile = (await usersRepository.findById(normalizedUser.id)) || normalizedUser;
196
142
  const userSettings = await userSettingsRepository.ensureForUserId(latestProfile.id);
197
143
 
198
- let seededConsoleOwnerUserId = null;
199
- if (
200
- consoleService &&
201
- typeof consoleService.ensureInitialConsoleMember === "function"
202
- ) {
203
- seededConsoleOwnerUserId = normalizeRecordId(
204
- await consoleService.ensureInitialConsoleMember(latestProfile.id),
205
- { fallback: null }
206
- );
207
- }
208
-
209
144
  payload = {
210
145
  session: {
211
146
  authenticated: true,
@@ -216,18 +151,8 @@ function createUsersBootstrapContributor({
216
151
  email: latestProfile.email,
217
152
  avatar: accountAvatarFormatter(latestProfile, userSettings)
218
153
  },
219
- tenancy: resolvedTenancyProfile,
220
154
  app: appState,
221
- workspaces: [],
222
- pendingInvites: [],
223
- activeWorkspace: null,
224
- membership: null,
225
- requestedWorkspace: null,
226
- permissions: [],
227
- surfaceAccess: {
228
- consoleowner: Boolean(seededConsoleOwnerUserId) && seededConsoleOwnerUserId === String(latestProfile.id)
229
- },
230
- workspaceSettings: null,
155
+ surfaceAccess: inheritedSurfaceAccess,
231
156
  userSettings: mapUserSettingsBootstrap(userSettings),
232
157
  requestMeta: {
233
158
  hasRequest: Boolean(request)