@jskit-ai/users-core 0.1.160 → 0.1.163

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/users-core",
3
- "version": "0.1.160",
3
+ "version": "0.1.163",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "node --test"
@@ -11,14 +11,373 @@
11
11
  "./shared/resources/userSettingsResource": "./src/shared/resources/userSettingsResource.js"
12
12
  },
13
13
  "dependencies": {
14
- "@jskit-ai/auth-core": "0.1.145",
15
- "@jskit-ai/database-runtime": "0.1.146",
16
- "@jskit-ai/http-runtime": "0.1.145",
17
- "@jskit-ai/json-rest-api-core": "0.1.91",
18
- "@jskit-ai/kernel": "0.1.146",
19
- "@jskit-ai/resource-crud-core": "0.1.89",
20
- "@jskit-ai/resource-core": "0.1.89",
21
- "@jskit-ai/uploads-runtime": "0.1.122",
22
- "json-rest-schema": "1.x.x"
14
+ "@jskit-ai/auth-core": "0.1.148",
15
+ "@jskit-ai/database-runtime": "0.1.150",
16
+ "@jskit-ai/http-runtime": "0.1.148",
17
+ "@jskit-ai/json-rest-api-core": "0.1.94",
18
+ "@jskit-ai/kernel": "0.1.150",
19
+ "@jskit-ai/resource-core": "0.1.92",
20
+ "@jskit-ai/resource-crud-core": "0.1.92",
21
+ "@jskit-ai/storage-runtime": "0.1.147",
22
+ "@jskit-ai/uploads-runtime": "0.1.125",
23
+ "json-rest-schema": "^1.0.17"
24
+ },
25
+ "description": "Users/account runtime plus HTTP routes for account features.",
26
+ "jskit": {
27
+ "kind": "runtime",
28
+ "capabilities": {
29
+ "provides": [
30
+ "users.core",
31
+ "users.server-routes"
32
+ ],
33
+ "requires": [
34
+ "runtime.actions",
35
+ "runtime.database",
36
+ "json-rest-api.core",
37
+ "runtime.storage",
38
+ "runtime.uploads",
39
+ "auth.provider",
40
+ "auth.policy"
41
+ ]
42
+ },
43
+ "runtime": {
44
+ "server": {
45
+ "providers": [
46
+ {
47
+ "entrypoint": "src/server/UsersCoreServiceProvider.js",
48
+ "export": "UsersCoreServiceProvider"
49
+ }
50
+ ]
51
+ },
52
+ "client": {
53
+ "providers": []
54
+ }
55
+ },
56
+ "metadata": {
57
+ "jskit": {
58
+ "tableOwnership": {
59
+ "tables": [
60
+ {
61
+ "tableName": "user_settings",
62
+ "provenance": "runtime-package",
63
+ "ownerKind": "package-runtime"
64
+ }
65
+ ]
66
+ }
67
+ },
68
+ "apiSummary": {
69
+ "surfaces": [
70
+ {
71
+ "subpath": "./server",
72
+ "summary": "Exports UsersCoreServiceProvider plus account feature route registration modules and action definitions."
73
+ },
74
+ {
75
+ "subpath": "./shared",
76
+ "summary": "Exports shared users settings resources and defaults."
77
+ },
78
+ {
79
+ "subpath": "./client",
80
+ "summary": "Exports no runtime API today (reserved client entrypoint)."
81
+ }
82
+ ],
83
+ "containerTokens": {
84
+ "server": [
85
+ "users.profile.sync.service",
86
+ "auth.profile.projector"
87
+ ],
88
+ "client": []
89
+ }
90
+ },
91
+ "server": {
92
+ "routes": [
93
+ {
94
+ "method": "GET",
95
+ "path": "/api/settings",
96
+ "summary": "Get authenticated user settings."
97
+ },
98
+ {
99
+ "method": "PATCH",
100
+ "path": "/api/settings/profile",
101
+ "summary": "Update profile settings."
102
+ },
103
+ {
104
+ "method": "GET",
105
+ "path": "/api/settings/profile/avatar",
106
+ "summary": "Read authenticated user's uploaded avatar."
107
+ },
108
+ {
109
+ "method": "POST",
110
+ "path": "/api/settings/profile/avatar",
111
+ "summary": "Upload profile avatar."
112
+ },
113
+ {
114
+ "method": "DELETE",
115
+ "path": "/api/settings/profile/avatar",
116
+ "summary": "Delete profile avatar."
117
+ },
118
+ {
119
+ "method": "PATCH",
120
+ "path": "/api/settings/preferences",
121
+ "summary": "Update user preferences."
122
+ },
123
+ {
124
+ "method": "PATCH",
125
+ "path": "/api/settings/notifications",
126
+ "summary": "Update notification settings."
127
+ },
128
+ {
129
+ "method": "POST",
130
+ "path": "/api/settings/security/change-password",
131
+ "summary": "Set or change password for authenticated user."
132
+ },
133
+ {
134
+ "method": "PATCH",
135
+ "path": "/api/settings/security/methods/password",
136
+ "summary": "Enable or disable password sign-in method."
137
+ },
138
+ {
139
+ "method": "GET",
140
+ "path": "/api/settings/security/oauth/:provider/start",
141
+ "summary": "Start linking an OAuth provider for authenticated user."
142
+ },
143
+ {
144
+ "method": "DELETE",
145
+ "path": "/api/settings/security/oauth/:provider",
146
+ "summary": "Unlink an OAuth provider from authenticated account."
147
+ },
148
+ {
149
+ "method": "POST",
150
+ "path": "/api/settings/security/logout-others",
151
+ "summary": "Sign out from other active sessions."
152
+ }
153
+ ]
154
+ }
155
+ },
156
+ "mutations": {
157
+ "dependencies": {
158
+ "runtime": {
159
+ "@jskit-ai/auth-core": "0.1.148",
160
+ "@jskit-ai/crud-core": "0.1.161",
161
+ "@jskit-ai/database-runtime": "0.1.150",
162
+ "@jskit-ai/http-runtime": "0.1.148",
163
+ "@jskit-ai/json-rest-api-core": "0.1.94",
164
+ "@jskit-ai/kernel": "0.1.150",
165
+ "@jskit-ai/resource-core": "0.1.92",
166
+ "@jskit-ai/resource-crud-core": "0.1.92",
167
+ "@local/users": "file:packages/users",
168
+ "@jskit-ai/uploads-runtime": "0.1.125"
169
+ },
170
+ "dev": {}
171
+ },
172
+ "packageJson": {
173
+ "scripts": {}
174
+ },
175
+ "procfile": {},
176
+ "files": [
177
+ {
178
+ "op": "install-migration",
179
+ "from": "templates/migrations/users_core_generic_initial.cjs",
180
+ "toDir": "migrations",
181
+ "extension": ".cjs",
182
+ "reason": "Install users/account core schema migration.",
183
+ "category": "migration",
184
+ "id": "users-core-generic-initial-schema"
185
+ },
186
+ {
187
+ "op": "install-migration",
188
+ "from": "templates/migrations/users_core_profile_username.cjs",
189
+ "toDir": "migrations",
190
+ "extension": ".cjs",
191
+ "reason": "Install users profile username migration.",
192
+ "category": "migration",
193
+ "id": "users-core-profile-username-schema"
194
+ },
195
+ {
196
+ "op": "install-migration",
197
+ "from": "templates/migrations/users_core_profile_updated_at.cjs",
198
+ "toDir": "migrations",
199
+ "extension": ".cjs",
200
+ "reason": "Install users profile updated-at migration.",
201
+ "category": "migration",
202
+ "id": "users-core-profile-updated-at-schema"
203
+ },
204
+ {
205
+ "from": "templates/packages/users/package.json",
206
+ "to": "packages/users/package.json",
207
+ "ownership": "app",
208
+ "preserveOnRemove": true,
209
+ "reason": "Install app-owned users package manifest for non-workspace tenancy.",
210
+ "category": "users-core",
211
+ "id": "users-core-users-package-json-base",
212
+ "when": {
213
+ "config": "tenancyMode",
214
+ "notIn": [
215
+ "personal",
216
+ "workspaces"
217
+ ]
218
+ }
219
+ },
220
+ {
221
+ "from": "templates/packages/users-workspace/package.json",
222
+ "to": "packages/users/package.json",
223
+ "ownership": "app",
224
+ "preserveOnRemove": true,
225
+ "reason": "Install app-owned users package manifest for workspace tenancy.",
226
+ "category": "users-core",
227
+ "id": "users-core-users-package-json-workspace",
228
+ "when": {
229
+ "config": "tenancyMode",
230
+ "in": [
231
+ "personal",
232
+ "workspaces"
233
+ ]
234
+ }
235
+ },
236
+ {
237
+ "from": "templates/packages/users/src/server/UsersProvider.js",
238
+ "to": "packages/users/src/server/UsersProvider.js",
239
+ "ownership": "app",
240
+ "preserveOnRemove": true,
241
+ "reason": "Install app-owned users CRUD provider for non-workspace tenancy.",
242
+ "category": "users-core",
243
+ "id": "users-core-users-provider-base",
244
+ "when": {
245
+ "config": "tenancyMode",
246
+ "notIn": [
247
+ "personal",
248
+ "workspaces"
249
+ ]
250
+ }
251
+ },
252
+ {
253
+ "from": "templates/packages/users-workspace/src/server/UsersProvider.js",
254
+ "to": "packages/users/src/server/UsersProvider.js",
255
+ "ownership": "app",
256
+ "preserveOnRemove": true,
257
+ "reason": "Install app-owned users CRUD provider for workspace tenancy.",
258
+ "category": "users-core",
259
+ "id": "users-core-users-provider-workspace",
260
+ "when": {
261
+ "config": "tenancyMode",
262
+ "in": [
263
+ "personal",
264
+ "workspaces"
265
+ ]
266
+ }
267
+ },
268
+ {
269
+ "from": "templates/packages/users/src/server/actions.js",
270
+ "to": "packages/users/src/server/actions.js",
271
+ "ownership": "app",
272
+ "preserveOnRemove": true,
273
+ "reason": "Install app-owned users CRUD actions for non-workspace tenancy.",
274
+ "category": "users-core",
275
+ "id": "users-core-users-actions-base",
276
+ "when": {
277
+ "config": "tenancyMode",
278
+ "notIn": [
279
+ "personal",
280
+ "workspaces"
281
+ ]
282
+ }
283
+ },
284
+ {
285
+ "from": "templates/packages/users-workspace/src/server/actions.js",
286
+ "to": "packages/users/src/server/actions.js",
287
+ "ownership": "app",
288
+ "preserveOnRemove": true,
289
+ "reason": "Install app-owned users CRUD actions for workspace tenancy.",
290
+ "category": "users-core",
291
+ "id": "users-core-users-actions-workspace",
292
+ "when": {
293
+ "config": "tenancyMode",
294
+ "in": [
295
+ "personal",
296
+ "workspaces"
297
+ ]
298
+ }
299
+ },
300
+ {
301
+ "from": "templates/packages/users/src/server/registerRoutes.js",
302
+ "to": "packages/users/src/server/registerRoutes.js",
303
+ "ownership": "app",
304
+ "preserveOnRemove": true,
305
+ "reason": "Install app-owned users CRUD routes for non-workspace tenancy.",
306
+ "category": "users-core",
307
+ "id": "users-core-users-routes-base",
308
+ "when": {
309
+ "config": "tenancyMode",
310
+ "notIn": [
311
+ "personal",
312
+ "workspaces"
313
+ ]
314
+ }
315
+ },
316
+ {
317
+ "from": "templates/packages/users-workspace/src/server/registerRoutes.js",
318
+ "to": "packages/users/src/server/registerRoutes.js",
319
+ "ownership": "app",
320
+ "preserveOnRemove": true,
321
+ "reason": "Install app-owned users CRUD routes for workspace tenancy.",
322
+ "category": "users-core",
323
+ "id": "users-core-users-routes-workspace",
324
+ "when": {
325
+ "config": "tenancyMode",
326
+ "in": [
327
+ "personal",
328
+ "workspaces"
329
+ ]
330
+ }
331
+ },
332
+ {
333
+ "from": "templates/packages/users/src/server/repository.js",
334
+ "to": "packages/users/src/server/repository.js",
335
+ "ownership": "app",
336
+ "preserveOnRemove": true,
337
+ "reason": "Install app-owned users CRUD repository.",
338
+ "category": "users-core",
339
+ "id": "users-core-users-repository"
340
+ },
341
+ {
342
+ "from": "templates/packages/users/src/server/service.js",
343
+ "to": "packages/users/src/server/service.js",
344
+ "ownership": "app",
345
+ "preserveOnRemove": true,
346
+ "reason": "Install app-owned users CRUD service.",
347
+ "category": "users-core",
348
+ "id": "users-core-users-service"
349
+ },
350
+ {
351
+ "from": "templates/packages/users/src/shared/index.js",
352
+ "to": "packages/users/src/shared/index.js",
353
+ "ownership": "app",
354
+ "preserveOnRemove": true,
355
+ "reason": "Install app-owned users shared entrypoint.",
356
+ "category": "users-core",
357
+ "id": "users-core-users-shared-index"
358
+ },
359
+ {
360
+ "from": "templates/packages/users/src/shared/userResource.js",
361
+ "to": "packages/users/src/shared/userResource.js",
362
+ "ownership": "app",
363
+ "preserveOnRemove": true,
364
+ "reason": "Install app-owned users shared resource.",
365
+ "category": "users-core",
366
+ "id": "users-core-users-resource"
367
+ }
368
+ ],
369
+ "text": [
370
+ {
371
+ "op": "append-text",
372
+ "file": "config/server.js",
373
+ "position": "bottom",
374
+ "skipIfContains": "config.auth.profileMode = \"users\";",
375
+ "value": "\nconfig.auth ||= {};\nconfig.auth.profileMode = \"users\";\n",
376
+ "reason": "Enable users-backed auth profile sync when users-core is installed.",
377
+ "category": "runtime-config",
378
+ "id": "users-core-auth-profile-mode"
379
+ }
380
+ ]
381
+ }
23
382
  }
24
383
  }
@@ -1,24 +1,11 @@
1
1
  import {
2
2
  normalizeDbRecordId,
3
- toInsertDateTime,
4
- toNullableDateTime,
5
3
  toIsoString,
6
4
  createWithTransaction
7
5
  } from "@jskit-ai/database-runtime/shared";
8
6
  import { isDuplicateEntryError } from "@jskit-ai/database-runtime/shared/duplicateEntry";
9
7
  import { normalizeLowerText, normalizeRecordId, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
10
8
 
11
- function nowDb() {
12
- return toInsertDateTime();
13
- }
14
-
15
- function toNullableIso(value) {
16
- if (!value) {
17
- return null;
18
- }
19
- return toIsoString(value);
20
- }
21
-
22
9
  function uniqueSorted(values) {
23
10
  return [...new Set(values)].sort((left, right) => String(left).localeCompare(String(right)));
24
11
  }
@@ -43,15 +30,12 @@ function toDbJson(value, fallback = {}) {
43
30
  }
44
31
 
45
32
  export {
46
- toNullableDateTime,
47
33
  toIsoString,
48
34
  isDuplicateEntryError,
49
35
  normalizeText,
50
36
  normalizeLowerText,
51
37
  normalizeRecordId,
52
38
  normalizeDbRecordId,
53
- nowDb,
54
- toNullableIso,
55
39
  uniqueSorted,
56
40
  parseJson,
57
41
  toDbJson,
@@ -5,7 +5,6 @@ import {
5
5
  normalizeLowerText,
6
6
  normalizeRecordId,
7
7
  normalizeText,
8
- nowDb,
9
8
  toIsoString
10
9
  } from "./repositoryUtils.js";
11
10
  import {
@@ -89,7 +88,7 @@ function normalizeCreatePayload(payload = {}) {
89
88
  normalized.avatarVersion = normalizeNullableVersion(source.avatarVersion);
90
89
  }
91
90
  if (Object.hasOwn(source, "avatarUpdatedAt")) {
92
- normalized.avatarUpdatedAt = source.avatarUpdatedAt == null ? null : new Date(source.avatarUpdatedAt);
91
+ normalized.avatarUpdatedAt = source.avatarUpdatedAt == null ? null : toIsoString(source.avatarUpdatedAt);
93
92
  }
94
93
 
95
94
  return normalized;
@@ -238,7 +237,7 @@ function createRepository({ api, knex } = {}) {
238
237
  RESOURCE_TYPE,
239
238
  {
240
239
  displayName,
241
- updatedAt: new Date()
240
+ updatedAt: new Date().toISOString()
242
241
  },
243
242
  {
244
243
  id: normalizedUserId
@@ -265,8 +264,8 @@ function createRepository({ api, knex } = {}) {
265
264
  {
266
265
  avatarStorageKey: avatar.avatarStorageKey ?? null,
267
266
  avatarVersion: avatar.avatarVersion ?? null,
268
- avatarUpdatedAt: avatar.avatarUpdatedAt ?? nowDb(),
269
- updatedAt: new Date()
267
+ avatarUpdatedAt: toIsoString(avatar.avatarUpdatedAt ?? new Date()),
268
+ updatedAt: new Date().toISOString()
270
269
  },
271
270
  {
272
271
  id: normalizedUserId
@@ -294,7 +293,7 @@ function createRepository({ api, knex } = {}) {
294
293
  avatarStorageKey: null,
295
294
  avatarVersion: null,
296
295
  avatarUpdatedAt: null,
297
- updatedAt: new Date()
296
+ updatedAt: new Date().toISOString()
298
297
  },
299
298
  {
300
299
  id: normalizedUserId
@@ -350,7 +349,7 @@ function createRepository({ api, knex } = {}) {
350
349
  email,
351
350
  displayName,
352
351
  username,
353
- updatedAt: new Date()
352
+ updatedAt: new Date().toISOString()
354
353
  },
355
354
  {
356
355
  id: normalizeDbRecordId(existing.id, { fallback: null })
@@ -378,7 +377,7 @@ function createRepository({ api, knex } = {}) {
378
377
  email,
379
378
  displayName,
380
379
  username,
381
- createdAt: new Date()
380
+ createdAt: new Date().toISOString()
382
381
  }),
383
382
  transaction: trx
384
383
  },
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  normalizeRecordId,
3
- nowDb,
4
3
  isDuplicateEntryError,
5
4
  createWithTransaction
6
5
  } from "./repositoryUtils.js";
@@ -146,7 +145,7 @@ function createRepository({ api, knex } = {}) {
146
145
  RESOURCE_TYPE,
147
146
  {
148
147
  ...updatePayload,
149
- updatedAt: nowDb()
148
+ updatedAt: new Date().toISOString()
150
149
  },
151
150
  {
152
151
  id: normalizedUserId
@@ -5,5 +5,78 @@
5
5
  "type": "module",
6
6
  "exports": {
7
7
  "./shared": "./src/shared/index.js"
8
+ },
9
+ "description": "App-local CRUD package (users).",
10
+ "jskit": {
11
+ "kind": "runtime",
12
+ "capabilities": {
13
+ "provides": [
14
+ "crud.users"
15
+ ],
16
+ "requires": [
17
+ "runtime.actions",
18
+ "runtime.database",
19
+ "auth.policy",
20
+ "json-rest-api.core"
21
+ ]
22
+ },
23
+ "runtime": {
24
+ "server": {
25
+ "providers": [
26
+ {
27
+ "entrypoint": "src/server/UsersProvider.js",
28
+ "export": "UsersProvider"
29
+ }
30
+ ]
31
+ }
32
+ },
33
+ "metadata": {
34
+ "jskit": {
35
+ "scaffoldShape": "users-core-crud-v1",
36
+ "tableOwnership": {
37
+ "tables": [
38
+ {
39
+ "tableName": "users",
40
+ "provenance": "users-core-template",
41
+ "ownerKind": "baseline-crud"
42
+ }
43
+ ]
44
+ }
45
+ },
46
+ "apiSummary": {
47
+ "surfaces": [
48
+ {
49
+ "subpath": "./shared",
50
+ "summary": "App-local CRUD shared resource."
51
+ }
52
+ ],
53
+ "containerTokens": {
54
+ "server": [
55
+ "repository.users",
56
+ "crud.users"
57
+ ]
58
+ }
59
+ }
60
+ },
61
+ "mutations": {
62
+ "dependencies": {
63
+ "runtime": {},
64
+ "dev": {}
65
+ },
66
+ "packageJson": {
67
+ "scripts": {}
68
+ },
69
+ "procfile": {},
70
+ "files": []
71
+ }
72
+ },
73
+ "dependencies": {
74
+ "@jskit-ai/auth-core": "0.1.146",
75
+ "@jskit-ai/crud-core": "0.1.159",
76
+ "@jskit-ai/database-runtime": "0.1.148",
77
+ "@jskit-ai/http-runtime": "0.1.146",
78
+ "@jskit-ai/json-rest-api-core": "0.1.92",
79
+ "@jskit-ai/resource-crud-core": "0.1.90",
80
+ "@jskit-ai/users-core": "0.1.161"
8
81
  }
9
82
  }
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "@local/users",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "exports": {
7
+ "./shared": "./src/shared/index.js"
8
+ },
9
+ "description": "App-local CRUD package (users).",
10
+ "jskit": {
11
+ "kind": "runtime",
12
+ "capabilities": {
13
+ "provides": [
14
+ "crud.users"
15
+ ],
16
+ "requires": [
17
+ "runtime.actions",
18
+ "runtime.database",
19
+ "auth.policy",
20
+ "json-rest-api.core"
21
+ ]
22
+ },
23
+ "runtime": {
24
+ "server": {
25
+ "providers": [
26
+ {
27
+ "entrypoint": "src/server/UsersProvider.js",
28
+ "export": "UsersProvider"
29
+ }
30
+ ]
31
+ }
32
+ },
33
+ "metadata": {
34
+ "jskit": {
35
+ "scaffoldShape": "users-core-crud-v1",
36
+ "tableOwnership": {
37
+ "tables": [
38
+ {
39
+ "tableName": "users",
40
+ "provenance": "users-core-template",
41
+ "ownerKind": "baseline-crud"
42
+ }
43
+ ]
44
+ }
45
+ },
46
+ "apiSummary": {
47
+ "surfaces": [
48
+ {
49
+ "subpath": "./shared",
50
+ "summary": "App-local CRUD shared resource."
51
+ }
52
+ ],
53
+ "containerTokens": {
54
+ "server": [
55
+ "repository.users",
56
+ "crud.users"
57
+ ]
58
+ }
59
+ }
60
+ },
61
+ "mutations": {
62
+ "dependencies": {
63
+ "runtime": {},
64
+ "dev": {}
65
+ },
66
+ "packageJson": {
67
+ "scripts": {}
68
+ },
69
+ "procfile": {},
70
+ "files": []
71
+ }
72
+ },
73
+ "dependencies": {
74
+ "@jskit-ai/auth-core": "0.1.146",
75
+ "@jskit-ai/crud-core": "0.1.159",
76
+ "@jskit-ai/database-runtime": "0.1.148",
77
+ "@jskit-ai/http-runtime": "0.1.146",
78
+ "@jskit-ai/json-rest-api-core": "0.1.92",
79
+ "@jskit-ai/resource-crud-core": "0.1.90",
80
+ "@jskit-ai/users-core": "0.1.161",
81
+ "@jskit-ai/workspaces-core": "0.1.126"
82
+ }
83
+ }
@@ -235,9 +235,9 @@ test("userProfilesRepository.upsert patches existing profiles with resource-back
235
235
  assert.equal(attributes.email, "ada.renamed@example.com");
236
236
  assert.equal(attributes.displayName, "Ada Renamed");
237
237
  assert.equal(attributes.username, "ada");
238
- assert.ok(attributes.updatedAt instanceof Date);
238
+ assert.equal(typeof attributes.updatedAt, "string");
239
239
  assert.equal(record?.id, "7");
240
- assert.equal(record?.updatedAt, attributes.updatedAt.toISOString());
240
+ assert.equal(record?.updatedAt, attributes.updatedAt);
241
241
  });
242
242
 
243
243
  test("userProfilesRepository profile patch helpers stamp updatedAt through the resource contract", async () => {
@@ -285,7 +285,7 @@ test("userProfilesRepository profile patch helpers stamp updatedAt through the r
285
285
  assert.equal(calls.length, 3);
286
286
  for (const call of calls) {
287
287
  assert.equal(call.transaction, trx);
288
- assert.ok(call.inputRecord?.data?.attributes?.updatedAt instanceof Date);
288
+ assert.equal(typeof call.inputRecord?.data?.attributes?.updatedAt, "string");
289
289
  }
290
290
  assert.deepEqual(Object.keys(calls[0].inputRecord.data.attributes).sort(), ["displayName", "updatedAt"]);
291
291
  assert.deepEqual(Object.keys(calls[1].inputRecord.data.attributes).sort(), [
@@ -296,7 +296,7 @@ test("userProfilesRepository profile patch helpers stamp updatedAt through the r
296
296
  ]);
297
297
  assert.equal(calls[1].inputRecord.data.attributes.avatarStorageKey, "avatars/7.png");
298
298
  assert.equal(calls[1].inputRecord.data.attributes.avatarVersion, "v1");
299
- assert.equal(calls[1].inputRecord.data.attributes.avatarUpdatedAt, avatarUpdatedAt);
299
+ assert.equal(calls[1].inputRecord.data.attributes.avatarUpdatedAt, avatarUpdatedAt.toISOString());
300
300
  assert.deepEqual(calls[2].inputRecord.data.attributes.avatarStorageKey, null);
301
301
  assert.deepEqual(calls[2].inputRecord.data.attributes.avatarVersion, null);
302
302
  assert.deepEqual(calls[2].inputRecord.data.attributes.avatarUpdatedAt, null);
@@ -3,7 +3,9 @@ import { readFile } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import test from "node:test";
5
5
  import { fileURLToPath, pathToFileURL } from "node:url";
6
- import descriptor from "../package.descriptor.mjs";
6
+ import packageJson from "../package.json" with { type: "json" };
7
+
8
+ const packageMetadata = packageJson.jskit;
7
9
  import crudCorePackage from "../../crud-core/package.json" with { type: "json" };
8
10
  import resourceCrudCorePackage from "../../resource-crud-core/package.json" with { type: "json" };
9
11
 
@@ -11,21 +13,20 @@ const TEST_DIRECTORY = path.dirname(fileURLToPath(import.meta.url));
11
13
  const PACKAGE_ROOT = path.resolve(TEST_DIRECTORY, "..");
12
14
 
13
15
  function readFileMutationById(id) {
14
- return descriptor.mutations.files.find((entry) => entry.id === id) || null;
16
+ return packageMetadata.mutations.files.find((entry) => entry.id === id) || null;
15
17
  }
16
18
 
17
19
  test("users-core installs the app-local users package scaffold", () => {
18
- assert.equal(descriptor.mutations.dependencies.runtime["@local/users"], "file:packages/users");
19
- assert.equal(descriptor.mutations.dependencies.runtime["@jskit-ai/crud-core"], crudCorePackage.version);
20
+ assert.equal(packageMetadata.mutations.dependencies.runtime["@local/users"], "file:packages/users");
21
+ assert.equal(packageMetadata.mutations.dependencies.runtime["@jskit-ai/crud-core"], crudCorePackage.version);
20
22
  assert.equal(
21
- descriptor.mutations.dependencies.runtime["@jskit-ai/resource-crud-core"],
23
+ packageMetadata.mutations.dependencies.runtime["@jskit-ai/resource-crud-core"],
22
24
  resourceCrudCorePackage.version
23
25
  );
24
26
 
25
27
  const expectedFileIds = [
26
- "users-core-users-package-json",
27
- "users-core-users-package-descriptor-base",
28
- "users-core-users-package-descriptor-workspace",
28
+ "users-core-users-package-json-base",
29
+ "users-core-users-package-json-workspace",
29
30
  "users-core-users-provider-base",
30
31
  "users-core-users-provider-workspace",
31
32
  "users-core-users-actions-base",
@@ -82,8 +83,8 @@ test("users-core installs all users profile schema migrations", async () => {
82
83
  });
83
84
 
84
85
  test("users-core base users package templates stay aligned with non-workspace apps", async () => {
85
- const packageDescriptorSource = await readFile(
86
- path.join(PACKAGE_ROOT, "templates/packages/users/package.descriptor.mjs"),
86
+ const packageManifestSource = await readFile(
87
+ path.join(PACKAGE_ROOT, "templates/packages/users/package.json"),
87
88
  "utf8"
88
89
  );
89
90
  const providerSource = await readFile(
@@ -107,13 +108,13 @@ test("users-core base users package templates stay aligned with non-workspace ap
107
108
  "utf8"
108
109
  );
109
110
 
110
- assert.doesNotMatch(packageDescriptorSource, /@jskit-ai\/workspaces-core/);
111
- assert.match(packageDescriptorSource, /@jskit-ai\/json-rest-api-core/);
112
- assert.match(packageDescriptorSource, /json-rest-api\.core/);
113
- assert.match(packageDescriptorSource, /scaffoldShape: "users-core-crud-v1"/);
114
- assert.match(packageDescriptorSource, /tableName: "users"/);
115
- assert.match(packageDescriptorSource, /provenance: "users-core-template"/);
116
- assert.doesNotMatch(packageDescriptorSource, /server\/actionIds/);
111
+ assert.doesNotMatch(packageManifestSource, /@jskit-ai\/workspaces-core/);
112
+ assert.match(packageManifestSource, /@jskit-ai\/json-rest-api-core/);
113
+ assert.match(packageManifestSource, /json-rest-api\.core/);
114
+ assert.match(packageManifestSource, /"scaffoldShape": "users-core-crud-v1"/);
115
+ assert.match(packageManifestSource, /"tableName": "users"/);
116
+ assert.match(packageManifestSource, /"provenance": "users-core-template"/);
117
+ assert.doesNotMatch(packageManifestSource, /server\/actionIds/);
117
118
  assert.match(providerSource, /surface: "home"/);
118
119
  assert.doesNotMatch(providerSource, /routeSurfaceRequiresWorkspace/);
119
120
  assert.doesNotMatch(providerSource, /createCrudLookup/);
@@ -155,8 +156,8 @@ test("users-core base users package templates stay aligned with non-workspace ap
155
156
  });
156
157
 
157
158
  test("users-core workspace users package templates stay aligned with workspace apps", async () => {
158
- const packageDescriptorSource = await readFile(
159
- path.join(PACKAGE_ROOT, "templates/packages/users-workspace/package.descriptor.mjs"),
159
+ const packageManifestSource = await readFile(
160
+ path.join(PACKAGE_ROOT, "templates/packages/users-workspace/package.json"),
160
161
  "utf8"
161
162
  );
162
163
  const providerSource = await readFile(
@@ -176,13 +177,13 @@ test("users-core workspace users package templates stay aligned with workspace a
176
177
  "utf8"
177
178
  );
178
179
 
179
- assert.match(packageDescriptorSource, /@jskit-ai\/workspaces-core/);
180
- assert.match(packageDescriptorSource, /@jskit-ai\/json-rest-api-core/);
181
- assert.match(packageDescriptorSource, /json-rest-api\.core/);
182
- assert.match(packageDescriptorSource, /scaffoldShape: "users-core-crud-v1"/);
183
- assert.match(packageDescriptorSource, /tableName: "users"/);
184
- assert.match(packageDescriptorSource, /provenance: "users-core-template"/);
185
- assert.doesNotMatch(packageDescriptorSource, /server\/actionIds/);
180
+ assert.match(packageManifestSource, /@jskit-ai\/workspaces-core/);
181
+ assert.match(packageManifestSource, /@jskit-ai\/json-rest-api-core/);
182
+ assert.match(packageManifestSource, /json-rest-api\.core/);
183
+ assert.match(packageManifestSource, /"scaffoldShape": "users-core-crud-v1"/);
184
+ assert.match(packageManifestSource, /"tableName": "users"/);
185
+ assert.match(packageManifestSource, /"provenance": "users-core-template"/);
186
+ assert.doesNotMatch(packageManifestSource, /server\/actionIds/);
186
187
  assert.match(providerSource, /surface: "admin"/);
187
188
  assert.match(providerSource, /routeSurfaceRequiresWorkspace/);
188
189
  assert.doesNotMatch(providerSource, /createCrudLookup/);
@@ -1,357 +0,0 @@
1
- export default Object.freeze({
2
- packageVersion: 1,
3
- packageId: "@jskit-ai/users-core",
4
- version: "0.1.160",
5
- kind: "runtime",
6
- description: "Users/account runtime plus HTTP routes for account features.",
7
- dependsOn: [
8
- "@jskit-ai/auth-core",
9
- "@jskit-ai/database-runtime",
10
- "@jskit-ai/http-runtime",
11
- "@jskit-ai/json-rest-api-core",
12
- "@jskit-ai/resource-core",
13
- "@jskit-ai/resource-crud-core",
14
- "@jskit-ai/uploads-runtime",
15
- "@jskit-ai/storage-runtime"
16
- ],
17
- capabilities: {
18
- provides: [
19
- "users.core",
20
- "users.server-routes"
21
- ],
22
- requires: [
23
- "runtime.actions",
24
- "runtime.database",
25
- "json-rest-api.core",
26
- "runtime.storage",
27
- "runtime.uploads",
28
- "auth.provider",
29
- "auth.policy"
30
- ]
31
- },
32
- runtime: {
33
- server: {
34
- providers: [
35
- {
36
- entrypoint: "src/server/UsersCoreServiceProvider.js",
37
- export: "UsersCoreServiceProvider"
38
- }
39
- ]
40
- },
41
- client: {
42
- providers: []
43
- }
44
- },
45
- metadata: {
46
- jskit: {
47
- tableOwnership: {
48
- tables: [
49
- {
50
- tableName: "user_settings",
51
- provenance: "runtime-package",
52
- ownerKind: "package-runtime"
53
- }
54
- ]
55
- }
56
- },
57
- apiSummary: {
58
- surfaces: [
59
- {
60
- subpath: "./server",
61
- summary:
62
- "Exports UsersCoreServiceProvider plus account feature route registration modules and action definitions."
63
- },
64
- {
65
- subpath: "./shared",
66
- summary: "Exports shared users settings resources and defaults."
67
- },
68
- {
69
- subpath: "./client",
70
- summary: "Exports no runtime API today (reserved client entrypoint)."
71
- }
72
- ],
73
- containerTokens: {
74
- server: [
75
- "users.profile.sync.service",
76
- "auth.profile.projector"
77
- ],
78
- client: []
79
- }
80
- },
81
- server: {
82
- routes: [
83
- {
84
- method: "GET",
85
- path: "/api/settings",
86
- summary: "Get authenticated user settings."
87
- },
88
- {
89
- method: "PATCH",
90
- path: "/api/settings/profile",
91
- summary: "Update profile settings."
92
- },
93
- {
94
- method: "GET",
95
- path: "/api/settings/profile/avatar",
96
- summary: "Read authenticated user's uploaded avatar."
97
- },
98
- {
99
- method: "POST",
100
- path: "/api/settings/profile/avatar",
101
- summary: "Upload profile avatar."
102
- },
103
- {
104
- method: "DELETE",
105
- path: "/api/settings/profile/avatar",
106
- summary: "Delete profile avatar."
107
- },
108
- {
109
- method: "PATCH",
110
- path: "/api/settings/preferences",
111
- summary: "Update user preferences."
112
- },
113
- {
114
- method: "PATCH",
115
- path: "/api/settings/notifications",
116
- summary: "Update notification settings."
117
- },
118
- {
119
- method: "POST",
120
- path: "/api/settings/security/change-password",
121
- summary: "Set or change password for authenticated user."
122
- },
123
- {
124
- method: "PATCH",
125
- path: "/api/settings/security/methods/password",
126
- summary: "Enable or disable password sign-in method."
127
- },
128
- {
129
- method: "GET",
130
- path: "/api/settings/security/oauth/:provider/start",
131
- summary: "Start linking an OAuth provider for authenticated user."
132
- },
133
- {
134
- method: "DELETE",
135
- path: "/api/settings/security/oauth/:provider",
136
- summary: "Unlink an OAuth provider from authenticated account."
137
- },
138
- {
139
- method: "POST",
140
- path: "/api/settings/security/logout-others",
141
- summary: "Sign out from other active sessions."
142
- }
143
- ]
144
- }
145
- },
146
- mutations: {
147
- dependencies: {
148
- runtime: {
149
- "@jskit-ai/auth-core": "0.1.145",
150
- "@jskit-ai/crud-core": "0.1.157",
151
- "@jskit-ai/database-runtime": "0.1.146",
152
- "@jskit-ai/http-runtime": "0.1.145",
153
- "@jskit-ai/json-rest-api-core": "0.1.91",
154
- "@jskit-ai/kernel": "0.1.146",
155
- "@jskit-ai/resource-core": "0.1.89",
156
- "@jskit-ai/resource-crud-core": "0.1.89",
157
- "@local/users": "file:packages/users",
158
- "@jskit-ai/uploads-runtime": "0.1.122"
159
- },
160
- dev: {}
161
- },
162
- packageJson: {
163
- scripts: {}
164
- },
165
- procfile: {},
166
- files: [
167
- {
168
- op: "install-migration",
169
- from: "templates/migrations/users_core_generic_initial.cjs",
170
- toDir: "migrations",
171
- extension: ".cjs",
172
- reason: "Install users/account core schema migration.",
173
- category: "migration",
174
- id: "users-core-generic-initial-schema"
175
- },
176
- {
177
- op: "install-migration",
178
- from: "templates/migrations/users_core_profile_username.cjs",
179
- toDir: "migrations",
180
- extension: ".cjs",
181
- reason: "Install users profile username migration.",
182
- category: "migration",
183
- id: "users-core-profile-username-schema"
184
- },
185
- {
186
- op: "install-migration",
187
- from: "templates/migrations/users_core_profile_updated_at.cjs",
188
- toDir: "migrations",
189
- extension: ".cjs",
190
- reason: "Install users profile updated-at migration.",
191
- category: "migration",
192
- id: "users-core-profile-updated-at-schema"
193
- },
194
- {
195
- from: "templates/packages/users/package.json",
196
- to: "packages/users/package.json",
197
- ownership: "app",
198
- preserveOnRemove: true,
199
- reason: "Install app-owned users package manifest.",
200
- category: "users-core",
201
- id: "users-core-users-package-json"
202
- },
203
- {
204
- from: "templates/packages/users/package.descriptor.mjs",
205
- to: "packages/users/package.descriptor.mjs",
206
- ownership: "app",
207
- preserveOnRemove: true,
208
- reason: "Install app-owned users package descriptor for non-workspace tenancy.",
209
- category: "users-core",
210
- id: "users-core-users-package-descriptor-base",
211
- when: {
212
- config: "tenancyMode",
213
- notIn: ["personal", "workspaces"]
214
- }
215
- },
216
- {
217
- from: "templates/packages/users-workspace/package.descriptor.mjs",
218
- to: "packages/users/package.descriptor.mjs",
219
- ownership: "app",
220
- preserveOnRemove: true,
221
- reason: "Install app-owned users package descriptor for workspace tenancy.",
222
- category: "users-core",
223
- id: "users-core-users-package-descriptor-workspace",
224
- when: {
225
- config: "tenancyMode",
226
- in: ["personal", "workspaces"]
227
- }
228
- },
229
- {
230
- from: "templates/packages/users/src/server/UsersProvider.js",
231
- to: "packages/users/src/server/UsersProvider.js",
232
- ownership: "app",
233
- preserveOnRemove: true,
234
- reason: "Install app-owned users CRUD provider for non-workspace tenancy.",
235
- category: "users-core",
236
- id: "users-core-users-provider-base",
237
- when: {
238
- config: "tenancyMode",
239
- notIn: ["personal", "workspaces"]
240
- }
241
- },
242
- {
243
- from: "templates/packages/users-workspace/src/server/UsersProvider.js",
244
- to: "packages/users/src/server/UsersProvider.js",
245
- ownership: "app",
246
- preserveOnRemove: true,
247
- reason: "Install app-owned users CRUD provider for workspace tenancy.",
248
- category: "users-core",
249
- id: "users-core-users-provider-workspace",
250
- when: {
251
- config: "tenancyMode",
252
- in: ["personal", "workspaces"]
253
- }
254
- },
255
- {
256
- from: "templates/packages/users/src/server/actions.js",
257
- to: "packages/users/src/server/actions.js",
258
- ownership: "app",
259
- preserveOnRemove: true,
260
- reason: "Install app-owned users CRUD actions for non-workspace tenancy.",
261
- category: "users-core",
262
- id: "users-core-users-actions-base",
263
- when: {
264
- config: "tenancyMode",
265
- notIn: ["personal", "workspaces"]
266
- }
267
- },
268
- {
269
- from: "templates/packages/users-workspace/src/server/actions.js",
270
- to: "packages/users/src/server/actions.js",
271
- ownership: "app",
272
- preserveOnRemove: true,
273
- reason: "Install app-owned users CRUD actions for workspace tenancy.",
274
- category: "users-core",
275
- id: "users-core-users-actions-workspace",
276
- when: {
277
- config: "tenancyMode",
278
- in: ["personal", "workspaces"]
279
- }
280
- },
281
- {
282
- from: "templates/packages/users/src/server/registerRoutes.js",
283
- to: "packages/users/src/server/registerRoutes.js",
284
- ownership: "app",
285
- preserveOnRemove: true,
286
- reason: "Install app-owned users CRUD routes for non-workspace tenancy.",
287
- category: "users-core",
288
- id: "users-core-users-routes-base",
289
- when: {
290
- config: "tenancyMode",
291
- notIn: ["personal", "workspaces"]
292
- }
293
- },
294
- {
295
- from: "templates/packages/users-workspace/src/server/registerRoutes.js",
296
- to: "packages/users/src/server/registerRoutes.js",
297
- ownership: "app",
298
- preserveOnRemove: true,
299
- reason: "Install app-owned users CRUD routes for workspace tenancy.",
300
- category: "users-core",
301
- id: "users-core-users-routes-workspace",
302
- when: {
303
- config: "tenancyMode",
304
- in: ["personal", "workspaces"]
305
- }
306
- },
307
- {
308
- from: "templates/packages/users/src/server/repository.js",
309
- to: "packages/users/src/server/repository.js",
310
- ownership: "app",
311
- preserveOnRemove: true,
312
- reason: "Install app-owned users CRUD repository.",
313
- category: "users-core",
314
- id: "users-core-users-repository"
315
- },
316
- {
317
- from: "templates/packages/users/src/server/service.js",
318
- to: "packages/users/src/server/service.js",
319
- ownership: "app",
320
- preserveOnRemove: true,
321
- reason: "Install app-owned users CRUD service.",
322
- category: "users-core",
323
- id: "users-core-users-service"
324
- },
325
- {
326
- from: "templates/packages/users/src/shared/index.js",
327
- to: "packages/users/src/shared/index.js",
328
- ownership: "app",
329
- preserveOnRemove: true,
330
- reason: "Install app-owned users shared entrypoint.",
331
- category: "users-core",
332
- id: "users-core-users-shared-index"
333
- },
334
- {
335
- from: "templates/packages/users/src/shared/userResource.js",
336
- to: "packages/users/src/shared/userResource.js",
337
- ownership: "app",
338
- preserveOnRemove: true,
339
- reason: "Install app-owned users shared resource.",
340
- category: "users-core",
341
- id: "users-core-users-resource"
342
- }
343
- ],
344
- text: [
345
- {
346
- op: "append-text",
347
- file: "config/server.js",
348
- position: "bottom",
349
- skipIfContains: "config.auth.profileMode = \"users\";",
350
- value: "\nconfig.auth ||= {};\nconfig.auth.profileMode = \"users\";\n",
351
- reason: "Enable users-backed auth profile sync when users-core is installed.",
352
- category: "runtime-config",
353
- id: "users-core-auth-profile-mode"
354
- },
355
- ]
356
- }
357
- });
@@ -1,76 +0,0 @@
1
- export default Object.freeze({
2
- packageVersion: 1,
3
- packageId: "@local/users",
4
- version: "0.1.0",
5
- kind: "runtime",
6
- description: "App-local CRUD package (users).",
7
- dependsOn: [
8
- "@jskit-ai/auth-core",
9
- "@jskit-ai/crud-core",
10
- "@jskit-ai/database-runtime",
11
- "@jskit-ai/http-runtime",
12
- "@jskit-ai/json-rest-api-core",
13
- "@jskit-ai/resource-crud-core",
14
- "@jskit-ai/users-core"
15
- ],
16
- capabilities: {
17
- provides: [
18
- "crud.users"
19
- ],
20
- requires: [
21
- "runtime.actions",
22
- "runtime.database",
23
- "auth.policy",
24
- "json-rest-api.core"
25
- ]
26
- },
27
- runtime: {
28
- server: {
29
- providers: [
30
- {
31
- entrypoint: "src/server/UsersProvider.js",
32
- export: "UsersProvider"
33
- }
34
- ]
35
- }
36
- },
37
- metadata: {
38
- jskit: {
39
- scaffoldShape: "users-core-crud-v1",
40
- tableOwnership: {
41
- tables: [
42
- {
43
- tableName: "users",
44
- provenance: "users-core-template",
45
- ownerKind: "baseline-crud"
46
- }
47
- ]
48
- }
49
- },
50
- apiSummary: {
51
- surfaces: [
52
- {
53
- subpath: "./shared",
54
- summary: "App-local CRUD shared resource."
55
- }
56
- ],
57
- containerTokens: {
58
- server: [
59
- "repository.users",
60
- "crud.users"
61
- ]
62
- }
63
- }
64
- },
65
- mutations: {
66
- dependencies: {
67
- runtime: {},
68
- dev: {}
69
- },
70
- packageJson: {
71
- scripts: {}
72
- },
73
- procfile: {},
74
- files: []
75
- }
76
- });
@@ -1,77 +0,0 @@
1
- export default Object.freeze({
2
- packageVersion: 1,
3
- packageId: "@local/users",
4
- version: "0.1.0",
5
- kind: "runtime",
6
- description: "App-local CRUD package (users).",
7
- dependsOn: [
8
- "@jskit-ai/auth-core",
9
- "@jskit-ai/crud-core",
10
- "@jskit-ai/database-runtime",
11
- "@jskit-ai/http-runtime",
12
- "@jskit-ai/json-rest-api-core",
13
- "@jskit-ai/resource-crud-core",
14
- "@jskit-ai/users-core",
15
- "@jskit-ai/workspaces-core"
16
- ],
17
- capabilities: {
18
- provides: [
19
- "crud.users"
20
- ],
21
- requires: [
22
- "runtime.actions",
23
- "runtime.database",
24
- "auth.policy",
25
- "json-rest-api.core"
26
- ]
27
- },
28
- runtime: {
29
- server: {
30
- providers: [
31
- {
32
- entrypoint: "src/server/UsersProvider.js",
33
- export: "UsersProvider"
34
- }
35
- ]
36
- }
37
- },
38
- metadata: {
39
- jskit: {
40
- scaffoldShape: "users-core-crud-v1",
41
- tableOwnership: {
42
- tables: [
43
- {
44
- tableName: "users",
45
- provenance: "users-core-template",
46
- ownerKind: "baseline-crud"
47
- }
48
- ]
49
- }
50
- },
51
- apiSummary: {
52
- surfaces: [
53
- {
54
- subpath: "./shared",
55
- summary: "App-local CRUD shared resource."
56
- }
57
- ],
58
- containerTokens: {
59
- server: [
60
- "repository.users",
61
- "crud.users"
62
- ]
63
- }
64
- }
65
- },
66
- mutations: {
67
- dependencies: {
68
- runtime: {},
69
- dev: {}
70
- },
71
- packageJson: {
72
- scripts: {}
73
- },
74
- procfile: {},
75
- files: []
76
- }
77
- });