pipework 0.250.0 → 0.251.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/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.251.0
4
+
5
+ - **Added: `startPolling(ask, options?)` in `pipework/client`** (#890), for a job a person waits on in the browser. One ask at a time; waits back off from `firstMs` (3 s) by `backOff` (1.5) to `longestMs` (10 s); nothing is asked while the tab is hidden, and visible again asks at once; an ask may return `{ atLeastMs }` to wait longer; a throwing ask goes to `onError` and polling goes on. Returns `stop`.
6
+ - **Added: `waitFromResponse(response)`** (#890) turns a 429 or 503 into `{ atLeastMs }` from `Retry-After` (seconds or HTTP date), or from the body's `error.retryAfterSeconds` when a cross-origin page cannot read the header.
7
+
3
8
  ## 0.250.0
4
9
 
5
10
  - **Added: `filter.arrayHas(arrayCol, col)` takes a column or SQL expression as the value** (#888). It renders `<col> = ANY(<arrayCol>)`, with no bind and no cast, so a join from rows holding an array of ids to the table they point at can use that table's index. `arrayAny(arr, el => eq(el, col))` renders `EXISTS (... unnest ...)`, which can't (subseam: 347 s against 37.8 s for the same rows). A JS value is bound and cast as before.
package/REFERENCE.md CHANGED
@@ -2024,6 +2024,19 @@ window.addEventListener('unhandledrejection', e => send('rejection', String(e.re
2024
2024
  // server: server.registerHandlers(alerts.clientFaults(ops, { path: '/client-faults', apps: { web: { owner: 'web team', runbook: 'Check the release' } }, cors: { origin: ['https://app.example.com'], credentials: true } }))
2025
2025
  ```
2026
2026
 
2027
+ `startPolling(ask, options?)` from `pipework/client` asks again and again until the caller stops it, for a job a person waits on in the browser. The first ask is after `firstMs` (default 3000, the caller already has the enqueue's state); each wait is the last × `backOff` (default 1.5; 1 is a fixed interval) up to `longestMs` (default 10000). One ask at a time: the next wait starts when the ask settles. An ask that throws goes to `onError` and polling goes on. An ask may return `{ atLeastMs }`: the next wait is at least that, and the back-off carries on as if it had not. While the page is hidden nothing is asked (`pauseWhenHidden`, default true); visible again asks at once and the waits restart from `firstMs`. It returns `stop` (idempotent; an ask still in flight schedules nothing). Bad options throw. `waitFromResponse(response)` turns a 429 or 503 into `{ atLeastMs }` from `Retry-After` (seconds or HTTP date), or from `error.retryAfterSeconds` in the JSON body when the header is not readable; the response's body stays unread.
2028
+
2029
+ ```ts
2030
+ import { startPolling, waitFromResponse } from 'pipework/client'
2031
+ const stop = startPolling(async () => {
2032
+ const res = await fetch(`/jobs/${id}`, { credentials: 'include' })
2033
+ const wait = await waitFromResponse(res)
2034
+ if (wait) return wait
2035
+ const job = await res.json()
2036
+ if (job.state === 'done') stop()
2037
+ }, { onError: e => console.warn(e) })
2038
+ ```
2039
+
2027
2040
  ## support — Support Tickets
2028
2041
 
2029
2042
  Mail to a support address becomes a ticket that an agent triages; nothing reaches a customer without a human's approval. Built on `mail` (send and receive) and the delivery of `alerts`.
@@ -1,3 +1,5 @@
1
1
  export { reportClientFault, pageLeaving, LEAVING_HOLD_MS, CLIENT_FAULT_MESSAGE_MAX, CLIENT_FAULT_STACK_MAX } from './report-fault.js';
2
2
  export type { ClientFaultReport, ReportClientFaultOptions } from './report-fault.js';
3
+ export { startPolling, waitFromResponse } from './poll.js';
4
+ export type { PollingOptions, PollingPage, PollAnswer } from './poll.js';
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,eAAe,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AACrI,YAAY,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,eAAe,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AACrI,YAAY,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAA;AACpF,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAC1D,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA"}
@@ -1,2 +1,3 @@
1
1
  export { reportClientFault, pageLeaving, LEAVING_HOLD_MS, CLIENT_FAULT_MESSAGE_MAX, CLIENT_FAULT_STACK_MAX } from './report-fault.js';
2
+ export { startPolling, waitFromResponse } from './poll.js';
2
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,eAAe,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,eAAe,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAErI,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA"}
@@ -0,0 +1,41 @@
1
+ /** What an ask may return to slow the next one: the next wait is at least `atLeastMs`. */
2
+ export type PollAnswer = {
3
+ atLeastMs: number;
4
+ } | void;
5
+ /** The part of `document` the poller uses. */
6
+ export interface PollingPage {
7
+ readonly hidden: boolean;
8
+ addEventListener(type: 'visibilitychange', listener: () => void): void;
9
+ removeEventListener(type: 'visibilitychange', listener: () => void): void;
10
+ }
11
+ export interface PollingOptions {
12
+ /** First wait in ms, before the first ask. Default 3000. */
13
+ firstMs?: number;
14
+ /** Each wait is the last times `backOff`, up to this. Default 10000. */
15
+ longestMs?: number;
16
+ /** At least 1; 1 is a fixed interval. Default 1.5. */
17
+ backOff?: number;
18
+ /** Default true: nothing is asked while the page is hidden; visible again asks at once and the waits restart from `firstMs`. */
19
+ pauseWhenHidden?: boolean;
20
+ /** An ask that throws or rejects stops nothing; the error is passed here and the next ask is scheduled. */
21
+ onError?: (error: unknown) => void;
22
+ /** Default `document` when there is one. */
23
+ page?: PollingPage;
24
+ }
25
+ /**
26
+ * Calls `ask` after `firstMs`, then again after each answer, waiting longer each time (`backOff`, up to `longestMs`).
27
+ * One ask at a time: the next wait starts when the last ask settled. An ask returning `{ atLeastMs }` makes the next
28
+ * wait at least that long; the back-off carries on as if it had not. While the page is hidden nothing is asked.
29
+ * Returns `stop`, which is idempotent; an ask still in flight when it is called schedules nothing and reports no error.
30
+ */
31
+ export declare function startPolling(ask: () => Promise<PollAnswer>, options?: PollingOptions): () => void;
32
+ /**
33
+ * For a 429 or 503: how long the server asked the client to wait, as `{ atLeastMs }` to return from an ask.
34
+ * Reads `Retry-After` (seconds or an HTTP date); when that is absent or unreadable (a cross-origin page cannot see it
35
+ * without CORS `exposedHeaders`), reads `error.retryAfterSeconds` from the JSON body. The response's own body stays unread.
36
+ * Any other status, or no wait found, gives undefined.
37
+ */
38
+ export declare function waitFromResponse(response: Response): Promise<{
39
+ atLeastMs: number;
40
+ } | undefined>;
41
+ //# sourceMappingURL=poll.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poll.d.ts","sourceRoot":"","sources":["../../src/client/poll.ts"],"names":[],"mappings":"AAEA,0FAA0F;AAC1F,MAAM,MAAM,UAAU,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAA;AAErD,8CAA8C;AAC9C,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,gBAAgB,CAAC,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAA;IACtE,mBAAmB,CAAC,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAA;CAC1E;AAED,MAAM,WAAW,cAAc;IAC7B,4DAA4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,sDAAsD;IACtD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gIAAgI;IAChI,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,2GAA2G;IAC3G,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAClC,4CAA4C;IAC5C,IAAI,CAAC,EAAE,WAAW,CAAA;CACnB;AAMD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,EAAE,OAAO,GAAE,cAAmB,GAAG,MAAM,IAAI,CAgErG;AAUD;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAAC,CAYrG"}
@@ -0,0 +1,112 @@
1
+ // Browser-safe: no node imports, no runtime dependencies.
2
+ function check(name, ok, rule, value) {
3
+ if (!ok)
4
+ throw new Error(`[pipework] startPolling: ${name} must be ${rule}, got ${String(value)}.`);
5
+ }
6
+ /**
7
+ * Calls `ask` after `firstMs`, then again after each answer, waiting longer each time (`backOff`, up to `longestMs`).
8
+ * One ask at a time: the next wait starts when the last ask settled. An ask returning `{ atLeastMs }` makes the next
9
+ * wait at least that long; the back-off carries on as if it had not. While the page is hidden nothing is asked.
10
+ * Returns `stop`, which is idempotent; an ask still in flight when it is called schedules nothing and reports no error.
11
+ */
12
+ export function startPolling(ask, options = {}) {
13
+ const firstMs = options.firstMs ?? 3000;
14
+ const longestMs = options.longestMs ?? 10_000;
15
+ const backOff = options.backOff ?? 1.5;
16
+ check('firstMs', Number.isFinite(firstMs) && firstMs >= 0, 'a non-negative finite number of ms', firstMs);
17
+ check('longestMs', Number.isFinite(longestMs) && longestMs >= 0, 'a non-negative finite number of ms', longestMs);
18
+ check('backOff', Number.isFinite(backOff) && backOff >= 1, 'a finite number of at least 1', backOff);
19
+ check('firstMs', firstMs <= longestMs, `at most longestMs (${longestMs})`, firstMs);
20
+ const page = options.page ?? globalThis.document;
21
+ const watched = options.pauseWhenHidden !== false ? page : undefined;
22
+ let stopped = false;
23
+ let inFlight = false;
24
+ let timer;
25
+ let base = firstMs;
26
+ const nextWait = () => {
27
+ const wait = base;
28
+ base = Math.min(base * backOff, longestMs);
29
+ return wait;
30
+ };
31
+ const clear = () => {
32
+ if (timer !== undefined)
33
+ clearTimeout(timer);
34
+ timer = undefined;
35
+ };
36
+ const schedule = (ms) => {
37
+ clear();
38
+ timer = setTimeout(due, ms);
39
+ };
40
+ const due = () => {
41
+ timer = undefined;
42
+ if (stopped || watched?.hidden === true)
43
+ return;
44
+ void run();
45
+ };
46
+ const run = async () => {
47
+ inFlight = true;
48
+ let answer;
49
+ try {
50
+ answer = (await ask()) ?? undefined;
51
+ }
52
+ catch (error) {
53
+ if (!stopped)
54
+ options.onError?.(error);
55
+ }
56
+ inFlight = false;
57
+ if (stopped)
58
+ return;
59
+ const atLeast = answer !== undefined && Number.isFinite(answer.atLeastMs) ? answer.atLeastMs : 0;
60
+ schedule(Math.max(nextWait(), atLeast));
61
+ };
62
+ const onVisibility = () => {
63
+ if (stopped || watched === undefined || watched.hidden)
64
+ return;
65
+ clear();
66
+ base = firstMs;
67
+ if (!inFlight)
68
+ void run();
69
+ };
70
+ watched?.addEventListener('visibilitychange', onVisibility);
71
+ schedule(nextWait());
72
+ return () => {
73
+ if (stopped)
74
+ return;
75
+ stopped = true;
76
+ clear();
77
+ watched?.removeEventListener('visibilitychange', onVisibility);
78
+ };
79
+ }
80
+ function fromHeader(value) {
81
+ if (value === null)
82
+ return undefined;
83
+ const text = value.trim();
84
+ if (/^\d+$/.test(text))
85
+ return Number(text) * 1000;
86
+ const at = Date.parse(text);
87
+ return Number.isNaN(at) ? undefined : Math.max(0, at - Date.now());
88
+ }
89
+ /**
90
+ * For a 429 or 503: how long the server asked the client to wait, as `{ atLeastMs }` to return from an ask.
91
+ * Reads `Retry-After` (seconds or an HTTP date); when that is absent or unreadable (a cross-origin page cannot see it
92
+ * without CORS `exposedHeaders`), reads `error.retryAfterSeconds` from the JSON body. The response's own body stays unread.
93
+ * Any other status, or no wait found, gives undefined.
94
+ */
95
+ export async function waitFromResponse(response) {
96
+ if (response.status !== 429 && response.status !== 503)
97
+ return undefined;
98
+ const fromHead = fromHeader(response.headers.get('retry-after'));
99
+ if (fromHead !== undefined)
100
+ return { atLeastMs: fromHead };
101
+ try {
102
+ const body = (await response.clone().json());
103
+ const seconds = body?.error?.retryAfterSeconds;
104
+ if (typeof seconds === 'number' && Number.isFinite(seconds) && seconds >= 0)
105
+ return { atLeastMs: seconds * 1000 };
106
+ }
107
+ catch {
108
+ // no readable body
109
+ }
110
+ return undefined;
111
+ }
112
+ //# sourceMappingURL=poll.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poll.js","sourceRoot":"","sources":["../../src/client/poll.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pipework",
3
- "version": "0.250.0",
3
+ "version": "0.251.0",
4
4
  "description": "TypeScript framework for multi-tenant SaaS applications. PostgreSQL-only.",
5
5
  "type": "module",
6
6
  "license": "MIT",