@adonisjs/session 8.0.0-next.1 → 8.0.0-next.2
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/build/commands/commands.json +1 -0
- package/build/commands/main.d.ts +4 -0
- package/build/commands/main.js +36 -0
- package/build/commands/make_session_table.d.ts +9 -0
- package/build/commands/make_session_table.js +13 -0
- package/build/{cookie-aLBno-zS.js → cookie--JOJxrtW.js} +1 -1
- package/build/database-vbrhCOPd.js +86 -0
- package/build/{dynamodb-CU8BrQfU.js → dynamodb-BFVgTQSf.js} +1 -1
- package/build/factories/main.js +7 -5
- package/build/{file-CNxCs957.js → file-BBU02j4z.js} +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.js +7 -5
- package/build/main-kn40V-hF.js +2 -0
- package/build/make/migration/sessions.stub +26 -0
- package/build/providers/session_provider.d.ts +2 -1
- package/build/providers/session_provider.js +15 -8
- package/build/redis-D8D9UtiD.js +91 -0
- package/build/{session-CBqhcnvJ.js → session-C9DdRahS.js} +10 -4
- package/build/{session-Cc1LPXRc.js → session-Cb9-DoMh.js} +40 -8
- package/build/session_collection-CvS5yIq6.js +31 -0
- package/build/{session_middleware-CS0R7hmq.js → session_middleware-gegOBxmm.js} +1 -1
- package/build/src/client.js +2 -2
- package/build/src/define_config.d.ts +2 -1
- package/build/src/errors.d.ts +9 -0
- package/build/src/plugins/edge.js +1 -1
- package/build/src/plugins/japa/api_client.js +2 -2
- package/build/src/plugins/japa/browser_client.js +2 -2
- package/build/src/session.d.ts +12 -0
- package/build/src/session_collection.d.ts +81 -0
- package/build/src/session_middleware.js +4 -4
- package/build/src/stores/database.d.ts +70 -0
- package/build/src/stores/memory.d.ts +19 -2
- package/build/src/stores/redis.d.ts +15 -2
- package/build/src/types.d.ts +61 -0
- package/package.json +18 -4
- package/build/redis-Bcjum7z7.js +0 -36
- /package/build/{debug-Ba-0Cgn9.js → debug-BZVg83L1.js} +0 -0
- /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,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,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 };
|
|
@@ -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-
|
|
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";
|
package/build/factories/main.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import "../session-
|
|
2
|
-
import
|
|
3
|
-
import "../
|
|
4
|
-
import "../
|
|
5
|
-
import
|
|
1
|
+
import "../session-C9DdRahS.js";
|
|
2
|
+
import "../main-kn40V-hF.js";
|
|
3
|
+
import { t as defineConfig } from "../session-Cb9-DoMh.js";
|
|
4
|
+
import "../debug-BZVg83L1.js";
|
|
5
|
+
import "../values_store-CvR1Sn37.js";
|
|
6
|
+
import "../session_collection-CvS5yIq6.js";
|
|
7
|
+
import { t as SessionMiddleware } from "../session_middleware-gegOBxmm.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-
|
|
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 {
|
|
2
|
-
import {
|
|
3
|
-
import "./
|
|
4
|
-
import
|
|
5
|
-
|
|
1
|
+
import { r as errors_exports, t as Session } from "./session-C9DdRahS.js";
|
|
2
|
+
import { t as stubsRoot } from "./main-kn40V-hF.js";
|
|
3
|
+
import { n as stores, r as configure, t as defineConfig } from "./session-Cb9-DoMh.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-CvS5yIq6.js";
|
|
7
|
+
export { ReadOnlyValuesStore, Session, SessionCollection, ValuesStore, configure, defineConfig, errors_exports as errors, stores, stubsRoot };
|
|
@@ -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
|
|
35
|
+
* Registering bindings
|
|
35
36
|
*/
|
|
36
37
|
register(): void;
|
|
37
38
|
/**
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import "../session-
|
|
2
|
-
import "../debug-
|
|
3
|
-
import "../values_store-
|
|
4
|
-
import { t as
|
|
1
|
+
import "../session-C9DdRahS.js";
|
|
2
|
+
import "../debug-BZVg83L1.js";
|
|
3
|
+
import "../values_store-CvR1Sn37.js";
|
|
4
|
+
import { t as SessionCollection } from "../session_collection-CvS5yIq6.js";
|
|
5
|
+
import { t as SessionMiddleware } from "../session_middleware-gegOBxmm.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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return new
|
|
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,5 +1,5 @@
|
|
|
1
|
-
import { t as debug_default } from "./debug-
|
|
2
|
-
import { n as ValuesStore, t as ReadOnlyValuesStore } from "./values_store-
|
|
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";
|
|
@@ -17,10 +17,12 @@ var __export = (all, symbols) => {
|
|
|
17
17
|
};
|
|
18
18
|
var errors_exports = /* @__PURE__ */ __export({
|
|
19
19
|
E_SESSION_NOT_MUTABLE: () => E_SESSION_NOT_MUTABLE,
|
|
20
|
-
E_SESSION_NOT_READY: () => E_SESSION_NOT_READY
|
|
20
|
+
E_SESSION_NOT_READY: () => E_SESSION_NOT_READY,
|
|
21
|
+
E_SESSION_TAGGING_NOT_SUPPORTED: () => E_SESSION_TAGGING_NOT_SUPPORTED
|
|
21
22
|
});
|
|
22
23
|
const E_SESSION_NOT_MUTABLE = createError("Session store is in readonly mode and cannot be mutated", "E_SESSION_NOT_MUTABLE", 500);
|
|
23
24
|
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);
|
|
25
|
+
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);
|
|
24
26
|
var Session = class extends Macroable {
|
|
25
27
|
#store;
|
|
26
28
|
#emitter;
|
|
@@ -161,6 +163,10 @@ var Session = class extends Macroable {
|
|
|
161
163
|
reflashExcept(keys) {
|
|
162
164
|
this.#getFlashStore("write").set("reflashed", lodash.omit(this.flashMessages.all(), keys));
|
|
163
165
|
}
|
|
166
|
+
async tag(userId) {
|
|
167
|
+
if (!("tag" in this.#store)) throw new E_SESSION_TAGGING_NOT_SUPPORTED();
|
|
168
|
+
await this.#store.tag(this.#sessionId, userId);
|
|
169
|
+
}
|
|
164
170
|
regenerate() {
|
|
165
171
|
this.#sessionId = randomUUID();
|
|
166
172
|
}
|
|
@@ -206,4 +212,4 @@ var Session = class extends Macroable {
|
|
|
206
212
|
this.#emitter.emit("session:committed", { session: this });
|
|
207
213
|
}
|
|
208
214
|
};
|
|
209
|
-
export {
|
|
215
|
+
export { E_SESSION_TAGGING_NOT_SUPPORTED as n, errors_exports as r, Session as t };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
import {
|
|
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,8 +28,24 @@ 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);
|
|
@@ -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-
|
|
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-
|
|
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
|
|
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-
|
|
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 {
|
|
139
|
+
export { stores as n, configure as r, defineConfig as t };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { n as E_SESSION_TAGGING_NOT_SUPPORTED } from "./session-C9DdRahS.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-
|
|
1
|
+
import { t as Session } from "./session-C9DdRahS.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) {
|
package/build/src/client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as debug_default } from "../debug-
|
|
2
|
-
import { n as ValuesStore } from "../values_store-
|
|
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 {};
|
package/build/src/errors.d.ts
CHANGED
|
@@ -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,5 +1,5 @@
|
|
|
1
|
-
import "../../../debug-
|
|
2
|
-
import "../../../values_store-
|
|
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-
|
|
2
|
-
import "../../../values_store-
|
|
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";
|
package/build/src/session.d.ts
CHANGED
|
@@ -266,6 +266,18 @@ export declare class Session extends Macroable {
|
|
|
266
266
|
* session.reflashExcept(['error', 'warning'])
|
|
267
267
|
*/
|
|
268
268
|
reflashExcept(keys: string[]): void;
|
|
269
|
+
/**
|
|
270
|
+
* Tag the current session with a user ID.
|
|
271
|
+
* Only supported by Memory, Redis, and Database stores.
|
|
272
|
+
* This enables features like "logout from all devices".
|
|
273
|
+
*
|
|
274
|
+
* @param userId - The user ID to tag this session with
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* // During login, tag the session with the user's ID
|
|
278
|
+
* await session.tag(String(user.id))
|
|
279
|
+
*/
|
|
280
|
+
tag(userId: string): Promise<void>;
|
|
269
281
|
/**
|
|
270
282
|
* Re-generates the session id and migrates data to it
|
|
271
283
|
*
|
|
@@ -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-
|
|
2
|
-
import "../debug-
|
|
3
|
-
import "../values_store-
|
|
4
|
-
import { t as SessionMiddleware } from "../session_middleware-
|
|
1
|
+
import "../session-C9DdRahS.js";
|
|
2
|
+
import "../debug-BZVg83L1.js";
|
|
3
|
+
import "../values_store-CvR1Sn37.js";
|
|
4
|
+
import { t as SessionMiddleware } from "../session_middleware-gegOBxmm.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,
|
|
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
|
|
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 {
|
|
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
|
|
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
|
}
|
package/build/src/types.d.ts
CHANGED
|
@@ -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.
|
|
4
|
+
"version": "8.0.0-next.2",
|
|
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",
|
|
@@ -46,6 +48,7 @@
|
|
|
46
48
|
"@adonisjs/core": "^7.0.0-next.16",
|
|
47
49
|
"@adonisjs/eslint-config": "^3.0.0-next.5",
|
|
48
50
|
"@adonisjs/i18n": "^3.0.0-next.2",
|
|
51
|
+
"@adonisjs/lucid": "^22.0.0-next.0",
|
|
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",
|
|
@@ -66,6 +69,9 @@
|
|
|
66
69
|
"@vinejs/vine": "^4.2.0",
|
|
67
70
|
"c8": "^10.1.3",
|
|
68
71
|
"copyfiles": "^2.4.1",
|
|
72
|
+
"better-sqlite3": "^12.5.0",
|
|
73
|
+
"mysql2": "^3.15.3",
|
|
74
|
+
"pg": "^8.16.3",
|
|
69
75
|
"cross-env": "^10.1.0",
|
|
70
76
|
"edge.js": "^6.4.0",
|
|
71
77
|
"eslint": "^9.39.2",
|
|
@@ -86,6 +92,7 @@
|
|
|
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,
|
package/build/redis-Bcjum7z7.js
DELETED
|
@@ -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
|
|
File without changes
|