@exabugs/dynamodb-client 1.3.20 → 1.3.22

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,52 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.3.22] - 2026-01-05
11
+
12
+ ### Added
13
+
14
+ - **$setOnInsert オペレータ**: MongoDB互換のupsert専用フィールド設定機能
15
+ - upsert時のinsert専用フィールドを指定可能(`$setOnInsert`)
16
+ - update時は`$setOnInsert`が無視され、`$set`のみが適用される
17
+ - `$set`と`$setOnInsert`で同じフィールドを指定した場合、`$set`が優先
18
+ - タイムスタンプ管理(`createdAt`は初回のみ、`updatedAt`は常に更新)が明確に
19
+ - MongoDB公式ドキュメントと完全互換
20
+
21
+ ### Changed
22
+
23
+ - **UpdateOperators型**: `$setOnInsert?: Partial<T>` フィールドを追加
24
+ - **handleUpsertCreate**: `$set`と`$setOnInsert`をマージして新規作成(`$set`が優先)
25
+ - **handleUpsertUpdate**: `$setOnInsert`を無視して`$set`のみを適用
26
+
27
+ ### Documentation
28
+
29
+ - **API.md**: `$setOnInsert`オペレータの詳細な説明を追加
30
+ - 動作仕様(insert時/update時の挙動)
31
+ - 使用例とコードスニペット
32
+ - MongoDB互換性の説明
33
+ - ユースケース(タイムスタンプ管理、デフォルト値設定、初期ステータス)
34
+
35
+ ### Technical
36
+
37
+ - **テスト**: 包括的なテストカバレッジ(単体テスト・統合テスト)
38
+ - insert時に`$set`と`$setOnInsert`の両方を適用するテスト
39
+ - update時に`$setOnInsert`を無視するテスト
40
+ - `$set`が`$setOnInsert`より優先されるテスト
41
+ - `createdAt`の保持テスト
42
+ - シャドウレコード生成の確認
43
+ - 後方互換性テスト(従来のパッチ形式)
44
+
45
+ ## [1.3.21] - 2026-01-04
46
+
47
+ ### Changed
48
+
49
+ - **Shadow仕様変更**: オブジェクト型はシャドウを作成しない仕様に変更
50
+ - `inferFieldType`: オブジェクト型に対して`null`を返すように変更
51
+ - `generateShadowRecords`: オブジェクト型を明示的にスキップ
52
+ - `formatFieldValue`: `object`型のケースを削除
53
+ - 理由: オブジェクトは構造が複雑で、シャドウキーとして適切でない
54
+ - 例外: GeoCoordinates型(`{latitude, longitude}`)は地理空間検索用にGeoHashシャドウを生成
55
+
10
56
  ## [1.3.20] - 2026-01-03
11
57
 
12
58
  ### Added
@@ -1,5 +1,5 @@
1
- // @exabugs/dynamodb-client v1.3.20
2
- // Built: 2026-01-02T23:49:33.625Z
1
+ // @exabugs/dynamodb-client v1.3.22
2
+ // Built: 2026-01-05T00:55:07.904Z
3
3
  "use strict";
4
4
  var __create = Object.create;
5
5
  var __defProp = Object.defineProperty;
@@ -29373,7 +29373,7 @@ function inferFieldType(value) {
29373
29373
  return "array";
29374
29374
  }
29375
29375
  if (typeof value === "object") {
29376
- return "object";
29376
+ return null;
29377
29377
  }
29378
29378
  return null;
29379
29379
  }
@@ -29451,8 +29451,7 @@ function formatFieldValue2(type, value, config) {
29451
29451
  return formatDatetime2(value);
29452
29452
  case "boolean":
29453
29453
  return formatBoolean2(value);
29454
- case "array":
29455
- case "object": {
29454
+ case "array": {
29456
29455
  const normalized = normalizeJson(value);
29457
29456
  const jsonStr = JSON.stringify(normalized);
29458
29457
  const maxBytes = config.stringMaxBytes * 2;
@@ -29480,17 +29479,19 @@ function generateShadowRecords(record, resourceName, config) {
29480
29479
  if (value === null || value === void 0) {
29481
29480
  continue;
29482
29481
  }
29483
- if (isGeoCoordinates(value)) {
29484
- const geohash = encodeGeoHash(
29485
- value.latitude,
29486
- value.longitude,
29487
- DEFAULT_GEOHASH_CONFIG.shadowPrecision
29488
- );
29489
- const sk2 = `${fieldName}#${geohash}#id#${record.id}`;
29490
- shadows.push({
29491
- PK: resourceName,
29492
- SK: sk2
29493
- });
29482
+ if (typeof value === "object" && !Array.isArray(value)) {
29483
+ if (isGeoCoordinates(value)) {
29484
+ const geohash = encodeGeoHash(
29485
+ value.latitude,
29486
+ value.longitude,
29487
+ DEFAULT_GEOHASH_CONFIG.shadowPrecision
29488
+ );
29489
+ const sk2 = `${fieldName}#${geohash}#id#${record.id}`;
29490
+ shadows.push({
29491
+ PK: resourceName,
29492
+ SK: sk2
29493
+ });
29494
+ }
29494
29495
  continue;
29495
29496
  }
29496
29497
  const type = inferFieldType(value);
@@ -31503,7 +31504,8 @@ async function handleUpdateMany(resource, params, requestId) {
31503
31504
  const existingData = item.data;
31504
31505
  const id = existingData.id;
31505
31506
  const oldShadowKeys = existingData.__shadowKeys || [];
31506
- const mergedData = applyJsonMergePatch(removeShadowKeys(existingData), patchData);
31507
+ const actualPatchData = patchData.$set ? patchData.$set : patchData;
31508
+ const mergedData = applyJsonMergePatch(removeShadowKeys(existingData), actualPatchData);
31507
31509
  const updatedData = addUpdateTimestamp({
31508
31510
  ...mergedData,
31509
31511
  id
@@ -31761,7 +31763,13 @@ async function handleUpdateOne(resource, params, requestId) {
31761
31763
  }
31762
31764
  __name(handleUpdateOne, "handleUpdateOne");
31763
31765
  async function handleUpsertCreate(resource, id, data2, requestId) {
31764
- const recordData = addCreateTimestamps({ ...data2, id });
31766
+ const $set = data2.$set || {};
31767
+ const $setOnInsert = data2.$setOnInsert || {};
31768
+ const mergedData = {
31769
+ ...$setOnInsert,
31770
+ ...$set
31771
+ };
31772
+ const recordData = addCreateTimestamps({ ...mergedData, id });
31765
31773
  const shadowConfig = getShadowConfig();
31766
31774
  const shadowRecords = generateShadowRecords(recordData, resource, shadowConfig);
31767
31775
  const shadowKeys = shadowRecords.map((shadow) => shadow.SK);
@@ -31810,7 +31818,8 @@ __name(handleUpsertCreate, "handleUpsertCreate");
31810
31818
  async function handleUpsertUpdate(resource, id, existingItem, patchData, requestId) {
31811
31819
  const existingData = existingItem.data;
31812
31820
  const oldShadowKeys = existingData.__shadowKeys || [];
31813
- const mergedData = applyJsonMergePatch2(removeShadowKeys(existingData), patchData);
31821
+ const actualPatchData = patchData.$set ? patchData.$set : patchData;
31822
+ const mergedData = applyJsonMergePatch2(removeShadowKeys(existingData), actualPatchData);
31814
31823
  const updatedData = addUpdateTimestamp({
31815
31824
  ...mergedData,
31816
31825
  id
@@ -33458,7 +33467,7 @@ async function handler(event) {
33458
33467
  return createCorsResponse(HTTP_STATUS.OK);
33459
33468
  }
33460
33469
  if (event.requestContext.http.method === "GET" && event.requestContext.http.path === "/version") {
33461
- const version = "1.3.20";
33470
+ const version = "1.3.22";
33462
33471
  return createSuccessResponse({ version, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, requestId);
33463
33472
  }
33464
33473
  if (event.requestContext.http.method !== "POST") {