@mastra/spanner 1.2.4-alpha.1 → 1.3.0-alpha.2
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/CHANGELOG.md +26 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/index.cjs +75 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +76 -40
- package/dist/index.js.map +1 -1
- package/dist/storage/domains/datasets/index.d.ts.map +1 -1
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# @mastra/spanner
|
|
2
2
|
|
|
3
|
+
## 1.3.0-alpha.2
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Added atomic caller-defined dataset IDs for idempotent dataset creation across built-in storage adapters. Supplying `id` to `mastra.datasets.create()` now creates the dataset once and resolves compatible retries to the persisted record; incompatible immutable identity fields throw `DATASET_ID_CONFLICT`. ([#19370](https://github.com/mastra-ai/mastra/pull/19370))
|
|
8
|
+
|
|
9
|
+
- Added caller-defined dataset item identities for safe retries across all dataset storage adapters. ([#19384](https://github.com/mastra-ai/mastra/pull/19384))
|
|
10
|
+
|
|
11
|
+
Dataset items can now include an `externalId` when calling `addItem` or `addItems`:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
await dataset.addItem({
|
|
15
|
+
externalId: 'source-item-123',
|
|
16
|
+
input: { prompt: 'Hello' },
|
|
17
|
+
});
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Retrying with the same identity and payload returns the existing item. Reusing an identity with different content returns a typed conflict, including during concurrent writes. Updates and deletes preserve the identity, Spanner retries transactions without changing the outcome, and MySQL batch writes now preserve every supported dataset item field.
|
|
21
|
+
|
|
22
|
+
### Patch Changes
|
|
23
|
+
|
|
24
|
+
- Raised the `@mastra/core` peer dependency floor to `>=1.51.0-0` so the dataset item identity helpers used by the storage adapters are available at runtime. ([#19384](https://github.com/mastra-ai/mastra/pull/19384))
|
|
25
|
+
|
|
26
|
+
- Updated dependencies [[`a99eae8`](https://github.com/mastra-ai/mastra/commit/a99eae8908e500c1b2d12f9d277be616b98617a5), [`fd13f8e`](https://github.com/mastra-ai/mastra/commit/fd13f8e21990f9904c3eedba3a626bb4a929cdb8), [`f703f87`](https://github.com/mastra-ai/mastra/commit/f703f878de072d51fda557f9c50867d8252bef05), [`0ad646f`](https://github.com/mastra-ai/mastra/commit/0ad646f71a530f2454664299e5e01bfd13fa12e5)]:
|
|
27
|
+
- @mastra/core@1.51.0-alpha.13
|
|
28
|
+
|
|
3
29
|
## 1.2.4-alpha.1
|
|
4
30
|
|
|
5
31
|
### Patch Changes
|
package/dist/docs/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: mastra-spanner
|
|
|
3
3
|
description: Documentation for @mastra/spanner. Use when working with @mastra/spanner APIs, configuration, or implementation.
|
|
4
4
|
metadata:
|
|
5
5
|
package: "@mastra/spanner"
|
|
6
|
-
version: "1.
|
|
6
|
+
version: "1.3.0-alpha.2"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
## When to use
|
package/dist/index.cjs
CHANGED
|
@@ -2995,6 +2995,7 @@ function rowToItem(row) {
|
|
|
2995
2995
|
id: String(t.id),
|
|
2996
2996
|
datasetId: String(t.datasetId),
|
|
2997
2997
|
datasetVersion: Number(t.datasetVersion),
|
|
2998
|
+
externalId: t.externalId ?? null,
|
|
2998
2999
|
organizationId: t.organizationId ?? null,
|
|
2999
3000
|
projectId: t.projectId ?? null,
|
|
3000
3001
|
input: t.input,
|
|
@@ -3059,7 +3060,7 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
|
|
|
3059
3060
|
await this.db.alterTable({
|
|
3060
3061
|
tableName: storage.TABLE_DATASET_ITEMS,
|
|
3061
3062
|
schema: storage.TABLE_SCHEMAS[storage.TABLE_DATASET_ITEMS],
|
|
3062
|
-
ifNotExists: ["organizationId", "projectId"]
|
|
3063
|
+
ifNotExists: ["organizationId", "projectId", "externalId"]
|
|
3063
3064
|
});
|
|
3064
3065
|
await this.createDefaultIndexes();
|
|
3065
3066
|
await this.createCustomIndexes();
|
|
@@ -3078,6 +3079,11 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
|
|
|
3078
3079
|
table: storage.TABLE_DATASET_ITEMS,
|
|
3079
3080
|
columns: ["datasetId", "datasetVersion"]
|
|
3080
3081
|
},
|
|
3082
|
+
{
|
|
3083
|
+
name: "mastra_dataset_items_dataset_externalid_version_idx",
|
|
3084
|
+
table: storage.TABLE_DATASET_ITEMS,
|
|
3085
|
+
columns: ["datasetId", "externalId", "datasetVersion"]
|
|
3086
|
+
},
|
|
3081
3087
|
{
|
|
3082
3088
|
// Unique invariant: one snapshot row per (datasetId, version). The DESC
|
|
3083
3089
|
// ordering also serves listDatasetVersions' newest-first scan.
|
|
@@ -3121,7 +3127,8 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
|
|
|
3121
3127
|
async createDataset(input) {
|
|
3122
3128
|
try {
|
|
3123
3129
|
const now = /* @__PURE__ */ new Date();
|
|
3124
|
-
const id = crypto.randomUUID();
|
|
3130
|
+
const id = input.id ?? crypto.randomUUID();
|
|
3131
|
+
if (input.id !== void 0) this.validateCallerDefinedDatasetId(input.id);
|
|
3125
3132
|
const record = {
|
|
3126
3133
|
id,
|
|
3127
3134
|
name: input.name,
|
|
@@ -3167,6 +3174,11 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
|
|
|
3167
3174
|
});
|
|
3168
3175
|
return record;
|
|
3169
3176
|
} catch (error$1) {
|
|
3177
|
+
if (input.id !== void 0 && storage.hasErrorCode(error$1, /* @__PURE__ */ new Set([6, "ALREADY_EXISTS"]))) {
|
|
3178
|
+
const existing = await this.getDatasetById({ id: input.id });
|
|
3179
|
+
if (existing) return this.resolveExistingDataset(existing, { ...input, id: input.id });
|
|
3180
|
+
}
|
|
3181
|
+
if (error$1 instanceof error.MastraError) throw error$1;
|
|
3170
3182
|
throw new error.MastraError(
|
|
3171
3183
|
{
|
|
3172
3184
|
id: storage.createStorageErrorId("SPANNER", "CREATE_DATASET", "FAILED"),
|
|
@@ -3589,6 +3601,7 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
|
|
|
3589
3601
|
id: args.id,
|
|
3590
3602
|
datasetId: args.datasetId,
|
|
3591
3603
|
datasetVersion: newVersion,
|
|
3604
|
+
externalId: existing.externalId ?? null,
|
|
3592
3605
|
organizationId,
|
|
3593
3606
|
projectId,
|
|
3594
3607
|
validTo: null,
|
|
@@ -3611,6 +3624,7 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
|
|
|
3611
3624
|
id: args.id,
|
|
3612
3625
|
datasetId: args.datasetId,
|
|
3613
3626
|
datasetVersion: newVersion,
|
|
3627
|
+
externalId: existing.externalId ?? null,
|
|
3614
3628
|
organizationId,
|
|
3615
3629
|
projectId,
|
|
3616
3630
|
input: merged.input,
|
|
@@ -3670,6 +3684,7 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
|
|
|
3670
3684
|
id: args.id,
|
|
3671
3685
|
datasetId: args.datasetId,
|
|
3672
3686
|
datasetVersion: newVersion,
|
|
3687
|
+
externalId: existing.externalId ?? null,
|
|
3673
3688
|
organizationId,
|
|
3674
3689
|
projectId,
|
|
3675
3690
|
validTo: null,
|
|
@@ -3939,55 +3954,75 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
|
|
|
3939
3954
|
async _doBatchInsertItems(input) {
|
|
3940
3955
|
try {
|
|
3941
3956
|
if (input.items.length === 0) return [];
|
|
3942
|
-
const now = /* @__PURE__ */ new Date();
|
|
3943
|
-
const prepared = input.items.map((item) => ({ id: crypto.randomUUID(), item }));
|
|
3944
3957
|
let result = [];
|
|
3945
3958
|
await this.db.runWithAbortRetry(
|
|
3946
3959
|
() => this.database.runTransactionAsync(async (tx) => {
|
|
3947
3960
|
try {
|
|
3948
|
-
const
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3961
|
+
const [datasetRows] = await tx.run({
|
|
3962
|
+
sql: `SELECT * FROM ${quoteIdent(storage.TABLE_DATASETS, "table name")} WHERE ${quoteIdent("id", "column name")} = @id LIMIT 1`,
|
|
3963
|
+
params: { id: input.datasetId },
|
|
3964
|
+
json: true
|
|
3965
|
+
});
|
|
3966
|
+
const dataset = datasetRows[0];
|
|
3967
|
+
if (!dataset) {
|
|
3968
|
+
throw new error.MastraError({
|
|
3969
|
+
id: storage.createStorageErrorId("SPANNER", "BATCH_INSERT_ITEMS", "DATASET_NOT_FOUND"),
|
|
3970
|
+
domain: error.ErrorDomain.STORAGE,
|
|
3971
|
+
category: error.ErrorCategory.USER,
|
|
3972
|
+
details: { datasetId: input.datasetId }
|
|
3973
|
+
});
|
|
3974
|
+
}
|
|
3975
|
+
const externalIds = [...new Set(input.items.flatMap((item) => item.externalId ? [item.externalId] : []))];
|
|
3976
|
+
let historyRows = [];
|
|
3977
|
+
if (externalIds.length > 0) {
|
|
3978
|
+
const [rows] = await tx.run({
|
|
3979
|
+
sql: `SELECT * FROM ${quoteIdent(storage.TABLE_DATASET_ITEMS, "table name")} WHERE ${quoteIdent("datasetId", "column name")} = @datasetId AND ${quoteIdent("externalId", "column name")} IN UNNEST(@externalIds) ORDER BY ${quoteIdent("datasetVersion", "column name")}`,
|
|
3980
|
+
params: { datasetId: input.datasetId, externalIds },
|
|
3981
|
+
types: { externalIds: { type: "array", child: "string" } },
|
|
3982
|
+
json: true
|
|
3983
|
+
});
|
|
3984
|
+
historyRows = rows.map(rowToItemRow);
|
|
3985
|
+
}
|
|
3986
|
+
const plan = this.planDatasetItemBatch(input.items, historyRows, crypto.randomUUID);
|
|
3987
|
+
const resolved = new Map(
|
|
3988
|
+
[...plan.existingCurrentItems].map(([id, row]) => [id, this.datasetItemFromRow(row)])
|
|
3989
|
+
);
|
|
3990
|
+
if (plan.inserts.length > 0) {
|
|
3991
|
+
const now = /* @__PURE__ */ new Date();
|
|
3992
|
+
const {
|
|
3993
|
+
version: newVersion,
|
|
3994
|
+
organizationId,
|
|
3995
|
+
projectId
|
|
3996
|
+
} = await this.bumpVersion(tx, input.datasetId, now);
|
|
3997
|
+
for (const insert of plan.inserts) {
|
|
3998
|
+
const item = {
|
|
3999
|
+
id: insert.id,
|
|
3954
4000
|
datasetId: input.datasetId,
|
|
3955
4001
|
datasetVersion: newVersion,
|
|
4002
|
+
externalId: insert.item.externalId ?? null,
|
|
3956
4003
|
organizationId,
|
|
3957
4004
|
projectId,
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
metadata: item.metadata ?? null,
|
|
3966
|
-
source: item.source ?? null,
|
|
4005
|
+
input: insert.item.input,
|
|
4006
|
+
groundTruth: insert.item.groundTruth,
|
|
4007
|
+
expectedTrajectory: insert.item.expectedTrajectory,
|
|
4008
|
+
toolMocks: insert.item.toolMocks,
|
|
4009
|
+
requestContext: insert.item.requestContext,
|
|
4010
|
+
metadata: insert.item.metadata,
|
|
4011
|
+
source: insert.item.source,
|
|
3967
4012
|
createdAt: now,
|
|
3968
4013
|
updatedAt: now
|
|
3969
|
-
}
|
|
3970
|
-
|
|
3971
|
-
|
|
4014
|
+
};
|
|
4015
|
+
await this.db.insert({
|
|
4016
|
+
tableName: storage.TABLE_DATASET_ITEMS,
|
|
4017
|
+
record: { ...item, validTo: null, isDeleted: false },
|
|
4018
|
+
transaction: tx
|
|
4019
|
+
});
|
|
4020
|
+
resolved.set(item.id, item);
|
|
4021
|
+
}
|
|
4022
|
+
await this.insertVersionRow(tx, input.datasetId, newVersion, now);
|
|
3972
4023
|
}
|
|
3973
|
-
await this.insertVersionRow(tx, input.datasetId, newVersion, now);
|
|
3974
4024
|
await tx.commit();
|
|
3975
|
-
result =
|
|
3976
|
-
id,
|
|
3977
|
-
datasetId: input.datasetId,
|
|
3978
|
-
datasetVersion: newVersion,
|
|
3979
|
-
organizationId,
|
|
3980
|
-
projectId,
|
|
3981
|
-
input: item.input,
|
|
3982
|
-
groundTruth: item.groundTruth,
|
|
3983
|
-
expectedTrajectory: item.expectedTrajectory,
|
|
3984
|
-
toolMocks: item.toolMocks,
|
|
3985
|
-
requestContext: item.requestContext,
|
|
3986
|
-
metadata: item.metadata,
|
|
3987
|
-
source: item.source,
|
|
3988
|
-
createdAt: now,
|
|
3989
|
-
updatedAt: now
|
|
3990
|
-
}));
|
|
4025
|
+
result = plan.resolvedIds.map((id) => resolved.get(id));
|
|
3991
4026
|
} catch (err) {
|
|
3992
4027
|
await tx.rollback().catch((rollbackErr) => {
|
|
3993
4028
|
throw new AggregateError([err, rollbackErr], "Transaction and rollback both failed");
|
|
@@ -4031,6 +4066,7 @@ var DatasetsSpanner = class _DatasetsSpanner extends storage.DatasetsStorage {
|
|
|
4031
4066
|
id: existing.id,
|
|
4032
4067
|
datasetId: input.datasetId,
|
|
4033
4068
|
datasetVersion: newVersion,
|
|
4069
|
+
externalId: existing.externalId ?? null,
|
|
4034
4070
|
organizationId,
|
|
4035
4071
|
projectId,
|
|
4036
4072
|
validTo: null,
|