@jskit-ai/users-web 0.1.178-oldline.3 → 0.1.178

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 (124) hide show
  1. package/package.json +197 -45
  2. package/patterns/account-settings/PATTERN.md +61 -0
  3. package/src/client/bootstrap/user-bootstrap-handler.js +2 -12
  4. package/src/client/composables/account-settings/accountSettingsAvatarUploadRuntime.js +2 -2
  5. package/src/client/composables/useAccountSettingsRuntime.js +3 -3
  6. package/src/client/index.js +0 -16
  7. package/src/client/providers/UsersWebClientProvider.js +13 -14
  8. package/test/bootstrap.test.js +6 -41
  9. package/test/exportsContract.test.js +3 -29
  10. package/test/packageMetadataOwnership.test.js +19 -0
  11. package/test/settingsPlacementContract.test.js +93 -405
  12. package/fixtures/crud-list-date-filters/App.vue +0 -107
  13. package/fixtures/crud-list-date-filters/index.html +0 -12
  14. package/fixtures/crud-list-date-filters/main.js +0 -24
  15. package/fixtures/crud-list-date-filters/vite.config.mjs +0 -18
  16. package/package.descriptor.mjs +0 -468
  17. package/src/client/bulkActions.js +0 -47
  18. package/src/client/components/CrudAddEditScreen.vue +0 -186
  19. package/src/client/components/CrudListBulkActionSurface.vue +0 -126
  20. package/src/client/components/CrudListDateFilterControl.vue +0 -188
  21. package/src/client/components/CrudListFilterSurface.vue +0 -388
  22. package/src/client/components/CrudListRecordActionMenu.vue +0 -120
  23. package/src/client/components/CrudListScreen.vue +0 -501
  24. package/src/client/components/CrudViewScreen.vue +0 -197
  25. package/src/client/composables/crud/crudBindingSupport.js +0 -75
  26. package/src/client/composables/crud/crudHttpClientSupport.js +0 -57
  27. package/src/client/composables/crud/crudJsonApiTransportSupport.js +0 -145
  28. package/src/client/composables/crud/crudLookupFieldLabelSupport.js +0 -151
  29. package/src/client/composables/crud/crudLookupFieldRuntime.js +0 -251
  30. package/src/client/composables/crud/crudSchemaFormHelpers.js +0 -464
  31. package/src/client/composables/internal/crudListFilterLookupSupport.js +0 -161
  32. package/src/client/composables/internal/crudListParentTitleSupport.js +0 -171
  33. package/src/client/composables/internal/useOperationScope.js +0 -144
  34. package/src/client/composables/records/useAddEdit.js +0 -224
  35. package/src/client/composables/records/useCrudAddEdit.js +0 -227
  36. package/src/client/composables/records/useCrudList.js +0 -101
  37. package/src/client/composables/records/useCrudView.js +0 -47
  38. package/src/client/composables/records/useList.js +0 -500
  39. package/src/client/composables/records/useView.js +0 -164
  40. package/src/client/composables/runtime/addEditUiRuntime.js +0 -130
  41. package/src/client/composables/runtime/listUiRuntime.js +0 -123
  42. package/src/client/composables/runtime/modelStateHelpers.js +0 -90
  43. package/src/client/composables/runtime/operationAdapters.js +0 -34
  44. package/src/client/composables/runtime/operationUiHelpers.js +0 -125
  45. package/src/client/composables/runtime/operationValidationHelpers.js +0 -51
  46. package/src/client/composables/runtime/useAddEditCore.js +0 -112
  47. package/src/client/composables/runtime/useCommandCore.js +0 -123
  48. package/src/client/composables/runtime/useEndpointResource.js +0 -199
  49. package/src/client/composables/runtime/useFieldErrorBag.js +0 -61
  50. package/src/client/composables/runtime/useListCore.js +0 -112
  51. package/src/client/composables/runtime/useUiFeedback.js +0 -98
  52. package/src/client/composables/runtime/useViewCore.js +0 -93
  53. package/src/client/composables/runtime/viewUiRuntime.js +0 -96
  54. package/src/client/composables/support/errorMessageHelpers.js +0 -68
  55. package/src/client/composables/support/listQueryParamSupport.js +0 -473
  56. package/src/client/composables/support/listSearchSupport.js +0 -70
  57. package/src/client/composables/support/refValueHelpers.js +0 -19
  58. package/src/client/composables/support/requestQueryRuntimeSupport.js +0 -141
  59. package/src/client/composables/support/resourceLoadStateHelpers.js +0 -229
  60. package/src/client/composables/support/routeTemplateHelpers.js +0 -285
  61. package/src/client/composables/support/scopeHelpers.js +0 -145
  62. package/src/client/composables/useAccess.js +0 -109
  63. package/src/client/composables/useCommand.js +0 -118
  64. package/src/client/composables/useCrudAddEditScreen.js +0 -88
  65. package/src/client/composables/useCrudDeleteAction.js +0 -160
  66. package/src/client/composables/useCrudListBulkActions.js +0 -147
  67. package/src/client/composables/useCrudListFilterLookups.js +0 -146
  68. package/src/client/composables/useCrudListFilters.js +0 -339
  69. package/src/client/composables/useCrudListParentTitle.js +0 -151
  70. package/src/client/composables/useCrudListRowActions.js +0 -144
  71. package/src/client/composables/useCrudListScreen.js +0 -237
  72. package/src/client/composables/useCrudViewScreen.js +0 -77
  73. package/src/client/composables/usePagedCollection.js +0 -181
  74. package/src/client/composables/usePaths.js +0 -120
  75. package/src/client/composables/useRealtimeQueryInvalidation.js +0 -156
  76. package/src/client/composables/useScopeRuntime.js +0 -135
  77. package/src/client/composables/useSurfaceRouteContext.js +0 -25
  78. package/src/client/filters.js +0 -15
  79. package/src/client/lib/httpClient.js +0 -47
  80. package/src/client/lib/permissions.js +0 -34
  81. package/src/client/rowActions.js +0 -47
  82. package/src/client/support/contractGuards.js +0 -34
  83. package/src/client/support/crudListDateFilterSupport.js +0 -91
  84. package/test/addEditUiRuntime.test.js +0 -110
  85. package/test/crudBindingSupport.test.js +0 -110
  86. package/test/crudHttpClientSupport.test.js +0 -77
  87. package/test/crudJsonApiTransportSupport.test.js +0 -179
  88. package/test/crudListBulkActionSurface.test.js +0 -27
  89. package/test/crudListDateFilterSupport.test.js +0 -92
  90. package/test/crudListDateFilterSurface.browser.test.js +0 -273
  91. package/test/crudListFilterSurface.test.js +0 -90
  92. package/test/crudLookupFieldRuntime.test.js +0 -264
  93. package/test/crudScreenComponents.test.js +0 -108
  94. package/test/errorIntentContract.test.js +0 -31
  95. package/test/errorMessageHelpers.test.js +0 -39
  96. package/test/listQueryParamSupport.test.js +0 -223
  97. package/test/listUiRuntime.test.js +0 -102
  98. package/test/operationValidationHelpers.test.js +0 -64
  99. package/test/packageDescriptorOwnership.test.js +0 -18
  100. package/test/permissions.test.js +0 -35
  101. package/test/refValueHelpers.test.js +0 -14
  102. package/test/requestTransportOptions.test.js +0 -457
  103. package/test/resourceLoadStateHelpers.test.js +0 -189
  104. package/test/routeTemplateHelpers.test.js +0 -85
  105. package/test/scopeHelpers.test.js +0 -57
  106. package/test/useAddEditCore.test.js +0 -124
  107. package/test/useAddEditRequestQueryParams.test.js +0 -190
  108. package/test/useCrudAddEdit.test.js +0 -435
  109. package/test/useCrudDeleteAction.test.js +0 -139
  110. package/test/useCrudListBulkActions.test.js +0 -65
  111. package/test/useCrudListFilterLookups.test.js +0 -114
  112. package/test/useCrudListFilters.test.js +0 -293
  113. package/test/useCrudListParentTitle.test.js +0 -262
  114. package/test/useCrudListRowActions.test.js +0 -77
  115. package/test/useListRouteInitialization.test.js +0 -337
  116. package/test/useListSearchSupport.test.js +0 -61
  117. package/test/usePagedCollection.test.js +0 -53
  118. package/test/useViewRequestQueryParams.test.js +0 -20
  119. package/test/viewCoreLoading.test.js +0 -44
  120. package/test/viewUiRuntime.test.js +0 -95
  121. /package/{templates → patterns/account-settings/example}/src/components/account/settings/AccountSettingsNotificationsSection.vue +0 -0
  122. /package/{templates → patterns/account-settings/example}/src/components/account/settings/AccountSettingsPreferencesSection.vue +0 -0
  123. /package/{templates → patterns/account-settings/example}/src/components/account/settings/AccountSettingsProfileSection.vue +0 -0
  124. /package/{templates → patterns/account-settings/example}/src/pages/account/index.vue +0 -0
package/package.json CHANGED
@@ -1,64 +1,216 @@
1
1
  {
2
2
  "name": "@jskit-ai/users-web",
3
- "version": "0.1.178-oldline.3",
3
+ "version": "0.1.178",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "node --test"
7
7
  },
8
8
  "exports": {
9
9
  "./client": "./src/client/index.js",
10
+ "./client/providers/UsersWebClientProvider": "./src/client/providers/UsersWebClientProvider.js",
10
11
  "./client/components/AccountSettingsClientElement": "./src/client/components/AccountSettingsClientElement.vue",
11
- "./client/components/CrudAddEditScreen": "./src/client/components/CrudAddEditScreen.vue",
12
- "./client/components/CrudListBulkActionSurface": "./src/client/components/CrudListBulkActionSurface.vue",
13
- "./client/components/CrudListFilterSurface": "./src/client/components/CrudListFilterSurface.vue",
14
- "./client/components/CrudListScreen": "./src/client/components/CrudListScreen.vue",
15
- "./client/components/CrudViewScreen": "./src/client/components/CrudViewScreen.vue",
16
- "./client/account-settings/sections": "./src/client/account-settings/sections.js",
17
- "./client/bulkActions": "./src/client/bulkActions.js",
18
- "./client/filters": "./src/client/filters.js",
19
- "./client/rowActions": "./src/client/rowActions.js",
20
- "./client/composables/useAddEdit": "./src/client/composables/records/useAddEdit.js",
21
- "./client/composables/useAccess": "./src/client/composables/useAccess.js",
22
- "./client/composables/useCommand": "./src/client/composables/useCommand.js",
23
- "./client/composables/useCrudAddEdit": "./src/client/composables/records/useCrudAddEdit.js",
24
- "./client/composables/useCrudAddEditScreen": "./src/client/composables/useCrudAddEditScreen.js",
25
- "./client/composables/useCrudDeleteAction": "./src/client/composables/useCrudDeleteAction.js",
26
- "./client/composables/useCrudListBulkActions": "./src/client/composables/useCrudListBulkActions.js",
27
- "./client/composables/useCrudListRowActions": "./src/client/composables/useCrudListRowActions.js",
28
- "./client/composables/crudLookupFieldRuntime": "./src/client/composables/crud/crudLookupFieldRuntime.js",
29
- "./client/composables/useCrudListFilterLookups": "./src/client/composables/useCrudListFilterLookups.js",
30
- "./client/composables/useCrudListFilters": "./src/client/composables/useCrudListFilters.js",
31
- "./client/composables/useEndpointResource": "./src/client/composables/runtime/useEndpointResource.js",
32
- "./client/composables/useList": "./src/client/composables/records/useList.js",
33
- "./client/composables/useCrudList": "./src/client/composables/records/useCrudList.js",
34
- "./client/composables/useCrudListScreen": "./src/client/composables/useCrudListScreen.js",
35
- "./client/crudHttpClient": "./src/client/composables/crud/crudHttpClientSupport.js",
36
- "./client/composables/useCrudListParentTitle": "./src/client/composables/useCrudListParentTitle.js",
37
- "./client/composables/useRealtimeQueryInvalidation": "./src/client/composables/useRealtimeQueryInvalidation.js",
38
- "./client/composables/useSurfaceRouteContext": "./src/client/composables/useSurfaceRouteContext.js",
39
- "./client/composables/useView": "./src/client/composables/records/useView.js",
40
- "./client/composables/useCrudView": "./src/client/composables/records/useCrudView.js",
41
- "./client/composables/useCrudViewScreen": "./src/client/composables/useCrudViewScreen.js",
42
- "./client/composables/usePagedCollection": "./src/client/composables/usePagedCollection.js",
43
- "./client/composables/usePaths": "./src/client/composables/usePaths.js",
44
- "./client/composables/runtime/useUiFeedback": "./src/client/composables/runtime/useUiFeedback.js",
45
- "./client/lib/httpClient": "./src/client/lib/httpClient.js",
46
- "./client/lib/permissions": "./src/client/lib/permissions.js",
47
- "./client/support/contractGuards": "./src/client/support/contractGuards.js"
12
+ "./client/account-settings/sections": "./src/client/account-settings/sections.js"
48
13
  },
49
14
  "dependencies": {
50
- "@mdi/js": "^7.4.47",
51
- "@jskit-ai/http-runtime": "0.1.146",
52
- "@jskit-ai/kernel": "0.1.147",
53
- "@jskit-ai/realtime": "0.1.145",
54
- "@jskit-ai/shell-web": "0.1.149",
55
- "@jskit-ai/uploads-image-web": "0.1.123",
56
- "@jskit-ai/users-core": "0.1.161"
15
+ "@jskit-ai/auth-web": "0.1.160",
16
+ "@jskit-ai/http-web": "0.1.5",
17
+ "@jskit-ai/http-runtime": "0.1.158",
18
+ "@jskit-ai/kernel": "0.1.160",
19
+ "@jskit-ai/shell-web": "0.1.164",
20
+ "@jskit-ai/uploads-image-web": "0.1.135",
21
+ "@jskit-ai/users-core": "0.1.173"
57
22
  },
58
23
  "peerDependencies": {
59
24
  "@tanstack/vue-query": "^5.90.5",
60
25
  "vue": "^3.5.13",
61
26
  "vue-router": "^5.0.4",
62
27
  "vuetify": "^4.0.0"
28
+ },
29
+ "description": "Users web module with account, profile, and user-specific shell UI.",
30
+ "jskit": {
31
+ "kind": "runtime",
32
+ "capabilities": {
33
+ "provides": [
34
+ "users.web"
35
+ ],
36
+ "requires": []
37
+ },
38
+ "runtime": {
39
+ "server": {
40
+ "providers": []
41
+ },
42
+ "client": {
43
+ "providers": [
44
+ {
45
+ "entrypoint": "src/client/providers/UsersWebClientProvider.js",
46
+ "export": "UsersWebClientProvider"
47
+ }
48
+ ]
49
+ }
50
+ },
51
+ "metadata": {
52
+ "apiSummary": {
53
+ "surfaces": [
54
+ {
55
+ "subpath": "./client",
56
+ "summary": "Exports users-web client provider registration surface."
57
+ },
58
+ {
59
+ "subpath": "./client/providers/UsersWebClientProvider",
60
+ "summary": "Exports users-web client provider class."
61
+ },
62
+ {
63
+ "subpath": "./client/components/AccountSettingsClientElement",
64
+ "summary": "Exports the package-owned account settings host that renders placement-backed account sections."
65
+ },
66
+ {
67
+ "subpath": "./client/account-settings/sections",
68
+ "summary": "Exports placement-backed account settings section helpers."
69
+ }
70
+ ]
71
+ },
72
+ "ui": {
73
+ "placements": {
74
+ "outlets": [
75
+ {
76
+ "target": "home-cog:primary-menu",
77
+ "surfaces": [
78
+ "home"
79
+ ],
80
+ "source": "src/client/components/UsersHomeToolsWidget.vue"
81
+ },
82
+ {
83
+ "target": "account-settings:sections",
84
+ "surfaces": [
85
+ "account"
86
+ ],
87
+ "source": "src/client/components/AccountSettingsClientElement.vue"
88
+ }
89
+ ],
90
+ "topology": {
91
+ "placements": [
92
+ {
93
+ "id": "home.tools-menu",
94
+ "description": "Home surface tools menu actions.",
95
+ "surfaces": [
96
+ "home"
97
+ ],
98
+ "variants": {
99
+ "compact": {
100
+ "outlet": "home-cog:primary-menu",
101
+ "renderers": {
102
+ "link": "local.main.ui.surface-aware-menu-link-item"
103
+ }
104
+ },
105
+ "medium": {
106
+ "outlet": "home-cog:primary-menu",
107
+ "renderers": {
108
+ "link": "local.main.ui.surface-aware-menu-link-item"
109
+ }
110
+ },
111
+ "expanded": {
112
+ "outlet": "home-cog:primary-menu",
113
+ "renderers": {
114
+ "link": "local.main.ui.surface-aware-menu-link-item"
115
+ }
116
+ }
117
+ }
118
+ },
119
+ {
120
+ "id": "settings.sections",
121
+ "owner": "account-settings",
122
+ "description": "Account settings content sections.",
123
+ "surfaces": [
124
+ "account"
125
+ ],
126
+ "variants": {
127
+ "compact": {
128
+ "outlet": "account-settings:sections"
129
+ },
130
+ "medium": {
131
+ "outlet": "account-settings:sections"
132
+ },
133
+ "expanded": {
134
+ "outlet": "account-settings:sections"
135
+ }
136
+ }
137
+ }
138
+ ]
139
+ },
140
+ "contributions": [
141
+ {
142
+ "id": "users.profile.menu.settings",
143
+ "target": "auth.profile-menu",
144
+ "kind": "link",
145
+ "surfaces": [
146
+ "*"
147
+ ],
148
+ "order": 500,
149
+ "when": "auth.authenticated === true",
150
+ "source": "patterns/account-settings/PATTERN.md"
151
+ },
152
+ {
153
+ "id": "users.home.tools.widget",
154
+ "target": "shell.status",
155
+ "kind": "component",
156
+ "surfaces": [
157
+ "home"
158
+ ],
159
+ "order": 900,
160
+ "componentToken": "users.web.home.tools.widget",
161
+ "when": "auth.authenticated === true",
162
+ "source": "src/client/components/UsersHomeToolsWidget.vue"
163
+ },
164
+ {
165
+ "id": "users.home.menu.settings",
166
+ "target": "home.tools-menu",
167
+ "kind": "link",
168
+ "surfaces": [
169
+ "home"
170
+ ],
171
+ "order": 100,
172
+ "when": "auth.authenticated === true",
173
+ "source": "patterns/account-settings/PATTERN.md"
174
+ },
175
+ {
176
+ "id": "users.account.settings.profile",
177
+ "target": "settings.sections",
178
+ "owner": "account-settings",
179
+ "kind": "component",
180
+ "surfaces": [
181
+ "account"
182
+ ],
183
+ "order": 100,
184
+ "componentToken": "local.main.account-settings.section.profile",
185
+ "source": "patterns/account-settings/example/src/components/account/settings/AccountSettingsProfileSection.vue"
186
+ },
187
+ {
188
+ "id": "users.account.settings.preferences",
189
+ "target": "settings.sections",
190
+ "owner": "account-settings",
191
+ "kind": "component",
192
+ "surfaces": [
193
+ "account"
194
+ ],
195
+ "order": 200,
196
+ "componentToken": "local.main.account-settings.section.preferences",
197
+ "source": "patterns/account-settings/example/src/components/account/settings/AccountSettingsPreferencesSection.vue"
198
+ },
199
+ {
200
+ "id": "users.account.settings.notifications",
201
+ "target": "settings.sections",
202
+ "owner": "account-settings",
203
+ "kind": "component",
204
+ "surfaces": [
205
+ "account"
206
+ ],
207
+ "order": 300,
208
+ "componentToken": "local.main.account-settings.section.notifications",
209
+ "source": "patterns/account-settings/example/src/components/account/settings/AccountSettingsNotificationsSection.vue"
210
+ }
211
+ ]
212
+ }
213
+ }
214
+ }
63
215
  }
64
216
  }
@@ -0,0 +1,61 @@
1
+ ---
2
+ id: users/account-settings
3
+ title: Account settings surface
4
+ summary: Compose an account settings route and profile, preference, and notification sections from JSKIT user web components.
5
+ keywords: account, notifications, preferences, profile, settings, user, vue
6
+ requires: @jskit-ai/users-core, @jskit-ai/users-web, @jskit-ai/shell-web
7
+ ---
8
+
9
+ # Account settings surface
10
+
11
+ ## Use when
12
+
13
+ Use this pattern when authenticated users need to manage their own profile and
14
+ account preferences.
15
+
16
+ ## Do not use when
17
+
18
+ Do not use this pattern for administrator-managed users, authentication
19
+ credential storage, or anonymous profile editing.
20
+
21
+ ## Product decisions
22
+
23
+ Choose editable profile fields, preference vocabulary, notification options,
24
+ section order, and which account operations require reauthentication.
25
+
26
+ ## Framework APIs
27
+
28
+ Use account settings elements and composables from `@jskit-ai/users-web`,
29
+ ordinary `useCommand()` mutation behavior, and JSKIT shell placements.
30
+
31
+ ## Invariants
32
+
33
+ - Sections use JSKIT account settings APIs rather than direct HTTP helpers.
34
+ - Cached resource data hydrates local writable fields immediately.
35
+ - Mutation errors use the standard toast and do not push the form down.
36
+ - Loading uses layout-stable skeletons, never an indeterminate spinner.
37
+ - Pending controls keep stable labels and prevent duplicate submission.
38
+ - The route and placement owner agree.
39
+
40
+ ## Example files
41
+
42
+ `example/` contains an account route and three concrete settings sections.
43
+ Adapt their fields and copy while retaining the shared account runtime.
44
+
45
+ ## Variation points
46
+
47
+ Change sections, fields, copy, order, permissions, preference choices, and
48
+ notification vocabulary while keeping server-owned validation authoritative.
49
+
50
+ ## Verification
51
+
52
+ Navigate away and back with warm cache, exercise browser back/forward, save and
53
+ clear fields, verify toast errors, inspect skeleton geometry, and test keyboard
54
+ operation at compact and expanded widths.
55
+
56
+ ## Avoid
57
+
58
+ - page-local request clients
59
+ - inline mutation alerts that shift content
60
+ - lazy non-immediate watchers for initial hydration
61
+ - copied user repositories or generator source markers
@@ -1,7 +1,4 @@
1
- import {
2
- registerBootstrapPayloadHandler,
3
- resolveBootstrapErrorStatusCode
4
- } from "@jskit-ai/shell-web/client/bootstrap";
1
+ import { resolveBootstrapErrorStatusCode } from "@jskit-ai/shell-web/client/bootstrap";
5
2
  import { resolvePlacementUserFromBootstrapPayload } from "../lib/bootstrap.js";
6
3
 
7
4
  function createUsersBootstrapUserHandler() {
@@ -43,11 +40,4 @@ function createUsersBootstrapUserHandler() {
43
40
  });
44
41
  }
45
42
 
46
- function registerUsersBootstrapPayloadHandlers(app) {
47
- registerBootstrapPayloadHandler(app, "users.web.bootstrap.user-handler", () => createUsersBootstrapUserHandler());
48
- }
49
-
50
- export {
51
- createUsersBootstrapUserHandler,
52
- registerUsersBootstrapPayloadHandlers
53
- };
43
+ export { createUsersBootstrapUserHandler };
@@ -1,7 +1,7 @@
1
1
  import "@jskit-ai/uploads-image-web/client/styles";
2
2
  import { createManagedImageAssetRuntime } from "@jskit-ai/uploads-image-web/client/composables/createManagedImageAssetRuntime";
3
3
  import { resolveFieldErrors } from "@jskit-ai/http-runtime/client";
4
- import { usersWebHttpClient } from "../../lib/httpClient.js";
4
+ import { httpWebClient } from "@jskit-ai/http-web/client/lib/httpClient";
5
5
 
6
6
  function createAccountSettingsAvatarUploadRuntime({
7
7
  queryClient,
@@ -15,7 +15,7 @@ function createAccountSettingsAvatarUploadRuntime({
15
15
  const sessionPayload = await queryClient.fetchQuery({
16
16
  queryKey: sessionQueryKey,
17
17
  queryFn: () =>
18
- usersWebHttpClient.request("/api/session", {
18
+ httpWebClient.request("/api/session", {
19
19
  method: "GET"
20
20
  }),
21
21
  staleTime: 60_000
@@ -10,14 +10,14 @@ import { useShellWebErrorRuntime } from "@jskit-ai/shell-web/client/error";
10
10
  import { ROUTE_VISIBILITY_PUBLIC } from "@jskit-ai/kernel/shared/support/visibility";
11
11
  import { userProfileResource } from "@jskit-ai/users-core/shared/resources/userProfileResource";
12
12
  import { userSettingsResource } from "@jskit-ai/users-core/shared/resources/userSettingsResource";
13
+ import { useAddEdit } from "@jskit-ai/http-web/client/composables/useAddEdit";
14
+ import { useCommand } from "@jskit-ai/http-web/client/composables/useCommand";
15
+ import { useView } from "@jskit-ai/http-web/client/composables/useView";
13
16
  import {
14
17
  persistThemePreference,
15
18
  resolveThemeNameForPreference,
16
19
  setVuetifyThemeName
17
20
  } from "../lib/theme.js";
18
- import { useAddEdit } from "./records/useAddEdit.js";
19
- import { useCommand } from "./useCommand.js";
20
- import { useView } from "./records/useView.js";
21
21
  import { resolveSurfacePathFromPlacementContext } from "@jskit-ai/shell-web/client/placement";
22
22
  import {
23
23
  ACCOUNT_SETTINGS_DEFAULTS,
@@ -1,18 +1,2 @@
1
- import { UsersWebClientProvider } from "./providers/UsersWebClientProvider.js";
2
-
3
1
  export { UsersWebClientProvider } from "./providers/UsersWebClientProvider.js";
4
2
  export { default as AccountSettingsClientElement } from "./components/AccountSettingsClientElement.vue";
5
- export { default as CrudAddEditScreen } from "./components/CrudAddEditScreen.vue";
6
- export { default as CrudListBulkActionSurface } from "./components/CrudListBulkActionSurface.vue";
7
- export { default as CrudListFilterSurface } from "./components/CrudListFilterSurface.vue";
8
- export { default as CrudListScreen } from "./components/CrudListScreen.vue";
9
- export { default as CrudViewScreen } from "./components/CrudViewScreen.vue";
10
- export {
11
- normalizeCrudApiAccess,
12
- resolveCrudHttpClient
13
- } from "./composables/crud/crudHttpClientSupport.js";
14
- export { useCrudDeleteAction } from "./composables/useCrudDeleteAction.js";
15
-
16
- const clientProviders = Object.freeze([UsersWebClientProvider]);
17
-
18
- export { clientProviders };
@@ -1,20 +1,19 @@
1
+ import { defineProvider } from "@jskit-ai/kernel/shared/capabilities";
1
2
  import UsersHomeToolsWidget from "../components/UsersHomeToolsWidget.vue";
2
3
  import ProfileClientElement from "../components/ProfileClientElement.vue";
3
- import { registerUsersBootstrapPayloadHandlers } from "../bootstrap/user-bootstrap-handler.js";
4
+ import { createUsersBootstrapUserHandler } from "../bootstrap/user-bootstrap-handler.js";
4
5
 
5
- class UsersWebClientProvider {
6
- static id = "users.web.client";
7
- static dependsOn = ["shell.web.client"];
8
-
9
- register(app) {
10
- if (!app || typeof app.singleton !== "function" || typeof app.tag !== "function") {
11
- throw new Error("UsersWebClientProvider requires application singleton()/tag().");
12
- }
13
-
14
- app.singleton("users.web.home.tools.widget", () => UsersHomeToolsWidget);
15
- app.singleton("users.web.profile.element", () => ProfileClientElement);
16
- registerUsersBootstrapPayloadHandlers(app);
6
+ const UsersWebClientProvider = defineProvider({
7
+ id: "users.web.client",
8
+ requires: {
9
+ components: "client.components",
10
+ shell: "client.shell"
11
+ },
12
+ setup({ components, shell }) {
13
+ components.register("users.web.home.tools.widget", UsersHomeToolsWidget);
14
+ components.register("users.web.profile.element", ProfileClientElement);
15
+ shell.bootstrapHandlers.register(createUsersBootstrapUserHandler());
17
16
  }
18
- }
17
+ });
19
18
 
20
19
  export { UsersWebClientProvider };
@@ -1,11 +1,8 @@
1
1
  import assert from "node:assert/strict";
2
2
  import test from "node:test";
3
- import { resolveBootstrapPayloadHandlers } from "@jskit-ai/shell-web/client/bootstrap";
3
+ import { createBootstrapPayloadHandlerRegistry } from "@jskit-ai/shell-web/client/bootstrap";
4
4
  import { resolvePlacementUserFromBootstrapPayload } from "../src/client/lib/bootstrap.js";
5
- import {
6
- createUsersBootstrapUserHandler,
7
- registerUsersBootstrapPayloadHandlers
8
- } from "../src/client/bootstrap/user-bootstrap-handler.js";
5
+ import { createUsersBootstrapUserHandler } from "../src/client/bootstrap/user-bootstrap-handler.js";
9
6
 
10
7
  test("resolvePlacementUserFromBootstrapPayload returns null for anonymous sessions", () => {
11
8
  assert.equal(
@@ -62,38 +59,6 @@ function createPlacementRuntimeDouble() {
62
59
  };
63
60
  }
64
61
 
65
- function createBootstrapRegistryAppDouble() {
66
- const singletons = new Map();
67
- const instances = new Map();
68
- const tags = new Map();
69
-
70
- return {
71
- singleton(token, factory) {
72
- singletons.set(token, factory);
73
- },
74
- tag(token, tagName) {
75
- const current = tags.get(tagName) || [];
76
- current.push(token);
77
- tags.set(tagName, current);
78
- },
79
- resolveTag(tagName) {
80
- return (tags.get(tagName) || []).map((token) => this.make(token));
81
- },
82
- make(token) {
83
- if (instances.has(token)) {
84
- return instances.get(token);
85
- }
86
- const factory = singletons.get(token);
87
- if (typeof factory !== "function") {
88
- throw new Error(`Unknown token ${String(token)}`);
89
- }
90
- const instance = factory(this);
91
- instances.set(token, instance);
92
- return instance;
93
- }
94
- };
95
- }
96
-
97
62
  test("users web bootstrap user handler writes placement user", async () => {
98
63
  const handler = createUsersBootstrapUserHandler();
99
64
  const placementRuntime = createPlacementRuntimeDouble();
@@ -156,11 +121,11 @@ test("users web bootstrap user handler clears placement user on bootstrap 401 on
156
121
  assert.equal(placementRuntime.getContext().user, null);
157
122
  });
158
123
 
159
- test("registerUsersBootstrapPayloadHandlers registers the users bootstrap user handler", () => {
160
- const app = createBootstrapRegistryAppDouble();
161
- registerUsersBootstrapPayloadHandlers(app);
124
+ test("users bootstrap user handler registers with the explicit shell registry", () => {
125
+ const registry = createBootstrapPayloadHandlerRegistry();
126
+ registry.register(createUsersBootstrapUserHandler());
162
127
 
163
- const handlers = resolveBootstrapPayloadHandlers(app);
128
+ const handlers = registry.list();
164
129
  assert.equal(handlers.length, 1);
165
130
  assert.equal(handlers[0]?.handlerId, "users.web.bootstrap.user");
166
131
  assert.equal(typeof handlers[0]?.applyBootstrapPayload, "function");
@@ -8,42 +8,16 @@ const TEST_DIRECTORY = path.dirname(fileURLToPath(import.meta.url));
8
8
  const REPO_ROOT = path.resolve(TEST_DIRECTORY, "..", "..", "..");
9
9
  const PACKAGE_DIR = path.join(REPO_ROOT, "packages", "users-web");
10
10
 
11
- test("users-web exports are explicit and aligned with production/template usage", () => {
11
+ test("users-web exports only user-owned client surfaces", () => {
12
12
  const result = evaluatePackageExportsContract({
13
13
  repoRoot: REPO_ROOT,
14
14
  packageDir: PACKAGE_DIR,
15
15
  packageId: "@jskit-ai/users-web",
16
16
  requiredExports: [
17
17
  "./client",
18
+ "./client/providers/UsersWebClientProvider",
18
19
  "./client/components/AccountSettingsClientElement",
19
- "./client/components/CrudAddEditScreen",
20
- "./client/components/CrudListBulkActionSurface",
21
- "./client/components/CrudListFilterSurface",
22
- "./client/components/CrudListScreen",
23
- "./client/components/CrudViewScreen",
24
- "./client/account-settings/sections",
25
- "./client/bulkActions",
26
- "./client/filters",
27
- "./client/rowActions",
28
- "./client/composables/useAddEdit",
29
- "./client/composables/useCommand",
30
- "./client/composables/useEndpointResource",
31
- "./client/composables/useList",
32
- "./client/composables/usePaths",
33
- "./client/composables/useView",
34
- "./client/composables/useCrudAddEdit",
35
- "./client/composables/useCrudAddEditScreen",
36
- "./client/composables/useCrudDeleteAction",
37
- "./client/composables/useCrudListBulkActions",
38
- "./client/composables/useCrudListRowActions",
39
- "./client/composables/useCrudListFilterLookups",
40
- "./client/composables/useCrudListFilters",
41
- "./client/composables/useCrudList",
42
- "./client/composables/useCrudListScreen",
43
- "./client/crudHttpClient",
44
- "./client/composables/useCrudView",
45
- "./client/composables/useCrudViewScreen",
46
- "./client/lib/httpClient"
20
+ "./client/account-settings/sections"
47
21
  ]
48
22
  });
49
23
 
@@ -0,0 +1,19 @@
1
+ import assert from "node:assert/strict";
2
+ import path from "node:path";
3
+ import test from "node:test";
4
+ import { readFile } from "node:fs/promises";
5
+ import { fileURLToPath } from "node:url";
6
+ import packageJson from "../package.json" with { type: "json" };
7
+
8
+ const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
9
+
10
+ test("users-web publishes account examples as agent-readable patterns", async () => {
11
+ assert.equal(packageJson.jskit?.mutations, undefined);
12
+
13
+ const pattern = await readFile(path.join(packageRoot, "patterns", "account-settings", "PATTERN.md"), "utf8");
14
+ assert.match(pattern, /## Product decisions/);
15
+ assert.match(pattern, /## Invariants/);
16
+ assert.match(pattern, /Mutation errors use the standard toast/);
17
+ assert.match(pattern, /Loading uses layout-stable skeletons/);
18
+ assert.match(pattern, /## Example files/);
19
+ });