@mastra/spanner 1.6.6 → 1.7.0-alpha.1
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/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/index.cjs +147 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +147 -49
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/datasets/index.d.ts.map +1 -1
- package/dist/storage/domains/memory/index.d.ts +14 -0
- package/dist/storage/domains/memory/index.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -3325,8 +3325,8 @@ var DatasetsSpanner = class DatasetsSpanner extends DatasetsStorage {
|
|
|
3325
3325
|
});
|
|
3326
3326
|
}
|
|
3327
3327
|
/** Reads the current live row for an item (validTo IS NULL, not deleted). */
|
|
3328
|
-
async loadCurrentItemRow(itemId) {
|
|
3329
|
-
const [rows] = await
|
|
3328
|
+
async loadCurrentItemRow(tx, itemId) {
|
|
3329
|
+
const [rows] = await tx.run({
|
|
3330
3330
|
sql: `SELECT * FROM ${quoteIdent(TABLE_DATASET_ITEMS, "table name")}
|
|
3331
3331
|
WHERE ${quoteIdent("id", "column name")} = @id
|
|
3332
3332
|
AND ${quoteIdent("validTo", "column name")} IS NULL
|
|
@@ -3409,39 +3409,49 @@ var DatasetsSpanner = class DatasetsSpanner extends DatasetsStorage {
|
|
|
3409
3409
|
}
|
|
3410
3410
|
async _doUpdateItem(args) {
|
|
3411
3411
|
try {
|
|
3412
|
-
const existing = await this.loadCurrentItemRow(args.id);
|
|
3413
|
-
if (!existing) throw new MastraError({
|
|
3414
|
-
id: createStorageErrorId("SPANNER", "UPDATE_DATASET_ITEM", "NOT_FOUND"),
|
|
3415
|
-
domain: ErrorDomain.STORAGE,
|
|
3416
|
-
category: ErrorCategory.USER,
|
|
3417
|
-
text: `Dataset item not found: ${args.id}`,
|
|
3418
|
-
details: { id: args.id }
|
|
3419
|
-
});
|
|
3420
|
-
if (existing.datasetId !== args.datasetId) throw new MastraError({
|
|
3421
|
-
id: createStorageErrorId("SPANNER", "UPDATE_DATASET_ITEM", "DATASET_MISMATCH"),
|
|
3422
|
-
domain: ErrorDomain.STORAGE,
|
|
3423
|
-
category: ErrorCategory.USER,
|
|
3424
|
-
text: `Dataset item ${args.id} does not belong to dataset ${args.datasetId}`,
|
|
3425
|
-
details: {
|
|
3426
|
-
id: args.id,
|
|
3427
|
-
datasetId: args.datasetId
|
|
3428
|
-
}
|
|
3429
|
-
});
|
|
3430
|
-
const merged = {
|
|
3431
|
-
input: args.input !== void 0 ? args.input : existing.input,
|
|
3432
|
-
groundTruth: args.groundTruth !== void 0 ? args.groundTruth : existing.groundTruth,
|
|
3433
|
-
expectedTrajectory: args.expectedTrajectory !== void 0 ? args.expectedTrajectory : existing.expectedTrajectory,
|
|
3434
|
-
toolMocks: args.toolMocks !== void 0 ? args.toolMocks : existing.toolMocks,
|
|
3435
|
-
unmockedToolPolicy: args.unmockedToolPolicy !== void 0 ? args.unmockedToolPolicy : existing.unmockedToolPolicy,
|
|
3436
|
-
scorerIds: args.scorerIds !== void 0 ? args.scorerIds ?? void 0 : existing.scorerIds,
|
|
3437
|
-
requestContext: args.requestContext !== void 0 ? args.requestContext : existing.requestContext,
|
|
3438
|
-
metadata: args.metadata !== void 0 ? args.metadata : existing.metadata,
|
|
3439
|
-
source: args.source !== void 0 ? args.source : existing.source
|
|
3440
|
-
};
|
|
3441
3412
|
const now = /* @__PURE__ */ new Date();
|
|
3442
3413
|
let updated = null;
|
|
3443
3414
|
await this.db.runWithAbortRetry(() => this.database.runTransactionAsync(async (tx) => {
|
|
3444
3415
|
try {
|
|
3416
|
+
const existing = await this.loadCurrentItemRow(tx, args.id);
|
|
3417
|
+
if (!existing) throw new MastraError({
|
|
3418
|
+
id: createStorageErrorId("SPANNER", "UPDATE_DATASET_ITEM", "NOT_FOUND"),
|
|
3419
|
+
domain: ErrorDomain.STORAGE,
|
|
3420
|
+
category: ErrorCategory.USER,
|
|
3421
|
+
text: `Dataset item not found: ${args.id}`,
|
|
3422
|
+
details: { id: args.id }
|
|
3423
|
+
});
|
|
3424
|
+
if (existing.datasetId !== args.datasetId) throw new MastraError({
|
|
3425
|
+
id: createStorageErrorId("SPANNER", "UPDATE_DATASET_ITEM", "DATASET_MISMATCH"),
|
|
3426
|
+
domain: ErrorDomain.STORAGE,
|
|
3427
|
+
category: ErrorCategory.USER,
|
|
3428
|
+
text: `Dataset item ${args.id} does not belong to dataset ${args.datasetId}`,
|
|
3429
|
+
details: {
|
|
3430
|
+
id: args.id,
|
|
3431
|
+
datasetId: args.datasetId
|
|
3432
|
+
}
|
|
3433
|
+
});
|
|
3434
|
+
if (existing.metadata?.__purged === true) throw new MastraError({
|
|
3435
|
+
id: "DATASET_ITEM_PURGED",
|
|
3436
|
+
domain: ErrorDomain.STORAGE,
|
|
3437
|
+
category: ErrorCategory.USER,
|
|
3438
|
+
details: {
|
|
3439
|
+
datasetId: args.datasetId,
|
|
3440
|
+
itemId: args.id
|
|
3441
|
+
},
|
|
3442
|
+
text: `Purged dataset item cannot be updated: ${args.id}`
|
|
3443
|
+
});
|
|
3444
|
+
const merged = {
|
|
3445
|
+
input: args.input !== void 0 ? args.input : existing.input,
|
|
3446
|
+
groundTruth: args.groundTruth !== void 0 ? args.groundTruth : existing.groundTruth,
|
|
3447
|
+
expectedTrajectory: args.expectedTrajectory !== void 0 ? args.expectedTrajectory : existing.expectedTrajectory,
|
|
3448
|
+
toolMocks: args.toolMocks !== void 0 ? args.toolMocks : existing.toolMocks,
|
|
3449
|
+
unmockedToolPolicy: args.unmockedToolPolicy !== void 0 ? args.unmockedToolPolicy : existing.unmockedToolPolicy,
|
|
3450
|
+
scorerIds: args.scorerIds !== void 0 ? args.scorerIds ?? void 0 : existing.scorerIds,
|
|
3451
|
+
requestContext: args.requestContext !== void 0 ? args.requestContext : existing.requestContext,
|
|
3452
|
+
metadata: args.metadata !== void 0 ? args.metadata : existing.metadata,
|
|
3453
|
+
source: args.source !== void 0 ? args.source : existing.source
|
|
3454
|
+
};
|
|
3445
3455
|
const { version: newVersion, organizationId, projectId } = await this.bumpVersion(tx, args.datasetId, now);
|
|
3446
3456
|
await this.closeCurrentRow(tx, args.id, newVersion);
|
|
3447
3457
|
await this.db.insert({
|
|
@@ -3510,21 +3520,24 @@ var DatasetsSpanner = class DatasetsSpanner extends DatasetsStorage {
|
|
|
3510
3520
|
}
|
|
3511
3521
|
async _doDeleteItem(args) {
|
|
3512
3522
|
try {
|
|
3513
|
-
const existing = await this.loadCurrentItemRow(args.id);
|
|
3514
|
-
if (!existing) return;
|
|
3515
|
-
if (existing.datasetId !== args.datasetId) throw new MastraError({
|
|
3516
|
-
id: createStorageErrorId("SPANNER", "DELETE_DATASET_ITEM", "DATASET_MISMATCH"),
|
|
3517
|
-
domain: ErrorDomain.STORAGE,
|
|
3518
|
-
category: ErrorCategory.USER,
|
|
3519
|
-
text: `Dataset item ${args.id} does not belong to dataset ${args.datasetId}`,
|
|
3520
|
-
details: {
|
|
3521
|
-
id: args.id,
|
|
3522
|
-
datasetId: args.datasetId
|
|
3523
|
-
}
|
|
3524
|
-
});
|
|
3525
3523
|
const now = /* @__PURE__ */ new Date();
|
|
3526
3524
|
await this.db.runWithAbortRetry(() => this.database.runTransactionAsync(async (tx) => {
|
|
3527
3525
|
try {
|
|
3526
|
+
const existing = await this.loadCurrentItemRow(tx, args.id);
|
|
3527
|
+
if (!existing) {
|
|
3528
|
+
await tx.commit();
|
|
3529
|
+
return;
|
|
3530
|
+
}
|
|
3531
|
+
if (existing.datasetId !== args.datasetId) throw new MastraError({
|
|
3532
|
+
id: createStorageErrorId("SPANNER", "DELETE_DATASET_ITEM", "DATASET_MISMATCH"),
|
|
3533
|
+
domain: ErrorDomain.STORAGE,
|
|
3534
|
+
category: ErrorCategory.USER,
|
|
3535
|
+
text: `Dataset item ${args.id} does not belong to dataset ${args.datasetId}`,
|
|
3536
|
+
details: {
|
|
3537
|
+
id: args.id,
|
|
3538
|
+
datasetId: args.datasetId
|
|
3539
|
+
}
|
|
3540
|
+
});
|
|
3528
3541
|
const { version: newVersion, organizationId, projectId } = await this.bumpVersion(tx, args.datasetId, now);
|
|
3529
3542
|
await this.closeCurrentRow(tx, args.id, newVersion);
|
|
3530
3543
|
await this.db.insert({
|
|
@@ -3991,15 +4004,18 @@ var DatasetsSpanner = class DatasetsSpanner extends DatasetsStorage {
|
|
|
3991
4004
|
}
|
|
3992
4005
|
async _doBatchDeleteItems(input) {
|
|
3993
4006
|
try {
|
|
3994
|
-
const current = [];
|
|
3995
|
-
for (const itemId of input.itemIds) {
|
|
3996
|
-
const row = await this.loadCurrentItemRow(itemId);
|
|
3997
|
-
if (row && row.datasetId === input.datasetId) current.push(row);
|
|
3998
|
-
}
|
|
3999
|
-
if (current.length === 0) return;
|
|
4000
4007
|
const now = /* @__PURE__ */ new Date();
|
|
4001
4008
|
await this.db.runWithAbortRetry(() => this.database.runTransactionAsync(async (tx) => {
|
|
4002
4009
|
try {
|
|
4010
|
+
const current = [];
|
|
4011
|
+
for (const itemId of input.itemIds) {
|
|
4012
|
+
const row = await this.loadCurrentItemRow(tx, itemId);
|
|
4013
|
+
if (row && row.datasetId === input.datasetId) current.push(row);
|
|
4014
|
+
}
|
|
4015
|
+
if (current.length === 0) {
|
|
4016
|
+
await tx.commit();
|
|
4017
|
+
return;
|
|
4018
|
+
}
|
|
4003
4019
|
const { version: newVersion, organizationId, projectId } = await this.bumpVersion(tx, input.datasetId, now);
|
|
4004
4020
|
for (const existing of current) {
|
|
4005
4021
|
await this.closeCurrentRow(tx, existing.id, newVersion);
|
|
@@ -6942,6 +6958,88 @@ var MemorySpanner = class MemorySpanner extends MemoryStorage {
|
|
|
6942
6958
|
}, error);
|
|
6943
6959
|
}
|
|
6944
6960
|
}
|
|
6961
|
+
/**
|
|
6962
|
+
* Atomically reassign a thread and all of its messages to a different resource.
|
|
6963
|
+
*
|
|
6964
|
+
* Runs inside a single read-write transaction: the thread row is read inside the
|
|
6965
|
+
* transaction (acquiring a lock), then the thread and every message are moved together.
|
|
6966
|
+
* Overlapping transfers of the same thread serialize on the transaction, so they can never
|
|
6967
|
+
* interleave the thread update with the message update — either both move or neither does,
|
|
6968
|
+
* with no split-ownership window. The thread's `createdAt` is preserved. Callers are
|
|
6969
|
+
* responsible for authorizing the reassignment.
|
|
6970
|
+
*/
|
|
6971
|
+
async updateThreadResourceId({ threadId, resourceId }) {
|
|
6972
|
+
const tableThreads = quoteIdent(TABLE_THREADS, "table name");
|
|
6973
|
+
const messagesTable = quoteIdent(TABLE_MESSAGES, "table name");
|
|
6974
|
+
const now = /* @__PURE__ */ new Date();
|
|
6975
|
+
let existingThread = null;
|
|
6976
|
+
let noop = false;
|
|
6977
|
+
try {
|
|
6978
|
+
await this.db.runWithAbortRetry(() => this.database.runTransactionAsync(async (tx) => {
|
|
6979
|
+
try {
|
|
6980
|
+
const [rows] = await tx.run({
|
|
6981
|
+
sql: `SELECT * FROM ${tableThreads} WHERE id = @threadId LIMIT 1`,
|
|
6982
|
+
params: { threadId },
|
|
6983
|
+
json: true
|
|
6984
|
+
});
|
|
6985
|
+
const row = rows[0];
|
|
6986
|
+
if (!row) throw new MastraError({
|
|
6987
|
+
id: createStorageErrorId("SPANNER", "UPDATE_THREAD_RESOURCE_ID", "NOT_FOUND"),
|
|
6988
|
+
domain: ErrorDomain.STORAGE,
|
|
6989
|
+
category: ErrorCategory.USER,
|
|
6990
|
+
text: `Thread ${threadId} not found`,
|
|
6991
|
+
details: { threadId }
|
|
6992
|
+
});
|
|
6993
|
+
existingThread = this.formatThreadRow(row);
|
|
6994
|
+
if (existingThread.resourceId === resourceId) {
|
|
6995
|
+
noop = true;
|
|
6996
|
+
await tx.commit();
|
|
6997
|
+
return;
|
|
6998
|
+
}
|
|
6999
|
+
await this.db.update({
|
|
7000
|
+
tableName: TABLE_THREADS,
|
|
7001
|
+
keys: { id: threadId },
|
|
7002
|
+
data: {
|
|
7003
|
+
resourceId,
|
|
7004
|
+
updatedAt: now
|
|
7005
|
+
},
|
|
7006
|
+
transaction: tx
|
|
7007
|
+
});
|
|
7008
|
+
await tx.runUpdate({
|
|
7009
|
+
sql: `UPDATE ${messagesTable} SET ${quoteIdent("resourceId", "column name")} = @resourceId WHERE ${quoteIdent("thread_id", "column name")} = @threadId`,
|
|
7010
|
+
params: {
|
|
7011
|
+
resourceId,
|
|
7012
|
+
threadId
|
|
7013
|
+
}
|
|
7014
|
+
});
|
|
7015
|
+
await tx.commit();
|
|
7016
|
+
} catch (err) {
|
|
7017
|
+
await tx.rollback().catch((rollbackErr) => {
|
|
7018
|
+
throw new AggregateError([err, rollbackErr], "Transaction and rollback both failed");
|
|
7019
|
+
});
|
|
7020
|
+
throw err;
|
|
7021
|
+
}
|
|
7022
|
+
}));
|
|
7023
|
+
const thread = existingThread;
|
|
7024
|
+
if (noop) return thread;
|
|
7025
|
+
return {
|
|
7026
|
+
...thread,
|
|
7027
|
+
resourceId,
|
|
7028
|
+
updatedAt: now
|
|
7029
|
+
};
|
|
7030
|
+
} catch (error) {
|
|
7031
|
+
if (error instanceof MastraError) throw error;
|
|
7032
|
+
throw new MastraError({
|
|
7033
|
+
id: createStorageErrorId("SPANNER", "UPDATE_THREAD_RESOURCE_ID", "FAILED"),
|
|
7034
|
+
domain: ErrorDomain.STORAGE,
|
|
7035
|
+
category: ErrorCategory.THIRD_PARTY,
|
|
7036
|
+
details: {
|
|
7037
|
+
threadId,
|
|
7038
|
+
resourceId
|
|
7039
|
+
}
|
|
7040
|
+
}, error);
|
|
7041
|
+
}
|
|
7042
|
+
}
|
|
6945
7043
|
/** Deletes a thread and all its messages atomically. */
|
|
6946
7044
|
async deleteThread({ threadId }) {
|
|
6947
7045
|
const messagesTable = quoteIdent(TABLE_MESSAGES, "table name");
|