@bumail/queue 0.8.0 → 0.9.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.
Files changed (68) hide show
  1. package/README.md +25 -1
  2. package/dist/chunks/{index-e5wa9f3j.js → index-9yp15arf.js} +91 -21
  3. package/dist/chunks/index-9yp15arf.js.map +14 -0
  4. package/dist/chunks/index-r8wb840a.js +24 -0
  5. package/dist/chunks/index-r8wb840a.js.map +10 -0
  6. package/dist/contract/checks.d.ts +2 -6
  7. package/dist/contract/checks.d.ts.map +1 -1
  8. package/dist/contract/dsn-checks.d.ts +4 -0
  9. package/dist/contract/dsn-checks.d.ts.map +1 -0
  10. package/dist/contract/dsn-column.d.ts +12 -0
  11. package/dist/contract/dsn-column.d.ts.map +1 -0
  12. package/dist/contract/storable.d.ts +7 -0
  13. package/dist/contract/storable.d.ts.map +1 -0
  14. package/dist/contract/types.d.ts +42 -0
  15. package/dist/contract/types.d.ts.map +1 -1
  16. package/dist/dsn/build.d.ts +2 -0
  17. package/dist/dsn/build.d.ts.map +1 -1
  18. package/dist/dsn/delivered.d.ts +39 -0
  19. package/dist/dsn/delivered.d.ts.map +1 -0
  20. package/dist/dsn/fields.d.ts +11 -2
  21. package/dist/dsn/fields.d.ts.map +1 -1
  22. package/dist/dsn/human.d.ts +7 -0
  23. package/dist/dsn/human.d.ts.map +1 -0
  24. package/dist/dsn/policy.d.ts +17 -0
  25. package/dist/dsn/policy.d.ts.map +1 -0
  26. package/dist/index.d.ts +2 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +384 -254
  29. package/dist/index.js.map +18 -15
  30. package/dist/memory/index.js +1 -1
  31. package/dist/postgres/index.js +17 -9
  32. package/dist/postgres/index.js.map +6 -6
  33. package/dist/postgres/rows.d.ts +6 -1
  34. package/dist/postgres/rows.d.ts.map +1 -1
  35. package/dist/postgres/schema.d.ts.map +1 -1
  36. package/dist/postgres/statements.d.ts +9 -8
  37. package/dist/postgres/statements.d.ts.map +1 -1
  38. package/dist/postgres/store.d.ts.map +1 -1
  39. package/dist/queue/envelope.d.ts +24 -4
  40. package/dist/queue/envelope.d.ts.map +1 -1
  41. package/dist/queue/events.d.ts +2 -1
  42. package/dist/queue/events.d.ts.map +1 -1
  43. package/dist/queue/notify.d.ts +6 -3
  44. package/dist/queue/notify.d.ts.map +1 -1
  45. package/dist/queue/outcome.d.ts.map +1 -1
  46. package/dist/queue/queue.d.ts.map +1 -1
  47. package/dist/queue/route.d.ts +7 -0
  48. package/dist/queue/route.d.ts.map +1 -1
  49. package/dist/queue/session.d.ts.map +1 -1
  50. package/dist/queue/settle.d.ts.map +1 -1
  51. package/dist/redis/index.js +26 -13
  52. package/dist/redis/index.js.map +5 -5
  53. package/dist/redis/items.d.ts +2 -0
  54. package/dist/redis/items.d.ts.map +1 -1
  55. package/dist/redis/scripts.d.ts +1 -1
  56. package/dist/redis/scripts.d.ts.map +1 -1
  57. package/dist/redis/store.d.ts.map +1 -1
  58. package/dist/sqlite/index.js +15 -8
  59. package/dist/sqlite/index.js.map +5 -5
  60. package/dist/sqlite/rows.d.ts +2 -1
  61. package/dist/sqlite/rows.d.ts.map +1 -1
  62. package/dist/sqlite/schema.d.ts.map +1 -1
  63. package/dist/sqlite/store.d.ts.map +1 -1
  64. package/docs/guide.md +72 -3
  65. package/docs/roadmap.md +8 -3
  66. package/docs/troubleshooting.md +41 -0
  67. package/package.json +3 -3
  68. package/dist/chunks/index-e5wa9f3j.js.map +0 -12
@@ -11,6 +11,7 @@ parts shown as … vary.
11
11
  - [`QueueError: The queue holds … items, its limit`](#queueerror-the-queue-holds--items-its-limit)
12
12
  - [`QueueError: … must be an address, local@domain, not "…"`](#queueerror--must-be-an-address-localdomain-not-)
13
13
  - [`QueueError: to must hold one recipient or more`](#queueerror-to-must-hold-one-recipient-or-more)
14
+ - [`QueueError: ret must be 'FULL' or 'HDRS'`, and the other DSN parameters](#queueerror-ret-must-be-full-or-hdrs-and-the-other-dsn-parameters)
14
15
  - [`QueueError: The envelope is an object: { from, to }`](#queueerror-the-envelope-is-an-object--from-to-)
15
16
  - [`QueueError: The message is a Uint8Array, a string or a ReadableStream<Uint8Array>`](#queueerror-the-message-is-a-uint8array-a-string-or-a-readablestreamuint8array)
16
17
  - [`QueueError: The message stream must give Uint8Array chunks`](#queueerror-the-message-stream-must-give-uint8array-chunks)
@@ -49,6 +50,7 @@ parts shown as … vary.
49
50
  - [`QueueError: A SQLite queue store needs a directory`](#queueerror-a-sqlite-queue-store-needs-a-directory)
50
51
  - [`QueueError: The queue at "…" cannot be opened: …`](#queueerror-the-queue-at--cannot-be-opened-)
51
52
  - [`QueueError: The database is at schema version …, newer than this store's …`](#queueerror-the-database-is-at-schema-version--newer-than-this-stores-)
53
+ - [`QueueError: The database is at schema version 2, newer than this store's 1`](#queueerror-the-database-is-at-schema-version-2-newer-than-this-stores-1)
52
54
  - [`QueueError: busyTimeout must be an integer of at least 0, not …`](#queueerror-busytimeout-must-be-an-integer-of-at-least-0-not-)
53
55
  - [`QueueError: The queue store is closed`](#queueerror-the-queue-store-is-closed)
54
56
 
@@ -148,6 +150,36 @@ For a bounce of your own, the null sender is `from: ''`.
148
150
  **Code:** `INVALID`. `to` is an empty array, or not an array nor a
149
151
  string. Give one address or more.
150
152
 
153
+ ### `QueueError: ret must be 'FULL' or 'HDRS'`, and the other DSN parameters
154
+
155
+ **Code:** `INVALID`. The envelope's DSN parameters (RFC 3461) are
156
+ checked at `enqueue`, and by every store at `add`, never at delivery:
157
+
158
+ - `ret must be 'FULL' or 'HDRS'`: `RET` is one of the two.
159
+ - `envid must be 1 to 100 printable ASCII characters, at most 100 once encoded as xtext`: the decoded
160
+ `ENVID`, with no control character, whose xtext is 100 characters or fewer
161
+ (RFC 3461 §4.4).
162
+ - `recipients names "…", which is not in to` and `recipients names … twice`:
163
+ each entry of `recipients` matches one address of `to`, once.
164
+ - `notify of … must be 'NEVER' or a list of 'SUCCESS', 'FAILURE', 'DELAY', each once`:
165
+ `NEVER` alone, or a non-empty list without repeats.
166
+ - `orcpt of … must be { type, address }`: both strings, the address not
167
+ empty, or the type is not a token (letters, digits, hyphens).
168
+ - `orcpt of … is over 500 characters once encoded`: `type;` and the xtext of
169
+ the address take at most 500 characters (RFC 3461 §4.2).
170
+
171
+ ```ts
172
+ await queue.enqueue(message, {
173
+ from: 'mary@example.net',
174
+ to: ['joe@example.com'],
175
+ ret: 'HDRS',
176
+ envid: 'QQ314159',
177
+ recipients: [{ address: 'joe@example.com', notify: ['SUCCESS', 'FAILURE'] }],
178
+ });
179
+ ```
180
+
181
+ The SMTP server's `Envelope.recipients` is already in this shape.
182
+
151
183
  ### `QueueError: The envelope is an object: { from, to }`
152
184
 
153
185
  **Code:** `INVALID`. `enqueue`'s second argument is missing.
@@ -470,6 +502,15 @@ should read the queue.
470
502
  prefix's keys, whose layout version is `<prefix>schema`): upgrade this
471
503
  one.
472
504
 
505
+ ### `QueueError: The database is at schema version 2, newer than this store's 1`
506
+
507
+ **Code:** `INVALID`. A worker of a version before the DSN parameters was
508
+ started on a database that a newer one migrated (SQLite and PostgreSQL;
509
+ the second migration adds the `dsn` column). Upgrade it. When rolling
510
+ out, stop every old worker first: one still running is not refused, but
511
+ it ignores `NOTIFY`, `RET` and `ENVID` and drops the recipients' `notify`
512
+ and `orcpt` from the items it completes.
513
+
473
514
  ### `QueueError: busyTimeout must be an integer of at least 0, not …`
474
515
 
475
516
  **Code:** `INVALID`. Milliseconds a write waits for another process.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bumail/queue",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "The outbound mail queue of a mail server: every recipient's state, retries with back-off, delivery status notifications (RFC 3464), leases for several workers, and a memory, a bun:sqlite, a PostgreSQL (Bun.sql) and a Redis (Bun.redis) store, delivering through @bumail/smtp/client",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -67,12 +67,12 @@
67
67
  "devDependencies": {
68
68
  "@bumail/dns": "^0.2.1",
69
69
  "@bumail/mime": "^0.2.0",
70
- "@bumail/smtp": "^0.9.0",
70
+ "@bumail/smtp": "^0.10.0",
71
71
  "@types/bun": "^1.4.2"
72
72
  },
73
73
  "peerDependencies": {
74
74
  "@bumail/mime": "^0.2.0",
75
- "@bumail/smtp": "^0.9.0",
75
+ "@bumail/smtp": "^0.10.0",
76
76
  "typescript": "^6.0.3 || ^7.0.0"
77
77
  },
78
78
  "peerDependenciesMeta": {
@@ -1,12 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/errors.ts", "../src/text.ts", "../src/contract/checks.ts"],
4
- "sourcesContent": [
5
- "/**\n * Why the queue or a store refused a call. `INVALID`: an option or an\n * argument it cannot take. `MESSAGE_TOO_BIG`, `TOO_MANY_RECIPIENTS` and\n * `QUEUE_FULL`: a limit `enqueue` enforces. `CLOSED`: a store used after\n * `close()`. `LEASE_LOST`: given to the `error` event when another worker\n * took an item before its outcome was recorded — or, once the lease's\n * expiry passed, when the item is gone: lost or cancelled.\n * `MESSAGE_UNREADABLE`: given to the `error` event when the store holds an\n * item but not its message, whose pending recipients then fail.\n */\nexport type QueueErrorCode =\n\t| 'INVALID'\n\t| 'MESSAGE_TOO_BIG'\n\t| 'TOO_MANY_RECIPIENTS'\n\t| 'QUEUE_FULL'\n\t| 'CLOSED'\n\t| 'LEASE_LOST'\n\t| 'MESSAGE_UNREADABLE';\n\n/** Thrown by the queue and by every store, whichever answers the contract, with the same codes. */\nexport class QueueError extends Error {\n\toverride readonly name = 'QueueError';\n\treadonly code: QueueErrorCode;\n\n\tconstructor(code: QueueErrorCode, message: string) {\n\t\tsuper(message);\n\t\tthis.code = code;\n\t}\n}\n\n/** `INVALID`, for an option or an argument. */\nexport const invalid = (message: string) => new QueueError('INVALID', message);\n",
6
- "/** Text from the other side, made safe to keep and to write in a header field. */\n\n/** C0, DEL and C1: every control character, CR, LF and TAB included. */\nconst isControl = (code: number) =>\n\tcode < 0x20 || (code >= 0x7f && code <= 0x9f);\n\n/** Whether the text holds a control character. */\nexport function hasControl(text: string): boolean {\n\tfor (let i = 0; i < text.length; i++) {\n\t\tif (isControl(text.charCodeAt(i))) return true;\n\t}\n\treturn false;\n}\n\n/** Each control character replaced by `by`. */\nexport function replaceControls(text: string, by: string): string {\n\tlet out = '';\n\tfor (let i = 0; i < text.length; i++) {\n\t\tout += isControl(text.charCodeAt(i)) ? by : (text[i] as string);\n\t}\n\treturn out;\n}\n\n/**\n * Every control character (CR and LF included) as a space, a lone\n * surrogate as U+FFFD, runs of space as one, cut at `max` characters with\n * `...`, never inside a surrogate pair: what a reply leaves in an item,\n * an event and a DSN.\n */\nexport function cleanText(text: string, max: number): string {\n\tconst clean = replaceControls(text.toWellFormed(), ' ')\n\t\t.replace(/ {2,}/g, ' ')\n\t\t.trim();\n\treturn clean.length > max ? `${cut(clean, max - 3)}...` : clean;\n}\n\n/**\n * The first `length` UTF-16 units, never ending inside a surrogate pair:\n * a lone surrogate is text no store can keep (PostgreSQL's `jsonb` refuses\n * it), nor any UTF-8 encode.\n */\nfunction cut(text: string, length: number): string {\n\tconst head = text.slice(0, length);\n\tconst last = head.charCodeAt(head.length - 1);\n\treturn last >= 0xd800 && last <= 0xdbff ? head.slice(0, -1) : head;\n}\n\n/**\n * Text every store can keep: no NUL (PostgreSQL's `text` and `jsonb` hold\n * none) and no lone surrogate. `cleanText`'s output always is.\n */\nexport const isStorable = (text: string): boolean =>\n\ttext.isWellFormed() && !text.includes('\\0');\n\n/** `cleanText`, then anything not printable ASCII as `?`: for a `message/delivery-status` field. */\nexport const asciiText = (text: string, max: number): string =>\n\tcleanText(text, max).replace(/[^ -~]/g, '?');\n",
7
- "import { invalid } from '../errors';\nimport { isStorable } from '../text';\nimport type {\n\tAttemptResult,\n\tClaimRequest,\n\tDiagnostic,\n\tNewQueueItem,\n\tQueueItem,\n\tQueueListOptions,\n\tRecipientState,\n\tRecipientUpdate,\n} from './types';\n\n/** What every store checks the same way, so each refuses the same input with the same error. */\n\nconst isTime = (value: unknown): value is number =>\n\ttypeof value === 'number' && Number.isFinite(value);\n\n/** A time in milliseconds since the epoch, or `INVALID` naming it. */\nexport function checkTime(name: string, value: unknown): number {\n\tif (!isTime(value)) throw invalid(`${name} must be a finite number`);\n\treturn value;\n}\n\n/**\n * Text a store keeps: refused, the same way by every store, when it holds\n * a NUL or a lone surrogate, which PostgreSQL cannot keep and no UTF-8\n * encode can carry.\n */\nexport function checkStorable(name: string, text: string): void {\n\tif (!isStorable(text)) {\n\t\tthrow invalid(\n\t\t\t`${name} holds a NUL or a lone surrogate, which a store cannot keep`,\n\t\t);\n\t}\n}\n\n/** A lease owner: a non-empty string. */\nexport function checkOwner(owner: unknown): string {\n\tif (typeof owner !== 'string' || owner === '') {\n\t\tthrow invalid('owner must be a non-empty string');\n\t}\n\tcheckStorable('owner', owner);\n\treturn owner;\n}\n\n/** Every text of a recipient's outcome. */\nfunction checkUpdate(update: RecipientUpdate): void {\n\tcheckStorable('A recipient', update.address);\n\tfor (const key of ['mayHaveReachedMx', 'mayHaveReachedFallback'] as const) {\n\t\tconst mark = update[key] as unknown;\n\t\tif (mark !== undefined && mark !== true) {\n\t\t\tthrow invalid(`${key} must be true or absent`);\n\t\t}\n\t}\n\tconst reply = update.reply as Partial<Diagnostic> | undefined;\n\tif (reply === undefined) return;\n\tif (\n\t\ttypeof reply !== 'object' ||\n\t\treply === null ||\n\t\ttypeof reply.text !== 'string'\n\t) {\n\t\tthrow invalid('A reply is an object with a text');\n\t}\n\tcheckStorable('reply.text', reply.text);\n\tfor (const name of ['status', 'host'] as const) {\n\t\tconst value = reply[name];\n\t\tif (value === undefined) continue;\n\t\tif (typeof value !== 'string')\n\t\t\tthrow invalid(`reply.${name} must be a string`);\n\t\tcheckStorable(`reply.${name}`, value);\n\t}\n}\n\nexport function checkNewItem(item: NewQueueItem): void {\n\tif (typeof item !== 'object' || item === null) {\n\t\tthrow invalid('A new item is an object');\n\t}\n\tif (typeof item.from !== 'string') throw invalid('from must be a string');\n\tcheckStorable('from', item.from);\n\tconst to = item.to as unknown;\n\tif (\n\t\t!Array.isArray(to) ||\n\t\tto.length === 0 ||\n\t\t!to.every((a) => typeof a === 'string' && a !== '')\n\t) {\n\t\tthrow invalid('to must be a non-empty array of addresses');\n\t}\n\tfor (const address of to as string[]) checkStorable('A recipient', address);\n\tif (!(item.message instanceof Uint8Array)) {\n\t\tthrow invalid('message must be a Uint8Array');\n\t}\n\tcheckTime('createdAt', item.createdAt);\n}\n\n/** `maxItems`: a positive integer, or nothing. */\nexport function checkMaxItems(max: unknown): number | undefined {\n\tif (max === undefined) return undefined;\n\tif (typeof max !== 'number' || !Number.isInteger(max) || max < 1) {\n\t\tthrow invalid(`maxItems must be an integer of at least 1, not ${max}`);\n\t}\n\treturn max;\n}\n\nexport function checkClaim(request: ClaimRequest): void {\n\tif (typeof request !== 'object' || request === null) {\n\t\tthrow invalid('A claim is an object');\n\t}\n\tcheckOwner(request.owner);\n\tcheckTime('now', request.now);\n\tif (!isTime(request.leaseMs) || request.leaseMs <= 0) {\n\t\tthrow invalid('leaseMs must be a positive number');\n\t}\n}\n\nexport function checkResult(result: AttemptResult): void {\n\tif (typeof result !== 'object' || result === null) {\n\t\tthrow invalid('An attempt result is an object');\n\t}\n\tcheckTime('now', result.now);\n\tcheckTime('nextAttemptAt', result.nextAttemptAt);\n\tif (!Number.isInteger(result.attempts) || result.attempts < 0) {\n\t\tthrow invalid('attempts must be an integer of at least 0');\n\t}\n\tif (typeof result.delayNotified !== 'boolean') {\n\t\tthrow invalid('delayNotified must be true or false');\n\t}\n\tconst statuses = ['delivered', 'deferred', 'failed'];\n\tif (\n\t\t!Array.isArray(result.recipients) ||\n\t\t!result.recipients.every(\n\t\t\t(u) => typeof u?.address === 'string' && statuses.includes(u.status),\n\t\t)\n\t) {\n\t\tthrow invalid(\n\t\t\t'recipients must be an array of { address, status: delivered, deferred or failed }',\n\t\t);\n\t}\n\tfor (const update of result.recipients) checkUpdate(update);\n}\n\n/** The offset and limit of a list: integers, the limit 1 to 1000. */\nexport function checkList(options: QueueListOptions = {}): {\n\toffset: number;\n\tlimit: number;\n} {\n\tconst offset = options.offset ?? 0;\n\tconst limit = options.limit ?? 100;\n\tif (!Number.isInteger(offset) || offset < 0) {\n\t\tthrow invalid(`offset must be an integer of at least 0, not ${offset}`);\n\t}\n\tif (!Number.isInteger(limit) || limit < 1 || limit > 1000) {\n\t\tthrow invalid(`limit must be an integer from 1 to 1000, not ${limit}`);\n\t}\n\treturn { offset, limit };\n}\n\n/** Every recipient is `delivered` or `failed`. */\nexport const isDone = (item: Pick<QueueItem, 'recipients'>): boolean =>\n\titem.recipients.every(\n\t\t(r) => r.status === 'delivered' || r.status === 'failed',\n\t);\n\n/**\n * The item once an attempt is recorded: its recipients' outcomes, a final\n * state never changed again, the schedule, and no lease.\n */\nexport function applyAttempt(\n\titem: QueueItem,\n\tresult: AttemptResult,\n): QueueItem {\n\tconst updates = new Map(result.recipients.map((u) => [u.address, u]));\n\tconst recipients = item.recipients.map((r): RecipientState => {\n\t\tconst update = updates.get(r.address);\n\t\tif (!update || r.status === 'delivered' || r.status === 'failed') return r;\n\t\treturn {\n\t\t\taddress: r.address,\n\t\t\tstatus: update.status,\n\t\t\t...(update.reply ? { reply: update.reply } : {}),\n\t\t\tupdatedAt: result.now,\n\t\t\t...(r.mayHaveReachedMx || update.mayHaveReachedMx\n\t\t\t\t? { mayHaveReachedMx: true as const }\n\t\t\t\t: {}),\n\t\t\t...(r.mayHaveReachedFallback || update.mayHaveReachedFallback\n\t\t\t\t? { mayHaveReachedFallback: true as const }\n\t\t\t\t: {}),\n\t\t};\n\t});\n\tconst { lease: _, ...rest } = item;\n\treturn {\n\t\t...rest,\n\t\trecipients,\n\t\tnextAttemptAt: result.nextAttemptAt,\n\t\tattempts: result.attempts,\n\t\tdelayNotified: result.delayNotified,\n\t};\n}\n\n/** A new item as a store first keeps it. */\nexport function newItem(id: string, item: NewQueueItem): QueueItem {\n\treturn {\n\t\tid,\n\t\tfrom: item.from,\n\t\trecipients: item.to.map((address) => ({ address, status: 'pending' })),\n\t\tsize: item.message.length,\n\t\tcreatedAt: item.createdAt,\n\t\tnextAttemptAt: item.createdAt,\n\t\tattempts: 0,\n\t\tdelayNotified: false,\n\t};\n}\n\n/** Whether a lease, if any, leaves the item free to claim at `now`. */\nexport const claimable = (item: QueueItem, now: number): boolean =>\n\titem.nextAttemptAt <= now &&\n\t(item.lease === undefined || item.lease.expiresAt <= now);\n"
8
- ],
9
- "mappings": "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",
10
- "debugId": "16AF1EB72729D01E64756E2164756E21",
11
- "names": []
12
- }