@trokky/trokky 3.4.1 → 3.5.1

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 (59) hide show
  1. package/dist/adapters/filesystem-data/filesystem-data-adapter.d.ts.map +1 -1
  2. package/dist/adapters/filesystem-data/filesystem-data-adapter.js +3 -1
  3. package/dist/adapters/filesystem-data/filesystem-data-adapter.js.map +1 -1
  4. package/dist/adapters/filesystem-media/filesystem-media-adapter.d.ts.map +1 -1
  5. package/dist/adapters/filesystem-media/filesystem-media-adapter.js +3 -1
  6. package/dist/adapters/filesystem-media/filesystem-media-adapter.js.map +1 -1
  7. package/dist/adapters/s3-media/index-key.d.ts +52 -0
  8. package/dist/adapters/s3-media/index-key.d.ts.map +1 -0
  9. package/dist/adapters/s3-media/index-key.js +91 -0
  10. package/dist/adapters/s3-media/index-key.js.map +1 -0
  11. package/dist/adapters/s3-media/index.d.ts +3 -0
  12. package/dist/adapters/s3-media/index.d.ts.map +1 -0
  13. package/dist/adapters/s3-media/index.js +12 -0
  14. package/dist/adapters/s3-media/index.js.map +1 -0
  15. package/dist/adapters/s3-media/s3-client.d.ts +112 -0
  16. package/dist/adapters/s3-media/s3-client.d.ts.map +1 -0
  17. package/dist/adapters/s3-media/s3-client.js +261 -0
  18. package/dist/adapters/s3-media/s3-client.js.map +1 -0
  19. package/dist/adapters/s3-media/s3-media-adapter.d.ts +118 -0
  20. package/dist/adapters/s3-media/s3-media-adapter.d.ts.map +1 -0
  21. package/dist/adapters/s3-media/s3-media-adapter.js +631 -0
  22. package/dist/adapters/s3-media/s3-media-adapter.js.map +1 -0
  23. package/dist/adapters/s3-media/types.d.ts +63 -0
  24. package/dist/adapters/s3-media/types.d.ts.map +1 -0
  25. package/dist/adapters/s3-media/types.js +2 -0
  26. package/dist/adapters/s3-media/types.js.map +1 -0
  27. package/dist/core/services/auth-service.d.ts.map +1 -1
  28. package/dist/core/services/auth-service.js +4 -1
  29. package/dist/core/services/auth-service.js.map +1 -1
  30. package/dist/core/services/token-service.d.ts.map +1 -1
  31. package/dist/core/services/token-service.js +11 -1
  32. package/dist/core/services/token-service.js.map +1 -1
  33. package/dist/core/types/storage-adapters.d.ts +7 -0
  34. package/dist/core/types/storage-adapters.d.ts.map +1 -1
  35. package/dist/integrations/express/config.d.ts +6 -1
  36. package/dist/integrations/express/config.d.ts.map +1 -1
  37. package/dist/integrations/express/config.js.map +1 -1
  38. package/dist/integrations/express/integration.js +3 -3
  39. package/dist/integrations/express/integration.js.map +1 -1
  40. package/dist/integrations/workers/index.d.ts +5 -0
  41. package/dist/integrations/workers/index.d.ts.map +1 -1
  42. package/dist/integrations/workers/index.js +10 -1
  43. package/dist/integrations/workers/index.js.map +1 -1
  44. package/dist/routes/handlers/base.d.ts +21 -0
  45. package/dist/routes/handlers/base.d.ts.map +1 -1
  46. package/dist/routes/handlers/base.js +62 -12
  47. package/dist/routes/handlers/base.js.map +1 -1
  48. package/dist/routes/handlers/documents.d.ts.map +1 -1
  49. package/dist/routes/handlers/documents.js +16 -0
  50. package/dist/routes/handlers/documents.js.map +1 -1
  51. package/dist/routes/handlers/media.js +7 -7
  52. package/dist/routes/handlers/media.js.map +1 -1
  53. package/dist/routes/handlers/search.d.ts.map +1 -1
  54. package/dist/routes/handlers/search.js +8 -2
  55. package/dist/routes/handlers/search.js.map +1 -1
  56. package/dist/routes/handlers/tokens.d.ts.map +1 -1
  57. package/dist/routes/handlers/tokens.js +42 -8
  58. package/dist/routes/handlers/tokens.js.map +1 -1
  59. package/package.json +6 -1
@@ -0,0 +1,631 @@
1
+ /**
2
+ * S3-compatible media storage adapter.
3
+ *
4
+ * Talks SigV4 over HTTP, so it reaches R2 from a Node process, and MinIO, Backblaze B2, Spaces,
5
+ * Ceph or AWS from anywhere. `CloudflareR2Adapter` is the better choice *inside* a Worker — a
6
+ * binding is faster and needs no credentials — and the two share a key layout on purpose, so
7
+ * one bucket can be handed from a Worker to a Node server and back.
8
+ *
9
+ * The layout, identical to the R2 adapter's:
10
+ *
11
+ * files/<id> the bytes
12
+ * meta/<id>.json the record — the only authority for existence
13
+ * variants/<id>/<name> variant bytes, format in user metadata
14
+ * index/... the listing cache, see index-key.ts
15
+ *
16
+ * What differs, and why:
17
+ *
18
+ * - **The listing is a join, not a sweep.** R2's `list()` can return `customMetadata`;
19
+ * `ListObjectsV2` cannot return user metadata at all. So the sortable fields are encoded into
20
+ * keys under `index/`, and `listMedia` enumerates ids from `meta/`, reads fields from the
21
+ * index, and falls back to reading the record for any id the index does not cover.
22
+ * - **The index must never be the enumeration.** Writes go bytes → record → index and deletes
23
+ * reverse that, so an interrupted operation leaves a record with no index entry. If the index
24
+ * were authoritative that file would exist and never be listed — a ghost. Enumerating from
25
+ * `meta/` makes a missing entry cost one GET instead of hiding a file, and `cleanup()`
26
+ * rebuilds it.
27
+ * - **`listVariants` HEADs each variant.** The format lives in user metadata and a listing does
28
+ * not carry it. A file has a handful of variants, so this is a handful of requests, and it
29
+ * keeps the R2 invariant that variant keys carry no format — re-encoding a variant to another
30
+ * format overwrites it instead of leaving both behind.
31
+ */
32
+ import { InvalidInputError, SecurityValidator, createLogger } from '../../core/index.js';
33
+ import { S3Client } from './s3-client.js';
34
+ import { decodeIndexKey, encodeIndexKey, entryPrefix, indexPrefix } from './index-key.js';
35
+ /** Matches the other media adapters' ceiling, so a file that uploads on one uploads on all. */
36
+ const DEFAULT_MAX_FILE_SIZE = 100 * 1024 * 1024;
37
+ /** Long enough to be useful in a page, short enough that a leaked URL expires. */
38
+ const DEFAULT_URL_EXPIRY_SECONDS = 900;
39
+ /** An extension goes into no key, but it is still validated so every adapter rejects the same input. */
40
+ const EXTENSION_PATTERN = /^[A-Za-z0-9]{1,10}$/;
41
+ /** Fields the caller may set through `updateFile` that belong to the record, not to `custom`. */
42
+ const RECORD_FIELDS = new Set(['filename', 'contentType', 'size', 'extension']);
43
+ /** Reads that fan out (records behind a listing, variant HEADs) run this many at a time. */
44
+ const READ_CONCURRENCY = 16;
45
+ export class S3MediaAdapter {
46
+ s3;
47
+ prefix;
48
+ publicBaseUrl;
49
+ urlExpiry;
50
+ maxFileSize;
51
+ silent;
52
+ logger = createLogger('adapter', 'S3MediaAdapter');
53
+ constructor(config) {
54
+ for (const field of ['endpoint', 'bucket', 'accessKeyId', 'secretAccessKey']) {
55
+ if (!config?.[field] || typeof config[field] !== 'string') {
56
+ throw new InvalidInputError(`S3MediaAdapter requires a ${field}`, field);
57
+ }
58
+ }
59
+ this.s3 = new S3Client({
60
+ endpoint: config.endpoint,
61
+ bucket: config.bucket,
62
+ accessKeyId: config.accessKeyId,
63
+ secretAccessKey: config.secretAccessKey,
64
+ region: config.region ?? 'auto',
65
+ forcePathStyle: config.forcePathStyle ?? true
66
+ });
67
+ // A prefix becomes URL path segments, where `..` is an operator rather than a name: writes
68
+ // would climb out of the bucket while `list()` (which goes through URLSearchParams) would
69
+ // not, leaving a store whose reads and writes disagree.
70
+ if (config.prefix && !/^[A-Za-z0-9._~\-]+(?:\/[A-Za-z0-9._~\-]+)*\/?$/.test(config.prefix)) {
71
+ throw new InvalidInputError('prefix may contain only letters, digits, dot, dash, underscore, tilde and slash', 'prefix');
72
+ }
73
+ if (config.prefix?.split('/').includes('..')) {
74
+ throw new InvalidInputError('prefix may not contain a ".." segment', 'prefix');
75
+ }
76
+ this.prefix = config.prefix ? config.prefix.replace(/\/*$/, '/') : '';
77
+ this.publicBaseUrl = config.publicBaseUrl?.replace(/\/+$/, '');
78
+ this.urlExpiry = config.defaultUrlExpirySeconds ?? DEFAULT_URL_EXPIRY_SECONDS;
79
+ this.maxFileSize = config.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
80
+ this.silent = config.silent ?? false;
81
+ if (!this.silent) {
82
+ this.logger.info('S3MediaAdapter initialized', {
83
+ bucket: config.bucket,
84
+ prefix: this.prefix || '(none)'
85
+ });
86
+ }
87
+ }
88
+ // ==========================================================================
89
+ // KEYS
90
+ // ==========================================================================
91
+ fileKey(id) {
92
+ return `${this.prefix}files/${id}`;
93
+ }
94
+ recordKey(id) {
95
+ return `${this.prefix}meta/${id}.json`;
96
+ }
97
+ variantKey(parentId, variantName) {
98
+ return `${this.prefix}variants/${parentId}/${variantName}`;
99
+ }
100
+ variantPrefix(parentId) {
101
+ return `${this.prefix}variants/${parentId}/`;
102
+ }
103
+ recordPrefix() {
104
+ return `${this.prefix}meta/`;
105
+ }
106
+ // ==========================================================================
107
+ // FILE OPERATIONS
108
+ // ==========================================================================
109
+ async uploadFile(file, metadata) {
110
+ this.validateMediaMetadata(metadata);
111
+ const buffer = await file.arrayBuffer();
112
+ if (buffer.byteLength > this.maxFileSize) {
113
+ throw new InvalidInputError(`File too large (${buffer.byteLength} bytes). Maximum size is ${this.maxFileSize} bytes.`, 'size');
114
+ }
115
+ // New bytes make any existing variant a description of content that is gone.
116
+ await this.deleteVariantFiles(metadata.id);
117
+ // The previous index entry described the previous upload; its epoch no longer matches.
118
+ await this.deleteIndexEntries(metadata.id);
119
+ const now = new Date();
120
+ const record = {
121
+ id: metadata.id,
122
+ filename: metadata.filename,
123
+ contentType: metadata.contentType,
124
+ size: metadata.size,
125
+ extension: metadata.extension,
126
+ createdAt: now.toISOString(),
127
+ updatedAt: now.toISOString(),
128
+ custom: {}
129
+ };
130
+ await this.s3.put(this.fileKey(metadata.id), buffer, { contentType: metadata.contentType });
131
+ // The record lands before the index: until it does, nothing lists the file as present.
132
+ await this.writeRecord(record);
133
+ await this.writeIndexEntry(record);
134
+ if (!this.silent) {
135
+ this.logger.info('File uploaded', {
136
+ id: metadata.id,
137
+ filename: metadata.filename,
138
+ size: metadata.size
139
+ });
140
+ }
141
+ return this.toMediaFile(record);
142
+ }
143
+ async getFile(id) {
144
+ SecurityValidator.validateDocumentId(id);
145
+ const record = await this.readRecord(id);
146
+ return record ? this.toMediaFile(record) : null;
147
+ }
148
+ async updateFile(id, metadata) {
149
+ SecurityValidator.validateDocumentId(id);
150
+ const record = await this.readRecord(id);
151
+ if (!record) {
152
+ throw new Error(`Media file ${id} not found`);
153
+ }
154
+ const updated = {
155
+ ...record,
156
+ custom: { ...record.custom },
157
+ updatedAt: new Date().toISOString()
158
+ };
159
+ for (const [key, value] of Object.entries(metadata ?? {})) {
160
+ // The id identifies the object; letting it be rewritten would orphan the bytes.
161
+ if (key === 'id')
162
+ continue;
163
+ if (RECORD_FIELDS.has(key)) {
164
+ // Coerced, because these are encoded into the index key and `decodeIndexKey` rejects an
165
+ // entry whose `size` is not a number. An uncoerced `size: '4242'` un-indexed the file
166
+ // permanently and made cleanup() non-convergent: it rebuilt an equally undecodable
167
+ // entry every run and deleted it again the next.
168
+ ;
169
+ updated[key] =
170
+ key === 'size' ? toSize(value, record.size) : String(value);
171
+ }
172
+ else {
173
+ updated.custom[key] = value;
174
+ }
175
+ }
176
+ await this.writeRecord(updated);
177
+ // The index encodes filename, contentType and size — `updateFile` can change all three, and
178
+ // a stale entry means a wrong filter, a wrong sort and a wrong total with nothing to heal it.
179
+ if (updated.filename !== record.filename ||
180
+ updated.contentType !== record.contentType ||
181
+ updated.size !== record.size) {
182
+ await this.deleteIndexEntries(id);
183
+ await this.writeIndexEntry(updated);
184
+ }
185
+ if (!this.silent) {
186
+ this.logger.info('File metadata updated', { id });
187
+ }
188
+ return this.toMediaFile(updated);
189
+ }
190
+ async getFileContent(id) {
191
+ SecurityValidator.validateDocumentId(id);
192
+ const response = await this.s3.get(this.fileKey(id));
193
+ return response ? response.arrayBuffer() : null;
194
+ }
195
+ async getFileUrl(id, options = {}) {
196
+ SecurityValidator.validateDocumentId(id);
197
+ if (!(await this.fileExists(id)))
198
+ return null;
199
+ if (this.publicBaseUrl)
200
+ return `${this.publicBaseUrl}/${this.fileKey(id)}`;
201
+ // Unlike a bucket binding, SigV4 can sign a URL — so an install with no public base still
202
+ // has one to give.
203
+ return this.s3.presign(this.fileKey(id), options.expiresIn ?? this.urlExpiry);
204
+ }
205
+ async fileExists(id) {
206
+ SecurityValidator.validateDocumentId(id);
207
+ return (await this.s3.head(this.recordKey(id))) !== null;
208
+ }
209
+ async getFileSize(id) {
210
+ SecurityValidator.validateDocumentId(id);
211
+ const record = await this.readRecord(id);
212
+ return record ? record.size : null;
213
+ }
214
+ async deleteFile(id) {
215
+ SecurityValidator.validateDocumentId(id);
216
+ const record = await this.readRecord(id);
217
+ if (!record) {
218
+ throw new Error(`Media file ${id} not found`);
219
+ }
220
+ await this.deleteVariantFiles(id);
221
+ // Index first, then the record, then the bytes: every step makes the file less visible than
222
+ // the last, so an interruption can only leave something cleanup() can reclaim.
223
+ await this.deleteIndexEntries(id);
224
+ await this.s3.delete(this.recordKey(id));
225
+ await this.s3.delete(this.fileKey(id));
226
+ if (!this.silent) {
227
+ this.logger.debug('File deleted', { id });
228
+ }
229
+ }
230
+ // ==========================================================================
231
+ // LISTING
232
+ // ==========================================================================
233
+ async listMedia(options = {}) {
234
+ let entries = await this.listIndex();
235
+ if (options.contentType) {
236
+ const wanted = options.contentType;
237
+ entries = entries.filter(entry => entry.contentType.includes(wanted));
238
+ }
239
+ if (options.sizeRange) {
240
+ const { min, max } = options.sizeRange;
241
+ entries = entries.filter(entry => {
242
+ if (min !== undefined && entry.size < min)
243
+ return false;
244
+ if (max !== undefined && entry.size > max)
245
+ return false;
246
+ return true;
247
+ });
248
+ }
249
+ if (options.dateRange) {
250
+ const { from, to } = options.dateRange;
251
+ entries = entries.filter(entry => {
252
+ if (from && entry.createdAt < from)
253
+ return false;
254
+ if (to && entry.createdAt > to)
255
+ return false;
256
+ return true;
257
+ });
258
+ }
259
+ // No sort means newest first, matching the other media adapters and what the Studio shows.
260
+ this.sortIndex(entries, options.sort ?? 'date', options.sortDirection ?? (options.sort ? 'asc' : 'desc'));
261
+ const total = entries.length;
262
+ const offset = options.offset ?? 0;
263
+ const page = options.limit === undefined
264
+ ? entries.slice(offset)
265
+ : entries.slice(offset, offset + options.limit);
266
+ // Only the page is read in full; the filters and the total came from the two listings alone.
267
+ const records = await this.mapWithConcurrency(page, entry => this.readRecord(entry.id));
268
+ const items = records
269
+ .filter((record) => record !== null)
270
+ .map(record => this.toMediaFile(record));
271
+ return { items, total };
272
+ }
273
+ // ==========================================================================
274
+ // VARIANT OPERATIONS
275
+ // ==========================================================================
276
+ async saveVariantFile(parentId, variantName, buffer, format) {
277
+ SecurityValidator.validateDocumentId(parentId);
278
+ SecurityValidator.validateDocumentId(variantName);
279
+ if (!format || typeof format !== 'string' || !EXTENSION_PATTERN.test(format)) {
280
+ throw new InvalidInputError('Invalid variant format', 'format');
281
+ }
282
+ const key = this.variantKey(parentId, variantName);
283
+ // Buffer is a Uint8Array view; copy out so the store gets exactly this variant's bytes and
284
+ // never a window onto a larger pooled allocation.
285
+ const bytes = new Uint8Array(buffer.byteLength);
286
+ bytes.set(buffer);
287
+ await this.s3.put(key, bytes, {
288
+ contentType: `image/${format}`,
289
+ metadata: { format }
290
+ });
291
+ if (!this.silent) {
292
+ this.logger.info('Variant file saved', { parentId, variantName, format });
293
+ }
294
+ return key;
295
+ }
296
+ async getVariantContent(parentId, variantName) {
297
+ SecurityValidator.validateDocumentId(parentId);
298
+ SecurityValidator.validateDocumentId(variantName);
299
+ const response = await this.s3.get(this.variantKey(parentId, variantName));
300
+ return response ? response.arrayBuffer() : null;
301
+ }
302
+ async getVariantUrl(parentId, variantName, options = {}) {
303
+ SecurityValidator.validateDocumentId(parentId);
304
+ SecurityValidator.validateDocumentId(variantName);
305
+ const key = this.variantKey(parentId, variantName);
306
+ if (!(await this.s3.head(key)))
307
+ return null;
308
+ if (this.publicBaseUrl)
309
+ return `${this.publicBaseUrl}/${key}`;
310
+ return this.s3.presign(key, options.expiresIn ?? this.urlExpiry);
311
+ }
312
+ async listVariants(parentId) {
313
+ SecurityValidator.validateDocumentId(parentId);
314
+ const prefix = this.variantPrefix(parentId);
315
+ const objects = await this.s3.listAll(prefix);
316
+ // A listing carries no user metadata, and the format lives there. A file has a handful of
317
+ // variants, so a HEAD each is proportionate — and the alternative, putting the format in the
318
+ // key, would let one variant exist twice in two formats.
319
+ return this.mapWithConcurrency(objects, async (object) => {
320
+ const head = await this.s3.head(object.key);
321
+ return {
322
+ name: object.key.slice(prefix.length),
323
+ format: head?.metadata.format ?? 'unknown',
324
+ size: object.size,
325
+ url: this.publicBaseUrl ? `${this.publicBaseUrl}/${object.key}` : undefined,
326
+ createdAt: object.lastModified
327
+ };
328
+ });
329
+ }
330
+ async deleteVariantFiles(parentId) {
331
+ SecurityValidator.validateDocumentId(parentId);
332
+ const objects = await this.s3.listAll(this.variantPrefix(parentId));
333
+ if (objects.length === 0)
334
+ return;
335
+ await this.s3.deleteMany(objects.map(object => object.key));
336
+ if (!this.silent) {
337
+ this.logger.debug('Variant files deleted', { parentId, count: objects.length });
338
+ }
339
+ }
340
+ async deleteVariant(parentId, variantName) {
341
+ SecurityValidator.validateDocumentId(parentId);
342
+ SecurityValidator.validateDocumentId(variantName);
343
+ await this.s3.delete(this.variantKey(parentId, variantName));
344
+ }
345
+ // ==========================================================================
346
+ // UTILITY OPERATIONS
347
+ // ==========================================================================
348
+ async healthCheck() {
349
+ try {
350
+ // A one-key listing, not a HEAD on a missing key: a HEAD 404s identically whether the key
351
+ // is absent or the whole bucket is, so a mistyped bucket reported healthy and then
352
+ // reported an empty media library.
353
+ await this.s3.list(this.prefix, { maxKeys: 1 });
354
+ return true;
355
+ }
356
+ catch (error) {
357
+ this.logger.error('Health check failed', error);
358
+ return false;
359
+ }
360
+ }
361
+ async getStorageInfo() {
362
+ const entries = await this.listIndex();
363
+ const totalSize = entries.reduce((sum, entry) => sum + entry.size, 0);
364
+ return {
365
+ totalFiles: entries.length,
366
+ totalSize,
367
+ // Object storage is unmetered in size, so there is no quota and no free space to compute.
368
+ usedSpace: totalSize
369
+ };
370
+ }
371
+ /**
372
+ * Reconcile the bucket against its records, which are the only authority.
373
+ *
374
+ * Three jobs, and the order they are listed in is the order of how much they matter:
375
+ *
376
+ * 1. **Rebuild missing index entries.** An upload interrupted after the record but before the
377
+ * index — or a delete interrupted the other way — leaves a file that `getFile` serves and
378
+ * no listing shows. `listMedia` already heals the symptom by reading the record; this
379
+ * removes the cost.
380
+ * 2. **Drop orphaned bytes.** Anything under `files/` or `variants/` whose id has no record.
381
+ * 3. **Drop stale index entries.** Unreachable by an interruption under these write orders,
382
+ * but reachable by a partial restore or by a re-upload whose old-entry delete failed.
383
+ *
384
+ * Returns the number of objects written or deleted.
385
+ */
386
+ async cleanup() {
387
+ const records = await this.listRecordIds();
388
+ const live = new Set(records);
389
+ let touched = 0;
390
+ // 1. Derive the ONE key each record should have, and write it if it is not already there.
391
+ //
392
+ // Every record is read, not just the unindexed ones: an entry that disagrees with its
393
+ // record is invisible without the record to compare against, and testing only for
394
+ // absence left such an entry permanent — `listMedia` considers that id covered and never
395
+ // re-reads, so every filter, sort and total is served from the stale copy forever. That
396
+ // is what an `updateFile` interrupted between its two writes leaves behind.
397
+ const keep = new Map();
398
+ const present = new Set((await this.s3.listAll(indexPrefix(this.prefix))).map(object => object.key));
399
+ for (const record of await this.mapWithConcurrency(records, id => this.readRecord(id))) {
400
+ if (!record)
401
+ continue;
402
+ const canonical = encodeIndexKey(this.prefix, entryOf(record));
403
+ keep.set(record.id, canonical);
404
+ if (present.has(canonical))
405
+ continue;
406
+ await this.writeIndexEntry(record);
407
+ touched++;
408
+ }
409
+ // 2. Orphaned bytes: anything under files/ or variants/ whose id has no record.
410
+ const orphans = [];
411
+ const filePrefix = `${this.prefix}files/`;
412
+ for (const object of await this.s3.listAll(filePrefix)) {
413
+ if (!live.has(object.key.slice(filePrefix.length)))
414
+ orphans.push(object.key);
415
+ }
416
+ const variantRoot = `${this.prefix}variants/`;
417
+ for (const object of await this.s3.listAll(variantRoot)) {
418
+ const parentId = object.key.slice(variantRoot.length).split('/')[0];
419
+ if (!live.has(parentId))
420
+ orphans.push(object.key);
421
+ }
422
+ // 3. Every index object that is not the canonical key for a live record: unparseable,
423
+ // recordless, superseded, or a duplicate. The record decides, so this converges in one
424
+ // pass and does not depend on which of two tied entries a listing happens to return
425
+ // first.
426
+ for (const object of await this.s3.listAll(indexPrefix(this.prefix))) {
427
+ const entry = decodeIndexKey(this.prefix, object.key);
428
+ if (!entry || keep.get(entry.id) !== object.key)
429
+ orphans.push(object.key);
430
+ }
431
+ if (orphans.length > 0) {
432
+ await this.s3.deleteMany(orphans);
433
+ touched += orphans.length;
434
+ }
435
+ if (touched > 0 && !this.silent) {
436
+ this.logger.info('Cleanup reconciled the bucket', {
437
+ rebuilt: touched - orphans.length,
438
+ deleted: orphans.length
439
+ });
440
+ }
441
+ return touched;
442
+ }
443
+ // ==========================================================================
444
+ // PRIVATE HELPERS
445
+ // ==========================================================================
446
+ async writeRecord(record) {
447
+ await this.s3.put(this.recordKey(record.id), JSON.stringify(record), {
448
+ contentType: 'application/json'
449
+ });
450
+ }
451
+ async readRecord(id) {
452
+ const response = await this.s3.get(this.recordKey(id));
453
+ if (!response)
454
+ return null;
455
+ const text = await response.text();
456
+ try {
457
+ const parsed = JSON.parse(text);
458
+ return { ...parsed, custom: parsed.custom ?? {} };
459
+ }
460
+ catch (error) {
461
+ this.logger.warn(`Media record ${id} is not valid JSON`, error);
462
+ return null;
463
+ }
464
+ }
465
+ async writeIndexEntry(record) {
466
+ await this.s3.put(encodeIndexKey(this.prefix, entryOf(record)), new Uint8Array(0));
467
+ }
468
+ /**
469
+ * Every index key for an id, whatever epoch it carries.
470
+ *
471
+ * One prefix listing rather than a sweep of the whole index — which is the entire reason the
472
+ * key puts the id before the timestamp. This runs on every upload.
473
+ */
474
+ async deleteIndexEntries(id) {
475
+ const keys = (await this.s3.listAll(entryPrefix(this.prefix, id))).map(object => object.key);
476
+ if (keys.length > 0)
477
+ await this.s3.deleteMany(keys);
478
+ }
479
+ async listRecordIds() {
480
+ const prefix = this.recordPrefix();
481
+ return (await this.s3.listAll(prefix))
482
+ .map(object => object.key.slice(prefix.length))
483
+ .filter(name => name.endsWith('.json'))
484
+ .map(name => name.slice(0, -'.json'.length));
485
+ }
486
+ /**
487
+ * Every record in the bucket, with its listable fields.
488
+ *
489
+ * Two key-only listings — `meta/` for the ids, `index/` for the fields — joined by id. The
490
+ * records are the authority for *which* files exist; the index only saves reading them. An id
491
+ * the index does not cover (a bucket written by the R2 adapter, a hand-copied object, an
492
+ * interrupted upload) falls back to a real read, so it is never silently invisible.
493
+ */
494
+ async listIndex() {
495
+ const [ids, indexObjects] = await Promise.all([
496
+ this.listRecordIds(),
497
+ this.s3.listAll(indexPrefix(this.prefix))
498
+ ]);
499
+ const byId = winningEntries(this.prefix, indexObjects);
500
+ const entries = [];
501
+ const needsRead = [];
502
+ for (const id of ids) {
503
+ const entry = byId.get(id);
504
+ if (entry)
505
+ entries.push(entry);
506
+ else
507
+ needsRead.push(id);
508
+ }
509
+ for (const record of await this.mapWithConcurrency(needsRead, id => this.readRecord(id))) {
510
+ if (!record)
511
+ continue;
512
+ entries.push({
513
+ id: record.id,
514
+ filename: record.filename,
515
+ contentType: record.contentType,
516
+ size: record.size,
517
+ createdAt: new Date(record.createdAt)
518
+ });
519
+ }
520
+ return entries;
521
+ }
522
+ /** Every request here is a network round trip, so fan out — but not without a ceiling. */
523
+ async mapWithConcurrency(items, map) {
524
+ const results = [];
525
+ for (let index = 0; index < items.length; index += READ_CONCURRENCY) {
526
+ results.push(...(await Promise.all(items.slice(index, index + READ_CONCURRENCY).map(map))));
527
+ }
528
+ return results;
529
+ }
530
+ sortIndex(entries, sortBy, direction) {
531
+ const pick = (entry) => {
532
+ switch (sortBy) {
533
+ case 'name':
534
+ return entry.filename.toLowerCase();
535
+ case 'size':
536
+ return entry.size;
537
+ case 'type':
538
+ return entry.contentType;
539
+ case 'date':
540
+ default:
541
+ return entry.createdAt.getTime();
542
+ }
543
+ };
544
+ entries.sort((a, b) => {
545
+ const left = pick(a);
546
+ const right = pick(b);
547
+ let comparison = 0;
548
+ if (left < right)
549
+ comparison = -1;
550
+ else if (left > right)
551
+ comparison = 1;
552
+ return direction === 'desc' ? -comparison : comparison;
553
+ });
554
+ }
555
+ toMediaFile(record) {
556
+ return {
557
+ id: record.id,
558
+ filename: record.filename,
559
+ contentType: record.contentType,
560
+ size: record.size,
561
+ metadata: {
562
+ // Caller fields first: the derived ones below are facts about the object and must win.
563
+ ...record.custom,
564
+ path: this.fileKey(record.id),
565
+ extension: record.extension,
566
+ originalFilename: record.filename
567
+ },
568
+ _createdAt: new Date(record.createdAt)
569
+ };
570
+ }
571
+ validateMediaMetadata(metadata) {
572
+ if (!metadata || typeof metadata !== 'object') {
573
+ throw new InvalidInputError('Media metadata is required', 'metadata');
574
+ }
575
+ // Rejects an empty, overlong or path-like id before it can become a key.
576
+ SecurityValidator.validateDocumentId(metadata.id);
577
+ if (!metadata.filename || typeof metadata.filename !== 'string') {
578
+ throw new InvalidInputError('Media metadata must have a valid filename', 'filename');
579
+ }
580
+ if (!metadata.contentType || typeof metadata.contentType !== 'string') {
581
+ throw new InvalidInputError('Media metadata must have a valid content type', 'contentType');
582
+ }
583
+ if (!metadata.extension ||
584
+ typeof metadata.extension !== 'string' ||
585
+ !EXTENSION_PATTERN.test(metadata.extension)) {
586
+ throw new InvalidInputError('Media metadata must have a valid extension', 'extension');
587
+ }
588
+ if (typeof metadata.size !== 'number' || metadata.size <= 0) {
589
+ throw new InvalidInputError('Media metadata must have a valid size', 'size');
590
+ }
591
+ }
592
+ }
593
+ /** `updateFile` may be handed anything; the index cannot encode a non-number size. */
594
+ function toSize(value, fallback) {
595
+ const size = Number(value);
596
+ return Number.isFinite(size) && size >= 0 ? size : fallback;
597
+ }
598
+ /** The index entry a record should have. One record, one canonical key. */
599
+ function entryOf(record) {
600
+ return {
601
+ id: record.id,
602
+ filename: record.filename,
603
+ contentType: record.contentType,
604
+ size: record.size,
605
+ createdAt: new Date(record.createdAt)
606
+ };
607
+ }
608
+ /**
609
+ * One entry per id, chosen the same way by every reader.
610
+ *
611
+ * Newest wins; equal epochs break on the key. Ties are ordinary rather than exotic —
612
+ * `updateFile` re-encodes with the record's original `createdAt` — and a strict `<` on its own
613
+ * left the winner up to listing order and made `cleanup()` delete none of the duplicates.
614
+ */
615
+ function winningEntries(prefix, objects) {
616
+ const byId = new Map();
617
+ for (const object of objects) {
618
+ const entry = decodeIndexKey(prefix, object.key);
619
+ if (!entry)
620
+ continue;
621
+ const existing = byId.get(entry.id);
622
+ const better = !existing ||
623
+ existing.entry.createdAt < entry.createdAt ||
624
+ (existing.entry.createdAt.getTime() === entry.createdAt.getTime() &&
625
+ existing.key < object.key);
626
+ if (better)
627
+ byId.set(entry.id, { entry, key: object.key });
628
+ }
629
+ return new Map([...byId].map(([id, winner]) => [id, winner.entry]));
630
+ }
631
+ //# sourceMappingURL=s3-media-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3-media-adapter.js","sourceRoot":"","sources":["../../../src/adapters/s3-media/s3-media-adapter.ts"],"names":[],"mappings":"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"}