contree-client 0.2.1-dev0 → 0.2.1

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/lib/client.d.ts CHANGED
@@ -215,10 +215,10 @@ export declare class ContreeClient {
215
215
  inspectImageList(imageUuid: string, path: string): Promise<DirectoryList>;
216
216
  inspectImageGrep(
217
217
  imageUuid: string,
218
- pattern: string,
218
+ pattern: string | readonly string[],
219
219
  options?: {
220
- path?: string | null;
221
- glob?: string | null;
220
+ path?: string | readonly string[] | null;
221
+ glob?: string | readonly string[] | null;
222
222
  max_count?: number | null;
223
223
  max_total?: number | null;
224
224
  case?: "sensitive" | "insensitive" | "smart" | null;
package/lib/client.js CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  } from "./errors.js";
9
9
  import { AUTH_TYPE_IAM, ProfileError, resolveProfile } from "./profiles.js";
10
10
  import {
11
+ EVENTS_UNAVAILABLE_STATUSES,
11
12
  IS_NODE,
12
13
  SSEParser,
13
14
  TIGHT_LOOP_FLOOR,
@@ -25,7 +26,11 @@ import {
25
26
  sleep,
26
27
  } from "./runtime.js";
27
28
  import { DEFAULT_BASE_URL } from "./specInfo.js";
28
- import { OperationEvent, isTerminalStatus } from "./models.js";
29
+ import {
30
+ EventDataCompletion,
31
+ OperationEvent,
32
+ isTerminalStatus,
33
+ } from "./models.js";
29
34
  import * as operations from "./operations.js";
30
35
 
31
36
  /** Contree API client on top of the platform fetch.
@@ -409,8 +414,13 @@ export class ContreeClient {
409
414
 
410
415
  /** Stream operation events with transparent reconnection: network
411
416
  * drops, in-band SSE error frames and retryable API statuses
412
- * (410/425/5xx) reconnect from the last received event id; other
413
- * API errors propagate. Ends after the `completion` event. */
417
+ * (410/425/5xx) reconnect from the last received event id. A status
418
+ * meaning the events endpoint itself doesn't exist for this
419
+ * operation/server (400/404/405/406) stops reconnecting and instead
420
+ * polls getOperationStatus() until the operation is terminal, then
421
+ * yields a synthesized `completion` event built from that status
422
+ * (there is no real event log to relay). Other API errors
423
+ * propagate. Ends after the `completion` event. */
414
424
  async *followOperationEvents(
415
425
  operationId,
416
426
  { last_event_id = null, spid = null, since = null, timeout = null } = {},
@@ -450,6 +460,64 @@ export class ContreeClient {
450
460
  lastId = error.lastEventId;
451
461
  }
452
462
  } else if (error instanceof ContreeAPIError) {
463
+ if (EVENTS_UNAVAILABLE_STATUSES.has(error.status)) {
464
+ // the endpoint is gone, not just failing: reconnecting the
465
+ // same request will never work, but the operation may
466
+ // still finish - stop touching /events and poll status
467
+ // instead for the rest of this wait
468
+ for (;;) {
469
+ checkDeadline();
470
+ let response;
471
+ try {
472
+ response = await this.getOperationStatus(operationId);
473
+ } catch (pollError) {
474
+ if (
475
+ !(pollError instanceof ContreeError) &&
476
+ !this._transportRetryable(pollError)
477
+ ) {
478
+ throw pollError;
479
+ }
480
+ response = null;
481
+ }
482
+ if (
483
+ response !== null &&
484
+ response.status !== undefined &&
485
+ isTerminalStatus(response.status)
486
+ ) {
487
+ // no event log to relay, but a caller of
488
+ // followOperationEvents must still observe a terminal
489
+ // completion rather than nothing at all
490
+ lastId = lastId === null ? 0 : lastId + 1;
491
+ yield new OperationEvent({
492
+ id: lastId,
493
+ ts: new Date(),
494
+ type: "completion",
495
+ data: new EventDataCompletion({
496
+ status: response.status,
497
+ duration_ms:
498
+ typeof response.duration === "number"
499
+ ? Math.round(response.duration * 1000)
500
+ : 0,
501
+ result_image_uuid: response.result_image_uuid,
502
+ error: response.error,
503
+ image_size_bytes:
504
+ typeof response.image_size === "number"
505
+ ? response.image_size
506
+ : undefined,
507
+ }),
508
+ });
509
+ return;
510
+ }
511
+ let pollDelay = delays.next().value;
512
+ if (deadline !== null) {
513
+ pollDelay = Math.min(
514
+ pollDelay,
515
+ Math.max(0, deadline - monotonic()),
516
+ );
517
+ }
518
+ await sleep(pollDelay);
519
+ }
520
+ }
453
521
  const retryable =
454
522
  error.status === 410 ||
455
523
  error.status === 425 ||
@@ -246,10 +246,10 @@ export declare function parseInspectImageList(
246
246
 
247
247
  export declare function buildInspectImageGrep(
248
248
  imageUuid: string,
249
- pattern: string,
249
+ pattern: string | readonly string[],
250
250
  options?: {
251
- path?: string | null;
252
- glob?: string | null;
251
+ path?: string | readonly string[] | null;
252
+ glob?: string | readonly string[] | null;
253
253
  max_count?: number | null;
254
254
  max_total?: number | null;
255
255
  case?: "sensitive" | "insensitive" | "smart" | null;
package/lib/runtime.d.ts CHANGED
@@ -21,7 +21,7 @@ export type RequestBody =
21
21
  export interface RequestSpec {
22
22
  method: string;
23
23
  path: string;
24
- query?: Record<string, string>;
24
+ query?: Record<string, string | readonly string[]>;
25
25
  headers?: Record<string, string>;
26
26
  body?: RequestBody;
27
27
  contentType?: string | null;
@@ -58,7 +58,9 @@ export declare function sleep(seconds: number): Promise<void>;
58
58
  export declare function monotonic(): number;
59
59
  export declare function isUuid(ref: string): boolean;
60
60
  export declare function quotePath(value: unknown): string;
61
- export declare function encodeQuery(query: Record<string, string>): string;
61
+ export declare function encodeQuery(
62
+ query: Record<string, string | readonly string[]>,
63
+ ): string;
62
64
  export declare function formatTimeParam(value: string | number | Date): string;
63
65
  export declare function parseDatetime(value: string): Date;
64
66
  export declare function bytesToText(bytes: Uint8Array): string;
package/lib/runtime.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
 
15
15
  export const CHUNK_SIZE = 65536;
16
16
 
17
- export const PACKAGE_VERSION = "0.2.1-dev0";
17
+ export const PACKAGE_VERSION = "0.2.1";
18
18
  export const UA_PRODUCT = `contree-client-js/${PACKAGE_VERSION}`;
19
19
 
20
20
  const NODE_VERSION =
@@ -38,6 +38,17 @@ export const RETRY_DELAYS = Object.freeze([0.1, 0.2, 0.5, 1.0, 2.0, 5.0]);
38
38
  // returning immediate empty streams does not spin the client
39
39
  export const TIGHT_LOOP_FLOOR = 0.5;
40
40
 
41
+ // the events route itself doesn't exist for this operation/server
42
+ // (malformed request an older backend rejects, or a reverse proxy
43
+ // that never forwards it) rather than merely being down: reconnecting
44
+ // will never succeed, but the operation itself may still complete -
45
+ // degrade to polling instead of failing the whole wait. 401/403 are
46
+ // deliberately excluded: an auth/permission failure here likely means
47
+ // the whole client is broken, not just this route, so it still throws
48
+ export const EVENTS_UNAVAILABLE_STATUSES = Object.freeze(
49
+ new Set([400, 404, 405, 406]),
50
+ );
51
+
41
52
  /** An endless ladder of backoff delays: the ladder is walked once and
42
53
  * then the tail delay repeats forever. */
43
54
  export function* retryDelays(delays = RETRY_DELAYS) {
@@ -140,9 +151,11 @@ export function quotePath(value) {
140
151
 
141
152
  export function encodeQuery(query) {
142
153
  return Object.entries(query)
143
- .map(
144
- ([key, value]) =>
145
- `${encodeURIComponent(key)}=${encodeURIComponent(value).replaceAll("%2F", "/")}`,
154
+ .flatMap(([key, value]) =>
155
+ (Array.isArray(value) ? value : [value]).map(
156
+ (item) =>
157
+ `${encodeURIComponent(key)}=${encodeURIComponent(item).replaceAll("%2F", "/")}`,
158
+ ),
146
159
  )
147
160
  .join("&");
148
161
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "contree-client",
3
- "version": "0.2.1-dev0",
3
+ "version": "0.2.1",
4
4
  "description": "JavaScript client for the Contree API, generated from the OpenAPI spec",
5
5
  "homepage": "https://contree.dev/",
6
6
  "repository": {