@mastra/spanner 1.2.1 → 1.2.2-alpha.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/dist/index.js CHANGED
@@ -3032,7 +3032,15 @@ var DatasetsSpanner = class _DatasetsSpanner extends DatasetsStorage {
3032
3032
  await this.db.alterTable({
3033
3033
  tableName: TABLE_DATASETS,
3034
3034
  schema: TABLE_SCHEMAS[TABLE_DATASETS],
3035
- ifNotExists: ["organizationId", "projectId", "candidateKey", "candidateId"]
3035
+ ifNotExists: [
3036
+ "organizationId",
3037
+ "projectId",
3038
+ "candidateKey",
3039
+ "candidateId",
3040
+ "targetType",
3041
+ "targetIds",
3042
+ "scorerIds"
3043
+ ]
3036
3044
  });
3037
3045
  await this.db.alterTable({
3038
3046
  tableName: TABLE_DATASET_ITEMS,
@@ -3079,6 +3087,20 @@ var DatasetsSpanner = class _DatasetsSpanner extends DatasetsStorage {
3079
3087
  await this.db.clearTable({ tableName: TABLE_DATASET_ITEMS });
3080
3088
  await this.db.clearTable({ tableName: TABLE_DATASETS });
3081
3089
  }
3090
+ async experimentTablesExist() {
3091
+ try {
3092
+ const [rows] = await this.database.run({
3093
+ sql: `SELECT COUNT(*) AS c FROM INFORMATION_SCHEMA.TABLES
3094
+ WHERE TABLE_SCHEMA = "" AND TABLE_NAME IN (@a, @b)`,
3095
+ params: { a: TABLE_EXPERIMENTS, b: TABLE_EXPERIMENT_RESULTS },
3096
+ json: true
3097
+ });
3098
+ const row = rows?.[0];
3099
+ return Number(row?.c ?? 0) === 2;
3100
+ } catch {
3101
+ return false;
3102
+ }
3103
+ }
3082
3104
  // ==========================================================================
3083
3105
  // Dataset CRUD
3084
3106
  // ==========================================================================
@@ -3143,7 +3165,27 @@ var DatasetsSpanner = class _DatasetsSpanner extends DatasetsStorage {
3143
3165
  }
3144
3166
  async getDatasetById(args) {
3145
3167
  try {
3146
- const row = await this.db.load({ tableName: TABLE_DATASETS, keys: { id: args.id } });
3168
+ const hasTenancy = args.filters?.organizationId !== void 0 || args.filters?.projectId !== void 0;
3169
+ if (!hasTenancy) {
3170
+ const row2 = await this.db.load({ tableName: TABLE_DATASETS, keys: { id: args.id } });
3171
+ return row2 ? rowToDataset(row2) : null;
3172
+ }
3173
+ const conditions = [`${quoteIdent("id", "column name")} = @id`];
3174
+ const params = { id: args.id };
3175
+ if (args.filters?.organizationId !== void 0) {
3176
+ conditions.push(`${quoteIdent("organizationId", "column name")} = @organizationId`);
3177
+ params.organizationId = args.filters.organizationId;
3178
+ }
3179
+ if (args.filters?.projectId !== void 0) {
3180
+ conditions.push(`${quoteIdent("projectId", "column name")} = @projectId`);
3181
+ params.projectId = args.filters.projectId;
3182
+ }
3183
+ const [rows] = await this.database.run({
3184
+ sql: `SELECT * FROM ${quoteIdent(TABLE_DATASETS, "table name")} WHERE ${conditions.join(" AND ")} LIMIT 1`,
3185
+ params,
3186
+ json: true
3187
+ });
3188
+ const row = rows[0];
3147
3189
  return row ? rowToDataset(row) : null;
3148
3190
  } catch (error) {
3149
3191
  throw new MastraError(
@@ -3172,7 +3214,7 @@ var DatasetsSpanner = class _DatasetsSpanner extends DatasetsStorage {
3172
3214
  if (args.scorerIds !== void 0) data.scorerIds = args.scorerIds;
3173
3215
  data.updatedAt = /* @__PURE__ */ new Date();
3174
3216
  await this.db.update({ tableName: TABLE_DATASETS, keys: { id: args.id }, data });
3175
- const updated = await this.getDatasetById({ id: args.id });
3217
+ const updated = await this.getDatasetById({ id: args.id, filters: args.filters });
3176
3218
  if (!updated) {
3177
3219
  throw new MastraError({
3178
3220
  id: createStorageErrorId("SPANNER", "UPDATE_DATASET", "NOT_FOUND"),
@@ -3198,26 +3240,46 @@ var DatasetsSpanner = class _DatasetsSpanner extends DatasetsStorage {
3198
3240
  }
3199
3241
  async deleteDataset(args) {
3200
3242
  try {
3201
- try {
3202
- await this.db.runDml({
3203
- sql: `DELETE FROM ${quoteIdent(TABLE_EXPERIMENT_RESULTS, "table name")}
3204
- WHERE ${quoteIdent("experimentId", "column name")} IN (
3205
- SELECT ${quoteIdent("id", "column name")} FROM ${quoteIdent(TABLE_EXPERIMENTS, "table name")}
3206
- WHERE ${quoteIdent("datasetId", "column name")} = @id)`,
3207
- params: { id: args.id }
3208
- });
3209
- await this.db.runDml({
3210
- sql: `UPDATE ${quoteIdent(TABLE_EXPERIMENTS, "table name")}
3211
- SET ${quoteIdent("datasetId", "column name")} = NULL,
3212
- ${quoteIdent("datasetVersion", "column name")} = NULL
3213
- WHERE ${quoteIdent("datasetId", "column name")} = @id`,
3214
- params: { id: args.id }
3215
- });
3216
- } catch {
3243
+ const tenancyConditions = [];
3244
+ const tenancyParams = {};
3245
+ if (args.filters?.organizationId !== void 0) {
3246
+ tenancyConditions.push(`${quoteIdent("organizationId", "column name")} = @organizationId`);
3247
+ tenancyParams.organizationId = args.filters.organizationId;
3248
+ }
3249
+ if (args.filters?.projectId !== void 0) {
3250
+ tenancyConditions.push(`${quoteIdent("projectId", "column name")} = @projectId`);
3251
+ tenancyParams.projectId = args.filters.projectId;
3217
3252
  }
3253
+ const scopedWhere = [`${quoteIdent("id", "column name")} = @id`, ...tenancyConditions].join(" AND ");
3254
+ const experimentTablesExist = await this.experimentTablesExist();
3218
3255
  await this.db.runWithAbortRetry(
3219
3256
  () => this.database.runTransactionAsync(async (tx) => {
3220
3257
  try {
3258
+ const [rows] = await tx.run({
3259
+ sql: `SELECT ${quoteIdent("id", "column name")} FROM ${quoteIdent(TABLE_DATASETS, "table name")} WHERE ${scopedWhere}`,
3260
+ params: { id: args.id, ...tenancyParams },
3261
+ json: true
3262
+ });
3263
+ if (!rows || rows.length === 0) {
3264
+ await tx.commit();
3265
+ return;
3266
+ }
3267
+ if (experimentTablesExist) {
3268
+ await tx.runUpdate({
3269
+ sql: `DELETE FROM ${quoteIdent(TABLE_EXPERIMENT_RESULTS, "table name")}
3270
+ WHERE ${quoteIdent("experimentId", "column name")} IN (
3271
+ SELECT ${quoteIdent("id", "column name")} FROM ${quoteIdent(TABLE_EXPERIMENTS, "table name")}
3272
+ WHERE ${quoteIdent("datasetId", "column name")} = @id)`,
3273
+ params: { id: args.id }
3274
+ });
3275
+ await tx.runUpdate({
3276
+ sql: `UPDATE ${quoteIdent(TABLE_EXPERIMENTS, "table name")}
3277
+ SET ${quoteIdent("datasetId", "column name")} = NULL,
3278
+ ${quoteIdent("datasetVersion", "column name")} = NULL
3279
+ WHERE ${quoteIdent("datasetId", "column name")} = @id`,
3280
+ params: { id: args.id }
3281
+ });
3282
+ }
3221
3283
  for (const table of [TABLE_DATASET_VERSIONS, TABLE_DATASET_ITEMS]) {
3222
3284
  await tx.runUpdate({
3223
3285
  sql: `DELETE FROM ${quoteIdent(table, "table name")} WHERE ${quoteIdent("datasetId", "column name")} = @id`,
@@ -3225,8 +3287,8 @@ var DatasetsSpanner = class _DatasetsSpanner extends DatasetsStorage {
3225
3287
  });
3226
3288
  }
3227
3289
  await tx.runUpdate({
3228
- sql: `DELETE FROM ${quoteIdent(TABLE_DATASETS, "table name")} WHERE ${quoteIdent("id", "column name")} = @id`,
3229
- params: { id: args.id }
3290
+ sql: `DELETE FROM ${quoteIdent(TABLE_DATASETS, "table name")} WHERE ${scopedWhere}`,
3291
+ params: { id: args.id, ...tenancyParams }
3230
3292
  });
3231
3293
  await tx.commit();
3232
3294
  } catch (err) {
@@ -3272,10 +3334,26 @@ var DatasetsSpanner = class _DatasetsSpanner extends DatasetsStorage {
3272
3334
  filterConditions.push(`${quoteIdent("candidateId", "column name")} = @candidateId`);
3273
3335
  filterParams.candidateId = args.filters.candidateId;
3274
3336
  }
3337
+ if (args.filters?.targetType !== void 0) {
3338
+ filterConditions.push(`${quoteIdent("targetType", "column name")} = @targetType`);
3339
+ filterParams.targetType = args.filters.targetType;
3340
+ }
3341
+ if (args.filters?.targetIds !== void 0 && args.filters.targetIds.length > 0) {
3342
+ filterConditions.push(
3343
+ `EXISTS (SELECT 1 FROM UNNEST(JSON_QUERY_ARRAY(${quoteIdent("targetIds", "column name")})) AS t WHERE JSON_VALUE(t) IN UNNEST(@targetIds))`
3344
+ );
3345
+ filterParams.targetIds = args.filters.targetIds;
3346
+ }
3347
+ if (args.filters?.name !== void 0 && args.filters.name.length > 0) {
3348
+ filterConditions.push(`LOWER(${quoteIdent("name", "column name")}) LIKE LOWER(@nameSubstring)`);
3349
+ filterParams.nameSubstring = `%${args.filters.name}%`;
3350
+ }
3275
3351
  const whereClause = filterConditions.length > 0 ? `WHERE ${filterConditions.join(" AND ")}` : "";
3352
+ const filterTypes = args.filters?.targetIds !== void 0 && args.filters.targetIds.length > 0 ? { targetIds: { type: "array", child: { type: "string" } } } : {};
3276
3353
  const [countRows] = await this.database.run({
3277
3354
  sql: `SELECT COUNT(*) AS count FROM ${tableName} ${whereClause}`,
3278
3355
  params: filterParams,
3356
+ types: filterTypes,
3279
3357
  json: true
3280
3358
  });
3281
3359
  const total = Number(countRows[0]?.count ?? 0);
@@ -3288,6 +3366,7 @@ var DatasetsSpanner = class _DatasetsSpanner extends DatasetsStorage {
3288
3366
  ORDER BY ${quoteIdent("createdAt", "column name")} DESC, ${quoteIdent("id", "column name")} ASC
3289
3367
  LIMIT @limit OFFSET @offset`,
3290
3368
  params: { ...filterParams, limit, offset },
3369
+ types: filterTypes,
3291
3370
  json: true
3292
3371
  });
3293
3372
  const datasets = rows.map(rowToDataset);
@@ -4212,7 +4291,27 @@ var ExperimentsSpanner = class _ExperimentsSpanner extends ExperimentsStorage {
4212
4291
  }
4213
4292
  async getExperimentById(args) {
4214
4293
  try {
4215
- const row = await this.db.load({ tableName: TABLE_EXPERIMENTS, keys: { id: args.id } });
4294
+ const hasTenancy = args.filters?.organizationId !== void 0 || args.filters?.projectId !== void 0;
4295
+ if (!hasTenancy) {
4296
+ const row2 = await this.db.load({ tableName: TABLE_EXPERIMENTS, keys: { id: args.id } });
4297
+ return row2 ? rowToExperiment(row2) : null;
4298
+ }
4299
+ const conditions = [`${quoteIdent("id", "column name")} = @id`];
4300
+ const params = { id: args.id };
4301
+ if (args.filters?.organizationId !== void 0) {
4302
+ conditions.push(`${quoteIdent("organizationId", "column name")} = @organizationId`);
4303
+ params.organizationId = args.filters.organizationId;
4304
+ }
4305
+ if (args.filters?.projectId !== void 0) {
4306
+ conditions.push(`${quoteIdent("projectId", "column name")} = @projectId`);
4307
+ params.projectId = args.filters.projectId;
4308
+ }
4309
+ const [rows] = await this.database.run({
4310
+ sql: `SELECT * FROM ${quoteIdent(TABLE_EXPERIMENTS, "table name")} WHERE ${conditions.join(" AND ")} LIMIT 1`,
4311
+ params,
4312
+ json: true
4313
+ });
4314
+ const row = rows[0];
4216
4315
  return row ? rowToExperiment(row) : null;
4217
4316
  } catch (error) {
4218
4317
  throw new MastraError(
@@ -4306,18 +4405,43 @@ var ExperimentsSpanner = class _ExperimentsSpanner extends ExperimentsStorage {
4306
4405
  }
4307
4406
  async deleteExperiment(args) {
4308
4407
  try {
4408
+ const tenancyConditions = [];
4409
+ const tenancyParams = {};
4410
+ if (args.filters?.organizationId !== void 0) {
4411
+ tenancyConditions.push(`${quoteIdent("organizationId", "column name")} = @organizationId`);
4412
+ tenancyParams.organizationId = args.filters.organizationId;
4413
+ }
4414
+ if (args.filters?.projectId !== void 0) {
4415
+ tenancyConditions.push(`${quoteIdent("projectId", "column name")} = @projectId`);
4416
+ tenancyParams.projectId = args.filters.projectId;
4417
+ }
4418
+ const gateWhere = [`${quoteIdent("id", "column name")} = @id`, ...tenancyConditions].join(" AND ");
4309
4419
  await this.db.runWithAbortRetry(
4310
4420
  () => this.database.runTransactionAsync(async (tx) => {
4311
4421
  try {
4422
+ const [gateRows] = await tx.run({
4423
+ sql: `SELECT ${quoteIdent("id", "column name")} FROM ${quoteIdent(TABLE_EXPERIMENTS, "table name")}
4424
+ WHERE ${gateWhere} LIMIT 1`,
4425
+ params: { id: args.id, ...tenancyParams },
4426
+ json: true
4427
+ });
4428
+ if (!Array.isArray(gateRows) || gateRows.length === 0) {
4429
+ await tx.commit();
4430
+ return;
4431
+ }
4432
+ const cascadeWhere = [
4433
+ `${quoteIdent("experimentId", "column name")} = @id`,
4434
+ // Result rows carry the same organizationId/projectId as their parent,
4435
+ // so applying tenancy here makes the cascade itself tenant-scoped.
4436
+ ...tenancyConditions
4437
+ ].join(" AND ");
4312
4438
  await tx.runUpdate({
4313
- sql: `DELETE FROM ${quoteIdent(TABLE_EXPERIMENT_RESULTS, "table name")}
4314
- WHERE ${quoteIdent("experimentId", "column name")} = @id`,
4315
- params: { id: args.id }
4439
+ sql: `DELETE FROM ${quoteIdent(TABLE_EXPERIMENT_RESULTS, "table name")} WHERE ${cascadeWhere}`,
4440
+ params: { id: args.id, ...tenancyParams }
4316
4441
  });
4317
4442
  await tx.runUpdate({
4318
- sql: `DELETE FROM ${quoteIdent(TABLE_EXPERIMENTS, "table name")}
4319
- WHERE ${quoteIdent("id", "column name")} = @id`,
4320
- params: { id: args.id }
4443
+ sql: `DELETE FROM ${quoteIdent(TABLE_EXPERIMENTS, "table name")} WHERE ${gateWhere}`,
4444
+ params: { id: args.id, ...tenancyParams }
4321
4445
  });
4322
4446
  await tx.commit();
4323
4447
  } catch (err) {
@@ -4473,10 +4597,30 @@ var ExperimentsSpanner = class _ExperimentsSpanner extends ExperimentsStorage {
4473
4597
  }
4474
4598
  async getExperimentResultById(args) {
4475
4599
  try {
4476
- const row = await this.db.load({
4477
- tableName: TABLE_EXPERIMENT_RESULTS,
4478
- keys: { id: args.id }
4600
+ const hasTenancy = args.filters?.organizationId !== void 0 || args.filters?.projectId !== void 0;
4601
+ if (!hasTenancy) {
4602
+ const row2 = await this.db.load({
4603
+ tableName: TABLE_EXPERIMENT_RESULTS,
4604
+ keys: { id: args.id }
4605
+ });
4606
+ return row2 ? rowToExperimentResult(row2) : null;
4607
+ }
4608
+ const conditions = [`${quoteIdent("id", "column name")} = @id`];
4609
+ const params = { id: args.id };
4610
+ if (args.filters?.organizationId !== void 0) {
4611
+ conditions.push(`${quoteIdent("organizationId", "column name")} = @organizationId`);
4612
+ params.organizationId = args.filters.organizationId;
4613
+ }
4614
+ if (args.filters?.projectId !== void 0) {
4615
+ conditions.push(`${quoteIdent("projectId", "column name")} = @projectId`);
4616
+ params.projectId = args.filters.projectId;
4617
+ }
4618
+ const [rows] = await this.database.run({
4619
+ sql: `SELECT * FROM ${quoteIdent(TABLE_EXPERIMENT_RESULTS, "table name")} WHERE ${conditions.join(" AND ")} LIMIT 1`,
4620
+ params,
4621
+ json: true
4479
4622
  });
4623
+ const row = rows[0];
4480
4624
  return row ? rowToExperimentResult(row) : null;
4481
4625
  } catch (error) {
4482
4626
  throw new MastraError(
@@ -4559,6 +4703,23 @@ var ExperimentsSpanner = class _ExperimentsSpanner extends ExperimentsStorage {
4559
4703
  }
4560
4704
  async deleteExperimentResults(args) {
4561
4705
  try {
4706
+ if (args.filters?.organizationId !== void 0 || args.filters?.projectId !== void 0) {
4707
+ const conditions = [`${quoteIdent("experimentId", "column name")} = @experimentId`];
4708
+ const params = { experimentId: args.experimentId };
4709
+ if (args.filters?.organizationId !== void 0) {
4710
+ conditions.push(`${quoteIdent("organizationId", "column name")} = @organizationId`);
4711
+ params.organizationId = args.filters.organizationId;
4712
+ }
4713
+ if (args.filters?.projectId !== void 0) {
4714
+ conditions.push(`${quoteIdent("projectId", "column name")} = @projectId`);
4715
+ params.projectId = args.filters.projectId;
4716
+ }
4717
+ await this.db.runDml({
4718
+ sql: `DELETE FROM ${quoteIdent(TABLE_EXPERIMENT_RESULTS, "table name")} WHERE ${conditions.join(" AND ")}`,
4719
+ params
4720
+ });
4721
+ return;
4722
+ }
4562
4723
  await this.db.runDml({
4563
4724
  sql: `DELETE FROM ${quoteIdent(TABLE_EXPERIMENT_RESULTS, "table name")}
4564
4725
  WHERE ${quoteIdent("experimentId", "column name")} = @experimentId`,
@@ -10548,6 +10709,16 @@ function transformScoreRow(row) {
10548
10709
  const normalized = transformFromSpannerRow({ tableName: TABLE_SCORERS, row });
10549
10710
  return transformScoreRow$1(normalized, { convertTimestamps: true });
10550
10711
  }
10712
+ function applyTenancyFilters(conditions, params, filters) {
10713
+ if (filters?.organizationId !== void 0) {
10714
+ conditions.push(`${quoteIdent("organizationId", "column name")} = @organizationId`);
10715
+ params.organizationId = filters.organizationId;
10716
+ }
10717
+ if (filters?.projectId !== void 0) {
10718
+ conditions.push(`${quoteIdent("projectId", "column name")} = @projectId`);
10719
+ params.projectId = filters.projectId;
10720
+ }
10721
+ }
10551
10722
  var ScoresSpanner = class _ScoresSpanner extends ScoresStorage {
10552
10723
  database;
10553
10724
  db;
@@ -10726,7 +10897,8 @@ var ScoresSpanner = class _ScoresSpanner extends ScoresStorage {
10726
10897
  pagination,
10727
10898
  entityId,
10728
10899
  entityType,
10729
- source
10900
+ source,
10901
+ filters
10730
10902
  }) {
10731
10903
  try {
10732
10904
  const conditions = [`${quoteIdent("scorerId", "column name")} = @scorerId`];
@@ -10743,6 +10915,7 @@ var ScoresSpanner = class _ScoresSpanner extends ScoresStorage {
10743
10915
  conditions.push(`${quoteIdent("source", "column name")} = @source`);
10744
10916
  params.source = source;
10745
10917
  }
10918
+ applyTenancyFilters(conditions, params, filters);
10746
10919
  return await this.listScoresByConditions(conditions, params, pagination);
10747
10920
  } catch (error) {
10748
10921
  throw new MastraError(
@@ -10758,14 +10931,14 @@ var ScoresSpanner = class _ScoresSpanner extends ScoresStorage {
10758
10931
  }
10759
10932
  async listScoresByRunId({
10760
10933
  runId,
10761
- pagination
10934
+ pagination,
10935
+ filters
10762
10936
  }) {
10763
10937
  try {
10764
- return await this.listScoresByConditions(
10765
- [`${quoteIdent("runId", "column name")} = @runId`],
10766
- { runId },
10767
- pagination
10768
- );
10938
+ const conditions = [`${quoteIdent("runId", "column name")} = @runId`];
10939
+ const params = { runId };
10940
+ applyTenancyFilters(conditions, params, filters);
10941
+ return await this.listScoresByConditions(conditions, params, pagination);
10769
10942
  } catch (error) {
10770
10943
  throw new MastraError(
10771
10944
  {
@@ -10781,17 +10954,17 @@ var ScoresSpanner = class _ScoresSpanner extends ScoresStorage {
10781
10954
  async listScoresByEntityId({
10782
10955
  entityId,
10783
10956
  entityType,
10784
- pagination
10957
+ pagination,
10958
+ filters
10785
10959
  }) {
10786
10960
  try {
10787
- return await this.listScoresByConditions(
10788
- [
10789
- `${quoteIdent("entityId", "column name")} = @entityId`,
10790
- `${quoteIdent("entityType", "column name")} = @entityType`
10791
- ],
10792
- { entityId, entityType },
10793
- pagination
10794
- );
10961
+ const conditions = [
10962
+ `${quoteIdent("entityId", "column name")} = @entityId`,
10963
+ `${quoteIdent("entityType", "column name")} = @entityType`
10964
+ ];
10965
+ const params = { entityId, entityType };
10966
+ applyTenancyFilters(conditions, params, filters);
10967
+ return await this.listScoresByConditions(conditions, params, pagination);
10795
10968
  } catch (error) {
10796
10969
  throw new MastraError(
10797
10970
  {
@@ -10807,14 +10980,17 @@ var ScoresSpanner = class _ScoresSpanner extends ScoresStorage {
10807
10980
  async listScoresBySpan({
10808
10981
  traceId,
10809
10982
  spanId,
10810
- pagination
10983
+ pagination,
10984
+ filters
10811
10985
  }) {
10812
10986
  try {
10813
- return await this.listScoresByConditions(
10814
- [`${quoteIdent("traceId", "column name")} = @traceId`, `${quoteIdent("spanId", "column name")} = @spanId`],
10815
- { traceId, spanId },
10816
- pagination
10817
- );
10987
+ const conditions = [
10988
+ `${quoteIdent("traceId", "column name")} = @traceId`,
10989
+ `${quoteIdent("spanId", "column name")} = @spanId`
10990
+ ];
10991
+ const params = { traceId, spanId };
10992
+ applyTenancyFilters(conditions, params, filters);
10993
+ return await this.listScoresByConditions(conditions, params, pagination);
10818
10994
  } catch (error) {
10819
10995
  throw new MastraError(
10820
10996
  {