@hyperserve/hyperserve-js 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +291 -0
- package/dist/browser.cjs +91 -0
- package/dist/browser.cjs.map +1 -0
- package/dist/browser.d.cts +28 -0
- package/dist/browser.d.ts +28 -0
- package/dist/browser.js +86 -0
- package/dist/browser.js.map +1 -0
- package/dist/errors-C89laaKB.d.cts +176 -0
- package/dist/errors-C89laaKB.d.ts +176 -0
- package/dist/index.cjs +342 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +88 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.js +333 -0
- package/dist/index.js.map +1 -0
- package/dist/react-native.cjs +97 -0
- package/dist/react-native.cjs.map +1 -0
- package/dist/react-native.d.cts +40 -0
- package/dist/react-native.d.ts +40 -0
- package/dist/react-native.js +92 -0
- package/dist/react-native.js.map +1 -0
- package/package.json +89 -0
package/README.md
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# @hyperserve/hyperserve-js
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for the [Hyperserve](https://hyperserve.io) video infrastructure API. Works in Node.js, browsers, and React Native.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @hyperserve/hyperserve-js
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## How it works
|
|
12
|
+
|
|
13
|
+
Hyperserve uploads are a three-step flow. Your **backend** handles steps 1 and 3 (API key stays on the server). Your **frontend or mobile app** handles step 2 (direct to storage, no API key needed).
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
1. Backend → POST /api/video → Hyperserve (get presigned URL)
|
|
17
|
+
2. Client → PUT file to uploadUrl → Storage (upload the bytes)
|
|
18
|
+
3. Backend → POST complete-upload → Hyperserve (verify + queue transcoding)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
### 1. Backend — create the upload
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { HyperserveClient } from '@hyperserve/hyperserve-js';
|
|
29
|
+
|
|
30
|
+
const hyperserve = new HyperserveClient({ apiKey: process.env.HYPERSERVE_API_KEY });
|
|
31
|
+
|
|
32
|
+
// In your API route or server action
|
|
33
|
+
const upload = await hyperserve.createVideo({
|
|
34
|
+
filename: 'promo.mp4',
|
|
35
|
+
fileSizeBytes: 10_485_760,
|
|
36
|
+
resolutions: ['480p', '1080p'],
|
|
37
|
+
isPublic: true,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Return to your client — uploadUrl and contentType are all it needs
|
|
41
|
+
return { videoId: upload.id, uploadUrl: upload.uploadUrl, contentType: upload.contentType };
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 2a. Browser — upload the file
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { putVideoToStorage } from '@hyperserve/hyperserve-js/browser';
|
|
48
|
+
|
|
49
|
+
const { videoId, uploadUrl, contentType } = await fetch('/api/create-upload', {
|
|
50
|
+
method: 'POST',
|
|
51
|
+
body: JSON.stringify({ filename: file.name, fileSizeBytes: file.size }),
|
|
52
|
+
}).then(r => r.json());
|
|
53
|
+
|
|
54
|
+
await putVideoToStorage({
|
|
55
|
+
uploadUrl,
|
|
56
|
+
contentType,
|
|
57
|
+
file, // File from <input type="file">
|
|
58
|
+
onProgress: (percent) => console.log(`${percent}%`),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Tell your backend the upload is done
|
|
62
|
+
await fetch('/api/complete-upload', { method: 'POST', body: JSON.stringify({ videoId }) });
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 2b. React Native — upload the file
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { putVideoToStorage } from '@hyperserve/hyperserve-js/react-native';
|
|
69
|
+
|
|
70
|
+
// asset from expo-image-picker, react-native-image-picker, etc.
|
|
71
|
+
const { videoId, uploadUrl, contentType } = await fetch('https://your-api.com/create-upload', {
|
|
72
|
+
method: 'POST',
|
|
73
|
+
body: JSON.stringify({ filename: asset.fileName, fileSizeBytes: asset.fileSize }),
|
|
74
|
+
}).then(r => r.json());
|
|
75
|
+
|
|
76
|
+
await putVideoToStorage({
|
|
77
|
+
uploadUrl,
|
|
78
|
+
contentType,
|
|
79
|
+
uri: asset.uri, // file:/// URI — SDK handles the rest
|
|
80
|
+
onProgress: (percent) => setProgress(percent),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
await fetch('https://your-api.com/complete-upload', {
|
|
84
|
+
method: 'POST',
|
|
85
|
+
body: JSON.stringify({ videoId }),
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 3. Backend — complete the upload
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
const result = await hyperserve.completeUpload(videoId);
|
|
93
|
+
// Hyperserve verifies the file and queues transcoding
|
|
94
|
+
// result.resolutions — all statuses are now 'processing'
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Server API
|
|
100
|
+
|
|
101
|
+
### `new HyperserveClient(options)`
|
|
102
|
+
|
|
103
|
+
| Option | Type | Required | Default | Description |
|
|
104
|
+
|---|---|---|---|---|
|
|
105
|
+
| `apiKey` | `string` | Yes | — | Your Hyperserve API key |
|
|
106
|
+
| `baseUrl` | `string` | No | `https://api.hyperserve.io` | Override for local dev |
|
|
107
|
+
| `timeoutMs` | `number` | No | `30000` | Timeout for API calls |
|
|
108
|
+
| `retries` | `number` | No | `0` | Retry attempts on 5xx responses and network errors. Uses exponential backoff with full jitter. Does not retry on 4xx or timeouts. |
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
### `createVideo(options)`
|
|
113
|
+
|
|
114
|
+
Creates a video record and returns a presigned upload URL.
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
const upload = await hyperserve.createVideo({
|
|
118
|
+
filename: 'clip.mp4', // required — extension determines content type
|
|
119
|
+
fileSizeBytes: 5_242_880, // required
|
|
120
|
+
resolutions: ['720p', '1080p'],// required — at least one
|
|
121
|
+
isPublic: true, // required
|
|
122
|
+
thumbnailTimestampsSeconds: [5, 30, 60], // optional
|
|
123
|
+
customMetadata: { campaignId: 'launch' }, // optional
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
upload.id // video ID for subsequent calls
|
|
127
|
+
upload.uploadUrl // presigned PUT URL — expires shortly, pass to your client
|
|
128
|
+
upload.contentType // exact Content-Type to use on the PUT
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### `completeUpload(videoId)`
|
|
134
|
+
|
|
135
|
+
Notifies Hyperserve that the file has been uploaded. Verifies the object and queues transcoding.
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
const result = await hyperserve.completeUpload(videoId);
|
|
139
|
+
result.resolutions // Record<VideoResolution, { status: 'processing' }>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
### `getVideo(videoId, options?)`
|
|
145
|
+
|
|
146
|
+
Retrieves video status and playback URLs.
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
// Public video
|
|
150
|
+
const video = await hyperserve.getVideo(videoId);
|
|
151
|
+
|
|
152
|
+
// Private video — time-limited signed URLs
|
|
153
|
+
const video = await hyperserve.getVideo(videoId, {
|
|
154
|
+
private: true,
|
|
155
|
+
expirationSeconds: 3600, // defaults to 3600
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
video.status // 'pending_upload' | 'processing' | 'ready' | 'fail'
|
|
159
|
+
video.resolutions['1080p']?.videoUrl // playback URL (set when status is 'ready')
|
|
160
|
+
video.resolutions['1080p']?.thumbnailImageUrls // array of thumbnail URLs
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
### `deleteVideo(videoId)`
|
|
166
|
+
|
|
167
|
+
Deletes a video and all associated resolutions and thumbnails.
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
await hyperserve.deleteVideo(videoId);
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
### `deleteResolution(resolutionId)`
|
|
176
|
+
|
|
177
|
+
Deletes a single resolution.
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
await hyperserve.deleteResolution(resolutionId);
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
### `uploadVideo(options)` — convenience
|
|
186
|
+
|
|
187
|
+
Wraps `createVideo`, the storage PUT, and `completeUpload` into a single call. Intended for scripts and server-to-server use cases where the server holds the file. **Not suitable for the browser proxy pattern.**
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
import { readFileSync, statSync } from 'fs';
|
|
191
|
+
|
|
192
|
+
const buffer = readFileSync('./promo.mp4');
|
|
193
|
+
const { size } = statSync('./promo.mp4');
|
|
194
|
+
|
|
195
|
+
const result = await hyperserve.uploadVideo({
|
|
196
|
+
file: buffer, // Blob | Buffer | ReadableStream
|
|
197
|
+
filename: 'promo.mp4',
|
|
198
|
+
fileSizeBytes: size, // required for ReadableStream, inferred for Blob/Buffer
|
|
199
|
+
resolutions: ['1080p'],
|
|
200
|
+
isPublic: false,
|
|
201
|
+
});
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
### `verifyWebhookSignature(options)`
|
|
207
|
+
|
|
208
|
+
Verifies the `x-hyperserve-signature` header on incoming webhook requests. Returns `Promise<boolean>` — never throws.
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
import { verifyWebhookSignature } from '@hyperserve/hyperserve-js';
|
|
212
|
+
|
|
213
|
+
const isValid = await verifyWebhookSignature({
|
|
214
|
+
signature: req.headers['x-hyperserve-signature'] ?? '',
|
|
215
|
+
secret: process.env.HYPERSERVE_WEBHOOK_SECRET,
|
|
216
|
+
});
|
|
217
|
+
if (!isValid) return res.status(401).end();
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
| Option | Type | Required | Default | Description |
|
|
221
|
+
|---|---|---|---|---|
|
|
222
|
+
| `signature` | `string` | Yes | — | Value of the `x-hyperserve-signature` header |
|
|
223
|
+
| `secret` | `string` | Yes | — | Webhook signing secret from the Hyperserve dashboard |
|
|
224
|
+
| `toleranceMs` | `number` | No | `300000` | Max timestamp age in ms (default 5 min) |
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Error handling
|
|
229
|
+
|
|
230
|
+
All errors extend `HyperserveError` and can be caught broadly or narrowly.
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
import {
|
|
234
|
+
HyperserveClient,
|
|
235
|
+
HyperserveError,
|
|
236
|
+
HyperserveValidationError,
|
|
237
|
+
HyperserveNotFoundError,
|
|
238
|
+
HyperserveUploadError,
|
|
239
|
+
HyperserveTimeoutError,
|
|
240
|
+
} from '@hyperserve/hyperserve-js';
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
await hyperserve.createVideo({ ... });
|
|
244
|
+
} catch (err) {
|
|
245
|
+
if (err instanceof HyperserveValidationError) {
|
|
246
|
+
// 4xx — unsupported format, file too large, invalid resolutions, etc.
|
|
247
|
+
console.error(err.message, err.statusCode);
|
|
248
|
+
} else if (err instanceof HyperserveNotFoundError) {
|
|
249
|
+
// 404
|
|
250
|
+
} else if (err instanceof HyperserveTimeoutError) {
|
|
251
|
+
// request exceeded timeoutMs
|
|
252
|
+
} else if (err instanceof HyperserveError) {
|
|
253
|
+
// any other SDK error
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
`HyperserveUploadError` and `HyperserveTimeoutError` are also exported from `@hyperserve/hyperserve-js/browser` and `@hyperserve/hyperserve-js/react-native`.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Types
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
import type {
|
|
266
|
+
VideoResolution, // '144p' | '240p' | '360p' | '480p' | '720p' | '1080p' | '1440p' | '4k' | '8k'
|
|
267
|
+
VideoStatus, // 'pending_upload' | 'processing' | 'ready' | 'fail'
|
|
268
|
+
CreateVideoOptions,
|
|
269
|
+
CreateVideoResult,
|
|
270
|
+
CompleteUploadResult,
|
|
271
|
+
VideoResult,
|
|
272
|
+
VideoResolutionResult,
|
|
273
|
+
VerifyWebhookSignatureOptions,
|
|
274
|
+
PutVideoToStorageOptions, // for @hyperserve/hyperserve-js/browser
|
|
275
|
+
PutVideoToStorageRNOptions, // for @hyperserve/hyperserve-js/react-native
|
|
276
|
+
} from 'hyperserve-js';
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Requirements
|
|
282
|
+
|
|
283
|
+
| Environment | Minimum version |
|
|
284
|
+
|---|---|
|
|
285
|
+
| Node.js | 18+ |
|
|
286
|
+
| Modern browsers | Chrome 80+, Firefox 75+, Safari 14+ |
|
|
287
|
+
| React Native | 0.60+ |
|
|
288
|
+
| Expo | SDK 41+ |
|
|
289
|
+
| Bun / Deno | Current stable |
|
|
290
|
+
|
|
291
|
+
No polyfills required. The SDK uses native `fetch`, `Blob`, and `XMLHttpRequest` — all available in the supported environments without additional setup.
|
package/dist/browser.cjs
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/errors.ts
|
|
4
|
+
var HyperserveError = class extends Error {
|
|
5
|
+
constructor(message, statusCode) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.statusCode = statusCode;
|
|
8
|
+
this.name = "HyperserveError";
|
|
9
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var HyperserveUploadError = class extends HyperserveError {
|
|
13
|
+
constructor(message, uploadStatus) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.uploadStatus = uploadStatus;
|
|
16
|
+
this.name = "HyperserveUploadError";
|
|
17
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var HyperserveTimeoutError = class extends HyperserveError {
|
|
21
|
+
constructor(message = "Request timed out") {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = "HyperserveTimeoutError";
|
|
24
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// src/storage.ts
|
|
29
|
+
async function putToStorage(uploadUrl, contentType, body, onProgress) {
|
|
30
|
+
if (onProgress !== void 0 && typeof XMLHttpRequest !== "undefined" && !(body instanceof ReadableStream)) {
|
|
31
|
+
return putWithXhr(uploadUrl, contentType, body, onProgress);
|
|
32
|
+
}
|
|
33
|
+
return putWithFetch(uploadUrl, contentType, body);
|
|
34
|
+
}
|
|
35
|
+
function putWithFetch(uploadUrl, contentType, body) {
|
|
36
|
+
return fetch(uploadUrl, {
|
|
37
|
+
method: "PUT",
|
|
38
|
+
headers: { "Content-Type": contentType },
|
|
39
|
+
// duplex is required for ReadableStream bodies in some runtimes (Node 18)
|
|
40
|
+
...body instanceof ReadableStream ? { duplex: "half" } : {},
|
|
41
|
+
body
|
|
42
|
+
}).then((response) => {
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
throw new HyperserveUploadError(
|
|
45
|
+
`Storage PUT failed with status ${response.status}`,
|
|
46
|
+
response.status
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function putWithXhr(uploadUrl, contentType, body, onProgress) {
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
const xhr = new XMLHttpRequest();
|
|
54
|
+
xhr.open("PUT", uploadUrl);
|
|
55
|
+
xhr.setRequestHeader("Content-Type", contentType);
|
|
56
|
+
xhr.upload.addEventListener("progress", (event) => {
|
|
57
|
+
if (event.lengthComputable) {
|
|
58
|
+
onProgress(Math.round(event.loaded / event.total * 100));
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
xhr.addEventListener("load", () => {
|
|
62
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
63
|
+
onProgress(100);
|
|
64
|
+
resolve();
|
|
65
|
+
} else {
|
|
66
|
+
reject(
|
|
67
|
+
new HyperserveUploadError(`Storage PUT failed with status ${xhr.status}`, xhr.status)
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
xhr.addEventListener("timeout", () => {
|
|
72
|
+
reject(new HyperserveTimeoutError("Storage PUT timed out"));
|
|
73
|
+
});
|
|
74
|
+
xhr.addEventListener("error", () => {
|
|
75
|
+
reject(new HyperserveUploadError("Storage PUT failed due to a network error"));
|
|
76
|
+
});
|
|
77
|
+
xhr.send(body);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// src/browser.ts
|
|
82
|
+
async function putVideoToStorage(options) {
|
|
83
|
+
return putToStorage(options.uploadUrl, options.contentType, options.file, options.onProgress);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
exports.HyperserveError = HyperserveError;
|
|
87
|
+
exports.HyperserveTimeoutError = HyperserveTimeoutError;
|
|
88
|
+
exports.HyperserveUploadError = HyperserveUploadError;
|
|
89
|
+
exports.putVideoToStorage = putVideoToStorage;
|
|
90
|
+
//# sourceMappingURL=browser.cjs.map
|
|
91
|
+
//# sourceMappingURL=browser.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/storage.ts","../src/browser.ts"],"names":[],"mappings":"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","file":"browser.cjs","sourcesContent":["/**\n * Base class for all Hyperserve SDK errors.\n */\nexport class HyperserveError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly statusCode?: number,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"HyperserveError\";\n\t\t// Maintain proper prototype chain in transpiled environments\n\t\tObject.setPrototypeOf(this, new.target.prototype);\n\t}\n}\n\n/**\n * The Hyperserve API returned a 4xx response.\n * Typically indicates a validation problem: unsupported file format,\n * file too large, invalid resolutions, video not in expected state, etc.\n */\nexport class HyperserveValidationError extends HyperserveError {\n\tconstructor(\n\t\tmessage: string,\n\t\tstatusCode: number,\n\t\tpublic readonly detail?: unknown,\n\t) {\n\t\tsuper(message, statusCode);\n\t\tthis.name = \"HyperserveValidationError\";\n\t\tObject.setPrototypeOf(this, new.target.prototype);\n\t}\n}\n\n/**\n * The Hyperserve API returned a 404 response.\n */\nexport class HyperserveNotFoundError extends HyperserveError {\n\tconstructor(message = \"Resource not found\") {\n\t\tsuper(message, 404);\n\t\tthis.name = \"HyperserveNotFoundError\";\n\t\tObject.setPrototypeOf(this, new.target.prototype);\n\t}\n}\n\n/**\n * The Hyperserve API returned a 5xx response.\n */\nexport class HyperserveApiError extends HyperserveError {\n\tconstructor(message: string, statusCode: number) {\n\t\tsuper(message, statusCode);\n\t\tthis.name = \"HyperserveApiError\";\n\t\tObject.setPrototypeOf(this, new.target.prototype);\n\t}\n}\n\n/**\n * The storage PUT request failed.\n */\nexport class HyperserveUploadError extends HyperserveError {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly uploadStatus?: number,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"HyperserveUploadError\";\n\t\tObject.setPrototypeOf(this, new.target.prototype);\n\t}\n}\n\n/**\n * A request exceeded the configured timeoutMs.\n */\nexport class HyperserveTimeoutError extends HyperserveError {\n\tconstructor(message = \"Request timed out\") {\n\t\tsuper(message);\n\t\tthis.name = \"HyperserveTimeoutError\";\n\t\tObject.setPrototypeOf(this, new.target.prototype);\n\t}\n}\n","import { HyperserveTimeoutError, HyperserveUploadError } from \"./errors.js\";\n\n/**\n * PUT a file to a presigned S3 URL.\n * Used internally by uploadVideo (server) and exported as putVideoToStorage (browser).\n *\n * When onProgress is provided, uses XMLHttpRequest for upload progress events.\n * Falls back to fetch otherwise.\n */\nexport async function putToStorage(\n\tuploadUrl: string,\n\tcontentType: string,\n\tbody: Blob | ReadableStream,\n\tonProgress?: (percent: number) => void,\n): Promise<void> {\n\t// XHR is used for progress reporting but does not support ReadableStream bodies.\n\t// Fall back to fetch (no progress) when the body is a stream.\n\tif (\n\t\tonProgress !== undefined &&\n\t\ttypeof XMLHttpRequest !== \"undefined\" &&\n\t\t!(body instanceof ReadableStream)\n\t) {\n\t\treturn putWithXhr(uploadUrl, contentType, body, onProgress);\n\t}\n\treturn putWithFetch(uploadUrl, contentType, body);\n}\n\nfunction putWithFetch(\n\tuploadUrl: string,\n\tcontentType: string,\n\tbody: Blob | ReadableStream,\n): Promise<void> {\n\treturn fetch(uploadUrl, {\n\t\tmethod: \"PUT\",\n\t\theaders: { \"Content-Type\": contentType },\n\t\t// duplex is required for ReadableStream bodies in some runtimes (Node 18)\n\t\t...(body instanceof ReadableStream ? { duplex: \"half\" } : {}),\n\t\tbody: body as BodyInit,\n\t}).then((response) => {\n\t\tif (!response.ok) {\n\t\t\tthrow new HyperserveUploadError(\n\t\t\t\t`Storage PUT failed with status ${response.status}`,\n\t\t\t\tresponse.status,\n\t\t\t);\n\t\t}\n\t});\n}\n\nfunction putWithXhr(\n\tuploadUrl: string,\n\tcontentType: string,\n\tbody: Blob,\n\tonProgress: (percent: number) => void,\n): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst xhr = new XMLHttpRequest();\n\n\t\txhr.open(\"PUT\", uploadUrl);\n\t\txhr.setRequestHeader(\"Content-Type\", contentType);\n\n\t\txhr.upload.addEventListener(\"progress\", (event) => {\n\t\t\tif (event.lengthComputable) {\n\t\t\t\tonProgress(Math.round((event.loaded / event.total) * 100));\n\t\t\t}\n\t\t});\n\n\t\txhr.addEventListener(\"load\", () => {\n\t\t\tif (xhr.status >= 200 && xhr.status < 300) {\n\t\t\t\tonProgress(100);\n\t\t\t\tresolve();\n\t\t\t} else {\n\t\t\t\treject(\n\t\t\t\t\tnew HyperserveUploadError(`Storage PUT failed with status ${xhr.status}`, xhr.status),\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\n\t\txhr.addEventListener(\"timeout\", () => {\n\t\t\treject(new HyperserveTimeoutError(\"Storage PUT timed out\"));\n\t\t});\n\n\t\txhr.addEventListener(\"error\", () => {\n\t\t\treject(new HyperserveUploadError(\"Storage PUT failed due to a network error\"));\n\t\t});\n\n\t\txhr.send(body);\n\t});\n}\n","/**\n * Browser-only utilities for the Hyperserve SDK.\n *\n * Import from '@hyperserve/hyperserve-js/browser' — this entry point contains no API key\n * logic and is safe to bundle into client-side code.\n *\n * Usage:\n * import { putVideoToStorage } from '@hyperserve/hyperserve-js/browser';\n *\n * // uploadUrl and contentType come from your own backend\n * await putVideoToStorage({ uploadUrl, contentType, file, onProgress });\n */\n\nexport { HyperserveError, HyperserveTimeoutError, HyperserveUploadError } from \"./errors.js\";\nexport type { PutVideoToStorageOptions, VideoResolution, VideoStatus } from \"./types.js\";\n\nimport { putToStorage } from \"./storage.js\";\nimport type { PutVideoToStorageOptions } from \"./types.js\";\n\n/**\n * PUT a video file to the presigned storage URL obtained from your backend.\n * No API key required — this call goes directly to storage, not to the Hyperserve API.\n *\n * @example\n * const { uploadUrl, contentType } = await fetch('/api/create-upload', { ... }).then(r => r.json());\n * await putVideoToStorage({ uploadUrl, contentType, file, onProgress: (p) => console.log(p) });\n * await fetch('/api/complete-upload', { method: 'POST', body: JSON.stringify({ videoId }) });\n */\nexport async function putVideoToStorage(options: PutVideoToStorageOptions): Promise<void> {\n\treturn putToStorage(options.uploadUrl, options.contentType, options.file, options.onProgress);\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { P as PutVideoToStorageOptions } from './errors-C89laaKB.cjs';
|
|
2
|
+
export { e as HyperserveError, g as HyperserveTimeoutError, h as HyperserveUploadError, k as VideoResolution, m as VideoStatus } from './errors-C89laaKB.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Browser-only utilities for the Hyperserve SDK.
|
|
6
|
+
*
|
|
7
|
+
* Import from '@hyperserve/hyperserve-js/browser' — this entry point contains no API key
|
|
8
|
+
* logic and is safe to bundle into client-side code.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* import { putVideoToStorage } from '@hyperserve/hyperserve-js/browser';
|
|
12
|
+
*
|
|
13
|
+
* // uploadUrl and contentType come from your own backend
|
|
14
|
+
* await putVideoToStorage({ uploadUrl, contentType, file, onProgress });
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* PUT a video file to the presigned storage URL obtained from your backend.
|
|
19
|
+
* No API key required — this call goes directly to storage, not to the Hyperserve API.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* const { uploadUrl, contentType } = await fetch('/api/create-upload', { ... }).then(r => r.json());
|
|
23
|
+
* await putVideoToStorage({ uploadUrl, contentType, file, onProgress: (p) => console.log(p) });
|
|
24
|
+
* await fetch('/api/complete-upload', { method: 'POST', body: JSON.stringify({ videoId }) });
|
|
25
|
+
*/
|
|
26
|
+
declare function putVideoToStorage(options: PutVideoToStorageOptions): Promise<void>;
|
|
27
|
+
|
|
28
|
+
export { PutVideoToStorageOptions, putVideoToStorage };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { P as PutVideoToStorageOptions } from './errors-C89laaKB.js';
|
|
2
|
+
export { e as HyperserveError, g as HyperserveTimeoutError, h as HyperserveUploadError, k as VideoResolution, m as VideoStatus } from './errors-C89laaKB.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Browser-only utilities for the Hyperserve SDK.
|
|
6
|
+
*
|
|
7
|
+
* Import from '@hyperserve/hyperserve-js/browser' — this entry point contains no API key
|
|
8
|
+
* logic and is safe to bundle into client-side code.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* import { putVideoToStorage } from '@hyperserve/hyperserve-js/browser';
|
|
12
|
+
*
|
|
13
|
+
* // uploadUrl and contentType come from your own backend
|
|
14
|
+
* await putVideoToStorage({ uploadUrl, contentType, file, onProgress });
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* PUT a video file to the presigned storage URL obtained from your backend.
|
|
19
|
+
* No API key required — this call goes directly to storage, not to the Hyperserve API.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* const { uploadUrl, contentType } = await fetch('/api/create-upload', { ... }).then(r => r.json());
|
|
23
|
+
* await putVideoToStorage({ uploadUrl, contentType, file, onProgress: (p) => console.log(p) });
|
|
24
|
+
* await fetch('/api/complete-upload', { method: 'POST', body: JSON.stringify({ videoId }) });
|
|
25
|
+
*/
|
|
26
|
+
declare function putVideoToStorage(options: PutVideoToStorageOptions): Promise<void>;
|
|
27
|
+
|
|
28
|
+
export { PutVideoToStorageOptions, putVideoToStorage };
|
package/dist/browser.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var HyperserveError = class extends Error {
|
|
3
|
+
constructor(message, statusCode) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.statusCode = statusCode;
|
|
6
|
+
this.name = "HyperserveError";
|
|
7
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var HyperserveUploadError = class extends HyperserveError {
|
|
11
|
+
constructor(message, uploadStatus) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.uploadStatus = uploadStatus;
|
|
14
|
+
this.name = "HyperserveUploadError";
|
|
15
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
var HyperserveTimeoutError = class extends HyperserveError {
|
|
19
|
+
constructor(message = "Request timed out") {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = "HyperserveTimeoutError";
|
|
22
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// src/storage.ts
|
|
27
|
+
async function putToStorage(uploadUrl, contentType, body, onProgress) {
|
|
28
|
+
if (onProgress !== void 0 && typeof XMLHttpRequest !== "undefined" && !(body instanceof ReadableStream)) {
|
|
29
|
+
return putWithXhr(uploadUrl, contentType, body, onProgress);
|
|
30
|
+
}
|
|
31
|
+
return putWithFetch(uploadUrl, contentType, body);
|
|
32
|
+
}
|
|
33
|
+
function putWithFetch(uploadUrl, contentType, body) {
|
|
34
|
+
return fetch(uploadUrl, {
|
|
35
|
+
method: "PUT",
|
|
36
|
+
headers: { "Content-Type": contentType },
|
|
37
|
+
// duplex is required for ReadableStream bodies in some runtimes (Node 18)
|
|
38
|
+
...body instanceof ReadableStream ? { duplex: "half" } : {},
|
|
39
|
+
body
|
|
40
|
+
}).then((response) => {
|
|
41
|
+
if (!response.ok) {
|
|
42
|
+
throw new HyperserveUploadError(
|
|
43
|
+
`Storage PUT failed with status ${response.status}`,
|
|
44
|
+
response.status
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function putWithXhr(uploadUrl, contentType, body, onProgress) {
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
const xhr = new XMLHttpRequest();
|
|
52
|
+
xhr.open("PUT", uploadUrl);
|
|
53
|
+
xhr.setRequestHeader("Content-Type", contentType);
|
|
54
|
+
xhr.upload.addEventListener("progress", (event) => {
|
|
55
|
+
if (event.lengthComputable) {
|
|
56
|
+
onProgress(Math.round(event.loaded / event.total * 100));
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
xhr.addEventListener("load", () => {
|
|
60
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
61
|
+
onProgress(100);
|
|
62
|
+
resolve();
|
|
63
|
+
} else {
|
|
64
|
+
reject(
|
|
65
|
+
new HyperserveUploadError(`Storage PUT failed with status ${xhr.status}`, xhr.status)
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
xhr.addEventListener("timeout", () => {
|
|
70
|
+
reject(new HyperserveTimeoutError("Storage PUT timed out"));
|
|
71
|
+
});
|
|
72
|
+
xhr.addEventListener("error", () => {
|
|
73
|
+
reject(new HyperserveUploadError("Storage PUT failed due to a network error"));
|
|
74
|
+
});
|
|
75
|
+
xhr.send(body);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/browser.ts
|
|
80
|
+
async function putVideoToStorage(options) {
|
|
81
|
+
return putToStorage(options.uploadUrl, options.contentType, options.file, options.onProgress);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export { HyperserveError, HyperserveTimeoutError, HyperserveUploadError, putVideoToStorage };
|
|
85
|
+
//# sourceMappingURL=browser.js.map
|
|
86
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/storage.ts","../src/browser.ts"],"names":[],"mappings":"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","file":"browser.js","sourcesContent":["/**\n * Base class for all Hyperserve SDK errors.\n */\nexport class HyperserveError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly statusCode?: number,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"HyperserveError\";\n\t\t// Maintain proper prototype chain in transpiled environments\n\t\tObject.setPrototypeOf(this, new.target.prototype);\n\t}\n}\n\n/**\n * The Hyperserve API returned a 4xx response.\n * Typically indicates a validation problem: unsupported file format,\n * file too large, invalid resolutions, video not in expected state, etc.\n */\nexport class HyperserveValidationError extends HyperserveError {\n\tconstructor(\n\t\tmessage: string,\n\t\tstatusCode: number,\n\t\tpublic readonly detail?: unknown,\n\t) {\n\t\tsuper(message, statusCode);\n\t\tthis.name = \"HyperserveValidationError\";\n\t\tObject.setPrototypeOf(this, new.target.prototype);\n\t}\n}\n\n/**\n * The Hyperserve API returned a 404 response.\n */\nexport class HyperserveNotFoundError extends HyperserveError {\n\tconstructor(message = \"Resource not found\") {\n\t\tsuper(message, 404);\n\t\tthis.name = \"HyperserveNotFoundError\";\n\t\tObject.setPrototypeOf(this, new.target.prototype);\n\t}\n}\n\n/**\n * The Hyperserve API returned a 5xx response.\n */\nexport class HyperserveApiError extends HyperserveError {\n\tconstructor(message: string, statusCode: number) {\n\t\tsuper(message, statusCode);\n\t\tthis.name = \"HyperserveApiError\";\n\t\tObject.setPrototypeOf(this, new.target.prototype);\n\t}\n}\n\n/**\n * The storage PUT request failed.\n */\nexport class HyperserveUploadError extends HyperserveError {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly uploadStatus?: number,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"HyperserveUploadError\";\n\t\tObject.setPrototypeOf(this, new.target.prototype);\n\t}\n}\n\n/**\n * A request exceeded the configured timeoutMs.\n */\nexport class HyperserveTimeoutError extends HyperserveError {\n\tconstructor(message = \"Request timed out\") {\n\t\tsuper(message);\n\t\tthis.name = \"HyperserveTimeoutError\";\n\t\tObject.setPrototypeOf(this, new.target.prototype);\n\t}\n}\n","import { HyperserveTimeoutError, HyperserveUploadError } from \"./errors.js\";\n\n/**\n * PUT a file to a presigned S3 URL.\n * Used internally by uploadVideo (server) and exported as putVideoToStorage (browser).\n *\n * When onProgress is provided, uses XMLHttpRequest for upload progress events.\n * Falls back to fetch otherwise.\n */\nexport async function putToStorage(\n\tuploadUrl: string,\n\tcontentType: string,\n\tbody: Blob | ReadableStream,\n\tonProgress?: (percent: number) => void,\n): Promise<void> {\n\t// XHR is used for progress reporting but does not support ReadableStream bodies.\n\t// Fall back to fetch (no progress) when the body is a stream.\n\tif (\n\t\tonProgress !== undefined &&\n\t\ttypeof XMLHttpRequest !== \"undefined\" &&\n\t\t!(body instanceof ReadableStream)\n\t) {\n\t\treturn putWithXhr(uploadUrl, contentType, body, onProgress);\n\t}\n\treturn putWithFetch(uploadUrl, contentType, body);\n}\n\nfunction putWithFetch(\n\tuploadUrl: string,\n\tcontentType: string,\n\tbody: Blob | ReadableStream,\n): Promise<void> {\n\treturn fetch(uploadUrl, {\n\t\tmethod: \"PUT\",\n\t\theaders: { \"Content-Type\": contentType },\n\t\t// duplex is required for ReadableStream bodies in some runtimes (Node 18)\n\t\t...(body instanceof ReadableStream ? { duplex: \"half\" } : {}),\n\t\tbody: body as BodyInit,\n\t}).then((response) => {\n\t\tif (!response.ok) {\n\t\t\tthrow new HyperserveUploadError(\n\t\t\t\t`Storage PUT failed with status ${response.status}`,\n\t\t\t\tresponse.status,\n\t\t\t);\n\t\t}\n\t});\n}\n\nfunction putWithXhr(\n\tuploadUrl: string,\n\tcontentType: string,\n\tbody: Blob,\n\tonProgress: (percent: number) => void,\n): Promise<void> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst xhr = new XMLHttpRequest();\n\n\t\txhr.open(\"PUT\", uploadUrl);\n\t\txhr.setRequestHeader(\"Content-Type\", contentType);\n\n\t\txhr.upload.addEventListener(\"progress\", (event) => {\n\t\t\tif (event.lengthComputable) {\n\t\t\t\tonProgress(Math.round((event.loaded / event.total) * 100));\n\t\t\t}\n\t\t});\n\n\t\txhr.addEventListener(\"load\", () => {\n\t\t\tif (xhr.status >= 200 && xhr.status < 300) {\n\t\t\t\tonProgress(100);\n\t\t\t\tresolve();\n\t\t\t} else {\n\t\t\t\treject(\n\t\t\t\t\tnew HyperserveUploadError(`Storage PUT failed with status ${xhr.status}`, xhr.status),\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\n\t\txhr.addEventListener(\"timeout\", () => {\n\t\t\treject(new HyperserveTimeoutError(\"Storage PUT timed out\"));\n\t\t});\n\n\t\txhr.addEventListener(\"error\", () => {\n\t\t\treject(new HyperserveUploadError(\"Storage PUT failed due to a network error\"));\n\t\t});\n\n\t\txhr.send(body);\n\t});\n}\n","/**\n * Browser-only utilities for the Hyperserve SDK.\n *\n * Import from '@hyperserve/hyperserve-js/browser' — this entry point contains no API key\n * logic and is safe to bundle into client-side code.\n *\n * Usage:\n * import { putVideoToStorage } from '@hyperserve/hyperserve-js/browser';\n *\n * // uploadUrl and contentType come from your own backend\n * await putVideoToStorage({ uploadUrl, contentType, file, onProgress });\n */\n\nexport { HyperserveError, HyperserveTimeoutError, HyperserveUploadError } from \"./errors.js\";\nexport type { PutVideoToStorageOptions, VideoResolution, VideoStatus } from \"./types.js\";\n\nimport { putToStorage } from \"./storage.js\";\nimport type { PutVideoToStorageOptions } from \"./types.js\";\n\n/**\n * PUT a video file to the presigned storage URL obtained from your backend.\n * No API key required — this call goes directly to storage, not to the Hyperserve API.\n *\n * @example\n * const { uploadUrl, contentType } = await fetch('/api/create-upload', { ... }).then(r => r.json());\n * await putVideoToStorage({ uploadUrl, contentType, file, onProgress: (p) => console.log(p) });\n * await fetch('/api/complete-upload', { method: 'POST', body: JSON.stringify({ videoId }) });\n */\nexport async function putVideoToStorage(options: PutVideoToStorageOptions): Promise<void> {\n\treturn putToStorage(options.uploadUrl, options.contentType, options.file, options.onProgress);\n}\n"]}
|