@uplift-io/uplift 1.0.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/LICENSE +21 -0
- package/README.md +85 -0
- package/dist/client.cjs +102 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +9 -0
- package/dist/client.d.ts +9 -0
- package/dist/client.js +75 -0
- package/dist/client.js.map +1 -0
- package/dist/index.cjs +275 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +78 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.js +236 -0
- package/dist/index.js.map +1 -0
- package/dist/react.cjs +155 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +15 -0
- package/dist/react.d.ts +15 -0
- package/dist/react.js +130 -0
- package/dist/react.js.map +1 -0
- package/dist/server.cjs +243 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +5 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.js +216 -0
- package/dist/server.js.map +1 -0
- package/dist/types-BdcszAj8.d.cts +128 -0
- package/dist/types-BdcszAj8.d.ts +128 -0
- package/package.json +74 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/types.ts","../src/utils.ts","../src/builder.ts","../src/client.ts"],"sourcesContent":["export {\r\n any,\r\n audio,\r\n csv,\r\n custom,\r\n image,\r\n json,\r\n pdf,\r\n text,\r\n UploadBuilder,\r\n video,\r\n type AudioExtension,\r\n type DimensionRule,\r\n type DurationRule,\r\n type ImageExtension,\r\n type PageRule,\r\n type TextEncoding,\r\n type TextExtension,\r\n type VideoExtension\r\n} from \"./builder\";\r\nexport { createUploadClient } from \"./client\";\r\n// export { createMemoryStorage } from \"./storage/memory\";\r\nexport {\r\n UploadError,\r\n type ClientInput,\r\n type ClientOutput,\r\n type DoneContext,\r\n type KeyContext,\r\n type Middleware,\r\n type SizeValue,\r\n type StandardSchema,\r\n type StorageAdapter,\r\n type StoragePutInput,\r\n type UploadedFile,\r\n type UploadErrorCode,\r\n type UploadInputFile,\r\n type UploadClient,\r\n type UpliftApp\r\n} from \"./types\";\r\n\r\nimport type { Middleware, StorageAdapter, UpliftApp, UploadRoutes } from \"./types\";\r\n\r\nexport function uplift<TRoutes extends UploadRoutes>(config: {\r\n storage: StorageAdapter;\r\n routes: TRoutes;\r\n middleware?: Middleware<unknown>;\r\n onUploadComplete?: UpliftApp<TRoutes>[\"onUploadComplete\"];\r\n}): UpliftApp<TRoutes> {\r\n return config;\r\n}\r\n","export type SizeValue = `${number}b` | `${number}kb` | `${number}mb` | `${number}gb`;\r\nexport type DurationValue = `${number}s` | `${number}m` | `${number}h`;\r\n\r\nexport type UploadInputFile = {\r\n name: string;\r\n type: string;\r\n size: number;\r\n extension?: string;\r\n file?: File;\r\n};\r\n\r\nexport type UploadedFile = {\r\n url: string;\r\n key: string;\r\n name: string;\r\n type: string;\r\n size: number;\r\n extension?: string | undefined;\r\n provider: string;\r\n};\r\n\r\nexport type UploadErrorCode =\r\n | \"FILE_TOO_LARGE\"\r\n | \"FILE_TOO_SMALL\"\r\n | \"INVALID_TYPE\"\r\n | \"AUTH_FAILED\"\r\n | \"VALIDATION_FAILED\"\r\n | \"UPLOAD_FAILED\"\r\n | \"UNKNOWN\";\r\n\r\nexport class UploadError extends Error {\r\n readonly code: UploadErrorCode;\r\n\r\n constructor(code: UploadErrorCode, message: string) {\r\n super(message);\r\n this.name = \"UploadError\";\r\n this.code = code;\r\n }\r\n\r\n toJSON() {\r\n return {\r\n message: this.message,\r\n code: this.code\r\n };\r\n }\r\n}\r\n\r\nexport type StandardSchema<T = unknown> = {\r\n parse(input: unknown): T;\r\n};\r\n\r\nexport type KeyContext<TAuth = unknown, TMeta = unknown> = {\r\n req: Request;\r\n file: UploadInputFile;\r\n user: TAuth;\r\n meta: TMeta;\r\n};\r\n\r\nexport type DoneContext<\r\n TAuth = unknown,\r\n TMeta = unknown,\r\n TMultiple extends boolean = false\r\n> = TMultiple extends true\r\n ? { req: Request; files: UploadedFile[]; user: TAuth; meta: TMeta[] }\r\n : { req: Request; file: UploadedFile; user: TAuth; meta: TMeta };\r\n\r\nexport type StoragePutInput = {\r\n key: string;\r\n file: UploadInputFile;\r\n body: File;\r\n};\r\n\r\nexport type StorageAdapter = {\r\n provider: string;\r\n put(input: StoragePutInput): Promise<UploadedFile>;\r\n};\r\n\r\nexport type Middleware<TUser = unknown> = (ctx: { req: Request }) => TUser | Promise<TUser>;\r\n\r\nexport type UploadKind =\r\n | \"any\"\r\n | \"image\"\r\n | \"pdf\"\r\n | \"video\"\r\n | \"audio\"\r\n | \"text\"\r\n | \"json\"\r\n | \"csv\"\r\n | \"custom\";\r\n\r\nexport type UploadRouteDefinition = {\r\n kind: UploadKind;\r\n maxBytes?: number;\r\n minBytes?: number;\r\n multiple: boolean;\r\n multipleLimit?: number;\r\n auth?: Middleware<unknown>;\r\n overrideAuth: boolean;\r\n key?: (ctx: KeyContext<unknown, unknown>) => string | Promise<string>;\r\n meta?: (ctx: { req: Request; file: UploadInputFile; user: unknown }) => unknown | Promise<unknown>;\r\n validate?: (ctx: {\r\n req: Request;\r\n file: UploadInputFile;\r\n user: unknown;\r\n meta: unknown;\r\n }) => true | string | Promise<true | string>;\r\n done?: (ctx: DoneContext<unknown, unknown, boolean>) => void | Promise<void>;\r\n extensions?: string[];\r\n mimeTypes?: string[];\r\n dimensionRule?: { minWidth?: number; minHeight?: number; maxWidth?: number; maxHeight?: number };\r\n requireSquare?: boolean;\r\n aspectRatio?: `${number}:${number}`;\r\n encoding?: \"utf-8\" | \"utf-16\" | \"ascii\";\r\n schema?: StandardSchema;\r\n headers?: string[];\r\n delimiter?: \",\" | \";\" | \"\\t\" | \"|\";\r\n pageRule?: { min?: number; max?: number };\r\n encrypted?: boolean;\r\n durationRule?: { min?: DurationValue; max?: DurationValue };\r\n};\r\n\r\nexport type UploadRoutes = Record<string, { _def: UploadRouteDefinition }>;\r\n\r\nexport type UpliftApp<TRoutes extends UploadRoutes = UploadRoutes> = {\r\n storage: StorageAdapter;\r\n routes: TRoutes;\r\n middleware?: Middleware<unknown> | undefined;\r\n onUploadComplete?: ((ctx: {\r\n route: keyof TRoutes & string;\r\n result: UploadedFile | UploadedFile[];\r\n user: unknown;\r\n }) => void | Promise<void>) | undefined;\r\n};\r\n\r\nexport type IsMultiple<TRoute> = TRoute extends { __multiple?: infer TMultiple }\r\n ? TMultiple extends true\r\n ? true\r\n : false\r\n : false;\r\n\r\nexport type ClientInput<TRoute> = IsMultiple<TRoute> extends true ? File[] | FileList : File;\r\nexport type ClientOutput<TRoute> = IsMultiple<TRoute> extends true ? UploadedFile[] : UploadedFile;\r\n\r\nexport type UploadClient<TApp extends UpliftApp> = {\r\n [TRouteName in keyof TApp[\"routes\"] & string]: (\r\n input: ClientInput<TApp[\"routes\"][TRouteName]>\r\n ) => Promise<ClientOutput<TApp[\"routes\"][TRouteName]>>;\r\n};\r\n","import { UploadError, type SizeValue, type UploadInputFile } from \"./types\";\r\n\r\nconst sizeUnits = {\r\n b: 1,\r\n kb: 1024,\r\n mb: 1024 * 1024,\r\n gb: 1024 * 1024 * 1024\r\n} as const;\r\n\r\nexport function parseSize(value: SizeValue): number {\r\n const match = /^(\\d+(?:\\.\\d+)?)(b|kb|mb|gb)$/.exec(value);\r\n if (!match) throw new UploadError(\"VALIDATION_FAILED\", `Invalid size value: ${value}`);\r\n const amount = Number(match[1]);\r\n const unit = match[2] as keyof typeof sizeUnits;\r\n return Math.floor(amount * sizeUnits[unit]);\r\n}\r\n\r\nexport function extensionFor(name: string): string | undefined {\r\n const index = name.lastIndexOf(\".\");\r\n if (index < 0 || index === name.length - 1) return undefined;\r\n return name.slice(index + 1).toLowerCase();\r\n}\r\n\r\nexport function toInputFile(file: File): UploadInputFile {\r\n const extension = extensionFor(file.name);\r\n const input: UploadInputFile = {\r\n name: file.name,\r\n type: file.type,\r\n size: file.size,\r\n file\r\n };\r\n if (extension) input.extension = extension;\r\n return input;\r\n}\r\n\r\nexport function defaultKey(file: UploadInputFile): string {\r\n const random = globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`;\r\n return `${random}/${file.name}`;\r\n}\r\n\r\nexport async function readJsonFile(file: File): Promise<unknown> {\r\n try {\r\n return JSON.parse(await file.text());\r\n } catch {\r\n throw new UploadError(\"VALIDATION_FAILED\", \"Invalid JSON file.\");\r\n }\r\n}\r\n","import {\r\n type DoneContext,\r\n type DurationValue,\r\n type KeyContext,\r\n type Middleware,\r\n type SizeValue,\r\n type StandardSchema,\r\n type UploadInputFile,\r\n type UploadKind,\r\n type UploadRouteDefinition\r\n} from \"./types\";\r\nimport { parseSize } from \"./utils\";\r\n\r\nexport type ImageExtension = \"png\" | \"jpg\" | \"jpeg\" | \"webp\" | \"gif\" | \"avif\";\r\nexport type VideoExtension = \"mp4\" | \"webm\" | \"mov\" | \"avi\" | \"mkv\";\r\nexport type AudioExtension = \"mp3\" | \"wav\" | \"ogg\" | \"m4a\" | \"aac\" | \"flac\";\r\nexport type TextExtension = \"txt\" | \"md\" | \"log\";\r\nexport type TextEncoding = \"utf-8\" | \"utf-16\" | \"ascii\";\r\n\r\nexport type DimensionRule = {\r\n minWidth?: number;\r\n minHeight?: number;\r\n maxWidth?: number;\r\n maxHeight?: number;\r\n};\r\n\r\nexport type PageRule = {\r\n min?: number;\r\n max?: number;\r\n};\r\n\r\nexport type DurationRule = {\r\n min?: DurationValue;\r\n max?: DurationValue;\r\n};\r\n\r\nexport class UploadBuilder<\r\n TAuth = unknown,\r\n TMeta = unknown,\r\n TMultiple extends boolean = false,\r\n TKind extends UploadKind = UploadKind\r\n> {\r\n readonly __auth?: TAuth;\r\n readonly __meta?: TMeta;\r\n readonly __multiple?: TMultiple;\r\n readonly __kind?: TKind;\r\n readonly _def: UploadRouteDefinition;\r\n\r\n constructor(kind: TKind, mimeTypes: string[] = [], extensions: string[] = []) {\r\n this._def = {\r\n kind,\r\n multiple: false,\r\n overrideAuth: false\r\n };\r\n if (mimeTypes.length > 0) this._def.mimeTypes = mimeTypes;\r\n if (extensions.length > 0) this._def.extensions = extensions;\r\n }\r\n\r\n max(size: SizeValue): this {\r\n this._def.maxBytes = parseSize(size);\r\n return this;\r\n }\r\n\r\n min(size: SizeValue): this {\r\n this._def.minBytes = parseSize(size);\r\n return this;\r\n }\r\n\r\n multiple(count?: number): UploadBuilder<TAuth, TMeta, true, TKind> {\r\n this._def.multiple = true;\r\n if (count !== undefined) this._def.multipleLimit = count;\r\n return this as unknown as UploadBuilder<TAuth, TMeta, true, TKind>;\r\n }\r\n\r\n auth<TUser>(handler: Middleware<TUser>): UploadBuilder<TUser, TMeta, TMultiple, TKind> {\r\n this._def.auth = handler as Middleware<unknown>;\r\n this._def.overrideAuth = false;\r\n return this as unknown as UploadBuilder<TUser, TMeta, TMultiple, TKind>;\r\n }\r\n\r\n overrideAuth(): this {\r\n this._def.overrideAuth = true;\r\n delete this._def.auth;\r\n return this;\r\n }\r\n\r\n key(handler: (ctx: KeyContext<TAuth, TMeta>) => string | Promise<string>): this {\r\n this._def.key = handler as (ctx: KeyContext<unknown, unknown>) => string | Promise<string>;\r\n return this;\r\n }\r\n\r\n meta<TNextMeta>(\r\n handler: (ctx: {\r\n req: Request;\r\n file: UploadInputFile;\r\n user: TAuth;\r\n }) => TNextMeta | Promise<TNextMeta>\r\n ): UploadBuilder<TAuth, TNextMeta, TMultiple, TKind> {\r\n this._def.meta = handler as (ctx: {\r\n req: Request;\r\n file: UploadInputFile;\r\n user: unknown;\r\n }) => unknown | Promise<unknown>;\r\n return this as unknown as UploadBuilder<TAuth, TNextMeta, TMultiple, TKind>;\r\n }\r\n\r\n validate(\r\n handler: (ctx: {\r\n req: Request;\r\n file: UploadInputFile;\r\n user: TAuth;\r\n meta: TMeta;\r\n }) => true | string | Promise<true | string>\r\n ): this {\r\n this._def.validate = handler as (ctx: {\r\n req: Request;\r\n file: UploadInputFile;\r\n user: unknown;\r\n meta: unknown;\r\n }) => true | string | Promise<true | string>;\r\n return this;\r\n }\r\n\r\n done(handler: (ctx: DoneContext<TAuth, TMeta, TMultiple>) => void | Promise<void>): this {\r\n this._def.done = handler as (ctx: DoneContext<unknown, unknown, boolean>) => void | Promise<void>;\r\n return this;\r\n }\r\n\r\n types(types: string[]): this {\r\n this._def.extensions = types.map((type) => type.toLowerCase());\r\n return this;\r\n }\r\n\r\n dimensions(rule: DimensionRule): this {\r\n this._def.dimensionRule = rule;\r\n return this;\r\n }\r\n\r\n square(): this {\r\n this._def.requireSquare = true;\r\n return this;\r\n }\r\n\r\n aspectRatio(value: `${number}:${number}`): this {\r\n this._def.aspectRatio = value;\r\n return this;\r\n }\r\n\r\n encoding(value: TextEncoding): this {\r\n this._def.encoding = value;\r\n return this;\r\n }\r\n\r\n schema<TSchema extends StandardSchema>(schema: TSchema): this {\r\n this._def.schema = schema;\r\n return this;\r\n }\r\n\r\n headers(headers: string[]): this {\r\n this._def.headers = headers;\r\n return this;\r\n }\r\n\r\n delimiter(value: \",\" | \";\" | \"\\t\" | \"|\"): this {\r\n this._def.delimiter = value;\r\n return this;\r\n }\r\n\r\n pages(rule: PageRule): this {\r\n this._def.pageRule = rule;\r\n return this;\r\n }\r\n\r\n encrypted(value: boolean): this {\r\n this._def.encrypted = value;\r\n return this;\r\n }\r\n\r\n duration(rule: DurationRule): this {\r\n this._def.durationRule = rule;\r\n return this;\r\n }\r\n}\r\n\r\nexport function any(): UploadBuilder<unknown, unknown, false, \"any\"> {\r\n return new UploadBuilder(\"any\");\r\n}\r\n\r\nexport function image(): UploadBuilder<unknown, unknown, false, \"image\"> {\r\n return new UploadBuilder(\"image\", [\"image/\"], [\"png\", \"jpg\", \"jpeg\", \"webp\", \"gif\", \"avif\"]);\r\n}\r\n\r\nexport function pdf(): UploadBuilder<unknown, unknown, false, \"pdf\"> {\r\n return new UploadBuilder(\"pdf\", [\"application/pdf\"], [\"pdf\"]);\r\n}\r\n\r\nexport function video(): UploadBuilder<unknown, unknown, false, \"video\"> {\r\n return new UploadBuilder(\"video\", [\"video/\"], [\"mp4\", \"webm\", \"mov\", \"avi\", \"mkv\"]);\r\n}\r\n\r\nexport function audio(): UploadBuilder<unknown, unknown, false, \"audio\"> {\r\n return new UploadBuilder(\"audio\", [\"audio/\"], [\"mp3\", \"wav\", \"ogg\", \"m4a\", \"aac\", \"flac\"]);\r\n}\r\n\r\nexport function text(): UploadBuilder<unknown, unknown, false, \"text\"> {\r\n return new UploadBuilder(\"text\", [\"text/\"], [\"txt\", \"md\", \"log\"]);\r\n}\r\n\r\nexport function json(): UploadBuilder<unknown, unknown, false, \"json\"> {\r\n return new UploadBuilder(\"json\", [\"application/json\"], [\"json\"]);\r\n}\r\n\r\nexport function csv(): UploadBuilder<unknown, unknown, false, \"csv\"> {\r\n return new UploadBuilder(\"csv\", [\"text/csv\"], [\"csv\"]);\r\n}\r\n\r\nexport function custom(type: string | string[]): UploadBuilder<unknown, unknown, false, \"custom\"> {\r\n const mimeTypes = Array.isArray(type) ? type : [type];\r\n return new UploadBuilder(\"custom\", mimeTypes);\r\n}\r\n","import { UploadError, type UpliftApp, type UploadClient } from \"./types\";\r\n\r\nexport type UploadProgressHandler = (progress: number) => void;\r\n\r\nexport function createUploadClient<TApp extends UpliftApp>(\r\n baseUrl: string,\r\n options: { fetch?: typeof fetch; onProgress?: (route: string, progress: number) => void } = {}\r\n): UploadClient<TApp> {\r\n const fetcher = options.fetch ?? fetch;\r\n\r\n return new Proxy({}, {\r\n get(_target, property) {\r\n if (typeof property !== \"string\") return undefined;\r\n return async (input: File | File[] | FileList) => {\r\n const files = input instanceof File ? [input] : Array.from(input);\r\n const form = new FormData();\r\n const field = files.length === 1 ? \"file\" : \"files\";\r\n for (const file of files) form.append(field, file);\r\n\r\n const url = routeUrl(baseUrl, property);\r\n if (!options.fetch && typeof XMLHttpRequest !== \"undefined\") {\r\n return uploadWithXhr(url, form, property, options.onProgress);\r\n }\r\n\r\n options.onProgress?.(property, 0);\r\n const response = await fetcher(url, { method: \"POST\", body: form });\r\n const body = await response.json() as { result?: unknown; error?: { code: string; message: string } };\r\n if (!response.ok) {\r\n throw new UploadError((body.error?.code ?? \"UNKNOWN\") as never, body.error?.message ?? \"Upload failed.\");\r\n }\r\n options.onProgress?.(property, 100);\r\n return body.result;\r\n };\r\n }\r\n }) as UploadClient<TApp>;\r\n}\r\n\r\nfunction routeUrl(baseUrl: string, route: string): string {\r\n const url = new URL(baseUrl, globalThis.location?.href ?? \"http://localhost\");\r\n url.searchParams.set(\"route\", route);\r\n const value = url.toString();\r\n return baseUrl.startsWith(\"/\") ? `${url.pathname}${url.search}` : value;\r\n}\r\n\r\nfunction uploadWithXhr(\r\n url: string,\r\n form: FormData,\r\n route: string,\r\n onProgress?: (route: string, progress: number) => void\r\n): Promise<unknown> {\r\n return new Promise((resolve, reject) => {\r\n const xhr = new XMLHttpRequest();\r\n xhr.open(\"POST\", url);\r\n xhr.upload.onprogress = (event) => {\r\n if (!event.lengthComputable) return;\r\n onProgress?.(route, Math.round((event.loaded / event.total) * 100));\r\n };\r\n xhr.onload = () => {\r\n const body = JSON.parse(xhr.responseText || \"{}\") as {\r\n result?: unknown;\r\n error?: { code: string; message: string };\r\n };\r\n if (xhr.status < 200 || xhr.status >= 300) {\r\n reject(new UploadError((body.error?.code ?? \"UNKNOWN\") as never, body.error?.message ?? \"Upload failed.\"));\r\n return;\r\n }\r\n onProgress?.(route, 100);\r\n resolve(body.result);\r\n };\r\n xhr.onerror = () => reject(new UploadError(\"UPLOAD_FAILED\", \"Upload request failed.\"));\r\n onProgress?.(route, 0);\r\n xhr.send(form);\r\n });\r\n}\r\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { D as DurationValue, U as UploadKind, a as UploadRouteDefinition, S as SizeValue, M as Middleware, K as KeyContext, b as UploadInputFile, c as DoneContext, d as StandardSchema, e as UploadRoutes, f as StorageAdapter, g as UpliftApp } from './types-BdcszAj8.cjs';
|
|
2
|
+
export { C as ClientInput, h as ClientOutput, i as StoragePutInput, j as UploadClient, k as UploadError, l as UploadErrorCode, m as UploadedFile } from './types-BdcszAj8.cjs';
|
|
3
|
+
export { createUploadClient } from './client.cjs';
|
|
4
|
+
|
|
5
|
+
type ImageExtension = "png" | "jpg" | "jpeg" | "webp" | "gif" | "avif";
|
|
6
|
+
type VideoExtension = "mp4" | "webm" | "mov" | "avi" | "mkv";
|
|
7
|
+
type AudioExtension = "mp3" | "wav" | "ogg" | "m4a" | "aac" | "flac";
|
|
8
|
+
type TextExtension = "txt" | "md" | "log";
|
|
9
|
+
type TextEncoding = "utf-8" | "utf-16" | "ascii";
|
|
10
|
+
type DimensionRule = {
|
|
11
|
+
minWidth?: number;
|
|
12
|
+
minHeight?: number;
|
|
13
|
+
maxWidth?: number;
|
|
14
|
+
maxHeight?: number;
|
|
15
|
+
};
|
|
16
|
+
type PageRule = {
|
|
17
|
+
min?: number;
|
|
18
|
+
max?: number;
|
|
19
|
+
};
|
|
20
|
+
type DurationRule = {
|
|
21
|
+
min?: DurationValue;
|
|
22
|
+
max?: DurationValue;
|
|
23
|
+
};
|
|
24
|
+
declare class UploadBuilder<TAuth = unknown, TMeta = unknown, TMultiple extends boolean = false, TKind extends UploadKind = UploadKind> {
|
|
25
|
+
readonly __auth?: TAuth;
|
|
26
|
+
readonly __meta?: TMeta;
|
|
27
|
+
readonly __multiple?: TMultiple;
|
|
28
|
+
readonly __kind?: TKind;
|
|
29
|
+
readonly _def: UploadRouteDefinition;
|
|
30
|
+
constructor(kind: TKind, mimeTypes?: string[], extensions?: string[]);
|
|
31
|
+
max(size: SizeValue): this;
|
|
32
|
+
min(size: SizeValue): this;
|
|
33
|
+
multiple(count?: number): UploadBuilder<TAuth, TMeta, true, TKind>;
|
|
34
|
+
auth<TUser>(handler: Middleware<TUser>): UploadBuilder<TUser, TMeta, TMultiple, TKind>;
|
|
35
|
+
overrideAuth(): this;
|
|
36
|
+
key(handler: (ctx: KeyContext<TAuth, TMeta>) => string | Promise<string>): this;
|
|
37
|
+
meta<TNextMeta>(handler: (ctx: {
|
|
38
|
+
req: Request;
|
|
39
|
+
file: UploadInputFile;
|
|
40
|
+
user: TAuth;
|
|
41
|
+
}) => TNextMeta | Promise<TNextMeta>): UploadBuilder<TAuth, TNextMeta, TMultiple, TKind>;
|
|
42
|
+
validate(handler: (ctx: {
|
|
43
|
+
req: Request;
|
|
44
|
+
file: UploadInputFile;
|
|
45
|
+
user: TAuth;
|
|
46
|
+
meta: TMeta;
|
|
47
|
+
}) => true | string | Promise<true | string>): this;
|
|
48
|
+
done(handler: (ctx: DoneContext<TAuth, TMeta, TMultiple>) => void | Promise<void>): this;
|
|
49
|
+
types(types: string[]): this;
|
|
50
|
+
dimensions(rule: DimensionRule): this;
|
|
51
|
+
square(): this;
|
|
52
|
+
aspectRatio(value: `${number}:${number}`): this;
|
|
53
|
+
encoding(value: TextEncoding): this;
|
|
54
|
+
schema<TSchema extends StandardSchema>(schema: TSchema): this;
|
|
55
|
+
headers(headers: string[]): this;
|
|
56
|
+
delimiter(value: "," | ";" | "\t" | "|"): this;
|
|
57
|
+
pages(rule: PageRule): this;
|
|
58
|
+
encrypted(value: boolean): this;
|
|
59
|
+
duration(rule: DurationRule): this;
|
|
60
|
+
}
|
|
61
|
+
declare function any(): UploadBuilder<unknown, unknown, false, "any">;
|
|
62
|
+
declare function image(): UploadBuilder<unknown, unknown, false, "image">;
|
|
63
|
+
declare function pdf(): UploadBuilder<unknown, unknown, false, "pdf">;
|
|
64
|
+
declare function video(): UploadBuilder<unknown, unknown, false, "video">;
|
|
65
|
+
declare function audio(): UploadBuilder<unknown, unknown, false, "audio">;
|
|
66
|
+
declare function text(): UploadBuilder<unknown, unknown, false, "text">;
|
|
67
|
+
declare function json(): UploadBuilder<unknown, unknown, false, "json">;
|
|
68
|
+
declare function csv(): UploadBuilder<unknown, unknown, false, "csv">;
|
|
69
|
+
declare function custom(type: string | string[]): UploadBuilder<unknown, unknown, false, "custom">;
|
|
70
|
+
|
|
71
|
+
declare function uplift<TRoutes extends UploadRoutes>(config: {
|
|
72
|
+
storage: StorageAdapter;
|
|
73
|
+
routes: TRoutes;
|
|
74
|
+
middleware?: Middleware<unknown>;
|
|
75
|
+
onUploadComplete?: UpliftApp<TRoutes>["onUploadComplete"];
|
|
76
|
+
}): UpliftApp<TRoutes>;
|
|
77
|
+
|
|
78
|
+
export { type AudioExtension, type DimensionRule, DoneContext, type DurationRule, type ImageExtension, KeyContext, Middleware, type PageRule, SizeValue, StandardSchema, StorageAdapter, type TextEncoding, type TextExtension, UpliftApp, UploadBuilder, UploadInputFile, type VideoExtension, any, audio, csv, custom, image, json, pdf, text, uplift, video };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { D as DurationValue, U as UploadKind, a as UploadRouteDefinition, S as SizeValue, M as Middleware, K as KeyContext, b as UploadInputFile, c as DoneContext, d as StandardSchema, e as UploadRoutes, f as StorageAdapter, g as UpliftApp } from './types-BdcszAj8.js';
|
|
2
|
+
export { C as ClientInput, h as ClientOutput, i as StoragePutInput, j as UploadClient, k as UploadError, l as UploadErrorCode, m as UploadedFile } from './types-BdcszAj8.js';
|
|
3
|
+
export { createUploadClient } from './client.js';
|
|
4
|
+
|
|
5
|
+
type ImageExtension = "png" | "jpg" | "jpeg" | "webp" | "gif" | "avif";
|
|
6
|
+
type VideoExtension = "mp4" | "webm" | "mov" | "avi" | "mkv";
|
|
7
|
+
type AudioExtension = "mp3" | "wav" | "ogg" | "m4a" | "aac" | "flac";
|
|
8
|
+
type TextExtension = "txt" | "md" | "log";
|
|
9
|
+
type TextEncoding = "utf-8" | "utf-16" | "ascii";
|
|
10
|
+
type DimensionRule = {
|
|
11
|
+
minWidth?: number;
|
|
12
|
+
minHeight?: number;
|
|
13
|
+
maxWidth?: number;
|
|
14
|
+
maxHeight?: number;
|
|
15
|
+
};
|
|
16
|
+
type PageRule = {
|
|
17
|
+
min?: number;
|
|
18
|
+
max?: number;
|
|
19
|
+
};
|
|
20
|
+
type DurationRule = {
|
|
21
|
+
min?: DurationValue;
|
|
22
|
+
max?: DurationValue;
|
|
23
|
+
};
|
|
24
|
+
declare class UploadBuilder<TAuth = unknown, TMeta = unknown, TMultiple extends boolean = false, TKind extends UploadKind = UploadKind> {
|
|
25
|
+
readonly __auth?: TAuth;
|
|
26
|
+
readonly __meta?: TMeta;
|
|
27
|
+
readonly __multiple?: TMultiple;
|
|
28
|
+
readonly __kind?: TKind;
|
|
29
|
+
readonly _def: UploadRouteDefinition;
|
|
30
|
+
constructor(kind: TKind, mimeTypes?: string[], extensions?: string[]);
|
|
31
|
+
max(size: SizeValue): this;
|
|
32
|
+
min(size: SizeValue): this;
|
|
33
|
+
multiple(count?: number): UploadBuilder<TAuth, TMeta, true, TKind>;
|
|
34
|
+
auth<TUser>(handler: Middleware<TUser>): UploadBuilder<TUser, TMeta, TMultiple, TKind>;
|
|
35
|
+
overrideAuth(): this;
|
|
36
|
+
key(handler: (ctx: KeyContext<TAuth, TMeta>) => string | Promise<string>): this;
|
|
37
|
+
meta<TNextMeta>(handler: (ctx: {
|
|
38
|
+
req: Request;
|
|
39
|
+
file: UploadInputFile;
|
|
40
|
+
user: TAuth;
|
|
41
|
+
}) => TNextMeta | Promise<TNextMeta>): UploadBuilder<TAuth, TNextMeta, TMultiple, TKind>;
|
|
42
|
+
validate(handler: (ctx: {
|
|
43
|
+
req: Request;
|
|
44
|
+
file: UploadInputFile;
|
|
45
|
+
user: TAuth;
|
|
46
|
+
meta: TMeta;
|
|
47
|
+
}) => true | string | Promise<true | string>): this;
|
|
48
|
+
done(handler: (ctx: DoneContext<TAuth, TMeta, TMultiple>) => void | Promise<void>): this;
|
|
49
|
+
types(types: string[]): this;
|
|
50
|
+
dimensions(rule: DimensionRule): this;
|
|
51
|
+
square(): this;
|
|
52
|
+
aspectRatio(value: `${number}:${number}`): this;
|
|
53
|
+
encoding(value: TextEncoding): this;
|
|
54
|
+
schema<TSchema extends StandardSchema>(schema: TSchema): this;
|
|
55
|
+
headers(headers: string[]): this;
|
|
56
|
+
delimiter(value: "," | ";" | "\t" | "|"): this;
|
|
57
|
+
pages(rule: PageRule): this;
|
|
58
|
+
encrypted(value: boolean): this;
|
|
59
|
+
duration(rule: DurationRule): this;
|
|
60
|
+
}
|
|
61
|
+
declare function any(): UploadBuilder<unknown, unknown, false, "any">;
|
|
62
|
+
declare function image(): UploadBuilder<unknown, unknown, false, "image">;
|
|
63
|
+
declare function pdf(): UploadBuilder<unknown, unknown, false, "pdf">;
|
|
64
|
+
declare function video(): UploadBuilder<unknown, unknown, false, "video">;
|
|
65
|
+
declare function audio(): UploadBuilder<unknown, unknown, false, "audio">;
|
|
66
|
+
declare function text(): UploadBuilder<unknown, unknown, false, "text">;
|
|
67
|
+
declare function json(): UploadBuilder<unknown, unknown, false, "json">;
|
|
68
|
+
declare function csv(): UploadBuilder<unknown, unknown, false, "csv">;
|
|
69
|
+
declare function custom(type: string | string[]): UploadBuilder<unknown, unknown, false, "custom">;
|
|
70
|
+
|
|
71
|
+
declare function uplift<TRoutes extends UploadRoutes>(config: {
|
|
72
|
+
storage: StorageAdapter;
|
|
73
|
+
routes: TRoutes;
|
|
74
|
+
middleware?: Middleware<unknown>;
|
|
75
|
+
onUploadComplete?: UpliftApp<TRoutes>["onUploadComplete"];
|
|
76
|
+
}): UpliftApp<TRoutes>;
|
|
77
|
+
|
|
78
|
+
export { type AudioExtension, type DimensionRule, DoneContext, type DurationRule, type ImageExtension, KeyContext, Middleware, type PageRule, SizeValue, StandardSchema, StorageAdapter, type TextEncoding, type TextExtension, UpliftApp, UploadBuilder, UploadInputFile, type VideoExtension, any, audio, csv, custom, image, json, pdf, text, uplift, video };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var UploadError = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
constructor(code, message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "UploadError";
|
|
7
|
+
this.code = code;
|
|
8
|
+
}
|
|
9
|
+
toJSON() {
|
|
10
|
+
return {
|
|
11
|
+
message: this.message,
|
|
12
|
+
code: this.code
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// src/utils.ts
|
|
18
|
+
var sizeUnits = {
|
|
19
|
+
b: 1,
|
|
20
|
+
kb: 1024,
|
|
21
|
+
mb: 1024 * 1024,
|
|
22
|
+
gb: 1024 * 1024 * 1024
|
|
23
|
+
};
|
|
24
|
+
function parseSize(value) {
|
|
25
|
+
const match = /^(\d+(?:\.\d+)?)(b|kb|mb|gb)$/.exec(value);
|
|
26
|
+
if (!match) throw new UploadError("VALIDATION_FAILED", `Invalid size value: ${value}`);
|
|
27
|
+
const amount = Number(match[1]);
|
|
28
|
+
const unit = match[2];
|
|
29
|
+
return Math.floor(amount * sizeUnits[unit]);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/builder.ts
|
|
33
|
+
var UploadBuilder = class {
|
|
34
|
+
__auth;
|
|
35
|
+
__meta;
|
|
36
|
+
__multiple;
|
|
37
|
+
__kind;
|
|
38
|
+
_def;
|
|
39
|
+
constructor(kind, mimeTypes = [], extensions = []) {
|
|
40
|
+
this._def = {
|
|
41
|
+
kind,
|
|
42
|
+
multiple: false,
|
|
43
|
+
overrideAuth: false
|
|
44
|
+
};
|
|
45
|
+
if (mimeTypes.length > 0) this._def.mimeTypes = mimeTypes;
|
|
46
|
+
if (extensions.length > 0) this._def.extensions = extensions;
|
|
47
|
+
}
|
|
48
|
+
max(size) {
|
|
49
|
+
this._def.maxBytes = parseSize(size);
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
min(size) {
|
|
53
|
+
this._def.minBytes = parseSize(size);
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
multiple(count) {
|
|
57
|
+
this._def.multiple = true;
|
|
58
|
+
if (count !== void 0) this._def.multipleLimit = count;
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
auth(handler) {
|
|
62
|
+
this._def.auth = handler;
|
|
63
|
+
this._def.overrideAuth = false;
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
overrideAuth() {
|
|
67
|
+
this._def.overrideAuth = true;
|
|
68
|
+
delete this._def.auth;
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
key(handler) {
|
|
72
|
+
this._def.key = handler;
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
meta(handler) {
|
|
76
|
+
this._def.meta = handler;
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
validate(handler) {
|
|
80
|
+
this._def.validate = handler;
|
|
81
|
+
return this;
|
|
82
|
+
}
|
|
83
|
+
done(handler) {
|
|
84
|
+
this._def.done = handler;
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
types(types) {
|
|
88
|
+
this._def.extensions = types.map((type) => type.toLowerCase());
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
dimensions(rule) {
|
|
92
|
+
this._def.dimensionRule = rule;
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
square() {
|
|
96
|
+
this._def.requireSquare = true;
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
aspectRatio(value) {
|
|
100
|
+
this._def.aspectRatio = value;
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
encoding(value) {
|
|
104
|
+
this._def.encoding = value;
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
schema(schema) {
|
|
108
|
+
this._def.schema = schema;
|
|
109
|
+
return this;
|
|
110
|
+
}
|
|
111
|
+
headers(headers) {
|
|
112
|
+
this._def.headers = headers;
|
|
113
|
+
return this;
|
|
114
|
+
}
|
|
115
|
+
delimiter(value) {
|
|
116
|
+
this._def.delimiter = value;
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
pages(rule) {
|
|
120
|
+
this._def.pageRule = rule;
|
|
121
|
+
return this;
|
|
122
|
+
}
|
|
123
|
+
encrypted(value) {
|
|
124
|
+
this._def.encrypted = value;
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
127
|
+
duration(rule) {
|
|
128
|
+
this._def.durationRule = rule;
|
|
129
|
+
return this;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
function any() {
|
|
133
|
+
return new UploadBuilder("any");
|
|
134
|
+
}
|
|
135
|
+
function image() {
|
|
136
|
+
return new UploadBuilder("image", ["image/"], ["png", "jpg", "jpeg", "webp", "gif", "avif"]);
|
|
137
|
+
}
|
|
138
|
+
function pdf() {
|
|
139
|
+
return new UploadBuilder("pdf", ["application/pdf"], ["pdf"]);
|
|
140
|
+
}
|
|
141
|
+
function video() {
|
|
142
|
+
return new UploadBuilder("video", ["video/"], ["mp4", "webm", "mov", "avi", "mkv"]);
|
|
143
|
+
}
|
|
144
|
+
function audio() {
|
|
145
|
+
return new UploadBuilder("audio", ["audio/"], ["mp3", "wav", "ogg", "m4a", "aac", "flac"]);
|
|
146
|
+
}
|
|
147
|
+
function text() {
|
|
148
|
+
return new UploadBuilder("text", ["text/"], ["txt", "md", "log"]);
|
|
149
|
+
}
|
|
150
|
+
function json() {
|
|
151
|
+
return new UploadBuilder("json", ["application/json"], ["json"]);
|
|
152
|
+
}
|
|
153
|
+
function csv() {
|
|
154
|
+
return new UploadBuilder("csv", ["text/csv"], ["csv"]);
|
|
155
|
+
}
|
|
156
|
+
function custom(type) {
|
|
157
|
+
const mimeTypes = Array.isArray(type) ? type : [type];
|
|
158
|
+
return new UploadBuilder("custom", mimeTypes);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// src/client.ts
|
|
162
|
+
function createUploadClient(baseUrl, options = {}) {
|
|
163
|
+
const fetcher = options.fetch ?? fetch;
|
|
164
|
+
return new Proxy({}, {
|
|
165
|
+
get(_target, property) {
|
|
166
|
+
if (typeof property !== "string") return void 0;
|
|
167
|
+
return async (input) => {
|
|
168
|
+
const files = input instanceof File ? [input] : Array.from(input);
|
|
169
|
+
const form = new FormData();
|
|
170
|
+
const field = files.length === 1 ? "file" : "files";
|
|
171
|
+
for (const file of files) form.append(field, file);
|
|
172
|
+
const url = routeUrl(baseUrl, property);
|
|
173
|
+
if (!options.fetch && typeof XMLHttpRequest !== "undefined") {
|
|
174
|
+
return uploadWithXhr(url, form, property, options.onProgress);
|
|
175
|
+
}
|
|
176
|
+
options.onProgress?.(property, 0);
|
|
177
|
+
const response = await fetcher(url, { method: "POST", body: form });
|
|
178
|
+
const body = await response.json();
|
|
179
|
+
if (!response.ok) {
|
|
180
|
+
throw new UploadError(body.error?.code ?? "UNKNOWN", body.error?.message ?? "Upload failed.");
|
|
181
|
+
}
|
|
182
|
+
options.onProgress?.(property, 100);
|
|
183
|
+
return body.result;
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
function routeUrl(baseUrl, route) {
|
|
189
|
+
const url = new URL(baseUrl, globalThis.location?.href ?? "http://localhost");
|
|
190
|
+
url.searchParams.set("route", route);
|
|
191
|
+
const value = url.toString();
|
|
192
|
+
return baseUrl.startsWith("/") ? `${url.pathname}${url.search}` : value;
|
|
193
|
+
}
|
|
194
|
+
function uploadWithXhr(url, form, route, onProgress) {
|
|
195
|
+
return new Promise((resolve, reject) => {
|
|
196
|
+
const xhr = new XMLHttpRequest();
|
|
197
|
+
xhr.open("POST", url);
|
|
198
|
+
xhr.upload.onprogress = (event) => {
|
|
199
|
+
if (!event.lengthComputable) return;
|
|
200
|
+
onProgress?.(route, Math.round(event.loaded / event.total * 100));
|
|
201
|
+
};
|
|
202
|
+
xhr.onload = () => {
|
|
203
|
+
const body = JSON.parse(xhr.responseText || "{}");
|
|
204
|
+
if (xhr.status < 200 || xhr.status >= 300) {
|
|
205
|
+
reject(new UploadError(body.error?.code ?? "UNKNOWN", body.error?.message ?? "Upload failed."));
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
onProgress?.(route, 100);
|
|
209
|
+
resolve(body.result);
|
|
210
|
+
};
|
|
211
|
+
xhr.onerror = () => reject(new UploadError("UPLOAD_FAILED", "Upload request failed."));
|
|
212
|
+
onProgress?.(route, 0);
|
|
213
|
+
xhr.send(form);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/index.ts
|
|
218
|
+
function uplift(config) {
|
|
219
|
+
return config;
|
|
220
|
+
}
|
|
221
|
+
export {
|
|
222
|
+
UploadBuilder,
|
|
223
|
+
UploadError,
|
|
224
|
+
any,
|
|
225
|
+
audio,
|
|
226
|
+
createUploadClient,
|
|
227
|
+
csv,
|
|
228
|
+
custom,
|
|
229
|
+
image,
|
|
230
|
+
json,
|
|
231
|
+
pdf,
|
|
232
|
+
text,
|
|
233
|
+
uplift,
|
|
234
|
+
video
|
|
235
|
+
};
|
|
236
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/utils.ts","../src/builder.ts","../src/client.ts","../src/index.ts"],"sourcesContent":["export type SizeValue = `${number}b` | `${number}kb` | `${number}mb` | `${number}gb`;\r\nexport type DurationValue = `${number}s` | `${number}m` | `${number}h`;\r\n\r\nexport type UploadInputFile = {\r\n name: string;\r\n type: string;\r\n size: number;\r\n extension?: string;\r\n file?: File;\r\n};\r\n\r\nexport type UploadedFile = {\r\n url: string;\r\n key: string;\r\n name: string;\r\n type: string;\r\n size: number;\r\n extension?: string | undefined;\r\n provider: string;\r\n};\r\n\r\nexport type UploadErrorCode =\r\n | \"FILE_TOO_LARGE\"\r\n | \"FILE_TOO_SMALL\"\r\n | \"INVALID_TYPE\"\r\n | \"AUTH_FAILED\"\r\n | \"VALIDATION_FAILED\"\r\n | \"UPLOAD_FAILED\"\r\n | \"UNKNOWN\";\r\n\r\nexport class UploadError extends Error {\r\n readonly code: UploadErrorCode;\r\n\r\n constructor(code: UploadErrorCode, message: string) {\r\n super(message);\r\n this.name = \"UploadError\";\r\n this.code = code;\r\n }\r\n\r\n toJSON() {\r\n return {\r\n message: this.message,\r\n code: this.code\r\n };\r\n }\r\n}\r\n\r\nexport type StandardSchema<T = unknown> = {\r\n parse(input: unknown): T;\r\n};\r\n\r\nexport type KeyContext<TAuth = unknown, TMeta = unknown> = {\r\n req: Request;\r\n file: UploadInputFile;\r\n user: TAuth;\r\n meta: TMeta;\r\n};\r\n\r\nexport type DoneContext<\r\n TAuth = unknown,\r\n TMeta = unknown,\r\n TMultiple extends boolean = false\r\n> = TMultiple extends true\r\n ? { req: Request; files: UploadedFile[]; user: TAuth; meta: TMeta[] }\r\n : { req: Request; file: UploadedFile; user: TAuth; meta: TMeta };\r\n\r\nexport type StoragePutInput = {\r\n key: string;\r\n file: UploadInputFile;\r\n body: File;\r\n};\r\n\r\nexport type StorageAdapter = {\r\n provider: string;\r\n put(input: StoragePutInput): Promise<UploadedFile>;\r\n};\r\n\r\nexport type Middleware<TUser = unknown> = (ctx: { req: Request }) => TUser | Promise<TUser>;\r\n\r\nexport type UploadKind =\r\n | \"any\"\r\n | \"image\"\r\n | \"pdf\"\r\n | \"video\"\r\n | \"audio\"\r\n | \"text\"\r\n | \"json\"\r\n | \"csv\"\r\n | \"custom\";\r\n\r\nexport type UploadRouteDefinition = {\r\n kind: UploadKind;\r\n maxBytes?: number;\r\n minBytes?: number;\r\n multiple: boolean;\r\n multipleLimit?: number;\r\n auth?: Middleware<unknown>;\r\n overrideAuth: boolean;\r\n key?: (ctx: KeyContext<unknown, unknown>) => string | Promise<string>;\r\n meta?: (ctx: { req: Request; file: UploadInputFile; user: unknown }) => unknown | Promise<unknown>;\r\n validate?: (ctx: {\r\n req: Request;\r\n file: UploadInputFile;\r\n user: unknown;\r\n meta: unknown;\r\n }) => true | string | Promise<true | string>;\r\n done?: (ctx: DoneContext<unknown, unknown, boolean>) => void | Promise<void>;\r\n extensions?: string[];\r\n mimeTypes?: string[];\r\n dimensionRule?: { minWidth?: number; minHeight?: number; maxWidth?: number; maxHeight?: number };\r\n requireSquare?: boolean;\r\n aspectRatio?: `${number}:${number}`;\r\n encoding?: \"utf-8\" | \"utf-16\" | \"ascii\";\r\n schema?: StandardSchema;\r\n headers?: string[];\r\n delimiter?: \",\" | \";\" | \"\\t\" | \"|\";\r\n pageRule?: { min?: number; max?: number };\r\n encrypted?: boolean;\r\n durationRule?: { min?: DurationValue; max?: DurationValue };\r\n};\r\n\r\nexport type UploadRoutes = Record<string, { _def: UploadRouteDefinition }>;\r\n\r\nexport type UpliftApp<TRoutes extends UploadRoutes = UploadRoutes> = {\r\n storage: StorageAdapter;\r\n routes: TRoutes;\r\n middleware?: Middleware<unknown> | undefined;\r\n onUploadComplete?: ((ctx: {\r\n route: keyof TRoutes & string;\r\n result: UploadedFile | UploadedFile[];\r\n user: unknown;\r\n }) => void | Promise<void>) | undefined;\r\n};\r\n\r\nexport type IsMultiple<TRoute> = TRoute extends { __multiple?: infer TMultiple }\r\n ? TMultiple extends true\r\n ? true\r\n : false\r\n : false;\r\n\r\nexport type ClientInput<TRoute> = IsMultiple<TRoute> extends true ? File[] | FileList : File;\r\nexport type ClientOutput<TRoute> = IsMultiple<TRoute> extends true ? UploadedFile[] : UploadedFile;\r\n\r\nexport type UploadClient<TApp extends UpliftApp> = {\r\n [TRouteName in keyof TApp[\"routes\"] & string]: (\r\n input: ClientInput<TApp[\"routes\"][TRouteName]>\r\n ) => Promise<ClientOutput<TApp[\"routes\"][TRouteName]>>;\r\n};\r\n","import { UploadError, type SizeValue, type UploadInputFile } from \"./types\";\r\n\r\nconst sizeUnits = {\r\n b: 1,\r\n kb: 1024,\r\n mb: 1024 * 1024,\r\n gb: 1024 * 1024 * 1024\r\n} as const;\r\n\r\nexport function parseSize(value: SizeValue): number {\r\n const match = /^(\\d+(?:\\.\\d+)?)(b|kb|mb|gb)$/.exec(value);\r\n if (!match) throw new UploadError(\"VALIDATION_FAILED\", `Invalid size value: ${value}`);\r\n const amount = Number(match[1]);\r\n const unit = match[2] as keyof typeof sizeUnits;\r\n return Math.floor(amount * sizeUnits[unit]);\r\n}\r\n\r\nexport function extensionFor(name: string): string | undefined {\r\n const index = name.lastIndexOf(\".\");\r\n if (index < 0 || index === name.length - 1) return undefined;\r\n return name.slice(index + 1).toLowerCase();\r\n}\r\n\r\nexport function toInputFile(file: File): UploadInputFile {\r\n const extension = extensionFor(file.name);\r\n const input: UploadInputFile = {\r\n name: file.name,\r\n type: file.type,\r\n size: file.size,\r\n file\r\n };\r\n if (extension) input.extension = extension;\r\n return input;\r\n}\r\n\r\nexport function defaultKey(file: UploadInputFile): string {\r\n const random = globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`;\r\n return `${random}/${file.name}`;\r\n}\r\n\r\nexport async function readJsonFile(file: File): Promise<unknown> {\r\n try {\r\n return JSON.parse(await file.text());\r\n } catch {\r\n throw new UploadError(\"VALIDATION_FAILED\", \"Invalid JSON file.\");\r\n }\r\n}\r\n","import {\r\n type DoneContext,\r\n type DurationValue,\r\n type KeyContext,\r\n type Middleware,\r\n type SizeValue,\r\n type StandardSchema,\r\n type UploadInputFile,\r\n type UploadKind,\r\n type UploadRouteDefinition\r\n} from \"./types\";\r\nimport { parseSize } from \"./utils\";\r\n\r\nexport type ImageExtension = \"png\" | \"jpg\" | \"jpeg\" | \"webp\" | \"gif\" | \"avif\";\r\nexport type VideoExtension = \"mp4\" | \"webm\" | \"mov\" | \"avi\" | \"mkv\";\r\nexport type AudioExtension = \"mp3\" | \"wav\" | \"ogg\" | \"m4a\" | \"aac\" | \"flac\";\r\nexport type TextExtension = \"txt\" | \"md\" | \"log\";\r\nexport type TextEncoding = \"utf-8\" | \"utf-16\" | \"ascii\";\r\n\r\nexport type DimensionRule = {\r\n minWidth?: number;\r\n minHeight?: number;\r\n maxWidth?: number;\r\n maxHeight?: number;\r\n};\r\n\r\nexport type PageRule = {\r\n min?: number;\r\n max?: number;\r\n};\r\n\r\nexport type DurationRule = {\r\n min?: DurationValue;\r\n max?: DurationValue;\r\n};\r\n\r\nexport class UploadBuilder<\r\n TAuth = unknown,\r\n TMeta = unknown,\r\n TMultiple extends boolean = false,\r\n TKind extends UploadKind = UploadKind\r\n> {\r\n readonly __auth?: TAuth;\r\n readonly __meta?: TMeta;\r\n readonly __multiple?: TMultiple;\r\n readonly __kind?: TKind;\r\n readonly _def: UploadRouteDefinition;\r\n\r\n constructor(kind: TKind, mimeTypes: string[] = [], extensions: string[] = []) {\r\n this._def = {\r\n kind,\r\n multiple: false,\r\n overrideAuth: false\r\n };\r\n if (mimeTypes.length > 0) this._def.mimeTypes = mimeTypes;\r\n if (extensions.length > 0) this._def.extensions = extensions;\r\n }\r\n\r\n max(size: SizeValue): this {\r\n this._def.maxBytes = parseSize(size);\r\n return this;\r\n }\r\n\r\n min(size: SizeValue): this {\r\n this._def.minBytes = parseSize(size);\r\n return this;\r\n }\r\n\r\n multiple(count?: number): UploadBuilder<TAuth, TMeta, true, TKind> {\r\n this._def.multiple = true;\r\n if (count !== undefined) this._def.multipleLimit = count;\r\n return this as unknown as UploadBuilder<TAuth, TMeta, true, TKind>;\r\n }\r\n\r\n auth<TUser>(handler: Middleware<TUser>): UploadBuilder<TUser, TMeta, TMultiple, TKind> {\r\n this._def.auth = handler as Middleware<unknown>;\r\n this._def.overrideAuth = false;\r\n return this as unknown as UploadBuilder<TUser, TMeta, TMultiple, TKind>;\r\n }\r\n\r\n overrideAuth(): this {\r\n this._def.overrideAuth = true;\r\n delete this._def.auth;\r\n return this;\r\n }\r\n\r\n key(handler: (ctx: KeyContext<TAuth, TMeta>) => string | Promise<string>): this {\r\n this._def.key = handler as (ctx: KeyContext<unknown, unknown>) => string | Promise<string>;\r\n return this;\r\n }\r\n\r\n meta<TNextMeta>(\r\n handler: (ctx: {\r\n req: Request;\r\n file: UploadInputFile;\r\n user: TAuth;\r\n }) => TNextMeta | Promise<TNextMeta>\r\n ): UploadBuilder<TAuth, TNextMeta, TMultiple, TKind> {\r\n this._def.meta = handler as (ctx: {\r\n req: Request;\r\n file: UploadInputFile;\r\n user: unknown;\r\n }) => unknown | Promise<unknown>;\r\n return this as unknown as UploadBuilder<TAuth, TNextMeta, TMultiple, TKind>;\r\n }\r\n\r\n validate(\r\n handler: (ctx: {\r\n req: Request;\r\n file: UploadInputFile;\r\n user: TAuth;\r\n meta: TMeta;\r\n }) => true | string | Promise<true | string>\r\n ): this {\r\n this._def.validate = handler as (ctx: {\r\n req: Request;\r\n file: UploadInputFile;\r\n user: unknown;\r\n meta: unknown;\r\n }) => true | string | Promise<true | string>;\r\n return this;\r\n }\r\n\r\n done(handler: (ctx: DoneContext<TAuth, TMeta, TMultiple>) => void | Promise<void>): this {\r\n this._def.done = handler as (ctx: DoneContext<unknown, unknown, boolean>) => void | Promise<void>;\r\n return this;\r\n }\r\n\r\n types(types: string[]): this {\r\n this._def.extensions = types.map((type) => type.toLowerCase());\r\n return this;\r\n }\r\n\r\n dimensions(rule: DimensionRule): this {\r\n this._def.dimensionRule = rule;\r\n return this;\r\n }\r\n\r\n square(): this {\r\n this._def.requireSquare = true;\r\n return this;\r\n }\r\n\r\n aspectRatio(value: `${number}:${number}`): this {\r\n this._def.aspectRatio = value;\r\n return this;\r\n }\r\n\r\n encoding(value: TextEncoding): this {\r\n this._def.encoding = value;\r\n return this;\r\n }\r\n\r\n schema<TSchema extends StandardSchema>(schema: TSchema): this {\r\n this._def.schema = schema;\r\n return this;\r\n }\r\n\r\n headers(headers: string[]): this {\r\n this._def.headers = headers;\r\n return this;\r\n }\r\n\r\n delimiter(value: \",\" | \";\" | \"\\t\" | \"|\"): this {\r\n this._def.delimiter = value;\r\n return this;\r\n }\r\n\r\n pages(rule: PageRule): this {\r\n this._def.pageRule = rule;\r\n return this;\r\n }\r\n\r\n encrypted(value: boolean): this {\r\n this._def.encrypted = value;\r\n return this;\r\n }\r\n\r\n duration(rule: DurationRule): this {\r\n this._def.durationRule = rule;\r\n return this;\r\n }\r\n}\r\n\r\nexport function any(): UploadBuilder<unknown, unknown, false, \"any\"> {\r\n return new UploadBuilder(\"any\");\r\n}\r\n\r\nexport function image(): UploadBuilder<unknown, unknown, false, \"image\"> {\r\n return new UploadBuilder(\"image\", [\"image/\"], [\"png\", \"jpg\", \"jpeg\", \"webp\", \"gif\", \"avif\"]);\r\n}\r\n\r\nexport function pdf(): UploadBuilder<unknown, unknown, false, \"pdf\"> {\r\n return new UploadBuilder(\"pdf\", [\"application/pdf\"], [\"pdf\"]);\r\n}\r\n\r\nexport function video(): UploadBuilder<unknown, unknown, false, \"video\"> {\r\n return new UploadBuilder(\"video\", [\"video/\"], [\"mp4\", \"webm\", \"mov\", \"avi\", \"mkv\"]);\r\n}\r\n\r\nexport function audio(): UploadBuilder<unknown, unknown, false, \"audio\"> {\r\n return new UploadBuilder(\"audio\", [\"audio/\"], [\"mp3\", \"wav\", \"ogg\", \"m4a\", \"aac\", \"flac\"]);\r\n}\r\n\r\nexport function text(): UploadBuilder<unknown, unknown, false, \"text\"> {\r\n return new UploadBuilder(\"text\", [\"text/\"], [\"txt\", \"md\", \"log\"]);\r\n}\r\n\r\nexport function json(): UploadBuilder<unknown, unknown, false, \"json\"> {\r\n return new UploadBuilder(\"json\", [\"application/json\"], [\"json\"]);\r\n}\r\n\r\nexport function csv(): UploadBuilder<unknown, unknown, false, \"csv\"> {\r\n return new UploadBuilder(\"csv\", [\"text/csv\"], [\"csv\"]);\r\n}\r\n\r\nexport function custom(type: string | string[]): UploadBuilder<unknown, unknown, false, \"custom\"> {\r\n const mimeTypes = Array.isArray(type) ? type : [type];\r\n return new UploadBuilder(\"custom\", mimeTypes);\r\n}\r\n","import { UploadError, type UpliftApp, type UploadClient } from \"./types\";\r\n\r\nexport type UploadProgressHandler = (progress: number) => void;\r\n\r\nexport function createUploadClient<TApp extends UpliftApp>(\r\n baseUrl: string,\r\n options: { fetch?: typeof fetch; onProgress?: (route: string, progress: number) => void } = {}\r\n): UploadClient<TApp> {\r\n const fetcher = options.fetch ?? fetch;\r\n\r\n return new Proxy({}, {\r\n get(_target, property) {\r\n if (typeof property !== \"string\") return undefined;\r\n return async (input: File | File[] | FileList) => {\r\n const files = input instanceof File ? [input] : Array.from(input);\r\n const form = new FormData();\r\n const field = files.length === 1 ? \"file\" : \"files\";\r\n for (const file of files) form.append(field, file);\r\n\r\n const url = routeUrl(baseUrl, property);\r\n if (!options.fetch && typeof XMLHttpRequest !== \"undefined\") {\r\n return uploadWithXhr(url, form, property, options.onProgress);\r\n }\r\n\r\n options.onProgress?.(property, 0);\r\n const response = await fetcher(url, { method: \"POST\", body: form });\r\n const body = await response.json() as { result?: unknown; error?: { code: string; message: string } };\r\n if (!response.ok) {\r\n throw new UploadError((body.error?.code ?? \"UNKNOWN\") as never, body.error?.message ?? \"Upload failed.\");\r\n }\r\n options.onProgress?.(property, 100);\r\n return body.result;\r\n };\r\n }\r\n }) as UploadClient<TApp>;\r\n}\r\n\r\nfunction routeUrl(baseUrl: string, route: string): string {\r\n const url = new URL(baseUrl, globalThis.location?.href ?? \"http://localhost\");\r\n url.searchParams.set(\"route\", route);\r\n const value = url.toString();\r\n return baseUrl.startsWith(\"/\") ? `${url.pathname}${url.search}` : value;\r\n}\r\n\r\nfunction uploadWithXhr(\r\n url: string,\r\n form: FormData,\r\n route: string,\r\n onProgress?: (route: string, progress: number) => void\r\n): Promise<unknown> {\r\n return new Promise((resolve, reject) => {\r\n const xhr = new XMLHttpRequest();\r\n xhr.open(\"POST\", url);\r\n xhr.upload.onprogress = (event) => {\r\n if (!event.lengthComputable) return;\r\n onProgress?.(route, Math.round((event.loaded / event.total) * 100));\r\n };\r\n xhr.onload = () => {\r\n const body = JSON.parse(xhr.responseText || \"{}\") as {\r\n result?: unknown;\r\n error?: { code: string; message: string };\r\n };\r\n if (xhr.status < 200 || xhr.status >= 300) {\r\n reject(new UploadError((body.error?.code ?? \"UNKNOWN\") as never, body.error?.message ?? \"Upload failed.\"));\r\n return;\r\n }\r\n onProgress?.(route, 100);\r\n resolve(body.result);\r\n };\r\n xhr.onerror = () => reject(new UploadError(\"UPLOAD_FAILED\", \"Upload request failed.\"));\r\n onProgress?.(route, 0);\r\n xhr.send(form);\r\n });\r\n}\r\n","export {\r\n any,\r\n audio,\r\n csv,\r\n custom,\r\n image,\r\n json,\r\n pdf,\r\n text,\r\n UploadBuilder,\r\n video,\r\n type AudioExtension,\r\n type DimensionRule,\r\n type DurationRule,\r\n type ImageExtension,\r\n type PageRule,\r\n type TextEncoding,\r\n type TextExtension,\r\n type VideoExtension\r\n} from \"./builder\";\r\nexport { createUploadClient } from \"./client\";\r\n// export { createMemoryStorage } from \"./storage/memory\";\r\nexport {\r\n UploadError,\r\n type ClientInput,\r\n type ClientOutput,\r\n type DoneContext,\r\n type KeyContext,\r\n type Middleware,\r\n type SizeValue,\r\n type StandardSchema,\r\n type StorageAdapter,\r\n type StoragePutInput,\r\n type UploadedFile,\r\n type UploadErrorCode,\r\n type UploadInputFile,\r\n type UploadClient,\r\n type UpliftApp\r\n} from \"./types\";\r\n\r\nimport type { Middleware, StorageAdapter, UpliftApp, UploadRoutes } from \"./types\";\r\n\r\nexport function uplift<TRoutes extends UploadRoutes>(config: {\r\n storage: StorageAdapter;\r\n routes: TRoutes;\r\n middleware?: Middleware<unknown>;\r\n onUploadComplete?: UpliftApp<TRoutes>[\"onUploadComplete\"];\r\n}): UpliftApp<TRoutes> {\r\n return config;\r\n}\r\n"],"mappings":"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","names":[]}
|
package/dist/react.cjs
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/react.ts
|
|
21
|
+
var react_exports = {};
|
|
22
|
+
__export(react_exports, {
|
|
23
|
+
useUploads: () => useUploads
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(react_exports);
|
|
26
|
+
var import_react = require("react");
|
|
27
|
+
|
|
28
|
+
// src/types.ts
|
|
29
|
+
var UploadError = class extends Error {
|
|
30
|
+
code;
|
|
31
|
+
constructor(code, message) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = "UploadError";
|
|
34
|
+
this.code = code;
|
|
35
|
+
}
|
|
36
|
+
toJSON() {
|
|
37
|
+
return {
|
|
38
|
+
message: this.message,
|
|
39
|
+
code: this.code
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// src/client.ts
|
|
45
|
+
function createUploadClient(baseUrl, options = {}) {
|
|
46
|
+
const fetcher = options.fetch ?? fetch;
|
|
47
|
+
return new Proxy({}, {
|
|
48
|
+
get(_target, property) {
|
|
49
|
+
if (typeof property !== "string") return void 0;
|
|
50
|
+
return async (input) => {
|
|
51
|
+
const files = input instanceof File ? [input] : Array.from(input);
|
|
52
|
+
const form = new FormData();
|
|
53
|
+
const field = files.length === 1 ? "file" : "files";
|
|
54
|
+
for (const file of files) form.append(field, file);
|
|
55
|
+
const url = routeUrl(baseUrl, property);
|
|
56
|
+
if (!options.fetch && typeof XMLHttpRequest !== "undefined") {
|
|
57
|
+
return uploadWithXhr(url, form, property, options.onProgress);
|
|
58
|
+
}
|
|
59
|
+
options.onProgress?.(property, 0);
|
|
60
|
+
const response = await fetcher(url, { method: "POST", body: form });
|
|
61
|
+
const body = await response.json();
|
|
62
|
+
if (!response.ok) {
|
|
63
|
+
throw new UploadError(body.error?.code ?? "UNKNOWN", body.error?.message ?? "Upload failed.");
|
|
64
|
+
}
|
|
65
|
+
options.onProgress?.(property, 100);
|
|
66
|
+
return body.result;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function routeUrl(baseUrl, route) {
|
|
72
|
+
const url = new URL(baseUrl, globalThis.location?.href ?? "http://localhost");
|
|
73
|
+
url.searchParams.set("route", route);
|
|
74
|
+
const value = url.toString();
|
|
75
|
+
return baseUrl.startsWith("/") ? `${url.pathname}${url.search}` : value;
|
|
76
|
+
}
|
|
77
|
+
function uploadWithXhr(url, form, route, onProgress) {
|
|
78
|
+
return new Promise((resolve, reject) => {
|
|
79
|
+
const xhr = new XMLHttpRequest();
|
|
80
|
+
xhr.open("POST", url);
|
|
81
|
+
xhr.upload.onprogress = (event) => {
|
|
82
|
+
if (!event.lengthComputable) return;
|
|
83
|
+
onProgress?.(route, Math.round(event.loaded / event.total * 100));
|
|
84
|
+
};
|
|
85
|
+
xhr.onload = () => {
|
|
86
|
+
const body = JSON.parse(xhr.responseText || "{}");
|
|
87
|
+
if (xhr.status < 200 || xhr.status >= 300) {
|
|
88
|
+
reject(new UploadError(body.error?.code ?? "UNKNOWN", body.error?.message ?? "Upload failed."));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
onProgress?.(route, 100);
|
|
92
|
+
resolve(body.result);
|
|
93
|
+
};
|
|
94
|
+
xhr.onerror = () => reject(new UploadError("UPLOAD_FAILED", "Upload request failed."));
|
|
95
|
+
onProgress?.(route, 0);
|
|
96
|
+
xhr.send(form);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/react.ts
|
|
101
|
+
function useUploads(baseUrl) {
|
|
102
|
+
const [states, setStates] = (0, import_react.useState)({});
|
|
103
|
+
return (0, import_react.useMemo)(() => {
|
|
104
|
+
const client = createUploadClient(baseUrl, {
|
|
105
|
+
onProgress(route, progress) {
|
|
106
|
+
setStates((current) => ({
|
|
107
|
+
...current,
|
|
108
|
+
[route]: { ...current[route] ?? emptyState(), progress }
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
return new Proxy({}, {
|
|
113
|
+
get(_target, property) {
|
|
114
|
+
if (typeof property !== "string") return void 0;
|
|
115
|
+
const state = states[property] ?? emptyState();
|
|
116
|
+
const method = async (input) => {
|
|
117
|
+
setStates((current) => ({
|
|
118
|
+
...current,
|
|
119
|
+
[property]: { ...current[property] ?? emptyState(), isUploading: true, error: null }
|
|
120
|
+
}));
|
|
121
|
+
try {
|
|
122
|
+
const upload = client[property];
|
|
123
|
+
if (!upload) throw new Error(`Unknown upload route: ${property}`);
|
|
124
|
+
const data = await upload(input);
|
|
125
|
+
setStates((current) => ({
|
|
126
|
+
...current,
|
|
127
|
+
[property]: { progress: 100, isUploading: false, error: null, data }
|
|
128
|
+
}));
|
|
129
|
+
return data;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
setStates((current) => ({
|
|
132
|
+
...current,
|
|
133
|
+
[property]: { ...current[property] ?? emptyState(), isUploading: false, error }
|
|
134
|
+
}));
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
return Object.assign(method, state);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}, [baseUrl, states]);
|
|
142
|
+
}
|
|
143
|
+
function emptyState() {
|
|
144
|
+
return {
|
|
145
|
+
progress: 0,
|
|
146
|
+
isUploading: false,
|
|
147
|
+
error: null,
|
|
148
|
+
data: null
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
152
|
+
0 && (module.exports = {
|
|
153
|
+
useUploads
|
|
154
|
+
});
|
|
155
|
+
//# sourceMappingURL=react.cjs.map
|