@atscript/db-sqlite 0.1.64 → 0.1.66
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/dist/index.cjs +74 -16
- package/dist/index.mjs +74 -16
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -64,24 +64,82 @@ function esc(name) {
|
|
|
64
64
|
return name.replace(/"/g, "\"\"");
|
|
65
65
|
}
|
|
66
66
|
/**
|
|
67
|
-
*
|
|
68
|
-
* -
|
|
69
|
-
*
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
* Regex characters whose backslash form means "literal char". The walker emits
|
|
68
|
+
* the unescaped char (with re-escaping for SQL LIKE wildcards). Anything outside
|
|
69
|
+
* this set — `\d`, `\w`, `\s`, `\b`, etc. — is rejected as an unsupported feature.
|
|
70
|
+
*/
|
|
71
|
+
const REGEX_LITERAL_ESCAPES = /* @__PURE__ */ new Set(".^$()[]{}|/+*?-\\");
|
|
72
|
+
/**
|
|
73
|
+
* Unescaped regex metacharacters with no LIKE equivalent. Subset of
|
|
74
|
+
* {@link REGEX_LITERAL_ESCAPES}: the same chars are rejected unescaped here
|
|
75
|
+
* but accepted as literals when preceded by `\`.
|
|
76
|
+
*/
|
|
77
|
+
const REGEX_UNSUPPORTED = /* @__PURE__ */ new Set("()[]{}|*+?");
|
|
78
|
+
/**
|
|
79
|
+
* Re-escape a literal char for SQL LIKE under `ESCAPE '\'`. `%`, `_`, and `\`
|
|
80
|
+
* are LIKE metachars and need a backslash prefix; everything else passes through.
|
|
81
|
+
*/
|
|
82
|
+
function likeEscape(ch) {
|
|
83
|
+
return ch === "%" || ch === "_" || ch === "\\" ? `\\${ch}` : ch;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Translates a regex pattern into a SQLite LIKE pattern. The dialect emits the
|
|
87
|
+
* resulting SQL with `ESCAPE '\'`, so `\%` and `\_` in the output denote literal
|
|
88
|
+
* `%` / `_`, and `\\` denotes a literal backslash.
|
|
89
|
+
*
|
|
90
|
+
* Supported subset:
|
|
91
|
+
* - anchors `^` and `$` (must appear at the very start / end of the pattern)
|
|
92
|
+
* - `.` (any single char) and `.*` (any run of chars)
|
|
93
|
+
* - escaped literals: `\.`, `\^`, `\$`, `\(`, `\)`, `\[`, `\]`, `\{`, `\}`,
|
|
94
|
+
* `\|`, `\/`, `\+`, `\*`, `\?`, `\-`, `\\`
|
|
95
|
+
*
|
|
96
|
+
* Throws on character classes, alternation, groups, quantifiers other than `.*`,
|
|
97
|
+
* and shorthand classes (`\d`, `\w`, `\s`, `\b`, …) — these would silently match
|
|
98
|
+
* the wrong rows under a naive translation, and a Node-side fallback would break
|
|
99
|
+
* pagination, ordering, and aggregation pushdown.
|
|
100
|
+
*
|
|
101
|
+
* `^` and `$` outside the start/end of the pattern are treated as literal chars
|
|
102
|
+
* (multiline anchors aren't supported).
|
|
72
103
|
*/
|
|
73
104
|
function regexToLike(pattern) {
|
|
74
105
|
const hasStart = pattern.startsWith("^");
|
|
75
|
-
const hasEnd = pattern
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
106
|
+
const hasEnd = endsWithUnescapedDollar(pattern);
|
|
107
|
+
const start = hasStart ? 1 : 0;
|
|
108
|
+
const end = hasEnd ? pattern.length - 1 : pattern.length;
|
|
109
|
+
let out = "";
|
|
110
|
+
for (let i = start; i < end; i++) {
|
|
111
|
+
const c = pattern[i];
|
|
112
|
+
if (c === "\\") {
|
|
113
|
+
const next = pattern[i + 1];
|
|
114
|
+
if (next === void 0) throw new Error(`Trailing backslash in regex pattern: ${pattern}`);
|
|
115
|
+
if (!REGEX_LITERAL_ESCAPES.has(next)) throw new Error(`Unsupported regex escape '\\${next}' in pattern '${pattern}' — only literal-meaning escapes are supported by the SQLite LIKE translation`);
|
|
116
|
+
out += likeEscape(next);
|
|
117
|
+
i++;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (c === ".") {
|
|
121
|
+
if (pattern[i + 1] === "*") {
|
|
122
|
+
out += "%";
|
|
123
|
+
i++;
|
|
124
|
+
} else out += "_";
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (REGEX_UNSUPPORTED.has(c)) throw new Error(`Unsupported regex feature '${c}' in pattern '${pattern}' — only anchors, '.', '.*', and escaped literals are supported by the SQLite LIKE translation`);
|
|
128
|
+
out += likeEscape(c);
|
|
129
|
+
}
|
|
130
|
+
if (hasStart && hasEnd) return out;
|
|
131
|
+
if (hasStart) return `${out}%`;
|
|
132
|
+
if (hasEnd) return `%${out}`;
|
|
133
|
+
return `%${out}%`;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* `$` at the end of the pattern is the end-anchor only if it is not preceded by
|
|
137
|
+
* an odd number of backslashes (i.e. not escaped). `\$` and `\\\$` are literal,
|
|
138
|
+
* `$` and `\\$` are anchors.
|
|
139
|
+
*/
|
|
140
|
+
function endsWithUnescapedDollar(s) {
|
|
141
|
+
const m = s.match(/(\\*)\$$/);
|
|
142
|
+
return m !== null && m[1].length % 2 === 0;
|
|
85
143
|
}
|
|
86
144
|
const sqliteDialect = {
|
|
87
145
|
quoteIdentifier(name) {
|
|
@@ -100,7 +158,7 @@ const sqliteDialect = {
|
|
|
100
158
|
const { pattern, flags } = (0, _atscript_db_sql_tools.parseRegexString)(value);
|
|
101
159
|
const likePattern = regexToLike(pattern);
|
|
102
160
|
return {
|
|
103
|
-
sql: flags.includes("i") ?
|
|
161
|
+
sql: `${quotedCol} LIKE ? ESCAPE '\\'${flags.includes("i") ? " COLLATE NOCASE" : ""}`,
|
|
104
162
|
params: [likePattern]
|
|
105
163
|
};
|
|
106
164
|
},
|
package/dist/index.mjs
CHANGED
|
@@ -63,24 +63,82 @@ function esc(name) {
|
|
|
63
63
|
return name.replace(/"/g, "\"\"");
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
66
|
-
*
|
|
67
|
-
* -
|
|
68
|
-
*
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
* Regex characters whose backslash form means "literal char". The walker emits
|
|
67
|
+
* the unescaped char (with re-escaping for SQL LIKE wildcards). Anything outside
|
|
68
|
+
* this set — `\d`, `\w`, `\s`, `\b`, etc. — is rejected as an unsupported feature.
|
|
69
|
+
*/
|
|
70
|
+
const REGEX_LITERAL_ESCAPES = /* @__PURE__ */ new Set(".^$()[]{}|/+*?-\\");
|
|
71
|
+
/**
|
|
72
|
+
* Unescaped regex metacharacters with no LIKE equivalent. Subset of
|
|
73
|
+
* {@link REGEX_LITERAL_ESCAPES}: the same chars are rejected unescaped here
|
|
74
|
+
* but accepted as literals when preceded by `\`.
|
|
75
|
+
*/
|
|
76
|
+
const REGEX_UNSUPPORTED = /* @__PURE__ */ new Set("()[]{}|*+?");
|
|
77
|
+
/**
|
|
78
|
+
* Re-escape a literal char for SQL LIKE under `ESCAPE '\'`. `%`, `_`, and `\`
|
|
79
|
+
* are LIKE metachars and need a backslash prefix; everything else passes through.
|
|
80
|
+
*/
|
|
81
|
+
function likeEscape(ch) {
|
|
82
|
+
return ch === "%" || ch === "_" || ch === "\\" ? `\\${ch}` : ch;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Translates a regex pattern into a SQLite LIKE pattern. The dialect emits the
|
|
86
|
+
* resulting SQL with `ESCAPE '\'`, so `\%` and `\_` in the output denote literal
|
|
87
|
+
* `%` / `_`, and `\\` denotes a literal backslash.
|
|
88
|
+
*
|
|
89
|
+
* Supported subset:
|
|
90
|
+
* - anchors `^` and `$` (must appear at the very start / end of the pattern)
|
|
91
|
+
* - `.` (any single char) and `.*` (any run of chars)
|
|
92
|
+
* - escaped literals: `\.`, `\^`, `\$`, `\(`, `\)`, `\[`, `\]`, `\{`, `\}`,
|
|
93
|
+
* `\|`, `\/`, `\+`, `\*`, `\?`, `\-`, `\\`
|
|
94
|
+
*
|
|
95
|
+
* Throws on character classes, alternation, groups, quantifiers other than `.*`,
|
|
96
|
+
* and shorthand classes (`\d`, `\w`, `\s`, `\b`, …) — these would silently match
|
|
97
|
+
* the wrong rows under a naive translation, and a Node-side fallback would break
|
|
98
|
+
* pagination, ordering, and aggregation pushdown.
|
|
99
|
+
*
|
|
100
|
+
* `^` and `$` outside the start/end of the pattern are treated as literal chars
|
|
101
|
+
* (multiline anchors aren't supported).
|
|
71
102
|
*/
|
|
72
103
|
function regexToLike(pattern) {
|
|
73
104
|
const hasStart = pattern.startsWith("^");
|
|
74
|
-
const hasEnd = pattern
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
105
|
+
const hasEnd = endsWithUnescapedDollar(pattern);
|
|
106
|
+
const start = hasStart ? 1 : 0;
|
|
107
|
+
const end = hasEnd ? pattern.length - 1 : pattern.length;
|
|
108
|
+
let out = "";
|
|
109
|
+
for (let i = start; i < end; i++) {
|
|
110
|
+
const c = pattern[i];
|
|
111
|
+
if (c === "\\") {
|
|
112
|
+
const next = pattern[i + 1];
|
|
113
|
+
if (next === void 0) throw new Error(`Trailing backslash in regex pattern: ${pattern}`);
|
|
114
|
+
if (!REGEX_LITERAL_ESCAPES.has(next)) throw new Error(`Unsupported regex escape '\\${next}' in pattern '${pattern}' — only literal-meaning escapes are supported by the SQLite LIKE translation`);
|
|
115
|
+
out += likeEscape(next);
|
|
116
|
+
i++;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (c === ".") {
|
|
120
|
+
if (pattern[i + 1] === "*") {
|
|
121
|
+
out += "%";
|
|
122
|
+
i++;
|
|
123
|
+
} else out += "_";
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (REGEX_UNSUPPORTED.has(c)) throw new Error(`Unsupported regex feature '${c}' in pattern '${pattern}' — only anchors, '.', '.*', and escaped literals are supported by the SQLite LIKE translation`);
|
|
127
|
+
out += likeEscape(c);
|
|
128
|
+
}
|
|
129
|
+
if (hasStart && hasEnd) return out;
|
|
130
|
+
if (hasStart) return `${out}%`;
|
|
131
|
+
if (hasEnd) return `%${out}`;
|
|
132
|
+
return `%${out}%`;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* `$` at the end of the pattern is the end-anchor only if it is not preceded by
|
|
136
|
+
* an odd number of backslashes (i.e. not escaped). `\$` and `\\\$` are literal,
|
|
137
|
+
* `$` and `\\$` are anchors.
|
|
138
|
+
*/
|
|
139
|
+
function endsWithUnescapedDollar(s) {
|
|
140
|
+
const m = s.match(/(\\*)\$$/);
|
|
141
|
+
return m !== null && m[1].length % 2 === 0;
|
|
84
142
|
}
|
|
85
143
|
const sqliteDialect = {
|
|
86
144
|
quoteIdentifier(name) {
|
|
@@ -99,7 +157,7 @@ const sqliteDialect = {
|
|
|
99
157
|
const { pattern, flags } = parseRegexString(value);
|
|
100
158
|
const likePattern = regexToLike(pattern);
|
|
101
159
|
return {
|
|
102
|
-
sql: flags.includes("i") ?
|
|
160
|
+
sql: `${quotedCol} LIKE ? ESCAPE '\\'${flags.includes("i") ? " COLLATE NOCASE" : ""}`,
|
|
103
161
|
params: [likePattern]
|
|
104
162
|
};
|
|
105
163
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/db-sqlite",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.66",
|
|
4
4
|
"description": "SQLite adapter for @atscript/db with swappable driver support.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"atscript",
|
|
@@ -39,17 +39,17 @@
|
|
|
39
39
|
"@atscript/core": "^0.1.50",
|
|
40
40
|
"@atscript/typescript": "^0.1.50",
|
|
41
41
|
"@types/better-sqlite3": "^7.6.13",
|
|
42
|
-
"@uniqu/core": "^0.1.
|
|
42
|
+
"@uniqu/core": "^0.1.6",
|
|
43
43
|
"better-sqlite3": "^12.6.2",
|
|
44
44
|
"unplugin-atscript": "^0.1.50"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@atscript/core": "^0.1.50",
|
|
48
48
|
"@atscript/typescript": "^0.1.50",
|
|
49
|
-
"@uniqu/core": "^0.1.
|
|
49
|
+
"@uniqu/core": "^0.1.6",
|
|
50
50
|
"better-sqlite3": ">=11.0.0",
|
|
51
|
-
"@atscript/db": "^0.1.
|
|
52
|
-
"@atscript/db-sql-tools": "^0.1.
|
|
51
|
+
"@atscript/db": "^0.1.66",
|
|
52
|
+
"@atscript/db-sql-tools": "^0.1.66"
|
|
53
53
|
},
|
|
54
54
|
"peerDependenciesMeta": {
|
|
55
55
|
"better-sqlite3": {
|