@powerhousedao/reactor-attachments 6.2.3-dev.6 → 6.2.3-dev.60

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.
@@ -0,0 +1,284 @@
1
+ //#region src/errors.ts
2
+ /**
3
+ * Thrown when an attachment ref or hash is not known to the store.
4
+ */
5
+ var AttachmentNotFound = class extends Error {
6
+ constructor(identifier) {
7
+ super(`Attachment not found: ${identifier}`);
8
+ this.name = "AttachmentNotFound";
9
+ }
10
+ };
11
+ /**
12
+ * Thrown when a reservation ID is not found in the reservation store.
13
+ */
14
+ var ReservationNotFound = class extends Error {
15
+ constructor(reservationId) {
16
+ super(`Reservation not found: ${reservationId}`);
17
+ this.name = "ReservationNotFound";
18
+ }
19
+ };
20
+ /**
21
+ * Thrown when an attachment ref string does not match the expected format.
22
+ */
23
+ var InvalidAttachmentRef = class extends Error {
24
+ constructor(ref) {
25
+ super(`Invalid attachment ref: ${ref}`);
26
+ this.name = "InvalidAttachmentRef";
27
+ }
28
+ };
29
+ /**
30
+ * Thrown when reserve metadata (MIME type, file name or extension) is not
31
+ * acceptable. Route handlers should map this to HTTP 400 Bad Request.
32
+ */
33
+ var InvalidAttachmentMetadata = class extends Error {
34
+ field;
35
+ constructor(field) {
36
+ super(`Invalid attachment ${field}`);
37
+ this.name = "InvalidAttachmentMetadata";
38
+ this.field = field;
39
+ }
40
+ };
41
+ /**
42
+ * Thrown when an upload exceeds the configured maximum byte cap.
43
+ * Route handlers should map this to HTTP 413 Payload Too Large.
44
+ */
45
+ var UploadTooLarge = class extends Error {
46
+ maxBytes;
47
+ constructor(maxBytes) {
48
+ super(`Upload exceeds maximum size of ${maxBytes} bytes`);
49
+ this.name = "UploadTooLarge";
50
+ this.maxBytes = maxBytes;
51
+ }
52
+ };
53
+ /**
54
+ * Thrown by reserve() when the claimed hash is already available in the store.
55
+ * The caller should use err.ref directly and upload nothing -- this is the
56
+ * dedup fast path: duplicate content never leaves the client.
57
+ */
58
+ var AttachmentAlreadyExists = class extends Error {
59
+ hash;
60
+ ref;
61
+ constructor(hash, ref) {
62
+ super(`Attachment already exists for hash: ${hash}`);
63
+ this.name = "AttachmentAlreadyExists";
64
+ this.hash = hash;
65
+ this.ref = ref;
66
+ }
67
+ };
68
+ /**
69
+ * Thrown by send() when the server-computed hash of the uploaded bytes
70
+ * does not match the hash claimed at reservation time. Nothing is committed;
71
+ * the reservation is retained so the client can retry with correct bytes.
72
+ */
73
+ var HashMismatch = class extends Error {
74
+ claimed;
75
+ actual;
76
+ constructor(claimed, actual) {
77
+ super(`Hash mismatch: claimed ${claimed} but computed ${actual}`);
78
+ this.name = "HashMismatch";
79
+ this.claimed = claimed;
80
+ this.actual = actual;
81
+ }
82
+ };
83
+ /**
84
+ * Thrown by send() when the uploaded byte count does not equal the
85
+ * sizeBytes declared at reservation time. The handle may reject
86
+ * mid-stream as soon as the count exceeds the declaration.
87
+ * Nothing is committed; the reservation is retained for retry.
88
+ *
89
+ * "actual" is the byte count received from the stream before aborting --
90
+ * it includes the chunk that crossed the declaration and can exceed bytes
91
+ * persisted. On mid-stream aborts the true total is unknown; at least
92
+ * "actual" bytes were sent.
93
+ */
94
+ var SizeMismatch = class extends Error {
95
+ declared;
96
+ actual;
97
+ constructor(declared, actual) {
98
+ super(`Size mismatch: declared ${declared} bytes but received ${actual}`);
99
+ this.name = "SizeMismatch";
100
+ this.declared = declared;
101
+ this.actual = actual;
102
+ }
103
+ };
104
+ /**
105
+ * Thrown when a remote transfer step fails. Identifies the stage only; it
106
+ * intentionally carries no URL, signature query, bucket key, or bearer
107
+ * material so it is always safe to log or surface.
108
+ */
109
+ var AttachmentTransferError = class extends Error {
110
+ stage;
111
+ status;
112
+ constructor(stage, status) {
113
+ super(status === void 0 ? `Attachment ${stage} request failed` : `Attachment ${stage} request failed with status ${status}`);
114
+ this.name = "AttachmentTransferError";
115
+ this.stage = stage;
116
+ this.status = status;
117
+ }
118
+ };
119
+ var AttachmentPending = class extends Error {
120
+ hash;
121
+ expiresAtUtc;
122
+ metadata;
123
+ constructor(hash, expiresAtUtc, meta) {
124
+ super(`Attachment pending upload for hash: ${hash}, expires: ${expiresAtUtc}`);
125
+ this.name = "AttachmentPending";
126
+ this.hash = hash;
127
+ this.expiresAtUtc = expiresAtUtc;
128
+ this.metadata = meta;
129
+ }
130
+ };
131
+ //#endregion
132
+ //#region src/ref.ts
133
+ const REF_PATTERN = /^attachment:\/\/v(\d+):(.+)$/;
134
+ const DEFAULT_VERSION = 1;
135
+ function parseRef(ref) {
136
+ const match = REF_PATTERN.exec(ref);
137
+ if (!match) throw new InvalidAttachmentRef(ref);
138
+ return {
139
+ version: Number(match[1]),
140
+ hash: match[2]
141
+ };
142
+ }
143
+ function createRef(hash, version = DEFAULT_VERSION) {
144
+ return `attachment://v${version}:${hash}`;
145
+ }
146
+ //#endregion
147
+ //#region src/attachment-metadata.ts
148
+ function isRecord(value) {
149
+ return typeof value === "object" && value !== null && !Array.isArray(value);
150
+ }
151
+ function isAttachmentMetadata(value) {
152
+ if (!isRecord(value)) return false;
153
+ if (typeof value.mimeType !== "string") return false;
154
+ if (typeof value.fileName !== "string") return false;
155
+ if (typeof value.sizeBytes !== "number" || !Number.isFinite(value.sizeBytes) || value.sizeBytes < 0) return false;
156
+ if (value.extension !== null && typeof value.extension !== "string") return false;
157
+ if (typeof value.createdAtUtc !== "string") return false;
158
+ if (value.lastAccessedAtUtc !== void 0 && typeof value.lastAccessedAtUtc !== "string") return false;
159
+ return true;
160
+ }
161
+ //#endregion
162
+ //#region src/switchboard/build-auth-headers.ts
163
+ async function buildAuthHeaders(url, jwtHandler) {
164
+ const headers = {};
165
+ if (jwtHandler) {
166
+ const token = await jwtHandler(url);
167
+ if (token) headers["Authorization"] = `Bearer ${token}`;
168
+ }
169
+ return headers;
170
+ }
171
+ //#endregion
172
+ //#region src/switchboard/switchboard-attachment-transport.ts
173
+ var SwitchboardAttachmentTransport = class {
174
+ remoteUrl;
175
+ jwtHandler;
176
+ fetchFn;
177
+ constructor(config) {
178
+ this.remoteUrl = config.remoteUrl;
179
+ this.jwtHandler = config.jwtHandler;
180
+ this.fetchFn = (config.fetchFn ?? globalThis.fetch).bind(globalThis);
181
+ }
182
+ async fetch(hash, documentId, signal) {
183
+ const url = `${this.remoteUrl}/attachments/${hash}?documentId=${encodeURIComponent(documentId)}`;
184
+ const headers = await buildAuthHeaders(url, this.jwtHandler);
185
+ const response = await this.fetchFn(url, {
186
+ signal,
187
+ headers
188
+ });
189
+ if (response.status === 202) {
190
+ const expiresAtUtc = this.parsePendingExpiry(response);
191
+ if (!expiresAtUtc) throw new Error("Attachment fetch returned 202 with missing or malformed Attachment-Pending header");
192
+ return {
193
+ kind: "pending",
194
+ hash,
195
+ expiresAtUtc,
196
+ retryAfterMs: parseRetryAfterMs(response)
197
+ };
198
+ }
199
+ if (response.status === 404) return { kind: "not-found" };
200
+ if (!response.ok) throw new Error(`Attachment fetch failed: ${response.status} ${response.statusText}`);
201
+ const metadata = this.parseMetadataHeaders(response);
202
+ const body = response.body;
203
+ if (!body) throw new Error("Response body is null");
204
+ return {
205
+ kind: "data",
206
+ response: {
207
+ hash,
208
+ metadata,
209
+ body
210
+ }
211
+ };
212
+ }
213
+ async announce(_hash) {}
214
+ async push(hash, remote, data) {
215
+ const url = `${remote}/attachments/${hash}`;
216
+ const headers = await buildAuthHeaders(url, this.jwtHandler);
217
+ const response = await this.fetchFn(url, {
218
+ method: "PUT",
219
+ body: data,
220
+ headers,
221
+ duplex: "half"
222
+ });
223
+ if (!response.ok) throw new Error(`Attachment push failed: ${response.status} ${response.statusText}`);
224
+ }
225
+ parsePendingExpiry(response) {
226
+ const header = response.headers.get("Attachment-Pending");
227
+ if (!header) return null;
228
+ try {
229
+ const parsed = JSON.parse(header);
230
+ if (!isRecord(parsed)) return null;
231
+ if (typeof parsed.expiresAtUtc !== "string") return null;
232
+ return parsed.expiresAtUtc;
233
+ } catch {
234
+ return null;
235
+ }
236
+ }
237
+ parseMetadataHeaders(response) {
238
+ let fallbackCache;
239
+ const fallback = () => {
240
+ if (fallbackCache === void 0) fallbackCache = contentTypeFallback(response);
241
+ return fallbackCache;
242
+ };
243
+ const metaHeader = response.headers.get("Attachment-Metadata");
244
+ if (metaHeader) try {
245
+ const parsed = JSON.parse(metaHeader);
246
+ if (isRecord(parsed)) {
247
+ if (parsed.extension === void 0) parsed.extension = null;
248
+ if (parsed.createdAtUtc === void 0) parsed.createdAtUtc = fallback().createdAtUtc;
249
+ if (parsed.lastAccessedAtUtc === void 0) parsed.lastAccessedAtUtc = fallback().lastAccessedAtUtc;
250
+ }
251
+ if (isAttachmentMetadata(parsed)) return parsed;
252
+ } catch {}
253
+ return fallback();
254
+ }
255
+ };
256
+ const DEFAULT_RETRY_AFTER_MS = 5e3;
257
+ function parseRetryAfterMs(response) {
258
+ const retryAfter = response.headers.get("Retry-After");
259
+ if (!retryAfter) return DEFAULT_RETRY_AFTER_MS;
260
+ const seconds = Number(retryAfter);
261
+ if (!Number.isFinite(seconds) || seconds < 0) return DEFAULT_RETRY_AFTER_MS;
262
+ return Math.round(seconds * 1e3);
263
+ }
264
+ function contentTypeFallback(response) {
265
+ const contentLength = response.headers.get("Content-Length");
266
+ if (contentLength === null) throw new Error("Switchboard response missing both Attachment-Metadata and Content-Length headers");
267
+ const sizeBytes = Number(contentLength);
268
+ if (!Number.isInteger(sizeBytes) || sizeBytes < 0) throw new Error(`Switchboard response has invalid Content-Length header: ${JSON.stringify(contentLength)}`);
269
+ const lastModified = response.headers.get("Last-Modified");
270
+ const dateHeader = response.headers.get("Date");
271
+ const createdAtUtc = lastModified ? new Date(lastModified).toISOString() : dateHeader ? new Date(dateHeader).toISOString() : (/* @__PURE__ */ new Date()).toISOString();
272
+ return {
273
+ mimeType: response.headers.get("Content-Type") ?? "application/octet-stream",
274
+ fileName: "unknown",
275
+ sizeBytes,
276
+ extension: null,
277
+ createdAtUtc,
278
+ lastAccessedAtUtc: createdAtUtc
279
+ };
280
+ }
281
+ //#endregion
282
+ export { parseRef as a, AttachmentPending as c, InvalidAttachmentMetadata as d, InvalidAttachmentRef as f, UploadTooLarge as h, createRef as i, AttachmentTransferError as l, SizeMismatch as m, buildAuthHeaders as n, AttachmentAlreadyExists as o, ReservationNotFound as p, isAttachmentMetadata as r, AttachmentNotFound as s, SwitchboardAttachmentTransport as t, HashMismatch as u };
283
+
284
+ //# sourceMappingURL=switchboard-attachment-transport-BH3ZAmxE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"switchboard-attachment-transport-BH3ZAmxE.js","names":[],"sources":["../src/errors.ts","../src/ref.ts","../src/attachment-metadata.ts","../src/switchboard/build-auth-headers.ts","../src/switchboard/switchboard-attachment-transport.ts"],"sourcesContent":["import type { AttachmentHash, AttachmentRef } from \"@powerhousedao/reactor\";\n\n/**\n * Thrown when an attachment ref or hash is not known to the store.\n */\nexport class AttachmentNotFound extends Error {\n constructor(identifier: string) {\n super(`Attachment not found: ${identifier}`);\n this.name = \"AttachmentNotFound\";\n }\n}\n\n/**\n * Thrown when a reservation ID is not found in the reservation store.\n */\nexport class ReservationNotFound extends Error {\n constructor(reservationId: string) {\n super(`Reservation not found: ${reservationId}`);\n this.name = \"ReservationNotFound\";\n }\n}\n\n/**\n * Thrown when an attachment ref string does not match the expected format.\n */\nexport class InvalidAttachmentRef extends Error {\n constructor(ref: string) {\n super(`Invalid attachment ref: ${ref}`);\n this.name = \"InvalidAttachmentRef\";\n }\n}\n\n/** The reserve metadata field that failed validation. */\nexport type AttachmentMetadataField = \"mimeType\" | \"fileName\" | \"extension\";\n\n/**\n * Thrown when reserve metadata (MIME type, file name or extension) is not\n * acceptable. Route handlers should map this to HTTP 400 Bad Request.\n */\nexport class InvalidAttachmentMetadata extends Error {\n readonly field: AttachmentMetadataField;\n constructor(field: AttachmentMetadataField) {\n super(`Invalid attachment ${field}`);\n this.name = \"InvalidAttachmentMetadata\";\n this.field = field;\n }\n}\n\n/**\n * Thrown when an upload exceeds the configured maximum byte cap.\n * Route handlers should map this to HTTP 413 Payload Too Large.\n */\nexport class UploadTooLarge extends Error {\n readonly maxBytes: number;\n constructor(maxBytes: number) {\n super(`Upload exceeds maximum size of ${maxBytes} bytes`);\n this.name = \"UploadTooLarge\";\n this.maxBytes = maxBytes;\n }\n}\n\n/**\n * Thrown by reserve() when the claimed hash is already available in the store.\n * The caller should use err.ref directly and upload nothing -- this is the\n * dedup fast path: duplicate content never leaves the client.\n */\nexport class AttachmentAlreadyExists extends Error {\n readonly hash: AttachmentHash;\n readonly ref: AttachmentRef;\n constructor(hash: AttachmentHash, ref: AttachmentRef) {\n super(`Attachment already exists for hash: ${hash}`);\n this.name = \"AttachmentAlreadyExists\";\n this.hash = hash;\n this.ref = ref;\n }\n}\n\n/**\n * Thrown by send() when the server-computed hash of the uploaded bytes\n * does not match the hash claimed at reservation time. Nothing is committed;\n * the reservation is retained so the client can retry with correct bytes.\n */\nexport class HashMismatch extends Error {\n readonly claimed: AttachmentHash;\n readonly actual: AttachmentHash;\n constructor(claimed: AttachmentHash, actual: AttachmentHash) {\n super(`Hash mismatch: claimed ${claimed} but computed ${actual}`);\n this.name = \"HashMismatch\";\n this.claimed = claimed;\n this.actual = actual;\n }\n}\n\n/**\n * Thrown by send() when the uploaded byte count does not equal the\n * sizeBytes declared at reservation time. The handle may reject\n * mid-stream as soon as the count exceeds the declaration.\n * Nothing is committed; the reservation is retained for retry.\n *\n * \"actual\" is the byte count received from the stream before aborting --\n * it includes the chunk that crossed the declaration and can exceed bytes\n * persisted. On mid-stream aborts the true total is unknown; at least\n * \"actual\" bytes were sent.\n */\nexport class SizeMismatch extends Error {\n readonly declared: number;\n readonly actual: number;\n constructor(declared: number, actual: number) {\n super(`Size mismatch: declared ${declared} bytes but received ${actual}`);\n this.name = \"SizeMismatch\";\n this.declared = declared;\n this.actual = actual;\n }\n}\n\n/**\n * Thrown by get() when the hash is reserved by an in-flight upload and\n * bytes are not yet available anywhere. Deliberately NOT a subclass of\n * AttachmentNotFound -- callers must distinguish \"retry later\" from \"unknown\".\n * After expiresAtUtc has passed the hash reads as not found.\n *\n * metadata is populated when the reservation is local and its fields are\n * known (mimeType, fileName, sizeBytes). It is undefined when the pending\n * state is learned from a remote transport that did not supply the full\n * Attachment-Pending header (transport-pending / degraded wire case).\n */\nexport type AttachmentTransferStage =\n | \"download-target\"\n | \"switchboard-get\"\n | \"presigned-get\"\n | \"presigned-put\";\n\n/**\n * Thrown when a remote transfer step fails. Identifies the stage only; it\n * intentionally carries no URL, signature query, bucket key, or bearer\n * material so it is always safe to log or surface.\n */\nexport class AttachmentTransferError extends Error {\n readonly stage: AttachmentTransferStage;\n readonly status: number | undefined;\n\n constructor(stage: AttachmentTransferStage, status?: number) {\n super(\n status === undefined\n ? `Attachment ${stage} request failed`\n : `Attachment ${stage} request failed with status ${status}`,\n );\n this.name = \"AttachmentTransferError\";\n this.stage = stage;\n this.status = status;\n }\n}\n\nexport class AttachmentPending extends Error {\n readonly hash: AttachmentHash;\n readonly expiresAtUtc: string;\n readonly metadata:\n | {\n readonly mimeType: string;\n readonly fileName: string;\n readonly sizeBytes: number;\n }\n | undefined;\n\n constructor(\n hash: AttachmentHash,\n expiresAtUtc: string,\n meta?: { mimeType: string; fileName: string; sizeBytes: number },\n ) {\n super(\n `Attachment pending upload for hash: ${hash}, expires: ${expiresAtUtc}`,\n );\n this.name = \"AttachmentPending\";\n this.hash = hash;\n this.expiresAtUtc = expiresAtUtc;\n this.metadata = meta;\n }\n}\n","import type { AttachmentHash, AttachmentRef } from \"@powerhousedao/reactor\";\nimport { InvalidAttachmentRef } from \"./errors.js\";\n\nconst REF_PATTERN = /^attachment:\\/\\/v(\\d+):(.+)$/;\nconst DEFAULT_VERSION = 1;\n\nexport type ParsedRef = {\n version: number;\n hash: AttachmentHash;\n};\n\nexport function parseRef(ref: AttachmentRef): ParsedRef {\n const match = REF_PATTERN.exec(ref);\n if (!match) {\n throw new InvalidAttachmentRef(ref);\n }\n return {\n version: Number(match[1]),\n hash: match[2],\n };\n}\n\nexport function createRef(\n hash: AttachmentHash,\n version: number = DEFAULT_VERSION,\n): AttachmentRef {\n return `attachment://v${version}:${hash}`;\n}\n","import type { AttachmentMetadata } from \"./types.js\";\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function isAttachmentMetadata(\n value: unknown,\n): value is AttachmentMetadata {\n if (!isRecord(value)) return false;\n if (typeof value.mimeType !== \"string\") return false;\n if (typeof value.fileName !== \"string\") return false;\n if (\n typeof value.sizeBytes !== \"number\" ||\n !Number.isFinite(value.sizeBytes) ||\n value.sizeBytes < 0\n ) {\n return false;\n }\n if (value.extension !== null && typeof value.extension !== \"string\") {\n return false;\n }\n if (typeof value.createdAtUtc !== \"string\") return false;\n if (\n value.lastAccessedAtUtc !== undefined &&\n typeof value.lastAccessedAtUtc !== \"string\"\n ) {\n return false;\n }\n return true;\n}\n","import type { JwtHandler } from \"@powerhousedao/reactor\";\n\nexport async function buildAuthHeaders(\n url: string,\n jwtHandler: JwtHandler | undefined,\n): Promise<Record<string, string>> {\n const headers: Record<string, string> = {};\n if (jwtHandler) {\n const token = await jwtHandler(url);\n if (token) {\n headers[\"Authorization\"] = `Bearer ${token}`;\n }\n }\n return headers;\n}\n","import type { AttachmentHash } from \"@powerhousedao/reactor\";\nimport type { JwtHandler } from \"@powerhousedao/reactor\";\nimport type { IAttachmentTransport } from \"../interfaces.js\";\nimport { isAttachmentMetadata, isRecord } from \"../attachment-metadata.js\";\nimport type { AttachmentMetadata, TransportFetchResult } from \"../types.js\";\nimport { buildAuthHeaders } from \"./build-auth-headers.js\";\n\nexport type SwitchboardTransportConfig = {\n remoteUrl: string;\n jwtHandler?: JwtHandler;\n fetchFn?: typeof fetch;\n};\n\nexport class SwitchboardAttachmentTransport implements IAttachmentTransport {\n private readonly remoteUrl: string;\n private readonly jwtHandler?: JwtHandler;\n private readonly fetchFn: typeof fetch;\n\n constructor(config: SwitchboardTransportConfig) {\n this.remoteUrl = config.remoteUrl;\n this.jwtHandler = config.jwtHandler;\n this.fetchFn = (config.fetchFn ?? globalThis.fetch).bind(globalThis);\n }\n\n async fetch(\n hash: AttachmentHash,\n documentId: string,\n signal?: AbortSignal,\n ): Promise<TransportFetchResult> {\n const url = `${this.remoteUrl}/attachments/${hash}?documentId=${encodeURIComponent(documentId)}`;\n const headers = await buildAuthHeaders(url, this.jwtHandler);\n\n const response = await this.fetchFn(url, { signal, headers });\n\n if (response.status === 202) {\n const expiresAtUtc = this.parsePendingExpiry(response);\n if (!expiresAtUtc) {\n throw new Error(\n \"Attachment fetch returned 202 with missing or malformed Attachment-Pending header\",\n );\n }\n const retryAfterMs = parseRetryAfterMs(response);\n return { kind: \"pending\", hash, expiresAtUtc, retryAfterMs };\n }\n\n if (response.status === 404) {\n return { kind: \"not-found\" };\n }\n\n if (!response.ok) {\n throw new Error(\n `Attachment fetch failed: ${response.status} ${response.statusText}`,\n );\n }\n\n const metadata = this.parseMetadataHeaders(response);\n const body = response.body;\n if (!body) {\n throw new Error(\"Response body is null\");\n }\n\n return { kind: \"data\", response: { hash, metadata, body } };\n }\n\n async announce(_hash: AttachmentHash): Promise<void> {\n // No-op for switchboard -- data is already on the server after upload.\n }\n\n async push(\n hash: AttachmentHash,\n remote: string,\n data: ReadableStream<Uint8Array>,\n ): Promise<void> {\n const url = `${remote}/attachments/${hash}`;\n const headers = await buildAuthHeaders(url, this.jwtHandler);\n\n const response = await this.fetchFn(url, {\n method: \"PUT\",\n body: data,\n headers,\n // @ts-expect-error Node fetch requires duplex for streaming request bodies\n duplex: \"half\",\n });\n\n if (!response.ok) {\n throw new Error(\n `Attachment push failed: ${response.status} ${response.statusText}`,\n );\n }\n }\n\n private parsePendingExpiry(response: Response): string | null {\n const header = response.headers.get(\"Attachment-Pending\");\n if (!header) return null;\n try {\n const parsed: unknown = JSON.parse(header);\n if (!isRecord(parsed)) return null;\n if (typeof parsed.expiresAtUtc !== \"string\") return null;\n return parsed.expiresAtUtc;\n } catch {\n return null;\n }\n }\n\n private parseMetadataHeaders(response: Response): AttachmentMetadata {\n // Compute the fallback at most once; both the recovery path inside the\n // header parser and the outer \"no header / parse failed\" path share it.\n let fallbackCache: AttachmentMetadata | undefined;\n const fallback = (): AttachmentMetadata => {\n if (fallbackCache === undefined) {\n fallbackCache = contentTypeFallback(response);\n }\n return fallbackCache;\n };\n\n const metaHeader = response.headers.get(\"Attachment-Metadata\");\n if (metaHeader) {\n try {\n const parsed: unknown = JSON.parse(metaHeader);\n if (isRecord(parsed)) {\n if (parsed.extension === undefined) {\n parsed.extension = null;\n }\n if (parsed.createdAtUtc === undefined) {\n parsed.createdAtUtc = fallback().createdAtUtc;\n }\n if (parsed.lastAccessedAtUtc === undefined) {\n parsed.lastAccessedAtUtc = fallback().lastAccessedAtUtc;\n }\n }\n if (isAttachmentMetadata(parsed)) {\n return parsed;\n }\n } catch {\n // fall through to Content-Type fallback\n }\n }\n return fallback();\n }\n}\n\nconst DEFAULT_RETRY_AFTER_MS = 5000;\n\nfunction parseRetryAfterMs(response: Response): number {\n const retryAfter = response.headers.get(\"Retry-After\");\n if (!retryAfter) return DEFAULT_RETRY_AFTER_MS;\n const seconds = Number(retryAfter);\n if (!Number.isFinite(seconds) || seconds < 0) return DEFAULT_RETRY_AFTER_MS;\n return Math.round(seconds * 1000);\n}\n\nfunction contentTypeFallback(response: Response): AttachmentMetadata {\n const contentLength = response.headers.get(\"Content-Length\");\n if (contentLength === null) {\n throw new Error(\n \"Switchboard response missing both Attachment-Metadata and Content-Length headers\",\n );\n }\n const sizeBytes = Number(contentLength);\n if (!Number.isInteger(sizeBytes) || sizeBytes < 0) {\n throw new Error(\n `Switchboard response has invalid Content-Length header: ${JSON.stringify(contentLength)}`,\n );\n }\n // Last-Modified is the closest legitimate signal we have for an original\n // creation time when Attachment-Metadata is absent. If that's missing too,\n // fall back to the response Date header (still server-attributed). This is\n // imperfect — Last-Modified reflects the most recent change, not the\n // original upload — but unlike sizeBytes there is no zero-equivalent\n // sentinel for a date, and downstream consumers expect a value.\n const lastModified = response.headers.get(\"Last-Modified\");\n const dateHeader = response.headers.get(\"Date\");\n const createdAtUtc = lastModified\n ? new Date(lastModified).toISOString()\n : dateHeader\n ? new Date(dateHeader).toISOString()\n : new Date().toISOString();\n\n return {\n // application/octet-stream is the RFC 2046 sentinel for \"unknown binary\",\n // and \"unknown\" is a non-real filename sentinel; neither is a fabricated\n // semantic value the way Content-Length=0 would be.\n mimeType:\n response.headers.get(\"Content-Type\") ?? \"application/octet-stream\",\n fileName: \"unknown\",\n sizeBytes,\n extension: null,\n createdAtUtc,\n lastAccessedAtUtc: createdAtUtc,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ import { AttachmentRef } from "@powerhousedao/reactor";
2
+ import { Action, DocumentModelModule } from "@powerhousedao/shared/document-model";
3
+ //#region src/reference-index/types.d.ts
4
+ interface CompiledAttachmentExtractor {
5
+ extract(action: Action): AttachmentRef[];
6
+ }
7
+ interface IAttachmentSchemaCompiler {
8
+ forModuleAction(module: DocumentModelModule, actionType: string): CompiledAttachmentExtractor;
9
+ }
10
+ //#endregion
11
+ //#region src/read-models/attachment-reference/types.d.ts
12
+ interface AttachmentReferenceInput {
13
+ documentId: string;
14
+ ref: AttachmentRef;
15
+ operationId: string;
16
+ branch: string;
17
+ scope: string;
18
+ ordinal: number;
19
+ }
20
+ interface IAttachmentReferenceReader {
21
+ hasReference(documentId: string, ref: AttachmentRef): Promise<boolean>;
22
+ /** The scopes whose operations reference the attachment; empty when none. */
23
+ referencingScopes(documentId: string, ref: AttachmentRef): Promise<string[]>;
24
+ }
25
+ /**
26
+ * Pages the whole reference index, in a stable order, for a consumer that has
27
+ * to re-derive work from it on boot -- the `AttachmentReplicator`'s resume.
28
+ *
29
+ * Separate from {@link IAttachmentReferenceReader} rather than added to it:
30
+ * the reader is the authorization surface every attachment read goes through,
31
+ * and a full-table scan is a different capability with a different cost. A
32
+ * forwarding or remote reader can implement the first without the second.
33
+ */
34
+ interface IAttachmentReferenceScanner {
35
+ /**
36
+ * References after `cursor` (absent for the first page), at most `limit` of
37
+ * them, plus the cursor for the next page (absent when exhausted). The order
38
+ * is the index's own key order, so paging cannot skip or repeat a row while
39
+ * new references are being inserted behind the cursor.
40
+ */
41
+ listReferences(cursor: string | undefined, limit: number): Promise<AttachmentReferencePageResult>;
42
+ }
43
+ /** One page of {@link IAttachmentReferenceScanner.listReferences}. */
44
+ interface AttachmentReferencePageResult {
45
+ references: readonly AttachmentReferenceRow[];
46
+ nextCursor: string | undefined;
47
+ }
48
+ /** A reference as the index holds it: the document that authorizes a ref. */
49
+ interface AttachmentReferenceRow {
50
+ documentId: string;
51
+ ref: AttachmentRef;
52
+ }
53
+ interface IAttachmentReferenceWriter {
54
+ addReferences(references: readonly AttachmentReferenceInput[]): Promise<void>;
55
+ /** Deletes every reference of the documents; for a purged document. */
56
+ removeDocuments(documentIds: readonly string[]): Promise<void>;
57
+ }
58
+ //#endregion
59
+ export { IAttachmentReferenceScanner as a, IAttachmentSchemaCompiler as c, IAttachmentReferenceReader as i, AttachmentReferencePageResult as n, IAttachmentReferenceWriter as o, AttachmentReferenceRow as r, CompiledAttachmentExtractor as s, AttachmentReferenceInput as t };
60
+ export {};
61
+ //# sourceMappingURL=types-AagIcjTD.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-AagIcjTD.d.ts","names":[],"sources":["../src/reference-index/types.ts","../src/read-models/attachment-reference/types.ts"],"mappings":";;;UAMiB;EACf,QAAQ,QAAQ,SAAS;;UAGV;EACf,gBACE,QAAQ,qBACR,qBACC;;;;UCZY;EACf;EACA,KAAK;EACL;EACA;EACA;EACA;;UAGe;EACf,aAAa,oBAAoB,KAAK,gBAAgB;;EAEtD,kBAAkB,oBAAoB,KAAK,gBAAgB;;;;;;;;;;;UAY5C;;;;;;;EAOf,eACE,4BACA,gBACC,QAAQ;;;UAII;EACf,qBAAqB;EACrB;;;UAIe;EACf;EACA,KAAK;;UAGU;EACf,cAAc,qBAAqB,6BAA6B;;EAEhE,gBAAgB,iCAAiC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@powerhousedao/reactor-attachments",
3
- "version": "6.2.3-dev.6",
3
+ "version": "6.2.3-dev.60",
4
4
  "license": "AGPL-3.0-only",
5
5
  "type": "module",
6
6
  "repository": {
@@ -24,25 +24,34 @@
24
24
  "source": "./src/client.ts",
25
25
  "types": "./dist/client.d.ts",
26
26
  "import": "./dist/client.js"
27
+ },
28
+ "./replication": {
29
+ "source": "./src/replication-entry.ts",
30
+ "types": "./dist/replication.d.ts",
31
+ "import": "./dist/replication.js"
27
32
  }
28
33
  },
29
34
  "sideEffects": false,
30
35
  "dependencies": {
31
36
  "@aws-sdk/client-s3": "3.1087.0",
32
37
  "@aws-sdk/s3-request-presigner": "3.1087.0",
38
+ "@noble/hashes": "^1.8.0",
33
39
  "@powerhousedao/document-engineering": "1.40.5",
34
- "@powerhousedao/reactor": "6.2.3-dev.6",
35
- "@powerhousedao/shared": "6.2.3-dev.6",
40
+ "@powerhousedao/reactor": "6.2.3-dev.60",
41
+ "@powerhousedao/shared": "6.2.3-dev.60",
36
42
  "change-case": "5.4.4",
37
43
  "graphql": "^16",
38
44
  "kysely": "0.28.16"
39
45
  },
40
46
  "devDependencies": {
41
47
  "@electric-sql/pglite": "0.3.15",
48
+ "@types/pg": "8.16.0",
49
+ "fake-indexeddb": "6.2.5",
42
50
  "kysely-pglite-dialect": "1.2.0",
43
- "tsdown": "0.21.1",
44
- "tsx": "4.21.0",
45
- "vitest": "4.1.1"
51
+ "pg": "8.18.0",
52
+ "tsdown": "0.23.0",
53
+ "tsx": "4.23.13",
54
+ "vitest": "5.0.3"
46
55
  },
47
56
  "description": "Content-addressed attachment service for the Powerhouse reactor — decouples large binaries from the action pipeline using opaque refs.",
48
57
  "keywords": [
@@ -60,7 +69,7 @@
60
69
  "homepage": "https://academy.vetra.io/academy/Reference/Reactor/AttachmentService",
61
70
  "scripts": {
62
71
  "tsc": "tsc",
63
- "lint": "eslint",
72
+ "lint": "cd ../.. && oxlint packages/reactor-attachments",
64
73
  "build": "tsdown",
65
74
  "test": "vitest --run",
66
75
  "verify:s3-compatibility": "tsx scripts/verify-s3-compatibility.ts"
@@ -1 +0,0 @@
1
- {"version":3,"file":"null-attachment-transport-CMrO_ZKA.d.ts","names":[],"sources":["../src/errors.ts","../src/types.ts","../src/interfaces.ts","../src/targets.ts","../src/attachment-service.ts","../src/ref.ts","../src/switchboard/switchboard-attachment-transport.ts","../src/switchboard/upload-transport.ts","../src/switchboard/xhr-upload-transport.ts","../src/switchboard/remote-reservation-store.ts","../src/switchboard/remote-attachment-upload.ts","../src/switchboard/remote-attachment-upload-factory.ts","../src/switchboard/remote-attachment-store.ts","../src/switchboard/create-remote-attachment-service.ts","../src/null-attachment-transport.ts"],"mappings":"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"}