@jskit-ai/users-web 0.1.4

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 (97) hide show
  1. package/package.descriptor.mjs +507 -0
  2. package/package.json +31 -0
  3. package/src/client/components/ConsoleSettingsClientElement.vue +24 -0
  4. package/src/client/components/MembersAdminClientElement.vue +404 -0
  5. package/src/client/components/ProfileClientElement.vue +242 -0
  6. package/src/client/components/UsersProfileSurfaceSwitchMenuItem.vue +39 -0
  7. package/src/client/components/UsersShellMenuLinkItem.vue +140 -0
  8. package/src/client/components/UsersSurfaceAwareMenuLinkItem.vue +87 -0
  9. package/src/client/components/UsersWorkspaceMembersMenuItem.vue +36 -0
  10. package/src/client/components/UsersWorkspacePermissionMenuItem.vue +90 -0
  11. package/src/client/components/UsersWorkspaceSelector.vue +237 -0
  12. package/src/client/components/UsersWorkspaceSettingsMenuItem.vue +39 -0
  13. package/src/client/components/UsersWorkspaceToolsWidget.vue +23 -0
  14. package/src/client/components/WorkspaceMembersClientElement.vue +663 -0
  15. package/src/client/components/WorkspaceSettingsClientElement.vue +230 -0
  16. package/src/client/components/WorkspacesClientElement.vue +514 -0
  17. package/src/client/composables/accountSettingsAvatarUploadRuntime.js +241 -0
  18. package/src/client/composables/accountSettingsInvitesRuntime.js +88 -0
  19. package/src/client/composables/accountSettingsRuntimeConstants.js +77 -0
  20. package/src/client/composables/accountSettingsRuntimeHelpers.js +75 -0
  21. package/src/client/composables/errorMessageHelpers.js +66 -0
  22. package/src/client/composables/internal/useOperationScope.js +144 -0
  23. package/src/client/composables/modelStateHelpers.js +49 -0
  24. package/src/client/composables/operationUiHelpers.js +121 -0
  25. package/src/client/composables/operationValidationHelpers.js +52 -0
  26. package/src/client/composables/refValueHelpers.js +19 -0
  27. package/src/client/composables/scopeHelpers.js +145 -0
  28. package/src/client/composables/useAccess.js +109 -0
  29. package/src/client/composables/useAccountSettingsRuntime.js +533 -0
  30. package/src/client/composables/useAddEdit.js +135 -0
  31. package/src/client/composables/useAddEditCore.js +137 -0
  32. package/src/client/composables/useBootstrapQuery.js +52 -0
  33. package/src/client/composables/useCommand.js +112 -0
  34. package/src/client/composables/useCommandCore.js +130 -0
  35. package/src/client/composables/useEndpointResource.js +104 -0
  36. package/src/client/composables/useFieldErrorBag.js +61 -0
  37. package/src/client/composables/useList.js +85 -0
  38. package/src/client/composables/useListCore.js +65 -0
  39. package/src/client/composables/usePagedCollection.js +125 -0
  40. package/src/client/composables/usePaths.js +108 -0
  41. package/src/client/composables/useRealtimeQueryInvalidation.js +105 -0
  42. package/src/client/composables/useScopeRuntime.js +107 -0
  43. package/src/client/composables/useSurfaceRouteContext.js +31 -0
  44. package/src/client/composables/useUiFeedback.js +96 -0
  45. package/src/client/composables/useView.js +89 -0
  46. package/src/client/composables/useViewCore.js +104 -0
  47. package/src/client/composables/useWorkspaceRouteContext.js +28 -0
  48. package/src/client/composables/useWorkspaceSurfaceId.js +43 -0
  49. package/src/client/index.js +7 -0
  50. package/src/client/lib/bootstrap.js +95 -0
  51. package/src/client/lib/httpClient.js +67 -0
  52. package/src/client/lib/menuIcons.js +192 -0
  53. package/src/client/lib/permissions.js +34 -0
  54. package/src/client/lib/profileSurfaceMenuLinks.js +142 -0
  55. package/src/client/lib/surfaceAccessPolicy.js +350 -0
  56. package/src/client/lib/theme.js +99 -0
  57. package/src/client/lib/workspaceLinkResolver.js +207 -0
  58. package/src/client/lib/workspaceSurfaceContext.js +82 -0
  59. package/src/client/lib/workspaceSurfacePaths.js +163 -0
  60. package/src/client/providers/UsersWebClientProvider.js +85 -0
  61. package/src/client/runtime/bootstrapPlacementRouteGuards.js +371 -0
  62. package/src/client/runtime/bootstrapPlacementRuntime.js +413 -0
  63. package/src/client/runtime/bootstrapPlacementRuntimeConstants.js +32 -0
  64. package/src/client/runtime/bootstrapPlacementRuntimeHelpers.js +157 -0
  65. package/src/client/support/contractGuards.js +34 -0
  66. package/src/client/support/realtimeWorkspace.js +12 -0
  67. package/src/client/support/runtimeNormalization.js +27 -0
  68. package/src/client/support/workspaceQueryKeys.js +15 -0
  69. package/templates/packages/main/src/client/components/AccountPendingInvitesCue.vue +162 -0
  70. package/templates/src/components/WorkspaceNotFoundCard.vue +33 -0
  71. package/templates/src/components/account/settings/AccountSettingsClientElement.vue +153 -0
  72. package/templates/src/components/account/settings/AccountSettingsInvitesSection.vue +77 -0
  73. package/templates/src/components/account/settings/AccountSettingsNotificationsSection.vue +55 -0
  74. package/templates/src/components/account/settings/AccountSettingsPreferencesSection.vue +125 -0
  75. package/templates/src/components/account/settings/AccountSettingsProfileSection.vue +94 -0
  76. package/templates/src/composables/useWorkspaceNotFoundState.js +48 -0
  77. package/templates/src/pages/account/index.vue +17 -0
  78. package/templates/src/pages/admin/members/index.vue +7 -0
  79. package/templates/src/pages/admin/workspace/settings/index.vue +16 -0
  80. package/templates/src/pages/console/settings/index.vue +16 -0
  81. package/templates/src/surfaces/admin/index.vue +29 -0
  82. package/templates/src/surfaces/admin/root.vue +20 -0
  83. package/templates/src/surfaces/app/index.vue +27 -0
  84. package/templates/src/surfaces/app/root.vue +20 -0
  85. package/test/bootstrap.test.js +38 -0
  86. package/test/bootstrapPlacementRuntime.test.js +991 -0
  87. package/test/errorMessageHelpers.test.js +28 -0
  88. package/test/exportsContract.test.js +39 -0
  89. package/test/menuIcons.test.js +33 -0
  90. package/test/permissions.test.js +35 -0
  91. package/test/profileSurfaceMenuLinks.test.js +207 -0
  92. package/test/refValueHelpers.test.js +14 -0
  93. package/test/scopeHelpers.test.js +57 -0
  94. package/test/surfaceAccessPolicy.test.js +129 -0
  95. package/test/theme.test.js +95 -0
  96. package/test/workspaceLinkResolver.test.js +61 -0
  97. package/test/workspaceSurfacePaths.test.js +39 -0
@@ -0,0 +1,371 @@
1
+ import {
2
+ resolveSurfaceDefinitionFromPlacementContext,
3
+ resolveSurfaceIdFromPlacementPathname,
4
+ resolveSurfaceRootPathFromPlacementContext
5
+ } from "@jskit-ai/shell-web/client/placement";
6
+ import {
7
+ extractWorkspaceSlugFromSurfacePathname,
8
+ resolveSurfaceWorkspacePathFromPlacementContext
9
+ } from "../lib/workspaceSurfacePaths.js";
10
+ import { evaluateSurfaceAccess } from "../lib/surfaceAccessPolicy.js";
11
+ import {
12
+ SHELL_GUARD_EVALUATOR_KEY,
13
+ WORKSPACE_BOOTSTRAP_STATUS_FORBIDDEN,
14
+ WORKSPACE_BOOTSTRAP_STATUS_NOT_FOUND,
15
+ WORKSPACE_FORBIDDEN_GUARD_REASON,
16
+ WORKSPACE_NOT_FOUND_GUARD_REASON
17
+ } from "./bootstrapPlacementRuntimeConstants.js";
18
+ import {
19
+ isGuardDenied,
20
+ normalizeGuardPathname,
21
+ normalizeSearch,
22
+ normalizeWorkspaceBootstrapStatus,
23
+ normalizeWorkspaceSlugKey,
24
+ resolveSearchFromFullPath
25
+ } from "./bootstrapPlacementRuntimeHelpers.js";
26
+
27
+ function createBootstrapPlacementRouteGuards({
28
+ placementRuntime,
29
+ router = null,
30
+ root = null,
31
+ getWorkspaceBootstrapStatus = () => ""
32
+ } = {}) {
33
+ const cleanup = [];
34
+ let delegatedGuardEvaluator = null;
35
+ let workspaceGuardEvaluatorInstalled = false;
36
+
37
+ function resolveNormalizedSurfaceState(pathname = "/", search = "") {
38
+ const context = placementRuntime.getContext();
39
+ const normalizedPathname = normalizeGuardPathname(pathname);
40
+ const normalizedSearch = normalizeSearch(search);
41
+ const surfaceId = String(resolveSurfaceIdFromPlacementPathname(context, normalizedPathname) || "")
42
+ .trim()
43
+ .toLowerCase();
44
+ if (!surfaceId) {
45
+ return null;
46
+ }
47
+
48
+ const surfaceDefinition = resolveSurfaceDefinitionFromPlacementContext(context, surfaceId);
49
+ if (!surfaceDefinition) {
50
+ return null;
51
+ }
52
+
53
+ return Object.freeze({
54
+ context,
55
+ normalizedPathname,
56
+ normalizedSearch,
57
+ surfaceId,
58
+ surfaceDefinition
59
+ });
60
+ }
61
+
62
+ function resolveWorkspaceRouteState(pathname = "/", search = "") {
63
+ const surfaceState = resolveNormalizedSurfaceState(pathname, search);
64
+ if (!surfaceState || surfaceState.surfaceDefinition.requiresWorkspace !== true) {
65
+ return null;
66
+ }
67
+
68
+ const workspaceSlug = normalizeWorkspaceSlugKey(
69
+ extractWorkspaceSlugFromSurfacePathname(surfaceState.context, surfaceState.surfaceId, surfaceState.normalizedPathname)
70
+ );
71
+ if (!workspaceSlug) {
72
+ return null;
73
+ }
74
+
75
+ return Object.freeze({
76
+ pathname: surfaceState.normalizedPathname,
77
+ search: surfaceState.normalizedSearch,
78
+ surfaceId: surfaceState.surfaceId,
79
+ workspaceSlug,
80
+ workspaceRootPath: normalizeGuardPathname(
81
+ resolveSurfaceWorkspacePathFromPlacementContext(surfaceState.context, surfaceState.surfaceId, workspaceSlug, "/")
82
+ ),
83
+ workspaceBootstrapStatus: String(getWorkspaceBootstrapStatus(workspaceSlug) || "")
84
+ });
85
+ }
86
+
87
+ function resolveSurfaceRouteState(pathname = "/", search = "") {
88
+ const surfaceState = resolveNormalizedSurfaceState(pathname, search);
89
+ if (!surfaceState || surfaceState.surfaceDefinition.enabled === false) {
90
+ return null;
91
+ }
92
+
93
+ const workspaceSlug =
94
+ surfaceState.surfaceDefinition.requiresWorkspace === true
95
+ ? normalizeWorkspaceSlugKey(
96
+ extractWorkspaceSlugFromSurfacePathname(
97
+ surfaceState.context,
98
+ surfaceState.surfaceId,
99
+ surfaceState.normalizedPathname
100
+ )
101
+ )
102
+ : "";
103
+ return Object.freeze({
104
+ pathname: surfaceState.normalizedPathname,
105
+ search: surfaceState.normalizedSearch,
106
+ surfaceId: surfaceState.surfaceId,
107
+ workspaceSlug,
108
+ workspaceBootstrapStatus: workspaceSlug ? String(getWorkspaceBootstrapStatus(workspaceSlug) || "") : ""
109
+ });
110
+ }
111
+
112
+ function resolveDefaultSurfaceFallbackPath(surfaceState = null) {
113
+ const context = placementRuntime.getContext();
114
+ const defaultSurfaceId = String(context?.surfaceConfig?.defaultSurfaceId || "")
115
+ .trim()
116
+ .toLowerCase();
117
+ const fallbackSurfaceId = defaultSurfaceId || surfaceState?.surfaceId || "";
118
+ if (!fallbackSurfaceId) {
119
+ return "/";
120
+ }
121
+
122
+ const fallbackSurfaceDefinition = resolveSurfaceDefinitionFromPlacementContext(context, fallbackSurfaceId);
123
+ if (fallbackSurfaceDefinition?.requiresWorkspace === true) {
124
+ const fallbackWorkspaceSlug =
125
+ normalizeWorkspaceSlugKey(surfaceState?.workspaceSlug) || normalizeWorkspaceSlugKey(context?.workspace?.slug);
126
+ if (!fallbackWorkspaceSlug) {
127
+ return "/";
128
+ }
129
+ return normalizeGuardPathname(
130
+ resolveSurfaceWorkspacePathFromPlacementContext(context, fallbackSurfaceId, fallbackWorkspaceSlug, "/")
131
+ );
132
+ }
133
+
134
+ const fallbackPath = normalizeGuardPathname(resolveSurfaceRootPathFromPlacementContext(context, fallbackSurfaceId));
135
+ if (fallbackPath.includes(":")) {
136
+ return "/";
137
+ }
138
+ return fallbackPath || "/";
139
+ }
140
+
141
+ function resolveWorkspaceGuardDecision(pathname = "/", search = "") {
142
+ const workspaceState = resolveWorkspaceRouteState(pathname, search);
143
+ if (!workspaceState) {
144
+ return null;
145
+ }
146
+
147
+ if (workspaceState.workspaceBootstrapStatus === WORKSPACE_BOOTSTRAP_STATUS_NOT_FOUND) {
148
+ if (workspaceState.workspaceRootPath && workspaceState.workspaceRootPath !== workspaceState.pathname) {
149
+ return {
150
+ allow: false,
151
+ redirectTo: workspaceState.workspaceRootPath,
152
+ reason: WORKSPACE_NOT_FOUND_GUARD_REASON
153
+ };
154
+ }
155
+ return null;
156
+ }
157
+
158
+ if (workspaceState.workspaceBootstrapStatus === WORKSPACE_BOOTSTRAP_STATUS_FORBIDDEN) {
159
+ if (workspaceState.workspaceRootPath && workspaceState.workspaceRootPath !== workspaceState.pathname) {
160
+ return {
161
+ allow: false,
162
+ redirectTo: workspaceState.workspaceRootPath,
163
+ reason: WORKSPACE_FORBIDDEN_GUARD_REASON
164
+ };
165
+ }
166
+ return null;
167
+ }
168
+
169
+ return null;
170
+ }
171
+
172
+ function resolveSurfaceAccessGuardDecision(pathname = "/", search = "", { allowOnUnknown = true } = {}) {
173
+ const surfaceState = resolveSurfaceRouteState(pathname, search);
174
+ if (!surfaceState) {
175
+ return null;
176
+ }
177
+
178
+ if (
179
+ surfaceState.workspaceBootstrapStatus === WORKSPACE_BOOTSTRAP_STATUS_NOT_FOUND ||
180
+ surfaceState.workspaceBootstrapStatus === WORKSPACE_BOOTSTRAP_STATUS_FORBIDDEN
181
+ ) {
182
+ return null;
183
+ }
184
+
185
+ const accessDecision = evaluateSurfaceAccess({
186
+ context: placementRuntime.getContext(),
187
+ surfaceId: surfaceState.surfaceId,
188
+ workspaceSlug: surfaceState.workspaceSlug,
189
+ allowOnUnknown
190
+ });
191
+ if (accessDecision.allowed || accessDecision.pending) {
192
+ return null;
193
+ }
194
+
195
+ const redirectTarget = resolveDefaultSurfaceFallbackPath(surfaceState);
196
+ const redirectTo = redirectTarget && redirectTarget !== surfaceState.pathname ? redirectTarget : "";
197
+
198
+ return {
199
+ allow: false,
200
+ redirectTo,
201
+ reason: accessDecision.reason || "surface-access-denied"
202
+ };
203
+ }
204
+
205
+ async function replaceRouteIfNeeded(targetPath = "") {
206
+ if (!router || typeof router.replace !== "function") {
207
+ return;
208
+ }
209
+
210
+ const normalizedTargetPath = String(targetPath || "").trim();
211
+ if (!normalizedTargetPath) {
212
+ return;
213
+ }
214
+
215
+ const currentRoute = router.currentRoute?.value || {};
216
+ const currentFullPath = String(currentRoute.fullPath || "").trim();
217
+ const currentPath = normalizeGuardPathname(currentRoute.path || "/");
218
+ const currentComparablePath = currentFullPath || currentPath;
219
+ if (currentComparablePath === normalizedTargetPath) {
220
+ return;
221
+ }
222
+
223
+ try {
224
+ await router.replace(normalizedTargetPath);
225
+ } catch {}
226
+ }
227
+
228
+ function enforceWorkspaceRouteForStatusUpdate({ workspaceSlug = "", status = "" } = {}) {
229
+ const normalizedWorkspaceSlug = normalizeWorkspaceSlugKey(workspaceSlug);
230
+ const normalizedStatus = normalizeWorkspaceBootstrapStatus(status);
231
+ if (!normalizedWorkspaceSlug || !normalizedStatus || !router) {
232
+ return;
233
+ }
234
+
235
+ const currentRoute = router.currentRoute?.value || {};
236
+ const currentPath = normalizeGuardPathname(currentRoute.path || "/");
237
+ const currentSearch = resolveSearchFromFullPath(currentRoute.fullPath || "");
238
+ const workspaceState = resolveWorkspaceRouteState(currentPath, currentSearch);
239
+ if (!workspaceState || workspaceState.workspaceSlug !== normalizedWorkspaceSlug) {
240
+ return;
241
+ }
242
+
243
+ if (normalizedStatus === WORKSPACE_BOOTSTRAP_STATUS_NOT_FOUND) {
244
+ if (workspaceState.workspaceRootPath && workspaceState.workspaceRootPath !== workspaceState.pathname) {
245
+ void replaceRouteIfNeeded(workspaceState.workspaceRootPath);
246
+ }
247
+ return;
248
+ }
249
+
250
+ if (normalizedStatus === WORKSPACE_BOOTSTRAP_STATUS_FORBIDDEN) {
251
+ if (workspaceState.workspaceRootPath && workspaceState.workspaceRootPath !== workspaceState.pathname) {
252
+ void replaceRouteIfNeeded(workspaceState.workspaceRootPath);
253
+ }
254
+ }
255
+ }
256
+
257
+ function enforceSurfaceAccessForCurrentRoute() {
258
+ if (!router) {
259
+ return;
260
+ }
261
+
262
+ const currentRoute = router.currentRoute?.value || {};
263
+ const currentPath = normalizeGuardPathname(currentRoute.path || "/");
264
+ const currentSearch = resolveSearchFromFullPath(currentRoute.fullPath || "");
265
+ const surfaceDecision = resolveSurfaceAccessGuardDecision(currentPath, currentSearch, {
266
+ allowOnUnknown: false
267
+ });
268
+ if (!surfaceDecision || !surfaceDecision.redirectTo) {
269
+ return;
270
+ }
271
+
272
+ void replaceRouteIfNeeded(surfaceDecision.redirectTo);
273
+ }
274
+
275
+ function installWorkspaceGuardEvaluator() {
276
+ if (!root || workspaceGuardEvaluatorInstalled) {
277
+ return;
278
+ }
279
+
280
+ if (typeof delegatedGuardEvaluator !== "function") {
281
+ const currentEvaluator = root[SHELL_GUARD_EVALUATOR_KEY];
282
+ delegatedGuardEvaluator = typeof currentEvaluator === "function" ? currentEvaluator : null;
283
+ }
284
+
285
+ const previousDescriptor = Object.getOwnPropertyDescriptor(root, SHELL_GUARD_EVALUATOR_KEY);
286
+ const previousOwnProperty = previousDescriptor || null;
287
+ let released = false;
288
+
289
+ const workspaceGuardEvaluator = ({ guard, phase, context } = {}) => {
290
+ const baseOutcome =
291
+ typeof delegatedGuardEvaluator === "function"
292
+ ? delegatedGuardEvaluator({
293
+ guard,
294
+ phase,
295
+ context
296
+ })
297
+ : true;
298
+ if (isGuardDenied(baseOutcome)) {
299
+ return baseOutcome;
300
+ }
301
+
302
+ const pathname = normalizeGuardPathname(context?.location?.pathname || context?.to?.path || "/");
303
+ const search = normalizeSearch(context?.location?.search || resolveSearchFromFullPath(context?.to?.fullPath || ""));
304
+ const workspaceDecision = resolveWorkspaceGuardDecision(pathname, search);
305
+ if (workspaceDecision) {
306
+ return workspaceDecision;
307
+ }
308
+
309
+ const surfaceDecision = resolveSurfaceAccessGuardDecision(pathname, search, {
310
+ allowOnUnknown: true
311
+ });
312
+ if (surfaceDecision) {
313
+ return surfaceDecision;
314
+ }
315
+
316
+ return baseOutcome;
317
+ };
318
+
319
+ Object.defineProperty(root, SHELL_GUARD_EVALUATOR_KEY, {
320
+ configurable: true,
321
+ enumerable: true,
322
+ get() {
323
+ return workspaceGuardEvaluator;
324
+ },
325
+ set(nextEvaluator) {
326
+ if (nextEvaluator === workspaceGuardEvaluator) {
327
+ return;
328
+ }
329
+ delegatedGuardEvaluator = typeof nextEvaluator === "function" ? nextEvaluator : null;
330
+ }
331
+ });
332
+ workspaceGuardEvaluatorInstalled = true;
333
+
334
+ cleanup.push(() => {
335
+ if (released || !root) {
336
+ return;
337
+ }
338
+ released = true;
339
+
340
+ try {
341
+ if (previousOwnProperty) {
342
+ Object.defineProperty(root, SHELL_GUARD_EVALUATOR_KEY, previousOwnProperty);
343
+ } else {
344
+ delete root[SHELL_GUARD_EVALUATOR_KEY];
345
+ }
346
+ } catch {}
347
+ delegatedGuardEvaluator = null;
348
+ workspaceGuardEvaluatorInstalled = false;
349
+ });
350
+ }
351
+
352
+ function shutdown() {
353
+ for (const release of cleanup.splice(0, cleanup.length)) {
354
+ if (typeof release !== "function") {
355
+ continue;
356
+ }
357
+ try {
358
+ release();
359
+ } catch {}
360
+ }
361
+ }
362
+
363
+ return Object.freeze({
364
+ enforceSurfaceAccessForCurrentRoute,
365
+ enforceWorkspaceRouteForStatusUpdate,
366
+ installWorkspaceGuardEvaluator,
367
+ shutdown
368
+ });
369
+ }
370
+
371
+ export { createBootstrapPlacementRouteGuards };