@work-shape/catalyst-api-client 0.8.0-rc.2 → 0.8.0-rc.3
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/dist/index.d.ts +24 -3
- package/dist/index.js +33 -3
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -127,7 +127,12 @@ type ControlPart<Op extends Operation> = {
|
|
|
127
127
|
} ? {
|
|
128
128
|
/** Called when the server has nothing after the given position and sends no stream. */
|
|
129
129
|
onCaughtUp?: () => void;
|
|
130
|
-
}
|
|
130
|
+
} & (Op["response"] extends {
|
|
131
|
+
skip: infer Skip extends z.ZodType;
|
|
132
|
+
} ? {
|
|
133
|
+
/** Called for an event this client does not know, which the stream passes over. */
|
|
134
|
+
onSkipped?: (value: z.output<Skip>) => void;
|
|
135
|
+
} : unknown) : unknown);
|
|
131
136
|
/**
|
|
132
137
|
* An operation that takes an `Idempotency-Key` gets a new one with every call. A caller that
|
|
133
138
|
* sends the same call again after an answer that never arrived passes the key of the first.
|
|
@@ -139,7 +144,18 @@ type IdempotencyPart<Op> = Op extends {
|
|
|
139
144
|
} ? "Idempotency-Key" extends Header ? {
|
|
140
145
|
idempotencyKey?: string;
|
|
141
146
|
} : unknown : unknown;
|
|
142
|
-
|
|
147
|
+
/**
|
|
148
|
+
* An operation that takes `Catalyst-Unattended` is sent with it when `unattended` is set: nobody
|
|
149
|
+
* watches the caller's terminal, so a call that needs a confirmation waits in the Inbox.
|
|
150
|
+
*/
|
|
151
|
+
type UnattendedPart<Op> = Op extends {
|
|
152
|
+
headers: {
|
|
153
|
+
request: readonly (infer Header extends OperationRequestHeader)[];
|
|
154
|
+
};
|
|
155
|
+
} ? "Catalyst-Unattended" extends Header ? {
|
|
156
|
+
unattended?: boolean;
|
|
157
|
+
} : unknown : unknown;
|
|
158
|
+
type InputOf<Op extends Operation> = ParamsPart<Op["path"]> & QueryPart<Op> & BodyPart<Op> & IdempotencyPart<Op> & UnattendedPart<Op> & ControlPart<Op>;
|
|
143
159
|
/**
|
|
144
160
|
* How a call of an operation that may wait for an approval came out. A refusal and a failure
|
|
145
161
|
* are thrown as `ApiError`, as for every operation.
|
|
@@ -153,7 +169,10 @@ type OperationOutcome<Output, Run = unknown> = {
|
|
|
153
169
|
status: "done";
|
|
154
170
|
output: Output;
|
|
155
171
|
}
|
|
156
|
-
/**
|
|
172
|
+
/**
|
|
173
|
+
* Nothing ran yet: somebody decides it, an approver or, for a run `pending_confirmation`, the
|
|
174
|
+
* caller in their Inbox. `location` is where the run is read again.
|
|
175
|
+
*/
|
|
157
176
|
| {
|
|
158
177
|
status: "pending_approval";
|
|
159
178
|
run: Run;
|
|
@@ -169,6 +188,8 @@ type OutputOf<Op extends Operation> = Op["response"] extends {
|
|
|
169
188
|
kind: "sse";
|
|
170
189
|
schema: infer Schema extends z.ZodType;
|
|
171
190
|
} ? AsyncIterable<z.output<Schema>> : Op["response"] extends {
|
|
191
|
+
kind: "redirect";
|
|
192
|
+
} ? Promise<undefined> : Op["response"] extends {
|
|
172
193
|
schema: infer Schema extends z.ZodType;
|
|
173
194
|
} ? Op extends {
|
|
174
195
|
accepted: infer Accepted extends z.ZodType;
|
package/dist/index.js
CHANGED
|
@@ -209,6 +209,9 @@ function createApiTransport(options) {
|
|
|
209
209
|
if (operation.headers?.request?.includes("Idempotency-Key")) {
|
|
210
210
|
headers.set("idempotency-key", request.idempotencyKey ?? createIdempotencyKey());
|
|
211
211
|
}
|
|
212
|
+
if (request.unattended && operation.headers?.request?.includes("Catalyst-Unattended")) {
|
|
213
|
+
headers.set("catalyst-unattended", "1");
|
|
214
|
+
}
|
|
212
215
|
const token = await options.getToken?.();
|
|
213
216
|
if (token) {
|
|
214
217
|
headers.set("authorization", `Bearer ${token}`);
|
|
@@ -311,7 +314,13 @@ function hasEveryOperation(operations, tree) {
|
|
|
311
314
|
function callOperation(transport, operation, request) {
|
|
312
315
|
const { response } = operation;
|
|
313
316
|
if (response.kind === "sse") {
|
|
314
|
-
return streamEvents(transport, operation, response
|
|
317
|
+
return streamEvents(transport, operation, response, request);
|
|
318
|
+
}
|
|
319
|
+
if (response.kind === "redirect") {
|
|
320
|
+
return transport.send(operation, request).then(async (answer) => {
|
|
321
|
+
await answer.body?.cancel();
|
|
322
|
+
return void 0;
|
|
323
|
+
});
|
|
315
324
|
}
|
|
316
325
|
if (response.kind === "blob") {
|
|
317
326
|
return transport.send(operation, request).then((answer) => readBody(() => answer.blob(), request));
|
|
@@ -341,7 +350,7 @@ function callOperation(transport, operation, request) {
|
|
|
341
350
|
};
|
|
342
351
|
});
|
|
343
352
|
}
|
|
344
|
-
async function* streamEvents(transport, operation,
|
|
353
|
+
async function* streamEvents(transport, operation, response, request) {
|
|
345
354
|
const answer = await transport.send(operation, request);
|
|
346
355
|
if (answer.status === 204) {
|
|
347
356
|
request.onCaughtUp?.();
|
|
@@ -352,7 +361,12 @@ async function* streamEvents(transport, operation, schema, request) {
|
|
|
352
361
|
}
|
|
353
362
|
try {
|
|
354
363
|
for await (const data of readServerSentEvents(answer.body)) {
|
|
355
|
-
|
|
364
|
+
const event = readEvent(transport, data, response, answer.status);
|
|
365
|
+
if (event.skipped) {
|
|
366
|
+
request.onSkipped?.(event.value);
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
yield event.value;
|
|
356
370
|
}
|
|
357
371
|
} catch (error) {
|
|
358
372
|
throw asCallFailure(error, request);
|
|
@@ -368,6 +382,22 @@ async function readBody(read, request) {
|
|
|
368
382
|
function asCallFailure(error, request) {
|
|
369
383
|
return error instanceof ApiError || request.signal?.aborted ? error : transportError(error);
|
|
370
384
|
}
|
|
385
|
+
function readEvent(transport, text, response, status) {
|
|
386
|
+
const { skip } = response;
|
|
387
|
+
if (skip) {
|
|
388
|
+
let value;
|
|
389
|
+
try {
|
|
390
|
+
value = JSON.parse(text);
|
|
391
|
+
} catch (error) {
|
|
392
|
+
throw unreadableResponseError(status, error);
|
|
393
|
+
}
|
|
394
|
+
const parsed = response.schema.safeParse(value);
|
|
395
|
+
if (parsed.success) return { skipped: false, value: parsed.data };
|
|
396
|
+
const skipped = skip.safeParse(value);
|
|
397
|
+
if (skipped.success) return { skipped: true, value: skipped.data };
|
|
398
|
+
}
|
|
399
|
+
return { skipped: false, value: parseJson(transport, text, response.schema, status) };
|
|
400
|
+
}
|
|
371
401
|
function parseJson(transport, text, schema, status) {
|
|
372
402
|
let value;
|
|
373
403
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@work-shape/catalyst-api-client",
|
|
3
|
-
"version": "0.8.0-rc.
|
|
3
|
+
"version": "0.8.0-rc.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"zod": "4.4.3",
|
|
32
|
-
"@work-shape/catalyst-api-contract": "0.8.0-rc.
|
|
32
|
+
"@work-shape/catalyst-api-contract": "0.8.0-rc.3"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/node": "^24.13.1",
|