@joymerrevent/porters-connect 0.12.0 → 0.12.1
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 +33 -1
- package/dist/index.js +81 -36
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,34 @@
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.12.1] - 2026-09-03
|
|
9
|
+
|
|
10
|
+
**フェイルセーフの修正 2 件**です。どちらも「黙って壊れる/安全側に倒しすぎている」場面を直したもので、
|
|
11
|
+
**公開 API・型に変更はありません**(既存コードはそのまま動きます)。
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **`searchAll` が「渡したクエリの全件」を必ず返すようになりました**([RV-32][rv32])。
|
|
16
|
+
これまではクエリ オブジェクトへの参照を保持してページごとに読み直していたため、`for await` の反復中に
|
|
17
|
+
呼び出し側がそのオブジェクトを書き換えると、**同じ 1 回の `searchAll` の途中で検索条件が変わって**いました
|
|
18
|
+
(例外も警告も出ないまま「全件取ったつもりが途中から別条件の全件」になる)。
|
|
19
|
+
|
|
20
|
+
クエリは**最初のページを取る直前に 1 度だけ直列化**し、以降は `count` / `start` だけを差し替えます。
|
|
21
|
+
入れ子(`q.condition.P_Name.part` など)の書き換えも後続ページに影響しません。クエリ ガード
|
|
22
|
+
(`keywords` の長さ・`itemstate` の condition 制限など)の失敗が届くタイミングは従来どおりです。
|
|
23
|
+
対象は全データ系リソースと `field` / `partition` / `user`。
|
|
24
|
+
|
|
25
|
+
- **適用されていないと分かっている失敗では、`create` / `createMany` が自動リトライされるようになりました**
|
|
26
|
+
([ADR-0063][adr63] / [RV-22][rv22])。非冪等な書き込みを不確定な失敗で再送しない方針(二重登録を作らない)は
|
|
27
|
+
変わりません。変えたのは「不確定」の判定で、これまでは `PortersNetworkError` かどうかだけを見ていたため、
|
|
28
|
+
**リクエストが一度も出ていない失敗**まで「適用されたかもしれない」扱いになっていました。
|
|
29
|
+
|
|
30
|
+
- **トークン取得に失敗した場合** — 送信前なので再送しても二重登録は起きません。
|
|
31
|
+
- **HTTP 429** — 処理される前に拒否された応答なので、同じく対象になります
|
|
32
|
+
(実 PORTERS はレート超過で切断する想定なので、効くのはプロキシ経由の環境です)。
|
|
33
|
+
|
|
34
|
+
**送信後の timeout / 切断 / 5xx では、従来どおり再送しません**(適用されたかどうか分からないため)。
|
|
35
|
+
|
|
8
36
|
## [0.12.0] - 2026-08-31
|
|
9
37
|
|
|
10
38
|
**PORTERS の全リソースに対応した版**です。データ系は 6/13 から **13/13** になり、
|
|
@@ -511,11 +539,15 @@
|
|
|
511
539
|
[adr59]: docs/adr/0059-read-field-bare-alias.md
|
|
512
540
|
[adr60]: docs/adr/0060-full-resource-coverage-direction.md
|
|
513
541
|
[adr61]: docs/adr/0061-phase-resource-surface.md
|
|
542
|
+
[adr63]: docs/adr/0063-idempotency-guard-scope.md
|
|
543
|
+
[rv22]: docs/reviews/rv/0022-ratelimit-create-no-retry.md
|
|
544
|
+
[rv32]: docs/reviews/rv/0032-searchall-query-mutation.md
|
|
514
545
|
[write-constraints]: docs/guide/write-constraints.md
|
|
515
546
|
[lv]: docs/live-verification.md
|
|
516
547
|
[kac]: https://keepachangelog.com/en/1.1.0/
|
|
517
548
|
[semver]: https://semver.org/
|
|
518
|
-
[unreleased]: https://github.com/Joymerrevent/porters-connect/compare/v0.12.
|
|
549
|
+
[unreleased]: https://github.com/Joymerrevent/porters-connect/compare/v0.12.1...HEAD
|
|
550
|
+
[0.12.1]: https://github.com/Joymerrevent/porters-connect/compare/v0.12.0...v0.12.1
|
|
519
551
|
[0.12.0]: https://github.com/Joymerrevent/porters-connect/compare/v0.11.0...v0.12.0
|
|
520
552
|
[0.11.0]: https://github.com/Joymerrevent/porters-connect/compare/v0.10.0...v0.11.0
|
|
521
553
|
[0.10.0]: https://github.com/Joymerrevent/porters-connect/compare/v0.9.0...v0.10.0
|
package/dist/index.js
CHANGED
|
@@ -324,11 +324,13 @@ var createRequester = (o) => {
|
|
|
324
324
|
let attempt = 0;
|
|
325
325
|
for (; ; ) {
|
|
326
326
|
await o.throttle.take(write);
|
|
327
|
+
let sent = false;
|
|
327
328
|
try {
|
|
328
329
|
const token = await o.auth.getAccessToken(
|
|
329
330
|
forceRefresh ? { forceRefresh: true } : void 0
|
|
330
331
|
);
|
|
331
332
|
forceRefresh = false;
|
|
333
|
+
sent = true;
|
|
332
334
|
const res = await o.transport.send(withAuth(req, token, write));
|
|
333
335
|
return readResponse(res, parse);
|
|
334
336
|
} catch (e) {
|
|
@@ -338,7 +340,9 @@ var createRequester = (o) => {
|
|
|
338
340
|
forceRefresh = true;
|
|
339
341
|
continue;
|
|
340
342
|
}
|
|
341
|
-
|
|
343
|
+
const mayHaveApplied = sent && e.category !== "rateLimit";
|
|
344
|
+
if (mayHaveApplied && e instanceof PortersNetworkError && write && !idempotent)
|
|
345
|
+
throw e;
|
|
342
346
|
if (e.retryable && attempt < maxRetries) {
|
|
343
347
|
attempt += 1;
|
|
344
348
|
await sleep2(o.backoff(attempt - 1));
|
|
@@ -915,6 +919,11 @@ var appendPaging = (p, count, start) => {
|
|
|
915
919
|
}
|
|
916
920
|
if (start !== void 0) p.set("start", String(start));
|
|
917
921
|
};
|
|
922
|
+
var readUrlOf = (accessPoint, path, base, count, start) => {
|
|
923
|
+
const p = new URLSearchParams(base);
|
|
924
|
+
appendPaging(p, count, start);
|
|
925
|
+
return apiUrl(accessPoint, path, p);
|
|
926
|
+
};
|
|
918
927
|
var paginate = async function* (fetchPage) {
|
|
919
928
|
let start = 0;
|
|
920
929
|
for (; ; ) {
|
|
@@ -924,6 +933,9 @@ var paginate = async function* (fetchPage) {
|
|
|
924
933
|
if (page.items.length === 0 || start >= page.total) return;
|
|
925
934
|
}
|
|
926
935
|
};
|
|
936
|
+
var paginateOnce = async function* (prepare) {
|
|
937
|
+
yield* paginate(prepare());
|
|
938
|
+
};
|
|
927
939
|
|
|
928
940
|
// src/resources/query.ts
|
|
929
941
|
var KEYWORDS_MAX_CHARS = 100;
|
|
@@ -1144,7 +1156,7 @@ var firstWriteResultId = (body, path, name) => {
|
|
|
1144
1156
|
}
|
|
1145
1157
|
return first.id;
|
|
1146
1158
|
};
|
|
1147
|
-
var
|
|
1159
|
+
var buildReadParams = (partition, q, ctx) => {
|
|
1148
1160
|
const p = new URLSearchParams();
|
|
1149
1161
|
p.set("partition", String(partition));
|
|
1150
1162
|
for (const [key, value] of Object.entries(ctx.params ?? {}))
|
|
@@ -1159,8 +1171,7 @@ var buildReadUrl = (accessPoint, partition, path, q, ctx) => {
|
|
|
1159
1171
|
p.set("field", entries.join(","));
|
|
1160
1172
|
}
|
|
1161
1173
|
appendReadQuery(p, q, ctx);
|
|
1162
|
-
|
|
1163
|
-
return apiUrl(accessPoint, path, p);
|
|
1174
|
+
return p;
|
|
1164
1175
|
};
|
|
1165
1176
|
var buildWriteUrl = (accessPoint, partition, path) => apiUrl(
|
|
1166
1177
|
accessPoint,
|
|
@@ -1175,12 +1186,13 @@ var createResource = (config, deps) => {
|
|
|
1175
1186
|
const idAlias = config.idAlias ?? "P_Id";
|
|
1176
1187
|
const decode = decoderFor(config.fields);
|
|
1177
1188
|
const defaultFields = Object.keys(config.fields);
|
|
1178
|
-
const
|
|
1189
|
+
const readParams = (q) => buildReadParams(deps.partition, q, {
|
|
1179
1190
|
prefix: config.prefix,
|
|
1180
1191
|
fields: fieldMap,
|
|
1181
1192
|
references,
|
|
1182
1193
|
params: config.readParams
|
|
1183
1194
|
});
|
|
1195
|
+
const readUrl = (q) => readUrlOf(deps.accessPoint, config.path, readParams(q), q.count, q.start);
|
|
1184
1196
|
const writeUrl = () => buildWriteUrl(deps.accessPoint, deps.partition, config.path);
|
|
1185
1197
|
const firstWriteId = (body) => firstWriteResultId(body, config.path, config.name);
|
|
1186
1198
|
const decoderWith = (expand) => {
|
|
@@ -1193,7 +1205,19 @@ var createResource = (config, deps) => {
|
|
|
1193
1205
|
readUrl({ ...query, field: query.field ?? defaultFields }),
|
|
1194
1206
|
decoderWith(query.expand)
|
|
1195
1207
|
);
|
|
1196
|
-
const searchAll = (query = {}) =>
|
|
1208
|
+
const searchAll = (query = {}) => paginateOnce(() => {
|
|
1209
|
+
const base = readParams({
|
|
1210
|
+
...query,
|
|
1211
|
+
field: query.field ?? defaultFields
|
|
1212
|
+
});
|
|
1213
|
+
const decode2 = decoderWith(query.expand);
|
|
1214
|
+
return (count, start) => runRead(
|
|
1215
|
+
deps.requester,
|
|
1216
|
+
config.name,
|
|
1217
|
+
readUrlOf(deps.accessPoint, config.path, base, count, start),
|
|
1218
|
+
decode2
|
|
1219
|
+
);
|
|
1220
|
+
});
|
|
1197
1221
|
const get = async (id, options = {}) => {
|
|
1198
1222
|
const condition = { [idAlias]: { eq: id } };
|
|
1199
1223
|
const page = await search({
|
|
@@ -2012,21 +2036,24 @@ var PARTITION_DESCRIPTOR = {
|
|
|
2012
2036
|
prefix: "Partition",
|
|
2013
2037
|
fields: FIELDS13
|
|
2014
2038
|
};
|
|
2015
|
-
var
|
|
2039
|
+
var buildParams = (q) => {
|
|
2016
2040
|
const p = new URLSearchParams();
|
|
2017
2041
|
p.set("request_type", String(q.requestType ?? 1));
|
|
2018
|
-
|
|
2019
|
-
return apiUrl(accessPoint, "partition", p);
|
|
2042
|
+
return p;
|
|
2020
2043
|
};
|
|
2021
2044
|
var createPartitionResource = (deps) => {
|
|
2022
2045
|
const decode = decoderFor(FIELDS13);
|
|
2023
|
-
const
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2046
|
+
const readUrl = (q) => readUrlOf(deps.accessPoint, "partition", buildParams(q), q.count, q.start);
|
|
2047
|
+
const search = async (query = {}) => runRead(deps.requester, PARTITION_DESCRIPTOR.name, readUrl(query), decode);
|
|
2048
|
+
const searchAll = (query = {}) => paginateOnce(() => {
|
|
2049
|
+
const base = buildParams(query);
|
|
2050
|
+
return (count, start) => runRead(
|
|
2051
|
+
deps.requester,
|
|
2052
|
+
PARTITION_DESCRIPTOR.name,
|
|
2053
|
+
readUrlOf(deps.accessPoint, "partition", base, count, start),
|
|
2054
|
+
decode
|
|
2055
|
+
);
|
|
2056
|
+
});
|
|
2030
2057
|
return { search, searchAll };
|
|
2031
2058
|
};
|
|
2032
2059
|
|
|
@@ -2044,7 +2071,7 @@ var USER_DESCRIPTOR = {
|
|
|
2044
2071
|
fields: FIELDS14
|
|
2045
2072
|
};
|
|
2046
2073
|
var FIELD_MAP = new Map(Object.entries(FIELDS14));
|
|
2047
|
-
var
|
|
2074
|
+
var buildParams2 = (partition, q) => {
|
|
2048
2075
|
const p = new URLSearchParams();
|
|
2049
2076
|
p.set("partition", String(partition));
|
|
2050
2077
|
p.set("request_type", String(q.requestType ?? 1));
|
|
@@ -2055,18 +2082,27 @@ var buildUrl2 = (accessPoint, partition, q) => {
|
|
|
2055
2082
|
qualifyReadFields(USER_DESCRIPTOR.prefix, FIELD_MAP, q.field).join(",")
|
|
2056
2083
|
);
|
|
2057
2084
|
}
|
|
2058
|
-
|
|
2059
|
-
return apiUrl(accessPoint, "user", p);
|
|
2085
|
+
return p;
|
|
2060
2086
|
};
|
|
2061
2087
|
var createUserResource = (deps) => {
|
|
2062
2088
|
const decode = decoderFor(FIELDS14);
|
|
2063
|
-
const
|
|
2064
|
-
deps.
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2089
|
+
const readUrl = (q) => readUrlOf(
|
|
2090
|
+
deps.accessPoint,
|
|
2091
|
+
"user",
|
|
2092
|
+
buildParams2(deps.partition, q),
|
|
2093
|
+
q.count,
|
|
2094
|
+
q.start
|
|
2068
2095
|
);
|
|
2069
|
-
const
|
|
2096
|
+
const search = async (query = {}) => runRead(deps.requester, USER_DESCRIPTOR.name, readUrl(query), decode);
|
|
2097
|
+
const searchAll = (query = {}) => paginateOnce(() => {
|
|
2098
|
+
const base = buildParams2(deps.partition, query);
|
|
2099
|
+
return (count, start) => runRead(
|
|
2100
|
+
deps.requester,
|
|
2101
|
+
USER_DESCRIPTOR.name,
|
|
2102
|
+
readUrlOf(deps.accessPoint, "user", base, count, start),
|
|
2103
|
+
decode
|
|
2104
|
+
);
|
|
2105
|
+
});
|
|
2070
2106
|
const current = async () => (await search({ requestType: 0, count: 1 })).items[0];
|
|
2071
2107
|
return { search, searchAll, current };
|
|
2072
2108
|
};
|
|
@@ -2105,23 +2141,32 @@ var FIELD_DESCRIPTOR = {
|
|
|
2105
2141
|
prefix: "Field",
|
|
2106
2142
|
fields: FIELDS15
|
|
2107
2143
|
};
|
|
2108
|
-
var
|
|
2144
|
+
var buildParams3 = (partition, q) => {
|
|
2109
2145
|
const p = new URLSearchParams();
|
|
2110
2146
|
p.set("partition", String(partition));
|
|
2111
2147
|
p.set("resource", String(RESOURCE_VALUE[q.resource]));
|
|
2112
2148
|
p.set("active", String(q.active ?? -1));
|
|
2113
|
-
|
|
2114
|
-
return apiUrl(accessPoint, "field", p);
|
|
2149
|
+
return p;
|
|
2115
2150
|
};
|
|
2116
2151
|
var createFieldResource = (deps) => {
|
|
2117
2152
|
const decode = decoderFor(FIELDS15);
|
|
2118
|
-
const
|
|
2119
|
-
deps.
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2153
|
+
const readUrl = (q) => readUrlOf(
|
|
2154
|
+
deps.accessPoint,
|
|
2155
|
+
"field",
|
|
2156
|
+
buildParams3(deps.partition, q),
|
|
2157
|
+
q.count,
|
|
2158
|
+
q.start
|
|
2123
2159
|
);
|
|
2124
|
-
const
|
|
2160
|
+
const search = async (query) => runRead(deps.requester, FIELD_DESCRIPTOR.name, readUrl(query), decode);
|
|
2161
|
+
const searchAll = (query) => paginateOnce(() => {
|
|
2162
|
+
const base = buildParams3(deps.partition, query);
|
|
2163
|
+
return (count, start) => runRead(
|
|
2164
|
+
deps.requester,
|
|
2165
|
+
FIELD_DESCRIPTOR.name,
|
|
2166
|
+
readUrlOf(deps.accessPoint, "field", base, count, start),
|
|
2167
|
+
decode
|
|
2168
|
+
);
|
|
2169
|
+
});
|
|
2125
2170
|
return { search, searchAll };
|
|
2126
2171
|
};
|
|
2127
2172
|
|
|
@@ -2140,7 +2185,7 @@ var OPTION_DESCRIPTOR = {
|
|
|
2140
2185
|
prefix: "Option",
|
|
2141
2186
|
fields: FIELDS16
|
|
2142
2187
|
};
|
|
2143
|
-
var
|
|
2188
|
+
var buildUrl = (accessPoint, partition, q) => {
|
|
2144
2189
|
const p = new URLSearchParams();
|
|
2145
2190
|
p.set("partition", String(partition));
|
|
2146
2191
|
if (q.alias !== void 0) p.set("alias", q.alias);
|
|
@@ -2164,7 +2209,7 @@ var createOptionResource = (deps) => {
|
|
|
2164
2209
|
const search = async (query = {}) => deps.requester.request(
|
|
2165
2210
|
{
|
|
2166
2211
|
method: "GET",
|
|
2167
|
-
url:
|
|
2212
|
+
url: buildUrl(deps.accessPoint, deps.partition, query),
|
|
2168
2213
|
headers: {}
|
|
2169
2214
|
},
|
|
2170
2215
|
(body) => {
|