@exabugs/dynamodb-client 0.2.2 → 0.3.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +92 -28
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +1 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/server/handler.cjs +137 -178
  8. package/dist/server/handler.cjs.map +4 -4
  9. package/dist/server/handler.d.ts.map +1 -1
  10. package/dist/server/handler.js +2 -15
  11. package/dist/server/handler.js.map +1 -1
  12. package/dist/server/operations/find.d.ts.map +1 -1
  13. package/dist/server/operations/find.js +5 -12
  14. package/dist/server/operations/find.js.map +1 -1
  15. package/dist/server/operations/findManyReference.d.ts.map +1 -1
  16. package/dist/server/operations/findManyReference.js +3 -10
  17. package/dist/server/operations/findManyReference.js.map +1 -1
  18. package/dist/server/operations/insertMany.d.ts.map +1 -1
  19. package/dist/server/operations/insertMany.js +3 -8
  20. package/dist/server/operations/insertMany.js.map +1 -1
  21. package/dist/server/operations/insertOne.d.ts.map +1 -1
  22. package/dist/server/operations/insertOne.js +3 -8
  23. package/dist/server/operations/insertOne.js.map +1 -1
  24. package/dist/server/operations/updateMany.d.ts.map +1 -1
  25. package/dist/server/operations/updateMany.js +3 -4
  26. package/dist/server/operations/updateMany.js.map +1 -1
  27. package/dist/server/operations/updateOne.d.ts.map +1 -1
  28. package/dist/server/operations/updateOne.js +3 -4
  29. package/dist/server/operations/updateOne.js.map +1 -1
  30. package/dist/server/shadow/config.d.ts +18 -112
  31. package/dist/server/shadow/config.d.ts.map +1 -1
  32. package/dist/server/shadow/config.js +33 -131
  33. package/dist/server/shadow/config.js.map +1 -1
  34. package/dist/server/shadow/generator.d.ts +92 -43
  35. package/dist/server/shadow/generator.d.ts.map +1 -1
  36. package/dist/server/shadow/generator.js +175 -56
  37. package/dist/server/shadow/generator.js.map +1 -1
  38. package/dist/server/shadow/index.d.ts +4 -3
  39. package/dist/server/shadow/index.d.ts.map +1 -1
  40. package/dist/server/shadow/index.js +4 -2
  41. package/dist/server/shadow/index.js.map +1 -1
  42. package/dist/server/shadow/typeInference.d.ts +61 -0
  43. package/dist/server/shadow/typeInference.d.ts.map +1 -0
  44. package/dist/server/shadow/typeInference.js +94 -0
  45. package/dist/server/shadow/typeInference.js.map +1 -0
  46. package/dist/server/shadow/types.d.ts +1 -1
  47. package/dist/server/shadow/types.d.ts.map +1 -1
  48. package/dist/server/utils/timestamps.d.ts.map +1 -1
  49. package/dist/server/utils/timestamps.js +3 -26
  50. package/dist/server/utils/timestamps.js.map +1 -1
  51. package/dist/server/utils/ttl.d.ts.map +1 -1
  52. package/dist/server/utils/ttl.js +11 -9
  53. package/dist/server/utils/ttl.js.map +1 -1
  54. package/dist/server/utils/validation.d.ts +14 -8
  55. package/dist/server/utils/validation.d.ts.map +1 -1
  56. package/dist/server/utils/validation.js +19 -26
  57. package/dist/server/utils/validation.js.map +1 -1
  58. package/package.json +1 -1
  59. package/terraform/README.md +3 -3
  60. package/terraform/main.tf +1 -1
  61. package/dist/scripts/generate-shadow-config.d.ts +0 -3
  62. package/dist/scripts/generate-shadow-config.d.ts.map +0 -1
  63. package/dist/scripts/generate-shadow-config.js +0 -159
  64. package/dist/scripts/generate-shadow-config.js.map +0 -1
@@ -1,3 +1,78 @@
1
+ import { inferFieldType } from './typeInference.js';
2
+ /**
3
+ * JSONオブジェクトのフィールドを正規化
4
+ *
5
+ * フィールドの順序を以下のルールで並び替えます:
6
+ * 1. id: 先頭
7
+ * 2. その他: アルファベット順
8
+ * 3. createdAt, updatedAt: 末尾
9
+ *
10
+ * @param value - 正規化する値
11
+ * @returns 正規化された値
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * normalizeJson({ updatedAt: '...', title: 'A', id: '1', author: 'B' })
16
+ * // => { id: '1', author: 'B', title: 'A', updatedAt: '...' }
17
+ * ```
18
+ */
19
+ export function normalizeJson(value) {
20
+ if (value === null || value === undefined)
21
+ return value;
22
+ if (Array.isArray(value))
23
+ return value.map((item) => normalizeJson(item));
24
+ if (typeof value === 'object') {
25
+ const obj = value;
26
+ const sorted = {};
27
+ // 1. id を先頭に
28
+ if ('id' in obj)
29
+ sorted.id = normalizeJson(obj.id);
30
+ // 2. その他をアルファベット順に
31
+ const otherKeys = Object.keys(obj)
32
+ .filter((key) => key !== 'id' && key !== 'createdAt' && key !== 'updatedAt')
33
+ .sort();
34
+ for (const key of otherKeys) {
35
+ sorted[key] = normalizeJson(obj[key]);
36
+ }
37
+ // 3. タイムスタンプを末尾に
38
+ if ('createdAt' in obj)
39
+ sorted.createdAt = normalizeJson(obj.createdAt);
40
+ if ('updatedAt' in obj)
41
+ sorted.updatedAt = normalizeJson(obj.updatedAt);
42
+ return sorted;
43
+ }
44
+ return value;
45
+ }
46
+ /**
47
+ * 文字列を先頭Nバイトまで切り詰める(UTF-8)
48
+ *
49
+ * マルチバイト文字の境界を考慮して切り詰めます。
50
+ * 切り詰め位置がマルチバイト文字の途中の場合、前の文字境界まで戻ります。
51
+ *
52
+ * @param value - 切り詰める文字列
53
+ * @param maxBytes - 最大バイト数
54
+ * @returns 切り詰められた文字列
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * truncateString('Hello World', 5) // => 'Hello'
59
+ * truncateString('こんにちは世界', 9) // => 'こんに' (9バイト = 3文字)
60
+ * truncateString('Hello', 100) // => 'Hello' (そのまま)
61
+ * ```
62
+ */
63
+ export function truncateString(value, maxBytes) {
64
+ const encoder = new TextEncoder();
65
+ const bytes = encoder.encode(value);
66
+ if (bytes.length <= maxBytes) {
67
+ return value;
68
+ }
69
+ // マルチバイト文字の境界を考慮して切り詰め
70
+ const decoder = new TextDecoder('utf-8', { fatal: false });
71
+ let truncated = decoder.decode(bytes.slice(0, maxBytes));
72
+ // 不完全な文字を削除(U+FFFDは置換文字)
73
+ truncated = truncated.replace(/[\uFFFD]$/, '');
74
+ return truncated;
75
+ }
1
76
  /**
2
77
  * 文字列値をエスケープする
3
78
  * ルール: # → ##, スペース → #
@@ -11,22 +86,41 @@ export function escapeString(value) {
11
86
  .replace(/ /g, '#'); // スペースを # に置換
12
87
  }
13
88
  /**
14
- * 数値を20桁のゼロ埋め文字列に変換する
89
+ * 数値をオフセット方式でゼロパディング
15
90
  *
16
- * @param value - 変換する数値(null/undefinedも許容)
17
- * @returns 20桁のゼロ埋め文字列
91
+ * 負数を含む数値を文字列としてソート可能にするため、オフセットを加算します。
92
+ *
93
+ * 範囲: -10^padding ~ +10^padding
94
+ * オフセット: 10^padding
95
+ *
96
+ * 注意: padding は15以下を推奨(JavaScriptの安全な整数範囲: 2^53-1 ≈ 9×10^15)
97
+ *
98
+ * @param value - 変換する数値
99
+ * @param padding - パディング桁数(推奨: 15、デフォルト設定で使用)
100
+ * @returns ゼロパディングされた文字列
101
+ * @throws 数値が範囲外の場合
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * formatNumberWithOffset(-99999, 15) // => "0999999999900001"
106
+ * formatNumberWithOffset(0, 15) // => "1000000000000000"
107
+ * formatNumberWithOffset(99999, 15) // => "1000000000099999"
108
+ * ```
18
109
  */
19
- export function formatNumber(value) {
20
- // null/undefined は空文字を返す
21
- if (value === null || value === undefined) {
22
- return '';
23
- }
110
+ export function formatNumberWithOffset(value, padding) {
24
111
  if (!Number.isFinite(value)) {
25
112
  throw new Error(`Invalid number value: ${value}`);
26
113
  }
27
- // 負の数値は0として扱う(または別のエラーハンドリング)
28
- const normalized = Math.max(0, Math.floor(value));
29
- return normalized.toString().padStart(20, '0');
114
+ // 範囲チェック
115
+ const maxValue = Math.pow(10, padding);
116
+ if (value < -maxValue || value >= maxValue) {
117
+ throw new Error(`Number ${value} is out of range (-10^${padding} to 10^${padding})`);
118
+ }
119
+ // オフセットを加算
120
+ const offset = maxValue;
121
+ const adjusted = Math.floor(value) + offset;
122
+ // ゼロパディング(padding + 1桁)
123
+ return adjusted.toString().padStart(padding + 1, '0');
30
124
  }
31
125
  /**
32
126
  * 日時をUTC ISO 8601形式にフォーマットする
@@ -58,56 +152,50 @@ export function formatBoolean(value) {
58
152
  return value ? '1' : '0';
59
153
  }
60
154
  /**
61
- * フィールド値を型に応じてフォーマットする
155
+ * フィールド値を型に応じてフォーマットする(更新版)
62
156
  *
63
157
  * @param type - フィールドの型
64
- * @param value - フォーマットする値(null/undefinedも許容)
158
+ * @param value - フォーマットする値
159
+ * @param config - シャドウ設定
65
160
  * @returns フォーマットされた文字列
161
+ *
162
+ * @example
163
+ * ```typescript
164
+ * const config = { stringMaxBytes: 100, numberPadding: 20, ... };
165
+ * formatFieldValue('string', 'Hello World', config) // => 'Hello#World'
166
+ * formatFieldValue('number', 123, config) // => '10000000000000000123'
167
+ * formatFieldValue('array', ['a', 'b'], config) // => '["a","b"]'
168
+ * ```
66
169
  */
67
- export function formatFieldValue(type, value) {
170
+ export function formatFieldValue(type, value, config) {
68
171
  switch (type) {
69
- case 'string':
70
- // 文字列型の場合、null/undefinedは空文字として扱う
172
+ case 'string': {
71
173
  if (value === null || value === undefined) {
72
174
  return '';
73
175
  }
74
- return escapeString(String(value));
176
+ const str = String(value);
177
+ const truncated = truncateString(str, config.stringMaxBytes);
178
+ return escapeString(truncated);
179
+ }
75
180
  case 'number':
76
- return formatNumber(value);
181
+ return formatNumberWithOffset(value, config.numberPadding);
77
182
  case 'datetime':
78
183
  return formatDatetime(value);
79
184
  case 'boolean':
80
185
  return formatBoolean(value);
186
+ case 'array':
187
+ case 'object': {
188
+ // JSON文字列化して2倍のバイト制限で切り詰め
189
+ const normalized = normalizeJson(value);
190
+ const jsonStr = JSON.stringify(normalized);
191
+ const maxBytes = config.stringMaxBytes * 2;
192
+ const truncated = truncateString(jsonStr, maxBytes);
193
+ return escapeString(truncated);
194
+ }
81
195
  default:
82
196
  throw new Error(`Unknown shadow field type: ${type}`);
83
197
  }
84
198
  }
85
- /**
86
- * シャドーSKを生成する
87
- * フォーマット: {fieldName}#{formattedValue}#id#{recordId}
88
- *
89
- * @param fieldName - フィールド名
90
- * @param value - フィールド値
91
- * @param recordId - レコードID(ULID)
92
- * @param type - フィールドの型(デフォルト: 'string')
93
- * @returns 生成されたシャドーSK
94
- *
95
- * @example
96
- * generateShadowSK('name', 'Tech News', '01HZXY123', 'string')
97
- * // => 'name#Tech#News#id#01HZXY123'
98
- *
99
- * @example
100
- * generateShadowSK('priority', 123, '01HZXY123', 'number')
101
- * // => 'priority#00000000000000000123#id#01HZXY123'
102
- *
103
- * @example
104
- * generateShadowSK('createdAt', '2025-11-12T10:00:00.000Z', '01HZXY123', 'datetime')
105
- * // => 'createdAt#2025-11-12T10:00:00.000Z#id#01HZXY123'
106
- */
107
- export function generateShadowSK(fieldName, value, recordId, type = 'string') {
108
- const formattedValue = formatFieldValue(type, value);
109
- return `${fieldName}#${formattedValue}#id#${recordId}`;
110
- }
111
199
  /**
112
200
  * レコードIDからメインレコードのSKを生成する
113
201
  * フォーマット: id#{recordId}
@@ -119,26 +207,57 @@ export function generateMainRecordSK(recordId) {
119
207
  return `id#${recordId}`;
120
208
  }
121
209
  /**
122
- * レコードからシャドウレコードを生成する
210
+ * レコードからシャドウレコードを自動生成する(更新版)
123
211
  *
124
- * sortableFieldsに定義されたフィールドのみを処理します。
125
- * 値がundefined/nullの場合は空文字として扱い、シャドウレコードを生成します。
212
+ * レコードに存在するすべてのフィールドを自動的にシャドウ化します。
213
+ * スキーマ定義は不要で、実行時に型を推論します。
126
214
  *
127
215
  * @param record - レコードオブジェクト(idフィールドを含む)
128
- * @param schema - シャドウスキーマ定義
216
+ * @param resourceName - リソース名
217
+ * @param config - シャドウ設定
129
218
  * @returns 生成されたシャドウレコードの配列
219
+ *
220
+ * @example
221
+ * ```typescript
222
+ * const record = {
223
+ * id: '01HQXYZ...',
224
+ * title: 'Article',
225
+ * viewCount: 123,
226
+ * tags: ['tech', 'aws'],
227
+ * };
228
+ * const config = getShadowConfig();
229
+ * const shadows = generateShadowRecords(record, 'articles', config);
230
+ * // => [
231
+ * // { PK: 'articles', SK: 'id#01HQXYZ...#id#01HQXYZ...', data: { id: '01HQXYZ...' } },
232
+ * // { PK: 'articles', SK: 'title#Article#id#01HQXYZ...', data: { id: '01HQXYZ...' } },
233
+ * // { PK: 'articles', SK: 'viewCount#10000000000000000123#id#01HQXYZ...', data: { id: '01HQXYZ...' } },
234
+ * // { PK: 'articles', SK: 'tags#["aws","tech"]#id#01HQXYZ...', data: { id: '01HQXYZ...' } },
235
+ * // ]
236
+ * ```
130
237
  */
131
- export function generateShadowRecords(record, schema) {
238
+ export function generateShadowRecords(record, resourceName, config) {
132
239
  const shadows = [];
133
- // sortableFieldsに定義されたフィールドのみ処理
134
- for (const [fieldName, fieldDef] of Object.entries(schema.sortableFields)) {
135
- const value = record[fieldName];
136
- // 値を型に応じて正規化(undefined/nullは空文字として扱う)
137
- const normalizedValue = formatFieldValue(fieldDef.type, value);
240
+ // レコードの各フィールドを処理
241
+ for (const [fieldName, value] of Object.entries(record)) {
242
+ // __ プレフィックスは除外(内部メタデータ)
243
+ if (fieldName.startsWith('__')) {
244
+ continue;
245
+ }
246
+ // null/undefined は除外
247
+ if (value === null || value === undefined) {
248
+ continue;
249
+ }
250
+ // 型推論
251
+ const type = inferFieldType(value);
252
+ if (!type) {
253
+ continue;
254
+ }
255
+ // フィールド値をフォーマット
256
+ const formattedValue = formatFieldValue(type, value, config);
138
257
  // シャドウキーを生成
139
- const sk = `${fieldName}#${normalizedValue}#id#${record.id}`;
258
+ const sk = `${fieldName}#${formattedValue}#id#${record.id}`;
140
259
  shadows.push({
141
- PK: schema.resource,
260
+ PK: resourceName,
142
261
  SK: sk,
143
262
  data: { id: record.id },
144
263
  });
@@ -1 +1 @@
1
- {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../src/server/shadow/generator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../src/server/shadow/generator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAmBpD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACxD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;IAE1E,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,GAAG,GAAG,KAAgC,CAAC;QAC7C,MAAM,MAAM,GAA4B,EAAE,CAAC;QAE3C,aAAa;QACb,IAAI,IAAI,IAAI,GAAG;YAAE,MAAM,CAAC,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAEnD,mBAAmB;QACnB,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;aAC/B,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,WAAW,CAAC;aAC3E,IAAI,EAAE,CAAC;QACV,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;YAC5B,MAAM,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QACxC,CAAC;QAED,iBAAiB;QACjB,IAAI,WAAW,IAAI,GAAG;YAAE,MAAM,CAAC,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACxE,IAAI,WAAW,IAAI,GAAG;YAAE,MAAM,CAAC,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAExE,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa,EAAE,QAAgB;IAC5D,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAEpC,IAAI,KAAK,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,uBAAuB;IACvB,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3D,IAAI,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEzD,yBAAyB;IACzB,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAE/C,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,OAAO,KAAK;SACT,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,aAAa;SACjC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,cAAc;AACvC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAa,EAAE,OAAe;IACnE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,yBAAyB,KAAK,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,SAAS;IACT,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IACvC,IAAI,KAAK,GAAG,CAAC,QAAQ,IAAI,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,UAAU,KAAK,yBAAyB,OAAO,UAAU,OAAO,GAAG,CAAC,CAAC;IACvF,CAAC;IAED,WAAW;IACX,MAAM,MAAM,GAAG,QAAQ,CAAC;IACxB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC;IAE5C,wBAAwB;IACxB,OAAO,QAAQ,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,OAAO,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;AACxD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,KAAuC;IACpE,yBAAyB;IACzB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAEjE,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,2BAA2B,KAAK,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;AAC5B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,KAAiC;IAC7D,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;AAC3B,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,gBAAgB,CAC9B,IAAqB,EACrB,KAAc,EACd,MAAoB;IAEpB,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBAC1C,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAC1B,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;YAC7D,OAAO,YAAY,CAAC,SAAS,CAAC,CAAC;QACjC,CAAC;QACD,KAAK,QAAQ;YACX,OAAO,sBAAsB,CAAC,KAAe,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;QACvE,KAAK,UAAU;YACb,OAAO,cAAc,CAAC,KAAsB,CAAC,CAAC;QAChD,KAAK,SAAS;YACZ,OAAO,aAAa,CAAC,KAAgB,CAAC,CAAC;QACzC,KAAK,OAAO,CAAC;QACb,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,0BAA0B;YAC1B,MAAM,UAAU,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;YACxC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;YAC3C,MAAM,QAAQ,GAAG,MAAM,CAAC,cAAc,GAAG,CAAC,CAAC;YAC3C,MAAM,SAAS,GAAG,cAAc,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YACpD,OAAO,YAAY,CAAC,SAAS,CAAC,CAAC;QACjC,CAAC;QACD;YACE,MAAM,IAAI,KAAK,CAAC,8BAA8B,IAAI,EAAE,CAAC,CAAC;IAC1D,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAgB;IACnD,OAAO,MAAM,QAAQ,EAAE,CAAC;AAC1B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAA+B,EAC/B,YAAoB,EACpB,MAAoB;IAEpB,MAAM,OAAO,GAAmB,EAAE,CAAC;IAEnC,iBAAiB;IACjB,KAAK,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACxD,yBAAyB;QACzB,IAAI,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/B,SAAS;QACX,CAAC;QAED,qBAAqB;QACrB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YAC1C,SAAS;QACX,CAAC;QAED,MAAM;QACN,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,SAAS;QACX,CAAC;QAED,gBAAgB;QAChB,MAAM,cAAc,GAAG,gBAAgB,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QAE7D,YAAY;QACZ,MAAM,EAAE,GAAG,GAAG,SAAS,IAAI,cAAc,OAAO,MAAM,CAAC,EAAE,EAAE,CAAC;QAE5D,OAAO,CAAC,IAAI,CAAC;YACX,EAAE,EAAE,YAAY;YAChB,EAAE,EAAE,EAAE;YACN,IAAI,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAY,EAAE;SAClC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -4,8 +4,9 @@
4
4
  * DynamoDB Single-Table設計における動的シャドーレコード管理を提供します。
5
5
  */
6
6
  export type { ShadowFieldType, ShadowFieldConfig, ResourceShadowConfig, ShadowConfig, ShadowDiff, } from './types.js';
7
- export type { ShadowSchema, ShadowRecord } from './generator.js';
8
- export { escapeString, formatNumber, formatDatetime, formatBoolean, formatFieldValue, generateShadowSK, generateMainRecordSK, generateShadowRecords, } from './generator.js';
7
+ export type { ShadowRecord } from './generator.js';
8
+ export { escapeString, formatDatetime, formatBoolean, formatFieldValue, generateMainRecordSK, generateShadowRecords, formatNumberWithOffset, truncateString, normalizeJson, } from './generator.js';
9
9
  export { calculateShadowDiff, isDiffEmpty, mergeShadowDiffs } from './differ.js';
10
- export { getShadowConfig, getResourceSchema, getShadowConfigHash, getSchemaVersion, clearShadowConfigCache, getAllShadowFields, isValidShadowField, getDefaultSort, } from './config.js';
10
+ export { getShadowConfig, clearShadowConfigCache, } from './config.js';
11
+ export { inferFieldType, extractShadowableFields, } from './typeInference.js';
11
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/shadow/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,EACZ,UAAU,GACX,MAAM,YAAY,CAAC;AAGpB,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGjF,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,sBAAsB,EACtB,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,GACf,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/shadow/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,EACZ,UAAU,GACX,MAAM,YAAY,CAAC;AAGpB,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EACL,YAAY,EACZ,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,EACtB,cAAc,EACd,aAAa,GACd,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGjF,OAAO,EACL,eAAe,EACf,sBAAsB,GACvB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,cAAc,EACd,uBAAuB,GACxB,MAAM,oBAAoB,CAAC"}
@@ -3,9 +3,11 @@
3
3
  *
4
4
  * DynamoDB Single-Table設計における動的シャドーレコード管理を提供します。
5
5
  */
6
- export { escapeString, formatNumber, formatDatetime, formatBoolean, formatFieldValue, generateShadowSK, generateMainRecordSK, generateShadowRecords, } from './generator.js';
6
+ export { escapeString, formatDatetime, formatBoolean, formatFieldValue, generateMainRecordSK, generateShadowRecords, formatNumberWithOffset, truncateString, normalizeJson, } from './generator.js';
7
7
  // 差分計算関数のエクスポート
8
8
  export { calculateShadowDiff, isDiffEmpty, mergeShadowDiffs } from './differ.js';
9
9
  // 設定管理関数のエクスポート
10
- export { getShadowConfig, getResourceSchema, getShadowConfigHash, getSchemaVersion, clearShadowConfigCache, getAllShadowFields, isValidShadowField, getDefaultSort, } from './config.js';
10
+ export { getShadowConfig, clearShadowConfigCache, } from './config.js';
11
+ // 型推論関数のエクスポート
12
+ export { inferFieldType, extractShadowableFields, } from './typeInference.js';
11
13
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/shadow/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAaH,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,gBAAgB,CAAC;AAExB,gBAAgB;AAChB,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEjF,gBAAgB;AAChB,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,sBAAsB,EACtB,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,GACf,MAAM,aAAa,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/shadow/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAaH,OAAO,EACL,YAAY,EACZ,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,EACtB,cAAc,EACd,aAAa,GACd,MAAM,gBAAgB,CAAC;AAExB,gBAAgB;AAChB,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEjF,gBAAgB;AAChB,OAAO,EACL,eAAe,EACf,sBAAsB,GACvB,MAAM,aAAa,CAAC;AAErB,eAAe;AACf,OAAO,EACL,cAAc,EACd,uBAAuB,GACxB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * シャドウフィールドの型推論
3
+ *
4
+ * レコードのフィールド値から型を自動的に推論します。
5
+ * JSON設定ファイルは不要で、実行時に型を判定します。
6
+ *
7
+ * 要件: 3.1-3.9
8
+ */
9
+ import type { ShadowFieldType } from './types.js';
10
+ /**
11
+ * フィールド値から型を推論する
12
+ *
13
+ * @param value - 推論するフィールド値
14
+ * @returns 推論された型、またはnull(シャドウ化不可能な場合)
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * inferFieldType('Hello') // => 'string'
19
+ * inferFieldType(123) // => 'number'
20
+ * inferFieldType(true) // => 'boolean'
21
+ * inferFieldType('2024-01-15T10:30:00.000Z') // => 'datetime'
22
+ * inferFieldType(['a', 'b']) // => 'array'
23
+ * inferFieldType({ key: 'value' }) // => 'object'
24
+ * inferFieldType(null) // => null
25
+ * inferFieldType(undefined) // => null
26
+ * ```
27
+ */
28
+ export declare function inferFieldType(value: unknown): ShadowFieldType | null;
29
+ /**
30
+ * レコードから自動的にシャドウ可能なフィールドを抽出する
31
+ *
32
+ * @param record - レコードオブジェクト
33
+ * @returns フィールド名と型のマップ
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const record = {
38
+ * id: '01HQXYZ...',
39
+ * title: 'Article',
40
+ * viewCount: 123,
41
+ * published: true,
42
+ * createdAt: '2024-01-15T10:30:00.000Z',
43
+ * tags: ['tech', 'aws'],
44
+ * metadata: { category: 'tech' },
45
+ * __shadowKeys: ['...'], // 除外される
46
+ * };
47
+ *
48
+ * extractShadowableFields(record);
49
+ * // => {
50
+ * // id: 'string',
51
+ * // title: 'string',
52
+ * // viewCount: 'number',
53
+ * // published: 'boolean',
54
+ * // createdAt: 'datetime',
55
+ * // tags: 'array',
56
+ * // metadata: 'object'
57
+ * // }
58
+ * ```
59
+ */
60
+ export declare function extractShadowableFields(record: Record<string, unknown>): Record<string, ShadowFieldType>;
61
+ //# sourceMappingURL=typeInference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typeInference.d.ts","sourceRoot":"","sources":["../../../src/server/shadow/typeInference.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAkCrE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAiBjC"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * フィールド値から型を推論する
3
+ *
4
+ * @param value - 推論するフィールド値
5
+ * @returns 推論された型、またはnull(シャドウ化不可能な場合)
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * inferFieldType('Hello') // => 'string'
10
+ * inferFieldType(123) // => 'number'
11
+ * inferFieldType(true) // => 'boolean'
12
+ * inferFieldType('2024-01-15T10:30:00.000Z') // => 'datetime'
13
+ * inferFieldType(['a', 'b']) // => 'array'
14
+ * inferFieldType({ key: 'value' }) // => 'object'
15
+ * inferFieldType(null) // => null
16
+ * inferFieldType(undefined) // => null
17
+ * ```
18
+ */
19
+ export function inferFieldType(value) {
20
+ // null/undefined は除外
21
+ if (value === null || value === undefined) {
22
+ return null;
23
+ }
24
+ // 型判定
25
+ if (typeof value === 'string') {
26
+ // ISO 8601形式の日時文字列かチェック
27
+ if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value)) {
28
+ return 'datetime';
29
+ }
30
+ return 'string';
31
+ }
32
+ if (typeof value === 'number') {
33
+ return 'number';
34
+ }
35
+ if (typeof value === 'boolean') {
36
+ return 'boolean';
37
+ }
38
+ // array型
39
+ if (Array.isArray(value)) {
40
+ return 'array';
41
+ }
42
+ // object型
43
+ if (typeof value === 'object') {
44
+ return 'object';
45
+ }
46
+ return null;
47
+ }
48
+ /**
49
+ * レコードから自動的にシャドウ可能なフィールドを抽出する
50
+ *
51
+ * @param record - レコードオブジェクト
52
+ * @returns フィールド名と型のマップ
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * const record = {
57
+ * id: '01HQXYZ...',
58
+ * title: 'Article',
59
+ * viewCount: 123,
60
+ * published: true,
61
+ * createdAt: '2024-01-15T10:30:00.000Z',
62
+ * tags: ['tech', 'aws'],
63
+ * metadata: { category: 'tech' },
64
+ * __shadowKeys: ['...'], // 除外される
65
+ * };
66
+ *
67
+ * extractShadowableFields(record);
68
+ * // => {
69
+ * // id: 'string',
70
+ * // title: 'string',
71
+ * // viewCount: 'number',
72
+ * // published: 'boolean',
73
+ * // createdAt: 'datetime',
74
+ * // tags: 'array',
75
+ * // metadata: 'object'
76
+ * // }
77
+ * ```
78
+ */
79
+ export function extractShadowableFields(record) {
80
+ const fields = {};
81
+ for (const [key, value] of Object.entries(record)) {
82
+ // __ プレフィックスは除外(内部メタデータ)
83
+ if (key.startsWith('__')) {
84
+ continue;
85
+ }
86
+ // 型推論
87
+ const type = inferFieldType(value);
88
+ if (type) {
89
+ fields[key] = type;
90
+ }
91
+ }
92
+ return fields;
93
+ }
94
+ //# sourceMappingURL=typeInference.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typeInference.js","sourceRoot":"","sources":["../../../src/server/shadow/typeInference.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,qBAAqB;IACrB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM;IACN,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,wBAAwB;QACxB,IAAI,sCAAsC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACvD,OAAO,UAAU,CAAC;QACpB,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,IAAI,OAAO,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,SAAS;IACT,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,UAAU;IACV,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAU,uBAAuB,CACrC,MAA+B;IAE/B,MAAM,MAAM,GAAoC,EAAE,CAAC;IAEnD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,yBAAyB;QACzB,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,SAAS;QACX,CAAC;QAED,MAAM;QACN,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;QACrB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * シャドーフィールドの型定義
3
3
  */
4
- export type ShadowFieldType = 'string' | 'number' | 'datetime' | 'boolean';
4
+ export type ShadowFieldType = 'string' | 'number' | 'datetime' | 'boolean' | 'array' | 'object';
5
5
  /**
6
6
  * シャドー設定のフィールド定義
7
7
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/server/shadow/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;AAE3E;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,eAAe,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC;KACvB,CAAC;IACF,OAAO,EAAE;QACP,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,CAAC;KACxC,CAAC;IACF,GAAG,CAAC,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE;QACT,CAAC,YAAY,EAAE,MAAM,GAAG,oBAAoB,CAAC;KAC9C,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,sBAAsB;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,sBAAsB;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/server/shadow/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEhG;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,eAAe,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC;KACvB,CAAC;IACF,OAAO,EAAE;QACP,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,CAAC;KACxC,CAAC;IACF,GAAG,CAAC,EAAE;QACJ,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE;QACT,CAAC,YAAY,EAAE,MAAM,GAAG,oBAAoB,CAAC;KAC9C,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,sBAAsB;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,sBAAsB;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB"}
@@ -1 +1 @@
1
- {"version":3,"file":"timestamps.d.ts","sourceRoot":"","sources":["../../../src/server/utils/timestamps.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,GAAG,IAAI,CAmC3D;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAe1F;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAczF"}
1
+ {"version":3,"file":"timestamps.d.ts","sourceRoot":"","sources":["../../../src/server/utils/timestamps.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,GAAG,IAAI,CAQ3D;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAe1F;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAczF"}
@@ -15,33 +15,10 @@ import { getShadowConfig } from '../../index.js';
15
15
  */
16
16
  export function getTimestampFields() {
17
17
  const shadowConfig = getShadowConfig();
18
- // database 設定が存在するか確認
19
- if (!shadowConfig.database) {
20
- // デフォルト値を返す
21
- return {
22
- createdAt: 'createdAt',
23
- updatedAt: 'updatedAt',
24
- };
25
- }
26
- const { timestamps } = shadowConfig.database;
27
- // timestamps 設定がない場合はデフォルト値を返す
28
- if (!timestamps) {
29
- return {
30
- createdAt: 'createdAt',
31
- updatedAt: 'updatedAt',
32
- };
33
- }
34
- // timestamps 設定がある場合はそれを使用
35
- if (typeof timestamps === 'object') {
36
- return {
37
- createdAt: timestamps.createdAt,
38
- updatedAt: timestamps.updatedAt,
39
- };
40
- }
41
- // デフォルト値を返す
18
+ // 新しい設定形式: 環境変数ベース
42
19
  return {
43
- createdAt: 'createdAt',
44
- updatedAt: 'updatedAt',
20
+ createdAt: shadowConfig.createdAtField,
21
+ updatedAt: shadowConfig.updatedAtField,
45
22
  };
46
23
  }
47
24
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"timestamps.js","sourceRoot":"","sources":["../../../src/server/utils/timestamps.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAUjD;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,YAAY,GAAG,eAAe,EAAE,CAAC;IAEvC,sBAAsB;IACtB,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC;QAC3B,YAAY;QACZ,OAAO;YACL,SAAS,EAAE,WAAW;YACtB,SAAS,EAAE,WAAW;SACvB,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,UAAU,EAAE,GAAG,YAAY,CAAC,QAAQ,CAAC;IAE7C,+BAA+B;IAC/B,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO;YACL,SAAS,EAAE,WAAW;YACtB,SAAS,EAAE,WAAW;SACvB,CAAC;IACJ,CAAC;IAED,2BAA2B;IAC3B,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;QACnC,OAAO;YACL,SAAS,EAAE,UAAU,CAAC,SAAS;YAC/B,SAAS,EAAE,UAAU,CAAC,SAAS;SAChC,CAAC;IACJ,CAAC;IAED,YAAY;IACZ,OAAO;QACL,SAAS,EAAE,WAAW;QACtB,SAAS,EAAE,WAAW;KACvB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAA6B;IAC/D,MAAM,eAAe,GAAG,kBAAkB,EAAE,CAAC;IAE7C,4BAA4B;IAC5B,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAErC,OAAO;QACL,GAAG,IAAI;QACP,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,GAAG;QAChC,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,GAAG;KACjC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAA6B;IAC9D,MAAM,eAAe,GAAG,kBAAkB,EAAE,CAAC;IAE7C,4BAA4B;IAC5B,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAErC,OAAO;QACL,GAAG,IAAI;QACP,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,GAAG;KACjC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"timestamps.js","sourceRoot":"","sources":["../../../src/server/utils/timestamps.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAUjD;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,YAAY,GAAG,eAAe,EAAE,CAAC;IAEvC,mBAAmB;IACnB,OAAO;QACL,SAAS,EAAE,YAAY,CAAC,cAAc;QACtC,SAAS,EAAE,YAAY,CAAC,cAAc;KACvC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAA6B;IAC/D,MAAM,eAAe,GAAG,kBAAkB,EAAE,CAAC;IAE7C,4BAA4B;IAC5B,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAErC,OAAO;QACL,GAAG,IAAI;QACP,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,GAAG;QAChC,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,GAAG;KACjC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAA6B;IAC9D,MAAM,eAAe,GAAG,kBAAkB,EAAE,CAAC;IAE7C,4BAA4B;IAC5B,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAErC,OAAO;QACL,GAAG,IAAI;QACP,CAAC,eAAe,CAAC,SAAS,CAAC,EAAE,GAAG;KACjC,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ttl.d.ts","sourceRoot":"","sources":["../../../src/server/utils/ttl.ts"],"names":[],"mappings":"AAQA;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CA8BpF;AAED;;;;;;GAMG;AACH,wBAAgB,MAAM,CACpB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAiBzB"}
1
+ {"version":3,"file":"ttl.d.ts","sourceRoot":"","sources":["../../../src/server/utils/ttl.ts"],"names":[],"mappings":"AAQA;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CA+BpF;AAED;;;;;;GAMG;AACH,wBAAgB,MAAM,CACpB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAiBzB"}
@@ -2,7 +2,7 @@
2
2
  * TTL(Time To Live)ユーティリティ
3
3
  * DynamoDB TTL設定を管理
4
4
  */
5
- import { createLogger, getShadowConfig } from '../../index.js';
5
+ import { createLogger } from '../../index.js';
6
6
  const logger = createLogger({ service: 'records-lambda' });
7
7
  /**
8
8
  * TTLを計算する
@@ -12,17 +12,19 @@ const logger = createLogger({ service: 'records-lambda' });
12
12
  * @returns TTL(Unix timestamp、秒単位)、TTL不要の場合はundefined
13
13
  */
14
14
  export function calculateTTL(resource, createdAt) {
15
- // shadow.config.jsonからTTL設定を取得
16
- const shadowConfig = getShadowConfig();
17
- const resourceConfig = shadowConfig.resources[resource];
18
- if (!resourceConfig?.ttl) {
15
+ // 新しい実装: 環境変数ベースのTTL設定
16
+ // 環境変数から取得(例: ARTICLES_TTL_DAYS=30)
17
+ const envKey = `${resource.toUpperCase()}_TTL_DAYS`;
18
+ const ttlDaysStr = process.env[envKey];
19
+ if (!ttlDaysStr) {
19
20
  // TTL設定がないリソースはundefinedを返す
20
21
  return undefined;
21
22
  }
22
- const defaultDays = resourceConfig.ttl.days;
23
- // 環境変数から上書き可能(後方互換性のため)
24
- const envKey = `${resource.toUpperCase()}_TTL_DAYS`;
25
- const ttlDays = parseInt(process.env[envKey] || String(defaultDays), 10);
23
+ const ttlDays = parseInt(ttlDaysStr, 10);
24
+ if (isNaN(ttlDays) || ttlDays <= 0) {
25
+ logger.warn('Invalid TTL_DAYS value', { resource, envKey, value: ttlDaysStr });
26
+ return undefined;
27
+ }
26
28
  // 作成日時からTTLを計算
27
29
  const createdAtMs = new Date(createdAt).getTime();
28
30
  const ttlMs = createdAtMs + ttlDays * 24 * 60 * 60 * 1000;
@@ -1 +1 @@
1
- {"version":3,"file":"ttl.js","sourceRoot":"","sources":["../../../src/server/utils/ttl.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAE/D,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC,CAAC;AAE3D;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,SAAiB;IAC9D,+BAA+B;IAC/B,MAAM,YAAY,GAAG,eAAe,EAAE,CAAC;IACvC,MAAM,cAAc,GAAG,YAAY,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAExD,IAAI,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC;QACzB,4BAA4B;QAC5B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,WAAW,GAAG,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC;IAE5C,wBAAwB;IACxB,MAAM,MAAM,GAAG,GAAG,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IACpD,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,WAAW,CAAC,EAAE,EAAE,CAAC,CAAC;IAEzE,eAAe;IACf,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;IAClD,MAAM,KAAK,GAAG,WAAW,GAAG,OAAO,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC1D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAErC,MAAM,CAAC,KAAK,CAAC,gBAAgB,EAAE;QAC7B,QAAQ;QACR,OAAO;QACP,SAAS;QACT,GAAG;QACH,OAAO,EAAE,IAAI,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE;KAC5C,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,MAAM,CACpB,QAAgB,EAChB,UAAmC;IAEnC,MAAM,SAAS,GAAG,UAAU,CAAC,SAAmB,CAAC;IACjD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,+CAA+C,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC3E,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC9C,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,aAAa;QACb,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,OAAO;QACL,GAAG,UAAU;QACb,GAAG;KACJ,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"ttl.js","sourceRoot":"","sources":["../../../src/server/utils/ttl.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC,CAAC;AAE3D;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,SAAiB;IAC9D,uBAAuB;IACvB,oCAAoC;IACpC,MAAM,MAAM,GAAG,GAAG,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IACpD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAEvC,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,4BAA4B;QAC5B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,OAAO,GAAG,QAAQ,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACzC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;QAC/E,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,eAAe;IACf,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;IAClD,MAAM,KAAK,GAAG,WAAW,GAAG,OAAO,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC1D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAErC,MAAM,CAAC,KAAK,CAAC,gBAAgB,EAAE;QAC7B,QAAQ;QACR,OAAO;QACP,SAAS;QACT,GAAG;QACH,OAAO,EAAE,IAAI,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE;KAC5C,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,MAAM,CACpB,QAAgB,EAChB,UAAmC;IAEnC,MAAM,SAAS,GAAG,UAAU,CAAC,SAAmB,CAAC;IACjD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,CAAC,IAAI,CAAC,+CAA+C,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC3E,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC9C,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,aAAa;QACb,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,OAAO;QACL,GAAG,UAAU;QACb,GAAG;KACJ,CAAC;AACJ,CAAC"}