@adonisjs/session 7.5.1 → 7.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/chunk-5ECC6OWF.js +8 -0
- package/build/{chunk-ZVSEMDIC.js.map → chunk-5ECC6OWF.js.map} +1 -1
- package/build/{chunk-TZLOND27.js → chunk-6BSSM3HO.js} +2 -2
- package/build/chunk-G7KFHA57.js +59 -0
- package/build/chunk-G7KFHA57.js.map +1 -0
- package/build/{chunk-GB5FBZCV.js → chunk-GCO66PQD.js} +2 -2
- package/build/{chunk-ZVSEMDIC.js → chunk-PZ5AY32C.js} +2 -7
- package/build/chunk-PZ5AY32C.js.map +1 -0
- package/build/{chunk-WUWXIKIB.js → chunk-Q24L5XPC.js} +51 -12
- package/build/chunk-Q24L5XPC.js.map +1 -0
- package/build/chunk-V3OAEXMJ.js +8 -0
- package/build/chunk-V3OAEXMJ.js.map +1 -0
- package/build/{chunk-OCQGCVXK.js → chunk-XP3CBOXR.js} +24 -4
- package/build/chunk-XP3CBOXR.js.map +1 -0
- 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 +21 -0
- package/build/commands/make_session_table.js.map +1 -0
- package/build/{cookie-WBWYVEDW.js → cookie-3C33DMWA.js} +3 -2
- package/build/{cookie-WBWYVEDW.js.map → cookie-3C33DMWA.js.map} +1 -1
- package/build/database-443FB7MM.js +118 -0
- package/build/database-443FB7MM.js.map +1 -0
- package/build/{dynamodb-3PG52TE3.js → dynamodb-53YXEFIJ.js} +3 -2
- package/build/{dynamodb-3PG52TE3.js.map → dynamodb-53YXEFIJ.js.map} +1 -1
- package/build/factories/main.js +7 -4
- package/build/factories/main.js.map +1 -1
- package/build/{file-B6QKOZXW.js → file-K3GBSVSU.js} +5 -4
- package/build/{file-B6QKOZXW.js.map → file-K3GBSVSU.js.map} +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.js +11 -4
- package/build/make/migration/sessions.stub +26 -0
- package/build/providers/session_provider.d.ts +2 -1
- package/build/providers/session_provider.js +26 -11
- package/build/providers/session_provider.js.map +1 -1
- package/build/redis-YGX2CNE2.js +119 -0
- package/build/redis-YGX2CNE2.js.map +1 -0
- package/build/src/client.js +3 -2
- package/build/src/define_config.d.ts +2 -1
- package/build/src/errors.d.ts +5 -0
- package/build/src/plugins/edge.js +2 -1
- package/build/src/plugins/edge.js.map +1 -1
- package/build/src/plugins/japa/api_client.js +3 -2
- package/build/src/plugins/japa/api_client.js.map +1 -1
- package/build/src/plugins/japa/browser_client.js +3 -2
- package/build/src/plugins/japa/browser_client.js.map +1 -1
- package/build/src/session.d.ts +8 -0
- package/build/src/session_collection.d.ts +55 -0
- package/build/src/session_middleware.js +4 -3
- package/build/src/stores/database.d.ts +55 -0
- package/build/src/stores/memory.d.ts +14 -2
- package/build/src/stores/redis.d.ts +15 -8
- package/build/src/types.d.ts +45 -0
- package/package.json +39 -28
- package/build/chunk-OCQGCVXK.js.map +0 -1
- package/build/chunk-WUWXIKIB.js.map +0 -1
- package/build/redis-CAY24YIA.js +0 -58
- package/build/redis-CAY24YIA.js.map +0 -1
- /package/build/{chunk-TZLOND27.js.map → chunk-6BSSM3HO.js.map} +0 -0
- /package/build/{chunk-GB5FBZCV.js.map → chunk-GCO66PQD.js.map} +0 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import {
|
|
2
|
+
debug_default
|
|
3
|
+
} from "./chunk-5ECC6OWF.js";
|
|
4
|
+
import "./chunk-PZ5AY32C.js";
|
|
5
|
+
|
|
6
|
+
// src/stores/database.ts
|
|
7
|
+
import string from "@poppinss/utils/string";
|
|
8
|
+
import { MessageBuilder } from "@adonisjs/core/helpers";
|
|
9
|
+
var DatabaseStore = class {
|
|
10
|
+
#client;
|
|
11
|
+
#tableName;
|
|
12
|
+
#ttlSeconds;
|
|
13
|
+
#gcProbability;
|
|
14
|
+
constructor(client, age, options) {
|
|
15
|
+
this.#client = client;
|
|
16
|
+
this.#tableName = options?.tableName ?? "sessions";
|
|
17
|
+
this.#ttlSeconds = string.seconds.parse(age);
|
|
18
|
+
this.#gcProbability = options?.gcProbability ?? 2;
|
|
19
|
+
debug_default("initiating database store");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Run garbage collection to delete expired sessions.
|
|
23
|
+
* This is called based on gcProbability after writing session data.
|
|
24
|
+
*/
|
|
25
|
+
async #collectGarbage() {
|
|
26
|
+
if (this.#gcProbability <= 0) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const random = Math.random() * 100;
|
|
30
|
+
if (random < this.#gcProbability) {
|
|
31
|
+
debug_default("database store: running garbage collection");
|
|
32
|
+
const expiredBefore = new Date(Date.now());
|
|
33
|
+
await this.#client.from(this.#tableName).where("expires_at", "<=", expiredBefore).delete();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Parses and verifies session data using MessageBuilder
|
|
38
|
+
*/
|
|
39
|
+
#parseSessionData(contents, sessionId) {
|
|
40
|
+
try {
|
|
41
|
+
return new MessageBuilder().verify(contents, sessionId);
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Returns session data
|
|
48
|
+
*/
|
|
49
|
+
async read(sessionId) {
|
|
50
|
+
debug_default("database store: reading session data %s", sessionId);
|
|
51
|
+
const row = await this.#client.from(this.#tableName).where("id", sessionId).first();
|
|
52
|
+
if (!row) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const expiresAt = new Date(row.expires_at).getTime();
|
|
56
|
+
if (Date.now() > expiresAt) {
|
|
57
|
+
await this.destroy(sessionId);
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return this.#parseSessionData(row.data, sessionId);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Write session values to the database
|
|
64
|
+
*/
|
|
65
|
+
async write(sessionId, values) {
|
|
66
|
+
debug_default("database store: writing session data %s, %O", sessionId, values);
|
|
67
|
+
const message = new MessageBuilder().build(values, void 0, sessionId);
|
|
68
|
+
const expiresAt = new Date(Date.now() + this.#ttlSeconds * 1e3);
|
|
69
|
+
await this.#client.insertQuery().table(this.#tableName).insert({
|
|
70
|
+
id: sessionId,
|
|
71
|
+
data: message,
|
|
72
|
+
expires_at: expiresAt
|
|
73
|
+
}).knexQuery.onConflict("id").merge(["data", "expires_at"]);
|
|
74
|
+
await this.#collectGarbage();
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Cleanup session by removing it
|
|
78
|
+
*/
|
|
79
|
+
async destroy(sessionId) {
|
|
80
|
+
debug_default("database store: destroying session data %s", sessionId);
|
|
81
|
+
await this.#client.from(this.#tableName).where("id", sessionId).delete();
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Updates the session expiry
|
|
85
|
+
*/
|
|
86
|
+
async touch(sessionId) {
|
|
87
|
+
debug_default("database store: touching session data %s", sessionId);
|
|
88
|
+
const expiresAt = new Date(Date.now() + this.#ttlSeconds * 1e3);
|
|
89
|
+
await this.#client.from(this.#tableName).where("id", sessionId).update({ expires_at: expiresAt });
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Tag a session with a user ID
|
|
93
|
+
*/
|
|
94
|
+
async tag(sessionId, userId) {
|
|
95
|
+
debug_default("database store: tagging session %s with user %s", sessionId, userId);
|
|
96
|
+
await this.#client.from(this.#tableName).where("id", sessionId).update({ user_id: userId });
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Converts a database row to a TaggedSession object
|
|
100
|
+
*/
|
|
101
|
+
#rowToTaggedSession(row) {
|
|
102
|
+
const data = this.#parseSessionData(row.data, row.id);
|
|
103
|
+
if (!data) return null;
|
|
104
|
+
return { id: row.id, data };
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get all sessions for a given user ID (tag)
|
|
108
|
+
*/
|
|
109
|
+
async tagged(userId) {
|
|
110
|
+
debug_default("database store: getting sessions tagged with user %s", userId);
|
|
111
|
+
const rows = await this.#client.from(this.#tableName).select("id", "data").where("user_id", userId).where("expires_at", ">", /* @__PURE__ */ new Date());
|
|
112
|
+
return rows.map((row) => this.#rowToTaggedSession(row)).filter((session) => session !== null);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
export {
|
|
116
|
+
DatabaseStore
|
|
117
|
+
};
|
|
118
|
+
//# sourceMappingURL=database-443FB7MM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/stores/database.ts"],"sourcesContent":["/**\n * @adonisjs/session\n *\n * (c) AdonisJS\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nimport string from '@poppinss/utils/string'\nimport { MessageBuilder } from '@adonisjs/core/helpers'\nimport type { QueryClientContract } from '@adonisjs/lucid/types/database'\n\nimport debug from '../debug.js'\nimport type { SessionStoreWithTaggingContract, SessionData, TaggedSession } from '../types.js'\n\n/**\n * Database store to read/write session to SQL databases using Lucid\n */\nexport class DatabaseStore implements SessionStoreWithTaggingContract {\n #client: QueryClientContract\n #tableName: string\n #ttlSeconds: number\n #gcProbability: number\n\n constructor(\n client: QueryClientContract,\n age: string | number,\n options?: {\n /**\n * Defaults to \"sessions\"\n */\n tableName?: string\n\n /**\n * The probability (in percent) that garbage collection will be\n * triggered on any given request. For example, 2 means 2% chance.\n *\n * Set to 0 to disable garbage collection.\n *\n * Defaults to 2 (2% chance)\n */\n gcProbability?: number\n }\n ) {\n this.#client = client\n this.#tableName = options?.tableName ?? 'sessions'\n this.#ttlSeconds = string.seconds.parse(age)\n this.#gcProbability = options?.gcProbability ?? 2\n debug('initiating database store')\n }\n\n /**\n * Run garbage collection to delete expired sessions.\n * This is called based on gcProbability after writing session data.\n */\n async #collectGarbage(): Promise<void> {\n if (this.#gcProbability <= 0) {\n return\n }\n\n const random = Math.random() * 100\n if (random < this.#gcProbability) {\n debug('database store: running garbage collection')\n const expiredBefore = new Date(Date.now())\n await this.#client.from(this.#tableName).where('expires_at', '<=', expiredBefore).delete()\n }\n }\n\n /**\n * Parses and verifies session data using MessageBuilder\n */\n #parseSessionData(contents: string, sessionId: string): SessionData | null {\n try {\n return new MessageBuilder().verify<SessionData>(contents, sessionId)\n } catch {\n return null\n }\n }\n\n /**\n * Returns session data\n */\n async read(sessionId: string): Promise<SessionData | null> {\n debug('database store: reading session data %s', sessionId)\n\n const row = await this.#client.from(this.#tableName).where('id', sessionId).first()\n\n if (!row) {\n return null\n }\n\n /**\n * Check if the session has expired. If so, delete it and return null.\n */\n const expiresAt = new Date(row.expires_at).getTime()\n if (Date.now() > expiresAt) {\n await this.destroy(sessionId)\n return null\n }\n\n return this.#parseSessionData(row.data, sessionId)\n }\n\n /**\n * Write session values to the database\n */\n async write(sessionId: string, values: Object): Promise<void> {\n debug('database store: writing session data %s, %O', sessionId, values)\n\n const message = new MessageBuilder().build(values, undefined, sessionId)\n const expiresAt = new Date(Date.now() + this.#ttlSeconds * 1000)\n\n await this.#client\n .insertQuery()\n .table(this.#tableName)\n .insert({\n id: sessionId,\n data: message,\n expires_at: expiresAt,\n })\n .knexQuery.onConflict('id')\n .merge(['data', 'expires_at'])\n\n await this.#collectGarbage()\n }\n\n /**\n * Cleanup session by removing it\n */\n async destroy(sessionId: string): Promise<void> {\n debug('database store: destroying session data %s', sessionId)\n\n await this.#client.from(this.#tableName).where('id', sessionId).delete()\n }\n\n /**\n * Updates the session expiry\n */\n async touch(sessionId: string): Promise<void> {\n debug('database store: touching session data %s', sessionId)\n\n const expiresAt = new Date(Date.now() + this.#ttlSeconds * 1000)\n\n await this.#client\n .from(this.#tableName)\n .where('id', sessionId)\n .update({ expires_at: expiresAt })\n }\n\n /**\n * Tag a session with a user ID\n */\n async tag(sessionId: string, userId: string): Promise<void> {\n debug('database store: tagging session %s with user %s', sessionId, userId)\n\n await this.#client.from(this.#tableName).where('id', sessionId).update({ user_id: userId })\n }\n\n /**\n * Converts a database row to a TaggedSession object\n */\n #rowToTaggedSession(row: { id: string; data: string }): TaggedSession | null {\n const data = this.#parseSessionData(row.data, row.id)\n if (!data) return null\n\n return { id: row.id, data }\n }\n\n /**\n * Get all sessions for a given user ID (tag)\n */\n async tagged(userId: string): Promise<TaggedSession[]> {\n debug('database store: getting sessions tagged with user %s', userId)\n\n const rows = await this.#client\n .from(this.#tableName)\n .select('id', 'data')\n .where('user_id', userId)\n .where('expires_at', '>', new Date())\n\n return rows.map((row) => this.#rowToTaggedSession(row)).filter((session) => session !== null)\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
debug_default
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-5ECC6OWF.js";
|
|
4
|
+
import "./chunk-PZ5AY32C.js";
|
|
4
5
|
|
|
5
6
|
// src/stores/dynamodb.ts
|
|
6
7
|
import string from "@poppinss/utils/string";
|
|
@@ -104,4 +105,4 @@ var DynamoDBStore = class {
|
|
|
104
105
|
export {
|
|
105
106
|
DynamoDBStore
|
|
106
107
|
};
|
|
107
|
-
//# sourceMappingURL=dynamodb-
|
|
108
|
+
//# sourceMappingURL=dynamodb-53YXEFIJ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/stores/dynamodb.ts"],"sourcesContent":["/**\n * @adonisjs/session\n *\n * (c) AdonisJS\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nimport string from '@poppinss/utils/string'\nimport { MessageBuilder } from '@adonisjs/core/helpers'\nimport { marshall, unmarshall } from '@aws-sdk/util-dynamodb'\nimport {\n DynamoDBClient,\n GetItemCommand,\n PutItemCommand,\n DeleteItemCommand,\n UpdateItemCommand,\n} from '@aws-sdk/client-dynamodb'\n\nimport debug from '../debug.js'\nimport type { SessionStoreContract, SessionData } from '../types.js'\n\n/**\n * DynamoDB store to read/write session to DynamoDB\n */\nexport class DynamoDBStore implements SessionStoreContract {\n #client: DynamoDBClient\n #tableName: string\n #keyAttribute: string\n #ttlSeconds: number\n #valueAttribute: string = 'value'\n #expiresAtAttribute: string = 'expires_at'\n\n constructor(\n client: DynamoDBClient,\n age: string | number,\n options?: {\n /**\n * Defaults to \"Session\"\n */\n tableName?: string\n\n /**\n * Defaults to \"key\"\n */\n keyAttribute?: string\n }\n ) {\n this.#client = client\n this.#tableName = options?.tableName ?? 'Session'\n this.#keyAttribute = options?.keyAttribute ?? 'key'\n this.#ttlSeconds = string.seconds.parse(age)\n debug('initiating dynamodb store')\n }\n\n /**\n * Returns session data. A new item will be created if it's\n * missing.\n */\n async read(sessionId: string): Promise<SessionData | null> {\n debug('dynamodb store: reading session data %s', sessionId)\n\n const command = new GetItemCommand({\n TableName: this.#tableName,\n Key: marshall({ [this.#keyAttribute]: sessionId }),\n })\n\n const response = await this.#client.send(command)\n if (!response.Item) {\n return null\n }\n\n if (!response.Item[this.#valueAttribute]) {\n return null\n }\n\n const item = unmarshall(response.Item)\n const contents = item[this.#valueAttribute] as string\n const expiresAt = item[this.#expiresAtAttribute] as number\n\n /**\n * Check if the item has been expired and return null (if expired)\n */\n if (Date.now() > expiresAt) {\n return null\n }\n\n /**\n * Verify contents with the session id and return them as an object. The verify\n * method can fail when the contents is not JSON.\n */\n try {\n return new MessageBuilder().verify<SessionData>(contents, sessionId)\n } catch {\n return null\n }\n }\n\n /**\n * Write session values to DynamoDB\n */\n async write(sessionId: string, values: Object): Promise<void> {\n debug('dynamodb store: writing session data %s, %O', sessionId, values)\n\n const message = new MessageBuilder().build(values, undefined, sessionId)\n const command = new PutItemCommand({\n TableName: this.#tableName,\n Item: marshall({\n [this.#keyAttribute]: sessionId,\n [this.#valueAttribute]: message,\n [this.#expiresAtAttribute]: Date.now() + this.#ttlSeconds * 1000,\n }),\n })\n\n await this.#client.send(command)\n }\n\n /**\n * Cleanup session item by removing it\n */\n async destroy(sessionId: string): Promise<void> {\n debug('dynamodb store: destroying session data %s', sessionId)\n\n const command = new DeleteItemCommand({\n TableName: this.#tableName,\n Key: marshall({ [this.#keyAttribute]: sessionId }),\n })\n\n await this.#client.send(command)\n }\n\n /**\n * Updates the value expiry\n */\n async touch(sessionId: string): Promise<void> {\n debug('dynamodb store: touching session data %s', sessionId)\n\n const command = new UpdateItemCommand({\n TableName: this.#tableName,\n Key: marshall({ [this.#keyAttribute]: sessionId }),\n UpdateExpression: 'SET #expires_at = :expires_at',\n ExpressionAttributeNames: {\n '#expires_at': this.#expiresAtAttribute,\n },\n ExpressionAttributeValues: marshall({\n ':expires_at': Date.now() + this.#ttlSeconds * 1000,\n }),\n })\n\n await this.#client.send(command)\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/stores/dynamodb.ts"],"sourcesContent":["/**\n * @adonisjs/session\n *\n * (c) AdonisJS\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nimport string from '@poppinss/utils/string'\nimport { MessageBuilder } from '@adonisjs/core/helpers'\nimport { marshall, unmarshall } from '@aws-sdk/util-dynamodb'\nimport {\n DynamoDBClient,\n GetItemCommand,\n PutItemCommand,\n DeleteItemCommand,\n UpdateItemCommand,\n} from '@aws-sdk/client-dynamodb'\n\nimport debug from '../debug.js'\nimport type { SessionStoreContract, SessionData } from '../types.js'\n\n/**\n * DynamoDB store to read/write session to DynamoDB\n */\nexport class DynamoDBStore implements SessionStoreContract {\n #client: DynamoDBClient\n #tableName: string\n #keyAttribute: string\n #ttlSeconds: number\n #valueAttribute: string = 'value'\n #expiresAtAttribute: string = 'expires_at'\n\n constructor(\n client: DynamoDBClient,\n age: string | number,\n options?: {\n /**\n * Defaults to \"Session\"\n */\n tableName?: string\n\n /**\n * Defaults to \"key\"\n */\n keyAttribute?: string\n }\n ) {\n this.#client = client\n this.#tableName = options?.tableName ?? 'Session'\n this.#keyAttribute = options?.keyAttribute ?? 'key'\n this.#ttlSeconds = string.seconds.parse(age)\n debug('initiating dynamodb store')\n }\n\n /**\n * Returns session data. A new item will be created if it's\n * missing.\n */\n async read(sessionId: string): Promise<SessionData | null> {\n debug('dynamodb store: reading session data %s', sessionId)\n\n const command = new GetItemCommand({\n TableName: this.#tableName,\n Key: marshall({ [this.#keyAttribute]: sessionId }),\n })\n\n const response = await this.#client.send(command)\n if (!response.Item) {\n return null\n }\n\n if (!response.Item[this.#valueAttribute]) {\n return null\n }\n\n const item = unmarshall(response.Item)\n const contents = item[this.#valueAttribute] as string\n const expiresAt = item[this.#expiresAtAttribute] as number\n\n /**\n * Check if the item has been expired and return null (if expired)\n */\n if (Date.now() > expiresAt) {\n return null\n }\n\n /**\n * Verify contents with the session id and return them as an object. The verify\n * method can fail when the contents is not JSON.\n */\n try {\n return new MessageBuilder().verify<SessionData>(contents, sessionId)\n } catch {\n return null\n }\n }\n\n /**\n * Write session values to DynamoDB\n */\n async write(sessionId: string, values: Object): Promise<void> {\n debug('dynamodb store: writing session data %s, %O', sessionId, values)\n\n const message = new MessageBuilder().build(values, undefined, sessionId)\n const command = new PutItemCommand({\n TableName: this.#tableName,\n Item: marshall({\n [this.#keyAttribute]: sessionId,\n [this.#valueAttribute]: message,\n [this.#expiresAtAttribute]: Date.now() + this.#ttlSeconds * 1000,\n }),\n })\n\n await this.#client.send(command)\n }\n\n /**\n * Cleanup session item by removing it\n */\n async destroy(sessionId: string): Promise<void> {\n debug('dynamodb store: destroying session data %s', sessionId)\n\n const command = new DeleteItemCommand({\n TableName: this.#tableName,\n Key: marshall({ [this.#keyAttribute]: sessionId }),\n })\n\n await this.#client.send(command)\n }\n\n /**\n * Updates the value expiry\n */\n async touch(sessionId: string): Promise<void> {\n debug('dynamodb store: touching session data %s', sessionId)\n\n const command = new UpdateItemCommand({\n TableName: this.#tableName,\n Key: marshall({ [this.#keyAttribute]: sessionId }),\n UpdateExpression: 'SET #expires_at = :expires_at',\n ExpressionAttributeNames: {\n '#expires_at': this.#expiresAtAttribute,\n },\n ExpressionAttributeValues: marshall({\n ':expires_at': Date.now() + this.#ttlSeconds * 1000,\n }),\n })\n\n await this.#client.send(command)\n }\n}\n"],"mappings":";;;;;;AASA,OAAO,YAAY;AACnB,SAAS,sBAAsB;AAC/B,SAAS,UAAU,kBAAkB;AACrC;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAQA,IAAM,gBAAN,MAAoD;AAAA,EACzD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAA0B;AAAA,EAC1B,sBAA8B;AAAA,EAE9B,YACE,QACA,KACA,SAWA;AACA,SAAK,UAAU;AACf,SAAK,aAAa,SAAS,aAAa;AACxC,SAAK,gBAAgB,SAAS,gBAAgB;AAC9C,SAAK,cAAc,OAAO,QAAQ,MAAM,GAAG;AAC3C,kBAAM,2BAA2B;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,KAAK,WAAgD;AACzD,kBAAM,2CAA2C,SAAS;AAE1D,UAAM,UAAU,IAAI,eAAe;AAAA,MACjC,WAAW,KAAK;AAAA,MAChB,KAAK,SAAS,EAAE,CAAC,KAAK,aAAa,GAAG,UAAU,CAAC;AAAA,IACnD,CAAC;AAED,UAAM,WAAW,MAAM,KAAK,QAAQ,KAAK,OAAO;AAChD,QAAI,CAAC,SAAS,MAAM;AAClB,aAAO;AAAA,IACT;AAEA,QAAI,CAAC,SAAS,KAAK,KAAK,eAAe,GAAG;AACxC,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,WAAW,SAAS,IAAI;AACrC,UAAM,WAAW,KAAK,KAAK,eAAe;AAC1C,UAAM,YAAY,KAAK,KAAK,mBAAmB;AAK/C,QAAI,KAAK,IAAI,IAAI,WAAW;AAC1B,aAAO;AAAA,IACT;AAMA,QAAI;AACF,aAAO,IAAI,eAAe,EAAE,OAAoB,UAAU,SAAS;AAAA,IACrE,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAM,WAAmB,QAA+B;AAC5D,kBAAM,+CAA+C,WAAW,MAAM;AAEtE,UAAM,UAAU,IAAI,eAAe,EAAE,MAAM,QAAQ,QAAW,SAAS;AACvE,UAAM,UAAU,IAAI,eAAe;AAAA,MACjC,WAAW,KAAK;AAAA,MAChB,MAAM,SAAS;AAAA,QACb,CAAC,KAAK,aAAa,GAAG;AAAA,QACtB,CAAC,KAAK,eAAe,GAAG;AAAA,QACxB,CAAC,KAAK,mBAAmB,GAAG,KAAK,IAAI,IAAI,KAAK,cAAc;AAAA,MAC9D,CAAC;AAAA,IACH,CAAC;AAED,UAAM,KAAK,QAAQ,KAAK,OAAO;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,WAAkC;AAC9C,kBAAM,8CAA8C,SAAS;AAE7D,UAAM,UAAU,IAAI,kBAAkB;AAAA,MACpC,WAAW,KAAK;AAAA,MAChB,KAAK,SAAS,EAAE,CAAC,KAAK,aAAa,GAAG,UAAU,CAAC;AAAA,IACnD,CAAC;AAED,UAAM,KAAK,QAAQ,KAAK,OAAO;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAM,WAAkC;AAC5C,kBAAM,4CAA4C,SAAS;AAE3D,UAAM,UAAU,IAAI,kBAAkB;AAAA,MACpC,WAAW,KAAK;AAAA,MAChB,KAAK,SAAS,EAAE,CAAC,KAAK,aAAa,GAAG,UAAU,CAAC;AAAA,MACjD,kBAAkB;AAAA,MAClB,0BAA0B;AAAA,QACxB,eAAe,KAAK;AAAA,MACtB;AAAA,MACA,2BAA2B,SAAS;AAAA,QAClC,eAAe,KAAK,IAAI,IAAI,KAAK,cAAc;AAAA,MACjD,CAAC;AAAA,IACH,CAAC;AAED,UAAM,KAAK,QAAQ,KAAK,OAAO;AAAA,EACjC;AACF;","names":[]}
|
package/build/factories/main.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
defineConfig
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-Q24L5XPC.js";
|
|
4
|
+
import "../chunk-V3OAEXMJ.js";
|
|
5
|
+
import "../chunk-G7KFHA57.js";
|
|
4
6
|
import {
|
|
5
7
|
SessionMiddleware
|
|
6
|
-
} from "../chunk-
|
|
7
|
-
import "../chunk-
|
|
8
|
+
} from "../chunk-6BSSM3HO.js";
|
|
9
|
+
import "../chunk-XP3CBOXR.js";
|
|
8
10
|
import "../chunk-TE5JP3SX.js";
|
|
9
|
-
import "../chunk-
|
|
11
|
+
import "../chunk-5ECC6OWF.js";
|
|
12
|
+
import "../chunk-PZ5AY32C.js";
|
|
10
13
|
|
|
11
14
|
// factories/session_middleware_factory.ts
|
|
12
15
|
import { Emitter } from "@adonisjs/core/events";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../factories/session_middleware_factory.ts"],"sourcesContent":["/*\n * @adonisjs/session\n *\n * (c) AdonisJS\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nimport { Emitter } from '@adonisjs/core/events'\nimport { AppFactory } from '@adonisjs/core/factories/app'\nimport type { ApplicationService, EventsList } from '@adonisjs/core/types'\n\nimport { defineConfig } from '../index.js'\nimport SessionMiddleware from '../src/session_middleware.js'\nimport type { SessionConfig, SessionStoreFactory } from '../src/types.js'\n\n/**\n * Exposes the API to create an instance of the session middleware\n * without additional plumbing\n */\nexport class SessionMiddlewareFactory {\n #config: Partial<SessionConfig> & {\n store: string\n stores: Record<string, SessionStoreFactory>\n } = {\n store: 'memory',\n stores: {},\n }\n\n #emitter?: Emitter<EventsList>\n\n #getApp() {\n return new AppFactory().create(new URL('./', import.meta.url), () => {}) as ApplicationService\n }\n\n #getEmitter() {\n return this.#emitter || new Emitter<EventsList>(this.#getApp())\n }\n\n /**\n * Merge custom options\n */\n merge(options: {\n config?: Partial<SessionConfig> & {\n store: string\n stores: Record<string, SessionStoreFactory>\n }\n emitter?: Emitter<EventsList>\n }) {\n if (options.config) {\n this.#config = options.config\n }\n\n if (options.emitter) {\n this.#emitter = options.emitter\n }\n\n return this\n }\n\n /**\n * Creates an instance of the session middleware\n */\n async create() {\n const config = await defineConfig(this.#config).resolver(this.#getApp())\n return new SessionMiddleware(config, this.#getEmitter())\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../factories/session_middleware_factory.ts"],"sourcesContent":["/*\n * @adonisjs/session\n *\n * (c) AdonisJS\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nimport { Emitter } from '@adonisjs/core/events'\nimport { AppFactory } from '@adonisjs/core/factories/app'\nimport type { ApplicationService, EventsList } from '@adonisjs/core/types'\n\nimport { defineConfig } from '../index.js'\nimport SessionMiddleware from '../src/session_middleware.js'\nimport type { SessionConfig, SessionStoreFactory } from '../src/types.js'\n\n/**\n * Exposes the API to create an instance of the session middleware\n * without additional plumbing\n */\nexport class SessionMiddlewareFactory {\n #config: Partial<SessionConfig> & {\n store: string\n stores: Record<string, SessionStoreFactory>\n } = {\n store: 'memory',\n stores: {},\n }\n\n #emitter?: Emitter<EventsList>\n\n #getApp() {\n return new AppFactory().create(new URL('./', import.meta.url), () => {}) as ApplicationService\n }\n\n #getEmitter() {\n return this.#emitter || new Emitter<EventsList>(this.#getApp())\n }\n\n /**\n * Merge custom options\n */\n merge(options: {\n config?: Partial<SessionConfig> & {\n store: string\n stores: Record<string, SessionStoreFactory>\n }\n emitter?: Emitter<EventsList>\n }) {\n if (options.config) {\n this.#config = options.config\n }\n\n if (options.emitter) {\n this.#emitter = options.emitter\n }\n\n return this\n }\n\n /**\n * Creates an instance of the session middleware\n */\n async create() {\n const config = await defineConfig(this.#config).resolver(this.#getApp())\n return new SessionMiddleware(config, this.#getEmitter())\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AASA,SAAS,eAAe;AACxB,SAAS,kBAAkB;AAWpB,IAAM,2BAAN,MAA+B;AAAA,EACpC,UAGI;AAAA,IACF,OAAO;AAAA,IACP,QAAQ,CAAC;AAAA,EACX;AAAA,EAEA;AAAA,EAEA,UAAU;AACR,WAAO,IAAI,WAAW,EAAE,OAAO,IAAI,IAAI,MAAM,YAAY,GAAG,GAAG,MAAM;AAAA,IAAC,CAAC;AAAA,EACzE;AAAA,EAEA,cAAc;AACZ,WAAO,KAAK,YAAY,IAAI,QAAoB,KAAK,QAAQ,CAAC;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAMH;AACD,QAAI,QAAQ,QAAQ;AAClB,WAAK,UAAU,QAAQ;AAAA,IACzB;AAEA,QAAI,QAAQ,SAAS;AACnB,WAAK,WAAW,QAAQ;AAAA,IAC1B;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,SAAS;AACb,UAAM,SAAS,MAAM,aAAa,KAAK,OAAO,EAAE,SAAS,KAAK,QAAQ,CAAC;AACvE,WAAO,IAAI,kBAAkB,QAAQ,KAAK,YAAY,CAAC;AAAA,EACzD;AACF;","names":[]}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
debug_default
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-5ECC6OWF.js";
|
|
4
|
+
import "./chunk-PZ5AY32C.js";
|
|
4
5
|
|
|
5
6
|
// src/stores/file.ts
|
|
6
|
-
import { dirname, join } from "
|
|
7
|
+
import { dirname, join } from "path";
|
|
7
8
|
import string from "@poppinss/utils/string";
|
|
8
9
|
import { MessageBuilder } from "@adonisjs/core/helpers";
|
|
9
|
-
import { access, mkdir, readFile, rm, writeFile, utimes, stat } from "
|
|
10
|
+
import { access, mkdir, readFile, rm, writeFile, utimes, stat } from "fs/promises";
|
|
10
11
|
var FileStore = class {
|
|
11
12
|
#config;
|
|
12
13
|
#age;
|
|
@@ -113,4 +114,4 @@ var FileStore = class {
|
|
|
113
114
|
export {
|
|
114
115
|
FileStore
|
|
115
116
|
};
|
|
116
|
-
//# sourceMappingURL=file-
|
|
117
|
+
//# sourceMappingURL=file-K3GBSVSU.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/stores/file.ts"],"sourcesContent":["/**\n * @adonisjs/session\n *\n * (c) AdonisJS\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nimport type { Stats } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport string from '@poppinss/utils/string'\nimport { MessageBuilder } from '@adonisjs/core/helpers'\nimport { access, mkdir, readFile, rm, writeFile, utimes, stat } from 'node:fs/promises'\n\nimport debug from '../debug.js'\nimport type { FileStoreConfig, SessionData, SessionStoreContract } from '../types.js'\n\n/**\n * File store writes the session data on the file system as. Each session\n * id gets its own file.\n *\n */\nexport class FileStore implements SessionStoreContract {\n #config: FileStoreConfig\n #age: string | number\n\n /**\n * @param {FileStoreConfig} config\n * @param {string|number} The age must be in seconds or a time expression\n */\n constructor(config: FileStoreConfig, age: string | number) {\n this.#config = config\n this.#age = age\n debug('initiating file store %O', this.#config)\n }\n\n /**\n * Returns an absolute path to the session id file\n */\n #getFilePath(sessionId: string): string {\n return join(this.#config.location, `${sessionId}.txt`)\n }\n\n /**\n * Check if a file exists at a given path or not\n */\n async #pathExists(path: string) {\n try {\n await access(path)\n return true\n } catch {\n return false\n }\n }\n\n /**\n * Returns stats for a file and ignoring missing\n * files.\n */\n async #stats(path: string): Promise<Stats | null> {\n try {\n const stats = await stat(path)\n return stats\n } catch {\n return null\n }\n }\n\n /**\n * Output file with contents to the given path\n */\n async #outputFile(path: string, contents: string) {\n const pathDirname = dirname(path)\n\n const dirExists = await this.#pathExists(pathDirname)\n if (!dirExists) {\n await mkdir(pathDirname, { recursive: true })\n }\n\n await writeFile(path, contents, 'utf-8')\n }\n\n /**\n * Reads the session data from the disk.\n */\n async read(sessionId: string): Promise<SessionData | null> {\n const filePath = this.#getFilePath(sessionId)\n debug('file store: reading session data %', sessionId)\n\n /**\n * Return null when no session id file exists in first\n * place\n */\n const stats = await this.#stats(filePath)\n if (!stats) {\n return null\n }\n\n /**\n * Check if the file has been expired and return null (if expired)\n */\n const sessionWillExpireAt = stats.mtimeMs + string.seconds.parse(this.#age) * 1000\n if (Date.now() > sessionWillExpireAt) {\n debug('file store: expired session data %s', sessionId)\n return null\n }\n\n /**\n * Reading the file contents if the file exists\n */\n let contents = await readFile(filePath, 'utf-8')\n contents = contents.trim()\n if (!contents) {\n return null\n }\n\n /**\n * Verify contents with the session id and return them as an object. The verify\n * method can fail when the contents is not JSON>\n */\n try {\n return new MessageBuilder().verify<SessionData>(contents, sessionId)\n } catch {\n return null\n }\n }\n\n /**\n * Writes the session data to the disk as a string\n */\n async write(sessionId: string, values: SessionData): Promise<void> {\n debug('file store: writing session data %s: %O', sessionId, values)\n\n const filePath = this.#getFilePath(sessionId)\n const message = new MessageBuilder().build(values, undefined, sessionId)\n\n await this.#outputFile(filePath, message)\n }\n\n /**\n * Removes the session file from the disk\n */\n async destroy(sessionId: string): Promise<void> {\n debug('file store: destroying session data %s', sessionId)\n await rm(this.#getFilePath(sessionId), { force: true })\n }\n\n /**\n * Updates the session expiry by rewriting it to the\n * persistence store\n */\n async touch(sessionId: string): Promise<void> {\n debug('file store: touching session data %s', sessionId)\n await utimes(this.#getFilePath(sessionId), new Date(), new Date())\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../src/stores/file.ts"],"sourcesContent":["/**\n * @adonisjs/session\n *\n * (c) AdonisJS\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nimport type { Stats } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport string from '@poppinss/utils/string'\nimport { MessageBuilder } from '@adonisjs/core/helpers'\nimport { access, mkdir, readFile, rm, writeFile, utimes, stat } from 'node:fs/promises'\n\nimport debug from '../debug.js'\nimport type { FileStoreConfig, SessionData, SessionStoreContract } from '../types.js'\n\n/**\n * File store writes the session data on the file system as. Each session\n * id gets its own file.\n *\n */\nexport class FileStore implements SessionStoreContract {\n #config: FileStoreConfig\n #age: string | number\n\n /**\n * @param {FileStoreConfig} config\n * @param {string|number} The age must be in seconds or a time expression\n */\n constructor(config: FileStoreConfig, age: string | number) {\n this.#config = config\n this.#age = age\n debug('initiating file store %O', this.#config)\n }\n\n /**\n * Returns an absolute path to the session id file\n */\n #getFilePath(sessionId: string): string {\n return join(this.#config.location, `${sessionId}.txt`)\n }\n\n /**\n * Check if a file exists at a given path or not\n */\n async #pathExists(path: string) {\n try {\n await access(path)\n return true\n } catch {\n return false\n }\n }\n\n /**\n * Returns stats for a file and ignoring missing\n * files.\n */\n async #stats(path: string): Promise<Stats | null> {\n try {\n const stats = await stat(path)\n return stats\n } catch {\n return null\n }\n }\n\n /**\n * Output file with contents to the given path\n */\n async #outputFile(path: string, contents: string) {\n const pathDirname = dirname(path)\n\n const dirExists = await this.#pathExists(pathDirname)\n if (!dirExists) {\n await mkdir(pathDirname, { recursive: true })\n }\n\n await writeFile(path, contents, 'utf-8')\n }\n\n /**\n * Reads the session data from the disk.\n */\n async read(sessionId: string): Promise<SessionData | null> {\n const filePath = this.#getFilePath(sessionId)\n debug('file store: reading session data %', sessionId)\n\n /**\n * Return null when no session id file exists in first\n * place\n */\n const stats = await this.#stats(filePath)\n if (!stats) {\n return null\n }\n\n /**\n * Check if the file has been expired and return null (if expired)\n */\n const sessionWillExpireAt = stats.mtimeMs + string.seconds.parse(this.#age) * 1000\n if (Date.now() > sessionWillExpireAt) {\n debug('file store: expired session data %s', sessionId)\n return null\n }\n\n /**\n * Reading the file contents if the file exists\n */\n let contents = await readFile(filePath, 'utf-8')\n contents = contents.trim()\n if (!contents) {\n return null\n }\n\n /**\n * Verify contents with the session id and return them as an object. The verify\n * method can fail when the contents is not JSON>\n */\n try {\n return new MessageBuilder().verify<SessionData>(contents, sessionId)\n } catch {\n return null\n }\n }\n\n /**\n * Writes the session data to the disk as a string\n */\n async write(sessionId: string, values: SessionData): Promise<void> {\n debug('file store: writing session data %s: %O', sessionId, values)\n\n const filePath = this.#getFilePath(sessionId)\n const message = new MessageBuilder().build(values, undefined, sessionId)\n\n await this.#outputFile(filePath, message)\n }\n\n /**\n * Removes the session file from the disk\n */\n async destroy(sessionId: string): Promise<void> {\n debug('file store: destroying session data %s', sessionId)\n await rm(this.#getFilePath(sessionId), { force: true })\n }\n\n /**\n * Updates the session expiry by rewriting it to the\n * persistence store\n */\n async touch(sessionId: string): Promise<void> {\n debug('file store: touching session data %s', sessionId)\n await utimes(this.#getFilePath(sessionId), new Date(), new Date())\n }\n}\n"],"mappings":";;;;;;AAUA,SAAS,SAAS,YAAY;AAC9B,OAAO,YAAY;AACnB,SAAS,sBAAsB;AAC/B,SAAS,QAAQ,OAAO,UAAU,IAAI,WAAW,QAAQ,YAAY;AAU9D,IAAM,YAAN,MAAgD;AAAA,EACrD;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,YAAY,QAAyB,KAAsB;AACzD,SAAK,UAAU;AACf,SAAK,OAAO;AACZ,kBAAM,4BAA4B,KAAK,OAAO;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,WAA2B;AACtC,WAAO,KAAK,KAAK,QAAQ,UAAU,GAAG,SAAS,MAAM;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,MAAc;AAC9B,QAAI;AACF,YAAM,OAAO,IAAI;AACjB,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAO,MAAqC;AAChD,QAAI;AACF,YAAM,QAAQ,MAAM,KAAK,IAAI;AAC7B,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAY,MAAc,UAAkB;AAChD,UAAM,cAAc,QAAQ,IAAI;AAEhC,UAAM,YAAY,MAAM,KAAK,YAAY,WAAW;AACpD,QAAI,CAAC,WAAW;AACd,YAAM,MAAM,aAAa,EAAE,WAAW,KAAK,CAAC;AAAA,IAC9C;AAEA,UAAM,UAAU,MAAM,UAAU,OAAO;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,KAAK,WAAgD;AACzD,UAAM,WAAW,KAAK,aAAa,SAAS;AAC5C,kBAAM,sCAAsC,SAAS;AAMrD,UAAM,QAAQ,MAAM,KAAK,OAAO,QAAQ;AACxC,QAAI,CAAC,OAAO;AACV,aAAO;AAAA,IACT;AAKA,UAAM,sBAAsB,MAAM,UAAU,OAAO,QAAQ,MAAM,KAAK,IAAI,IAAI;AAC9E,QAAI,KAAK,IAAI,IAAI,qBAAqB;AACpC,oBAAM,uCAAuC,SAAS;AACtD,aAAO;AAAA,IACT;AAKA,QAAI,WAAW,MAAM,SAAS,UAAU,OAAO;AAC/C,eAAW,SAAS,KAAK;AACzB,QAAI,CAAC,UAAU;AACb,aAAO;AAAA,IACT;AAMA,QAAI;AACF,aAAO,IAAI,eAAe,EAAE,OAAoB,UAAU,SAAS;AAAA,IACrE,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAM,WAAmB,QAAoC;AACjE,kBAAM,2CAA2C,WAAW,MAAM;AAElE,UAAM,WAAW,KAAK,aAAa,SAAS;AAC5C,UAAM,UAAU,IAAI,eAAe,EAAE,MAAM,QAAQ,QAAW,SAAS;AAEvE,UAAM,KAAK,YAAY,UAAU,OAAO;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,WAAkC;AAC9C,kBAAM,0CAA0C,SAAS;AACzD,UAAM,GAAG,KAAK,aAAa,SAAS,GAAG,EAAE,OAAO,KAAK,CAAC;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,MAAM,WAAkC;AAC5C,kBAAM,wCAAwC,SAAS;AACvD,UAAM,OAAO,KAAK,aAAa,SAAS,GAAG,oBAAI,KAAK,GAAG,oBAAI,KAAK,CAAC;AAAA,EACnE;AACF;","names":[]}
|
package/build/index.d.ts
CHANGED
|
@@ -11,4 +11,5 @@ export { configure } from './configure.js';
|
|
|
11
11
|
export { Session } from './src/session.js';
|
|
12
12
|
export { stubsRoot } from './stubs/main.js';
|
|
13
13
|
export { defineConfig, stores } from './src/define_config.js';
|
|
14
|
+
export { SessionCollection } from './src/session_collection.js';
|
|
14
15
|
export { ReadOnlyValuesStore, ValuesStore } from './src/values_store.js';
|
package/build/index.js
CHANGED
|
@@ -1,21 +1,28 @@
|
|
|
1
1
|
import {
|
|
2
2
|
configure,
|
|
3
3
|
defineConfig,
|
|
4
|
-
stores
|
|
4
|
+
stores
|
|
5
|
+
} from "./chunk-Q24L5XPC.js";
|
|
6
|
+
import {
|
|
5
7
|
stubsRoot
|
|
6
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-V3OAEXMJ.js";
|
|
9
|
+
import {
|
|
10
|
+
SessionCollection
|
|
11
|
+
} from "./chunk-G7KFHA57.js";
|
|
7
12
|
import {
|
|
8
13
|
Session,
|
|
9
14
|
errors_exports
|
|
10
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-XP3CBOXR.js";
|
|
11
16
|
import {
|
|
12
17
|
ReadOnlyValuesStore,
|
|
13
18
|
ValuesStore
|
|
14
19
|
} from "./chunk-TE5JP3SX.js";
|
|
15
|
-
import "./chunk-
|
|
20
|
+
import "./chunk-5ECC6OWF.js";
|
|
21
|
+
import "./chunk-PZ5AY32C.js";
|
|
16
22
|
export {
|
|
17
23
|
ReadOnlyValuesStore,
|
|
18
24
|
Session,
|
|
25
|
+
SessionCollection,
|
|
19
26
|
ValuesStore,
|
|
20
27
|
configure,
|
|
21
28
|
defineConfig,
|
|
@@ -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,9 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SessionCollection
|
|
3
|
+
} from "../chunk-G7KFHA57.js";
|
|
1
4
|
import {
|
|
2
5
|
SessionMiddleware
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
6
|
+
} from "../chunk-6BSSM3HO.js";
|
|
7
|
+
import "../chunk-XP3CBOXR.js";
|
|
5
8
|
import "../chunk-TE5JP3SX.js";
|
|
6
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-5ECC6OWF.js";
|
|
10
|
+
import "../chunk-PZ5AY32C.js";
|
|
7
11
|
|
|
8
12
|
// providers/session_provider.ts
|
|
9
13
|
import { configProvider } from "@adonisjs/core";
|
|
@@ -24,20 +28,31 @@ var SessionProvider = class {
|
|
|
24
28
|
}
|
|
25
29
|
}
|
|
26
30
|
/**
|
|
27
|
-
*
|
|
31
|
+
* Resolves the session config from the config provider
|
|
32
|
+
*/
|
|
33
|
+
async #resolveConfig() {
|
|
34
|
+
const sessionConfigProvider = this.app.config.get("session", {});
|
|
35
|
+
const config = await configProvider.resolve(this.app, sessionConfigProvider);
|
|
36
|
+
if (!config) {
|
|
37
|
+
throw new RuntimeException(
|
|
38
|
+
'Invalid "config/session.ts" file. Make sure you are using the "defineConfig" method'
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
return config;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Registering bindings
|
|
28
45
|
*/
|
|
29
46
|
register() {
|
|
30
47
|
this.app.container.singleton(SessionMiddleware, async (resolver) => {
|
|
31
|
-
const
|
|
32
|
-
const config = await configProvider.resolve(this.app, sessionConfigProvider);
|
|
33
|
-
if (!config) {
|
|
34
|
-
throw new RuntimeException(
|
|
35
|
-
'Invalid "config/session.ts" file. Make sure you are using the "defineConfig" method'
|
|
36
|
-
);
|
|
37
|
-
}
|
|
48
|
+
const config = await this.#resolveConfig();
|
|
38
49
|
const emitter = await resolver.make("emitter");
|
|
39
50
|
return new SessionMiddleware(config, emitter);
|
|
40
51
|
});
|
|
52
|
+
this.app.container.singleton(SessionCollection, async () => {
|
|
53
|
+
const config = await this.#resolveConfig();
|
|
54
|
+
return new SessionCollection(config);
|
|
55
|
+
});
|
|
41
56
|
}
|
|
42
57
|
/**
|
|
43
58
|
* Adding edge tags (if edge is installed)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../providers/session_provider.ts"],"sourcesContent":["/*\n * @adonisjs/session\n *\n * (c) AdonisJS\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nimport { configProvider } from '@adonisjs/core'\nimport { RuntimeException } from '@poppinss/utils'\nimport type { ApplicationService } from '@adonisjs/core/types'\n\nimport type { Session } from '../src/session.js'\nimport SessionMiddleware from '../src/session_middleware.js'\n\n/**\n * Events emitted by the session class\n */\ndeclare module '@adonisjs/core/types' {\n interface EventsList {\n 'session:initiated': { session: Session }\n 'session:committed': { session: Session }\n 'session:migrated': { fromSessionId: string; toSessionId: string; session: Session }\n }\n}\n\n/**\n * Session provider configures the session management inside an\n * AdonisJS application\n */\nexport default class SessionProvider {\n constructor(protected app: ApplicationService) {}\n\n /**\n * Registers edge plugin when edge is installed\n * in the user application.\n */\n protected async registerEdgePlugin() {\n if (this.app.usingEdgeJS) {\n const edge = await import('edge.js')\n const { edgePluginSession } = await import('../src/plugins/edge.js')\n edge.default.use(edgePluginSession)\n }\n }\n\n /**\n *
|
|
1
|
+
{"version":3,"sources":["../../providers/session_provider.ts"],"sourcesContent":["/*\n * @adonisjs/session\n *\n * (c) AdonisJS\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nimport { configProvider } from '@adonisjs/core'\nimport { RuntimeException } from '@poppinss/utils'\nimport type { ApplicationService } from '@adonisjs/core/types'\n\nimport type { Session } from '../src/session.js'\nimport SessionMiddleware from '../src/session_middleware.js'\nimport { SessionCollection } from '../src/session_collection.js'\n\n/**\n * Events emitted by the session class\n */\ndeclare module '@adonisjs/core/types' {\n interface EventsList {\n 'session:initiated': { session: Session }\n 'session:committed': { session: Session }\n 'session:migrated': { fromSessionId: string; toSessionId: string; session: Session }\n }\n}\n\n/**\n * Session provider configures the session management inside an\n * AdonisJS application\n */\nexport default class SessionProvider {\n constructor(protected app: ApplicationService) {}\n\n /**\n * Registers edge plugin when edge is installed\n * in the user application.\n */\n protected async registerEdgePlugin() {\n if (this.app.usingEdgeJS) {\n const edge = await import('edge.js')\n const { edgePluginSession } = await import('../src/plugins/edge.js')\n edge.default.use(edgePluginSession)\n }\n }\n\n /**\n * Resolves the session config from the config provider\n */\n async #resolveConfig() {\n const sessionConfigProvider = this.app.config.get('session', {})\n const config = await configProvider.resolve<any>(this.app, sessionConfigProvider)\n\n if (!config) {\n throw new RuntimeException(\n 'Invalid \"config/session.ts\" file. Make sure you are using the \"defineConfig\" method'\n )\n }\n\n return config\n }\n\n /**\n * Registering bindings\n */\n register() {\n this.app.container.singleton(SessionMiddleware, async (resolver) => {\n const config = await this.#resolveConfig()\n const emitter = await resolver.make('emitter')\n return new SessionMiddleware(config, emitter)\n })\n\n this.app.container.singleton(SessionCollection, async () => {\n const config = await this.#resolveConfig()\n return new SessionCollection(config)\n })\n }\n\n /**\n * Adding edge tags (if edge is installed)\n */\n async boot() {\n await this.registerEdgePlugin()\n }\n}\n"],"mappings":";;;;;;;;;;;;AASA,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AAsBjC,IAAqB,kBAArB,MAAqC;AAAA,EACnC,YAAsB,KAAyB;AAAzB;AAAA,EAA0B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMhD,MAAgB,qBAAqB;AACnC,QAAI,KAAK,IAAI,aAAa;AACxB,YAAM,OAAO,MAAM,OAAO,SAAS;AACnC,YAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,wBAAwB;AACnE,WAAK,QAAQ,IAAI,iBAAiB;AAAA,IACpC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,iBAAiB;AACrB,UAAM,wBAAwB,KAAK,IAAI,OAAO,IAAI,WAAW,CAAC,CAAC;AAC/D,UAAM,SAAS,MAAM,eAAe,QAAa,KAAK,KAAK,qBAAqB;AAEhF,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW;AACT,SAAK,IAAI,UAAU,UAAU,mBAAmB,OAAO,aAAa;AAClE,YAAM,SAAS,MAAM,KAAK,eAAe;AACzC,YAAM,UAAU,MAAM,SAAS,KAAK,SAAS;AAC7C,aAAO,IAAI,kBAAkB,QAAQ,OAAO;AAAA,IAC9C,CAAC;AAED,SAAK,IAAI,UAAU,UAAU,mBAAmB,YAAY;AAC1D,YAAM,SAAS,MAAM,KAAK,eAAe;AACzC,aAAO,IAAI,kBAAkB,MAAM;AAAA,IACrC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAAO;AACX,UAAM,KAAK,mBAAmB;AAAA,EAChC;AACF;","names":[]}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {
|
|
2
|
+
debug_default
|
|
3
|
+
} from "./chunk-5ECC6OWF.js";
|
|
4
|
+
import "./chunk-PZ5AY32C.js";
|
|
5
|
+
|
|
6
|
+
// src/stores/redis.ts
|
|
7
|
+
import string from "@poppinss/utils/string";
|
|
8
|
+
import { MessageBuilder } from "@adonisjs/core/helpers";
|
|
9
|
+
var RedisStore = class {
|
|
10
|
+
#connection;
|
|
11
|
+
#ttlSeconds;
|
|
12
|
+
constructor(connection, age) {
|
|
13
|
+
this.#connection = connection;
|
|
14
|
+
this.#ttlSeconds = string.seconds.parse(age);
|
|
15
|
+
debug_default("initiating redis store");
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Returns the key for a user's tag set (stores session IDs for a user)
|
|
19
|
+
*/
|
|
20
|
+
#getTagKey(userId) {
|
|
21
|
+
return `session_tag:${userId}`;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Verify contents with the session id and return them as an object. The verify
|
|
25
|
+
* method can fail when the contents is not JSON
|
|
26
|
+
*/
|
|
27
|
+
#parseSessionData(contents, sessionId) {
|
|
28
|
+
try {
|
|
29
|
+
return new MessageBuilder().verify(contents, sessionId);
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Returns session data
|
|
36
|
+
*/
|
|
37
|
+
async read(sessionId) {
|
|
38
|
+
debug_default("redis store: reading session data %s", sessionId);
|
|
39
|
+
const contents = await this.#connection.get(sessionId);
|
|
40
|
+
if (!contents) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return this.#parseSessionData(contents, sessionId);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Write session values to redis
|
|
47
|
+
*/
|
|
48
|
+
async write(sessionId, values) {
|
|
49
|
+
debug_default("redis store: writing session data %s, %O", sessionId, values);
|
|
50
|
+
const message = new MessageBuilder().build(values, void 0, sessionId);
|
|
51
|
+
await this.#connection.setex(sessionId, this.#ttlSeconds, message);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Cleanup session by removing it
|
|
55
|
+
*/
|
|
56
|
+
async destroy(sessionId) {
|
|
57
|
+
debug_default("redis store: destroying session data %s", sessionId);
|
|
58
|
+
await this.#connection.del(sessionId);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Updates the value expiry
|
|
62
|
+
*/
|
|
63
|
+
async touch(sessionId) {
|
|
64
|
+
debug_default("redis store: touching session data %s", sessionId);
|
|
65
|
+
await this.#connection.expire(sessionId, this.#ttlSeconds);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Tag a session with a user ID
|
|
69
|
+
*/
|
|
70
|
+
async tag(sessionId, userId) {
|
|
71
|
+
debug_default("redis store: tagging session %s with user %s", sessionId, userId);
|
|
72
|
+
await this.#connection.sadd(this.#getTagKey(userId), sessionId);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Processes a single session result from the pipeline
|
|
76
|
+
*/
|
|
77
|
+
#processSessionResult(options) {
|
|
78
|
+
if (!options.contents) return { session: null, isInvalid: true };
|
|
79
|
+
const data = this.#parseSessionData(options.contents, options.sessionId);
|
|
80
|
+
if (!data) return { session: null, isInvalid: true };
|
|
81
|
+
return { session: { id: options.sessionId, data }, isInvalid: false };
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Fetches session contents for multiple session IDs using a pipeline
|
|
85
|
+
*/
|
|
86
|
+
async #fetchSessionContents(sessionIds) {
|
|
87
|
+
const pipeline = this.#connection.pipeline();
|
|
88
|
+
sessionIds.forEach((sessionId) => pipeline.get(sessionId));
|
|
89
|
+
const results = await pipeline.exec();
|
|
90
|
+
return results?.map((result) => result[1]) ?? [];
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Removes invalid session IDs from the user's tag set
|
|
94
|
+
*/
|
|
95
|
+
async #cleanupInvalidSessions(userId, invalidSessionIds) {
|
|
96
|
+
if (invalidSessionIds.length === 0) return;
|
|
97
|
+
await this.#connection.srem(this.#getTagKey(userId), ...invalidSessionIds);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get all sessions for a given user ID (tag)
|
|
101
|
+
*/
|
|
102
|
+
async tagged(userId) {
|
|
103
|
+
debug_default("redis store: getting sessions tagged with user %s", userId);
|
|
104
|
+
const sessionIds = await this.#connection.smembers(this.#getTagKey(userId));
|
|
105
|
+
if (sessionIds.length === 0) return [];
|
|
106
|
+
const contents = await this.#fetchSessionContents(sessionIds);
|
|
107
|
+
const results = sessionIds.map(
|
|
108
|
+
(sessionId, index) => this.#processSessionResult({ sessionId, contents: contents[index] })
|
|
109
|
+
);
|
|
110
|
+
const validSessions = results.filter((r) => r.session !== null).map((r) => r.session);
|
|
111
|
+
const invalidSessionIds = results.map((result, index) => result.isInvalid ? sessionIds[index] : null).filter((id) => id !== null);
|
|
112
|
+
await this.#cleanupInvalidSessions(userId, invalidSessionIds);
|
|
113
|
+
return validSessions;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
export {
|
|
117
|
+
RedisStore
|
|
118
|
+
};
|
|
119
|
+
//# sourceMappingURL=redis-YGX2CNE2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/stores/redis.ts"],"sourcesContent":["/**\n * @adonisjs/session\n *\n * (c) AdonisJS\n *\n * For the full copyright and license information, please view the LICENSE\n * file that was distributed with this source code.\n */\n\nimport string from '@poppinss/utils/string'\nimport { MessageBuilder } from '@adonisjs/core/helpers'\nimport type { Connection } from '@adonisjs/redis/types'\n\nimport debug from '../debug.js'\nimport type { SessionStoreWithTaggingContract, SessionData, TaggedSession } from '../types.js'\n\n/**\n * Redis store to read/write session to Redis\n */\nexport class RedisStore implements SessionStoreWithTaggingContract {\n #connection: Connection\n #ttlSeconds: number\n\n constructor(connection: Connection, age: string | number) {\n this.#connection = connection\n this.#ttlSeconds = string.seconds.parse(age)\n debug('initiating redis store')\n }\n\n /**\n * Returns the key for a user's tag set (stores session IDs for a user)\n */\n #getTagKey(userId: string): string {\n return `session_tag:${userId}`\n }\n\n /**\n * Verify contents with the session id and return them as an object. The verify\n * method can fail when the contents is not JSON\n */\n #parseSessionData(contents: string, sessionId: string): SessionData | null {\n try {\n return new MessageBuilder().verify<SessionData>(contents, sessionId)\n } catch {\n return null\n }\n }\n\n /**\n * Returns session data\n */\n async read(sessionId: string): Promise<SessionData | null> {\n debug('redis store: reading session data %s', sessionId)\n\n const contents = await this.#connection.get(sessionId)\n if (!contents) {\n return null\n }\n\n return this.#parseSessionData(contents, sessionId)\n }\n\n /**\n * Write session values to redis\n */\n async write(sessionId: string, values: Record<string, any>): Promise<void> {\n debug('redis store: writing session data %s, %O', sessionId, values)\n\n const message = new MessageBuilder().build(values, undefined, sessionId)\n await this.#connection.setex(sessionId, this.#ttlSeconds, message)\n }\n\n /**\n * Cleanup session by removing it\n */\n async destroy(sessionId: string): Promise<void> {\n debug('redis store: destroying session data %s', sessionId)\n await this.#connection.del(sessionId)\n }\n\n /**\n * Updates the value expiry\n */\n async touch(sessionId: string): Promise<void> {\n debug('redis store: touching session data %s', sessionId)\n await this.#connection.expire(sessionId, this.#ttlSeconds)\n }\n\n /**\n * Tag a session with a user ID\n */\n async tag(sessionId: string, userId: string): Promise<void> {\n debug('redis store: tagging session %s with user %s', sessionId, userId)\n await this.#connection.sadd(this.#getTagKey(userId), sessionId)\n }\n\n /**\n * Processes a single session result from the pipeline\n */\n #processSessionResult(options: { sessionId: string; contents: string | null }): {\n session: TaggedSession | null\n isInvalid: boolean\n } {\n if (!options.contents) return { session: null, isInvalid: true }\n\n const data = this.#parseSessionData(options.contents, options.sessionId)\n if (!data) return { session: null, isInvalid: true }\n\n return { session: { id: options.sessionId, data }, isInvalid: false }\n }\n\n /**\n * Fetches session contents for multiple session IDs using a pipeline\n */\n async #fetchSessionContents(sessionIds: string[]): Promise<Array<string | null>> {\n const pipeline = this.#connection.pipeline()\n sessionIds.forEach((sessionId) => pipeline.get(sessionId))\n const results = await pipeline.exec()\n\n return results?.map((result) => result[1] as string | null) ?? []\n }\n\n /**\n * Removes invalid session IDs from the user's tag set\n */\n async #cleanupInvalidSessions(userId: string, invalidSessionIds: string[]): Promise<void> {\n if (invalidSessionIds.length === 0) return\n\n await this.#connection.srem(this.#getTagKey(userId), ...invalidSessionIds)\n }\n\n /**\n * Get all sessions for a given user ID (tag)\n */\n async tagged(userId: string): Promise<TaggedSession[]> {\n debug('redis store: getting sessions tagged with user %s', userId)\n\n const sessionIds = await this.#connection.smembers(this.#getTagKey(userId))\n if (sessionIds.length === 0) return []\n\n const contents = await this.#fetchSessionContents(sessionIds)\n\n const results = sessionIds.map((sessionId, index) =>\n this.#processSessionResult({ sessionId, contents: contents[index] })\n )\n\n const validSessions = results.filter((r) => r.session !== null).map((r) => r.session!)\n const invalidSessionIds = results\n .map((result, index) => (result.isInvalid ? sessionIds[index] : null))\n .filter((id) => id !== null)\n\n await this.#cleanupInvalidSessions(userId, invalidSessionIds)\n\n return validSessions\n }\n}\n"],"mappings":"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","names":[]}
|
package/build/src/client.js
CHANGED
|
@@ -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.js';
|
|
3
|
+
import type { SessionConfig, FileStoreConfig, RedisStoreConfig, SessionStoreFactory, DynamoDBStoreConfig, DatabaseStoreConfig } from './types.js';
|
|
4
4
|
/**
|
|
5
5
|
* Resolved config with stores
|
|
6
6
|
*/
|
|
@@ -26,5 +26,6 @@ export declare const stores: {
|
|
|
26
26
|
redis: (config: RedisStoreConfig) => ConfigProvider<SessionStoreFactory>;
|
|
27
27
|
cookie: () => ConfigProvider<SessionStoreFactory>;
|
|
28
28
|
dynamodb: (config: DynamoDBStoreConfig) => ConfigProvider<SessionStoreFactory>;
|
|
29
|
+
database: (config?: DatabaseStoreConfig) => ConfigProvider<SessionStoreFactory>;
|
|
29
30
|
};
|
|
30
31
|
export {};
|
package/build/src/errors.d.ts
CHANGED
|
@@ -6,3 +6,8 @@ export declare const E_SESSION_NOT_MUTABLE: new (args?: any, options?: ErrorOpti
|
|
|
6
6
|
* Raised when session store has been initiated
|
|
7
7
|
*/
|
|
8
8
|
export declare const E_SESSION_NOT_READY: new (args?: any, options?: ErrorOptions) => import("@poppinss/utils").Exception;
|
|
9
|
+
/**
|
|
10
|
+
* Raised when trying to use tagging with a store that
|
|
11
|
+
* doesn't support tagging operations
|
|
12
|
+
*/
|
|
13
|
+
export declare const E_SESSION_TAGGING_NOT_SUPPORTED: new (args?: any, options?: ErrorOptions) => import("@poppinss/utils").Exception;
|