@deploybaseline/microsoft 0.0.0-stage → 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/LICENSE +202 -0
- package/NOTICE +11 -0
- package/README.md +35 -2
- package/THIRD_PARTY_NOTICES.md +622 -0
- package/api-reference.json +59783 -0
- package/dist/chunk-BEGIZZSN.js +4762 -0
- package/dist/chunk-BEGIZZSN.js.map +1 -0
- package/dist/chunk-G7326MH2.js +3837 -0
- package/dist/chunk-G7326MH2.js.map +1 -0
- package/dist/chunk-PBJS3RQA.js +41 -0
- package/dist/chunk-PBJS3RQA.js.map +1 -0
- package/dist/fonts/GeistPixel-Square.woff2 +0 -0
- package/dist/fonts/favicon.ico +0 -0
- package/dist/fonts/geist-sans.woff2 +0 -0
- package/dist/index.d.ts +355 -0
- package/dist/index.js +58927 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.d.ts +5 -0
- package/dist/manifest.js +7 -0
- package/dist/manifest.js.map +1 -0
- package/dist/provider.d.ts +2 -0
- package/dist/provider.js +10 -0
- package/dist/provider.js.map +1 -0
- package/dist/upload-L2WXFOLU.js +11 -0
- package/dist/upload-L2WXFOLU.js.map +1 -0
- package/init.json +20 -0
- package/package.json +93 -3
|
@@ -0,0 +1,3837 @@
|
|
|
1
|
+
// ../core/dist/index.js
|
|
2
|
+
import { timingSafeEqual } from "crypto";
|
|
3
|
+
import { createPublicKey } from "crypto";
|
|
4
|
+
|
|
5
|
+
// ../../../node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/lib/buffer_utils.js
|
|
6
|
+
var encoder = new TextEncoder();
|
|
7
|
+
var decoder = new TextDecoder();
|
|
8
|
+
var strictDecoder = new TextDecoder("utf-8", { fatal: true });
|
|
9
|
+
var MAX_INT32 = 2 ** 32;
|
|
10
|
+
function concat(...buffers) {
|
|
11
|
+
const size = buffers.reduce((acc, { length }) => acc + length, 0), buf = new Uint8Array(size);
|
|
12
|
+
let i = 0;
|
|
13
|
+
for (const buffer of buffers)
|
|
14
|
+
buf.set(buffer, i), i += buffer.length;
|
|
15
|
+
return buf;
|
|
16
|
+
}
|
|
17
|
+
var NON_ASCII = /[^\x00-\x7f]/;
|
|
18
|
+
function encode(string) {
|
|
19
|
+
if (typeof string == "string" && string.length >= 128) {
|
|
20
|
+
if (NON_ASCII.test(string))
|
|
21
|
+
throw new TypeError("non-ASCII string encountered in encode()");
|
|
22
|
+
return encoder.encode(string);
|
|
23
|
+
}
|
|
24
|
+
const bytes = new Uint8Array(string.length);
|
|
25
|
+
for (let i = 0; i < string.length; i++) {
|
|
26
|
+
const code = string.charCodeAt(i);
|
|
27
|
+
if (code > 127)
|
|
28
|
+
throw new TypeError("non-ASCII string encountered in encode()");
|
|
29
|
+
bytes[i] = code;
|
|
30
|
+
}
|
|
31
|
+
return bytes;
|
|
32
|
+
}
|
|
33
|
+
function encodeBase64(input, url = false) {
|
|
34
|
+
if (Uint8Array.prototype.toBase64)
|
|
35
|
+
return input.toBase64({ alphabet: url ? "base64url" : "base64", omitPadding: url });
|
|
36
|
+
const CHUNK_SIZE = 32768, arr = [];
|
|
37
|
+
for (let i = 0; i < input.length; i += CHUNK_SIZE)
|
|
38
|
+
arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE)));
|
|
39
|
+
const encoded = btoa(arr.join(""));
|
|
40
|
+
return url ? encoded.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_") : encoded;
|
|
41
|
+
}
|
|
42
|
+
function decodeBase64(encoded, url = false) {
|
|
43
|
+
if (Uint8Array.fromBase64)
|
|
44
|
+
return Uint8Array.fromBase64(encoded, { alphabet: url ? "base64url" : "base64" });
|
|
45
|
+
if (url) {
|
|
46
|
+
if (encoded.includes("+") || encoded.includes("/"))
|
|
47
|
+
throw new TypeError("Invalid base64url");
|
|
48
|
+
encoded = encoded.replace(/-/g, "+").replace(/_/g, "/");
|
|
49
|
+
}
|
|
50
|
+
const binary = atob(encoded), bytes = new Uint8Array(binary.length);
|
|
51
|
+
for (let i = 0; i < binary.length; i++)
|
|
52
|
+
bytes[i] = binary.charCodeAt(i);
|
|
53
|
+
return bytes;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ../../../node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/util/errors.js
|
|
57
|
+
var JOSEError = class extends Error {
|
|
58
|
+
static code = "ERR_JOSE_GENERIC";
|
|
59
|
+
code = "ERR_JOSE_GENERIC";
|
|
60
|
+
constructor(message2, options) {
|
|
61
|
+
super(message2, options), this.name = this.constructor.name, Error.captureStackTrace?.(this, this.constructor);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
var JWTClaimValidationFailed = class extends JOSEError {
|
|
65
|
+
static code = "ERR_JWT_CLAIM_VALIDATION_FAILED";
|
|
66
|
+
code = "ERR_JWT_CLAIM_VALIDATION_FAILED";
|
|
67
|
+
claim;
|
|
68
|
+
reason;
|
|
69
|
+
payload;
|
|
70
|
+
constructor(message2, payload, claim = "unspecified", reason = "unspecified") {
|
|
71
|
+
super(message2, { cause: { claim, reason, payload } }), this.claim = claim, this.reason = reason, this.payload = payload;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var JWTExpired = class extends JOSEError {
|
|
75
|
+
static code = "ERR_JWT_EXPIRED";
|
|
76
|
+
code = "ERR_JWT_EXPIRED";
|
|
77
|
+
claim;
|
|
78
|
+
reason;
|
|
79
|
+
payload;
|
|
80
|
+
constructor(message2, payload, claim = "unspecified", reason = "unspecified") {
|
|
81
|
+
super(message2, { cause: { claim, reason, payload } }), this.claim = claim, this.reason = reason, this.payload = payload;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
var JOSEAlgNotAllowed = class extends JOSEError {
|
|
85
|
+
static code = "ERR_JOSE_ALG_NOT_ALLOWED";
|
|
86
|
+
code = "ERR_JOSE_ALG_NOT_ALLOWED";
|
|
87
|
+
};
|
|
88
|
+
var JOSENotSupported = class extends JOSEError {
|
|
89
|
+
static code = "ERR_JOSE_NOT_SUPPORTED";
|
|
90
|
+
code = "ERR_JOSE_NOT_SUPPORTED";
|
|
91
|
+
};
|
|
92
|
+
var JWSInvalid = class extends JOSEError {
|
|
93
|
+
static code = "ERR_JWS_INVALID";
|
|
94
|
+
code = "ERR_JWS_INVALID";
|
|
95
|
+
};
|
|
96
|
+
var JWTInvalid = class extends JOSEError {
|
|
97
|
+
static code = "ERR_JWT_INVALID";
|
|
98
|
+
code = "ERR_JWT_INVALID";
|
|
99
|
+
};
|
|
100
|
+
var JWSSignatureVerificationFailed = class extends JOSEError {
|
|
101
|
+
static code = "ERR_JWS_SIGNATURE_VERIFICATION_FAILED";
|
|
102
|
+
code = "ERR_JWS_SIGNATURE_VERIFICATION_FAILED";
|
|
103
|
+
constructor(message2 = "signature verification failed", options) {
|
|
104
|
+
super(message2, options);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// ../../../node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/util/base64url.js
|
|
109
|
+
var invalid = "The input to be decoded is not correctly encoded.";
|
|
110
|
+
function decode(input) {
|
|
111
|
+
try {
|
|
112
|
+
return decodeBase64(typeof input == "string" ? input : decoder.decode(input), true);
|
|
113
|
+
} catch (cause) {
|
|
114
|
+
throw new TypeError(invalid, { cause });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function encode2(input) {
|
|
118
|
+
return encodeBase64(typeof input == "string" ? encoder.encode(input) : input, true);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ../../../node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/lib/validate.js
|
|
122
|
+
function isObject(input) {
|
|
123
|
+
if (typeof input != "object" || input === null || Object.prototype.toString.call(input) !== "[object Object]")
|
|
124
|
+
return false;
|
|
125
|
+
const prototype = Object.getPrototypeOf(input);
|
|
126
|
+
return prototype === null || Object.getPrototypeOf(prototype) === null;
|
|
127
|
+
}
|
|
128
|
+
function isDisjoint(...headers) {
|
|
129
|
+
const parameters = /* @__PURE__ */ new Set();
|
|
130
|
+
for (const header of headers)
|
|
131
|
+
if (header)
|
|
132
|
+
for (const parameter of Object.keys(header)) {
|
|
133
|
+
if (parameters.has(parameter))
|
|
134
|
+
return false;
|
|
135
|
+
parameters.add(parameter);
|
|
136
|
+
}
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
function assertNotSet(value, name) {
|
|
140
|
+
if (value !== void 0)
|
|
141
|
+
throw new TypeError(`${name} can only be called once`);
|
|
142
|
+
}
|
|
143
|
+
function decodeBase64url(value, label, ErrorClass) {
|
|
144
|
+
try {
|
|
145
|
+
return decode(value);
|
|
146
|
+
} catch {
|
|
147
|
+
throw new ErrorClass(`Failed to base64url decode the ${label}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function encodeBase64url(value, label, ErrorClass) {
|
|
151
|
+
try {
|
|
152
|
+
return encode(value);
|
|
153
|
+
} catch {
|
|
154
|
+
throw new ErrorClass(`The ${label} is not a valid base64url string`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function parseJoseHeader(b64, ErrorClass, message2) {
|
|
158
|
+
let parsed;
|
|
159
|
+
try {
|
|
160
|
+
parsed = JSON.parse(strictDecoder.decode(decode(b64)));
|
|
161
|
+
} catch {
|
|
162
|
+
throw new ErrorClass(message2);
|
|
163
|
+
}
|
|
164
|
+
if (!isObject(parsed))
|
|
165
|
+
throw new ErrorClass(message2);
|
|
166
|
+
return parsed;
|
|
167
|
+
}
|
|
168
|
+
var JWS_RECOGNIZED = { __proto__: null, b64: true };
|
|
169
|
+
function validateAlgorithms(option, algorithms) {
|
|
170
|
+
if (algorithms !== void 0 && (!Array.isArray(algorithms) || algorithms.some((s) => typeof s != "string")))
|
|
171
|
+
throw new TypeError(`"${option}" option must be an array of strings`);
|
|
172
|
+
return algorithms === void 0 ? void 0 : new Set(algorithms);
|
|
173
|
+
}
|
|
174
|
+
function validateCritDuplicates(Err, protectedHeader) {
|
|
175
|
+
const { crit } = protectedHeader ?? {};
|
|
176
|
+
if (Array.isArray(crit) && new Set(crit).size !== crit.length)
|
|
177
|
+
throw new Err('"crit" (Critical) Header Parameter MUST NOT contain duplicate values');
|
|
178
|
+
}
|
|
179
|
+
function validateCrit(Err, recognizedDefault, recognizedOption, protectedHeader, joseHeader) {
|
|
180
|
+
if (joseHeader.crit !== void 0 && protectedHeader?.crit === void 0)
|
|
181
|
+
throw new Err('"crit" (Critical) Header Parameter MUST be integrity protected');
|
|
182
|
+
if (!protectedHeader || protectedHeader.crit === void 0)
|
|
183
|
+
return [];
|
|
184
|
+
if (!Array.isArray(protectedHeader.crit) || protectedHeader.crit.length === 0 || protectedHeader.crit.some((input) => typeof input != "string" || input.length === 0))
|
|
185
|
+
throw new Err('"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present');
|
|
186
|
+
const recognized = recognizedOption === void 0 ? recognizedDefault : { __proto__: null, ...recognizedOption, ...recognizedDefault };
|
|
187
|
+
for (const parameter of protectedHeader.crit) {
|
|
188
|
+
if (!(parameter in recognized))
|
|
189
|
+
throw new JOSENotSupported(`Extension Header Parameter "${parameter}" is not recognized`);
|
|
190
|
+
if (!Object.hasOwn(joseHeader, parameter) || joseHeader[parameter] === void 0)
|
|
191
|
+
throw new Err(`Extension Header Parameter "${parameter}" is missing`);
|
|
192
|
+
if (recognized[parameter] && (!Object.hasOwn(protectedHeader, parameter) || protectedHeader[parameter] === void 0))
|
|
193
|
+
throw new Err(`Extension Header Parameter "${parameter}" MUST be integrity protected`);
|
|
194
|
+
}
|
|
195
|
+
return protectedHeader.crit;
|
|
196
|
+
}
|
|
197
|
+
function validateB64(protectedHeader, extensions) {
|
|
198
|
+
if (extensions.includes("b64")) {
|
|
199
|
+
const b64 = protectedHeader.b64;
|
|
200
|
+
if (typeof b64 != "boolean")
|
|
201
|
+
throw new JWSInvalid('The "b64" (base64url-encode payload) Header Parameter must be a boolean');
|
|
202
|
+
return b64;
|
|
203
|
+
}
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
function serializeJoseHeader(Err, header) {
|
|
207
|
+
let serialized, parsed;
|
|
208
|
+
try {
|
|
209
|
+
serialized = JSON.stringify(header), parsed = JSON.parse(serialized);
|
|
210
|
+
} catch (cause) {
|
|
211
|
+
throw new Err("JOSE Header is not valid JSON", { cause });
|
|
212
|
+
}
|
|
213
|
+
if (!isObject(parsed))
|
|
214
|
+
throw new Err("JOSE Header is not a JSON object");
|
|
215
|
+
return [parsed, serialized];
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ../../../node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/lib/key.js
|
|
219
|
+
var tag = (key) => key[Symbol.toStringTag];
|
|
220
|
+
var jwkMatchesOp = (entry, key, usage) => {
|
|
221
|
+
const { alg } = entry;
|
|
222
|
+
if (key.use !== void 0) {
|
|
223
|
+
const expected = usage === "sign" || usage === "verify" ? "sig" : "enc";
|
|
224
|
+
if (key.use !== expected)
|
|
225
|
+
throw new TypeError(`Invalid key for this operation, its "use" must be "${expected}" when present`);
|
|
226
|
+
}
|
|
227
|
+
if (key.alg !== void 0 && key.alg !== alg)
|
|
228
|
+
throw new TypeError(`Invalid key for this operation, its "alg" must be "${alg}" when present`);
|
|
229
|
+
if (Array.isArray(key.key_ops)) {
|
|
230
|
+
const expectedKeyOp = usage === "encrypt" || usage === "decrypt" ? entry.ops?.[usage === "encrypt" ? 0 : 1] : usage;
|
|
231
|
+
if (expectedKeyOp && !key.key_ops.includes(expectedKeyOp))
|
|
232
|
+
throw new TypeError(`Invalid key for this operation, its "key_ops" must include "${expectedKeyOp}" when present`);
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
async function prepareKey(entry, key, usage) {
|
|
236
|
+
const { alg, secret } = entry, privateKey = usage === "decrypt" || usage === "sign";
|
|
237
|
+
if (secret && key instanceof Uint8Array)
|
|
238
|
+
return key;
|
|
239
|
+
let normalized, keyObject;
|
|
240
|
+
if (isObject(key)) {
|
|
241
|
+
if (normalized = normalizeJwk(key), typeof normalized.kty != "string")
|
|
242
|
+
throw invalidKeyType(alg, key, secret);
|
|
243
|
+
if (!(secret ? normalized.kty === "oct" && typeof normalized.k == "string" : normalized.kty !== "oct" && (privateKey ? normalized.kty === "AKP" && typeof normalized.priv == "string" || typeof normalized.d == "string" : normalized.d === void 0 && normalized.priv === void 0)))
|
|
244
|
+
throw new TypeError(secret ? 'JSON Web Key for symmetric algorithms must have JWK "kty" (Key Type) equal to "oct" and the JWK "k" (Key Value) present' : `JSON Web Key for this operation must be a ${privateKey ? "private" : "public"} JWK`);
|
|
245
|
+
if (jwkMatchesOp(entry, normalized, usage), normalized.kty === "oct")
|
|
246
|
+
return decode(normalized.k);
|
|
247
|
+
if (!Object.isFrozen(key)) {
|
|
248
|
+
const { key_ops } = key;
|
|
249
|
+
Array.isArray(key_ops) && Object.freeze(key_ops), Object.freeze(key);
|
|
250
|
+
}
|
|
251
|
+
} else {
|
|
252
|
+
if (!isKeyLike(key))
|
|
253
|
+
throw invalidKeyType(alg, key, secret);
|
|
254
|
+
const expectedType = secret ? "secret" : privateKey ? "private" : "public";
|
|
255
|
+
if (key.type !== expectedType && (secret || ["secret", "public", "private"].includes(key.type)))
|
|
256
|
+
throw new TypeError(`${tag(key)} instances must be of type "${expectedType}" for the ${alg} algorithm`);
|
|
257
|
+
if (isCryptoKey(key))
|
|
258
|
+
return key;
|
|
259
|
+
if (keyObject = key, keyObject.type === "secret")
|
|
260
|
+
return keyObject.export();
|
|
261
|
+
}
|
|
262
|
+
cache ||= /* @__PURE__ */ new WeakMap();
|
|
263
|
+
const cacheKey = key;
|
|
264
|
+
let cached = cache.get(cacheKey);
|
|
265
|
+
if (cached?.[alg])
|
|
266
|
+
return cached[alg];
|
|
267
|
+
if (cached || cache.set(cacheKey, cached = {}), keyObject && typeof keyObject.toCryptoKey == "function") {
|
|
268
|
+
const isPublic = keyObject.type === "public", crv = nist[keyObject.asymmetricKeyDetails?.namedCurve], params = entry.resolve?.({ crv, asymmetricKeyType: keyObject.asymmetricKeyType }) ?? entry.subtle;
|
|
269
|
+
return cached[alg] = keyObject.toCryptoKey(params, isPublic, entry.usages[isPublic ? 0 : 1]);
|
|
270
|
+
}
|
|
271
|
+
return normalized ??= keyObject.export({ format: "jwk" }), normalized.alg = alg, cached[alg] = await jwkToKey(entry, normalized);
|
|
272
|
+
}
|
|
273
|
+
var cache;
|
|
274
|
+
var nist = {
|
|
275
|
+
__proto__: null,
|
|
276
|
+
prime256v1: "P-256",
|
|
277
|
+
secp384r1: "P-384",
|
|
278
|
+
secp521r1: "P-521"
|
|
279
|
+
};
|
|
280
|
+
var isCryptoKey = (key) => {
|
|
281
|
+
if (key?.[Symbol.toStringTag] === "CryptoKey")
|
|
282
|
+
return true;
|
|
283
|
+
try {
|
|
284
|
+
return key instanceof CryptoKey;
|
|
285
|
+
} catch {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
var isKeyObject = (key) => key?.[Symbol.toStringTag] === "KeyObject";
|
|
290
|
+
var isKeyLike = (key) => isCryptoKey(key) || isKeyObject(key);
|
|
291
|
+
function message(msg, actual, ...types) {
|
|
292
|
+
if (types.length > 2) {
|
|
293
|
+
const last = types.pop();
|
|
294
|
+
msg += `one of type ${types.join(", ")}, or ${last}.`;
|
|
295
|
+
} else types.length === 2 ? msg += `one of type ${types[0]} or ${types[1]}.` : msg += `of type ${types[0]}.`;
|
|
296
|
+
return actual == null ? msg += ` Received ${actual}` : typeof actual == "function" && actual.name ? msg += ` Received function ${actual.name}` : typeof actual == "object" && actual != null && actual.constructor?.name && (msg += ` Received an instance of ${actual.constructor.name}`), msg;
|
|
297
|
+
}
|
|
298
|
+
function invalidKeyType(alg, actual, secret) {
|
|
299
|
+
const types = ["CryptoKey", "KeyObject", "JSON Web Key"];
|
|
300
|
+
return secret && types.push("Uint8Array"), new TypeError(message(`Key for the ${alg} algorithm must be `, actual, ...types));
|
|
301
|
+
}
|
|
302
|
+
var unusable = (name, prop = "algorithm.name") => new TypeError(`CryptoKey does not support this operation, its ${prop} must be ${name}`);
|
|
303
|
+
function checkUsage(key, usage) {
|
|
304
|
+
if (usage && !key.usages.includes(usage))
|
|
305
|
+
throw new TypeError(`CryptoKey does not support this operation, its usages must include ${usage}.`);
|
|
306
|
+
}
|
|
307
|
+
function checkModulusLength(alg, key) {
|
|
308
|
+
const { modulusLength } = key.algorithm;
|
|
309
|
+
if (typeof modulusLength != "number" || modulusLength < 2048)
|
|
310
|
+
throw new TypeError(`${alg} requires key modulusLength to be 2048 bits or larger`);
|
|
311
|
+
}
|
|
312
|
+
function checkCryptoKey(key, expected, usage) {
|
|
313
|
+
const algorithm = key.algorithm;
|
|
314
|
+
if (algorithm.name !== expected.name)
|
|
315
|
+
throw unusable(expected.name);
|
|
316
|
+
if (expected.hash && algorithm.hash?.name !== expected.hash)
|
|
317
|
+
throw unusable(expected.hash, "algorithm.hash");
|
|
318
|
+
if (expected.namedCurve && algorithm.namedCurve !== expected.namedCurve)
|
|
319
|
+
throw unusable(expected.namedCurve, "algorithm.namedCurve");
|
|
320
|
+
if (expected.length !== void 0 && algorithm.length !== expected.length)
|
|
321
|
+
throw unusable(expected.length, "algorithm.length");
|
|
322
|
+
checkUsage(key, usage);
|
|
323
|
+
}
|
|
324
|
+
function snapshotJwk(jwk) {
|
|
325
|
+
return { __proto__: null, ...jwk };
|
|
326
|
+
}
|
|
327
|
+
function normalizeJwk(jwk) {
|
|
328
|
+
const normalized = snapshotJwk(jwk);
|
|
329
|
+
if (normalized.ext !== void 0 && typeof normalized.ext != "boolean")
|
|
330
|
+
throw new TypeError('"ext" (Extractable) Parameter must be a boolean');
|
|
331
|
+
if (normalized.key_ops !== void 0) {
|
|
332
|
+
const value = normalized.key_ops, keyOps = Array.isArray(value) ? [...value] : void 0;
|
|
333
|
+
if (!keyOps || keyOps.some((operation) => typeof operation != "string") || new Set(keyOps).size !== keyOps.length)
|
|
334
|
+
throw new TypeError('"key_ops" (Key Operations) Parameter must be an array of unique strings');
|
|
335
|
+
normalized.key_ops = keyOps;
|
|
336
|
+
}
|
|
337
|
+
return normalized;
|
|
338
|
+
}
|
|
339
|
+
function validateExtractableOption(extractable) {
|
|
340
|
+
if (extractable !== void 0 && typeof extractable != "boolean")
|
|
341
|
+
throw new TypeError('"extractable" option must be a boolean');
|
|
342
|
+
return extractable;
|
|
343
|
+
}
|
|
344
|
+
async function jwkToKey(entry, jwk, extractable) {
|
|
345
|
+
if (!entry.kty.includes(jwk.kty))
|
|
346
|
+
throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
|
|
347
|
+
const algorithm = entry.resolve?.({ kty: jwk.kty, crv: jwk.crv }) ?? entry.subtle, isPrivate = !!(jwk.d || jwk.priv), keyData = { ...jwk, ext: extractable ?? jwk.ext };
|
|
348
|
+
return keyData.kty !== "AKP" && delete keyData.alg, delete keyData.use, crypto.subtle.importKey("jwk", keyData, algorithm, keyData.ext ?? !isPrivate, jwk.key_ops ?? entry.usages[isPrivate ? 1 : 0]);
|
|
349
|
+
}
|
|
350
|
+
async function rawKey(key, expected, usage, extractable = false) {
|
|
351
|
+
return key instanceof Uint8Array && (key = await crypto.subtle.importKey("raw", key, expected, extractable, [usage])), checkCryptoKey(key, expected, usage), key;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// ../../../node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/lib/key_descriptor.js
|
|
355
|
+
function table(entries) {
|
|
356
|
+
const out = { __proto__: null };
|
|
357
|
+
for (const alg in entries)
|
|
358
|
+
out[alg] = { ...entries[alg], alg };
|
|
359
|
+
return out;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// ../../../node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/lib/jwe_algorithms.js
|
|
363
|
+
var wrap = [
|
|
364
|
+
["encrypt", "wrapKey"],
|
|
365
|
+
["decrypt", "unwrapKey"]
|
|
366
|
+
];
|
|
367
|
+
var derive = [[], ["deriveBits"]];
|
|
368
|
+
var none = [[], []];
|
|
369
|
+
function rsaes(bits) {
|
|
370
|
+
return {
|
|
371
|
+
kty: ["RSA"],
|
|
372
|
+
mode: "key-encryption",
|
|
373
|
+
subtle: { name: "RSA-OAEP", hash: `SHA-${bits}` },
|
|
374
|
+
usages: wrap,
|
|
375
|
+
ops: ["wrapKey", "unwrapKey"]
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
function ecdh(mode) {
|
|
379
|
+
return {
|
|
380
|
+
kty: ["EC", "OKP"],
|
|
381
|
+
mode,
|
|
382
|
+
subtle: { name: "ECDH" },
|
|
383
|
+
resolve: ({ kty, crv, asymmetricKeyType }) => {
|
|
384
|
+
if (crv === "X25519" || asymmetricKeyType === "x25519")
|
|
385
|
+
return { name: "X25519" };
|
|
386
|
+
if (kty === "OKP")
|
|
387
|
+
throw new JOSENotSupported('Invalid or unsupported JWK "alg" (Algorithm) Parameter value');
|
|
388
|
+
return { name: "ECDH", namedCurve: crv };
|
|
389
|
+
},
|
|
390
|
+
usages: derive,
|
|
391
|
+
ops: [void 0, "deriveBits"]
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
function aeskw(bits, gcm = false) {
|
|
395
|
+
return {
|
|
396
|
+
kty: ["oct"],
|
|
397
|
+
mode: "key-wrapping",
|
|
398
|
+
secret: true,
|
|
399
|
+
subtle: { name: gcm ? "AES-GCM" : "AES-KW", length: bits },
|
|
400
|
+
usages: none,
|
|
401
|
+
ops: gcm ? ["encrypt", "decrypt"] : ["wrapKey", "unwrapKey"]
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function pbes2() {
|
|
405
|
+
return {
|
|
406
|
+
kty: ["oct"],
|
|
407
|
+
mode: "key-wrapping",
|
|
408
|
+
secret: true,
|
|
409
|
+
subtle: { name: "PBKDF2" },
|
|
410
|
+
usages: none,
|
|
411
|
+
ops: ["deriveBits", "deriveBits"]
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
var JWE = table({
|
|
415
|
+
dir: {
|
|
416
|
+
kty: ["oct"],
|
|
417
|
+
mode: "direct-encryption",
|
|
418
|
+
secret: true,
|
|
419
|
+
subtle: { name: "AES-GCM" },
|
|
420
|
+
usages: none,
|
|
421
|
+
ops: ["encrypt", "decrypt"]
|
|
422
|
+
},
|
|
423
|
+
"RSA-OAEP": rsaes(1),
|
|
424
|
+
"RSA-OAEP-256": rsaes(256),
|
|
425
|
+
"RSA-OAEP-384": rsaes(384),
|
|
426
|
+
"RSA-OAEP-512": rsaes(512),
|
|
427
|
+
"ECDH-ES": ecdh("direct-key-agreement"),
|
|
428
|
+
"ECDH-ES+A128KW": ecdh("key-agreement-with-key-wrapping"),
|
|
429
|
+
"ECDH-ES+A192KW": ecdh("key-agreement-with-key-wrapping"),
|
|
430
|
+
"ECDH-ES+A256KW": ecdh("key-agreement-with-key-wrapping"),
|
|
431
|
+
A128KW: aeskw(128),
|
|
432
|
+
A192KW: aeskw(192),
|
|
433
|
+
A256KW: aeskw(256),
|
|
434
|
+
A128GCMKW: aeskw(128, true),
|
|
435
|
+
A192GCMKW: aeskw(192, true),
|
|
436
|
+
A256GCMKW: aeskw(256, true),
|
|
437
|
+
"PBES2-HS256+A128KW": pbes2(),
|
|
438
|
+
"PBES2-HS384+A192KW": pbes2(),
|
|
439
|
+
"PBES2-HS512+A256KW": pbes2()
|
|
440
|
+
});
|
|
441
|
+
var contentOps = ["encrypt", "decrypt"];
|
|
442
|
+
function contentEncryption(bits, cbc = false) {
|
|
443
|
+
return {
|
|
444
|
+
kty: ["oct"],
|
|
445
|
+
secret: true,
|
|
446
|
+
subtle: { name: cbc ? "AES-CBC" : "AES-GCM", length: bits },
|
|
447
|
+
usages: none,
|
|
448
|
+
ops: contentOps,
|
|
449
|
+
cekBits: bits,
|
|
450
|
+
ivBits: cbc ? 128 : 96,
|
|
451
|
+
cbc
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
var ENC = table({
|
|
455
|
+
A128GCM: contentEncryption(128),
|
|
456
|
+
A192GCM: contentEncryption(192),
|
|
457
|
+
A256GCM: contentEncryption(256),
|
|
458
|
+
"A128CBC-HS256": contentEncryption(256, true),
|
|
459
|
+
"A192CBC-HS384": contentEncryption(384, true),
|
|
460
|
+
"A256CBC-HS512": contentEncryption(512, true)
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
// ../../../node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/lib/jws_algorithms.js
|
|
464
|
+
var sig = [["verify"], ["sign"]];
|
|
465
|
+
function hmac(bits) {
|
|
466
|
+
const subtle = { name: "HMAC", hash: `SHA-${bits}` };
|
|
467
|
+
return { kty: ["oct"], secret: true, subtle, signing: subtle, usages: sig };
|
|
468
|
+
}
|
|
469
|
+
function rsa(bits, saltLength) {
|
|
470
|
+
const subtle = { name: saltLength ? "RSA-PSS" : "RSASSA-PKCS1-v1_5", hash: `SHA-${bits}` };
|
|
471
|
+
return {
|
|
472
|
+
kty: ["RSA"],
|
|
473
|
+
subtle,
|
|
474
|
+
signing: saltLength ? { ...subtle, saltLength } : subtle,
|
|
475
|
+
usages: sig,
|
|
476
|
+
minRsaBits: 2048
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
function ecdsa(crv, bits) {
|
|
480
|
+
return {
|
|
481
|
+
kty: ["EC"],
|
|
482
|
+
crv,
|
|
483
|
+
subtle: { name: "ECDSA", namedCurve: crv },
|
|
484
|
+
signing: { name: "ECDSA", hash: `SHA-${bits}` },
|
|
485
|
+
usages: sig
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
function eddsa() {
|
|
489
|
+
const subtle = { name: "Ed25519" };
|
|
490
|
+
return {
|
|
491
|
+
kty: ["OKP"],
|
|
492
|
+
crv: "Ed25519",
|
|
493
|
+
subtle,
|
|
494
|
+
signing: subtle,
|
|
495
|
+
usages: sig
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
function mldsa(bits) {
|
|
499
|
+
const subtle = { name: `ML-DSA-${bits}` };
|
|
500
|
+
return {
|
|
501
|
+
kty: ["AKP"],
|
|
502
|
+
subtle,
|
|
503
|
+
signing: subtle,
|
|
504
|
+
usages: sig
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
var JWS = table({
|
|
508
|
+
HS256: hmac(256),
|
|
509
|
+
HS384: hmac(384),
|
|
510
|
+
HS512: hmac(512),
|
|
511
|
+
RS256: rsa(256),
|
|
512
|
+
RS384: rsa(384),
|
|
513
|
+
RS512: rsa(512),
|
|
514
|
+
PS256: rsa(256, 32),
|
|
515
|
+
PS384: rsa(384, 48),
|
|
516
|
+
PS512: rsa(512, 64),
|
|
517
|
+
ES256: ecdsa("P-256", 256),
|
|
518
|
+
ES384: ecdsa("P-384", 384),
|
|
519
|
+
ES512: ecdsa("P-521", 512),
|
|
520
|
+
EdDSA: eddsa(),
|
|
521
|
+
Ed25519: eddsa(),
|
|
522
|
+
"ML-DSA-44": mldsa(44),
|
|
523
|
+
"ML-DSA-65": mldsa(65),
|
|
524
|
+
"ML-DSA-87": mldsa(87)
|
|
525
|
+
});
|
|
526
|
+
function jwsAlgorithm(alg) {
|
|
527
|
+
const entry = typeof alg == "string" ? JWS[alg] : void 0;
|
|
528
|
+
if (!entry)
|
|
529
|
+
throw new JOSENotSupported(`alg ${alg} is not supported either by JOSE or your javascript runtime`);
|
|
530
|
+
return entry;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// ../../../node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/lib/jws_verify.js
|
|
534
|
+
function prepareVerify(options) {
|
|
535
|
+
return [options && validateAlgorithms("algorithms", options.algorithms), options?.crit];
|
|
536
|
+
}
|
|
537
|
+
function parseProtectedHeader(encodedProtected) {
|
|
538
|
+
return encodedProtected === void 0 ? {} : parseJoseHeader(encodedProtected, JWSInvalid, "JWS Protected Header is invalid");
|
|
539
|
+
}
|
|
540
|
+
function encodeCompactUnencodedPayload(payload) {
|
|
541
|
+
try {
|
|
542
|
+
return encode(payload);
|
|
543
|
+
} catch {
|
|
544
|
+
throw new JWSInvalid("JWS Compact Serialization payload must use only ASCII characters");
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
async function verifySignature(jws, shared, key, encodeUnencodedPayload, parsedProtected) {
|
|
548
|
+
const { protected: encodedProtected, header, payload: inputPayload } = jws, parsedProt = parsedProtected ?? parseProtectedHeader(encodedProtected);
|
|
549
|
+
if (!isDisjoint(parsedProt, header))
|
|
550
|
+
throw new JWSInvalid("JWS Protected and JWS Unprotected Header Parameter names must be disjoint");
|
|
551
|
+
const joseHeader = { ...parsedProt, ...header }, b64 = validateB64(parsedProt, validateCrit(JWSInvalid, JWS_RECOGNIZED, shared[1], parsedProt, joseHeader)), { alg } = joseHeader;
|
|
552
|
+
if (typeof alg != "string" || !alg)
|
|
553
|
+
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
|
|
554
|
+
if (shared[0] && !shared[0].has(alg))
|
|
555
|
+
throw new JOSEAlgNotAllowed('"alg" (Algorithm) Header Parameter value not allowed');
|
|
556
|
+
if (b64) {
|
|
557
|
+
if (typeof inputPayload != "string")
|
|
558
|
+
throw new JWSInvalid("JWS Payload must be a string");
|
|
559
|
+
} else if (typeof inputPayload != "string" && !(inputPayload instanceof Uint8Array))
|
|
560
|
+
throw new JWSInvalid("JWS Payload must be a string or an Uint8Array instance");
|
|
561
|
+
const signingPayload = b64 || typeof inputPayload != "string" ? inputPayload : encodeUnencodedPayload(inputPayload);
|
|
562
|
+
let resolvedKey = false;
|
|
563
|
+
typeof key == "function" && (key = await key(parsedProt, jws), resolvedKey = true);
|
|
564
|
+
const entry = jwsAlgorithm(alg), data = concat(encodedProtected !== void 0 ? encode(encodedProtected) : new Uint8Array(), encode("."), typeof signingPayload == "string" ? shared[2] ??= encodeBase64url(signingPayload, "payload", JWSInvalid) : signingPayload), signature = decodeBase64url(jws.signature, "signature", JWSInvalid), k = await prepareKey(entry, key, "verify"), cryptoKey = await rawKey(k, entry.subtle, "verify");
|
|
565
|
+
entry.minRsaBits && checkModulusLength(entry.alg, cryptoKey);
|
|
566
|
+
let verified = false;
|
|
567
|
+
try {
|
|
568
|
+
verified = await crypto.subtle.verify(entry.signing, cryptoKey, signature, data);
|
|
569
|
+
} catch {
|
|
570
|
+
}
|
|
571
|
+
if (!verified)
|
|
572
|
+
throw new JWSSignatureVerificationFailed();
|
|
573
|
+
const result = { payload: typeof signingPayload == "string" ? decodeBase64url(signingPayload, "payload", JWSInvalid) : signingPayload };
|
|
574
|
+
return encodedProtected !== void 0 && (result.protectedHeader = parsedProt), header !== void 0 && (result.unprotectedHeader = header), resolvedKey ? [{ ...result, key: k }, b64] : [result, b64];
|
|
575
|
+
}
|
|
576
|
+
async function verifyCompact(jws, shared, key) {
|
|
577
|
+
if (jws instanceof Uint8Array && (jws = decoder.decode(jws)), typeof jws != "string")
|
|
578
|
+
throw new JWSInvalid("Compact JWS must be a string or Uint8Array");
|
|
579
|
+
const { 0: protectedHeader, 1: payload, 2: signature, length } = jws.split(".");
|
|
580
|
+
if (length !== 3)
|
|
581
|
+
throw new JWSInvalid("Invalid Compact JWS");
|
|
582
|
+
return verifySignature({ payload, protected: protectedHeader, signature }, shared, key, encodeCompactUnencodedPayload);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// ../../../node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/lib/jwt_claims_set.js
|
|
586
|
+
var epoch = (date) => Math.floor(date.getTime() / 1e3);
|
|
587
|
+
var multipliers = {
|
|
588
|
+
s: 1,
|
|
589
|
+
m: 60,
|
|
590
|
+
h: 3600,
|
|
591
|
+
d: 86400,
|
|
592
|
+
w: 604800,
|
|
593
|
+
y: 31557600
|
|
594
|
+
};
|
|
595
|
+
var REGEX = /^(\+|\-)? ?(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i;
|
|
596
|
+
var checkFailed = "check_failed";
|
|
597
|
+
function invalidDuration() {
|
|
598
|
+
throw new TypeError("Invalid time period format");
|
|
599
|
+
}
|
|
600
|
+
function secs(str) {
|
|
601
|
+
typeof str != "string" && invalidDuration();
|
|
602
|
+
const matched = REGEX.exec(str);
|
|
603
|
+
(!matched || matched[4] && matched[1]) && invalidDuration();
|
|
604
|
+
const value = parseFloat(matched[2]), numericDate2 = Math.round(value * multipliers[matched[3][0].toLowerCase()]);
|
|
605
|
+
return Number.isFinite(numericDate2) || invalidDuration(), matched[1] === "-" || matched[4] === "ago" ? -numericDate2 : numericDate2;
|
|
606
|
+
}
|
|
607
|
+
function validateInput(label, input) {
|
|
608
|
+
if (!Number.isFinite(input))
|
|
609
|
+
throw new TypeError(`Invalid ${label} input`);
|
|
610
|
+
return input;
|
|
611
|
+
}
|
|
612
|
+
function validateStringClaim(claim, value) {
|
|
613
|
+
if (typeof value != "string")
|
|
614
|
+
throw new TypeError(`"${claim}" claim must be a string`);
|
|
615
|
+
}
|
|
616
|
+
function validateAudienceClaim(value) {
|
|
617
|
+
if (typeof value != "string" && (!Array.isArray(value) || Array.from(value).some((member) => typeof member != "string")))
|
|
618
|
+
throw new TypeError('"aud" claim must be a string or an array of strings');
|
|
619
|
+
}
|
|
620
|
+
function numericDate(value, label) {
|
|
621
|
+
return typeof value == "number" ? validateInput(label, value) : value instanceof Date ? validateInput(label, epoch(value)) : epoch(/* @__PURE__ */ new Date()) + secs(value);
|
|
622
|
+
}
|
|
623
|
+
var normalizeTyp = (value) => {
|
|
624
|
+
const normalized = value.toLowerCase();
|
|
625
|
+
return value.includes("/") ? normalized : `application/${normalized}`;
|
|
626
|
+
};
|
|
627
|
+
var checkAudiencePresence = (audPayload, audOption) => typeof audPayload == "string" ? audOption.includes(audPayload) : Array.isArray(audPayload) ? audOption.some((aud) => audPayload.includes(aud)) : false;
|
|
628
|
+
function validateNumericDate(payload, claim, required = false) {
|
|
629
|
+
const value = payload[claim];
|
|
630
|
+
if (!(value === void 0 && !required)) {
|
|
631
|
+
if (typeof value != "number")
|
|
632
|
+
throw new JWTClaimValidationFailed(`"${claim}" claim must be a number`, payload, claim, "invalid");
|
|
633
|
+
return value;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
function unexpectedClaim(payload, claim) {
|
|
637
|
+
throw new JWTClaimValidationFailed(`unexpected "${claim}" claim value`, payload, claim, checkFailed);
|
|
638
|
+
}
|
|
639
|
+
function validateClaimsSet(protectedHeader, encodedPayload, options = {}) {
|
|
640
|
+
let payload;
|
|
641
|
+
try {
|
|
642
|
+
payload = JSON.parse(strictDecoder.decode(encodedPayload));
|
|
643
|
+
} catch {
|
|
644
|
+
}
|
|
645
|
+
if (!isObject(payload))
|
|
646
|
+
throw new JWTInvalid("JWT Claims Set must be a top-level JSON object");
|
|
647
|
+
const { typ } = options;
|
|
648
|
+
if (typ !== void 0 && (typeof protectedHeader.typ != "string" || normalizeTyp(protectedHeader.typ) !== normalizeTyp(typ)))
|
|
649
|
+
throw new JWTClaimValidationFailed('unexpected "typ" JWT header value', payload, "typ", checkFailed);
|
|
650
|
+
const { requiredClaims = [], issuer, subject, audience, maxTokenAge } = options, presenceCheck = [...requiredClaims];
|
|
651
|
+
maxTokenAge !== void 0 && presenceCheck.push("iat"), audience !== void 0 && presenceCheck.push("aud"), subject !== void 0 && presenceCheck.push("sub"), issuer !== void 0 && presenceCheck.push("iss");
|
|
652
|
+
for (const claim of new Set(presenceCheck.reverse()))
|
|
653
|
+
if (!Object.hasOwn(payload, claim))
|
|
654
|
+
throw new JWTClaimValidationFailed(`missing required "${claim}" claim`, payload, claim, "missing");
|
|
655
|
+
issuer !== void 0 && !(Array.isArray(issuer) ? issuer : [issuer]).includes(payload.iss) && unexpectedClaim(payload, "iss"), subject !== void 0 && payload.sub !== subject && unexpectedClaim(payload, "sub"), audience !== void 0 && !checkAudiencePresence(payload.aud, typeof audience == "string" ? [audience] : audience) && unexpectedClaim(payload, "aud");
|
|
656
|
+
const { clockTolerance } = options;
|
|
657
|
+
let tolerance = 0;
|
|
658
|
+
if (typeof clockTolerance == "string")
|
|
659
|
+
tolerance = secs(clockTolerance);
|
|
660
|
+
else if (clockTolerance !== void 0) {
|
|
661
|
+
if (typeof clockTolerance != "number")
|
|
662
|
+
throw new TypeError("Invalid clockTolerance option type");
|
|
663
|
+
tolerance = clockTolerance;
|
|
664
|
+
}
|
|
665
|
+
validateInput("clockTolerance option", tolerance);
|
|
666
|
+
const { currentDate } = options, now = validateInput("currentDate option", epoch(currentDate === void 0 ? /* @__PURE__ */ new Date() : currentDate)), iat = validateNumericDate(payload, "iat", maxTokenAge !== void 0), nbf = validateNumericDate(payload, "nbf");
|
|
667
|
+
if (nbf !== void 0 && nbf > now + tolerance)
|
|
668
|
+
throw new JWTClaimValidationFailed('"nbf" claim timestamp check failed', payload, "nbf", checkFailed);
|
|
669
|
+
const exp = validateNumericDate(payload, "exp");
|
|
670
|
+
if (exp !== void 0 && exp <= now - tolerance)
|
|
671
|
+
throw new JWTExpired('"exp" claim timestamp check failed', payload, "exp", checkFailed);
|
|
672
|
+
if (maxTokenAge !== void 0) {
|
|
673
|
+
const age = now - iat, max = validateInput("maxTokenAge option", typeof maxTokenAge == "number" ? maxTokenAge : secs(maxTokenAge));
|
|
674
|
+
if (age - tolerance > max)
|
|
675
|
+
throw new JWTExpired('"iat" claim timestamp check failed (too far in the past)', payload, "iat", checkFailed);
|
|
676
|
+
if (age < -tolerance)
|
|
677
|
+
throw new JWTClaimValidationFailed('"iat" claim timestamp check failed (it should be in the past)', payload, "iat", checkFailed);
|
|
678
|
+
}
|
|
679
|
+
return payload;
|
|
680
|
+
}
|
|
681
|
+
var producerPayloads;
|
|
682
|
+
function producerPayload(producer) {
|
|
683
|
+
return producerPayloads.get(producer);
|
|
684
|
+
}
|
|
685
|
+
function jwtData(producer) {
|
|
686
|
+
const payload = producerPayload(producer);
|
|
687
|
+
for (const claim of ["iat", "nbf", "exp"]) {
|
|
688
|
+
const value = payload[claim];
|
|
689
|
+
if (typeof value == "number" && !Number.isFinite(value))
|
|
690
|
+
throw new TypeError(`"${claim}" claim must be a finite number`);
|
|
691
|
+
}
|
|
692
|
+
return encoder.encode(JSON.stringify(payload));
|
|
693
|
+
}
|
|
694
|
+
var JWTClaimsBuilder = class {
|
|
695
|
+
constructor(payload = {}) {
|
|
696
|
+
if (!isObject(payload))
|
|
697
|
+
throw new TypeError("JWT Claims Set MUST be an object");
|
|
698
|
+
(producerPayloads ||= /* @__PURE__ */ new WeakMap()).set(this, structuredClone(payload));
|
|
699
|
+
}
|
|
700
|
+
setIssuer(value) {
|
|
701
|
+
return validateStringClaim("iss", value), producerPayload(this).iss = value, this;
|
|
702
|
+
}
|
|
703
|
+
setSubject(value) {
|
|
704
|
+
return validateStringClaim("sub", value), producerPayload(this).sub = value, this;
|
|
705
|
+
}
|
|
706
|
+
setAudience(value) {
|
|
707
|
+
return validateAudienceClaim(value), producerPayload(this).aud = value, this;
|
|
708
|
+
}
|
|
709
|
+
setJti(value) {
|
|
710
|
+
return validateStringClaim("jti", value), producerPayload(this).jti = value, this;
|
|
711
|
+
}
|
|
712
|
+
setNotBefore(value) {
|
|
713
|
+
return producerPayload(this).nbf = numericDate(value, "setNotBefore"), this;
|
|
714
|
+
}
|
|
715
|
+
setExpirationTime(value) {
|
|
716
|
+
return producerPayload(this).exp = numericDate(value, "setExpirationTime"), this;
|
|
717
|
+
}
|
|
718
|
+
setIssuedAt(value) {
|
|
719
|
+
const payload = producerPayload(this);
|
|
720
|
+
return value === void 0 ? payload.iat = epoch(/* @__PURE__ */ new Date()) : typeof value == "string" ? payload.iat = validateInput("setIssuedAt", epoch(/* @__PURE__ */ new Date()) + secs(value)) : payload.iat = numericDate(value, "setIssuedAt"), this;
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
|
|
724
|
+
// ../../../node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/jwt/verify.js
|
|
725
|
+
async function jwtVerify(jwt, key, options) {
|
|
726
|
+
const [verified, b64] = await verifyCompact(jwt, prepareVerify(options), key);
|
|
727
|
+
if (!b64)
|
|
728
|
+
throw new JWTInvalid("JWTs MUST NOT use unencoded payload");
|
|
729
|
+
const payload = validateClaimsSet(verified.protectedHeader, verified.payload, options);
|
|
730
|
+
return { ...verified, payload };
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// ../../../node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/lib/jws_sign.js
|
|
734
|
+
async function createSignature(input, key, rejectUnencoded) {
|
|
735
|
+
let [payload, protectedHeader, unprotectedHeader, crit] = input, protectedHeaderString = "";
|
|
736
|
+
if (protectedHeader !== void 0) {
|
|
737
|
+
const normalized = serializeJoseHeader(JWSInvalid, protectedHeader);
|
|
738
|
+
protectedHeader = normalized[0], protectedHeaderString = encode2(normalized[1]);
|
|
739
|
+
}
|
|
740
|
+
if (unprotectedHeader !== void 0 && (unprotectedHeader = serializeJoseHeader(JWSInvalid, unprotectedHeader)[0]), !protectedHeader && !unprotectedHeader)
|
|
741
|
+
throw new JWSInvalid("either setProtectedHeader or setUnprotectedHeader must be called before #sign()");
|
|
742
|
+
if (!isDisjoint(protectedHeader, unprotectedHeader))
|
|
743
|
+
throw new JWSInvalid("JWS Protected and JWS Unprotected Header Parameter names must be disjoint");
|
|
744
|
+
const joseHeader = { ...protectedHeader, ...unprotectedHeader };
|
|
745
|
+
validateCritDuplicates(JWSInvalid, protectedHeader);
|
|
746
|
+
const b64 = validateB64(protectedHeader, validateCrit(JWSInvalid, JWS_RECOGNIZED, crit, protectedHeader, joseHeader));
|
|
747
|
+
b64 || rejectUnencoded?.();
|
|
748
|
+
const { alg } = joseHeader;
|
|
749
|
+
if (typeof alg != "string" || !alg)
|
|
750
|
+
throw new JWSInvalid('JWS "alg" (Algorithm) Header Parameter missing or invalid');
|
|
751
|
+
const entry = jwsAlgorithm(alg);
|
|
752
|
+
let payloadS = "", payloadB = payload, data;
|
|
753
|
+
if (b64) {
|
|
754
|
+
const encoded = input[4];
|
|
755
|
+
encoded ? (payloadS = encoded[0] ??= encode2(payload), payloadB = encoded[1] ??= encode(payloadS)) : (payloadS = encode2(payload), data = encoder.encode(`${protectedHeaderString}.${payloadS}`));
|
|
756
|
+
}
|
|
757
|
+
data ??= concat(encode(protectedHeaderString), encode("."), payloadB);
|
|
758
|
+
const k = await rawKey(await prepareKey(entry, key, "sign"), entry.subtle, "sign");
|
|
759
|
+
entry.minRsaBits && checkModulusLength(entry.alg, k);
|
|
760
|
+
const jws = {
|
|
761
|
+
signature: encode2(new Uint8Array(await crypto.subtle.sign(entry.signing, k, data))),
|
|
762
|
+
payload: payloadS
|
|
763
|
+
};
|
|
764
|
+
return protectedHeader && (jws.protected = protectedHeaderString), unprotectedHeader && (jws.header = unprotectedHeader), [jws, b64];
|
|
765
|
+
}
|
|
766
|
+
async function createCompactSignature(payload, protectedHeader, crit, key, rejectUnencoded) {
|
|
767
|
+
const [jws] = await createSignature([payload, protectedHeader, void 0, crit], key, rejectUnencoded);
|
|
768
|
+
return `${jws.protected}.${jws.payload}.${jws.signature}`;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
// ../../../node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/jwt/sign.js
|
|
772
|
+
var SignJWT_base = JWTClaimsBuilder;
|
|
773
|
+
var SignJWT = class extends SignJWT_base {
|
|
774
|
+
#protectedHeader;
|
|
775
|
+
setProtectedHeader(protectedHeader) {
|
|
776
|
+
return assertNotSet(this.#protectedHeader, "setProtectedHeader"), this.#protectedHeader = protectedHeader, this;
|
|
777
|
+
}
|
|
778
|
+
async sign(key, options) {
|
|
779
|
+
return createCompactSignature(jwtData(this), this.#protectedHeader, options?.crit, key, () => {
|
|
780
|
+
throw new JWTInvalid("JWTs MUST NOT use unencoded payload");
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
// ../../../node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/lib/key_algorithm.js
|
|
786
|
+
var algArgument = '"alg" (Algorithm)';
|
|
787
|
+
function unsupportedAlg(source = 'JWK "alg" (Algorithm) Parameter') {
|
|
788
|
+
throw new JOSENotSupported(`Invalid or unsupported ${source} value`);
|
|
789
|
+
}
|
|
790
|
+
function keyAlgorithm(alg, source) {
|
|
791
|
+
return (typeof alg == "string" ? JWS[alg] ?? JWE[alg] : void 0) ?? unsupportedAlg(source);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// ../../../node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/lib/asn1.js
|
|
795
|
+
var bytesEqual = (a, b) => {
|
|
796
|
+
if (a.byteLength !== b.length)
|
|
797
|
+
return false;
|
|
798
|
+
for (let i = 0; i < a.byteLength; i++)
|
|
799
|
+
if (a[i] !== b[i])
|
|
800
|
+
return false;
|
|
801
|
+
return true;
|
|
802
|
+
};
|
|
803
|
+
var createASN1State = (data) => ({ data, pos: 0 });
|
|
804
|
+
var readByte = (state) => {
|
|
805
|
+
const byte = state.data[state.pos++];
|
|
806
|
+
if (byte === void 0)
|
|
807
|
+
throw new Error("Unexpected end of ASN.1 input");
|
|
808
|
+
return byte;
|
|
809
|
+
};
|
|
810
|
+
var parseLength = (state) => {
|
|
811
|
+
const first = readByte(state);
|
|
812
|
+
if (first & 128) {
|
|
813
|
+
const lengthOfLen = first & 127;
|
|
814
|
+
let length = 0;
|
|
815
|
+
for (let i = 0; i < lengthOfLen; i++)
|
|
816
|
+
length = length << 8 | readByte(state);
|
|
817
|
+
return length;
|
|
818
|
+
}
|
|
819
|
+
return first;
|
|
820
|
+
};
|
|
821
|
+
var skipElement = (state, count = 1) => {
|
|
822
|
+
for (; count-- > 0; ) {
|
|
823
|
+
state.pos++;
|
|
824
|
+
const length = parseLength(state);
|
|
825
|
+
state.pos += length;
|
|
826
|
+
}
|
|
827
|
+
};
|
|
828
|
+
var expectTag = (state, expectedTag, errorMessage) => {
|
|
829
|
+
if (readByte(state) !== expectedTag)
|
|
830
|
+
throw new Error(errorMessage);
|
|
831
|
+
};
|
|
832
|
+
var getSubarray = (state, length) => {
|
|
833
|
+
if (length < 0 || state.pos + length > state.data.length)
|
|
834
|
+
throw new Error("Unexpected end of ASN.1 input");
|
|
835
|
+
const result = state.data.subarray(state.pos, state.pos + length);
|
|
836
|
+
return state.pos += length, result;
|
|
837
|
+
};
|
|
838
|
+
var parseAlgorithmOID = (state) => {
|
|
839
|
+
expectTag(state, 6, "Expected algorithm OID");
|
|
840
|
+
const oidLen = parseLength(state);
|
|
841
|
+
return getSubarray(state, oidLen);
|
|
842
|
+
};
|
|
843
|
+
function parseKeyHeader(state, keyFormat) {
|
|
844
|
+
if (expectTag(state, 48, `Invalid ${keyFormat === "spki" ? "SPKI" : "PKCS#8"} structure`), parseLength(state), keyFormat === "pkcs8") {
|
|
845
|
+
expectTag(state, 2, "Expected version field");
|
|
846
|
+
const length = parseLength(state);
|
|
847
|
+
state.pos += length;
|
|
848
|
+
}
|
|
849
|
+
expectTag(state, 48, "Expected algorithm identifier"), parseLength(state);
|
|
850
|
+
}
|
|
851
|
+
var parseECAlgorithmIdentifier = (state) => {
|
|
852
|
+
const algOid = parseAlgorithmOID(state);
|
|
853
|
+
if (bytesEqual(algOid, [43, 101, 110]))
|
|
854
|
+
return "X25519";
|
|
855
|
+
if (!bytesEqual(algOid, [42, 134, 72, 206, 61, 2, 1]))
|
|
856
|
+
throw new Error("Unsupported key algorithm");
|
|
857
|
+
expectTag(state, 6, "Expected curve OID");
|
|
858
|
+
const curveOidLen = parseLength(state), curveOid = getSubarray(state, curveOidLen);
|
|
859
|
+
if (bytesEqual(curveOid, [42, 134, 72, 206, 61, 3, 1, 7]))
|
|
860
|
+
return "P-256";
|
|
861
|
+
if (bytesEqual(curveOid, [43, 129, 4, 0, 34]))
|
|
862
|
+
return "P-384";
|
|
863
|
+
if (bytesEqual(curveOid, [43, 129, 4, 0, 35]))
|
|
864
|
+
return "P-521";
|
|
865
|
+
throw new Error("Unsupported named curve");
|
|
866
|
+
};
|
|
867
|
+
var genericImport = async (keyFormat, keyData, alg, options) => {
|
|
868
|
+
const extractable = validateExtractableOption(options?.extractable), entry = keyAlgorithm(alg, algArgument);
|
|
869
|
+
entry.secret && unsupportedAlg(algArgument);
|
|
870
|
+
const isPublic = keyFormat === "spki";
|
|
871
|
+
let algorithm;
|
|
872
|
+
if (entry.resolve)
|
|
873
|
+
try {
|
|
874
|
+
const state = createASN1State(keyData);
|
|
875
|
+
parseKeyHeader(state, keyFormat), algorithm = entry.resolve({ crv: parseECAlgorithmIdentifier(state) });
|
|
876
|
+
} catch {
|
|
877
|
+
throw new JOSENotSupported("Invalid or unsupported key format");
|
|
878
|
+
}
|
|
879
|
+
else
|
|
880
|
+
algorithm = entry.subtle;
|
|
881
|
+
return crypto.subtle.importKey(keyFormat, keyData, algorithm, extractable ?? isPublic, entry.usages[isPublic ? 0 : 1]);
|
|
882
|
+
};
|
|
883
|
+
var processPEMData = (pem, pattern) => decodeBase64(pem.replace(pattern, ""));
|
|
884
|
+
function spkiFromX509(buf) {
|
|
885
|
+
const state = createASN1State(buf);
|
|
886
|
+
expectTag(state, 48, "Invalid certificate structure");
|
|
887
|
+
const certificateLength = parseLength(state);
|
|
888
|
+
if (certificateLength < 0 || state.pos + certificateLength > state.data.length)
|
|
889
|
+
throw new Error("Unexpected end of ASN.1 input");
|
|
890
|
+
expectTag(state, 48, "Invalid tbsCertificate structure"), parseLength(state), buf[state.pos] === 160 ? skipElement(state, 6) : skipElement(state, 5);
|
|
891
|
+
const spkiStart = state.pos;
|
|
892
|
+
expectTag(state, 48, "Invalid SPKI structure");
|
|
893
|
+
const spkiContentLen = parseLength(state);
|
|
894
|
+
return buf.subarray(spkiStart, spkiStart + spkiContentLen + (state.pos - spkiStart));
|
|
895
|
+
}
|
|
896
|
+
var fromX509 = (pem, alg, options) => {
|
|
897
|
+
let spki;
|
|
898
|
+
try {
|
|
899
|
+
const certificate = processPEMData(pem, /(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g);
|
|
900
|
+
spki = spkiFromX509(certificate);
|
|
901
|
+
} catch (cause) {
|
|
902
|
+
throw new TypeError("Failed to parse the X.509 certificate", { cause });
|
|
903
|
+
}
|
|
904
|
+
return genericImport("spki", spki, alg, options);
|
|
905
|
+
};
|
|
906
|
+
|
|
907
|
+
// ../../../node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/key/import.js
|
|
908
|
+
async function importX509(x509, alg, options) {
|
|
909
|
+
if (typeof x509 != "string" || x509.indexOf("-----BEGIN CERTIFICATE-----") !== 0)
|
|
910
|
+
throw new TypeError('"x509" must be X.509 formatted string');
|
|
911
|
+
return fromX509(x509, alg, options);
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// ../../../node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/util/decode_protected_header.js
|
|
915
|
+
function decodeProtectedHeader(token) {
|
|
916
|
+
let protectedB64u;
|
|
917
|
+
if (typeof token == "string") {
|
|
918
|
+
const parts = token.split(".");
|
|
919
|
+
(parts.length === 3 || parts.length === 5) && ([protectedB64u] = parts);
|
|
920
|
+
} else if (typeof token == "object" && token)
|
|
921
|
+
if ("protected" in token)
|
|
922
|
+
protectedB64u = token.protected;
|
|
923
|
+
else
|
|
924
|
+
throw new TypeError("Token does not contain a Protected Header");
|
|
925
|
+
const invalid2 = "Invalid Token or Protected Header formatting";
|
|
926
|
+
if (typeof protectedB64u != "string" || !protectedB64u)
|
|
927
|
+
throw new TypeError(invalid2);
|
|
928
|
+
return parseJoseHeader(protectedB64u, TypeError, invalid2);
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// ../../../node_modules/.pnpm/jose@6.2.12/node_modules/jose/dist/webapi/util/decode_jwt.js
|
|
932
|
+
function decodeJwt(jwt) {
|
|
933
|
+
if (typeof jwt != "string")
|
|
934
|
+
throw new JWTInvalid("JWTs must use Compact JWS serialization, JWT must be a string");
|
|
935
|
+
const { 1: payload, length } = jwt.split(".");
|
|
936
|
+
if (length === 5)
|
|
937
|
+
throw new JWTInvalid("Only JWTs using Compact JWS serialization can be decoded");
|
|
938
|
+
if (length !== 3)
|
|
939
|
+
throw new JWTInvalid("Invalid JWT");
|
|
940
|
+
if (!payload)
|
|
941
|
+
throw new JWTInvalid("JWTs must contain a payload");
|
|
942
|
+
let decoded;
|
|
943
|
+
try {
|
|
944
|
+
decoded = decode(payload);
|
|
945
|
+
} catch {
|
|
946
|
+
throw new JWTInvalid("Failed to base64url decode the payload");
|
|
947
|
+
}
|
|
948
|
+
let result;
|
|
949
|
+
try {
|
|
950
|
+
result = JSON.parse(strictDecoder.decode(decoded));
|
|
951
|
+
} catch {
|
|
952
|
+
throw new JWTInvalid("Failed to parse the decoded payload as JSON");
|
|
953
|
+
}
|
|
954
|
+
if (!isObject(result))
|
|
955
|
+
throw new JWTInvalid("Invalid JWT Claims Set");
|
|
956
|
+
return result;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// ../core/dist/index.js
|
|
960
|
+
import { readFileSync } from "fs";
|
|
961
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
962
|
+
import { fileURLToPath } from "url";
|
|
963
|
+
import { appendFileSync, mkdirSync } from "fs";
|
|
964
|
+
import { createServer as createNodeServer } from "http";
|
|
965
|
+
import { join as join3 } from "path";
|
|
966
|
+
import { threadId } from "worker_threads";
|
|
967
|
+
import { createHmac as createHmac2, sign as signData, timingSafeEqual as timingSafeEqual3, verify as verifyData } from "crypto";
|
|
968
|
+
import {
|
|
969
|
+
createHash as createHash2,
|
|
970
|
+
createPrivateKey,
|
|
971
|
+
createPublicKey as createPublicKey2,
|
|
972
|
+
generateKeyPair
|
|
973
|
+
} from "crypto";
|
|
974
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
975
|
+
function resolveBaseUrl(opts) {
|
|
976
|
+
if (opts.seedBaseUrl) {
|
|
977
|
+
return opts.seedBaseUrl.replace(/\{service\}/g, opts.service);
|
|
978
|
+
}
|
|
979
|
+
if (opts.baseUrl) {
|
|
980
|
+
return opts.baseUrl.replace(/\{service\}/g, opts.service);
|
|
981
|
+
}
|
|
982
|
+
const envBaseUrl = process.env.BASELINE_BASE_URL;
|
|
983
|
+
if (envBaseUrl) {
|
|
984
|
+
return envBaseUrl.replace(/\{service\}/g, opts.service);
|
|
985
|
+
}
|
|
986
|
+
return `http://localhost:${opts.port}`;
|
|
987
|
+
}
|
|
988
|
+
function composeService(options) {
|
|
989
|
+
const { name, modules } = options;
|
|
990
|
+
const owners = /* @__PURE__ */ new Map();
|
|
991
|
+
const moduleNames = /* @__PURE__ */ new Set();
|
|
992
|
+
for (const module of modules) {
|
|
993
|
+
if (moduleNames.has(module.name)) {
|
|
994
|
+
throw new Error(`${name}: module ${module.name} is listed twice`);
|
|
995
|
+
}
|
|
996
|
+
moduleNames.add(module.name);
|
|
997
|
+
for (const collection of module.collections ?? []) {
|
|
998
|
+
if (!collection.includes(".")) {
|
|
999
|
+
throw new Error(
|
|
1000
|
+
`${name}/${module.name}: collection "${collection}" must be namespaced, e.g. "${module.name}.${collection}"`
|
|
1001
|
+
);
|
|
1002
|
+
}
|
|
1003
|
+
const owner = owners.get(collection);
|
|
1004
|
+
if (owner) {
|
|
1005
|
+
throw new Error(`${name}: modules ${owner} and ${module.name} both own collection "${collection}"`);
|
|
1006
|
+
}
|
|
1007
|
+
owners.set(collection, module.name);
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
let routes = /* @__PURE__ */ new Map();
|
|
1011
|
+
return {
|
|
1012
|
+
name,
|
|
1013
|
+
modules,
|
|
1014
|
+
moduleRoutes: () => routes,
|
|
1015
|
+
register(app, store, webhooks, baseUrl, tokenMap) {
|
|
1016
|
+
const registered = /* @__PURE__ */ new Map();
|
|
1017
|
+
const routeOwner = /* @__PURE__ */ new Map();
|
|
1018
|
+
for (const module of modules) {
|
|
1019
|
+
const before = app.routeTable.length;
|
|
1020
|
+
const operationsBefore = app.operationTable.length;
|
|
1021
|
+
const upgradesBefore = app.upgradeTable.length;
|
|
1022
|
+
module.register({ app, store: guardedStore(store, name, module.name, owners), webhooks, baseUrl, tokenMap });
|
|
1023
|
+
const added = [
|
|
1024
|
+
...app.routeTable.slice(before).map((route) => `${route.method} ${route.path}`),
|
|
1025
|
+
...app.upgradeTable.slice(upgradesBefore).map((path) => `WS ${path}`),
|
|
1026
|
+
...app.operationTable.slice(operationsBefore)
|
|
1027
|
+
];
|
|
1028
|
+
for (const route of added) {
|
|
1029
|
+
const owner = routeOwner.get(route);
|
|
1030
|
+
if (owner) {
|
|
1031
|
+
throw new Error(`${name}: modules ${owner} and ${module.name} both register ${route}`);
|
|
1032
|
+
}
|
|
1033
|
+
routeOwner.set(route, module.name);
|
|
1034
|
+
}
|
|
1035
|
+
registered.set(module.name, added);
|
|
1036
|
+
}
|
|
1037
|
+
routes = registered;
|
|
1038
|
+
},
|
|
1039
|
+
seed(store, baseUrl) {
|
|
1040
|
+
for (const module of modules) {
|
|
1041
|
+
module.seed?.({ store: guardedStore(store, name, module.name, owners), baseUrl });
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
function guardedStore(store, service, module, owners) {
|
|
1047
|
+
return new Proxy(store, {
|
|
1048
|
+
get(target, property, receiver) {
|
|
1049
|
+
if (property !== "collection") {
|
|
1050
|
+
const value = Reflect.get(target, property, receiver);
|
|
1051
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
1052
|
+
}
|
|
1053
|
+
return (collection, indexFields) => {
|
|
1054
|
+
if (!(target.hasCollection(collection) || owners.has(collection))) {
|
|
1055
|
+
throw new Error(
|
|
1056
|
+
`${service}/${module}: creates collection "${collection}" without declaring it in collections`
|
|
1057
|
+
);
|
|
1058
|
+
}
|
|
1059
|
+
return target.collection(collection, indexFields);
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
var TRAILING_SLASHES = /\/+$/;
|
|
1065
|
+
function normalizeUri(uri) {
|
|
1066
|
+
try {
|
|
1067
|
+
const u = new URL(uri);
|
|
1068
|
+
return `${u.origin}${u.pathname.replace(TRAILING_SLASHES, "")}`;
|
|
1069
|
+
} catch {
|
|
1070
|
+
return uri.replace(TRAILING_SLASHES, "").split("?")[0];
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
function matchesRedirectUri(incoming, registered) {
|
|
1074
|
+
const normalized = normalizeUri(incoming);
|
|
1075
|
+
return registered.some((r) => normalizeUri(r) === normalized);
|
|
1076
|
+
}
|
|
1077
|
+
function constantTimeSecretEqual(a, b) {
|
|
1078
|
+
const bufA = Buffer.from(a, "utf-8");
|
|
1079
|
+
const bufB = Buffer.from(b, "utf-8");
|
|
1080
|
+
if (bufA.length !== bufB.length) {
|
|
1081
|
+
return false;
|
|
1082
|
+
}
|
|
1083
|
+
return timingSafeEqual(bufA, bufB);
|
|
1084
|
+
}
|
|
1085
|
+
function bodyStr(v) {
|
|
1086
|
+
if (typeof v === "string") {
|
|
1087
|
+
return v;
|
|
1088
|
+
}
|
|
1089
|
+
if (Array.isArray(v) && typeof v[0] === "string") {
|
|
1090
|
+
return v[0];
|
|
1091
|
+
}
|
|
1092
|
+
return "";
|
|
1093
|
+
}
|
|
1094
|
+
var isDebug = typeof process !== "undefined" && (process.env.DEBUG === "1" || process.env.DEBUG === "true" || process.env.BASELINE_DEBUG === "1");
|
|
1095
|
+
function debug(label, ...args) {
|
|
1096
|
+
if (isDebug) {
|
|
1097
|
+
console.log(`[${label}]`, ...args);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
var AUTH_SCHEME_PREFIX = /^(Bearer|token)\s+/i;
|
|
1101
|
+
function serializeTokenMap(tokenMap) {
|
|
1102
|
+
return [...tokenMap.entries()].map(([token, user]) => ({
|
|
1103
|
+
token,
|
|
1104
|
+
login: user.login,
|
|
1105
|
+
id: user.id,
|
|
1106
|
+
scopes: user.scopes,
|
|
1107
|
+
...user.installation ? { installation: user.installation } : {}
|
|
1108
|
+
}));
|
|
1109
|
+
}
|
|
1110
|
+
function restoreTokenMap(tokenMap, tokens) {
|
|
1111
|
+
tokenMap.clear();
|
|
1112
|
+
for (const t of tokens) {
|
|
1113
|
+
tokenMap.set(t.token, {
|
|
1114
|
+
login: t.login,
|
|
1115
|
+
id: t.id,
|
|
1116
|
+
scopes: t.scopes,
|
|
1117
|
+
...t.installation ? { installation: t.installation } : {}
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
function authMiddleware(tokens, appKeyResolver, fallbackUser) {
|
|
1122
|
+
return async (c, next) => {
|
|
1123
|
+
const authHeader = c.req.header("Authorization");
|
|
1124
|
+
if (authHeader) {
|
|
1125
|
+
const token = authHeader.replace(AUTH_SCHEME_PREFIX, "").trim();
|
|
1126
|
+
if (token.startsWith("eyJ") && appKeyResolver) {
|
|
1127
|
+
try {
|
|
1128
|
+
const [, payloadB64] = token.split(".");
|
|
1129
|
+
const payload = JSON.parse(Buffer.from(payloadB64, "base64url").toString());
|
|
1130
|
+
const appId = typeof payload.iss === "string" ? Number.parseInt(payload.iss, 10) : payload.iss;
|
|
1131
|
+
if (typeof appId === "number" && !Number.isNaN(appId)) {
|
|
1132
|
+
const appInfo = appKeyResolver(appId);
|
|
1133
|
+
if (appInfo) {
|
|
1134
|
+
const publicKey = createPublicKey(appInfo.privateKey);
|
|
1135
|
+
await jwtVerify(token, publicKey, { algorithms: ["RS256"] });
|
|
1136
|
+
c.set("authApp", {
|
|
1137
|
+
appId,
|
|
1138
|
+
slug: appInfo.slug,
|
|
1139
|
+
name: appInfo.name
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
} catch {
|
|
1144
|
+
}
|
|
1145
|
+
} else {
|
|
1146
|
+
let user = tokens.get(token);
|
|
1147
|
+
if (!user && fallbackUser && token.length > 0) {
|
|
1148
|
+
debug("auth", "fallback user for unknown token", { login: fallbackUser.login, id: fallbackUser.id });
|
|
1149
|
+
user = { login: fallbackUser.login, id: fallbackUser.id, scopes: fallbackUser.scopes };
|
|
1150
|
+
}
|
|
1151
|
+
if (user) {
|
|
1152
|
+
c.set("authUser", user);
|
|
1153
|
+
c.set("authToken", token);
|
|
1154
|
+
c.set("authScopes", user.scopes);
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
await next();
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
var SDK_VERSION = true ? "0.14.0" : "dev";
|
|
1162
|
+
var STATE_WRITER = `@deploybaseline/core@${SDK_VERSION}`;
|
|
1163
|
+
function captureServiceState(target, stateVersion) {
|
|
1164
|
+
return {
|
|
1165
|
+
stateVersion,
|
|
1166
|
+
store: target.store.snapshot(),
|
|
1167
|
+
tokens: serializeTokenMap(target.tokenMap),
|
|
1168
|
+
webhooks: target.webhooks.snapshot()
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
function restoreServiceState(target, state) {
|
|
1172
|
+
target.store.restore(state.store);
|
|
1173
|
+
restoreTokenMap(target.tokenMap, state.tokens);
|
|
1174
|
+
target.webhooks.restore(state.webhooks);
|
|
1175
|
+
}
|
|
1176
|
+
function escapeHtml(s) {
|
|
1177
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
1178
|
+
}
|
|
1179
|
+
function escapeAttr(s) {
|
|
1180
|
+
return escapeHtml(s).replace(/'/g, "'");
|
|
1181
|
+
}
|
|
1182
|
+
var CSS = `
|
|
1183
|
+
.inspector-json{white-space:pre-wrap;overflow-wrap:anywhere;font-size:.8125rem;line-height:1.6;max-height:70vh;overflow:auto}
|
|
1184
|
+
.inspector-detail{padding:14px 0;border-bottom:1px solid #0a3300}
|
|
1185
|
+
.inspector-detail summary{cursor:pointer;overflow-wrap:anywhere}
|
|
1186
|
+
.email-preview{width:100%;min-height:300px;border:1px solid #0a3300;border-radius:8px;background:#fff;margin-top:10px}
|
|
1187
|
+
.email-text{white-space:pre-wrap;overflow-wrap:anywhere}
|
|
1188
|
+
.inspector-action{display:inline-block;margin-top:12px;padding:8px 12px;border:1px solid #0a3300;border-radius:6px;background:#001a00;color:#33ff00;font:inherit;font-size:.8125rem;cursor:pointer}
|
|
1189
|
+
.inspector-action:hover{background:#0a3300}
|
|
1190
|
+
.inspector-scroll{overflow-x:auto}
|
|
1191
|
+
.inspector-inline-form{display:inline-flex;gap:8px;align-items:center;margin:0}
|
|
1192
|
+
.inspector-inline-form .inspector-action,.inspector-inline-form .btn-revoke{margin-top:0}
|
|
1193
|
+
.inspector-actions{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-top:12px}
|
|
1194
|
+
.inspector-actions .inspector-action,.inspector-actions .inspector-input{margin-top:0}
|
|
1195
|
+
a.inspector-action{text-decoration:none}
|
|
1196
|
+
.inspector-actions .btn-revoke{padding:8px 12px;font-size:.8125rem;font-weight:400}
|
|
1197
|
+
td .inspector-actions{margin-top:0}
|
|
1198
|
+
.inspector-input{padding:8px 10px;border:1px solid #0a3300;border-radius:6px;background:#000;color:#33ff00;font:inherit;font-size:.8125rem;margin-top:12px}
|
|
1199
|
+
.inspector-input:focus{outline:none;border-color:#33ff00}
|
|
1200
|
+
.inspector-textarea{display:block;width:100%;min-height:96px;resize:vertical;box-sizing:border-box;white-space:pre-wrap}
|
|
1201
|
+
.inspector-notice{padding:10px 12px;margin-bottom:16px;border:1px solid #0a3300;border-radius:6px;background:#001a00;font-size:.8125rem;overflow-wrap:anywhere}
|
|
1202
|
+
.inspector-notice-error{border-color:#ff4444;color:#ff4444;background:#1a0a0a}
|
|
1203
|
+
.inspector-section code{font-size:.8125rem;overflow-wrap:anywhere}
|
|
1204
|
+
@font-face{
|
|
1205
|
+
font-family:'Geist';font-style:normal;font-weight:100 900;font-display:swap;
|
|
1206
|
+
src:url('/_baseline/fonts/geist-sans.woff2') format('woff2');
|
|
1207
|
+
}
|
|
1208
|
+
@font-face{
|
|
1209
|
+
font-family:'Geist Pixel';font-style:normal;font-weight:400;font-display:swap;
|
|
1210
|
+
src:url('/_baseline/fonts/GeistPixel-Square.woff2') format('woff2');
|
|
1211
|
+
}
|
|
1212
|
+
*{box-sizing:border-box;margin:0;padding:0}
|
|
1213
|
+
body{
|
|
1214
|
+
font-family:'Geist',-apple-system,BlinkMacSystemFont,sans-serif;
|
|
1215
|
+
background:#000;color:#33ff00;min-height:100vh;
|
|
1216
|
+
-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
|
|
1217
|
+
}
|
|
1218
|
+
.brand-bar{
|
|
1219
|
+
border-bottom:1px solid #0a3300;padding:10px 20px;
|
|
1220
|
+
display:flex;align-items:center;gap:10px;font-size:.8125rem;color:#1a8c00;
|
|
1221
|
+
}
|
|
1222
|
+
.brand-bar.sticky{position:sticky;top:0;z-index:1000;background:#000;height:42px;}
|
|
1223
|
+
.brand-bar-title{font-weight:600;color:#33ff00;font-family:'Geist Pixel',monospace;}
|
|
1224
|
+
.brand-bar-links{margin-left:auto;display:flex;gap:16px;}
|
|
1225
|
+
.brand-bar-links a{
|
|
1226
|
+
color:#1a8c00;font-size:.75rem;text-decoration:none;transition:color .15s;
|
|
1227
|
+
}
|
|
1228
|
+
.brand-bar-links a:hover{color:#33ff00;}
|
|
1229
|
+
.brand-bar-links a .full{display:inline;}
|
|
1230
|
+
.brand-bar-links a .short{display:none;}
|
|
1231
|
+
@media(max-width:600px){
|
|
1232
|
+
.brand-bar-links a .full{display:none;}
|
|
1233
|
+
.brand-bar-links a .short{display:inline;}
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
.content{
|
|
1237
|
+
display:flex;align-items:center;justify-content:center;
|
|
1238
|
+
min-height:calc(100vh - 42px);padding:24px 16px;
|
|
1239
|
+
}
|
|
1240
|
+
.content-inner{width:100%;max-width:420px;}
|
|
1241
|
+
.card-title{
|
|
1242
|
+
font-family:'Geist Pixel',monospace;
|
|
1243
|
+
font-size:1.125rem;font-weight:600;margin-bottom:4px;color:#33ff00;
|
|
1244
|
+
}
|
|
1245
|
+
.card-subtitle{color:#1a8c00;font-size:.8125rem;margin-bottom:18px;line-height:1.45;}
|
|
1246
|
+
.powered-by{
|
|
1247
|
+
position:fixed;bottom:0;left:0;right:0;
|
|
1248
|
+
text-align:center;padding:12px;font-size:.6875rem;color:#0a3300;
|
|
1249
|
+
font-family:'Geist Pixel',monospace;
|
|
1250
|
+
}
|
|
1251
|
+
.powered-by a{color:#1a8c00;text-decoration:none;transition:color .15s;}
|
|
1252
|
+
.powered-by a:hover{color:#33ff00;}
|
|
1253
|
+
|
|
1254
|
+
.error-title{
|
|
1255
|
+
font-family:'Geist Pixel',monospace;
|
|
1256
|
+
color:#ff4444;font-size:1.125rem;font-weight:600;margin-bottom:8px;
|
|
1257
|
+
}
|
|
1258
|
+
.error-msg{color:#1a8c00;font-size:.875rem;line-height:1.5;}
|
|
1259
|
+
.error-card{text-align:center;}
|
|
1260
|
+
|
|
1261
|
+
.user-form{margin-bottom:8px;}
|
|
1262
|
+
.user-form:last-of-type{margin-bottom:0;}
|
|
1263
|
+
.user-btn{
|
|
1264
|
+
width:100%;display:flex;align-items:center;gap:12px;
|
|
1265
|
+
padding:10px 12px;border:1px solid #0a3300;border-radius:8px;
|
|
1266
|
+
background:#000;color:inherit;cursor:pointer;text-align:left;
|
|
1267
|
+
font:inherit;transition:border-color .15s;
|
|
1268
|
+
}
|
|
1269
|
+
.user-btn:hover{border-color:#33ff00;}
|
|
1270
|
+
.avatar{
|
|
1271
|
+
width:36px;height:36px;border-radius:50%;
|
|
1272
|
+
background:#0a3300;color:#33ff00;font-weight:600;font-size:.875rem;
|
|
1273
|
+
display:flex;align-items:center;justify-content:center;flex-shrink:0;
|
|
1274
|
+
font-family:'Geist Pixel',monospace;
|
|
1275
|
+
}
|
|
1276
|
+
.user-text{min-width:0;}
|
|
1277
|
+
.user-login{font-weight:600;font-size:.875rem;display:block;color:#33ff00;}
|
|
1278
|
+
.user-meta{color:#1a8c00;font-size:.75rem;margin-top:1px;}
|
|
1279
|
+
.user-email{font-size:.6875rem;color:#116600;word-break:break-all;margin-top:1px;}
|
|
1280
|
+
|
|
1281
|
+
.settings-layout{
|
|
1282
|
+
max-width:920px;margin:0 auto;padding:28px 20px;
|
|
1283
|
+
display:flex;gap:28px;
|
|
1284
|
+
}
|
|
1285
|
+
.settings-sidebar{width:200px;flex-shrink:0;}
|
|
1286
|
+
.settings-sidebar a{
|
|
1287
|
+
display:block;padding:6px 10px;border-radius:6px;color:#1a8c00;
|
|
1288
|
+
text-decoration:none;font-size:.8125rem;transition:color .15s;
|
|
1289
|
+
}
|
|
1290
|
+
.settings-sidebar a:hover{color:#33ff00;}
|
|
1291
|
+
.settings-sidebar a.active{color:#33ff00;font-weight:600;}
|
|
1292
|
+
.settings-main{flex:1;min-width:0;}
|
|
1293
|
+
|
|
1294
|
+
.s-card{
|
|
1295
|
+
padding:18px 0;margin-bottom:14px;border-bottom:1px solid #0a3300;
|
|
1296
|
+
}
|
|
1297
|
+
.s-card:last-child{border-bottom:none;}
|
|
1298
|
+
.s-card-header{display:flex;align-items:center;gap:14px;margin-bottom:14px;}
|
|
1299
|
+
.s-icon{
|
|
1300
|
+
width:42px;height:42px;border-radius:8px;
|
|
1301
|
+
background:#0a3300;display:flex;align-items:center;justify-content:center;
|
|
1302
|
+
font-size:1.125rem;font-weight:700;color:#116600;flex-shrink:0;
|
|
1303
|
+
font-family:'Geist Pixel',monospace;
|
|
1304
|
+
}
|
|
1305
|
+
.s-title{
|
|
1306
|
+
font-family:'Geist Pixel',monospace;
|
|
1307
|
+
font-size:1.25rem;font-weight:600;color:#33ff00;
|
|
1308
|
+
}
|
|
1309
|
+
.s-subtitle{font-size:.75rem;color:#1a8c00;margin-top:2px;}
|
|
1310
|
+
.section-heading{
|
|
1311
|
+
font-size:.9375rem;font-weight:600;margin-bottom:10px;color:#33ff00;
|
|
1312
|
+
display:flex;align-items:center;justify-content:space-between;
|
|
1313
|
+
}
|
|
1314
|
+
.perm-list{list-style:none;}
|
|
1315
|
+
.perm-list li{padding:5px 0;font-size:.8125rem;display:flex;align-items:center;gap:6px;color:#1a8c00;}
|
|
1316
|
+
.check{color:#33ff00;}
|
|
1317
|
+
.org-row{
|
|
1318
|
+
display:flex;align-items:center;gap:8px;padding:7px 0;
|
|
1319
|
+
border-bottom:1px solid #0a3300;font-size:.8125rem;
|
|
1320
|
+
}
|
|
1321
|
+
.org-row:last-child{border-bottom:none;}
|
|
1322
|
+
.org-icon{
|
|
1323
|
+
width:22px;height:22px;border-radius:4px;background:#0a3300;
|
|
1324
|
+
display:flex;align-items:center;justify-content:center;
|
|
1325
|
+
font-size:.625rem;font-weight:700;color:#116600;flex-shrink:0;
|
|
1326
|
+
font-family:'Geist Pixel',monospace;
|
|
1327
|
+
}
|
|
1328
|
+
.org-name{font-weight:600;color:#33ff00;}
|
|
1329
|
+
.badge{font-size:.6875rem;padding:1px 7px;border-radius:999px;font-weight:500;}
|
|
1330
|
+
.badge-granted{background:#0a3300;color:#33ff00;}
|
|
1331
|
+
.badge-denied{background:#1a0a0a;color:#ff4444;}
|
|
1332
|
+
.badge-requested{background:#0a3300;color:#1a8c00;}
|
|
1333
|
+
.btn-revoke{
|
|
1334
|
+
display:inline-block;padding:5px 14px;border-radius:6px;
|
|
1335
|
+
border:1px solid #0a3300;background:transparent;color:#ff4444;
|
|
1336
|
+
font-size:.75rem;font-weight:600;cursor:pointer;transition:border-color .15s;
|
|
1337
|
+
}
|
|
1338
|
+
.btn-revoke:hover{border-color:#ff4444;}
|
|
1339
|
+
.info-text{color:#1a8c00;font-size:.75rem;line-height:1.5;margin-top:10px;}
|
|
1340
|
+
.app-link{
|
|
1341
|
+
display:flex;align-items:center;gap:12px;padding:12px;
|
|
1342
|
+
border:1px solid #0a3300;border-radius:8px;background:#000;
|
|
1343
|
+
text-decoration:none;color:inherit;margin-bottom:8px;transition:border-color .15s;
|
|
1344
|
+
}
|
|
1345
|
+
.app-link:hover{border-color:#33ff00;}
|
|
1346
|
+
.app-link-name{font-weight:600;font-size:.875rem;color:#33ff00;}
|
|
1347
|
+
.app-link-scopes{font-size:.6875rem;color:#1a8c00;margin-top:1px;}
|
|
1348
|
+
.empty{color:#1a8c00;text-align:center;padding:28px 0;font-size:.875rem;}
|
|
1349
|
+
|
|
1350
|
+
.inspector-layout{max-width:960px;margin:0 auto;padding:28px 20px;}
|
|
1351
|
+
.inspector-tabs{display:flex;gap:4px;margin-bottom:20px;}
|
|
1352
|
+
.inspector-tabs a{
|
|
1353
|
+
padding:7px 16px;border-radius:6px;text-decoration:none;
|
|
1354
|
+
font-size:.8125rem;color:#1a8c00;border:1px solid transparent;
|
|
1355
|
+
transition:color .15s,border-color .15s;
|
|
1356
|
+
}
|
|
1357
|
+
.inspector-tabs a:hover{color:#33ff00;}
|
|
1358
|
+
.inspector-tabs a.active{color:#33ff00;font-weight:600;border-color:#0a3300;background:#0a3300;}
|
|
1359
|
+
.inspector-section{margin-bottom:24px;}
|
|
1360
|
+
.inspector-section h2{
|
|
1361
|
+
font-family:'Geist Pixel',monospace;
|
|
1362
|
+
font-size:1rem;font-weight:600;color:#33ff00;margin-bottom:10px;
|
|
1363
|
+
}
|
|
1364
|
+
.inspector-section h3{
|
|
1365
|
+
font-family:'Geist Pixel',monospace;
|
|
1366
|
+
font-size:.875rem;font-weight:600;color:#1a8c00;margin:16px 0 8px;
|
|
1367
|
+
}
|
|
1368
|
+
.inspector-table{width:100%;border-collapse:collapse;margin-bottom:12px;}
|
|
1369
|
+
.inspector-table th,.inspector-table td{
|
|
1370
|
+
text-align:left;padding:8px 12px;border-bottom:1px solid #0a3300;
|
|
1371
|
+
font-size:.8125rem;
|
|
1372
|
+
}
|
|
1373
|
+
.inspector-table th{color:#1a8c00;font-weight:600;font-size:.75rem;text-transform:uppercase;letter-spacing:.04em;}
|
|
1374
|
+
.inspector-table td{color:#33ff00;}
|
|
1375
|
+
.inspector-table tbody tr{transition:background .1s;}
|
|
1376
|
+
.inspector-table tbody tr:hover{background:#0a3300;}
|
|
1377
|
+
.inspector-empty{color:#1a8c00;text-align:center;padding:20px 0;font-size:.8125rem;}
|
|
1378
|
+
|
|
1379
|
+
.checkout-layout{
|
|
1380
|
+
display:flex;min-height:calc(100vh - 42px);
|
|
1381
|
+
}
|
|
1382
|
+
.checkout-summary{
|
|
1383
|
+
flex:1;background:#020;padding:48px 40px 48px 10%;
|
|
1384
|
+
display:flex;flex-direction:column;justify-content:center;
|
|
1385
|
+
border-right:1px solid #0a3300;
|
|
1386
|
+
}
|
|
1387
|
+
.checkout-form-side{
|
|
1388
|
+
flex:1;background:#000;padding:48px 10% 48px 40px;
|
|
1389
|
+
display:flex;flex-direction:column;justify-content:center;
|
|
1390
|
+
}
|
|
1391
|
+
.checkout-merchant{
|
|
1392
|
+
display:flex;align-items:center;gap:10px;margin-bottom:6px;
|
|
1393
|
+
}
|
|
1394
|
+
.checkout-merchant-name{
|
|
1395
|
+
font-family:'Geist Pixel',monospace;
|
|
1396
|
+
font-size:.9375rem;font-weight:600;color:#33ff00;
|
|
1397
|
+
}
|
|
1398
|
+
.checkout-test-badge{
|
|
1399
|
+
font-size:.625rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;
|
|
1400
|
+
background:#0a3300;color:#1a8c00;padding:2px 8px;border-radius:4px;
|
|
1401
|
+
}
|
|
1402
|
+
.checkout-total{
|
|
1403
|
+
font-family:'Geist Pixel',monospace;
|
|
1404
|
+
font-size:2rem;font-weight:700;color:#33ff00;margin:8px 0 28px;
|
|
1405
|
+
}
|
|
1406
|
+
.checkout-line-item{
|
|
1407
|
+
display:flex;align-items:center;gap:14px;padding:14px 0;
|
|
1408
|
+
border-bottom:1px solid #0a3300;
|
|
1409
|
+
}
|
|
1410
|
+
.checkout-line-item:first-child{border-top:1px solid #0a3300;}
|
|
1411
|
+
.checkout-item-icon{
|
|
1412
|
+
width:42px;height:42px;border-radius:6px;background:#0a3300;
|
|
1413
|
+
display:flex;align-items:center;justify-content:center;flex-shrink:0;
|
|
1414
|
+
font-family:'Geist Pixel',monospace;font-size:.875rem;font-weight:700;color:#116600;
|
|
1415
|
+
}
|
|
1416
|
+
.checkout-item-details{flex:1;min-width:0;}
|
|
1417
|
+
.checkout-item-name{font-size:.875rem;font-weight:600;color:#33ff00;}
|
|
1418
|
+
.checkout-item-qty{font-size:.75rem;color:#1a8c00;margin-top:2px;}
|
|
1419
|
+
.checkout-item-price{
|
|
1420
|
+
font-size:.875rem;font-weight:600;color:#33ff00;text-align:right;white-space:nowrap;
|
|
1421
|
+
}
|
|
1422
|
+
.checkout-item-unit{font-size:.6875rem;color:#1a8c00;text-align:right;margin-top:2px;}
|
|
1423
|
+
.checkout-totals{margin-top:20px;}
|
|
1424
|
+
.checkout-totals-row{
|
|
1425
|
+
display:flex;justify-content:space-between;padding:6px 0;
|
|
1426
|
+
font-size:.8125rem;color:#1a8c00;
|
|
1427
|
+
}
|
|
1428
|
+
.checkout-totals-row.total{
|
|
1429
|
+
border-top:1px solid #0a3300;margin-top:8px;padding-top:14px;
|
|
1430
|
+
font-size:.9375rem;font-weight:600;color:#33ff00;
|
|
1431
|
+
}
|
|
1432
|
+
.checkout-form-section{margin-bottom:24px;}
|
|
1433
|
+
.checkout-form-label{
|
|
1434
|
+
font-size:.8125rem;font-weight:600;color:#33ff00;margin-bottom:8px;display:block;
|
|
1435
|
+
}
|
|
1436
|
+
.checkout-input{
|
|
1437
|
+
width:100%;padding:10px 12px;border:1px solid #0a3300;border-radius:6px;
|
|
1438
|
+
background:#020;color:#33ff00;font:inherit;font-size:.875rem;
|
|
1439
|
+
transition:border-color .15s;outline:none;
|
|
1440
|
+
}
|
|
1441
|
+
.checkout-input:focus{border-color:#33ff00;}
|
|
1442
|
+
.checkout-input::placeholder{color:#116600;}
|
|
1443
|
+
.checkout-card-box{
|
|
1444
|
+
border:1px solid #0a3300;border-radius:6px;padding:14px;
|
|
1445
|
+
background:#020;
|
|
1446
|
+
}
|
|
1447
|
+
.checkout-card-row{
|
|
1448
|
+
display:flex;gap:12px;margin-top:10px;
|
|
1449
|
+
}
|
|
1450
|
+
.checkout-card-row .checkout-input{flex:1;}
|
|
1451
|
+
.checkout-error{font-size:.75rem;color:#ff5555;margin-top:6px;}
|
|
1452
|
+
.checkout-sim-note{
|
|
1453
|
+
font-size:.6875rem;color:#1a8c00;margin-top:10px;text-align:center;
|
|
1454
|
+
font-style:italic;
|
|
1455
|
+
}
|
|
1456
|
+
.checkout-pay-btn{
|
|
1457
|
+
width:100%;padding:14px;border:none;border-radius:8px;
|
|
1458
|
+
background:#33ff00;color:#000;font:inherit;font-size:.9375rem;font-weight:700;
|
|
1459
|
+
cursor:pointer;transition:background .15s;
|
|
1460
|
+
font-family:'Geist Pixel',monospace;
|
|
1461
|
+
}
|
|
1462
|
+
.checkout-pay-btn:hover{background:#44ff22;}
|
|
1463
|
+
.checkout-cancel{
|
|
1464
|
+
text-align:center;margin-top:14px;
|
|
1465
|
+
}
|
|
1466
|
+
.checkout-cancel a{
|
|
1467
|
+
color:#1a8c00;text-decoration:none;font-size:.8125rem;
|
|
1468
|
+
transition:color .15s;
|
|
1469
|
+
}
|
|
1470
|
+
.checkout-cancel a:hover{color:#33ff00;}
|
|
1471
|
+
.consent-scopes{list-style:none;margin:0 0 18px;padding:0;border:1px solid #0a3300;}
|
|
1472
|
+
.consent-scopes li{padding:10px 14px;border-bottom:1px solid #0a3300;}
|
|
1473
|
+
.consent-scopes li:last-child{border-bottom:none;}
|
|
1474
|
+
.consent-scope-name{display:block;font-size:.8125rem;color:#33ff00;}
|
|
1475
|
+
.consent-scope-key{display:block;font-size:.6875rem;color:#116600;margin-top:2px;word-break:break-all;}
|
|
1476
|
+
.card-cancel{text-align:center;margin-top:14px;}
|
|
1477
|
+
.card-cancel a{color:#1a8c00;text-decoration:none;font-size:.8125rem;transition:color .15s;}
|
|
1478
|
+
.card-cancel a:hover{color:#33ff00;}
|
|
1479
|
+
.card-form{margin:0 0 8px;}
|
|
1480
|
+
.card-field{margin-bottom:14px;}
|
|
1481
|
+
.card-label{display:block;font-size:.8125rem;font-weight:600;color:#33ff00;margin-bottom:6px;}
|
|
1482
|
+
.card-hint{display:block;font-size:.6875rem;color:#116600;margin-top:4px;}
|
|
1483
|
+
.card-input{
|
|
1484
|
+
width:100%;padding:10px 12px;border:1px solid #0a3300;border-radius:6px;
|
|
1485
|
+
background:#020;color:#33ff00;font:inherit;font-size:.875rem;outline:none;transition:border-color .15s;
|
|
1486
|
+
}
|
|
1487
|
+
.card-input:focus{border-color:#33ff00;}
|
|
1488
|
+
.card-choices{list-style:none;margin:0;padding:0;border:1px solid #0a3300;border-radius:8px;}
|
|
1489
|
+
.card-choices li{border-bottom:1px solid #0a3300;}
|
|
1490
|
+
.card-choices li:last-child{border-bottom:none;}
|
|
1491
|
+
.card-choice{display:flex;align-items:center;gap:10px;padding:10px 12px;cursor:pointer;font-size:.8125rem;color:#33ff00;}
|
|
1492
|
+
.card-choice input{accent-color:#33ff00;flex-shrink:0;}
|
|
1493
|
+
.card-choice-detail{display:block;font-size:.6875rem;color:#1a8c00;margin-top:1px;}
|
|
1494
|
+
.card-submit{
|
|
1495
|
+
width:100%;padding:12px;border:none;border-radius:8px;background:#33ff00;color:#000;
|
|
1496
|
+
font:inherit;font-size:.875rem;font-weight:700;cursor:pointer;transition:background .15s;
|
|
1497
|
+
font-family:'Geist Pixel',monospace;
|
|
1498
|
+
}
|
|
1499
|
+
.card-submit:hover{background:#44ff22;}
|
|
1500
|
+
.card-notice{padding:10px 12px;margin-bottom:14px;border:1px solid #0a3300;border-radius:6px;background:#001a00;font-size:.8125rem;color:#1a8c00;overflow-wrap:anywhere;}
|
|
1501
|
+
.card-notice-error{border-color:#ff4444;color:#ff4444;background:#1a0a0a;}
|
|
1502
|
+
.card-code{display:block;padding:10px 12px;margin-bottom:14px;border:1px solid #0a3300;border-radius:6px;font-size:.75rem;color:#33ff00;overflow-wrap:anywhere;font-family:monospace;}
|
|
1503
|
+
@media(max-width:768px){
|
|
1504
|
+
.checkout-layout{flex-direction:column;}
|
|
1505
|
+
.checkout-summary{padding:32px 20px;border-right:none;border-bottom:1px solid #0a3300;}
|
|
1506
|
+
.checkout-form-side{padding:32px 20px;}
|
|
1507
|
+
}
|
|
1508
|
+
`;
|
|
1509
|
+
var POWERED_BY = `<div class="powered-by">Powered by <a href="https://docs.deploybaseline.com" target="_blank" rel="noopener">baseline</a></div>`;
|
|
1510
|
+
function brandBar(service) {
|
|
1511
|
+
const title = service ? `${escapeHtml(service)} Emulator` : "Emulator";
|
|
1512
|
+
return `<div class="brand-bar">
|
|
1513
|
+
<span class="brand-bar-title">${title}</span>
|
|
1514
|
+
<nav class="brand-bar-links">
|
|
1515
|
+
<a href="https://github.com/deploybaseline/baseline-cli/issues" target="_blank" rel="noopener"><span class="full">Report Issue</span><span class="short">Report</span></a>
|
|
1516
|
+
<a href="https://github.com/deploybaseline/baseline-cli" target="_blank" rel="noopener"><span class="full">Source Code</span><span class="short">Source</span></a>
|
|
1517
|
+
<a href="https://docs.deploybaseline.com" target="_blank" rel="noopener"><span class="full">Learn More</span><span class="short">Learn</span></a>
|
|
1518
|
+
</nav>
|
|
1519
|
+
</div>`;
|
|
1520
|
+
}
|
|
1521
|
+
function head(title) {
|
|
1522
|
+
return `<!DOCTYPE html>
|
|
1523
|
+
<html lang="en">
|
|
1524
|
+
<head>
|
|
1525
|
+
<meta charset="utf-8"/>
|
|
1526
|
+
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
1527
|
+
<link rel="icon" href="/_baseline/favicon.ico"/>
|
|
1528
|
+
<title>${escapeHtml(title)} | baseline</title>
|
|
1529
|
+
<style>${CSS}</style>
|
|
1530
|
+
</head>`;
|
|
1531
|
+
}
|
|
1532
|
+
function renderCardPage(title, subtitle, body, service) {
|
|
1533
|
+
return `${head(title)}
|
|
1534
|
+
<body>
|
|
1535
|
+
${brandBar(service)}
|
|
1536
|
+
<div class="content">
|
|
1537
|
+
<div class="content-inner">
|
|
1538
|
+
<div class="card-title">${escapeHtml(title)}</div>
|
|
1539
|
+
<div class="card-subtitle">${subtitle}</div>
|
|
1540
|
+
${body}
|
|
1541
|
+
</div>
|
|
1542
|
+
</div>
|
|
1543
|
+
${POWERED_BY}
|
|
1544
|
+
</body></html>`;
|
|
1545
|
+
}
|
|
1546
|
+
function renderErrorPage(title, message2, service) {
|
|
1547
|
+
return `${head(title)}
|
|
1548
|
+
<body>
|
|
1549
|
+
${brandBar(service)}
|
|
1550
|
+
<div class="content">
|
|
1551
|
+
<div class="content-inner error-card">
|
|
1552
|
+
<div class="error-title">${escapeHtml(title)}</div>
|
|
1553
|
+
<div class="error-msg">${escapeHtml(message2)}</div>
|
|
1554
|
+
</div>
|
|
1555
|
+
</div>
|
|
1556
|
+
${POWERED_BY}
|
|
1557
|
+
</body></html>`;
|
|
1558
|
+
}
|
|
1559
|
+
function renderJsonDetails(label, value, open2 = false) {
|
|
1560
|
+
const text = JSON.stringify(value, null, 2) ?? "";
|
|
1561
|
+
const bounded2 = text.length > 65536 ? `${text.slice(0, 65536)}
|
|
1562
|
+
[remaining data omitted from preview]` : text;
|
|
1563
|
+
return `<details class="inspector-detail"${open2 ? " open" : ""}><summary>${escapeHtml(label)}</summary><pre class="inspector-json">${escapeHtml(bounded2)}</pre></details>`;
|
|
1564
|
+
}
|
|
1565
|
+
function renderInspectorPage(title, tabs, activeTab, body, service) {
|
|
1566
|
+
const tabLinks = tabs.map(
|
|
1567
|
+
(t) => `<a href="${escapeAttr(t.href)}" class="${t.id === activeTab ? "active" : ""}">${escapeHtml(t.label)}</a>`
|
|
1568
|
+
).join("");
|
|
1569
|
+
return `${head(title)}
|
|
1570
|
+
<body>
|
|
1571
|
+
${brandBar(service)}
|
|
1572
|
+
<div class="inspector-layout">
|
|
1573
|
+
<nav class="inspector-tabs">${tabLinks}</nav>
|
|
1574
|
+
${body}
|
|
1575
|
+
</div>
|
|
1576
|
+
${POWERED_BY}
|
|
1577
|
+
</body></html>`;
|
|
1578
|
+
}
|
|
1579
|
+
function renderFormPostPage(action, fields, service) {
|
|
1580
|
+
const hiddens = Object.entries(fields).filter(([, v]) => v !== null && v !== void 0).map(([k, v]) => `<input type="hidden" name="${escapeAttr(k)}" value="${escapeAttr(v)}"/>`).join("\n");
|
|
1581
|
+
return `${head("Redirecting")}
|
|
1582
|
+
<body onload="document.forms[0].submit()">
|
|
1583
|
+
${brandBar(service)}
|
|
1584
|
+
<div class="content">
|
|
1585
|
+
<div class="content-inner" style="text-align:center">
|
|
1586
|
+
<div class="card-subtitle">Redirecting…</div>
|
|
1587
|
+
<form method="POST" action="${escapeAttr(action)}">
|
|
1588
|
+
${hiddens}
|
|
1589
|
+
<noscript><button type="submit" class="user-btn" style="margin-top:12px;justify-content:center">
|
|
1590
|
+
<span class="user-login">Continue</span>
|
|
1591
|
+
</button></noscript>
|
|
1592
|
+
</form>
|
|
1593
|
+
</div>
|
|
1594
|
+
</div>
|
|
1595
|
+
${POWERED_BY}
|
|
1596
|
+
</body></html>`;
|
|
1597
|
+
}
|
|
1598
|
+
function renderUserButton(opts) {
|
|
1599
|
+
const hiddens = Object.entries(opts.hiddenFields).map(([k, v]) => `<input type="hidden" name="${escapeAttr(k)}" value="${escapeAttr(v)}"/>`).join("");
|
|
1600
|
+
const nameLine = opts.name ? `<div class="user-meta">${escapeHtml(opts.name)}</div>` : "";
|
|
1601
|
+
const emailLine = opts.email ? `<div class="user-email">${escapeHtml(opts.email)}</div>` : "";
|
|
1602
|
+
return `<form class="user-form" method="post" action="${escapeAttr(opts.formAction)}">
|
|
1603
|
+
${hiddens}
|
|
1604
|
+
<button type="submit" class="user-btn">
|
|
1605
|
+
<span class="avatar">${escapeHtml(opts.letter)}</span>
|
|
1606
|
+
<span class="user-text">
|
|
1607
|
+
<span class="user-login">${escapeHtml(opts.login)}</span>
|
|
1608
|
+
${nameLine}${emailLine}
|
|
1609
|
+
</span>
|
|
1610
|
+
</button>
|
|
1611
|
+
</form>`;
|
|
1612
|
+
}
|
|
1613
|
+
function renderEmailPreview(html, text) {
|
|
1614
|
+
if (html) {
|
|
1615
|
+
return `<iframe class="email-preview" sandbox="allow-popups allow-popups-to-escape-sandbox" srcdoc="${escapeAttr(`<base target="_blank">${html}`)}"></iframe>`;
|
|
1616
|
+
}
|
|
1617
|
+
if (text) {
|
|
1618
|
+
return `<div class="s-card"><pre class="info-text email-text">${escapeHtml(text)}</pre></div>`;
|
|
1619
|
+
}
|
|
1620
|
+
return '<div class="empty">No content</div>';
|
|
1621
|
+
}
|
|
1622
|
+
var INSPECTOR = "/_baseline/control/inspector";
|
|
1623
|
+
var STATE_PAGE = `${INSPECTOR}/state`;
|
|
1624
|
+
var __dirname = dirname2(fileURLToPath(import.meta.url));
|
|
1625
|
+
var FONTS = {
|
|
1626
|
+
"geist-sans.woff2": readFileSync(join2(__dirname, "fonts", "geist-sans.woff2")),
|
|
1627
|
+
"GeistPixel-Square.woff2": readFileSync(join2(__dirname, "fonts", "GeistPixel-Square.woff2"))
|
|
1628
|
+
};
|
|
1629
|
+
var FAVICON = readFileSync(join2(__dirname, "fonts", "favicon.ico"));
|
|
1630
|
+
function registerFontRoutes(app) {
|
|
1631
|
+
app.get("/_baseline/fonts/:name", (c) => {
|
|
1632
|
+
const name = c.req.param("name");
|
|
1633
|
+
const buf = FONTS[name];
|
|
1634
|
+
if (!buf) {
|
|
1635
|
+
return c.notFound();
|
|
1636
|
+
}
|
|
1637
|
+
return new Response(buf, {
|
|
1638
|
+
headers: {
|
|
1639
|
+
"Content-Type": "font/woff2",
|
|
1640
|
+
"Cache-Control": "public, max-age=31536000, immutable",
|
|
1641
|
+
"Access-Control-Allow-Origin": "*"
|
|
1642
|
+
}
|
|
1643
|
+
});
|
|
1644
|
+
});
|
|
1645
|
+
app.get(
|
|
1646
|
+
"/_baseline/favicon.ico",
|
|
1647
|
+
(_c) => new Response(FAVICON, {
|
|
1648
|
+
headers: {
|
|
1649
|
+
"Content-Type": "image/x-icon",
|
|
1650
|
+
"Cache-Control": "public, max-age=31536000, immutable"
|
|
1651
|
+
}
|
|
1652
|
+
})
|
|
1653
|
+
);
|
|
1654
|
+
}
|
|
1655
|
+
var MATCH_ANYTHING = /^.*$/;
|
|
1656
|
+
var PARAM_NAME_CHAR = /[A-Za-z0-9_]/;
|
|
1657
|
+
var STATUS_TEXT = {
|
|
1658
|
+
400: "Bad Request",
|
|
1659
|
+
401: "Unauthorized",
|
|
1660
|
+
403: "Forbidden",
|
|
1661
|
+
404: "Not Found",
|
|
1662
|
+
500: "Internal Server Error",
|
|
1663
|
+
501: "Not Implemented",
|
|
1664
|
+
503: "Service Unavailable"
|
|
1665
|
+
};
|
|
1666
|
+
function rejectUpgrade(socket, status, message2 = STATUS_TEXT[status] ?? "") {
|
|
1667
|
+
if (socket.destroyed) {
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
const body = Buffer.from(message2);
|
|
1671
|
+
socket.end(
|
|
1672
|
+
`HTTP/1.1 ${status} ${STATUS_TEXT[status] ?? "Error"}\r
|
|
1673
|
+
Connection: close\r
|
|
1674
|
+
Content-Type: text/plain; charset=utf-8\r
|
|
1675
|
+
Content-Length: ${body.length}\r
|
|
1676
|
+
\r
|
|
1677
|
+
${message2}`,
|
|
1678
|
+
() => socket.destroy()
|
|
1679
|
+
);
|
|
1680
|
+
}
|
|
1681
|
+
var HonoRequest = class {
|
|
1682
|
+
raw;
|
|
1683
|
+
url;
|
|
1684
|
+
method;
|
|
1685
|
+
path;
|
|
1686
|
+
params;
|
|
1687
|
+
strictJson;
|
|
1688
|
+
constructor(request, params, strictJson = false) {
|
|
1689
|
+
this.params = params;
|
|
1690
|
+
this.strictJson = strictJson;
|
|
1691
|
+
this.raw = request;
|
|
1692
|
+
this.url = request.url;
|
|
1693
|
+
this.method = request.method;
|
|
1694
|
+
this.path = new URL(request.url).pathname;
|
|
1695
|
+
}
|
|
1696
|
+
header(name) {
|
|
1697
|
+
if (name) {
|
|
1698
|
+
return this.raw.headers.get(name) ?? void 0;
|
|
1699
|
+
}
|
|
1700
|
+
const headers = {};
|
|
1701
|
+
this.raw.headers.forEach((value, key) => {
|
|
1702
|
+
headers[key] = value;
|
|
1703
|
+
});
|
|
1704
|
+
return headers;
|
|
1705
|
+
}
|
|
1706
|
+
query(name) {
|
|
1707
|
+
return new URL(this.url).searchParams.get(name) ?? void 0;
|
|
1708
|
+
}
|
|
1709
|
+
queries(name) {
|
|
1710
|
+
const values = new URL(this.url).searchParams.getAll(name);
|
|
1711
|
+
return values.length > 0 ? values : void 0;
|
|
1712
|
+
}
|
|
1713
|
+
param(name) {
|
|
1714
|
+
if (!name) {
|
|
1715
|
+
return { ...this.params };
|
|
1716
|
+
}
|
|
1717
|
+
return this.params[name] ?? "";
|
|
1718
|
+
}
|
|
1719
|
+
// biome-ignore lint/suspicious/noExplicitAny: public default, as in Hono; callers read untyped JSON bodies without a cast
|
|
1720
|
+
async json() {
|
|
1721
|
+
try {
|
|
1722
|
+
return await this.raw.json();
|
|
1723
|
+
} catch (error) {
|
|
1724
|
+
if (!this.strictJson) {
|
|
1725
|
+
throw error;
|
|
1726
|
+
}
|
|
1727
|
+
throw Object.assign(new Error("Invalid JSON request body", { cause: error }), { status: 400 });
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
text() {
|
|
1731
|
+
return this.raw.text();
|
|
1732
|
+
}
|
|
1733
|
+
arrayBuffer() {
|
|
1734
|
+
return this.raw.arrayBuffer();
|
|
1735
|
+
}
|
|
1736
|
+
async parseBody() {
|
|
1737
|
+
const contentType = this.header("Content-Type") ?? "";
|
|
1738
|
+
if (contentType.includes("multipart/form-data")) {
|
|
1739
|
+
return formDataToObject(await this.raw.formData());
|
|
1740
|
+
}
|
|
1741
|
+
if (contentType.includes("application/x-www-form-urlencoded")) {
|
|
1742
|
+
const params = new URLSearchParams(await this.raw.text());
|
|
1743
|
+
const out = {};
|
|
1744
|
+
for (const [key, value] of params) {
|
|
1745
|
+
appendBodyValue(out, key, value);
|
|
1746
|
+
}
|
|
1747
|
+
return out;
|
|
1748
|
+
}
|
|
1749
|
+
if (contentType.includes("application/json")) {
|
|
1750
|
+
const body = this.strictJson ? await this.json() : await this.raw.json().catch(() => ({}));
|
|
1751
|
+
return body && typeof body === "object" && !Array.isArray(body) ? body : {};
|
|
1752
|
+
}
|
|
1753
|
+
return {};
|
|
1754
|
+
}
|
|
1755
|
+
};
|
|
1756
|
+
var Context = class {
|
|
1757
|
+
req;
|
|
1758
|
+
vars = /* @__PURE__ */ new Map();
|
|
1759
|
+
responseHeaders = new Headers();
|
|
1760
|
+
headerOperations = [];
|
|
1761
|
+
appliedHeaders = /* @__PURE__ */ new WeakMap();
|
|
1762
|
+
responseStatus = 200;
|
|
1763
|
+
/** In middleware, after `await next()`: the response the rest of the chain produced, as in Hono. */
|
|
1764
|
+
res;
|
|
1765
|
+
notFoundHandler;
|
|
1766
|
+
constructor(request, params, notFoundHandler, strictJson = false) {
|
|
1767
|
+
this.notFoundHandler = notFoundHandler;
|
|
1768
|
+
this.req = new HonoRequest(request, params, strictJson);
|
|
1769
|
+
}
|
|
1770
|
+
get(key) {
|
|
1771
|
+
return this.vars.get(key);
|
|
1772
|
+
}
|
|
1773
|
+
set(key, value) {
|
|
1774
|
+
this.vars.set(key, value);
|
|
1775
|
+
}
|
|
1776
|
+
header(name, value, options) {
|
|
1777
|
+
if (options?.append) {
|
|
1778
|
+
this.responseHeaders.append(name, value);
|
|
1779
|
+
} else {
|
|
1780
|
+
this.responseHeaders.set(name, value);
|
|
1781
|
+
}
|
|
1782
|
+
this.headerOperations.push({ name, value, append: options?.append === true });
|
|
1783
|
+
}
|
|
1784
|
+
status(status) {
|
|
1785
|
+
this.responseStatus = status;
|
|
1786
|
+
}
|
|
1787
|
+
json(data, status, headers) {
|
|
1788
|
+
return this.response(JSON.stringify(data), status, defaultContentType(headers, "application/json; charset=UTF-8"));
|
|
1789
|
+
}
|
|
1790
|
+
text(text, status, headers) {
|
|
1791
|
+
return this.response(text, status, defaultContentType(headers, "text/plain; charset=UTF-8"));
|
|
1792
|
+
}
|
|
1793
|
+
html(html, status, headers) {
|
|
1794
|
+
return this.response(html, status, defaultContentType(headers, "text/html; charset=UTF-8"));
|
|
1795
|
+
}
|
|
1796
|
+
body(body, status, headers) {
|
|
1797
|
+
return this.response(body, status, headers);
|
|
1798
|
+
}
|
|
1799
|
+
redirect(location, status = 302) {
|
|
1800
|
+
return this.response(null, status, { Location: location });
|
|
1801
|
+
}
|
|
1802
|
+
notFound() {
|
|
1803
|
+
return this.notFoundHandler(this);
|
|
1804
|
+
}
|
|
1805
|
+
finalize(response) {
|
|
1806
|
+
const applied = this.appliedHeaders.get(response) ?? 0;
|
|
1807
|
+
if (applied === this.headerOperations.length) {
|
|
1808
|
+
return response;
|
|
1809
|
+
}
|
|
1810
|
+
const headers = new Headers(response.headers);
|
|
1811
|
+
mergeHeaders(headers, this.headerOperations.slice(applied));
|
|
1812
|
+
return new Response(response.body, {
|
|
1813
|
+
status: response.status,
|
|
1814
|
+
statusText: response.statusText,
|
|
1815
|
+
headers
|
|
1816
|
+
});
|
|
1817
|
+
}
|
|
1818
|
+
response(body, status, headers) {
|
|
1819
|
+
const merged = new Headers(headers);
|
|
1820
|
+
mergeHeaders(merged, this.headerOperations);
|
|
1821
|
+
const response = new Response(body, {
|
|
1822
|
+
status: status ?? this.responseStatus,
|
|
1823
|
+
headers: merged
|
|
1824
|
+
});
|
|
1825
|
+
this.appliedHeaders.set(response, this.headerOperations.length);
|
|
1826
|
+
return response;
|
|
1827
|
+
}
|
|
1828
|
+
};
|
|
1829
|
+
var recordedRoutes = /* @__PURE__ */ new Set();
|
|
1830
|
+
function recordRouteHit(method, pattern) {
|
|
1831
|
+
recordCoverageHit(`${method} ${pattern}`);
|
|
1832
|
+
}
|
|
1833
|
+
function recordCoverageHit(key) {
|
|
1834
|
+
const dir = process.env.BASELINE_COVERAGE_DIR;
|
|
1835
|
+
if (!dir) {
|
|
1836
|
+
return;
|
|
1837
|
+
}
|
|
1838
|
+
if (recordedRoutes.has(key)) {
|
|
1839
|
+
return;
|
|
1840
|
+
}
|
|
1841
|
+
recordedRoutes.add(key);
|
|
1842
|
+
try {
|
|
1843
|
+
mkdirSync(dir, { recursive: true });
|
|
1844
|
+
appendFileSync(join3(dir, `${process.pid}-${threadId}.jsonl`), `${JSON.stringify(key)}
|
|
1845
|
+
`);
|
|
1846
|
+
} catch {
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
var SAMPLES_PER_KEY = 3;
|
|
1850
|
+
var sampleCounts = /* @__PURE__ */ new Map();
|
|
1851
|
+
async function recordResponseSample(method, pattern, response) {
|
|
1852
|
+
if (!process.env.BASELINE_RESPONSE_DIR) {
|
|
1853
|
+
return;
|
|
1854
|
+
}
|
|
1855
|
+
if (!((response.headers.get("content-type") ?? "").includes("json") && response.body)) {
|
|
1856
|
+
return;
|
|
1857
|
+
}
|
|
1858
|
+
if (!sampleSlot(method, pattern, response.status)) {
|
|
1859
|
+
return;
|
|
1860
|
+
}
|
|
1861
|
+
try {
|
|
1862
|
+
writeResponseSample(method, pattern, response.status, JSON.parse(await response.clone().text()));
|
|
1863
|
+
} catch {
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
function recordResponseBody(method, pattern, status, body) {
|
|
1867
|
+
if (!process.env.BASELINE_RESPONSE_DIR || body === null || body === void 0) {
|
|
1868
|
+
return;
|
|
1869
|
+
}
|
|
1870
|
+
if (!sampleSlot(method, pattern, status)) {
|
|
1871
|
+
return;
|
|
1872
|
+
}
|
|
1873
|
+
try {
|
|
1874
|
+
writeResponseSample(method, pattern, status, body);
|
|
1875
|
+
} catch {
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
function sampleSlot(method, pattern, status) {
|
|
1879
|
+
const key = `${method} ${pattern} ${status}`;
|
|
1880
|
+
const count = sampleCounts.get(key) ?? 0;
|
|
1881
|
+
if (count >= SAMPLES_PER_KEY) {
|
|
1882
|
+
return false;
|
|
1883
|
+
}
|
|
1884
|
+
sampleCounts.set(key, count + 1);
|
|
1885
|
+
return true;
|
|
1886
|
+
}
|
|
1887
|
+
function writeResponseSample(method, pattern, status, body) {
|
|
1888
|
+
const dir = process.env.BASELINE_RESPONSE_DIR;
|
|
1889
|
+
mkdirSync(dir, { recursive: true });
|
|
1890
|
+
appendFileSync(
|
|
1891
|
+
join3(dir, `${process.pid}-${threadId}.jsonl`),
|
|
1892
|
+
`${JSON.stringify({ method, route: pattern, status, body })}
|
|
1893
|
+
`
|
|
1894
|
+
);
|
|
1895
|
+
}
|
|
1896
|
+
function mergeHeaders(target, operations) {
|
|
1897
|
+
for (const { name, value, append } of operations) {
|
|
1898
|
+
if (append) {
|
|
1899
|
+
target.append(name, value);
|
|
1900
|
+
} else {
|
|
1901
|
+
target.set(name, value);
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
var Hono = class {
|
|
1906
|
+
options;
|
|
1907
|
+
constructor(options = {}) {
|
|
1908
|
+
this.options = options;
|
|
1909
|
+
}
|
|
1910
|
+
get routeTable() {
|
|
1911
|
+
return this.routes.map((r) => ({ method: r.method, path: r.compiled.pattern, source: r.source }));
|
|
1912
|
+
}
|
|
1913
|
+
/**
|
|
1914
|
+
* Operations served inside routes, such as GraphQL root fields
|
|
1915
|
+
* (`"Query.issues"`, `"Mutation.issueCreate"`) or vendor routes a service
|
|
1916
|
+
* dispatches itself (`"GET /v1.0/me/events"`, Microsoft Graph). The
|
|
1917
|
+
* coverage matrix lists them next to routes.
|
|
1918
|
+
*/
|
|
1919
|
+
get operationTable() {
|
|
1920
|
+
return [...this.operations];
|
|
1921
|
+
}
|
|
1922
|
+
/** Upgrade routes (WebSocket endpoints) as registered paths. */
|
|
1923
|
+
get upgradeTable() {
|
|
1924
|
+
return this.upgrades.map((route) => route.compiled.pattern);
|
|
1925
|
+
}
|
|
1926
|
+
/**
|
|
1927
|
+
* Accept upgrade requests (WebSocket handshakes) on `path`. The handler gets
|
|
1928
|
+
* Node's request and socket; `@deploybaseline/ws` completes the handshake.
|
|
1929
|
+
* Upgrades skip middleware, so the handler checks its own credentials.
|
|
1930
|
+
* A request that reaches `fetch` without going through an upgrading server answers 501.
|
|
1931
|
+
*/
|
|
1932
|
+
upgrade(path, handler) {
|
|
1933
|
+
if (this.options.strictRoutes && (path === "/_baseline" || path.startsWith("/_baseline/"))) {
|
|
1934
|
+
throw new Error(`Upgrade route ${path} uses the reserved /_baseline namespace`);
|
|
1935
|
+
}
|
|
1936
|
+
this.upgrades.push({ compiled: compilePath(path), handler });
|
|
1937
|
+
return this;
|
|
1938
|
+
}
|
|
1939
|
+
/** Node `upgrade` event handler for this app's upgrade routes. Sockets it accepts are closed by `dispose()`. */
|
|
1940
|
+
handleUpgrade = (request, socket, head2, path) => {
|
|
1941
|
+
const url = new URL(path ?? request.url ?? "/", `http://${request.headers.host ?? "localhost"}`);
|
|
1942
|
+
const route = this.upgrades.find((candidate) => pathMatches(candidate.compiled, url.pathname));
|
|
1943
|
+
if (!route) {
|
|
1944
|
+
return false;
|
|
1945
|
+
}
|
|
1946
|
+
recordCoverageHit(`WS ${route.compiled.pattern}`);
|
|
1947
|
+
this.upgradedSockets.add(socket);
|
|
1948
|
+
socket.once("close", () => this.upgradedSockets.delete(socket));
|
|
1949
|
+
const params = matchPath(route.compiled, url.pathname) ?? {};
|
|
1950
|
+
Promise.resolve().then(() => route.handler({ request, socket, head: head2, url, params })).catch((error) => {
|
|
1951
|
+
this.options.onError?.(error, new Request(url));
|
|
1952
|
+
rejectUpgrade(socket, 500);
|
|
1953
|
+
});
|
|
1954
|
+
return true;
|
|
1955
|
+
};
|
|
1956
|
+
/** Run cleanup when the service stops, e.g. closing WebSocket connections. */
|
|
1957
|
+
onDispose(callback) {
|
|
1958
|
+
this.disposers.push(callback);
|
|
1959
|
+
return this;
|
|
1960
|
+
}
|
|
1961
|
+
/** Run `onDispose` callbacks, then close any upgraded socket still open. Safe to call more than once. */
|
|
1962
|
+
async dispose() {
|
|
1963
|
+
const callbacks = this.disposers.splice(0);
|
|
1964
|
+
await Promise.allSettled(callbacks.map((callback) => Promise.resolve().then(callback)));
|
|
1965
|
+
for (const socket of this.upgradedSockets) {
|
|
1966
|
+
socket.destroy();
|
|
1967
|
+
}
|
|
1968
|
+
this.upgradedSockets.clear();
|
|
1969
|
+
}
|
|
1970
|
+
upgrades = [];
|
|
1971
|
+
upgradedSockets = /* @__PURE__ */ new Set();
|
|
1972
|
+
disposers = [];
|
|
1973
|
+
/** Declare operations served by this app's routes (see `operationTable`). Duplicates are ignored. */
|
|
1974
|
+
declareOperations(keys) {
|
|
1975
|
+
for (const key of keys) {
|
|
1976
|
+
if (!this.operations.includes(key)) {
|
|
1977
|
+
this.operations.push(key);
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
return this;
|
|
1981
|
+
}
|
|
1982
|
+
operations = [];
|
|
1983
|
+
matchedRoute(method, path) {
|
|
1984
|
+
const route = this.routes.find((r) => r.method === method && matchPath(r.compiled, path)) ?? (method === "HEAD" ? this.routes.find((r) => r.method === "GET" && matchPath(r.compiled, path)) : void 0);
|
|
1985
|
+
return route && { method: route.method, path: route.compiled.pattern, source: route.source };
|
|
1986
|
+
}
|
|
1987
|
+
middleware = [];
|
|
1988
|
+
routes = [];
|
|
1989
|
+
errorHandler = (err) => {
|
|
1990
|
+
const message2 = err instanceof Error ? err.message : "Internal Server Error";
|
|
1991
|
+
return new Response(message2, { status: 500 });
|
|
1992
|
+
};
|
|
1993
|
+
notFoundHandler = () => new Response("404 Not Found", { status: 404 });
|
|
1994
|
+
use(pathOrHandler, ...handlers) {
|
|
1995
|
+
if (typeof pathOrHandler === "string") {
|
|
1996
|
+
this.middleware.push({ method: "ALL", compiled: compilePath(pathOrHandler), handlers });
|
|
1997
|
+
} else {
|
|
1998
|
+
this.middleware.push({ method: "ALL", compiled: compilePath("*"), handlers: [pathOrHandler, ...handlers] });
|
|
1999
|
+
}
|
|
2000
|
+
return this;
|
|
2001
|
+
}
|
|
2002
|
+
on(method, path, ...handlers) {
|
|
2003
|
+
const compiled = compilePath(path);
|
|
2004
|
+
const source = this.options.strictRoutes ? new Error().stack?.split("\n").slice(2).join("\n") : void 0;
|
|
2005
|
+
if (this.options.strictRoutes) {
|
|
2006
|
+
if (path === "/_baseline" || path.startsWith("/_baseline/")) {
|
|
2007
|
+
throw new Error(`Route ${method} ${path} uses the reserved /_baseline namespace`);
|
|
2008
|
+
}
|
|
2009
|
+
const existing = this.routes.find(
|
|
2010
|
+
(r) => r.method === method.toUpperCase() && r.compiled.regex.source === compiled.regex.source
|
|
2011
|
+
);
|
|
2012
|
+
if (existing) {
|
|
2013
|
+
throw new Error(
|
|
2014
|
+
`Duplicate route ${method} ${path}
|
|
2015
|
+
First registration:${existing.source}
|
|
2016
|
+
Second registration:${source}`
|
|
2017
|
+
);
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
this.routes.push({ method: method.toUpperCase(), compiled, handlers, source });
|
|
2021
|
+
return this;
|
|
2022
|
+
}
|
|
2023
|
+
get(path, ...handlers) {
|
|
2024
|
+
return this.on("GET", path, ...handlers);
|
|
2025
|
+
}
|
|
2026
|
+
post(path, ...handlers) {
|
|
2027
|
+
return this.on("POST", path, ...handlers);
|
|
2028
|
+
}
|
|
2029
|
+
put(path, ...handlers) {
|
|
2030
|
+
return this.on("PUT", path, ...handlers);
|
|
2031
|
+
}
|
|
2032
|
+
patch(path, ...handlers) {
|
|
2033
|
+
return this.on("PATCH", path, ...handlers);
|
|
2034
|
+
}
|
|
2035
|
+
delete(path, ...handlers) {
|
|
2036
|
+
return this.on("DELETE", path, ...handlers);
|
|
2037
|
+
}
|
|
2038
|
+
onError(handler) {
|
|
2039
|
+
this.errorHandler = handler;
|
|
2040
|
+
return this;
|
|
2041
|
+
}
|
|
2042
|
+
notFound(handler) {
|
|
2043
|
+
this.notFoundHandler = handler;
|
|
2044
|
+
return this;
|
|
2045
|
+
}
|
|
2046
|
+
async request(input, init) {
|
|
2047
|
+
if (input instanceof Request) {
|
|
2048
|
+
return this.fetch(input);
|
|
2049
|
+
}
|
|
2050
|
+
const url = input.startsWith("/") ? `http://localhost${input}` : input;
|
|
2051
|
+
return this.fetch(new Request(url, init));
|
|
2052
|
+
}
|
|
2053
|
+
fetch = async (request) => {
|
|
2054
|
+
const url = new URL(request.url);
|
|
2055
|
+
const path = url.pathname;
|
|
2056
|
+
const method = request.method.toUpperCase();
|
|
2057
|
+
if (request.headers.get("upgrade")?.toLowerCase() === "websocket" && this.upgrades.some((route) => pathMatches(route.compiled, path) || pathMatches(route.compiled, `${path}/`))) {
|
|
2058
|
+
return Response.json(
|
|
2059
|
+
{
|
|
2060
|
+
error: "WebSocket endpoints need a server that can upgrade connections. Run this service with the Baseline CLI or createEmulator; a plain fetch handler serves HTTP only.",
|
|
2061
|
+
documentation_url: "https://docs.deploybaseline.com/websockets"
|
|
2062
|
+
},
|
|
2063
|
+
{ status: 501 }
|
|
2064
|
+
);
|
|
2065
|
+
}
|
|
2066
|
+
const matched = this.match(method, path);
|
|
2067
|
+
const context = new Context(request, matched.params, this.notFoundHandler, this.options.strictJson);
|
|
2068
|
+
try {
|
|
2069
|
+
const response = await this.dispatch(context, matched.handlers);
|
|
2070
|
+
const finalized = context.finalize(response ?? await this.notFoundHandler(context));
|
|
2071
|
+
if (matched.route && process.env.BASELINE_RESPONSE_DIR) {
|
|
2072
|
+
await recordResponseSample(matched.route.method, matched.route.compiled.pattern, finalized);
|
|
2073
|
+
}
|
|
2074
|
+
return finalized;
|
|
2075
|
+
} catch (err) {
|
|
2076
|
+
this.options.onError?.(err, request);
|
|
2077
|
+
return context.finalize(await this.errorHandler(err, context));
|
|
2078
|
+
}
|
|
2079
|
+
};
|
|
2080
|
+
match(method, path) {
|
|
2081
|
+
const handlers = [];
|
|
2082
|
+
const params = {};
|
|
2083
|
+
for (const route2 of this.middleware) {
|
|
2084
|
+
const match = matchPath(route2.compiled, path);
|
|
2085
|
+
if (!match) {
|
|
2086
|
+
continue;
|
|
2087
|
+
}
|
|
2088
|
+
Object.assign(params, match);
|
|
2089
|
+
for (const handler of route2.handlers) {
|
|
2090
|
+
handlers.push({ handler, params: match });
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
const route = this.routes.find((candidate) => candidate.method === method && pathMatches(candidate.compiled, path)) ?? (method === "HEAD" ? this.routes.find((candidate) => candidate.method === "GET" && pathMatches(candidate.compiled, path)) : void 0);
|
|
2094
|
+
if (route) {
|
|
2095
|
+
recordRouteHit(route.method, route.compiled.pattern);
|
|
2096
|
+
const match = matchPath(route.compiled, path) ?? {};
|
|
2097
|
+
Object.assign(params, match);
|
|
2098
|
+
for (const handler of route.handlers) {
|
|
2099
|
+
handlers.push({ handler, params: match });
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
return { handlers, params, route };
|
|
2103
|
+
}
|
|
2104
|
+
async dispatch(context, handlers) {
|
|
2105
|
+
let index = -1;
|
|
2106
|
+
const run = async (nextIndex) => {
|
|
2107
|
+
if (nextIndex <= index) {
|
|
2108
|
+
throw new Error("next() called multiple times");
|
|
2109
|
+
}
|
|
2110
|
+
index = nextIndex;
|
|
2111
|
+
const matched = handlers[nextIndex];
|
|
2112
|
+
if (!matched) {
|
|
2113
|
+
return void 0;
|
|
2114
|
+
}
|
|
2115
|
+
const originalParams = context.req.param();
|
|
2116
|
+
Object.assign(originalParams, matched.params);
|
|
2117
|
+
let nextResponse;
|
|
2118
|
+
let nextCalled = false;
|
|
2119
|
+
const next = async () => {
|
|
2120
|
+
nextCalled = true;
|
|
2121
|
+
nextResponse = await run(nextIndex + 1);
|
|
2122
|
+
if (nextResponse instanceof Response) {
|
|
2123
|
+
context.res = nextResponse;
|
|
2124
|
+
}
|
|
2125
|
+
};
|
|
2126
|
+
const response = await matched.handler(context, next);
|
|
2127
|
+
if (response instanceof Response) {
|
|
2128
|
+
return response;
|
|
2129
|
+
}
|
|
2130
|
+
if (nextCalled) {
|
|
2131
|
+
return nextResponse;
|
|
2132
|
+
}
|
|
2133
|
+
return response;
|
|
2134
|
+
};
|
|
2135
|
+
return run(0);
|
|
2136
|
+
}
|
|
2137
|
+
};
|
|
2138
|
+
function cors(options = {}) {
|
|
2139
|
+
const origin = options.origin ?? "*";
|
|
2140
|
+
const allowMethods = options.allowMethods ?? ["GET", "HEAD", "PUT", "POST", "DELETE", "PATCH", "OPTIONS"];
|
|
2141
|
+
return async (c, next) => {
|
|
2142
|
+
c.header("Access-Control-Allow-Origin", origin);
|
|
2143
|
+
if (options.credentials) {
|
|
2144
|
+
c.header("Access-Control-Allow-Credentials", "true");
|
|
2145
|
+
}
|
|
2146
|
+
if (c.req.method.toUpperCase() === "OPTIONS") {
|
|
2147
|
+
if (!c.req.header("Access-Control-Request-Method") && options.passThrough?.(c)) {
|
|
2148
|
+
await next();
|
|
2149
|
+
return;
|
|
2150
|
+
}
|
|
2151
|
+
c.header("Access-Control-Allow-Methods", allowMethods.join(","));
|
|
2152
|
+
const allowHeaders = options.allowHeaders?.join(",") ?? c.req.header("Access-Control-Request-Headers");
|
|
2153
|
+
if (allowHeaders) {
|
|
2154
|
+
c.header("Access-Control-Allow-Headers", allowHeaders);
|
|
2155
|
+
}
|
|
2156
|
+
if (options.maxAge !== null && options.maxAge !== void 0) {
|
|
2157
|
+
c.header("Access-Control-Max-Age", String(options.maxAge));
|
|
2158
|
+
}
|
|
2159
|
+
return c.body(null, 204);
|
|
2160
|
+
}
|
|
2161
|
+
await next();
|
|
2162
|
+
};
|
|
2163
|
+
}
|
|
2164
|
+
function serve(options) {
|
|
2165
|
+
const port = options.port ?? 3e3;
|
|
2166
|
+
const { upgrade } = options;
|
|
2167
|
+
const server = createNodeServer(async (req, res) => {
|
|
2168
|
+
try {
|
|
2169
|
+
const request = nodeRequestToFetchRequest(req);
|
|
2170
|
+
const response = await options.fetch(request);
|
|
2171
|
+
await writeFetchResponse(res, response, req.method?.toUpperCase() === "HEAD");
|
|
2172
|
+
} catch (err) {
|
|
2173
|
+
const message2 = err instanceof Error ? err.message : "Internal Server Error";
|
|
2174
|
+
res.statusCode = 500;
|
|
2175
|
+
res.setHeader("Content-Type", "text/plain; charset=UTF-8");
|
|
2176
|
+
res.end(message2);
|
|
2177
|
+
}
|
|
2178
|
+
});
|
|
2179
|
+
server.on("upgrade", (req, socket, head2) => {
|
|
2180
|
+
socket.on("error", () => {
|
|
2181
|
+
});
|
|
2182
|
+
let handled = false;
|
|
2183
|
+
try {
|
|
2184
|
+
handled = upgrade?.(req, socket, head2) ?? false;
|
|
2185
|
+
} catch {
|
|
2186
|
+
rejectUpgrade(socket, 500);
|
|
2187
|
+
return;
|
|
2188
|
+
}
|
|
2189
|
+
if (!handled) {
|
|
2190
|
+
rejectUpgrade(socket, 404);
|
|
2191
|
+
}
|
|
2192
|
+
});
|
|
2193
|
+
server.listen(port, options.hostname ?? "127.0.0.1");
|
|
2194
|
+
return server;
|
|
2195
|
+
}
|
|
2196
|
+
function compilePath(pattern) {
|
|
2197
|
+
if (pattern === "*" || pattern === "/*") {
|
|
2198
|
+
return { pattern, regex: MATCH_ANYTHING, paramNames: [] };
|
|
2199
|
+
}
|
|
2200
|
+
const paramNames = [];
|
|
2201
|
+
let source = "^";
|
|
2202
|
+
for (let i = 0; i < pattern.length; i += 1) {
|
|
2203
|
+
const char = pattern[i];
|
|
2204
|
+
if (char === "/" && pattern[i + 1] === "*" && i + 2 === pattern.length) {
|
|
2205
|
+
source += "(?:/.*)?";
|
|
2206
|
+
break;
|
|
2207
|
+
}
|
|
2208
|
+
if (char === "*") {
|
|
2209
|
+
source += ".*";
|
|
2210
|
+
continue;
|
|
2211
|
+
}
|
|
2212
|
+
if (char !== ":") {
|
|
2213
|
+
source += escapeRegex(char);
|
|
2214
|
+
continue;
|
|
2215
|
+
}
|
|
2216
|
+
let name = "";
|
|
2217
|
+
i += 1;
|
|
2218
|
+
while (i < pattern.length && PARAM_NAME_CHAR.test(pattern[i])) {
|
|
2219
|
+
name += pattern[i];
|
|
2220
|
+
i += 1;
|
|
2221
|
+
}
|
|
2222
|
+
i -= 1;
|
|
2223
|
+
paramNames.push(name);
|
|
2224
|
+
if (pattern[i + 1] === "{") {
|
|
2225
|
+
const close = pattern.indexOf("}", i + 2);
|
|
2226
|
+
if (close < 0) {
|
|
2227
|
+
throw new Error(`Invalid route pattern: ${pattern}`);
|
|
2228
|
+
}
|
|
2229
|
+
const expr = pattern.slice(i + 2, close);
|
|
2230
|
+
source += `(${expr})`;
|
|
2231
|
+
i = close;
|
|
2232
|
+
} else {
|
|
2233
|
+
source += "([^/]+)";
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
source += "$";
|
|
2237
|
+
return { pattern, regex: new RegExp(source), paramNames };
|
|
2238
|
+
}
|
|
2239
|
+
function pathMatches(compiled, path) {
|
|
2240
|
+
const match = matchPath(compiled, path);
|
|
2241
|
+
return match !== null && match !== void 0;
|
|
2242
|
+
}
|
|
2243
|
+
function matchPath(compiled, path) {
|
|
2244
|
+
const match = compiled.regex.exec(path);
|
|
2245
|
+
if (!match) {
|
|
2246
|
+
return null;
|
|
2247
|
+
}
|
|
2248
|
+
const params = {};
|
|
2249
|
+
for (let i = 0; i < compiled.paramNames.length; i += 1) {
|
|
2250
|
+
params[compiled.paramNames[i]] = decodePathParam(match[i + 1] ?? "");
|
|
2251
|
+
}
|
|
2252
|
+
return params;
|
|
2253
|
+
}
|
|
2254
|
+
function decodePathParam(value) {
|
|
2255
|
+
try {
|
|
2256
|
+
return decodeURIComponent(value);
|
|
2257
|
+
} catch {
|
|
2258
|
+
return value;
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
function escapeRegex(value) {
|
|
2262
|
+
return value.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&");
|
|
2263
|
+
}
|
|
2264
|
+
function defaultContentType(headers, contentType) {
|
|
2265
|
+
const out = new Headers(headers);
|
|
2266
|
+
if (!out.has("Content-Type")) {
|
|
2267
|
+
out.set("Content-Type", contentType);
|
|
2268
|
+
}
|
|
2269
|
+
return out;
|
|
2270
|
+
}
|
|
2271
|
+
function formDataToObject(formData) {
|
|
2272
|
+
const out = {};
|
|
2273
|
+
for (const [key, value] of formData) {
|
|
2274
|
+
appendBodyValue(out, key, value);
|
|
2275
|
+
}
|
|
2276
|
+
return out;
|
|
2277
|
+
}
|
|
2278
|
+
function appendBodyValue(target, key, value) {
|
|
2279
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") {
|
|
2280
|
+
return;
|
|
2281
|
+
}
|
|
2282
|
+
const existing = Object.hasOwn(target, key) ? target[key] : void 0;
|
|
2283
|
+
if (existing === void 0) {
|
|
2284
|
+
target[key] = value;
|
|
2285
|
+
} else if (Array.isArray(existing)) {
|
|
2286
|
+
existing.push(value);
|
|
2287
|
+
} else {
|
|
2288
|
+
target[key] = [existing, value];
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
function nodeRequestToFetchRequest(req) {
|
|
2292
|
+
const host = req.headers.host ?? "localhost";
|
|
2293
|
+
const url = new URL(req.url ?? "/", `http://${host}`);
|
|
2294
|
+
const headers = new Headers();
|
|
2295
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
2296
|
+
if (value === null || value === void 0) {
|
|
2297
|
+
continue;
|
|
2298
|
+
}
|
|
2299
|
+
if (Array.isArray(value)) {
|
|
2300
|
+
for (const item of value) {
|
|
2301
|
+
headers.append(key, item);
|
|
2302
|
+
}
|
|
2303
|
+
} else {
|
|
2304
|
+
headers.set(key, value);
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
const method = req.method ?? "GET";
|
|
2308
|
+
const hasBody = method !== "GET" && method !== "HEAD";
|
|
2309
|
+
return new Request(url.toString(), {
|
|
2310
|
+
method,
|
|
2311
|
+
headers,
|
|
2312
|
+
body: hasBody ? req : void 0,
|
|
2313
|
+
duplex: "half"
|
|
2314
|
+
});
|
|
2315
|
+
}
|
|
2316
|
+
async function writeFetchResponse(res, response, headOnly) {
|
|
2317
|
+
res.statusCode = response.status;
|
|
2318
|
+
res.statusMessage = response.statusText;
|
|
2319
|
+
const headersWithCookies = response.headers;
|
|
2320
|
+
const cookies = headersWithCookies.getSetCookie?.();
|
|
2321
|
+
response.headers.forEach((value, key) => {
|
|
2322
|
+
if (key.toLowerCase() === "set-cookie" && cookies && cookies.length > 0) {
|
|
2323
|
+
return;
|
|
2324
|
+
}
|
|
2325
|
+
res.setHeader(key, value);
|
|
2326
|
+
});
|
|
2327
|
+
if (cookies && cookies.length > 0) {
|
|
2328
|
+
res.setHeader("Set-Cookie", cookies);
|
|
2329
|
+
}
|
|
2330
|
+
if (headOnly || !response.body) {
|
|
2331
|
+
res.end();
|
|
2332
|
+
return;
|
|
2333
|
+
}
|
|
2334
|
+
res.flushHeaders();
|
|
2335
|
+
const reader = response.body.getReader();
|
|
2336
|
+
const onClose = () => {
|
|
2337
|
+
if (!res.writableFinished) {
|
|
2338
|
+
reader.cancel().catch(() => {
|
|
2339
|
+
});
|
|
2340
|
+
}
|
|
2341
|
+
};
|
|
2342
|
+
res.once("close", onClose);
|
|
2343
|
+
try {
|
|
2344
|
+
while (true) {
|
|
2345
|
+
const { done, value } = await reader.read();
|
|
2346
|
+
if (done) {
|
|
2347
|
+
break;
|
|
2348
|
+
}
|
|
2349
|
+
if (res.destroyed) {
|
|
2350
|
+
break;
|
|
2351
|
+
}
|
|
2352
|
+
if (!res.write(value)) {
|
|
2353
|
+
await new Promise((resolve2) => {
|
|
2354
|
+
const settle = () => {
|
|
2355
|
+
res.off("drain", settle);
|
|
2356
|
+
res.off("close", settle);
|
|
2357
|
+
resolve2();
|
|
2358
|
+
};
|
|
2359
|
+
res.once("drain", settle);
|
|
2360
|
+
res.once("close", settle);
|
|
2361
|
+
});
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
res.end();
|
|
2365
|
+
} catch (err) {
|
|
2366
|
+
res.destroy(err instanceof Error ? err : void 0);
|
|
2367
|
+
} finally {
|
|
2368
|
+
res.off("close", onClose);
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2371
|
+
var toBuffer = (body) => typeof body === "string" ? Buffer.from(body) : Buffer.from(body);
|
|
2372
|
+
function hmacSha256Hex(secret, body) {
|
|
2373
|
+
return createHmac2("sha256", secret).update(toBuffer(body)).digest("hex");
|
|
2374
|
+
}
|
|
2375
|
+
function githubSignature256(secret, body) {
|
|
2376
|
+
return `sha256=${hmacSha256Hex(secret, body)}`;
|
|
2377
|
+
}
|
|
2378
|
+
var TRAILING_COLON = /:$/;
|
|
2379
|
+
var URL_SCHEME2 = /^[a-z][a-z0-9+.-]*:/i;
|
|
2380
|
+
var MAX_DELIVERIES = 1e3;
|
|
2381
|
+
function githubHeaders({ event, body, subscription, deliveryId }) {
|
|
2382
|
+
const headers = {
|
|
2383
|
+
"Content-Type": "application/json",
|
|
2384
|
+
"X-GitHub-Event": event,
|
|
2385
|
+
"X-GitHub-Delivery": String(deliveryId)
|
|
2386
|
+
};
|
|
2387
|
+
if (subscription.secret) {
|
|
2388
|
+
headers["X-Hub-Signature-256"] = githubSignature256(subscription.secret, body);
|
|
2389
|
+
}
|
|
2390
|
+
return headers;
|
|
2391
|
+
}
|
|
2392
|
+
var WebhookDispatcher = class {
|
|
2393
|
+
options;
|
|
2394
|
+
constructor(options = {}) {
|
|
2395
|
+
this.options = options;
|
|
2396
|
+
if (options.neutral) {
|
|
2397
|
+
this.headerFactory = () => ({ "Content-Type": "application/json" });
|
|
2398
|
+
}
|
|
2399
|
+
this.setRetryPolicy(options.retry);
|
|
2400
|
+
}
|
|
2401
|
+
retryPolicy;
|
|
2402
|
+
pendingRetries = /* @__PURE__ */ new Map();
|
|
2403
|
+
subscriptions = [];
|
|
2404
|
+
deliveries = [];
|
|
2405
|
+
subscriptionIdCounter = 1;
|
|
2406
|
+
deliveryIdCounter = 1;
|
|
2407
|
+
headerFactory = githubHeaders;
|
|
2408
|
+
/**
|
|
2409
|
+
* Deliver to subscriptions whose URL starts with `<scheme>:` through
|
|
2410
|
+
* `transport` instead of HTTP. Delivery records, retries, and redelivery work
|
|
2411
|
+
* the same way.
|
|
2412
|
+
*/
|
|
2413
|
+
setTransport(scheme, transport) {
|
|
2414
|
+
this.transports.set(`${scheme.replace(TRAILING_COLON, "")}:`, transport);
|
|
2415
|
+
}
|
|
2416
|
+
transports = /* @__PURE__ */ new Map();
|
|
2417
|
+
setHeaderFactory(factory) {
|
|
2418
|
+
this.headerFactory = factory;
|
|
2419
|
+
}
|
|
2420
|
+
setRetryPolicy(policy) {
|
|
2421
|
+
if (policy && (!Number.isInteger(policy.attempts) || policy.attempts < 1)) {
|
|
2422
|
+
throw new Error("Webhook retry attempts must be a positive integer");
|
|
2423
|
+
}
|
|
2424
|
+
this.retryPolicy = policy;
|
|
2425
|
+
}
|
|
2426
|
+
/** Resolves once no retries are scheduled. For tests. */
|
|
2427
|
+
async settled() {
|
|
2428
|
+
while (this.pendingRetries.size > 0 || this.inFlight.size > 0) {
|
|
2429
|
+
await Promise.allSettled([...this.inFlight]);
|
|
2430
|
+
if (this.pendingRetries.size > 0) {
|
|
2431
|
+
await new Promise((resolve2) => setTimeout(resolve2, 5));
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
}
|
|
2435
|
+
inFlight = /* @__PURE__ */ new Set();
|
|
2436
|
+
register(sub) {
|
|
2437
|
+
const { id: explicitId, ...rest } = sub;
|
|
2438
|
+
let id;
|
|
2439
|
+
if (explicitId === void 0) {
|
|
2440
|
+
id = this.subscriptionIdCounter;
|
|
2441
|
+
this.subscriptionIdCounter += 1;
|
|
2442
|
+
} else {
|
|
2443
|
+
id = explicitId;
|
|
2444
|
+
}
|
|
2445
|
+
if (id >= this.subscriptionIdCounter) {
|
|
2446
|
+
this.subscriptionIdCounter = id + 1;
|
|
2447
|
+
}
|
|
2448
|
+
const subscription = { ...rest, id };
|
|
2449
|
+
this.subscriptions.push(subscription);
|
|
2450
|
+
return subscription;
|
|
2451
|
+
}
|
|
2452
|
+
unregister(id) {
|
|
2453
|
+
const idx = this.subscriptions.findIndex((s) => s.id === id);
|
|
2454
|
+
if (idx === -1) {
|
|
2455
|
+
return false;
|
|
2456
|
+
}
|
|
2457
|
+
this.subscriptions.splice(idx, 1);
|
|
2458
|
+
return true;
|
|
2459
|
+
}
|
|
2460
|
+
getSubscription(id) {
|
|
2461
|
+
return this.subscriptions.find((s) => s.id === id);
|
|
2462
|
+
}
|
|
2463
|
+
getSubscriptions(owner, repo) {
|
|
2464
|
+
return this.subscriptions.filter((s) => {
|
|
2465
|
+
if (owner && s.owner !== owner) {
|
|
2466
|
+
return false;
|
|
2467
|
+
}
|
|
2468
|
+
if (repo !== void 0 && s.repo !== repo) {
|
|
2469
|
+
return false;
|
|
2470
|
+
}
|
|
2471
|
+
return true;
|
|
2472
|
+
});
|
|
2473
|
+
}
|
|
2474
|
+
updateSubscription(id, data) {
|
|
2475
|
+
const sub = this.subscriptions.find((s) => s.id === id);
|
|
2476
|
+
if (!sub) {
|
|
2477
|
+
return void 0;
|
|
2478
|
+
}
|
|
2479
|
+
Object.assign(sub, data);
|
|
2480
|
+
return sub;
|
|
2481
|
+
}
|
|
2482
|
+
async dispatch(event, action, payload, owner, repo) {
|
|
2483
|
+
const matchingSubs = this.subscriptions.filter((s) => {
|
|
2484
|
+
if (!s.active) {
|
|
2485
|
+
return false;
|
|
2486
|
+
}
|
|
2487
|
+
if (s.owner !== owner) {
|
|
2488
|
+
return false;
|
|
2489
|
+
}
|
|
2490
|
+
if (repo !== void 0) {
|
|
2491
|
+
if (s.repo !== repo) {
|
|
2492
|
+
return false;
|
|
2493
|
+
}
|
|
2494
|
+
} else if (s.repo !== void 0) {
|
|
2495
|
+
return false;
|
|
2496
|
+
}
|
|
2497
|
+
return event === "ping" || s.events.includes("*") || s.events.includes(event);
|
|
2498
|
+
});
|
|
2499
|
+
for (const sub of matchingSubs) {
|
|
2500
|
+
const first = await this.deliver(sub, event, action, payload);
|
|
2501
|
+
if (this.retryPolicy && !first.success && retryable(first)) {
|
|
2502
|
+
this.scheduleRetry(sub, first, 2);
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
scheduleRetry(sub, first, attempt) {
|
|
2507
|
+
const policy = this.retryPolicy;
|
|
2508
|
+
if (!policy || attempt > policy.attempts || this.options.signal?.aborted) {
|
|
2509
|
+
return;
|
|
2510
|
+
}
|
|
2511
|
+
const delay = Math.min((policy.backoffMs ?? 1e3) * 2 ** (attempt - 2), policy.maxBackoffMs ?? 6e4);
|
|
2512
|
+
const timer = setTimeout(() => {
|
|
2513
|
+
this.pendingRetries.delete(timer);
|
|
2514
|
+
if (!(this.subscriptions.includes(sub) && sub.active)) {
|
|
2515
|
+
return;
|
|
2516
|
+
}
|
|
2517
|
+
const run = this.deliver(sub, first.event, first.action, first.payload, { attempt, retry_of: first.id }).then(
|
|
2518
|
+
(delivery) => {
|
|
2519
|
+
if (!delivery.success && retryable(delivery)) {
|
|
2520
|
+
this.scheduleRetry(sub, first, attempt + 1);
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
);
|
|
2524
|
+
this.inFlight.add(run);
|
|
2525
|
+
run.finally(() => this.inFlight.delete(run));
|
|
2526
|
+
}, delay);
|
|
2527
|
+
timer.unref?.();
|
|
2528
|
+
this.pendingRetries.set(timer, () => clearTimeout(timer));
|
|
2529
|
+
}
|
|
2530
|
+
async redeliver(deliveryId, options = {}) {
|
|
2531
|
+
const original = this.deliveries.find((delivery) => delivery.id === deliveryId);
|
|
2532
|
+
if (!original) {
|
|
2533
|
+
return void 0;
|
|
2534
|
+
}
|
|
2535
|
+
const sub = this.getSubscription(original.hook_id);
|
|
2536
|
+
if (!sub?.active) {
|
|
2537
|
+
return void 0;
|
|
2538
|
+
}
|
|
2539
|
+
return this.deliver(sub, original.event, original.action, original.payload, {
|
|
2540
|
+
headers: options.headers,
|
|
2541
|
+
redelivery_of: original.id,
|
|
2542
|
+
redelivery_count: (original.redelivery_count ?? 0) + 1
|
|
2543
|
+
});
|
|
2544
|
+
}
|
|
2545
|
+
async deliver(sub, event, action, payload, options = {}) {
|
|
2546
|
+
const body = JSON.stringify(payload);
|
|
2547
|
+
const id = this.deliveryIdCounter;
|
|
2548
|
+
this.deliveryIdCounter += 1;
|
|
2549
|
+
const delivery = {
|
|
2550
|
+
id,
|
|
2551
|
+
hook_id: sub.id,
|
|
2552
|
+
event,
|
|
2553
|
+
action,
|
|
2554
|
+
payload: body === void 0 ? void 0 : JSON.parse(body),
|
|
2555
|
+
status_code: null,
|
|
2556
|
+
delivered_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2557
|
+
duration: null,
|
|
2558
|
+
success: false,
|
|
2559
|
+
...options.redelivery_of === void 0 ? {} : { redelivery_of: options.redelivery_of, redelivery_count: options.redelivery_count },
|
|
2560
|
+
...options.attempt === void 0 ? {} : { attempt: options.attempt, retry_of: options.retry_of }
|
|
2561
|
+
};
|
|
2562
|
+
this.deliveries.push(delivery);
|
|
2563
|
+
if (this.deliveries.length > MAX_DELIVERIES) {
|
|
2564
|
+
this.deliveries.splice(0, this.deliveries.length - MAX_DELIVERIES);
|
|
2565
|
+
}
|
|
2566
|
+
try {
|
|
2567
|
+
const headers = {
|
|
2568
|
+
...this.headerFactory({ event, action, body, subscription: sub, deliveryId: delivery.id }),
|
|
2569
|
+
...options.headers
|
|
2570
|
+
};
|
|
2571
|
+
const start = Date.now();
|
|
2572
|
+
const transport = this.transports.get(URL_SCHEME2.exec(sub.url)?.[0].toLowerCase() ?? "");
|
|
2573
|
+
if (transport) {
|
|
2574
|
+
const result = await transport({
|
|
2575
|
+
subscription: sub,
|
|
2576
|
+
event,
|
|
2577
|
+
payload: delivery.payload,
|
|
2578
|
+
body,
|
|
2579
|
+
headers,
|
|
2580
|
+
deliveryId: delivery.id,
|
|
2581
|
+
attempt: options.attempt ?? 1,
|
|
2582
|
+
redeliveryCount: options.redelivery_count
|
|
2583
|
+
});
|
|
2584
|
+
delivery.duration = Date.now() - start;
|
|
2585
|
+
delivery.status_code = result.status_code;
|
|
2586
|
+
delivery.success = result.success;
|
|
2587
|
+
return delivery;
|
|
2588
|
+
}
|
|
2589
|
+
const response = await fetch(sub.url, {
|
|
2590
|
+
method: "POST",
|
|
2591
|
+
headers,
|
|
2592
|
+
body,
|
|
2593
|
+
signal: this.options.signal ? AbortSignal.any([this.options.signal, AbortSignal.timeout(1e4)]) : AbortSignal.timeout(1e4)
|
|
2594
|
+
});
|
|
2595
|
+
delivery.duration = Date.now() - start;
|
|
2596
|
+
delivery.status_code = response.status;
|
|
2597
|
+
delivery.success = response.ok;
|
|
2598
|
+
} catch {
|
|
2599
|
+
delivery.duration = 0;
|
|
2600
|
+
delivery.success = false;
|
|
2601
|
+
}
|
|
2602
|
+
return delivery;
|
|
2603
|
+
}
|
|
2604
|
+
snapshot() {
|
|
2605
|
+
return {
|
|
2606
|
+
subscriptions: this.subscriptions.map((sub) => ({ ...sub, events: [...sub.events] })),
|
|
2607
|
+
nextSubscriptionId: this.subscriptionIdCounter,
|
|
2608
|
+
nextDeliveryId: this.deliveryIdCounter
|
|
2609
|
+
};
|
|
2610
|
+
}
|
|
2611
|
+
/**
|
|
2612
|
+
* Replace subscriptions and counters with saved ones. Delivery history and
|
|
2613
|
+
* scheduled retries are dropped, as after a restart. The header factory and
|
|
2614
|
+
* retry policy stay as configured.
|
|
2615
|
+
*/
|
|
2616
|
+
restore(snapshot) {
|
|
2617
|
+
this.clear();
|
|
2618
|
+
this.subscriptions = snapshot.subscriptions.map((sub) => ({ ...sub, events: [...sub.events] }));
|
|
2619
|
+
const highest = this.subscriptions.reduce((max, sub) => Math.max(max, sub.id), 0);
|
|
2620
|
+
this.subscriptionIdCounter = Math.max(snapshot.nextSubscriptionId, highest + 1);
|
|
2621
|
+
this.deliveryIdCounter = Math.max(snapshot.nextDeliveryId, 1);
|
|
2622
|
+
}
|
|
2623
|
+
getDeliveries(hookId) {
|
|
2624
|
+
if (hookId !== void 0) {
|
|
2625
|
+
return this.deliveries.filter((d) => d.hook_id === hookId);
|
|
2626
|
+
}
|
|
2627
|
+
return [...this.deliveries];
|
|
2628
|
+
}
|
|
2629
|
+
clear() {
|
|
2630
|
+
for (const cancel of this.pendingRetries.values()) {
|
|
2631
|
+
cancel();
|
|
2632
|
+
}
|
|
2633
|
+
this.pendingRetries.clear();
|
|
2634
|
+
this.subscriptions.length = 0;
|
|
2635
|
+
this.deliveries.length = 0;
|
|
2636
|
+
this.subscriptionIdCounter = 1;
|
|
2637
|
+
this.deliveryIdCounter = 1;
|
|
2638
|
+
}
|
|
2639
|
+
};
|
|
2640
|
+
function retryable(delivery) {
|
|
2641
|
+
return delivery.status_code === null || delivery.status_code === 429 || delivery.status_code >= 500;
|
|
2642
|
+
}
|
|
2643
|
+
var FAULT_PROFILES = {
|
|
2644
|
+
degraded: [
|
|
2645
|
+
{ match: "*", latency_ms: 300 },
|
|
2646
|
+
{ match: "*", rate: 0.1, response: { status: 503, body: { error: "Service Unavailable (injected fault)" } } }
|
|
2647
|
+
],
|
|
2648
|
+
throttled: [
|
|
2649
|
+
{
|
|
2650
|
+
match: "*",
|
|
2651
|
+
rate: 0.3,
|
|
2652
|
+
response: { status: 429, headers: { "Retry-After": "1" }, body: { error: "Too Many Requests (injected fault)" } }
|
|
2653
|
+
}
|
|
2654
|
+
]
|
|
2655
|
+
};
|
|
2656
|
+
function normalizeFaults(service, config) {
|
|
2657
|
+
const raw = Array.isArray(config) ? { rules: config } : config;
|
|
2658
|
+
if (!raw || typeof raw !== "object") {
|
|
2659
|
+
throw new Error(`${service}.faults must be a list of rules or an object`);
|
|
2660
|
+
}
|
|
2661
|
+
const { seed, profile, rules = [] } = raw;
|
|
2662
|
+
if (seed !== void 0 && !Number.isInteger(seed)) {
|
|
2663
|
+
throw new Error(`${service}.faults.seed must be an integer`);
|
|
2664
|
+
}
|
|
2665
|
+
if (profile !== void 0 && !Object.hasOwn(FAULT_PROFILES, profile)) {
|
|
2666
|
+
throw new Error(`${service}.faults.profile must be one of ${Object.keys(FAULT_PROFILES).join(", ")}`);
|
|
2667
|
+
}
|
|
2668
|
+
if (!Array.isArray(rules)) {
|
|
2669
|
+
throw new Error(`${service}.faults.rules must be a list`);
|
|
2670
|
+
}
|
|
2671
|
+
for (const [index, rule] of rules.entries()) {
|
|
2672
|
+
validateRule(`${service}.faults.rules[${index}]`, rule);
|
|
2673
|
+
}
|
|
2674
|
+
return {
|
|
2675
|
+
...seed === void 0 ? {} : { seed },
|
|
2676
|
+
...profile === void 0 ? {} : { profile },
|
|
2677
|
+
rules: [...profile ? FAULT_PROFILES[profile] : [], ...rules]
|
|
2678
|
+
};
|
|
2679
|
+
}
|
|
2680
|
+
function validateRule(where, rule) {
|
|
2681
|
+
if (!rule || typeof rule !== "object") {
|
|
2682
|
+
throw new Error(`${where} must be an object`);
|
|
2683
|
+
}
|
|
2684
|
+
const { match, rate, latency_ms, response } = rule;
|
|
2685
|
+
if (typeof match !== "string" || !match) {
|
|
2686
|
+
throw new Error(`${where}.match must be "*" or "METHOD /path"`);
|
|
2687
|
+
}
|
|
2688
|
+
if (rate !== void 0 && (typeof rate !== "number" || rate < 0 || rate > 1)) {
|
|
2689
|
+
throw new Error(`${where}.rate must be a number from 0 to 1`);
|
|
2690
|
+
}
|
|
2691
|
+
if (latency_ms !== void 0 && (typeof latency_ms !== "number" || latency_ms < 0 || latency_ms > 6e4)) {
|
|
2692
|
+
throw new Error(`${where}.latency_ms must be from 0 to 60000`);
|
|
2693
|
+
}
|
|
2694
|
+
if (latency_ms === void 0 && response === void 0) {
|
|
2695
|
+
throw new Error(`${where} needs latency_ms, response, or both`);
|
|
2696
|
+
}
|
|
2697
|
+
if (response !== void 0) {
|
|
2698
|
+
const status = response?.status;
|
|
2699
|
+
if (!Number.isInteger(status) || status < 100 || status > 599) {
|
|
2700
|
+
throw new Error(`${where}.response.status must be an HTTP status code`);
|
|
2701
|
+
}
|
|
2702
|
+
const { headers } = response;
|
|
2703
|
+
if (headers !== void 0) {
|
|
2704
|
+
try {
|
|
2705
|
+
new Headers(headers);
|
|
2706
|
+
} catch (error) {
|
|
2707
|
+
throw new Error(`${where}.response.headers must map valid header names to strings`, { cause: error });
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
var MAX_DELAY_MS = 6e4;
|
|
2713
|
+
var WHITESPACE = /\s+/;
|
|
2714
|
+
function seededRandom(seed) {
|
|
2715
|
+
let state = seed >>> 0;
|
|
2716
|
+
return () => {
|
|
2717
|
+
state = state + 1831565813 >>> 0;
|
|
2718
|
+
let t = state;
|
|
2719
|
+
t = Math.imul(t ^ t >>> 15, t | 1);
|
|
2720
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
2721
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
2722
|
+
};
|
|
2723
|
+
}
|
|
2724
|
+
function compileMatch(match) {
|
|
2725
|
+
if (match === "*") {
|
|
2726
|
+
return () => true;
|
|
2727
|
+
}
|
|
2728
|
+
const [first, ...rest] = match.trim().split(WHITESPACE);
|
|
2729
|
+
const method = rest.length ? first.toUpperCase() : void 0;
|
|
2730
|
+
const pattern = rest.length ? rest.join(" ") : first;
|
|
2731
|
+
const source = pattern.split("/").map(
|
|
2732
|
+
(segment) => segment.startsWith(":") ? "[^/]+" : segment.split("*").map((part) => part.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).join(".*")
|
|
2733
|
+
).join("/");
|
|
2734
|
+
const regex = new RegExp(`^${source}/?$`);
|
|
2735
|
+
return (requestMethod, path) => (!method || method === requestMethod) && regex.test(path);
|
|
2736
|
+
}
|
|
2737
|
+
var FaultInjector = class {
|
|
2738
|
+
service;
|
|
2739
|
+
active = { rules: [] };
|
|
2740
|
+
compiled = [];
|
|
2741
|
+
random = Math.random;
|
|
2742
|
+
constructor(service, config) {
|
|
2743
|
+
this.service = service;
|
|
2744
|
+
if (config !== void 0) {
|
|
2745
|
+
this.set(config);
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
set(config) {
|
|
2749
|
+
this.active = normalizeFaults(this.service, config);
|
|
2750
|
+
this.compiled = this.active.rules.map((rule) => ({ rule, test: compileMatch(rule.match) }));
|
|
2751
|
+
this.random = this.active.seed === void 0 ? Math.random : seededRandom(this.active.seed);
|
|
2752
|
+
return this.current();
|
|
2753
|
+
}
|
|
2754
|
+
clear() {
|
|
2755
|
+
this.set([]);
|
|
2756
|
+
}
|
|
2757
|
+
current() {
|
|
2758
|
+
return structuredClone(this.active);
|
|
2759
|
+
}
|
|
2760
|
+
/**
|
|
2761
|
+
* Wrap a fetch handler: delay and short-circuit matching requests. Control,
|
|
2762
|
+
* inspector, and font paths are exempt, so opening the inspector never
|
|
2763
|
+
* shifts the seeded sequence.
|
|
2764
|
+
*/
|
|
2765
|
+
wrap(fetch2) {
|
|
2766
|
+
return async (request) => {
|
|
2767
|
+
if (this.compiled.length === 0) {
|
|
2768
|
+
return fetch2(request);
|
|
2769
|
+
}
|
|
2770
|
+
const path = new URL(request.url).pathname;
|
|
2771
|
+
if (path.startsWith("/_baseline") || path.startsWith("/_inspector") || path.startsWith("/fonts/")) {
|
|
2772
|
+
return fetch2(request);
|
|
2773
|
+
}
|
|
2774
|
+
let delay = 0;
|
|
2775
|
+
let response;
|
|
2776
|
+
for (const { rule, test } of this.compiled) {
|
|
2777
|
+
if (!test(request.method, path)) {
|
|
2778
|
+
continue;
|
|
2779
|
+
}
|
|
2780
|
+
if (rule.rate !== void 0 && rule.rate < 1 && this.random() >= rule.rate) {
|
|
2781
|
+
continue;
|
|
2782
|
+
}
|
|
2783
|
+
delay += rule.latency_ms ?? 0;
|
|
2784
|
+
if (rule.response && !response) {
|
|
2785
|
+
({ response } = rule);
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
if (delay > 0) {
|
|
2789
|
+
await new Promise((resolve2) => setTimeout(resolve2, Math.min(delay, MAX_DELAY_MS)));
|
|
2790
|
+
}
|
|
2791
|
+
if (!response) {
|
|
2792
|
+
return fetch2(request);
|
|
2793
|
+
}
|
|
2794
|
+
const headers = new Headers({ "X-Baseline-Fault": "injected", ...response.headers });
|
|
2795
|
+
if (response.body === void 0) {
|
|
2796
|
+
return new Response(null, { status: response.status, headers });
|
|
2797
|
+
}
|
|
2798
|
+
if (typeof response.body === "string") {
|
|
2799
|
+
return new Response(response.body, { status: response.status, headers });
|
|
2800
|
+
}
|
|
2801
|
+
headers.set("Content-Type", "application/json");
|
|
2802
|
+
return new Response(JSON.stringify(response.body), { status: response.status, headers });
|
|
2803
|
+
};
|
|
2804
|
+
}
|
|
2805
|
+
};
|
|
2806
|
+
var WHITESPACE2 = /\s+/;
|
|
2807
|
+
function splitName(name) {
|
|
2808
|
+
const [given, ...rest] = name.trim().split(WHITESPACE2);
|
|
2809
|
+
return { given, family: rest.join(" ") || void 0 };
|
|
2810
|
+
}
|
|
2811
|
+
var lowerCase = (value) => typeof value === "string" ? value.toLowerCase() : void 0;
|
|
2812
|
+
var defaultUsername = (email) => email.split("@")[0].replace(/[^A-Za-z0-9-]+/g, "-").replace(/^-+|-+$/g, "") || "user";
|
|
2813
|
+
var compact = (user) => Object.fromEntries(Object.entries(user).filter(([, v]) => v !== void 0));
|
|
2814
|
+
function applyIdentities(seed, identities, mapping) {
|
|
2815
|
+
if (!(mapping && identities.length)) {
|
|
2816
|
+
return seed;
|
|
2817
|
+
}
|
|
2818
|
+
const existing = seed?.[mapping.key];
|
|
2819
|
+
if (existing !== void 0 && !Array.isArray(existing)) {
|
|
2820
|
+
return seed;
|
|
2821
|
+
}
|
|
2822
|
+
const users = [...existing ?? []];
|
|
2823
|
+
const seen = new Set(users.map(mapping.match).filter((key) => !!key));
|
|
2824
|
+
for (const identity of identities) {
|
|
2825
|
+
const { override, ...person } = identity;
|
|
2826
|
+
if (override === false) {
|
|
2827
|
+
continue;
|
|
2828
|
+
}
|
|
2829
|
+
const user = compact({
|
|
2830
|
+
...mapping.user({ ...person, username: person.username ?? defaultUsername(person.email) }),
|
|
2831
|
+
...override ?? {}
|
|
2832
|
+
});
|
|
2833
|
+
const key = mapping.match(user);
|
|
2834
|
+
if (key && seen.has(key)) {
|
|
2835
|
+
continue;
|
|
2836
|
+
}
|
|
2837
|
+
if (key) {
|
|
2838
|
+
seen.add(key);
|
|
2839
|
+
}
|
|
2840
|
+
users.push(user);
|
|
2841
|
+
}
|
|
2842
|
+
return { ...seed, [mapping.key]: users };
|
|
2843
|
+
}
|
|
2844
|
+
var DEFAULT_DOCS_URL = "https://docs.deploybaseline.com";
|
|
2845
|
+
function getDocsUrl(c) {
|
|
2846
|
+
return c.get("docsUrl") ?? DEFAULT_DOCS_URL;
|
|
2847
|
+
}
|
|
2848
|
+
function errorStatus(err) {
|
|
2849
|
+
if (err && typeof err === "object" && "status" in err) {
|
|
2850
|
+
const s = err.status;
|
|
2851
|
+
if (typeof s === "number" && Number.isFinite(s)) {
|
|
2852
|
+
return s;
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
return 500;
|
|
2856
|
+
}
|
|
2857
|
+
function createApiErrorHandler(documentationUrl) {
|
|
2858
|
+
return (err, c) => {
|
|
2859
|
+
if (documentationUrl) {
|
|
2860
|
+
c.set("docsUrl", documentationUrl);
|
|
2861
|
+
}
|
|
2862
|
+
const status = errorStatus(err);
|
|
2863
|
+
const message2 = err instanceof Error ? err.message : "Internal Server Error";
|
|
2864
|
+
const errors = err instanceof ApiError && err.errors?.length ? { errors: err.errors } : {};
|
|
2865
|
+
return c.json(
|
|
2866
|
+
{
|
|
2867
|
+
message: message2,
|
|
2868
|
+
...errors,
|
|
2869
|
+
documentation_url: getDocsUrl(c)
|
|
2870
|
+
},
|
|
2871
|
+
status
|
|
2872
|
+
);
|
|
2873
|
+
};
|
|
2874
|
+
}
|
|
2875
|
+
function createErrorHandler(documentationUrl) {
|
|
2876
|
+
return async (c, next) => {
|
|
2877
|
+
if (documentationUrl) {
|
|
2878
|
+
c.set("docsUrl", documentationUrl);
|
|
2879
|
+
}
|
|
2880
|
+
await next();
|
|
2881
|
+
};
|
|
2882
|
+
}
|
|
2883
|
+
var errorHandler = createErrorHandler();
|
|
2884
|
+
var ApiError = class extends Error {
|
|
2885
|
+
status;
|
|
2886
|
+
/** Detail entries, sent as `errors`: GitHub's `{ resource, field, code }` objects, or plain messages. */
|
|
2887
|
+
errors;
|
|
2888
|
+
constructor(status, message2, errors, options) {
|
|
2889
|
+
super(message2, options);
|
|
2890
|
+
this.status = status;
|
|
2891
|
+
this.errors = errors;
|
|
2892
|
+
this.name = "ApiError";
|
|
2893
|
+
}
|
|
2894
|
+
};
|
|
2895
|
+
var BASIC_SCHEME = /^basic /i;
|
|
2896
|
+
var ALGS = ["RS256", "ES256"];
|
|
2897
|
+
function signingKeyStoreKey(service) {
|
|
2898
|
+
return `${service}.signing_keys`;
|
|
2899
|
+
}
|
|
2900
|
+
var inflight = /* @__PURE__ */ new WeakMap();
|
|
2901
|
+
var privateKeys = /* @__PURE__ */ new WeakMap();
|
|
2902
|
+
function isRecord3(value) {
|
|
2903
|
+
if (typeof value !== "object" || value === null) {
|
|
2904
|
+
return false;
|
|
2905
|
+
}
|
|
2906
|
+
const v = value;
|
|
2907
|
+
return typeof v.kid === "string" && ALGS.includes(v.alg) && typeof v.private_jwk === "object" && v.private_jwk !== null && typeof v.public_jwk === "object" && v.public_jwk !== null;
|
|
2908
|
+
}
|
|
2909
|
+
function importPrivateJwk(jwk) {
|
|
2910
|
+
return createPrivateKey({ key: jwk, format: "jwk" });
|
|
2911
|
+
}
|
|
2912
|
+
function publicJwkOf(privateKey) {
|
|
2913
|
+
const jwk = createPublicKey2(privateKey).export({ format: "jwk" });
|
|
2914
|
+
if (jwk.kty === "EC") {
|
|
2915
|
+
return { kty: jwk.kty, crv: jwk.crv, x: jwk.x, y: jwk.y };
|
|
2916
|
+
}
|
|
2917
|
+
return { kty: jwk.kty, n: jwk.n, e: jwk.e };
|
|
2918
|
+
}
|
|
2919
|
+
function toSigningKey(record) {
|
|
2920
|
+
let privateKey = privateKeys.get(record);
|
|
2921
|
+
if (!privateKey) {
|
|
2922
|
+
privateKey = importPrivateJwk(record.private_jwk);
|
|
2923
|
+
privateKeys.set(record, privateKey);
|
|
2924
|
+
}
|
|
2925
|
+
return {
|
|
2926
|
+
kid: record.kid,
|
|
2927
|
+
alg: record.alg,
|
|
2928
|
+
privateKey,
|
|
2929
|
+
publicJwk: { ...record.public_jwk, kid: record.kid, use: "sig", alg: record.alg }
|
|
2930
|
+
};
|
|
2931
|
+
}
|
|
2932
|
+
async function generateRecord(kid, alg) {
|
|
2933
|
+
const privateKey = await new Promise((resolve2, reject) => {
|
|
2934
|
+
const done = (error, _publicKey, key) => error ? reject(error) : resolve2(key);
|
|
2935
|
+
if (alg === "ES256") {
|
|
2936
|
+
generateKeyPair("ec", { namedCurve: "P-256" }, done);
|
|
2937
|
+
} else {
|
|
2938
|
+
generateKeyPair("rsa", { modulusLength: 2048 }, done);
|
|
2939
|
+
}
|
|
2940
|
+
});
|
|
2941
|
+
return {
|
|
2942
|
+
kid,
|
|
2943
|
+
alg,
|
|
2944
|
+
private_jwk: privateKey.export({ format: "jwk" }),
|
|
2945
|
+
public_jwk: publicJwkOf(privateKey)
|
|
2946
|
+
};
|
|
2947
|
+
}
|
|
2948
|
+
async function createSigningKeys(store, options) {
|
|
2949
|
+
const storeKey = signingKeyStoreKey(options.service);
|
|
2950
|
+
const existing = store.getData(storeKey);
|
|
2951
|
+
if (isRecord3(existing)) {
|
|
2952
|
+
return toSigningKey(existing);
|
|
2953
|
+
}
|
|
2954
|
+
let pending = inflight.get(store);
|
|
2955
|
+
if (!pending) {
|
|
2956
|
+
pending = /* @__PURE__ */ new Map();
|
|
2957
|
+
inflight.set(store, pending);
|
|
2958
|
+
}
|
|
2959
|
+
let generation = pending.get(storeKey);
|
|
2960
|
+
if (!generation) {
|
|
2961
|
+
const map = pending;
|
|
2962
|
+
generation = generateRecord(options.kid, options.alg ?? "RS256").then((record) => {
|
|
2963
|
+
const current = store.getData(storeKey);
|
|
2964
|
+
if (isRecord3(current)) {
|
|
2965
|
+
return current;
|
|
2966
|
+
}
|
|
2967
|
+
store.setData(storeKey, record);
|
|
2968
|
+
return record;
|
|
2969
|
+
}).finally(() => map.delete(storeKey));
|
|
2970
|
+
pending.set(storeKey, generation);
|
|
2971
|
+
}
|
|
2972
|
+
return toSigningKey(await generation);
|
|
2973
|
+
}
|
|
2974
|
+
function seedSigningKey(store, options, seed) {
|
|
2975
|
+
const jwk = seed?.private_jwk;
|
|
2976
|
+
const alg = options.alg ?? "RS256";
|
|
2977
|
+
const ec = alg === "ES256";
|
|
2978
|
+
if (!jwk || typeof jwk !== "object" || jwk.kty !== (ec ? "EC" : "RSA") || ec && jwk.crv !== "P-256" || typeof jwk.d !== "string") {
|
|
2979
|
+
throw new Error(
|
|
2980
|
+
ec ? `${options.service}.signing_key.private_jwk must be a P-256 EC private JWK (kty "EC", crv "P-256" with "d")` : `${options.service}.signing_key.private_jwk must be an RSA private JWK (kty "RSA" with "d")`
|
|
2981
|
+
);
|
|
2982
|
+
}
|
|
2983
|
+
let privateKey;
|
|
2984
|
+
try {
|
|
2985
|
+
privateKey = importPrivateJwk(jwk);
|
|
2986
|
+
} catch (error) {
|
|
2987
|
+
throw new Error(`${options.service}.signing_key.private_jwk is not a valid ${ec ? "EC" : "RSA"} private key`, {
|
|
2988
|
+
cause: error
|
|
2989
|
+
});
|
|
2990
|
+
}
|
|
2991
|
+
const kid = typeof jwk.kid === "string" && jwk.kid ? jwk.kid : options.kid;
|
|
2992
|
+
const record = {
|
|
2993
|
+
kid,
|
|
2994
|
+
alg,
|
|
2995
|
+
private_jwk: privateKey.export({ format: "jwk" }),
|
|
2996
|
+
public_jwk: publicJwkOf(privateKey)
|
|
2997
|
+
};
|
|
2998
|
+
store.setData(signingKeyStoreKey(options.service), record);
|
|
2999
|
+
return record;
|
|
3000
|
+
}
|
|
3001
|
+
async function jwksDocument(store, options) {
|
|
3002
|
+
const key = await createSigningKeys(store, options);
|
|
3003
|
+
return { keys: [key.publicJwk] };
|
|
3004
|
+
}
|
|
3005
|
+
async function signJwt(store, key, claims, options) {
|
|
3006
|
+
const { kid, alg, privateKey } = await createSigningKeys(store, key);
|
|
3007
|
+
const builder = new SignJWT(claims).setProtectedHeader({ alg, kid, typ: "JWT" });
|
|
3008
|
+
if (options.issuer !== void 0) {
|
|
3009
|
+
builder.setIssuer(options.issuer);
|
|
3010
|
+
}
|
|
3011
|
+
if (options.audience !== void 0) {
|
|
3012
|
+
builder.setAudience(options.audience);
|
|
3013
|
+
}
|
|
3014
|
+
if (options.subject !== void 0) {
|
|
3015
|
+
builder.setSubject(options.subject);
|
|
3016
|
+
}
|
|
3017
|
+
builder.setIssuedAt(options.issuedAt);
|
|
3018
|
+
if (options.notBefore !== void 0) {
|
|
3019
|
+
builder.setNotBefore(options.notBefore);
|
|
3020
|
+
}
|
|
3021
|
+
if (options.expiresIn !== null) {
|
|
3022
|
+
builder.setExpirationTime(options.expiresIn ?? "1h");
|
|
3023
|
+
}
|
|
3024
|
+
return builder.sign(privateKey);
|
|
3025
|
+
}
|
|
3026
|
+
function issueIdToken(store, key, claims, options) {
|
|
3027
|
+
return signJwt(store, key, claims, options);
|
|
3028
|
+
}
|
|
3029
|
+
function pendingCodes(store, service) {
|
|
3030
|
+
const storeKey = `${service}.oauth.pendingCodes`;
|
|
3031
|
+
let map = store.getData(storeKey);
|
|
3032
|
+
if (!map) {
|
|
3033
|
+
map = /* @__PURE__ */ new Map();
|
|
3034
|
+
store.setData(storeKey, map);
|
|
3035
|
+
}
|
|
3036
|
+
return map;
|
|
3037
|
+
}
|
|
3038
|
+
function basicClientCredentials(header) {
|
|
3039
|
+
if (!(header && BASIC_SCHEME.test(header))) {
|
|
3040
|
+
return null;
|
|
3041
|
+
}
|
|
3042
|
+
const decoded = Buffer.from(header.slice(6).trim(), "base64").toString("utf8");
|
|
3043
|
+
const sep = decoded.indexOf(":");
|
|
3044
|
+
if (sep === -1) {
|
|
3045
|
+
return null;
|
|
3046
|
+
}
|
|
3047
|
+
const formDecode = (value) => {
|
|
3048
|
+
try {
|
|
3049
|
+
return decodeURIComponent(value.replace(/\+/g, " "));
|
|
3050
|
+
} catch {
|
|
3051
|
+
return value;
|
|
3052
|
+
}
|
|
3053
|
+
};
|
|
3054
|
+
return { clientId: formDecode(decoded.slice(0, sep)), clientSecret: formDecode(decoded.slice(sep + 1)) };
|
|
3055
|
+
}
|
|
3056
|
+
function verifyPkce(verifier, challenge, method) {
|
|
3057
|
+
if (!verifier) {
|
|
3058
|
+
return false;
|
|
3059
|
+
}
|
|
3060
|
+
const m = (method ?? "plain").toLowerCase();
|
|
3061
|
+
if (m === "s256") {
|
|
3062
|
+
return createHash2("sha256").update(verifier).digest("base64url") === challenge;
|
|
3063
|
+
}
|
|
3064
|
+
if (m === "plain") {
|
|
3065
|
+
return verifier === challenge;
|
|
3066
|
+
}
|
|
3067
|
+
return false;
|
|
3068
|
+
}
|
|
3069
|
+
var PROTOCOL_VERSION = 1;
|
|
3070
|
+
var FAULT_KINDS = ["latency", "response"];
|
|
3071
|
+
function processChannel() {
|
|
3072
|
+
if (typeof process.send !== "function") {
|
|
3073
|
+
throw new Error(
|
|
3074
|
+
"This is a Baseline provider entry. It is started by the Baseline CLI, which talks to it over IPC; run `npx @deploybaseline/cli` instead."
|
|
3075
|
+
);
|
|
3076
|
+
}
|
|
3077
|
+
return {
|
|
3078
|
+
send: (message2) => {
|
|
3079
|
+
if (process.connected) {
|
|
3080
|
+
process.send(message2, void 0, {}, () => {
|
|
3081
|
+
});
|
|
3082
|
+
}
|
|
3083
|
+
},
|
|
3084
|
+
onMessage: (listener) => process.on("message", (message2) => listener(message2)),
|
|
3085
|
+
onDisconnect: (listener) => process.once("disconnect", listener),
|
|
3086
|
+
disconnect: () => {
|
|
3087
|
+
if (process.connected) {
|
|
3088
|
+
process.disconnect();
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
};
|
|
3092
|
+
}
|
|
3093
|
+
function isControlPath(pathname) {
|
|
3094
|
+
return ["control", "faults", "state"].some(
|
|
3095
|
+
(root) => pathname === `/_baseline/${root}` || pathname.startsWith(`/_baseline/${root}/`)
|
|
3096
|
+
);
|
|
3097
|
+
}
|
|
3098
|
+
async function toWireRequest(request) {
|
|
3099
|
+
const body = ["GET", "HEAD"].includes(request.method) ? void 0 : Buffer.from(await request.arrayBuffer());
|
|
3100
|
+
return {
|
|
3101
|
+
method: request.method,
|
|
3102
|
+
url: request.url,
|
|
3103
|
+
headers: [...request.headers],
|
|
3104
|
+
...body?.length ? { body: body.toString("base64") } : {}
|
|
3105
|
+
};
|
|
3106
|
+
}
|
|
3107
|
+
function fromWireResponse(wire) {
|
|
3108
|
+
const headers = new Headers();
|
|
3109
|
+
for (const [name, value] of wire.headers) {
|
|
3110
|
+
headers.append(name, value);
|
|
3111
|
+
}
|
|
3112
|
+
const body = wire.body !== void 0 && ![204, 304].includes(wire.status) ? Buffer.from(wire.body, "base64") : null;
|
|
3113
|
+
return new Response(body, { status: wire.status, headers });
|
|
3114
|
+
}
|
|
3115
|
+
var ProviderError = class extends Error {
|
|
3116
|
+
code;
|
|
3117
|
+
name = "ProviderError";
|
|
3118
|
+
constructor(code, message2, options) {
|
|
3119
|
+
super(message2, options);
|
|
3120
|
+
this.code = code;
|
|
3121
|
+
}
|
|
3122
|
+
};
|
|
3123
|
+
function serializeValue(value) {
|
|
3124
|
+
if (value instanceof Map) {
|
|
3125
|
+
return { __type: "Map", entries: [...value.entries()].map(([k, v]) => [k, serializeValue(v)]) };
|
|
3126
|
+
}
|
|
3127
|
+
if (value instanceof Set) {
|
|
3128
|
+
return { __type: "Set", values: [...value.values()] };
|
|
3129
|
+
}
|
|
3130
|
+
return value;
|
|
3131
|
+
}
|
|
3132
|
+
function deserializeValue(value) {
|
|
3133
|
+
if (value !== null && typeof value === "object" && "__type" in value) {
|
|
3134
|
+
const tagged = value;
|
|
3135
|
+
if (tagged.__type === "Map") {
|
|
3136
|
+
const entries = tagged.entries;
|
|
3137
|
+
return new Map(entries.map(([k, v]) => [k, deserializeValue(v)]));
|
|
3138
|
+
}
|
|
3139
|
+
if (tagged.__type === "Set") {
|
|
3140
|
+
return new Set(tagged.values);
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
return value;
|
|
3144
|
+
}
|
|
3145
|
+
var Collection = class {
|
|
3146
|
+
items = /* @__PURE__ */ new Map();
|
|
3147
|
+
indexes = /* @__PURE__ */ new Map();
|
|
3148
|
+
autoId = 1;
|
|
3149
|
+
fieldNames;
|
|
3150
|
+
indexFields;
|
|
3151
|
+
/** Called after every change, so the owning store can count revisions. */
|
|
3152
|
+
onChange;
|
|
3153
|
+
constructor(indexFields = [], onChange = () => {
|
|
3154
|
+
}) {
|
|
3155
|
+
this.indexFields = indexFields;
|
|
3156
|
+
this.onChange = onChange;
|
|
3157
|
+
this.fieldNames = indexFields.map(String).sort();
|
|
3158
|
+
for (const field of indexFields) {
|
|
3159
|
+
this.indexes.set(String(field), /* @__PURE__ */ new Map());
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
addToIndex(item) {
|
|
3163
|
+
for (const field of this.indexFields) {
|
|
3164
|
+
const value = item[field];
|
|
3165
|
+
if (value === void 0 || value === null) {
|
|
3166
|
+
continue;
|
|
3167
|
+
}
|
|
3168
|
+
const indexMap = this.indexes.get(String(field));
|
|
3169
|
+
const key = String(value);
|
|
3170
|
+
if (!indexMap.has(key)) {
|
|
3171
|
+
indexMap.set(key, /* @__PURE__ */ new Set());
|
|
3172
|
+
}
|
|
3173
|
+
indexMap.get(key).add(item.id);
|
|
3174
|
+
}
|
|
3175
|
+
}
|
|
3176
|
+
removeFromIndex(item) {
|
|
3177
|
+
for (const field of this.indexFields) {
|
|
3178
|
+
const value = item[field];
|
|
3179
|
+
if (value === void 0 || value === null) {
|
|
3180
|
+
continue;
|
|
3181
|
+
}
|
|
3182
|
+
const indexMap = this.indexes.get(String(field));
|
|
3183
|
+
const key = String(value);
|
|
3184
|
+
indexMap.get(key)?.delete(item.id);
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
insert(data) {
|
|
3188
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3189
|
+
const explicitId = data.id !== null && data.id !== void 0 && data.id > 0 ? data.id : void 0;
|
|
3190
|
+
let id;
|
|
3191
|
+
if (explicitId === void 0) {
|
|
3192
|
+
id = this.autoId;
|
|
3193
|
+
this.autoId += 1;
|
|
3194
|
+
} else {
|
|
3195
|
+
id = explicitId;
|
|
3196
|
+
}
|
|
3197
|
+
if (id >= this.autoId) {
|
|
3198
|
+
this.autoId = id + 1;
|
|
3199
|
+
}
|
|
3200
|
+
const item = {
|
|
3201
|
+
...data,
|
|
3202
|
+
id,
|
|
3203
|
+
created_at: now,
|
|
3204
|
+
updated_at: now
|
|
3205
|
+
};
|
|
3206
|
+
this.items.set(id, item);
|
|
3207
|
+
this.addToIndex(item);
|
|
3208
|
+
this.onChange();
|
|
3209
|
+
return item;
|
|
3210
|
+
}
|
|
3211
|
+
get(id) {
|
|
3212
|
+
return this.items.get(id);
|
|
3213
|
+
}
|
|
3214
|
+
findBy(field, value) {
|
|
3215
|
+
if (this.indexes.has(String(field))) {
|
|
3216
|
+
const ids = this.indexes.get(String(field)).get(String(value));
|
|
3217
|
+
if (!ids) {
|
|
3218
|
+
return [];
|
|
3219
|
+
}
|
|
3220
|
+
return Array.from(ids).map((id) => this.items.get(id)).filter(Boolean);
|
|
3221
|
+
}
|
|
3222
|
+
return this.all().filter((item) => item[field] === value);
|
|
3223
|
+
}
|
|
3224
|
+
findOneBy(field, value) {
|
|
3225
|
+
return this.findBy(field, value)[0];
|
|
3226
|
+
}
|
|
3227
|
+
update(id, data) {
|
|
3228
|
+
const existing = this.items.get(id);
|
|
3229
|
+
if (!existing) {
|
|
3230
|
+
return void 0;
|
|
3231
|
+
}
|
|
3232
|
+
this.removeFromIndex(existing);
|
|
3233
|
+
const updated = {
|
|
3234
|
+
...existing,
|
|
3235
|
+
...data,
|
|
3236
|
+
id,
|
|
3237
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
3238
|
+
};
|
|
3239
|
+
this.items.set(id, updated);
|
|
3240
|
+
this.addToIndex(updated);
|
|
3241
|
+
this.onChange();
|
|
3242
|
+
return updated;
|
|
3243
|
+
}
|
|
3244
|
+
delete(id) {
|
|
3245
|
+
const existing = this.items.get(id);
|
|
3246
|
+
if (!existing) {
|
|
3247
|
+
return false;
|
|
3248
|
+
}
|
|
3249
|
+
this.removeFromIndex(existing);
|
|
3250
|
+
this.items.delete(id);
|
|
3251
|
+
this.onChange();
|
|
3252
|
+
return true;
|
|
3253
|
+
}
|
|
3254
|
+
all() {
|
|
3255
|
+
return Array.from(this.items.values());
|
|
3256
|
+
}
|
|
3257
|
+
query(options = {}) {
|
|
3258
|
+
let results = this.all();
|
|
3259
|
+
if (options.filter) {
|
|
3260
|
+
results = results.filter(options.filter);
|
|
3261
|
+
}
|
|
3262
|
+
const total_count = results.length;
|
|
3263
|
+
if (options.sort) {
|
|
3264
|
+
results.sort(options.sort);
|
|
3265
|
+
}
|
|
3266
|
+
const page = options.page ?? 1;
|
|
3267
|
+
const per_page = Math.min(options.per_page ?? 30, 100);
|
|
3268
|
+
const start = (page - 1) * per_page;
|
|
3269
|
+
const paged = results.slice(start, start + per_page);
|
|
3270
|
+
return {
|
|
3271
|
+
items: paged,
|
|
3272
|
+
total_count,
|
|
3273
|
+
page,
|
|
3274
|
+
per_page,
|
|
3275
|
+
has_next: start + per_page < total_count,
|
|
3276
|
+
has_prev: page > 1
|
|
3277
|
+
};
|
|
3278
|
+
}
|
|
3279
|
+
count(filter) {
|
|
3280
|
+
if (!filter) {
|
|
3281
|
+
return this.items.size;
|
|
3282
|
+
}
|
|
3283
|
+
return this.all().filter(filter).length;
|
|
3284
|
+
}
|
|
3285
|
+
clear() {
|
|
3286
|
+
this.items.clear();
|
|
3287
|
+
for (const indexMap of this.indexes.values()) {
|
|
3288
|
+
indexMap.clear();
|
|
3289
|
+
}
|
|
3290
|
+
this.autoId = 1;
|
|
3291
|
+
this.onChange();
|
|
3292
|
+
}
|
|
3293
|
+
snapshot() {
|
|
3294
|
+
return {
|
|
3295
|
+
items: this.all(),
|
|
3296
|
+
autoId: this.autoId,
|
|
3297
|
+
indexFields: this.fieldNames
|
|
3298
|
+
};
|
|
3299
|
+
}
|
|
3300
|
+
restore(snap) {
|
|
3301
|
+
this.clear();
|
|
3302
|
+
this.autoId = snap.autoId;
|
|
3303
|
+
for (const item of snap.items) {
|
|
3304
|
+
this.items.set(item.id, item);
|
|
3305
|
+
this.addToIndex(item);
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
};
|
|
3309
|
+
var Store = class {
|
|
3310
|
+
collections = /* @__PURE__ */ new Map();
|
|
3311
|
+
_data = /* @__PURE__ */ new Map();
|
|
3312
|
+
_revision = 0;
|
|
3313
|
+
/**
|
|
3314
|
+
* Increases on every collection change, `setData`, `reset`, and `restore`.
|
|
3315
|
+
* Runners compare it before and after a request to decide whether to save.
|
|
3316
|
+
* Mutating an object returned by `getData` in place does not count.
|
|
3317
|
+
*/
|
|
3318
|
+
get revision() {
|
|
3319
|
+
return this._revision;
|
|
3320
|
+
}
|
|
3321
|
+
touch = () => {
|
|
3322
|
+
this._revision += 1;
|
|
3323
|
+
};
|
|
3324
|
+
collection(name, indexFields = []) {
|
|
3325
|
+
const existing = this.collections.get(name);
|
|
3326
|
+
if (existing) {
|
|
3327
|
+
if (indexFields.length > 0) {
|
|
3328
|
+
const requested = indexFields.map(String).sort();
|
|
3329
|
+
if (existing.fieldNames.length !== requested.length || existing.fieldNames.some((f, i) => f !== requested[i])) {
|
|
3330
|
+
throw new Error(
|
|
3331
|
+
`Collection "${name}" already exists with indexes [${existing.fieldNames}] but was requested with [${requested}]`
|
|
3332
|
+
);
|
|
3333
|
+
}
|
|
3334
|
+
}
|
|
3335
|
+
return existing;
|
|
3336
|
+
}
|
|
3337
|
+
const col = new Collection(indexFields, this.touch);
|
|
3338
|
+
this.collections.set(name, col);
|
|
3339
|
+
return col;
|
|
3340
|
+
}
|
|
3341
|
+
hasCollection(name) {
|
|
3342
|
+
return this.collections.has(name);
|
|
3343
|
+
}
|
|
3344
|
+
getData(key) {
|
|
3345
|
+
return this._data.get(key);
|
|
3346
|
+
}
|
|
3347
|
+
setData(key, value) {
|
|
3348
|
+
this._data.set(key, value);
|
|
3349
|
+
this.touch();
|
|
3350
|
+
}
|
|
3351
|
+
reset() {
|
|
3352
|
+
for (const collection of this.collections.values()) {
|
|
3353
|
+
collection.clear();
|
|
3354
|
+
}
|
|
3355
|
+
this._data.clear();
|
|
3356
|
+
this.touch();
|
|
3357
|
+
}
|
|
3358
|
+
snapshot() {
|
|
3359
|
+
const collections = {};
|
|
3360
|
+
for (const [name, col] of this.collections) {
|
|
3361
|
+
collections[name] = col.snapshot();
|
|
3362
|
+
}
|
|
3363
|
+
const data = {};
|
|
3364
|
+
for (const [key, value] of this._data) {
|
|
3365
|
+
data[key] = serializeValue(value);
|
|
3366
|
+
}
|
|
3367
|
+
return { collections, data };
|
|
3368
|
+
}
|
|
3369
|
+
restore(snap) {
|
|
3370
|
+
const snapshotNames = new Set(Object.keys(snap.collections));
|
|
3371
|
+
for (const name of this.collections.keys()) {
|
|
3372
|
+
if (!snapshotNames.has(name)) {
|
|
3373
|
+
this.collections.delete(name);
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3376
|
+
for (const [name, colSnap] of Object.entries(snap.collections)) {
|
|
3377
|
+
const indexFields = colSnap.indexFields;
|
|
3378
|
+
const col = this.collection(name, indexFields);
|
|
3379
|
+
col.restore(colSnap);
|
|
3380
|
+
}
|
|
3381
|
+
this._data.clear();
|
|
3382
|
+
for (const [key, value] of Object.entries(snap.data)) {
|
|
3383
|
+
this._data.set(key, deserializeValue(value));
|
|
3384
|
+
}
|
|
3385
|
+
this.touch();
|
|
3386
|
+
}
|
|
3387
|
+
};
|
|
3388
|
+
function createServer(plugin, options = {}) {
|
|
3389
|
+
const port = options.port ?? 4e3;
|
|
3390
|
+
const baseUrl = options.baseUrl ?? `http://localhost:${port}`;
|
|
3391
|
+
const app = new Hono();
|
|
3392
|
+
const store = new Store();
|
|
3393
|
+
const webhooks = new WebhookDispatcher();
|
|
3394
|
+
const tokenMap = /* @__PURE__ */ new Map();
|
|
3395
|
+
if (options.tokens) {
|
|
3396
|
+
for (const [token, user] of Object.entries(options.tokens)) {
|
|
3397
|
+
tokenMap.set(token, {
|
|
3398
|
+
login: user.login,
|
|
3399
|
+
id: user.id,
|
|
3400
|
+
scopes: user.scopes ?? ["repo", "user", "admin:org", "admin:repo_hook"]
|
|
3401
|
+
});
|
|
3402
|
+
}
|
|
3403
|
+
}
|
|
3404
|
+
const docsUrl = options.docsUrl ?? `https://docs.deploybaseline.com/${plugin.name}`;
|
|
3405
|
+
registerFontRoutes(app);
|
|
3406
|
+
app.onError(createApiErrorHandler(docsUrl));
|
|
3407
|
+
app.use(
|
|
3408
|
+
"*",
|
|
3409
|
+
cors({
|
|
3410
|
+
passThrough: (c) => app.matchedRoute("OPTIONS", new URL(c.req.url).pathname)?.method === "OPTIONS"
|
|
3411
|
+
})
|
|
3412
|
+
);
|
|
3413
|
+
app.use("*", createErrorHandler(docsUrl));
|
|
3414
|
+
app.use("*", authMiddleware(tokenMap, options.appKeyResolver, options.fallbackUser));
|
|
3415
|
+
const rateLimitCounters = /* @__PURE__ */ new Map();
|
|
3416
|
+
let lastPruneAt = Math.floor(Date.now() / 1e3);
|
|
3417
|
+
app.use("*", async (c, next) => {
|
|
3418
|
+
const token = c.get("authToken") ?? "__anonymous__";
|
|
3419
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
3420
|
+
if (now - lastPruneAt > 3600) {
|
|
3421
|
+
for (const [key, val] of rateLimitCounters) {
|
|
3422
|
+
if (val.resetAt <= now) {
|
|
3423
|
+
rateLimitCounters.delete(key);
|
|
3424
|
+
}
|
|
3425
|
+
}
|
|
3426
|
+
lastPruneAt = now;
|
|
3427
|
+
}
|
|
3428
|
+
let counter = rateLimitCounters.get(token);
|
|
3429
|
+
if (!counter || counter.resetAt <= now) {
|
|
3430
|
+
counter = { remaining: 5e3, resetAt: now + 3600 };
|
|
3431
|
+
rateLimitCounters.set(token, counter);
|
|
3432
|
+
}
|
|
3433
|
+
counter.remaining = Math.max(0, counter.remaining - 1);
|
|
3434
|
+
c.header("X-RateLimit-Limit", "5000");
|
|
3435
|
+
c.header("X-RateLimit-Remaining", String(counter.remaining));
|
|
3436
|
+
c.header("X-RateLimit-Reset", String(counter.resetAt));
|
|
3437
|
+
c.header("X-RateLimit-Resource", "core");
|
|
3438
|
+
if (counter.remaining === 0) {
|
|
3439
|
+
return c.json(
|
|
3440
|
+
{
|
|
3441
|
+
message: "API rate limit exceeded",
|
|
3442
|
+
documentation_url: docsUrl
|
|
3443
|
+
},
|
|
3444
|
+
403
|
|
3445
|
+
);
|
|
3446
|
+
}
|
|
3447
|
+
await next();
|
|
3448
|
+
});
|
|
3449
|
+
plugin.register(app, store, webhooks, baseUrl, tokenMap);
|
|
3450
|
+
app.notFound(
|
|
3451
|
+
(c) => c.json(
|
|
3452
|
+
{
|
|
3453
|
+
message: "Not Found",
|
|
3454
|
+
documentation_url: docsUrl
|
|
3455
|
+
},
|
|
3456
|
+
404
|
|
3457
|
+
)
|
|
3458
|
+
);
|
|
3459
|
+
return { app, store, webhooks, port, baseUrl, tokenMap };
|
|
3460
|
+
}
|
|
3461
|
+
var MUTATING_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
3462
|
+
var STOP_TIMEOUT_MS = 4e3;
|
|
3463
|
+
function runProvider(manifest, options = {}) {
|
|
3464
|
+
const channel = options.channel ?? processChannel();
|
|
3465
|
+
const exit = options.exit ?? ((code) => process.exit(code));
|
|
3466
|
+
const version = options.version ?? readVersion(options.packageUrl);
|
|
3467
|
+
let running;
|
|
3468
|
+
let welcomed = false;
|
|
3469
|
+
let stopping = false;
|
|
3470
|
+
let finish;
|
|
3471
|
+
const done = new Promise((resolve2) => {
|
|
3472
|
+
finish = resolve2;
|
|
3473
|
+
});
|
|
3474
|
+
let nextControlId = 1;
|
|
3475
|
+
const controls = /* @__PURE__ */ new Map();
|
|
3476
|
+
let queue = Promise.resolve();
|
|
3477
|
+
const send = (message2) => channel.send(message2);
|
|
3478
|
+
const fail = (id, error) => {
|
|
3479
|
+
const code = error instanceof ProviderError ? error.code : "internal";
|
|
3480
|
+
send({ type: "error", ...id === void 0 ? {} : { id }, code, message: describe(error) });
|
|
3481
|
+
};
|
|
3482
|
+
const end = (code) => {
|
|
3483
|
+
channel.disconnect();
|
|
3484
|
+
finish(code);
|
|
3485
|
+
exit(code);
|
|
3486
|
+
};
|
|
3487
|
+
channel.onMessage((message2) => {
|
|
3488
|
+
if (message2.type === "control-response") {
|
|
3489
|
+
const resolve2 = controls.get(message2.id);
|
|
3490
|
+
controls.delete(message2.id);
|
|
3491
|
+
resolve2?.(message2.response);
|
|
3492
|
+
return;
|
|
3493
|
+
}
|
|
3494
|
+
if (message2.type === "welcome") {
|
|
3495
|
+
welcomed = true;
|
|
3496
|
+
return;
|
|
3497
|
+
}
|
|
3498
|
+
queue = queue.then(async () => {
|
|
3499
|
+
if (stopping) {
|
|
3500
|
+
return;
|
|
3501
|
+
}
|
|
3502
|
+
try {
|
|
3503
|
+
if (!welcomed) {
|
|
3504
|
+
throw new ProviderError("bad_request", `Expected welcome before ${message2.type}`);
|
|
3505
|
+
}
|
|
3506
|
+
await handle(message2);
|
|
3507
|
+
} catch (error) {
|
|
3508
|
+
fail("id" in message2 ? message2.id : void 0, error);
|
|
3509
|
+
}
|
|
3510
|
+
});
|
|
3511
|
+
});
|
|
3512
|
+
channel.onDisconnect(() => {
|
|
3513
|
+
if (stopping) {
|
|
3514
|
+
return;
|
|
3515
|
+
}
|
|
3516
|
+
stopping = true;
|
|
3517
|
+
for (const resolve2 of controls.values()) {
|
|
3518
|
+
resolve2(hostGone());
|
|
3519
|
+
}
|
|
3520
|
+
controls.clear();
|
|
3521
|
+
const timer = setTimeout(() => end(1), STOP_TIMEOUT_MS);
|
|
3522
|
+
timer.unref?.();
|
|
3523
|
+
shutdown().catch(() => {
|
|
3524
|
+
}).finally(() => {
|
|
3525
|
+
clearTimeout(timer);
|
|
3526
|
+
end(0);
|
|
3527
|
+
});
|
|
3528
|
+
});
|
|
3529
|
+
send({
|
|
3530
|
+
type: "hello",
|
|
3531
|
+
protocol: PROTOCOL_VERSION,
|
|
3532
|
+
name: manifest.name,
|
|
3533
|
+
version,
|
|
3534
|
+
sdk: SDK_VERSION,
|
|
3535
|
+
stateVersion: manifest.stateVersion,
|
|
3536
|
+
faults: FAULT_KINDS
|
|
3537
|
+
});
|
|
3538
|
+
async function handle(message2) {
|
|
3539
|
+
switch (message2.type) {
|
|
3540
|
+
case "start": {
|
|
3541
|
+
if (running) {
|
|
3542
|
+
throw new ProviderError("bad_request", "Already started");
|
|
3543
|
+
}
|
|
3544
|
+
running = await start(message2);
|
|
3545
|
+
const { port } = running;
|
|
3546
|
+
send({ type: "ready", id: message2.id, secrets, url: running.baseUrl, ...port === void 0 ? {} : { port } });
|
|
3547
|
+
return;
|
|
3548
|
+
}
|
|
3549
|
+
case "listen": {
|
|
3550
|
+
const service = started();
|
|
3551
|
+
const port = service.listener ? service.port : await listen(service, startMessage);
|
|
3552
|
+
send({ type: "listening", id: message2.id, port, url: service.baseUrl });
|
|
3553
|
+
return;
|
|
3554
|
+
}
|
|
3555
|
+
case "snapshot":
|
|
3556
|
+
send({ type: "snapshot", id: message2.id, state: capture(started()) });
|
|
3557
|
+
return;
|
|
3558
|
+
case "restore":
|
|
3559
|
+
restore(started(), message2.state);
|
|
3560
|
+
send({ type: "ok", id: message2.id });
|
|
3561
|
+
return;
|
|
3562
|
+
case "reset": {
|
|
3563
|
+
const { server, seed } = started();
|
|
3564
|
+
for (const [token, user] of server.tokenMap) {
|
|
3565
|
+
if (user.installation) {
|
|
3566
|
+
server.tokenMap.delete(token);
|
|
3567
|
+
}
|
|
3568
|
+
}
|
|
3569
|
+
server.store.reset();
|
|
3570
|
+
server.webhooks.clear();
|
|
3571
|
+
seed();
|
|
3572
|
+
send({ type: "ok", id: message2.id });
|
|
3573
|
+
return;
|
|
3574
|
+
}
|
|
3575
|
+
case "faults":
|
|
3576
|
+
try {
|
|
3577
|
+
started().faults.set(message2.faults);
|
|
3578
|
+
} catch (error) {
|
|
3579
|
+
throw new ProviderError("faults_invalid", describe(error), { cause: error });
|
|
3580
|
+
}
|
|
3581
|
+
send({ type: "ok", id: message2.id });
|
|
3582
|
+
return;
|
|
3583
|
+
case "stop": {
|
|
3584
|
+
stopping = true;
|
|
3585
|
+
const state = await shutdown(message2.snapshot);
|
|
3586
|
+
send({ type: "stopped", id: message2.id, ...state ? { state } : {} });
|
|
3587
|
+
setImmediate(() => end(0));
|
|
3588
|
+
return;
|
|
3589
|
+
}
|
|
3590
|
+
default:
|
|
3591
|
+
throw new ProviderError("bad_request", `Unknown message ${message2.type}`);
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
3594
|
+
let secrets = [];
|
|
3595
|
+
let startMessage;
|
|
3596
|
+
let loaded;
|
|
3597
|
+
function started() {
|
|
3598
|
+
if (!running) {
|
|
3599
|
+
throw new ProviderError("bad_request", "Not started");
|
|
3600
|
+
}
|
|
3601
|
+
return running;
|
|
3602
|
+
}
|
|
3603
|
+
async function start(message2) {
|
|
3604
|
+
startMessage = message2;
|
|
3605
|
+
loaded = await manifest.load();
|
|
3606
|
+
const service = loaded;
|
|
3607
|
+
let config = applyIdentities(message2.seed, message2.identities ?? [], service.mapIdentity);
|
|
3608
|
+
secrets = (message2.secrets ?? []).map((secret) => ({ ...secret }));
|
|
3609
|
+
if (message2.generateSecrets && config && service.prepareSeed) {
|
|
3610
|
+
try {
|
|
3611
|
+
const prepared = await service.prepareSeed(config, secrets);
|
|
3612
|
+
({ config } = prepared);
|
|
3613
|
+
secrets = prepared.generatedSecrets;
|
|
3614
|
+
} catch (error) {
|
|
3615
|
+
throw new ProviderError("seed_invalid", describe(error), { cause: error });
|
|
3616
|
+
}
|
|
3617
|
+
}
|
|
3618
|
+
let listener;
|
|
3619
|
+
let { port } = message2;
|
|
3620
|
+
if (message2.listen) {
|
|
3621
|
+
listener = await bind(
|
|
3622
|
+
message2,
|
|
3623
|
+
(request) => handler ? handler(request) : starting(),
|
|
3624
|
+
(...args) => upgrade ? upgrade(...args) : false
|
|
3625
|
+
);
|
|
3626
|
+
port = boundPort(listener, message2.port);
|
|
3627
|
+
}
|
|
3628
|
+
const baseUrl = resolveBaseUrl({
|
|
3629
|
+
service: message2.service,
|
|
3630
|
+
port,
|
|
3631
|
+
baseUrl: message2.baseUrl,
|
|
3632
|
+
seedBaseUrl: message2.serviceBaseUrl
|
|
3633
|
+
});
|
|
3634
|
+
let resolver;
|
|
3635
|
+
const appKeyResolver = service.createAppKeyResolver ? (appId) => resolver(appId) : void 0;
|
|
3636
|
+
const server = createServer(service.plugin, {
|
|
3637
|
+
port,
|
|
3638
|
+
baseUrl,
|
|
3639
|
+
tokens: message2.tokens,
|
|
3640
|
+
appKeyResolver,
|
|
3641
|
+
fallbackUser: manifest.defaultFallback(config)
|
|
3642
|
+
});
|
|
3643
|
+
resolver = service.createAppKeyResolver?.(server.store);
|
|
3644
|
+
const seed = () => {
|
|
3645
|
+
service.plugin.seed?.(server.store, baseUrl);
|
|
3646
|
+
if (config && service.seedFromConfig) {
|
|
3647
|
+
service.seedFromConfig(server.store, baseUrl, config, server.webhooks);
|
|
3648
|
+
}
|
|
3649
|
+
};
|
|
3650
|
+
let faults;
|
|
3651
|
+
try {
|
|
3652
|
+
faults = new FaultInjector(message2.service, message2.faults);
|
|
3653
|
+
} catch (error) {
|
|
3654
|
+
throw new ProviderError("faults_invalid", describe(error), { cause: error });
|
|
3655
|
+
}
|
|
3656
|
+
const result = { config, baseUrl, server, faults, seed, listener, ...listener ? { port } : {} };
|
|
3657
|
+
if (message2.state) {
|
|
3658
|
+
restore(result, message2.state);
|
|
3659
|
+
} else {
|
|
3660
|
+
try {
|
|
3661
|
+
seed();
|
|
3662
|
+
} catch (error) {
|
|
3663
|
+
throw new ProviderError("seed_invalid", describe(error), { cause: error });
|
|
3664
|
+
}
|
|
3665
|
+
}
|
|
3666
|
+
handler = fetchFor(result);
|
|
3667
|
+
upgrade = server.app.handleUpgrade;
|
|
3668
|
+
return result;
|
|
3669
|
+
}
|
|
3670
|
+
let handler;
|
|
3671
|
+
let upgrade;
|
|
3672
|
+
function fetchFor(service) {
|
|
3673
|
+
const { store, app } = service.server;
|
|
3674
|
+
const inner = service.faults.wrap(async (request) => {
|
|
3675
|
+
const { revision } = store;
|
|
3676
|
+
const response = await app.fetch(request);
|
|
3677
|
+
if (MUTATING_METHODS.has(request.method) || store.revision !== revision) {
|
|
3678
|
+
send({ type: "dirty" });
|
|
3679
|
+
}
|
|
3680
|
+
return response;
|
|
3681
|
+
});
|
|
3682
|
+
return async (request) => isControlPath(new URL(request.url).pathname) ? forward(request) : inner(request);
|
|
3683
|
+
}
|
|
3684
|
+
async function forward(request) {
|
|
3685
|
+
if (stopping) {
|
|
3686
|
+
return fromWireResponse(hostGone());
|
|
3687
|
+
}
|
|
3688
|
+
const id = nextControlId;
|
|
3689
|
+
nextControlId += 1;
|
|
3690
|
+
const wire = await toWireRequest(request);
|
|
3691
|
+
const response = await new Promise((resolve2) => {
|
|
3692
|
+
controls.set(id, resolve2);
|
|
3693
|
+
send({ type: "control", id, request: wire });
|
|
3694
|
+
});
|
|
3695
|
+
return fromWireResponse(response);
|
|
3696
|
+
}
|
|
3697
|
+
async function listen(service, message2) {
|
|
3698
|
+
service.listener = await bind(
|
|
3699
|
+
message2,
|
|
3700
|
+
(request) => handler ? handler(request) : starting(),
|
|
3701
|
+
(...args) => upgrade ? upgrade(...args) : false
|
|
3702
|
+
);
|
|
3703
|
+
service.port = boundPort(service.listener, message2.port);
|
|
3704
|
+
return service.port;
|
|
3705
|
+
}
|
|
3706
|
+
function capture(service) {
|
|
3707
|
+
const { store, tokenMap, webhooks } = service.server;
|
|
3708
|
+
return captureServiceState({ store, tokenMap, webhooks }, manifest.stateVersion);
|
|
3709
|
+
}
|
|
3710
|
+
function restore(service, state) {
|
|
3711
|
+
if (state.stateVersion !== manifest.stateVersion) {
|
|
3712
|
+
throw new ProviderError(
|
|
3713
|
+
"state_incompatible",
|
|
3714
|
+
`Saved state for ${startMessage?.service ?? manifest.name} has stateVersion ${state.stateVersion}, but ${manifest.name} ${version} expects ${manifest.stateVersion}.`
|
|
3715
|
+
);
|
|
3716
|
+
}
|
|
3717
|
+
const { store, tokenMap, webhooks } = service.server;
|
|
3718
|
+
try {
|
|
3719
|
+
restoreServiceState({ store, tokenMap, webhooks }, state);
|
|
3720
|
+
} catch (error) {
|
|
3721
|
+
throw new ProviderError("state_invalid", describe(error), { cause: error });
|
|
3722
|
+
}
|
|
3723
|
+
}
|
|
3724
|
+
async function shutdown(snapshot = false) {
|
|
3725
|
+
const service = running;
|
|
3726
|
+
if (!service) {
|
|
3727
|
+
return void 0;
|
|
3728
|
+
}
|
|
3729
|
+
const closing = service.listener ? closeListener(service.listener) : Promise.resolve();
|
|
3730
|
+
const state = snapshot ? capture(service) : void 0;
|
|
3731
|
+
await Promise.all([closing, service.server.app.dispose()]);
|
|
3732
|
+
service.server.webhooks.clear();
|
|
3733
|
+
return state;
|
|
3734
|
+
}
|
|
3735
|
+
return { done };
|
|
3736
|
+
}
|
|
3737
|
+
function bind(message2, fetch2, upgrade) {
|
|
3738
|
+
const server = serve({ port: message2.port, hostname: message2.hostname, fetch: fetch2, upgrade });
|
|
3739
|
+
return new Promise((resolve2, reject) => {
|
|
3740
|
+
const ready = () => {
|
|
3741
|
+
server.off("error", failed);
|
|
3742
|
+
resolve2(server);
|
|
3743
|
+
};
|
|
3744
|
+
const failed = (error) => {
|
|
3745
|
+
server.off("listening", ready);
|
|
3746
|
+
reject(
|
|
3747
|
+
new ProviderError(
|
|
3748
|
+
error.code === "EADDRINUSE" || error.code === "EACCES" ? "port_in_use" : "internal",
|
|
3749
|
+
error.message
|
|
3750
|
+
)
|
|
3751
|
+
);
|
|
3752
|
+
};
|
|
3753
|
+
server.once("listening", ready);
|
|
3754
|
+
server.once("error", failed);
|
|
3755
|
+
});
|
|
3756
|
+
}
|
|
3757
|
+
function boundPort(server, fallback) {
|
|
3758
|
+
const address = server.address();
|
|
3759
|
+
return address && typeof address === "object" ? address.port : fallback;
|
|
3760
|
+
}
|
|
3761
|
+
function closeListener(server) {
|
|
3762
|
+
return new Promise((resolve2) => {
|
|
3763
|
+
server.close(() => resolve2());
|
|
3764
|
+
server.closeAllConnections();
|
|
3765
|
+
});
|
|
3766
|
+
}
|
|
3767
|
+
var starting = () => Response.json({ error: "Starting" }, { status: 503 });
|
|
3768
|
+
var hostGone = () => ({
|
|
3769
|
+
status: 503,
|
|
3770
|
+
headers: [["content-type", "application/json"]],
|
|
3771
|
+
body: Buffer.from(JSON.stringify({ error: "The Baseline host is not available" })).toString("base64")
|
|
3772
|
+
});
|
|
3773
|
+
function describe(error) {
|
|
3774
|
+
return error instanceof Error ? error.message : String(error);
|
|
3775
|
+
}
|
|
3776
|
+
function readVersion(url) {
|
|
3777
|
+
if (!url) {
|
|
3778
|
+
return "0.0.0";
|
|
3779
|
+
}
|
|
3780
|
+
try {
|
|
3781
|
+
const pkg = JSON.parse(readFileSync3(url, "utf8"));
|
|
3782
|
+
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
3783
|
+
} catch {
|
|
3784
|
+
return "0.0.0";
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
var SQL_STATE_TABLE = "baseline_state";
|
|
3788
|
+
var SQL_STATE_SCHEMA = `CREATE TABLE IF NOT EXISTS ${SQL_STATE_TABLE} (
|
|
3789
|
+
name TEXT PRIMARY KEY,
|
|
3790
|
+
format INTEGER NOT NULL,
|
|
3791
|
+
data TEXT NOT NULL,
|
|
3792
|
+
created_at INTEGER NOT NULL,
|
|
3793
|
+
updated_at INTEGER NOT NULL
|
|
3794
|
+
)`;
|
|
3795
|
+
var UPSERT = `INSERT INTO ${SQL_STATE_TABLE} (name, format, data, created_at, updated_at) VALUES (?, ?, ?, ?, ?)
|
|
3796
|
+
ON CONFLICT(name) DO UPDATE SET format = excluded.format, data = excluded.data, updated_at = excluded.updated_at`;
|
|
3797
|
+
|
|
3798
|
+
export {
|
|
3799
|
+
jwtVerify,
|
|
3800
|
+
importX509,
|
|
3801
|
+
decodeProtectedHeader,
|
|
3802
|
+
decodeJwt,
|
|
3803
|
+
composeService,
|
|
3804
|
+
matchesRedirectUri,
|
|
3805
|
+
constantTimeSecretEqual,
|
|
3806
|
+
bodyStr,
|
|
3807
|
+
debug,
|
|
3808
|
+
escapeHtml,
|
|
3809
|
+
escapeAttr,
|
|
3810
|
+
renderCardPage,
|
|
3811
|
+
renderErrorPage,
|
|
3812
|
+
renderJsonDetails,
|
|
3813
|
+
renderInspectorPage,
|
|
3814
|
+
renderFormPostPage,
|
|
3815
|
+
renderUserButton,
|
|
3816
|
+
renderEmailPreview,
|
|
3817
|
+
recordCoverageHit,
|
|
3818
|
+
recordResponseBody,
|
|
3819
|
+
splitName,
|
|
3820
|
+
lowerCase,
|
|
3821
|
+
createSigningKeys,
|
|
3822
|
+
seedSigningKey,
|
|
3823
|
+
jwksDocument,
|
|
3824
|
+
signJwt,
|
|
3825
|
+
issueIdToken,
|
|
3826
|
+
pendingCodes,
|
|
3827
|
+
basicClientCredentials,
|
|
3828
|
+
verifyPkce,
|
|
3829
|
+
runProvider
|
|
3830
|
+
};
|
|
3831
|
+
/*!
|
|
3832
|
+
* This HTTP compatibility layer builds on Hono's API and design.
|
|
3833
|
+
* https://github.com/honojs/hono
|
|
3834
|
+
* Copyright (c) 2021 - present, Yusuke Wada and Hono contributors
|
|
3835
|
+
* MIT license: see THIRD_PARTY_NOTICES.md in the repository and npm packages.
|
|
3836
|
+
*/
|
|
3837
|
+
//# sourceMappingURL=chunk-G7326MH2.js.map
|