@joymerrevent/porters-connect 0.8.0 → 0.10.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 +119 -1
- package/README.md +66 -34
- package/dist/index.d.ts +60 -41
- package/dist/index.js +73 -53
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,113 @@
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.10.0] - 2026-08-22
|
|
9
|
+
|
|
10
|
+
**partition の束ね方を 1 つに絞り、削除済みレコードを判別できるようにした版**です。
|
|
11
|
+
`PortersClient` から `partition` を外した**破壊的変更**を含みます(移行は下記)。
|
|
12
|
+
併せて、削除済みを読めるのに**どれが削除済みか分からなかった**穴を塞ぎました。
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **削除フラグ `P_Deleted`**([ADR-0056][adr56])。データ系 5 リソース(Candidate / Job / Client /
|
|
17
|
+
Process / Resume)の公開型に加わり、`itemstate: "all"` の結果から削除済みを**判別できる**ようになりました。
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
const page = await t.candidate.search({ itemstate: "all" });
|
|
21
|
+
const deleted = page.items.filter((c) => c.P_Deleted === "1");
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- **値は文字列**の `"0"`(生存)/`"1"`(削除済み)です。`number` でも `boolean` でもありません。
|
|
25
|
+
PORTERS がこの項目に **Data Type を与えていない**(reference の Field Type / Data Type 欄がともに「ー」)ため、
|
|
26
|
+
変換の基準がありません。勝手に決めればライブラリの発明になるので、**生の値のまま**返します。
|
|
27
|
+
- **`condition` にも `order` にも指定できず、Write もできません**(PORTERS の制約)。
|
|
28
|
+
いずれも型で表現されているので、書くとコンパイルエラーになります。
|
|
29
|
+
- `field` を省略すれば**自動で要求**されます。自分で `field` を渡すときは
|
|
30
|
+
`"Person.P_Deleted"` のように接頭辞付きで明示してください。
|
|
31
|
+
- 削除 API が無い PORTERS では「削除済みを読む」こと自体が運用手段(同期・監査・復元の判断材料)ですが、
|
|
32
|
+
これまでは**読めるのに解釈できない**状態でした。
|
|
33
|
+
- 応答での出現条件と値域は**実機で未確認**です([live-verification][lv] LV-14)。
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- **(破壊的)`PortersClient` から `partition` を外しました**([ADR-0055][adr55])。
|
|
38
|
+
partition(Company DB)スコープのリソースは **`porters.tenant(id)` 経由でのみ**取得します。
|
|
39
|
+
**単一テナントでも同じ形**です。
|
|
40
|
+
|
|
41
|
+
```diff
|
|
42
|
+
- const porters = new PortersClient({ host, appId, appSecret, partition: 123 });
|
|
43
|
+
- await porters.candidate.search();
|
|
44
|
+
+ const porters = new PortersClient({ host, appId, appSecret });
|
|
45
|
+
+ const t = porters.tenant(123);
|
|
46
|
+
+ await t.candidate.search();
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
- 影響するのは `candidate` / `job` / `client` / `process` / `resume` / `attachment` /
|
|
50
|
+
`user` / `field` / `option` の 9 アクセサ。`t` は以降クライアントのように使えるので、
|
|
51
|
+
**呼び出し側の書き味は変わりません**。
|
|
52
|
+
- **App レベルのものは `porters` 側に残ります**(いずれも `partition` を取りません)—
|
|
53
|
+
`porters.auth.*`(OAuth)と `porters.partition.search()`(partition の発見)。
|
|
54
|
+
- 理由: `partition` 未設定のクライアントは**`partition=0`** を全リクエストに載せていました。
|
|
55
|
+
`0` は PORTERS のドキュメントに存在しない値で、由来は最初の PoC の穴埋めです。実際には
|
|
56
|
+
Result Code **404** を招くため、**設定漏れが「サーバーが 404 を返す」形に化け**ていました。
|
|
57
|
+
`tenant(id)` を唯一の経路にすることで、**「partition を束ね忘れたクライアント」という状態が
|
|
58
|
+
型として存在しなくなります**。実行時ガードを足すのではなく、設計で防ぐ形にしました。
|
|
59
|
+
- **pre-1.0 のため minor**。誤った partition を渡すこと自体は妨げません(404 はサーバーが答えます)。
|
|
60
|
+
|
|
61
|
+
### Fixed
|
|
62
|
+
|
|
63
|
+
- **`itemstate: "existing"` を明示指定したとき、省略せずそのまま送る**ようになりました
|
|
64
|
+
([ADR-0057][adr57])。**返るデータは変わりません** — 変わるのは送信 URL だけです。
|
|
65
|
+
- これまでは `existing` が API の既定であることを理由に、明示指定でも param を省いていました。
|
|
66
|
+
しかし**省略(「既定に委ねる」)と明示(「生存レコードのみが欲しい」)は別の意思表示**です。
|
|
67
|
+
いま結果が同じなのは PORTERS の既定が `existing` だからで、**もし将来この既定が変われば、
|
|
68
|
+
生存のみを求めたコードに削除済みが混ざり始めます**(例外も Result Code も出ないので気づけません)。
|
|
69
|
+
- 生存のみであることが業務上重要なら、省略せず `itemstate: "existing"` と書いてください。
|
|
70
|
+
省略した場合はこれまでどおり API の既定に従います。
|
|
71
|
+
- `itemstate=existing` の実機送信実績はまだありません([live-verification][lv] LV-15)。
|
|
72
|
+
|
|
73
|
+
## [0.9.0] - 2026-08-20
|
|
74
|
+
|
|
75
|
+
**Candidate でメモ・住所詳細が扱えるようになった版**です。標準項目でありながらカタログから漏れていた
|
|
76
|
+
4 項目を追加し、同じ取りこぼしが二度と起きないよう reference との突合を CI で検査するようにしました。
|
|
77
|
+
併せて `defineFields` と Read クエリの**利用ガイドを新設**しています。
|
|
78
|
+
|
|
79
|
+
### Added
|
|
80
|
+
|
|
81
|
+
- **Candidate に標準 4 項目**(`P_Memo`(メモ)/`P_Street`(住所詳細)/`P_Fax`(FAX)/`P_PhaseMemo`(フェーズメモ))。
|
|
82
|
+
カタログが型の真実源([ADR-0019][adr19])なので、これまで **`Candidate` 型にも `CandidateCreateInput` /
|
|
83
|
+
`CandidateUpdateInput` にも現れず**、`candidate.create({ P_Memo: "…" })` は型エラーでした。
|
|
84
|
+
`field` 省略時の既定 field([ADR-0020][adr20])にも含まれないため **Read でも取得されません**でした。
|
|
85
|
+
同じ 4 種は Client には最初から載っており、リソース間で非一貫でした。
|
|
86
|
+
- `field` を省略した Read は**既定 field が 4 つ増えるぶん URL が長くなります**(Candidate で約 100 文字)。
|
|
87
|
+
- **公開ジェネリクスの制約型を export**(`DeclaredCatalogs` / `CustomFor` / `CustomFieldResource`)。
|
|
88
|
+
`PortersClient<C>` / `TenantScope<C>` は公開型なのに制約側が非公開で、
|
|
89
|
+
クライアントを引数に取るヘルパーの型を**名前で書けません**でした(`typeof porters` での回避のみ)。
|
|
90
|
+
型の追加のみで、既存コードへの影響はありません。
|
|
91
|
+
- **利用ガイドを 2 本新設**([ADR-0035][adr35] の型)。
|
|
92
|
+
- [カスタム項目ガイド][custom-fields-guide] — `defineFields` の宣言、**宣言しないとどうなるか**
|
|
93
|
+
(型が付かず `field` 省略時に取得されない)、宣言できる 11 の Data Type、
|
|
94
|
+
Field Read でテナントの項目を調べる手順、複数テナントでの使い分け。
|
|
95
|
+
- [Read クエリ ガイド][read-query-guide] — `field` の 3 通りの意味、**Data Type ごとの演算子一覧**、
|
|
96
|
+
削除済み Read の制約(condition は 3 項目・90 日以内)、送信前に落ちるものの一覧。
|
|
97
|
+
|
|
98
|
+
### Changed
|
|
99
|
+
|
|
100
|
+
- **Read の `count` が範囲外なら送信前に落ちます**。`count` は PORTERS 上 **1〜200**(既定 10)ですが、
|
|
101
|
+
これまで範囲外の値をそのまま送っており、不透明なサーバー応答に倒れていました。
|
|
102
|
+
今後は `PortersConfigError`(`category: "config"` + `searchAll()` を案内する `hint`)で**リクエストの前に**弾きます。
|
|
103
|
+
整数でない値(`1.5` など)も同様です。
|
|
104
|
+
- keywords の 100 文字・itemstate の condition 制限・リクエスト長 ~15000 字と同じ
|
|
105
|
+
「早く・明確に落とす」系列に揃いました。データ系・マスタ系・Attachment の**すべての Read 経路**に効きます。
|
|
106
|
+
- `count` を省略した場合の挙動は変わりません(API 既定に委ねます)。
|
|
107
|
+
|
|
108
|
+
### Fixed
|
|
109
|
+
|
|
110
|
+
- **reference と静的カタログの突合を CI で検査**するようになりました。値を持つ標準項目の取りこぼし・
|
|
111
|
+
カタログ側の幻の項目・Field Type → Data Type の取り違えを検出します。
|
|
112
|
+
上記 Candidate の欠落は **0.1.0 から 12 版・563 テストをすべて素通り**していたため、
|
|
113
|
+
人の目ではなく仕組みで守ります。
|
|
114
|
+
|
|
8
115
|
## [0.8.0] - 2026-08-14
|
|
9
116
|
|
|
10
117
|
**「0 件」と「届いていない」を区別できるようにした版**です。HTTP 200 を返す中間装置
|
|
@@ -241,9 +348,20 @@
|
|
|
241
348
|
[adr51]: docs/adr/0051-read-envelope-identification.md
|
|
242
349
|
[adr47]: docs/adr/0047-access-point-scheme.md
|
|
243
350
|
[oauth-guide]: docs/guide/oauth.md
|
|
351
|
+
[adr19]: docs/adr/0019-static-resource-types.md
|
|
352
|
+
[adr20]: docs/adr/0020-read-field-default.md
|
|
353
|
+
[adr35]: docs/adr/0035-usage-documentation-structure.md
|
|
354
|
+
[custom-fields-guide]: docs/guide/custom-fields.md
|
|
355
|
+
[read-query-guide]: docs/guide/read-query.md
|
|
356
|
+
[adr55]: docs/adr/0055-partition-binding-guard.md
|
|
357
|
+
[adr56]: docs/adr/0056-deleted-flag-typing.md
|
|
358
|
+
[adr57]: docs/adr/0057-itemstate-existing-explicit.md
|
|
359
|
+
[lv]: docs/live-verification.md
|
|
244
360
|
[kac]: https://keepachangelog.com/en/1.1.0/
|
|
245
361
|
[semver]: https://semver.org/
|
|
246
|
-
[unreleased]: https://github.com/Joymerrevent/porters-connect/compare/v0.
|
|
362
|
+
[unreleased]: https://github.com/Joymerrevent/porters-connect/compare/v0.10.0...HEAD
|
|
363
|
+
[0.10.0]: https://github.com/Joymerrevent/porters-connect/compare/v0.9.0...v0.10.0
|
|
364
|
+
[0.9.0]: https://github.com/Joymerrevent/porters-connect/compare/v0.8.0...v0.9.0
|
|
247
365
|
[0.8.0]: https://github.com/Joymerrevent/porters-connect/compare/v0.7.0...v0.8.0
|
|
248
366
|
[0.7.0]: https://github.com/Joymerrevent/porters-connect/compare/v0.6.2...v0.7.0
|
|
249
367
|
[0.6.2]: https://github.com/Joymerrevent/porters-connect/compare/v0.6.1...v0.6.2
|
package/README.md
CHANGED
|
@@ -58,15 +58,14 @@ const porters = new PortersClient({
|
|
|
58
58
|
scheme: process.env.PORTERS_SCHEME === "http" ? "http" : undefined,
|
|
59
59
|
appId: process.env.PORTERS_APP_ID!,
|
|
60
60
|
appSecret: process.env.PORTERS_APP_SECRET!,
|
|
61
|
-
partition: 123, // 既定 Partition(Company DB)Id
|
|
62
61
|
});
|
|
63
62
|
|
|
64
|
-
//
|
|
63
|
+
// partition(Company DB)は tenant で一度だけ束ねる。**単一テナントでもこの形**
|
|
64
|
+
// 以降は `t` をクライアントのように使う(client 直下には auth と partition マスタだけ)
|
|
65
65
|
const t = porters.tenant(456);
|
|
66
|
-
await t.candidate.search({ condition: { P_Name: { part: "鈴木" } } }); // partition=456
|
|
67
66
|
|
|
68
67
|
// 検索(最大 200 件/ページ)。condition は項目の Data Type ごとに型付き
|
|
69
|
-
const page = await
|
|
68
|
+
const page = await t.candidate.search({
|
|
70
69
|
condition: { P_Name: { part: "山田" } }, // テキストは part(部分一致)/ full(完全一致)
|
|
71
70
|
order: [{ P_UpdateDate: "desc" }], // 並び順(数値・日時・System のみ)
|
|
72
71
|
count: 50,
|
|
@@ -74,18 +73,21 @@ const page = await porters.candidate.search({
|
|
|
74
73
|
console.log(page.total, page.items.length);
|
|
75
74
|
|
|
76
75
|
// 1 件取得
|
|
77
|
-
const one = await
|
|
76
|
+
const one = await t.candidate.get(10001);
|
|
78
77
|
console.log(one?.P_Name);
|
|
79
78
|
|
|
80
79
|
// 全件を自動ページング(200 件刻み)
|
|
81
|
-
for await (const c of
|
|
80
|
+
for await (const c of t.candidate.searchAll({
|
|
82
81
|
condition: { P_Prefecture: { full: "東京都" } },
|
|
83
82
|
})) {
|
|
84
83
|
// c は 1 件ずつ
|
|
85
84
|
}
|
|
86
85
|
```
|
|
87
86
|
|
|
88
|
-
>
|
|
87
|
+
> **`partition` はクライアントに持たせません**(ADR-0055)。PORTERS は partition スコープの全リクエストで
|
|
88
|
+
> `partition` を要求するので、`tenant(id)` で**明示的に一度だけ**束ねる形にしています。
|
|
89
|
+
> 複数 partition を 1 つの App で扱う場合・テナント別 client・partition の発見は
|
|
90
|
+
> [マルチテナント ガイド][multi-tenancy] にまとめています。
|
|
89
91
|
>
|
|
90
92
|
> 認証情報やホスト名は**コミットしない**でください。`.env.example` を参考に `.env` で渡します。
|
|
91
93
|
|
|
@@ -103,7 +105,6 @@ const porters = new PortersClient({
|
|
|
103
105
|
host: "sandbox.invalid",
|
|
104
106
|
appId: "demo",
|
|
105
107
|
appSecret: "demo",
|
|
106
|
-
partition: 1,
|
|
107
108
|
transport: createMockTransport((req) =>
|
|
108
109
|
req.url.includes("/v1/candidate")
|
|
109
110
|
? `<Candidate Total="1" Count="1" Start="0"><Code>0</Code><Item><Person.P_Id>1</Person.P_Id><Person.P_Name>山田 太郎</Person.P_Name></Item></Candidate>`
|
|
@@ -111,7 +112,7 @@ const porters = new PortersClient({
|
|
|
111
112
|
), // 未モックのリクエストは明示エラー(フェイルセーフ)
|
|
112
113
|
});
|
|
113
114
|
|
|
114
|
-
const page = await
|
|
115
|
+
const page = await t.candidate.search();
|
|
115
116
|
console.log(page.items[0]?.P_Name); // 山田 太郎
|
|
116
117
|
```
|
|
117
118
|
|
|
@@ -141,7 +142,7 @@ const tokenStore: TokenStore = {
|
|
|
141
142
|
/* 破棄 */
|
|
142
143
|
},
|
|
143
144
|
};
|
|
144
|
-
new PortersClient({ host, appId, appSecret,
|
|
145
|
+
new PortersClient({ host, appId, appSecret, tokenStore });
|
|
145
146
|
```
|
|
146
147
|
|
|
147
148
|
`transport`(HTTP 注入)や `auth`(独自 `TokenProvider`)も差し替え可能です。
|
|
@@ -166,14 +167,14 @@ await porters.auth.exchangeAuthorizationCode(code);
|
|
|
166
167
|
|
|
167
168
|
すべてのデータ系リソースは同じ形のアクセサを持ちます。
|
|
168
169
|
|
|
169
|
-
| アクセサ
|
|
170
|
-
|
|
|
171
|
-
| `
|
|
172
|
-
| `
|
|
173
|
-
| `
|
|
174
|
-
| `
|
|
175
|
-
| `
|
|
176
|
-
| `
|
|
170
|
+
| アクセサ | リソース | メソッド |
|
|
171
|
+
| -------------- | ------------ | ---------------------------------------------------- |
|
|
172
|
+
| `t.candidate` | 個人連絡先 | `search` / `searchAll` / `get` / `create` / `update` |
|
|
173
|
+
| `t.job` | JOB | `search` / `searchAll` / `get` / `create` / `update` |
|
|
174
|
+
| `t.client` | 企業 | `search` / `searchAll` / `get` / `create` / `update` |
|
|
175
|
+
| `t.process` | 選考プロセス | `search` / `searchAll` / `get` / `create` / `update` |
|
|
176
|
+
| `t.resume` | レジュメ | `search` / `searchAll` / `get` / `create` / `update` |
|
|
177
|
+
| `t.attachment` | 添付ファイル | `search` / `get` / `create` / `update` |
|
|
177
178
|
|
|
178
179
|
- `search(query?)` → `{ items, total, count, start }`(オフセット式ページング)。
|
|
179
180
|
- `searchAll(query?)` → `AsyncIterable`(200 件刻みで全件 yield)。
|
|
@@ -192,10 +193,39 @@ await porters.auth.exchangeAuthorizationCode(code);
|
|
|
192
193
|
- `order`:並び順。`[{ 項目: "asc" | "desc" }]`(数値・日時・System 型のみ)。
|
|
193
194
|
- `keywords`:テキスト項目のキーワード AND 検索(`string[]`・カンマ込み **100 文字まで**)。
|
|
194
195
|
- `itemstate`:`"existing"`(既定)/ `"deleted"` / `"all"`。削除済みデータの取得。
|
|
195
|
-
- `count`(1–200・既定 10)、`start`(0
|
|
196
|
+
- `count`(1–200・既定 10)、`start`(0 始まり)。**範囲外の `count` は送信前に `PortersConfigError`** で落ちます。
|
|
196
197
|
|
|
197
198
|
> **削除 API はありません**(PORTERS 仕様)。`delete()` メソッドは提供しません。削除済みは `itemstate: "deleted"` で読みます
|
|
198
199
|
> (`condition` は `P_Id` / `P_UpdateDate` / `P_UpdatedBy` に限られ、更新日は 90 日以内)。
|
|
200
|
+
>
|
|
201
|
+
> `field` の 3 通りの意味(省略=全項目 / `[]`=主キーのみ / 明示)、Data Type ごとの演算子一覧、
|
|
202
|
+
> 削除済み Read の制約、送信前に落ちる条件は [Read クエリ ガイド][read-query-guide] にまとめています。
|
|
203
|
+
|
|
204
|
+
### カスタム項目(`U_` / `A_`)
|
|
205
|
+
|
|
206
|
+
テナント固有のカスタム項目は `defineFields` で宣言すると、**読み書きの型に現れ**、Data Type どおりに変換されます。
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
import { PortersClient, defineFields } from "@joymerrevent/porters-connect";
|
|
210
|
+
|
|
211
|
+
const fields = defineFields({
|
|
212
|
+
candidate: (f) => ({ U_score: f.number(), U_source: f.option() }),
|
|
213
|
+
});
|
|
214
|
+
const porters = new PortersClient({
|
|
215
|
+
host,
|
|
216
|
+
appId,
|
|
217
|
+
appSecret,
|
|
218
|
+
fields,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
const one = await t.candidate.get(10001);
|
|
222
|
+
one?.U_score; // number | null | undefined
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
宣言しなくても読み書きはできますが(生の文字列として通ります)、**型が付かず、`field` 省略時に取得もされません**。
|
|
226
|
+
|
|
227
|
+
> 宣言できる型の一覧、テナントの項目を Field Read で調べる方法、複数テナントでの扱いは
|
|
228
|
+
> [カスタム項目 ガイド][custom-fields-guide] にまとめています。
|
|
199
229
|
|
|
200
230
|
### マスタ Read(読み取り専用)
|
|
201
231
|
|
|
@@ -204,25 +234,25 @@ await porters.auth.exchangeAuthorizationCode(code);
|
|
|
204
234
|
| アクセサ | リソース | メソッド | 主なクエリ |
|
|
205
235
|
| ------------------- | ----------------- | ---------------------------------- | ----------------------------------------- |
|
|
206
236
|
| `porters.partition` | Partition | `search` / `searchAll` | `requestType`(1=アクセス可能一覧・既定) |
|
|
207
|
-
| `
|
|
208
|
-
| `
|
|
209
|
-
| `
|
|
237
|
+
| `t.user` | User | `search` / `searchAll` / `current` | `requestType` / `userType` / `field` |
|
|
238
|
+
| `t.field` | Field(項目定義) | `search` / `searchAll` | `resource`(必須)/ `active` |
|
|
239
|
+
| `t.option` | Option(選択肢) | `search` | `alias` / `level` / `enabled` |
|
|
210
240
|
|
|
211
241
|
```ts
|
|
212
242
|
// アクセス可能な Partition(Company DB)を発見
|
|
213
243
|
const partitions = await porters.partition.search();
|
|
214
244
|
|
|
215
245
|
// 現在の API ユーザー(code_direct ではアプリ自身の User)=自己同定
|
|
216
|
-
const me = await
|
|
246
|
+
const me = await t.user.current();
|
|
217
247
|
|
|
218
248
|
// Job リソースの項目定義(U_/A_ カスタム項目を含む)を取得
|
|
219
|
-
const fields = await
|
|
249
|
+
const fields = await t.field.search({ resource: "job" });
|
|
220
250
|
|
|
221
251
|
// 選択肢マスタをフラットな配列で取得(親子は P_ParentId で復元)
|
|
222
|
-
const options = await
|
|
252
|
+
const options = await t.option.search({ alias: "Option.P_Gender" });
|
|
223
253
|
```
|
|
224
254
|
|
|
225
|
-
- `
|
|
255
|
+
- `t.option.search()` は入れ子の選択肢ツリーを**深さ優先でフラット化**して返します(全ノード・`P_ParentId`/`P_Order` で階層復元可)。`start` が無いため `searchAll` はありません。
|
|
226
256
|
- `porters.partition.current()` は提供しません(`request_type=0` は既定の `code_direct` 認証では 403 になるため)。一覧は `search()`(既定 `requestType: 1`)で取得します。
|
|
227
257
|
|
|
228
258
|
## 読み取り値の表現
|
|
@@ -242,7 +272,7 @@ PORTERS の Field Type に応じてデコードされます。
|
|
|
242
272
|
| 空・未設定 | `null` |
|
|
243
273
|
|
|
244
274
|
```ts
|
|
245
|
-
const c = await
|
|
275
|
+
const c = await t.candidate.get(10001);
|
|
246
276
|
c?.P_Id; // number
|
|
247
277
|
c?.P_Name; // string | null
|
|
248
278
|
c?.P_RegistrationDate; // "2026-01-02T03:04:05Z" | null
|
|
@@ -265,17 +295,17 @@ c?.P_Owner; // { P_Id, P_Name, ... } | null
|
|
|
265
295
|
|
|
266
296
|
```ts
|
|
267
297
|
// 作成(新規)
|
|
268
|
-
const newId = await
|
|
298
|
+
const newId = await t.candidate.create({
|
|
269
299
|
P_Owner: 5, // User 項目は id
|
|
270
300
|
P_Name: "鈴木 一郎",
|
|
271
301
|
P_Reading: "すずき いちろう",
|
|
272
302
|
});
|
|
273
303
|
|
|
274
304
|
// 更新
|
|
275
|
-
await
|
|
305
|
+
await t.candidate.update(newId, { P_Mail: "ichiro@example.com" });
|
|
276
306
|
|
|
277
307
|
// 選考プロセス(関連 id を指定)
|
|
278
|
-
await
|
|
308
|
+
await t.process.create({
|
|
279
309
|
P_Owner: 1,
|
|
280
310
|
P_Client: 100,
|
|
281
311
|
P_Recruiter: 200,
|
|
@@ -285,7 +315,7 @@ await porters.process.create({
|
|
|
285
315
|
});
|
|
286
316
|
|
|
287
317
|
// 一括作成/更新(200 件+サイズで自動分割・部分成功を返す)
|
|
288
|
-
const bulk = await
|
|
318
|
+
const bulk = await t.candidate.createMany([
|
|
289
319
|
{ P_Owner: 5, P_Name: "山田 太郎" },
|
|
290
320
|
{ P_Owner: 5, P_Name: "鈴木 花子" },
|
|
291
321
|
]);
|
|
@@ -301,7 +331,7 @@ if (bulk.hasFailures) console.warn(bulk.failed); // per-item は throw せず結
|
|
|
301
331
|
```ts
|
|
302
332
|
import { bytesToBase64 } from "@joymerrevent/porters-connect";
|
|
303
333
|
|
|
304
|
-
const id = await
|
|
334
|
+
const id = await t.attachment.create({
|
|
305
335
|
resource: 17, // 関連リソース種別コード(Resource List 参照)
|
|
306
336
|
resourceId: 10001, // 関連レコードの id
|
|
307
337
|
contentType: "application/pdf",
|
|
@@ -321,7 +351,7 @@ const id = await porters.attachment.create({
|
|
|
321
351
|
import { PortersError } from "@joymerrevent/porters-connect";
|
|
322
352
|
|
|
323
353
|
try {
|
|
324
|
-
await
|
|
354
|
+
await t.candidate.get(1);
|
|
325
355
|
} catch (e) {
|
|
326
356
|
if (e instanceof PortersError) {
|
|
327
357
|
e.category; // "auth" | "permission" | "notFound" | "conflict" | "network" | ...
|
|
@@ -335,7 +365,7 @@ try {
|
|
|
335
365
|
|
|
336
366
|
- トークン失効は内側で自動回復します。設定ミスは `PortersConfigError` で早期に落とします。
|
|
337
367
|
- **`Promise` を返す公開メソッドは同期 throw しません**([ADR-0046][adr46])。設定ミスも含め常に **reject** で届くので、
|
|
338
|
-
`
|
|
368
|
+
`t.candidate.search(q).catch(handler)` でも捕まえられます(`string` を返す `auth.authorizationUrl` や
|
|
339
369
|
コンストラクタなど、Promise を返さない API は同期 throw のままです)。
|
|
340
370
|
- 一時エラー・ネットワークは内蔵リトライ。非冪等な `create` はネットワーク不確実時に握り潰さず表面化します。
|
|
341
371
|
- レート制限超過時、PORTERS は判別可能なコードを返さず接続を切るため、`PortersNetworkError`(category `"network"`)として表面化します。
|
|
@@ -407,6 +437,8 @@ try {
|
|
|
407
437
|
[auth-flow]: ./docs/reference/authentication-api/README.md
|
|
408
438
|
[oauth-guide]: ./docs/guide/oauth.md
|
|
409
439
|
[error-handling]: ./docs/guide/error-handling.md
|
|
440
|
+
[custom-fields-guide]: ./docs/guide/custom-fields.md
|
|
441
|
+
[read-query-guide]: ./docs/guide/read-query.md
|
|
410
442
|
[multi-tenancy]: ./docs/guide/multi-tenancy.md
|
|
411
443
|
[bulk-write]: ./docs/guide/bulk-write.md
|
|
412
444
|
[sandbox]: ./examples/offline-sandbox.ts
|
package/dist/index.d.ts
CHANGED
|
@@ -138,12 +138,12 @@ type UserRef = {
|
|
|
138
138
|
P_Mail: string | null;
|
|
139
139
|
};
|
|
140
140
|
type FieldValue = string | number | string[] | UserRef | null;
|
|
141
|
-
type DecodedValue<D extends DataType> = D extends "System[Id]" | "Number" | "System[Reference]" ? number : D extends "User" ? UserRef : D extends "Option" ? string[] : string;
|
|
141
|
+
type DecodedValue<D extends DataType | null> = D extends null ? string : D extends "System[Id]" | "Number" | "System[Reference]" ? number : D extends "User" ? UserRef : D extends "Option" ? string[] : string;
|
|
142
142
|
|
|
143
143
|
type WritableDataType = Exclude<DataType, "System[Id]" | "System[DateTime]">;
|
|
144
|
-
type WriteValueOf<D extends DataType> = D extends "User" | "System[Reference]" | "Number" ? number : D extends "Option" ? string[] : string;
|
|
144
|
+
type WriteValueOf<D extends DataType | null> = D extends null ? never : D extends "User" | "System[Reference]" | "Number" ? number : D extends "Option" ? string[] : string;
|
|
145
145
|
|
|
146
|
-
type FieldCatalog = Record<string, DataType>;
|
|
146
|
+
type FieldCatalog = Record<string, DataType | null>;
|
|
147
147
|
/**
|
|
148
148
|
* "No custom fields": the intersection-identity default for the generic catalog params (ADR-0023).
|
|
149
149
|
* A bare `{}` is flagged by `no-empty-object-type`; `Record<never, never>` is the same empty object,
|
|
@@ -208,8 +208,12 @@ type ReferenceCondition = {
|
|
|
208
208
|
or?: number[];
|
|
209
209
|
and?: number[];
|
|
210
210
|
};
|
|
211
|
-
/**
|
|
212
|
-
|
|
211
|
+
/**
|
|
212
|
+
* The condition-operator object a field of Data Type `D` accepts. A field PORTERS gives no Data
|
|
213
|
+
* Type (`null` — ADR-0056) falls through to the closing `never`, which is exactly right: the
|
|
214
|
+
* reference says such a field cannot appear in `condition` at all.
|
|
215
|
+
*/
|
|
216
|
+
type ConditionFor<D extends DataType | null> = D extends "System[Id]" ? IdCondition : D extends "Number" ? NumberCondition : D extends "DateTime" | "System[DateTime]" | "Date" | "Age" ? TemporalCondition : D extends "SinglelineText" | "MultilineText" | "Mail" | "Telephone" | "URL" ? TextCondition : D extends "Option" ? OptionCondition : D extends "User" | "System[Reference]" ? ReferenceCondition : never;
|
|
213
217
|
/**
|
|
214
218
|
* A typed search condition over a catalog: each field maps to the operator object its Data Type
|
|
215
219
|
* allows (ADR-0038 案1a). Multiple fields are AND-joined (reference). Unknown aliases / wrong
|
|
@@ -229,10 +233,15 @@ type OrderableKeys<F extends FieldCatalog> = {
|
|
|
229
233
|
*/
|
|
230
234
|
type Order<F extends FieldCatalog> = Array<Partial<Record<OrderableKeys<F>, "asc" | "desc">>>;
|
|
231
235
|
/**
|
|
232
|
-
* Which delete state to read.
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
+
* Which delete state to read. `existing` reads live data, `deleted`/`all` read deleted records —
|
|
237
|
+
* the only way to read deleted data, since there is no delete API. When `deleted`/`all`, condition
|
|
238
|
+
* is restricted to `P_Id` / `P_UpdateDate` / `P_UpdatedBy` and PORTERS auto-adds a "updated within
|
|
239
|
+
* 90 days" filter (`P_UpdateDate` = the delete time, `P_UpdatedBy` = the last editor).
|
|
240
|
+
*
|
|
241
|
+
* **Omitting the field is not the same as passing `existing`** (ADR-0057). Omitting defers to the
|
|
242
|
+
* API's own default (today: `existing`); passing `existing` states that you want live records only,
|
|
243
|
+
* and is sent as such. Both read live data now, but only the explicit form keeps doing so if PORTERS
|
|
244
|
+
* ever changes that default. Use `itemstate: "existing"` when live-only actually matters to you.
|
|
236
245
|
*/
|
|
237
246
|
type ItemState = "existing" | "deleted" | "all";
|
|
238
247
|
type SearchQuery<F extends FieldCatalog = FieldCatalog> = {
|
|
@@ -332,16 +341,21 @@ declare const FIELDS$8: {
|
|
|
332
341
|
readonly P_UpdatedBy: "User";
|
|
333
342
|
readonly P_Phase: "Option";
|
|
334
343
|
readonly P_PhaseDate: "DateTime";
|
|
344
|
+
readonly P_PhaseMemo: "MultilineText";
|
|
335
345
|
readonly P_Name: "SinglelineText";
|
|
336
346
|
readonly P_Reading: "SinglelineText";
|
|
347
|
+
readonly P_Memo: "MultilineText";
|
|
337
348
|
readonly P_Mail: "Mail";
|
|
338
349
|
readonly P_MobileMail: "Mail";
|
|
339
350
|
readonly P_Telephone: "Telephone";
|
|
340
351
|
readonly P_Mobile: "Telephone";
|
|
352
|
+
readonly P_Fax: "Telephone";
|
|
341
353
|
readonly P_Country: "SinglelineText";
|
|
342
354
|
readonly P_Prefecture: "SinglelineText";
|
|
343
355
|
readonly P_City: "SinglelineText";
|
|
356
|
+
readonly P_Street: "MultilineText";
|
|
344
357
|
readonly P_Zipcode: "SinglelineText";
|
|
358
|
+
readonly P_Deleted: null;
|
|
345
359
|
};
|
|
346
360
|
declare const REQUIRED_ON_CREATE$4: readonly ["P_Owner"];
|
|
347
361
|
/** A decoded Candidate: known `P_` fields, each requested field `value | null`. */
|
|
@@ -390,6 +404,7 @@ declare const FIELDS$7: {
|
|
|
390
404
|
readonly P_CapitalText: "SinglelineText";
|
|
391
405
|
readonly P_EmploymentType: "Option";
|
|
392
406
|
readonly P_ExpectedAgeReason: "Option";
|
|
407
|
+
readonly P_Deleted: null;
|
|
393
408
|
};
|
|
394
409
|
declare const REQUIRED_ON_CREATE$3: readonly ["P_Owner", "P_Client", "P_Recruiter"];
|
|
395
410
|
/** A decoded Job: known `P_` fields, each requested field `value | null`. */
|
|
@@ -422,6 +437,7 @@ declare const FIELDS$6: {
|
|
|
422
437
|
readonly P_Zipcode: "SinglelineText";
|
|
423
438
|
readonly P_Telephone: "Telephone";
|
|
424
439
|
readonly P_Fax: "Telephone";
|
|
440
|
+
readonly P_Deleted: null;
|
|
425
441
|
};
|
|
426
442
|
declare const REQUIRED_ON_CREATE$2: readonly ["P_Owner"];
|
|
427
443
|
/** A decoded Client (company): known `P_` fields, each requested field `value | null`. */
|
|
@@ -454,6 +470,7 @@ declare const FIELDS$5: {
|
|
|
454
470
|
readonly P_CloseReason: "Option";
|
|
455
471
|
readonly P_ExpectedSalesAmount: "Number";
|
|
456
472
|
readonly P_ExpectedClosingDate: "Date";
|
|
473
|
+
readonly P_Deleted: null;
|
|
457
474
|
};
|
|
458
475
|
declare const REQUIRED_ON_CREATE$1: readonly ["P_Owner", "P_Client", "P_Recruiter", "P_Job", "P_Candidate", "P_Resume"];
|
|
459
476
|
/** A decoded Process (a Candidate's progress through a Job): known `P_` fields, each
|
|
@@ -499,6 +516,7 @@ declare const FIELDS$4: {
|
|
|
499
516
|
readonly P_ExpectSalary: "Number";
|
|
500
517
|
readonly P_DesiredHourlyRate: "Number";
|
|
501
518
|
readonly P_HourlyRate: "Number";
|
|
519
|
+
readonly P_Deleted: null;
|
|
502
520
|
};
|
|
503
521
|
declare const REQUIRED_ON_CREATE: readonly ["P_Owner", "P_Candidate"];
|
|
504
522
|
/** A decoded Resume (a Candidate's CV / profile): known `P_` fields, each requested field
|
|
@@ -775,12 +793,6 @@ type PortersClientOptions<C extends DeclaredCatalogs = EmptyCatalog> = {
|
|
|
775
793
|
appId?: string;
|
|
776
794
|
appSecret?: string;
|
|
777
795
|
scopes?: Scope[];
|
|
778
|
-
/**
|
|
779
|
-
* Default partition (Company DB) for every call. For multi-tenant routing, bind a partition
|
|
780
|
-
* per call with {@link PortersClient.tenant} (ADR-0040 / F-3); for a fully separated per-partition
|
|
781
|
-
* token, construct a dedicated client per tenant instead (ADR-0008 案3).
|
|
782
|
-
*/
|
|
783
|
-
partition?: PartitionId;
|
|
784
796
|
/** Custom auth strategy; defaults to the transparent code_direct strategy. */
|
|
785
797
|
auth?: TokenProvider;
|
|
786
798
|
/** Token persistence; defaults to in-memory. */
|
|
@@ -796,8 +808,10 @@ type PortersClientOptions<C extends DeclaredCatalogs = EmptyCatalog> = {
|
|
|
796
808
|
};
|
|
797
809
|
/**
|
|
798
810
|
* The partition-bound resource accessors returned by {@link PortersClient.tenant} (ADR-0040 / F-3).
|
|
799
|
-
*
|
|
800
|
-
*
|
|
811
|
+
* **This is the only way to reach a partition-scoped resource** (ADR-0055): PORTERS requires
|
|
812
|
+
* `partition` on every one of these calls, so the API makes you supply it exactly once, explicitly.
|
|
813
|
+
* `auth` (App-level), the `partition` master (discovery — partition-less), and `tenant` itself
|
|
814
|
+
* (no nesting) are deliberately absent: none of them takes a partition.
|
|
801
815
|
*/
|
|
802
816
|
type TenantScope<C extends DeclaredCatalogs = EmptyCatalog> = {
|
|
803
817
|
readonly candidate: CandidateResource<CustomFor<C, "candidate">>;
|
|
@@ -811,36 +825,41 @@ type TenantScope<C extends DeclaredCatalogs = EmptyCatalog> = {
|
|
|
811
825
|
readonly option: OptionResource;
|
|
812
826
|
};
|
|
813
827
|
/**
|
|
814
|
-
* Entry point of the library. Wires the default transport / auth / throttle /
|
|
815
|
-
*
|
|
816
|
-
*
|
|
828
|
+
* Entry point of the library. Wires the default transport / auth / throttle / requester and exposes
|
|
829
|
+
* the **App-level** surface: `auth`, the `partition` master (discovery), and {@link PortersClient.tenant}.
|
|
830
|
+
*
|
|
831
|
+
* Everything that PORTERS scopes to a partition (Company DB) lives behind `tenant(id)` — see
|
|
832
|
+
* {@link TenantScope}. The client holds no default partition (ADR-0055): a partition is bound
|
|
833
|
+
* explicitly, exactly once, so "unbound" is not a state this API can be in.
|
|
834
|
+
*
|
|
835
|
+
* @example
|
|
836
|
+
* const porters = new PortersClient({ host, appId, appSecret });
|
|
837
|
+
* await porters.auth.ensureAuthenticated(); // App-level
|
|
838
|
+
* const t = porters.tenant(123); // bind the partition once
|
|
839
|
+
* const page = await t.candidate.search();
|
|
817
840
|
*/
|
|
818
841
|
declare class PortersClient<C extends DeclaredCatalogs = EmptyCatalog> {
|
|
819
842
|
#private;
|
|
820
|
-
readonly candidate: CandidateResource<CustomFor<C, "candidate">>;
|
|
821
|
-
readonly job: JobResource<CustomFor<C, "job">>;
|
|
822
|
-
readonly client: ClientResource<CustomFor<C, "client">>;
|
|
823
|
-
readonly process: ProcessResource<CustomFor<C, "process">>;
|
|
824
|
-
readonly resume: ResumeResource<CustomFor<C, "resume">>;
|
|
825
|
-
readonly attachment: AttachmentResource;
|
|
826
843
|
/** OAuth surface: initial browser grant, token warm-up/inspection, local revoke (ADR-0007/0034). */
|
|
827
844
|
readonly auth: AuthApi;
|
|
828
|
-
/**
|
|
845
|
+
/**
|
|
846
|
+
* Master Read: the partitions this App can reach (ADR-0021/0022). Takes no `partition` itself —
|
|
847
|
+
* it is how you *discover* the ids to pass to {@link PortersClient.tenant}.
|
|
848
|
+
*/
|
|
829
849
|
readonly partition: PartitionResource;
|
|
830
|
-
/** Master Read: users, plus `current()` self-identification (ADR-0021/0022). */
|
|
831
|
-
readonly user: UserResource;
|
|
832
|
-
/** Master Read: a resource's field catalog (ADR-0021/0022). */
|
|
833
|
-
readonly field: FieldResource;
|
|
834
|
-
/** Master Read: a tenant's choice (option) master (ADR-0021/0022). */
|
|
835
|
-
readonly option: OptionResource;
|
|
836
850
|
/**
|
|
837
|
-
* Bind a
|
|
838
|
-
*
|
|
839
|
-
*
|
|
840
|
-
*
|
|
841
|
-
*
|
|
842
|
-
*
|
|
843
|
-
*
|
|
851
|
+
* Bind a partition (Company DB) and get the accessors that route through it (ADR-0040 F-3).
|
|
852
|
+
* **Single-tenant apps use this too** — it is the only path to a partition-scoped resource
|
|
853
|
+
* (ADR-0055). Hold the scope once and use it like a client:
|
|
854
|
+
*
|
|
855
|
+
* ```ts
|
|
856
|
+
* const t = porters.tenant(123);
|
|
857
|
+
* await t.candidate.search();
|
|
858
|
+
* ```
|
|
859
|
+
*
|
|
860
|
+
* `auth` (App-level), the `partition` master (discovery — takes no partition), and `tenant`
|
|
861
|
+
* itself (no nesting) are intentionally absent from the returned scope. For a fully separated
|
|
862
|
+
* per-partition token, construct a dedicated {@link PortersClient} per tenant (ADR-0008 案3).
|
|
844
863
|
*/
|
|
845
864
|
readonly tenant: (id: PartitionId) => TenantScope<C>;
|
|
846
865
|
constructor(options: PortersClientOptions<C>);
|
|
@@ -901,4 +920,4 @@ declare const bytesToBase64: (bytes: Uint8Array) => string;
|
|
|
901
920
|
/** Decode a Base64 string back to raw bytes. */
|
|
902
921
|
declare const base64ToBytes: (b64: string) => Uint8Array;
|
|
903
922
|
|
|
904
|
-
export { 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 CustomDataType, type DefinedFields, type ErrorCategory, type Field, type FieldBuilder, type FieldDecls, type FieldDef, type FieldPage, type FieldResource, type FieldSearchQuery, type FieldValue, type GetAccessTokenOptions, type ItemState, type Job, type JobCreateInput, type JobPage, type JobResource, type JobSearchQuery, type JobUpdateInput, type MockHandler, type MockReply, type MockTransportOptions, type Option, type OptionResource, type OptionSearchQuery, type Order, type Partition, type PartitionId, type PartitionPage, type PartitionResource, type PartitionSearchQuery, 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 ResourceType, type Resume, type ResumeCreateInput, type ResumePage, type ResumeResource, type ResumeSearchQuery, type ResumeUpdateInput, type RevokeUrlOptions, type Scheme, type Scope, type SearchQuery, type StoredTokens, type TenantScope, type TokenProvider, type TokenStore, type Transport, type TransportRequest, type TransportResponse, type User, type UserPage, type UserRef, type UserResource, type UserSearchQuery, base64ToBytes, bytesToBase64, createMockTransport, defineFields };
|
|
923
|
+
export { 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 CustomDataType, type CustomFieldResource, type CustomFor, type DeclaredCatalogs, type DefinedFields, type ErrorCategory, type Field, type FieldBuilder, type FieldDecls, type FieldDef, type FieldPage, type FieldResource, type FieldSearchQuery, type FieldValue, type GetAccessTokenOptions, type ItemState, type Job, type JobCreateInput, type JobPage, type JobResource, type JobSearchQuery, type JobUpdateInput, type MockHandler, type MockReply, type MockTransportOptions, type Option, type OptionResource, type OptionSearchQuery, type Order, type Partition, type PartitionId, type PartitionPage, type PartitionResource, type PartitionSearchQuery, 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 ResourceType, type Resume, type ResumeCreateInput, type ResumePage, type ResumeResource, type ResumeSearchQuery, type ResumeUpdateInput, type RevokeUrlOptions, type Scheme, type Scope, type SearchQuery, type StoredTokens, type TenantScope, type TokenProvider, type TokenStore, type Transport, type TransportRequest, type TransportResponse, type User, type UserPage, type UserRef, type UserResource, type UserSearchQuery, base64ToBytes, bytesToBase64, createMockTransport, defineFields };
|