@puzzle-ui/session 0.0.0-stage → 0.5.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.
Files changed (61) hide show
  1. package/README.md +143 -2
  2. package/dist/LICENSE +21 -0
  3. package/dist/errors/session.errors.cjs +47 -0
  4. package/dist/errors/session.errors.cjs.map +1 -0
  5. package/dist/errors/session.errors.d.cts +19 -0
  6. package/dist/errors/session.errors.d.ts +19 -0
  7. package/dist/errors/session.errors.js +42 -0
  8. package/dist/errors/session.errors.js.map +1 -0
  9. package/dist/index.cjs +32 -0
  10. package/dist/index.d.cts +12 -0
  11. package/dist/index.d.ts +12 -0
  12. package/dist/index.js +9 -0
  13. package/dist/session-data.manager.cjs +141 -0
  14. package/dist/session-data.manager.cjs.map +1 -0
  15. package/dist/session-data.manager.d.cts +26 -0
  16. package/dist/session-data.manager.d.ts +26 -0
  17. package/dist/session-data.manager.js +141 -0
  18. package/dist/session-data.manager.js.map +1 -0
  19. package/dist/session.cookies.cjs +64 -0
  20. package/dist/session.cookies.cjs.map +1 -0
  21. package/dist/session.cookies.d.cts +10 -0
  22. package/dist/session.cookies.d.ts +10 -0
  23. package/dist/session.cookies.js +61 -0
  24. package/dist/session.cookies.js.map +1 -0
  25. package/dist/session.csrf.cjs +42 -0
  26. package/dist/session.csrf.cjs.map +1 -0
  27. package/dist/session.csrf.d.cts +6 -0
  28. package/dist/session.csrf.d.ts +6 -0
  29. package/dist/session.csrf.js +41 -0
  30. package/dist/session.csrf.js.map +1 -0
  31. package/dist/session.manager.cjs +289 -0
  32. package/dist/session.manager.cjs.map +1 -0
  33. package/dist/session.manager.d.cts +25 -0
  34. package/dist/session.manager.d.ts +25 -0
  35. package/dist/session.manager.js +289 -0
  36. package/dist/session.manager.js.map +1 -0
  37. package/dist/session.migration.cjs +163 -0
  38. package/dist/session.migration.cjs.map +1 -0
  39. package/dist/session.migration.d.cts +50 -0
  40. package/dist/session.migration.d.ts +50 -0
  41. package/dist/session.migration.js +163 -0
  42. package/dist/session.migration.js.map +1 -0
  43. package/dist/session.storage.cjs +64 -0
  44. package/dist/session.storage.cjs.map +1 -0
  45. package/dist/session.storage.d.cts +8 -0
  46. package/dist/session.storage.d.ts +8 -0
  47. package/dist/session.storage.js +58 -0
  48. package/dist/session.storage.js.map +1 -0
  49. package/dist/types/session-lock.types.d.cts +4 -0
  50. package/dist/types/session-lock.types.d.ts +4 -0
  51. package/dist/types/session-manager.types.d.cts +37 -0
  52. package/dist/types/session-manager.types.d.ts +37 -0
  53. package/dist/types/session.types.d.cts +51 -0
  54. package/dist/types/session.types.d.ts +51 -0
  55. package/dist/utils/session-crypto.cjs +29 -0
  56. package/dist/utils/session-crypto.cjs.map +1 -0
  57. package/dist/utils/session-crypto.d.cts +3 -0
  58. package/dist/utils/session-crypto.d.ts +3 -0
  59. package/dist/utils/session-crypto.js +27 -0
  60. package/dist/utils/session-crypto.js.map +1 -0
  61. package/package.json +43 -4
package/README.md CHANGED
@@ -1,3 +1,144 @@
1
- # Temporary Holding Version
1
+ # @puzzle-ui/session
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Secure, database-independent session management for Puzzle Auth.
4
+
5
+ ## Features
6
+
7
+ - Cryptographically secure opaque session tokens.
8
+ - SHA-256 token hashing.
9
+ - Anonymous and authenticated sessions.
10
+ - Idle and absolute expiration.
11
+ - Session rotation and revocation.
12
+ - Session listing and device metadata.
13
+ - Maximum active session policy.
14
+ - Namespaced session data with TTL.
15
+ - Allowlisted anonymous session migration.
16
+ - Secure cookie serialization.
17
+ - Origin validation and CSRF protection.
18
+
19
+ ## Requirements
20
+
21
+ The storage adapter must support:
22
+
23
+ - Unique constraints.
24
+ - Conditional writes.
25
+ - Atomic batches.
26
+
27
+ Session management fails closed when these capabilities are unavailable.
28
+
29
+ ## Installation
30
+
31
+ ```sh
32
+ bun add @puzzle-ui/session
33
+ ```
34
+
35
+ ## Initialization
36
+
37
+ ```ts
38
+ import {
39
+ StorageSessionManager,
40
+ initializeSessionStorage,
41
+ } from "@puzzle-ui/session";
42
+
43
+ const sessions = new StorageSessionManager(storage, {
44
+ lifetime: {
45
+ idleTimeoutSeconds: 60 * 60 * 24 * 7,
46
+ absoluteLifetimeSeconds: 60 * 60 * 24 * 30,
47
+ },
48
+ maximumSessionsPerUser: 10,
49
+ });
50
+
51
+ await initializeSessionStorage(storage);
52
+ await sessions.initialize();
53
+ ```
54
+
55
+ ## Create an anonymous session
56
+
57
+ ```ts
58
+ const result = await sessions.create({
59
+ kind: "anonymous",
60
+ userId: null,
61
+ });
62
+
63
+ console.log(result.session.id);
64
+ console.log(result.token);
65
+ ```
66
+
67
+ The token must only be delivered through a secure HttpOnly cookie.
68
+
69
+ ## Create an authenticated session
70
+
71
+ ```ts
72
+ const result = await sessions.create({
73
+ kind: "authenticated",
74
+ userId: user.id,
75
+ metadata: {
76
+ userAgent: request.headers.get("user-agent"),
77
+ },
78
+ });
79
+ ```
80
+
81
+ ## Validate a session
82
+
83
+ ```ts
84
+ const session = await sessions.validate(token);
85
+
86
+ if (!session) {
87
+ // Unauthenticated request.
88
+ }
89
+ ```
90
+
91
+ ## Rotate a session
92
+
93
+ ```ts
94
+ const rotated = await sessions.rotate({
95
+ sessionId: session.id,
96
+ });
97
+ ```
98
+
99
+ The old session is revoked as part of the atomic rotation.
100
+
101
+ ## Revoke sessions
102
+
103
+ ```ts
104
+ await sessions.revoke(session.id);
105
+ await sessions.revokeAll(user.id);
106
+ ```
107
+
108
+ ## List devices
109
+
110
+ ```ts
111
+ const devices = await sessions.list(user.id, session.id);
112
+ ```
113
+
114
+ ## Session data
115
+
116
+ ```ts
117
+ const data = new SessionDataManager(storage, {
118
+ allowedNamespaces: ["cart", "checkout"],
119
+ });
120
+
121
+ await data.initialize();
122
+
123
+ await data.set(
124
+ session.id,
125
+ "cart",
126
+ "items",
127
+ [{ productId: "product-1", quantity: 2 }],
128
+ 60 * 60,
129
+ );
130
+
131
+ const cart = await data.get(session.id, "cart", "items");
132
+ ```
133
+
134
+ ## Security
135
+
136
+ - Raw tokens are never persisted.
137
+ - Session validation always checks durable storage.
138
+ - Session expiration is enforced server-side.
139
+ - Anonymous sessions cannot be converted by changing their user ID.
140
+ - Migration creates a new identity-bound session.
141
+ - Session data namespaces must be explicitly allowlisted.
142
+ - Cookie configuration does not expose session tokens to JavaScript.
143
+ - Security-critical operations require atomic storage support.
144
+ In-memory storage is intended exclusively for development and tests.
package/dist/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sjoerd van Den Berg
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,47 @@
1
+ // src/errors/session.errors.ts
2
+ var SessionError = class extends Error {
3
+ code;
4
+ constructor(message, code) {
5
+ super(message);
6
+ this.code = code;
7
+ this.name = "SessionError";
8
+ }
9
+ };
10
+ var SessionNotFoundError = class extends SessionError {
11
+ constructor() {
12
+ super("Session not found or no longer active.", "SESSION_NOT_FOUND");
13
+ this.name = "SessionNotFoundError";
14
+ }
15
+ };
16
+ var SessionExpiredError = class extends SessionError {
17
+ constructor() {
18
+ super("Session has expired.", "SESSION_EXPIRED");
19
+ this.name = "SessionExpiredError";
20
+ }
21
+ };
22
+ var SessionRevokedError = class extends SessionError {
23
+ constructor() {
24
+ super("Session has been revoked.", "SESSION_REVOKED");
25
+ this.name = "SessionRevokedError";
26
+ }
27
+ };
28
+ var SessionSecurityError = class extends SessionError {
29
+ constructor(message) {
30
+ super(message, "SESSION_SECURITY_ERROR");
31
+ this.name = "SessionSecurityError";
32
+ }
33
+ };
34
+ var SessionConfigurationError = class extends SessionError {
35
+ constructor(message) {
36
+ super(message, "SESSION_CONFIGURATION_ERROR");
37
+ this.name = "SessionConfigurationError";
38
+ }
39
+ };
40
+ exports.SessionConfigurationError = SessionConfigurationError;
41
+ exports.SessionError = SessionError;
42
+ exports.SessionExpiredError = SessionExpiredError;
43
+ exports.SessionNotFoundError = SessionNotFoundError;
44
+ exports.SessionRevokedError = SessionRevokedError;
45
+ exports.SessionSecurityError = SessionSecurityError;
46
+
47
+ //# sourceMappingURL=session.errors.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.errors.cjs","names":[],"sources":["../../src/errors/session.errors.ts"],"sourcesContent":["export class SessionError extends Error {\n constructor(\n message: string,\n readonly code: string,\n ) {\n super(message);\n this.name = \"SessionError\";\n }\n}\n\nexport class SessionNotFoundError extends SessionError {\n constructor() {\n super(\"Session not found or no longer active.\", \"SESSION_NOT_FOUND\");\n this.name = \"SessionNotFoundError\";\n }\n}\n\nexport class SessionExpiredError extends SessionError {\n constructor() {\n super(\"Session has expired.\", \"SESSION_EXPIRED\");\n this.name = \"SessionExpiredError\";\n }\n}\n\nexport class SessionRevokedError extends SessionError {\n constructor() {\n super(\"Session has been revoked.\", \"SESSION_REVOKED\");\n this.name = \"SessionRevokedError\";\n }\n}\n\nexport class SessionSecurityError extends SessionError {\n constructor(message: string) {\n super(message, \"SESSION_SECURITY_ERROR\");\n this.name = \"SessionSecurityError\";\n }\n}\n\nexport class SessionConfigurationError extends SessionError {\n constructor(message: string) {\n super(message, \"SESSION_CONFIGURATION_ERROR\");\n this.name = \"SessionConfigurationError\";\n }\n}\n"],"mappings":";AAAA,IAAa,eAAb,cAAkC,MAAM;CAG3B;CAFX,YACE,SACA,MACA;EACA,MAAM,OAAO;EAFJ,KAAA,OAAA;EAGT,KAAK,OAAO;CACd;AACF;AAEA,IAAa,uBAAb,cAA0C,aAAa;CACrD,cAAc;EACZ,MAAM,0CAA0C,mBAAmB;EACnE,KAAK,OAAO;CACd;AACF;AAEA,IAAa,sBAAb,cAAyC,aAAa;CACpD,cAAc;EACZ,MAAM,wBAAwB,iBAAiB;EAC/C,KAAK,OAAO;CACd;AACF;AAEA,IAAa,sBAAb,cAAyC,aAAa;CACpD,cAAc;EACZ,MAAM,6BAA6B,iBAAiB;EACpD,KAAK,OAAO;CACd;AACF;AAEA,IAAa,uBAAb,cAA0C,aAAa;CACrD,YAAY,SAAiB;EAC3B,MAAM,SAAS,wBAAwB;EACvC,KAAK,OAAO;CACd;AACF;AAEA,IAAa,4BAAb,cAA+C,aAAa;CAC1D,YAAY,SAAiB;EAC3B,MAAM,SAAS,6BAA6B;EAC5C,KAAK,OAAO;CACd;AACF"}
@@ -0,0 +1,19 @@
1
+ export declare class SessionError extends Error {
2
+ readonly code: string;
3
+ constructor(message: string, code: string);
4
+ }
5
+ export declare class SessionNotFoundError extends SessionError {
6
+ constructor();
7
+ }
8
+ export declare class SessionExpiredError extends SessionError {
9
+ constructor();
10
+ }
11
+ export declare class SessionRevokedError extends SessionError {
12
+ constructor();
13
+ }
14
+ export declare class SessionSecurityError extends SessionError {
15
+ constructor(message: string);
16
+ }
17
+ export declare class SessionConfigurationError extends SessionError {
18
+ constructor(message: string);
19
+ }
@@ -0,0 +1,19 @@
1
+ export declare class SessionError extends Error {
2
+ readonly code: string;
3
+ constructor(message: string, code: string);
4
+ }
5
+ export declare class SessionNotFoundError extends SessionError {
6
+ constructor();
7
+ }
8
+ export declare class SessionExpiredError extends SessionError {
9
+ constructor();
10
+ }
11
+ export declare class SessionRevokedError extends SessionError {
12
+ constructor();
13
+ }
14
+ export declare class SessionSecurityError extends SessionError {
15
+ constructor(message: string);
16
+ }
17
+ export declare class SessionConfigurationError extends SessionError {
18
+ constructor(message: string);
19
+ }
@@ -0,0 +1,42 @@
1
+ // src/errors/session.errors.ts
2
+ var SessionError = class extends Error {
3
+ code;
4
+ constructor(message, code) {
5
+ super(message);
6
+ this.code = code;
7
+ this.name = "SessionError";
8
+ }
9
+ };
10
+ var SessionNotFoundError = class extends SessionError {
11
+ constructor() {
12
+ super("Session not found or no longer active.", "SESSION_NOT_FOUND");
13
+ this.name = "SessionNotFoundError";
14
+ }
15
+ };
16
+ var SessionExpiredError = class extends SessionError {
17
+ constructor() {
18
+ super("Session has expired.", "SESSION_EXPIRED");
19
+ this.name = "SessionExpiredError";
20
+ }
21
+ };
22
+ var SessionRevokedError = class extends SessionError {
23
+ constructor() {
24
+ super("Session has been revoked.", "SESSION_REVOKED");
25
+ this.name = "SessionRevokedError";
26
+ }
27
+ };
28
+ var SessionSecurityError = class extends SessionError {
29
+ constructor(message) {
30
+ super(message, "SESSION_SECURITY_ERROR");
31
+ this.name = "SessionSecurityError";
32
+ }
33
+ };
34
+ var SessionConfigurationError = class extends SessionError {
35
+ constructor(message) {
36
+ super(message, "SESSION_CONFIGURATION_ERROR");
37
+ this.name = "SessionConfigurationError";
38
+ }
39
+ };
40
+ export { SessionConfigurationError, SessionError, SessionExpiredError, SessionNotFoundError, SessionRevokedError, SessionSecurityError };
41
+
42
+ //# sourceMappingURL=session.errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.errors.js","names":[],"sources":["../../src/errors/session.errors.ts"],"sourcesContent":["export class SessionError extends Error {\n constructor(\n message: string,\n readonly code: string,\n ) {\n super(message);\n this.name = \"SessionError\";\n }\n}\n\nexport class SessionNotFoundError extends SessionError {\n constructor() {\n super(\"Session not found or no longer active.\", \"SESSION_NOT_FOUND\");\n this.name = \"SessionNotFoundError\";\n }\n}\n\nexport class SessionExpiredError extends SessionError {\n constructor() {\n super(\"Session has expired.\", \"SESSION_EXPIRED\");\n this.name = \"SessionExpiredError\";\n }\n}\n\nexport class SessionRevokedError extends SessionError {\n constructor() {\n super(\"Session has been revoked.\", \"SESSION_REVOKED\");\n this.name = \"SessionRevokedError\";\n }\n}\n\nexport class SessionSecurityError extends SessionError {\n constructor(message: string) {\n super(message, \"SESSION_SECURITY_ERROR\");\n this.name = \"SessionSecurityError\";\n }\n}\n\nexport class SessionConfigurationError extends SessionError {\n constructor(message: string) {\n super(message, \"SESSION_CONFIGURATION_ERROR\");\n this.name = \"SessionConfigurationError\";\n }\n}\n"],"mappings":";AAAA,IAAa,eAAb,cAAkC,MAAM;CAG3B;CAFX,YACE,SACA,MACA;EACA,MAAM,OAAO;EAFJ,KAAA,OAAA;EAGT,KAAK,OAAO;CACd;AACF;AAEA,IAAa,uBAAb,cAA0C,aAAa;CACrD,cAAc;EACZ,MAAM,0CAA0C,mBAAmB;EACnE,KAAK,OAAO;CACd;AACF;AAEA,IAAa,sBAAb,cAAyC,aAAa;CACpD,cAAc;EACZ,MAAM,wBAAwB,iBAAiB;EAC/C,KAAK,OAAO;CACd;AACF;AAEA,IAAa,sBAAb,cAAyC,aAAa;CACpD,cAAc;EACZ,MAAM,6BAA6B,iBAAiB;EACpD,KAAK,OAAO;CACd;AACF;AAEA,IAAa,uBAAb,cAA0C,aAAa;CACrD,YAAY,SAAiB;EAC3B,MAAM,SAAS,wBAAwB;EACvC,KAAK,OAAO;CACd;AACF;AAEA,IAAa,4BAAb,cAA+C,aAAa;CAC1D,YAAY,SAAiB;EAC3B,MAAM,SAAS,6BAA6B;EAC5C,KAAK,OAAO;CACd;AACF"}
package/dist/index.cjs ADDED
@@ -0,0 +1,32 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_session_manager = require("./session.manager.cjs");
3
+ const require_session_data_manager = require("./session-data.manager.cjs");
4
+ const require_session_migration = require("./session.migration.cjs");
5
+ const require_session_cookies = require("./session.cookies.cjs");
6
+ const require_session_csrf = require("./session.csrf.cjs");
7
+ const require_session_crypto = require("./utils/session-crypto.cjs");
8
+ const require_session_storage = require("./session.storage.cjs");
9
+ const require_session_errors = require("./errors/session.errors.cjs");
10
+ exports.SESSION_COLLECTION = require_session_storage.SESSION_COLLECTION;
11
+ exports.SESSION_DATA_COLLECTION = require_session_storage.SESSION_DATA_COLLECTION;
12
+ exports.SessionConfigurationError = require_session_errors.SessionConfigurationError;
13
+ exports.SessionDataManager = require_session_data_manager.SessionDataManager;
14
+ exports.SessionError = require_session_errors.SessionError;
15
+ exports.SessionExpiredError = require_session_errors.SessionExpiredError;
16
+ exports.SessionMigrationManager = require_session_migration.SessionMigrationManager;
17
+ exports.SessionNotFoundError = require_session_errors.SessionNotFoundError;
18
+ exports.SessionRevokedError = require_session_errors.SessionRevokedError;
19
+ exports.SessionSecurityError = require_session_errors.SessionSecurityError;
20
+ exports.StorageSessionManager = require_session_manager.StorageSessionManager;
21
+ exports.createSessionCookieOptions = require_session_cookies.createSessionCookieOptions;
22
+ exports.generateSessionId = require_session_crypto.generateSessionId;
23
+ exports.generateSessionToken = require_session_crypto.generateSessionToken;
24
+ exports.hashSessionToken = require_session_crypto.hashSessionToken;
25
+ exports.initializeSessionStorage = require_session_storage.initializeSessionStorage;
26
+ exports.protectStateChangingRequest = require_session_csrf.protectStateChangingRequest;
27
+ exports.readSessionCookie = require_session_cookies.readSessionCookie;
28
+ exports.serializeClearedSessionCookie = require_session_cookies.serializeClearedSessionCookie;
29
+ exports.serializeSessionCookie = require_session_cookies.serializeSessionCookie;
30
+ exports.sessionCollection = require_session_storage.sessionCollection;
31
+ exports.sessionDataCollection = require_session_storage.sessionDataCollection;
32
+ exports.validateRequestOrigin = require_session_csrf.validateRequestOrigin;
@@ -0,0 +1,12 @@
1
+ import { StorageSessionManager } from "./session.manager.cjs";
2
+ import { SessionDataManager, SessionDataOptions } from "./session-data.manager.cjs";
3
+ import { AnonymousMigrationOptions, AnonymousMigrationResult, SessionMigrationContext, SessionMigrationEntry, SessionMigrationManager } from "./session.migration.cjs";
4
+ import { SessionCookieEnvironment, createSessionCookieOptions, readSessionCookie, serializeClearedSessionCookie, serializeSessionCookie } from "./session.cookies.cjs";
5
+ import { CsrfOptions, protectStateChangingRequest, validateRequestOrigin } from "./session.csrf.cjs";
6
+ import { generateSessionId, generateSessionToken, hashSessionToken } from "./utils/session-crypto.cjs";
7
+ import { SESSION_COLLECTION, SESSION_DATA_COLLECTION, initializeSessionStorage, sessionCollection, sessionDataCollection } from "./session.storage.cjs";
8
+ import { SessionConfigurationError, SessionError, SessionExpiredError, SessionNotFoundError, SessionRevokedError, SessionSecurityError } from "./errors/session.errors.cjs";
9
+ import { SessionCookieOptions, SessionDataRecord, SessionDevice, SessionKind, SessionLifetime, SessionRecord, SessionStorageRecord } from "./types/session.types.cjs";
10
+ import { CreateSessionInput, CreatedSession, RotateSessionInput, SessionManager, SessionManagerOptions, ValidateSessionOptions } from "./types/session-manager.types.cjs";
11
+ import { SessionUserLockRecord } from "./types/session-lock.types.cjs";
12
+ export { type AnonymousMigrationOptions, type AnonymousMigrationResult, type CreateSessionInput, type CreatedSession, type CsrfOptions, type RotateSessionInput, SESSION_COLLECTION, SESSION_DATA_COLLECTION, SessionConfigurationError, type SessionCookieEnvironment, type SessionCookieOptions, SessionDataManager, type SessionDataOptions, type SessionDataRecord, type SessionDevice, SessionError, SessionExpiredError, type SessionKind, type SessionLifetime, type SessionManager, type SessionManagerOptions, type SessionMigrationContext, type SessionMigrationEntry, SessionMigrationManager, SessionNotFoundError, type SessionRecord, SessionRevokedError, SessionSecurityError, type SessionStorageRecord, type SessionUserLockRecord, StorageSessionManager, type ValidateSessionOptions, createSessionCookieOptions, generateSessionId, generateSessionToken, hashSessionToken, initializeSessionStorage, protectStateChangingRequest, readSessionCookie, serializeClearedSessionCookie, serializeSessionCookie, sessionCollection, sessionDataCollection, validateRequestOrigin };
@@ -0,0 +1,12 @@
1
+ import { StorageSessionManager } from "./session.manager.js";
2
+ import { SessionDataManager, SessionDataOptions } from "./session-data.manager.js";
3
+ import { AnonymousMigrationOptions, AnonymousMigrationResult, SessionMigrationContext, SessionMigrationEntry, SessionMigrationManager } from "./session.migration.js";
4
+ import { SessionCookieEnvironment, createSessionCookieOptions, readSessionCookie, serializeClearedSessionCookie, serializeSessionCookie } from "./session.cookies.js";
5
+ import { CsrfOptions, protectStateChangingRequest, validateRequestOrigin } from "./session.csrf.js";
6
+ import { generateSessionId, generateSessionToken, hashSessionToken } from "./utils/session-crypto.js";
7
+ import { SESSION_COLLECTION, SESSION_DATA_COLLECTION, initializeSessionStorage, sessionCollection, sessionDataCollection } from "./session.storage.js";
8
+ import { SessionConfigurationError, SessionError, SessionExpiredError, SessionNotFoundError, SessionRevokedError, SessionSecurityError } from "./errors/session.errors.js";
9
+ import { SessionCookieOptions, SessionDataRecord, SessionDevice, SessionKind, SessionLifetime, SessionRecord, SessionStorageRecord } from "./types/session.types.js";
10
+ import { CreateSessionInput, CreatedSession, RotateSessionInput, SessionManager, SessionManagerOptions, ValidateSessionOptions } from "./types/session-manager.types.js";
11
+ import { SessionUserLockRecord } from "./types/session-lock.types.js";
12
+ export { type AnonymousMigrationOptions, type AnonymousMigrationResult, type CreateSessionInput, type CreatedSession, type CsrfOptions, type RotateSessionInput, SESSION_COLLECTION, SESSION_DATA_COLLECTION, SessionConfigurationError, type SessionCookieEnvironment, type SessionCookieOptions, SessionDataManager, type SessionDataOptions, type SessionDataRecord, type SessionDevice, SessionError, SessionExpiredError, type SessionKind, type SessionLifetime, type SessionManager, type SessionManagerOptions, type SessionMigrationContext, type SessionMigrationEntry, SessionMigrationManager, SessionNotFoundError, type SessionRecord, SessionRevokedError, SessionSecurityError, type SessionStorageRecord, type SessionUserLockRecord, StorageSessionManager, type ValidateSessionOptions, createSessionCookieOptions, generateSessionId, generateSessionToken, hashSessionToken, initializeSessionStorage, protectStateChangingRequest, readSessionCookie, serializeClearedSessionCookie, serializeSessionCookie, sessionCollection, sessionDataCollection, validateRequestOrigin };
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ import { StorageSessionManager } from "./session.manager.js";
2
+ import { SessionDataManager } from "./session-data.manager.js";
3
+ import { SessionMigrationManager } from "./session.migration.js";
4
+ import { createSessionCookieOptions, readSessionCookie, serializeClearedSessionCookie, serializeSessionCookie } from "./session.cookies.js";
5
+ import { protectStateChangingRequest, validateRequestOrigin } from "./session.csrf.js";
6
+ import { generateSessionId, generateSessionToken, hashSessionToken } from "./utils/session-crypto.js";
7
+ import { SESSION_COLLECTION, SESSION_DATA_COLLECTION, initializeSessionStorage, sessionCollection, sessionDataCollection } from "./session.storage.js";
8
+ import { SessionConfigurationError, SessionError, SessionExpiredError, SessionNotFoundError, SessionRevokedError, SessionSecurityError } from "./errors/session.errors.js";
9
+ export { SESSION_COLLECTION, SESSION_DATA_COLLECTION, SessionConfigurationError, SessionDataManager, SessionError, SessionExpiredError, SessionMigrationManager, SessionNotFoundError, SessionRevokedError, SessionSecurityError, StorageSessionManager, createSessionCookieOptions, generateSessionId, generateSessionToken, hashSessionToken, initializeSessionStorage, protectStateChangingRequest, readSessionCookie, serializeClearedSessionCookie, serializeSessionCookie, sessionCollection, sessionDataCollection, validateRequestOrigin };
@@ -0,0 +1,141 @@
1
+ const require_session_storage = require("./session.storage.cjs");
2
+ const require_session_crypto = require("./utils/session-crypto.cjs");
3
+ let _puzzle_ui_storage = require("@puzzle-ui/storage");
4
+ // src/session-data.manager.ts
5
+ var SessionDataManager = class {
6
+ storage;
7
+ options;
8
+ clock;
9
+ maximumValueBytes;
10
+ maximumRetries;
11
+ constructor(storage, options) {
12
+ this.storage = storage;
13
+ this.options = options;
14
+ this.clock = options.clock ?? (() => /* @__PURE__ */ new Date());
15
+ this.maximumValueBytes = options.maximumValueBytes ?? 16384;
16
+ this.maximumRetries = options.maximumRetries ?? 5;
17
+ if (!options.allowedNamespaces.length) throw new Error("At least one session data namespace is required.");
18
+ if (!Number.isInteger(this.maximumValueBytes) || this.maximumValueBytes < 1) throw new Error("maximumValueBytes must be a positive integer.");
19
+ if (!Number.isInteger(this.maximumRetries) || this.maximumRetries < 1) throw new Error("maximumRetries must be a positive integer.");
20
+ if (options.defaultTtlSeconds !== void 0 && (!Number.isFinite(options.defaultTtlSeconds) || options.defaultTtlSeconds <= 0)) throw new Error("defaultTtlSeconds must be a positive number.");
21
+ }
22
+ async initialize() {
23
+ if (!this.storage.capabilities.uniqueConstraints) throw new Error("Session data requires unique storage constraints.");
24
+ await this.storage.registerCollection(require_session_storage.sessionDataCollection);
25
+ }
26
+ assertOwner(ownerId) {
27
+ if (!ownerId.trim()) throw new Error("A valid session owner ID is required.");
28
+ }
29
+ assertNamespace(namespace) {
30
+ if (!this.options.allowedNamespaces.includes(namespace)) throw new Error(`Session data namespace is not allowed: ${namespace}`);
31
+ }
32
+ assertKey(key) {
33
+ if (!key.trim()) throw new Error("A valid session data key is required.");
34
+ }
35
+ assertValue(value) {
36
+ let serialized;
37
+ try {
38
+ serialized = JSON.stringify(value);
39
+ } catch {
40
+ throw new Error("Session data must be JSON serializable.");
41
+ }
42
+ if (serialized === void 0) throw new Error("Session data must be JSON serializable.");
43
+ if (new TextEncoder().encode(serialized).byteLength > this.maximumValueBytes) throw new Error("Session data exceeds the maximum allowed size.");
44
+ }
45
+ query(ownerId, namespace, key) {
46
+ return { where: {
47
+ ownerId: {
48
+ operator: "eq",
49
+ value: ownerId
50
+ },
51
+ namespace: {
52
+ operator: "eq",
53
+ value: namespace
54
+ },
55
+ key: {
56
+ operator: "eq",
57
+ value: key
58
+ }
59
+ } };
60
+ }
61
+ async set(ownerId, namespace, key, value, ttlSeconds = this.options.defaultTtlSeconds) {
62
+ this.assertOwner(ownerId);
63
+ this.assertNamespace(namespace);
64
+ this.assertKey(key);
65
+ this.assertValue(value);
66
+ if (ttlSeconds !== void 0 && (!Number.isFinite(ttlSeconds) || ttlSeconds <= 0)) throw new Error("TTL must be a positive number.");
67
+ for (let attempt = 0; attempt < this.maximumRetries; attempt++) {
68
+ const now = this.clock();
69
+ const expiresAt = ttlSeconds === void 0 ? null : new Date(now.getTime() + ttlSeconds * 1e3);
70
+ const existing = await this.storage.findOne(require_session_storage.SESSION_DATA_COLLECTION, this.query(ownerId, namespace, key));
71
+ if (existing) {
72
+ try {
73
+ if (await this.storage.update("session_data", existing.id, {
74
+ value,
75
+ expiresAt
76
+ }, { expectedRevision: existing.revision })) return;
77
+ } catch (error) {
78
+ if (!(error instanceof _puzzle_ui_storage.StorageConflictError)) throw error;
79
+ }
80
+ continue;
81
+ }
82
+ try {
83
+ await this.storage.insert(require_session_storage.SESSION_DATA_COLLECTION, {
84
+ id: require_session_crypto.generateSessionId(),
85
+ ownerId,
86
+ namespace,
87
+ key,
88
+ value,
89
+ expiresAt,
90
+ createdAt: now,
91
+ updatedAt: now,
92
+ revision: 1
93
+ });
94
+ return;
95
+ } catch (error) {
96
+ if (error instanceof _puzzle_ui_storage.StorageDuplicateError || error instanceof _puzzle_ui_storage.StorageConflictError) continue;
97
+ throw error;
98
+ }
99
+ }
100
+ throw new _puzzle_ui_storage.StorageConflictError("Could not write session data after repeated concurrent modifications.");
101
+ }
102
+ async get(ownerId, namespace, key) {
103
+ this.assertOwner(ownerId);
104
+ this.assertNamespace(namespace);
105
+ this.assertKey(key);
106
+ const record = await this.storage.findOne(require_session_storage.SESSION_DATA_COLLECTION, this.query(ownerId, namespace, key));
107
+ if (!record) return null;
108
+ if (record.expiresAt && record.expiresAt.getTime() <= this.clock().getTime()) {
109
+ await this.storage.delete(require_session_storage.SESSION_DATA_COLLECTION, record.id, { expectedRevision: record.revision }).catch((error) => {
110
+ if (!(error instanceof _puzzle_ui_storage.StorageConflictError)) throw error;
111
+ });
112
+ return null;
113
+ }
114
+ return record.value;
115
+ }
116
+ async remove(ownerId, namespace, key) {
117
+ this.assertOwner(ownerId);
118
+ this.assertNamespace(namespace);
119
+ this.assertKey(key);
120
+ for (let attempt = 0; attempt < this.maximumRetries; attempt++) {
121
+ const record = await this.storage.findOne(require_session_storage.SESSION_DATA_COLLECTION, this.query(ownerId, namespace, key));
122
+ if (!record) return;
123
+ try {
124
+ if (await this.storage.delete("session_data", record.id, { expectedRevision: record.revision })) return;
125
+ } catch (error) {
126
+ if (!(error instanceof _puzzle_ui_storage.StorageConflictError)) throw error;
127
+ }
128
+ }
129
+ throw new _puzzle_ui_storage.StorageConflictError("Could not remove session data after repeated concurrent modifications.");
130
+ }
131
+ async removeExpired() {
132
+ const now = this.clock();
133
+ return this.storage.deleteMany(require_session_storage.SESSION_DATA_COLLECTION, { where: { expiresAt: {
134
+ operator: "lte",
135
+ value: now
136
+ } } });
137
+ }
138
+ };
139
+ exports.SessionDataManager = SessionDataManager;
140
+
141
+ //# sourceMappingURL=session-data.manager.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-data.manager.cjs","names":["sessionDataCollection","SESSION_DATA_COLLECTION","StorageConflictError","generateSessionId","StorageDuplicateError"],"sources":["../src/session-data.manager.ts"],"sourcesContent":["import {\n StorageConflictError,\n StorageDuplicateError,\n type StorageAdapter,\n} from \"@puzzle-ui/storage\";\n\nimport {\n SESSION_DATA_COLLECTION,\n sessionDataCollection,\n} from \"./session.storage\";\n\nimport type { SessionDataRecord } from \"./types/session.types\";\n\nimport { generateSessionId } from \"./utils/session-crypto\";\n\nexport type SessionDataOptions = {\n readonly allowedNamespaces: readonly string[];\n readonly defaultTtlSeconds?: number;\n readonly maximumValueBytes?: number;\n readonly maximumRetries?: number;\n readonly clock?: () => Date;\n};\n\nexport class SessionDataManager {\n private readonly clock: () => Date;\n private readonly maximumValueBytes: number;\n private readonly maximumRetries: number;\n\n constructor(\n private readonly storage: StorageAdapter,\n private readonly options: SessionDataOptions,\n ) {\n this.clock = options.clock ?? (() => new Date());\n this.maximumValueBytes = options.maximumValueBytes ?? 16_384;\n this.maximumRetries = options.maximumRetries ?? 5;\n\n if (!options.allowedNamespaces.length) {\n throw new Error(\"At least one session data namespace is required.\");\n }\n\n if (\n !Number.isInteger(this.maximumValueBytes) ||\n this.maximumValueBytes < 1\n ) {\n throw new Error(\"maximumValueBytes must be a positive integer.\");\n }\n\n if (!Number.isInteger(this.maximumRetries) || this.maximumRetries < 1) {\n throw new Error(\"maximumRetries must be a positive integer.\");\n }\n\n if (\n options.defaultTtlSeconds !== undefined &&\n (!Number.isFinite(options.defaultTtlSeconds) ||\n options.defaultTtlSeconds <= 0)\n ) {\n throw new Error(\"defaultTtlSeconds must be a positive number.\");\n }\n }\n\n async initialize(): Promise<void> {\n if (!this.storage.capabilities.uniqueConstraints) {\n throw new Error(\"Session data requires unique storage constraints.\");\n }\n\n await this.storage.registerCollection(sessionDataCollection);\n }\n\n private assertOwner(ownerId: string): void {\n if (!ownerId.trim()) {\n throw new Error(\"A valid session owner ID is required.\");\n }\n }\n\n private assertNamespace(namespace: string): void {\n if (!this.options.allowedNamespaces.includes(namespace)) {\n throw new Error(`Session data namespace is not allowed: ${namespace}`);\n }\n }\n\n private assertKey(key: string): void {\n if (!key.trim()) {\n throw new Error(\"A valid session data key is required.\");\n }\n }\n\n private assertValue(value: unknown): void {\n let serialized: string;\n\n try {\n serialized = JSON.stringify(value);\n } catch {\n throw new Error(\"Session data must be JSON serializable.\");\n }\n\n if (serialized === undefined) {\n throw new Error(\"Session data must be JSON serializable.\");\n }\n\n const size = new TextEncoder().encode(serialized).byteLength;\n\n if (size > this.maximumValueBytes) {\n throw new Error(\"Session data exceeds the maximum allowed size.\");\n }\n }\n\n private query(ownerId: string, namespace: string, key: string) {\n return {\n where: {\n ownerId: { operator: \"eq\" as const, value: ownerId },\n namespace: { operator: \"eq\" as const, value: namespace },\n key: { operator: \"eq\" as const, value: key },\n },\n };\n }\n\n async set(\n ownerId: string,\n namespace: string,\n key: string,\n value: unknown,\n ttlSeconds = this.options.defaultTtlSeconds,\n ): Promise<void> {\n this.assertOwner(ownerId);\n this.assertNamespace(namespace);\n this.assertKey(key);\n this.assertValue(value);\n\n if (\n ttlSeconds !== undefined &&\n (!Number.isFinite(ttlSeconds) || ttlSeconds <= 0)\n ) {\n throw new Error(\"TTL must be a positive number.\");\n }\n\n for (let attempt = 0; attempt < this.maximumRetries; attempt++) {\n const now = this.clock();\n\n const expiresAt =\n ttlSeconds === undefined\n ? null\n : new Date(now.getTime() + ttlSeconds * 1000);\n\n const existing = await this.storage.findOne<SessionDataRecord>(\n SESSION_DATA_COLLECTION,\n this.query(ownerId, namespace, key),\n );\n\n if (existing) {\n try {\n const updated = await this.storage.update<SessionDataRecord>(\n SESSION_DATA_COLLECTION,\n existing.id,\n {\n value,\n expiresAt,\n },\n {\n expectedRevision: existing.revision,\n },\n );\n\n if (updated) return;\n } catch (error) {\n if (!(error instanceof StorageConflictError)) {\n throw error;\n }\n }\n\n continue;\n }\n\n try {\n await this.storage.insert<SessionDataRecord>(SESSION_DATA_COLLECTION, {\n id: generateSessionId(),\n ownerId,\n namespace,\n key,\n value,\n expiresAt,\n createdAt: now,\n updatedAt: now,\n revision: 1,\n });\n\n return;\n } catch (error) {\n if (\n error instanceof StorageDuplicateError ||\n error instanceof StorageConflictError\n ) {\n continue;\n }\n\n throw error;\n }\n }\n\n throw new StorageConflictError(\n \"Could not write session data after repeated concurrent modifications.\",\n );\n }\n\n async get<T = unknown>(\n ownerId: string,\n namespace: string,\n key: string,\n ): Promise<T | null> {\n this.assertOwner(ownerId);\n this.assertNamespace(namespace);\n this.assertKey(key);\n\n const record = await this.storage.findOne<SessionDataRecord>(\n SESSION_DATA_COLLECTION,\n this.query(ownerId, namespace, key),\n );\n\n if (!record) return null;\n\n if (\n record.expiresAt &&\n record.expiresAt.getTime() <= this.clock().getTime()\n ) {\n await this.storage\n .delete(SESSION_DATA_COLLECTION, record.id, {\n expectedRevision: record.revision,\n })\n .catch((error) => {\n if (!(error instanceof StorageConflictError)) {\n throw error;\n }\n });\n\n return null;\n }\n\n return record.value as T;\n }\n\n async remove(ownerId: string, namespace: string, key: string): Promise<void> {\n this.assertOwner(ownerId);\n this.assertNamespace(namespace);\n this.assertKey(key);\n\n for (let attempt = 0; attempt < this.maximumRetries; attempt++) {\n const record = await this.storage.findOne<SessionDataRecord>(\n SESSION_DATA_COLLECTION,\n this.query(ownerId, namespace, key),\n );\n\n if (!record) return;\n\n try {\n const deleted = await this.storage.delete(\n SESSION_DATA_COLLECTION,\n record.id,\n {\n expectedRevision: record.revision,\n },\n );\n\n if (deleted) return;\n } catch (error) {\n if (!(error instanceof StorageConflictError)) {\n throw error;\n }\n }\n }\n\n throw new StorageConflictError(\n \"Could not remove session data after repeated concurrent modifications.\",\n );\n }\n\n async removeExpired(): Promise<number> {\n const now = this.clock();\n\n return this.storage.deleteMany<SessionDataRecord>(SESSION_DATA_COLLECTION, {\n where: {\n expiresAt: {\n operator: \"lte\",\n value: now,\n },\n },\n });\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { StorageAdapter } from "@puzzle-ui/storage";
2
+ export type SessionDataOptions = {
3
+ readonly allowedNamespaces: readonly string[];
4
+ readonly defaultTtlSeconds?: number;
5
+ readonly maximumValueBytes?: number;
6
+ readonly maximumRetries?: number;
7
+ readonly clock?: () => Date;
8
+ };
9
+ export declare class SessionDataManager {
10
+ private readonly storage;
11
+ private readonly options;
12
+ private readonly clock;
13
+ private readonly maximumValueBytes;
14
+ private readonly maximumRetries;
15
+ constructor(storage: StorageAdapter, options: SessionDataOptions);
16
+ initialize(): Promise<void>;
17
+ private assertOwner;
18
+ private assertNamespace;
19
+ private assertKey;
20
+ private assertValue;
21
+ private query;
22
+ set(ownerId: string, namespace: string, key: string, value: unknown, ttlSeconds?: number | undefined): Promise<void>;
23
+ get<T = unknown>(ownerId: string, namespace: string, key: string): Promise<T | null>;
24
+ remove(ownerId: string, namespace: string, key: string): Promise<void>;
25
+ removeExpired(): Promise<number>;
26
+ }
@@ -0,0 +1,26 @@
1
+ import { StorageAdapter } from "@puzzle-ui/storage";
2
+ export type SessionDataOptions = {
3
+ readonly allowedNamespaces: readonly string[];
4
+ readonly defaultTtlSeconds?: number;
5
+ readonly maximumValueBytes?: number;
6
+ readonly maximumRetries?: number;
7
+ readonly clock?: () => Date;
8
+ };
9
+ export declare class SessionDataManager {
10
+ private readonly storage;
11
+ private readonly options;
12
+ private readonly clock;
13
+ private readonly maximumValueBytes;
14
+ private readonly maximumRetries;
15
+ constructor(storage: StorageAdapter, options: SessionDataOptions);
16
+ initialize(): Promise<void>;
17
+ private assertOwner;
18
+ private assertNamespace;
19
+ private assertKey;
20
+ private assertValue;
21
+ private query;
22
+ set(ownerId: string, namespace: string, key: string, value: unknown, ttlSeconds?: number | undefined): Promise<void>;
23
+ get<T = unknown>(ownerId: string, namespace: string, key: string): Promise<T | null>;
24
+ remove(ownerId: string, namespace: string, key: string): Promise<void>;
25
+ removeExpired(): Promise<number>;
26
+ }