pipework 0.203.1 → 0.204.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,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.204.0
4
+
5
+ - **Fixed: a read cut off by the page leaving was reported as a client fault** (#779). A link, reload or `location.href` cuts off the page's in-flight fetches, and their rejections reached `reportClientFault` as faults. `reportClientFault` now holds a report made after `beforeunload`: it is dropped if `pagehide` follows, and sent if the page stays (10 s with no `pagehide`, `LEAVING_HOLD_MS`, or a bfcache restore). A report made after `pagehide` is dropped. This covers every kind (`request`, `error`, `rejection`, `render`). Pass `{ whileLeaving: 'send' }` as the third argument to send at once, for a failure that matters even while leaving (a save cut off by leaving). `pageLeaving()` says whether the page is leaving. A failure while the page stays is reported as before.
6
+
3
7
  ## 0.203.1
4
8
 
5
9
  - **Fixed: two `alerts.source.deadLetter` sources on different tables shared one position** (#777). The second to evaluate started from where the first had moved, so a dead letter in its table could be missed. Each table now keeps its own position, as each filter already did: state key `job-dead-letter@<table>` (with the `:only:`/`:except:` suffix as before). A source on the default `pipework_jobs` table keeps the key `job-dead-letter`, so its position carries over. A source on another table starts a new position from its first evaluation after the upgrade, so dead letters committed between its last evaluation before the upgrade and that first one are not raised. Condition keys are unchanged.
package/REFERENCE.md CHANGED
@@ -1918,7 +1918,7 @@ await ops.resolve('engine-unavailable')
1918
1918
  - Body, strict (an unknown field is 400, and nothing is raised): `app`, `where` (1-100 of `A-Za-z0-9._:/-`), `kind` (`error | rejection | render | request`), `severity?` (`warning | error`, default `error`), `code?` (100), `message` (1-500), `stack?` (4096), `route?` (200, starts with `/`, no `?`, `#` or `://`: a route pattern, never a page URL), `release?` (100), `traceId?` (a uuid: the `x-pipework-trace-id` of an answer), `requestId?` (1-100 of `A-Za-z0-9._:-`: the `x-request-id` of an answer).
1919
1919
  - Each report is a trace step `pipework/alerts:client-fault` capturing the report, and raises the key with severity as sent, the app's `owner` and `runbook` (or the report's from `assign`), and context `{ app, where, kind, code?, route?, release?, traceId?, requestId? }`. The stack is in the step's capture only. A raise that fails is logged; the answer is still 204.
1920
1920
 
1921
- `reportClientFault(url, report)` from `pipework/client` sends a report with `keepalive` and `credentials: 'include'`, cuts `message` and `stack` to their caps, and never throws.
1921
+ `reportClientFault(url, report)` from `pipework/client` sends a report with `keepalive` and `credentials: 'include'`, cuts `message` and `stack` to their caps, and never throws. A report made while the page is leaving (after `beforeunload`) is held: dropped if `pagehide` follows, sent if the page stays (10 s with no `pagehide`, or a bfcache restore), so a read cut off by a link or reload raises nothing. Pass `{ whileLeaving: 'send' }` as the third argument to send at once (a save cut off by leaving); `pageLeaving()` says whether the page is leaving.
1922
1922
 
1923
1923
  ```ts
1924
1924
  import { reportClientFault } from 'pipework/client'
@@ -1,3 +1,3 @@
1
- export { reportClientFault, CLIENT_FAULT_MESSAGE_MAX, CLIENT_FAULT_STACK_MAX } from './report-fault.js';
2
- export type { ClientFaultReport } from './report-fault.js';
1
+ export { reportClientFault, pageLeaving, LEAVING_HOLD_MS, CLIENT_FAULT_MESSAGE_MAX, CLIENT_FAULT_STACK_MAX } from './report-fault.js';
2
+ export type { ClientFaultReport, ReportClientFaultOptions } from './report-fault.js';
3
3
  //# 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,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AACvG,YAAY,EAAE,iBAAiB,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"}
@@ -1,2 +1,2 @@
1
- export { reportClientFault, CLIENT_FAULT_MESSAGE_MAX, CLIENT_FAULT_STACK_MAX } from './report-fault.js';
1
+ export { reportClientFault, pageLeaving, LEAVING_HOLD_MS, CLIENT_FAULT_MESSAGE_MAX, CLIENT_FAULT_STACK_MAX } from './report-fault.js';
2
2
  //# 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,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"}
@@ -24,9 +24,18 @@ export interface ClientFaultReport {
24
24
  }
25
25
  export declare const CLIENT_FAULT_MESSAGE_MAX = 500;
26
26
  export declare const CLIENT_FAULT_STACK_MAX = 4096;
27
+ /** How long a report made after `beforeunload` is held waiting for `pagehide` before the page is taken to be staying. */
28
+ export declare const LEAVING_HOLD_MS = 10000;
29
+ export interface ReportClientFaultOptions {
30
+ /** Default `hold`: a report made while the page is leaving waits, and is dropped if the page goes. `send` sends at once (a save cut off by leaving). */
31
+ whileLeaving?: 'hold' | 'send';
32
+ }
33
+ /** True from `beforeunload` until the page stays again (the hold timer ran out or a bfcache restore); stays true after `pagehide`. */
34
+ export declare function pageLeaving(): boolean;
27
35
  /**
28
36
  * Sends a failure to the intake route at `url` with `keepalive`, so it survives a page unload.
29
- * Cuts `message` and `stack` to their caps. Never throws and never rejects: a report that cannot be sent is dropped.
37
+ * Cuts `message` and `stack` to their caps. A report made while the page is leaving is held and dropped on `pagehide`,
38
+ * unless `options.whileLeaving` is `send`. Never throws and never rejects: a report that cannot be sent is dropped.
30
39
  */
31
- export declare function reportClientFault(url: string, report: ClientFaultReport): void;
40
+ export declare function reportClientFault(url: string, report: ClientFaultReport, options?: ReportClientFaultOptions): void;
32
41
  //# sourceMappingURL=report-fault.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"report-fault.d.ts","sourceRoot":"","sources":["../../src/client/report-fault.ts"],"names":[],"mappings":"AAEA,gFAAgF;AAChF,MAAM,WAAW,iBAAiB;IAChC,mCAAmC;IACnC,GAAG,EAAE,MAAM,CAAA;IACX,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,OAAO,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAA;IAClD,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,SAAS,GAAG,OAAO,CAAA;IAC9B,8BAA8B;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,8GAA8G;IAC9G,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,4HAA4H;IAC5H,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,qGAAqG;IACrG,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,eAAO,MAAM,wBAAwB,MAAM,CAAA;AAC3C,eAAO,MAAM,sBAAsB,OAAO,CAAA;AAE1C;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAe9E"}
1
+ {"version":3,"file":"report-fault.d.ts","sourceRoot":"","sources":["../../src/client/report-fault.ts"],"names":[],"mappings":"AAEA,gFAAgF;AAChF,MAAM,WAAW,iBAAiB;IAChC,mCAAmC;IACnC,GAAG,EAAE,MAAM,CAAA;IACX,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,OAAO,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAA;IAClD,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,SAAS,GAAG,OAAO,CAAA;IAC9B,8BAA8B;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAA;IACf,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,8GAA8G;IAC9G,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,4HAA4H;IAC5H,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,qGAAqG;IACrG,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,eAAO,MAAM,wBAAwB,MAAM,CAAA;AAC3C,eAAO,MAAM,sBAAsB,OAAO,CAAA;AAE1C,yHAAyH;AACzH,eAAO,MAAM,eAAe,QAAS,CAAA;AAErC,MAAM,WAAW,wBAAwB;IACvC,wJAAwJ;IACxJ,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAC/B;AA2CD,sIAAsI;AACtI,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,wBAAwB,GAAG,IAAI,CA6BlH"}
@@ -1,23 +1,85 @@
1
1
  // Browser-safe: no node imports, no runtime dependencies.
2
2
  export const CLIENT_FAULT_MESSAGE_MAX = 500;
3
3
  export const CLIENT_FAULT_STACK_MAX = 4096;
4
+ /** How long a report made after `beforeunload` is held waiting for `pagehide` before the page is taken to be staying. */
5
+ export const LEAVING_HOLD_MS = 10_000;
6
+ let leaving = false;
7
+ let left = false;
8
+ let timer;
9
+ let held = [];
10
+ function clearHold() {
11
+ if (timer !== undefined)
12
+ clearTimeout(timer);
13
+ timer = undefined;
14
+ held = [];
15
+ }
16
+ function stay() {
17
+ const pending = held;
18
+ leaving = false;
19
+ left = false;
20
+ if (timer !== undefined)
21
+ clearTimeout(timer);
22
+ timer = undefined;
23
+ held = [];
24
+ for (const send of pending)
25
+ send();
26
+ }
27
+ const win = globalThis;
28
+ if (typeof win.addEventListener === 'function') {
29
+ win.addEventListener('beforeunload', () => {
30
+ leaving = true;
31
+ if (timer !== undefined)
32
+ clearTimeout(timer);
33
+ timer = setTimeout(stay, LEAVING_HOLD_MS);
34
+ }, { passive: true });
35
+ win.addEventListener('pagehide', () => {
36
+ leaving = true;
37
+ left = true;
38
+ clearHold();
39
+ }, { passive: true });
40
+ win.addEventListener('pageshow', (event) => {
41
+ if (event.persisted === true)
42
+ stay();
43
+ }, { passive: true });
44
+ }
45
+ /** True from `beforeunload` until the page stays again (the hold timer ran out or a bfcache restore); stays true after `pagehide`. */
46
+ export function pageLeaving() {
47
+ return leaving;
48
+ }
4
49
  /**
5
50
  * Sends a failure to the intake route at `url` with `keepalive`, so it survives a page unload.
6
- * Cuts `message` and `stack` to their caps. Never throws and never rejects: a report that cannot be sent is dropped.
51
+ * Cuts `message` and `stack` to their caps. A report made while the page is leaving is held and dropped on `pagehide`,
52
+ * unless `options.whileLeaving` is `send`. Never throws and never rejects: a report that cannot be sent is dropped.
7
53
  */
8
- export function reportClientFault(url, report) {
54
+ export function reportClientFault(url, report, options) {
9
55
  try {
10
56
  const body = { ...report, message: report.message.slice(0, CLIENT_FAULT_MESSAGE_MAX) };
11
57
  if (body.stack !== undefined)
12
58
  body.stack = body.stack.slice(0, CLIENT_FAULT_STACK_MAX);
13
- const sent = fetch(url, {
14
- method: 'POST',
15
- keepalive: true,
16
- credentials: 'include',
17
- headers: { 'content-type': 'application/json' },
18
- body: JSON.stringify(body),
19
- });
20
- Promise.resolve(sent).catch(() => undefined);
59
+ const send = () => {
60
+ try {
61
+ const sent = fetch(url, {
62
+ method: 'POST',
63
+ keepalive: true,
64
+ credentials: 'include',
65
+ headers: { 'content-type': 'application/json' },
66
+ body: JSON.stringify(body),
67
+ });
68
+ Promise.resolve(sent).catch(() => undefined);
69
+ }
70
+ catch {
71
+ // dropped
72
+ }
73
+ };
74
+ if (options?.whileLeaving !== 'send') {
75
+ if (left)
76
+ return;
77
+ if (leaving) {
78
+ held.push(send);
79
+ return;
80
+ }
81
+ }
82
+ send();
21
83
  }
22
84
  catch {
23
85
  // dropped
@@ -1 +1 @@
1
- {"version":3,"file":"report-fault.js","sourceRoot":"","sources":["../../src/client/report-fault.ts"],"names":[],"mappings":"AAAA,0DAA0D;AA2B1D,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAA;AAC3C,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAA;AAE1C;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAW,EAAE,MAAyB;IACtE,IAAI,CAAC;QACH,MAAM,IAAI,GAAsB,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,wBAAwB,CAAC,EAAE,CAAA;QACzG,IAAI,IAAI,CAAC,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,sBAAsB,CAAC,CAAA;QACtF,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,EAAE;YACtB,MAAM,EAAE,MAAM;YACd,SAAS,EAAE,IAAI;YACf,WAAW,EAAE,SAAS;YACtB,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;SAC3B,CAAC,CAAA;QACF,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,UAAU;IACZ,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"report-fault.js","sourceRoot":"","sources":["../../src/client/report-fault.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pipework",
3
- "version": "0.203.1",
3
+ "version": "0.204.0",
4
4
  "description": "TypeScript framework for multi-tenant SaaS applications. PostgreSQL-only.",
5
5
  "type": "module",
6
6
  "license": "MIT",