@exabugs/dynamodb-client 1.6.2 → 1.7.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 +13 -0
- package/dist/server/handler.cjs +126 -48
- package/dist/server/handler.cjs.map +4 -4
- package/dist/server/media-handler.cjs +2 -2
- package/dist/server/media-process-handler-dlq.cjs +131 -53
- package/dist/server/media-process-handler-dlq.cjs.map +4 -4
- package/dist/server/media-process-handler.cjs +131 -53
- package/dist/server/media-process-handler.cjs.map +4 -4
- package/dist/server/operations/find/utils.js +2 -2
- package/dist/server/operations/find/utils.js.map +1 -1
- package/dist/server/operations/updateMany.d.ts.map +1 -1
- package/dist/server/operations/updateMany.js +6 -76
- package/dist/server/operations/updateMany.js.map +1 -1
- package/dist/server/operations/updateOne.d.ts.map +1 -1
- package/dist/server/operations/updateOne.js +9 -1
- package/dist/server/operations/updateOne.js.map +1 -1
- package/dist/server/utils/mergePatch.d.ts +8 -0
- package/dist/server/utils/mergePatch.d.ts.map +1 -0
- package/dist/server/utils/mergePatch.js +127 -0
- package/dist/server/utils/mergePatch.js.map +1 -0
- package/dist/server/utils/timestamps.d.ts.map +1 -1
- package/dist/server/utils/timestamps.js +2 -1
- package/dist/server/utils/timestamps.js.map +1 -1
- package/dist/sharp-layer.zip +0 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.7.0] - 2026-10-12
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- 更新のドット記法パス(例: `sections.1.t`)が配列をオブジェクトに壊していた。配列は要素単位で更新し、範囲外・非数値の添字は 400 で拒否する。`__proto__` を含むパスも拒否する (#48)
|
|
15
|
+
- 更新時に `updatedAt` が変わらなかった(既存の値が優先されていた)。パッチ側に付与して毎回更新する。クライアントが値を指定した場合はそれを尊重し、`null` / `undefined` は未指定として扱う (#48)
|
|
16
|
+
- 解釈できないフィルタ指定が 500 (`CONFIG_ERROR`) になっていた。400 (`INVALID_FILTER`) を返す (#48)
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **`updatedAt` を明示しない更新で、値が毎回変わる**ようになった。`updatedAt` 順の一覧は更新のたびに並びが変わり、`updatedAt` のシャドーの再作成により 1 レコードあたりの書き込み項目が増える
|
|
21
|
+
- ドット記法で、存在しない(またはオブジェクト以外の)親に対する `null`(削除)は何もしなくなった(従来は `{a: null}` のような残骸が残っていた)
|
|
22
|
+
|
|
10
23
|
## [1.6.2] - 2026-10-11
|
|
11
24
|
|
|
12
25
|
### Security
|
package/dist/server/handler.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// @exabugs/dynamodb-client v1.
|
|
2
|
-
// Built: 2026-10-
|
|
1
|
+
// @exabugs/dynamodb-client v1.7.0
|
|
2
|
+
// Built: 2026-10-11T16:38:03.698Z
|
|
3
3
|
"use strict";
|
|
4
4
|
var __create = Object.create;
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
@@ -73,7 +73,7 @@ var init_types = __esm({
|
|
|
73
73
|
function isAppError(error2) {
|
|
74
74
|
return error2 instanceof AppError;
|
|
75
75
|
}
|
|
76
|
-
var AppError, AuthError, ConfigError, InvalidTokenError, ItemNotFoundError;
|
|
76
|
+
var AppError, AuthError, ConfigError, InvalidFilterError, InvalidTokenError, ItemNotFoundError;
|
|
77
77
|
var init_errors = __esm({
|
|
78
78
|
"src/shared/errors/index.ts"() {
|
|
79
79
|
"use strict";
|
|
@@ -117,6 +117,14 @@ var init_errors = __esm({
|
|
|
117
117
|
super("CONFIG_ERROR" /* CONFIG_ERROR */, message, 500, details);
|
|
118
118
|
}
|
|
119
119
|
};
|
|
120
|
+
InvalidFilterError = class extends AppError {
|
|
121
|
+
static {
|
|
122
|
+
__name(this, "InvalidFilterError");
|
|
123
|
+
}
|
|
124
|
+
constructor(message, details) {
|
|
125
|
+
super("INVALID_FILTER" /* INVALID_FILTER */, message, 400, details);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
120
128
|
InvalidTokenError = class extends AppError {
|
|
121
129
|
static {
|
|
122
130
|
__name(this, "InvalidTokenError");
|
|
@@ -34459,7 +34467,7 @@ function parseFilters(filter) {
|
|
|
34459
34467
|
fieldKey,
|
|
34460
34468
|
error: error2 instanceof Error ? error2.message : String(error2)
|
|
34461
34469
|
});
|
|
34462
|
-
throw new
|
|
34470
|
+
throw new InvalidFilterError(`Invalid filter field syntax: ${fieldKey}`, {
|
|
34463
34471
|
field: fieldKey,
|
|
34464
34472
|
error: error2 instanceof Error ? error2.message : String(error2)
|
|
34465
34473
|
});
|
|
@@ -35918,8 +35926,8 @@ function addUpdateTimestamp(data) {
|
|
|
35918
35926
|
}
|
|
35919
35927
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
35920
35928
|
return {
|
|
35921
|
-
|
|
35922
|
-
|
|
35929
|
+
...data,
|
|
35930
|
+
[timestampFields.updatedAt]: data[timestampFields.updatedAt] ?? now
|
|
35923
35931
|
};
|
|
35924
35932
|
}
|
|
35925
35933
|
var init_timestamps = __esm({
|
|
@@ -36219,51 +36227,118 @@ var init_insertMany = __esm({
|
|
|
36219
36227
|
}
|
|
36220
36228
|
});
|
|
36221
36229
|
|
|
36222
|
-
// src/server/
|
|
36223
|
-
|
|
36224
|
-
|
|
36225
|
-
|
|
36226
|
-
|
|
36227
|
-
|
|
36228
|
-
|
|
36230
|
+
// src/server/utils/mergePatch.ts
|
|
36231
|
+
function isPlainObject(value) {
|
|
36232
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
36233
|
+
}
|
|
36234
|
+
function invalidPath(path, reason) {
|
|
36235
|
+
return new AppError("VALIDATION_ERROR" /* VALIDATION_ERROR */, `Invalid patch path "${path}": ${reason}`, 400, {
|
|
36236
|
+
path
|
|
36237
|
+
});
|
|
36238
|
+
}
|
|
36239
|
+
function mergeInto(target, patch) {
|
|
36229
36240
|
for (const [key, value] of Object.entries(patch)) {
|
|
36230
36241
|
if (key.includes(".")) {
|
|
36231
|
-
|
|
36232
|
-
let current = result;
|
|
36233
|
-
for (let i4 = 0; i4 < keys.length - 1; i4++) {
|
|
36234
|
-
const k4 = keys[i4];
|
|
36235
|
-
if (!(k4 in current)) {
|
|
36236
|
-
current[k4] = {};
|
|
36237
|
-
} else if (typeof current[k4] !== "object" || Array.isArray(current[k4])) {
|
|
36238
|
-
current[k4] = {};
|
|
36239
|
-
}
|
|
36240
|
-
current = current[k4];
|
|
36241
|
-
}
|
|
36242
|
-
const lastKey = keys[keys.length - 1];
|
|
36243
|
-
current[lastKey] = value;
|
|
36242
|
+
setByPath(target, key, value);
|
|
36244
36243
|
} else {
|
|
36245
|
-
|
|
36244
|
+
setKey(target, key, value, key);
|
|
36246
36245
|
}
|
|
36247
36246
|
}
|
|
36248
|
-
return result;
|
|
36249
36247
|
}
|
|
36250
|
-
function
|
|
36251
|
-
|
|
36252
|
-
|
|
36253
|
-
|
|
36254
|
-
|
|
36255
|
-
|
|
36256
|
-
|
|
36257
|
-
|
|
36258
|
-
|
|
36259
|
-
|
|
36260
|
-
|
|
36248
|
+
function setKey(target, key, value, path) {
|
|
36249
|
+
if (key === "__proto__") {
|
|
36250
|
+
throw invalidPath(path, "forbidden segment");
|
|
36251
|
+
}
|
|
36252
|
+
if (value === null) {
|
|
36253
|
+
delete target[key];
|
|
36254
|
+
} else if (isPlainObject(value) && isPlainObject(target[key])) {
|
|
36255
|
+
mergeInto(target[key], value);
|
|
36256
|
+
} else {
|
|
36257
|
+
target[key] = value;
|
|
36258
|
+
}
|
|
36259
|
+
}
|
|
36260
|
+
function setByPath(target, path, value) {
|
|
36261
|
+
const keys = path.split(".");
|
|
36262
|
+
if (keys.some((k4) => k4 === "")) {
|
|
36263
|
+
throw invalidPath(path, "empty segment");
|
|
36264
|
+
}
|
|
36265
|
+
let current = target;
|
|
36266
|
+
for (let i4 = 0; i4 < keys.length; i4++) {
|
|
36267
|
+
const key = keys[i4];
|
|
36268
|
+
const isLast = i4 === keys.length - 1;
|
|
36269
|
+
if (Array.isArray(current)) {
|
|
36270
|
+
if (!/^(0|[1-9]\d*)$/.test(key) || Number(key) >= current.length) {
|
|
36271
|
+
throw invalidPath(path, `"${key}" is not a valid index (length ${current.length})`);
|
|
36272
|
+
}
|
|
36273
|
+
const index = Number(key);
|
|
36274
|
+
if (isLast) {
|
|
36275
|
+
if (value === null) {
|
|
36276
|
+
throw invalidPath(path, "array elements cannot be deleted by path");
|
|
36277
|
+
}
|
|
36278
|
+
const existing = current[index];
|
|
36279
|
+
if (isPlainObject(value) && isPlainObject(existing)) {
|
|
36280
|
+
mergeInto(existing, value);
|
|
36281
|
+
} else {
|
|
36282
|
+
current[index] = value;
|
|
36283
|
+
}
|
|
36284
|
+
return;
|
|
36285
|
+
}
|
|
36286
|
+
const next2 = current[index];
|
|
36287
|
+
if (isPlainObject(next2) || Array.isArray(next2)) {
|
|
36288
|
+
current = next2;
|
|
36289
|
+
} else {
|
|
36290
|
+
if (value === null) {
|
|
36291
|
+
return;
|
|
36292
|
+
}
|
|
36293
|
+
const created = {};
|
|
36294
|
+
current[index] = created;
|
|
36295
|
+
current = created;
|
|
36296
|
+
}
|
|
36297
|
+
continue;
|
|
36298
|
+
}
|
|
36299
|
+
if (isLast) {
|
|
36300
|
+
setKey(current, key, value, path);
|
|
36301
|
+
return;
|
|
36302
|
+
}
|
|
36303
|
+
if (key === "__proto__") {
|
|
36304
|
+
throw invalidPath(path, "forbidden segment");
|
|
36305
|
+
}
|
|
36306
|
+
const next = current[key];
|
|
36307
|
+
if (isPlainObject(next) || Array.isArray(next)) {
|
|
36308
|
+
current = next;
|
|
36261
36309
|
} else {
|
|
36262
|
-
|
|
36310
|
+
if (value === null) {
|
|
36311
|
+
return;
|
|
36312
|
+
}
|
|
36313
|
+
const created = {};
|
|
36314
|
+
current[key] = created;
|
|
36315
|
+
current = created;
|
|
36263
36316
|
}
|
|
36264
36317
|
}
|
|
36318
|
+
}
|
|
36319
|
+
function applyJsonMergePatch(target, patch) {
|
|
36320
|
+
const result = structuredClone(target);
|
|
36321
|
+
mergeInto(result, structuredClone(patch));
|
|
36265
36322
|
return result;
|
|
36266
36323
|
}
|
|
36324
|
+
var init_mergePatch = __esm({
|
|
36325
|
+
"src/server/utils/mergePatch.ts"() {
|
|
36326
|
+
"use strict";
|
|
36327
|
+
init_shared();
|
|
36328
|
+
__name(isPlainObject, "isPlainObject");
|
|
36329
|
+
__name(invalidPath, "invalidPath");
|
|
36330
|
+
__name(mergeInto, "mergeInto");
|
|
36331
|
+
__name(setKey, "setKey");
|
|
36332
|
+
__name(setByPath, "setByPath");
|
|
36333
|
+
__name(applyJsonMergePatch, "applyJsonMergePatch");
|
|
36334
|
+
}
|
|
36335
|
+
});
|
|
36336
|
+
|
|
36337
|
+
// src/server/operations/updateMany.ts
|
|
36338
|
+
var updateMany_exports = {};
|
|
36339
|
+
__export(updateMany_exports, {
|
|
36340
|
+
handleUpdateMany: () => handleUpdateMany
|
|
36341
|
+
});
|
|
36267
36342
|
async function handleUpdateMany(resource, params, requestId) {
|
|
36268
36343
|
const { data: patchData, options } = params;
|
|
36269
36344
|
const upsert = options?.upsert ?? false;
|
|
@@ -36391,11 +36466,9 @@ async function handleUpdateMany(resource, params, requestId) {
|
|
|
36391
36466
|
const id = existingData.id;
|
|
36392
36467
|
const oldShadowKeys = item.__shadowKeys ?? existingData.__shadowKeys ?? [];
|
|
36393
36468
|
const actualPatchData2 = patchData.$set ? patchData.$set : patchData;
|
|
36394
|
-
const
|
|
36395
|
-
const
|
|
36396
|
-
|
|
36397
|
-
id
|
|
36398
|
-
});
|
|
36469
|
+
const timestampedPatch = addUpdateTimestamp(actualPatchData2);
|
|
36470
|
+
const mergedData = applyJsonMergePatch(removeShadowKeys(existingData), timestampedPatch);
|
|
36471
|
+
const updatedData = { ...mergedData, id };
|
|
36399
36472
|
const newShadowRecords = generateShadowRecords(updatedData, resource, shadowConfig);
|
|
36400
36473
|
const newShadowKeys = newShadowRecords.map((shadow) => shadow.SK);
|
|
36401
36474
|
const mainSK = generateMainRecordSK2(id);
|
|
@@ -36676,10 +36749,9 @@ var init_updateMany = __esm({
|
|
|
36676
36749
|
init_chunking();
|
|
36677
36750
|
init_cost_tracker();
|
|
36678
36751
|
init_dynamodb3();
|
|
36752
|
+
init_mergePatch();
|
|
36679
36753
|
init_timestamps();
|
|
36680
36754
|
logger18 = createLogger({ service: "records-lambda" });
|
|
36681
|
-
__name(expandDotNotation, "expandDotNotation");
|
|
36682
|
-
__name(applyJsonMergePatch, "applyJsonMergePatch");
|
|
36683
36755
|
__name(handleUpdateMany, "handleUpdateMany");
|
|
36684
36756
|
__name(getPreparationErrorCode2, "getPreparationErrorCode");
|
|
36685
36757
|
}
|
|
@@ -37115,6 +37187,9 @@ async function handleUpdateOne(resource, params, requestId) {
|
|
|
37115
37187
|
if (updateManyResult.count === 0) {
|
|
37116
37188
|
const error2 = Object.values(updateManyResult.errors || {})[0];
|
|
37117
37189
|
if (error2) {
|
|
37190
|
+
if (error2.code === "VALIDATION_ERROR" /* VALIDATION_ERROR */) {
|
|
37191
|
+
throw new AppError("VALIDATION_ERROR" /* VALIDATION_ERROR */, error2.message ?? "Invalid patch", 400);
|
|
37192
|
+
}
|
|
37118
37193
|
throw new Error(`Failed to update record: ${error2.message}`);
|
|
37119
37194
|
} else {
|
|
37120
37195
|
throw new Error(`Failed to update record: ${targetId}`);
|
|
@@ -37154,6 +37229,9 @@ async function handleUpdateOne(resource, params, requestId) {
|
|
|
37154
37229
|
if (updateManyResult.count === 0) {
|
|
37155
37230
|
const error2 = Object.values(updateManyResult.errors || {})[0];
|
|
37156
37231
|
if (error2) {
|
|
37232
|
+
if (error2.code === "VALIDATION_ERROR" /* VALIDATION_ERROR */) {
|
|
37233
|
+
throw new AppError("VALIDATION_ERROR" /* VALIDATION_ERROR */, error2.message ?? "Invalid patch", 400);
|
|
37234
|
+
}
|
|
37157
37235
|
throw new Error(`Failed to update record: ${error2.message}`);
|
|
37158
37236
|
} else {
|
|
37159
37237
|
throw new Error(`No records found matching filter`);
|
|
@@ -38706,7 +38784,7 @@ async function handler(event) {
|
|
|
38706
38784
|
return createCorsResponse(HTTP_STATUS.OK);
|
|
38707
38785
|
}
|
|
38708
38786
|
if (event.requestContext.http.method === "GET" && event.requestContext.http.path === "/version") {
|
|
38709
|
-
const version = "1.
|
|
38787
|
+
const version = "1.7.0";
|
|
38710
38788
|
return createSuccessResponse({ version, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, requestId);
|
|
38711
38789
|
}
|
|
38712
38790
|
if (event.requestContext.http.method !== "POST") {
|