h3 1.3.0 → 1.4.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 +6 -4
- package/dist/index.cjs +66 -17
- package/dist/index.d.ts +12 -2
- package/dist/index.mjs +66 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -155,10 +155,12 @@ H3 has a concept of composable utilities that accept `event` (from `eventHandler
|
|
|
155
155
|
- `getResponseStatus(event)`
|
|
156
156
|
- `getResponseStatusText(event)`
|
|
157
157
|
- `readMultipartFormData(event)`
|
|
158
|
-
- `useSession(event, { password, name?, cookie?, seal?, crypto? })`
|
|
159
|
-
- `getSession(event,
|
|
160
|
-
- `updateSession(event,
|
|
161
|
-
- `clearSession(event,
|
|
158
|
+
- `useSession(event, config = { password, maxAge?, name?, cookie?, seal?, crypto? })`
|
|
159
|
+
- `getSession(event, config)`
|
|
160
|
+
- `updateSession(event, config, update)`
|
|
161
|
+
- `clearSession(event, config)`
|
|
162
|
+
- `sealSession(event, config)`
|
|
163
|
+
- `unsealSession(event, config, sealed)`
|
|
162
164
|
|
|
163
165
|
👉 You can learn more about usage in [JSDocs Documentation](https://www.jsdocs.io/package/h3#package-functions).
|
|
164
166
|
|
package/dist/index.cjs
CHANGED
|
@@ -640,21 +640,35 @@ async function getSession(event, config) {
|
|
|
640
640
|
if (event.context.sessions[sessionName]) {
|
|
641
641
|
return event.context.sessions[sessionName];
|
|
642
642
|
}
|
|
643
|
-
const session = {
|
|
643
|
+
const session = {
|
|
644
|
+
id: "",
|
|
645
|
+
createdAt: 0,
|
|
646
|
+
data: /* @__PURE__ */ Object.create(null)
|
|
647
|
+
};
|
|
644
648
|
event.context.sessions[sessionName] = session;
|
|
645
|
-
|
|
646
|
-
if (
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
649
|
+
let sealedSession;
|
|
650
|
+
if (config.sessionHeader !== false) {
|
|
651
|
+
const headerName = typeof config.sessionHeader === "string" ? config.sessionHeader.toLowerCase() : `x-${sessionName.toLowerCase()}-session`;
|
|
652
|
+
const headerValue = event.node.req.headers[headerName];
|
|
653
|
+
if (typeof headerValue === "string") {
|
|
654
|
+
sealedSession = headerValue;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
if (!sealedSession) {
|
|
658
|
+
sealedSession = getCookie(event, sessionName);
|
|
659
|
+
}
|
|
660
|
+
if (sealedSession) {
|
|
661
|
+
const unsealed = await unsealSession(event, config, sealedSession).catch(
|
|
662
|
+
() => {
|
|
663
|
+
}
|
|
655
664
|
);
|
|
656
665
|
Object.assign(session, unsealed);
|
|
657
666
|
}
|
|
667
|
+
if (!session.id) {
|
|
668
|
+
session.id = (config.crypto || crypto).randomUUID();
|
|
669
|
+
session.createdAt = Date.now();
|
|
670
|
+
await updateSession(event, config);
|
|
671
|
+
}
|
|
658
672
|
return session;
|
|
659
673
|
}
|
|
660
674
|
async function updateSession(event, config, update) {
|
|
@@ -666,21 +680,54 @@ async function updateSession(event, config, update) {
|
|
|
666
680
|
if (update) {
|
|
667
681
|
Object.assign(session.data, update);
|
|
668
682
|
}
|
|
669
|
-
|
|
683
|
+
if (config.cookie !== false) {
|
|
684
|
+
const sealed = await sealSession(event, config);
|
|
685
|
+
setCookie(event, sessionName, sealed, {
|
|
686
|
+
...DEFAULT_COOKIE,
|
|
687
|
+
expires: config.maxAge ? new Date(session.createdAt + config.maxAge * 1e3) : void 0,
|
|
688
|
+
...config.cookie
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
return session;
|
|
692
|
+
}
|
|
693
|
+
async function sealSession(event, config) {
|
|
694
|
+
const sessionName = config.name || DEFAULT_NAME;
|
|
695
|
+
const session = event.context.sessions?.[sessionName] || await getSession(event, config);
|
|
696
|
+
const sealed = await ironWebcrypto.seal(config.crypto || crypto, session, config.password, {
|
|
697
|
+
...ironWebcrypto.defaults,
|
|
698
|
+
ttl: config.maxAge ? config.maxAge * 1e3 : 0,
|
|
699
|
+
...config.seal
|
|
700
|
+
});
|
|
701
|
+
return sealed;
|
|
702
|
+
}
|
|
703
|
+
async function unsealSession(_event, config, sealed) {
|
|
704
|
+
const unsealed = await ironWebcrypto.unseal(
|
|
670
705
|
config.crypto || crypto,
|
|
671
|
-
|
|
706
|
+
sealed,
|
|
672
707
|
config.password,
|
|
673
|
-
|
|
708
|
+
{
|
|
709
|
+
...ironWebcrypto.defaults,
|
|
710
|
+
ttl: config.maxAge ? config.maxAge * 1e3 : 0,
|
|
711
|
+
...config.seal
|
|
712
|
+
}
|
|
674
713
|
);
|
|
675
|
-
|
|
676
|
-
|
|
714
|
+
if (config.maxAge) {
|
|
715
|
+
const age = Date.now() - (unsealed.createdAt || Number.NEGATIVE_INFINITY);
|
|
716
|
+
if (age > config.maxAge * 1e3) {
|
|
717
|
+
throw new Error("Session expired!");
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return unsealed;
|
|
677
721
|
}
|
|
678
722
|
async function clearSession(event, config) {
|
|
679
723
|
const sessionName = config.name || DEFAULT_NAME;
|
|
680
724
|
if (event.context.sessions?.[sessionName]) {
|
|
681
725
|
delete event.context.sessions[sessionName];
|
|
682
726
|
}
|
|
683
|
-
await setCookie(event, sessionName, "",
|
|
727
|
+
await setCookie(event, sessionName, "", {
|
|
728
|
+
...DEFAULT_COOKIE,
|
|
729
|
+
...config.cookie
|
|
730
|
+
});
|
|
684
731
|
}
|
|
685
732
|
|
|
686
733
|
class H3Headers {
|
|
@@ -1187,6 +1234,7 @@ exports.proxyRequest = proxyRequest;
|
|
|
1187
1234
|
exports.readBody = readBody;
|
|
1188
1235
|
exports.readMultipartFormData = readMultipartFormData;
|
|
1189
1236
|
exports.readRawBody = readRawBody;
|
|
1237
|
+
exports.sealSession = sealSession;
|
|
1190
1238
|
exports.send = send;
|
|
1191
1239
|
exports.sendError = sendError;
|
|
1192
1240
|
exports.sendNoContent = sendNoContent;
|
|
@@ -1201,6 +1249,7 @@ exports.setResponseHeaders = setResponseHeaders;
|
|
|
1201
1249
|
exports.setResponseStatus = setResponseStatus;
|
|
1202
1250
|
exports.toEventHandler = toEventHandler;
|
|
1203
1251
|
exports.toNodeListener = toNodeListener;
|
|
1252
|
+
exports.unsealSession = unsealSession;
|
|
1204
1253
|
exports.updateSession = updateSession;
|
|
1205
1254
|
exports.use = use;
|
|
1206
1255
|
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";
|
|
@@ -353,4 +363,4 @@ interface CreateRouterOptions {
|
|
|
353
363
|
}
|
|
354
364
|
declare function createRouter(opts?: CreateRouterOptions): Router;
|
|
355
365
|
|
|
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 };
|
|
366
|
+
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, 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,7 @@ 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
7
|
|
|
8
8
|
function useBase(base, handler) {
|
|
9
9
|
base = withoutTrailingSlash(base);
|
|
@@ -638,21 +638,35 @@ async function getSession(event, config) {
|
|
|
638
638
|
if (event.context.sessions[sessionName]) {
|
|
639
639
|
return event.context.sessions[sessionName];
|
|
640
640
|
}
|
|
641
|
-
const session = {
|
|
641
|
+
const session = {
|
|
642
|
+
id: "",
|
|
643
|
+
createdAt: 0,
|
|
644
|
+
data: /* @__PURE__ */ Object.create(null)
|
|
645
|
+
};
|
|
642
646
|
event.context.sessions[sessionName] = session;
|
|
643
|
-
|
|
644
|
-
if (
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
647
|
+
let sealedSession;
|
|
648
|
+
if (config.sessionHeader !== false) {
|
|
649
|
+
const headerName = typeof config.sessionHeader === "string" ? config.sessionHeader.toLowerCase() : `x-${sessionName.toLowerCase()}-session`;
|
|
650
|
+
const headerValue = event.node.req.headers[headerName];
|
|
651
|
+
if (typeof headerValue === "string") {
|
|
652
|
+
sealedSession = headerValue;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
if (!sealedSession) {
|
|
656
|
+
sealedSession = getCookie(event, sessionName);
|
|
657
|
+
}
|
|
658
|
+
if (sealedSession) {
|
|
659
|
+
const unsealed = await unsealSession(event, config, sealedSession).catch(
|
|
660
|
+
() => {
|
|
661
|
+
}
|
|
653
662
|
);
|
|
654
663
|
Object.assign(session, unsealed);
|
|
655
664
|
}
|
|
665
|
+
if (!session.id) {
|
|
666
|
+
session.id = (config.crypto || crypto).randomUUID();
|
|
667
|
+
session.createdAt = Date.now();
|
|
668
|
+
await updateSession(event, config);
|
|
669
|
+
}
|
|
656
670
|
return session;
|
|
657
671
|
}
|
|
658
672
|
async function updateSession(event, config, update) {
|
|
@@ -664,21 +678,54 @@ async function updateSession(event, config, update) {
|
|
|
664
678
|
if (update) {
|
|
665
679
|
Object.assign(session.data, update);
|
|
666
680
|
}
|
|
667
|
-
|
|
681
|
+
if (config.cookie !== false) {
|
|
682
|
+
const sealed = await sealSession(event, config);
|
|
683
|
+
setCookie(event, sessionName, sealed, {
|
|
684
|
+
...DEFAULT_COOKIE,
|
|
685
|
+
expires: config.maxAge ? new Date(session.createdAt + config.maxAge * 1e3) : void 0,
|
|
686
|
+
...config.cookie
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
return session;
|
|
690
|
+
}
|
|
691
|
+
async function sealSession(event, config) {
|
|
692
|
+
const sessionName = config.name || DEFAULT_NAME;
|
|
693
|
+
const session = event.context.sessions?.[sessionName] || await getSession(event, config);
|
|
694
|
+
const sealed = await seal(config.crypto || crypto, session, config.password, {
|
|
695
|
+
...defaults,
|
|
696
|
+
ttl: config.maxAge ? config.maxAge * 1e3 : 0,
|
|
697
|
+
...config.seal
|
|
698
|
+
});
|
|
699
|
+
return sealed;
|
|
700
|
+
}
|
|
701
|
+
async function unsealSession(_event, config, sealed) {
|
|
702
|
+
const unsealed = await unseal(
|
|
668
703
|
config.crypto || crypto,
|
|
669
|
-
|
|
704
|
+
sealed,
|
|
670
705
|
config.password,
|
|
671
|
-
|
|
706
|
+
{
|
|
707
|
+
...defaults,
|
|
708
|
+
ttl: config.maxAge ? config.maxAge * 1e3 : 0,
|
|
709
|
+
...config.seal
|
|
710
|
+
}
|
|
672
711
|
);
|
|
673
|
-
|
|
674
|
-
|
|
712
|
+
if (config.maxAge) {
|
|
713
|
+
const age = Date.now() - (unsealed.createdAt || Number.NEGATIVE_INFINITY);
|
|
714
|
+
if (age > config.maxAge * 1e3) {
|
|
715
|
+
throw new Error("Session expired!");
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
return unsealed;
|
|
675
719
|
}
|
|
676
720
|
async function clearSession(event, config) {
|
|
677
721
|
const sessionName = config.name || DEFAULT_NAME;
|
|
678
722
|
if (event.context.sessions?.[sessionName]) {
|
|
679
723
|
delete event.context.sessions[sessionName];
|
|
680
724
|
}
|
|
681
|
-
await setCookie(event, sessionName, "",
|
|
725
|
+
await setCookie(event, sessionName, "", {
|
|
726
|
+
...DEFAULT_COOKIE,
|
|
727
|
+
...config.cookie
|
|
728
|
+
});
|
|
682
729
|
}
|
|
683
730
|
|
|
684
731
|
class H3Headers {
|
|
@@ -1130,4 +1177,4 @@ function createRouter(opts = {}) {
|
|
|
1130
1177
|
return router;
|
|
1131
1178
|
}
|
|
1132
1179
|
|
|
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 };
|
|
1180
|
+
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, sealSession, send, sendError, sendNoContent, sendProxy, sendRedirect, sendStream, setCookie, setHeader, setHeaders, setResponseHeader, setResponseHeaders, setResponseStatus, toEventHandler, toNodeListener, unsealSession, updateSession, use, useBase, useSession, writeEarlyHints };
|