@kollors/deep-json-server 0.9.0 → 1.0.0-alpha.10
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 +566 -347
- package/README.ru.md +560 -341
- package/dist/bin/deep-json-server.js +1 -1
- package/dist/bin/deep-json-server.js.map +1 -1
- package/dist/index.d.ts +13 -5
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/src/auth/contract.d.ts +31 -0
- package/dist/src/auth/contract.js +12 -0
- package/dist/src/auth/contract.js.map +1 -0
- package/dist/src/auth/input.d.ts +18 -0
- package/dist/src/auth/input.js +34 -0
- package/dist/src/auth/input.js.map +1 -0
- package/dist/src/auth/password.d.ts +17 -0
- package/dist/src/auth/password.js +41 -0
- package/dist/src/auth/password.js.map +1 -0
- package/dist/src/auth/public.d.ts +3 -0
- package/dist/src/auth/public.js +5 -0
- package/dist/src/auth/public.js.map +1 -0
- package/dist/src/auth/routes.d.ts +6 -0
- package/dist/src/auth/routes.js +18 -0
- package/dist/src/auth/routes.js.map +1 -0
- package/dist/src/auth/service.d.ts +78 -0
- package/dist/src/auth/service.js +218 -0
- package/dist/src/auth/service.js.map +1 -0
- package/dist/src/auth/store.d.ts +26 -0
- package/dist/src/auth/store.js +82 -0
- package/dist/src/auth/store.js.map +1 -0
- package/dist/src/cli/index.d.ts +7 -0
- package/dist/src/cli/index.js +122 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/core/config-values.d.ts +28 -0
- package/dist/src/core/config-values.js +53 -0
- package/dist/src/core/config-values.js.map +1 -0
- package/dist/src/{constants.d.ts → core/constants.d.ts} +2 -1
- package/dist/src/{constants.js → core/constants.js} +2 -1
- package/dist/src/core/constants.js.map +1 -0
- package/dist/src/core/database.d.ts +48 -0
- package/dist/src/{database.js → core/database.js} +64 -29
- package/dist/src/core/database.js.map +1 -0
- package/dist/src/core/engine.d.ts +65 -0
- package/dist/src/core/engine.js +312 -0
- package/dist/src/core/engine.js.map +1 -0
- package/dist/src/core/errors.d.ts +9 -0
- package/dist/src/core/errors.js +12 -0
- package/dist/src/core/errors.js.map +1 -0
- package/dist/src/core/http-errors.d.ts +9 -0
- package/dist/src/core/http-errors.js +15 -0
- package/dist/src/core/http-errors.js.map +1 -0
- package/dist/src/core/lifecycle/mutation.d.ts +49 -0
- package/dist/src/core/lifecycle/mutation.js +170 -0
- package/dist/src/core/lifecycle/mutation.js.map +1 -0
- package/dist/src/core/lifecycle/options.d.ts +21 -0
- package/dist/src/core/lifecycle/options.js +19 -0
- package/dist/src/core/lifecycle/options.js.map +1 -0
- package/dist/src/core/model.d.ts +145 -0
- package/dist/src/core/model.js +510 -0
- package/dist/src/core/model.js.map +1 -0
- package/dist/src/core/mutations/write.d.ts +26 -0
- package/dist/src/core/mutations/write.js +284 -0
- package/dist/src/core/mutations/write.js.map +1 -0
- package/dist/src/core/operations.d.ts +33 -0
- package/dist/src/core/operations.js +10 -0
- package/dist/src/core/operations.js.map +1 -0
- package/dist/src/core/pagination.d.ts +10 -0
- package/dist/src/core/pagination.js +13 -0
- package/dist/src/core/pagination.js.map +1 -0
- package/dist/src/core/paths.d.ts +16 -0
- package/dist/src/core/paths.js +64 -0
- package/dist/src/core/paths.js.map +1 -0
- package/dist/src/core/query/contract.d.ts +6 -0
- package/dist/src/core/query/contract.js +22 -0
- package/dist/src/core/query/contract.js.map +1 -0
- package/dist/src/core/query/filter.d.ts +6 -0
- package/dist/src/core/query/filter.js +100 -0
- package/dist/src/core/query/filter.js.map +1 -0
- package/dist/src/core/query/options.d.ts +30 -0
- package/dist/src/core/query/options.js +44 -0
- package/dist/src/core/query/options.js.map +1 -0
- package/dist/src/core/records.d.ts +46 -0
- package/dist/src/core/records.js +69 -0
- package/dist/src/core/records.js.map +1 -0
- package/dist/src/core/relation-metadata.d.ts +15 -0
- package/dist/src/{relation-metadata.js → core/relation-metadata.js} +7 -2
- package/dist/src/core/relation-metadata.js.map +1 -0
- package/dist/src/core/storage.d.ts +2 -0
- package/dist/src/core/storage.js +2 -0
- package/dist/src/core/storage.js.map +1 -0
- package/dist/src/core/types.d.ts +10 -0
- package/dist/src/{types.js.map → core/types.js.map} +1 -1
- package/dist/src/core/utils.d.ts +56 -0
- package/dist/src/core/utils.js +102 -0
- package/dist/src/core/utils.js.map +1 -0
- package/dist/src/files/contract.d.ts +32 -30
- package/dist/src/files/contract.js +29 -48
- package/dist/src/files/contract.js.map +1 -1
- package/dist/src/files/disk-store.d.ts +5 -1
- package/dist/src/files/disk-store.js +61 -33
- package/dist/src/files/disk-store.js.map +1 -1
- package/dist/src/files/http.d.ts +36 -0
- package/dist/src/files/http.js +53 -0
- package/dist/src/files/http.js.map +1 -0
- package/dist/src/files/index.d.ts +5 -4
- package/dist/src/files/index.js +7 -2
- package/dist/src/files/index.js.map +1 -1
- package/dist/src/files/memory-store.d.ts +4 -1
- package/dist/src/files/memory-store.js +29 -22
- package/dist/src/files/memory-store.js.map +1 -1
- package/dist/src/files/routes.d.ts +3 -0
- package/dist/src/files/routes.js +27 -2
- package/dist/src/files/routes.js.map +1 -1
- package/dist/src/files/streams.d.ts +5 -0
- package/dist/src/files/streams.js +20 -0
- package/dist/src/files/streams.js.map +1 -0
- package/dist/src/graphql/entry.d.ts +12 -0
- package/dist/src/graphql/entry.js +13 -0
- package/dist/src/graphql/entry.js.map +1 -0
- package/dist/src/graphql/generate.d.ts +12 -0
- package/dist/src/graphql/generate.js +19 -0
- package/dist/src/graphql/generate.js.map +1 -0
- package/dist/src/graphql/preflight.d.ts +6 -0
- package/dist/src/graphql/preflight.js +42 -0
- package/dist/src/graphql/preflight.js.map +1 -0
- package/dist/src/graphql/resolvers.d.ts +17 -0
- package/dist/src/graphql/resolvers.js +44 -0
- package/dist/src/graphql/resolvers.js.map +1 -0
- package/dist/src/graphql/routes.d.ts +8 -0
- package/dist/src/graphql/routes.js +30 -0
- package/dist/src/graphql/routes.js.map +1 -0
- package/dist/src/graphql/schema.d.ts +6 -0
- package/dist/src/graphql/schema.js +230 -0
- package/dist/src/graphql/schema.js.map +1 -0
- package/dist/src/graphql/write.d.ts +4 -0
- package/dist/src/graphql/write.js +10 -0
- package/dist/src/graphql/write.js.map +1 -0
- package/dist/src/openapi/auth.d.ts +13 -0
- package/dist/src/openapi/auth.js +139 -0
- package/dist/src/openapi/auth.js.map +1 -0
- package/dist/src/openapi/document.d.ts +11 -7
- package/dist/src/openapi/document.js +287 -318
- package/dist/src/openapi/document.js.map +1 -1
- package/dist/src/openapi/entry.d.ts +14 -0
- package/dist/src/openapi/entry.js +13 -0
- package/dist/src/openapi/entry.js.map +1 -0
- package/dist/src/openapi/files.d.ts +6 -0
- package/dist/src/openapi/files.js +87 -0
- package/dist/src/openapi/files.js.map +1 -0
- package/dist/src/openapi/generate.d.ts +19 -0
- package/dist/src/openapi/generate.js +41 -0
- package/dist/src/openapi/generate.js.map +1 -0
- package/dist/src/openapi/helpers.d.ts +26 -0
- package/dist/src/openapi/helpers.js +13 -0
- package/dist/src/openapi/helpers.js.map +1 -0
- package/dist/src/openapi/options.d.ts +18 -0
- package/dist/src/openapi/options.js +13 -0
- package/dist/src/openapi/options.js.map +1 -0
- package/dist/src/{types.d.ts → openapi/types.d.ts} +2 -12
- package/dist/src/openapi/types.js +2 -0
- package/dist/src/openapi/types.js.map +1 -0
- package/dist/src/openapi/write.d.ts +5 -0
- package/dist/src/openapi/write.js +12 -0
- package/dist/src/openapi/write.js.map +1 -0
- package/dist/src/rest/options.d.ts +22 -0
- package/dist/src/rest/options.js +65 -0
- package/dist/src/rest/options.js.map +1 -0
- package/dist/src/rest/projection.d.ts +13 -0
- package/dist/src/rest/projection.js +61 -0
- package/dist/src/rest/projection.js.map +1 -0
- package/dist/src/rest/routes.d.ts +7 -0
- package/dist/src/rest/routes.js +49 -0
- package/dist/src/rest/routes.js.map +1 -0
- package/dist/src/server/config.d.ts +74 -0
- package/dist/src/server/config.js +142 -0
- package/dist/src/server/config.js.map +1 -0
- package/dist/src/server/create.d.ts +16 -0
- package/dist/src/server/create.js +133 -0
- package/dist/src/server/create.js.map +1 -0
- package/dist/src/server/features.d.ts +4 -0
- package/dist/src/server/features.js +11 -0
- package/dist/src/server/features.js.map +1 -0
- package/dist/src/server/model.d.ts +6 -0
- package/dist/src/server/model.js +14 -0
- package/dist/src/server/model.js.map +1 -0
- package/dist/src/server/public.d.ts +2 -0
- package/dist/src/server/public.js +2 -0
- package/dist/src/server/public.js.map +1 -0
- package/package.json +25 -4
- package/dist/src/cli.d.ts +0 -4
- package/dist/src/cli.js +0 -80
- package/dist/src/cli.js.map +0 -1
- package/dist/src/config.d.ts +0 -54
- package/dist/src/config.js +0 -145
- package/dist/src/config.js.map +0 -1
- package/dist/src/constants.js.map +0 -1
- package/dist/src/database.d.ts +0 -20
- package/dist/src/database.js.map +0 -1
- package/dist/src/openapi/config.d.ts +0 -11
- package/dist/src/openapi/config.js +0 -204
- package/dist/src/openapi/config.js.map +0 -1
- package/dist/src/openapi/index.d.ts +0 -10
- package/dist/src/openapi/index.js +0 -27
- package/dist/src/openapi/index.js.map +0 -1
- package/dist/src/openapi/inference.d.ts +0 -14
- package/dist/src/openapi/inference.js +0 -145
- package/dist/src/openapi/inference.js.map +0 -1
- package/dist/src/query/filter.d.ts +0 -6
- package/dist/src/query/filter.js +0 -248
- package/dist/src/query/filter.js.map +0 -1
- package/dist/src/query/index.d.ts +0 -3
- package/dist/src/query/index.js +0 -4
- package/dist/src/query/index.js.map +0 -1
- package/dist/src/query/pagination.d.ts +0 -11
- package/dist/src/query/pagination.js +0 -28
- package/dist/src/query/pagination.js.map +0 -1
- package/dist/src/query/sort.d.ts +0 -1
- package/dist/src/query/sort.js +0 -54
- package/dist/src/query/sort.js.map +0 -1
- package/dist/src/relation-metadata.d.ts +0 -10
- package/dist/src/relation-metadata.js.map +0 -1
- package/dist/src/relations.d.ts +0 -12
- package/dist/src/relations.js +0 -155
- package/dist/src/relations.js.map +0 -1
- package/dist/src/server.d.ts +0 -13
- package/dist/src/server.js +0 -188
- package/dist/src/server.js.map +0 -1
- package/dist/src/utils.d.ts +0 -20
- package/dist/src/utils.js +0 -63
- package/dist/src/utils.js.map +0 -1
- /package/dist/src/{types.js → core/types.js} +0 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { createId } from '../database.js';
|
|
3
|
+
import { domainError } from '../errors.js';
|
|
4
|
+
import { AUDIT_FIELDS } from '../lifecycle/options.js';
|
|
5
|
+
import { bindingFor, canWriteKey, isReverseRelation, pathParts, readPath, validateRecord } from '../model.js';
|
|
6
|
+
import { keyOf, makeContext, related, rootRef, sourceValues } from '../records.js';
|
|
7
|
+
import { isEqual, isObject } from '../utils.js';
|
|
8
|
+
/** Выполняет вложенные изменения в общем черновике транзакции. */
|
|
9
|
+
export class MutationWriter {
|
|
10
|
+
database;
|
|
11
|
+
model;
|
|
12
|
+
existingMode;
|
|
13
|
+
beforeWrite;
|
|
14
|
+
indexes = new Map();
|
|
15
|
+
active = new Set();
|
|
16
|
+
selections = [];
|
|
17
|
+
constructor(database, model, existingMode, beforeWrite = () => { }) {
|
|
18
|
+
this.database = database;
|
|
19
|
+
this.model = model;
|
|
20
|
+
this.existingMode = existingMode;
|
|
21
|
+
this.beforeWrite = beforeWrite;
|
|
22
|
+
}
|
|
23
|
+
write(entity, mode, key, body, depth = 0) {
|
|
24
|
+
if (depth > 32)
|
|
25
|
+
throw domainError('INVALID_INPUT', 'Nested writes are too deep');
|
|
26
|
+
if (!isObject(body))
|
|
27
|
+
throw domainError('INVALID_INPUT', 'Record input must be an object');
|
|
28
|
+
if (Object.keys(body).some((name) => entity.fields[name]?.system))
|
|
29
|
+
throw domainError('INVALID_INPUT', 'System fields are read-only');
|
|
30
|
+
if (this.model.explicit)
|
|
31
|
+
validateRecord(entity, body, mode, true);
|
|
32
|
+
else if (Object.hasOwn(body, entity.primary))
|
|
33
|
+
throw domainError('INVALID_INPUT', 'id is generated and immutable');
|
|
34
|
+
this.database.data[entity.collection] ??= [];
|
|
35
|
+
const collection = this.database.data[entity.collection];
|
|
36
|
+
const index = this.index(entity);
|
|
37
|
+
const current = mode === 'create' ? undefined : index.get(String(key));
|
|
38
|
+
if (mode !== 'create' && !current)
|
|
39
|
+
throw domainError('NOT_FOUND', `${entity.name}: record not found`);
|
|
40
|
+
if (current && this.active.has(current))
|
|
41
|
+
throw domainError('CONFLICT', 'A nested write cannot modify an active ancestor record');
|
|
42
|
+
if (current)
|
|
43
|
+
this.beforeWrite(entity, current);
|
|
44
|
+
const input = structuredClone(body);
|
|
45
|
+
const next = mode === 'update' ? { ...current, ...input } : { ...input };
|
|
46
|
+
if (current) {
|
|
47
|
+
next[entity.primary] = current[entity.primary];
|
|
48
|
+
this.preserve(entity.root, next, current);
|
|
49
|
+
for (const name of AUDIT_FIELDS)
|
|
50
|
+
if (!entity.fields[name] && Object.hasOwn(current, name))
|
|
51
|
+
next[name] = current[name];
|
|
52
|
+
}
|
|
53
|
+
if (this.model.explicit) {
|
|
54
|
+
if (mode !== 'update')
|
|
55
|
+
this.defaults(entity.root, next);
|
|
56
|
+
if (mode === 'create')
|
|
57
|
+
this.generate(entity, next);
|
|
58
|
+
}
|
|
59
|
+
else if (mode === 'create')
|
|
60
|
+
next.id = createId(collection);
|
|
61
|
+
const duplicate = index.get(String(next[entity.primary]));
|
|
62
|
+
if (duplicate && duplicate !== current)
|
|
63
|
+
throw domainError('CONFLICT', 'Primary key already exists');
|
|
64
|
+
// Сохраняем ссылки на объекты, чтобы вложенные обращения видели одну и ту же запись черновика.
|
|
65
|
+
const record = current ?? {};
|
|
66
|
+
for (const name of Object.keys(record))
|
|
67
|
+
delete record[name];
|
|
68
|
+
Object.assign(record, next);
|
|
69
|
+
if (!current) {
|
|
70
|
+
collection.push(record);
|
|
71
|
+
index.set(String(record[entity.primary]), record);
|
|
72
|
+
}
|
|
73
|
+
this.active.add(record);
|
|
74
|
+
try {
|
|
75
|
+
const pending = [];
|
|
76
|
+
this.extract(rootRef(makeContext(this.database.data, this.model), entity, record), input, {}, pending, mode === 'replace', depth);
|
|
77
|
+
for (const selection of pending)
|
|
78
|
+
this.connect(selection, depth);
|
|
79
|
+
return record;
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
this.active.delete(record);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
extract(ref, input, inputBindings, pending, replace, depth) {
|
|
86
|
+
if (depth > 32)
|
|
87
|
+
throw domainError('INVALID_INPUT', 'Nested writes are too deep');
|
|
88
|
+
for (const [name, node] of Object.entries(ref.node.children)) {
|
|
89
|
+
if (node.relation) {
|
|
90
|
+
const supplied = Object.hasOwn(ref.value, name);
|
|
91
|
+
// При замене разрываем пропущенные обратные связи; прямые ключи заменяются вместе с записью.
|
|
92
|
+
const omitted = !supplied && replace && isReverseRelation(ref.entity, node) && canWriteKey(node.relation, node.target);
|
|
93
|
+
if (!supplied && !omitted)
|
|
94
|
+
continue;
|
|
95
|
+
if (supplied && node.source !== ref.entity.primary && this.slots(ref.entity, input, node.source, inputBindings).some(({ object, key }) => Object.hasOwn(object, key)))
|
|
96
|
+
throw domainError('INVALID_INPUT', `Supply either ${node.path} or its source key ${node.source}`);
|
|
97
|
+
pending.push({ ref, node, value: supplied ? ref.value[name] : node.many ? [] : null, omitted });
|
|
98
|
+
delete ref.value[name];
|
|
99
|
+
}
|
|
100
|
+
else if (node.base === 'object' && ref.value[name] != null && !node.readOnly) {
|
|
101
|
+
const values = Array.isArray(ref.value[name]) ? ref.value[name] : [ref.value[name]];
|
|
102
|
+
const parentInput = inputBindings[ref.node.path] ?? input;
|
|
103
|
+
const rawValue = parentInput[name];
|
|
104
|
+
const raw = Array.isArray(rawValue) ? rawValue : [rawValue];
|
|
105
|
+
for (const [index, value] of values.entries()) {
|
|
106
|
+
if (!isObject(value))
|
|
107
|
+
continue;
|
|
108
|
+
const inputValue = raw[index];
|
|
109
|
+
this.extract({ ...ref, node, value: value, bindings: { ...ref.bindings, [node.path]: value } }, input, isObject(inputValue) ? { ...inputBindings, [node.path]: inputValue } : inputBindings, pending, replace, depth + 1);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
resolve(entity, input, depth) {
|
|
115
|
+
const object = isObject(input);
|
|
116
|
+
if (object && !Object.hasOwn(input, entity.primary))
|
|
117
|
+
return this.write(entity, 'create', undefined, input, depth + 1);
|
|
118
|
+
const key = object ? input[entity.primary] : input;
|
|
119
|
+
const primary = entity.fields[entity.primary];
|
|
120
|
+
if (!['string', 'number'].includes(typeof key) || (this.model.explicit && typeof key !== primary.base))
|
|
121
|
+
throw domainError('INVALID_INPUT', `${entity.name}.${entity.primary}: invalid key type`);
|
|
122
|
+
const current = this.index(entity).get(String(key));
|
|
123
|
+
if (!current || keyOf(current[entity.primary]) !== keyOf(key))
|
|
124
|
+
throw domainError('NOT_FOUND', `${entity.name}: related record not found`);
|
|
125
|
+
if (!object)
|
|
126
|
+
return current;
|
|
127
|
+
const data = { ...input };
|
|
128
|
+
delete data[entity.primary];
|
|
129
|
+
return this.write(entity, this.existingMode, key, data, depth + 1);
|
|
130
|
+
}
|
|
131
|
+
connect({ ref, node, value, omitted }, depth) {
|
|
132
|
+
const target = node.relation;
|
|
133
|
+
if (value === null && (node.many || (!omitted && !node.nullable)))
|
|
134
|
+
throw domainError('INVALID_INPUT', `${node.path} cannot be null`);
|
|
135
|
+
if (node.many && !Array.isArray(value))
|
|
136
|
+
throw domainError('INVALID_INPUT', `${node.path} must be an array`);
|
|
137
|
+
if (!node.many && Array.isArray(value))
|
|
138
|
+
throw domainError('INVALID_INPUT', `${node.path} must be a record or key`);
|
|
139
|
+
const entries = value === null ? [] : node.many ? value : [value];
|
|
140
|
+
const targets = entries.map((entry) => this.resolve(target, entry, depth));
|
|
141
|
+
if (new Set(targets).size !== targets.length)
|
|
142
|
+
throw domainError('INVALID_INPUT', `Duplicate record in ${node.path}`);
|
|
143
|
+
if (!isReverseRelation(ref.entity, node)) {
|
|
144
|
+
const slots = this.slots(ref.entity, ref.root, node.source, ref.bindings, true);
|
|
145
|
+
if (slots.length !== 1)
|
|
146
|
+
throw domainError('INVALID_INPUT', `Ambiguous source ${node.source}; supply its storage keys explicitly`);
|
|
147
|
+
const keys = targets.flatMap((record) => {
|
|
148
|
+
const values = readPath(record, node.target);
|
|
149
|
+
if (!values.length)
|
|
150
|
+
throw domainError('INVALID_INPUT', `Related record has no ${node.target}`);
|
|
151
|
+
return values;
|
|
152
|
+
});
|
|
153
|
+
const unique = [...new Map(keys.map((key) => [keyOf(key), key])).values()];
|
|
154
|
+
if (!slots[0].field.many && unique.length > 1)
|
|
155
|
+
throw domainError('INVALID_INPUT', `${node.source} cannot store multiple relation keys`);
|
|
156
|
+
this.set(ref.entity, slots[0], slots[0].field.many ? unique : (unique[0] ?? (slots[0].field.nullable ? null : undefined)));
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
const keys = sourceValues(ref, node);
|
|
160
|
+
if (keys.length !== 1)
|
|
161
|
+
throw domainError('INVALID_INPUT', `Relation ${node.path} needs one source key`);
|
|
162
|
+
const key = keys[0];
|
|
163
|
+
const previous = related({ ...ref, context: makeContext(this.database.data, this.model) }, node);
|
|
164
|
+
for (const match of previous)
|
|
165
|
+
if (!targets.includes(match.value)) {
|
|
166
|
+
for (const slot of this.slots(target, match.value, node.target)) {
|
|
167
|
+
const old = slot.object[slot.key];
|
|
168
|
+
if (Array.isArray(old))
|
|
169
|
+
this.set(target, slot, old.filter((v) => keyOf(v) !== keyOf(key)));
|
|
170
|
+
else if (keyOf(old) === keyOf(key))
|
|
171
|
+
this.set(target, slot, slot.field.nullable ? null : undefined);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
for (const record of targets) {
|
|
175
|
+
if (readPath(record, node.target).some((v) => keyOf(v) === keyOf(key)))
|
|
176
|
+
continue;
|
|
177
|
+
const slots = this.slots(target, record, node.target, {}, true);
|
|
178
|
+
if (slots.length !== 1)
|
|
179
|
+
throw domainError('INVALID_INPUT', `Ambiguous target ${node.target}; supply its object or array explicitly`);
|
|
180
|
+
const slot = slots[0];
|
|
181
|
+
this.set(target, slot, slot.field.many ? [...(Array.isArray(slot.object[slot.key]) ? slot.object[slot.key] : []), key] : key);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
this.selections.push({ ref, node, targets });
|
|
185
|
+
}
|
|
186
|
+
validateRelations() {
|
|
187
|
+
const context = makeContext(this.database.data, this.model);
|
|
188
|
+
for (const { ref, node, targets } of this.selections) {
|
|
189
|
+
if (Object.entries(ref.bindings).some(([path, value]) => !readPath(ref.root, path).includes(value)))
|
|
190
|
+
throw domainError('CONFLICT', `Nested writes conflict at ${node.path}`);
|
|
191
|
+
const actual = related({ ...ref, context }, node).map((record) => record.value);
|
|
192
|
+
if (actual.length !== targets.length || targets.some((target) => !actual.includes(target)))
|
|
193
|
+
throw domainError('CONFLICT', `Relation ${node.path} cannot represent the requested records`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
index(entity) {
|
|
197
|
+
let index = this.indexes.get(entity);
|
|
198
|
+
if (!index) {
|
|
199
|
+
index = new Map((this.database.data[entity.collection] ?? []).map((record) => [String(record[entity.primary]), record]));
|
|
200
|
+
this.indexes.set(entity, index);
|
|
201
|
+
}
|
|
202
|
+
return index;
|
|
203
|
+
}
|
|
204
|
+
set(entity, slot, value) {
|
|
205
|
+
if (isEqual(slot.object[slot.key], value))
|
|
206
|
+
return;
|
|
207
|
+
if (!canWriteKey(entity, slot.field.path))
|
|
208
|
+
throw domainError('INVALID_INPUT', `Cannot change protected relation key ${entity.name}.${slot.field.path}`);
|
|
209
|
+
if (value === undefined)
|
|
210
|
+
delete slot.object[slot.key];
|
|
211
|
+
else
|
|
212
|
+
slot.object[slot.key] = value;
|
|
213
|
+
}
|
|
214
|
+
slots(entity, root, path, bindings = {}, create = false) {
|
|
215
|
+
const binding = bindingFor(bindings, path);
|
|
216
|
+
const parts = pathParts(binding ? path.slice(binding.length + 1) : path);
|
|
217
|
+
const field = entity.fields[path];
|
|
218
|
+
if (!field)
|
|
219
|
+
throw domainError('INVALID_INPUT', `Unknown relation key ${entity.name}.${path}`);
|
|
220
|
+
const walk = (value, index) => {
|
|
221
|
+
if (Array.isArray(value))
|
|
222
|
+
return value.flatMap((item) => walk(item, index));
|
|
223
|
+
if (!isObject(value))
|
|
224
|
+
return [];
|
|
225
|
+
const key = parts[index];
|
|
226
|
+
if (index === parts.length - 1)
|
|
227
|
+
return [{ object: value, key, field }];
|
|
228
|
+
const prefix = [binding, ...parts.slice(0, index + 1)].filter(Boolean).join('.');
|
|
229
|
+
if (create && value[key] === undefined && !entity.fields[prefix]?.many)
|
|
230
|
+
value[key] = {};
|
|
231
|
+
return walk(value[key], index + 1);
|
|
232
|
+
};
|
|
233
|
+
return walk(binding ? bindings[binding] : root, 0);
|
|
234
|
+
}
|
|
235
|
+
defaults(node, record) {
|
|
236
|
+
for (const [key, child] of Object.entries(node.children)) {
|
|
237
|
+
if (child.relation)
|
|
238
|
+
continue;
|
|
239
|
+
if (!Object.hasOwn(record, key) && child.default !== undefined)
|
|
240
|
+
record[key] = structuredClone(child.default);
|
|
241
|
+
if (child.base === 'object' && record[key] != null) {
|
|
242
|
+
const values = child.many ? record[key] : [record[key]];
|
|
243
|
+
for (const value of values)
|
|
244
|
+
this.defaults(child, value);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
preserve(node, record, previous) {
|
|
249
|
+
for (const [key, child] of Object.entries(node.children)) {
|
|
250
|
+
if (child.relation)
|
|
251
|
+
continue;
|
|
252
|
+
if ((child.generated || child.readOnly) && previous && Object.hasOwn(previous, key))
|
|
253
|
+
record[key] = structuredClone(previous[key]);
|
|
254
|
+
else if (child.base === 'object' && !child.many) {
|
|
255
|
+
const old = previous?.[key];
|
|
256
|
+
if (isObject(record[key]))
|
|
257
|
+
this.preserve(child, record[key], isObject(old) ? old : undefined);
|
|
258
|
+
else if (record[key] === undefined && isObject(old)) {
|
|
259
|
+
const preserved = {};
|
|
260
|
+
this.preserve(child, preserved, old);
|
|
261
|
+
if (Object.keys(preserved).length)
|
|
262
|
+
record[key] = preserved;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
generate(entity, record) {
|
|
268
|
+
for (const [name, field] of Object.entries(entity.root.children)) {
|
|
269
|
+
if (field.generated === 'uuid')
|
|
270
|
+
record[name] = randomUUID();
|
|
271
|
+
if (field.generated === 'increment') {
|
|
272
|
+
this.database.counters ??= {};
|
|
273
|
+
const counters = this.database.counters;
|
|
274
|
+
const key = `${entity.collection}.${name}`;
|
|
275
|
+
const largest = counters[key] ?? 0;
|
|
276
|
+
if (!Number.isSafeInteger(largest) || largest >= Number.MAX_SAFE_INTEGER)
|
|
277
|
+
throw domainError('CONFLICT', 'Increment key exhausted');
|
|
278
|
+
record[name] = largest + 1;
|
|
279
|
+
counters[key] = largest + 1;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
//# sourceMappingURL=write.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write.js","sourceRoot":"","sources":["../../../../src/core/mutations/write.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** Описывает изменения записей: метод, наличие ключа и тела, код успешного ответа.
|
|
2
|
+
* @example MUTATIONS[0] → { mode: 'create', method: 'POST', hasKey: false, hasBody: true, status: 201 }.
|
|
3
|
+
*/
|
|
4
|
+
export declare const MUTATIONS: readonly [{
|
|
5
|
+
readonly mode: 'create';
|
|
6
|
+
readonly method: 'POST';
|
|
7
|
+
readonly hasKey: false;
|
|
8
|
+
readonly hasBody: true;
|
|
9
|
+
readonly status: 201;
|
|
10
|
+
}, {
|
|
11
|
+
readonly mode: 'replace';
|
|
12
|
+
readonly method: 'PUT';
|
|
13
|
+
readonly hasKey: true;
|
|
14
|
+
readonly hasBody: true;
|
|
15
|
+
readonly status: 200;
|
|
16
|
+
}, {
|
|
17
|
+
readonly mode: 'update';
|
|
18
|
+
readonly method: 'PATCH';
|
|
19
|
+
readonly hasKey: true;
|
|
20
|
+
readonly hasBody: true;
|
|
21
|
+
readonly status: 200;
|
|
22
|
+
}, {
|
|
23
|
+
readonly mode: 'delete';
|
|
24
|
+
readonly method: 'DELETE';
|
|
25
|
+
readonly hasKey: true;
|
|
26
|
+
readonly hasBody: false;
|
|
27
|
+
readonly status: 200;
|
|
28
|
+
}];
|
|
29
|
+
export type Mutation = (typeof MUTATIONS)[number];
|
|
30
|
+
export type MutationMode = Mutation['mode'];
|
|
31
|
+
export type WriteMode = Extract<Mutation, {
|
|
32
|
+
hasBody: true;
|
|
33
|
+
}>['mode'];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Описывает изменения записей: метод, наличие ключа и тела, код успешного ответа.
|
|
2
|
+
* @example MUTATIONS[0] → { mode: 'create', method: 'POST', hasKey: false, hasBody: true, status: 201 }.
|
|
3
|
+
*/
|
|
4
|
+
export const MUTATIONS = [
|
|
5
|
+
{ mode: 'create', method: 'POST', hasKey: false, hasBody: true, status: 201 },
|
|
6
|
+
{ mode: 'replace', method: 'PUT', hasKey: true, hasBody: true, status: 200 },
|
|
7
|
+
{ mode: 'update', method: 'PATCH', hasKey: true, hasBody: true, status: 200 },
|
|
8
|
+
{ mode: 'delete', method: 'DELETE', hasKey: true, hasBody: false, status: 200 },
|
|
9
|
+
];
|
|
10
|
+
//# sourceMappingURL=operations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operations.js","sourceRoot":"","sources":["../../../src/core/operations.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;IAC7E,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;IAC5E,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;IAC7E,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE;CACvE,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Подставляет размеры страниц по умолчанию и проверяет положительные безопасные целые числа.
|
|
2
|
+
* @example normalizePagination({ maxPageSize: 5 }) → { pageSize: 5, maxPageSize: 5 }; { pageSize: 0 } → ошибка.
|
|
3
|
+
*/
|
|
4
|
+
export declare function normalizePagination(options: {
|
|
5
|
+
pageSize?: unknown;
|
|
6
|
+
maxPageSize?: unknown;
|
|
7
|
+
}): {
|
|
8
|
+
pageSize: number;
|
|
9
|
+
maxPageSize: number;
|
|
10
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { getPositiveInteger } from './config-values.js';
|
|
2
|
+
import { DEFAULT_MAX_PAGE_SIZE, DEFAULT_PAGE_SIZE } from './constants.js';
|
|
3
|
+
/** Подставляет размеры страниц по умолчанию и проверяет положительные безопасные целые числа.
|
|
4
|
+
* @example normalizePagination({ maxPageSize: 5 }) → { pageSize: 5, maxPageSize: 5 }; { pageSize: 0 } → ошибка.
|
|
5
|
+
*/
|
|
6
|
+
export function normalizePagination(options) {
|
|
7
|
+
const maxPageSize = getPositiveInteger(options.maxPageSize, 'config.server.maxPageSize') ?? DEFAULT_MAX_PAGE_SIZE;
|
|
8
|
+
const pageSize = getPositiveInteger(options.pageSize, 'config.server.pageSize') ?? Math.min(DEFAULT_PAGE_SIZE, maxPageSize);
|
|
9
|
+
if (pageSize > maxPageSize)
|
|
10
|
+
throw new Error('pageSize exceeds maxPageSize');
|
|
11
|
+
return { pageSize, maxPageSize };
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=pagination.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pagination.js","sourceRoot":"","sources":["../../../src/core/pagination.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAC1E;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAsD;IACxF,MAAM,WAAW,GAAG,kBAAkB,CAAC,OAAO,CAAC,WAAW,EAAE,2BAA2B,CAAC,IAAI,qBAAqB,CAAC;IAClH,MAAM,QAAQ,GAAG,kBAAkB,CAAC,OAAO,CAAC,QAAQ,EAAE,wBAAwB,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,iBAAiB,EAAE,WAAW,CAAC,CAAC;IAC5H,IAAI,QAAQ,GAAG,WAAW;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAC5E,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Раскрывает символические ссылки в существующей части пути и сохраняет ещё не созданный остаток.
|
|
2
|
+
* @example Если /tmp/link указывает на /data: /tmp/link/new.json → /data/new.json.
|
|
3
|
+
*/
|
|
4
|
+
export declare function canonicalPath(path: string): Promise<string>;
|
|
5
|
+
/** Собирает пути входных файлов, счётчиков и временных файлов записи без чтения диска.
|
|
6
|
+
* @example auth.source = '/tmp/users.json' → среди защищённых путей '/tmp/users.json' и '/tmp/.users.json.tmp'.
|
|
7
|
+
*/
|
|
8
|
+
export declare function inputPaths(config: {
|
|
9
|
+
database?: unknown;
|
|
10
|
+
files?: unknown;
|
|
11
|
+
auth?: unknown;
|
|
12
|
+
}, directory?: string, sourcePath?: string): string[];
|
|
13
|
+
/** Проверяет по реальным путям и inode, что выходные файлы различны и не совпадают с входными.
|
|
14
|
+
* @example outputs = ['/tmp/a'], inputs = ['/tmp/a'] → ошибка; разные свободные пути → Promise<void>.
|
|
15
|
+
*/
|
|
16
|
+
export declare function validateExportPaths(outputs: string[], inputs: string[]): Promise<void>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { realpath, stat } from 'node:fs/promises';
|
|
2
|
+
import { basename, dirname, resolve } from 'node:path';
|
|
3
|
+
import { isObject, isSystemError } from './utils.js';
|
|
4
|
+
/** Раскрывает символические ссылки в существующей части пути и сохраняет ещё не созданный остаток.
|
|
5
|
+
* @example Если /tmp/link указывает на /data: /tmp/link/new.json → /data/new.json.
|
|
6
|
+
*/
|
|
7
|
+
export async function canonicalPath(path) {
|
|
8
|
+
const absolute = resolve(path);
|
|
9
|
+
try {
|
|
10
|
+
return await realpath(absolute);
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
if (!isSystemError(error) || error.code !== 'ENOENT')
|
|
14
|
+
throw error;
|
|
15
|
+
const parent = dirname(absolute);
|
|
16
|
+
if (parent === absolute)
|
|
17
|
+
throw error;
|
|
18
|
+
return resolve(await canonicalPath(parent), basename(absolute));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** Собирает пути входных файлов, счётчиков и временных файлов записи без чтения диска.
|
|
22
|
+
* @example auth.source = '/tmp/users.json' → среди защищённых путей '/tmp/users.json' и '/tmp/.users.json.tmp'.
|
|
23
|
+
*/
|
|
24
|
+
export function inputPaths(config, directory = '.', sourcePath) {
|
|
25
|
+
const database = isObject(config.database) ? config.database : {};
|
|
26
|
+
const files = isObject(config.files) ? config.files : {};
|
|
27
|
+
const auth = isObject(config.auth) ? config.auth : {};
|
|
28
|
+
const paths = [sourcePath, database.source, database.schema, files.metadata ?? (typeof files.source === 'string' ? resolve(directory, files.source, '.files.json') : undefined), auth.source].filter((path) => typeof path === 'string');
|
|
29
|
+
if (typeof database.source === 'string')
|
|
30
|
+
paths.push(`${database.source}.counters.json`);
|
|
31
|
+
// JSONFile использует эти соседние файлы при записи; они тоже содержат защищённые данные.
|
|
32
|
+
for (const path of [database.source, typeof database.source === 'string' ? `${database.source}.counters.json` : undefined, auth.source])
|
|
33
|
+
if (typeof path === 'string')
|
|
34
|
+
paths.push(resolve(directory, dirname(path), `.${basename(path)}.tmp`));
|
|
35
|
+
return paths.map((path) => resolve(directory, path));
|
|
36
|
+
}
|
|
37
|
+
/** Читает идентификатор файла из устройства и inode; для отсутствующего файла возвращает исходный путь.
|
|
38
|
+
* @example Существующий файл → строка dev:ino; отсутствующий /tmp/new → /tmp/new.
|
|
39
|
+
*/
|
|
40
|
+
async function identity(path) {
|
|
41
|
+
try {
|
|
42
|
+
const info = await stat(path);
|
|
43
|
+
return `${info.dev}:${info.ino}`;
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
if (!isSystemError(error) || error.code !== 'ENOENT')
|
|
47
|
+
throw error;
|
|
48
|
+
return path;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** Проверяет по реальным путям и inode, что выходные файлы различны и не совпадают с входными.
|
|
52
|
+
* @example outputs = ['/tmp/a'], inputs = ['/tmp/a'] → ошибка; разные свободные пути → Promise<void>.
|
|
53
|
+
*/
|
|
54
|
+
export async function validateExportPaths(outputs, inputs) {
|
|
55
|
+
const canonicalOutputs = await Promise.all(outputs.map(canonicalPath));
|
|
56
|
+
const canonicalInputs = await Promise.all(inputs.map(canonicalPath));
|
|
57
|
+
const [outputKeys, inputKeys] = await Promise.all([Promise.all(canonicalOutputs.map(identity)), Promise.all(canonicalInputs.map(identity))]);
|
|
58
|
+
if (new Set(outputKeys).size !== outputKeys.length)
|
|
59
|
+
throw new Error('Schema export destinations must be different');
|
|
60
|
+
for (let index = 0; index < outputKeys.length; index++)
|
|
61
|
+
if (inputKeys.includes(outputKeys[index]))
|
|
62
|
+
throw new Error(`Schema export would overwrite an input file: ${outputs[index]}`);
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../src/core/paths.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Node } from '../model.js';
|
|
2
|
+
export type Operand = 'value' | 'values' | 'comparison' | 'text' | 'element' | 'condition';
|
|
3
|
+
/** Возвращает допустимые операторы и виды их аргументов для типа значения.
|
|
4
|
+
* @example Для boolean без массива → { not: 'condition', eq: 'value', ne: 'value', in: 'values' }.
|
|
5
|
+
*/
|
|
6
|
+
export declare const operatorsFor: (node: Node) => Record<string, Operand>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Возвращает допустимые операторы и виды их аргументов для типа значения.
|
|
2
|
+
* @example Для boolean без массива → { not: 'condition', eq: 'value', ne: 'value', in: 'values' }.
|
|
3
|
+
*/
|
|
4
|
+
export const operatorsFor = (node) => {
|
|
5
|
+
const result = { not: 'condition' };
|
|
6
|
+
if (node.many) {
|
|
7
|
+
Object.assign(result, { some: 'element', every: 'element', none: 'element' });
|
|
8
|
+
if (!node.relation && node.base !== 'object')
|
|
9
|
+
Object.assign(result, { contains: 'value', in: 'values' });
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
Object.assign(result, { eq: 'value', ne: 'value', in: 'values' });
|
|
13
|
+
if (node.base !== 'boolean')
|
|
14
|
+
for (const name of ['gt', 'gte', 'lt', 'lte'])
|
|
15
|
+
result[name] = 'comparison';
|
|
16
|
+
if (node.base === 'string')
|
|
17
|
+
for (const name of ['contains', 'startsWith', 'endsWith'])
|
|
18
|
+
result[name] = 'text';
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../../../../src/core/query/contract.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,IAAU,EAA2B,EAAE;IAClE,MAAM,MAAM,GAA4B,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC;IAC7D,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;QAC9E,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;YAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC3G,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;QAClE,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;YAAE,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC;QACxG,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ;YAAE,KAAK,MAAM,IAAI,IAAI,CAAC,UAAU,EAAE,YAAY,EAAE,UAAU,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC;IAC/G,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Node } from '../model.js';
|
|
2
|
+
export type Predicate = (value: unknown) => boolean;
|
|
3
|
+
/** Компилирует условия объекта в предикат; учитывает логические операторы и фильтрацию удалённых записей.
|
|
4
|
+
* @example Для числового age: compileWhere(node, { age: { gte: 18 } })({ age: 20 }) → true.
|
|
5
|
+
*/
|
|
6
|
+
export declare function compileWhere(node: Node, input: unknown, depth?: number, defaults?: boolean): Predicate;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { mentionsDeletedAt } from '../lifecycle/options.js';
|
|
2
|
+
import { isEqual, isObject } from '../utils.js';
|
|
3
|
+
import { operatorsFor } from './contract.js';
|
|
4
|
+
import { badQuery, childrenOf } from './options.js';
|
|
5
|
+
const comparable = (value) => typeof value === 'string' || typeof value === 'number';
|
|
6
|
+
const equal = (left, right) => isEqual(left, right);
|
|
7
|
+
/** Компилирует условие поля в предикат, проверяя операторы и их аргументы.
|
|
8
|
+
* @example Для строкового узла condition(node, { contains: 'ан' }, 0)('Анна') → true.
|
|
9
|
+
*/
|
|
10
|
+
function condition(node, input, depth) {
|
|
11
|
+
if (!isObject(input) || depth > 32)
|
|
12
|
+
badQuery('Field filter must contain operators with depth at most 32');
|
|
13
|
+
if (!node.many && (node.relation || node.base === 'object'))
|
|
14
|
+
return compileWhere(node, input, depth);
|
|
15
|
+
const operators = operatorsFor(node);
|
|
16
|
+
const predicates = Object.entries(input).map(([operator, value]) => {
|
|
17
|
+
const operand = operators[operator];
|
|
18
|
+
if (!Object.hasOwn(operators, operator))
|
|
19
|
+
badQuery(`Invalid operator ${operator} for ${node.type}`);
|
|
20
|
+
if (operand === 'condition') {
|
|
21
|
+
const nested = condition(node, value, depth + 1);
|
|
22
|
+
return (field) => !nested(field);
|
|
23
|
+
}
|
|
24
|
+
if (operand === 'element') {
|
|
25
|
+
const nested = condition({ ...node, many: false }, value, depth + 1);
|
|
26
|
+
return (field) => {
|
|
27
|
+
if (!Array.isArray(field))
|
|
28
|
+
return false;
|
|
29
|
+
const values = node.relation?.softDelete && !mentionsDeletedAt(value) ? field.filter((item) => isObject(item) && item.deletedAt == null) : field;
|
|
30
|
+
return operator === 'every' ? values.every(nested) : operator === 'none' ? !values.some(nested) : values.some(nested);
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const values = operand === 'values' ? value : [value];
|
|
34
|
+
if (!Array.isArray(values))
|
|
35
|
+
badQuery(`Invalid value for ${operator}`);
|
|
36
|
+
const nullable = ['eq', 'ne', 'in'].includes(operator);
|
|
37
|
+
for (const candidate of values) {
|
|
38
|
+
if (candidate === null && nullable)
|
|
39
|
+
continue;
|
|
40
|
+
const base = operand === 'text' ? 'string' : node.base;
|
|
41
|
+
if (typeof candidate !== base || (typeof candidate === 'number' && !Number.isFinite(candidate)))
|
|
42
|
+
badQuery(`Invalid value for ${operator}`);
|
|
43
|
+
if ((operand === 'value' || operand === 'values') && node.enum && !node.enum.some((v) => equal(v, candidate)))
|
|
44
|
+
badQuery(`Invalid enum value for ${operator}`);
|
|
45
|
+
}
|
|
46
|
+
switch (operator) {
|
|
47
|
+
case 'eq':
|
|
48
|
+
return (field) => equal(field, value);
|
|
49
|
+
case 'ne':
|
|
50
|
+
return (field) => !equal(field, value);
|
|
51
|
+
case 'in':
|
|
52
|
+
return (field) => (Array.isArray(field) ? field : [field]).some((item) => values.some((v) => equal(item, v)));
|
|
53
|
+
case 'contains':
|
|
54
|
+
return (field) => (typeof field === 'string' ? field.toLowerCase().includes(String(value).toLowerCase()) : Array.isArray(field) && field.some((v) => equal(v, value)));
|
|
55
|
+
case 'startsWith':
|
|
56
|
+
return (field) => typeof field === 'string' && field.toLowerCase().startsWith(String(value).toLowerCase());
|
|
57
|
+
case 'endsWith':
|
|
58
|
+
return (field) => typeof field === 'string' && field.toLowerCase().endsWith(String(value).toLowerCase());
|
|
59
|
+
case 'gt':
|
|
60
|
+
return (field) => comparable(field) && comparable(value) && field > value;
|
|
61
|
+
case 'gte':
|
|
62
|
+
return (field) => comparable(field) && comparable(value) && field >= value;
|
|
63
|
+
case 'lt':
|
|
64
|
+
return (field) => comparable(field) && comparable(value) && field < value;
|
|
65
|
+
default:
|
|
66
|
+
return (field) => comparable(field) && comparable(value) && field <= value;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
return (value) => predicates.every((predicate) => predicate(value));
|
|
70
|
+
}
|
|
71
|
+
/** Компилирует условия объекта в предикат; учитывает логические операторы и фильтрацию удалённых записей.
|
|
72
|
+
* @example Для числового age: compileWhere(node, { age: { gte: 18 } })({ age: 20 }) → true.
|
|
73
|
+
*/
|
|
74
|
+
export function compileWhere(node, input, depth = 0, defaults = true) {
|
|
75
|
+
if (!isObject(input) || depth > 32)
|
|
76
|
+
badQuery('where must be an object with depth at most 32');
|
|
77
|
+
const predicates = Object.entries(input).map(([key, value]) => {
|
|
78
|
+
if (key === 'and' || key === 'or') {
|
|
79
|
+
if (!Array.isArray(value) || (key === 'or' && !value.length))
|
|
80
|
+
badQuery(`Invalid ${key}`);
|
|
81
|
+
const nested = value.map((item) => compileWhere(node, item, depth + 1, false));
|
|
82
|
+
return (field) => (key === 'and' ? nested.every((p) => p(field)) : nested.some((p) => p(field)));
|
|
83
|
+
}
|
|
84
|
+
if (key === 'not') {
|
|
85
|
+
const nested = compileWhere(node, value, depth + 1, false);
|
|
86
|
+
return (field) => !nested(field);
|
|
87
|
+
}
|
|
88
|
+
const child = childrenOf(node)[key];
|
|
89
|
+
if (!Object.hasOwn(childrenOf(node), key) || child.writeOnly)
|
|
90
|
+
badQuery(`Unknown or inaccessible filter field ${key}`);
|
|
91
|
+
if (child.mixed)
|
|
92
|
+
badQuery(`Field ${key} has inconsistent types; provide an explicit schema`);
|
|
93
|
+
const nested = condition(child, value, depth + 1);
|
|
94
|
+
return (field) => isObject(field) && nested(Object.hasOwn(field, key) ? field[key] : undefined);
|
|
95
|
+
});
|
|
96
|
+
if (defaults && (node.relation?.softDelete || node.softDelete) && !mentionsDeletedAt(input))
|
|
97
|
+
predicates.push((value) => isObject(value) && value.deletedAt == null);
|
|
98
|
+
return (value) => isObject(value) && predicates.every((predicate) => predicate(value));
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=filter.js.map
|