@rpcbase/server 0.601.0 → 0.602.0

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.
Files changed (38) hide show
  1. package/dist/fileReferences-Ca0tivFd.js +101 -0
  2. package/dist/fileReferences-Ca0tivFd.js.map +1 -0
  3. package/dist/{handler-Fk-5fOSm.js → handler-BUTqP40B.js} +2 -2
  4. package/dist/{handler-Fk-5fOSm.js.map → handler-BUTqP40B.js.map} +1 -1
  5. package/dist/{handler-C92_gJkX.js → handler-BgcIWWMc.js} +2 -2
  6. package/dist/{handler-C92_gJkX.js.map → handler-BgcIWWMc.js.map} +1 -1
  7. package/dist/{handler-CapJTGzc.js → handler-CqSlSCdd.js} +215 -57
  8. package/dist/handler-CqSlSCdd.js.map +1 -0
  9. package/dist/{handler-BqlcQ9sE.js → handler-LRLmxrsg.js} +69 -24
  10. package/dist/handler-LRLmxrsg.js.map +1 -0
  11. package/dist/notifications.js +1 -1
  12. package/dist/postProcessors-iffHvje4.js.map +1 -1
  13. package/dist/rts/index.js +1 -1
  14. package/dist/{schemas-B2fUvgYo.js → schemas-POKnVn0c.js} +58 -2
  15. package/dist/schemas-POKnVn0c.js.map +1 -0
  16. package/dist/{shared-DfrVDSp0.js → shared-Zt17vuAT.js} +54 -2
  17. package/dist/shared-Zt17vuAT.js.map +1 -0
  18. package/dist/uploads/api/file-uploads/fileReferences.d.ts +42 -0
  19. package/dist/uploads/api/file-uploads/fileReferences.d.ts.map +1 -0
  20. package/dist/uploads/api/file-uploads/handlers/completeUpload.d.ts.map +1 -1
  21. package/dist/uploads/api/file-uploads/handlers/initUpload.d.ts.map +1 -1
  22. package/dist/uploads/api/file-uploads/imageVariantsPostProcessor.d.ts.map +1 -1
  23. package/dist/uploads/api/file-uploads/index.d.ts +23 -0
  24. package/dist/uploads/api/file-uploads/index.d.ts.map +1 -1
  25. package/dist/uploads/api/file-uploads/postProcessors.d.ts +2 -0
  26. package/dist/uploads/api/file-uploads/postProcessors.d.ts.map +1 -1
  27. package/dist/uploads/api/file-uploads/shared.d.ts +21 -0
  28. package/dist/uploads/api/file-uploads/shared.d.ts.map +1 -1
  29. package/dist/uploads/api/files/handlers/deleteFile.d.ts.map +1 -1
  30. package/dist/uploads/api/files/handlers/getFile.d.ts.map +1 -1
  31. package/dist/uploads/worker.js +3 -2
  32. package/dist/uploads/worker.js.map +1 -1
  33. package/dist/uploads.js +2 -2
  34. package/package.json +1 -1
  35. package/dist/handler-BqlcQ9sE.js.map +0 -1
  36. package/dist/handler-CapJTGzc.js.map +0 -1
  37. package/dist/schemas-B2fUvgYo.js.map +0 -1
  38. package/dist/shared-DfrVDSp0.js.map +0 -1
@@ -1,4 +1,5 @@
1
- import { d as getUploadKeyHash, i as getBucketName, p as getUserId, u as getTenantId } from "./shared-DfrVDSp0.js";
1
+ import { _ as getUserId, h as getUploadKeyHash, m as getTenantId, s as getBucketName, y as resolveStoredUploadAccessScope } from "./shared-Zt17vuAT.js";
2
+ import { i as findUploadFileReference, r as deleteUploadFileReference } from "./fileReferences-Ca0tivFd.js";
2
3
  import { GridFSBucket, ObjectId } from "mongodb";
3
4
  import { getTenantFilesystemDb } from "@rpcbase/db";
4
5
  //#region src/uploads/api/files/handlers/deleteFile.ts
@@ -45,7 +46,8 @@ var deleteFile = async (_payload, ctx) => {
45
46
  error: "filesystem_db_unavailable"
46
47
  };
47
48
  }
48
- const bucket = new GridFSBucket(nativeDb, { bucketName: getBucketName() });
49
+ const bucketName = getBucketName();
50
+ const bucket = new GridFSBucket(nativeDb, { bucketName });
49
51
  const userId = getUserId(ctx);
50
52
  const uploadKeyHash = getUploadKeyHash(ctx);
51
53
  if (!userId && !uploadKeyHash) {
@@ -55,6 +57,35 @@ var deleteFile = async (_payload, ctx) => {
55
57
  error: "unauthorized"
56
58
  };
57
59
  }
60
+ const reference = await findUploadFileReference(nativeDb, bucketName, fileObjectId);
61
+ if (reference) {
62
+ const referenceMetadata = reference;
63
+ const metadataUserId = resolveHeaderString$1(reference.userId);
64
+ const ownerKeyHash = resolveHeaderString$1(reference.ownerKeyHash);
65
+ const accessScope = resolveStoredUploadAccessScope(referenceMetadata);
66
+ if (!Boolean(userId && metadataUserId && userId === metadataUserId) && !Boolean(uploadKeyHash && ownerKeyHash && uploadKeyHash === ownerKeyHash) && !(accessScope === "tenant" && Boolean(userId))) {
67
+ ctx.res.status(401);
68
+ return {
69
+ ok: false,
70
+ error: "unauthorized"
71
+ };
72
+ }
73
+ try {
74
+ await deleteUploadFileReference({
75
+ tenantId,
76
+ bucketName,
77
+ referenceId: fileObjectId
78
+ });
79
+ } catch {
80
+ ctx.res.status(500);
81
+ return {
82
+ ok: false,
83
+ error: "delete_failed"
84
+ };
85
+ }
86
+ ctx.res.status(204);
87
+ return { ok: true };
88
+ }
58
89
  const [file] = await bucket.find({ _id: fileObjectId }).limit(1).toArray();
59
90
  if (!file) {
60
91
  ctx.res.status(204);
@@ -62,13 +93,23 @@ var deleteFile = async (_payload, ctx) => {
62
93
  }
63
94
  const metadataUserId = resolveHeaderString$1(file?.metadata?.userId);
64
95
  const ownerKeyHash = resolveHeaderString$1(file?.metadata?.ownerKeyHash);
65
- if (!Boolean(userId && metadataUserId && userId === metadataUserId) && !Boolean(uploadKeyHash && ownerKeyHash && uploadKeyHash === ownerKeyHash)) {
96
+ const rawMetadata = file?.metadata;
97
+ const fileMetadata = rawMetadata && typeof rawMetadata === "object" ? rawMetadata : {};
98
+ const accessScope = resolveStoredUploadAccessScope(fileMetadata);
99
+ if (!Boolean(userId && metadataUserId && userId === metadataUserId) && !Boolean(uploadKeyHash && ownerKeyHash && uploadKeyHash === ownerKeyHash) && !(accessScope === "tenant" && Boolean(userId))) {
66
100
  ctx.res.status(401);
67
101
  return {
68
102
  ok: false,
69
103
  error: "unauthorized"
70
104
  };
71
105
  }
106
+ if (fileMetadata.deduplication && typeof fileMetadata.deduplication === "object") {
107
+ ctx.res.status(409);
108
+ return {
109
+ ok: false,
110
+ error: "file_reference_required"
111
+ };
112
+ }
72
113
  try {
73
114
  const variantIds = (await bucket.find({ "metadata.variantOf": fileObjectId.toHexString() }).toArray()).map((variant) => variant?._id).filter((id) => id instanceof ObjectId);
74
115
  await deleteGridFsFile(bucket, fileObjectId);
@@ -90,15 +131,6 @@ var resolveHeaderString = (value) => {
90
131
  const normalized = value.trim();
91
132
  return normalized ? normalized : null;
92
133
  };
93
- var resolveHeaderBoolean = (value) => {
94
- if (typeof value === "boolean") return value;
95
- if (typeof value !== "string") return null;
96
- const normalized = value.trim().toLowerCase();
97
- if (!normalized) return null;
98
- if (normalized === "true") return true;
99
- if (normalized === "false") return false;
100
- return null;
101
- };
102
134
  var escapeHeaderFilename = (filename) => filename.replace(/[\\"]/g, "_");
103
135
  var parseRequestedWidth = (value) => {
104
136
  if (typeof value !== "string") return null;
@@ -168,28 +200,41 @@ var getFile = async (_payload, ctx) => {
168
200
  ctx.res.status(500).end();
169
201
  return {};
170
202
  }
171
- const bucket = new GridFSBucket(nativeDb, { bucketName: getBucketName() });
172
- const [file] = await bucket.find({ _id: fileObjectId }).limit(1).toArray();
203
+ const bucketName = getBucketName();
204
+ const bucket = new GridFSBucket(nativeDb, { bucketName });
205
+ const reference = await findUploadFileReference(nativeDb, bucketName, fileObjectId);
206
+ const blobObjectId = reference?.blobId ?? fileObjectId;
207
+ const [file] = await bucket.find({ _id: blobObjectId }).limit(1).toArray();
173
208
  if (!file) {
174
209
  ctx.res.status(404).end();
175
210
  return {};
176
211
  }
177
- if (!(resolveHeaderBoolean(file?.metadata?.isPublic) ?? false)) {
212
+ const rawMetadata = reference ?? file?.metadata;
213
+ const fileMetadata = rawMetadata && typeof rawMetadata === "object" ? rawMetadata : {};
214
+ const accessScope = resolveStoredUploadAccessScope(fileMetadata);
215
+ if (accessScope !== "public") {
178
216
  const userId = getUserId(ctx);
179
- const uploadKeyHash = getUploadKeyHash(ctx);
180
- const metadataUserId = resolveHeaderString(file?.metadata?.userId);
181
- const ownerKeyHash = resolveHeaderString(file?.metadata?.ownerKeyHash);
182
- if (!Boolean(userId && metadataUserId && userId === metadataUserId) && !Boolean(uploadKeyHash && ownerKeyHash && uploadKeyHash === ownerKeyHash)) {
183
- ctx.res.status(401).end();
184
- return {};
217
+ if (accessScope === "tenant") {
218
+ if (!userId) {
219
+ ctx.res.status(401).end();
220
+ return {};
221
+ }
222
+ } else {
223
+ const uploadKeyHash = getUploadKeyHash(ctx);
224
+ const metadataUserId = resolveHeaderString(fileMetadata.userId);
225
+ const ownerKeyHash = resolveHeaderString(fileMetadata.ownerKeyHash);
226
+ if (!Boolean(userId && metadataUserId && userId === metadataUserId) && !Boolean(uploadKeyHash && ownerKeyHash && uploadKeyHash === ownerKeyHash)) {
227
+ ctx.res.status(401).end();
228
+ return {};
229
+ }
185
230
  }
186
231
  }
187
232
  const variantFileObjectId = selectVariantFileId(file, parseRequestedWidth(ctx.req.query?.w), ctx.req.query?.variant);
188
233
  const [variantFile] = variantFileObjectId ? await bucket.find({ _id: variantFileObjectId }).limit(1).toArray() : [];
189
- const selectedFileObjectId = variantFile ? variantFileObjectId : fileObjectId;
234
+ const selectedFileObjectId = variantFile ? variantFileObjectId : blobObjectId;
190
235
  const selectedFile = variantFile ?? file;
191
236
  const mimeType = resolveHeaderString(selectedFile?.metadata?.mimeType) ?? "application/octet-stream";
192
- const filenameSafe = escapeHeaderFilename(resolveHeaderString(selectedFile?.filename) ?? fileIdRaw);
237
+ const filenameSafe = escapeHeaderFilename(resolveHeaderString(reference?.filename ?? selectedFile?.filename) ?? fileIdRaw);
193
238
  const cacheControl = "private, max-age=0, must-revalidate";
194
239
  const md5 = resolveHeaderString(selectedFile?.md5);
195
240
  const uploadDate = selectedFile?.uploadDate instanceof Date ? selectedFile.uploadDate : null;
@@ -242,4 +287,4 @@ var handler_default = (api) => {
242
287
  //#endregion
243
288
  export { handler_default as default };
244
289
 
245
- //# sourceMappingURL=handler-BqlcQ9sE.js.map
290
+ //# sourceMappingURL=handler-LRLmxrsg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler-LRLmxrsg.js","names":["ApiHandler","getTenantFilesystemDb","GridFSBucket","ObjectId","deleteUploadFileReference","findUploadFileReference","getBucketName","getTenantId","getUploadKeyHash","getUserId","resolveStoredUploadAccessScope","SessionUser","DeleteResponsePayload","ok","error","resolveHeaderString","value","normalized","trim","isFileNotFoundError","message","Error","String","includes","deleteGridFsFile","bucket","fileId","Promise","delete","deleteFile","Record","_payload","ctx","tenantId","res","status","fileIdRaw","req","params","fileObjectId","fsDb","nativeDb","db","bucketName","userId","uploadKeyHash","reference","referenceMetadata","metadataUserId","ownerKeyHash","accessScope","authorizedByUser","Boolean","authorizedByKey","authorizedByTenant","referenceId","file","find","_id","limit","toArray","metadata","rawMetadata","fileMetadata","deduplication","variants","toHexString","variantIds","map","variant","filter","id","variantId","ApiHandler","getTenantFilesystemDb","GridFSBucket","ObjectId","findUploadFileReference","getBucketName","getTenantId","getUploadKeyHash","getUserId","resolveStoredUploadAccessScope","SessionUser","resolveHeaderString","value","normalized","trim","escapeHeaderFilename","filename","replace","parseRequestedWidth","parsed","Number","isFinite","Math","floor","resolveImageVariants","file","Record","metadata","image","variants","resolveVariantFileId","variant","fileId","selectVariantFileId","requestedWidth","requestedVariant","aliasWidth","toLowerCase","variantFileId","String","targetWidth","widths","Object","keys","map","width","filter","isInteger","sort","a","b","selectedWidth","reduce","selected","selectedDistance","abs","distance","getFile","_payload","ctx","Promise","tenantId","res","status","end","fileIdRaw","req","params","fileObjectId","fsDb","nativeDb","db","bucketName","bucket","reference","blobObjectId","blobId","find","_id","limit","toArray","rawMetadata","fileMetadata","accessScope","userId","uploadKeyHash","metadataUserId","ownerKeyHash","authorizedByUser","Boolean","authorizedByKey","query","w","variantFileObjectId","variantFile","selectedFileObjectId","selectedFile","mimeTypeFromMetadata","mimeType","filenameFromDb","filenameSafe","cacheControl","md5","uploadDate","Date","etagValue","toHexString","length","getTime","etag","ifNoneMatch","headers","candidates","split","includes","setHeader","flushHeaders","method","stream","openDownloadStream","on","destroy","pipe","Route","GetRoute","DeleteRoute"],"sources":["../src/uploads/api/files/handlers/deleteFile.ts","../src/uploads/api/files/handlers/getFile.ts","../src/uploads/api/files/index.ts","../src/uploads/api/files/handler.ts"],"sourcesContent":["import { ApiHandler } from \"@rpcbase/api\"\nimport { getTenantFilesystemDb } from \"@rpcbase/db\"\nimport { GridFSBucket, ObjectId } from \"mongodb\"\n\nimport {\n deleteUploadFileReference,\n findUploadFileReference,\n} from \"../../file-uploads/fileReferences\"\nimport {\n getBucketName,\n getTenantId,\n getUploadKeyHash,\n getUserId,\n resolveStoredUploadAccessScope,\n type SessionUser,\n} from \"../../file-uploads/shared\"\n\n\ntype DeleteResponsePayload = {\n ok: boolean\n error?: string\n}\n\nconst resolveHeaderString = (value: unknown): string | null => {\n if (typeof value !== \"string\") return null\n const normalized = value.trim()\n return normalized ? normalized : null\n}\n\nconst isFileNotFoundError = (error: unknown): boolean => {\n const message = error instanceof Error ? error.message : String(error)\n return message.includes(\"FileNotFound\")\n}\n\nconst deleteGridFsFile = async (bucket: GridFSBucket, fileId: ObjectId): Promise<void> => {\n try {\n await bucket.delete(fileId)\n } catch (error) {\n if (!isFileNotFoundError(error)) throw error\n }\n}\n\nexport const deleteFile: ApiHandler<Record<string, never>, DeleteResponsePayload, SessionUser> = async (\n _payload,\n ctx,\n): Promise<DeleteResponsePayload> => {\n const tenantId = getTenantId(ctx)\n if (!tenantId) {\n ctx.res.status(400)\n return { ok: false, error: \"tenant_missing\" }\n }\n\n const fileIdRaw = String(ctx.req.params?.fileId ?? \"\").trim()\n let fileObjectId: ObjectId\n try {\n fileObjectId = new ObjectId(fileIdRaw)\n } catch {\n ctx.res.status(400)\n return { ok: false, error: \"invalid_file_id\" }\n }\n\n const fsDb = await getTenantFilesystemDb(tenantId)\n const nativeDb = fsDb.db\n if (!nativeDb) {\n ctx.res.status(500)\n return { ok: false, error: \"filesystem_db_unavailable\" }\n }\n\n const bucketName = getBucketName()\n const bucket = new GridFSBucket(nativeDb, { bucketName })\n\n const userId = getUserId(ctx)\n const uploadKeyHash = getUploadKeyHash(ctx)\n if (!userId && !uploadKeyHash) {\n ctx.res.status(401)\n return { ok: false, error: \"unauthorized\" }\n }\n\n const reference = await findUploadFileReference(nativeDb, bucketName, fileObjectId)\n if (reference) {\n const referenceMetadata = reference as unknown as Record<string, unknown>\n const metadataUserId = resolveHeaderString(reference.userId)\n const ownerKeyHash = resolveHeaderString(reference.ownerKeyHash)\n const accessScope = resolveStoredUploadAccessScope(referenceMetadata)\n\n const authorizedByUser = Boolean(userId && metadataUserId && userId === metadataUserId)\n const authorizedByKey = Boolean(uploadKeyHash && ownerKeyHash && uploadKeyHash === ownerKeyHash)\n const authorizedByTenant = accessScope === \"tenant\" && Boolean(userId)\n\n if (!authorizedByUser && !authorizedByKey && !authorizedByTenant) {\n ctx.res.status(401)\n return { ok: false, error: \"unauthorized\" }\n }\n\n try {\n await deleteUploadFileReference({\n tenantId,\n bucketName,\n referenceId: fileObjectId,\n })\n } catch {\n ctx.res.status(500)\n return { ok: false, error: \"delete_failed\" }\n }\n\n ctx.res.status(204)\n return { ok: true }\n }\n\n const [file] = await bucket.find({ _id: fileObjectId }).limit(1).toArray()\n if (!file) {\n ctx.res.status(204)\n return { ok: true }\n }\n\n const metadataUserId = resolveHeaderString((file as any)?.metadata?.userId)\n const ownerKeyHash = resolveHeaderString((file as any)?.metadata?.ownerKeyHash)\n const rawMetadata = (file as any)?.metadata\n const fileMetadata = rawMetadata && typeof rawMetadata === \"object\"\n ? rawMetadata as Record<string, unknown>\n : {}\n const accessScope = resolveStoredUploadAccessScope(fileMetadata)\n\n const authorizedByUser = Boolean(userId && metadataUserId && userId === metadataUserId)\n const authorizedByKey = Boolean(uploadKeyHash && ownerKeyHash && uploadKeyHash === ownerKeyHash)\n const authorizedByTenant = accessScope === \"tenant\" && Boolean(userId)\n\n if (!authorizedByUser && !authorizedByKey && !authorizedByTenant) {\n ctx.res.status(401)\n return { ok: false, error: \"unauthorized\" }\n }\n\n if (fileMetadata.deduplication && typeof fileMetadata.deduplication === \"object\") {\n ctx.res.status(409)\n return { ok: false, error: \"file_reference_required\" }\n }\n\n try {\n const variants = await bucket.find({ \"metadata.variantOf\": fileObjectId.toHexString() }).toArray()\n const variantIds = variants\n .map((variant) => (variant as any)?._id)\n .filter((id): id is ObjectId => id instanceof ObjectId)\n\n await deleteGridFsFile(bucket, fileObjectId)\n for (const variantId of variantIds) {\n await deleteGridFsFile(bucket, variantId)\n }\n } catch {\n ctx.res.status(500)\n return { ok: false, error: \"delete_failed\" }\n }\n\n ctx.res.status(204)\n return { ok: true }\n}\n","import { ApiHandler } from \"@rpcbase/api\"\nimport { getTenantFilesystemDb } from \"@rpcbase/db\"\nimport { GridFSBucket, ObjectId } from \"mongodb\"\n\nimport { findUploadFileReference } from \"../../file-uploads/fileReferences\"\nimport {\n getBucketName,\n getTenantId,\n getUploadKeyHash,\n getUserId,\n resolveStoredUploadAccessScope,\n type SessionUser,\n} from \"../../file-uploads/shared\"\n\n\nconst resolveHeaderString = (value: unknown): string | null => {\n if (typeof value !== \"string\") return null\n const normalized = value.trim()\n return normalized ? normalized : null\n}\n\nconst escapeHeaderFilename = (filename: string): string => filename.replace(/[\\\\\"]/g, \"_\")\n\nconst parseRequestedWidth = (value: unknown): number | null => {\n if (typeof value !== \"string\") return null\n const parsed = Number(value.trim())\n if (!Number.isFinite(parsed) || parsed <= 0) return null\n return Math.floor(parsed)\n}\n\nconst resolveImageVariants = (file: unknown): Record<string, unknown> | null => {\n const metadata = (file as any)?.metadata\n if (!metadata || typeof metadata !== \"object\") return null\n const image = (metadata as Record<string, unknown>).image\n if (!image || typeof image !== \"object\") return null\n const variants = (image as Record<string, unknown>).variants\n if (!variants || typeof variants !== \"object\") return null\n return variants as Record<string, unknown>\n}\n\nconst resolveVariantFileId = (variant: unknown): ObjectId | null => {\n if (!variant || typeof variant !== \"object\") return null\n const fileId = (variant as Record<string, unknown>).fileId\n if (typeof fileId !== \"string\") return null\n try {\n return new ObjectId(fileId)\n } catch {\n return null\n }\n}\n\nconst selectVariantFileId = (file: unknown, requestedWidth: number | null, requestedVariant: unknown): ObjectId | null => {\n const variants = resolveImageVariants(file)\n if (!variants) return null\n\n let aliasWidth: number | null = null\n if (typeof requestedVariant === \"string\") {\n const normalized = requestedVariant.trim().toLowerCase()\n aliasWidth = normalized === \"thumb\" ? 320 : normalized === \"small\" ? 640 : normalized === \"medium\" ? 960 : normalized === \"large\" ? 1600 : null\n if (aliasWidth) {\n const variantFileId = resolveVariantFileId(variants[String(aliasWidth)])\n if (variantFileId) return variantFileId\n }\n }\n\n const targetWidth = aliasWidth ?? requestedWidth\n if (!targetWidth) return null\n\n const widths = Object.keys(variants)\n .map((width) => Number(width))\n .filter((width) => Number.isInteger(width) && width > 0)\n .sort((a, b) => a - b)\n\n const selectedWidth = widths.reduce<number | null>((selected, width) => {\n if (selected === null) return width\n\n const selectedDistance = Math.abs(selected - targetWidth)\n const distance = Math.abs(width - targetWidth)\n if (distance < selectedDistance) return width\n if (distance === selectedDistance && width > selected) return width\n return selected\n }, null)\n return selectedWidth ? resolveVariantFileId(variants[String(selectedWidth)]) : null\n}\n\nexport const getFile: ApiHandler<Record<string, never>, Record<string, never>, SessionUser> = async (\n _payload,\n ctx,\n): Promise<Record<string, never>> => {\n const tenantId = getTenantId(ctx)\n if (!tenantId) {\n ctx.res.status(400).end()\n return {}\n }\n\n const fileIdRaw = String(ctx.req.params?.fileId ?? \"\").trim()\n let fileObjectId: ObjectId\n try {\n fileObjectId = new ObjectId(fileIdRaw)\n } catch {\n ctx.res.status(400).end()\n return {}\n }\n\n const fsDb = await getTenantFilesystemDb(tenantId)\n const nativeDb = fsDb.db\n if (!nativeDb) {\n ctx.res.status(500).end()\n return {}\n }\n\n const bucketName = getBucketName()\n const bucket = new GridFSBucket(nativeDb, { bucketName })\n const reference = await findUploadFileReference(nativeDb, bucketName, fileObjectId)\n const blobObjectId = reference?.blobId ?? fileObjectId\n\n const [file] = await bucket.find({ _id: blobObjectId }).limit(1).toArray()\n if (!file) {\n ctx.res.status(404).end()\n return {}\n }\n\n const rawMetadata = reference ?? (file as any)?.metadata\n const fileMetadata = rawMetadata && typeof rawMetadata === \"object\"\n ? rawMetadata as Record<string, unknown>\n : {}\n const accessScope = resolveStoredUploadAccessScope(fileMetadata)\n if (accessScope !== \"public\") {\n const userId = getUserId(ctx)\n if (accessScope === \"tenant\") {\n if (!userId) {\n ctx.res.status(401).end()\n return {}\n }\n } else {\n const uploadKeyHash = getUploadKeyHash(ctx)\n const metadataUserId = resolveHeaderString(fileMetadata.userId)\n const ownerKeyHash = resolveHeaderString(fileMetadata.ownerKeyHash)\n\n const authorizedByUser = Boolean(userId && metadataUserId && userId === metadataUserId)\n const authorizedByKey = Boolean(uploadKeyHash && ownerKeyHash && uploadKeyHash === ownerKeyHash)\n\n if (!authorizedByUser && !authorizedByKey) {\n ctx.res.status(401).end()\n return {}\n }\n }\n }\n\n const requestedWidth = parseRequestedWidth((ctx.req.query as any)?.w)\n const variantFileObjectId = selectVariantFileId(file, requestedWidth, (ctx.req.query as any)?.variant)\n const [variantFile] = variantFileObjectId\n ? await bucket.find({ _id: variantFileObjectId }).limit(1).toArray()\n : []\n const selectedFileObjectId = variantFile ? variantFileObjectId as ObjectId : blobObjectId\n const selectedFile = variantFile ?? file\n\n const mimeTypeFromMetadata = resolveHeaderString((selectedFile as any)?.metadata?.mimeType)\n const mimeType = mimeTypeFromMetadata ?? \"application/octet-stream\"\n const filenameFromDb = resolveHeaderString(reference?.filename ?? (selectedFile as any)?.filename)\n const filename = filenameFromDb ?? fileIdRaw\n const filenameSafe = escapeHeaderFilename(filename)\n\n const cacheControl = \"private, max-age=0, must-revalidate\"\n const md5 = resolveHeaderString((selectedFile as any)?.md5)\n const uploadDate = (selectedFile as any)?.uploadDate instanceof Date ? (selectedFile as any).uploadDate : null\n const etagValue = md5 ?? `${selectedFileObjectId.toHexString()}-${String((selectedFile as any)?.length ?? 0)}-${String(uploadDate?.getTime() ?? 0)}`\n const etag = md5 ? `\"${etagValue}\"` : `W/\"${etagValue}\"`\n const ifNoneMatch = resolveHeaderString((ctx.req.headers as any)?.[\"if-none-match\"])\n if (ifNoneMatch) {\n const candidates = ifNoneMatch.split(\",\").map((value) => value.trim()).filter(Boolean)\n if (candidates.includes(\"*\") || candidates.includes(etag)) {\n ctx.res.status(304)\n ctx.res.setHeader(\"Cache-Control\", cacheControl)\n ctx.res.setHeader(\"ETag\", etag)\n ctx.res.end()\n return {}\n }\n }\n\n ctx.res.status(200)\n ctx.res.setHeader(\"Content-Type\", mimeType)\n ctx.res.setHeader(\"Content-Length\", String((selectedFile as any)?.length ?? 0))\n ctx.res.setHeader(\"Content-Disposition\", `inline; filename=\"${filenameSafe}\"`)\n ctx.res.setHeader(\"Cache-Control\", cacheControl)\n ctx.res.setHeader(\"ETag\", etag)\n ctx.res.setHeader(\"X-Content-Type-Options\", \"nosniff\")\n if (mimeType === \"image/svg+xml\") {\n ctx.res.setHeader(\n \"Content-Security-Policy\",\n \"default-src 'none'; style-src 'unsafe-inline'; sandbox; base-uri 'none'; form-action 'none'\",\n )\n }\n ctx.res.flushHeaders()\n\n if (ctx.req.method === \"HEAD\") {\n ctx.res.end()\n return {}\n }\n\n const stream = bucket.openDownloadStream(selectedFileObjectId)\n\n stream.on(\"error\", () => {\n try {\n ctx.res.destroy()\n } catch {\n //\n }\n })\n\n stream.pipe(ctx.res)\n return {}\n}\n","export const Route = \"/api/rb/files/:fileId\"\n\nexport const GetRoute = Route\nexport const DeleteRoute = Route\n","import { Api } from \"@rpcbase/api\"\n\nimport { deleteFile } from \"./handlers/deleteFile\"\nimport { getFile } from \"./handlers/getFile\"\n\nimport * as Files from \"./index\"\n\n\nexport default (api: Api) => {\n api.get(Files.GetRoute, getFile)\n api.delete(Files.DeleteRoute, deleteFile)\n}\n"],"mappings":"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"}
@@ -2,7 +2,7 @@ import { i as sendEmail } from "./email-CK8NNDQY.js";
2
2
  import { models } from "@rpcbase/db";
3
3
  import { randomUUID } from "node:crypto";
4
4
  //#region src/notifications/routes.ts
5
- var routes = Object.entries({ .../* #__PURE__ */ Object.assign({ "./api/notifications/handler.ts": () => import("./handler-C92_gJkX.js") }) }).reduce((acc, [path, mod]) => {
5
+ var routes = Object.entries({ .../* #__PURE__ */ Object.assign({ "./api/notifications/handler.ts": () => import("./handler-BgcIWWMc.js") }) }).reduce((acc, [path, mod]) => {
6
6
  acc[path.replace("./api/", "@rpcbase/server/notifications/api/")] = mod;
7
7
  return acc;
8
8
  }, {});
@@ -1 +1 @@
1
- {"version":3,"file":"postProcessors-iffHvje4.js","names":["queue","UploadPostProcessorContext","tenantId","uploadId","fileId","filename","mimeType","clientMimeType","totalSize","isPublic","userId","ownerKeyHash","inlineProcessors","metadata","Record","UploadPostProcessor","id","version","match","ctx","process","Promise","processorsById","Object","create","uploadPostProcessorTaskName","normalizeProcessorId","value","trim","normalizeProcessorVersion","undefined","Number","isInteger","registerUploadPostProcessor","processor","normalizedId","Error","normalizedVersion","registerUploadProcessor","unregisterUploadPostProcessor","unregisterUploadProcessor","clearUploadPostProcessors","keys","clearUploadProcessors","getUploadPostProcessors","values","getUploadProcessors","runUploadPostProcessors","processors","length","error","console","processorId","processorVersion","stage","isUploadPostProcessorTaskRegistered","registerUploadPostProcessorTask","registerTask","payload","enqueueUploadPostProcessors","add","attempts","backoff","type","delay","removeOnComplete","removeOnFail"],"sources":["../src/uploads/api/file-uploads/postProcessors.ts"],"sourcesContent":["import { queue } from \"@rpcbase/worker\"\n\n\nexport type UploadPostProcessorContext = {\n tenantId: string\n uploadId: string\n fileId: string\n filename: string\n mimeType: string\n clientMimeType: string\n totalSize: number\n isPublic?: boolean\n userId?: string\n ownerKeyHash?: string\n inlineProcessors: string[]\n metadata: Record<string, unknown>\n}\n\nexport type UploadPostProcessor = {\n id: string\n version?: number\n match?: (ctx: UploadPostProcessorContext) => boolean\n process: (ctx: UploadPostProcessorContext) => Promise<unknown> | unknown\n}\n\nconst processorsById: Record<string, UploadPostProcessor> = Object.create(null)\nexport const uploadPostProcessorTaskName = \"rb-upload-post-processors\"\n\nconst normalizeProcessorId = (value: unknown): string => (typeof value === \"string\" ? value.trim() : \"\")\n\nconst normalizeProcessorVersion = (value: unknown): number | undefined => {\n if (typeof value !== \"number\") return undefined\n if (!Number.isInteger(value) || value < 1) return undefined\n return value\n}\n\nexport const registerUploadPostProcessor = (processor: UploadPostProcessor): void => {\n const normalizedId = normalizeProcessorId(processor.id)\n if (!normalizedId) {\n throw new Error(\"Upload post processor id is required.\")\n }\n\n const normalizedVersion = normalizeProcessorVersion(processor.version)\n\n processorsById[normalizedId] = {\n ...processor,\n id: normalizedId,\n ...(normalizedVersion ? { version: normalizedVersion } : {}),\n }\n}\n\nexport const registerUploadProcessor = registerUploadPostProcessor\n\nexport const unregisterUploadPostProcessor = (id: string): void => {\n const normalizedId = normalizeProcessorId(id)\n if (!normalizedId) return\n delete processorsById[normalizedId]\n}\n\nexport const unregisterUploadProcessor = unregisterUploadPostProcessor\n\nexport const clearUploadPostProcessors = (): void => {\n for (const id of Object.keys(processorsById)) {\n delete processorsById[id]\n }\n}\n\nexport const clearUploadProcessors = clearUploadPostProcessors\n\nexport const getUploadPostProcessors = (): UploadPostProcessor[] => Object.values(processorsById)\n\nexport const getUploadProcessors = getUploadPostProcessors\n\nexport const runUploadPostProcessors = async (ctx: UploadPostProcessorContext): Promise<void> => {\n const processors = getUploadPostProcessors()\n if (!processors.length) return\n\n for (const processor of processors) {\n if (processor.match) {\n try {\n if (!processor.match(ctx)) continue\n } catch (error) {\n console.error(\"Upload post processor failed\", {\n processorId: processor.id,\n processorVersion: processor.version,\n tenantId: ctx.tenantId,\n uploadId: ctx.uploadId,\n fileId: ctx.fileId,\n stage: \"match\",\n error,\n })\n continue\n }\n }\n\n try {\n await processor.process(ctx)\n } catch (error) {\n console.error(\"Upload post processor failed\", {\n processorId: processor.id,\n processorVersion: processor.version,\n tenantId: ctx.tenantId,\n uploadId: ctx.uploadId,\n fileId: ctx.fileId,\n stage: \"process\",\n error,\n })\n }\n }\n}\n\nlet isUploadPostProcessorTaskRegistered = false\n\nexport const registerUploadPostProcessorTask = (): void => {\n if (isUploadPostProcessorTaskRegistered) return\n\n queue.registerTask(uploadPostProcessorTaskName, async (payload) => {\n await runUploadPostProcessors(payload as UploadPostProcessorContext)\n })\n isUploadPostProcessorTaskRegistered = true\n}\n\nexport const enqueueUploadPostProcessors = async (ctx: UploadPostProcessorContext): Promise<void> => {\n await queue.add(uploadPostProcessorTaskName, ctx, {\n attempts: 3,\n backoff: { type: \"exponential\", delay: 5_000 },\n removeOnComplete: true,\n removeOnFail: false,\n })\n}\n"],"mappings":";;AAyBA,IAAMsB,iBAAsDC,OAAOC,OAAO,IAAI;AAC9E,IAAaC,8BAA8B;AAE3C,IAAMC,wBAAwBC,UAA4B,OAAOA,UAAU,WAAWA,MAAMC,KAAK,IAAI;AAErG,IAAMC,6BAA6BF,UAAuC;CACxE,IAAI,OAAOA,UAAU,UAAU,OAAOG,KAAAA;CACtC,IAAI,CAACC,OAAOC,UAAUL,KAAK,KAAKA,QAAQ,GAAG,OAAOG,KAAAA;CAClD,OAAOH;AACT;AAEA,IAAaM,+BAA+BC,cAAyC;CACnF,MAAMC,eAAeT,qBAAqBQ,UAAUlB,EAAE;CACtD,IAAI,CAACmB,cACH,MAAM,IAAIC,MAAM,uCAAuC;CAGzD,MAAMC,oBAAoBR,0BAA0BK,UAAUjB,OAAO;CAErEK,eAAea,gBAAgB;EAC7B,GAAGD;EACHlB,IAAImB;EACJ,GAAIE,oBAAoB,EAAEpB,SAASoB,kBAAkB,IAAI,CAAC;CAC5D;AACF;AAEA,IAAaC,0BAA0BL;AAEvC,IAAaM,iCAAiCvB,OAAqB;CACjE,MAAMmB,eAAeT,qBAAqBV,EAAE;CAC5C,IAAI,CAACmB,cAAc;CACnB,OAAOb,eAAea;AACxB;AAEA,IAAaK,4BAA4BD;AAEzC,IAAaE,kCAAwC;CACnD,KAAK,MAAMzB,MAAMO,OAAOmB,KAAKpB,cAAc,GACzC,OAAOA,eAAeN;AAE1B;AAEA,IAAa2B,wBAAwBF;AAErC,IAAaG,gCAAuDrB,OAAOsB,OAAOvB,cAAc;AAEhG,IAAawB,sBAAsBF;AAEnC,IAAaG,0BAA0B,OAAO5B,QAAmD;CAC/F,MAAM6B,aAAaJ,wBAAwB;CAC3C,IAAI,CAACI,WAAWC,QAAQ;CAExB,KAAK,MAAMf,aAAac,YAAY;EAClC,IAAId,UAAUhB,OACZ,IAAI;GACF,IAAI,CAACgB,UAAUhB,MAAMC,GAAG,GAAG;EAC7B,SAAS+B,OAAO;GACdC,QAAQD,MAAM,gCAAgC;IAC5CE,aAAalB,UAAUlB;IACvBqC,kBAAkBnB,UAAUjB;IAC5Bf,UAAUiB,IAAIjB;IACdC,UAAUgB,IAAIhB;IACdC,QAAQe,IAAIf;IACZkD,OAAO;IACPJ;GACF,CAAC;GACD;EACF;EAGF,IAAI;GACF,MAAMhB,UAAUd,QAAQD,GAAG;EAC7B,SAAS+B,OAAO;GACdC,QAAQD,MAAM,gCAAgC;IAC5CE,aAAalB,UAAUlB;IACvBqC,kBAAkBnB,UAAUjB;IAC5Bf,UAAUiB,IAAIjB;IACdC,UAAUgB,IAAIhB;IACdC,QAAQe,IAAIf;IACZkD,OAAO;IACPJ;GACF,CAAC;EACH;CACF;AACF;AAEA,IAAIK,sCAAsC;AAE1C,IAAaC,wCAA8C;CACzD,IAAID,qCAAqC;CAEzCvD,MAAMyD,aAAahC,6BAA6B,OAAOiC,YAAY;EACjE,MAAMX,wBAAwBW,OAAqC;CACrE,CAAC;CACDH,sCAAsC;AACxC;AAEA,IAAaI,8BAA8B,OAAOxC,QAAmD;CACnG,MAAMnB,MAAM4D,IAAInC,6BAA6BN,KAAK;EAChD0C,UAAU;EACVC,SAAS;GAAEC,MAAM;GAAeC,OAAO;EAAM;EAC7CC,kBAAkB;EAClBC,cAAc;CAChB,CAAC;AACH"}
1
+ {"version":3,"file":"postProcessors-iffHvje4.js","names":["queue","UploadAccessScope","UploadPostProcessorContext","tenantId","uploadId","fileId","filename","mimeType","clientMimeType","totalSize","isPublic","accessScope","userId","ownerKeyHash","inlineProcessors","metadata","Record","UploadPostProcessor","id","version","match","ctx","process","Promise","processorsById","Object","create","uploadPostProcessorTaskName","normalizeProcessorId","value","trim","normalizeProcessorVersion","undefined","Number","isInteger","registerUploadPostProcessor","processor","normalizedId","Error","normalizedVersion","registerUploadProcessor","unregisterUploadPostProcessor","unregisterUploadProcessor","clearUploadPostProcessors","keys","clearUploadProcessors","getUploadPostProcessors","values","getUploadProcessors","runUploadPostProcessors","processors","length","error","console","processorId","processorVersion","stage","isUploadPostProcessorTaskRegistered","registerUploadPostProcessorTask","registerTask","payload","enqueueUploadPostProcessors","add","attempts","backoff","type","delay","removeOnComplete","removeOnFail"],"sources":["../src/uploads/api/file-uploads/postProcessors.ts"],"sourcesContent":["import { queue } from \"@rpcbase/worker\"\n\nimport type { UploadAccessScope } from \"./shared\"\n\n\nexport type UploadPostProcessorContext = {\n tenantId: string\n uploadId: string\n fileId: string\n filename: string\n mimeType: string\n clientMimeType: string\n totalSize: number\n isPublic?: boolean\n accessScope?: UploadAccessScope\n userId?: string\n ownerKeyHash?: string\n inlineProcessors: string[]\n metadata: Record<string, unknown>\n}\n\nexport type UploadPostProcessor = {\n id: string\n version?: number\n match?: (ctx: UploadPostProcessorContext) => boolean\n process: (ctx: UploadPostProcessorContext) => Promise<unknown> | unknown\n}\n\nconst processorsById: Record<string, UploadPostProcessor> = Object.create(null)\nexport const uploadPostProcessorTaskName = \"rb-upload-post-processors\"\n\nconst normalizeProcessorId = (value: unknown): string => (typeof value === \"string\" ? value.trim() : \"\")\n\nconst normalizeProcessorVersion = (value: unknown): number | undefined => {\n if (typeof value !== \"number\") return undefined\n if (!Number.isInteger(value) || value < 1) return undefined\n return value\n}\n\nexport const registerUploadPostProcessor = (processor: UploadPostProcessor): void => {\n const normalizedId = normalizeProcessorId(processor.id)\n if (!normalizedId) {\n throw new Error(\"Upload post processor id is required.\")\n }\n\n const normalizedVersion = normalizeProcessorVersion(processor.version)\n\n processorsById[normalizedId] = {\n ...processor,\n id: normalizedId,\n ...(normalizedVersion ? { version: normalizedVersion } : {}),\n }\n}\n\nexport const registerUploadProcessor = registerUploadPostProcessor\n\nexport const unregisterUploadPostProcessor = (id: string): void => {\n const normalizedId = normalizeProcessorId(id)\n if (!normalizedId) return\n delete processorsById[normalizedId]\n}\n\nexport const unregisterUploadProcessor = unregisterUploadPostProcessor\n\nexport const clearUploadPostProcessors = (): void => {\n for (const id of Object.keys(processorsById)) {\n delete processorsById[id]\n }\n}\n\nexport const clearUploadProcessors = clearUploadPostProcessors\n\nexport const getUploadPostProcessors = (): UploadPostProcessor[] => Object.values(processorsById)\n\nexport const getUploadProcessors = getUploadPostProcessors\n\nexport const runUploadPostProcessors = async (ctx: UploadPostProcessorContext): Promise<void> => {\n const processors = getUploadPostProcessors()\n if (!processors.length) return\n\n for (const processor of processors) {\n if (processor.match) {\n try {\n if (!processor.match(ctx)) continue\n } catch (error) {\n console.error(\"Upload post processor failed\", {\n processorId: processor.id,\n processorVersion: processor.version,\n tenantId: ctx.tenantId,\n uploadId: ctx.uploadId,\n fileId: ctx.fileId,\n stage: \"match\",\n error,\n })\n continue\n }\n }\n\n try {\n await processor.process(ctx)\n } catch (error) {\n console.error(\"Upload post processor failed\", {\n processorId: processor.id,\n processorVersion: processor.version,\n tenantId: ctx.tenantId,\n uploadId: ctx.uploadId,\n fileId: ctx.fileId,\n stage: \"process\",\n error,\n })\n }\n }\n}\n\nlet isUploadPostProcessorTaskRegistered = false\n\nexport const registerUploadPostProcessorTask = (): void => {\n if (isUploadPostProcessorTaskRegistered) return\n\n queue.registerTask(uploadPostProcessorTaskName, async (payload) => {\n await runUploadPostProcessors(payload as UploadPostProcessorContext)\n })\n isUploadPostProcessorTaskRegistered = true\n}\n\nexport const enqueueUploadPostProcessors = async (ctx: UploadPostProcessorContext): Promise<void> => {\n await queue.add(uploadPostProcessorTaskName, ctx, {\n attempts: 3,\n backoff: { type: \"exponential\", delay: 5_000 },\n removeOnComplete: true,\n removeOnFail: false,\n })\n}\n"],"mappings":";;AA4BA,IAAMwB,iBAAsDC,OAAOC,OAAO,IAAI;AAC9E,IAAaC,8BAA8B;AAE3C,IAAMC,wBAAwBC,UAA4B,OAAOA,UAAU,WAAWA,MAAMC,KAAK,IAAI;AAErG,IAAMC,6BAA6BF,UAAuC;CACxE,IAAI,OAAOA,UAAU,UAAU,OAAOG,KAAAA;CACtC,IAAI,CAACC,OAAOC,UAAUL,KAAK,KAAKA,QAAQ,GAAG,OAAOG,KAAAA;CAClD,OAAOH;AACT;AAEA,IAAaM,+BAA+BC,cAAyC;CACnF,MAAMC,eAAeT,qBAAqBQ,UAAUlB,EAAE;CACtD,IAAI,CAACmB,cACH,MAAM,IAAIC,MAAM,uCAAuC;CAGzD,MAAMC,oBAAoBR,0BAA0BK,UAAUjB,OAAO;CAErEK,eAAea,gBAAgB;EAC7B,GAAGD;EACHlB,IAAImB;EACJ,GAAIE,oBAAoB,EAAEpB,SAASoB,kBAAkB,IAAI,CAAC;CAC5D;AACF;AAEA,IAAaC,0BAA0BL;AAEvC,IAAaM,iCAAiCvB,OAAqB;CACjE,MAAMmB,eAAeT,qBAAqBV,EAAE;CAC5C,IAAI,CAACmB,cAAc;CACnB,OAAOb,eAAea;AACxB;AAEA,IAAaK,4BAA4BD;AAEzC,IAAaE,kCAAwC;CACnD,KAAK,MAAMzB,MAAMO,OAAOmB,KAAKpB,cAAc,GACzC,OAAOA,eAAeN;AAE1B;AAEA,IAAa2B,wBAAwBF;AAErC,IAAaG,gCAAuDrB,OAAOsB,OAAOvB,cAAc;AAEhG,IAAawB,sBAAsBF;AAEnC,IAAaG,0BAA0B,OAAO5B,QAAmD;CAC/F,MAAM6B,aAAaJ,wBAAwB;CAC3C,IAAI,CAACI,WAAWC,QAAQ;CAExB,KAAK,MAAMf,aAAac,YAAY;EAClC,IAAId,UAAUhB,OACZ,IAAI;GACF,IAAI,CAACgB,UAAUhB,MAAMC,GAAG,GAAG;EAC7B,SAAS+B,OAAO;GACdC,QAAQD,MAAM,gCAAgC;IAC5CE,aAAalB,UAAUlB;IACvBqC,kBAAkBnB,UAAUjB;IAC5BhB,UAAUkB,IAAIlB;IACdC,UAAUiB,IAAIjB;IACdC,QAAQgB,IAAIhB;IACZmD,OAAO;IACPJ;GACF,CAAC;GACD;EACF;EAGF,IAAI;GACF,MAAMhB,UAAUd,QAAQD,GAAG;EAC7B,SAAS+B,OAAO;GACdC,QAAQD,MAAM,gCAAgC;IAC5CE,aAAalB,UAAUlB;IACvBqC,kBAAkBnB,UAAUjB;IAC5BhB,UAAUkB,IAAIlB;IACdC,UAAUiB,IAAIjB;IACdC,QAAQgB,IAAIhB;IACZmD,OAAO;IACPJ;GACF,CAAC;EACH;CACF;AACF;AAEA,IAAIK,sCAAsC;AAE1C,IAAaC,wCAA8C;CACzD,IAAID,qCAAqC;CAEzCzD,MAAM2D,aAAahC,6BAA6B,OAAOiC,YAAY;EACjE,MAAMX,wBAAwBW,OAAqC;CACrE,CAAC;CACDH,sCAAsC;AACxC;AAEA,IAAaI,8BAA8B,OAAOxC,QAAmD;CACnG,MAAMrB,MAAM8D,IAAInC,6BAA6BN,KAAK;EAChD0C,UAAU;EACVC,SAAS;GAAEC,MAAM;GAAeC,OAAO;EAAM;EAC7CC,kBAAkB;EAClBC,cAAc;CAChB,CAAC;AACH"}
package/dist/rts/index.js CHANGED
@@ -4,7 +4,7 @@ import { randomUUID } from "node:crypto";
4
4
  import { buildAbilityFromSession } from "@rpcbase/db/acl";
5
5
  import { WebSocketServer } from "ws";
6
6
  //#region src/rts/routes.ts
7
- var routes = Object.entries({ .../* #__PURE__ */ Object.assign({ "./api/changes/handler.ts": () => import("../handler-Fk-5fOSm.js") }) }).reduce((acc, [path, mod]) => {
7
+ var routes = Object.entries({ .../* #__PURE__ */ Object.assign({ "./api/changes/handler.ts": () => import("../handler-BUTqP40B.js") }) }).reduce((acc, [path, mod]) => {
8
8
  acc[path.replace("./api/", "@rpcbase/server/rts/api/")] = mod;
9
9
  return acc;
10
10
  }, {});
@@ -1989,6 +1989,24 @@ var $ZodEnum = /*@__PURE__*/ $constructor("$ZodEnum", (inst, def) => {
1989
1989
  return payload;
1990
1990
  };
1991
1991
  });
1992
+ var $ZodLiteral = /*@__PURE__*/ $constructor("$ZodLiteral", (inst, def) => {
1993
+ $ZodType.init(inst, def);
1994
+ if (def.values.length === 0) throw new Error("Cannot create literal schema with no valid values");
1995
+ const values = new Set(def.values);
1996
+ inst._zod.values = values;
1997
+ inst._zod.pattern = new RegExp(`^(${def.values.map((o) => typeof o === "string" ? escapeRegex(o) : o ? escapeRegex(o.toString()) : String(o)).join("|")})$`);
1998
+ inst._zod.parse = (payload, _ctx) => {
1999
+ const input = payload.value;
2000
+ if (values.has(input)) return payload;
2001
+ payload.issues.push({
2002
+ code: "invalid_value",
2003
+ values: def.values,
2004
+ input,
2005
+ inst
2006
+ });
2007
+ return payload;
2008
+ };
2009
+ });
1992
2010
  var $ZodTransform = /*@__PURE__*/ $constructor("$ZodTransform", (inst, def) => {
1993
2011
  $ZodType.init(inst, def);
1994
2012
  inst._zod.optin = "optional";
@@ -3125,6 +3143,27 @@ var enumProcessor = (schema, _ctx, json, _params) => {
3125
3143
  if (values.every((v) => typeof v === "string")) json.type = "string";
3126
3144
  json.enum = values;
3127
3145
  };
3146
+ var literalProcessor = (schema, ctx, json, _params) => {
3147
+ const def = schema._zod.def;
3148
+ const vals = [];
3149
+ for (const val of def.values) if (val === void 0) {
3150
+ if (ctx.unrepresentable === "throw") throw new Error("Literal `undefined` cannot be represented in JSON Schema");
3151
+ } else if (typeof val === "bigint") if (ctx.unrepresentable === "throw") throw new Error("BigInt literals cannot be represented in JSON Schema");
3152
+ else vals.push(Number(val));
3153
+ else vals.push(val);
3154
+ if (vals.length === 0) {} else if (vals.length === 1) {
3155
+ const val = vals[0];
3156
+ json.type = val === null ? "null" : typeof val;
3157
+ if (ctx.target === "draft-04" || ctx.target === "openapi-3.0") json.enum = [val];
3158
+ else json.const = val;
3159
+ } else {
3160
+ if (vals.every((v) => typeof v === "number")) json.type = "number";
3161
+ if (vals.every((v) => typeof v === "string")) json.type = "string";
3162
+ if (vals.every((v) => typeof v === "boolean")) json.type = "boolean";
3163
+ if (vals.every((v) => v === null)) json.type = "null";
3164
+ json.enum = vals;
3165
+ }
3166
+ };
3128
3167
  var customProcessor = (_schema, ctx, _json, _params) => {
3129
3168
  if (ctx.unrepresentable === "throw") throw new Error("Custom types cannot be represented in JSON Schema");
3130
3169
  };
@@ -3980,6 +4019,23 @@ function _enum(values, params) {
3980
4019
  ...normalizeParams(params)
3981
4020
  });
3982
4021
  }
4022
+ var ZodLiteral = /*@__PURE__*/ $constructor("ZodLiteral", (inst, def) => {
4023
+ $ZodLiteral.init(inst, def);
4024
+ ZodType.init(inst, def);
4025
+ inst._zod.processJSONSchema = (ctx, json, params) => literalProcessor(inst, ctx, json, params);
4026
+ inst.values = new Set(def.values);
4027
+ Object.defineProperty(inst, "value", { get() {
4028
+ if (def.values.length > 1) throw new Error("This schema contains multiple valid literal values. Use `.values` instead.");
4029
+ return def.values[0];
4030
+ } });
4031
+ });
4032
+ function literal(value, params) {
4033
+ return new ZodLiteral({
4034
+ type: "literal",
4035
+ values: Array.isArray(value) ? value : [value],
4036
+ ...normalizeParams(params)
4037
+ });
4038
+ }
3983
4039
  var ZodTransform = /*@__PURE__*/ $constructor("ZodTransform", (inst, def) => {
3984
4040
  $ZodTransform.init(inst, def);
3985
4041
  ZodType.init(inst, def);
@@ -4146,6 +4202,6 @@ function superRefine(fn, params) {
4146
4202
  return /* @__PURE__ */ _superRefine(fn, params);
4147
4203
  }
4148
4204
  //#endregion
4149
- export { object as a, unknown as c, number as i, array as n, record as o, boolean as r, string as s, _enum as t };
4205
+ export { number as a, string as c, literal as i, unknown as l, array as n, object as o, boolean as r, record as s, _enum as t };
4150
4206
 
4151
- //# sourceMappingURL=schemas-B2fUvgYo.js.map
4207
+ //# sourceMappingURL=schemas-POKnVn0c.js.map