@jskit-ai/workspaces-core 0.1.124 → 0.1.126
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.descriptor.mjs +5 -5
- package/package.json +10 -10
- package/src/server/common/repositories/repositoryUtils.js +0 -16
- package/src/server/common/repositories/workspaceInvitesRepository.js +14 -12
- package/src/server/common/repositories/workspaceMembershipsRepository.js +10 -6
- package/src/server/common/repositories/workspacesRepository.js +4 -3
- package/src/server/workspaceSettings/workspaceSettingsRepository.js +1 -2
- package/test/workspaceInternalCrudResources.test.js +7 -7
- package/test/workspaceInvitesRepository.test.js +2 -2
- package/test/workspaceMembershipsRepository.test.js +1 -1
- package/test/workspacesRepository.test.js +3 -3
package/package.descriptor.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default Object.freeze({
|
|
2
2
|
packageVersion: 1,
|
|
3
3
|
packageId: "@jskit-ai/workspaces-core",
|
|
4
|
-
version: "0.1.
|
|
4
|
+
version: "0.1.126",
|
|
5
5
|
kind: "runtime",
|
|
6
6
|
description: "Workspace tenancy runtime plus HTTP routes, role catalog, and workspace config scaffolding.",
|
|
7
7
|
dependsOn: [
|
|
@@ -147,10 +147,10 @@ export default Object.freeze({
|
|
|
147
147
|
mutations: {
|
|
148
148
|
dependencies: {
|
|
149
149
|
runtime: {
|
|
150
|
-
"@jskit-ai/json-rest-api-core": "0.1.
|
|
151
|
-
"@jskit-ai/resource-core": "0.1.
|
|
152
|
-
"@jskit-ai/resource-crud-core": "0.1.
|
|
153
|
-
"@jskit-ai/users-core": "0.1.
|
|
150
|
+
"@jskit-ai/json-rest-api-core": "0.1.92",
|
|
151
|
+
"@jskit-ai/resource-core": "0.1.90",
|
|
152
|
+
"@jskit-ai/resource-crud-core": "0.1.90",
|
|
153
|
+
"@jskit-ai/users-core": "0.1.161"
|
|
154
154
|
},
|
|
155
155
|
dev: {}
|
|
156
156
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/workspaces-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.126",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -17,14 +17,14 @@
|
|
|
17
17
|
"./shared/resources/workspaceSettingsResource": "./src/shared/resources/workspaceSettingsResource.js"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@jskit-ai/auth-core": "0.1.
|
|
21
|
-
"@jskit-ai/database-runtime": "0.1.
|
|
22
|
-
"@jskit-ai/http-runtime": "0.1.
|
|
23
|
-
"@jskit-ai/json-rest-api-core": "0.1.
|
|
24
|
-
"@jskit-ai/kernel": "0.1.
|
|
25
|
-
"@jskit-ai/resource-crud-core": "0.1.
|
|
26
|
-
"@jskit-ai/resource-core": "0.1.
|
|
27
|
-
"@jskit-ai/users-core": "0.1.
|
|
28
|
-
"json-rest-schema": "1.
|
|
20
|
+
"@jskit-ai/auth-core": "0.1.146",
|
|
21
|
+
"@jskit-ai/database-runtime": "0.1.147",
|
|
22
|
+
"@jskit-ai/http-runtime": "0.1.146",
|
|
23
|
+
"@jskit-ai/json-rest-api-core": "0.1.92",
|
|
24
|
+
"@jskit-ai/kernel": "0.1.147",
|
|
25
|
+
"@jskit-ai/resource-crud-core": "0.1.90",
|
|
26
|
+
"@jskit-ai/resource-core": "0.1.90",
|
|
27
|
+
"@jskit-ai/users-core": "0.1.161",
|
|
28
|
+
"json-rest-schema": "^1.0.17"
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -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,
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
normalizeRecordId,
|
|
5
5
|
normalizeDbRecordId,
|
|
6
6
|
normalizeText,
|
|
7
|
-
nowDb,
|
|
8
7
|
isDuplicateEntryError,
|
|
9
8
|
toIsoString
|
|
10
9
|
} from "./repositoryUtils.js";
|
|
@@ -55,13 +54,13 @@ function normalizeInvitePatchPayload(payload = {}) {
|
|
|
55
54
|
: normalizeRecordId(source.invitedByUserId, { fallback: null });
|
|
56
55
|
}
|
|
57
56
|
if (Object.hasOwn(source, "expiresAt")) {
|
|
58
|
-
normalized.expiresAt = source.expiresAt == null ? null :
|
|
57
|
+
normalized.expiresAt = source.expiresAt == null ? null : toIsoString(source.expiresAt);
|
|
59
58
|
}
|
|
60
59
|
if (Object.hasOwn(source, "acceptedAt")) {
|
|
61
|
-
normalized.acceptedAt = source.acceptedAt == null ? null :
|
|
60
|
+
normalized.acceptedAt = source.acceptedAt == null ? null : toIsoString(source.acceptedAt);
|
|
62
61
|
}
|
|
63
62
|
if (Object.hasOwn(source, "revokedAt")) {
|
|
64
|
-
normalized.revokedAt = source.revokedAt == null ? null :
|
|
63
|
+
normalized.revokedAt = source.revokedAt == null ? null : toIsoString(source.revokedAt);
|
|
65
64
|
}
|
|
66
65
|
|
|
67
66
|
return normalized;
|
|
@@ -208,6 +207,7 @@ function createRepository({ api, knex } = {}) {
|
|
|
208
207
|
};
|
|
209
208
|
|
|
210
209
|
try {
|
|
210
|
+
const createdAt = new Date().toISOString();
|
|
211
211
|
const created = await api.resources.workspaceInvites.post(
|
|
212
212
|
{
|
|
213
213
|
inputRecord: createJsonApiInputRecord(
|
|
@@ -217,11 +217,11 @@ function createRepository({ api, knex } = {}) {
|
|
|
217
217
|
roleSid: createPayload.roleSid,
|
|
218
218
|
status: createPayload.status,
|
|
219
219
|
tokenHash: createPayload.tokenHash,
|
|
220
|
-
expiresAt: createPayload.expiresAt
|
|
220
|
+
expiresAt: createPayload.expiresAt == null ? null : toIsoString(createPayload.expiresAt),
|
|
221
221
|
acceptedAt: null,
|
|
222
222
|
revokedAt: null,
|
|
223
|
-
createdAt
|
|
224
|
-
updatedAt:
|
|
223
|
+
createdAt,
|
|
224
|
+
updatedAt: createdAt
|
|
225
225
|
},
|
|
226
226
|
{
|
|
227
227
|
relationships: createInviteRelationships({
|
|
@@ -281,7 +281,7 @@ function createRepository({ api, knex } = {}) {
|
|
|
281
281
|
RESOURCE_TYPE,
|
|
282
282
|
{
|
|
283
283
|
status: patch.status,
|
|
284
|
-
updatedAt:
|
|
284
|
+
updatedAt: new Date().toISOString()
|
|
285
285
|
},
|
|
286
286
|
{
|
|
287
287
|
id: row.id
|
|
@@ -300,14 +300,15 @@ function createRepository({ api, knex } = {}) {
|
|
|
300
300
|
return;
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
+
const acceptedAt = new Date().toISOString();
|
|
303
304
|
await api.resources.workspaceInvites.patch(
|
|
304
305
|
{
|
|
305
306
|
inputRecord: createJsonApiInputRecord(
|
|
306
307
|
RESOURCE_TYPE,
|
|
307
308
|
{
|
|
308
309
|
status: "accepted",
|
|
309
|
-
acceptedAt
|
|
310
|
-
updatedAt:
|
|
310
|
+
acceptedAt,
|
|
311
|
+
updatedAt: acceptedAt
|
|
311
312
|
},
|
|
312
313
|
{
|
|
313
314
|
id: normalizedInviteId
|
|
@@ -325,14 +326,15 @@ function createRepository({ api, knex } = {}) {
|
|
|
325
326
|
return;
|
|
326
327
|
}
|
|
327
328
|
|
|
329
|
+
const revokedAt = new Date().toISOString();
|
|
328
330
|
await api.resources.workspaceInvites.patch(
|
|
329
331
|
{
|
|
330
332
|
inputRecord: createJsonApiInputRecord(
|
|
331
333
|
RESOURCE_TYPE,
|
|
332
334
|
{
|
|
333
335
|
status: "revoked",
|
|
334
|
-
revokedAt
|
|
335
|
-
updatedAt:
|
|
336
|
+
revokedAt,
|
|
337
|
+
updatedAt: revokedAt
|
|
336
338
|
},
|
|
337
339
|
{
|
|
338
340
|
id: normalizedInviteId
|
|
@@ -136,6 +136,7 @@ function createRepository({ api, knex } = {}) {
|
|
|
136
136
|
const existing = await findByWorkspaceIdAndUserId(normalizedWorkspaceId, normalizedUserId, options);
|
|
137
137
|
if (existing) {
|
|
138
138
|
if (existing.roleSid !== OWNER_ROLE_ID || existing.status !== "active") {
|
|
139
|
+
const updatedAt = new Date().toISOString();
|
|
139
140
|
await api.resources.workspaceMemberships.patch(
|
|
140
141
|
{
|
|
141
142
|
inputRecord: createJsonApiInputRecord(
|
|
@@ -143,7 +144,7 @@ function createRepository({ api, knex } = {}) {
|
|
|
143
144
|
{
|
|
144
145
|
roleSid: OWNER_ROLE_ID,
|
|
145
146
|
status: "active",
|
|
146
|
-
updatedAt
|
|
147
|
+
updatedAt
|
|
147
148
|
},
|
|
148
149
|
{
|
|
149
150
|
id: existing.id
|
|
@@ -158,6 +159,7 @@ function createRepository({ api, knex } = {}) {
|
|
|
158
159
|
}
|
|
159
160
|
|
|
160
161
|
try {
|
|
162
|
+
const createdAt = new Date().toISOString();
|
|
161
163
|
await api.resources.workspaceMemberships.post(
|
|
162
164
|
{
|
|
163
165
|
inputRecord: createJsonApiInputRecord(
|
|
@@ -165,8 +167,8 @@ function createRepository({ api, knex } = {}) {
|
|
|
165
167
|
{
|
|
166
168
|
roleSid: OWNER_ROLE_ID,
|
|
167
169
|
status: "active",
|
|
168
|
-
createdAt
|
|
169
|
-
updatedAt:
|
|
170
|
+
createdAt,
|
|
171
|
+
updatedAt: createdAt
|
|
170
172
|
},
|
|
171
173
|
{
|
|
172
174
|
relationships: createMembershipRelationships({
|
|
@@ -205,6 +207,7 @@ function createRepository({ api, knex } = {}) {
|
|
|
205
207
|
|
|
206
208
|
if (!existing) {
|
|
207
209
|
try {
|
|
210
|
+
const createdAt = new Date().toISOString();
|
|
208
211
|
await api.resources.workspaceMemberships.post(
|
|
209
212
|
{
|
|
210
213
|
inputRecord: createJsonApiInputRecord(
|
|
@@ -212,8 +215,8 @@ function createRepository({ api, knex } = {}) {
|
|
|
212
215
|
{
|
|
213
216
|
roleSid,
|
|
214
217
|
status,
|
|
215
|
-
createdAt
|
|
216
|
-
updatedAt:
|
|
218
|
+
createdAt,
|
|
219
|
+
updatedAt: createdAt
|
|
217
220
|
},
|
|
218
221
|
{
|
|
219
222
|
relationships: createMembershipRelationships({
|
|
@@ -234,6 +237,7 @@ function createRepository({ api, knex } = {}) {
|
|
|
234
237
|
return findByWorkspaceIdAndUserId(normalizedWorkspaceId, normalizedUserId, options);
|
|
235
238
|
}
|
|
236
239
|
|
|
240
|
+
const updatedAt = new Date().toISOString();
|
|
237
241
|
await api.resources.workspaceMemberships.patch(
|
|
238
242
|
{
|
|
239
243
|
inputRecord: createJsonApiInputRecord(
|
|
@@ -241,7 +245,7 @@ function createRepository({ api, knex } = {}) {
|
|
|
241
245
|
{
|
|
242
246
|
roleSid,
|
|
243
247
|
status,
|
|
244
|
-
updatedAt
|
|
248
|
+
updatedAt
|
|
245
249
|
},
|
|
246
250
|
{
|
|
247
251
|
id: existing.id
|
|
@@ -137,14 +137,15 @@ function createRepository({ api, knex } = {}) {
|
|
|
137
137
|
};
|
|
138
138
|
|
|
139
139
|
try {
|
|
140
|
+
const createdAt = new Date().toISOString();
|
|
140
141
|
const created = await api.resources.workspaces.post(
|
|
141
142
|
{
|
|
142
143
|
inputRecord: createJsonApiInputRecord(
|
|
143
144
|
RESOURCE_TYPE,
|
|
144
145
|
{
|
|
145
146
|
...createPayload,
|
|
146
|
-
createdAt
|
|
147
|
-
updatedAt:
|
|
147
|
+
createdAt,
|
|
148
|
+
updatedAt: createdAt
|
|
148
149
|
},
|
|
149
150
|
{
|
|
150
151
|
relationships: createWorkspaceRelationships({ ownerUserId })
|
|
@@ -181,7 +182,7 @@ function createRepository({ api, knex } = {}) {
|
|
|
181
182
|
const sourcePatch = patch && typeof patch === "object" && !Array.isArray(patch) ? patch : {};
|
|
182
183
|
const workspacePatch = {
|
|
183
184
|
...sourcePatch,
|
|
184
|
-
updatedAt: new Date()
|
|
185
|
+
updatedAt: new Date().toISOString()
|
|
185
186
|
};
|
|
186
187
|
const relationships = createWorkspaceRelationships(sourcePatch);
|
|
187
188
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
2
|
normalizeRecordId,
|
|
3
|
-
nowDb,
|
|
4
3
|
isDuplicateEntryError,
|
|
5
4
|
createWithTransaction
|
|
6
5
|
} from "../common/repositories/repositoryUtils.js";
|
|
@@ -142,7 +141,7 @@ function createRepository({ api, knex } = {}) {
|
|
|
142
141
|
RESOURCE_TYPE,
|
|
143
142
|
{
|
|
144
143
|
...updatePayload,
|
|
145
|
-
updatedAt:
|
|
144
|
+
updatedAt: new Date().toISOString()
|
|
146
145
|
},
|
|
147
146
|
{
|
|
148
147
|
id: normalizedWorkspaceId
|
|
@@ -48,8 +48,8 @@ test("workspace memberships derived bodies accept normalized internal writes", a
|
|
|
48
48
|
assert.equal(String(create.value.userId), "9");
|
|
49
49
|
assert.equal(create.value.roleSid, "OWNER");
|
|
50
50
|
assert.equal(create.value.status, "ACTIVE");
|
|
51
|
-
assert.equal(
|
|
52
|
-
assert.equal(
|
|
51
|
+
assert.equal(create.value.createdAt, "2026-05-02T10:11:12.000Z");
|
|
52
|
+
assert.equal(create.value.updatedAt, "2026-05-02T10:11:12.000Z");
|
|
53
53
|
|
|
54
54
|
const patch = await parseBody(workspaceMembershipsResource.operations.patch, {
|
|
55
55
|
roleSid: "ADMIN",
|
|
@@ -59,7 +59,7 @@ test("workspace memberships derived bodies accept normalized internal writes", a
|
|
|
59
59
|
assert.equal(patch.ok, true);
|
|
60
60
|
assert.equal(patch.value.roleSid, "ADMIN");
|
|
61
61
|
assert.equal(patch.value.status, "ACTIVE");
|
|
62
|
-
assert.equal(
|
|
62
|
+
assert.equal(patch.value.updatedAt, "2026-05-02T10:11:12.000Z");
|
|
63
63
|
});
|
|
64
64
|
|
|
65
65
|
test("workspace invites derived bodies keep lifecycle fields available for internal writes", async () => {
|
|
@@ -83,8 +83,8 @@ test("workspace invites derived bodies keep lifecycle fields available for inter
|
|
|
83
83
|
assert.equal(create.value.status, "PENDING");
|
|
84
84
|
assert.equal(create.value.tokenHash, "invite-token-hash");
|
|
85
85
|
assert.equal(String(create.value.invitedByUserId), "9");
|
|
86
|
-
assert.equal(
|
|
87
|
-
assert.equal(
|
|
86
|
+
assert.equal(create.value.createdAt, "2026-05-02T10:11:12.000Z");
|
|
87
|
+
assert.equal(create.value.updatedAt, "2026-05-02T10:11:12.000Z");
|
|
88
88
|
|
|
89
89
|
const patch = await parseBody(workspaceInvitesResource.operations.patch, {
|
|
90
90
|
status: "ACCEPTED",
|
|
@@ -93,6 +93,6 @@ test("workspace invites derived bodies keep lifecycle fields available for inter
|
|
|
93
93
|
});
|
|
94
94
|
assert.equal(patch.ok, true);
|
|
95
95
|
assert.equal(patch.value.status, "ACCEPTED");
|
|
96
|
-
assert.equal(
|
|
97
|
-
assert.equal(
|
|
96
|
+
assert.equal(patch.value.acceptedAt, "2026-05-11T00:00:00.000Z");
|
|
97
|
+
assert.equal(patch.value.updatedAt, "2026-05-11T00:00:00.000Z");
|
|
98
98
|
});
|
|
@@ -233,8 +233,8 @@ test("workspaceInvitesRepository.markAcceptedById uses the internal invite resou
|
|
|
233
233
|
|
|
234
234
|
const payload = state.patchPayloads[0];
|
|
235
235
|
assert.equal(payload.attributes?.status, "accepted");
|
|
236
|
-
assert.equal(typeof payload.attributes?.acceptedAt, "
|
|
237
|
-
assert.equal(
|
|
236
|
+
assert.equal(typeof payload.attributes?.acceptedAt, "string");
|
|
237
|
+
assert.equal(payload.attributes?.updatedAt, payload.attributes?.acceptedAt);
|
|
238
238
|
});
|
|
239
239
|
|
|
240
240
|
test("workspaceInvitesRepository.listPendingByWorkspaceIdWithWorkspace keeps workspace join fields outside the base resource contract", async () => {
|
|
@@ -191,7 +191,7 @@ test("workspaceMembershipsRepository.ensureOwnerMembership upgrades an existing
|
|
|
191
191
|
|
|
192
192
|
assert.equal(state.patchPayload.attributes?.roleSid, "owner");
|
|
193
193
|
assert.equal(state.patchPayload.attributes?.status, "active");
|
|
194
|
-
assert.equal(typeof state.patchPayload.attributes?.updatedAt, "
|
|
194
|
+
assert.equal(typeof state.patchPayload.attributes?.updatedAt, "string");
|
|
195
195
|
assert.deepEqual(membership, {
|
|
196
196
|
id: "11",
|
|
197
197
|
workspaceId: "7",
|
|
@@ -232,8 +232,8 @@ test("workspacesRepository.insert writes canonical fields through json-rest-api"
|
|
|
232
232
|
assert.equal(state.postPayload.attributes?.name, "TonyMobily3");
|
|
233
233
|
assert.equal(state.postPayload.attributes?.isPersonal, false);
|
|
234
234
|
assert.equal(state.postPayload.attributes?.avatarUrl, "");
|
|
235
|
-
assert.equal(typeof state.postPayload.attributes?.createdAt, "
|
|
236
|
-
assert.equal(
|
|
235
|
+
assert.equal(typeof state.postPayload.attributes?.createdAt, "string");
|
|
236
|
+
assert.equal(state.postPayload.attributes?.updatedAt, state.postPayload.attributes?.createdAt);
|
|
237
237
|
assert.equal(inserted.id, "1");
|
|
238
238
|
assert.equal(inserted.ownerUserId, "9");
|
|
239
239
|
});
|
|
@@ -290,7 +290,7 @@ test("workspacesRepository.updateById patches canonical fields and updatedAt", a
|
|
|
290
290
|
|
|
291
291
|
assert.equal(state.patchPayload.id, "7");
|
|
292
292
|
assert.equal(state.patchPayload.attributes?.name, "TonyMobily 4");
|
|
293
|
-
assert.equal(typeof state.patchPayload.attributes?.updatedAt, "
|
|
293
|
+
assert.equal(typeof state.patchPayload.attributes?.updatedAt, "string");
|
|
294
294
|
});
|
|
295
295
|
|
|
296
296
|
test("workspacesRepository.listForUserId keeps membership fields outside the canonical workspace row", async () => {
|