@adonis-agora/media 0.8.1 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/CHANGELOG.md +121 -0
  2. package/README.md +206 -0
  3. package/dist/providers/media_provider.d.ts +3 -13
  4. package/dist/providers/media_provider.d.ts.map +1 -1
  5. package/dist/providers/media_provider.js +151 -4
  6. package/dist/providers/media_provider.js.map +1 -1
  7. package/dist/src/content_type.d.ts +6 -1
  8. package/dist/src/content_type.d.ts.map +1 -1
  9. package/dist/src/content_type.js +102 -7
  10. package/dist/src/content_type.js.map +1 -1
  11. package/dist/src/define_config.d.ts +88 -5
  12. package/dist/src/define_config.d.ts.map +1 -1
  13. package/dist/src/define_config.js +7 -2
  14. package/dist/src/define_config.js.map +1 -1
  15. package/dist/src/direct_upload.d.ts +177 -0
  16. package/dist/src/direct_upload.d.ts.map +1 -0
  17. package/dist/src/direct_upload.js +299 -0
  18. package/dist/src/direct_upload.js.map +1 -0
  19. package/dist/src/direct_upload_handler.d.ts +83 -0
  20. package/dist/src/direct_upload_handler.d.ts.map +1 -0
  21. package/dist/src/direct_upload_handler.js +180 -0
  22. package/dist/src/direct_upload_handler.js.map +1 -0
  23. package/dist/src/disks/factory.d.ts +11 -3
  24. package/dist/src/disks/factory.d.ts.map +1 -1
  25. package/dist/src/disks/factory.js +5 -3
  26. package/dist/src/disks/factory.js.map +1 -1
  27. package/dist/src/disks/s3.d.ts +10 -0
  28. package/dist/src/disks/s3.d.ts.map +1 -1
  29. package/dist/src/disks/s3.js +96 -22
  30. package/dist/src/disks/s3.js.map +1 -1
  31. package/dist/src/disks/sigv4.d.ts +58 -0
  32. package/dist/src/disks/sigv4.d.ts.map +1 -0
  33. package/dist/src/disks/sigv4.js +87 -0
  34. package/dist/src/disks/sigv4.js.map +1 -0
  35. package/dist/src/disks/xml_fallback.d.ts +3 -3
  36. package/dist/src/disks/xml_fallback.js +3 -3
  37. package/dist/src/errors.d.ts +104 -0
  38. package/dist/src/errors.d.ts.map +1 -1
  39. package/dist/src/errors.js +145 -0
  40. package/dist/src/errors.js.map +1 -1
  41. package/dist/src/hls/delivery.d.ts +112 -0
  42. package/dist/src/hls/delivery.d.ts.map +1 -0
  43. package/dist/src/hls/delivery.js +118 -0
  44. package/dist/src/hls/delivery.js.map +1 -0
  45. package/dist/src/hls/playlist.d.ts +67 -0
  46. package/dist/src/hls/playlist.d.ts.map +1 -0
  47. package/dist/src/hls/playlist.js +126 -0
  48. package/dist/src/hls/playlist.js.map +1 -0
  49. package/dist/src/index.d.ts +18 -3
  50. package/dist/src/index.d.ts.map +1 -1
  51. package/dist/src/index.js +13 -2
  52. package/dist/src/index.js.map +1 -1
  53. package/dist/src/media_collection.d.ts +8 -1
  54. package/dist/src/media_collection.d.ts.map +1 -1
  55. package/dist/src/media_collection.js +23 -1
  56. package/dist/src/media_collection.js.map +1 -1
  57. package/dist/src/media_library.d.ts +32 -2
  58. package/dist/src/media_library.d.ts.map +1 -1
  59. package/dist/src/media_library.js +218 -11
  60. package/dist/src/media_library.js.map +1 -1
  61. package/dist/src/media_manager.d.ts +37 -2
  62. package/dist/src/media_manager.d.ts.map +1 -1
  63. package/dist/src/media_manager.js +54 -1
  64. package/dist/src/media_manager.js.map +1 -1
  65. package/dist/src/media_record.d.ts +22 -2
  66. package/dist/src/media_record.d.ts.map +1 -1
  67. package/dist/src/services/booted_app.d.ts +9 -0
  68. package/dist/src/services/booted_app.d.ts.map +1 -0
  69. package/dist/src/services/booted_app.js +33 -0
  70. package/dist/src/services/booted_app.js.map +1 -0
  71. package/dist/src/services/main.d.ts +12 -0
  72. package/dist/src/services/main.d.ts.map +1 -1
  73. package/dist/src/services/main.js +15 -3
  74. package/dist/src/services/main.js.map +1 -1
  75. package/dist/src/single_file_store.d.ts +4 -0
  76. package/dist/src/single_file_store.d.ts.map +1 -1
  77. package/dist/src/single_file_store.js +8 -4
  78. package/dist/src/single_file_store.js.map +1 -1
  79. package/dist/src/testing/fake_transformer.d.ts +32 -0
  80. package/dist/src/testing/fake_transformer.d.ts.map +1 -0
  81. package/dist/src/testing/fake_transformer.js +32 -0
  82. package/dist/src/testing/fake_transformer.js.map +1 -0
  83. package/dist/src/testing/index.d.ts +2 -0
  84. package/dist/src/testing/index.d.ts.map +1 -1
  85. package/dist/src/testing/index.js +1 -0
  86. package/dist/src/testing/index.js.map +1 -1
  87. package/dist/src/transformer.d.ts +111 -0
  88. package/dist/src/transformer.d.ts.map +1 -0
  89. package/dist/src/transformer.js +2 -0
  90. package/dist/src/transformer.js.map +1 -0
  91. package/dist/src/transformers/factory.d.ts +30 -0
  92. package/dist/src/transformers/factory.d.ts.map +1 -0
  93. package/dist/src/transformers/factory.js +34 -0
  94. package/dist/src/transformers/factory.js.map +1 -0
  95. package/dist/src/transformers/hls.d.ts +110 -0
  96. package/dist/src/transformers/hls.d.ts.map +1 -0
  97. package/dist/src/transformers/hls.js +111 -0
  98. package/dist/src/transformers/hls.js.map +1 -0
  99. package/dist/src/transformers/mediabunny.d.ts +5 -0
  100. package/dist/src/transformers/mediabunny.d.ts.map +1 -0
  101. package/dist/src/transformers/mediabunny.js +150 -0
  102. package/dist/src/transformers/mediabunny.js.map +1 -0
  103. package/dist/src/transformers/mediabunny_loader.d.ts +15 -0
  104. package/dist/src/transformers/mediabunny_loader.d.ts.map +1 -0
  105. package/dist/src/transformers/mediabunny_loader.js +21 -0
  106. package/dist/src/transformers/mediabunny_loader.js.map +1 -0
  107. package/dist/src/transformers/probe.d.ts +56 -0
  108. package/dist/src/transformers/probe.d.ts.map +1 -0
  109. package/dist/src/transformers/probe.js +32 -0
  110. package/dist/src/transformers/probe.js.map +1 -0
  111. package/dist/src/transformers/source_file.d.ts +20 -0
  112. package/dist/src/transformers/source_file.d.ts.map +1 -0
  113. package/dist/src/transformers/source_file.js +30 -0
  114. package/dist/src/transformers/source_file.js.map +1 -0
  115. package/dist/stubs/config/media.stub +12 -0
  116. package/package.json +11 -7
  117. package/LICENSE +0 -21
@@ -0,0 +1,299 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { publishMedia } from './diagnostics.js';
3
+ import { MimeNotAllowedError, UploadNotSupportedError, UploadPartOutOfRangeError, UploadPartSizeError, UploadPartsIncompleteError, UploadSessionExpiredError, UploadSessionNotFoundError, } from './errors.js';
4
+ import { isMultipartCapable } from './multipart.js';
5
+ /**
6
+ * Default part size: 20 MiB. Between S3's 5 MiB minimum and a reasonable payload per browser `PUT`;
7
+ * with S3's 10,000-part cap it covers files up to ~195 GiB. Larger than the proxy default (8 MiB)
8
+ * on purpose — direct parts don't occupy app memory, so the only cost of a bigger part is a bigger
9
+ * retry unit.
10
+ */
11
+ export const DEFAULT_DIRECT_PART_SIZE = 20 * 1024 * 1024;
12
+ /** S3's minimum part size (every part but the last). Enforced at `initiate` — see {@link UploadPartSizeError}. */
13
+ export const MIN_DIRECT_PART_SIZE = 5 * 1024 * 1024;
14
+ /** S3's cap on parts per multipart upload. */
15
+ export const MAX_DIRECT_PARTS = 10_000;
16
+ /**
17
+ * Default presigned part-URL lifetime (1 hour). Deliberately not sized to "the whole upload on a
18
+ * slow connection": {@link DirectUploadManager.status} re-issues fresh URLs for whatever is still
19
+ * pending, so an expired URL costs one status round-trip, not the upload.
20
+ */
21
+ const DEFAULT_PRESIGN_TTL_SECONDS = 3600;
22
+ /** Session-metadata key marking a session as a direct upload and carrying its agreed part size. */
23
+ const PART_SIZE_METADATA_KEY = 'direct:partSize';
24
+ /**
25
+ * Session-backed direct-to-S3 multipart uploads: the browser `PUT`s every byte straight to storage
26
+ * through presigned part URLs, and the app only coordinates — open, presign, collect ETags, close.
27
+ * The app's bandwidth cost is a handful of JSON round-trips regardless of file size, which is the
28
+ * entire point for video-sized files: the TUS path streams every chunk THROUGH the app (2× the
29
+ * bytes: client→app, app→S3), where this path streams none.
30
+ *
31
+ * What this adds over the raw primitives on {@link UploadManager} (`initiateDirect` & friends) is
32
+ * the **persisted session**: `uploadId`, the agreed `partSize` and every confirmed part ETag live
33
+ * in an {@link UploadSessionStore} — the same SPI the TUS engine persists its offsets in — instead
34
+ * of in the client's memory. A page reload no longer orphans the upload: the client asks
35
+ * {@link status} and gets back the confirmed ETags plus fresh URLs for what is missing.
36
+ *
37
+ * Framework-free, like {@link ResumableUploadManager}: the `DirectUploadHandler` maps it onto HTTP
38
+ * and the AdonisJS provider mounts that. `MediaManager.completeDirectUploadToLibrary` bridges a
39
+ * finished session into the media library through `attachExisting` — where the collection's
40
+ * whitelist checks the REAL bytes, exactly like every other upload path.
41
+ */
42
+ export class DirectUploadManager {
43
+ storage;
44
+ sessions;
45
+ collections;
46
+ partSize;
47
+ presignTtlSeconds;
48
+ ttlSeconds;
49
+ newId;
50
+ now;
51
+ emitDiagnostics;
52
+ constructor(options) {
53
+ this.storage = options.storage;
54
+ this.sessions = options.sessions;
55
+ this.collections = options.collections;
56
+ this.partSize = options.partSize ?? DEFAULT_DIRECT_PART_SIZE;
57
+ this.presignTtlSeconds = options.presignTtlSeconds ?? DEFAULT_PRESIGN_TTL_SECONDS;
58
+ this.ttlSeconds = options.sessionTtlSeconds;
59
+ this.newId = options.idGenerator ?? (() => randomUUID());
60
+ this.now = options.clock ?? (() => new Date());
61
+ this.emitDiagnostics = options.emitDiagnostics ?? true;
62
+ }
63
+ /**
64
+ * Open a direct upload: validate everything that can be validated without bytes, start the native
65
+ * multipart upload, persist the session, and presign every part URL in one batch (presigning is
66
+ * local computation — see `sigv4.ts` — so a 10,000-part batch costs no S3 round-trips).
67
+ */
68
+ async initiate(input) {
69
+ const diskName = input.disk ?? this.storage.defaultDisk;
70
+ const disk = this.storage.disk(diskName);
71
+ if (!isMultipartCapable(disk))
72
+ throw new UploadNotSupportedError(diskName);
73
+ if (typeof this.sessions.addPart !== 'function' ||
74
+ typeof this.sessions.listParts !== 'function') {
75
+ throw new UploadNotSupportedError(diskName, 'direct upload part recording');
76
+ }
77
+ if (!Number.isInteger(input.size) || input.size <= 0) {
78
+ throw new RangeError(`Direct upload size must be a positive integer, got ${input.size}`);
79
+ }
80
+ const partSize = input.partSize ?? this.partSize;
81
+ if (!Number.isInteger(partSize) || partSize < MIN_DIRECT_PART_SIZE) {
82
+ throw new UploadPartSizeError(`${partSize} bytes is below S3's ${MIN_DIRECT_PART_SIZE}-byte minimum`);
83
+ }
84
+ const totalParts = Math.max(1, Math.ceil(input.size / partSize));
85
+ if (totalParts > MAX_DIRECT_PARTS) {
86
+ throw new UploadPartSizeError(`${input.size} bytes at ${partSize} bytes/part needs ${totalParts} parts, over S3's cap of ${MAX_DIRECT_PARTS} — increase partSize`);
87
+ }
88
+ // The cheapest possible rejection, before the multipart upload even opens. Only the DECLARED
89
+ // type can be checked here (there are no bytes yet); the collection config stays the single
90
+ // source of truth, and attach-time validation of the real content remains the final barrier.
91
+ if (input.collection !== undefined && this.collections !== undefined) {
92
+ const accepts = this.collections.get(input.collection).acceptsMimeTypes;
93
+ if (accepts && input.contentType !== undefined && !accepts.includes(input.contentType)) {
94
+ throw new MimeNotAllowedError(input.collection, input.contentType);
95
+ }
96
+ }
97
+ const putOptions = input.contentType !== undefined || input.visibility !== undefined
98
+ ? {
99
+ ...(input.contentType !== undefined ? { contentType: input.contentType } : {}),
100
+ ...(input.visibility !== undefined ? { visibility: input.visibility } : {}),
101
+ }
102
+ : undefined;
103
+ const { uploadId } = await disk.createMultipartUpload(input.key, putOptions);
104
+ const createdAt = this.now();
105
+ const session = await this.sessions.create({
106
+ id: this.newId(),
107
+ disk: diskName,
108
+ key: input.key,
109
+ contentType: input.contentType,
110
+ size: input.size,
111
+ offset: 0,
112
+ parts: 0,
113
+ multipartUploadId: uploadId,
114
+ createdAt,
115
+ ...(this.ttlSeconds !== undefined
116
+ ? { expiresAt: new Date(createdAt.getTime() + this.ttlSeconds * 1000) }
117
+ : {}),
118
+ metadata: { ...input.metadata, [PART_SIZE_METADATA_KEY]: String(partSize) },
119
+ });
120
+ const parts = await this.#presignParts(disk, session, Array.from({ length: totalParts }, (_, index) => index + 1));
121
+ this.emit('upload.start', {
122
+ id: session.id,
123
+ disk: diskName,
124
+ key: input.key,
125
+ mode: 'direct',
126
+ size: input.size,
127
+ contentType: input.contentType,
128
+ });
129
+ return {
130
+ id: session.id,
131
+ key: session.key,
132
+ disk: diskName,
133
+ partSize,
134
+ size: input.size,
135
+ totalParts,
136
+ parts,
137
+ expiresAt: session.expiresAt,
138
+ };
139
+ }
140
+ /**
141
+ * Record one part the client finished `PUT`ing (its S3 `ETag` response header). This is what makes
142
+ * the session resumable: an ETag the server holds survives any client-side loss of state. Safe to
143
+ * retry — confirming the same part again overwrites its ETag.
144
+ */
145
+ async confirmPart(id, part) {
146
+ const session = await this.#require(id);
147
+ const { partSize, totalParts } = this.#geometry(session);
148
+ if (!Number.isInteger(part.partNumber) || part.partNumber < 1 || part.partNumber > totalParts) {
149
+ throw new UploadPartOutOfRangeError(part.partNumber, totalParts);
150
+ }
151
+ await this.sessions.addPart?.(id, { partNumber: part.partNumber, etag: part.etag });
152
+ // Recompute progress from what is actually recorded (parts confirm in any order and in
153
+ // parallel), so offset means "bytes safely on S3" rather than a linear cursor.
154
+ const confirmed = (await this.sessions.listParts?.(id)) ?? [];
155
+ session.parts = confirmed.length;
156
+ session.offset = confirmed.reduce((sum, recorded) => sum + this.#partBytes(session, partSize, totalParts, recorded.partNumber), 0);
157
+ await this.sessions.update(session);
158
+ this.emit('upload.progress', {
159
+ id: session.id,
160
+ offset: session.offset,
161
+ parts: session.parts,
162
+ size: session.size,
163
+ });
164
+ return { offset: session.offset, completedParts: confirmed.length };
165
+ }
166
+ /**
167
+ * Where the upload stands, framed for resumption: the ETags already confirmed and a FRESH
168
+ * presigned URL for every part still missing. After a page reload the client calls this, slices
169
+ * the file with the returned `partSize`, uploads only `pendingParts`, and completes.
170
+ */
171
+ async status(id) {
172
+ const session = await this.#require(id);
173
+ const disk = this.storage.disk(session.disk);
174
+ if (!isMultipartCapable(disk))
175
+ throw new UploadNotSupportedError(session.disk);
176
+ const { partSize, totalParts } = this.#geometry(session);
177
+ const completedParts = [...((await this.sessions.listParts?.(id)) ?? [])].sort((a, b) => a.partNumber - b.partNumber);
178
+ const confirmedNumbers = new Set(completedParts.map((part) => part.partNumber));
179
+ const pendingNumbers = Array.from({ length: totalParts }, (_, index) => index + 1).filter((partNumber) => !confirmedNumbers.has(partNumber));
180
+ return {
181
+ id: session.id,
182
+ key: session.key,
183
+ disk: session.disk,
184
+ partSize,
185
+ size: session.size,
186
+ totalParts,
187
+ contentType: session.contentType,
188
+ completedParts,
189
+ pendingParts: await this.#presignParts(disk, session, pendingNumbers),
190
+ expiresAt: session.expiresAt,
191
+ };
192
+ }
193
+ /**
194
+ * Assemble the parts into the final object and close the session. `parts` supplements what the
195
+ * session already holds (a client may hand over ETags it never got to confirm — e.g. it raced
196
+ * completion against its last confirms); on overlap the caller's ETag wins. Every part number in
197
+ * `1..totalParts` must be accounted for, or this throws {@link UploadPartsIncompleteError} naming
198
+ * the gaps — BEFORE the S3 round-trip that would report an opaque `InvalidPart`.
199
+ *
200
+ * This is the raw primitive (the object exists, no media record). To land in the library, use
201
+ * `MediaManager.completeDirectUploadToLibrary`, which chains `attachExisting` — collection
202
+ * whitelist against the real bytes included.
203
+ */
204
+ async complete(id, parts = []) {
205
+ const session = await this.#require(id);
206
+ const disk = this.storage.disk(session.disk);
207
+ if (!isMultipartCapable(disk))
208
+ throw new UploadNotSupportedError(session.disk);
209
+ const { totalParts } = this.#geometry(session);
210
+ const byNumber = new Map();
211
+ for (const recorded of (await this.sessions.listParts?.(id)) ?? []) {
212
+ byNumber.set(recorded.partNumber, recorded);
213
+ }
214
+ for (const given of parts)
215
+ byNumber.set(given.partNumber, given);
216
+ const missing = Array.from({ length: totalParts }, (_, index) => index + 1).filter((partNumber) => !byNumber.has(partNumber));
217
+ if (missing.length > 0)
218
+ throw new UploadPartsIncompleteError(id, missing);
219
+ const assembled = [...byNumber.values()].sort((a, b) => a.partNumber - b.partNumber);
220
+ await disk.completeMultipartUpload(session.key, session.multipartUploadId, assembled);
221
+ await this.sessions.delete(id);
222
+ this.emit('upload.complete', { id: session.id, disk: session.disk, key: session.key });
223
+ return { key: session.key, disk: session.disk, size: session.size };
224
+ }
225
+ /**
226
+ * Discard an in-flight upload: abort the native multipart upload (S3 frees the stored parts and
227
+ * stops charging for them) and drop the session. The S3 abort is best-effort — a bucket lifecycle
228
+ * rule may have already reaped the upload (`NoSuchUpload`), and a session that can no longer be
229
+ * aborted remotely must still be deletable locally.
230
+ */
231
+ async abort(id) {
232
+ const session = await this.sessions.get(id);
233
+ if (!session)
234
+ return;
235
+ const disk = this.storage.disk(session.disk);
236
+ if (session.multipartUploadId && isMultipartCapable(disk)) {
237
+ await disk.abortMultipartUpload(session.key, session.multipartUploadId).catch(() => { });
238
+ }
239
+ await this.sessions.delete(id);
240
+ this.emit('upload.abort', { id: session.id, disk: session.disk, key: session.key });
241
+ }
242
+ /** In-progress direct sessions, optionally filtered. Empty when the store cannot enumerate. */
243
+ async list(filter) {
244
+ const sessions = (await this.sessions.list?.(filter)) ?? [];
245
+ return sessions.filter((session) => session.metadata?.[PART_SIZE_METADATA_KEY] !== undefined);
246
+ }
247
+ /** Presign a `PUT` URL for each given part number, in one local batch. */
248
+ async #presignParts(disk, session, partNumbers) {
249
+ const urls = [];
250
+ for (const partNumber of partNumbers) {
251
+ urls.push({
252
+ partNumber,
253
+ url: await disk.presignUploadPart(session.key, session.multipartUploadId, partNumber, this.presignTtlSeconds),
254
+ });
255
+ }
256
+ return urls;
257
+ }
258
+ /** The agreed slicing for a session, recovered from its persisted metadata. */
259
+ #geometry(session) {
260
+ const partSize = Number(session.metadata?.[PART_SIZE_METADATA_KEY]);
261
+ const size = session.size;
262
+ return { partSize, totalParts: Math.max(1, Math.ceil(size / partSize)) };
263
+ }
264
+ /** Byte length of one part: `partSize`, except the last part which takes the remainder. */
265
+ #partBytes(session, partSize, totalParts, partNumber) {
266
+ const size = session.size;
267
+ return partNumber === totalParts ? size - (totalParts - 1) * partSize : partSize;
268
+ }
269
+ /**
270
+ * Load a session, requiring it to be a DIRECT one: the store may be shared with the TUS engine
271
+ * (the Lucid driver even shares tables), so a session lacking the direct part-size marker — or a
272
+ * declared size, or a native upload id — is "not found" here, never misinterpreted. An expired
273
+ * session is treated as gone: native upload aborted, record deleted,
274
+ * {@link UploadSessionExpiredError} thrown.
275
+ */
276
+ async #require(id) {
277
+ const session = await this.sessions.get(id);
278
+ if (!session ||
279
+ session.multipartUploadId === undefined ||
280
+ session.size === undefined ||
281
+ !Number.isFinite(Number(session.metadata?.[PART_SIZE_METADATA_KEY]))) {
282
+ throw new UploadSessionNotFoundError(id);
283
+ }
284
+ if (session.expiresAt && session.expiresAt.getTime() <= this.now().getTime()) {
285
+ const disk = this.storage.disk(session.disk);
286
+ if (isMultipartCapable(disk)) {
287
+ await disk.abortMultipartUpload(session.key, session.multipartUploadId).catch(() => { });
288
+ }
289
+ await this.sessions.delete(id);
290
+ throw new UploadSessionExpiredError(id);
291
+ }
292
+ return session;
293
+ }
294
+ emit(event, payload) {
295
+ if (this.emitDiagnostics)
296
+ publishMedia(event, payload);
297
+ }
298
+ }
299
+ //# sourceMappingURL=direct_upload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"direct_upload.js","sourceRoot":"","sources":["../../src/direct_upload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,yBAAyB,EACzB,mBAAmB,EACnB,0BAA0B,EAC1B,yBAAyB,EACzB,0BAA0B,GAC3B,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AASpD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AACzD,kHAAkH;AAClH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AACpD,8CAA8C;AAC9C,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC;AACvC;;;;GAIG;AACH,MAAM,2BAA2B,GAAG,IAAI,CAAC;AAEzC,mGAAmG;AACnG,MAAM,sBAAsB,GAAG,iBAAiB,CAAC;AA0FjD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,mBAAmB;IACb,OAAO,CAAiB;IACxB,QAAQ,CAAqB;IAC7B,WAAW,CAAsC;IACjD,QAAQ,CAAS;IACjB,iBAAiB,CAAS;IAC1B,UAAU,CAAqB;IAC/B,KAAK,CAAe;IACpB,GAAG,CAAa;IAChB,eAAe,CAAU;IAE1C,YAAY,OAAmC;QAC7C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QACvC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,wBAAwB,CAAC;QAC7D,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,IAAI,2BAA2B,CAAC;QAClF,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,iBAAiB,CAAC;QAC5C,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,WAAW,IAAI,CAAC,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QAC/C,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,IAAI,CAAC;IACzD,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,QAAQ,CAAC,KAAgC;QAC7C,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC;QACxD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,uBAAuB,CAAC,QAAQ,CAAC,CAAC;QAC3E,IACE,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,KAAK,UAAU;YAC3C,OAAO,IAAI,CAAC,QAAQ,CAAC,SAAS,KAAK,UAAU,EAC7C,CAAC;YACD,MAAM,IAAI,uBAAuB,CAAC,QAAQ,EAAE,8BAA8B,CAAC,CAAC;QAC9E,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC;YACrD,MAAM,IAAI,UAAU,CAAC,sDAAsD,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3F,CAAC;QAED,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC;QACjD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,oBAAoB,EAAE,CAAC;YACnE,MAAM,IAAI,mBAAmB,CAC3B,GAAG,QAAQ,wBAAwB,oBAAoB,eAAe,CACvE,CAAC;QACJ,CAAC;QACD,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC;QACjE,IAAI,UAAU,GAAG,gBAAgB,EAAE,CAAC;YAClC,MAAM,IAAI,mBAAmB,CAC3B,GAAG,KAAK,CAAC,IAAI,aAAa,QAAQ,qBAAqB,UAAU,4BAA4B,gBAAgB,sBAAsB,CACpI,CAAC;QACJ,CAAC;QAED,6FAA6F;QAC7F,4FAA4F;QAC5F,6FAA6F;QAC7F,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;YACrE,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,gBAAgB,CAAC;YACxE,IAAI,OAAO,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC;gBACvF,MAAM,IAAI,mBAAmB,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;YACrE,CAAC;QACH,CAAC;QAED,MAAM,UAAU,GACd,KAAK,CAAC,WAAW,KAAK,SAAS,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS;YAC/D,CAAC,CAAC;gBACE,GAAG,CAAC,KAAK,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC9E,GAAG,CAAC,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC5E;YACH,CAAC,CAAC,SAAS,CAAC;QAChB,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QAE7E,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;YACzC,EAAE,EAAE,IAAI,CAAC,KAAK,EAAE;YAChB,IAAI,EAAE,QAAQ;YACd,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,MAAM,EAAE,CAAC;YACT,KAAK,EAAE,CAAC;YACR,iBAAiB,EAAE,QAAQ;YAC3B,SAAS;YACT,GAAG,CAAC,IAAI,CAAC,UAAU,KAAK,SAAS;gBAC/B,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,EAAE;gBACvE,CAAC,CAAC,EAAE,CAAC;YACP,QAAQ,EAAE,EAAE,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE;SAC5E,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,aAAa,CACpC,IAAI,EACJ,OAAO,EACP,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,CAC5D,CAAC;QAEF,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE;YACxB,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,IAAI,EAAE,QAAQ;YACd,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,WAAW,EAAE,KAAK,CAAC,WAAW;SAC/B,CAAC,CAAC;QAEH,OAAO;YACL,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,IAAI,EAAE,QAAQ;YACd,QAAQ;YACR,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,UAAU;YACV,KAAK;YACL,SAAS,EAAE,OAAO,CAAC,SAAS;SAC7B,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,WAAW,CACf,EAAU,EACV,IAAmB;QAEnB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACxC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACzD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,UAAU,GAAG,CAAC,IAAI,IAAI,CAAC,UAAU,GAAG,UAAU,EAAE,CAAC;YAC9F,MAAM,IAAI,yBAAyB,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QACnE,CAAC;QAED,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAEpF,uFAAuF;QACvF,+EAA+E;QAC/E,MAAM,SAAS,GAAG,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9D,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC;QACjC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAC/B,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC5F,CAAC,CACF,CAAC;QACF,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAEpC,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE;YAC3B,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,IAAI,EAAE,OAAO,CAAC,IAAI;SACnB,CAAC,CAAC;QACH,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;IACtE,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACxC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,uBAAuB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC/E,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAEzD,MAAM,cAAc,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAC5E,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CACtC,CAAC;QACF,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAChF,MAAM,cAAc,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,MAAM,CACvF,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,gBAAgB,CAAC,GAAG,CAAC,UAAU,CAAC,CAClD,CAAC;QAEF,OAAO;YACL,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,QAAQ;YACR,IAAI,EAAE,OAAO,CAAC,IAAc;YAC5B,UAAU;YACV,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,cAAc;YACd,YAAY,EAAE,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,cAAc,CAAC;YACrE,SAAS,EAAE,OAAO,CAAC,SAAS;SAC7B,CAAC;IACJ,CAAC;IAED;;;;;;;;;;OAUG;IACH,KAAK,CAAC,QAAQ,CACZ,EAAU,EACV,QAAyB,EAAE;QAE3B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACxC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,uBAAuB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC/E,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAE/C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;QAClD,KAAK,MAAM,QAAQ,IAAI,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YACnE,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC9C,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,KAAK;YAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAEjE,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,MAAM,CAChF,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,CAC1C,CAAC;QACF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,0BAA0B,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QAE1E,MAAM,SAAS,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC;QACrF,MAAM,IAAI,CAAC,uBAAuB,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,iBAA2B,EAAE,SAAS,CAAC,CAAC;QAChG,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAE/B,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QACvF,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,IAAc,EAAE,CAAC;IAChF,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,KAAK,CAAC,EAAU;QACpB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5C,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,OAAO,CAAC,iBAAiB,IAAI,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1D,MAAM,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC1F,CAAC;QACD,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACtF,CAAC;IAED,+FAA+F;IAC/F,KAAK,CAAC,IAAI,CAAC,MAAgC;QACzC,MAAM,QAAQ,GAAG,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QAC5D,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,sBAAsB,CAAC,KAAK,SAAS,CAAC,CAAC;IAChG,CAAC;IAED,0EAA0E;IAC1E,KAAK,CAAC,aAAa,CACjB,IAAyB,EACzB,OAAsB,EACtB,WAAqB;QAErB,MAAM,IAAI,GAA0B,EAAE,CAAC;QACvC,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;YACrC,IAAI,CAAC,IAAI,CAAC;gBACR,UAAU;gBACV,GAAG,EAAE,MAAM,IAAI,CAAC,iBAAiB,CAC/B,OAAO,CAAC,GAAG,EACX,OAAO,CAAC,iBAA2B,EACnC,UAAU,EACV,IAAI,CAAC,iBAAiB,CACvB;aACF,CAAC,CAAC;QACL,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,+EAA+E;IAC/E,SAAS,CAAC,OAAsB;QAC9B,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,sBAAsB,CAAC,CAAC,CAAC;QACpE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAc,CAAC;QACpC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAC,EAAE,CAAC;IAC3E,CAAC;IAED,2FAA2F;IAC3F,UAAU,CACR,OAAsB,EACtB,QAAgB,EAChB,UAAkB,EAClB,UAAkB;QAElB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAc,CAAC;QACpC,OAAO,UAAU,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,UAAU,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;IACnF,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,QAAQ,CAAC,EAAU;QACvB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC5C,IACE,CAAC,OAAO;YACR,OAAO,CAAC,iBAAiB,KAAK,SAAS;YACvC,OAAO,CAAC,IAAI,KAAK,SAAS;YAC1B,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,sBAAsB,CAAC,CAAC,CAAC,EACpE,CAAC;YACD,MAAM,IAAI,0BAA0B,CAAC,EAAE,CAAC,CAAC;QAC3C,CAAC;QACD,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC;YAC7E,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC7C,IAAI,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC7B,MAAM,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YAC1F,CAAC;YACD,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC/B,MAAM,IAAI,yBAAyB,CAAC,EAAE,CAAC,CAAC;QAC1C,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,IAAI,CACV,KAAQ,EACR,OAA8C;QAE9C,IAAI,IAAI,CAAC,eAAe;YAAE,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACzD,CAAC;CACF"}
@@ -0,0 +1,83 @@
1
+ import type { DirectUploadManager } from './direct_upload.js';
2
+ import type { MultipartPart } from './types.js';
3
+ /**
4
+ * A web-framework-neutral direct-upload request, one variant per lifecycle step (the provider maps
5
+ * an AdonisJS `HttpContext` to this; any other framework maps its own request the same way).
6
+ */
7
+ export type DirectUploadRequest = {
8
+ action: 'initiate';
9
+ /** Name of the file being uploaded — feeds `keyFor`, never used as a key verbatim. */
10
+ fileName: string;
11
+ /** Total size in bytes. */
12
+ size: number;
13
+ contentType?: string | undefined;
14
+ /** Extra metadata persisted on the session. */
15
+ metadata?: Record<string, string> | undefined;
16
+ } | {
17
+ action: 'status';
18
+ uploadId: string;
19
+ } | {
20
+ action: 'confirm-part';
21
+ uploadId: string;
22
+ partNumber: number;
23
+ etag: string;
24
+ } | {
25
+ action: 'complete';
26
+ uploadId: string;
27
+ parts?: MultipartPart[] | undefined;
28
+ } | {
29
+ action: 'abort';
30
+ uploadId: string;
31
+ };
32
+ /** A web-framework-neutral response: a status code and a JSON-serializable body. */
33
+ export interface DirectUploadResponse {
34
+ status: number;
35
+ body?: unknown;
36
+ }
37
+ export interface DirectUploadHandlerOptions {
38
+ manager: DirectUploadManager;
39
+ /** Disk uploads land on. Defaults to the manager's default disk. */
40
+ disk?: string | undefined;
41
+ /**
42
+ * Collection this endpoint uploads into. Forwarded to the manager's `initiate`, whose MIME gate
43
+ * (against the registry the manager holds) rejects a wrong declared type with `415` before the
44
+ * multipart upload opens. The collection config stays the single source of truth.
45
+ */
46
+ collection?: string | undefined;
47
+ /** Reject initiations whose `size` exceeds this many bytes (`413`). */
48
+ maxSize?: number | undefined;
49
+ /** Compute the final object key. Default: `uploads/<token>/<fileName>`. */
50
+ keyFor?: ((fileName: string, token: string, metadata: Record<string, string>) => string) | undefined;
51
+ idGenerator?: (() => string) | undefined;
52
+ }
53
+ /**
54
+ * HTTP half of the session-backed direct upload flow, mirroring {@link TusUploadHandler}: the core
55
+ * ({@link DirectUploadManager}) never sees HTTP, this class never sees a framework, and a thin
56
+ * AdonisJS route adapter maps `HttpContext` to {@link DirectUploadRequest}/{@link DirectUploadResponse}.
57
+ *
58
+ * The endpoint surface it expects to be mounted on (see the provider's `uploads.direct.routes`):
59
+ *
60
+ * - `POST /` → `initiate` — `201` + session id, part size, presigned URLs
61
+ * - `GET /:id` → `status` — confirmed ETags + fresh URLs for pending parts
62
+ * - `POST /:id/parts/:partNumber` → `confirm-part` — record one uploaded part's ETag
63
+ * - `POST /:id/complete` → `complete` — assemble; `409` names any missing parts
64
+ * - `DELETE /:id` → `abort`
65
+ *
66
+ * **This handler performs NO authorization** — the same split as `TusUploadHandler` and
67
+ * `MediaDeliveryHandler`. Who may upload, and into whose namespace the key resolves, are questions
68
+ * only the app can answer: guard the route, and resolve the key server-side via {@link keyFor}
69
+ * (the client sends a `fileName`, never a key).
70
+ */
71
+ export declare class DirectUploadHandler {
72
+ private readonly manager;
73
+ private readonly disk;
74
+ private readonly collection;
75
+ private readonly maxSize;
76
+ private readonly keyFor;
77
+ private readonly newId;
78
+ constructor(options: DirectUploadHandlerOptions);
79
+ handle(req: DirectUploadRequest): Promise<DirectUploadResponse>;
80
+ /** Map manager errors onto HTTP statuses; anything unexpected keeps propagating. */
81
+ private mapError;
82
+ }
83
+ //# sourceMappingURL=direct_upload_handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"direct_upload_handler.d.ts","sourceRoot":"","sources":["../../src/direct_upload_handler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAU9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAC3B;IACE,MAAM,EAAE,UAAU,CAAC;IACnB,sFAAsF;IACtF,QAAQ,EAAE,MAAM,CAAC;IACjB,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;CAC/C,GACD;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,MAAM,EAAE,cAAc,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9E;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,aAAa,EAAE,GAAG,SAAS,CAAA;CAAE,GAC7E;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1C,oFAAoF;AACpF,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,mBAAmB,CAAC;IAC7B,oEAAoE;IACpE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,2EAA2E;IAC3E,MAAM,CAAC,EACH,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC,GAC/E,SAAS,CAAC;IACd,WAAW,CAAC,EAAE,CAAC,MAAM,MAAM,CAAC,GAAG,SAAS,CAAC;CAC1C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;IAC1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8D;IACrF,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;gBAEzB,OAAO,EAAE,0BAA0B;IASzC,MAAM,CAAC,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IA2HrE,oFAAoF;IACpF,OAAO,CAAC,QAAQ;CA4BjB"}
@@ -0,0 +1,180 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { MimeNotAllowedError, UploadNotSupportedError, UploadPartOutOfRangeError, UploadPartSizeError, UploadPartsIncompleteError, UploadSessionExpiredError, UploadSessionNotFoundError, } from './errors.js';
3
+ /**
4
+ * HTTP half of the session-backed direct upload flow, mirroring {@link TusUploadHandler}: the core
5
+ * ({@link DirectUploadManager}) never sees HTTP, this class never sees a framework, and a thin
6
+ * AdonisJS route adapter maps `HttpContext` to {@link DirectUploadRequest}/{@link DirectUploadResponse}.
7
+ *
8
+ * The endpoint surface it expects to be mounted on (see the provider's `uploads.direct.routes`):
9
+ *
10
+ * - `POST /` → `initiate` — `201` + session id, part size, presigned URLs
11
+ * - `GET /:id` → `status` — confirmed ETags + fresh URLs for pending parts
12
+ * - `POST /:id/parts/:partNumber` → `confirm-part` — record one uploaded part's ETag
13
+ * - `POST /:id/complete` → `complete` — assemble; `409` names any missing parts
14
+ * - `DELETE /:id` → `abort`
15
+ *
16
+ * **This handler performs NO authorization** — the same split as `TusUploadHandler` and
17
+ * `MediaDeliveryHandler`. Who may upload, and into whose namespace the key resolves, are questions
18
+ * only the app can answer: guard the route, and resolve the key server-side via {@link keyFor}
19
+ * (the client sends a `fileName`, never a key).
20
+ */
21
+ export class DirectUploadHandler {
22
+ manager;
23
+ disk;
24
+ collection;
25
+ maxSize;
26
+ keyFor;
27
+ newId;
28
+ constructor(options) {
29
+ this.manager = options.manager;
30
+ this.disk = options.disk;
31
+ this.collection = options.collection;
32
+ this.maxSize = options.maxSize;
33
+ this.keyFor = options.keyFor ?? ((fileName, token) => `uploads/${token}/${fileName}`);
34
+ this.newId = options.idGenerator ?? (() => randomUUID());
35
+ }
36
+ async handle(req) {
37
+ switch (req.action) {
38
+ case 'initiate': {
39
+ if (typeof req.fileName !== 'string' || req.fileName.length === 0) {
40
+ return { status: 400, body: { error: 'fileName is required' } };
41
+ }
42
+ if (!Number.isInteger(req.size) || req.size <= 0) {
43
+ return { status: 400, body: { error: 'size must be a positive integer' } };
44
+ }
45
+ if (this.maxSize !== undefined && req.size > this.maxSize) {
46
+ return {
47
+ status: 413,
48
+ body: { error: `Upload exceeds the maximum size of ${this.maxSize} bytes` },
49
+ };
50
+ }
51
+ const metadata = req.metadata ?? {};
52
+ try {
53
+ const created = await this.manager.initiate({
54
+ key: this.keyFor(req.fileName, this.newId(), metadata),
55
+ size: req.size,
56
+ contentType: req.contentType,
57
+ metadata,
58
+ ...(this.disk !== undefined ? { disk: this.disk } : {}),
59
+ ...(this.collection !== undefined ? { collection: this.collection } : {}),
60
+ });
61
+ return {
62
+ status: 201,
63
+ body: {
64
+ id: created.id,
65
+ key: created.key,
66
+ disk: created.disk,
67
+ partSize: created.partSize,
68
+ size: created.size,
69
+ totalParts: created.totalParts,
70
+ parts: created.parts,
71
+ ...(created.expiresAt !== undefined
72
+ ? { expiresAt: created.expiresAt.toISOString() }
73
+ : {}),
74
+ },
75
+ };
76
+ }
77
+ catch (err) {
78
+ return this.mapError(err);
79
+ }
80
+ }
81
+ case 'status': {
82
+ try {
83
+ const status = await this.manager.status(req.uploadId);
84
+ return {
85
+ status: 200,
86
+ body: {
87
+ id: status.id,
88
+ key: status.key,
89
+ disk: status.disk,
90
+ partSize: status.partSize,
91
+ size: status.size,
92
+ totalParts: status.totalParts,
93
+ ...(status.contentType !== undefined ? { contentType: status.contentType } : {}),
94
+ completedParts: status.completedParts,
95
+ pendingParts: status.pendingParts,
96
+ ...(status.expiresAt !== undefined
97
+ ? { expiresAt: status.expiresAt.toISOString() }
98
+ : {}),
99
+ },
100
+ };
101
+ }
102
+ catch (err) {
103
+ return this.mapError(err);
104
+ }
105
+ }
106
+ case 'confirm-part': {
107
+ if (!Number.isInteger(req.partNumber) || req.partNumber < 1) {
108
+ return { status: 400, body: { error: 'partNumber must be a positive integer' } };
109
+ }
110
+ if (typeof req.etag !== 'string' || req.etag.length === 0) {
111
+ return { status: 400, body: { error: 'etag is required' } };
112
+ }
113
+ try {
114
+ const progress = await this.manager.confirmPart(req.uploadId, {
115
+ partNumber: req.partNumber,
116
+ etag: req.etag,
117
+ });
118
+ return { status: 200, body: progress };
119
+ }
120
+ catch (err) {
121
+ return this.mapError(err);
122
+ }
123
+ }
124
+ case 'complete': {
125
+ const parts = req.parts ?? [];
126
+ if (!Array.isArray(parts) ||
127
+ parts.some((part) => !Number.isInteger(part?.partNumber) ||
128
+ part.partNumber < 1 ||
129
+ typeof part?.etag !== 'string' ||
130
+ part.etag.length === 0)) {
131
+ return {
132
+ status: 400,
133
+ body: { error: 'parts must be an array of { partNumber, etag }' },
134
+ };
135
+ }
136
+ try {
137
+ const done = await this.manager.complete(req.uploadId, parts);
138
+ return { status: 200, body: done };
139
+ }
140
+ catch (err) {
141
+ return this.mapError(err);
142
+ }
143
+ }
144
+ case 'abort': {
145
+ await this.manager.abort(req.uploadId);
146
+ return { status: 204 };
147
+ }
148
+ default:
149
+ return { status: 400, body: { error: 'Unknown action' } };
150
+ }
151
+ }
152
+ /** Map manager errors onto HTTP statuses; anything unexpected keeps propagating. */
153
+ mapError(err) {
154
+ if (err instanceof UploadSessionNotFoundError) {
155
+ return { status: 404, body: { error: err.message, code: err.code } };
156
+ }
157
+ if (err instanceof UploadSessionExpiredError) {
158
+ return { status: 410, body: { error: err.message, code: err.code } };
159
+ }
160
+ if (err instanceof MimeNotAllowedError) {
161
+ return { status: 415, body: { error: err.message, code: err.code } };
162
+ }
163
+ // Not-yet-uploaded parts are client state out of sync with the session, not a bad request.
164
+ if (err instanceof UploadPartsIncompleteError) {
165
+ return {
166
+ status: 409,
167
+ body: { error: err.message, code: err.code, missingParts: [...err.missingParts] },
168
+ };
169
+ }
170
+ if (err instanceof UploadPartOutOfRangeError ||
171
+ err instanceof UploadPartSizeError ||
172
+ err instanceof UploadNotSupportedError ||
173
+ err instanceof RangeError) {
174
+ const code = err.code;
175
+ return { status: 400, body: { error: err.message, ...(code ? { code } : {}) } };
176
+ }
177
+ throw err;
178
+ }
179
+ }
180
+ //# sourceMappingURL=direct_upload_handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"direct_upload_handler.js","sourceRoot":"","sources":["../../src/direct_upload_handler.ts"],"names":[],"mappings":"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"}
@@ -24,6 +24,13 @@ export interface S3DiskConfig {
24
24
  credentials?: S3Credentials;
25
25
  /** Custom endpoint for S3-compatible services (MinIO, R2, DigitalOcean Spaces, …). */
26
26
  endpoint?: string;
27
+ /**
28
+ * Endpoint presigned URLs are signed against, when it differs from `endpoint`. Presigned URLs are
29
+ * consumed by the BROWSER (direct-upload part `PUT`s, signed `GET`s) and SigV4 bakes the host into
30
+ * the signature — so when `endpoint` is an internal FQDN the internet cannot resolve (MinIO on a
31
+ * private network), set the public one here. Server-side operations keep using `endpoint`.
32
+ */
33
+ publicEndpoint?: string;
27
34
  /** Use path-style addressing (`endpoint/bucket/key`) — required by most S3-compatible services. */
28
35
  forcePathStyle?: boolean;
29
36
  /** Prefix prepended to every key (e.g. `uploads`). */
@@ -51,11 +58,12 @@ export interface S3DiskConfig {
51
58
  * ```
52
59
  *
53
60
  * Each factory returns a {@link DiskFactory} — a lazy thunk. Calling it in the config file costs
54
- * nothing; `@aws-sdk/client-s3` (and `@aws-sdk/s3-request-presigner`) are only imported when the
55
- * provider builds the selected disk at boot, keeping the AWS SDK an optional peer.
61
+ * nothing; `@aws-sdk/client-s3` is only imported when the provider builds the selected disk at
62
+ * boot, keeping the AWS SDK an optional peer. (Presigning needs no SDK at all — it is hand-rolled
63
+ * SigV4, see `sigv4.ts`.)
56
64
  */
57
65
  export declare const disks: {
58
- /** S3 (or S3-compatible) disk — needs the optional `@aws-sdk/client-s3` + `@aws-sdk/s3-request-presigner` peers. */
66
+ /** S3 (or S3-compatible) disk — needs the optional `@aws-sdk/client-s3` peer. */
59
67
  s3(config: S3DiskConfig): DiskFactory;
60
68
  };
61
69
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/disks/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAExC;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AAE9C,yGAAyG;AACzG,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,sGAAsG;AACtG,MAAM,WAAW,YAAY;IAC3B,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,WAAW,CAAC,EAAE,aAAa,CAAC;IAC5B,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mGAAmG;IACnG,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,UAAU,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,KAAK;IAChB,oHAAoH;eACzG,YAAY,GAAG,WAAW;CAsBtC,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,SAAS,GACjD,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAQ/B"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/disks/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAExC;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AAE9C,yGAAyG;AACzG,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,sGAAsG;AACtG,MAAM,WAAW,YAAY;IAC3B,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,WAAW,CAAC,EAAE,aAAa,CAAC;IAC5B,sFAAsF;IACtF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mGAAmG;IACnG,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,UAAU,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,KAAK;IAChB,iFAAiF;eACtE,YAAY,GAAG,WAAW;CAuBtC,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,SAAS,GACjD,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAQ/B"}