@joymerrevent/porters-connect 0.15.1 → 0.17.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/CHANGELOG.md CHANGED
@@ -5,6 +5,147 @@
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.17.0] - 2026-09-16
9
+
10
+ **添付ファイルの運び方を決め、出典に無いパラメータを型から外した版**です。**破壊的変更を 2 つ**
11
+ 含みます(添付の本体は `get` でだけ取れる/Phase の Read から `keywords` / `itemstate` が消える)。
12
+
13
+ ### Added
14
+
15
+ - **`t.attachment.searchAll()`** — 200 件を超える添付を、`start` を自分で回さずに順に見られます
16
+ ([ADR-0075][adr75])。ほかの 15 エンドポイントと同じ語彙になりました。
17
+
18
+ ```ts
19
+ for await (const a of t.attachment.searchAll({
20
+ condition: { "Resource:eq": "17" },
21
+ })) {
22
+ console.log(a.fileName, a.contentType);
23
+ }
24
+ ```
25
+
26
+ 流れるのは**メタデータだけ**なので、全部を歩いてもファイル本体はダウンロードされません。
27
+
28
+ - **`createFetchTransport`** — 既定の transport を公開しました([ADR-0077][adr77])。
29
+ 1 リクエストのタイムアウト(既定 **30 秒**)を変えられます。
30
+
31
+ ```ts
32
+ import {
33
+ PortersClient,
34
+ createFetchTransport,
35
+ } from "@joymerrevent/porters-connect";
36
+
37
+ const porters = new PortersClient({
38
+ host,
39
+ appId,
40
+ appSecret,
41
+ transport: createFetchTransport({ timeoutMs: 120_000 }), // 2 分
42
+ });
43
+ ```
44
+
45
+ この 30 秒は**接続から本文の受信完了まで**で、**1 リクエストごと**に数えます(自動リトライを
46
+ 含めると最悪 `maxRetries + 1` 倍)。レートの待ち時間は含みません。`timeoutMs` は**正の整数**だけを
47
+ 受け、`0`(=即中断で「無制限」ではない)は構築時に `PortersConfigError` で弾きます。
48
+ これまで変えるには `Transport` の自前実装が必要で、`PortersNetworkError` への分類まで
49
+ 書き直すことになっていました。
50
+
51
+ - 公開した型: `AttachmentMetaField` / `AttachmentWalkQuery` / `FetchTransportOptions`。
52
+
53
+ ### Changed
54
+
55
+ - **(破壊的)添付の本体(`content`)は `get` でだけ取れます**([ADR-0075][adr75])。
56
+ `search` / `searchAll` の `field` に `"Content"` は書けません。
57
+
58
+ ```ts
59
+ await t.attachment.search({ field: ["Id", "Content"] });
60
+ // ^^^^^^^^^ 型エラーになります
61
+ const file = await t.attachment.get(900); // 本体はこちら
62
+ ```
63
+
64
+ 1 ページは最大 200 件で、PORTERS は 1 ファイル 10MB まで許します。本体を混ぜた一覧は
65
+ **読める大きさを越えることがあります** — 1 ファイル 2MB 超 × 200 件で V8 の文字列上限
66
+ (536,870,888 文字)に当たり、`RangeError` になって再送しても直りません。しかも Attachment は
67
+ **ファイルサイズを返さない**ので、「何件までなら安全か」を呼び出し側が判断することもできません。
68
+ そこで件数ではなく**メソッド**で分けました。型を外して渡した場合も、送信前に
69
+ `PortersConfigError` で止まります。
70
+
71
+ - **(破壊的)Phase の Read クエリから `keywords` / `itemstate` が消えました**([ADR-0076][adr76])。
72
+ PORTERS の `Phase - Read` はこの 2 つを Input Variables に挙げていません。Read 記事 17 本を
73
+ 数えると、共通語彙のデータ系 11 本は両方を載せ、**Phase / Attachment / マスタ 4 種は 1 本も
74
+ 載せていません**(11/11 対 0/6)ので、記事側の省略ではなく**エンドポイントごとに取るものが
75
+ 違う**と読めます。出典に無いパラメータは、無視されるのではなく **Read 全体を失敗させうる**
76
+ (Result Code 100 / 102)ため、型の側で閉じました。
77
+
78
+ ```ts
79
+ await t.phase.of("client").search({ keywords: ["山田"] });
80
+ // ^^^^^^^^ 型エラーになります
81
+ ```
82
+
83
+ **実行時は変えていません**。cast すれば今までどおり送られます(契約環境で「実は受け付ける」と
84
+ 分かったときに確かめられるように残しました)。
85
+
86
+ - **エンドポイント × 機能のマトリクスを起こしました**。PORTERS が取るものとライブラリが送るものを
87
+ 表に並べ、**reference ↔ 表 ↔ 実装を両方向で突き合わせる検査**(94 件)を足しています。
88
+ ずれているセルには必ず根拠(ADR / ライブ検証)が要る形で、今回の 2 つの破壊的変更も
89
+ この表から出てきました。パッケージの中身は変わりません。
90
+
91
+ - **既定 30 秒のタイムアウトをドキュメントに明文化しました**([上限][limits]・[失敗の扱い][failures])。
92
+ これまでどこにも書かれていませんでした。パッケージの中身は変わりません。
93
+
94
+ ## [0.16.0] - 2026-09-15
95
+
96
+ **カスタム項目を「宣言してから使う」に揃えた版**です。**破壊的変更**(`field` が未宣言の
97
+ `U_` / `A_` を受け付けなくなります)を含みます。逃げ道として `rawValue` を用意しました。
98
+
99
+ ### Added
100
+
101
+ - **`rawValue`** — 宣言していない項目の値を、レコードから**そのまま**読み出します([ADR-0074][adr74])。
102
+
103
+ ```ts
104
+ import { rawValue } from "@joymerrevent/porters-connect";
105
+ import type { CandidateSearchQuery } from "@joymerrevent/porters-connect";
106
+
107
+ const page = await t.candidate.search({
108
+ field: ["P_Name", "U_memo"] as CandidateSearchQuery["field"],
109
+ });
110
+ const memo = rawValue(page.items[0], "U_memo"); // string | null | undefined
111
+ ```
112
+
113
+ 応答に無ければ `undefined`、スカラでなければ(`Option` / `User` / `Image` の入れ子)`null`、
114
+ あれば生の文字列を返します。**変換はしません**ので、日時は PORTERS の書式
115
+ (`2026/09/10 12:00:00`)のままです。宣言できる項目は宣言してください — こちらは逃げ道です。
116
+
117
+ ### Changed
118
+
119
+ - **(破壊的)`field` が未宣言のカスタム項目を受け付けなくなりました**([ADR-0074][adr74])。
120
+ カスタム項目を使う入口は 4 つ(`field` / `condition` / `order` / 書き込み)あり、**`field` だけが
121
+ 宣言なしの `U_` / `A_` を受けていました**。しかも受け取り側の型には出ないため、
122
+ **要求はできるのに読めない**という非対称が残っていました。
123
+
124
+ ```ts
125
+ await t.candidate.search({ field: ["U_memo"] });
126
+ // ^^^^^^^^ 型エラーになります
127
+ ```
128
+
129
+ 直し方は**宣言**です(`defineFields`)。項目 1 つなら 1 行で済み、テナントの項目からは
130
+ `generateFieldDecls` で生成できます。宣言すると綴りが検査され、値も宣言した Data Type で
131
+ 変換されます。**実行時の挙動は変えていません** — 型を外せば送れますし、応答に知らない項目が
132
+ 混ざっても落ちません。宣言せずに触る必要があるときは、上記の `rawValue` を使ってください。
133
+
134
+ - **(型のみ)宣言の違うクライアントを関数に渡せなくなりました**。`TenantScope<typeof fields>` は
135
+ その宣言のスコープだけを受け取ります。以前は型が通ってしまい、`number` と型が言う値に生の
136
+ 文字列が入ることがありました。どの宣言でも受ける関数は `TenantScope<DeclaredCatalogs>` と
137
+ 書けます(これまでどおり)。
138
+
139
+ - **使い方ドキュメントで、読み取りと書き込みの非対称を揃えました**。`Option` / `User` /
140
+ `System[Reference]` / `Image` / `Link` は Read と Write で形が違うため、同じ項目の往復に
141
+ 読み替えが要ります。ガイドと[正典(PORTERS API の事実)][ref]の双方を、出典に突き合わせて
142
+ 埋めました。パッケージの中身は変わりません。
143
+
144
+ - **`src/fields` が品質ゲートの対象に戻りました**([RV-44][rv44])。カバレッジとミューテーションの
145
+ 除外リストに、プレースホルダだった頃の `src/fields/**` が残っていました。宣言 DSL と
146
+ テナントカタログの照合はすでに実ロジックなので、計測対象に戻し、見つかった抜けを埋めています。
147
+ こちらもパッケージの中身は変わりません。
148
+
8
149
  ## [0.15.1] - 2026-09-13
9
150
 
10
151
  **開発・CI まわりだけの版**です。**公開されるパッケージの中身(`dist`)は 0.15.0 と同一**で、
@@ -737,14 +878,24 @@
737
878
  [adr63]: docs/adr/0063-idempotency-guard-scope.md
738
879
  [rv22]: docs/reviews/rv/0022-ratelimit-create-no-retry.md
739
880
  [rv32]: docs/reviews/rv/0032-searchall-query-mutation.md
881
+ [rv44]: docs/reviews/rv/0044-fields-excluded-from-coverage.md
740
882
  [write-constraints]: docs/usage/concepts/limits.md
741
883
  [adr68]: docs/adr/0068-api-reference-tooling.md
742
884
  [adr69]: docs/adr/0069-tenant-field-catalog-tooling.md
743
885
  [adr73]: docs/adr/0073-throttle-sharing.md
886
+ [adr74]: docs/adr/0074-custom-field-declaration-required.md
887
+ [adr75]: docs/adr/0075-attachment-search-all.md
888
+ [adr76]: docs/adr/0076-phase-read-query-surface.md
889
+ [adr77]: docs/adr/0077-fetch-transport-timeout.md
890
+ [limits]: docs/usage/concepts/limits.md
891
+ [failures]: docs/usage/howto/handle-failures.md
744
892
  [lv]: docs/live-verification.md
893
+ [ref]: docs/usage/reference/README.md
745
894
  [kac]: https://keepachangelog.com/en/1.1.0/
746
895
  [semver]: https://semver.org/
747
- [unreleased]: https://github.com/Joymerrevent/porters-connect/compare/v0.15.1...HEAD
896
+ [unreleased]: https://github.com/Joymerrevent/porters-connect/compare/v0.16.0...HEAD
897
+ [0.17.0]: https://github.com/Joymerrevent/porters-connect/compare/v0.16.0...v0.17.0
898
+ [0.16.0]: https://github.com/Joymerrevent/porters-connect/compare/v0.15.1...v0.16.0
748
899
  [0.15.1]: https://github.com/Joymerrevent/porters-connect/compare/v0.15.0...v0.15.1
749
900
  [0.15.0]: https://github.com/Joymerrevent/porters-connect/compare/v0.14.0...v0.15.0
750
901
  [0.14.0]: https://github.com/Joymerrevent/porters-connect/compare/v0.13.0...v0.14.0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @joymerrevent/porters-connect
2
2
 
3
- [![npm version][npm-badge]][npm] [![License: MIT][mit-badge]][mit] ![Node >= 20][node-badge] [![OpenSSF Scorecard][scorecard-badge]][scorecard]
3
+ [![npm version][npm-badge]][npm] [![License: MIT][mit-badge]][mit] ![Node >= 20][node-badge] [![OpenSSF Scorecard][scorecard-badge]][scorecard] [![OpenSSF Best Practices][bp-badge]][bp]
4
4
 
5
5
  PORTERS Connect API(旧 HRBC)を **TypeScript から型安全・簡単に**扱うための、
6
6
  [Joymerrevent(ジョイメリベント)][joymerrevent] 製の **非公式(unofficial)** ラッパーです。
@@ -147,6 +147,8 @@ console.log(page.total, page.items[0]?.P_Name);
147
147
  [node-badge]: https://img.shields.io/badge/node-%3E%3D20-brightgreen.svg
148
148
  [scorecard]: https://scorecard.dev/viewer/?uri=github.com/Joymerrevent/porters-connect
149
149
  [scorecard-badge]: https://api.scorecard.dev/projects/github.com/Joymerrevent/porters-connect/badge
150
+ [bp]: https://www.bestpractices.dev/projects/14611
151
+ [bp-badge]: https://www.bestpractices.dev/projects/14611/badge
150
152
  [joymerrevent]: https://github.com/Joymerrevent
151
153
  [contributing]: ./CONTRIBUTING.md
152
154
  [security]: ./SECURITY.md
package/dist/index.d.ts CHANGED
@@ -52,6 +52,26 @@ type PartitionId = number;
52
52
  */
53
53
  type Scheme = "https" | "http";
54
54
 
55
+ type FetchTransportOptions = {
56
+ /**
57
+ * How long one request may take, in milliseconds. Default 30,000.
58
+ *
59
+ * **It covers the whole exchange** — connect, send, and reading the response body to the end —
60
+ * because the abort signal is handed to `fetch` itself. A large download therefore hits it even
61
+ * when the headers came back instantly. It is **per request**, so a retried call can take
62
+ * `maxRetries + 1` times this (plus backoff), and it does not include waiting for a throttle
63
+ * slot (throttling sits above the transport).
64
+ *
65
+ * Raise it to read large attachments over a slow link; lower it to fail fast in an interactive
66
+ * tool. Must be a positive integer — `0` would abort every request immediately, which reads
67
+ * like "no timeout" and is not (ADR-0077).
68
+ */
69
+ timeoutMs?: number;
70
+ /** Injectable fetch (tests / custom dispatcher). Default global fetch. */
71
+ fetchImpl?: typeof fetch;
72
+ };
73
+ declare const createFetchTransport: (opts?: FetchTransportOptions) => Transport;
74
+
55
75
  /** A mock reply: an XML body string (HTTP 200), or an explicit status + body. */
56
76
  type MockReply = string | {
57
77
  status?: number;
@@ -268,13 +288,34 @@ type FieldCatalog = Record<string, DataType | null>;
268
288
  type EmptyCatalog = Record<never, never>;
269
289
  /**
270
290
  * A decoded record: every known field, each `DecodedValue | null`, and **optional** because a
271
- * field not named in `field` is simply absent (SD-3 "simple" type — ADR-0005/0019). Custom
272
- * `U_`/`A_` aliases are not in the catalog, so they are not typed here (access via a cast until
273
- * the declaration DSL lands — ADR-0005 SD-2); at runtime they still pass through as raw values.
291
+ * field not named in `field` is simply absent (SD-3 "simple" type — ADR-0005/0019). An alias the
292
+ * catalog does not know is not typed here — declare it with `defineFields` (ADR-0023) to get it
293
+ * typed and converted. At runtime such a field still passes through as a raw value; read it with
294
+ * {@link rawValue} (ADR-0074 D2).
274
295
  */
275
296
  type ReadRecord<F extends FieldCatalog> = {
276
297
  [K in keyof F]?: DecodedValue<F[K]> | null;
277
298
  };
299
+ /**
300
+ * Read a field the catalog does not know (ADR-0074 D2) — the named escape hatch for a value that
301
+ * arrived without a declaration: through a cast in `field`, inside an expanded reference record,
302
+ * or because PORTERS returned a field that was not asked for.
303
+ *
304
+ * Returns what the record actually holds, unconverted:
305
+ *
306
+ * - `undefined` — the alias is not on the record (it was never returned)
307
+ * - `null` — it is there but not a scalar (PORTERS sends a nested node for Option / User / Image)
308
+ * - `string` — the raw text, exactly as PORTERS sent it
309
+ *
310
+ * **No conversion happens.** A date comes back in PORTERS' own format (`2026/09/10 12:00:00`), not
311
+ * ISO 8601, and a number comes back as text. Declare the field with `defineFields` to get the
312
+ * converted, typed value instead — this is the escape hatch, not the normal path.
313
+ *
314
+ * @example
315
+ * const page = await t.candidate.search({ field: ["P_Name"] });
316
+ * const memo = rawValue(page.items[0], "U_memo"); // string | null | undefined
317
+ */
318
+ declare const rawValue: (record: unknown, alias: string) => string | null | undefined;
278
319
  /**
279
320
  * A page of decoded records: the standard Read envelope (Total / Count / Start) around whatever
280
321
  * the item decoder produced. Parametrised by the *record* rather than the catalog because a read
@@ -288,16 +329,21 @@ type ResourcePageOf<T> = {
288
329
  };
289
330
  type ResourcePage<F extends FieldCatalog> = ResourcePageOf<ReadRecord<F>>;
290
331
  /**
291
- * What a Read `field` entry may name (ADR-0059): a catalogued alias — every standard `P_` field
292
- * plus the custom fields declared with `defineFields` (ADR-0023) — or an undeclared tenant custom
293
- * field, admitted by the `U_`/`A_` naming rule `defineFields` already enforces at runtime.
332
+ * What a Read `field` entry may name (ADR-0059 / ADR-0074 D1): a **catalogued** alias — every
333
+ * standard `P_` field plus the custom fields declared with `defineFields` (ADR-0023). An
334
+ * undeclared `U_`/`A_` alias is **not** accepted: `condition`, `order` and the Write inputs have
335
+ * always required a declaration, and ADR-0074 D1 brings `field` in line, so custom fields follow
336
+ * one rule — declare, then use.
294
337
  *
295
338
  * Aliases are **bare**: the resource's prefix (`Person.` for Candidate) is a constant the
296
339
  * descriptor knows, so the library adds it. That makes `condition` / `order` / `field` one
297
340
  * vocabulary and turns a typo (`P_Nmae`) or a hand-written prefix into a compile error instead of
298
341
  * a request that quietly returns nothing.
342
+ *
343
+ * The runtime stays permissive (ADR-0074): an alias that arrives through a cast is still sent, and
344
+ * a response field the catalog does not know still decodes — read it with {@link rawValue}.
299
345
  */
300
- type ReadFieldAlias<F extends FieldCatalog> = (keyof F & string) | `U_${string}` | `A_${string}`;
346
+ type ReadFieldAlias<F extends FieldCatalog> = keyof F & string;
301
347
 
302
348
  /**
303
349
  * The resource a `System[Reference]` field points at, as far as expansion needs it: its alias
@@ -612,13 +658,38 @@ type UpdateInput<F extends FieldCatalog> = {
612
658
  * {@link EmptyReferences}. With no keys, `ImageReadRecord` collapses back to the record it wrapped.
613
659
  */
614
660
  type EmptyImages = Record<never, never>;
615
- type Resource<F extends FieldCatalog, Req extends keyof F, R extends ReferenceMap = EmptyReferences> = {
616
- search<const E extends Expand<R> = EmptyReferences, const I extends ImageOption<F> = EmptyImages>(query?: SearchQuery<F, R> & {
661
+ /**
662
+ * A query with `K` taken out — and **kept out**. `Omit` alone only stops a fresh object literal
663
+ * (excess-property checking); a variable that happens to carry the key still assigns. Re-declaring
664
+ * each removed key as `?: never` closes that hole, so `search(query)` fails whichever way the
665
+ * object was built. The runtime is unaffected: the key can still arrive through a cast (ADR-0074).
666
+ *
667
+ * Only the **endpoint-level** exclusions (`Unsupported`) get this treatment. `searchAll` keeps a
668
+ * plain `Omit` for `count` / `start`: those are not "PORTERS does not take this", they are
669
+ * "the walk decides them", and tightening that is a different decision from ADR-0076.
670
+ */
671
+ type WithoutQueryKeys<Q, K extends keyof Q> = Omit<Q, K> & {
672
+ [P in K]?: never;
673
+ };
674
+ type Resource<F extends FieldCatalog, Req extends keyof F, R extends ReferenceMap = EmptyReferences,
675
+ /**
676
+ * Query keys **this endpoint does not take** (ADR-0076). The common Read vocabulary is not
677
+ * universal: PORTERS lists `keywords` / `itemstate` for the 11 common data resources and for
678
+ * none of the others, so a resource on this factory can say which of them its own endpoint
679
+ * leaves out. `never` — the default — means "takes the whole vocabulary".
680
+ *
681
+ * Sending a parameter the endpoint does not list can fail the *whole* Read (Result Code 100 /
682
+ * 102), so the safe side is not to offer it. The runtime stays permissive (ADR-0074): a key
683
+ * forced in through a cast is still sent, which is how a live contract can test whether
684
+ * PORTERS accepts it at all.
685
+ */
686
+ Unsupported extends keyof SearchQuery<F, R> = never> = {
687
+ search<const E extends Expand<R> = EmptyReferences, const I extends ImageOption<F> = EmptyImages>(query?: WithoutQueryKeys<SearchQuery<F, R>, Unsupported> & {
617
688
  expand?: E;
618
689
  image?: I;
619
690
  }): Promise<ResourcePageOf<ImageReadRecord<ExpandedReadRecord<F, R, E>, I>>>;
620
691
  /** Auto-paginating search: yields every matching record (200 per page). */
621
- searchAll<const E extends Expand<R> = EmptyReferences, const I extends ImageOption<F> = EmptyImages>(query?: Omit<SearchQuery<F, R>, "count" | "start"> & {
692
+ searchAll<const E extends Expand<R> = EmptyReferences, const I extends ImageOption<F> = EmptyImages>(query?: Omit<WithoutQueryKeys<SearchQuery<F, R>, Unsupported>, "count" | "start"> & {
622
693
  expand?: E;
623
694
  image?: I;
624
695
  }): AsyncIterable<ImageReadRecord<ExpandedReadRecord<F, R, E>, I>>;
@@ -1708,13 +1779,23 @@ declare const REQUIRED_ON_CREATE$2: readonly ["ResourceId"];
1708
1779
  /** A decoded Phase entry: known aliases, each requested field `value | null`. */
1709
1780
  type Phase = ReadRecord<typeof FIELDS$6>;
1710
1781
  type PhasePage = ResourcePage<typeof FIELDS$6>;
1711
- type PhaseSearchQuery = SearchQuery<typeof FIELDS$6>;
1782
+ type PhaseUnsupportedQuery = "keywords" | "itemstate";
1783
+ /**
1784
+ * Phase's Read query: the common vocabulary **minus `keywords` / `itemstate`**, which
1785
+ * `Phase - Read` does not list (ADR-0076).
1786
+ */
1787
+ type PhaseSearchQuery = Omit<SearchQuery<typeof FIELDS$6>, PhaseUnsupportedQuery> & {
1788
+ [K in PhaseUnsupportedQuery]?: never;
1789
+ };
1712
1790
  /** Fields for `create`: `ResourceId` required (`Id` and `Resource` are supplied for you). */
1713
1791
  type PhaseCreateInput = CreateInput<typeof FIELDS$6, (typeof REQUIRED_ON_CREATE$2)[number]>;
1714
1792
  /** Fields for `update`: all optional (`null` omits, `""` clears a text field). */
1715
1793
  type PhaseUpdateInput = UpdateInput<typeof FIELDS$6>;
1716
- /** The Phase accessor for one bound resource — same shape as every other resource. */
1717
- type PhaseResource = Resource<typeof FIELDS$6, (typeof REQUIRED_ON_CREATE$2)[number]>;
1794
+ /**
1795
+ * The Phase accessor for one bound resource — the same shape as every other resource, except that
1796
+ * `search` / `searchAll` do not take `keywords` / `itemstate` (ADR-0076).
1797
+ */
1798
+ type PhaseResource = Resource<typeof FIELDS$6, (typeof REQUIRED_ON_CREATE$2)[number], EmptyReferences, PhaseUnsupportedQuery>;
1718
1799
  /**
1719
1800
  * Phase is reached through the resource whose history you want (ADR-0061 案2a):
1720
1801
  *
@@ -2162,6 +2243,11 @@ type ResumeUpdateInput = UpdateInput<typeof FIELDS$4>;
2162
2243
  /** The Resume accessor; `C` is the declared custom-field catalog merged on (ADR-0023). */
2163
2244
  type ResumeResource<C extends FieldCatalog = EmptyCatalog> = Resource<typeof FIELDS$4 & C, (typeof REQUIRED_ON_CREATE)[number], typeof REFERENCES>;
2164
2245
 
2246
+ /**
2247
+ * Every Attachment field **except** the body: what a listing may ask for (ADR-0075).
2248
+ * These are the aliases `search` / `searchAll` accept.
2249
+ */
2250
+ type AttachmentMetaField = "Id" | "Resource" | "ResourceId" | "ContentType" | "FileName";
2165
2251
  /** A decoded Attachment. A field is `null` unless it was returned (see `field`). */
2166
2252
  type Attachment = {
2167
2253
  id: number | null;
@@ -2182,16 +2268,19 @@ type AttachmentPage = {
2182
2268
  };
2183
2269
  type AttachmentSearchQuery = {
2184
2270
  /**
2185
- * Output fields. **Omit** to fetch metadata by default (Id / Resource / ResourceId /
2186
- * ContentType / FileName) — the large Base64 `Content` is excluded so listing doesn't download
2187
- * every file body (ADR-0020); request `["Content", …]` or use `get()` for the body. Pass `[]`
2188
- * for the API-native primary-key-only response. A non-empty list is sent verbatim.
2271
+ * Output fields — **metadata only**. Omit for all five (Id / Resource / ResourceId /
2272
+ * ContentType / FileName), or pass `[]` for the API-native primary-key-only response.
2273
+ *
2274
+ * The file body is **not** on this list: a listing never carries it, whatever the count
2275
+ * (ADR-0075). Read a body with {@link AttachmentResource.get}, one record at a time.
2189
2276
  */
2190
- field?: string[];
2277
+ field?: AttachmentMetaField[];
2191
2278
  condition?: Record<string, string>;
2192
2279
  count?: number;
2193
2280
  start?: number;
2194
2281
  };
2282
+ /** A walking Read: `count` / `start` are the walk's to decide. */
2283
+ type AttachmentWalkQuery = Omit<AttachmentSearchQuery, "count" | "start">;
2195
2284
  /** Fields for creating an Attachment. `content` is the Base64 file body. */
2196
2285
  type AttachmentCreate = {
2197
2286
  resource: number;
@@ -2208,6 +2297,16 @@ type AttachmentUpdate = {
2208
2297
  };
2209
2298
  type AttachmentResource = {
2210
2299
  search(query?: AttachmentSearchQuery): Promise<AttachmentPage>;
2300
+ /**
2301
+ * Auto-paginating search: yields every matching attachment (200 per page). Metadata only —
2302
+ * the body stays behind {@link AttachmentResource.get} (ADR-0075), so walking every attachment
2303
+ * in a partition never drags the files along with it.
2304
+ */
2305
+ searchAll(query?: AttachmentWalkQuery): AsyncIterable<Attachment>;
2306
+ /**
2307
+ * Read one attachment **with its body** (`content`). This is the only method that carries it:
2308
+ * one record at a time is a size PORTERS' own 10MB-per-file limit keeps readable (ADR-0075).
2309
+ */
2211
2310
  get(id: number): Promise<Attachment | undefined>;
2212
2311
  /** Create an Attachment; resolves to the newly assigned id. */
2213
2312
  create(input: AttachmentCreate): Promise<number>;
@@ -2801,4 +2900,4 @@ declare const bytesToBase64: (bytes: Uint8Array) => string;
2801
2900
  /** Decode a Base64 string back to raw bytes. */
2802
2901
  declare const base64ToBytes: (b64: string) => Uint8Array;
2803
2902
 
2804
- export { type Activity, type ActivityCreateInput, type ActivityPage, type ActivityResource, type ActivitySearchQuery, type ActivityUpdateInput, type Attachment, type AttachmentCreate, type AttachmentPage, type AttachmentResource, type AttachmentSearchQuery, type AttachmentUpdate, type AuthApi, type AuthorizationUrlOptions, type BulkWriteResult, type BulkWriteResultItem, type Candidate, type CandidateCreateInput, type CandidatePage, type CandidateResource, type CandidateSearchQuery, type CandidateUpdateInput, type Client, type ClientCreateInput, type ClientPage, type ClientResource, type ClientSearchQuery, type ClientUpdateInput, type Condition, type Contact, type ContactCreateInput, type ContactPage, type ContactResource, type ContactSearchQuery, type ContactUpdateInput, type Contract, type ContractCreateInput, type ContractPage, type ContractResource, type ContractSearchQuery, type ContractUpdateInput, type CustomDataType, type CustomFieldResource, type CustomFor, type DeclaredCatalogs, type DefinedFields, type DepartmentRef, type ErrorCategory, type Expand, type ExpandedReadRecord, type Field, type FieldBuilder, type FieldCatalogSource, type FieldDecls, type FieldDef, type FieldPage, type FieldResource, type FieldSearchQuery, type FieldTypeMismatch, type FieldValue, type FieldVerification, type GenerateFieldDeclsOptions, type GetAccessTokenOptions, type ImageContentType, type ImageOption, type ImageReadRecord, type ImageSelectedValue, type ImageSubField, type ImageValue, type ImageWriteValue, type ItemState, type Job, type JobCreateInput, type JobPage, type JobResource, type JobSearchQuery, type JobUpdateInput, type LinkValue, type MissingField, type MockHandler, type MockReply, type MockTransportOptions, type Opportunity, type OpportunityCreateInput, type OpportunityPage, type OpportunityResource, type OpportunitySearchQuery, type OpportunityUpdateInput, type Option, type OptionResource, type OptionSearchQuery, type Order, type Partition, type PartitionId, type PartitionPage, type PartitionResource, type PartitionSearchQuery, type Phase, type PhaseAccessor, type PhaseCreateInput, type PhasePage, type PhaseResource, type PhaseSearchQuery, type PhaseUpdateInput, PortersAuthError, PortersClient, type PortersClientOptions, PortersConfigError, PortersError, type PortersErrorContext, type PortersErrorOptions, PortersNetworkError, PortersResourceError, type Process, type ProcessCreateInput, type ProcessPage, type ProcessResource, type ProcessSearchQuery, type ProcessUpdateInput, type ReadCustomCatalogOptions, type ReadFieldAlias, type Recruiter, type RecruiterCreateInput, type RecruiterPage, type RecruiterResource, type RecruiterSearchQuery, type RecruiterUpdateInput, type ReferenceMap, type ReferenceRecord, type ResourceName, type ResourcePageOf, type ResourceType, type Resume, type ResumeCreateInput, type ResumePage, type ResumeResource, type ResumeSearchQuery, type ResumeUpdateInput, type RevokeUrlOptions, type Sales, type SalesCreateInput, type SalesPage, type SalesResource, type SalesSearchQuery, type SalesUpdateInput, type Scheme, type Scope, type SearchQuery, type StoredTokens, type TenantCustomCatalog, type TenantScope, type Throttle, type ThrottleOptions, type TokenProvider, type TokenStore, type Transport, type TransportRequest, type TransportResponse, type UndeclarableField, type UndeclarableReason, type UndeclarableTenantField, type UndeclaredField, type UnverifiableResource, type User, type UserPage, type UserRef, type UserResource, type UserSearchQuery, type VerifyFieldsOptions, assertFieldsMatch, base64ToBytes, bytesToBase64, createMockTransport, createThrottle, defineFields, generateFieldDecls, readCustomCatalog, verifyFields };
2903
+ export { type Activity, type ActivityCreateInput, type ActivityPage, type ActivityResource, type ActivitySearchQuery, type ActivityUpdateInput, type Attachment, type AttachmentCreate, type AttachmentMetaField, type AttachmentPage, type AttachmentResource, type AttachmentSearchQuery, type AttachmentUpdate, type AttachmentWalkQuery, type AuthApi, type AuthorizationUrlOptions, type BulkWriteResult, type BulkWriteResultItem, type Candidate, type CandidateCreateInput, type CandidatePage, type CandidateResource, type CandidateSearchQuery, type CandidateUpdateInput, type Client, type ClientCreateInput, type ClientPage, type ClientResource, type ClientSearchQuery, type ClientUpdateInput, type Condition, type Contact, type ContactCreateInput, type ContactPage, type ContactResource, type ContactSearchQuery, type ContactUpdateInput, type Contract, type ContractCreateInput, type ContractPage, type ContractResource, type ContractSearchQuery, type ContractUpdateInput, type CustomDataType, type CustomFieldResource, type CustomFor, type DeclaredCatalogs, type DefinedFields, type DepartmentRef, type ErrorCategory, type Expand, type ExpandedReadRecord, type FetchTransportOptions, type Field, type FieldBuilder, type FieldCatalogSource, type FieldDecls, type FieldDef, type FieldPage, type FieldResource, type FieldSearchQuery, type FieldTypeMismatch, type FieldValue, type FieldVerification, type GenerateFieldDeclsOptions, type GetAccessTokenOptions, type ImageContentType, type ImageOption, type ImageReadRecord, type ImageSelectedValue, type ImageSubField, type ImageValue, type ImageWriteValue, type ItemState, type Job, type JobCreateInput, type JobPage, type JobResource, type JobSearchQuery, type JobUpdateInput, type LinkValue, type MissingField, type MockHandler, type MockReply, type MockTransportOptions, type Opportunity, type OpportunityCreateInput, type OpportunityPage, type OpportunityResource, type OpportunitySearchQuery, type OpportunityUpdateInput, type Option, type OptionResource, type OptionSearchQuery, type Order, type Partition, type PartitionId, type PartitionPage, type PartitionResource, type PartitionSearchQuery, type Phase, type PhaseAccessor, type PhaseCreateInput, type PhasePage, type PhaseResource, type PhaseSearchQuery, type PhaseUpdateInput, PortersAuthError, PortersClient, type PortersClientOptions, PortersConfigError, PortersError, type PortersErrorContext, type PortersErrorOptions, PortersNetworkError, PortersResourceError, type Process, type ProcessCreateInput, type ProcessPage, type ProcessResource, type ProcessSearchQuery, type ProcessUpdateInput, type ReadCustomCatalogOptions, type ReadFieldAlias, type Recruiter, type RecruiterCreateInput, type RecruiterPage, type RecruiterResource, type RecruiterSearchQuery, type RecruiterUpdateInput, type ReferenceMap, type ReferenceRecord, type ResourceName, type ResourcePageOf, type ResourceType, type Resume, type ResumeCreateInput, type ResumePage, type ResumeResource, type ResumeSearchQuery, type ResumeUpdateInput, type RevokeUrlOptions, type Sales, type SalesCreateInput, type SalesPage, type SalesResource, type SalesSearchQuery, type SalesUpdateInput, type Scheme, type Scope, type SearchQuery, type StoredTokens, type TenantCustomCatalog, type TenantScope, type Throttle, type ThrottleOptions, type TokenProvider, type TokenStore, type Transport, type TransportRequest, type TransportResponse, type UndeclarableField, type UndeclarableReason, type UndeclarableTenantField, type UndeclaredField, type UnverifiableResource, type User, type UserPage, type UserRef, type UserResource, type UserSearchQuery, type VerifyFieldsOptions, assertFieldsMatch, base64ToBytes, bytesToBase64, createFetchTransport, createMockTransport, createThrottle, defineFields, generateFieldDecls, rawValue, readCustomCatalog, verifyFields };