@bumail/queue 0.11.0 → 0.13.0
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 +47 -1
- package/dist/conformance/claims.d.ts +4 -0
- package/dist/conformance/claims.d.ts.map +1 -0
- package/dist/conformance/complete-dsns.d.ts +4 -0
- package/dist/conformance/complete-dsns.d.ts.map +1 -0
- package/dist/conformance/concurrency.d.ts +4 -0
- package/dist/conformance/concurrency.d.ts.map +1 -0
- package/dist/conformance/describe-queue-store.d.ts +11 -0
- package/dist/conformance/describe-queue-store.d.ts.map +1 -0
- package/dist/conformance/doubt.d.ts +5 -0
- package/dist/conformance/doubt.d.ts.map +1 -0
- package/dist/conformance/dsn.d.ts +4 -0
- package/dist/conformance/dsn.d.ts.map +1 -0
- package/dist/conformance/index.d.ts +3 -0
- package/dist/conformance/index.d.ts.map +1 -0
- package/dist/conformance/index.js +820 -0
- package/dist/conformance/index.js.map +18 -0
- package/dist/conformance/items.d.ts +4 -0
- package/dist/conformance/items.d.ts.map +1 -0
- package/dist/conformance/outcomes.d.ts +4 -0
- package/dist/conformance/outcomes.d.ts.map +1 -0
- package/dist/conformance/setup.d.ts +40 -0
- package/dist/conformance/setup.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +77 -8
- package/dist/index.js.map +10 -9
- package/dist/queue/attempt.d.ts.map +1 -1
- package/dist/queue/events.d.ts +7 -0
- package/dist/queue/events.d.ts.map +1 -1
- package/dist/queue/fallback-session.d.ts.map +1 -1
- package/dist/queue/options.d.ts +15 -1
- package/dist/queue/options.d.ts.map +1 -1
- package/dist/queue/session.d.ts +5 -1
- package/dist/queue/session.d.ts.map +1 -1
- package/dist/queue/settings/index.d.ts +5 -2
- package/dist/queue/settings/index.d.ts.map +1 -1
- package/dist/queue/settings/sender.d.ts +16 -0
- package/dist/queue/settings/sender.d.ts.map +1 -0
- package/dist/queue/worker.d.ts.map +1 -1
- package/docs/guide.md +119 -3
- package/docs/roadmap.md +20 -6
- package/docs/troubleshooting.md +68 -0
- package/package.json +10 -4
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/conformance/describe-queue-store.ts", "../src/conformance/claims.ts", "../src/conformance/setup.ts", "../src/conformance/concurrency.ts", "../src/conformance/items.ts", "../src/conformance/dsn.ts", "../src/conformance/outcomes.ts", "../src/conformance/complete-dsns.ts", "../src/conformance/doubt.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { afterEach, describe } from 'bun:test';\nimport type { QueueStore } from '../contract/queue-store';\nimport { describeClaims } from './claims';\nimport { describeConcurrency } from './concurrency';\nimport { describeItems } from './items';\nimport { describeOutcomes } from './outcomes';\nimport type { QueueStoreHarness } from './setup';\n\n/**\n * The specs every `QueueStore` must pass, to call from a spec file run by\n * `bun test`. The harness builds an empty store with `create`; `share` is\n * another handle on the same queue (a second connection, as a second\n * process opens), so the claim's atomicity is checked across handles, not\n * only within one: without it those tests are registered as skipped.\n * `dispose` runs after every test with the stores that test created.\n */\nexport function describeQueueStore(\n\tname: string,\n\tharness: QueueStoreHarness,\n): void {\n\tdescribe(`${name}: the QueueStore contract`, () => {\n\t\tconst created: QueueStore[] = [];\n\t\tconst factories = {\n\t\t\tshare: harness.share,\n\t\t\tcreate: async () => {\n\t\t\t\tconst store = await harness.create();\n\t\t\t\tcreated.push(store);\n\t\t\t\treturn store;\n\t\t\t},\n\t\t};\n\t\tafterEach(async () => {\n\t\t\tconst stores = created.splice(0);\n\t\t\tconst settled = await Promise.allSettled(\n\t\t\t\tstores.map(async (store) => harness.dispose?.(store)),\n\t\t\t);\n\t\t\tconst failed = settled.filter((r) => r.status === 'rejected');\n\t\t\tif (failed.length === 1) throw failed[0]?.reason;\n\t\t\tif (failed.length > 1)\n\t\t\t\tthrow new AggregateError(\n\t\t\t\t\tfailed.map((r) => r.reason),\n\t\t\t\t\t'several stores failed to dispose',\n\t\t\t\t);\n\t\t});\n\t\tdescribeItems(factories);\n\t\tdescribeClaims(factories);\n\t\tdescribeOutcomes(factories);\n\t\tdescribeConcurrency(factories);\n\t});\n}\n",
|
|
6
|
+
"import { describe, expect, test } from 'bun:test';\nimport type { AttemptResult } from '../contract/types';\nimport { entry, MINUTE, rejects, type StoreFactories, T0 } from './setup';\n\nconst lease = { leaseMs: 10 * MINUTE };\n\nconst result = (overrides: Partial<AttemptResult> = {}): AttemptResult => ({\n\tnow: T0 + MINUTE,\n\trecipients: [],\n\tnextAttemptAt: T0 + 30 * MINUTE,\n\tattempts: 1,\n\tdelayNotified: false,\n\t...overrides,\n});\n\n/** Claims, leases and schedules. */\nexport function describeClaims(factories: StoreFactories): void {\n\tdescribe('claims and leases', () => {\n\t\tclaiming(factories);\n\t\tleasing(factories);\n\t\trescheduling(factories);\n\t});\n}\n\nfunction claiming({ create }: StoreFactories): void {\n\ttest('claim takes a due item with a lease; nothing else is due', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tconst claimed = await store.claim({ owner: 'w1', now: T0, ...lease });\n\t\texpect(claimed?.id).toBe(item.id);\n\t\texpect(claimed?.lease).toEqual({\n\t\t\towner: 'w1',\n\t\t\texpiresAt: T0 + 10 * MINUTE,\n\t\t});\n\t\texpect((await store.get(item.id))?.lease?.owner).toBe('w1');\n\t\texpect(\n\t\t\tawait store.claim({ owner: 'w2', now: T0, ...lease }),\n\t\t).toBeUndefined();\n\t});\n\n\ttest('an item not yet due is not claimed', async () => {\n\t\tconst store = await create();\n\t\tawait store.add(entry({ createdAt: T0 + MINUTE }));\n\t\texpect(\n\t\t\tawait store.claim({ owner: 'w1', now: T0, ...lease }),\n\t\t).toBeUndefined();\n\t\texpect(\n\t\t\tawait store.claim({ owner: 'w1', now: T0 + MINUTE, ...lease }),\n\t\t).toBeDefined();\n\t});\n\n\ttest('the earliest due is claimed first, then the oldest', async () => {\n\t\tconst store = await create();\n\t\tconst late = await store.add(entry({ createdAt: T0 + MINUTE }));\n\t\tconst first = await store.add(entry());\n\t\tconst second = await store.add(entry());\n\t\tconst now = T0 + MINUTE;\n\t\tconst order = [];\n\t\tfor (let i = 0; i < 3; i++) {\n\t\t\torder.push((await store.claim({ owner: 'w', now, ...lease }))?.id);\n\t\t}\n\t\texpect(order).toEqual([first.id, second.id, late.id]);\n\t});\n}\n\nfunction leasing({ create }: StoreFactories): void {\n\ttest('an expired lease is claimed again, and its old owner has lost it', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tawait store.claim({ owner: 'crashed', now: T0, leaseMs: MINUTE });\n\t\texpect(\n\t\t\tawait store.claim({ owner: 'w2', now: T0 + MINUTE - 1, ...lease }),\n\t\t).toBeUndefined();\n\t\tconst again = await store.claim({\n\t\t\towner: 'w2',\n\t\t\tnow: T0 + MINUTE,\n\t\t\t...lease,\n\t\t});\n\t\texpect(again?.id).toBe(item.id);\n\t\texpect(again?.lease?.owner).toBe('w2');\n\t\texpect(await store.renew(item.id, 'crashed', T0 + 5 * MINUTE)).toBe(false);\n\t\texpect(\n\t\t\tawait store.complete(item.id, 'crashed', result(), []),\n\t\t).toBeUndefined();\n\t\texpect(await store.reschedule(item.id, T0, 'crashed')).toBe(false);\n\t\texpect((await store.get(item.id))?.lease?.owner).toBe('w2');\n\t});\n\n\ttest('renew moves the expiry while the owner holds the lease', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tawait store.claim({ owner: 'w1', now: T0, leaseMs: MINUTE });\n\t\texpect(await store.renew(item.id, 'w1', T0 + 3 * MINUTE)).toBe(true);\n\t\texpect(\n\t\t\tawait store.claim({ owner: 'w2', now: T0 + 2 * MINUTE, ...lease }),\n\t\t).toBeUndefined();\n\t\texpect(await store.renew('nope', 'w1', T0)).toBe(false);\n\t});\n}\n\nfunction rescheduling({ create }: StoreFactories): void {\n\ttest('reschedule with the owner gives the item back, due at the new time', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tawait store.claim({ owner: 'w1', now: T0, ...lease });\n\t\texpect(await store.reschedule(item.id, T0 + MINUTE, 'w2')).toBe(false);\n\t\texpect(await store.reschedule(item.id, T0 + MINUTE, 'w1')).toBe(true);\n\t\tconst stored = await store.get(item.id);\n\t\texpect(stored?.lease).toBeUndefined();\n\t\texpect(stored?.nextAttemptAt).toBe(T0 + MINUTE);\n\t\texpect(stored?.attempts).toBe(0);\n\t});\n\n\ttest('reschedule without an owner moves the time and leaves the lease alone', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tawait store.claim({ owner: 'w1', now: T0, ...lease });\n\t\tawait store.complete(item.id, 'w1', result(), []);\n\t\texpect(await store.reschedule(item.id, T0 + MINUTE)).toBe(true);\n\t\texpect(\n\t\t\t(await store.claim({ owner: 'w2', now: T0 + MINUTE, ...lease }))?.id,\n\t\t).toBe(item.id);\n\t\texpect(await store.reschedule(item.id, T0)).toBe(true);\n\t\texpect((await store.get(item.id))?.lease?.owner).toBe('w2');\n\t});\n\n\ttest('claim refuses a bad request', async () => {\n\t\tconst store = await create();\n\t\tawait rejects(store.claim({ owner: '', now: T0, ...lease }), 'INVALID');\n\t\tawait rejects(store.claim({ owner: 'w', now: T0, leaseMs: 0 }), 'INVALID');\n\t\tawait rejects(\n\t\t\tstore.claim({ owner: 'w', now: Number.NaN, ...lease }),\n\t\t\t'INVALID',\n\t\t);\n\t\tawait rejects(store.reschedule('nope', T0, ''), 'INVALID');\n\t});\n}\n",
|
|
7
|
+
"import { expect } from 'bun:test';\nimport type { QueueStore } from '../contract/queue-store';\nimport type { AttemptResult, NewQueueItem } from '../contract/types';\n\n/** A new store, empty. */\nexport type CreateQueueStore = () => Promise<QueueStore> | QueueStore;\n\n/**\n * Another handle on the same queue as `store`: a second connection to the\n * same database, as a second process would open. A harness with no second\n * handle omits `share`, and the two several-workers tests are then skipped.\n */\nexport type ShareStore = (store: QueueStore) => QueueStore;\n\n/** What a store's spec hands the suite. */\nexport interface QueueStoreHarness {\n\t/** A new store, empty. */\n\treadonly create: CreateQueueStore;\n\t/** Another handle on the same queue; without it the share tests are skipped. */\n\treadonly share?: ShareStore | undefined;\n\t/**\n\t * Closes a store `create` made, after the test that used it. Handles\n\t * returned by `share` are the harness's to close.\n\t */\n\treadonly dispose?: ((store: QueueStore) => Promise<void> | void) | undefined;\n}\n\n/** What the suite is handed: how to build a store, and what the harness can do beyond that. */\nexport interface StoreFactories {\n\treadonly create: CreateQueueStore;\n\t/** Absent when the harness has no second handle: the share tests are skipped. */\n\treadonly share?: ShareStore | undefined;\n}\n\nexport const bytes = (text: string) => new TextEncoder().encode(text);\n\nexport const T0 = Date.UTC(2026, 9, 1, 12);\nexport const MINUTE = 60_000;\n\nexport const lease = { leaseMs: 10 * MINUTE };\n\nexport const result = (\n\toverrides: Partial<AttemptResult> = {},\n): AttemptResult => ({\n\tnow: T0 + MINUTE,\n\trecipients: [],\n\tnextAttemptAt: T0 + 30 * MINUTE,\n\tattempts: 1,\n\tdelayNotified: false,\n\t...overrides,\n});\n\n/** A new item from mary to two recipients, created at `T0` unless told otherwise. */\nexport function entry(overrides: Partial<NewQueueItem> = {}): NewQueueItem {\n\treturn {\n\t\tfrom: 'mary@example.net',\n\t\tto: ['joe@example.com', 'ann@example.org'],\n\t\tmessage: bytes('Subject: Hi\\r\\n\\r\\nHello\\r\\n'),\n\t\tcreatedAt: T0,\n\t\t...overrides,\n\t};\n}\n\n/** Expects a promise to reject with a QueueError of this code. */\nexport async function rejects(promise: Promise<unknown>, code: string) {\n\tawait expect(promise).rejects.toMatchObject({ name: 'QueueError', code });\n}\n",
|
|
8
|
+
"import { describe, expect, test } from 'bun:test';\nimport type { QueueStore } from '../contract/queue-store';\nimport { entry, MINUTE, type StoreFactories, T0 } from './setup';\n\nconst delivered = (now: number) => ({\n\tnow,\n\trecipients: [\n\t\t{ address: 'joe@example.com', status: 'delivered' as const },\n\t\t{ address: 'ann@example.org', status: 'delivered' as const },\n\t],\n\tnextAttemptAt: now,\n\tattempts: 1,\n\tdelayNotified: false,\n});\n\n/** A worker: claims and delivers until nothing is due, yielding between steps. */\nasync function work(store: QueueStore, owner: string, now: number) {\n\tconst taken: string[] = [];\n\tfor (;;) {\n\t\tawait new Promise((resolve) => setTimeout(resolve, Math.random() * 2));\n\t\tconst item = await store.claim({ owner, now, leaseMs: 10 * MINUTE });\n\t\tif (!item) return taken;\n\t\ttaken.push(item.id);\n\t\tawait new Promise((resolve) => setTimeout(resolve, Math.random() * 2));\n\t\tconst done = await store.complete(item.id, owner, delivered(now), []);\n\t\texpect(done).toBeDefined();\n\t}\n}\n\n/** Several claimers on one queue, and a claimer that crashed. */\nexport function describeConcurrency({ create, share }: StoreFactories): void {\n\t// A harness with no second handle cannot prove atomicity across handles:\n\t// the test is registered as skipped, with the reason in its name.\n\tconst shared = share ? test : test.skip;\n\tconst note = share ? '' : ' (skipped: harness has no share)';\n\tdescribe('several workers', () => {\n\t\tshared(\n\t\t\t`many claimers race: every item is delivered once${note}`,\n\t\t\tasync () => {\n\t\t\t\tconst store = await create();\n\t\t\t\tconst ids = new Set<string>();\n\t\t\t\tfor (let i = 0; i < 60; i++) ids.add((await store.add(entry())).id);\n\t\t\t\tconst workers = Array.from({ length: 8 }, (_, i) =>\n\t\t\t\t\twork(share?.(store) ?? store, `w${i}`, T0),\n\t\t\t\t);\n\t\t\t\tconst taken = (await Promise.all(workers)).flat();\n\t\t\t\texpect(taken.length).toBe(60);\n\t\t\t\texpect(new Set(taken)).toEqual(ids);\n\t\t\t\texpect(await store.count()).toBe(0);\n\t\t\t},\n\t\t);\n\n\t\tshared(\n\t\t\t`a crashed worker’s items are claimed again once its leases expire${note}`,\n\t\t\tasync () => {\n\t\t\t\tconst store = await create();\n\t\t\t\tfor (let i = 0; i < 5; i++) await store.add(entry());\n\t\t\t\tconst crashed: string[] = [];\n\t\t\t\tfor (let i = 0; i < 5; i++) {\n\t\t\t\t\tconst item = await store.claim({\n\t\t\t\t\t\towner: 'crashed',\n\t\t\t\t\t\tnow: T0,\n\t\t\t\t\t\tleaseMs: MINUTE,\n\t\t\t\t\t});\n\t\t\t\t\tif (item) crashed.push(item.id);\n\t\t\t\t}\n\t\t\t\texpect(crashed.length).toBe(5);\n\t\t\t\tconst other = share?.(store) ?? store;\n\t\t\t\texpect(await work(other, 'w2', T0 + MINUTE - 1)).toEqual([]);\n\t\t\t\tconst recovered = await work(other, 'w2', T0 + MINUTE);\n\t\t\t\texpect(new Set(recovered)).toEqual(new Set(crashed));\n\t\t\t\tfor (const id of crashed) {\n\t\t\t\t\texpect(\n\t\t\t\t\t\tawait store.complete(id, 'crashed', delivered(T0), []),\n\t\t\t\t\t).toBeUndefined();\n\t\t\t\t}\n\t\t\t\texpect(await store.count()).toBe(0);\n\t\t\t},\n\t\t);\n\t});\n}\n",
|
|
9
|
+
"import { describe, expect, test } from 'bun:test';\nimport { dsnParameters } from './dsn';\nimport {\n\tbytes,\n\tentry,\n\tMINUTE,\n\trejects,\n\ttype StoreFactories,\n\tT0,\n} from './setup';\n\n/** Adding, reading, listing and cancelling items. */\nexport function describeItems(factories: StoreFactories): void {\n\tdescribe('items', () => {\n\t\tadding(factories);\n\t\tlisting(factories);\n\t\tdsnParameters(factories);\n\t});\n}\n\nfunction adding({ create }: StoreFactories): void {\n\ttest('add keeps the envelope, every recipient pending, due at once', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\texpect(item).toMatchObject({\n\t\t\tfrom: 'mary@example.net',\n\t\t\trecipients: [\n\t\t\t\t{ address: 'joe@example.com', status: 'pending' },\n\t\t\t\t{ address: 'ann@example.org', status: 'pending' },\n\t\t\t],\n\t\t\tsize: 22,\n\t\t\tcreatedAt: T0,\n\t\t\tnextAttemptAt: T0,\n\t\t\tattempts: 0,\n\t\t\tdelayNotified: false,\n\t\t});\n\t\texpect(item.lease).toBeUndefined();\n\t\texpect(await store.get(item.id)).toEqual(item);\n\t\texpect(await store.count()).toBe(1);\n\t});\n\n\ttest('the null sender is kept as the empty string', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry({ from: '' }));\n\t\texpect((await store.get(item.id))?.from).toBe('');\n\t});\n\n\ttest('readMessage gives the bytes back, a copy each time', async () => {\n\t\tconst store = await create();\n\t\tconst message = bytes('Subject: x\\r\\n\\r\\nbody\\r\\n');\n\t\tconst item = await store.add(entry({ message }));\n\t\tmessage[0] = 0;\n\t\tconst read = await store.readMessage(item.id);\n\t\texpect(new TextDecoder().decode(read)).toBe('Subject: x\\r\\n\\r\\nbody\\r\\n');\n\t\tif (read) read[0] = 0;\n\t\texpect((await store.readMessage(item.id))?.[0]).toBe(83);\n\t});\n\n\ttest('an item returned is a copy', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tconst first = (item.recipients as unknown as { status: string }[])[0];\n\t\tif (first) first.status = 'failed';\n\t\texpect((await store.get(item.id))?.recipients[0]?.status).toBe('pending');\n\t});\n\n\ttest('an unknown id is undefined, not an error', async () => {\n\t\tconst store = await create();\n\t\texpect(await store.get('nope')).toBeUndefined();\n\t\texpect(await store.readMessage('nope')).toBeUndefined();\n\t\texpect(await store.cancel('nope')).toBeUndefined();\n\t\texpect(await store.reschedule('nope', T0)).toBe(false);\n\t});\n\n\ttest('an id no store could hold, a NUL or a lone surrogate in it, is unknown too', async () => {\n\t\tconst store = await create();\n\t\tawait store.add(entry());\n\t\tfor (const id of ['a\\u0000b', 'a\\ud83d']) {\n\t\t\texpect(await store.get(id)).toBeUndefined();\n\t\t\texpect(await store.readMessage(id)).toBeUndefined();\n\t\t\texpect(await store.renew(id, 'w1', T0)).toBe(false);\n\t\t\texpect(await store.reschedule(id, T0)).toBe(false);\n\t\t\texpect(await store.cancel(id)).toBeUndefined();\n\t\t}\n\t\texpect(await store.count()).toBe(1);\n\t});\n}\n\nfunction listing({ create }: StoreFactories): void {\n\ttest('list gives the next due first, then the oldest, a page at a time', async () => {\n\t\tconst store = await create();\n\t\tconst a = await store.add(entry({ createdAt: T0 + 2 * MINUTE }));\n\t\tconst b = await store.add(entry({ createdAt: T0 }));\n\t\tconst c = await store.add(entry({ createdAt: T0 }));\n\t\tconst ids = (await store.list()).map((i) => i.id);\n\t\texpect(ids).toEqual([b.id, c.id, a.id]);\n\t\tconst page = await store.list({ offset: 1, limit: 1 });\n\t\texpect(page.map((i) => i.id)).toEqual([c.id]);\n\t});\n\n\ttest('list refuses a bad page', async () => {\n\t\tconst store = await create();\n\t\tawait rejects(store.list({ limit: 0 }), 'INVALID');\n\t\tawait rejects(store.list({ offset: -1 }), 'INVALID');\n\t});\n\n\ttest('cancel drops the item and its message, leased or not', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tawait store.claim({ owner: 'w1', now: T0, leaseMs: MINUTE });\n\t\tconst cancelled = await store.cancel(item.id);\n\t\texpect(cancelled?.id).toBe(item.id);\n\t\texpect(await store.get(item.id)).toBeUndefined();\n\t\texpect(await store.readMessage(item.id)).toBeUndefined();\n\t\texpect(await store.count()).toBe(0);\n\t});\n\n\ttest('maxItems refuses an add past the limit with QUEUE_FULL', async () => {\n\t\tconst store = await create();\n\t\tawait store.add(entry(), { maxItems: 2 });\n\t\tawait store.add(entry(), { maxItems: 2 });\n\t\tawait rejects(store.add(entry(), { maxItems: 2 }), 'QUEUE_FULL');\n\t\texpect(await store.count()).toBe(2);\n\t\tawait rejects(store.add(entry(), { maxItems: 0 }), 'INVALID');\n\t});\n\n\ttest('add refuses what is not an item', async () => {\n\t\tconst store = await create();\n\t\tawait rejects(store.add(entry({ to: [] })), 'INVALID');\n\t\tawait rejects(\n\t\t\tstore.add(entry({ message: 'text' as unknown as Uint8Array })),\n\t\t\t'INVALID',\n\t\t);\n\t\tawait rejects(store.add(entry({ createdAt: Number.NaN })), 'INVALID');\n\t\tawait rejects(store.add(entry({ from: 'a\\u0000@example.net' })), 'INVALID');\n\t\tawait rejects(\n\t\t\tstore.add(entry({ to: ['jo\\ud83d@example.com'] })),\n\t\t\t'INVALID',\n\t\t);\n\t\texpect(await store.count()).toBe(0);\n\t});\n}\n",
|
|
10
|
+
"import { expect, test } from 'bun:test';\nimport {\n\tentry,\n\tlease,\n\trejects,\n\tresult,\n\ttype StoreFactories,\n\tT0,\n} from './setup';\n\n/** RFC 3461's parameters: kept as given, for the item's whole life. */\nexport function dsnParameters({ create }: StoreFactories): void {\n\tconst asked = {\n\t\tret: 'HDRS' as const,\n\t\tenvid: 'QQ314159 + x=',\n\t\trecipients: [\n\t\t\t{\n\t\t\t\taddress: 'joe@example.com',\n\t\t\t\tnotify: ['SUCCESS', 'DELAY'] as ('SUCCESS' | 'DELAY')[],\n\t\t\t\torcpt: { type: 'rfc822', address: 'Joe.Original@example.com' },\n\t\t\t},\n\t\t\t{ address: 'ann@example.org', notify: 'NEVER' as const },\n\t\t],\n\t};\n\n\ttest('RFC 3461 §4: ret, envid, notify and orcpt are kept by add, get, list and claim', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry(asked));\n\t\tconst kept = {\n\t\t\tret: 'HDRS',\n\t\t\tenvid: 'QQ314159 + x=',\n\t\t\trecipients: [\n\t\t\t\t{\n\t\t\t\t\taddress: 'joe@example.com',\n\t\t\t\t\tnotify: ['SUCCESS', 'DELAY'],\n\t\t\t\t\torcpt: { type: 'rfc822', address: 'Joe.Original@example.com' },\n\t\t\t\t},\n\t\t\t\t{ address: 'ann@example.org', notify: 'NEVER' },\n\t\t\t],\n\t\t};\n\t\texpect(item).toMatchObject(kept);\n\t\texpect(await store.get(item.id)).toEqual(item);\n\t\texpect((await store.list())[0]).toEqual(item);\n\t\texpect(await store.claim({ owner: 'w1', now: T0, ...lease })).toMatchObject(\n\t\t\tkept,\n\t\t);\n\t});\n\n\ttest('complete keeps them: the notify and orcpt of a deferred recipient, and the item level', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry(asked));\n\t\tawait store.claim({ owner: 'w1', now: T0, ...lease });\n\t\tconst done = await store.complete(\n\t\t\titem.id,\n\t\t\t'w1',\n\t\t\tresult({\n\t\t\t\trecipients: [\n\t\t\t\t\t{\n\t\t\t\t\t\taddress: 'joe@example.com',\n\t\t\t\t\t\tstatus: 'deferred',\n\t\t\t\t\t\treply: { text: 'try later' },\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t}),\n\t\t\t[],\n\t\t);\n\t\texpect(done?.item.recipients[0]).toMatchObject({\n\t\t\tnotify: ['SUCCESS', 'DELAY'],\n\t\t\torcpt: { type: 'rfc822', address: 'Joe.Original@example.com' },\n\t\t});\n\t\tconst again = await store.get(item.id);\n\t\texpect(again?.ret).toBe('HDRS');\n\t\texpect(again?.envid).toBe('QQ314159 + x=');\n\t\texpect(again?.recipients[1]?.notify).toBe('NEVER');\n\t});\n\n\ttest('an item added with none carries none: the fields are absent, as before', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tconst read = await store.get(item.id);\n\t\tfor (const key of ['ret', 'envid'] as const) {\n\t\t\texpect(read).not.toHaveProperty(key);\n\t\t}\n\t\tfor (const r of read?.recipients ?? []) {\n\t\t\texpect(r).not.toHaveProperty('notify');\n\t\t\texpect(r).not.toHaveProperty('orcpt');\n\t\t}\n\t});\n\n\ttest('RFC 6533: 50 emoji in a utf-8 ORCPT are within 500 characters, as the server counts them', async () => {\n\t\tconst store = await create();\n\t\tconst orcpt = { type: 'utf-8', address: `${'😀'.repeat(50)}@x` };\n\t\tconst item = await store.add(\n\t\t\tentry({ recipients: [{ address: 'joe@example.com', orcpt }] }),\n\t\t);\n\t\texpect(item.recipients[0]?.orcpt).toEqual(orcpt);\n\t\tawait expect(\n\t\t\tstore.add(\n\t\t\t\tentry({\n\t\t\t\t\trecipients: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\taddress: 'joe@example.com',\n\t\t\t\t\t\t\torcpt: { type: 'rfc822', address: orcpt.address },\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t}),\n\t\t\t),\n\t\t).rejects.toMatchObject({ code: 'INVALID' });\n\t});\n\n\ttest('RFC 6533: a utf-8 ORCPT of 500 characters (82 escapes) is kept, 506 is INVALID', async () => {\n\t\tconst store = await create();\n\t\tconst orcpt = (n: number) => ({\n\t\t\ttype: 'utf-8',\n\t\t\taddress: `${'+'.repeat(n)}@x`,\n\t\t});\n\t\tconst item = await store.add(\n\t\t\tentry({ recipients: [{ address: 'joe@example.com', orcpt: orcpt(82) }] }),\n\t\t);\n\t\texpect(item.recipients[0]?.orcpt).toEqual(orcpt(82));\n\t\tawait rejects(\n\t\t\tstore.add(\n\t\t\t\tentry({\n\t\t\t\t\trecipients: [{ address: 'joe@example.com', orcpt: orcpt(83) }],\n\t\t\t\t}),\n\t\t\t),\n\t\t\t'INVALID',\n\t\t);\n\t});\n\n\ttest('what is not a parameter is INVALID, the same for every store', async () => {\n\t\tconst store = await create();\n\t\tconst bad: Record<string, unknown>[] = [\n\t\t\t{ ret: 'ALL' },\n\t\t\t{ envid: '' },\n\t\t\t{ envid: 'a\\r\\nb' },\n\t\t\t{ envid: 'x'.repeat(101) },\n\t\t\t{ recipients: [{ address: 'zed@example.com', notify: 'NEVER' }] },\n\t\t\t{ recipients: [{ address: 'joe@example.com', notify: 'SOMETIMES' }] },\n\t\t\t{ recipients: [{ address: 'joe@example.com', notify: [] }] },\n\t\t\t{\n\t\t\t\trecipients: [\n\t\t\t\t\t{ address: 'joe@example.com', notify: ['DELAY', 'DELAY'] },\n\t\t\t\t],\n\t\t\t},\n\t\t\t{\n\t\t\t\trecipients: [{ address: 'joe@example.com', orcpt: { type: 'rfc822' } }],\n\t\t\t},\n\t\t\t{\n\t\t\t\trecipients: [\n\t\t\t\t\t{ address: 'joe@example.com' },\n\t\t\t\t\t{ address: 'joe@example.com' },\n\t\t\t\t],\n\t\t\t},\n\t\t];\n\t\tfor (const params of bad) {\n\t\t\tawait rejects(store.add(entry(params as never)), 'INVALID');\n\t\t}\n\t\texpect(await store.count()).toBe(0);\n\t});\n}\n",
|
|
11
|
+
"import { describe, expect, test } from 'bun:test';\nimport { completeDsns } from './complete-dsns';\nimport { doubt, fallbackDoubt } from './doubt';\nimport {\n\tentry,\n\tlease,\n\tMINUTE,\n\trejects,\n\tresult,\n\ttype StoreFactories,\n\tT0,\n} from './setup';\n\n/** What `complete` records. */\nexport function describeOutcomes(factories: StoreFactories): void {\n\tdescribe('outcomes', () => {\n\t\trecording(factories);\n\t\trefusing(factories);\n\t\tfinality(factories);\n\t\tcompleteDsns(factories);\n\t\tdoubt(factories);\n\t\tfallbackDoubt(factories);\n\t});\n}\n\nfunction recording({ create }: StoreFactories): void {\n\ttest('complete records each outcome, the schedule, and lets go of the lease', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tawait store.claim({ owner: 'w1', now: T0, ...lease });\n\t\tconst reply = {\n\t\t\tcode: 451,\n\t\t\tstatus: '4.3.0',\n\t\t\ttext: 'try later',\n\t\t\thost: 'mx.example.com',\n\t\t};\n\t\tconst done = await store.complete(\n\t\t\titem.id,\n\t\t\t'w1',\n\t\t\tresult({\n\t\t\t\trecipients: [{ address: 'joe@example.com', status: 'deferred', reply }],\n\t\t\t}),\n\t\t\t[],\n\t\t);\n\t\texpect(done?.item.recipients).toEqual([\n\t\t\t{\n\t\t\t\taddress: 'joe@example.com',\n\t\t\t\tstatus: 'deferred',\n\t\t\t\treply,\n\t\t\t\tupdatedAt: T0 + MINUTE,\n\t\t\t},\n\t\t\t{ address: 'ann@example.org', status: 'pending' },\n\t\t]);\n\t\tconst stored = await store.get(item.id);\n\t\texpect(stored).toEqual(done?.item);\n\t\texpect(stored).toMatchObject({\n\t\t\tnextAttemptAt: T0 + 30 * MINUTE,\n\t\t\tattempts: 1,\n\t\t});\n\t\texpect(stored?.lease).toBeUndefined();\n\t\texpect(\n\t\t\tawait store.claim({ owner: 'w2', now: T0 + 29 * MINUTE, ...lease }),\n\t\t).toBeUndefined();\n\t\texpect(\n\t\t\tawait store.claim({ owner: 'w2', now: T0 + 30 * MINUTE, ...lease }),\n\t\t).toBeDefined();\n\t});\n\n\ttest('an item whose every recipient is final is dropped with its message', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tawait store.claim({ owner: 'w1', now: T0, ...lease });\n\t\tconst done = await store.complete(\n\t\t\titem.id,\n\t\t\t'w1',\n\t\t\tresult({\n\t\t\t\trecipients: [\n\t\t\t\t\t{\n\t\t\t\t\t\taddress: 'joe@example.com',\n\t\t\t\t\t\tstatus: 'delivered',\n\t\t\t\t\t\treply: { code: 250, text: 'ok' },\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\taddress: 'ann@example.org',\n\t\t\t\t\t\tstatus: 'failed',\n\t\t\t\t\t\treply: { code: 550, text: 'no' },\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tdelayNotified: true,\n\t\t\t}),\n\t\t\t[],\n\t\t);\n\t\texpect(done?.item.recipients.map((r) => r.status)).toEqual([\n\t\t\t'delivered',\n\t\t\t'failed',\n\t\t]);\n\t\texpect(done?.item.delayNotified).toBe(true);\n\t\texpect(await store.get(item.id)).toBeUndefined();\n\t\texpect(await store.readMessage(item.id)).toBeUndefined();\n\t\texpect(await store.count()).toBe(0);\n\t});\n}\n\nfunction refusing({ create }: StoreFactories): void {\n\ttest('an outcome holding a NUL or a lone surrogate is refused, and the item stays as it was', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tawait store.claim({ owner: 'w1', now: T0, ...lease });\n\t\tconst bad = [\n\t\t\t{ code: 451, text: `${'a'.repeat(8)}\\ud83d` },\n\t\t\t{ code: 451, text: 'nul \\u0000 here' },\n\t\t\t{ code: 451, text: 'ok', host: 'mx\\udc00.example.com' },\n\t\t\t{ code: 451, status: '4.3.0\\u0000', text: 'ok' },\n\t\t];\n\t\tfor (const reply of bad) {\n\t\t\tawait rejects(\n\t\t\t\tstore.complete(\n\t\t\t\t\titem.id,\n\t\t\t\t\t'w1',\n\t\t\t\t\tresult({\n\t\t\t\t\t\trecipients: [\n\t\t\t\t\t\t\t{ address: 'joe@example.com', status: 'deferred', reply },\n\t\t\t\t\t\t],\n\t\t\t\t\t}),\n\t\t\t\t\t[],\n\t\t\t\t),\n\t\t\t\t'INVALID',\n\t\t\t);\n\t\t}\n\t\tawait rejects(\n\t\t\tstore.claim({ owner: 'w\\ud800', now: T0, ...lease }),\n\t\t\t'INVALID',\n\t\t);\n\t\tconst stored = await store.get(item.id);\n\t\texpect(stored?.attempts).toBe(0);\n\t\texpect(stored?.lease?.owner).toBe('w1');\n\t\tconst done = await store.complete(\n\t\t\titem.id,\n\t\t\t'w1',\n\t\t\tresult({\n\t\t\t\trecipients: [\n\t\t\t\t\t{\n\t\t\t\t\t\taddress: 'joe@example.com',\n\t\t\t\t\t\tstatus: 'deferred',\n\t\t\t\t\t\treply: { code: 451, text: 'Try later 😀' },\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t}),\n\t\t\t[],\n\t\t);\n\t\texpect(done?.item.recipients[0]?.reply?.text).toBe('Try later 😀');\n\t\texpect((await store.get(item.id))?.recipients[0]?.reply?.text).toBe(\n\t\t\t'Try later 😀',\n\t\t);\n\t});\n}\n\nfunction finality({ create }: StoreFactories): void {\n\ttest('a final state is never changed by a later outcome', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tawait store.claim({ owner: 'w1', now: T0, ...lease });\n\t\tawait store.complete(\n\t\t\titem.id,\n\t\t\t'w1',\n\t\t\tresult({\n\t\t\t\trecipients: [{ address: 'joe@example.com', status: 'delivered' }],\n\t\t\t}),\n\t\t\t[],\n\t\t);\n\t\tawait store.claim({ owner: 'w1', now: T0 + 30 * MINUTE, ...lease });\n\t\tconst after = await store.complete(\n\t\t\titem.id,\n\t\t\t'w1',\n\t\t\tresult({\n\t\t\t\trecipients: [{ address: 'joe@example.com', status: 'failed' }],\n\t\t\t\tattempts: 2,\n\t\t\t}),\n\t\t\t[],\n\t\t);\n\t\texpect(after?.item.recipients[0]?.status).toBe('delivered');\n\t});\n}\n",
|
|
12
|
+
"import { expect, test } from 'bun:test';\nimport type { NewQueueItem } from '../contract/types';\nimport {\n\tbytes,\n\tentry,\n\tlease,\n\tMINUTE,\n\trejects,\n\tresult,\n\ttype StoreFactories,\n\tT0,\n} from './setup';\n\nconst dsn = (text: string): NewQueueItem =>\n\tentry({\n\t\tfrom: '',\n\t\tto: ['mary@example.net'],\n\t\tmessage: bytes(`Subject: ${text}\\r\\n\\r\\n${text}\\r\\n`),\n\t});\n\nconst delivered = result({\n\trecipients: [\n\t\t{ address: 'joe@example.com', status: 'delivered' },\n\t\t{ address: 'ann@example.org', status: 'failed' },\n\t],\n});\n\n/** The DSNs `complete` takes: stored with the outcome, or not at all. */\nexport function completeDsns(factories: StoreFactories): void {\n\tstored(factories);\n\tleaseHeld(factories);\n\trefused(factories);\n\tnotFinal(factories);\n}\n\nfunction stored({ create }: StoreFactories): void {\n\ttest('complete stores its DSNs with the outcome: new ids, in the order given, due at once', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tawait store.claim({ owner: 'w1', now: T0, ...lease });\n\t\tconst done = await store.complete(item.id, 'w1', delivered, [\n\t\t\tdsn('failed'),\n\t\t\tdsn('relayed'),\n\t\t]);\n\t\texpect(done?.item.id).toBe(item.id);\n\t\texpect(done?.dsns).toHaveLength(2);\n\t\tconst [first, second] = done?.dsns ?? [];\n\t\texpect(first?.id).not.toBe(second?.id);\n\t\texpect([first?.id, second?.id]).not.toContain(item.id);\n\t\texpect(first).toMatchObject({ from: '', attempts: 0 });\n\t\texpect(await store.get(item.id)).toBeUndefined();\n\t\tfor (const [kept, subject] of [\n\t\t\t[first, 'failed'],\n\t\t\t[second, 'relayed'],\n\t\t] as const) {\n\t\t\texpect(await store.get(kept?.id ?? '')).toEqual(kept);\n\t\t\texpect(\n\t\t\t\tnew TextDecoder().decode(await store.readMessage(kept?.id ?? '')),\n\t\t\t).toContain(`Subject: ${subject}`);\n\t\t}\n\t\t// Due at once, the first stored first.\n\t\texpect(\n\t\t\t(await store.claim({ owner: 'w2', now: T0 + MINUTE, ...lease }))?.id,\n\t\t).toBe(first?.id);\n\t\texpect(await store.count()).toBe(2);\n\t});\n}\n\nfunction leaseHeld({ create }: StoreFactories): void {\n\ttest('complete by an owner that no longer holds the lease stores no DSN, and a second one by the first stores none twice', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tawait store.claim({ owner: 'w1', now: T0, ...lease });\n\t\texpect(\n\t\t\tawait store.complete(item.id, 'w2', delivered, [dsn('failed')]),\n\t\t).toBeUndefined();\n\t\texpect(await store.count()).toBe(1);\n\t\texpect(\n\t\t\tawait store.complete(item.id, 'w1', delivered, [dsn('failed')]),\n\t\t).toBeDefined();\n\t\texpect(await store.count()).toBe(1);\n\t\texpect(\n\t\t\tawait store.complete(item.id, 'w1', delivered, [dsn('failed')]),\n\t\t).toBeUndefined();\n\t\texpect(await store.count()).toBe(1);\n\t});\n}\n\nfunction refused({ create }: StoreFactories): void {\n\ttest('a DSN that fails the checks of add refuses the whole call with INVALID: no outcome, no DSN', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tawait store.claim({ owner: 'w1', now: T0, ...lease });\n\t\tawait rejects(\n\t\t\tstore.complete(item.id, 'w1', delivered, [\n\t\t\t\tdsn('failed'),\n\t\t\t\t{ ...dsn('bad'), to: [] },\n\t\t\t]),\n\t\t\t'INVALID',\n\t\t);\n\t\texpect(await store.count()).toBe(1);\n\t\texpect(await store.get(item.id)).toMatchObject({\n\t\t\tattempts: 0,\n\t\t\tlease: { owner: 'w1' },\n\t\t});\n\t});\n\n\ttest('dsns that are not an array refuse the call with INVALID, nothing recorded', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tawait store.claim({ owner: 'w1', now: T0, ...lease });\n\t\tawait rejects(\n\t\t\tstore.complete(item.id, 'w1', delivered, undefined as never),\n\t\t\t'INVALID',\n\t\t);\n\t\texpect(await store.get(item.id)).toMatchObject({ attempts: 0 });\n\t});\n}\n\nfunction notFinal({ create }: StoreFactories): void {\n\ttest('complete with no DSN returns an empty list; a not-final item stays with its DSNs beside it', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tawait store.claim({ owner: 'w1', now: T0, ...lease });\n\t\tconst none = await store.complete(item.id, 'w1', result(), []);\n\t\texpect(none?.dsns).toEqual([]);\n\t\tawait store.claim({ owner: 'w1', now: T0 + 30 * MINUTE, ...lease });\n\t\tconst deferred = result({\n\t\t\trecipients: [{ address: 'joe@example.com', status: 'deferred' }],\n\t\t\tattempts: 2,\n\t\t\tdelayNotified: true,\n\t\t});\n\t\tconst done = await store.complete(item.id, 'w1', deferred, [\n\t\t\tdsn('delayed'),\n\t\t]);\n\t\texpect(done?.item.delayNotified).toBe(true);\n\t\texpect(done?.dsns).toHaveLength(1);\n\t\texpect(await store.count()).toBe(2);\n\t\texpect(await store.get(item.id)).toEqual(done?.item);\n\t});\n}\n",
|
|
13
|
+
"import { expect, test } from 'bun:test';\nimport type { AttemptResult } from '../contract/types';\nimport {\n\tentry,\n\tlease,\n\tMINUTE,\n\trejects,\n\tresult,\n\ttype StoreFactories,\n\tT0,\n} from './setup';\n\n/** The marks a recipient keeps when a server may hold a copy. */\nexport function doubt({ create }: StoreFactories): void {\n\tconst deferred = (mayHaveReachedMx?: unknown) =>\n\t\t({\n\t\t\taddress: 'joe@example.com',\n\t\t\tstatus: 'deferred',\n\t\t\treply: { status: '4.4.2', text: 'no reply to the final dot' },\n\t\t\t...(mayHaveReachedMx === undefined ? {} : { mayHaveReachedMx }),\n\t\t}) as AttemptResult['recipients'][number];\n\n\ttest('mayHaveReachedMx: recorded, read by get and list, and kept by a later outcome without it', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tawait store.claim({ owner: 'w1', now: T0, ...lease });\n\t\tconst done = await store.complete(\n\t\t\titem.id,\n\t\t\t'w1',\n\t\t\tresult({ recipients: [deferred(true)] }),\n\t\t\t[],\n\t\t);\n\t\texpect(done?.item.recipients[0]?.mayHaveReachedMx).toBe(true);\n\t\texpect(done?.item.recipients[1]).not.toHaveProperty('mayHaveReachedMx');\n\t\texpect((await store.get(item.id))?.recipients[0]?.mayHaveReachedMx).toBe(\n\t\t\ttrue,\n\t\t);\n\t\texpect((await store.list())[0]?.recipients[0]?.mayHaveReachedMx).toBe(true);\n\t\tawait store.claim({ owner: 'w1', now: T0 + 30 * MINUTE, ...lease });\n\t\tconst again = await store.complete(\n\t\t\titem.id,\n\t\t\t'w1',\n\t\t\tresult({ recipients: [deferred()], attempts: 2 }),\n\t\t\t[],\n\t\t);\n\t\texpect(again?.item.recipients[0]?.mayHaveReachedMx).toBe(true);\n\t\texpect((await store.get(item.id))?.recipients[0]?.mayHaveReachedMx).toBe(\n\t\t\ttrue,\n\t\t);\n\t});\n\n\ttest('mayHaveReachedMx: an unmarked recipient has no such key', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tawait store.claim({ owner: 'w1', now: T0, ...lease });\n\t\tawait store.complete(\n\t\t\titem.id,\n\t\t\t'w1',\n\t\t\tresult({ recipients: [deferred()] }),\n\t\t\t[],\n\t\t);\n\t\tfor (const r of (await store.get(item.id))?.recipients ?? []) {\n\t\t\texpect(r).not.toHaveProperty('mayHaveReachedMx');\n\t\t}\n\t});\n\n\ttest('mayHaveReachedMx: a value other than true is INVALID, and the item stays as it was', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tawait store.claim({ owner: 'w1', now: T0, ...lease });\n\t\tfor (const bad of [false, 'true', 1, null]) {\n\t\t\tawait rejects(\n\t\t\t\tstore.complete(\n\t\t\t\t\titem.id,\n\t\t\t\t\t'w1',\n\t\t\t\t\tresult({ recipients: [deferred(bad)] }),\n\t\t\t\t\t[],\n\t\t\t\t),\n\t\t\t\t'INVALID',\n\t\t\t);\n\t\t}\n\t\tconst kept = await store.get(item.id);\n\t\texpect(kept?.recipients[0]?.status).toBe('pending');\n\t\texpect(kept?.lease?.owner).toBe('w1');\n\t});\n}\n\nexport function fallbackDoubt({ create }: StoreFactories): void {\n\tconst deferred = (mayHaveReachedFallback?: unknown) =>\n\t\t({\n\t\t\taddress: 'joe@example.com',\n\t\t\tstatus: 'deferred',\n\t\t\treply: { status: '4.4.2', text: 'no reply to the final dot' },\n\t\t\t...(mayHaveReachedFallback === undefined\n\t\t\t\t? {}\n\t\t\t\t: { mayHaveReachedFallback }),\n\t\t}) as AttemptResult['recipients'][number];\n\n\ttest('mayHaveReachedFallback: recorded, read by get and list, and kept by a later outcome without it', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tawait store.claim({ owner: 'w1', now: T0, ...lease });\n\t\tconst done = await store.complete(\n\t\t\titem.id,\n\t\t\t'w1',\n\t\t\tresult({ recipients: [deferred(true)] }),\n\t\t\t[],\n\t\t);\n\t\texpect(done?.item.recipients[0]?.mayHaveReachedFallback).toBe(true);\n\t\texpect(done?.item.recipients[1]).not.toHaveProperty(\n\t\t\t'mayHaveReachedFallback',\n\t\t);\n\t\texpect(\n\t\t\t(await store.get(item.id))?.recipients[0]?.mayHaveReachedFallback,\n\t\t).toBe(true);\n\t\texpect((await store.list())[0]?.recipients[0]?.mayHaveReachedFallback).toBe(\n\t\t\ttrue,\n\t\t);\n\t\tawait store.claim({ owner: 'w1', now: T0 + 30 * MINUTE, ...lease });\n\t\tconst again = await store.complete(\n\t\t\titem.id,\n\t\t\t'w1',\n\t\t\tresult({ recipients: [deferred()], attempts: 2 }),\n\t\t\t[],\n\t\t);\n\t\texpect(again?.item.recipients[0]?.mayHaveReachedFallback).toBe(true);\n\t\texpect(\n\t\t\t(await store.get(item.id))?.recipients[0]?.mayHaveReachedFallback,\n\t\t).toBe(true);\n\t});\n\n\ttest('mayHaveReachedFallback: an unmarked recipient has no such key', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tawait store.claim({ owner: 'w1', now: T0, ...lease });\n\t\tawait store.complete(\n\t\t\titem.id,\n\t\t\t'w1',\n\t\t\tresult({ recipients: [deferred()] }),\n\t\t\t[],\n\t\t);\n\t\tfor (const r of (await store.get(item.id))?.recipients ?? []) {\n\t\t\texpect(r).not.toHaveProperty('mayHaveReachedFallback');\n\t\t}\n\t});\n\n\ttest('mayHaveReachedFallback: a value other than true is INVALID, and the item stays as it was', async () => {\n\t\tconst store = await create();\n\t\tconst item = await store.add(entry());\n\t\tawait store.claim({ owner: 'w1', now: T0, ...lease });\n\t\tfor (const bad of [false, 'true', 1, null]) {\n\t\t\tawait rejects(\n\t\t\t\tstore.complete(\n\t\t\t\t\titem.id,\n\t\t\t\t\t'w1',\n\t\t\t\t\tresult({ recipients: [deferred(bad)] }),\n\t\t\t\t\t[],\n\t\t\t\t),\n\t\t\t\t'INVALID',\n\t\t\t);\n\t\t}\n\t\tconst kept = await store.get(item.id);\n\t\texpect(kept?.recipients[0]?.status).toBe('pending');\n\t\texpect(kept?.lease?.owner).toBe('w1');\n\t});\n}\n"
|
|
14
|
+
],
|
|
15
|
+
"mappings": "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",
|
|
16
|
+
"debugId": "434F5188249F7D1F64756E2164756E21",
|
|
17
|
+
"names": []
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"items.d.ts","sourceRoot":"","sources":["../../src/conformance/items.ts"],"names":[],"mappings":"AAEA,OAAO,EAKN,KAAK,cAAc,EAEnB,MAAM,SAAS,CAAC;AAEjB,qDAAqD;AACrD,wBAAgB,aAAa,CAAC,SAAS,EAAE,cAAc,GAAG,IAAI,CAM7D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outcomes.d.ts","sourceRoot":"","sources":["../../src/conformance/outcomes.ts"],"names":[],"mappings":"AAGA,OAAO,EAMN,KAAK,cAAc,EAEnB,MAAM,SAAS,CAAC;AAEjB,+BAA+B;AAC/B,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,cAAc,GAAG,IAAI,CAShE"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { QueueStore } from '../contract/queue-store';
|
|
2
|
+
import type { AttemptResult, NewQueueItem } from '../contract/types';
|
|
3
|
+
/** A new store, empty. */
|
|
4
|
+
export type CreateQueueStore = () => Promise<QueueStore> | QueueStore;
|
|
5
|
+
/**
|
|
6
|
+
* Another handle on the same queue as `store`: a second connection to the
|
|
7
|
+
* same database, as a second process would open. A harness with no second
|
|
8
|
+
* handle omits `share`, and the two several-workers tests are then skipped.
|
|
9
|
+
*/
|
|
10
|
+
export type ShareStore = (store: QueueStore) => QueueStore;
|
|
11
|
+
/** What a store's spec hands the suite. */
|
|
12
|
+
export interface QueueStoreHarness {
|
|
13
|
+
/** A new store, empty. */
|
|
14
|
+
readonly create: CreateQueueStore;
|
|
15
|
+
/** Another handle on the same queue; without it the share tests are skipped. */
|
|
16
|
+
readonly share?: ShareStore | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Closes a store `create` made, after the test that used it. Handles
|
|
19
|
+
* returned by `share` are the harness's to close.
|
|
20
|
+
*/
|
|
21
|
+
readonly dispose?: ((store: QueueStore) => Promise<void> | void) | undefined;
|
|
22
|
+
}
|
|
23
|
+
/** What the suite is handed: how to build a store, and what the harness can do beyond that. */
|
|
24
|
+
export interface StoreFactories {
|
|
25
|
+
readonly create: CreateQueueStore;
|
|
26
|
+
/** Absent when the harness has no second handle: the share tests are skipped. */
|
|
27
|
+
readonly share?: ShareStore | undefined;
|
|
28
|
+
}
|
|
29
|
+
export declare const bytes: (text: string) => Uint8Array<ArrayBuffer>;
|
|
30
|
+
export declare const T0: number;
|
|
31
|
+
export declare const MINUTE = 60000;
|
|
32
|
+
export declare const lease: {
|
|
33
|
+
leaseMs: number;
|
|
34
|
+
};
|
|
35
|
+
export declare const result: (overrides?: Partial<AttemptResult>) => AttemptResult;
|
|
36
|
+
/** A new item from mary to two recipients, created at `T0` unless told otherwise. */
|
|
37
|
+
export declare function entry(overrides?: Partial<NewQueueItem>): NewQueueItem;
|
|
38
|
+
/** Expects a promise to reject with a QueueError of this code. */
|
|
39
|
+
export declare function rejects(promise: Promise<unknown>, code: string): Promise<void>;
|
|
40
|
+
//# sourceMappingURL=setup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/conformance/setup.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAErE,0BAA0B;AAC1B,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,GAAG,UAAU,CAAC;AAEtE;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,KAAK,EAAE,UAAU,KAAK,UAAU,CAAC;AAE3D,2CAA2C;AAC3C,MAAM,WAAW,iBAAiB;IACjC,0BAA0B;IAC1B,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,gFAAgF;IAChF,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IACxC;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,SAAS,CAAC;CAC7E;AAED,+FAA+F;AAC/F,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,iFAAiF;IACjF,QAAQ,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CACxC;AAED,eAAO,MAAM,KAAK,GAAI,MAAM,MAAM,4BAAmC,CAAC;AAEtE,eAAO,MAAM,EAAE,QAA2B,CAAC;AAC3C,eAAO,MAAM,MAAM,QAAS,CAAC;AAE7B,eAAO,MAAM,KAAK;;CAA2B,CAAC;AAE9C,eAAO,MAAM,MAAM,GAClB,YAAW,OAAO,CAAC,aAAa,CAAM,KACpC,aAOD,CAAC;AAEH,qFAAqF;AACrF,wBAAgB,KAAK,CAAC,SAAS,GAAE,OAAO,CAAC,YAAY,CAAM,GAAG,YAAY,CAQzE;AAED,kEAAkE;AAClE,wBAAsB,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,iBAEpE"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,6 @@ export { DEFAULT_MAX_RETURN } from './dsn/policy';
|
|
|
6
6
|
export { QueueError, type QueueErrorCode } from './errors';
|
|
7
7
|
export type { MessageSource, QueueEnvelope } from './queue/envelope';
|
|
8
8
|
export type { DeferredEvent, DsnEvent, QueueErrorEvent, QueueEvents, QueueListener, RecipientEvent, } from './queue/events';
|
|
9
|
-
export type { Clock, DsnOptions, QueueLimits, QueueOptions, RetrySchedule, Route, Sender, Smarthost, } from './queue/options';
|
|
9
|
+
export type { Clock, DsnOptions, QueueLimits, QueueOptions, RetrySchedule, ReuseOptions, Route, Sender, Smarthost, } from './queue/options';
|
|
10
10
|
export { createQueue, type Queue } from './queue/queue';
|
|
11
11
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,YAAY,EACX,UAAU,EACV,aAAa,EACb,YAAY,EACZ,SAAS,EACT,UAAU,EACV,WAAW,EACX,KAAK,EACL,YAAY,EACZ,WAAW,EACX,iBAAiB,EACjB,SAAS,EACT,gBAAgB,EAChB,YAAY,EACZ,eAAe,EACf,cAAc,EACd,eAAe,EACf,eAAe,EACf,OAAO,GACP,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACN,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,YAAY,GACZ,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACN,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,SAAS,GACT,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAC3D,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACrE,YAAY,EACX,aAAa,EACb,QAAQ,EACR,eAAe,EACf,WAAW,EACX,aAAa,EACb,cAAc,GACd,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACX,KAAK,EACL,UAAU,EACV,WAAW,EACX,YAAY,EACZ,aAAa,EACb,KAAK,EACL,MAAM,EACN,SAAS,GACT,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,WAAW,EAAE,KAAK,KAAK,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,YAAY,EACX,UAAU,EACV,aAAa,EACb,YAAY,EACZ,SAAS,EACT,UAAU,EACV,WAAW,EACX,KAAK,EACL,YAAY,EACZ,WAAW,EACX,iBAAiB,EACjB,SAAS,EACT,gBAAgB,EAChB,YAAY,EACZ,eAAe,EACf,cAAc,EACd,eAAe,EACf,eAAe,EACf,OAAO,GACP,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACN,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,YAAY,GACZ,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACN,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,SAAS,GACT,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAC3D,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACrE,YAAY,EACX,aAAa,EACb,QAAQ,EACR,eAAe,EACf,WAAW,EACX,aAAa,EACb,cAAc,GACd,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACX,KAAK,EACL,UAAU,EACV,WAAW,EACX,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,KAAK,EACL,MAAM,EACN,SAAS,GACT,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,WAAW,EAAE,KAAK,KAAK,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -471,9 +471,6 @@ class Events {
|
|
|
471
471
|
}
|
|
472
472
|
}
|
|
473
473
|
|
|
474
|
-
// src/queue/settings/index.ts
|
|
475
|
-
import { sendMail } from "@bumail/smtp/client";
|
|
476
|
-
|
|
477
474
|
// src/queue/settings/numbers.ts
|
|
478
475
|
var MINUTE = 60000;
|
|
479
476
|
var HOUR = 60 * MINUTE;
|
|
@@ -606,6 +603,57 @@ function checkRoutes(options) {
|
|
|
606
603
|
}
|
|
607
604
|
}
|
|
608
605
|
|
|
606
|
+
// src/queue/settings/sender.ts
|
|
607
|
+
import {
|
|
608
|
+
createSmtpPool,
|
|
609
|
+
SmtpError,
|
|
610
|
+
sendMail
|
|
611
|
+
} from "@bumail/smtp/client";
|
|
612
|
+
function reuseOf(options) {
|
|
613
|
+
const { reuse } = options;
|
|
614
|
+
if (reuse === undefined)
|
|
615
|
+
return {};
|
|
616
|
+
if (reuse === false)
|
|
617
|
+
return false;
|
|
618
|
+
if (typeof reuse !== "object" || reuse === null) {
|
|
619
|
+
throw invalid(`reuse must be false or an object of limits, not ${String(reuse)}`);
|
|
620
|
+
}
|
|
621
|
+
const { maxMessages, idleSeconds } = reuse;
|
|
622
|
+
return {
|
|
623
|
+
...maxMessages === undefined ? {} : { maxMessages },
|
|
624
|
+
...idleSeconds === undefined ? {} : { idleSeconds }
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
function poolOf(reuse) {
|
|
628
|
+
try {
|
|
629
|
+
return createSmtpPool(reuse);
|
|
630
|
+
} catch (error) {
|
|
631
|
+
if (error instanceof SmtpError && error.code === "INVALID_OPTION") {
|
|
632
|
+
throw invalid(`reuse.${error.message.replace(/^createSmtpPool\(\): /, "")}`);
|
|
633
|
+
}
|
|
634
|
+
throw error;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
function senderOf(options) {
|
|
638
|
+
const reuse = reuseOf(options);
|
|
639
|
+
let pool = reuse === false ? undefined : poolOf(reuse);
|
|
640
|
+
if (options.send)
|
|
641
|
+
return { send: options.send, close: async () => {} };
|
|
642
|
+
if (reuse === false)
|
|
643
|
+
return { send: sendMail, close: async () => {} };
|
|
644
|
+
return {
|
|
645
|
+
send: (message, sendOptions) => {
|
|
646
|
+
pool ??= poolOf(reuse);
|
|
647
|
+
return pool.sendMail(message, sendOptions);
|
|
648
|
+
},
|
|
649
|
+
async close() {
|
|
650
|
+
const closing = pool;
|
|
651
|
+
pool = undefined;
|
|
652
|
+
await closing?.close();
|
|
653
|
+
}
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
|
|
609
657
|
// src/queue/settings/index.ts
|
|
610
658
|
var HOSTNAME = /^(?=.{1,253}$)[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?)*$/;
|
|
611
659
|
function settingsOf(options) {
|
|
@@ -623,6 +671,7 @@ function settingsOf(options) {
|
|
|
623
671
|
checkSessions(options);
|
|
624
672
|
const owner = checkOwner(options.owner ?? crypto.randomUUID());
|
|
625
673
|
const clock = options.clock;
|
|
674
|
+
const sender = senderOf(options);
|
|
626
675
|
return {
|
|
627
676
|
options,
|
|
628
677
|
hostname,
|
|
@@ -639,7 +688,8 @@ function settingsOf(options) {
|
|
|
639
688
|
pollInterval: numberOf("pollInterval", options.pollInterval, 5000, 1, MAX_TIMER),
|
|
640
689
|
owner,
|
|
641
690
|
now: clock ? () => clock.now() : Date.now,
|
|
642
|
-
send:
|
|
691
|
+
send: sender.send,
|
|
692
|
+
close: sender.close,
|
|
643
693
|
random: options.random ?? Math.random
|
|
644
694
|
};
|
|
645
695
|
}
|
|
@@ -812,6 +862,7 @@ function doubtful(updates, error, key) {
|
|
|
812
862
|
const refused = new Set((error.rejected ?? []).map((r) => r.recipient).filter((a) => !taken.has(a)));
|
|
813
863
|
return updates.map((u) => refused.has(u.address) ? u : marked(u, key));
|
|
814
864
|
}
|
|
865
|
+
var reusedOf = (a) => a.reused === undefined ? {} : { reused: a.reused };
|
|
815
866
|
async function session(ctx, item, message, domain, group, route, override) {
|
|
816
867
|
const { settings } = ctx;
|
|
817
868
|
const max = settings.limits.maxReplyText;
|
|
@@ -821,7 +872,10 @@ async function session(ctx, item, message, domain, group, route, override) {
|
|
|
821
872
|
...dsnOptionsOf(item, group)
|
|
822
873
|
};
|
|
823
874
|
const result = await settings.send(message, options);
|
|
824
|
-
return {
|
|
875
|
+
return {
|
|
876
|
+
updates: outcomesOf(result, group, max),
|
|
877
|
+
...result.reused === undefined ? {} : { reused: result.reused }
|
|
878
|
+
};
|
|
825
879
|
} catch (error) {
|
|
826
880
|
if (isRouteError(error))
|
|
827
881
|
ctx.events.emit("error", { error, id: item.id });
|
|
@@ -850,6 +904,7 @@ async function fallBackAtOnce(ctx, item, message, domain, first, fallback, used)
|
|
|
850
904
|
used.set(address, {
|
|
851
905
|
route: "smarthost",
|
|
852
906
|
fallback: true,
|
|
907
|
+
...reusedOf(second),
|
|
853
908
|
...u.reply ? { mxReply: u.reply } : {}
|
|
854
909
|
});
|
|
855
910
|
}
|
|
@@ -874,6 +929,13 @@ async function deliverHeld(ctx, item, message, domain, held, fallback, used) {
|
|
|
874
929
|
used.set(address, { route: "smarthost", fallback: true });
|
|
875
930
|
}
|
|
876
931
|
const second = await session(ctx, item, message, domain, held, fallback, fallback);
|
|
932
|
+
for (const address of held) {
|
|
933
|
+
used.set(address, {
|
|
934
|
+
route: "smarthost",
|
|
935
|
+
fallback: true,
|
|
936
|
+
...reusedOf(second)
|
|
937
|
+
});
|
|
938
|
+
}
|
|
877
939
|
return second.updates.map(stillHeld);
|
|
878
940
|
}
|
|
879
941
|
|
|
@@ -903,6 +965,9 @@ async function attemptGroup(ctx, item, message, domain, recipients, used) {
|
|
|
903
965
|
let updates = [];
|
|
904
966
|
if (viaMx.length > 0) {
|
|
905
967
|
const first = await session(ctx, item, message, domain, viaMx, route);
|
|
968
|
+
for (const address of viaMx) {
|
|
969
|
+
used.set(address, { route: label, ...reusedOf(first) });
|
|
970
|
+
}
|
|
906
971
|
updates = fallback !== undefined && route === "mx" ? await fallBackAtOnce(ctx, item, message, domain, first, fallback, used) : first.updates;
|
|
907
972
|
}
|
|
908
973
|
return [...kept, ...updates, ...bad.map(badAddress)];
|
|
@@ -1423,8 +1488,12 @@ class Worker {
|
|
|
1423
1488
|
this.#wake?.();
|
|
1424
1489
|
await this.#loop;
|
|
1425
1490
|
await Promise.all(this.#inFlight);
|
|
1426
|
-
|
|
1427
|
-
|
|
1491
|
+
try {
|
|
1492
|
+
await this.#ctx.settings.close();
|
|
1493
|
+
} finally {
|
|
1494
|
+
this.#loop = undefined;
|
|
1495
|
+
this.#stopping = false;
|
|
1496
|
+
}
|
|
1428
1497
|
if (this.#restart) {
|
|
1429
1498
|
this.#restart = false;
|
|
1430
1499
|
this.start();
|
|
@@ -1476,5 +1545,5 @@ export {
|
|
|
1476
1545
|
failedDsn
|
|
1477
1546
|
};
|
|
1478
1547
|
|
|
1479
|
-
//# debugId=
|
|
1548
|
+
//# debugId=C2B635B10EF5403B64756E2164756E21
|
|
1480
1549
|
//# sourceMappingURL=index.js.map
|