@hyperserve/hyperserve-js 0.1.0 → 0.2.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 +22 -10
- package/dist/browser.cjs +15 -6
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +2 -2
- package/dist/browser.d.ts +2 -2
- package/dist/browser.js +15 -6
- package/dist/browser.js.map +1 -1
- package/dist/{errors-C89laaKB.d.cts → errors-DJGPqrI5.d.cts} +9 -3
- package/dist/{errors-C89laaKB.d.ts → errors-DJGPqrI5.d.ts} +9 -3
- package/dist/index.cjs +49 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +49 -10
- package/dist/index.js.map +1 -1
- package/dist/react-native.cjs +15 -6
- package/dist/react-native.cjs.map +1 -1
- package/dist/react-native.d.cts +3 -3
- package/dist/react-native.d.ts +3 -3
- package/dist/react-native.js +15 -6
- package/dist/react-native.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @hyperserve/hyperserve-js
|
|
2
2
|
|
|
3
|
-
TypeScript SDK for the [Hyperserve](https://hyperserve.io) video infrastructure API. Works in Node.js, browsers, and React Native.
|
|
3
|
+
TypeScript SDK for the [Hyperserve](https://hyperserve.io?utm_source=github&utm_medium=readme&utm_campaign=hyperserve-js) video infrastructure API. Works in Node.js, browsers, and React Native.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -32,7 +32,6 @@ const hyperserve = new HyperserveClient({ apiKey: process.env.HYPERSERVE_API_KEY
|
|
|
32
32
|
// In your API route or server action
|
|
33
33
|
const upload = await hyperserve.createVideo({
|
|
34
34
|
filename: 'promo.mp4',
|
|
35
|
-
fileSizeBytes: 10_485_760,
|
|
36
35
|
resolutions: ['480p', '1080p'],
|
|
37
36
|
isPublic: true,
|
|
38
37
|
});
|
|
@@ -48,7 +47,7 @@ import { putVideoToStorage } from '@hyperserve/hyperserve-js/browser';
|
|
|
48
47
|
|
|
49
48
|
const { videoId, uploadUrl, contentType } = await fetch('/api/create-upload', {
|
|
50
49
|
method: 'POST',
|
|
51
|
-
body: JSON.stringify({ filename: file.name
|
|
50
|
+
body: JSON.stringify({ filename: file.name }),
|
|
52
51
|
}).then(r => r.json());
|
|
53
52
|
|
|
54
53
|
await putVideoToStorage({
|
|
@@ -70,7 +69,7 @@ import { putVideoToStorage } from '@hyperserve/hyperserve-js/react-native';
|
|
|
70
69
|
// asset from expo-image-picker, react-native-image-picker, etc.
|
|
71
70
|
const { videoId, uploadUrl, contentType } = await fetch('https://your-api.com/create-upload', {
|
|
72
71
|
method: 'POST',
|
|
73
|
-
body: JSON.stringify({ filename: asset.fileName
|
|
72
|
+
body: JSON.stringify({ filename: asset.fileName }),
|
|
74
73
|
}).then(r => r.json());
|
|
75
74
|
|
|
76
75
|
await putVideoToStorage({
|
|
@@ -116,7 +115,6 @@ Creates a video record and returns a presigned upload URL.
|
|
|
116
115
|
```typescript
|
|
117
116
|
const upload = await hyperserve.createVideo({
|
|
118
117
|
filename: 'clip.mp4', // required — extension determines content type
|
|
119
|
-
fileSizeBytes: 5_242_880, // required
|
|
120
118
|
resolutions: ['720p', '1080p'],// required — at least one
|
|
121
119
|
isPublic: true, // required
|
|
122
120
|
thumbnailTimestampsSeconds: [5, 30, 60], // optional
|
|
@@ -187,15 +185,29 @@ await hyperserve.deleteResolution(resolutionId);
|
|
|
187
185
|
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
186
|
|
|
189
187
|
```typescript
|
|
190
|
-
import { readFileSync
|
|
188
|
+
import { readFileSync } from 'fs';
|
|
189
|
+
|
|
190
|
+
const result = await hyperserve.uploadVideo({
|
|
191
|
+
file: readFileSync('./promo.mp4'), // Blob | Buffer | ReadableStream
|
|
192
|
+
filename: 'promo.mp4',
|
|
193
|
+
resolutions: ['1080p'],
|
|
194
|
+
isPublic: false,
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
For a `ReadableStream`, pass `fileSizeBytes` — the size cannot be inferred, and it is
|
|
199
|
+
needed to set `Content-Length` on the storage PUT:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
import { createReadStream, statSync } from 'fs';
|
|
203
|
+
import { Readable } from 'stream';
|
|
191
204
|
|
|
192
|
-
const buffer = readFileSync('./promo.mp4');
|
|
193
205
|
const { size } = statSync('./promo.mp4');
|
|
194
206
|
|
|
195
|
-
|
|
196
|
-
file:
|
|
207
|
+
await hyperserve.uploadVideo({
|
|
208
|
+
file: Readable.toWeb(createReadStream('./promo.mp4')) as ReadableStream,
|
|
197
209
|
filename: 'promo.mp4',
|
|
198
|
-
fileSizeBytes: size, // required for ReadableStream
|
|
210
|
+
fileSizeBytes: size, // required for ReadableStream
|
|
199
211
|
resolutions: ['1080p'],
|
|
200
212
|
isPublic: false,
|
|
201
213
|
});
|
package/dist/browser.cjs
CHANGED
|
@@ -26,18 +26,25 @@ var HyperserveTimeoutError = class extends HyperserveError {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
// src/storage.ts
|
|
29
|
-
async function putToStorage(uploadUrl, contentType, body,
|
|
29
|
+
async function putToStorage(uploadUrl, contentType, body, options = {}) {
|
|
30
|
+
const { contentLength, onProgress } = options;
|
|
30
31
|
if (onProgress !== void 0 && typeof XMLHttpRequest !== "undefined" && !(body instanceof ReadableStream)) {
|
|
31
32
|
return putWithXhr(uploadUrl, contentType, body, onProgress);
|
|
32
33
|
}
|
|
33
|
-
return putWithFetch(uploadUrl, contentType, body);
|
|
34
|
+
return putWithFetch(uploadUrl, contentType, body, contentLength);
|
|
34
35
|
}
|
|
35
|
-
function putWithFetch(uploadUrl, contentType, body) {
|
|
36
|
+
function putWithFetch(uploadUrl, contentType, body, contentLength) {
|
|
37
|
+
const isStream = body instanceof ReadableStream;
|
|
36
38
|
return fetch(uploadUrl, {
|
|
37
39
|
method: "PUT",
|
|
38
|
-
headers: {
|
|
40
|
+
headers: {
|
|
41
|
+
"Content-Type": contentType,
|
|
42
|
+
// A stream body would otherwise go out chunked, which S3-compatible
|
|
43
|
+
// storage rejects with 411. Blob bodies get Content-Length from fetch.
|
|
44
|
+
...isStream && contentLength !== void 0 ? { "Content-Length": String(contentLength) } : {}
|
|
45
|
+
},
|
|
39
46
|
// duplex is required for ReadableStream bodies in some runtimes (Node 18)
|
|
40
|
-
...
|
|
47
|
+
...isStream ? { duplex: "half" } : {},
|
|
41
48
|
body
|
|
42
49
|
}).then((response) => {
|
|
43
50
|
if (!response.ok) {
|
|
@@ -80,7 +87,9 @@ function putWithXhr(uploadUrl, contentType, body, onProgress) {
|
|
|
80
87
|
|
|
81
88
|
// src/browser.ts
|
|
82
89
|
async function putVideoToStorage(options) {
|
|
83
|
-
return putToStorage(options.uploadUrl, options.contentType, options.file,
|
|
90
|
+
return putToStorage(options.uploadUrl, options.contentType, options.file, {
|
|
91
|
+
...options.onProgress !== void 0 && { onProgress: options.onProgress }
|
|
92
|
+
});
|
|
84
93
|
}
|
|
85
94
|
|
|
86
95
|
exports.HyperserveError = HyperserveError;
|
package/dist/browser.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/storage.ts","../src/browser.ts"],"names":[],"mappings":";;;AAGO,IAAM,eAAA,GAAN,cAA8B,KAAA,CAAM;AAAA,EAC1C,WAAA,CACC,SACgB,UAAA,EACf;AACD,IAAA,KAAA,CAAM,OAAO,CAAA;AAFG,IAAA,IAAA,CAAA,UAAA,GAAA,UAAA;AAGhB,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AAEZ,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,GAAA,CAAA,MAAA,CAAW,SAAS,CAAA;AAAA,EACjD;AACD;AA4CO,IAAM,qBAAA,GAAN,cAAoC,eAAA,CAAgB;AAAA,EAC1D,WAAA,CACC,SACgB,YAAA,EACf;AACD,IAAA,KAAA,CAAM,OAAO,CAAA;AAFG,IAAA,IAAA,CAAA,YAAA,GAAA,YAAA;AAGhB,IAAA,IAAA,CAAK,IAAA,GAAO,uBAAA;AACZ,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,GAAA,CAAA,MAAA,CAAW,SAAS,CAAA;AAAA,EACjD;AACD;AAKO,IAAM,sBAAA,GAAN,cAAqC,eAAA,CAAgB;AAAA,EAC3D,WAAA,CAAY,UAAU,mBAAA,EAAqB;AAC1C,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,wBAAA;AACZ,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,GAAA,CAAA,MAAA,CAAW,SAAS,CAAA;AAAA,EACjD;AACD;;;ACpEA,eAAsB,YAAA,CACrB,SAAA,EACA,WAAA,EACA,IAAA,EACA,UAAA,EACgB;AAGhB,EAAA,IACC,eAAe,MAAA,IACf,OAAO,mBAAmB,WAAA,IAC1B,EAAE,gBAAgB,cAAA,CAAA,EACjB;AACD,IAAA,OAAO,UAAA,CAAW,SAAA,EAAW,WAAA,EAAa,IAAA,EAAM,UAAU,CAAA;AAAA,EAC3D;AACA,EAAA,OAAO,YAAA,CAAa,SAAA,EAAW,WAAA,EAAa,IAAI,CAAA;AACjD;AAEA,SAAS,YAAA,CACR,SAAA,EACA,WAAA,EACA,IAAA,EACgB;AAChB,EAAA,OAAO,MAAM,SAAA,EAAW;AAAA,IACvB,MAAA,EAAQ,KAAA;AAAA,IACR,OAAA,EAAS,EAAE,cAAA,EAAgB,WAAA,EAAY;AAAA;AAAA,IAEvC,GAAI,IAAA,YAAgB,cAAA,GAAiB,EAAE,MAAA,EAAQ,MAAA,KAAW,EAAC;AAAA,IAC3D;AAAA,GACA,CAAA,CAAE,IAAA,CAAK,CAAC,QAAA,KAAa;AACrB,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AACjB,MAAA,MAAM,IAAI,qBAAA;AAAA,QACT,CAAA,+BAAA,EAAkC,SAAS,MAAM,CAAA,CAAA;AAAA,QACjD,QAAA,CAAS;AAAA,OACV;AAAA,IACD;AAAA,EACD,CAAC,CAAA;AACF;AAEA,SAAS,UAAA,CACR,SAAA,EACA,WAAA,EACA,IAAA,EACA,UAAA,EACgB;AAChB,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACvC,IAAA,MAAM,GAAA,GAAM,IAAI,cAAA,EAAe;AAE/B,IAAA,GAAA,CAAI,IAAA,CAAK,OAAO,SAAS,CAAA;AACzB,IAAA,GAAA,CAAI,gBAAA,CAAiB,gBAAgB,WAAW,CAAA;AAEhD,IAAA,GAAA,CAAI,MAAA,CAAO,gBAAA,CAAiB,UAAA,EAAY,CAAC,KAAA,KAAU;AAClD,MAAA,IAAI,MAAM,gBAAA,EAAkB;AAC3B,QAAA,UAAA,CAAW,KAAK,KAAA,CAAO,KAAA,CAAM,SAAS,KAAA,CAAM,KAAA,GAAS,GAAG,CAAC,CAAA;AAAA,MAC1D;AAAA,IACD,CAAC,CAAA;AAED,IAAA,GAAA,CAAI,gBAAA,CAAiB,QAAQ,MAAM;AAClC,MAAA,IAAI,GAAA,CAAI,MAAA,IAAU,GAAA,IAAO,GAAA,CAAI,SAAS,GAAA,EAAK;AAC1C,QAAA,UAAA,CAAW,GAAG,CAAA;AACd,QAAA,OAAA,EAAQ;AAAA,MACT,CAAA,MAAO;AACN,QAAA,MAAA;AAAA,UACC,IAAI,qBAAA,CAAsB,CAAA,+BAAA,EAAkC,IAAI,MAAM,CAAA,CAAA,EAAI,IAAI,MAAM;AAAA,SACrF;AAAA,MACD;AAAA,IACD,CAAC,CAAA;AAED,IAAA,GAAA,CAAI,gBAAA,CAAiB,WAAW,MAAM;AACrC,MAAA,MAAA,CAAO,IAAI,sBAAA,CAAuB,uBAAuB,CAAC,CAAA;AAAA,IAC3D,CAAC,CAAA;AAED,IAAA,GAAA,CAAI,gBAAA,CAAiB,SAAS,MAAM;AACnC,MAAA,MAAA,CAAO,IAAI,qBAAA,CAAsB,2CAA2C,CAAC,CAAA;AAAA,IAC9E,CAAC,CAAA;AAED,IAAA,GAAA,CAAI,KAAK,IAAI,CAAA;AAAA,EACd,CAAC,CAAA;AACF;;;AC3DA,eAAsB,kBAAkB,OAAA,EAAkD;AACzF,EAAA,OAAO,YAAA,CAAa,QAAQ,SAAA,EAAW,OAAA,CAAQ,aAAa,OAAA,CAAQ,IAAA,EAAM,QAAQ,UAAU,CAAA;AAC7F","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"]}
|
|
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\nexport interface PutToStorageOptions {\n\t/**\n\t * Byte length of the body. Only used for ReadableStream bodies, which are sent\n\t * with chunked transfer encoding unless Content-Length is set explicitly — and\n\t * S3-compatible storage rejects a chunked PUT with 411 MissingContentLength.\n\t * Blob bodies carry their own length, so this is ignored for them.\n\t */\n\tcontentLength?: number;\n\tonProgress?: (percent: number) => void;\n}\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\toptions: PutToStorageOptions = {},\n): Promise<void> {\n\tconst { contentLength, onProgress } = options;\n\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, contentLength);\n}\n\nfunction putWithFetch(\n\tuploadUrl: string,\n\tcontentType: string,\n\tbody: Blob | ReadableStream,\n\tcontentLength?: number,\n): Promise<void> {\n\tconst isStream = body instanceof ReadableStream;\n\n\treturn fetch(uploadUrl, {\n\t\tmethod: \"PUT\",\n\t\theaders: {\n\t\t\t\"Content-Type\": contentType,\n\t\t\t// A stream body would otherwise go out chunked, which S3-compatible\n\t\t\t// storage rejects with 411. Blob bodies get Content-Length from fetch.\n\t\t\t...(isStream && contentLength !== undefined\n\t\t\t\t? { \"Content-Length\": String(contentLength) }\n\t\t\t\t: {}),\n\t\t},\n\t\t// duplex is required for ReadableStream bodies in some runtimes (Node 18)\n\t\t...(isStream ? { 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, {\n\t\t...(options.onProgress !== undefined && { onProgress: options.onProgress }),\n\t});\n}\n"]}
|
package/dist/browser.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as PutVideoToStorageOptions } from './errors-
|
|
2
|
-
export { e as HyperserveError, g as HyperserveTimeoutError, h as HyperserveUploadError, k as VideoResolution, m as VideoStatus } from './errors-
|
|
1
|
+
import { P as PutVideoToStorageOptions } from './errors-DJGPqrI5.cjs';
|
|
2
|
+
export { e as HyperserveError, g as HyperserveTimeoutError, h as HyperserveUploadError, k as VideoResolution, m as VideoStatus } from './errors-DJGPqrI5.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Browser-only utilities for the Hyperserve SDK.
|
package/dist/browser.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as PutVideoToStorageOptions } from './errors-
|
|
2
|
-
export { e as HyperserveError, g as HyperserveTimeoutError, h as HyperserveUploadError, k as VideoResolution, m as VideoStatus } from './errors-
|
|
1
|
+
import { P as PutVideoToStorageOptions } from './errors-DJGPqrI5.js';
|
|
2
|
+
export { e as HyperserveError, g as HyperserveTimeoutError, h as HyperserveUploadError, k as VideoResolution, m as VideoStatus } from './errors-DJGPqrI5.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Browser-only utilities for the Hyperserve SDK.
|
package/dist/browser.js
CHANGED
|
@@ -24,18 +24,25 @@ var HyperserveTimeoutError = class extends HyperserveError {
|
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
// src/storage.ts
|
|
27
|
-
async function putToStorage(uploadUrl, contentType, body,
|
|
27
|
+
async function putToStorage(uploadUrl, contentType, body, options = {}) {
|
|
28
|
+
const { contentLength, onProgress } = options;
|
|
28
29
|
if (onProgress !== void 0 && typeof XMLHttpRequest !== "undefined" && !(body instanceof ReadableStream)) {
|
|
29
30
|
return putWithXhr(uploadUrl, contentType, body, onProgress);
|
|
30
31
|
}
|
|
31
|
-
return putWithFetch(uploadUrl, contentType, body);
|
|
32
|
+
return putWithFetch(uploadUrl, contentType, body, contentLength);
|
|
32
33
|
}
|
|
33
|
-
function putWithFetch(uploadUrl, contentType, body) {
|
|
34
|
+
function putWithFetch(uploadUrl, contentType, body, contentLength) {
|
|
35
|
+
const isStream = body instanceof ReadableStream;
|
|
34
36
|
return fetch(uploadUrl, {
|
|
35
37
|
method: "PUT",
|
|
36
|
-
headers: {
|
|
38
|
+
headers: {
|
|
39
|
+
"Content-Type": contentType,
|
|
40
|
+
// A stream body would otherwise go out chunked, which S3-compatible
|
|
41
|
+
// storage rejects with 411. Blob bodies get Content-Length from fetch.
|
|
42
|
+
...isStream && contentLength !== void 0 ? { "Content-Length": String(contentLength) } : {}
|
|
43
|
+
},
|
|
37
44
|
// duplex is required for ReadableStream bodies in some runtimes (Node 18)
|
|
38
|
-
...
|
|
45
|
+
...isStream ? { duplex: "half" } : {},
|
|
39
46
|
body
|
|
40
47
|
}).then((response) => {
|
|
41
48
|
if (!response.ok) {
|
|
@@ -78,7 +85,9 @@ function putWithXhr(uploadUrl, contentType, body, onProgress) {
|
|
|
78
85
|
|
|
79
86
|
// src/browser.ts
|
|
80
87
|
async function putVideoToStorage(options) {
|
|
81
|
-
return putToStorage(options.uploadUrl, options.contentType, options.file,
|
|
88
|
+
return putToStorage(options.uploadUrl, options.contentType, options.file, {
|
|
89
|
+
...options.onProgress !== void 0 && { onProgress: options.onProgress }
|
|
90
|
+
});
|
|
82
91
|
}
|
|
83
92
|
|
|
84
93
|
export { HyperserveError, HyperserveTimeoutError, HyperserveUploadError, putVideoToStorage };
|
package/dist/browser.js.map
CHANGED
|
@@ -1 +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"]}
|
|
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\nexport interface PutToStorageOptions {\n\t/**\n\t * Byte length of the body. Only used for ReadableStream bodies, which are sent\n\t * with chunked transfer encoding unless Content-Length is set explicitly — and\n\t * S3-compatible storage rejects a chunked PUT with 411 MissingContentLength.\n\t * Blob bodies carry their own length, so this is ignored for them.\n\t */\n\tcontentLength?: number;\n\tonProgress?: (percent: number) => void;\n}\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\toptions: PutToStorageOptions = {},\n): Promise<void> {\n\tconst { contentLength, onProgress } = options;\n\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, contentLength);\n}\n\nfunction putWithFetch(\n\tuploadUrl: string,\n\tcontentType: string,\n\tbody: Blob | ReadableStream,\n\tcontentLength?: number,\n): Promise<void> {\n\tconst isStream = body instanceof ReadableStream;\n\n\treturn fetch(uploadUrl, {\n\t\tmethod: \"PUT\",\n\t\theaders: {\n\t\t\t\"Content-Type\": contentType,\n\t\t\t// A stream body would otherwise go out chunked, which S3-compatible\n\t\t\t// storage rejects with 411. Blob bodies get Content-Length from fetch.\n\t\t\t...(isStream && contentLength !== undefined\n\t\t\t\t? { \"Content-Length\": String(contentLength) }\n\t\t\t\t: {}),\n\t\t},\n\t\t// duplex is required for ReadableStream bodies in some runtimes (Node 18)\n\t\t...(isStream ? { 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, {\n\t\t...(options.onProgress !== undefined && { onProgress: options.onProgress }),\n\t});\n}\n"]}
|
|
@@ -46,8 +46,6 @@ interface VerifyWebhookSignatureOptions {
|
|
|
46
46
|
interface CreateVideoOptions {
|
|
47
47
|
/** Original filename including extension (e.g. "promo.mp4"). Used server-side to derive content type. */
|
|
48
48
|
filename: string;
|
|
49
|
-
/** File size in bytes. */
|
|
50
|
-
fileSizeBytes: number;
|
|
51
49
|
/** At least one resolution is required. */
|
|
52
50
|
resolutions: [VideoResolution, ...VideoResolution[]];
|
|
53
51
|
/** Controls whether playback URLs are public or time-limited signed URLs. */
|
|
@@ -81,7 +79,13 @@ interface UploadVideoOptions {
|
|
|
81
79
|
file: Blob | Buffer | ReadableStream;
|
|
82
80
|
/** Filename including extension (e.g. "clip.mp4"). */
|
|
83
81
|
filename: string;
|
|
84
|
-
/**
|
|
82
|
+
/**
|
|
83
|
+
* Byte length of the file. Required when file is a ReadableStream, where it sets
|
|
84
|
+
* Content-Length on the storage PUT — a stream body would otherwise be sent chunked,
|
|
85
|
+
* which storage rejects with 411. Inferred and then ignored for Blob/Buffer, which
|
|
86
|
+
* carry their own length; supplying it for those has no effect on the request.
|
|
87
|
+
* Never sent to the Hyperserve API.
|
|
88
|
+
*/
|
|
85
89
|
fileSizeBytes?: number;
|
|
86
90
|
resolutions: [VideoResolution, ...VideoResolution[]];
|
|
87
91
|
isPublic: boolean;
|
|
@@ -104,6 +108,8 @@ interface VideoResult {
|
|
|
104
108
|
id: string;
|
|
105
109
|
status: VideoStatus;
|
|
106
110
|
isPublic: boolean;
|
|
111
|
+
/** Arbitrary key/value bag stored against the video at creation. Keys are returned verbatim. Null when none was set. */
|
|
112
|
+
customMetadata?: Record<string, unknown> | null;
|
|
107
113
|
resolutions: Partial<Record<VideoResolution, VideoResolutionResult>>;
|
|
108
114
|
}
|
|
109
115
|
interface PutVideoToStorageOptions {
|
|
@@ -46,8 +46,6 @@ interface VerifyWebhookSignatureOptions {
|
|
|
46
46
|
interface CreateVideoOptions {
|
|
47
47
|
/** Original filename including extension (e.g. "promo.mp4"). Used server-side to derive content type. */
|
|
48
48
|
filename: string;
|
|
49
|
-
/** File size in bytes. */
|
|
50
|
-
fileSizeBytes: number;
|
|
51
49
|
/** At least one resolution is required. */
|
|
52
50
|
resolutions: [VideoResolution, ...VideoResolution[]];
|
|
53
51
|
/** Controls whether playback URLs are public or time-limited signed URLs. */
|
|
@@ -81,7 +79,13 @@ interface UploadVideoOptions {
|
|
|
81
79
|
file: Blob | Buffer | ReadableStream;
|
|
82
80
|
/** Filename including extension (e.g. "clip.mp4"). */
|
|
83
81
|
filename: string;
|
|
84
|
-
/**
|
|
82
|
+
/**
|
|
83
|
+
* Byte length of the file. Required when file is a ReadableStream, where it sets
|
|
84
|
+
* Content-Length on the storage PUT — a stream body would otherwise be sent chunked,
|
|
85
|
+
* which storage rejects with 411. Inferred and then ignored for Blob/Buffer, which
|
|
86
|
+
* carry their own length; supplying it for those has no effect on the request.
|
|
87
|
+
* Never sent to the Hyperserve API.
|
|
88
|
+
*/
|
|
85
89
|
fileSizeBytes?: number;
|
|
86
90
|
resolutions: [VideoResolution, ...VideoResolution[]];
|
|
87
91
|
isPublic: boolean;
|
|
@@ -104,6 +108,8 @@ interface VideoResult {
|
|
|
104
108
|
id: string;
|
|
105
109
|
status: VideoStatus;
|
|
106
110
|
isPublic: boolean;
|
|
111
|
+
/** Arbitrary key/value bag stored against the video at creation. Keys are returned verbatim. Null when none was set. */
|
|
112
|
+
customMetadata?: Record<string, unknown> | null;
|
|
107
113
|
resolutions: Partial<Record<VideoResolution, VideoResolutionResult>>;
|
|
108
114
|
}
|
|
109
115
|
interface PutVideoToStorageOptions {
|
package/dist/index.cjs
CHANGED
|
@@ -155,15 +155,25 @@ function deriveTypeHint(filename) {
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
// src/storage.ts
|
|
158
|
-
async function putToStorage(uploadUrl, contentType, body,
|
|
159
|
-
|
|
158
|
+
async function putToStorage(uploadUrl, contentType, body, options = {}) {
|
|
159
|
+
const { contentLength, onProgress } = options;
|
|
160
|
+
if (onProgress !== void 0 && typeof XMLHttpRequest !== "undefined" && !(body instanceof ReadableStream)) {
|
|
161
|
+
return putWithXhr(uploadUrl, contentType, body, onProgress);
|
|
162
|
+
}
|
|
163
|
+
return putWithFetch(uploadUrl, contentType, body, contentLength);
|
|
160
164
|
}
|
|
161
|
-
function putWithFetch(uploadUrl, contentType, body) {
|
|
165
|
+
function putWithFetch(uploadUrl, contentType, body, contentLength) {
|
|
166
|
+
const isStream = body instanceof ReadableStream;
|
|
162
167
|
return fetch(uploadUrl, {
|
|
163
168
|
method: "PUT",
|
|
164
|
-
headers: {
|
|
169
|
+
headers: {
|
|
170
|
+
"Content-Type": contentType,
|
|
171
|
+
// A stream body would otherwise go out chunked, which S3-compatible
|
|
172
|
+
// storage rejects with 411. Blob bodies get Content-Length from fetch.
|
|
173
|
+
...isStream && contentLength !== void 0 ? { "Content-Length": String(contentLength) } : {}
|
|
174
|
+
},
|
|
165
175
|
// duplex is required for ReadableStream bodies in some runtimes (Node 18)
|
|
166
|
-
...
|
|
176
|
+
...isStream ? { duplex: "half" } : {},
|
|
167
177
|
body
|
|
168
178
|
}).then((response) => {
|
|
169
179
|
if (!response.ok) {
|
|
@@ -174,6 +184,35 @@ function putWithFetch(uploadUrl, contentType, body) {
|
|
|
174
184
|
}
|
|
175
185
|
});
|
|
176
186
|
}
|
|
187
|
+
function putWithXhr(uploadUrl, contentType, body, onProgress) {
|
|
188
|
+
return new Promise((resolve, reject) => {
|
|
189
|
+
const xhr = new XMLHttpRequest();
|
|
190
|
+
xhr.open("PUT", uploadUrl);
|
|
191
|
+
xhr.setRequestHeader("Content-Type", contentType);
|
|
192
|
+
xhr.upload.addEventListener("progress", (event) => {
|
|
193
|
+
if (event.lengthComputable) {
|
|
194
|
+
onProgress(Math.round(event.loaded / event.total * 100));
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
xhr.addEventListener("load", () => {
|
|
198
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
199
|
+
onProgress(100);
|
|
200
|
+
resolve();
|
|
201
|
+
} else {
|
|
202
|
+
reject(
|
|
203
|
+
new HyperserveUploadError(`Storage PUT failed with status ${xhr.status}`, xhr.status)
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
xhr.addEventListener("timeout", () => {
|
|
208
|
+
reject(new HyperserveTimeoutError("Storage PUT timed out"));
|
|
209
|
+
});
|
|
210
|
+
xhr.addEventListener("error", () => {
|
|
211
|
+
reject(new HyperserveUploadError("Storage PUT failed due to a network error"));
|
|
212
|
+
});
|
|
213
|
+
xhr.send(body);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
177
216
|
|
|
178
217
|
// src/client.ts
|
|
179
218
|
var DEFAULT_BASE_URL = "https://api.hyperserve.io/api";
|
|
@@ -199,14 +238,13 @@ var HyperserveClient = class {
|
|
|
199
238
|
retries: this.retries,
|
|
200
239
|
body: {
|
|
201
240
|
filename: options.filename,
|
|
202
|
-
fileSizeBytes: options.fileSizeBytes,
|
|
203
241
|
resolutions: options.resolutions,
|
|
204
242
|
isPublic: options.isPublic,
|
|
205
243
|
...options.thumbnailTimestampsSeconds !== void 0 && {
|
|
206
|
-
|
|
244
|
+
thumbnailTimestampsSeconds: options.thumbnailTimestampsSeconds
|
|
207
245
|
},
|
|
208
246
|
...options.customMetadata !== void 0 && {
|
|
209
|
-
|
|
247
|
+
customMetadata: options.customMetadata
|
|
210
248
|
}
|
|
211
249
|
}
|
|
212
250
|
});
|
|
@@ -280,13 +318,14 @@ var HyperserveClient = class {
|
|
|
280
318
|
const normalized = normalizeFile(file, filename, options.fileSizeBytes);
|
|
281
319
|
const upload = await this.createVideo({
|
|
282
320
|
filename,
|
|
283
|
-
fileSizeBytes: normalized.size,
|
|
284
321
|
resolutions,
|
|
285
322
|
isPublic,
|
|
286
323
|
...thumbnailTimestampsSeconds !== void 0 && { thumbnailTimestampsSeconds },
|
|
287
324
|
...customMetadata !== void 0 && { customMetadata }
|
|
288
325
|
});
|
|
289
|
-
await putToStorage(upload.uploadUrl, upload.contentType, normalized.body
|
|
326
|
+
await putToStorage(upload.uploadUrl, upload.contentType, normalized.body, {
|
|
327
|
+
contentLength: normalized.size
|
|
328
|
+
});
|
|
290
329
|
return this.completeUpload(upload.id);
|
|
291
330
|
}
|
|
292
331
|
};
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/normalize.ts","../src/storage.ts","../src/client.ts","../src/webhook.ts"],"names":["size"],"mappings":"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","file":"index.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 {\n\tHyperserveApiError,\n\tHyperserveError,\n\tHyperserveNotFoundError,\n\tHyperserveTimeoutError,\n\tHyperserveValidationError,\n} from \"./errors.js\";\n\ninterface RequestOptions {\n\tmethod: \"GET\" | \"POST\" | \"DELETE\";\n\turl: string;\n\tapiKey: string;\n\ttimeoutMs: number;\n\tbody?: unknown;\n\tretries?: number;\n}\n\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction isRetryable(err: unknown): boolean {\n\t// Retry on 5xx API errors only — not 4xx, not timeouts\n\tif (err instanceof HyperserveApiError && err.statusCode !== undefined && err.statusCode >= 500)\n\t\treturn true;\n\t// Retry on network/infrastructure errors that aren't SDK-typed (e.g. TypeError: Failed to fetch)\n\tif (err instanceof Error && !(err instanceof HyperserveError)) return true;\n\treturn false;\n}\n\nexport async function apiRequest<T>(options: RequestOptions): Promise<T> {\n\tconst { retries = 0 } = options;\n\tlet attempt = 0;\n\n\twhile (true) {\n\t\ttry {\n\t\t\treturn await attemptRequest<T>(options);\n\t\t} catch (err) {\n\t\t\tif (attempt >= retries || !isRetryable(err)) {\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t\t// Full jitter: random delay up to min(10s, 100ms × 2^attempt)\n\t\t\tconst delay = Math.random() * Math.min(10_000, 100 * 2 ** attempt);\n\t\t\tawait sleep(delay);\n\t\t\tattempt++;\n\t\t}\n\t}\n}\n\nasync function attemptRequest<T>(options: RequestOptions): Promise<T> {\n\tconst { method, url, apiKey, timeoutMs, body } = options;\n\n\tconst controller = new AbortController();\n\tconst timer = setTimeout(() => controller.abort(), timeoutMs);\n\n\tlet response: Response;\n\n\ttry {\n\t\tresponse = await fetch(url, {\n\t\t\tmethod,\n\t\t\theaders: {\n\t\t\t\t\"X-API-KEY\": apiKey,\n\t\t\t\t...(body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n\t\t\t},\n\t\t\t// Omit body entirely when not present — passing body: null on DELETE requests\n\t\t\t// can be treated differently by some proxies and intermediaries.\n\t\t\t...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n\t\t\tsignal: controller.signal,\n\t\t});\n\t} catch (err) {\n\t\tif (err instanceof Error && err.name === \"AbortError\") {\n\t\t\tthrow new HyperserveTimeoutError(`Request to ${url} timed out after ${timeoutMs}ms`);\n\t\t}\n\t\tthrow err;\n\t} finally {\n\t\tclearTimeout(timer);\n\t}\n\n\tif (response.ok) {\n\t\t// 204 No Content\n\t\tif (response.status === 204) {\n\t\t\treturn undefined as T;\n\t\t}\n\t\ttry {\n\t\t\treturn (await response.json()) as T;\n\t\t} catch {\n\t\t\tthrow new HyperserveApiError(`Failed to parse response from ${url}`, response.status);\n\t\t}\n\t}\n\n\tlet errorBody: { message?: string } = {};\n\ttry {\n\t\terrorBody = (await response.json()) as { message?: string };\n\t} catch {\n\t\t// ignore parse failure — use status text\n\t}\n\n\tconst message = errorBody.message ?? response.statusText;\n\n\tif (response.status === 404) {\n\t\tthrow new HyperserveNotFoundError(message);\n\t}\n\n\tif (response.status >= 400 && response.status < 500) {\n\t\tthrow new HyperserveValidationError(message, response.status, errorBody);\n\t}\n\n\tthrow new HyperserveApiError(message, response.status);\n}\n","/**\n * Normalizes the various accepted file input types into a { body, size } pair\n * suitable for use as a fetch/XHR request body.\n *\n * Size inference rules:\n * Blob / File → blob.size\n * Buffer → buffer.byteLength\n * ReadableStream → must be provided via fileSizeBytes\n */\nexport interface NormalizedFile {\n\tbody: Blob | ReadableStream;\n\tsize: number;\n}\n\nexport function normalizeFile(\n\tfile: Blob | Buffer | ReadableStream,\n\tfilename: string,\n\tfileSizeBytes?: number,\n): NormalizedFile {\n\tif (file instanceof ReadableStream) {\n\t\tif (fileSizeBytes === undefined) {\n\t\t\tthrow new TypeError(\n\t\t\t\t\"fileSizeBytes is required when file is a ReadableStream (size cannot be inferred)\",\n\t\t\t);\n\t\t}\n\t\treturn { body: file, size: fileSizeBytes };\n\t}\n\n\t// Node.js Buffer. `Buffer` is a Node global; all explicitly supported edge runtimes\n\t// (Cloudflare Workers, Vercel Edge) ship a Buffer compatibility layer, so this is safe\n\t// for the stated server targets. Pure browser or RN bundles never reach this branch\n\t// because normalize.ts is not imported by the browser or react-native entry points.\n\tif (Buffer.isBuffer(file)) {\n\t\tconst size = fileSizeBytes ?? file.byteLength;\n\t\t// Wrap in a Blob so fetch/XHR handle it uniformly\n\t\t// Copy into a plain ArrayBuffer to avoid SharedArrayBuffer assignability issues\n\t\tconst blob = new Blob([new Uint8Array(file)], { type: deriveTypeHint(filename) });\n\t\treturn { body: blob, size };\n\t}\n\n\t// Blob / File\n\tconst size = fileSizeBytes ?? file.size;\n\treturn { body: file, size };\n}\n\nfunction deriveTypeHint(filename: string): string {\n\t// Minimal hint — the actual Content-Type for the presigned PUT always\n\t// comes from the server, not from this inference. This is only used\n\t// so the Blob is constructed with a reasonable type attribute.\n\tconst ext = filename.slice(filename.lastIndexOf(\".\") + 1).toLowerCase();\n\tconst map: Record<string, string> = {\n\t\tmp4: \"video/mp4\",\n\t\tmov: \"video/quicktime\",\n\t\twebm: \"video/webm\",\n\t\tavi: \"video/x-msvideo\",\n\t\tmkv: \"video/x-matroska\",\n\t\tm4v: \"video/x-m4v\",\n\t};\n\treturn map[ext] ?? \"application/octet-stream\";\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","import { apiRequest } from \"./http.js\";\nimport { normalizeFile } from \"./normalize.js\";\nimport { putToStorage } from \"./storage.js\";\nimport type {\n\tCompleteUploadResult,\n\tCreateVideoOptions,\n\tCreateVideoResult,\n\tGetVideoOptions,\n\tHyperserveClientOptions,\n\tUploadVideoOptions,\n\tVideoResult,\n} from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.hyperserve.io/api\";\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\nexport class HyperserveClient {\n\tprivate readonly apiKey: string;\n\tprivate readonly baseUrl: string;\n\tprivate readonly timeoutMs: number;\n\tprivate readonly retries: number;\n\n\tconstructor(options: HyperserveClientOptions) {\n\t\tthis.apiKey = options.apiKey;\n\t\tthis.baseUrl = options.baseUrl?.replace(/\\/$/, \"\") ?? DEFAULT_BASE_URL;\n\t\tthis.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\t\tthis.retries = options.retries ?? 0;\n\t}\n\n\t/**\n\t * Creates a video record and returns a presigned upload URL.\n\t * Pass uploadUrl and contentType to your frontend so it can PUT the file directly to storage.\n\t * Call completeUpload once the frontend confirms the PUT is done.\n\t */\n\tasync createVideo(options: CreateVideoOptions): Promise<CreateVideoResult> {\n\t\treturn apiRequest<CreateVideoResult>({\n\t\t\tmethod: \"POST\",\n\t\t\turl: `${this.baseUrl}/video`,\n\t\t\tapiKey: this.apiKey,\n\t\t\ttimeoutMs: this.timeoutMs,\n\t\t\tretries: this.retries,\n\t\t\tbody: {\n\t\t\t\tfilename: options.filename,\n\t\t\t\tfileSizeBytes: options.fileSizeBytes,\n\t\t\t\tresolutions: options.resolutions,\n\t\t\t\tisPublic: options.isPublic,\n\t\t\t\t...(options.thumbnailTimestampsSeconds !== undefined && {\n\t\t\t\t\tthumbnail_timestamps_seconds: options.thumbnailTimestampsSeconds,\n\t\t\t\t}),\n\t\t\t\t...(options.customMetadata !== undefined && {\n\t\t\t\t\tcustom_user_metadata: options.customMetadata,\n\t\t\t\t}),\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Notifies Hyperserve that the file has been uploaded to the presigned URL.\n\t * Hyperserve verifies the object and queues transcoding.\n\t * Call this after your frontend confirms the storage PUT is complete.\n\t */\n\tasync completeUpload(videoId: string): Promise<CompleteUploadResult> {\n\t\treturn apiRequest<CompleteUploadResult>({\n\t\t\tmethod: \"POST\",\n\t\t\turl: `${this.baseUrl}/video/${videoId}/complete-upload`,\n\t\t\tapiKey: this.apiKey,\n\t\t\ttimeoutMs: this.timeoutMs,\n\t\t\tretries: this.retries,\n\t\t});\n\t}\n\n\t/**\n\t * Retrieves the current state of a video, including per-resolution status and playback URLs.\n\t *\n\t * @param videoId - The video ID returned by createVideo or uploadVideo.\n\t * @param options.private - Return time-limited signed URLs instead of public URLs.\n\t * @param options.expirationSeconds - Signed URL TTL when private is true. Defaults to 3600.\n\t */\n\tasync getVideo(videoId: string, options?: GetVideoOptions): Promise<VideoResult> {\n\t\tconst isPrivate = options?.private === true;\n\t\tconst expiration = options?.expirationSeconds ?? 3600;\n\n\t\tconst url = isPrivate\n\t\t\t? `${this.baseUrl}/video/${videoId}/private/${expiration}`\n\t\t\t: `${this.baseUrl}/video/${videoId}/public`;\n\n\t\treturn apiRequest<VideoResult>({\n\t\t\tmethod: \"GET\",\n\t\t\turl,\n\t\t\tapiKey: this.apiKey,\n\t\t\ttimeoutMs: this.timeoutMs,\n\t\t\tretries: this.retries,\n\t\t});\n\t}\n\n\t/**\n\t * Deletes a video and all associated resolutions and thumbnails.\n\t */\n\tasync deleteVideo(videoId: string): Promise<void> {\n\t\treturn apiRequest<void>({\n\t\t\tmethod: \"DELETE\",\n\t\t\turl: `${this.baseUrl}/video/${videoId}`,\n\t\t\tapiKey: this.apiKey,\n\t\t\ttimeoutMs: this.timeoutMs,\n\t\t\tretries: this.retries,\n\t\t});\n\t}\n\n\t/**\n\t * Deletes a single resolution for a video.\n\t */\n\tasync deleteResolution(resolutionId: string): Promise<void> {\n\t\treturn apiRequest<void>({\n\t\t\tmethod: \"DELETE\",\n\t\t\turl: `${this.baseUrl}/video/resolution/${resolutionId}`,\n\t\t\tapiKey: this.apiKey,\n\t\t\ttimeoutMs: this.timeoutMs,\n\t\t\tretries: this.retries,\n\t\t});\n\t}\n\n\t/**\n\t * Convenience method for server-side / script use cases.\n\t * Wraps createVideo, the storage PUT, and completeUpload into a single call.\n\t *\n\t * Not suitable for the browser proxy pattern — use createVideo + putVideoToStorage\n\t * from '@hyperserve/hyperserve-js/browser' + completeUpload separately for that flow.\n\t */\n\tasync uploadVideo(options: UploadVideoOptions): Promise<CompleteUploadResult> {\n\t\tconst { file, filename, resolutions, isPublic, thumbnailTimestampsSeconds, customMetadata } =\n\t\t\toptions;\n\n\t\tconst normalized = normalizeFile(file, filename, options.fileSizeBytes);\n\n\t\tconst upload = await this.createVideo({\n\t\t\tfilename,\n\t\t\tfileSizeBytes: normalized.size,\n\t\t\tresolutions,\n\t\t\tisPublic,\n\t\t\t...(thumbnailTimestampsSeconds !== undefined && { thumbnailTimestampsSeconds }),\n\t\t\t...(customMetadata !== undefined && { customMetadata }),\n\t\t});\n\n\t\tawait putToStorage(upload.uploadUrl, upload.contentType, normalized.body);\n\n\t\treturn this.completeUpload(upload.id);\n\t}\n}\n","import type { VerifyWebhookSignatureOptions } from \"./types.js\";\n\nconst DEFAULT_TOLERANCE_MS = 300_000; // 5 minutes — matches server-side enforcement\n\n/**\n * Verifies the x-hyperserve-signature header on an incoming webhook request.\n *\n * The signature header has the format \"{timestampMs}.{hmac-sha256-hex}\", where the HMAC\n * is computed over \"{timestampMs}.{rawBody}\" using your webhook signing secret. This proves\n * both when the request was sent (replay protection) and that the body was not tampered with\n * (integrity). Any modification to the body or timestamp will invalidate the signature.\n *\n * Returns true if the signature is valid and the timestamp is within the tolerance window.\n * Returns false if the signature is invalid, the timestamp has expired, or the header is malformed.\n *\n * Uses the Web Crypto API for a constant-time HMAC comparison — safe on Node 18+, Bun, Deno,\n * Cloudflare Workers, Vercel Edge, and all other supported server environments.\n *\n * IMPORTANT: pass the raw request body string exactly as received. Do not parse and re-serialize\n * JSON — any whitespace difference will invalidate the signature.\n *\n * @example\n * import { verifyWebhookSignature } from '@hyperserve/hyperserve-js';\n *\n * // Express (use express.raw, not express.json, so you get the raw body)\n * app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {\n * const isValid = await verifyWebhookSignature({\n * signature: req.headers['x-hyperserve-signature'] ?? '',\n * secret: process.env.HYPERSERVE_WEBHOOK_SECRET,\n * body: req.body.toString(),\n * });\n * if (!isValid) return res.status(401).end();\n * });\n *\n * // Next.js App Router\n * const body = await request.text();\n * const isValid = await verifyWebhookSignature({\n * signature: request.headers.get('x-hyperserve-signature') ?? '',\n * secret: process.env.HYPERSERVE_WEBHOOK_SECRET!,\n * body,\n * });\n */\nexport async function verifyWebhookSignature(\n\toptions: VerifyWebhookSignatureOptions,\n): Promise<boolean> {\n\tconst { signature, secret, body, toleranceMs = DEFAULT_TOLERANCE_MS } = options;\n\n\tconst dotIndex = signature.indexOf(\".\");\n\tif (dotIndex === -1) return false;\n\n\tconst timestampStr = signature.slice(0, dotIndex);\n\tconst receivedHex = signature.slice(dotIndex + 1);\n\n\t// Validate timestamp is a non-negative integer\n\tconst timestamp = Number(timestampStr);\n\tif (!Number.isInteger(timestamp) || timestamp < 0) return false;\n\n\t// Reject if timestamp is outside the tolerance window.\n\t// Math.abs handles future timestamps (clock skew or forged headers) — without it,\n\t// a negative difference would never exceed toleranceMs, accepting the signature forever.\n\tif (Math.abs(Date.now() - timestamp) > toleranceMs) return false;\n\n\tconst receivedBytes = hexToBytes(receivedHex);\n\tif (receivedBytes === null) return false;\n\n\tconst encoder = new TextEncoder();\n\tconst key = await crypto.subtle.importKey(\n\t\t\"raw\",\n\t\tencoder.encode(secret),\n\t\t{ name: \"HMAC\", hash: \"SHA-256\" },\n\t\tfalse,\n\t\t[\"verify\"],\n\t);\n\n\t// The signed message is \"{timestampMs}.{rawBody}\" — covers both freshness and integrity.\n\t// crypto.subtle.verify performs a constant-time comparison.\n\treturn crypto.subtle.verify(\n\t\t\"HMAC\",\n\t\tkey,\n\t\treceivedBytes,\n\t\tencoder.encode(`${timestampStr}.${body}`),\n\t);\n}\n\nfunction hexToBytes(hex: string): Uint8Array<ArrayBuffer> | null {\n\tif (hex.length === 0 || hex.length % 2 !== 0) return null;\n\tconst bytes = new Uint8Array(new ArrayBuffer(hex.length / 2));\n\tfor (let i = 0; i < hex.length; i += 2) {\n\t\tconst value = parseInt(hex.slice(i, i + 2), 16);\n\t\tif (Number.isNaN(value)) return null;\n\t\tbytes[i / 2] = value;\n\t}\n\treturn bytes;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/normalize.ts","../src/storage.ts","../src/client.ts","../src/webhook.ts"],"names":["size"],"mappings":"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","file":"index.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 {\n\tHyperserveApiError,\n\tHyperserveError,\n\tHyperserveNotFoundError,\n\tHyperserveTimeoutError,\n\tHyperserveValidationError,\n} from \"./errors.js\";\n\ninterface RequestOptions {\n\tmethod: \"GET\" | \"POST\" | \"DELETE\";\n\turl: string;\n\tapiKey: string;\n\ttimeoutMs: number;\n\tbody?: unknown;\n\tretries?: number;\n}\n\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction isRetryable(err: unknown): boolean {\n\t// Retry on 5xx API errors only — not 4xx, not timeouts\n\tif (err instanceof HyperserveApiError && err.statusCode !== undefined && err.statusCode >= 500)\n\t\treturn true;\n\t// Retry on network/infrastructure errors that aren't SDK-typed (e.g. TypeError: Failed to fetch)\n\tif (err instanceof Error && !(err instanceof HyperserveError)) return true;\n\treturn false;\n}\n\nexport async function apiRequest<T>(options: RequestOptions): Promise<T> {\n\tconst { retries = 0 } = options;\n\tlet attempt = 0;\n\n\twhile (true) {\n\t\ttry {\n\t\t\treturn await attemptRequest<T>(options);\n\t\t} catch (err) {\n\t\t\tif (attempt >= retries || !isRetryable(err)) {\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t\t// Full jitter: random delay up to min(10s, 100ms × 2^attempt)\n\t\t\tconst delay = Math.random() * Math.min(10_000, 100 * 2 ** attempt);\n\t\t\tawait sleep(delay);\n\t\t\tattempt++;\n\t\t}\n\t}\n}\n\nasync function attemptRequest<T>(options: RequestOptions): Promise<T> {\n\tconst { method, url, apiKey, timeoutMs, body } = options;\n\n\tconst controller = new AbortController();\n\tconst timer = setTimeout(() => controller.abort(), timeoutMs);\n\n\tlet response: Response;\n\n\ttry {\n\t\tresponse = await fetch(url, {\n\t\t\tmethod,\n\t\t\theaders: {\n\t\t\t\t\"X-API-KEY\": apiKey,\n\t\t\t\t...(body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n\t\t\t},\n\t\t\t// Omit body entirely when not present — passing body: null on DELETE requests\n\t\t\t// can be treated differently by some proxies and intermediaries.\n\t\t\t...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n\t\t\tsignal: controller.signal,\n\t\t});\n\t} catch (err) {\n\t\tif (err instanceof Error && err.name === \"AbortError\") {\n\t\t\tthrow new HyperserveTimeoutError(`Request to ${url} timed out after ${timeoutMs}ms`);\n\t\t}\n\t\tthrow err;\n\t} finally {\n\t\tclearTimeout(timer);\n\t}\n\n\tif (response.ok) {\n\t\t// 204 No Content\n\t\tif (response.status === 204) {\n\t\t\treturn undefined as T;\n\t\t}\n\t\ttry {\n\t\t\treturn (await response.json()) as T;\n\t\t} catch {\n\t\t\tthrow new HyperserveApiError(`Failed to parse response from ${url}`, response.status);\n\t\t}\n\t}\n\n\tlet errorBody: { message?: string } = {};\n\ttry {\n\t\terrorBody = (await response.json()) as { message?: string };\n\t} catch {\n\t\t// ignore parse failure — use status text\n\t}\n\n\tconst message = errorBody.message ?? response.statusText;\n\n\tif (response.status === 404) {\n\t\tthrow new HyperserveNotFoundError(message);\n\t}\n\n\tif (response.status >= 400 && response.status < 500) {\n\t\tthrow new HyperserveValidationError(message, response.status, errorBody);\n\t}\n\n\tthrow new HyperserveApiError(message, response.status);\n}\n","/**\n * Normalizes the various accepted file input types into a { body, size } pair\n * suitable for use as a fetch/XHR request body. The size is only consumed for a\n * ReadableStream body, where it becomes the Content-Length header on the storage\n * PUT — S3-compatible storage rejects a chunked PUT with 411. Blob bodies carry\n * their own length, so the size is ignored for them.\n *\n * Size inference rules:\n * Blob / File → blob.size\n * Buffer → buffer.byteLength\n * ReadableStream → must be provided via fileSizeBytes\n */\nexport interface NormalizedFile {\n\tbody: Blob | ReadableStream;\n\tsize: number;\n}\n\nexport function normalizeFile(\n\tfile: Blob | Buffer | ReadableStream,\n\tfilename: string,\n\tfileSizeBytes?: number,\n): NormalizedFile {\n\tif (file instanceof ReadableStream) {\n\t\tif (fileSizeBytes === undefined) {\n\t\t\tthrow new TypeError(\n\t\t\t\t\"fileSizeBytes is required when file is a ReadableStream (size cannot be inferred)\",\n\t\t\t);\n\t\t}\n\t\treturn { body: file, size: fileSizeBytes };\n\t}\n\n\t// Node.js Buffer. `Buffer` is a Node global; all explicitly supported edge runtimes\n\t// (Cloudflare Workers, Vercel Edge) ship a Buffer compatibility layer, so this is safe\n\t// for the stated server targets. Pure browser or RN bundles never reach this branch\n\t// because normalize.ts is not imported by the browser or react-native entry points.\n\tif (Buffer.isBuffer(file)) {\n\t\tconst size = fileSizeBytes ?? file.byteLength;\n\t\t// Wrap in a Blob so fetch/XHR handle it uniformly\n\t\t// Copy into a plain ArrayBuffer to avoid SharedArrayBuffer assignability issues\n\t\tconst blob = new Blob([new Uint8Array(file)], { type: deriveTypeHint(filename) });\n\t\treturn { body: blob, size };\n\t}\n\n\t// Blob / File\n\tconst size = fileSizeBytes ?? file.size;\n\treturn { body: file, size };\n}\n\nfunction deriveTypeHint(filename: string): string {\n\t// Minimal hint — the actual Content-Type for the presigned PUT always\n\t// comes from the server, not from this inference. This is only used\n\t// so the Blob is constructed with a reasonable type attribute.\n\tconst ext = filename.slice(filename.lastIndexOf(\".\") + 1).toLowerCase();\n\tconst map: Record<string, string> = {\n\t\tmp4: \"video/mp4\",\n\t\tmov: \"video/quicktime\",\n\t\twebm: \"video/webm\",\n\t\tavi: \"video/x-msvideo\",\n\t\tmkv: \"video/x-matroska\",\n\t\tm4v: \"video/x-m4v\",\n\t};\n\treturn map[ext] ?? \"application/octet-stream\";\n}\n","import { HyperserveTimeoutError, HyperserveUploadError } from \"./errors.js\";\n\nexport interface PutToStorageOptions {\n\t/**\n\t * Byte length of the body. Only used for ReadableStream bodies, which are sent\n\t * with chunked transfer encoding unless Content-Length is set explicitly — and\n\t * S3-compatible storage rejects a chunked PUT with 411 MissingContentLength.\n\t * Blob bodies carry their own length, so this is ignored for them.\n\t */\n\tcontentLength?: number;\n\tonProgress?: (percent: number) => void;\n}\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\toptions: PutToStorageOptions = {},\n): Promise<void> {\n\tconst { contentLength, onProgress } = options;\n\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, contentLength);\n}\n\nfunction putWithFetch(\n\tuploadUrl: string,\n\tcontentType: string,\n\tbody: Blob | ReadableStream,\n\tcontentLength?: number,\n): Promise<void> {\n\tconst isStream = body instanceof ReadableStream;\n\n\treturn fetch(uploadUrl, {\n\t\tmethod: \"PUT\",\n\t\theaders: {\n\t\t\t\"Content-Type\": contentType,\n\t\t\t// A stream body would otherwise go out chunked, which S3-compatible\n\t\t\t// storage rejects with 411. Blob bodies get Content-Length from fetch.\n\t\t\t...(isStream && contentLength !== undefined\n\t\t\t\t? { \"Content-Length\": String(contentLength) }\n\t\t\t\t: {}),\n\t\t},\n\t\t// duplex is required for ReadableStream bodies in some runtimes (Node 18)\n\t\t...(isStream ? { 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","import { apiRequest } from \"./http.js\";\nimport { normalizeFile } from \"./normalize.js\";\nimport { putToStorage } from \"./storage.js\";\nimport type {\n\tCompleteUploadResult,\n\tCreateVideoOptions,\n\tCreateVideoResult,\n\tGetVideoOptions,\n\tHyperserveClientOptions,\n\tUploadVideoOptions,\n\tVideoResult,\n} from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.hyperserve.io/api\";\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\nexport class HyperserveClient {\n\tprivate readonly apiKey: string;\n\tprivate readonly baseUrl: string;\n\tprivate readonly timeoutMs: number;\n\tprivate readonly retries: number;\n\n\tconstructor(options: HyperserveClientOptions) {\n\t\tthis.apiKey = options.apiKey;\n\t\tthis.baseUrl = options.baseUrl?.replace(/\\/$/, \"\") ?? DEFAULT_BASE_URL;\n\t\tthis.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\t\tthis.retries = options.retries ?? 0;\n\t}\n\n\t/**\n\t * Creates a video record and returns a presigned upload URL.\n\t * Pass uploadUrl and contentType to your frontend so it can PUT the file directly to storage.\n\t * Call completeUpload once the frontend confirms the PUT is done.\n\t */\n\tasync createVideo(options: CreateVideoOptions): Promise<CreateVideoResult> {\n\t\treturn apiRequest<CreateVideoResult>({\n\t\t\tmethod: \"POST\",\n\t\t\turl: `${this.baseUrl}/video`,\n\t\t\tapiKey: this.apiKey,\n\t\t\ttimeoutMs: this.timeoutMs,\n\t\t\tretries: this.retries,\n\t\t\tbody: {\n\t\t\t\tfilename: options.filename,\n\t\t\t\tresolutions: options.resolutions,\n\t\t\t\tisPublic: options.isPublic,\n\t\t\t\t...(options.thumbnailTimestampsSeconds !== undefined && {\n\t\t\t\t\tthumbnailTimestampsSeconds: options.thumbnailTimestampsSeconds,\n\t\t\t\t}),\n\t\t\t\t...(options.customMetadata !== undefined && {\n\t\t\t\t\tcustomMetadata: options.customMetadata,\n\t\t\t\t}),\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Notifies Hyperserve that the file has been uploaded to the presigned URL.\n\t * Hyperserve verifies the object and queues transcoding.\n\t * Call this after your frontend confirms the storage PUT is complete.\n\t */\n\tasync completeUpload(videoId: string): Promise<CompleteUploadResult> {\n\t\treturn apiRequest<CompleteUploadResult>({\n\t\t\tmethod: \"POST\",\n\t\t\turl: `${this.baseUrl}/video/${videoId}/complete-upload`,\n\t\t\tapiKey: this.apiKey,\n\t\t\ttimeoutMs: this.timeoutMs,\n\t\t\tretries: this.retries,\n\t\t});\n\t}\n\n\t/**\n\t * Retrieves the current state of a video, including per-resolution status and playback URLs.\n\t *\n\t * @param videoId - The video ID returned by createVideo or uploadVideo.\n\t * @param options.private - Return time-limited signed URLs instead of public URLs.\n\t * @param options.expirationSeconds - Signed URL TTL when private is true. Defaults to 3600.\n\t */\n\tasync getVideo(videoId: string, options?: GetVideoOptions): Promise<VideoResult> {\n\t\tconst isPrivate = options?.private === true;\n\t\tconst expiration = options?.expirationSeconds ?? 3600;\n\n\t\tconst url = isPrivate\n\t\t\t? `${this.baseUrl}/video/${videoId}/private/${expiration}`\n\t\t\t: `${this.baseUrl}/video/${videoId}/public`;\n\n\t\treturn apiRequest<VideoResult>({\n\t\t\tmethod: \"GET\",\n\t\t\turl,\n\t\t\tapiKey: this.apiKey,\n\t\t\ttimeoutMs: this.timeoutMs,\n\t\t\tretries: this.retries,\n\t\t});\n\t}\n\n\t/**\n\t * Deletes a video and all associated resolutions and thumbnails.\n\t */\n\tasync deleteVideo(videoId: string): Promise<void> {\n\t\treturn apiRequest<void>({\n\t\t\tmethod: \"DELETE\",\n\t\t\turl: `${this.baseUrl}/video/${videoId}`,\n\t\t\tapiKey: this.apiKey,\n\t\t\ttimeoutMs: this.timeoutMs,\n\t\t\tretries: this.retries,\n\t\t});\n\t}\n\n\t/**\n\t * Deletes a single resolution for a video.\n\t */\n\tasync deleteResolution(resolutionId: string): Promise<void> {\n\t\treturn apiRequest<void>({\n\t\t\tmethod: \"DELETE\",\n\t\t\turl: `${this.baseUrl}/video/resolution/${resolutionId}`,\n\t\t\tapiKey: this.apiKey,\n\t\t\ttimeoutMs: this.timeoutMs,\n\t\t\tretries: this.retries,\n\t\t});\n\t}\n\n\t/**\n\t * Convenience method for server-side / script use cases.\n\t * Wraps createVideo, the storage PUT, and completeUpload into a single call.\n\t *\n\t * Not suitable for the browser proxy pattern — use createVideo + putVideoToStorage\n\t * from '@hyperserve/hyperserve-js/browser' + completeUpload separately for that flow.\n\t */\n\tasync uploadVideo(options: UploadVideoOptions): Promise<CompleteUploadResult> {\n\t\tconst { file, filename, resolutions, isPublic, thumbnailTimestampsSeconds, customMetadata } =\n\t\t\toptions;\n\n\t\tconst normalized = normalizeFile(file, filename, options.fileSizeBytes);\n\n\t\tconst upload = await this.createVideo({\n\t\t\tfilename,\n\t\t\tresolutions,\n\t\t\tisPublic,\n\t\t\t...(thumbnailTimestampsSeconds !== undefined && { thumbnailTimestampsSeconds }),\n\t\t\t...(customMetadata !== undefined && { customMetadata }),\n\t\t});\n\n\t\tawait putToStorage(upload.uploadUrl, upload.contentType, normalized.body, {\n\t\t\tcontentLength: normalized.size,\n\t\t});\n\n\t\treturn this.completeUpload(upload.id);\n\t}\n}\n","import type { VerifyWebhookSignatureOptions } from \"./types.js\";\n\nconst DEFAULT_TOLERANCE_MS = 300_000; // 5 minutes — matches server-side enforcement\n\n/**\n * Verifies the x-hyperserve-signature header on an incoming webhook request.\n *\n * The signature header has the format \"{timestampMs}.{hmac-sha256-hex}\", where the HMAC\n * is computed over \"{timestampMs}.{rawBody}\" using your webhook signing secret. This proves\n * both when the request was sent (replay protection) and that the body was not tampered with\n * (integrity). Any modification to the body or timestamp will invalidate the signature.\n *\n * Returns true if the signature is valid and the timestamp is within the tolerance window.\n * Returns false if the signature is invalid, the timestamp has expired, or the header is malformed.\n *\n * Uses the Web Crypto API for a constant-time HMAC comparison — safe on Node 18+, Bun, Deno,\n * Cloudflare Workers, Vercel Edge, and all other supported server environments.\n *\n * IMPORTANT: pass the raw request body string exactly as received. Do not parse and re-serialize\n * JSON — any whitespace difference will invalidate the signature.\n *\n * @example\n * import { verifyWebhookSignature } from '@hyperserve/hyperserve-js';\n *\n * // Express (use express.raw, not express.json, so you get the raw body)\n * app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {\n * const isValid = await verifyWebhookSignature({\n * signature: req.headers['x-hyperserve-signature'] ?? '',\n * secret: process.env.HYPERSERVE_WEBHOOK_SECRET,\n * body: req.body.toString(),\n * });\n * if (!isValid) return res.status(401).end();\n * });\n *\n * // Next.js App Router\n * const body = await request.text();\n * const isValid = await verifyWebhookSignature({\n * signature: request.headers.get('x-hyperserve-signature') ?? '',\n * secret: process.env.HYPERSERVE_WEBHOOK_SECRET!,\n * body,\n * });\n */\nexport async function verifyWebhookSignature(\n\toptions: VerifyWebhookSignatureOptions,\n): Promise<boolean> {\n\tconst { signature, secret, body, toleranceMs = DEFAULT_TOLERANCE_MS } = options;\n\n\tconst dotIndex = signature.indexOf(\".\");\n\tif (dotIndex === -1) return false;\n\n\tconst timestampStr = signature.slice(0, dotIndex);\n\tconst receivedHex = signature.slice(dotIndex + 1);\n\n\t// Validate timestamp is a non-negative integer\n\tconst timestamp = Number(timestampStr);\n\tif (!Number.isInteger(timestamp) || timestamp < 0) return false;\n\n\t// Reject if timestamp is outside the tolerance window.\n\t// Math.abs handles future timestamps (clock skew or forged headers) — without it,\n\t// a negative difference would never exceed toleranceMs, accepting the signature forever.\n\tif (Math.abs(Date.now() - timestamp) > toleranceMs) return false;\n\n\tconst receivedBytes = hexToBytes(receivedHex);\n\tif (receivedBytes === null) return false;\n\n\tconst encoder = new TextEncoder();\n\tconst key = await crypto.subtle.importKey(\n\t\t\"raw\",\n\t\tencoder.encode(secret),\n\t\t{ name: \"HMAC\", hash: \"SHA-256\" },\n\t\tfalse,\n\t\t[\"verify\"],\n\t);\n\n\t// The signed message is \"{timestampMs}.{rawBody}\" — covers both freshness and integrity.\n\t// crypto.subtle.verify performs a constant-time comparison.\n\treturn crypto.subtle.verify(\n\t\t\"HMAC\",\n\t\tkey,\n\t\treceivedBytes,\n\t\tencoder.encode(`${timestampStr}.${body}`),\n\t);\n}\n\nfunction hexToBytes(hex: string): Uint8Array<ArrayBuffer> | null {\n\tif (hex.length === 0 || hex.length % 2 !== 0) return null;\n\tconst bytes = new Uint8Array(new ArrayBuffer(hex.length / 2));\n\tfor (let i = 0; i < hex.length; i += 2) {\n\t\tconst value = parseInt(hex.slice(i, i + 2), 16);\n\t\tif (Number.isNaN(value)) return null;\n\t\tbytes[i / 2] = value;\n\t}\n\treturn bytes;\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { H as HyperserveClientOptions, C as CreateVideoOptions, a as CreateVideoResult, b as CompleteUploadResult, G as GetVideoOptions, V as VideoResult, U as UploadVideoOptions, c as VerifyWebhookSignatureOptions } from './errors-
|
|
2
|
-
export { d as HyperserveApiError, e as HyperserveError, f as HyperserveNotFoundError, g as HyperserveTimeoutError, h as HyperserveUploadError, i as HyperserveValidationError, P as PutVideoToStorageOptions, j as PutVideoToStorageRNOptions, k as VideoResolution, l as VideoResolutionResult, m as VideoStatus } from './errors-
|
|
1
|
+
import { H as HyperserveClientOptions, C as CreateVideoOptions, a as CreateVideoResult, b as CompleteUploadResult, G as GetVideoOptions, V as VideoResult, U as UploadVideoOptions, c as VerifyWebhookSignatureOptions } from './errors-DJGPqrI5.cjs';
|
|
2
|
+
export { d as HyperserveApiError, e as HyperserveError, f as HyperserveNotFoundError, g as HyperserveTimeoutError, h as HyperserveUploadError, i as HyperserveValidationError, P as PutVideoToStorageOptions, j as PutVideoToStorageRNOptions, k as VideoResolution, l as VideoResolutionResult, m as VideoStatus } from './errors-DJGPqrI5.cjs';
|
|
3
3
|
|
|
4
4
|
declare class HyperserveClient {
|
|
5
5
|
private readonly apiKey;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { H as HyperserveClientOptions, C as CreateVideoOptions, a as CreateVideoResult, b as CompleteUploadResult, G as GetVideoOptions, V as VideoResult, U as UploadVideoOptions, c as VerifyWebhookSignatureOptions } from './errors-
|
|
2
|
-
export { d as HyperserveApiError, e as HyperserveError, f as HyperserveNotFoundError, g as HyperserveTimeoutError, h as HyperserveUploadError, i as HyperserveValidationError, P as PutVideoToStorageOptions, j as PutVideoToStorageRNOptions, k as VideoResolution, l as VideoResolutionResult, m as VideoStatus } from './errors-
|
|
1
|
+
import { H as HyperserveClientOptions, C as CreateVideoOptions, a as CreateVideoResult, b as CompleteUploadResult, G as GetVideoOptions, V as VideoResult, U as UploadVideoOptions, c as VerifyWebhookSignatureOptions } from './errors-DJGPqrI5.js';
|
|
2
|
+
export { d as HyperserveApiError, e as HyperserveError, f as HyperserveNotFoundError, g as HyperserveTimeoutError, h as HyperserveUploadError, i as HyperserveValidationError, P as PutVideoToStorageOptions, j as PutVideoToStorageRNOptions, k as VideoResolution, l as VideoResolutionResult, m as VideoStatus } from './errors-DJGPqrI5.js';
|
|
3
3
|
|
|
4
4
|
declare class HyperserveClient {
|
|
5
5
|
private readonly apiKey;
|
package/dist/index.js
CHANGED
|
@@ -153,15 +153,25 @@ function deriveTypeHint(filename) {
|
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
// src/storage.ts
|
|
156
|
-
async function putToStorage(uploadUrl, contentType, body,
|
|
157
|
-
|
|
156
|
+
async function putToStorage(uploadUrl, contentType, body, options = {}) {
|
|
157
|
+
const { contentLength, onProgress } = options;
|
|
158
|
+
if (onProgress !== void 0 && typeof XMLHttpRequest !== "undefined" && !(body instanceof ReadableStream)) {
|
|
159
|
+
return putWithXhr(uploadUrl, contentType, body, onProgress);
|
|
160
|
+
}
|
|
161
|
+
return putWithFetch(uploadUrl, contentType, body, contentLength);
|
|
158
162
|
}
|
|
159
|
-
function putWithFetch(uploadUrl, contentType, body) {
|
|
163
|
+
function putWithFetch(uploadUrl, contentType, body, contentLength) {
|
|
164
|
+
const isStream = body instanceof ReadableStream;
|
|
160
165
|
return fetch(uploadUrl, {
|
|
161
166
|
method: "PUT",
|
|
162
|
-
headers: {
|
|
167
|
+
headers: {
|
|
168
|
+
"Content-Type": contentType,
|
|
169
|
+
// A stream body would otherwise go out chunked, which S3-compatible
|
|
170
|
+
// storage rejects with 411. Blob bodies get Content-Length from fetch.
|
|
171
|
+
...isStream && contentLength !== void 0 ? { "Content-Length": String(contentLength) } : {}
|
|
172
|
+
},
|
|
163
173
|
// duplex is required for ReadableStream bodies in some runtimes (Node 18)
|
|
164
|
-
...
|
|
174
|
+
...isStream ? { duplex: "half" } : {},
|
|
165
175
|
body
|
|
166
176
|
}).then((response) => {
|
|
167
177
|
if (!response.ok) {
|
|
@@ -172,6 +182,35 @@ function putWithFetch(uploadUrl, contentType, body) {
|
|
|
172
182
|
}
|
|
173
183
|
});
|
|
174
184
|
}
|
|
185
|
+
function putWithXhr(uploadUrl, contentType, body, onProgress) {
|
|
186
|
+
return new Promise((resolve, reject) => {
|
|
187
|
+
const xhr = new XMLHttpRequest();
|
|
188
|
+
xhr.open("PUT", uploadUrl);
|
|
189
|
+
xhr.setRequestHeader("Content-Type", contentType);
|
|
190
|
+
xhr.upload.addEventListener("progress", (event) => {
|
|
191
|
+
if (event.lengthComputable) {
|
|
192
|
+
onProgress(Math.round(event.loaded / event.total * 100));
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
xhr.addEventListener("load", () => {
|
|
196
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
197
|
+
onProgress(100);
|
|
198
|
+
resolve();
|
|
199
|
+
} else {
|
|
200
|
+
reject(
|
|
201
|
+
new HyperserveUploadError(`Storage PUT failed with status ${xhr.status}`, xhr.status)
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
xhr.addEventListener("timeout", () => {
|
|
206
|
+
reject(new HyperserveTimeoutError("Storage PUT timed out"));
|
|
207
|
+
});
|
|
208
|
+
xhr.addEventListener("error", () => {
|
|
209
|
+
reject(new HyperserveUploadError("Storage PUT failed due to a network error"));
|
|
210
|
+
});
|
|
211
|
+
xhr.send(body);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
175
214
|
|
|
176
215
|
// src/client.ts
|
|
177
216
|
var DEFAULT_BASE_URL = "https://api.hyperserve.io/api";
|
|
@@ -197,14 +236,13 @@ var HyperserveClient = class {
|
|
|
197
236
|
retries: this.retries,
|
|
198
237
|
body: {
|
|
199
238
|
filename: options.filename,
|
|
200
|
-
fileSizeBytes: options.fileSizeBytes,
|
|
201
239
|
resolutions: options.resolutions,
|
|
202
240
|
isPublic: options.isPublic,
|
|
203
241
|
...options.thumbnailTimestampsSeconds !== void 0 && {
|
|
204
|
-
|
|
242
|
+
thumbnailTimestampsSeconds: options.thumbnailTimestampsSeconds
|
|
205
243
|
},
|
|
206
244
|
...options.customMetadata !== void 0 && {
|
|
207
|
-
|
|
245
|
+
customMetadata: options.customMetadata
|
|
208
246
|
}
|
|
209
247
|
}
|
|
210
248
|
});
|
|
@@ -278,13 +316,14 @@ var HyperserveClient = class {
|
|
|
278
316
|
const normalized = normalizeFile(file, filename, options.fileSizeBytes);
|
|
279
317
|
const upload = await this.createVideo({
|
|
280
318
|
filename,
|
|
281
|
-
fileSizeBytes: normalized.size,
|
|
282
319
|
resolutions,
|
|
283
320
|
isPublic,
|
|
284
321
|
...thumbnailTimestampsSeconds !== void 0 && { thumbnailTimestampsSeconds },
|
|
285
322
|
...customMetadata !== void 0 && { customMetadata }
|
|
286
323
|
});
|
|
287
|
-
await putToStorage(upload.uploadUrl, upload.contentType, normalized.body
|
|
324
|
+
await putToStorage(upload.uploadUrl, upload.contentType, normalized.body, {
|
|
325
|
+
contentLength: normalized.size
|
|
326
|
+
});
|
|
288
327
|
return this.completeUpload(upload.id);
|
|
289
328
|
}
|
|
290
329
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/normalize.ts","../src/storage.ts","../src/client.ts","../src/webhook.ts"],"names":["size"],"mappings":"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","file":"index.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 {\n\tHyperserveApiError,\n\tHyperserveError,\n\tHyperserveNotFoundError,\n\tHyperserveTimeoutError,\n\tHyperserveValidationError,\n} from \"./errors.js\";\n\ninterface RequestOptions {\n\tmethod: \"GET\" | \"POST\" | \"DELETE\";\n\turl: string;\n\tapiKey: string;\n\ttimeoutMs: number;\n\tbody?: unknown;\n\tretries?: number;\n}\n\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction isRetryable(err: unknown): boolean {\n\t// Retry on 5xx API errors only — not 4xx, not timeouts\n\tif (err instanceof HyperserveApiError && err.statusCode !== undefined && err.statusCode >= 500)\n\t\treturn true;\n\t// Retry on network/infrastructure errors that aren't SDK-typed (e.g. TypeError: Failed to fetch)\n\tif (err instanceof Error && !(err instanceof HyperserveError)) return true;\n\treturn false;\n}\n\nexport async function apiRequest<T>(options: RequestOptions): Promise<T> {\n\tconst { retries = 0 } = options;\n\tlet attempt = 0;\n\n\twhile (true) {\n\t\ttry {\n\t\t\treturn await attemptRequest<T>(options);\n\t\t} catch (err) {\n\t\t\tif (attempt >= retries || !isRetryable(err)) {\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t\t// Full jitter: random delay up to min(10s, 100ms × 2^attempt)\n\t\t\tconst delay = Math.random() * Math.min(10_000, 100 * 2 ** attempt);\n\t\t\tawait sleep(delay);\n\t\t\tattempt++;\n\t\t}\n\t}\n}\n\nasync function attemptRequest<T>(options: RequestOptions): Promise<T> {\n\tconst { method, url, apiKey, timeoutMs, body } = options;\n\n\tconst controller = new AbortController();\n\tconst timer = setTimeout(() => controller.abort(), timeoutMs);\n\n\tlet response: Response;\n\n\ttry {\n\t\tresponse = await fetch(url, {\n\t\t\tmethod,\n\t\t\theaders: {\n\t\t\t\t\"X-API-KEY\": apiKey,\n\t\t\t\t...(body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n\t\t\t},\n\t\t\t// Omit body entirely when not present — passing body: null on DELETE requests\n\t\t\t// can be treated differently by some proxies and intermediaries.\n\t\t\t...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n\t\t\tsignal: controller.signal,\n\t\t});\n\t} catch (err) {\n\t\tif (err instanceof Error && err.name === \"AbortError\") {\n\t\t\tthrow new HyperserveTimeoutError(`Request to ${url} timed out after ${timeoutMs}ms`);\n\t\t}\n\t\tthrow err;\n\t} finally {\n\t\tclearTimeout(timer);\n\t}\n\n\tif (response.ok) {\n\t\t// 204 No Content\n\t\tif (response.status === 204) {\n\t\t\treturn undefined as T;\n\t\t}\n\t\ttry {\n\t\t\treturn (await response.json()) as T;\n\t\t} catch {\n\t\t\tthrow new HyperserveApiError(`Failed to parse response from ${url}`, response.status);\n\t\t}\n\t}\n\n\tlet errorBody: { message?: string } = {};\n\ttry {\n\t\terrorBody = (await response.json()) as { message?: string };\n\t} catch {\n\t\t// ignore parse failure — use status text\n\t}\n\n\tconst message = errorBody.message ?? response.statusText;\n\n\tif (response.status === 404) {\n\t\tthrow new HyperserveNotFoundError(message);\n\t}\n\n\tif (response.status >= 400 && response.status < 500) {\n\t\tthrow new HyperserveValidationError(message, response.status, errorBody);\n\t}\n\n\tthrow new HyperserveApiError(message, response.status);\n}\n","/**\n * Normalizes the various accepted file input types into a { body, size } pair\n * suitable for use as a fetch/XHR request body.\n *\n * Size inference rules:\n * Blob / File → blob.size\n * Buffer → buffer.byteLength\n * ReadableStream → must be provided via fileSizeBytes\n */\nexport interface NormalizedFile {\n\tbody: Blob | ReadableStream;\n\tsize: number;\n}\n\nexport function normalizeFile(\n\tfile: Blob | Buffer | ReadableStream,\n\tfilename: string,\n\tfileSizeBytes?: number,\n): NormalizedFile {\n\tif (file instanceof ReadableStream) {\n\t\tif (fileSizeBytes === undefined) {\n\t\t\tthrow new TypeError(\n\t\t\t\t\"fileSizeBytes is required when file is a ReadableStream (size cannot be inferred)\",\n\t\t\t);\n\t\t}\n\t\treturn { body: file, size: fileSizeBytes };\n\t}\n\n\t// Node.js Buffer. `Buffer` is a Node global; all explicitly supported edge runtimes\n\t// (Cloudflare Workers, Vercel Edge) ship a Buffer compatibility layer, so this is safe\n\t// for the stated server targets. Pure browser or RN bundles never reach this branch\n\t// because normalize.ts is not imported by the browser or react-native entry points.\n\tif (Buffer.isBuffer(file)) {\n\t\tconst size = fileSizeBytes ?? file.byteLength;\n\t\t// Wrap in a Blob so fetch/XHR handle it uniformly\n\t\t// Copy into a plain ArrayBuffer to avoid SharedArrayBuffer assignability issues\n\t\tconst blob = new Blob([new Uint8Array(file)], { type: deriveTypeHint(filename) });\n\t\treturn { body: blob, size };\n\t}\n\n\t// Blob / File\n\tconst size = fileSizeBytes ?? file.size;\n\treturn { body: file, size };\n}\n\nfunction deriveTypeHint(filename: string): string {\n\t// Minimal hint — the actual Content-Type for the presigned PUT always\n\t// comes from the server, not from this inference. This is only used\n\t// so the Blob is constructed with a reasonable type attribute.\n\tconst ext = filename.slice(filename.lastIndexOf(\".\") + 1).toLowerCase();\n\tconst map: Record<string, string> = {\n\t\tmp4: \"video/mp4\",\n\t\tmov: \"video/quicktime\",\n\t\twebm: \"video/webm\",\n\t\tavi: \"video/x-msvideo\",\n\t\tmkv: \"video/x-matroska\",\n\t\tm4v: \"video/x-m4v\",\n\t};\n\treturn map[ext] ?? \"application/octet-stream\";\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","import { apiRequest } from \"./http.js\";\nimport { normalizeFile } from \"./normalize.js\";\nimport { putToStorage } from \"./storage.js\";\nimport type {\n\tCompleteUploadResult,\n\tCreateVideoOptions,\n\tCreateVideoResult,\n\tGetVideoOptions,\n\tHyperserveClientOptions,\n\tUploadVideoOptions,\n\tVideoResult,\n} from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.hyperserve.io/api\";\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\nexport class HyperserveClient {\n\tprivate readonly apiKey: string;\n\tprivate readonly baseUrl: string;\n\tprivate readonly timeoutMs: number;\n\tprivate readonly retries: number;\n\n\tconstructor(options: HyperserveClientOptions) {\n\t\tthis.apiKey = options.apiKey;\n\t\tthis.baseUrl = options.baseUrl?.replace(/\\/$/, \"\") ?? DEFAULT_BASE_URL;\n\t\tthis.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\t\tthis.retries = options.retries ?? 0;\n\t}\n\n\t/**\n\t * Creates a video record and returns a presigned upload URL.\n\t * Pass uploadUrl and contentType to your frontend so it can PUT the file directly to storage.\n\t * Call completeUpload once the frontend confirms the PUT is done.\n\t */\n\tasync createVideo(options: CreateVideoOptions): Promise<CreateVideoResult> {\n\t\treturn apiRequest<CreateVideoResult>({\n\t\t\tmethod: \"POST\",\n\t\t\turl: `${this.baseUrl}/video`,\n\t\t\tapiKey: this.apiKey,\n\t\t\ttimeoutMs: this.timeoutMs,\n\t\t\tretries: this.retries,\n\t\t\tbody: {\n\t\t\t\tfilename: options.filename,\n\t\t\t\tfileSizeBytes: options.fileSizeBytes,\n\t\t\t\tresolutions: options.resolutions,\n\t\t\t\tisPublic: options.isPublic,\n\t\t\t\t...(options.thumbnailTimestampsSeconds !== undefined && {\n\t\t\t\t\tthumbnail_timestamps_seconds: options.thumbnailTimestampsSeconds,\n\t\t\t\t}),\n\t\t\t\t...(options.customMetadata !== undefined && {\n\t\t\t\t\tcustom_user_metadata: options.customMetadata,\n\t\t\t\t}),\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Notifies Hyperserve that the file has been uploaded to the presigned URL.\n\t * Hyperserve verifies the object and queues transcoding.\n\t * Call this after your frontend confirms the storage PUT is complete.\n\t */\n\tasync completeUpload(videoId: string): Promise<CompleteUploadResult> {\n\t\treturn apiRequest<CompleteUploadResult>({\n\t\t\tmethod: \"POST\",\n\t\t\turl: `${this.baseUrl}/video/${videoId}/complete-upload`,\n\t\t\tapiKey: this.apiKey,\n\t\t\ttimeoutMs: this.timeoutMs,\n\t\t\tretries: this.retries,\n\t\t});\n\t}\n\n\t/**\n\t * Retrieves the current state of a video, including per-resolution status and playback URLs.\n\t *\n\t * @param videoId - The video ID returned by createVideo or uploadVideo.\n\t * @param options.private - Return time-limited signed URLs instead of public URLs.\n\t * @param options.expirationSeconds - Signed URL TTL when private is true. Defaults to 3600.\n\t */\n\tasync getVideo(videoId: string, options?: GetVideoOptions): Promise<VideoResult> {\n\t\tconst isPrivate = options?.private === true;\n\t\tconst expiration = options?.expirationSeconds ?? 3600;\n\n\t\tconst url = isPrivate\n\t\t\t? `${this.baseUrl}/video/${videoId}/private/${expiration}`\n\t\t\t: `${this.baseUrl}/video/${videoId}/public`;\n\n\t\treturn apiRequest<VideoResult>({\n\t\t\tmethod: \"GET\",\n\t\t\turl,\n\t\t\tapiKey: this.apiKey,\n\t\t\ttimeoutMs: this.timeoutMs,\n\t\t\tretries: this.retries,\n\t\t});\n\t}\n\n\t/**\n\t * Deletes a video and all associated resolutions and thumbnails.\n\t */\n\tasync deleteVideo(videoId: string): Promise<void> {\n\t\treturn apiRequest<void>({\n\t\t\tmethod: \"DELETE\",\n\t\t\turl: `${this.baseUrl}/video/${videoId}`,\n\t\t\tapiKey: this.apiKey,\n\t\t\ttimeoutMs: this.timeoutMs,\n\t\t\tretries: this.retries,\n\t\t});\n\t}\n\n\t/**\n\t * Deletes a single resolution for a video.\n\t */\n\tasync deleteResolution(resolutionId: string): Promise<void> {\n\t\treturn apiRequest<void>({\n\t\t\tmethod: \"DELETE\",\n\t\t\turl: `${this.baseUrl}/video/resolution/${resolutionId}`,\n\t\t\tapiKey: this.apiKey,\n\t\t\ttimeoutMs: this.timeoutMs,\n\t\t\tretries: this.retries,\n\t\t});\n\t}\n\n\t/**\n\t * Convenience method for server-side / script use cases.\n\t * Wraps createVideo, the storage PUT, and completeUpload into a single call.\n\t *\n\t * Not suitable for the browser proxy pattern — use createVideo + putVideoToStorage\n\t * from '@hyperserve/hyperserve-js/browser' + completeUpload separately for that flow.\n\t */\n\tasync uploadVideo(options: UploadVideoOptions): Promise<CompleteUploadResult> {\n\t\tconst { file, filename, resolutions, isPublic, thumbnailTimestampsSeconds, customMetadata } =\n\t\t\toptions;\n\n\t\tconst normalized = normalizeFile(file, filename, options.fileSizeBytes);\n\n\t\tconst upload = await this.createVideo({\n\t\t\tfilename,\n\t\t\tfileSizeBytes: normalized.size,\n\t\t\tresolutions,\n\t\t\tisPublic,\n\t\t\t...(thumbnailTimestampsSeconds !== undefined && { thumbnailTimestampsSeconds }),\n\t\t\t...(customMetadata !== undefined && { customMetadata }),\n\t\t});\n\n\t\tawait putToStorage(upload.uploadUrl, upload.contentType, normalized.body);\n\n\t\treturn this.completeUpload(upload.id);\n\t}\n}\n","import type { VerifyWebhookSignatureOptions } from \"./types.js\";\n\nconst DEFAULT_TOLERANCE_MS = 300_000; // 5 minutes — matches server-side enforcement\n\n/**\n * Verifies the x-hyperserve-signature header on an incoming webhook request.\n *\n * The signature header has the format \"{timestampMs}.{hmac-sha256-hex}\", where the HMAC\n * is computed over \"{timestampMs}.{rawBody}\" using your webhook signing secret. This proves\n * both when the request was sent (replay protection) and that the body was not tampered with\n * (integrity). Any modification to the body or timestamp will invalidate the signature.\n *\n * Returns true if the signature is valid and the timestamp is within the tolerance window.\n * Returns false if the signature is invalid, the timestamp has expired, or the header is malformed.\n *\n * Uses the Web Crypto API for a constant-time HMAC comparison — safe on Node 18+, Bun, Deno,\n * Cloudflare Workers, Vercel Edge, and all other supported server environments.\n *\n * IMPORTANT: pass the raw request body string exactly as received. Do not parse and re-serialize\n * JSON — any whitespace difference will invalidate the signature.\n *\n * @example\n * import { verifyWebhookSignature } from '@hyperserve/hyperserve-js';\n *\n * // Express (use express.raw, not express.json, so you get the raw body)\n * app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {\n * const isValid = await verifyWebhookSignature({\n * signature: req.headers['x-hyperserve-signature'] ?? '',\n * secret: process.env.HYPERSERVE_WEBHOOK_SECRET,\n * body: req.body.toString(),\n * });\n * if (!isValid) return res.status(401).end();\n * });\n *\n * // Next.js App Router\n * const body = await request.text();\n * const isValid = await verifyWebhookSignature({\n * signature: request.headers.get('x-hyperserve-signature') ?? '',\n * secret: process.env.HYPERSERVE_WEBHOOK_SECRET!,\n * body,\n * });\n */\nexport async function verifyWebhookSignature(\n\toptions: VerifyWebhookSignatureOptions,\n): Promise<boolean> {\n\tconst { signature, secret, body, toleranceMs = DEFAULT_TOLERANCE_MS } = options;\n\n\tconst dotIndex = signature.indexOf(\".\");\n\tif (dotIndex === -1) return false;\n\n\tconst timestampStr = signature.slice(0, dotIndex);\n\tconst receivedHex = signature.slice(dotIndex + 1);\n\n\t// Validate timestamp is a non-negative integer\n\tconst timestamp = Number(timestampStr);\n\tif (!Number.isInteger(timestamp) || timestamp < 0) return false;\n\n\t// Reject if timestamp is outside the tolerance window.\n\t// Math.abs handles future timestamps (clock skew or forged headers) — without it,\n\t// a negative difference would never exceed toleranceMs, accepting the signature forever.\n\tif (Math.abs(Date.now() - timestamp) > toleranceMs) return false;\n\n\tconst receivedBytes = hexToBytes(receivedHex);\n\tif (receivedBytes === null) return false;\n\n\tconst encoder = new TextEncoder();\n\tconst key = await crypto.subtle.importKey(\n\t\t\"raw\",\n\t\tencoder.encode(secret),\n\t\t{ name: \"HMAC\", hash: \"SHA-256\" },\n\t\tfalse,\n\t\t[\"verify\"],\n\t);\n\n\t// The signed message is \"{timestampMs}.{rawBody}\" — covers both freshness and integrity.\n\t// crypto.subtle.verify performs a constant-time comparison.\n\treturn crypto.subtle.verify(\n\t\t\"HMAC\",\n\t\tkey,\n\t\treceivedBytes,\n\t\tencoder.encode(`${timestampStr}.${body}`),\n\t);\n}\n\nfunction hexToBytes(hex: string): Uint8Array<ArrayBuffer> | null {\n\tif (hex.length === 0 || hex.length % 2 !== 0) return null;\n\tconst bytes = new Uint8Array(new ArrayBuffer(hex.length / 2));\n\tfor (let i = 0; i < hex.length; i += 2) {\n\t\tconst value = parseInt(hex.slice(i, i + 2), 16);\n\t\tif (Number.isNaN(value)) return null;\n\t\tbytes[i / 2] = value;\n\t}\n\treturn bytes;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/http.ts","../src/normalize.ts","../src/storage.ts","../src/client.ts","../src/webhook.ts"],"names":["size"],"mappings":"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","file":"index.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 {\n\tHyperserveApiError,\n\tHyperserveError,\n\tHyperserveNotFoundError,\n\tHyperserveTimeoutError,\n\tHyperserveValidationError,\n} from \"./errors.js\";\n\ninterface RequestOptions {\n\tmethod: \"GET\" | \"POST\" | \"DELETE\";\n\turl: string;\n\tapiKey: string;\n\ttimeoutMs: number;\n\tbody?: unknown;\n\tretries?: number;\n}\n\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction isRetryable(err: unknown): boolean {\n\t// Retry on 5xx API errors only — not 4xx, not timeouts\n\tif (err instanceof HyperserveApiError && err.statusCode !== undefined && err.statusCode >= 500)\n\t\treturn true;\n\t// Retry on network/infrastructure errors that aren't SDK-typed (e.g. TypeError: Failed to fetch)\n\tif (err instanceof Error && !(err instanceof HyperserveError)) return true;\n\treturn false;\n}\n\nexport async function apiRequest<T>(options: RequestOptions): Promise<T> {\n\tconst { retries = 0 } = options;\n\tlet attempt = 0;\n\n\twhile (true) {\n\t\ttry {\n\t\t\treturn await attemptRequest<T>(options);\n\t\t} catch (err) {\n\t\t\tif (attempt >= retries || !isRetryable(err)) {\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t\t// Full jitter: random delay up to min(10s, 100ms × 2^attempt)\n\t\t\tconst delay = Math.random() * Math.min(10_000, 100 * 2 ** attempt);\n\t\t\tawait sleep(delay);\n\t\t\tattempt++;\n\t\t}\n\t}\n}\n\nasync function attemptRequest<T>(options: RequestOptions): Promise<T> {\n\tconst { method, url, apiKey, timeoutMs, body } = options;\n\n\tconst controller = new AbortController();\n\tconst timer = setTimeout(() => controller.abort(), timeoutMs);\n\n\tlet response: Response;\n\n\ttry {\n\t\tresponse = await fetch(url, {\n\t\t\tmethod,\n\t\t\theaders: {\n\t\t\t\t\"X-API-KEY\": apiKey,\n\t\t\t\t...(body !== undefined ? { \"Content-Type\": \"application/json\" } : {}),\n\t\t\t},\n\t\t\t// Omit body entirely when not present — passing body: null on DELETE requests\n\t\t\t// can be treated differently by some proxies and intermediaries.\n\t\t\t...(body !== undefined ? { body: JSON.stringify(body) } : {}),\n\t\t\tsignal: controller.signal,\n\t\t});\n\t} catch (err) {\n\t\tif (err instanceof Error && err.name === \"AbortError\") {\n\t\t\tthrow new HyperserveTimeoutError(`Request to ${url} timed out after ${timeoutMs}ms`);\n\t\t}\n\t\tthrow err;\n\t} finally {\n\t\tclearTimeout(timer);\n\t}\n\n\tif (response.ok) {\n\t\t// 204 No Content\n\t\tif (response.status === 204) {\n\t\t\treturn undefined as T;\n\t\t}\n\t\ttry {\n\t\t\treturn (await response.json()) as T;\n\t\t} catch {\n\t\t\tthrow new HyperserveApiError(`Failed to parse response from ${url}`, response.status);\n\t\t}\n\t}\n\n\tlet errorBody: { message?: string } = {};\n\ttry {\n\t\terrorBody = (await response.json()) as { message?: string };\n\t} catch {\n\t\t// ignore parse failure — use status text\n\t}\n\n\tconst message = errorBody.message ?? response.statusText;\n\n\tif (response.status === 404) {\n\t\tthrow new HyperserveNotFoundError(message);\n\t}\n\n\tif (response.status >= 400 && response.status < 500) {\n\t\tthrow new HyperserveValidationError(message, response.status, errorBody);\n\t}\n\n\tthrow new HyperserveApiError(message, response.status);\n}\n","/**\n * Normalizes the various accepted file input types into a { body, size } pair\n * suitable for use as a fetch/XHR request body. The size is only consumed for a\n * ReadableStream body, where it becomes the Content-Length header on the storage\n * PUT — S3-compatible storage rejects a chunked PUT with 411. Blob bodies carry\n * their own length, so the size is ignored for them.\n *\n * Size inference rules:\n * Blob / File → blob.size\n * Buffer → buffer.byteLength\n * ReadableStream → must be provided via fileSizeBytes\n */\nexport interface NormalizedFile {\n\tbody: Blob | ReadableStream;\n\tsize: number;\n}\n\nexport function normalizeFile(\n\tfile: Blob | Buffer | ReadableStream,\n\tfilename: string,\n\tfileSizeBytes?: number,\n): NormalizedFile {\n\tif (file instanceof ReadableStream) {\n\t\tif (fileSizeBytes === undefined) {\n\t\t\tthrow new TypeError(\n\t\t\t\t\"fileSizeBytes is required when file is a ReadableStream (size cannot be inferred)\",\n\t\t\t);\n\t\t}\n\t\treturn { body: file, size: fileSizeBytes };\n\t}\n\n\t// Node.js Buffer. `Buffer` is a Node global; all explicitly supported edge runtimes\n\t// (Cloudflare Workers, Vercel Edge) ship a Buffer compatibility layer, so this is safe\n\t// for the stated server targets. Pure browser or RN bundles never reach this branch\n\t// because normalize.ts is not imported by the browser or react-native entry points.\n\tif (Buffer.isBuffer(file)) {\n\t\tconst size = fileSizeBytes ?? file.byteLength;\n\t\t// Wrap in a Blob so fetch/XHR handle it uniformly\n\t\t// Copy into a plain ArrayBuffer to avoid SharedArrayBuffer assignability issues\n\t\tconst blob = new Blob([new Uint8Array(file)], { type: deriveTypeHint(filename) });\n\t\treturn { body: blob, size };\n\t}\n\n\t// Blob / File\n\tconst size = fileSizeBytes ?? file.size;\n\treturn { body: file, size };\n}\n\nfunction deriveTypeHint(filename: string): string {\n\t// Minimal hint — the actual Content-Type for the presigned PUT always\n\t// comes from the server, not from this inference. This is only used\n\t// so the Blob is constructed with a reasonable type attribute.\n\tconst ext = filename.slice(filename.lastIndexOf(\".\") + 1).toLowerCase();\n\tconst map: Record<string, string> = {\n\t\tmp4: \"video/mp4\",\n\t\tmov: \"video/quicktime\",\n\t\twebm: \"video/webm\",\n\t\tavi: \"video/x-msvideo\",\n\t\tmkv: \"video/x-matroska\",\n\t\tm4v: \"video/x-m4v\",\n\t};\n\treturn map[ext] ?? \"application/octet-stream\";\n}\n","import { HyperserveTimeoutError, HyperserveUploadError } from \"./errors.js\";\n\nexport interface PutToStorageOptions {\n\t/**\n\t * Byte length of the body. Only used for ReadableStream bodies, which are sent\n\t * with chunked transfer encoding unless Content-Length is set explicitly — and\n\t * S3-compatible storage rejects a chunked PUT with 411 MissingContentLength.\n\t * Blob bodies carry their own length, so this is ignored for them.\n\t */\n\tcontentLength?: number;\n\tonProgress?: (percent: number) => void;\n}\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\toptions: PutToStorageOptions = {},\n): Promise<void> {\n\tconst { contentLength, onProgress } = options;\n\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, contentLength);\n}\n\nfunction putWithFetch(\n\tuploadUrl: string,\n\tcontentType: string,\n\tbody: Blob | ReadableStream,\n\tcontentLength?: number,\n): Promise<void> {\n\tconst isStream = body instanceof ReadableStream;\n\n\treturn fetch(uploadUrl, {\n\t\tmethod: \"PUT\",\n\t\theaders: {\n\t\t\t\"Content-Type\": contentType,\n\t\t\t// A stream body would otherwise go out chunked, which S3-compatible\n\t\t\t// storage rejects with 411. Blob bodies get Content-Length from fetch.\n\t\t\t...(isStream && contentLength !== undefined\n\t\t\t\t? { \"Content-Length\": String(contentLength) }\n\t\t\t\t: {}),\n\t\t},\n\t\t// duplex is required for ReadableStream bodies in some runtimes (Node 18)\n\t\t...(isStream ? { 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","import { apiRequest } from \"./http.js\";\nimport { normalizeFile } from \"./normalize.js\";\nimport { putToStorage } from \"./storage.js\";\nimport type {\n\tCompleteUploadResult,\n\tCreateVideoOptions,\n\tCreateVideoResult,\n\tGetVideoOptions,\n\tHyperserveClientOptions,\n\tUploadVideoOptions,\n\tVideoResult,\n} from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.hyperserve.io/api\";\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\nexport class HyperserveClient {\n\tprivate readonly apiKey: string;\n\tprivate readonly baseUrl: string;\n\tprivate readonly timeoutMs: number;\n\tprivate readonly retries: number;\n\n\tconstructor(options: HyperserveClientOptions) {\n\t\tthis.apiKey = options.apiKey;\n\t\tthis.baseUrl = options.baseUrl?.replace(/\\/$/, \"\") ?? DEFAULT_BASE_URL;\n\t\tthis.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n\t\tthis.retries = options.retries ?? 0;\n\t}\n\n\t/**\n\t * Creates a video record and returns a presigned upload URL.\n\t * Pass uploadUrl and contentType to your frontend so it can PUT the file directly to storage.\n\t * Call completeUpload once the frontend confirms the PUT is done.\n\t */\n\tasync createVideo(options: CreateVideoOptions): Promise<CreateVideoResult> {\n\t\treturn apiRequest<CreateVideoResult>({\n\t\t\tmethod: \"POST\",\n\t\t\turl: `${this.baseUrl}/video`,\n\t\t\tapiKey: this.apiKey,\n\t\t\ttimeoutMs: this.timeoutMs,\n\t\t\tretries: this.retries,\n\t\t\tbody: {\n\t\t\t\tfilename: options.filename,\n\t\t\t\tresolutions: options.resolutions,\n\t\t\t\tisPublic: options.isPublic,\n\t\t\t\t...(options.thumbnailTimestampsSeconds !== undefined && {\n\t\t\t\t\tthumbnailTimestampsSeconds: options.thumbnailTimestampsSeconds,\n\t\t\t\t}),\n\t\t\t\t...(options.customMetadata !== undefined && {\n\t\t\t\t\tcustomMetadata: options.customMetadata,\n\t\t\t\t}),\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Notifies Hyperserve that the file has been uploaded to the presigned URL.\n\t * Hyperserve verifies the object and queues transcoding.\n\t * Call this after your frontend confirms the storage PUT is complete.\n\t */\n\tasync completeUpload(videoId: string): Promise<CompleteUploadResult> {\n\t\treturn apiRequest<CompleteUploadResult>({\n\t\t\tmethod: \"POST\",\n\t\t\turl: `${this.baseUrl}/video/${videoId}/complete-upload`,\n\t\t\tapiKey: this.apiKey,\n\t\t\ttimeoutMs: this.timeoutMs,\n\t\t\tretries: this.retries,\n\t\t});\n\t}\n\n\t/**\n\t * Retrieves the current state of a video, including per-resolution status and playback URLs.\n\t *\n\t * @param videoId - The video ID returned by createVideo or uploadVideo.\n\t * @param options.private - Return time-limited signed URLs instead of public URLs.\n\t * @param options.expirationSeconds - Signed URL TTL when private is true. Defaults to 3600.\n\t */\n\tasync getVideo(videoId: string, options?: GetVideoOptions): Promise<VideoResult> {\n\t\tconst isPrivate = options?.private === true;\n\t\tconst expiration = options?.expirationSeconds ?? 3600;\n\n\t\tconst url = isPrivate\n\t\t\t? `${this.baseUrl}/video/${videoId}/private/${expiration}`\n\t\t\t: `${this.baseUrl}/video/${videoId}/public`;\n\n\t\treturn apiRequest<VideoResult>({\n\t\t\tmethod: \"GET\",\n\t\t\turl,\n\t\t\tapiKey: this.apiKey,\n\t\t\ttimeoutMs: this.timeoutMs,\n\t\t\tretries: this.retries,\n\t\t});\n\t}\n\n\t/**\n\t * Deletes a video and all associated resolutions and thumbnails.\n\t */\n\tasync deleteVideo(videoId: string): Promise<void> {\n\t\treturn apiRequest<void>({\n\t\t\tmethod: \"DELETE\",\n\t\t\turl: `${this.baseUrl}/video/${videoId}`,\n\t\t\tapiKey: this.apiKey,\n\t\t\ttimeoutMs: this.timeoutMs,\n\t\t\tretries: this.retries,\n\t\t});\n\t}\n\n\t/**\n\t * Deletes a single resolution for a video.\n\t */\n\tasync deleteResolution(resolutionId: string): Promise<void> {\n\t\treturn apiRequest<void>({\n\t\t\tmethod: \"DELETE\",\n\t\t\turl: `${this.baseUrl}/video/resolution/${resolutionId}`,\n\t\t\tapiKey: this.apiKey,\n\t\t\ttimeoutMs: this.timeoutMs,\n\t\t\tretries: this.retries,\n\t\t});\n\t}\n\n\t/**\n\t * Convenience method for server-side / script use cases.\n\t * Wraps createVideo, the storage PUT, and completeUpload into a single call.\n\t *\n\t * Not suitable for the browser proxy pattern — use createVideo + putVideoToStorage\n\t * from '@hyperserve/hyperserve-js/browser' + completeUpload separately for that flow.\n\t */\n\tasync uploadVideo(options: UploadVideoOptions): Promise<CompleteUploadResult> {\n\t\tconst { file, filename, resolutions, isPublic, thumbnailTimestampsSeconds, customMetadata } =\n\t\t\toptions;\n\n\t\tconst normalized = normalizeFile(file, filename, options.fileSizeBytes);\n\n\t\tconst upload = await this.createVideo({\n\t\t\tfilename,\n\t\t\tresolutions,\n\t\t\tisPublic,\n\t\t\t...(thumbnailTimestampsSeconds !== undefined && { thumbnailTimestampsSeconds }),\n\t\t\t...(customMetadata !== undefined && { customMetadata }),\n\t\t});\n\n\t\tawait putToStorage(upload.uploadUrl, upload.contentType, normalized.body, {\n\t\t\tcontentLength: normalized.size,\n\t\t});\n\n\t\treturn this.completeUpload(upload.id);\n\t}\n}\n","import type { VerifyWebhookSignatureOptions } from \"./types.js\";\n\nconst DEFAULT_TOLERANCE_MS = 300_000; // 5 minutes — matches server-side enforcement\n\n/**\n * Verifies the x-hyperserve-signature header on an incoming webhook request.\n *\n * The signature header has the format \"{timestampMs}.{hmac-sha256-hex}\", where the HMAC\n * is computed over \"{timestampMs}.{rawBody}\" using your webhook signing secret. This proves\n * both when the request was sent (replay protection) and that the body was not tampered with\n * (integrity). Any modification to the body or timestamp will invalidate the signature.\n *\n * Returns true if the signature is valid and the timestamp is within the tolerance window.\n * Returns false if the signature is invalid, the timestamp has expired, or the header is malformed.\n *\n * Uses the Web Crypto API for a constant-time HMAC comparison — safe on Node 18+, Bun, Deno,\n * Cloudflare Workers, Vercel Edge, and all other supported server environments.\n *\n * IMPORTANT: pass the raw request body string exactly as received. Do not parse and re-serialize\n * JSON — any whitespace difference will invalidate the signature.\n *\n * @example\n * import { verifyWebhookSignature } from '@hyperserve/hyperserve-js';\n *\n * // Express (use express.raw, not express.json, so you get the raw body)\n * app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {\n * const isValid = await verifyWebhookSignature({\n * signature: req.headers['x-hyperserve-signature'] ?? '',\n * secret: process.env.HYPERSERVE_WEBHOOK_SECRET,\n * body: req.body.toString(),\n * });\n * if (!isValid) return res.status(401).end();\n * });\n *\n * // Next.js App Router\n * const body = await request.text();\n * const isValid = await verifyWebhookSignature({\n * signature: request.headers.get('x-hyperserve-signature') ?? '',\n * secret: process.env.HYPERSERVE_WEBHOOK_SECRET!,\n * body,\n * });\n */\nexport async function verifyWebhookSignature(\n\toptions: VerifyWebhookSignatureOptions,\n): Promise<boolean> {\n\tconst { signature, secret, body, toleranceMs = DEFAULT_TOLERANCE_MS } = options;\n\n\tconst dotIndex = signature.indexOf(\".\");\n\tif (dotIndex === -1) return false;\n\n\tconst timestampStr = signature.slice(0, dotIndex);\n\tconst receivedHex = signature.slice(dotIndex + 1);\n\n\t// Validate timestamp is a non-negative integer\n\tconst timestamp = Number(timestampStr);\n\tif (!Number.isInteger(timestamp) || timestamp < 0) return false;\n\n\t// Reject if timestamp is outside the tolerance window.\n\t// Math.abs handles future timestamps (clock skew or forged headers) — without it,\n\t// a negative difference would never exceed toleranceMs, accepting the signature forever.\n\tif (Math.abs(Date.now() - timestamp) > toleranceMs) return false;\n\n\tconst receivedBytes = hexToBytes(receivedHex);\n\tif (receivedBytes === null) return false;\n\n\tconst encoder = new TextEncoder();\n\tconst key = await crypto.subtle.importKey(\n\t\t\"raw\",\n\t\tencoder.encode(secret),\n\t\t{ name: \"HMAC\", hash: \"SHA-256\" },\n\t\tfalse,\n\t\t[\"verify\"],\n\t);\n\n\t// The signed message is \"{timestampMs}.{rawBody}\" — covers both freshness and integrity.\n\t// crypto.subtle.verify performs a constant-time comparison.\n\treturn crypto.subtle.verify(\n\t\t\"HMAC\",\n\t\tkey,\n\t\treceivedBytes,\n\t\tencoder.encode(`${timestampStr}.${body}`),\n\t);\n}\n\nfunction hexToBytes(hex: string): Uint8Array<ArrayBuffer> | null {\n\tif (hex.length === 0 || hex.length % 2 !== 0) return null;\n\tconst bytes = new Uint8Array(new ArrayBuffer(hex.length / 2));\n\tfor (let i = 0; i < hex.length; i += 2) {\n\t\tconst value = parseInt(hex.slice(i, i + 2), 16);\n\t\tif (Number.isNaN(value)) return null;\n\t\tbytes[i / 2] = value;\n\t}\n\treturn bytes;\n}\n"]}
|
package/dist/react-native.cjs
CHANGED
|
@@ -26,18 +26,25 @@ var HyperserveTimeoutError = class extends HyperserveError {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
// src/storage.ts
|
|
29
|
-
async function putToStorage(uploadUrl, contentType, body,
|
|
29
|
+
async function putToStorage(uploadUrl, contentType, body, options = {}) {
|
|
30
|
+
const { contentLength, onProgress } = options;
|
|
30
31
|
if (onProgress !== void 0 && typeof XMLHttpRequest !== "undefined" && !(body instanceof ReadableStream)) {
|
|
31
32
|
return putWithXhr(uploadUrl, contentType, body, onProgress);
|
|
32
33
|
}
|
|
33
|
-
return putWithFetch(uploadUrl, contentType, body);
|
|
34
|
+
return putWithFetch(uploadUrl, contentType, body, contentLength);
|
|
34
35
|
}
|
|
35
|
-
function putWithFetch(uploadUrl, contentType, body) {
|
|
36
|
+
function putWithFetch(uploadUrl, contentType, body, contentLength) {
|
|
37
|
+
const isStream = body instanceof ReadableStream;
|
|
36
38
|
return fetch(uploadUrl, {
|
|
37
39
|
method: "PUT",
|
|
38
|
-
headers: {
|
|
40
|
+
headers: {
|
|
41
|
+
"Content-Type": contentType,
|
|
42
|
+
// A stream body would otherwise go out chunked, which S3-compatible
|
|
43
|
+
// storage rejects with 411. Blob bodies get Content-Length from fetch.
|
|
44
|
+
...isStream && contentLength !== void 0 ? { "Content-Length": String(contentLength) } : {}
|
|
45
|
+
},
|
|
39
46
|
// duplex is required for ReadableStream bodies in some runtimes (Node 18)
|
|
40
|
-
...
|
|
47
|
+
...isStream ? { duplex: "half" } : {},
|
|
41
48
|
body
|
|
42
49
|
}).then((response) => {
|
|
43
50
|
if (!response.ok) {
|
|
@@ -86,7 +93,9 @@ async function putVideoToStorage(options) {
|
|
|
86
93
|
throw new HyperserveUploadError(`Failed to read local file: ${uri}`);
|
|
87
94
|
}
|
|
88
95
|
const blob = await localResponse.blob();
|
|
89
|
-
return putToStorage(uploadUrl, contentType, blob,
|
|
96
|
+
return putToStorage(uploadUrl, contentType, blob, {
|
|
97
|
+
...onProgress !== void 0 && { onProgress }
|
|
98
|
+
});
|
|
90
99
|
}
|
|
91
100
|
|
|
92
101
|
exports.HyperserveError = HyperserveError;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/storage.ts","../src/react-native.ts"],"names":[],"mappings":"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","file":"react-native.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 * React Native utilities for the Hyperserve SDK.\n *\n * Import from '@hyperserve/hyperserve-js/react-native' — this entry point contains no\n * API key logic and is safe to bundle into your React Native app.\n *\n * Usage:\n * import { putVideoToStorage } from '@hyperserve/hyperserve-js/react-native';\n *\n * // uri comes from expo-image-picker, react-native-image-picker, etc.\n * await putVideoToStorage({ uploadUrl, contentType, uri: asset.uri, onProgress });\n *\n * Note: your backend (not this utility) should hold the API key and call\n * createVideo / completeUpload on your app's behalf.\n */\n\nimport { HyperserveError, HyperserveTimeoutError, HyperserveUploadError } from \"./errors.js\";\n\nexport type { PutVideoToStorageRNOptions, VideoResolution, VideoStatus } from \"./types.js\";\nexport { HyperserveError, HyperserveTimeoutError, HyperserveUploadError };\n\nimport { putToStorage } from \"./storage.js\";\nimport type { PutVideoToStorageRNOptions } from \"./types.js\";\n\n/**\n * PUT a video file to the presigned storage URL obtained from your backend.\n * Accepts a local file URI from a React Native video/image picker.\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('https://your-api.com/create-upload', {\n * method: 'POST',\n * body: JSON.stringify({ filename: asset.fileName, fileSizeBytes: asset.fileSize }),\n * }).then(r => r.json());\n *\n * await putVideoToStorage({ uploadUrl, contentType, uri: asset.uri, onProgress: (p) => setProgress(p) });\n *\n * await fetch('https://your-api.com/complete-upload', {\n * method: 'POST',\n * body: JSON.stringify({ videoId }),\n * });\n */\nexport async function putVideoToStorage(options: PutVideoToStorageRNOptions): Promise<void> {\n\tconst { uploadUrl, contentType, uri, onProgress } = options;\n\n\t// Fetch the local file URI to obtain a Blob. React Native's fetch implementation\n\t// supports file:// URIs, allowing us to read local files from the device.\n\tconst localResponse = await fetch(uri);\n\tif (!localResponse.ok) {\n\t\tthrow new HyperserveUploadError(`Failed to read local file: ${uri}`);\n\t}\n\tconst blob = await localResponse.blob();\n\n\treturn putToStorage(uploadUrl, contentType, blob, onProgress);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/storage.ts","../src/react-native.ts"],"names":[],"mappings":"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","file":"react-native.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\nexport interface PutToStorageOptions {\n\t/**\n\t * Byte length of the body. Only used for ReadableStream bodies, which are sent\n\t * with chunked transfer encoding unless Content-Length is set explicitly — and\n\t * S3-compatible storage rejects a chunked PUT with 411 MissingContentLength.\n\t * Blob bodies carry their own length, so this is ignored for them.\n\t */\n\tcontentLength?: number;\n\tonProgress?: (percent: number) => void;\n}\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\toptions: PutToStorageOptions = {},\n): Promise<void> {\n\tconst { contentLength, onProgress } = options;\n\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, contentLength);\n}\n\nfunction putWithFetch(\n\tuploadUrl: string,\n\tcontentType: string,\n\tbody: Blob | ReadableStream,\n\tcontentLength?: number,\n): Promise<void> {\n\tconst isStream = body instanceof ReadableStream;\n\n\treturn fetch(uploadUrl, {\n\t\tmethod: \"PUT\",\n\t\theaders: {\n\t\t\t\"Content-Type\": contentType,\n\t\t\t// A stream body would otherwise go out chunked, which S3-compatible\n\t\t\t// storage rejects with 411. Blob bodies get Content-Length from fetch.\n\t\t\t...(isStream && contentLength !== undefined\n\t\t\t\t? { \"Content-Length\": String(contentLength) }\n\t\t\t\t: {}),\n\t\t},\n\t\t// duplex is required for ReadableStream bodies in some runtimes (Node 18)\n\t\t...(isStream ? { 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 * React Native utilities for the Hyperserve SDK.\n *\n * Import from '@hyperserve/hyperserve-js/react-native' — this entry point contains no\n * API key logic and is safe to bundle into your React Native app.\n *\n * Usage:\n * import { putVideoToStorage } from '@hyperserve/hyperserve-js/react-native';\n *\n * // uri comes from expo-image-picker, react-native-image-picker, etc.\n * await putVideoToStorage({ uploadUrl, contentType, uri: asset.uri, onProgress });\n *\n * Note: your backend (not this utility) should hold the API key and call\n * createVideo / completeUpload on your app's behalf.\n */\n\nimport { HyperserveError, HyperserveTimeoutError, HyperserveUploadError } from \"./errors.js\";\n\nexport type { PutVideoToStorageRNOptions, VideoResolution, VideoStatus } from \"./types.js\";\nexport { HyperserveError, HyperserveTimeoutError, HyperserveUploadError };\n\nimport { putToStorage } from \"./storage.js\";\nimport type { PutVideoToStorageRNOptions } from \"./types.js\";\n\n/**\n * PUT a video file to the presigned storage URL obtained from your backend.\n * Accepts a local file URI from a React Native video/image picker.\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('https://your-api.com/create-upload', {\n * method: 'POST',\n * body: JSON.stringify({ filename: asset.fileName }),\n * }).then(r => r.json());\n *\n * await putVideoToStorage({ uploadUrl, contentType, uri: asset.uri, onProgress: (p) => setProgress(p) });\n *\n * await fetch('https://your-api.com/complete-upload', {\n * method: 'POST',\n * body: JSON.stringify({ videoId }),\n * });\n */\nexport async function putVideoToStorage(options: PutVideoToStorageRNOptions): Promise<void> {\n\tconst { uploadUrl, contentType, uri, onProgress } = options;\n\n\t// Fetch the local file URI to obtain a Blob. React Native's fetch implementation\n\t// supports file:// URIs, allowing us to read local files from the device.\n\tconst localResponse = await fetch(uri);\n\tif (!localResponse.ok) {\n\t\tthrow new HyperserveUploadError(`Failed to read local file: ${uri}`);\n\t}\n\tconst blob = await localResponse.blob();\n\n\treturn putToStorage(uploadUrl, contentType, blob, {\n\t\t...(onProgress !== undefined && { onProgress }),\n\t});\n}\n"]}
|
package/dist/react-native.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { j as PutVideoToStorageRNOptions } from './errors-
|
|
2
|
-
export { e as HyperserveError, g as HyperserveTimeoutError, h as HyperserveUploadError, k as VideoResolution, m as VideoStatus } from './errors-
|
|
1
|
+
import { j as PutVideoToStorageRNOptions } from './errors-DJGPqrI5.cjs';
|
|
2
|
+
export { e as HyperserveError, g as HyperserveTimeoutError, h as HyperserveUploadError, k as VideoResolution, m as VideoStatus } from './errors-DJGPqrI5.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* React Native utilities for the Hyperserve SDK.
|
|
@@ -25,7 +25,7 @@ export { e as HyperserveError, g as HyperserveTimeoutError, h as HyperserveUploa
|
|
|
25
25
|
* @example
|
|
26
26
|
* const { uploadUrl, contentType } = await fetch('https://your-api.com/create-upload', {
|
|
27
27
|
* method: 'POST',
|
|
28
|
-
* body: JSON.stringify({ filename: asset.fileName
|
|
28
|
+
* body: JSON.stringify({ filename: asset.fileName }),
|
|
29
29
|
* }).then(r => r.json());
|
|
30
30
|
*
|
|
31
31
|
* await putVideoToStorage({ uploadUrl, contentType, uri: asset.uri, onProgress: (p) => setProgress(p) });
|
package/dist/react-native.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { j as PutVideoToStorageRNOptions } from './errors-
|
|
2
|
-
export { e as HyperserveError, g as HyperserveTimeoutError, h as HyperserveUploadError, k as VideoResolution, m as VideoStatus } from './errors-
|
|
1
|
+
import { j as PutVideoToStorageRNOptions } from './errors-DJGPqrI5.js';
|
|
2
|
+
export { e as HyperserveError, g as HyperserveTimeoutError, h as HyperserveUploadError, k as VideoResolution, m as VideoStatus } from './errors-DJGPqrI5.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* React Native utilities for the Hyperserve SDK.
|
|
@@ -25,7 +25,7 @@ export { e as HyperserveError, g as HyperserveTimeoutError, h as HyperserveUploa
|
|
|
25
25
|
* @example
|
|
26
26
|
* const { uploadUrl, contentType } = await fetch('https://your-api.com/create-upload', {
|
|
27
27
|
* method: 'POST',
|
|
28
|
-
* body: JSON.stringify({ filename: asset.fileName
|
|
28
|
+
* body: JSON.stringify({ filename: asset.fileName }),
|
|
29
29
|
* }).then(r => r.json());
|
|
30
30
|
*
|
|
31
31
|
* await putVideoToStorage({ uploadUrl, contentType, uri: asset.uri, onProgress: (p) => setProgress(p) });
|
package/dist/react-native.js
CHANGED
|
@@ -24,18 +24,25 @@ var HyperserveTimeoutError = class extends HyperserveError {
|
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
// src/storage.ts
|
|
27
|
-
async function putToStorage(uploadUrl, contentType, body,
|
|
27
|
+
async function putToStorage(uploadUrl, contentType, body, options = {}) {
|
|
28
|
+
const { contentLength, onProgress } = options;
|
|
28
29
|
if (onProgress !== void 0 && typeof XMLHttpRequest !== "undefined" && !(body instanceof ReadableStream)) {
|
|
29
30
|
return putWithXhr(uploadUrl, contentType, body, onProgress);
|
|
30
31
|
}
|
|
31
|
-
return putWithFetch(uploadUrl, contentType, body);
|
|
32
|
+
return putWithFetch(uploadUrl, contentType, body, contentLength);
|
|
32
33
|
}
|
|
33
|
-
function putWithFetch(uploadUrl, contentType, body) {
|
|
34
|
+
function putWithFetch(uploadUrl, contentType, body, contentLength) {
|
|
35
|
+
const isStream = body instanceof ReadableStream;
|
|
34
36
|
return fetch(uploadUrl, {
|
|
35
37
|
method: "PUT",
|
|
36
|
-
headers: {
|
|
38
|
+
headers: {
|
|
39
|
+
"Content-Type": contentType,
|
|
40
|
+
// A stream body would otherwise go out chunked, which S3-compatible
|
|
41
|
+
// storage rejects with 411. Blob bodies get Content-Length from fetch.
|
|
42
|
+
...isStream && contentLength !== void 0 ? { "Content-Length": String(contentLength) } : {}
|
|
43
|
+
},
|
|
37
44
|
// duplex is required for ReadableStream bodies in some runtimes (Node 18)
|
|
38
|
-
...
|
|
45
|
+
...isStream ? { duplex: "half" } : {},
|
|
39
46
|
body
|
|
40
47
|
}).then((response) => {
|
|
41
48
|
if (!response.ok) {
|
|
@@ -84,7 +91,9 @@ async function putVideoToStorage(options) {
|
|
|
84
91
|
throw new HyperserveUploadError(`Failed to read local file: ${uri}`);
|
|
85
92
|
}
|
|
86
93
|
const blob = await localResponse.blob();
|
|
87
|
-
return putToStorage(uploadUrl, contentType, blob,
|
|
94
|
+
return putToStorage(uploadUrl, contentType, blob, {
|
|
95
|
+
...onProgress !== void 0 && { onProgress }
|
|
96
|
+
});
|
|
88
97
|
}
|
|
89
98
|
|
|
90
99
|
export { HyperserveError, HyperserveTimeoutError, HyperserveUploadError, putVideoToStorage };
|
package/dist/react-native.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/storage.ts","../src/react-native.ts"],"names":[],"mappings":"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","file":"react-native.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 * React Native utilities for the Hyperserve SDK.\n *\n * Import from '@hyperserve/hyperserve-js/react-native' — this entry point contains no\n * API key logic and is safe to bundle into your React Native app.\n *\n * Usage:\n * import { putVideoToStorage } from '@hyperserve/hyperserve-js/react-native';\n *\n * // uri comes from expo-image-picker, react-native-image-picker, etc.\n * await putVideoToStorage({ uploadUrl, contentType, uri: asset.uri, onProgress });\n *\n * Note: your backend (not this utility) should hold the API key and call\n * createVideo / completeUpload on your app's behalf.\n */\n\nimport { HyperserveError, HyperserveTimeoutError, HyperserveUploadError } from \"./errors.js\";\n\nexport type { PutVideoToStorageRNOptions, VideoResolution, VideoStatus } from \"./types.js\";\nexport { HyperserveError, HyperserveTimeoutError, HyperserveUploadError };\n\nimport { putToStorage } from \"./storage.js\";\nimport type { PutVideoToStorageRNOptions } from \"./types.js\";\n\n/**\n * PUT a video file to the presigned storage URL obtained from your backend.\n * Accepts a local file URI from a React Native video/image picker.\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('https://your-api.com/create-upload', {\n * method: 'POST',\n * body: JSON.stringify({ filename: asset.fileName, fileSizeBytes: asset.fileSize }),\n * }).then(r => r.json());\n *\n * await putVideoToStorage({ uploadUrl, contentType, uri: asset.uri, onProgress: (p) => setProgress(p) });\n *\n * await fetch('https://your-api.com/complete-upload', {\n * method: 'POST',\n * body: JSON.stringify({ videoId }),\n * });\n */\nexport async function putVideoToStorage(options: PutVideoToStorageRNOptions): Promise<void> {\n\tconst { uploadUrl, contentType, uri, onProgress } = options;\n\n\t// Fetch the local file URI to obtain a Blob. React Native's fetch implementation\n\t// supports file:// URIs, allowing us to read local files from the device.\n\tconst localResponse = await fetch(uri);\n\tif (!localResponse.ok) {\n\t\tthrow new HyperserveUploadError(`Failed to read local file: ${uri}`);\n\t}\n\tconst blob = await localResponse.blob();\n\n\treturn putToStorage(uploadUrl, contentType, blob, onProgress);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/storage.ts","../src/react-native.ts"],"names":[],"mappings":"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","file":"react-native.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\nexport interface PutToStorageOptions {\n\t/**\n\t * Byte length of the body. Only used for ReadableStream bodies, which are sent\n\t * with chunked transfer encoding unless Content-Length is set explicitly — and\n\t * S3-compatible storage rejects a chunked PUT with 411 MissingContentLength.\n\t * Blob bodies carry their own length, so this is ignored for them.\n\t */\n\tcontentLength?: number;\n\tonProgress?: (percent: number) => void;\n}\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\toptions: PutToStorageOptions = {},\n): Promise<void> {\n\tconst { contentLength, onProgress } = options;\n\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, contentLength);\n}\n\nfunction putWithFetch(\n\tuploadUrl: string,\n\tcontentType: string,\n\tbody: Blob | ReadableStream,\n\tcontentLength?: number,\n): Promise<void> {\n\tconst isStream = body instanceof ReadableStream;\n\n\treturn fetch(uploadUrl, {\n\t\tmethod: \"PUT\",\n\t\theaders: {\n\t\t\t\"Content-Type\": contentType,\n\t\t\t// A stream body would otherwise go out chunked, which S3-compatible\n\t\t\t// storage rejects with 411. Blob bodies get Content-Length from fetch.\n\t\t\t...(isStream && contentLength !== undefined\n\t\t\t\t? { \"Content-Length\": String(contentLength) }\n\t\t\t\t: {}),\n\t\t},\n\t\t// duplex is required for ReadableStream bodies in some runtimes (Node 18)\n\t\t...(isStream ? { 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 * React Native utilities for the Hyperserve SDK.\n *\n * Import from '@hyperserve/hyperserve-js/react-native' — this entry point contains no\n * API key logic and is safe to bundle into your React Native app.\n *\n * Usage:\n * import { putVideoToStorage } from '@hyperserve/hyperserve-js/react-native';\n *\n * // uri comes from expo-image-picker, react-native-image-picker, etc.\n * await putVideoToStorage({ uploadUrl, contentType, uri: asset.uri, onProgress });\n *\n * Note: your backend (not this utility) should hold the API key and call\n * createVideo / completeUpload on your app's behalf.\n */\n\nimport { HyperserveError, HyperserveTimeoutError, HyperserveUploadError } from \"./errors.js\";\n\nexport type { PutVideoToStorageRNOptions, VideoResolution, VideoStatus } from \"./types.js\";\nexport { HyperserveError, HyperserveTimeoutError, HyperserveUploadError };\n\nimport { putToStorage } from \"./storage.js\";\nimport type { PutVideoToStorageRNOptions } from \"./types.js\";\n\n/**\n * PUT a video file to the presigned storage URL obtained from your backend.\n * Accepts a local file URI from a React Native video/image picker.\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('https://your-api.com/create-upload', {\n * method: 'POST',\n * body: JSON.stringify({ filename: asset.fileName }),\n * }).then(r => r.json());\n *\n * await putVideoToStorage({ uploadUrl, contentType, uri: asset.uri, onProgress: (p) => setProgress(p) });\n *\n * await fetch('https://your-api.com/complete-upload', {\n * method: 'POST',\n * body: JSON.stringify({ videoId }),\n * });\n */\nexport async function putVideoToStorage(options: PutVideoToStorageRNOptions): Promise<void> {\n\tconst { uploadUrl, contentType, uri, onProgress } = options;\n\n\t// Fetch the local file URI to obtain a Blob. React Native's fetch implementation\n\t// supports file:// URIs, allowing us to read local files from the device.\n\tconst localResponse = await fetch(uri);\n\tif (!localResponse.ok) {\n\t\tthrow new HyperserveUploadError(`Failed to read local file: ${uri}`);\n\t}\n\tconst blob = await localResponse.blob();\n\n\treturn putToStorage(uploadUrl, contentType, blob, {\n\t\t...(onProgress !== undefined && { onProgress }),\n\t});\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperserve/hyperserve-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "TypeScript SDK for the Hyperserve video infrastructure API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ryan Trann <admin@hyperserve.io>",
|
|
7
|
-
"homepage": "https://hyperserve.io",
|
|
7
|
+
"homepage": "https://hyperserve.io?utm_source=npm&utm_medium=package_page&utm_campaign=hyperserve-js",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "https://github.com/hyper-serve/hyperserve-js"
|