@atscript/db-sql-tools 0.1.38 → 0.1.40
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/README.md +43 -0
- package/dist/index.cjs +120 -113
- package/dist/{index.d.ts → index.d.cts} +32 -28
- package/dist/index.d.mts +103 -0
- package/dist/index.mjs +95 -65
- package/package.json +17 -10
- package/LICENSE +0 -21
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://atscript.dev/logo.svg" alt="Atscript" width="120" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">@atscript/db-sql-tools</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<strong>Define your models once</strong> — get TypeScript types, runtime validation, and DB metadata from a single <code>.as</code> model.
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://db.atscript.dev">Documentation</a> · <a href="https://db.atscript.dev/adapters/">DB Adapters</a>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
Shared SQL builder utilities for Atscript's SQL-based database adapters (`db-sqlite`, `db-mysql`, `db-postgres`). Provides parameterized query generation, MongoDB-style filter translation, and dialect abstraction.
|
|
18
|
+
|
|
19
|
+
This is an internal package — not typically installed directly by end users.
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pnpm add @atscript/db-sql-tools
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Features
|
|
28
|
+
|
|
29
|
+
- `SqlDialect` interface abstracting identifier quoting, parameter placeholders, regex, and value serialization
|
|
30
|
+
- `buildWhere` — translates MongoDB-style filters (`$gt`, `$in`, `$or`, `$regex`, etc.) to parameterized SQL
|
|
31
|
+
- `buildInsert`, `buildSelect`, `buildUpdate`, `buildDelete` — full SQL statement builders
|
|
32
|
+
- `buildAggregateSelect`, `buildAggregateCount` — GROUP BY, HAVING, aggregate functions
|
|
33
|
+
- `buildCreateView` — CREATE VIEW with JOINs, filters, and GROUP BY from Atscript view plans
|
|
34
|
+
- DDL helpers: query expression → SQL, referential actions, default value literals
|
|
35
|
+
|
|
36
|
+
## Documentation
|
|
37
|
+
|
|
38
|
+
- [DB Adapters Guide](https://db.atscript.dev/adapters/)
|
|
39
|
+
- [Full Documentation](https://db.atscript.dev)
|
|
40
|
+
|
|
41
|
+
## License
|
|
42
|
+
|
|
43
|
+
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -1,37 +1,16 @@
|
|
|
1
|
-
"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
var __copyProps = (to, from, except, desc) => {
|
|
10
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
-
key = keys[i];
|
|
12
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
-
get: ((k) => from[k]).bind(null, key),
|
|
14
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
return to;
|
|
18
|
-
};
|
|
19
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
-
value: mod,
|
|
21
|
-
enumerable: true
|
|
22
|
-
}) : target, mod));
|
|
23
|
-
|
|
24
|
-
//#endregion
|
|
25
|
-
const __uniqu_core = __toESM(require("@uniqu/core"));
|
|
26
|
-
const __atscript_db_agg = __toESM(require("@atscript/db/agg"));
|
|
27
|
-
|
|
28
|
-
//#region packages/db-sql-tools/src/dialect.ts
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _uniqu_core = require("@uniqu/core");
|
|
3
|
+
let _atscript_db_agg = require("@atscript/db/agg");
|
|
4
|
+
//#region src/dialect.ts
|
|
5
|
+
/**
|
|
6
|
+
* Replaces positional `?` placeholders with dialect-specific numbered placeholders
|
|
7
|
+
* (e.g. `$1, $2, ...` for PostgreSQL). No-op when `dialect.paramPlaceholder` is not set.
|
|
8
|
+
*/
|
|
29
9
|
function finalizeParams(dialect, fragment) {
|
|
30
10
|
if (!dialect.paramPlaceholder) return fragment;
|
|
31
11
|
let idx = 0;
|
|
32
|
-
const sql = fragment.sql.replace(/\?/g, () => dialect.paramPlaceholder(++idx));
|
|
33
12
|
return {
|
|
34
|
-
sql,
|
|
13
|
+
sql: fragment.sql.replace(/\?/g, () => dialect.paramPlaceholder(++idx)),
|
|
35
14
|
params: fragment.params
|
|
36
15
|
};
|
|
37
16
|
}
|
|
@@ -43,16 +22,18 @@ const EMPTY_OR = {
|
|
|
43
22
|
sql: "0=1",
|
|
44
23
|
params: []
|
|
45
24
|
};
|
|
46
|
-
|
|
47
25
|
//#endregion
|
|
48
|
-
//#region
|
|
26
|
+
//#region src/filter-builder.ts
|
|
27
|
+
/**
|
|
28
|
+
* Creates a dialect-specific filter visitor for `walkFilter`.
|
|
29
|
+
*/
|
|
49
30
|
function createFilterVisitor(dialect) {
|
|
50
31
|
return {
|
|
51
32
|
comparison(field, op, value) {
|
|
52
33
|
const col = dialect.quoteIdentifier(field);
|
|
53
34
|
const v = dialect.toParam(value);
|
|
54
35
|
switch (op) {
|
|
55
|
-
case "$eq":
|
|
36
|
+
case "$eq":
|
|
56
37
|
if (v === null) return {
|
|
57
38
|
sql: `${col} IS NULL`,
|
|
58
39
|
params: []
|
|
@@ -61,8 +42,7 @@ function createFilterVisitor(dialect) {
|
|
|
61
42
|
sql: `${col} = ?`,
|
|
62
43
|
params: [v]
|
|
63
44
|
};
|
|
64
|
-
|
|
65
|
-
case "$ne": {
|
|
45
|
+
case "$ne":
|
|
66
46
|
if (v === null) return {
|
|
67
47
|
sql: `${col} IS NOT NULL`,
|
|
68
48
|
params: []
|
|
@@ -71,7 +51,6 @@ function createFilterVisitor(dialect) {
|
|
|
71
51
|
sql: `${col} != ?`,
|
|
72
52
|
params: [v]
|
|
73
53
|
};
|
|
74
|
-
}
|
|
75
54
|
case "$gt": return {
|
|
76
55
|
sql: `${col} > ?`,
|
|
77
56
|
params: [v]
|
|
@@ -91,18 +70,16 @@ function createFilterVisitor(dialect) {
|
|
|
91
70
|
case "$in": {
|
|
92
71
|
const arr = value.map((x) => dialect.toParam(x));
|
|
93
72
|
if (arr.length === 0) return EMPTY_OR;
|
|
94
|
-
const placeholders = arr.map(() => "?").join(", ");
|
|
95
73
|
return {
|
|
96
|
-
sql: `${col} IN (${
|
|
74
|
+
sql: `${col} IN (${arr.map(() => "?").join(", ")})`,
|
|
97
75
|
params: arr
|
|
98
76
|
};
|
|
99
77
|
}
|
|
100
78
|
case "$nin": {
|
|
101
79
|
const arr = value.map((x) => dialect.toParam(x));
|
|
102
80
|
if (arr.length === 0) return EMPTY_AND;
|
|
103
|
-
const placeholders = arr.map(() => "?").join(", ");
|
|
104
81
|
return {
|
|
105
|
-
sql: `${col} NOT IN (${
|
|
82
|
+
sql: `${col} NOT IN (${arr.map(() => "?").join(", ")})`,
|
|
106
83
|
params: arr
|
|
107
84
|
};
|
|
108
85
|
}
|
|
@@ -114,7 +91,7 @@ function createFilterVisitor(dialect) {
|
|
|
114
91
|
params: []
|
|
115
92
|
};
|
|
116
93
|
case "$regex": return dialect.regex(col, value);
|
|
117
|
-
default: throw new Error(`Unsupported filter operator: ${op}`);
|
|
94
|
+
default: throw new Error(`Unsupported filter operator: ${String(op)}`);
|
|
118
95
|
}
|
|
119
96
|
},
|
|
120
97
|
and(children) {
|
|
@@ -139,7 +116,7 @@ function createFilterVisitor(dialect) {
|
|
|
139
116
|
}
|
|
140
117
|
};
|
|
141
118
|
}
|
|
142
|
-
const visitorCache = new WeakMap();
|
|
119
|
+
const visitorCache = /* @__PURE__ */ new WeakMap();
|
|
143
120
|
function getVisitor(dialect) {
|
|
144
121
|
let visitor = visitorCache.get(dialect);
|
|
145
122
|
if (!visitor) {
|
|
@@ -148,18 +125,22 @@ function getVisitor(dialect) {
|
|
|
148
125
|
}
|
|
149
126
|
return visitor;
|
|
150
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* Translates a filter expression into a parameterized SQL WHERE clause.
|
|
130
|
+
*/
|
|
151
131
|
function buildWhere(dialect, filter) {
|
|
152
132
|
if (!filter || Object.keys(filter).length === 0) return EMPTY_AND;
|
|
153
|
-
return (0,
|
|
133
|
+
return (0, _uniqu_core.walkFilter)(filter, getVisitor(dialect)) ?? EMPTY_AND;
|
|
154
134
|
}
|
|
155
|
-
|
|
156
135
|
//#endregion
|
|
157
|
-
//#region
|
|
136
|
+
//#region src/common.ts
|
|
137
|
+
/** Formats a string value as a SQL literal with single-quote escaping. */
|
|
158
138
|
function sqlStringLiteral(value) {
|
|
159
139
|
return `'${value.replace(/'/g, "''")}'`;
|
|
160
140
|
}
|
|
141
|
+
/** Converts a JS value to a SQL-bindable parameter. Objects/arrays -> JSON, booleans -> 0/1. */
|
|
161
142
|
function toSqlValue(value) {
|
|
162
|
-
if (value ===
|
|
143
|
+
if (value === void 0) return null;
|
|
163
144
|
if (value === null) return null;
|
|
164
145
|
if (typeof value === "object") return JSON.stringify(value);
|
|
165
146
|
if (typeof value === "boolean") return value ? 1 : 0;
|
|
@@ -174,6 +155,7 @@ function refActionToSql(action) {
|
|
|
174
155
|
default: return "NO ACTION";
|
|
175
156
|
}
|
|
176
157
|
}
|
|
158
|
+
/** Returns a safe SQL DEFAULT literal for a given design type. */
|
|
177
159
|
function defaultValueForType(designType) {
|
|
178
160
|
switch (designType) {
|
|
179
161
|
case "number":
|
|
@@ -183,6 +165,11 @@ function defaultValueForType(designType) {
|
|
|
183
165
|
default: return "''";
|
|
184
166
|
}
|
|
185
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* Converts a stored default value string to a SQL DEFAULT literal,
|
|
170
|
+
* respecting the field's designType. Booleans become 0/1, numbers stay unquoted,
|
|
171
|
+
* strings are single-quote-escaped.
|
|
172
|
+
*/
|
|
186
173
|
function defaultValueToSqlLiteral(designType, value) {
|
|
187
174
|
switch (designType) {
|
|
188
175
|
case "boolean": return value === "true" || value === "1" ? "1" : "0";
|
|
@@ -203,27 +190,23 @@ const queryOpToSql = {
|
|
|
203
190
|
$lt: "<",
|
|
204
191
|
$lte: "<="
|
|
205
192
|
};
|
|
193
|
+
/**
|
|
194
|
+
* Renders an AtscriptQueryNode tree to raw SQL (no parameters -- for DDL use only).
|
|
195
|
+
*/
|
|
206
196
|
function queryNodeToSql(node, resolveFieldRef) {
|
|
207
|
-
if ("$and" in node)
|
|
208
|
-
|
|
209
|
-
return children.map((n) => queryNodeToSql(n, resolveFieldRef)).join(" AND ");
|
|
210
|
-
}
|
|
211
|
-
if ("$or" in node) {
|
|
212
|
-
const children = node.$or;
|
|
213
|
-
return `(${children.map((n) => queryNodeToSql(n, resolveFieldRef)).join(" OR ")})`;
|
|
214
|
-
}
|
|
197
|
+
if ("$and" in node) return node.$and.map((n) => queryNodeToSql(n, resolveFieldRef)).join(" AND ");
|
|
198
|
+
if ("$or" in node) return `(${node.$or.map((n) => queryNodeToSql(n, resolveFieldRef)).join(" OR ")})`;
|
|
215
199
|
if ("$not" in node) return `NOT (${queryNodeToSql(node.$not, resolveFieldRef)})`;
|
|
216
200
|
const comp = node;
|
|
217
201
|
const leftSql = resolveFieldRef(comp.left);
|
|
218
202
|
const sqlOp = queryOpToSql[comp.op] || "=";
|
|
219
203
|
if (comp.right && typeof comp.right === "object" && "field" in comp.right) return `${leftSql} ${sqlOp} ${resolveFieldRef(comp.right)}`;
|
|
220
|
-
if (comp.right === null || comp.right ===
|
|
204
|
+
if (comp.right === null || comp.right === void 0) return comp.op === "$ne" ? `${leftSql} IS NOT NULL` : `${leftSql} IS NULL`;
|
|
221
205
|
if (typeof comp.right === "string") return `${leftSql} ${sqlOp} '${comp.right.replace(/'/g, "''")}'`;
|
|
222
206
|
return `${leftSql} ${sqlOp} ${comp.right}`;
|
|
223
207
|
}
|
|
224
|
-
|
|
225
208
|
//#endregion
|
|
226
|
-
//#region
|
|
209
|
+
//#region src/agg.ts
|
|
227
210
|
const AGG_FN_SQL = {
|
|
228
211
|
sum: "SUM",
|
|
229
212
|
avg: "AVG",
|
|
@@ -233,18 +216,19 @@ const AGG_FN_SQL = {
|
|
|
233
216
|
};
|
|
234
217
|
function buildAggExpr(dialect, expr) {
|
|
235
218
|
const fn = AGG_FN_SQL[expr.$fn] ?? expr.$fn.toUpperCase();
|
|
236
|
-
const alias = dialect.quoteIdentifier((0,
|
|
237
|
-
|
|
238
|
-
return `${fn}(${field}) AS ${alias}`;
|
|
219
|
+
const alias = dialect.quoteIdentifier((0, _atscript_db_agg.resolveAlias)(expr));
|
|
220
|
+
return `${fn}(${expr.$field === "*" ? "*" : dialect.quoteIdentifier(expr.$field)}) AS ${alias}`;
|
|
239
221
|
}
|
|
222
|
+
/**
|
|
223
|
+
* Builds a SELECT ... GROUP BY statement with aggregate functions.
|
|
224
|
+
*/
|
|
240
225
|
function buildAggregateSelect(dialect, table, where, controls) {
|
|
241
226
|
const selectParts = [];
|
|
242
227
|
const plainFields = controls.$select?.asArray;
|
|
243
228
|
if (plainFields) for (const f of plainFields) selectParts.push(dialect.quoteIdentifier(f));
|
|
244
229
|
const aggregates = controls.$select?.aggregates;
|
|
245
230
|
if (aggregates) for (const expr of aggregates) selectParts.push(buildAggExpr(dialect, expr));
|
|
246
|
-
|
|
247
|
-
let sql = `SELECT ${cols} FROM ${dialect.quoteTable(table)} WHERE ${where.sql}`;
|
|
231
|
+
let sql = `SELECT ${selectParts.length > 0 ? selectParts.join(", ") : "*"} FROM ${dialect.quoteTable(table)} WHERE ${where.sql}`;
|
|
248
232
|
const params = [...where.params];
|
|
249
233
|
const groupBy = controls.$groupBy;
|
|
250
234
|
if (groupBy?.length) {
|
|
@@ -263,12 +247,12 @@ function buildAggregateSelect(dialect, table, where, controls) {
|
|
|
263
247
|
for (const [col, dir] of Object.entries(controls.$sort)) orderParts.push(`${dialect.quoteIdentifier(col)} ${dir === -1 ? "DESC" : "ASC"}`);
|
|
264
248
|
if (orderParts.length > 0) sql += ` ORDER BY ${orderParts.join(", ")}`;
|
|
265
249
|
}
|
|
266
|
-
if (controls.$limit !==
|
|
250
|
+
if (controls.$limit !== void 0) {
|
|
267
251
|
sql += ` LIMIT ?`;
|
|
268
252
|
params.push(controls.$limit);
|
|
269
253
|
}
|
|
270
|
-
if (controls.$skip !==
|
|
271
|
-
if (controls.$limit ===
|
|
254
|
+
if (controls.$skip !== void 0) {
|
|
255
|
+
if (controls.$limit === void 0) sql += ` LIMIT ${dialect.unlimitedLimit}`;
|
|
272
256
|
sql += ` OFFSET ?`;
|
|
273
257
|
params.push(controls.$skip);
|
|
274
258
|
}
|
|
@@ -277,25 +261,27 @@ function buildAggregateSelect(dialect, table, where, controls) {
|
|
|
277
261
|
params
|
|
278
262
|
});
|
|
279
263
|
}
|
|
264
|
+
/**
|
|
265
|
+
* Builds a COUNT query for the number of distinct groups.
|
|
266
|
+
* Returns `{ count: N }` when executed.
|
|
267
|
+
*/
|
|
280
268
|
function buildAggregateCount(dialect, table, where, controls) {
|
|
281
269
|
const groupFields = controls.$groupBy;
|
|
282
|
-
if (!groupFields?.length) {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
params: where.params
|
|
287
|
-
});
|
|
288
|
-
}
|
|
270
|
+
if (!groupFields?.length) return finalizeParams(dialect, {
|
|
271
|
+
sql: `SELECT COUNT(*) AS ${dialect.quoteIdentifier("count")} FROM ${dialect.quoteTable(table)} WHERE ${where.sql}`,
|
|
272
|
+
params: where.params
|
|
273
|
+
});
|
|
289
274
|
const groupCols = groupFields.map((f) => dialect.quoteIdentifier(f)).join(", ");
|
|
290
|
-
const sql = `SELECT COUNT(*) AS ${dialect.quoteIdentifier("count")} FROM (SELECT 1 FROM ${dialect.quoteTable(table)} WHERE ${where.sql} GROUP BY ${groupCols}) AS ${dialect.quoteIdentifier("_groups")}`;
|
|
291
275
|
return finalizeParams(dialect, {
|
|
292
|
-
sql
|
|
276
|
+
sql: `SELECT COUNT(*) AS ${dialect.quoteIdentifier("count")} FROM (SELECT 1 FROM ${dialect.quoteTable(table)} WHERE ${where.sql} GROUP BY ${groupCols}) AS ${dialect.quoteIdentifier("_groups")}`,
|
|
293
277
|
params: where.params
|
|
294
278
|
});
|
|
295
279
|
}
|
|
296
|
-
|
|
297
280
|
//#endregion
|
|
298
|
-
//#region
|
|
281
|
+
//#region src/sql-builder.ts
|
|
282
|
+
/**
|
|
283
|
+
* Builds an INSERT statement.
|
|
284
|
+
*/
|
|
299
285
|
function buildInsert(dialect, table, data) {
|
|
300
286
|
const keys = Object.keys(data);
|
|
301
287
|
const cols = keys.map((k) => dialect.quoteIdentifier(k)).join(", ");
|
|
@@ -305,21 +291,23 @@ function buildInsert(dialect, table, data) {
|
|
|
305
291
|
params: keys.map((k) => dialect.toValue(data[k]))
|
|
306
292
|
});
|
|
307
293
|
}
|
|
294
|
+
/**
|
|
295
|
+
* Builds a SELECT statement with optional sort, limit, offset, projection.
|
|
296
|
+
*/
|
|
308
297
|
function buildSelect(dialect, table, where, controls) {
|
|
309
|
-
|
|
310
|
-
let sql = `SELECT ${cols} FROM ${dialect.quoteTable(table)} WHERE ${where.sql}`;
|
|
298
|
+
let sql = `SELECT ${buildProjection(dialect, controls?.$select)} FROM ${dialect.quoteTable(table)} WHERE ${where.sql}`;
|
|
311
299
|
const params = [...where.params];
|
|
312
300
|
if (controls?.$sort) {
|
|
313
301
|
const orderParts = [];
|
|
314
302
|
for (const [col, dir] of Object.entries(controls.$sort)) orderParts.push(`${dialect.quoteIdentifier(col)} ${dir === -1 ? "DESC" : "ASC"}`);
|
|
315
303
|
if (orderParts.length > 0) sql += ` ORDER BY ${orderParts.join(", ")}`;
|
|
316
304
|
}
|
|
317
|
-
if (controls?.$limit !==
|
|
305
|
+
if (controls?.$limit !== void 0) {
|
|
318
306
|
sql += ` LIMIT ?`;
|
|
319
307
|
params.push(controls.$limit);
|
|
320
308
|
}
|
|
321
|
-
if (controls?.$skip !==
|
|
322
|
-
if (controls.$limit ===
|
|
309
|
+
if (controls?.$skip !== void 0) {
|
|
310
|
+
if (controls.$limit === void 0) sql += ` LIMIT ${dialect.unlimitedLimit}`;
|
|
323
311
|
sql += ` OFFSET ?`;
|
|
324
312
|
params.push(controls.$skip);
|
|
325
313
|
}
|
|
@@ -328,28 +316,47 @@ function buildSelect(dialect, table, where, controls) {
|
|
|
328
316
|
params
|
|
329
317
|
});
|
|
330
318
|
}
|
|
331
|
-
|
|
319
|
+
/**
|
|
320
|
+
* Builds an UPDATE ... SET ... WHERE statement with optional LIMIT.
|
|
321
|
+
*/
|
|
322
|
+
function buildUpdate(dialect, table, data, where, limit, ops) {
|
|
332
323
|
const setClauses = [];
|
|
333
324
|
const params = [];
|
|
334
325
|
for (const [key, value] of Object.entries(data)) {
|
|
335
326
|
setClauses.push(`${dialect.quoteIdentifier(key)} = ?`);
|
|
336
327
|
params.push(dialect.toValue(value));
|
|
337
328
|
}
|
|
329
|
+
if (ops?.inc) for (const key in ops.inc) {
|
|
330
|
+
const col = dialect.quoteIdentifier(key);
|
|
331
|
+
setClauses.push(`${col} = ${col} + ?`);
|
|
332
|
+
params.push(ops.inc[key]);
|
|
333
|
+
}
|
|
334
|
+
if (ops?.mul) for (const key in ops.mul) {
|
|
335
|
+
const col = dialect.quoteIdentifier(key);
|
|
336
|
+
setClauses.push(`${col} = ${col} * ?`);
|
|
337
|
+
params.push(ops.mul[key]);
|
|
338
|
+
}
|
|
338
339
|
let sql = `UPDATE ${dialect.quoteTable(table)} SET ${setClauses.join(", ")} WHERE ${where.sql}`;
|
|
339
|
-
if (limit !==
|
|
340
|
+
if (limit !== void 0) sql += ` LIMIT ${limit}`;
|
|
340
341
|
return finalizeParams(dialect, {
|
|
341
342
|
sql,
|
|
342
343
|
params: [...params, ...where.params]
|
|
343
344
|
});
|
|
344
345
|
}
|
|
346
|
+
/**
|
|
347
|
+
* Builds a DELETE ... WHERE statement with optional LIMIT.
|
|
348
|
+
*/
|
|
345
349
|
function buildDelete(dialect, table, where, limit) {
|
|
346
350
|
let sql = `DELETE FROM ${dialect.quoteTable(table)} WHERE ${where.sql}`;
|
|
347
|
-
if (limit !==
|
|
351
|
+
if (limit !== void 0) sql += ` LIMIT ${limit}`;
|
|
348
352
|
return finalizeParams(dialect, {
|
|
349
353
|
sql,
|
|
350
354
|
params: where.params
|
|
351
355
|
});
|
|
352
356
|
}
|
|
357
|
+
/**
|
|
358
|
+
* Builds a column projection (SELECT clause fields).
|
|
359
|
+
*/
|
|
353
360
|
function buildProjection(dialect, select) {
|
|
354
361
|
const fields = select?.asArray;
|
|
355
362
|
if (!fields) return "*";
|
|
@@ -360,11 +367,13 @@ function buildProjection(dialect, select) {
|
|
|
360
367
|
}
|
|
361
368
|
return sql || "*";
|
|
362
369
|
}
|
|
363
|
-
/** Builds the SQL expression for a single aggregate column. */
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
return `${fn}(${arg})`;
|
|
370
|
+
/** Builds the SQL expression for a single aggregate column. */
|
|
371
|
+
function buildAggColExpr(dialect, c) {
|
|
372
|
+
return `${AGG_FN_SQL[c.aggFn] ?? c.aggFn.toUpperCase()}(${c.aggField === "*" ? "*" : `${dialect.quoteIdentifier(c.sourceTable)}.${dialect.quoteIdentifier(c.sourceColumn)}`})`;
|
|
367
373
|
}
|
|
374
|
+
/**
|
|
375
|
+
* Builds a CREATE VIEW statement from a view plan and column mappings.
|
|
376
|
+
*/
|
|
368
377
|
function buildCreateView(dialect, viewName, plan, columns, resolveFieldRef) {
|
|
369
378
|
const selectCols = columns.map((c) => {
|
|
370
379
|
if (c.aggFn) return `${buildAggColExpr(dialect, c)} AS ${dialect.quoteIdentifier(c.viewColumn)}`;
|
|
@@ -379,15 +388,14 @@ function buildCreateView(dialect, viewName, plan, columns, resolveFieldRef) {
|
|
|
379
388
|
const whereClause = queryNodeToSql(plan.filter, resolveFieldRef);
|
|
380
389
|
sql += ` WHERE ${whereClause}`;
|
|
381
390
|
}
|
|
382
|
-
|
|
383
|
-
if (hasAggregates) {
|
|
391
|
+
if (columns.some((c) => c.aggFn)) {
|
|
384
392
|
const dimensionCols = columns.filter((c) => !c.aggFn);
|
|
385
393
|
if (dimensionCols.length > 0) {
|
|
386
394
|
const groupByCols = dimensionCols.map((c) => `${dialect.quoteIdentifier(c.sourceTable)}.${dialect.quoteIdentifier(c.sourceColumn)}`).join(", ");
|
|
387
395
|
sql += ` GROUP BY ${groupByCols}`;
|
|
388
396
|
}
|
|
389
397
|
if (plan.having) {
|
|
390
|
-
const columnMap = new Map();
|
|
398
|
+
const columnMap = /* @__PURE__ */ new Map();
|
|
391
399
|
for (const c of columns) columnMap.set(c.viewColumn, c);
|
|
392
400
|
const havingResolver = (ref) => {
|
|
393
401
|
if (!ref.type) {
|
|
@@ -403,26 +411,25 @@ function buildCreateView(dialect, viewName, plan, columns, resolveFieldRef) {
|
|
|
403
411
|
}
|
|
404
412
|
return sql;
|
|
405
413
|
}
|
|
406
|
-
|
|
407
414
|
//#endregion
|
|
408
|
-
exports.AGG_FN_SQL = AGG_FN_SQL
|
|
409
|
-
exports.EMPTY_AND = EMPTY_AND
|
|
410
|
-
exports.EMPTY_OR = EMPTY_OR
|
|
411
|
-
exports.buildAggregateCount = buildAggregateCount
|
|
412
|
-
exports.buildAggregateSelect = buildAggregateSelect
|
|
413
|
-
exports.buildCreateView = buildCreateView
|
|
414
|
-
exports.buildDelete = buildDelete
|
|
415
|
-
exports.buildInsert = buildInsert
|
|
416
|
-
exports.buildProjection = buildProjection
|
|
417
|
-
exports.buildSelect = buildSelect
|
|
418
|
-
exports.buildUpdate = buildUpdate
|
|
419
|
-
exports.buildWhere = buildWhere
|
|
420
|
-
exports.createFilterVisitor = createFilterVisitor
|
|
421
|
-
exports.defaultValueForType = defaultValueForType
|
|
422
|
-
exports.defaultValueToSqlLiteral = defaultValueToSqlLiteral
|
|
423
|
-
exports.finalizeParams = finalizeParams
|
|
424
|
-
exports.queryNodeToSql = queryNodeToSql
|
|
425
|
-
exports.queryOpToSql = queryOpToSql
|
|
426
|
-
exports.refActionToSql = refActionToSql
|
|
427
|
-
exports.sqlStringLiteral = sqlStringLiteral
|
|
428
|
-
exports.toSqlValue = toSqlValue
|
|
415
|
+
exports.AGG_FN_SQL = AGG_FN_SQL;
|
|
416
|
+
exports.EMPTY_AND = EMPTY_AND;
|
|
417
|
+
exports.EMPTY_OR = EMPTY_OR;
|
|
418
|
+
exports.buildAggregateCount = buildAggregateCount;
|
|
419
|
+
exports.buildAggregateSelect = buildAggregateSelect;
|
|
420
|
+
exports.buildCreateView = buildCreateView;
|
|
421
|
+
exports.buildDelete = buildDelete;
|
|
422
|
+
exports.buildInsert = buildInsert;
|
|
423
|
+
exports.buildProjection = buildProjection;
|
|
424
|
+
exports.buildSelect = buildSelect;
|
|
425
|
+
exports.buildUpdate = buildUpdate;
|
|
426
|
+
exports.buildWhere = buildWhere;
|
|
427
|
+
exports.createFilterVisitor = createFilterVisitor;
|
|
428
|
+
exports.defaultValueForType = defaultValueForType;
|
|
429
|
+
exports.defaultValueToSqlLiteral = defaultValueToSqlLiteral;
|
|
430
|
+
exports.finalizeParams = finalizeParams;
|
|
431
|
+
exports.queryNodeToSql = queryNodeToSql;
|
|
432
|
+
exports.queryOpToSql = queryOpToSql;
|
|
433
|
+
exports.refActionToSql = refActionToSql;
|
|
434
|
+
exports.sqlStringLiteral = sqlStringLiteral;
|
|
435
|
+
exports.toSqlValue = toSqlValue;
|
|
@@ -1,27 +1,28 @@
|
|
|
1
|
-
import { FilterExpr, FilterVisitor } from
|
|
2
|
-
import {
|
|
1
|
+
import { FilterExpr, FilterVisitor } from "@uniqu/core";
|
|
2
|
+
import { AtscriptQueryFieldRef, AtscriptQueryNode, DbControls, TDbReferentialAction, TFieldOps, TViewColumnMapping, TViewPlan, UniquSelect } from "@atscript/db";
|
|
3
3
|
|
|
4
|
+
//#region src/dialect.d.ts
|
|
4
5
|
interface TSqlFragment {
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
sql: string;
|
|
7
|
+
params: unknown[];
|
|
7
8
|
}
|
|
8
9
|
interface SqlDialect {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
10
|
+
/** Quotes a column/table name */
|
|
11
|
+
quoteIdentifier(name: string): string;
|
|
12
|
+
/** Quotes a possibly schema-qualified table name */
|
|
13
|
+
quoteTable(name: string): string;
|
|
14
|
+
/** SQL literal for unlimited LIMIT (SQLite: '-1', MySQL: '18446744073709551615') */
|
|
15
|
+
unlimitedLimit: string;
|
|
16
|
+
/** Convert JS value to SQL-bindable param for DML */
|
|
17
|
+
toValue(value: unknown): unknown;
|
|
18
|
+
/** Convert JS value to SQL-bindable param for filters (lighter) */
|
|
19
|
+
toParam(value: unknown): unknown;
|
|
20
|
+
/** Handle $regex filter */
|
|
21
|
+
regex(quotedCol: string, value: unknown): TSqlFragment;
|
|
22
|
+
/** e.g. 'CREATE VIEW IF NOT EXISTS' or 'CREATE OR REPLACE VIEW' */
|
|
23
|
+
createViewPrefix: string;
|
|
24
|
+
/** Returns a parameter placeholder for the given 1-based index. When absent, '?' is used. */
|
|
25
|
+
paramPlaceholder?: (index: number) => string;
|
|
25
26
|
}
|
|
26
27
|
/**
|
|
27
28
|
* Replaces positional `?` placeholders with dialect-specific numbered placeholders
|
|
@@ -30,7 +31,8 @@ interface SqlDialect {
|
|
|
30
31
|
declare function finalizeParams(dialect: SqlDialect, fragment: TSqlFragment): TSqlFragment;
|
|
31
32
|
declare const EMPTY_AND: TSqlFragment;
|
|
32
33
|
declare const EMPTY_OR: TSqlFragment;
|
|
33
|
-
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/filter-builder.d.ts
|
|
34
36
|
/**
|
|
35
37
|
* Creates a dialect-specific filter visitor for `walkFilter`.
|
|
36
38
|
*/
|
|
@@ -39,7 +41,8 @@ declare function createFilterVisitor(dialect: SqlDialect): FilterVisitor<TSqlFra
|
|
|
39
41
|
* Translates a filter expression into a parameterized SQL WHERE clause.
|
|
40
42
|
*/
|
|
41
43
|
declare function buildWhere(dialect: SqlDialect, filter: FilterExpr): TSqlFragment;
|
|
42
|
-
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/sql-builder.d.ts
|
|
43
46
|
/**
|
|
44
47
|
* Builds an INSERT statement.
|
|
45
48
|
*/
|
|
@@ -51,7 +54,7 @@ declare function buildSelect(dialect: SqlDialect, table: string, where: TSqlFrag
|
|
|
51
54
|
/**
|
|
52
55
|
* Builds an UPDATE ... SET ... WHERE statement with optional LIMIT.
|
|
53
56
|
*/
|
|
54
|
-
declare function buildUpdate(dialect: SqlDialect, table: string, data: Record<string, unknown>, where: TSqlFragment, limit?: number): TSqlFragment;
|
|
57
|
+
declare function buildUpdate(dialect: SqlDialect, table: string, data: Record<string, unknown>, where: TSqlFragment, limit?: number, ops?: TFieldOps): TSqlFragment;
|
|
55
58
|
/**
|
|
56
59
|
* Builds a DELETE ... WHERE statement with optional LIMIT.
|
|
57
60
|
*/
|
|
@@ -64,7 +67,8 @@ declare function buildProjection(dialect: SqlDialect, select?: UniquSelect): str
|
|
|
64
67
|
* Builds a CREATE VIEW statement from a view plan and column mappings.
|
|
65
68
|
*/
|
|
66
69
|
declare function buildCreateView(dialect: SqlDialect, viewName: string, plan: TViewPlan, columns: TViewColumnMapping[], resolveFieldRef: (ref: AtscriptQueryFieldRef) => string): string;
|
|
67
|
-
|
|
70
|
+
//#endregion
|
|
71
|
+
//#region src/common.d.ts
|
|
68
72
|
/** Formats a string value as a SQL literal with single-quote escaping. */
|
|
69
73
|
declare function sqlStringLiteral(value: string): string;
|
|
70
74
|
/** Converts a JS value to a SQL-bindable parameter. Objects/arrays -> JSON, booleans -> 0/1. */
|
|
@@ -83,7 +87,8 @@ declare const queryOpToSql: Record<string, string>;
|
|
|
83
87
|
* Renders an AtscriptQueryNode tree to raw SQL (no parameters -- for DDL use only).
|
|
84
88
|
*/
|
|
85
89
|
declare function queryNodeToSql(node: AtscriptQueryNode, resolveFieldRef: (ref: AtscriptQueryFieldRef) => string): string;
|
|
86
|
-
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/agg.d.ts
|
|
87
92
|
declare const AGG_FN_SQL: Record<string, string>;
|
|
88
93
|
/**
|
|
89
94
|
* Builds a SELECT ... GROUP BY statement with aggregate functions.
|
|
@@ -94,6 +99,5 @@ declare function buildAggregateSelect(dialect: SqlDialect, table: string, where:
|
|
|
94
99
|
* Returns `{ count: N }` when executed.
|
|
95
100
|
*/
|
|
96
101
|
declare function buildAggregateCount(dialect: SqlDialect, table: string, where: TSqlFragment, controls: DbControls): TSqlFragment;
|
|
97
|
-
|
|
98
|
-
export { AGG_FN_SQL, EMPTY_AND, EMPTY_OR, buildAggregateCount, buildAggregateSelect, buildCreateView, buildDelete, buildInsert, buildProjection, buildSelect, buildUpdate, buildWhere, createFilterVisitor, defaultValueForType, defaultValueToSqlLiteral, finalizeParams, queryNodeToSql, queryOpToSql, refActionToSql, sqlStringLiteral, toSqlValue };
|
|
99
|
-
export type { SqlDialect, TSqlFragment };
|
|
102
|
+
//#endregion
|
|
103
|
+
export { AGG_FN_SQL, EMPTY_AND, EMPTY_OR, type SqlDialect, type TSqlFragment, buildAggregateCount, buildAggregateSelect, buildCreateView, buildDelete, buildInsert, buildProjection, buildSelect, buildUpdate, buildWhere, createFilterVisitor, defaultValueForType, defaultValueToSqlLiteral, finalizeParams, queryNodeToSql, queryOpToSql, refActionToSql, sqlStringLiteral, toSqlValue };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { FilterExpr, FilterVisitor } from "@uniqu/core";
|
|
2
|
+
import { AtscriptQueryFieldRef, AtscriptQueryNode, DbControls, TDbReferentialAction, TFieldOps, TViewColumnMapping, TViewPlan, UniquSelect } from "@atscript/db";
|
|
3
|
+
|
|
4
|
+
//#region src/dialect.d.ts
|
|
5
|
+
interface TSqlFragment {
|
|
6
|
+
sql: string;
|
|
7
|
+
params: unknown[];
|
|
8
|
+
}
|
|
9
|
+
interface SqlDialect {
|
|
10
|
+
/** Quotes a column/table name */
|
|
11
|
+
quoteIdentifier(name: string): string;
|
|
12
|
+
/** Quotes a possibly schema-qualified table name */
|
|
13
|
+
quoteTable(name: string): string;
|
|
14
|
+
/** SQL literal for unlimited LIMIT (SQLite: '-1', MySQL: '18446744073709551615') */
|
|
15
|
+
unlimitedLimit: string;
|
|
16
|
+
/** Convert JS value to SQL-bindable param for DML */
|
|
17
|
+
toValue(value: unknown): unknown;
|
|
18
|
+
/** Convert JS value to SQL-bindable param for filters (lighter) */
|
|
19
|
+
toParam(value: unknown): unknown;
|
|
20
|
+
/** Handle $regex filter */
|
|
21
|
+
regex(quotedCol: string, value: unknown): TSqlFragment;
|
|
22
|
+
/** e.g. 'CREATE VIEW IF NOT EXISTS' or 'CREATE OR REPLACE VIEW' */
|
|
23
|
+
createViewPrefix: string;
|
|
24
|
+
/** Returns a parameter placeholder for the given 1-based index. When absent, '?' is used. */
|
|
25
|
+
paramPlaceholder?: (index: number) => string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Replaces positional `?` placeholders with dialect-specific numbered placeholders
|
|
29
|
+
* (e.g. `$1, $2, ...` for PostgreSQL). No-op when `dialect.paramPlaceholder` is not set.
|
|
30
|
+
*/
|
|
31
|
+
declare function finalizeParams(dialect: SqlDialect, fragment: TSqlFragment): TSqlFragment;
|
|
32
|
+
declare const EMPTY_AND: TSqlFragment;
|
|
33
|
+
declare const EMPTY_OR: TSqlFragment;
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/filter-builder.d.ts
|
|
36
|
+
/**
|
|
37
|
+
* Creates a dialect-specific filter visitor for `walkFilter`.
|
|
38
|
+
*/
|
|
39
|
+
declare function createFilterVisitor(dialect: SqlDialect): FilterVisitor<TSqlFragment>;
|
|
40
|
+
/**
|
|
41
|
+
* Translates a filter expression into a parameterized SQL WHERE clause.
|
|
42
|
+
*/
|
|
43
|
+
declare function buildWhere(dialect: SqlDialect, filter: FilterExpr): TSqlFragment;
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/sql-builder.d.ts
|
|
46
|
+
/**
|
|
47
|
+
* Builds an INSERT statement.
|
|
48
|
+
*/
|
|
49
|
+
declare function buildInsert(dialect: SqlDialect, table: string, data: Record<string, unknown>): TSqlFragment;
|
|
50
|
+
/**
|
|
51
|
+
* Builds a SELECT statement with optional sort, limit, offset, projection.
|
|
52
|
+
*/
|
|
53
|
+
declare function buildSelect(dialect: SqlDialect, table: string, where: TSqlFragment, controls?: DbControls): TSqlFragment;
|
|
54
|
+
/**
|
|
55
|
+
* Builds an UPDATE ... SET ... WHERE statement with optional LIMIT.
|
|
56
|
+
*/
|
|
57
|
+
declare function buildUpdate(dialect: SqlDialect, table: string, data: Record<string, unknown>, where: TSqlFragment, limit?: number, ops?: TFieldOps): TSqlFragment;
|
|
58
|
+
/**
|
|
59
|
+
* Builds a DELETE ... WHERE statement with optional LIMIT.
|
|
60
|
+
*/
|
|
61
|
+
declare function buildDelete(dialect: SqlDialect, table: string, where: TSqlFragment, limit?: number): TSqlFragment;
|
|
62
|
+
/**
|
|
63
|
+
* Builds a column projection (SELECT clause fields).
|
|
64
|
+
*/
|
|
65
|
+
declare function buildProjection(dialect: SqlDialect, select?: UniquSelect): string;
|
|
66
|
+
/**
|
|
67
|
+
* Builds a CREATE VIEW statement from a view plan and column mappings.
|
|
68
|
+
*/
|
|
69
|
+
declare function buildCreateView(dialect: SqlDialect, viewName: string, plan: TViewPlan, columns: TViewColumnMapping[], resolveFieldRef: (ref: AtscriptQueryFieldRef) => string): string;
|
|
70
|
+
//#endregion
|
|
71
|
+
//#region src/common.d.ts
|
|
72
|
+
/** Formats a string value as a SQL literal with single-quote escaping. */
|
|
73
|
+
declare function sqlStringLiteral(value: string): string;
|
|
74
|
+
/** Converts a JS value to a SQL-bindable parameter. Objects/arrays -> JSON, booleans -> 0/1. */
|
|
75
|
+
declare function toSqlValue(value: unknown): unknown;
|
|
76
|
+
declare function refActionToSql(action: TDbReferentialAction): string;
|
|
77
|
+
/** Returns a safe SQL DEFAULT literal for a given design type. */
|
|
78
|
+
declare function defaultValueForType(designType: string): string;
|
|
79
|
+
/**
|
|
80
|
+
* Converts a stored default value string to a SQL DEFAULT literal,
|
|
81
|
+
* respecting the field's designType. Booleans become 0/1, numbers stay unquoted,
|
|
82
|
+
* strings are single-quote-escaped.
|
|
83
|
+
*/
|
|
84
|
+
declare function defaultValueToSqlLiteral(designType: string, value: string): string;
|
|
85
|
+
declare const queryOpToSql: Record<string, string>;
|
|
86
|
+
/**
|
|
87
|
+
* Renders an AtscriptQueryNode tree to raw SQL (no parameters -- for DDL use only).
|
|
88
|
+
*/
|
|
89
|
+
declare function queryNodeToSql(node: AtscriptQueryNode, resolveFieldRef: (ref: AtscriptQueryFieldRef) => string): string;
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/agg.d.ts
|
|
92
|
+
declare const AGG_FN_SQL: Record<string, string>;
|
|
93
|
+
/**
|
|
94
|
+
* Builds a SELECT ... GROUP BY statement with aggregate functions.
|
|
95
|
+
*/
|
|
96
|
+
declare function buildAggregateSelect(dialect: SqlDialect, table: string, where: TSqlFragment, controls: DbControls): TSqlFragment;
|
|
97
|
+
/**
|
|
98
|
+
* Builds a COUNT query for the number of distinct groups.
|
|
99
|
+
* Returns `{ count: N }` when executed.
|
|
100
|
+
*/
|
|
101
|
+
declare function buildAggregateCount(dialect: SqlDialect, table: string, where: TSqlFragment, controls: DbControls): TSqlFragment;
|
|
102
|
+
//#endregion
|
|
103
|
+
export { AGG_FN_SQL, EMPTY_AND, EMPTY_OR, type SqlDialect, type TSqlFragment, buildAggregateCount, buildAggregateSelect, buildCreateView, buildDelete, buildInsert, buildProjection, buildSelect, buildUpdate, buildWhere, createFilterVisitor, defaultValueForType, defaultValueToSqlLiteral, finalizeParams, queryNodeToSql, queryOpToSql, refActionToSql, sqlStringLiteral, toSqlValue };
|
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { walkFilter } from "@uniqu/core";
|
|
2
2
|
import { resolveAlias } from "@atscript/db/agg";
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
//#region src/dialect.ts
|
|
4
|
+
/**
|
|
5
|
+
* Replaces positional `?` placeholders with dialect-specific numbered placeholders
|
|
6
|
+
* (e.g. `$1, $2, ...` for PostgreSQL). No-op when `dialect.paramPlaceholder` is not set.
|
|
7
|
+
*/
|
|
5
8
|
function finalizeParams(dialect, fragment) {
|
|
6
9
|
if (!dialect.paramPlaceholder) return fragment;
|
|
7
10
|
let idx = 0;
|
|
8
|
-
const sql = fragment.sql.replace(/\?/g, () => dialect.paramPlaceholder(++idx));
|
|
9
11
|
return {
|
|
10
|
-
sql,
|
|
12
|
+
sql: fragment.sql.replace(/\?/g, () => dialect.paramPlaceholder(++idx)),
|
|
11
13
|
params: fragment.params
|
|
12
14
|
};
|
|
13
15
|
}
|
|
@@ -19,16 +21,18 @@ const EMPTY_OR = {
|
|
|
19
21
|
sql: "0=1",
|
|
20
22
|
params: []
|
|
21
23
|
};
|
|
22
|
-
|
|
23
24
|
//#endregion
|
|
24
|
-
//#region
|
|
25
|
+
//#region src/filter-builder.ts
|
|
26
|
+
/**
|
|
27
|
+
* Creates a dialect-specific filter visitor for `walkFilter`.
|
|
28
|
+
*/
|
|
25
29
|
function createFilterVisitor(dialect) {
|
|
26
30
|
return {
|
|
27
31
|
comparison(field, op, value) {
|
|
28
32
|
const col = dialect.quoteIdentifier(field);
|
|
29
33
|
const v = dialect.toParam(value);
|
|
30
34
|
switch (op) {
|
|
31
|
-
case "$eq":
|
|
35
|
+
case "$eq":
|
|
32
36
|
if (v === null) return {
|
|
33
37
|
sql: `${col} IS NULL`,
|
|
34
38
|
params: []
|
|
@@ -37,8 +41,7 @@ function createFilterVisitor(dialect) {
|
|
|
37
41
|
sql: `${col} = ?`,
|
|
38
42
|
params: [v]
|
|
39
43
|
};
|
|
40
|
-
|
|
41
|
-
case "$ne": {
|
|
44
|
+
case "$ne":
|
|
42
45
|
if (v === null) return {
|
|
43
46
|
sql: `${col} IS NOT NULL`,
|
|
44
47
|
params: []
|
|
@@ -47,7 +50,6 @@ function createFilterVisitor(dialect) {
|
|
|
47
50
|
sql: `${col} != ?`,
|
|
48
51
|
params: [v]
|
|
49
52
|
};
|
|
50
|
-
}
|
|
51
53
|
case "$gt": return {
|
|
52
54
|
sql: `${col} > ?`,
|
|
53
55
|
params: [v]
|
|
@@ -67,18 +69,16 @@ function createFilterVisitor(dialect) {
|
|
|
67
69
|
case "$in": {
|
|
68
70
|
const arr = value.map((x) => dialect.toParam(x));
|
|
69
71
|
if (arr.length === 0) return EMPTY_OR;
|
|
70
|
-
const placeholders = arr.map(() => "?").join(", ");
|
|
71
72
|
return {
|
|
72
|
-
sql: `${col} IN (${
|
|
73
|
+
sql: `${col} IN (${arr.map(() => "?").join(", ")})`,
|
|
73
74
|
params: arr
|
|
74
75
|
};
|
|
75
76
|
}
|
|
76
77
|
case "$nin": {
|
|
77
78
|
const arr = value.map((x) => dialect.toParam(x));
|
|
78
79
|
if (arr.length === 0) return EMPTY_AND;
|
|
79
|
-
const placeholders = arr.map(() => "?").join(", ");
|
|
80
80
|
return {
|
|
81
|
-
sql: `${col} NOT IN (${
|
|
81
|
+
sql: `${col} NOT IN (${arr.map(() => "?").join(", ")})`,
|
|
82
82
|
params: arr
|
|
83
83
|
};
|
|
84
84
|
}
|
|
@@ -90,7 +90,7 @@ function createFilterVisitor(dialect) {
|
|
|
90
90
|
params: []
|
|
91
91
|
};
|
|
92
92
|
case "$regex": return dialect.regex(col, value);
|
|
93
|
-
default: throw new Error(`Unsupported filter operator: ${op}`);
|
|
93
|
+
default: throw new Error(`Unsupported filter operator: ${String(op)}`);
|
|
94
94
|
}
|
|
95
95
|
},
|
|
96
96
|
and(children) {
|
|
@@ -115,7 +115,7 @@ function createFilterVisitor(dialect) {
|
|
|
115
115
|
}
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
|
-
const visitorCache = new WeakMap();
|
|
118
|
+
const visitorCache = /* @__PURE__ */ new WeakMap();
|
|
119
119
|
function getVisitor(dialect) {
|
|
120
120
|
let visitor = visitorCache.get(dialect);
|
|
121
121
|
if (!visitor) {
|
|
@@ -124,18 +124,22 @@ function getVisitor(dialect) {
|
|
|
124
124
|
}
|
|
125
125
|
return visitor;
|
|
126
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Translates a filter expression into a parameterized SQL WHERE clause.
|
|
129
|
+
*/
|
|
127
130
|
function buildWhere(dialect, filter) {
|
|
128
131
|
if (!filter || Object.keys(filter).length === 0) return EMPTY_AND;
|
|
129
132
|
return walkFilter(filter, getVisitor(dialect)) ?? EMPTY_AND;
|
|
130
133
|
}
|
|
131
|
-
|
|
132
134
|
//#endregion
|
|
133
|
-
//#region
|
|
135
|
+
//#region src/common.ts
|
|
136
|
+
/** Formats a string value as a SQL literal with single-quote escaping. */
|
|
134
137
|
function sqlStringLiteral(value) {
|
|
135
138
|
return `'${value.replace(/'/g, "''")}'`;
|
|
136
139
|
}
|
|
140
|
+
/** Converts a JS value to a SQL-bindable parameter. Objects/arrays -> JSON, booleans -> 0/1. */
|
|
137
141
|
function toSqlValue(value) {
|
|
138
|
-
if (value ===
|
|
142
|
+
if (value === void 0) return null;
|
|
139
143
|
if (value === null) return null;
|
|
140
144
|
if (typeof value === "object") return JSON.stringify(value);
|
|
141
145
|
if (typeof value === "boolean") return value ? 1 : 0;
|
|
@@ -150,6 +154,7 @@ function refActionToSql(action) {
|
|
|
150
154
|
default: return "NO ACTION";
|
|
151
155
|
}
|
|
152
156
|
}
|
|
157
|
+
/** Returns a safe SQL DEFAULT literal for a given design type. */
|
|
153
158
|
function defaultValueForType(designType) {
|
|
154
159
|
switch (designType) {
|
|
155
160
|
case "number":
|
|
@@ -159,6 +164,11 @@ function defaultValueForType(designType) {
|
|
|
159
164
|
default: return "''";
|
|
160
165
|
}
|
|
161
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* Converts a stored default value string to a SQL DEFAULT literal,
|
|
169
|
+
* respecting the field's designType. Booleans become 0/1, numbers stay unquoted,
|
|
170
|
+
* strings are single-quote-escaped.
|
|
171
|
+
*/
|
|
162
172
|
function defaultValueToSqlLiteral(designType, value) {
|
|
163
173
|
switch (designType) {
|
|
164
174
|
case "boolean": return value === "true" || value === "1" ? "1" : "0";
|
|
@@ -179,27 +189,23 @@ const queryOpToSql = {
|
|
|
179
189
|
$lt: "<",
|
|
180
190
|
$lte: "<="
|
|
181
191
|
};
|
|
192
|
+
/**
|
|
193
|
+
* Renders an AtscriptQueryNode tree to raw SQL (no parameters -- for DDL use only).
|
|
194
|
+
*/
|
|
182
195
|
function queryNodeToSql(node, resolveFieldRef) {
|
|
183
|
-
if ("$and" in node)
|
|
184
|
-
|
|
185
|
-
return children.map((n) => queryNodeToSql(n, resolveFieldRef)).join(" AND ");
|
|
186
|
-
}
|
|
187
|
-
if ("$or" in node) {
|
|
188
|
-
const children = node.$or;
|
|
189
|
-
return `(${children.map((n) => queryNodeToSql(n, resolveFieldRef)).join(" OR ")})`;
|
|
190
|
-
}
|
|
196
|
+
if ("$and" in node) return node.$and.map((n) => queryNodeToSql(n, resolveFieldRef)).join(" AND ");
|
|
197
|
+
if ("$or" in node) return `(${node.$or.map((n) => queryNodeToSql(n, resolveFieldRef)).join(" OR ")})`;
|
|
191
198
|
if ("$not" in node) return `NOT (${queryNodeToSql(node.$not, resolveFieldRef)})`;
|
|
192
199
|
const comp = node;
|
|
193
200
|
const leftSql = resolveFieldRef(comp.left);
|
|
194
201
|
const sqlOp = queryOpToSql[comp.op] || "=";
|
|
195
202
|
if (comp.right && typeof comp.right === "object" && "field" in comp.right) return `${leftSql} ${sqlOp} ${resolveFieldRef(comp.right)}`;
|
|
196
|
-
if (comp.right === null || comp.right ===
|
|
203
|
+
if (comp.right === null || comp.right === void 0) return comp.op === "$ne" ? `${leftSql} IS NOT NULL` : `${leftSql} IS NULL`;
|
|
197
204
|
if (typeof comp.right === "string") return `${leftSql} ${sqlOp} '${comp.right.replace(/'/g, "''")}'`;
|
|
198
205
|
return `${leftSql} ${sqlOp} ${comp.right}`;
|
|
199
206
|
}
|
|
200
|
-
|
|
201
207
|
//#endregion
|
|
202
|
-
//#region
|
|
208
|
+
//#region src/agg.ts
|
|
203
209
|
const AGG_FN_SQL = {
|
|
204
210
|
sum: "SUM",
|
|
205
211
|
avg: "AVG",
|
|
@@ -210,17 +216,18 @@ const AGG_FN_SQL = {
|
|
|
210
216
|
function buildAggExpr(dialect, expr) {
|
|
211
217
|
const fn = AGG_FN_SQL[expr.$fn] ?? expr.$fn.toUpperCase();
|
|
212
218
|
const alias = dialect.quoteIdentifier(resolveAlias(expr));
|
|
213
|
-
|
|
214
|
-
return `${fn}(${field}) AS ${alias}`;
|
|
219
|
+
return `${fn}(${expr.$field === "*" ? "*" : dialect.quoteIdentifier(expr.$field)}) AS ${alias}`;
|
|
215
220
|
}
|
|
221
|
+
/**
|
|
222
|
+
* Builds a SELECT ... GROUP BY statement with aggregate functions.
|
|
223
|
+
*/
|
|
216
224
|
function buildAggregateSelect(dialect, table, where, controls) {
|
|
217
225
|
const selectParts = [];
|
|
218
226
|
const plainFields = controls.$select?.asArray;
|
|
219
227
|
if (plainFields) for (const f of plainFields) selectParts.push(dialect.quoteIdentifier(f));
|
|
220
228
|
const aggregates = controls.$select?.aggregates;
|
|
221
229
|
if (aggregates) for (const expr of aggregates) selectParts.push(buildAggExpr(dialect, expr));
|
|
222
|
-
|
|
223
|
-
let sql = `SELECT ${cols} FROM ${dialect.quoteTable(table)} WHERE ${where.sql}`;
|
|
230
|
+
let sql = `SELECT ${selectParts.length > 0 ? selectParts.join(", ") : "*"} FROM ${dialect.quoteTable(table)} WHERE ${where.sql}`;
|
|
224
231
|
const params = [...where.params];
|
|
225
232
|
const groupBy = controls.$groupBy;
|
|
226
233
|
if (groupBy?.length) {
|
|
@@ -239,12 +246,12 @@ function buildAggregateSelect(dialect, table, where, controls) {
|
|
|
239
246
|
for (const [col, dir] of Object.entries(controls.$sort)) orderParts.push(`${dialect.quoteIdentifier(col)} ${dir === -1 ? "DESC" : "ASC"}`);
|
|
240
247
|
if (orderParts.length > 0) sql += ` ORDER BY ${orderParts.join(", ")}`;
|
|
241
248
|
}
|
|
242
|
-
if (controls.$limit !==
|
|
249
|
+
if (controls.$limit !== void 0) {
|
|
243
250
|
sql += ` LIMIT ?`;
|
|
244
251
|
params.push(controls.$limit);
|
|
245
252
|
}
|
|
246
|
-
if (controls.$skip !==
|
|
247
|
-
if (controls.$limit ===
|
|
253
|
+
if (controls.$skip !== void 0) {
|
|
254
|
+
if (controls.$limit === void 0) sql += ` LIMIT ${dialect.unlimitedLimit}`;
|
|
248
255
|
sql += ` OFFSET ?`;
|
|
249
256
|
params.push(controls.$skip);
|
|
250
257
|
}
|
|
@@ -253,25 +260,27 @@ function buildAggregateSelect(dialect, table, where, controls) {
|
|
|
253
260
|
params
|
|
254
261
|
});
|
|
255
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* Builds a COUNT query for the number of distinct groups.
|
|
265
|
+
* Returns `{ count: N }` when executed.
|
|
266
|
+
*/
|
|
256
267
|
function buildAggregateCount(dialect, table, where, controls) {
|
|
257
268
|
const groupFields = controls.$groupBy;
|
|
258
|
-
if (!groupFields?.length) {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
params: where.params
|
|
263
|
-
});
|
|
264
|
-
}
|
|
269
|
+
if (!groupFields?.length) return finalizeParams(dialect, {
|
|
270
|
+
sql: `SELECT COUNT(*) AS ${dialect.quoteIdentifier("count")} FROM ${dialect.quoteTable(table)} WHERE ${where.sql}`,
|
|
271
|
+
params: where.params
|
|
272
|
+
});
|
|
265
273
|
const groupCols = groupFields.map((f) => dialect.quoteIdentifier(f)).join(", ");
|
|
266
|
-
const sql = `SELECT COUNT(*) AS ${dialect.quoteIdentifier("count")} FROM (SELECT 1 FROM ${dialect.quoteTable(table)} WHERE ${where.sql} GROUP BY ${groupCols}) AS ${dialect.quoteIdentifier("_groups")}`;
|
|
267
274
|
return finalizeParams(dialect, {
|
|
268
|
-
sql
|
|
275
|
+
sql: `SELECT COUNT(*) AS ${dialect.quoteIdentifier("count")} FROM (SELECT 1 FROM ${dialect.quoteTable(table)} WHERE ${where.sql} GROUP BY ${groupCols}) AS ${dialect.quoteIdentifier("_groups")}`,
|
|
269
276
|
params: where.params
|
|
270
277
|
});
|
|
271
278
|
}
|
|
272
|
-
|
|
273
279
|
//#endregion
|
|
274
|
-
//#region
|
|
280
|
+
//#region src/sql-builder.ts
|
|
281
|
+
/**
|
|
282
|
+
* Builds an INSERT statement.
|
|
283
|
+
*/
|
|
275
284
|
function buildInsert(dialect, table, data) {
|
|
276
285
|
const keys = Object.keys(data);
|
|
277
286
|
const cols = keys.map((k) => dialect.quoteIdentifier(k)).join(", ");
|
|
@@ -281,21 +290,23 @@ function buildInsert(dialect, table, data) {
|
|
|
281
290
|
params: keys.map((k) => dialect.toValue(data[k]))
|
|
282
291
|
});
|
|
283
292
|
}
|
|
293
|
+
/**
|
|
294
|
+
* Builds a SELECT statement with optional sort, limit, offset, projection.
|
|
295
|
+
*/
|
|
284
296
|
function buildSelect(dialect, table, where, controls) {
|
|
285
|
-
|
|
286
|
-
let sql = `SELECT ${cols} FROM ${dialect.quoteTable(table)} WHERE ${where.sql}`;
|
|
297
|
+
let sql = `SELECT ${buildProjection(dialect, controls?.$select)} FROM ${dialect.quoteTable(table)} WHERE ${where.sql}`;
|
|
287
298
|
const params = [...where.params];
|
|
288
299
|
if (controls?.$sort) {
|
|
289
300
|
const orderParts = [];
|
|
290
301
|
for (const [col, dir] of Object.entries(controls.$sort)) orderParts.push(`${dialect.quoteIdentifier(col)} ${dir === -1 ? "DESC" : "ASC"}`);
|
|
291
302
|
if (orderParts.length > 0) sql += ` ORDER BY ${orderParts.join(", ")}`;
|
|
292
303
|
}
|
|
293
|
-
if (controls?.$limit !==
|
|
304
|
+
if (controls?.$limit !== void 0) {
|
|
294
305
|
sql += ` LIMIT ?`;
|
|
295
306
|
params.push(controls.$limit);
|
|
296
307
|
}
|
|
297
|
-
if (controls?.$skip !==
|
|
298
|
-
if (controls.$limit ===
|
|
308
|
+
if (controls?.$skip !== void 0) {
|
|
309
|
+
if (controls.$limit === void 0) sql += ` LIMIT ${dialect.unlimitedLimit}`;
|
|
299
310
|
sql += ` OFFSET ?`;
|
|
300
311
|
params.push(controls.$skip);
|
|
301
312
|
}
|
|
@@ -304,28 +315,47 @@ function buildSelect(dialect, table, where, controls) {
|
|
|
304
315
|
params
|
|
305
316
|
});
|
|
306
317
|
}
|
|
307
|
-
|
|
318
|
+
/**
|
|
319
|
+
* Builds an UPDATE ... SET ... WHERE statement with optional LIMIT.
|
|
320
|
+
*/
|
|
321
|
+
function buildUpdate(dialect, table, data, where, limit, ops) {
|
|
308
322
|
const setClauses = [];
|
|
309
323
|
const params = [];
|
|
310
324
|
for (const [key, value] of Object.entries(data)) {
|
|
311
325
|
setClauses.push(`${dialect.quoteIdentifier(key)} = ?`);
|
|
312
326
|
params.push(dialect.toValue(value));
|
|
313
327
|
}
|
|
328
|
+
if (ops?.inc) for (const key in ops.inc) {
|
|
329
|
+
const col = dialect.quoteIdentifier(key);
|
|
330
|
+
setClauses.push(`${col} = ${col} + ?`);
|
|
331
|
+
params.push(ops.inc[key]);
|
|
332
|
+
}
|
|
333
|
+
if (ops?.mul) for (const key in ops.mul) {
|
|
334
|
+
const col = dialect.quoteIdentifier(key);
|
|
335
|
+
setClauses.push(`${col} = ${col} * ?`);
|
|
336
|
+
params.push(ops.mul[key]);
|
|
337
|
+
}
|
|
314
338
|
let sql = `UPDATE ${dialect.quoteTable(table)} SET ${setClauses.join(", ")} WHERE ${where.sql}`;
|
|
315
|
-
if (limit !==
|
|
339
|
+
if (limit !== void 0) sql += ` LIMIT ${limit}`;
|
|
316
340
|
return finalizeParams(dialect, {
|
|
317
341
|
sql,
|
|
318
342
|
params: [...params, ...where.params]
|
|
319
343
|
});
|
|
320
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* Builds a DELETE ... WHERE statement with optional LIMIT.
|
|
347
|
+
*/
|
|
321
348
|
function buildDelete(dialect, table, where, limit) {
|
|
322
349
|
let sql = `DELETE FROM ${dialect.quoteTable(table)} WHERE ${where.sql}`;
|
|
323
|
-
if (limit !==
|
|
350
|
+
if (limit !== void 0) sql += ` LIMIT ${limit}`;
|
|
324
351
|
return finalizeParams(dialect, {
|
|
325
352
|
sql,
|
|
326
353
|
params: where.params
|
|
327
354
|
});
|
|
328
355
|
}
|
|
356
|
+
/**
|
|
357
|
+
* Builds a column projection (SELECT clause fields).
|
|
358
|
+
*/
|
|
329
359
|
function buildProjection(dialect, select) {
|
|
330
360
|
const fields = select?.asArray;
|
|
331
361
|
if (!fields) return "*";
|
|
@@ -336,11 +366,13 @@ function buildProjection(dialect, select) {
|
|
|
336
366
|
}
|
|
337
367
|
return sql || "*";
|
|
338
368
|
}
|
|
339
|
-
/** Builds the SQL expression for a single aggregate column. */
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
return `${fn}(${arg})`;
|
|
369
|
+
/** Builds the SQL expression for a single aggregate column. */
|
|
370
|
+
function buildAggColExpr(dialect, c) {
|
|
371
|
+
return `${AGG_FN_SQL[c.aggFn] ?? c.aggFn.toUpperCase()}(${c.aggField === "*" ? "*" : `${dialect.quoteIdentifier(c.sourceTable)}.${dialect.quoteIdentifier(c.sourceColumn)}`})`;
|
|
343
372
|
}
|
|
373
|
+
/**
|
|
374
|
+
* Builds a CREATE VIEW statement from a view plan and column mappings.
|
|
375
|
+
*/
|
|
344
376
|
function buildCreateView(dialect, viewName, plan, columns, resolveFieldRef) {
|
|
345
377
|
const selectCols = columns.map((c) => {
|
|
346
378
|
if (c.aggFn) return `${buildAggColExpr(dialect, c)} AS ${dialect.quoteIdentifier(c.viewColumn)}`;
|
|
@@ -355,15 +387,14 @@ function buildCreateView(dialect, viewName, plan, columns, resolveFieldRef) {
|
|
|
355
387
|
const whereClause = queryNodeToSql(plan.filter, resolveFieldRef);
|
|
356
388
|
sql += ` WHERE ${whereClause}`;
|
|
357
389
|
}
|
|
358
|
-
|
|
359
|
-
if (hasAggregates) {
|
|
390
|
+
if (columns.some((c) => c.aggFn)) {
|
|
360
391
|
const dimensionCols = columns.filter((c) => !c.aggFn);
|
|
361
392
|
if (dimensionCols.length > 0) {
|
|
362
393
|
const groupByCols = dimensionCols.map((c) => `${dialect.quoteIdentifier(c.sourceTable)}.${dialect.quoteIdentifier(c.sourceColumn)}`).join(", ");
|
|
363
394
|
sql += ` GROUP BY ${groupByCols}`;
|
|
364
395
|
}
|
|
365
396
|
if (plan.having) {
|
|
366
|
-
const columnMap = new Map();
|
|
397
|
+
const columnMap = /* @__PURE__ */ new Map();
|
|
367
398
|
for (const c of columns) columnMap.set(c.viewColumn, c);
|
|
368
399
|
const havingResolver = (ref) => {
|
|
369
400
|
if (!ref.type) {
|
|
@@ -379,6 +410,5 @@ function buildCreateView(dialect, viewName, plan, columns, resolveFieldRef) {
|
|
|
379
410
|
}
|
|
380
411
|
return sql;
|
|
381
412
|
}
|
|
382
|
-
|
|
383
413
|
//#endregion
|
|
384
|
-
export { AGG_FN_SQL, EMPTY_AND, EMPTY_OR, buildAggregateCount, buildAggregateSelect, buildCreateView, buildDelete, buildInsert, buildProjection, buildSelect, buildUpdate, buildWhere, createFilterVisitor, defaultValueForType, defaultValueToSqlLiteral, finalizeParams, queryNodeToSql, queryOpToSql, refActionToSql, sqlStringLiteral, toSqlValue };
|
|
414
|
+
export { AGG_FN_SQL, EMPTY_AND, EMPTY_OR, buildAggregateCount, buildAggregateSelect, buildCreateView, buildDelete, buildInsert, buildProjection, buildSelect, buildUpdate, buildWhere, createFilterVisitor, defaultValueForType, defaultValueToSqlLiteral, finalizeParams, queryNodeToSql, queryOpToSql, refActionToSql, sqlStringLiteral, toSqlValue };
|
package/package.json
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/db-sql-tools",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.40",
|
|
4
4
|
"description": "Shared SQL builder utilities for @atscript database adapters.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"atscript",
|
|
7
7
|
"database",
|
|
8
8
|
"sql"
|
|
9
9
|
],
|
|
10
|
-
"homepage": "https://github.com/moostjs/atscript/tree/main/packages/db-sql-tools#readme",
|
|
10
|
+
"homepage": "https://github.com/moostjs/atscript-db/tree/main/packages/db-sql-tools#readme",
|
|
11
11
|
"bugs": {
|
|
12
|
-
"url": "https://github.com/moostjs/atscript/issues"
|
|
12
|
+
"url": "https://github.com/moostjs/atscript-db/issues"
|
|
13
13
|
},
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"author": "Artem Maltsev",
|
|
16
16
|
"repository": {
|
|
17
17
|
"type": "git",
|
|
18
|
-
"url": "git+https://github.com/moostjs/atscript.git",
|
|
18
|
+
"url": "git+https://github.com/moostjs/atscript-db.git",
|
|
19
19
|
"directory": "packages/db-sql-tools"
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
@@ -23,24 +23,31 @@
|
|
|
23
23
|
],
|
|
24
24
|
"type": "module",
|
|
25
25
|
"main": "dist/index.mjs",
|
|
26
|
-
"
|
|
26
|
+
"module": "./dist/index.mjs",
|
|
27
|
+
"types": "dist/index.d.mts",
|
|
27
28
|
"exports": {
|
|
28
29
|
".": {
|
|
29
|
-
"types": "./dist/index.d.
|
|
30
|
+
"types": "./dist/index.d.mts",
|
|
30
31
|
"import": "./dist/index.mjs",
|
|
31
32
|
"require": "./dist/index.cjs"
|
|
32
33
|
},
|
|
33
34
|
"./package.json": "./package.json"
|
|
34
35
|
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
35
39
|
"devDependencies": {
|
|
36
|
-
"
|
|
40
|
+
"@uniqu/core": "^0.1.2",
|
|
41
|
+
"unplugin-atscript": "^0.1.39"
|
|
37
42
|
},
|
|
38
43
|
"peerDependencies": {
|
|
39
44
|
"@uniqu/core": "^0.1.2",
|
|
40
|
-
"@atscript/db": "^0.1.
|
|
45
|
+
"@atscript/db": "^0.1.40"
|
|
41
46
|
},
|
|
42
47
|
"scripts": {
|
|
43
|
-
"
|
|
44
|
-
"
|
|
48
|
+
"build": "vp pack",
|
|
49
|
+
"dev": "vp pack --watch",
|
|
50
|
+
"test": "vp test",
|
|
51
|
+
"check": "vp check"
|
|
45
52
|
}
|
|
46
53
|
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025-present Artem Maltsev
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|