Skip to content

Commit

Permalink
refactor: make overloads, not unions
Browse files Browse the repository at this point in the history
  • Loading branch information
MKRhere committed Feb 26, 2023
1 parent 2ba0c30 commit 5f0c2c3
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 27 deletions.
29 changes: 23 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This package provides official storage adapters for Telegraf v4.12+ sessions [[s

> ⚠️ Very Important!
>
> Read one of the following sections before using this module. You're not meant to import the default route!
> Read one of the following sections before using this module. You're not meant to import the default path!
An in-memory session module is bundled with Telegraf. The following modules are available here:

Expand All @@ -27,14 +27,18 @@ Usage is pretty straightforward:
```TS
import { Redis } from "@telegraf/session/redis";

const store = Redis({ url: "redis://127.0.0.1:6379" });
const store = Redis({
url: "redis://127.0.0.1:6379",
});

const bot = new Telegraf(token, opts);
bot.use(session({ store }));

// the rest of your bot
```

To reuse an existing Redis client, use `Redis({ client })` instead.

## MongoDB

Install the official MongoDB driver alongside this module.
Expand All @@ -48,14 +52,19 @@ Usage is pretty straightforward:
```TS
import { Mongo } from "@telegraf/session/mongodb";

const store = Mongo({ url: "mongodb://127.0.0.1:27017", database: "telegraf-bot" });
const store = Mongo({
url: "mongodb://127.0.0.1:27017",
database: "telegraf-bot",
});

const bot = new Telegraf(token, opts);
bot.use(session({ store }));

// the rest of your bot
```

To reuse an existing MongoDB client, use `Mongo({ client })` instead.

## SQLite

Install the Better-SQLite3 driver and types alongside this module.
Expand All @@ -70,14 +79,18 @@ Usage is pretty straightforward:
```TS
import { SQLite } from "@telegraf/session/sqlite";

const store = SQLite({ filename: "./telegraf-sessions.sqlite" });
const store = SQLite({
filename: "./telegraf-sessions.sqlite",
});

const bot = new Telegraf(token, opts);
bot.use(session({ store }));

// the rest of your bot
```

To reuse an existing Better-SQLite3 database instance, use `SQLite({ database })` instead.

## PostgreSQL

Install the 'pg' PostgreSQL driver and types alongside this module.
Expand Down Expand Up @@ -105,6 +118,8 @@ bot.use(session({ store }));
// the rest of your bot
```

To reuse an existing pg pool, use `Postgres({ pool })` instead.

## MySQL / MariaDB

Install the 'mysql2' MySQL driver alongside this module.
Expand All @@ -131,8 +146,10 @@ bot.use(session({ store }));
// the rest of your bot
```

To reuse an existing MySQL2 pool, use `MySQL({ pool })` instead.

## Background

Since [telegraf#1372](https://github.com/telegraf/telegraf/issues/1372), it has been known that all asynchronous session middleware have been prone to race-conditions. This was addressed in [telegraf#1713](https://github.com/telegraf/telegraf/pull/1713), but third-party session middleware continue to be affected. Since Telegraf 1.12.0, it's recommended that third-party middleware only provide the store parameter for session, instead of implementing session themselves. This way, they can take advantage of the safety provided by Telegraf's builtin session. Of course, if your plugin has an exceptional case, it may need to implement a middleware.
Since [telegraf#1372](https://github.com/telegraf/telegraf/issues/1372), it has been known that all asynchronous session middleware have been prone to race-conditions. This was addressed in [telegraf#1713](https://github.com/telegraf/telegraf/pull/1713), but third-party session middleware continue to be affected. Since Telegraf 1.12.0, it's recommended that third-party plugins only provide the store parameter for session, instead of implementing session themselves. This way, they can take advantage of the safety provided by Telegraf's builtin session. Of course, if your plugin has an exceptional usecase, it may need to implement its own middleware.

To begin to solve this problem, we officially maintain the 5 most common storage backends. This package is considered beta, and while it appears stable, it may have changes and bugfixes before a semver stable release.
To begin to solve this problem, we officially maintain the 5 most common storage backends. This package is considered beta, and may have minor breaking changes and bugfixes before a semver stable release. Feedback is welcome via issues and in the group: [TelegrafJSChat](https://t.me/TelegrafJSChat)
8 changes: 4 additions & 4 deletions mongodb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ interface ExistingClientOpts {
onInitError?: (err: unknown) => void;
}

export type Opts = NewClientOpts | ExistingClientOpts;

/** @unstable */
export const Mongo = <Session>(opts: Opts): SessionStore<Session> => {
export function Mongo<Session>(opts: NewClientOpts): SessionStore<Session>;
export function Mongo<Session>(opts: ExistingClientOpts): SessionStore<Session>;
export function Mongo<Session>(opts: NewClientOpts | ExistingClientOpts): SessionStore<Session> {
interface SessionDoc {
key: string;
session: Session;
Expand Down Expand Up @@ -68,4 +68,4 @@ export const Mongo = <Session>(opts: Opts): SessionStore<Session> => {
await collection.deleteOne({ key });
},
};
};
}
10 changes: 6 additions & 4 deletions mysql.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { MysqlDialect } from "kysely";
import { createPool, Pool, PoolOptions } from "mysql2";
import { KyselyStore } from "./kysely";
import { SessionStore } from "./types";

interface NewPoolOpts {
host?: string | undefined;
Expand Down Expand Up @@ -31,11 +32,11 @@ interface ExistingPoolOpts {
onInitError?: (err: unknown) => void;
}

export type Opts = NewPoolOpts | ExistingPoolOpts;

/** @unstable */
export const MySQL = <Session>(opts: Opts) =>
KyselyStore<Session>({
export function MySQL<Session>(opts: NewPoolOpts): SessionStore<Session>;
export function MySQL<Session>(opts: ExistingPoolOpts): SessionStore<Session>;
export function MySQL<Session>(opts: NewPoolOpts | ExistingPoolOpts) {
return KyselyStore<Session>({
config:
"pool" in opts
? { dialect: new MysqlDialect({ pool: opts.pool }) }
Expand All @@ -54,3 +55,4 @@ export const MySQL = <Session>(opts: Opts) =>
table: opts.table,
onInitError: opts.onInitError,
});
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@telegraf/session",
"version": "2.0.0-beta.2",
"version": "2.0.0-beta.3",
"description": "Session store adapters for Telegraf",
"main": "./memory.js",
"scripts": {
Expand Down
10 changes: 6 additions & 4 deletions pg.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { PostgresDialect } from "kysely";
import { Pool, PoolConfig } from "pg";
import { KyselyStore } from "./kysely";
import { SessionStore } from "./types";

interface NewPoolOpts {
host?: string | undefined;
Expand Down Expand Up @@ -31,11 +32,11 @@ interface ExistingPoolOpts {
onInitError?: (err: unknown) => void;
}

export type Opts = NewPoolOpts | ExistingPoolOpts;

/** @unstable */
export const Postgres = <Session>(opts: Opts) =>
KyselyStore<Session>({
export function Postgres<Session>(opts: NewPoolOpts): SessionStore<Session>;
export function Postgres<Session>(opts: ExistingPoolOpts): SessionStore<Session>;
export function Postgres<Session>(opts: NewPoolOpts | ExistingPoolOpts) {
return KyselyStore<Session>({
config:
"pool" in opts
? { dialect: new PostgresDialect({ pool: opts.pool }) }
Expand All @@ -54,3 +55,4 @@ export const Postgres = <Session>(opts: Opts) =>
table: opts.table,
onInitError: opts.onInitError,
});
}
8 changes: 4 additions & 4 deletions redis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ interface ExistingClientOpts {
prefix?: string;
}

export type Opts = NewClientOpts | ExistingClientOpts;

/** @unstable */
export const Redis = <Session>(opts: Opts): SessionStore<Session> => {
export function Redis<Session>(opts: NewClientOpts): SessionStore<Session>;
export function Redis<Session>(opts: ExistingClientOpts): SessionStore<Session>;
export function Redis<Session>(opts: NewClientOpts | ExistingClientOpts): SessionStore<Session> {
let client: Client;
if ("client" in opts) client = opts.client;
else client = createClient({ ...opts.config, url: opts.url });
Expand All @@ -52,4 +52,4 @@ export const Redis = <Session>(opts: Opts): SessionStore<Session> => {
return await client.del(prefix + key);
},
};
};
}
10 changes: 6 additions & 4 deletions sqlite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { SqliteDialect } from "kysely";
import { Database as Db, Options } from "better-sqlite3";
import { KyselyStore } from "./kysely";
import Database = require("better-sqlite3");
import { SessionStore } from "./types";

interface NewDatabaseOpts {
/** Filename to use for SQLite sessions. */
Expand All @@ -25,14 +26,15 @@ interface ExistingDatabaseOpts {
onInitError?: (err: unknown) => void;
}

export type Opts = NewDatabaseOpts | ExistingDatabaseOpts;

/** @unstable */
export const SQLite = <Session>(opts: Opts) =>
KyselyStore<Session>({
export function SQLite<Session>(opts: NewDatabaseOpts): SessionStore<Session>;
export function SQLite<Session>(opts: ExistingDatabaseOpts): SessionStore<Session>;
export function SQLite<Session>(opts: NewDatabaseOpts | ExistingDatabaseOpts) {
return KyselyStore<Session>({
config:
"database" in opts
? { dialect: new SqliteDialect({ database: opts.database }) }
: { dialect: new SqliteDialect({ database: new Database(opts.filename, opts.config) }) },
onInitError: opts.onInitError,
});
}

0 comments on commit 5f0c2c3

Please sign in to comment.