@aeriajs/core 0.0.276 → 0.0.278
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/dist/__scripts__/postinstall.js +2 -37
- package/dist/accessControl.js +14 -18
- package/dist/assets.js +25 -31
- package/dist/collection/cascadingRemove.js +14 -19
- package/dist/collection/define.js +5 -10
- package/dist/collection/description.js +2 -7
- package/dist/collection/index.js +8 -24
- package/dist/collection/makePagination.js +5 -9
- package/dist/collection/normalizeProjection.js +1 -5
- package/dist/collection/preload.js +13 -18
- package/dist/collection/reference.js +19 -26
- package/dist/collection/traverseDocument.js +97 -131
- package/dist/context.js +16 -53
- package/dist/database.js +12 -52
- package/dist/endpoints.js +11 -48
- package/dist/functions/count.js +11 -15
- package/dist/functions/get.js +20 -24
- package/dist/functions/getAll.js +8 -12
- package/dist/functions/index.js +9 -27
- package/dist/functions/insert.js +19 -23
- package/dist/functions/remove.js +15 -19
- package/dist/functions/removeAll.js +11 -15
- package/dist/functions/removeFile.js +7 -11
- package/dist/functions/unpaginatedGetAll.js +15 -19
- package/dist/functions/upload.js +19 -57
- package/dist/index.js +11 -51
- package/dist/presets/add.js +1 -4
- package/dist/presets/crud.js +1 -4
- package/dist/presets/duplicate.js +1 -4
- package/dist/presets/index.js +17 -20
- package/dist/presets/owned.js +1 -4
- package/dist/presets/remove.js +1 -4
- package/dist/presets/removeAll.js +1 -4
- package/dist/presets/timestamped.js +1 -4
- package/dist/presets/view.js +1 -4
- package/dist/token.js +7 -15
- package/package.json +12 -17
- package/dist/__scripts__/postinstall.mjs +0 -50
- package/dist/accessControl.mjs +0 -31
- package/dist/assets.mjs +0 -67
- package/dist/collection/cascadingRemove.mjs +0 -71
- package/dist/collection/define.mjs +0 -13
- package/dist/collection/description.mjs +0 -8
- package/dist/collection/index.mjs +0 -9
- package/dist/collection/makePagination.mjs +0 -20
- package/dist/collection/normalizeProjection.mjs +0 -17
- package/dist/collection/preload.mjs +0 -88
- package/dist/collection/reference.mjs +0 -374
- package/dist/collection/traverseDocument.mjs +0 -451
- package/dist/context.mjs +0 -120
- package/dist/database.mjs +0 -49
- package/dist/endpoints.mjs +0 -52
- package/dist/functions/count.mjs +0 -50
- package/dist/functions/get.mjs +0 -89
- package/dist/functions/getAll.mjs +0 -14
- package/dist/functions/index.mjs +0 -12
- package/dist/functions/insert.mjs +0 -102
- package/dist/functions/remove.mjs +0 -41
- package/dist/functions/removeAll.mjs +0 -40
- package/dist/functions/removeFile.mjs +0 -29
- package/dist/functions/unpaginatedGetAll.mjs +0 -123
- package/dist/functions/upload.mjs +0 -91
- package/dist/index.mjs +0 -14
- package/dist/presets/add.mjs +0 -12
- package/dist/presets/crud.mjs +0 -35
- package/dist/presets/duplicate.mjs +0 -11
- package/dist/presets/index.mjs +0 -19
- package/dist/presets/owned.mjs +0 -9
- package/dist/presets/remove.mjs +0 -11
- package/dist/presets/removeAll.mjs +0 -11
- package/dist/presets/timestamped.mjs +0 -19
- package/dist/presets/view.mjs +0 -14
- package/dist/token.mjs +0 -31
|
@@ -1,451 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import * as fs from "node:fs/promises";
|
|
4
|
-
import { Result, ACError, ValidationErrorCode, TraverseError } from "@aeriajs/types";
|
|
5
|
-
import { throwIfError, isReference, getReferenceProperty, getValueFromPath } from "@aeriajs/common";
|
|
6
|
-
import { makeValidationError, validatePropertyWithRefs, validateWholeness } from "@aeriajs/validation";
|
|
7
|
-
import { getCollection } from "@aeriajs/entrypoint";
|
|
8
|
-
import { ObjectId } from "mongodb";
|
|
9
|
-
import { getCollectionAsset } from "../assets.mjs";
|
|
10
|
-
import { createContext } from "../context.mjs";
|
|
11
|
-
import { preloadDescription } from "./preload.mjs";
|
|
12
|
-
import { getReferences } from "./reference.mjs";
|
|
13
|
-
import { preferredRemove } from "./cascadingRemove.mjs";
|
|
14
|
-
const escapeRegExp = (text) => {
|
|
15
|
-
return text.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");
|
|
16
|
-
};
|
|
17
|
-
const getProperty = (propName, parentProperty) => {
|
|
18
|
-
if (propName === "_id") {
|
|
19
|
-
return {
|
|
20
|
-
type: "string"
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
if ("items" in parentProperty && "properties" in parentProperty.items && propName in parentProperty.items.properties) {
|
|
24
|
-
return parentProperty.items.properties[propName];
|
|
25
|
-
}
|
|
26
|
-
if ("additionalProperties" in parentProperty && typeof parentProperty.additionalProperties === "object") {
|
|
27
|
-
return parentProperty.additionalProperties;
|
|
28
|
-
}
|
|
29
|
-
if ("properties" in parentProperty) {
|
|
30
|
-
return parentProperty.properties[propName];
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
const cleanupReferences = async (value, ctx) => {
|
|
34
|
-
if (ctx.root._id) {
|
|
35
|
-
const refProperty = getReferenceProperty(ctx.property);
|
|
36
|
-
if (refProperty && (refProperty.$ref === "file" || refProperty.inline)) {
|
|
37
|
-
if (ctx.isArray && !Array.isArray(value)) {
|
|
38
|
-
return Result.result(value);
|
|
39
|
-
}
|
|
40
|
-
const context = ctx.options.context;
|
|
41
|
-
const doc = await context.collections[ctx.options.description.$id].model.findOne({
|
|
42
|
-
_id: new ObjectId(ctx.root._id)
|
|
43
|
-
}, {
|
|
44
|
-
projection: {
|
|
45
|
-
[ctx.propPath]: 1
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
if (!doc) {
|
|
49
|
-
return Result.error(TraverseError.InvalidDocumentId);
|
|
50
|
-
}
|
|
51
|
-
let referenceIds = getValueFromPath(doc, ctx.propPath);
|
|
52
|
-
if (!referenceIds) {
|
|
53
|
-
return Result.result(value);
|
|
54
|
-
}
|
|
55
|
-
if (Array.isArray(referenceIds)) {
|
|
56
|
-
if (!Array.isArray(value)) {
|
|
57
|
-
throw new Error();
|
|
58
|
-
}
|
|
59
|
-
referenceIds = referenceIds.filter((oldId) => !value.some((valueId) => valueId.equals(oldId)));
|
|
60
|
-
} else {
|
|
61
|
-
if (referenceIds.equals(value)) {
|
|
62
|
-
return Result.result(value);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
const refMap = await getReferences({
|
|
66
|
-
[ctx.propName]: ctx.property
|
|
67
|
-
});
|
|
68
|
-
await preferredRemove(referenceIds, refMap[ctx.propName], await createContext({
|
|
69
|
-
parentContext: context,
|
|
70
|
-
collectionName: refProperty.$ref
|
|
71
|
-
}));
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return Result.result(value);
|
|
75
|
-
};
|
|
76
|
-
const autoCast = (value, ctx) => {
|
|
77
|
-
switch (typeof value) {
|
|
78
|
-
case "boolean": {
|
|
79
|
-
return Result.result(!!value);
|
|
80
|
-
}
|
|
81
|
-
case "string": {
|
|
82
|
-
if (isReference(ctx.property)) {
|
|
83
|
-
return Result.result(ObjectId.isValid(value) ? new ObjectId(value) : value);
|
|
84
|
-
}
|
|
85
|
-
if ("format" in ctx.property) {
|
|
86
|
-
if (ctx.property.format === "date" || ctx.property.format === "date-time") {
|
|
87
|
-
const timestamp = Date.parse(value);
|
|
88
|
-
return Result.result(!Number.isNaN(timestamp) ? new Date(timestamp) : null);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
break;
|
|
92
|
-
}
|
|
93
|
-
case "number": {
|
|
94
|
-
if ("type" in ctx.property && ctx.property.type === "integer") {
|
|
95
|
-
return Result.result(parseInt(value.toString()));
|
|
96
|
-
}
|
|
97
|
-
if ("format" in ctx.property) {
|
|
98
|
-
if (ctx.property.format === "date" || ctx.property.format === "date-time") {
|
|
99
|
-
return Result.result(new Date(value));
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
case "object": {
|
|
105
|
-
if (!value || value instanceof ObjectId) {
|
|
106
|
-
return Result.result(value);
|
|
107
|
-
}
|
|
108
|
-
if (!("description" in ctx.options) || !ctx.options.recurseDeep) {
|
|
109
|
-
if (Array.isArray(value)) {
|
|
110
|
-
return Result.result(value.map((v) => throwIfError(autoCast(v, ctx))));
|
|
111
|
-
}
|
|
112
|
-
if (Object.keys(value).length > 0) {
|
|
113
|
-
const entries = {};
|
|
114
|
-
for (const [k, v] of Object.entries(value)) {
|
|
115
|
-
const subProperty = !k.startsWith("$") ? getProperty(k, ctx.property) : ctx.property;
|
|
116
|
-
if (!subProperty) {
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
entries[k] = throwIfError(autoCast(v, {
|
|
120
|
-
...ctx,
|
|
121
|
-
property: subProperty
|
|
122
|
-
}));
|
|
123
|
-
}
|
|
124
|
-
return Result.result(entries);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return Result.result(value);
|
|
130
|
-
};
|
|
131
|
-
const getters = async (value, ctx) => {
|
|
132
|
-
if ("getter" in ctx.property) {
|
|
133
|
-
if (!ctx.options.context) {
|
|
134
|
-
throw new Error();
|
|
135
|
-
}
|
|
136
|
-
if (!ctx.property.getter) {
|
|
137
|
-
return Result.result(void 0);
|
|
138
|
-
}
|
|
139
|
-
return Result.result(await ctx.property.getter(ctx.target, ctx.options.context));
|
|
140
|
-
}
|
|
141
|
-
return Result.result(value);
|
|
142
|
-
};
|
|
143
|
-
const validate = async (value, ctx) => {
|
|
144
|
-
if (ctx.options.recurseDeep) {
|
|
145
|
-
if ("properties" in ctx.property) {
|
|
146
|
-
return Result.result(value);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
if ("$ref" in ctx.property && ctx.property.$ref === "file") {
|
|
150
|
-
return Result.result(value);
|
|
151
|
-
}
|
|
152
|
-
const { error } = await validatePropertyWithRefs(value, ctx.property, {
|
|
153
|
-
checkObjectIds: true,
|
|
154
|
-
context: ctx.options.context,
|
|
155
|
-
objectIdConstructor: ObjectId
|
|
156
|
-
});
|
|
157
|
-
if (error) {
|
|
158
|
-
return Result.error({
|
|
159
|
-
[ctx.propName]: error
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
return Result.result(value);
|
|
163
|
-
};
|
|
164
|
-
const isValidTempFile = (value) => {
|
|
165
|
-
if (value && typeof value === "object") {
|
|
166
|
-
if (value instanceof ObjectId) {
|
|
167
|
-
return true;
|
|
168
|
-
}
|
|
169
|
-
return "tempId" in value && (typeof value.tempId === "string" || value.tempId instanceof ObjectId);
|
|
170
|
-
}
|
|
171
|
-
return !!(value === void 0 || value === null);
|
|
172
|
-
};
|
|
173
|
-
const isMissingPropertyError = (error) => {
|
|
174
|
-
return "code" in error && error.code === ValidationErrorCode.MissingProperties;
|
|
175
|
-
};
|
|
176
|
-
const moveFiles = async (value, ctx) => {
|
|
177
|
-
if (!("$ref" in ctx.property) || ctx.property.$ref !== "file") {
|
|
178
|
-
return Result.result(value);
|
|
179
|
-
}
|
|
180
|
-
const tempFileCollection = await getCollection("tempFile");
|
|
181
|
-
if (!tempFileCollection) {
|
|
182
|
-
throw new Error('The "tempFile" collection is absent, yet it is required to upload files.');
|
|
183
|
-
}
|
|
184
|
-
if (!isValidTempFile(value)) {
|
|
185
|
-
return Result.error(TraverseError.InvalidTempfile);
|
|
186
|
-
}
|
|
187
|
-
if (!value) {
|
|
188
|
-
return Result.result(null);
|
|
189
|
-
}
|
|
190
|
-
if (value instanceof ObjectId) {
|
|
191
|
-
return Result.result(value);
|
|
192
|
-
}
|
|
193
|
-
if (!ctx.options.context) {
|
|
194
|
-
throw new Error();
|
|
195
|
-
}
|
|
196
|
-
const tempFile = await ctx.options.context.collections.tempFile.model.findOne({
|
|
197
|
-
_id: new ObjectId(value.tempId)
|
|
198
|
-
});
|
|
199
|
-
if (!tempFile) {
|
|
200
|
-
return Result.error(TraverseError.InvalidTempfile);
|
|
201
|
-
}
|
|
202
|
-
const { _id: fileId, ...newFile } = tempFile;
|
|
203
|
-
newFile.absolute_path = `${ctx.options.context.config.storage.fs}/${tempFile.absolute_path.split(path.sep).at(-1)}`;
|
|
204
|
-
newFile.owner = ctx.options.context.token.sub;
|
|
205
|
-
await fs.rename(tempFile.absolute_path, newFile.absolute_path);
|
|
206
|
-
const file = await ctx.options.context.collections.file.model.insertOne(newFile);
|
|
207
|
-
return Result.result(file.insertedId);
|
|
208
|
-
};
|
|
209
|
-
const recurseDeep = async (value, ctx) => {
|
|
210
|
-
if (!value) {
|
|
211
|
-
return Result.result(value);
|
|
212
|
-
}
|
|
213
|
-
if ("properties" in ctx.property) {
|
|
214
|
-
if (ctx.options.validateWholeness) {
|
|
215
|
-
const wholenessError = validateWholeness(value, ctx.property);
|
|
216
|
-
if (wholenessError) {
|
|
217
|
-
return Result.error(wholenessError);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
return recurse(value, ctx);
|
|
221
|
-
}
|
|
222
|
-
if ("items" in ctx.property) {
|
|
223
|
-
if (!Array.isArray(value)) {
|
|
224
|
-
return Result.result(value);
|
|
225
|
-
}
|
|
226
|
-
const items = [];
|
|
227
|
-
for (const item of value) {
|
|
228
|
-
const { error, result } = await ctx.options.pipe(item, {
|
|
229
|
-
...ctx,
|
|
230
|
-
property: ctx.property.items,
|
|
231
|
-
isArray: true
|
|
232
|
-
});
|
|
233
|
-
if (!error) {
|
|
234
|
-
items.push(result);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
return Result.result(items);
|
|
238
|
-
}
|
|
239
|
-
return Result.result(value);
|
|
240
|
-
};
|
|
241
|
-
const recurse = async (target, ctx) => {
|
|
242
|
-
const entries = {};
|
|
243
|
-
const entrypoint = ctx.options.fromProperties && "properties" in ctx.property ? {
|
|
244
|
-
_id: null,
|
|
245
|
-
...ctx.property.properties
|
|
246
|
-
} : target;
|
|
247
|
-
entrypoint: for (const propName in entrypoint) {
|
|
248
|
-
const value = target[propName];
|
|
249
|
-
const property = getProperty(propName, ctx.property);
|
|
250
|
-
if (propName === "_id") {
|
|
251
|
-
if (value) {
|
|
252
|
-
if (ctx.options.autoCast) {
|
|
253
|
-
entries[propName] = throwIfError(autoCast(value, {
|
|
254
|
-
...ctx,
|
|
255
|
-
target,
|
|
256
|
-
propName,
|
|
257
|
-
property: {
|
|
258
|
-
$ref: ""
|
|
259
|
-
}
|
|
260
|
-
}));
|
|
261
|
-
} else {
|
|
262
|
-
entries[propName] = value;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
continue;
|
|
266
|
-
}
|
|
267
|
-
if (ctx.options.undefinedToNull) {
|
|
268
|
-
if (value === void 0) {
|
|
269
|
-
entries[propName] = null;
|
|
270
|
-
continue;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
if (value && typeof value === "object") {
|
|
274
|
-
for (const key in value) {
|
|
275
|
-
if (key.startsWith("$")) {
|
|
276
|
-
if (!ctx.options.allowOperators) {
|
|
277
|
-
return Result.error(ACError.InsecureOperator);
|
|
278
|
-
}
|
|
279
|
-
if (key === "$regex" && typeof value[key] === "string") {
|
|
280
|
-
if (!ctx.options.noRegExpEscaping) {
|
|
281
|
-
entries[propName] = {
|
|
282
|
-
...value,
|
|
283
|
-
$regex: escapeRegExp(value[key])
|
|
284
|
-
};
|
|
285
|
-
continue entrypoint;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
if (!property) {
|
|
292
|
-
if (value && (value.constructor === Object || value.constructor === Array)) {
|
|
293
|
-
if (Array.isArray(value)) {
|
|
294
|
-
const operations = [];
|
|
295
|
-
for (const operation of value) {
|
|
296
|
-
const { error: error2, result } = await recurse(operation, ctx);
|
|
297
|
-
if (error2) {
|
|
298
|
-
return Result.error(error2);
|
|
299
|
-
}
|
|
300
|
-
operations.push(result);
|
|
301
|
-
}
|
|
302
|
-
entries[propName] = operations;
|
|
303
|
-
continue;
|
|
304
|
-
}
|
|
305
|
-
const { error, result: operator } = await recurse(value, ctx);
|
|
306
|
-
if (error) {
|
|
307
|
-
return Result.error(error);
|
|
308
|
-
}
|
|
309
|
-
entries[propName] = operator;
|
|
310
|
-
continue;
|
|
311
|
-
}
|
|
312
|
-
entries[propName] = value;
|
|
313
|
-
} else {
|
|
314
|
-
if (!ctx.options.preserveHidden && property.hidden) {
|
|
315
|
-
continue;
|
|
316
|
-
}
|
|
317
|
-
if ("getters" in ctx.options && ctx.options.getters && "getter" in property) {
|
|
318
|
-
if (property.requires) {
|
|
319
|
-
const missing = property.requires.some((requiredPropName) => !(requiredPropName in target));
|
|
320
|
-
if (missing) {
|
|
321
|
-
continue;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
if (ctx.options.recurseReferences) {
|
|
326
|
-
const propCast = "items" in property ? property.items : property;
|
|
327
|
-
if ("$ref" in propCast && value && !(value instanceof ObjectId)) {
|
|
328
|
-
const targetDescription = await preloadDescription(throwIfError(await getCollectionAsset(propCast.$ref, "description")));
|
|
329
|
-
if (Array.isArray(value)) {
|
|
330
|
-
const documents = [];
|
|
331
|
-
for (const elem of value) {
|
|
332
|
-
if (elem instanceof ObjectId) {
|
|
333
|
-
documents.push(elem);
|
|
334
|
-
continue;
|
|
335
|
-
}
|
|
336
|
-
if (typeof elem === "string") {
|
|
337
|
-
documents.push(new ObjectId(elem));
|
|
338
|
-
continue;
|
|
339
|
-
}
|
|
340
|
-
const { error: error3, result: result2 } = await traverseDocument(elem, targetDescription, ctx.options);
|
|
341
|
-
if (error3) {
|
|
342
|
-
return Result.error(error3);
|
|
343
|
-
}
|
|
344
|
-
documents.push(result2);
|
|
345
|
-
}
|
|
346
|
-
entries[propName] = documents;
|
|
347
|
-
continue;
|
|
348
|
-
}
|
|
349
|
-
const { error: error2, result: doc } = await traverseDocument(value, targetDescription, ctx.options);
|
|
350
|
-
if (error2) {
|
|
351
|
-
return Result.error(error2);
|
|
352
|
-
}
|
|
353
|
-
entries[propName] = doc;
|
|
354
|
-
continue;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
const { error, result } = await ctx.options.pipe(value, {
|
|
358
|
-
...ctx,
|
|
359
|
-
target,
|
|
360
|
-
propName,
|
|
361
|
-
propPath: ctx.propPath ? `${ctx.propPath}.${propName}` : propName,
|
|
362
|
-
property
|
|
363
|
-
});
|
|
364
|
-
if (!error) {
|
|
365
|
-
entries[propName] = result;
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
return Result.result(entries);
|
|
370
|
-
};
|
|
371
|
-
export const traverseDocument = async (what, description, _options) => {
|
|
372
|
-
if (!what) {
|
|
373
|
-
return Result.result(what);
|
|
374
|
-
}
|
|
375
|
-
const whatCopy = Object.assign({}, what);
|
|
376
|
-
const options = Object.assign({
|
|
377
|
-
description
|
|
378
|
-
}, _options);
|
|
379
|
-
const functions = [];
|
|
380
|
-
if (!options.validate && Object.keys(whatCopy).length === 0) {
|
|
381
|
-
return Result.result(whatCopy);
|
|
382
|
-
}
|
|
383
|
-
if (options.recurseDeep) {
|
|
384
|
-
functions.push(recurseDeep);
|
|
385
|
-
}
|
|
386
|
-
if (options.autoCast) {
|
|
387
|
-
functions.push(autoCast);
|
|
388
|
-
}
|
|
389
|
-
if ("getters" in options && options.getters) {
|
|
390
|
-
functions.push(getters);
|
|
391
|
-
}
|
|
392
|
-
if (options.validate) {
|
|
393
|
-
if (options.validateWholeness === true) {
|
|
394
|
-
const wholenessError = validateWholeness(whatCopy, options.description);
|
|
395
|
-
if (wholenessError) {
|
|
396
|
-
return Result.error(wholenessError);
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
functions.push(validate);
|
|
400
|
-
}
|
|
401
|
-
if (options.cleanupReferences) {
|
|
402
|
-
functions.push(cleanupReferences);
|
|
403
|
-
}
|
|
404
|
-
if ("moveFiles" in options && options.moveFiles) {
|
|
405
|
-
functions.push(moveFiles);
|
|
406
|
-
}
|
|
407
|
-
let traverseError, validationError;
|
|
408
|
-
options.pipe = async (initialValue, ctx) => {
|
|
409
|
-
let value = initialValue;
|
|
410
|
-
for (const fn of functions) {
|
|
411
|
-
const { error: error2, result: result2 } = await fn(value, ctx);
|
|
412
|
-
if (error2) {
|
|
413
|
-
const narrowedError = error2;
|
|
414
|
-
switch (narrowedError) {
|
|
415
|
-
case TraverseError.InvalidDocumentId:
|
|
416
|
-
case TraverseError.InvalidTempfile:
|
|
417
|
-
traverseError = narrowedError;
|
|
418
|
-
break;
|
|
419
|
-
default: {
|
|
420
|
-
validationError = narrowedError;
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
return Result.error(error2);
|
|
424
|
-
}
|
|
425
|
-
value = ctx.target[ctx.propName] = result2;
|
|
426
|
-
}
|
|
427
|
-
return Result.result(value);
|
|
428
|
-
};
|
|
429
|
-
const { error, result } = await recurse(whatCopy, {
|
|
430
|
-
root: whatCopy,
|
|
431
|
-
property: description,
|
|
432
|
-
propPath: "",
|
|
433
|
-
options
|
|
434
|
-
});
|
|
435
|
-
if (error) {
|
|
436
|
-
return Result.error(error);
|
|
437
|
-
}
|
|
438
|
-
if (traverseError) {
|
|
439
|
-
return Result.error(traverseError);
|
|
440
|
-
}
|
|
441
|
-
if (validationError) {
|
|
442
|
-
if (isMissingPropertyError(validationError)) {
|
|
443
|
-
return Result.error(validationError);
|
|
444
|
-
}
|
|
445
|
-
return Result.error(makeValidationError({
|
|
446
|
-
code: ValidationErrorCode.InvalidProperties,
|
|
447
|
-
details: validationError
|
|
448
|
-
}));
|
|
449
|
-
}
|
|
450
|
-
return Result.result(result);
|
|
451
|
-
};
|
package/dist/context.mjs
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import { throwIfError, endpointError } from "@aeriajs/common";
|
|
3
|
-
import { getCollections } from "@aeriajs/entrypoint";
|
|
4
|
-
import { limitRate } from "@aeriajs/security";
|
|
5
|
-
import { getDatabaseCollection } from "./database.mjs";
|
|
6
|
-
import { preloadDescription } from "./collection/preload.mjs";
|
|
7
|
-
const indepthCollection = (collectionName, collections, parentContext) => {
|
|
8
|
-
const candidate = collections[collectionName];
|
|
9
|
-
const collection = typeof candidate === "function" ? candidate() : candidate;
|
|
10
|
-
const proxiedFunctions = new Proxy({}, {
|
|
11
|
-
get: (_, functionName) => {
|
|
12
|
-
if (typeof functionName !== "string") {
|
|
13
|
-
throw new Error();
|
|
14
|
-
}
|
|
15
|
-
const fn = collection.functions?.[functionName];
|
|
16
|
-
if (!fn) {
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
return async (props, ...args) => {
|
|
20
|
-
const childContext = await createContext({
|
|
21
|
-
parentContext,
|
|
22
|
-
collectionName,
|
|
23
|
-
inherited: true
|
|
24
|
-
});
|
|
25
|
-
return fn(props, childContext, ...args);
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
return {
|
|
30
|
-
...collection,
|
|
31
|
-
context: () => createContext({
|
|
32
|
-
parentContext,
|
|
33
|
-
collectionName
|
|
34
|
-
}),
|
|
35
|
-
functions: proxiedFunctions,
|
|
36
|
-
originalFunctions: collection.functions,
|
|
37
|
-
model: getDatabaseCollection(collectionName)
|
|
38
|
-
};
|
|
39
|
-
};
|
|
40
|
-
const isCollectionContext = (_context, collectionName) => {
|
|
41
|
-
return !!collectionName;
|
|
42
|
-
};
|
|
43
|
-
export const createContext = async (_options) => {
|
|
44
|
-
const options = _options || {};
|
|
45
|
-
const {
|
|
46
|
-
collectionName,
|
|
47
|
-
parentContext,
|
|
48
|
-
token = parentContext?.token || {
|
|
49
|
-
authenticated: false,
|
|
50
|
-
sub: null
|
|
51
|
-
}
|
|
52
|
-
} = options;
|
|
53
|
-
const { getCollectionAsset } = await import("./assets.mjs");
|
|
54
|
-
const collections = await getCollections();
|
|
55
|
-
let config;
|
|
56
|
-
if (options.config) {
|
|
57
|
-
config = options.config;
|
|
58
|
-
} else if (parentContext?.config) {
|
|
59
|
-
config = parentContext.config;
|
|
60
|
-
} else {
|
|
61
|
-
config = {
|
|
62
|
-
security: {
|
|
63
|
-
mutableUserProperties: []
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
let request = options.request, response = options.response, inherited = !!options.inherited;
|
|
68
|
-
if (parentContext) {
|
|
69
|
-
request ??= parentContext.request;
|
|
70
|
-
response ??= parentContext.response;
|
|
71
|
-
inherited ||= parentContext.inherited;
|
|
72
|
-
} else {
|
|
73
|
-
request ??= {};
|
|
74
|
-
response ??= {};
|
|
75
|
-
}
|
|
76
|
-
const context = {
|
|
77
|
-
token,
|
|
78
|
-
config,
|
|
79
|
-
inherited,
|
|
80
|
-
request,
|
|
81
|
-
response,
|
|
82
|
-
collections: new Proxy({}, {
|
|
83
|
-
get: (_, collectionName2) => {
|
|
84
|
-
if (typeof collectionName2 !== "string") {
|
|
85
|
-
throw new Error();
|
|
86
|
-
}
|
|
87
|
-
return indepthCollection(collectionName2, collections, context);
|
|
88
|
-
}
|
|
89
|
-
}),
|
|
90
|
-
log: async (message, details) => {
|
|
91
|
-
return getDatabaseCollection("log").insertOne({
|
|
92
|
-
message,
|
|
93
|
-
details,
|
|
94
|
-
context: collectionName,
|
|
95
|
-
owner: token.sub,
|
|
96
|
-
created_at: /* @__PURE__ */ new Date()
|
|
97
|
-
});
|
|
98
|
-
},
|
|
99
|
-
error: (httpStatus, error) => {
|
|
100
|
-
return endpointError(Object.assign({
|
|
101
|
-
httpStatus
|
|
102
|
-
}, error));
|
|
103
|
-
},
|
|
104
|
-
limitRate: (params) => {
|
|
105
|
-
return limitRate(params, context);
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
if (isCollectionContext(context, collectionName) && collectionName) {
|
|
109
|
-
const description = throwIfError(await getCollectionAsset(collectionName, "description"));
|
|
110
|
-
context.description = await preloadDescription(description);
|
|
111
|
-
context.collectionName = collectionName;
|
|
112
|
-
if (!options.calledFunction && parentContext && "calledFunction" in parentContext) {
|
|
113
|
-
context.calledFunction = parentContext.calledFunction;
|
|
114
|
-
} else {
|
|
115
|
-
context.calledFunction = options.calledFunction;
|
|
116
|
-
}
|
|
117
|
-
context.collection = indepthCollection(collectionName, collections, context);
|
|
118
|
-
}
|
|
119
|
-
return context;
|
|
120
|
-
};
|
package/dist/database.mjs
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import { getConfig } from "@aeriajs/entrypoint";
|
|
3
|
-
import { inspect } from "node:util";
|
|
4
|
-
import { MongoClient } from "mongodb";
|
|
5
|
-
export {
|
|
6
|
-
ObjectId
|
|
7
|
-
} from "mongodb";
|
|
8
|
-
const dbMemo = {};
|
|
9
|
-
export const getDatabase = async () => {
|
|
10
|
-
if (!dbMemo.db) {
|
|
11
|
-
const config = await getConfig();
|
|
12
|
-
const mongodbUri = await (async () => {
|
|
13
|
-
const envUri = config.database?.mongodbUrl;
|
|
14
|
-
if (!envUri) {
|
|
15
|
-
console.warn("mongo URI wasn't supplied, fallbacking to memory storage (this means your data will only be alive during runtime)");
|
|
16
|
-
const { MongoMemoryServer } = await import("mongodb-memory-server");
|
|
17
|
-
const mongod = await MongoMemoryServer.create();
|
|
18
|
-
return mongod.getUri();
|
|
19
|
-
}
|
|
20
|
-
return envUri;
|
|
21
|
-
})();
|
|
22
|
-
const logQueries = config.database?.logQueries || false;
|
|
23
|
-
const client = new MongoClient(mongodbUri, {
|
|
24
|
-
monitorCommands: logQueries
|
|
25
|
-
});
|
|
26
|
-
if (logQueries) {
|
|
27
|
-
client.on("commandStarted", (event) => {
|
|
28
|
-
console.debug(inspect(event, {
|
|
29
|
-
colors: true,
|
|
30
|
-
compact: false,
|
|
31
|
-
depth: Infinity
|
|
32
|
-
}));
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
dbMemo.client = client;
|
|
36
|
-
dbMemo.db = client.db();
|
|
37
|
-
}
|
|
38
|
-
return dbMemo;
|
|
39
|
-
};
|
|
40
|
-
export const getDatabaseSync = () => {
|
|
41
|
-
if (!dbMemo.db) {
|
|
42
|
-
throw new Error("getDatabaseSync() called with no active database");
|
|
43
|
-
}
|
|
44
|
-
return dbMemo.db;
|
|
45
|
-
};
|
|
46
|
-
export const getDatabaseCollection = (collectionName) => {
|
|
47
|
-
const db = getDatabaseSync();
|
|
48
|
-
return db.collection(collectionName);
|
|
49
|
-
};
|
package/dist/endpoints.mjs
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
import { getCollections, getRouter, getAvailableRoles } from "@aeriajs/entrypoint";
|
|
3
|
-
import { deepMerge } from "@aeriajs/common";
|
|
4
|
-
import { isFunctionExposed, FunctionExposedStatus } from "./accessControl.mjs";
|
|
5
|
-
import * as builtinFunctions from "./functions/index.mjs";
|
|
6
|
-
export const getEndpoints = async () => {
|
|
7
|
-
const router = await getRouter();
|
|
8
|
-
const collections = await getCollections();
|
|
9
|
-
const functions = {};
|
|
10
|
-
for (const collectionName in collections) {
|
|
11
|
-
const candidate = collections[collectionName];
|
|
12
|
-
const collection = typeof candidate === "function" ? candidate() : candidate;
|
|
13
|
-
const {
|
|
14
|
-
description,
|
|
15
|
-
functions: collectionFunctions,
|
|
16
|
-
contracts,
|
|
17
|
-
exposedFunctions = {}
|
|
18
|
-
} = collection;
|
|
19
|
-
if (collectionFunctions) {
|
|
20
|
-
for (const fnName in collectionFunctions) {
|
|
21
|
-
const exposedStatus = await isFunctionExposed(collection, fnName);
|
|
22
|
-
if (exposedStatus !== FunctionExposedStatus.FunctionAccessible) {
|
|
23
|
-
continue;
|
|
24
|
-
}
|
|
25
|
-
const endpoint = `/${description.$id}/${fnName}`;
|
|
26
|
-
const exposed = exposedFunctions[fnName];
|
|
27
|
-
const roles = Array.isArray(exposed) ? exposed : exposed ? await getAvailableRoles() : [];
|
|
28
|
-
const endpointContracts = {
|
|
29
|
-
POST: null
|
|
30
|
-
};
|
|
31
|
-
if (roles.length) {
|
|
32
|
-
endpointContracts.POST ??= {};
|
|
33
|
-
endpointContracts.POST.roles = roles;
|
|
34
|
-
}
|
|
35
|
-
if (contracts && fnName in contracts) {
|
|
36
|
-
endpointContracts.POST ??= {};
|
|
37
|
-
Object.assign(endpointContracts.POST, contracts[fnName]);
|
|
38
|
-
}
|
|
39
|
-
if (fnName in builtinFunctions) {
|
|
40
|
-
endpointContracts.POST ??= {};
|
|
41
|
-
endpointContracts.POST.builtin = true;
|
|
42
|
-
}
|
|
43
|
-
functions[endpoint] = endpointContracts;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
const result = deepMerge(
|
|
48
|
-
functions,
|
|
49
|
-
router?.routesMeta || {}
|
|
50
|
-
);
|
|
51
|
-
return result;
|
|
52
|
-
};
|