@jskit-ai/crud-core 0.1.144 → 0.1.146
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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default Object.freeze({
|
|
2
2
|
packageVersion: 1,
|
|
3
3
|
packageId: "@jskit-ai/crud-core",
|
|
4
|
-
version: "0.1.
|
|
4
|
+
version: "0.1.146",
|
|
5
5
|
kind: "runtime",
|
|
6
6
|
description: "Shared CRUD helpers used by CRUD modules.",
|
|
7
7
|
dependsOn: [
|
|
@@ -28,7 +28,7 @@ export default Object.freeze({
|
|
|
28
28
|
mutations: {
|
|
29
29
|
dependencies: {
|
|
30
30
|
runtime: {
|
|
31
|
-
"@jskit-ai/crud-core": "0.1.
|
|
31
|
+
"@jskit-ai/crud-core": "0.1.146"
|
|
32
32
|
},
|
|
33
33
|
dev: {}
|
|
34
34
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/crud-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.146",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -27,15 +27,15 @@
|
|
|
27
27
|
"./server/routeContracts": "./src/server/routeContracts.js"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@jskit-ai/database-runtime": "0.1.
|
|
31
|
-
"@jskit-ai/http-runtime": "0.1.
|
|
32
|
-
"@jskit-ai/kernel": "0.1.
|
|
30
|
+
"@jskit-ai/database-runtime": "0.1.135",
|
|
31
|
+
"@jskit-ai/http-runtime": "0.1.134",
|
|
32
|
+
"@jskit-ai/kernel": "0.1.136",
|
|
33
33
|
"json-rest-schema": "1.x.x",
|
|
34
|
-
"@jskit-ai/realtime": "0.1.
|
|
35
|
-
"@jskit-ai/resource-crud-core": "0.1.
|
|
36
|
-
"@jskit-ai/shell-web": "0.1.
|
|
37
|
-
"@jskit-ai/users-core": "0.1.
|
|
38
|
-
"@jskit-ai/users-web": "0.1.
|
|
34
|
+
"@jskit-ai/realtime": "0.1.133",
|
|
35
|
+
"@jskit-ai/resource-crud-core": "0.1.79",
|
|
36
|
+
"@jskit-ai/shell-web": "0.1.137",
|
|
37
|
+
"@jskit-ai/users-core": "0.1.149",
|
|
38
|
+
"@jskit-ai/users-web": "0.1.153"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@tanstack/vue-query": "^5.90.5",
|
|
@@ -25,6 +25,23 @@ const lookupIncludeQueryValidator = Object.freeze({
|
|
|
25
25
|
mode: "patch"
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
+
const jsonApiFieldsetsQueryValidator = Object.freeze({
|
|
29
|
+
schema: createSchema({
|
|
30
|
+
fields: {
|
|
31
|
+
type: "object",
|
|
32
|
+
required: false,
|
|
33
|
+
values: {
|
|
34
|
+
type: "array",
|
|
35
|
+
items: {
|
|
36
|
+
type: "string",
|
|
37
|
+
minLength: 1
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}),
|
|
42
|
+
mode: "patch"
|
|
43
|
+
});
|
|
44
|
+
|
|
28
45
|
function resolveCrudListUsesOrderedCursor(list = {}) {
|
|
29
46
|
const entries = Array.isArray(list?.orderBy)
|
|
30
47
|
? list.orderBy
|
|
@@ -88,10 +105,47 @@ function createCrudParentFilterQueryValidator(resource = {}) {
|
|
|
88
105
|
});
|
|
89
106
|
}
|
|
90
107
|
|
|
108
|
+
function createStandardCrudListQueryValidators({
|
|
109
|
+
resource = {},
|
|
110
|
+
listFilterQueryValidator = null,
|
|
111
|
+
searchQueryValidator = listSearchQueryValidator,
|
|
112
|
+
includeQueryValidator = lookupIncludeQueryValidator
|
|
113
|
+
} = {}) {
|
|
114
|
+
const resolvedListFilterQueryValidator =
|
|
115
|
+
listFilterQueryValidator
|
|
116
|
+
?? resource?.contract?.listFilters?.queryValidator
|
|
117
|
+
?? null;
|
|
118
|
+
|
|
119
|
+
return [
|
|
120
|
+
createCrudCursorPaginationQueryValidator({
|
|
121
|
+
orderBy: resource?.defaultSort
|
|
122
|
+
}),
|
|
123
|
+
searchQueryValidator,
|
|
124
|
+
createCrudParentFilterQueryValidator(resource),
|
|
125
|
+
...(resolvedListFilterQueryValidator
|
|
126
|
+
? [resolvedListFilterQueryValidator]
|
|
127
|
+
: []),
|
|
128
|
+
includeQueryValidator,
|
|
129
|
+
jsonApiFieldsetsQueryValidator
|
|
130
|
+
];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function createStandardCrudViewQueryValidators({
|
|
134
|
+
includeQueryValidator = lookupIncludeQueryValidator
|
|
135
|
+
} = {}) {
|
|
136
|
+
return [
|
|
137
|
+
includeQueryValidator,
|
|
138
|
+
jsonApiFieldsetsQueryValidator
|
|
139
|
+
];
|
|
140
|
+
}
|
|
141
|
+
|
|
91
142
|
export {
|
|
92
143
|
createCrudCursorPaginationQueryValidator,
|
|
93
144
|
listSearchQueryValidator,
|
|
94
145
|
lookupIncludeQueryValidator,
|
|
146
|
+
jsonApiFieldsetsQueryValidator,
|
|
95
147
|
resolveCrudParentFilterKeys,
|
|
96
|
-
createCrudParentFilterQueryValidator
|
|
148
|
+
createCrudParentFilterQueryValidator,
|
|
149
|
+
createStandardCrudListQueryValidators,
|
|
150
|
+
createStandardCrudViewQueryValidators
|
|
97
151
|
};
|
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
} from "@jskit-ai/kernel/shared/validators";
|
|
9
9
|
import { resolveCrudResourceScopeName } from "@jskit-ai/kernel/shared/support/crudLookup";
|
|
10
10
|
import {
|
|
11
|
-
|
|
11
|
+
createStandardCrudListQueryValidators,
|
|
12
|
+
createStandardCrudViewQueryValidators,
|
|
12
13
|
listSearchQueryValidator as defaultListSearchQueryValidator,
|
|
13
|
-
lookupIncludeQueryValidator as defaultLookupIncludeQueryValidator
|
|
14
|
-
createCrudParentFilterQueryValidator
|
|
14
|
+
lookupIncludeQueryValidator as defaultLookupIncludeQueryValidator
|
|
15
15
|
} from "./listQueryValidators.js";
|
|
16
16
|
|
|
17
17
|
function isRecord(value) {
|
|
@@ -444,18 +444,19 @@ function createCrudJsonApiRouteContracts({
|
|
|
444
444
|
lookupIncludeQueryValidator = defaultLookupIncludeQueryValidator,
|
|
445
445
|
listFilterQueryValidator = null
|
|
446
446
|
} = {}) {
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
447
|
+
const listRouteQueryValidator = composeSchemaDefinitions(
|
|
448
|
+
createStandardCrudListQueryValidators({
|
|
449
|
+
resource,
|
|
450
|
+
listFilterQueryValidator,
|
|
451
|
+
searchQueryValidator: listSearchQueryValidator,
|
|
452
|
+
includeQueryValidator: lookupIncludeQueryValidator
|
|
453
|
+
})
|
|
454
|
+
);
|
|
455
|
+
const viewRouteQueryValidator = composeSchemaDefinitions(
|
|
456
|
+
createStandardCrudViewQueryValidators({
|
|
457
|
+
includeQueryValidator: lookupIncludeQueryValidator
|
|
458
|
+
})
|
|
459
|
+
);
|
|
459
460
|
const recordRouteParamsValidator = routeParamsValidator
|
|
460
461
|
? composeSchemaDefinitions([
|
|
461
462
|
routeParamsValidator,
|
|
@@ -511,19 +512,23 @@ function createCrudJsonApiRouteContracts({
|
|
|
511
512
|
query: listRouteQueryValidator,
|
|
512
513
|
output: viewOutput,
|
|
513
514
|
outputKind: "collection",
|
|
515
|
+
includeValidation400: true,
|
|
514
516
|
outputAttributeExcludeKeys,
|
|
515
517
|
outputRelationshipEntries: viewOutputRelationships,
|
|
518
|
+
allowSparseFields: true,
|
|
516
519
|
getRecordAttributes: viewRecordAttributes,
|
|
517
520
|
getRecordRelationships: viewRecordRelationships,
|
|
518
521
|
getIncluded: viewIncluded
|
|
519
522
|
}),
|
|
520
523
|
viewRouteContract: createJsonApiResourceRouteContract({
|
|
521
524
|
type: routeType,
|
|
522
|
-
query:
|
|
525
|
+
query: viewRouteQueryValidator,
|
|
523
526
|
output: viewOutput,
|
|
524
527
|
outputKind: "record",
|
|
528
|
+
includeValidation400: true,
|
|
525
529
|
outputAttributeExcludeKeys,
|
|
526
530
|
outputRelationshipEntries: viewOutputRelationships,
|
|
531
|
+
allowSparseFields: true,
|
|
527
532
|
getRecordAttributes: viewRecordAttributes,
|
|
528
533
|
getRecordRelationships: viewRecordRelationships,
|
|
529
534
|
getIncluded: viewIncluded
|
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
lookupIncludeQueryValidator,
|
|
12
12
|
createCrudCursorPaginationQueryValidator,
|
|
13
13
|
createCrudParentFilterQueryValidator,
|
|
14
|
+
createStandardCrudListQueryValidators,
|
|
15
|
+
createStandardCrudViewQueryValidators,
|
|
14
16
|
resolveCrudParentFilterKeys
|
|
15
17
|
} from "../src/server/listQueryValidators.js";
|
|
16
18
|
|
|
@@ -254,3 +256,49 @@ test("createCrudParentFilterQueryValidator keeps parent filters optional when me
|
|
|
254
256
|
});
|
|
255
257
|
assert.deepEqual(compiled.schema.querystring.required || [], []);
|
|
256
258
|
});
|
|
259
|
+
|
|
260
|
+
test("standard CRUD query validator groups remain plain and extensible", () => {
|
|
261
|
+
const listFilterQueryValidator = {
|
|
262
|
+
schema: createSchema({
|
|
263
|
+
currentness: {
|
|
264
|
+
type: "string",
|
|
265
|
+
required: false
|
|
266
|
+
}
|
|
267
|
+
}),
|
|
268
|
+
mode: "patch"
|
|
269
|
+
};
|
|
270
|
+
const resource = {
|
|
271
|
+
...createCrudResource(),
|
|
272
|
+
defaultSort: ["-createdAt"],
|
|
273
|
+
contract: {
|
|
274
|
+
listFilters: {
|
|
275
|
+
queryValidator: listFilterQueryValidator
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const listQueryValidator = composeSchemaDefinition(
|
|
281
|
+
...createStandardCrudListQueryValidators({ resource }),
|
|
282
|
+
{
|
|
283
|
+
schema: createSchema({
|
|
284
|
+
archived: {
|
|
285
|
+
type: "boolean",
|
|
286
|
+
required: false
|
|
287
|
+
}
|
|
288
|
+
}),
|
|
289
|
+
mode: "patch"
|
|
290
|
+
}
|
|
291
|
+
);
|
|
292
|
+
assert.deepEqual(
|
|
293
|
+
Object.keys(listQueryValidator.schema.getFieldDefinitions()).sort(),
|
|
294
|
+
["archived", "currentness", "cursor", "fields", "include", "limit", "q"]
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
const viewQueryValidator = composeSchemaDefinition(
|
|
298
|
+
...createStandardCrudViewQueryValidators()
|
|
299
|
+
);
|
|
300
|
+
assert.deepEqual(
|
|
301
|
+
Object.keys(viewQueryValidator.schema.getFieldDefinitions()).sort(),
|
|
302
|
+
["fields", "include"]
|
|
303
|
+
);
|
|
304
|
+
});
|
|
@@ -177,7 +177,11 @@ test("createCrudJsonApiRouteContracts builds default CRUD JSON:API contracts", a
|
|
|
177
177
|
include: " ownerId ",
|
|
178
178
|
contactId: " 42 ",
|
|
179
179
|
cursor: " offset:2 ",
|
|
180
|
-
limit: "25"
|
|
180
|
+
limit: "25",
|
|
181
|
+
fields: {
|
|
182
|
+
contacts: ["id", "name"],
|
|
183
|
+
userProfiles: ["id", "name"]
|
|
184
|
+
}
|
|
181
185
|
}, { phase: "input" });
|
|
182
186
|
|
|
183
187
|
assert.deepEqual(normalizedListQuery, {
|
|
@@ -185,9 +189,39 @@ test("createCrudJsonApiRouteContracts builds default CRUD JSON:API contracts", a
|
|
|
185
189
|
include: "ownerId",
|
|
186
190
|
contactId: "42",
|
|
187
191
|
cursor: "offset:2",
|
|
188
|
-
limit: 25
|
|
192
|
+
limit: 25,
|
|
193
|
+
fields: {
|
|
194
|
+
contacts: ["id", "name"],
|
|
195
|
+
userProfiles: ["id", "name"]
|
|
196
|
+
}
|
|
189
197
|
});
|
|
190
198
|
|
|
199
|
+
assert.deepEqual(
|
|
200
|
+
await validateSchemaPayload(contracts.viewRouteContract.query, {
|
|
201
|
+
include: "owner",
|
|
202
|
+
fields: {
|
|
203
|
+
contacts: ["id", "name"],
|
|
204
|
+
userProfiles: ["id", "name"]
|
|
205
|
+
}
|
|
206
|
+
}, { phase: "input" }),
|
|
207
|
+
{
|
|
208
|
+
include: "owner",
|
|
209
|
+
fields: {
|
|
210
|
+
contacts: ["id", "name"],
|
|
211
|
+
userProfiles: ["id", "name"]
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
const sparseResourceSchema =
|
|
217
|
+
contracts.listRouteContract.responses[200].transportSchema.definitions.contactsSuccessResource;
|
|
218
|
+
const sparseAttributesSchemaName = sparseResourceSchema.properties.attributes.allOf[0].$ref
|
|
219
|
+
.replace("#/definitions/", "");
|
|
220
|
+
const sparseAttributesSchema =
|
|
221
|
+
contracts.listRouteContract.responses[200].transportSchema.definitions[sparseAttributesSchemaName];
|
|
222
|
+
assert.deepEqual(sparseResourceSchema.required, ["type", "id"]);
|
|
223
|
+
assert.equal(Object.hasOwn(sparseAttributesSchema, "required"), false);
|
|
224
|
+
|
|
191
225
|
const decodedCreateBody = contracts.createRouteContract.transport.request.body({
|
|
192
226
|
data: {
|
|
193
227
|
type: "contacts",
|