@powerhousedao/reactor-attachments 6.2.3-dev.5 → 6.2.3-dev.51
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/attachment-schema-compiler-rIaJpiHO.js +262 -0
- package/dist/attachment-schema-compiler-rIaJpiHO.js.map +1 -0
- package/dist/client.d.ts +52 -36
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +23 -6
- package/dist/client.js.map +1 -1
- package/dist/hash-CKHeI4AH.js +26 -0
- package/dist/hash-CKHeI4AH.js.map +1 -0
- package/dist/index.d.ts +80 -110
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +131 -403
- package/dist/index.js.map +1 -1
- package/dist/{null-attachment-transport-CMrO_ZKA.d.ts → interfaces-jHW9JXv1.d.ts} +30 -326
- package/dist/interfaces-jHW9JXv1.d.ts.map +1 -0
- package/dist/{null-attachment-transport-CrsUafCi.js → null-attachment-transport-BGG9cRC-.js} +24 -283
- package/dist/null-attachment-transport-BGG9cRC-.js.map +1 -0
- package/dist/null-attachment-transport-dg7vi6F4.d.ts +327 -0
- package/dist/null-attachment-transport-dg7vi6F4.d.ts.map +1 -0
- package/dist/replication.d.ts +955 -0
- package/dist/replication.d.ts.map +1 -0
- package/dist/replication.js +1975 -0
- package/dist/replication.js.map +1 -0
- package/dist/switchboard-attachment-transport-BH3ZAmxE.js +284 -0
- package/dist/switchboard-attachment-transport-BH3ZAmxE.js.map +1 -0
- package/dist/types-AagIcjTD.d.ts +61 -0
- package/dist/types-AagIcjTD.d.ts.map +1 -0
- package/package.json +15 -7
- package/dist/null-attachment-transport-CMrO_ZKA.d.ts.map +0 -1
- package/dist/null-attachment-transport-CrsUafCi.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as parseRef, c as AttachmentPending, d as InvalidAttachmentMetadata, f as InvalidAttachmentRef, h as UploadTooLarge, i as createRef, l as AttachmentTransferError, m as SizeMismatch, o as AttachmentAlreadyExists, p as ReservationNotFound, s as AttachmentNotFound, t as SwitchboardAttachmentTransport, u as HashMismatch } from "./switchboard-attachment-transport-BH3ZAmxE.js";
|
|
2
|
+
import { a as RemoteAttachmentUpload, c as createFetchUploadTransport, d as parseAttachmentUploadTarget, i as RemoteAttachmentUploadFactory, l as AttachmentService, n as createRemoteAttachmentService, o as RemoteReservationStore, r as RemoteAttachmentStore, s as createXhrUploadTransport, t as NullAttachmentTransport, u as parseAttachmentDownloadTarget } from "./null-attachment-transport-BGG9cRC-.js";
|
|
3
|
+
import { t as AttachmentSchemaCompiler } from "./attachment-schema-compiler-rIaJpiHO.js";
|
|
2
4
|
import { dirname, join } from "node:path";
|
|
3
5
|
import { Migrator, sql } from "kysely";
|
|
4
6
|
import { mkdir, rename, rm } from "node:fs/promises";
|
|
@@ -6,10 +8,8 @@ import { createReadStream, createWriteStream } from "node:fs";
|
|
|
6
8
|
import { createHash, randomUUID } from "node:crypto";
|
|
7
9
|
import { Readable } from "node:stream";
|
|
8
10
|
import { finished } from "node:stream/promises";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { Kind, buildASTSchema, getNamedType, isInputObjectType, isListType, isNonNullType, parse, print } from "graphql";
|
|
12
|
-
import { BaseReadModel } from "@powerhousedao/reactor";
|
|
11
|
+
import { BaseReadModel, defaultReadModelIndexingConfig } from "@powerhousedao/reactor";
|
|
12
|
+
import { isPurgeMarker } from "@powerhousedao/shared/document-model";
|
|
13
13
|
import { Buffer as Buffer$1 } from "node:buffer";
|
|
14
14
|
import { GetObjectCommand, HeadObjectCommand, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
|
15
15
|
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
@@ -25,6 +25,27 @@ var __exportAll = (all, no_symbols) => {
|
|
|
25
25
|
return target;
|
|
26
26
|
};
|
|
27
27
|
//#endregion
|
|
28
|
+
//#region src/reserve-metadata.ts
|
|
29
|
+
const CONTROL_CHARS = /[\x00-\x1f\x7f]/;
|
|
30
|
+
const NON_HEADER_CHARS = /[^\t\x20-\x7e\x80-\xff]/;
|
|
31
|
+
const MIME_TYPE_PATTERN = /^[!#$%&'*+\-.^_`|~\w]+\/[!#$%&'*+\-.^_`|~\w]+(?:[ \t]*;[ \t]*[!#$%&'*+\-.^_`|~\w]+=(?:[!#$%&'*+\-.^_`|~\w]+|"(?:[^"\\\r\n]|\\[^\r\n])*"))*$/;
|
|
32
|
+
const MAX_FILENAME_LEN = 255;
|
|
33
|
+
const MAX_MIMETYPE_LEN = 255;
|
|
34
|
+
/**
|
|
35
|
+
* Validates reserve metadata with the rules the switchboard reserve route
|
|
36
|
+
* applies.
|
|
37
|
+
*
|
|
38
|
+
* @throws InvalidAttachmentMetadata naming the first field that fails.
|
|
39
|
+
*/
|
|
40
|
+
function validateReserveMetadata(meta) {
|
|
41
|
+
const { mimeType, fileName, extension } = meta;
|
|
42
|
+
if (typeof mimeType !== "string" || mimeType.length === 0 || mimeType.length > MAX_MIMETYPE_LEN || NON_HEADER_CHARS.test(mimeType) || !MIME_TYPE_PATTERN.test(mimeType)) throw new InvalidAttachmentMetadata("mimeType");
|
|
43
|
+
if (typeof fileName !== "string" || fileName.length === 0 || fileName.length > MAX_FILENAME_LEN || CONTROL_CHARS.test(fileName)) throw new InvalidAttachmentMetadata("fileName");
|
|
44
|
+
if (typeof extension === "string") {
|
|
45
|
+
if (extension.length === 0 || /[\\/]/.test(extension)) throw new InvalidAttachmentMetadata("extension");
|
|
46
|
+
} else if (extension !== void 0 && extension !== null) throw new InvalidAttachmentMetadata("extension");
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
28
49
|
//#region src/storage/fs/attachment-fs.ts
|
|
29
50
|
/**
|
|
30
51
|
* Compute the relative storage path for an attachment hash.
|
|
@@ -262,6 +283,9 @@ function wrapStreamWithCleanup(source, cleanup) {
|
|
|
262
283
|
});
|
|
263
284
|
}
|
|
264
285
|
var KyselyAttachmentStore = class {
|
|
286
|
+
db;
|
|
287
|
+
transport;
|
|
288
|
+
basePath;
|
|
265
289
|
activeReaders = /* @__PURE__ */ new Map();
|
|
266
290
|
constructor(db, transport, basePath) {
|
|
267
291
|
this.db = db;
|
|
@@ -290,18 +314,10 @@ var KyselyAttachmentStore = class {
|
|
|
290
314
|
async has(hash) {
|
|
291
315
|
return (await this.db.selectFrom("attachment").select("status").where("hash", "=", hash).executeTakeFirst())?.status === "available";
|
|
292
316
|
}
|
|
293
|
-
async get(hash, signal) {
|
|
317
|
+
async get(hash, signal, documentId) {
|
|
294
318
|
const row = await this.db.selectFrom("attachment").selectAll().where("hash", "=", hash).executeTakeFirst();
|
|
295
319
|
if (row) {
|
|
296
|
-
if (row.status === "evicted")
|
|
297
|
-
const remote = await this.transport.fetch(hash, signal);
|
|
298
|
-
if (remote.kind === "data") {
|
|
299
|
-
await this.put(hash, remote.response.metadata, remote.response.body);
|
|
300
|
-
return this.get(hash, signal);
|
|
301
|
-
}
|
|
302
|
-
if (remote.kind === "pending") throw new AttachmentPending(hash, remote.expiresAtUtc);
|
|
303
|
-
throw new AttachmentNotFound(hash);
|
|
304
|
-
}
|
|
320
|
+
if (row.status === "evicted") return this.fetchRemote(hash, signal, documentId);
|
|
305
321
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
306
322
|
await this.db.updateTable("attachment").set({ last_accessed_at_utc: now }).where("hash", "=", hash).execute();
|
|
307
323
|
const header = rowToHeader$1(row);
|
|
@@ -319,13 +335,7 @@ var KyselyAttachmentStore = class {
|
|
|
319
335
|
fileName: pending.file_name,
|
|
320
336
|
sizeBytes: pending.size_bytes
|
|
321
337
|
});
|
|
322
|
-
|
|
323
|
-
if (remote.kind === "data") {
|
|
324
|
-
await this.put(hash, remote.response.metadata, remote.response.body);
|
|
325
|
-
return this.get(hash, signal);
|
|
326
|
-
}
|
|
327
|
-
if (remote.kind === "pending") throw new AttachmentPending(hash, remote.expiresAtUtc);
|
|
328
|
-
throw new AttachmentNotFound(hash);
|
|
338
|
+
return this.fetchRemote(hash, signal, documentId);
|
|
329
339
|
}
|
|
330
340
|
async put(hash, metadata, data) {
|
|
331
341
|
const existing = await this.db.selectFrom("attachment").select(["hash", "status"]).where("hash", "=", hash).executeTakeFirst();
|
|
@@ -365,6 +375,16 @@ var KyselyAttachmentStore = class {
|
|
|
365
375
|
const result = await this.db.selectFrom("attachment").select(sql`COALESCE(SUM(size_bytes), 0)`.as("total")).where("status", "=", "available").executeTakeFirst();
|
|
366
376
|
return Number(result?.total ?? 0);
|
|
367
377
|
}
|
|
378
|
+
async fetchRemote(hash, signal, documentId) {
|
|
379
|
+
if (documentId === void 0) throw new AttachmentNotFound(hash);
|
|
380
|
+
const remote = await this.transport.fetch(hash, documentId, signal);
|
|
381
|
+
if (remote.kind === "data") {
|
|
382
|
+
await this.put(hash, remote.response.metadata, remote.response.body);
|
|
383
|
+
return this.get(hash, signal);
|
|
384
|
+
}
|
|
385
|
+
if (remote.kind === "pending") throw new AttachmentPending(hash, remote.expiresAtUtc);
|
|
386
|
+
throw new AttachmentNotFound(hash);
|
|
387
|
+
}
|
|
368
388
|
async findPendingReservation(hash, now) {
|
|
369
389
|
const row = await this.db.selectFrom("attachment_reservation as r").leftJoin("attachment as a", "a.hash", "r.client_hash").select([
|
|
370
390
|
"r.mime_type",
|
|
@@ -398,7 +418,7 @@ var KyselyAttachmentStore = class {
|
|
|
398
418
|
};
|
|
399
419
|
//#endregion
|
|
400
420
|
//#region src/storage/kysely/reservation-store.ts
|
|
401
|
-
const DEFAULT_RESERVATION_TTL_MS =
|
|
421
|
+
const DEFAULT_RESERVATION_TTL_MS = 864e5;
|
|
402
422
|
function rowToReservation(row) {
|
|
403
423
|
return {
|
|
404
424
|
reservationId: row.reservation_id,
|
|
@@ -412,6 +432,7 @@ function rowToReservation(row) {
|
|
|
412
432
|
};
|
|
413
433
|
}
|
|
414
434
|
var KyselyReservationStore = class {
|
|
435
|
+
db;
|
|
415
436
|
ttlMs;
|
|
416
437
|
constructor(db, ttlMs = DEFAULT_RESERVATION_TTL_MS) {
|
|
417
438
|
this.db = db;
|
|
@@ -601,6 +622,11 @@ function rowToHeader(row) {
|
|
|
601
622
|
};
|
|
602
623
|
}
|
|
603
624
|
var DirectAttachmentUpload = class {
|
|
625
|
+
reservation;
|
|
626
|
+
db;
|
|
627
|
+
basePath;
|
|
628
|
+
reservations;
|
|
629
|
+
maxBytes;
|
|
604
630
|
reservationId;
|
|
605
631
|
ref;
|
|
606
632
|
expiresAtUtc;
|
|
@@ -670,6 +696,10 @@ var DirectAttachmentUpload = class {
|
|
|
670
696
|
//#endregion
|
|
671
697
|
//#region src/direct/direct-attachment-upload-factory.ts
|
|
672
698
|
var DirectAttachmentUploadFactory = class {
|
|
699
|
+
db;
|
|
700
|
+
basePath;
|
|
701
|
+
reservations;
|
|
702
|
+
maxBytes;
|
|
673
703
|
constructor(db, basePath, reservations, maxBytes) {
|
|
674
704
|
this.db = db;
|
|
675
705
|
this.basePath = basePath;
|
|
@@ -688,6 +718,8 @@ var DirectAttachmentUploadFactory = class {
|
|
|
688
718
|
* can never accidentally return a direct-provider target.
|
|
689
719
|
*/
|
|
690
720
|
var FilesystemAttachmentBackend = class {
|
|
721
|
+
store;
|
|
722
|
+
config;
|
|
691
723
|
kind = "filesystem";
|
|
692
724
|
constructor(store, config) {
|
|
693
725
|
this.store = store;
|
|
@@ -739,6 +771,8 @@ var S3AttachmentUploadFactory = class {
|
|
|
739
771
|
//#endregion
|
|
740
772
|
//#region src/attachment-builder.ts
|
|
741
773
|
var AttachmentBuilder = class {
|
|
774
|
+
db;
|
|
775
|
+
storagePath;
|
|
742
776
|
transport = new NullAttachmentTransport();
|
|
743
777
|
customUploadFactory;
|
|
744
778
|
maxUploadBytes;
|
|
@@ -809,266 +843,59 @@ var AttachmentBuilder = class {
|
|
|
809
843
|
}
|
|
810
844
|
};
|
|
811
845
|
//#endregion
|
|
812
|
-
//#region src/
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
"Unknown",
|
|
816
|
-
"DateTime",
|
|
817
|
-
"Address",
|
|
818
|
-
ATTACHMENT_REF_TYPE,
|
|
819
|
-
...Object.keys(generatorTypeDefs)
|
|
820
|
-
]);
|
|
821
|
-
function describeContext(context) {
|
|
822
|
-
return `document type "${context.documentType}", version ${context.version}, action "${context.actionType}"`;
|
|
823
|
-
}
|
|
824
|
-
function compilationError(context, reason) {
|
|
825
|
-
return /* @__PURE__ */ new Error(`Attachment schema compilation failed for ${describeContext(context)}: ${reason}`);
|
|
826
|
-
}
|
|
827
|
-
function extractionError(context, path, reason) {
|
|
828
|
-
return /* @__PURE__ */ new Error(`Attachment extraction failed for ${describeContext(context)} at ${path}: ${reason}`);
|
|
829
|
-
}
|
|
830
|
-
function applicableSpecification(module, context) {
|
|
831
|
-
const matches = module.documentModel.global.specifications.filter((specification) => specification.version === context.version);
|
|
832
|
-
if (matches.length !== 1) throw compilationError(context, matches.length === 0 ? "the module has no matching specification" : "the module has multiple matching specifications");
|
|
833
|
-
return matches[0];
|
|
834
|
-
}
|
|
835
|
-
function parseOperations(specification, context) {
|
|
836
|
-
return specification.modules.flatMap((moduleSpecification) => moduleSpecification.operations.map((operation) => {
|
|
837
|
-
if (operation.schema === null) return {
|
|
838
|
-
document: null,
|
|
839
|
-
operation
|
|
840
|
-
};
|
|
841
|
-
try {
|
|
842
|
-
return {
|
|
843
|
-
document: parse(operation.schema),
|
|
844
|
-
operation
|
|
845
|
-
};
|
|
846
|
-
} catch {
|
|
847
|
-
throw compilationError(context, "an operation has invalid GraphQL SDL");
|
|
848
|
-
}
|
|
849
|
-
}));
|
|
850
|
-
}
|
|
851
|
-
function selectOperation(operations, context) {
|
|
852
|
-
const matches = operations.filter(({ operation }) => operation.name !== null && constantCase(operation.name) === context.actionType);
|
|
853
|
-
if (matches.length > 1) throw compilationError(context, "multiple operations map to the action");
|
|
854
|
-
if (matches.length === 0) return null;
|
|
855
|
-
return matches[0];
|
|
846
|
+
//#region src/read-models/attachment-reference/kysely-attachment-reference-store.ts
|
|
847
|
+
function encodeCursor(cursor) {
|
|
848
|
+
return JSON.stringify([cursor.documentId, cursor.ref]);
|
|
856
849
|
}
|
|
857
|
-
function
|
|
858
|
-
|
|
859
|
-
const stateSchemas = Object.values(specification.state).map((state) => state.schema);
|
|
860
|
-
const operationSchemas = specification.modules.flatMap((moduleSpecification) => moduleSpecification.operations.flatMap((operation) => operation.schema === null ? [] : [operation.schema]));
|
|
850
|
+
function decodeCursor(cursor) {
|
|
851
|
+
let parsed;
|
|
861
852
|
try {
|
|
862
|
-
|
|
863
|
-
...scalarSchemas,
|
|
864
|
-
...stateSchemas,
|
|
865
|
-
...operationSchemas
|
|
866
|
-
].filter(Boolean).join("\n\n"))));
|
|
853
|
+
parsed = JSON.parse(cursor);
|
|
867
854
|
} catch {
|
|
868
|
-
throw
|
|
855
|
+
throw new Error(`Invalid attachment reference scan cursor: ${JSON.stringify(cursor)}`);
|
|
869
856
|
}
|
|
870
|
-
}
|
|
871
|
-
function dedupeTypeDefinitions(document) {
|
|
872
|
-
const seen = /* @__PURE__ */ new Map();
|
|
873
|
-
const definitions = document.definitions.filter((definition) => {
|
|
874
|
-
if (!("name" in definition) || definition.name?.value === void 0 || definition.kind === Kind.SCALAR_TYPE_DEFINITION) return true;
|
|
875
|
-
const name = definition.name.value;
|
|
876
|
-
const printed = print(definition);
|
|
877
|
-
const existing = seen.get(name);
|
|
878
|
-
if (existing === void 0) {
|
|
879
|
-
seen.set(name, printed);
|
|
880
|
-
return true;
|
|
881
|
-
}
|
|
882
|
-
return existing !== printed;
|
|
883
|
-
});
|
|
884
|
-
return {
|
|
885
|
-
...document,
|
|
886
|
-
definitions
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
|
-
function attachmentReachableTypes(definitions) {
|
|
890
|
-
const reachable = /* @__PURE__ */ new Set();
|
|
891
|
-
let changed = true;
|
|
892
|
-
while (changed) {
|
|
893
|
-
changed = false;
|
|
894
|
-
for (const [name, definition] of definitions) {
|
|
895
|
-
if (reachable.has(name)) continue;
|
|
896
|
-
if (Object.values(definition.getFields()).some((field) => {
|
|
897
|
-
const typeName = getNamedType(field.type).name;
|
|
898
|
-
return typeName === ATTACHMENT_REF_TYPE || definitions.has(typeName) && reachable.has(typeName);
|
|
899
|
-
})) {
|
|
900
|
-
reachable.add(name);
|
|
901
|
-
changed = true;
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
return reachable;
|
|
906
|
-
}
|
|
907
|
-
function compileValuePlan(type, objectPlans) {
|
|
908
|
-
if (isNonNullType(type)) return {
|
|
909
|
-
...compileValuePlan(type.ofType, objectPlans),
|
|
910
|
-
required: true
|
|
911
|
-
};
|
|
912
|
-
if (isListType(type)) return {
|
|
913
|
-
item: compileValuePlan(type.ofType, objectPlans),
|
|
914
|
-
kind: "list",
|
|
915
|
-
required: false
|
|
916
|
-
};
|
|
917
|
-
const typeName = type.name;
|
|
918
|
-
if (typeName === ATTACHMENT_REF_TYPE) return {
|
|
919
|
-
kind: "attachment",
|
|
920
|
-
required: false
|
|
921
|
-
};
|
|
922
|
-
const body = objectPlans.get(typeName);
|
|
923
|
-
if (!body) throw new Error(`Internal attachment schema plan error for ${typeName}`);
|
|
857
|
+
if (!Array.isArray(parsed) || parsed.length !== 2 || typeof parsed[0] !== "string" || parsed[0] === "" || typeof parsed[1] !== "string" || parsed[1] === "") throw new Error(`Invalid attachment reference scan cursor: ${JSON.stringify(cursor)}`);
|
|
924
858
|
return {
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
required: false
|
|
928
|
-
};
|
|
929
|
-
}
|
|
930
|
-
function compileRootPlan(rootName, definitions) {
|
|
931
|
-
const reachable = attachmentReachableTypes(definitions);
|
|
932
|
-
if (!reachable.has(rootName)) return null;
|
|
933
|
-
const objectPlans = /* @__PURE__ */ new Map();
|
|
934
|
-
for (const name of reachable) objectPlans.set(name, { fields: [] });
|
|
935
|
-
for (const name of reachable) {
|
|
936
|
-
const definition = definitions.get(name);
|
|
937
|
-
const body = objectPlans.get(name);
|
|
938
|
-
if (!definition || !body) continue;
|
|
939
|
-
for (const field of Object.values(definition.getFields())) {
|
|
940
|
-
const typeName = getNamedType(field.type).name;
|
|
941
|
-
if (typeName !== ATTACHMENT_REF_TYPE && !reachable.has(typeName)) continue;
|
|
942
|
-
body.fields.push({
|
|
943
|
-
hasDefault: field.defaultValue !== void 0,
|
|
944
|
-
name: field.name,
|
|
945
|
-
value: compileValuePlan(field.type, objectPlans)
|
|
946
|
-
});
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
return objectPlans.get(rootName) ?? null;
|
|
950
|
-
}
|
|
951
|
-
function readOwnField(value, fieldName, context, path) {
|
|
952
|
-
try {
|
|
953
|
-
if (!Object.prototype.hasOwnProperty.call(value, fieldName)) return {
|
|
954
|
-
present: false,
|
|
955
|
-
value: void 0
|
|
956
|
-
};
|
|
957
|
-
return {
|
|
958
|
-
present: true,
|
|
959
|
-
value: value[fieldName]
|
|
960
|
-
};
|
|
961
|
-
} catch {
|
|
962
|
-
throw extractionError(context, path, "the declared field cannot be read");
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
function extractValue(plan, value, path, context, refs, seenRefs, activeObjects) {
|
|
966
|
-
if (value === null || value === void 0) {
|
|
967
|
-
if (plan.required) throw extractionError(context, path, "a required value is missing or null");
|
|
968
|
-
return;
|
|
969
|
-
}
|
|
970
|
-
if (plan.kind === "attachment") {
|
|
971
|
-
if (typeof value !== "string") throw extractionError(context, path, "expected an AttachmentRef string");
|
|
972
|
-
try {
|
|
973
|
-
parseRef(value);
|
|
974
|
-
} catch {
|
|
975
|
-
throw extractionError(context, path, "the AttachmentRef is malformed");
|
|
976
|
-
}
|
|
977
|
-
if (!seenRefs.has(value)) {
|
|
978
|
-
seenRefs.add(value);
|
|
979
|
-
refs.push(value);
|
|
980
|
-
}
|
|
981
|
-
return;
|
|
982
|
-
}
|
|
983
|
-
if (plan.kind === "list") {
|
|
984
|
-
if (!Array.isArray(value)) throw extractionError(context, path, "expected a list");
|
|
985
|
-
for (let index = 0; index < value.length; index += 1) extractValue(plan.item, value[index], `${path}[${index}]`, context, refs, seenRefs, activeObjects);
|
|
986
|
-
return;
|
|
987
|
-
}
|
|
988
|
-
if (typeof value !== "object" || Array.isArray(value)) throw extractionError(context, path, "expected an input object");
|
|
989
|
-
if (activeObjects.has(value)) throw extractionError(context, path, "the input value contains a cycle");
|
|
990
|
-
activeObjects.add(value);
|
|
991
|
-
try {
|
|
992
|
-
const record = value;
|
|
993
|
-
for (const field of plan.body.fields) {
|
|
994
|
-
const fieldPath = `${path}.${field.name}`;
|
|
995
|
-
const result = readOwnField(record, field.name, context, fieldPath);
|
|
996
|
-
if ((!result.present || result.value === void 0) && field.hasDefault) continue;
|
|
997
|
-
extractValue(field.value, result.value, fieldPath, context, refs, seenRefs, activeObjects);
|
|
998
|
-
}
|
|
999
|
-
} finally {
|
|
1000
|
-
activeObjects.delete(value);
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
var SchemaCompiledAttachmentExtractor = class {
|
|
1004
|
-
constructor(context, rootPlan) {
|
|
1005
|
-
this.context = context;
|
|
1006
|
-
this.rootPlan = rootPlan;
|
|
1007
|
-
}
|
|
1008
|
-
extract(action) {
|
|
1009
|
-
if (action.type !== this.context.actionType) throw extractionError(this.context, "input", "the action type does not match the compiled schema");
|
|
1010
|
-
if (!this.rootPlan) return [];
|
|
1011
|
-
if (action.input === null || typeof action.input !== "object" || Array.isArray(action.input)) throw extractionError(this.context, "input", "expected an input object");
|
|
1012
|
-
const refs = [];
|
|
1013
|
-
const seenRefs = /* @__PURE__ */ new Set();
|
|
1014
|
-
const activeObjects = /* @__PURE__ */ new WeakSet();
|
|
1015
|
-
activeObjects.add(action.input);
|
|
1016
|
-
try {
|
|
1017
|
-
const input = action.input;
|
|
1018
|
-
for (const field of this.rootPlan.fields) {
|
|
1019
|
-
const path = `input.${field.name}`;
|
|
1020
|
-
const result = readOwnField(input, field.name, this.context, path);
|
|
1021
|
-
if ((!result.present || result.value === void 0) && field.hasDefault) continue;
|
|
1022
|
-
extractValue(field.value, result.value, path, this.context, refs, seenRefs, activeObjects);
|
|
1023
|
-
}
|
|
1024
|
-
} finally {
|
|
1025
|
-
activeObjects.delete(action.input);
|
|
1026
|
-
}
|
|
1027
|
-
return refs;
|
|
1028
|
-
}
|
|
1029
|
-
};
|
|
1030
|
-
function compileExtractor(module, actionType) {
|
|
1031
|
-
const context = {
|
|
1032
|
-
actionType,
|
|
1033
|
-
documentType: module.documentModel.global.id,
|
|
1034
|
-
version: module.version ?? 1
|
|
859
|
+
documentId: parsed[0],
|
|
860
|
+
ref: parsed[1]
|
|
1035
861
|
};
|
|
1036
|
-
const specification = applicableSpecification(module, context);
|
|
1037
|
-
const selected = selectOperation(parseOperations(specification, context), context);
|
|
1038
|
-
if (selected === null || selected.operation.schema === null) return new SchemaCompiledAttachmentExtractor(context, null);
|
|
1039
|
-
const effectiveSchema = buildEffectiveSchema(specification, context);
|
|
1040
|
-
const operationName = selected.operation.name;
|
|
1041
|
-
if (operationName === null) throw compilationError(context, "the operation has no name");
|
|
1042
|
-
const rootName = `${pascalCase(operationName)}Input`;
|
|
1043
|
-
const definitions = /* @__PURE__ */ new Map();
|
|
1044
|
-
for (const type of Object.values(effectiveSchema.getTypeMap())) if (isInputObjectType(type) && !type.name.startsWith("__")) definitions.set(type.name, type);
|
|
1045
|
-
if (!(selected.document?.definitions.filter((definition) => (definition.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION || definition.kind === Kind.INPUT_OBJECT_TYPE_EXTENSION) && definition.name.value === rootName))?.length || !definitions.has(rootName)) throw compilationError(context, `the operation does not declare its expected root input "${rootName}"`);
|
|
1046
|
-
return new SchemaCompiledAttachmentExtractor(context, compileRootPlan(rootName, definitions));
|
|
1047
862
|
}
|
|
1048
|
-
var AttachmentSchemaCompiler = class {
|
|
1049
|
-
cache = /* @__PURE__ */ new WeakMap();
|
|
1050
|
-
forModuleAction(module, actionType) {
|
|
1051
|
-
let moduleCache = this.cache.get(module);
|
|
1052
|
-
if (!moduleCache) {
|
|
1053
|
-
moduleCache = /* @__PURE__ */ new Map();
|
|
1054
|
-
this.cache.set(module, moduleCache);
|
|
1055
|
-
}
|
|
1056
|
-
const cached = moduleCache.get(actionType);
|
|
1057
|
-
if (cached) return cached;
|
|
1058
|
-
const compiled = compileExtractor(module, actionType);
|
|
1059
|
-
moduleCache.set(actionType, compiled);
|
|
1060
|
-
return compiled;
|
|
1061
|
-
}
|
|
1062
|
-
};
|
|
1063
|
-
//#endregion
|
|
1064
|
-
//#region src/read-models/attachment-reference/kysely-attachment-reference-store.ts
|
|
1065
863
|
var KyselyAttachmentReferenceStore = class {
|
|
864
|
+
db;
|
|
1066
865
|
constructor(db) {
|
|
1067
866
|
this.db = db;
|
|
1068
867
|
}
|
|
1069
868
|
async hasReference(documentId, ref) {
|
|
1070
869
|
return await this.db.selectFrom("attachment_reference").select("document_id").where("document_id", "=", documentId).where("attachment_ref", "=", ref).executeTakeFirst() !== void 0;
|
|
1071
870
|
}
|
|
871
|
+
async referencingScopes(documentId, ref) {
|
|
872
|
+
return (await this.db.selectFrom("attachment_reference").select("scope").distinct().where("document_id", "=", documentId).where("attachment_ref", "=", ref).execute()).map((row) => row.scope);
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Keyset paging over the unique `(document_id, attachment_ref)` constraint,
|
|
876
|
+
* rather than OFFSET: a scan that runs while the read model is still
|
|
877
|
+
* indexing must not skip or repeat a row because rows appeared behind the
|
|
878
|
+
* cursor. That is exactly the boot case the replicator's re-scan runs in.
|
|
879
|
+
*/
|
|
880
|
+
async listReferences(cursor, limit) {
|
|
881
|
+
let query = this.db.selectFrom("attachment_reference").select(["document_id", "attachment_ref"]).orderBy("document_id", "asc").orderBy("attachment_ref", "asc").limit(limit);
|
|
882
|
+
if (cursor !== void 0) {
|
|
883
|
+
const after = decodeCursor(cursor);
|
|
884
|
+
query = query.where((eb) => eb.or([eb("document_id", ">", after.documentId), eb.and([eb("document_id", "=", after.documentId), eb("attachment_ref", ">", after.ref)])]));
|
|
885
|
+
}
|
|
886
|
+
const rows = await query.execute();
|
|
887
|
+
const last = rows.at(-1);
|
|
888
|
+
return {
|
|
889
|
+
references: rows.map((row) => ({
|
|
890
|
+
documentId: row.document_id,
|
|
891
|
+
ref: row.attachment_ref
|
|
892
|
+
})),
|
|
893
|
+
nextCursor: rows.length === limit && last ? encodeCursor({
|
|
894
|
+
documentId: last.document_id,
|
|
895
|
+
ref: last.attachment_ref
|
|
896
|
+
}) : void 0
|
|
897
|
+
};
|
|
898
|
+
}
|
|
1072
899
|
async addReferences(references) {
|
|
1073
900
|
if (references.length === 0) return;
|
|
1074
901
|
await this.db.insertInto("attachment_reference").values(references.map((reference) => ({
|
|
@@ -1082,6 +909,10 @@ var KyselyAttachmentReferenceStore = class {
|
|
|
1082
909
|
created_at_utc: (/* @__PURE__ */ new Date()).toISOString()
|
|
1083
910
|
}))).onConflict((oc) => oc.columns(["document_id", "attachment_ref"]).doNothing()).execute();
|
|
1084
911
|
}
|
|
912
|
+
async removeDocuments(documentIds) {
|
|
913
|
+
if (documentIds.length === 0) return;
|
|
914
|
+
await this.db.deleteFrom("attachment_reference").where("document_id", "in", [...documentIds]).execute();
|
|
915
|
+
}
|
|
1085
916
|
};
|
|
1086
917
|
//#endregion
|
|
1087
918
|
//#region src/read-models/attachment-reference/storage/migrations/001_create_attachment_reference_table.ts
|
|
@@ -1160,6 +991,7 @@ async function getAttachmentReferenceMigrationStatus(db, schema = ATTACHMENT_REF
|
|
|
1160
991
|
//#endregion
|
|
1161
992
|
//#region src/read-models/attachment-reference/index-builder.ts
|
|
1162
993
|
var AttachmentReferenceIndexBuilder = class {
|
|
994
|
+
db;
|
|
1163
995
|
constructor(db) {
|
|
1164
996
|
this.db = db;
|
|
1165
997
|
}
|
|
@@ -1172,52 +1004,40 @@ var AttachmentReferenceIndexBuilder = class {
|
|
|
1172
1004
|
//#endregion
|
|
1173
1005
|
//#region src/read-models/attachment-reference/attachment-reference-read-model.ts
|
|
1174
1006
|
const ATTACHMENT_REFERENCE_READ_MODEL_ID = "attachment-reference-read-model";
|
|
1007
|
+
/**
|
|
1008
|
+
* Indexes attachment references in chunks.
|
|
1009
|
+
*
|
|
1010
|
+
* Each reference row stands on its own and is written insert-or-do-nothing, so
|
|
1011
|
+
* a batch that commits in pieces exposes no partial structure, and re-indexing
|
|
1012
|
+
* the same range writes nothing new: the catch-up sweep needs no stream suffix.
|
|
1013
|
+
*
|
|
1014
|
+
* The rows live on another handle, so the purge fence is "skip": a tombstoned
|
|
1015
|
+
* id's operations are dropped at commit and its marker deletes its rows.
|
|
1016
|
+
*/
|
|
1175
1017
|
var AttachmentReferenceReadModel = class extends BaseReadModel {
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
* How far a replay may skip ahead of the cursor: the highest ordinal already
|
|
1180
|
-
* pulled from the index while the cursor was parked.
|
|
1181
|
-
*
|
|
1182
|
-
* NOT a claim that every ordinal below it committed -- the parked ordinal is
|
|
1183
|
-
* precisely the one that did not -- so it is a hint, and {@link replayFrom}
|
|
1184
|
-
* re-probes the gap before honouring it.
|
|
1185
|
-
*/
|
|
1186
|
-
replayedThrough;
|
|
1187
|
-
warnedCheckpoint;
|
|
1018
|
+
documentModelRegistry;
|
|
1019
|
+
schemaCompiler;
|
|
1020
|
+
referenceWriter;
|
|
1188
1021
|
constructor(db, operationIndex, writeCache, consistencyTracker, documentModelRegistry, schemaCompiler, referenceWriter) {
|
|
1189
1022
|
super(db, operationIndex, writeCache, consistencyTracker, {
|
|
1190
1023
|
readModelId: ATTACHMENT_REFERENCE_READ_MODEL_ID,
|
|
1191
|
-
rebuildStateOnInit: false
|
|
1024
|
+
rebuildStateOnInit: false,
|
|
1025
|
+
indexing: defaultReadModelIndexingConfig,
|
|
1026
|
+
replayStreamSuffix: false,
|
|
1027
|
+
purgeFence: "skip"
|
|
1192
1028
|
});
|
|
1193
1029
|
this.documentModelRegistry = documentModelRegistry;
|
|
1194
1030
|
this.schemaCompiler = schemaCompiler;
|
|
1195
1031
|
this.referenceWriter = referenceWriter;
|
|
1196
1032
|
}
|
|
1197
|
-
indexOperations(items) {
|
|
1198
|
-
return this.enqueue(() => this.indexOperationsInOrdinalOrder(items));
|
|
1199
|
-
}
|
|
1200
|
-
init() {
|
|
1201
|
-
return this.enqueue(async () => {
|
|
1202
|
-
const viewState = await this.loadState();
|
|
1203
|
-
if (viewState !== void 0) this.lastOrdinal = viewState;
|
|
1204
|
-
else await this.initializeState();
|
|
1205
|
-
let page = await this.operationIndex.getSinceOrdinal(this.lastOrdinal);
|
|
1206
|
-
while (page.results.length > 0) {
|
|
1207
|
-
await this.indexOperationsInOrdinalOrder(page.results);
|
|
1208
|
-
if (!page.next) break;
|
|
1209
|
-
page = await page.next();
|
|
1210
|
-
}
|
|
1211
|
-
});
|
|
1212
|
-
}
|
|
1213
|
-
enqueue(work) {
|
|
1214
|
-
const result = this.indexingQueue.then(work);
|
|
1215
|
-
this.indexingQueue = result.catch(() => void 0);
|
|
1216
|
-
return result;
|
|
1217
|
-
}
|
|
1218
1033
|
async commitOperations(items) {
|
|
1219
1034
|
const references = [];
|
|
1035
|
+
const purged = /* @__PURE__ */ new Set();
|
|
1220
1036
|
for (const { operation, context } of items) {
|
|
1037
|
+
if (isPurgeMarker(operation)) {
|
|
1038
|
+
purged.add(context.documentId);
|
|
1039
|
+
continue;
|
|
1040
|
+
}
|
|
1221
1041
|
if (operation.error !== void 0) continue;
|
|
1222
1042
|
const module = this.documentModelRegistry.getModule(context.documentType);
|
|
1223
1043
|
const refs = this.schemaCompiler.forModuleAction(module, operation.action.type).extract(operation.action);
|
|
@@ -1231,102 +1051,7 @@ var AttachmentReferenceReadModel = class extends BaseReadModel {
|
|
|
1231
1051
|
});
|
|
1232
1052
|
}
|
|
1233
1053
|
if (references.length > 0) await this.referenceWriter.addReferences(references);
|
|
1234
|
-
|
|
1235
|
-
async indexOperationsInOrdinalOrder(incoming) {
|
|
1236
|
-
let candidates = this.sortAndDedupe(incoming);
|
|
1237
|
-
if (candidates.length === 0) return;
|
|
1238
|
-
const incomingMax = candidates[candidates.length - 1].context.ordinal;
|
|
1239
|
-
if (this.contiguousEnd(candidates) < incomingMax) {
|
|
1240
|
-
const replayed = await this.loadThroughOrdinal(incomingMax);
|
|
1241
|
-
candidates = this.sortAndDedupe([...replayed, ...candidates]);
|
|
1242
|
-
}
|
|
1243
|
-
const checkpoint = this.contiguousEnd(candidates);
|
|
1244
|
-
if (checkpoint < incomingMax && checkpoint !== this.warnedCheckpoint) {
|
|
1245
|
-
this.warnedCheckpoint = checkpoint;
|
|
1246
|
-
console.warn(`[${this.config.readModelId}] indexed through ordinal ${incomingMax} but parked the cursor at ${checkpoint}: ordinal ${checkpoint + 1} is missing`);
|
|
1247
|
-
}
|
|
1248
|
-
const previousOrdinal = this.lastOrdinal;
|
|
1249
|
-
this.checkpointTarget = checkpoint;
|
|
1250
|
-
try {
|
|
1251
|
-
await super.indexOperations(candidates);
|
|
1252
|
-
} catch (error) {
|
|
1253
|
-
this.lastOrdinal = previousOrdinal;
|
|
1254
|
-
this.replayedThrough = void 0;
|
|
1255
|
-
throw error;
|
|
1256
|
-
} finally {
|
|
1257
|
-
this.checkpointTarget = void 0;
|
|
1258
|
-
}
|
|
1259
|
-
if (checkpoint > previousOrdinal) this.replayedThrough = void 0;
|
|
1260
|
-
else if (checkpoint < incomingMax) this.replayedThrough = Math.max(this.replayedThrough ?? checkpoint, incomingMax);
|
|
1261
|
-
}
|
|
1262
|
-
/**
|
|
1263
|
-
* Writes the cursor parked by indexOperationsInOrdinalOrder instead of the
|
|
1264
|
-
* batch maximum, so indexing an operation above a gap never advances past it.
|
|
1265
|
-
*/
|
|
1266
|
-
async saveState(trx, items) {
|
|
1267
|
-
const target = this.checkpointTarget;
|
|
1268
|
-
if (target === void 0) {
|
|
1269
|
-
await super.saveState(trx, items);
|
|
1270
|
-
return;
|
|
1271
|
-
}
|
|
1272
|
-
this.lastOrdinal = target;
|
|
1273
|
-
await trx.updateTable("ViewState").set({
|
|
1274
|
-
lastOrdinal: target,
|
|
1275
|
-
lastOperationTimestamp: /* @__PURE__ */ new Date()
|
|
1276
|
-
}).where("readModelId", "=", this.config.readModelId).execute();
|
|
1277
|
-
}
|
|
1278
|
-
/** Last ordinal of the contiguous run starting at lastOrdinal + 1. */
|
|
1279
|
-
contiguousEnd(items) {
|
|
1280
|
-
let expectedOrdinal = this.lastOrdinal + 1;
|
|
1281
|
-
for (const item of items) {
|
|
1282
|
-
const ordinal = item.context.ordinal;
|
|
1283
|
-
if (ordinal < expectedOrdinal) continue;
|
|
1284
|
-
if (ordinal > expectedOrdinal) break;
|
|
1285
|
-
expectedOrdinal++;
|
|
1286
|
-
}
|
|
1287
|
-
return expectedOrdinal - 1;
|
|
1288
|
-
}
|
|
1289
|
-
/**
|
|
1290
|
-
* Opens a replay at the lowest ordinal still worth reading.
|
|
1291
|
-
*
|
|
1292
|
-
* {@link replayedThrough} is what keeps a permanently held hole -- a
|
|
1293
|
-
* rolled-back insert, which never fills -- from re-reading the whole tail on
|
|
1294
|
-
* every batch. It cannot be trusted on its own: a hole that fills without
|
|
1295
|
-
* being delivered here is visible only in the index, and a mark that is
|
|
1296
|
-
* never questioned would hide that operation for the life of the process.
|
|
1297
|
-
* That happens whenever another writer commits the gap (a second reactor on
|
|
1298
|
-
* the same database), and it leaves the cursor parked below an operation
|
|
1299
|
-
* whose references were never written -- `hasReference` then answers false
|
|
1300
|
-
* for an attachment that is genuinely referenced.
|
|
1301
|
-
*
|
|
1302
|
-
* So a parked batch spends one page probing the gap: the first row above the
|
|
1303
|
-
* cursor is the missing ordinal itself once it commits. Finding it drops the
|
|
1304
|
-
* mark and replays from the cursor; not finding it leaves the mark standing,
|
|
1305
|
-
* which is the cheap path and the common one.
|
|
1306
|
-
*/
|
|
1307
|
-
async replayFrom() {
|
|
1308
|
-
const fromCursor = await this.operationIndex.getSinceOrdinal(this.lastOrdinal);
|
|
1309
|
-
if (this.replayedThrough === void 0) return fromCursor;
|
|
1310
|
-
if (fromCursor.results[0]?.context.ordinal === this.lastOrdinal + 1) {
|
|
1311
|
-
this.replayedThrough = void 0;
|
|
1312
|
-
return fromCursor;
|
|
1313
|
-
}
|
|
1314
|
-
return this.operationIndex.getSinceOrdinal(this.replayedThrough);
|
|
1315
|
-
}
|
|
1316
|
-
async loadThroughOrdinal(maxOrdinal) {
|
|
1317
|
-
const operations = [];
|
|
1318
|
-
let page = await this.replayFrom();
|
|
1319
|
-
for (;;) {
|
|
1320
|
-
for (const item of page.results) if (item.context.ordinal <= maxOrdinal) operations.push(item);
|
|
1321
|
-
if (page.results.some(({ context }) => context.ordinal >= maxOrdinal) || !page.next) break;
|
|
1322
|
-
page = await page.next();
|
|
1323
|
-
}
|
|
1324
|
-
return operations;
|
|
1325
|
-
}
|
|
1326
|
-
sortAndDedupe(items) {
|
|
1327
|
-
const byOrdinal = /* @__PURE__ */ new Map();
|
|
1328
|
-
for (const item of items) byOrdinal.set(item.context.ordinal, item);
|
|
1329
|
-
return [...byOrdinal.values()].sort((left, right) => left.context.ordinal - right.context.ordinal);
|
|
1054
|
+
if (purged.size > 0) await this.referenceWriter.removeDocuments([...purged]);
|
|
1330
1055
|
}
|
|
1331
1056
|
};
|
|
1332
1057
|
//#endregion
|
|
@@ -1341,7 +1066,7 @@ function required(env, name) {
|
|
|
1341
1066
|
if (value.trim() !== value) throw new Error(`${name} must not have leading or trailing whitespace`);
|
|
1342
1067
|
return value;
|
|
1343
1068
|
}
|
|
1344
|
-
const LOOPBACK_HOSTNAMES = new Set([
|
|
1069
|
+
const LOOPBACK_HOSTNAMES = /* @__PURE__ */ new Set([
|
|
1345
1070
|
"127.0.0.1",
|
|
1346
1071
|
"localhost",
|
|
1347
1072
|
"[::1]"
|
|
@@ -1423,9 +1148,10 @@ function sha256HexToBase64(hash) {
|
|
|
1423
1148
|
//#region src/storage/s3/primitives.ts
|
|
1424
1149
|
const defaultPresigner = (client, command, expiresInSeconds) => getSignedUrl(client, command, {
|
|
1425
1150
|
expiresIn: expiresInSeconds,
|
|
1426
|
-
unhoistableHeaders: new Set(["x-amz-checksum-sha256"])
|
|
1151
|
+
unhoistableHeaders: /* @__PURE__ */ new Set(["x-amz-checksum-sha256"])
|
|
1427
1152
|
});
|
|
1428
1153
|
var S3AttachmentPrimitives = class {
|
|
1154
|
+
config;
|
|
1429
1155
|
client;
|
|
1430
1156
|
presign;
|
|
1431
1157
|
now;
|
|
@@ -1496,7 +1222,7 @@ function createS3AttachmentPrimitives(config, dependencies = {}) {
|
|
|
1496
1222
|
//#endregion
|
|
1497
1223
|
//#region src/storage/s3/backend.ts
|
|
1498
1224
|
const READINESS_PROBE_HASH = "0".repeat(64);
|
|
1499
|
-
const OBJECT_NOT_FOUND_NAMES = new Set([
|
|
1225
|
+
const OBJECT_NOT_FOUND_NAMES = /* @__PURE__ */ new Set([
|
|
1500
1226
|
"NotFound",
|
|
1501
1227
|
"NoSuchKey",
|
|
1502
1228
|
"NoSuchObject"
|
|
@@ -1514,6 +1240,8 @@ function requireHashFirstReservation(reservation) {
|
|
|
1514
1240
|
}
|
|
1515
1241
|
/** Server-only S3 capability. Callers complete authorization before download. */
|
|
1516
1242
|
var S3AttachmentBackend = class {
|
|
1243
|
+
db;
|
|
1244
|
+
config;
|
|
1517
1245
|
kind = "s3";
|
|
1518
1246
|
primitives;
|
|
1519
1247
|
now;
|
|
@@ -1600,6 +1328,6 @@ function createS3AttachmentBackend(db, config, dependencies = {}) {
|
|
|
1600
1328
|
return new S3AttachmentBackend(db, config, dependencies);
|
|
1601
1329
|
}
|
|
1602
1330
|
//#endregion
|
|
1603
|
-
export { ATTACHMENT_REFERENCE_MIGRATION_LOCK_TABLE, ATTACHMENT_REFERENCE_MIGRATION_TABLE, ATTACHMENT_REFERENCE_READ_MODEL_ID, ATTACHMENT_REFERENCE_SCHEMA, ATTACHMENT_SCHEMA, AttachmentAlreadyExists, AttachmentBuilder, AttachmentNotFound, AttachmentPending, AttachmentReferenceIndexBuilder, AttachmentReferenceReadModel, AttachmentSchemaCompiler, AttachmentService, AttachmentTransferError, DEFAULT_RESERVATION_TTL_MS, DEFAULT_S3_ATTACHMENT_PREFIX, DEFAULT_S3_DOWNLOAD_TTL_SECONDS, DEFAULT_S3_UPLOAD_TTL_SECONDS, DirectAttachmentUpload, DirectAttachmentUploadFactory, FilesystemAttachmentBackend, HashMismatch, InvalidAttachmentRef, KyselyAttachmentReferenceStore, KyselyAttachmentStore, KyselyReservationStore, MAX_S3_PRESIGN_TTL_SECONDS, NullAttachmentTransport, RemoteAttachmentStore, RemoteAttachmentUpload, RemoteAttachmentUploadFactory, RemoteReservationStore, ReservationNotFound, S3AttachmentBackend, S3AttachmentPrimitives, S3AttachmentUploadFactory, SizeMismatch, SwitchboardAttachmentTransport, UploadTooLarge, createFetchUploadTransport, createRef, createRemoteAttachmentService, createS3AttachmentBackend, createS3AttachmentPrimitives, createXhrUploadTransport, deriveS3AttachmentKey, getAttachmentReferenceMigrationStatus, normalizeS3AttachmentPrefix, parseAttachmentDownloadTarget, parseAttachmentStorageConfig, parseAttachmentUploadTarget, parseRef, rollbackAttachmentReferenceMigration, runAttachmentMigrations, runAttachmentReferenceMigrations, sha256HexToBase64 };
|
|
1331
|
+
export { ATTACHMENT_REFERENCE_MIGRATION_LOCK_TABLE, ATTACHMENT_REFERENCE_MIGRATION_TABLE, ATTACHMENT_REFERENCE_READ_MODEL_ID, ATTACHMENT_REFERENCE_SCHEMA, ATTACHMENT_SCHEMA, AttachmentAlreadyExists, AttachmentBuilder, AttachmentNotFound, AttachmentPending, AttachmentReferenceIndexBuilder, AttachmentReferenceReadModel, AttachmentSchemaCompiler, AttachmentService, AttachmentTransferError, DEFAULT_RESERVATION_TTL_MS, DEFAULT_S3_ATTACHMENT_PREFIX, DEFAULT_S3_DOWNLOAD_TTL_SECONDS, DEFAULT_S3_UPLOAD_TTL_SECONDS, DirectAttachmentUpload, DirectAttachmentUploadFactory, FilesystemAttachmentBackend, HashMismatch, InvalidAttachmentMetadata, InvalidAttachmentRef, KyselyAttachmentReferenceStore, KyselyAttachmentStore, KyselyReservationStore, MAX_S3_PRESIGN_TTL_SECONDS, NullAttachmentTransport, RemoteAttachmentStore, RemoteAttachmentUpload, RemoteAttachmentUploadFactory, RemoteReservationStore, ReservationNotFound, S3AttachmentBackend, S3AttachmentPrimitives, S3AttachmentUploadFactory, SizeMismatch, SwitchboardAttachmentTransport, UploadTooLarge, createFetchUploadTransport, createRef, createRemoteAttachmentService, createS3AttachmentBackend, createS3AttachmentPrimitives, createXhrUploadTransport, deriveS3AttachmentKey, getAttachmentReferenceMigrationStatus, normalizeS3AttachmentPrefix, parseAttachmentDownloadTarget, parseAttachmentStorageConfig, parseAttachmentUploadTarget, parseRef, rollbackAttachmentReferenceMigration, runAttachmentMigrations, runAttachmentReferenceMigrations, sha256HexToBase64, validateReserveMetadata };
|
|
1604
1332
|
|
|
1605
1333
|
//# sourceMappingURL=index.js.map
|