@burojs/file-storage 0.2.0 → 0.5.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/README.md +1 -1
- package/dist/index.cjs +808 -822
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +321 -300
- package/dist/index.d.ts +321 -300
- package/dist/index.js +807 -820
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { AppRuntime, Plugin } from "@burojs/core";
|
|
2
|
+
//#region src/types.d.ts
|
|
3
3
|
/** How a file's bytes travel relative to the record mutation. */
|
|
4
4
|
type UploadPipeline = 'immediate' | 'fused' | 'handshake' | 'resumable';
|
|
5
5
|
/**
|
|
@@ -30,51 +30,51 @@ type OrphanPolicy = 'best-effort' | 'keep';
|
|
|
30
30
|
* so, rather than leaving the consumer to guess.
|
|
31
31
|
*/
|
|
32
32
|
type ResolvedUrl = string | {
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
url: string;
|
|
34
|
+
expiresAt: number;
|
|
35
35
|
};
|
|
36
36
|
/** The framework's in-memory normal form for one file. */
|
|
37
37
|
interface FileRef {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
38
|
+
/** Client-side identity. Present from the moment the file is picked. */
|
|
39
|
+
localId: string;
|
|
40
|
+
/**
|
|
41
|
+
* Storage identity — id, key, path or filename, whichever this backend
|
|
42
|
+
* uses. Absent until the bytes have landed, and always absent for `fused`
|
|
43
|
+
* until the mutation succeeds.
|
|
44
|
+
*/
|
|
45
|
+
handle?: string;
|
|
46
|
+
/** Raw bytes not yet sent. Present for `fused` and for `defer: true`. */
|
|
47
|
+
pending?: File;
|
|
48
|
+
name?: string;
|
|
49
|
+
mime?: string;
|
|
50
|
+
size?: number;
|
|
51
|
+
width?: number;
|
|
52
|
+
height?: number;
|
|
53
|
+
/**
|
|
54
|
+
* Metadata that belongs to THIS REFERENCE rather than to the file — a
|
|
55
|
+
* Sanity-style hotspot/crop, an alt text that differs per usage, a sort
|
|
56
|
+
* position. Opaque to the framework; carried through to the renderer.
|
|
57
|
+
*/
|
|
58
|
+
usage?: Record<string, unknown>;
|
|
59
|
+
/**
|
|
60
|
+
* The original column entry this ref was read from. Absent for newly added
|
|
61
|
+
* files.
|
|
62
|
+
*
|
|
63
|
+
* ⚠️ `write` MUST merge onto this rather than rebuild the value. A Strapi
|
|
64
|
+
* media object carries `provider_metadata`, `createdAt`, `caption` and more;
|
|
65
|
+
* reading four known keys and writing four keys back DELETES the rest.
|
|
66
|
+
*/
|
|
67
|
+
raw?: unknown;
|
|
68
68
|
}
|
|
69
69
|
interface MapperCtx {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
70
|
+
/** Registry name of the resource being read or written. */
|
|
71
|
+
resource: string;
|
|
72
|
+
/** Field name this column belongs to. */
|
|
73
|
+
field: string;
|
|
74
|
+
/** Declared cardinality — a single-value mapper must not emit an array. */
|
|
75
|
+
multiple: boolean;
|
|
76
|
+
/** The record the column belongs to. Absent on create. */
|
|
77
|
+
record?: Record<string, unknown>;
|
|
78
78
|
}
|
|
79
79
|
/**
|
|
80
80
|
* Translates between the record's column and the normalised form.
|
|
@@ -87,88 +87,91 @@ interface MapperCtx {
|
|
|
87
87
|
* only an id.
|
|
88
88
|
*/
|
|
89
89
|
interface FileMapper<TRead = unknown, TWrite = TRead> {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
90
|
+
read(column: TRead, ctx: MapperCtx): FileRef[];
|
|
91
|
+
/**
|
|
92
|
+
* `prev` is not decoration: some backends update a multi-value field by
|
|
93
|
+
* DIFF rather than replacement (PocketBase uses `field+` to add and
|
|
94
|
+
* `field-` to remove), and a diff cannot be computed without the old value.
|
|
95
|
+
*/
|
|
96
|
+
write(next: FileRef[], prev: FileRef[], ctx: MapperCtx): TWrite;
|
|
97
|
+
/**
|
|
98
|
+
* Declares that `write` carries a `pending`-only ref (no `handle` yet)
|
|
99
|
+
* forward into its output — typically by embedding the raw `File` itself —
|
|
100
|
+
* rather than silently omitting it.
|
|
101
|
+
*
|
|
102
|
+
* This matters because a ref on a `fused`-pipeline storage NEVER has a
|
|
103
|
+
* `handle` before the record mutation runs: the bytes ride the mutation
|
|
104
|
+
* itself, so `pending` is all a freshly-added file ever has until the
|
|
105
|
+
* backend accepts it. A mapper that only knows how to write a `handle`
|
|
106
|
+
* (the ordinary case — `richImage`, `junction`) silently drops that file
|
|
107
|
+
* with no error the moment it is paired with a `fused` adapter.
|
|
108
|
+
* `assertFileStorageWiring` refuses that pairing at boot rather than
|
|
109
|
+
* discovering it via a vanished upload.
|
|
110
|
+
*
|
|
111
|
+
* Absent/false is the safe default: a mapper written assuming every ref
|
|
112
|
+
* already has a handle by the time `write` sees it should not claim
|
|
113
|
+
* otherwise. `scalarHandle` and `pocketbaseDiff` are the two mappers that
|
|
114
|
+
* set this.
|
|
115
|
+
*/
|
|
116
|
+
carriesPending?: boolean;
|
|
117
117
|
}
|
|
118
118
|
interface UploadCtx {
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
signal: AbortSignal;
|
|
120
|
+
onProgress: (loaded: number, total: number) => void;
|
|
121
121
|
}
|
|
122
122
|
interface ResolveCtx {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
123
|
+
variant?: {
|
|
124
|
+
width?: number;
|
|
125
|
+
height?: number;
|
|
126
|
+
};
|
|
127
|
+
disposition?: 'inline' | 'attachment';
|
|
128
|
+
resource?: string;
|
|
129
|
+
record?: Record<string, unknown>;
|
|
130
130
|
}
|
|
131
131
|
interface FileStorageAdapter {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
132
|
+
pipeline: UploadPipeline;
|
|
133
|
+
/** Used by every field that does not name its own mapper. */
|
|
134
|
+
defaultMapper: FileMapper;
|
|
135
|
+
/** Only meaningful when `pipeline === 'fused'`. */
|
|
136
|
+
encoding?: FusedEncoding;
|
|
137
|
+
/** Storage-level default; a field may override. */
|
|
138
|
+
orphans?: OrphanPolicy;
|
|
139
|
+
/** For a `fused` adapter this is a no-op — the bytes have nowhere to go yet. */
|
|
140
|
+
upload(file: File, ctx: UploadCtx): Promise<FileRef>;
|
|
141
|
+
resolveUrl(ref: FileRef, ctx: ResolveCtx): ResolvedUrl | Promise<ResolvedUrl>;
|
|
142
|
+
/**
|
|
143
|
+
* Batched metadata read. Implement only when the column holds a bare handle
|
|
144
|
+
* with no name/mime/size — otherwise the framework never calls it.
|
|
145
|
+
*
|
|
146
|
+
* Honesty rule: a field this backend cannot report is left ABSENT on the
|
|
147
|
+
* returned ref. Never guess, and never substitute a zero.
|
|
148
|
+
*/
|
|
149
|
+
stat?(refs: FileRef[]): Promise<FileRef[]>;
|
|
150
|
+
remove?(ref: FileRef): Promise<void>;
|
|
151
151
|
}
|
|
152
152
|
interface FileStorageRegistry {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
153
|
+
/** `undefined` resolves the `default` entry. Throws on an unknown name. */
|
|
154
|
+
get(name?: string): FileStorageAdapter;
|
|
155
|
+
has(name: string): boolean;
|
|
156
|
+
names(): string[];
|
|
157
|
+
/** True when any registered adapter carries its bytes inside the mutation. */
|
|
158
|
+
hasFused(): boolean;
|
|
159
159
|
}
|
|
160
|
-
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region src/registry.d.ts
|
|
161
162
|
/** Service name under which the registry is registered on the app. */
|
|
162
163
|
declare const FILE_STORAGE_SERVICE: "fileStorage";
|
|
163
164
|
declare function createFileStorageRegistry(map: Record<string, FileStorageAdapter>): FileStorageRegistry;
|
|
164
|
-
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region src/plugin.d.ts
|
|
165
167
|
/**
|
|
166
168
|
* Declare the app's file storages as a named map, mirroring the existing
|
|
167
169
|
* multi-data-provider convention. `default` is required; other keys are free
|
|
168
170
|
* form and are what a field names in `f.file({ fileStorage: '…' })`.
|
|
169
171
|
*/
|
|
170
172
|
declare function fileStoragePlugin(map: Record<string, FileStorageAdapter>): Plugin;
|
|
171
|
-
|
|
173
|
+
//#endregion
|
|
174
|
+
//#region src/mappers/ref.d.ts
|
|
172
175
|
/**
|
|
173
176
|
* Mint a ref with a fresh client-side identity.
|
|
174
177
|
*
|
|
@@ -177,7 +180,8 @@ declare function fileStoragePlugin(map: Record<string, FileStorageAdapter>): Plu
|
|
|
177
180
|
* readable and snapshots stable.
|
|
178
181
|
*/
|
|
179
182
|
declare function newRef(init?: Partial<FileRef>): FileRef;
|
|
180
|
-
|
|
183
|
+
//#endregion
|
|
184
|
+
//#region src/kind.d.ts
|
|
181
185
|
/**
|
|
182
186
|
* Kind by mime; the filename is consulted only when mime is unknown.
|
|
183
187
|
*
|
|
@@ -190,7 +194,8 @@ declare function newRef(init?: Partial<FileRef>): FileRef;
|
|
|
190
194
|
* of the stored string, which can never match a uuid or other opaque handle.
|
|
191
195
|
*/
|
|
192
196
|
declare function isImage(ref: FileRef): boolean;
|
|
193
|
-
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region src/mappers/scalar-handle.d.ts
|
|
194
199
|
type ScalarColumn = string | string[] | null | undefined;
|
|
195
200
|
type ScalarWriteValue = string | File | (string | File)[] | null;
|
|
196
201
|
/**
|
|
@@ -200,33 +205,35 @@ type ScalarWriteValue = string | File | (string | File)[] | null;
|
|
|
200
205
|
* in sync for no behavioural gain.
|
|
201
206
|
*/
|
|
202
207
|
declare function scalarHandle(): FileMapper<ScalarColumn, ScalarWriteValue>;
|
|
203
|
-
|
|
208
|
+
//#endregion
|
|
209
|
+
//#region src/mappers/rich-image.d.ts
|
|
204
210
|
interface RichImageOpts {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
211
|
+
/**
|
|
212
|
+
* Who owns the focal point.
|
|
213
|
+
*
|
|
214
|
+
* - `'usage'` — it lives on the reference inside the record, so the same
|
|
215
|
+
* asset crops differently in two records (Sanity-shaped).
|
|
216
|
+
* - `'file'` — it is a property of the file that the storage owns; the
|
|
217
|
+
* mapper leaves it alone (Directus-shaped).
|
|
218
|
+
* - `false` — this column has none.
|
|
219
|
+
*
|
|
220
|
+
* There is no default. Guessing wrong silently corrupts a neighbouring
|
|
221
|
+
* record's crop, which is exactly the failure nobody notices for months.
|
|
222
|
+
*/
|
|
223
|
+
focus: 'usage' | 'file' | false;
|
|
224
|
+
/** Dotted path to the handle inside the object. Defaults to `'id'`. */
|
|
225
|
+
handleKey?: string;
|
|
226
|
+
/** Keys lifted into `usage` when `focus === 'usage'`. */
|
|
227
|
+
usageKeys?: string[];
|
|
222
228
|
}
|
|
223
229
|
declare function richImage(opts: RichImageOpts): FileMapper<unknown, unknown>;
|
|
224
|
-
|
|
230
|
+
//#endregion
|
|
231
|
+
//#region src/mappers/junction.d.ts
|
|
225
232
|
interface JunctionOpts {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
233
|
+
/** Column on the junction row holding the file's handle. */
|
|
234
|
+
fileKey: string;
|
|
235
|
+
/** Column holding the 1-based position. Omit when the table is unordered. */
|
|
236
|
+
sortKey?: string;
|
|
230
237
|
}
|
|
231
238
|
type Row = Record<string, unknown>;
|
|
232
239
|
/**
|
|
@@ -235,39 +242,40 @@ type Row = Record<string, unknown>;
|
|
|
235
242
|
* must survive a reorder: dropping it would delete and recreate every row.
|
|
236
243
|
*/
|
|
237
244
|
declare function junction(opts: JunctionOpts): FileMapper<Row[] | null | undefined, Row[]>;
|
|
238
|
-
|
|
245
|
+
//#endregion
|
|
246
|
+
//#region src/contract/run-contract.d.ts
|
|
239
247
|
interface ContractOpts {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
248
|
+
/** A FRESH adapter per case — shared state between cases hides bugs. */
|
|
249
|
+
adapter: () => FileStorageAdapter;
|
|
250
|
+
mapper: FileMapper;
|
|
251
|
+
/** Representative column values this mapper reads, for the invariants. */
|
|
252
|
+
sampleColumn: {
|
|
253
|
+
single: unknown;
|
|
254
|
+
multiple: unknown;
|
|
255
|
+
};
|
|
256
|
+
/** Skip upload cases for adapters whose bytes ride the mutation. */
|
|
257
|
+
skipUpload?: boolean;
|
|
258
|
+
/**
|
|
259
|
+
* Set for a mapper whose `write` returns a COLUMN PATCH rather than a column
|
|
260
|
+
* value — `pocketbaseDiff` is the one in this package.
|
|
261
|
+
*
|
|
262
|
+
* Round-trip identity is meaningless for such a mapper: it never returns the
|
|
263
|
+
* value it was given, it returns the keys needed to change it. The patch
|
|
264
|
+
* invariants below replace the round-trip cases rather than sitting beside
|
|
265
|
+
* them, because running both would demand two contradictory things of one
|
|
266
|
+
* `write`.
|
|
267
|
+
*/
|
|
268
|
+
writesColumnPatch?: boolean;
|
|
269
|
+
/**
|
|
270
|
+
* Context every `resolveUrl` case is called with. Defaults to `{}`.
|
|
271
|
+
*
|
|
272
|
+
* Supply it for an adapter whose URL cannot be built from a handle alone.
|
|
273
|
+
* A PocketBase filename is meaningless by itself — the URL is assembled from
|
|
274
|
+
* collection + record + filename — so that adapter correctly THROWS on an
|
|
275
|
+
* empty context, and the contract must hand it a real one rather than
|
|
276
|
+
* treating the throw as a conformance failure.
|
|
277
|
+
*/
|
|
278
|
+
resolveCtx?: ResolveCtx;
|
|
271
279
|
}
|
|
272
280
|
/**
|
|
273
281
|
* The test runner, injected.
|
|
@@ -278,8 +286,8 @@ interface ContractOpts {
|
|
|
278
286
|
* or node:test run the same contract.
|
|
279
287
|
*/
|
|
280
288
|
interface ContractRunner {
|
|
281
|
-
|
|
282
|
-
|
|
289
|
+
it: (name: string, fn: () => void | Promise<void>) => void;
|
|
290
|
+
expect: (actual: never) => unknown;
|
|
283
291
|
}
|
|
284
292
|
/**
|
|
285
293
|
* The conformance suite every adapter owes. Call inside a `describe`.
|
|
@@ -289,16 +297,17 @@ interface ContractRunner {
|
|
|
289
297
|
* if it is executable by the person doing the stretching.
|
|
290
298
|
*/
|
|
291
299
|
declare function runFileStorageContract(opts: ContractOpts, runner: ContractRunner): void;
|
|
292
|
-
|
|
300
|
+
//#endregion
|
|
301
|
+
//#region src/contract/fake-adapter.d.ts
|
|
293
302
|
interface FakeAdapterOpts {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
303
|
+
/** Emit this many progress ticks before resolving. Default 2. */
|
|
304
|
+
progressTicks?: number;
|
|
305
|
+
/** Make every upload reject with this message. */
|
|
306
|
+
failWith?: string;
|
|
307
|
+
/** Return signed URLs that expire this many ms from resolution. */
|
|
308
|
+
signedTtlMs?: number;
|
|
309
|
+
/** Default `'immediate'`. Set `'fused'` to stand in for an inline/PocketBase-shaped storage. */
|
|
310
|
+
pipeline?: 'immediate' | 'fused';
|
|
302
311
|
}
|
|
303
312
|
/**
|
|
304
313
|
* In-memory adapter. Exists so the contract suite can be tested against
|
|
@@ -306,23 +315,24 @@ interface FakeAdapterOpts {
|
|
|
306
315
|
* when their own adapter fails a case.
|
|
307
316
|
*/
|
|
308
317
|
declare function createFakeAdapter(opts?: FakeAdapterOpts): FileStorageAdapter & {
|
|
309
|
-
|
|
310
|
-
|
|
318
|
+
stored: Map<string, File>;
|
|
319
|
+
removed: string[];
|
|
311
320
|
};
|
|
312
|
-
|
|
321
|
+
//#endregion
|
|
322
|
+
//#region src/adapters/inline.d.ts
|
|
313
323
|
interface InlineFileStorageOpts {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
324
|
+
/**
|
|
325
|
+
* How the data-provider wrapper should encode the bytes into the mutation.
|
|
326
|
+
* `base64` transforms the value; `multipart` and `graphql-upload` set the
|
|
327
|
+
* request body and are mutually exclusive with each other.
|
|
328
|
+
*/
|
|
329
|
+
encoding: FusedEncoding;
|
|
330
|
+
/**
|
|
331
|
+
* How a saved file is read back. `ctx.resource` and `ctx.record` are given
|
|
332
|
+
* because backends of this shape usually address files through the record
|
|
333
|
+
* that owns them.
|
|
334
|
+
*/
|
|
335
|
+
resolveUrl: (ref: FileRef, ctx: ResolveCtx) => ResolvedUrl | Promise<ResolvedUrl>;
|
|
326
336
|
}
|
|
327
337
|
/**
|
|
328
338
|
* The storage that is not a storage: the backend takes the file inside the
|
|
@@ -334,19 +344,20 @@ interface InlineFileStorageOpts {
|
|
|
334
344
|
* request path — that can report it.
|
|
335
345
|
*/
|
|
336
346
|
declare function inlineFileStorage(opts: InlineFileStorageOpts): FileStorageAdapter;
|
|
337
|
-
|
|
347
|
+
//#endregion
|
|
348
|
+
//#region src/adapters/nhost.d.ts
|
|
338
349
|
interface NhostFileStorageOpts {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
+
/** Storage service base URL, e.g. `https://<project>.storage.<region>.nhost.run`. */
|
|
351
|
+
baseUrl: string;
|
|
352
|
+
/**
|
|
353
|
+
* Access token supplier. Injected rather than read from localStorage: the
|
|
354
|
+
* v0.2 implementation hardcoded a storage key and a window global, which
|
|
355
|
+
* meant one app shape was supported and no test could drive it.
|
|
356
|
+
*/
|
|
357
|
+
getToken: () => string | null | Promise<string | null>;
|
|
358
|
+
bucketId?: string;
|
|
359
|
+
orphans?: OrphanPolicy;
|
|
360
|
+
fetchImpl?: typeof fetch;
|
|
350
361
|
}
|
|
351
362
|
/**
|
|
352
363
|
* nhost storage: bytes go up first, the column keeps the file's UUID, and the
|
|
@@ -356,90 +367,92 @@ interface NhostFileStorageOpts {
|
|
|
356
367
|
* as v0.2 did — writes a link with a TTL into the database.
|
|
357
368
|
*/
|
|
358
369
|
declare function nhostFileStorage(opts: NhostFileStorageOpts): FileStorageAdapter;
|
|
359
|
-
|
|
370
|
+
//#endregion
|
|
371
|
+
//#region src/adapters/supabase.d.ts
|
|
360
372
|
/**
|
|
361
373
|
* The slice of `@supabase/supabase-js` this adapter uses, declared
|
|
362
374
|
* structurally so the package keeps zero runtime dependencies and the adapter
|
|
363
375
|
* can be driven by a plain object in tests.
|
|
364
376
|
*/
|
|
365
377
|
interface SupabaseStorageLike {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
};
|
|
383
|
-
};
|
|
384
|
-
createSignedUrl(path: string, expiresIn: number): Promise<{
|
|
385
|
-
data: {
|
|
386
|
-
signedUrl: string;
|
|
387
|
-
} | null;
|
|
388
|
-
error: {
|
|
389
|
-
message: string;
|
|
390
|
-
} | null;
|
|
391
|
-
}>;
|
|
392
|
-
remove(paths: string[]): Promise<{
|
|
393
|
-
data: unknown;
|
|
394
|
-
error: {
|
|
395
|
-
message: string;
|
|
396
|
-
} | null;
|
|
397
|
-
}>;
|
|
378
|
+
storage: {
|
|
379
|
+
from(bucket: string): {
|
|
380
|
+
upload(path: string, file: File, opts?: {
|
|
381
|
+
contentType?: string;
|
|
382
|
+
upsert?: boolean;
|
|
383
|
+
}): Promise<{
|
|
384
|
+
data: {
|
|
385
|
+
path: string;
|
|
386
|
+
} | null;
|
|
387
|
+
error: {
|
|
388
|
+
message: string;
|
|
389
|
+
} | null;
|
|
390
|
+
}>;
|
|
391
|
+
getPublicUrl(path: string): {
|
|
392
|
+
data: {
|
|
393
|
+
publicUrl: string;
|
|
398
394
|
};
|
|
395
|
+
};
|
|
396
|
+
createSignedUrl(path: string, expiresIn: number): Promise<{
|
|
397
|
+
data: {
|
|
398
|
+
signedUrl: string;
|
|
399
|
+
} | null;
|
|
400
|
+
error: {
|
|
401
|
+
message: string;
|
|
402
|
+
} | null;
|
|
403
|
+
}>;
|
|
404
|
+
remove(paths: string[]): Promise<{
|
|
405
|
+
data: unknown;
|
|
406
|
+
error: {
|
|
407
|
+
message: string;
|
|
408
|
+
} | null;
|
|
409
|
+
}>;
|
|
399
410
|
};
|
|
411
|
+
};
|
|
400
412
|
}
|
|
401
413
|
interface SupabaseFileStorageOpts {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
414
|
+
client: SupabaseStorageLike;
|
|
415
|
+
bucket: string;
|
|
416
|
+
/** Public buckets yield permanent URLs; signed buckets yield expiring ones. */
|
|
417
|
+
access: 'public' | 'signed';
|
|
418
|
+
/** Lifetime of a signed URL in seconds. Default 3600. */
|
|
419
|
+
signedTtlSec?: number;
|
|
420
|
+
/** Prepended to every generated object path, e.g. `'avatars'`. */
|
|
421
|
+
pathPrefix?: string;
|
|
422
|
+
orphans?: OrphanPolicy;
|
|
411
423
|
}
|
|
412
424
|
declare function supabaseFileStorage(opts: SupabaseFileStorageOpts): FileStorageAdapter;
|
|
413
|
-
|
|
425
|
+
//#endregion
|
|
426
|
+
//#region src/adapters/s3.d.ts
|
|
414
427
|
/** What the adopter's signing endpoint hands back. */
|
|
415
428
|
interface SignedTarget {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
429
|
+
/**
|
|
430
|
+
* The object key. **The server chooses it, not the client** — letting the
|
|
431
|
+
* browser name the object is how buckets get overwritten by whoever asks.
|
|
432
|
+
*/
|
|
433
|
+
key: string;
|
|
434
|
+
url: string;
|
|
435
|
+
method?: 'PUT' | 'POST';
|
|
436
|
+
/** Headers that were part of the signature. Sent verbatim. */
|
|
437
|
+
headers?: Record<string, string>;
|
|
438
|
+
/** POST-policy form fields. Sent before the file part. */
|
|
439
|
+
fields?: Record<string, string>;
|
|
427
440
|
}
|
|
428
441
|
interface S3FileStorageOpts {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
442
|
+
/** Ask your backend to sign. Authorization happens there, not here. */
|
|
443
|
+
sign: (file: {
|
|
444
|
+
name: string;
|
|
445
|
+
type: string;
|
|
446
|
+
size: number;
|
|
447
|
+
}) => Promise<SignedTarget>;
|
|
448
|
+
/** CDN or bucket origin for permanent read URLs. */
|
|
449
|
+
publicBaseUrl?: string;
|
|
450
|
+
/** Supply for private buckets; return an expiring URL. */
|
|
451
|
+
signView?: (key: string, ctx: ResolveCtx) => Promise<ResolvedUrl>;
|
|
452
|
+
remove?: (key: string) => Promise<void>;
|
|
453
|
+
orphans?: OrphanPolicy;
|
|
454
|
+
/** Injected for tests. Defaults to `new XMLHttpRequest()`. */
|
|
455
|
+
xhrImpl?: () => XMLHttpRequest;
|
|
443
456
|
}
|
|
444
457
|
/**
|
|
445
458
|
* Direct-to-bucket upload behind a signed handshake: ask the server to sign,
|
|
@@ -450,7 +463,8 @@ interface S3FileStorageOpts {
|
|
|
450
463
|
* absence to be felt.
|
|
451
464
|
*/
|
|
452
465
|
declare function s3FileStorage(opts: S3FileStorageOpts): FileStorageAdapter;
|
|
453
|
-
|
|
466
|
+
//#endregion
|
|
467
|
+
//#region src/adapters/pocketbase.d.ts
|
|
454
468
|
/**
|
|
455
469
|
* PocketBase stores a filename per file and updates multi-value fields by
|
|
456
470
|
* DIFF: `field+` appends, `field-` removes by name. Single-value fields are
|
|
@@ -458,14 +472,14 @@ declare function s3FileStorage(opts: S3FileStorageOpts): FileStorageAdapter;
|
|
|
458
472
|
*/
|
|
459
473
|
declare function pocketbaseDiff(): FileMapper<string | string[] | null | undefined, unknown>;
|
|
460
474
|
interface PocketbaseFileStorageOpts {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
475
|
+
/** PocketBase origin, e.g. `http://127.0.0.1:8090`. */
|
|
476
|
+
baseUrl: string;
|
|
477
|
+
/** Set when the file field is marked Protected in the collection schema. */
|
|
478
|
+
protectedFiles?: boolean;
|
|
479
|
+
/** Supplies a short-lived file token — `pb.files.getToken()`. */
|
|
480
|
+
getFileToken?: () => Promise<string>;
|
|
481
|
+
/** Lifetime assumed for a file token, in seconds. Default 120. */
|
|
482
|
+
tokenTtlSec?: number;
|
|
469
483
|
}
|
|
470
484
|
/**
|
|
471
485
|
* PocketBase: bytes ride the record mutation as multipart, the column keeps a
|
|
@@ -476,7 +490,8 @@ interface PocketbaseFileStorageOpts {
|
|
|
476
490
|
* one the example app runs against.
|
|
477
491
|
*/
|
|
478
492
|
declare function pocketbaseFileStorage(opts: PocketbaseFileStorageOpts): FileStorageAdapter;
|
|
479
|
-
|
|
493
|
+
//#endregion
|
|
494
|
+
//#region src/mappers/column-patch.d.ts
|
|
480
495
|
/**
|
|
481
496
|
* A mapper's answer when the change cannot be expressed as "the new value of
|
|
482
497
|
* this column".
|
|
@@ -489,12 +504,13 @@ declare function pocketbaseFileStorage(opts: PocketbaseFileStorageOpts): FileSto
|
|
|
489
504
|
*/
|
|
490
505
|
declare const COLUMN_PATCH: unique symbol;
|
|
491
506
|
interface ColumnPatch {
|
|
492
|
-
|
|
493
|
-
|
|
507
|
+
[COLUMN_PATCH]: true;
|
|
508
|
+
keys: Record<string, unknown>;
|
|
494
509
|
}
|
|
495
510
|
declare function columnPatch(keys: Record<string, unknown>): ColumnPatch;
|
|
496
511
|
declare function isColumnPatch(value: unknown): value is ColumnPatch;
|
|
497
|
-
|
|
512
|
+
//#endregion
|
|
513
|
+
//#region src/wrapper/with-file-storage.d.ts
|
|
498
514
|
/** Stamped on a wrapped provider so the wiring assertion can see it. */
|
|
499
515
|
declare const FILE_STORAGE_WRAPPED: unique symbol;
|
|
500
516
|
/**
|
|
@@ -523,21 +539,22 @@ declare function withFileStorage<T>(provider: T, app: AppRuntime): T;
|
|
|
523
539
|
* being Files. Only what remains decides the request body.
|
|
524
540
|
*/
|
|
525
541
|
interface EncodingPlan {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
542
|
+
/** Encoding EXPLICITLY declared for a field, via `f.file({ fileStorage })`. Authoritative. */
|
|
543
|
+
knownEncodings: Map<string, FusedEncoding>;
|
|
544
|
+
/**
|
|
545
|
+
* Assumed for a payload key with NO declared field to consult — the
|
|
546
|
+
* resource isn't registered, or has no `f.file()` for this key. Mirrors
|
|
547
|
+
* `ui.fileStorage ?? 'default'`, the exact resolution a KNOWN field with no
|
|
548
|
+
* explicit storage name already gets, extended to "no field info at all".
|
|
549
|
+
* This is what keeps a single-storage app working without ever having to
|
|
550
|
+
* declare its fields through `@burojs/field-types`.
|
|
551
|
+
*/
|
|
552
|
+
defaultEncoding: FusedEncoding;
|
|
553
|
+
/** Body encoding for whatever Files remain once base64 keys are converted. */
|
|
554
|
+
bodyEncoding: Exclude<FusedEncoding, 'base64'>;
|
|
539
555
|
}
|
|
540
|
-
|
|
556
|
+
//#endregion
|
|
557
|
+
//#region src/wrapper/assert-wiring.d.ts
|
|
541
558
|
/**
|
|
542
559
|
* Three checks that must fail at boot rather than at runtime.
|
|
543
560
|
*
|
|
@@ -560,7 +577,8 @@ interface EncodingPlan {
|
|
|
560
577
|
* provider.
|
|
561
578
|
*/
|
|
562
579
|
declare function assertFileStorageWiring(app: AppRuntime, provider: unknown): void;
|
|
563
|
-
|
|
580
|
+
//#endregion
|
|
581
|
+
//#region src/wrapper/encode.d.ts
|
|
564
582
|
/**
|
|
565
583
|
* Splice column patches into the payload; leave everything else alone.
|
|
566
584
|
*
|
|
@@ -596,14 +614,17 @@ declare function containsColumnPatch(value: unknown, seen?: WeakSet<object>): bo
|
|
|
596
614
|
* content-type, because the browser writes the boundary.
|
|
597
615
|
*/
|
|
598
616
|
declare function encodePayload(variables: Record<string, unknown>, plan: EncodingPlan): Promise<Record<string, unknown> | FormData>;
|
|
599
|
-
|
|
617
|
+
//#endregion
|
|
618
|
+
//#region src/baseline-carrier.d.ts
|
|
600
619
|
/**
|
|
601
620
|
* Copy the opaque mapped-file baseline capability from a decoded record to a
|
|
602
621
|
* fresh edit payload. The physical baseline itself remains private to the
|
|
603
622
|
* provider wrapper; this helper can neither inspect nor serialize it.
|
|
604
623
|
*/
|
|
605
624
|
declare function inheritFileMapperBaseline<T extends object>(source: object, target: T): T;
|
|
606
|
-
|
|
625
|
+
//#endregion
|
|
626
|
+
//#region src/index.d.ts
|
|
607
627
|
declare const PACKAGE_NAME: "@burojs/file-storage";
|
|
608
|
-
|
|
628
|
+
//#endregion
|
|
609
629
|
export { COLUMN_PATCH, type ColumnPatch, type ContractOpts, type ContractRunner, FILE_STORAGE_SERVICE, FILE_STORAGE_WRAPPED, type FakeAdapterOpts, type FileMapper, type FileRef, type FileStorageAdapter, type FileStorageRegistry, type FusedEncoding, type InlineFileStorageOpts, type JunctionOpts, type MapperCtx, type NhostFileStorageOpts, type OrphanPolicy, PACKAGE_NAME, type PocketbaseFileStorageOpts, type ResolveCtx, type ResolvedUrl, type RichImageOpts, type S3FileStorageOpts, type SignedTarget, type SupabaseFileStorageOpts, type SupabaseStorageLike, type UploadCtx, type UploadPipeline, assertFileStorageWiring, columnPatch, containsColumnPatch, containsFile, createFakeAdapter, createFileStorageRegistry, encodePayload, fileStoragePlugin, inheritFileMapperBaseline, inlineFileStorage, isColumnPatch, isImage, junction, newRef, nhostFileStorage, pocketbaseDiff, pocketbaseFileStorage, richImage, runFileStorageContract, s3FileStorage, scalarHandle, splicePatches, supabaseFileStorage, withFileStorage };
|
|
630
|
+
//# sourceMappingURL=index.d.cts.map
|