reltype 0.1.5 → 0.1.7

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 (36) hide show
  1. package/CHANGELOG.md +37 -3
  2. package/README.ko.md +517 -623
  3. package/README.md +511 -623
  4. package/dist/features/query/builder.d.ts +13 -3
  5. package/dist/features/query/builder.d.ts.map +1 -1
  6. package/dist/features/query/builder.js +39 -13
  7. package/dist/features/query/bulkInsert.d.ts +1 -0
  8. package/dist/features/query/bulkInsert.d.ts.map +1 -1
  9. package/dist/features/query/bulkInsert.js +3 -1
  10. package/dist/features/query/insert.d.ts +1 -0
  11. package/dist/features/query/insert.d.ts.map +1 -1
  12. package/dist/features/query/insert.js +3 -1
  13. package/dist/features/query/select.d.ts +1 -1
  14. package/dist/features/query/select.d.ts.map +1 -1
  15. package/dist/features/query/select.js +7 -2
  16. package/dist/features/query/update.d.ts +1 -0
  17. package/dist/features/query/update.d.ts.map +1 -1
  18. package/dist/features/query/update.js +3 -1
  19. package/dist/features/query/upsert.d.ts +1 -0
  20. package/dist/features/query/upsert.d.ts.map +1 -1
  21. package/dist/features/query/upsert.js +12 -6
  22. package/dist/features/query/where.d.ts +1 -0
  23. package/dist/features/query/where.d.ts.map +1 -1
  24. package/dist/features/query/where.js +3 -1
  25. package/dist/features/schema/table.d.ts.map +1 -1
  26. package/dist/features/schema/table.js +4 -2
  27. package/dist/index.d.ts +1 -0
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +8 -1
  30. package/dist/utils/index.d.ts +1 -0
  31. package/dist/utils/index.d.ts.map +1 -1
  32. package/dist/utils/index.js +1 -0
  33. package/dist/utils/sqlGuard.d.ts +42 -0
  34. package/dist/utils/sqlGuard.d.ts.map +1 -0
  35. package/dist/utils/sqlGuard.js +94 -0
  36. package/package.json +1 -1
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.quoteIdentifier = quoteIdentifier;
4
+ exports.escapeSchemaIdentifier = escapeSchemaIdentifier;
5
+ exports.validateOrderDir = validateOrderDir;
6
+ exports.validateAggregateFn = validateAggregateFn;
7
+ exports.validateJoinType = validateJoinType;
8
+ const logger_1 = require("./logger");
9
+ const logger = logger_1.Logger.fromEnv(process.env, { prefix: '[SqlGuard]' });
10
+ /**
11
+ * 유효한 SQL 식별자 패턴.
12
+ * - 단순 식별자: `[a-zA-Z_][a-zA-Z0-9_]*` 예: `first_name`, `userId`
13
+ * - 점 표기법: `schema.table`, `table.column`
14
+ *
15
+ * 공백, 세미콜론, 따옴표, 괄호 등 SQL 특수문자를 포함하는 경우 거부합니다.
16
+ */
17
+ const IDENTIFIER_RE = /^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)*$/;
18
+ const VALID_ORDER_DIRS = new Set(['ASC', 'DESC']);
19
+ const VALID_AGG_FNS = new Set(['COUNT', 'SUM', 'AVG', 'MIN', 'MAX']);
20
+ const VALID_JOIN_TYPES = new Set(['INNER', 'LEFT', 'RIGHT', 'FULL']);
21
+ /**
22
+ * SQL 식별자(컬럼명, 테이블명 등)를 검증하고 PostgreSQL 표준으로 이스케이프합니다.
23
+ *
24
+ * - 유효한 식별자 패턴 `[a-zA-Z_][a-zA-Z0-9_.]*` 에 맞지 않으면
25
+ * `logger.error` 로 기록하고 `""` (빈 식별자) 를 반환합니다.
26
+ * - `"` 문자는 `""` 로 이중 이스케이프합니다 (PostgreSQL 표준).
27
+ * - `schema.table` / `table.column` 도트 표기법을 지원합니다.
28
+ *
29
+ * @example
30
+ * quoteIdentifier('first_name') // → '"first_name"'
31
+ * quoteIdentifier('auth.users') // → '"auth"."users"'
32
+ * quoteIdentifier('1; DROP TABLE')// → '""' + logger.error
33
+ */
34
+ function quoteIdentifier(raw) {
35
+ if (!IDENTIFIER_RE.test(raw)) {
36
+ logger.error(`유효하지 않은 SQL 식별자 "${raw}". ` +
37
+ `허용 패턴: [a-zA-Z_][a-zA-Z0-9_]*(.[a-zA-Z_][a-zA-Z0-9_]*)*. ` +
38
+ `빈 식별자("")로 대체합니다.`);
39
+ return '""';
40
+ }
41
+ return raw
42
+ .split('.')
43
+ .map((part) => `"${part.replace(/"/g, '""')}"`)
44
+ .join('.');
45
+ }
46
+ /**
47
+ * 개발자가 정의하는 테이블/스키마 식별자를 이스케이프합니다.
48
+ * 패턴 검증 없이 `"` → `""` 이중 이스케이프만 수행합니다.
49
+ *
50
+ * `defineTable` 내부에서만 사용합니다 (개발자가 제어하는 정적 값).
51
+ *
52
+ * @example
53
+ * escapeSchemaIdentifier('auth') // → '"auth"'
54
+ * escapeSchemaIdentifier('my"table') // → '"my""table"'
55
+ */
56
+ function escapeSchemaIdentifier(name) {
57
+ return `"${name.replace(/"/g, '""')}"`;
58
+ }
59
+ /**
60
+ * ORDER BY 방향(ASC / DESC)을 검증합니다.
61
+ * 유효하지 않으면 `logger.error` 후 `'ASC'` 를 반환합니다.
62
+ */
63
+ function validateOrderDir(dir) {
64
+ const upper = dir.toUpperCase();
65
+ if (!VALID_ORDER_DIRS.has(upper)) {
66
+ logger.error(`유효하지 않은 ORDER BY 방향 "${dir}". 허용 값: ASC, DESC. 'ASC'로 대체합니다.`);
67
+ return 'ASC';
68
+ }
69
+ return upper;
70
+ }
71
+ /**
72
+ * 집계 함수명(COUNT / SUM / AVG / MIN / MAX)을 검증합니다.
73
+ * 유효하지 않으면 `logger.error` 후 `'COUNT'` 를 반환합니다.
74
+ */
75
+ function validateAggregateFn(fn) {
76
+ const upper = fn.toUpperCase();
77
+ if (!VALID_AGG_FNS.has(upper)) {
78
+ logger.error(`유효하지 않은 집계 함수 "${fn}". 허용 값: COUNT, SUM, AVG, MIN, MAX. 'COUNT'로 대체합니다.`);
79
+ return 'COUNT';
80
+ }
81
+ return upper;
82
+ }
83
+ /**
84
+ * JOIN 타입(INNER / LEFT / RIGHT / FULL)을 검증합니다.
85
+ * 유효하지 않으면 `logger.error` 후 `'INNER'` 를 반환합니다.
86
+ */
87
+ function validateJoinType(type) {
88
+ const upper = type.toUpperCase();
89
+ if (!VALID_JOIN_TYPES.has(upper)) {
90
+ logger.error(`유효하지 않은 JOIN 타입 "${type}". 허용 값: INNER, LEFT, RIGHT, FULL. 'INNER'로 대체합니다.`);
91
+ return 'INNER';
92
+ }
93
+ return upper;
94
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reltype",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Type-first relational modeling for PostgreSQL in TypeScript. Fluent query builder with automatic camelCase ↔ snake_case conversion, CRUD, streaming, cursor pagination, and hooks.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",