@rebasepro/server 0.14.0 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/rest/query-parser.d.ts +37 -1
- package/dist/api/rest/write-validation.d.ts +26 -0
- package/dist/auth/index.d.ts +3 -1
- package/dist/auth/interfaces.d.ts +14 -1
- package/dist/auth/jwks-routes.d.ts +17 -0
- package/dist/auth/jwt-keys.d.ts +108 -0
- package/dist/auth/jwt.d.ts +62 -2
- package/dist/{auth-CYoPVf-E.js → auth-BobZVd0j.js} +142 -167
- package/dist/auth-BobZVd0j.js.map +1 -0
- package/dist/boot/boot.d.ts +36 -50
- package/dist/boot/ddl-bootstrap.d.ts +15 -0
- package/dist/boot/env.d.ts +20 -0
- package/dist/boot/provision.d.ts +182 -0
- package/dist/boot/role.d.ts +88 -0
- package/dist/{cron-store-Dvr4Y1sZ.js → cron-store-CB1x-Ken.js} +3 -3
- package/dist/{cron-store-Dvr4Y1sZ.js.map → cron-store-CB1x-Ken.js.map} +1 -1
- package/dist/{ddl-bootstrap-BhXbTnBl.js → ddl-bootstrap-Cywoj8Ta.js} +40 -2
- package/dist/ddl-bootstrap-Cywoj8Ta.js.map +1 -0
- package/dist/env.d.ts +2 -0
- package/dist/functions/proxy.d.ts +41 -0
- package/dist/functions/selection.d.ts +45 -0
- package/dist/index.d.ts +8 -3
- package/dist/index.es.js +1113 -661
- package/dist/index.es.js.map +1 -1
- package/dist/init/shutdown.d.ts +4 -0
- package/dist/init/surfaces.d.ts +79 -0
- package/dist/init.d.ts +121 -1
- package/dist/jobs/index.d.ts +5 -0
- package/dist/jobs/job-queue.d.ts +14 -0
- package/dist/jobs/job-store.d.ts +22 -0
- package/dist/jobs/types.d.ts +125 -0
- package/dist/jobs-DR4SjGrD.js +326 -0
- package/dist/jobs-DR4SjGrD.js.map +1 -0
- package/dist/{jwt-_IFqfTOg.js → jwt-VJyXTdQQ.js} +447 -11
- package/dist/jwt-VJyXTdQQ.js.map +1 -0
- package/dist/{openapi-generator-DPKtUC9X.js → openapi-generator-DQeQ_q2f.js} +68 -3
- package/dist/openapi-generator-DQeQ_q2f.js.map +1 -0
- package/dist/proxy-Bj5DVllb.js +139 -0
- package/dist/proxy-Bj5DVllb.js.map +1 -0
- package/dist/{request-timeout-RivJsME0.js → request-timeout-BuFoEKwT.js} +6 -3
- package/dist/request-timeout-BuFoEKwT.js.map +1 -0
- package/dist/selection-_z6TM1DB.js +64 -0
- package/dist/selection-_z6TM1DB.js.map +1 -0
- package/dist/services/webhook-service.d.ts +43 -5
- package/dist/{src-C7rkDGxA.js → src-8XDWyDfR.js} +84 -13
- package/dist/src-8XDWyDfR.js.map +1 -0
- package/dist/src-Cz9nMgUR.js.map +1 -1
- package/dist/storage/keys.d.ts +17 -0
- package/dist/storage/routes.d.ts +1 -1
- package/dist/storage/storage-registry.d.ts +46 -4
- package/dist/storage/tus-handler.d.ts +1 -1
- package/package.json +5 -5
- package/dist/auth-CYoPVf-E.js.map +0 -1
- package/dist/ddl-bootstrap-BhXbTnBl.js.map +0 -1
- package/dist/jwt-_IFqfTOg.js.map +0 -1
- package/dist/openapi-generator-DPKtUC9X.js.map +0 -1
- package/dist/request-timeout-RivJsME0.js.map +0 -1
- package/dist/src-C7rkDGxA.js.map +0 -1
|
@@ -1,6 +1,42 @@
|
|
|
1
|
-
import type { ListLimitBounds } from "@rebasepro/types";
|
|
1
|
+
import type { ListLimitBounds, OrderByTuple } from "@rebasepro/types";
|
|
2
2
|
import { QueryOptions } from "../types";
|
|
3
3
|
export declare const mapOperator: (op: string) => import("@rebasepro/types").WhereFilterOp | null;
|
|
4
|
+
type OrderByEntry = {
|
|
5
|
+
field: string;
|
|
6
|
+
direction: "asc" | "desc";
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* The parsed entries as the driver contract spells them: `[field, direction]`
|
|
10
|
+
* tuples in order of significance.
|
|
11
|
+
*
|
|
12
|
+
* The REST layer used to hand the driver `orderBy[0].field` and drop the rest,
|
|
13
|
+
* so `?orderBy=[{"field":"roles"},{"field":"created_at","direction":"desc"}]`
|
|
14
|
+
* — a shape this parser has always accepted and validated in full — sorted by
|
|
15
|
+
* `roles` alone and returned the ties in whatever order Postgres pleased.
|
|
16
|
+
*/
|
|
17
|
+
export declare function orderByEntriesToTuples(entries?: OrderByEntry[]): OrderByTuple[] | undefined;
|
|
18
|
+
export interface ParsedAggregate {
|
|
19
|
+
fn: "count" | "sum" | "avg" | "min" | "max";
|
|
20
|
+
/** Absent only for `count()`, which counts rows rather than values. */
|
|
21
|
+
field?: string;
|
|
22
|
+
/** The key this appears under in the response. */
|
|
23
|
+
alias: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Parse `?select=count(),sum(total),avg(total)`.
|
|
27
|
+
*
|
|
28
|
+
* The spelling is SQL's, because whoever writes it is thinking in SQL and
|
|
29
|
+
* because any other spelling has to be learned first. `count()` with no field
|
|
30
|
+
* counts rows; every other function names a column.
|
|
31
|
+
*
|
|
32
|
+
* Aliases are derived rather than accepted: `sum(total)` returns as
|
|
33
|
+
* `sum_total`, `count()` as `count`. Letting a caller choose would mean
|
|
34
|
+
* checking their alias is not also a `groupBy` field — a rule nobody would
|
|
35
|
+
* guess, and a silently overwritten value if it went unchecked.
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseAggregateSelect(raw: unknown): ParsedAggregate[] | undefined;
|
|
38
|
+
/** Parse `?groupBy=status,country`. */
|
|
39
|
+
export declare function parseGroupBy(raw: unknown): string[] | undefined;
|
|
4
40
|
export { DEFAULT_LIST_LIMIT, DEFAULT_VECTOR_LIST_LIMIT, MAX_LIST_LIMIT } from "@rebasepro/types";
|
|
5
41
|
/**
|
|
6
42
|
* Overridable list-pagination bounds for {@link parseQueryOptions}. Without
|
|
@@ -85,3 +85,29 @@ export declare function assertWriteValuesValid(values: Record<string, unknown>,
|
|
|
85
85
|
export declare function projectResponseFields<T extends Record<string, unknown>>(rows: T[], fields: readonly string[] | undefined, collection: CollectionConfig, options?: {
|
|
86
86
|
include?: readonly string[];
|
|
87
87
|
}): T[];
|
|
88
|
+
/**
|
|
89
|
+
* Both write checks, as one call, for a transport that is not the REST router.
|
|
90
|
+
*
|
|
91
|
+
* The REST routes run `assertKnownWriteFields` and `assertWriteValuesValid`
|
|
92
|
+
* back to back on the caller's body, seven times over — and they were the only
|
|
93
|
+
* place either ran. The WebSocket `SAVE` handler took a client payload straight
|
|
94
|
+
* to `driver.save`, so the same write arrived validated through one door and
|
|
95
|
+
* unvalidated through the other: `PATCH /api/data/users/1 { age: 999 }` was a
|
|
96
|
+
* 400, and the socket wrote it.
|
|
97
|
+
*
|
|
98
|
+
* Exported for the sockets in `@rebasepro/server-postgres` and
|
|
99
|
+
* `@rebasepro/server-mongo`, which are the other request boundaries. Not the
|
|
100
|
+
* route builder — `index.ts` keeps that internal, and this is a rule rather
|
|
101
|
+
* than wiring.
|
|
102
|
+
*
|
|
103
|
+
* It stays at the boundary rather than moving into the driver deliberately: it
|
|
104
|
+
* validates what a *caller sent*, before `beforeSave` gets a chance to fill in
|
|
105
|
+
* or rewrite anything. In-process writes through `rebase.data` are trusted
|
|
106
|
+
* server code and are not run through it, which is the placement the REST layer
|
|
107
|
+
* already chose.
|
|
108
|
+
*
|
|
109
|
+
* @param values the caller's payload, exactly as it arrived
|
|
110
|
+
* @param collection resolved from the registry by path — never the copy the
|
|
111
|
+
* client sent, or the rules would be the caller's to pick
|
|
112
|
+
*/
|
|
113
|
+
export declare function assertWriteRequestValid(values: Record<string, unknown>, collection: CollectionConfig): void;
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export * from "./interfaces";
|
|
2
|
-
export { configureJwt, isJwtConfigured, generateAccessToken, verifyAccessToken, generateRefreshToken, hashRefreshToken, getRefreshTokenExpiry, getAccessTokenExpiry, generateDownloadToken, verifyDownloadToken } from "./jwt";
|
|
2
|
+
export { configureJwt, isJwtConfigured, generateAccessToken, verifyAccessToken, generateRefreshToken, hashRefreshToken, getRefreshTokenExpiry, getAccessTokenExpiry, generateDownloadToken, verifyDownloadToken, getJwks, hasAsymmetricSigningKey } from "./jwt";
|
|
3
3
|
export type { JwtConfig, AccessTokenPayload, DownloadTokenPayload } from "./jwt";
|
|
4
|
+
export { createJwksRoutes } from "./jwks-routes";
|
|
5
|
+
export type { JwtSigningKeyConfig, JwtSigningAlgorithm, PublicJwk } from "./jwt-keys";
|
|
4
6
|
export { hashPassword, verifyPassword, validatePasswordStrength } from "./password";
|
|
5
7
|
export { safeCompare } from "./crypto-utils";
|
|
6
8
|
export type { PasswordValidationResult } from "./password";
|
|
@@ -227,7 +227,20 @@ export interface PaginatedUsersResult {
|
|
|
227
227
|
*/
|
|
228
228
|
export interface UserRepository {
|
|
229
229
|
/**
|
|
230
|
-
* Create a new user
|
|
230
|
+
* Create a new user.
|
|
231
|
+
*
|
|
232
|
+
* **An email already in use is a 409 `EMAIL_EXISTS`, not a 500.** Every
|
|
233
|
+
* caller checks first — `POST /auth/register` reads `getUserByEmail` and
|
|
234
|
+
* answers 409 — and every engine backs that check with a unique index,
|
|
235
|
+
* because a check cannot hold its answer still. What is left is the window
|
|
236
|
+
* between the two, and a double-clicked signup button is wide enough: both
|
|
237
|
+
* requests read "no such user", both insert, one wins.
|
|
238
|
+
*
|
|
239
|
+
* The loser's insert violates the index either way; the only question is
|
|
240
|
+
* what it is turned into. An unmapped driver error is a 500 "Internal
|
|
241
|
+
* Server Error", which tells the person who clicked twice that the server
|
|
242
|
+
* is broken rather than that they already have an account — and tells the
|
|
243
|
+
* operator to go looking for a fault that is not there.
|
|
231
244
|
*/
|
|
232
245
|
createUser(data: CreateUserData): Promise<UserData>;
|
|
233
246
|
/**
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import type { HonoEnv } from "../api/types";
|
|
3
|
+
/**
|
|
4
|
+
* `GET /.well-known/jwks.json` — the public keys that verify this issuer's
|
|
5
|
+
* access tokens.
|
|
6
|
+
*
|
|
7
|
+
* Deliberately unauthenticated and world-readable: the whole point is that a
|
|
8
|
+
* gateway, an edge function or a neighbouring service can check a Rebase token
|
|
9
|
+
* without being trusted with anything. Public keys are not a secret, and
|
|
10
|
+
* `jwt-keys.ts` derives what is served here from the public half of each pair.
|
|
11
|
+
*
|
|
12
|
+
* Mounted at the root rather than under `basePath`, because `/.well-known/` is
|
|
13
|
+
* where every verifier looks — an issuer of `https://api.example.com` implies
|
|
14
|
+
* `https://api.example.com/.well-known/jwks.json`, whatever the API happens to
|
|
15
|
+
* be prefixed with.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createJwksRoutes(): Hono<HonoEnv>;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { type KeyObject } from "crypto";
|
|
2
|
+
/**
|
|
3
|
+
* Asymmetric signing keys for access tokens, and the JWKS built from them.
|
|
4
|
+
*
|
|
5
|
+
* The symmetric secret this replaces is not going away — it still signs every
|
|
6
|
+
* purpose-scoped token (download, MFA-pending, password reset), which are read
|
|
7
|
+
* only by the server that minted them and are better off short. What a shared
|
|
8
|
+
* secret cannot do is let *anybody else* verify a session:
|
|
9
|
+
*
|
|
10
|
+
* - a gateway, an edge worker, or a second service that wants to check a token
|
|
11
|
+
* has to be handed the key that mints them, so every verifier becomes a
|
|
12
|
+
* forger;
|
|
13
|
+
* - and rotating it invalidates every token in circulation at once, which is
|
|
14
|
+
* why in practice it never gets rotated at all.
|
|
15
|
+
*
|
|
16
|
+
* A private key signs, the matching public key verifies, and the public half is
|
|
17
|
+
* published at `/.well-known/jwks.json` for anyone to fetch. Rotation stops
|
|
18
|
+
* being an outage: mint with the new key, keep the old one in the list until
|
|
19
|
+
* the last token signed by it has expired, then drop it.
|
|
20
|
+
*
|
|
21
|
+
* **The key is chosen by `kid`, and the algorithm comes from the key — never
|
|
22
|
+
* from the token.** A verifier that reads `alg` out of the header it is
|
|
23
|
+
* checking will accept an `HS256` token whose "secret" is the RSA public key it
|
|
24
|
+
* published, which is a complete authentication bypass and the best-known way
|
|
25
|
+
* to get this wrong. {@link resolveVerificationKey} therefore returns the
|
|
26
|
+
* algorithm alongside the key, and the caller pins it.
|
|
27
|
+
*/
|
|
28
|
+
/** The algorithms a signing key may use. Both are widely supported by verifiers. */
|
|
29
|
+
export type JwtSigningAlgorithm = "RS256" | "ES256";
|
|
30
|
+
/**
|
|
31
|
+
* One asymmetric key pair, as an operator configures it.
|
|
32
|
+
*
|
|
33
|
+
* Only the private key is supplied: the public half is derived from it, so a
|
|
34
|
+
* mismatched pair — a configuration error that produces tokens nobody can
|
|
35
|
+
* verify, and which no amount of local testing catches because the signer never
|
|
36
|
+
* consults the public key — cannot be expressed.
|
|
37
|
+
*/
|
|
38
|
+
export interface JwtSigningKeyConfig {
|
|
39
|
+
/**
|
|
40
|
+
* Names this key in the token header and in the JWKS. Any stable string;
|
|
41
|
+
* something that identifies *when* it was minted (`"2026-08"`) is the usual
|
|
42
|
+
* choice, because the question you ask of a `kid` later is always "is this
|
|
43
|
+
* the old one?".
|
|
44
|
+
*/
|
|
45
|
+
kid: string;
|
|
46
|
+
/** PEM-encoded PKCS#8 or SEC1 private key. */
|
|
47
|
+
privateKey: string;
|
|
48
|
+
/**
|
|
49
|
+
* Defaults to the algorithm implied by the key type — RSA keys sign RS256,
|
|
50
|
+
* EC keys sign ES256. Worth setting only to be explicit.
|
|
51
|
+
*/
|
|
52
|
+
algorithm?: JwtSigningAlgorithm;
|
|
53
|
+
}
|
|
54
|
+
/** A configured key, parsed and ready to sign or verify with. */
|
|
55
|
+
export interface ResolvedJwtKey {
|
|
56
|
+
kid: string;
|
|
57
|
+
algorithm: JwtSigningAlgorithm;
|
|
58
|
+
privateKey: KeyObject;
|
|
59
|
+
publicKey: KeyObject;
|
|
60
|
+
}
|
|
61
|
+
/** A JSON Web Key, as served by the JWKS endpoint. Public parameters only. */
|
|
62
|
+
export type PublicJwk = Record<string, unknown> & {
|
|
63
|
+
kid: string;
|
|
64
|
+
alg: JwtSigningAlgorithm;
|
|
65
|
+
use: "sig";
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Parse the configured keys, deriving each public half from its private key.
|
|
69
|
+
*
|
|
70
|
+
* Throws on anything malformed. This runs at boot, from `configureJwt`, so a
|
|
71
|
+
* key that cannot sign takes the process down at start rather than at the first
|
|
72
|
+
* login — the same bargain every other credential in this file makes.
|
|
73
|
+
*/
|
|
74
|
+
export declare function resolveSigningKeys(configs: JwtSigningKeyConfig[]): ResolvedJwtKey[];
|
|
75
|
+
/**
|
|
76
|
+
* The key a token names, or `null` if it names none we hold.
|
|
77
|
+
*
|
|
78
|
+
* The returned algorithm is the *key's*, and the caller must verify with that
|
|
79
|
+
* one alone. See the module docblock for what happens otherwise.
|
|
80
|
+
*/
|
|
81
|
+
export declare function resolveVerificationKey(keys: ResolvedJwtKey[], kid: string | undefined): ResolvedJwtKey | null;
|
|
82
|
+
/**
|
|
83
|
+
* A PEM as an environment variable can actually carry it.
|
|
84
|
+
*
|
|
85
|
+
* A PEM is multi-line and environment variables are not, so every deployment
|
|
86
|
+
* tool solves it differently: `.env` files and most secret managers escape the
|
|
87
|
+
* newlines to `\n`, Kubernetes and Docker secrets pass the bytes through
|
|
88
|
+
* intact, and CI systems that mangle both are usually fed base64. All three
|
|
89
|
+
* arrive here, and guessing wrong produces "not a readable PEM private key" at
|
|
90
|
+
* boot with a key the operator can see is perfectly valid.
|
|
91
|
+
*
|
|
92
|
+
* Detection is on content, not on a flag: a PEM says so on its first line, and
|
|
93
|
+
* anything that does not is tried as base64.
|
|
94
|
+
*/
|
|
95
|
+
export declare function normalizePemFromEnv(value: string): string;
|
|
96
|
+
/**
|
|
97
|
+
* The public halves, in JWKS form.
|
|
98
|
+
*
|
|
99
|
+
* Node exports a JWK containing only public parameters for a public
|
|
100
|
+
* `KeyObject` — no `d`, no primes — so the private material cannot leak
|
|
101
|
+
* through this path even if a private key were passed by mistake. The keys are
|
|
102
|
+
* derived from `publicKey` regardless, and this is asserted in the tests,
|
|
103
|
+
* because "cannot" is worth checking on the one endpoint whose entire job is to
|
|
104
|
+
* be world-readable.
|
|
105
|
+
*/
|
|
106
|
+
export declare function toJwks(keys: ResolvedJwtKey[]): {
|
|
107
|
+
keys: PublicJwk[];
|
|
108
|
+
};
|
package/dist/auth/jwt.d.ts
CHANGED
|
@@ -1,7 +1,26 @@
|
|
|
1
|
+
import { type JwtSigningKeyConfig, type PublicJwk } from "./jwt-keys";
|
|
1
2
|
export interface JwtConfig {
|
|
2
3
|
secret: string;
|
|
3
4
|
accessExpiresIn?: string;
|
|
4
5
|
refreshExpiresIn?: string;
|
|
6
|
+
/**
|
|
7
|
+
* Asymmetric keys for signing **access tokens**, newest first.
|
|
8
|
+
*
|
|
9
|
+
* Optional and additive: with none configured everything below behaves
|
|
10
|
+
* exactly as it did, signing HS256 with {@link JwtConfig.secret}. With one
|
|
11
|
+
* configured, access tokens are signed by {@link JwtConfig.activeKid} (or
|
|
12
|
+
* the first entry) and carry its `kid`, and every key in the list keeps
|
|
13
|
+
* verifying — which is what makes rotation a deploy rather than a mass
|
|
14
|
+
* sign-out. Tokens minted before any key existed carry no `kid` and are
|
|
15
|
+
* still verified against the secret until they expire.
|
|
16
|
+
*
|
|
17
|
+
* The secret stays required regardless: purpose-scoped tokens (download,
|
|
18
|
+
* MFA-pending, password reset) are read only by this server, and are
|
|
19
|
+
* shorter and cheaper symmetric. See `jwt-keys.ts`.
|
|
20
|
+
*/
|
|
21
|
+
signingKeys?: JwtSigningKeyConfig[];
|
|
22
|
+
/** Which key signs. Defaults to the first entry of {@link JwtConfig.signingKeys}. */
|
|
23
|
+
activeKid?: string;
|
|
5
24
|
}
|
|
6
25
|
export interface AccessTokenPayload {
|
|
7
26
|
/**
|
|
@@ -42,6 +61,19 @@ export interface AccessTokenPayload {
|
|
|
42
61
|
* Validates the secret strength to prevent deployment with default/weak secrets.
|
|
43
62
|
*/
|
|
44
63
|
export declare function configureJwt(config: JwtConfig): void;
|
|
64
|
+
/**
|
|
65
|
+
* The public keys, in JWKS form, for `/.well-known/jwks.json`.
|
|
66
|
+
*
|
|
67
|
+
* An empty `keys` array on a backend with no asymmetric keys configured is the
|
|
68
|
+
* correct answer rather than a 404: it says "this issuer publishes none",
|
|
69
|
+
* which a verifier can act on, where a 404 is indistinguishable from a
|
|
70
|
+
* misconfigured URL.
|
|
71
|
+
*/
|
|
72
|
+
export declare function getJwks(): {
|
|
73
|
+
keys: PublicJwk[];
|
|
74
|
+
};
|
|
75
|
+
/** Is this backend signing access tokens asymmetrically? */
|
|
76
|
+
export declare function hasAsymmetricSigningKey(): boolean;
|
|
45
77
|
/**
|
|
46
78
|
* Has this server been given a JWT secret?
|
|
47
79
|
*
|
|
@@ -141,12 +173,40 @@ export declare function verifyMfaPendingToken(token: string): {
|
|
|
141
173
|
export interface DownloadTokenPayload {
|
|
142
174
|
purpose: "file-read";
|
|
143
175
|
path: string;
|
|
176
|
+
/**
|
|
177
|
+
* The storage source the grant is good for, canonicalized by
|
|
178
|
+
* {@link canonicalStorageId}. Always present on a decoded payload; see
|
|
179
|
+
* {@link verifyDownloadToken} for how tokens minted before this claim
|
|
180
|
+
* existed are read.
|
|
181
|
+
*/
|
|
182
|
+
storageId: string;
|
|
144
183
|
}
|
|
145
184
|
/**
|
|
146
185
|
* Generate a short-lived download token scoped to a specific file path or prefix
|
|
186
|
+
* *within one storage source*.
|
|
187
|
+
*
|
|
188
|
+
* Both halves of that scope are load-bearing. A key is only unique inside its
|
|
189
|
+
* own bucket, and a project with more than one source routinely holds the same
|
|
190
|
+
* key in several of them — `avatars/u1.png` in `(default)` and in `media` are
|
|
191
|
+
* different objects, quite possibly with different owners. A token that names
|
|
192
|
+
* only the path is therefore a grant on every source at once: authorize a read
|
|
193
|
+
* on the source whose `storageAuthorize` hook says yes, then spend the token
|
|
194
|
+
* against `?storageId=` pointing somewhere else.
|
|
195
|
+
*
|
|
196
|
+
* `storageId` is optional here only because omitting it *is* the default
|
|
197
|
+
* source, which is what the overwhelming majority of deployments have. A mint
|
|
198
|
+
* site that forgets to pass a named source produces a default-scoped token,
|
|
199
|
+
* which fails closed at `/file/*` rather than over-granting.
|
|
147
200
|
*/
|
|
148
|
-
export declare function generateDownloadToken(path: string, expiresInSeconds?: number): string;
|
|
201
|
+
export declare function generateDownloadToken(path: string, expiresInSeconds?: number, storageId?: string | null): string;
|
|
149
202
|
/**
|
|
150
|
-
* Verify and decode a download token
|
|
203
|
+
* Verify and decode a download token.
|
|
204
|
+
*
|
|
205
|
+
* A token minted before `storageId` existed carries no such claim. It is read
|
|
206
|
+
* as a grant on the **default source** rather than on all of them: that is the
|
|
207
|
+
* fail-closed reading, and it is what such a token almost always was, since a
|
|
208
|
+
* named source has to be asked for explicitly. The cost is bounded by the
|
|
209
|
+
* five-minute TTL — for at most that long after a deploy, an in-flight token
|
|
210
|
+
* for a *named* source is refused and the client re-fetches `/metadata`.
|
|
151
211
|
*/
|
|
152
212
|
export declare function verifyDownloadToken(token: string): DownloadTokenPayload | null;
|