@exabugs/dynamodb-client 1.3.36 → 1.3.38

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
@@ -7,6 +7,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.3.38] - 2026-01-11
11
+
12
+ ### Changed
13
+
14
+ - **テストカバレッジ大幅向上**: 86.37%達成(Phase 2目標80%を超過)
15
+ - dataProviderの完全テスト実装(17テスト、カバレッジ86.37%)
16
+ - react-admin v5 APIに対応(`pageInfo`形式のレスポンス)
17
+ - 全481テスト成功
18
+
19
+ ### Removed
20
+
21
+ - **useManyToManyTransformフック削除**: 不要なコード削除
22
+ - Many-to-many関係の処理はdataProvider内で統合済み
23
+ - コードの重複を削減し、保守性を向上
24
+
25
+ ## [1.3.37] - 2026-01-09
26
+
27
+ ### Security
28
+
29
+ - **updateOne情報漏洩の修正**: ADR 001に基づきセキュリティ脆弱性を修正
30
+ - update権限のみでread権限がない場合の情報漏洩を防止
31
+ - `findOne`呼び出しを削除し、`{ id, ...更新したフィールドのみ }` を返却
32
+ - UpdateOperators形式(`$set`)にも対応
33
+ - パフォーマンス向上: 不要な`findOne`クエリを削減
34
+
35
+ ### Added
36
+
37
+ - **ADR 001**: 最小限のレスポンスデータ(セキュリティ重視)を文書化
38
+ - セキュリティ最優先の設計決定
39
+ - 全操作で最小限の情報のみ返す統一ポリシー
40
+
41
+ ### Changed
42
+
43
+ - **破壊的変更**: `updateOne`のレスポンス形式が変更
44
+ - 従来: 完全なレコードを返却
45
+ - 新仕様: `{ id, ...更新したフィールドのみ }` を返却
46
+ - 完全なデータが必要な場合は追加の`findOne`呼び出しが必要
47
+
10
48
  ## [1.3.36] - 2026-01-09
11
49
 
12
50
  ### Refactored
@@ -1,5 +1,5 @@
1
- // @exabugs/dynamodb-client v1.3.36
2
- // Built: 2026-01-09T00:36:32.014Z
1
+ // @exabugs/dynamodb-client v1.3.38
2
+ // Built: 2026-01-10T23:33:04.543Z
3
3
  "use strict";
4
4
  var __create = Object.create;
5
5
  var __defProp = Object.defineProperty;
@@ -31681,8 +31681,14 @@ function applyJsonMergePatch(target, patch) {
31681
31681
  return result;
31682
31682
  }
31683
31683
  async function handleUpdateMany(resource, params, requestId) {
31684
- const { data: patchData } = params;
31684
+ const { data: patchData, options } = params;
31685
+ const upsert = options?.upsert ?? false;
31685
31686
  const startTime = Date.now();
31687
+ logger18.debug("Executing updateMany", {
31688
+ requestId,
31689
+ resource,
31690
+ upsert
31691
+ });
31686
31692
  let ids;
31687
31693
  if ("ids" in params) {
31688
31694
  ids = params.ids;
@@ -31742,6 +31748,15 @@ async function handleUpdateMany(resource, params, requestId) {
31742
31748
  })
31743
31749
  );
31744
31750
  const notFoundIds = ids.filter((id) => !existingIds.has(id));
31751
+ if (!upsert && notFoundIds.length > 0) {
31752
+ logger18.warn("Records not found (upsert: false)", {
31753
+ requestId,
31754
+ resource,
31755
+ notFoundCount: notFoundIds.length,
31756
+ notFoundIds: notFoundIds.slice(0, 10)
31757
+ // 最初の10件のみログ
31758
+ });
31759
+ }
31745
31760
  const shadowConfig = getShadowConfig();
31746
31761
  const preparedRecords = [];
31747
31762
  const preparationFailedIds = [];
@@ -31751,8 +31766,8 @@ async function handleUpdateMany(resource, params, requestId) {
31751
31766
  const existingData = item.data;
31752
31767
  const id = existingData.id;
31753
31768
  const oldShadowKeys = existingData.__shadowKeys || [];
31754
- const actualPatchData = patchData.$set ? patchData.$set : patchData;
31755
- const mergedData = applyJsonMergePatch(removeShadowKeys(existingData), actualPatchData);
31769
+ const actualPatchData2 = patchData.$set ? patchData.$set : patchData;
31770
+ const mergedData = applyJsonMergePatch(removeShadowKeys(existingData), actualPatchData2);
31756
31771
  const updatedData = addUpdateTimestamp({
31757
31772
  ...mergedData,
31758
31773
  id
@@ -31856,13 +31871,19 @@ async function handleUpdateMany(resource, params, requestId) {
31856
31871
  errors: chunkErrors
31857
31872
  } = await executeChunks(chunks, executeChunk, (record) => record.id);
31858
31873
  const successIds = {};
31874
+ const items = [];
31859
31875
  const failedIdsMap = {};
31860
31876
  const errorsMap = {};
31877
+ const actualPatchData = patchData.$set ? patchData.$set : patchData;
31861
31878
  const successIdSet = new Set(successRecords.map((r4) => r4.id));
31862
31879
  for (let i4 = 0; i4 < ids.length; i4++) {
31863
31880
  const id = ids[i4];
31864
31881
  if (successIdSet.has(id)) {
31865
31882
  successIds[i4] = id;
31883
+ items.push({
31884
+ id,
31885
+ ...actualPatchData
31886
+ });
31866
31887
  }
31867
31888
  }
31868
31889
  for (let i4 = 0; i4 < ids.length; i4++) {
@@ -31938,7 +31959,9 @@ async function handleUpdateMany(resource, params, requestId) {
31938
31959
  count,
31939
31960
  successIds,
31940
31961
  failedIds: failedIdsMap,
31941
- errors: errorsMap
31962
+ errors: errorsMap,
31963
+ items
31964
+ // 更新したフィールドのみを含むレコード配列(ADR 001)
31942
31965
  };
31943
31966
  }
31944
31967
  function getPreparationErrorCode2(error2) {
@@ -32223,9 +32246,8 @@ var logger19 = createLogger({ service: "records-lambda" });
32223
32246
  async function handleUpdateOne(resource, params, requestId) {
32224
32247
  const { data: patchData, options } = params;
32225
32248
  const { handleUpdateMany: handleUpdateMany2 } = await Promise.resolve().then(() => (init_updateMany(), updateMany_exports));
32226
- let targetId;
32227
32249
  if ("id" in params) {
32228
- targetId = params.id;
32250
+ const targetId = params.id;
32229
32251
  logger19.debug("Executing updateOne with id", {
32230
32252
  requestId,
32231
32253
  resource,
@@ -32236,7 +32258,8 @@ async function handleUpdateOne(resource, params, requestId) {
32236
32258
  resource,
32237
32259
  {
32238
32260
  ids: [targetId],
32239
- data: patchData
32261
+ data: patchData,
32262
+ options
32240
32263
  },
32241
32264
  requestId
32242
32265
  );
@@ -32248,9 +32271,10 @@ async function handleUpdateOne(resource, params, requestId) {
32248
32271
  throw new Error(`Failed to update record: ${targetId}`);
32249
32272
  }
32250
32273
  }
32251
- const { handleFindOne: handleFindOne2 } = await Promise.resolve().then(() => (init_findOne(), findOne_exports));
32252
- const updatedRecord = await handleFindOne2(resource, { id: targetId }, requestId);
32253
- return updatedRecord;
32274
+ if (!updateManyResult.items || updateManyResult.items.length === 0) {
32275
+ throw new Error("updateMany did not return items");
32276
+ }
32277
+ return updateManyResult.items[0];
32254
32278
  } else {
32255
32279
  logger19.debug("Executing updateOne with filter", {
32256
32280
  requestId,
@@ -32262,7 +32286,8 @@ async function handleUpdateOne(resource, params, requestId) {
32262
32286
  resource,
32263
32287
  {
32264
32288
  filter: params.filter,
32265
- data: patchData
32289
+ data: patchData,
32290
+ options
32266
32291
  },
32267
32292
  requestId
32268
32293
  );
@@ -32274,13 +32299,10 @@ async function handleUpdateOne(resource, params, requestId) {
32274
32299
  throw new Error(`No records found matching filter`);
32275
32300
  }
32276
32301
  }
32277
- const updatedId = Object.values(updateManyResult.successIds)[0];
32278
- if (!updatedId) {
32279
- throw new Error("Failed to get updated record ID");
32302
+ if (!updateManyResult.items || updateManyResult.items.length === 0) {
32303
+ throw new Error("updateMany did not return items");
32280
32304
  }
32281
- const { handleFindOne: handleFindOne2 } = await Promise.resolve().then(() => (init_findOne(), findOne_exports));
32282
- const updatedRecord = await handleFindOne2(resource, { id: updatedId }, requestId);
32283
- return updatedRecord;
32305
+ return updateManyResult.items[0];
32284
32306
  }
32285
32307
  }
32286
32308
  __name(handleUpdateOne, "handleUpdateOne");
@@ -33905,7 +33927,7 @@ async function handler(event) {
33905
33927
  return createCorsResponse(HTTP_STATUS.OK);
33906
33928
  }
33907
33929
  if (event.requestContext.http.method === "GET" && event.requestContext.http.path === "/version") {
33908
- const version = "1.3.36";
33930
+ const version = "1.3.38";
33909
33931
  return createSuccessResponse({ version, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, requestId);
33910
33932
  }
33911
33933
  if (event.requestContext.http.method !== "POST") {