pg-mvc-service 2.0.3 → 2.0.5

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.
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.Base64Client = void 0;
16
16
  const pdf_lib_1 = require("pdf-lib");
17
17
  const sharp_1 = __importDefault(require("sharp"));
18
+ const type_utils_n_daira_1 = require("type-utils-n-daira");
18
19
  class Base64Client {
19
20
  constructor() { }
20
21
  // public encode(text: string): string {
@@ -150,5 +151,62 @@ class Base64Client {
150
151
  return Buffer.from(pdfBytes);
151
152
  });
152
153
  }
154
+ static isJpeg(value) {
155
+ if (type_utils_n_daira_1.ValidateStringUtil.isBase64(value) === false) {
156
+ return false;
157
+ }
158
+ if (value.startsWith('data:')) {
159
+ if (value.startsWith('data:image/jpeg,') === false && value.startsWith('data:image/jpg,') === false) {
160
+ return false;
161
+ }
162
+ const valueParts = value.split(',');
163
+ if (valueParts.length !== 2) {
164
+ return false;
165
+ }
166
+ return valueParts[1].startsWith(this.PREFIX_JPEG_DATA);
167
+ }
168
+ return value.startsWith(this.PREFIX_JPEG_DATA);
169
+ }
170
+ static isPng(value) {
171
+ if (type_utils_n_daira_1.ValidateStringUtil.isBase64(value) === false) {
172
+ return false;
173
+ }
174
+ if (value.startsWith('data:')) {
175
+ if (value.startsWith('data:image/png,') === false) {
176
+ return false;
177
+ }
178
+ const valueParts = value.split(',');
179
+ if (valueParts.length !== 2) {
180
+ return false;
181
+ }
182
+ return valueParts[1].startsWith(this.PREFIX_PNG_DATA);
183
+ }
184
+ return value.startsWith(this.PREFIX_PNG_DATA);
185
+ }
186
+ static tryConvertToPng(base64Value) {
187
+ return __awaiter(this, void 0, void 0, function* () {
188
+ if (type_utils_n_daira_1.ValidateStringUtil.isBase64(base64Value) === false) {
189
+ return false;
190
+ }
191
+ const base64Data = base64Value.startsWith('data:') ? base64Value.split(',')[1] : base64Value;
192
+ if (this.isPng(base64Data)) {
193
+ return base64Data;
194
+ }
195
+ else if (this.isJpeg(base64Data)) {
196
+ const buffer = Buffer.from(base64Data, 'base64');
197
+ try {
198
+ const pngBuffer = yield (0, sharp_1.default)(buffer)
199
+ .ensureAlpha().png().toBuffer();
200
+ return pngBuffer.toString('base64');
201
+ }
202
+ catch (e) {
203
+ return false;
204
+ }
205
+ }
206
+ return false;
207
+ });
208
+ }
153
209
  }
154
210
  exports.Base64Client = Base64Client;
211
+ Base64Client.PREFIX_JPEG_DATA = '/9j/';
212
+ Base64Client.PREFIX_PNG_DATA = 'iVBORw0KGgo';
@@ -198,6 +198,52 @@ class ValidateValueUtil {
198
198
  }
199
199
  return true;
200
200
  }
201
+ static isErrorInteger(value) {
202
+ let numberValue;
203
+ if (typeof value === 'string') {
204
+ if (value.trim() === "" || isNaN(Number(value))) {
205
+ return true;
206
+ }
207
+ numberValue = Number(value);
208
+ }
209
+ else if (typeof value === 'number') {
210
+ numberValue = value;
211
+ }
212
+ else {
213
+ return true;
214
+ }
215
+ if (Number.isInteger(numberValue) === false) {
216
+ return true;
217
+ }
218
+ if (numberValue < -2147483648 || numberValue > 2147483647) {
219
+ return true;
220
+ }
221
+ return false;
222
+ }
223
+ static isErrorReal(value) {
224
+ let numberValue;
225
+ if (typeof value === 'string') {
226
+ if (value.trim() === "" || isNaN(Number(value))) {
227
+ return true;
228
+ }
229
+ numberValue = Number(value);
230
+ }
231
+ else if (typeof value === 'number') {
232
+ numberValue = value;
233
+ }
234
+ else {
235
+ return true;
236
+ }
237
+ // 特殊値チェック
238
+ if (isFinite(numberValue) === false) {
239
+ return true;
240
+ }
241
+ // 範囲チェック(real型の範囲)
242
+ if (numberValue < -3.4e38 || numberValue > 3.4e38) {
243
+ return true;
244
+ }
245
+ return false;
246
+ }
201
247
  static isErrorNumber(value) {
202
248
  if (typeof value === 'string') {
203
249
  if (value.trim() === "" || isNaN(Number(value))) {
@@ -83,21 +83,21 @@ class WhereExpression {
83
83
  // Are the operators correct?
84
84
  const useableOperator = {
85
85
  integer: ["=", "!=", ">", ">=", "<", "<=", "in", "not in"],
86
- 'integer[]': [],
86
+ 'integer[]': ["=", "any", "@>", "&&"],
87
87
  real: ["=", "!=", ">", ">=", "<", "<="],
88
- 'real[]': [],
88
+ 'real[]': ["=", "any", "@>", "&&"],
89
89
  string: ["=", "!=", "like", "ilike", "h2f_like", "h2f_ilike", "in", "not in"],
90
- 'string[]': [],
90
+ 'string[]': ["=", "any", "@>", "&&"],
91
91
  uuid: ["=", "!=", "in", "not in"],
92
- 'uuid[]': [],
92
+ 'uuid[]': ["=", "any", "@>", "&&"],
93
93
  bool: ["=", "!=", "in", "not in"],
94
- 'bool[]': [],
94
+ 'bool[]': ["=", "any", "@>", "&&"],
95
95
  date: ["=", "!=", ">", ">=", "<", "<="],
96
- 'date[]': [],
96
+ 'date[]': ["=", "any", "@>", "&&"],
97
97
  time: ["=", "!=", ">", ">=", "<", "<="],
98
- 'time[]': [],
98
+ 'time[]': ["=", "any", "@>", "&&"],
99
99
  timestamp: ["=", "!=", ">", ">=", "<", "<="],
100
- 'timestamp[]': [],
100
+ 'timestamp[]': ["=", "any", "@>", "&&"],
101
101
  json: [],
102
102
  'json[]': [],
103
103
  jsonb: [],
@@ -124,8 +124,15 @@ class WhereExpression {
124
124
  throw new Error(`When comparing with null, operators other than =, != cannot be used. (${operator})`);
125
125
  }
126
126
  }
127
- if (leftColumn.type.endsWith("[]")) {
128
- return this.createExpression(leftColumn, operator, right, varLength);
127
+ const isColumnRight = right !== null && typeof right === 'object' && 'model' in right && 'name' in right;
128
+ const isArrayColumnLeft = leftColumn.type.endsWith("[]");
129
+ if (isArrayColumnLeft) {
130
+ if (isColumnRight) {
131
+ return this.createExpressionArrayColumn(leftColumn, operator, right, varLength);
132
+ }
133
+ else {
134
+ return this.createExpressionArrayValue(leftColumn, operator, right, varLength);
135
+ }
129
136
  }
130
137
  else {
131
138
  return this.createExpression(leftColumn, operator, right, varLength);
@@ -156,8 +163,9 @@ class WhereExpression {
156
163
  // If the right side value is a column specification
157
164
  if (right !== null && typeof right === 'object' && 'model' in right && 'name' in right) {
158
165
  const rightColumn = right.model.getColumn(right.name);
166
+ // 型の不一致エラーメッセージを改善
159
167
  if (leftColumn.type !== rightColumn.type) {
160
- throw new Error(`The types of [${leftColumn.tableName}].[${leftColumn.columnName}] and [${rightColumn.tableName}].[${rightColumn.columnName}] are different.`);
168
+ throw new Error(`Type mismatch: column [${leftColumn.tableName}].[${leftColumn.columnName}] (${leftColumn.type}) and [${rightColumn.tableName}].[${rightColumn.columnName}] (${rightColumn.type}) must be the same type.`);
161
169
  }
162
170
  // LIKE operators are different, so handle separately
163
171
  switch (operator) {
@@ -198,34 +206,157 @@ class WhereExpression {
198
206
  };
199
207
  }
200
208
  static createExpressionArrayValue(leftColumn, operator, right, varLength) {
201
- // ValidateValueUtil.validateValue(leftColumn, right);
202
- // LIKE operators are different, so handle separately
203
- // switch (operator) {
204
- // case 'like':
205
- // case 'ilike':
206
- // return {
207
- // sql: `${leftColumn.expression} ${operator} $${varLength}`,
208
- // vars: [`%${right}%`]
209
- // }
210
- // case 'h2f_like': // half to full like
211
- // case 'h2f_ilike': // half to full ilike
212
- // return {
213
- // sql: `${this.makeSqlReplaceHalfToFull(leftColumn.expression)} ${operator.replace("h2f_", "")} ${this.makeSqlReplaceHalfToFull(`$${varLength}`)}`,
214
- // vars: [`%${right}%`]
215
- // }
216
- // }
209
+ // バリデーションチェック
210
+ switch (operator) {
211
+ case 'any':
212
+ switch (leftColumn.type) {
213
+ case 'integer[]':
214
+ if (ValidateValueUtil_1.default.isErrorInteger(right)) {
215
+ throw new Error(`Expected integer value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
216
+ }
217
+ break;
218
+ case 'real[]':
219
+ if (ValidateValueUtil_1.default.isErrorReal(right)) {
220
+ throw new Error(`Expected numeric value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
221
+ }
222
+ break;
223
+ case 'string[]':
224
+ if (ValidateValueUtil_1.default.isErrorString(right)) {
225
+ throw new Error(`Expected string value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
226
+ }
227
+ break;
228
+ case 'uuid[]':
229
+ if (ValidateValueUtil_1.default.isErrorUUID(right)) {
230
+ throw new Error(`Expected UUID value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
231
+ }
232
+ break;
233
+ case 'bool[]':
234
+ if (ValidateValueUtil_1.default.isErrorBool(right)) {
235
+ throw new Error(`Expected boolean value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
236
+ }
237
+ break;
238
+ case 'date[]':
239
+ if (ValidateValueUtil_1.default.isErrorDate(right)) {
240
+ throw new Error(`Expected date value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
241
+ }
242
+ break;
243
+ case 'time[]':
244
+ if (ValidateValueUtil_1.default.isErrorTime(right)) {
245
+ throw new Error(`Expected time value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
246
+ }
247
+ break;
248
+ case 'timestamp[]':
249
+ if (ValidateValueUtil_1.default.isErrorTimestamp(right)) {
250
+ throw new Error(`Expected timestamp value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
251
+ }
252
+ break;
253
+ }
254
+ break;
255
+ case '=':
256
+ case '@>':
257
+ case '&&':
258
+ if (Array.isArray(right) === false) {
259
+ throw new Error(`Expected array format for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
260
+ }
261
+ for (const value of right) {
262
+ switch (leftColumn.type) {
263
+ case 'integer[]':
264
+ if (ValidateValueUtil_1.default.isErrorInteger(value)) {
265
+ throw new Error(`Expected integer value in array element, but received: ${JSON.stringify(value)}`);
266
+ }
267
+ break;
268
+ case 'real[]':
269
+ if (ValidateValueUtil_1.default.isErrorReal(value)) {
270
+ throw new Error(`Expected numeric value in array element, but received: ${JSON.stringify(value)}`);
271
+ }
272
+ break;
273
+ case 'string[]':
274
+ if (ValidateValueUtil_1.default.isErrorString(value)) {
275
+ throw new Error(`Expected string value in array element, but received: ${JSON.stringify(value)}`);
276
+ }
277
+ break;
278
+ case 'uuid[]':
279
+ if (ValidateValueUtil_1.default.isErrorUUID(value)) {
280
+ throw new Error(`Expected UUID value in array element, but received: ${JSON.stringify(value)}`);
281
+ }
282
+ break;
283
+ case 'bool[]':
284
+ if (ValidateValueUtil_1.default.isErrorBool(value)) {
285
+ throw new Error(`Expected boolean value in array element, but received: ${JSON.stringify(value)}`);
286
+ }
287
+ break;
288
+ case 'date[]':
289
+ if (ValidateValueUtil_1.default.isErrorDate(value)) {
290
+ throw new Error(`Expected date value in array element, but received: ${JSON.stringify(value)}`);
291
+ }
292
+ break;
293
+ case 'time[]':
294
+ if (ValidateValueUtil_1.default.isErrorTime(value)) {
295
+ throw new Error(`Expected time value in array element, but received: ${JSON.stringify(value)}`);
296
+ }
297
+ break;
298
+ case 'timestamp[]':
299
+ if (ValidateValueUtil_1.default.isErrorTimestamp(value)) {
300
+ throw new Error(`Expected timestamp value in array element, but received: ${JSON.stringify(value)}`);
301
+ }
302
+ break;
303
+ }
304
+ }
305
+ break;
306
+ default:
307
+ throw new Error(`Unsupported operator '${operator}' for array column operations.`);
308
+ }
217
309
  switch (operator) {
218
310
  case '=':
219
- // バリデーションチェックをする
311
+ case '@>':
312
+ case '&&':
220
313
  return {
221
314
  expression: `${leftColumn.expression} ${operator} $${varLength}`,
222
315
  vars: [right]
223
316
  };
317
+ case 'any':
318
+ return {
319
+ expression: `$${varLength} = ANY(${leftColumn.expression})`,
320
+ vars: [right]
321
+ };
322
+ }
323
+ }
324
+ static createExpressionArrayColumn(leftColumn, operator, right, varLength) {
325
+ const rightColumn = right.model.getColumn(right.name);
326
+ // バリデーションチェック
327
+ switch (operator) {
328
+ case 'any':
329
+ // any演算子の場合
330
+ if (leftColumn.type !== rightColumn.type.replace('[]', '')) {
331
+ throw new Error(`Type mismatch: array column [${leftColumn.tableName}].[${leftColumn.columnName}] (${leftColumn.type}) and scalar column [${rightColumn.tableName}].[${rightColumn.columnName}] (${rightColumn.type}) are incompatible for ANY operation.`);
332
+ }
333
+ break;
334
+ case '=':
335
+ case '@>':
336
+ case '&&':
337
+ // 配列演算子の場合
338
+ if (leftColumn.type !== rightColumn.type) {
339
+ throw new Error(`Type mismatch: array columns [${leftColumn.tableName}].[${leftColumn.columnName}] (${leftColumn.type}) and [${rightColumn.tableName}].[${rightColumn.columnName}] (${rightColumn.type}) must be the same type.`);
340
+ }
341
+ break;
342
+ default:
343
+ // サポートされていない演算子
344
+ throw new Error(`Operator '${operator}' is not supported for array column operations. Supported operators: =, @>, &&, ANY`);
345
+ }
346
+ switch (operator) {
347
+ case '=':
348
+ case '@>':
349
+ case '&&':
350
+ return {
351
+ expression: `${leftColumn.expression} ${operator} $${rightColumn.expression}`,
352
+ vars: []
353
+ };
354
+ case 'any':
355
+ return {
356
+ expression: `$${rightColumn.expression} = ANY(${leftColumn.expression})`,
357
+ vars: []
358
+ };
224
359
  }
225
- return {
226
- expression: `${leftColumn.expression} ${operator} $${varLength}`,
227
- vars: [right]
228
- };
229
360
  }
230
361
  /**
231
362
  * SQL statement to convert half-width characters to full-width
@@ -114,55 +114,6 @@ class ReqResType {
114
114
  return false;
115
115
  }
116
116
  }
117
- /**
118
- * 値がメールアドレス形式であるかどうかを検証します
119
- * Validates if the given value is in the format of an email address
120
- * @param value - 検証する値, The value to be validated
121
- * @returns {boolean} - 値がメールアドレス形式であるかどうか, Whether the value is in the format of an email address
122
- */
123
- isMail(value) {
124
- if (typeof value !== 'string') {
125
- return false;
126
- }
127
- const pattern = new RegExp('^[a-zA-Z0-9_%+-]+([.][a-zA-Z0-9_%+-]+)*@[a-zA-Z0-9]+([-.]?[a-zA-Z0-9]+)*\\.[a-zA-Z]{2,}$');
128
- return pattern.test(value);
129
- }
130
- /**
131
- * 値がHTTPS URLであるかどうかを検証します
132
- * Validates if the given value is an HTTPS URL
133
- * @param value - 検証する値, The value to be validated
134
- * @returns {boolean} - 値がHTTPS URLであるかどうか, Whether the value is an HTTPS URL
135
- */
136
- isHttps(value) {
137
- if (typeof value !== 'string') {
138
- return false;
139
- }
140
- const urlPattern = new RegExp('^(https?:\\/\\/[^\\s/$.?#].[^\\s]*)$');
141
- return urlPattern.test(value);
142
- }
143
- /**
144
- * 値がBase64形式であるかどうかを検証します
145
- * Validates if the given value is in Base64 format
146
- * @param value - 検証する値, The value to be validated
147
- * @returns {boolean} - 値がBase64形式であるかどうか, Whether the value is in Base64 format
148
- */
149
- isBase64(value) {
150
- if (typeof value !== 'string') {
151
- return false;
152
- }
153
- // base64は4倍の長さである必要がある
154
- if (value.length % 4 !== 0) {
155
- return false;
156
- }
157
- // 基本的なbase64パターン
158
- // 使用可能な文字
159
- // ・ アルファベット(A-Z, a-z)
160
- // ・ 数字(0-9)
161
- // ・ +と/(基本文字)
162
- // ・ =(パディング文字)
163
- const base64Pattern = /^[A-Za-z0-9+/]*={0,2}$/;
164
- return base64Pattern.test(value);
165
- }
166
117
  /**
167
118
  * プロパティの型をSwagger形式に変換します
168
119
  * Converts the property type to Swagger format
@@ -7,6 +7,7 @@ exports.RequestType = void 0;
7
7
  const ReqResType_1 = __importDefault(require("./ReqResType"));
8
8
  const Exception_1 = require("../exceptions/Exception");
9
9
  const StringUtil_1 = __importDefault(require("../Utils/StringUtil"));
10
+ const type_utils_n_daira_1 = require("type-utils-n-daira");
10
11
  class RequestType extends ReqResType_1.default {
11
12
  constructor() {
12
13
  super(...arguments);
@@ -532,7 +533,7 @@ class RequestType extends ReqResType_1.default {
532
533
  this.throwInputError(isRequestBody ? "UUID_21" : "UUID_91", keys, value);
533
534
  case 'mail':
534
535
  case 'mail?':
535
- if (this.isMail(value)) {
536
+ if (type_utils_n_daira_1.ValidateStringUtil.isMail(value)) {
536
537
  return value;
537
538
  }
538
539
  this.throwInputError(isRequestBody ? "MAIL_21" : "MAIL_91", keys, value);
@@ -565,13 +566,13 @@ class RequestType extends ReqResType_1.default {
565
566
  return value.replace('T', ' ');
566
567
  case 'https':
567
568
  case 'https?':
568
- if (this.isHttps(value)) {
569
+ if (type_utils_n_daira_1.ValidateStringUtil.isHttps(value)) {
569
570
  return value;
570
571
  }
571
572
  this.throwInputError(isRequestBody ? "HTTPS_21" : "HTTPS_91", keys, value);
572
573
  case 'base64':
573
574
  case 'base64?':
574
- if (this.isBase64(value)) {
575
+ if (type_utils_n_daira_1.ValidateStringUtil.isBase64(value)) {
575
576
  return value;
576
577
  }
577
578
  this.throwInputError(isRequestBody ? "BASE64_21" : "BASE64_91", keys, value);
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ResponseType = void 0;
7
+ const type_utils_n_daira_1 = require("type-utils-n-daira");
7
8
  const StringUtil_1 = __importDefault(require("../Utils/StringUtil"));
8
9
  const ReqResType_1 = __importDefault(require("./ReqResType"));
9
10
  class ResponseType extends ReqResType_1.default {
@@ -210,7 +211,7 @@ class ResponseType extends ReqResType_1.default {
210
211
  return undefined;
211
212
  case 'mail':
212
213
  case 'mail?':
213
- if (this.isMail(value)) {
214
+ if (type_utils_n_daira_1.ValidateStringUtil.isMail(value)) {
214
215
  return value;
215
216
  }
216
217
  return undefined;
@@ -252,13 +253,13 @@ class ResponseType extends ReqResType_1.default {
252
253
  return undefined;
253
254
  case 'https':
254
255
  case 'https?':
255
- if (this.isHttps(value)) {
256
+ if (type_utils_n_daira_1.ValidateStringUtil.isHttps(value)) {
256
257
  return value;
257
258
  }
258
259
  return undefined;
259
260
  case 'base64':
260
261
  case 'base64?':
261
- if (this.isBase64(value)) {
262
+ if (type_utils_n_daira_1.ValidateStringUtil.isBase64(value)) {
262
263
  return value;
263
264
  }
264
265
  return undefined;
package/index.d.ts CHANGED
@@ -20,7 +20,6 @@ import ValidateClient from './src/models/ValidateClient';
20
20
  import { TableModel } from "./src/models/TableModel";
21
21
  export { TableModel } from "./src/models/TableModel";
22
22
 
23
- import { TColumnAttribute, TColumnType, TColumnArrayType, TColumn, TColumnDetail, TOperator, TColumnInfo, TQuery, TSelectExpression, TAggregateFuncType, TCondition, TNestedCondition, TSortKeyword, TKeyFormat } from './src/models/Type';
24
23
  export { TColumnAttribute, TColumnType, TColumnArrayType, TColumn, TColumnDetail, TOperator, TColumnInfo, TQuery, TSelectExpression, TAggregateFuncType, TCondition, TNestedCondition, TSortKeyword, TKeyFormat } from './src/models/Type';
25
24
 
26
25
  declare module 'pg-mvc-service' {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pg-mvc-service",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "description": "",
5
5
  "homepage": "https://github.com/n-daira/npm-pack_mvc-service#readme",
6
6
  "bugs": {
@@ -31,6 +31,7 @@
31
31
  "axios": "1.10.0",
32
32
  "crypto": "1.0.1",
33
33
  "pdf-lib": "1.17.1",
34
- "sharp": "0.34.1"
34
+ "sharp": "0.34.1",
35
+ "type-utils-n-daira": "1.0.11"
35
36
  }
36
37
  }
@@ -1,5 +1,6 @@
1
1
  import { PDFDocument } from 'pdf-lib';
2
2
  import sharp from 'sharp';
3
+ import { ValidateStringUtil } from 'type-utils-n-daira';
3
4
 
4
5
  export type TPng = 'image/png';
5
6
  export type TJpeg = 'image/jpeg';
@@ -10,6 +11,9 @@ export type TPdf = 'application/pdf';
10
11
  export type TJson = 'application/json';
11
12
 
12
13
  export class Base64Client {
14
+ public static readonly PREFIX_JPEG_DATA = '/9j/';
15
+ public static readonly PREFIX_PNG_DATA = 'iVBORw0KGgo';
16
+
13
17
  constructor() { }
14
18
 
15
19
  // public encode(text: string): string {
@@ -152,4 +156,68 @@ export class Base64Client {
152
156
  const pdfBytes = await pdfDoc.save();
153
157
  return Buffer.from(pdfBytes);
154
158
  }
159
+
160
+ public static isJpeg(value: any): value is string {
161
+ if (ValidateStringUtil.isBase64(value) === false) {
162
+ return false
163
+ }
164
+
165
+ if (value.startsWith('data:')) {
166
+ if (value.startsWith('data:image/jpeg,') === false && value.startsWith('data:image/jpg,') === false) {
167
+ return false;
168
+ }
169
+
170
+ const valueParts = value.split(',');
171
+ if (valueParts.length !== 2) {
172
+ return false;
173
+ }
174
+
175
+ return valueParts[1].startsWith(this.PREFIX_JPEG_DATA);
176
+ }
177
+
178
+ return value.startsWith(this.PREFIX_JPEG_DATA);
179
+ }
180
+
181
+ public static isPng(value: any): value is string {
182
+ if (ValidateStringUtil.isBase64(value) === false) {
183
+ return false
184
+ }
185
+
186
+ if (value.startsWith('data:')) {
187
+ if (value.startsWith('data:image/png,') === false) {
188
+ return false;
189
+ }
190
+
191
+ const valueParts = value.split(',');
192
+ if (valueParts.length !== 2) {
193
+ return false;
194
+ }
195
+
196
+ return valueParts[1].startsWith(this.PREFIX_PNG_DATA);
197
+ }
198
+
199
+ return value.startsWith(this.PREFIX_PNG_DATA);
200
+ }
201
+
202
+ public static async tryConvertToPng(base64Value: any): Promise<string | false> {
203
+ if (ValidateStringUtil.isBase64(base64Value) === false) {
204
+ return false;
205
+ }
206
+
207
+ const base64Data = base64Value.startsWith('data:') ? base64Value.split(',')[1] : base64Value;
208
+ if (this.isPng(base64Data)) {
209
+ return base64Data;
210
+ } else if (this.isJpeg(base64Data)) {
211
+ const buffer = Buffer.from(base64Data, 'base64');
212
+ try {
213
+ const pngBuffer = await sharp(buffer)
214
+ .ensureAlpha().png().toBuffer();
215
+ return pngBuffer.toString('base64');
216
+ } catch (e) {
217
+ return false;
218
+ }
219
+ }
220
+
221
+ return false;
222
+ }
155
223
  }
@@ -216,6 +216,58 @@ export default class ValidateValueUtil {
216
216
  return true;
217
217
  }
218
218
 
219
+ static isErrorInteger(value: any): boolean {
220
+ let numberValue: number;
221
+ if (typeof value === 'string') {
222
+ if (value.trim() === "" || isNaN(Number(value))) {
223
+ return true;
224
+ }
225
+
226
+ numberValue = Number(value);
227
+ } else if (typeof value === 'number') {
228
+ numberValue = value;
229
+ } else {
230
+ return true;
231
+ }
232
+
233
+ if (Number.isInteger(numberValue) === false) {
234
+ return true;
235
+ }
236
+
237
+ if (numberValue < -2147483648 || numberValue > 2147483647) {
238
+ return true;
239
+ }
240
+
241
+ return false;
242
+ }
243
+
244
+ static isErrorReal(value: any): boolean {
245
+ let numberValue: number;
246
+ if (typeof value === 'string') {
247
+ if (value.trim() === "" || isNaN(Number(value))) {
248
+ return true;
249
+ }
250
+
251
+ numberValue = Number(value);
252
+ } else if (typeof value === 'number') {
253
+ numberValue = value;
254
+ } else {
255
+ return true;
256
+ }
257
+
258
+ // 特殊値チェック
259
+ if (isFinite(numberValue) === false) {
260
+ return true;
261
+ }
262
+
263
+ // 範囲チェック(real型の範囲)
264
+ if (numberValue < -3.4e38 || numberValue > 3.4e38) {
265
+ return true;
266
+ }
267
+
268
+ return false;
269
+ }
270
+
219
271
  static isErrorNumber(value: any): boolean {
220
272
  if (typeof value === 'string') {
221
273
  if (value.trim() === "" || isNaN(Number(value))) {
@@ -96,23 +96,23 @@ export default class WhereExpression {
96
96
  const leftColumn = left.model.getColumn(left.name);
97
97
 
98
98
  // Are the operators correct?
99
- const useableOperator: { [key in TColumnType | TColumnArrayType]: string[] } = {
99
+ const useableOperator: { [key in TColumnType | TColumnArrayType]: TOperator[] } = {
100
100
  integer: ["=", "!=", ">", ">=", "<", "<=", "in", "not in"],
101
- 'integer[]': [],
101
+ 'integer[]': ["=", "any", "@>", "&&"],
102
102
  real: ["=", "!=", ">", ">=", "<", "<="],
103
- 'real[]': [],
103
+ 'real[]': ["=", "any", "@>", "&&"],
104
104
  string: ["=", "!=", "like", "ilike", "h2f_like", "h2f_ilike", "in", "not in"],
105
- 'string[]': [],
105
+ 'string[]': ["=", "any", "@>", "&&"],
106
106
  uuid: ["=", "!=", "in", "not in"],
107
- 'uuid[]': [],
107
+ 'uuid[]': ["=", "any", "@>", "&&"],
108
108
  bool: ["=", "!=", "in", "not in"],
109
- 'bool[]': [],
109
+ 'bool[]': ["=", "any", "@>", "&&"],
110
110
  date: ["=", "!=", ">", ">=", "<", "<="],
111
- 'date[]': [],
111
+ 'date[]': ["=", "any", "@>", "&&"],
112
112
  time: ["=", "!=", ">", ">=", "<", "<="],
113
- 'time[]': [],
113
+ 'time[]': ["=", "any", "@>", "&&"],
114
114
  timestamp: ["=", "!=", ">", ">=", "<", "<="],
115
- 'timestamp[]': [],
115
+ 'timestamp[]': ["=", "any", "@>", "&&"],
116
116
  json: [],
117
117
  'json[]': [],
118
118
  jsonb: [],
@@ -141,8 +141,14 @@ export default class WhereExpression {
141
141
  }
142
142
  }
143
143
 
144
- if (leftColumn.type.endsWith("[]")) {
145
- return this.createExpression(leftColumn, operator, right, varLength);
144
+ const isColumnRight = right !== null && typeof right === 'object' && 'model' in right && 'name' in right;
145
+ const isArrayColumnLeft = leftColumn.type.endsWith("[]");
146
+ if (isArrayColumnLeft) {
147
+ if (isColumnRight) {
148
+ return this.createExpressionArrayColumn(leftColumn, operator, right, varLength);
149
+ } else {
150
+ return this.createExpressionArrayValue(leftColumn, operator, right, varLength);
151
+ }
146
152
  } else {
147
153
  return this.createExpression(leftColumn, operator, right, varLength);
148
154
  }
@@ -176,8 +182,9 @@ export default class WhereExpression {
176
182
  if (right !== null && typeof right === 'object' && 'model' in right && 'name' in right) {
177
183
  const rightColumn = right.model.getColumn(right.name);
178
184
 
185
+ // 型の不一致エラーメッセージを改善
179
186
  if (leftColumn.type !== rightColumn.type) {
180
- throw new Error(`The types of [${leftColumn.tableName}].[${leftColumn.columnName}] and [${rightColumn.tableName}].[${rightColumn.columnName}] are different.`);
187
+ throw new Error(`Type mismatch: column [${leftColumn.tableName}].[${leftColumn.columnName}] (${leftColumn.type}) and [${rightColumn.tableName}].[${rightColumn.columnName}] (${rightColumn.type}) must be the same type.`);
181
188
  }
182
189
 
183
190
  // LIKE operators are different, so handle separately
@@ -224,37 +231,162 @@ export default class WhereExpression {
224
231
 
225
232
  private static createExpressionArrayValue(leftColumn: TColumnDetail, operator: TOperator, right: any, varLength: number) : TQuery {
226
233
 
227
- // ValidateValueUtil.validateValue(leftColumn, right);
228
- // LIKE operators are different, so handle separately
229
- // switch (operator) {
230
- // case 'like':
231
- // case 'ilike':
232
- // return {
233
- // sql: `${leftColumn.expression} ${operator} $${varLength}`,
234
- // vars: [`%${right}%`]
235
- // }
236
- // case 'h2f_like': // half to full like
237
- // case 'h2f_ilike': // half to full ilike
238
- // return {
239
- // sql: `${this.makeSqlReplaceHalfToFull(leftColumn.expression)} ${operator.replace("h2f_", "")} ${this.makeSqlReplaceHalfToFull(`$${varLength}`)}`,
240
- // vars: [`%${right}%`]
241
- // }
242
- // }
234
+ // バリデーションチェック
235
+ switch (operator) {
236
+ case 'any':
237
+ switch (leftColumn.type) {
238
+ case 'integer[]':
239
+ if (ValidateValueUtil.isErrorInteger(right)) {
240
+ throw new Error(`Expected integer value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
241
+ }
242
+ break;
243
+ case 'real[]':
244
+ if (ValidateValueUtil.isErrorReal(right)) {
245
+ throw new Error(`Expected numeric value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
246
+ }
247
+ break;
248
+ case 'string[]':
249
+ if (ValidateValueUtil.isErrorString(right)) {
250
+ throw new Error(`Expected string value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
251
+ }
252
+ break;
253
+ case 'uuid[]':
254
+ if (ValidateValueUtil.isErrorUUID(right)) {
255
+ throw new Error(`Expected UUID value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
256
+ }
257
+ break;
258
+ case 'bool[]':
259
+ if (ValidateValueUtil.isErrorBool(right)) {
260
+ throw new Error(`Expected boolean value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
261
+ }
262
+ break;
263
+ case 'date[]':
264
+ if (ValidateValueUtil.isErrorDate(right)) {
265
+ throw new Error(`Expected date value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
266
+ }
267
+ break;
268
+ case 'time[]':
269
+ if (ValidateValueUtil.isErrorTime(right)) {
270
+ throw new Error(`Expected time value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
271
+ }
272
+ break;
273
+ case 'timestamp[]':
274
+ if (ValidateValueUtil.isErrorTimestamp(right)) {
275
+ throw new Error(`Expected timestamp value for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
276
+ }
277
+ break;
278
+ }
279
+ break;
280
+ case '=':
281
+ case '@>':
282
+ case '&&':
283
+ if (Array.isArray(right) === false) {
284
+ throw new Error(`Expected array format for array column (${leftColumn.type}), but received: ${JSON.stringify(right)}`);
285
+ }
286
+
287
+ for (const value of right) {
288
+ switch (leftColumn.type) {
289
+ case 'integer[]':
290
+ if (ValidateValueUtil.isErrorInteger(value)) {
291
+ throw new Error(`Expected integer value in array element, but received: ${JSON.stringify(value)}`);
292
+ }
293
+ break;
294
+ case 'real[]':
295
+ if (ValidateValueUtil.isErrorReal(value)) {
296
+ throw new Error(`Expected numeric value in array element, but received: ${JSON.stringify(value)}`);
297
+ }
298
+ break;
299
+ case 'string[]':
300
+ if (ValidateValueUtil.isErrorString(value)) {
301
+ throw new Error(`Expected string value in array element, but received: ${JSON.stringify(value)}`);
302
+ }
303
+ break;
304
+ case 'uuid[]':
305
+ if (ValidateValueUtil.isErrorUUID(value)) {
306
+ throw new Error(`Expected UUID value in array element, but received: ${JSON.stringify(value)}`);
307
+ }
308
+ break;
309
+ case 'bool[]':
310
+ if (ValidateValueUtil.isErrorBool(value)) {
311
+ throw new Error(`Expected boolean value in array element, but received: ${JSON.stringify(value)}`);
312
+ }
313
+ break;
314
+ case 'date[]':
315
+ if (ValidateValueUtil.isErrorDate(value)) {
316
+ throw new Error(`Expected date value in array element, but received: ${JSON.stringify(value)}`);
317
+ }
318
+ break;
319
+ case 'time[]':
320
+ if (ValidateValueUtil.isErrorTime(value)) {
321
+ throw new Error(`Expected time value in array element, but received: ${JSON.stringify(value)}`);
322
+ }
323
+ break;
324
+ case 'timestamp[]':
325
+ if (ValidateValueUtil.isErrorTimestamp(value)) {
326
+ throw new Error(`Expected timestamp value in array element, but received: ${JSON.stringify(value)}`);
327
+ }
328
+ break;
329
+ }
330
+ }
331
+ break;
332
+ default:
333
+ throw new Error(`Unsupported operator '${operator}' for array column operations.`);
334
+ }
243
335
 
244
336
  switch (operator) {
245
337
  case '=':
246
- // バリデーションチェックをする
247
-
338
+ case '@>':
339
+ case '&&':
248
340
  return {
249
341
  expression: `${leftColumn.expression} ${operator} $${varLength}`,
250
342
  vars: [right]
251
343
  }
344
+ case 'any':
345
+ return {
346
+ expression: `$${varLength} = ANY(${leftColumn.expression})`,
347
+ vars: [right]
348
+ }
252
349
  }
350
+ }
253
351
 
352
+ private static createExpressionArrayColumn(leftColumn: TColumnDetail, operator: TOperator, right: TColumnInfo, varLength: number) : TQuery {
254
353
 
255
- return {
256
- expression: `${leftColumn.expression} ${operator} $${varLength}`,
257
- vars: [right]
354
+ const rightColumn = right.model.getColumn(right.name);
355
+
356
+ // バリデーションチェック
357
+ switch (operator) {
358
+ case 'any':
359
+ // any演算子の場合
360
+ if (leftColumn.type !== rightColumn.type.replace('[]', '')) {
361
+ throw new Error(`Type mismatch: array column [${leftColumn.tableName}].[${leftColumn.columnName}] (${leftColumn.type}) and scalar column [${rightColumn.tableName}].[${rightColumn.columnName}] (${rightColumn.type}) are incompatible for ANY operation.`);
362
+ }
363
+ break;
364
+ case '=':
365
+ case '@>':
366
+ case '&&':
367
+ // 配列演算子の場合
368
+ if (leftColumn.type !== rightColumn.type) {
369
+ throw new Error(`Type mismatch: array columns [${leftColumn.tableName}].[${leftColumn.columnName}] (${leftColumn.type}) and [${rightColumn.tableName}].[${rightColumn.columnName}] (${rightColumn.type}) must be the same type.`);
370
+ }
371
+ break;
372
+ default:
373
+ // サポートされていない演算子
374
+ throw new Error(`Operator '${operator}' is not supported for array column operations. Supported operators: =, @>, &&, ANY`);
375
+ }
376
+
377
+ switch (operator) {
378
+ case '=':
379
+ case '@>':
380
+ case '&&':
381
+ return {
382
+ expression: `${leftColumn.expression} ${operator} $${rightColumn.expression}`,
383
+ vars: []
384
+ }
385
+ case 'any':
386
+ return {
387
+ expression: `$${rightColumn.expression} = ANY(${leftColumn.expression})`,
388
+ vars: []
389
+ }
258
390
  }
259
391
  }
260
392
 
@@ -47,7 +47,7 @@ export type TColumnDetail = TColumn & {
47
47
  expression: string
48
48
  }
49
49
 
50
- export type TOperator = "=" | "!=" | ">" | ">=" | "<" | "<=" | "like" | "ilike" | "h2f_like" | "h2f_ilike" | "in" | "not in";
50
+ export type TOperator = "=" | "!=" | ">" | ">=" | "<" | "<=" | "like" | "ilike" | "h2f_like" | "h2f_ilike" | "in" | "not in" | "any" | "@>" | "&&";
51
51
  export type TColumnInfo = { model: TableModel, name: string }
52
52
  export type TQuery = {expression: string, vars?: Array<any>};
53
53
  export type TSelectExpression = { expression: string, alias: string }
@@ -151,62 +151,6 @@ export default class ReqResType {
151
151
  }
152
152
  }
153
153
 
154
- /**
155
- * 値がメールアドレス形式であるかどうかを検証します
156
- * Validates if the given value is in the format of an email address
157
- * @param value - 検証する値, The value to be validated
158
- * @returns {boolean} - 値がメールアドレス形式であるかどうか, Whether the value is in the format of an email address
159
- */
160
- protected isMail(value: any) {
161
- if (typeof value !== 'string') {
162
- return false;
163
- }
164
-
165
- const pattern = new RegExp('^[a-zA-Z0-9_%+-]+([.][a-zA-Z0-9_%+-]+)*@[a-zA-Z0-9]+([-.]?[a-zA-Z0-9]+)*\\.[a-zA-Z]{2,}$');
166
- return pattern.test(value);
167
- }
168
-
169
- /**
170
- * 値がHTTPS URLであるかどうかを検証します
171
- * Validates if the given value is an HTTPS URL
172
- * @param value - 検証する値, The value to be validated
173
- * @returns {boolean} - 値がHTTPS URLであるかどうか, Whether the value is an HTTPS URL
174
- */
175
- protected isHttps(value: any) {
176
- if (typeof value !== 'string') {
177
- return false;
178
- }
179
-
180
- const urlPattern = new RegExp('^(https?:\\/\\/[^\\s/$.?#].[^\\s]*)$');
181
- return urlPattern.test(value);
182
- }
183
-
184
- /**
185
- * 値がBase64形式であるかどうかを検証します
186
- * Validates if the given value is in Base64 format
187
- * @param value - 検証する値, The value to be validated
188
- * @returns {boolean} - 値がBase64形式であるかどうか, Whether the value is in Base64 format
189
- */
190
- protected isBase64(value: any) {
191
- if (typeof value !== 'string') {
192
- return false;
193
- }
194
-
195
- // base64は4倍の長さである必要がある
196
- if (value.length % 4 !== 0) {
197
- return false;
198
- }
199
-
200
- // 基本的なbase64パターン
201
- // 使用可能な文字
202
- // ・ アルファベット(A-Z, a-z)
203
- // ・ 数字(0-9)
204
- // ・ +と/(基本文字)
205
- // ・ =(パディング文字)
206
- const base64Pattern = /^[A-Za-z0-9+/]*={0,2}$/;
207
- return base64Pattern.test(value);
208
- }
209
-
210
154
  /**
211
155
  * プロパティの型をSwagger形式に変換します
212
156
  * Converts the property type to Swagger format
@@ -2,6 +2,7 @@ import { Request } from 'express';
2
2
  import ReqResType, { EnumType, PrimitiveType, PropertyType } from "./ReqResType";
3
3
  import { InputErrorException } from '../exceptions/Exception';
4
4
  import StringUtil from '../Utils/StringUtil';
5
+ import { ValidateStringUtil } from 'type-utils-n-daira';
5
6
 
6
7
  // エラーメッセージの型定義
7
8
  export interface ErrorMessageType {
@@ -575,7 +576,7 @@ export class RequestType extends ReqResType {
575
576
  this.throwInputError(isRequestBody ? "UUID_21" : "UUID_91", keys, value);
576
577
  case 'mail':
577
578
  case 'mail?':
578
- if (this.isMail(value)) {
579
+ if (ValidateStringUtil.isMail(value)) {
579
580
  return value;
580
581
  }
581
582
  this.throwInputError(isRequestBody ? "MAIL_21" : "MAIL_91", keys, value);
@@ -611,13 +612,13 @@ export class RequestType extends ReqResType {
611
612
  return value.replace('T', ' ');
612
613
  case 'https':
613
614
  case 'https?':
614
- if (this.isHttps(value)) {
615
+ if (ValidateStringUtil.isHttps(value)) {
615
616
  return value;
616
617
  }
617
618
  this.throwInputError(isRequestBody ? "HTTPS_21" : "HTTPS_91", keys, value);
618
619
  case 'base64':
619
620
  case 'base64?':
620
- if (this.isBase64(value)) {
621
+ if (ValidateStringUtil.isBase64(value)) {
621
622
  return value;
622
623
  }
623
624
  this.throwInputError(isRequestBody ? "BASE64_21" : "BASE64_91", keys, value);
@@ -1,3 +1,4 @@
1
+ import { ValidateStringUtil } from "type-utils-n-daira";
1
2
  import StringUtil from "../Utils/StringUtil";
2
3
  import ReqResType from "./ReqResType";
3
4
 
@@ -226,7 +227,7 @@ export class ResponseType extends ReqResType {
226
227
  return undefined;
227
228
  case 'mail':
228
229
  case 'mail?':
229
- if (this.isMail(value)) {
230
+ if (ValidateStringUtil.isMail(value)) {
230
231
  return value;
231
232
  }
232
233
  return undefined;
@@ -274,13 +275,13 @@ export class ResponseType extends ReqResType {
274
275
  return undefined;
275
276
  case 'https':
276
277
  case 'https?':
277
- if (this.isHttps(value)) {
278
+ if (ValidateStringUtil.isHttps(value)) {
278
279
  return value;
279
280
  }
280
281
  return undefined;
281
282
  case 'base64':
282
283
  case 'base64?':
283
- if (this.isBase64(value)) {
284
+ if (ValidateStringUtil.isBase64(value)) {
284
285
  return value;
285
286
  }
286
287
  return undefined;