@bumail/queue 0.3.0 → 0.4.1
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.
- package/README.md +4 -2
- package/dist/chunks/index-b4xvg3xn.js +23 -0
- package/dist/chunks/index-b4xvg3xn.js.map +10 -0
- package/dist/chunks/index-vg971acr.js.map +2 -2
- package/dist/contract/queue-store.d.ts +8 -2
- package/dist/contract/queue-store.d.ts.map +1 -1
- package/dist/dsn/build.d.ts +7 -3
- package/dist/dsn/build.d.ts.map +1 -1
- package/dist/errors.d.ts +3 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/index.js +45 -11
- package/dist/index.js.map +5 -5
- package/dist/masked.d.ts +2 -1
- package/dist/masked.d.ts.map +1 -1
- package/dist/postgres/connect.d.ts +6 -0
- package/dist/postgres/connect.d.ts.map +1 -1
- package/dist/postgres/index.js +47 -17
- package/dist/postgres/index.js.map +7 -6
- package/dist/postgres/isolation.d.ts +6 -0
- package/dist/postgres/isolation.d.ts.map +1 -0
- package/dist/postgres/schema.d.ts.map +1 -1
- package/dist/postgres/store.d.ts.map +1 -1
- package/dist/queue/deliver.d.ts +3 -1
- package/dist/queue/deliver.d.ts.map +1 -1
- package/dist/queue/notify.d.ts +3 -2
- package/dist/queue/notify.d.ts.map +1 -1
- package/dist/redis/connect.d.ts +2 -0
- package/dist/redis/connect.d.ts.map +1 -1
- package/dist/redis/index.js +14 -6
- package/dist/redis/index.js.map +4 -4
- package/dist/redis/store.d.ts.map +1 -1
- package/docs/guide.md +87 -11
- package/docs/roadmap.md +25 -18
- package/docs/troubleshooting.md +76 -5
- package/package.json +3 -3
- package/dist/chunks/index-y3n1w2az.js +0 -18
- package/dist/chunks/index-y3n1w2az.js.map +0 -10
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/postgres/connect.ts", "../src/postgres/rows.ts", "../src/postgres/schema.ts", "../src/postgres/statements.ts", "../src/postgres/store.ts"],
|
|
3
|
+
"sources": ["../src/postgres/connect.ts", "../src/postgres/rows.ts", "../src/postgres/isolation.ts", "../src/postgres/schema.ts", "../src/postgres/statements.ts", "../src/postgres/store.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import { invalid } from '../errors';\nimport { masked } from '../masked';\nimport type { PostgresClient, PostgresQueueStoreOptions } from './options';\n\n/** The tables of one queue, each name its prefix and a fixed suffix. */\nexport interface Tables {\n\treadonly prefix: string;\n\treadonly items: string;\n\treadonly messages: string;\n\treadonly schema: string;\n}\n\n/** The client a store uses, and whether it opened it (and so closes it). */\nexport interface Connection {\n\treadonly client: PostgresClient;\n\treadonly owned: boolean;\n\treadonly tables: Tables;\n}\n\nconst DEFAULT_PREFIX = 'bumail_queue_';\n\n/**\n * Within PostgreSQL's 63 bytes for a name once the longest name derived\n * from it (`messages_item_id_fkey`, 21) follows. Nothing else is allowed:\n * the prefix is written into the statements, never bound.\n */\nconst PREFIX = /^[a-z_][a-z0-9_]{0,39}$/;\n\nfunction tablesOf(value: unknown): Tables {\n\tconst prefix = value ?? DEFAULT_PREFIX;\n\tif (typeof prefix !== 'string' || !PREFIX.test(prefix)) {\n\t\tthrow invalid(\n\t\t\t`tablePrefix must be lowercase letters, digits and underscores, starting with a letter or an underscore, at most 40 characters, not ${JSON.stringify(prefix)}`,\n\t\t);\n\t}\n\treturn {\n\t\tprefix,\n\t\titems: `${prefix}items`,\n\t\tmessages: `${prefix}messages`,\n\t\tschema: `${prefix}schema`,\n\t};\n}\n\nconst NEEDS_SQL =\n\t'A PostgreSQL queue store needs sql: a Bun.SQL client or a postgres:// URL';\n\n/** A `postgres://` or `postgresql://` URL; never echoed, since it may hold a password. */\nfunction urlOf(value: string | URL): URL {\n\tconst url = URL.parse(String(value));\n\tif (url === null || !['postgres:', 'postgresql:'].includes(url.protocol)) {\n\t\tthrow invalid(NEEDS_SQL);\n\t}\n\treturn url;\n}\n\n/**\n * A client of the store's own for the URL. `Bun.SQL` refuses some\n * parameters at once (a `sslmode` it does not know): that is `INVALID`,\n * its reason kept and the password, should it be repeated, masked.\n */\nfunction clientFor(url: URL): PostgresClient {\n\ttry {\n\t\treturn new Bun.SQL(url.href);\n\t} catch (error) {\n\t\tconst reason = error instanceof Error ? error.message : String(error);\n\t\tthrow invalid(\n\t\t\t`The URL in sql cannot be opened: ${masked(reason, url.password)}`,\n\t\t);\n\t}\n}\n\nconst isClient = (value: unknown): value is PostgresClient =>\n\t(typeof value === 'object' || typeof value === 'function') &&\n\tvalue !== null &&\n\ttypeof (value as PostgresClient).unsafe === 'function' &&\n\ttypeof (value as PostgresClient).begin === 'function' &&\n\ttypeof (value as PostgresClient).close === 'function';\n\n/** A `Bun.SQL` client says which database it speaks: the queue needs PostgreSQL. */\nfunction checkAdapter(client: PostgresClient): void {\n\tconst adapter = (client as { options?: { adapter?: unknown } }).options\n\t\t?.adapter;\n\tif (adapter !== undefined && adapter !== 'postgres') {\n\t\tthrow invalid(\n\t\t\t`sql is a ${String(adapter)} client; the queue needs a PostgreSQL one`,\n\t\t);\n\t}\n}\n\n/** Checks the options and opens a client for a URL; connects to nothing yet. */\nexport function connect(options: PostgresQueueStoreOptions): Connection {\n\tconst given = (options as PostgresQueueStoreOptions | undefined)?.sql;\n\tconst tables = tablesOf(options?.tablePrefix);\n\tif (isClient(given)) {\n\t\tcheckAdapter(given);\n\t\treturn {
|
|
5
|
+
"import { invalid } from '../errors';\nimport { masked } from '../masked';\nimport type { PostgresClient, PostgresQueueStoreOptions } from './options';\n\n/** The tables of one queue, each name its prefix and a fixed suffix. */\nexport interface Tables {\n\treadonly prefix: string;\n\treadonly items: string;\n\treadonly messages: string;\n\treadonly schema: string;\n}\n\n/** The client a store uses, and whether it opened it (and so closes it). */\nexport interface Connection {\n\treadonly client: PostgresClient;\n\treadonly owned: boolean;\n\treadonly tables: Tables;\n\t/**\n\t * The password the client logs in with, as far as the store can know\n\t * it — its URL's, or a given `Bun.SQL`'s `options.password` — so an\n\t * error that repeats it is masked; `''` when unknown.\n\t */\n\treadonly password: string;\n}\n\nconst DEFAULT_PREFIX = 'bumail_queue_';\n\n/**\n * Within PostgreSQL's 63 bytes for a name once the longest name derived\n * from it (`messages_item_id_fkey`, 21) follows. Nothing else is allowed:\n * the prefix is written into the statements, never bound.\n */\nconst PREFIX = /^[a-z_][a-z0-9_]{0,39}$/;\n\nfunction tablesOf(value: unknown): Tables {\n\tconst prefix = value ?? DEFAULT_PREFIX;\n\tif (typeof prefix !== 'string' || !PREFIX.test(prefix)) {\n\t\tthrow invalid(\n\t\t\t`tablePrefix must be lowercase letters, digits and underscores, starting with a letter or an underscore, at most 40 characters, not ${JSON.stringify(prefix)}`,\n\t\t);\n\t}\n\treturn {\n\t\tprefix,\n\t\titems: `${prefix}items`,\n\t\tmessages: `${prefix}messages`,\n\t\tschema: `${prefix}schema`,\n\t};\n}\n\nconst NEEDS_SQL =\n\t'A PostgreSQL queue store needs sql: a Bun.SQL client or a postgres:// URL';\n\n/** A `postgres://` or `postgresql://` URL; never echoed, since it may hold a password. */\nfunction urlOf(value: string | URL): URL {\n\tconst url = URL.parse(String(value));\n\tif (url === null || !['postgres:', 'postgresql:'].includes(url.protocol)) {\n\t\tthrow invalid(NEEDS_SQL);\n\t}\n\treturn url;\n}\n\n/**\n * A client of the store's own for the URL. `Bun.SQL` refuses some\n * parameters at once (a `sslmode` it does not know): that is `INVALID`,\n * its reason kept and the password, should it be repeated, masked.\n */\nfunction clientFor(url: URL): PostgresClient {\n\ttry {\n\t\treturn new Bun.SQL(url.href);\n\t} catch (error) {\n\t\tconst reason = error instanceof Error ? error.message : String(error);\n\t\tthrow invalid(\n\t\t\t`The URL in sql cannot be opened: ${masked(reason, url.password)}`,\n\t\t);\n\t}\n}\n\nconst isClient = (value: unknown): value is PostgresClient =>\n\t(typeof value === 'object' || typeof value === 'function') &&\n\tvalue !== null &&\n\ttypeof (value as PostgresClient).unsafe === 'function' &&\n\ttypeof (value as PostgresClient).begin === 'function' &&\n\ttypeof (value as PostgresClient).close === 'function';\n\n/** A `Bun.SQL` client says which database it speaks: the queue needs PostgreSQL. */\nfunction checkAdapter(client: PostgresClient): void {\n\tconst adapter = (client as { options?: { adapter?: unknown } }).options\n\t\t?.adapter;\n\tif (adapter !== undefined && adapter !== 'postgres') {\n\t\tthrow invalid(\n\t\t\t`sql is a ${String(adapter)} client; the queue needs a PostgreSQL one`,\n\t\t);\n\t}\n}\n\n/** A given client's password, when it says it as text (`Bun.SQL` does). */\nfunction passwordOf(client: PostgresClient): string {\n\tconst password = (client as { options?: { password?: unknown } }).options\n\t\t?.password;\n\treturn typeof password === 'string' ? password : '';\n}\n\n/** Checks the options and opens a client for a URL; connects to nothing yet. */\nexport function connect(options: PostgresQueueStoreOptions): Connection {\n\tconst given = (options as PostgresQueueStoreOptions | undefined)?.sql;\n\tconst tables = tablesOf(options?.tablePrefix);\n\tif (isClient(given)) {\n\t\tcheckAdapter(given);\n\t\treturn {\n\t\t\tclient: given,\n\t\t\towned: false,\n\t\t\ttables,\n\t\t\tpassword: passwordOf(given),\n\t\t};\n\t}\n\tif (typeof given !== 'string' && !(given instanceof URL)) {\n\t\tthrow invalid(NEEDS_SQL);\n\t}\n\tconst url = urlOf(given);\n\treturn {\n\t\tclient: clientFor(url),\n\t\towned: true,\n\t\ttables,\n\t\tpassword: url.password,\n\t};\n}\n",
|
|
6
6
|
"import type { QueueItem, RecipientState } from '../contract/types';\n\n/** A row of the items table, as `Bun.SQL` reads it. */\nexport interface ItemRow {\n\treadonly id: string;\n\treadonly sender: string;\n\t/** `jsonb`: parsed or not, depending on the client. */\n\treadonly recipients: string | RecipientState[];\n\treadonly size: number;\n\treadonly created_at: number;\n\treadonly next_attempt_at: number;\n\treadonly attempts: number;\n\treadonly delay_notified: boolean;\n\treadonly lease_owner: string | null;\n\treadonly lease_expires_at: number | null;\n}\n\n/** The columns a query reads to build an item. */\nexport const COLUMNS =\n\t'id, sender, recipients, size, created_at, next_attempt_at, attempts, delay_notified, lease_owner, lease_expires_at';\n\nexport function itemOf(row: ItemRow): QueueItem {\n\tconst recipients =\n\t\ttypeof row.recipients === 'string'\n\t\t\t? (JSON.parse(row.recipients) as RecipientState[])\n\t\t\t: structuredClone(row.recipients);\n\tconst item: QueueItem = {\n\t\tid: row.id,\n\t\tfrom: row.sender,\n\t\trecipients,\n\t\tsize: row.size,\n\t\tcreatedAt: row.created_at,\n\t\tnextAttemptAt: row.next_attempt_at,\n\t\tattempts: row.attempts,\n\t\tdelayNotified: row.delay_notified,\n\t};\n\tif (row.lease_owner === null || row.lease_expires_at === null) return item;\n\treturn {\n\t\t...item,\n\t\tlease: { owner: row.lease_owner, expiresAt: row.lease_expires_at },\n\t};\n}\n\n/** The rows a statement returned. */\nexport const rowsOf = async <T>(pending: PromiseLike<unknown>) =>\n\t(await pending) as T[];\n",
|
|
7
|
-
"import
|
|
7
|
+
"import type { PostgresClient, PostgresQueryable } from './options';\nimport { rowsOf } from './rows';\n\n/**\n * The first statement of every transaction that writes: READ COMMITTED\n * whatever the client's sessions default to. The claim's `FOR UPDATE SKIP\n * LOCKED` and the `maxItems` advisory lock count on it: under `repeatable\n * read` or `serializable`, a row another transaction changed since the\n * snapshot fails with SQLSTATE 40001, and the count taken after the lock\n * would read a snapshot from before it.\n */\nconst READ_COMMITTED =\n\t'SET TRANSACTION ISOLATION LEVEL READ COMMITTED, READ WRITE';\n\n/** `fn` in a transaction at READ COMMITTED: every write of the store, its migration included, goes through here. */\nexport async function writing<T>(\n\tsql: PostgresClient,\n\tfn: (tx: PostgresQueryable) => Promise<T>,\n): Promise<T> {\n\treturn (await sql.begin(async (tx: PostgresQueryable) => {\n\t\tawait tx.unsafe(READ_COMMITTED);\n\t\treturn fn(tx);\n\t})) as T;\n}\n\n/** One statement that writes, at READ COMMITTED; its rows. */\nexport function written<T>(\n\tsql: PostgresClient,\n\tquery: string,\n\tvalues: unknown[] = [],\n): Promise<T[]> {\n\treturn writing(sql, (tx) => rowsOf<T>(tx.unsafe(query, values)));\n}\n",
|
|
8
|
+
"import { QueueError } from '../errors';\nimport type { Tables } from './connect';\nimport { writing } from './isolation';\nimport type { PostgresClient, PostgresQueryable } from './options';\nimport { rowsOf } from './rows';\n\n/**\n * The migrations, in order, each a list of statements: the schema table's\n * `version` is how many of them the database has run. A migration is\n * never edited once released; a change is a new one at the end. They\n * follow the `bun:sqlite` store's, column for column: times are\n * milliseconds as `double precision` (SQLite's `REAL`), the recipients\n * JSON, the message its bytes, in a table of its own.\n */\nexport function migrations({\n\tprefix,\n\titems,\n\tmessages,\n}: Tables): readonly (readonly string[])[] {\n\treturn [\n\t\t// 1: the items, and their messages in a table of their own.\n\t\t[\n\t\t\t`CREATE TABLE ${items} (\n\t\t\t\tseq bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n\t\t\t\tid text NOT NULL UNIQUE,\n\t\t\t\tsender text NOT NULL,\n\t\t\t\trecipients jsonb NOT NULL CHECK (jsonb_typeof(recipients) = 'array'),\n\t\t\t\tsize integer NOT NULL,\n\t\t\t\tcreated_at double precision NOT NULL,\n\t\t\t\tnext_attempt_at double precision NOT NULL,\n\t\t\t\tattempts integer NOT NULL,\n\t\t\t\tdelay_notified boolean NOT NULL,\n\t\t\t\tlease_owner text,\n\t\t\t\tlease_expires_at double precision,\n\t\t\t\tCHECK ((lease_owner IS NULL) = (lease_expires_at IS NULL))\n\t\t\t)`,\n\t\t\t// The claim's order, and its range: the earliest due first.\n\t\t\t`CREATE INDEX ${prefix}items_due ON ${items} (next_attempt_at, seq)`,\n\t\t\t`CREATE TABLE ${messages} (\n\t\t\t\titem_id text PRIMARY KEY REFERENCES ${items} (id) ON DELETE CASCADE,\n\t\t\t\tcontent bytea NOT NULL\n\t\t\t)`,\n\t\t],\n\t];\n}\n\n/** Tables a newer store wrote, with more migrations than this one knows, are refused. */\nfunction checkNotNewer(version: number, known: number): void {\n\tif (version > known) {\n\t\tthrow new QueueError(\n\t\t\t'INVALID',\n\t\t\t`The database is at schema version ${version}, newer than this store's ${known}`,\n\t\t);\n\t}\n}\n\n/**\n * The version the tables are at, read with no lock and no DDL — so a role\n * with no `CREATE` reads it — or `undefined` with no schema table yet.\n */\nasync function versionOf(\n\tsql: PostgresQueryable,\n\ttables: Tables,\n): Promise<number | undefined> {\n\tconst [found] = await rowsOf<{ present: boolean }>(\n\t\tsql.unsafe('SELECT to_regclass($1) IS NOT NULL AS present', [\n\t\t\ttables.schema,\n\t\t]),\n\t);\n\tif (!found?.present) return undefined;\n\tconst [row] = await rowsOf<{ version: number }>(\n\t\tsql.unsafe(`SELECT version FROM ${tables.schema}`),\n\t);\n\treturn row?.version ?? 0;\n}\n\n/**\n * Brings the tables to the last migration. Tables already current are\n * only read: no lock, no DDL, so a worker's role needs no `CREATE`.\n * Otherwise in one transaction — a migration that fails leaves them as\n * they were — under one advisory lock, so instances starting together\n * run each migration once.\n */\nexport async function migrate(\n\tclient: PostgresClient,\n\ttables: Tables,\n): Promise<void> {\n\tconst all = migrations(tables);\n\tconst current = await versionOf(client, tables);\n\tif (current !== undefined) {\n\t\tcheckNotNewer(current, all.length);\n\t\tif (current === all.length) return;\n\t}\n\t// READ COMMITTED, as every write: under a stricter level the version\n\t// read after the lock would be the snapshot's, from before it.\n\tawait writing(client, async (sql: PostgresQueryable) => {\n\t\tawait sql.unsafe('SELECT pg_advisory_xact_lock(hashtext($1)) IS NULL', [\n\t\t\ttables.schema,\n\t\t]);\n\t\tawait sql.unsafe(\n\t\t\t`CREATE TABLE IF NOT EXISTS ${tables.schema} (version integer NOT NULL)`,\n\t\t);\n\t\tconst [row] = await rowsOf<{ version: number }>(\n\t\t\tsql.unsafe(`SELECT version FROM ${tables.schema}`),\n\t\t);\n\t\tconst version = row?.version ?? 0;\n\t\tcheckNotNewer(version, all.length);\n\t\tfor (const statements of all.slice(version)) {\n\t\t\tfor (const statement of statements) await sql.unsafe(statement);\n\t\t}\n\t\tif (row === undefined) {\n\t\t\tawait sql.unsafe(\n\t\t\t\t`INSERT INTO ${tables.schema} (version) VALUES ($1::int)`,\n\t\t\t\t[all.length],\n\t\t\t);\n\t\t} else if (version < all.length) {\n\t\t\tawait sql.unsafe(`UPDATE ${tables.schema} SET version = $1::int`, [\n\t\t\t\tall.length,\n\t\t\t]);\n\t\t}\n\t});\n}\n",
|
|
8
9
|
"import type { Tables } from './connect';\nimport { COLUMNS } from './rows';\n\n/**\n * The statements a store runs on its tables, written once per store: the\n * table names are its prefix's (checked to be a plain name), and every\n * value is a parameter. Each parameter is cast, so nothing depends on the\n * type a client infers for it; JSON goes as text, cast to `jsonb`.\n */\nexport function statementsOf({ items, messages }: Tables) {\n\treturn {\n\t\t/** $1 id, $2 sender, $3 recipients, $4 size, $5 created_at, $6 message: the item and its message in one statement, or neither. */\n\t\tinsert: `WITH item AS (\n\t\t\tINSERT INTO ${items} (id, sender, recipients, size, created_at,\n\t\t\t\tnext_attempt_at, attempts, delay_notified)\n\t\t\tVALUES ($1, $2, $3::text::jsonb, $4::int, $5::float8, $5::float8, 0, false)\n\t\t\tRETURNING id)\n\t\tINSERT INTO ${messages} (item_id, content) SELECT id, $6::bytea FROM item`,\n\t\t/** Taken before counting, by every add given `maxItems`. */\n\t\tlockAdds: `SELECT pg_advisory_xact_lock(hashtext('${items}')) IS NULL`,\n\t\tcount: `SELECT count(*)::int AS n FROM ${items}`,\n\t\tget: `SELECT ${COLUMNS} FROM ${items} WHERE id = $1`,\n\t\tlist: `SELECT ${COLUMNS} FROM ${items}\n\t\t\tORDER BY next_attempt_at, seq LIMIT $1::int OFFSET $2::int`,\n\t\tmessage: `SELECT content FROM ${messages} WHERE item_id = $1`,\n\t\t/**\n\t\t * $1 owner, $2 now, $3 expiry. The earliest due item no other\n\t\t * transaction holds, locked as it is picked — one being claimed\n\t\t * elsewhere is skipped, not waited for — then leased.\n\t\t */\n\t\tclaim: `UPDATE ${items} SET lease_owner = $1, lease_expires_at = $3::float8\n\t\t\tWHERE seq = (SELECT seq FROM ${items}\n\t\t\t\tWHERE next_attempt_at <= $2::float8\n\t\t\t\t\tAND (lease_owner IS NULL OR lease_expires_at <= $2::float8)\n\t\t\t\tORDER BY next_attempt_at, seq LIMIT 1\n\t\t\t\tFOR UPDATE SKIP LOCKED)\n\t\t\tRETURNING ${COLUMNS}`,\n\t\trenew: `UPDATE ${items} SET lease_expires_at = $1::float8\n\t\t\tWHERE id = $2 AND lease_owner = $3 RETURNING seq`,\n\t\t/** The owner's item, locked until the transaction ends. */\n\t\theld: `SELECT ${COLUMNS} FROM ${items}\n\t\t\tWHERE id = $1 AND lease_owner = $2 FOR UPDATE`,\n\t\t/** $1 id, $2 recipients, $3 next attempt, $4 attempts, $5 delay notified: the lease let go of. */\n\t\trecord: `UPDATE ${items} SET recipients = $2::text::jsonb,\n\t\t\tnext_attempt_at = $3::float8, attempts = $4::int,\n\t\t\tdelay_notified = $5::boolean, lease_owner = NULL,\n\t\t\tlease_expires_at = NULL WHERE id = $1`,\n\t\t/** The message goes with it: ON DELETE CASCADE. */\n\t\tdrop: `DELETE FROM ${items} WHERE id = $1 RETURNING ${COLUMNS}`,\n\t\tmoveDue: `UPDATE ${items} SET next_attempt_at = $1::float8\n\t\t\tWHERE id = $2 RETURNING seq`,\n\t\tgiveBack: `UPDATE ${items} SET next_attempt_at = $1::float8,\n\t\t\tlease_owner = NULL, lease_expires_at = NULL\n\t\t\tWHERE id = $2 AND lease_owner = $3 RETURNING seq`,\n\t} as const;\n}\n\nexport type Statements = ReturnType<typeof statementsOf>;\n",
|
|
9
|
-
"import {\n\tapplyAttempt,\n\tcheckClaim,\n\tcheckList,\n\tcheckMaxItems,\n\tcheckNewItem,\n\tcheckOwner,\n\tcheckResult,\n\tcheckTime,\n\tisDone,\n\tnewItem,\n} from '../contract/checks';\nimport type { QueueStore } from '../contract/queue-store';\nimport type {\n\tAddOptions,\n\tAttemptResult,\n\tClaimRequest,\n\tNewQueueItem,\n\tQueueItem,\n\tQueueListOptions,\n} from '../contract/types';\nimport { QueueError } from '../errors';\nimport { isStorable } from '../text';\nimport { type Connection, connect, type Tables } from './connect';\nimport type {
|
|
10
|
+
"import {\n\tapplyAttempt,\n\tcheckClaim,\n\tcheckList,\n\tcheckMaxItems,\n\tcheckNewItem,\n\tcheckOwner,\n\tcheckResult,\n\tcheckTime,\n\tisDone,\n\tnewItem,\n} from '../contract/checks';\nimport type { QueueStore } from '../contract/queue-store';\nimport type {\n\tAddOptions,\n\tAttemptResult,\n\tClaimRequest,\n\tNewQueueItem,\n\tQueueItem,\n\tQueueListOptions,\n} from '../contract/types';\nimport { QueueError } from '../errors';\nimport { masked } from '../masked';\nimport { isStorable } from '../text';\nimport { type Connection, connect, type Tables } from './connect';\nimport { writing, written } from './isolation';\nimport type { PostgresClient, PostgresQueueStoreOptions } from './options';\nimport { type ItemRow, itemOf, rowsOf } from './rows';\nimport { migrate } from './schema';\nimport { type Statements, statementsOf } from './statements';\n\nexport type {\n\tPostgresClient,\n\tPostgresQueryable,\n\tPostgresQueueStoreOptions,\n} from './options';\n\nconst messageOf = (error: unknown) =>\n\terror instanceof Error ? error.message : String(error);\n\n/**\n * A `QueueStore` on PostgreSQL through Bun's own `Bun.SQL`: several\n * instances, on one machine or many, share one queue. A claim is one\n * `UPDATE … RETURNING` whose item a `SELECT … FOR UPDATE SKIP LOCKED`\n * picks, so two instances never take the same item, and none waits on an\n * item another is taking. Messages live in a table of their own, dropped\n * with their item. The tables are made, or brought up to date, by\n * `migrate()`, or by the first call that needs them.\n */\nexport class PostgresQueueStore implements QueueStore {\n\treadonly #client: PostgresClient;\n\treadonly #owned: boolean;\n\treadonly #tables: Tables;\n\treadonly #q: Statements;\n\treadonly #password: string;\n\t#migrated: Promise<void> | undefined;\n\t#closed = false;\n\n\tprivate constructor({ client, owned, tables, password }: Connection) {\n\t\tthis.#client = client;\n\t\tthis.#owned = owned;\n\t\tthis.#tables = tables;\n\t\tthis.#password = password;\n\t\tthis.#q = statementsOf(tables);\n\t}\n\n\t/** Checks the options; connects to nothing until the first call. What is wrong is `INVALID`. */\n\tstatic open(options: PostgresQueueStoreOptions): PostgresQueueStore {\n\t\treturn new PostgresQueueStore(connect(options));\n\t}\n\n\t/**\n\t * Makes the tables, or brings them to the last migration; once per\n\t * store, and safe while other instances do the same. A failure is\n\t * `INVALID`, and the next call tries again.\n\t */\n\tmigrate(): Promise<void> {\n\t\tif (this.#closed) return Promise.reject(closed());\n\t\tthis.#migrated ??= migrate(this.#client, this.#tables).catch((error) => {\n\t\t\tthis.#migrated = undefined;\n\t\t\tif (error instanceof QueueError) throw error;\n\t\t\tthrow new QueueError(\n\t\t\t\t'INVALID',\n\t\t\t\t`The PostgreSQL queue cannot be set up: ${masked(messageOf(error), this.#password)}`,\n\t\t\t);\n\t\t});\n\t\treturn this.#migrated;\n\t}\n\n\t/** Closes the client the store opened for a URL, never one it was given; closing twice is fine. */\n\tasync close(): Promise<void> {\n\t\tif (this.#closed) return;\n\t\tthis.#closed = true;\n\t\tif (this.#owned) await this.#client.close();\n\t}\n\n\t/** The client, once the tables are there; `CLOSED` after `close()`. */\n\tasync #sql(): Promise<PostgresClient> {\n\t\tif (this.#closed) throw closed();\n\t\tawait this.migrate();\n\t\treturn this.#client;\n\t}\n\n\tasync #rows<T>(query: string, values: unknown[] = []): Promise<T[]> {\n\t\treturn rowsOf<T>((await this.#sql()).unsafe(query, values));\n\t}\n\n\t/** One statement that writes, at READ COMMITTED; its rows. */\n\tasync #written<T>(query: string, values: unknown[] = []): Promise<T[]> {\n\t\treturn written<T>(await this.#sql(), query, values);\n\t}\n\n\tasync add(item: NewQueueItem, options: AddOptions = {}): Promise<QueueItem> {\n\t\tcheckNewItem(item);\n\t\tconst max = checkMaxItems(options.maxItems);\n\t\tconst added = newItem(crypto.randomUUID(), item);\n\t\tconst values = [\n\t\t\tadded.id,\n\t\t\tadded.from,\n\t\t\tJSON.stringify(added.recipients),\n\t\t\tadded.size,\n\t\t\tadded.createdAt,\n\t\t\titem.message,\n\t\t];\n\t\tconst q = this.#q;\n\t\tif (max === undefined) {\n\t\t\tawait this.#written(q.insert, values);\n\t\t\treturn added;\n\t\t}\n\t\t// Adds that count wait on one lock, so two never both take the last place.\n\t\tawait writing(await this.#sql(), async (tx) => {\n\t\t\tawait tx.unsafe(q.lockAdds);\n\t\t\tconst [row] = await rowsOf<{ n: number }>(tx.unsafe(q.count));\n\t\t\tconst n = row?.n ?? 0;\n\t\t\tif (n >= max) {\n\t\t\t\tthrow new QueueError(\n\t\t\t\t\t'QUEUE_FULL',\n\t\t\t\t\t`The queue holds ${n} items, its limit`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait tx.unsafe(q.insert, values);\n\t\t});\n\t\treturn added;\n\t}\n\n\tasync get(id: string): Promise<QueueItem | undefined> {\n\t\tif (!isStorable(id)) return undefined;\n\t\tconst [row] = await this.#rows<ItemRow>(this.#q.get, [id]);\n\t\treturn row ? itemOf(row) : undefined;\n\t}\n\n\tasync list(options?: QueueListOptions): Promise<QueueItem[]> {\n\t\tconst { offset, limit } = checkList(options);\n\t\tconst rows = await this.#rows<ItemRow>(this.#q.list, [limit, offset]);\n\t\treturn rows.map(itemOf);\n\t}\n\n\tasync count(): Promise<number> {\n\t\tconst [row] = await this.#rows<{ n: number }>(this.#q.count);\n\t\treturn row?.n ?? 0;\n\t}\n\n\tasync readMessage(id: string): Promise<Uint8Array | undefined> {\n\t\tif (!isStorable(id)) return undefined;\n\t\tconst [row] = await this.#rows<{ content: Uint8Array }>(this.#q.message, [\n\t\t\tid,\n\t\t]);\n\t\treturn row ? new Uint8Array(row.content) : undefined;\n\t}\n\n\tasync claim(request: ClaimRequest): Promise<QueueItem | undefined> {\n\t\tcheckClaim(request);\n\t\tconst { owner, now, leaseMs } = request;\n\t\tconst [row] = await this.#written<ItemRow>(this.#q.claim, [\n\t\t\towner,\n\t\t\tnow,\n\t\t\tnow + leaseMs,\n\t\t]);\n\t\treturn row ? itemOf(row) : undefined;\n\t}\n\n\tasync renew(id: string, owner: string, expiresAt: number): Promise<boolean> {\n\t\tcheckOwner(owner);\n\t\tcheckTime('expiresAt', expiresAt);\n\t\tif (!isStorable(id)) return false;\n\t\tconst rows = await this.#written(this.#q.renew, [expiresAt, id, owner]);\n\t\treturn rows.length > 0;\n\t}\n\n\tasync complete(\n\t\tid: string,\n\t\towner: string,\n\t\tresult: AttemptResult,\n\t): Promise<QueueItem | undefined> {\n\t\tcheckOwner(owner);\n\t\tcheckResult(result);\n\t\tif (!isStorable(id)) return undefined;\n\t\tconst q = this.#q;\n\t\tlet done: QueueItem | undefined;\n\t\tawait writing(await this.#sql(), async (tx) => {\n\t\t\tconst [row] = await rowsOf<ItemRow>(tx.unsafe(q.held, [id, owner]));\n\t\t\tif (!row) return;\n\t\t\tconst item = applyAttempt(itemOf(row), result);\n\t\t\tif (isDone(item)) {\n\t\t\t\tawait tx.unsafe(q.drop, [id]);\n\t\t\t} else {\n\t\t\t\tawait tx.unsafe(q.record, [\n\t\t\t\t\tid,\n\t\t\t\t\tJSON.stringify(item.recipients),\n\t\t\t\t\titem.nextAttemptAt,\n\t\t\t\t\titem.attempts,\n\t\t\t\t\titem.delayNotified,\n\t\t\t\t]);\n\t\t\t}\n\t\t\tdone = item;\n\t\t});\n\t\treturn done;\n\t}\n\n\tasync reschedule(id: string, at: number, owner?: string): Promise<boolean> {\n\t\tcheckTime('at', at);\n\t\tif (!isStorable(id)) return false;\n\t\tif (owner === undefined) {\n\t\t\treturn (await this.#written(this.#q.moveDue, [at, id])).length > 0;\n\t\t}\n\t\tcheckOwner(owner);\n\t\treturn (await this.#written(this.#q.giveBack, [at, id, owner])).length > 0;\n\t}\n\n\tasync cancel(id: string): Promise<QueueItem | undefined> {\n\t\t// An id PostgreSQL cannot hold is one no item has.\n\t\tif (!isStorable(id)) return undefined;\n\t\tconst [row] = await this.#written<ItemRow>(this.#q.drop, [id]);\n\t\treturn row ? itemOf(row) : undefined;\n\t}\n}\n\nconst closed = () => new QueueError('CLOSED', 'The queue store is closed');\n"
|
|
10
11
|
],
|
|
11
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;
|
|
12
|
-
"debugId": "
|
|
12
|
+
"mappings": "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",
|
|
13
|
+
"debugId": "B46CC738D07AA2A964756E2164756E21",
|
|
13
14
|
"names": []
|
|
14
15
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { PostgresClient, PostgresQueryable } from './options';
|
|
2
|
+
/** `fn` in a transaction at READ COMMITTED: every write of the store, its migration included, goes through here. */
|
|
3
|
+
export declare function writing<T>(sql: PostgresClient, fn: (tx: PostgresQueryable) => Promise<T>): Promise<T>;
|
|
4
|
+
/** One statement that writes, at READ COMMITTED; its rows. */
|
|
5
|
+
export declare function written<T>(sql: PostgresClient, query: string, values?: unknown[]): Promise<T[]>;
|
|
6
|
+
//# sourceMappingURL=isolation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isolation.d.ts","sourceRoot":"","sources":["../../src/postgres/isolation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAcnE,oHAAoH;AACpH,wBAAsB,OAAO,CAAC,CAAC,EAC9B,GAAG,EAAE,cAAc,EACnB,EAAE,EAAE,CAAC,EAAE,EAAE,iBAAiB,KAAK,OAAO,CAAC,CAAC,CAAC,GACvC,OAAO,CAAC,CAAC,CAAC,CAKZ;AAED,8DAA8D;AAC9D,wBAAgB,OAAO,CAAC,CAAC,EACxB,GAAG,EAAE,cAAc,EACnB,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,OAAO,EAAO,GACpB,OAAO,CAAC,CAAC,EAAE,CAAC,CAEd"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/postgres/schema.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/postgres/schema.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAExC,OAAO,KAAK,EAAE,cAAc,EAAqB,MAAM,WAAW,CAAC;AAGnE;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,EAC1B,MAAM,EACN,KAAK,EACL,QAAQ,GACR,EAAE,MAAM,GAAG,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CA0BzC;AAgCD;;;;;;GAMG;AACH,wBAAsB,OAAO,CAC5B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAmCf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/postgres/store.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EACX,UAAU,EACV,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/postgres/store.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EACX,UAAU,EACV,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,MAAM,mBAAmB,CAAC;AAM3B,OAAO,KAAK,EAAkB,yBAAyB,EAAE,MAAM,WAAW,CAAC;AAK3E,YAAY,EACX,cAAc,EACd,iBAAiB,EACjB,yBAAyB,GACzB,MAAM,WAAW,CAAC;AAKnB;;;;;;;;GAQG;AACH,qBAAa,kBAAmB,YAAW,UAAU;;IASpD,OAAO;IAQP,gGAAgG;IAChG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,yBAAyB,GAAG,kBAAkB;IAInE;;;;OAIG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAaxB,mGAAmG;IAC7F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsBtB,GAAG,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,SAAS,CAAC;IAiCrE,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAM/C,IAAI,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAMtD,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAKxB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAQxD,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAW5D,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAQrE,QAAQ,CACb,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,aAAa,GACnB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IA0B3B,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAUpE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;CAMxD"}
|
package/dist/queue/deliver.d.ts
CHANGED
|
@@ -21,7 +21,9 @@ export interface DeliveryContext extends NotifyContext {
|
|
|
21
21
|
* Delivers a claimed item: one session per recipient domain, at most
|
|
22
22
|
* `perDomain` at once to each — none, and no slot taken, when its sender
|
|
23
23
|
* is one `sendMail` would refuse — then the outcome recorded — which lets go
|
|
24
|
-
* of the lease — then the events and the DSNs.
|
|
24
|
+
* of the lease — then the events and the DSNs. An item whose message the
|
|
25
|
+
* store no longer gives fails every pending recipient, said on the `error`
|
|
26
|
+
* event, its DSN without the original.
|
|
25
27
|
*/
|
|
26
28
|
export declare function deliverItem(ctx: DeliveryContext, item: QueueItem): Promise<void>;
|
|
27
29
|
//# sourceMappingURL=deliver.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deliver.d.ts","sourceRoot":"","sources":["../../src/queue/deliver.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,mBAAmB,CAAC;AAGpE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,KAAK,aAAa,EAAU,MAAM,UAAU,CAAC;AAKtD,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACrD,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC;IACjC,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;CAClE;
|
|
1
|
+
{"version":3,"file":"deliver.d.ts","sourceRoot":"","sources":["../../src/queue/deliver.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,mBAAmB,CAAC;AAGpE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,KAAK,aAAa,EAAU,MAAM,UAAU,CAAC;AAKtD,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACrD,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC;IACjC,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;CAClE;AA8HD;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAChC,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,SAAS,GACb,OAAO,CAAC,IAAI,CAAC,CA+Bf"}
|
package/dist/queue/notify.d.ts
CHANGED
|
@@ -13,7 +13,8 @@ export interface NotifyContext {
|
|
|
13
13
|
* recipients, from the null sender, so it never causes one of its own.
|
|
14
14
|
* Nothing for an item from the null sender (RFC 5321 §4.5.5, RFC 3464
|
|
15
15
|
* §2: a DSN is never sent about a DSN). It bypasses `maxItems`: a full
|
|
16
|
-
* queue loses no bounce. A failure goes to the `error` event.
|
|
16
|
+
* queue loses no bounce. A failure goes to the `error` event. With no
|
|
17
|
+
* `message` — the store lost it — the DSN returns nothing of it.
|
|
17
18
|
*/
|
|
18
|
-
export declare function notify(ctx: NotifyContext, item: QueueItem, message: Uint8Array, kind: DsnKind, recipients: readonly RecipientUpdate[], now: number): Promise<void>;
|
|
19
|
+
export declare function notify(ctx: NotifyContext, item: QueueItem, message: Uint8Array | undefined, kind: DsnKind, recipients: readonly RecipientUpdate[], now: number): Promise<void>;
|
|
19
20
|
//# sourceMappingURL=notify.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/queue/notify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAY,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB;AAED
|
|
1
|
+
{"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/queue/notify.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAY,KAAK,OAAO,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,wBAAsB,MAAM,CAC3B,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,UAAU,GAAG,SAAS,EAC/B,IAAI,EAAE,OAAO,EACb,UAAU,EAAE,SAAS,eAAe,EAAE,EACtC,GAAG,EAAE,MAAM,GACT,OAAO,CAAC,IAAI,CAAC,CAwCf"}
|
package/dist/redis/connect.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ export interface Connection {
|
|
|
18
18
|
readonly client: RedisQueueClient;
|
|
19
19
|
readonly owned: boolean;
|
|
20
20
|
readonly keys: Keys;
|
|
21
|
+
/** The URL's password, so an error that repeats it is masked; `''` for a given client. */
|
|
22
|
+
readonly password: string;
|
|
21
23
|
}
|
|
22
24
|
export declare function keysOf(value: unknown): Keys;
|
|
23
25
|
/** Checks the options and opens a client for a URL; connects to nothing yet. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../src/redis/connect.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAE1E,+DAA+D;AAC/D,MAAM,WAAW,IAAI;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kEAAkE;IAClE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,wFAAwF;IACxF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,iDAAiD;IACjD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,0BAA0B;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB;AAED,4EAA4E;AAC5E,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../src/redis/connect.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAE1E,+DAA+D;AAC/D,MAAM,WAAW,IAAI;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kEAAkE;IAClE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,wFAAwF;IACxF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,iDAAiD;IACjD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,0BAA0B;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB;AAED,4EAA4E;AAC5E,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,0FAA0F;IAC1F,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC1B;AAWD,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAe3C;AAiDD,gFAAgF;AAChF,wBAAgB,OAAO,CAAC,OAAO,EAAE,sBAAsB,GAAG,UAAU,CAkBnE"}
|
package/dist/redis/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
masked
|
|
3
|
-
} from "../chunks/index-
|
|
3
|
+
} from "../chunks/index-b4xvg3xn.js";
|
|
4
4
|
import {
|
|
5
5
|
QueueError2,
|
|
6
6
|
invalid,
|
|
@@ -72,9 +72,15 @@ function connect(options) {
|
|
|
72
72
|
if (client !== undefined) {
|
|
73
73
|
if (!isClient(client))
|
|
74
74
|
throw invalid(NEEDS);
|
|
75
|
-
return { client, owned: false, keys };
|
|
75
|
+
return { client, owned: false, keys, password: "" };
|
|
76
76
|
}
|
|
77
|
-
|
|
77
|
+
const parsed = urlOf(url);
|
|
78
|
+
return {
|
|
79
|
+
client: clientFor(parsed),
|
|
80
|
+
owned: true,
|
|
81
|
+
keys,
|
|
82
|
+
password: parsed.password
|
|
83
|
+
};
|
|
78
84
|
}
|
|
79
85
|
|
|
80
86
|
// src/redis/items.ts
|
|
@@ -279,12 +285,14 @@ class RedisQueueStore {
|
|
|
279
285
|
#client;
|
|
280
286
|
#owned;
|
|
281
287
|
#keys;
|
|
288
|
+
#password;
|
|
282
289
|
#ready;
|
|
283
290
|
#closed = false;
|
|
284
|
-
constructor({ client, owned, keys }) {
|
|
291
|
+
constructor({ client, owned, keys, password }) {
|
|
285
292
|
this.#client = client;
|
|
286
293
|
this.#owned = owned;
|
|
287
294
|
this.#keys = keys;
|
|
295
|
+
this.#password = password;
|
|
288
296
|
}
|
|
289
297
|
static open(options) {
|
|
290
298
|
return new RedisQueueStore(connect(options));
|
|
@@ -302,7 +310,7 @@ class RedisQueueStore {
|
|
|
302
310
|
if (error instanceof QueueError2)
|
|
303
311
|
throw error;
|
|
304
312
|
const reason = error instanceof Error ? error.message : String(error);
|
|
305
|
-
throw new QueueError2("INVALID", `The Redis queue cannot be set up: ${reason}`);
|
|
313
|
+
throw new QueueError2("INVALID", `The Redis queue cannot be set up: ${masked(reason, this.#password)}`);
|
|
306
314
|
});
|
|
307
315
|
return this.#ready;
|
|
308
316
|
}
|
|
@@ -426,5 +434,5 @@ export {
|
|
|
426
434
|
RedisQueueStore
|
|
427
435
|
};
|
|
428
436
|
|
|
429
|
-
//# debugId=
|
|
437
|
+
//# debugId=63554783C4CC726464756E2164756E21
|
|
430
438
|
//# sourceMappingURL=index.js.map
|
package/dist/redis/index.js.map
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/redis/connect.ts", "../src/redis/items.ts", "../src/redis/scripts.ts", "../src/redis/run.ts", "../src/redis/store.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import { invalid } from '../errors';\nimport { masked } from '../masked';\nimport type { RedisQueueClient, RedisQueueStoreOptions } from './options';\n\n/** The keys of one queue, each its prefix and a fixed name. */\nexport interface Keys {\n\treadonly prefix: string;\n\t/** Every item, scored by its next attempt: `list` and `count`. */\n\treadonly items: string;\n\t/** The items no lease holds, scored by their next attempt: what a claim takes first. */\n\treadonly ready: string;\n\t/** The leased items, scored by when their leases expire. */\n\treadonly leases: string;\n\t/** The counter that orders items equally due. */\n\treadonly seq: string;\n\t/** The layout version. */\n\treadonly schema: string;\n}\n\n/** The client a store uses, and whether it opened it (and so closes it). */\nexport interface Connection {\n\treadonly client: RedisQueueClient;\n\treadonly owned: boolean;\n\treadonly keys: Keys;\n}\n\nconst DEFAULT_PREFIX = 'bumail:queue:';\n\n/**\n * Nothing Redis reads specially: no `{`, so no Cluster hash tag; no `*`,\n * `?`, `[` or `\\`, so a `SCAN MATCH` on the prefix matches its keys only;\n * no space or control character.\n */\nconst PREFIX = /^[a-z][a-z0-9_:.-]{0,39}$/;\n\nexport function keysOf(value: unknown): Keys {\n\tconst prefix = value ?? DEFAULT_PREFIX;\n\tif (typeof prefix !== 'string' || !PREFIX.test(prefix)) {\n\t\tthrow invalid(\n\t\t\t`keyPrefix must be lowercase letters, digits, '_', ':', '.' and '-', starting with a letter, at most 40 characters, not ${JSON.stringify(prefix)}`,\n\t\t);\n\t}\n\treturn {\n\t\tprefix,\n\t\titems: `${prefix}items`,\n\t\tready: `${prefix}ready`,\n\t\tleases: `${prefix}leases`,\n\t\tseq: `${prefix}seq`,\n\t\tschema: `${prefix}schema`,\n\t};\n}\n\nconst NEEDS =\n\t'A Redis queue store needs client, a Bun.RedisClient, or url, a redis:// URL';\n\n/** The schemes `Bun.RedisClient` takes. */\nconst SCHEMES = [\n\t'redis:',\n\t'rediss:',\n\t'valkey:',\n\t'valkeys:',\n\t'redis+tls:',\n\t'redis+unix:',\n\t'redis+tls+unix:',\n];\n\n/** A Redis URL; never echoed, since it may hold a password. */\nfunction urlOf(value: unknown): URL {\n\tif (typeof value !== 'string' && !(value instanceof URL))\n\t\tthrow invalid(NEEDS);\n\tconst url = URL.parse(String(value));\n\tif (url === null || !SCHEMES.includes(url.protocol)) throw invalid(NEEDS);\n\treturn url;\n}\n\n/**\n * A client of the store's own for the URL. `Bun.RedisClient` refuses\n * some URLs at once (a database that is not a number): that is\n * `INVALID`, its reason kept and the password, should it be repeated,\n * masked.\n */\nfunction clientFor(url: URL): RedisQueueClient {\n\ttry {\n\t\treturn new Bun.RedisClient(url.href) as unknown as RedisQueueClient;\n\t} catch (error) {\n\t\tconst reason = error instanceof Error ? error.message : String(error);\n\t\tthrow invalid(\n\t\t\t`The URL in url cannot be opened: ${masked(reason, url.password)}`,\n\t\t);\n\t}\n}\n\nconst isClient = (value: unknown): value is RedisQueueClient =>\n\ttypeof value === 'object' &&\n\tvalue !== null &&\n\ttypeof (value as RedisQueueClient).send === 'function' &&\n\ttypeof (value as RedisQueueClient).getBuffer === 'function' &&\n\ttypeof (value as RedisQueueClient).close === 'function';\n\n/** Checks the options and opens a client for a URL; connects to nothing yet. */\nexport function connect(options: RedisQueueStoreOptions): Connection {\n\tif (typeof options !== 'object' || options === null) throw invalid(NEEDS);\n\tconst keys = keysOf(options.keyPrefix);\n\tconst { client, url } = options;\n\tif (client !== undefined && url !== undefined) {\n\t\tthrow invalid('A Redis queue store takes client or url, not both');\n\t}\n\tif (client !== undefined) {\n\t\tif (!isClient(client)) throw invalid(NEEDS);\n\t\treturn { client, owned: false, keys };\n\t}\n\
|
|
5
|
+
"import { invalid } from '../errors';\nimport { masked } from '../masked';\nimport type { RedisQueueClient, RedisQueueStoreOptions } from './options';\n\n/** The keys of one queue, each its prefix and a fixed name. */\nexport interface Keys {\n\treadonly prefix: string;\n\t/** Every item, scored by its next attempt: `list` and `count`. */\n\treadonly items: string;\n\t/** The items no lease holds, scored by their next attempt: what a claim takes first. */\n\treadonly ready: string;\n\t/** The leased items, scored by when their leases expire. */\n\treadonly leases: string;\n\t/** The counter that orders items equally due. */\n\treadonly seq: string;\n\t/** The layout version. */\n\treadonly schema: string;\n}\n\n/** The client a store uses, and whether it opened it (and so closes it). */\nexport interface Connection {\n\treadonly client: RedisQueueClient;\n\treadonly owned: boolean;\n\treadonly keys: Keys;\n\t/** The URL's password, so an error that repeats it is masked; `''` for a given client. */\n\treadonly password: string;\n}\n\nconst DEFAULT_PREFIX = 'bumail:queue:';\n\n/**\n * Nothing Redis reads specially: no `{`, so no Cluster hash tag; no `*`,\n * `?`, `[` or `\\`, so a `SCAN MATCH` on the prefix matches its keys only;\n * no space or control character.\n */\nconst PREFIX = /^[a-z][a-z0-9_:.-]{0,39}$/;\n\nexport function keysOf(value: unknown): Keys {\n\tconst prefix = value ?? DEFAULT_PREFIX;\n\tif (typeof prefix !== 'string' || !PREFIX.test(prefix)) {\n\t\tthrow invalid(\n\t\t\t`keyPrefix must be lowercase letters, digits, '_', ':', '.' and '-', starting with a letter, at most 40 characters, not ${JSON.stringify(prefix)}`,\n\t\t);\n\t}\n\treturn {\n\t\tprefix,\n\t\titems: `${prefix}items`,\n\t\tready: `${prefix}ready`,\n\t\tleases: `${prefix}leases`,\n\t\tseq: `${prefix}seq`,\n\t\tschema: `${prefix}schema`,\n\t};\n}\n\nconst NEEDS =\n\t'A Redis queue store needs client, a Bun.RedisClient, or url, a redis:// URL';\n\n/** The schemes `Bun.RedisClient` takes. */\nconst SCHEMES = [\n\t'redis:',\n\t'rediss:',\n\t'valkey:',\n\t'valkeys:',\n\t'redis+tls:',\n\t'redis+unix:',\n\t'redis+tls+unix:',\n];\n\n/** A Redis URL; never echoed, since it may hold a password. */\nfunction urlOf(value: unknown): URL {\n\tif (typeof value !== 'string' && !(value instanceof URL))\n\t\tthrow invalid(NEEDS);\n\tconst url = URL.parse(String(value));\n\tif (url === null || !SCHEMES.includes(url.protocol)) throw invalid(NEEDS);\n\treturn url;\n}\n\n/**\n * A client of the store's own for the URL. `Bun.RedisClient` refuses\n * some URLs at once (a database that is not a number): that is\n * `INVALID`, its reason kept and the password, should it be repeated,\n * masked.\n */\nfunction clientFor(url: URL): RedisQueueClient {\n\ttry {\n\t\treturn new Bun.RedisClient(url.href) as unknown as RedisQueueClient;\n\t} catch (error) {\n\t\tconst reason = error instanceof Error ? error.message : String(error);\n\t\tthrow invalid(\n\t\t\t`The URL in url cannot be opened: ${masked(reason, url.password)}`,\n\t\t);\n\t}\n}\n\nconst isClient = (value: unknown): value is RedisQueueClient =>\n\ttypeof value === 'object' &&\n\tvalue !== null &&\n\ttypeof (value as RedisQueueClient).send === 'function' &&\n\ttypeof (value as RedisQueueClient).getBuffer === 'function' &&\n\ttypeof (value as RedisQueueClient).close === 'function';\n\n/** Checks the options and opens a client for a URL; connects to nothing yet. */\nexport function connect(options: RedisQueueStoreOptions): Connection {\n\tif (typeof options !== 'object' || options === null) throw invalid(NEEDS);\n\tconst keys = keysOf(options.keyPrefix);\n\tconst { client, url } = options;\n\tif (client !== undefined && url !== undefined) {\n\t\tthrow invalid('A Redis queue store takes client or url, not both');\n\t}\n\tif (client !== undefined) {\n\t\tif (!isClient(client)) throw invalid(NEEDS);\n\t\treturn { client, owned: false, keys, password: '' };\n\t}\n\tconst parsed = urlOf(url);\n\treturn {\n\t\tclient: clientFor(parsed),\n\t\towned: true,\n\t\tkeys,\n\t\tpassword: parsed.password,\n\t};\n}\n",
|
|
6
6
|
"import { isDone } from '../contract/checks';\nimport type { QueueItem, RecipientState } from '../contract/types';\n\n/** An item's hash, as `HGETALL` gives it: a map, or a script's flat list of names and values. */\nexport type Fields = Readonly<Record<string, string>>;\n\n/** A script's `HGETALL`, `[name, value, …]`, as a map; a map as it is. */\nexport function fieldsOf(reply: unknown): Fields | undefined {\n\tif (Array.isArray(reply)) {\n\t\tconst fields: Record<string, string> = {};\n\t\tfor (let i = 0; i + 1 < reply.length; i += 2) {\n\t\t\tfields[String(reply[i])] = String(reply[i + 1]);\n\t\t}\n\t\treturn fieldsOf(fields);\n\t}\n\tif (typeof reply !== 'object' || reply === null) return undefined;\n\t// No id: no item, whatever else a hash left half written holds.\n\treturn (reply as Fields)['id'] === undefined ? undefined : (reply as Fields);\n}\n\nconst field = (fields: Fields, name: string): string => fields[name] ?? '';\n\n/** The item a hash holds. Times are kept as JavaScript wrote them, so each reads back exactly. */\nexport function itemOf(fields: Fields): QueueItem {\n\tconst item: QueueItem = {\n\t\tid: field(fields, 'id'),\n\t\tfrom: field(fields, 'from'),\n\t\trecipients: JSON.parse(field(fields, 'recipients')) as RecipientState[],\n\t\tsize: Number(field(fields, 'size')),\n\t\tcreatedAt: Number(field(fields, 'created')),\n\t\tnextAttemptAt: Number(field(fields, 'next')),\n\t\tattempts: Number(field(fields, 'attempts')),\n\t\tdelayNotified: field(fields, 'delay') === '1',\n\t};\n\tconst owner = fields['owner'];\n\tconst expires = fields['expires'];\n\tif (owner === undefined || expires === undefined) return item;\n\treturn { ...item, lease: { owner, expiresAt: Number(expires) } };\n}\n\n/**\n * The ids a store makes, `crypto.randomUUID()`'s. Any other id is one no\n * item has, so no id a caller gives ever reaches a key but an item's.\n */\nconst ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;\n\nexport const isId = (id: unknown): id is string =>\n\ttypeof id === 'string' && ID.test(id);\n\n/** A time as a script takes it: the string JavaScript writes, which reads back exactly. */\nexport const time = (value: number) => String(value);\n\n/** What `COMPLETE` records of an attempt: the item dropped, or its new state. */\nexport const outcomeOf = (item: QueueItem): string[] =>\n\tisDone(item)\n\t\t? ['drop']\n\t\t: [\n\t\t\t\t'record',\n\t\t\t\tJSON.stringify(item.recipients),\n\t\t\t\ttime(item.nextAttemptAt),\n\t\t\t\t`${item.attempts}`,\n\t\t\t\titem.delayNotified ? '1' : '0',\n\t\t\t];\n",
|
|
7
7
|
"/**\n * The Lua scripts a Redis store runs: each operation that writes is one\n * script, so Redis runs it whole, with no other command between its\n * steps. The fixed keys come as `KEYS`; an item's own keys are its\n * prefix (`ARGV[1]`), `item:` or `message:`, and its id.\n *\n * An item is a hash, `<prefix>item:<id>`; its message a string,\n * `<prefix>message:<id>`, written from the bytes given, as they are. Its\n * member in the sorted sets is its sequence number, sixteen digits, `:`,\n * and its id: Redis orders members of one score by their bytes, so items\n * equally due come oldest first. Times travel as the strings JavaScript\n * writes for them and are only compared here (`tonumber`), never written\n * back from Lua, so none loses a digit.\n */\n\n/** What every script starts with. */\nconst PRELUDE = `\nlocal prefix = ARGV[1]\nlocal function itemKey(member) return prefix .. 'item:' .. string.sub(member, 18) end\nlocal function seqOf(member) return tonumber(string.sub(member, 1, 16)) end\n`;\n\n/** For CLAIM: when an item's due time, or false when its hash is gone (evicted, deleted by hand). */\nconst NEXT_OF = `\nlocal function nextOf(member)\n\tlocal found = redis.call('HGET', itemKey(member), 'next')\n\tif found then return tonumber(found) end\n\t-- A member whose hash is gone is dropped, never leased.\n\tredis.call('ZREM', KEYS[1], member)\n\tredis.call('ZREM', KEYS[2], member)\n\tredis.call('ZREM', KEYS[3], member)\n\tredis.call('DEL', prefix .. 'message:' .. string.sub(member, 18))\n\treturn false\nend\n`;\n\n/** A script, and the SHA-1 `EVALSHA` names it by. */\nexport interface Script {\n\treadonly source: string;\n\treadonly sha: string;\n}\n\nconst script = (body: string): Script => {\n\tconst source = PRELUDE + body;\n\tconst sha = new Bun.CryptoHasher('sha1').update(source).digest('hex');\n\treturn { source, sha };\n};\n\n/**\n * KEYS schema; ARGV prefix, version. Writes the version on a new queue;\n * returns the one found.\n */\nexport const SCHEMA = script(`\nlocal found = redis.call('GET', KEYS[1])\nif found then return found end\nredis.call('SET', KEYS[1], ARGV[2])\nreturn ARGV[2]\n`);\n\n/**\n * KEYS items, ready, seq; ARGV prefix, id, from, recipients, size,\n * created, message, max (or ''). `{'full', n}` when `max` items are\n * there already, else `{'ok'}`.\n */\nexport const ADD = script(`\nlocal max = tonumber(ARGV[8])\nif max then\n\tlocal n = redis.call('ZCARD', KEYS[1])\n\tif n >= max then return {'full', tostring(n)} end\nend\nlocal member = string.format('%016d', redis.call('INCR', KEYS[3])) .. ':' .. ARGV[2]\nredis.call('HSET', prefix .. 'item:' .. ARGV[2], 'id', ARGV[2], 'from', ARGV[3],\n\t'recipients', ARGV[4], 'size', ARGV[5], 'created', ARGV[6], 'next', ARGV[6],\n\t'attempts', '0', 'delay', '0', 'rev', '0', 'member', member)\nredis.call('SET', prefix .. 'message:' .. ARGV[2], ARGV[7])\nredis.call('ZADD', KEYS[1], ARGV[6], member)\nredis.call('ZADD', KEYS[2], ARGV[6], member)\nreturn {'ok'}\n`);\n\n/**\n * KEYS ready, leases, items; ARGV prefix, owner, now, expiry. The\n * earliest due, then the oldest, of the items no lease holds and of those\n * whose lease expired, leased to the owner; its fields, or false. The\n * expired leases are read whole: there are as many as the leases a\n * crashed or stalled worker left behind, and the leased items an admin\n * moved later, until they are due.\n */\nexport const CLAIM = script(`${NEXT_OF}\nlocal now = tonumber(ARGV[3])\nlocal best, bestNext\nwhile best == nil do\n\tlocal first = redis.call('ZRANGEBYSCORE', KEYS[1], '-inf', ARGV[3], 'LIMIT', 0, 1)[1]\n\tif not first then break end\n\tlocal nextAt = nextOf(first)\n\tif nextAt then\n\t\tbest = first\n\t\tbestNext = nextAt\n\tend\nend\nfor _, member in ipairs(redis.call('ZRANGEBYSCORE', KEYS[2], '-inf', ARGV[3])) do\n\tlocal nextAt = nextOf(member)\n\tif nextAt and nextAt <= now and (best == nil or nextAt < bestNext\n\t\tor (nextAt == bestNext and seqOf(member) < seqOf(best))) then\n\t\tbest = member\n\t\tbestNext = nextAt\n\tend\nend\nif best == nil then return false end\nredis.call('ZREM', KEYS[1], best)\nredis.call('ZADD', KEYS[2], ARGV[4], best)\nredis.call('HSET', itemKey(best), 'owner', ARGV[2], 'expires', ARGV[4])\nreturn redis.call('HGETALL', itemKey(best))\n`);\n\n/** KEYS leases; ARGV prefix, id, owner, expiry. 1 while the owner holds the lease, else 0. */\nexport const RENEW = script(`\nlocal key = prefix .. 'item:' .. ARGV[2]\nif redis.call('HGET', key, 'owner') ~= ARGV[3] then return 0 end\nredis.call('HSET', key, 'expires', ARGV[4])\nredis.call('ZADD', KEYS[1], ARGV[4], redis.call('HGET', key, 'member'))\nreturn 1\n`);\n\n/**\n * KEYS items, ready, leases; ARGV prefix, id, owner, rev, then 'drop',\n * or 'record' and recipients, next, attempts, delay. 0 when the owner\n * does not hold the lease; -1 when another outcome was recorded since\n * the item was read at `rev`, to read it again; 1 once recorded, the\n * lease let go of, or the item and its message dropped.\n */\nexport const COMPLETE = script(`\nlocal key = prefix .. 'item:' .. ARGV[2]\nif redis.call('HGET', key, 'owner') ~= ARGV[3] then return 0 end\nif redis.call('HGET', key, 'rev') ~= ARGV[4] then return -1 end\nlocal member = redis.call('HGET', key, 'member')\nredis.call('ZREM', KEYS[3], member)\nif ARGV[5] == 'drop' then\n\tredis.call('ZREM', KEYS[1], member)\n\tredis.call('ZREM', KEYS[2], member)\n\tredis.call('DEL', key, prefix .. 'message:' .. ARGV[2])\n\treturn 1\nend\nredis.call('HSET', key, 'recipients', ARGV[6], 'next', ARGV[7],\n\t'attempts', ARGV[8], 'delay', ARGV[9])\nredis.call('HINCRBY', key, 'rev', 1)\nredis.call('HDEL', key, 'owner', 'expires')\nredis.call('ZADD', KEYS[1], ARGV[7], member)\nredis.call('ZADD', KEYS[2], ARGV[7], member)\nreturn 1\n`);\n\n/**\n * KEYS items, ready, leases; ARGV prefix, id, at, owner (or ''). With an\n * owner, only while it holds the lease, which it lets go of; without,\n * the lease stays. 1 when moved, else 0.\n */\nexport const RESCHEDULE = script(`\nlocal key = prefix .. 'item:' .. ARGV[2]\nlocal member = redis.call('HGET', key, 'member')\nif not member then return 0 end\nlocal owner = redis.call('HGET', key, 'owner')\nif ARGV[4] ~= '' then\n\tif owner ~= ARGV[4] then return 0 end\n\tredis.call('HDEL', key, 'owner', 'expires')\n\tredis.call('ZREM', KEYS[3], member)\n\towner = false\nend\nredis.call('HSET', key, 'next', ARGV[3])\nredis.call('ZADD', KEYS[1], ARGV[3], member)\nif not owner then redis.call('ZADD', KEYS[2], ARGV[3], member) end\nreturn 1\n`);\n\n/** KEYS items, ready, leases; ARGV prefix, id. The item's fields as it stood, or false; dropped with its message. */\nexport const CANCEL = script(`\nlocal key = prefix .. 'item:' .. ARGV[2]\nlocal fields = redis.call('HGETALL', key)\nif #fields == 0 then return false end\nlocal member = redis.call('HGET', key, 'member')\nredis.call('ZREM', KEYS[1], member)\nredis.call('ZREM', KEYS[2], member)\nredis.call('ZREM', KEYS[3], member)\nredis.call('DEL', key, prefix .. 'message:' .. ARGV[2])\nreturn fields\n`);\n\n/** KEYS items; ARGV prefix, first, last. A page of items, each its fields, in one read. */\nexport const LIST = script(`\nlocal out = {}\nfor _, member in ipairs(redis.call('ZRANGE', KEYS[1], ARGV[2], ARGV[3])) do\n\tout[#out + 1] = redis.call('HGETALL', itemKey(member))\nend\nreturn out\n`);\n",
|
|
8
8
|
"import { QueueError } from '../errors';\nimport type { Keys } from './connect';\nimport type { RedisQueueClient } from './options';\nimport { SCHEMA, type Script } from './scripts';\n\n/** Redis no longer has the script: restarted, failed over, or `SCRIPT FLUSH`ed. */\nconst isNoScript = (error: unknown) =>\n\terror instanceof Error && error.message.startsWith('NOSCRIPT');\n\n/**\n * Runs a script by its SHA-1 (`EVALSHA`), or, when Redis does not have\n * it, by its source (`EVAL`), which also keeps it for the next call.\n * Either way Redis runs it whole.\n */\nexport async function run(\n\tclient: RedisQueueClient,\n\tscript: Script,\n\tkeys: string[],\n\targs: (string | Uint8Array)[],\n): Promise<unknown> {\n\tconst rest = [`${keys.length}`, ...keys, ...args];\n\ttry {\n\t\treturn await client.send('EVALSHA', [script.sha, ...rest]);\n\t} catch (error) {\n\t\tif (!isNoScript(error)) throw error;\n\t\treturn client.send('EVAL', [script.source, ...rest]);\n\t}\n}\n\n/** The layout of the keys; a change is a new version, and a newer one is refused. */\nconst LAYOUT = 1;\n\n/** Reads the layout version, writing it on a new queue; a newer one is `INVALID`. */\nexport async function checkLayout(\n\tclient: RedisQueueClient,\n\tkeys: Keys,\n): Promise<void> {\n\tconst found = Number(\n\t\tawait run(client, SCHEMA, [keys.schema], [keys.prefix, `${LAYOUT}`]),\n\t);\n\tif (!Number.isInteger(found) || found < 1) {\n\t\tthrow new QueueError(\n\t\t\t'INVALID',\n\t\t\t`The key ${keys.schema} does not hold a layout version: is the prefix another application's?`,\n\t\t);\n\t}\n\tif (found > LAYOUT) {\n\t\tthrow new QueueError(\n\t\t\t'INVALID',\n\t\t\t`The database is at schema version ${found}, newer than this store's ${LAYOUT}`,\n\t\t);\n\t}\n}\n",
|
|
9
|
-
"import {\n\tapplyAttempt,\n\tcheckClaim,\n\tcheckList,\n\tcheckMaxItems,\n\tcheckNewItem,\n\tcheckOwner,\n\tcheckResult,\n\tcheckTime,\n\tnewItem,\n} from '../contract/checks';\nimport type { QueueStore } from '../contract/queue-store';\nimport type {\n\tAddOptions,\n\tAttemptResult,\n\tClaimRequest,\n\tNewQueueItem,\n\tQueueItem,\n\tQueueListOptions,\n} from '../contract/types';\nimport { QueueError } from '../errors';\nimport { isStorable } from '../text';\nimport { type Connection, connect, type Keys } from './connect';\nimport { fieldsOf, isId, itemOf, outcomeOf, time } from './items';\nimport type { RedisQueueClient, RedisQueueStoreOptions } from './options';\nimport { checkLayout, run } from './run';\nimport {\n\tADD,\n\tCANCEL,\n\tCLAIM,\n\tCOMPLETE,\n\tLIST,\n\tRENEW,\n\tRESCHEDULE,\n\ttype Script,\n} from './scripts';\n\n/**\n * A `QueueStore` on Redis through Bun's own `Bun.RedisClient`: several\n * instances, on one machine or many, share one queue. Every operation\n * that writes is one Lua script, which Redis runs whole, so two\n * instances never take the same item. The due items are in sorted sets,\n * each item a hash, its message a string written byte for byte. One\n * Redis, or a primary with replicas — not Redis Cluster.\n */\nexport class RedisQueueStore implements QueueStore {\n\treadonly #client: RedisQueueClient;\n\treadonly #owned: boolean;\n\treadonly #keys: Keys;\n\t#ready: Promise<void> | undefined;\n\t#closed = false;\n\n\tprivate constructor({ client, owned, keys }: Connection) {\n\t\tthis.#client = client;\n\t\tthis.#owned = owned;\n\t\tthis.#keys = keys;\n\t}\n\n\t/** Checks the options; connects to nothing until the first call. What is wrong is `INVALID`. */\n\tstatic open(options: RedisQueueStoreOptions): RedisQueueStore {\n\t\treturn new RedisQueueStore(connect(options));\n\t}\n\n\t/** Closes the client the store opened for a URL, never one it was given; closing twice is fine. */\n\tasync close(): Promise<void> {\n\t\tif (this.#closed) return;\n\t\tthis.#closed = true;\n\t\tif (this.#owned) await this.#client.close();\n\t}\n\n\t/**\n\t * Reads the layout version, writing it on a new queue; once per\n\t * store. A failure is `INVALID`, and the next call tries again.\n\t */\n\t#setUp(): Promise<void> {\n\t\tthis.#ready ??= checkLayout(this.#client, this.#keys).catch((error) => {\n\t\t\tthis.#ready = undefined;\n\t\t\tif (error instanceof QueueError) throw error;\n\t\t\tconst reason = error instanceof Error ? error.message : String(error);\n\t\t\tthrow new QueueError(\n\t\t\t\t'INVALID',\n\t\t\t\t`The Redis queue cannot be set up: ${reason}`,\n\t\t\t);\n\t\t});\n\t\treturn this.#ready;\n\t}\n\n\t/** The client, once the layout is checked; `CLOSED` after `close()`. */\n\tasync #redis(): Promise<RedisQueueClient> {\n\t\tif (this.#closed) throw closed();\n\t\tawait this.#setUp();\n\t\treturn this.#client;\n\t}\n\n\tasync #run(script: Script, keys: string[], args: (string | Uint8Array)[]) {\n\t\tconst client = await this.#redis();\n\t\treturn run(client, script, keys, [this.#keys.prefix, ...args]);\n\t}\n\n\tasync add(item: NewQueueItem, options: AddOptions = {}): Promise<QueueItem> {\n\t\tcheckNewItem(item);\n\t\tconst max = checkMaxItems(options.maxItems);\n\t\tconst added = newItem(crypto.randomUUID(), item);\n\t\tconst k = this.#keys;\n\t\tconst reply = (await this.#run(\n\t\t\tADD,\n\t\t\t[k.items, k.ready, k.seq],\n\t\t\t[\n\t\t\t\tadded.id,\n\t\t\t\tadded.from,\n\t\t\t\tJSON.stringify(added.recipients),\n\t\t\t\t`${added.size}`,\n\t\t\t\ttime(added.createdAt),\n\t\t\t\titem.message,\n\t\t\t\tmax === undefined ? '' : `${max}`,\n\t\t\t],\n\t\t)) as string[];\n\t\tif (reply[0] === 'full') {\n\t\t\tthrow new QueueError(\n\t\t\t\t'QUEUE_FULL',\n\t\t\t\t`The queue holds ${reply[1]} items, its limit`,\n\t\t\t);\n\t\t}\n\t\treturn added;\n\t}\n\n\tasync get(id: string): Promise<QueueItem | undefined> {\n\t\tif (!isId(id)) return undefined;\n\t\tconst client = await this.#redis();\n\t\tconst fields = fieldsOf(\n\t\t\tawait client.send('HGETALL', [`${this.#keys.prefix}item:${id}`]),\n\t\t);\n\t\treturn fields ? itemOf(fields) : undefined;\n\t}\n\n\tasync list(options?: QueueListOptions): Promise<QueueItem[]> {\n\t\tconst { offset, limit } = checkList(options);\n\t\tconst pages = (await this.#run(\n\t\t\tLIST,\n\t\t\t[this.#keys.items],\n\t\t\t[`${offset}`, `${offset + limit - 1}`],\n\t\t)) as unknown[];\n\t\treturn pages.flatMap((reply) => {\n\t\t\tconst fields = fieldsOf(reply);\n\t\t\treturn fields ? [itemOf(fields)] : [];\n\t\t});\n\t}\n\n\tasync count(): Promise<number> {\n\t\tconst client = await this.#redis();\n\t\treturn Number(await client.send('ZCARD', [this.#keys.items]));\n\t}\n\n\tasync readMessage(id: string): Promise<Uint8Array | undefined> {\n\t\tif (!isId(id)) return undefined;\n\t\tconst client = await this.#redis();\n\t\tconst bytes = await client.getBuffer(`${this.#keys.prefix}message:${id}`);\n\t\treturn bytes === null ? undefined : new Uint8Array(bytes);\n\t}\n\n\tasync claim(request: ClaimRequest): Promise<QueueItem | undefined> {\n\t\tcheckClaim(request);\n\t\tconst { owner, now, leaseMs } = request;\n\t\tconst k = this.#keys;\n\t\tconst fields = fieldsOf(\n\t\t\tawait this.#run(\n\t\t\t\tCLAIM,\n\t\t\t\t[k.ready, k.leases, k.items],\n\t\t\t\t[owner, time(now), time(now + leaseMs)],\n\t\t\t),\n\t\t);\n\t\treturn fields ? itemOf(fields) : undefined;\n\t}\n\n\tasync renew(id: string, owner: string, expiresAt: number): Promise<boolean> {\n\t\tcheckOwner(owner);\n\t\tcheckTime('expiresAt', expiresAt);\n\t\tif (!isId(id)) return false;\n\t\tconst k = this.#keys;\n\t\tconst done = await this.#run(\n\t\t\tRENEW,\n\t\t\t[k.leases],\n\t\t\t[id, owner, time(expiresAt)],\n\t\t);\n\t\treturn Number(done) === 1;\n\t}\n\n\tasync complete(\n\t\tid: string,\n\t\towner: string,\n\t\tresult: AttemptResult,\n\t): Promise<QueueItem | undefined> {\n\t\tcheckOwner(owner);\n\t\tcheckResult(result);\n\t\tif (!isId(id)) return undefined;\n\t\tconst k = this.#keys;\n\t\tconst client = await this.#redis();\n\t\tfor (let read = 0; read < MAX_READS; read++) {\n\t\t\tconst fields = fieldsOf(\n\t\t\t\tawait client.send('HGETALL', [`${k.prefix}item:${id}`]),\n\t\t\t);\n\t\t\tif (!fields || fields['owner'] !== owner) return undefined;\n\t\t\tconst rev = fields['rev']; // none: damaged by hand, nothing recorded\n\t\t\tif (rev === undefined) return undefined;\n\t\t\tconst item = applyAttempt(itemOf(fields), result);\n\t\t\tconst done = Number(\n\t\t\t\tawait this.#run(\n\t\t\t\t\tCOMPLETE,\n\t\t\t\t\t[k.items, k.ready, k.leases],\n\t\t\t\t\t[id, owner, rev, ...outcomeOf(item)],\n\t\t\t\t),\n\t\t\t);\n\t\t\tif (done === 1) return item;\n\t\t\tif (done === 0) return undefined;\n\t\t}\n\t\treturn undefined; // outrun MAX_READS times: told as a lease lost\n\t}\n\n\tasync reschedule(id: string, at: number, owner?: string): Promise<boolean> {\n\t\tcheckTime('at', at);\n\t\t// As PostgreSQL: an id no store could hold is unknown before the owner is checked.\n\t\tif (!isStorable(id)) return false;\n\t\tif (owner !== undefined) checkOwner(owner);\n\t\tif (!isId(id)) return false;\n\t\tconst k = this.#keys;\n\t\tconst done = await this.#run(\n\t\t\tRESCHEDULE,\n\t\t\t[k.items, k.ready, k.leases],\n\t\t\t[id, time(at), owner ?? ''],\n\t\t);\n\t\treturn Number(done) === 1;\n\t}\n\n\tasync cancel(id: string): Promise<QueueItem | undefined> {\n\t\tif (!isId(id)) return undefined;\n\t\tconst k = this.#keys;\n\t\tconst fields = fieldsOf(\n\t\t\tawait this.#run(CANCEL, [k.items, k.ready, k.leases], [id]),\n\t\t);\n\t\treturn fields ? itemOf(fields) : undefined;\n\t}\n}\n\n/** How often `complete` reads an item, another outcome recorded since each read. */\nconst MAX_READS = 8;\n\nconst closed = () => new QueueError('CLOSED', 'The queue store is closed');\n"
|
|
9
|
+
"import {\n\tapplyAttempt,\n\tcheckClaim,\n\tcheckList,\n\tcheckMaxItems,\n\tcheckNewItem,\n\tcheckOwner,\n\tcheckResult,\n\tcheckTime,\n\tnewItem,\n} from '../contract/checks';\nimport type { QueueStore } from '../contract/queue-store';\nimport type {\n\tAddOptions,\n\tAttemptResult,\n\tClaimRequest,\n\tNewQueueItem,\n\tQueueItem,\n\tQueueListOptions,\n} from '../contract/types';\nimport { QueueError } from '../errors';\nimport { masked } from '../masked';\nimport { isStorable } from '../text';\nimport { type Connection, connect, type Keys } from './connect';\nimport { fieldsOf, isId, itemOf, outcomeOf, time } from './items';\nimport type { RedisQueueClient, RedisQueueStoreOptions } from './options';\nimport { checkLayout, run } from './run';\nimport {\n\tADD,\n\tCANCEL,\n\tCLAIM,\n\tCOMPLETE,\n\tLIST,\n\tRENEW,\n\tRESCHEDULE,\n\ttype Script,\n} from './scripts';\n\n/**\n * A `QueueStore` on Redis through Bun's own `Bun.RedisClient`: several\n * instances, on one machine or many, share one queue. Every operation\n * that writes is one Lua script, which Redis runs whole, so two\n * instances never take the same item. The due items are in sorted sets,\n * each item a hash, its message a string written byte for byte. One\n * Redis, or a primary with replicas — not Redis Cluster.\n */\nexport class RedisQueueStore implements QueueStore {\n\treadonly #client: RedisQueueClient;\n\treadonly #owned: boolean;\n\treadonly #keys: Keys;\n\treadonly #password: string;\n\t#ready: Promise<void> | undefined;\n\t#closed = false;\n\n\tprivate constructor({ client, owned, keys, password }: Connection) {\n\t\tthis.#client = client;\n\t\tthis.#owned = owned;\n\t\tthis.#keys = keys;\n\t\tthis.#password = password;\n\t}\n\n\t/** Checks the options; connects to nothing until the first call. What is wrong is `INVALID`. */\n\tstatic open(options: RedisQueueStoreOptions): RedisQueueStore {\n\t\treturn new RedisQueueStore(connect(options));\n\t}\n\n\t/** Closes the client the store opened for a URL, never one it was given; closing twice is fine. */\n\tasync close(): Promise<void> {\n\t\tif (this.#closed) return;\n\t\tthis.#closed = true;\n\t\tif (this.#owned) await this.#client.close();\n\t}\n\n\t/**\n\t * Reads the layout version, writing it on a new queue; once per\n\t * store. A failure is `INVALID`, and the next call tries again.\n\t */\n\t#setUp(): Promise<void> {\n\t\tthis.#ready ??= checkLayout(this.#client, this.#keys).catch((error) => {\n\t\t\tthis.#ready = undefined;\n\t\t\tif (error instanceof QueueError) throw error;\n\t\t\tconst reason = error instanceof Error ? error.message : String(error);\n\t\t\tthrow new QueueError(\n\t\t\t\t'INVALID',\n\t\t\t\t`The Redis queue cannot be set up: ${masked(reason, this.#password)}`,\n\t\t\t);\n\t\t});\n\t\treturn this.#ready;\n\t}\n\n\t/** The client, once the layout is checked; `CLOSED` after `close()`. */\n\tasync #redis(): Promise<RedisQueueClient> {\n\t\tif (this.#closed) throw closed();\n\t\tawait this.#setUp();\n\t\treturn this.#client;\n\t}\n\n\tasync #run(script: Script, keys: string[], args: (string | Uint8Array)[]) {\n\t\tconst client = await this.#redis();\n\t\treturn run(client, script, keys, [this.#keys.prefix, ...args]);\n\t}\n\n\tasync add(item: NewQueueItem, options: AddOptions = {}): Promise<QueueItem> {\n\t\tcheckNewItem(item);\n\t\tconst max = checkMaxItems(options.maxItems);\n\t\tconst added = newItem(crypto.randomUUID(), item);\n\t\tconst k = this.#keys;\n\t\tconst reply = (await this.#run(\n\t\t\tADD,\n\t\t\t[k.items, k.ready, k.seq],\n\t\t\t[\n\t\t\t\tadded.id,\n\t\t\t\tadded.from,\n\t\t\t\tJSON.stringify(added.recipients),\n\t\t\t\t`${added.size}`,\n\t\t\t\ttime(added.createdAt),\n\t\t\t\titem.message,\n\t\t\t\tmax === undefined ? '' : `${max}`,\n\t\t\t],\n\t\t)) as string[];\n\t\tif (reply[0] === 'full') {\n\t\t\tthrow new QueueError(\n\t\t\t\t'QUEUE_FULL',\n\t\t\t\t`The queue holds ${reply[1]} items, its limit`,\n\t\t\t);\n\t\t}\n\t\treturn added;\n\t}\n\n\tasync get(id: string): Promise<QueueItem | undefined> {\n\t\tif (!isId(id)) return undefined;\n\t\tconst client = await this.#redis();\n\t\tconst fields = fieldsOf(\n\t\t\tawait client.send('HGETALL', [`${this.#keys.prefix}item:${id}`]),\n\t\t);\n\t\treturn fields ? itemOf(fields) : undefined;\n\t}\n\n\tasync list(options?: QueueListOptions): Promise<QueueItem[]> {\n\t\tconst { offset, limit } = checkList(options);\n\t\tconst pages = (await this.#run(\n\t\t\tLIST,\n\t\t\t[this.#keys.items],\n\t\t\t[`${offset}`, `${offset + limit - 1}`],\n\t\t)) as unknown[];\n\t\treturn pages.flatMap((reply) => {\n\t\t\tconst fields = fieldsOf(reply);\n\t\t\treturn fields ? [itemOf(fields)] : [];\n\t\t});\n\t}\n\n\tasync count(): Promise<number> {\n\t\tconst client = await this.#redis();\n\t\treturn Number(await client.send('ZCARD', [this.#keys.items]));\n\t}\n\n\tasync readMessage(id: string): Promise<Uint8Array | undefined> {\n\t\tif (!isId(id)) return undefined;\n\t\tconst client = await this.#redis();\n\t\tconst bytes = await client.getBuffer(`${this.#keys.prefix}message:${id}`);\n\t\treturn bytes === null ? undefined : new Uint8Array(bytes);\n\t}\n\n\tasync claim(request: ClaimRequest): Promise<QueueItem | undefined> {\n\t\tcheckClaim(request);\n\t\tconst { owner, now, leaseMs } = request;\n\t\tconst k = this.#keys;\n\t\tconst fields = fieldsOf(\n\t\t\tawait this.#run(\n\t\t\t\tCLAIM,\n\t\t\t\t[k.ready, k.leases, k.items],\n\t\t\t\t[owner, time(now), time(now + leaseMs)],\n\t\t\t),\n\t\t);\n\t\treturn fields ? itemOf(fields) : undefined;\n\t}\n\n\tasync renew(id: string, owner: string, expiresAt: number): Promise<boolean> {\n\t\tcheckOwner(owner);\n\t\tcheckTime('expiresAt', expiresAt);\n\t\tif (!isId(id)) return false;\n\t\tconst k = this.#keys;\n\t\tconst done = await this.#run(\n\t\t\tRENEW,\n\t\t\t[k.leases],\n\t\t\t[id, owner, time(expiresAt)],\n\t\t);\n\t\treturn Number(done) === 1;\n\t}\n\n\tasync complete(\n\t\tid: string,\n\t\towner: string,\n\t\tresult: AttemptResult,\n\t): Promise<QueueItem | undefined> {\n\t\tcheckOwner(owner);\n\t\tcheckResult(result);\n\t\tif (!isId(id)) return undefined;\n\t\tconst k = this.#keys;\n\t\tconst client = await this.#redis();\n\t\tfor (let read = 0; read < MAX_READS; read++) {\n\t\t\tconst fields = fieldsOf(\n\t\t\t\tawait client.send('HGETALL', [`${k.prefix}item:${id}`]),\n\t\t\t);\n\t\t\tif (!fields || fields['owner'] !== owner) return undefined;\n\t\t\tconst rev = fields['rev']; // none: damaged by hand, nothing recorded\n\t\t\tif (rev === undefined) return undefined;\n\t\t\tconst item = applyAttempt(itemOf(fields), result);\n\t\t\tconst done = Number(\n\t\t\t\tawait this.#run(\n\t\t\t\t\tCOMPLETE,\n\t\t\t\t\t[k.items, k.ready, k.leases],\n\t\t\t\t\t[id, owner, rev, ...outcomeOf(item)],\n\t\t\t\t),\n\t\t\t);\n\t\t\tif (done === 1) return item;\n\t\t\tif (done === 0) return undefined;\n\t\t}\n\t\treturn undefined; // outrun MAX_READS times: told as a lease lost\n\t}\n\n\tasync reschedule(id: string, at: number, owner?: string): Promise<boolean> {\n\t\tcheckTime('at', at);\n\t\t// As PostgreSQL: an id no store could hold is unknown before the owner is checked.\n\t\tif (!isStorable(id)) return false;\n\t\tif (owner !== undefined) checkOwner(owner);\n\t\tif (!isId(id)) return false;\n\t\tconst k = this.#keys;\n\t\tconst done = await this.#run(\n\t\t\tRESCHEDULE,\n\t\t\t[k.items, k.ready, k.leases],\n\t\t\t[id, time(at), owner ?? ''],\n\t\t);\n\t\treturn Number(done) === 1;\n\t}\n\n\tasync cancel(id: string): Promise<QueueItem | undefined> {\n\t\tif (!isId(id)) return undefined;\n\t\tconst k = this.#keys;\n\t\tconst fields = fieldsOf(\n\t\t\tawait this.#run(CANCEL, [k.items, k.ready, k.leases], [id]),\n\t\t);\n\t\treturn fields ? itemOf(fields) : undefined;\n\t}\n}\n\n/** How often `complete` reads an item, another outcome recorded since each read. */\nconst MAX_READS = 8;\n\nconst closed = () => new QueueError('CLOSED', 'The queue store is closed');\n"
|
|
10
10
|
],
|
|
11
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;
|
|
12
|
-
"debugId": "
|
|
11
|
+
"mappings": "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",
|
|
12
|
+
"debugId": "63554783C4CC726464756E2164756E21",
|
|
13
13
|
"names": []
|
|
14
14
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/redis/store.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EACX,UAAU,EACV,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/redis/store.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EACX,UAAU,EACV,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,MAAM,mBAAmB,CAAC;AAM3B,OAAO,KAAK,EAAoB,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAa1E;;;;;;;GAOG;AACH,qBAAa,eAAgB,YAAW,UAAU;;IAQjD,OAAO;IAOP,gGAAgG;IAChG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,sBAAsB,GAAG,eAAe;IAI7D,mGAAmG;IAC7F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAmCtB,GAAG,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,SAAS,CAAC;IA2BrE,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAS/C,IAAI,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAatD,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAKxB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC;IAOxD,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAc5D,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAarE,QAAQ,CACb,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,aAAa,GACnB,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IA2B3B,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAepE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;CAQxD"}
|