@pipelex/sdk 0.51.0 → 0.53.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.
Files changed (88) hide show
  1. package/README.md +20 -19
  2. package/dist/answer-reading.d.ts +70 -0
  3. package/dist/answer-reading.js +106 -0
  4. package/dist/answer-reading.js.map +1 -0
  5. package/dist/artifacts.js +45 -32
  6. package/dist/artifacts.js.map +1 -1
  7. package/dist/cli/present.d.ts +11 -14
  8. package/dist/cli/present.js +41 -37
  9. package/dist/cli/present.js.map +1 -1
  10. package/dist/cli/run.js +8 -7
  11. package/dist/cli/run.js.map +1 -1
  12. package/dist/cli/script.d.ts +1 -1
  13. package/dist/cli/source.js +2 -2
  14. package/dist/cli/source.js.map +1 -1
  15. package/dist/client.d.ts +254 -213
  16. package/dist/client.js +542 -472
  17. package/dist/client.js.map +1 -1
  18. package/dist/codegen-check.d.ts +21 -15
  19. package/dist/codegen-check.js +54 -27
  20. package/dist/codegen-check.js.map +1 -1
  21. package/dist/errors.d.ts +298 -455
  22. package/dist/errors.js +426 -534
  23. package/dist/errors.js.map +1 -1
  24. package/dist/exact-json.d.ts +8 -12
  25. package/dist/exact-json.js +19 -12
  26. package/dist/exact-json.js.map +1 -1
  27. package/dist/hooks/check-core.d.ts +12 -7
  28. package/dist/hooks/check-core.js +20 -14
  29. package/dist/hooks/check-core.js.map +1 -1
  30. package/dist/hooks/validate-stage.js +12 -3
  31. package/dist/hooks/validate-stage.js.map +1 -1
  32. package/dist/index.d.ts +4 -5
  33. package/dist/index.js +7 -6
  34. package/dist/index.js.map +1 -1
  35. package/dist/method-selector.js +7 -5
  36. package/dist/method-selector.js.map +1 -1
  37. package/dist/method-trees.d.ts +6 -7
  38. package/dist/method-trees.js +31 -37
  39. package/dist/method-trees.js.map +1 -1
  40. package/dist/models.d.ts +33 -210
  41. package/dist/models.js +2 -1
  42. package/dist/models.js.map +1 -1
  43. package/dist/prepare-inputs.d.ts +2 -2
  44. package/dist/prepare-inputs.js +51 -54
  45. package/dist/prepare-inputs.js.map +1 -1
  46. package/dist/problem-items.d.ts +19 -0
  47. package/dist/problem-items.js +52 -0
  48. package/dist/problem-items.js.map +1 -0
  49. package/dist/product-models.d.ts +43 -29
  50. package/dist/records.d.ts +12 -0
  51. package/dist/records.js +23 -0
  52. package/dist/records.js.map +1 -0
  53. package/dist/runs.d.ts +49 -38
  54. package/dist/runs.js +45 -19
  55. package/dist/runs.js.map +1 -1
  56. package/dist/transport-reading.d.ts +18 -0
  57. package/dist/{run-may-have-started.js → transport-reading.js} +15 -44
  58. package/dist/transport-reading.js.map +1 -0
  59. package/dist/upload-grant.d.ts +9 -6
  60. package/dist/upload-grant.js +42 -29
  61. package/dist/upload-grant.js.map +1 -1
  62. package/dist/upload.js +53 -33
  63. package/dist/upload.js.map +1 -1
  64. package/dist/version.d.ts +1 -1
  65. package/dist/version.js +1 -1
  66. package/docs/architecture.md +19 -19
  67. package/docs/artifact-download.md +6 -6
  68. package/docs/cli.md +1 -1
  69. package/docs/crate-routes.md +13 -14
  70. package/docs/errors.md +154 -267
  71. package/docs/exact-integers.md +1 -1
  72. package/docs/files.md +13 -13
  73. package/docs/hook-bundle.md +15 -1
  74. package/docs/input-preparation.md +14 -14
  75. package/docs/method-copy.md +35 -33
  76. package/docs/method-examples.md +13 -14
  77. package/docs/method-trees.md +27 -28
  78. package/docs/organization-branding.md +14 -16
  79. package/docs/run-results.md +8 -8
  80. package/package.json +1 -1
  81. package/dist/error-models.d.ts +0 -302
  82. package/dist/error-models.js +0 -203
  83. package/dist/error-models.js.map +0 -1
  84. package/dist/error-verdicts.d.ts +0 -46
  85. package/dist/error-verdicts.js +0 -120
  86. package/dist/error-verdicts.js.map +0 -1
  87. package/dist/run-may-have-started.d.ts +0 -31
  88. package/dist/run-may-have-started.js.map +0 -1
package/dist/models.d.ts CHANGED
@@ -3,12 +3,13 @@
3
3
  *
4
4
  * Holds the Dict-serialized protocol concretes (`DictStuff` / `DictWorkingMemory`
5
5
  * / `DictPipeOutput` and the default `RunResultExecute` binding), the Pipelex
6
- * `/v1/validate` surface (`PipelexValidationResult` + `ValidationErrorItem`), the
6
+ * `/v1/validate` surface (`PipelexValidationResult`, whose faults are the standard's
7
+ * `ProblemItem`s), the
7
8
  * request/response models of the tools routes and the crate routes, and the verdict of
8
9
  * the model reference check. Built on the protocol wire types imported from the
9
10
  * `mthds/protocol` subpath.
10
11
  */
11
- import type { InputForm, MethodFiles, MethodTreeSource, OutputForm, PipeIOContracts, RunResultExecute, RunResultStart } from "mthds/protocol";
12
+ import type { InputForm, MethodFiles, ProblemItem, MethodTreeSource, OutputForm, PipeIOContracts, RunResultExecute, RunResultStart } from "mthds/protocol";
12
13
  import type { MethodTreeForm, NoMethodTree } from "./method-trees.js";
13
14
  export interface DictStuff {
14
15
  concept: string;
@@ -85,8 +86,8 @@ export interface PipelexRunResultStart extends RunResultStart {
85
86
  * one of inline source / `method_ref` / `method_id` per request — any second
86
87
  * selector is a request-shape `422`. An unknown or foreign-org id is a `404`
87
88
  * (indistinguishable by design); a bare id of a method never published is a `409`
88
- * `method_not_published`; a version never published is a `404`
89
- * `method_version_not_found`; a malformed suffix, or a stored method with no MTHDS
89
+ * `pipelex.catalog.not_published`; a version never published is a `404`
90
+ * `pipelex.catalog.not_found`; a malformed suffix, or a stored method with no MTHDS
90
91
  * source, is a `422`.
91
92
  */
92
93
  export interface PipelexHostedToolingExtensions {
@@ -301,17 +302,12 @@ export interface PipelexValidationReport {
301
302
  /** Per-pipe dry-run sweep outcomes. */
302
303
  validated_pipes: ValidatedPipeEntry[];
303
304
  /**
304
- * Advisory findings on a VALID bundle — same item shape as `validation_errors[]`, so one
305
- * parser serves both channels, but these never flip `is_valid`. Empty when there is
306
- * nothing to report. Their `error_type`s come in families, always in this order: the
307
- * useless-`!` lint (`optional_force_redundant`), the entry pipe's vacuous-presence lint
308
- * (`input_presence_vacuous`), the intent-hint lints (`hint_unknown_key`,
309
- * `hint_unknown_intent`, `hint_inapplicable_intent`, which ride here exclusively), then the
310
- * findings a `PipeDocGen` step's template check made in its office template file during
311
- * the dry run (`template_check_warning`, a `dry_run` item carrying `pipe_code`,
312
- * `domain_code`, `template_file` and `template_location`).
305
+ * Advisory findings on a VALID bundle, one problem item each with the code its finding's
306
+ * type names: the same item shape as the invalid arm's `errors[]`, so one reader serves
307
+ * both, but these never flip `is_valid`. Empty when there is nothing to report. The client
308
+ * reads them member by member (`readProblemItems`).
313
309
  */
314
- warnings: ValidationErrorItem[];
310
+ warnings: ProblemItem[];
315
311
  /** Qualified refs of pipes still declared as `PipeSignature`. */
316
312
  pending_signatures: string[];
317
313
  /** `not pending_signatures` — whether the validated library is complete enough to run. */
@@ -332,17 +328,20 @@ export interface PipelexValidationReport {
332
328
  }
333
329
  /**
334
330
  * Pipelex's `POST /v1/validate` 200 body for an INVALID bundle — the `is_valid: false`
335
- * arm of the response union, carrying the closed-vocabulary `ValidationErrorItem[]`.
336
- * The structural artifacts of the valid arm
337
- * are absent (they don't exist when load/parse/wiring failed); `validation_errors[]` is
338
- * non-empty on every invalid verdict (the structured-info invariant — even a parse-level
339
- * failure carries one source-less `blueprint_validation` residual).
331
+ * arm of the response union, listing its faults as problem items. The structural
332
+ * artifacts of the valid arm are absent (they don't exist when load/parse/wiring failed);
333
+ * `errors[]` is non-empty on every invalid verdict the server sends, and the client reads
334
+ * it member by member (`readProblemItems`), so an item whose `code` or `detail` is not a
335
+ * string is skipped and a malformed member is left out of its item.
340
336
  */
341
337
  export interface PipelexInvalidReport {
342
338
  /** Discriminant of the invalid arm. */
343
339
  is_valid: false;
344
- /** Structured per-error diagnostics, built by pipelex's one shared builder. */
345
- validation_errors: ValidationErrorItem[];
340
+ /**
341
+ * One problem item per fault, each with its own `code`, `detail`, `params`, `location`
342
+ * and, when the server has one, its `suggested_fix`.
343
+ */
344
+ errors: ProblemItem[];
346
345
  /** Outstanding signatures (best-effort; empty when no library could be assembled). */
347
346
  pending_signatures: string[];
348
347
  /** An invalid bundle is never runnable. */
@@ -351,8 +350,8 @@ export interface PipelexInvalidReport {
351
350
  message: string;
352
351
  /**
353
352
  * Opt-in Pipelex-API presentation extra: the server-rendered Markdown view of the
354
- * invalid verdict (`# Validation failed` + the `validation_errors`), present only
355
- * when the request asked for it (`render: ["markdown"]`). Absent by default.
353
+ * invalid verdict's errors, present only when the request asked for it
354
+ * (`render: ["markdown"]`). Absent by default.
356
355
  */
357
356
  rendered_markdown?: string;
358
357
  }
@@ -363,185 +362,6 @@ export interface PipelexInvalidReport {
363
362
  * answers, not narrowings of the protocol's (see the section's heading above).
364
363
  */
365
364
  export type PipelexValidationResult = PipelexValidationReport | PipelexInvalidReport;
366
- /**
367
- * Which validation stage produced a `ValidationErrorItem` — mirror of pipelex's
368
- * closed `ValidationErrorCategory` set. `pipe_factory` and graph-level `dry_run`
369
- * items carry no `source`; map those by `domain_code` / `pipe_code`. The other
370
- * categories carry `source` when the runtime can attribute one.
371
- */
372
- export type ValidationErrorCategory = "blueprint_validation" | "pipe_factory" | "pipe_validation" | "dry_run";
373
- /**
374
- * One structured bundle-validation error — mirror of pipelex's `ValidationErrorItem`.
375
- * Carried by `PipelexInvalidReport.validation_errors[]` on the **200** invalid arm of
376
- * `POST /v1/validate` (NOT a 422 — an invalid bundle is a produced verdict), by the
377
- * VALID arm's advisory `warnings[]`, by the crate routes' **200** invalid arm
378
- * (`CrateInvalidReport.validation_errors[]`), by the 422 problem body a run route answers
379
- * when the runner refuses the method for its validation errors
380
- * (`ApiResponseError.validationErrors`), and by a failed run's stored report.
381
- *
382
- * **The shape is the hosted plane's.** It is the item the runner sends today, which
383
- * predates the standard's problem items: from protocol 2.0.0 a verdict lists its faults
384
- * as `ProblemItem`s under a registry `code`, which the hosted plane does not send yet, so
385
- * this declaration is not the standard's and is not pinned to it. Its fix shares the
386
- * standard's operation and safety vocabularies, which `tests/validation-items.test.ts`
387
- * pins.
388
- *
389
- * Only `category` and `message` are always present; the rest are populated per
390
- * `category`. Every other member is `?: T | null` because the two channels serialize an
391
- * unset locator differently: the invalid arm and the crate routes drop the key
392
- * (`exclude_none` server-side) while the valid arm — which carries `warnings[]` — does
393
- * not, so the same item arrives with explicit `null`s there. A truthiness check reads
394
- * both; an `=== undefined` check would be wrong on one of them.
395
- *
396
- * `source` is the declaring file: its path (CLI), or its tree path in the tree the
397
- * request sent — the owning file for cross-file diagnostics.
398
- *
399
- * An `unknown_model` item (a model reference the runner's model deck does not know)
400
- * carries the reference as written, the kind of model the field takes and the close
401
- * matches, and, when there is exactly one close match, a `suggested_fix` that remaps
402
- * the reference to it.
403
- *
404
- * A `template_check_warning` item, on the valid arm's `warnings[]` only, is a finding of a
405
- * `PipeDocGen` step's template check in its office template file during the dry run, such
406
- * as a field of the template that matches no input: a `dry_run` item carrying the step's
407
- * `pipe_code` and `domain_code`, the template file as the step names it and the place in
408
- * it the engine's finding names.
409
- */
410
- export interface ValidationErrorItem {
411
- category: ValidationErrorCategory;
412
- message: string;
413
- error_type?: string | null;
414
- pipe_code?: string | null;
415
- concept_code?: string | null;
416
- domain_code?: string | null;
417
- source?: string | null;
418
- field_path?: string | null;
419
- field_name?: string | null;
420
- variable_names?: string[] | null;
421
- missing_concept_code?: string | null;
422
- /** The pipe a reference names that the bundle does not declare. */
423
- missing_pipe_code?: string | null;
424
- declared_concepts?: string[] | null;
425
- /**
426
- * On an `unknown_model` item: the model reference exactly as the author wrote it
427
- * (`gpt-5.1`, `@best-sonet`, `$writting-factual`).
428
- */
429
- model_reference?: string | null;
430
- /** On an `unknown_model` item: the kind of model the field takes (`llm`, `img_gen`, …). */
431
- model_type?: string | null;
432
- /**
433
- * On an `unknown_model` item: the close matches of the same kind, each spelled as a
434
- * reference the field accepts.
435
- */
436
- suggestions?: string[] | null;
437
- /**
438
- * On a `template_check_warning` item: the template file as the `PipeDocGen` step names it
439
- * (`templates/invoice.xlsx`); `null` for a step of the server's own library, whose path is
440
- * the server's and is never shown.
441
- */
442
- template_file?: string | null;
443
- /**
444
- * On a `template_check_warning` item: the place in the template file the finding names, in
445
- * the engine's own words (a defined name, a table, a cell, a shape, a tag); `null` when the
446
- * finding names none.
447
- */
448
- template_location?: string | null;
449
- /** The server's deterministic repair proposal for this error, when it has one. */
450
- suggested_fix?: SuggestedFix | null;
451
- }
452
- /** Whether a `SuggestedFix` is safe to auto-apply, or needs explicit opt-in. */
453
- export type FixSafety = "safe" | "unsafe";
454
- /** The semantic patch operations a `SuggestedFix` is composed of. */
455
- export type FixOpKind = "set_key" | "ensure_table" | "delete_key" | "delete_table" | "rename_table_key" | "move_key" | "remap_value";
456
- /** A TOML-representable scalar a `set_key` op can write. */
457
- export type TomlScalar = string | number | boolean;
458
- /**
459
- * What a `set_key` op writes: a scalar, or a flat scalar mapping for fixes that create
460
- * a whole table at once (written as an inline table). Deeper nesting is not modelled —
461
- * the server does not emit it.
462
- */
463
- export type TomlValue = TomlScalar | Record<string, TomlScalar>;
464
- /** `TomlValue` under the name `mthds` gives it, so either client's vocabulary reads here. */
465
- export type FixValue = TomlValue;
466
- /**
467
- * What every fix op carries: the table it acts in. `table_path` addresses the containing
468
- * table (e.g. `["pipe", "my_seq"]`), aligned with the `field_path` conventions of
469
- * `ValidationErrorItem`, and is empty for the document root.
470
- *
471
- * The segment `"*"` is the wildcard — "every entry of the open mapping at this node".
472
- * The server refuses it as a `key` on every kind but `remap_value`, the only one with a
473
- * per-key meaning for it.
474
- */
475
- interface FixOpBase {
476
- table_path: string[];
477
- }
478
- /** Write `key = value` in the addressed table, whatever it currently holds. */
479
- export interface SetKeyOp extends FixOpBase {
480
- kind: "set_key";
481
- key: string;
482
- value: TomlValue;
483
- }
484
- /** Create the addressed table when it is missing. `table_path` is never empty. */
485
- export interface EnsureTableOp extends FixOpBase {
486
- kind: "ensure_table";
487
- }
488
- /** Remove `key` from the addressed table. */
489
- export interface DeleteKeyOp extends FixOpBase {
490
- kind: "delete_key";
491
- key: string;
492
- }
493
- /** Remove the addressed table entirely. `table_path` is never empty. */
494
- export interface DeleteTableOp extends FixOpBase {
495
- kind: "delete_table";
496
- }
497
- /** Rename `key` to `new_key` within the addressed table. */
498
- export interface RenameTableKeyOp extends FixOpBase {
499
- kind: "rename_table_key";
500
- key: string;
501
- new_key: string;
502
- }
503
- /** Move `key` out of the addressed table into `new_table_path`, under `new_key`. */
504
- export interface MoveKeyOp extends FixOpBase {
505
- kind: "move_key";
506
- key: string;
507
- new_table_path: string[];
508
- new_key: string;
509
- }
510
- /**
511
- * Rewrite `key`'s value through `mapping`, leaving an unmapped value untouched. The
512
- * unknown-model fix is one: it maps the reference as written to its one close match, so
513
- * it changes nothing once the author has edited the field.
514
- */
515
- export interface RemapValueOp extends FixOpBase {
516
- kind: "remap_value";
517
- key: string;
518
- mapping: Record<string, string>;
519
- }
520
- /** One patch operation of a `SuggestedFix` — discriminated on `kind`. */
521
- export type FixOp = SetKeyOp | EnsureTableOp | DeleteKeyOp | DeleteTableOp | RenameTableKeyOp | MoveKeyOp | RemapValueOp;
522
- /**
523
- * A deterministic fix for one validation error, ready for a style-preserving applier —
524
- * mirror of pipelex's `SuggestedFix`. The ops are semantic patches over the `.mthds`
525
- * document, not a text diff, so an applier keeps the author's formatting.
526
- *
527
- * The shape is the hosted plane's, as `ValidationErrorItem`'s is: the standard's
528
- * `SuggestedFix` names the file `file` rather than `source` and is open to members it does
529
- * not declare. Its operation kinds, its safeties and the values a `set_key` writes are the
530
- * standard's (this SDK's `TomlValue` is the standard's `FixValue`), and a type-level test
531
- * pins them.
532
- */
533
- export interface SuggestedFix {
534
- /** The kebab-case rule id, e.g. `"match-sequence-output"`. */
535
- fix_code: string;
536
- description: string;
537
- safety: FixSafety;
538
- /**
539
- * The file the ops target, when known (multi-file libraries). An applier must only
540
- * apply these ops to that file.
541
- */
542
- source?: string | null;
543
- ops: FixOp[];
544
- }
545
365
  /** Which analysis produced a `Diagnostic` — mirror of `pipelex-tools`' closed kind set. */
546
366
  export type DiagnosticKind = "syntax" | "semantic" | "schema";
547
367
  /** Source span of a `Diagnostic` — byte offsets plus 1-based line/column coordinates. */
@@ -673,7 +493,11 @@ export interface CrateRouteOptions {
673
493
  */
674
494
  export interface CrateInvalidReport {
675
495
  is_valid: false;
676
- validation_errors: ValidationErrorItem[];
496
+ /**
497
+ * One problem item per fault, the same items `/v1/validate` lists for the identical
498
+ * failure, read member by member (`readProblemItems`).
499
+ */
500
+ errors: ProblemItem[];
677
501
  message: string;
678
502
  }
679
503
  /**
@@ -787,12 +611,12 @@ export interface PipeIORequest extends CrateRequestBase, PipelexHostedToolingExt
787
611
  * The pipe to describe, as a QUALIFIED `domain.pipe_code` ref. Omit it and the
788
612
  * server's selection chain decides: the `main_pipe` of the `METHODS.toml` at the tree's
789
613
  * root, whether the tree was sent or fetched, else the closure's single `main_pipe`
790
- * declaration. A selection the server refuses is a
791
- * `422` typed by what went wrong, with `error_domain: "input"` and the candidates, where
792
- * there are some, in its `detail`: `EntryPipeNotFoundError` for a ref that names no pipe
793
- * or (without `all_pipes`) a method with no entry pipe, `EntryPipeAmbiguousError` for a
794
- * code that matches pipes in several domains or (without `all_pipes`) several `main_pipe`
795
- * declarations. A malformed request stays a `ValidationError` `422`.
614
+ * declaration. A selection the server refuses is a problem coded by what went wrong, of
615
+ * the domain `input`, the candidates in its parameters where there are some:
616
+ * `request.pipe_not_found` for a ref that names no pipe, `request.pipe_unspecified` for
617
+ * (without `all_pipes`) a method with no entry pipe, `request.pipe_ambiguous` for a code that
618
+ * matches pipes in several domains or (without `all_pipes`) several `main_pipe` declarations.
619
+ * A malformed request is `request.malformed`.
796
620
  *
797
621
  * The server does not refuse a bare code yet: a bare ref that matches one pipe is
798
622
  * resolved across domains, and the valid arm reports the qualified ref.
@@ -978,4 +802,3 @@ export interface HandleReferenceVerdict extends ModelReferenceVerdictBase {
978
802
  * matches of one verdict share its kind; no field is added to the wire to make it so.
979
803
  */
980
804
  export type ModelReferenceVerdict = PresetReferenceVerdict | AliasReferenceVerdict | WaterfallReferenceVerdict | HandleReferenceVerdict;
981
- export {};
package/dist/models.js CHANGED
@@ -3,7 +3,8 @@
3
3
  *
4
4
  * Holds the Dict-serialized protocol concretes (`DictStuff` / `DictWorkingMemory`
5
5
  * / `DictPipeOutput` and the default `RunResultExecute` binding), the Pipelex
6
- * `/v1/validate` surface (`PipelexValidationResult` + `ValidationErrorItem`), the
6
+ * `/v1/validate` surface (`PipelexValidationResult`, whose faults are the standard's
7
+ * `ProblemItem`s), the
7
8
  * request/response models of the tools routes and the crate routes, and the verdict of
8
9
  * the model reference check. Built on the protocol wire types imported from the
9
10
  * `mthds/protocol` subpath.
@@ -1 +1 @@
1
- {"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
@@ -180,8 +180,8 @@ export declare function inputFormFault(entry: unknown): InputFormFault | undefin
180
180
  * `MethodFileTransferError` before the route is asked. A bare or alias-qualified `pipe_ref`, a
181
181
  * closure that does not load, and a selection the route refuses (an unknown
182
182
  * `pipe_ref`, no entry pipe, several) throw {@link InputPreparationError}. So
183
- * does an answer that breaks the route's contract, with a `runtime` verdict that
184
- * is not retryable: no boolean `is_valid`, an `input_form` that does not describe
183
+ * does an answer that breaks the route's contract, with the problem
184
+ * `client.response_unreadable`: no boolean `is_valid`, an `input_form` that does not describe
185
185
  * the selected pipe, or a descriptor that is malformed or names a kind this SDK
186
186
  * does not know, which is checked whole before anything is uploaded. Any other
187
187
  * failure from the route (a malformed selector, an unknown or foreign-org id, no
@@ -30,9 +30,9 @@
30
30
  * dict. See `docs/input-preparation.md`, which `pipelex-sdk-python` mirrors case
31
31
  * for case.
32
32
  */
33
- import { isValidationItem } from "./error-models.js";
33
+ import { readProblemItems } from "./problem-items.js";
34
34
  import { throwIfAborted } from "./runs.js";
35
- import { ApiResponseError, InputPreparationError, InvalidInputValueError, MethodLoadError, UploadNotAllowedError, } from "./errors.js";
35
+ import { ApiResponseError, InputPreparationError, InvalidInputValueError, MethodLoadError, UploadNotAllowedError, argumentInvalidProblem, responseUnreadableProblem, } from "./errors.js";
36
36
  import { treeShapeFault } from "./method-trees.js";
37
37
  import { uploadFile, uploadKindFields } from "./upload.js";
38
38
  const PIPELEX_STORAGE_SCHEME = "pipelex-storage://";
@@ -59,18 +59,17 @@ const WALKED_KINDS = {
59
59
  };
60
60
  const KNOWN_KINDS = new Set(Object.keys(WALKED_KINDS));
61
61
  /**
62
- * The `error_type`s of the route's pipe-selection refusals — the only `422`s
63
- * `prepareInputs` turns into an `InputPreparationError`. They are the runtime's
64
- * entry-lookup classes, the vocabulary the run routes speak too: a ref that names
65
- * no pipe, or no entry pipe at all, is `EntryPipeNotFoundError`; a code that
66
- * matches pipes in several domains, or several `main_pipe` declarations, is
67
- * `EntryPipeAmbiguousError`. Every other `422` (a malformed body, an over-limit
68
- * file, a fetched package with no `.mthds` file, a stored method with no source)
69
- * shares the generic `ValidationError` type and stays an `ApiResponseError`.
62
+ * The problem codes of the route's pipe-selection refusals, the only refusals `prepareInputs`
63
+ * turns into an `InputPreparationError`: a ref that names no pipe (`request.pipe_not_found`),
64
+ * several pipes it could mean (`request.pipe_ambiguous`), and no pipe named where the method has
65
+ * no entry pipe (`request.pipe_unspecified`). They are the run routes' vocabulary too. Every other
66
+ * refusal (a malformed body, an over-limit file, a fetched package with no `.mthds` file, a stored
67
+ * method with no source) stays an `ApiResponseError`.
70
68
  */
71
- const PIPE_SELECTION_ERROR_TYPES = new Set([
72
- "EntryPipeNotFoundError",
73
- "EntryPipeAmbiguousError",
69
+ const PIPE_SELECTION_CODES = new Set([
70
+ "request.pipe_not_found",
71
+ "request.pipe_ambiguous",
72
+ "request.pipe_unspecified",
74
73
  ]);
75
74
  /** Strict plain-object test — excludes arrays, `Uint8Array`, `Blob`, and other exotics. */
76
75
  function isPlainObject(value) {
@@ -316,7 +315,7 @@ async function resolveNode(ctx, node, callerValue, at) {
316
315
  default: {
317
316
  // Unreachable: the descriptor was checked against WALKED_KINDS before the walk began.
318
317
  const unwalked = node;
319
- throw new InputPreparationError(`Cannot prepare inputs: the walk has no arm for the kind ${described(unwalked.kind)}.`, { verdict: { errorDomain: "runtime", retryable: false } });
318
+ throw new InputPreparationError(`Cannot prepare inputs: the walk has no arm for the kind ${described(unwalked.kind)}.`, { problem: responseUnreadableProblem(200) });
320
319
  }
321
320
  }
322
321
  }
@@ -343,12 +342,12 @@ function refuseNonMap(name, value, holds, isValue) {
343
342
  if (value === undefined || value === null)
344
343
  return;
345
344
  if (!isPlainObject(value)) {
346
- throw new InputPreparationError(`Cannot prepare inputs: \`${name}\` must be ${holds}, got ${describeNonMap(value)}.`);
345
+ throw new InputPreparationError(`Cannot prepare inputs: \`${name}\` must be ${holds}, got ${describeNonMap(value)}.`, { problem: argumentInvalidProblem(name, "not_a_map") });
347
346
  }
348
347
  for (const [key, entry] of Object.entries(value)) {
349
348
  if (!isValue(entry)) {
350
349
  throw new InputPreparationError(`Cannot prepare inputs: \`${name}\` must be ${holds}, and "${key}" holds ` +
351
- `${entry === null ? "null" : describeNonMap(entry)}.`);
350
+ `${entry === null ? "null" : describeNonMap(entry)}.`, { problem: argumentInvalidProblem(name, "entry_mistyped") });
352
351
  }
353
352
  }
354
353
  }
@@ -365,11 +364,11 @@ function checkedTree(value) {
365
364
  if (fault !== undefined) {
366
365
  throw new InputPreparationError("Cannot prepare inputs: `tree` must be a listed method tree — its `files`, its `digest` and " +
367
366
  "a `readFile` — such as the `MethodTree` the client's `listMethodTree` returns, got " +
368
- `${fault}.`);
367
+ `${fault}.`, { problem: argumentInvalidProblem("tree", "not_a_tree") });
369
368
  }
370
369
  const tree = value;
371
370
  if (tree.files.length === 0) {
372
- throw new InputPreparationError("Cannot prepare inputs: `tree` holds no file, and a method tree holds at least one.");
371
+ throw new InputPreparationError("Cannot prepare inputs: `tree` holds no file, and a method tree holds at least one.", { problem: argumentInvalidProblem("tree", "empty") });
373
372
  }
374
373
  return tree;
375
374
  }
@@ -390,14 +389,14 @@ function resolveSelector(request) {
390
389
  const rawFiles = raw["files"];
391
390
  if (rawFiles !== undefined && rawFiles !== null && !isPlainObject(rawFiles)) {
392
391
  throw new InputPreparationError("Cannot prepare inputs: `files` must be the method's tree as a map from tree path to file " +
393
- `entry, got ${Array.isArray(rawFiles) ? "a list" : typeof rawFiles}.`);
392
+ `entry, got ${Array.isArray(rawFiles) ? "a list" : typeof rawFiles}.`, { problem: argumentInvalidProblem("files", "not_a_map") });
394
393
  }
395
394
  refuseNonMap("contents", raw["contents"], "the method's files as a map from tree path to bytes or text", (entry) => typeof entry === "string" || entry instanceof Uint8Array);
396
395
  refuseNonMap("dependencies", raw["dependencies"], "the method packages as a map from package address to its map of file entries", isPlainObject);
397
396
  const rawDirectory = raw["directory"];
398
397
  if (rawDirectory !== undefined && rawDirectory !== null && typeof rawDirectory !== "string") {
399
398
  throw new InputPreparationError("Cannot prepare inputs: `directory` must be the method's directory as a path, got " +
400
- `${describeNonMap(rawDirectory)}.`);
399
+ `${describeNonMap(rawDirectory)}.`, { problem: argumentInvalidProblem("directory", "not_a_string") });
401
400
  }
402
401
  const directory = typeof rawDirectory === "string" && rawDirectory !== "" ? rawDirectory : undefined;
403
402
  const files = nonEmptyMap(rawFiles);
@@ -417,7 +416,7 @@ function resolveSelector(request) {
417
416
  treeForms.push("`files`");
418
417
  if (treeForms.length > 1) {
419
418
  throw new InputPreparationError(`Cannot prepare inputs: the method's tree was given as ${treeForms.join(" and ")}. Give it ` +
420
- "in one form — `directory`, `contents`, `tree` or `files`.");
419
+ "in one form — `directory`, `contents`, `tree` or `files`.", { problem: argumentInvalidProblem("files", "mutually_exclusive") });
421
420
  }
422
421
  const given = [...treeForms];
423
422
  if (methodRef !== undefined)
@@ -427,12 +426,12 @@ function resolveSelector(request) {
427
426
  if (given.length === 0) {
428
427
  throw new InputPreparationError("Cannot prepare inputs: no method selector. Supply exactly one of the method's tree " +
429
428
  "(`directory`, `contents`, `tree` or `files`), `method_ref` (a published method's address) or " +
430
- "`method_id` (a stored method's catalog id).");
429
+ "`method_id` (a stored method's catalog id).", { problem: argumentInvalidProblem("files", "nothing_to_prepare") });
431
430
  }
432
431
  if (given.length > 1) {
433
432
  throw new InputPreparationError(`Cannot prepare inputs: ${given.join(" and ")} were ${given.length === 2 ? "both" : "all"} ` +
434
433
  "given. Supply exactly one method " +
435
- "selector — the method's tree, `method_ref` or `method_id`.");
434
+ "selector — the method's tree, `method_ref` or `method_id`.", { problem: argumentInvalidProblem("files", "mutually_exclusive") });
436
435
  }
437
436
  if (treeForms.length === 1) {
438
437
  return {
@@ -447,7 +446,7 @@ function resolveSelector(request) {
447
446
  const owner = methodRef !== undefined ? "published" : "stored";
448
447
  if (dependencies !== undefined) {
449
448
  throw new InputPreparationError(`Cannot prepare inputs: \`dependencies\` accompany the method's own tree; a ${owner} ` +
450
- "method's packages are its own.");
449
+ "method's packages are its own.", { problem: argumentInvalidProblem("dependencies", "without_a_tree") });
451
450
  }
452
451
  if (methodRef !== undefined)
453
452
  return { method_ref: methodRef };
@@ -472,11 +471,11 @@ function normalizePipeRef(raw) {
472
471
  return undefined;
473
472
  if (pipeRef.includes("->")) {
474
473
  throw new InputPreparationError(`Cannot prepare inputs: \`pipe_ref\` "${pipeRef}" names a dependency package's pipe. ` +
475
- "Preparation covers the method's own pipes: name one as `domain.pipe_code`.");
474
+ "Preparation covers the method's own pipes: name one as `domain.pipe_code`.", { problem: argumentInvalidProblem("pipe_ref", "dependency_pipe") });
476
475
  }
477
476
  if (!pipeRef.includes(".")) {
478
477
  throw new InputPreparationError(`Cannot prepare inputs: \`pipe_ref\` must be qualified (\`domain.pipe_code\`), got the bare ` +
479
- `"${pipeRef}".`);
478
+ `"${pipeRef}".`, { problem: argumentInvalidProblem("pipe_ref", "not_qualified") });
480
479
  }
481
480
  return pipeRef;
482
481
  }
@@ -490,7 +489,7 @@ function resolveAllowUploads(raw) {
490
489
  return true;
491
490
  if (typeof raw === "boolean")
492
491
  return raw;
493
- throw new InputPreparationError(`Cannot prepare inputs: \`allowUploads\` must be a boolean, got ${raw === null ? "null" : typeof raw}.`);
492
+ throw new InputPreparationError(`Cannot prepare inputs: \`allowUploads\` must be a boolean, got ${raw === null ? "null" : typeof raw}.`, { problem: argumentInvalidProblem("allowUploads", "not_a_bool") });
494
493
  }
495
494
  /**
496
495
  * The request's `upload`, checked once before any request as `uploadFile` checks its own: absent
@@ -502,7 +501,7 @@ function resolveUploadAs(raw) {
502
501
  return {};
503
502
  if (!isPlainObject(raw)) {
504
503
  throw new InputPreparationError("Cannot prepare inputs: `upload` must be an object of `kind` and `method_id`, got " +
505
- `${describeNonMap(raw)}.`);
504
+ `${describeNonMap(raw)}.`, { problem: argumentInvalidProblem("upload", "not_upload_kind_options") });
506
505
  }
507
506
  return uploadKindFields(raw, "Cannot prepare inputs", "upload.");
508
507
  }
@@ -515,9 +514,9 @@ function resolveUploadAs(raw) {
515
514
  * the method runs is the run's verdict, not preparation's. The `is_valid: false`
516
515
  * arm means the closure does not load, which is a preparation failure. So is a
517
516
  * selection the route refuses — an unknown `pipe_ref`, no entry pipe, several —
518
- * which it answers with a `422` typed by one of {@link PIPE_SELECTION_ERROR_TYPES};
519
- * every other failure is re-thrown unchanged. An answer with no boolean `is_valid`
520
- * is neither arm, so it fails the preparation with a `runtime` verdict, as an
517
+ * which it answers with a problem of one of {@link PIPE_SELECTION_CODES}; every
518
+ * other failure is re-thrown unchanged. An answer with no boolean `is_valid` is
519
+ * neither arm, so it fails the preparation with `client.response_unreadable`, as an
521
520
  * `input_form` that does not describe the selected pipe does, and one whose
522
521
  * descriptor is malformed where the walk reads it.
523
522
  */
@@ -528,12 +527,11 @@ async function fetchSignature(client, selector, pipeRef, signal) {
528
527
  result = await client.pipeIo(request, signal === undefined ? {} : { signal });
529
528
  }
530
529
  catch (error) {
531
- if (error instanceof ApiResponseError &&
532
- error.status === 422 &&
533
- error.errorType !== undefined &&
534
- PIPE_SELECTION_ERROR_TYPES.has(error.errorType)) {
535
- const detail = error.serverMessage ?? error.message;
536
- throw new InputPreparationError(`Cannot prepare inputs: ${detail}`, { cause: error });
530
+ if (error instanceof ApiResponseError && PIPE_SELECTION_CODES.has(error.code)) {
531
+ throw new InputPreparationError(`Cannot prepare inputs: ${error.problem.detail}`, {
532
+ cause: error,
533
+ problem: error.problem,
534
+ });
537
535
  }
538
536
  throw error;
539
537
  }
@@ -542,27 +540,26 @@ async function fetchSignature(client, selector, pipeRef, signal) {
542
540
  // An answer with no boolean discriminant is neither arm: the route broke its own contract,
543
541
  // which no change to the request fixes.
544
542
  throw new InputPreparationError("Cannot prepare inputs: the pipe I/O answer carries no boolean `is_valid`, so it says " +
545
- "neither that the method loaded nor why it did not.", { verdict: { errorDomain: "runtime", retryable: false } });
543
+ "neither that the method loaded nor why it did not.", { problem: responseUnreadableProblem(200) });
546
544
  }
547
545
  if (!answer.is_valid) {
548
- const items = answer.validation_errors;
549
- throw new MethodLoadError(`Cannot prepare inputs: the method signature did not resolve — ${invalidReason(answer)}`, {
550
- validationErrors: Array.isArray(items) ? items.filter(isValidationItem) : [],
546
+ const errors = readProblemItems(answer.errors);
547
+ throw new MethodLoadError(`Cannot prepare inputs: the method signature did not resolve — ${invalidReason(answer, errors)}`, {
548
+ errors,
551
549
  serverMessage: typeof answer.message === "string" ? answer.message : undefined,
552
550
  });
553
551
  }
554
552
  return result;
555
553
  }
556
554
  /**
557
- * Why an invalid pipe I/O answer says the closure did not load: its first validation item's
558
- * message, else its own `message`. Each is read only when it has its declared type, since the
559
- * answer is the server's, and an answer carrying neither still fails the preparation.
555
+ * Why an invalid pipe I/O answer says the closure did not load: its first problem item's `detail`,
556
+ * else its own `message`. Each is read only when it has its declared type, since the answer is the
557
+ * server's, and an answer carrying neither still fails the preparation.
560
558
  */
561
- function invalidReason(answer) {
562
- const items = answer.validation_errors;
563
- const first = Array.isArray(items) ? items[0] : undefined;
564
- if (isPlainObject(first) && typeof first.message === "string")
565
- return first.message;
559
+ function invalidReason(answer, errors) {
560
+ const first = errors[0];
561
+ if (first !== undefined)
562
+ return first.detail;
566
563
  return typeof answer.message === "string" ? answer.message : "the answer gives no reason";
567
564
  }
568
565
  /**
@@ -581,7 +578,7 @@ function selectedDescriptor(report) {
581
578
  : "none";
582
579
  // The route broke its own contract, which no change to the request fixes.
583
580
  throw new InputPreparationError(`Cannot prepare inputs: the pipe I/O answer selected ${pipeRef === undefined ? "no pipe" : `"${pipeRef}"`}, ` +
584
- `but its \`input_form\` does not describe it (it describes: ${describedPipes}).`, { verdict: { errorDomain: "runtime", retryable: false } });
581
+ `but its \`input_form\` does not describe it (it describes: ${describedPipes}).`, { problem: responseUnreadableProblem(200) });
585
582
  }
586
583
  return checkedDescriptor(pipeRef, inputForm[pipeRef]);
587
584
  }
@@ -591,7 +588,7 @@ function selectedDescriptor(report) {
591
588
  * its first fault.
592
589
  *
593
590
  * A malformation is the route breaking its contract, at whatever depth and under
594
- * whichever input, so it is refused with a `runtime` verdict, not retryable. It is
591
+ * whichever input, so it is refused with `client.response_unreadable`. It is
595
592
  * never passed through: a node the SDK cannot read may be a file position, and
596
593
  * under the default mode passing it through would send the caller's local path to
597
594
  * the runner verbatim. A `kind` outside {@link WALKED_KINDS} is refused for the
@@ -601,7 +598,7 @@ function checkedDescriptor(pipeRef, entry) {
601
598
  const found = inputFormFault(entry);
602
599
  if (found !== undefined) {
603
600
  throw new InputPreparationError(`Cannot prepare inputs: the pipe I/O answer's \`input_form\` for "${pipeRef}" ` +
604
- `is ${found.message}.`, { verdict: { errorDomain: "runtime", retryable: false } });
601
+ `is ${found.message}.`, { problem: responseUnreadableProblem(200) });
605
602
  }
606
603
  return entry;
607
604
  }
@@ -706,8 +703,8 @@ function described(value) {
706
703
  * `MethodFileTransferError` before the route is asked. A bare or alias-qualified `pipe_ref`, a
707
704
  * closure that does not load, and a selection the route refuses (an unknown
708
705
  * `pipe_ref`, no entry pipe, several) throw {@link InputPreparationError}. So
709
- * does an answer that breaks the route's contract, with a `runtime` verdict that
710
- * is not retryable: no boolean `is_valid`, an `input_form` that does not describe
706
+ * does an answer that breaks the route's contract, with the problem
707
+ * `client.response_unreadable`: no boolean `is_valid`, an `input_form` that does not describe
711
708
  * the selected pipe, or a descriptor that is malformed or names a kind this SDK
712
709
  * does not know, which is checked whole before anything is uploaded. Any other
713
710
  * failure from the route (a malformed selector, an unknown or foreign-org id, no