@pipelex/sdk 0.60.0 → 0.61.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 +13 -0
- package/dist/cli/script.d.ts +1 -1
- package/dist/client.d.ts +91 -21
- package/dist/client.js +241 -23
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/product-models.d.ts +214 -20
- package/dist/product-models.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/architecture.md +2 -1
- package/docs/files.md +1 -0
- package/docs/method-copy.md +9 -5
- package/docs/method-trees.md +1 -1
- package/docs/public-methods.md +97 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -178,6 +178,18 @@ const copy = await client.copyMethod(
|
|
|
178
178
|
);
|
|
179
179
|
if (copy.outcome === "copied") console.log(copy.method.copied_from); // the original it names
|
|
180
180
|
|
|
181
|
+
// The template catalog: the public methods every organization reads, in the platform's order, with
|
|
182
|
+
// the categories that have a template and each card's thumbnails behind short-lived links.
|
|
183
|
+
const templates = await client.listMethodTemplates({ category: "human-resources", limit: 8 });
|
|
184
|
+
for (const tab of templates.categories) console.log(tab.slug, tab.label);
|
|
185
|
+
// Another organization's public method reads in a narrower shape, its latest published version
|
|
186
|
+
// and no draft: `updated_at` is what tells it from one of your own.
|
|
187
|
+
const first = templates.items[0];
|
|
188
|
+
const template = first === undefined ? undefined : await client.getMethod(first.method_id);
|
|
189
|
+
if (template !== undefined && !("updated_at" in template)) {
|
|
190
|
+
console.log(template.latest_published.categories);
|
|
191
|
+
}
|
|
192
|
+
|
|
181
193
|
// The organization's files, newest first: uploads, test files and the files runs made. Each row's
|
|
182
194
|
// `uri` goes into a run's inputs as any address does. A filtered page can be empty and still carry
|
|
183
195
|
// a nextCursor; iterateFiles follows it.
|
|
@@ -285,6 +297,7 @@ These pages ship inside the published package, so a reader who has only installe
|
|
|
285
297
|
| [`docs/files.md`](./docs/files.md) | An organization's files: `listFiles` and `iterateFiles`, the row, the whole record and its provenance, the query and its repeated filters, paging through filtered pages, one file and its uses, the records of an address, the rename rule, links that name the file, a zip of files and its caps, a run's files, the copy into a method's draft, and an upload recorded as a method's test file with the refusals that come with it |
|
|
286
298
|
| [`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, the build assistant's save and its `kept` or `created` verdict, how an integer past `Number.MAX_SAFE_INTEGER` in the inputs stays exact, and what is refused before sending |
|
|
287
299
|
| [`docs/method-copy.md`](./docs/method-copy.md) | Copying a method into another organization or duplicating it: what a copy holds and the stored files it copies, the `copied` or `refused` verdict, every refusal, the copy's idempotency key, and `copied_from` |
|
|
300
|
+
| [`docs/public-methods.md`](./docs/public-methods.md) | Public methods and the template catalog: `listMethodTemplates` and its iterator, the ordering, the categories and the thumbnails' links, another organization's public method read in its narrower shape, the copy of a public method, `visibility` and `categories` on a method, and the refusals a public method's publish adds |
|
|
288
301
|
| [`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 |
|
|
289
302
|
| [`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 |
|
|
290
303
|
| [`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 |
|
package/dist/cli/script.d.ts
CHANGED
|
@@ -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.
|
|
18
|
+
export declare const SCRIPT_RUNNER = "npx --yes --no-update-notifier --loglevel=warn @pipelex/sdk@0.61.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, DraftFileFromFileInput, DraftFileFromFileResult, FileArchive, FileArchiveRequest, FileDetail, FileLinkTarget, FileLinksResult, FilePage, FileRecord, FileRecordList, FileRenameInput, FileSummary, GetFileQuery, GetFilesByUriOptions, InvoiceView, Membership, MembershipsResponse, OrganizationBranding, OrganizationBrandingUpdate, ListFilesQuery, ListMethodExamplesQuery, ListMethodsQuery, ListMethodVersionsQuery, ListRunFilesQuery, MethodCopyInput, MethodCopyResult, MethodData, MethodDeletionAccepted, MethodDetail, MethodDraftInput, MethodExample, MethodExampleAssistantSaveInput, MethodExampleAssistantSaveResult, MethodExampleCreateInput, MethodExampleLatestRunInput, MethodExampleLatestRunResult, MethodExamplePage, MethodExampleUpdateInput, MethodPage, MethodPublishInput, MethodPublishResult, MethodRenameInput, MethodSummary, MethodVersion, MethodVersionPage, MethodWriteInput, OnboardingSubmission, PipelexApiKeyCreated, PipelexApiKeyList, ListRunsQuery, RunHistoryItem, RunDetail, RunFilesPage, RunPage, PlanView, ResolvedStorageUrl, SavedMethodFile, SubscriptionResponse, UpdateRunInput, UploadGrant, UploadGrantInput, UploadInput, UploadedFile, UserProfile } from "./product-models.js";
|
|
4
|
+
import type { BillingPortalResponse, ChangePlanResponse, CheckoutResponse, DraftFileFromFileInput, DraftFileFromFileResult, FileArchive, FileArchiveRequest, FileDetail, FileLinkTarget, FileLinksResult, FilePage, FileRecord, FileRecordList, FileRenameInput, FileSummary, GetFileQuery, GetFilesByUriOptions, InvoiceView, Membership, MembershipsResponse, OrganizationBranding, OrganizationBrandingUpdate, ListFilesQuery, ListMethodExamplesQuery, ListMethodsQuery, ListMethodTemplatesQuery, ListMethodVersionsQuery, ListRunFilesQuery, MethodCopyInput, MethodCopyResult, MethodData, MethodDeletionAccepted, MethodDetail, MethodDraftInput, MethodExample, MethodExampleAssistantSaveInput, MethodExampleAssistantSaveResult, MethodExampleCreateInput, MethodExampleLatestRunInput, MethodExampleLatestRunResult, MethodExamplePage, MethodExampleUpdateInput, MethodPage, MethodPublishInput, MethodPublishResult, MethodRenameInput, MethodSummary, MethodTemplate, MethodTemplatePage, MethodVersion, MethodVersionPage, MethodWriteInput, OnboardingSubmission, PublicMethodDetail, PipelexApiKeyCreated, PipelexApiKeyList, ListRunsQuery, RunHistoryItem, RunDetail, RunFilesPage, 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 { WebhookDeliveryDetail, WebhookDeliveryList, WebhookReplayAccepted, WebhookReplayOptions, WebhookSecret, WebhookSecretRotateOptions } from "./webhooks.js";
|
|
@@ -988,7 +988,49 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
988
988
|
*/
|
|
989
989
|
iterateMethods(query?: Omit<ListMethodsQuery, "cursor">): AsyncGenerator<MethodSummary, void, undefined>;
|
|
990
990
|
/**
|
|
991
|
-
*
|
|
991
|
+
* One page of the template catalog — `GET /v1/method-templates`: the public methods of the
|
|
992
|
+
* organization the deployment names as its templates organization, the same for every caller.
|
|
993
|
+
*
|
|
994
|
+
* The templates the ordering lists lead, in its order, and every other template follows by name.
|
|
995
|
+
* `ordering` names the ordering to serve, such as `default` or an arm of a test; an absent,
|
|
996
|
+
* unknown or malformed name serves `default`, and the page's `ordering` names the one served,
|
|
997
|
+
* `null` when there is none. `category` keeps one category's templates, by slug, in the same
|
|
998
|
+
* order; an unknown slug keeps none. Whatever the filter, the page's `categories` names, with its
|
|
999
|
+
* label, every category at least one template shows, in the catalog's order, so no tab drawn
|
|
1000
|
+
* from it is ever empty.
|
|
1001
|
+
*
|
|
1002
|
+
* Each `MethodTemplate` carries the method's id and name, its latest published version's number,
|
|
1003
|
+
* description and categories, its organization's display name and each thumbnail with its size
|
|
1004
|
+
* and a link to its bytes valid for about fifteen minutes. A template reads, runs and copies by
|
|
1005
|
+
* its `method_id`, as another organization's public method does (`getMethod`, `copyMethod`).
|
|
1006
|
+
*
|
|
1007
|
+
* Pass the page's `nextCursor` back as `cursor` with the same `ordering` and `category`, which the
|
|
1008
|
+
* cursor does not hold; `null` is the last page. `iterateMethodTemplates` follows it for you.
|
|
1009
|
+
*
|
|
1010
|
+
* Throws `ApiResponseError`: `422` `request.invalid` for a `limit` outside 1 to 200 or a cursor of
|
|
1011
|
+
* another listing, `422` `request.malformed` for a cursor that does not decode, `500`
|
|
1012
|
+
* `runtime.misconfigured` when the templates organization the deployment names has no record or
|
|
1013
|
+
* a link cannot be signed, and `503` `runtime.unavailable`, safe to retry. A page whose `items`
|
|
1014
|
+
* or `categories` is not an array, whose `next_cursor` or `ordering` is neither a string nor
|
|
1015
|
+
* `null`, a template lacking a member a card is drawn from (a string `method_id`, `name` and
|
|
1016
|
+
* `organization_name`, a `description` that is a string or `null`, a positive integer
|
|
1017
|
+
* `latest_version`, an array of string `categories`, and `thumbnails` each with a string `path`,
|
|
1018
|
+
* `media_type`, `sha256`, `url` and `expires_at` and a positive integer `width` and `height`), and a
|
|
1019
|
+
* category without a string `slug` and `label` are an answer the SDK cannot read, thrown as an
|
|
1020
|
+
* `ApiResponseError` too, its links redacted.
|
|
1021
|
+
*/
|
|
1022
|
+
listMethodTemplates(query?: ListMethodTemplatesQuery): Promise<MethodTemplatePage>;
|
|
1023
|
+
/**
|
|
1024
|
+
* Every template of the catalog, in the order served — follows `listMethodTemplates`'s cursor
|
|
1025
|
+
* for you, with the same `ordering` and `category` on every page. It stops on a `null` cursor or
|
|
1026
|
+
* on a cursor the server failed to advance, and throws `PagingNotTerminatingError` past a page
|
|
1027
|
+
* ceiling no real catalog reaches. The ordering served and the categories are on each page, so
|
|
1028
|
+
* read one page with `listMethodTemplates` when you need them.
|
|
1029
|
+
*/
|
|
1030
|
+
iterateMethodTemplates(query?: Omit<ListMethodTemplatesQuery, "cursor">): AsyncGenerator<MethodTemplate, void, undefined>;
|
|
1031
|
+
/**
|
|
1032
|
+
* Read one method with its files — `GET /v1/methods/{id}`. One of the caller's organization's
|
|
1033
|
+
* methods answers a `MethodDetail`: its identity, its draft
|
|
992
1034
|
* (`files`, `input_data`) with the draft's token (`updated_at`) and digest (`draft_digest`),
|
|
993
1035
|
* and its latest published version's summary (`latest_version`, `latest_published`), whatever
|
|
994
1036
|
* the publish state. A method never published reads with both `null`, never as an error.
|
|
@@ -996,6 +1038,11 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
996
1038
|
* link to its bytes valid for about fifteen minutes, and is empty for an empty draft;
|
|
997
1039
|
* `writeMethodTree` brings the tree to disk.
|
|
998
1040
|
*
|
|
1041
|
+
* **Another organization's public method reads too, in a narrower shape**, a
|
|
1042
|
+
* `PublicMethodDetail`: its latest published version, that version's description, categories
|
|
1043
|
+
* and files, and nothing of its draft or of the owner's people, so no `updated_at` to write or
|
|
1044
|
+
* publish with. Narrow the answer with `"updated_at" in method` before reading the draft.
|
|
1045
|
+
*
|
|
999
1046
|
* Takes a bare catalog id: the method routes address the method itself, never a version of
|
|
1000
1047
|
* it. A caller holding `mt_…@3` strips the suffix with `parseMethodSelector` and reads that
|
|
1001
1048
|
* version with `getMethodVersion`. Every method route throws `RequestArgumentError` for a
|
|
@@ -1003,13 +1050,18 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
1003
1050
|
* and for an id that is empty, `.` or `..`, which the URL would resolve into another route's
|
|
1004
1051
|
* path.
|
|
1005
1052
|
*
|
|
1006
|
-
* Throws `ApiResponseError`: `404` `pipelex.catalog.not_found` for an unknown
|
|
1007
|
-
*
|
|
1008
|
-
* `
|
|
1009
|
-
*
|
|
1010
|
-
* `
|
|
1053
|
+
* Throws `ApiResponseError`: `404` `pipelex.catalog.not_found` for an unknown method, for
|
|
1054
|
+
* another organization's internal one, and for a public one made internal again or being
|
|
1055
|
+
* erased; `409` `pipelex.catalog.being_deleted` while the erasure of the caller's own method
|
|
1056
|
+
* runs; and a `500` `runtime.unexpected` for a method whose tree the platform holds no record
|
|
1057
|
+
* of. An answer whose `files` is not a listing is an answer the SDK cannot read, thrown as an
|
|
1058
|
+
* `ApiResponseError` too, its links redacted, and so is one that lacks `updated_at` while it
|
|
1059
|
+
* carries a member only the owner's read has (`draft_digest`, `created_by_user_id`,
|
|
1060
|
+
* `input_data`, `pipe_output`, `copied_from`, `deletion_state`), which would otherwise pass for
|
|
1061
|
+
* another organization's public method, and a public read without an integer `latest_version`
|
|
1062
|
+
* and a `latest_published` object.
|
|
1011
1063
|
*/
|
|
1012
|
-
getMethod(methodId: string): Promise<MethodDetail>;
|
|
1064
|
+
getMethod(methodId: string): Promise<MethodDetail | PublicMethodDetail>;
|
|
1013
1065
|
/**
|
|
1014
1066
|
* Create a method — `POST /v1/methods`. The new method holds its tree as its draft and has no
|
|
1015
1067
|
* published version, so its bare id answers `409` `pipelex.catalog.not_published` on the run and
|
|
@@ -1239,9 +1291,12 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
1239
1291
|
*
|
|
1240
1292
|
* The answer is a `MethodPublishResult` discriminated on `outcome` — branch on it:
|
|
1241
1293
|
* `published` with the new `version`; `unchanged` with the existing latest `version`;
|
|
1242
|
-
* `refused` with a `reason
|
|
1243
|
-
*
|
|
1244
|
-
*
|
|
1294
|
+
* `refused` with a `reason`, a `message` and the runner's `validation` verdict, whose problem
|
|
1295
|
+
* items are read member by member as `validate` reads them. The reason is `invalid`, or
|
|
1296
|
+
* `not_runnable` for a draft that validates with pending signatures, and for a public method
|
|
1297
|
+
* also `thumbnail_missing`, `thumbnail_invalid` or `storage_reference` (see
|
|
1298
|
+
* `MethodPublishRefusalReason`); a reason this SDK does not know is passed through as its raw
|
|
1299
|
+
* string, still a verdict, so show its `message`. Every arm carries the `method`. A publish
|
|
1245
1300
|
* moves no token.
|
|
1246
1301
|
*
|
|
1247
1302
|
* Takes a bare catalog id, as `getMethod` does. Throws `RequestArgumentError` when
|
|
@@ -1256,11 +1311,14 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
1256
1311
|
* cannot be reached, answers unusably or does not finish within the platform's deadline is a
|
|
1257
1312
|
* `502` or a `503` with nothing written, so a retry is safe; a runner that refuses the request
|
|
1258
1313
|
* itself is relayed under its own status. A retry of a publish that landed while its answer was
|
|
1259
|
-
* lost, as on a client timeout, answers `unchanged` with the version it wrote.
|
|
1314
|
+
* lost, as on a client timeout, answers `unchanged` with the version it wrote. A method made
|
|
1315
|
+
* public while its publish ran is a `503` with nothing written, safe to retry, which a retry
|
|
1316
|
+
* judges as a public method's publish.
|
|
1260
1317
|
*
|
|
1261
1318
|
* An answer whose `outcome` is not one of the outcomes above, that carries no `method` object,
|
|
1262
|
-
* or a refusal whose `
|
|
1263
|
-
* the SDK cannot read, thrown as an
|
|
1319
|
+
* or a refusal whose `reason` or `message` is not a string or whose `validation` is not a verdict
|
|
1320
|
+
* object with a boolean `is_valid`, is an answer the SDK cannot read, thrown as an
|
|
1321
|
+
* `ApiResponseError` too.
|
|
1264
1322
|
*/
|
|
1265
1323
|
publishMethod(methodId: string, input: MethodPublishInput): Promise<MethodPublishResult>;
|
|
1266
1324
|
/**
|
|
@@ -1269,11 +1327,13 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
1269
1327
|
*
|
|
1270
1328
|
* Pass the page's `nextCursor` back as `cursor` to continue; `null` is the last page. A page
|
|
1271
1329
|
* may be short while `nextCursor` is set, because the platform reads versions whole. A method
|
|
1272
|
-
* never published answers an empty page.
|
|
1330
|
+
* never published answers an empty page. Another organization's public method lists its
|
|
1331
|
+
* versions too, each without its `published_by`.
|
|
1273
1332
|
*
|
|
1274
1333
|
* Takes a bare catalog id, as `getMethod` does. Throws `ApiResponseError`: `404`
|
|
1275
|
-
* `pipelex.catalog.not_found` for an unknown method
|
|
1276
|
-
* `
|
|
1334
|
+
* `pipelex.catalog.not_found` for an unknown method and for another organization's internal
|
|
1335
|
+
* one, `409` `pipelex.catalog.being_deleted` while the erasure of the caller's own method runs,
|
|
1336
|
+
* and `422` `request.invalid` for a cursor the listing did not issue for this method or a `limit`
|
|
1277
1337
|
* outside 1 to 100. A page whose `items` is not an array or whose `next_cursor` is neither a
|
|
1278
1338
|
* string nor `null` is an answer the SDK cannot read, thrown as an `ApiResponseError` too.
|
|
1279
1339
|
*/
|
|
@@ -1281,12 +1341,15 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
1281
1341
|
/**
|
|
1282
1342
|
* One published version of a method, with its files — `GET /v1/methods/{id}/versions/{n}`.
|
|
1283
1343
|
* `files` lists the version's tree as `getMethod` lists the draft's, each file with a link to
|
|
1284
|
-
* its bytes valid for about fifteen minutes, which `writeMethodTree` writes to disk.
|
|
1344
|
+
* its bytes valid for about fifteen minutes, which `writeMethodTree` writes to disk. Another
|
|
1345
|
+
* organization's public method reads its versions too, without their `published_by`, the files
|
|
1346
|
+
* linked in the organization that holds it.
|
|
1285
1347
|
*
|
|
1286
1348
|
* Takes a bare catalog id, as `getMethod` does, and the version number. Throws
|
|
1287
1349
|
* `RequestArgumentError` for a `version` that is not a positive integer, before any request.
|
|
1288
1350
|
* Throws `ApiResponseError`: `404` `pipelex.catalog.not_found` for a version the method never
|
|
1289
|
-
* published
|
|
1351
|
+
* published, for an unknown method and for another organization's internal one, `409`
|
|
1352
|
+
* `pipelex.catalog.being_deleted` while the erasure of the caller's own method runs, and `500`
|
|
1290
1353
|
* `runtime.unexpected` for a version stored before methods were trees and never rewritten as
|
|
1291
1354
|
* one, which names no tree to serve.
|
|
1292
1355
|
*/
|
|
@@ -1303,10 +1366,16 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
1303
1366
|
* original stays as it was. The copy takes the draft, never the latest published version; with
|
|
1304
1367
|
* `expected_draft_updated_at`, it takes exactly the draft the caller last saw.
|
|
1305
1368
|
*
|
|
1369
|
+
* **Another organization's public method copies too**, a template of the catalog among them,
|
|
1370
|
+
* from its latest published version rather than its draft: that version's tree, description and
|
|
1371
|
+
* categories, and the method's examples, never the owner's form inputs, with `copied_from`
|
|
1372
|
+
* naming the `version`. Leave `expected_draft_updated_at` out then: sent with such a method, it
|
|
1373
|
+
* is refused with a `422` `request.invalid` naming it.
|
|
1374
|
+
*
|
|
1306
1375
|
* The answer is a `MethodCopyResult` discriminated on `outcome` — branch on it: `copied` with
|
|
1307
1376
|
* the copy as `method`, `examples_copied` and `stored_files_copied`; `refused` with the
|
|
1308
1377
|
* `reason` `bundle_names_other_storage`, a `message` and every `references` entry (`{ path, uri
|
|
1309
|
-
* }`) a `.mthds` file of the
|
|
1378
|
+
* }`) a `.mthds` file of the tree holds that the destination cannot read and, in a duplicate,
|
|
1310
1379
|
* that names a file under the original's run files, which erasing the original would remove,
|
|
1311
1380
|
* nothing written.
|
|
1312
1381
|
*
|
|
@@ -1325,7 +1394,8 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
1325
1394
|
* that is not from a web session, or for a role that may not change methods, in the method's
|
|
1326
1395
|
* organization or in the destination; `404` `pipelex.org.not_found` for a destination the
|
|
1327
1396
|
* caller does not belong to, still being set up or unknown, alike; `404`
|
|
1328
|
-
* `pipelex.catalog.not_found` for an unknown method
|
|
1397
|
+
* `pipelex.catalog.not_found` for an unknown method and for another organization's internal
|
|
1398
|
+
* one; `409` `pipelex.catalog.being_deleted` while the erasure of the caller's own method runs;
|
|
1329
1399
|
* `409` `pipelex.catalog.update_conflict` for a draft that moved since
|
|
1330
1400
|
* `expected_draft_updated_at`; a `409` whose `domain` is `runtime` and `retry` `no`, for a
|
|
1331
1401
|
* draft whose tree the method's organization holds no record of or no longer holds every file
|
package/dist/client.js
CHANGED
|
@@ -1935,7 +1935,84 @@ export class PipelexApiClient {
|
|
|
1935
1935
|
yield* followCursor("listMethods", (cursor) => this.listMethods({ ...query, cursor }));
|
|
1936
1936
|
}
|
|
1937
1937
|
/**
|
|
1938
|
-
*
|
|
1938
|
+
* One page of the template catalog — `GET /v1/method-templates`: the public methods of the
|
|
1939
|
+
* organization the deployment names as its templates organization, the same for every caller.
|
|
1940
|
+
*
|
|
1941
|
+
* The templates the ordering lists lead, in its order, and every other template follows by name.
|
|
1942
|
+
* `ordering` names the ordering to serve, such as `default` or an arm of a test; an absent,
|
|
1943
|
+
* unknown or malformed name serves `default`, and the page's `ordering` names the one served,
|
|
1944
|
+
* `null` when there is none. `category` keeps one category's templates, by slug, in the same
|
|
1945
|
+
* order; an unknown slug keeps none. Whatever the filter, the page's `categories` names, with its
|
|
1946
|
+
* label, every category at least one template shows, in the catalog's order, so no tab drawn
|
|
1947
|
+
* from it is ever empty.
|
|
1948
|
+
*
|
|
1949
|
+
* Each `MethodTemplate` carries the method's id and name, its latest published version's number,
|
|
1950
|
+
* description and categories, its organization's display name and each thumbnail with its size
|
|
1951
|
+
* and a link to its bytes valid for about fifteen minutes. A template reads, runs and copies by
|
|
1952
|
+
* its `method_id`, as another organization's public method does (`getMethod`, `copyMethod`).
|
|
1953
|
+
*
|
|
1954
|
+
* Pass the page's `nextCursor` back as `cursor` with the same `ordering` and `category`, which the
|
|
1955
|
+
* cursor does not hold; `null` is the last page. `iterateMethodTemplates` follows it for you.
|
|
1956
|
+
*
|
|
1957
|
+
* Throws `ApiResponseError`: `422` `request.invalid` for a `limit` outside 1 to 200 or a cursor of
|
|
1958
|
+
* another listing, `422` `request.malformed` for a cursor that does not decode, `500`
|
|
1959
|
+
* `runtime.misconfigured` when the templates organization the deployment names has no record or
|
|
1960
|
+
* a link cannot be signed, and `503` `runtime.unavailable`, safe to retry. A page whose `items`
|
|
1961
|
+
* or `categories` is not an array, whose `next_cursor` or `ordering` is neither a string nor
|
|
1962
|
+
* `null`, a template lacking a member a card is drawn from (a string `method_id`, `name` and
|
|
1963
|
+
* `organization_name`, a `description` that is a string or `null`, a positive integer
|
|
1964
|
+
* `latest_version`, an array of string `categories`, and `thumbnails` each with a string `path`,
|
|
1965
|
+
* `media_type`, `sha256`, `url` and `expires_at` and a positive integer `width` and `height`), and a
|
|
1966
|
+
* category without a string `slug` and `label` are an answer the SDK cannot read, thrown as an
|
|
1967
|
+
* `ApiResponseError` too, its links redacted.
|
|
1968
|
+
*/
|
|
1969
|
+
async listMethodTemplates(query = {}) {
|
|
1970
|
+
const search = productQuery({
|
|
1971
|
+
ordering: query.ordering,
|
|
1972
|
+
category: query.category,
|
|
1973
|
+
limit: query.limit,
|
|
1974
|
+
cursor: query.cursor,
|
|
1975
|
+
});
|
|
1976
|
+
const endpoint = `method-templates${search}`;
|
|
1977
|
+
const res = withLinks(await this.requestProductAnswer("GET", endpoint, undefined));
|
|
1978
|
+
const page = this.readObjectAnswer("GET", endpoint, res);
|
|
1979
|
+
const { items, next_cursor: nextCursor, ordering, categories } = page;
|
|
1980
|
+
let fault = null;
|
|
1981
|
+
if (!Array.isArray(items))
|
|
1982
|
+
fault = "`items` is not an array";
|
|
1983
|
+
else if (nextCursor !== null && typeof nextCursor !== "string") {
|
|
1984
|
+
fault = "`next_cursor` is neither a string nor null";
|
|
1985
|
+
}
|
|
1986
|
+
else if (ordering !== null && typeof ordering !== "string") {
|
|
1987
|
+
fault = "`ordering` is neither a string nor null";
|
|
1988
|
+
}
|
|
1989
|
+
else if (!Array.isArray(categories))
|
|
1990
|
+
fault = "`categories` is not an array";
|
|
1991
|
+
else
|
|
1992
|
+
fault = methodTemplatesFault(items) ?? methodCategoriesFault(categories);
|
|
1993
|
+
if (fault !== null) {
|
|
1994
|
+
throw this.unreadableAnswer("GET", `/${API_PREFIX}/${endpoint}`, res, `a page of templates whose ${fault}`);
|
|
1995
|
+
}
|
|
1996
|
+
return {
|
|
1997
|
+
items: items,
|
|
1998
|
+
nextCursor: nextCursor,
|
|
1999
|
+
ordering: ordering,
|
|
2000
|
+
categories: categories,
|
|
2001
|
+
};
|
|
2002
|
+
}
|
|
2003
|
+
/**
|
|
2004
|
+
* Every template of the catalog, in the order served — follows `listMethodTemplates`'s cursor
|
|
2005
|
+
* for you, with the same `ordering` and `category` on every page. It stops on a `null` cursor or
|
|
2006
|
+
* on a cursor the server failed to advance, and throws `PagingNotTerminatingError` past a page
|
|
2007
|
+
* ceiling no real catalog reaches. The ordering served and the categories are on each page, so
|
|
2008
|
+
* read one page with `listMethodTemplates` when you need them.
|
|
2009
|
+
*/
|
|
2010
|
+
async *iterateMethodTemplates(query = {}) {
|
|
2011
|
+
yield* followCursor("listMethodTemplates", (cursor) => this.listMethodTemplates({ ...query, cursor }));
|
|
2012
|
+
}
|
|
2013
|
+
/**
|
|
2014
|
+
* Read one method with its files — `GET /v1/methods/{id}`. One of the caller's organization's
|
|
2015
|
+
* methods answers a `MethodDetail`: its identity, its draft
|
|
1939
2016
|
* (`files`, `input_data`) with the draft's token (`updated_at`) and digest (`draft_digest`),
|
|
1940
2017
|
* and its latest published version's summary (`latest_version`, `latest_published`), whatever
|
|
1941
2018
|
* the publish state. A method never published reads with both `null`, never as an error.
|
|
@@ -1943,6 +2020,11 @@ export class PipelexApiClient {
|
|
|
1943
2020
|
* link to its bytes valid for about fifteen minutes, and is empty for an empty draft;
|
|
1944
2021
|
* `writeMethodTree` brings the tree to disk.
|
|
1945
2022
|
*
|
|
2023
|
+
* **Another organization's public method reads too, in a narrower shape**, a
|
|
2024
|
+
* `PublicMethodDetail`: its latest published version, that version's description, categories
|
|
2025
|
+
* and files, and nothing of its draft or of the owner's people, so no `updated_at` to write or
|
|
2026
|
+
* publish with. Narrow the answer with `"updated_at" in method` before reading the draft.
|
|
2027
|
+
*
|
|
1946
2028
|
* Takes a bare catalog id: the method routes address the method itself, never a version of
|
|
1947
2029
|
* it. A caller holding `mt_…@3` strips the suffix with `parseMethodSelector` and reads that
|
|
1948
2030
|
* version with `getMethodVersion`. Every method route throws `RequestArgumentError` for a
|
|
@@ -1950,16 +2032,26 @@ export class PipelexApiClient {
|
|
|
1950
2032
|
* and for an id that is empty, `.` or `..`, which the URL would resolve into another route's
|
|
1951
2033
|
* path.
|
|
1952
2034
|
*
|
|
1953
|
-
* Throws `ApiResponseError`: `404` `pipelex.catalog.not_found` for an unknown
|
|
1954
|
-
*
|
|
1955
|
-
* `
|
|
1956
|
-
*
|
|
1957
|
-
* `
|
|
2035
|
+
* Throws `ApiResponseError`: `404` `pipelex.catalog.not_found` for an unknown method, for
|
|
2036
|
+
* another organization's internal one, and for a public one made internal again or being
|
|
2037
|
+
* erased; `409` `pipelex.catalog.being_deleted` while the erasure of the caller's own method
|
|
2038
|
+
* runs; and a `500` `runtime.unexpected` for a method whose tree the platform holds no record
|
|
2039
|
+
* of. An answer whose `files` is not a listing is an answer the SDK cannot read, thrown as an
|
|
2040
|
+
* `ApiResponseError` too, its links redacted, and so is one that lacks `updated_at` while it
|
|
2041
|
+
* carries a member only the owner's read has (`draft_digest`, `created_by_user_id`,
|
|
2042
|
+
* `input_data`, `pipe_output`, `copied_from`, `deletion_state`), which would otherwise pass for
|
|
2043
|
+
* another organization's public method, and a public read without an integer `latest_version`
|
|
2044
|
+
* and a `latest_published` object.
|
|
1958
2045
|
*/
|
|
1959
2046
|
async getMethod(methodId) {
|
|
1960
2047
|
const endpoint = methodPath(methodId, "getMethod");
|
|
1961
|
-
const res = await this.requestProductAnswer("GET", endpoint, undefined);
|
|
1962
|
-
|
|
2048
|
+
const res = withLinks(await this.requestProductAnswer("GET", endpoint, undefined));
|
|
2049
|
+
const method = this.readSavedTree("GET", endpoint, res, "a stored method");
|
|
2050
|
+
const fault = methodReadFault(method);
|
|
2051
|
+
if (fault !== null) {
|
|
2052
|
+
throw this.unreadableAnswer("GET", `/${API_PREFIX}/${endpoint}`, res, `a stored method ${fault}`);
|
|
2053
|
+
}
|
|
2054
|
+
return method;
|
|
1963
2055
|
}
|
|
1964
2056
|
/**
|
|
1965
2057
|
* Create a method — `POST /v1/methods`. The new method holds its tree as its draft and has no
|
|
@@ -2399,9 +2491,12 @@ export class PipelexApiClient {
|
|
|
2399
2491
|
*
|
|
2400
2492
|
* The answer is a `MethodPublishResult` discriminated on `outcome` — branch on it:
|
|
2401
2493
|
* `published` with the new `version`; `unchanged` with the existing latest `version`;
|
|
2402
|
-
* `refused` with a `reason
|
|
2403
|
-
*
|
|
2404
|
-
*
|
|
2494
|
+
* `refused` with a `reason`, a `message` and the runner's `validation` verdict, whose problem
|
|
2495
|
+
* items are read member by member as `validate` reads them. The reason is `invalid`, or
|
|
2496
|
+
* `not_runnable` for a draft that validates with pending signatures, and for a public method
|
|
2497
|
+
* also `thumbnail_missing`, `thumbnail_invalid` or `storage_reference` (see
|
|
2498
|
+
* `MethodPublishRefusalReason`); a reason this SDK does not know is passed through as its raw
|
|
2499
|
+
* string, still a verdict, so show its `message`. Every arm carries the `method`. A publish
|
|
2405
2500
|
* moves no token.
|
|
2406
2501
|
*
|
|
2407
2502
|
* Takes a bare catalog id, as `getMethod` does. Throws `RequestArgumentError` when
|
|
@@ -2416,11 +2511,14 @@ export class PipelexApiClient {
|
|
|
2416
2511
|
* cannot be reached, answers unusably or does not finish within the platform's deadline is a
|
|
2417
2512
|
* `502` or a `503` with nothing written, so a retry is safe; a runner that refuses the request
|
|
2418
2513
|
* itself is relayed under its own status. A retry of a publish that landed while its answer was
|
|
2419
|
-
* lost, as on a client timeout, answers `unchanged` with the version it wrote.
|
|
2514
|
+
* lost, as on a client timeout, answers `unchanged` with the version it wrote. A method made
|
|
2515
|
+
* public while its publish ran is a `503` with nothing written, safe to retry, which a retry
|
|
2516
|
+
* judges as a public method's publish.
|
|
2420
2517
|
*
|
|
2421
2518
|
* An answer whose `outcome` is not one of the outcomes above, that carries no `method` object,
|
|
2422
|
-
* or a refusal whose `
|
|
2423
|
-
* the SDK cannot read, thrown as an
|
|
2519
|
+
* or a refusal whose `reason` or `message` is not a string or whose `validation` is not a verdict
|
|
2520
|
+
* object with a boolean `is_valid`, is an answer the SDK cannot read, thrown as an
|
|
2521
|
+
* `ApiResponseError` too.
|
|
2424
2522
|
*/
|
|
2425
2523
|
async publishMethod(methodId, input) {
|
|
2426
2524
|
const token = input
|
|
@@ -2445,8 +2543,16 @@ export class PipelexApiClient {
|
|
|
2445
2543
|
// The refused arm carries the runner's verdict, whose problem items are read member by
|
|
2446
2544
|
// member as `validate` reads them, so one malformed item never costs the caller the others.
|
|
2447
2545
|
const validation = answer.validation;
|
|
2448
|
-
|
|
2449
|
-
|
|
2546
|
+
let fault = null;
|
|
2547
|
+
if (typeof answer.reason !== "string")
|
|
2548
|
+
fault = "`reason` is not a string";
|
|
2549
|
+
else if (typeof answer.message !== "string")
|
|
2550
|
+
fault = "`message` is not a string";
|
|
2551
|
+
else if (!isPlainObject(validation) || typeof validation.is_valid !== "boolean") {
|
|
2552
|
+
fault = "`validation` is not an object";
|
|
2553
|
+
}
|
|
2554
|
+
if (fault !== null) {
|
|
2555
|
+
throw this.unreadableAnswer("POST", `/${API_PREFIX}/${endpoint}`, res, `a publish result whose ${fault}`);
|
|
2450
2556
|
}
|
|
2451
2557
|
return {
|
|
2452
2558
|
...answer,
|
|
@@ -2461,11 +2567,13 @@ export class PipelexApiClient {
|
|
|
2461
2567
|
*
|
|
2462
2568
|
* Pass the page's `nextCursor` back as `cursor` to continue; `null` is the last page. A page
|
|
2463
2569
|
* may be short while `nextCursor` is set, because the platform reads versions whole. A method
|
|
2464
|
-
* never published answers an empty page.
|
|
2570
|
+
* never published answers an empty page. Another organization's public method lists its
|
|
2571
|
+
* versions too, each without its `published_by`.
|
|
2465
2572
|
*
|
|
2466
2573
|
* Takes a bare catalog id, as `getMethod` does. Throws `ApiResponseError`: `404`
|
|
2467
|
-
* `pipelex.catalog.not_found` for an unknown method
|
|
2468
|
-
* `
|
|
2574
|
+
* `pipelex.catalog.not_found` for an unknown method and for another organization's internal
|
|
2575
|
+
* one, `409` `pipelex.catalog.being_deleted` while the erasure of the caller's own method runs,
|
|
2576
|
+
* and `422` `request.invalid` for a cursor the listing did not issue for this method or a `limit`
|
|
2469
2577
|
* outside 1 to 100. A page whose `items` is not an array or whose `next_cursor` is neither a
|
|
2470
2578
|
* string nor `null` is an answer the SDK cannot read, thrown as an `ApiResponseError` too.
|
|
2471
2579
|
*/
|
|
@@ -2477,12 +2585,15 @@ export class PipelexApiClient {
|
|
|
2477
2585
|
/**
|
|
2478
2586
|
* One published version of a method, with its files — `GET /v1/methods/{id}/versions/{n}`.
|
|
2479
2587
|
* `files` lists the version's tree as `getMethod` lists the draft's, each file with a link to
|
|
2480
|
-
* its bytes valid for about fifteen minutes, which `writeMethodTree` writes to disk.
|
|
2588
|
+
* its bytes valid for about fifteen minutes, which `writeMethodTree` writes to disk. Another
|
|
2589
|
+
* organization's public method reads its versions too, without their `published_by`, the files
|
|
2590
|
+
* linked in the organization that holds it.
|
|
2481
2591
|
*
|
|
2482
2592
|
* Takes a bare catalog id, as `getMethod` does, and the version number. Throws
|
|
2483
2593
|
* `RequestArgumentError` for a `version` that is not a positive integer, before any request.
|
|
2484
2594
|
* Throws `ApiResponseError`: `404` `pipelex.catalog.not_found` for a version the method never
|
|
2485
|
-
* published
|
|
2595
|
+
* published, for an unknown method and for another organization's internal one, `409`
|
|
2596
|
+
* `pipelex.catalog.being_deleted` while the erasure of the caller's own method runs, and `500`
|
|
2486
2597
|
* `runtime.unexpected` for a version stored before methods were trees and never rewritten as
|
|
2487
2598
|
* one, which names no tree to serve.
|
|
2488
2599
|
*/
|
|
@@ -2506,10 +2617,16 @@ export class PipelexApiClient {
|
|
|
2506
2617
|
* original stays as it was. The copy takes the draft, never the latest published version; with
|
|
2507
2618
|
* `expected_draft_updated_at`, it takes exactly the draft the caller last saw.
|
|
2508
2619
|
*
|
|
2620
|
+
* **Another organization's public method copies too**, a template of the catalog among them,
|
|
2621
|
+
* from its latest published version rather than its draft: that version's tree, description and
|
|
2622
|
+
* categories, and the method's examples, never the owner's form inputs, with `copied_from`
|
|
2623
|
+
* naming the `version`. Leave `expected_draft_updated_at` out then: sent with such a method, it
|
|
2624
|
+
* is refused with a `422` `request.invalid` naming it.
|
|
2625
|
+
*
|
|
2509
2626
|
* The answer is a `MethodCopyResult` discriminated on `outcome` — branch on it: `copied` with
|
|
2510
2627
|
* the copy as `method`, `examples_copied` and `stored_files_copied`; `refused` with the
|
|
2511
2628
|
* `reason` `bundle_names_other_storage`, a `message` and every `references` entry (`{ path, uri
|
|
2512
|
-
* }`) a `.mthds` file of the
|
|
2629
|
+
* }`) a `.mthds` file of the tree holds that the destination cannot read and, in a duplicate,
|
|
2513
2630
|
* that names a file under the original's run files, which erasing the original would remove,
|
|
2514
2631
|
* nothing written.
|
|
2515
2632
|
*
|
|
@@ -2528,7 +2645,8 @@ export class PipelexApiClient {
|
|
|
2528
2645
|
* that is not from a web session, or for a role that may not change methods, in the method's
|
|
2529
2646
|
* organization or in the destination; `404` `pipelex.org.not_found` for a destination the
|
|
2530
2647
|
* caller does not belong to, still being set up or unknown, alike; `404`
|
|
2531
|
-
* `pipelex.catalog.not_found` for an unknown method
|
|
2648
|
+
* `pipelex.catalog.not_found` for an unknown method and for another organization's internal
|
|
2649
|
+
* one; `409` `pipelex.catalog.being_deleted` while the erasure of the caller's own method runs;
|
|
2532
2650
|
* `409` `pipelex.catalog.update_conflict` for a draft that moved since
|
|
2533
2651
|
* `expected_draft_updated_at`; a `409` whose `domain` is `runtime` and `retry` `no`, for a
|
|
2534
2652
|
* draft whose tree the method's organization holds no record of or no longer holds every file
|
|
@@ -4387,6 +4505,106 @@ function savedFilesFault(files) {
|
|
|
4387
4505
|
}
|
|
4388
4506
|
return null;
|
|
4389
4507
|
}
|
|
4508
|
+
/** The members only the owner's read of a method carries, never another organization's. */
|
|
4509
|
+
const OWNER_ONLY_METHOD_MEMBERS = [
|
|
4510
|
+
"draft_digest",
|
|
4511
|
+
"created_by_user_id",
|
|
4512
|
+
"input_data",
|
|
4513
|
+
"pipe_output",
|
|
4514
|
+
"copied_from",
|
|
4515
|
+
"deletion_state",
|
|
4516
|
+
];
|
|
4517
|
+
/**
|
|
4518
|
+
* Why a method read is not one the SDK reads, or `null` when it is. The owner's read carries the
|
|
4519
|
+
* draft's token, `updated_at`, which is what tells it from another organization's public method;
|
|
4520
|
+
* so an answer without the token that still carries a member only the owner's read has is a
|
|
4521
|
+
* damaged owner read rather than a public one, and a public read must carry the latest published
|
|
4522
|
+
* version it is about.
|
|
4523
|
+
*/
|
|
4524
|
+
function methodReadFault(method) {
|
|
4525
|
+
if ("updated_at" in method)
|
|
4526
|
+
return null;
|
|
4527
|
+
const ownerMember = OWNER_ONLY_METHOD_MEMBERS.find((member) => member in method);
|
|
4528
|
+
if (ownerMember !== undefined) {
|
|
4529
|
+
return `without \`updated_at\` that carries the owner's \`${ownerMember}\``;
|
|
4530
|
+
}
|
|
4531
|
+
if (!isPositiveInteger(method.latest_version)) {
|
|
4532
|
+
return "read as another organization's public method whose `latest_version` is not a positive integer";
|
|
4533
|
+
}
|
|
4534
|
+
if (!isPlainObject(method.latest_published)) {
|
|
4535
|
+
return "read as another organization's public method whose `latest_published` is not an object";
|
|
4536
|
+
}
|
|
4537
|
+
return null;
|
|
4538
|
+
}
|
|
4539
|
+
function isPositiveInteger(value) {
|
|
4540
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 1;
|
|
4541
|
+
}
|
|
4542
|
+
function isStringArray(value) {
|
|
4543
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
4544
|
+
}
|
|
4545
|
+
/**
|
|
4546
|
+
* Why the catalog's templates are not ones the SDK reads, or `null` when they are: each an object
|
|
4547
|
+
* with what a card is drawn from, a string `method_id`, `name` and `organization_name`, a
|
|
4548
|
+
* `description` that is a string or `null`, a positive integer `latest_version`, an array of string `categories`, and `thumbnails`, each with
|
|
4549
|
+
* a string `path`, `media_type`, `sha256`, `url` and `expires_at` and a positive integer `width`
|
|
4550
|
+
* and `height`.
|
|
4551
|
+
*/
|
|
4552
|
+
function methodTemplatesFault(items) {
|
|
4553
|
+
for (let index = 0; index < items.length; index += 1) {
|
|
4554
|
+
const item = items[index];
|
|
4555
|
+
if (!isPlainObject(item))
|
|
4556
|
+
return `\`items\` holds an entry that is not an object, at [${index}]`;
|
|
4557
|
+
for (const member of ["method_id", "name", "organization_name"]) {
|
|
4558
|
+
if (typeof item[member] !== "string") {
|
|
4559
|
+
return `\`items\` holds a template whose \`${member}\` is not a string, at [${index}]`;
|
|
4560
|
+
}
|
|
4561
|
+
}
|
|
4562
|
+
if (item.description !== null &&
|
|
4563
|
+
item.description !== undefined &&
|
|
4564
|
+
typeof item.description !== "string") {
|
|
4565
|
+
return `\`items\` holds a template whose \`description\` is neither a string nor null, at [${index}]`;
|
|
4566
|
+
}
|
|
4567
|
+
if (!isPositiveInteger(item.latest_version)) {
|
|
4568
|
+
return `\`items\` holds a template whose \`latest_version\` is not a positive integer, at [${index}]`;
|
|
4569
|
+
}
|
|
4570
|
+
if (!isStringArray(item.categories)) {
|
|
4571
|
+
return `\`items\` holds a template whose \`categories\` is not an array of strings, at [${index}]`;
|
|
4572
|
+
}
|
|
4573
|
+
const thumbnails = item.thumbnails;
|
|
4574
|
+
if (!Array.isArray(thumbnails)) {
|
|
4575
|
+
return `\`items\` holds a template whose \`thumbnails\` is not an array, at [${index}]`;
|
|
4576
|
+
}
|
|
4577
|
+
for (let position = 0; position < thumbnails.length; position += 1) {
|
|
4578
|
+
const thumbnail = thumbnails[position];
|
|
4579
|
+
const where = `at [${index}].thumbnails[${position}]`;
|
|
4580
|
+
if (!isPlainObject(thumbnail))
|
|
4581
|
+
return `\`items\` holds a thumbnail that is not an object, ${where}`;
|
|
4582
|
+
for (const member of ["path", "media_type", "sha256", "url", "expires_at"]) {
|
|
4583
|
+
if (typeof thumbnail[member] !== "string") {
|
|
4584
|
+
return `\`items\` holds a thumbnail whose \`${member}\` is not a string, ${where}`;
|
|
4585
|
+
}
|
|
4586
|
+
}
|
|
4587
|
+
for (const member of ["width", "height"]) {
|
|
4588
|
+
if (!isPositiveInteger(thumbnail[member])) {
|
|
4589
|
+
return `\`items\` holds a thumbnail whose \`${member}\` is not a positive integer, ${where}`;
|
|
4590
|
+
}
|
|
4591
|
+
}
|
|
4592
|
+
}
|
|
4593
|
+
}
|
|
4594
|
+
return null;
|
|
4595
|
+
}
|
|
4596
|
+
/** Why the catalog's categories are not ones the SDK reads, or `null`: each a string `slug` and `label`. */
|
|
4597
|
+
function methodCategoriesFault(categories) {
|
|
4598
|
+
for (let index = 0; index < categories.length; index += 1) {
|
|
4599
|
+
const category = categories[index];
|
|
4600
|
+
if (!isPlainObject(category) ||
|
|
4601
|
+
typeof category.slug !== "string" ||
|
|
4602
|
+
typeof category.label !== "string") {
|
|
4603
|
+
return `\`categories\` holds an entry without a string \`slug\` and \`label\`, at [${index}]`;
|
|
4604
|
+
}
|
|
4605
|
+
}
|
|
4606
|
+
return null;
|
|
4607
|
+
}
|
|
4390
4608
|
function extractNetworkErrorCode(err) {
|
|
4391
4609
|
if (err instanceof DOMException && err.name === "TimeoutError") {
|
|
4392
4610
|
return "ABORT_TIMEOUT";
|