@pipelex/sdk 0.48.1 → 0.49.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/README.md CHANGED
@@ -167,6 +167,14 @@ for await (const saved of client.iterateMethodExamples(created.method_id)) {
167
167
  // every example, oldest first
168
168
  }
169
169
 
170
+ // The organization's files, newest first: uploads, test files and the files runs made. Each row's
171
+ // `uri` goes into a run's inputs as any address does. A filtered page can be empty and still carry
172
+ // a nextCursor; iterateFiles follows it.
173
+ const files = await client.listFiles({ q: "invoice", kind: "uploaded", format: ["pdf"] });
174
+ await client.start({ method_id: created.method_id, inputs: { invoice: { url: files.items[0]!.uri } } });
175
+ // An upload recorded as a test file of the method it was made to try.
176
+ await client.uploadFile("cases/receipt.pdf", { kind: "test", method_id: created.method_id });
177
+
170
178
  try {
171
179
  const { portal_url } = await client.getBillingPortal();
172
180
  // open portal_url ...
@@ -255,7 +263,9 @@ These pages ship inside the published package, so a reader who has only installe
255
263
  | [`docs/artifact-download.md`](./docs/artifact-download.md) | Turning the `pipelex-storage://` references a run produced back into bytes: `locateArtifacts`, `collectArtifacts`, `resolveArtifacts`, `fetchArtifact`, `downloadArtifacts`, and how a saved file is named after the field it fills |
256
264
  | [`docs/input-preparation.md`](./docs/input-preparation.md) | The other direction — `uploadFile` and `prepareInputs`, which turn local files into references a run can take, and the upload grant a browser page sends a file with |
257
265
  | [`docs/method-trees.md`](./docs/method-trees.md) | A method's tree: the four forms a run, a validation, a save and the crate routes take it in (a directory on disk among them), how it travels inline or by hash and what is uploaded, the files a step reads, the template check's warnings and which step reads which file of the tree, the deployment's limits, the read's listing with its links, `writeMethodTree`, and `MethodFileTransferError` |
266
+ | [`docs/files.md`](./docs/files.md) | An organization's files: `listFiles` and `iterateFiles`, the row and its provenance, the query and its repeated filters, paging through filtered pages, and an upload recorded as a method's test file with the refusals that come with it |
258
267
  | [`docs/method-examples.md`](./docs/method-examples.md) | A method's saved examples: the create, the paged list and its iterator, the edit that raises the input revision, the latest-run write and its `recorded` or `stale` verdict, how an integer past `Number.MAX_SAFE_INTEGER` in the inputs stays exact, and what is refused before sending |
268
+ | [`docs/organization-branding.md`](./docs/organization-branding.md) | An organization's logos and colours: `getBranding`, which every member and API key reads, `updateOrganizationBranding`, an admin's write that sets, clears or keeps each key, and how a refused logo or colour names its slot in `errors` so a settings screen shows the sentence under it |
259
269
  | [`docs/exact-integers.md`](./docs/exact-integers.md) | Which reads keep an integer past `Number.MAX_SAFE_INTEGER` exact, as a `bigint` (a run's inputs and outputs, an example's inputs), how a request body writes it back with its digits, and `parseExactJson` and `stringifyExactJson` for code showing such data as JSON text |
260
270
  | [`docs/crate-routes.md`](./docs/crate-routes.md) | `resolve`, `codegen` and `pipeIo` — the method's tree as the protocol's `files` map, with its dependencies, the normalized crate, the stamped types, and a method's I/O artifacts without a validation — and the offline `runCodegenCheck` that guards a committed tree |
261
271
  | [`docs/client-identification.md`](./docs/client-identification.md) | The `User-Agent` every API request carries, and `appInfo`, the option that puts your program's name in front of it |
@@ -15,7 +15,7 @@ import type { CommandIO } from "./io.js";
15
15
  * update notice and npm 12's run notices off the script's stderr, which carries the command's
16
16
  * own lines, while npm's warnings and errors still print.
17
17
  */
18
- export declare const SCRIPT_RUNNER = "npx --yes --no-update-notifier --loglevel=warn @pipelex/sdk@0.48.1";
18
+ export declare const SCRIPT_RUNNER = "npx --yes --no-update-notifier --loglevel=warn @pipelex/sdk@0.49.0";
19
19
  export declare const SCRIPT_WRITER = "@pipelex/sdk";
20
20
  export declare const SCRIPT_NEEDS = "Node 22.12 or later";
21
21
  /** Run `pipelex-sdk script` and return its exit code. */
package/dist/client.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { MTHDSProtocol, ModelCategory, ModelDeck, RunOptions, StartOptions, VersionInfo } from "mthds/protocol";
2
2
  import type { CodegenRequest, CodegenResponse, CrateRouteOptions, DictPipeOutput, FormatResponse, LintResponse, ModelCheckCategory, ModelReferenceVerdict, PipeIORequest, PipeIOResponse, PipelexRunResultStart, PipelexValidateSource, PipelexValidationResult, ResolveRequest, ResolveResponse, ValidateMethodSelector } from "./models.js";
3
3
  import { type GetRunResultOptions, type RunProgress, type RunRead, type RunResults, type RunResultState, type StartAndWaitForResultOptions, type WaitForResultOptions } from "./runs.js";
4
- import type { BillingPortalResponse, ChangePlanResponse, CheckoutResponse, InvoiceView, Membership, MembershipsResponse, ListMethodExamplesQuery, ListMethodsQuery, ListMethodVersionsQuery, MethodData, MethodDeletionAccepted, MethodDetail, MethodDraftInput, MethodExample, MethodExampleCreateInput, MethodExampleLatestRunInput, MethodExampleLatestRunResult, MethodExamplePage, MethodExampleUpdateInput, MethodPage, MethodPublishInput, MethodPublishResult, MethodRenameInput, MethodSummary, MethodVersion, MethodVersionPage, MethodWriteInput, OnboardingSubmission, PipelexApiKeyCreated, PipelexApiKeyList, ListRunsQuery, RunHistoryItem, RunDetail, RunPage, PlanView, ResolvedStorageUrl, SavedMethodFile, SubscriptionResponse, UpdateRunInput, UploadGrant, UploadGrantInput, UploadInput, UploadedFile, UserProfile } from "./product-models.js";
4
+ import type { BillingPortalResponse, ChangePlanResponse, CheckoutResponse, FilePage, FileSummary, InvoiceView, Membership, MembershipsResponse, OrganizationBranding, OrganizationBrandingUpdate, ListFilesQuery, ListMethodExamplesQuery, ListMethodsQuery, ListMethodVersionsQuery, MethodData, MethodDeletionAccepted, MethodDetail, MethodDraftInput, MethodExample, MethodExampleCreateInput, MethodExampleLatestRunInput, MethodExampleLatestRunResult, MethodExamplePage, MethodExampleUpdateInput, MethodPage, MethodPublishInput, MethodPublishResult, MethodRenameInput, MethodSummary, MethodVersion, MethodVersionPage, MethodWriteInput, OnboardingSubmission, PipelexApiKeyCreated, PipelexApiKeyList, ListRunsQuery, RunHistoryItem, RunDetail, RunPage, PlanView, ResolvedStorageUrl, SavedMethodFile, SubscriptionResponse, UpdateRunInput, UploadGrant, UploadGrantInput, UploadInput, UploadedFile, UserProfile } from "./product-models.js";
5
5
  import type { MethodTreeForms, MethodTreeLimits } from "./method-trees.js";
6
6
  import type { AppInfo } from "./user-agent.js";
7
7
  import type { UploadableAsset, UploadFileOptions, UploadRecord } from "./upload.js";
@@ -279,7 +279,7 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
279
279
  private readVerdict;
280
280
  /**
281
281
  * One page of a cursor-paged product route (`listMethods`, `listMethodVersions`,
282
- * `listMethodExamples`, `listRuns`), read as the platform always serializes it: `items` an
282
+ * `listMethodExamples`, `listRuns`, `listFiles`), read as the platform always serializes it: `items` an
283
283
  * array, `next_cursor` a string or `null`. A page breaking either is an answer the SDK cannot
284
284
  * read, so it throws the `ApiResponseError` `unreadableAnswer` builds, `runtime` and not
285
285
  * retryable, rather than handing the iterators a page they cannot walk: `items` that is not
@@ -1362,6 +1362,42 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
1362
1362
  renameOrganization(orgId: string, input: {
1363
1363
  name: string;
1364
1364
  }): Promise<Membership>;
1365
+ /**
1366
+ * The branding of the caller's active organization — `GET /v1/branding`: its name, its light
1367
+ * and dark colour sets and its two logos. Any role reads it, `runner` included, by web session
1368
+ * or API key, so an app scaffolded from the method-app template reads its organization's
1369
+ * branding with its own key. A personal workspace, or a team that set nothing, answers every
1370
+ * colour and logo `null`. A logo's `uri` is a `pipelex-storage://` reference, which
1371
+ * `resolveStorageUrl` turns into a short-lived link to its bytes.
1372
+ *
1373
+ * Throws `ApiResponseError`: `400` when the caller has no active organization, `404` when its
1374
+ * row is missing, and an answer that is not a JSON object, an empty one included.
1375
+ */
1376
+ getBranding(): Promise<OrganizationBranding>;
1377
+ /**
1378
+ * Set or clear an organization's branding — `PATCH /v1/organizations/{org_id}/branding`, an
1379
+ * admin's write to the active organization. Every key of `input` is optional: a value sets it,
1380
+ * `null` clears it, and a key left out keeps its value (`OrganizationBrandingUpdate`), so `{}`
1381
+ * writes nothing. A new logo is the `pipelex-storage://` URI of a file the organization uploaded
1382
+ * on its own, which the platform reads and measures, then copies under `branding/`: the answer's
1383
+ * `uri` is that copy, and sending it back unchanged keeps the logo. The logo a write replaces or
1384
+ * clears is erased. Answers the branding as the write left it.
1385
+ *
1386
+ * Throws `RequestArgumentError` for an organization id that is empty, `.` or `..`, before any
1387
+ * request. Throws `ApiResponseError`, refusing every change of the request when it refuses one:
1388
+ * `403` `forbidden` when `orgId` is not the active organization, `403` `role_not_permitted` for
1389
+ * a caller who is not its admin, `404` for a missing organization, `409` `conflict` for a
1390
+ * personal workspace, which has no branding, and `422` `validation_failed` for a colour that is
1391
+ * not `#rrggbb` or a key the body does not know. A logo's refusal names its slot in the error's
1392
+ * `errors[]`, each entry's `field` being `square_logo` or `wide_logo` and its `detail` the
1393
+ * sentence to show under that slot: `400` `invalid_storage_uri` for a URI that is not a storage
1394
+ * URI, `403` `forbidden` for another organization's file, and `422` `validation_failed` with a
1395
+ * `LogoRefusalCode` per broken rule for a file the platform refused. A failure of the platform's
1396
+ * storage while reading or copying a logo is a `500` `storage_backend_failed`, retryable, with
1397
+ * nothing written. A `503` `service_unavailable` is a failure of its database, after which the
1398
+ * write may have landed or not: the same input sent again is safe.
1399
+ */
1400
+ updateOrganizationBranding(orgId: string, input: OrganizationBrandingUpdate): Promise<OrganizationBranding>;
1365
1401
  /** The active org's subscription state — `GET /v1/billing/subscription`. */
1366
1402
  getSubscription(): Promise<SubscriptionResponse>;
1367
1403
  /** Available plans (with `is_current`) — `GET /v1/billing/plans`. */
@@ -1476,9 +1512,11 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
1476
1512
  * Upload one local asset and return its {@link UploadRecord} — the single-asset
1477
1513
  * convenience over {@link upload}. Accepts `Blob`/`File`/`ArrayBuffer`/`Uint8Array`
1478
1514
  * in every runtime; a path string is Node-only (it fails instructively elsewhere).
1479
- * The record guarantees `uri`, `contentType`, `size`, and `filename`. Transport
1480
- * failures surface as the semantic input-preparation errors (rejected asset, auth,
1481
- * unsupported capability, transport). See `docs/input-preparation.md`.
1515
+ * The record guarantees `uri`, `contentType`, `size`, and `filename`. A `kind` and its
1516
+ * `method_id` in the options record the upload as a method's test file, checked before the
1517
+ * asset is read. Transport failures surface as the semantic input-preparation errors
1518
+ * (rejected asset, refused test file, auth, unsupported capability, transport). See
1519
+ * `docs/input-preparation.md`.
1482
1520
  */
1483
1521
  uploadFile(asset: UploadableAsset, options?: UploadFileOptions): Promise<UploadRecord>;
1484
1522
  /**
@@ -1511,6 +1549,52 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
1511
1549
  * input too.
1512
1550
  */
1513
1551
  prepareInputs(request: PrepareInputsRequest): Promise<PreparedInputs>;
1552
+ /**
1553
+ * One page of the organization's files, newest first — `GET /v1/files`.
1554
+ *
1555
+ * Rows are `FileSummary`: the record's id, its `uri`, which a run's inputs take as they take
1556
+ * any address, the name a person sees, its type, size and kind, and where it came from. A list
1557
+ * never holds a file a run made along the way, nor an upload whose bytes have not been seen
1558
+ * stored. Every role of the organization reads every file of it.
1559
+ *
1560
+ * `methodId`, `runId` and `createdBy` choose which list serves the page: the method's files
1561
+ * (what its runs and examples took or made), the run's own files (its final outputs and inline
1562
+ * inputs, in the order of their addresses), one member's (`"me"` for the caller's own), or the
1563
+ * organization's. `q`, `kind`, `format`, `mediaType`, `createdFrom` and `createdTo` filter that
1564
+ * list server-side; `format` and `mediaType` are sent as one repeated parameter per value, and
1565
+ * an empty list of either is a `RequestArgumentError` before any request. The filters run after
1566
+ * the index read, so **a filtered page can hold fewer rows than `limit`, or none, and still
1567
+ * carry a `nextCursor`**: keep following it until it is `null`, with the same filters, since a
1568
+ * cursor is bound to the list and filters that issued it.
1569
+ *
1570
+ * Throws `ApiResponseError`: `400` for a date bound that is not an instant with an offset, a
1571
+ * range whose start is after its end, or a cursor of another list or other filters; `404`
1572
+ * `run_not_found` for a run that is not the organization's; `422` for a `limit` outside 1 to
1573
+ * 200, an unknown `kind`, more than 100 values of `format` or `mediaType`, or an id that cannot
1574
+ * be one. A page whose `items` is not an array or whose `next_cursor` is neither a string nor
1575
+ * `null` is an `ApiResponseError` of an answer the SDK cannot read. See `docs/files.md`.
1576
+ */
1577
+ listFiles(query?: ListFilesQuery): Promise<FilePage>;
1578
+ /**
1579
+ * Every file of the organization the query selects, streamed — follows the cursor for you.
1580
+ *
1581
+ * ```ts
1582
+ * for await (const file of client.iterateFiles({ kind: "uploaded", format: ["pdf"] })) {
1583
+ * if (file.name === "March invoice.pdf") break; // stop whenever you like
1584
+ * }
1585
+ * ```
1586
+ *
1587
+ * **Prefer `listFiles`** for anything user-facing: this makes as many round trips as the data
1588
+ * demands. It is an iterator rather than an all-at-once helper for the reason `iterateRuns` is
1589
+ * one: a helper returning an array needs a page cap, and a cap returns a truncated list with no
1590
+ * error.
1591
+ *
1592
+ * It keeps going **through an empty page that carries a cursor**, which under any filter means
1593
+ * "nothing matched in the slice just read", not "done", sending the same filters with every
1594
+ * page. It stops on a `null` cursor or on a cursor the server failed to advance, and throws
1595
+ * `PagingNotTerminatingError` past a page ceiling no real file list reaches.
1596
+ */
1597
+ iterateFiles(query?: Omit<ListFilesQuery, "cursor">): AsyncGenerator<FileSummary, void, undefined>;
1514
1598
  /**
1515
1599
  * One PAGE of a method's runs, newest first — `GET /v1/runs?method_id=`.
1516
1600
  *
package/dist/client.js CHANGED
@@ -14,7 +14,7 @@ import { PipelexExecuteResult, resultsFromExecute } from "./execute-result.js";
14
14
  import { ExactJsonUnsupportedError, parseExactJson, stringifyExactJson } from "./exact-json.js";
15
15
  import { MAX_TIMER_DELAY_MS, isTimerDelay } from "./timers.js";
16
16
  // A pure RUNAWAY guard on the paged-list iterators (`iterateMethods`,
17
- // `iterateRuns`, `iterateMethodExamples`), deliberately not a coverage limit.
17
+ // `iterateRuns`, `iterateMethodExamples`, `iterateFiles`), deliberately not a coverage limit.
18
18
  //
19
19
  // It counts TOTAL pages, not empty ones. An earlier version capped consecutive
20
20
  // empty pages, which is the wrong axis: an empty page whose cursor ADVANCED is
@@ -31,8 +31,8 @@ import { MAX_TIMER_DELAY_MS, isTimerDelay } from "./timers.js";
31
31
  const MAX_PAGES = 10_000;
32
32
  /**
33
33
  * Follow a paged list's cursor until the server says there is no more, yielding every row —
34
- * the one loop behind `iterateMethods`, `iterateRuns` and `iterateMethodExamples`, so they cannot
35
- * drift apart.
34
+ * the one loop behind `iterateMethods`, `iterateRuns`, `iterateMethodExamples` and
35
+ * `iterateFiles`, so they cannot drift apart.
36
36
  *
37
37
  * It yields lazily: a caller that breaks early stops the requests. It stops when `nextCursor`
38
38
  * is `null`, the normal end, and when the server hands back the cursor it was just sent, which
@@ -186,6 +186,24 @@ function productQuery(params) {
186
186
  const text = search.toString();
187
187
  return text ? `?${text}` : "";
188
188
  }
189
+ /**
190
+ * A list filter sent as one repeated query parameter per value, such as `listFiles`' `format`:
191
+ * `undefined` when omitted, the values as given otherwise. An empty list names no value, and sent
192
+ * as no parameter at all it would read as no filter and list everything, so it is a
193
+ * `RequestArgumentError` before any request; a value that is not a list of strings is a
194
+ * `TypeError`, a bug in the calling code.
195
+ */
196
+ function repeatedFilter(call, name, values) {
197
+ if (values === undefined)
198
+ return undefined;
199
+ if (!Array.isArray(values) || values.some((value) => typeof value !== "string")) {
200
+ throw new TypeError(`${call}() takes "${name}" as a list of strings.`);
201
+ }
202
+ if (values.length === 0) {
203
+ throw new RequestArgumentError(`${call}() takes "${name}" as one or more values; omit it to list every ${name}.`);
204
+ }
205
+ return values;
206
+ }
189
207
  /** `items` joined as a sentence names them, the last after `conjunction`: `a`, `a or b`, `a, b or c`. */
190
208
  function inWords(items, conjunction) {
191
209
  if (items.length < 2)
@@ -552,7 +570,7 @@ export class PipelexApiClient {
552
570
  }
553
571
  /**
554
572
  * One page of a cursor-paged product route (`listMethods`, `listMethodVersions`,
555
- * `listMethodExamples`, `listRuns`), read as the platform always serializes it: `items` an
573
+ * `listMethodExamples`, `listRuns`, `listFiles`), read as the platform always serializes it: `items` an
556
574
  * array, `next_cursor` a string or `null`. A page breaking either is an answer the SDK cannot
557
575
  * read, so it throws the `ApiResponseError` `unreadableAnswer` builds, `runtime` and not
558
576
  * retryable, rather than handing the iterators a page they cannot walk: `items` that is not
@@ -2499,6 +2517,47 @@ export class PipelexApiClient {
2499
2517
  const segment = idPathSegment(orgId, "renameOrganization", ORG_ID_ARGUMENT);
2500
2518
  return this.requestProduct("PATCH", `organizations/${segment}`, input);
2501
2519
  }
2520
+ /**
2521
+ * The branding of the caller's active organization — `GET /v1/branding`: its name, its light
2522
+ * and dark colour sets and its two logos. Any role reads it, `runner` included, by web session
2523
+ * or API key, so an app scaffolded from the method-app template reads its organization's
2524
+ * branding with its own key. A personal workspace, or a team that set nothing, answers every
2525
+ * colour and logo `null`. A logo's `uri` is a `pipelex-storage://` reference, which
2526
+ * `resolveStorageUrl` turns into a short-lived link to its bytes.
2527
+ *
2528
+ * Throws `ApiResponseError`: `400` when the caller has no active organization, `404` when its
2529
+ * row is missing, and an answer that is not a JSON object, an empty one included.
2530
+ */
2531
+ async getBranding() {
2532
+ return this.requestProductObject("GET", "branding");
2533
+ }
2534
+ /**
2535
+ * Set or clear an organization's branding — `PATCH /v1/organizations/{org_id}/branding`, an
2536
+ * admin's write to the active organization. Every key of `input` is optional: a value sets it,
2537
+ * `null` clears it, and a key left out keeps its value (`OrganizationBrandingUpdate`), so `{}`
2538
+ * writes nothing. A new logo is the `pipelex-storage://` URI of a file the organization uploaded
2539
+ * on its own, which the platform reads and measures, then copies under `branding/`: the answer's
2540
+ * `uri` is that copy, and sending it back unchanged keeps the logo. The logo a write replaces or
2541
+ * clears is erased. Answers the branding as the write left it.
2542
+ *
2543
+ * Throws `RequestArgumentError` for an organization id that is empty, `.` or `..`, before any
2544
+ * request. Throws `ApiResponseError`, refusing every change of the request when it refuses one:
2545
+ * `403` `forbidden` when `orgId` is not the active organization, `403` `role_not_permitted` for
2546
+ * a caller who is not its admin, `404` for a missing organization, `409` `conflict` for a
2547
+ * personal workspace, which has no branding, and `422` `validation_failed` for a colour that is
2548
+ * not `#rrggbb` or a key the body does not know. A logo's refusal names its slot in the error's
2549
+ * `errors[]`, each entry's `field` being `square_logo` or `wide_logo` and its `detail` the
2550
+ * sentence to show under that slot: `400` `invalid_storage_uri` for a URI that is not a storage
2551
+ * URI, `403` `forbidden` for another organization's file, and `422` `validation_failed` with a
2552
+ * `LogoRefusalCode` per broken rule for a file the platform refused. A failure of the platform's
2553
+ * storage while reading or copying a logo is a `500` `storage_backend_failed`, retryable, with
2554
+ * nothing written. A `503` `service_unavailable` is a failure of its database, after which the
2555
+ * write may have landed or not: the same input sent again is safe.
2556
+ */
2557
+ async updateOrganizationBranding(orgId, input) {
2558
+ const segment = idPathSegment(orgId, "updateOrganizationBranding", ORG_ID_ARGUMENT);
2559
+ return this.requestProductObject("PATCH", `organizations/${segment}/branding`, input);
2560
+ }
2502
2561
  /** The active org's subscription state — `GET /v1/billing/subscription`. */
2503
2562
  async getSubscription() {
2504
2563
  return this.requestProduct("GET", "billing/subscription");
@@ -2642,9 +2701,11 @@ export class PipelexApiClient {
2642
2701
  * Upload one local asset and return its {@link UploadRecord} — the single-asset
2643
2702
  * convenience over {@link upload}. Accepts `Blob`/`File`/`ArrayBuffer`/`Uint8Array`
2644
2703
  * in every runtime; a path string is Node-only (it fails instructively elsewhere).
2645
- * The record guarantees `uri`, `contentType`, `size`, and `filename`. Transport
2646
- * failures surface as the semantic input-preparation errors (rejected asset, auth,
2647
- * unsupported capability, transport). See `docs/input-preparation.md`.
2704
+ * The record guarantees `uri`, `contentType`, `size`, and `filename`. A `kind` and its
2705
+ * `method_id` in the options record the upload as a method's test file, checked before the
2706
+ * asset is read. Transport failures surface as the semantic input-preparation errors
2707
+ * (rejected asset, refused test file, auth, unsupported capability, transport). See
2708
+ * `docs/input-preparation.md`.
2648
2709
  */
2649
2710
  async uploadFile(asset, options) {
2650
2711
  return uploadFileImpl(this, asset, options);
@@ -2681,6 +2742,69 @@ export class PipelexApiClient {
2681
2742
  async prepareInputs(request) {
2682
2743
  return prepareInputsImpl(this, request);
2683
2744
  }
2745
+ /**
2746
+ * One page of the organization's files, newest first — `GET /v1/files`.
2747
+ *
2748
+ * Rows are `FileSummary`: the record's id, its `uri`, which a run's inputs take as they take
2749
+ * any address, the name a person sees, its type, size and kind, and where it came from. A list
2750
+ * never holds a file a run made along the way, nor an upload whose bytes have not been seen
2751
+ * stored. Every role of the organization reads every file of it.
2752
+ *
2753
+ * `methodId`, `runId` and `createdBy` choose which list serves the page: the method's files
2754
+ * (what its runs and examples took or made), the run's own files (its final outputs and inline
2755
+ * inputs, in the order of their addresses), one member's (`"me"` for the caller's own), or the
2756
+ * organization's. `q`, `kind`, `format`, `mediaType`, `createdFrom` and `createdTo` filter that
2757
+ * list server-side; `format` and `mediaType` are sent as one repeated parameter per value, and
2758
+ * an empty list of either is a `RequestArgumentError` before any request. The filters run after
2759
+ * the index read, so **a filtered page can hold fewer rows than `limit`, or none, and still
2760
+ * carry a `nextCursor`**: keep following it until it is `null`, with the same filters, since a
2761
+ * cursor is bound to the list and filters that issued it.
2762
+ *
2763
+ * Throws `ApiResponseError`: `400` for a date bound that is not an instant with an offset, a
2764
+ * range whose start is after its end, or a cursor of another list or other filters; `404`
2765
+ * `run_not_found` for a run that is not the organization's; `422` for a `limit` outside 1 to
2766
+ * 200, an unknown `kind`, more than 100 values of `format` or `mediaType`, or an id that cannot
2767
+ * be one. A page whose `items` is not an array or whose `next_cursor` is neither a string nor
2768
+ * `null` is an `ApiResponseError` of an answer the SDK cannot read. See `docs/files.md`.
2769
+ */
2770
+ async listFiles(query = {}) {
2771
+ const search = productQuery({
2772
+ method_id: query.methodId,
2773
+ run_id: query.runId,
2774
+ created_by: query.createdBy,
2775
+ kind: query.kind,
2776
+ format: repeatedFilter("listFiles", "format", query.format),
2777
+ media_type: repeatedFilter("listFiles", "mediaType", query.mediaType),
2778
+ created_from: query.createdFrom,
2779
+ created_to: query.createdTo,
2780
+ q: query.q,
2781
+ limit: query.limit,
2782
+ cursor: query.cursor,
2783
+ });
2784
+ return this.requestPage(`files${search}`);
2785
+ }
2786
+ /**
2787
+ * Every file of the organization the query selects, streamed — follows the cursor for you.
2788
+ *
2789
+ * ```ts
2790
+ * for await (const file of client.iterateFiles({ kind: "uploaded", format: ["pdf"] })) {
2791
+ * if (file.name === "March invoice.pdf") break; // stop whenever you like
2792
+ * }
2793
+ * ```
2794
+ *
2795
+ * **Prefer `listFiles`** for anything user-facing: this makes as many round trips as the data
2796
+ * demands. It is an iterator rather than an all-at-once helper for the reason `iterateRuns` is
2797
+ * one: a helper returning an array needs a page cap, and a cap returns a truncated list with no
2798
+ * error.
2799
+ *
2800
+ * It keeps going **through an empty page that carries a cursor**, which under any filter means
2801
+ * "nothing matched in the slice just read", not "done", sending the same filters with every
2802
+ * page. It stops on a `null` cursor or on a cursor the server failed to advance, and throws
2803
+ * `PagingNotTerminatingError` past a page ceiling no real file list reaches.
2804
+ */
2805
+ async *iterateFiles(query = {}) {
2806
+ yield* followCursor("listFiles", (cursor) => this.listFiles({ ...query, cursor }));
2807
+ }
2684
2808
  /**
2685
2809
  * One PAGE of a method's runs, newest first — `GET /v1/runs?method_id=`.
2686
2810
  *