@edgestore/shared 1.0.0-next.0 → 1.0.0-next.2

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.
@@ -1,33 +1,24 @@
1
+ import type { StandardSchemaV1 } from '@standard-schema/spec';
1
2
  import { type MaybePromise } from '../types';
2
3
  import {
3
- type AnyBuilder,
4
+ type AnyContext,
4
5
  type AnyMetadata,
5
6
  type EdgeStoreRouter,
6
7
  } from './bucketBuilder';
7
8
 
8
- export type InitParams = {
9
- ctx: any;
10
- router: EdgeStoreRouter<any>;
9
+ export type InitParams<TCtx extends AnyContext = AnyContext> = {
10
+ ctx: TCtx;
11
+ router: EdgeStoreRouter<TCtx>;
11
12
  };
12
13
 
13
- export type InitRes = {
14
- token?: string;
15
- };
16
-
17
- export type GetFileParams = {
18
- url: string;
14
+ export type ClientInit = {
15
+ path: string;
16
+ headers?: Record<string, string>;
19
17
  };
20
18
 
21
- export type GetFileRes = {
22
- url: string;
23
- size: number;
24
- uploadedAt: Date;
25
- path: {
26
- [key: string]: string;
27
- };
28
- metadata: {
29
- [key: string]: string;
30
- };
19
+ export type InitRes = {
20
+ token?: string;
21
+ clientInit?: ClientInit;
31
22
  };
32
23
 
33
24
  export type RequestUploadParams = {
@@ -38,6 +29,7 @@ export type RequestUploadParams = {
38
29
  bucketName: string;
39
30
  bucketType: string;
40
31
  fileInfo: {
32
+ type?: string;
41
33
  size: number;
42
34
  extension: string;
43
35
  isPublic: boolean;
@@ -56,6 +48,160 @@ export type RequestUploadParams = {
56
48
  };
57
49
  };
58
50
 
51
+ export type ProviderFilterValue =
52
+ | string
53
+ | Partial<{
54
+ eq: string;
55
+ neq: string;
56
+ gt: string;
57
+ gte: string;
58
+ lt: string;
59
+ lte: string;
60
+ startsWith: string;
61
+ endsWith: string;
62
+ between: [string, string];
63
+ }>;
64
+
65
+ export type ListFilesFilter = {
66
+ AND?: ListFilesFilter[];
67
+ OR?: ListFilesFilter[];
68
+ uploadedAt?: ProviderFilterValue;
69
+ path?: Record<string, ProviderFilterValue>;
70
+ metadata?: Record<string, ProviderFilterValue>;
71
+ };
72
+
73
+ export type FileReference = { id: string } | { key: string } | { url: string };
74
+
75
+ export type ProviderFile = {
76
+ id: string;
77
+ url: string;
78
+ key: string;
79
+ thumbnailUrl: string | null;
80
+ thumbnailKey: string | null;
81
+ bucketId: string;
82
+ bucketName: string;
83
+ projectId: string;
84
+ accountId: string;
85
+ name: string;
86
+ path: Record<string, string>;
87
+ metadata: Record<string, string>;
88
+ sizeBytes: number;
89
+ mimeType: string | null;
90
+ state: 'requested' | 'uploaded' | 'deleted' | 'replace_requested';
91
+ temporary: boolean;
92
+ uploadedAt: Date;
93
+ updatedAt: Date;
94
+ };
95
+
96
+ export type BackendFile = {
97
+ url: string;
98
+ sizeBytes: number;
99
+ /** Router-derived path values, when this operation retrieves them. */
100
+ path?: Record<string, string>;
101
+ /** Router-derived metadata values, when this operation retrieves them. */
102
+ metadata?: Record<string, string>;
103
+ uploadedAt: Date | string;
104
+ updatedAt: Date | string;
105
+ };
106
+
107
+ export type ProviderFileMutationResult<
108
+ TErrorCode extends string =
109
+ | 'FILE_NOT_CONFIRMABLE'
110
+ | 'FILE_NOT_DELETABLE'
111
+ | 'FILE_NOT_RESTORABLE'
112
+ | 'INVALID_FILE_REF',
113
+ > = {
114
+ results: (
115
+ | { success: true }
116
+ | {
117
+ success: false;
118
+ error: {
119
+ code: TErrorCode;
120
+ message: string;
121
+ };
122
+ }
123
+ )[];
124
+ };
125
+
126
+ export type BackendUploadParams = {
127
+ bucketName: string;
128
+ bucketType: string;
129
+ fileInfo: RequestUploadParams['fileInfo'];
130
+ autoSignedUrls?: RequestUploadParams['autoSignedUrls'];
131
+ source: Blob;
132
+ signal?: AbortSignal;
133
+ onProgress?: (progress: {
134
+ transferredBytes: number;
135
+ totalBytes: number;
136
+ percentage: number;
137
+ phase: 'preparing' | 'uploading' | 'processing';
138
+ }) => void;
139
+ };
140
+
141
+ export type BackendUploadResult<TFile extends BackendFile = ProviderFile> = {
142
+ file: TFile;
143
+ signedReadUrl?: {
144
+ signedUrl: string;
145
+ signedThumbnailUrl?: string | null;
146
+ expiresAt: Date | string;
147
+ expiresIn: number;
148
+ };
149
+ };
150
+
151
+ export type BackendUploadOperation<TFile extends BackendFile = ProviderFile> = (
152
+ params: BackendUploadParams,
153
+ ) => MaybePromise<BackendUploadResult<TFile>>;
154
+
155
+ export type BackendGetFileOperation<
156
+ TFile extends BackendFile = ProviderFile,
157
+ TFileReference = FileReference,
158
+ > = (params: {
159
+ bucketName: string;
160
+ file: TFileReference;
161
+ }) => MaybePromise<TFile>;
162
+
163
+ export type BackendListFilesResult<
164
+ TFile extends BackendFile = ProviderFile,
165
+ TCursor = string,
166
+ > = {
167
+ items: TFile[];
168
+ limit: number;
169
+ nextCursor: TCursor | null;
170
+ hasMore: boolean;
171
+ };
172
+
173
+ export type BackendListFilesOperation<
174
+ TFile extends BackendFile = ProviderFile,
175
+ TCursor = string,
176
+ > = (params: {
177
+ bucketName: string;
178
+ filter?: ListFilesFilter;
179
+ cursor?: TCursor;
180
+ limit?: number;
181
+ }) => MaybePromise<BackendListFilesResult<TFile, TCursor>>;
182
+
183
+ export type BackendFileMutationOperation<
184
+ TFileReference = FileReference,
185
+ TErrorCode extends string =
186
+ | 'FILE_NOT_CONFIRMABLE'
187
+ | 'FILE_NOT_DELETABLE'
188
+ | 'FILE_NOT_RESTORABLE'
189
+ | 'INVALID_FILE_REF',
190
+ > = (params: {
191
+ bucketName: string;
192
+ files: TFileReference[];
193
+ }) => MaybePromise<ProviderFileMutationResult<TErrorCode>>;
194
+
195
+ export type BackendGetSignedUrlsOperation<
196
+ TFileReference = FileReference,
197
+ TResult extends GetSignedUrlRes = GetSignedUrlRes,
198
+ > = (params: {
199
+ bucketName: string;
200
+ files: TFileReference[];
201
+ expiresIn?: number;
202
+ includeThumbnails?: boolean;
203
+ }) => MaybePromise<TResult[]>;
204
+
59
205
  export type RequestUploadPartsParams = {
60
206
  multipart: {
61
207
  uploadId: string;
@@ -83,46 +229,35 @@ export type CompleteMultipartUploadParams = {
83
229
  }[];
84
230
  };
85
231
 
86
- export type CompleteMultipartUploadRes = {
87
- success: boolean;
232
+ type RequestUploadAccess = {
233
+ accessUrl: string;
234
+ thumbnailUrl?: string | null;
235
+ accessSignedUrl?: string;
236
+ accessSignedThumbnailUrl?: string | null;
237
+ accessSignedUrlExpiresAt?: Date | string;
238
+ accessSignedUrlExpiresIn?: number;
88
239
  };
89
240
 
90
- export type RequestUploadRes =
91
- | {
241
+ export type SinglePartRequestUploadRes = RequestUploadAccess & {
242
+ uploadUrl: string;
243
+ };
244
+
245
+ export type MultipartRequestUploadRes = RequestUploadAccess & {
246
+ multipart: {
247
+ key: string;
248
+ uploadId: string;
249
+ partSize: number;
250
+ totalParts: number;
251
+ parts: {
252
+ partNumber: number;
92
253
  uploadUrl: string;
93
- accessUrl: string;
94
- thumbnailUrl?: string | null;
95
- accessSignedUrl?: string;
96
- accessSignedThumbnailUrl?: string | null;
97
- accessSignedUrlExpiresAt?: Date | string;
98
- accessSignedUrlExpiresIn?: number;
99
- }
100
- | {
101
- multipart: {
102
- key: string;
103
- uploadId: string;
104
- partSize: number;
105
- totalParts: number;
106
- parts: {
107
- partNumber: number;
108
- uploadUrl: string;
109
- }[];
110
- };
111
- accessUrl: string;
112
- thumbnailUrl?: string | null;
113
- accessSignedUrl?: string;
114
- accessSignedThumbnailUrl?: string | null;
115
- accessSignedUrlExpiresAt?: Date | string;
116
- accessSignedUrlExpiresIn?: number;
117
- };
118
-
119
- export type GetSignedUrlsParams = {
120
- bucketName: string;
121
- urls: string[];
122
- expiresIn?: number;
123
- includeThumbnails?: boolean;
254
+ }[];
255
+ };
124
256
  };
125
257
 
258
+ export type RequestUploadRes =
259
+ SinglePartRequestUploadRes | MultipartRequestUploadRes;
260
+
126
261
  export type GetSignedUrlRes = {
127
262
  url: string;
128
263
  signedUrl: string;
@@ -132,41 +267,113 @@ export type GetSignedUrlRes = {
132
267
  signedThumbnailUrl?: string | null;
133
268
  };
134
269
 
135
- export type ConfirmUpload = {
136
- bucket: AnyBuilder;
137
- url: string;
270
+ export type ProviderReferenceDefinition<
271
+ TSchema extends StandardSchemaV1 = StandardSchemaV1,
272
+ > = {
273
+ schema: TSchema;
274
+ fromUrl: (url: string) => MaybePromise<unknown>;
138
275
  };
139
276
 
140
- export type ConfirmUploadRes = {
141
- success: boolean;
277
+ type ProviderUploadBase<
278
+ TUpload extends BackendUploadOperation<BackendFile> | undefined =
279
+ BackendUploadOperation<BackendFile> | undefined,
280
+ > = {
281
+ upload?: TUpload;
142
282
  };
143
283
 
144
- export type DeleteFileParams = {
145
- bucket: AnyBuilder;
146
- url: string;
284
+ export type ProviderMultipartUploads = {
285
+ requestParts: (
286
+ params: RequestUploadPartsParams,
287
+ ) => MaybePromise<RequestUploadPartsRes>;
288
+ complete: (params: CompleteMultipartUploadParams) => MaybePromise<void>;
147
289
  };
148
290
 
149
- export type DeleteFileRes = {
150
- success: boolean;
291
+ export type ProviderUploads<
292
+ TUpload extends BackendUploadOperation<BackendFile> | undefined =
293
+ BackendUploadOperation<BackendFile> | undefined,
294
+ > =
295
+ | (ProviderUploadBase<TUpload> & {
296
+ request: (
297
+ params: RequestUploadParams,
298
+ ) => MaybePromise<SinglePartRequestUploadRes>;
299
+ multipart?: never;
300
+ })
301
+ | (ProviderUploadBase<TUpload> & {
302
+ request: (params: RequestUploadParams) => MaybePromise<RequestUploadRes>;
303
+ multipart: ProviderMultipartUploads;
304
+ });
305
+
306
+ export type ProviderFiles<
307
+ TFileReference = FileReference,
308
+ TCursor = string,
309
+ TGet extends BackendGetFileOperation<BackendFile, TFileReference> =
310
+ BackendGetFileOperation<BackendFile, TFileReference>,
311
+ TList extends BackendListFilesOperation<BackendFile, TCursor> | undefined =
312
+ BackendListFilesOperation<BackendFile, TCursor> | undefined,
313
+ TConfirm extends
314
+ BackendFileMutationOperation<TFileReference, string> | undefined =
315
+ BackendFileMutationOperation<TFileReference, string> | undefined,
316
+ TDelete extends
317
+ BackendFileMutationOperation<TFileReference, string> | undefined =
318
+ BackendFileMutationOperation<TFileReference, string> | undefined,
319
+ TRestore extends
320
+ BackendFileMutationOperation<TFileReference, string> | undefined =
321
+ BackendFileMutationOperation<TFileReference, string> | undefined,
322
+ TGetSignedUrls extends
323
+ BackendGetSignedUrlsOperation<TFileReference> | undefined =
324
+ BackendGetSignedUrlsOperation<TFileReference> | undefined,
325
+ > = {
326
+ cursorSchema?: StandardSchemaV1<unknown, TCursor>;
327
+ get: TGet;
328
+ list?: TList;
329
+ confirm?: TConfirm;
330
+ delete?: TDelete;
331
+ restore?: TRestore;
332
+ getSignedUrls?: TGetSignedUrls;
151
333
  };
152
334
 
153
- export type Provider = {
335
+ export type EdgeStoreProvider<
336
+ TReferenceSchema extends StandardSchemaV1 = StandardSchemaV1<
337
+ unknown,
338
+ FileReference
339
+ >,
340
+ TCursor = string,
341
+ TUploads extends ProviderUploads = ProviderUploads,
342
+ TFiles extends ProviderFiles<
343
+ StandardSchemaV1.InferOutput<TReferenceSchema>,
344
+ TCursor
345
+ > = ProviderFiles<StandardSchemaV1.InferOutput<TReferenceSchema>, TCursor>,
346
+ > = {
154
347
  name: string;
155
- init: (params: InitParams) => MaybePromise<InitRes>;
156
- getBaseUrl: () => MaybePromise<string>;
157
- getFile: (params: GetFileParams) => MaybePromise<GetFileRes>;
158
- requestUpload: (
159
- params: RequestUploadParams,
160
- ) => MaybePromise<RequestUploadRes>;
161
- requestUploadParts: (
162
- params: RequestUploadPartsParams,
163
- ) => MaybePromise<RequestUploadPartsRes>;
164
- getSignedUrls?: (
165
- params: GetSignedUrlsParams,
166
- ) => MaybePromise<GetSignedUrlRes[]>;
167
- completeMultipartUpload: (
168
- params: CompleteMultipartUploadParams,
169
- ) => MaybePromise<CompleteMultipartUploadRes>;
170
- confirmUpload: (params: ConfirmUpload) => MaybePromise<ConfirmUploadRes>;
171
- deleteFile: (params: DeleteFileParams) => MaybePromise<DeleteFileRes>;
348
+ baseUrl: string | (() => MaybePromise<string>);
349
+ init: <TCtx extends AnyContext>(
350
+ params: InitParams<TCtx>,
351
+ ) => MaybePromise<InitRes>;
352
+ reference: ProviderReferenceDefinition<TReferenceSchema>;
353
+ uploads: TUploads;
354
+ files: TFiles;
355
+ };
356
+
357
+ export type AnyEdgeStoreProvider = EdgeStoreProvider<
358
+ StandardSchemaV1<any, any>,
359
+ any,
360
+ ProviderUploads,
361
+ ProviderFiles<any, any>
362
+ >;
363
+
364
+ export type DefaultEdgeStoreProvider = EdgeStoreProvider<
365
+ StandardSchemaV1<unknown, FileReference>,
366
+ string
367
+ > & {
368
+ uploads: ProviderUploads<BackendUploadOperation> & {
369
+ upload: BackendUploadOperation;
370
+ };
371
+ files: ProviderFiles<FileReference, string> & {
372
+ get: BackendGetFileOperation;
373
+ list: BackendListFilesOperation;
374
+ confirm: BackendFileMutationOperation;
375
+ delete: BackendFileMutationOperation;
376
+ restore: BackendFileMutationOperation;
377
+ getSignedUrls: BackendGetSignedUrlsOperation;
378
+ };
172
379
  };
@@ -0,0 +1,88 @@
1
+ import { type StandardSchemaV1 } from '@standard-schema/spec';
2
+ import { EdgeStoreError } from '../errors';
3
+
4
+ export type AnySchema = StandardSchemaV1<any, Record<string, unknown>>;
5
+
6
+ /** @internal */
7
+ export type AnyInput = AnySchema | undefined;
8
+
9
+ export type InferSchemaInput<TSchema extends AnyInput> =
10
+ TSchema extends StandardSchemaV1
11
+ ? StandardSchemaV1.InferInput<TSchema>
12
+ : never;
13
+
14
+ export type InferSchemaOutput<TSchema extends AnyInput> =
15
+ TSchema extends StandardSchemaV1
16
+ ? StandardSchemaV1.InferOutput<TSchema>
17
+ : never;
18
+
19
+ function formatIssue(issue: StandardSchemaV1.Issue): string {
20
+ const path = issue.path
21
+ ?.map((segment) =>
22
+ typeof segment === 'object' ? String(segment.key) : String(segment),
23
+ )
24
+ .join('.');
25
+
26
+ return path ? `${path}: ${issue.message}` : issue.message;
27
+ }
28
+
29
+ /** @internal */
30
+ export function assertStandardSchema(
31
+ schema: unknown,
32
+ ): asserts schema is AnySchema {
33
+ const protocol =
34
+ typeof schema === 'object' && schema !== null && '~standard' in schema
35
+ ? schema['~standard']
36
+ : undefined;
37
+
38
+ if (
39
+ typeof protocol !== 'object' ||
40
+ protocol === null ||
41
+ !('version' in protocol) ||
42
+ protocol.version !== 1 ||
43
+ !('validate' in protocol) ||
44
+ typeof protocol.validate !== 'function'
45
+ ) {
46
+ throw new EdgeStoreError({
47
+ code: 'SERVER_ERROR',
48
+ message: 'Bucket input schemas must implement Standard Schema V1',
49
+ });
50
+ }
51
+ }
52
+
53
+ /** @internal */
54
+ export async function parseBucketInput<TSchema extends AnyInput>(
55
+ schema: TSchema,
56
+ input: unknown,
57
+ ): Promise<
58
+ TSchema extends undefined ? Record<string, never> : InferSchemaOutput<TSchema>
59
+ > {
60
+ if (schema === undefined) {
61
+ return {} as TSchema extends undefined
62
+ ? Record<string, never>
63
+ : InferSchemaOutput<TSchema>;
64
+ }
65
+
66
+ const result = await schema['~standard'].validate(input);
67
+ if (result.issues !== undefined) {
68
+ throw new EdgeStoreError({
69
+ code: 'BAD_REQUEST',
70
+ message: `Invalid input: ${result.issues.map(formatIssue).join('; ')}`,
71
+ });
72
+ }
73
+
74
+ if (
75
+ typeof result.value !== 'object' ||
76
+ result.value === null ||
77
+ Array.isArray(result.value)
78
+ ) {
79
+ throw new EdgeStoreError({
80
+ code: 'SERVER_ERROR',
81
+ message: 'Bucket input schemas must return an object',
82
+ });
83
+ }
84
+
85
+ return result.value as TSchema extends undefined
86
+ ? Record<string, never>
87
+ : InferSchemaOutput<TSchema>;
88
+ }
@@ -1,17 +1,16 @@
1
1
  import { type Simplify } from '../types';
2
2
  import { type AnyMetadata } from './bucketBuilder';
3
3
  import {
4
- type DeleteFileRes,
4
+ type ClientInit,
5
5
  type RequestUploadPartsRes,
6
6
  type RequestUploadRes,
7
7
  } from './providerTypes';
8
8
 
9
9
  export type SharedInitRes = {
10
10
  newCookies: string[];
11
- token: string | undefined;
12
11
  baseUrl: string;
13
12
  providerName: string;
14
- requiresFileAccessCookie: boolean;
13
+ clientInit?: ClientInit;
15
14
  };
16
15
  export type SharedRequestUploadRes = Simplify<
17
16
  RequestUploadRes & {
@@ -23,4 +22,17 @@ export type SharedRequestUploadRes = Simplify<
23
22
  }
24
23
  >;
25
24
  export type SharedRequestUploadPartsRes = RequestUploadPartsRes;
26
- export type SharedDeleteFileRes = DeleteFileRes;
25
+
26
+ export type SharedFileMutationRes = {
27
+ succeeded: string[];
28
+ failed: {
29
+ url: string;
30
+ error: {
31
+ code: string;
32
+ message: string;
33
+ };
34
+ }[];
35
+ };
36
+
37
+ export type SharedConfirmUploadsRes = SharedFileMutationRes;
38
+ export type SharedDeleteFilesRes = SharedFileMutationRes;
@@ -32,7 +32,7 @@ export type UploadOptions = {
32
32
  */
33
33
  replaceTargetUrl?: string;
34
34
  /**
35
- * If true, the file needs to be confirmed by using the `confirmUpload` function.
35
+ * If true, the file needs to be confirmed by using the `confirm` function.
36
36
  * If the file is not confirmed within 24 hours, it will be deleted.
37
37
  *
38
38
  * This is useful for pages where the file is uploaded as soon as it is selected,