@mindstudio-ai/agent 0.1.9 → 0.1.11
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 +8 -2
- package/dist/cli.js +302 -405
- package/dist/index.d.ts +93 -342
- package/dist/index.js +294 -401
- package/dist/index.js.map +1 -1
- package/dist/postinstall.js +302 -405
- package/package.json +1 -1
package/dist/postinstall.js
CHANGED
|
@@ -1419,6 +1419,19 @@ var init_auth = __esm({
|
|
|
1419
1419
|
});
|
|
1420
1420
|
|
|
1421
1421
|
// src/db/sql.ts
|
|
1422
|
+
function serializeParam(val) {
|
|
1423
|
+
if (val === null || val === void 0) return null;
|
|
1424
|
+
if (typeof val === "boolean") return val ? 1 : 0;
|
|
1425
|
+
if (typeof val === "number" || typeof val === "string") return val;
|
|
1426
|
+
return JSON.stringify(val);
|
|
1427
|
+
}
|
|
1428
|
+
function serializeColumnParam(val, columnName, columns) {
|
|
1429
|
+
const col = columns.find((c) => c.name === columnName);
|
|
1430
|
+
if (col?.type === "user" && typeof val === "string") {
|
|
1431
|
+
return `@@user@@${val}`;
|
|
1432
|
+
}
|
|
1433
|
+
return serializeParam(val);
|
|
1434
|
+
}
|
|
1422
1435
|
function escapeValue(val) {
|
|
1423
1436
|
if (val === null || val === void 0) return "NULL";
|
|
1424
1437
|
if (typeof val === "boolean") return val ? "1" : "0";
|
|
@@ -1427,13 +1440,6 @@ function escapeValue(val) {
|
|
|
1427
1440
|
const json = JSON.stringify(val);
|
|
1428
1441
|
return `'${json.replace(/'/g, "''")}'`;
|
|
1429
1442
|
}
|
|
1430
|
-
function serializeValue(val, columnName, columns) {
|
|
1431
|
-
const col = columns.find((c) => c.name === columnName);
|
|
1432
|
-
if (col?.type === "user" && typeof val === "string") {
|
|
1433
|
-
return escapeValue(`@@user@@${val}`);
|
|
1434
|
-
}
|
|
1435
|
-
return escapeValue(val);
|
|
1436
|
-
}
|
|
1437
1443
|
function deserializeRow(row, columns) {
|
|
1438
1444
|
const result = {};
|
|
1439
1445
|
for (const [key, value] of Object.entries(row)) {
|
|
@@ -1454,37 +1460,54 @@ function deserializeRow(row, columns) {
|
|
|
1454
1460
|
}
|
|
1455
1461
|
function buildSelect(table, options = {}) {
|
|
1456
1462
|
let sql = `SELECT * FROM ${table}`;
|
|
1457
|
-
|
|
1463
|
+
const params = [];
|
|
1464
|
+
if (options.where) {
|
|
1465
|
+
sql += ` WHERE ${options.where}`;
|
|
1466
|
+
if (options.whereParams) params.push(...options.whereParams);
|
|
1467
|
+
}
|
|
1458
1468
|
if (options.orderBy) sql += ` ORDER BY ${options.orderBy}${options.desc ? " DESC" : " ASC"}`;
|
|
1459
1469
|
if (options.limit != null) sql += ` LIMIT ${options.limit}`;
|
|
1460
1470
|
if (options.offset != null) sql += ` OFFSET ${options.offset}`;
|
|
1461
|
-
return sql;
|
|
1471
|
+
return { sql, params: params.length > 0 ? params : void 0 };
|
|
1462
1472
|
}
|
|
1463
|
-
function buildCount(table, where) {
|
|
1473
|
+
function buildCount(table, where, whereParams) {
|
|
1464
1474
|
let sql = `SELECT COUNT(*) as count FROM ${table}`;
|
|
1465
1475
|
if (where) sql += ` WHERE ${where}`;
|
|
1466
|
-
return sql;
|
|
1476
|
+
return { sql, params: whereParams?.length ? whereParams : void 0 };
|
|
1467
1477
|
}
|
|
1468
|
-
function buildExists(table, where, negate) {
|
|
1478
|
+
function buildExists(table, where, whereParams, negate) {
|
|
1469
1479
|
const inner = where ? `SELECT 1 FROM ${table} WHERE ${where}` : `SELECT 1 FROM ${table}`;
|
|
1470
1480
|
const fn = negate ? "NOT EXISTS" : "EXISTS";
|
|
1471
|
-
return `SELECT ${fn}(${inner}) as result
|
|
1481
|
+
return { sql: `SELECT ${fn}(${inner}) as result`, params: whereParams?.length ? whereParams : void 0 };
|
|
1472
1482
|
}
|
|
1473
1483
|
function buildInsert(table, data, columns) {
|
|
1474
1484
|
const filtered = stripSystemColumns(data);
|
|
1475
1485
|
const keys = Object.keys(filtered);
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1486
|
+
const placeholders = keys.map(() => "?").join(", ");
|
|
1487
|
+
const params = keys.map((k) => serializeColumnParam(filtered[k], k, columns));
|
|
1488
|
+
return {
|
|
1489
|
+
sql: `INSERT INTO ${table} (${keys.join(", ")}) VALUES (${placeholders}) RETURNING *`,
|
|
1490
|
+
params
|
|
1491
|
+
};
|
|
1478
1492
|
}
|
|
1479
1493
|
function buildUpdate(table, id, data, columns) {
|
|
1480
1494
|
const filtered = stripSystemColumns(data);
|
|
1481
|
-
const
|
|
1482
|
-
|
|
1495
|
+
const keys = Object.keys(filtered);
|
|
1496
|
+
const assignments = keys.map((k) => `${k} = ?`).join(", ");
|
|
1497
|
+
const params = [
|
|
1498
|
+
...keys.map((k) => serializeColumnParam(filtered[k], k, columns)),
|
|
1499
|
+
id
|
|
1500
|
+
// for WHERE id = ?
|
|
1501
|
+
];
|
|
1502
|
+
return {
|
|
1503
|
+
sql: `UPDATE ${table} SET ${assignments} WHERE id = ? RETURNING *`,
|
|
1504
|
+
params
|
|
1505
|
+
};
|
|
1483
1506
|
}
|
|
1484
|
-
function buildDelete(table, where) {
|
|
1507
|
+
function buildDelete(table, where, whereParams) {
|
|
1485
1508
|
let sql = `DELETE FROM ${table}`;
|
|
1486
1509
|
if (where) sql += ` WHERE ${where}`;
|
|
1487
|
-
return sql;
|
|
1510
|
+
return { sql, params: whereParams?.length ? whereParams : void 0 };
|
|
1488
1511
|
}
|
|
1489
1512
|
function stripSystemColumns(data) {
|
|
1490
1513
|
const result = {};
|
|
@@ -1945,8 +1968,7 @@ var init_predicate = __esm({
|
|
|
1945
1968
|
return PARSE_FAILED;
|
|
1946
1969
|
}
|
|
1947
1970
|
/**
|
|
1948
|
-
* Attempt to resolve a closure variable
|
|
1949
|
-
* with a recording Proxy and inspecting what values it compares against.
|
|
1971
|
+
* Attempt to resolve a closure variable's value.
|
|
1950
1972
|
*
|
|
1951
1973
|
* This handles the common pattern:
|
|
1952
1974
|
* ```ts
|
|
@@ -1954,40 +1976,28 @@ var init_predicate = __esm({
|
|
|
1954
1976
|
* orders.filter(o => o.requestedBy === userId)
|
|
1955
1977
|
* ```
|
|
1956
1978
|
*
|
|
1957
|
-
*
|
|
1958
|
-
*
|
|
1959
|
-
*
|
|
1960
|
-
*
|
|
1979
|
+
* Closure variable resolution is fundamentally limited in JavaScript —
|
|
1980
|
+
* we can't access another function's closure scope from outside without
|
|
1981
|
+
* `eval`. The `===` operator can't be overridden via Proxy or
|
|
1982
|
+
* Symbol.toPrimitive, so we can't intercept comparisons.
|
|
1983
|
+
*
|
|
1984
|
+
* For now, this falls back to JS execution. The predicate still works
|
|
1985
|
+
* correctly — it just scans all rows instead of generating SQL.
|
|
1986
|
+
* This is the most common reason for JS fallback in practice, since
|
|
1987
|
+
* almost every real-world filter references a variable like `userId`.
|
|
1988
|
+
*
|
|
1989
|
+
* A future improvement could accept an explicit `vars` argument:
|
|
1990
|
+
* ```ts
|
|
1991
|
+
* orders.filter(o => o.requestedBy === $userId, { $userId: auth.userId })
|
|
1992
|
+
* ```
|
|
1961
1993
|
*/
|
|
1962
1994
|
resolveClosureVariable() {
|
|
1963
|
-
|
|
1964
|
-
let closureExpr = identToken.value;
|
|
1995
|
+
this.advance();
|
|
1965
1996
|
while (this.match("dot") && this.tokens[this.pos + 1]?.type === "identifier") {
|
|
1966
1997
|
this.advance();
|
|
1967
|
-
|
|
1968
|
-
}
|
|
1969
|
-
try {
|
|
1970
|
-
const MARKER = /* @__PURE__ */ Symbol("field_access_marker");
|
|
1971
|
-
const accessed = [];
|
|
1972
|
-
const proxy = new Proxy(
|
|
1973
|
-
{},
|
|
1974
|
-
{
|
|
1975
|
-
get(_, prop) {
|
|
1976
|
-
accessed.push(prop);
|
|
1977
|
-
return new Proxy(() => MARKER, {
|
|
1978
|
-
get(_2, nestedProp) {
|
|
1979
|
-
accessed.push(nestedProp);
|
|
1980
|
-
return MARKER;
|
|
1981
|
-
}
|
|
1982
|
-
});
|
|
1983
|
-
}
|
|
1984
|
-
}
|
|
1985
|
-
);
|
|
1986
|
-
void proxy;
|
|
1987
|
-
return PARSE_FAILED;
|
|
1988
|
-
} catch {
|
|
1989
|
-
return PARSE_FAILED;
|
|
1998
|
+
this.advance();
|
|
1990
1999
|
}
|
|
2000
|
+
return PARSE_FAILED;
|
|
1991
2001
|
}
|
|
1992
2002
|
/**
|
|
1993
2003
|
* Look ahead to check if the next tokens form `.includes(`.
|
|
@@ -2026,17 +2036,11 @@ var init_query = __esm({
|
|
|
2026
2036
|
init_predicate();
|
|
2027
2037
|
init_sql();
|
|
2028
2038
|
Query = class _Query {
|
|
2029
|
-
/** @internal Accumulated predicate functions to filter by. */
|
|
2030
2039
|
_predicates;
|
|
2031
|
-
/** @internal The field accessor for sorting, if set. */
|
|
2032
2040
|
_sortAccessor;
|
|
2033
|
-
/** @internal Whether the sort order is reversed (DESC). */
|
|
2034
2041
|
_reversed;
|
|
2035
|
-
/** @internal Maximum number of results (SQL LIMIT). */
|
|
2036
2042
|
_limit;
|
|
2037
|
-
/** @internal Number of results to skip (SQL OFFSET). */
|
|
2038
2043
|
_offset;
|
|
2039
|
-
/** @internal Binding to the database execution layer. */
|
|
2040
2044
|
_config;
|
|
2041
2045
|
constructor(config, options) {
|
|
2042
2046
|
this._config = config;
|
|
@@ -2046,10 +2050,6 @@ var init_query = __esm({
|
|
|
2046
2050
|
this._limit = options?.limit;
|
|
2047
2051
|
this._offset = options?.offset;
|
|
2048
2052
|
}
|
|
2049
|
-
/**
|
|
2050
|
-
* Create a clone of this query with some options overridden.
|
|
2051
|
-
* Used internally by chain methods to maintain immutability.
|
|
2052
|
-
*/
|
|
2053
2053
|
_clone(overrides) {
|
|
2054
2054
|
return new _Query(this._config, {
|
|
2055
2055
|
predicates: overrides.predicates ?? this._predicates,
|
|
@@ -2060,126 +2060,73 @@ var init_query = __esm({
|
|
|
2060
2060
|
});
|
|
2061
2061
|
}
|
|
2062
2062
|
// -------------------------------------------------------------------------
|
|
2063
|
-
// Chain methods
|
|
2063
|
+
// Chain methods
|
|
2064
2064
|
// -------------------------------------------------------------------------
|
|
2065
|
-
/**
|
|
2066
|
-
* Add a filter predicate. Multiple filters are ANDed together.
|
|
2067
|
-
*
|
|
2068
|
-
* @example
|
|
2069
|
-
* ```ts
|
|
2070
|
-
* const active = Orders.filter(o => o.status === 'active');
|
|
2071
|
-
* const expensive = active.filter(o => o.amount > 5000);
|
|
2072
|
-
* // WHERE status = 'active' AND amount > 5000
|
|
2073
|
-
* ```
|
|
2074
|
-
*/
|
|
2075
2065
|
filter(predicate) {
|
|
2076
|
-
return this._clone({
|
|
2077
|
-
predicates: [...this._predicates, predicate]
|
|
2078
|
-
});
|
|
2066
|
+
return this._clone({ predicates: [...this._predicates, predicate] });
|
|
2079
2067
|
}
|
|
2080
|
-
/**
|
|
2081
|
-
* Sort results by a field (ascending by default).
|
|
2082
|
-
* Use `.reverse()` after `.sortBy()` for descending order.
|
|
2083
|
-
*
|
|
2084
|
-
* @example
|
|
2085
|
-
* ```ts
|
|
2086
|
-
* const newest = Orders.sortBy(o => o.createdAt).reverse();
|
|
2087
|
-
* ```
|
|
2088
|
-
*/
|
|
2089
2068
|
sortBy(accessor) {
|
|
2090
2069
|
return this._clone({ sortAccessor: accessor });
|
|
2091
2070
|
}
|
|
2092
|
-
/**
|
|
2093
|
-
* Reverse the current sort order. If no sort is set, this has no effect.
|
|
2094
|
-
*/
|
|
2095
2071
|
reverse() {
|
|
2096
2072
|
return this._clone({ reversed: !this._reversed });
|
|
2097
2073
|
}
|
|
2098
|
-
/**
|
|
2099
|
-
* Limit the number of results returned.
|
|
2100
|
-
*
|
|
2101
|
-
* @example
|
|
2102
|
-
* ```ts
|
|
2103
|
-
* const top10 = Orders.sortBy(o => o.amount).reverse().take(10);
|
|
2104
|
-
* ```
|
|
2105
|
-
*/
|
|
2106
2074
|
take(n) {
|
|
2107
2075
|
return this._clone({ limit: n });
|
|
2108
2076
|
}
|
|
2109
|
-
/**
|
|
2110
|
-
* Skip the first n results. Use with `.take()` for pagination.
|
|
2111
|
-
*
|
|
2112
|
-
* @example
|
|
2113
|
-
* ```ts
|
|
2114
|
-
* const page2 = Orders.sortBy(o => o.createdAt).skip(50).take(50);
|
|
2115
|
-
* ```
|
|
2116
|
-
*/
|
|
2117
2077
|
skip(n) {
|
|
2118
2078
|
return this._clone({ offset: n });
|
|
2119
2079
|
}
|
|
2120
2080
|
// -------------------------------------------------------------------------
|
|
2121
|
-
// Terminal methods
|
|
2081
|
+
// Terminal methods
|
|
2122
2082
|
// -------------------------------------------------------------------------
|
|
2123
|
-
/**
|
|
2124
|
-
* Return the first matching row, or null if no rows match.
|
|
2125
|
-
* Applies the current sort order before taking the first result.
|
|
2126
|
-
*/
|
|
2127
2083
|
async first() {
|
|
2128
2084
|
const rows = await this._clone({ limit: 1 })._execute();
|
|
2129
2085
|
return rows[0] ?? null;
|
|
2130
2086
|
}
|
|
2131
|
-
/**
|
|
2132
|
-
* Return the last matching row (per current sort), or null.
|
|
2133
|
-
* Flips the sort direction and takes 1 row.
|
|
2134
|
-
*/
|
|
2135
2087
|
async last() {
|
|
2136
2088
|
const rows = await this._clone({ limit: 1, reversed: !this._reversed })._execute();
|
|
2137
2089
|
return rows[0] ?? null;
|
|
2138
2090
|
}
|
|
2139
|
-
/**
|
|
2140
|
-
* Count matching rows. Returns a number, not the rows themselves.
|
|
2141
|
-
* Executes as `SELECT COUNT(*)` when predicates compile to SQL.
|
|
2142
|
-
*/
|
|
2143
2091
|
async count() {
|
|
2144
2092
|
const compiled = this._compilePredicates();
|
|
2145
2093
|
if (compiled.allSql) {
|
|
2146
|
-
const
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2094
|
+
const query = buildCount(
|
|
2095
|
+
this._config.tableName,
|
|
2096
|
+
compiled.sqlWhere || void 0
|
|
2097
|
+
);
|
|
2098
|
+
const results = await this._config.executeBatch([query]);
|
|
2099
|
+
const row = results[0]?.rows[0];
|
|
2150
2100
|
return row?.count ?? 0;
|
|
2151
2101
|
}
|
|
2152
2102
|
const rows = await this._fetchAndFilterInJs(compiled);
|
|
2153
2103
|
return rows.length;
|
|
2154
2104
|
}
|
|
2155
|
-
/**
|
|
2156
|
-
* Check if any row matches the current filters. Short-circuits —
|
|
2157
|
-
* doesn't load all rows when using SQL.
|
|
2158
|
-
*/
|
|
2159
2105
|
async some() {
|
|
2160
2106
|
const compiled = this._compilePredicates();
|
|
2161
2107
|
if (compiled.allSql) {
|
|
2162
|
-
const
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2108
|
+
const query = buildExists(
|
|
2109
|
+
this._config.tableName,
|
|
2110
|
+
compiled.sqlWhere || void 0
|
|
2111
|
+
);
|
|
2112
|
+
const results = await this._config.executeBatch([query]);
|
|
2113
|
+
const row = results[0]?.rows[0];
|
|
2166
2114
|
return row?.result === 1;
|
|
2167
2115
|
}
|
|
2168
2116
|
const rows = await this._fetchAndFilterInJs(compiled);
|
|
2169
2117
|
return rows.length > 0;
|
|
2170
2118
|
}
|
|
2171
|
-
/**
|
|
2172
|
-
* Check if all rows match the current filters. Short-circuits on false.
|
|
2173
|
-
*
|
|
2174
|
-
* Implemented as NOT EXISTS(... WHERE NOT predicate) — returns true
|
|
2175
|
-
* if no rows fail the predicate.
|
|
2176
|
-
*/
|
|
2177
2119
|
async every() {
|
|
2178
2120
|
const compiled = this._compilePredicates();
|
|
2179
2121
|
if (compiled.allSql && compiled.sqlWhere) {
|
|
2180
|
-
const
|
|
2181
|
-
|
|
2182
|
-
|
|
2122
|
+
const query = buildExists(
|
|
2123
|
+
this._config.tableName,
|
|
2124
|
+
`NOT (${compiled.sqlWhere})`,
|
|
2125
|
+
void 0,
|
|
2126
|
+
true
|
|
2127
|
+
);
|
|
2128
|
+
const results = await this._config.executeBatch([query]);
|
|
2129
|
+
const row = results[0]?.rows[0];
|
|
2183
2130
|
return row?.result === 1;
|
|
2184
2131
|
}
|
|
2185
2132
|
if (this._predicates.length === 0) return true;
|
|
@@ -2188,24 +2135,12 @@ var init_query = __esm({
|
|
|
2188
2135
|
(row) => this._predicates.every((pred) => pred(row))
|
|
2189
2136
|
);
|
|
2190
2137
|
}
|
|
2191
|
-
/**
|
|
2192
|
-
* Return the row with the minimum value for the given field.
|
|
2193
|
-
* Executes as `ORDER BY field ASC LIMIT 1` in SQL.
|
|
2194
|
-
*/
|
|
2195
2138
|
async min(accessor) {
|
|
2196
2139
|
return this.sortBy(accessor).first();
|
|
2197
2140
|
}
|
|
2198
|
-
/**
|
|
2199
|
-
* Return the row with the maximum value for the given field.
|
|
2200
|
-
* Executes as `ORDER BY field DESC LIMIT 1` in SQL.
|
|
2201
|
-
*/
|
|
2202
2141
|
async max(accessor) {
|
|
2203
2142
|
return this.sortBy(accessor).reverse().first();
|
|
2204
2143
|
}
|
|
2205
|
-
/**
|
|
2206
|
-
* Group rows by a field value. Returns a Map.
|
|
2207
|
-
* Always executes in JS (no SQL equivalent for grouping into a Map).
|
|
2208
|
-
*/
|
|
2209
2144
|
async groupBy(accessor) {
|
|
2210
2145
|
const rows = await this._execute();
|
|
2211
2146
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -2221,40 +2156,97 @@ var init_query = __esm({
|
|
|
2221
2156
|
return map;
|
|
2222
2157
|
}
|
|
2223
2158
|
// -------------------------------------------------------------------------
|
|
2224
|
-
//
|
|
2159
|
+
// Batch compilation — used by db.batch() to extract SQL without executing
|
|
2225
2160
|
// -------------------------------------------------------------------------
|
|
2226
2161
|
/**
|
|
2227
|
-
*
|
|
2228
|
-
*
|
|
2162
|
+
* @internal Compile this query into a SqlQuery for batch execution.
|
|
2163
|
+
*
|
|
2164
|
+
* Returns the compiled SQL query (if all predicates compile to SQL),
|
|
2165
|
+
* or null (if JS fallback is needed). In the fallback case, a bare
|
|
2166
|
+
* `SELECT *` is returned as `fallbackQuery` so the batch can fetch
|
|
2167
|
+
* all rows and this query can filter them in JS post-fetch.
|
|
2168
|
+
*/
|
|
2169
|
+
_compile() {
|
|
2170
|
+
const compiled = this._compilePredicates();
|
|
2171
|
+
const sortField = this._sortAccessor ? extractFieldName(this._sortAccessor) : void 0;
|
|
2172
|
+
if (compiled.allSql) {
|
|
2173
|
+
const query = buildSelect(this._config.tableName, {
|
|
2174
|
+
where: compiled.sqlWhere || void 0,
|
|
2175
|
+
orderBy: sortField ?? void 0,
|
|
2176
|
+
desc: this._reversed,
|
|
2177
|
+
limit: this._limit,
|
|
2178
|
+
offset: this._offset
|
|
2179
|
+
});
|
|
2180
|
+
return { query, fallbackQuery: null, config: this._config };
|
|
2181
|
+
}
|
|
2182
|
+
const fallbackQuery = buildSelect(this._config.tableName);
|
|
2183
|
+
return {
|
|
2184
|
+
query: null,
|
|
2185
|
+
fallbackQuery,
|
|
2186
|
+
config: this._config,
|
|
2187
|
+
predicates: this._predicates,
|
|
2188
|
+
sortAccessor: this._sortAccessor,
|
|
2189
|
+
reversed: this._reversed,
|
|
2190
|
+
limit: this._limit,
|
|
2191
|
+
offset: this._offset
|
|
2192
|
+
};
|
|
2193
|
+
}
|
|
2194
|
+
/**
|
|
2195
|
+
* @internal Process raw SQL results into typed rows. Used by db.batch()
|
|
2196
|
+
* after executing the compiled query.
|
|
2197
|
+
*
|
|
2198
|
+
* For SQL-compiled queries: just deserialize the rows.
|
|
2199
|
+
* For JS-fallback queries: filter, sort, and slice in JS.
|
|
2229
2200
|
*/
|
|
2201
|
+
static _processResults(result, compiled) {
|
|
2202
|
+
const rows = result.rows.map(
|
|
2203
|
+
(row) => deserializeRow(
|
|
2204
|
+
row,
|
|
2205
|
+
compiled.config.columns
|
|
2206
|
+
)
|
|
2207
|
+
);
|
|
2208
|
+
if (compiled.query) return rows;
|
|
2209
|
+
let filtered = compiled.predicates ? rows.filter((row) => compiled.predicates.every((pred) => pred(row))) : rows;
|
|
2210
|
+
if (compiled.sortAccessor) {
|
|
2211
|
+
const accessor = compiled.sortAccessor;
|
|
2212
|
+
const reversed = compiled.reversed ?? false;
|
|
2213
|
+
filtered.sort((a, b) => {
|
|
2214
|
+
const aVal = accessor(a);
|
|
2215
|
+
const bVal = accessor(b);
|
|
2216
|
+
if (aVal < bVal) return reversed ? 1 : -1;
|
|
2217
|
+
if (aVal > bVal) return reversed ? -1 : 1;
|
|
2218
|
+
return 0;
|
|
2219
|
+
});
|
|
2220
|
+
}
|
|
2221
|
+
if (compiled.offset != null || compiled.limit != null) {
|
|
2222
|
+
const start = compiled.offset ?? 0;
|
|
2223
|
+
const end = compiled.limit != null ? start + compiled.limit : void 0;
|
|
2224
|
+
filtered = filtered.slice(start, end);
|
|
2225
|
+
}
|
|
2226
|
+
return filtered;
|
|
2227
|
+
}
|
|
2228
|
+
// -------------------------------------------------------------------------
|
|
2229
|
+
// PromiseLike
|
|
2230
|
+
// -------------------------------------------------------------------------
|
|
2230
2231
|
then(onfulfilled, onrejected) {
|
|
2231
2232
|
return this._execute().then(onfulfilled, onrejected);
|
|
2232
2233
|
}
|
|
2233
2234
|
// -------------------------------------------------------------------------
|
|
2234
2235
|
// Execution internals
|
|
2235
2236
|
// -------------------------------------------------------------------------
|
|
2236
|
-
/**
|
|
2237
|
-
* Execute the query and return typed result rows.
|
|
2238
|
-
*
|
|
2239
|
-
* This is the core execution method. It:
|
|
2240
|
-
* 1. Tries to compile all predicates to SQL
|
|
2241
|
-
* 2. If all compile → builds and executes a single SQL query
|
|
2242
|
-
* 3. If any fail → fetches all rows and processes in JS
|
|
2243
|
-
* 4. Deserializes rows (user prefix stripping, JSON parsing)
|
|
2244
|
-
*/
|
|
2245
2237
|
async _execute() {
|
|
2246
2238
|
const compiled = this._compilePredicates();
|
|
2247
2239
|
if (compiled.allSql) {
|
|
2248
2240
|
const sortField = this._sortAccessor ? extractFieldName(this._sortAccessor) : void 0;
|
|
2249
|
-
const
|
|
2241
|
+
const query = buildSelect(this._config.tableName, {
|
|
2250
2242
|
where: compiled.sqlWhere || void 0,
|
|
2251
2243
|
orderBy: sortField ?? void 0,
|
|
2252
2244
|
desc: this._reversed,
|
|
2253
2245
|
limit: this._limit,
|
|
2254
2246
|
offset: this._offset
|
|
2255
2247
|
});
|
|
2256
|
-
const
|
|
2257
|
-
return
|
|
2248
|
+
const results = await this._config.executeBatch([query]);
|
|
2249
|
+
return results[0].rows.map(
|
|
2258
2250
|
(row) => deserializeRow(
|
|
2259
2251
|
row,
|
|
2260
2252
|
this._config.columns
|
|
@@ -2279,14 +2271,6 @@ var init_query = __esm({
|
|
|
2279
2271
|
}
|
|
2280
2272
|
return rows;
|
|
2281
2273
|
}
|
|
2282
|
-
/**
|
|
2283
|
-
* Compile all accumulated predicates and determine the execution strategy.
|
|
2284
|
-
*
|
|
2285
|
-
* Returns an object with:
|
|
2286
|
-
* - `allSql`: whether all predicates compiled to SQL
|
|
2287
|
-
* - `sqlWhere`: combined WHERE clause (ANDed) if all compiled
|
|
2288
|
-
* - `compiled`: individual compilation results
|
|
2289
|
-
*/
|
|
2290
2274
|
_compilePredicates() {
|
|
2291
2275
|
if (this._predicates.length === 0) {
|
|
2292
2276
|
return { allSql: true, sqlWhere: "", compiled: [] };
|
|
@@ -2299,12 +2283,6 @@ var init_query = __esm({
|
|
|
2299
2283
|
}
|
|
2300
2284
|
return { allSql, sqlWhere, compiled };
|
|
2301
2285
|
}
|
|
2302
|
-
/**
|
|
2303
|
-
* Fetch all rows from the table and apply JS predicates.
|
|
2304
|
-
* This is the fallback path when SQL compilation fails.
|
|
2305
|
-
*
|
|
2306
|
-
* Logs a warning to stderr so developers know they're on the slow path.
|
|
2307
|
-
*/
|
|
2308
2286
|
async _fetchAndFilterInJs(compiled) {
|
|
2309
2287
|
const allRows = await this._fetchAllRows();
|
|
2310
2288
|
if (compiled.compiled.some((c) => c.type === "js")) {
|
|
@@ -2316,14 +2294,10 @@ var init_query = __esm({
|
|
|
2316
2294
|
(row) => this._predicates.every((pred) => pred(row))
|
|
2317
2295
|
);
|
|
2318
2296
|
}
|
|
2319
|
-
/**
|
|
2320
|
-
* Fetch all rows from the table (SELECT * with no WHERE).
|
|
2321
|
-
* Used by the JS fallback path.
|
|
2322
|
-
*/
|
|
2323
2297
|
async _fetchAllRows() {
|
|
2324
|
-
const
|
|
2325
|
-
const
|
|
2326
|
-
return
|
|
2298
|
+
const query = buildSelect(this._config.tableName);
|
|
2299
|
+
const results = await this._config.executeBatch([query]);
|
|
2300
|
+
return results[0].rows.map(
|
|
2327
2301
|
(row) => deserializeRow(row, this._config.columns)
|
|
2328
2302
|
);
|
|
2329
2303
|
}
|
|
@@ -2340,300 +2314,149 @@ var init_table = __esm({
|
|
|
2340
2314
|
init_predicate();
|
|
2341
2315
|
init_sql();
|
|
2342
2316
|
Table = class {
|
|
2343
|
-
/** @internal
|
|
2317
|
+
/** @internal */
|
|
2344
2318
|
_config;
|
|
2345
2319
|
constructor(config) {
|
|
2346
2320
|
this._config = config;
|
|
2347
2321
|
}
|
|
2348
2322
|
// -------------------------------------------------------------------------
|
|
2349
|
-
// Reads — direct
|
|
2323
|
+
// Reads — direct
|
|
2350
2324
|
// -------------------------------------------------------------------------
|
|
2351
|
-
/**
|
|
2352
|
-
* Get a single row by ID. Returns null if not found.
|
|
2353
|
-
*
|
|
2354
|
-
* @example
|
|
2355
|
-
* ```ts
|
|
2356
|
-
* const order = await Orders.get('abc-123');
|
|
2357
|
-
* if (order) console.log(order.status);
|
|
2358
|
-
* ```
|
|
2359
|
-
*/
|
|
2360
2325
|
async get(id) {
|
|
2361
|
-
const
|
|
2362
|
-
where: `id =
|
|
2326
|
+
const query = buildSelect(this._config.tableName, {
|
|
2327
|
+
where: `id = ?`,
|
|
2328
|
+
whereParams: [id],
|
|
2363
2329
|
limit: 1
|
|
2364
2330
|
});
|
|
2365
|
-
const
|
|
2366
|
-
if (
|
|
2331
|
+
const results = await this._config.executeBatch([query]);
|
|
2332
|
+
if (results[0].rows.length === 0) return null;
|
|
2367
2333
|
return deserializeRow(
|
|
2368
|
-
|
|
2334
|
+
results[0].rows[0],
|
|
2369
2335
|
this._config.columns
|
|
2370
2336
|
);
|
|
2371
2337
|
}
|
|
2372
|
-
/**
|
|
2373
|
-
* Find the first row matching a predicate. Returns null if none match.
|
|
2374
|
-
*
|
|
2375
|
-
* @example
|
|
2376
|
-
* ```ts
|
|
2377
|
-
* const activeOrder = await Orders.findOne(o => o.status === 'active');
|
|
2378
|
-
* ```
|
|
2379
|
-
*/
|
|
2380
2338
|
async findOne(predicate) {
|
|
2381
2339
|
return this.filter(predicate).first();
|
|
2382
2340
|
}
|
|
2383
|
-
/**
|
|
2384
|
-
* Count rows, optionally filtered by a predicate.
|
|
2385
|
-
*
|
|
2386
|
-
* @example
|
|
2387
|
-
* ```ts
|
|
2388
|
-
* const total = await Orders.count();
|
|
2389
|
-
* const pending = await Orders.count(o => o.status === 'pending');
|
|
2390
|
-
* ```
|
|
2391
|
-
*/
|
|
2392
2341
|
async count(predicate) {
|
|
2393
|
-
if (predicate)
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
const
|
|
2397
|
-
const result = await this._config.executeQuery(sql);
|
|
2398
|
-
const row = result.rows[0];
|
|
2342
|
+
if (predicate) return this.filter(predicate).count();
|
|
2343
|
+
const query = buildCount(this._config.tableName);
|
|
2344
|
+
const results = await this._config.executeBatch([query]);
|
|
2345
|
+
const row = results[0]?.rows[0];
|
|
2399
2346
|
return row?.count ?? 0;
|
|
2400
2347
|
}
|
|
2401
|
-
/**
|
|
2402
|
-
* Check if any row matches a predicate. Short-circuits.
|
|
2403
|
-
*
|
|
2404
|
-
* @example
|
|
2405
|
-
* ```ts
|
|
2406
|
-
* const hasActive = await Orders.some(o => o.status === 'active');
|
|
2407
|
-
* ```
|
|
2408
|
-
*/
|
|
2409
2348
|
async some(predicate) {
|
|
2410
2349
|
return this.filter(predicate).some();
|
|
2411
2350
|
}
|
|
2412
|
-
/**
|
|
2413
|
-
* Check if all rows match a predicate.
|
|
2414
|
-
*
|
|
2415
|
-
* @example
|
|
2416
|
-
* ```ts
|
|
2417
|
-
* const allComplete = await Orders.every(o => o.status === 'completed');
|
|
2418
|
-
* ```
|
|
2419
|
-
*/
|
|
2420
2351
|
async every(predicate) {
|
|
2421
2352
|
return this.filter(predicate).every();
|
|
2422
2353
|
}
|
|
2423
|
-
/**
|
|
2424
|
-
* Check if the table has zero rows.
|
|
2425
|
-
*
|
|
2426
|
-
* @example
|
|
2427
|
-
* ```ts
|
|
2428
|
-
* if (await Orders.isEmpty()) console.log('No orders yet');
|
|
2429
|
-
* ```
|
|
2430
|
-
*/
|
|
2431
2354
|
async isEmpty() {
|
|
2432
|
-
const
|
|
2433
|
-
const
|
|
2434
|
-
const row =
|
|
2355
|
+
const query = buildExists(this._config.tableName, void 0, void 0, true);
|
|
2356
|
+
const results = await this._config.executeBatch([query]);
|
|
2357
|
+
const row = results[0]?.rows[0];
|
|
2435
2358
|
return row?.result === 1;
|
|
2436
2359
|
}
|
|
2437
|
-
/**
|
|
2438
|
-
* Return the row with the minimum value for a field.
|
|
2439
|
-
* Executes as `ORDER BY field ASC LIMIT 1`.
|
|
2440
|
-
*
|
|
2441
|
-
* @example
|
|
2442
|
-
* ```ts
|
|
2443
|
-
* const cheapest = await Orders.min(o => o.amount);
|
|
2444
|
-
* ```
|
|
2445
|
-
*/
|
|
2446
2360
|
async min(accessor) {
|
|
2447
2361
|
return this.sortBy(accessor).first();
|
|
2448
2362
|
}
|
|
2449
|
-
/**
|
|
2450
|
-
* Return the row with the maximum value for a field.
|
|
2451
|
-
* Executes as `ORDER BY field DESC LIMIT 1`.
|
|
2452
|
-
*
|
|
2453
|
-
* @example
|
|
2454
|
-
* ```ts
|
|
2455
|
-
* const mostExpensive = await Orders.max(o => o.amount);
|
|
2456
|
-
* ```
|
|
2457
|
-
*/
|
|
2458
2363
|
async max(accessor) {
|
|
2459
2364
|
return this.sortBy(accessor).reverse().first();
|
|
2460
2365
|
}
|
|
2461
|
-
/**
|
|
2462
|
-
* Group all rows by a field value. Returns a Map.
|
|
2463
|
-
*
|
|
2464
|
-
* @example
|
|
2465
|
-
* ```ts
|
|
2466
|
-
* const byStatus = await Orders.groupBy(o => o.status);
|
|
2467
|
-
* // Map { 'pending' => [...], 'approved' => [...] }
|
|
2468
|
-
* ```
|
|
2469
|
-
*/
|
|
2470
2366
|
async groupBy(accessor) {
|
|
2471
2367
|
return new Query(this._config).groupBy(accessor);
|
|
2472
2368
|
}
|
|
2473
2369
|
// -------------------------------------------------------------------------
|
|
2474
|
-
// Reads — chainable
|
|
2370
|
+
// Reads — chainable
|
|
2475
2371
|
// -------------------------------------------------------------------------
|
|
2476
|
-
/**
|
|
2477
|
-
* Filter rows by a predicate. Returns a chainable Query.
|
|
2478
|
-
*
|
|
2479
|
-
* The predicate is compiled to SQL when possible. If compilation fails,
|
|
2480
|
-
* the query falls back to fetching all rows and filtering in JS.
|
|
2481
|
-
*
|
|
2482
|
-
* @example
|
|
2483
|
-
* ```ts
|
|
2484
|
-
* const active = await Orders.filter(o => o.status === 'active');
|
|
2485
|
-
* const recentActive = await Orders
|
|
2486
|
-
* .filter(o => o.status === 'active')
|
|
2487
|
-
* .sortBy(o => o.createdAt)
|
|
2488
|
-
* .reverse()
|
|
2489
|
-
* .take(10);
|
|
2490
|
-
* ```
|
|
2491
|
-
*/
|
|
2492
2372
|
filter(predicate) {
|
|
2493
2373
|
return new Query(this._config).filter(predicate);
|
|
2494
2374
|
}
|
|
2495
|
-
/**
|
|
2496
|
-
* Sort all rows by a field. Returns a chainable Query.
|
|
2497
|
-
*
|
|
2498
|
-
* @example
|
|
2499
|
-
* ```ts
|
|
2500
|
-
* const newest = await Orders.sortBy(o => o.createdAt).reverse().take(5);
|
|
2501
|
-
* ```
|
|
2502
|
-
*/
|
|
2503
2375
|
sortBy(accessor) {
|
|
2504
2376
|
return new Query(this._config).sortBy(accessor);
|
|
2505
2377
|
}
|
|
2506
2378
|
async push(data) {
|
|
2507
2379
|
const isArray = Array.isArray(data);
|
|
2508
2380
|
const items = isArray ? data : [data];
|
|
2509
|
-
const
|
|
2510
|
-
|
|
2511
|
-
const insertSql = buildInsert(
|
|
2381
|
+
const queries = items.map(
|
|
2382
|
+
(item) => buildInsert(
|
|
2512
2383
|
this._config.tableName,
|
|
2513
2384
|
item,
|
|
2514
2385
|
this._config.columns
|
|
2515
|
-
)
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
if (
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
this._config.columns
|
|
2524
|
-
)
|
|
2386
|
+
)
|
|
2387
|
+
);
|
|
2388
|
+
const results = await this._config.executeBatch(queries);
|
|
2389
|
+
const rows = results.map((r) => {
|
|
2390
|
+
if (r.rows.length > 0) {
|
|
2391
|
+
return deserializeRow(
|
|
2392
|
+
r.rows[0],
|
|
2393
|
+
this._config.columns
|
|
2525
2394
|
);
|
|
2526
2395
|
}
|
|
2527
|
-
|
|
2528
|
-
|
|
2396
|
+
return void 0;
|
|
2397
|
+
});
|
|
2398
|
+
return isArray ? rows : rows[0];
|
|
2529
2399
|
}
|
|
2530
2400
|
/**
|
|
2531
2401
|
* Update a row by ID. Only the provided fields are changed.
|
|
2532
|
-
* Returns the updated row
|
|
2533
|
-
*
|
|
2534
|
-
* System columns cannot be updated — they're stripped automatically.
|
|
2535
|
-
* `updatedAt` and `lastUpdatedBy` are set by the platform.
|
|
2536
|
-
*
|
|
2537
|
-
* @example
|
|
2538
|
-
* ```ts
|
|
2539
|
-
* const updated = await Orders.update(order.id, { status: 'approved' });
|
|
2540
|
-
* console.log(updated.updatedAt); // freshly updated
|
|
2541
|
-
* ```
|
|
2402
|
+
* Returns the updated row via `UPDATE ... RETURNING *`.
|
|
2542
2403
|
*/
|
|
2543
2404
|
async update(id, data) {
|
|
2544
|
-
const
|
|
2405
|
+
const query = buildUpdate(
|
|
2545
2406
|
this._config.tableName,
|
|
2546
2407
|
id,
|
|
2547
2408
|
data,
|
|
2548
2409
|
this._config.columns
|
|
2549
2410
|
);
|
|
2550
|
-
await this._config.
|
|
2551
|
-
const fetchSql = buildSelect(this._config.tableName, {
|
|
2552
|
-
where: `id = ${escapeValue(id)}`,
|
|
2553
|
-
limit: 1
|
|
2554
|
-
});
|
|
2555
|
-
const result = await this._config.executeQuery(fetchSql);
|
|
2411
|
+
const results = await this._config.executeBatch([query]);
|
|
2556
2412
|
return deserializeRow(
|
|
2557
|
-
|
|
2413
|
+
results[0].rows[0],
|
|
2558
2414
|
this._config.columns
|
|
2559
2415
|
);
|
|
2560
2416
|
}
|
|
2561
|
-
/**
|
|
2562
|
-
* Remove a row by ID.
|
|
2563
|
-
*
|
|
2564
|
-
* @example
|
|
2565
|
-
* ```ts
|
|
2566
|
-
* await Orders.remove('abc-123');
|
|
2567
|
-
* ```
|
|
2568
|
-
*/
|
|
2569
2417
|
async remove(id) {
|
|
2570
|
-
const
|
|
2571
|
-
|
|
2572
|
-
`id = ${escapeValue(id)}`
|
|
2573
|
-
);
|
|
2574
|
-
await this._config.executeQuery(sql);
|
|
2418
|
+
const query = buildDelete(this._config.tableName, `id = ?`, [id]);
|
|
2419
|
+
await this._config.executeBatch([query]);
|
|
2575
2420
|
}
|
|
2576
2421
|
/**
|
|
2577
2422
|
* Remove all rows matching a predicate. Returns the count removed.
|
|
2578
|
-
*
|
|
2579
|
-
* The predicate is compiled to SQL when possible. If compilation fails,
|
|
2580
|
-
* the function fetches all matching rows, collects their IDs, and
|
|
2581
|
-
* deletes them individually.
|
|
2582
|
-
*
|
|
2583
|
-
* @example
|
|
2584
|
-
* ```ts
|
|
2585
|
-
* const removed = await Orders.removeAll(o => o.status === 'rejected');
|
|
2586
|
-
* console.log(`Removed ${removed} orders`);
|
|
2587
|
-
* ```
|
|
2588
2423
|
*/
|
|
2589
2424
|
async removeAll(predicate) {
|
|
2590
2425
|
const compiled = compilePredicate(predicate);
|
|
2591
2426
|
if (compiled.type === "sql") {
|
|
2592
|
-
const
|
|
2593
|
-
const
|
|
2594
|
-
return
|
|
2427
|
+
const query = buildDelete(this._config.tableName, compiled.where);
|
|
2428
|
+
const results = await this._config.executeBatch([query]);
|
|
2429
|
+
return results[0].changes;
|
|
2595
2430
|
}
|
|
2596
2431
|
console.warn(
|
|
2597
2432
|
`[mindstudio] removeAll predicate on ${this._config.tableName} could not be compiled to SQL \u2014 fetching all rows first`
|
|
2598
2433
|
);
|
|
2599
|
-
const
|
|
2600
|
-
const
|
|
2601
|
-
const allRows =
|
|
2434
|
+
const allQuery = buildSelect(this._config.tableName);
|
|
2435
|
+
const allResults = await this._config.executeBatch([allQuery]);
|
|
2436
|
+
const allRows = allResults[0].rows.map(
|
|
2602
2437
|
(r) => deserializeRow(
|
|
2603
2438
|
r,
|
|
2604
2439
|
this._config.columns
|
|
2605
2440
|
)
|
|
2606
2441
|
);
|
|
2607
2442
|
const matching = allRows.filter((row) => predicate(row));
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
const sql = buildDelete(this._config.tableName, `id = ${escapeValue(id)}`);
|
|
2613
|
-
await this._config.executeQuery(sql);
|
|
2614
|
-
count++;
|
|
2615
|
-
}
|
|
2443
|
+
if (matching.length === 0) return 0;
|
|
2444
|
+
const deleteQueries = matching.filter((row) => row.id).map((row) => buildDelete(this._config.tableName, `id = ?`, [row.id]));
|
|
2445
|
+
if (deleteQueries.length > 0) {
|
|
2446
|
+
await this._config.executeBatch(deleteQueries);
|
|
2616
2447
|
}
|
|
2617
|
-
return
|
|
2448
|
+
return matching.length;
|
|
2618
2449
|
}
|
|
2619
|
-
/**
|
|
2620
|
-
* Remove all rows from the table.
|
|
2621
|
-
*
|
|
2622
|
-
* @example
|
|
2623
|
-
* ```ts
|
|
2624
|
-
* await Orders.clear();
|
|
2625
|
-
* ```
|
|
2626
|
-
*/
|
|
2627
2450
|
async clear() {
|
|
2628
|
-
const
|
|
2629
|
-
await this._config.
|
|
2451
|
+
const query = buildDelete(this._config.tableName);
|
|
2452
|
+
await this._config.executeBatch([query]);
|
|
2630
2453
|
}
|
|
2631
2454
|
};
|
|
2632
2455
|
}
|
|
2633
2456
|
});
|
|
2634
2457
|
|
|
2635
2458
|
// src/db/index.ts
|
|
2636
|
-
function createDb(databases,
|
|
2459
|
+
function createDb(databases, executeBatch) {
|
|
2637
2460
|
return {
|
|
2638
2461
|
defineTable(name, options) {
|
|
2639
2462
|
const resolved = resolveTable(databases, name, options?.database);
|
|
@@ -2641,7 +2464,7 @@ function createDb(databases, executeQuery) {
|
|
|
2641
2464
|
databaseId: resolved.databaseId,
|
|
2642
2465
|
tableName: name,
|
|
2643
2466
|
columns: resolved.columns,
|
|
2644
|
-
|
|
2467
|
+
executeBatch: (queries) => executeBatch(resolved.databaseId, queries)
|
|
2645
2468
|
};
|
|
2646
2469
|
return new Table(config);
|
|
2647
2470
|
},
|
|
@@ -2652,7 +2475,49 @@ function createDb(databases, executeQuery) {
|
|
|
2652
2475
|
hours: (n) => n * 36e5,
|
|
2653
2476
|
minutes: (n) => n * 6e4,
|
|
2654
2477
|
ago: (ms) => Date.now() - ms,
|
|
2655
|
-
fromNow: (ms) => Date.now() + ms
|
|
2478
|
+
fromNow: (ms) => Date.now() + ms,
|
|
2479
|
+
// --- Batch execution ---
|
|
2480
|
+
batch: ((...queries) => {
|
|
2481
|
+
return (async () => {
|
|
2482
|
+
const compiled = queries.map((q) => {
|
|
2483
|
+
if (!(q instanceof Query)) {
|
|
2484
|
+
throw new MindStudioError(
|
|
2485
|
+
"db.batch() only accepts Query objects (from .filter(), .sortBy(), etc.)",
|
|
2486
|
+
"invalid_batch_query",
|
|
2487
|
+
400
|
|
2488
|
+
);
|
|
2489
|
+
}
|
|
2490
|
+
return q._compile();
|
|
2491
|
+
});
|
|
2492
|
+
const groups = /* @__PURE__ */ new Map();
|
|
2493
|
+
for (let i = 0; i < compiled.length; i++) {
|
|
2494
|
+
const c = compiled[i];
|
|
2495
|
+
const dbId = c.config.databaseId;
|
|
2496
|
+
const sqlQuery = c.query ?? c.fallbackQuery;
|
|
2497
|
+
if (!groups.has(dbId)) groups.set(dbId, []);
|
|
2498
|
+
groups.get(dbId).push({ index: i, sqlQuery });
|
|
2499
|
+
}
|
|
2500
|
+
const allResults = new Array(compiled.length);
|
|
2501
|
+
await Promise.all(
|
|
2502
|
+
Array.from(groups.entries()).map(async ([dbId, entries]) => {
|
|
2503
|
+
const sqlQueries = entries.map((e) => e.sqlQuery);
|
|
2504
|
+
const results = await executeBatch(dbId, sqlQueries);
|
|
2505
|
+
for (let i = 0; i < entries.length; i++) {
|
|
2506
|
+
allResults[entries[i].index] = results[i];
|
|
2507
|
+
}
|
|
2508
|
+
})
|
|
2509
|
+
);
|
|
2510
|
+
return compiled.map((c, i) => {
|
|
2511
|
+
const result = allResults[i];
|
|
2512
|
+
if (!c.query && c.predicates?.length) {
|
|
2513
|
+
console.warn(
|
|
2514
|
+
`[mindstudio] db.batch(): filter on ${c.config.tableName} could not be compiled to SQL \u2014 processing in JS`
|
|
2515
|
+
);
|
|
2516
|
+
}
|
|
2517
|
+
return Query._processResults(result, c);
|
|
2518
|
+
});
|
|
2519
|
+
})();
|
|
2520
|
+
})
|
|
2656
2521
|
};
|
|
2657
2522
|
}
|
|
2658
2523
|
function resolveTable(databases, tableName, databaseHint) {
|
|
@@ -2707,6 +2572,7 @@ var init_db = __esm({
|
|
|
2707
2572
|
"use strict";
|
|
2708
2573
|
init_errors();
|
|
2709
2574
|
init_table();
|
|
2575
|
+
init_query();
|
|
2710
2576
|
init_table();
|
|
2711
2577
|
}
|
|
2712
2578
|
});
|
|
@@ -3612,6 +3478,9 @@ var init_client = __esm({
|
|
|
3612
3478
|
* ```
|
|
3613
3479
|
*/
|
|
3614
3480
|
get auth() {
|
|
3481
|
+
if (!this._auth) {
|
|
3482
|
+
this._trySandboxHydration();
|
|
3483
|
+
}
|
|
3615
3484
|
if (!this._auth) {
|
|
3616
3485
|
throw new MindStudioError(
|
|
3617
3486
|
"Auth context not yet loaded. Call `await agent.ensureContext()` or perform any db operation first (which auto-hydrates context). Inside the MindStudio sandbox, context is loaded automatically.",
|
|
@@ -3637,6 +3506,9 @@ var init_client = __esm({
|
|
|
3637
3506
|
* ```
|
|
3638
3507
|
*/
|
|
3639
3508
|
get db() {
|
|
3509
|
+
if (!this._db) {
|
|
3510
|
+
this._trySandboxHydration();
|
|
3511
|
+
}
|
|
3640
3512
|
if (this._db) return this._db;
|
|
3641
3513
|
return this._createLazyDb();
|
|
3642
3514
|
}
|
|
@@ -3694,7 +3566,7 @@ var init_client = __esm({
|
|
|
3694
3566
|
this._auth = new AuthContext(context.auth);
|
|
3695
3567
|
this._db = createDb(
|
|
3696
3568
|
context.databases,
|
|
3697
|
-
this.
|
|
3569
|
+
this._executeDbBatch.bind(this)
|
|
3698
3570
|
);
|
|
3699
3571
|
}
|
|
3700
3572
|
/**
|
|
@@ -3715,25 +3587,40 @@ var init_client = __esm({
|
|
|
3715
3587
|
}
|
|
3716
3588
|
}
|
|
3717
3589
|
/**
|
|
3718
|
-
* @internal Execute a SQL
|
|
3719
|
-
* Used as the `
|
|
3590
|
+
* @internal Execute a batch of SQL queries against a managed database.
|
|
3591
|
+
* Used as the `executeBatch` callback for Table/Query instances.
|
|
3720
3592
|
*
|
|
3721
|
-
* Calls
|
|
3722
|
-
* (
|
|
3593
|
+
* Calls `POST /_internal/v2/db/query` directly with the hook token
|
|
3594
|
+
* (raw, no Bearer prefix). All queries run on a single SQLite connection,
|
|
3595
|
+
* enabling RETURNING clauses and multi-statement batches.
|
|
3723
3596
|
*/
|
|
3724
|
-
async
|
|
3725
|
-
const
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3597
|
+
async _executeDbBatch(databaseId, queries) {
|
|
3598
|
+
const url = `${this._httpConfig.baseUrl}/_internal/v2/db/query`;
|
|
3599
|
+
const res = await fetch(url, {
|
|
3600
|
+
method: "POST",
|
|
3601
|
+
headers: {
|
|
3602
|
+
"Content-Type": "application/json",
|
|
3603
|
+
Authorization: this._httpConfig.token
|
|
3604
|
+
},
|
|
3605
|
+
body: JSON.stringify({ databaseId, queries })
|
|
3729
3606
|
});
|
|
3730
|
-
|
|
3607
|
+
if (!res.ok) {
|
|
3608
|
+
let message = `Database query failed: ${res.status} ${res.statusText}`;
|
|
3609
|
+
try {
|
|
3610
|
+
const body = await res.json();
|
|
3611
|
+
if (body.error) message = body.error;
|
|
3612
|
+
} catch {
|
|
3613
|
+
}
|
|
3614
|
+
throw new MindStudioError(message, "db_query_error", res.status);
|
|
3615
|
+
}
|
|
3616
|
+
const data = await res.json();
|
|
3617
|
+
return data.results;
|
|
3731
3618
|
}
|
|
3732
3619
|
/**
|
|
3733
3620
|
* @internal Create a lazy Db proxy that auto-hydrates context.
|
|
3734
3621
|
*
|
|
3735
3622
|
* defineTable() returns Table instances immediately (no async needed).
|
|
3736
|
-
* But the Table's
|
|
3623
|
+
* But the Table's executeBatch callback is wrapped to call ensureContext()
|
|
3737
3624
|
* before the first query, so context is fetched lazily.
|
|
3738
3625
|
*/
|
|
3739
3626
|
_createLazyDb() {
|
|
@@ -3745,7 +3632,7 @@ var init_client = __esm({
|
|
|
3745
3632
|
databaseId: "",
|
|
3746
3633
|
tableName: name,
|
|
3747
3634
|
columns: [],
|
|
3748
|
-
|
|
3635
|
+
executeBatch: async (queries) => {
|
|
3749
3636
|
await agent.ensureContext();
|
|
3750
3637
|
const databases = agent._context.databases;
|
|
3751
3638
|
let targetDb;
|
|
@@ -3759,7 +3646,7 @@ var init_client = __esm({
|
|
|
3759
3646
|
);
|
|
3760
3647
|
}
|
|
3761
3648
|
const databaseId = targetDb?.id ?? databases[0]?.id ?? "";
|
|
3762
|
-
return agent.
|
|
3649
|
+
return agent._executeDbBatch(databaseId, queries);
|
|
3763
3650
|
}
|
|
3764
3651
|
});
|
|
3765
3652
|
},
|
|
@@ -3769,7 +3656,14 @@ var init_client = __esm({
|
|
|
3769
3656
|
hours: (n) => n * 36e5,
|
|
3770
3657
|
minutes: (n) => n * 6e4,
|
|
3771
3658
|
ago: (ms) => Date.now() - ms,
|
|
3772
|
-
fromNow: (ms) => Date.now() + ms
|
|
3659
|
+
fromNow: (ms) => Date.now() + ms,
|
|
3660
|
+
// Batch needs context — hydrate first, then delegate to real db
|
|
3661
|
+
batch: ((...queries) => {
|
|
3662
|
+
return (async () => {
|
|
3663
|
+
await agent.ensureContext();
|
|
3664
|
+
return agent._db.batch(...queries);
|
|
3665
|
+
})();
|
|
3666
|
+
})
|
|
3773
3667
|
};
|
|
3774
3668
|
}
|
|
3775
3669
|
// -------------------------------------------------------------------------
|
|
@@ -3974,7 +3868,7 @@ async function startMcpServer(options) {
|
|
|
3974
3868
|
capabilities: { tools: {} },
|
|
3975
3869
|
serverInfo: {
|
|
3976
3870
|
name: "mindstudio-agent",
|
|
3977
|
-
version: "0.1.
|
|
3871
|
+
version: "0.1.11"
|
|
3978
3872
|
},
|
|
3979
3873
|
instructions: "Welcome to MindStudio \u2014 a platform with 200+ AI models, 850+ third-party integrations, and pre-built agents.\n\nGetting started:\n1. Call `listAgents` to verify your connection and see available agents.\n2. Call `changeName` to set your display name \u2014 use your name or whatever your user calls you. This is how you'll appear in MindStudio request logs.\n3. If you have a profile picture or icon, call `uploadFile` to upload it, then `changeProfilePicture` with the returned URL. This helps users identify your requests in their logs.\n4. Call `listActions` to discover all available actions.\n\nThen use the tools to generate text, images, video, audio, search the web, work with data sources, run agents, and more.\n\nImportant:\n- AI-powered actions (text generation, image generation, video, audio, etc.) cost money. Before running these, call `estimateActionCost` and confirm with the user before proceeding \u2014 unless they've explicitly told you to go ahead.\n- Not all agents from `listAgents` are configured for API use. Do not try to run an agent just because it appears in the list \u2014 it will likely fail. Only run agents the user specifically asks you to run."
|
|
3980
3874
|
});
|
|
@@ -4794,7 +4688,7 @@ function isNewerVersion(current, latest) {
|
|
|
4794
4688
|
return false;
|
|
4795
4689
|
}
|
|
4796
4690
|
async function checkForUpdate() {
|
|
4797
|
-
const currentVersion = "0.1.
|
|
4691
|
+
const currentVersion = "0.1.11";
|
|
4798
4692
|
if (!currentVersion) return null;
|
|
4799
4693
|
try {
|
|
4800
4694
|
const { loadConfig: loadConfig2, saveConfig: saveConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
@@ -4823,7 +4717,7 @@ async function checkForUpdate() {
|
|
|
4823
4717
|
}
|
|
4824
4718
|
}
|
|
4825
4719
|
function printUpdateNotice(latestVersion) {
|
|
4826
|
-
const currentVersion = "0.1.
|
|
4720
|
+
const currentVersion = "0.1.11";
|
|
4827
4721
|
process.stderr.write(
|
|
4828
4722
|
`
|
|
4829
4723
|
${ansi.cyanBright("Update available")} ${ansi.gray(currentVersion + " \u2192")} ${ansi.cyanBold(latestVersion)}
|
|
@@ -4878,7 +4772,7 @@ async function cmdLogin(options) {
|
|
|
4878
4772
|
process.stderr.write("\n");
|
|
4879
4773
|
printLogo();
|
|
4880
4774
|
process.stderr.write("\n");
|
|
4881
|
-
const ver = "0.1.
|
|
4775
|
+
const ver = "0.1.11";
|
|
4882
4776
|
process.stderr.write(
|
|
4883
4777
|
` ${ansi.bold("MindStudio Agent")} ${ver ? " " + ansi.gray("v" + ver) : ""}
|
|
4884
4778
|
`
|
|
@@ -4961,6 +4855,9 @@ async function cmdLogin(options) {
|
|
|
4961
4855
|
` ${ansi.greenBold("\u2714")} Authenticated successfully!
|
|
4962
4856
|
${ansi.gray("Credentials saved to")} ${getConfigPath2()}
|
|
4963
4857
|
|
|
4858
|
+
${ansi.bold("Using with Claude Code?")} Run once to enable the MCP server:
|
|
4859
|
+
${ansi.cyan("claude mcp add mindstudio -- mindstudio mcp")}
|
|
4860
|
+
|
|
4964
4861
|
`
|
|
4965
4862
|
);
|
|
4966
4863
|
return;
|