@scayle/h3-session 0.4.2 → 0.5.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/CHANGELOG.md +18 -0
- package/dist/index.d.mts +11 -6
- package/dist/index.d.ts +11 -6
- package/dist/index.mjs +37 -25
- package/package.json +13 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @scayle/h3-session
|
|
2
2
|
|
|
3
|
+
## 0.5.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- When a sessionID exists, but there is no session data, re-use the sessionID instead of generating both new data and a new ID. This enables proper functioning of the `saveUnitialized` configuration.
|
|
8
|
+
- Fix the `clear()` method on `UnstorageSessionStore` not clearing sessions
|
|
9
|
+
|
|
10
|
+
## 0.5.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- Cookie metadata is no longer saved in the session store. Previously, `h3-session` stored a copy of the cookie settings in the session store alongside the session data. This has been changed and now only the session data itself is stored. The cookie settings can still be read and manipulated from the `Session` object.
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- Removed dependency `zod@^3.23.8`
|
|
19
|
+
- Replace regex used for cookie parsing
|
|
20
|
+
|
|
3
21
|
## 0.4.2
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/dist/index.d.mts
CHANGED
|
@@ -41,7 +41,7 @@ declare class UnstorageSessionStore implements SessionStore {
|
|
|
41
41
|
* Get the session with the specified session ID
|
|
42
42
|
* @param sid the un-prefixed session ID
|
|
43
43
|
*/
|
|
44
|
-
get(sid: string): Promise<
|
|
44
|
+
get(sid: string): Promise<SessionDataT | undefined>;
|
|
45
45
|
/**
|
|
46
46
|
* Save the session with the specified session ID
|
|
47
47
|
* If the session has expired (has a TTL <= 0) it is deleted.
|
|
@@ -62,7 +62,7 @@ declare class UnstorageSessionStore implements SessionStore {
|
|
|
62
62
|
/**
|
|
63
63
|
* Fetch all saved sessions.
|
|
64
64
|
*/
|
|
65
|
-
all(): Promise<
|
|
65
|
+
all(): Promise<SessionDataT[]>;
|
|
66
66
|
/**
|
|
67
67
|
* Returns the number of saved sessions
|
|
68
68
|
*/
|
|
@@ -84,9 +84,7 @@ type SessionCookie = SessionCookieOptions & {
|
|
|
84
84
|
};
|
|
85
85
|
interface SessionDataT {
|
|
86
86
|
}
|
|
87
|
-
type RawSession = SessionDataT
|
|
88
|
-
cookie?: SessionCookieOptions;
|
|
89
|
-
};
|
|
87
|
+
type RawSession = SessionDataT;
|
|
90
88
|
interface SessionStore {
|
|
91
89
|
all?: () => Promise<RawSession[]>;
|
|
92
90
|
destroy: (sid: string) => Promise<void>;
|
|
@@ -128,6 +126,13 @@ declare module 'h3' {
|
|
|
128
126
|
* @param secrets an array of secret strings to verify with
|
|
129
127
|
*/
|
|
130
128
|
declare function unsignCookie(value: string, secrets: string[]): Promise<string | false>;
|
|
129
|
+
/**
|
|
130
|
+
* Creates a signed cookie value in the format `s:[value].[signature]`
|
|
131
|
+
* @param value
|
|
132
|
+
* @param secret
|
|
133
|
+
*/
|
|
134
|
+
declare function signCookie(value: string, secret: string): Promise<string>;
|
|
135
|
+
declare function validateConfig(config: H3SessionOptions): void;
|
|
131
136
|
/**
|
|
132
137
|
* Attach a session to an H3Event
|
|
133
138
|
* @param event
|
|
@@ -135,4 +140,4 @@ declare function unsignCookie(value: string, secrets: string[]): Promise<string
|
|
|
135
140
|
*/
|
|
136
141
|
declare function useSession(event: H3Event, config: H3SessionOptions): Promise<void>;
|
|
137
142
|
|
|
138
|
-
export { type RawSession, Session, type SessionCookie, type SessionCookieOptions, type SessionDataT, type SessionMethods, type SessionStore, UnstorageSessionStore, unsignCookie, useSession };
|
|
143
|
+
export { type H3SessionOptions, type RawSession, Session, type SessionCookie, type SessionCookieOptions, type SessionDataT, type SessionMethods, type SessionStore, UnstorageSessionStore, signCookie, unsignCookie, useSession, validateConfig };
|
package/dist/index.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ declare class UnstorageSessionStore implements SessionStore {
|
|
|
41
41
|
* Get the session with the specified session ID
|
|
42
42
|
* @param sid the un-prefixed session ID
|
|
43
43
|
*/
|
|
44
|
-
get(sid: string): Promise<
|
|
44
|
+
get(sid: string): Promise<SessionDataT | undefined>;
|
|
45
45
|
/**
|
|
46
46
|
* Save the session with the specified session ID
|
|
47
47
|
* If the session has expired (has a TTL <= 0) it is deleted.
|
|
@@ -62,7 +62,7 @@ declare class UnstorageSessionStore implements SessionStore {
|
|
|
62
62
|
/**
|
|
63
63
|
* Fetch all saved sessions.
|
|
64
64
|
*/
|
|
65
|
-
all(): Promise<
|
|
65
|
+
all(): Promise<SessionDataT[]>;
|
|
66
66
|
/**
|
|
67
67
|
* Returns the number of saved sessions
|
|
68
68
|
*/
|
|
@@ -84,9 +84,7 @@ type SessionCookie = SessionCookieOptions & {
|
|
|
84
84
|
};
|
|
85
85
|
interface SessionDataT {
|
|
86
86
|
}
|
|
87
|
-
type RawSession = SessionDataT
|
|
88
|
-
cookie?: SessionCookieOptions;
|
|
89
|
-
};
|
|
87
|
+
type RawSession = SessionDataT;
|
|
90
88
|
interface SessionStore {
|
|
91
89
|
all?: () => Promise<RawSession[]>;
|
|
92
90
|
destroy: (sid: string) => Promise<void>;
|
|
@@ -128,6 +126,13 @@ declare module 'h3' {
|
|
|
128
126
|
* @param secrets an array of secret strings to verify with
|
|
129
127
|
*/
|
|
130
128
|
declare function unsignCookie(value: string, secrets: string[]): Promise<string | false>;
|
|
129
|
+
/**
|
|
130
|
+
* Creates a signed cookie value in the format `s:[value].[signature]`
|
|
131
|
+
* @param value
|
|
132
|
+
* @param secret
|
|
133
|
+
*/
|
|
134
|
+
declare function signCookie(value: string, secret: string): Promise<string>;
|
|
135
|
+
declare function validateConfig(config: H3SessionOptions): void;
|
|
131
136
|
/**
|
|
132
137
|
* Attach a session to an H3Event
|
|
133
138
|
* @param event
|
|
@@ -135,4 +140,4 @@ declare function unsignCookie(value: string, secrets: string[]): Promise<string
|
|
|
135
140
|
*/
|
|
136
141
|
declare function useSession(event: H3Event, config: H3SessionOptions): Promise<void>;
|
|
137
142
|
|
|
138
|
-
export { type RawSession, Session, type SessionCookie, type SessionCookieOptions, type SessionDataT, type SessionMethods, type SessionStore, UnstorageSessionStore, unsignCookie, useSession };
|
|
143
|
+
export { type H3SessionOptions, type RawSession, Session, type SessionCookie, type SessionCookieOptions, type SessionDataT, type SessionMethods, type SessionStore, UnstorageSessionStore, signCookie, unsignCookie, useSession, validateConfig };
|
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,6 @@ import { Buffer } from 'node:buffer';
|
|
|
2
2
|
import { getCookie, setCookie } from 'h3';
|
|
3
3
|
import { defu } from 'defu';
|
|
4
4
|
import { subtle, randomUUID } from 'uncrypto';
|
|
5
|
-
import { z } from 'zod';
|
|
6
5
|
|
|
7
6
|
class Session {
|
|
8
7
|
#id;
|
|
@@ -21,7 +20,7 @@ class Session {
|
|
|
21
20
|
return this.#id;
|
|
22
21
|
}
|
|
23
22
|
async save() {
|
|
24
|
-
await this.#store.set(this.#id,
|
|
23
|
+
await this.#store.set(this.#id, this.data);
|
|
25
24
|
}
|
|
26
25
|
async reload() {
|
|
27
26
|
this.data = await this.#store.get(this.#id) ?? (await this.#generate()).data;
|
|
@@ -39,15 +38,6 @@ class Session {
|
|
|
39
38
|
}
|
|
40
39
|
}
|
|
41
40
|
|
|
42
|
-
const CookieSchema = z.object({
|
|
43
|
-
domain: z.string().optional(),
|
|
44
|
-
expires: z.coerce.date().optional(),
|
|
45
|
-
httpOnly: z.boolean().optional(),
|
|
46
|
-
maxAge: z.number().optional(),
|
|
47
|
-
path: z.string().optional(),
|
|
48
|
-
sameSite: z.enum(["lax", "none", "strict"]).or(z.boolean()).optional(),
|
|
49
|
-
secure: z.boolean().optional()
|
|
50
|
-
});
|
|
51
41
|
class UnstorageSessionStore {
|
|
52
42
|
storage;
|
|
53
43
|
prefix;
|
|
@@ -70,13 +60,6 @@ class UnstorageSessionStore {
|
|
|
70
60
|
*/
|
|
71
61
|
async get(sid) {
|
|
72
62
|
const item = await this.storage.getItem(this.getKey(sid));
|
|
73
|
-
if (item && item.cookie) {
|
|
74
|
-
try {
|
|
75
|
-
item.cookie = CookieSchema.parse(item.cookie);
|
|
76
|
-
} catch {
|
|
77
|
-
delete item.cookie;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
63
|
return item ?? void 0;
|
|
81
64
|
}
|
|
82
65
|
/**
|
|
@@ -105,7 +88,8 @@ class UnstorageSessionStore {
|
|
|
105
88
|
* Remove all saved sessions
|
|
106
89
|
*/
|
|
107
90
|
async clear() {
|
|
108
|
-
|
|
91
|
+
const keys = await this.getAllKeys();
|
|
92
|
+
await Promise.all(keys.map((k) => this.storage.removeItem(k)));
|
|
109
93
|
}
|
|
110
94
|
/**
|
|
111
95
|
* Fetch all saved sessions.
|
|
@@ -144,12 +128,26 @@ class UnstorageSessionStore {
|
|
|
144
128
|
}
|
|
145
129
|
}
|
|
146
130
|
|
|
131
|
+
const signatureLength = 43;
|
|
132
|
+
function parseCookieValue(value) {
|
|
133
|
+
if (value.length < signatureLength + 3) {
|
|
134
|
+
return void 0;
|
|
135
|
+
}
|
|
136
|
+
const separatorIndex = value.length - signatureLength - 1;
|
|
137
|
+
if (value.charAt(0) !== "s" || value.charAt(1) !== ":" || value.charAt(separatorIndex) !== ".") {
|
|
138
|
+
return void 0;
|
|
139
|
+
}
|
|
140
|
+
return [
|
|
141
|
+
value.substring(2, separatorIndex),
|
|
142
|
+
value.substring(separatorIndex + 1)
|
|
143
|
+
];
|
|
144
|
+
}
|
|
147
145
|
async function unsignCookie(value, secrets) {
|
|
148
|
-
const matches =
|
|
146
|
+
const matches = parseCookieValue(value);
|
|
149
147
|
if (!matches) {
|
|
150
148
|
return false;
|
|
151
149
|
}
|
|
152
|
-
const [
|
|
150
|
+
const [message, signature] = matches;
|
|
153
151
|
const encoder = new TextEncoder();
|
|
154
152
|
const messageUint8Array = encoder.encode(message);
|
|
155
153
|
const signatureUint8Array = Uint8Array.from(Buffer.from(signature, "base64"));
|
|
@@ -175,6 +173,12 @@ async function unsignCookie(value, secrets) {
|
|
|
175
173
|
return false;
|
|
176
174
|
}
|
|
177
175
|
async function signCookie(value, secret) {
|
|
176
|
+
if (!value) {
|
|
177
|
+
throw new Error("[h3-session] No value was provided!");
|
|
178
|
+
}
|
|
179
|
+
if (!secret) {
|
|
180
|
+
throw new Error("[h3-session] No secret was provided!");
|
|
181
|
+
}
|
|
178
182
|
const encoder = new TextEncoder();
|
|
179
183
|
const messageUint8Array = encoder.encode(value);
|
|
180
184
|
const keyUint8Array = encoder.encode(secret);
|
|
@@ -259,18 +263,26 @@ async function useSession(event, config) {
|
|
|
259
263
|
}
|
|
260
264
|
async function createExistingSession(id, data) {
|
|
261
265
|
const cookie = await createSessionCookie(id);
|
|
262
|
-
if (store.touch) {
|
|
266
|
+
if (store.touch && data) {
|
|
263
267
|
await store.touch(id, data);
|
|
264
268
|
}
|
|
265
|
-
event.context.session = new Session(
|
|
269
|
+
event.context.session = new Session(
|
|
270
|
+
id,
|
|
271
|
+
data ?? sessionConfig.generate(),
|
|
272
|
+
store,
|
|
273
|
+
generate,
|
|
274
|
+
cookie
|
|
275
|
+
);
|
|
266
276
|
event.context.sessionId = id;
|
|
267
277
|
}
|
|
268
|
-
if (!sessionId
|
|
278
|
+
if (!sessionId) {
|
|
269
279
|
await createNewSession();
|
|
280
|
+
} else if (!sessionData) {
|
|
281
|
+
await createExistingSession(sessionId);
|
|
270
282
|
} else {
|
|
271
283
|
await createExistingSession(sessionId, sessionData);
|
|
272
284
|
}
|
|
273
285
|
event.context.sessionStore = store;
|
|
274
286
|
}
|
|
275
287
|
|
|
276
|
-
export { Session, UnstorageSessionStore, unsignCookie, useSession };
|
|
288
|
+
export { Session, UnstorageSessionStore, signCookie, unsignCookie, useSession, validateConfig };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scayle/h3-session",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Persistent sessions for h3",
|
|
5
5
|
"author": "SCAYLE Commerce Engine",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,7 +22,11 @@
|
|
|
22
22
|
"format": "dprint check",
|
|
23
23
|
"format:fix": "dprint fmt",
|
|
24
24
|
"lint": "eslint . --format gitlab",
|
|
25
|
-
"lint:fix": "eslint . --fix"
|
|
25
|
+
"lint:fix": "eslint . --fix",
|
|
26
|
+
"test": "vitest run",
|
|
27
|
+
"test:ci": "vitest --run --coverage --reporter=default --reporter=junit --outputFile=./junit.xml",
|
|
28
|
+
"test:watch": "vitest watch",
|
|
29
|
+
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
26
30
|
},
|
|
27
31
|
"peerDependencies": {
|
|
28
32
|
"h3": "^1.10.0"
|
|
@@ -30,18 +34,19 @@
|
|
|
30
34
|
"dependencies": {
|
|
31
35
|
"defu": "^6.1.4",
|
|
32
36
|
"uncrypto": "^0.1.3",
|
|
33
|
-
"unstorage": "^1.10.2"
|
|
34
|
-
"zod": "^3.23.8"
|
|
37
|
+
"unstorage": "^1.10.2"
|
|
35
38
|
},
|
|
36
39
|
"devDependencies": {
|
|
37
|
-
"@arethetypeswrong/cli": "0.17.
|
|
40
|
+
"@arethetypeswrong/cli": "0.17.2",
|
|
38
41
|
"@scayle/eslint-config-storefront": "4.4.0",
|
|
39
|
-
"
|
|
42
|
+
"cookie-es": "1.2.2",
|
|
43
|
+
"dprint": "0.48.0",
|
|
40
44
|
"eslint": "9.17.0",
|
|
41
45
|
"eslint-formatter-gitlab": "5.1.0",
|
|
42
46
|
"h3": "1.13.0",
|
|
43
|
-
"typescript": "5.
|
|
44
|
-
"unbuild": "3.0
|
|
47
|
+
"typescript": "5.7.2",
|
|
48
|
+
"unbuild": "3.2.0",
|
|
49
|
+
"vitest": "2.1.8"
|
|
45
50
|
},
|
|
46
51
|
"volta": {
|
|
47
52
|
"node": "22.12.0"
|