@andesine/sdk 0.4.0 → 0.5.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.
@@ -0,0 +1,220 @@
1
+ import { n as AndesineAPIError } from "./answer-stream-7N_gLhVS.js";
2
+ import { t as createClient } from "./client-DioqVOxl.js";
3
+ //#region src/extensions/jwt.ts
4
+ const TOKEN_LIFETIME = 300;
5
+ const RENEWAL_MARGIN = 60;
6
+ const MAX_CACHED_TOKENS = 1e3;
7
+ const ALGORITHMS = {
8
+ "Ed25519": {
9
+ alg: "EdDSA",
10
+ importParams: { name: "Ed25519" },
11
+ signParams: { name: "Ed25519" }
12
+ },
13
+ "P-256": {
14
+ alg: "ES256",
15
+ importParams: {
16
+ name: "ECDSA",
17
+ namedCurve: "P-256"
18
+ },
19
+ signParams: {
20
+ name: "ECDSA",
21
+ hash: "SHA-256"
22
+ }
23
+ }
24
+ };
25
+ const encodeBase64URL = (bytes) => {
26
+ return btoa(String.fromCharCode(...bytes)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
27
+ };
28
+ const encodeJSON = (value) => {
29
+ return encodeBase64URL(new TextEncoder().encode(JSON.stringify(value)));
30
+ };
31
+ const parsePrivateKey = (input) => {
32
+ if (typeof input !== "string") return input;
33
+ try {
34
+ return JSON.parse(input);
35
+ } catch {
36
+ throw new TypeError("privateKey must be a JSON Web Key");
37
+ }
38
+ };
39
+ /**
40
+ * Signs extension JWTs with a private JWK (Ed25519 for EdDSA, or P-256 for ES256) that has the
41
+ * registry key ID in `kid`. Tokens are cached per instance and installation until shortly
42
+ * before they expire.
43
+ */
44
+ const createExtensionSigner = (name, privateKey) => {
45
+ const jwk = parsePrivateKey(privateKey);
46
+ const algorithm = ALGORITHMS[jwk.crv];
47
+ const cache = /* @__PURE__ */ new Map();
48
+ if (!algorithm || !jwk.d || !jwk.kid) throw new TypeError("privateKey must be a private Ed25519 or P-256 JWK with a kid");
49
+ let key = null;
50
+ const getKey = () => {
51
+ key ??= crypto.subtle.importKey("jwk", {
52
+ kty: jwk.kty,
53
+ crv: jwk.crv,
54
+ x: jwk.x,
55
+ y: jwk.y,
56
+ d: jwk.d
57
+ }, algorithm.importParams, false, ["sign"]);
58
+ return key;
59
+ };
60
+ return { async sign({ audience, subject }) {
61
+ const now = Math.floor(Date.now() / 1e3);
62
+ const cacheKey = `${audience} ${subject ?? ""}`;
63
+ const cached = cache.get(cacheKey);
64
+ if (cached && cached.expiresAt - RENEWAL_MARGIN > now) return cached.token;
65
+ const header = encodeJSON({
66
+ alg: algorithm.alg,
67
+ kid: jwk.kid,
68
+ typ: "JWT"
69
+ });
70
+ const payload = encodeJSON({
71
+ iss: name,
72
+ ...subject && { sub: subject },
73
+ aud: audience,
74
+ iat: now,
75
+ exp: now + TOKEN_LIFETIME
76
+ });
77
+ const signature = await crypto.subtle.sign(algorithm.signParams, await getKey(), new TextEncoder().encode(`${header}.${payload}`));
78
+ const token = `${header}.${payload}.${encodeBase64URL(new Uint8Array(signature))}`;
79
+ if (cache.size >= MAX_CACHED_TOKENS) cache.clear();
80
+ cache.set(cacheKey, {
81
+ token,
82
+ expiresAt: now + TOKEN_LIFETIME
83
+ });
84
+ return token;
85
+ } };
86
+ };
87
+ //#endregion
88
+ //#region src/extensions.ts
89
+ var ExtensionSessionError = class extends Error {
90
+ code;
91
+ constructor(code) {
92
+ super(`Extension session verification failed: ${code}`);
93
+ this.code = code;
94
+ }
95
+ };
96
+ var ExtensionNotificationError = class extends Error {
97
+ code;
98
+ constructor(code) {
99
+ super(`Extension notification failed: ${code}`);
100
+ this.code = code;
101
+ }
102
+ };
103
+ const isRejection = (error) => {
104
+ return error instanceof AndesineAPIError && [
105
+ 401,
106
+ 403,
107
+ 404
108
+ ].includes(error.status);
109
+ };
110
+ const parseJSON = (text) => {
111
+ try {
112
+ return JSON.parse(text);
113
+ } catch {
114
+ return null;
115
+ }
116
+ };
117
+ const getString = (value, key) => {
118
+ const field = value[key];
119
+ return typeof field === "string" && field ? field : null;
120
+ };
121
+ const normalizeInstance = (instance) => {
122
+ try {
123
+ const url = new URL(instance);
124
+ return url.protocol === "https:" || url.protocol === "http:" ? `${url.origin}${url.pathname}`.replace(/\/+$/, "") : null;
125
+ } catch {
126
+ return null;
127
+ }
128
+ };
129
+ const getHeader = (headers, name) => {
130
+ if (headers instanceof Headers) return headers.get(name);
131
+ const value = headers[name.toLowerCase()];
132
+ return (Array.isArray(value) ? value[0] : value) ?? null;
133
+ };
134
+ /**
135
+ * Creates the helpers of an extension backend. They sign and cache extension JWTs, and accept
136
+ * only the trusted instances; the backend stores no per-installation state.
137
+ * @example
138
+ * const extension = createExtensionBackend({
139
+ * name: "acme/publish",
140
+ * privateKey: process.env.ANDESINE_EXTENSION_KEY!,
141
+ * trustedInstances: ["https://api.andesine.app"]
142
+ * });
143
+ * const session = await extension.verifySession(request.headers);
144
+ */
145
+ const createExtensionBackend = (options) => {
146
+ const signer = createExtensionSigner(options.name, options.privateKey);
147
+ const trusted = new Set(options.trustedInstances.map(normalizeInstance));
148
+ const fetcher = options.client?.fetch ?? globalThis.fetch;
149
+ const getTrustedInstance = (instance) => {
150
+ const normalized = normalizeInstance(instance);
151
+ return normalized && trusted.has(normalized) ? normalized : null;
152
+ };
153
+ const createBackendClient = (instance, extensionID) => {
154
+ const audience = getTrustedInstance(instance);
155
+ if (!audience) throw new TypeError("The instance is not trusted");
156
+ return createClient({
157
+ ...options.client,
158
+ baseURL: audience,
159
+ apiKey: "",
160
+ fetch: async (input, init) => {
161
+ const request = new Request(input, init);
162
+ const token = await signer.sign({
163
+ audience,
164
+ subject: extensionID ?? null
165
+ });
166
+ request.headers.set("Authorization", `Bearer ${token}`);
167
+ return fetcher(request);
168
+ }
169
+ });
170
+ };
171
+ return {
172
+ createClient: createBackendClient,
173
+ async verifySession(headers) {
174
+ const token = /^Bearer\s+(\S+)$/i.exec(getHeader(headers, "Authorization") ?? "")?.[1];
175
+ const extensionID = getHeader(headers, "Andesine-Extension");
176
+ const instance = getHeader(headers, "Andesine-Instance");
177
+ if (!token || !extensionID || !instance) throw new ExtensionSessionError("MISSING_SESSION");
178
+ const audience = getTrustedInstance(instance);
179
+ if (!audience) throw new ExtensionSessionError("UNTRUSTED_INSTANCE");
180
+ const client = createBackendClient(audience, extensionID);
181
+ try {
182
+ return {
183
+ ...await client.extensions.verifySession({ token }),
184
+ extensionID,
185
+ instance: audience,
186
+ client
187
+ };
188
+ } catch (error) {
189
+ if (isRejection(error)) throw new ExtensionSessionError("INVALID_SESSION");
190
+ throw error;
191
+ }
192
+ },
193
+ async receive(body) {
194
+ const notification = typeof body === "string" ? parseJSON(body) : body;
195
+ const isObject = typeof notification === "object" && notification !== null;
196
+ const deliveryID = isObject ? getString(notification, "deliveryID") : null;
197
+ const extensionID = isObject ? getString(notification, "extensionID") : null;
198
+ const instance = isObject ? getString(notification, "instance") : null;
199
+ if (!deliveryID || !extensionID || !instance) throw new ExtensionNotificationError("INVALID_NOTIFICATION");
200
+ const audience = getTrustedInstance(instance);
201
+ if (!audience) throw new ExtensionNotificationError("UNTRUSTED_INSTANCE");
202
+ const client = createBackendClient(audience, extensionID);
203
+ try {
204
+ return {
205
+ ...await client.extensions.getSelfDelivery({ deliveryID }),
206
+ extensionID,
207
+ instance: audience,
208
+ client
209
+ };
210
+ } catch (error) {
211
+ if (isRejection(error)) throw new ExtensionNotificationError("UNKNOWN_DELIVERY");
212
+ throw error;
213
+ }
214
+ }
215
+ };
216
+ };
217
+ //#endregion
218
+ export { ExtensionNotificationError, ExtensionSessionError, createExtensionBackend };
219
+
220
+ //# sourceMappingURL=extensions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extensions.js","names":[],"sources":["../src/extensions/jwt.ts","../src/extensions.ts"],"sourcesContent":["interface ExtensionPrivateKey extends JsonWebKey {\n kid?: string;\n}\ninterface ExtensionTokenClaims {\n /** The instance API URL. */\n audience: string;\n /** The installation's `ext_` ID; null for app-level tokens. */\n subject: string | null;\n}\ninterface ExtensionSigner {\n sign(claims: ExtensionTokenClaims): Promise<string>;\n}\ninterface CachedToken {\n token: string;\n expiresAt: number;\n}\n\n// Andesine accepts tokens up to 300 seconds old; cached tokens renew a minute before that.\nconst TOKEN_LIFETIME = 300;\nconst RENEWAL_MARGIN = 60;\nconst MAX_CACHED_TOKENS = 1_000;\nconst ALGORITHMS = {\n \"Ed25519\": {\n alg: \"EdDSA\",\n importParams: { name: \"Ed25519\" },\n signParams: { name: \"Ed25519\" }\n },\n \"P-256\": {\n alg: \"ES256\",\n importParams: { name: \"ECDSA\", namedCurve: \"P-256\" },\n signParams: { name: \"ECDSA\", hash: \"SHA-256\" }\n }\n} as const;\n\nconst encodeBase64URL = (bytes: Uint8Array): string => {\n return btoa(String.fromCharCode(...bytes))\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n};\nconst encodeJSON = (value: object): string => {\n return encodeBase64URL(new TextEncoder().encode(JSON.stringify(value)));\n};\n// Errors never include the key, which can come from an environment variable.\nconst parsePrivateKey = (input: string | ExtensionPrivateKey): ExtensionPrivateKey => {\n if (typeof input !== \"string\") return input;\n\n try {\n return JSON.parse(input) as ExtensionPrivateKey;\n } catch {\n throw new TypeError(\"privateKey must be a JSON Web Key\");\n }\n};\n/**\n * Signs extension JWTs with a private JWK (Ed25519 for EdDSA, or P-256 for ES256) that has the\n * registry key ID in `kid`. Tokens are cached per instance and installation until shortly\n * before they expire.\n */\nconst createExtensionSigner = (\n name: string,\n privateKey: string | ExtensionPrivateKey\n): ExtensionSigner => {\n const jwk = parsePrivateKey(privateKey);\n const algorithm = ALGORITHMS[jwk.crv as keyof typeof ALGORITHMS];\n const cache = new Map<string, CachedToken>();\n\n if (!algorithm || !jwk.d || !jwk.kid) {\n throw new TypeError(\"privateKey must be a private Ed25519 or P-256 JWK with a kid\");\n }\n\n let key: Promise<CryptoKey> | null = null;\n\n // Imported on first use, so an invalid key fails the first request, not module loading.\n const getKey = (): Promise<CryptoKey> => {\n key ??= crypto.subtle.importKey(\n \"jwk\",\n { kty: jwk.kty, crv: jwk.crv, x: jwk.x, y: jwk.y, d: jwk.d },\n algorithm.importParams,\n false,\n [\"sign\"]\n );\n\n return key;\n };\n\n return {\n async sign({ audience, subject }) {\n const now = Math.floor(Date.now() / 1000);\n const cacheKey = `${audience} ${subject ?? \"\"}`;\n const cached = cache.get(cacheKey);\n\n if (cached && cached.expiresAt - RENEWAL_MARGIN > now) return cached.token;\n\n const header = encodeJSON({ alg: algorithm.alg, kid: jwk.kid, typ: \"JWT\" });\n const payload = encodeJSON({\n iss: name,\n ...(subject && { sub: subject }),\n aud: audience,\n iat: now,\n exp: now + TOKEN_LIFETIME\n });\n const signature = await crypto.subtle.sign(\n algorithm.signParams,\n await getKey(),\n new TextEncoder().encode(`${header}.${payload}`)\n );\n const token = `${header}.${payload}.${encodeBase64URL(new Uint8Array(signature))}`;\n\n if (cache.size >= MAX_CACHED_TOKENS) cache.clear();\n\n cache.set(cacheKey, { token, expiresAt: now + TOKEN_LIFETIME });\n\n return token;\n }\n };\n};\n\nexport { createExtensionSigner };\nexport type { ExtensionPrivateKey, ExtensionSigner };\n","import { createClient, type AndesineClient, type ClientOptions } from \"./client\";\nimport { AndesineAPIError } from \"./error\";\nimport { createExtensionSigner, type ExtensionPrivateKey } from \"./extensions/jwt\";\n\ninterface ExtensionBackendOptions {\n /** The extension's registry name, e.g. `acme/publish`. */\n name: string;\n /**\n * The private JSON Web Key (a JSON string or an object) whose public key is in the registry:\n * Ed25519 (EdDSA) or P-256 (ES256), with the registry key ID in `kid`. Keep it on the server.\n */\n privateKey: string | ExtensionPrivateKey;\n /** API URLs of the Andesine instances that the backend works with, e.g. `https://api.andesine.app`. */\n trustedInstances: string[];\n /** Options for the API clients; the backend sets the base URL and the credential. */\n client?: Omit<ClientOptions, \"baseURL\" | \"apiKey\" | \"accessToken\">;\n}\ninterface ExtensionSessionContext extends VerifiedSession {\n /** The installation's `ext_` ID. */\n extensionID: string;\n /** The instance API URL. */\n instance: string;\n /** An API client for the installation, with the extension's permissions. */\n client: AndesineClient;\n}\ninterface ExtensionDeliveryContext extends ReceivedDelivery {\n /** The installation's `ext_` ID. */\n extensionID: string;\n /** The instance API URL. */\n instance: string;\n /** An API client for the installation, with the extension's permissions. */\n client: AndesineClient;\n}\ninterface ExtensionBackend {\n /**\n * An API client for an installation on a trusted instance, authenticated with extension JWTs.\n * Without `extensionID`, the client is app-level: it can only list installations.\n * @throws TypeError if the instance is not trusted.\n */\n createClient(instance: string, extensionID?: string): AndesineClient;\n /**\n * Verifies a session token from the extension frontend (`backend.fetch` sends the\n * `Authorization`, `Andesine-Extension`, and `Andesine-Instance` headers).\n * @throws ExtensionSessionError if the headers are missing, the instance is not trusted, or\n * the token is invalid or expired. Other API failures throw AndesineAPIError.\n */\n verifySession(headers: HeaderSource): Promise<ExtensionSessionContext>;\n /**\n * Handles the body of an extension webhook request: checks the instance allowlist and fetches\n * the delivery with the installation's JWT, which confirms the notification. Respond with a\n * 2xx status once the event is handled; other responses are retried.\n * @throws ExtensionNotificationError if the body is invalid, the instance is not trusted, or\n * the delivery does not exist for the installation. Other API failures throw AndesineAPIError.\n */\n receive(body: unknown): Promise<ExtensionDeliveryContext>;\n}\n\ntype VerifiedSession = Awaited<ReturnType<AndesineClient[\"extensions\"][\"verifySession\"]>>;\ntype ReceivedDelivery = Awaited<ReturnType<AndesineClient[\"extensions\"][\"getSelfDelivery\"]>>;\ntype HeaderSource = Headers | Record<string, string | string[] | undefined>;\ntype ExtensionSessionErrorCode = \"MISSING_SESSION\" | \"UNTRUSTED_INSTANCE\" | \"INVALID_SESSION\";\ntype ExtensionNotificationErrorCode =\n \"INVALID_NOTIFICATION\" | \"UNTRUSTED_INSTANCE\" | \"UNKNOWN_DELIVERY\";\n\nclass ExtensionSessionError extends Error {\n constructor(readonly code: ExtensionSessionErrorCode) {\n super(`Extension session verification failed: ${code}`);\n }\n}\n\nclass ExtensionNotificationError extends Error {\n constructor(readonly code: ExtensionNotificationErrorCode) {\n super(`Extension notification failed: ${code}`);\n }\n}\n\nconst isRejection = (error: unknown): boolean => {\n return error instanceof AndesineAPIError && [401, 403, 404].includes(error.status);\n};\nconst parseJSON = (text: string): unknown => {\n try {\n return JSON.parse(text);\n } catch {\n return null;\n }\n};\nconst getString = (value: object, key: string): string | null => {\n const field = (value as Record<string, unknown>)[key];\n\n return typeof field === \"string\" && field ? field : null;\n};\nconst normalizeInstance = (instance: string): string | null => {\n try {\n const url = new URL(instance);\n const isHTTP = url.protocol === \"https:\" || url.protocol === \"http:\";\n\n return isHTTP ? `${url.origin}${url.pathname}`.replace(/\\/+$/, \"\") : null;\n } catch {\n return null;\n }\n};\nconst getHeader = (headers: HeaderSource, name: string): string | null => {\n if (headers instanceof Headers) return headers.get(name);\n\n const value = headers[name.toLowerCase()];\n\n return (Array.isArray(value) ? value[0] : value) ?? null;\n};\n/**\n * Creates the helpers of an extension backend. They sign and cache extension JWTs, and accept\n * only the trusted instances; the backend stores no per-installation state.\n * @example\n * const extension = createExtensionBackend({\n * name: \"acme/publish\",\n * privateKey: process.env.ANDESINE_EXTENSION_KEY!,\n * trustedInstances: [\"https://api.andesine.app\"]\n * });\n * const session = await extension.verifySession(request.headers);\n */\nconst createExtensionBackend = (options: ExtensionBackendOptions): ExtensionBackend => {\n const signer = createExtensionSigner(options.name, options.privateKey);\n const trusted = new Set(options.trustedInstances.map(normalizeInstance));\n const fetcher = options.client?.fetch ?? globalThis.fetch;\n const getTrustedInstance = (instance: string): string | null => {\n const normalized = normalizeInstance(instance);\n\n return normalized && trusted.has(normalized) ? normalized : null;\n };\n const createBackendClient = (instance: string, extensionID?: string): AndesineClient => {\n const audience = getTrustedInstance(instance);\n\n if (!audience) throw new TypeError(\"The instance is not trusted\");\n\n return createClient({\n ...options.client,\n baseURL: audience,\n apiKey: \"\",\n fetch: async (input, init) => {\n const request = new Request(input, init);\n const token = await signer.sign({ audience, subject: extensionID ?? null });\n\n request.headers.set(\"Authorization\", `Bearer ${token}`);\n\n return fetcher(request);\n }\n });\n };\n\n return {\n createClient: createBackendClient,\n async verifySession(headers) {\n const token = /^Bearer\\s+(\\S+)$/i.exec(getHeader(headers, \"Authorization\") ?? \"\")?.[1];\n const extensionID = getHeader(headers, \"Andesine-Extension\");\n const instance = getHeader(headers, \"Andesine-Instance\");\n\n if (!token || !extensionID || !instance) throw new ExtensionSessionError(\"MISSING_SESSION\");\n\n const audience = getTrustedInstance(instance);\n\n if (!audience) throw new ExtensionSessionError(\"UNTRUSTED_INSTANCE\");\n\n const client = createBackendClient(audience, extensionID);\n\n try {\n const session = await client.extensions.verifySession({ token });\n\n return { ...session, extensionID, instance: audience, client };\n } catch (error) {\n if (isRejection(error)) throw new ExtensionSessionError(\"INVALID_SESSION\");\n\n throw error;\n }\n },\n async receive(body) {\n const notification = typeof body === \"string\" ? parseJSON(body) : body;\n const isObject = typeof notification === \"object\" && notification !== null;\n const deliveryID = isObject ? getString(notification, \"deliveryID\") : null;\n const extensionID = isObject ? getString(notification, \"extensionID\") : null;\n const instance = isObject ? getString(notification, \"instance\") : null;\n\n if (!deliveryID || !extensionID || !instance) {\n throw new ExtensionNotificationError(\"INVALID_NOTIFICATION\");\n }\n\n const audience = getTrustedInstance(instance);\n\n if (!audience) throw new ExtensionNotificationError(\"UNTRUSTED_INSTANCE\");\n\n const client = createBackendClient(audience, extensionID);\n\n try {\n const delivery = await client.extensions.getSelfDelivery({ deliveryID });\n\n return { ...delivery, extensionID, instance: audience, client };\n } catch (error) {\n if (isRejection(error)) throw new ExtensionNotificationError(\"UNKNOWN_DELIVERY\");\n\n throw error;\n }\n }\n };\n};\n\nexport { createExtensionBackend, ExtensionNotificationError, ExtensionSessionError };\nexport type {\n ExtensionBackend,\n ExtensionBackendOptions,\n ExtensionDeliveryContext,\n ExtensionNotificationErrorCode,\n ExtensionPrivateKey,\n ExtensionSessionContext,\n ExtensionSessionErrorCode\n};\n"],"mappings":"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"}