@fadhilp/stateql 0.6.0 → 0.7.0
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 +27 -2
- package/dist/src/index.d.ts +1 -1
- package/dist/src/migrations.js +12 -0
- package/dist/src/stateql.d.ts +4 -3
- package/dist/src/stateql.js +216 -162
- package/dist/src/store.d.ts +4 -2
- package/dist/src/store.js +7 -7
- package/dist/src/types.d.ts +13 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -253,8 +253,9 @@ cancels active work.
|
|
|
253
253
|
- MySQL deadlines destroy the active connection.
|
|
254
254
|
- MongoDB uses driver deadlines and closes stopped operations.
|
|
255
255
|
|
|
256
|
-
A timed-out write may return `OUTCOME_UNKNOWN` when its commit
|
|
257
|
-
proven.
|
|
256
|
+
A timed-out or cancelled write may return `OUTCOME_UNKNOWN` when its commit
|
|
257
|
+
status cannot be proven. Cancellation stops that command's driver work; it does
|
|
258
|
+
not close the `StateQL` actor, and later commands remain usable.
|
|
258
259
|
|
|
259
260
|
## Durable state and result reuse
|
|
260
261
|
|
|
@@ -424,6 +425,30 @@ if (response.ok) {
|
|
|
424
425
|
}
|
|
425
426
|
```
|
|
426
427
|
|
|
428
|
+
Hosts that dispatch batch-shaped commands can attach trusted metadata out of
|
|
429
|
+
band. `origin` is audit/source metadata only; it never changes actor membership,
|
|
430
|
+
workspace access, or write authorization.
|
|
431
|
+
|
|
432
|
+
```ts
|
|
433
|
+
const controller = new AbortController();
|
|
434
|
+
await stateql.executeCommand(
|
|
435
|
+
{ command: "query", sql: "SELECT * FROM users", cache: "bypass" },
|
|
436
|
+
{ origin: "user", signal: controller.signal },
|
|
437
|
+
);
|
|
438
|
+
|
|
439
|
+
const userHistory = await stateql.history(50, { origin: "user" });
|
|
440
|
+
await stateql.executeCommand(
|
|
441
|
+
{ command: "history", limit: 50, history_origin: "user" },
|
|
442
|
+
{ origin: "model" },
|
|
443
|
+
);
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Supported origins are `legacy`, `user`, `model`, `system`, and `api`. Existing
|
|
447
|
+
direct calls and `executeCommand(command)` calls are recorded as `legacy`.
|
|
448
|
+
`history_origin` is only a retrieval filter; putting an `origin` field in a
|
|
449
|
+
`BatchCommand` cannot attribute the command. `batch` accepts the same trusted
|
|
450
|
+
context as `options.executionContext` for all commands in that batch.
|
|
451
|
+
|
|
427
452
|
### Actor workspaces
|
|
428
453
|
|
|
429
454
|
`StateQL.forActor(...)` resolves the actor's attached session directly from
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { StateQL } from "./stateql.js";
|
|
2
2
|
export { CredentialResolutionError, StateQLError, exitCodeFor, } from "./errors.js";
|
|
3
3
|
export type { CredentialResolutionFailure } from "./errors.js";
|
|
4
|
-
export type { ActorData, ActorLinkData, ActorResolutionData, ActorsData, ActorUnlinkData, AliasData, ApplyData, BatchCommand, BatchCommandName, BatchOptions, CapabilitiesData, CloseSessionData, Column, ColumnsData, CommitTransactionData, ConnectOptions, ConnectionData, CountData, CredentialAccess, CredentialOperation, CredentialRequest, CredentialResolver, DisconnectData, DoctorData, Driver, ExecData, ExecOptions, ExecutionOptions, ExportData, Failure, FilterOptions, HistoryData, HistoryEntry, MongoAggregateOptions, MongoDocument, MongoFindOptions, MongoMutationOptions, MongoExecOptions, MongoPlanOptions, MongoQueryOptions, MongoReadCommand, MongoWriteCommand, MongoWriteOutcome, OperationData, PlanData, PlanOptions, ProfileData, ProfileOptions, ProfilesData, PurgeData, QueryOptions, RecentOperationData, RecentResultData, RemovedProfileData, Response, ResultData, RollbackTransactionData, Row, RowsData, RowsOptions, SqlParameters, SqlDriver, StateQLActorOptions, StateQLOptions, StateConfidence, StateQLSnapshot, StatusConnectionData, StatusData, Success, SessionData, SessionListItem, SessionsData, SessionSummaryData, TransactionData, TransactionReferenceData, Warning, } from "./types.js";
|
|
4
|
+
export type { ActorData, ActorLinkData, ActorResolutionData, ActorsData, ActorUnlinkData, AliasData, ApplyData, BatchCommand, BatchCommandName, BatchOptions, CapabilitiesData, CommandExecutionContext, CommandOrigin, CloseSessionData, Column, ColumnsData, CommitTransactionData, ConnectOptions, ConnectionData, CountData, CredentialAccess, CredentialOperation, CredentialRequest, CredentialResolver, DisconnectData, DoctorData, Driver, ExecData, ExecOptions, ExecutionOptions, ExportData, Failure, FilterOptions, HistoryData, HistoryEntry, HistoryOptions, MongoAggregateOptions, MongoDocument, MongoFindOptions, MongoMutationOptions, MongoExecOptions, MongoPlanOptions, MongoQueryOptions, MongoReadCommand, MongoWriteCommand, MongoWriteOutcome, OperationData, PlanData, PlanOptions, ProfileData, ProfileOptions, ProfilesData, PurgeData, QueryOptions, RecentOperationData, RecentResultData, RemovedProfileData, Response, ResultData, RollbackTransactionData, Row, RowsData, RowsOptions, SqlParameters, SqlDriver, StateQLActorOptions, StateQLOptions, StateConfidence, StateQLSnapshot, StatusConnectionData, StatusData, Success, SessionData, SessionListItem, SessionsData, SessionSummaryData, TransactionData, TransactionReferenceData, Warning, } from "./types.js";
|
package/dist/src/migrations.js
CHANGED
|
@@ -64,6 +64,17 @@ const MIGRATIONS = [
|
|
|
64
64
|
requireColumns(db, "operations", ["outcome_json"]);
|
|
65
65
|
},
|
|
66
66
|
},
|
|
67
|
+
{
|
|
68
|
+
name: "history_origin_v1",
|
|
69
|
+
apply(db) {
|
|
70
|
+
addColumn(db, "history", "origin", "TEXT NOT NULL DEFAULT 'legacy'");
|
|
71
|
+
db.exec("CREATE INDEX IF NOT EXISTS history_session_origin ON history(session_id, origin)");
|
|
72
|
+
},
|
|
73
|
+
validate(db) {
|
|
74
|
+
requireColumns(db, "history", ["origin"]);
|
|
75
|
+
requireIndexes(db, ["history_session_origin"]);
|
|
76
|
+
},
|
|
77
|
+
},
|
|
67
78
|
];
|
|
68
79
|
export function runMigrations(db, now) {
|
|
69
80
|
db.exec(`
|
|
@@ -221,6 +232,7 @@ function createInitialSchema(db) {
|
|
|
221
232
|
session_id TEXT NOT NULL,
|
|
222
233
|
actor_id TEXT NOT NULL,
|
|
223
234
|
command TEXT NOT NULL,
|
|
235
|
+
origin TEXT NOT NULL DEFAULT 'legacy',
|
|
224
236
|
sql TEXT,
|
|
225
237
|
handle TEXT,
|
|
226
238
|
executed INTEGER NOT NULL,
|
package/dist/src/stateql.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ActorLinkData, ActorResolutionData, ActorsData, ActorUnlinkData, AliasData, ApplyData, BatchCommand, BatchOptions, CapabilitiesData, CloseSessionData, ColumnsData, CommitTransactionData, ConnectOptions, ConnectionData, CountData, DisconnectData, DoctorData, ExecData, ExecOptions, ExecutionOptions, ExportData, FilterOptions, HistoryData, OperationData, PlanData, PlanOptions, ProfileData, ProfilesData, ProfileOptions, MongoExecOptions, MongoPlanOptions, MongoQueryOptions, MongoReadCommand, MongoWriteCommand, PurgeData, QueryOptions, RemovedProfileData, Response, ResultData, RollbackTransactionData, RowsData, RowsOptions, StateQLActorOptions, StateQLOptions, StateQLSnapshot, StatusData, SessionData, SessionsData, SessionSummaryData, TransactionData } from "./types.js";
|
|
1
|
+
import type { ActorLinkData, ActorResolutionData, ActorsData, ActorUnlinkData, AliasData, ApplyData, BatchCommand, CommandExecutionContext, BatchOptions, CapabilitiesData, CloseSessionData, ColumnsData, CommitTransactionData, ConnectOptions, ConnectionData, CountData, DisconnectData, DoctorData, ExecData, ExecOptions, ExecutionOptions, ExportData, FilterOptions, HistoryData, HistoryOptions, OperationData, PlanData, PlanOptions, ProfileData, ProfilesData, ProfileOptions, MongoExecOptions, MongoPlanOptions, MongoQueryOptions, MongoReadCommand, MongoWriteCommand, PurgeData, QueryOptions, RemovedProfileData, Response, ResultData, RollbackTransactionData, RowsData, RowsOptions, StateQLActorOptions, StateQLOptions, StateQLSnapshot, StatusData, SessionData, SessionsData, SessionSummaryData, TransactionData } from "./types.js";
|
|
2
2
|
export declare class StateQL {
|
|
3
3
|
static forActor(options: StateQLActorOptions): StateQL;
|
|
4
4
|
private readonly store;
|
|
@@ -12,6 +12,7 @@ export declare class StateQL {
|
|
|
12
12
|
private readonly maxResultBytes;
|
|
13
13
|
private readonly timeoutMs;
|
|
14
14
|
private readonly signal?;
|
|
15
|
+
private readonly commandContexts;
|
|
15
16
|
private readonly credentialResolver?;
|
|
16
17
|
private readonly now;
|
|
17
18
|
private closed;
|
|
@@ -57,11 +58,11 @@ export declare class StateQL {
|
|
|
57
58
|
plan(sql: string, options?: PlanOptions): Promise<Response<PlanData>>;
|
|
58
59
|
mongoPlan(command: MongoWriteCommand, options?: MongoPlanOptions): Promise<Response<PlanData>>;
|
|
59
60
|
apply(planId: string, options?: ExecutionOptions): Promise<Response<ApplyData>>;
|
|
60
|
-
history(limit?: number): Promise<Response<HistoryData>>;
|
|
61
|
+
history(limit?: number, options?: HistoryOptions): Promise<Response<HistoryData>>;
|
|
61
62
|
doctor(): Promise<Response<DoctorData>>;
|
|
62
63
|
purge(scope?: "expired" | "results" | "history" | "all"): Promise<Response<PurgeData>>;
|
|
63
64
|
capabilities(): Promise<Response<CapabilitiesData>>;
|
|
64
|
-
executeCommand(command: BatchCommand): Promise<Response<unknown>>;
|
|
65
|
+
executeCommand(command: BatchCommand, context?: CommandExecutionContext): Promise<Response<unknown>>;
|
|
65
66
|
batch(commands: Iterable<BatchCommand> | AsyncIterable<BatchCommand>, options?: BatchOptions): AsyncGenerator<Response<unknown>>;
|
|
66
67
|
private performExec;
|
|
67
68
|
private performMongoExec;
|
package/dist/src/stateql.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
1
2
|
import { writeFileSync } from "node:fs";
|
|
2
3
|
import { basename, resolve } from "node:path";
|
|
3
4
|
import { env } from "node:process";
|
|
@@ -41,6 +42,7 @@ export class StateQL {
|
|
|
41
42
|
maxResultBytes;
|
|
42
43
|
timeoutMs;
|
|
43
44
|
signal;
|
|
45
|
+
commandContexts = new AsyncLocalStorage();
|
|
44
46
|
credentialResolver;
|
|
45
47
|
now;
|
|
46
48
|
closed = false;
|
|
@@ -1303,11 +1305,13 @@ export class StateQL {
|
|
|
1303
1305
|
}
|
|
1304
1306
|
}, () => historySql);
|
|
1305
1307
|
}
|
|
1306
|
-
async history(limit = 20) {
|
|
1308
|
+
async history(limit = 20, options = {}) {
|
|
1307
1309
|
return this.run("history", async (session) => ({
|
|
1308
1310
|
data: {
|
|
1309
1311
|
history: this.store
|
|
1310
|
-
.history(session.id, positiveInteger(limit, "limit")
|
|
1312
|
+
.history(session.id, positiveInteger(limit, "limit"), options.origin === undefined
|
|
1313
|
+
? undefined
|
|
1314
|
+
: parseCommandOrigin(options.origin))
|
|
1311
1315
|
.map(historyEntry),
|
|
1312
1316
|
},
|
|
1313
1317
|
}));
|
|
@@ -1364,169 +1368,180 @@ export class StateQL {
|
|
|
1364
1368
|
},
|
|
1365
1369
|
}));
|
|
1366
1370
|
}
|
|
1367
|
-
async executeCommand(command) {
|
|
1368
|
-
|
|
1369
|
-
return this.batchFailure("Batch command must be an object.");
|
|
1370
|
-
}
|
|
1371
|
+
async executeCommand(command, context = {}) {
|
|
1372
|
+
let activeContext;
|
|
1371
1373
|
try {
|
|
1372
|
-
|
|
1373
|
-
case "connect":
|
|
1374
|
-
return this.connect(command.target, {
|
|
1375
|
-
name: command.name,
|
|
1376
|
-
readOnly: command.read_only,
|
|
1377
|
-
secretEnv: command.secret_env,
|
|
1378
|
-
profile: command.profile,
|
|
1379
|
-
timeoutMs: command.timeout_ms,
|
|
1380
|
-
});
|
|
1381
|
-
case "disconnect":
|
|
1382
|
-
return this.disconnect();
|
|
1383
|
-
case "status":
|
|
1384
|
-
return this.status();
|
|
1385
|
-
case "profile.add":
|
|
1386
|
-
return this.addProfile(batchString(command.name, "name"), command.target, {
|
|
1387
|
-
readOnly: command.read_only ?? true,
|
|
1388
|
-
secretEnv: command.secret_env,
|
|
1389
|
-
});
|
|
1390
|
-
case "profile.list":
|
|
1391
|
-
return this.listProfiles();
|
|
1392
|
-
case "profile.show":
|
|
1393
|
-
return this.showProfile(batchString(command.name, "name"));
|
|
1394
|
-
case "profile.remove":
|
|
1395
|
-
return this.removeProfile(batchString(command.name, "name"));
|
|
1396
|
-
case "session.start":
|
|
1397
|
-
return this.startSession(batchString(command.name, "name"));
|
|
1398
|
-
case "session.list":
|
|
1399
|
-
return this.listSessions();
|
|
1400
|
-
case "session.show":
|
|
1401
|
-
return this.showSession(command.name);
|
|
1402
|
-
case "session.summary":
|
|
1403
|
-
return this.sessionSummary();
|
|
1404
|
-
case "session.close":
|
|
1405
|
-
return this.closeSession();
|
|
1406
|
-
case "query": {
|
|
1407
|
-
const response = await this.query(batchString(command.sql, "sql"), {
|
|
1408
|
-
params: command.params ?? [],
|
|
1409
|
-
cache: command.cache ?? "auto",
|
|
1410
|
-
timeoutMs: command.timeout_ms,
|
|
1411
|
-
});
|
|
1412
|
-
if (!response.ok || !command.as)
|
|
1413
|
-
return response;
|
|
1414
|
-
const resultId = response.data.result_id;
|
|
1415
|
-
if (typeof resultId !== "string")
|
|
1416
|
-
return response;
|
|
1417
|
-
this.store.setAlias(response.session_id, command.as, resultId);
|
|
1418
|
-
return {
|
|
1419
|
-
...response,
|
|
1420
|
-
data: { ...response.data, alias: command.as },
|
|
1421
|
-
};
|
|
1422
|
-
}
|
|
1423
|
-
case "mongo.query": {
|
|
1424
|
-
const response = await this.mongoQuery(command.mongo, {
|
|
1425
|
-
cache: command.cache ?? "auto",
|
|
1426
|
-
timeoutMs: command.timeout_ms,
|
|
1427
|
-
});
|
|
1428
|
-
if (!response.ok || !command.as)
|
|
1429
|
-
return response;
|
|
1430
|
-
const resultId = response.data.result_id;
|
|
1431
|
-
if (typeof resultId !== "string")
|
|
1432
|
-
return response;
|
|
1433
|
-
this.store.setAlias(response.session_id, command.as, resultId);
|
|
1434
|
-
return {
|
|
1435
|
-
...response,
|
|
1436
|
-
data: { ...response.data, alias: command.as },
|
|
1437
|
-
};
|
|
1438
|
-
}
|
|
1439
|
-
case "filter": {
|
|
1440
|
-
const response = await this.filter(batchString(command.handle, "handle"), batchString(command.where, "where"), { params: command.params ?? [] });
|
|
1441
|
-
if (!response.ok || !command.as)
|
|
1442
|
-
return response;
|
|
1443
|
-
const resultId = response.data.result_id;
|
|
1444
|
-
if (typeof resultId !== "string")
|
|
1445
|
-
return response;
|
|
1446
|
-
this.store.setAlias(response.session_id, command.as, resultId);
|
|
1447
|
-
return {
|
|
1448
|
-
...response,
|
|
1449
|
-
data: { ...response.data, alias: command.as },
|
|
1450
|
-
};
|
|
1451
|
-
}
|
|
1452
|
-
case "exec":
|
|
1453
|
-
return this.exec(batchString(command.sql, "sql"), {
|
|
1454
|
-
params: command.params ?? [],
|
|
1455
|
-
replay: command.replay ?? false,
|
|
1456
|
-
idempotencyKey: command.idempotency_key,
|
|
1457
|
-
allowUnbounded: command.allow_unbounded ?? false,
|
|
1458
|
-
allowDestructive: command.allow_destructive ?? false,
|
|
1459
|
-
timeoutMs: command.timeout_ms,
|
|
1460
|
-
});
|
|
1461
|
-
case "mongo.exec":
|
|
1462
|
-
return this.mongoExec(command.mongo, {
|
|
1463
|
-
replay: command.replay ?? false,
|
|
1464
|
-
idempotencyKey: command.idempotency_key,
|
|
1465
|
-
allowUnbounded: command.allow_unbounded ?? false,
|
|
1466
|
-
allowDestructive: command.allow_destructive ?? false,
|
|
1467
|
-
timeoutMs: command.timeout_ms,
|
|
1468
|
-
});
|
|
1469
|
-
case "show":
|
|
1470
|
-
return this.show(batchString(command.handle, "handle"));
|
|
1471
|
-
case "rows":
|
|
1472
|
-
return this.rows(batchString(command.handle, "handle"), {
|
|
1473
|
-
offset: command.offset ?? 0,
|
|
1474
|
-
limit: command.limit ?? 20,
|
|
1475
|
-
});
|
|
1476
|
-
case "count":
|
|
1477
|
-
return this.count(batchString(command.handle, "handle"));
|
|
1478
|
-
case "columns":
|
|
1479
|
-
return this.columns(batchString(command.handle, "handle"));
|
|
1480
|
-
case "alias.set":
|
|
1481
|
-
return this.setAlias(batchString(command.name, "name"), batchString(command.handle, "handle"));
|
|
1482
|
-
case "inspect":
|
|
1483
|
-
return this.inspect(batchString(command.kind, "kind"), command.table, {
|
|
1484
|
-
timeoutMs: command.timeout_ms,
|
|
1485
|
-
});
|
|
1486
|
-
case "transaction.begin":
|
|
1487
|
-
return this.beginTransaction(command.isolation);
|
|
1488
|
-
case "transaction.status":
|
|
1489
|
-
return this.transactionStatus(command.handle);
|
|
1490
|
-
case "transaction.commit":
|
|
1491
|
-
return this.commitTransaction(command.handle, {
|
|
1492
|
-
timeoutMs: command.timeout_ms,
|
|
1493
|
-
});
|
|
1494
|
-
case "transaction.rollback":
|
|
1495
|
-
return this.rollbackTransaction(command.handle);
|
|
1496
|
-
case "plan":
|
|
1497
|
-
return this.plan(batchString(command.sql, "sql"), {
|
|
1498
|
-
params: command.params ?? [],
|
|
1499
|
-
allowUnbounded: command.allow_unbounded ?? false,
|
|
1500
|
-
allowDestructive: command.allow_destructive,
|
|
1501
|
-
timeoutMs: command.timeout_ms,
|
|
1502
|
-
});
|
|
1503
|
-
case "mongo.plan":
|
|
1504
|
-
return this.mongoPlan(command.mongo, {
|
|
1505
|
-
allowUnbounded: command.allow_unbounded ?? false,
|
|
1506
|
-
allowDestructive: command.allow_destructive,
|
|
1507
|
-
timeoutMs: command.timeout_ms,
|
|
1508
|
-
});
|
|
1509
|
-
case "apply":
|
|
1510
|
-
return this.apply(batchString(command.handle, "handle"), {
|
|
1511
|
-
timeoutMs: command.timeout_ms,
|
|
1512
|
-
});
|
|
1513
|
-
case "history":
|
|
1514
|
-
return this.history(command.limit ?? 20);
|
|
1515
|
-
case "receipt":
|
|
1516
|
-
return this.receipt(batchString(command.handle, "handle"));
|
|
1517
|
-
case "doctor":
|
|
1518
|
-
return this.doctor();
|
|
1519
|
-
case "purge":
|
|
1520
|
-
return this.purge(command.scope ?? "expired");
|
|
1521
|
-
case "capabilities":
|
|
1522
|
-
return this.capabilities();
|
|
1523
|
-
default:
|
|
1524
|
-
return this.batchFailure(`Unknown batch command "${String(command.command)}".`);
|
|
1525
|
-
}
|
|
1374
|
+
activeContext = mergeCommandExecutionContext(this.commandContexts.getStore(), context);
|
|
1526
1375
|
}
|
|
1527
1376
|
catch (error) {
|
|
1528
1377
|
return this.batchFailure(errorMessage(error));
|
|
1529
1378
|
}
|
|
1379
|
+
return this.commandContexts.run(activeContext, async () => {
|
|
1380
|
+
if (!command || typeof command !== "object") {
|
|
1381
|
+
return this.batchFailure("Batch command must be an object.");
|
|
1382
|
+
}
|
|
1383
|
+
try {
|
|
1384
|
+
switch (command.command) {
|
|
1385
|
+
case "connect":
|
|
1386
|
+
return this.connect(command.target, {
|
|
1387
|
+
name: command.name,
|
|
1388
|
+
readOnly: command.read_only,
|
|
1389
|
+
secretEnv: command.secret_env,
|
|
1390
|
+
profile: command.profile,
|
|
1391
|
+
timeoutMs: command.timeout_ms,
|
|
1392
|
+
});
|
|
1393
|
+
case "disconnect":
|
|
1394
|
+
return this.disconnect();
|
|
1395
|
+
case "status":
|
|
1396
|
+
return this.status();
|
|
1397
|
+
case "profile.add":
|
|
1398
|
+
return this.addProfile(batchString(command.name, "name"), command.target, {
|
|
1399
|
+
readOnly: command.read_only ?? true,
|
|
1400
|
+
secretEnv: command.secret_env,
|
|
1401
|
+
});
|
|
1402
|
+
case "profile.list":
|
|
1403
|
+
return this.listProfiles();
|
|
1404
|
+
case "profile.show":
|
|
1405
|
+
return this.showProfile(batchString(command.name, "name"));
|
|
1406
|
+
case "profile.remove":
|
|
1407
|
+
return this.removeProfile(batchString(command.name, "name"));
|
|
1408
|
+
case "session.start":
|
|
1409
|
+
return this.startSession(batchString(command.name, "name"));
|
|
1410
|
+
case "session.list":
|
|
1411
|
+
return this.listSessions();
|
|
1412
|
+
case "session.show":
|
|
1413
|
+
return this.showSession(command.name);
|
|
1414
|
+
case "session.summary":
|
|
1415
|
+
return this.sessionSummary();
|
|
1416
|
+
case "session.close":
|
|
1417
|
+
return this.closeSession();
|
|
1418
|
+
case "query": {
|
|
1419
|
+
const response = await this.query(batchString(command.sql, "sql"), {
|
|
1420
|
+
params: command.params ?? [],
|
|
1421
|
+
cache: command.cache ?? "auto",
|
|
1422
|
+
timeoutMs: command.timeout_ms,
|
|
1423
|
+
});
|
|
1424
|
+
if (!response.ok || !command.as)
|
|
1425
|
+
return response;
|
|
1426
|
+
const resultId = response.data.result_id;
|
|
1427
|
+
if (typeof resultId !== "string")
|
|
1428
|
+
return response;
|
|
1429
|
+
this.store.setAlias(response.session_id, command.as, resultId);
|
|
1430
|
+
return {
|
|
1431
|
+
...response,
|
|
1432
|
+
data: { ...response.data, alias: command.as },
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1435
|
+
case "mongo.query": {
|
|
1436
|
+
const response = await this.mongoQuery(command.mongo, {
|
|
1437
|
+
cache: command.cache ?? "auto",
|
|
1438
|
+
timeoutMs: command.timeout_ms,
|
|
1439
|
+
});
|
|
1440
|
+
if (!response.ok || !command.as)
|
|
1441
|
+
return response;
|
|
1442
|
+
const resultId = response.data.result_id;
|
|
1443
|
+
if (typeof resultId !== "string")
|
|
1444
|
+
return response;
|
|
1445
|
+
this.store.setAlias(response.session_id, command.as, resultId);
|
|
1446
|
+
return {
|
|
1447
|
+
...response,
|
|
1448
|
+
data: { ...response.data, alias: command.as },
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
case "filter": {
|
|
1452
|
+
const response = await this.filter(batchString(command.handle, "handle"), batchString(command.where, "where"), { params: command.params ?? [] });
|
|
1453
|
+
if (!response.ok || !command.as)
|
|
1454
|
+
return response;
|
|
1455
|
+
const resultId = response.data.result_id;
|
|
1456
|
+
if (typeof resultId !== "string")
|
|
1457
|
+
return response;
|
|
1458
|
+
this.store.setAlias(response.session_id, command.as, resultId);
|
|
1459
|
+
return {
|
|
1460
|
+
...response,
|
|
1461
|
+
data: { ...response.data, alias: command.as },
|
|
1462
|
+
};
|
|
1463
|
+
}
|
|
1464
|
+
case "exec":
|
|
1465
|
+
return this.exec(batchString(command.sql, "sql"), {
|
|
1466
|
+
params: command.params ?? [],
|
|
1467
|
+
replay: command.replay ?? false,
|
|
1468
|
+
idempotencyKey: command.idempotency_key,
|
|
1469
|
+
allowUnbounded: command.allow_unbounded ?? false,
|
|
1470
|
+
allowDestructive: command.allow_destructive ?? false,
|
|
1471
|
+
timeoutMs: command.timeout_ms,
|
|
1472
|
+
});
|
|
1473
|
+
case "mongo.exec":
|
|
1474
|
+
return this.mongoExec(command.mongo, {
|
|
1475
|
+
replay: command.replay ?? false,
|
|
1476
|
+
idempotencyKey: command.idempotency_key,
|
|
1477
|
+
allowUnbounded: command.allow_unbounded ?? false,
|
|
1478
|
+
allowDestructive: command.allow_destructive ?? false,
|
|
1479
|
+
timeoutMs: command.timeout_ms,
|
|
1480
|
+
});
|
|
1481
|
+
case "show":
|
|
1482
|
+
return this.show(batchString(command.handle, "handle"));
|
|
1483
|
+
case "rows":
|
|
1484
|
+
return this.rows(batchString(command.handle, "handle"), {
|
|
1485
|
+
offset: command.offset ?? 0,
|
|
1486
|
+
limit: command.limit ?? 20,
|
|
1487
|
+
});
|
|
1488
|
+
case "count":
|
|
1489
|
+
return this.count(batchString(command.handle, "handle"));
|
|
1490
|
+
case "columns":
|
|
1491
|
+
return this.columns(batchString(command.handle, "handle"));
|
|
1492
|
+
case "alias.set":
|
|
1493
|
+
return this.setAlias(batchString(command.name, "name"), batchString(command.handle, "handle"));
|
|
1494
|
+
case "inspect":
|
|
1495
|
+
return this.inspect(batchString(command.kind, "kind"), command.table, {
|
|
1496
|
+
timeoutMs: command.timeout_ms,
|
|
1497
|
+
});
|
|
1498
|
+
case "transaction.begin":
|
|
1499
|
+
return this.beginTransaction(command.isolation);
|
|
1500
|
+
case "transaction.status":
|
|
1501
|
+
return this.transactionStatus(command.handle);
|
|
1502
|
+
case "transaction.commit":
|
|
1503
|
+
return this.commitTransaction(command.handle, {
|
|
1504
|
+
timeoutMs: command.timeout_ms,
|
|
1505
|
+
});
|
|
1506
|
+
case "transaction.rollback":
|
|
1507
|
+
return this.rollbackTransaction(command.handle);
|
|
1508
|
+
case "plan":
|
|
1509
|
+
return this.plan(batchString(command.sql, "sql"), {
|
|
1510
|
+
params: command.params ?? [],
|
|
1511
|
+
allowUnbounded: command.allow_unbounded ?? false,
|
|
1512
|
+
allowDestructive: command.allow_destructive,
|
|
1513
|
+
timeoutMs: command.timeout_ms,
|
|
1514
|
+
});
|
|
1515
|
+
case "mongo.plan":
|
|
1516
|
+
return this.mongoPlan(command.mongo, {
|
|
1517
|
+
allowUnbounded: command.allow_unbounded ?? false,
|
|
1518
|
+
allowDestructive: command.allow_destructive,
|
|
1519
|
+
timeoutMs: command.timeout_ms,
|
|
1520
|
+
});
|
|
1521
|
+
case "apply":
|
|
1522
|
+
return this.apply(batchString(command.handle, "handle"), {
|
|
1523
|
+
timeoutMs: command.timeout_ms,
|
|
1524
|
+
});
|
|
1525
|
+
case "history":
|
|
1526
|
+
return this.history(command.limit ?? 20, {
|
|
1527
|
+
origin: command.history_origin,
|
|
1528
|
+
});
|
|
1529
|
+
case "receipt":
|
|
1530
|
+
return this.receipt(batchString(command.handle, "handle"));
|
|
1531
|
+
case "doctor":
|
|
1532
|
+
return this.doctor();
|
|
1533
|
+
case "purge":
|
|
1534
|
+
return this.purge(command.scope ?? "expired");
|
|
1535
|
+
case "capabilities":
|
|
1536
|
+
return this.capabilities();
|
|
1537
|
+
default:
|
|
1538
|
+
return this.batchFailure(`Unknown batch command "${String(command.command)}".`);
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
catch (error) {
|
|
1542
|
+
return this.batchFailure(errorMessage(error));
|
|
1543
|
+
}
|
|
1544
|
+
});
|
|
1530
1545
|
}
|
|
1531
1546
|
async *batch(commands, options = {}) {
|
|
1532
1547
|
const maxCommands = options.maxCommands ?? 1_000;
|
|
@@ -1541,7 +1556,7 @@ export class StateQL {
|
|
|
1541
1556
|
yield await this.batchFailure(`Batch cannot exceed ${maxCommands} commands.`);
|
|
1542
1557
|
return;
|
|
1543
1558
|
}
|
|
1544
|
-
const response = await this.executeCommand(command);
|
|
1559
|
+
const response = await this.executeCommand(command, options.executionContext);
|
|
1545
1560
|
yield response;
|
|
1546
1561
|
if (!response.ok && !options.continueOnError)
|
|
1547
1562
|
return;
|
|
@@ -2074,7 +2089,7 @@ export class StateQL {
|
|
|
2074
2089
|
}
|
|
2075
2090
|
}
|
|
2076
2091
|
executionContext(options) {
|
|
2077
|
-
return createAdapterContext(executionTimeout(options.timeoutMs ?? this.timeoutMs), options.signal
|
|
2092
|
+
return createAdapterContext(executionTimeout(options.timeoutMs ?? this.timeoutMs), combineAbortSignals(options.signal, this.commandContexts.getStore()?.signal, this.signal));
|
|
2078
2093
|
}
|
|
2079
2094
|
resultData(result, cached) {
|
|
2080
2095
|
const rows = this.store.resultRows(result);
|
|
@@ -2104,6 +2119,7 @@ export class StateQL {
|
|
|
2104
2119
|
}
|
|
2105
2120
|
async run(command, action, historySql) {
|
|
2106
2121
|
const started = performance.now();
|
|
2122
|
+
const origin = this.commandContexts.getStore()?.origin ?? "legacy";
|
|
2107
2123
|
let session = this.store.ensureSession(this.sessionName);
|
|
2108
2124
|
const commandId = this.store.nextId("cmd");
|
|
2109
2125
|
if (!this.store.isSessionMember(session.id, this.actorId)) {
|
|
@@ -2126,6 +2142,7 @@ export class StateQL {
|
|
|
2126
2142
|
id: commandId,
|
|
2127
2143
|
sessionId: session.id,
|
|
2128
2144
|
actorId: this.actorId,
|
|
2145
|
+
origin,
|
|
2129
2146
|
command,
|
|
2130
2147
|
...(result.handle ? { handle: result.handle } : {}),
|
|
2131
2148
|
...(sqlText !== undefined ? { sql: sqlText } : {}),
|
|
@@ -2157,6 +2174,7 @@ export class StateQL {
|
|
|
2157
2174
|
id: commandId,
|
|
2158
2175
|
sessionId: session.id,
|
|
2159
2176
|
actorId: this.actorId,
|
|
2177
|
+
origin,
|
|
2160
2178
|
command,
|
|
2161
2179
|
...(sqlText !== undefined ? { sql: sqlText } : {}),
|
|
2162
2180
|
executed: stateqlError.details.executed,
|
|
@@ -2185,6 +2203,7 @@ function historyEntry(item) {
|
|
|
2185
2203
|
timestamp: item.timestamp,
|
|
2186
2204
|
session_id: item.session_id,
|
|
2187
2205
|
actor_id: item.actor_id,
|
|
2206
|
+
origin: item.origin,
|
|
2188
2207
|
command: item.command,
|
|
2189
2208
|
sql: item.sql,
|
|
2190
2209
|
handle: item.handle,
|
|
@@ -2194,6 +2213,41 @@ function historyEntry(item) {
|
|
|
2194
2213
|
error_code: item.error_code,
|
|
2195
2214
|
};
|
|
2196
2215
|
}
|
|
2216
|
+
const COMMAND_ORIGINS = new Set([
|
|
2217
|
+
"legacy",
|
|
2218
|
+
"user",
|
|
2219
|
+
"model",
|
|
2220
|
+
"system",
|
|
2221
|
+
"api",
|
|
2222
|
+
]);
|
|
2223
|
+
function parseCommandOrigin(value) {
|
|
2224
|
+
if (typeof value === "string" && COMMAND_ORIGINS.has(value)) {
|
|
2225
|
+
return value;
|
|
2226
|
+
}
|
|
2227
|
+
throw new StateQLError("INVALID_COMMAND", `Unknown command origin "${String(value)}".`);
|
|
2228
|
+
}
|
|
2229
|
+
function mergeCommandExecutionContext(inherited, supplied) {
|
|
2230
|
+
if (!supplied || typeof supplied !== "object") {
|
|
2231
|
+
throw new StateQLError("INVALID_COMMAND", "Command execution context must be an object.");
|
|
2232
|
+
}
|
|
2233
|
+
if (supplied.signal !== undefined && !(supplied.signal instanceof AbortSignal)) {
|
|
2234
|
+
throw new StateQLError("INVALID_COMMAND", "Command execution context signal must be an AbortSignal.");
|
|
2235
|
+
}
|
|
2236
|
+
return {
|
|
2237
|
+
signal: combineAbortSignals(inherited?.signal, supplied.signal),
|
|
2238
|
+
origin: supplied.origin === undefined
|
|
2239
|
+
? inherited?.origin
|
|
2240
|
+
: parseCommandOrigin(supplied.origin),
|
|
2241
|
+
};
|
|
2242
|
+
}
|
|
2243
|
+
function combineAbortSignals(...signals) {
|
|
2244
|
+
const present = signals.filter((signal) => signal !== undefined);
|
|
2245
|
+
if (present.length === 0)
|
|
2246
|
+
return undefined;
|
|
2247
|
+
if (present.length === 1)
|
|
2248
|
+
return present[0];
|
|
2249
|
+
return AbortSignal.any(present);
|
|
2250
|
+
}
|
|
2197
2251
|
function markTransactionOutcomeUnknown(store, transactionId, sessionId, actorId) {
|
|
2198
2252
|
try {
|
|
2199
2253
|
store.markTransactionOutcomeUnknown(transactionId, sessionId, actorId);
|
package/dist/src/store.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DatabaseSync } from "node:sqlite";
|
|
2
|
-
import type { Column, Driver, MongoWriteOutcome, Row, SqlParameters, StateConfidence } from "./types.js";
|
|
2
|
+
import type { Column, CommandOrigin, Driver, MongoWriteOutcome, Row, SqlParameters, StateConfidence } from "./types.js";
|
|
3
3
|
export interface SessionRecord {
|
|
4
4
|
id: string;
|
|
5
5
|
name: string;
|
|
@@ -98,6 +98,7 @@ export interface HistoryRecord {
|
|
|
98
98
|
timestamp: string;
|
|
99
99
|
session_id: string;
|
|
100
100
|
actor_id: string;
|
|
101
|
+
origin: CommandOrigin;
|
|
101
102
|
command: string;
|
|
102
103
|
sql: string | null;
|
|
103
104
|
handle: string | null;
|
|
@@ -270,6 +271,7 @@ export declare class StateStore {
|
|
|
270
271
|
addHistory(input: {
|
|
271
272
|
sessionId: string;
|
|
272
273
|
actorId: string;
|
|
274
|
+
origin?: CommandOrigin;
|
|
273
275
|
command: string;
|
|
274
276
|
sql?: string;
|
|
275
277
|
handle?: string;
|
|
@@ -279,7 +281,7 @@ export declare class StateStore {
|
|
|
279
281
|
errorCode?: string;
|
|
280
282
|
id?: string;
|
|
281
283
|
}): HistoryRecord;
|
|
282
|
-
history(sessionId: string, limit: number): HistoryRecord[];
|
|
284
|
+
history(sessionId: string, limit: number, origin?: CommandOrigin): HistoryRecord[];
|
|
283
285
|
recentOperations(sessionId: string, limit: number): OperationRecord[];
|
|
284
286
|
knownResults(sessionId: string, limit: number): Array<ResultRecord & {
|
|
285
287
|
alias: string | null;
|
package/dist/src/store.js
CHANGED
|
@@ -795,10 +795,10 @@ export class StateStore {
|
|
|
795
795
|
const id = input.id ?? this.nextId("cmd");
|
|
796
796
|
this.db
|
|
797
797
|
.prepare(`INSERT INTO history
|
|
798
|
-
(id, timestamp, session_id, actor_id, command, sql, handle,
|
|
799
|
-
cached, success, error_code)
|
|
800
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
801
|
-
.run(id, this.now().toISOString(), input.sessionId, input.actorId, input.command, boundedHistorySql(input.sql), input.handle ?? null, input.executed ? 1 : 0, input.cached ? 1 : 0, input.success ? 1 : 0, input.errorCode ?? null);
|
|
798
|
+
(id, timestamp, session_id, actor_id, origin, command, sql, handle,
|
|
799
|
+
executed, cached, success, error_code)
|
|
800
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
801
|
+
.run(id, this.now().toISOString(), input.sessionId, input.actorId, input.origin ?? "legacy", input.command, boundedHistorySql(input.sql), input.handle ?? null, input.executed ? 1 : 0, input.cached ? 1 : 0, input.success ? 1 : 0, input.errorCode ?? null);
|
|
802
802
|
this.db
|
|
803
803
|
.prepare(`DELETE FROM history
|
|
804
804
|
WHERE rowid IN (
|
|
@@ -812,13 +812,13 @@ export class StateStore {
|
|
|
812
812
|
.prepare("SELECT * FROM history WHERE id = ?")
|
|
813
813
|
.get(id);
|
|
814
814
|
}
|
|
815
|
-
history(sessionId, limit) {
|
|
815
|
+
history(sessionId, limit, origin) {
|
|
816
816
|
return this.db
|
|
817
817
|
.prepare(`SELECT * FROM history
|
|
818
|
-
WHERE session_id = ?
|
|
818
|
+
WHERE session_id = ?${origin === undefined ? "" : " AND origin = ?"}
|
|
819
819
|
ORDER BY rowid DESC
|
|
820
820
|
LIMIT ?`)
|
|
821
|
-
.all(sessionId, limit);
|
|
821
|
+
.all(...(origin === undefined ? [sessionId, limit] : [sessionId, origin, limit]));
|
|
822
822
|
}
|
|
823
823
|
recentOperations(sessionId, limit) {
|
|
824
824
|
return this.db
|
package/dist/src/types.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
export type SqlDriver = "sqlite" | "postgres" | "mysql";
|
|
2
2
|
export type Driver = SqlDriver | "mongodb";
|
|
3
|
+
export type CommandOrigin = "legacy" | "user" | "model" | "system" | "api";
|
|
4
|
+
/** Trusted host metadata for one executeCommand call; never part of BatchCommand input. */
|
|
5
|
+
export interface CommandExecutionContext {
|
|
6
|
+
signal?: AbortSignal;
|
|
7
|
+
origin?: CommandOrigin;
|
|
8
|
+
}
|
|
3
9
|
export type CredentialAccess = "read" | "write";
|
|
4
10
|
export type CredentialOperation = "connect" | "query" | "inspect" | "plan" | "exec" | "apply" | "transaction.commit";
|
|
5
11
|
export interface CredentialRequest {
|
|
@@ -64,6 +70,7 @@ export interface HistoryEntry {
|
|
|
64
70
|
timestamp: string;
|
|
65
71
|
session_id: string;
|
|
66
72
|
actor_id: string;
|
|
73
|
+
origin: CommandOrigin;
|
|
67
74
|
command: string;
|
|
68
75
|
sql: string | null;
|
|
69
76
|
handle: string | null;
|
|
@@ -184,6 +191,9 @@ export interface ExecutionOptions {
|
|
|
184
191
|
timeoutMs?: number;
|
|
185
192
|
signal?: AbortSignal;
|
|
186
193
|
}
|
|
194
|
+
export interface HistoryOptions {
|
|
195
|
+
origin?: CommandOrigin;
|
|
196
|
+
}
|
|
187
197
|
export interface StateQLOptions extends ExecutionOptions {
|
|
188
198
|
home?: string;
|
|
189
199
|
session?: string;
|
|
@@ -272,11 +282,14 @@ export interface BatchCommand {
|
|
|
272
282
|
limit?: number;
|
|
273
283
|
isolation?: string;
|
|
274
284
|
timeout_ms?: number;
|
|
285
|
+
/** Retrieval filter for the history command; does not attribute this command. */
|
|
286
|
+
history_origin?: CommandOrigin;
|
|
275
287
|
scope?: "expired" | "results" | "history" | "all";
|
|
276
288
|
}
|
|
277
289
|
export interface BatchOptions {
|
|
278
290
|
continueOnError?: boolean;
|
|
279
291
|
maxCommands?: number;
|
|
292
|
+
executionContext?: CommandExecutionContext;
|
|
280
293
|
}
|
|
281
294
|
export interface Column {
|
|
282
295
|
name: string;
|