@adonisjs/session 8.0.0-next.1 → 8.0.0-next.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 (38) hide show
  1. package/build/commands/commands.json +1 -0
  2. package/build/commands/main.d.ts +4 -0
  3. package/build/commands/main.js +36 -0
  4. package/build/commands/make_session_table.d.ts +9 -0
  5. package/build/commands/make_session_table.js +13 -0
  6. package/build/{cookie-aLBno-zS.js → cookie--JOJxrtW.js} +1 -1
  7. package/build/database-vbrhCOPd.js +86 -0
  8. package/build/{dynamodb-CU8BrQfU.js → dynamodb-BFVgTQSf.js} +1 -1
  9. package/build/factories/main.js +7 -5
  10. package/build/{file-CNxCs957.js → file-BBU02j4z.js} +1 -1
  11. package/build/index.d.ts +1 -0
  12. package/build/index.js +7 -5
  13. package/build/main-kn40V-hF.js +2 -0
  14. package/build/make/migration/sessions.stub +26 -0
  15. package/build/providers/session_provider.d.ts +2 -1
  16. package/build/providers/session_provider.js +15 -8
  17. package/build/redis-D8D9UtiD.js +91 -0
  18. package/build/{session-Cc1LPXRc.js → session-DZqZLOtR.js} +41 -9
  19. package/build/{session-CBqhcnvJ.js → session-Dd5u7w5Y.js} +38 -11
  20. package/build/session_collection-D9JQJjpq.js +31 -0
  21. package/build/{session_middleware-CS0R7hmq.js → session_middleware-BoOMxNVH.js} +1 -1
  22. package/build/src/client.js +2 -2
  23. package/build/src/define_config.d.ts +2 -1
  24. package/build/src/errors.d.ts +9 -0
  25. package/build/src/plugins/edge.js +1 -1
  26. package/build/src/plugins/japa/api_client.js +2 -2
  27. package/build/src/plugins/japa/browser_client.js +2 -2
  28. package/build/src/session.d.ts +14 -0
  29. package/build/src/session_collection.d.ts +81 -0
  30. package/build/src/session_middleware.js +4 -4
  31. package/build/src/stores/database.d.ts +70 -0
  32. package/build/src/stores/memory.d.ts +19 -2
  33. package/build/src/stores/redis.d.ts +15 -2
  34. package/build/src/types.d.ts +61 -0
  35. package/package.json +37 -23
  36. package/build/redis-Bcjum7z7.js +0 -36
  37. /package/build/{debug-Ba-0Cgn9.js → debug-BZVg83L1.js} +0 -0
  38. /package/build/{values_store-smX0sQBJ.js → values_store-CvR1Sn37.js} +0 -0
@@ -0,0 +1 @@
1
+ {"commands":[{"commandName":"make:session-table","description":"Create a migration for the sessions database table","help":"","namespace":"make","aliases":[],"flags":[],"args":[],"options":{},"filePath":"make_session_table.js"}],"version":1}
@@ -0,0 +1,4 @@
1
+ import { CommandMetaData, Command } from '@adonisjs/ace/types';
2
+
3
+ export function getMetaData(): Promise<CommandMetaData[]>
4
+ export function getCommand(metaData: CommandMetaData): Promise<Command | null>
@@ -0,0 +1,36 @@
1
+ import { readFile } from 'node:fs/promises'
2
+
3
+ /**
4
+ * In-memory cache of commands after they have been loaded
5
+ */
6
+ let commandsMetaData
7
+
8
+ /**
9
+ * Reads the commands from the "./commands.json" file. Since, the commands.json
10
+ * file is generated automatically, we do not have to validate its contents
11
+ */
12
+ export async function getMetaData() {
13
+ if (commandsMetaData) {
14
+ return commandsMetaData
15
+ }
16
+
17
+ const commandsIndex = await readFile(new URL('./commands.json', import.meta.url), 'utf-8')
18
+ commandsMetaData = JSON.parse(commandsIndex).commands
19
+
20
+ return commandsMetaData
21
+ }
22
+
23
+ /**
24
+ * Imports the command by lookingup its path from the commands
25
+ * metadata
26
+ */
27
+ export async function getCommand(metaData) {
28
+ const commands = await getMetaData()
29
+ const command = commands.find(({ commandName }) => metaData.commandName === commandName)
30
+ if (!command) {
31
+ return null
32
+ }
33
+
34
+ const { default: commandConstructor } = await import(new URL(command.filePath, import.meta.url).href)
35
+ return commandConstructor
36
+ }
@@ -0,0 +1,9 @@
1
+ import { BaseCommand } from '@adonisjs/core/ace';
2
+ /**
3
+ * Command to create the sessions table migration
4
+ */
5
+ export default class MakeSessionTable extends BaseCommand {
6
+ static commandName: string;
7
+ static description: string;
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,13 @@
1
+ import { t as stubsRoot } from "../main-kn40V-hF.js";
2
+ import { BaseCommand } from "@adonisjs/core/ace";
3
+ var MakeSessionTable = class extends BaseCommand {
4
+ static commandName = "make:session-table";
5
+ static description = "Create a migration for the sessions database table";
6
+ async run() {
7
+ await (await this.createCodemods()).makeUsingStub(stubsRoot, "make/migration/sessions.stub", { migration: {
8
+ tableName: "sessions",
9
+ prefix: Date.now()
10
+ } });
11
+ }
12
+ };
13
+ export { MakeSessionTable as default };
@@ -1,4 +1,4 @@
1
- import { t as debug_default } from "./debug-Ba-0Cgn9.js";
1
+ import { t as debug_default } from "./debug-BZVg83L1.js";
2
2
  var CookieStore = class {
3
3
  #ctx;
4
4
  #config;
@@ -0,0 +1,86 @@
1
+ import { t as debug_default } from "./debug-BZVg83L1.js";
2
+ import string from "@poppinss/utils/string";
3
+ import { MessageBuilder } from "@adonisjs/core/helpers";
4
+ var DatabaseStore = class {
5
+ #client;
6
+ #tableName;
7
+ #ttlSeconds;
8
+ #gcProbability;
9
+ constructor(client, age, options) {
10
+ this.#client = client;
11
+ this.#tableName = options?.tableName ?? "sessions";
12
+ this.#ttlSeconds = string.seconds.parse(age);
13
+ this.#gcProbability = options?.gcProbability ?? 2;
14
+ debug_default("initiating database store");
15
+ }
16
+ async #collectGarbage() {
17
+ if (this.#gcProbability <= 0) return;
18
+ if (Math.random() * 100 < this.#gcProbability) {
19
+ debug_default("database store: running garbage collection");
20
+ const expiredBefore = new Date(Date.now());
21
+ await this.#client.from(this.#tableName).where("expires_at", "<=", expiredBefore).delete();
22
+ }
23
+ }
24
+ #parseSessionData(contents, sessionId) {
25
+ try {
26
+ return new MessageBuilder().verify(contents, sessionId);
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+ async read(sessionId) {
32
+ debug_default("database store: reading session data %s", sessionId);
33
+ const row = await this.#client.from(this.#tableName).where("id", sessionId).first();
34
+ if (!row) return null;
35
+ const expiresAt = new Date(row.expires_at).getTime();
36
+ if (Date.now() > expiresAt) {
37
+ await this.destroy(sessionId);
38
+ return null;
39
+ }
40
+ return this.#parseSessionData(row.data, sessionId);
41
+ }
42
+ async write(sessionId, values) {
43
+ debug_default("database store: writing session data %s, %O", sessionId, values);
44
+ const message = new MessageBuilder().build(values, void 0, sessionId);
45
+ const expiresAt = new Date(Date.now() + this.#ttlSeconds * 1e3);
46
+ await this.#client.insertQuery().table(this.#tableName).insert({
47
+ id: sessionId,
48
+ data: message,
49
+ expires_at: expiresAt
50
+ }).knexQuery.onConflict("id").merge(["data", "expires_at"]);
51
+ await this.#collectGarbage();
52
+ }
53
+ async destroy(sessionId) {
54
+ debug_default("database store: destroying session data %s", sessionId);
55
+ await this.#client.from(this.#tableName).where("id", sessionId).delete();
56
+ }
57
+ async touch(sessionId) {
58
+ debug_default("database store: touching session data %s", sessionId);
59
+ const expiresAt = new Date(Date.now() + this.#ttlSeconds * 1e3);
60
+ await this.#client.from(this.#tableName).where("id", sessionId).update({ expires_at: expiresAt });
61
+ }
62
+ async tag(sessionId, userId) {
63
+ debug_default("database store: tagging session %s with user %s", sessionId, userId);
64
+ const data = new MessageBuilder().build({}, void 0, sessionId);
65
+ const expiresAt = new Date(Date.now() + this.#ttlSeconds * 1e3);
66
+ await this.#client.insertQuery().table(this.#tableName).insert({
67
+ id: sessionId,
68
+ user_id: userId,
69
+ data,
70
+ expires_at: expiresAt
71
+ }).knexQuery.onConflict("id").merge(["user_id"]);
72
+ }
73
+ #rowToTaggedSession(row) {
74
+ const data = this.#parseSessionData(row.data, row.id);
75
+ if (!data) return null;
76
+ return {
77
+ id: row.id,
78
+ data
79
+ };
80
+ }
81
+ async tagged(userId) {
82
+ debug_default("database store: getting sessions tagged with user %s", userId);
83
+ return (await this.#client.from(this.#tableName).select("id", "data").where("user_id", userId).where("expires_at", ">", /* @__PURE__ */ new Date())).map((row) => this.#rowToTaggedSession(row)).filter((session) => session !== null);
84
+ }
85
+ };
86
+ export { DatabaseStore };
@@ -1,4 +1,4 @@
1
- import { t as debug_default } from "./debug-Ba-0Cgn9.js";
1
+ import { t as debug_default } from "./debug-BZVg83L1.js";
2
2
  import string from "@adonisjs/core/helpers/string";
3
3
  import { MessageBuilder } from "@adonisjs/core/helpers";
4
4
  import { marshall, unmarshall } from "@aws-sdk/util-dynamodb";
@@ -1,8 +1,10 @@
1
- import "../session-CBqhcnvJ.js";
2
- import { t as defineConfig } from "../session-Cc1LPXRc.js";
3
- import "../debug-Ba-0Cgn9.js";
4
- import "../values_store-smX0sQBJ.js";
5
- import { t as SessionMiddleware } from "../session_middleware-CS0R7hmq.js";
1
+ import "../session-Dd5u7w5Y.js";
2
+ import "../main-kn40V-hF.js";
3
+ import { t as defineConfig } from "../session-DZqZLOtR.js";
4
+ import "../debug-BZVg83L1.js";
5
+ import "../values_store-CvR1Sn37.js";
6
+ import "../session_collection-D9JQJjpq.js";
7
+ import { t as SessionMiddleware } from "../session_middleware-BoOMxNVH.js";
6
8
  import { Emitter } from "@adonisjs/core/events";
7
9
  import { AppFactory } from "@adonisjs/core/factories/app";
8
10
  var SessionMiddlewareFactory = class {
@@ -1,4 +1,4 @@
1
- import { t as debug_default } from "./debug-Ba-0Cgn9.js";
1
+ import { t as debug_default } from "./debug-BZVg83L1.js";
2
2
  import string from "@adonisjs/core/helpers/string";
3
3
  import { MessageBuilder } from "@adonisjs/core/helpers";
4
4
  import { dirname, join } from "node:path";
package/build/index.d.ts CHANGED
@@ -11,4 +11,5 @@ export { configure } from './configure.ts';
11
11
  export { Session } from './src/session.ts';
12
12
  export { stubsRoot } from './stubs/main.ts';
13
13
  export { defineConfig, stores } from './src/define_config.ts';
14
+ export { SessionCollection } from './src/session_collection.ts';
14
15
  export { ReadOnlyValuesStore, ValuesStore } from './src/values_store.ts';
package/build/index.js CHANGED
@@ -1,5 +1,7 @@
1
- import { n as errors_exports, t as Session } from "./session-CBqhcnvJ.js";
2
- import { i as stubsRoot, n as stores, r as configure, t as defineConfig } from "./session-Cc1LPXRc.js";
3
- import "./debug-Ba-0Cgn9.js";
4
- import { n as ValuesStore, t as ReadOnlyValuesStore } from "./values_store-smX0sQBJ.js";
5
- export { ReadOnlyValuesStore, Session, ValuesStore, configure, defineConfig, errors_exports as errors, stores, stubsRoot };
1
+ import { r as errors_exports, t as Session } from "./session-Dd5u7w5Y.js";
2
+ import { t as stubsRoot } from "./main-kn40V-hF.js";
3
+ import { n as stores, r as configure, t as defineConfig } from "./session-DZqZLOtR.js";
4
+ import "./debug-BZVg83L1.js";
5
+ import { n as ValuesStore, t as ReadOnlyValuesStore } from "./values_store-CvR1Sn37.js";
6
+ import { t as SessionCollection } from "./session_collection-D9JQJjpq.js";
7
+ export { ReadOnlyValuesStore, Session, SessionCollection, ValuesStore, configure, defineConfig, errors_exports as errors, stores, stubsRoot };
@@ -0,0 +1,2 @@
1
+ const stubsRoot = import.meta.dirname;
2
+ export { stubsRoot as t };
@@ -0,0 +1,26 @@
1
+ {{{
2
+ exports({
3
+ to: app.makePath(
4
+ 'database/migrations',
5
+ `${migration.prefix}_create_${migration.tableName}_table.ts`
6
+ )
7
+ })
8
+ }}}
9
+ import { BaseSchema } from '@adonisjs/lucid/schema'
10
+
11
+ export default class extends BaseSchema {
12
+ protected tableName = '{{ migration.tableName }}'
13
+
14
+ async up() {
15
+ this.schema.createTable(this.tableName, (table) => {
16
+ table.string('id').primary()
17
+ table.text('data').notNullable()
18
+ table.string('user_id').nullable().index()
19
+ table.timestamp('expires_at').notNullable().index()
20
+ })
21
+ }
22
+
23
+ async down() {
24
+ this.schema.dropTable(this.tableName)
25
+ }
26
+ }
@@ -23,6 +23,7 @@ declare module '@adonisjs/core/types' {
23
23
  * AdonisJS application
24
24
  */
25
25
  export default class SessionProvider {
26
+ #private;
26
27
  protected app: ApplicationService;
27
28
  constructor(app: ApplicationService);
28
29
  /**
@@ -31,7 +32,7 @@ export default class SessionProvider {
31
32
  */
32
33
  protected registerEdgePlugin(): Promise<void>;
33
34
  /**
34
- * Registering muddleware
35
+ * Registering bindings
35
36
  */
36
37
  register(): void;
37
38
  /**
@@ -1,7 +1,8 @@
1
- import "../session-CBqhcnvJ.js";
2
- import "../debug-Ba-0Cgn9.js";
3
- import "../values_store-smX0sQBJ.js";
4
- import { t as SessionMiddleware } from "../session_middleware-CS0R7hmq.js";
1
+ import "../session-Dd5u7w5Y.js";
2
+ import "../debug-BZVg83L1.js";
3
+ import "../values_store-CvR1Sn37.js";
4
+ import { t as SessionCollection } from "../session_collection-D9JQJjpq.js";
5
+ import { t as SessionMiddleware } from "../session_middleware-BoOMxNVH.js";
5
6
  import { RuntimeException } from "@adonisjs/core/exceptions";
6
7
  import { configProvider } from "@adonisjs/core";
7
8
  var SessionProvider = class {
@@ -15,12 +16,18 @@ var SessionProvider = class {
15
16
  edge.default.use(edgePluginSession);
16
17
  }
17
18
  }
19
+ async #resolveConfig() {
20
+ const sessionConfigProvider = this.app.config.get("session", {});
21
+ const config = await configProvider.resolve(this.app, sessionConfigProvider);
22
+ if (!config) throw new RuntimeException("Invalid \"config/session.ts\" file. Make sure you are using the \"defineConfig\" method");
23
+ return config;
24
+ }
18
25
  register() {
19
26
  this.app.container.singleton(SessionMiddleware, async (resolver) => {
20
- const sessionConfigProvider = this.app.config.get("session", {});
21
- const config = await configProvider.resolve(this.app, sessionConfigProvider);
22
- if (!config) throw new RuntimeException("Invalid \"config/session.ts\" file. Make sure you are using the \"defineConfig\" method");
23
- return new SessionMiddleware(config, await resolver.make("emitter"));
27
+ return new SessionMiddleware(await this.#resolveConfig(), await resolver.make("emitter"));
28
+ });
29
+ this.app.container.singleton(SessionCollection, async () => {
30
+ return new SessionCollection(await this.#resolveConfig());
24
31
  });
25
32
  }
26
33
  async boot() {
@@ -0,0 +1,91 @@
1
+ import { t as debug_default } from "./debug-BZVg83L1.js";
2
+ import string from "@adonisjs/core/helpers/string";
3
+ import { MessageBuilder } from "@adonisjs/core/helpers";
4
+ var RedisStore = class {
5
+ #connection;
6
+ #ttlSeconds;
7
+ constructor(connection, age) {
8
+ this.#connection = connection;
9
+ this.#ttlSeconds = string.seconds.parse(age);
10
+ debug_default("initiating redis store");
11
+ }
12
+ #processSessionResult(options) {
13
+ if (!options.contents) return {
14
+ session: null,
15
+ isInvalid: true
16
+ };
17
+ const data = this.#parseSessionData(options.contents, options.sessionId);
18
+ if (!data) return {
19
+ session: null,
20
+ isInvalid: true
21
+ };
22
+ return {
23
+ session: {
24
+ id: options.sessionId,
25
+ data
26
+ },
27
+ isInvalid: false
28
+ };
29
+ }
30
+ async #fetchSessionContents(sessionIds) {
31
+ const pipeline = this.#connection.pipeline();
32
+ sessionIds.forEach((sessionId) => pipeline.get(sessionId));
33
+ return (await pipeline.exec())?.map((result) => result[1]) ?? [];
34
+ }
35
+ async #cleanupInvalidSessions(userId, invalidSessionIds) {
36
+ if (invalidSessionIds.length === 0) return;
37
+ await this.#connection.srem(this.#getTagKey(userId), ...invalidSessionIds);
38
+ }
39
+ #getTagKey(userId) {
40
+ return `session_tag:${userId}`;
41
+ }
42
+ #parseSessionData(contents, sessionId) {
43
+ try {
44
+ return new MessageBuilder().verify(contents, sessionId);
45
+ } catch {
46
+ return null;
47
+ }
48
+ }
49
+ async read(sessionId) {
50
+ debug_default("redis store: reading session data %s", sessionId);
51
+ const contents = await this.#connection.get(sessionId);
52
+ if (!contents) return null;
53
+ try {
54
+ return new MessageBuilder().verify(contents, sessionId);
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+ async write(sessionId, values) {
60
+ debug_default("redis store: writing session data %s, %O", sessionId, values);
61
+ const message = new MessageBuilder().build(values, void 0, sessionId);
62
+ await this.#connection.setex(sessionId, this.#ttlSeconds, message);
63
+ }
64
+ async destroy(sessionId) {
65
+ debug_default("redis store: destroying session data %s", sessionId);
66
+ await this.#connection.del(sessionId);
67
+ }
68
+ async touch(sessionId) {
69
+ debug_default("redis store: touching session data %s", sessionId);
70
+ await this.#connection.expire(sessionId, this.#ttlSeconds);
71
+ }
72
+ async tag(sessionId, userId) {
73
+ debug_default("redis store: tagging session %s with user %s", sessionId, userId);
74
+ await this.#connection.sadd(this.#getTagKey(userId), sessionId);
75
+ }
76
+ async tagged(userId) {
77
+ debug_default("redis store: getting sessions tagged with user %s", userId);
78
+ const sessionIds = await this.#connection.smembers(this.#getTagKey(userId));
79
+ if (sessionIds.length === 0) return [];
80
+ const contents = await this.#fetchSessionContents(sessionIds);
81
+ const results = sessionIds.map((sessionId, index) => this.#processSessionResult({
82
+ sessionId,
83
+ contents: contents[index]
84
+ }));
85
+ const validSessions = results.filter((r) => r.session !== null).map((r) => r.session);
86
+ const invalidSessionIds = results.map((result, index) => result.isInvalid ? sessionIds[index] : null).filter((id) => id !== null);
87
+ await this.#cleanupInvalidSessions(userId, invalidSessionIds);
88
+ return validSessions;
89
+ }
90
+ };
91
+ export { RedisStore };
@@ -1,8 +1,8 @@
1
- import { t as debug_default } from "./debug-Ba-0Cgn9.js";
2
- import { InvalidArgumentsException } from "@adonisjs/core/exceptions";
1
+ import { t as stubsRoot } from "./main-kn40V-hF.js";
2
+ import { t as debug_default } from "./debug-BZVg83L1.js";
3
+ import { InvalidArgumentsException, RuntimeException } from "@adonisjs/core/exceptions";
3
4
  import { configProvider } from "@adonisjs/core";
4
5
  import string from "@adonisjs/core/helpers/string";
5
- const stubsRoot = import.meta.dirname;
6
6
  async function configure(command) {
7
7
  const codemods = await command.createCodemods();
8
8
  await codemods.makeUsingStub(stubsRoot, "config/session.stub", {});
@@ -14,10 +14,12 @@ async function configure(command) {
14
14
  await codemods.registerMiddleware("router", [{ path: "@adonisjs/session/session_middleware" }]);
15
15
  await codemods.updateRcFile((rcFile) => {
16
16
  rcFile.addProvider("@adonisjs/session/session_provider");
17
+ rcFile.addCommand("@adonisjs/session/commands");
17
18
  });
18
19
  }
19
20
  var MemoryStore = class MemoryStore {
20
21
  static sessions = /* @__PURE__ */ new Map();
22
+ static tags = /* @__PURE__ */ new Map();
21
23
  read(sessionId) {
22
24
  return MemoryStore.sessions.get(sessionId) || null;
23
25
  }
@@ -26,13 +28,29 @@ var MemoryStore = class MemoryStore {
26
28
  }
27
29
  destroy(sessionId) {
28
30
  MemoryStore.sessions.delete(sessionId);
31
+ MemoryStore.tags.delete(sessionId);
29
32
  }
30
33
  touch(_) {}
34
+ tag(sessionId, userId) {
35
+ MemoryStore.tags.set(sessionId, userId);
36
+ }
37
+ tagged(userId) {
38
+ const sessions = [];
39
+ for (const [sessionId, tagUserId] of MemoryStore.tags) {
40
+ if (tagUserId !== userId) continue;
41
+ const data = MemoryStore.sessions.get(sessionId);
42
+ if (data) sessions.push({
43
+ id: sessionId,
44
+ data
45
+ });
46
+ }
47
+ return sessions;
48
+ }
31
49
  };
32
50
  function defineConfig(config) {
33
51
  debug_default("processing session config %O", config);
34
52
  if (!config.store) throw new InvalidArgumentsException("Missing \"store\" property inside the session config");
35
- const { stores: stores$1, cookie, ...rest } = {
53
+ const { stores, cookie, ...rest } = {
36
54
  enabled: true,
37
55
  age: "2h",
38
56
  cookieName: "adonis_session",
@@ -67,7 +85,7 @@ function defineConfig(config) {
67
85
  const stores = {
68
86
  file: (config) => {
69
87
  return configProvider.create(async () => {
70
- const { FileStore } = await import("./file-CNxCs957.js");
88
+ const { FileStore } = await import("./file-BBU02j4z.js");
71
89
  return (_, sessionConfig) => {
72
90
  return new FileStore(config, sessionConfig.age);
73
91
  };
@@ -75,7 +93,7 @@ const stores = {
75
93
  },
76
94
  redis: (config) => {
77
95
  return configProvider.create(async (app) => {
78
- const { RedisStore } = await import("./redis-Bcjum7z7.js");
96
+ const { RedisStore } = await import("./redis-D8D9UtiD.js");
79
97
  const redis = await app.container.make("redis");
80
98
  return (_, sessionConfig) => {
81
99
  return new RedisStore(redis.connection(config.connection), sessionConfig.age);
@@ -84,7 +102,7 @@ const stores = {
84
102
  },
85
103
  cookie: () => {
86
104
  return configProvider.create(async () => {
87
- const { CookieStore } = await import("./cookie-aLBno-zS.js");
105
+ const { CookieStore } = await import("./cookie--JOJxrtW.js");
88
106
  return (ctx, sessionConfig) => {
89
107
  return new CookieStore(sessionConfig.cookie, ctx);
90
108
  };
@@ -92,7 +110,7 @@ const stores = {
92
110
  },
93
111
  dynamodb: (config) => {
94
112
  return configProvider.create(async () => {
95
- const { DynamoDBStore } = await import("./dynamodb-CU8BrQfU.js");
113
+ const { DynamoDBStore } = await import("./dynamodb-BFVgTQSf.js");
96
114
  const { DynamoDBClient } = await import("@aws-sdk/client-dynamodb");
97
115
  const client = "clientConfig" in config ? new DynamoDBClient(config.clientConfig) : config.client;
98
116
  return (_, sessionConfig) => {
@@ -102,6 +120,20 @@ const stores = {
102
120
  });
103
121
  };
104
122
  });
123
+ },
124
+ database: (config) => {
125
+ return configProvider.create(async (app) => {
126
+ const { DatabaseStore } = await import("./database-vbrhCOPd.js");
127
+ const db = await app.container.make("lucid.db");
128
+ const connectionName = config?.connectionName || db.primaryConnectionName;
129
+ if (!db.manager.has(connectionName)) throw new RuntimeException(`Invalid database connection "${connectionName}" referenced in session config`);
130
+ return (_, sessionConfig) => {
131
+ return new DatabaseStore(db.connection(connectionName), sessionConfig.age, {
132
+ tableName: config?.tableName,
133
+ gcProbability: config?.gcProbability
134
+ });
135
+ };
136
+ });
105
137
  }
106
138
  };
107
- export { stubsRoot as i, stores as n, configure as r, defineConfig as t };
139
+ export { stores as n, configure as r, defineConfig as t };
@@ -1,12 +1,13 @@
1
- import { t as debug_default } from "./debug-Ba-0Cgn9.js";
2
- import { n as ValuesStore, t as ReadOnlyValuesStore } from "./values_store-smX0sQBJ.js";
1
+ import { t as debug_default } from "./debug-BZVg83L1.js";
2
+ import { n as ValuesStore, t as ReadOnlyValuesStore } from "./values_store-CvR1Sn37.js";
3
3
  import "node:module";
4
4
  import { createError } from "@adonisjs/core/exceptions";
5
5
  import { randomUUID } from "node:crypto";
6
6
  import Macroable from "@poppinss/macroable";
7
7
  import lodash from "@poppinss/utils/lodash";
8
+ import is from "@adonisjs/core/helpers/is";
8
9
  var __defProp = Object.defineProperty;
9
- var __export = (all, symbols) => {
10
+ var __exportAll = (all, symbols) => {
10
11
  let target = {};
11
12
  for (var name in all) __defProp(target, name, {
12
13
  get: all[name],
@@ -15,12 +16,15 @@ var __export = (all, symbols) => {
15
16
  if (symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
16
17
  return target;
17
18
  };
18
- var errors_exports = /* @__PURE__ */ __export({
19
+ var errors_exports = /* @__PURE__ */ __exportAll({
19
20
  E_SESSION_NOT_MUTABLE: () => E_SESSION_NOT_MUTABLE,
20
- E_SESSION_NOT_READY: () => E_SESSION_NOT_READY
21
+ E_SESSION_NOT_READY: () => E_SESSION_NOT_READY,
22
+ E_SESSION_TAGGING_NOT_SUPPORTED: () => E_SESSION_TAGGING_NOT_SUPPORTED
21
23
  });
22
24
  const E_SESSION_NOT_MUTABLE = createError("Session store is in readonly mode and cannot be mutated", "E_SESSION_NOT_MUTABLE", 500);
23
25
  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);
26
+ const E_SESSION_TAGGING_NOT_SUPPORTED = createError("Session store does not support tagging. Use memory, redis, or database store instead", "E_SESSION_TAGGING_NOT_SUPPORTED", 500);
27
+ const STORE_IN_FLASH = Symbol.for("store_in_flash");
24
28
  var Session = class extends Macroable {
25
29
  #store;
26
30
  #emitter;
@@ -67,6 +71,18 @@ var Session = class extends Macroable {
67
71
  if (mode === "write" && this.readonly) throw new E_SESSION_NOT_MUTABLE();
68
72
  return this.responseFlashMessages;
69
73
  }
74
+ shouldFlashValue(value) {
75
+ if (value && typeof value === "object") return STORE_IN_FLASH in value ? value[STORE_IN_FLASH] : true;
76
+ return true;
77
+ }
78
+ cleanupFlashData(data) {
79
+ if (is.plainObject(data)) return Object.keys(data).reduce((result, key) => {
80
+ const value = data[key];
81
+ if (this.shouldFlashValue(value)) result[key] = value;
82
+ return result;
83
+ }, {});
84
+ return data;
85
+ }
70
86
  #getValuesStore(mode) {
71
87
  if (!this.#valuesStore) throw new E_SESSION_NOT_READY();
72
88
  if (mode === "write" && this.readonly) throw new E_SESSION_NOT_MUTABLE();
@@ -120,8 +136,8 @@ var Session = class extends Macroable {
120
136
  }
121
137
  flash(key, value) {
122
138
  if (typeof key === "string") {
123
- if (value) this.#getFlashStore("write").set(key, value);
124
- } else this.#getFlashStore("write").merge(key);
139
+ if (value && this.shouldFlashValue(value)) this.#getFlashStore("write").set(key, value);
140
+ } else this.#getFlashStore("write").merge(this.cleanupFlashData(key));
125
141
  }
126
142
  flashErrors(errorsCollection) {
127
143
  this.flash({ errorsBag: errorsCollection });
@@ -144,13 +160,20 @@ var Session = class extends Macroable {
144
160
  this.flash("inputErrorsBag", errorsBag);
145
161
  }
146
162
  flashAll() {
147
- return this.#getFlashStore("write").set("input", this.#ctx.request.original());
163
+ let requestInput = this.#ctx.request.original();
164
+ return this.#getFlashStore("write").set("input", this.cleanupFlashData(requestInput));
148
165
  }
149
166
  flashExcept(keys) {
150
- this.#getFlashStore("write").set("input", lodash.omit(this.#ctx.request.original(), keys));
167
+ this.#getFlashStore("write").set("input", lodash.omitBy(this.#ctx.request.original(), (value, key) => {
168
+ if (keys.includes(key)) return true;
169
+ return !this.shouldFlashValue(value);
170
+ }));
151
171
  }
152
172
  flashOnly(keys) {
153
- this.#getFlashStore("write").set("input", lodash.pick(this.#ctx.request.original(), keys));
173
+ this.#getFlashStore("write").set("input", lodash.pickBy(this.#ctx.request.original(), (value, key) => {
174
+ if (keys.includes(key)) return this.shouldFlashValue(value);
175
+ return false;
176
+ }));
154
177
  }
155
178
  reflash() {
156
179
  this.#getFlashStore("write").set("reflashed", this.flashMessages.all());
@@ -161,6 +184,10 @@ var Session = class extends Macroable {
161
184
  reflashExcept(keys) {
162
185
  this.#getFlashStore("write").set("reflashed", lodash.omit(this.flashMessages.all(), keys));
163
186
  }
187
+ async tag(userId) {
188
+ if (!("tag" in this.#store)) throw new E_SESSION_TAGGING_NOT_SUPPORTED();
189
+ await this.#store.tag(this.#sessionId, userId);
190
+ }
164
191
  regenerate() {
165
192
  this.#sessionId = randomUUID();
166
193
  }
@@ -206,4 +233,4 @@ var Session = class extends Macroable {
206
233
  this.#emitter.emit("session:committed", { session: this });
207
234
  }
208
235
  };
209
- export { errors_exports as n, Session as t };
236
+ export { E_SESSION_TAGGING_NOT_SUPPORTED as n, errors_exports as r, Session as t };
@@ -0,0 +1,31 @@
1
+ import { n as E_SESSION_TAGGING_NOT_SUPPORTED } from "./session-Dd5u7w5Y.js";
2
+ import { t as debug_default } from "./debug-BZVg83L1.js";
3
+ var SessionCollection = class {
4
+ #store;
5
+ constructor(config) {
6
+ const storeFactory = config.stores[config.store];
7
+ this.#store = storeFactory(null, config);
8
+ }
9
+ supportsTagging() {
10
+ return "tag" in this.#store && "tagged" in this.#store;
11
+ }
12
+ async get(sessionId) {
13
+ debug_default("session collection: getting session data %s", sessionId);
14
+ return this.#store.read(sessionId);
15
+ }
16
+ async destroy(sessionId) {
17
+ debug_default("session collection: destroying session %s", sessionId);
18
+ return this.#store.destroy(sessionId);
19
+ }
20
+ async tag(sessionId, userId) {
21
+ debug_default("session collection: tagging session %s with user %s", sessionId, userId);
22
+ if (!this.supportsTagging()) throw new E_SESSION_TAGGING_NOT_SUPPORTED();
23
+ return this.#store.tag(sessionId, userId);
24
+ }
25
+ async tagged(userId) {
26
+ debug_default("session collection: getting sessions tagged with user %s", userId);
27
+ if (!this.supportsTagging()) throw new E_SESSION_TAGGING_NOT_SUPPORTED();
28
+ return this.#store.tagged(userId);
29
+ }
30
+ };
31
+ export { SessionCollection as t };
@@ -1,4 +1,4 @@
1
- import { t as Session } from "./session-CBqhcnvJ.js";
1
+ import { t as Session } from "./session-Dd5u7w5Y.js";
2
2
  import { ExceptionHandler } from "@adonisjs/core/http";
3
3
  const originalErrorHandler = ExceptionHandler.prototype.renderValidationErrorAsHTML;
4
4
  ExceptionHandler.macro("renderValidationErrorAsHTML", async function(error, ctx) {
@@ -1,5 +1,5 @@
1
- import { t as debug_default } from "../debug-Ba-0Cgn9.js";
2
- import { n as ValuesStore } from "../values_store-smX0sQBJ.js";
1
+ import { t as debug_default } from "../debug-BZVg83L1.js";
2
+ import { n as ValuesStore } from "../values_store-CvR1Sn37.js";
3
3
  import { randomUUID } from "node:crypto";
4
4
  var SessionClient = class {
5
5
  #valuesStore = new ValuesStore({});
@@ -1,6 +1,6 @@
1
1
  import type { ConfigProvider } from '@adonisjs/core/types';
2
2
  import type { CookieOptions } from '@adonisjs/core/types/http';
3
- import type { SessionConfig, FileStoreConfig, RedisStoreConfig, SessionStoreFactory, DynamoDBStoreConfig } from './types.ts';
3
+ import type { SessionConfig, FileStoreConfig, RedisStoreConfig, SessionStoreFactory, DynamoDBStoreConfig, DatabaseStoreConfig } from './types.ts';
4
4
  type ConfigInput<KnownStores extends Record<string, SessionStoreFactory | ConfigProvider<SessionStoreFactory>>> = Partial<SessionConfig> & {
5
5
  store: keyof KnownStores | 'memory';
6
6
  stores: KnownStores;
@@ -66,5 +66,6 @@ export declare const stores: {
66
66
  redis: (config: RedisStoreConfig) => ConfigProvider<SessionStoreFactory>;
67
67
  cookie: () => ConfigProvider<SessionStoreFactory>;
68
68
  dynamodb: (config: DynamoDBStoreConfig) => ConfigProvider<SessionStoreFactory>;
69
+ database: (config?: DatabaseStoreConfig) => ConfigProvider<SessionStoreFactory>;
69
70
  };
70
71
  export {};
@@ -22,3 +22,12 @@ export declare const E_SESSION_NOT_MUTABLE: new (args?: any, options?: ErrorOpti
22
22
  * session.put('key', 'value') // Works fine
23
23
  */
24
24
  export declare const E_SESSION_NOT_READY: new (args?: any, options?: ErrorOptions) => import("@adonisjs/core/exceptions").Exception;
25
+ /**
26
+ * Error thrown when attempting to use tagging features with a store that doesn't support it.
27
+ * Only Memory, Redis, and Database stores support session tagging.
28
+ *
29
+ * @example
30
+ * // This will throw E_SESSION_TAGGING_NOT_SUPPORTED when using cookie store
31
+ * const sessions = await sessionCollection.tagged(userId)
32
+ */
33
+ export declare const E_SESSION_TAGGING_NOT_SUPPORTED: new (args?: any, options?: ErrorOptions) => import("@adonisjs/core/exceptions").Exception;
@@ -1,4 +1,4 @@
1
- import { t as debug_default } from "../../debug-Ba-0Cgn9.js";
1
+ import { t as debug_default } from "../../debug-BZVg83L1.js";
2
2
  const edgePluginSession = (edge) => {
3
3
  debug_default("registering session tags with edge");
4
4
  edge.registerTag({
@@ -1,5 +1,5 @@
1
- import "../../../debug-Ba-0Cgn9.js";
2
- import "../../../values_store-smX0sQBJ.js";
1
+ import "../../../debug-BZVg83L1.js";
2
+ import "../../../values_store-CvR1Sn37.js";
3
3
  import { SessionClient } from "../../client.js";
4
4
  import { RuntimeException } from "@adonisjs/core/exceptions";
5
5
  import lodash from "@poppinss/utils/lodash";
@@ -1,5 +1,5 @@
1
- import "../../../debug-Ba-0Cgn9.js";
2
- import "../../../values_store-smX0sQBJ.js";
1
+ import "../../../debug-BZVg83L1.js";
2
+ import "../../../values_store-CvR1Sn37.js";
3
3
  import { SessionClient } from "../../client.js";
4
4
  import { RuntimeException } from "@adonisjs/core/exceptions";
5
5
  import { configProvider } from "@adonisjs/core";
@@ -73,6 +73,8 @@ export declare class Session extends Macroable {
73
73
  * @param ctx - HTTP context
74
74
  */
75
75
  constructor(config: SessionConfig, storeFactory: SessionStoreFactory, emitter: EmitterService, ctx: HttpContext);
76
+ protected shouldFlashValue(value: unknown): unknown;
77
+ protected cleanupFlashData<T>(data: T): T | Record<string, any>;
76
78
  /**
77
79
  * Initiates the session store. The method results in a noop when called multiple times.
78
80
  *
@@ -266,6 +268,18 @@ export declare class Session extends Macroable {
266
268
  * session.reflashExcept(['error', 'warning'])
267
269
  */
268
270
  reflashExcept(keys: string[]): void;
271
+ /**
272
+ * Tag the current session with a user ID.
273
+ * Only supported by Memory, Redis, and Database stores.
274
+ * This enables features like "logout from all devices".
275
+ *
276
+ * @param userId - The user ID to tag this session with
277
+ *
278
+ * @example
279
+ * // During login, tag the session with the user's ID
280
+ * await session.tag(String(user.id))
281
+ */
282
+ tag(userId: string): Promise<void>;
269
283
  /**
270
284
  * Re-generates the session id and migrates data to it
271
285
  *
@@ -0,0 +1,81 @@
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 type { ResolvedSessionConfig, SessionData, TaggedSession } from './types.ts';
10
+ /**
11
+ * SessionCollection provides APIs for programmatic session
12
+ * management. It allows reading, destroying, and tagging
13
+ * sessions without an HTTP context.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * import app from '@adonisjs/core/services/app'
18
+ * import { SessionCollection } from '@adonisjs/session'
19
+ *
20
+ * const sessionCollection = await app.container.make(SessionCollection)
21
+ *
22
+ * // List all sessions for a user
23
+ * const sessions = await sessionCollection.tagged(String(user.id))
24
+ *
25
+ * // Destroy a specific session
26
+ * await sessionCollection.destroy(sessionId)
27
+ * ```
28
+ */
29
+ export declare class SessionCollection {
30
+ #private;
31
+ /**
32
+ * Creates a new SessionCollection instance
33
+ *
34
+ * @param config - Resolved session configuration
35
+ */
36
+ constructor(config: ResolvedSessionConfig);
37
+ /**
38
+ * Check if the current store supports tagging
39
+ */
40
+ supportsTagging(): boolean;
41
+ /**
42
+ * Returns the session data for the given session ID,
43
+ * or null if the session does not exist
44
+ *
45
+ * @param sessionId - Session identifier
46
+ *
47
+ * @example
48
+ * const data = await sessionCollection.get('sess_abc123')
49
+ */
50
+ get(sessionId: string): Promise<SessionData | null>;
51
+ /**
52
+ * Destroys a session by its ID
53
+ *
54
+ * @param sessionId - Session identifier
55
+ *
56
+ * @example
57
+ * await sessionCollection.destroy('sess_abc123')
58
+ */
59
+ destroy(sessionId: string): Promise<void>;
60
+ /**
61
+ * Tag a session with a user ID.
62
+ * Only supported by Memory, Redis and Database stores.
63
+ *
64
+ * @param sessionId - Session identifier
65
+ * @param userId - User identifier to tag the session with
66
+ *
67
+ * @example
68
+ * await sessionCollection.tag('sess_abc123', 'user_456')
69
+ */
70
+ tag(sessionId: string, userId: string): Promise<void>;
71
+ /**
72
+ * Get all sessions for a given user ID (tag).
73
+ * Only supported by Memory, Redis and Database stores.
74
+ *
75
+ * @param userId - User identifier to get sessions for
76
+ *
77
+ * @example
78
+ * const sessions = await sessionCollection.tagged('user_456')
79
+ */
80
+ tagged(userId: string): Promise<TaggedSession[]>;
81
+ }
@@ -1,5 +1,5 @@
1
- import "../session-CBqhcnvJ.js";
2
- import "../debug-Ba-0Cgn9.js";
3
- import "../values_store-smX0sQBJ.js";
4
- import { t as SessionMiddleware } from "../session_middleware-CS0R7hmq.js";
1
+ import "../session-Dd5u7w5Y.js";
2
+ import "../debug-BZVg83L1.js";
3
+ import "../values_store-CvR1Sn37.js";
4
+ import { t as SessionMiddleware } from "../session_middleware-BoOMxNVH.js";
5
5
  export { SessionMiddleware as default };
@@ -0,0 +1,70 @@
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 type { QueryClientContract } from '@adonisjs/lucid/types/database';
10
+ import type { SessionStoreWithTaggingContract, SessionData, TaggedSession } from '../types.ts';
11
+ /**
12
+ * Database store to read/write session to SQL databases using Lucid
13
+ */
14
+ export declare class DatabaseStore implements SessionStoreWithTaggingContract {
15
+ #private;
16
+ constructor(client: QueryClientContract, age: string | number, options?: {
17
+ /**
18
+ * Defaults to "sessions"
19
+ */
20
+ tableName?: string;
21
+ /**
22
+ * The probability (in percent) that garbage collection will be
23
+ * triggered on any given request. For example, 2 means 2% chance.
24
+ *
25
+ * Set to 0 to disable garbage collection.
26
+ *
27
+ * Defaults to 2 (2% chance)
28
+ */
29
+ gcProbability?: number;
30
+ });
31
+ /**
32
+ * Returns session data
33
+ *
34
+ * @param sessionId - Session identifier
35
+ */
36
+ read(sessionId: string): Promise<SessionData | null>;
37
+ /**
38
+ * Write session values to the database
39
+ *
40
+ * @param sessionId - Session identifier
41
+ * @param values - Session data to store
42
+ */
43
+ write(sessionId: string, values: Object): Promise<void>;
44
+ /**
45
+ * Cleanup session by removing it
46
+ *
47
+ * @param sessionId - Session identifier
48
+ */
49
+ destroy(sessionId: string): Promise<void>;
50
+ /**
51
+ * Updates the session expiry
52
+ *
53
+ * @param sessionId - Session identifier
54
+ */
55
+ touch(sessionId: string): Promise<void>;
56
+ /**
57
+ * Tag a session with a user ID.
58
+ * Uses UPSERT to handle both existing and new sessions.
59
+ *
60
+ * @param sessionId - Session identifier
61
+ * @param userId - User identifier to tag the session with
62
+ */
63
+ tag(sessionId: string, userId: string): Promise<void>;
64
+ /**
65
+ * Get all sessions for a given user ID (tag)
66
+ *
67
+ * @param userId - User identifier to get sessions for
68
+ */
69
+ tagged(userId: string): Promise<TaggedSession[]>;
70
+ }
@@ -6,7 +6,7 @@
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 { SessionData, SessionStoreContract } from '../types.ts';
9
+ import type { SessionData, SessionStoreWithTaggingContract, TaggedSession } from '../types.ts';
10
10
  /**
11
11
  * Memory store is meant to be used for writing tests.
12
12
  * All session data is stored in memory and will be lost when the process restarts.
@@ -15,11 +15,15 @@ import type { SessionData, SessionStoreContract } from '../types.ts';
15
15
  * const memoryStore = new MemoryStore()
16
16
  * memoryStore.write('sess_abc123', { userId: 123 })
17
17
  */
18
- export declare class MemoryStore implements SessionStoreContract {
18
+ export declare class MemoryStore implements SessionStoreWithTaggingContract {
19
19
  /**
20
20
  * Static map to store all session data in memory
21
21
  */
22
22
  static sessions: Map<string, SessionData>;
23
+ /**
24
+ * Static map to store session tags (sessionId -> userId)
25
+ */
26
+ static tags: Map<string, string>;
23
27
  /**
24
28
  * Reads session value from memory
25
29
  *
@@ -54,4 +58,17 @@ export declare class MemoryStore implements SessionStoreContract {
54
58
  * @param sessionId - Session identifier (unused)
55
59
  */
56
60
  touch(_?: string): void;
61
+ /**
62
+ * Tag a session with a user ID
63
+ *
64
+ * @param sessionId - Session identifier
65
+ * @param userId - User identifier to tag the session with
66
+ */
67
+ tag(sessionId: string, userId: string): void;
68
+ /**
69
+ * Get all sessions for a given user ID (tag)
70
+ *
71
+ * @param userId - User identifier to get sessions for
72
+ */
73
+ tagged(userId: string): TaggedSession[];
57
74
  }
@@ -7,7 +7,7 @@
7
7
  * file that was distributed with this source code.
8
8
  */
9
9
  import type { Connection } from '@adonisjs/redis/types';
10
- import type { SessionStoreContract, SessionData } from '../types.ts';
10
+ import type { SessionData, TaggedSession, SessionStoreWithTaggingContract } from '../types.ts';
11
11
  /**
12
12
  * Redis store to read/write session data to Redis server.
13
13
  * Provides fast, scalable session storage with automatic expiry.
@@ -15,7 +15,7 @@ import type { SessionStoreContract, SessionData } from '../types.ts';
15
15
  * @example
16
16
  * const redisStore = new RedisStore(redisConnection, '2 hours')
17
17
  */
18
- export declare class RedisStore implements SessionStoreContract {
18
+ export declare class RedisStore implements SessionStoreWithTaggingContract {
19
19
  #private;
20
20
  /**
21
21
  * Creates a new Redis store instance
@@ -61,4 +61,17 @@ export declare class RedisStore implements SessionStoreContract {
61
61
  * await store.touch('sess_abc123')
62
62
  */
63
63
  touch(sessionId: string): Promise<void>;
64
+ /**
65
+ * Tag a session with a user ID
66
+ *
67
+ * @param sessionId - Session identifier
68
+ * @param userId - User identifier to tag the session with
69
+ */
70
+ tag(sessionId: string, userId: string): Promise<void>;
71
+ /**
72
+ * Get all sessions for a given user ID (tag)
73
+ *
74
+ * @param userId - User identifier to get sessions for
75
+ */
76
+ tagged(userId: string): Promise<TaggedSession[]>;
64
77
  }
@@ -210,3 +210,64 @@ export type DynamoDBStoreConfig = ({
210
210
  * }
211
211
  */
212
212
  export type SessionStoreFactory = (ctx: HttpContext, sessionConfig: SessionConfig) => SessionStoreContract;
213
+ /**
214
+ * Extended session store contract that supports tagging sessions with user IDs.
215
+ * This enables querying all sessions for a specific user, useful for features
216
+ * like "logout from all devices" or "view active sessions".
217
+ *
218
+ * @example
219
+ * class MyStore implements SessionStoreWithTaggingContract {
220
+ * // ... base SessionStoreContract methods ...
221
+ *
222
+ * async tag(sessionId: string, userId: string) {
223
+ * await this.storage.tag(sessionId, userId)
224
+ * }
225
+ *
226
+ * async tagged(userId: string) {
227
+ * return await this.storage.getSessionsByUser(userId)
228
+ * }
229
+ * }
230
+ */
231
+ export interface SessionStoreWithTaggingContract extends SessionStoreContract {
232
+ /**
233
+ * Associates a session with a user ID (tag).
234
+ * This allows querying all sessions for a specific user.
235
+ */
236
+ tag(sessionId: string, userId: string): Promise<void> | void;
237
+ /**
238
+ * Returns all sessions associated with a given user ID (tag).
239
+ * Only returns non-expired sessions.
240
+ */
241
+ tagged(userId: string): Promise<TaggedSession[]> | TaggedSession[];
242
+ }
243
+ /**
244
+ * Represents a tagged session with its ID and data
245
+ */
246
+ export interface TaggedSession {
247
+ id: string;
248
+ data: SessionData;
249
+ }
250
+ /**
251
+ * Configuration used by the database store.
252
+ */
253
+ export interface DatabaseStoreConfig {
254
+ connectionName?: string;
255
+ tableName?: string;
256
+ /**
257
+ * The probability (in percent) that garbage collection of expired
258
+ * sessions will be triggered on any given request.
259
+ *
260
+ * For example, 2 means 2% chance.
261
+ * Set to 0 to disable garbage collection.
262
+ *
263
+ * Defaults to 2 (2% chance)
264
+ */
265
+ gcProbability?: number;
266
+ }
267
+ /**
268
+ * Resolved session config after processing by defineConfig
269
+ */
270
+ export interface ResolvedSessionConfig extends SessionConfig {
271
+ store: string;
272
+ stores: Record<string, SessionStoreFactory>;
273
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@adonisjs/session",
3
3
  "description": "Session provider for AdonisJS",
4
- "version": "8.0.0-next.1",
4
+ "version": "8.0.0-next.3",
5
5
  "engines": {
6
6
  "node": ">=24.0.0"
7
7
  },
@@ -22,7 +22,8 @@
22
22
  "./plugins/api_client": "./build/src/plugins/japa/api_client.js",
23
23
  "./plugins/browser_client": "./build/src/plugins/japa/browser_client.js",
24
24
  "./client": "./build/src/client.js",
25
- "./types": "./build/src/types.js"
25
+ "./types": "./build/src/types.js",
26
+ "./commands": "./build/commands/main.js"
26
27
  },
27
28
  "scripts": {
28
29
  "pretest": "npm run lint",
@@ -33,7 +34,8 @@
33
34
  "copy:templates": "copyfiles \"stubs/**/*.stub\" --up=\"1\" build",
34
35
  "precompile": "npm run lint",
35
36
  "compile": "tsdown && tsc --emitDeclarationOnly --declaration",
36
- "postcompile": "npm run copy:templates",
37
+ "postcompile": "npm run copy:templates && npm run index:commands",
38
+ "index:commands": "adonis-kit index build/commands",
37
39
  "build": "npm run compile",
38
40
  "docs": "typedoc",
39
41
  "version": "npm run build",
@@ -42,50 +44,55 @@
42
44
  "quick:test": "node --import=@poppinss/ts-exec --enable-source-maps bin/test.ts"
43
45
  },
44
46
  "devDependencies": {
45
- "@adonisjs/assembler": "^8.0.0-next.26",
46
- "@adonisjs/core": "^7.0.0-next.16",
47
- "@adonisjs/eslint-config": "^3.0.0-next.5",
47
+ "@adonisjs/assembler": "^8.0.0-next.31",
48
+ "@adonisjs/core": "^7.0.0-next.27",
49
+ "@adonisjs/eslint-config": "^3.0.0-next.9",
48
50
  "@adonisjs/i18n": "^3.0.0-next.2",
51
+ "@adonisjs/lucid": "^22.0.0-next.7",
49
52
  "@adonisjs/prettier-config": "^1.4.5",
50
53
  "@adonisjs/redis": "^10.0.0-next.2",
51
54
  "@adonisjs/tsconfig": "^2.0.0-next.3",
52
- "@aws-sdk/client-dynamodb": "^3.955.0",
53
- "@aws-sdk/util-dynamodb": "^3.955.0",
54
- "@japa/api-client": "^3.1.1",
55
+ "@aws-sdk/client-dynamodb": "^3.980.0",
56
+ "@aws-sdk/util-dynamodb": "^3.980.0",
57
+ "@japa/api-client": "^3.2.1",
55
58
  "@japa/assert": "^4.2.0",
56
- "@japa/browser-client": "^2.2.0",
59
+ "@japa/browser-client": "^2.3.0",
57
60
  "@japa/file-system": "^3.0.0",
58
- "@japa/plugin-adonisjs": "^5.1.0-next.0",
59
- "@japa/runner": "^5.0.0",
61
+ "@japa/plugin-adonisjs": "^5.1.0-next.1",
62
+ "@japa/runner": "^5.3.0",
60
63
  "@japa/snapshot": "^2.0.10",
61
- "@poppinss/ts-exec": "^1.4.1",
64
+ "@poppinss/ts-exec": "^1.4.3",
62
65
  "@release-it/conventional-changelog": "^10.0.4",
63
- "@types/node": "^25.0.3",
66
+ "@types/node": "^25.1.0",
64
67
  "@types/set-cookie-parser": "^2.4.10",
65
68
  "@types/supertest": "^6.0.3",
66
69
  "@vinejs/vine": "^4.2.0",
70
+ "better-sqlite3": "^12.6.2",
67
71
  "c8": "^10.1.3",
68
72
  "copyfiles": "^2.4.1",
69
73
  "cross-env": "^10.1.0",
70
74
  "edge.js": "^6.4.0",
71
75
  "eslint": "^9.39.2",
72
76
  "get-port": "^7.1.0",
73
- "playwright": "^1.57.0",
74
- "prettier": "^3.7.4",
75
- "release-it": "^19.1.0",
76
- "set-cookie-parser": "^2.7.2",
77
- "supertest": "^7.1.4",
78
- "tsdown": "^0.18.1",
79
- "typedoc": "^0.28.15",
77
+ "mysql2": "^3.16.2",
78
+ "pg": "^8.18.0",
79
+ "playwright": "^1.58.1",
80
+ "prettier": "^3.8.1",
81
+ "release-it": "^19.2.4",
82
+ "set-cookie-parser": "^3.0.1",
83
+ "supertest": "^7.2.2",
84
+ "tsdown": "^0.20.1",
85
+ "typedoc": "^0.28.16",
80
86
  "typescript": "^5.9.3"
81
87
  },
82
88
  "dependencies": {
83
89
  "@poppinss/macroable": "^1.1.0",
84
- "@poppinss/utils": "^7.0.0-next.4"
90
+ "@poppinss/utils": "^7.0.0-next.6"
85
91
  },
86
92
  "peerDependencies": {
87
93
  "@adonisjs/assembler": "^8.0.0-next.26",
88
94
  "@adonisjs/core": "^7.0.0-next.16",
95
+ "@adonisjs/lucid": "^22.0.0-next.0",
89
96
  "@adonisjs/redis": "^10.0.0-next.2",
90
97
  "@aws-sdk/client-dynamodb": "^3.955.0",
91
98
  "@aws-sdk/util-dynamodb": "^3.955.0",
@@ -98,6 +105,9 @@
98
105
  "@adonisjs/assembler": {
99
106
  "optional": true
100
107
  },
108
+ "@adonisjs/lucid": {
109
+ "optional": true
110
+ },
101
111
  "@adonisjs/redis": {
102
112
  "optional": true
103
113
  },
@@ -121,6 +131,9 @@
121
131
  }
122
132
  },
123
133
  "author": "virk,adonisjs",
134
+ "contributors": [
135
+ "Julien Ripouteau <julien@ripouteau.com>"
136
+ ],
124
137
  "license": "MIT",
125
138
  "homepage": "https://github.com/adonisjs/session#readme",
126
139
  "repository": {
@@ -148,7 +161,8 @@
148
161
  "./src/plugins/edge.ts",
149
162
  "./src/plugins/japa/api_client.ts",
150
163
  "./src/plugins/japa/browser_client.ts",
151
- "./src/client.ts"
164
+ "./src/client.ts",
165
+ "./commands/make_session_table.ts"
152
166
  ],
153
167
  "outDir": "./build",
154
168
  "clean": true,
@@ -1,36 +0,0 @@
1
- import { t as debug_default } from "./debug-Ba-0Cgn9.js";
2
- import string from "@adonisjs/core/helpers/string";
3
- import { MessageBuilder } from "@adonisjs/core/helpers";
4
- var RedisStore = class {
5
- #connection;
6
- #ttlSeconds;
7
- constructor(connection, age) {
8
- this.#connection = connection;
9
- this.#ttlSeconds = string.seconds.parse(age);
10
- debug_default("initiating redis store");
11
- }
12
- async read(sessionId) {
13
- debug_default("redis store: reading session data %s", sessionId);
14
- const contents = await this.#connection.get(sessionId);
15
- if (!contents) return null;
16
- try {
17
- return new MessageBuilder().verify(contents, sessionId);
18
- } catch {
19
- return null;
20
- }
21
- }
22
- async write(sessionId, values) {
23
- debug_default("redis store: writing session data %s, %O", sessionId, values);
24
- const message = new MessageBuilder().build(values, void 0, sessionId);
25
- await this.#connection.setex(sessionId, this.#ttlSeconds, message);
26
- }
27
- async destroy(sessionId) {
28
- debug_default("redis store: destroying session data %s", sessionId);
29
- await this.#connection.del(sessionId);
30
- }
31
- async touch(sessionId) {
32
- debug_default("redis store: touching session data %s", sessionId);
33
- await this.#connection.expire(sessionId, this.#ttlSeconds);
34
- }
35
- };
36
- export { RedisStore };
File without changes