{"version":3,"file":"mysql.mjs","sources":["../../../src/mysql.ts"],"sourcesContent":["import {Session} from '@shopify/shopify-api';\nimport {\n  SessionStorage,\n  RdbmsSessionStorageOptions,\n} from '@shopify/shopify-app-session-storage';\n\nimport {migrationList} from './migrations';\nimport {MySqlConnection} from './mysql-connection';\nimport {MySqlSessionStorageMigrator} from './mysql-migrator';\n\nexport interface MySQLSessionStorageOptions extends RdbmsSessionStorageOptions {\n  connectionPoolLimit: number;\n}\n\nconst defaultMySQLSessionStorageOptions: MySQLSessionStorageOptions = {\n  connectionPoolLimit: 10,\n  sessionTableName: 'shopify_sessions',\n  migratorOptions: {\n    migrationDBIdentifier: 'shopify_sessions_migrations',\n    migrationNameColumnName: 'migration_name',\n  },\n};\n\nexport class MySQLSessionStorage implements SessionStorage {\n  static withCredentials(\n    host: string,\n    dbName: string,\n    username: string,\n    password: string,\n    opts: Partial<MySQLSessionStorageOptions>,\n  ) {\n    return new MySQLSessionStorage(\n      new URL(\n        `mysql://${encodeURIComponent(username)}:${encodeURIComponent(\n          password,\n        )}@${host}/${encodeURIComponent(dbName)}`,\n      ),\n      opts,\n    );\n  }\n\n  public readonly ready: Promise<void>;\n  private internalInit: Promise<void>;\n  private options: MySQLSessionStorageOptions;\n  private connection: MySqlConnection;\n  private migrator: MySqlSessionStorageMigrator;\n\n  constructor(\n    dbUrl: URL | string,\n    opts: Partial<MySQLSessionStorageOptions> = {},\n  ) {\n    this.options = {...defaultMySQLSessionStorageOptions, ...opts};\n    this.internalInit = this.init(\n      typeof dbUrl === 'string' ? new URL(dbUrl) : dbUrl,\n    );\n    this.migrator = new MySqlSessionStorageMigrator(\n      this.connection,\n      this.options.migratorOptions,\n      migrationList,\n    );\n    this.ready = this.migrator.applyMigrations(this.internalInit);\n  }\n\n  public async storeSession(session: Session): Promise<boolean> {\n    await this.ready;\n\n    // Note milliseconds to seconds conversion for `expires` property\n    const entries = session.toPropertyArray(true).map(([key, value]) => {\n      if (key === 'expires' || key === 'refreshTokenExpires') {\n        return [key, value ? Math.floor((value as number) / 1000) : null];\n      }\n      return [key, value];\n    });\n    const query = `\n      REPLACE INTO ${this.options.sessionTableName}\n      (${entries.map(([key]) => key).join(', ')})\n      VALUES (${entries\n        .map(() => `${this.connection.getArgumentPlaceholder()}`)\n        .join(', ')})\n    `;\n    await this.connection.query(\n      query,\n      entries.map(([_key, value]) => value),\n    );\n    return true;\n  }\n\n  public async loadSession(id: string): Promise<Session | undefined> {\n    await this.ready;\n    const query = `\n      SELECT * FROM \\`${this.options.sessionTableName}\\`\n      WHERE id = ${this.connection.getArgumentPlaceholder()};\n    `;\n    const [rows] = await this.connection.query(query, [id]);\n    if (!Array.isArray(rows) || rows?.length !== 1) return undefined;\n    const rawResult = rows[0] as any;\n    return this.databaseRowToSession(rawResult);\n  }\n\n  public async deleteSession(id: string): Promise<boolean> {\n    await this.ready;\n    const query = `\n      DELETE FROM ${this.options.sessionTableName}\n      WHERE id = ${this.connection.getArgumentPlaceholder()};\n    `;\n    await this.connection.query(query, [id]);\n    return true;\n  }\n\n  public async deleteSessions(ids: string[]): Promise<boolean> {\n    await this.ready;\n    const query = `\n      DELETE FROM ${this.options.sessionTableName}\n      WHERE id IN (${ids\n        .map(() => `${this.connection.getArgumentPlaceholder()}`)\n        .join(',')});\n    `;\n    await this.connection.query(query, ids);\n    return true;\n  }\n\n  public async findSessionsByShop(shop: string): Promise<Session[]> {\n    await this.ready;\n\n    const query = `\n      SELECT * FROM ${this.options.sessionTableName}\n      WHERE shop = ${this.connection.getArgumentPlaceholder()};\n    `;\n    const [rows] = await this.connection.query(query, [shop]);\n    if (!Array.isArray(rows) || rows?.length === 0) return [];\n\n    const results: Session[] = rows.map((row: any) => {\n      return this.databaseRowToSession(row);\n    });\n    return results;\n  }\n\n  public async disconnect(): Promise<void> {\n    await this.connection.disconnect();\n  }\n\n  private async init(dbUrl: URL) {\n    this.connection = new MySqlConnection(\n      dbUrl,\n      this.options.sessionTableName,\n      this.options.connectionPoolLimit,\n    );\n    await this.createTable();\n  }\n\n  private async createTable() {\n    const hasSessionTable = await this.connection.hasTable(\n      this.options.sessionTableName,\n    );\n    if (!hasSessionTable) {\n      const query = `\n        CREATE TABLE ${this.options.sessionTableName} (\n          id varchar(255) NOT NULL PRIMARY KEY,\n          shop varchar(255) NOT NULL,\n          state varchar(255) NOT NULL,\n          isOnline tinyint NOT NULL,\n          scope varchar(255),\n          expires integer,\n          accessToken varchar(255),\n          refreshToken varchar(255),\n          refreshTokenExpires integer,\n          userId BIGINT,\n          firstName varchar(255),\n          lastName varchar(255),\n          email varchar(255),\n          accountOwner tinyint,\n          locale varchar(255),\n          collaborator tinyint,\n          emailVerified tinyint\n        )\n      `;\n      await this.connection.query(query);\n    }\n  }\n\n  private databaseRowToSession(row: any): Session {\n    // convert seconds to milliseconds prior to creating Session object\n    if (row.expires) row.expires *= 1000;\n    if (row.refreshTokenExpires) row.refreshTokenExpires *= 1000;\n    return Session.fromPropertyArray(Object.entries(row), true);\n  }\n}\n"],"names":[],"mappings":"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"}