@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.
- package/CHANGELOG.md +41 -0
- package/LICENSE +21 -0
- package/README.md +283 -0
- package/dist/client/Collection.d.ts +57 -0
- package/dist/client/Collection.d.ts.map +1 -0
- package/dist/client/Collection.js +174 -0
- package/dist/client/Collection.js.map +1 -0
- package/dist/client/Database.d.ts +35 -0
- package/dist/client/Database.d.ts.map +1 -0
- package/dist/client/Database.js +48 -0
- package/dist/client/Database.js.map +1 -0
- package/dist/client/DynamoClient.d.ts +43 -0
- package/dist/client/DynamoClient.d.ts.map +1 -0
- package/dist/client/DynamoClient.js +62 -0
- package/dist/client/DynamoClient.js.map +1 -0
- package/dist/client/FindCursor.d.ts +174 -0
- package/dist/client/FindCursor.d.ts.map +1 -0
- package/dist/client/FindCursor.js +256 -0
- package/dist/client/FindCursor.js.map +1 -0
- package/dist/client/aws-sigv4.d.ts +10 -0
- package/dist/client/aws-sigv4.d.ts.map +1 -0
- package/dist/client/aws-sigv4.js +54 -0
- package/dist/client/aws-sigv4.js.map +1 -0
- package/dist/client/index.cognito.d.ts +34 -0
- package/dist/client/index.cognito.d.ts.map +1 -0
- package/dist/client/index.cognito.js +30 -0
- package/dist/client/index.cognito.js.map +1 -0
- package/dist/client/index.d.ts +12 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.iam.d.ts +34 -0
- package/dist/client/index.iam.d.ts.map +1 -0
- package/dist/client/index.iam.js +28 -0
- package/dist/client/index.iam.js.map +1 -0
- package/dist/client/index.js +12 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/index.token.d.ts +33 -0
- package/dist/client/index.token.d.ts.map +1 -0
- package/dist/client/index.token.js +28 -0
- package/dist/client/index.token.js.map +1 -0
- package/dist/dynamodb.d.ts +20 -0
- package/dist/dynamodb.d.ts.map +1 -0
- package/dist/dynamodb.js +31 -0
- package/dist/dynamodb.js.map +1 -0
- package/dist/errors.d.ts +100 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +146 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/react-admin/dataProvider.d.ts +59 -0
- package/dist/integrations/react-admin/dataProvider.d.ts.map +1 -0
- package/dist/integrations/react-admin/dataProvider.js +364 -0
- package/dist/integrations/react-admin/dataProvider.js.map +1 -0
- package/dist/integrations/react-admin/index.d.ts +24 -0
- package/dist/integrations/react-admin/index.d.ts.map +1 -0
- package/dist/integrations/react-admin/index.js +23 -0
- package/dist/integrations/react-admin/index.js.map +1 -0
- package/dist/integrations/react-admin/types.d.ts +47 -0
- package/dist/integrations/react-admin/types.d.ts.map +1 -0
- package/dist/integrations/react-admin/types.js +5 -0
- package/dist/integrations/react-admin/types.js.map +1 -0
- package/dist/logger.d.ts +61 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +87 -0
- package/dist/logger.js.map +1 -0
- package/dist/scripts/repair-shadows.d.ts +3 -0
- package/dist/scripts/repair-shadows.d.ts.map +1 -0
- package/dist/scripts/repair-shadows.js +190 -0
- package/dist/scripts/repair-shadows.js.map +1 -0
- package/dist/server/handler.cjs +31378 -0
- package/dist/server/handler.cjs.map +7 -0
- package/dist/server/handler.d.ts +18 -0
- package/dist/server/handler.d.ts.map +1 -0
- package/dist/server/handler.js +435 -0
- package/dist/server/handler.js.map +1 -0
- package/dist/server/handler.zip +0 -0
- package/dist/server/index.d.ts +8 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +8 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/operations/deleteMany.d.ts +18 -0
- package/dist/server/operations/deleteMany.d.ts.map +1 -0
- package/dist/server/operations/deleteMany.js +222 -0
- package/dist/server/operations/deleteMany.js.map +1 -0
- package/dist/server/operations/deleteOne.d.ts +17 -0
- package/dist/server/operations/deleteOne.d.ts.map +1 -0
- package/dist/server/operations/deleteOne.js +87 -0
- package/dist/server/operations/deleteOne.js.map +1 -0
- package/dist/server/operations/find.d.ts +18 -0
- package/dist/server/operations/find.d.ts.map +1 -0
- package/dist/server/operations/find.js +382 -0
- package/dist/server/operations/find.js.map +1 -0
- package/dist/server/operations/findMany.d.ts +13 -0
- package/dist/server/operations/findMany.d.ts.map +1 -0
- package/dist/server/operations/findMany.js +61 -0
- package/dist/server/operations/findMany.js.map +1 -0
- package/dist/server/operations/findManyReference.d.ts +18 -0
- package/dist/server/operations/findManyReference.d.ts.map +1 -0
- package/dist/server/operations/findManyReference.js +150 -0
- package/dist/server/operations/findManyReference.js.map +1 -0
- package/dist/server/operations/findOne.d.ts +14 -0
- package/dist/server/operations/findOne.d.ts.map +1 -0
- package/dist/server/operations/findOne.js +56 -0
- package/dist/server/operations/findOne.js.map +1 -0
- package/dist/server/operations/insertMany.d.ts +19 -0
- package/dist/server/operations/insertMany.d.ts.map +1 -0
- package/dist/server/operations/insertMany.js +243 -0
- package/dist/server/operations/insertMany.js.map +1 -0
- package/dist/server/operations/insertOne.d.ts +18 -0
- package/dist/server/operations/insertOne.d.ts.map +1 -0
- package/dist/server/operations/insertOne.js +85 -0
- package/dist/server/operations/insertOne.js.map +1 -0
- package/dist/server/operations/updateMany.d.ts +20 -0
- package/dist/server/operations/updateMany.d.ts.map +1 -0
- package/dist/server/operations/updateMany.js +316 -0
- package/dist/server/operations/updateMany.js.map +1 -0
- package/dist/server/operations/updateOne.d.ts +20 -0
- package/dist/server/operations/updateOne.d.ts.map +1 -0
- package/dist/server/operations/updateOne.js +159 -0
- package/dist/server/operations/updateOne.js.map +1 -0
- package/dist/server/query/converter.d.ts +85 -0
- package/dist/server/query/converter.d.ts.map +1 -0
- package/dist/server/query/converter.js +161 -0
- package/dist/server/query/converter.js.map +1 -0
- package/dist/server/query/index.d.ts +5 -0
- package/dist/server/query/index.d.ts.map +1 -0
- package/dist/server/query/index.js +5 -0
- package/dist/server/query/index.js.map +1 -0
- package/dist/server/shadow/config.d.ts +147 -0
- package/dist/server/shadow/config.d.ts.map +1 -0
- package/dist/server/shadow/config.js +162 -0
- package/dist/server/shadow/config.js.map +1 -0
- package/dist/server/shadow/differ.d.ts +42 -0
- package/dist/server/shadow/differ.d.ts.map +1 -0
- package/dist/server/shadow/differ.js +66 -0
- package/dist/server/shadow/differ.js.map +1 -0
- package/dist/server/shadow/generator.d.ts +104 -0
- package/dist/server/shadow/generator.d.ts.map +1 -0
- package/dist/server/shadow/generator.js +148 -0
- package/dist/server/shadow/generator.js.map +1 -0
- package/dist/server/shadow/index.d.ts +11 -0
- package/dist/server/shadow/index.d.ts.map +1 -0
- package/dist/server/shadow/index.js +11 -0
- package/dist/server/shadow/index.js.map +1 -0
- package/dist/server/shadow/types.d.ts +44 -0
- package/dist/server/shadow/types.d.ts.map +1 -0
- package/dist/server/shadow/types.js +2 -0
- package/dist/server/shadow/types.js.map +1 -0
- package/dist/server/types.d.ts +295 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/types.js +7 -0
- package/dist/server/types.js.map +1 -0
- package/dist/server/utils/auth.d.ts +43 -0
- package/dist/server/utils/auth.d.ts.map +1 -0
- package/dist/server/utils/auth.js +123 -0
- package/dist/server/utils/auth.js.map +1 -0
- package/dist/server/utils/bulkOperations.d.ts +81 -0
- package/dist/server/utils/bulkOperations.d.ts.map +1 -0
- package/dist/server/utils/bulkOperations.js +147 -0
- package/dist/server/utils/bulkOperations.js.map +1 -0
- package/dist/server/utils/chunking.d.ts +96 -0
- package/dist/server/utils/chunking.d.ts.map +1 -0
- package/dist/server/utils/chunking.js +225 -0
- package/dist/server/utils/chunking.js.map +1 -0
- package/dist/server/utils/dynamodb.d.ts +41 -0
- package/dist/server/utils/dynamodb.d.ts.map +1 -0
- package/dist/server/utils/dynamodb.js +83 -0
- package/dist/server/utils/dynamodb.js.map +1 -0
- package/dist/server/utils/filter.d.ts +152 -0
- package/dist/server/utils/filter.d.ts.map +1 -0
- package/dist/server/utils/filter.js +270 -0
- package/dist/server/utils/filter.js.map +1 -0
- package/dist/server/utils/pagination.d.ts +27 -0
- package/dist/server/utils/pagination.d.ts.map +1 -0
- package/dist/server/utils/pagination.js +56 -0
- package/dist/server/utils/pagination.js.map +1 -0
- package/dist/server/utils/timestamps.d.ts +31 -0
- package/dist/server/utils/timestamps.d.ts.map +1 -0
- package/dist/server/utils/timestamps.js +84 -0
- package/dist/server/utils/timestamps.js.map +1 -0
- package/dist/server/utils/ttl.d.ts +17 -0
- package/dist/server/utils/ttl.d.ts.map +1 -0
- package/dist/server/utils/ttl.js +62 -0
- package/dist/server/utils/ttl.js.map +1 -0
- package/dist/server/utils/validation.d.ts +40 -0
- package/dist/server/utils/validation.d.ts.map +1 -0
- package/dist/server/utils/validation.js +54 -0
- package/dist/server/utils/validation.js.map +1 -0
- package/dist/shadows/config.d.ts +54 -0
- package/dist/shadows/config.d.ts.map +1 -0
- package/dist/shadows/config.js +95 -0
- package/dist/shadows/config.js.map +1 -0
- package/dist/shadows/differ.d.ts +42 -0
- package/dist/shadows/differ.d.ts.map +1 -0
- package/dist/shadows/differ.js +66 -0
- package/dist/shadows/differ.js.map +1 -0
- package/dist/shadows/generator.d.ts +63 -0
- package/dist/shadows/generator.d.ts.map +1 -0
- package/dist/shadows/generator.js +107 -0
- package/dist/shadows/generator.js.map +1 -0
- package/dist/shadows/index.d.ts +15 -0
- package/dist/shadows/index.d.ts.map +1 -0
- package/dist/shadows/index.js +17 -0
- package/dist/shadows/index.js.map +1 -0
- package/dist/shadows/types.d.ts +44 -0
- package/dist/shadows/types.d.ts.map +1 -0
- package/dist/shadows/types.js +2 -0
- package/dist/shadows/types.js.map +1 -0
- package/dist/types.d.ts +165 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/ulid.d.ts +46 -0
- package/dist/ulid.d.ts.map +1 -0
- package/dist/ulid.js +66 -0
- package/dist/ulid.js.map +1 -0
- package/package.json +136 -0
- package/terraform/README.md +222 -0
- package/terraform/examples/advanced/README.md +129 -0
- package/terraform/examples/advanced/main.tf +158 -0
- package/terraform/examples/advanced/shadow.config.json +35 -0
- package/terraform/examples/advanced/variables.tf +28 -0
- package/terraform/examples/basic/README.md +53 -0
- package/terraform/examples/basic/main.tf +99 -0
- package/terraform/examples/basic/variables.tf +17 -0
- package/terraform/main.tf +159 -0
- package/terraform/outputs.tf +56 -0
- 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"}
|