@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 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.0...HEAD
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
- if (e instanceof PortersNetworkError && write && !idempotent) throw e;
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 buildReadUrl = (accessPoint, partition, path, q, ctx) => {
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
- appendPaging(p, q.count, q.start);
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 readUrl = (q) => buildReadUrl(deps.accessPoint, deps.partition, config.path, q, {
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 = {}) => paginate((count, start) => search({ ...query, count, start }));
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 buildUrl = (accessPoint, q) => {
2039
+ var buildParams = (q) => {
2016
2040
  const p = new URLSearchParams();
2017
2041
  p.set("request_type", String(q.requestType ?? 1));
2018
- appendPaging(p, q.count, q.start);
2019
- return apiUrl(accessPoint, "partition", p);
2042
+ return p;
2020
2043
  };
2021
2044
  var createPartitionResource = (deps) => {
2022
2045
  const decode = decoderFor(FIELDS13);
2023
- const search = async (query = {}) => runRead(
2024
- deps.requester,
2025
- PARTITION_DESCRIPTOR.name,
2026
- buildUrl(deps.accessPoint, query),
2027
- decode
2028
- );
2029
- const searchAll = (query = {}) => paginate((count, start) => search({ ...query, count, start }));
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 buildUrl2 = (accessPoint, partition, q) => {
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
- appendPaging(p, q.count, q.start);
2059
- return apiUrl(accessPoint, "user", p);
2085
+ return p;
2060
2086
  };
2061
2087
  var createUserResource = (deps) => {
2062
2088
  const decode = decoderFor(FIELDS14);
2063
- const search = async (query = {}) => runRead(
2064
- deps.requester,
2065
- USER_DESCRIPTOR.name,
2066
- buildUrl2(deps.accessPoint, deps.partition, query),
2067
- decode
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 searchAll = (query = {}) => paginate((count, start) => search({ ...query, count, start }));
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 buildUrl3 = (accessPoint, partition, q) => {
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
- appendPaging(p, q.count, q.start);
2114
- return apiUrl(accessPoint, "field", p);
2149
+ return p;
2115
2150
  };
2116
2151
  var createFieldResource = (deps) => {
2117
2152
  const decode = decoderFor(FIELDS15);
2118
- const search = async (query) => runRead(
2119
- deps.requester,
2120
- FIELD_DESCRIPTOR.name,
2121
- buildUrl3(deps.accessPoint, deps.partition, query),
2122
- decode
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 searchAll = (query) => paginate((count, start) => search({ ...query, count, start }));
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 buildUrl4 = (accessPoint, partition, q) => {
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: buildUrl4(deps.accessPoint, deps.partition, query),
2212
+ url: buildUrl(deps.accessPoint, deps.partition, query),
2168
2213
  headers: {}
2169
2214
  },
2170
2215
  (body) => {