h3 1.3.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -10
- package/dist/index.cjs +277 -17
- package/dist/index.d.ts +33 -2
- package/dist/index.mjs +272 -19
- package/package.json +12 -10
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[](https://npmjs.com/package/h3)
|
|
2
2
|
[](https://npmjs.com/package/h3)
|
|
3
3
|
[](https://bundlephobia.com/result?p=h3)
|
|
4
|
-
[](https://github.com/unjs/h3/actions)
|
|
5
5
|
[](https://codecov.io/gh/unjs/h3)
|
|
6
6
|
[](https://www.jsdocs.io/package/h3)
|
|
7
7
|
|
|
@@ -110,6 +110,19 @@ app.use(eventHandler(() => '<h1>Hello world!</h1>'))
|
|
|
110
110
|
app.use('/1', eventHandler(() => '<h1>Hello world!</h1>'))
|
|
111
111
|
.use('/2', eventHandler(() => '<h1>Goodbye!</h1>'))
|
|
112
112
|
|
|
113
|
+
// We can proxy requests and rewrite cookie's domain and path
|
|
114
|
+
app.use('/api', eventHandler((event) => proxyRequest('https://example.com', {
|
|
115
|
+
// f.e. keep one domain unchanged, rewrite one domain and remove other domains
|
|
116
|
+
cookieDomainRewrite: {
|
|
117
|
+
"example.com": "example.com",
|
|
118
|
+
"example.com": "somecompany.co.uk",
|
|
119
|
+
"*": "",
|
|
120
|
+
},
|
|
121
|
+
cookiePathRewrite: {
|
|
122
|
+
"/": "/api"
|
|
123
|
+
},
|
|
124
|
+
}))
|
|
125
|
+
|
|
113
126
|
// Legacy middleware with 3rd argument are automatically promisified
|
|
114
127
|
app.use(fromNodeMiddleware((req, res, next) => { req.setHeader('x-foo', 'bar'); next() }))
|
|
115
128
|
|
|
@@ -146,8 +159,8 @@ H3 has a concept of composable utilities that accept `event` (from `eventHandler
|
|
|
146
159
|
- `isMethod(event, expected, allowHead?)`
|
|
147
160
|
- `assertMethod(event, expected, allowHead?)`
|
|
148
161
|
- `createError({ statusCode, statusMessage, data? })`
|
|
149
|
-
- `sendProxy(event, { target,
|
|
150
|
-
- `proxyRequest(event, { target,
|
|
162
|
+
- `sendProxy(event, { target, ...options })`
|
|
163
|
+
- `proxyRequest(event, { target, ...options })`
|
|
151
164
|
- `fetchWithEvent(event, req, init, { fetch? }?)`
|
|
152
165
|
- `getProxyRequestHeaders(event)`
|
|
153
166
|
- `sendNoContent(event, code = 204)`
|
|
@@ -155,10 +168,17 @@ H3 has a concept of composable utilities that accept `event` (from `eventHandler
|
|
|
155
168
|
- `getResponseStatus(event)`
|
|
156
169
|
- `getResponseStatusText(event)`
|
|
157
170
|
- `readMultipartFormData(event)`
|
|
158
|
-
- `useSession(event, { password, name?, cookie?, seal?, crypto? })`
|
|
159
|
-
- `getSession(event,
|
|
160
|
-
- `updateSession(event,
|
|
161
|
-
- `clearSession(event,
|
|
171
|
+
- `useSession(event, config = { password, maxAge?, name?, cookie?, seal?, crypto? })`
|
|
172
|
+
- `getSession(event, config)`
|
|
173
|
+
- `updateSession(event, config, update)`
|
|
174
|
+
- `clearSession(event, config)`
|
|
175
|
+
- `sealSession(event, config)`
|
|
176
|
+
- `unsealSession(event, config, sealed)`
|
|
177
|
+
- `handleCors(options)` (see [h3-cors](https://github.com/NozomuIkuta/h3-cors) for more detail about options)
|
|
178
|
+
- `isPreflightRequest(event)`
|
|
179
|
+
- `isCorsOriginAllowed(event)`
|
|
180
|
+
- `appendCorsHeaders(event, options)` (see [h3-cors](https://github.com/NozomuIkuta/h3-cors) for more detail about options)
|
|
181
|
+
- `appendCorsPreflightHeaders(event, options)` (see [h3-cors](https://github.com/NozomuIkuta/h3-cors) for more detail about options)
|
|
162
182
|
|
|
163
183
|
👉 You can learn more about usage in [JSDocs Documentation](https://www.jsdocs.io/package/h3#package-functions).
|
|
164
184
|
|
|
@@ -170,9 +190,6 @@ Please check their READMEs for more details.
|
|
|
170
190
|
|
|
171
191
|
PRs are welcome to add your packages.
|
|
172
192
|
|
|
173
|
-
- [h3-cors](https://github.com/NozomuIkuta/h3-cors)
|
|
174
|
-
- `defineCorsEventHandler(options)`
|
|
175
|
-
- `isPreflight(event)`
|
|
176
193
|
- [h3-typebox](https://github.com/kevinmarrec/h3-typebox)
|
|
177
194
|
- `validateBody(event, schema)`
|
|
178
195
|
- `validateQuery(event, schema)`
|
package/dist/index.cjs
CHANGED
|
@@ -6,6 +6,7 @@ const destr = require('destr');
|
|
|
6
6
|
const cookieEs = require('cookie-es');
|
|
7
7
|
const crypto = require('uncrypto');
|
|
8
8
|
const ironWebcrypto = require('iron-webcrypto');
|
|
9
|
+
const defu = require('defu');
|
|
9
10
|
|
|
10
11
|
function useBase(base, handler) {
|
|
11
12
|
base = ufo.withoutTrailingSlash(base);
|
|
@@ -380,6 +381,59 @@ function deleteCookie(event, name, serializeOptions) {
|
|
|
380
381
|
});
|
|
381
382
|
}
|
|
382
383
|
|
|
384
|
+
function splitCookiesString(cookiesString) {
|
|
385
|
+
if (typeof cookiesString !== "string") {
|
|
386
|
+
return [];
|
|
387
|
+
}
|
|
388
|
+
const cookiesStrings = [];
|
|
389
|
+
let pos = 0;
|
|
390
|
+
let start;
|
|
391
|
+
let ch;
|
|
392
|
+
let lastComma;
|
|
393
|
+
let nextStart;
|
|
394
|
+
let cookiesSeparatorFound;
|
|
395
|
+
function skipWhitespace() {
|
|
396
|
+
while (pos < cookiesString.length && /\s/.test(cookiesString.charAt(pos))) {
|
|
397
|
+
pos += 1;
|
|
398
|
+
}
|
|
399
|
+
return pos < cookiesString.length;
|
|
400
|
+
}
|
|
401
|
+
function notSpecialChar() {
|
|
402
|
+
ch = cookiesString.charAt(pos);
|
|
403
|
+
return ch !== "=" && ch !== ";" && ch !== ",";
|
|
404
|
+
}
|
|
405
|
+
while (pos < cookiesString.length) {
|
|
406
|
+
start = pos;
|
|
407
|
+
cookiesSeparatorFound = false;
|
|
408
|
+
while (skipWhitespace()) {
|
|
409
|
+
ch = cookiesString.charAt(pos);
|
|
410
|
+
if (ch === ",") {
|
|
411
|
+
lastComma = pos;
|
|
412
|
+
pos += 1;
|
|
413
|
+
skipWhitespace();
|
|
414
|
+
nextStart = pos;
|
|
415
|
+
while (pos < cookiesString.length && notSpecialChar()) {
|
|
416
|
+
pos += 1;
|
|
417
|
+
}
|
|
418
|
+
if (pos < cookiesString.length && cookiesString.charAt(pos) === "=") {
|
|
419
|
+
cookiesSeparatorFound = true;
|
|
420
|
+
pos = nextStart;
|
|
421
|
+
cookiesStrings.push(cookiesString.slice(start, lastComma));
|
|
422
|
+
start = pos;
|
|
423
|
+
} else {
|
|
424
|
+
pos = lastComma + 1;
|
|
425
|
+
}
|
|
426
|
+
} else {
|
|
427
|
+
pos += 1;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
if (!cookiesSeparatorFound || pos >= cookiesString.length) {
|
|
431
|
+
cookiesStrings.push(cookiesString.slice(start, cookiesString.length));
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return cookiesStrings;
|
|
435
|
+
}
|
|
436
|
+
|
|
383
437
|
const PayloadMethods = /* @__PURE__ */ new Set(["PATCH", "POST", "PUT", "DELETE"]);
|
|
384
438
|
const ignoredHeaders = /* @__PURE__ */ new Set([
|
|
385
439
|
"transfer-encoding",
|
|
@@ -426,6 +480,27 @@ async function sendProxy(event, target, opts = {}) {
|
|
|
426
480
|
if (key === "content-length") {
|
|
427
481
|
continue;
|
|
428
482
|
}
|
|
483
|
+
if (key === "set-cookie") {
|
|
484
|
+
const cookies = splitCookiesString(value).map((cookie) => {
|
|
485
|
+
if (opts.cookieDomainRewrite) {
|
|
486
|
+
cookie = rewriteCookieProperty(
|
|
487
|
+
cookie,
|
|
488
|
+
opts.cookieDomainRewrite,
|
|
489
|
+
"domain"
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
if (opts.cookiePathRewrite) {
|
|
493
|
+
cookie = rewriteCookieProperty(
|
|
494
|
+
cookie,
|
|
495
|
+
opts.cookiePathRewrite,
|
|
496
|
+
"path"
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
return cookie;
|
|
500
|
+
});
|
|
501
|
+
event.node.res.setHeader("set-cookie", cookies);
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
429
504
|
event.node.res.setHeader(key, value);
|
|
430
505
|
}
|
|
431
506
|
if (response._data !== void 0) {
|
|
@@ -472,6 +547,23 @@ function _getFetch(_fetch) {
|
|
|
472
547
|
"fetch is not available. Try importing `node-fetch-native/polyfill` for Node.js."
|
|
473
548
|
);
|
|
474
549
|
}
|
|
550
|
+
function rewriteCookieProperty(header, map, property) {
|
|
551
|
+
const _map = typeof map === "string" ? { "*": map } : map;
|
|
552
|
+
return header.replace(
|
|
553
|
+
new RegExp(`(;\\s*${property}=)([^;]+)`, "gi"),
|
|
554
|
+
(match, prefix, previousValue) => {
|
|
555
|
+
let newValue;
|
|
556
|
+
if (previousValue in _map) {
|
|
557
|
+
newValue = _map[previousValue];
|
|
558
|
+
} else if ("*" in _map) {
|
|
559
|
+
newValue = _map["*"];
|
|
560
|
+
} else {
|
|
561
|
+
return match;
|
|
562
|
+
}
|
|
563
|
+
return newValue ? prefix + newValue : "";
|
|
564
|
+
}
|
|
565
|
+
);
|
|
566
|
+
}
|
|
475
567
|
|
|
476
568
|
const defer = typeof setImmediate !== "undefined" ? setImmediate : (fn) => fn();
|
|
477
569
|
function send(event, data, type) {
|
|
@@ -640,21 +732,35 @@ async function getSession(event, config) {
|
|
|
640
732
|
if (event.context.sessions[sessionName]) {
|
|
641
733
|
return event.context.sessions[sessionName];
|
|
642
734
|
}
|
|
643
|
-
const session = {
|
|
735
|
+
const session = {
|
|
736
|
+
id: "",
|
|
737
|
+
createdAt: 0,
|
|
738
|
+
data: /* @__PURE__ */ Object.create(null)
|
|
739
|
+
};
|
|
644
740
|
event.context.sessions[sessionName] = session;
|
|
645
|
-
|
|
646
|
-
if (
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
741
|
+
let sealedSession;
|
|
742
|
+
if (config.sessionHeader !== false) {
|
|
743
|
+
const headerName = typeof config.sessionHeader === "string" ? config.sessionHeader.toLowerCase() : `x-${sessionName.toLowerCase()}-session`;
|
|
744
|
+
const headerValue = event.node.req.headers[headerName];
|
|
745
|
+
if (typeof headerValue === "string") {
|
|
746
|
+
sealedSession = headerValue;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
if (!sealedSession) {
|
|
750
|
+
sealedSession = getCookie(event, sessionName);
|
|
751
|
+
}
|
|
752
|
+
if (sealedSession) {
|
|
753
|
+
const unsealed = await unsealSession(event, config, sealedSession).catch(
|
|
754
|
+
() => {
|
|
755
|
+
}
|
|
655
756
|
);
|
|
656
757
|
Object.assign(session, unsealed);
|
|
657
758
|
}
|
|
759
|
+
if (!session.id) {
|
|
760
|
+
session.id = (config.crypto || crypto).randomUUID();
|
|
761
|
+
session.createdAt = Date.now();
|
|
762
|
+
await updateSession(event, config);
|
|
763
|
+
}
|
|
658
764
|
return session;
|
|
659
765
|
}
|
|
660
766
|
async function updateSession(event, config, update) {
|
|
@@ -666,21 +772,168 @@ async function updateSession(event, config, update) {
|
|
|
666
772
|
if (update) {
|
|
667
773
|
Object.assign(session.data, update);
|
|
668
774
|
}
|
|
669
|
-
|
|
775
|
+
if (config.cookie !== false) {
|
|
776
|
+
const sealed = await sealSession(event, config);
|
|
777
|
+
setCookie(event, sessionName, sealed, {
|
|
778
|
+
...DEFAULT_COOKIE,
|
|
779
|
+
expires: config.maxAge ? new Date(session.createdAt + config.maxAge * 1e3) : void 0,
|
|
780
|
+
...config.cookie
|
|
781
|
+
});
|
|
782
|
+
}
|
|
783
|
+
return session;
|
|
784
|
+
}
|
|
785
|
+
async function sealSession(event, config) {
|
|
786
|
+
const sessionName = config.name || DEFAULT_NAME;
|
|
787
|
+
const session = event.context.sessions?.[sessionName] || await getSession(event, config);
|
|
788
|
+
const sealed = await ironWebcrypto.seal(config.crypto || crypto, session, config.password, {
|
|
789
|
+
...ironWebcrypto.defaults,
|
|
790
|
+
ttl: config.maxAge ? config.maxAge * 1e3 : 0,
|
|
791
|
+
...config.seal
|
|
792
|
+
});
|
|
793
|
+
return sealed;
|
|
794
|
+
}
|
|
795
|
+
async function unsealSession(_event, config, sealed) {
|
|
796
|
+
const unsealed = await ironWebcrypto.unseal(
|
|
670
797
|
config.crypto || crypto,
|
|
671
|
-
|
|
798
|
+
sealed,
|
|
672
799
|
config.password,
|
|
673
|
-
|
|
800
|
+
{
|
|
801
|
+
...ironWebcrypto.defaults,
|
|
802
|
+
ttl: config.maxAge ? config.maxAge * 1e3 : 0,
|
|
803
|
+
...config.seal
|
|
804
|
+
}
|
|
674
805
|
);
|
|
675
|
-
|
|
676
|
-
|
|
806
|
+
if (config.maxAge) {
|
|
807
|
+
const age = Date.now() - (unsealed.createdAt || Number.NEGATIVE_INFINITY);
|
|
808
|
+
if (age > config.maxAge * 1e3) {
|
|
809
|
+
throw new Error("Session expired!");
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
return unsealed;
|
|
677
813
|
}
|
|
678
814
|
async function clearSession(event, config) {
|
|
679
815
|
const sessionName = config.name || DEFAULT_NAME;
|
|
680
816
|
if (event.context.sessions?.[sessionName]) {
|
|
681
817
|
delete event.context.sessions[sessionName];
|
|
682
818
|
}
|
|
683
|
-
await setCookie(event, sessionName, "",
|
|
819
|
+
await setCookie(event, sessionName, "", {
|
|
820
|
+
...DEFAULT_COOKIE,
|
|
821
|
+
...config.cookie
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
function resolveCorsOptions(options = {}) {
|
|
826
|
+
const defaultOptions = {
|
|
827
|
+
origin: "*",
|
|
828
|
+
methods: "*",
|
|
829
|
+
allowHeaders: "*",
|
|
830
|
+
exposeHeaders: "*",
|
|
831
|
+
credentials: false,
|
|
832
|
+
maxAge: false,
|
|
833
|
+
preflight: {
|
|
834
|
+
statusCode: 204
|
|
835
|
+
}
|
|
836
|
+
};
|
|
837
|
+
return defu.defu(options, defaultOptions);
|
|
838
|
+
}
|
|
839
|
+
function isPreflightRequest(event) {
|
|
840
|
+
const method = getMethod(event);
|
|
841
|
+
const origin = getRequestHeader(event, "origin");
|
|
842
|
+
const accessControlRequestMethod = getRequestHeader(
|
|
843
|
+
event,
|
|
844
|
+
"access-control-request-method"
|
|
845
|
+
);
|
|
846
|
+
return method === "OPTIONS" && !!origin && !!accessControlRequestMethod;
|
|
847
|
+
}
|
|
848
|
+
function isCorsOriginAllowed(origin, options) {
|
|
849
|
+
const { origin: originOption } = options;
|
|
850
|
+
if (!origin || !originOption || originOption === "*" || originOption === "null") {
|
|
851
|
+
return true;
|
|
852
|
+
}
|
|
853
|
+
if (Array.isArray(originOption)) {
|
|
854
|
+
return originOption.some((_origin) => {
|
|
855
|
+
if (_origin instanceof RegExp) {
|
|
856
|
+
return _origin.test(origin);
|
|
857
|
+
}
|
|
858
|
+
return origin === _origin;
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
return originOption(origin);
|
|
862
|
+
}
|
|
863
|
+
function createOriginHeaders(event, options) {
|
|
864
|
+
const { origin: originOption } = options;
|
|
865
|
+
const origin = getRequestHeader(event, "origin");
|
|
866
|
+
if (!origin || !originOption || originOption === "*") {
|
|
867
|
+
return { "access-control-allow-origin": "*" };
|
|
868
|
+
}
|
|
869
|
+
if (typeof originOption === "string") {
|
|
870
|
+
return { "access-control-allow-origin": originOption, vary: "origin" };
|
|
871
|
+
}
|
|
872
|
+
return isCorsOriginAllowed(origin, options) ? { "access-control-allow-origin": origin, vary: "origin" } : {};
|
|
873
|
+
}
|
|
874
|
+
function createMethodsHeaders(options) {
|
|
875
|
+
const { methods } = options;
|
|
876
|
+
if (!methods) {
|
|
877
|
+
return {};
|
|
878
|
+
}
|
|
879
|
+
if (methods === "*") {
|
|
880
|
+
return { "access-control-allow-methods": "*" };
|
|
881
|
+
}
|
|
882
|
+
return methods.length > 0 ? { "access-control-allow-methods": methods.join(",") } : {};
|
|
883
|
+
}
|
|
884
|
+
function createCredentialsHeaders(options) {
|
|
885
|
+
const { credentials } = options;
|
|
886
|
+
if (credentials) {
|
|
887
|
+
return { "access-control-allow-credentials": "true" };
|
|
888
|
+
}
|
|
889
|
+
return {};
|
|
890
|
+
}
|
|
891
|
+
function createAllowHeaderHeaders(event, options) {
|
|
892
|
+
const { allowHeaders } = options;
|
|
893
|
+
if (!allowHeaders || allowHeaders === "*" || allowHeaders.length === 0) {
|
|
894
|
+
const header = getRequestHeader(event, "access-control-request-headers");
|
|
895
|
+
return header ? {
|
|
896
|
+
"access-control-allow-headers": header,
|
|
897
|
+
vary: "access-control-request-headers"
|
|
898
|
+
} : {};
|
|
899
|
+
}
|
|
900
|
+
return {
|
|
901
|
+
"access-control-allow-headers": allowHeaders.join(","),
|
|
902
|
+
vary: "access-control-request-headers"
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
function createExposeHeaders(options) {
|
|
906
|
+
const { exposeHeaders } = options;
|
|
907
|
+
if (!exposeHeaders) {
|
|
908
|
+
return {};
|
|
909
|
+
}
|
|
910
|
+
if (exposeHeaders === "*") {
|
|
911
|
+
return { "access-control-expose-headers": exposeHeaders };
|
|
912
|
+
}
|
|
913
|
+
return { "access-control-expose-headers": exposeHeaders.join(",") };
|
|
914
|
+
}
|
|
915
|
+
function appendCorsPreflightHeaders(event, options) {
|
|
916
|
+
appendHeaders(event, createOriginHeaders(event, options));
|
|
917
|
+
appendHeaders(event, createCredentialsHeaders(options));
|
|
918
|
+
appendHeaders(event, createExposeHeaders(options));
|
|
919
|
+
appendHeaders(event, createMethodsHeaders(options));
|
|
920
|
+
appendHeaders(event, createAllowHeaderHeaders(event, options));
|
|
921
|
+
}
|
|
922
|
+
function appendCorsHeaders(event, options) {
|
|
923
|
+
appendHeaders(event, createOriginHeaders(event, options));
|
|
924
|
+
appendHeaders(event, createCredentialsHeaders(options));
|
|
925
|
+
appendHeaders(event, createExposeHeaders(options));
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
function handleCors(event, options) {
|
|
929
|
+
const _options = resolveCorsOptions(options);
|
|
930
|
+
if (isPreflightRequest(event)) {
|
|
931
|
+
appendCorsPreflightHeaders(event, options);
|
|
932
|
+
sendNoContent(event, _options.preflight.statusCode);
|
|
933
|
+
return true;
|
|
934
|
+
}
|
|
935
|
+
appendCorsHeaders(event, options);
|
|
936
|
+
return false;
|
|
684
937
|
}
|
|
685
938
|
|
|
686
939
|
class H3Headers {
|
|
@@ -1137,6 +1390,8 @@ exports.H3Event = H3Event;
|
|
|
1137
1390
|
exports.H3Headers = H3Headers;
|
|
1138
1391
|
exports.H3Response = H3Response;
|
|
1139
1392
|
exports.MIMES = MIMES;
|
|
1393
|
+
exports.appendCorsHeaders = appendCorsHeaders;
|
|
1394
|
+
exports.appendCorsPreflightHeaders = appendCorsPreflightHeaders;
|
|
1140
1395
|
exports.appendHeader = appendHeader;
|
|
1141
1396
|
exports.appendHeaders = appendHeaders;
|
|
1142
1397
|
exports.appendResponseHeader = appendResponseHeader;
|
|
@@ -1175,10 +1430,13 @@ exports.getRouterParam = getRouterParam;
|
|
|
1175
1430
|
exports.getRouterParams = getRouterParams;
|
|
1176
1431
|
exports.getSession = getSession;
|
|
1177
1432
|
exports.handleCacheHeaders = handleCacheHeaders;
|
|
1433
|
+
exports.handleCors = handleCors;
|
|
1434
|
+
exports.isCorsOriginAllowed = isCorsOriginAllowed;
|
|
1178
1435
|
exports.isError = isError;
|
|
1179
1436
|
exports.isEvent = isEvent;
|
|
1180
1437
|
exports.isEventHandler = isEventHandler;
|
|
1181
1438
|
exports.isMethod = isMethod;
|
|
1439
|
+
exports.isPreflightRequest = isPreflightRequest;
|
|
1182
1440
|
exports.isStream = isStream;
|
|
1183
1441
|
exports.lazyEventHandler = lazyEventHandler;
|
|
1184
1442
|
exports.parseCookies = parseCookies;
|
|
@@ -1187,6 +1445,7 @@ exports.proxyRequest = proxyRequest;
|
|
|
1187
1445
|
exports.readBody = readBody;
|
|
1188
1446
|
exports.readMultipartFormData = readMultipartFormData;
|
|
1189
1447
|
exports.readRawBody = readRawBody;
|
|
1448
|
+
exports.sealSession = sealSession;
|
|
1190
1449
|
exports.send = send;
|
|
1191
1450
|
exports.sendError = sendError;
|
|
1192
1451
|
exports.sendNoContent = sendNoContent;
|
|
@@ -1201,6 +1460,7 @@ exports.setResponseHeaders = setResponseHeaders;
|
|
|
1201
1460
|
exports.setResponseStatus = setResponseStatus;
|
|
1202
1461
|
exports.toEventHandler = toEventHandler;
|
|
1203
1462
|
exports.toNodeListener = toNodeListener;
|
|
1463
|
+
exports.unsealSession = unsealSession;
|
|
1204
1464
|
exports.updateSession = updateSession;
|
|
1205
1465
|
exports.use = use;
|
|
1206
1466
|
exports.useBase = useBase;
|
package/dist/index.d.ts
CHANGED
|
@@ -8,12 +8,20 @@ type SessionDataT = Record<string, any>;
|
|
|
8
8
|
type SessionData<T extends SessionDataT = SessionDataT> = T;
|
|
9
9
|
interface Session<T extends SessionDataT = SessionDataT> {
|
|
10
10
|
id: string;
|
|
11
|
+
createdAt: number;
|
|
11
12
|
data: SessionData<T>;
|
|
12
13
|
}
|
|
13
14
|
interface SessionConfig {
|
|
15
|
+
/** Private key used to encrypt session tokens */
|
|
14
16
|
password: string;
|
|
17
|
+
/** Session expiration time in seconds */
|
|
18
|
+
maxAge?: number;
|
|
19
|
+
/** default is h3 */
|
|
15
20
|
name?: string;
|
|
16
|
-
|
|
21
|
+
/** Default is secure, httpOnly, / */
|
|
22
|
+
cookie?: false | CookieSerializeOptions;
|
|
23
|
+
/** Default is x-h3-session / x-{name}-session */
|
|
24
|
+
sessionHeader?: false | string;
|
|
17
25
|
seal?: SealOptions;
|
|
18
26
|
crypto?: Crypto;
|
|
19
27
|
}
|
|
@@ -26,6 +34,8 @@ declare function useSession<T extends SessionDataT = SessionDataT>(event: H3Even
|
|
|
26
34
|
declare function getSession<T extends SessionDataT = SessionDataT>(event: H3Event, config: SessionConfig): Promise<Session<T>>;
|
|
27
35
|
type SessionUpdate<T extends SessionDataT = SessionDataT> = Partial<SessionData<T>> | ((oldData: SessionData<T>) => Partial<SessionData<T>> | undefined);
|
|
28
36
|
declare function updateSession<T extends SessionDataT = SessionDataT>(event: H3Event, config: SessionConfig, update?: SessionUpdate<T>): Promise<Session<T>>;
|
|
37
|
+
declare function sealSession<T extends SessionDataT = SessionDataT>(event: H3Event, config: SessionConfig): Promise<string>;
|
|
38
|
+
declare function unsealSession(_event: H3Event, config: SessionConfig, sealed: string): Promise<Partial<Session<SessionDataT>>>;
|
|
29
39
|
declare function clearSession(event: H3Event, config: SessionConfig): Promise<void>;
|
|
30
40
|
|
|
31
41
|
type HTTPMethod = "GET" | "HEAD" | "PATCH" | "POST" | "PUT" | "DELETE" | "CONNECT" | "OPTIONS" | "TRACE";
|
|
@@ -291,6 +301,8 @@ interface ProxyOptions {
|
|
|
291
301
|
fetchOptions?: RequestInit;
|
|
292
302
|
fetch?: typeof fetch;
|
|
293
303
|
sendStream?: boolean;
|
|
304
|
+
cookieDomainRewrite?: string | Record<string, string>;
|
|
305
|
+
cookiePathRewrite?: string | Record<string, string>;
|
|
294
306
|
}
|
|
295
307
|
declare function proxyRequest(event: H3Event, target: string, opts?: ProxyOptions): Promise<any>;
|
|
296
308
|
declare function sendProxy(event: H3Event, target: string, opts?: ProxyOptions): Promise<any>;
|
|
@@ -338,6 +350,25 @@ declare function isStream(data: any): any;
|
|
|
338
350
|
declare function sendStream(event: H3Event, data: any): Promise<void>;
|
|
339
351
|
declare function writeEarlyHints(event: H3Event, hints: string | string[] | Record<string, string | string[]>, cb?: () => void): void;
|
|
340
352
|
|
|
353
|
+
interface H3CorsOptions {
|
|
354
|
+
origin?: "*" | "null" | (string | RegExp)[] | ((origin: string) => boolean);
|
|
355
|
+
methods?: "*" | HTTPMethod[];
|
|
356
|
+
allowHeaders?: "*" | string[];
|
|
357
|
+
exposeHeaders?: "*" | string[];
|
|
358
|
+
credentials?: boolean;
|
|
359
|
+
maxAge?: string | false;
|
|
360
|
+
preflight?: {
|
|
361
|
+
statusCode?: number;
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
declare function handleCors(event: H3Event, options: H3CorsOptions): boolean;
|
|
366
|
+
|
|
367
|
+
declare function isPreflightRequest(event: H3Event): boolean;
|
|
368
|
+
declare function isCorsOriginAllowed(origin: ReturnType<typeof getRequestHeaders>["origin"], options: H3CorsOptions): boolean;
|
|
369
|
+
declare function appendCorsPreflightHeaders(event: H3Event, options: H3CorsOptions): void;
|
|
370
|
+
declare function appendCorsHeaders(event: H3Event, options: H3CorsOptions): void;
|
|
371
|
+
|
|
341
372
|
type RouterMethod = Lowercase<HTTPMethod>;
|
|
342
373
|
type RouterUse = (path: string, handler: EventHandler, method?: RouterMethod | RouterMethod[]) => Router;
|
|
343
374
|
type AddRouteShortcuts = Record<RouterMethod, RouterUse>;
|
|
@@ -353,4 +384,4 @@ interface CreateRouterOptions {
|
|
|
353
384
|
}
|
|
354
385
|
declare function createRouter(opts?: CreateRouterOptions): Router;
|
|
355
386
|
|
|
356
|
-
export { AddRouteShortcuts, App, AppOptions, AppUse, CacheConditions, CreateRouterOptions, DynamicEventHandler, Encoding, EventHandler, EventHandlerResponse, H3Error, H3Event, H3EventContext, H3Headers, H3Response, HTTPMethod, InputLayer, InputStack, Layer, LazyEventHandler, MIMES, Matcher, NodeEventContext, NodeListener, NodeMiddleware, NodePromisifiedHandler, ProxyOptions, RequestHeaders, Router, RouterMethod, RouterUse, Session, SessionConfig, SessionData, Stack, appendHeader, appendHeaders, appendResponseHeader, appendResponseHeaders, assertMethod, callNodeListener, clearSession, createApp, createAppEventHandler, createError, createEvent, createRouter, defaultContentType, defineEventHandler, defineLazyEventHandler, defineNodeListener, defineNodeMiddleware, deleteCookie, dynamicEventHandler, eventHandler, fetchWithEvent, fromNodeMiddleware, getCookie, getHeader, getHeaders, getMethod, getProxyRequestHeaders, getQuery, getRequestHeader, getRequestHeaders, getResponseHeader, getResponseHeaders, getResponseStatus, getResponseStatusText, getRouterParam, getRouterParams, getSession, handleCacheHeaders, isError, isEvent, isEventHandler, isMethod, isStream, lazyEventHandler, parseCookies, promisifyNodeListener, proxyRequest, readBody, readMultipartFormData, readRawBody, send, sendError, sendNoContent, sendProxy, sendRedirect, sendStream, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, setResponseStatus, toEventHandler, toNodeListener, updateSession, use, useBase, useSession, writeEarlyHints };
|
|
387
|
+
export { AddRouteShortcuts, App, AppOptions, AppUse, CacheConditions, CreateRouterOptions, DynamicEventHandler, Encoding, EventHandler, EventHandlerResponse, H3CorsOptions, H3Error, H3Event, H3EventContext, H3Headers, H3Response, HTTPMethod, InputLayer, InputStack, Layer, LazyEventHandler, MIMES, Matcher, MultiPartData, NodeEventContext, NodeListener, NodeMiddleware, NodePromisifiedHandler, ProxyOptions, RequestHeaders, Router, RouterMethod, RouterUse, Session, SessionConfig, SessionData, Stack, appendCorsHeaders, appendCorsPreflightHeaders, appendHeader, appendHeaders, appendResponseHeader, appendResponseHeaders, assertMethod, callNodeListener, clearSession, createApp, createAppEventHandler, createError, createEvent, createRouter, defaultContentType, defineEventHandler, defineLazyEventHandler, defineNodeListener, defineNodeMiddleware, deleteCookie, dynamicEventHandler, eventHandler, fetchWithEvent, fromNodeMiddleware, getCookie, getHeader, getHeaders, getMethod, getProxyRequestHeaders, getQuery, getRequestHeader, getRequestHeaders, getResponseHeader, getResponseHeaders, getResponseStatus, getResponseStatusText, getRouterParam, getRouterParams, getSession, handleCacheHeaders, handleCors, isCorsOriginAllowed, isError, isEvent, isEventHandler, isMethod, isPreflightRequest, isStream, lazyEventHandler, parseCookies, promisifyNodeListener, proxyRequest, readBody, readMultipartFormData, readRawBody, sealSession, send, sendError, sendNoContent, sendProxy, sendRedirect, sendStream, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, setResponseStatus, toEventHandler, toNodeListener, unsealSession, updateSession, use, useBase, useSession, writeEarlyHints };
|
package/dist/index.mjs
CHANGED
|
@@ -3,7 +3,8 @@ import { createRouter as createRouter$1 } from 'radix3';
|
|
|
3
3
|
import destr from 'destr';
|
|
4
4
|
import { parse as parse$1, serialize } from 'cookie-es';
|
|
5
5
|
import crypto from 'uncrypto';
|
|
6
|
-
import {
|
|
6
|
+
import { seal, defaults, unseal } from 'iron-webcrypto';
|
|
7
|
+
import { defu } from 'defu';
|
|
7
8
|
|
|
8
9
|
function useBase(base, handler) {
|
|
9
10
|
base = withoutTrailingSlash(base);
|
|
@@ -378,6 +379,59 @@ function deleteCookie(event, name, serializeOptions) {
|
|
|
378
379
|
});
|
|
379
380
|
}
|
|
380
381
|
|
|
382
|
+
function splitCookiesString(cookiesString) {
|
|
383
|
+
if (typeof cookiesString !== "string") {
|
|
384
|
+
return [];
|
|
385
|
+
}
|
|
386
|
+
const cookiesStrings = [];
|
|
387
|
+
let pos = 0;
|
|
388
|
+
let start;
|
|
389
|
+
let ch;
|
|
390
|
+
let lastComma;
|
|
391
|
+
let nextStart;
|
|
392
|
+
let cookiesSeparatorFound;
|
|
393
|
+
function skipWhitespace() {
|
|
394
|
+
while (pos < cookiesString.length && /\s/.test(cookiesString.charAt(pos))) {
|
|
395
|
+
pos += 1;
|
|
396
|
+
}
|
|
397
|
+
return pos < cookiesString.length;
|
|
398
|
+
}
|
|
399
|
+
function notSpecialChar() {
|
|
400
|
+
ch = cookiesString.charAt(pos);
|
|
401
|
+
return ch !== "=" && ch !== ";" && ch !== ",";
|
|
402
|
+
}
|
|
403
|
+
while (pos < cookiesString.length) {
|
|
404
|
+
start = pos;
|
|
405
|
+
cookiesSeparatorFound = false;
|
|
406
|
+
while (skipWhitespace()) {
|
|
407
|
+
ch = cookiesString.charAt(pos);
|
|
408
|
+
if (ch === ",") {
|
|
409
|
+
lastComma = pos;
|
|
410
|
+
pos += 1;
|
|
411
|
+
skipWhitespace();
|
|
412
|
+
nextStart = pos;
|
|
413
|
+
while (pos < cookiesString.length && notSpecialChar()) {
|
|
414
|
+
pos += 1;
|
|
415
|
+
}
|
|
416
|
+
if (pos < cookiesString.length && cookiesString.charAt(pos) === "=") {
|
|
417
|
+
cookiesSeparatorFound = true;
|
|
418
|
+
pos = nextStart;
|
|
419
|
+
cookiesStrings.push(cookiesString.slice(start, lastComma));
|
|
420
|
+
start = pos;
|
|
421
|
+
} else {
|
|
422
|
+
pos = lastComma + 1;
|
|
423
|
+
}
|
|
424
|
+
} else {
|
|
425
|
+
pos += 1;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if (!cookiesSeparatorFound || pos >= cookiesString.length) {
|
|
429
|
+
cookiesStrings.push(cookiesString.slice(start, cookiesString.length));
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return cookiesStrings;
|
|
433
|
+
}
|
|
434
|
+
|
|
381
435
|
const PayloadMethods = /* @__PURE__ */ new Set(["PATCH", "POST", "PUT", "DELETE"]);
|
|
382
436
|
const ignoredHeaders = /* @__PURE__ */ new Set([
|
|
383
437
|
"transfer-encoding",
|
|
@@ -424,6 +478,27 @@ async function sendProxy(event, target, opts = {}) {
|
|
|
424
478
|
if (key === "content-length") {
|
|
425
479
|
continue;
|
|
426
480
|
}
|
|
481
|
+
if (key === "set-cookie") {
|
|
482
|
+
const cookies = splitCookiesString(value).map((cookie) => {
|
|
483
|
+
if (opts.cookieDomainRewrite) {
|
|
484
|
+
cookie = rewriteCookieProperty(
|
|
485
|
+
cookie,
|
|
486
|
+
opts.cookieDomainRewrite,
|
|
487
|
+
"domain"
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
if (opts.cookiePathRewrite) {
|
|
491
|
+
cookie = rewriteCookieProperty(
|
|
492
|
+
cookie,
|
|
493
|
+
opts.cookiePathRewrite,
|
|
494
|
+
"path"
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
return cookie;
|
|
498
|
+
});
|
|
499
|
+
event.node.res.setHeader("set-cookie", cookies);
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
427
502
|
event.node.res.setHeader(key, value);
|
|
428
503
|
}
|
|
429
504
|
if (response._data !== void 0) {
|
|
@@ -470,6 +545,23 @@ function _getFetch(_fetch) {
|
|
|
470
545
|
"fetch is not available. Try importing `node-fetch-native/polyfill` for Node.js."
|
|
471
546
|
);
|
|
472
547
|
}
|
|
548
|
+
function rewriteCookieProperty(header, map, property) {
|
|
549
|
+
const _map = typeof map === "string" ? { "*": map } : map;
|
|
550
|
+
return header.replace(
|
|
551
|
+
new RegExp(`(;\\s*${property}=)([^;]+)`, "gi"),
|
|
552
|
+
(match, prefix, previousValue) => {
|
|
553
|
+
let newValue;
|
|
554
|
+
if (previousValue in _map) {
|
|
555
|
+
newValue = _map[previousValue];
|
|
556
|
+
} else if ("*" in _map) {
|
|
557
|
+
newValue = _map["*"];
|
|
558
|
+
} else {
|
|
559
|
+
return match;
|
|
560
|
+
}
|
|
561
|
+
return newValue ? prefix + newValue : "";
|
|
562
|
+
}
|
|
563
|
+
);
|
|
564
|
+
}
|
|
473
565
|
|
|
474
566
|
const defer = typeof setImmediate !== "undefined" ? setImmediate : (fn) => fn();
|
|
475
567
|
function send(event, data, type) {
|
|
@@ -638,21 +730,35 @@ async function getSession(event, config) {
|
|
|
638
730
|
if (event.context.sessions[sessionName]) {
|
|
639
731
|
return event.context.sessions[sessionName];
|
|
640
732
|
}
|
|
641
|
-
const session = {
|
|
733
|
+
const session = {
|
|
734
|
+
id: "",
|
|
735
|
+
createdAt: 0,
|
|
736
|
+
data: /* @__PURE__ */ Object.create(null)
|
|
737
|
+
};
|
|
642
738
|
event.context.sessions[sessionName] = session;
|
|
643
|
-
|
|
644
|
-
if (
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
739
|
+
let sealedSession;
|
|
740
|
+
if (config.sessionHeader !== false) {
|
|
741
|
+
const headerName = typeof config.sessionHeader === "string" ? config.sessionHeader.toLowerCase() : `x-${sessionName.toLowerCase()}-session`;
|
|
742
|
+
const headerValue = event.node.req.headers[headerName];
|
|
743
|
+
if (typeof headerValue === "string") {
|
|
744
|
+
sealedSession = headerValue;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
if (!sealedSession) {
|
|
748
|
+
sealedSession = getCookie(event, sessionName);
|
|
749
|
+
}
|
|
750
|
+
if (sealedSession) {
|
|
751
|
+
const unsealed = await unsealSession(event, config, sealedSession).catch(
|
|
752
|
+
() => {
|
|
753
|
+
}
|
|
653
754
|
);
|
|
654
755
|
Object.assign(session, unsealed);
|
|
655
756
|
}
|
|
757
|
+
if (!session.id) {
|
|
758
|
+
session.id = (config.crypto || crypto).randomUUID();
|
|
759
|
+
session.createdAt = Date.now();
|
|
760
|
+
await updateSession(event, config);
|
|
761
|
+
}
|
|
656
762
|
return session;
|
|
657
763
|
}
|
|
658
764
|
async function updateSession(event, config, update) {
|
|
@@ -664,21 +770,168 @@ async function updateSession(event, config, update) {
|
|
|
664
770
|
if (update) {
|
|
665
771
|
Object.assign(session.data, update);
|
|
666
772
|
}
|
|
667
|
-
|
|
773
|
+
if (config.cookie !== false) {
|
|
774
|
+
const sealed = await sealSession(event, config);
|
|
775
|
+
setCookie(event, sessionName, sealed, {
|
|
776
|
+
...DEFAULT_COOKIE,
|
|
777
|
+
expires: config.maxAge ? new Date(session.createdAt + config.maxAge * 1e3) : void 0,
|
|
778
|
+
...config.cookie
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
return session;
|
|
782
|
+
}
|
|
783
|
+
async function sealSession(event, config) {
|
|
784
|
+
const sessionName = config.name || DEFAULT_NAME;
|
|
785
|
+
const session = event.context.sessions?.[sessionName] || await getSession(event, config);
|
|
786
|
+
const sealed = await seal(config.crypto || crypto, session, config.password, {
|
|
787
|
+
...defaults,
|
|
788
|
+
ttl: config.maxAge ? config.maxAge * 1e3 : 0,
|
|
789
|
+
...config.seal
|
|
790
|
+
});
|
|
791
|
+
return sealed;
|
|
792
|
+
}
|
|
793
|
+
async function unsealSession(_event, config, sealed) {
|
|
794
|
+
const unsealed = await unseal(
|
|
668
795
|
config.crypto || crypto,
|
|
669
|
-
|
|
796
|
+
sealed,
|
|
670
797
|
config.password,
|
|
671
|
-
|
|
798
|
+
{
|
|
799
|
+
...defaults,
|
|
800
|
+
ttl: config.maxAge ? config.maxAge * 1e3 : 0,
|
|
801
|
+
...config.seal
|
|
802
|
+
}
|
|
672
803
|
);
|
|
673
|
-
|
|
674
|
-
|
|
804
|
+
if (config.maxAge) {
|
|
805
|
+
const age = Date.now() - (unsealed.createdAt || Number.NEGATIVE_INFINITY);
|
|
806
|
+
if (age > config.maxAge * 1e3) {
|
|
807
|
+
throw new Error("Session expired!");
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
return unsealed;
|
|
675
811
|
}
|
|
676
812
|
async function clearSession(event, config) {
|
|
677
813
|
const sessionName = config.name || DEFAULT_NAME;
|
|
678
814
|
if (event.context.sessions?.[sessionName]) {
|
|
679
815
|
delete event.context.sessions[sessionName];
|
|
680
816
|
}
|
|
681
|
-
await setCookie(event, sessionName, "",
|
|
817
|
+
await setCookie(event, sessionName, "", {
|
|
818
|
+
...DEFAULT_COOKIE,
|
|
819
|
+
...config.cookie
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
function resolveCorsOptions(options = {}) {
|
|
824
|
+
const defaultOptions = {
|
|
825
|
+
origin: "*",
|
|
826
|
+
methods: "*",
|
|
827
|
+
allowHeaders: "*",
|
|
828
|
+
exposeHeaders: "*",
|
|
829
|
+
credentials: false,
|
|
830
|
+
maxAge: false,
|
|
831
|
+
preflight: {
|
|
832
|
+
statusCode: 204
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
return defu(options, defaultOptions);
|
|
836
|
+
}
|
|
837
|
+
function isPreflightRequest(event) {
|
|
838
|
+
const method = getMethod(event);
|
|
839
|
+
const origin = getRequestHeader(event, "origin");
|
|
840
|
+
const accessControlRequestMethod = getRequestHeader(
|
|
841
|
+
event,
|
|
842
|
+
"access-control-request-method"
|
|
843
|
+
);
|
|
844
|
+
return method === "OPTIONS" && !!origin && !!accessControlRequestMethod;
|
|
845
|
+
}
|
|
846
|
+
function isCorsOriginAllowed(origin, options) {
|
|
847
|
+
const { origin: originOption } = options;
|
|
848
|
+
if (!origin || !originOption || originOption === "*" || originOption === "null") {
|
|
849
|
+
return true;
|
|
850
|
+
}
|
|
851
|
+
if (Array.isArray(originOption)) {
|
|
852
|
+
return originOption.some((_origin) => {
|
|
853
|
+
if (_origin instanceof RegExp) {
|
|
854
|
+
return _origin.test(origin);
|
|
855
|
+
}
|
|
856
|
+
return origin === _origin;
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
return originOption(origin);
|
|
860
|
+
}
|
|
861
|
+
function createOriginHeaders(event, options) {
|
|
862
|
+
const { origin: originOption } = options;
|
|
863
|
+
const origin = getRequestHeader(event, "origin");
|
|
864
|
+
if (!origin || !originOption || originOption === "*") {
|
|
865
|
+
return { "access-control-allow-origin": "*" };
|
|
866
|
+
}
|
|
867
|
+
if (typeof originOption === "string") {
|
|
868
|
+
return { "access-control-allow-origin": originOption, vary: "origin" };
|
|
869
|
+
}
|
|
870
|
+
return isCorsOriginAllowed(origin, options) ? { "access-control-allow-origin": origin, vary: "origin" } : {};
|
|
871
|
+
}
|
|
872
|
+
function createMethodsHeaders(options) {
|
|
873
|
+
const { methods } = options;
|
|
874
|
+
if (!methods) {
|
|
875
|
+
return {};
|
|
876
|
+
}
|
|
877
|
+
if (methods === "*") {
|
|
878
|
+
return { "access-control-allow-methods": "*" };
|
|
879
|
+
}
|
|
880
|
+
return methods.length > 0 ? { "access-control-allow-methods": methods.join(",") } : {};
|
|
881
|
+
}
|
|
882
|
+
function createCredentialsHeaders(options) {
|
|
883
|
+
const { credentials } = options;
|
|
884
|
+
if (credentials) {
|
|
885
|
+
return { "access-control-allow-credentials": "true" };
|
|
886
|
+
}
|
|
887
|
+
return {};
|
|
888
|
+
}
|
|
889
|
+
function createAllowHeaderHeaders(event, options) {
|
|
890
|
+
const { allowHeaders } = options;
|
|
891
|
+
if (!allowHeaders || allowHeaders === "*" || allowHeaders.length === 0) {
|
|
892
|
+
const header = getRequestHeader(event, "access-control-request-headers");
|
|
893
|
+
return header ? {
|
|
894
|
+
"access-control-allow-headers": header,
|
|
895
|
+
vary: "access-control-request-headers"
|
|
896
|
+
} : {};
|
|
897
|
+
}
|
|
898
|
+
return {
|
|
899
|
+
"access-control-allow-headers": allowHeaders.join(","),
|
|
900
|
+
vary: "access-control-request-headers"
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
function createExposeHeaders(options) {
|
|
904
|
+
const { exposeHeaders } = options;
|
|
905
|
+
if (!exposeHeaders) {
|
|
906
|
+
return {};
|
|
907
|
+
}
|
|
908
|
+
if (exposeHeaders === "*") {
|
|
909
|
+
return { "access-control-expose-headers": exposeHeaders };
|
|
910
|
+
}
|
|
911
|
+
return { "access-control-expose-headers": exposeHeaders.join(",") };
|
|
912
|
+
}
|
|
913
|
+
function appendCorsPreflightHeaders(event, options) {
|
|
914
|
+
appendHeaders(event, createOriginHeaders(event, options));
|
|
915
|
+
appendHeaders(event, createCredentialsHeaders(options));
|
|
916
|
+
appendHeaders(event, createExposeHeaders(options));
|
|
917
|
+
appendHeaders(event, createMethodsHeaders(options));
|
|
918
|
+
appendHeaders(event, createAllowHeaderHeaders(event, options));
|
|
919
|
+
}
|
|
920
|
+
function appendCorsHeaders(event, options) {
|
|
921
|
+
appendHeaders(event, createOriginHeaders(event, options));
|
|
922
|
+
appendHeaders(event, createCredentialsHeaders(options));
|
|
923
|
+
appendHeaders(event, createExposeHeaders(options));
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
function handleCors(event, options) {
|
|
927
|
+
const _options = resolveCorsOptions(options);
|
|
928
|
+
if (isPreflightRequest(event)) {
|
|
929
|
+
appendCorsPreflightHeaders(event, options);
|
|
930
|
+
sendNoContent(event, _options.preflight.statusCode);
|
|
931
|
+
return true;
|
|
932
|
+
}
|
|
933
|
+
appendCorsHeaders(event, options);
|
|
934
|
+
return false;
|
|
682
935
|
}
|
|
683
936
|
|
|
684
937
|
class H3Headers {
|
|
@@ -1130,4 +1383,4 @@ function createRouter(opts = {}) {
|
|
|
1130
1383
|
return router;
|
|
1131
1384
|
}
|
|
1132
1385
|
|
|
1133
|
-
export { H3Error, H3Event, H3Headers, H3Response, MIMES, appendHeader, appendHeaders, appendResponseHeader, appendResponseHeaders, assertMethod, callNodeListener, clearSession, createApp, createAppEventHandler, createError, createEvent, createRouter, defaultContentType, defineEventHandler, defineLazyEventHandler, defineNodeListener, defineNodeMiddleware, deleteCookie, dynamicEventHandler, eventHandler, fetchWithEvent, fromNodeMiddleware, getCookie, getHeader, getHeaders, getMethod, getProxyRequestHeaders, getQuery, getRequestHeader, getRequestHeaders, getResponseHeader, getResponseHeaders, getResponseStatus, getResponseStatusText, getRouterParam, getRouterParams, getSession, handleCacheHeaders, isError, isEvent, isEventHandler, isMethod, isStream, lazyEventHandler, parseCookies, promisifyNodeListener, proxyRequest, readBody, readMultipartFormData, readRawBody, send, sendError, sendNoContent, sendProxy, sendRedirect, sendStream, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, setResponseStatus, toEventHandler, toNodeListener, updateSession, use, useBase, useSession, writeEarlyHints };
|
|
1386
|
+
export { H3Error, H3Event, H3Headers, H3Response, MIMES, appendCorsHeaders, appendCorsPreflightHeaders, appendHeader, appendHeaders, appendResponseHeader, appendResponseHeaders, assertMethod, callNodeListener, clearSession, createApp, createAppEventHandler, createError, createEvent, createRouter, defaultContentType, defineEventHandler, defineLazyEventHandler, defineNodeListener, defineNodeMiddleware, deleteCookie, dynamicEventHandler, eventHandler, fetchWithEvent, fromNodeMiddleware, getCookie, getHeader, getHeaders, getMethod, getProxyRequestHeaders, getQuery, getRequestHeader, getRequestHeaders, getResponseHeader, getResponseHeaders, getResponseStatus, getResponseStatusText, getRouterParam, getRouterParams, getSession, handleCacheHeaders, handleCors, isCorsOriginAllowed, isError, isEvent, isEventHandler, isMethod, isPreflightRequest, isStream, lazyEventHandler, parseCookies, promisifyNodeListener, proxyRequest, readBody, readMultipartFormData, readRawBody, sealSession, send, sendError, sendNoContent, sendProxy, sendRedirect, sendStream, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, setResponseStatus, toEventHandler, toNodeListener, unsealSession, updateSession, use, useBase, useSession, writeEarlyHints };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "h3",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Tiny JavaScript Server",
|
|
5
5
|
"repository": "unjs/h3",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,10 +21,11 @@
|
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"cookie-es": "^0.5.0",
|
|
24
|
+
"defu": "^6.1.2",
|
|
24
25
|
"destr": "^1.2.2",
|
|
25
|
-
"iron-webcrypto": "^0.
|
|
26
|
+
"iron-webcrypto": "^0.5.0",
|
|
26
27
|
"radix3": "^1.0.0",
|
|
27
|
-
"ufo": "^1.0
|
|
28
|
+
"ufo": "^1.1.0",
|
|
28
29
|
"uncrypto": "^0.1.2"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
@@ -32,28 +33,29 @@
|
|
|
32
33
|
"@types/express": "^4.17.17",
|
|
33
34
|
"@types/node": "^18.13.0",
|
|
34
35
|
"@types/supertest": "^2.0.12",
|
|
35
|
-
"@vitest/coverage-c8": "^0.28.
|
|
36
|
+
"@vitest/coverage-c8": "^0.28.5",
|
|
36
37
|
"autocannon": "^7.10.0",
|
|
37
38
|
"changelogen": "^0.4.1",
|
|
38
39
|
"connect": "^3.7.0",
|
|
39
|
-
"eslint": "^8.
|
|
40
|
+
"eslint": "^8.34.0",
|
|
40
41
|
"eslint-config-unjs": "^0.1.0",
|
|
41
42
|
"express": "^4.18.2",
|
|
42
43
|
"get-port": "^6.1.2",
|
|
43
|
-
"jiti": "^1.
|
|
44
|
+
"jiti": "^1.17.0",
|
|
44
45
|
"listhen": "^1.0.2",
|
|
45
|
-
"node-fetch-native": "^1.0.
|
|
46
|
+
"node-fetch-native": "^1.0.2",
|
|
46
47
|
"prettier": "^2.8.4",
|
|
47
48
|
"supertest": "^6.3.3",
|
|
48
49
|
"typescript": "^4.9.5",
|
|
49
50
|
"unbuild": "^1.1.1",
|
|
50
|
-
"vitest": "^0.28.
|
|
51
|
+
"vitest": "^0.28.5"
|
|
51
52
|
},
|
|
52
|
-
"packageManager": "pnpm@7.
|
|
53
|
+
"packageManager": "pnpm@7.27.0",
|
|
53
54
|
"scripts": {
|
|
54
55
|
"build": "unbuild",
|
|
55
56
|
"dev": "vitest",
|
|
56
|
-
"lint": "eslint --ext ts
|
|
57
|
+
"lint": "eslint --cache --ext .ts,.js,.mjs,.cjs . && prettier -c src test playground",
|
|
58
|
+
"lint:fix": "eslint --cache --ext .ts,.js,.mjs,.cjs . --fix && prettier -c src test playground -w",
|
|
57
59
|
"play": "jiti ./playground/index.ts",
|
|
58
60
|
"profile": "0x -o -D .profile -P 'autocannon -c 100 -p 10 -d 40 http://localhost:$PORT' ./playground/server.cjs",
|
|
59
61
|
"release": "pnpm test && pnpm build && changelogen --release && pnpm publish && git push --follow-tags",
|