@exabugs/dynamodb-client 0.1.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.
Files changed (230) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/LICENSE +21 -0
  3. package/README.md +283 -0
  4. package/dist/client/Collection.d.ts +57 -0
  5. package/dist/client/Collection.d.ts.map +1 -0
  6. package/dist/client/Collection.js +174 -0
  7. package/dist/client/Collection.js.map +1 -0
  8. package/dist/client/Database.d.ts +35 -0
  9. package/dist/client/Database.d.ts.map +1 -0
  10. package/dist/client/Database.js +48 -0
  11. package/dist/client/Database.js.map +1 -0
  12. package/dist/client/DynamoClient.d.ts +43 -0
  13. package/dist/client/DynamoClient.d.ts.map +1 -0
  14. package/dist/client/DynamoClient.js +62 -0
  15. package/dist/client/DynamoClient.js.map +1 -0
  16. package/dist/client/FindCursor.d.ts +174 -0
  17. package/dist/client/FindCursor.d.ts.map +1 -0
  18. package/dist/client/FindCursor.js +256 -0
  19. package/dist/client/FindCursor.js.map +1 -0
  20. package/dist/client/aws-sigv4.d.ts +10 -0
  21. package/dist/client/aws-sigv4.d.ts.map +1 -0
  22. package/dist/client/aws-sigv4.js +54 -0
  23. package/dist/client/aws-sigv4.js.map +1 -0
  24. package/dist/client/index.cognito.d.ts +34 -0
  25. package/dist/client/index.cognito.d.ts.map +1 -0
  26. package/dist/client/index.cognito.js +30 -0
  27. package/dist/client/index.cognito.js.map +1 -0
  28. package/dist/client/index.d.ts +12 -0
  29. package/dist/client/index.d.ts.map +1 -0
  30. package/dist/client/index.iam.d.ts +34 -0
  31. package/dist/client/index.iam.d.ts.map +1 -0
  32. package/dist/client/index.iam.js +28 -0
  33. package/dist/client/index.iam.js.map +1 -0
  34. package/dist/client/index.js +12 -0
  35. package/dist/client/index.js.map +1 -0
  36. package/dist/client/index.token.d.ts +33 -0
  37. package/dist/client/index.token.d.ts.map +1 -0
  38. package/dist/client/index.token.js +28 -0
  39. package/dist/client/index.token.js.map +1 -0
  40. package/dist/dynamodb.d.ts +20 -0
  41. package/dist/dynamodb.d.ts.map +1 -0
  42. package/dist/dynamodb.js +31 -0
  43. package/dist/dynamodb.js.map +1 -0
  44. package/dist/errors.d.ts +100 -0
  45. package/dist/errors.d.ts.map +1 -0
  46. package/dist/errors.js +146 -0
  47. package/dist/errors.js.map +1 -0
  48. package/dist/index.d.ts +10 -0
  49. package/dist/index.d.ts.map +1 -0
  50. package/dist/index.js +7 -0
  51. package/dist/index.js.map +1 -0
  52. package/dist/integrations/react-admin/dataProvider.d.ts +59 -0
  53. package/dist/integrations/react-admin/dataProvider.d.ts.map +1 -0
  54. package/dist/integrations/react-admin/dataProvider.js +364 -0
  55. package/dist/integrations/react-admin/dataProvider.js.map +1 -0
  56. package/dist/integrations/react-admin/index.d.ts +24 -0
  57. package/dist/integrations/react-admin/index.d.ts.map +1 -0
  58. package/dist/integrations/react-admin/index.js +23 -0
  59. package/dist/integrations/react-admin/index.js.map +1 -0
  60. package/dist/integrations/react-admin/types.d.ts +47 -0
  61. package/dist/integrations/react-admin/types.d.ts.map +1 -0
  62. package/dist/integrations/react-admin/types.js +5 -0
  63. package/dist/integrations/react-admin/types.js.map +1 -0
  64. package/dist/logger.d.ts +61 -0
  65. package/dist/logger.d.ts.map +1 -0
  66. package/dist/logger.js +87 -0
  67. package/dist/logger.js.map +1 -0
  68. package/dist/scripts/repair-shadows.d.ts +3 -0
  69. package/dist/scripts/repair-shadows.d.ts.map +1 -0
  70. package/dist/scripts/repair-shadows.js +190 -0
  71. package/dist/scripts/repair-shadows.js.map +1 -0
  72. package/dist/server/handler.cjs +31378 -0
  73. package/dist/server/handler.cjs.map +7 -0
  74. package/dist/server/handler.d.ts +18 -0
  75. package/dist/server/handler.d.ts.map +1 -0
  76. package/dist/server/handler.js +435 -0
  77. package/dist/server/handler.js.map +1 -0
  78. package/dist/server/handler.zip +0 -0
  79. package/dist/server/index.d.ts +8 -0
  80. package/dist/server/index.d.ts.map +1 -0
  81. package/dist/server/index.js +8 -0
  82. package/dist/server/index.js.map +1 -0
  83. package/dist/server/operations/deleteMany.d.ts +18 -0
  84. package/dist/server/operations/deleteMany.d.ts.map +1 -0
  85. package/dist/server/operations/deleteMany.js +222 -0
  86. package/dist/server/operations/deleteMany.js.map +1 -0
  87. package/dist/server/operations/deleteOne.d.ts +17 -0
  88. package/dist/server/operations/deleteOne.d.ts.map +1 -0
  89. package/dist/server/operations/deleteOne.js +87 -0
  90. package/dist/server/operations/deleteOne.js.map +1 -0
  91. package/dist/server/operations/find.d.ts +18 -0
  92. package/dist/server/operations/find.d.ts.map +1 -0
  93. package/dist/server/operations/find.js +382 -0
  94. package/dist/server/operations/find.js.map +1 -0
  95. package/dist/server/operations/findMany.d.ts +13 -0
  96. package/dist/server/operations/findMany.d.ts.map +1 -0
  97. package/dist/server/operations/findMany.js +61 -0
  98. package/dist/server/operations/findMany.js.map +1 -0
  99. package/dist/server/operations/findManyReference.d.ts +18 -0
  100. package/dist/server/operations/findManyReference.d.ts.map +1 -0
  101. package/dist/server/operations/findManyReference.js +150 -0
  102. package/dist/server/operations/findManyReference.js.map +1 -0
  103. package/dist/server/operations/findOne.d.ts +14 -0
  104. package/dist/server/operations/findOne.d.ts.map +1 -0
  105. package/dist/server/operations/findOne.js +56 -0
  106. package/dist/server/operations/findOne.js.map +1 -0
  107. package/dist/server/operations/insertMany.d.ts +19 -0
  108. package/dist/server/operations/insertMany.d.ts.map +1 -0
  109. package/dist/server/operations/insertMany.js +243 -0
  110. package/dist/server/operations/insertMany.js.map +1 -0
  111. package/dist/server/operations/insertOne.d.ts +18 -0
  112. package/dist/server/operations/insertOne.d.ts.map +1 -0
  113. package/dist/server/operations/insertOne.js +85 -0
  114. package/dist/server/operations/insertOne.js.map +1 -0
  115. package/dist/server/operations/updateMany.d.ts +20 -0
  116. package/dist/server/operations/updateMany.d.ts.map +1 -0
  117. package/dist/server/operations/updateMany.js +316 -0
  118. package/dist/server/operations/updateMany.js.map +1 -0
  119. package/dist/server/operations/updateOne.d.ts +20 -0
  120. package/dist/server/operations/updateOne.d.ts.map +1 -0
  121. package/dist/server/operations/updateOne.js +159 -0
  122. package/dist/server/operations/updateOne.js.map +1 -0
  123. package/dist/server/query/converter.d.ts +85 -0
  124. package/dist/server/query/converter.d.ts.map +1 -0
  125. package/dist/server/query/converter.js +161 -0
  126. package/dist/server/query/converter.js.map +1 -0
  127. package/dist/server/query/index.d.ts +5 -0
  128. package/dist/server/query/index.d.ts.map +1 -0
  129. package/dist/server/query/index.js +5 -0
  130. package/dist/server/query/index.js.map +1 -0
  131. package/dist/server/shadow/config.d.ts +147 -0
  132. package/dist/server/shadow/config.d.ts.map +1 -0
  133. package/dist/server/shadow/config.js +162 -0
  134. package/dist/server/shadow/config.js.map +1 -0
  135. package/dist/server/shadow/differ.d.ts +42 -0
  136. package/dist/server/shadow/differ.d.ts.map +1 -0
  137. package/dist/server/shadow/differ.js +66 -0
  138. package/dist/server/shadow/differ.js.map +1 -0
  139. package/dist/server/shadow/generator.d.ts +104 -0
  140. package/dist/server/shadow/generator.d.ts.map +1 -0
  141. package/dist/server/shadow/generator.js +148 -0
  142. package/dist/server/shadow/generator.js.map +1 -0
  143. package/dist/server/shadow/index.d.ts +11 -0
  144. package/dist/server/shadow/index.d.ts.map +1 -0
  145. package/dist/server/shadow/index.js +11 -0
  146. package/dist/server/shadow/index.js.map +1 -0
  147. package/dist/server/shadow/types.d.ts +44 -0
  148. package/dist/server/shadow/types.d.ts.map +1 -0
  149. package/dist/server/shadow/types.js +2 -0
  150. package/dist/server/shadow/types.js.map +1 -0
  151. package/dist/server/types.d.ts +295 -0
  152. package/dist/server/types.d.ts.map +1 -0
  153. package/dist/server/types.js +7 -0
  154. package/dist/server/types.js.map +1 -0
  155. package/dist/server/utils/auth.d.ts +43 -0
  156. package/dist/server/utils/auth.d.ts.map +1 -0
  157. package/dist/server/utils/auth.js +123 -0
  158. package/dist/server/utils/auth.js.map +1 -0
  159. package/dist/server/utils/bulkOperations.d.ts +81 -0
  160. package/dist/server/utils/bulkOperations.d.ts.map +1 -0
  161. package/dist/server/utils/bulkOperations.js +147 -0
  162. package/dist/server/utils/bulkOperations.js.map +1 -0
  163. package/dist/server/utils/chunking.d.ts +96 -0
  164. package/dist/server/utils/chunking.d.ts.map +1 -0
  165. package/dist/server/utils/chunking.js +225 -0
  166. package/dist/server/utils/chunking.js.map +1 -0
  167. package/dist/server/utils/dynamodb.d.ts +41 -0
  168. package/dist/server/utils/dynamodb.d.ts.map +1 -0
  169. package/dist/server/utils/dynamodb.js +83 -0
  170. package/dist/server/utils/dynamodb.js.map +1 -0
  171. package/dist/server/utils/filter.d.ts +152 -0
  172. package/dist/server/utils/filter.d.ts.map +1 -0
  173. package/dist/server/utils/filter.js +270 -0
  174. package/dist/server/utils/filter.js.map +1 -0
  175. package/dist/server/utils/pagination.d.ts +27 -0
  176. package/dist/server/utils/pagination.d.ts.map +1 -0
  177. package/dist/server/utils/pagination.js +56 -0
  178. package/dist/server/utils/pagination.js.map +1 -0
  179. package/dist/server/utils/timestamps.d.ts +31 -0
  180. package/dist/server/utils/timestamps.d.ts.map +1 -0
  181. package/dist/server/utils/timestamps.js +84 -0
  182. package/dist/server/utils/timestamps.js.map +1 -0
  183. package/dist/server/utils/ttl.d.ts +17 -0
  184. package/dist/server/utils/ttl.d.ts.map +1 -0
  185. package/dist/server/utils/ttl.js +62 -0
  186. package/dist/server/utils/ttl.js.map +1 -0
  187. package/dist/server/utils/validation.d.ts +40 -0
  188. package/dist/server/utils/validation.d.ts.map +1 -0
  189. package/dist/server/utils/validation.js +54 -0
  190. package/dist/server/utils/validation.js.map +1 -0
  191. package/dist/shadows/config.d.ts +54 -0
  192. package/dist/shadows/config.d.ts.map +1 -0
  193. package/dist/shadows/config.js +95 -0
  194. package/dist/shadows/config.js.map +1 -0
  195. package/dist/shadows/differ.d.ts +42 -0
  196. package/dist/shadows/differ.d.ts.map +1 -0
  197. package/dist/shadows/differ.js +66 -0
  198. package/dist/shadows/differ.js.map +1 -0
  199. package/dist/shadows/generator.d.ts +63 -0
  200. package/dist/shadows/generator.d.ts.map +1 -0
  201. package/dist/shadows/generator.js +107 -0
  202. package/dist/shadows/generator.js.map +1 -0
  203. package/dist/shadows/index.d.ts +15 -0
  204. package/dist/shadows/index.d.ts.map +1 -0
  205. package/dist/shadows/index.js +17 -0
  206. package/dist/shadows/index.js.map +1 -0
  207. package/dist/shadows/types.d.ts +44 -0
  208. package/dist/shadows/types.d.ts.map +1 -0
  209. package/dist/shadows/types.js +2 -0
  210. package/dist/shadows/types.js.map +1 -0
  211. package/dist/types.d.ts +165 -0
  212. package/dist/types.d.ts.map +1 -0
  213. package/dist/types.js +2 -0
  214. package/dist/types.js.map +1 -0
  215. package/dist/ulid.d.ts +46 -0
  216. package/dist/ulid.d.ts.map +1 -0
  217. package/dist/ulid.js +66 -0
  218. package/dist/ulid.js.map +1 -0
  219. package/package.json +136 -0
  220. package/terraform/README.md +222 -0
  221. package/terraform/examples/advanced/README.md +129 -0
  222. package/terraform/examples/advanced/main.tf +158 -0
  223. package/terraform/examples/advanced/shadow.config.json +35 -0
  224. package/terraform/examples/advanced/variables.tf +28 -0
  225. package/terraform/examples/basic/README.md +53 -0
  226. package/terraform/examples/basic/main.tf +99 -0
  227. package/terraform/examples/basic/variables.tf +17 -0
  228. package/terraform/main.tf +159 -0
  229. package/terraform/outputs.tf +56 -0
  230. package/terraform/variables.tf +59 -0
@@ -0,0 +1,270 @@
1
+ /**
2
+ * DynamoDB フィルター式生成ユーティリティ
3
+ * FilterExpression の構築とシャドーレコード除外条件の生成
4
+ *
5
+ * 要件: 5.5, 7.1, 12.1-12.12
6
+ */
7
+ /**
8
+ * フィルターフィールド名をパースする
9
+ *
10
+ * フォーマット: `フィールド名:オペレータ:型`
11
+ * - オペレータ省略時: eq(デフォルト)
12
+ * - 型省略時: string(デフォルト)
13
+ *
14
+ * 例:
15
+ * - "status" → { field: "status", operator: "eq", type: "string" }
16
+ * - "priority:gte" → { field: "priority", operator: "gte", type: "string" }
17
+ * - "priority:gte:number" → { field: "priority", operator: "gte", type: "number" }
18
+ *
19
+ * 要件: 12.1, 12.3, 12.4
20
+ *
21
+ * @param fieldKey - フィルターフィールド名
22
+ * @returns パース結果
23
+ * @throws Error - 構文エラーの場合
24
+ */
25
+ export function parseFilterField(fieldKey) {
26
+ const parts = fieldKey.split(':');
27
+ if (parts.length === 1) {
28
+ // "status" → { field: "status", operator: "eq", type: "string" }
29
+ return { field: parts[0], operator: 'eq', type: 'string' };
30
+ }
31
+ if (parts.length === 2) {
32
+ // "priority:gte" → { field: "priority", operator: "gte", type: "string" }
33
+ const operator = parts[1];
34
+ if (!isValidOperator(operator)) {
35
+ throw new Error(`Invalid filter operator: ${parts[1]}`);
36
+ }
37
+ return { field: parts[0], operator, type: 'string' };
38
+ }
39
+ if (parts.length === 3) {
40
+ // "priority:gte:number" → { field: "priority", operator: "gte", type: "number" }
41
+ const operator = parts[1];
42
+ const type = parts[2];
43
+ if (!isValidOperator(operator)) {
44
+ throw new Error(`Invalid filter operator: ${parts[1]}`);
45
+ }
46
+ if (!isValidType(type)) {
47
+ throw new Error(`Invalid filter type: ${parts[2]}`);
48
+ }
49
+ return { field: parts[0], operator, type };
50
+ }
51
+ throw new Error(`Invalid filter field syntax: ${fieldKey}`);
52
+ }
53
+ /**
54
+ * 演算子が有効かチェックする
55
+ */
56
+ function isValidOperator(operator) {
57
+ return ['eq', 'lt', 'lte', 'gt', 'gte', 'starts', 'ends'].includes(operator);
58
+ }
59
+ /**
60
+ * 型が有効かチェックする
61
+ */
62
+ function isValidType(type) {
63
+ return ['string', 'number', 'date', 'boolean'].includes(type);
64
+ }
65
+ /**
66
+ * シャドーレコードを除外するフィルター式を生成する
67
+ *
68
+ * シャドーレコードの特徴:
69
+ * - SK に '#' が含まれる(例: "name#value#id#ULID")
70
+ * - 本体レコードは "id#ULID" 形式で '#' が1つのみ
71
+ *
72
+ * 除外条件:
73
+ * - attribute_exists(data) AND NOT contains(SK, '#id#')
74
+ *
75
+ * @returns シャドー除外フィルター式
76
+ */
77
+ export function createShadowExclusionFilter() {
78
+ return {
79
+ expression: 'attribute_exists(#data) AND NOT contains(#sk, :shadowMarker)',
80
+ names: {
81
+ '#data': 'data',
82
+ '#sk': 'SK',
83
+ },
84
+ values: {
85
+ ':shadowMarker': '#id#',
86
+ },
87
+ };
88
+ }
89
+ /**
90
+ * ユーザー指定のフィルター条件から FilterExpression を構築する
91
+ *
92
+ * 注意: この実装は基本的な等価比較のみをサポート
93
+ * より複雑な条件(範囲検索、部分一致など)は将来の拡張として実装可能
94
+ *
95
+ * @param filter - フィルター条件(キー: フィールド名、値: 期待値)
96
+ * @returns フィルター式の構築結果
97
+ */
98
+ export function buildFilterExpression(filter) {
99
+ const entries = Object.entries(filter);
100
+ // フィルター条件が空の場合は null を返す
101
+ if (entries.length === 0) {
102
+ return null;
103
+ }
104
+ const expressions = [];
105
+ const names = {};
106
+ const values = {};
107
+ entries.forEach(([key, value], index) => {
108
+ const nameKey = `#field${index}`;
109
+ const valueKey = `:value${index}`;
110
+ // data.{field} = :value 形式の条件を生成
111
+ expressions.push(`#data.${nameKey} = ${valueKey}`);
112
+ names['#data'] = 'data';
113
+ names[nameKey] = key;
114
+ values[valueKey] = value;
115
+ });
116
+ return {
117
+ expression: expressions.join(' AND '),
118
+ names,
119
+ values,
120
+ };
121
+ }
122
+ /**
123
+ * 複数のフィルター式を AND 条件で結合する
124
+ *
125
+ * @param filters - 結合するフィルター式の配列
126
+ * @returns 結合されたフィルター式
127
+ */
128
+ export function combineFilters(filters) {
129
+ // null を除外
130
+ const validFilters = filters.filter((f) => f !== null);
131
+ if (validFilters.length === 0) {
132
+ return null;
133
+ }
134
+ if (validFilters.length === 1) {
135
+ return validFilters[0];
136
+ }
137
+ // 式を AND で結合
138
+ const expression = validFilters.map((f) => `(${f.expression})`).join(' AND ');
139
+ // names と values をマージ
140
+ const names = {};
141
+ const values = {};
142
+ validFilters.forEach((f) => {
143
+ Object.assign(names, f.names);
144
+ Object.assign(values, f.values);
145
+ });
146
+ return {
147
+ expression,
148
+ names,
149
+ values,
150
+ };
151
+ }
152
+ /**
153
+ * フィルター式を DynamoDB Query/Scan パラメータに適用する
154
+ *
155
+ * @param params - DynamoDB Query/Scan パラメータ
156
+ * @param filter - フィルター式
157
+ * @returns フィルター式が適用されたパラメータ
158
+ */
159
+ export function applyFilterExpression(params, filter) {
160
+ if (!filter) {
161
+ return params;
162
+ }
163
+ return {
164
+ ...params,
165
+ FilterExpression: filter.expression,
166
+ ExpressionAttributeNames: {
167
+ ...params.ExpressionAttributeNames,
168
+ ...filter.names,
169
+ },
170
+ ExpressionAttributeValues: {
171
+ ...params.ExpressionAttributeValues,
172
+ ...filter.values,
173
+ },
174
+ };
175
+ }
176
+ /**
177
+ * 型変換ヘルパー
178
+ *
179
+ * 要件: 12.5
180
+ *
181
+ * @param value - 変換する値
182
+ * @param type - 変換先の型
183
+ * @returns 変換後の値
184
+ */
185
+ export function convertType(value, type) {
186
+ switch (type) {
187
+ case 'string':
188
+ return String(value);
189
+ case 'number':
190
+ return Number(value);
191
+ case 'date':
192
+ return new Date(String(value));
193
+ case 'boolean':
194
+ // 文字列 "true"/"false" または boolean値を処理
195
+ if (typeof value === 'boolean')
196
+ return value;
197
+ return String(value).toLowerCase() === 'true';
198
+ default:
199
+ return String(value);
200
+ }
201
+ }
202
+ /**
203
+ * レコードが単一のフィルター条件に一致するかチェックする
204
+ *
205
+ * 要件: 12.2, 12.6, 12.8, 12.9
206
+ *
207
+ * @param record - チェック対象レコード
208
+ * @param parsed - パース済みフィルター条件
209
+ * @param value - フィルター値
210
+ * @returns 一致する場合true
211
+ */
212
+ export function matchesFilter(record, parsed, value) {
213
+ const fieldValue = record[parsed.field];
214
+ // フィールドが存在しない場合は不一致
215
+ if (fieldValue === undefined || fieldValue === null) {
216
+ return false;
217
+ }
218
+ // 型変換
219
+ const typedFieldValue = convertType(fieldValue, parsed.type);
220
+ const typedFilterValue = convertType(value, parsed.type);
221
+ // 演算子に応じた比較
222
+ switch (parsed.operator) {
223
+ case 'eq':
224
+ return typedFieldValue === typedFilterValue;
225
+ case 'lt':
226
+ return typedFieldValue < typedFilterValue;
227
+ case 'lte':
228
+ return typedFieldValue <= typedFilterValue;
229
+ case 'gt':
230
+ return typedFieldValue > typedFilterValue;
231
+ case 'gte':
232
+ return typedFieldValue >= typedFilterValue;
233
+ case 'starts':
234
+ return String(typedFieldValue).startsWith(String(typedFilterValue));
235
+ case 'ends':
236
+ return String(typedFieldValue).endsWith(String(typedFilterValue));
237
+ default:
238
+ return false;
239
+ }
240
+ }
241
+ /**
242
+ * レコードがすべてのフィルター条件に一致するかチェックする(AND条件)
243
+ *
244
+ * 要件: 12.9
245
+ *
246
+ * @param record - チェック対象レコード
247
+ * @param parsedFilters - パース済みフィルター条件の配列
248
+ * @returns すべての条件に一致する場合true
249
+ */
250
+ export function matchesAllFilters(record, parsedFilters) {
251
+ return parsedFilters.every((f) => matchesFilter(record, f.parsed, f.value));
252
+ }
253
+ /**
254
+ * Query最適化が可能かチェックする
255
+ *
256
+ * ソートフィールドと一致し、Query可能な演算子を持つフィルター条件を検出する
257
+ *
258
+ * 要件: 12.7
259
+ *
260
+ * @param sortField - ソート対象フィールド
261
+ * @param parsedFilters - パース済みフィルター条件
262
+ * @returns 最適化可能なフィルター条件(なければnull)
263
+ */
264
+ export function findOptimizableFilter(sortField, parsedFilters) {
265
+ // Query可能な演算子
266
+ const queryableOperators = ['eq', 'lt', 'lte', 'gt', 'gte', 'starts'];
267
+ // ソートフィールドと一致し、Query可能な演算子を持つフィルターを探す
268
+ return (parsedFilters.find((f) => f.parsed.field === sortField && queryableOperators.includes(f.parsed.operator)) || null);
269
+ }
270
+ //# sourceMappingURL=filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter.js","sourceRoot":"","sources":["../../../src/server/utils/filter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * nextToken のペイロード構造
3
+ */
4
+ interface NextTokenPayload {
5
+ /** パーティションキー */
6
+ PK: string;
7
+ /** ソートキー */
8
+ SK: string;
9
+ }
10
+ /**
11
+ * DynamoDB LastEvaluatedKey を Base64URL エンコードされた nextToken に変換する
12
+ *
13
+ * @param pk - パーティションキー
14
+ * @param sk - ソートキー
15
+ * @returns Base64URL エンコードされた nextToken
16
+ */
17
+ export declare function encodeNextToken(pk: string, sk: string): string;
18
+ /**
19
+ * Base64URL エンコードされた nextToken を DynamoDB ExclusiveStartKey にデコードする
20
+ *
21
+ * @param token - Base64URL エンコードされた nextToken
22
+ * @returns デコードされた NextTokenPayload
23
+ * @throws {InvalidTokenError} トークンのデコードに失敗した場合
24
+ */
25
+ export declare function decodeNextToken(token: string): NextTokenPayload;
26
+ export {};
27
+ //# sourceMappingURL=pagination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../../src/server/utils/pagination.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,UAAU,gBAAgB;IACxB,gBAAgB;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,YAAY;IACZ,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAW9D;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CA4B/D"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * ページネーションユーティリティ
3
+ * nextToken のエンコード/デコード(Base64URL形式)
4
+ *
5
+ * 要件: 5.5, 7.1
6
+ */
7
+ import { InvalidTokenError } from '../../index.js';
8
+ /**
9
+ * DynamoDB LastEvaluatedKey を Base64URL エンコードされた nextToken に変換する
10
+ *
11
+ * @param pk - パーティションキー
12
+ * @param sk - ソートキー
13
+ * @returns Base64URL エンコードされた nextToken
14
+ */
15
+ export function encodeNextToken(pk, sk) {
16
+ const payload = { PK: pk, SK: sk };
17
+ const json = JSON.stringify(payload);
18
+ // Base64 エンコード
19
+ const base64 = Buffer.from(json, 'utf-8').toString('base64');
20
+ // Base64URL 形式に変換(URL セーフ)
21
+ const base64url = base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
22
+ return base64url;
23
+ }
24
+ /**
25
+ * Base64URL エンコードされた nextToken を DynamoDB ExclusiveStartKey にデコードする
26
+ *
27
+ * @param token - Base64URL エンコードされた nextToken
28
+ * @returns デコードされた NextTokenPayload
29
+ * @throws {InvalidTokenError} トークンのデコードに失敗した場合
30
+ */
31
+ export function decodeNextToken(token) {
32
+ try {
33
+ // Base64URL から Base64 に変換
34
+ let base64 = token.replace(/-/g, '+').replace(/_/g, '/');
35
+ // パディングを追加(必要に応じて)
36
+ while (base64.length % 4 !== 0) {
37
+ base64 += '=';
38
+ }
39
+ // Base64 デコード
40
+ const json = Buffer.from(base64, 'base64').toString('utf-8');
41
+ // JSON パース
42
+ const payload = JSON.parse(json);
43
+ // 必須フィールドの検証
44
+ if (!payload.PK || !payload.SK) {
45
+ throw new Error('Missing required fields in token payload');
46
+ }
47
+ return payload;
48
+ }
49
+ catch (error) {
50
+ throw new InvalidTokenError('Failed to decode nextToken', {
51
+ token,
52
+ error: error instanceof Error ? error.message : String(error),
53
+ });
54
+ }
55
+ }
56
+ //# sourceMappingURL=pagination.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination.js","sourceRoot":"","sources":["../../../src/server/utils/pagination.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAYnD;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,EAAU,EAAE,EAAU;IACpD,MAAM,OAAO,GAAqB,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;IACrD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAErC,eAAe;IACf,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAE7D,2BAA2B;IAC3B,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAEnF,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,IAAI,CAAC;QACH,0BAA0B;QAC1B,IAAI,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAEzD,mBAAmB;QACnB,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,GAAG,CAAC;QAChB,CAAC;QAED,cAAc;QACd,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAE7D,WAAW;QACX,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAqB,CAAC;QAErD,aAAa;QACb,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAC9D,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,iBAAiB,CAAC,4BAA4B,EAAE;YACxD,KAAK;YACL,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAC9D,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * タイムスタンプフィールド設定
3
+ */
4
+ export interface TimestampFields {
5
+ createdAt: string;
6
+ updatedAt: string;
7
+ }
8
+ /**
9
+ * タイムスタンプフィールド設定を取得する
10
+ *
11
+ * shadow.config.json の database.timestamps から取得
12
+ * 設定がない場合はデフォルト値(createdAt, updatedAt)を使用
13
+ *
14
+ * @returns タイムスタンプフィールド設定、または null(無効化されている場合)
15
+ */
16
+ export declare function getTimestampFields(): TimestampFields | null;
17
+ /**
18
+ * レコードデータにタイムスタンプフィールドを追加する(作成時)
19
+ *
20
+ * @param data - レコードデータ
21
+ * @returns タイムスタンプが追加されたレコードデータ
22
+ */
23
+ export declare function addCreateTimestamps(data: Record<string, unknown>): Record<string, unknown>;
24
+ /**
25
+ * レコードデータにタイムスタンプフィールドを追加する(更新時)
26
+ *
27
+ * @param data - レコードデータ
28
+ * @returns タイムスタンプが追加されたレコードデータ
29
+ */
30
+ export declare function addUpdateTimestamp(data: Record<string, unknown>): Record<string, unknown>;
31
+ //# sourceMappingURL=timestamps.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * タイムスタンプフィールド管理ユーティリティ
3
+ *
4
+ * shadow.config.json のデータベース設定から
5
+ * タイムスタンプフィールド名を取得し、動的に設定する
6
+ */
7
+ import { getShadowConfig } from '../../index.js';
8
+ /**
9
+ * タイムスタンプフィールド設定を取得する
10
+ *
11
+ * shadow.config.json の database.timestamps から取得
12
+ * 設定がない場合はデフォルト値(createdAt, updatedAt)を使用
13
+ *
14
+ * @returns タイムスタンプフィールド設定、または null(無効化されている場合)
15
+ */
16
+ export function getTimestampFields() {
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
+ // デフォルト値を返す
42
+ return {
43
+ createdAt: 'createdAt',
44
+ updatedAt: 'updatedAt',
45
+ };
46
+ }
47
+ /**
48
+ * レコードデータにタイムスタンプフィールドを追加する(作成時)
49
+ *
50
+ * @param data - レコードデータ
51
+ * @returns タイムスタンプが追加されたレコードデータ
52
+ */
53
+ export function addCreateTimestamps(data) {
54
+ const timestampFields = getTimestampFields();
55
+ // タイムスタンプが無効化されている場合はそのまま返す
56
+ if (!timestampFields) {
57
+ return data;
58
+ }
59
+ const now = new Date().toISOString();
60
+ return {
61
+ ...data,
62
+ [timestampFields.createdAt]: now,
63
+ [timestampFields.updatedAt]: now,
64
+ };
65
+ }
66
+ /**
67
+ * レコードデータにタイムスタンプフィールドを追加する(更新時)
68
+ *
69
+ * @param data - レコードデータ
70
+ * @returns タイムスタンプが追加されたレコードデータ
71
+ */
72
+ export function addUpdateTimestamp(data) {
73
+ const timestampFields = getTimestampFields();
74
+ // タイムスタンプが無効化されている場合はそのまま返す
75
+ if (!timestampFields) {
76
+ return data;
77
+ }
78
+ const now = new Date().toISOString();
79
+ return {
80
+ ...data,
81
+ [timestampFields.updatedAt]: now,
82
+ };
83
+ }
84
+ //# sourceMappingURL=timestamps.js.map
@@ -0,0 +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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * TTLを計算する
3
+ *
4
+ * @param resource - リソース名
5
+ * @param createdAt - 作成日時(ISO 8601形式)
6
+ * @returns TTL(Unix timestamp、秒単位)、TTL不要の場合はundefined
7
+ */
8
+ export declare function calculateTTL(resource: string, createdAt: string): number | undefined;
9
+ /**
10
+ * レコードデータにTTLを追加する
11
+ *
12
+ * @param resource - リソース名
13
+ * @param recordData - レコードデータ
14
+ * @returns TTLが追加されたレコードデータ
15
+ */
16
+ export declare function addTTL(resource: string, recordData: Record<string, unknown>): Record<string, unknown>;
17
+ //# sourceMappingURL=ttl.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * TTL(Time To Live)ユーティリティ
3
+ * DynamoDB TTL設定を管理
4
+ */
5
+ import { createLogger, getShadowConfig } from '../../index.js';
6
+ const logger = createLogger({ service: 'records-lambda' });
7
+ /**
8
+ * TTLを計算する
9
+ *
10
+ * @param resource - リソース名
11
+ * @param createdAt - 作成日時(ISO 8601形式)
12
+ * @returns TTL(Unix timestamp、秒単位)、TTL不要の場合はundefined
13
+ */
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) {
19
+ // TTL設定がないリソースはundefinedを返す
20
+ return undefined;
21
+ }
22
+ const defaultDays = resourceConfig.ttl.days;
23
+ // 環境変数から上書き可能(後方互換性のため)
24
+ const envKey = `${resource.toUpperCase()}_TTL_DAYS`;
25
+ const ttlDays = parseInt(process.env[envKey] || String(defaultDays), 10);
26
+ // 作成日時からTTLを計算
27
+ const createdAtMs = new Date(createdAt).getTime();
28
+ const ttlMs = createdAtMs + ttlDays * 24 * 60 * 60 * 1000;
29
+ const ttl = Math.floor(ttlMs / 1000);
30
+ logger.debug('TTL calculated', {
31
+ resource,
32
+ ttlDays,
33
+ createdAt,
34
+ ttl,
35
+ ttlDate: new Date(ttl * 1000).toISOString(),
36
+ });
37
+ return ttl;
38
+ }
39
+ /**
40
+ * レコードデータにTTLを追加する
41
+ *
42
+ * @param resource - リソース名
43
+ * @param recordData - レコードデータ
44
+ * @returns TTLが追加されたレコードデータ
45
+ */
46
+ export function addTTL(resource, recordData) {
47
+ const createdAt = recordData.createdAt;
48
+ if (!createdAt) {
49
+ logger.warn('createdAt not found, skipping TTL calculation', { resource });
50
+ return recordData;
51
+ }
52
+ const ttl = calculateTTL(resource, createdAt);
53
+ if (ttl === undefined) {
54
+ // TTL不要のリソース
55
+ return recordData;
56
+ }
57
+ return {
58
+ ...recordData,
59
+ ttl,
60
+ };
61
+ }
62
+ //# sourceMappingURL=ttl.js.map
@@ -0,0 +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"}
@@ -0,0 +1,40 @@
1
+ import type { ShadowConfig } from '../shadow/index.js';
2
+ import type { FindManyReferenceParams, FindParams } from '../types.js';
3
+ /**
4
+ * getList/getManyReferenceのソートフィールドを検証する
5
+ *
6
+ * @param config - シャドー設定
7
+ * @param resource - リソース名
8
+ * @param sort - ソート条件
9
+ * @throws {InvalidFilterError} ソートフィールドが無効な場合
10
+ */
11
+ export declare function validateSortField(config: ShadowConfig, resource: string, sort?: {
12
+ field: string;
13
+ order: 'ASC' | 'DESC';
14
+ }): void;
15
+ /**
16
+ * ページネーションパラメータを正規化する
17
+ *
18
+ * @param pagination - ページネーション条件
19
+ * @returns 正規化されたページネーション条件
20
+ */
21
+ export declare function normalizePagination(pagination?: FindParams['pagination'] | FindManyReferenceParams['pagination']): {
22
+ perPage: number;
23
+ nextToken?: string;
24
+ };
25
+ /**
26
+ * ソート条件を正規化する(デフォルト値を適用)
27
+ *
28
+ * @param config - シャドー設定
29
+ * @param resource - リソース名
30
+ * @param sort - ソート条件
31
+ * @returns 正規化されたソート条件
32
+ */
33
+ export declare function normalizeSort(config: ShadowConfig, resource: string, sort?: {
34
+ field: string;
35
+ order: 'ASC' | 'DESC';
36
+ }): {
37
+ field: string;
38
+ order: 'ASC' | 'DESC';
39
+ };
40
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../../src/server/utils/validation.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEvE;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAAA;CAAE,GAC9C,IAAI,CAgBN;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,CAAC,EAAE,UAAU,CAAC,YAAY,CAAC,GAAG,uBAAuB,CAAC,YAAY,CAAC,GAC5E;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAKzC;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAAA;CAAE,GAC9C;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAAA;CAAE,CAO1C"}