@powerhousedao/reactor-attachments 6.2.2-dev.9 → 6.2.2-staging.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.
@@ -90,16 +90,20 @@ var SizeMismatch = class extends Error {
90
90
  }
91
91
  };
92
92
  /**
93
- * Thrown by get() when the hash is reserved by an in-flight upload and
94
- * bytes are not yet available anywhere. Deliberately NOT a subclass of
95
- * AttachmentNotFound -- callers must distinguish "retry later" from "unknown".
96
- * After expiresAtUtc has passed the hash reads as not found.
97
- *
98
- * metadata is populated when the reservation is local and its fields are
99
- * known (mimeType, fileName, sizeBytes). It is undefined when the pending
100
- * state is learned from a remote transport that did not supply the full
101
- * Attachment-Pending header (transport-pending / degraded wire case).
93
+ * Thrown when a remote transfer step fails. Identifies the stage only; it
94
+ * intentionally carries no URL, signature query, bucket key, or bearer
95
+ * material so it is always safe to log or surface.
102
96
  */
97
+ var AttachmentTransferError = class extends Error {
98
+ stage;
99
+ status;
100
+ constructor(stage, status) {
101
+ super(status === void 0 ? `Attachment ${stage} request failed` : `Attachment ${stage} request failed with status ${status}`);
102
+ this.name = "AttachmentTransferError";
103
+ this.stage = stage;
104
+ this.status = status;
105
+ }
106
+ };
103
107
  var AttachmentPending = class extends Error {
104
108
  hash;
105
109
  expiresAtUtc;
@@ -113,6 +117,107 @@ var AttachmentPending = class extends Error {
113
117
  }
114
118
  };
115
119
  //#endregion
120
+ //#region src/targets.ts
121
+ function isRecord$4(value) {
122
+ return typeof value === "object" && value !== null && !Array.isArray(value);
123
+ }
124
+ function parseUrl(value) {
125
+ if (typeof value !== "string" || value.length === 0) throw new Error("Attachment target URL must be a non-empty string");
126
+ if (value.trim() !== value || hasForbiddenUrlWhitespace(value)) throw new Error("Attachment target URL must not contain raw whitespace");
127
+ let parsed;
128
+ try {
129
+ parsed = new URL(value);
130
+ } catch {
131
+ throw new Error("Attachment target URL is invalid");
132
+ }
133
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.username !== "" || parsed.password !== "") throw new Error("Attachment target URL must be HTTP(S) and must not contain credentials");
134
+ return value;
135
+ }
136
+ function parseHeaders(value) {
137
+ if (!isRecord$4(value)) throw new Error("Attachment target headers must be an object");
138
+ const headers = Object.create(null);
139
+ const headerName = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
140
+ for (const [name, headerValue] of Object.entries(value)) {
141
+ if (!headerName.test(name)) throw new Error(`Attachment target header name is invalid: ${name}`);
142
+ if (typeof headerValue !== "string" || hasForbiddenHeaderCharacter(headerValue)) throw new Error(`Attachment target header value is invalid: ${name}`);
143
+ headers[name] = headerValue;
144
+ }
145
+ return headers;
146
+ }
147
+ function hasForbiddenHeaderCharacter(value) {
148
+ for (let index = 0; index < value.length; index++) {
149
+ const code = value.charCodeAt(index);
150
+ if (code <= 8 || code >= 10 && code <= 31 || code === 127 || code > 255) return true;
151
+ }
152
+ return false;
153
+ }
154
+ function hasForbiddenUrlWhitespace(value) {
155
+ for (let index = 0; index < value.length; index++) {
156
+ const code = value.charCodeAt(index);
157
+ if (code <= 32 || code === 127) return true;
158
+ }
159
+ return false;
160
+ }
161
+ function parseExpiry(value, required) {
162
+ if (value === void 0 && !required) return void 0;
163
+ if (typeof value !== "string") throw new Error("Attachment target expiry must be an ISO 8601 UTC string");
164
+ const parsed = new Date(value);
165
+ if (Number.isNaN(parsed.getTime()) || parsed.toISOString() !== value) throw new Error("Attachment target expiry must be an ISO 8601 UTC string");
166
+ return value;
167
+ }
168
+ function parseAttachmentUploadTarget(value) {
169
+ if (!isRecord$4(value)) throw new Error("Attachment upload target must be an object");
170
+ const url = parseUrl(value.url);
171
+ const headers = parseHeaders(value.headers);
172
+ switch (value.kind) {
173
+ case "switchboard":
174
+ if (value.method !== "PUT") throw new Error("Switchboard upload target method must be PUT");
175
+ return {
176
+ kind: "switchboard",
177
+ method: "PUT",
178
+ url,
179
+ headers,
180
+ ...value.expiresAtUtc === void 0 ? {} : { expiresAtUtc: parseExpiry(value.expiresAtUtc, false) }
181
+ };
182
+ case "presigned-put":
183
+ if (value.method !== "PUT") throw new Error("Presigned upload target method must be PUT");
184
+ return {
185
+ kind: "presigned-put",
186
+ method: "PUT",
187
+ url,
188
+ headers,
189
+ expiresAtUtc: parseExpiry(value.expiresAtUtc, true)
190
+ };
191
+ default: throw new Error("Attachment upload target kind is unknown");
192
+ }
193
+ }
194
+ function parseAttachmentDownloadTarget(value) {
195
+ if (!isRecord$4(value)) throw new Error("Attachment download target must be an object");
196
+ const url = parseUrl(value.url);
197
+ const headers = parseHeaders(value.headers);
198
+ switch (value.kind) {
199
+ case "switchboard":
200
+ if (value.method !== "GET") throw new Error("Switchboard download target method must be GET");
201
+ return {
202
+ kind: "switchboard",
203
+ method: "GET",
204
+ url,
205
+ headers,
206
+ ...value.expiresAtUtc === void 0 ? {} : { expiresAtUtc: parseExpiry(value.expiresAtUtc, false) }
207
+ };
208
+ case "presigned-get":
209
+ if (value.method !== "GET") throw new Error("Presigned download target method must be GET");
210
+ return {
211
+ kind: "presigned-get",
212
+ method: "GET",
213
+ url,
214
+ headers,
215
+ expiresAtUtc: parseExpiry(value.expiresAtUtc, true)
216
+ };
217
+ default: throw new Error("Attachment download target kind is unknown");
218
+ }
219
+ }
220
+ //#endregion
116
221
  //#region src/ref.ts
117
222
  const REF_PATTERN = /^attachment:\/\/v(\d+):(.+)$/;
118
223
  const DEFAULT_VERSION = 1;
@@ -131,13 +236,15 @@ function createRef(hash, version = DEFAULT_VERSION) {
131
236
  //#region src/attachment-service.ts
132
237
  const CLIENT_HASH_PATTERN = /^[a-f0-9]{64}$/;
133
238
  var AttachmentService = class {
134
- constructor(store, reservations, uploadFactory) {
239
+ constructor(store, reservations, uploadFactory, backend) {
135
240
  this.store = store;
136
241
  this.reservations = reservations;
137
242
  this.uploadFactory = uploadFactory;
243
+ this.backend = backend;
138
244
  }
139
245
  async reserve(options) {
140
246
  if (options.clientHash !== void 0) return this.reserveHashFirst(options);
247
+ if (this.backend?.kind === "s3") throw new Error("S3 attachment reservations require a client hash");
141
248
  const reservation = await this.reservations.create(options);
142
249
  return this.uploadFactory.createUpload(reservation);
143
250
  }
@@ -145,9 +252,16 @@ var AttachmentService = class {
145
252
  const { hash } = parseRef(ref);
146
253
  return this.store.stat(hash);
147
254
  }
148
- async get(ref, signal) {
255
+ async get(ref, options) {
149
256
  const { hash } = parseRef(ref);
150
- return this.store.get(hash, signal);
257
+ const normalized = options === void 0 || options instanceof AbortSignal ? { signal: options } : options;
258
+ return normalized.documentId === void 0 ? this.store.get(hash, normalized.signal) : this.store.get(hash, normalized.signal, normalized.documentId);
259
+ }
260
+ getDownloadTarget(ref, options) {
261
+ const { hash } = parseRef(ref);
262
+ const getTarget = this.store.getDownloadTarget?.bind(this.store);
263
+ if (getTarget === void 0) return Promise.reject(/* @__PURE__ */ new Error("Download targets are not supported by this attachment store: only remote stores negotiate direct URLs"));
264
+ return getTarget(hash, options);
151
265
  }
152
266
  async reserveHashFirst(options) {
153
267
  const normalized = options.clientHash.toLowerCase();
@@ -163,9 +277,18 @@ var AttachmentService = class {
163
277
  } catch (err) {
164
278
  if (!(err instanceof AttachmentNotFound) && !(err instanceof AttachmentPending)) throw err;
165
279
  }
166
- if (existingHeader !== null && existingHeader.status === "available") throw new AttachmentAlreadyExists(normalized, createRef(normalized));
280
+ if (existingHeader !== null) {
281
+ if (this.backend?.kind === "s3") {
282
+ if (await this.backend.exists(normalized)) throw new AttachmentAlreadyExists(normalized, createRef(normalized));
283
+ } else if (existingHeader.status === "available") throw new AttachmentAlreadyExists(normalized, createRef(normalized));
284
+ }
167
285
  const reservation = await this.reservations.create(normalizedOptions);
168
- return this.uploadFactory.createUpload(reservation);
286
+ if (this.backend?.kind !== "s3") return this.uploadFactory.createUpload(reservation);
287
+ const uploadTarget = await this.backend.prepareUploadTarget(reservation);
288
+ return this.uploadFactory.createUpload({
289
+ ...reservation,
290
+ uploadTarget
291
+ });
169
292
  }
170
293
  };
171
294
  //#endregion
@@ -302,6 +425,115 @@ function contentTypeFallback$1(response) {
302
425
  };
303
426
  }
304
427
  //#endregion
428
+ //#region src/switchboard/upload-transport.ts
429
+ /**
430
+ * The default transport. Ignores `onProgress` — fetch has no upload-side
431
+ * progress event, and silence is how a transport reports that bytes are
432
+ * unobservable.
433
+ */
434
+ function createFetchUploadTransport(fetchFn) {
435
+ return (request) => fetchFn(request.url, {
436
+ method: request.method,
437
+ headers: request.headers,
438
+ body: request.body,
439
+ ...request.signal ? { signal: request.signal } : {}
440
+ });
441
+ }
442
+ //#endregion
443
+ //#region src/switchboard/xhr-upload-transport.ts
444
+ /**
445
+ * Upload transport backed by `XMLHttpRequest`, the only browser API that
446
+ * reports upload-side byte progress (`xhr.upload.onprogress`). Everything else
447
+ * about the request is identical to the fetch path.
448
+ *
449
+ * Capability detection happens **inside** the returned function, never at
450
+ * module load: this module is reachable from the client entry, which is
451
+ * executed in Node by the entrypoint tests, and Node has no
452
+ * `XMLHttpRequest`. Detecting per call also means a test can install a fake on
453
+ * `globalThis` without any module-registry reset.
454
+ */
455
+ function createXhrUploadTransport(options) {
456
+ return (request) => {
457
+ const XhrCtor = globalThis.XMLHttpRequest;
458
+ if (typeof XhrCtor !== "function") return createFetchUploadTransport((options?.fetchFn ?? globalThis.fetch).bind(globalThis))(request);
459
+ return sendWithXhr(new XhrCtor(), request);
460
+ };
461
+ }
462
+ /**
463
+ * Mirrors fetch's observable behavior closely enough that
464
+ * `RemoteAttachmentUpload` cannot tell the two apart:
465
+ *
466
+ * - `responseType` is left at `""`, so a non-JSON 422 body still reaches the
467
+ * caller's `json()` and fails there rather than being swallowed.
468
+ * - status 0 never resolves. XHR reports 0 for network failure, CORS refusal
469
+ * and abort alike; resolving it would fabricate a transfer error claiming
470
+ * the provider answered 0.
471
+ * - failures reject with fetch's error shapes: `TypeError` for network,
472
+ * `AbortError` for abort.
473
+ */
474
+ function sendWithXhr(xhr, request) {
475
+ return new Promise((resolve, reject) => {
476
+ const signal = request.signal;
477
+ let settled = false;
478
+ const abortListener = () => xhr.abort();
479
+ const detach = () => signal?.removeEventListener("abort", abortListener);
480
+ const succeed = (response) => {
481
+ if (settled) return;
482
+ settled = true;
483
+ detach();
484
+ resolve(response);
485
+ };
486
+ const fail = (error) => {
487
+ if (settled) return;
488
+ settled = true;
489
+ detach();
490
+ reject(error);
491
+ };
492
+ if (signal?.aborted) {
493
+ fail(abortError());
494
+ return;
495
+ }
496
+ xhr.open(request.method, request.url, true);
497
+ for (const [name, value] of Object.entries(request.headers)) xhr.setRequestHeader(name, value);
498
+ if (request.onProgress) xhr.upload.addEventListener("progress", (event) => {
499
+ if (!event.lengthComputable) return;
500
+ request.onProgress?.(event.loaded, event.total);
501
+ });
502
+ xhr.addEventListener("load", () => {
503
+ if (xhr.status === 0) {
504
+ fail(networkError());
505
+ return;
506
+ }
507
+ succeed(toResponse(xhr));
508
+ });
509
+ xhr.addEventListener("error", () => fail(networkError()));
510
+ xhr.addEventListener("timeout", () => fail(networkError()));
511
+ xhr.addEventListener("abort", () => fail(abortError()));
512
+ signal?.addEventListener("abort", abortListener);
513
+ xhr.send(request.body);
514
+ });
515
+ }
516
+ function toResponse(xhr) {
517
+ return {
518
+ status: xhr.status,
519
+ statusText: xhr.statusText,
520
+ ok: xhr.status >= 200 && xhr.status < 300,
521
+ json: () => {
522
+ try {
523
+ return Promise.resolve(JSON.parse(xhr.responseText));
524
+ } catch (err) {
525
+ return Promise.reject(err instanceof Error ? err : new Error(String(err)));
526
+ }
527
+ }
528
+ };
529
+ }
530
+ function networkError() {
531
+ return /* @__PURE__ */ new TypeError("Failed to fetch");
532
+ }
533
+ function abortError() {
534
+ return new DOMException("The operation was aborted", "AbortError");
535
+ }
536
+ //#endregion
305
537
  //#region src/switchboard/remote-reservation-store.ts
306
538
  function isRecord$2(value) {
307
539
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -393,7 +625,8 @@ var RemoteReservationStore = class {
393
625
  createdAtUtc: body.createdAtUtc ?? now.toISOString(),
394
626
  expiresAtUtc: body.expiresAtUtc ?? new Date(now.getTime() + 1440 * 60 * 1e3).toISOString(),
395
627
  clientHash: options.clientHash ?? null,
396
- sizeBytes: options.sizeBytes ?? null
628
+ sizeBytes: options.sizeBytes ?? null,
629
+ ...body.uploadTarget === void 0 ? {} : { uploadTarget: parseAttachmentUploadTarget(body.uploadTarget) }
397
630
  };
398
631
  }
399
632
  async get(reservationId) {
@@ -417,7 +650,8 @@ var RemoteReservationStore = class {
417
650
  createdAtUtc: parsed.createdAtUtc,
418
651
  expiresAtUtc: parsed.expiresAtUtc,
419
652
  clientHash: parsed.clientHash ?? null,
420
- sizeBytes: parsed.sizeBytes ?? null
653
+ sizeBytes: parsed.sizeBytes ?? null,
654
+ ...parsed.uploadTarget === void 0 ? {} : { uploadTarget: parseAttachmentUploadTarget(parsed.uploadTarget) }
421
655
  };
422
656
  }
423
657
  async delete(reservationId) {
@@ -442,28 +676,36 @@ var RemoteAttachmentUpload = class {
442
676
  reservationId;
443
677
  ref;
444
678
  expiresAtUtc;
679
+ uploadTarget;
680
+ reservation;
445
681
  remoteUrl;
446
682
  jwtHandler;
447
- fetchFn;
683
+ uploadTransport;
448
684
  constructor(reservation, config) {
449
685
  this.reservationId = reservation.reservationId;
450
686
  this.ref = reservation.clientHash !== null ? createRef(reservation.clientHash) : null;
451
687
  this.expiresAtUtc = reservation.expiresAtUtc;
688
+ if (reservation.uploadTarget) this.uploadTarget = reservation.uploadTarget;
689
+ this.reservation = reservation;
452
690
  this.remoteUrl = config.remoteUrl;
453
691
  this.jwtHandler = config.jwtHandler;
454
- this.fetchFn = (config.fetchFn ?? globalThis.fetch).bind(globalThis);
692
+ this.uploadTransport = config.fetchFn ? createFetchUploadTransport(config.fetchFn.bind(globalThis)) : config.uploadTransport ?? createFetchUploadTransport(globalThis.fetch.bind(globalThis));
455
693
  }
456
- async send(data) {
694
+ async send(data, options) {
695
+ if (this.uploadTarget?.kind === "presigned-put") return this.sendPresigned(this.uploadTarget, data, options);
457
696
  const url = `${this.remoteUrl}/attachments/reservations/${this.reservationId}`;
458
697
  const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
459
698
  const body = await new Response(data).blob();
460
- const response = await this.fetchFn(url, {
699
+ const response = await this.uploadTransport({
700
+ url,
461
701
  method: "PUT",
462
702
  headers: {
463
703
  ...authHeaders,
464
704
  "Content-Type": "application/octet-stream"
465
705
  },
466
- body
706
+ body,
707
+ ...options?.onProgress ? { onProgress: (loaded, total) => options.onProgress?.(loaded, total) } : {},
708
+ ...options?.signal ? { signal: options.signal } : {}
467
709
  });
468
710
  if (response.status === 422) {
469
711
  let errorBody;
@@ -481,6 +723,44 @@ var RemoteAttachmentUpload = class {
481
723
  if (!response.ok) throw new Error(`Attachment upload failed: ${response.status} ${response.statusText}`);
482
724
  return await response.json();
483
725
  }
726
+ /**
727
+ * Direct provider upload: PUT the bytes to the presigned URL with exactly
728
+ * the returned headers — never the Switchboard JWT — and treat any 2xx as
729
+ * final success with no follow-up control request. The result is
730
+ * synthesized from the hash-first reservation, which is the only path that
731
+ * can produce a presigned target.
732
+ */
733
+ async sendPresigned(target, data, options) {
734
+ if (this.reservation.clientHash === null || this.ref === null) throw new Error("Presigned upload targets require a hash-first reservation");
735
+ const body = await new Response(data).blob();
736
+ const response = await this.uploadTransport({
737
+ url: target.url,
738
+ method: target.method,
739
+ headers: { ...target.headers },
740
+ body,
741
+ ...options?.onProgress ? { onProgress: (loaded, total) => options.onProgress?.(loaded, total) } : {},
742
+ ...options?.signal ? { signal: options.signal } : {}
743
+ });
744
+ if (!response.ok) throw new AttachmentTransferError("presigned-put", response.status);
745
+ const hash = this.reservation.clientHash;
746
+ const now = (/* @__PURE__ */ new Date()).toISOString();
747
+ return {
748
+ hash,
749
+ ref: this.ref,
750
+ header: {
751
+ hash,
752
+ mimeType: this.reservation.mimeType,
753
+ fileName: this.reservation.fileName,
754
+ sizeBytes: this.reservation.sizeBytes ?? body.size,
755
+ extension: this.reservation.extension,
756
+ status: "available",
757
+ source: "local",
758
+ createdAtUtc: this.reservation.createdAtUtc || now,
759
+ lastAccessedAtUtc: now,
760
+ expiresAtUtc: null
761
+ }
762
+ };
763
+ }
484
764
  };
485
765
  //#endregion
486
766
  //#region src/switchboard/remote-attachment-upload-factory.ts
@@ -603,16 +883,70 @@ var RemoteAttachmentStore = class {
603
883
  if (!response.ok) throw new Error(`Attachment stat failed: ${response.status} ${response.statusText}`);
604
884
  return buildHeader(hash, parseMetadata(response));
605
885
  }
606
- async get(hash, signal) {
607
- return this.fetchAttachment(hash, signal);
886
+ async get(hash, signal, documentId) {
887
+ if (documentId === void 0) return this.fetchAttachment(hash, signal);
888
+ const target = await this.getDownloadTarget(hash, {
889
+ documentId,
890
+ signal
891
+ });
892
+ if (target.kind === "presigned-get") return this.fetchPresigned(hash, target, signal);
893
+ return this.fetchAttachment(hash, signal, target);
608
894
  }
609
- async fetchAttachment(hash, signal) {
610
- const url = `${this.remoteUrl}/attachments/${hash}`;
895
+ /**
896
+ * Asks Switchboard for an authorized download target. The request carries
897
+ * the JWT; the response is runtime-validated before any byte transfer.
898
+ * Public so callers can mint direct URLs (previews, share links) without
899
+ * transferring bytes; `expiresIn` requests a caller-chosen lifetime.
900
+ */
901
+ async getDownloadTarget(hash, options) {
902
+ const { documentId, expiresIn, signal } = options;
903
+ const expiry = expiresIn === void 0 ? "" : `&expiresIn=${encodeURIComponent(String(expiresIn))}`;
904
+ const url = `${this.remoteUrl}/attachments/${hash}/download-target?documentId=${encodeURIComponent(documentId)}${expiry}`;
611
905
  const headers = await buildAuthHeaders(url, this.jwtHandler);
612
906
  const response = await this.fetchFn(url, {
613
907
  signal,
614
908
  headers
615
909
  });
910
+ if (response.status === 404) throw new AttachmentNotFound(hash);
911
+ if (!response.ok) throw new AttachmentTransferError("download-target", response.status);
912
+ let body;
913
+ try {
914
+ body = await response.json();
915
+ } catch {
916
+ throw new AttachmentTransferError("download-target");
917
+ }
918
+ return parseAttachmentDownloadTarget(body);
919
+ }
920
+ /**
921
+ * Executes a presigned GET with exactly the returned headers and no JWT.
922
+ * A provider 404 means the object is missing despite available metadata
923
+ * (the accepted abandoned-upload trade-off) and surfaces as the same typed
924
+ * not-found error callers already handle.
925
+ */
926
+ async fetchPresigned(hash, target, signal) {
927
+ const response = await this.fetchFn(target.url, {
928
+ signal,
929
+ headers: { ...target.headers }
930
+ });
931
+ if (response.status === 404) throw new AttachmentNotFound(hash);
932
+ if (!response.ok) throw new AttachmentTransferError("presigned-get", response.status);
933
+ if (!response.body) throw new Error("Response body is null");
934
+ return {
935
+ header: buildHeader(hash, parseMetadata(response)),
936
+ body: response.body
937
+ };
938
+ }
939
+ async fetchAttachment(hash, signal, target) {
940
+ const url = target?.url ?? `${this.remoteUrl}/attachments/${hash}`;
941
+ const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
942
+ const headers = {
943
+ ...target?.headers,
944
+ ...authHeaders
945
+ };
946
+ const response = await this.fetchFn(url, {
947
+ signal,
948
+ headers
949
+ });
616
950
  if (response.status === 202) {
617
951
  const pending = parsePendingExpiry(response);
618
952
  if (!pending) throw new Error("Attachment fetch returned 202 with missing or malformed Attachment-Pending header");
@@ -681,6 +1015,6 @@ var NullAttachmentTransport = class {
681
1015
  }
682
1016
  };
683
1017
  //#endregion
684
- export { SizeMismatch as _, RemoteAttachmentUpload as a, AttachmentService as c, AttachmentAlreadyExists as d, AttachmentNotFound as f, ReservationNotFound as g, InvalidAttachmentRef as h, RemoteAttachmentUploadFactory as i, createRef as l, HashMismatch as m, createRemoteAttachmentService as n, RemoteReservationStore as o, AttachmentPending as p, RemoteAttachmentStore as r, SwitchboardAttachmentTransport as s, NullAttachmentTransport as t, parseRef as u, UploadTooLarge as v };
1018
+ export { UploadTooLarge as C, SizeMismatch as S, AttachmentPending as _, RemoteAttachmentUpload as a, InvalidAttachmentRef as b, createFetchUploadTransport as c, createRef as d, parseRef as f, AttachmentNotFound as g, AttachmentAlreadyExists as h, RemoteAttachmentUploadFactory as i, SwitchboardAttachmentTransport as l, parseAttachmentUploadTarget as m, createRemoteAttachmentService as n, RemoteReservationStore as o, parseAttachmentDownloadTarget as p, RemoteAttachmentStore as r, createXhrUploadTransport as s, NullAttachmentTransport as t, AttachmentService as u, AttachmentTransferError as v, ReservationNotFound as x, HashMismatch as y };
685
1019
 
686
- //# sourceMappingURL=null-attachment-transport-Drx03s02.js.map
1020
+ //# sourceMappingURL=null-attachment-transport-CrsUafCi.js.map