@randajan/koa-io-session 2.2.0 → 3.0.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.
@@ -1,70 +1,23 @@
1
+ import {
2
+ _customOptKeys,
3
+ _errPrefix,
4
+ _privs,
5
+ generateUid,
6
+ is,
7
+ ms,
8
+ valid,
9
+ validArray,
10
+ validInterval,
11
+ validObject,
12
+ validRange,
13
+ validStore
14
+ } from "./chunk-NKL2ZYZW.js";
15
+
1
16
  // src/class/SessionBridge.js
2
- import { EventEmitter as EventEmitter2 } from "events";
17
+ import { EventEmitter } from "events";
3
18
  import { ServerResponse } from "http";
4
19
  import { solid as solid2, virtual } from "@randajan/props";
5
20
 
6
- // src/tools.js
7
- import crypto from "crypto";
8
- var generateUid = (len = 16) => crypto.randomBytes(len).toString("base64url").slice(0, len);
9
- var is = (type, any) => typeof any === type;
10
- var valid = (type, any, req = false, msg = "argument") => {
11
- if (any == null) {
12
- if (!req) {
13
- return;
14
- }
15
- throw new Error(`${msg} require typeof '${type}'`);
16
- }
17
- if (is(type, any)) {
18
- return any;
19
- }
20
- throw new Error(`${msg} is not typeof '${type}'`);
21
- };
22
- var validRange = (min, max, any, req = false, msg = "argument") => {
23
- const num = valid("number", any, req, msg);
24
- if (num == null) {
25
- return;
26
- }
27
- if (num < min) {
28
- throw new Error(`${msg} must be greater than ${min}`);
29
- }
30
- if (num > max) {
31
- throw new Error(`${msg} must be less than ${max}`);
32
- }
33
- return num;
34
- };
35
- var validInterval = (any, req = false, msg = "argument") => {
36
- return validRange(10, 2147483647, any, req, msg);
37
- };
38
- var validObject = (any, req = false, msg = "argument") => {
39
- const obj = valid("object", any, req, msg);
40
- if (obj == null) {
41
- return;
42
- }
43
- if (!Array.isArray(obj)) {
44
- return obj;
45
- }
46
- throw new Error(`${msg} must be object, not array`);
47
- };
48
- var validStore = (store) => {
49
- const missing = [];
50
- if (!is("function", store?.get)) {
51
- missing.push("get()");
52
- }
53
- if (!is("function", store?.set)) {
54
- missing.push("set()");
55
- }
56
- if (!is("function", store?.destroy)) {
57
- missing.push("destroy()");
58
- }
59
- if (!is("function", store?.on)) {
60
- missing.push("on()");
61
- }
62
- if (missing.length) {
63
- throw new TypeError(`store is missing required API: ${missing.join(", ")}`);
64
- }
65
- return store;
66
- };
67
-
68
21
  // src/httpSession.js
69
22
  import session from "koa-session";
70
23
 
@@ -88,10 +41,32 @@ var wrapExternalKey = (opt, onSet) => {
88
41
  };
89
42
 
90
43
  // src/httpSession.js
91
- var createKoaSession = (opt, app, onSet) => {
92
- const store = wrapStore(opt.store);
44
+ var ensureAppKeys = (app, keys, allowRnd = false) => {
45
+ if (app.keys) {
46
+ if (!keys) {
47
+ return;
48
+ }
49
+ throw new Error(`${_errPrefix} Cannot set 'appKeys' because app.keys is already defined.`);
50
+ }
51
+ if (keys) {
52
+ app.keys = keys;
53
+ return;
54
+ }
55
+ app.keys = keys = Array(2).fill().map(() => generateUid(32));
56
+ if (!allowRnd) {
57
+ console.warn([
58
+ `${_errPrefix} app.keys were generated at runtime.`,
59
+ `${_errPrefix} Resolve this by adding one of these options to bridge constructor:`,
60
+ `${_errPrefix} 1) "appKeys": ${JSON.stringify(app.keys)}`,
61
+ `${_errPrefix} 2) "allowRndAppKeys": true`
62
+ ].join("\n"));
63
+ }
64
+ };
65
+ var createKoaSession = (app, gw, opt, onSet) => {
66
+ const maxAge = gw.maxAge;
67
+ const store = wrapStore(gw);
93
68
  const externalKey = wrapExternalKey(opt, onSet);
94
- const koaSession = session({ ...opt, store, externalKey }, app);
69
+ const koaSession = session({ ...opt, maxAge, store, externalKey }, app);
95
70
  return [koaSession, externalKey];
96
71
  };
97
72
  var createClientCookie = (opt) => {
@@ -111,41 +86,6 @@ var createClientCookie = (opt) => {
111
86
  // src/socketSession.js
112
87
  import { solids } from "@randajan/props";
113
88
  var sidLocks = /* @__PURE__ */ new Map();
114
- var createSessionCtx = (sessionId, session2, socket) => solids({ session: session2 }, { sessionId, socket });
115
- var createSessionHash = (session2) => {
116
- try {
117
- return JSON.stringify(session2 ?? null);
118
- } catch {
119
- return null;
120
- }
121
- };
122
- var isSessionHashChanged = (originalHash, session2) => {
123
- const nextHash = createSessionHash(session2);
124
- if (originalHash == null || nextHash == null) {
125
- return true;
126
- }
127
- return originalHash !== nextHash;
128
- };
129
- var withLock = async (task, socket, ...args) => {
130
- const sid = socket.sessionId;
131
- const previous = sidLocks.get(sid);
132
- let releaseCurrent;
133
- const current = new Promise((resolve) => {
134
- releaseCurrent = resolve;
135
- });
136
- sidLocks.set(sid, current);
137
- if (previous) {
138
- await previous;
139
- }
140
- try {
141
- return await task(socket, ...args);
142
- } finally {
143
- releaseCurrent();
144
- if (sidLocks.get(sid) === current) {
145
- sidLocks.delete(sid);
146
- }
147
- }
148
- };
149
89
  var applyOnMissing = (onMissing) => {
150
90
  if (onMissing instanceof Error) {
151
91
  throw onMissing;
@@ -155,126 +95,55 @@ var applyOnMissing = (onMissing) => {
155
95
  }
156
96
  return onMissing;
157
97
  };
158
- var runSessionHandler = async (socket, handler, store, onMissing) => {
159
- const sid = socket.sessionId;
160
- const current = await store.get(sid);
161
- if (!current) {
98
+ var runSessionHandler = async (sid, socket, handler, gw, onMissing) => {
99
+ const session2 = await gw.get(sid);
100
+ if (!session2) {
162
101
  return applyOnMissing(onMissing);
163
102
  }
164
- const session2 = current;
165
- const sessionCtx = createSessionCtx(sid, session2, socket);
166
- const originalHash = createSessionHash(sessionCtx.session);
103
+ const sessionCtx = solids({ session: session2 }, { sessionId: sid, socket });
167
104
  const result = await handler(sessionCtx, socket);
168
- if (sessionCtx.session == null) {
169
- await store.destroy(sid);
170
- return result;
171
- }
172
- sessionCtx.session = validObject(sessionCtx.session, false, "session");
173
- if (isSessionHashChanged(originalHash, sessionCtx.session)) {
174
- await store.set(sid, sessionCtx.session);
105
+ if (sid === socket.sessionId) {
106
+ await gw.set(sid, sessionCtx.session);
175
107
  }
176
108
  return result;
177
109
  };
178
- var applySessionHandler = async (socket, handler, store, onMissing) => {
179
- if (typeof handler !== "function") {
180
- throw new TypeError("socket.withSession(handler) requires a function");
181
- }
182
- if (!socket.sessionId) {
183
- return applyOnMissing(onMissing);
184
- }
185
- return withLock(runSessionHandler, socket, handler, store, onMissing);
186
- };
187
-
188
- // src/class/SessionStore.js
189
- import { solid } from "@randajan/props";
190
- import { EventEmitter } from "events";
191
-
192
- // src/const.js
193
- var ms = {
194
- s: (v = 1) => v * 1e3,
195
- m: (v = 1) => ms.s(v * 60),
196
- h: (v = 1) => ms.m(v * 60),
197
- d: (v = 1) => ms.h(v * 24),
198
- w: (v = 1) => ms.d(v * 7),
199
- M: (v = 1) => ms.d(v * 30),
200
- y: (v = 1) => ms.d(v * 365)
201
- };
202
- var _customOptKeys = /* @__PURE__ */ new Set([
203
- "store",
204
- "autoCleanup",
205
- "autoCleanupMs",
206
- "clientKey",
207
- "clientMaxAge",
208
- "clientAlwaysRoll"
209
- ]);
210
-
211
- // src/class/SessionStore.js
212
- var formatState = (session2, maxAge, prevTTL, maxAgeDefault) => {
213
- const ttl = maxAge ?? prevTTL ?? maxAgeDefault;
214
- const expiresAt = Date.now() + ttl;
215
- return { session: session2, expiresAt, ttl };
216
- };
217
- var SessionStore = class extends Map {
218
- constructor(opt = {}) {
219
- super();
220
- const maxAge = validRange(ms.s(), ms.y(), opt.maxAge, false, "maxAge") ?? ms.M();
221
- const autoCleanup = valid("boolean", opt.autoCleanup, false, "autoCleanup") ?? true;
222
- const autoCleanupMs = validInterval(opt.autoCleanupMs, false, "autoCleanupMs") ?? Math.max(ms.s(), Math.min(ms.h(), maxAge / 10));
223
- solid(this, "maxAge", maxAge);
224
- solid(this, "event", new EventEmitter());
225
- if (!autoCleanup) {
226
- return;
227
- }
228
- setInterval((_) => this.cleanup(), autoCleanupMs);
229
- }
230
- on(eventName, callback) {
231
- return this.event.on(eventName, callback);
232
- }
233
- get(sid) {
234
- const d = super.get(sid);
235
- if (!d) {
236
- return;
110
+ var createLock = (sid) => {
111
+ let _release;
112
+ const previous = sidLocks.get(sid);
113
+ const current = new Promise((resolve) => {
114
+ _release = resolve;
115
+ });
116
+ sidLocks.set(sid, current);
117
+ const release = () => {
118
+ _release();
119
+ if (sidLocks.get(sid) === current) {
120
+ sidLocks.delete(sid);
237
121
  }
238
- if (Date.now() < d.expiresAt) {
239
- return d.session;
122
+ };
123
+ return [previous, release];
124
+ };
125
+ var applySessionHandler = async (socket, handler, gw, onMissing) => {
126
+ valid("function", handler, true, "handler");
127
+ for (let i = 0; i < 5; i++) {
128
+ const sid = socket.sessionId;
129
+ if (!sid) {
130
+ return applyOnMissing(onMissing);
240
131
  }
241
- this.delete(sid);
242
- }
243
- set(sid, session2, maxAge) {
244
- const d = super.get(sid);
245
- if (session2 == null) {
246
- return !d || this.destroy(sid);
132
+ const [previous, release] = createLock(sid);
133
+ if (previous) {
134
+ await previous;
247
135
  }
248
- super.set(sid, formatState(session2, maxAge, d?.ttl, this.maxAge));
249
- this.event.emit("set", this, sid, !d);
250
- return true;
251
- }
252
- delete(sid) {
253
- return this.destroy(sid);
254
- }
255
- destroy(sid) {
256
- if (this.has(sid)) {
257
- super.delete(sid);
258
- this.event.emit("destroy", this, sid);
259
- }
260
- return true;
261
- }
262
- cleanup() {
263
- const now = Date.now();
264
- let cleared = 0;
265
- for (const [sid, d] of this.entries()) {
266
- if (now < d.expiresAt) {
267
- continue;
268
- }
269
- if (this.destroy(sid)) {
270
- cleared++;
271
- }
136
+ if (previous && sid !== socket.sessionId) {
137
+ release();
138
+ continue;
272
139
  }
273
- if (cleared) {
274
- this.event.emit("cleanup", this, cleared);
140
+ try {
141
+ return await runSessionHandler(sid, socket, handler, gw, onMissing);
142
+ } finally {
143
+ release();
275
144
  }
276
- return cleared;
277
145
  }
146
+ throw new Error(`${_errPrefix} socket.sessionId changed during withSession execution.`);
278
147
  };
279
148
 
280
149
  // src/formatOptions.js
@@ -286,20 +155,22 @@ var pickKoaOpt = (rawOpt) => {
286
155
  }
287
156
  koaOpt[key] = rawOpt[key];
288
157
  }
289
- koaOpt.key = valid("string", koaOpt.key, false, "key") ?? generateUid(12);
290
- koaOpt.maxAge = validRange(ms.s(), ms.y(), koaOpt.maxAge, false, "maxAge") ?? ms.M();
158
+ koaOpt.key = valid("string", koaOpt.key, false, "key") ?? "sid";
291
159
  koaOpt.signed = valid("boolean", koaOpt.signed, false, "signed") ?? true;
292
- koaOpt.store = validStore(rawOpt.store || new SessionStore(rawOpt));
293
160
  return koaOpt;
294
161
  };
295
162
  var formatOptions = (opt = {}) => {
296
163
  opt = validObject(opt, true, "options");
164
+ const appKeys = validArray(opt.appKeys, false, "appKeys");
165
+ const allowRndAppKeys = valid("boolean", opt.allowRndAppKeys, false, "allowRnddAppKeys") ?? false;
297
166
  const koaOpt = pickKoaOpt(opt);
298
- const clientKey = valid("string", opt.clientKey) ?? `${koaOpt.key}.cid`;
167
+ const clientKey = valid("string", opt.clientKey) ?? "cid";
299
168
  const clientMaxAge = validInterval(opt.clientMaxAge, false, "clientMaxAge") ?? ms.y();
300
169
  const clientAlwaysRoll = valid("boolean", opt.clientAlwaysRoll, false, "clientAlwaysRoll") ?? true;
301
170
  const clientOpt = { ...koaOpt, key: clientKey, maxAge: clientMaxAge };
302
171
  return {
172
+ appKeys,
173
+ allowRndAppKeys,
303
174
  koaOpt,
304
175
  clientOpt,
305
176
  clientAlwaysRoll
@@ -329,7 +200,7 @@ var Bridge = class {
329
200
  }
330
201
  this.c2s.set(cid, sid);
331
202
  this.s2c.set(sid, cid);
332
- this.onSet({ clientId: cid, sessionId: sid });
203
+ this.onSet(cid, sid);
333
204
  return true;
334
205
  }
335
206
  getByCid(cid) {
@@ -348,7 +219,7 @@ var Bridge = class {
348
219
  }
349
220
  this.s2c.delete(sid);
350
221
  this.c2s.delete(cid);
351
- this.onDelete({ clientId: cid, sessionId: sid });
222
+ this.onDelete(cid, sid);
352
223
  return true;
353
224
  }
354
225
  deleteByCid(cid, skipIf) {
@@ -361,30 +232,243 @@ var Bridge = class {
361
232
  }
362
233
  this.c2s.delete(cid);
363
234
  this.s2c.delete(sid);
364
- this.onDelete({ clientId: cid, sessionId: sid });
235
+ this.onDelete(cid, sid);
236
+ return true;
237
+ }
238
+ };
239
+
240
+ // src/class/TempMap.js
241
+ var TempMap = class extends Map {
242
+ constructor(ttl) {
243
+ super();
244
+ const _p = {
245
+ ttl: validInterval(ttl, true, "ttl"),
246
+ ts: /* @__PURE__ */ new Map()
247
+ };
248
+ _privs.set(this, _p);
249
+ }
250
+ set(key, value, overwrite = true) {
251
+ const { ts, ttl } = _privs.get(this);
252
+ if (!overwrite && this.has(key)) {
253
+ return false;
254
+ }
255
+ this.delete(key);
256
+ super.set(key, value);
257
+ const t = setTimeout((_) => {
258
+ this.delete(key);
259
+ }, ttl);
260
+ ts.set(key, t);
261
+ return true;
262
+ }
263
+ get(key, andDelete = false) {
264
+ const v = super.get(key);
265
+ if (andDelete) {
266
+ this.delete(key);
267
+ }
268
+ return v;
269
+ }
270
+ delete(key) {
271
+ if (!super.delete(key)) {
272
+ return false;
273
+ }
274
+ const { ts } = _privs.get(this);
275
+ const t = ts.get(key);
276
+ clearTimeout(t);
277
+ ts.delete(key);
278
+ return true;
279
+ }
280
+ };
281
+
282
+ // src/class/StoreGateway.js
283
+ import { solid } from "@randajan/props";
284
+
285
+ // src/stores/LiveStore.js
286
+ var LiveStore = class extends Map {
287
+ get(sid) {
288
+ return super.get(sid);
289
+ }
290
+ set(sid, state) {
291
+ super.set(sid, state);
292
+ return true;
293
+ }
294
+ destroy(sid) {
295
+ return super.delete(sid);
296
+ }
297
+ list() {
298
+ return this.keys();
299
+ }
300
+ };
301
+
302
+ // src/class/StoreGateway.js
303
+ var formatState = (session2, maxAge, maxAgeDefault) => {
304
+ const ttl = maxAge ?? maxAgeDefault;
305
+ const expiresAt = Date.now() + ttl;
306
+ return { session: session2, expiresAt, ttl };
307
+ };
308
+ var requireList = (store) => {
309
+ if (!store.list) {
310
+ throw new TypeError(`${_errPrefix} store.list() is required`);
311
+ }
312
+ };
313
+ var StoreGateway = class _StoreGateway {
314
+ static is(any) {
315
+ return any instanceof _StoreGateway;
316
+ }
317
+ constructor(opt = {}, emit = {}) {
318
+ const maxAge = validRange(opt.maxAge, ms.m(), ms.y(), false, "maxAge") ?? ms.M();
319
+ const autoCleanup = valid("boolean", opt.autoCleanup, false, "autoCleanup") ?? false;
320
+ solid(this, "emit", emit);
321
+ solid(this, "maxAge", maxAge);
322
+ solid(this, "store", validStore(opt.store, false) ?? new LiveStore());
323
+ if (autoCleanup) {
324
+ this.startAutoCleanup(validInterval(opt.autoCleanupMs, false, "autoCleanupMs"));
325
+ }
326
+ }
327
+ async list() {
328
+ const { store } = this;
329
+ requireList(store);
330
+ return store.list();
331
+ }
332
+ async get(sid) {
333
+ const { store } = this;
334
+ const c = await store.get(sid);
335
+ if (!c) {
336
+ return;
337
+ }
338
+ if (Date.now() >= c.expiresAt) {
339
+ await this.destroy(sid);
340
+ return;
341
+ }
342
+ try {
343
+ return JSON.parse(c.session);
344
+ } catch {
345
+ await this.destroy(sid);
346
+ throw new Error(`${_errPrefix} Invalid session JSON for sid='${sid}'.`);
347
+ }
348
+ ;
349
+ }
350
+ async set(sid, session2, maxAge) {
351
+ const { store } = this;
352
+ const ses = validObject(session2, false, "session");
353
+ if (ses == null) {
354
+ return this.destroy(sid);
355
+ }
356
+ const to = JSON.stringify(ses);
357
+ const from = await store.get(sid, false);
358
+ if (to === from?.session) {
359
+ return true;
360
+ }
361
+ const isOk = await store.set(sid, formatState(to, maxAge, this.maxAge));
362
+ if (isOk === false) {
363
+ return false;
364
+ }
365
+ this.emit.notifySet(sid, !from);
366
+ return true;
367
+ }
368
+ async destroy(sid) {
369
+ const { store } = this;
370
+ const isOk = await store.destroy(sid);
371
+ if (isOk === false) {
372
+ return false;
373
+ }
374
+ this.emit.notifyDestroy(sid);
375
+ return true;
376
+ }
377
+ async cleanup() {
378
+ const { store } = this;
379
+ requireList(store);
380
+ const list = await store.list();
381
+ const now = Date.now();
382
+ let cleared = 0;
383
+ await Promise.all([...list].map(async (sid) => {
384
+ const d = await store.get(sid);
385
+ if (!d) {
386
+ return;
387
+ }
388
+ if (now < d.expiresAt) {
389
+ return;
390
+ }
391
+ if (await this.destroy(sid)) {
392
+ cleared++;
393
+ }
394
+ }));
395
+ if (is("function", store.optimize)) {
396
+ await store.optimize(cleared);
397
+ }
398
+ this.emit.notifyCleanup(cleared);
399
+ return cleared;
400
+ }
401
+ startAutoCleanup(interval) {
402
+ const { store, maxAge, _intId } = this;
403
+ requireList(store);
404
+ const int = validRange(interval, ms.m(), ms.d(), false, "interval") ?? Math.max(ms.m(), Math.min(ms.d(), maxAge / 4));
405
+ clearInterval(_intId);
406
+ this._intId = setInterval((_) => this.cleanup().catch(() => {
407
+ }), int);
408
+ return true;
409
+ }
410
+ stopAutoCleanup() {
411
+ const { _intId } = this;
412
+ if (!_intId) {
413
+ return false;
414
+ }
415
+ clearInterval(_intId);
416
+ delete this._intId;
365
417
  return true;
366
418
  }
367
419
  };
368
420
 
369
421
  // src/class/SessionBridge.js
370
- var SessionBridge = class extends EventEmitter2 {
422
+ var SessionBridge = class extends EventEmitter {
371
423
  constructor(app, io, opt = {}) {
372
424
  super();
373
- if (!app.keys) {
374
- app.keys = Array(6).fill().map(() => generateUid(12));
375
- }
376
- const o = formatOptions(opt);
377
- const { store } = o.koaOpt;
378
- const brg = new Bridge({
379
- onSet: (pair) => this.emit("sessionStart", pair),
380
- onDelete: (pair) => this.emit("sessionEnd", pair)
425
+ const { appKeys, allowRndAppKeys, koaOpt, clientOpt, clientAlwaysRoll } = formatOptions(opt);
426
+ const _p = {};
427
+ ensureAppKeys(app, appKeys, allowRndAppKeys);
428
+ const tmp = new TempMap(5e3);
429
+ const brg = _p.brg = new Bridge({
430
+ onSet: (clientId, sessionId) => {
431
+ const isNew = !!tmp.get(sessionId, true);
432
+ this.emit("sessionSet", { clientId, sessionId, isNew, isInit: true });
433
+ },
434
+ onDelete: (clientId, sessionId) => this.emit("sessionDestroy", { clientId, sessionId })
381
435
  });
382
- const cc = createClientCookie(o.clientOpt);
383
- const [koaSession, sc] = createKoaSession(o.koaOpt, app, (ctx, sid) => {
436
+ _p.notifySet = (sid, isNew) => {
437
+ if (!sid) {
438
+ return;
439
+ }
440
+ const cid = brg.getBySid(sid);
441
+ if (!cid) {
442
+ tmp.set(sid, isNew, false);
443
+ return;
444
+ }
445
+ this.emit("sessionSet", { clientId: cid, sessionId: sid, isNew: !!isNew, isInit: false });
446
+ };
447
+ _p.notifyDestroy = (sid) => {
448
+ if (sid) {
449
+ brg.deleteBySid(sid);
450
+ }
451
+ };
452
+ _p.notifyCleanup = (cleared) => {
453
+ this.emit("cleanup", cleared);
454
+ };
455
+ const gw = _p.gw = new StoreGateway(opt, _p);
456
+ const cc = createClientCookie(clientOpt);
457
+ const [koaSession, sc] = createKoaSession(app, gw, koaOpt, (ctx, sid) => {
384
458
  const cid = cc.get(ctx);
385
459
  brg.set(cid, sid);
386
460
  });
387
- const regenerateSid = async (ctx, cid, reqSid) => {
461
+ const reviveCid = (ctx) => {
462
+ let cid = cc.get(ctx);
463
+ if (!cid) {
464
+ cc.set(ctx, cid = generateUid(24));
465
+ } else if (clientAlwaysRoll) {
466
+ cc.set(ctx, cid);
467
+ }
468
+ return cid;
469
+ };
470
+ const reviveSid = async (ctx, cid) => {
471
+ const reqSid = sc.get(ctx);
388
472
  if (cid == null || reqSid == null) {
389
473
  return;
390
474
  }
@@ -398,21 +482,15 @@ var SessionBridge = class extends EventEmitter2 {
398
482
  if (brg.getBySid(reqSid)) {
399
483
  return;
400
484
  }
401
- if (!await store.get(reqSid)) {
485
+ if (!await gw.get(reqSid)) {
402
486
  return;
403
487
  }
404
488
  brg.set(cid, reqSid);
405
489
  };
406
490
  app.use(koaSession);
407
491
  app.use(async (ctx, next) => {
408
- let cid = cc.get(ctx);
409
- const sid = sc.get(ctx);
410
- if (!cid) {
411
- cc.set(ctx, cid = generateUid(24));
412
- } else if (o.clientAlwaysRoll) {
413
- cc.set(ctx, cid);
414
- }
415
- await regenerateSid(ctx, cid, sid);
492
+ const cid = reviveCid(ctx);
493
+ await reviveSid(ctx, cid);
416
494
  solid2(ctx, "clientId", cid);
417
495
  virtual(ctx, "sessionId", (_) => brg.getByCid(cid));
418
496
  await next();
@@ -423,24 +501,87 @@ var SessionBridge = class extends EventEmitter2 {
423
501
  const ctx = app.createContext(req, res);
424
502
  await koaSession(ctx, async () => {
425
503
  });
426
- const cid = cc.get(ctx);
427
- const sid = sc.get(ctx);
428
- await regenerateSid(ctx, cid, sid);
504
+ const cid = reviveCid(ctx);
505
+ await reviveSid(ctx, cid);
429
506
  solid2(socket, "clientId", cid);
430
507
  virtual(socket, "sessionId", (_) => brg.getByCid(cid));
431
508
  solid2(socket, "withSession", async function(handler, onMissing) {
432
- const onm = arguments.length > 1 ? onMissing : new Error("Session missing");
433
- return applySessionHandler(socket, handler, store, onm);
509
+ const onm = arguments.length > 1 ? onMissing : new Error(`${_errPrefix} Session is missing for this socket.`);
510
+ return applySessionHandler(socket, handler, gw, onm);
434
511
  }, false);
435
512
  await next();
436
513
  });
437
- store.on("destroy", (_store, sid) => {
438
- if (!sid) {
439
- return;
440
- }
441
- brg.deleteBySid(sid);
442
- });
443
- solid2(this, "store", store);
514
+ _privs.set(this, _p);
515
+ }
516
+ async getById(sessionId) {
517
+ const { gw, brg } = _privs.get(this);
518
+ const cid = brg.getBySid(sessionId);
519
+ if (cid) {
520
+ return gw.get(sessionId);
521
+ }
522
+ }
523
+ async destroyById(sessionId) {
524
+ const { gw, brg } = _privs.get(this);
525
+ const cid = brg.getBySid(sessionId);
526
+ if (cid) {
527
+ return gw.destroy(sessionId);
528
+ }
529
+ return false;
530
+ }
531
+ async setById(sessionId, session2, maxAge) {
532
+ const { gw, brg } = _privs.get(this);
533
+ const cid = brg.getBySid(sessionId);
534
+ if (cid) {
535
+ return gw.set(sessionId, session2, maxAge);
536
+ }
537
+ throw new Error(`${_errPrefix} Creating session via setById() is prohibited. sessionId='${sessionId}'.`);
538
+ }
539
+ async getByClientId(clientId) {
540
+ const { gw, brg } = _privs.get(this);
541
+ const sid = brg.getByCid(clientId);
542
+ if (sid) {
543
+ return gw.get(sid);
544
+ }
545
+ }
546
+ async destroyByClientId(clientId) {
547
+ const { gw, brg } = _privs.get(this);
548
+ const sid = brg.getByCid(clientId);
549
+ if (sid) {
550
+ return gw.destroy(sid);
551
+ }
552
+ return false;
553
+ }
554
+ async setByClientId(clientId, session2, maxAge) {
555
+ const { gw, brg } = _privs.get(this);
556
+ const sid = brg.getByCid(clientId);
557
+ if (sid) {
558
+ return gw.set(sid, session2, maxAge);
559
+ }
560
+ throw new Error(`${_errPrefix} Creating session via setByClientId() is prohibited. clientId='${clientId}'.`);
561
+ }
562
+ getSessionId(clientId) {
563
+ return _privs.get(this).brg.getByCid(clientId);
564
+ }
565
+ getClientId(sessionId) {
566
+ return _privs.get(this).brg.getBySid(sessionId);
567
+ }
568
+ async cleanup() {
569
+ return _privs.get(this).gw.cleanup();
570
+ }
571
+ startAutoCleanup(interval) {
572
+ return _privs.get(this).gw.startAutoCleanup(interval);
573
+ }
574
+ stopAutoCleanup() {
575
+ return _privs.get(this).gw.stopAutoCleanup();
576
+ }
577
+ notifyStoreSet(sessionId, isNew) {
578
+ return _privs.get(this).notifySet(sessionId, isNew);
579
+ }
580
+ notifyStoreDestroy(sessionId) {
581
+ return _privs.get(this).notifyDestroy(sessionId);
582
+ }
583
+ notifyStoreCleanup(clearedCount) {
584
+ return _privs.get(this).notifyCleanup(clearedCount);
444
585
  }
445
586
  };
446
587
 
@@ -448,10 +589,11 @@ var SessionBridge = class extends EventEmitter2 {
448
589
  var bridgeSession = (app, io, opt = {}) => new SessionBridge(app, io, opt);
449
590
  var index_default = bridgeSession;
450
591
  export {
592
+ LiveStore,
451
593
  SessionBridge,
452
- SessionStore,
453
594
  bridgeSession,
454
595
  index_default as default,
455
- generateUid
596
+ generateUid,
597
+ ms
456
598
  };
457
599
  //# sourceMappingURL=index.js.map