@h3ravel/session 1.29.0-alpha.16 → 2.0.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/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /// <reference path="./app.globals.d.ts" />
2
+ import { ServiceProvider } from "@h3ravel/support";
2
3
  import { IApplication, IHttpContext, ISessionDriver, ISessionManager, SessionDriverBuilder, SessionDriverOption } from "@h3ravel/contracts";
3
4
  import { Command } from "@h3ravel/musket";
4
- import { ServiceProvider } from "@h3ravel/support";
5
5
 
6
6
  //#region src/adapters.d.ts
7
7
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@h3ravel/session",
3
- "version": "1.29.0-alpha.16",
3
+ "version": "2.0.0",
4
4
  "description": "Provides a unified session management layer for h3ravel, with secure encryption, consistent API design, and optional adapters (memory, file, redis, db).",
5
5
  "h3ravel": {
6
6
  "providers": [
@@ -8,9 +8,7 @@
8
8
  ]
9
9
  },
10
10
  "type": "module",
11
- "main": "./dist/index.cjs",
12
11
  "types": "./dist/index.d.ts",
13
- "module": "./dist/index.js",
14
12
  "exports": {
15
13
  ".": {
16
14
  "import": "./dist/index.js",
@@ -43,17 +41,17 @@
43
41
  "session"
44
42
  ],
45
43
  "peerDependencies": {
46
- "@h3ravel/database": "^11.15.0-alpha.16",
47
- "@h3ravel/foundation": "^1.29.0-alpha.16",
48
- "@h3ravel/shared": "^1.29.0-alpha.16"
44
+ "@h3ravel/database": "^11.17.0",
45
+ "@h3ravel/foundation": "^2.0.0",
46
+ "@h3ravel/shared": "^2.0.0"
49
47
  },
50
48
  "devDependencies": {
51
- "@h3ravel/contracts": "^1.29.0-alpha.16",
49
+ "@h3ravel/contracts": "^2.0.0",
52
50
  "typescript": "^6.0.0"
53
51
  },
54
52
  "scripts": {
55
53
  "build": "tsdown --config-loader unrun",
56
- "dev": "tsx watch src/index.ts",
54
+ "dev": "tsdown --watch --config-loader unrun",
57
55
  "start": "node dist/index.js",
58
56
  "lint": "eslint . --ext .ts",
59
57
  "test": "jest --passWithNoTests",
package/dist/index.cjs DELETED
@@ -1,1240 +0,0 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- //#region \0rolldown/runtime.js
3
- var __create = Object.create;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
- key = keys[i];
12
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
- get: ((k) => from[k]).bind(null, key),
14
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
- });
16
- }
17
- return to;
18
- };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
- value: mod,
21
- enumerable: true
22
- }) : target, mod));
23
- //#endregion
24
- let _h3ravel_support = require("@h3ravel/support");
25
- let _h3ravel_database = require("@h3ravel/database");
26
- let crypto$1 = require("crypto");
27
- crypto$1 = __toESM(crypto$1, 1);
28
- let _h3ravel_foundation = require("@h3ravel/foundation");
29
- let _h3ravel_contracts = require("@h3ravel/contracts");
30
- let fs = require("fs");
31
- let path = require("path");
32
- path = __toESM(path, 1);
33
- let _h3ravel_musket = require("@h3ravel/musket");
34
- let h3 = require("h3");
35
- //#region src/Encryption.ts
36
- var Encryption = class {
37
- key;
38
- constructor() {
39
- const appKey = process.env.APP_KEY;
40
- if (!appKey) throw new _h3ravel_foundation.ConfigException("APP_KEY not set in env");
41
- this.key = (0, crypto$1.createHash)("sha256").update(Buffer.from(appKey, "base64")).digest();
42
- }
43
- /**
44
- * Encrypt session data using AES-256-CBC and the APP_KEY.
45
- */
46
- encrypt(value) {
47
- value = typeof value === "string" ? value : JSON.stringify(value);
48
- const iv = crypto$1.default.randomBytes(16);
49
- const cipher = crypto$1.default.createCipheriv("aes-256-cbc", this.key, iv);
50
- const encrypted = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
51
- return iv.toString("hex") + ":" + encrypted.toString("hex");
52
- }
53
- /**
54
- * Decrypt session data.
55
- */
56
- decrypt(value) {
57
- const [ivHex, encryptedHex] = value.split(":");
58
- const iv = Buffer.from(ivHex, "hex");
59
- const encrypted = Buffer.from(encryptedHex, "hex");
60
- const decipher = crypto$1.default.createDecipheriv("aes-256-cbc", this.key, iv);
61
- const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]).toString("utf8");
62
- try {
63
- return JSON.parse(decrypted);
64
- } catch {
65
- return decrypted;
66
- }
67
- }
68
- };
69
- //#endregion
70
- //#region src/FlashBag.ts
71
- /**
72
- * FlashBag
73
- *
74
- * Manages flash data for session management, handling temporary data
75
- * that persists for one request cycle.
76
- */
77
- var FlashBag = class {
78
- /**
79
- * Storage for flash data
80
- *
81
- * Structure:
82
- * {
83
- * new: { key1: value1, key2: value2 },
84
- * old: { key3: value3, key4: value4 }
85
- * }
86
- */
87
- flashData = {
88
- new: {},
89
- old: {}
90
- };
91
- /**
92
- * Flash a value for the next request
93
- *
94
- * @param key Key to store in flash
95
- * @param value Value to be flashed
96
- */
97
- flash(key, value) {
98
- this.flashData.new[key] = value;
99
- }
100
- /**
101
- * Store a temporary value for the current request only
102
- *
103
- * @param key Key to store
104
- * @param value Value to store
105
- */
106
- now(key, value) {
107
- this.flashData.new[key] = value;
108
- }
109
- /**
110
- * Reflash all current flash data for another request cycle
111
- */
112
- reflash() {
113
- this.flashData.old = { ...this.flashData.new };
114
- }
115
- /**
116
- * Keep only specific flash keys for the next request
117
- *
118
- * @param keys Keys to keep
119
- */
120
- keep(keys) {
121
- const keptNew = {};
122
- const keptOld = {};
123
- keys.forEach((key) => {
124
- if (this.flashData.new[key] !== void 0) keptNew[key] = this.flashData.new[key];
125
- if (this.flashData.old[key] !== void 0) keptOld[key] = this.flashData.old[key];
126
- });
127
- this.flashData.new = keptNew;
128
- this.flashData.old = keptOld;
129
- }
130
- /**
131
- * Age flash data at the end of the request
132
- *
133
- * - Removes old flash data
134
- * - Moves new flash data to old
135
- * - Clears new flash data
136
- */
137
- ageFlashData() {
138
- this.flashData.old = {};
139
- this.flashData.old = { ...this.flashData.new };
140
- this.flashData.new = {};
141
- }
142
- /**
143
- * Get a flash value
144
- *
145
- * @param key Key to retrieve
146
- * @param defaultValue Default value if key doesn't exist
147
- * @returns Flash value or default
148
- */
149
- get(key, defaultValue) {
150
- return this.flashData.new[key] ?? this.flashData.old[key] ?? defaultValue;
151
- }
152
- /**
153
- * Check if a flash key exists
154
- *
155
- * @param key Key to check
156
- * @returns Boolean indicating existence
157
- */
158
- has(key) {
159
- return key in this.flashData.new || key in this.flashData.old;
160
- }
161
- /**
162
- * Get all flash data
163
- *
164
- * @returns Combined flash data
165
- */
166
- all() {
167
- return {
168
- ...this.flashData.old,
169
- ...this.flashData.new
170
- };
171
- }
172
- /**
173
- * Get all flash data keys
174
- *
175
- * @returns Combined flash data
176
- */
177
- keys() {
178
- return Object.keys({
179
- ...this.flashData.old,
180
- ...this.flashData.new
181
- });
182
- }
183
- /**
184
- * Get the raww flash data
185
- *
186
- * @returns raw flash data
187
- */
188
- raw() {
189
- return this.flashData;
190
- }
191
- /**
192
- * Clear all flash data
193
- */
194
- clear() {
195
- this.flashData.new = {};
196
- this.flashData.old = {};
197
- }
198
- };
199
- //#endregion
200
- //#region src/drivers/Driver.ts
201
- /**
202
- * Driver
203
- *
204
- * Base Session driver.
205
- */
206
- var Driver = class extends _h3ravel_contracts.ISessionDriver {
207
- encryptor = new Encryption();
208
- sessionId;
209
- flashBag = new FlashBag();
210
- /**
211
- * Save the raw session payload (session + flash)
212
- */
213
- saveRawPayload() {
214
- this.savePayload(Object.assign({}, this.fetchPayload(), { _flash: this.flashBag.raw() }));
215
- }
216
- /**
217
- * Retrieve all data from the session including flash
218
- *
219
- * @returns
220
- */
221
- getAll() {
222
- const payload = this.fetchPayload();
223
- const flash = payload._flash ?? {
224
- old: {},
225
- new: {}
226
- };
227
- return {
228
- ...payload,
229
- ...flash.old,
230
- ...flash.new
231
- };
232
- }
233
- /**
234
- * Retrieve a value from the session
235
- *
236
- * @param key
237
- * @param defaultValue
238
- * @returns
239
- */
240
- get(key, defaultValue) {
241
- return (0, _h3ravel_support.safeDot)(this.getAll(), key) || defaultValue;
242
- }
243
- /**
244
- * Store a value in the session
245
- *
246
- * @param key
247
- * @param value
248
- */
249
- set(value) {
250
- const payload = this.fetchPayload();
251
- Object.assign(payload, value);
252
- return this.savePayload(payload);
253
- }
254
- /**
255
- * Store multiple key/value pairs
256
- *
257
- * @param values
258
- */
259
- put(key, value) {
260
- const payload = this.fetchPayload();
261
- (0, _h3ravel_support.setNested)(payload, key, value);
262
- return this.savePayload(payload);
263
- }
264
- /**
265
- * Append a value to an array key
266
- *
267
- * @param key
268
- * @param value
269
- */
270
- push(key, value) {
271
- const payload = this.fetchPayload();
272
- if (!Array.isArray(payload[key])) payload[key] = [];
273
- payload[key].push(value);
274
- return this.savePayload(payload);
275
- }
276
- /**
277
- * Remove a key from the session
278
- *
279
- * @param key
280
- */
281
- forget(key) {
282
- const payload = this.fetchPayload();
283
- delete payload[key];
284
- return this.savePayload(payload);
285
- }
286
- /**
287
- * Retrieve all session data
288
- *
289
- * @returns
290
- */
291
- all() {
292
- return this.fetchPayload();
293
- }
294
- /**
295
- * Determine if a key exists (even if null).
296
- *
297
- * @param key
298
- * @returns
299
- */
300
- exists(key) {
301
- const data = this.getAll();
302
- return Object.prototype.hasOwnProperty.call(data, key);
303
- }
304
- /**
305
- * Determine if a key has a non-null value.
306
- *
307
- * @param key
308
- * @returns
309
- */
310
- has(key) {
311
- const data = this.getAll();
312
- return data[key] !== void 0 && data[key] !== null;
313
- }
314
- /**
315
- * Get only specific keys.
316
- *
317
- * @param keys
318
- * @returns
319
- */
320
- only(keys) {
321
- const data = this.fetchPayload();
322
- const result = {};
323
- keys.forEach((k) => {
324
- if (k in data) result[k] = data[k];
325
- });
326
- return result;
327
- }
328
- /**
329
- * Return all keys except the specified ones.
330
- *
331
- * @param keys
332
- * @returns
333
- */
334
- except(keys) {
335
- const data = this.fetchPayload();
336
- keys.forEach((k) => delete data[k]);
337
- return data;
338
- }
339
- /**
340
- * Return and delete a key from the session.
341
- *
342
- * @param key
343
- * @param defaultValue
344
- * @returns
345
- */
346
- pull(key, defaultValue = null) {
347
- const data = this.fetchPayload();
348
- const value = data[key] ?? defaultValue;
349
- delete data[key];
350
- this.savePayload(data);
351
- return value;
352
- }
353
- /**
354
- * Increment a numeric value by amount (default 1).
355
- *
356
- * @param key
357
- * @param amount
358
- * @returns
359
- */
360
- increment(key, amount = 1) {
361
- const data = this.fetchPayload();
362
- const newVal = (parseFloat(data[key]) || 0) + amount;
363
- data[key] = newVal;
364
- this.savePayload(data);
365
- return newVal;
366
- }
367
- /**
368
- * Decrement a numeric value by amount (default 1).
369
- *
370
- * @param key
371
- * @param amount
372
- * @returns
373
- */
374
- decrement(key, amount = 1) {
375
- return this.increment(key, -amount);
376
- }
377
- /**
378
- * Flash a value for next request only.
379
- *
380
- * @param key
381
- * @param value
382
- */
383
- flash(key, value) {
384
- this.flashBag.flash(key, value);
385
- this.saveRawPayload();
386
- }
387
- /**
388
- * Reflash all flash data for one more cycle.
389
- *
390
- * @returns
391
- */
392
- reflash() {
393
- this.flashBag.reflash();
394
- this.saveRawPayload();
395
- }
396
- /**
397
- * Keep only selected flash data.
398
- *
399
- * @param keys
400
- * @returns
401
- */
402
- keep(keys) {
403
- this.flashBag.keep(keys);
404
- this.saveRawPayload();
405
- }
406
- /**
407
- * Store a temporary value (flash) for this request only (not persisted)
408
- *
409
- * @param key
410
- * @param value
411
- */
412
- now(key, value) {
413
- this.flashBag.now(key, value);
414
- this.saveRawPayload();
415
- }
416
- /**
417
- * Regenerate session ID and persist data under new ID.
418
- */
419
- regenerate() {
420
- const oldData = this.fetchPayload();
421
- this.sessionId = crypto.randomUUID();
422
- this.savePayload(oldData);
423
- }
424
- /**
425
- * Age flash data at the end of the request lifecycle.
426
- */
427
- ageFlashData() {
428
- const data = this.flashBag.ageFlashData();
429
- this.saveRawPayload();
430
- return data;
431
- }
432
- /**
433
- * Determine if an item is not present in the session.
434
- *
435
- * @param key
436
- * @returns
437
- */
438
- missing(key) {
439
- return !this.exists(key);
440
- }
441
- /**
442
- * Flush all session data
443
- */
444
- flush() {
445
- return this.savePayload({});
446
- }
447
- };
448
- //#endregion
449
- //#region src/drivers/DatabaseDriver.ts
450
- /**
451
- * DatabaseDriver
452
- *
453
- * Stores sessions in a database table. Each session ID maps to a row.
454
- * The `payload` column contains all session key/value pairs as JSON.
455
- */
456
- var DatabaseDriver = class extends Driver {
457
- sessionId;
458
- table;
459
- /**
460
- *
461
- * @param sessionId The current session ID
462
- * @param table
463
- */
464
- constructor(sessionId, table = "sessions") {
465
- super();
466
- this.sessionId = sessionId;
467
- this.table = table;
468
- }
469
- /**
470
- * Get the query builder for this table
471
- */
472
- query() {
473
- return _h3ravel_database.DB.table(this.table).where("id", this.sessionId);
474
- }
475
- /**
476
- * Fetch the session payload
477
- */
478
- async fetchPayload() {
479
- const row = await this.query().first();
480
- if (!row) return {};
481
- try {
482
- const decrypted = this.encryptor.decrypt(row.payload);
483
- return typeof decrypted === "string" ? JSON.parse(decrypted) : decrypted;
484
- } catch {
485
- return {};
486
- }
487
- }
488
- /**
489
- * Save the session payload back to DB
490
- */
491
- async savePayload(payload) {
492
- const now = Math.floor(Date.now() / 1e3);
493
- const exists = await this.query().exists();
494
- const encrypted = this.encryptor.encrypt(JSON.stringify(payload));
495
- if (exists) await this.query().update({
496
- payload: encrypted,
497
- last_activity: now
498
- });
499
- else await _h3ravel_database.DB.table(this.table).insert({
500
- id: this.sessionId,
501
- payload: encrypted,
502
- last_activity: now
503
- });
504
- }
505
- /**
506
- * Retrieve all data from the session including flash
507
- */
508
- async getAll() {
509
- const payload = await this.fetchPayload();
510
- const flash = payload._flash ?? {
511
- old: {},
512
- new: {}
513
- };
514
- return {
515
- ...payload,
516
- ...flash.old,
517
- ...flash.new
518
- };
519
- }
520
- /**
521
- * Get a value from the session
522
- */
523
- async get(key, defaultValue) {
524
- return (0, _h3ravel_support.safeDot)(await this.getAll(), key) || defaultValue;
525
- }
526
- /**
527
- * Set one or multiple session values
528
- */
529
- async set(values) {
530
- const payload = await this.fetchPayload();
531
- Object.assign(payload, values);
532
- await this.savePayload(payload);
533
- }
534
- /**
535
- * Store a single key/value pair
536
- */
537
- async put(key, value) {
538
- const payload = await this.fetchPayload();
539
- (0, _h3ravel_support.setNested)(payload, key, value);
540
- await this.savePayload(payload);
541
- }
542
- /**
543
- * Append a value to an array key
544
- */
545
- async push(key, value) {
546
- const payload = await this.fetchPayload();
547
- if (!Array.isArray(payload[key])) payload[key] = [];
548
- payload[key].push(value);
549
- await this.savePayload(payload);
550
- }
551
- /**
552
- * Forget a session key
553
- */
554
- async forget(key) {
555
- const payload = await this.fetchPayload();
556
- delete payload[key];
557
- await this.savePayload(payload);
558
- }
559
- /**
560
- * Retrieve all session data (excluding flash)
561
- */
562
- async all() {
563
- return this.fetchPayload();
564
- }
565
- /**
566
- * Determine if a key exists (even if null)
567
- */
568
- async exists(key) {
569
- const data = await this.getAll();
570
- return Object.prototype.hasOwnProperty.call(data, key);
571
- }
572
- /**
573
- * Determine if a key has a non-null value
574
- */
575
- async has(key) {
576
- const data = await this.getAll();
577
- return data[key] !== void 0 && data[key] !== null;
578
- }
579
- /**
580
- * Get only specific keys
581
- */
582
- async only(keys) {
583
- const data = await this.fetchPayload();
584
- const result = {};
585
- keys.forEach((k) => {
586
- if (k in data) result[k] = data[k];
587
- });
588
- return result;
589
- }
590
- /**
591
- * Return all except specific keys
592
- */
593
- async except(keys) {
594
- const data = await this.fetchPayload();
595
- keys.forEach((k) => delete data[k]);
596
- return data;
597
- }
598
- /**
599
- * Retrieve and delete a value
600
- */
601
- async pull(key, defaultValue = null) {
602
- const data = await this.fetchPayload();
603
- const value = data[key] ?? defaultValue;
604
- delete data[key];
605
- await this.savePayload(data);
606
- return value;
607
- }
608
- /**
609
- * Increment a numeric value
610
- */
611
- async increment(key, amount = 1) {
612
- const data = await this.fetchPayload();
613
- const newVal = (parseFloat(data[key]) || 0) + amount;
614
- data[key] = newVal;
615
- await this.savePayload(data);
616
- return newVal;
617
- }
618
- /**
619
- * Decrement a numeric value
620
- */
621
- async decrement(key, amount = 1) {
622
- return this.increment(key, -amount);
623
- }
624
- /**
625
- * Flash a value for next request only
626
- */
627
- async flash(key, value) {
628
- this.flashBag.flash(key, value);
629
- }
630
- /**
631
- * Reflash all flash data for one more cycle
632
- */
633
- async reflash() {
634
- this.flashBag.reflash();
635
- }
636
- /**
637
- * Keep only specific flash keys
638
- */
639
- async keep(keys) {
640
- this.flashBag.keep(keys);
641
- }
642
- /**
643
- * Store a temporary value (flash) for this request only (not persisted)
644
- */
645
- async now(key, value) {
646
- this.flashBag.now(key, value);
647
- }
648
- /**
649
- * Regenerate session ID with same data
650
- */
651
- async regenerate() {
652
- const oldData = await this.fetchPayload();
653
- this.sessionId = crypto.randomUUID();
654
- await this.savePayload(oldData);
655
- }
656
- /**
657
- * Check if a key is missing
658
- */
659
- async missing(key) {
660
- return !await this.exists(key);
661
- }
662
- /**
663
- * Flush all session data
664
- */
665
- async flush() {
666
- await this.savePayload({});
667
- }
668
- /**
669
- * Invalidate the session and regenerate
670
- */
671
- async invalidate() {
672
- await _h3ravel_database.DB.table(this.table).where("id", this.sessionId).delete();
673
- this.sessionId = crypto.randomUUID();
674
- this.flashBag = new FlashBag();
675
- await this.savePayload({});
676
- }
677
- /**
678
- * Age flash data at the end of the request lifecycle.
679
- */
680
- async ageFlashData() {
681
- this.flashBag.ageFlashData();
682
- }
683
- };
684
- //#endregion
685
- //#region src/drivers/FileDriver.ts
686
- /**
687
- * FileDriver
688
- *
689
- * Stores session data as encrypted JSON files.
690
- * Each session is stored in its own file named after the session ID.
691
- * Ideal for local development or low-scale deployments.
692
- */
693
- var FileDriver = class extends Driver {
694
- sessionId;
695
- sessionDir;
696
- cwd;
697
- constructor(sessionId, sessionDir = path.default.resolve(".sessions"), cwd = process.cwd()) {
698
- super();
699
- this.sessionId = sessionId;
700
- this.sessionDir = sessionDir;
701
- this.cwd = cwd;
702
- this.sessionDir = path.default.join(this.cwd, sessionDir);
703
- this.sessionId = sessionId;
704
- if (!(0, fs.existsSync)(this.sessionDir)) (0, fs.mkdirSync)(this.sessionDir, { recursive: true });
705
- this.ensureSessionFile();
706
- }
707
- /**
708
- * Ensures the session file exists and is initialized.
709
- */
710
- ensureSessionFile() {
711
- if (!(0, fs.existsSync)(this.sessionFilePath())) this.savePayload({});
712
- }
713
- /**
714
- * Get the absolute path for the current session file.
715
- */
716
- sessionFilePath() {
717
- return path.default.join(this.sessionDir, this.sessionId);
718
- }
719
- /**
720
- * Read raw decrypted payload (including _flash).
721
- */
722
- readRawPayload() {
723
- const file = this.sessionFilePath();
724
- if (!(0, fs.existsSync)(file)) return {};
725
- const content = (0, fs.readFileSync)(file, "utf8");
726
- try {
727
- return this.encryptor.decrypt(content);
728
- } catch {
729
- return {};
730
- }
731
- }
732
- /**
733
- * Fetch decrypted payload and strip out flash metadata.
734
- */
735
- fetchPayload() {
736
- return this.readRawPayload();
737
- }
738
- /**
739
- * Write and encrypt session data to file.
740
- * Always persists flash state.
741
- *
742
- * @param data
743
- */
744
- savePayload(payload) {
745
- (0, fs.writeFileSync)(this.sessionFilePath(), this.encryptor.encrypt(payload), "utf8");
746
- }
747
- /**
748
- * Completely invalidate the current session and regenerate a new one.
749
- */
750
- invalidate() {
751
- const file = this.sessionFilePath();
752
- if ((0, fs.existsSync)(file)) (0, fs.rmSync)(file, { recursive: true });
753
- this.sessionId = crypto.randomUUID();
754
- this.flashBag = new FlashBag();
755
- this.savePayload({});
756
- }
757
- };
758
- //#endregion
759
- //#region src/drivers/MemoryDriver.ts
760
- /**
761
- * MemoryDriver
762
- *
763
- * Lightweight, ephemeral session storage.
764
- * Intended for tests, local development, or short-lived apps.
765
- */
766
- var MemoryDriver = class MemoryDriver extends Driver {
767
- sessionId;
768
- static store = {};
769
- constructor(sessionId) {
770
- super();
771
- this.sessionId = sessionId;
772
- this.sessionId = sessionId;
773
- if (!MemoryDriver.store[this.sessionId]) MemoryDriver.store[this.sessionId] = {};
774
- }
775
- /**
776
- * Fetch and return session payload.
777
- *
778
- * @returns Decrypted and usable payload
779
- */
780
- fetchPayload() {
781
- return { ...MemoryDriver.store[this.sessionId] };
782
- }
783
- /**
784
- * Persist session payload and flash bag state.
785
- *
786
- * @param data
787
- */
788
- savePayload(payload) {
789
- MemoryDriver.store[this.sessionId] = { ...payload };
790
- }
791
- /**
792
- * Invalidate current session and regenerate new session ID.
793
- */
794
- invalidate() {
795
- delete MemoryDriver.store[this.sessionId];
796
- this.sessionId = crypto$1.default.randomUUID();
797
- this.flashBag = new FlashBag();
798
- this.savePayload({});
799
- }
800
- };
801
- //#endregion
802
- //#region src/drivers/RedisDriver.ts
803
- /**
804
- * RedisDriver (placeholder)
805
- */
806
- var RedisDriver = class extends Driver {
807
- sessionId;
808
- redisClient;
809
- prefix;
810
- static store = {};
811
- constructor(sessionId, redisClient, prefix) {
812
- super();
813
- this.sessionId = sessionId;
814
- this.redisClient = redisClient;
815
- this.prefix = prefix;
816
- }
817
- /**
818
- * Fetch and return session payload.
819
- *
820
- * @returns Decrypted and usable payload
821
- */
822
- fetchPayload() {
823
- return {};
824
- }
825
- /**
826
- * Persist session payload and flash bag state.
827
- *
828
- * @param data
829
- */
830
- savePayload(_payload) {}
831
- /**
832
- * Invalidate current session and regenerate new session ID.
833
- */
834
- invalidate() {
835
- this.flashBag = new FlashBag();
836
- this.savePayload({});
837
- }
838
- };
839
- //#endregion
840
- //#region src/adapters.ts
841
- /**
842
- * FileDriver builder
843
- * constructor(sessionId: string, sessionDir?: string, cwd?: string)
844
- */
845
- const fileBuilder = (sessionId, options = {}) => {
846
- return new FileDriver(sessionId, options.sessionDir ?? options.dir ?? "./storage/sessions", options.cwd ?? process.cwd());
847
- };
848
- /**
849
- * DatabaseDriver builder
850
- * constructor(sessionId: string, table?: string)
851
- */
852
- const dbBuilder = (sessionId, options = {}) => {
853
- const table = options.table ?? "sessions";
854
- return new DatabaseDriver(options.sessionId ?? sessionId, table);
855
- };
856
- /**
857
- * MemoryDriver builder
858
- * constructor(sessionId: string)
859
- */
860
- const memoryBuilder = (sessionId) => {
861
- return new MemoryDriver(sessionId);
862
- };
863
- /**
864
- * RedisDriver builder
865
- * constructor(sessionId: string, redisClient?: RedisClient, prefix?: string)
866
- */
867
- const redisBuilder = (sessionId, options = {}) => {
868
- const client = options.client;
869
- return new RedisDriver(sessionId, client, options.prefix ?? "h3ravel:sessions:");
870
- };
871
- //#endregion
872
- //#region src/Commands/MakeSessionTableCommand.ts
873
- var MakeSessionTableCommand = class extends _h3ravel_musket.Command {
874
- /**
875
- * The name and signature of the console command.
876
- *
877
- * @var string
878
- */
879
- signature = "make:session-table";
880
- /**
881
- * The console command description.
882
- *
883
- * @var string
884
- */
885
- description = "Create a migration for the session database table";
886
- async handle() {
887
- await _h3ravel_database.DB.instance().schema.hasTable("sessions").then(async function(exists) {
888
- if (!exists) return _h3ravel_database.DB.instance().schema.createTable("sessions", (table) => {
889
- table.string("id", 255).primary();
890
- table.bigInteger("user_id").nullable().index();
891
- table.string("ip_address", 45).nullable();
892
- table.text("user_agent").nullable();
893
- table.text("payload", "longtext").nullable();
894
- table.integer("last_activity").index();
895
- });
896
- });
897
- this.info("INFO: session table created successfully.");
898
- }
899
- };
900
- //#endregion
901
- //#region src/SessionStore.ts
902
- /**
903
- * SessionStore (Driver registry)
904
- *
905
- * Register driver builders under a name and then create instances using:
906
- * SessionStore.make('file', sessionId, options)
907
- */
908
- var SessionStore = class {
909
- static registry = /* @__PURE__ */ new Map();
910
- /**
911
- * Register a driver builder under a key (e.g. 'file', 'database', 'memory').
912
- */
913
- static register(name, builder) {
914
- this.registry.set(name, builder);
915
- }
916
- /**
917
- * Create a driver instance for the given sessionId using the named builder.
918
- *
919
- * If driver not found, throws. Options is a simple key/value bag passed to the builder.
920
- */
921
- static make(name, sessionId, options = {}) {
922
- const builder = this.registry.get(name);
923
- if (!builder) throw new Error(`Session driver "${name}" is not registered`);
924
- return builder(sessionId, options);
925
- }
926
- };
927
- //#endregion
928
- //#region src/SessionManager.ts
929
- /**
930
- * SessionManager
931
- *
932
- * Handles session initialization, ID generation, and encryption.
933
- * Each request gets a unique session namespace tied to its ID.
934
- */
935
- var SessionManager = class SessionManager extends _h3ravel_contracts.ISessionManager {
936
- app;
937
- ctx;
938
- driver;
939
- appKey;
940
- sessionId;
941
- request;
942
- flashBag;
943
- constructor(app, driverName = "file", driverOptions = {}) {
944
- super();
945
- this.appKey = process.env.APP_KEY;
946
- if (app instanceof _h3ravel_contracts.IHttpContext) {
947
- this.request = app.request;
948
- this.ctx = app;
949
- this.app = app.app;
950
- } else {
951
- this.app = app;
952
- this.ctx = app.make("http.context");
953
- this.request = this.ctx.request;
954
- }
955
- this.sessionId = this.resolveSessionId();
956
- this.driver = SessionStore.make(driverName, driverOptions.sessionId ?? this.sessionId, driverOptions);
957
- this.flashBag = this.driver.flashBag;
958
- }
959
- /**
960
- * Initialize the Session Manager
961
- *
962
- * @param ctx
963
- * @returns
964
- */
965
- static init(app) {
966
- return new SessionManager(app, config("session.driver", "file"), {
967
- cwd: config("session.files"),
968
- sessionDir: "/",
969
- dir: "/",
970
- table: config("session.table"),
971
- prefix: config("database.connections.redis.options.prefix"),
972
- client: config(`database.connections.${config("session.driver", "file")}.client`)
973
- });
974
- }
975
- /**
976
- * Generate a secure session ID unique to the user device.
977
- */
978
- generateSessionId() {
979
- const userAgent = this.request.getHeader("user-agent") || "";
980
- const ip = this.request.getHeader("x-forwarded-for") || this.request.ip() || "";
981
- const random = (0, crypto$1.randomBytes)(32).toString("hex");
982
- const fingerprint = (0, crypto$1.createHash)("sha256").update(`${userAgent}-${ip}`).digest("hex");
983
- return (0, crypto$1.createHmac)("sha256", this.appKey).update(`${fingerprint}-${random}`).digest("hex");
984
- }
985
- /**
986
- * Resolve the session ID from cookie, header, or create a new one.
987
- */
988
- resolveSessionId() {
989
- const cookieSession = (0, h3.getCookie)(this.ctx.event, "h3ravel_session");
990
- if (cookieSession) return cookieSession;
991
- const newId = this.generateSessionId();
992
- (0, h3.setCookie)(this.ctx.event, "h3ravel_session", newId, {
993
- httpOnly: true,
994
- secure: true,
995
- sameSite: "lax",
996
- maxAge: 3600 * 24 * 7
997
- });
998
- return newId;
999
- }
1000
- /**
1001
- * Access the current session ID.
1002
- */
1003
- id() {
1004
- return this.sessionId;
1005
- }
1006
- /**
1007
- * Get the current session driver
1008
- */
1009
- getDriver() {
1010
- return this.driver;
1011
- }
1012
- /**
1013
- * Retrieve a value from the session
1014
- *
1015
- * @param key
1016
- * @param defaultValue
1017
- * @returns
1018
- */
1019
- get(key, defaultValue) {
1020
- return this.driver.get(key, defaultValue);
1021
- }
1022
- /**
1023
- * Store a value in the session
1024
- *
1025
- * @param key
1026
- * @param value
1027
- */
1028
- set(value) {
1029
- return this.driver.set(value);
1030
- }
1031
- /**
1032
- * Store multiple key/value pairs
1033
- *
1034
- * @param values
1035
- */
1036
- put(key, value) {
1037
- return this.driver.put(key, value);
1038
- }
1039
- /**
1040
- * Append a value to an array key
1041
- *
1042
- * @param key
1043
- * @param value
1044
- */
1045
- push(key, value) {
1046
- return this.driver.push(key, value);
1047
- }
1048
- /**
1049
- * Remove a key from the session
1050
- *
1051
- * @param key
1052
- */
1053
- forget(key) {
1054
- return this.driver.forget(key);
1055
- }
1056
- /**
1057
- * Retrieve all session data
1058
- *
1059
- * @returns
1060
- */
1061
- all() {
1062
- return this.driver.all();
1063
- }
1064
- /**
1065
- * Determine if a key exists (even if null).
1066
- *
1067
- * @param key
1068
- * @returns
1069
- */
1070
- exists(key) {
1071
- return this.driver.exists(key);
1072
- }
1073
- /**
1074
- * Determine if a key has a non-null value.
1075
- *
1076
- * @param key
1077
- * @returns
1078
- */
1079
- has(key) {
1080
- return this.driver.has(key);
1081
- }
1082
- /**
1083
- * Get only specific keys.
1084
- *
1085
- * @param keys
1086
- * @returns
1087
- */
1088
- only(keys) {
1089
- return this.driver.only(keys);
1090
- }
1091
- /**
1092
- * Return all keys except the specified ones.
1093
- *
1094
- * @param keys
1095
- * @returns
1096
- */
1097
- except(keys) {
1098
- return this.driver.except(keys);
1099
- }
1100
- /**
1101
- * Return and delete a key from the session.
1102
- *
1103
- * @param key
1104
- * @param defaultValue
1105
- * @returns
1106
- */
1107
- pull(key, defaultValue = null) {
1108
- return this.driver.pull(key, defaultValue);
1109
- }
1110
- /**
1111
- * Increment a numeric value by amount (default 1).
1112
- *
1113
- * @param key
1114
- * @param amount
1115
- * @returns
1116
- */
1117
- increment(key, amount = 1) {
1118
- return this.driver.increment(key, amount);
1119
- }
1120
- /**
1121
- * Decrement a numeric value by amount (default 1).
1122
- *
1123
- * @param key
1124
- * @param amount
1125
- * @returns
1126
- */
1127
- decrement(key, amount = 1) {
1128
- return this.driver.decrement(key, amount);
1129
- }
1130
- /**
1131
- * Flash a value for next request only.
1132
- *
1133
- * @param key
1134
- * @param value
1135
- */
1136
- flash(key, value) {
1137
- return this.driver.flash(key, value);
1138
- }
1139
- /**
1140
- * Reflash all flash data for one more cycle.
1141
- *
1142
- * @returns
1143
- */
1144
- reflash() {
1145
- return this.driver.reflash();
1146
- }
1147
- /**
1148
- * Keep only selected flash data.
1149
- *
1150
- * @param keys
1151
- * @returns
1152
- */
1153
- keep(keys) {
1154
- return this.driver.keep(keys);
1155
- }
1156
- /**
1157
- * Store data only for current request cycle (not persisted).
1158
- *
1159
- * @param key
1160
- * @param value
1161
- */
1162
- now(key, value) {
1163
- return this.driver.now(key, value);
1164
- }
1165
- /**
1166
- * Regenerate session ID and persist data under new ID.
1167
- */
1168
- regenerate() {
1169
- return this.driver.regenerate();
1170
- }
1171
- /**
1172
- * Determine if an item is not present in the session.
1173
- *
1174
- * @param key
1175
- * @returns
1176
- */
1177
- missing(key) {
1178
- return this.driver.missing(key);
1179
- }
1180
- /**
1181
- * Flush all session data
1182
- */
1183
- flush() {
1184
- return this.driver.flush();
1185
- }
1186
- /**
1187
- * Invalidate the session completely and regenerate ID.
1188
- *
1189
- * @returns
1190
- */
1191
- invalidate() {
1192
- return this.driver.invalidate();
1193
- }
1194
- /**
1195
- * Age flash data at the end of the request lifecycle.
1196
- *
1197
- * @returns
1198
- */
1199
- ageFlashData() {
1200
- return this.driver.ageFlashData();
1201
- }
1202
- };
1203
- //#endregion
1204
- //#region src/Providers/SessionServiceProvider.ts
1205
- var SessionServiceProvider = class extends _h3ravel_support.ServiceProvider {
1206
- static priority = 895;
1207
- static order = "before:HttpServiceProvider";
1208
- register() {
1209
- /**
1210
- * Register default drivers.
1211
- */
1212
- SessionStore.register("file", fileBuilder);
1213
- SessionStore.register("database", dbBuilder);
1214
- SessionStore.register("memory", memoryBuilder);
1215
- SessionStore.register("redis", redisBuilder);
1216
- this.app.singleton("session", (app) => {
1217
- return SessionManager.init(app);
1218
- });
1219
- this.app.singleton("session.store", (app) => {
1220
- return app.make("session").getDriver();
1221
- });
1222
- this.registerCommands([MakeSessionTableCommand]);
1223
- }
1224
- };
1225
- //#endregion
1226
- exports.DatabaseDriver = DatabaseDriver;
1227
- exports.Driver = Driver;
1228
- exports.Encryption = Encryption;
1229
- exports.FileDriver = FileDriver;
1230
- exports.FlashBag = FlashBag;
1231
- exports.MakeSessionTableCommand = MakeSessionTableCommand;
1232
- exports.MemoryDriver = MemoryDriver;
1233
- exports.RedisDriver = RedisDriver;
1234
- exports.SessionManager = SessionManager;
1235
- exports.SessionServiceProvider = SessionServiceProvider;
1236
- exports.SessionStore = SessionStore;
1237
- exports.dbBuilder = dbBuilder;
1238
- exports.fileBuilder = fileBuilder;
1239
- exports.memoryBuilder = memoryBuilder;
1240
- exports.redisBuilder = redisBuilder;