@devultur/self-host 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +235 -0
- package/README.md +66 -2
- package/dist/chunk-D3KOHAVH.js +140 -0
- package/dist/chunk-VF4KBW3I.js +459 -0
- package/dist/cloudflare-kZ7J3LQ2.d.ts +19 -0
- package/dist/codecs-RKUOPKWR.js +198 -0
- package/dist/index.d.ts +530 -0
- package/dist/index.js +4008 -0
- package/dist/transcode/container.d.ts +10 -0
- package/dist/transcode/container.js +23 -0
- package/dist/wasm-modules-55Q2QP7Q.js +22 -0
- package/package.json +52 -3
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
import * as hono_types from 'hono/types';
|
|
2
|
+
import { Context, MiddlewareHandler, Hono } from 'hono';
|
|
3
|
+
import { Visibility, SecurityPolicy, CacheRule, TranscodeSettings, TranscodeOutputKeys, TranscodeOutputUrls } from '@devultur/core';
|
|
4
|
+
import { T as TranscodeContainerNamespace } from './cloudflare-kZ7J3LQ2.js';
|
|
5
|
+
|
|
6
|
+
/** What an upload token allows (claims `res`, `visibility`, `max_bytes`, `max_files`, `file_ttl`). */
|
|
7
|
+
interface UploadGrant {
|
|
8
|
+
/** Token's `jti`: best-effort counter for `maxFiles`. */
|
|
9
|
+
tokenId: string;
|
|
10
|
+
/** Prefix relative to the storage prefix, without visibility, with a trailing `/` (`asset/exports/inst-1/`). */
|
|
11
|
+
pathPrefix: string;
|
|
12
|
+
visibility: Visibility;
|
|
13
|
+
maxBytes?: number;
|
|
14
|
+
maxFiles?: number;
|
|
15
|
+
/** TTL (seconds) of each file uploaded with the token. */
|
|
16
|
+
fileExpiresIn?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** A download link as the Worker hands it to plugins. */
|
|
20
|
+
interface DownloadLinkInfo {
|
|
21
|
+
/** Storage prefix the link was issued under. */
|
|
22
|
+
storagePrefix: string;
|
|
23
|
+
/** Key of the file (`{visibility}/…`), relative to the storage prefix. */
|
|
24
|
+
key: string;
|
|
25
|
+
/** Plugin context of the request that issued the link, as it was stored. */
|
|
26
|
+
context?: PluginContext;
|
|
27
|
+
}
|
|
28
|
+
/** The transcode job a runner callback belongs to (from the job ticket the Worker signed at launch). */
|
|
29
|
+
interface TranscodeJob {
|
|
30
|
+
/** Unique per launch: a re-encode of the same video is another run. */
|
|
31
|
+
runId: string;
|
|
32
|
+
videoId: string;
|
|
33
|
+
/** Launch time (epoch ms, second precision). */
|
|
34
|
+
startedAt: number;
|
|
35
|
+
/** Plugin context of the request that launched the job, as it was. */
|
|
36
|
+
context?: PluginContext;
|
|
37
|
+
}
|
|
38
|
+
interface RouteResult {
|
|
39
|
+
/** `GET /v1/download/{id}` redeemed this link (one use spent). */
|
|
40
|
+
redeemedLink?: DownloadLinkInfo;
|
|
41
|
+
/** `transcode.complete` deleted the video's original (`config.transcode.deleteOriginalOnSuccess`). */
|
|
42
|
+
originalDeleted?: boolean;
|
|
43
|
+
/** The transcode callback carried a valid job ticket: the job it belongs to. */
|
|
44
|
+
transcodeJob?: TranscodeJob;
|
|
45
|
+
}
|
|
46
|
+
type AnyContext$3 = Context<any>;
|
|
47
|
+
declare function getRouteResult(context: AnyContext$3): RouteResult;
|
|
48
|
+
|
|
49
|
+
interface WorkerEnv {
|
|
50
|
+
MEDIA_BUCKET: R2Bucket;
|
|
51
|
+
KEYS_BUCKET?: R2Bucket;
|
|
52
|
+
MEDIA_KV: KVNamespace;
|
|
53
|
+
DELIVERY_SIGN_SECRET: string;
|
|
54
|
+
TRANSCODE_RUNNER?: string;
|
|
55
|
+
FLY_API_TOKEN?: string;
|
|
56
|
+
FLY_APP_NAME?: string;
|
|
57
|
+
FLY_TRANSCODE_IMAGE?: string;
|
|
58
|
+
FLY_REGION?: string;
|
|
59
|
+
FLY_TRANSCODE_CPU_KIND?: string;
|
|
60
|
+
FLY_TRANSCODE_CPUS?: string;
|
|
61
|
+
FLY_TRANSCODE_MEMORY_MB?: string;
|
|
62
|
+
TRANSCODE_CONTAINER?: TranscodeContainerNamespace;
|
|
63
|
+
CF_TRANSCODE_INSTANCE?: string;
|
|
64
|
+
TRANSCODE_RUNNER_URL?: string;
|
|
65
|
+
TRANSCODE_RUNNER_SECRET?: string;
|
|
66
|
+
TRANSCODE_RUNNER_CPUS?: string;
|
|
67
|
+
GROQ_API_KEY: string;
|
|
68
|
+
MISTRAL_API_KEY: string;
|
|
69
|
+
WORKER_AUTH_SECRET?: string;
|
|
70
|
+
TRANSCODE_WEBHOOK_SECRET?: string;
|
|
71
|
+
DOWNLOAD_LINKS?: DurableObjectNamespace;
|
|
72
|
+
IMAGE_TRANSFORMS?: string;
|
|
73
|
+
IMAGE_TRANSFORMS_MAX_MEGAPIXELS?: string;
|
|
74
|
+
}
|
|
75
|
+
declare const MaxResolution: {
|
|
76
|
+
readonly P720: "720p";
|
|
77
|
+
readonly P1080: "1080p";
|
|
78
|
+
readonly P4K: "4k";
|
|
79
|
+
};
|
|
80
|
+
type MaxResolution = (typeof MaxResolution)[keyof typeof MaxResolution];
|
|
81
|
+
interface UploadTypeRule {
|
|
82
|
+
maxSizeBytes?: number;
|
|
83
|
+
accept?: string[];
|
|
84
|
+
visibility?: Visibility;
|
|
85
|
+
}
|
|
86
|
+
interface UploadRules {
|
|
87
|
+
video?: UploadTypeRule;
|
|
88
|
+
image?: UploadTypeRule;
|
|
89
|
+
file?: UploadTypeRule;
|
|
90
|
+
}
|
|
91
|
+
/** R2 storage classes the worker uses for the video raw. */
|
|
92
|
+
type RawStorageClass = "Standard" | "InfrequentAccess";
|
|
93
|
+
interface WorkerVariables {
|
|
94
|
+
/** Request state (storage prefix, rules, access, credentials, plugin context): see request-state.ts. */
|
|
95
|
+
requestState?: RequestState;
|
|
96
|
+
/** Hooks of the plugins (plugin-hooks.ts). */
|
|
97
|
+
pluginHooks?: PluginHooks;
|
|
98
|
+
/** What the route did (route-result.ts). */
|
|
99
|
+
routeResult?: RouteResult;
|
|
100
|
+
}
|
|
101
|
+
type WorkerApp = {
|
|
102
|
+
Bindings: WorkerEnv;
|
|
103
|
+
Variables: WorkerVariables;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/** Any JSON value. */
|
|
107
|
+
type Json = string | number | boolean | null | Json[] | {
|
|
108
|
+
[key: string]: Json;
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Data that plugins attach to a request. The Worker stores it with the uploads and download links the request creates
|
|
112
|
+
* and in the transcode job ticket, and hands it back when they are used (`getRouteResult`, the pending upload): a
|
|
113
|
+
* credential-less request has nothing else to tell who started it. The Worker never reads it.
|
|
114
|
+
*
|
|
115
|
+
* Augment the interface to type your plugin's keys:
|
|
116
|
+
*
|
|
117
|
+
* ```ts
|
|
118
|
+
* declare module "@devultur/self-host" {
|
|
119
|
+
* interface PluginContext { appId?: string }
|
|
120
|
+
* }
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
interface PluginContext {
|
|
124
|
+
[key: string]: Json | undefined;
|
|
125
|
+
}
|
|
126
|
+
interface TranscodeConfig {
|
|
127
|
+
/** Named presets and the default one, used when a request names no preset. */
|
|
128
|
+
presets?: TranscodeSettings;
|
|
129
|
+
/** Highest rung of the adaptive ladder. Unset: a `hls-*` alias derives it from its name, a spec asks up to 4k. */
|
|
130
|
+
maxResolution?: MaxResolution;
|
|
131
|
+
/** Delete the original once a transcode completes (never after a failure). Unset: the original is kept. */
|
|
132
|
+
deleteOriginalOnSuccess?: boolean;
|
|
133
|
+
/** Base of the output URLs in the client webhook. Unset: the origin of the transcode request. */
|
|
134
|
+
outputBaseUrl?: string;
|
|
135
|
+
}
|
|
136
|
+
/** Rules the routes enforce. */
|
|
137
|
+
interface WorkerConfig {
|
|
138
|
+
/** Default security policy (session/IP binding, encryption at rest). Unset: secure default. */
|
|
139
|
+
security?: SecurityPolicy;
|
|
140
|
+
/** Per media type: size limit, accepted content types and default visibility. */
|
|
141
|
+
uploadRules?: UploadRules;
|
|
142
|
+
/** Allowed caption locales. Unset or empty: any. */
|
|
143
|
+
captionsLocales?: string[];
|
|
144
|
+
/** `Cache-Control` per prefix of the `asset/` namespace (the longest one wins). */
|
|
145
|
+
cacheRules?: CacheRule[];
|
|
146
|
+
/** R2 storage class of an uploaded video original. Unset: Standard. Fixed at upload time. */
|
|
147
|
+
originalStorageClass?: RawStorageClass;
|
|
148
|
+
/**
|
|
149
|
+
* Longest lifetime, in seconds, of a file uploaded in the request: it is the default TTL and a shorter `expiresIn`
|
|
150
|
+
* (or upload token `fileExpiresIn`) still wins. For a demo or scratch Worker whose uploads clean themselves up.
|
|
151
|
+
* Unset: files only expire when the upload asks for it.
|
|
152
|
+
*/
|
|
153
|
+
maxFileLifetimeSeconds?: number;
|
|
154
|
+
/**
|
|
155
|
+
* R2 custom metadata added to every object the request uploads (also on the later credential-less PUT), for example
|
|
156
|
+
* which app or user uploaded it. Keys the Worker owns (integrity `sha256*`, `expiresAt`, `security`, `enc`,
|
|
157
|
+
* `derivedFrom`) are dropped from it.
|
|
158
|
+
*/
|
|
159
|
+
uploadMetadata?: Record<string, string>;
|
|
160
|
+
transcode?: TranscodeConfig;
|
|
161
|
+
}
|
|
162
|
+
/** What an auth plugin established about the request's credential. */
|
|
163
|
+
interface RequestAccess {
|
|
164
|
+
/**
|
|
165
|
+
* The request's credential was verified. Without it, an upload URL (`/v1/upload/<key>`) only acts on the upload
|
|
166
|
+
* that an authenticated `POST /v1/upload` created (the `?u=` id of that URL), never on a key of its own.
|
|
167
|
+
*/
|
|
168
|
+
authenticated?: boolean;
|
|
169
|
+
/**
|
|
170
|
+
* The request is a trusted read (the backend with its own credential, already authorized): private media is
|
|
171
|
+
* served without a viewer token. Never set it from a `?token=` query: its credential would end up in URLs.
|
|
172
|
+
*/
|
|
173
|
+
trustedRead?: boolean;
|
|
174
|
+
/** The credential is an upload token: `POST /v1/upload` only creates files under its prefix. */
|
|
175
|
+
uploadGrant?: UploadGrant;
|
|
176
|
+
}
|
|
177
|
+
interface RequestState {
|
|
178
|
+
/** Prefix of every storage key the request touches (`""` = the bucket root). */
|
|
179
|
+
storagePrefix: string;
|
|
180
|
+
config?: WorkerConfig;
|
|
181
|
+
access?: RequestAccess;
|
|
182
|
+
/**
|
|
183
|
+
* Bearer the transcode runner uploads its outputs with. `workerSecretAuth()` sets the Worker secret; an auth
|
|
184
|
+
* plugin of your own can hand the runner a narrower credential.
|
|
185
|
+
*/
|
|
186
|
+
runnerCredential?: string;
|
|
187
|
+
/**
|
|
188
|
+
* Bearer sent to the request's `callbackUrl` (client webhook of a transcode or captions job). Unset: the runner
|
|
189
|
+
* credential; `""`: no `Authorization` header.
|
|
190
|
+
*/
|
|
191
|
+
webhookCredential?: string;
|
|
192
|
+
context?: PluginContext;
|
|
193
|
+
}
|
|
194
|
+
interface RequestStatePatch extends Partial<Omit<RequestState, "config" | "access" | "context">> {
|
|
195
|
+
config?: Omit<WorkerConfig, "transcode"> & {
|
|
196
|
+
transcode?: TranscodeConfig;
|
|
197
|
+
};
|
|
198
|
+
access?: RequestAccess;
|
|
199
|
+
context?: PluginContext;
|
|
200
|
+
}
|
|
201
|
+
type AnyContext$2 = Context<any>;
|
|
202
|
+
/** State of the request. Without one set, the bucket root and no rules. */
|
|
203
|
+
declare function getRequestState(context: AnyContext$2): RequestState;
|
|
204
|
+
/** Did an auth plugin verify the request's credential (`access.authenticated`)? */
|
|
205
|
+
declare function isAuthenticated(context: AnyContext$2): boolean;
|
|
206
|
+
/**
|
|
207
|
+
* Narrows the state of the request. Top-level fields replace; `config` (and `config.transcode`), `access` and
|
|
208
|
+
* `context` merge field by field.
|
|
209
|
+
*/
|
|
210
|
+
declare function updateRequestState(context: AnyContext$2, patch: RequestStatePatch): RequestState;
|
|
211
|
+
|
|
212
|
+
/** What is being issued: a viewer token, an upload token, a download link or the transcode runner's token. */
|
|
213
|
+
type CredentialKind = "viewer" | "upload" | "download-link" | "service";
|
|
214
|
+
interface CredentialRequest {
|
|
215
|
+
kind: CredentialKind;
|
|
216
|
+
/** Scopes the credential will carry. */
|
|
217
|
+
scopes: string[];
|
|
218
|
+
/** What it is bound to: the upload prefix (`asset/…/`), the download link's key or the original being transcoded. */
|
|
219
|
+
path?: string;
|
|
220
|
+
/** Expiry the request asked for (epoch ms). */
|
|
221
|
+
expiresAt: number;
|
|
222
|
+
}
|
|
223
|
+
type CredentialDecision =
|
|
224
|
+
/** Refuse: `response` is returned as is. */
|
|
225
|
+
{
|
|
226
|
+
allow: false;
|
|
227
|
+
response: Response;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Issue it. `expiresAt` (epoch ms) caps the expiry: the Worker uses the earliest of it and the requested one.
|
|
231
|
+
* `claims` are added to a token's payload (never over the claims the Worker sets itself).
|
|
232
|
+
*/
|
|
233
|
+
| {
|
|
234
|
+
allow: true;
|
|
235
|
+
expiresAt?: number;
|
|
236
|
+
claims?: Record<string, Json>;
|
|
237
|
+
};
|
|
238
|
+
type AllowedCredential = Extract<CredentialDecision, {
|
|
239
|
+
allow: true;
|
|
240
|
+
}>;
|
|
241
|
+
type AnyContext$1 = Context<any>;
|
|
242
|
+
type IssueCredentialHook = (context: AnyContext$1, request: CredentialRequest) => CredentialDecision | Promise<CredentialDecision>;
|
|
243
|
+
/** A response refuses the redemption (returned as is, and the use is NOT spent); `null` lets it go on. */
|
|
244
|
+
type BeforeLinkRedeemHook = (context: AnyContext$1, link: DownloadLinkInfo) => Response | null | Promise<Response | null>;
|
|
245
|
+
/** The hooks of every plugin, in plugin order. */
|
|
246
|
+
interface PluginHooks {
|
|
247
|
+
issueCredential: IssueCredentialHook[];
|
|
248
|
+
beforeLinkRedeem: BeforeLinkRedeemHook[];
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
type AnyMiddleware = MiddlewareHandler<any>;
|
|
252
|
+
type AnyApp = Hono<any>;
|
|
253
|
+
/**
|
|
254
|
+
* Where a plugin's middleware runs. Every request goes through all plugins' `beforeAuth`, then their `auth`, then
|
|
255
|
+
* their `afterAuth` (each phase in plugin order), and then the route.
|
|
256
|
+
*/
|
|
257
|
+
interface PluginMiddleware {
|
|
258
|
+
/** Before authentication: request normalization, routing by host, endpoints a plugin answers on its own. */
|
|
259
|
+
beforeAuth?: AnyMiddleware[];
|
|
260
|
+
/** Authentication: who is calling, and what the request may touch (`updateRequestState`). */
|
|
261
|
+
auth?: AnyMiddleware[];
|
|
262
|
+
/** After authentication: authorization, limits, per-request rules, and work after `await next()` (logs, metrics). */
|
|
263
|
+
afterAuth?: AnyMiddleware[];
|
|
264
|
+
}
|
|
265
|
+
interface PluginRoute {
|
|
266
|
+
/** Mount path (`/v1/reports`). Mounted after the Worker's own routes, behind every plugin's middleware. */
|
|
267
|
+
path: string;
|
|
268
|
+
app: AnyApp;
|
|
269
|
+
}
|
|
270
|
+
interface WorkerPlugin {
|
|
271
|
+
/** Unique name, for errors and logs. */
|
|
272
|
+
name: string;
|
|
273
|
+
middleware?: PluginMiddleware;
|
|
274
|
+
routes?: PluginRoute[];
|
|
275
|
+
/** Before the Worker issues a viewer token, an upload token, a download link or the runner's service token. */
|
|
276
|
+
issueCredential?: IssueCredentialHook;
|
|
277
|
+
/** Before `GET /v1/download/{id}` spends a use of a link that still has one. */
|
|
278
|
+
beforeLinkRedeem?: BeforeLinkRedeemHook;
|
|
279
|
+
}
|
|
280
|
+
interface ComposedPlugins {
|
|
281
|
+
middleware: AnyMiddleware[];
|
|
282
|
+
routes: PluginRoute[];
|
|
283
|
+
hooks: PluginHooks;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
interface DownloadLinkRecord {
|
|
287
|
+
/** Full storage key (with the storage prefix). */
|
|
288
|
+
storageKey: string;
|
|
289
|
+
/** Virtual key (`{visibility}/…`). */
|
|
290
|
+
key: string;
|
|
291
|
+
/** Epoch ms. */
|
|
292
|
+
expiresAt: number;
|
|
293
|
+
maxUses: number;
|
|
294
|
+
uses: number;
|
|
295
|
+
filename?: string;
|
|
296
|
+
/** Storage prefix the link was issued under. */
|
|
297
|
+
storagePrefix: string;
|
|
298
|
+
/** Plugin context of the request that issued the link (opaque to the Worker). */
|
|
299
|
+
context?: PluginContext;
|
|
300
|
+
}
|
|
301
|
+
/** Storage surface of a DO that the class uses (the Workers one satisfies it; tests pass a Map). */
|
|
302
|
+
interface DownloadLinkStorage {
|
|
303
|
+
get<T>(key: string): Promise<T | undefined>;
|
|
304
|
+
put<T>(key: string, value: T): Promise<void>;
|
|
305
|
+
deleteAll(): Promise<void>;
|
|
306
|
+
setAlarm(scheduledTime: number): Promise<void>;
|
|
307
|
+
}
|
|
308
|
+
/** Surface of the `DurableObjectState` the class uses. */
|
|
309
|
+
interface DownloadLinkState {
|
|
310
|
+
storage: DownloadLinkStorage;
|
|
311
|
+
blockConcurrencyWhile<T>(callback: () => Promise<T>): Promise<T>;
|
|
312
|
+
}
|
|
313
|
+
declare class DownloadLink {
|
|
314
|
+
private readonly state;
|
|
315
|
+
private readonly storage;
|
|
316
|
+
constructor(state: DownloadLinkState);
|
|
317
|
+
fetch(request: Request): Promise<Response>;
|
|
318
|
+
private create;
|
|
319
|
+
/** The record if it still has a use left, or the response explaining why not: 404 if it never existed (or the alarm already
|
|
320
|
+
* cleared it), 410 if it expired or was already used. */
|
|
321
|
+
private usableRecord;
|
|
322
|
+
private peek;
|
|
323
|
+
/** Consumes ONE use. */
|
|
324
|
+
private redeem;
|
|
325
|
+
alarm(): Promise<void>;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
declare function storageKey(context: Context<WorkerApp>, key: string): string;
|
|
329
|
+
/**
|
|
330
|
+
* True when a verified token payload is the Worker's own service token (by its `typ` claim, never its `sub`): a read
|
|
331
|
+
* the Worker makes for itself, not a viewer's.
|
|
332
|
+
*/
|
|
333
|
+
declare function isServiceToken(payload: Record<string, unknown> | null | undefined): boolean;
|
|
334
|
+
/**
|
|
335
|
+
* Verifies a viewer token (HMAC-SHA256 with `DELIVERY_SIGN_SECRET`) for the private delivery gate:
|
|
336
|
+
* checks signature and expiration. Returns the payload if valid, `null` if not. Pure with respect to the
|
|
337
|
+
* worker (it receives the secret) so it can be unit-tested. It does NOT check fingerprint/ip — that's the delivery
|
|
338
|
+
* gate's job (`bindingMismatch` in routes/media.ts) after reading the object; here it's only "is this token authentic and current?".
|
|
339
|
+
*/
|
|
340
|
+
declare function verifyViewerToken(token: string | undefined, secret: string): Promise<Record<string, unknown> | null>;
|
|
341
|
+
declare function hashKey(key: string): Promise<string>;
|
|
342
|
+
declare function deletePrefix(bucket: R2Bucket, prefix: string): Promise<number>;
|
|
343
|
+
|
|
344
|
+
interface ImageTransformParams {
|
|
345
|
+
width?: number;
|
|
346
|
+
height?: number;
|
|
347
|
+
fit?: string;
|
|
348
|
+
format?: string;
|
|
349
|
+
quality?: number;
|
|
350
|
+
}
|
|
351
|
+
declare function parseTransformParams(url: URL): ImageTransformParams | null;
|
|
352
|
+
|
|
353
|
+
/** Everything derived from an original (variants, mezzanine, and their L4 keys) lives under this prefix. */
|
|
354
|
+
declare function variantPrefix(fullKey: string): string;
|
|
355
|
+
declare function isVariantKey(key: string): boolean;
|
|
356
|
+
|
|
357
|
+
/** The original that Cloudflare re-requests: its URL (without query) and the storage prefix it belongs to. */
|
|
358
|
+
interface ImageOrigin {
|
|
359
|
+
url: URL;
|
|
360
|
+
scope: string;
|
|
361
|
+
}
|
|
362
|
+
/** `Authorization` for Cloudflare's subrequest: `Bearer dvo.<scope>.<signature of scope + URL>`. */
|
|
363
|
+
declare function imageOriginAuthorization(secret: string, origin: ImageOrigin): Promise<string>;
|
|
364
|
+
/**
|
|
365
|
+
* If the request is Cloudflare Images' subrequest asking for an original, the signed storage prefix; otherwise null.
|
|
366
|
+
* `Via` alone is NOT enough (a client can send it): it has to carry the signature for THIS URL.
|
|
367
|
+
*/
|
|
368
|
+
declare function readImageOriginScope(request: Request, secret: string | undefined): Promise<string | null>;
|
|
369
|
+
/** Is this Cloudflare Images' signed subrequest? It's not delivery to the client: it isn't gated or counted. */
|
|
370
|
+
declare function isImageOriginRequest(request: Request, secret: string | undefined): Promise<boolean>;
|
|
371
|
+
|
|
372
|
+
interface CredentialPolicyOptions {
|
|
373
|
+
/** Longest lifetime, in seconds, per kind of credential. A longer request is shortened to it, not refused. */
|
|
374
|
+
maxTtlSeconds?: Partial<Record<CredentialKind, number>>;
|
|
375
|
+
}
|
|
376
|
+
declare function credentialPolicy(options?: CredentialPolicyOptions): WorkerPlugin;
|
|
377
|
+
|
|
378
|
+
declare function downloadLinks(): WorkerPlugin;
|
|
379
|
+
|
|
380
|
+
/** Applies `config` to every request. Put it first: plugins after it narrow it, field by field. */
|
|
381
|
+
declare function staticConfig(config: WorkerConfig): WorkerPlugin;
|
|
382
|
+
|
|
383
|
+
declare const WorkerAuthErrorCode: {
|
|
384
|
+
readonly SECRET_MISSING: "worker_auth_secret_missing";
|
|
385
|
+
readonly UNAUTHORIZED: "unauthorized";
|
|
386
|
+
};
|
|
387
|
+
/**
|
|
388
|
+
* Routes that never need a credential. Pure.
|
|
389
|
+
* - `/health` and the playback metrics endpoint: public by design.
|
|
390
|
+
* - Media delivery: public media is tokenless; the route itself requires a viewer token for private media.
|
|
391
|
+
* - Download links: the opaque link id is the credential.
|
|
392
|
+
* - Webhooks: they check their own shared secret.
|
|
393
|
+
*/
|
|
394
|
+
declare function isOpenRoute(method: string, path: string): boolean;
|
|
395
|
+
/**
|
|
396
|
+
* An upload URL (`/v1/upload/<key>`): the URL that `POST /v1/upload` returned is the credential. Without an
|
|
397
|
+
* authenticated request it only acts on that upload (see `RequestAccess.authenticated`). Pure.
|
|
398
|
+
*/
|
|
399
|
+
declare function isUploadUrl(path: string): boolean;
|
|
400
|
+
/** The default authentication: `Authorization: Bearer <WORKER_AUTH_SECRET>` on control routes. */
|
|
401
|
+
declare function workerSecretAuth(): WorkerPlugin;
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* The plugins `createWorker()` uses when given none: `config` for every request, the worker-secret auth, the default
|
|
405
|
+
* credential policy and download links. Spread them to add your own, or keep only the ones you want.
|
|
406
|
+
*/
|
|
407
|
+
declare function defaultPlugins(config?: WorkerConfig): WorkerPlugin[];
|
|
408
|
+
|
|
409
|
+
declare const MAX_ASSET_LIST_LIMIT = 1000;
|
|
410
|
+
interface AssetListEntry {
|
|
411
|
+
key: string;
|
|
412
|
+
path: string;
|
|
413
|
+
size: number;
|
|
414
|
+
etag: string;
|
|
415
|
+
sha256: string | null;
|
|
416
|
+
sha256Verified: boolean;
|
|
417
|
+
uploaded: string;
|
|
418
|
+
cacheControl: string | null;
|
|
419
|
+
visibility: Visibility;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/** The unit's tag: a hash (Cloudflare's tags are case-insensitive but paths aren't). */
|
|
423
|
+
declare function edgeCacheTag(storagePrefix: string, virtualKey: string): Promise<string>;
|
|
424
|
+
|
|
425
|
+
/** `Cache-Control` served: the one stored on the object (for private, no shared caches) or the one for a generated id. */
|
|
426
|
+
declare function servedCacheControl(stored: string | undefined, isPrivate: boolean): string;
|
|
427
|
+
|
|
428
|
+
/** Has the object expired (TTL)? From then on we respond 410 even if the sweeper hasn't deleted it yet. Pure. */
|
|
429
|
+
declare function isObjectExpired(customMetadata: Record<string, string> | undefined, now?: number): boolean;
|
|
430
|
+
|
|
431
|
+
declare const UPLOAD_TOKEN_TYPE = "upload";
|
|
432
|
+
/** Short-lived: it's a JWT (an auth layer can also reject it if the credential that issued it is no longer valid). */
|
|
433
|
+
declare const MAX_UPLOAD_TOKEN_TTL_SECONDS: number;
|
|
434
|
+
/** Grant of the claims of a verified upload token. `null` if it isn't a valid upload token. Pure. */
|
|
435
|
+
declare function uploadGrantFromClaims(payload: Record<string, unknown>): UploadGrant | null;
|
|
436
|
+
|
|
437
|
+
/** `customMetadata` keys on the R2 object (strings). */
|
|
438
|
+
declare const ObjectMeta: {
|
|
439
|
+
readonly SHA256: "sha256";
|
|
440
|
+
/** `"true"` = the worker hashed the bytes; `"false"` = sha256 declared by the client (multipart). */
|
|
441
|
+
readonly SHA256_VERIFIED: "sha256Verified";
|
|
442
|
+
/** Epoch ms. From then on the gate responds 410 (and an external sweeper may delete it). */
|
|
443
|
+
readonly EXPIRES_AT: "expiresAt";
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
/** The upload an upload URL (`/v1/upload/<key>?u=<id>`) points to, as plugins see it. */
|
|
447
|
+
interface PendingUploadInfo {
|
|
448
|
+
/** Key of the file (`{visibility}/…`), relative to the storage prefix. */
|
|
449
|
+
key: string;
|
|
450
|
+
/** Full storage key, with the storage prefix of the request that created it. */
|
|
451
|
+
storageKey: string;
|
|
452
|
+
contentType: string;
|
|
453
|
+
filename?: string;
|
|
454
|
+
/** A multipart upload was started on it. */
|
|
455
|
+
isMultipart: boolean;
|
|
456
|
+
/** The file expires at this time (epoch ms). */
|
|
457
|
+
expiresAt?: number;
|
|
458
|
+
/** An `asset/` upload that replaces the file at its path. */
|
|
459
|
+
isOverwrite: boolean;
|
|
460
|
+
/** Plugin context of the `POST /v1/upload` that created it. */
|
|
461
|
+
context?: PluginContext;
|
|
462
|
+
}
|
|
463
|
+
type AnyContext = Context<any>;
|
|
464
|
+
/**
|
|
465
|
+
* The upload this request's upload URL points to, for a plugin that runs before the route (the route deletes it once
|
|
466
|
+
* the file is complete). `undefined` = not an upload URL, or one without an id; `null` = an id that does not resolve
|
|
467
|
+
* (malformed, unknown, expired or issued for another key).
|
|
468
|
+
*/
|
|
469
|
+
declare function getPendingUpload(context: AnyContext): Promise<PendingUploadInfo | null | undefined>;
|
|
470
|
+
/**
|
|
471
|
+
* Cancels the upload behind an upload URL that `POST /v1/upload` returned: the URL stops working. For a plugin that
|
|
472
|
+
* rejects the created upload after the route ran.
|
|
473
|
+
*/
|
|
474
|
+
declare function cancelPendingUpload(context: AnyContext, uploadUrl: string): Promise<void>;
|
|
475
|
+
|
|
476
|
+
/** Code of every refusal of a valid token: which claim failed is logged, never told to the caller. */
|
|
477
|
+
declare const TOKEN_REJECTED_CODE = "token_rejected";
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Scopes of a verified token, from its `scope` claim. `undefined` = the token carries no `scope` (not limited by it);
|
|
481
|
+
* a claim that is not a string carries no scope at all. Pure.
|
|
482
|
+
*/
|
|
483
|
+
declare function readTokenScopes(payload: Record<string, unknown>): string[] | undefined;
|
|
484
|
+
|
|
485
|
+
interface EncodeMetrics {
|
|
486
|
+
/** Wall-clock seconds of the ladder's FFmpeg (no download, upload or thumbnail). */
|
|
487
|
+
encodeSeconds: number;
|
|
488
|
+
/** vCPUs of the runner (0 = unknown). */
|
|
489
|
+
cpus: number;
|
|
490
|
+
/** Duration of the source, in seconds. */
|
|
491
|
+
durationSeconds: number;
|
|
492
|
+
/** Highest rendition generated (`360p`…`4k`). */
|
|
493
|
+
topRung?: string;
|
|
494
|
+
runner?: string;
|
|
495
|
+
/** Per-title encoding: complexity 0 (simple) to 1 (complex). Absent = no probe (short clip, opt-out). */
|
|
496
|
+
complexity?: number;
|
|
497
|
+
/** Wall-clock seconds of the complexity probe. */
|
|
498
|
+
probeSeconds?: number;
|
|
499
|
+
/** CRF and maxrate chosen per rung (rungs at CRF; an MP4 with the spec's bitrate doesn't appear). */
|
|
500
|
+
encodeParams?: EncodeParam[];
|
|
501
|
+
}
|
|
502
|
+
interface EncodeParam {
|
|
503
|
+
rung: string;
|
|
504
|
+
crf: number;
|
|
505
|
+
maxrateKbps: number;
|
|
506
|
+
}
|
|
507
|
+
/** The encode measurement of a `transcode.complete`. Null if the event doesn't carry it or it's invalid. */
|
|
508
|
+
declare function parseEncodeMetrics(event: unknown): EncodeMetrics | null;
|
|
509
|
+
/** vCPU-seconds per minute of video (assumes the CPU saturated during the encode). Null without vCPUs or duration. */
|
|
510
|
+
declare function vcpuSecondsPerVideoMinute(metrics: EncodeMetrics): number | null;
|
|
511
|
+
/** Structured log line (grep `[transcode-metrics]`) to measure vCPU-s/min without extra infrastructure. */
|
|
512
|
+
declare function logEncodeMetrics(videoId: string, metrics: EncodeMetrics): void;
|
|
513
|
+
|
|
514
|
+
type Raw = Record<string, unknown>;
|
|
515
|
+
/** Storyboard key from a `transcode.complete` (any plan), or null if there was no storyboard. */
|
|
516
|
+
declare function readStoryboardKey(event: unknown): string | null;
|
|
517
|
+
/** Outputs (keys) from a `transcode.complete`, or null if the event doesn't report them (`hls-*` alias, old script). */
|
|
518
|
+
declare function readTranscodeOutputKeys(event: unknown): TranscodeOutputKeys | null;
|
|
519
|
+
/** Outputs in their public form (URLs built from `baseUrl`), or null if the event doesn't report them. */
|
|
520
|
+
declare function readTranscodeOutputs(event: unknown, baseUrl: string): TranscodeOutputUrls | null;
|
|
521
|
+
/** Event with the outputs in their public form (renditions with `url`, `posterUrls`). No outputs → unchanged. */
|
|
522
|
+
declare function withOutputUrls(event: Raw, baseUrl: string): Raw;
|
|
523
|
+
|
|
524
|
+
interface CreateWorkerOptions {
|
|
525
|
+
/** Plugins, composed in order (see plugin.ts). Omitted: `defaultPlugins()`. */
|
|
526
|
+
plugins?: WorkerPlugin[];
|
|
527
|
+
}
|
|
528
|
+
declare function createWorker(options?: CreateWorkerOptions): Hono<WorkerApp, hono_types.BlankSchema, "/">;
|
|
529
|
+
|
|
530
|
+
export { type AllowedCredential, type AssetListEntry, type BeforeLinkRedeemHook, type ComposedPlugins, type CreateWorkerOptions, type CredentialDecision, type CredentialKind, type CredentialPolicyOptions, type CredentialRequest, DownloadLink, type DownloadLinkInfo, type DownloadLinkRecord, type DownloadLinkStorage, type EncodeMetrics, type ImageTransformParams, type IssueCredentialHook, type Json, MAX_ASSET_LIST_LIMIT, MAX_UPLOAD_TOKEN_TTL_SECONDS, MaxResolution, ObjectMeta, type PendingUploadInfo, type PluginContext, type PluginMiddleware, type PluginRoute, type RawStorageClass, type RequestAccess, type RequestState, type RequestStatePatch, type RouteResult, TOKEN_REJECTED_CODE, type TranscodeConfig, type TranscodeJob, UPLOAD_TOKEN_TYPE, type UploadGrant, type UploadRules, type UploadTypeRule, type WorkerApp, WorkerAuthErrorCode, type WorkerConfig, type WorkerEnv, type WorkerPlugin, type WorkerVariables, cancelPendingUpload, createWorker, credentialPolicy, defaultPlugins, deletePrefix, downloadLinks, edgeCacheTag, getPendingUpload, getRequestState, getRouteResult, hashKey, imageOriginAuthorization, isAuthenticated, isImageOriginRequest, isObjectExpired, isOpenRoute, isServiceToken, isUploadUrl, isVariantKey, logEncodeMetrics, parseEncodeMetrics, parseTransformParams, readImageOriginScope, readStoryboardKey, readTokenScopes, readTranscodeOutputKeys, readTranscodeOutputs, servedCacheControl, staticConfig, storageKey, updateRequestState, uploadGrantFromClaims, variantPrefix, vcpuSecondsPerVideoMinute, verifyViewerToken, withOutputUrls, workerSecretAuth };
|