@kollors/deep-json-server 1.0.0-alpha.1 → 1.0.0-alpha.3
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/README.md +104 -53
- package/README.ru.md +103 -50
- package/dist/index.d.ts +5 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/src/cli.d.ts +2 -2
- package/dist/src/cli.js +97 -48
- package/dist/src/cli.js.map +1 -1
- package/dist/src/config.d.ts +14 -1
- package/dist/src/config.js +56 -8
- package/dist/src/config.js.map +1 -1
- package/dist/src/constants.d.ts +1 -0
- package/dist/src/constants.js +1 -0
- package/dist/src/constants.js.map +1 -1
- package/dist/src/database.d.ts +0 -1
- package/dist/src/database.js +12 -10
- package/dist/src/database.js.map +1 -1
- package/dist/src/engine.d.ts +21 -10
- package/dist/src/engine.js +77 -144
- package/dist/src/engine.js.map +1 -1
- package/dist/src/errors.d.ts +6 -0
- package/dist/src/errors.js +9 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/features.d.ts +8 -0
- package/dist/src/features.js +18 -0
- package/dist/src/features.js.map +1 -0
- package/dist/src/files/contract.d.ts +0 -30
- package/dist/src/files/contract.js +6 -47
- package/dist/src/files/contract.js.map +1 -1
- package/dist/src/files/disk-store.d.ts +2 -1
- package/dist/src/files/disk-store.js +28 -18
- package/dist/src/files/disk-store.js.map +1 -1
- package/dist/src/files/http.d.ts +31 -0
- package/dist/src/files/http.js +44 -0
- package/dist/src/files/http.js.map +1 -0
- package/dist/src/files/index.d.ts +1 -1
- package/dist/src/files/index.js +1 -1
- package/dist/src/files/index.js.map +1 -1
- package/dist/src/files/memory-store.js +8 -7
- package/dist/src/files/memory-store.js.map +1 -1
- package/dist/src/files/openapi.js +2 -4
- package/dist/src/files/openapi.js.map +1 -1
- package/dist/src/files/routes.js +3 -2
- package/dist/src/files/routes.js.map +1 -1
- package/dist/src/graphql/entry.d.ts +3 -0
- package/dist/src/graphql/entry.js +3 -0
- package/dist/src/graphql/entry.js.map +1 -0
- package/dist/src/graphql/preflight.d.ts +3 -0
- package/dist/src/graphql/preflight.js +30 -0
- package/dist/src/graphql/preflight.js.map +1 -0
- package/dist/src/graphql/public.d.ts +3 -0
- package/dist/src/graphql/public.js +11 -0
- package/dist/src/graphql/public.js.map +1 -0
- package/dist/src/graphql/resolvers.d.ts +8 -0
- package/dist/src/graphql/resolvers.js +38 -0
- package/dist/src/graphql/resolvers.js.map +1 -0
- package/dist/src/graphql/routes.d.ts +4 -0
- package/dist/src/graphql/routes.js +27 -0
- package/dist/src/graphql/routes.js.map +1 -0
- package/dist/src/graphql/write.d.ts +1 -0
- package/dist/src/graphql/write.js +7 -0
- package/dist/src/graphql/write.js.map +1 -0
- package/dist/src/graphql.d.ts +1 -2
- package/dist/src/graphql.js +24 -34
- package/dist/src/graphql.js.map +1 -1
- package/dist/src/http/errors.d.ts +6 -0
- package/dist/src/http/errors.js +3 -0
- package/dist/src/http/errors.js.map +1 -0
- package/dist/src/model.d.ts +2 -3
- package/dist/src/model.js +54 -12
- package/dist/src/model.js.map +1 -1
- package/dist/src/openapi/document.js +36 -29
- package/dist/src/openapi/document.js.map +1 -1
- package/dist/src/openapi/entry.d.ts +3 -0
- package/dist/src/openapi/entry.js +2 -0
- package/dist/src/openapi/entry.js.map +1 -0
- package/dist/src/openapi/helpers.d.ts +17 -0
- package/dist/src/openapi/helpers.js +4 -0
- package/dist/src/openapi/helpers.js.map +1 -0
- package/dist/src/openapi/index.js +6 -4
- package/dist/src/openapi/index.js.map +1 -1
- package/dist/src/openapi/public.d.ts +17 -0
- package/dist/src/openapi/public.js +18 -0
- package/dist/src/openapi/public.js.map +1 -0
- package/dist/src/pagination.d.ts +7 -0
- package/dist/src/pagination.js +12 -0
- package/dist/src/pagination.js.map +1 -0
- package/dist/src/paths.d.ts +6 -0
- package/dist/src/paths.js +47 -0
- package/dist/src/paths.js.map +1 -0
- package/dist/src/query/contract.d.ts +3 -0
- package/dist/src/query/contract.js +19 -0
- package/dist/src/query/contract.js.map +1 -0
- package/dist/src/query/filter.d.ts +3 -1
- package/dist/src/query/filter.js +78 -76
- package/dist/src/query/filter.js.map +1 -1
- package/dist/src/query/options.d.ts +1 -14
- package/dist/src/query/options.js +6 -109
- package/dist/src/query/options.js.map +1 -1
- package/dist/src/relation-metadata.d.ts +0 -1
- package/dist/src/relation-metadata.js +0 -1
- package/dist/src/relation-metadata.js.map +1 -1
- package/dist/src/rest/options.d.ts +14 -0
- package/dist/src/rest/options.js +108 -0
- package/dist/src/rest/options.js.map +1 -0
- package/dist/src/rest/projection.d.ts +6 -0
- package/dist/src/rest/projection.js +38 -0
- package/dist/src/rest/projection.js.map +1 -0
- package/dist/src/rest/routes.d.ts +3 -0
- package/dist/src/rest/routes.js +48 -0
- package/dist/src/rest/routes.js.map +1 -0
- package/dist/src/schema.d.ts +8 -0
- package/dist/src/schema.js +13 -0
- package/dist/src/schema.js.map +1 -0
- package/dist/src/server/public.d.ts +3 -0
- package/dist/src/server/public.js +2 -0
- package/dist/src/server/public.js.map +1 -0
- package/dist/src/server.d.ts +5 -7
- package/dist/src/server.js +86 -143
- package/dist/src/server.js.map +1 -1
- package/dist/src/utils.d.ts +0 -7
- package/dist/src/utils.js +0 -4
- package/dist/src/utils.js.map +1 -1
- package/package.json +13 -1
package/dist/src/query/filter.js
CHANGED
|
@@ -1,84 +1,86 @@
|
|
|
1
|
-
import { isEqual, isObject
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
if (
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
case 'contains':
|
|
20
|
-
return typeof field === 'string' ? field.toLowerCase().includes(String(expectedValue).toLowerCase()) : Array.isArray(field) && field.some((value) => isFilterEqual(value, expectedValue));
|
|
21
|
-
case 'endsWith':
|
|
22
|
-
return typeof field === 'string' && field.toLowerCase().endsWith(String(expectedValue).toLowerCase());
|
|
23
|
-
case 'eq':
|
|
24
|
-
return isFilterEqual(field, expectedValue);
|
|
25
|
-
case 'every':
|
|
26
|
-
return Array.isArray(field) && field.every((value) => matchesValue(value, expectedValue));
|
|
27
|
-
case 'gt':
|
|
28
|
-
return isComparable(field) && isComparable(expectedValue) && field > expectedValue;
|
|
29
|
-
case 'gte':
|
|
30
|
-
return isComparable(field) && isComparable(expectedValue) && field >= expectedValue;
|
|
31
|
-
case 'in': {
|
|
32
|
-
const expectedValues = toArray(expectedValue);
|
|
33
|
-
return Array.isArray(field)
|
|
34
|
-
? field.some((value) => expectedValues.some((expectedItem) => isFilterEqual(value, expectedItem)))
|
|
35
|
-
: expectedValues.some((expectedItem) => isFilterEqual(field, expectedItem));
|
|
1
|
+
import { isEqual, isObject } from '../utils.js';
|
|
2
|
+
import { operatorsFor } from './contract.js';
|
|
3
|
+
import { badQuery, childrenOf } from './options.js';
|
|
4
|
+
const comparable = (value) => typeof value === 'string' || typeof value === 'number';
|
|
5
|
+
const equal = (left, right) => isEqual(left, right);
|
|
6
|
+
function condition(node, input, depth) {
|
|
7
|
+
if (!isObject(input) || depth > 32)
|
|
8
|
+
badQuery('Field filter must contain operators with depth at most 32');
|
|
9
|
+
if (!node.many && (node.relation || node.base === 'object'))
|
|
10
|
+
return compileWhere(node, input, depth);
|
|
11
|
+
const operators = operatorsFor(node);
|
|
12
|
+
const predicates = Object.entries(input).map(([operator, value]) => {
|
|
13
|
+
const operand = operators[operator];
|
|
14
|
+
if (!Object.hasOwn(operators, operator))
|
|
15
|
+
badQuery(`Invalid operator ${operator} for ${node.type}`);
|
|
16
|
+
if (operand === 'condition') {
|
|
17
|
+
const nested = condition(node, value, depth + 1);
|
|
18
|
+
return (field) => !nested(field);
|
|
36
19
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
20
|
+
if (operand === 'element') {
|
|
21
|
+
const nested = condition({ ...node, many: false }, value, depth + 1);
|
|
22
|
+
return (field) => Array.isArray(field) && (operator === 'every' ? field.every(nested) : operator === 'none' ? !field.some(nested) : field.some(nested));
|
|
23
|
+
}
|
|
24
|
+
const values = operand === 'values' ? value : [value];
|
|
25
|
+
if (!Array.isArray(values))
|
|
26
|
+
badQuery(`Invalid value for ${operator}`);
|
|
27
|
+
const nullable = ['eq', 'ne', 'in'].includes(operator);
|
|
28
|
+
for (const candidate of values) {
|
|
29
|
+
if (candidate === null && nullable)
|
|
30
|
+
continue;
|
|
31
|
+
const base = operand === 'text' ? 'string' : node.base;
|
|
32
|
+
if (typeof candidate !== base || (typeof candidate === 'number' && !Number.isFinite(candidate)))
|
|
33
|
+
badQuery(`Invalid value for ${operator}`);
|
|
34
|
+
if ((operand === 'value' || operand === 'values') && node.enum && !node.enum.some((v) => equal(v, candidate)))
|
|
35
|
+
badQuery(`Invalid enum value for ${operator}`);
|
|
36
|
+
}
|
|
37
|
+
switch (operator) {
|
|
38
|
+
case 'eq':
|
|
39
|
+
return (field) => equal(field, value);
|
|
40
|
+
case 'ne':
|
|
41
|
+
return (field) => !equal(field, value);
|
|
42
|
+
case 'in':
|
|
43
|
+
return (field) => (Array.isArray(field) ? field : [field]).some((item) => values.some((v) => equal(item, v)));
|
|
44
|
+
case 'contains':
|
|
45
|
+
return (field) => (typeof field === 'string' ? field.toLowerCase().includes(String(value).toLowerCase()) : Array.isArray(field) && field.some((v) => equal(v, value)));
|
|
46
|
+
case 'startsWith':
|
|
47
|
+
return (field) => typeof field === 'string' && field.toLowerCase().startsWith(String(value).toLowerCase());
|
|
48
|
+
case 'endsWith':
|
|
49
|
+
return (field) => typeof field === 'string' && field.toLowerCase().endsWith(String(value).toLowerCase());
|
|
50
|
+
case 'gt':
|
|
51
|
+
return (field) => comparable(field) && comparable(value) && field > value;
|
|
52
|
+
case 'gte':
|
|
53
|
+
return (field) => comparable(field) && comparable(value) && field >= value;
|
|
54
|
+
case 'lt':
|
|
55
|
+
return (field) => comparable(field) && comparable(value) && field < value;
|
|
56
|
+
default:
|
|
57
|
+
return (field) => comparable(field) && comparable(value) && field <= value;
|
|
74
58
|
}
|
|
75
|
-
|
|
76
|
-
|
|
59
|
+
});
|
|
60
|
+
return (value) => predicates.every((predicate) => predicate(value));
|
|
61
|
+
}
|
|
62
|
+
export function compileWhere(node, input, depth = 0) {
|
|
63
|
+
if (!isObject(input) || depth > 32)
|
|
64
|
+
badQuery('where must be an object with depth at most 32');
|
|
65
|
+
const predicates = Object.entries(input).map(([key, value]) => {
|
|
66
|
+
if (key === 'and' || key === 'or') {
|
|
67
|
+
if (!Array.isArray(value) || (key === 'or' && !value.length))
|
|
68
|
+
badQuery(`Invalid ${key}`);
|
|
69
|
+
const nested = value.map((item) => compileWhere(node, item, depth + 1));
|
|
70
|
+
return (field) => (key === 'and' ? nested.every((p) => p(field)) : nested.some((p) => p(field)));
|
|
77
71
|
}
|
|
78
72
|
if (key === 'not') {
|
|
79
|
-
|
|
73
|
+
const nested = compileWhere(node, value, depth + 1);
|
|
74
|
+
return (field) => !nested(field);
|
|
80
75
|
}
|
|
81
|
-
|
|
76
|
+
const child = childrenOf(node)[key];
|
|
77
|
+
if (!Object.hasOwn(childrenOf(node), key) || child.writeOnly)
|
|
78
|
+
badQuery(`Unknown or inaccessible filter field ${key}`);
|
|
79
|
+
if (child.mixed)
|
|
80
|
+
badQuery(`Field ${key} has inconsistent types; provide an explicit schema`);
|
|
81
|
+
const nested = condition(child, value, depth + 1);
|
|
82
|
+
return (field) => isObject(field) && nested(Object.hasOwn(field, key) ? field[key] : undefined);
|
|
82
83
|
});
|
|
84
|
+
return (value) => isObject(value) && predicates.every((predicate) => predicate(value));
|
|
83
85
|
}
|
|
84
86
|
//# sourceMappingURL=filter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter.js","sourceRoot":"","sources":["../../../src/query/filter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"filter.js","sourceRoot":"","sources":["../../../src/query/filter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Node } from '../model.js';
|
|
2
2
|
export interface Order {
|
|
3
3
|
field: string;
|
|
4
4
|
direction: 'ASC' | 'DESC';
|
|
@@ -12,20 +12,7 @@ export interface ListOptions {
|
|
|
12
12
|
order?: Order[];
|
|
13
13
|
pager?: Pager;
|
|
14
14
|
}
|
|
15
|
-
export interface Scope {
|
|
16
|
-
[key: string]: Scope | null;
|
|
17
|
-
}
|
|
18
|
-
export interface RestOptions extends ListOptions {
|
|
19
|
-
scope: Scope;
|
|
20
|
-
nested: Record<string, ListOptions>;
|
|
21
|
-
}
|
|
22
|
-
export declare const ownScope: Scope;
|
|
23
15
|
export declare function badQuery(message: string): never;
|
|
24
|
-
export declare function parseScope(value: unknown): Scope;
|
|
25
|
-
export declare function parseRestOptions(query: unknown, list: boolean): RestOptions;
|
|
26
16
|
export declare function childrenOf(node: Node): Record<string, Node>;
|
|
27
17
|
export declare function nodeAt(node: Node, path: string, scalarOnly?: boolean): Node;
|
|
28
18
|
export declare function sortableFields(node: Node, prefix?: string): string[];
|
|
29
|
-
export declare function validateScope(node: Node, scope: Scope, depth?: number): void;
|
|
30
|
-
export declare function validateNested(root: Node, scope: Scope, nested: Record<string, ListOptions>): void;
|
|
31
|
-
export declare const selectionEntity: (entity: Entity, node: Node) => Entity;
|
|
@@ -1,77 +1,7 @@
|
|
|
1
|
+
import { domainError } from '../errors.js';
|
|
1
2
|
import { pathParts } from '../model.js';
|
|
2
|
-
import { createHttpError, isObject } from '../utils.js';
|
|
3
|
-
export const ownScope = { '*': null };
|
|
4
3
|
export function badQuery(message) {
|
|
5
|
-
throw
|
|
6
|
-
}
|
|
7
|
-
export function parseScope(value) {
|
|
8
|
-
if (value === undefined)
|
|
9
|
-
return ownScope;
|
|
10
|
-
if (typeof value !== 'string' || !value.length || value.length > 10000)
|
|
11
|
-
badQuery('scope must be a nonempty string of at most 10000 characters');
|
|
12
|
-
let index = 0;
|
|
13
|
-
function parse(depth) {
|
|
14
|
-
if (depth > 32)
|
|
15
|
-
badQuery('scope is too deep');
|
|
16
|
-
const result = Object.create(null);
|
|
17
|
-
while (index < value.length) {
|
|
18
|
-
const match = /^(\*|[A-Za-z][A-Za-z0-9_]*)/.exec(value.slice(index));
|
|
19
|
-
if (!match)
|
|
20
|
-
badQuery('Invalid scope syntax');
|
|
21
|
-
const key = match[1];
|
|
22
|
-
if (key !== '*')
|
|
23
|
-
pathParts(key);
|
|
24
|
-
index += key.length;
|
|
25
|
-
let child = null;
|
|
26
|
-
if (value[index] === '(') {
|
|
27
|
-
if (key === '*')
|
|
28
|
-
badQuery('Wildcard cannot have a selection');
|
|
29
|
-
index++;
|
|
30
|
-
child = parse(depth + 1);
|
|
31
|
-
if (value[index++] !== ')')
|
|
32
|
-
badQuery('Unclosed scope selection');
|
|
33
|
-
}
|
|
34
|
-
if (Object.hasOwn(result, key))
|
|
35
|
-
badQuery(`Duplicate scope field ${key}`);
|
|
36
|
-
result[key] = child;
|
|
37
|
-
if (value[index] !== ',')
|
|
38
|
-
break;
|
|
39
|
-
index++;
|
|
40
|
-
if (index === value.length)
|
|
41
|
-
badQuery('Trailing scope comma');
|
|
42
|
-
}
|
|
43
|
-
if (!Object.keys(result).length)
|
|
44
|
-
badQuery('Empty scope selection');
|
|
45
|
-
return result;
|
|
46
|
-
}
|
|
47
|
-
const result = parse(0);
|
|
48
|
-
if (index !== value.length)
|
|
49
|
-
badQuery('Invalid scope syntax');
|
|
50
|
-
return result;
|
|
51
|
-
}
|
|
52
|
-
export function parseRestOptions(query, list) {
|
|
53
|
-
if (!isObject(query))
|
|
54
|
-
badQuery('Invalid query');
|
|
55
|
-
const allowed = list ? ['scope', 'nested', 'where', 'order', 'pager'] : ['scope', 'nested'];
|
|
56
|
-
for (const key of Object.keys(query))
|
|
57
|
-
if (!allowed.includes(key))
|
|
58
|
-
badQuery(`Unknown query parameter ${key}`);
|
|
59
|
-
const result = { scope: parseScope(query.scope), nested: {} };
|
|
60
|
-
for (const key of ['where', 'order', 'pager', 'nested']) {
|
|
61
|
-
if (query[key] === undefined)
|
|
62
|
-
continue;
|
|
63
|
-
if (typeof query[key] !== 'string')
|
|
64
|
-
badQuery(`${key} must occur once and contain JSON`);
|
|
65
|
-
try {
|
|
66
|
-
result[key] = JSON.parse(query[key]);
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
badQuery(`Invalid JSON in ${key}`);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
if (!isObject(result.nested))
|
|
73
|
-
badQuery('nested must be an object');
|
|
74
|
-
return result;
|
|
4
|
+
throw domainError('INVALID_QUERY', message);
|
|
75
5
|
}
|
|
76
6
|
export function childrenOf(node) {
|
|
77
7
|
return node.relation?.root.children ?? node.children;
|
|
@@ -81,8 +11,10 @@ export function nodeAt(node, path, scalarOnly = false) {
|
|
|
81
11
|
const parts = pathParts(path);
|
|
82
12
|
parts.forEach((part, index) => {
|
|
83
13
|
const child = childrenOf(current)[part];
|
|
84
|
-
if (!
|
|
14
|
+
if (!Object.hasOwn(childrenOf(current), part) || child.writeOnly)
|
|
85
15
|
badQuery(`Unknown or inaccessible field ${path}`);
|
|
16
|
+
if (child.mixed)
|
|
17
|
+
badQuery(`Field ${path} has inconsistent types; provide an explicit schema`);
|
|
86
18
|
if (scalarOnly && (child.many || child.relation || (index === parts.length - 1 && child.base === 'object')))
|
|
87
19
|
badQuery(`Cannot sort by ${path}`);
|
|
88
20
|
current = child;
|
|
@@ -91,45 +23,10 @@ export function nodeAt(node, path, scalarOnly = false) {
|
|
|
91
23
|
}
|
|
92
24
|
export function sortableFields(node, prefix = '') {
|
|
93
25
|
return Object.entries(childrenOf(node)).flatMap(([key, child]) => {
|
|
94
|
-
if (child.writeOnly || child.many || child.relation)
|
|
26
|
+
if (child.writeOnly || child.mixed || child.many || child.relation)
|
|
95
27
|
return [];
|
|
96
28
|
const path = prefix + key;
|
|
97
29
|
return child.base === 'object' ? sortableFields(child, `${path}.`) : [path];
|
|
98
30
|
});
|
|
99
31
|
}
|
|
100
|
-
export function validateScope(node, scope, depth = 0) {
|
|
101
|
-
if (depth > 32)
|
|
102
|
-
badQuery('scope is too deep');
|
|
103
|
-
for (const [key, selection] of Object.entries(scope)) {
|
|
104
|
-
if (key === '*')
|
|
105
|
-
continue;
|
|
106
|
-
const child = childrenOf(node)[key];
|
|
107
|
-
if (!child || child.writeOnly)
|
|
108
|
-
badQuery(`Unknown or inaccessible scope field ${key}`);
|
|
109
|
-
if (selection) {
|
|
110
|
-
if (!child.relation && child.base !== 'object')
|
|
111
|
-
badQuery(`Scalar field ${key} cannot have a selection`);
|
|
112
|
-
validateScope(child, selection, depth + 1);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
export function validateNested(root, scope, nested) {
|
|
117
|
-
for (const [path, options] of Object.entries(nested)) {
|
|
118
|
-
const node = nodeAt(root, path);
|
|
119
|
-
if (!node.many || (!node.relation && node.base !== 'object'))
|
|
120
|
-
badQuery(`nested.${path} must address an object list`);
|
|
121
|
-
if (!isObject(options) || Object.keys(options).some((k) => !['where', 'order', 'pager'].includes(k)))
|
|
122
|
-
badQuery(`Invalid nested options at ${path}`);
|
|
123
|
-
let current = root;
|
|
124
|
-
let selection = scope;
|
|
125
|
-
for (const key of pathParts(path)) {
|
|
126
|
-
const child = childrenOf(current)[key];
|
|
127
|
-
if (!Object.hasOwn(selection, key) && !(Object.hasOwn(selection, '*') && !child.relation))
|
|
128
|
-
badQuery(`nested path ${path} is not selected by scope`);
|
|
129
|
-
selection = Object.hasOwn(selection, key) ? (selection[key] ?? ownScope) : ownScope;
|
|
130
|
-
current = child;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
export const selectionEntity = (entity, node) => node.relation ?? entity;
|
|
135
32
|
//# sourceMappingURL=options.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.js","sourceRoot":"","sources":["../../../src/query/options.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../../../src/query/options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAcxC,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,MAAM,WAAW,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;AAC9C,CAAC;AACD,MAAM,UAAU,UAAU,CAAC,IAAU;IACnC,OAAO,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC;AACvD,CAAC;AACD,MAAM,UAAU,MAAM,CAAC,IAAU,EAAE,IAAY,EAAE,UAAU,GAAG,KAAK;IACjE,IAAI,OAAO,GAAG,IAAI,CAAC;IACnB,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC9B,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAC5B,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,SAAS;YAAE,QAAQ,CAAC,iCAAiC,IAAI,EAAE,CAAC,CAAC;QACpH,IAAI,KAAK,CAAC,KAAK;YAAE,QAAQ,CAAC,SAAS,IAAI,qDAAqD,CAAC,CAAC;QAC9F,IAAI,UAAU,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,QAAQ,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;YAAE,QAAQ,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAC;QAChJ,OAAO,GAAG,KAAK,CAAC;IAClB,CAAC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC;AACD,MAAM,UAAU,cAAc,CAAC,IAAU,EAAE,MAAM,GAAG,EAAE;IACpD,OAAO,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QAC/D,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,QAAQ;YAAE,OAAO,EAAE,CAAC;QAC9E,MAAM,IAAI,GAAG,MAAM,GAAG,GAAG,CAAC;QAC1B,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,EAAE,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -5,6 +5,5 @@ export interface RelationMetadata {
|
|
|
5
5
|
sourceResource: string;
|
|
6
6
|
targetResource: string;
|
|
7
7
|
}
|
|
8
|
-
export declare const getRelationKeys: (...names: string[]) => string[];
|
|
9
8
|
export declare const resolveRelationResource: (resourceNames: string[], relation: string, sourceResource: string) => string | undefined;
|
|
10
9
|
export declare const getRelationMetadata: (key: string, resourceNames: string[], sourceResource: string) => RelationMetadata | undefined;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { singularize } from './utils.js';
|
|
2
2
|
const SELF_RELATIONS = new Set(['child', 'children', 'parent', 'parents']);
|
|
3
|
-
export const getRelationKeys = (...names) => [...new Set(names.flatMap((name) => [`${name}Id`, `${name}Ids`]))];
|
|
4
3
|
export const resolveRelationResource = (resourceNames, relation, sourceResource) => {
|
|
5
4
|
const resource = resourceNames.find((resourceName) => resourceName === relation) ?? resourceNames.find((resourceName) => singularize(resourceName) === relation);
|
|
6
5
|
if (resource != null) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relation-metadata.js","sourceRoot":"","sources":["../../src/relation-metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;AAU3E,MAAM,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"relation-metadata.js","sourceRoot":"","sources":["../../src/relation-metadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;AAU3E,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,aAAuB,EAAE,QAAgB,EAAE,cAAsB,EAAsB,EAAE;IAC/H,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,KAAK,QAAQ,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,WAAW,CAAC,YAAY,CAAC,KAAK,QAAQ,CAAC,CAAC;IAEjK,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;QACrB,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,OAAO,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,aAAa,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7G,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,GAAW,EAAE,aAAuB,EAAE,cAAsB,EAAgC,EAAE;IAChI,yEAAyE;IACzE,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAE1C,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;QAClB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC;IACnC,MAAM,MAAM,GAAG,MAAM,KAAK,KAAK,CAAC;IAChC,MAAM,YAAY,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,KAAK,QAAQ,CAAC,IAAI,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAClI,MAAM,cAAc,GAAG,uBAAuB,CAAC,aAAa,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC;IAE5F,IAAI,cAAc,IAAI,IAAI,EAAE,CAAC;QAC3B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,MAAM;QACN,YAAY;QACZ,mBAAmB,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,cAAc;QAC/F,cAAc;QACd,cAAc;KACf,CAAC;AACJ,CAAC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Node } from '../model.js';
|
|
2
|
+
import { type ListOptions } from '../query/options.js';
|
|
3
|
+
export interface Scope {
|
|
4
|
+
[key: string]: Scope | null;
|
|
5
|
+
}
|
|
6
|
+
export interface RestOptions extends ListOptions {
|
|
7
|
+
scope: Scope;
|
|
8
|
+
nested: Record<string, ListOptions>;
|
|
9
|
+
}
|
|
10
|
+
export declare const ownScope: Scope;
|
|
11
|
+
export declare function parseScope(value: unknown): Scope;
|
|
12
|
+
export declare function parseRestOptions(query: unknown, list: boolean): RestOptions;
|
|
13
|
+
export declare function validateScope(node: Node, scope: Scope, depth?: number): void;
|
|
14
|
+
export declare function validateNested(root: Node, scope: Scope, nested: Record<string, ListOptions>): void;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { pathParts } from '../model.js';
|
|
2
|
+
import { badQuery, childrenOf, nodeAt } from '../query/options.js';
|
|
3
|
+
import { isObject } from '../utils.js';
|
|
4
|
+
export const ownScope = { '*': null };
|
|
5
|
+
export function parseScope(value) {
|
|
6
|
+
if (value === undefined)
|
|
7
|
+
return ownScope;
|
|
8
|
+
if (typeof value !== 'string' || !value.length || value.length > 10000)
|
|
9
|
+
badQuery('scope must be a nonempty string of at most 10000 characters');
|
|
10
|
+
let index = 0;
|
|
11
|
+
function parse(depth) {
|
|
12
|
+
if (depth > 32)
|
|
13
|
+
badQuery('scope is too deep');
|
|
14
|
+
const result = Object.create(null);
|
|
15
|
+
while (index < value.length) {
|
|
16
|
+
const match = /^(\*|[A-Za-z][A-Za-z0-9_]*)/.exec(value.slice(index));
|
|
17
|
+
if (!match)
|
|
18
|
+
badQuery('Invalid scope syntax');
|
|
19
|
+
const key = match[1];
|
|
20
|
+
if (key !== '*')
|
|
21
|
+
pathParts(key);
|
|
22
|
+
index += key.length;
|
|
23
|
+
let child = null;
|
|
24
|
+
if (value[index] === '(') {
|
|
25
|
+
if (key === '*')
|
|
26
|
+
badQuery('Wildcard cannot have a selection');
|
|
27
|
+
index++;
|
|
28
|
+
child = parse(depth + 1);
|
|
29
|
+
if (value[index++] !== ')')
|
|
30
|
+
badQuery('Unclosed scope selection');
|
|
31
|
+
}
|
|
32
|
+
if (Object.hasOwn(result, key))
|
|
33
|
+
badQuery(`Duplicate scope field ${key}`);
|
|
34
|
+
result[key] = child;
|
|
35
|
+
if (value[index] !== ',')
|
|
36
|
+
break;
|
|
37
|
+
index++;
|
|
38
|
+
if (index === value.length)
|
|
39
|
+
badQuery('Trailing scope comma');
|
|
40
|
+
}
|
|
41
|
+
if (!Object.keys(result).length)
|
|
42
|
+
badQuery('Empty scope selection');
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
const result = parse(0);
|
|
46
|
+
if (index !== value.length)
|
|
47
|
+
badQuery('Invalid scope syntax');
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
export function parseRestOptions(query, list) {
|
|
51
|
+
if (!isObject(query))
|
|
52
|
+
badQuery('Invalid query');
|
|
53
|
+
const allowed = list ? ['scope', 'nested', 'where', 'order', 'pager'] : ['scope', 'nested'];
|
|
54
|
+
for (const key of Object.keys(query))
|
|
55
|
+
if (!allowed.includes(key))
|
|
56
|
+
badQuery(`Unknown query parameter ${key}`);
|
|
57
|
+
const result = { scope: parseScope(query.scope), nested: {} };
|
|
58
|
+
for (const key of ['where', 'order', 'pager', 'nested']) {
|
|
59
|
+
if (query[key] === undefined)
|
|
60
|
+
continue;
|
|
61
|
+
if (typeof query[key] !== 'string')
|
|
62
|
+
badQuery(`${key} must occur once and contain JSON`);
|
|
63
|
+
try {
|
|
64
|
+
result[key] = JSON.parse(query[key]);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
badQuery(`Invalid JSON in ${key}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (!isObject(result.nested))
|
|
71
|
+
badQuery('nested must be an object');
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
export function validateScope(node, scope, depth = 0) {
|
|
75
|
+
if (depth > 32)
|
|
76
|
+
badQuery('scope is too deep');
|
|
77
|
+
for (const [key, selection] of Object.entries(scope)) {
|
|
78
|
+
if (key === '*')
|
|
79
|
+
continue;
|
|
80
|
+
const child = childrenOf(node)[key];
|
|
81
|
+
if (!Object.hasOwn(childrenOf(node), key) || child.writeOnly)
|
|
82
|
+
badQuery(`Unknown or inaccessible scope field ${key}`);
|
|
83
|
+
if (selection) {
|
|
84
|
+
if (!child.relation && child.base !== 'object')
|
|
85
|
+
badQuery(`Scalar field ${key} cannot have a selection`);
|
|
86
|
+
validateScope(child, selection, depth + 1);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export function validateNested(root, scope, nested) {
|
|
91
|
+
for (const [path, options] of Object.entries(nested)) {
|
|
92
|
+
const node = nodeAt(root, path);
|
|
93
|
+
if (!node.many || (!node.relation && node.base !== 'object'))
|
|
94
|
+
badQuery(`nested.${path} must address an object list`);
|
|
95
|
+
if (!isObject(options) || Object.keys(options).some((k) => !['where', 'order', 'pager'].includes(k)))
|
|
96
|
+
badQuery(`Invalid nested options at ${path}`);
|
|
97
|
+
let current = root;
|
|
98
|
+
let selection = scope;
|
|
99
|
+
for (const key of pathParts(path)) {
|
|
100
|
+
const child = childrenOf(current)[key];
|
|
101
|
+
if (!Object.hasOwn(selection, key) && !(Object.hasOwn(selection, '*') && !child.relation))
|
|
102
|
+
badQuery(`nested path ${path} is not selected by scope`);
|
|
103
|
+
selection = Object.hasOwn(selection, key) ? (selection[key] ?? ownScope) : ownScope;
|
|
104
|
+
current = child;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../../../src/rest/options.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type Engine, type PreparedList, type Ref } from '../engine.js';
|
|
2
|
+
import type { Entity } from '../model.js';
|
|
3
|
+
import type { JsonObject } from '../types.js';
|
|
4
|
+
import { type RestOptions, type Scope } from './options.js';
|
|
5
|
+
export declare function validateRest(engine: Engine, entity: Entity, options: RestOptions): Map<string, PreparedList>;
|
|
6
|
+
export declare function project(engine: Engine, ref: Ref, scope?: Scope, nested?: RestOptions['nested'], prefix?: string, plans?: Map<string, PreparedList>): JsonObject;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { isRef, resolveField } from '../engine.js';
|
|
2
|
+
import { childrenOf, nodeAt } from '../query/options.js';
|
|
3
|
+
import { ownScope, validateNested, validateScope } from './options.js';
|
|
4
|
+
export function validateRest(engine, entity, options) {
|
|
5
|
+
validateScope(entity.root, options.scope);
|
|
6
|
+
validateNested(entity.root, options.scope, options.nested);
|
|
7
|
+
return new Map(Object.entries(options.nested).map(([path, nested]) => [path, engine.prepareOptions(nodeAt(entity.root, path), nested)]));
|
|
8
|
+
}
|
|
9
|
+
export function project(engine, ref, scope = ownScope, nested = {}, prefix = '', plans = new Map()) {
|
|
10
|
+
const output = Object.create(null);
|
|
11
|
+
const children = childrenOf(ref.node);
|
|
12
|
+
for (const [key, node] of Object.entries(children)) {
|
|
13
|
+
if (node.writeOnly || (!Object.hasOwn(scope, key) && !(Object.hasOwn(scope, '*') && !node.relation)))
|
|
14
|
+
continue;
|
|
15
|
+
const value = resolveField(ref, node);
|
|
16
|
+
const selection = Object.hasOwn(scope, key) ? (scope[key] ?? ownScope) : ownScope;
|
|
17
|
+
const path = prefix + key;
|
|
18
|
+
if (value === undefined)
|
|
19
|
+
continue;
|
|
20
|
+
if (isRef(value))
|
|
21
|
+
output[key] = project(engine, value, selection, nested, `${path}.`, plans);
|
|
22
|
+
else if (Array.isArray(value) && value.every(isRef) && (value.length > 0 || node.relation || node.base === 'object')) {
|
|
23
|
+
const prepared = plans.get(path) ?? engine.prepareOptions(node, nested[path]);
|
|
24
|
+
plans.set(path, prepared);
|
|
25
|
+
const page = engine.list(value, node, nested[path], prepared);
|
|
26
|
+
output[key] = { data: page.data.map((v) => project(engine, v, selection, nested, `${path}.`, plans)), total: page.total };
|
|
27
|
+
}
|
|
28
|
+
else
|
|
29
|
+
output[key] = structuredClone(value);
|
|
30
|
+
}
|
|
31
|
+
// Schemaless REST includes all raw fields, including fields absent in earlier records.
|
|
32
|
+
if (!ref.context.model.explicit && scope['*'] === null)
|
|
33
|
+
for (const [key, value] of Object.entries(ref.value))
|
|
34
|
+
if (!Object.hasOwn(output, key) && !children[key]?.relation)
|
|
35
|
+
output[key] = structuredClone(value);
|
|
36
|
+
return output;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=projection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projection.js","sourceRoot":"","sources":["../../../src/rest/projection.ts"],"names":[],"mappings":"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"}
|