@joymerrevent/porters-connect 0.7.0 → 0.8.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 +24 -1
- package/dist/index.js +54 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,27 @@
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.8.0] - 2026-08-14
|
|
9
|
+
|
|
10
|
+
**「0 件」と「届いていない」を区別できるようにした版**です。HTTP 200 を返す中間装置
|
|
11
|
+
(キャプティブポータル・SSO のログイン画面・WAF の通知ページ)の応答が、これまで**正常な空ページ**として
|
|
12
|
+
通っていました。公開 API の形(型・メソッド)は不変で、**正しい PORTERS 応答に対する挙動も変わりません**。
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **HTTP 200 で返る「PORTERS 以外の応答」を 0 件として扱わなくなりました**([ADR-0051][adr51])。
|
|
17
|
+
Read 応答は**ルート要素名(`<Candidate>` などリソース名)と `<Code>` の両方**で同定します。
|
|
18
|
+
どちらかを欠くボディは `PortersResourceError`(`category: "unknown"` + `httpStatus: 200` + 中間装置を疑う `hint`)で拒否します。
|
|
19
|
+
- 従来は**キャプティブポータル・SSO のログイン画面・WAF の通知ページ**(いずれも HTTP 200)が
|
|
20
|
+
`total: 0` の**正常な空ページ**として返っていました。`get(id)` は `undefined` を返すため、
|
|
21
|
+
**「無ければ作る」コードが重複レコードを作りにいく**状態でした(データが無いのか届いていないのかを区別できない)。
|
|
22
|
+
- メッセージは原因を名指しします — `resource response root is <html>, expected <Candidate>` /
|
|
23
|
+
`resource response has no <Code> (not a PORTERS envelope)`。**観測したルート名**が載るので切り分けできます。
|
|
24
|
+
- **正しい PORTERS 応答に対する挙動は変わりません**([エラーハンドリング ガイド][guide]に判定表を追加)。
|
|
25
|
+
- ⚠️ **`createMockTransport` でモックを手書きしている場合**は、ボディに**リソース名のルート要素と `<Code>`** が
|
|
26
|
+
必要です(例 `<Candidate Total="1" Count="1" Start="0"><Code>0</Code>…</Candidate>`)。
|
|
27
|
+
実際の応答と同じ形にしていれば変更は不要です。
|
|
28
|
+
|
|
8
29
|
## [0.7.0] - 2026-08-13
|
|
9
30
|
|
|
10
31
|
エラーの見え方をまとめて是正した版です。**PORTERS の応答ではない HTTP エラー**が分類されるようになり、
|
|
@@ -217,11 +238,13 @@
|
|
|
217
238
|
[adr48]: docs/adr/0048-access-point-host-validation.md
|
|
218
239
|
[adr49]: docs/adr/0049-host-port-roundtrip.md
|
|
219
240
|
[adr50]: docs/adr/0050-auth-http-status-handling.md
|
|
241
|
+
[adr51]: docs/adr/0051-read-envelope-identification.md
|
|
220
242
|
[adr47]: docs/adr/0047-access-point-scheme.md
|
|
221
243
|
[oauth-guide]: docs/guide/oauth.md
|
|
222
244
|
[kac]: https://keepachangelog.com/en/1.1.0/
|
|
223
245
|
[semver]: https://semver.org/
|
|
224
|
-
[unreleased]: https://github.com/Joymerrevent/porters-connect/compare/v0.
|
|
246
|
+
[unreleased]: https://github.com/Joymerrevent/porters-connect/compare/v0.8.0...HEAD
|
|
247
|
+
[0.8.0]: https://github.com/Joymerrevent/porters-connect/compare/v0.7.0...v0.8.0
|
|
225
248
|
[0.7.0]: https://github.com/Joymerrevent/porters-connect/compare/v0.6.2...v0.7.0
|
|
226
249
|
[0.6.2]: https://github.com/Joymerrevent/porters-connect/compare/v0.6.1...v0.6.2
|
|
227
250
|
[0.6.1]: https://github.com/Joymerrevent/porters-connect/compare/v0.6.0...v0.6.1
|
package/dist/index.js
CHANGED
|
@@ -380,7 +380,8 @@ var toInt = (v) => {
|
|
|
380
380
|
const s = asString(v);
|
|
381
381
|
return s === void 0 ? 0 : Number(s);
|
|
382
382
|
};
|
|
383
|
-
var
|
|
383
|
+
var MIDDLEBOX_HINT = "A middlebox (proxy, captive portal, SSO login page, WAF notice) may be answering instead of PORTERS. Check PORTERS_HOST and the network path from this process to the API.";
|
|
384
|
+
var parseResourcePage = (xml, resource) => {
|
|
384
385
|
const root = asRecord(parser.parse(xml));
|
|
385
386
|
const rootKey = root ? Object.keys(root)[0] : void 0;
|
|
386
387
|
const body = root && rootKey ? asRecord(root[rootKey]) : void 0;
|
|
@@ -389,11 +390,21 @@ var parseResourcePage = (xml) => {
|
|
|
389
390
|
category: "unknown"
|
|
390
391
|
});
|
|
391
392
|
}
|
|
393
|
+
if (rootKey !== resource) {
|
|
394
|
+
throw new PortersResourceError(
|
|
395
|
+
`resource response root is <${rootKey}>, expected <${resource}>`,
|
|
396
|
+
{ category: "unknown", hint: MIDDLEBOX_HINT, context: { resource } }
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
if (body.Code === void 0) {
|
|
400
|
+
throw new PortersResourceError(
|
|
401
|
+
"resource response has no <Code> (not a PORTERS envelope)",
|
|
402
|
+
{ category: "unknown", hint: MIDDLEBOX_HINT, context: { resource } }
|
|
403
|
+
);
|
|
404
|
+
}
|
|
392
405
|
const code = toInt(body.Code);
|
|
393
406
|
if (code !== 0) {
|
|
394
|
-
throw resourceError(code, `resource returned code ${code}`, {
|
|
395
|
-
resource: rootKey
|
|
396
|
-
});
|
|
407
|
+
throw resourceError(code, `resource returned code ${code}`, { resource });
|
|
397
408
|
}
|
|
398
409
|
return {
|
|
399
410
|
total: toInt(body["@_Total"]),
|
|
@@ -826,8 +837,8 @@ var decoderFor = (fields) => {
|
|
|
826
837
|
return out;
|
|
827
838
|
};
|
|
828
839
|
};
|
|
829
|
-
var runRead = (requester, url, decode) => requester.request({ method: "GET", url, headers: {} }, (body) => {
|
|
830
|
-
const page = parseResourcePage(body);
|
|
840
|
+
var runRead = (requester, resource, url, decode) => requester.request({ method: "GET", url, headers: {} }, (body) => {
|
|
841
|
+
const page = parseResourcePage(body, resource);
|
|
831
842
|
return {
|
|
832
843
|
items: page.items.map(decode),
|
|
833
844
|
total: page.total,
|
|
@@ -1053,6 +1064,7 @@ var createResource = (config, deps) => {
|
|
|
1053
1064
|
const firstWriteId = (body) => firstWriteResultId(body, config.path, config.name);
|
|
1054
1065
|
const search = async (query = {}) => runRead(
|
|
1055
1066
|
deps.requester,
|
|
1067
|
+
config.name,
|
|
1056
1068
|
readUrl({ ...query, field: query.field ?? defaultFields }),
|
|
1057
1069
|
decode
|
|
1058
1070
|
);
|
|
@@ -1329,6 +1341,7 @@ var createResumeResource = (deps, custom) => {
|
|
|
1329
1341
|
|
|
1330
1342
|
// src/resources/attachment.ts
|
|
1331
1343
|
var MAX_CONTENT_CHARS = 14e6;
|
|
1344
|
+
var ATTACHMENT_RESOURCE = "Attachment";
|
|
1332
1345
|
var ATTACHMENT_FIELD_NAMES = [
|
|
1333
1346
|
"Id",
|
|
1334
1347
|
"Resource",
|
|
@@ -1388,7 +1401,7 @@ var createAttachmentResource = (deps) => {
|
|
|
1388
1401
|
headers: {}
|
|
1389
1402
|
},
|
|
1390
1403
|
(body) => {
|
|
1391
|
-
const page = parseResourcePage(body);
|
|
1404
|
+
const page = parseResourcePage(body, ATTACHMENT_RESOURCE);
|
|
1392
1405
|
return {
|
|
1393
1406
|
items: page.items.map(decodeAttachment),
|
|
1394
1407
|
total: page.total,
|
|
@@ -1412,7 +1425,7 @@ var createAttachmentResource = (deps) => {
|
|
|
1412
1425
|
headers: {},
|
|
1413
1426
|
body: `<Attachment><Item>${inner}</Item></Attachment>`
|
|
1414
1427
|
},
|
|
1415
|
-
(body) => firstWriteResultId(body, "attachment",
|
|
1428
|
+
(body) => firstWriteResultId(body, "attachment", ATTACHMENT_RESOURCE),
|
|
1416
1429
|
{ write: true, idempotent, unboundedBody: true }
|
|
1417
1430
|
);
|
|
1418
1431
|
const create = async (input) => {
|
|
@@ -1439,6 +1452,12 @@ var FIELDS6 = {
|
|
|
1439
1452
|
P_Name: "SinglelineText",
|
|
1440
1453
|
P_CompanyId: "SinglelineText"
|
|
1441
1454
|
};
|
|
1455
|
+
var PARTITION_DESCRIPTOR = {
|
|
1456
|
+
name: "Partition",
|
|
1457
|
+
path: "partition",
|
|
1458
|
+
prefix: "Partition",
|
|
1459
|
+
fields: FIELDS6
|
|
1460
|
+
};
|
|
1442
1461
|
var buildUrl = (accessPoint, q) => {
|
|
1443
1462
|
const p = new URLSearchParams();
|
|
1444
1463
|
p.set("request_type", String(q.requestType ?? 1));
|
|
@@ -1448,7 +1467,12 @@ var buildUrl = (accessPoint, q) => {
|
|
|
1448
1467
|
};
|
|
1449
1468
|
var createPartitionResource = (deps) => {
|
|
1450
1469
|
const decode = decoderFor(FIELDS6);
|
|
1451
|
-
const search = async (query = {}) => runRead(
|
|
1470
|
+
const search = async (query = {}) => runRead(
|
|
1471
|
+
deps.requester,
|
|
1472
|
+
PARTITION_DESCRIPTOR.name,
|
|
1473
|
+
buildUrl(deps.accessPoint, query),
|
|
1474
|
+
decode
|
|
1475
|
+
);
|
|
1452
1476
|
const searchAll = (query = {}) => paginate((count, start) => search({ ...query, count, start }));
|
|
1453
1477
|
return { search, searchAll };
|
|
1454
1478
|
};
|
|
@@ -1460,6 +1484,12 @@ var FIELDS7 = {
|
|
|
1460
1484
|
P_Name: "SinglelineText",
|
|
1461
1485
|
P_Mail: "Mail"
|
|
1462
1486
|
};
|
|
1487
|
+
var USER_DESCRIPTOR = {
|
|
1488
|
+
name: "User",
|
|
1489
|
+
path: "user",
|
|
1490
|
+
prefix: "User",
|
|
1491
|
+
fields: FIELDS7
|
|
1492
|
+
};
|
|
1463
1493
|
var buildUrl2 = (accessPoint, partition, q) => {
|
|
1464
1494
|
const p = new URLSearchParams();
|
|
1465
1495
|
p.set("partition", String(partition));
|
|
@@ -1474,6 +1504,7 @@ var createUserResource = (deps) => {
|
|
|
1474
1504
|
const decode = decoderFor(FIELDS7);
|
|
1475
1505
|
const search = async (query = {}) => runRead(
|
|
1476
1506
|
deps.requester,
|
|
1507
|
+
USER_DESCRIPTOR.name,
|
|
1477
1508
|
buildUrl2(deps.accessPoint, deps.partition, query),
|
|
1478
1509
|
decode
|
|
1479
1510
|
);
|
|
@@ -1510,6 +1541,12 @@ var FIELDS8 = {
|
|
|
1510
1541
|
P_ReferTo: "Option",
|
|
1511
1542
|
P_ResourceType: "Number"
|
|
1512
1543
|
};
|
|
1544
|
+
var FIELD_DESCRIPTOR = {
|
|
1545
|
+
name: "Field",
|
|
1546
|
+
path: "field",
|
|
1547
|
+
prefix: "Field",
|
|
1548
|
+
fields: FIELDS8
|
|
1549
|
+
};
|
|
1513
1550
|
var buildUrl3 = (accessPoint, partition, q) => {
|
|
1514
1551
|
const p = new URLSearchParams();
|
|
1515
1552
|
p.set("partition", String(partition));
|
|
@@ -1523,6 +1560,7 @@ var createFieldResource = (deps) => {
|
|
|
1523
1560
|
const decode = decoderFor(FIELDS8);
|
|
1524
1561
|
const search = async (query) => runRead(
|
|
1525
1562
|
deps.requester,
|
|
1563
|
+
FIELD_DESCRIPTOR.name,
|
|
1526
1564
|
buildUrl3(deps.accessPoint, deps.partition, query),
|
|
1527
1565
|
decode
|
|
1528
1566
|
);
|
|
@@ -1539,6 +1577,12 @@ var FIELDS9 = {
|
|
|
1539
1577
|
P_Type: "Number",
|
|
1540
1578
|
P_Order: "Number"
|
|
1541
1579
|
};
|
|
1580
|
+
var OPTION_DESCRIPTOR = {
|
|
1581
|
+
name: "Option",
|
|
1582
|
+
path: "option",
|
|
1583
|
+
prefix: "Option",
|
|
1584
|
+
fields: FIELDS9
|
|
1585
|
+
};
|
|
1542
1586
|
var buildUrl4 = (accessPoint, partition, q) => {
|
|
1543
1587
|
const p = new URLSearchParams();
|
|
1544
1588
|
p.set("partition", String(partition));
|
|
@@ -1568,7 +1612,7 @@ var createOptionResource = (deps) => {
|
|
|
1568
1612
|
},
|
|
1569
1613
|
(body) => {
|
|
1570
1614
|
const out = [];
|
|
1571
|
-
flatten(parseResourcePage(body).items, out);
|
|
1615
|
+
flatten(parseResourcePage(body, OPTION_DESCRIPTOR.name).items, out);
|
|
1572
1616
|
return out;
|
|
1573
1617
|
}
|
|
1574
1618
|
);
|