@pipe0/client 1.0.0 → 1.0.2
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 +12 -0
- package/README.md +26 -9
- package/dist/capacity-tracker.d.mts +64 -0
- package/dist/capacity-tracker.d.mts.map +1 -0
- package/dist/capacity-tracker.mjs +87 -0
- package/dist/capacity-tracker.mjs.map +1 -0
- package/dist/generated/openapi.types.d.mts +277 -33
- package/dist/generated/openapi.types.d.mts.map +1 -1
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +3 -2
- package/dist/pipe0.d.mts +381 -12
- package/dist/pipe0.d.mts.map +1 -1
- package/dist/pipe0.mjs +311 -90
- package/dist/pipe0.mjs.map +1 -1
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -59,20 +59,37 @@ const data = await piper.searches.search({
|
|
|
59
59
|
config: {
|
|
60
60
|
environment: "sandbox",
|
|
61
61
|
},
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
query: "Find all employees of pipe0",
|
|
69
|
-
},
|
|
62
|
+
search: {
|
|
63
|
+
search_id: "people:profiles:exa@1",
|
|
64
|
+
config: {
|
|
65
|
+
limit: 1,
|
|
66
|
+
filters: {
|
|
67
|
+
query: "Find all employees of pipe0",
|
|
70
68
|
},
|
|
71
69
|
},
|
|
72
|
-
|
|
70
|
+
},
|
|
73
71
|
});
|
|
74
72
|
```
|
|
75
73
|
|
|
74
|
+
## Rate limits — handled for you
|
|
75
|
+
|
|
76
|
+
pipe0's API [never rejects correct clients](https://pipe0.com/docs/rate-limits): work is always
|
|
77
|
+
accepted and scheduled by task priority, driven by how many records your organization has
|
|
78
|
+
queued. This SDK implements that contract end to end:
|
|
79
|
+
|
|
80
|
+
- **429s are absorbed automatically** — `org-rate-limited` waits out the server's
|
|
81
|
+
`Retry-After`; `queue-limit-exceeded` (the queued-record hard ceiling) backoff-retries the
|
|
82
|
+
create. Only an exhausted wait budget throws (`Pipe0RateLimitError`). Opt out with
|
|
83
|
+
`autoRetry429: false`.
|
|
84
|
+
- **`pipeInBatches` paces itself** — the rolling pool submits the next batch only when the
|
|
85
|
+
capacity headers (`X-P1/P2/P3-Capacity`) say it would still be admitted at `priorityFloor`
|
|
86
|
+
(default 2) or better, so large jobs never sink themselves to the slow lane.
|
|
87
|
+
- **Aborting cancels server-side** — when your `AbortSignal` fires, still-pending runs are
|
|
88
|
+
canceled remotely and their records return to your capacity instantly. Opt out with
|
|
89
|
+
`cancelOnAbort: false`.
|
|
90
|
+
- **Manual control** — `pipes.cancel(runId)` / `searches.cancel(runId)`, plus `pipe0.capacity`
|
|
91
|
+
and the `onCapacity` callback for the live capacity snapshot.
|
|
92
|
+
|
|
76
93
|
## Useful links
|
|
77
94
|
|
|
78
95
|
- [Documentation](https://pipe0.com/resources/documentation)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
//#region src/capacity-tracker.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Client-side estimate of the organization's queue capacity, driven by the
|
|
4
|
+
* `X-Task-Priority` / `X-P1-Capacity` / `X-P2-Capacity` / `X-P3-Capacity`
|
|
5
|
+
* headers that pipe0's run-creating endpoints return on every response.
|
|
6
|
+
*
|
|
7
|
+
* The server never rejects below the hard ceiling — submissions just enter at
|
|
8
|
+
* a worse priority — so pacing must be capacity-led rather than error-led:
|
|
9
|
+
* the last create's headers are the authoritative baseline, records the SDK
|
|
10
|
+
* observes completing (via its own polls or cancels) are credited back, and
|
|
11
|
+
* any drift from other clients consuming capacity self-corrects at the next
|
|
12
|
+
* create response.
|
|
13
|
+
*/
|
|
14
|
+
/** Estimated remaining records per priority window (P1 ≤ P2 ≤ P3). */
|
|
15
|
+
interface CapacitySnapshot {
|
|
16
|
+
/** Priority the most recently observed run was admitted at. */
|
|
17
|
+
taskPriority: 1 | 2 | 3;
|
|
18
|
+
/** Records that can still be submitted at priority 1. */
|
|
19
|
+
p1: number;
|
|
20
|
+
/** Records that can still be submitted at priority 2 or better. */
|
|
21
|
+
p2: number;
|
|
22
|
+
/** Records that can still be submitted at all — at 0, submissions 429. */
|
|
23
|
+
p3: number;
|
|
24
|
+
/** When the underlying headers were observed (ms epoch). */
|
|
25
|
+
observedAt: number;
|
|
26
|
+
}
|
|
27
|
+
declare class CapacityTracker {
|
|
28
|
+
/** Last authoritative header observation; null until the first create. */
|
|
29
|
+
private base;
|
|
30
|
+
/** Records of creates issued whose response has not settled yet. */
|
|
31
|
+
private pending;
|
|
32
|
+
/** Records credited back (completions/cancels) since `base` was observed. */
|
|
33
|
+
private credited;
|
|
34
|
+
/** runId → records, for crediting terminal runs exactly once. */
|
|
35
|
+
private runRecords;
|
|
36
|
+
/**
|
|
37
|
+
* Re-baseline from a response's headers. Returns the adjusted snapshot, or
|
|
38
|
+
* null when the response carries no capacity headers (e.g. an
|
|
39
|
+
* `org-rate-limited` 429 emitted by middleware before the handler ran).
|
|
40
|
+
*/
|
|
41
|
+
observeHeaders(headers: Headers): CapacitySnapshot | null;
|
|
42
|
+
/** Call synchronously BEFORE issuing a create — prevents two concurrent submitters from double-spending the same estimate. */
|
|
43
|
+
onCreateIssued(records: number): void;
|
|
44
|
+
/** Call after the create response settled (the middleware has re-baselined by then). */
|
|
45
|
+
onCreateSettled(records: number): void;
|
|
46
|
+
/** Remember a run's record weight so its completion can be credited back. */
|
|
47
|
+
registerRun(runId: string, records: number): void;
|
|
48
|
+
/**
|
|
49
|
+
* Credit a run's records back to every window estimate. The Map delete
|
|
50
|
+
* makes double-crediting (cancel + a later poll observing "canceled")
|
|
51
|
+
* impossible. Returns the credited record count (0 if unknown/already credited).
|
|
52
|
+
*/
|
|
53
|
+
creditTerminal(runId: string): number;
|
|
54
|
+
/**
|
|
55
|
+
* Estimated remaining records in the given priority window. Null when no
|
|
56
|
+
* create has been observed yet — callers treat that as "gate open".
|
|
57
|
+
*/
|
|
58
|
+
estimate(floor: 1 | 2 | 3): number | null;
|
|
59
|
+
/** The current adjusted snapshot, or null before the first observation. */
|
|
60
|
+
snapshot(): CapacitySnapshot | null;
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
export { CapacitySnapshot, CapacityTracker };
|
|
64
|
+
//# sourceMappingURL=capacity-tracker.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capacity-tracker.d.mts","names":[],"sources":["../src/capacity-tracker.ts"],"mappings":";;AAcA;;;;;;;;;;;AAeA;AAAA,UAfiB,gBAAA;;EAEf,YAAA;EA4BkC;EA1BlC,EAAA;EAwF4B;EAtF5B,EAAA;EAWQ;EATR,EAAA;EAaQ;EAXR,UAAA;AAAA;AAAA,cAKW,eAAA;EAeI;EAAA,QAbP,IAAA;EAiCR;EAAA,QA/BQ,OAAA;EAoCR;EAAA,QAlCQ,QAAA;EAuCR;EAAA,QArCQ,UAAA;EAqCmB;;;;;EA9B3B,cAAA,CAAe,OAAA,EAAS,OAAA,GAAU,gBAAA;EA8DtB;EA1CZ,cAAA,CAAe,OAAA;EA0Ca;EArC5B,eAAA,CAAgB,OAAA;;EAKhB,WAAA,CAAY,KAAA,UAAe,OAAA;;;;;;EAa3B,cAAA,CAAe,KAAA;;;;;EAYf,QAAA,CAAS,KAAA;;EAOT,QAAA,CAAA,GAAY,gBAAA;AAAA"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
//#region src/capacity-tracker.ts
|
|
2
|
+
const MAX_TRACKED_RUNS = 1e3;
|
|
3
|
+
var CapacityTracker = class {
|
|
4
|
+
/** Last authoritative header observation; null until the first create. */
|
|
5
|
+
base = null;
|
|
6
|
+
/** Records of creates issued whose response has not settled yet. */
|
|
7
|
+
pending = 0;
|
|
8
|
+
/** Records credited back (completions/cancels) since `base` was observed. */
|
|
9
|
+
credited = 0;
|
|
10
|
+
/** runId → records, for crediting terminal runs exactly once. */
|
|
11
|
+
runRecords = /* @__PURE__ */ new Map();
|
|
12
|
+
/**
|
|
13
|
+
* Re-baseline from a response's headers. Returns the adjusted snapshot, or
|
|
14
|
+
* null when the response carries no capacity headers (e.g. an
|
|
15
|
+
* `org-rate-limited` 429 emitted by middleware before the handler ran).
|
|
16
|
+
*/
|
|
17
|
+
observeHeaders(headers) {
|
|
18
|
+
if (headers.get("X-P3-Capacity") === null) return null;
|
|
19
|
+
const num = (name) => {
|
|
20
|
+
const value = Number(headers.get(name));
|
|
21
|
+
return Number.isFinite(value) ? Math.max(0, value) : 0;
|
|
22
|
+
};
|
|
23
|
+
const priority = Number(headers.get("X-Task-Priority"));
|
|
24
|
+
this.base = {
|
|
25
|
+
taskPriority: priority === 2 || priority === 3 ? priority : 1,
|
|
26
|
+
p1: num("X-P1-Capacity"),
|
|
27
|
+
p2: num("X-P2-Capacity"),
|
|
28
|
+
p3: num("X-P3-Capacity"),
|
|
29
|
+
observedAt: Date.now()
|
|
30
|
+
};
|
|
31
|
+
this.credited = 0;
|
|
32
|
+
return this.snapshot();
|
|
33
|
+
}
|
|
34
|
+
/** Call synchronously BEFORE issuing a create — prevents two concurrent submitters from double-spending the same estimate. */
|
|
35
|
+
onCreateIssued(records) {
|
|
36
|
+
this.pending += records;
|
|
37
|
+
}
|
|
38
|
+
/** Call after the create response settled (the middleware has re-baselined by then). */
|
|
39
|
+
onCreateSettled(records) {
|
|
40
|
+
this.pending = Math.max(0, this.pending - records);
|
|
41
|
+
}
|
|
42
|
+
/** Remember a run's record weight so its completion can be credited back. */
|
|
43
|
+
registerRun(runId, records) {
|
|
44
|
+
this.runRecords.set(runId, records);
|
|
45
|
+
if (this.runRecords.size > MAX_TRACKED_RUNS) {
|
|
46
|
+
const oldest = this.runRecords.keys().next().value;
|
|
47
|
+
if (oldest !== void 0) this.runRecords.delete(oldest);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Credit a run's records back to every window estimate. The Map delete
|
|
52
|
+
* makes double-crediting (cancel + a later poll observing "canceled")
|
|
53
|
+
* impossible. Returns the credited record count (0 if unknown/already credited).
|
|
54
|
+
*/
|
|
55
|
+
creditTerminal(runId) {
|
|
56
|
+
const records = this.runRecords.get(runId);
|
|
57
|
+
if (records === void 0) return 0;
|
|
58
|
+
this.runRecords.delete(runId);
|
|
59
|
+
this.credited += records;
|
|
60
|
+
return records;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Estimated remaining records in the given priority window. Null when no
|
|
64
|
+
* create has been observed yet — callers treat that as "gate open".
|
|
65
|
+
*/
|
|
66
|
+
estimate(floor) {
|
|
67
|
+
if (this.base === null) return null;
|
|
68
|
+
const window = floor === 1 ? this.base.p1 : floor === 2 ? this.base.p2 : this.base.p3;
|
|
69
|
+
return Math.max(0, window - this.pending + this.credited);
|
|
70
|
+
}
|
|
71
|
+
/** The current adjusted snapshot, or null before the first observation. */
|
|
72
|
+
snapshot() {
|
|
73
|
+
if (this.base === null) return null;
|
|
74
|
+
const adjust = (value) => Math.max(0, value - this.pending + this.credited);
|
|
75
|
+
return {
|
|
76
|
+
taskPriority: this.base.taskPriority,
|
|
77
|
+
p1: adjust(this.base.p1),
|
|
78
|
+
p2: adjust(this.base.p2),
|
|
79
|
+
p3: adjust(this.base.p3),
|
|
80
|
+
observedAt: this.base.observedAt
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
//#endregion
|
|
86
|
+
export { CapacityTracker };
|
|
87
|
+
//# sourceMappingURL=capacity-tracker.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capacity-tracker.mjs","names":[],"sources":["../src/capacity-tracker.ts"],"sourcesContent":["/**\n * Client-side estimate of the organization's queue capacity, driven by the\n * `X-Task-Priority` / `X-P1-Capacity` / `X-P2-Capacity` / `X-P3-Capacity`\n * headers that pipe0's run-creating endpoints return on every response.\n *\n * The server never rejects below the hard ceiling — submissions just enter at\n * a worse priority — so pacing must be capacity-led rather than error-led:\n * the last create's headers are the authoritative baseline, records the SDK\n * observes completing (via its own polls or cancels) are credited back, and\n * any drift from other clients consuming capacity self-corrects at the next\n * create response.\n */\n\n/** Estimated remaining records per priority window (P1 ≤ P2 ≤ P3). */\nexport interface CapacitySnapshot {\n /** Priority the most recently observed run was admitted at. */\n taskPriority: 1 | 2 | 3;\n /** Records that can still be submitted at priority 1. */\n p1: number;\n /** Records that can still be submitted at priority 2 or better. */\n p2: number;\n /** Records that can still be submitted at all — at 0, submissions 429. */\n p3: number;\n /** When the underlying headers were observed (ms epoch). */\n observedAt: number;\n}\n\nconst MAX_TRACKED_RUNS = 1000;\n\nexport class CapacityTracker {\n /** Last authoritative header observation; null until the first create. */\n private base: CapacitySnapshot | null = null;\n /** Records of creates issued whose response has not settled yet. */\n private pending = 0;\n /** Records credited back (completions/cancels) since `base` was observed. */\n private credited = 0;\n /** runId → records, for crediting terminal runs exactly once. */\n private runRecords = new Map<string, number>();\n\n /**\n * Re-baseline from a response's headers. Returns the adjusted snapshot, or\n * null when the response carries no capacity headers (e.g. an\n * `org-rate-limited` 429 emitted by middleware before the handler ran).\n */\n observeHeaders(headers: Headers): CapacitySnapshot | null {\n if (headers.get(\"X-P3-Capacity\") === null) return null;\n const num = (name: string) => {\n const value = Number(headers.get(name));\n return Number.isFinite(value) ? Math.max(0, value) : 0;\n };\n const priority = Number(headers.get(\"X-Task-Priority\"));\n this.base = {\n taskPriority: priority === 2 || priority === 3 ? priority : 1,\n p1: num(\"X-P1-Capacity\"),\n p2: num(\"X-P2-Capacity\"),\n p3: num(\"X-P3-Capacity\"),\n observedAt: Date.now(),\n };\n // The new baseline already reflects everything the server has seen.\n this.credited = 0;\n return this.snapshot();\n }\n\n /** Call synchronously BEFORE issuing a create — prevents two concurrent submitters from double-spending the same estimate. */\n onCreateIssued(records: number): void {\n this.pending += records;\n }\n\n /** Call after the create response settled (the middleware has re-baselined by then). */\n onCreateSettled(records: number): void {\n this.pending = Math.max(0, this.pending - records);\n }\n\n /** Remember a run's record weight so its completion can be credited back. */\n registerRun(runId: string, records: number): void {\n this.runRecords.set(runId, records);\n if (this.runRecords.size > MAX_TRACKED_RUNS) {\n const oldest = this.runRecords.keys().next().value;\n if (oldest !== undefined) this.runRecords.delete(oldest);\n }\n }\n\n /**\n * Credit a run's records back to every window estimate. The Map delete\n * makes double-crediting (cancel + a later poll observing \"canceled\")\n * impossible. Returns the credited record count (0 if unknown/already credited).\n */\n creditTerminal(runId: string): number {\n const records = this.runRecords.get(runId);\n if (records === undefined) return 0;\n this.runRecords.delete(runId);\n this.credited += records;\n return records;\n }\n\n /**\n * Estimated remaining records in the given priority window. Null when no\n * create has been observed yet — callers treat that as \"gate open\".\n */\n estimate(floor: 1 | 2 | 3): number | null {\n if (this.base === null) return null;\n const window = floor === 1 ? this.base.p1 : floor === 2 ? this.base.p2 : this.base.p3;\n return Math.max(0, window - this.pending + this.credited);\n }\n\n /** The current adjusted snapshot, or null before the first observation. */\n snapshot(): CapacitySnapshot | null {\n if (this.base === null) return null;\n const adjust = (value: number) => Math.max(0, value - this.pending + this.credited);\n return {\n taskPriority: this.base.taskPriority,\n p1: adjust(this.base.p1),\n p2: adjust(this.base.p2),\n p3: adjust(this.base.p3),\n observedAt: this.base.observedAt,\n };\n }\n}\n"],"mappings":";AA2BA,MAAM,mBAAmB;AAEzB,IAAa,kBAAb,MAA6B;;CAE3B,AAAQ,OAAgC;;CAExC,AAAQ,UAAU;;CAElB,AAAQ,WAAW;;CAEnB,AAAQ,6BAAa,IAAI,KAAqB;;;;;;CAO9C,eAAe,SAA2C;AACxD,MAAI,QAAQ,IAAI,gBAAgB,KAAK,KAAM,QAAO;EAClD,MAAM,OAAO,SAAiB;GAC5B,MAAM,QAAQ,OAAO,QAAQ,IAAI,KAAK,CAAC;AACvC,UAAO,OAAO,SAAS,MAAM,GAAG,KAAK,IAAI,GAAG,MAAM,GAAG;;EAEvD,MAAM,WAAW,OAAO,QAAQ,IAAI,kBAAkB,CAAC;AACvD,OAAK,OAAO;GACV,cAAc,aAAa,KAAK,aAAa,IAAI,WAAW;GAC5D,IAAI,IAAI,gBAAgB;GACxB,IAAI,IAAI,gBAAgB;GACxB,IAAI,IAAI,gBAAgB;GACxB,YAAY,KAAK,KAAK;GACvB;AAED,OAAK,WAAW;AAChB,SAAO,KAAK,UAAU;;;CAIxB,eAAe,SAAuB;AACpC,OAAK,WAAW;;;CAIlB,gBAAgB,SAAuB;AACrC,OAAK,UAAU,KAAK,IAAI,GAAG,KAAK,UAAU,QAAQ;;;CAIpD,YAAY,OAAe,SAAuB;AAChD,OAAK,WAAW,IAAI,OAAO,QAAQ;AACnC,MAAI,KAAK,WAAW,OAAO,kBAAkB;GAC3C,MAAM,SAAS,KAAK,WAAW,MAAM,CAAC,MAAM,CAAC;AAC7C,OAAI,WAAW,OAAW,MAAK,WAAW,OAAO,OAAO;;;;;;;;CAS5D,eAAe,OAAuB;EACpC,MAAM,UAAU,KAAK,WAAW,IAAI,MAAM;AAC1C,MAAI,YAAY,OAAW,QAAO;AAClC,OAAK,WAAW,OAAO,MAAM;AAC7B,OAAK,YAAY;AACjB,SAAO;;;;;;CAOT,SAAS,OAAiC;AACxC,MAAI,KAAK,SAAS,KAAM,QAAO;EAC/B,MAAM,SAAS,UAAU,IAAI,KAAK,KAAK,KAAK,UAAU,IAAI,KAAK,KAAK,KAAK,KAAK,KAAK;AACnF,SAAO,KAAK,IAAI,GAAG,SAAS,KAAK,UAAU,KAAK,SAAS;;;CAI3D,WAAoC;AAClC,MAAI,KAAK,SAAS,KAAM,QAAO;EAC/B,MAAM,UAAU,UAAkB,KAAK,IAAI,GAAG,QAAQ,KAAK,UAAU,KAAK,SAAS;AACnF,SAAO;GACL,cAAc,KAAK,KAAK;GACxB,IAAI,OAAO,KAAK,KAAK,GAAG;GACxB,IAAI,OAAO,KAAK,KAAK,GAAG;GACxB,IAAI,OAAO,KAAK,KAAK,GAAG;GACxB,YAAY,KAAK,KAAK;GACvB"}
|