@void/platform 0.20.0 → 0.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/runtime/api/README.md +1 -1
- package/dist/runtime/api/index.js +3465 -968
- package/dist/runtime/api/index.js.map +5 -5
- package/dist/runtime/dispatch/README.md +1 -1
- package/dist/runtime/dispatch/index.js +18 -18
- package/dist/runtime/dispatch/index.js.map +1 -1
- package/dist/runtime/drizzle/0039_email_domains.sql +32 -0
- package/dist/runtime/drizzle/0040_productive_vector.sql +1 -0
- package/dist/runtime/drizzle/meta/_journal.json +14 -0
- package/dist/runtime/manifest.json +24 -12
- package/dist/runtime/proxy/README.md +1 -1
- package/dist/runtime/proxy/index.js +992 -116
- package/dist/runtime/proxy/index.js.map +5 -5
- package/dist/runtime/tail/README.md +1 -1
- package/dist/runtime/tail/index.js +1 -1
- package/dist/runtime/tail/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -28,22 +28,22 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
28
28
|
mod
|
|
29
29
|
));
|
|
30
30
|
|
|
31
|
-
//
|
|
31
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/unenv@2.0.0-rc.24/node_modules/unenv/dist/runtime/_internal/utils.mjs
|
|
32
32
|
// @__NO_SIDE_EFFECTS__
|
|
33
33
|
function createNotImplementedError(name) {
|
|
34
34
|
return new Error(`[unenv] ${name} is not implemented yet!`);
|
|
35
35
|
}
|
|
36
36
|
var init_utils = __esm({
|
|
37
|
-
"
|
|
37
|
+
"../../home/runner/work/void/void/node_modules/.pnpm/unenv@2.0.0-rc.24/node_modules/unenv/dist/runtime/_internal/utils.mjs"() {
|
|
38
38
|
init_performance2();
|
|
39
39
|
__name(createNotImplementedError, "createNotImplementedError");
|
|
40
40
|
}
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
-
//
|
|
43
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/unenv@2.0.0-rc.24/node_modules/unenv/dist/runtime/node/internal/perf_hooks/performance.mjs
|
|
44
44
|
var _timeOrigin, _performanceNow, nodeTiming, PerformanceEntry, PerformanceMark, PerformanceMeasure, PerformanceResourceTiming, PerformanceObserverEntryList, Performance, PerformanceObserver, performance;
|
|
45
45
|
var init_performance = __esm({
|
|
46
|
-
"
|
|
46
|
+
"../../home/runner/work/void/void/node_modules/.pnpm/unenv@2.0.0-rc.24/node_modules/unenv/dist/runtime/node/internal/perf_hooks/performance.mjs"() {
|
|
47
47
|
init_performance2();
|
|
48
48
|
init_utils();
|
|
49
49
|
_timeOrigin = globalThis.performance?.timeOrigin ?? Date.now();
|
|
@@ -285,17 +285,17 @@ var init_performance = __esm({
|
|
|
285
285
|
}
|
|
286
286
|
});
|
|
287
287
|
|
|
288
|
-
//
|
|
288
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/unenv@2.0.0-rc.24/node_modules/unenv/dist/runtime/node/perf_hooks.mjs
|
|
289
289
|
var init_perf_hooks = __esm({
|
|
290
|
-
"
|
|
290
|
+
"../../home/runner/work/void/void/node_modules/.pnpm/unenv@2.0.0-rc.24/node_modules/unenv/dist/runtime/node/perf_hooks.mjs"() {
|
|
291
291
|
init_performance2();
|
|
292
292
|
init_performance();
|
|
293
293
|
}
|
|
294
294
|
});
|
|
295
295
|
|
|
296
|
-
//
|
|
296
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/@cloudflare+unenv-preset@2.16.1_unenv@2.0.0-rc.24_workerd@1.20260910.1/node_modules/@cloudflare/unenv-preset/dist/runtime/polyfill/performance.mjs
|
|
297
297
|
var init_performance2 = __esm({
|
|
298
|
-
"
|
|
298
|
+
"../../home/runner/work/void/void/node_modules/.pnpm/@cloudflare+unenv-preset@2.16.1_unenv@2.0.0-rc.24_workerd@1.20260910.1/node_modules/@cloudflare/unenv-preset/dist/runtime/polyfill/performance.mjs"() {
|
|
299
299
|
init_perf_hooks();
|
|
300
300
|
if (!("__unenv__" in performance)) {
|
|
301
301
|
const proto = Performance.prototype;
|
|
@@ -319,9 +319,9 @@ var init_performance2 = __esm({
|
|
|
319
319
|
}
|
|
320
320
|
});
|
|
321
321
|
|
|
322
|
-
//
|
|
322
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/email-validator@2.0.4/node_modules/email-validator/index.js
|
|
323
323
|
var require_email_validator = __commonJS({
|
|
324
|
-
"
|
|
324
|
+
"../../home/runner/work/void/void/node_modules/.pnpm/email-validator@2.0.4/node_modules/email-validator/index.js"(exports) {
|
|
325
325
|
"use strict";
|
|
326
326
|
init_performance2();
|
|
327
327
|
var tester = /^[-!#$%&'*+\/0-9=?A-Z^_a-z{|}~](\.?[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~])*@[a-zA-Z0-9](-*\.?[a-zA-Z0-9])*\.[a-zA-Z](-?[a-zA-Z0-9])+$/;
|
|
@@ -346,19 +346,19 @@ var require_email_validator = __commonJS({
|
|
|
346
346
|
}
|
|
347
347
|
});
|
|
348
348
|
|
|
349
|
-
//
|
|
349
|
+
// ../../home/runner/work/void/void/platform/packages/proxy/src/index.ts
|
|
350
350
|
init_performance2();
|
|
351
351
|
|
|
352
|
-
//
|
|
352
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/index.js
|
|
353
353
|
init_performance2();
|
|
354
354
|
|
|
355
|
-
//
|
|
355
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/hono.js
|
|
356
356
|
init_performance2();
|
|
357
357
|
|
|
358
|
-
//
|
|
358
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/hono-base.js
|
|
359
359
|
init_performance2();
|
|
360
360
|
|
|
361
|
-
//
|
|
361
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/compose.js
|
|
362
362
|
init_performance2();
|
|
363
363
|
var compose = /* @__PURE__ */ __name((middleware, onError, onNotFound) => {
|
|
364
364
|
return (context, next) => {
|
|
@@ -404,29 +404,29 @@ var compose = /* @__PURE__ */ __name((middleware, onError, onNotFound) => {
|
|
|
404
404
|
};
|
|
405
405
|
}, "compose");
|
|
406
406
|
|
|
407
|
-
//
|
|
407
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/context.js
|
|
408
408
|
init_performance2();
|
|
409
409
|
|
|
410
|
-
//
|
|
410
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/request.js
|
|
411
411
|
init_performance2();
|
|
412
412
|
|
|
413
|
-
//
|
|
413
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/http-exception.js
|
|
414
414
|
init_performance2();
|
|
415
415
|
|
|
416
|
-
//
|
|
416
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/request/constants.js
|
|
417
417
|
init_performance2();
|
|
418
418
|
var GET_MATCH_RESULT = /* @__PURE__ */ Symbol();
|
|
419
419
|
|
|
420
|
-
//
|
|
420
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/utils/body.js
|
|
421
421
|
init_performance2();
|
|
422
422
|
|
|
423
|
-
//
|
|
423
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/utils/buffer.js
|
|
424
424
|
init_performance2();
|
|
425
425
|
|
|
426
|
-
//
|
|
426
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/utils/crypto.js
|
|
427
427
|
init_performance2();
|
|
428
428
|
|
|
429
|
-
//
|
|
429
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/utils/buffer.js
|
|
430
430
|
var bufferToFormData = /* @__PURE__ */ __name((arrayBuffer, contentType) => {
|
|
431
431
|
const response = new Response(arrayBuffer, {
|
|
432
432
|
headers: {
|
|
@@ -437,7 +437,7 @@ var bufferToFormData = /* @__PURE__ */ __name((arrayBuffer, contentType) => {
|
|
|
437
437
|
return response.formData();
|
|
438
438
|
}, "bufferToFormData");
|
|
439
439
|
|
|
440
|
-
//
|
|
440
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/utils/body.js
|
|
441
441
|
var MAX_NESTING_DEPTH = 32;
|
|
442
442
|
var MAX_NESTED_OBJECTS = 1e4;
|
|
443
443
|
var isRawRequest = /* @__PURE__ */ __name((request) => "headers" in request, "isRawRequest");
|
|
@@ -537,7 +537,7 @@ var throwNestingLimitExceeded = /* @__PURE__ */ __name(() => {
|
|
|
537
537
|
throw new Error("Nesting limit exceeded");
|
|
538
538
|
}, "throwNestingLimitExceeded");
|
|
539
539
|
|
|
540
|
-
//
|
|
540
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/utils/url.js
|
|
541
541
|
init_performance2();
|
|
542
542
|
var splitPath = /* @__PURE__ */ __name((path) => {
|
|
543
543
|
const paths = path.split("/");
|
|
@@ -744,7 +744,7 @@ var getQueryParams = /* @__PURE__ */ __name((url, key) => {
|
|
|
744
744
|
}, "getQueryParams");
|
|
745
745
|
var decodeURIComponent_ = decodeURIComponent;
|
|
746
746
|
|
|
747
|
-
//
|
|
747
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/request.js
|
|
748
748
|
var HonoRequest = class {
|
|
749
749
|
static {
|
|
750
750
|
__name(this, "HonoRequest");
|
|
@@ -1028,7 +1028,7 @@ var HonoRequest = class {
|
|
|
1028
1028
|
}
|
|
1029
1029
|
};
|
|
1030
1030
|
|
|
1031
|
-
//
|
|
1031
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/utils/html.js
|
|
1032
1032
|
init_performance2();
|
|
1033
1033
|
var HtmlEscapedCallbackPhase = {
|
|
1034
1034
|
Stringify: 1,
|
|
@@ -1071,7 +1071,7 @@ var resolveCallback = /* @__PURE__ */ __name(async (str, phase, preserveCallback
|
|
|
1071
1071
|
}
|
|
1072
1072
|
}, "resolveCallback");
|
|
1073
1073
|
|
|
1074
|
-
//
|
|
1074
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/context.js
|
|
1075
1075
|
var TEXT_PLAIN = "text/plain; charset=UTF-8";
|
|
1076
1076
|
var setDefaultContentType = /* @__PURE__ */ __name((contentType, headers) => {
|
|
1077
1077
|
return {
|
|
@@ -1500,7 +1500,7 @@ var Context = class {
|
|
|
1500
1500
|
}, "notFound");
|
|
1501
1501
|
};
|
|
1502
1502
|
|
|
1503
|
-
//
|
|
1503
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/router.js
|
|
1504
1504
|
init_performance2();
|
|
1505
1505
|
var METHOD_NAME_ALL = "ALL";
|
|
1506
1506
|
var METHOD_NAME_ALL_LOWERCASE = "all";
|
|
@@ -1512,11 +1512,11 @@ var UnsupportedPathError = class extends Error {
|
|
|
1512
1512
|
}
|
|
1513
1513
|
};
|
|
1514
1514
|
|
|
1515
|
-
//
|
|
1515
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/utils/constants.js
|
|
1516
1516
|
init_performance2();
|
|
1517
1517
|
var COMPOSED_HANDLER = "__COMPOSED_HANDLER";
|
|
1518
1518
|
|
|
1519
|
-
//
|
|
1519
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/hono-base.js
|
|
1520
1520
|
var notFoundHandler = /* @__PURE__ */ __name((c) => {
|
|
1521
1521
|
return c.text("404 Not Found", 404);
|
|
1522
1522
|
}, "notFoundHandler");
|
|
@@ -1627,14 +1627,14 @@ var Hono = class _Hono {
|
|
|
1627
1627
|
* app.route("/api", app2) // GET /api/user
|
|
1628
1628
|
* ```
|
|
1629
1629
|
*/
|
|
1630
|
-
route(path,
|
|
1630
|
+
route(path, app5) {
|
|
1631
1631
|
const subApp = this.basePath(path);
|
|
1632
|
-
|
|
1632
|
+
app5.routes.map((r) => {
|
|
1633
1633
|
let handler;
|
|
1634
|
-
if (
|
|
1634
|
+
if (app5.errorHandler === errorHandler) {
|
|
1635
1635
|
handler = r.handler;
|
|
1636
1636
|
} else {
|
|
1637
|
-
handler = /* @__PURE__ */ __name(async (c, next) => (await compose([],
|
|
1637
|
+
handler = /* @__PURE__ */ __name(async (c, next) => (await compose([], app5.errorHandler)(c, () => r.handler(c, next))).res, "handler");
|
|
1638
1638
|
handler[COMPOSED_HANDLER] = r.handler;
|
|
1639
1639
|
}
|
|
1640
1640
|
subApp.#addRoute(r.method, r.path, handler, r.basePath);
|
|
@@ -1897,17 +1897,17 @@ var Hono = class _Hono {
|
|
|
1897
1897
|
}, "fire");
|
|
1898
1898
|
};
|
|
1899
1899
|
|
|
1900
|
-
//
|
|
1900
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/router/reg-exp-router/index.js
|
|
1901
1901
|
init_performance2();
|
|
1902
1902
|
|
|
1903
|
-
//
|
|
1903
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/router/reg-exp-router/router.js
|
|
1904
1904
|
init_performance2();
|
|
1905
1905
|
|
|
1906
|
-
//
|
|
1906
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/router/utils.js
|
|
1907
1907
|
init_performance2();
|
|
1908
1908
|
var createNullObject = /* @__PURE__ */ __name(() => /* @__PURE__ */ Object.create(null), "createNullObject");
|
|
1909
1909
|
|
|
1910
|
-
//
|
|
1910
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/router/reg-exp-router/matcher.js
|
|
1911
1911
|
init_performance2();
|
|
1912
1912
|
var emptyParam = [];
|
|
1913
1913
|
function match(method, path) {
|
|
@@ -1930,7 +1930,7 @@ function match(method, path) {
|
|
|
1930
1930
|
}
|
|
1931
1931
|
__name(match, "match");
|
|
1932
1932
|
|
|
1933
|
-
//
|
|
1933
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/router/reg-exp-router/node.js
|
|
1934
1934
|
init_performance2();
|
|
1935
1935
|
var LABEL_REG_EXP_STR = "[^/]+";
|
|
1936
1936
|
var ONLY_WILDCARD_REG_EXP_STR = ".*";
|
|
@@ -2042,7 +2042,7 @@ var Node = class _Node {
|
|
|
2042
2042
|
}
|
|
2043
2043
|
};
|
|
2044
2044
|
|
|
2045
|
-
//
|
|
2045
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/router/reg-exp-router/trie.js
|
|
2046
2046
|
init_performance2();
|
|
2047
2047
|
var Trie = class {
|
|
2048
2048
|
static {
|
|
@@ -2110,7 +2110,7 @@ var Trie = class {
|
|
|
2110
2110
|
}
|
|
2111
2111
|
};
|
|
2112
2112
|
|
|
2113
|
-
//
|
|
2113
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/router/reg-exp-router/router.js
|
|
2114
2114
|
var wildcardRegExpCache = createNullObject();
|
|
2115
2115
|
function buildWildcardRegExp(path) {
|
|
2116
2116
|
return wildcardRegExpCache[path] ??= new RegExp(
|
|
@@ -2236,13 +2236,13 @@ var RegExpRouter = class {
|
|
|
2236
2236
|
}
|
|
2237
2237
|
};
|
|
2238
2238
|
|
|
2239
|
-
//
|
|
2239
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/router/reg-exp-router/prepared-router.js
|
|
2240
2240
|
init_performance2();
|
|
2241
2241
|
|
|
2242
|
-
//
|
|
2242
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/router/smart-router/index.js
|
|
2243
2243
|
init_performance2();
|
|
2244
2244
|
|
|
2245
|
-
//
|
|
2245
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/router/smart-router/router.js
|
|
2246
2246
|
init_performance2();
|
|
2247
2247
|
var SmartRouter = class {
|
|
2248
2248
|
static {
|
|
@@ -2301,13 +2301,13 @@ var SmartRouter = class {
|
|
|
2301
2301
|
}
|
|
2302
2302
|
};
|
|
2303
2303
|
|
|
2304
|
-
//
|
|
2304
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/router/trie-router/index.js
|
|
2305
2305
|
init_performance2();
|
|
2306
2306
|
|
|
2307
|
-
//
|
|
2307
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/router/trie-router/router.js
|
|
2308
2308
|
init_performance2();
|
|
2309
2309
|
|
|
2310
|
-
//
|
|
2310
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/router/trie-router/node.js
|
|
2311
2311
|
init_performance2();
|
|
2312
2312
|
var emptyParams = createNullObject();
|
|
2313
2313
|
var order = 0;
|
|
@@ -2471,7 +2471,7 @@ var Node2 = class _Node2 {
|
|
|
2471
2471
|
}
|
|
2472
2472
|
};
|
|
2473
2473
|
|
|
2474
|
-
//
|
|
2474
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/router/trie-router/router.js
|
|
2475
2475
|
var TrieRouter = class {
|
|
2476
2476
|
static {
|
|
2477
2477
|
__name(this, "TrieRouter");
|
|
@@ -2488,7 +2488,7 @@ var TrieRouter = class {
|
|
|
2488
2488
|
}
|
|
2489
2489
|
};
|
|
2490
2490
|
|
|
2491
|
-
//
|
|
2491
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/hono.js
|
|
2492
2492
|
var Hono2 = class extends Hono {
|
|
2493
2493
|
static {
|
|
2494
2494
|
__name(this, "Hono");
|
|
@@ -2506,7 +2506,7 @@ var Hono2 = class extends Hono {
|
|
|
2506
2506
|
}
|
|
2507
2507
|
};
|
|
2508
2508
|
|
|
2509
|
-
//
|
|
2509
|
+
// ../../home/runner/work/void/void/node_modules/.pnpm/hono@4.13.7/node_modules/hono/dist/middleware/cors/index.js
|
|
2510
2510
|
init_performance2();
|
|
2511
2511
|
var cors = /* @__PURE__ */ __name((options) => {
|
|
2512
2512
|
const opts = {
|
|
@@ -2593,10 +2593,10 @@ var cors = /* @__PURE__ */ __name((options) => {
|
|
|
2593
2593
|
}, "cors2");
|
|
2594
2594
|
}, "cors");
|
|
2595
2595
|
|
|
2596
|
-
//
|
|
2596
|
+
// ../../home/runner/work/void/void/platform/packages/proxy/src/ai.ts
|
|
2597
2597
|
init_performance2();
|
|
2598
2598
|
|
|
2599
|
-
//
|
|
2599
|
+
// ../../home/runner/work/void/void/platform/packages/proxy/src/body.ts
|
|
2600
2600
|
init_performance2();
|
|
2601
2601
|
function isProxyBody(value) {
|
|
2602
2602
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -2619,7 +2619,7 @@ function isQueryValue(value) {
|
|
|
2619
2619
|
}
|
|
2620
2620
|
__name(isQueryValue, "isQueryValue");
|
|
2621
2621
|
|
|
2622
|
-
//
|
|
2622
|
+
// ../../home/runner/work/void/void/platform/packages/proxy/src/ai.ts
|
|
2623
2623
|
var DEFAULT_GATEWAY_ID = "void";
|
|
2624
2624
|
var PROVIDER_AUTH_DEFAULTS = {
|
|
2625
2625
|
"google-ai-studio": { header: "x-goog-api-key", prefix: "" }
|
|
@@ -2983,7 +2983,7 @@ ai.post("/toMarkdown", async (context) => {
|
|
|
2983
2983
|
}
|
|
2984
2984
|
});
|
|
2985
2985
|
|
|
2986
|
-
//
|
|
2986
|
+
// ../../home/runner/work/void/void/platform/packages/proxy/src/capabilities.ts
|
|
2987
2987
|
init_performance2();
|
|
2988
2988
|
function proxyCapabilityEnabled(value, capability) {
|
|
2989
2989
|
if (capability === "sandbox") {
|
|
@@ -3001,10 +3001,10 @@ function proxyCapabilityEnabled(value, capability) {
|
|
|
3001
3001
|
}
|
|
3002
3002
|
__name(proxyCapabilityEnabled, "proxyCapabilityEnabled");
|
|
3003
3003
|
|
|
3004
|
-
//
|
|
3004
|
+
// ../../home/runner/work/void/void/platform/packages/proxy/src/d1.ts
|
|
3005
3005
|
init_performance2();
|
|
3006
3006
|
|
|
3007
|
-
//
|
|
3007
|
+
// ../../home/runner/work/void/void/platform/packages/proxy/src/dispatch.ts
|
|
3008
3008
|
init_performance2();
|
|
3009
3009
|
async function resolveWorker(api, jwtSecret, projectId) {
|
|
3010
3010
|
const token = await hmacSign(projectId, jwtSecret);
|
|
@@ -3092,7 +3092,7 @@ async function hmacSign(data, secret) {
|
|
|
3092
3092
|
}
|
|
3093
3093
|
__name(hmacSign, "hmacSign");
|
|
3094
3094
|
|
|
3095
|
-
//
|
|
3095
|
+
// ../../home/runner/work/void/void/platform/packages/proxy/src/resolve.ts
|
|
3096
3096
|
init_performance2();
|
|
3097
3097
|
var cache = /* @__PURE__ */ new Map();
|
|
3098
3098
|
async function resolveResource(api, jwtSecret, projectId, type, bindingName) {
|
|
@@ -3179,7 +3179,7 @@ async function hmacSign2(data, secret) {
|
|
|
3179
3179
|
}
|
|
3180
3180
|
__name(hmacSign2, "hmacSign");
|
|
3181
3181
|
|
|
3182
|
-
//
|
|
3182
|
+
// ../../home/runner/work/void/void/platform/packages/proxy/src/d1.ts
|
|
3183
3183
|
var CF_API = "https://api.cloudflare.com/client/v4";
|
|
3184
3184
|
var d1 = new Hono2();
|
|
3185
3185
|
d1.post("/query", async (context) => {
|
|
@@ -3252,10 +3252,13 @@ d1.post("/batch", async (context) => {
|
|
|
3252
3252
|
return context.json({ error: "atomic D1 batch unavailable for active deployment" }, 503);
|
|
3253
3253
|
});
|
|
3254
3254
|
|
|
3255
|
-
//
|
|
3255
|
+
// ../../home/runner/work/void/void/platform/packages/proxy/src/email-relay.ts
|
|
3256
3256
|
init_performance2();
|
|
3257
3257
|
|
|
3258
|
-
//
|
|
3258
|
+
// ../../home/runner/work/void/void/platform/packages/proxy/src/email-router.ts
|
|
3259
|
+
init_performance2();
|
|
3260
|
+
|
|
3261
|
+
// ../../home/runner/work/void/void/platform/packages/api/src/reserved-slugs.ts
|
|
3259
3262
|
init_performance2();
|
|
3260
3263
|
var EMAIL_RESERVED_SLUGS = /* @__PURE__ */ new Set([
|
|
3261
3264
|
"abuse",
|
|
@@ -3269,11 +3272,101 @@ function isReservedEmailSlug(slug) {
|
|
|
3269
3272
|
}
|
|
3270
3273
|
__name(isReservedEmailSlug, "isReservedEmailSlug");
|
|
3271
3274
|
|
|
3272
|
-
//
|
|
3275
|
+
// ../../home/runner/work/void/void/platform/packages/proxy/src/email-router.ts
|
|
3273
3276
|
var import_email_validator = __toESM(require_email_validator(), 1);
|
|
3274
3277
|
import { EmailMessage as EmailMessage2 } from "cloudflare:email";
|
|
3275
3278
|
|
|
3276
|
-
//
|
|
3279
|
+
// ../../home/runner/work/void/void/platform/packages/proxy/src/email-lease.ts
|
|
3280
|
+
init_performance2();
|
|
3281
|
+
var LEASE_CACHE_TTL_MS = 45 * 60 * 1e3;
|
|
3282
|
+
var leaseCache = /* @__PURE__ */ new Map();
|
|
3283
|
+
function dropEmailDomainLease(domain) {
|
|
3284
|
+
leaseCache.delete(domain);
|
|
3285
|
+
}
|
|
3286
|
+
__name(dropEmailDomainLease, "dropEmailDomainLease");
|
|
3287
|
+
function leaseBearer(lease) {
|
|
3288
|
+
return lease.kind === "api_token" ? lease.token : lease.accessToken;
|
|
3289
|
+
}
|
|
3290
|
+
__name(leaseBearer, "leaseBearer");
|
|
3291
|
+
async function leaseEmailDomainCredential(env, domain) {
|
|
3292
|
+
const cached = leaseCache.get(domain);
|
|
3293
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
3294
|
+
return cached.lease;
|
|
3295
|
+
}
|
|
3296
|
+
let res;
|
|
3297
|
+
try {
|
|
3298
|
+
const token = await hmacSign3(domain, env.JWT_SECRET);
|
|
3299
|
+
res = await env.API.fetch("https://api/internal/email/domain-credential", {
|
|
3300
|
+
method: "POST",
|
|
3301
|
+
headers: {
|
|
3302
|
+
"content-type": "application/json",
|
|
3303
|
+
"x-void-internal": token
|
|
3304
|
+
},
|
|
3305
|
+
body: JSON.stringify({ domain })
|
|
3306
|
+
});
|
|
3307
|
+
} catch {
|
|
3308
|
+
return null;
|
|
3309
|
+
}
|
|
3310
|
+
if (!res.ok) {
|
|
3311
|
+
return null;
|
|
3312
|
+
}
|
|
3313
|
+
let data;
|
|
3314
|
+
try {
|
|
3315
|
+
data = await res.json();
|
|
3316
|
+
} catch {
|
|
3317
|
+
return null;
|
|
3318
|
+
}
|
|
3319
|
+
const lease = parseLease(data);
|
|
3320
|
+
if (!lease) {
|
|
3321
|
+
return null;
|
|
3322
|
+
}
|
|
3323
|
+
leaseCache.set(domain, { lease, expiresAt: Date.now() + LEASE_CACHE_TTL_MS });
|
|
3324
|
+
return lease;
|
|
3325
|
+
}
|
|
3326
|
+
__name(leaseEmailDomainCredential, "leaseEmailDomainCredential");
|
|
3327
|
+
function parseLease(data) {
|
|
3328
|
+
if (typeof data !== "object" || data === null) {
|
|
3329
|
+
return null;
|
|
3330
|
+
}
|
|
3331
|
+
const kind = data.kind;
|
|
3332
|
+
if (kind === "api_token") {
|
|
3333
|
+
const token = data.token;
|
|
3334
|
+
return typeof token === "string" && token.length > 0 ? { kind, token } : null;
|
|
3335
|
+
}
|
|
3336
|
+
if (kind === "oauth_grant") {
|
|
3337
|
+
const accessToken = data.accessToken;
|
|
3338
|
+
return typeof accessToken === "string" && accessToken.length > 0 ? { kind, accessToken } : null;
|
|
3339
|
+
}
|
|
3340
|
+
return null;
|
|
3341
|
+
}
|
|
3342
|
+
__name(parseLease, "parseLease");
|
|
3343
|
+
var encoder3 = new TextEncoder();
|
|
3344
|
+
var cachedSignKey3;
|
|
3345
|
+
var cachedKeySecret3;
|
|
3346
|
+
async function getSignKey3(secret) {
|
|
3347
|
+
if (cachedSignKey3 && cachedKeySecret3 === secret) {
|
|
3348
|
+
return cachedSignKey3;
|
|
3349
|
+
}
|
|
3350
|
+
cachedSignKey3 = await crypto.subtle.importKey(
|
|
3351
|
+
"raw",
|
|
3352
|
+
encoder3.encode(secret),
|
|
3353
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
3354
|
+
false,
|
|
3355
|
+
["sign"]
|
|
3356
|
+
);
|
|
3357
|
+
cachedKeySecret3 = secret;
|
|
3358
|
+
return cachedSignKey3;
|
|
3359
|
+
}
|
|
3360
|
+
__name(getSignKey3, "getSignKey");
|
|
3361
|
+
async function hmacSign3(data, secret) {
|
|
3362
|
+
const key = await getSignKey3(secret);
|
|
3363
|
+
const sig = await crypto.subtle.sign("HMAC", key, encoder3.encode(data));
|
|
3364
|
+
const binary = String.fromCharCode(...new Uint8Array(sig));
|
|
3365
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
3366
|
+
}
|
|
3367
|
+
__name(hmacSign3, "hmacSign");
|
|
3368
|
+
|
|
3369
|
+
// ../../home/runner/work/void/void/platform/packages/proxy/src/email.ts
|
|
3277
3370
|
init_performance2();
|
|
3278
3371
|
import { EmailMessage } from "cloudflare:email";
|
|
3279
3372
|
var app = new Hono2();
|
|
@@ -3638,6 +3731,20 @@ app.post("/send", async (context) => {
|
|
|
3638
3731
|
413
|
|
3639
3732
|
);
|
|
3640
3733
|
}
|
|
3734
|
+
const fromAt = from.indexOf("@");
|
|
3735
|
+
const fromDomain = fromAt === -1 ? "" : from.slice(fromAt + 1).toLowerCase();
|
|
3736
|
+
if (fromDomain !== "" && fromDomain !== senderDomain) {
|
|
3737
|
+
return sendViaCustomDomain(
|
|
3738
|
+
context,
|
|
3739
|
+
projectId,
|
|
3740
|
+
from,
|
|
3741
|
+
fromDomain,
|
|
3742
|
+
senderDomain,
|
|
3743
|
+
recipients,
|
|
3744
|
+
raw2,
|
|
3745
|
+
requestId
|
|
3746
|
+
);
|
|
3747
|
+
}
|
|
3641
3748
|
const expectedSlug = await context.env.ROUTING.get(`email-slug:${projectId}`);
|
|
3642
3749
|
if (!expectedSlug) {
|
|
3643
3750
|
return context.json(
|
|
@@ -3858,6 +3965,276 @@ app.post("/send", async (context) => {
|
|
|
3858
3965
|
}))
|
|
3859
3966
|
});
|
|
3860
3967
|
});
|
|
3968
|
+
function readSendRawAddressList(value) {
|
|
3969
|
+
const out = /* @__PURE__ */ new Set();
|
|
3970
|
+
if (Array.isArray(value)) {
|
|
3971
|
+
for (const entry of value) {
|
|
3972
|
+
if (typeof entry === "string") {
|
|
3973
|
+
out.add(entry.toLowerCase());
|
|
3974
|
+
}
|
|
3975
|
+
}
|
|
3976
|
+
}
|
|
3977
|
+
return out;
|
|
3978
|
+
}
|
|
3979
|
+
__name(readSendRawAddressList, "readSendRawAddressList");
|
|
3980
|
+
async function sendViaCustomDomain(context, projectId, from, fromDomain, senderDomain, recipients, raw2, requestId) {
|
|
3981
|
+
if (!/^[\x21-\x7e]+$/.test(from)) {
|
|
3982
|
+
return context.json(
|
|
3983
|
+
{
|
|
3984
|
+
ok: false,
|
|
3985
|
+
error: { code: "INVALID_FROM", message: "from address must be printable ASCII" }
|
|
3986
|
+
},
|
|
3987
|
+
403
|
|
3988
|
+
);
|
|
3989
|
+
}
|
|
3990
|
+
const fromLower = from.toLowerCase();
|
|
3991
|
+
const mirror = await readEmailDomainMirror(context.env.ROUTING, fromDomain, { cacheTtl: 60 });
|
|
3992
|
+
if (!mirror || mirror.projectId !== projectId || mirror.status !== "active") {
|
|
3993
|
+
return context.json(
|
|
3994
|
+
{
|
|
3995
|
+
ok: false,
|
|
3996
|
+
error: {
|
|
3997
|
+
code: "INVALID_FROM",
|
|
3998
|
+
message: `from address must be on the configured sender domain (${senderDomain}) or a domain registered to this project`
|
|
3999
|
+
}
|
|
4000
|
+
},
|
|
4001
|
+
403
|
|
4002
|
+
);
|
|
4003
|
+
}
|
|
4004
|
+
if (!mirror.accountId) {
|
|
4005
|
+
return context.json(
|
|
4006
|
+
{
|
|
4007
|
+
ok: false,
|
|
4008
|
+
error: { code: "UPSTREAM_ERROR", message: "email domain registration is incomplete" }
|
|
4009
|
+
},
|
|
4010
|
+
502
|
|
4011
|
+
);
|
|
4012
|
+
}
|
|
4013
|
+
const mimeFrom = readMimeFromAddrSpec(raw2);
|
|
4014
|
+
if (!mimeFrom.ok || mimeFrom.addrSpec.toLowerCase() !== fromLower) {
|
|
4015
|
+
const detail = mimeFrom.ok ? "the From header address does not match the envelope sender" : MIME_FROM_REFUSALS[mimeFrom.reason];
|
|
4016
|
+
return context.json(
|
|
4017
|
+
{
|
|
4018
|
+
ok: false,
|
|
4019
|
+
error: {
|
|
4020
|
+
code: "INVALID_FROM",
|
|
4021
|
+
message: `mime From must be the envelope sender ${from}: ${detail}`
|
|
4022
|
+
}
|
|
4023
|
+
},
|
|
4024
|
+
403
|
|
4025
|
+
);
|
|
4026
|
+
}
|
|
4027
|
+
const slug = await context.env.ROUTING.get(`email-slug:${projectId}`);
|
|
4028
|
+
if (!slug) {
|
|
4029
|
+
return context.json(
|
|
4030
|
+
{
|
|
4031
|
+
ok: false,
|
|
4032
|
+
error: {
|
|
4033
|
+
code: "UPSTREAM_ERROR",
|
|
4034
|
+
message: "project slug unknown \u2014 refusing to send"
|
|
4035
|
+
}
|
|
4036
|
+
},
|
|
4037
|
+
502
|
|
4038
|
+
);
|
|
4039
|
+
}
|
|
4040
|
+
if (!SLUG_RE.test(slug)) {
|
|
4041
|
+
console.error(
|
|
4042
|
+
`[email-send] project ${projectId} slug malformed in KV: ${sanitizeForLog(slug, 64)}`
|
|
4043
|
+
);
|
|
4044
|
+
return context.json(
|
|
4045
|
+
{
|
|
4046
|
+
ok: false,
|
|
4047
|
+
error: {
|
|
4048
|
+
code: "UPSTREAM_ERROR",
|
|
4049
|
+
message: "project slug malformed in routing"
|
|
4050
|
+
}
|
|
4051
|
+
},
|
|
4052
|
+
502
|
|
4053
|
+
);
|
|
4054
|
+
}
|
|
4055
|
+
const platformSuspension = await readRoutingSuspension(context.env.ROUTING, slug);
|
|
4056
|
+
if (platformSuspension.kind === "corrupt") {
|
|
4057
|
+
console.error(
|
|
4058
|
+
`[email-send] project ${projectId} routing blob malformed in KV for slug ${sanitizeForLog(slug, 64)}`
|
|
4059
|
+
);
|
|
4060
|
+
return context.json(
|
|
4061
|
+
{
|
|
4062
|
+
ok: false,
|
|
4063
|
+
error: {
|
|
4064
|
+
code: "UPSTREAM_ERROR",
|
|
4065
|
+
message: "project routing entry malformed"
|
|
4066
|
+
}
|
|
4067
|
+
},
|
|
4068
|
+
502
|
|
4069
|
+
);
|
|
4070
|
+
}
|
|
4071
|
+
if (platformSuspension.kind === "suspended") {
|
|
4072
|
+
return context.json(
|
|
4073
|
+
{
|
|
4074
|
+
ok: false,
|
|
4075
|
+
error: {
|
|
4076
|
+
code: "QUOTA_EXCEEDED",
|
|
4077
|
+
message: `project is suspended (${sanitizeForLog(platformSuspension.reason, 64)})`
|
|
4078
|
+
}
|
|
4079
|
+
},
|
|
4080
|
+
200
|
|
4081
|
+
);
|
|
4082
|
+
}
|
|
4083
|
+
if (await context.env.ROUTING.get(`email-suspended:${projectId}`)) {
|
|
4084
|
+
return context.json(
|
|
4085
|
+
{ ok: false, error: { code: "QUOTA_EXCEEDED", message: "project email is suspended" } },
|
|
4086
|
+
200
|
|
4087
|
+
);
|
|
4088
|
+
}
|
|
4089
|
+
const now = /* @__PURE__ */ new Date();
|
|
4090
|
+
const burstKey = `email-burst:${projectId}:${minuteBucket(now)}`;
|
|
4091
|
+
const burstCurrent = readCounter(await context.env.ROUTING.get(burstKey));
|
|
4092
|
+
const burstLimit = await getBurstLimit(context.env.ROUTING, projectId);
|
|
4093
|
+
if (burstCurrent + recipients.length > burstLimit) {
|
|
4094
|
+
return context.json(
|
|
4095
|
+
{
|
|
4096
|
+
ok: false,
|
|
4097
|
+
error: {
|
|
4098
|
+
code: "QUOTA_EXCEEDED",
|
|
4099
|
+
message: `outbound burst limit ${burstLimit}/min exceeded`
|
|
4100
|
+
}
|
|
4101
|
+
},
|
|
4102
|
+
429
|
|
4103
|
+
);
|
|
4104
|
+
}
|
|
4105
|
+
const counterKey = `email-sent:${projectId}:${monthBucket(now)}`;
|
|
4106
|
+
const monthCurrent = readCounter(await context.env.ROUTING.get(counterKey));
|
|
4107
|
+
const monthLimit = await getMonthlyLimit(context.env.ROUTING, projectId);
|
|
4108
|
+
if (monthCurrent + recipients.length > monthLimit) {
|
|
4109
|
+
return context.json(
|
|
4110
|
+
{
|
|
4111
|
+
ok: false,
|
|
4112
|
+
error: {
|
|
4113
|
+
code: "QUOTA_EXCEEDED",
|
|
4114
|
+
message: `monthly outbound limit ${monthLimit} exceeded`
|
|
4115
|
+
}
|
|
4116
|
+
},
|
|
4117
|
+
429
|
|
4118
|
+
);
|
|
4119
|
+
}
|
|
4120
|
+
let mimeMessage;
|
|
4121
|
+
try {
|
|
4122
|
+
mimeMessage = new TextDecoder("utf-8", { fatal: true, ignoreBOM: false }).decode(raw2);
|
|
4123
|
+
} catch {
|
|
4124
|
+
return context.json(
|
|
4125
|
+
{ ok: false, error: { code: "MIME_ERROR", message: "mime must be valid UTF-8" } },
|
|
4126
|
+
400
|
|
4127
|
+
);
|
|
4128
|
+
}
|
|
4129
|
+
const lease = await leaseEmailDomainCredential(context.env, fromDomain);
|
|
4130
|
+
if (!lease) {
|
|
4131
|
+
return context.json(
|
|
4132
|
+
{
|
|
4133
|
+
ok: false,
|
|
4134
|
+
error: { code: "UPSTREAM_ERROR", message: "could not lease the domain credential" }
|
|
4135
|
+
},
|
|
4136
|
+
502
|
|
4137
|
+
);
|
|
4138
|
+
}
|
|
4139
|
+
await Promise.all([
|
|
4140
|
+
context.env.ROUTING.put(counterKey, String(monthCurrent + recipients.length), {
|
|
4141
|
+
expirationTtl: monthTtlSeconds(now)
|
|
4142
|
+
}).catch((e) => {
|
|
4143
|
+
console.error(`[email-send] monthly counter put failed for ${projectId}:`, e);
|
|
4144
|
+
}),
|
|
4145
|
+
context.env.ROUTING.put(burstKey, String(burstCurrent + recipients.length), {
|
|
4146
|
+
expirationTtl: 5 * 60
|
|
4147
|
+
}).catch((e) => {
|
|
4148
|
+
console.error(`[email-send] burst counter put failed for ${projectId}:`, e);
|
|
4149
|
+
})
|
|
4150
|
+
]);
|
|
4151
|
+
let failure = null;
|
|
4152
|
+
let messageId = "";
|
|
4153
|
+
let bounced = /* @__PURE__ */ new Set();
|
|
4154
|
+
let suppressed = /* @__PURE__ */ new Set();
|
|
4155
|
+
let cfRes = null;
|
|
4156
|
+
try {
|
|
4157
|
+
cfRes = await fetch(
|
|
4158
|
+
`https://api.cloudflare.com/client/v4/accounts/${mirror.accountId}/email/sending/send_raw`,
|
|
4159
|
+
{
|
|
4160
|
+
method: "POST",
|
|
4161
|
+
headers: {
|
|
4162
|
+
authorization: `Bearer ${leaseBearer(lease)}`,
|
|
4163
|
+
"content-type": "application/json"
|
|
4164
|
+
},
|
|
4165
|
+
body: JSON.stringify({ from, recipients, mime_message: mimeMessage })
|
|
4166
|
+
}
|
|
4167
|
+
);
|
|
4168
|
+
} catch {
|
|
4169
|
+
failure = { code: "UPSTREAM_ERROR", message: "email sending request failed" };
|
|
4170
|
+
}
|
|
4171
|
+
if (cfRes) {
|
|
4172
|
+
if (!cfRes.ok) {
|
|
4173
|
+
if (cfRes.status === 401 || cfRes.status === 403) {
|
|
4174
|
+
dropEmailDomainLease(fromDomain);
|
|
4175
|
+
}
|
|
4176
|
+
failure = {
|
|
4177
|
+
code: cfRes.status === 429 ? "QUOTA_EXCEEDED" : "UPSTREAM_ERROR",
|
|
4178
|
+
message: `email sending API returned ${cfRes.status}`
|
|
4179
|
+
};
|
|
4180
|
+
} else {
|
|
4181
|
+
let data = null;
|
|
4182
|
+
try {
|
|
4183
|
+
data = await cfRes.json();
|
|
4184
|
+
} catch {
|
|
4185
|
+
data = null;
|
|
4186
|
+
}
|
|
4187
|
+
const success = typeof data === "object" && data !== null && data.success === true;
|
|
4188
|
+
if (!success) {
|
|
4189
|
+
failure = { code: "UPSTREAM_ERROR", message: "email sending API refused the message" };
|
|
4190
|
+
} else {
|
|
4191
|
+
const result = data.result;
|
|
4192
|
+
const id = result?.message_id;
|
|
4193
|
+
messageId = typeof id === "string" ? id : "";
|
|
4194
|
+
bounced = readSendRawAddressList(result?.permanent_bounces);
|
|
4195
|
+
suppressed = readSendRawAddressList(result?.suppressed_recipients);
|
|
4196
|
+
}
|
|
4197
|
+
}
|
|
4198
|
+
}
|
|
4199
|
+
const deliveries = [];
|
|
4200
|
+
for (const r of recipients) {
|
|
4201
|
+
if (failure) {
|
|
4202
|
+
deliveries.push({ recipient: r, ok: false, error: failure });
|
|
4203
|
+
} else if (bounced.has(r)) {
|
|
4204
|
+
deliveries.push({
|
|
4205
|
+
recipient: r,
|
|
4206
|
+
ok: false,
|
|
4207
|
+
error: {
|
|
4208
|
+
code: "UNVERIFIED_DESTINATION",
|
|
4209
|
+
message: `recipient '${r}' permanently bounced`
|
|
4210
|
+
}
|
|
4211
|
+
});
|
|
4212
|
+
} else if (suppressed.has(r)) {
|
|
4213
|
+
deliveries.push({
|
|
4214
|
+
recipient: r,
|
|
4215
|
+
ok: false,
|
|
4216
|
+
error: {
|
|
4217
|
+
code: "UNVERIFIED_DESTINATION",
|
|
4218
|
+
message: `recipient '${r}' is on the suppression list`
|
|
4219
|
+
}
|
|
4220
|
+
});
|
|
4221
|
+
} else {
|
|
4222
|
+
deliveries.push({ recipient: r, ok: true, messageId });
|
|
4223
|
+
}
|
|
4224
|
+
}
|
|
4225
|
+
logSend(projectId, requestId, deliveries);
|
|
4226
|
+
if (deliveries.some((d) => !d.ok)) {
|
|
4227
|
+
return context.json({ ok: false, deliveries }, 200);
|
|
4228
|
+
}
|
|
4229
|
+
return context.json({
|
|
4230
|
+
ok: true,
|
|
4231
|
+
ids: deliveries.map((d) => ({
|
|
4232
|
+
recipient: d.recipient,
|
|
4233
|
+
messageId: d.messageId
|
|
4234
|
+
}))
|
|
4235
|
+
});
|
|
4236
|
+
}
|
|
4237
|
+
__name(sendViaCustomDomain, "sendViaCustomDomain");
|
|
3861
4238
|
async function recordInboundDelivery(env, projectId, info) {
|
|
3862
4239
|
const now = /* @__PURE__ */ new Date();
|
|
3863
4240
|
const counterKey = `email-recv:${projectId}:${monthBucket(now)}`;
|
|
@@ -3927,6 +4304,35 @@ async function readAllowlist(kv2, projectId) {
|
|
|
3927
4304
|
}
|
|
3928
4305
|
}
|
|
3929
4306
|
__name(readAllowlist, "readAllowlist");
|
|
4307
|
+
async function readEmailDomainMirror(kv2, domain, options) {
|
|
4308
|
+
const raw2 = await kv2.get(`email-domain:${domain}`, options);
|
|
4309
|
+
if (!raw2) {
|
|
4310
|
+
return null;
|
|
4311
|
+
}
|
|
4312
|
+
let parsed;
|
|
4313
|
+
try {
|
|
4314
|
+
parsed = JSON.parse(raw2);
|
|
4315
|
+
} catch {
|
|
4316
|
+
return null;
|
|
4317
|
+
}
|
|
4318
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
4319
|
+
return null;
|
|
4320
|
+
}
|
|
4321
|
+
const entry = parsed;
|
|
4322
|
+
if (typeof entry.projectId !== "string" || entry.projectId === "" || typeof entry.status !== "string" || typeof entry.relaySecretSha256 !== "string") {
|
|
4323
|
+
return null;
|
|
4324
|
+
}
|
|
4325
|
+
return {
|
|
4326
|
+
projectId: entry.projectId,
|
|
4327
|
+
status: entry.status,
|
|
4328
|
+
relaySecretSha256: entry.relaySecretSha256,
|
|
4329
|
+
relayWorkerName: typeof entry.relayWorkerName === "string" ? entry.relayWorkerName : null,
|
|
4330
|
+
accountId: typeof entry.accountId === "string" ? entry.accountId : null,
|
|
4331
|
+
zoneId: typeof entry.zoneId === "string" ? entry.zoneId : null,
|
|
4332
|
+
mode: typeof entry.mode === "string" ? entry.mode : null
|
|
4333
|
+
};
|
|
4334
|
+
}
|
|
4335
|
+
__name(readEmailDomainMirror, "readEmailDomainMirror");
|
|
3930
4336
|
function readCounter(raw2) {
|
|
3931
4337
|
if (!raw2) {
|
|
3932
4338
|
return 0;
|
|
@@ -4029,7 +4435,7 @@ app.post("/internal/send", async (context) => {
|
|
|
4029
4435
|
}
|
|
4030
4436
|
});
|
|
4031
4437
|
|
|
4032
|
-
//
|
|
4438
|
+
// ../../home/runner/work/void/void/platform/packages/proxy/src/email-router.ts
|
|
4033
4439
|
var CONTROL_CHARS_RE2 = new RegExp("[\\u0000-\\u001f\\u007f]+", "g");
|
|
4034
4440
|
function sanitize(value, maxLen) {
|
|
4035
4441
|
return value.slice(0, maxLen).replace(CONTROL_CHARS_RE2, " ");
|
|
@@ -4129,7 +4535,7 @@ function forwardHeadersWithinBounds(headers) {
|
|
|
4129
4535
|
return true;
|
|
4130
4536
|
}
|
|
4131
4537
|
__name(forwardHeadersWithinBounds, "forwardHeadersWithinBounds");
|
|
4132
|
-
function isPermittedReplySender(from, pin) {
|
|
4538
|
+
async function isPermittedReplySender(from, pin) {
|
|
4133
4539
|
if (!/^[\x21-\x7e]+$/.test(from)) {
|
|
4134
4540
|
return false;
|
|
4135
4541
|
}
|
|
@@ -4138,13 +4544,32 @@ function isPermittedReplySender(from, pin) {
|
|
|
4138
4544
|
if (at <= 0) {
|
|
4139
4545
|
return false;
|
|
4140
4546
|
}
|
|
4141
|
-
|
|
4142
|
-
|
|
4547
|
+
const fromDomain = lower.slice(at + 1);
|
|
4548
|
+
if (fromDomain === pin.domain) {
|
|
4549
|
+
const localPrefix = lower.slice(0, at).split("+")[0] ?? "";
|
|
4550
|
+
if (localPrefix.length > 0 && localPrefix === pin.slug) {
|
|
4551
|
+
return true;
|
|
4552
|
+
}
|
|
4143
4553
|
}
|
|
4144
|
-
|
|
4145
|
-
return localPrefix.length > 0 && localPrefix === pin.slug;
|
|
4554
|
+
return isRegisteredDomainSender(fromDomain, pin);
|
|
4146
4555
|
}
|
|
4147
4556
|
__name(isPermittedReplySender, "isPermittedReplySender");
|
|
4557
|
+
async function isRegisteredDomainSender(fromDomain, pin) {
|
|
4558
|
+
if (!pin.projectId) {
|
|
4559
|
+
return false;
|
|
4560
|
+
}
|
|
4561
|
+
const cached = pin.registeredDomains.get(fromDomain);
|
|
4562
|
+
if (cached !== void 0) {
|
|
4563
|
+
return cached;
|
|
4564
|
+
}
|
|
4565
|
+
const mirror = await readEmailDomainMirror(pin.kv, fromDomain, { cacheTtl: 60 }).catch(
|
|
4566
|
+
() => null
|
|
4567
|
+
);
|
|
4568
|
+
const registered = mirror !== null && mirror.projectId === pin.projectId && mirror.status === "active";
|
|
4569
|
+
pin.registeredDomains.set(fromDomain, registered);
|
|
4570
|
+
return registered;
|
|
4571
|
+
}
|
|
4572
|
+
__name(isRegisteredDomainSender, "isRegisteredDomainSender");
|
|
4148
4573
|
function readCounter2(raw2) {
|
|
4149
4574
|
if (!raw2) {
|
|
4150
4575
|
return 0;
|
|
@@ -4310,6 +4735,7 @@ async function routeInbound(message, env, ctx) {
|
|
|
4310
4735
|
);
|
|
4311
4736
|
gate = {
|
|
4312
4737
|
projectId: routing.projectId,
|
|
4738
|
+
skipAllowlist: false,
|
|
4313
4739
|
allowlist: /* @__PURE__ */ new Set(),
|
|
4314
4740
|
burstKey: "",
|
|
4315
4741
|
burstLimit: 0,
|
|
@@ -4353,14 +4779,228 @@ async function routeInbound(message, env, ctx) {
|
|
|
4353
4779
|
);
|
|
4354
4780
|
return;
|
|
4355
4781
|
}
|
|
4356
|
-
await enactActions(env, message, actions, gate, {
|
|
4782
|
+
await enactActions(env, message, actions, gate, {
|
|
4783
|
+
slug,
|
|
4784
|
+
domain: receivedDomain,
|
|
4785
|
+
projectId: routing.projectId,
|
|
4786
|
+
kv: env.ROUTING,
|
|
4787
|
+
registeredDomains: /* @__PURE__ */ new Map()
|
|
4788
|
+
});
|
|
4357
4789
|
ctx.waitUntil(logRecv(env, routing.projectId, message, actions));
|
|
4358
4790
|
}
|
|
4359
4791
|
__name(routeInbound, "routeInbound");
|
|
4360
|
-
async function
|
|
4792
|
+
async function routeRelayed(env, ctx, input) {
|
|
4793
|
+
const { projectId, domain, envelope } = input;
|
|
4794
|
+
const slug = await env.ROUTING.get(`email-slug:${projectId}`);
|
|
4795
|
+
if (!slug) {
|
|
4796
|
+
console.error(`[email-relay] no slug mirror for project ${sanitize(projectId, 64)}`);
|
|
4797
|
+
return { reject: true };
|
|
4798
|
+
}
|
|
4799
|
+
const routingJson = await env.ROUTING.get(slug);
|
|
4800
|
+
if (!routingJson) {
|
|
4801
|
+
console.error(
|
|
4802
|
+
`[email-relay] no routing entry for slug ${sanitize(slug, 64)} (project ${sanitize(projectId, 64)})`
|
|
4803
|
+
);
|
|
4804
|
+
return { reject: true };
|
|
4805
|
+
}
|
|
4806
|
+
let parsedRouting;
|
|
4807
|
+
try {
|
|
4808
|
+
parsedRouting = JSON.parse(routingJson);
|
|
4809
|
+
} catch {
|
|
4810
|
+
console.error(`[email-relay] routing data corrupt for slug ${sanitize(slug, 64)}`);
|
|
4811
|
+
return { reject: true };
|
|
4812
|
+
}
|
|
4813
|
+
if (typeof parsedRouting !== "object" || parsedRouting === null) {
|
|
4814
|
+
console.error(`[email-relay] routing data corrupt for slug ${sanitize(slug, 64)}`);
|
|
4815
|
+
return { reject: true };
|
|
4816
|
+
}
|
|
4817
|
+
const routing = parsedRouting;
|
|
4818
|
+
if (routing.projectId !== projectId) {
|
|
4819
|
+
console.error(
|
|
4820
|
+
`[email-relay] routing blob names another project for slug ${sanitize(slug, 64)}`
|
|
4821
|
+
);
|
|
4822
|
+
return { reject: true };
|
|
4823
|
+
}
|
|
4824
|
+
if (routing.suspended) {
|
|
4825
|
+
console.error(`[email-relay] project ${sanitize(projectId, 64)} is suspended`);
|
|
4826
|
+
return { reject: true };
|
|
4827
|
+
}
|
|
4828
|
+
const workerName = routing.workerName;
|
|
4829
|
+
if (!workerName || typeof workerName !== "string") {
|
|
4830
|
+
console.error(`[email-relay] project ${sanitize(projectId, 64)} has no worker (static deploy)`);
|
|
4831
|
+
return { reject: true };
|
|
4832
|
+
}
|
|
4833
|
+
if (await env.ROUTING.get(`email-suspended:${projectId}`)) {
|
|
4834
|
+
console.error(`[email-relay] project ${sanitize(projectId, 64)} inbound is suspended`);
|
|
4835
|
+
return { reject: true };
|
|
4836
|
+
}
|
|
4837
|
+
let stub;
|
|
4838
|
+
try {
|
|
4839
|
+
stub = routing.limits ? env.DISPATCH.get(workerName, {}, { limits: routing.limits }) : env.DISPATCH.get(workerName);
|
|
4840
|
+
} catch (e) {
|
|
4841
|
+
console.error(`[email-relay] DISPATCH.get(${sanitize(workerName, 320)}) failed:`, e);
|
|
4842
|
+
return { reject: true };
|
|
4843
|
+
}
|
|
4844
|
+
const relayed = createRelayedMessage(env, envelope, { domain, projectId });
|
|
4845
|
+
let gate;
|
|
4846
|
+
try {
|
|
4847
|
+
gate = await loadOutboundGate(env, projectId, { skipAllowlist: true });
|
|
4848
|
+
} catch (e) {
|
|
4849
|
+
console.error(`[email-relay] failed to load outbound gate for ${sanitize(projectId, 64)}:`, e);
|
|
4850
|
+
gate = {
|
|
4851
|
+
projectId,
|
|
4852
|
+
skipAllowlist: true,
|
|
4853
|
+
allowlist: /* @__PURE__ */ new Set(),
|
|
4854
|
+
burstKey: "",
|
|
4855
|
+
burstLimit: 0,
|
|
4856
|
+
burstCurrent: 0,
|
|
4857
|
+
monthKey: "",
|
|
4858
|
+
monthLimit: 0,
|
|
4859
|
+
monthCurrent: 0,
|
|
4860
|
+
quotaExhausted: true
|
|
4861
|
+
};
|
|
4862
|
+
}
|
|
4863
|
+
let actions;
|
|
4864
|
+
try {
|
|
4865
|
+
const result = await stub.email(envelope);
|
|
4866
|
+
if (result === void 0 || result === null) {
|
|
4867
|
+
actions = [];
|
|
4868
|
+
} else if (Array.isArray(result)) {
|
|
4869
|
+
actions = result;
|
|
4870
|
+
} else {
|
|
4871
|
+
console.warn(
|
|
4872
|
+
`[email-relay] ${sanitize(workerName, 320)} email handler returned non-array; treating as no-op`
|
|
4873
|
+
);
|
|
4874
|
+
actions = [];
|
|
4875
|
+
}
|
|
4876
|
+
} catch (e) {
|
|
4877
|
+
const msg = e instanceof Error ? e.message.slice(0, 320) : describeUntrusted(e, 320);
|
|
4878
|
+
if (RPC_METHOD_MISSING.test(msg)) {
|
|
4879
|
+
console.error(
|
|
4880
|
+
`[email-relay] ${sanitize(workerName, 320)} has no .email() (needs redeploy):`,
|
|
4881
|
+
sanitize(msg, 320)
|
|
4882
|
+
);
|
|
4883
|
+
ctx.waitUntil(
|
|
4884
|
+
logRecv(env, projectId, relayed.message, [{ type: "reject", reason: "no-handler" }])
|
|
4885
|
+
);
|
|
4886
|
+
return { reject: true };
|
|
4887
|
+
}
|
|
4888
|
+
console.error(`[email-relay] dispatch to ${sanitize(workerName, 320)} failed:`, msg);
|
|
4889
|
+
ctx.waitUntil(
|
|
4890
|
+
logRecv(env, projectId, relayed.message, [{ type: "reject", reason: "dispatch-error" }])
|
|
4891
|
+
);
|
|
4892
|
+
return { reject: true };
|
|
4893
|
+
}
|
|
4894
|
+
await enactActions(env, relayed.message, actions, gate, {
|
|
4895
|
+
slug: slug.toLowerCase(),
|
|
4896
|
+
domain,
|
|
4897
|
+
projectId,
|
|
4898
|
+
kv: env.ROUTING,
|
|
4899
|
+
registeredDomains: /* @__PURE__ */ new Map()
|
|
4900
|
+
});
|
|
4901
|
+
ctx.waitUntil(logRecv(env, projectId, relayed.message, actions));
|
|
4902
|
+
return { reject: relayed.rejectedReason() !== null };
|
|
4903
|
+
}
|
|
4904
|
+
__name(routeRelayed, "routeRelayed");
|
|
4905
|
+
function createRelayedMessage(env, envelope, relay) {
|
|
4906
|
+
let reason = null;
|
|
4907
|
+
async function sendRaw(from, to, raw2) {
|
|
4908
|
+
const senderDomain = from.slice(from.indexOf("@") + 1).toLowerCase();
|
|
4909
|
+
const mirror = await readEmailDomainMirror(env.ROUTING, senderDomain);
|
|
4910
|
+
if (!mirror || mirror.projectId !== relay.projectId || mirror.status !== "active") {
|
|
4911
|
+
throw new Error("email domain registration is no longer active");
|
|
4912
|
+
}
|
|
4913
|
+
if (!mirror.accountId) {
|
|
4914
|
+
throw new Error("email domain registration is incomplete");
|
|
4915
|
+
}
|
|
4916
|
+
const lease = await leaseEmailDomainCredential(env, senderDomain);
|
|
4917
|
+
if (!lease) {
|
|
4918
|
+
throw new Error("could not lease the domain credential");
|
|
4919
|
+
}
|
|
4920
|
+
const mimeMessage = new TextDecoder("utf-8", { fatal: true, ignoreBOM: false }).decode(raw2);
|
|
4921
|
+
let res;
|
|
4922
|
+
try {
|
|
4923
|
+
res = await fetch(
|
|
4924
|
+
`https://api.cloudflare.com/client/v4/accounts/${mirror.accountId}/email/sending/send_raw`,
|
|
4925
|
+
{
|
|
4926
|
+
method: "POST",
|
|
4927
|
+
headers: {
|
|
4928
|
+
authorization: `Bearer ${leaseBearer(lease)}`,
|
|
4929
|
+
"content-type": "application/json"
|
|
4930
|
+
},
|
|
4931
|
+
body: JSON.stringify({ from, recipients: [to], mime_message: mimeMessage })
|
|
4932
|
+
}
|
|
4933
|
+
);
|
|
4934
|
+
} catch {
|
|
4935
|
+
throw new Error("email sending request failed");
|
|
4936
|
+
}
|
|
4937
|
+
if (res.status === 401 || res.status === 403) {
|
|
4938
|
+
dropEmailDomainLease(senderDomain);
|
|
4939
|
+
}
|
|
4940
|
+
if (!res.ok) {
|
|
4941
|
+
throw new Error(`email sending API returned ${res.status}`);
|
|
4942
|
+
}
|
|
4943
|
+
let data = null;
|
|
4944
|
+
try {
|
|
4945
|
+
data = await res.json();
|
|
4946
|
+
} catch {
|
|
4947
|
+
data = null;
|
|
4948
|
+
}
|
|
4949
|
+
const success = typeof data === "object" && data !== null && data.success === true;
|
|
4950
|
+
if (!success) {
|
|
4951
|
+
throw new Error("email sending API refused the message");
|
|
4952
|
+
}
|
|
4953
|
+
const result = data.result;
|
|
4954
|
+
const target = to.toLowerCase();
|
|
4955
|
+
if (readSendRawAddressList(result?.permanent_bounces).has(target)) {
|
|
4956
|
+
throw new Error("recipient permanently bounced");
|
|
4957
|
+
}
|
|
4958
|
+
if (readSendRawAddressList(result?.suppressed_recipients).has(target)) {
|
|
4959
|
+
throw new Error("recipient is on the suppression list");
|
|
4960
|
+
}
|
|
4961
|
+
}
|
|
4962
|
+
__name(sendRaw, "sendRaw");
|
|
4963
|
+
const message = {
|
|
4964
|
+
from: envelope.from,
|
|
4965
|
+
to: envelope.to,
|
|
4966
|
+
rawSize: envelope.rawSize,
|
|
4967
|
+
headers: new Headers(envelope.headers),
|
|
4968
|
+
raw: envelopeRawStream(envelope.raw),
|
|
4969
|
+
setReject(r) {
|
|
4970
|
+
reason = r;
|
|
4971
|
+
},
|
|
4972
|
+
async forward(to, _headers) {
|
|
4973
|
+
await sendRaw(envelope.to, to, envelope.raw);
|
|
4974
|
+
},
|
|
4975
|
+
// The reply channel `enactSanitized` prefers when present. A native
|
|
4976
|
+
// reply needs both the CF email event this lane never had and a genuine
|
|
4977
|
+
// EmailMessage — whose class exposes no body accessor, so the recorded
|
|
4978
|
+
// reply bytes could not cross it anyway; they arrive on the action.
|
|
4979
|
+
async __voidRelayReply(from, to, raw2) {
|
|
4980
|
+
await sendRaw(from, to, raw2);
|
|
4981
|
+
}
|
|
4982
|
+
};
|
|
4983
|
+
return {
|
|
4984
|
+
message,
|
|
4985
|
+
rejectedReason: /* @__PURE__ */ __name(() => reason, "rejectedReason")
|
|
4986
|
+
};
|
|
4987
|
+
}
|
|
4988
|
+
__name(createRelayedMessage, "createRelayedMessage");
|
|
4989
|
+
function envelopeRawStream(bytes) {
|
|
4990
|
+
return new ReadableStream({
|
|
4991
|
+
start(controller) {
|
|
4992
|
+
controller.enqueue(bytes);
|
|
4993
|
+
controller.close();
|
|
4994
|
+
}
|
|
4995
|
+
});
|
|
4996
|
+
}
|
|
4997
|
+
__name(envelopeRawStream, "envelopeRawStream");
|
|
4998
|
+
async function loadOutboundGate(env, projectId, opts) {
|
|
4999
|
+
const skipAllowlist = opts?.skipAllowlist ?? false;
|
|
4361
5000
|
if (!projectId) {
|
|
4362
5001
|
return {
|
|
4363
5002
|
projectId: void 0,
|
|
5003
|
+
skipAllowlist,
|
|
4364
5004
|
allowlist: /* @__PURE__ */ new Set(),
|
|
4365
5005
|
burstKey: "",
|
|
4366
5006
|
burstLimit: 0,
|
|
@@ -4371,7 +5011,7 @@ async function loadOutboundGate(env, projectId) {
|
|
|
4371
5011
|
quotaExhausted: true
|
|
4372
5012
|
};
|
|
4373
5013
|
}
|
|
4374
|
-
const allowlist = await readAllowlist2(env.ROUTING, projectId);
|
|
5014
|
+
const allowlist = skipAllowlist ? /* @__PURE__ */ new Set() : await readAllowlist2(env.ROUTING, projectId);
|
|
4375
5015
|
const now = /* @__PURE__ */ new Date();
|
|
4376
5016
|
const burstKey = `email-burst:${projectId}:${minuteBucket2(now)}`;
|
|
4377
5017
|
const monthKey = `email-sent:${projectId}:${monthBucket2(now)}`;
|
|
@@ -4383,6 +5023,7 @@ async function loadOutboundGate(env, projectId) {
|
|
|
4383
5023
|
]);
|
|
4384
5024
|
return {
|
|
4385
5025
|
projectId,
|
|
5026
|
+
skipAllowlist,
|
|
4386
5027
|
allowlist,
|
|
4387
5028
|
burstKey,
|
|
4388
5029
|
burstLimit,
|
|
@@ -4490,7 +5131,7 @@ async function readStreamToBytes(stream) {
|
|
|
4490
5131
|
return out;
|
|
4491
5132
|
}
|
|
4492
5133
|
__name(readStreamToBytes, "readStreamToBytes");
|
|
4493
|
-
function validateActions(actions, replySender) {
|
|
5134
|
+
async function validateActions(actions, replySender) {
|
|
4494
5135
|
if (actions.length > MAX_ACTIONS) {
|
|
4495
5136
|
console.error(
|
|
4496
5137
|
`[email-router] action list dropped: ${actions.length} actions returned (max ${MAX_ACTIONS})`
|
|
@@ -4540,7 +5181,7 @@ function validateActions(actions, replySender) {
|
|
|
4540
5181
|
);
|
|
4541
5182
|
continue;
|
|
4542
5183
|
}
|
|
4543
|
-
if (!isPermittedReplySender(action.from, replySender)) {
|
|
5184
|
+
if (!await isPermittedReplySender(action.from, replySender)) {
|
|
4544
5185
|
console.error(
|
|
4545
5186
|
`[email-router] reply dropped: sender not owned by project "${sanitize(replySender.slug, 64)}" (from=${logAddress(action.from)})`
|
|
4546
5187
|
);
|
|
@@ -4584,7 +5225,7 @@ function validateActions(actions, replySender) {
|
|
|
4584
5225
|
}
|
|
4585
5226
|
__name(validateActions, "validateActions");
|
|
4586
5227
|
async function enactActions(env, message, actions, gate, replySender) {
|
|
4587
|
-
const sanitized = validateActions(actions, replySender);
|
|
5228
|
+
const sanitized = await validateActions(actions, replySender);
|
|
4588
5229
|
if (sanitized === null) {
|
|
4589
5230
|
return;
|
|
4590
5231
|
}
|
|
@@ -4621,14 +5262,19 @@ async function enactSanitized(env, message, actions, gate) {
|
|
|
4621
5262
|
continue;
|
|
4622
5263
|
}
|
|
4623
5264
|
try {
|
|
4624
|
-
const
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
5265
|
+
const relayReply = message.__voidRelayReply;
|
|
5266
|
+
if (relayReply !== void 0) {
|
|
5267
|
+
await relayReply(action.from, action.to, action.raw);
|
|
5268
|
+
} else {
|
|
5269
|
+
const stream = new ReadableStream({
|
|
5270
|
+
start(controller) {
|
|
5271
|
+
controller.enqueue(action.raw);
|
|
5272
|
+
controller.close();
|
|
5273
|
+
}
|
|
5274
|
+
});
|
|
5275
|
+
const reply = new EmailMessage2(action.from, action.to, stream);
|
|
5276
|
+
await message.reply(reply);
|
|
5277
|
+
}
|
|
4632
5278
|
gate.burstCurrent += 1;
|
|
4633
5279
|
gate.monthCurrent += 1;
|
|
4634
5280
|
counterDirty = true;
|
|
@@ -4659,7 +5305,7 @@ function checkOutboundGate(gate, target, kind) {
|
|
|
4659
5305
|
return false;
|
|
4660
5306
|
}
|
|
4661
5307
|
const lowered = target.toLowerCase();
|
|
4662
|
-
if (!gate.allowlist.has(lowered)) {
|
|
5308
|
+
if (!gate.skipAllowlist && !gate.allowlist.has(lowered)) {
|
|
4663
5309
|
console.error(
|
|
4664
5310
|
`[email-router] ${kind} dropped: recipient not in project allowlist (target=${sanitize(target, 320)})`
|
|
4665
5311
|
);
|
|
@@ -4679,7 +5325,180 @@ function checkOutboundGate(gate, target, kind) {
|
|
|
4679
5325
|
}
|
|
4680
5326
|
__name(checkOutboundGate, "checkOutboundGate");
|
|
4681
5327
|
|
|
4682
|
-
//
|
|
5328
|
+
// ../../home/runner/work/void/void/platform/packages/proxy/src/email-relay.ts
|
|
5329
|
+
var app2 = new Hono2();
|
|
5330
|
+
app2.post("/relay", async (context) => {
|
|
5331
|
+
const projectId = context.get("projectId");
|
|
5332
|
+
const from = context.req.header("X-Void-Email-From") ?? "";
|
|
5333
|
+
const to = context.req.header("X-Void-Email-To") ?? "";
|
|
5334
|
+
const domain = to.slice(to.lastIndexOf("@") + 1).toLowerCase();
|
|
5335
|
+
const body = context.req.raw.body;
|
|
5336
|
+
if (!body) {
|
|
5337
|
+
return context.json({ error: "mime body required" }, 400);
|
|
5338
|
+
}
|
|
5339
|
+
const raw2 = await readMimeBody(body);
|
|
5340
|
+
if (raw2 === null) {
|
|
5341
|
+
return context.json({ error: "mime too large" }, 413);
|
|
5342
|
+
}
|
|
5343
|
+
const headers = parseRelayedHeaders(raw2);
|
|
5344
|
+
if (headers === null) {
|
|
5345
|
+
return context.json({ error: "too many headers" }, 413);
|
|
5346
|
+
}
|
|
5347
|
+
const envelope = {
|
|
5348
|
+
from,
|
|
5349
|
+
to,
|
|
5350
|
+
headers,
|
|
5351
|
+
raw: raw2,
|
|
5352
|
+
rawSize: raw2.byteLength,
|
|
5353
|
+
viaDomain: domain
|
|
5354
|
+
};
|
|
5355
|
+
const pending = [];
|
|
5356
|
+
let ctx = {
|
|
5357
|
+
waitUntil(p) {
|
|
5358
|
+
pending.push(p);
|
|
5359
|
+
},
|
|
5360
|
+
passThroughOnException() {
|
|
5361
|
+
}
|
|
5362
|
+
};
|
|
5363
|
+
try {
|
|
5364
|
+
ctx = context.executionCtx;
|
|
5365
|
+
} catch {
|
|
5366
|
+
}
|
|
5367
|
+
const outcome = await routeRelayed(context.env, ctx, { projectId, domain, envelope });
|
|
5368
|
+
if (pending.length > 0) {
|
|
5369
|
+
await Promise.allSettled(pending);
|
|
5370
|
+
}
|
|
5371
|
+
if (outcome.reject) {
|
|
5372
|
+
return context.json({ error: "message rejected" }, 403);
|
|
5373
|
+
}
|
|
5374
|
+
return context.json({ ok: true });
|
|
5375
|
+
});
|
|
5376
|
+
async function readMimeBody(stream) {
|
|
5377
|
+
const reader = stream.getReader();
|
|
5378
|
+
const chunks = [];
|
|
5379
|
+
let total = 0;
|
|
5380
|
+
for (; ; ) {
|
|
5381
|
+
const { value, done } = await reader.read();
|
|
5382
|
+
if (done) {
|
|
5383
|
+
break;
|
|
5384
|
+
}
|
|
5385
|
+
total += value.byteLength;
|
|
5386
|
+
if (total > MAX_MIME_BYTES) {
|
|
5387
|
+
try {
|
|
5388
|
+
await reader.cancel();
|
|
5389
|
+
} catch {
|
|
5390
|
+
}
|
|
5391
|
+
return null;
|
|
5392
|
+
}
|
|
5393
|
+
chunks.push(value);
|
|
5394
|
+
}
|
|
5395
|
+
const out = new Uint8Array(total);
|
|
5396
|
+
let offset = 0;
|
|
5397
|
+
for (const c of chunks) {
|
|
5398
|
+
out.set(c, offset);
|
|
5399
|
+
offset += c.byteLength;
|
|
5400
|
+
}
|
|
5401
|
+
return out;
|
|
5402
|
+
}
|
|
5403
|
+
__name(readMimeBody, "readMimeBody");
|
|
5404
|
+
var LF2 = 10;
|
|
5405
|
+
var CR2 = 13;
|
|
5406
|
+
var HEADER_NAME_RE = /^[!#$%&'*+\-.^_`|~0-9a-z]+$/;
|
|
5407
|
+
var MAX_INBOUND_HEADER_LINES = 4096;
|
|
5408
|
+
function parseRelayedHeaders(raw2) {
|
|
5409
|
+
const block = new TextDecoder().decode(raw2.subarray(0, headerBlockEnd(raw2)));
|
|
5410
|
+
const folded = /* @__PURE__ */ new Map();
|
|
5411
|
+
let totalPieces = 0;
|
|
5412
|
+
let current = null;
|
|
5413
|
+
let pos = 0;
|
|
5414
|
+
while (pos < block.length) {
|
|
5415
|
+
let nl = block.indexOf("\n", pos);
|
|
5416
|
+
if (nl === -1) {
|
|
5417
|
+
nl = block.length;
|
|
5418
|
+
}
|
|
5419
|
+
let line = block.slice(pos, nl);
|
|
5420
|
+
pos = nl + 1;
|
|
5421
|
+
if (line.endsWith("\r")) {
|
|
5422
|
+
line = line.slice(0, -1);
|
|
5423
|
+
}
|
|
5424
|
+
if (line === "") {
|
|
5425
|
+
break;
|
|
5426
|
+
}
|
|
5427
|
+
if (line.startsWith(" ") || line.startsWith(" ")) {
|
|
5428
|
+
if (current !== null && headerValueSafe(line)) {
|
|
5429
|
+
totalPieces += 1;
|
|
5430
|
+
if (totalPieces > MAX_INBOUND_HEADER_LINES) {
|
|
5431
|
+
return null;
|
|
5432
|
+
}
|
|
5433
|
+
const pieces2 = folded.get(current) ?? [];
|
|
5434
|
+
pieces2.push(` ${line.trim()}`);
|
|
5435
|
+
folded.set(current, pieces2);
|
|
5436
|
+
}
|
|
5437
|
+
continue;
|
|
5438
|
+
}
|
|
5439
|
+
const colon = line.indexOf(":");
|
|
5440
|
+
if (colon === -1) {
|
|
5441
|
+
current = null;
|
|
5442
|
+
continue;
|
|
5443
|
+
}
|
|
5444
|
+
const name = line.slice(0, colon).trim().toLowerCase();
|
|
5445
|
+
const value = line.slice(colon + 1).trim();
|
|
5446
|
+
if (!HEADER_NAME_RE.test(name) || !headerValueSafe(value)) {
|
|
5447
|
+
current = null;
|
|
5448
|
+
continue;
|
|
5449
|
+
}
|
|
5450
|
+
const pieces = folded.get(name);
|
|
5451
|
+
totalPieces += 1;
|
|
5452
|
+
if (totalPieces > MAX_INBOUND_HEADER_LINES) {
|
|
5453
|
+
return null;
|
|
5454
|
+
}
|
|
5455
|
+
if (pieces !== void 0) {
|
|
5456
|
+
pieces.push(`, ${value}`);
|
|
5457
|
+
} else {
|
|
5458
|
+
if (folded.size >= MAX_INBOUND_HEADERS) {
|
|
5459
|
+
return null;
|
|
5460
|
+
}
|
|
5461
|
+
folded.set(name, [value]);
|
|
5462
|
+
}
|
|
5463
|
+
current = name;
|
|
5464
|
+
}
|
|
5465
|
+
return Object.fromEntries(
|
|
5466
|
+
Array.from(folded, ([name, pieces]) => [name, pieces.join("")])
|
|
5467
|
+
);
|
|
5468
|
+
}
|
|
5469
|
+
__name(parseRelayedHeaders, "parseRelayedHeaders");
|
|
5470
|
+
function headerBlockEnd(raw2) {
|
|
5471
|
+
let lineStart = 0;
|
|
5472
|
+
while (lineStart < raw2.length) {
|
|
5473
|
+
const lf = raw2.indexOf(LF2, lineStart);
|
|
5474
|
+
const lineEnd = lf === -1 ? raw2.length : lf;
|
|
5475
|
+
let contentEnd = lineEnd;
|
|
5476
|
+
if (contentEnd > lineStart && raw2[contentEnd - 1] === CR2) {
|
|
5477
|
+
contentEnd--;
|
|
5478
|
+
}
|
|
5479
|
+
if (contentEnd === lineStart) {
|
|
5480
|
+
return lineStart;
|
|
5481
|
+
}
|
|
5482
|
+
lineStart = lf === -1 ? raw2.length : lf + 1;
|
|
5483
|
+
}
|
|
5484
|
+
return raw2.length;
|
|
5485
|
+
}
|
|
5486
|
+
__name(headerBlockEnd, "headerBlockEnd");
|
|
5487
|
+
function headerValueSafe(value) {
|
|
5488
|
+
for (let i = 0; i < value.length; i++) {
|
|
5489
|
+
const code = value.charCodeAt(i);
|
|
5490
|
+
if (code === 9) {
|
|
5491
|
+
continue;
|
|
5492
|
+
}
|
|
5493
|
+
if (code < 32 || code === 127 || code > 255) {
|
|
5494
|
+
return false;
|
|
5495
|
+
}
|
|
5496
|
+
}
|
|
5497
|
+
return true;
|
|
5498
|
+
}
|
|
5499
|
+
__name(headerValueSafe, "headerValueSafe");
|
|
5500
|
+
|
|
5501
|
+
// ../../home/runner/work/void/void/platform/packages/proxy/src/kv.ts
|
|
4683
5502
|
init_performance2();
|
|
4684
5503
|
var CF_API2 = "https://api.cloudflare.com/client/v4";
|
|
4685
5504
|
var kv = new Hono2();
|
|
@@ -4861,12 +5680,12 @@ kv.post("/list", async (context) => {
|
|
|
4861
5680
|
});
|
|
4862
5681
|
});
|
|
4863
5682
|
|
|
4864
|
-
//
|
|
5683
|
+
// ../../home/runner/work/void/void/platform/packages/proxy/src/platform-lifecycle.ts
|
|
4865
5684
|
init_performance2();
|
|
4866
5685
|
var PLATFORM_DISABLED_KEY = "__void/platform/disabled/v1";
|
|
4867
5686
|
var PLATFORM_DISABLED_VALUE = "disabled";
|
|
4868
5687
|
|
|
4869
|
-
//
|
|
5688
|
+
// ../../home/runner/work/void/void/platform/packages/proxy/src/r2.ts
|
|
4870
5689
|
init_performance2();
|
|
4871
5690
|
var CF_API3 = "https://api.cloudflare.com/client/v4";
|
|
4872
5691
|
var r2 = new Hono2();
|
|
@@ -5128,10 +5947,10 @@ r2.post("/head", async (context) => {
|
|
|
5128
5947
|
});
|
|
5129
5948
|
});
|
|
5130
5949
|
|
|
5131
|
-
//
|
|
5950
|
+
// ../../home/runner/work/void/void/platform/packages/proxy/src/revalidate.ts
|
|
5132
5951
|
init_performance2();
|
|
5133
|
-
var
|
|
5134
|
-
|
|
5952
|
+
var app3 = new Hono2();
|
|
5953
|
+
app3.post("/", async (context) => {
|
|
5135
5954
|
const projectId = context.get("projectId");
|
|
5136
5955
|
const body = context.get("body");
|
|
5137
5956
|
const response = await context.env.API.fetch(`https://api/internal/revalidate/${projectId}`, {
|
|
@@ -5151,9 +5970,9 @@ app2.post("/", async (context) => {
|
|
|
5151
5970
|
});
|
|
5152
5971
|
});
|
|
5153
5972
|
|
|
5154
|
-
//
|
|
5155
|
-
var
|
|
5156
|
-
|
|
5973
|
+
// ../../home/runner/work/void/void/platform/packages/proxy/src/index.ts
|
|
5974
|
+
var app4 = new Hono2();
|
|
5975
|
+
app4.use("/*", async (context, next) => {
|
|
5157
5976
|
const healthProbeToken = context.env.PLATFORM_HEALTH_PROBE_TOKEN;
|
|
5158
5977
|
const installerProbe = healthProbeToken !== void 0 && healthProbeToken !== "" && context.req.path === "/health" && context.req.header("X-Void-Platform-Probe") === healthProbeToken;
|
|
5159
5978
|
if (!installerProbe && (context.env.PLATFORM_TRAFFIC_STATE === "staging" || context.env.PLATFORM_TRAFFIC_STATE === "sandbox-drain" || context.env.PLATFORM_INSTALLATION_ID && await context.env.ROUTING.get(PLATFORM_DISABLED_KEY) === PLATFORM_DISABLED_VALUE)) {
|
|
@@ -5161,7 +5980,7 @@ app3.use("/*", async (context, next) => {
|
|
|
5161
5980
|
}
|
|
5162
5981
|
await next();
|
|
5163
5982
|
});
|
|
5164
|
-
|
|
5983
|
+
app4.use(
|
|
5165
5984
|
"/*",
|
|
5166
5985
|
cors({
|
|
5167
5986
|
origin: "*",
|
|
@@ -5169,7 +5988,7 @@ app3.use(
|
|
|
5169
5988
|
allowHeaders: ["content-type", "authorization"]
|
|
5170
5989
|
})
|
|
5171
5990
|
);
|
|
5172
|
-
|
|
5991
|
+
app4.get(
|
|
5173
5992
|
"/health",
|
|
5174
5993
|
(context) => context.json({
|
|
5175
5994
|
ok: true,
|
|
@@ -5183,7 +6002,7 @@ app3.get(
|
|
|
5183
6002
|
}
|
|
5184
6003
|
})
|
|
5185
6004
|
);
|
|
5186
|
-
|
|
6005
|
+
app4.all("/*", async (context, next) => {
|
|
5187
6006
|
if (context.req.method !== "POST") {
|
|
5188
6007
|
return context.json({ error: "method not allowed" }, 405);
|
|
5189
6008
|
}
|
|
@@ -5193,7 +6012,53 @@ var PLATFORM_INTERNAL_EMAIL_SENTINEL = "void-platform-internal-email-v1";
|
|
|
5193
6012
|
var MAX_PLATFORM_MIME_BASE64_LENGTH = Math.ceil(10 * 1024 * 1024 * 4 / 3) + 4;
|
|
5194
6013
|
var PLATFORM_TOKEN_MAX_SKEW_SECONDS = 5 * 60;
|
|
5195
6014
|
var PLATFORM_TOKEN_LENGTH = 43;
|
|
5196
|
-
|
|
6015
|
+
app4.post("/*", async (context, next) => {
|
|
6016
|
+
const path = new URL(context.req.url).pathname;
|
|
6017
|
+
if (path === "/email/relay") {
|
|
6018
|
+
const relayDomain = context.req.header("X-Void-Email-Domain");
|
|
6019
|
+
const relayFrom = context.req.header("X-Void-Email-From");
|
|
6020
|
+
const relayTo = context.req.header("X-Void-Email-To");
|
|
6021
|
+
const relayAuth = context.req.header("Authorization");
|
|
6022
|
+
if (!relayDomain || !relayFrom || !relayTo || !relayAuth) {
|
|
6023
|
+
return context.json({ error: "relay envelope required" }, 400);
|
|
6024
|
+
}
|
|
6025
|
+
if (relayDomain.length > 256 || relayFrom.length > 256 || relayTo.length > 256 || relayAuth.length > 256) {
|
|
6026
|
+
return context.json({ error: "relay envelope required" }, 400);
|
|
6027
|
+
}
|
|
6028
|
+
const contentLengthRaw = context.req.header("content-length");
|
|
6029
|
+
if (!contentLengthRaw) {
|
|
6030
|
+
return context.json({ error: "content-length required" }, 400);
|
|
6031
|
+
}
|
|
6032
|
+
const contentLength = Number(contentLengthRaw);
|
|
6033
|
+
if (!Number.isInteger(contentLength) || contentLength < 0) {
|
|
6034
|
+
return context.json({ error: "content-length required" }, 400);
|
|
6035
|
+
}
|
|
6036
|
+
if (contentLength > MAX_MIME_BYTES) {
|
|
6037
|
+
return context.json({ error: "mime too large" }, 413);
|
|
6038
|
+
}
|
|
6039
|
+
const relaySecret = relayAuth.startsWith("Bearer ") ? relayAuth.slice(7) : null;
|
|
6040
|
+
if (!relaySecret) {
|
|
6041
|
+
return context.json({ error: "invalid relay credentials" }, 403);
|
|
6042
|
+
}
|
|
6043
|
+
const recipientDomain = relayTo.slice(relayTo.lastIndexOf("@") + 1).toLowerCase();
|
|
6044
|
+
if (recipientDomain.length === 0 || recipientDomain !== relayDomain.toLowerCase()) {
|
|
6045
|
+
return context.json({ error: "relay domain mismatch" }, 403);
|
|
6046
|
+
}
|
|
6047
|
+
const mirror = await readEmailDomainMirror(context.env.ROUTING, recipientDomain);
|
|
6048
|
+
if (!mirror) {
|
|
6049
|
+
return context.json({ error: "invalid relay credentials" }, 403);
|
|
6050
|
+
}
|
|
6051
|
+
const presented = await sha256Hex(relaySecret);
|
|
6052
|
+
if (!constantTimeEqual(presented, mirror.relaySecretSha256)) {
|
|
6053
|
+
return context.json({ error: "invalid relay credentials" }, 403);
|
|
6054
|
+
}
|
|
6055
|
+
if (mirror.status !== "active") {
|
|
6056
|
+
return context.json({ error: "email domain is not active" }, 403);
|
|
6057
|
+
}
|
|
6058
|
+
context.set("projectId", mirror.projectId);
|
|
6059
|
+
await next();
|
|
6060
|
+
return;
|
|
6061
|
+
}
|
|
5197
6062
|
let body;
|
|
5198
6063
|
try {
|
|
5199
6064
|
const json = await context.req.json();
|
|
@@ -5205,7 +6070,6 @@ app3.post("/*", async (context, next) => {
|
|
|
5205
6070
|
return context.json({ error: "invalid JSON body" }, 400);
|
|
5206
6071
|
}
|
|
5207
6072
|
context.set("body", body);
|
|
5208
|
-
const path = new URL(context.req.url).pathname;
|
|
5209
6073
|
if (path.startsWith("/email/internal/")) {
|
|
5210
6074
|
const platformToken = asString(body.platformToken);
|
|
5211
6075
|
const platformTimestamp = asNumber(body.platformTimestamp);
|
|
@@ -5287,13 +6151,13 @@ app3.post("/*", async (context, next) => {
|
|
|
5287
6151
|
}
|
|
5288
6152
|
await next();
|
|
5289
6153
|
});
|
|
5290
|
-
|
|
6154
|
+
app4.post("/ai/*", async (context, next) => {
|
|
5291
6155
|
if (await context.env.ROUTING.get(`ai-block:${context.get("projectId")}`, { cacheTtl: 60 })) {
|
|
5292
6156
|
return context.json({ error: "AI usage limit exceeded" }, 429);
|
|
5293
6157
|
}
|
|
5294
6158
|
await next();
|
|
5295
6159
|
});
|
|
5296
|
-
|
|
6160
|
+
app4.post(
|
|
5297
6161
|
"/sandbox/*",
|
|
5298
6162
|
(context) => context.json(
|
|
5299
6163
|
{
|
|
@@ -5304,7 +6168,7 @@ app3.post(
|
|
|
5304
6168
|
501
|
|
5305
6169
|
)
|
|
5306
6170
|
);
|
|
5307
|
-
|
|
6171
|
+
app4.post("/email/*", async (context, next) => {
|
|
5308
6172
|
if (!proxyCapabilityEnabled(context.env.PLATFORM_CAPABILITIES, "email")) {
|
|
5309
6173
|
return context.json(
|
|
5310
6174
|
{
|
|
@@ -5317,14 +6181,15 @@ app3.post("/email/*", async (context, next) => {
|
|
|
5317
6181
|
}
|
|
5318
6182
|
await next();
|
|
5319
6183
|
});
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
6184
|
+
app4.route("/ai", ai);
|
|
6185
|
+
app4.route("/d1", d1);
|
|
6186
|
+
app4.route("/kv", kv);
|
|
6187
|
+
app4.route("/r2", r2);
|
|
6188
|
+
app4.route("/revalidate", app3);
|
|
6189
|
+
app4.route("/email", app);
|
|
6190
|
+
app4.route("/email", app2);
|
|
5326
6191
|
var index_default = {
|
|
5327
|
-
fetch:
|
|
6192
|
+
fetch: app4.fetch,
|
|
5328
6193
|
/**
|
|
5329
6194
|
* Inbound email entry point — bound as the catch-all CF Email Routing rule
|
|
5330
6195
|
* on the platform mail zone. See `email-router.ts`.
|
|
@@ -5333,27 +6198,27 @@ var index_default = {
|
|
|
5333
6198
|
await routeInbound(message, env, ctx);
|
|
5334
6199
|
}
|
|
5335
6200
|
};
|
|
5336
|
-
var
|
|
6201
|
+
var encoder4 = new TextEncoder();
|
|
5337
6202
|
var decoder = new TextDecoder();
|
|
5338
6203
|
var cachedVerifyKey;
|
|
5339
|
-
var
|
|
6204
|
+
var cachedKeySecret4;
|
|
5340
6205
|
async function getVerifyKey(secret) {
|
|
5341
|
-
if (cachedVerifyKey &&
|
|
6206
|
+
if (cachedVerifyKey && cachedKeySecret4 === secret) {
|
|
5342
6207
|
return cachedVerifyKey;
|
|
5343
6208
|
}
|
|
5344
6209
|
cachedVerifyKey = await crypto.subtle.importKey(
|
|
5345
6210
|
"raw",
|
|
5346
|
-
|
|
6211
|
+
encoder4.encode(secret),
|
|
5347
6212
|
{ name: "HMAC", hash: "SHA-256" },
|
|
5348
6213
|
false,
|
|
5349
6214
|
["verify"]
|
|
5350
6215
|
);
|
|
5351
|
-
|
|
6216
|
+
cachedKeySecret4 = secret;
|
|
5352
6217
|
return cachedVerifyKey;
|
|
5353
6218
|
}
|
|
5354
6219
|
__name(getVerifyKey, "getVerifyKey");
|
|
5355
6220
|
async function sha256Hex(input) {
|
|
5356
|
-
const digest = await crypto.subtle.digest("SHA-256",
|
|
6221
|
+
const digest = await crypto.subtle.digest("SHA-256", encoder4.encode(input));
|
|
5357
6222
|
const bytes = new Uint8Array(digest);
|
|
5358
6223
|
let out = "";
|
|
5359
6224
|
for (const b of bytes) {
|
|
@@ -5362,11 +6227,22 @@ async function sha256Hex(input) {
|
|
|
5362
6227
|
return out;
|
|
5363
6228
|
}
|
|
5364
6229
|
__name(sha256Hex, "sha256Hex");
|
|
6230
|
+
function constantTimeEqual(a, b) {
|
|
6231
|
+
if (a.length !== b.length) {
|
|
6232
|
+
return false;
|
|
6233
|
+
}
|
|
6234
|
+
let diff = 0;
|
|
6235
|
+
for (let i = 0; i < a.length; i++) {
|
|
6236
|
+
diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
6237
|
+
}
|
|
6238
|
+
return diff === 0;
|
|
6239
|
+
}
|
|
6240
|
+
__name(constantTimeEqual, "constantTimeEqual");
|
|
5365
6241
|
async function verifyHmac(data, signature, secret) {
|
|
5366
6242
|
try {
|
|
5367
6243
|
const key = await getVerifyKey(secret);
|
|
5368
6244
|
const sigBytes = base64urlDecode(signature);
|
|
5369
|
-
return crypto.subtle.verify("HMAC", key, sigBytes.buffer,
|
|
6245
|
+
return crypto.subtle.verify("HMAC", key, sigBytes.buffer, encoder4.encode(data));
|
|
5370
6246
|
} catch {
|
|
5371
6247
|
return false;
|
|
5372
6248
|
}
|
|
@@ -5391,7 +6267,7 @@ async function verifyJwt(token, secret) {
|
|
|
5391
6267
|
"HMAC",
|
|
5392
6268
|
key,
|
|
5393
6269
|
sigBytes.buffer,
|
|
5394
|
-
|
|
6270
|
+
encoder4.encode(`${header}.${body}`)
|
|
5395
6271
|
);
|
|
5396
6272
|
if (!valid) {
|
|
5397
6273
|
return null;
|