@adonis-agora/media 0.1.0 → 0.2.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.
- package/CHANGELOG.md +54 -0
- package/dist/configure.d.ts +2 -2
- package/dist/configure.d.ts.map +1 -1
- package/dist/configure.js +3 -2
- package/dist/configure.js.map +1 -1
- package/dist/providers/media_provider.d.ts +15 -0
- package/dist/providers/media_provider.d.ts.map +1 -1
- package/dist/providers/media_provider.js +224 -12
- package/dist/providers/media_provider.js.map +1 -1
- package/dist/src/attachment.d.ts +1 -0
- package/dist/src/attachment.d.ts.map +1 -1
- package/dist/src/attachment.js +52 -21
- package/dist/src/attachment.js.map +1 -1
- package/dist/src/define_config.d.ts +83 -5
- package/dist/src/define_config.d.ts.map +1 -1
- package/dist/src/define_config.js +3 -1
- package/dist/src/define_config.js.map +1 -1
- package/dist/src/diagnostics.d.ts +32 -1
- package/dist/src/diagnostics.d.ts.map +1 -1
- package/dist/src/diagnostics.js.map +1 -1
- package/dist/src/disks/factory.d.ts +62 -0
- package/dist/src/disks/factory.d.ts.map +1 -0
- package/dist/src/disks/factory.js +57 -0
- package/dist/src/disks/factory.js.map +1 -0
- package/dist/src/disks/s3.d.ts +70 -0
- package/dist/src/disks/s3.d.ts.map +1 -0
- package/dist/src/disks/s3.js +347 -0
- package/dist/src/disks/s3.js.map +1 -0
- package/dist/src/disks/xml_fallback.d.ts +29 -0
- package/dist/src/disks/xml_fallback.d.ts.map +1 -0
- package/dist/src/disks/xml_fallback.js +68 -0
- package/dist/src/disks/xml_fallback.js.map +1 -0
- package/dist/src/errors.d.ts +34 -0
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +60 -0
- package/dist/src/errors.js.map +1 -1
- package/dist/src/extended_disk.d.ts +10 -0
- package/dist/src/extended_disk.d.ts.map +1 -0
- package/dist/src/extended_disk.js +19 -0
- package/dist/src/extended_disk.js.map +1 -0
- package/dist/src/index.d.ts +17 -7
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +11 -4
- package/dist/src/index.js.map +1 -1
- package/dist/src/media_library.d.ts +4 -2
- package/dist/src/media_library.d.ts.map +1 -1
- package/dist/src/media_library.js +78 -32
- package/dist/src/media_library.js.map +1 -1
- package/dist/src/media_manager.d.ts +48 -0
- package/dist/src/media_manager.d.ts.map +1 -1
- package/dist/src/media_manager.js +68 -0
- package/dist/src/media_manager.js.map +1 -1
- package/dist/src/media_store.d.ts +40 -0
- package/dist/src/media_store.d.ts.map +1 -1
- package/dist/src/media_store.js +28 -1
- package/dist/src/media_store.js.map +1 -1
- package/dist/src/multipart.d.ts +10 -0
- package/dist/src/multipart.d.ts.map +1 -0
- package/dist/src/multipart.js +16 -0
- package/dist/src/multipart.js.map +1 -0
- package/dist/src/resumable_upload.d.ts +131 -0
- package/dist/src/resumable_upload.d.ts.map +1 -0
- package/dist/src/resumable_upload.js +177 -0
- package/dist/src/resumable_upload.js.map +1 -0
- package/dist/src/services/main.d.ts +14 -0
- package/dist/src/services/main.d.ts.map +1 -0
- package/dist/src/services/main.js +18 -0
- package/dist/src/services/main.js.map +1 -0
- package/dist/src/stores/factory.d.ts +12 -4
- package/dist/src/stores/factory.d.ts.map +1 -1
- package/dist/src/stores/factory.js +20 -1
- package/dist/src/stores/factory.js.map +1 -1
- package/dist/src/stores/lucid.d.ts +2 -1
- package/dist/src/stores/lucid.d.ts.map +1 -1
- package/dist/src/stores/lucid.js +42 -7
- package/dist/src/stores/lucid.js.map +1 -1
- package/dist/src/telescope/dashboard.d.ts +19 -0
- package/dist/src/telescope/dashboard.d.ts.map +1 -0
- package/dist/src/telescope/dashboard.js +116 -0
- package/dist/src/telescope/dashboard.js.map +1 -0
- package/dist/src/telescope/data-providers.d.ts +36 -0
- package/dist/src/telescope/data-providers.d.ts.map +1 -0
- package/dist/src/telescope/data-providers.js +286 -0
- package/dist/src/telescope/data-providers.js.map +1 -0
- package/dist/src/telescope/extension.d.ts +26 -0
- package/dist/src/telescope/extension.d.ts.map +1 -0
- package/dist/src/telescope/extension.js +42 -0
- package/dist/src/telescope/extension.js.map +1 -0
- package/dist/src/telescope/index.d.ts +5 -0
- package/dist/src/telescope/index.d.ts.map +1 -0
- package/dist/src/telescope/index.js +4 -0
- package/dist/src/telescope/index.js.map +1 -0
- package/dist/src/telescope/telescope-sdk.d.ts +125 -0
- package/dist/src/telescope/telescope-sdk.d.ts.map +1 -0
- package/dist/src/telescope/telescope-sdk.js +14 -0
- package/dist/src/telescope/telescope-sdk.js.map +1 -0
- package/dist/src/testing/in_memory_disk.d.ts +1 -0
- package/dist/src/testing/in_memory_disk.d.ts.map +1 -1
- package/dist/src/testing/in_memory_disk.js +10 -0
- package/dist/src/testing/in_memory_disk.js.map +1 -1
- package/dist/src/testing/in_memory_media_store.d.ts +2 -1
- package/dist/src/testing/in_memory_media_store.d.ts.map +1 -1
- package/dist/src/testing/in_memory_media_store.js +28 -0
- package/dist/src/testing/in_memory_media_store.js.map +1 -1
- package/dist/src/testing/in_memory_upload_session_store.d.ts +18 -0
- package/dist/src/testing/in_memory_upload_session_store.d.ts.map +1 -0
- package/dist/src/testing/in_memory_upload_session_store.js +40 -0
- package/dist/src/testing/in_memory_upload_session_store.js.map +1 -0
- package/dist/src/testing/index.d.ts +1 -0
- package/dist/src/testing/index.d.ts.map +1 -1
- package/dist/src/testing/index.js +1 -0
- package/dist/src/testing/index.js.map +1 -1
- package/dist/src/tus.d.ts +57 -0
- package/dist/src/tus.d.ts.map +1 -0
- package/dist/src/tus.js +143 -0
- package/dist/src/tus.js.map +1 -0
- package/dist/src/types.d.ts +118 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/upload_manager.d.ts +127 -0
- package/dist/src/upload_manager.d.ts.map +1 -0
- package/dist/src/upload_manager.js +152 -0
- package/dist/src/upload_manager.js.map +1 -0
- package/dist/src/upload_mode.d.ts +27 -0
- package/dist/src/upload_mode.d.ts.map +1 -0
- package/dist/src/upload_mode.js +22 -0
- package/dist/src/upload_mode.js.map +1 -0
- package/dist/src/upload_sessions/factory.d.ts +62 -0
- package/dist/src/upload_sessions/factory.d.ts.map +1 -0
- package/dist/src/upload_sessions/factory.js +60 -0
- package/dist/src/upload_sessions/factory.js.map +1 -0
- package/dist/src/upload_sessions/lucid.d.ts +35 -0
- package/dist/src/upload_sessions/lucid.d.ts.map +1 -0
- package/dist/src/upload_sessions/lucid.js +105 -0
- package/dist/src/upload_sessions/lucid.js.map +1 -0
- package/dist/stubs/config/media.stub +16 -1
- package/dist/stubs/database/migrations/create_media_upload_sessions_table.stub +45 -0
- package/package.json +40 -1
package/dist/src/types.d.ts
CHANGED
|
@@ -11,6 +11,12 @@ import type { Readable } from 'node:stream';
|
|
|
11
11
|
*/
|
|
12
12
|
export interface Disk {
|
|
13
13
|
put(key: string, contents: Uint8Array, options?: DiskWriteOptions): Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Write a stream straight to the disk without buffering it in memory (flydrive exposes this). Used
|
|
16
|
+
* for large uploads when no conversions are needed and the size is known up front. Optional so the
|
|
17
|
+
* structural contract stays minimal; callers fall back to {@link put} when it is absent.
|
|
18
|
+
*/
|
|
19
|
+
putStream?(key: string, contents: Readable, options?: DiskWriteOptions): Promise<void>;
|
|
14
20
|
getBytes(key: string): Promise<Uint8Array>;
|
|
15
21
|
getStream(key: string): Promise<Readable>;
|
|
16
22
|
exists(key: string): Promise<boolean>;
|
|
@@ -35,6 +41,118 @@ export interface DiskMetaData {
|
|
|
35
41
|
etag?: string | undefined;
|
|
36
42
|
lastModified?: Date | undefined;
|
|
37
43
|
}
|
|
44
|
+
/** One completed part of a native multipart upload: its number and the ETag S3 returned for it. */
|
|
45
|
+
export interface MultipartPart {
|
|
46
|
+
partNumber: number;
|
|
47
|
+
etag: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Coarse feature descriptor for a disk, letting callers branch on what a backend supports without
|
|
51
|
+
* probing methods one by one. Advertised by disks that implement {@link ExtendedDisk}; the media
|
|
52
|
+
* library reads it structurally (via `isExtendedDisk`) and never depends on it statically.
|
|
53
|
+
*/
|
|
54
|
+
export interface DiskCapabilities {
|
|
55
|
+
/** Can issue signed, time-limited URLs ({@link Disk.getSignedUrl}). */
|
|
56
|
+
presign: boolean;
|
|
57
|
+
/** Supports native server-side multipart assembly ({@link MultipartUploadDisk}). */
|
|
58
|
+
multipart: boolean;
|
|
59
|
+
/** Can serve stable public URLs ({@link Disk.getUrl}). */
|
|
60
|
+
publicUrls: boolean;
|
|
61
|
+
/** Can enumerate keys under a prefix ({@link ExtendedDisk.list}). */
|
|
62
|
+
list: boolean;
|
|
63
|
+
}
|
|
64
|
+
/** Options for {@link ExtendedDisk.list}. */
|
|
65
|
+
export interface ListOptions {
|
|
66
|
+
/** Delimiter that rolls deeper keys up into folder prefixes. Default `'/'`. */
|
|
67
|
+
delimiter?: string;
|
|
68
|
+
/** Opaque pagination cursor returned by a previous {@link ListResult}. */
|
|
69
|
+
cursor?: string;
|
|
70
|
+
/** Max entries per page. */
|
|
71
|
+
limit?: number;
|
|
72
|
+
/**
|
|
73
|
+
* Override the disk's configured bucket (admin cross-bucket browse). Ignored by disks without a
|
|
74
|
+
* bucket concept.
|
|
75
|
+
*/
|
|
76
|
+
bucket?: string;
|
|
77
|
+
}
|
|
78
|
+
/** One file entry returned by {@link ExtendedDisk.list}. */
|
|
79
|
+
export interface ListEntry {
|
|
80
|
+
/** Full key relative to the bucket/root. */
|
|
81
|
+
key: string;
|
|
82
|
+
/** Last path segment (file name, no trailing slash). */
|
|
83
|
+
name: string;
|
|
84
|
+
sizeBytes: number | null;
|
|
85
|
+
lastModified: Date | null;
|
|
86
|
+
}
|
|
87
|
+
/** One page of a {@link ExtendedDisk.list} enumeration. */
|
|
88
|
+
export interface ListResult {
|
|
89
|
+
/** Sub-folder prefixes (each ends in the delimiter), from `CommonPrefixes`. */
|
|
90
|
+
folders: string[];
|
|
91
|
+
/** File entries directly under the prefix. */
|
|
92
|
+
files: ListEntry[];
|
|
93
|
+
/** Present when the page is truncated; pass back as {@link ListOptions.cursor}. */
|
|
94
|
+
cursor?: string;
|
|
95
|
+
}
|
|
96
|
+
/** Object metadata from {@link ExtendedDisk.stat} — size/content-type/last-modified without a body download. */
|
|
97
|
+
export interface DiskStat {
|
|
98
|
+
size: number;
|
|
99
|
+
contentType?: string | undefined;
|
|
100
|
+
lastModified?: Date | undefined;
|
|
101
|
+
}
|
|
102
|
+
/** Optional destination overrides for {@link ExtendedDisk.copy}/{@link ExtendedDisk.move}. */
|
|
103
|
+
export interface CopyOptions {
|
|
104
|
+
/** Copy into a different bucket than the disk's configured one (cross-bucket copy/move). */
|
|
105
|
+
toBucket?: string;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Optional add-on surface for disks that support richer object operations — cross-key/cross-bucket
|
|
109
|
+
* copy & move, batched deletes, prefix listing, and cheap metadata (`size`/`stat`) — plus a
|
|
110
|
+
* {@link DiskCapabilities} descriptor. Like {@link MultipartUploadDisk} it is kept OFF the base
|
|
111
|
+
* {@link Disk} contract on purpose (the in-memory/Drive disks don't provide it) and detected
|
|
112
|
+
* structurally at runtime via `isExtendedDisk`. The bundled `disks.s3()` disk implements it.
|
|
113
|
+
*/
|
|
114
|
+
export interface ExtendedDisk {
|
|
115
|
+
/** Coarse feature descriptor for this disk. */
|
|
116
|
+
readonly capabilities: DiskCapabilities;
|
|
117
|
+
/** Server-side copy an object to another key (and optionally another bucket). */
|
|
118
|
+
copy(from: string, to: string, options?: CopyOptions): Promise<void>;
|
|
119
|
+
/** Server-side move (copy then delete the source). Supports cross-bucket via {@link CopyOptions}. */
|
|
120
|
+
move(from: string, to: string, options?: CopyOptions): Promise<void>;
|
|
121
|
+
/** Delete many keys in as few round-trips as the backend allows. An empty array is a no-op. */
|
|
122
|
+
deleteMany(keys: string[]): Promise<void>;
|
|
123
|
+
/** Enumerate keys under a prefix, one page at a time. */
|
|
124
|
+
list(prefix: string, options?: ListOptions): Promise<ListResult>;
|
|
125
|
+
/** Object size in bytes, without downloading the body. */
|
|
126
|
+
size(key: string): Promise<number>;
|
|
127
|
+
/** Object metadata (size/content-type/last-modified), without downloading the body. */
|
|
128
|
+
stat(key: string): Promise<DiskStat>;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Optional add-on surface for disks that support native server-side multipart uploads (S3). It is
|
|
132
|
+
* kept OFF the base {@link Disk} contract on purpose — the in-memory/Drive disks don't provide it —
|
|
133
|
+
* and detected structurally at runtime via `isMultipartCapable`. Powers the two direct-S3 upload
|
|
134
|
+
* modes:
|
|
135
|
+
*
|
|
136
|
+
* - `proxy`: the client PUTs each part to the app, which forwards the bytes to S3 with
|
|
137
|
+
* {@link MultipartUploadDisk.uploadPart} (bytes flow through the backend).
|
|
138
|
+
* - `direct`: the app issues presigned part URLs with
|
|
139
|
+
* {@link MultipartUploadDisk.presignUploadPart}; the client uploads each part straight to S3 and
|
|
140
|
+
* the app assembles them with {@link MultipartUploadDisk.completeMultipartUpload}.
|
|
141
|
+
*/
|
|
142
|
+
export interface MultipartUploadDisk {
|
|
143
|
+
/** Begin a multipart upload; returns the S3 `UploadId` that ties the parts together. */
|
|
144
|
+
createMultipartUpload(key: string, options?: DiskWriteOptions): Promise<{
|
|
145
|
+
uploadId: string;
|
|
146
|
+
}>;
|
|
147
|
+
/** Upload one part's bytes server-side (the `proxy` path). Returns that part's number + ETag. */
|
|
148
|
+
uploadPart(key: string, uploadId: string, partNumber: number, body: Uint8Array): Promise<MultipartPart>;
|
|
149
|
+
/** Issue a presigned URL the client PUTs one part to directly (the `direct` path). */
|
|
150
|
+
presignUploadPart(key: string, uploadId: string, partNumber: number, expiresInSeconds: number): Promise<string>;
|
|
151
|
+
/** Assemble the uploaded parts into the final object. */
|
|
152
|
+
completeMultipartUpload(key: string, uploadId: string, parts: MultipartPart[]): Promise<void>;
|
|
153
|
+
/** Discard an in-flight multipart upload and free its parts. */
|
|
154
|
+
abortMultipartUpload(key: string, uploadId: string): Promise<void>;
|
|
155
|
+
}
|
|
38
156
|
/**
|
|
39
157
|
* Resolves a disk by name. In an AdonisJS app this is `(name) => drive.use(name)`; the
|
|
40
158
|
* provider builds it from the booted Drive manager. Tests pass a resolver over fake disks.
|
package/dist/src/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;;;;;;GASG;AACH,MAAM,WAAW,IAAI;IACnB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClF,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAC3C,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC1C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACvE,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CACjD;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;CACnC;AAED,MAAM,WAAW,gBAAgB;IAC/B,+FAA+F;IAC/F,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,YAAY,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;CACjC;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { Readable } from 'node:stream';
|
|
2
|
+
import type { StorageManager } from './storage_manager.js';
|
|
3
|
+
import type { MultipartPart } from './types.js';
|
|
4
|
+
import { type ResolvedUploadMode, type UploadMode } from './upload_mode.js';
|
|
5
|
+
export interface UploadManagerOptions {
|
|
6
|
+
storage: StorageManager;
|
|
7
|
+
/** Global default upload mode (`auto` when omitted). */
|
|
8
|
+
mode?: UploadMode | undefined;
|
|
9
|
+
/** Default part size in bytes for direct multipart uploads. Default 8 MiB. */
|
|
10
|
+
partSize?: number | undefined;
|
|
11
|
+
/** Lifetime of presigned part URLs, in seconds. Default 3600. */
|
|
12
|
+
presignTtlSeconds?: number | undefined;
|
|
13
|
+
/** Emit `upload.*` diagnostics events (default true). */
|
|
14
|
+
emitDiagnostics?: boolean | undefined;
|
|
15
|
+
}
|
|
16
|
+
export interface InitiateDirectUploadInput {
|
|
17
|
+
/** Disk name; defaults to the manager's default disk. */
|
|
18
|
+
disk?: string;
|
|
19
|
+
/** Object key to write. Resolve this server-side (never trust the client) in your route. */
|
|
20
|
+
key: string;
|
|
21
|
+
/** Content-Type stamped on the final object. */
|
|
22
|
+
contentType?: string;
|
|
23
|
+
/** Public/private visibility for the final object. */
|
|
24
|
+
visibility?: 'public' | 'private';
|
|
25
|
+
/** Total size in bytes — used to pre-compute how many part URLs to hand back. */
|
|
26
|
+
size?: number;
|
|
27
|
+
/** Override the part size for this upload. */
|
|
28
|
+
partSize?: number;
|
|
29
|
+
}
|
|
30
|
+
export interface DirectUploadCreated {
|
|
31
|
+
uploadId: string;
|
|
32
|
+
key: string;
|
|
33
|
+
disk: string;
|
|
34
|
+
partSize: number;
|
|
35
|
+
parts: {
|
|
36
|
+
partNumber: number;
|
|
37
|
+
url: string;
|
|
38
|
+
}[];
|
|
39
|
+
}
|
|
40
|
+
export interface PresignPartInput {
|
|
41
|
+
disk?: string;
|
|
42
|
+
key: string;
|
|
43
|
+
uploadId: string;
|
|
44
|
+
partNumber: number;
|
|
45
|
+
}
|
|
46
|
+
export interface CompleteDirectUploadInput {
|
|
47
|
+
disk?: string;
|
|
48
|
+
key: string;
|
|
49
|
+
uploadId: string;
|
|
50
|
+
parts: MultipartPart[];
|
|
51
|
+
}
|
|
52
|
+
export interface AbortDirectUploadInput {
|
|
53
|
+
disk?: string;
|
|
54
|
+
key: string;
|
|
55
|
+
uploadId: string;
|
|
56
|
+
}
|
|
57
|
+
export interface ProxyUploadInput {
|
|
58
|
+
disk?: string;
|
|
59
|
+
key: string;
|
|
60
|
+
contents: Uint8Array | Readable;
|
|
61
|
+
contentType?: string;
|
|
62
|
+
visibility?: 'public' | 'private';
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Coordinates the two direct-S3 upload strategies over a {@link StorageManager}:
|
|
66
|
+
*
|
|
67
|
+
* - **proxy** — the client sends bytes to the app, which streams them to the disk with
|
|
68
|
+
* `put`/`putStream` (works on any disk). {@link proxyUpload} / {@link proxyUploadPart}.
|
|
69
|
+
* - **direct** — for multipart-capable disks (S3), the app hands the client presigned part URLs so
|
|
70
|
+
* the client uploads straight to S3, then assembles the parts. {@link initiateDirect} /
|
|
71
|
+
* {@link presignPart} / {@link completeDirect} / {@link abortDirect}.
|
|
72
|
+
*
|
|
73
|
+
* This is the framework-free core; the AdonisJS provider exposes it over HTTP routes and
|
|
74
|
+
* {@link MediaManager} delegates its `*DirectUpload` convenience methods here.
|
|
75
|
+
*/
|
|
76
|
+
export declare class UploadManager {
|
|
77
|
+
private readonly storage;
|
|
78
|
+
private readonly mode;
|
|
79
|
+
private readonly partSize;
|
|
80
|
+
private readonly presignTtlSeconds;
|
|
81
|
+
private readonly emitDiagnostics;
|
|
82
|
+
constructor(options: UploadManagerOptions);
|
|
83
|
+
/**
|
|
84
|
+
* Resolve the effective mode for a disk (`proxy`/`direct`), honouring the per-call override, the
|
|
85
|
+
* configured global default, and the disk's multipart capability. Throws when `direct` is forced
|
|
86
|
+
* on a disk that can't do multipart.
|
|
87
|
+
*/
|
|
88
|
+
resolveMode(input?: {
|
|
89
|
+
disk?: string;
|
|
90
|
+
mode?: UploadMode;
|
|
91
|
+
}): ResolvedUploadMode;
|
|
92
|
+
/** Begin a direct multipart upload and return a presigned URL per part for the client to PUT to. */
|
|
93
|
+
initiateDirect(input: InitiateDirectUploadInput): Promise<DirectUploadCreated>;
|
|
94
|
+
/** Issue a presigned URL for a single (e.g. retried) part of an in-flight direct upload. */
|
|
95
|
+
presignPart(input: PresignPartInput): Promise<{
|
|
96
|
+
url: string;
|
|
97
|
+
}>;
|
|
98
|
+
/** Assemble the uploaded parts into the final object, closing out the direct upload. */
|
|
99
|
+
completeDirect(input: CompleteDirectUploadInput): Promise<{
|
|
100
|
+
key: string;
|
|
101
|
+
disk: string;
|
|
102
|
+
}>;
|
|
103
|
+
/** Discard an in-flight direct upload and free its parts on S3. */
|
|
104
|
+
abortDirect(input: AbortDirectUploadInput): Promise<void>;
|
|
105
|
+
/**
|
|
106
|
+
* Proxy upload: stream the whole body through the app to the disk in a single write. Uses the
|
|
107
|
+
* disk's `putStream` for a `Readable` (no in-memory buffering) and falls back to `put` for bytes.
|
|
108
|
+
* Works on any disk — no multipart capability required.
|
|
109
|
+
*/
|
|
110
|
+
proxyUpload(input: ProxyUploadInput): Promise<{
|
|
111
|
+
key: string;
|
|
112
|
+
disk: string;
|
|
113
|
+
}>;
|
|
114
|
+
/**
|
|
115
|
+
* Proxy a single multipart part's bytes server-side (client PUTs the part to the app, the app
|
|
116
|
+
* forwards it to S3). Pairs with {@link initiateDirect}/{@link completeDirect} for a
|
|
117
|
+
* proxy-through-backend multipart flow on a multipart-capable disk.
|
|
118
|
+
*/
|
|
119
|
+
proxyUploadPart(input: {
|
|
120
|
+
disk?: string;
|
|
121
|
+
key: string;
|
|
122
|
+
uploadId: string;
|
|
123
|
+
partNumber: number;
|
|
124
|
+
body: Uint8Array;
|
|
125
|
+
}): Promise<MultipartPart>;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=upload_manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload_manager.d.ts","sourceRoot":"","sources":["../../src/upload_manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { publishMedia } from './diagnostics.js';
|
|
2
|
+
import { UploadNotSupportedError } from './errors.js';
|
|
3
|
+
import { isMultipartCapable } from './multipart.js';
|
|
4
|
+
import { resolveUploadMode } from './upload_mode.js';
|
|
5
|
+
/** Default size of each multipart part when the caller gives none (8 MiB — S3's part minimum is 5 MiB). */
|
|
6
|
+
const DEFAULT_PART_SIZE = 8 * 1024 * 1024;
|
|
7
|
+
/** Default lifetime of a presigned part URL (1 hour). */
|
|
8
|
+
const DEFAULT_PRESIGN_TTL_SECONDS = 3600;
|
|
9
|
+
/**
|
|
10
|
+
* Coordinates the two direct-S3 upload strategies over a {@link StorageManager}:
|
|
11
|
+
*
|
|
12
|
+
* - **proxy** — the client sends bytes to the app, which streams them to the disk with
|
|
13
|
+
* `put`/`putStream` (works on any disk). {@link proxyUpload} / {@link proxyUploadPart}.
|
|
14
|
+
* - **direct** — for multipart-capable disks (S3), the app hands the client presigned part URLs so
|
|
15
|
+
* the client uploads straight to S3, then assembles the parts. {@link initiateDirect} /
|
|
16
|
+
* {@link presignPart} / {@link completeDirect} / {@link abortDirect}.
|
|
17
|
+
*
|
|
18
|
+
* This is the framework-free core; the AdonisJS provider exposes it over HTTP routes and
|
|
19
|
+
* {@link MediaManager} delegates its `*DirectUpload` convenience methods here.
|
|
20
|
+
*/
|
|
21
|
+
export class UploadManager {
|
|
22
|
+
storage;
|
|
23
|
+
mode;
|
|
24
|
+
partSize;
|
|
25
|
+
presignTtlSeconds;
|
|
26
|
+
emitDiagnostics;
|
|
27
|
+
constructor(options) {
|
|
28
|
+
this.storage = options.storage;
|
|
29
|
+
this.mode = options.mode ?? 'auto';
|
|
30
|
+
this.partSize = options.partSize ?? DEFAULT_PART_SIZE;
|
|
31
|
+
this.presignTtlSeconds = options.presignTtlSeconds ?? DEFAULT_PRESIGN_TTL_SECONDS;
|
|
32
|
+
this.emitDiagnostics = options.emitDiagnostics ?? true;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Resolve the effective mode for a disk (`proxy`/`direct`), honouring the per-call override, the
|
|
36
|
+
* configured global default, and the disk's multipart capability. Throws when `direct` is forced
|
|
37
|
+
* on a disk that can't do multipart.
|
|
38
|
+
*/
|
|
39
|
+
resolveMode(input) {
|
|
40
|
+
const diskName = input?.disk ?? this.storage.defaultDisk;
|
|
41
|
+
const disk = this.storage.disk(diskName);
|
|
42
|
+
return resolveUploadMode({ global: this.mode, perCall: input?.mode }, isMultipartCapable(disk), diskName);
|
|
43
|
+
}
|
|
44
|
+
// --- direct mode ---------------------------------------------------------------------------
|
|
45
|
+
/** Begin a direct multipart upload and return a presigned URL per part for the client to PUT to. */
|
|
46
|
+
async initiateDirect(input) {
|
|
47
|
+
const diskName = input.disk ?? this.storage.defaultDisk;
|
|
48
|
+
const disk = this.storage.disk(diskName);
|
|
49
|
+
if (!isMultipartCapable(disk))
|
|
50
|
+
throw new UploadNotSupportedError(diskName);
|
|
51
|
+
const putOptions = input.contentType !== undefined || input.visibility !== undefined
|
|
52
|
+
? {
|
|
53
|
+
...(input.contentType !== undefined ? { contentType: input.contentType } : {}),
|
|
54
|
+
...(input.visibility !== undefined ? { visibility: input.visibility } : {}),
|
|
55
|
+
}
|
|
56
|
+
: undefined;
|
|
57
|
+
const { uploadId } = await disk.createMultipartUpload(input.key, putOptions);
|
|
58
|
+
const partSize = input.partSize ?? this.partSize;
|
|
59
|
+
const partCount = input.size !== undefined ? Math.max(1, Math.ceil(input.size / partSize)) : 1;
|
|
60
|
+
const parts = [];
|
|
61
|
+
for (let n = 1; n <= partCount; n++) {
|
|
62
|
+
const url = await disk.presignUploadPart(input.key, uploadId, n, this.presignTtlSeconds);
|
|
63
|
+
parts.push({ partNumber: n, url });
|
|
64
|
+
}
|
|
65
|
+
if (this.emitDiagnostics) {
|
|
66
|
+
publishMedia('upload.start', {
|
|
67
|
+
id: uploadId,
|
|
68
|
+
disk: diskName,
|
|
69
|
+
key: input.key,
|
|
70
|
+
mode: 'direct',
|
|
71
|
+
size: input.size,
|
|
72
|
+
contentType: input.contentType,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return { uploadId, key: input.key, disk: diskName, partSize, parts };
|
|
76
|
+
}
|
|
77
|
+
/** Issue a presigned URL for a single (e.g. retried) part of an in-flight direct upload. */
|
|
78
|
+
async presignPart(input) {
|
|
79
|
+
const diskName = input.disk ?? this.storage.defaultDisk;
|
|
80
|
+
const disk = this.storage.disk(diskName);
|
|
81
|
+
if (!isMultipartCapable(disk))
|
|
82
|
+
throw new UploadNotSupportedError(diskName);
|
|
83
|
+
const url = await disk.presignUploadPart(input.key, input.uploadId, input.partNumber, this.presignTtlSeconds);
|
|
84
|
+
return { url };
|
|
85
|
+
}
|
|
86
|
+
/** Assemble the uploaded parts into the final object, closing out the direct upload. */
|
|
87
|
+
async completeDirect(input) {
|
|
88
|
+
const diskName = input.disk ?? this.storage.defaultDisk;
|
|
89
|
+
const disk = this.storage.disk(diskName);
|
|
90
|
+
if (!isMultipartCapable(disk))
|
|
91
|
+
throw new UploadNotSupportedError(diskName);
|
|
92
|
+
await disk.completeMultipartUpload(input.key, input.uploadId, input.parts);
|
|
93
|
+
if (this.emitDiagnostics) {
|
|
94
|
+
publishMedia('upload.complete', { id: input.uploadId, disk: diskName, key: input.key });
|
|
95
|
+
}
|
|
96
|
+
return { key: input.key, disk: diskName };
|
|
97
|
+
}
|
|
98
|
+
/** Discard an in-flight direct upload and free its parts on S3. */
|
|
99
|
+
async abortDirect(input) {
|
|
100
|
+
const diskName = input.disk ?? this.storage.defaultDisk;
|
|
101
|
+
const disk = this.storage.disk(diskName);
|
|
102
|
+
if (!isMultipartCapable(disk))
|
|
103
|
+
throw new UploadNotSupportedError(diskName);
|
|
104
|
+
await disk.abortMultipartUpload(input.key, input.uploadId);
|
|
105
|
+
if (this.emitDiagnostics) {
|
|
106
|
+
publishMedia('upload.abort', { id: input.uploadId, disk: diskName, key: input.key });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// --- proxy mode ----------------------------------------------------------------------------
|
|
110
|
+
/**
|
|
111
|
+
* Proxy upload: stream the whole body through the app to the disk in a single write. Uses the
|
|
112
|
+
* disk's `putStream` for a `Readable` (no in-memory buffering) and falls back to `put` for bytes.
|
|
113
|
+
* Works on any disk — no multipart capability required.
|
|
114
|
+
*/
|
|
115
|
+
async proxyUpload(input) {
|
|
116
|
+
const diskName = input.disk ?? this.storage.defaultDisk;
|
|
117
|
+
const disk = this.storage.disk(diskName);
|
|
118
|
+
const writeOptions = input.contentType !== undefined || input.visibility !== undefined
|
|
119
|
+
? {
|
|
120
|
+
...(input.contentType !== undefined ? { contentType: input.contentType } : {}),
|
|
121
|
+
...(input.visibility !== undefined ? { visibility: input.visibility } : {}),
|
|
122
|
+
}
|
|
123
|
+
: undefined;
|
|
124
|
+
if (input.contents instanceof Uint8Array) {
|
|
125
|
+
await disk.put(input.key, input.contents, writeOptions);
|
|
126
|
+
}
|
|
127
|
+
else if (disk.putStream) {
|
|
128
|
+
await disk.putStream(input.key, input.contents, writeOptions);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
// A disk without streaming support: buffer the stream, then `put`.
|
|
132
|
+
const chunks = [];
|
|
133
|
+
for await (const chunk of input.contents)
|
|
134
|
+
chunks.push(Buffer.from(chunk));
|
|
135
|
+
await disk.put(input.key, new Uint8Array(Buffer.concat(chunks)), writeOptions);
|
|
136
|
+
}
|
|
137
|
+
return { key: input.key, disk: diskName };
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Proxy a single multipart part's bytes server-side (client PUTs the part to the app, the app
|
|
141
|
+
* forwards it to S3). Pairs with {@link initiateDirect}/{@link completeDirect} for a
|
|
142
|
+
* proxy-through-backend multipart flow on a multipart-capable disk.
|
|
143
|
+
*/
|
|
144
|
+
async proxyUploadPart(input) {
|
|
145
|
+
const diskName = input.disk ?? this.storage.defaultDisk;
|
|
146
|
+
const disk = this.storage.disk(diskName);
|
|
147
|
+
if (!isMultipartCapable(disk))
|
|
148
|
+
throw new UploadNotSupportedError(diskName, 'proxy multipart part');
|
|
149
|
+
return disk.uploadPart(input.key, input.uploadId, input.partNumber, input.body);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=upload_manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload_manager.js","sourceRoot":"","sources":["../../src/upload_manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAGpD,OAAO,EAA4C,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE/F,2GAA2G;AAC3G,MAAM,iBAAiB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAC1C,yDAAyD;AACzD,MAAM,2BAA2B,GAAG,IAAI,CAAC;AAiEzC;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,aAAa;IACP,OAAO,CAAiB;IACxB,IAAI,CAAa;IACjB,QAAQ,CAAS;IACjB,iBAAiB,CAAS;IAC1B,eAAe,CAAU;IAE1C,YAAY,OAA6B;QACvC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC;QACnC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,iBAAiB,CAAC;QACtD,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,IAAI,2BAA2B,CAAC;QAClF,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,IAAI,CAAC;IACzD,CAAC;IAED;;;;OAIG;IACH,WAAW,CAAC,KAA4C;QACtD,MAAM,QAAQ,GAAG,KAAK,EAAE,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC;QACzD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzC,OAAO,iBAAiB,CACtB,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAC3C,kBAAkB,CAAC,IAAI,CAAC,EACxB,QAAQ,CACT,CAAC;IACJ,CAAC;IAED,8FAA8F;IAE9F,oGAAoG;IACpG,KAAK,CAAC,cAAc,CAAC,KAAgC;QACnD,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;QAE3E,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;QAEhB,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QAE7E,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC;QACjD,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAE/F,MAAM,KAAK,GAA0C,EAAE,CAAC;QACxD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,GAAG,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;YACzF,KAAK,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;QACrC,CAAC;QAED,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,YAAY,CAAC,cAAc,EAAE;gBAC3B,EAAE,EAAE,QAAQ;gBACZ,IAAI,EAAE,QAAQ;gBACd,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,WAAW,EAAE,KAAK,CAAC,WAAW;aAC/B,CAAC,CAAC;QACL,CAAC;QAED,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IACvE,CAAC;IAED,4FAA4F;IAC5F,KAAK,CAAC,WAAW,CAAC,KAAuB;QACvC,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;QAE3E,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,iBAAiB,CACtC,KAAK,CAAC,GAAG,EACT,KAAK,CAAC,QAAQ,EACd,KAAK,CAAC,UAAU,EAChB,IAAI,CAAC,iBAAiB,CACvB,CAAC;QACF,OAAO,EAAE,GAAG,EAAE,CAAC;IACjB,CAAC;IAED,wFAAwF;IACxF,KAAK,CAAC,cAAc,CAAC,KAAgC;QACnD,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;QAE3E,MAAM,IAAI,CAAC,uBAAuB,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAE3E,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,YAAY,CAAC,iBAAiB,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;QAC1F,CAAC;QACD,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC5C,CAAC;IAED,mEAAmE;IACnE,KAAK,CAAC,WAAW,CAAC,KAA6B;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;QAE3E,MAAM,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QAE3D,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,YAAY,CAAC,cAAc,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;QACvF,CAAC;IACH,CAAC;IAED,8FAA8F;IAE9F;;;;OAIG;IACH,KAAK,CAAC,WAAW,CAAC,KAAuB;QACvC,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;QAEzC,MAAM,YAAY,GAChB,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;QAEhB,IAAI,KAAK,CAAC,QAAQ,YAAY,UAAU,EAAE,CAAC;YACzC,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QAC1D,CAAC;aAAM,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YAC1B,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QAChE,CAAC;aAAM,CAAC;YACN,mEAAmE;YACnE,MAAM,MAAM,GAAa,EAAE,CAAC;YAC5B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,KAAK,CAAC,QAAQ;gBAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YAC1E,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;QACjF,CAAC;QAED,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC5C,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,eAAe,CAAC,KAMrB;QACC,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;YAC3B,MAAM,IAAI,uBAAuB,CAAC,QAAQ,EAAE,sBAAsB,CAAC,CAAC;QAEtE,OAAO,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAClF,CAAC;CACF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Requested upload strategy. `auto` picks `direct` when the disk is multipart-capable, else `proxy`. */
|
|
2
|
+
export type UploadMode = 'auto' | 'proxy' | 'direct';
|
|
3
|
+
/** The concrete strategy after resolution — `auto` has been collapsed to one of these. */
|
|
4
|
+
export type ResolvedUploadMode = 'proxy' | 'direct';
|
|
5
|
+
/**
|
|
6
|
+
* The upload mode configured at each level. The most specific wins:
|
|
7
|
+
* per-call → per-disk → global → `auto`.
|
|
8
|
+
*/
|
|
9
|
+
export interface UploadModeLevels {
|
|
10
|
+
/** Global default from `config/media.ts` (`uploads.mode`). */
|
|
11
|
+
global?: UploadMode | undefined;
|
|
12
|
+
/** A per-disk override (unused by the bundled disks; reserved for future per-disk config). */
|
|
13
|
+
perDisk?: UploadMode | undefined;
|
|
14
|
+
/** A per-call override passed to the upload method. */
|
|
15
|
+
perCall?: UploadMode | undefined;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Decide whether an upload flows through the backend (`proxy` — the app streams bytes to the disk)
|
|
19
|
+
* or straight to the disk (`direct` — the app issues presigned multipart part URLs and the client
|
|
20
|
+
* uploads to S3 itself).
|
|
21
|
+
*
|
|
22
|
+
* - `proxy`: always allowed (every disk can accept bytes through `put`/`putStream`).
|
|
23
|
+
* - `direct`: requires a multipart-capable disk; otherwise throws {@link UploadNotSupportedError}.
|
|
24
|
+
* - `auto`: `direct` when the disk is multipart-capable, else `proxy`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveUploadMode(levels: UploadModeLevels, directCapable: boolean, diskName?: string): ResolvedUploadMode;
|
|
27
|
+
//# sourceMappingURL=upload_mode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload_mode.d.ts","sourceRoot":"","sources":["../../src/upload_mode.ts"],"names":[],"mappings":"AAEA,yGAAyG;AACzG,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAErD,0FAA0F;AAC1F,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEpD;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,8DAA8D;IAC9D,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAChC,8FAA8F;IAC9F,OAAO,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IACjC,uDAAuD;IACvD,OAAO,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CAClC;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,gBAAgB,EACxB,aAAa,EAAE,OAAO,EACtB,QAAQ,SAAS,GAChB,kBAAkB,CASpB"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { UploadNotSupportedError } from './errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* Decide whether an upload flows through the backend (`proxy` — the app streams bytes to the disk)
|
|
4
|
+
* or straight to the disk (`direct` — the app issues presigned multipart part URLs and the client
|
|
5
|
+
* uploads to S3 itself).
|
|
6
|
+
*
|
|
7
|
+
* - `proxy`: always allowed (every disk can accept bytes through `put`/`putStream`).
|
|
8
|
+
* - `direct`: requires a multipart-capable disk; otherwise throws {@link UploadNotSupportedError}.
|
|
9
|
+
* - `auto`: `direct` when the disk is multipart-capable, else `proxy`.
|
|
10
|
+
*/
|
|
11
|
+
export function resolveUploadMode(levels, directCapable, diskName = 'disk') {
|
|
12
|
+
const mode = levels.perCall ?? levels.perDisk ?? levels.global ?? 'auto';
|
|
13
|
+
if (mode === 'proxy')
|
|
14
|
+
return 'proxy';
|
|
15
|
+
if (mode === 'direct') {
|
|
16
|
+
if (!directCapable)
|
|
17
|
+
throw new UploadNotSupportedError(diskName);
|
|
18
|
+
return 'direct';
|
|
19
|
+
}
|
|
20
|
+
return directCapable ? 'direct' : 'proxy';
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=upload_mode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload_mode.js","sourceRoot":"","sources":["../../src/upload_mode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAqBtD;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAwB,EACxB,aAAsB,EACtB,QAAQ,GAAG,MAAM;IAEjB,MAAM,IAAI,GAAe,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC;IAErF,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,OAAO,CAAC;IACrC,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,IAAI,CAAC,aAAa;YAAE,MAAM,IAAI,uBAAuB,CAAC,QAAQ,CAAC,CAAC;QAChE,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,OAAO,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { ApplicationService } from '@adonisjs/core/types';
|
|
2
|
+
import type { UploadSessionStore } from '../resumable_upload.js';
|
|
3
|
+
/**
|
|
4
|
+
* Runtime context an {@link UploadSessionStoreFactory} thunk receives when the media provider builds
|
|
5
|
+
* the configured session store at boot. Carries the booted application so a driver can resolve a
|
|
6
|
+
* peer's service (the Lucid `db`).
|
|
7
|
+
*/
|
|
8
|
+
export interface UploadSessionStoreContext {
|
|
9
|
+
app: ApplicationService;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A configured resumable session store: a lazy thunk the media provider calls at boot to build the
|
|
13
|
+
* {@link UploadSessionStore}. Each factory imports its peer (`@adonisjs/lucid`) inside the thunk, so
|
|
14
|
+
* the driver only loads when actually selected — keeping that package optional.
|
|
15
|
+
*/
|
|
16
|
+
export type UploadSessionStoreFactory = (ctx: UploadSessionStoreContext) => Promise<UploadSessionStore>;
|
|
17
|
+
/** Options for the Lucid-backed resumable session store. */
|
|
18
|
+
export interface LucidUploadSessionStoreConfig {
|
|
19
|
+
/** Lucid connection name to use. Defaults to the `Database` default connection. */
|
|
20
|
+
connection?: string;
|
|
21
|
+
/** Sessions table name. Default `media_upload_sessions`. */
|
|
22
|
+
table?: string;
|
|
23
|
+
/** Parts side-index table name. Default `media_upload_parts`. */
|
|
24
|
+
partsTable?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The resumable session-store factory namespace used in `config/media.ts` (mirrors {@link stores}):
|
|
28
|
+
*
|
|
29
|
+
* ```ts
|
|
30
|
+
* import { defineConfig, uploadSessions } from '@adonis-agora/media'
|
|
31
|
+
*
|
|
32
|
+
* export default defineConfig({
|
|
33
|
+
* uploads: {
|
|
34
|
+
* resumable: {
|
|
35
|
+
* store: 'lucid',
|
|
36
|
+
* stores: {
|
|
37
|
+
* memory: uploadSessions.memory(),
|
|
38
|
+
* lucid: uploadSessions.lucid({ connection: 'pg' }),
|
|
39
|
+
* },
|
|
40
|
+
* routes: { enabled: true },
|
|
41
|
+
* },
|
|
42
|
+
* },
|
|
43
|
+
* })
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export declare const uploadSessions: {
|
|
47
|
+
/** In-memory session store — single-process, non-durable. Handy for tests and scratch apps. */
|
|
48
|
+
memory(): UploadSessionStoreFactory;
|
|
49
|
+
/** Persist resumable sessions in SQL via `@adonisjs/lucid`. */
|
|
50
|
+
lucid(config?: LucidUploadSessionStoreConfig): UploadSessionStoreFactory;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Build the resumable session store selected by `config.store` from the `config.stores` map. A named
|
|
54
|
+
* store with no matching factory THROWS {@link UploadSessionStoreNotConfiguredError} (a non-durable
|
|
55
|
+
* fallback would silently lose resumable state); only the zero-config path (no `store` named)
|
|
56
|
+
* resolves to the in-memory store.
|
|
57
|
+
*/
|
|
58
|
+
export declare function resolveUploadSessionStore(config: {
|
|
59
|
+
store?: string;
|
|
60
|
+
stores?: Record<string, UploadSessionStoreFactory>;
|
|
61
|
+
}, ctx: UploadSessionStoreContext): Promise<UploadSessionStore>;
|
|
62
|
+
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/upload_sessions/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE;;;;GAIG;AACH,MAAM,WAAW,yBAAyB;IACxC,GAAG,EAAE,kBAAkB,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,MAAM,yBAAyB,GAAG,CACtC,GAAG,EAAE,yBAAyB,KAC3B,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAEjC,4DAA4D;AAC5D,MAAM,WAAW,6BAA6B;IAC5C,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,cAAc;IACzB,+FAA+F;cACrF,yBAAyB;IASnC,+DAA+D;mBACjD,6BAA6B,GAAQ,yBAAyB;CAW7E,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAAA;CAAE,EAC9E,GAAG,EAAE,yBAAyB,GAC7B,OAAO,CAAC,kBAAkB,CAAC,CAW7B"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { UploadSessionStoreNotConfiguredError } from '../errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* The resumable session-store factory namespace used in `config/media.ts` (mirrors {@link stores}):
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { defineConfig, uploadSessions } from '@adonis-agora/media'
|
|
7
|
+
*
|
|
8
|
+
* export default defineConfig({
|
|
9
|
+
* uploads: {
|
|
10
|
+
* resumable: {
|
|
11
|
+
* store: 'lucid',
|
|
12
|
+
* stores: {
|
|
13
|
+
* memory: uploadSessions.memory(),
|
|
14
|
+
* lucid: uploadSessions.lucid({ connection: 'pg' }),
|
|
15
|
+
* },
|
|
16
|
+
* routes: { enabled: true },
|
|
17
|
+
* },
|
|
18
|
+
* },
|
|
19
|
+
* })
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export const uploadSessions = {
|
|
23
|
+
/** In-memory session store — single-process, non-durable. Handy for tests and scratch apps. */
|
|
24
|
+
memory() {
|
|
25
|
+
return async () => {
|
|
26
|
+
const { InMemoryUploadSessionStore } = await import('../testing/in_memory_upload_session_store.js');
|
|
27
|
+
return new InMemoryUploadSessionStore();
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
/** Persist resumable sessions in SQL via `@adonisjs/lucid`. */
|
|
31
|
+
lucid(config = {}) {
|
|
32
|
+
return async () => {
|
|
33
|
+
const db = (await import('@adonisjs/lucid/services/db')).default;
|
|
34
|
+
const { LucidUploadSessionStore } = await import('./lucid.js');
|
|
35
|
+
return new LucidUploadSessionStore(db, {
|
|
36
|
+
...(config.connection !== undefined ? { connectionName: config.connection } : {}),
|
|
37
|
+
...(config.table !== undefined ? { table: config.table } : {}),
|
|
38
|
+
...(config.partsTable !== undefined ? { partsTable: config.partsTable } : {}),
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Build the resumable session store selected by `config.store` from the `config.stores` map. A named
|
|
45
|
+
* store with no matching factory THROWS {@link UploadSessionStoreNotConfiguredError} (a non-durable
|
|
46
|
+
* fallback would silently lose resumable state); only the zero-config path (no `store` named)
|
|
47
|
+
* resolves to the in-memory store.
|
|
48
|
+
*/
|
|
49
|
+
export async function resolveUploadSessionStore(config, ctx) {
|
|
50
|
+
const name = config.store;
|
|
51
|
+
if (name) {
|
|
52
|
+
const factory = config.stores?.[name];
|
|
53
|
+
if (!factory)
|
|
54
|
+
throw new UploadSessionStoreNotConfiguredError(name);
|
|
55
|
+
return factory(ctx);
|
|
56
|
+
}
|
|
57
|
+
const { InMemoryUploadSessionStore } = await import('../testing/in_memory_upload_session_store.js');
|
|
58
|
+
return new InMemoryUploadSessionStore();
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../../src/upload_sessions/factory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oCAAoC,EAAE,MAAM,cAAc,CAAC;AA+BpE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,+FAA+F;IAC/F,MAAM;QACJ,OAAO,KAAK,IAAI,EAAE;YAChB,MAAM,EAAE,0BAA0B,EAAE,GAAG,MAAM,MAAM,CACjD,8CAA8C,CAC/C,CAAC;YACF,OAAO,IAAI,0BAA0B,EAAE,CAAC;QAC1C,CAAC,CAAC;IACJ,CAAC;IAED,+DAA+D;IAC/D,KAAK,CAAC,SAAwC,EAAE;QAC9C,OAAO,KAAK,IAAI,EAAE;YAChB,MAAM,EAAE,GAAG,CAAC,MAAM,MAAM,CAAC,6BAA6B,CAAC,CAAC,CAAC,OAAO,CAAC;YACjE,MAAM,EAAE,uBAAuB,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;YAC/D,OAAO,IAAI,uBAAuB,CAAC,EAAE,EAAE;gBACrC,GAAG,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjF,GAAG,CAAC,MAAM,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC9D,GAAG,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC9E,CAAC,CAAC;QACL,CAAC,CAAC;IACJ,CAAC;CACF,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,MAA8E,EAC9E,GAA8B;IAE9B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC;IAC1B,IAAI,IAAI,EAAE,CAAC;QACT,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,oCAAoC,CAAC,IAAI,CAAC,CAAC;QACnE,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC;IACtB,CAAC;IACD,MAAM,EAAE,0BAA0B,EAAE,GAAG,MAAM,MAAM,CACjD,8CAA8C,CAC/C,CAAC;IACF,OAAO,IAAI,0BAA0B,EAAE,CAAC;AAC1C,CAAC"}
|