{"version":3,"file":"connect-surreal.cjs","sources":["../src/main.ts"],"sourcesContent":["import { type SessionData, Store } from \"express-session\";\nimport { RecordId, Surreal, Table } from \"surrealdb\";\n\n\nexport type SurrealDBStoreOptions = {\n    /**\n     * URL used to connect to SurrealDB\n     * e.g. http://127.0.0.1:8000/rpc\n     */\n    url: string,\n    /**\n     * Table to use for storing the sessions\n     * @default `user_sessions`\n     */\n    tableName: string,\n\n    /**\n     * Options for the initial SurrealDB connection.\n     * You should set the namespace and database in this object.\n    */\n    connectionOpts: Parameters<Surreal['connect']>[1];\n    /**\n    * Sign-in options\n    * @deprecated Use SurrealDBStoreOptions.connectionOpts instead (fixes reconnection issues). This will be removed in a future release.\n    */\n    signinOpts?: Parameters<Surreal['signin']>[0];\n\n    /**\n     * Automatically sweep and remove expired sessions periodically.\n     * @default false\n     */\n    autoSweepExpired?: boolean;\n\n    /**\n     * Interval in milliseconds to sweep for expired sessions.\n     * @default 600000 (10 minutes)\n     */\n    autoSweepIntervalMs?: number;\n\n\n    /**\n     * Use options (Select namespace, database)\n     * @optional\n     * @deprecated Use SurrealDBStoreOptions.connectionOpts instead (fixes reconnection issues). This will be removed in a future release.\n     */\n    useOpts?: Parameters<Surreal['use']>[0];\n    /**\n     * Optional surreal db instance override.\n     */\n    surreal?: Surreal,\n\n    /**\n     * Optional logger\n     */\n    logger?: {\n        error?: (any) => void,\n        info?: (any) => void,\n        debug?: (any) => void,\n    };\n\n    /**\n     * Custom setter function for storing session data. If provided, this function will be used instead of the default upsert logic.\n     */\n    customSetter?: (db: Surreal, sessionId: string, session: SessionData) => Promise<any>;\n\n    /**\n     * Custom getter function for retrieving session data. If provided, this function will be used instead of the default select logic.\n     */\n    customGetter?: (db: Surreal, sessionId: string) => Promise<SessionData | null>;\n};\n\nexport class SurrealDBStore extends Store {\n\n    private db: Surreal;\n    private tableName: string;\n    private lastConnectionAttempt = 0;\n\n    // Has the store ever successfully connected\n    private hasConnected = false;\n    // Is currently connected\n    private isConnected = false;\n\n    constructor(private readonly options: SurrealDBStoreOptions) {\n        super();\n\n        this.db = options.surreal ?? new Surreal();\n\n        this.tableName = options.tableName ?? 'user_session';\n\n        this._connect()\n            .then(async () => {\n                this.hasConnected = true;\n                options.logger?.info?.(\"SurrealDBStore: Creating schema...\");\n                try {\n                    await this.db.query(`DEFINE TABLE OVERWRITE ${this.tableName} COMMENT 'Automatically created by express-session SurrealDB Store'`);\n                    options.logger?.info?.(\"SurrealDBStore: Schema created successfully.\");\n                }\n                catch (err) {\n                    options.logger?.error?.(\"SurrealDBStore: Failed to create schema: \" + err.message);\n                }\n            });\n\n\n        if (this.options.autoSweepExpired) {\n            const intervalMs = this.options.autoSweepIntervalMs ?? 10 * 60 * 1000;\n            setInterval(() => {\n                this.db.query(\n                    `DELETE type::table($table) WHERE expires < time::now()`,\n                    { table: this.tableName }\n                ).then(() => {\n                    options.logger?.info?.(`SurrealDBStore: Swept expired sessions from table ${this.tableName}`);\n                }).catch(err => {\n                    options.logger?.error?.(`SurrealDBStore: Failed to sweep expired sessions: ${err.message}`);\n                });\n            }, intervalMs);\n        }\n    }\n\n    /**\n     * Perform the initial connection to the database. This also sets the scope of our connection.\n     */\n    private async _connect() {\n        this.options.logger?.info?.(\"SurrealDBStore: Connecting to SurrealDB...\");\n        const connectionOpts: Parameters<Surreal['connect']>[1] = {\n            namespace: this.options.useOpts?.namespace,\n            database: this.options.useOpts?.database,\n            authentication: this.options.signinOpts as any,\n            ...this.options.connectionOpts,\n        };\n\n        await this.db.connect(this.options.url, connectionOpts)\n            .then(() => {\n                this.hasConnected = true;\n                this.options.logger?.info?.(\"SurrealDBStore: Connected to SurrealDB.\");\n            })\n            .catch(err => {\n                this.options.logger?.error?.(\"SurrealDBStore: Failed to connect to SurrealDB!\\n\" + err.message + '\\n' + err.stack);\n            });\n        this.isConnected = true;\n        this.hasConnected = true;\n    }\n\n    /**\n     * Get session data by session ID\n     */\n    get(sessionId: string, cb: Function) {\n        const getter = this.options.customGetter\n            ? this.options.customGetter(this.db, sessionId)\n            : this.db.select(new RecordId(this.tableName, sessionId));\n\n        this.options.logger?.debug?.(\"SurrealDBStore: Getting session data for session ID: \" + sessionId);\n        getter\n            .then((res) => {\n                this.options.logger?.debug?.(\"SurrealDBStore: Got session data for session ID: \" + sessionId);\n                cb(null, res);\n            })\n            .catch(err => {\n                this.options.logger?.error?.(\"SurrealDBStore: Failed to get session data for session ID: \" + sessionId + \"\\n\" + err.message + '\\n' + err.stack);\n                cb(err);\n            });\n    }\n\n    /**\n     * Set session data for a given session ID\n     */\n    set(sessionId: string, session: SessionData, cb: Function) {\n        const setter = this.options.customSetter\n            ? this.options.customSetter(this.db, sessionId, session)\n            : this.db.upsert(new RecordId(this.tableName, sessionId)).content(session as any);\n\n        this.options.logger?.debug?.(\"SurrealDBStore: Setting session data for session ID: \" + sessionId);\n        setter\n            .then((res) => {\n                this.options.logger?.debug?.(\"SurrealDBStore: Set session data for session ID: \" + sessionId);\n                cb(null, res);\n            })\n            .catch(err => {\n                this.options.logger?.error?.(\"SurrealDBStore: Failed to set session data for session ID: \" + sessionId + \"\\n\" + err.message + '\\n' + err.stack);\n                cb(err);\n            });\n    }\n\n    touch(sid: string, session, cb: Function) {\n        // TODO: The schema of the table should be automatically\n        // generated and should have a TTL on sessions\n        this.set(sid, session, cb);\n    }\n\n    destroy(sessionId: string, cb: Function) {\n        this.db.delete(new RecordId(this.tableName, sessionId))\n            .then(() => {\n                this.options.logger?.debug?.(\"SurrealDBStore: Destroyed session data for session ID: \" + sessionId);\n                cb(null);\n            })\n            .catch(err => {\n                // This is a bug with the SurrealDB SDK. It sometimes throws this error when \n                // the session is deleted. We should ignore this error and return null.\n                if (err.message.includes(\"Expected a single result output when using the ONLY keyword\")) {\n                    this.options.logger?.debug?.(\"SurrealDBStore: SurrealDB returned a known SDK error when destroying session: \" + sessionId);\n                    cb(null);\n                } else {\n                    this.options.logger?.error?.(\"SurrealDBStore: Failed to destroy session data for session ID: \" + sessionId + \"\\n\" + err.message + '\\n' + err.stack);\n                    cb(err);\n                }\n            });\n    }\n\n    length(cb: Function) {\n        this.db.query(`SELECT count() FROM type::table($table) GROUP ALL`, { 'table': this.tableName })\n            .collect()\n            .then(([result]) => {\n                this.options.logger?.debug?.(\"SurrealDBStore: Got session count: \" + result[0].count);\n                cb(result[0].count);\n            })\n            .catch(err => {\n                this.options.logger?.error?.(\"SurrealDBStore: Failed to get session count\\n\" + err.message + '\\n' + err.stack);\n                cb(err);\n            });\n    }\n\n    all(cb: Function) {\n        this.db.select(new Table(this.tableName))\n            .then(([result]) => {\n                this.options.logger?.debug?.(\"SurrealDBStore: Got all session data\");\n                cb(result);\n            })\n            .catch(err => {\n                this.options.logger?.error?.(\"SurrealDBStore: Failed to get all session data\\n\" + err.message + '\\n' + err.stack);\n                cb(err);\n            });\n    }\n\n    clear(cb: Function) {\n        this.db.query(`DELETE type::table($table)`, { 'table': this.tableName })\n            .then(() => {\n                this.options.logger?.debug?.(\"SurrealDBStore: Cleared all session data\");\n                cb(null);\n            })\n            .catch(err => {\n                this.options.logger?.error?.(\"SurrealDBStore: Failed to clear all session data\\n\" + err.message + '\\n' + err.stack);\n                cb(err);\n            });\n    }\n}\n"],"names":["SurrealDBStore","Store","options","Surreal","err","intervalMs","connectionOpts","sessionId","cb","getter","RecordId","res","session","setter","sid","result","Table"],"mappings":"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"}