@adonisjs/session 7.0.0-2 → 7.0.0-3

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 (45) hide show
  1. package/README.md +5 -8
  2. package/build/index.d.ts +5 -3
  3. package/build/index.js +5 -3
  4. package/build/providers/session_provider.d.ts +5 -8
  5. package/build/providers/session_provider.js +13 -35
  6. package/build/src/client.d.ts +13 -25
  7. package/build/src/client.js +24 -43
  8. package/build/src/debug.d.ts +3 -0
  9. package/build/src/debug.js +10 -0
  10. package/build/src/define_config.d.ts +6 -3
  11. package/build/src/define_config.js +34 -5
  12. package/build/src/drivers/cookie.d.ts +7 -9
  13. package/build/src/drivers/cookie.js +10 -6
  14. package/build/src/drivers/file.d.ts +11 -14
  15. package/build/src/drivers/file.js +64 -41
  16. package/build/src/drivers/memory.d.ts +4 -8
  17. package/build/src/drivers/memory.js +0 -3
  18. package/build/src/drivers/redis.d.ts +4 -6
  19. package/build/src/drivers/redis.js +24 -28
  20. package/build/src/drivers_collection.d.ts +22 -0
  21. package/build/src/drivers_collection.js +38 -0
  22. package/build/src/errors.d.ts +8 -0
  23. package/build/src/errors.js +17 -0
  24. package/build/src/helpers.d.ts +6 -0
  25. package/build/src/helpers.js +37 -0
  26. package/build/src/session.d.ts +86 -59
  27. package/build/src/session.js +221 -221
  28. package/build/src/session_middleware.d.ts +19 -5
  29. package/build/src/session_middleware.js +42 -6
  30. package/build/src/store.d.ts +17 -14
  31. package/build/src/store.js +33 -17
  32. package/build/src/types/extended.d.ts +19 -0
  33. package/build/src/types/main.d.ts +106 -0
  34. package/build/src/types/main.js +9 -0
  35. package/package.json +23 -20
  36. package/build/src/bindings/api_client.d.ts +0 -2
  37. package/build/src/bindings/api_client.js +0 -135
  38. package/build/src/bindings/http_context.d.ts +0 -5
  39. package/build/src/bindings/http_context.js +0 -17
  40. package/build/src/bindings/types.d.ts +0 -77
  41. package/build/src/session_manager.d.ts +0 -38
  42. package/build/src/session_manager.js +0 -149
  43. package/build/src/types.d.ts +0 -61
  44. package/build/src/types.js +0 -1
  45. /package/build/src/{bindings/types.js → types/extended.js} +0 -0
@@ -7,28 +7,30 @@
7
7
  * file that was distributed with this source code.
8
8
  */
9
9
  import { dirname, join } from 'node:path';
10
- import { access, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
11
- import { Exception } from '@poppinss/utils';
10
+ import string from '@poppinss/utils/string';
12
11
  import { MessageBuilder } from '@poppinss/utils';
12
+ import { access, mkdir, readFile, rm, writeFile, utimes, stat } from 'node:fs/promises';
13
+ import debug from '../debug.js';
13
14
  /**
14
- * File driver to read/write session to filesystem
15
+ * File driver writes the session data on the file system as. Each session
16
+ * id gets its own file.
15
17
  */
16
18
  export class FileDriver {
17
19
  #config;
18
- constructor(config) {
20
+ #age;
21
+ constructor(config, age) {
19
22
  this.#config = config;
20
- if (!this.#config.file || !this.#config.file.location) {
21
- throw new Exception('Missing "file.location" for session file driver inside "config/session" file', { code: 'E_INVALID_SESSION_DRIVER_CONFIG', status: 500 });
22
- }
23
+ this.#age = age;
24
+ debug('initiating file driver %O', this.#config);
23
25
  }
24
26
  /**
25
- * Returns complete path to the session file
27
+ * Returns an absolute path to the session id file
26
28
  */
27
29
  #getFilePath(sessionId) {
28
- return join(this.#config.file.location, `${sessionId}.txt`);
30
+ return join(this.#config.location, `${sessionId}.txt`);
29
31
  }
30
32
  /**
31
- * Check if the given path exists or not
33
+ * Check if a file exists at a given path or not
32
34
  */
33
35
  async #pathExists(path) {
34
36
  try {
@@ -40,71 +42,92 @@ export class FileDriver {
40
42
  }
41
43
  }
42
44
  /**
43
- * Output file with contents to the given path
45
+ * Returns stats for a file and ignoring missing
46
+ * files.
44
47
  */
45
- async #outputFile(path, content) {
46
- const pathDirname = dirname(path);
47
- const dirExists = await this.#pathExists(pathDirname);
48
- if (!dirExists) {
49
- await mkdir(pathDirname, { recursive: true });
48
+ async #stats(path) {
49
+ try {
50
+ const stats = await stat(path);
51
+ return stats;
52
+ }
53
+ catch {
54
+ return null;
50
55
  }
51
- await writeFile(path, content, 'utf-8');
52
56
  }
53
57
  /**
54
- * Ensure the file exists. Create it if missing
58
+ * Output file with contents to the given path
55
59
  */
56
- async #ensureFile(path) {
60
+ async #outputFile(path, contents) {
57
61
  const pathDirname = dirname(path);
58
62
  const dirExists = await this.#pathExists(pathDirname);
59
63
  if (!dirExists) {
60
64
  await mkdir(pathDirname, { recursive: true });
61
- await writeFile(path, '', 'utf-8');
62
65
  }
66
+ await writeFile(path, contents, 'utf-8');
63
67
  }
64
68
  /**
65
- * Returns file contents. A new file will be created if it's
66
- * missing.
69
+ * Reads the session data from the disk.
67
70
  */
68
71
  async read(sessionId) {
69
72
  const filePath = this.#getFilePath(sessionId);
70
- await this.#ensureFile(filePath);
71
- const contents = await readFile(filePath, 'utf-8');
72
- if (!contents.trim()) {
73
+ debug('file driver: reading session data %', sessionId);
74
+ /**
75
+ * Return null when no session id file exists in first
76
+ * place
77
+ */
78
+ const stats = await this.#stats(filePath);
79
+ if (!stats) {
73
80
  return null;
74
81
  }
75
82
  /**
76
- * Verify contents with the session id and return them as an object.
83
+ * Check if the file has been expired and return null (if expired)
77
84
  */
78
- const verifiedContents = new MessageBuilder().verify(contents.trim(), sessionId);
79
- if (typeof verifiedContents !== 'object') {
85
+ const sessionWillExpireAt = stats.mtimeMs + string.milliseconds.parse(this.#age);
86
+ if (Date.now() > sessionWillExpireAt) {
87
+ debug('file driver: expired session data %s', sessionId);
88
+ return null;
89
+ }
90
+ /**
91
+ * Reading the file contents if the file exists
92
+ */
93
+ let contents = await readFile(filePath, 'utf-8');
94
+ contents = contents.trim();
95
+ if (!contents) {
96
+ return null;
97
+ }
98
+ /**
99
+ * Verify contents with the session id and return them as an object. The verify
100
+ * method can fail when the contents is not JSON>
101
+ */
102
+ try {
103
+ return new MessageBuilder().verify(contents, sessionId);
104
+ }
105
+ catch {
80
106
  return null;
81
107
  }
82
- return verifiedContents;
83
108
  }
84
109
  /**
85
- * Write session values to a file
110
+ * Writes the session data to the disk as a string
86
111
  */
87
112
  async write(sessionId, values) {
88
- if (typeof values !== 'object') {
89
- throw new Error('Session file driver expects an object of values');
90
- }
113
+ debug('file driver: writing session data %s: %O', sessionId, values);
114
+ const filePath = this.#getFilePath(sessionId);
91
115
  const message = new MessageBuilder().build(values, undefined, sessionId);
92
- await this.#outputFile(this.#getFilePath(sessionId), message);
116
+ await this.#outputFile(filePath, message);
93
117
  }
94
118
  /**
95
- * Cleanup session file by removing it
119
+ * Removes the session file from the disk
96
120
  */
97
121
  async destroy(sessionId) {
122
+ debug('file driver: destroying session data %s', sessionId);
98
123
  await rm(this.#getFilePath(sessionId), { force: true });
99
124
  }
100
125
  /**
101
- * Writes the value by reading it from the store
126
+ * Updates the session expiry by rewriting it to the
127
+ * persistence store
102
128
  */
103
129
  async touch(sessionId) {
104
- const value = await this.read(sessionId);
105
- if (!value) {
106
- return;
107
- }
108
- await this.write(sessionId, value);
130
+ debug('file driver: touching session data %s', sessionId);
131
+ await utimes(this.#getFilePath(sessionId), new Date(), new Date());
109
132
  }
110
133
  }
@@ -6,24 +6,20 @@
6
6
  * For the full copyright and license information, please view the LICENSE
7
7
  * file that was distributed with this source code.
8
8
  */
9
- import { SessionDriverContract } from '../types.js';
9
+ import type { SessionData, SessionDriverContract } from '../types/main.js';
10
10
  /**
11
11
  * Memory driver is meant to be used for writing tests.
12
12
  */
13
13
  export declare class MemoryDriver implements SessionDriverContract {
14
- static sessions: Map<string, Object>;
14
+ static sessions: Map<string, SessionData>;
15
15
  /**
16
16
  * Read session id value from the memory
17
17
  */
18
- read(sessionId: string): {
19
- [key: string]: any;
20
- } | null;
18
+ read(sessionId: string): SessionData | null;
21
19
  /**
22
20
  * Save in memory value for a given session id
23
21
  */
24
- write(sessionId: string, values: {
25
- [key: string]: any;
26
- }): void;
22
+ write(sessionId: string, values: SessionData): void;
27
23
  /**
28
24
  * Cleanup for a single session
29
25
  */
@@ -21,9 +21,6 @@ export class MemoryDriver {
21
21
  * Save in memory value for a given session id
22
22
  */
23
23
  write(sessionId, values) {
24
- if (typeof values !== 'object') {
25
- throw new Error('Session memory driver expects an object of values');
26
- }
27
24
  MemoryDriver.sessions.set(sessionId, values);
28
25
  }
29
26
  /**
@@ -6,21 +6,19 @@
6
6
  * For the full copyright and license information, please view the LICENSE
7
7
  * file that was distributed with this source code.
8
8
  */
9
- import type { RedisManagerContract } from '@adonisjs/redis/types';
10
- import type { SessionDriverContract, SessionConfig } from '../types.js';
9
+ import type { RedisService } from '@adonisjs/redis/types';
10
+ import type { SessionDriverContract, RedisDriverConfig, SessionData } from '../types/main.js';
11
11
  /**
12
12
  * File driver to read/write session to filesystem
13
13
  */
14
14
  export declare class RedisDriver implements SessionDriverContract {
15
15
  #private;
16
- constructor(config: SessionConfig, redis: RedisManagerContract<any>);
16
+ constructor(redis: RedisService, config: RedisDriverConfig, age: string | number);
17
17
  /**
18
18
  * Returns file contents. A new file will be created if it's
19
19
  * missing.
20
20
  */
21
- read(sessionId: string): Promise<{
22
- [key: string]: any;
23
- } | null>;
21
+ read(sessionId: string): Promise<SessionData | null>;
24
22
  /**
25
23
  * Write session values to a file
26
24
  */
@@ -6,69 +6,65 @@
6
6
  * For the full copyright and license information, please view the LICENSE
7
7
  * file that was distributed with this source code.
8
8
  */
9
- import { Exception } from '@poppinss/utils';
10
9
  import string from '@poppinss/utils/string';
11
10
  import { MessageBuilder } from '@poppinss/utils';
11
+ import debug from '../debug.js';
12
12
  /**
13
13
  * File driver to read/write session to filesystem
14
14
  */
15
15
  export class RedisDriver {
16
16
  #config;
17
17
  #redis;
18
- #ttl;
19
- constructor(config, redis) {
18
+ #ttlSeconds;
19
+ constructor(redis, config, age) {
20
20
  this.#config = config;
21
21
  this.#redis = redis;
22
- /**
23
- * Convert milliseconds to seconds
24
- */
25
- this.#ttl = Math.round((typeof this.#config.age === 'string'
26
- ? string.milliseconds.parse(this.#config.age)
27
- : this.#config.age) / 1000);
28
- if (!this.#config.redisConnection) {
29
- throw new Exception('Missing redisConnection for session redis driver inside "config/session" file', { code: 'E_INVALID_SESSION_DRIVER_CONFIG', status: 500 });
30
- }
31
- }
32
- /**
33
- * Returns instance of the redis connection
34
- */
35
- #getRedisConnection() {
36
- return this.#redis.connection(this.#config.redisConnection);
22
+ this.#ttlSeconds = string.seconds.parse(age);
23
+ debug('initiating redis driver %O', this.#config);
37
24
  }
38
25
  /**
39
26
  * Returns file contents. A new file will be created if it's
40
27
  * missing.
41
28
  */
42
29
  async read(sessionId) {
43
- const contents = await this.#getRedisConnection().get(sessionId);
30
+ debug('redis driver: reading session data %s', sessionId);
31
+ const contents = await this.#redis.connection(this.#config.connection).get(sessionId);
44
32
  if (!contents) {
45
33
  return null;
46
34
  }
47
- const verifiedContents = new MessageBuilder().verify(contents, sessionId);
48
- if (typeof verifiedContents !== 'object') {
35
+ /**
36
+ * Verify contents with the session id and return them as an object. The verify
37
+ * method can fail when the contents is not JSON>
38
+ */
39
+ try {
40
+ return new MessageBuilder().verify(contents, sessionId);
41
+ }
42
+ catch {
49
43
  return null;
50
44
  }
51
- return verifiedContents;
52
45
  }
53
46
  /**
54
47
  * Write session values to a file
55
48
  */
56
49
  async write(sessionId, values) {
57
- if (typeof values !== 'object') {
58
- throw new Error('Session file driver expects an object of values');
59
- }
60
- await this.#getRedisConnection().setex(sessionId, this.#ttl, new MessageBuilder().build(values, undefined, sessionId));
50
+ debug('redis driver: writing session data %s, %O', sessionId, values);
51
+ const message = new MessageBuilder().build(values, undefined, sessionId);
52
+ await this.#redis
53
+ .connection(this.#config.connection)
54
+ .setex(sessionId, this.#ttlSeconds, message);
61
55
  }
62
56
  /**
63
57
  * Cleanup session file by removing it
64
58
  */
65
59
  async destroy(sessionId) {
66
- await this.#getRedisConnection().del(sessionId);
60
+ debug('redis driver: destroying session data %s', sessionId);
61
+ await this.#redis.connection(this.#config.connection).del(sessionId);
67
62
  }
68
63
  /**
69
64
  * Updates the value expiry
70
65
  */
71
66
  async touch(sessionId) {
72
- await this.#getRedisConnection().expire(sessionId, this.#ttl);
67
+ debug('redis driver: touching session data %s', sessionId);
68
+ await this.#redis.connection(this.#config.connection).expire(sessionId, this.#ttlSeconds);
73
69
  }
74
70
  }
@@ -0,0 +1,22 @@
1
+ import type { HttpContext } from '@adonisjs/core/http';
2
+ import type { SessionDriversList } from './types/main.js';
3
+ /**
4
+ * A global collection of session drivers
5
+ */
6
+ declare class SessionDriversCollection {
7
+ /**
8
+ * List of registered drivers
9
+ */
10
+ list: Partial<SessionDriversList>;
11
+ /**
12
+ * Extend drivers collection and add a custom
13
+ * driver to it.
14
+ */
15
+ extend<Name extends keyof SessionDriversList>(driverName: Name, factoryCallback: SessionDriversList[Name]): this;
16
+ /**
17
+ * Creates the driver instance with config
18
+ */
19
+ create<Name extends keyof SessionDriversList>(name: Name, config: Parameters<SessionDriversList[Name]>[0], ctx: HttpContext): ReturnType<SessionDriversList[Name]>;
20
+ }
21
+ declare const sessionDriversList: SessionDriversCollection;
22
+ export default sessionDriversList;
@@ -0,0 +1,38 @@
1
+ /*
2
+ * @adonisjs/redis
3
+ *
4
+ * (c) AdonisJS
5
+ *
6
+ * For the full copyright and license information, please view the LICENSE
7
+ * file that was distributed with this source code.
8
+ */
9
+ import { RuntimeException } from '@poppinss/utils';
10
+ /**
11
+ * A global collection of session drivers
12
+ */
13
+ class SessionDriversCollection {
14
+ /**
15
+ * List of registered drivers
16
+ */
17
+ list = {};
18
+ /**
19
+ * Extend drivers collection and add a custom
20
+ * driver to it.
21
+ */
22
+ extend(driverName, factoryCallback) {
23
+ this.list[driverName] = factoryCallback;
24
+ return this;
25
+ }
26
+ /**
27
+ * Creates the driver instance with config
28
+ */
29
+ create(name, config, ctx) {
30
+ const driverFactory = this.list[name];
31
+ if (!driverFactory) {
32
+ throw new RuntimeException(`Unknown redis driver "${String(name)}". Make sure the driver is registered`);
33
+ }
34
+ return driverFactory(config, ctx);
35
+ }
36
+ }
37
+ const sessionDriversList = new SessionDriversCollection();
38
+ export default sessionDriversList;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Raised when session store is not mutable
3
+ */
4
+ export declare const E_SESSION_NOT_MUTABLE: new (args?: any, options?: ErrorOptions | undefined) => import("@poppinss/utils").Exception;
5
+ /**
6
+ * Raised when session store has been initiated
7
+ */
8
+ export declare const E_SESSION_NOT_READY: new (args?: any, options?: ErrorOptions | undefined) => import("@poppinss/utils").Exception;
@@ -0,0 +1,17 @@
1
+ /*
2
+ * @adonisjs/session
3
+ *
4
+ * (c) AdonisJS
5
+ *
6
+ * For the full copyright and license information, please view the LICENSE
7
+ * file that was distributed with this source code.
8
+ */
9
+ import { createError } from '@poppinss/utils';
10
+ /**
11
+ * Raised when session store is not mutable
12
+ */
13
+ export const E_SESSION_NOT_MUTABLE = createError('Session store is in readonly mode and cannot be mutated', 'E_SESSION_NOT_MUTABLE', 500);
14
+ /**
15
+ * Raised when session store has been initiated
16
+ */
17
+ export const E_SESSION_NOT_READY = createError('Session store has not been initiated. Make sure you have registered the session middleware', 'E_SESSION_NOT_READY', 500);
@@ -0,0 +1,6 @@
1
+ import type { ApplicationService } from '@adonisjs/core/types';
2
+ import type { SessionDriversList } from './types/main.js';
3
+ /**
4
+ * Lazily imports and registers a driver with the sessionDriversList
5
+ */
6
+ export declare function registerSessionDriver(app: ApplicationService, driverInUse: keyof SessionDriversList): Promise<void>;
@@ -0,0 +1,37 @@
1
+ /*
2
+ * @adonisjs/session
3
+ *
4
+ * (c) AdonisJS
5
+ *
6
+ * For the full copyright and license information, please view the LICENSE
7
+ * file that was distributed with this source code.
8
+ */
9
+ import debug from './debug.js';
10
+ import sessionDriversList from './drivers_collection.js';
11
+ /**
12
+ * Lazily imports and registers a driver with the sessionDriversList
13
+ */
14
+ export async function registerSessionDriver(app, driverInUse) {
15
+ debug('registering %s driver', driverInUse);
16
+ if (driverInUse === 'cookie') {
17
+ const { CookieDriver } = await import('../src/drivers/cookie.js');
18
+ sessionDriversList.extend('cookie', (config, ctx) => new CookieDriver(config.cookie, ctx));
19
+ return;
20
+ }
21
+ if (driverInUse === 'memory') {
22
+ const { MemoryDriver } = await import('../src/drivers/memory.js');
23
+ sessionDriversList.extend('memory', () => new MemoryDriver());
24
+ return;
25
+ }
26
+ if (driverInUse === 'file') {
27
+ const { FileDriver } = await import('../src/drivers/file.js');
28
+ sessionDriversList.extend('file', (config) => new FileDriver(config.file, config.age));
29
+ return;
30
+ }
31
+ if (driverInUse === 'redis') {
32
+ const { RedisDriver } = await import('../src/drivers/redis.js');
33
+ const redis = await app.container.make('redis');
34
+ sessionDriversList.extend('redis', (config) => new RedisDriver(redis, config.redis, config.age));
35
+ return;
36
+ }
37
+ }