@randajan/koa-io-session 1.0.0 → 2.2.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 +119 -129
- package/dist/cjs/index.cjs +360 -199
- package/dist/cjs/index.cjs.map +4 -4
- package/dist/esm/index.mjs +358 -197
- package/dist/esm/index.mjs.map +4 -4
- package/package.json +3 -4
package/dist/cjs/index.cjs
CHANGED
|
@@ -29,122 +29,128 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
29
29
|
// src/index.js
|
|
30
30
|
var index_exports = {};
|
|
31
31
|
__export(index_exports, {
|
|
32
|
+
SessionBridge: () => SessionBridge,
|
|
32
33
|
SessionStore: () => SessionStore,
|
|
33
|
-
|
|
34
|
+
bridgeSession: () => bridgeSession,
|
|
34
35
|
default: () => index_default,
|
|
35
36
|
generateUid: () => generateUid
|
|
36
37
|
});
|
|
37
38
|
module.exports = __toCommonJS(index_exports);
|
|
38
39
|
|
|
39
|
-
// src/
|
|
40
|
+
// src/class/SessionBridge.js
|
|
41
|
+
var import_events2 = require("events");
|
|
40
42
|
var import_http = require("http");
|
|
41
|
-
var
|
|
42
|
-
var import_props3 = require("@randajan/props");
|
|
43
|
+
var import_props4 = require("@randajan/props");
|
|
43
44
|
|
|
44
45
|
// src/tools.js
|
|
45
46
|
var import_crypto = __toESM(require("crypto"), 1);
|
|
46
47
|
var generateUid = (len = 16) => import_crypto.default.randomBytes(len).toString("base64url").slice(0, len);
|
|
47
|
-
var
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
};
|
|
59
|
-
};
|
|
60
|
-
var formatState = (session2, maxAge, prevTTL, defaultTTL) => {
|
|
61
|
-
const ttl = maxAge ?? prevTTL ?? defaultTTL;
|
|
62
|
-
const expiresAt = Date.now() + ttl;
|
|
63
|
-
return { session: session2, expiresAt, ttl };
|
|
48
|
+
var is = (type, any) => typeof any === type;
|
|
49
|
+
var valid = (type, any, req = false, msg = "argument") => {
|
|
50
|
+
if (any == null) {
|
|
51
|
+
if (!req) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
throw new Error(`${msg} require typeof '${type}'`);
|
|
55
|
+
}
|
|
56
|
+
if (is(type, any)) {
|
|
57
|
+
return any;
|
|
58
|
+
}
|
|
59
|
+
throw new Error(`${msg} is not typeof '${type}'`);
|
|
64
60
|
};
|
|
65
|
-
var
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
(0, import_props.solid)(this, "event", new import_events.EventEmitter(eventEmitterOpt));
|
|
61
|
+
var validRange = (min, max, any, req = false, msg = "argument") => {
|
|
62
|
+
const num = valid("number", any, req, msg);
|
|
63
|
+
if (num == null) {
|
|
64
|
+
return;
|
|
70
65
|
}
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
if (num < min) {
|
|
67
|
+
throw new Error(`${msg} must be greater than ${min}`);
|
|
73
68
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (!d) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
if (Date.now() < d.expiresAt) {
|
|
80
|
-
return d.session;
|
|
81
|
-
}
|
|
82
|
-
this.delete(sid);
|
|
69
|
+
if (num > max) {
|
|
70
|
+
throw new Error(`${msg} must be less than ${max}`);
|
|
83
71
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return
|
|
72
|
+
return num;
|
|
73
|
+
};
|
|
74
|
+
var validInterval = (any, req = false, msg = "argument") => {
|
|
75
|
+
return validRange(10, 2147483647, any, req, msg);
|
|
76
|
+
};
|
|
77
|
+
var validObject = (any, req = false, msg = "argument") => {
|
|
78
|
+
const obj = valid("object", any, req, msg);
|
|
79
|
+
if (obj == null) {
|
|
80
|
+
return;
|
|
93
81
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const d = super.get(sid);
|
|
97
|
-
if (session2 == null) {
|
|
98
|
-
return !d || this.destroy(sid);
|
|
99
|
-
}
|
|
100
|
-
super.set(sid, formatState(session2, maxAge, d?.ttl, defaultTTL));
|
|
101
|
-
this.event.emit("set", this, sid, !d);
|
|
102
|
-
return true;
|
|
82
|
+
if (!Array.isArray(obj)) {
|
|
83
|
+
return obj;
|
|
103
84
|
}
|
|
104
|
-
|
|
105
|
-
|
|
85
|
+
throw new Error(`${msg} must be object, not array`);
|
|
86
|
+
};
|
|
87
|
+
var validStore = (store) => {
|
|
88
|
+
const missing = [];
|
|
89
|
+
if (!is("function", store?.get)) {
|
|
90
|
+
missing.push("get()");
|
|
106
91
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
super.delete(sid);
|
|
110
|
-
this.event.emit("destroy", this, sid);
|
|
111
|
-
}
|
|
112
|
-
return true;
|
|
92
|
+
if (!is("function", store?.set)) {
|
|
93
|
+
missing.push("set()");
|
|
113
94
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
let cleared = 0;
|
|
117
|
-
for (const [sid, d] of this.entries()) {
|
|
118
|
-
if (now < d.expiresAt) {
|
|
119
|
-
continue;
|
|
120
|
-
}
|
|
121
|
-
if (this.destroy(sid)) {
|
|
122
|
-
cleared++;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
if (cleared) {
|
|
126
|
-
this.event.emit("cleanup", this, cleared);
|
|
127
|
-
}
|
|
128
|
-
return cleared;
|
|
95
|
+
if (!is("function", store?.destroy)) {
|
|
96
|
+
missing.push("destroy()");
|
|
129
97
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
const tid = setInterval(() => {
|
|
136
|
-
this.cleanup();
|
|
137
|
-
}, interval);
|
|
138
|
-
return (_) => clearInterval(tid);
|
|
98
|
+
if (!is("function", store?.on)) {
|
|
99
|
+
missing.push("on()");
|
|
100
|
+
}
|
|
101
|
+
if (missing.length) {
|
|
102
|
+
throw new TypeError(`store is missing required API: ${missing.join(", ")}`);
|
|
139
103
|
}
|
|
104
|
+
return store;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// src/httpSession.js
|
|
108
|
+
var import_koa_session = __toESM(require("koa-session"), 1);
|
|
109
|
+
|
|
110
|
+
// src/wrappers.js
|
|
111
|
+
var wrapStore = (store) => {
|
|
112
|
+
return {
|
|
113
|
+
get: store.get.bind(store),
|
|
114
|
+
set: store.set.bind(store),
|
|
115
|
+
destroy: store.destroy.bind(store)
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
var wrapExternalKey = (opt, onSet) => {
|
|
119
|
+
const { externalKey: base, key, signed } = opt;
|
|
120
|
+
const get = base?.get ? (ctx) => base.get(ctx) : (ctx) => ctx.cookies.get(key, { signed });
|
|
121
|
+
const setRaw = base?.set ? (ctx, sid) => base.set(ctx, sid) : (ctx, sid) => ctx.cookies.set(key, sid, opt);
|
|
122
|
+
const set = typeof onSet != "function" ? setRaw : (ctx, sid) => {
|
|
123
|
+
setRaw(ctx, sid);
|
|
124
|
+
onSet(ctx, sid);
|
|
125
|
+
};
|
|
126
|
+
return { get, set };
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// src/httpSession.js
|
|
130
|
+
var createKoaSession = (opt, app, onSet) => {
|
|
131
|
+
const store = wrapStore(opt.store);
|
|
132
|
+
const externalKey = wrapExternalKey(opt, onSet);
|
|
133
|
+
const koaSession = (0, import_koa_session.default)({ ...opt, store, externalKey }, app);
|
|
134
|
+
return [koaSession, externalKey];
|
|
135
|
+
};
|
|
136
|
+
var createClientCookie = (opt) => {
|
|
137
|
+
const { key, maxAge, signed, path, secure, sameSite, httpOnly } = opt;
|
|
138
|
+
return wrapExternalKey({
|
|
139
|
+
key,
|
|
140
|
+
signed,
|
|
141
|
+
maxAge,
|
|
142
|
+
path: path ?? "/",
|
|
143
|
+
secure,
|
|
144
|
+
sameSite,
|
|
145
|
+
httpOnly: httpOnly ?? true,
|
|
146
|
+
overwrite: true
|
|
147
|
+
});
|
|
140
148
|
};
|
|
141
149
|
|
|
142
150
|
// src/socketSession.js
|
|
143
|
-
var
|
|
144
|
-
var import_queue = require("@randajan/queue");
|
|
151
|
+
var import_props = require("@randajan/props");
|
|
145
152
|
var sidLocks = /* @__PURE__ */ new Map();
|
|
146
|
-
var
|
|
147
|
-
var createSessionCtx = (sessionId, session2, socket) => (0, import_props2.solids)({ session: session2 }, { sessionId, socket });
|
|
153
|
+
var createSessionCtx = (sessionId, session2, socket) => (0, import_props.solids)({ session: session2 }, { sessionId, socket });
|
|
148
154
|
var createSessionHash = (session2) => {
|
|
149
155
|
try {
|
|
150
156
|
return JSON.stringify(session2 ?? null);
|
|
@@ -179,150 +185,305 @@ var withLock = async (task, socket, ...args) => {
|
|
|
179
185
|
}
|
|
180
186
|
}
|
|
181
187
|
};
|
|
182
|
-
var
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
return existing;
|
|
186
|
-
}
|
|
187
|
-
const queue = (0, import_queue.createQueue)(async (touchMaxAge) => {
|
|
188
|
-
try {
|
|
189
|
-
await store.touch(sid, touchMaxAge);
|
|
190
|
-
} catch {
|
|
191
|
-
}
|
|
192
|
-
if (!queue.isPending) {
|
|
193
|
-
touchQueues.delete(sid);
|
|
194
|
-
}
|
|
195
|
-
}, { pass: "last", softMs: 1e3, hardMs: 5e3 });
|
|
196
|
-
touchQueues.set(sid, queue);
|
|
197
|
-
return queue;
|
|
198
|
-
};
|
|
199
|
-
var scheduleTouch = (sid, store, maxAge) => {
|
|
200
|
-
if (typeof store?.touch !== "function") {
|
|
201
|
-
return;
|
|
188
|
+
var applyOnMissing = (onMissing) => {
|
|
189
|
+
if (onMissing instanceof Error) {
|
|
190
|
+
throw onMissing;
|
|
202
191
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const queue = touchQueues.get(sid);
|
|
208
|
-
if (!queue) {
|
|
209
|
-
return false;
|
|
210
|
-
}
|
|
211
|
-
queue.flush();
|
|
212
|
-
touchQueues.delete(sid);
|
|
213
|
-
return true;
|
|
192
|
+
if (is("function", onMissing)) {
|
|
193
|
+
return onMissing();
|
|
194
|
+
}
|
|
195
|
+
return onMissing;
|
|
214
196
|
};
|
|
215
|
-
var runSessionHandler = async (socket, handler,
|
|
197
|
+
var runSessionHandler = async (socket, handler, store, onMissing) => {
|
|
216
198
|
const sid = socket.sessionId;
|
|
217
|
-
const { store, maxAge } = opt;
|
|
218
199
|
const current = await store.get(sid);
|
|
219
|
-
if (!
|
|
220
|
-
|
|
200
|
+
if (!current) {
|
|
201
|
+
return applyOnMissing(onMissing);
|
|
221
202
|
}
|
|
222
203
|
const session2 = current;
|
|
223
204
|
const sessionCtx = createSessionCtx(sid, session2, socket);
|
|
224
205
|
const originalHash = createSessionHash(sessionCtx.session);
|
|
225
206
|
const result = await handler(sessionCtx, socket);
|
|
226
207
|
if (sessionCtx.session == null) {
|
|
227
|
-
clearTouchQueue(sid);
|
|
228
208
|
await store.destroy(sid);
|
|
229
209
|
return result;
|
|
230
210
|
}
|
|
231
|
-
|
|
232
|
-
throw new TypeError("sessionCtx.session must be an object or null");
|
|
233
|
-
}
|
|
211
|
+
sessionCtx.session = validObject(sessionCtx.session, false, "session");
|
|
234
212
|
if (isSessionHashChanged(originalHash, sessionCtx.session)) {
|
|
235
|
-
|
|
236
|
-
await store.set(sid, sessionCtx.session, maxAge);
|
|
237
|
-
} else {
|
|
238
|
-
scheduleTouch(sid, store, maxAge);
|
|
213
|
+
await store.set(sid, sessionCtx.session);
|
|
239
214
|
}
|
|
240
215
|
return result;
|
|
241
216
|
};
|
|
242
|
-
var applySessionHandler = async (socket, handler,
|
|
217
|
+
var applySessionHandler = async (socket, handler, store, onMissing) => {
|
|
243
218
|
if (typeof handler !== "function") {
|
|
244
219
|
throw new TypeError("socket.withSession(handler) requires a function");
|
|
245
220
|
}
|
|
246
221
|
if (!socket.sessionId) {
|
|
247
|
-
|
|
222
|
+
return applyOnMissing(onMissing);
|
|
248
223
|
}
|
|
249
|
-
return withLock(runSessionHandler, socket, handler,
|
|
224
|
+
return withLock(runSessionHandler, socket, handler, store, onMissing);
|
|
250
225
|
};
|
|
251
226
|
|
|
252
|
-
// src/
|
|
253
|
-
var
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
227
|
+
// src/class/SessionStore.js
|
|
228
|
+
var import_props2 = require("@randajan/props");
|
|
229
|
+
var import_events = require("events");
|
|
230
|
+
|
|
231
|
+
// src/const.js
|
|
232
|
+
var ms = {
|
|
233
|
+
s: (v = 1) => v * 1e3,
|
|
234
|
+
m: (v = 1) => ms.s(v * 60),
|
|
235
|
+
h: (v = 1) => ms.m(v * 60),
|
|
236
|
+
d: (v = 1) => ms.h(v * 24),
|
|
237
|
+
w: (v = 1) => ms.d(v * 7),
|
|
238
|
+
M: (v = 1) => ms.d(v * 30),
|
|
239
|
+
y: (v = 1) => ms.d(v * 365)
|
|
240
|
+
};
|
|
241
|
+
var _customOptKeys = /* @__PURE__ */ new Set([
|
|
242
|
+
"store",
|
|
243
|
+
"autoCleanup",
|
|
244
|
+
"autoCleanupMs",
|
|
245
|
+
"clientKey",
|
|
246
|
+
"clientMaxAge",
|
|
247
|
+
"clientAlwaysRoll"
|
|
248
|
+
]);
|
|
249
|
+
|
|
250
|
+
// src/class/SessionStore.js
|
|
251
|
+
var formatState = (session2, maxAge, prevTTL, maxAgeDefault) => {
|
|
252
|
+
const ttl = maxAge ?? prevTTL ?? maxAgeDefault;
|
|
253
|
+
const expiresAt = Date.now() + ttl;
|
|
254
|
+
return { session: session2, expiresAt, ttl };
|
|
255
|
+
};
|
|
256
|
+
var SessionStore = class extends Map {
|
|
257
|
+
constructor(opt = {}) {
|
|
258
|
+
super();
|
|
259
|
+
const maxAge = validRange(ms.s(), ms.y(), opt.maxAge, false, "maxAge") ?? ms.M();
|
|
260
|
+
const autoCleanup = valid("boolean", opt.autoCleanup, false, "autoCleanup") ?? true;
|
|
261
|
+
const autoCleanupMs = validInterval(opt.autoCleanupMs, false, "autoCleanupMs") ?? Math.max(ms.s(), Math.min(ms.h(), maxAge / 10));
|
|
262
|
+
(0, import_props2.solid)(this, "maxAge", maxAge);
|
|
263
|
+
(0, import_props2.solid)(this, "event", new import_events.EventEmitter());
|
|
264
|
+
if (!autoCleanup) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
setInterval((_) => this.cleanup(), autoCleanupMs);
|
|
257
268
|
}
|
|
258
|
-
|
|
259
|
-
|
|
269
|
+
on(eventName, callback) {
|
|
270
|
+
return this.event.on(eventName, callback);
|
|
260
271
|
}
|
|
261
|
-
|
|
262
|
-
|
|
272
|
+
get(sid) {
|
|
273
|
+
const d = super.get(sid);
|
|
274
|
+
if (!d) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
if (Date.now() < d.expiresAt) {
|
|
278
|
+
return d.session;
|
|
279
|
+
}
|
|
280
|
+
this.delete(sid);
|
|
263
281
|
}
|
|
264
|
-
|
|
265
|
-
|
|
282
|
+
set(sid, session2, maxAge) {
|
|
283
|
+
const d = super.get(sid);
|
|
284
|
+
if (session2 == null) {
|
|
285
|
+
return !d || this.destroy(sid);
|
|
286
|
+
}
|
|
287
|
+
super.set(sid, formatState(session2, maxAge, d?.ttl, this.maxAge));
|
|
288
|
+
this.event.emit("set", this, sid, !d);
|
|
289
|
+
return true;
|
|
266
290
|
}
|
|
267
|
-
|
|
268
|
-
|
|
291
|
+
delete(sid) {
|
|
292
|
+
return this.destroy(sid);
|
|
269
293
|
}
|
|
270
|
-
|
|
271
|
-
|
|
294
|
+
destroy(sid) {
|
|
295
|
+
if (this.has(sid)) {
|
|
296
|
+
super.delete(sid);
|
|
297
|
+
this.event.emit("destroy", this, sid);
|
|
298
|
+
}
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
cleanup() {
|
|
302
|
+
const now = Date.now();
|
|
303
|
+
let cleared = 0;
|
|
304
|
+
for (const [sid, d] of this.entries()) {
|
|
305
|
+
if (now < d.expiresAt) {
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
if (this.destroy(sid)) {
|
|
309
|
+
cleared++;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
if (cleared) {
|
|
313
|
+
this.event.emit("cleanup", this, cleared);
|
|
314
|
+
}
|
|
315
|
+
return cleared;
|
|
272
316
|
}
|
|
273
317
|
};
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
318
|
+
|
|
319
|
+
// src/formatOptions.js
|
|
320
|
+
var pickKoaOpt = (rawOpt) => {
|
|
321
|
+
const koaOpt = {};
|
|
322
|
+
for (const key in rawOpt) {
|
|
323
|
+
if (_customOptKeys.has(key)) {
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
koaOpt[key] = rawOpt[key];
|
|
327
|
+
}
|
|
328
|
+
koaOpt.key = valid("string", koaOpt.key, false, "key") ?? generateUid(12);
|
|
329
|
+
koaOpt.maxAge = validRange(ms.s(), ms.y(), koaOpt.maxAge, false, "maxAge") ?? ms.M();
|
|
330
|
+
koaOpt.signed = valid("boolean", koaOpt.signed, false, "signed") ?? true;
|
|
331
|
+
koaOpt.store = validStore(rawOpt.store || new SessionStore(rawOpt));
|
|
332
|
+
return koaOpt;
|
|
333
|
+
};
|
|
334
|
+
var formatOptions = (opt = {}) => {
|
|
335
|
+
opt = validObject(opt, true, "options");
|
|
336
|
+
const koaOpt = pickKoaOpt(opt);
|
|
337
|
+
const clientKey = valid("string", opt.clientKey) ?? `${koaOpt.key}.cid`;
|
|
338
|
+
const clientMaxAge = validInterval(opt.clientMaxAge, false, "clientMaxAge") ?? ms.y();
|
|
339
|
+
const clientAlwaysRoll = valid("boolean", opt.clientAlwaysRoll, false, "clientAlwaysRoll") ?? true;
|
|
340
|
+
const clientOpt = { ...koaOpt, key: clientKey, maxAge: clientMaxAge };
|
|
341
|
+
return {
|
|
342
|
+
koaOpt,
|
|
343
|
+
clientOpt,
|
|
344
|
+
clientAlwaysRoll
|
|
345
|
+
};
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
// src/class/Bridge.js
|
|
349
|
+
var import_props3 = require("@randajan/props");
|
|
350
|
+
var Bridge = class {
|
|
351
|
+
constructor(opt = {}) {
|
|
352
|
+
const { onSet, onDelete } = opt;
|
|
353
|
+
(0, import_props3.solids)(this, {
|
|
354
|
+
onSet,
|
|
355
|
+
onDelete,
|
|
356
|
+
s2c: /* @__PURE__ */ new Map(),
|
|
357
|
+
c2s: /* @__PURE__ */ new Map()
|
|
301
358
|
});
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
await next();
|
|
307
|
-
});
|
|
308
|
-
io.on("connection", (socket) => {
|
|
309
|
-
const sid = socket.sessionId;
|
|
310
|
-
if (sid) {
|
|
311
|
-
socket.join(`sessionId:${sid}`);
|
|
359
|
+
}
|
|
360
|
+
set(cid, sid) {
|
|
361
|
+
if (!cid || !sid) {
|
|
362
|
+
return false;
|
|
312
363
|
}
|
|
313
|
-
|
|
314
|
-
|
|
364
|
+
const byCid = this.deleteByCid(cid, sid);
|
|
365
|
+
const bySid = this.deleteBySid(sid, cid);
|
|
366
|
+
if (!byCid && !bySid) {
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
this.c2s.set(cid, sid);
|
|
370
|
+
this.s2c.set(sid, cid);
|
|
371
|
+
this.onSet({ clientId: cid, sessionId: sid });
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
getByCid(cid) {
|
|
375
|
+
return this.c2s.get(cid);
|
|
376
|
+
}
|
|
377
|
+
getBySid(sid) {
|
|
378
|
+
return this.s2c.get(sid);
|
|
379
|
+
}
|
|
380
|
+
deleteBySid(sid, skipIf) {
|
|
381
|
+
const cid = this.getBySid(sid);
|
|
382
|
+
if (!cid) {
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
if (skipIf && cid == skipIf) {
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
this.s2c.delete(sid);
|
|
389
|
+
this.c2s.delete(cid);
|
|
390
|
+
this.onDelete({ clientId: cid, sessionId: sid });
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
deleteByCid(cid, skipIf) {
|
|
394
|
+
const sid = this.getByCid(cid);
|
|
315
395
|
if (!sid) {
|
|
316
|
-
return;
|
|
396
|
+
return true;
|
|
317
397
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
398
|
+
if (skipIf && sid == skipIf) {
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
this.c2s.delete(cid);
|
|
402
|
+
this.s2c.delete(sid);
|
|
403
|
+
this.onDelete({ clientId: cid, sessionId: sid });
|
|
404
|
+
return true;
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
// src/class/SessionBridge.js
|
|
409
|
+
var SessionBridge = class extends import_events2.EventEmitter {
|
|
410
|
+
constructor(app, io, opt = {}) {
|
|
411
|
+
super();
|
|
412
|
+
if (!app.keys) {
|
|
413
|
+
app.keys = Array(6).fill().map(() => generateUid(12));
|
|
414
|
+
}
|
|
415
|
+
const o = formatOptions(opt);
|
|
416
|
+
const { store } = o.koaOpt;
|
|
417
|
+
const brg = new Bridge({
|
|
418
|
+
onSet: (pair) => this.emit("sessionStart", pair),
|
|
419
|
+
onDelete: (pair) => this.emit("sessionEnd", pair)
|
|
420
|
+
});
|
|
421
|
+
const cc = createClientCookie(o.clientOpt);
|
|
422
|
+
const [koaSession, sc] = createKoaSession(o.koaOpt, app, (ctx, sid) => {
|
|
423
|
+
const cid = cc.get(ctx);
|
|
424
|
+
brg.set(cid, sid);
|
|
425
|
+
});
|
|
426
|
+
const regenerateSid = async (ctx, cid, reqSid) => {
|
|
427
|
+
if (cid == null || reqSid == null) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
const brgSid = brg.getByCid(cid);
|
|
431
|
+
if (brgSid == reqSid) {
|
|
432
|
+
return;
|
|
433
|
+
} else if (brgSid) {
|
|
434
|
+
sc.set(ctx, brgSid);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
if (brg.getBySid(reqSid)) {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
if (!await store.get(reqSid)) {
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
brg.set(cid, reqSid);
|
|
444
|
+
};
|
|
445
|
+
app.use(koaSession);
|
|
446
|
+
app.use(async (ctx, next) => {
|
|
447
|
+
let cid = cc.get(ctx);
|
|
448
|
+
const sid = sc.get(ctx);
|
|
449
|
+
if (!cid) {
|
|
450
|
+
cc.set(ctx, cid = generateUid(24));
|
|
451
|
+
} else if (o.clientAlwaysRoll) {
|
|
452
|
+
cc.set(ctx, cid);
|
|
453
|
+
}
|
|
454
|
+
await regenerateSid(ctx, cid, sid);
|
|
455
|
+
(0, import_props4.solid)(ctx, "clientId", cid);
|
|
456
|
+
(0, import_props4.virtual)(ctx, "sessionId", (_) => brg.getByCid(cid));
|
|
457
|
+
await next();
|
|
458
|
+
});
|
|
459
|
+
io.use(async (socket, next) => {
|
|
460
|
+
const req = socket.request;
|
|
461
|
+
const res = req.res ?? socket.response ?? new import_http.ServerResponse(req);
|
|
462
|
+
const ctx = app.createContext(req, res);
|
|
463
|
+
await koaSession(ctx, async () => {
|
|
464
|
+
});
|
|
465
|
+
const cid = cc.get(ctx);
|
|
466
|
+
const sid = sc.get(ctx);
|
|
467
|
+
await regenerateSid(ctx, cid, sid);
|
|
468
|
+
(0, import_props4.solid)(socket, "clientId", cid);
|
|
469
|
+
(0, import_props4.virtual)(socket, "sessionId", (_) => brg.getByCid(cid));
|
|
470
|
+
(0, import_props4.solid)(socket, "withSession", async function(handler, onMissing) {
|
|
471
|
+
const onm = arguments.length > 1 ? onMissing : new Error("Session missing");
|
|
472
|
+
return applySessionHandler(socket, handler, store, onm);
|
|
473
|
+
}, false);
|
|
474
|
+
await next();
|
|
475
|
+
});
|
|
476
|
+
store.on("destroy", (_store, sid) => {
|
|
477
|
+
if (!sid) {
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
brg.deleteBySid(sid);
|
|
481
|
+
});
|
|
482
|
+
(0, import_props4.solid)(this, "store", store);
|
|
483
|
+
}
|
|
324
484
|
};
|
|
325
485
|
|
|
326
486
|
// src/index.js
|
|
327
|
-
var
|
|
487
|
+
var bridgeSession = (app, io, opt = {}) => new SessionBridge(app, io, opt);
|
|
488
|
+
var index_default = bridgeSession;
|
|
328
489
|
//# sourceMappingURL=index.js.map
|