@scayle/h3-session 0.5.0 → 0.6.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.
Files changed (3) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/index.mjs +53 -23
  3. package/package.json +11 -10
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @scayle/h3-session
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [Performance] Reduce the calls to `importKey` to improve performance.
8
+
9
+ ## 0.5.1
10
+
11
+ ### Patch Changes
12
+
13
+ - 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.
14
+ - Fix the `clear()` method on `UnstorageSessionStore` not clearing sessions
15
+
3
16
  ## 0.5.0
4
17
 
5
18
  ### Minor Changes
package/dist/index.mjs CHANGED
@@ -3,6 +3,41 @@ import { getCookie, setCookie } from 'h3';
3
3
  import { defu } from 'defu';
4
4
  import { subtle, randomUUID } from 'uncrypto';
5
5
 
6
+ const _signKeys = {};
7
+ async function getCryptoSignKey(secret) {
8
+ if (_signKeys[secret]) {
9
+ return _signKeys[secret];
10
+ }
11
+ const encoder = new TextEncoder();
12
+ const keyUint8Array = encoder.encode(secret);
13
+ const cryptoKey = await subtle.importKey(
14
+ "raw",
15
+ keyUint8Array,
16
+ { name: "HMAC", hash: "SHA-256" },
17
+ false,
18
+ ["sign"]
19
+ );
20
+ _signKeys[secret] = cryptoKey;
21
+ return cryptoKey;
22
+ }
23
+ const _verifyKeys = {};
24
+ async function getCryptoVerifyKey(secret) {
25
+ if (_verifyKeys[secret]) {
26
+ return _verifyKeys[secret];
27
+ }
28
+ const encoder = new TextEncoder();
29
+ const keyUint8Array = encoder.encode(secret);
30
+ const cryptoKey = await subtle.importKey(
31
+ "raw",
32
+ keyUint8Array,
33
+ { name: "HMAC", hash: "SHA-256" },
34
+ false,
35
+ ["verify"]
36
+ );
37
+ _verifyKeys[secret] = cryptoKey;
38
+ return cryptoKey;
39
+ }
40
+
6
41
  class Session {
7
42
  #id;
8
43
  #store;
@@ -60,7 +95,7 @@ class UnstorageSessionStore {
60
95
  */
61
96
  async get(sid) {
62
97
  const item = await this.storage.getItem(this.getKey(sid));
63
- return item ?? void 0;
98
+ return item ?? undefined;
64
99
  }
65
100
  /**
66
101
  * Save the session with the specified session ID
@@ -88,7 +123,8 @@ class UnstorageSessionStore {
88
123
  * Remove all saved sessions
89
124
  */
90
125
  async clear() {
91
- return await this.storage.clear(this.prefix);
126
+ const keys = await this.getAllKeys();
127
+ await Promise.all(keys.map((k) => this.storage.removeItem(k)));
92
128
  }
93
129
  /**
94
130
  * Fetch all saved sessions.
@@ -130,11 +166,11 @@ class UnstorageSessionStore {
130
166
  const signatureLength = 43;
131
167
  function parseCookieValue(value) {
132
168
  if (value.length < signatureLength + 3) {
133
- return void 0;
169
+ return undefined;
134
170
  }
135
171
  const separatorIndex = value.length - signatureLength - 1;
136
172
  if (value.charAt(0) !== "s" || value.charAt(1) !== ":" || value.charAt(separatorIndex) !== ".") {
137
- return void 0;
173
+ return undefined;
138
174
  }
139
175
  return [
140
176
  value.substring(2, separatorIndex),
@@ -151,14 +187,7 @@ async function unsignCookie(value, secrets) {
151
187
  const messageUint8Array = encoder.encode(message);
152
188
  const signatureUint8Array = Uint8Array.from(Buffer.from(signature, "base64"));
153
189
  for (let i = 0; i < secrets.length; i++) {
154
- const keyUint8Array = encoder.encode(secrets[i]);
155
- const cryptoKey = await subtle.importKey(
156
- "raw",
157
- keyUint8Array,
158
- { name: "HMAC", hash: "SHA-256" },
159
- false,
160
- ["verify"]
161
- );
190
+ const cryptoKey = await getCryptoVerifyKey(secrets[i]);
162
191
  const result = await subtle.verify(
163
192
  "HMAC",
164
193
  cryptoKey,
@@ -180,14 +209,7 @@ async function signCookie(value, secret) {
180
209
  }
181
210
  const encoder = new TextEncoder();
182
211
  const messageUint8Array = encoder.encode(value);
183
- const keyUint8Array = encoder.encode(secret);
184
- const cryptoKey = await subtle.importKey(
185
- "raw",
186
- keyUint8Array,
187
- { name: "HMAC", hash: "SHA-256" },
188
- false,
189
- ["sign"]
190
- );
212
+ const cryptoKey = await getCryptoSignKey(secret);
191
213
  const signature = await subtle.sign("HMAC", cryptoKey, messageUint8Array);
192
214
  const b64Signature = Buffer.from(signature).toString("base64").replace(/=+$/, "");
193
215
  return `s:${value}.${b64Signature}`;
@@ -262,14 +284,22 @@ async function useSession(event, config) {
262
284
  }
263
285
  async function createExistingSession(id, data) {
264
286
  const cookie = await createSessionCookie(id);
265
- if (store.touch) {
287
+ if (store.touch && data) {
266
288
  await store.touch(id, data);
267
289
  }
268
- event.context.session = new Session(id, data, store, generate, cookie);
290
+ event.context.session = new Session(
291
+ id,
292
+ data ?? sessionConfig.generate(),
293
+ store,
294
+ generate,
295
+ cookie
296
+ );
269
297
  event.context.sessionId = id;
270
298
  }
271
- if (!sessionId || !sessionData) {
299
+ if (!sessionId) {
272
300
  await createNewSession();
301
+ } else if (!sessionData) {
302
+ await createExistingSession(sessionId);
273
303
  } else {
274
304
  await createExistingSession(sessionId, sessionData);
275
305
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scayle/h3-session",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Persistent sessions for h3",
5
5
  "author": "SCAYLE Commerce Engine",
6
6
  "license": "MIT",
@@ -24,7 +24,7 @@
24
24
  "lint": "eslint . --format gitlab",
25
25
  "lint:fix": "eslint . --fix",
26
26
  "test": "vitest run",
27
- "test:ci": "vitest --run --coverage --reporter=default --reporter=junit --outputFile=./junit.xml",
27
+ "test:ci": "vitest --run --coverage --reporter=default --reporter=junit --outputFile=./coverage/junit.xml",
28
28
  "test:watch": "vitest watch",
29
29
  "typecheck": "tsc --noEmit -p tsconfig.json"
30
30
  },
@@ -37,17 +37,18 @@
37
37
  "unstorage": "^1.10.2"
38
38
  },
39
39
  "devDependencies": {
40
- "@arethetypeswrong/cli": "0.17.1",
41
- "@scayle/eslint-config-storefront": "4.4.0",
42
- "dprint": "0.47.6",
43
- "eslint": "9.17.0",
40
+ "@arethetypeswrong/cli": "0.17.3",
41
+ "@scayle/eslint-config-storefront": "4.4.1",
42
+ "cookie-es": "1.2.2",
43
+ "dprint": "0.48.0",
44
+ "eslint": "9.18.0",
44
45
  "eslint-formatter-gitlab": "5.1.0",
45
- "h3": "1.13.0",
46
- "typescript": "5.6.3",
47
- "unbuild": "3.0.1",
46
+ "h3": "1.13.1",
47
+ "typescript": "5.7.3",
48
+ "unbuild": "3.3.1",
48
49
  "vitest": "2.1.8"
49
50
  },
50
51
  "volta": {
51
- "node": "22.12.0"
52
+ "node": "22.13.0"
52
53
  }
53
54
  }