@classytic/arc 2.10.3 → 2.10.8
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 +1 -1
- package/dist/{BaseController-CbKKIflT.mjs → BaseController-DVNKvoX4.mjs} +151 -131
- package/dist/actionPermissions-TUVR3uiZ.mjs +22 -0
- package/dist/adapters/index.d.mts +2 -2
- package/dist/audit/index.d.mts +2 -2
- package/dist/audit/index.mjs +15 -17
- package/dist/auth/index.d.mts +4 -4
- package/dist/auth/index.mjs +5 -5
- package/dist/auth/redis-session.d.mts +1 -1
- package/dist/cache/index.d.mts +2 -2
- package/dist/cache/index.mjs +3 -3
- package/dist/cli/commands/docs.mjs +2 -2
- package/dist/cli/commands/generate.mjs +1 -1
- package/dist/cli/commands/init.mjs +1 -1
- package/dist/cli/commands/introspect.mjs +1 -1
- package/dist/context/index.d.mts +58 -0
- package/dist/context/index.mjs +2 -0
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +2 -2
- package/dist/{core-CcR01lup.mjs → core-3MWJosCH.mjs} +139 -91
- package/dist/{createApp-BuvPma24.mjs → createApp-BwnEAO2h.mjs} +54 -20
- package/dist/docs/index.d.mts +2 -2
- package/dist/docs/index.mjs +2 -2
- package/dist/{elevation-C7hgL_aI.mjs → elevation-Dci0AYLT.mjs} +2 -2
- package/dist/errorHandler-2ii4RIYr.d.mts +114 -0
- package/dist/{errorHandler-Bb49BvPD.mjs → errorHandler-CSxe7KIM.mjs} +1 -1
- package/dist/{eventPlugin-DCUjuiQT.mjs → eventPlugin-ByU4Cv0e.mjs} +1 -1
- package/dist/{eventPlugin-CxWgpd6K.d.mts → eventPlugin-D1ThQ1Pp.d.mts} +1 -1
- package/dist/events/index.d.mts +4 -4
- package/dist/events/index.mjs +69 -51
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/factory/index.d.mts +1 -1
- package/dist/factory/index.mjs +2 -2
- package/dist/{fields-Lo1VUDpt.d.mts → fields-C8Y0XLAu.d.mts} +1 -1
- package/dist/hooks/index.d.mts +1 -1
- package/dist/hooks/index.mjs +1 -1
- package/dist/idempotency/index.d.mts +3 -3
- package/dist/idempotency/index.mjs +38 -27
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/{index-Cl0uoKd5.d.mts → index-BGbpGVyM.d.mts} +2362 -2155
- package/dist/{index-DStwgFUK.d.mts → index-BziRPS4H.d.mts} +1 -1
- package/dist/{index-ChIw3776.d.mts → index-C_Noptz-.d.mts} +3 -3
- package/dist/{index-8qw4y6ff.d.mts → index-EqQN6p0W.d.mts} +3 -3
- package/dist/index.d.mts +7 -219
- package/dist/index.mjs +8 -128
- package/dist/integrations/event-gateway.d.mts +1 -1
- package/dist/integrations/event-gateway.mjs +1 -1
- package/dist/integrations/index.d.mts +1 -1
- package/dist/integrations/mcp/index.d.mts +2 -2
- package/dist/integrations/mcp/index.mjs +1 -1
- package/dist/integrations/mcp/testing.d.mts +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/logger/index.d.mts +81 -0
- package/dist/{logger-DLg8-Ueg.mjs → logger/index.mjs} +1 -6
- package/dist/middleware/index.d.mts +109 -0
- package/dist/middleware/index.mjs +70 -0
- package/dist/multipartBody-CUQGVlM_.mjs +123 -0
- package/dist/{openapi-B5F8AddX.mjs → openapi-DpNpqBmo.mjs} +9 -7
- package/dist/org/index.d.mts +2 -2
- package/dist/permissions/index.d.mts +2 -2
- package/dist/permissions/index.mjs +3 -3
- package/dist/{permissions-Dk6mshja.mjs → permissions-wkqRwicB.mjs} +2 -2
- package/dist/pipe-CGJxqDGx.mjs +62 -0
- package/dist/pipeline/index.d.mts +62 -0
- package/dist/pipeline/index.mjs +53 -0
- package/dist/plugins/index.d.mts +25 -5
- package/dist/plugins/index.mjs +9 -9
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/presets/filesUpload.d.mts +4 -4
- package/dist/presets/filesUpload.mjs +255 -1
- package/dist/presets/index.d.mts +1 -1
- package/dist/presets/index.mjs +2 -2
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/multiTenant.mjs +42 -8
- package/dist/presets/search.d.mts +2 -2
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-fLJVXdVn.mjs → presets-CrwOvuXI.mjs} +1 -1
- package/dist/{queryCachePlugin-DQCEfJis.mjs → queryCachePlugin-ChLNZvFT.mjs} +2 -2
- package/dist/{queryCachePlugin-BKbWjgDG.d.mts → queryCachePlugin-Dumka73q.d.mts} +1 -1
- package/dist/{queryParser-DBqBB6AC.mjs → queryParser-NR__Qiju.mjs} +68 -1
- package/dist/{redis-DqyeggCa.d.mts → redis-MXLp1oOf.d.mts} +1 -1
- package/dist/{redis-stream-CakIQmwR.d.mts → redis-stream-bkO88VHx.d.mts} +1 -1
- package/dist/registry/index.d.mts +1 -1
- package/dist/registry/index.mjs +2 -2
- package/dist/{requestContext-xHIKedG6.mjs → requestContext-C38GskNt.mjs} +1 -1
- package/dist/{resourceToTools-BElv3xPT.mjs → resourceToTools-BhF3JV5p.mjs} +8 -3
- package/dist/scope/index.d.mts +2 -2
- package/dist/scope/index.mjs +2 -2
- package/dist/{sse-yBCgOLGu.mjs → sse-D8UeDwis.mjs} +1 -1
- package/dist/{store-helpers-ZCSMJJAX.mjs → store-helpers-DYYUQbQN.mjs} +4 -0
- package/dist/testing/index.d.mts +2 -2
- package/dist/testing/index.mjs +11 -2
- package/dist/testing/storageContract.d.mts +1 -1
- package/dist/types/index.d.mts +4 -4
- package/dist/types/index.mjs +1 -1
- package/dist/types/storage.d.mts +1 -1
- package/dist/{types-Btdda02s.d.mts → types-CVKBssX5.d.mts} +1 -1
- package/dist/{types-Co8k3NyS.d.mts → types-CVdgPXBW.d.mts} +22 -9
- package/dist/utils/index.d.mts +73 -3
- package/dist/utils/index.mjs +4 -4
- package/dist/{utils-B2fNOD_i.mjs → utils-LMwVidKy.mjs} +20 -2
- package/dist/versioning-CeUXHfjw.d.mts +117 -0
- package/package.json +22 -6
- package/skills/arc/SKILL.md +1 -1
- package/dist/errorHandler-DRQ3EqfL.d.mts +0 -218
- package/dist/filesUpload-t21LS-py.mjs +0 -377
- /package/dist/{EventTransport-CUw5NNWe.d.mts → EventTransport-CfVEGaEl.d.mts} +0 -0
- /package/dist/{HookSystem-BNYKnrXF.mjs → HookSystem-BjFu7zf1.mjs} +0 -0
- /package/dist/{ResourceRegistry-BPd6NQDm.mjs → ResourceRegistry-CcN2LVrc.mjs} +0 -0
- /package/dist/{betterAuthOpenApi-BBRVhjQN.mjs → betterAuthOpenApi--rdY15Ld.mjs} +0 -0
- /package/dist/{caching-CBpK_SCM.mjs → caching-3h93rkJM.mjs} +0 -0
- /package/dist/{createActionRouter-Bp_5c_2b.mjs → createActionRouter-C8UUB3Px.mjs} +0 -0
- /package/dist/{elevation-C5SwtkAn.d.mts → elevation-s5ykdNHr.d.mts} +0 -0
- /package/dist/{errors-CCSsMpXE.d.mts → errors-BI8kEKsO.d.mts} +0 -0
- /package/dist/{errors-D5c-5BJL.mjs → errors-BqdUDja_.mjs} +0 -0
- /package/dist/{externalPaths-BQ8QijNH.d.mts → externalPaths-Bapitwvd.d.mts} +0 -0
- /package/dist/{fields-bxkeltzz.mjs → fields-CTMWOUDt.mjs} +0 -0
- /package/dist/{interface-CSbZdv_3.d.mts → interface-B-pe8fhj.d.mts} +0 -0
- /package/dist/{interface-D218ikEo.d.mts → interface-yhyb_pLY.d.mts} +0 -0
- /package/dist/{keys-qcD-TVJl.mjs → keys-nWQGUTu1.mjs} +0 -0
- /package/dist/{loadResources-BAzJItAJ.mjs → loadResources-Bksk8ydA.mjs} +0 -0
- /package/dist/{memory-B5Amv9A1.mjs → memory-DqI-449b.mjs} +0 -0
- /package/dist/{metrics-DuhiSEZI.mjs → metrics-TuOmguhi.mjs} +0 -0
- /package/dist/{pluralize-A0tWEl1K.mjs → pluralize-CWP6MB39.mjs} +0 -0
- /package/dist/{registry-B3lRFBWo.mjs → registry-B0Wl7uVV.mjs} +0 -0
- /package/dist/{replyHelpers-CXtJDAZ0.mjs → replyHelpers-BLojtuvR.mjs} +0 -0
- /package/dist/{sessionManager-BkzVU8h2.d.mts → sessionManager-D-oNWHz3.d.mts} +0 -0
- /package/dist/{storage-CVk_SEn2.d.mts → storage-BwGQXUpd.d.mts} +0 -0
- /package/dist/{tracing-65B51Dw3.d.mts → tracing-xqXzWeaf.d.mts} +0 -0
- /package/dist/{types-Csi3FLfq.mjs → types-CDnTEpga.mjs} +0 -0
- /package/dist/{types-DV9WDfeg.mjs → types-D57iXYb8.mjs} +0 -0
- /package/dist/{types-BD85MlEK.d.mts → types-tgR4Pt8F.d.mts} +0 -0
- /package/dist/{versioning-C2U_bLY0.mjs → versioning-B6mimogM.mjs} +0 -0
|
@@ -1,2 +1,256 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { o as getOrgId, p as getUserId } from "../types-AOD8fxIw.mjs";
|
|
2
|
+
import { i as NotFoundError, u as ValidationError } from "../errors-BqdUDja_.mjs";
|
|
3
|
+
import { C as requireAuth, y as allowPublic } from "../permissions-wkqRwicB.mjs";
|
|
4
|
+
import { t as multipartBody } from "../multipartBody-CUQGVlM_.mjs";
|
|
5
|
+
//#region src/presets/filesUpload.ts
|
|
6
|
+
const DEFAULT_FIELD_NAME = "file";
|
|
7
|
+
const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
8
|
+
function defaultContextFrom(scope) {
|
|
9
|
+
if (!scope) return {};
|
|
10
|
+
const userId = getUserId(scope);
|
|
11
|
+
const organizationId = getOrgId(scope);
|
|
12
|
+
const ctx = {};
|
|
13
|
+
if (userId !== void 0) ctx.userId = userId;
|
|
14
|
+
if (organizationId !== void 0) ctx.organizationId = organizationId;
|
|
15
|
+
return ctx;
|
|
16
|
+
}
|
|
17
|
+
function buildStorageContext(request, contextFrom) {
|
|
18
|
+
const scope = request.scope;
|
|
19
|
+
return {
|
|
20
|
+
scope: contextFrom(scope),
|
|
21
|
+
requestId: request.id
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Parse a single-range `Range: bytes=start-end` header.
|
|
26
|
+
*
|
|
27
|
+
* Returns `undefined` when the header is missing or unparseable. Only
|
|
28
|
+
* satisfiable single ranges are supported — multi-range requests fall through
|
|
29
|
+
* to the full-object response (per RFC 7233 §4.1 a server MAY ignore ranges).
|
|
30
|
+
*/
|
|
31
|
+
function parseRangeHeader(header, totalSize) {
|
|
32
|
+
if (!header || !header.startsWith("bytes=")) return void 0;
|
|
33
|
+
const spec = header.slice(6).split(",")[0]?.trim();
|
|
34
|
+
if (!spec) return void 0;
|
|
35
|
+
const dashIndex = spec.indexOf("-");
|
|
36
|
+
if (dashIndex === -1) return void 0;
|
|
37
|
+
const startRaw = spec.slice(0, dashIndex);
|
|
38
|
+
const endRaw = spec.slice(dashIndex + 1);
|
|
39
|
+
if (startRaw === "") {
|
|
40
|
+
if (totalSize === void 0) return void 0;
|
|
41
|
+
const suffix = Number(endRaw);
|
|
42
|
+
if (!Number.isFinite(suffix) || suffix <= 0) return void 0;
|
|
43
|
+
return {
|
|
44
|
+
start: Math.max(0, totalSize - suffix),
|
|
45
|
+
end: totalSize - 1
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const start = Number(startRaw);
|
|
49
|
+
if (!Number.isFinite(start) || start < 0) return void 0;
|
|
50
|
+
if (endRaw === "") {
|
|
51
|
+
if (totalSize === void 0) return void 0;
|
|
52
|
+
return {
|
|
53
|
+
start,
|
|
54
|
+
end: totalSize - 1
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const end = Number(endRaw);
|
|
58
|
+
if (!Number.isFinite(end) || end < start) return void 0;
|
|
59
|
+
if (totalSize !== void 0 && end >= totalSize) return {
|
|
60
|
+
start,
|
|
61
|
+
end: totalSize - 1
|
|
62
|
+
};
|
|
63
|
+
return {
|
|
64
|
+
start,
|
|
65
|
+
end
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Strict policy — rejects filenames that could escape a storage root or
|
|
70
|
+
* confuse a filesystem. Safe default for disk/S3 adapters that compose
|
|
71
|
+
* `${prefix}/${filename}` or `path.join(root, filename)`.
|
|
72
|
+
*/
|
|
73
|
+
function strictFilenamePolicy(filename) {
|
|
74
|
+
if (filename.length === 0) throw new ValidationError("Upload filename is empty");
|
|
75
|
+
if (filename.length > 255) throw new ValidationError("Upload filename exceeds 255 characters");
|
|
76
|
+
if (filename.includes("\0")) throw new ValidationError("Upload filename contains a NUL byte");
|
|
77
|
+
if (filename.includes("/") || filename.includes("\\")) throw new ValidationError("Upload filename contains a path separator");
|
|
78
|
+
if (filename === "." || filename === "..") throw new ValidationError("Upload filename is a path traversal component");
|
|
79
|
+
return filename;
|
|
80
|
+
}
|
|
81
|
+
/** Resolve the user-supplied policy option into a concrete validator. */
|
|
82
|
+
function resolveFilenamePolicy(policy) {
|
|
83
|
+
if (policy === void 0 || policy === true) return strictFilenamePolicy;
|
|
84
|
+
if (policy === false || policy === "*") return (f) => f;
|
|
85
|
+
if (typeof policy === "function") return (filename) => {
|
|
86
|
+
const result = policy(filename);
|
|
87
|
+
if (result === false) throw new ValidationError(`Upload filename rejected: ${filename}`);
|
|
88
|
+
if (typeof result === "string") return result;
|
|
89
|
+
return filename;
|
|
90
|
+
};
|
|
91
|
+
return strictFilenamePolicy;
|
|
92
|
+
}
|
|
93
|
+
function makeUploadHandler(deps) {
|
|
94
|
+
return async function uploadHandler(request, reply) {
|
|
95
|
+
const file = (request.body?._files)?.[deps.fieldName];
|
|
96
|
+
if (!file) throw new ValidationError(`Missing file field '${deps.fieldName}' in multipart body`);
|
|
97
|
+
const filename = deps.applyFilenamePolicy(file.filename);
|
|
98
|
+
const ctx = buildStorageContext(request, deps.contextFrom);
|
|
99
|
+
const result = await deps.storage.upload({
|
|
100
|
+
buffer: file.buffer,
|
|
101
|
+
filename,
|
|
102
|
+
mimeType: file.mimetype,
|
|
103
|
+
size: file.size
|
|
104
|
+
}, ctx);
|
|
105
|
+
return reply.code(201).send({
|
|
106
|
+
success: true,
|
|
107
|
+
data: toResponseFile(result)
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function toResponseFile(file) {
|
|
112
|
+
const payload = {
|
|
113
|
+
id: file.id,
|
|
114
|
+
url: file.url,
|
|
115
|
+
pathname: file.pathname,
|
|
116
|
+
contentType: file.contentType,
|
|
117
|
+
bytes: file.bytes
|
|
118
|
+
};
|
|
119
|
+
if (file.metadata !== void 0) payload.metadata = file.metadata;
|
|
120
|
+
return payload;
|
|
121
|
+
}
|
|
122
|
+
function makeReadHandler(deps) {
|
|
123
|
+
return async function readHandler(request, reply) {
|
|
124
|
+
const { id } = request.params;
|
|
125
|
+
const ctx = buildStorageContext(request, deps.contextFrom);
|
|
126
|
+
reply.header("Accept-Ranges", "bytes");
|
|
127
|
+
const rangeHeader = request.headers.range;
|
|
128
|
+
let result;
|
|
129
|
+
try {
|
|
130
|
+
const parsed = rangeHeader ? parseRangeHeader(rangeHeader, void 0) : void 0;
|
|
131
|
+
result = await deps.storage.read(id, ctx, parsed);
|
|
132
|
+
} catch (err) {
|
|
133
|
+
throw toNotFound(err, "File", id);
|
|
134
|
+
}
|
|
135
|
+
if (result.kind === "buffer") return sendBuffer(reply, result, rangeHeader);
|
|
136
|
+
return sendStream(reply, result, rangeHeader);
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function sendBuffer(reply, result, rangeHeader) {
|
|
140
|
+
reply.type(result.contentType);
|
|
141
|
+
const total = result.totalBytes ?? result.buffer.length;
|
|
142
|
+
if (result.range) {
|
|
143
|
+
const { start, end } = result.range;
|
|
144
|
+
reply.code(206);
|
|
145
|
+
reply.header("Content-Range", `bytes ${start}-${end}/${total}`);
|
|
146
|
+
reply.header("Content-Length", String(result.buffer.length));
|
|
147
|
+
return reply.send(result.buffer);
|
|
148
|
+
}
|
|
149
|
+
if (rangeHeader) {
|
|
150
|
+
const parsed = parseRangeHeader(rangeHeader, total);
|
|
151
|
+
if (parsed) {
|
|
152
|
+
const slice = result.buffer.subarray(parsed.start, parsed.end + 1);
|
|
153
|
+
reply.code(206);
|
|
154
|
+
reply.header("Content-Range", `bytes ${parsed.start}-${parsed.end}/${total}`);
|
|
155
|
+
reply.header("Content-Length", String(slice.length));
|
|
156
|
+
return reply.send(slice);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
reply.header("Content-Length", String(result.buffer.length));
|
|
160
|
+
return reply.send(result.buffer);
|
|
161
|
+
}
|
|
162
|
+
function sendStream(reply, result, rangeHeader) {
|
|
163
|
+
reply.type(result.contentType);
|
|
164
|
+
if (result.range && result.bytes !== void 0) {
|
|
165
|
+
const { start, end } = result.range;
|
|
166
|
+
reply.code(206);
|
|
167
|
+
reply.header("Content-Range", `bytes ${start}-${end}/${result.bytes}`);
|
|
168
|
+
reply.header("Content-Length", String(end - start + 1));
|
|
169
|
+
} else if (result.bytes !== void 0) {
|
|
170
|
+
reply.header("Content-Length", String(result.bytes));
|
|
171
|
+
if (rangeHeader) reply.request.log.debug({ url: reply.request.url }, "filesUploadPreset: adapter returned unsliced stream for a range request — sending full object");
|
|
172
|
+
}
|
|
173
|
+
return reply.send(result.stream);
|
|
174
|
+
}
|
|
175
|
+
function makeDeleteHandler(deps) {
|
|
176
|
+
return async function deleteHandler(request, reply) {
|
|
177
|
+
const { id } = request.params;
|
|
178
|
+
const ctx = buildStorageContext(request, deps.contextFrom);
|
|
179
|
+
if (!await deps.storage.delete(id, ctx)) throw new NotFoundError("File", id);
|
|
180
|
+
return reply.code(204).send();
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function toNotFound(err, resource, id) {
|
|
184
|
+
if (err instanceof NotFoundError) return err;
|
|
185
|
+
const maybe = err;
|
|
186
|
+
if (maybe?.statusCode === 404 || maybe?.code === "NOT_FOUND") return new NotFoundError(resource, id);
|
|
187
|
+
if (typeof maybe?.message === "string" && /not\s*found/i.test(maybe.message)) return new NotFoundError(resource, id);
|
|
188
|
+
return err;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Create a files-upload preset bound to a `Storage` adapter.
|
|
192
|
+
*
|
|
193
|
+
* The preset uses `raw: true` routes so binary responses bypass arc's JSON
|
|
194
|
+
* envelope. Upload still returns the standard `{ success: true, data }`
|
|
195
|
+
* envelope manually because the response is structured metadata, not bytes.
|
|
196
|
+
*/
|
|
197
|
+
function filesUploadPreset(options) {
|
|
198
|
+
if (!options?.storage) throw new Error("filesUploadPreset: `storage` is required");
|
|
199
|
+
const deps = {
|
|
200
|
+
storage: options.storage,
|
|
201
|
+
fieldName: options.fieldName ?? DEFAULT_FIELD_NAME,
|
|
202
|
+
contextFrom: options.contextFrom ?? defaultContextFrom,
|
|
203
|
+
applyFilenamePolicy: resolveFilenamePolicy(options.sanitizeFilename)
|
|
204
|
+
};
|
|
205
|
+
const maxFileSize = options.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
|
|
206
|
+
const allowedMimeTypes = options.allowedMimeTypes;
|
|
207
|
+
const includeRoutes = {
|
|
208
|
+
upload: options.includeRoutes?.upload ?? true,
|
|
209
|
+
read: options.includeRoutes?.read ?? true,
|
|
210
|
+
delete: options.includeRoutes?.delete ?? true
|
|
211
|
+
};
|
|
212
|
+
return {
|
|
213
|
+
name: "filesUpload",
|
|
214
|
+
routes: (permissions) => {
|
|
215
|
+
const routes = [];
|
|
216
|
+
if (includeRoutes.upload) routes.push({
|
|
217
|
+
method: "POST",
|
|
218
|
+
path: "/upload",
|
|
219
|
+
operation: "filesUpload.upload",
|
|
220
|
+
summary: "Upload a file",
|
|
221
|
+
description: "Accepts a multipart/form-data request and persists the bytes via the configured Storage adapter.",
|
|
222
|
+
permissions: options.permissions?.upload ?? permissions.create ?? requireAuth(),
|
|
223
|
+
preHandler: [multipartBody({
|
|
224
|
+
maxFileSize,
|
|
225
|
+
allowedMimeTypes,
|
|
226
|
+
requiredFields: [deps.fieldName]
|
|
227
|
+
})],
|
|
228
|
+
raw: true,
|
|
229
|
+
handler: makeUploadHandler(deps)
|
|
230
|
+
});
|
|
231
|
+
if (includeRoutes.read) routes.push({
|
|
232
|
+
method: "GET",
|
|
233
|
+
path: "/:id",
|
|
234
|
+
operation: "filesUpload.read",
|
|
235
|
+
summary: "Download a file",
|
|
236
|
+
description: "Streams the stored bytes. Supports single-range `Range: bytes=start-end`.",
|
|
237
|
+
permissions: options.permissions?.read ?? permissions.get ?? allowPublic(),
|
|
238
|
+
raw: true,
|
|
239
|
+
handler: makeReadHandler(deps),
|
|
240
|
+
mcp: false
|
|
241
|
+
});
|
|
242
|
+
if (includeRoutes.delete) routes.push({
|
|
243
|
+
method: "DELETE",
|
|
244
|
+
path: "/:id",
|
|
245
|
+
operation: "filesUpload.delete",
|
|
246
|
+
summary: "Delete a file",
|
|
247
|
+
permissions: options.permissions?.delete ?? permissions.delete ?? requireAuth(),
|
|
248
|
+
raw: true,
|
|
249
|
+
handler: makeDeleteHandler(deps)
|
|
250
|
+
});
|
|
251
|
+
return routes;
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
//#endregion
|
|
2
256
|
export { filesUploadPreset };
|
package/dist/presets/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Mt as IControllerResponse, Nt as IRequestContext, Yt as AnyRecord, _t as PresetResult, bt as ResourceConfig, d as PaginationResult } from "../index-BGbpGVyM.mjs";
|
|
2
2
|
import { FilesUploadPresetOptions, FilesUploadPresetPermissions, FilesUploadPresetRoutes, filesUploadPreset } from "./filesUpload.mjs";
|
|
3
3
|
import { MultiTenantOptions, TenantFieldSpec, multiTenantPreset } from "./multiTenant.mjs";
|
|
4
4
|
import { SearchHandler, SearchPresetOptions, SearchRouteConfig, searchPreset } from "./search.mjs";
|
package/dist/presets/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { multiTenantPreset } from "./multiTenant.mjs";
|
|
2
|
-
import { a as registerPreset, c as auditedPreset, d as ownedByUserPreset, i as getPreset, l as softDeletePreset, n as flexibleMultiTenantPreset, o as treePreset, r as getAvailablePresets, s as bulkPreset, t as applyPresets, u as slugLookupPreset } from "../presets-
|
|
3
|
-
import {
|
|
2
|
+
import { a as registerPreset, c as auditedPreset, d as ownedByUserPreset, i as getPreset, l as softDeletePreset, n as flexibleMultiTenantPreset, o as treePreset, r as getAvailablePresets, s as bulkPreset, t as applyPresets, u as slugLookupPreset } from "../presets-CrwOvuXI.mjs";
|
|
3
|
+
import { filesUploadPreset } from "./filesUpload.mjs";
|
|
4
4
|
import { searchPreset } from "./search.mjs";
|
|
5
5
|
export { applyPresets, auditedPreset, bulkPreset, filesUploadPreset, flexibleMultiTenantPreset, getAvailablePresets, getPreset, multiTenantPreset, ownedByUserPreset, registerPreset, searchPreset, slugLookupPreset, softDeletePreset, treePreset };
|
|
@@ -10,6 +10,20 @@ function resolveSpec(scope, spec) {
|
|
|
10
10
|
if (spec.type === "org") return getOrgId(scope);
|
|
11
11
|
if (spec.type === "team") return getTeamId(scope);
|
|
12
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Stash the resolved tenant field map on the request so `BaseController`
|
|
15
|
+
* can forward it to the repository layer as top-level options. Needed by
|
|
16
|
+
* plugin-scoped repos (mongokit's `multiTenantPlugin`) that read tenant
|
|
17
|
+
* from `context.<field>` rather than from filter/query/data stamping.
|
|
18
|
+
*/
|
|
19
|
+
function stashTenantFields(request, resolved) {
|
|
20
|
+
if (Object.keys(resolved).length === 0) return;
|
|
21
|
+
const target = request;
|
|
22
|
+
target._tenantFields = {
|
|
23
|
+
...target._tenantFields ?? {},
|
|
24
|
+
...resolved
|
|
25
|
+
};
|
|
26
|
+
}
|
|
13
27
|
/** Resolve every spec — returns the partial map of fields that have a value. */
|
|
14
28
|
function resolveAll(scope, specs) {
|
|
15
29
|
const resolved = {};
|
|
@@ -34,10 +48,13 @@ function createTenantFilter(specs) {
|
|
|
34
48
|
const scope = getRequestScope(request);
|
|
35
49
|
if (isElevated(scope)) {
|
|
36
50
|
const { resolved } = resolveAll(scope, specs);
|
|
37
|
-
if (Object.keys(resolved).length > 0)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
51
|
+
if (Object.keys(resolved).length > 0) {
|
|
52
|
+
request._policyFilters = {
|
|
53
|
+
...request._policyFilters ?? {},
|
|
54
|
+
...resolved
|
|
55
|
+
};
|
|
56
|
+
stashTenantFields(request, resolved);
|
|
57
|
+
}
|
|
41
58
|
return;
|
|
42
59
|
}
|
|
43
60
|
if (hasOrgAccess(scope)) {
|
|
@@ -47,6 +64,7 @@ function createTenantFilter(specs) {
|
|
|
47
64
|
...request._policyFilters ?? {},
|
|
48
65
|
...resolved
|
|
49
66
|
};
|
|
67
|
+
stashTenantFields(request, resolved);
|
|
50
68
|
return;
|
|
51
69
|
}
|
|
52
70
|
reply.code(403).send({
|
|
@@ -81,10 +99,13 @@ function createFlexibleTenantFilter(specs) {
|
|
|
81
99
|
const scope = getRequestScope(request);
|
|
82
100
|
if (isElevated(scope)) {
|
|
83
101
|
const { resolved } = resolveAll(scope, specs);
|
|
84
|
-
if (Object.keys(resolved).length > 0)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
102
|
+
if (Object.keys(resolved).length > 0) {
|
|
103
|
+
request._policyFilters = {
|
|
104
|
+
...request._policyFilters ?? {},
|
|
105
|
+
...resolved
|
|
106
|
+
};
|
|
107
|
+
stashTenantFields(request, resolved);
|
|
108
|
+
}
|
|
88
109
|
return;
|
|
89
110
|
}
|
|
90
111
|
if (hasOrgAccess(scope)) {
|
|
@@ -94,6 +115,7 @@ function createFlexibleTenantFilter(specs) {
|
|
|
94
115
|
...request._policyFilters ?? {},
|
|
95
116
|
...resolved
|
|
96
117
|
};
|
|
118
|
+
stashTenantFields(request, resolved);
|
|
97
119
|
return;
|
|
98
120
|
}
|
|
99
121
|
reply.code(403).send({
|
|
@@ -109,6 +131,14 @@ function createFlexibleTenantFilter(specs) {
|
|
|
109
131
|
* Create tenant injection middleware.
|
|
110
132
|
* Walks the configured tenant fields and writes each into the request body.
|
|
111
133
|
* Fails closed if any required dimension is missing for non-elevated callers.
|
|
134
|
+
*
|
|
135
|
+
* Also stashes the resolved fields on `request._tenantFields` so
|
|
136
|
+
* `BaseController.tenantRepoOptions()` can forward them to the repo layer
|
|
137
|
+
* as top-level options — needed by plugin-scoped repos like mongokit's
|
|
138
|
+
* `multiTenantPlugin`, which reads tenant from `context.<field>` rather
|
|
139
|
+
* than from `data.<field>`. Without this forwarding, multi-field preset
|
|
140
|
+
* writes (update/delete) work only when the plugin's `allowDataInjection`
|
|
141
|
+
* fallback covers the operation's policy key, which is write-only.
|
|
112
142
|
*/
|
|
113
143
|
function createTenantInjection(specs) {
|
|
114
144
|
return async (request, reply) => {
|
|
@@ -124,6 +154,10 @@ function createTenantInjection(specs) {
|
|
|
124
154
|
return;
|
|
125
155
|
}
|
|
126
156
|
if (request.body) Object.assign(request.body, resolved);
|
|
157
|
+
request._tenantFields = {
|
|
158
|
+
...request._tenantFields ?? {},
|
|
159
|
+
...resolved
|
|
160
|
+
};
|
|
127
161
|
};
|
|
128
162
|
}
|
|
129
163
|
function multiTenantPreset(options = {}) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Ot as ControllerHandler, Tt as RouteMcpConfig, _t as PresetResult, wt as RouteDefinition } from "../index-BGbpGVyM.mjs";
|
|
2
|
+
import { c as PermissionCheck } from "../fields-C8Y0XLAu.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/presets/search.d.ts
|
|
5
5
|
/**
|
package/dist/presets/search.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { _ as isElevated, n as PUBLIC_SCOPE } from "./types-AOD8fxIw.mjs";
|
|
2
2
|
import { multiTenantPreset } from "./presets/multiTenant.mjs";
|
|
3
|
-
import { C as requireAuth, T as requireRoles, y as allowPublic } from "./permissions-
|
|
3
|
+
import { C as requireAuth, T as requireRoles, y as allowPublic } from "./permissions-wkqRwicB.mjs";
|
|
4
4
|
//#region src/presets/ownedByUser.ts
|
|
5
5
|
/**
|
|
6
6
|
* Create ownership check middleware.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
|
-
import { i as versionKey, r as tagVersionKey } from "./keys-
|
|
2
|
+
import { i as versionKey, r as tagVersionKey } from "./keys-nWQGUTu1.mjs";
|
|
3
3
|
import { t as hasEvents } from "./typeGuards-Cj5Rgvlg.mjs";
|
|
4
|
-
import { t as MemoryCacheStore } from "./memory-
|
|
4
|
+
import { t as MemoryCacheStore } from "./memory-DqI-449b.mjs";
|
|
5
5
|
import fp from "fastify-plugin";
|
|
6
6
|
//#region src/cache/QueryCache.ts
|
|
7
7
|
var QueryCache = class {
|
|
@@ -1,4 +1,71 @@
|
|
|
1
1
|
import { m as RESERVED_QUERY_PARAMS } from "./constants-BhY1OHoH.mjs";
|
|
2
|
+
//#region src/utils/simpleEqualityMatcher.ts
|
|
3
|
+
/**
|
|
4
|
+
* `simpleEqualityMatcher` — a minimal, dialect-agnostic flat-key equality
|
|
5
|
+
* matcher for `DataAdapter.matchesFilter` / `BaseController({ matchesFilter })`.
|
|
6
|
+
*
|
|
7
|
+
* **What it does:** for each `[key, expected]` in the filter, compares
|
|
8
|
+
* `item[key]` to `expected` via string coercion (so Mongo `ObjectId` values
|
|
9
|
+
* match their string representation) and returns `true` only if every
|
|
10
|
+
* filter entry matches. Array item values are matched implicitly (contains).
|
|
11
|
+
*
|
|
12
|
+
* **What it does NOT do:**
|
|
13
|
+
* - No `$eq` / `$ne` / `$in` / `$nin` / `$gt` / `$lt` / `$regex` / `$exists`
|
|
14
|
+
* - No `$and` / `$or`
|
|
15
|
+
* - No dot-path traversal (`"owner.id"`)
|
|
16
|
+
* - No schema-specific coercion
|
|
17
|
+
*
|
|
18
|
+
* **Why it exists:** 95%+ of arc's `_policyFilters` are produced by built-in
|
|
19
|
+
* permission helpers and are shaped like `{ ownerId: "u1" }` or
|
|
20
|
+
* `{ organizationId: "org_x" }` — flat equality. For that common shape,
|
|
21
|
+
* this helper is a safe, tested, 15-line defense-in-depth matcher that
|
|
22
|
+
* hosts using minimal repos (no `getOne(compoundFilter)` DB path) can opt
|
|
23
|
+
* into without arc shipping a full Mongo-syntax engine.
|
|
24
|
+
*
|
|
25
|
+
* **When to use:**
|
|
26
|
+
* - Your adapter/repo doesn't natively filter on `getOne(compoundFilter)`
|
|
27
|
+
* - Your `_policyFilters` are flat equality (from arc's built-in permission helpers)
|
|
28
|
+
* - You want defense-in-depth on `validateItemAccess` / `fetchDetailed`'s `getById` fallback
|
|
29
|
+
*
|
|
30
|
+
* **When NOT to use:**
|
|
31
|
+
* - Your `_policyFilters` use operators (`$in`, `$ne`, etc.) — supply a
|
|
32
|
+
* native matcher (mongokit's repo does the filter at the DB layer; for
|
|
33
|
+
* custom repos, wrap the kit's own predicate engine).
|
|
34
|
+
* - You're a mongokit / sqlitekit / Prisma user — the DB-level filter
|
|
35
|
+
* applied by `getOne(compoundFilter)` already covers this.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* import { simpleEqualityMatcher } from '@classytic/arc/utils';
|
|
40
|
+
*
|
|
41
|
+
* // On a custom adapter
|
|
42
|
+
* const adapter: DataAdapter = {
|
|
43
|
+
* repository,
|
|
44
|
+
* type: 'custom',
|
|
45
|
+
* name: 'in-memory',
|
|
46
|
+
* matchesFilter: simpleEqualityMatcher,
|
|
47
|
+
* };
|
|
48
|
+
*
|
|
49
|
+
* // Or directly on BaseController for ad-hoc controllers
|
|
50
|
+
* new BaseController(repo, { matchesFilter: simpleEqualityMatcher });
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
function simpleEqualityMatcher(item, filters) {
|
|
54
|
+
if (!item || typeof item !== "object") return false;
|
|
55
|
+
const obj = item;
|
|
56
|
+
for (const [key, expected] of Object.entries(filters)) {
|
|
57
|
+
if (expected && typeof expected === "object" && !Array.isArray(expected) && Object.getPrototypeOf(expected) === Object.prototype && Object.keys(expected).some((k) => k.startsWith("$"))) return false;
|
|
58
|
+
const actual = obj[key];
|
|
59
|
+
if (Array.isArray(actual)) {
|
|
60
|
+
const expectedStr = String(expected);
|
|
61
|
+
if (!actual.some((v) => String(v) === expectedStr)) return false;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (String(actual) !== String(expected)) return false;
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
//#endregion
|
|
2
69
|
//#region src/utils/queryParser.ts
|
|
3
70
|
/**
|
|
4
71
|
* Arc Query Parser - Default URL-to-Query Parser
|
|
@@ -349,4 +416,4 @@ function createQueryParser(options) {
|
|
|
349
416
|
return new ArcQueryParser(options);
|
|
350
417
|
}
|
|
351
418
|
//#endregion
|
|
352
|
-
export { createQueryParser as n, ArcQueryParser as t };
|
|
419
|
+
export { createQueryParser as n, simpleEqualityMatcher as r, ArcQueryParser as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as EventLogger, n as DomainEvent, o as EventTransport, r as EventHandler } from "./EventTransport-
|
|
1
|
+
import { i as EventLogger, n as DomainEvent, o as EventTransport, r as EventHandler } from "./EventTransport-CfVEGaEl.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/events/transports/redis-stream.d.ts
|
|
4
4
|
interface RedisStreamLike {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { R as RegisterOptions, k as IntrospectionPluginOptions, z as ResourceRegistry } from "../index-BGbpGVyM.mjs";
|
|
2
2
|
import { FastifyPluginAsync } from "fastify";
|
|
3
3
|
|
|
4
4
|
//#region src/registry/introspectionPlugin.d.ts
|
package/dist/registry/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { n as introspectionPlugin_default, t as introspectionPlugin } from "../registry-
|
|
2
|
-
import { t as ResourceRegistry } from "../ResourceRegistry-
|
|
1
|
+
import { n as introspectionPlugin_default, t as introspectionPlugin } from "../registry-B0Wl7uVV.mjs";
|
|
2
|
+
import { t as ResourceRegistry } from "../ResourceRegistry-CcN2LVrc.mjs";
|
|
3
3
|
export { ResourceRegistry, introspectionPlugin_default as introspectionPlugin, introspectionPlugin as introspectionPluginFn };
|
|
@@ -11,7 +11,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
11
11
|
*
|
|
12
12
|
* @example
|
|
13
13
|
* ```typescript
|
|
14
|
-
* import { requestContext } from '@classytic/arc';
|
|
14
|
+
* import { requestContext } from '@classytic/arc/context';
|
|
15
15
|
*
|
|
16
16
|
* // Anywhere in the call stack — no parameter passing needed
|
|
17
17
|
* async function auditAction(action: string) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { t as BaseController } from "./BaseController-
|
|
1
|
+
import { t as BaseController } from "./BaseController-DVNKvoX4.mjs";
|
|
2
2
|
import { n as normalizePermissionResult } from "./applyPermissionResult-QhV1Pa-g.mjs";
|
|
3
|
-
import { t as
|
|
3
|
+
import { t as resolveActionPermission } from "./actionPermissions-TUVR3uiZ.mjs";
|
|
4
|
+
import { t as pluralize } from "./pluralize-CWP6MB39.mjs";
|
|
4
5
|
import { z } from "zod";
|
|
5
6
|
//#region src/integrations/mcp/createMcpServer.ts
|
|
6
7
|
/**
|
|
@@ -655,7 +656,11 @@ function resourceToTools(resource, config = {}) {
|
|
|
655
656
|
}
|
|
656
657
|
const toolName = prefix ? `${prefix}_${actionName}_${resource.name}` : `${actionName}_${resource.name}`;
|
|
657
658
|
const handler = typeof entry === "function" ? entry : def.handler;
|
|
658
|
-
const actionPerms = (
|
|
659
|
+
const actionPerms = resolveActionPermission({
|
|
660
|
+
action: entry,
|
|
661
|
+
resourcePermissions: resource.permissions,
|
|
662
|
+
resourceActionPermissions: resource.actionPermissions
|
|
663
|
+
});
|
|
659
664
|
tools.push({
|
|
660
665
|
name: toolName,
|
|
661
666
|
description: String(description),
|
package/dist/scope/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as
|
|
2
|
-
import {
|
|
1
|
+
import { _ as isAuthenticated, a as getClientId, b as isOrgInScope, c as getOrgRoles, d as getScopeContextMap, f as getServiceScopes, g as hasOrgAccess, h as getUserRoles, i as getAncestorOrgIds, l as getRequestScope, m as getUserId, n as PUBLIC_SCOPE, o as getOrgContext, p as getTeamId, r as RequestScope, s as getOrgId, t as AUTHENTICATED_SCOPE, u as getScopeContext, v as isElevated, x as isService, y as isMember } from "../types-tgR4Pt8F.mjs";
|
|
2
|
+
import { i as elevationPlugin, n as ElevationOptions, r as _default, t as ElevationEvent } from "../elevation-s5ykdNHr.mjs";
|
|
3
3
|
import { FastifyReply, FastifyRequest } from "fastify";
|
|
4
4
|
|
|
5
5
|
//#region src/scope/rateLimitKey.d.ts
|
package/dist/scope/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { _ as isElevated, a as getOrgContext, b as isService, c as getRequestScope, d as getServiceScopes, f as getTeamId, g as isAuthenticated, h as hasOrgAccess, i as getClientId, l as getScopeContext, m as getUserRoles, n as PUBLIC_SCOPE, o as getOrgId, p as getUserId, r as getAncestorOrgIds, s as getOrgRoles, t as AUTHENTICATED_SCOPE, u as getScopeContextMap, v as isMember, y as isOrgInScope } from "../types-AOD8fxIw.mjs";
|
|
2
|
-
import { n as normalizeRoles } from "../types-
|
|
3
|
-
import { n as elevation_default, t as elevationPlugin } from "../elevation-
|
|
2
|
+
import { n as normalizeRoles } from "../types-D57iXYb8.mjs";
|
|
3
|
+
import { n as elevation_default, t as elevationPlugin } from "../elevation-Dci0AYLT.mjs";
|
|
4
4
|
//#region src/scope/rateLimitKey.ts
|
|
5
5
|
function createTenantKeyGenerator(opts) {
|
|
6
6
|
if (opts?.strategy) return opts.strategy;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
|
+
import { arcLog } from "./logger/index.mjs";
|
|
2
3
|
import { n as PUBLIC_SCOPE, o as getOrgId } from "./types-AOD8fxIw.mjs";
|
|
3
|
-
import { t as arcLog } from "./logger-DLg8-Ueg.mjs";
|
|
4
4
|
import fp from "fastify-plugin";
|
|
5
5
|
//#region src/plugins/sse.ts
|
|
6
6
|
var sse_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -18,6 +18,10 @@ function isNotFoundError(err) {
|
|
|
18
18
|
* Build a `safeGetOne(filter)` that papers over the throw-vs-null split
|
|
19
19
|
* in kit implementations. Real errors propagate; miss returns `null`.
|
|
20
20
|
* Throws if the repository lacks `getOne` — callers must check.
|
|
21
|
+
*
|
|
22
|
+
* Accepts `FilterInput` (the repo-core union) so callers can compose
|
|
23
|
+
* portable Filter IR (`and(eq(...), gt(...))`) OR pass a flat kit-native
|
|
24
|
+
* record. Both forms reach the kit's `getOne` unchanged — kits dispatch.
|
|
21
25
|
*/
|
|
22
26
|
function createSafeGetOne(repository) {
|
|
23
27
|
if (typeof repository.getOne !== "function") throw new Error("createSafeGetOne: repository.getOne is required");
|
package/dist/testing/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { d as ResourceLike, r as CreateAppOptions } from "../types-
|
|
1
|
+
import { B as ResourceDefinition, Yt as AnyRecord } from "../index-BGbpGVyM.mjs";
|
|
2
|
+
import { d as ResourceLike, r as CreateAppOptions } from "../types-CVdgPXBW.mjs";
|
|
3
3
|
import { StorageContractSetup, StorageContractSetupResult, runStorageContract } from "./storageContract.mjs";
|
|
4
4
|
import Fastify, { FastifyInstance, FastifyServerOptions } from "fastify";
|
|
5
5
|
import { Connection } from "mongoose";
|
package/dist/testing/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as CRUD_OPERATIONS } from "../constants-BhY1OHoH.mjs";
|
|
2
|
-
import { n as applyFieldWritePermissions, t as applyFieldReadPermissions } from "../fields-
|
|
2
|
+
import { n as applyFieldWritePermissions, t as applyFieldReadPermissions } from "../fields-CTMWOUDt.mjs";
|
|
3
3
|
import { runStorageContract } from "./storageContract.mjs";
|
|
4
4
|
import Fastify from "fastify";
|
|
5
5
|
import mongoose from "mongoose";
|
|
@@ -915,6 +915,15 @@ function createMockRepository(overrides = {}) {
|
|
|
915
915
|
success: true,
|
|
916
916
|
message: "Deleted"
|
|
917
917
|
}),
|
|
918
|
+
updateMany: vi.fn().mockResolvedValue({
|
|
919
|
+
acknowledged: true,
|
|
920
|
+
matchedCount: 0,
|
|
921
|
+
modifiedCount: 0
|
|
922
|
+
}),
|
|
923
|
+
deleteMany: vi.fn().mockResolvedValue({
|
|
924
|
+
acknowledged: true,
|
|
925
|
+
deletedCount: 0
|
|
926
|
+
}),
|
|
918
927
|
getBySlug: vi.fn().mockResolvedValue(null),
|
|
919
928
|
getDeleted: vi.fn().mockResolvedValue([]),
|
|
920
929
|
restore: vi.fn().mockResolvedValue(null),
|
|
@@ -1741,7 +1750,7 @@ function runEventTests(resourceName, displayName, events) {
|
|
|
1741
1750
|
* ```
|
|
1742
1751
|
*/
|
|
1743
1752
|
async function createTestApp(options = {}) {
|
|
1744
|
-
const { createApp } = await import("../createApp-
|
|
1753
|
+
const { createApp } = await import("../createApp-BwnEAO2h.mjs").then((n) => n.r);
|
|
1745
1754
|
const { useInMemoryDb = true, mongoUri: providedMongoUri, ...appOptions } = options;
|
|
1746
1755
|
const defaultAuth = {
|
|
1747
1756
|
type: "jwt",
|