@jskit-ai/users-core 0.1.68 → 0.1.70

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.
@@ -1,7 +1,7 @@
1
1
  export default Object.freeze({
2
2
  packageVersion: 1,
3
3
  packageId: "@jskit-ai/users-core",
4
- version: "0.1.68",
4
+ version: "0.1.70",
5
5
  kind: "runtime",
6
6
  description: "Users/account runtime plus HTTP routes for account features.",
7
7
  dependsOn: [
@@ -132,16 +132,16 @@ export default Object.freeze({
132
132
  mutations: {
133
133
  dependencies: {
134
134
  runtime: {
135
- "@jskit-ai/auth-core": "0.1.57",
136
- "@jskit-ai/crud-core": "0.1.66",
137
- "@jskit-ai/database-runtime": "0.1.58",
138
- "@jskit-ai/http-runtime": "0.1.57",
139
- "@jskit-ai/json-rest-api-core": "0.1.3",
140
- "@jskit-ai/kernel": "0.1.58",
141
- "@jskit-ai/resource-core": "0.1.3",
142
- "@jskit-ai/resource-crud-core": "0.1.3",
135
+ "@jskit-ai/auth-core": "0.1.59",
136
+ "@jskit-ai/crud-core": "0.1.68",
137
+ "@jskit-ai/database-runtime": "0.1.60",
138
+ "@jskit-ai/http-runtime": "0.1.59",
139
+ "@jskit-ai/json-rest-api-core": "0.1.5",
140
+ "@jskit-ai/kernel": "0.1.60",
141
+ "@jskit-ai/resource-core": "0.1.5",
142
+ "@jskit-ai/resource-crud-core": "0.1.5",
143
143
  "@local/users": "file:packages/users",
144
- "@jskit-ai/uploads-runtime": "0.1.36"
144
+ "@jskit-ai/uploads-runtime": "0.1.38"
145
145
  },
146
146
  dev: {}
147
147
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/users-core",
3
- "version": "0.1.68",
3
+ "version": "0.1.70",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "node --test"
@@ -11,14 +11,14 @@
11
11
  "./shared/resources/userSettingsResource": "./src/shared/resources/userSettingsResource.js"
12
12
  },
13
13
  "dependencies": {
14
- "@jskit-ai/auth-core": "0.1.57",
15
- "@jskit-ai/database-runtime": "0.1.58",
16
- "@jskit-ai/http-runtime": "0.1.57",
17
- "@jskit-ai/json-rest-api-core": "0.1.3",
18
- "@jskit-ai/kernel": "0.1.58",
19
- "@jskit-ai/resource-crud-core": "0.1.3",
20
- "@jskit-ai/resource-core": "0.1.3",
21
- "@jskit-ai/uploads-runtime": "0.1.36",
14
+ "@jskit-ai/auth-core": "0.1.59",
15
+ "@jskit-ai/database-runtime": "0.1.60",
16
+ "@jskit-ai/http-runtime": "0.1.59",
17
+ "@jskit-ai/json-rest-api-core": "0.1.5",
18
+ "@jskit-ai/kernel": "0.1.60",
19
+ "@jskit-ai/resource-crud-core": "0.1.5",
20
+ "@jskit-ai/resource-core": "0.1.5",
21
+ "@jskit-ai/uploads-runtime": "0.1.38",
22
22
  "json-rest-schema": "1.x.x"
23
23
  }
24
24
  }
@@ -11,7 +11,7 @@ import {
11
11
  import {
12
12
  createJsonApiInputRecord,
13
13
  createJsonRestContext,
14
- simplifyJsonApiDocument
14
+ extractJsonRestCollectionRows
15
15
  } from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
16
16
  import { normalizeIdentity } from "../support/identity.js";
17
17
 
@@ -142,18 +142,18 @@ async function resolveUniqueUsername(api, baseUsername, { excludeUserId = null,
142
142
 
143
143
  for (let suffix = 0; suffix < 1000; suffix += 1) {
144
144
  const candidate = buildUsernameCandidate(baseUsername, suffix);
145
- const result = await api.resources.userProfiles.query({
146
- queryParams: {
147
- filters: {
148
- username: candidate
149
- }
150
- },
151
- transaction,
152
- simplified: false
153
- });
154
-
155
- const existingRows = simplifyJsonApiDocument(result);
156
- const existing = Array.isArray(existingRows) ? existingRows[0] || null : null;
145
+ const existingRows = extractJsonRestCollectionRows(
146
+ await api.resources.userProfiles.query({
147
+ queryParams: {
148
+ filters: {
149
+ username: candidate
150
+ }
151
+ },
152
+ transaction,
153
+ simplified: true
154
+ })
155
+ );
156
+ const existing = existingRows[0] || null;
157
157
  const existingId = normalizeDbRecordId(existing?.id, { fallback: null });
158
158
  if (!existing || existingId === normalizedExcludeUserId) {
159
159
  return candidate;
@@ -174,19 +174,20 @@ function createRepository({ api, knex } = {}) {
174
174
  const withTransaction = createWithTransaction(knex);
175
175
 
176
176
  async function queryFirst(filters = {}, options = {}) {
177
- const result = await api.resources.userProfiles.query(
178
- {
179
- queryParams: {
180
- filters
177
+ const rows = extractJsonRestCollectionRows(
178
+ await api.resources.userProfiles.query(
179
+ {
180
+ queryParams: {
181
+ filters
182
+ },
183
+ transaction: options?.trx || null,
184
+ simplified: true
181
185
  },
182
- transaction: options?.trx || null,
183
- simplified: false
184
- },
185
- createJsonRestContext(options?.context || null)
186
+ createJsonRestContext(options?.context || null)
187
+ )
186
188
  );
187
189
 
188
- const rows = simplifyJsonApiDocument(result);
189
- return Array.isArray(rows) ? rows[0] || null : null;
190
+ return rows[0] || null;
190
191
  }
191
192
 
192
193
  async function findById(userId, options = {}) {
@@ -242,13 +243,12 @@ function createRepository({ api, knex } = {}) {
242
243
  id: normalizedUserId
243
244
  }
244
245
  ),
245
- transaction: options?.trx || null,
246
- simplified: false
246
+ transaction: options?.trx || null
247
247
  },
248
248
  createJsonRestContext(options?.context || null)
249
249
  );
250
250
 
251
- return normalizeProfileRecord(simplifyJsonApiDocument(updated));
251
+ return normalizeProfileRecord(updated);
252
252
  }
253
253
 
254
254
  async function updateAvatarById(userId, avatar = {}, options = {}) {
@@ -271,13 +271,12 @@ function createRepository({ api, knex } = {}) {
271
271
  id: normalizedUserId
272
272
  }
273
273
  ),
274
- transaction: options?.trx || null,
275
- simplified: false
274
+ transaction: options?.trx || null
276
275
  },
277
276
  createJsonRestContext(options?.context || null)
278
277
  );
279
278
 
280
- return normalizeProfileRecord(simplifyJsonApiDocument(updated));
279
+ return normalizeProfileRecord(updated);
281
280
  }
282
281
 
283
282
  async function clearAvatarById(userId, options = {}) {
@@ -300,13 +299,12 @@ function createRepository({ api, knex } = {}) {
300
299
  id: normalizedUserId
301
300
  }
302
301
  ),
303
- transaction: options?.trx || null,
304
- simplified: false
302
+ transaction: options?.trx || null
305
303
  },
306
304
  createJsonRestContext(options?.context || null)
307
305
  );
308
306
 
309
- return normalizeProfileRecord(simplifyJsonApiDocument(updated));
307
+ return normalizeProfileRecord(updated);
310
308
  }
311
309
 
312
310
  async function upsert(profileLike = {}, options = {}) {
@@ -357,13 +355,12 @@ function createRepository({ api, knex } = {}) {
357
355
  id: normalizeDbRecordId(existing.id, { fallback: null })
358
356
  }
359
357
  ),
360
- transaction: trx,
361
- simplified: false
358
+ transaction: trx
362
359
  },
363
360
  createJsonRestContext(options?.context || null)
364
361
  );
365
362
 
366
- return normalizeProfileRecord(simplifyJsonApiDocument(updated));
363
+ return normalizeProfileRecord(updated);
367
364
  }
368
365
 
369
366
  const username = await resolveUniqueUsername(
@@ -382,13 +379,12 @@ function createRepository({ api, knex } = {}) {
382
379
  username,
383
380
  createdAt: new Date()
384
381
  }),
385
- transaction: trx,
386
- simplified: false
382
+ transaction: trx
387
383
  },
388
384
  createJsonRestContext(options?.context || null)
389
385
  );
390
386
 
391
- return normalizeProfileRecord(simplifyJsonApiDocument(created));
387
+ return normalizeProfileRecord(created);
392
388
  } catch (error) {
393
389
  if (duplicateTargetsEmail(error)) {
394
390
  throw createDuplicateEmailConflictError();
@@ -7,7 +7,7 @@ import {
7
7
  import {
8
8
  createJsonApiInputRecord,
9
9
  createJsonRestContext,
10
- simplifyJsonApiDocument
10
+ extractJsonRestCollectionRows
11
11
  } from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
12
12
  import { DEFAULT_USER_SETTINGS } from "../../../shared/settings.js";
13
13
 
@@ -67,19 +67,20 @@ function createRepository({ api, knex } = {}) {
67
67
  const withTransaction = createWithTransaction(knex);
68
68
 
69
69
  async function queryFirst(filters = {}, options = {}) {
70
- const result = await api.resources.userSettings.query(
71
- {
72
- queryParams: {
73
- filters
70
+ const rows = extractJsonRestCollectionRows(
71
+ await api.resources.userSettings.query(
72
+ {
73
+ queryParams: {
74
+ filters
75
+ },
76
+ transaction: options?.trx || null,
77
+ simplified: true
74
78
  },
75
- transaction: options?.trx || null,
76
- simplified: false
77
- },
78
- createJsonRestContext(options?.context || null)
79
+ createJsonRestContext(options?.context || null)
80
+ )
79
81
  );
80
82
 
81
- const rows = simplifyJsonApiDocument(result);
82
- return Array.isArray(rows) ? rows[0] || null : null;
83
+ return rows[0] || null;
83
84
  }
84
85
 
85
86
  async function findByUserId(userId, options = {}) {
@@ -112,8 +113,7 @@ function createRepository({ api, knex } = {}) {
112
113
  id: normalizedUserId
113
114
  }
114
115
  ),
115
- transaction: options?.trx || null,
116
- simplified: false
116
+ transaction: options?.trx || null
117
117
  },
118
118
  createJsonRestContext(options?.context || null)
119
119
  );
@@ -152,8 +152,7 @@ function createRepository({ api, knex } = {}) {
152
152
  id: normalizedUserId
153
153
  }
154
154
  ),
155
- transaction: options?.trx || null,
156
- simplified: false
155
+ transaction: options?.trx || null
157
156
  },
158
157
  createJsonRestContext(options?.context || null)
159
158
  );
@@ -16,18 +16,24 @@ function createKnexStub() {
16
16
  return knex;
17
17
  }
18
18
 
19
+ function asCollectionDocument(rows = []) {
20
+ return {
21
+ data: Array.isArray(rows) ? rows : []
22
+ };
23
+ }
24
+
19
25
  test("users-core repositories expose withTransaction", async () => {
20
26
  const knex = createKnexStub();
21
27
  const api = {
22
28
  resources: {
23
29
  userProfiles: {
24
30
  async query() {
25
- return { data: [] };
31
+ return asCollectionDocument([]);
26
32
  }
27
33
  },
28
34
  userSettings: {
29
35
  async query() {
30
- return { data: [] };
36
+ return asCollectionDocument([]);
31
37
  }
32
38
  }
33
39
  }
@@ -54,23 +60,12 @@ function createUserProfilesApiStub(expectedRecord) {
54
60
  userProfiles: {
55
61
  async query({ queryParams }) {
56
62
  calls.push(queryParams?.filters || {});
57
- return {
58
- data: expectedRecord ? [{
59
- type: "userProfiles",
60
- id: String(expectedRecord.id),
61
- attributes: {
62
- authProvider: expectedRecord.authProvider,
63
- authProviderUserSid: expectedRecord.authProviderUserSid,
64
- email: expectedRecord.email,
65
- username: expectedRecord.username,
66
- displayName: expectedRecord.displayName,
67
- avatarStorageKey: expectedRecord.avatarStorageKey,
68
- avatarVersion: expectedRecord.avatarVersion,
69
- avatarUpdatedAt: expectedRecord.avatarUpdatedAt,
70
- createdAt: expectedRecord.createdAt
71
- }
63
+ return asCollectionDocument(
64
+ expectedRecord ? [{
65
+ ...expectedRecord,
66
+ id: String(expectedRecord.id)
72
67
  }] : []
73
- };
68
+ );
74
69
  }
75
70
  }
76
71
  }
@@ -89,28 +84,20 @@ test("userSettingsRepository.ensureForUserId sends transaction outside simplifie
89
84
  userSettings: {
90
85
  async query() {
91
86
  queryCount += 1;
92
- return queryCount < 2
93
- ? { data: [] }
94
- : {
95
- data: [{
96
- type: "userSettings",
87
+ return asCollectionDocument(
88
+ queryCount < 2
89
+ ? []
90
+ : [{
97
91
  id: "7",
98
- attributes: {
99
- ...DEFAULT_USER_SETTINGS
100
- }
92
+ ...DEFAULT_USER_SETTINGS
101
93
  }]
102
- };
94
+ );
103
95
  },
104
96
  async post(params) {
105
97
  postParams = params;
106
98
  return {
107
- data: {
108
- type: "userSettings",
109
- id: "7",
110
- attributes: {
111
- ...DEFAULT_USER_SETTINGS
112
- }
113
- }
99
+ id: "7",
100
+ ...DEFAULT_USER_SETTINGS
114
101
  };
115
102
  }
116
103
  }
@@ -120,7 +107,7 @@ test("userSettingsRepository.ensureForUserId sends transaction outside simplifie
120
107
 
121
108
  const record = await repository.ensureForUserId("7", { trx });
122
109
 
123
- assert.equal(postParams?.simplified, false);
110
+ assert.equal(postParams?.simplified, undefined);
124
111
  assert.equal(postParams?.transaction, trx);
125
112
  assert.deepEqual(postParams?.inputRecord?.data, {
126
113
  type: "userSettings",
@@ -144,32 +131,27 @@ test("userProfilesRepository.upsert sends native JSON:API write documents with t
144
131
  async query({ queryParams }) {
145
132
  const filters = queryParams?.filters || {};
146
133
  if (Object.hasOwn(filters, "authProvider") || Object.hasOwn(filters, "authProviderUserSid")) {
147
- return { data: [] };
134
+ return asCollectionDocument([]);
148
135
  }
149
136
  if (Object.hasOwn(filters, "username")) {
150
- return { data: [] };
137
+ return asCollectionDocument([]);
151
138
  }
152
- return { data: [] };
139
+ return asCollectionDocument([]);
153
140
  },
154
141
  async post(params) {
155
142
  postParams = params;
156
143
  const attributes = params.inputRecord?.data?.attributes || {};
157
144
  return {
158
- data: {
159
- type: "userProfiles",
160
- id: "11",
161
- attributes: {
162
- authProvider: attributes.authProvider,
163
- authProviderUserSid: attributes.authProviderUserSid,
164
- email: attributes.email,
165
- username: attributes.username,
166
- displayName: attributes.displayName,
167
- avatarStorageKey: null,
168
- avatarVersion: null,
169
- avatarUpdatedAt: null,
170
- createdAt: attributes.createdAt
171
- }
172
- }
145
+ id: "11",
146
+ authProvider: attributes.authProvider,
147
+ authProviderUserSid: attributes.authProviderUserSid,
148
+ email: attributes.email,
149
+ username: attributes.username,
150
+ displayName: attributes.displayName,
151
+ avatarStorageKey: null,
152
+ avatarVersion: null,
153
+ avatarUpdatedAt: null,
154
+ createdAt: attributes.createdAt
173
155
  };
174
156
  }
175
157
  }
@@ -184,7 +166,7 @@ test("userProfilesRepository.upsert sends native JSON:API write documents with t
184
166
  displayName: "Ada Example"
185
167
  }, { trx });
186
168
 
187
- assert.equal(postParams?.simplified, false);
169
+ assert.equal(postParams?.simplified, undefined);
188
170
  assert.equal(postParams?.transaction, trx);
189
171
  assert.equal(postParams?.inputRecord?.transaction, undefined);
190
172
  assert.equal(postParams?.inputRecord?.data?.type, "userProfiles");
@@ -218,6 +200,37 @@ test("userProfilesRepository.findByEmail normalizes email lookup", async () => {
218
200
  assert.equal(profile?.displayName, "Ada Example");
219
201
  });
220
202
 
203
+ test("userProfilesRepository.findByIdentity reads existing profiles from collection documents", async () => {
204
+ const { api, calls } = createUserProfilesApiStub({
205
+ id: 7,
206
+ authProvider: "supabase",
207
+ authProviderUserSid: "supabase-user-7",
208
+ email: "ada@example.com",
209
+ username: "ada",
210
+ displayName: "Ada Example",
211
+ avatarStorageKey: null,
212
+ avatarVersion: null,
213
+ avatarUpdatedAt: null,
214
+ createdAt: "2026-04-20T00:00:00.000Z"
215
+ });
216
+ const repository = createUserProfilesRepository({
217
+ api,
218
+ knex: createKnexStub()
219
+ });
220
+
221
+ const profile = await repository.findByIdentity({
222
+ provider: "supabase",
223
+ providerUserId: "supabase-user-7"
224
+ });
225
+
226
+ assert.deepEqual(calls, [{
227
+ authProvider: "supabase",
228
+ authProviderUserSid: "supabase-user-7"
229
+ }]);
230
+ assert.equal(profile?.id, "7");
231
+ assert.equal(profile?.displayName, "Ada Example");
232
+ });
233
+
221
234
  test("userProfilesRepository.findByEmail returns null when the row is missing", async () => {
222
235
  const { api } = createUserProfilesApiStub(undefined);
223
236
  const repository = createUserProfilesRepository({