@thailandhosting/queue-service 0.0.0-stage → 1.1.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 CHANGED
@@ -1,3 +1,167 @@
1
- # Temporary Holding Version
1
+ # ThailandHosting Queue Service — Node.js SDK
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ TypeScript/JavaScript client for ThailandHosting Queue Service: message
4
+ queues that take slow work off your site or app. One side sends jobs,
5
+ workers receive them, and nothing is lost when a worker fails.
6
+
7
+ ```
8
+ npm install @thailandhosting/queue-service
9
+ ```
10
+
11
+ Node.js 18.17+. ESM with TypeScript types. No runtime dependencies: it uses
12
+ Node's built-in `fetch`.
13
+
14
+ ## Quick start
15
+
16
+ ```ts
17
+ import { Client } from '@thailandhosting/queue-service';
18
+
19
+ const client = new Client({
20
+ region: 'th-bkk-1',
21
+ accessKeyId: process.env.TH_QUEUE_ACCESS_KEY_ID!,
22
+ secretAccessKey: process.env.TH_QUEUE_SECRET_ACCESS_KEY!,
23
+ });
24
+ const jobs = client.queue('jobs');
25
+
26
+ // Send
27
+ await jobs.sendJSON({ orderId: 42 });
28
+ await jobs.send('plain text', { attributes: { kind: 'email' }, delay: 30 });
29
+ await jobs.sendBatch(['a', 'b', { body: 'c', attributes: { x: '1' } }]);
30
+
31
+ // A worker: long-polls, runs the handler, deletes what it handled.
32
+ const stop = new AbortController();
33
+ process.on('SIGTERM', () => stop.abort());
34
+ await jobs.process(async (m) => {
35
+ const { orderId } = m.json<{ orderId: number }>();
36
+ await ship(orderId); // throwing leaves the message to be retried
37
+ }, { concurrency: 8, retryDelay: 30, signal: stop.signal });
38
+ ```
39
+
40
+ Create access keys in the Console (Queue Service → Access keys). Reuse one
41
+ `Client`: it keeps connections open.
42
+
43
+ ## Using a service account
44
+
45
+ A program can use a service account's short-lived token instead of an access
46
+ key. Get it with the identity SDK and pass `tokenProvider` in place of the key:
47
+
48
+ ```ts
49
+ import { defaultCredential } from '@thailandhosting/identity';
50
+
51
+ const cred = defaultCredential();
52
+ const client = new Client({
53
+ region: 'th-bkk-1',
54
+ tokenProvider: () => cred.getToken(),
55
+ });
56
+ ```
57
+
58
+ The account needs a grant on the queue, set in the Console. Creating,
59
+ changing and deleting queues takes an access key allowed to manage them.
60
+
61
+ `tokenProvider` is called before every attempt, so a refreshed token is picked
62
+ up. Give a key or a token provider, not both. If it fails, nothing is sent or
63
+ retried: the call rejects with `TokenError`, and `process` stops and rejects
64
+ with it.
65
+
66
+ ## Receiving by hand
67
+
68
+ ```ts
69
+ const messages = await jobs.receive({ max: 10, wait: 20 });
70
+ for (const m of messages) {
71
+ try {
72
+ await handle(m.body);
73
+ await jobs.delete(m.receipt);
74
+ } catch {
75
+ // handed out again after the visibility timeout
76
+ }
77
+ }
78
+ ```
79
+
80
+ - `wait` long-polls up to 20 seconds for a first message, so an idle worker
81
+ makes few requests.
82
+ - A received message is hidden for the queue's visibility timeout (or
83
+ `visibilityTimeout`). Delete it with its receipt once handled; otherwise it
84
+ is handed out again. `m.receives` counts the deliveries.
85
+ - `changeVisibility(receipt, seconds)` keeps a message hidden longer while
86
+ you work on it, or with 0 releases it at once.
87
+ - `deleteBatch(receipts)` deletes up to 10 and reports each one in order.
88
+
89
+ Delivery is **at least once**: make handlers safe to run twice (check an
90
+ order ID before charging it, for example).
91
+
92
+ ## Queues
93
+
94
+ ```ts
95
+ const q = await client.putQueue('jobs', {
96
+ visibilityTimeout: 120,
97
+ maxReceives: 5,
98
+ deadLetterQueue: 'jobs-dlq', // create it first
99
+ });
100
+ console.log(q.created);
101
+
102
+ await client.listQueues();
103
+ const info = await client.getQueue('jobs'); // info.messages: visible, inFlight, delayed, oldestAge
104
+ await jobs.purge();
105
+ await client.deleteQueue('jobs');
106
+ ```
107
+
108
+ `putQueue` creates a queue or changes the given settings of an existing one
109
+ (the key must be allowed to manage queues). After `maxReceives` deliveries a
110
+ message moves to the dead-letter queue (or is dropped without one).
111
+
112
+ | Setting (seconds) | Default | Range |
113
+ | --- | --- | --- |
114
+ | `visibilityTimeout` | 30 | 0 – 43200 |
115
+ | `retention` | 345600 (4 days) | 60 – 1209600 |
116
+ | `delay` | 0 | 0 – 900 |
117
+ | `maxReceives` | 0 (never move) | 0 – 1000 |
118
+
119
+ Limits: a message is at most 256 KiB (body and up to 10 attributes), a batch
120
+ 10 messages and 1 MiB.
121
+
122
+ ## Errors
123
+
124
+ Error answers throw `QueueServiceError` (`NotFoundError` for 404) with the
125
+ service's `code`:
126
+
127
+ ```ts
128
+ import { NotFoundError, QueueServiceError } from '@thailandhosting/queue-service';
129
+
130
+ try {
131
+ await client.getQueue('missing');
132
+ } catch (e) {
133
+ if (e instanceof NotFoundError) console.log(e.code); // queue_not_found
134
+ }
135
+ ```
136
+
137
+ Common codes: `queue_not_found`, `forbidden`, `invalid_receipt`,
138
+ `queue_full`, `too_many_in_flight`, `too_large`, `project_suspended`,
139
+ `wrong_region`, `busy`. Busy or unreachable answers are retried with
140
+ exponential backoff (`maxRetries`, default 4). Every call takes an optional
141
+ `signal`.
142
+
143
+ ## Endpoint and region
144
+
145
+ The default endpoint is `https://queues.thailandhosting.com`. Set `region`
146
+ to the queues' region; without one the service finds each queue's region.
147
+ `endpoint` may also be given with the region already in it:
148
+ `https://queues.thailandhosting.com/th-bkk-1`.
149
+
150
+ ## Authentication
151
+
152
+ Every request is signed with HTTP Message Signatures (RFC 9421,
153
+ HMAC-SHA256 with the secret access key) over the method, host, path, query
154
+ and the body's Content-Digest (RFC 9530). The secret is never sent.
155
+ `signRequest` is exported for calling the API without the client.
156
+
157
+ ## Development
158
+
159
+ ```
160
+ npm test # unit tests; set TH_QUEUE_TEST_ENDPOINT, TH_QUEUE_TEST_REGION,
161
+ # TH_QUEUE_TEST_KEY and TH_QUEUE_TEST_SECRET for the round trip
162
+ npm run build
163
+ ```
164
+
165
+ ## License
166
+
167
+ MIT
@@ -0,0 +1,202 @@
1
+ export declare const VERSION = "1.0.0";
2
+ /** The Queue Service endpoint. */
3
+ export declare const DEFAULT_ENDPOINT = "https://queues.thailandhosting.com";
4
+ /** The most messages one send, receive or delete takes. */
5
+ export declare const MAX_BATCH = 10;
6
+ /** The longest a receive waits for a first message, in seconds. */
7
+ export declare const MAX_WAIT = 20;
8
+ export interface ClientOptions {
9
+ /** A Project access key from the Console (Queue Service → Access keys). Requests are signed with
10
+ * the secret (HTTP Message Signatures, RFC 9421); the secret itself is never sent.
11
+ * Give this or `tokenProvider`, not both. */
12
+ accessKeyId?: string;
13
+ secretAccessKey?: string;
14
+ /** Gives a service account's short-lived token, sent as `Authorization: Bearer` with no signature.
15
+ * Called before every attempt, retries included, so it should cache and refresh the token. */
16
+ tokenProvider?: () => Promise<string> | string;
17
+ /** The queues' region, e.g. `th-bkk-1`, appended to the endpoint. Without one the service finds
18
+ * each queue's region itself. */
19
+ region?: string;
20
+ /** Default https://queues.thailandhosting.com; it may already end in a region. */
21
+ endpoint?: string;
22
+ /** Retries after the service was busy or unreachable (default 4; 0 disables). */
23
+ maxRetries?: number;
24
+ /** Appended to the SDK's User-Agent. */
25
+ userAgent?: string;
26
+ /** A fetch implementation (default: the global fetch). */
27
+ fetch?: typeof fetch;
28
+ }
29
+ /** A queue's settings in whole seconds; left-out fields keep their value (or the default on a new queue). */
30
+ export interface QueueSettings {
31
+ /** How long a received message stays hidden before it is handed out again (0–43200, default 30). */
32
+ visibilityTimeout?: number;
33
+ /** How long a message is kept (60–1209600, default 345600: 4 days). */
34
+ retention?: number;
35
+ /** Hides every new message for this long (0–900, default 0). */
36
+ delay?: number;
37
+ /** Moves a message to `deadLetterQueue` (or drops it) after this many receives (0 never, up to 1000). */
38
+ maxReceives?: number;
39
+ /** A queue of the same Project ('' for none). */
40
+ deadLetterQueue?: string;
41
+ }
42
+ export interface QueueInfo {
43
+ name: string;
44
+ region: string;
45
+ createdAt: Date;
46
+ visibilityTimeout: number;
47
+ retention: number;
48
+ delay: number;
49
+ maxReceives: number;
50
+ deadLetterQueue?: string;
51
+ /** The backlog (getQueue and Queue.info only). */
52
+ messages?: QueueStats;
53
+ }
54
+ export interface QueueStats {
55
+ /** Ready to be received. */
56
+ visible: number;
57
+ /** Received, not yet deleted. */
58
+ inFlight: number;
59
+ /** Sent with a delay still running. */
60
+ delayed: number;
61
+ bytes: number;
62
+ /** Age of the oldest message, in seconds. */
63
+ oldestAge: number;
64
+ }
65
+ export interface SendOptions {
66
+ /** Travel with the message (string keys and values, up to 10). */
67
+ attributes?: Record<string, string>;
68
+ /** Hides the message for this many seconds first (at most 900; default: the queue's delay). */
69
+ delay?: number;
70
+ signal?: AbortSignal;
71
+ }
72
+ export interface OutgoingMessage {
73
+ body: string;
74
+ attributes?: Record<string, string>;
75
+ delay?: number;
76
+ }
77
+ export interface ReceiveOptions {
78
+ /** How many messages to take, 1 to 10 (default 1). */
79
+ max?: number;
80
+ /** Hide the received messages this many seconds instead of the queue's timeout (at least 1). */
81
+ visibilityTimeout?: number;
82
+ /** Long-poll up to this many seconds (at most 20) for a first message; 0 answers at once. */
83
+ wait?: number;
84
+ signal?: AbortSignal;
85
+ }
86
+ /** A received message. Delete it with its receipt once handled. */
87
+ export interface Message {
88
+ id: string;
89
+ receipt: string;
90
+ body: string;
91
+ attributes: Record<string, string>;
92
+ sentAt: Date;
93
+ /** How many times the message was handed out, this one included. */
94
+ receives: number;
95
+ firstReceivedAt: Date;
96
+ /** The body parsed as JSON. */
97
+ json<T = unknown>(): T;
98
+ }
99
+ export interface DeleteResult {
100
+ receipt: string;
101
+ deleted: boolean;
102
+ /** `invalid_receipt` or `unavailable` when not deleted. */
103
+ error?: string;
104
+ }
105
+ export interface ProcessOptions {
106
+ /** Messages handled at once (default 1). */
107
+ concurrency?: number;
108
+ /** Seconds each receive long-polls (default and at most 20). */
109
+ wait?: number;
110
+ /** Hide received messages this many seconds instead of the queue's timeout; make it longer than a handler takes. */
111
+ visibilityTimeout?: number;
112
+ /** Seconds a message whose handler failed stays hidden before it is handed out again
113
+ * (default: until its visibility timeout ends). */
114
+ retryDelay?: number;
115
+ /** Hears about handler failures, and errors of the service the worker rode out (message undefined). */
116
+ onError?: (err: unknown, message?: Message) => void;
117
+ /** Stops the worker; it resolves once the running handlers finish. */
118
+ signal?: AbortSignal;
119
+ }
120
+ /** Talks to Queue Service with one access key or service account. Reuse it: it keeps connections open. */
121
+ export declare class Client {
122
+ readonly baseUrl: string;
123
+ private readonly keyId;
124
+ private readonly secret;
125
+ private readonly tokenProvider?;
126
+ private readonly maxRetries;
127
+ private readonly userAgent;
128
+ private readonly fetch;
129
+ constructor(o: ClientOptions);
130
+ /** @internal One call: JSON in, JSON (or nothing) out. `path` is after /v1, already escaped. */
131
+ call<T = any>(method: string, path: string, payload?: unknown, signal?: AbortSignal): Promise<{
132
+ status: number;
133
+ data: T;
134
+ }>;
135
+ /** The queues the key can reach, by name. */
136
+ listQueues(opts?: {
137
+ signal?: AbortSignal;
138
+ }): Promise<QueueInfo[]>;
139
+ /** Creates a queue, or changes the given settings of the one by that name (the key must manage queues). */
140
+ putQueue(name: string, settings?: QueueSettings, opts?: {
141
+ signal?: AbortSignal;
142
+ }): Promise<QueueInfo & {
143
+ created: boolean;
144
+ }>;
145
+ /** A queue's settings and backlog. */
146
+ getQueue(name: string, opts?: {
147
+ signal?: AbortSignal;
148
+ }): Promise<QueueInfo>;
149
+ /** Deletes a queue with every message in it. */
150
+ deleteQueue(name: string, opts?: {
151
+ signal?: AbortSignal;
152
+ }): Promise<void>;
153
+ /** A handle for sending and receiving on one queue (it makes no call by itself). */
154
+ queue(name: string): Queue;
155
+ }
156
+ /** Sends and receives on one queue. */
157
+ export declare class Queue {
158
+ private readonly path;
159
+ private readonly client;
160
+ readonly name: string;
161
+ constructor(client: Client, name: string);
162
+ /** The queue's settings and backlog. */
163
+ info(opts?: {
164
+ signal?: AbortSignal;
165
+ }): Promise<QueueInfo>;
166
+ /** Sends one message and returns its ID. */
167
+ send(body: string, opts?: SendOptions): Promise<string>;
168
+ /** Sends a value encoded as JSON. */
169
+ sendJSON(value: unknown, opts?: SendOptions): Promise<string>;
170
+ /** Sends up to 10 messages in one request (all or none) and returns their IDs in order. */
171
+ sendBatch(messages: (OutgoingMessage | string)[], opts?: {
172
+ signal?: AbortSignal;
173
+ }): Promise<string[]>;
174
+ /** Takes up to `max` messages, hiding them for the visibility timeout; none when the queue is empty
175
+ * (after waiting up to `wait` seconds). */
176
+ receive(opts?: ReceiveOptions): Promise<Message[]>;
177
+ /** Deletes a handled message by its receipt (deleting it again is not an error). */
178
+ delete(receipt: string, opts?: {
179
+ signal?: AbortSignal;
180
+ }): Promise<void>;
181
+ /** Deletes up to 10 handled messages; each receipt gets a result, in the order given. */
182
+ deleteBatch(receipts: string[], opts?: {
183
+ signal?: AbortSignal;
184
+ }): Promise<DeleteResult[]>;
185
+ /** Hides a received message `timeout` seconds from now (to work on it longer), or with 0 hands it out again at once. */
186
+ changeVisibility(receipt: string, timeout: number, opts?: {
187
+ signal?: AbortSignal;
188
+ }): Promise<void>;
189
+ /** Removes every message of the queue. */
190
+ purge(opts?: {
191
+ signal?: AbortSignal;
192
+ }): Promise<void>;
193
+ /**
194
+ * A worker: receives with long polling and runs `handle` on each message, `concurrency` at a time,
195
+ * until `signal` aborts (it then resolves once the running handlers finish). A message whose handler
196
+ * resolves is deleted; one whose handler throws is left to be handed out again (after `retryDelay`
197
+ * when set), and after the queue's maxReceives it goes to its dead-letter queue. Busy or unreachable
198
+ * answers are retried with backoff; errors retrying cannot fix (a missing queue, a key that may not
199
+ * receive) reject.
200
+ */
201
+ process(handle: (message: Message) => unknown | Promise<unknown>, opts?: ProcessOptions): Promise<void>;
202
+ }
package/dist/client.js ADDED
@@ -0,0 +1,284 @@
1
+ import { NotFoundError, QueueServiceError, TokenError } from "./errors.js";
2
+ import { contentDigest, signRequest } from "./signing.js";
3
+ export const VERSION = '1.0.0';
4
+ /** The Queue Service endpoint. */
5
+ export const DEFAULT_ENDPOINT = 'https://queues.thailandhosting.com';
6
+ /** The most messages one send, receive or delete takes. */
7
+ export const MAX_BATCH = 10;
8
+ /** The longest a receive waits for a first message, in seconds. */
9
+ export const MAX_WAIT = 20;
10
+ const RETRY_STATUSES = new Set([502, 503, 504]);
11
+ const sleep = (ms, signal) => new Promise((resolve) => {
12
+ if (signal?.aborted)
13
+ return resolve();
14
+ const t = setTimeout(done, ms);
15
+ function done() {
16
+ clearTimeout(t);
17
+ signal?.removeEventListener('abort', done);
18
+ resolve();
19
+ }
20
+ signal?.addEventListener('abort', done, { once: true });
21
+ });
22
+ /** The pause before retry n: exponential from 100 ms, capped at 5 s, with jitter. */
23
+ const backoff = (n) => {
24
+ const d = Math.min(100 * 2 ** Math.min(n, 6), 5000);
25
+ return d / 2 + Math.random() * (d / 2);
26
+ };
27
+ /** The Authorization value from a token provider; its failure is a TokenError. */
28
+ async function bearer(provider) {
29
+ let token;
30
+ try {
31
+ token = await provider();
32
+ }
33
+ catch (err) {
34
+ throw new TokenError(`queue-service: tokenProvider failed: ${err instanceof Error ? err.message : err}`, err);
35
+ }
36
+ if (!token || typeof token !== 'string')
37
+ throw new TokenError('queue-service: tokenProvider gave no token');
38
+ return 'Bearer ' + token;
39
+ }
40
+ const toDate = (v) => (typeof v === 'string' ? new Date(v) : new Date(0));
41
+ function queueFromJSON(d) {
42
+ return { ...d, createdAt: toDate(d.createdAt) };
43
+ }
44
+ function messageFromJSON(d) {
45
+ return {
46
+ id: d.id, receipt: d.receipt, body: d.body ?? '', attributes: d.attributes ?? {}, sentAt: toDate(d.sentAt),
47
+ receives: d.receives ?? 0, firstReceivedAt: toDate(d.firstReceivedAt),
48
+ json() { return JSON.parse(this.body); },
49
+ };
50
+ }
51
+ /** Talks to Queue Service with one access key or service account. Reuse it: it keeps connections open. */
52
+ export class Client {
53
+ baseUrl; // up to and including /v1
54
+ keyId;
55
+ secret;
56
+ tokenProvider;
57
+ maxRetries;
58
+ userAgent;
59
+ fetch;
60
+ constructor(o) {
61
+ if (o?.tokenProvider) {
62
+ if (o.accessKeyId || o.secretAccessKey)
63
+ throw new Error('queue-service: give an access key or tokenProvider, not both');
64
+ }
65
+ else if (!o?.accessKeyId || !o?.secretAccessKey) {
66
+ throw new Error('queue-service: accessKeyId and secretAccessKey, or tokenProvider, are required');
67
+ }
68
+ const u = new URL((o.endpoint || DEFAULT_ENDPOINT).replace(/\/+$/, ''));
69
+ if (u.protocol !== 'https:' && u.protocol !== 'http:')
70
+ throw new Error(`queue-service: invalid endpoint ${o.endpoint}`);
71
+ let path = u.pathname.replace(/\/+$/, '').replace(/\/v1$/, '');
72
+ if (o.region)
73
+ path += '/' + encodeURIComponent(o.region);
74
+ this.baseUrl = `${u.origin}${path}/v1`;
75
+ this.keyId = o.accessKeyId ?? '';
76
+ this.secret = o.secretAccessKey ?? '';
77
+ this.tokenProvider = o.tokenProvider;
78
+ this.maxRetries = o.maxRetries ?? 4;
79
+ this.userAgent = `thailandhosting-queue-service-sdk-node/${VERSION}${o.userAgent ? ' ' + o.userAgent : ''}`;
80
+ this.fetch = o.fetch ?? globalThis.fetch.bind(globalThis);
81
+ }
82
+ /** @internal One call: JSON in, JSON (or nothing) out. `path` is after /v1, already escaped. */
83
+ async call(method, path, payload, signal) {
84
+ const url = this.baseUrl + path;
85
+ const body = payload === undefined ? undefined : JSON.stringify(payload);
86
+ for (let attempt = 0;; attempt++) {
87
+ const headers = { 'User-Agent': this.userAgent, Accept: 'application/json' };
88
+ if (body !== undefined) {
89
+ headers['Content-Type'] = 'application/json';
90
+ headers['Content-Digest'] = contentDigest(body);
91
+ }
92
+ if (this.tokenProvider)
93
+ headers.Authorization = await bearer(this.tokenProvider);
94
+ else
95
+ Object.assign(headers, signRequest({ method, url, accessKeyId: this.keyId, secretAccessKey: this.secret, contentDigest: headers['Content-Digest'] }));
96
+ let resp;
97
+ try {
98
+ resp = await this.fetch(url, { method, headers, body, signal, redirect: 'manual' });
99
+ }
100
+ catch (err) {
101
+ if (signal?.aborted || attempt >= this.maxRetries)
102
+ throw err;
103
+ await sleep(backoff(attempt), signal);
104
+ continue;
105
+ }
106
+ if (resp.status >= 300) {
107
+ const text = await resp.text().catch(() => '');
108
+ if (RETRY_STATUSES.has(resp.status) && attempt < this.maxRetries) {
109
+ const after = Number(resp.headers.get('retry-after'));
110
+ await sleep(Number.isFinite(after) && after > 0 ? Math.min(after, 60) * 1000 : backoff(attempt), signal);
111
+ if (!signal?.aborted)
112
+ continue;
113
+ }
114
+ let p = {};
115
+ try {
116
+ p = JSON.parse(text);
117
+ }
118
+ catch { /* not Problem Details */ }
119
+ const code = p.code || resp.headers.get('x-error-code') || `http_${resp.status}`;
120
+ const message = p.title || text.trim() || resp.statusText;
121
+ const problem = { type: p.type, instance: p.instance };
122
+ if (resp.status === 404)
123
+ throw new NotFoundError(code, message, problem);
124
+ throw new QueueServiceError(resp.status, code, message, problem);
125
+ }
126
+ const text = await resp.text();
127
+ return { status: resp.status, data: (text ? JSON.parse(text) : undefined) };
128
+ }
129
+ }
130
+ /** The queues the key can reach, by name. */
131
+ async listQueues(opts = {}) {
132
+ const { data } = await this.call('GET', '/queues', undefined, opts.signal);
133
+ return (data?.queues ?? []).map(queueFromJSON);
134
+ }
135
+ /** Creates a queue, or changes the given settings of the one by that name (the key must manage queues). */
136
+ async putQueue(name, settings = {}, opts = {}) {
137
+ const { status, data } = await this.call('PUT', '/queues/' + encodeURIComponent(name), settings, opts.signal);
138
+ return { ...queueFromJSON(data), created: status === 201 };
139
+ }
140
+ /** A queue's settings and backlog. */
141
+ async getQueue(name, opts = {}) {
142
+ const { data } = await this.call('GET', '/queues/' + encodeURIComponent(name), undefined, opts.signal);
143
+ return queueFromJSON(data);
144
+ }
145
+ /** Deletes a queue with every message in it. */
146
+ async deleteQueue(name, opts = {}) {
147
+ await this.call('DELETE', '/queues/' + encodeURIComponent(name), undefined, opts.signal);
148
+ }
149
+ /** A handle for sending and receiving on one queue (it makes no call by itself). */
150
+ queue(name) {
151
+ return new Queue(this, name);
152
+ }
153
+ }
154
+ /** Sends and receives on one queue. */
155
+ export class Queue {
156
+ path;
157
+ client;
158
+ name;
159
+ constructor(client, name) {
160
+ this.client = client;
161
+ this.name = name;
162
+ this.path = '/queues/' + encodeURIComponent(name);
163
+ }
164
+ /** The queue's settings and backlog. */
165
+ info(opts = {}) {
166
+ return this.client.getQueue(this.name, opts);
167
+ }
168
+ /** Sends one message and returns its ID. */
169
+ async send(body, opts = {}) {
170
+ const { data } = await this.client.call('POST', this.path + '/messages', { body, attributes: opts.attributes, delay: opts.delay || undefined }, opts.signal);
171
+ return data.id;
172
+ }
173
+ /** Sends a value encoded as JSON. */
174
+ sendJSON(value, opts = {}) {
175
+ return this.send(JSON.stringify(value), opts);
176
+ }
177
+ /** Sends up to 10 messages in one request (all or none) and returns their IDs in order. */
178
+ async sendBatch(messages, opts = {}) {
179
+ if (messages.length < 1 || messages.length > MAX_BATCH)
180
+ throw new RangeError(`queue-service: send 1 to ${MAX_BATCH} messages at once`);
181
+ const payload = messages.map((m) => (typeof m === 'string' ? { body: m } : { body: m.body, attributes: m.attributes, delay: m.delay || undefined }));
182
+ const { data } = await this.client.call('POST', this.path + '/messages', { messages: payload }, opts.signal);
183
+ return data.ids;
184
+ }
185
+ /** Takes up to `max` messages, hiding them for the visibility timeout; none when the queue is empty
186
+ * (after waiting up to `wait` seconds). */
187
+ async receive(opts = {}) {
188
+ const payload = {};
189
+ if (opts.max)
190
+ payload.max = opts.max;
191
+ if (opts.visibilityTimeout)
192
+ payload.visibilityTimeout = Math.max(1, Math.floor(opts.visibilityTimeout));
193
+ if (opts.wait)
194
+ payload.wait = Math.min(MAX_WAIT, Math.floor(opts.wait));
195
+ const { data } = await this.client.call('POST', this.path + '/receive', payload, opts.signal);
196
+ return (data?.messages ?? []).map(messageFromJSON);
197
+ }
198
+ /** Deletes a handled message by its receipt (deleting it again is not an error). */
199
+ async delete(receipt, opts = {}) {
200
+ await this.client.call('DELETE', this.path + '/messages/' + encodeURIComponent(receipt), undefined, opts.signal);
201
+ }
202
+ /** Deletes up to 10 handled messages; each receipt gets a result, in the order given. */
203
+ async deleteBatch(receipts, opts = {}) {
204
+ if (receipts.length < 1 || receipts.length > MAX_BATCH)
205
+ throw new RangeError(`queue-service: delete 1 to ${MAX_BATCH} receipts at once`);
206
+ const { data } = await this.client.call('POST', this.path + '/delete', { receipts }, opts.signal);
207
+ const byReceipt = new Map();
208
+ for (const r of (data?.results ?? []))
209
+ byReceipt.set(r.receipt, [...(byReceipt.get(r.receipt) ?? []), r]);
210
+ return receipts.map((receipt) => byReceipt.get(receipt)?.shift() ?? { receipt, deleted: false, error: 'unavailable' });
211
+ }
212
+ /** Hides a received message `timeout` seconds from now (to work on it longer), or with 0 hands it out again at once. */
213
+ async changeVisibility(receipt, timeout, opts = {}) {
214
+ await this.client.call('POST', this.path + '/visibility', { receipt, timeout: Math.floor(timeout) }, opts.signal);
215
+ }
216
+ /** Removes every message of the queue. */
217
+ async purge(opts = {}) {
218
+ await this.client.call('POST', this.path + '/purge', {}, opts.signal);
219
+ }
220
+ /**
221
+ * A worker: receives with long polling and runs `handle` on each message, `concurrency` at a time,
222
+ * until `signal` aborts (it then resolves once the running handlers finish). A message whose handler
223
+ * resolves is deleted; one whose handler throws is left to be handed out again (after `retryDelay`
224
+ * when set), and after the queue's maxReceives it goes to its dead-letter queue. Busy or unreachable
225
+ * answers are retried with backoff; errors retrying cannot fix (a missing queue, a key that may not
226
+ * receive) reject.
227
+ */
228
+ async process(handle, opts = {}) {
229
+ const concurrency = Math.max(1, opts.concurrency ?? 1);
230
+ const wait = Math.min(MAX_WAIT, opts.wait && opts.wait > 0 ? opts.wait : MAX_WAIT);
231
+ const signal = opts.signal;
232
+ const report = (err, m) => { try {
233
+ opts.onError?.(err, m);
234
+ }
235
+ catch { /* ignore */ } };
236
+ const running = new Set();
237
+ let failures = 0;
238
+ const runOne = async (m) => {
239
+ try {
240
+ await handle(m);
241
+ }
242
+ catch (err) {
243
+ report(err, m);
244
+ if (opts.retryDelay && opts.retryDelay > 0) {
245
+ await this.changeVisibility(m.receipt, opts.retryDelay).catch((e) => {
246
+ if (!(e instanceof QueueServiceError && e.code === 'invalid_receipt'))
247
+ report(e, m);
248
+ });
249
+ }
250
+ return;
251
+ }
252
+ await this.delete(m.receipt).catch((e) => report(e, m));
253
+ };
254
+ try {
255
+ while (!signal?.aborted) {
256
+ while (running.size >= concurrency)
257
+ await Promise.race(running);
258
+ const free = Math.min(concurrency - running.size, MAX_BATCH);
259
+ let messages;
260
+ try {
261
+ messages = await this.receive({ max: free, wait, visibilityTimeout: opts.visibilityTimeout, signal });
262
+ }
263
+ catch (err) {
264
+ if (signal?.aborted)
265
+ break;
266
+ if (err instanceof QueueServiceError && [400, 401, 403, 404, 421].includes(err.status) && err.code !== 'project_suspended')
267
+ throw err;
268
+ report(err);
269
+ await sleep(backoff(++failures), signal);
270
+ continue;
271
+ }
272
+ failures = 0;
273
+ for (const m of messages) {
274
+ const p = runOne(m).finally(() => running.delete(p));
275
+ running.add(p);
276
+ }
277
+ }
278
+ }
279
+ finally {
280
+ await Promise.allSettled(running);
281
+ }
282
+ }
283
+ }
284
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /** An error answer from the service, read from its Problem Details (RFC 9457).
2
+ * `code` is the machine-readable reason, for example `queue_not_found`,
3
+ * `queue_full`, `invalid_receipt` or `busy`; the message is the problem's title. */
4
+ export declare class QueueServiceError extends Error {
5
+ readonly status: number;
6
+ readonly code: string;
7
+ /** The problem's `type` URI and `instance` (the request path). */
8
+ readonly type?: string;
9
+ readonly instance?: string;
10
+ constructor(status: number, code: string, message: string, problem?: {
11
+ type?: string;
12
+ instance?: string;
13
+ });
14
+ }
15
+ /** The `tokenProvider` failed or gave no token: nothing was sent. The provider's error is in `cause`. Not retried. */
16
+ export declare class TokenError extends Error {
17
+ constructor(message: string, cause?: unknown);
18
+ }
19
+ /** The queue does not exist, or the key may not reach it (code `queue_not_found`). */
20
+ export declare class NotFoundError extends QueueServiceError {
21
+ constructor(code: string, message: string, problem?: {
22
+ type?: string;
23
+ instance?: string;
24
+ });
25
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,33 @@
1
+ /** An error answer from the service, read from its Problem Details (RFC 9457).
2
+ * `code` is the machine-readable reason, for example `queue_not_found`,
3
+ * `queue_full`, `invalid_receipt` or `busy`; the message is the problem's title. */
4
+ export class QueueServiceError extends Error {
5
+ status;
6
+ code;
7
+ /** The problem's `type` URI and `instance` (the request path). */
8
+ type;
9
+ instance;
10
+ constructor(status, code, message, problem = {}) {
11
+ super(`${code} (HTTP ${status}): ${message}`);
12
+ this.name = 'QueueServiceError';
13
+ this.status = status;
14
+ this.code = code;
15
+ this.type = problem.type;
16
+ this.instance = problem.instance;
17
+ }
18
+ }
19
+ /** The `tokenProvider` failed or gave no token: nothing was sent. The provider's error is in `cause`. Not retried. */
20
+ export class TokenError extends Error {
21
+ constructor(message, cause) {
22
+ super(message, { cause });
23
+ this.name = 'TokenError';
24
+ }
25
+ }
26
+ /** The queue does not exist, or the key may not reach it (code `queue_not_found`). */
27
+ export class NotFoundError extends QueueServiceError {
28
+ constructor(code, message, problem) {
29
+ super(404, code, message, problem);
30
+ this.name = 'NotFoundError';
31
+ }
32
+ }
33
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;qFAEqF;AACrF,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IACjC,MAAM,CAAS;IACf,IAAI,CAAS;IACtB,kEAAkE;IACzD,IAAI,CAAU;IACd,QAAQ,CAAU;IAE3B,YAAY,MAAc,EAAE,IAAY,EAAE,OAAe,EAAE,UAAgD,EAAE;QAC3G,KAAK,CAAC,GAAG,IAAI,UAAU,MAAM,MAAM,OAAO,EAAE,CAAC,CAAC;QAC9C,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;QAChC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QACzB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IACnC,CAAC;CACF;AAED,sHAAsH;AACtH,MAAM,OAAO,UAAW,SAAQ,KAAK;IACnC,YAAY,OAAe,EAAE,KAAe;QAC1C,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;IAC3B,CAAC;CACF;AAED,sFAAsF;AACtF,MAAM,OAAO,aAAc,SAAQ,iBAAiB;IAClD,YAAY,IAAY,EAAE,OAAe,EAAE,OAA8C;QACvF,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QACnC,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ export { Client, Queue, VERSION, DEFAULT_ENDPOINT, MAX_BATCH, MAX_WAIT, type ClientOptions, type DeleteResult, type Message, type OutgoingMessage, type ProcessOptions, type QueueInfo, type QueueSettings, type QueueStats, type ReceiveOptions, type SendOptions, } from './client.ts';
2
+ export { NotFoundError, QueueServiceError, TokenError } from './errors.ts';
3
+ export { contentDigest, signRequest, type SignInput } from './signing.ts';
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { Client, Queue, VERSION, DEFAULT_ENDPOINT, MAX_BATCH, MAX_WAIT, } from "./client.js";
2
+ export { NotFoundError, QueueServiceError, TokenError } from "./errors.js";
3
+ export { contentDigest, signRequest } from "./signing.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,QAAQ,GAG9D,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC3E,OAAO,EAAE,aAAa,EAAE,WAAW,EAAkB,MAAM,cAAc,CAAC"}
@@ -0,0 +1,22 @@
1
+ export interface SignInput {
2
+ method: string;
3
+ /** The full request URL, exactly as it will be sent. */
4
+ url: string | URL;
5
+ accessKeyId: string;
6
+ secretAccessKey: string;
7
+ /** The Content-Digest header sent with a body; leave out for a request without one. */
8
+ contentDigest?: string;
9
+ /** Unix seconds (default: now). */
10
+ created?: number;
11
+ }
12
+ /** The Content-Digest (RFC 9530) of a body: `sha-256=:<base64>:`. */
13
+ export declare const contentDigest: (body: Uint8Array | string) => string;
14
+ /** @internal The @signature-params value: Signature-Input after `th=`. */
15
+ export declare function signatureParams(keyId: string, created: number, withDigest: boolean): string;
16
+ /** @internal The bytes that are signed (RFC 9421 section 2.5), no trailing newline. */
17
+ export declare function signatureBase(method: string, url: string | URL, params: string, digest?: string): string;
18
+ /** The Signature-Input and Signature headers of one request. Sign each attempt afresh. */
19
+ export declare function signRequest(i: SignInput): {
20
+ 'Signature-Input': string;
21
+ Signature: string;
22
+ };
@@ -0,0 +1,39 @@
1
+ // Request signing: HTTP Message Signatures (RFC 9421) with HMAC-SHA256 keyed
2
+ // with the access key's secret, so the secret itself is never sent. A body is
3
+ // tied to the signature through its Content-Digest (RFC 9530).
4
+ import { createHash, createHmac } from 'node:crypto';
5
+ const LABEL = 'th';
6
+ const ALG = 'hmac-sha256';
7
+ const COMPONENTS = ['@method', '@authority', '@path', '@query'];
8
+ /** The Content-Digest (RFC 9530) of a body: `sha-256=:<base64>:`. */
9
+ export const contentDigest = (body) => `sha-256=:${createHash('sha256').update(body).digest('base64')}:`;
10
+ const sfString = (s) => `"${s.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
11
+ /** @internal The @signature-params value: Signature-Input after `th=`. */
12
+ export function signatureParams(keyId, created, withDigest) {
13
+ const covered = withDigest ? [...COMPONENTS, 'content-digest'] : COMPONENTS;
14
+ return `(${covered.map(sfString).join(' ')});created=${created};keyid=${sfString(keyId)};alg="${ALG}"`;
15
+ }
16
+ /** @internal The bytes that are signed (RFC 9421 section 2.5), no trailing newline. */
17
+ export function signatureBase(method, url, params, digest) {
18
+ const u = typeof url === 'string' ? new URL(url) : url;
19
+ const authority = u.host.toLowerCase().replace(/:(443|80)$/, '');
20
+ const lines = [
21
+ `"@method": ${method.toUpperCase()}`,
22
+ `"@authority": ${authority}`,
23
+ `"@path": ${u.pathname || '/'}`,
24
+ `"@query": ?${u.search.slice(1)}`,
25
+ ];
26
+ if (digest !== undefined)
27
+ lines.push(`"content-digest": ${digest}`);
28
+ lines.push(`"@signature-params": ${params}`);
29
+ return lines.join('\n');
30
+ }
31
+ /** The Signature-Input and Signature headers of one request. Sign each attempt afresh. */
32
+ export function signRequest(i) {
33
+ const created = i.created ?? Math.floor(Date.now() / 1000);
34
+ const params = signatureParams(i.accessKeyId, created, i.contentDigest !== undefined);
35
+ const base = signatureBase(i.method, i.url, params, i.contentDigest);
36
+ const sig = createHmac('sha256', i.secretAccessKey).update(base, 'utf8').digest('base64');
37
+ return { 'Signature-Input': `${LABEL}=${params}`, Signature: `${LABEL}=:${sig}:` };
38
+ }
39
+ //# sourceMappingURL=signing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signing.js","sourceRoot":"","sources":["../src/signing.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,46 @@
1
1
  {
2
2
  "name": "@thailandhosting/queue-service",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.1.0",
4
+ "description": "Node.js SDK for ThailandHosting Queue Service",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md"
17
+ ],
18
+ "engines": {
19
+ "node": ">=18.17"
20
+ },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/mrzack99s/thailandhosting-queue-service-sdk-node.git"
24
+ },
25
+ "scripts": {
26
+ "build": "tsc -p tsconfig.json",
27
+ "typecheck": "tsc -p tsconfig.json --noEmit",
28
+ "test": "node --test test/*.test.ts",
29
+ "prepublishOnly": "npm run typecheck && npm test && npm run build"
30
+ },
31
+ "keywords": [
32
+ "thailandhosting",
33
+ "queue",
34
+ "message-queue",
35
+ "jobs",
36
+ "worker"
37
+ ],
38
+ "license": "MIT",
39
+ "publishConfig": {
40
+ "access": "public"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^22.10.0",
44
+ "typescript": "^5.8.0"
45
+ }
46
+ }