| 6 | const error = Symbol("error"); |
| 7 | |
| 8 | function queue<S>( |
| 9 | connection: Promise<Kysely<S>>, |
| 10 | trigger?: (changes: EncodedChanges) => void, |
| 11 | ) { |
| 12 | const queue = new Map<object, Operation<[], unknown, S>>(); |
| 13 | let queueing: Promise<Map<object, unknown>> | undefined; |
| 14 | |
| 15 | async function dequeue() { |
| 16 | if (queueing) return queueing; |
| 17 | return (queueing = new Promise((resolve) => |
| 18 | raf(async () => { |
| 19 | const db = await connection; |
| 20 | const result = new Map<object, unknown>(); |
| 21 | await db |
| 22 | .transaction() |
| 23 | .execute(async (trx: any) => { |
| 24 | const current: any = |
| 25 | trigger && (await selectVersion.bind(trx)().execute()).current; |
| 26 | for (const [id, query] of queue.entries()) { |
| 27 | const rows = await query(trx).catch((x) => ({ [error]: x })); |
| 28 | result.set(id, rows); |
| 29 | } |
| 30 | trigger?.( |
| 31 | (await changesSince.bind(trx)(current).execute()) as string, |
| 32 | ); |
| 33 | }) |
| 34 | .catch((reason) => { |
| 35 | if (String(reason).includes("driver has already been destroyed")) { |
| 36 | return; |
| 37 | } |
| 38 | throw reason; |
| 39 | }); |
| 40 | queue.clear(); |
| 41 | queueing = undefined; |
| 42 | resolve(result); |
| 43 | }), |
| 44 | )); |
| 45 | } |
| 46 | |
| 47 | return { |
| 48 | enqueue<T extends any[], R>( |
| 49 | id: object, |
| 50 | operation: Operation<T, R, S>, |
| 51 | ...args: T |
| 52 | ) { |
| 53 | queue.set(id, (db: Kysely<S>) => operation(db, ...args)); |
| 54 | return dequeue() |
| 55 | .then((x) => x.get(id)) |
| 56 | .then((x) => { |
| 57 | if (x && typeof x === "object" && error in x) throw x[error]; |
| 58 | else return x as R; |
| 59 | }); |
| 60 | }, |
| 61 | }; |
| 62 | } |
| 63 | |
| 64 | export { queue }; |