@portabyte/node 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +166 -0
- package/dist/index.cjs +462 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +194 -0
- package/dist/index.d.ts +194 -0
- package/dist/index.js +433 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/client.ts","../src/assets.ts"],"sourcesContent":["import { FilesAPI } from './assets';\nimport { HttpClient } from './client';\nimport { PortabyteError } from './errors';\nimport type {\n Asset,\n AssetDeliveryURL,\n BrowserUploadSession,\n CreateSession,\n CreateSessionOptions,\n CreateSessionRequest,\n ListAssetsResult,\n MultipartPart,\n MultipartUploadOptions,\n MultipartUploadState,\n ResumeUploadRequest,\n UploadRequest,\n} from './types';\n\nexport const VERSION = '0.0.1';\n\nexport interface PortabyteOptions {\n apiKey: string;\n baseUrl?: string;\n // idempotent requests only; mutating requests never retry\n maxRetries?: number;\n timeoutMs?: number;\n fetch?: typeof fetch;\n}\n\nconst DEFAULT_BASE_URL = 'https://api.portabyte.dev';\n\n/**\n * Server-side Portabyte client. Its API key is scoped to one project, so a\n * project ID is never required in application configuration.\n */\nexport class Portabyte {\n /** Preferred API for application file uploads. */\n readonly files: FilesAPI;\n\n constructor(options: PortabyteOptions) {\n if (!options.apiKey.startsWith('pbt_sk_live_')) {\n throw new PortabyteError(\n 'apiKey must be a server API key starting with \"pbt_sk_live_\".',\n 0,\n 'invalid_argument',\n );\n }\n const http = new HttpClient({\n baseUrl: (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, ''),\n apiKey: options.apiKey,\n fetchImpl: options.fetch ?? fetch,\n maxRetries: options.maxRetries ?? 2,\n timeoutMs: options.timeoutMs ?? 30_000,\n sdkHeaderValue: `typescript/${VERSION}`,\n });\n this.files = new FilesAPI(http);\n }\n}\n\nexport { PortabyteError };\nexport type {\n Asset,\n AssetDeliveryURL,\n BrowserUploadSession,\n CreateSession,\n CreateSessionOptions,\n CreateSessionRequest,\n ListAssetsResult,\n MultipartPart,\n MultipartUploadOptions,\n MultipartUploadState,\n ResumeUploadRequest,\n UploadRequest,\n};\n","/**\n * Thrown for any failed Portabyte API request. `status` is `0` when the\n * request never reached the API.\n */\nexport class PortabyteError extends Error {\n readonly status: number;\n readonly code: string;\n readonly requestId?: string;\n\n constructor(\n message: string,\n status: number,\n code: string,\n requestId?: string,\n ) {\n super(message);\n this.name = 'PortabyteError';\n this.status = status;\n this.code = code;\n this.requestId = requestId;\n }\n}\n","import { PortabyteError } from './errors';\n\nexport interface RequestOptions {\n method: string;\n path: string;\n body?: unknown;\n}\n\nexport const SDK_HEADER_NAME = 'X-Portabyte-SDK';\n\nexport interface HttpClientOptions {\n baseUrl: string;\n apiKey: string;\n fetchImpl: typeof fetch;\n maxRetries: number;\n timeoutMs: number;\n sdkHeaderValue: string;\n}\n\nclass RetryableError extends Error {\n constructor(\n readonly retryAfterMs: number | null,\n readonly fallback: PortabyteError,\n ) {\n super('retryable request failure');\n }\n}\n\nexport class HttpClient {\n constructor(private readonly options: HttpClientOptions) {}\n\n async request<T>(options: RequestOptions): Promise<T> {\n const idempotent = options.method === 'GET';\n return this.run(idempotent, async () => {\n const response = await this.send(this.options.baseUrl + options.path, {\n method: options.method,\n headers: {\n Authorization: `Bearer ${this.options.apiKey}`,\n [SDK_HEADER_NAME]: this.options.sdkHeaderValue,\n ...(options.body !== undefined || options.method !== 'GET'\n ? { 'Content-Type': 'application/json' }\n : {}),\n },\n body:\n options.body === undefined ? undefined : JSON.stringify(options.body),\n });\n if (!response.ok) {\n throw await responseError(response, idempotent);\n }\n if (response.status === 204) {\n return undefined as T;\n }\n return (await response.json()) as T;\n });\n }\n\n // The gateway's CORS policy allows only Content-Type; the signed URL is\n // the authorization, and the runtime sets the exact-match Content-Length.\n async putBytes(\n uploadUrl: string,\n contentType: string,\n data: Blob | Uint8Array,\n ): Promise<void> {\n await this.run(true, async () => {\n const response = await this.send(uploadUrl, {\n method: 'PUT',\n headers: { 'Content-Type': contentType },\n body: data as BodyInit,\n });\n if (!response.ok) {\n throw await responseError(response, true);\n }\n });\n }\n\n async putBytesJSON<T>(\n uploadUrl: string,\n contentType: string,\n data: Blob | Uint8Array,\n ): Promise<T> {\n return this.run(true, async () => {\n const response = await this.send(uploadUrl, {\n method: 'PUT',\n headers: { 'Content-Type': contentType },\n body: data as BodyInit,\n });\n if (!response.ok) {\n throw await responseError(response, true);\n }\n return (await response.json()) as T;\n });\n }\n\n async uploadJSON<T>(\n uploadUrl: string,\n method: 'POST' | 'PUT' | 'DELETE',\n body: unknown,\n idempotent: boolean,\n ): Promise<T> {\n return this.run(idempotent, async () => {\n const response = await this.send(uploadUrl, {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n if (!response.ok) {\n throw await responseError(response, idempotent);\n }\n if (response.status === 204) {\n return undefined as T;\n }\n return (await response.json()) as T;\n });\n }\n\n private async send(url: string, init: RequestInit): Promise<Response> {\n try {\n return await this.options.fetchImpl(url, {\n ...init,\n signal:\n this.options.timeoutMs > 0\n ? AbortSignal.timeout(this.options.timeoutMs)\n : undefined,\n });\n } catch (cause) {\n throw new RetryableError(\n null,\n new PortabyteError(\n cause instanceof Error ? cause.message : 'Network request failed.',\n 0,\n 'network_error',\n ),\n );\n }\n }\n\n private async run<T>(\n idempotent: boolean,\n send: () => Promise<T>,\n ): Promise<T> {\n for (let attempt = 0; ; attempt++) {\n try {\n return await send();\n } catch (error) {\n const retry = error instanceof RetryableError;\n if (!retry || !idempotent || attempt >= this.options.maxRetries) {\n throw retry ? error.fallback : error;\n }\n await sleep(error.retryAfterMs ?? backoffMs(attempt));\n }\n }\n }\n}\n\nasync function responseError(\n response: Response,\n idempotent: boolean,\n): Promise<Error> {\n const error = await errorFromResponse(response);\n if (idempotent && (response.status === 429 || response.status >= 500)) {\n const retryAfter = Number(response.headers.get('retry-after'));\n const retryAfterMs =\n Number.isFinite(retryAfter) && retryAfter >= 0 ? retryAfter * 1000 : null;\n return new RetryableError(retryAfterMs, error);\n }\n return error;\n}\n\nexport function backoffMs(attempt: number): number {\n const baseMs = 500;\n const capMs = 8_000;\n return Math.floor(Math.random() * Math.min(capMs, baseMs * 2 ** attempt));\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function errorFromResponse(response: Response): Promise<PortabyteError> {\n let code = 'request_failed';\n let message = `Request failed with status ${response.status}.`;\n let requestId: string | undefined;\n try {\n const body = (await response.json()) as {\n code?: string;\n message?: string;\n requestId?: string;\n };\n code = body.code ?? code;\n message = body.message ?? message;\n requestId = body.requestId;\n } catch {\n // non-JSON error body\n }\n return new PortabyteError(message, response.status, code, requestId);\n}\n","import { HttpClient } from './client';\nimport { PortabyteError } from './errors';\nimport type {\n Asset,\n AssetDeliveryURL,\n BrowserUploadSession,\n CreateSession,\n CreateSessionRequest,\n ListAssetsResult,\n MultipartPart,\n MultipartUploadOptions,\n MultipartUploadState,\n ResumeUploadRequest,\n UploadRequest,\n} from './types';\n\nexport interface ListOptions {\n cursor?: string;\n limit?: number;\n}\n\nexport class FilesAPI {\n constructor(private readonly http: HttpClient) {}\n\n /** Creates a signed upload session. Prefer {@link upload}, which runs every step. */\n async create(input: CreateSessionRequest): Promise<CreateSession> {\n const body = {\n name: input.name,\n contentType: input.contentType,\n sizeBytes: input.sizeBytes,\n ...(input.path !== undefined && { path: input.path }),\n ...(input.visibility !== undefined && { visibility: input.visibility }),\n ...(input.corsOrigin !== undefined && { corsOrigin: input.corsOrigin }),\n };\n return this.http.request<CreateSession>({\n method: 'POST',\n path: this.path('assets'),\n body,\n });\n }\n\n /**\n * Prepares a direct browser upload. Call this only from your trusted server,\n * then return the result to the browser. The browser uploads bytes directly\n * to uploadUrl; call {@link confirm} from your server once it reports success.\n */\n async prepareBrowserUpload(\n input: CreateSessionRequest,\n ): Promise<BrowserUploadSession> {\n const session = await this.create(input);\n return {\n assetId: session.id,\n uploadUrl: session.uploadUrl,\n uploadExpiresAt: session.uploadExpiresAt,\n uploadMode: session.uploadMode,\n ...(session.partSize !== undefined && { partSize: session.partSize }),\n ...(session.maxConcurrency !== undefined && {\n maxConcurrency: session.maxConcurrency,\n }),\n };\n }\n\n /**\n * Confirms a completed direct upload and returns its live asset record.\n * Call this from your trusted server, never from a browser.\n */\n async confirm(assetID: string): Promise<Asset> {\n return this.http.request<Asset>({\n method: 'POST',\n path: this.path(`assets/${assetID}/uploaded`),\n });\n }\n\n /** Uploads a File, Blob, or bytes end to end. */\n async upload(request: UploadRequest): Promise<Asset> {\n const described = describeUpload(request);\n const { file, multipart } = request;\n const createOptions = {\n ...(request.path !== undefined && { path: request.path }),\n ...(request.visibility !== undefined && { visibility: request.visibility }),\n ...(request.corsOrigin !== undefined && { corsOrigin: request.corsOrigin }),\n };\n const session = await this.create({ ...described, ...createOptions });\n await this.transfer(\n session,\n file,\n described.contentType,\n multipart,\n );\n try {\n return await this.confirm(session.id);\n } catch (error) {\n if (error instanceof PortabyteError && error.status !== 0) {\n await this.remove(session.id).catch(() => undefined);\n }\n throw error;\n }\n }\n\n /**\n * Continues a previously-created session. Persist the create session and\n * MultipartUploadState after each part to resume after an interruption.\n */\n async resume(\n session: CreateSession,\n request: ResumeUploadRequest,\n ): Promise<Asset> {\n const contentType =\n request.contentType ??\n (request.file instanceof Blob ? request.file.type : '');\n if (\n fileSize(request.file) !== session.sizeBytes ||\n contentType !== session.contentType\n ) {\n throw new PortabyteError(\n 'The selected file does not match this upload session.',\n 0,\n 'invalid_argument',\n );\n }\n await this.transfer(session, request.file, contentType, request);\n return this.confirm(session.id);\n }\n\n async list(options: ListOptions = {}): Promise<ListAssetsResult> {\n const params = new URLSearchParams();\n if (options.cursor) params.set('cursor', options.cursor);\n if (options.limit !== undefined) params.set('limit', String(options.limit));\n const query = params.size > 0 ? `?${params}` : '';\n return this.http.request<ListAssetsResult>({\n method: 'GET',\n path: this.path(`assets${query}`),\n });\n }\n\n async get(assetID: string): Promise<Asset> {\n return this.http.request<Asset>({\n method: 'GET',\n path: this.path(`assets/${assetID}`),\n });\n }\n\n async remove(assetID: string): Promise<void> {\n await this.http.request<void>({\n method: 'DELETE',\n path: this.path(`assets/${assetID}`),\n });\n }\n\n /**\n * Cancels a multipart transfer and removes its pending asset. The gateway\n * abort is best-effort: removing the pending asset guarantees the object\n * can never be confirmed or delivered, even if its signed URL has expired.\n */\n async cancel(\n session: CreateSession,\n state?: MultipartUploadState,\n ): Promise<void> {\n if (session.uploadMode === 'multipart' && state?.uploadId) {\n await this.http\n .uploadJSON(\n `${session.uploadUrl}/multipart/${state.uploadId}`,\n 'DELETE',\n undefined,\n false,\n )\n .catch(() => undefined);\n }\n await this.remove(session.id);\n }\n\n /**\n * Returns the URL the asset is served from: stable and cacheable for\n * public assets, short-lived signed otherwise.\n */\n async url(assetID: string): Promise<AssetDeliveryURL> {\n return this.http.request<AssetDeliveryURL>({\n method: 'GET',\n path: this.path(`assets/${assetID}/url`),\n });\n }\n\n private path(suffix: string): string {\n return `/v1/${suffix}`;\n }\n\n private async transfer(\n session: CreateSession,\n body: Blob | Uint8Array,\n contentType: string,\n options?: MultipartUploadOptions,\n ): Promise<void> {\n if (session.uploadMode === 'single') {\n await this.http.putBytes(session.uploadUrl, contentType, body);\n return;\n }\n await this.uploadMultipart(session, body, contentType, options);\n }\n\n private async uploadMultipart(\n session: CreateSession,\n body: Blob | Uint8Array,\n contentType: string,\n options: MultipartUploadOptions = {},\n ): Promise<void> {\n if (!session.partSize || session.partSize < 5 * 1024 * 1024) {\n throw new PortabyteError(\n 'Multipart upload session is missing a valid part size.',\n 0,\n 'invalid_upload',\n );\n }\n const state: MultipartUploadState = {\n uploadId: options.state?.uploadId,\n parts: [...(options.state?.parts ?? [])],\n };\n if (!state.uploadId) {\n const started = await this.http.uploadJSON<{ uploadId: string }>(\n `${session.uploadUrl}/multipart`,\n 'POST',\n {},\n false,\n );\n state.uploadId = started.uploadId;\n await options.onStateChange?.({ ...state, parts: [...state.parts] });\n }\n const partCount = Math.ceil(session.sizeBytes / session.partSize);\n const completed = new Map(\n state.parts.map((part) => [part.partNumber, part]),\n );\n const concurrency = Math.max(\n 1,\n Math.min(options.concurrency ?? session.maxConcurrency ?? 3, 3),\n );\n let nextPart = 1;\n const uploadNext = async () => {\n for (;;) {\n const partNumber = nextPart;\n nextPart += 1;\n if (partNumber > partCount) return;\n if (completed.has(partNumber)) continue;\n const start = (partNumber - 1) * session.partSize!;\n const end = Math.min(start + session.partSize!, session.sizeBytes);\n const part = await this.http.putBytesJSON<MultipartPart>(\n `${session.uploadUrl}/multipart/${state.uploadId}/parts/${partNumber}`,\n contentType,\n sliceBody(body, start, end),\n );\n completed.set(part.partNumber, part);\n state.parts = [...completed.values()].sort(\n (left, right) => left.partNumber - right.partNumber,\n );\n await options.onStateChange?.({ ...state, parts: [...state.parts] });\n }\n };\n await Promise.all(\n Array.from({ length: Math.min(concurrency, partCount) }, uploadNext),\n );\n await this.http.uploadJSON(\n `${session.uploadUrl}/multipart/${state.uploadId}/complete`,\n 'POST',\n { parts: state.parts },\n true,\n );\n }\n}\n\nfunction describeUpload(request: UploadRequest): {\n name: string;\n contentType: string;\n sizeBytes: number;\n} {\n const filename = request.name ?? fileName(request.file);\n const mimeType =\n request.contentType ?? (request.file instanceof Blob ? request.file.type : '');\n if (!filename) {\n throw new PortabyteError(\n 'A file name is required when uploading a Blob or bytes.',\n 0,\n 'invalid_argument',\n );\n }\n if (!mimeType) {\n throw new PortabyteError(\n 'A content type is required when uploading bytes or a Blob without a type.',\n 0,\n 'invalid_argument',\n );\n }\n return {\n name: filename,\n contentType: mimeType,\n sizeBytes:\n request.file instanceof Blob ? request.file.size : request.file.byteLength,\n };\n}\n\nfunction fileName(file: Blob | Uint8Array): string | undefined {\n if (!(file instanceof Blob)) return undefined;\n const name = (file as Blob & { name?: unknown }).name;\n return typeof name === 'string' && name.length > 0 ? name : undefined;\n}\n\nfunction fileSize(file: Blob | Uint8Array): number {\n return file instanceof Blob ? file.size : file.byteLength;\n}\n\nfunction sliceBody(\n body: Blob | Uint8Array,\n start: number,\n end: number,\n): Blob | Uint8Array {\n return body instanceof Blob ? body.slice(start, end) : body.slice(start, end);\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
interface RequestOptions {
|
|
2
|
+
method: string;
|
|
3
|
+
path: string;
|
|
4
|
+
body?: unknown;
|
|
5
|
+
}
|
|
6
|
+
interface HttpClientOptions {
|
|
7
|
+
baseUrl: string;
|
|
8
|
+
apiKey: string;
|
|
9
|
+
fetchImpl: typeof fetch;
|
|
10
|
+
maxRetries: number;
|
|
11
|
+
timeoutMs: number;
|
|
12
|
+
sdkHeaderValue: string;
|
|
13
|
+
}
|
|
14
|
+
declare class HttpClient {
|
|
15
|
+
private readonly options;
|
|
16
|
+
constructor(options: HttpClientOptions);
|
|
17
|
+
request<T>(options: RequestOptions): Promise<T>;
|
|
18
|
+
putBytes(uploadUrl: string, contentType: string, data: Blob | Uint8Array): Promise<void>;
|
|
19
|
+
putBytesJSON<T>(uploadUrl: string, contentType: string, data: Blob | Uint8Array): Promise<T>;
|
|
20
|
+
uploadJSON<T>(uploadUrl: string, method: 'POST' | 'PUT' | 'DELETE', body: unknown, idempotent: boolean): Promise<T>;
|
|
21
|
+
private send;
|
|
22
|
+
private run;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type AssetVisibility = 'private' | 'public';
|
|
26
|
+
interface Asset {
|
|
27
|
+
id: string;
|
|
28
|
+
projectId: string;
|
|
29
|
+
name: string;
|
|
30
|
+
contentType: string;
|
|
31
|
+
sizeBytes: number;
|
|
32
|
+
path?: string;
|
|
33
|
+
etag?: string;
|
|
34
|
+
visibility: AssetVisibility;
|
|
35
|
+
publicUrl?: string;
|
|
36
|
+
createdAt: string;
|
|
37
|
+
}
|
|
38
|
+
interface CreateSessionOptions {
|
|
39
|
+
/**
|
|
40
|
+
* Customer-owned address, e.g. `reports/2026/may/x.pdf` (1–32 segments of
|
|
41
|
+
* letters, digits, `.`, `_`, `~`, `-`). Uploading to a path that already
|
|
42
|
+
* has a live file replaces it.
|
|
43
|
+
*/
|
|
44
|
+
path?: string;
|
|
45
|
+
visibility?: AssetVisibility;
|
|
46
|
+
corsOrigin?: string;
|
|
47
|
+
}
|
|
48
|
+
/** Input used by a trusted server to prepare an upload session. */
|
|
49
|
+
interface CreateSessionRequest extends CreateSessionOptions {
|
|
50
|
+
name: string;
|
|
51
|
+
contentType: string;
|
|
52
|
+
sizeBytes: number;
|
|
53
|
+
}
|
|
54
|
+
type CreateSession = Asset & {
|
|
55
|
+
uploadUrl: string;
|
|
56
|
+
uploadExpiresAt: string;
|
|
57
|
+
uploadMode: 'single' | 'multipart';
|
|
58
|
+
/** Present only when uploadMode is multipart. */
|
|
59
|
+
partSize?: number;
|
|
60
|
+
/** Recommended maximum simultaneous part uploads. */
|
|
61
|
+
maxConcurrency?: number;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* The safe subset of an upload session to return from your server to a
|
|
65
|
+
* browser. It contains no Portabyte API key or asset delivery URL.
|
|
66
|
+
*/
|
|
67
|
+
interface BrowserUploadSession {
|
|
68
|
+
assetId: string;
|
|
69
|
+
uploadUrl: string;
|
|
70
|
+
uploadExpiresAt: string;
|
|
71
|
+
uploadMode: 'single' | 'multipart';
|
|
72
|
+
partSize?: number;
|
|
73
|
+
maxConcurrency?: number;
|
|
74
|
+
}
|
|
75
|
+
interface MultipartPart {
|
|
76
|
+
partNumber: number;
|
|
77
|
+
etag: string;
|
|
78
|
+
}
|
|
79
|
+
/** Persist this alongside the create session to resume a multipart upload. */
|
|
80
|
+
interface MultipartUploadState {
|
|
81
|
+
uploadId?: string;
|
|
82
|
+
parts: MultipartPart[];
|
|
83
|
+
}
|
|
84
|
+
interface MultipartUploadOptions {
|
|
85
|
+
/** State from an earlier interrupted multipart upload. */
|
|
86
|
+
state?: MultipartUploadState;
|
|
87
|
+
/** Called after a part completes; persist the state here to enable resume. */
|
|
88
|
+
onStateChange?: (state: MultipartUploadState) => void | Promise<void>;
|
|
89
|
+
/** Defaults to Portabyte's recommendation, capped at 3. */
|
|
90
|
+
concurrency?: number;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The preferred input for an end-to-end upload.
|
|
94
|
+
*
|
|
95
|
+
* Pass a browser `File` directly. For a `Blob` without a filename or raw
|
|
96
|
+
* bytes, provide `name` and `contentType`.
|
|
97
|
+
*/
|
|
98
|
+
interface UploadRequest extends CreateSessionOptions {
|
|
99
|
+
file: Blob | Uint8Array;
|
|
100
|
+
name?: string;
|
|
101
|
+
contentType?: string;
|
|
102
|
+
multipart?: MultipartUploadOptions;
|
|
103
|
+
}
|
|
104
|
+
/** Input for resuming a previously-created multipart upload. */
|
|
105
|
+
interface ResumeUploadRequest extends MultipartUploadOptions {
|
|
106
|
+
file: Blob | Uint8Array;
|
|
107
|
+
contentType?: string;
|
|
108
|
+
}
|
|
109
|
+
interface ListAssetsResult {
|
|
110
|
+
records: Asset[];
|
|
111
|
+
cursor?: string;
|
|
112
|
+
}
|
|
113
|
+
interface AssetDeliveryURL {
|
|
114
|
+
url: string;
|
|
115
|
+
expiresAt?: string;
|
|
116
|
+
public: boolean;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface ListOptions {
|
|
120
|
+
cursor?: string;
|
|
121
|
+
limit?: number;
|
|
122
|
+
}
|
|
123
|
+
declare class FilesAPI {
|
|
124
|
+
private readonly http;
|
|
125
|
+
constructor(http: HttpClient);
|
|
126
|
+
/** Creates a signed upload session. Prefer {@link upload}, which runs every step. */
|
|
127
|
+
create(input: CreateSessionRequest): Promise<CreateSession>;
|
|
128
|
+
/**
|
|
129
|
+
* Prepares a direct browser upload. Call this only from your trusted server,
|
|
130
|
+
* then return the result to the browser. The browser uploads bytes directly
|
|
131
|
+
* to uploadUrl; call {@link confirm} from your server once it reports success.
|
|
132
|
+
*/
|
|
133
|
+
prepareBrowserUpload(input: CreateSessionRequest): Promise<BrowserUploadSession>;
|
|
134
|
+
/**
|
|
135
|
+
* Confirms a completed direct upload and returns its live asset record.
|
|
136
|
+
* Call this from your trusted server, never from a browser.
|
|
137
|
+
*/
|
|
138
|
+
confirm(assetID: string): Promise<Asset>;
|
|
139
|
+
/** Uploads a File, Blob, or bytes end to end. */
|
|
140
|
+
upload(request: UploadRequest): Promise<Asset>;
|
|
141
|
+
/**
|
|
142
|
+
* Continues a previously-created session. Persist the create session and
|
|
143
|
+
* MultipartUploadState after each part to resume after an interruption.
|
|
144
|
+
*/
|
|
145
|
+
resume(session: CreateSession, request: ResumeUploadRequest): Promise<Asset>;
|
|
146
|
+
list(options?: ListOptions): Promise<ListAssetsResult>;
|
|
147
|
+
get(assetID: string): Promise<Asset>;
|
|
148
|
+
remove(assetID: string): Promise<void>;
|
|
149
|
+
/**
|
|
150
|
+
* Cancels a multipart transfer and removes its pending asset. The gateway
|
|
151
|
+
* abort is best-effort: removing the pending asset guarantees the object
|
|
152
|
+
* can never be confirmed or delivered, even if its signed URL has expired.
|
|
153
|
+
*/
|
|
154
|
+
cancel(session: CreateSession, state?: MultipartUploadState): Promise<void>;
|
|
155
|
+
/**
|
|
156
|
+
* Returns the URL the asset is served from: stable and cacheable for
|
|
157
|
+
* public assets, short-lived signed otherwise.
|
|
158
|
+
*/
|
|
159
|
+
url(assetID: string): Promise<AssetDeliveryURL>;
|
|
160
|
+
private path;
|
|
161
|
+
private transfer;
|
|
162
|
+
private uploadMultipart;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Thrown for any failed Portabyte API request. `status` is `0` when the
|
|
167
|
+
* request never reached the API.
|
|
168
|
+
*/
|
|
169
|
+
declare class PortabyteError extends Error {
|
|
170
|
+
readonly status: number;
|
|
171
|
+
readonly code: string;
|
|
172
|
+
readonly requestId?: string;
|
|
173
|
+
constructor(message: string, status: number, code: string, requestId?: string);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
declare const VERSION = "0.0.1";
|
|
177
|
+
interface PortabyteOptions {
|
|
178
|
+
apiKey: string;
|
|
179
|
+
baseUrl?: string;
|
|
180
|
+
maxRetries?: number;
|
|
181
|
+
timeoutMs?: number;
|
|
182
|
+
fetch?: typeof fetch;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Server-side Portabyte client. Its API key is scoped to one project, so a
|
|
186
|
+
* project ID is never required in application configuration.
|
|
187
|
+
*/
|
|
188
|
+
declare class Portabyte {
|
|
189
|
+
/** Preferred API for application file uploads. */
|
|
190
|
+
readonly files: FilesAPI;
|
|
191
|
+
constructor(options: PortabyteOptions);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export { type Asset, type AssetDeliveryURL, type BrowserUploadSession, type CreateSession, type CreateSessionOptions, type CreateSessionRequest, type ListAssetsResult, type MultipartPart, type MultipartUploadOptions, type MultipartUploadState, Portabyte, PortabyteError, type PortabyteOptions, type ResumeUploadRequest, type UploadRequest, VERSION };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
interface RequestOptions {
|
|
2
|
+
method: string;
|
|
3
|
+
path: string;
|
|
4
|
+
body?: unknown;
|
|
5
|
+
}
|
|
6
|
+
interface HttpClientOptions {
|
|
7
|
+
baseUrl: string;
|
|
8
|
+
apiKey: string;
|
|
9
|
+
fetchImpl: typeof fetch;
|
|
10
|
+
maxRetries: number;
|
|
11
|
+
timeoutMs: number;
|
|
12
|
+
sdkHeaderValue: string;
|
|
13
|
+
}
|
|
14
|
+
declare class HttpClient {
|
|
15
|
+
private readonly options;
|
|
16
|
+
constructor(options: HttpClientOptions);
|
|
17
|
+
request<T>(options: RequestOptions): Promise<T>;
|
|
18
|
+
putBytes(uploadUrl: string, contentType: string, data: Blob | Uint8Array): Promise<void>;
|
|
19
|
+
putBytesJSON<T>(uploadUrl: string, contentType: string, data: Blob | Uint8Array): Promise<T>;
|
|
20
|
+
uploadJSON<T>(uploadUrl: string, method: 'POST' | 'PUT' | 'DELETE', body: unknown, idempotent: boolean): Promise<T>;
|
|
21
|
+
private send;
|
|
22
|
+
private run;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type AssetVisibility = 'private' | 'public';
|
|
26
|
+
interface Asset {
|
|
27
|
+
id: string;
|
|
28
|
+
projectId: string;
|
|
29
|
+
name: string;
|
|
30
|
+
contentType: string;
|
|
31
|
+
sizeBytes: number;
|
|
32
|
+
path?: string;
|
|
33
|
+
etag?: string;
|
|
34
|
+
visibility: AssetVisibility;
|
|
35
|
+
publicUrl?: string;
|
|
36
|
+
createdAt: string;
|
|
37
|
+
}
|
|
38
|
+
interface CreateSessionOptions {
|
|
39
|
+
/**
|
|
40
|
+
* Customer-owned address, e.g. `reports/2026/may/x.pdf` (1–32 segments of
|
|
41
|
+
* letters, digits, `.`, `_`, `~`, `-`). Uploading to a path that already
|
|
42
|
+
* has a live file replaces it.
|
|
43
|
+
*/
|
|
44
|
+
path?: string;
|
|
45
|
+
visibility?: AssetVisibility;
|
|
46
|
+
corsOrigin?: string;
|
|
47
|
+
}
|
|
48
|
+
/** Input used by a trusted server to prepare an upload session. */
|
|
49
|
+
interface CreateSessionRequest extends CreateSessionOptions {
|
|
50
|
+
name: string;
|
|
51
|
+
contentType: string;
|
|
52
|
+
sizeBytes: number;
|
|
53
|
+
}
|
|
54
|
+
type CreateSession = Asset & {
|
|
55
|
+
uploadUrl: string;
|
|
56
|
+
uploadExpiresAt: string;
|
|
57
|
+
uploadMode: 'single' | 'multipart';
|
|
58
|
+
/** Present only when uploadMode is multipart. */
|
|
59
|
+
partSize?: number;
|
|
60
|
+
/** Recommended maximum simultaneous part uploads. */
|
|
61
|
+
maxConcurrency?: number;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* The safe subset of an upload session to return from your server to a
|
|
65
|
+
* browser. It contains no Portabyte API key or asset delivery URL.
|
|
66
|
+
*/
|
|
67
|
+
interface BrowserUploadSession {
|
|
68
|
+
assetId: string;
|
|
69
|
+
uploadUrl: string;
|
|
70
|
+
uploadExpiresAt: string;
|
|
71
|
+
uploadMode: 'single' | 'multipart';
|
|
72
|
+
partSize?: number;
|
|
73
|
+
maxConcurrency?: number;
|
|
74
|
+
}
|
|
75
|
+
interface MultipartPart {
|
|
76
|
+
partNumber: number;
|
|
77
|
+
etag: string;
|
|
78
|
+
}
|
|
79
|
+
/** Persist this alongside the create session to resume a multipart upload. */
|
|
80
|
+
interface MultipartUploadState {
|
|
81
|
+
uploadId?: string;
|
|
82
|
+
parts: MultipartPart[];
|
|
83
|
+
}
|
|
84
|
+
interface MultipartUploadOptions {
|
|
85
|
+
/** State from an earlier interrupted multipart upload. */
|
|
86
|
+
state?: MultipartUploadState;
|
|
87
|
+
/** Called after a part completes; persist the state here to enable resume. */
|
|
88
|
+
onStateChange?: (state: MultipartUploadState) => void | Promise<void>;
|
|
89
|
+
/** Defaults to Portabyte's recommendation, capped at 3. */
|
|
90
|
+
concurrency?: number;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The preferred input for an end-to-end upload.
|
|
94
|
+
*
|
|
95
|
+
* Pass a browser `File` directly. For a `Blob` without a filename or raw
|
|
96
|
+
* bytes, provide `name` and `contentType`.
|
|
97
|
+
*/
|
|
98
|
+
interface UploadRequest extends CreateSessionOptions {
|
|
99
|
+
file: Blob | Uint8Array;
|
|
100
|
+
name?: string;
|
|
101
|
+
contentType?: string;
|
|
102
|
+
multipart?: MultipartUploadOptions;
|
|
103
|
+
}
|
|
104
|
+
/** Input for resuming a previously-created multipart upload. */
|
|
105
|
+
interface ResumeUploadRequest extends MultipartUploadOptions {
|
|
106
|
+
file: Blob | Uint8Array;
|
|
107
|
+
contentType?: string;
|
|
108
|
+
}
|
|
109
|
+
interface ListAssetsResult {
|
|
110
|
+
records: Asset[];
|
|
111
|
+
cursor?: string;
|
|
112
|
+
}
|
|
113
|
+
interface AssetDeliveryURL {
|
|
114
|
+
url: string;
|
|
115
|
+
expiresAt?: string;
|
|
116
|
+
public: boolean;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface ListOptions {
|
|
120
|
+
cursor?: string;
|
|
121
|
+
limit?: number;
|
|
122
|
+
}
|
|
123
|
+
declare class FilesAPI {
|
|
124
|
+
private readonly http;
|
|
125
|
+
constructor(http: HttpClient);
|
|
126
|
+
/** Creates a signed upload session. Prefer {@link upload}, which runs every step. */
|
|
127
|
+
create(input: CreateSessionRequest): Promise<CreateSession>;
|
|
128
|
+
/**
|
|
129
|
+
* Prepares a direct browser upload. Call this only from your trusted server,
|
|
130
|
+
* then return the result to the browser. The browser uploads bytes directly
|
|
131
|
+
* to uploadUrl; call {@link confirm} from your server once it reports success.
|
|
132
|
+
*/
|
|
133
|
+
prepareBrowserUpload(input: CreateSessionRequest): Promise<BrowserUploadSession>;
|
|
134
|
+
/**
|
|
135
|
+
* Confirms a completed direct upload and returns its live asset record.
|
|
136
|
+
* Call this from your trusted server, never from a browser.
|
|
137
|
+
*/
|
|
138
|
+
confirm(assetID: string): Promise<Asset>;
|
|
139
|
+
/** Uploads a File, Blob, or bytes end to end. */
|
|
140
|
+
upload(request: UploadRequest): Promise<Asset>;
|
|
141
|
+
/**
|
|
142
|
+
* Continues a previously-created session. Persist the create session and
|
|
143
|
+
* MultipartUploadState after each part to resume after an interruption.
|
|
144
|
+
*/
|
|
145
|
+
resume(session: CreateSession, request: ResumeUploadRequest): Promise<Asset>;
|
|
146
|
+
list(options?: ListOptions): Promise<ListAssetsResult>;
|
|
147
|
+
get(assetID: string): Promise<Asset>;
|
|
148
|
+
remove(assetID: string): Promise<void>;
|
|
149
|
+
/**
|
|
150
|
+
* Cancels a multipart transfer and removes its pending asset. The gateway
|
|
151
|
+
* abort is best-effort: removing the pending asset guarantees the object
|
|
152
|
+
* can never be confirmed or delivered, even if its signed URL has expired.
|
|
153
|
+
*/
|
|
154
|
+
cancel(session: CreateSession, state?: MultipartUploadState): Promise<void>;
|
|
155
|
+
/**
|
|
156
|
+
* Returns the URL the asset is served from: stable and cacheable for
|
|
157
|
+
* public assets, short-lived signed otherwise.
|
|
158
|
+
*/
|
|
159
|
+
url(assetID: string): Promise<AssetDeliveryURL>;
|
|
160
|
+
private path;
|
|
161
|
+
private transfer;
|
|
162
|
+
private uploadMultipart;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Thrown for any failed Portabyte API request. `status` is `0` when the
|
|
167
|
+
* request never reached the API.
|
|
168
|
+
*/
|
|
169
|
+
declare class PortabyteError extends Error {
|
|
170
|
+
readonly status: number;
|
|
171
|
+
readonly code: string;
|
|
172
|
+
readonly requestId?: string;
|
|
173
|
+
constructor(message: string, status: number, code: string, requestId?: string);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
declare const VERSION = "0.0.1";
|
|
177
|
+
interface PortabyteOptions {
|
|
178
|
+
apiKey: string;
|
|
179
|
+
baseUrl?: string;
|
|
180
|
+
maxRetries?: number;
|
|
181
|
+
timeoutMs?: number;
|
|
182
|
+
fetch?: typeof fetch;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Server-side Portabyte client. Its API key is scoped to one project, so a
|
|
186
|
+
* project ID is never required in application configuration.
|
|
187
|
+
*/
|
|
188
|
+
declare class Portabyte {
|
|
189
|
+
/** Preferred API for application file uploads. */
|
|
190
|
+
readonly files: FilesAPI;
|
|
191
|
+
constructor(options: PortabyteOptions);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export { type Asset, type AssetDeliveryURL, type BrowserUploadSession, type CreateSession, type CreateSessionOptions, type CreateSessionRequest, type ListAssetsResult, type MultipartPart, type MultipartUploadOptions, type MultipartUploadState, Portabyte, PortabyteError, type PortabyteOptions, type ResumeUploadRequest, type UploadRequest, VERSION };
|