@mindstudio-ai/agent 0.1.10 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +178 -47
- package/dist/index.d.ts +69 -7
- package/dist/index.js +173 -43
- package/dist/index.js.map +1 -1
- package/dist/postinstall.js +178 -47
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -302,7 +302,15 @@ function buildDelete(table, where, whereParams) {
|
|
|
302
302
|
if (where) sql += ` WHERE ${where}`;
|
|
303
303
|
return { sql, params: whereParams?.length ? whereParams : void 0 };
|
|
304
304
|
}
|
|
305
|
-
var SYSTEM_COLUMNS = /* @__PURE__ */ new Set([
|
|
305
|
+
var SYSTEM_COLUMNS = /* @__PURE__ */ new Set([
|
|
306
|
+
"id",
|
|
307
|
+
"created_at",
|
|
308
|
+
"createdAt",
|
|
309
|
+
"updated_at",
|
|
310
|
+
"updatedAt",
|
|
311
|
+
"last_updated_by",
|
|
312
|
+
"lastUpdatedBy"
|
|
313
|
+
]);
|
|
306
314
|
function stripSystemColumns(data) {
|
|
307
315
|
const result = {};
|
|
308
316
|
for (const [key, value] of Object.entries(data)) {
|
|
@@ -716,8 +724,7 @@ var Parser = class {
|
|
|
716
724
|
return PARSE_FAILED;
|
|
717
725
|
}
|
|
718
726
|
/**
|
|
719
|
-
* Attempt to resolve a closure variable
|
|
720
|
-
* with a recording Proxy and inspecting what values it compares against.
|
|
727
|
+
* Attempt to resolve a closure variable's value.
|
|
721
728
|
*
|
|
722
729
|
* This handles the common pattern:
|
|
723
730
|
* ```ts
|
|
@@ -725,40 +732,28 @@ var Parser = class {
|
|
|
725
732
|
* orders.filter(o => o.requestedBy === userId)
|
|
726
733
|
* ```
|
|
727
734
|
*
|
|
728
|
-
*
|
|
729
|
-
*
|
|
730
|
-
*
|
|
731
|
-
*
|
|
735
|
+
* Closure variable resolution is fundamentally limited in JavaScript —
|
|
736
|
+
* we can't access another function's closure scope from outside without
|
|
737
|
+
* `eval`. The `===` operator can't be overridden via Proxy or
|
|
738
|
+
* Symbol.toPrimitive, so we can't intercept comparisons.
|
|
739
|
+
*
|
|
740
|
+
* For now, this falls back to JS execution. The predicate still works
|
|
741
|
+
* correctly — it just scans all rows instead of generating SQL.
|
|
742
|
+
* This is the most common reason for JS fallback in practice, since
|
|
743
|
+
* almost every real-world filter references a variable like `userId`.
|
|
744
|
+
*
|
|
745
|
+
* A future improvement could accept an explicit `vars` argument:
|
|
746
|
+
* ```ts
|
|
747
|
+
* orders.filter(o => o.requestedBy === $userId, { $userId: auth.userId })
|
|
748
|
+
* ```
|
|
732
749
|
*/
|
|
733
750
|
resolveClosureVariable() {
|
|
734
|
-
|
|
735
|
-
let closureExpr = identToken.value;
|
|
751
|
+
this.advance();
|
|
736
752
|
while (this.match("dot") && this.tokens[this.pos + 1]?.type === "identifier") {
|
|
737
753
|
this.advance();
|
|
738
|
-
|
|
739
|
-
}
|
|
740
|
-
try {
|
|
741
|
-
const MARKER = /* @__PURE__ */ Symbol("field_access_marker");
|
|
742
|
-
const accessed = [];
|
|
743
|
-
const proxy = new Proxy(
|
|
744
|
-
{},
|
|
745
|
-
{
|
|
746
|
-
get(_, prop) {
|
|
747
|
-
accessed.push(prop);
|
|
748
|
-
return new Proxy(() => MARKER, {
|
|
749
|
-
get(_2, nestedProp) {
|
|
750
|
-
accessed.push(nestedProp);
|
|
751
|
-
return MARKER;
|
|
752
|
-
}
|
|
753
|
-
});
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
);
|
|
757
|
-
void proxy;
|
|
758
|
-
return PARSE_FAILED;
|
|
759
|
-
} catch {
|
|
760
|
-
return PARSE_FAILED;
|
|
754
|
+
this.advance();
|
|
761
755
|
}
|
|
756
|
+
return PARSE_FAILED;
|
|
762
757
|
}
|
|
763
758
|
/**
|
|
764
759
|
* Look ahead to check if the next tokens form `.includes(`.
|
|
@@ -935,6 +930,76 @@ var Query = class _Query {
|
|
|
935
930
|
return map;
|
|
936
931
|
}
|
|
937
932
|
// -------------------------------------------------------------------------
|
|
933
|
+
// Batch compilation — used by db.batch() to extract SQL without executing
|
|
934
|
+
// -------------------------------------------------------------------------
|
|
935
|
+
/**
|
|
936
|
+
* @internal Compile this query into a SqlQuery for batch execution.
|
|
937
|
+
*
|
|
938
|
+
* Returns the compiled SQL query (if all predicates compile to SQL),
|
|
939
|
+
* or null (if JS fallback is needed). In the fallback case, a bare
|
|
940
|
+
* `SELECT *` is returned as `fallbackQuery` so the batch can fetch
|
|
941
|
+
* all rows and this query can filter them in JS post-fetch.
|
|
942
|
+
*/
|
|
943
|
+
_compile() {
|
|
944
|
+
const compiled = this._compilePredicates();
|
|
945
|
+
const sortField = this._sortAccessor ? extractFieldName(this._sortAccessor) : void 0;
|
|
946
|
+
if (compiled.allSql) {
|
|
947
|
+
const query = buildSelect(this._config.tableName, {
|
|
948
|
+
where: compiled.sqlWhere || void 0,
|
|
949
|
+
orderBy: sortField ?? void 0,
|
|
950
|
+
desc: this._reversed,
|
|
951
|
+
limit: this._limit,
|
|
952
|
+
offset: this._offset
|
|
953
|
+
});
|
|
954
|
+
return { query, fallbackQuery: null, config: this._config };
|
|
955
|
+
}
|
|
956
|
+
const fallbackQuery = buildSelect(this._config.tableName);
|
|
957
|
+
return {
|
|
958
|
+
query: null,
|
|
959
|
+
fallbackQuery,
|
|
960
|
+
config: this._config,
|
|
961
|
+
predicates: this._predicates,
|
|
962
|
+
sortAccessor: this._sortAccessor,
|
|
963
|
+
reversed: this._reversed,
|
|
964
|
+
limit: this._limit,
|
|
965
|
+
offset: this._offset
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* @internal Process raw SQL results into typed rows. Used by db.batch()
|
|
970
|
+
* after executing the compiled query.
|
|
971
|
+
*
|
|
972
|
+
* For SQL-compiled queries: just deserialize the rows.
|
|
973
|
+
* For JS-fallback queries: filter, sort, and slice in JS.
|
|
974
|
+
*/
|
|
975
|
+
static _processResults(result, compiled) {
|
|
976
|
+
const rows = result.rows.map(
|
|
977
|
+
(row) => deserializeRow(
|
|
978
|
+
row,
|
|
979
|
+
compiled.config.columns
|
|
980
|
+
)
|
|
981
|
+
);
|
|
982
|
+
if (compiled.query) return rows;
|
|
983
|
+
let filtered = compiled.predicates ? rows.filter((row) => compiled.predicates.every((pred) => pred(row))) : rows;
|
|
984
|
+
if (compiled.sortAccessor) {
|
|
985
|
+
const accessor = compiled.sortAccessor;
|
|
986
|
+
const reversed = compiled.reversed ?? false;
|
|
987
|
+
filtered.sort((a, b) => {
|
|
988
|
+
const aVal = accessor(a);
|
|
989
|
+
const bVal = accessor(b);
|
|
990
|
+
if (aVal < bVal) return reversed ? 1 : -1;
|
|
991
|
+
if (aVal > bVal) return reversed ? -1 : 1;
|
|
992
|
+
return 0;
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
if (compiled.offset != null || compiled.limit != null) {
|
|
996
|
+
const start = compiled.offset ?? 0;
|
|
997
|
+
const end = compiled.limit != null ? start + compiled.limit : void 0;
|
|
998
|
+
filtered = filtered.slice(start, end);
|
|
999
|
+
}
|
|
1000
|
+
return filtered;
|
|
1001
|
+
}
|
|
1002
|
+
// -------------------------------------------------------------------------
|
|
938
1003
|
// PromiseLike
|
|
939
1004
|
// -------------------------------------------------------------------------
|
|
940
1005
|
then(onfulfilled, onrejected) {
|
|
@@ -1180,7 +1245,49 @@ function createDb(databases, executeBatch) {
|
|
|
1180
1245
|
hours: (n) => n * 36e5,
|
|
1181
1246
|
minutes: (n) => n * 6e4,
|
|
1182
1247
|
ago: (ms) => Date.now() - ms,
|
|
1183
|
-
fromNow: (ms) => Date.now() + ms
|
|
1248
|
+
fromNow: (ms) => Date.now() + ms,
|
|
1249
|
+
// --- Batch execution ---
|
|
1250
|
+
batch: ((...queries) => {
|
|
1251
|
+
return (async () => {
|
|
1252
|
+
const compiled = queries.map((q) => {
|
|
1253
|
+
if (!(q instanceof Query)) {
|
|
1254
|
+
throw new MindStudioError(
|
|
1255
|
+
"db.batch() only accepts Query objects (from .filter(), .sortBy(), etc.)",
|
|
1256
|
+
"invalid_batch_query",
|
|
1257
|
+
400
|
|
1258
|
+
);
|
|
1259
|
+
}
|
|
1260
|
+
return q._compile();
|
|
1261
|
+
});
|
|
1262
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1263
|
+
for (let i = 0; i < compiled.length; i++) {
|
|
1264
|
+
const c = compiled[i];
|
|
1265
|
+
const dbId = c.config.databaseId;
|
|
1266
|
+
const sqlQuery = c.query ?? c.fallbackQuery;
|
|
1267
|
+
if (!groups.has(dbId)) groups.set(dbId, []);
|
|
1268
|
+
groups.get(dbId).push({ index: i, sqlQuery });
|
|
1269
|
+
}
|
|
1270
|
+
const allResults = new Array(compiled.length);
|
|
1271
|
+
await Promise.all(
|
|
1272
|
+
Array.from(groups.entries()).map(async ([dbId, entries]) => {
|
|
1273
|
+
const sqlQueries = entries.map((e) => e.sqlQuery);
|
|
1274
|
+
const results = await executeBatch(dbId, sqlQueries);
|
|
1275
|
+
for (let i = 0; i < entries.length; i++) {
|
|
1276
|
+
allResults[entries[i].index] = results[i];
|
|
1277
|
+
}
|
|
1278
|
+
})
|
|
1279
|
+
);
|
|
1280
|
+
return compiled.map((c, i) => {
|
|
1281
|
+
const result = allResults[i];
|
|
1282
|
+
if (!c.query && c.predicates?.length) {
|
|
1283
|
+
console.warn(
|
|
1284
|
+
`[mindstudio] db.batch(): filter on ${c.config.tableName} could not be compiled to SQL \u2014 processing in JS`
|
|
1285
|
+
);
|
|
1286
|
+
}
|
|
1287
|
+
return Query._processResults(result, c);
|
|
1288
|
+
});
|
|
1289
|
+
})();
|
|
1290
|
+
})
|
|
1184
1291
|
};
|
|
1185
1292
|
}
|
|
1186
1293
|
function resolveTable(databases, tableName, databaseHint) {
|
|
@@ -1951,10 +2058,12 @@ var MindStudioAgent = class {
|
|
|
1951
2058
|
);
|
|
1952
2059
|
}
|
|
1953
2060
|
if (!res.ok) {
|
|
2061
|
+
const errorBody = await res.json().catch(() => ({}));
|
|
1954
2062
|
throw new MindStudioError(
|
|
1955
|
-
`Poll request failed: ${res.status} ${res.statusText}`,
|
|
1956
|
-
"poll_error",
|
|
1957
|
-
res.status
|
|
2063
|
+
errorBody.message ?? errorBody.error ?? `Poll request failed: ${res.status} ${res.statusText}`,
|
|
2064
|
+
errorBody.code ?? "poll_error",
|
|
2065
|
+
res.status,
|
|
2066
|
+
errorBody
|
|
1958
2067
|
);
|
|
1959
2068
|
}
|
|
1960
2069
|
const poll = await res.json();
|
|
@@ -2219,12 +2328,24 @@ var MindStudioAgent = class {
|
|
|
2219
2328
|
});
|
|
2220
2329
|
if (!res.ok) {
|
|
2221
2330
|
let message = `Database query failed: ${res.status} ${res.statusText}`;
|
|
2331
|
+
let code = "db_query_error";
|
|
2222
2332
|
try {
|
|
2223
|
-
const
|
|
2224
|
-
|
|
2333
|
+
const text = await res.text();
|
|
2334
|
+
try {
|
|
2335
|
+
const body = JSON.parse(text);
|
|
2336
|
+
const errMsg = body.error ?? body.message ?? body.details;
|
|
2337
|
+
if (errMsg) message = errMsg;
|
|
2338
|
+
if (body.code) code = body.code;
|
|
2339
|
+
} catch {
|
|
2340
|
+
if (text && text.length < 500) message = text;
|
|
2341
|
+
}
|
|
2225
2342
|
} catch {
|
|
2226
2343
|
}
|
|
2227
|
-
throw new MindStudioError(
|
|
2344
|
+
throw new MindStudioError(
|
|
2345
|
+
`[db] ${message}`,
|
|
2346
|
+
code,
|
|
2347
|
+
res.status
|
|
2348
|
+
);
|
|
2228
2349
|
}
|
|
2229
2350
|
const data = await res.json();
|
|
2230
2351
|
return data.results;
|
|
@@ -2269,7 +2390,14 @@ var MindStudioAgent = class {
|
|
|
2269
2390
|
hours: (n) => n * 36e5,
|
|
2270
2391
|
minutes: (n) => n * 6e4,
|
|
2271
2392
|
ago: (ms) => Date.now() - ms,
|
|
2272
|
-
fromNow: (ms) => Date.now() + ms
|
|
2393
|
+
fromNow: (ms) => Date.now() + ms,
|
|
2394
|
+
// Batch needs context — hydrate first, then delegate to real db
|
|
2395
|
+
batch: ((...queries) => {
|
|
2396
|
+
return (async () => {
|
|
2397
|
+
await agent.ensureContext();
|
|
2398
|
+
return agent._db.batch(...queries);
|
|
2399
|
+
})();
|
|
2400
|
+
})
|
|
2273
2401
|
};
|
|
2274
2402
|
}
|
|
2275
2403
|
// -------------------------------------------------------------------------
|
|
@@ -2401,10 +2529,12 @@ var MindStudioAgent = class {
|
|
|
2401
2529
|
headers: options.type ? { "Content-Type": options.type } : {}
|
|
2402
2530
|
});
|
|
2403
2531
|
if (!res.ok) {
|
|
2532
|
+
const errorBody = await res.json().catch(() => ({}));
|
|
2404
2533
|
throw new MindStudioError(
|
|
2405
|
-
`Upload failed: ${res.status} ${res.statusText}`,
|
|
2406
|
-
"upload_error",
|
|
2407
|
-
res.status
|
|
2534
|
+
errorBody.message ?? errorBody.error ?? `Upload failed: ${res.status} ${res.statusText}`,
|
|
2535
|
+
errorBody.code ?? "upload_error",
|
|
2536
|
+
res.status,
|
|
2537
|
+
errorBody
|
|
2408
2538
|
);
|
|
2409
2539
|
}
|
|
2410
2540
|
return { url: data.url };
|