@pipelex/sdk 0.62.0 → 0.64.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 +28 -3
- package/dist/cli/script.d.ts +1 -1
- package/dist/client.d.ts +118 -20
- package/dist/client.js +189 -23
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js.map +1 -1
- package/dist/method-layouts.d.ts +164 -0
- package/dist/method-layouts.js +115 -0
- package/dist/method-layouts.js.map +1 -0
- package/dist/models.d.ts +27 -0
- package/dist/product-models.d.ts +29 -9
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/architecture.md +4 -2
- package/docs/errors.md +2 -0
- package/docs/method-layouts.md +133 -0
- package/docs/public-methods.md +17 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -178,10 +178,34 @@ 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
|
+
// Store a layout a producer designed for one pipe of a published version, over that version's
|
|
182
|
+
// tree; a write of the same tree, pipe, catalog and prompt hash replaces it. A validate by
|
|
183
|
+
// method_id naming `layouts` then carries the layouts beside the descriptor, newest first, and
|
|
184
|
+
// leaves `layouts` out when there are none: render the plain form then.
|
|
185
|
+
const v1 = await client.getMethodVersion(created.method_id, 1);
|
|
186
|
+
const layoutKey = crypto.randomUUID(); // one key per layout, sent again unchanged on each retry
|
|
187
|
+
await client.storeMethodLayout(created.method_id, 1, {
|
|
188
|
+
tree_digest: v1.tree_digest!,
|
|
189
|
+
pipe_ref: "greeting.greet",
|
|
190
|
+
catalog: "composer",
|
|
191
|
+
prompt_hash: "a1b2c3d4e5f6",
|
|
192
|
+
jsonl: '{"op":"add","path":"/root","value":"page"}',
|
|
193
|
+
provenance: { producer: "pipelex-method", model: "claude-4.8-opus", style: "editorial" },
|
|
194
|
+
}, { idempotencyKey: layoutKey });
|
|
195
|
+
const laidOut = await client.validate({ method_id: `${created.method_id}@1` }, false, {
|
|
196
|
+
views: ["input_form", "layouts"],
|
|
197
|
+
});
|
|
198
|
+
if (laidOut.is_valid) console.log(laidOut.layouts?.map((layout) => layout.catalog) ?? "plain form");
|
|
199
|
+
|
|
181
200
|
// 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.
|
|
201
|
+
// the categories that have a template and each card's thumbnails behind short-lived links. A
|
|
202
|
+
// category's `labels` name it by language tag and may lack a language, in which case a client
|
|
203
|
+
// shows `label`, the English one.
|
|
183
204
|
const templates = await client.listMethodTemplates({ category: "human-resources", limit: 8 });
|
|
184
|
-
for (const tab of templates.categories) console.log(tab.slug, tab.label);
|
|
205
|
+
for (const tab of templates.categories) console.log(tab.slug, tab.labels.fr ?? tab.label);
|
|
206
|
+
// The catalog's whole list of categories, a category no template shows included.
|
|
207
|
+
const { categories } = await client.listMethodCategories();
|
|
208
|
+
console.log(categories.map((category) => category.slug));
|
|
185
209
|
// Another organization's public method reads in a narrower shape, its latest published version
|
|
186
210
|
// and no draft: `updated_at` is what tells it from one of your own.
|
|
187
211
|
const first = templates.items[0];
|
|
@@ -297,7 +321,8 @@ These pages ship inside the published package, so a reader who has only installe
|
|
|
297
321
|
| [`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 |
|
|
298
322
|
| [`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 |
|
|
299
323
|
| [`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/
|
|
324
|
+
| [`docs/method-layouts.md`](./docs/method-layouts.md) | A method's stored layouts: `storeMethodLayout`, which stores a layout designed for one pipe of a published version, its closed body and its refusals, the layout and its provenance, and the `layouts` a validate by `method_id` carries when `views` names `layouts`, absent when there are none |
|
|
325
|
+
| [`docs/public-methods.md`](./docs/public-methods.md) | Public methods and the template catalog: `listMethodTemplates` and its iterator, the ordering, the categories with their labels by language and `listMethodCategories`, 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 |
|
|
301
326
|
| [`docs/organization-branding.md`](./docs/organization-branding.md) | An organization's logos, colours, app style and typeface, and its white-label entitlement: `getBranding`, which every member and API key reads, `updateOrganizationBranding`, a write by the `admin` or the `member` role that sets, clears or keeps each key, and how a refused logo, colour, style or typeface names its field so a settings screen shows the sentence under it |
|
|
302
327
|
| [`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 |
|
|
303
328
|
| [`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.64.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,8 @@
|
|
|
1
1
|
import type { MTHDSProtocol, ModelCategory, ModelDeck, RunOptions, StartOptions, VersionInfo } from "mthds/protocol";
|
|
2
|
-
import type { CodegenRequest, CodegenResponse, CrateRouteOptions, DictPipeOutput, FormatResponse, LintResponse, ModelCheckCategory, ModelReferenceVerdict, PipeIORequest, PipeIOResponse, PipelexRunResultStart, PipelexValidateSource, PipelexValidationResult, ResolveRequest, ResolveResponse, ValidateMethodSelector } from "./models.js";
|
|
2
|
+
import type { CodegenRequest, CodegenResponse, CrateRouteOptions, DictPipeOutput, FormatResponse, LintResponse, ModelCheckCategory, ModelReferenceVerdict, PipeIORequest, PipeIOResponse, PipelexRunResultStart, PipelexValidateSource, PipelexValidationResult, ResolveRequest, ResolveResponse, ValidateMethodSelector, ValidateView } from "./models.js";
|
|
3
|
+
import type { MethodLayout, MethodLayoutInput } from "./method-layouts.js";
|
|
3
4
|
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, 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
|
+
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, MethodCategoryList, 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
6
|
import type { MethodTreeForms, MethodTreeLimits } from "./method-trees.js";
|
|
6
7
|
import type { AppInfo } from "./user-agent.js";
|
|
7
8
|
import type { WebhookDeliveryDetail, WebhookDeliveryList, WebhookReplayAccepted, WebhookReplayOptions, WebhookSecret, WebhookSecretRotateOptions } from "./webhooks.js";
|
|
@@ -173,6 +174,32 @@ export interface MethodCopyRequestOptions {
|
|
|
173
174
|
*/
|
|
174
175
|
idempotencyKey?: string | null;
|
|
175
176
|
}
|
|
177
|
+
/** Options for `storeMethodLayout`. */
|
|
178
|
+
export interface MethodLayoutWriteOptions {
|
|
179
|
+
/**
|
|
180
|
+
* The write's idempotency key, sent as the `Idempotency-Key` header of
|
|
181
|
+
* `POST /v1/methods/{id}/versions/{n}/layouts`, and not sent at all when absent or `null`.
|
|
182
|
+
* Generate one per layout the caller means to store (a `crypto.randomUUID()` is a good key), and
|
|
183
|
+
* keep it, with the arguments unchanged, across that write's retries.
|
|
184
|
+
*
|
|
185
|
+
* A write sent again without a key runs again: it stamps the layout with a new `stored_at`, which
|
|
186
|
+
* moves it ahead of every layout stored since in the newest-first order, and applies the bound
|
|
187
|
+
* again, so a layout another producer stored meanwhile, a newer prompt of the same pipe and
|
|
188
|
+
* catalog included, can be the one removed. Under the same key the hosted API answers the retry
|
|
189
|
+
* with the first write's stored answer and runs nothing, so a write whose answer was lost, as on
|
|
190
|
+
* a client timeout, can be sent again unchanged. The write's `200` and its `4xx` refusals are
|
|
191
|
+
* stored alike; a `5xx` and a refusal for its timing (a `408`, `425` or `429`) are not, so the
|
|
192
|
+
* same key sends the write afresh. The same key with a different body is refused (`409`
|
|
193
|
+
* `request.idempotency_conflict`, whose `retry` is `no`), as is a key whose earlier request is
|
|
194
|
+
* still being handled (`409` `request.idempotency_key_in_use`, whose `retry` is `yes`); these
|
|
195
|
+
* two are the key's own answers and are never stored, so a retry after the second runs as the
|
|
196
|
+
* key's first request would.
|
|
197
|
+
*
|
|
198
|
+
* A key is held to the rule a start's is (`PipelexStartRequestOptions.idempotencyKey`): 1 to
|
|
199
|
+
* 255 visible ASCII characters, anything else a `RequestArgumentError` before any request.
|
|
200
|
+
*/
|
|
201
|
+
idempotencyKey?: string | null;
|
|
202
|
+
}
|
|
176
203
|
/**
|
|
177
204
|
* `start()` / `startAndWaitForResult()` options — the same, for the durable path, plus the start
|
|
178
205
|
* request's own `idempotencyKey`.
|
|
@@ -191,8 +218,13 @@ export interface ValidateOptions {
|
|
|
191
218
|
* `"markdown"`, so both arms carry `rendered_markdown`.
|
|
192
219
|
*/
|
|
193
220
|
render?: string[];
|
|
194
|
-
/**
|
|
195
|
-
|
|
221
|
+
/**
|
|
222
|
+
* Structured-view opt-in tokens, e.g. `["input_form", "output_form"]`, or
|
|
223
|
+
* `["input_form", "layouts"]` for a stored method's descriptor and the layouts over it; sent
|
|
224
|
+
* verbatim, and only when given. A token this SDK does not know (`ValidateView`) is sent as its
|
|
225
|
+
* raw string.
|
|
226
|
+
*/
|
|
227
|
+
views?: (ValidateView | (string & {}))[];
|
|
196
228
|
/**
|
|
197
229
|
* Per-call request ceiling; defaults to the 20-min execute ceiling. A positive number
|
|
198
230
|
* no larger than 2147483647, the longest delay a timer honours, else a `RangeError`.
|
|
@@ -284,8 +316,8 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
284
316
|
* `url` is a fully resolved absolute URL. `mayStartRun` marks a request that may create a run,
|
|
285
317
|
* whose errors then say whether it may have (see `RunCreation`).
|
|
286
318
|
* `idempotencyKey`, already checked by `idempotencyKeyOf`, is sent as the
|
|
287
|
-
* `Idempotency-Key` header; only `start`, `copyMethod`, `
|
|
288
|
-
* `replayRunWebhookDelivery` pass one.
|
|
319
|
+
* `Idempotency-Key` header; only `start`, `copyMethod`, `storeMethodLayout`,
|
|
320
|
+
* `rotateWebhookSecret` and `replayRunWebhookDelivery` pass one.
|
|
289
321
|
*/
|
|
290
322
|
private requestRaw;
|
|
291
323
|
/**
|
|
@@ -565,7 +597,10 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
565
597
|
* `options.views` is the sibling opt-in for *structured* views where `render`
|
|
566
598
|
* carries *rendered text*. The two lists are independent, each resolving its own
|
|
567
599
|
* tokens against its own supported set, and each supported token adds a same-named
|
|
568
|
-
* top-level field to the valid arm — `input_form` and `output_form
|
|
600
|
+
* top-level field to the valid arm — `input_form` and `output_form`, and on a hosted
|
|
601
|
+
* validate by `method_id`, `layouts`, the layouts stored for the resolved tree
|
|
602
|
+
* (`PipelexValidationReport.layouts`, absent when there are none), each entry read as
|
|
603
|
+
* `storeMethodLayout`'s answer is and one that does not read as a layout left out. Unlike
|
|
569
604
|
* `render`, this client sends `views` ONLY when the caller asks: the point of an
|
|
570
605
|
* opt-in view is that the default response stays byte-identical, and the
|
|
571
606
|
* highest-frequency consumers (hook pipelines, CI gates, agent loops) never pay for
|
|
@@ -996,8 +1031,8 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
996
1031
|
* unknown or malformed name serves `default`, and the page's `ordering` names the one served,
|
|
997
1032
|
* `null` when there is none. `category` keeps one category's templates, by slug, in the same
|
|
998
1033
|
* order; an unknown slug keeps none. Whatever the filter, the page's `categories` names, with its
|
|
999
|
-
*
|
|
1000
|
-
* from it is ever empty.
|
|
1034
|
+
* labels, every category at least one template shows, in the catalog's order, so no tab drawn
|
|
1035
|
+
* from it is ever empty; `listMethodCategories` reads the whole list.
|
|
1001
1036
|
*
|
|
1002
1037
|
* Each `MethodTemplate` carries the method's id and name, its latest published version's number,
|
|
1003
1038
|
* description and categories, its organization's display name and each thumbnail with its size
|
|
@@ -1007,17 +1042,18 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
1007
1042
|
* Pass the page's `nextCursor` back as `cursor` with the same `ordering` and `category`, which the
|
|
1008
1043
|
* cursor does not hold; `null` is the last page. `iterateMethodTemplates` follows it for you.
|
|
1009
1044
|
*
|
|
1010
|
-
* Throws `ApiResponseError`: `
|
|
1011
|
-
*
|
|
1012
|
-
* `
|
|
1013
|
-
*
|
|
1014
|
-
*
|
|
1015
|
-
*
|
|
1016
|
-
*
|
|
1017
|
-
* `
|
|
1018
|
-
*
|
|
1019
|
-
*
|
|
1020
|
-
* `
|
|
1045
|
+
* Throws `ApiResponseError`: `403` `pipelex.org.required` without an active organization, `422`
|
|
1046
|
+
* `request.invalid` for a `limit` outside 1 to 200 or a cursor of another listing, `422`
|
|
1047
|
+
* `request.malformed` for a cursor that does not decode, `500` `runtime.misconfigured` when the
|
|
1048
|
+
* templates organization the deployment names has no record or a link cannot be signed, and
|
|
1049
|
+
* `503` `runtime.unavailable`, safe to retry. A page whose `items` or `categories` is not an
|
|
1050
|
+
* array, whose `next_cursor` or `ordering` is neither a string nor `null`, a template lacking a
|
|
1051
|
+
* member a card is drawn from (a string `method_id`, `name` and `organization_name`, a
|
|
1052
|
+
* `description` that is a string or `null`, a positive integer `latest_version`, an array of
|
|
1053
|
+
* string `categories`, and `thumbnails` each with a string `path`, `media_type`, `sha256`, `url`
|
|
1054
|
+
* and `expires_at` and a positive integer `width` and `height`), and a category without a string
|
|
1055
|
+
* `slug` and `label` or whose `labels` is not an object of strings are an answer the SDK cannot
|
|
1056
|
+
* read, thrown as an `ApiResponseError` too, its links redacted.
|
|
1021
1057
|
*/
|
|
1022
1058
|
listMethodTemplates(query?: ListMethodTemplatesQuery): Promise<MethodTemplatePage>;
|
|
1023
1059
|
/**
|
|
@@ -1028,6 +1064,23 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
1028
1064
|
* read one page with `listMethodTemplates` when you need them.
|
|
1029
1065
|
*/
|
|
1030
1066
|
iterateMethodTemplates(query?: Omit<ListMethodTemplatesQuery, "cursor">): AsyncGenerator<MethodTemplate, void, undefined>;
|
|
1067
|
+
/**
|
|
1068
|
+
* The catalog's whole list of categories — `GET /v1/method-categories`: every category an admin
|
|
1069
|
+
* of the platform put on it, in the catalog's order, whether or not a template shows it, the same
|
|
1070
|
+
* for every caller. Each `MethodCategory` carries its `slug`, the one a manifest declares, its
|
|
1071
|
+
* English `label`, and its `labels` by language tag, which may lack a language the platform
|
|
1072
|
+
* added after the category was stored; show `labels[locale] ?? label`.
|
|
1073
|
+
*
|
|
1074
|
+
* A template shows under the categories its manifest declares that are on this list, so this is
|
|
1075
|
+
* the list a method's categories are picked from. `listMethodTemplates` names, on every page, only
|
|
1076
|
+
* the categories some template shows, for the tabs a client draws.
|
|
1077
|
+
*
|
|
1078
|
+
* Throws `ApiResponseError`: `403` `pipelex.org.required` without an active organization, and
|
|
1079
|
+
* `503` `runtime.unavailable`, safe to retry. An answer whose `categories` is not an array, or
|
|
1080
|
+
* holds a category without a string `slug` and `label` or whose `labels` is not an object of
|
|
1081
|
+
* strings, is an answer the SDK cannot read, thrown as an `ApiResponseError` too.
|
|
1082
|
+
*/
|
|
1083
|
+
listMethodCategories(): Promise<MethodCategoryList>;
|
|
1031
1084
|
/**
|
|
1032
1085
|
* Read one method with its files — `GET /v1/methods/{id}`. One of the caller's organization's
|
|
1033
1086
|
* methods answers a `MethodDetail`: its identity, its draft
|
|
@@ -1354,6 +1407,51 @@ export declare class PipelexApiClient implements HostedProtocolSurface {
|
|
|
1354
1407
|
* one, which names no tree to serve.
|
|
1355
1408
|
*/
|
|
1356
1409
|
getMethodVersion(methodId: string, version: number): Promise<MethodVersion>;
|
|
1410
|
+
/**
|
|
1411
|
+
* Store a layout designed for one pipe of a published version of a method —
|
|
1412
|
+
* `POST /v1/methods/{id}/versions/{n}/layouts` — and answer it as stored, with the `stored_at` the
|
|
1413
|
+
* platform stamped.
|
|
1414
|
+
*
|
|
1415
|
+
* A layout is a data file a producer designed over the version's input-form descriptor, in the
|
|
1416
|
+
* vocabulary of a catalog of components. The platform stores it beside the method, keyed by the
|
|
1417
|
+
* content it was designed for, and hands it back on every validate of the method by `method_id`
|
|
1418
|
+
* that names `layouts` among its `views` and resolves the same tree
|
|
1419
|
+
* (`PipelexValidationReport.layouts`). It never produces, parses or judges one.
|
|
1420
|
+
*
|
|
1421
|
+
* `layout.tree_digest` must be the digest of the tree `version` holds, its
|
|
1422
|
+
* `MethodVersionSummary.tree_digest`: a producer designs over the descriptor of the version it was
|
|
1423
|
+
* handed. **A write of the same identity replaces the layout**, the identity being `tree_digest`,
|
|
1424
|
+
* `pipe_ref`, `catalog` and `prompt_hash`, and is answered the same `200` whether it stored a new
|
|
1425
|
+
* layout or replaced one. `options.idempotencyKey` is sent as the `Idempotency-Key` header, so a
|
|
1426
|
+
* write whose answer was lost is sent again under the same key and answered as the first was; sent
|
|
1427
|
+
* again without one, it runs again, with a new `stored_at` and the bound applied again
|
|
1428
|
+
* (`MethodLayoutWriteOptions`). A content keeps at most 16
|
|
1429
|
+
* layouts, every pipe, catalog and prompt hash together; past the bound a write removes first what
|
|
1430
|
+
* newer prompts of the same pipe and catalog superseded (see `docs/method-layouts.md`).
|
|
1431
|
+
*
|
|
1432
|
+
* The write changes the method, so it takes an `admin` or a `member` of the method's
|
|
1433
|
+
* organization. Takes a bare catalog id, as `getMethod` does, and the version number. Sends the
|
|
1434
|
+
* members of `MethodLayoutInput` and nothing else, the route's body being closed at both levels,
|
|
1435
|
+
* and leaves out each optional provenance member that is `null` or omitted, for the platform's
|
|
1436
|
+
* default. Throws `RequestArgumentError` before any request for a `version` that is not a positive
|
|
1437
|
+
* integer and for a `layout` or a `provenance` that is not an object; every other member is sent
|
|
1438
|
+
* as given. Throws `ApiResponseError` when the platform refuses the write, which stores nothing:
|
|
1439
|
+
* `422` `request.invalid` for a member breaking its shape or bound, naming it in `params.field`
|
|
1440
|
+
* and `location.field` (`provenance.<member>` for one of the provenance's), and, naming
|
|
1441
|
+
* `tree_digest`, for a digest that is not the tree the version holds; `403` `auth.forbidden`
|
|
1442
|
+
* naming the `member` requirement for a `runner` or a caller with no recognised role; `404`
|
|
1443
|
+
* `pipelex.catalog.not_found` for an unknown method or another organization's, its
|
|
1444
|
+
* `params.resource` `method`, and for a version the method never published, its
|
|
1445
|
+
* `params.resource` `version`; `409` `pipelex.catalog.being_deleted` while the method's erasure is
|
|
1446
|
+
* pending, under way or stopped; and `500` `runtime.unexpected` for a version stored before
|
|
1447
|
+
* methods were trees, which names no tree. Under an idempotency key, `409`
|
|
1448
|
+
* `request.idempotency_conflict` for the key sent with a different body and `409`
|
|
1449
|
+
* `request.idempotency_key_in_use` while its earlier request is still being handled; a key that is
|
|
1450
|
+
* not 1 to 255 visible ASCII characters is a `RequestArgumentError` before any request. An answer
|
|
1451
|
+
* that does not read as a layout (`methodLayoutFault`) is an answer the SDK cannot read, thrown as
|
|
1452
|
+
* an `ApiResponseError` too.
|
|
1453
|
+
*/
|
|
1454
|
+
storeMethodLayout(methodId: string, version: number, layout: MethodLayoutInput, options?: MethodLayoutWriteOptions): Promise<MethodLayout>;
|
|
1357
1455
|
/**
|
|
1358
1456
|
* Copy a method into an organization the caller may change methods in —
|
|
1359
1457
|
* `POST /v1/methods/{id}/copy`: the method's own organization for a duplicate, another team, or
|
package/dist/client.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// source combinations are legal is an invariant of `RunRequest` itself, so this
|
|
3
3
|
// client and the MTHDS runners cannot disagree about what they reject.
|
|
4
4
|
import { assertOneMethodSource, buildProblem, isProblem, parseJsonBody, readProblemBody, } from "mthds/protocol";
|
|
5
|
+
import { LAYOUT_INPUT_MEMBERS, PROVENANCE_INPUT_MEMBERS, methodLayoutFault, readMethodLayout, readMethodLayouts, } from "./method-layouts.js";
|
|
5
6
|
import { assertArtifactSelection, assertWaitOptions, pollUntilResult, readRunDegradation, RUN_STATUSES, runProgressFault, runStatusFilter, selectionIncludesMainStuff, throwIfAborted, withCheckedProgressHealth, } from "./runs.js";
|
|
6
7
|
import { ABORT_TIMEOUT, ApiResponseError, argumentInvalidProblem, ApiUnreachableError, MissingMainStuffError, PagingNotTerminatingError, PipelineExecuteTimeoutError, ProblemError, RequestArgumentError, RunLifecycleUnavailableError, RunStillRunningError, } from "./errors.js";
|
|
7
8
|
import { isPlainObject } from "./records.js";
|
|
@@ -326,6 +327,19 @@ const COPY_DRAFT_TOKEN_ARGUMENT = {
|
|
|
326
327
|
what: "an expected_draft_updated_at, the draft token the caller last read, the method's " +
|
|
327
328
|
"`updated_at`, a string, or none",
|
|
328
329
|
};
|
|
330
|
+
/** What each argument of a layout write is, as a refusal of it names it. */
|
|
331
|
+
const LAYOUT_VERSION_ARGUMENT = {
|
|
332
|
+
name: "version",
|
|
333
|
+
what: "a version number, the published version the layout was designed for, a positive integer",
|
|
334
|
+
};
|
|
335
|
+
const LAYOUT_ARGUMENT = {
|
|
336
|
+
name: "layout",
|
|
337
|
+
what: "the layout to store as an object, a MethodLayoutInput",
|
|
338
|
+
};
|
|
339
|
+
const LAYOUT_PROVENANCE_ARGUMENT = {
|
|
340
|
+
name: "provenance",
|
|
341
|
+
what: "the layout's provenance as an object naming at least its producer and its model",
|
|
342
|
+
};
|
|
329
343
|
/**
|
|
330
344
|
* The refusal of an argument a call cannot take, worded as every such refusal is:
|
|
331
345
|
* `<call>() takes <what>; got <got>.`, with what made it unusable as `cause` when there is one.
|
|
@@ -374,12 +388,16 @@ function requireNameAndInputs(name, inputs, call, needs) {
|
|
|
374
388
|
}
|
|
375
389
|
/**
|
|
376
390
|
* A caller's value as the refusal of a number argument names it: a number by its value, anything
|
|
377
|
-
* else by its type, as `requireNonEmptyId` names a value,
|
|
378
|
-
*
|
|
391
|
+
* else by its type, as `requireNonEmptyId` names a value, `null` as itself and an array as one, so
|
|
392
|
+
* a refusal of a value that is not an object never answers an array with "got object". Never by
|
|
393
|
+
* `String`, which throws for an object with no prototype and would replace the refusal with a
|
|
394
|
+
* `TypeError`.
|
|
379
395
|
*/
|
|
380
396
|
function describeNumberArgument(value) {
|
|
381
397
|
if (typeof value === "number")
|
|
382
398
|
return String(value);
|
|
399
|
+
if (Array.isArray(value))
|
|
400
|
+
return "an array";
|
|
383
401
|
return value === null ? "null" : typeof value;
|
|
384
402
|
}
|
|
385
403
|
/**
|
|
@@ -582,8 +600,8 @@ export class PipelexApiClient {
|
|
|
582
600
|
* `url` is a fully resolved absolute URL. `mayStartRun` marks a request that may create a run,
|
|
583
601
|
* whose errors then say whether it may have (see `RunCreation`).
|
|
584
602
|
* `idempotencyKey`, already checked by `idempotencyKeyOf`, is sent as the
|
|
585
|
-
* `Idempotency-Key` header; only `start`, `copyMethod`, `
|
|
586
|
-
* `replayRunWebhookDelivery` pass one.
|
|
603
|
+
* `Idempotency-Key` header; only `start`, `copyMethod`, `storeMethodLayout`,
|
|
604
|
+
* `rotateWebhookSecret` and `replayRunWebhookDelivery` pass one.
|
|
587
605
|
*/
|
|
588
606
|
async requestRaw(method, url, options = {}) {
|
|
589
607
|
const hasBody = options.body !== undefined;
|
|
@@ -1181,7 +1199,10 @@ export class PipelexApiClient {
|
|
|
1181
1199
|
* `options.views` is the sibling opt-in for *structured* views where `render`
|
|
1182
1200
|
* carries *rendered text*. The two lists are independent, each resolving its own
|
|
1183
1201
|
* tokens against its own supported set, and each supported token adds a same-named
|
|
1184
|
-
* top-level field to the valid arm — `input_form` and `output_form
|
|
1202
|
+
* top-level field to the valid arm — `input_form` and `output_form`, and on a hosted
|
|
1203
|
+
* validate by `method_id`, `layouts`, the layouts stored for the resolved tree
|
|
1204
|
+
* (`PipelexValidationReport.layouts`, absent when there are none), each entry read as
|
|
1205
|
+
* `storeMethodLayout`'s answer is and one that does not read as a layout left out. Unlike
|
|
1185
1206
|
* `render`, this client sends `views` ONLY when the caller asks: the point of an
|
|
1186
1207
|
* opt-in view is that the default response stays byte-identical, and the
|
|
1187
1208
|
* highest-frequency consumers (hook pipelines, CI gates, agent loops) never pay for
|
|
@@ -1239,7 +1260,7 @@ export class PipelexApiClient {
|
|
|
1239
1260
|
if (res.status < 200 || res.status >= 300) {
|
|
1240
1261
|
this.throwApiResponseError("POST", "validate", res);
|
|
1241
1262
|
}
|
|
1242
|
-
return withVerdictItems(this.readObjectAnswer("POST", "validate", res));
|
|
1263
|
+
return withReadLayouts(withVerdictItems(this.readObjectAnswer("POST", "validate", res)));
|
|
1243
1264
|
}
|
|
1244
1265
|
// ── Tools extensions (Pipelex API — `/v1/lint`, `/v1/format`) ─────────
|
|
1245
1266
|
//
|
|
@@ -1943,8 +1964,8 @@ export class PipelexApiClient {
|
|
|
1943
1964
|
* unknown or malformed name serves `default`, and the page's `ordering` names the one served,
|
|
1944
1965
|
* `null` when there is none. `category` keeps one category's templates, by slug, in the same
|
|
1945
1966
|
* order; an unknown slug keeps none. Whatever the filter, the page's `categories` names, with its
|
|
1946
|
-
*
|
|
1947
|
-
* from it is ever empty.
|
|
1967
|
+
* labels, every category at least one template shows, in the catalog's order, so no tab drawn
|
|
1968
|
+
* from it is ever empty; `listMethodCategories` reads the whole list.
|
|
1948
1969
|
*
|
|
1949
1970
|
* Each `MethodTemplate` carries the method's id and name, its latest published version's number,
|
|
1950
1971
|
* description and categories, its organization's display name and each thumbnail with its size
|
|
@@ -1954,17 +1975,18 @@ export class PipelexApiClient {
|
|
|
1954
1975
|
* Pass the page's `nextCursor` back as `cursor` with the same `ordering` and `category`, which the
|
|
1955
1976
|
* cursor does not hold; `null` is the last page. `iterateMethodTemplates` follows it for you.
|
|
1956
1977
|
*
|
|
1957
|
-
* Throws `ApiResponseError`: `
|
|
1958
|
-
*
|
|
1959
|
-
* `
|
|
1960
|
-
*
|
|
1961
|
-
*
|
|
1962
|
-
*
|
|
1963
|
-
*
|
|
1964
|
-
* `
|
|
1965
|
-
*
|
|
1966
|
-
*
|
|
1967
|
-
* `
|
|
1978
|
+
* Throws `ApiResponseError`: `403` `pipelex.org.required` without an active organization, `422`
|
|
1979
|
+
* `request.invalid` for a `limit` outside 1 to 200 or a cursor of another listing, `422`
|
|
1980
|
+
* `request.malformed` for a cursor that does not decode, `500` `runtime.misconfigured` when the
|
|
1981
|
+
* templates organization the deployment names has no record or a link cannot be signed, and
|
|
1982
|
+
* `503` `runtime.unavailable`, safe to retry. A page whose `items` or `categories` is not an
|
|
1983
|
+
* array, whose `next_cursor` or `ordering` is neither a string nor `null`, a template lacking a
|
|
1984
|
+
* member a card is drawn from (a string `method_id`, `name` and `organization_name`, a
|
|
1985
|
+
* `description` that is a string or `null`, a positive integer `latest_version`, an array of
|
|
1986
|
+
* string `categories`, and `thumbnails` each with a string `path`, `media_type`, `sha256`, `url`
|
|
1987
|
+
* and `expires_at` and a positive integer `width` and `height`), and a category without a string
|
|
1988
|
+
* `slug` and `label` or whose `labels` is not an object of strings are an answer the SDK cannot
|
|
1989
|
+
* read, thrown as an `ApiResponseError` too, its links redacted.
|
|
1968
1990
|
*/
|
|
1969
1991
|
async listMethodTemplates(query = {}) {
|
|
1970
1992
|
const search = productQuery({
|
|
@@ -2010,6 +2032,35 @@ export class PipelexApiClient {
|
|
|
2010
2032
|
async *iterateMethodTemplates(query = {}) {
|
|
2011
2033
|
yield* followCursor("listMethodTemplates", (cursor) => this.listMethodTemplates({ ...query, cursor }));
|
|
2012
2034
|
}
|
|
2035
|
+
/**
|
|
2036
|
+
* The catalog's whole list of categories — `GET /v1/method-categories`: every category an admin
|
|
2037
|
+
* of the platform put on it, in the catalog's order, whether or not a template shows it, the same
|
|
2038
|
+
* for every caller. Each `MethodCategory` carries its `slug`, the one a manifest declares, its
|
|
2039
|
+
* English `label`, and its `labels` by language tag, which may lack a language the platform
|
|
2040
|
+
* added after the category was stored; show `labels[locale] ?? label`.
|
|
2041
|
+
*
|
|
2042
|
+
* A template shows under the categories its manifest declares that are on this list, so this is
|
|
2043
|
+
* the list a method's categories are picked from. `listMethodTemplates` names, on every page, only
|
|
2044
|
+
* the categories some template shows, for the tabs a client draws.
|
|
2045
|
+
*
|
|
2046
|
+
* Throws `ApiResponseError`: `403` `pipelex.org.required` without an active organization, and
|
|
2047
|
+
* `503` `runtime.unavailable`, safe to retry. An answer whose `categories` is not an array, or
|
|
2048
|
+
* holds a category without a string `slug` and `label` or whose `labels` is not an object of
|
|
2049
|
+
* strings, is an answer the SDK cannot read, thrown as an `ApiResponseError` too.
|
|
2050
|
+
*/
|
|
2051
|
+
async listMethodCategories() {
|
|
2052
|
+
const endpoint = "method-categories";
|
|
2053
|
+
const res = await this.requestProductAnswer("GET", endpoint, undefined);
|
|
2054
|
+
const list = this.readObjectAnswer("GET", endpoint, res);
|
|
2055
|
+
const { categories } = list;
|
|
2056
|
+
const fault = Array.isArray(categories)
|
|
2057
|
+
? methodCategoriesFault(categories)
|
|
2058
|
+
: "`categories` is not an array";
|
|
2059
|
+
if (fault !== null) {
|
|
2060
|
+
throw this.unreadableAnswer("GET", `/${API_PREFIX}/${endpoint}`, res, `a list of categories whose ${fault}`);
|
|
2061
|
+
}
|
|
2062
|
+
return list;
|
|
2063
|
+
}
|
|
2013
2064
|
/**
|
|
2014
2065
|
* Read one method with its files — `GET /v1/methods/{id}`. One of the caller's organization's
|
|
2015
2066
|
* methods answers a `MethodDetail`: its identity, its draft
|
|
@@ -2605,6 +2656,66 @@ export class PipelexApiClient {
|
|
|
2605
2656
|
const res = await this.requestProductAnswer("GET", endpoint, undefined);
|
|
2606
2657
|
return this.readSavedTree("GET", endpoint, withLinks(res), "a published version");
|
|
2607
2658
|
}
|
|
2659
|
+
/**
|
|
2660
|
+
* Store a layout designed for one pipe of a published version of a method —
|
|
2661
|
+
* `POST /v1/methods/{id}/versions/{n}/layouts` — and answer it as stored, with the `stored_at` the
|
|
2662
|
+
* platform stamped.
|
|
2663
|
+
*
|
|
2664
|
+
* A layout is a data file a producer designed over the version's input-form descriptor, in the
|
|
2665
|
+
* vocabulary of a catalog of components. The platform stores it beside the method, keyed by the
|
|
2666
|
+
* content it was designed for, and hands it back on every validate of the method by `method_id`
|
|
2667
|
+
* that names `layouts` among its `views` and resolves the same tree
|
|
2668
|
+
* (`PipelexValidationReport.layouts`). It never produces, parses or judges one.
|
|
2669
|
+
*
|
|
2670
|
+
* `layout.tree_digest` must be the digest of the tree `version` holds, its
|
|
2671
|
+
* `MethodVersionSummary.tree_digest`: a producer designs over the descriptor of the version it was
|
|
2672
|
+
* handed. **A write of the same identity replaces the layout**, the identity being `tree_digest`,
|
|
2673
|
+
* `pipe_ref`, `catalog` and `prompt_hash`, and is answered the same `200` whether it stored a new
|
|
2674
|
+
* layout or replaced one. `options.idempotencyKey` is sent as the `Idempotency-Key` header, so a
|
|
2675
|
+
* write whose answer was lost is sent again under the same key and answered as the first was; sent
|
|
2676
|
+
* again without one, it runs again, with a new `stored_at` and the bound applied again
|
|
2677
|
+
* (`MethodLayoutWriteOptions`). A content keeps at most 16
|
|
2678
|
+
* layouts, every pipe, catalog and prompt hash together; past the bound a write removes first what
|
|
2679
|
+
* newer prompts of the same pipe and catalog superseded (see `docs/method-layouts.md`).
|
|
2680
|
+
*
|
|
2681
|
+
* The write changes the method, so it takes an `admin` or a `member` of the method's
|
|
2682
|
+
* organization. Takes a bare catalog id, as `getMethod` does, and the version number. Sends the
|
|
2683
|
+
* members of `MethodLayoutInput` and nothing else, the route's body being closed at both levels,
|
|
2684
|
+
* and leaves out each optional provenance member that is `null` or omitted, for the platform's
|
|
2685
|
+
* default. Throws `RequestArgumentError` before any request for a `version` that is not a positive
|
|
2686
|
+
* integer and for a `layout` or a `provenance` that is not an object; every other member is sent
|
|
2687
|
+
* as given. Throws `ApiResponseError` when the platform refuses the write, which stores nothing:
|
|
2688
|
+
* `422` `request.invalid` for a member breaking its shape or bound, naming it in `params.field`
|
|
2689
|
+
* and `location.field` (`provenance.<member>` for one of the provenance's), and, naming
|
|
2690
|
+
* `tree_digest`, for a digest that is not the tree the version holds; `403` `auth.forbidden`
|
|
2691
|
+
* naming the `member` requirement for a `runner` or a caller with no recognised role; `404`
|
|
2692
|
+
* `pipelex.catalog.not_found` for an unknown method or another organization's, its
|
|
2693
|
+
* `params.resource` `method`, and for a version the method never published, its
|
|
2694
|
+
* `params.resource` `version`; `409` `pipelex.catalog.being_deleted` while the method's erasure is
|
|
2695
|
+
* pending, under way or stopped; and `500` `runtime.unexpected` for a version stored before
|
|
2696
|
+
* methods were trees, which names no tree. Under an idempotency key, `409`
|
|
2697
|
+
* `request.idempotency_conflict` for the key sent with a different body and `409`
|
|
2698
|
+
* `request.idempotency_key_in_use` while its earlier request is still being handled; a key that is
|
|
2699
|
+
* not 1 to 255 visible ASCII characters is a `RequestArgumentError` before any request. An answer
|
|
2700
|
+
* that does not read as a layout (`methodLayoutFault`) is an answer the SDK cannot read, thrown as
|
|
2701
|
+
* an `ApiResponseError` too.
|
|
2702
|
+
*/
|
|
2703
|
+
async storeMethodLayout(methodId, version, layout, options = {}) {
|
|
2704
|
+
const call = "storeMethodLayout";
|
|
2705
|
+
if (!Number.isSafeInteger(version) || version < 1) {
|
|
2706
|
+
throw argumentRefusal(call, LAYOUT_VERSION_ARGUMENT, describeNumberArgument(version), "not_a_positive_integer");
|
|
2707
|
+
}
|
|
2708
|
+
const endpoint = `${methodPath(methodId, call)}/versions/${version}/layouts`;
|
|
2709
|
+
const body = methodLayoutBody(layout, call);
|
|
2710
|
+
const idempotencyKey = idempotencyKeyOf(options, "store the layout");
|
|
2711
|
+
const res = await this.requestProductAnswer("POST", endpoint, body, { idempotencyKey });
|
|
2712
|
+
const answer = this.readObjectAnswer("POST", endpoint, res);
|
|
2713
|
+
const stored = readMethodLayout(answer);
|
|
2714
|
+
if (stored === null) {
|
|
2715
|
+
throw this.unreadableAnswer("POST", `/${API_PREFIX}/${endpoint}`, res, `a layout ${methodLayoutFault(answer) ?? "that is not one"}`);
|
|
2716
|
+
}
|
|
2717
|
+
return stored;
|
|
2718
|
+
}
|
|
2608
2719
|
/**
|
|
2609
2720
|
* Copy a method into an organization the caller may change methods in —
|
|
2610
2721
|
* `POST /v1/methods/{id}/copy`: the method's own organization for a duplicate, another team, or
|
|
@@ -4372,13 +4483,14 @@ const MAX_IDEMPOTENCY_KEY_LENGTH = 255;
|
|
|
4372
4483
|
*/
|
|
4373
4484
|
const VISIBLE_ASCII = /^[\x21-\x7E]+$/;
|
|
4374
4485
|
/**
|
|
4375
|
-
* The idempotency key a start, a method copy, a secret rotation or a delivery
|
|
4486
|
+
* The idempotency key a start, a method copy, a layout write, a secret rotation or a delivery
|
|
4487
|
+
* replay sends, or
|
|
4376
4488
|
* `undefined` when the options carry none (absent or `null`). A key that is not a string of 1 to
|
|
4377
4489
|
* 255 visible ASCII characters is refused with a `RequestArgumentError` rather than dropped, an
|
|
4378
4490
|
* empty one included, since a request sent without the key its caller gave would lose the
|
|
4379
4491
|
* protection the caller asked for without a word. `request` says what the request does, as the
|
|
4380
|
-
* refusal's last sentence names it ("start the run", "copy the method", "
|
|
4381
|
-
* "replay the delivery"). The message never quotes the key, which a caller may derive from
|
|
4492
|
+
* refusal's last sentence names it ("start the run", "copy the method", "store the layout",
|
|
4493
|
+
* "rotate the secret", "replay the delivery"). The message never quotes the key, which a caller may derive from
|
|
4382
4494
|
* something private. The Python SDK holds a key to the same rule, in the same words.
|
|
4383
4495
|
*/
|
|
4384
4496
|
function idempotencyKeyOf(options, request = "start the run") {
|
|
@@ -4604,7 +4716,10 @@ function methodTemplatesFault(items) {
|
|
|
4604
4716
|
}
|
|
4605
4717
|
return null;
|
|
4606
4718
|
}
|
|
4607
|
-
/**
|
|
4719
|
+
/**
|
|
4720
|
+
* Why the catalog's categories are not ones the SDK reads, or `null`: each a string `slug` and
|
|
4721
|
+
* `label`, and `labels`, an object whose every value is a string.
|
|
4722
|
+
*/
|
|
4608
4723
|
function methodCategoriesFault(categories) {
|
|
4609
4724
|
for (let index = 0; index < categories.length; index += 1) {
|
|
4610
4725
|
const category = categories[index];
|
|
@@ -4613,6 +4728,11 @@ function methodCategoriesFault(categories) {
|
|
|
4613
4728
|
typeof category.label !== "string") {
|
|
4614
4729
|
return `\`categories\` holds an entry without a string \`slug\` and \`label\`, at [${index}]`;
|
|
4615
4730
|
}
|
|
4731
|
+
const labels = category.labels;
|
|
4732
|
+
if (!isPlainObject(labels) ||
|
|
4733
|
+
!Object.values(labels).every((label) => typeof label === "string")) {
|
|
4734
|
+
return `\`categories\` holds an entry whose \`labels\` is not an object of strings, at [${index}]`;
|
|
4735
|
+
}
|
|
4616
4736
|
}
|
|
4617
4737
|
return null;
|
|
4618
4738
|
}
|
|
@@ -4733,6 +4853,52 @@ function withVerdictItems(answer) {
|
|
|
4733
4853
|
read.warnings = readProblemItems(answer.warnings);
|
|
4734
4854
|
return read;
|
|
4735
4855
|
}
|
|
4856
|
+
/**
|
|
4857
|
+
* A valid verdict with its `layouts` read entry by entry (`readMethodLayouts`), only when the
|
|
4858
|
+
* answer carries the key: each entry that reads as a layout is kept, in the answer's order, and a
|
|
4859
|
+
* list left with none is removed, so `layouts` is either absent or holds at least one layout, as
|
|
4860
|
+
* the platform sends it. Any other answer is returned as it came.
|
|
4861
|
+
*/
|
|
4862
|
+
function withReadLayouts(answer) {
|
|
4863
|
+
if (!isPlainObject(answer) || answer.is_valid !== true || !Object.hasOwn(answer, "layouts")) {
|
|
4864
|
+
return answer;
|
|
4865
|
+
}
|
|
4866
|
+
const read = { ...answer };
|
|
4867
|
+
const layouts = readMethodLayouts(answer.layouts);
|
|
4868
|
+
if (layouts.length === 0)
|
|
4869
|
+
delete read.layouts;
|
|
4870
|
+
else
|
|
4871
|
+
read.layouts = layouts;
|
|
4872
|
+
return read;
|
|
4873
|
+
}
|
|
4874
|
+
/**
|
|
4875
|
+
* The body `storeMethodLayout` sends for `layout`: the members of `MethodLayoutInput` and nothing
|
|
4876
|
+
* else, since the route's body is closed at both levels, each provenance member that is `null` or
|
|
4877
|
+
* omitted left out for the platform's default. The members are `LAYOUT_INPUT_MEMBERS` and
|
|
4878
|
+
* `PROVENANCE_INPUT_MEMBERS`, which the build holds to the interfaces. A layout or a provenance that is not an object is
|
|
4879
|
+
* refused with `RequestArgumentError` before anything is sent; every other member is sent as given,
|
|
4880
|
+
* for the platform to hold to its shapes, its `422` naming the member it refuses.
|
|
4881
|
+
*/
|
|
4882
|
+
function methodLayoutBody(layout, call) {
|
|
4883
|
+
if (!isPlainObject(layout)) {
|
|
4884
|
+
throw argumentRefusal(call, LAYOUT_ARGUMENT, describeNumberArgument(layout), "not_an_object");
|
|
4885
|
+
}
|
|
4886
|
+
const provenance = layout.provenance;
|
|
4887
|
+
if (!isPlainObject(provenance)) {
|
|
4888
|
+
throw argumentRefusal(call, LAYOUT_PROVENANCE_ARGUMENT, describeNumberArgument(provenance), "not_an_object");
|
|
4889
|
+
}
|
|
4890
|
+
const sentProvenance = {};
|
|
4891
|
+
for (const [member, presence] of Object.entries(PROVENANCE_INPUT_MEMBERS)) {
|
|
4892
|
+
const value = provenance[member];
|
|
4893
|
+
if (presence === "required" || value != null)
|
|
4894
|
+
sentProvenance[member] = value;
|
|
4895
|
+
}
|
|
4896
|
+
const body = {};
|
|
4897
|
+
for (const member of Object.keys(LAYOUT_INPUT_MEMBERS))
|
|
4898
|
+
body[member] = layout[member];
|
|
4899
|
+
body.provenance = sentProvenance;
|
|
4900
|
+
return body;
|
|
4901
|
+
}
|
|
4736
4902
|
/**
|
|
4737
4903
|
* A run record with its `error` kept only when it is a problem document, and read as `null`
|
|
4738
4904
|
* otherwise. Only when the record carries the `error` key, so a server that does not serve it
|