@vantreeseba/drizzle-graphql 7.7.0 → 7.8.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.cjs CHANGED
@@ -1095,7 +1095,7 @@ var runOnSharedTx = async (state, body) => {
1095
1095
  throw e;
1096
1096
  }
1097
1097
  };
1098
- var runMutation = async (db, context, info, txCtx, body) => {
1098
+ var runMutation = async (db, context, info, txCtx, body, forceTx) => {
1099
1099
  const contextIsObject = context !== null && (typeof context === "object" || typeof context === "function");
1100
1100
  const stateKey = contextIsObject ? context : info.operation;
1101
1101
  let state = txCtx?.states.get(stateKey);
@@ -1104,7 +1104,7 @@ var runMutation = async (db, context, info, txCtx, body) => {
1104
1104
  return body(context[drizzleExecutorKey]);
1105
1105
  }
1106
1106
  if (!txCtx || info.operation.operation !== "mutation") {
1107
- return body(db);
1107
+ return forceTx ? db.transaction((tx) => body(tx)) : body(db);
1108
1108
  }
1109
1109
  const keys = /* @__PURE__ */ new Set();
1110
1110
  const allKnown = collectRootMutationKeys(
@@ -1119,10 +1119,28 @@ var runMutation = async (db, context, info, txCtx, body) => {
1119
1119
  txCtx.states.set(stateKey, state);
1120
1120
  }
1121
1121
  if (state.kind === "passthrough") {
1122
- return body(resolveExecutor(db, context));
1122
+ const executor = resolveExecutor(db, context);
1123
+ return forceTx && executor === db ? db.transaction((tx) => body(tx)) : body(executor);
1123
1124
  }
1124
1125
  return runOnSharedTx(state, body);
1125
1126
  };
1127
+ var normalizeWriteHooks = (hooks) => {
1128
+ if (!hooks) {
1129
+ return void 0;
1130
+ }
1131
+ if (typeof hooks === "function") {
1132
+ return { after: hooks };
1133
+ }
1134
+ const resolved = { before: hooks.before, after: hooks.after };
1135
+ return resolved.before || resolved.after ? resolved : void 0;
1136
+ };
1137
+ var runWriteHook = (hooks, position, payload) => {
1138
+ const hook = position === "before" ? hooks?.before : hooks?.after;
1139
+ if (!hook) {
1140
+ return void 0;
1141
+ }
1142
+ return Promise.resolve(hook({ ...payload, position, rows: payload.rows ?? [] }));
1143
+ };
1126
1144
  var batchedPaginatedRelationQuery = async (db, targetTable, foreignCol, whereCondition, orderByArg, limit, offset, pkNames, orderCtx, whereArgs) => {
1127
1145
  const cols = (0, import_drizzle_orm4.getColumns)(targetTable);
1128
1146
  const orderExprs = [
@@ -2771,10 +2789,11 @@ var withScope = (scope, tableName, table, condition, mode) => {
2771
2789
  const predicate = scope?.on(tableName, table, mode);
2772
2790
  return predicate ? (0, import_drizzle_orm4.and)(condition, predicate) : condition;
2773
2791
  };
2774
- var bindPolicies = (policies, filterCtx) => policies?.scope || policies?.contextValues || policies?.softDelete ? {
2792
+ var bindPolicies = (policies, filterCtx) => policies?.scope || policies?.contextValues || policies?.softDelete || policies?.onWrite ? {
2775
2793
  scope: policies.scope || policies.softDelete ? (context) => resolveScope(policies, context, filterCtx) : void 0,
2776
2794
  contextValues: policies.contextValues,
2777
- softDelete: policies.softDelete
2795
+ softDelete: policies.softDelete,
2796
+ onWrite: policies.onWrite
2778
2797
  } : void 0;
2779
2798
  var applyContextValues = (values, hooks, context) => {
2780
2799
  if (!hooks) {
@@ -4331,22 +4350,49 @@ var generateInsertArray = (db, tableName, table, baseType, fieldName, txCtx, pol
4331
4350
  type: new import_graphql7.GraphQLNonNull(new import_graphql7.GraphQLList(new import_graphql7.GraphQLNonNull(baseType)))
4332
4351
  }
4333
4352
  };
4353
+ const hooks = policies?.onWrite?.(tableName, "insert");
4334
4354
  return {
4335
4355
  name: fieldName,
4336
4356
  resolver: async (_source, args, context, info) => {
4337
4357
  try {
4338
- return await runMutation(db, context, info, txCtx, async (executor) => {
4339
- const input = applyContextValuesAll(
4340
- remapFromGraphQLArrayInput(args.values, table),
4341
- policies?.contextValues?.(tableName),
4342
- context
4343
- );
4344
- if (!input.length) {
4345
- throw new import_graphql7.GraphQLError("No values were provided!");
4346
- }
4347
- await executor.insert(table).values(input);
4348
- return { isSuccess: true };
4349
- });
4358
+ return await runMutation(
4359
+ db,
4360
+ context,
4361
+ info,
4362
+ txCtx,
4363
+ async (executor) => {
4364
+ const input = applyContextValuesAll(
4365
+ remapFromGraphQLArrayInput(args.values, table),
4366
+ policies?.contextValues?.(tableName),
4367
+ context
4368
+ );
4369
+ if (!input.length) {
4370
+ throw new import_graphql7.GraphQLError("No values were provided!");
4371
+ }
4372
+ await runWriteHook(hooks, "before", {
4373
+ table: tableName,
4374
+ operation: "insert",
4375
+ single: false,
4376
+ args,
4377
+ context,
4378
+ info,
4379
+ tx: executor
4380
+ });
4381
+ await executor.insert(table).values(input);
4382
+ await runWriteHook(hooks, "after", {
4383
+ table: tableName,
4384
+ operation: "insert",
4385
+ single: false,
4386
+ args,
4387
+ rows: [],
4388
+ context,
4389
+ info,
4390
+ tx: executor
4391
+ });
4392
+ return { isSuccess: true };
4393
+ },
4394
+ !!hooks
4395
+ );
4350
4396
  } catch (e) {
4351
4397
  throw toGraphQLError(e);
4352
4398
  }
@@ -4360,19 +4406,46 @@ var generateInsertSingle = (db, tableName, table, baseType, fieldName, txCtx, po
4360
4406
  type: new import_graphql7.GraphQLNonNull(baseType)
4361
4407
  }
4362
4408
  };
4409
+ const hooks = policies?.onWrite?.(tableName, "insert");
4363
4410
  return {
4364
4411
  name: fieldName,
4365
4412
  resolver: async (_source, args, context, info) => {
4366
4413
  try {
4367
- return await runMutation(db, context, info, txCtx, async (executor) => {
4368
- const input = applyContextValues(
4369
- remapFromGraphQLSingleInput(args.values, table),
4370
- policies?.contextValues?.(tableName),
4371
- context
4372
- );
4373
- await executor.insert(table).values(input);
4374
- return { isSuccess: true };
4375
- });
4414
+ return await runMutation(
4415
+ db,
4416
+ context,
4417
+ info,
4418
+ txCtx,
4419
+ async (executor) => {
4420
+ await runWriteHook(hooks, "before", {
4421
+ table: tableName,
4422
+ operation: "insert",
4423
+ single: true,
4424
+ args,
4425
+ context,
4426
+ info,
4427
+ tx: executor
4428
+ });
4429
+ const input = applyContextValues(
4430
+ remapFromGraphQLSingleInput(args.values, table),
4431
+ policies?.contextValues?.(tableName),
4432
+ context
4433
+ );
4434
+ await executor.insert(table).values(input);
4435
+ await runWriteHook(hooks, "after", {
4436
+ table: tableName,
4437
+ operation: "insert",
4438
+ single: true,
4439
+ args,
4440
+ rows: [],
4441
+ context,
4442
+ info,
4443
+ tx: executor
4444
+ });
4445
+ return { isSuccess: true };
4446
+ },
4447
+ !!hooks
4448
+ );
4376
4449
  } catch (e) {
4377
4450
  throw toGraphQLError(e);
4378
4451
  }
@@ -4391,35 +4464,62 @@ var generateUpsert = (db, tableName, table, baseType, onConflictType, fieldName,
4391
4464
  }
4392
4465
  };
4393
4466
  const pkNames = mysqlPrimaryKeyPropNames(table);
4467
+ const hooks = policies?.onWrite?.(tableName, "upsert");
4394
4468
  return {
4395
4469
  name: fieldName,
4396
4470
  resolver: async (_source, args, context, info) => {
4397
4471
  try {
4398
- return await runMutation(db, context, info, txCtx, async (executor) => {
4399
- const input = applyContextValuesAll(
4400
- single ? [remapFromGraphQLSingleInput(args.values, table)] : remapFromGraphQLArrayInput(args.values, table),
4401
- policies?.contextValues?.(tableName),
4402
- context
4403
- );
4404
- if (!input.length) {
4405
- throw new import_graphql7.GraphQLError("No values were provided!");
4406
- }
4407
- const plan = resolveConflictPlan({
4408
- table,
4409
- values: input,
4410
- onConflict: args.onConflict,
4411
- pkNames,
4412
- uniqueSets: [],
4413
- excludedRef: mysqlValuesColumnRef,
4414
- withTarget: false
4415
- });
4416
- if (plan.action === "NOTHING") {
4417
- await executor.insert(table).ignore().values(input);
4418
- } else {
4419
- await executor.insert(table).values(input).onDuplicateKeyUpdate({ set: plan.set });
4420
- }
4421
- return { isSuccess: true };
4422
- });
4472
+ return await runMutation(
4473
+ db,
4474
+ context,
4475
+ info,
4476
+ txCtx,
4477
+ async (executor) => {
4478
+ const input = applyContextValuesAll(
4479
+ single ? [remapFromGraphQLSingleInput(args.values, table)] : remapFromGraphQLArrayInput(args.values, table),
4480
+ policies?.contextValues?.(tableName),
4481
+ context
4482
+ );
4483
+ if (!input.length) {
4484
+ throw new import_graphql7.GraphQLError("No values were provided!");
4485
+ }
4486
+ await runWriteHook(hooks, "before", {
4487
+ table: tableName,
4488
+ operation: "upsert",
4489
+ single,
4490
+ args,
4491
+ context,
4492
+ info,
4493
+ tx: executor
4494
+ });
4495
+ const plan = resolveConflictPlan({
4496
+ table,
4497
+ values: input,
4498
+ onConflict: args.onConflict,
4499
+ pkNames,
4500
+ uniqueSets: [],
4501
+ excludedRef: mysqlValuesColumnRef,
4502
+ withTarget: false
4503
+ });
4504
+ if (plan.action === "NOTHING") {
4505
+ await executor.insert(table).ignore().values(input);
4506
+ } else {
4507
+ await executor.insert(table).values(input).onDuplicateKeyUpdate({ set: plan.set });
4508
+ }
4509
+ await runWriteHook(hooks, "after", {
4510
+ table: tableName,
4511
+ operation: "upsert",
4512
+ single,
4513
+ args,
4514
+ rows: [],
4515
+ context,
4516
+ info,
4517
+ tx: executor
4518
+ });
4519
+ return { isSuccess: true };
4520
+ },
4521
+ !!hooks
4522
+ );
4423
4523
  } catch (e) {
4424
4524
  throw toGraphQLError(e);
4425
4525
  }
@@ -4436,37 +4536,64 @@ var generateUpdate = (db, tableName, table, setArgs, filterArgs, fieldName, sing
4436
4536
  type: single || requireWhere ? new import_graphql7.GraphQLNonNull(filterArgs) : filterArgs
4437
4537
  }
4438
4538
  };
4539
+ const hooks = policies?.onWrite?.(tableName, "update");
4439
4540
  return {
4440
4541
  name: fieldName,
4441
4542
  resolver: async (_source, args, context, info) => {
4442
4543
  try {
4443
- return await runMutation(db, context, info, txCtx, async (executor) => {
4444
- const { where, set } = args;
4445
- const scope = policies?.scope?.(context);
4446
- const input = stripContextValues(
4447
- remapUpdateInput(set, table, tableName),
4448
- policies?.contextValues?.(tableName)
4449
- );
4450
- if (!Object.keys(input).length) {
4451
- throw new import_graphql7.GraphQLError("Unable to update with no values specified!");
4452
- }
4453
- const relationCtx = relationFilterCtx(filterCtx, tableName);
4454
- const filters = withScope(
4455
- scope,
4456
- tableName,
4457
- table,
4458
- single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
4459
- );
4460
- if (single) {
4461
- await assertSingleMatch(executor, table, filters, fieldName);
4462
- }
4463
- let query = executor.update(table).set(input);
4464
- if (filters) {
4465
- query = query.where(filters);
4466
- }
4467
- await query;
4468
- return { isSuccess: true };
4469
- });
4544
+ return await runMutation(
4545
+ db,
4546
+ context,
4547
+ info,
4548
+ txCtx,
4549
+ async (executor) => {
4550
+ const { where, set } = args;
4551
+ const scope = policies?.scope?.(context);
4552
+ const input = stripContextValues(
4553
+ remapUpdateInput(set, table, tableName),
4554
+ policies?.contextValues?.(tableName)
4555
+ );
4556
+ if (!Object.keys(input).length) {
4557
+ throw new import_graphql7.GraphQLError("Unable to update with no values specified!");
4558
+ }
4559
+ await runWriteHook(hooks, "before", {
4560
+ table: tableName,
4561
+ operation: "update",
4562
+ single,
4563
+ args,
4564
+ context,
4565
+ info,
4566
+ tx: executor
4567
+ });
4568
+ const relationCtx = relationFilterCtx(filterCtx, tableName);
4569
+ const filters = withScope(
4570
+ scope,
4571
+ tableName,
4572
+ table,
4573
+ single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
4574
+ );
4575
+ if (single) {
4576
+ await assertSingleMatch(executor, table, filters, fieldName);
4577
+ }
4578
+ let query = executor.update(table).set(input);
4579
+ if (filters) {
4580
+ query = query.where(filters);
4581
+ }
4582
+ await query;
4583
+ await runWriteHook(hooks, "after", {
4584
+ table: tableName,
4585
+ operation: "update",
4586
+ single,
4587
+ args,
4588
+ rows: [],
4589
+ context,
4590
+ info,
4591
+ tx: executor
4592
+ });
4593
+ return { isSuccess: true };
4594
+ },
4595
+ !!hooks
4596
+ );
4470
4597
  } catch (e) {
4471
4598
  throw toGraphQLError(e);
4472
4599
  }
@@ -4480,43 +4607,70 @@ var generateUpdateMany = (db, tableName, table, updateManyInput, fieldName, filt
4480
4607
  type: new import_graphql7.GraphQLNonNull(new import_graphql7.GraphQLList(new import_graphql7.GraphQLNonNull(updateManyInput)))
4481
4608
  }
4482
4609
  };
4610
+ const hooks = policies?.onWrite?.(tableName, "updateMany");
4483
4611
  return {
4484
4612
  name: fieldName,
4485
4613
  resolver: async (_source, args, context, info) => {
4486
4614
  try {
4487
- return await runMutation(db, context, info, txCtx, async (executor) => {
4488
- const { updates } = args;
4489
- if (!updates.length) {
4490
- throw new import_graphql7.GraphQLError("No updates were provided!");
4491
- }
4492
- const scope = policies?.scope?.(context);
4493
- const contextColumns = policies?.contextValues?.(tableName);
4494
- const entries = updates.map(({ where, set }) => {
4495
- const input = stripContextValues(remapUpdateInput(set, table, tableName), contextColumns);
4496
- if (!Object.keys(input).length) {
4497
- throw new import_graphql7.GraphQLError("Unable to update with no values specified!");
4615
+ return await runMutation(
4616
+ db,
4617
+ context,
4618
+ info,
4619
+ txCtx,
4620
+ async (executor) => {
4621
+ const { updates } = args;
4622
+ if (!updates.length) {
4623
+ throw new import_graphql7.GraphQLError("No updates were provided!");
4498
4624
  }
4499
- return {
4500
- set: input,
4501
- filters: withScope(
4502
- scope,
4503
- tableName,
4504
- table,
4505
- where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
4506
- )
4507
- };
4508
- });
4509
- await executor.transaction(async (tx) => {
4510
- for (const entry of entries) {
4511
- let query = tx.update(table).set(entry.set);
4512
- if (entry.filters) {
4513
- query = query.where(entry.filters);
4625
+ await runWriteHook(hooks, "before", {
4626
+ table: tableName,
4627
+ operation: "updateMany",
4628
+ single: false,
4629
+ args,
4630
+ context,
4631
+ info,
4632
+ tx: executor
4633
+ });
4634
+ const scope = policies?.scope?.(context);
4635
+ const contextColumns = policies?.contextValues?.(tableName);
4636
+ const entries = updates.map(({ where, set }) => {
4637
+ const input = stripContextValues(remapUpdateInput(set, table, tableName), contextColumns);
4638
+ if (!Object.keys(input).length) {
4639
+ throw new import_graphql7.GraphQLError("Unable to update with no values specified!");
4514
4640
  }
4515
- await query;
4516
- }
4517
- });
4518
- return { isSuccess: true };
4519
- });
4641
+ return {
4642
+ set: input,
4643
+ filters: withScope(
4644
+ scope,
4645
+ tableName,
4646
+ table,
4647
+ where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
4648
+ )
4649
+ };
4650
+ });
4651
+ await executor.transaction(async (tx) => {
4652
+ for (const entry of entries) {
4653
+ let query = tx.update(table).set(entry.set);
4654
+ if (entry.filters) {
4655
+ query = query.where(entry.filters);
4656
+ }
4657
+ await query;
4658
+ }
4659
+ });
4660
+ await runWriteHook(hooks, "after", {
4661
+ table: tableName,
4662
+ operation: "updateMany",
4663
+ single: false,
4664
+ args,
4665
+ rows: [],
4666
+ context,
4667
+ info,
4668
+ tx: executor
4669
+ });
4670
+ return { isSuccess: true };
4671
+ },
4672
+ !!hooks
4673
+ );
4520
4674
  } catch (e) {
4521
4675
  throw toGraphQLError(e);
4522
4676
  }
@@ -4526,36 +4680,64 @@ var generateUpdateMany = (db, tableName, table, updateManyInput, fieldName, filt
4526
4680
  };
4527
4681
  var generateDelete = (db, tableName, table, filterArgs, fieldName, single, requireWhere, filterCtx, txCtx, policies, restore = false) => {
4528
4682
  const softDelete = policies?.softDelete?.(tableName);
4683
+ const operation = restore ? "restore" : "delete";
4529
4684
  const queryArgs = {
4530
4685
  where: {
4531
4686
  type: single || requireWhere ? new import_graphql7.GraphQLNonNull(filterArgs) : filterArgs
4532
4687
  }
4533
4688
  };
4689
+ const hooks = policies?.onWrite?.(tableName, operation);
4534
4690
  return {
4535
4691
  name: fieldName,
4536
4692
  resolver: async (_source, args, context, info) => {
4537
4693
  try {
4538
- return await runMutation(db, context, info, txCtx, async (executor) => {
4539
- const { where } = args;
4540
- const scope = policies?.scope?.(context);
4541
- const relationCtx = relationFilterCtx(filterCtx, tableName);
4542
- const filters = withScope(
4543
- scope,
4544
- tableName,
4545
- table,
4546
- single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0,
4547
- restore ? "ONLY" : void 0
4548
- );
4549
- if (single) {
4550
- await assertSingleMatch(executor, table, filters, fieldName);
4551
- }
4552
- let query = softDelete ? executor.update(table).set({ [softDelete.columnName]: restore ? softDelete.writeRestored : softDelete.writeDeleted() }) : executor.delete(table);
4553
- if (filters) {
4554
- query = query.where(filters);
4555
- }
4556
- await query;
4557
- return { isSuccess: true };
4558
- });
4694
+ return await runMutation(
4695
+ db,
4696
+ context,
4697
+ info,
4698
+ txCtx,
4699
+ async (executor) => {
4700
+ await runWriteHook(hooks, "before", {
4701
+ table: tableName,
4702
+ operation,
4703
+ single,
4704
+ args,
4705
+ context,
4706
+ info,
4707
+ tx: executor
4708
+ });
4709
+ const { where } = args;
4710
+ const scope = policies?.scope?.(context);
4711
+ const relationCtx = relationFilterCtx(filterCtx, tableName);
4712
+ const filters = withScope(
4713
+ scope,
4714
+ tableName,
4715
+ table,
4716
+ single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0,
4717
+ restore ? "ONLY" : void 0
4718
+ );
4719
+ if (single) {
4720
+ await assertSingleMatch(executor, table, filters, fieldName);
4721
+ }
4722
+ let query = softDelete ? executor.update(table).set({ [softDelete.columnName]: restore ? softDelete.writeRestored : softDelete.writeDeleted() }) : executor.delete(table);
4723
+ if (filters) {
4724
+ query = query.where(filters);
4725
+ }
4726
+ await query;
4727
+ await runWriteHook(hooks, "after", {
4728
+ table: tableName,
4729
+ operation,
4730
+ single,
4731
+ args,
4732
+ rows: [],
4733
+ context,
4734
+ info,
4735
+ tx: executor
4736
+ });
4737
+ return { isSuccess: true };
4738
+ },
4739
+ !!hooks
4740
+ );
4559
4741
  } catch (e) {
4560
4742
  throw toGraphQLError(e);
4561
4743
  }
@@ -5029,6 +5211,64 @@ var import_graphql_parse_resolve_info3 = require("graphql-parse-resolve-info");
5029
5211
  var import_drizzle_orm7 = require("drizzle-orm");
5030
5212
  var import_graphql8 = require("graphql");
5031
5213
  var isOptionalOnInsert = (column) => !column.notNull || !!column.hasDefault || !!column.defaultFn;
5214
+ var propNameOf = (table, column) => {
5215
+ if (!column) {
5216
+ return void 0;
5217
+ }
5218
+ for (const [propName, candidate] of Object.entries((0, import_drizzle_orm7.getColumns)(table))) {
5219
+ if (candidate === column) {
5220
+ return propName;
5221
+ }
5222
+ }
5223
+ return void 0;
5224
+ };
5225
+ var throughColumn = (entry) => entry && (0, import_drizzle_orm7.is)(entry, import_drizzle_orm7.Column) ? entry : entry?._?.column;
5226
+ var buildThroughPlan = (relationName, relation, table, targetTableName, targetTable) => {
5227
+ if ((0, import_drizzle_orm7.is)(relation, import_drizzle_orm7.One)) {
5228
+ return void 0;
5229
+ }
5230
+ const junction = relation.throughTable;
5231
+ const sourceEntries = relation.through?.source;
5232
+ const targetEntries = relation.through?.target;
5233
+ if (!junction || sourceEntries?.length !== 1 || targetEntries?.length !== 1) {
5234
+ return void 0;
5235
+ }
5236
+ if (relation.sourceColumns?.length !== 1 || relation.targetColumns?.length !== 1) {
5237
+ return void 0;
5238
+ }
5239
+ const localCol = relation.sourceColumns[0];
5240
+ const foreignCol = relation.targetColumns[0];
5241
+ const localColPropName = propNameOf(table, localCol);
5242
+ const foreignColPropName = propNameOf(targetTable, foreignCol);
5243
+ const sourceColPropName = propNameOf(junction, throughColumn(sourceEntries[0]));
5244
+ const targetColPropName = propNameOf(junction, throughColumn(targetEntries[0]));
5245
+ if (!localColPropName || !foreignColPropName || !sourceColPropName || !targetColPropName) {
5246
+ return void 0;
5247
+ }
5248
+ const junctionColumns = (0, import_drizzle_orm7.getColumns)(junction);
5249
+ const sourceCol = junctionColumns[sourceColPropName];
5250
+ const targetCol = junctionColumns[targetColPropName];
5251
+ const unsuppliable = Object.entries(junctionColumns).some(
5252
+ ([propName, column]) => propName !== sourceColPropName && propName !== targetColPropName && !isOptionalOnInsert(column)
5253
+ );
5254
+ if (unsuppliable) {
5255
+ return void 0;
5256
+ }
5257
+ return {
5258
+ relationName,
5259
+ targetTableName,
5260
+ targetTable,
5261
+ localColPropName,
5262
+ foreignColPropName,
5263
+ foreignCol,
5264
+ localCol,
5265
+ isOne: false,
5266
+ // The link is written after the parent row exists, exactly like a child-side foreign key.
5267
+ fkSide: "child",
5268
+ canDetach: true,
5269
+ through: { table: junction, sourceColPropName, targetColPropName, sourceCol, targetCol }
5270
+ };
5271
+ };
5032
5272
  var buildNestedWritePlans = (tables, relationMap, resolveUniqueSets, resolvePrimaryKeys, extractJoinColumns) => {
5033
5273
  const uniqueCache = /* @__PURE__ */ new Map();
5034
5274
  const singleColumnUniques = (tableName, table) => {
@@ -5058,14 +5298,18 @@ var buildNestedWritePlans = (tables, relationMap, resolveUniqueSets, resolvePrim
5058
5298
  const tablePlans = {};
5059
5299
  for (const [relationName, relEntry] of Object.entries(relations)) {
5060
5300
  const relation = relEntry.relation;
5061
- if (relation.through) {
5062
- continue;
5063
- }
5064
5301
  const targetTableName = relEntry.targetTableName;
5065
5302
  const targetTable = tables[targetTableName];
5066
5303
  if (!targetTable) {
5067
5304
  continue;
5068
5305
  }
5306
+ if (relation.through) {
5307
+ const throughPlan = buildThroughPlan(relationName, relation, table, targetTableName, targetTable);
5308
+ if (throughPlan) {
5309
+ tablePlans[relationName] = throughPlan;
5310
+ }
5311
+ continue;
5312
+ }
5069
5313
  const joinColumns = extractJoinColumns(relEntry, table, targetTable);
5070
5314
  if (!joinColumns) {
5071
5315
  continue;
@@ -5150,11 +5394,31 @@ var createNestedWriteTypes = (params) => {
5150
5394
  return cached;
5151
5395
  }
5152
5396
  const filters = targetFilters(plan);
5153
- const payload = payloadType(tableName, typeName, plan);
5154
5397
  const targetTypeName = resolveTypeName(plan.targetTableName, typeNameMapper);
5155
5398
  const fields = {};
5156
- if (plan.isOne) {
5157
- fields["create"] = { type: payload, description: `Insert a ${targetTypeName} row and attach it` };
5399
+ if (plan.through) {
5400
+ if (!filters) {
5401
+ return void 0;
5402
+ }
5403
+ fields["connect"] = {
5404
+ type: listOf(filters),
5405
+ description: `Link every existing ${targetTypeName} row these match. Already-linked rows are left alone.`
5406
+ };
5407
+ if (forUpdate) {
5408
+ fields["disconnect"] = {
5409
+ type: listOf(filters),
5410
+ description: `Unlink every linked ${targetTypeName} row these match, leaving the rows in place`
5411
+ };
5412
+ fields["set"] = {
5413
+ type: listOf(filters),
5414
+ description: `Replace the whole set: unlink everything linked, then link every ${targetTypeName} row these match. \`set: []\` clears the relation. Applied before \`disconnect\` and \`connect\`.`
5415
+ };
5416
+ }
5417
+ } else if (plan.isOne) {
5418
+ fields["create"] = {
5419
+ type: payloadType(tableName, typeName, plan),
5420
+ description: `Insert a ${targetTypeName} row and attach it`
5421
+ };
5158
5422
  if (filters) {
5159
5423
  fields["connect"] = {
5160
5424
  type: filters,
@@ -5168,7 +5432,10 @@ var createNestedWriteTypes = (params) => {
5168
5432
  };
5169
5433
  }
5170
5434
  } else {
5171
- fields["create"] = { type: listOf(payload), description: `Insert these ${targetTypeName} rows and attach them` };
5435
+ fields["create"] = {
5436
+ type: listOf(payloadType(tableName, typeName, plan)),
5437
+ description: `Insert these ${targetTypeName} rows and attach them`
5438
+ };
5172
5439
  if (filters) {
5173
5440
  fields["connect"] = {
5174
5441
  type: listOf(filters),
@@ -5250,6 +5517,48 @@ var createNestedWriteRuntime = (params) => {
5250
5517
  );
5251
5518
  }
5252
5519
  };
5520
+ const selectedTargetKeys = async (executor, plan, filter, operation, scope) => {
5521
+ const rows = await executor.select().from(plan.targetTable).where(conditionOf(plan, filter, operation, scope));
5522
+ return rows.map((row) => row[plan.foreignColPropName]).filter((value) => value !== void 0 && value !== null);
5523
+ };
5524
+ const applyThroughOps = async (executor, plan, key, op, scope) => {
5525
+ const through = plan.through;
5526
+ const unlinkAll = () => executor.delete(through.table).where((0, import_drizzle_orm7.eq)(through.sourceCol, key));
5527
+ const link = async (operands, operation) => {
5528
+ const existing = new Set(
5529
+ (await executor.select().from(through.table).where((0, import_drizzle_orm7.eq)(through.sourceCol, key))).map(
5530
+ (row) => row[through.targetColPropName]
5531
+ )
5532
+ );
5533
+ const rows = [];
5534
+ for (const filter of operands) {
5535
+ for (const targetKey of await selectedTargetKeys(executor, plan, filter, operation, scope)) {
5536
+ if (!existing.has(targetKey)) {
5537
+ existing.add(targetKey);
5538
+ rows.push({ [through.sourceColPropName]: key, [through.targetColPropName]: targetKey });
5539
+ }
5540
+ }
5541
+ }
5542
+ if (rows.length) {
5543
+ await executor.insert(through.table).values(rows);
5544
+ }
5545
+ };
5546
+ if (op["set"] !== void 0 && op["set"] !== null) {
5547
+ await unlinkAll();
5548
+ await link(asArray(op["set"]), "set");
5549
+ }
5550
+ if (op["disconnect"] !== void 0 && op["disconnect"] !== null) {
5551
+ for (const filter of asArray(op["disconnect"])) {
5552
+ const targetKeys = await selectedTargetKeys(executor, plan, filter, "disconnect", scope);
5553
+ if (targetKeys.length) {
5554
+ await executor.delete(through.table).where((0, import_drizzle_orm7.and)((0, import_drizzle_orm7.eq)(through.sourceCol, key), (0, import_drizzle_orm7.inArray)(through.targetCol, targetKeys)));
5555
+ }
5556
+ }
5557
+ }
5558
+ if (op["connect"] !== void 0 && op["connect"] !== null) {
5559
+ await link(asArray(op["connect"]), "connect");
5560
+ }
5561
+ };
5253
5562
  const detachAll = async (executor, plan, key, scope) => executor.update(plan.targetTable).set({ [plan.foreignColPropName]: null }).where(withScope(scope, plan.targetTableName, plan.targetTable, (0, import_drizzle_orm7.eq)(plan.foreignCol, key)));
5254
5563
  return {
5255
5564
  enabled: (tableName) => !!plans[tableName],
@@ -5346,6 +5655,10 @@ var createNestedWriteRuntime = (params) => {
5346
5655
  `Drizzle-GraphQL Error: cannot write through '${relationName}': the ${tableName} row has no '${plan.localColPropName}' value to attach to.`
5347
5656
  );
5348
5657
  }
5658
+ if (plan.through) {
5659
+ await applyThroughOps(executor, plan, key, op, scope);
5660
+ continue;
5661
+ }
5349
5662
  if (op["set"] !== void 0) {
5350
5663
  await detachAll(executor, plan, key, scope);
5351
5664
  for (const filter of asArray(op["set"])) {
@@ -5625,58 +5938,85 @@ var generateInsertArray2 = (db, tableName, table, tables, relationMap, baseType,
5625
5938
  }
5626
5939
  };
5627
5940
  const pkNames = pgPrimaryKeyPropNames(table);
5941
+ const hooks = policies?.onWrite?.(tableName, "insert");
5628
5942
  return {
5629
5943
  name: fieldName,
5630
5944
  resolver: async (_source, args, context, info) => {
5631
5945
  try {
5632
- return await runMutation(db, context, info, txCtx, async (executor) => {
5633
- if (!args.values.length) {
5634
- throw new import_graphql9.GraphQLError("No values were provided!");
5635
- }
5636
- const entries = nested?.enabled(tableName) ? args.values.map((values) => nested.split(tableName, values)) : void 0;
5637
- const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
5638
- const contextColumns = policies?.contextValues?.(tableName);
5639
- const scope = policies?.scope?.(context);
5640
- const input = nestedEntries ? [] : applyContextValuesAll(
5641
- remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : args.values, table),
5642
- contextColumns,
5643
- context
5644
- );
5645
- const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, {
5646
- deep: true
5647
- });
5648
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
5649
- relationMap,
5650
- tables,
5651
- tableName,
5652
- typeName,
5653
- typeNameMapper,
5654
- table,
5655
- pkNames,
5656
- parsedInfo,
5657
- limits,
5658
- scope
5659
- });
5660
- const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
5661
- const runInsert = async (target, values) => {
5662
- let query = target.insert(table).values(values).returning(returning);
5663
- if (conflictDoNothing) {
5664
- query = query.onConflictDoNothing();
5946
+ return await runMutation(
5947
+ db,
5948
+ context,
5949
+ info,
5950
+ txCtx,
5951
+ async (executor) => {
5952
+ if (!args.values.length) {
5953
+ throw new import_graphql9.GraphQLError("No values were provided!");
5665
5954
  }
5666
- return await query;
5667
- };
5668
- const result = nestedEntries ? await writeWithNestedOps({
5669
- executor,
5670
- runtime: nested,
5671
- tableName,
5672
- entries: nestedEntries,
5673
- remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
5674
- write: (tx, values) => runInsert(tx, [values]),
5675
- context
5676
- }) : await runInsert(executor, input);
5677
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
5678
- return remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
5679
- });
5955
+ await runWriteHook(hooks, "before", {
5956
+ table: tableName,
5957
+ operation: "insert",
5958
+ single: false,
5959
+ args,
5960
+ context,
5961
+ info,
5962
+ tx: executor
5963
+ });
5964
+ const entries = nested?.enabled(tableName) ? args.values.map((values) => nested.split(tableName, values)) : void 0;
5965
+ const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
5966
+ const contextColumns = policies?.contextValues?.(tableName);
5967
+ const scope = policies?.scope?.(context);
5968
+ const input = nestedEntries ? [] : applyContextValuesAll(
5969
+ remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : args.values, table),
5970
+ contextColumns,
5971
+ context
5972
+ );
5973
+ const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, {
5974
+ deep: true
5975
+ });
5976
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
5977
+ relationMap,
5978
+ tables,
5979
+ tableName,
5980
+ typeName,
5981
+ typeNameMapper,
5982
+ table,
5983
+ pkNames,
5984
+ parsedInfo,
5985
+ limits,
5986
+ scope
5987
+ });
5988
+ const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
5989
+ const runInsert = async (target, values) => {
5990
+ let query = target.insert(table).values(values).returning(returning);
5991
+ if (conflictDoNothing) {
5992
+ query = query.onConflictDoNothing();
5993
+ }
5994
+ return await query;
5995
+ };
5996
+ const result = nestedEntries ? await writeWithNestedOps({
5997
+ executor,
5998
+ runtime: nested,
5999
+ tableName,
6000
+ entries: nestedEntries,
6001
+ remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
6002
+ write: (tx, values) => runInsert(tx, [values]),
6003
+ context
6004
+ }) : await runInsert(executor, input);
6005
+ await runWriteHook(hooks, "after", {
6006
+ table: tableName,
6007
+ operation: "insert",
6008
+ single: false,
6009
+ args,
6010
+ rows: result,
6011
+ context,
6012
+ info,
6013
+ tx: executor
6014
+ });
6015
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6016
+ return remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
6017
+ },
6018
+ !!hooks
6019
+ );
5680
6020
  } catch (e) {
5681
6021
  throw toGraphQLError(e);
5682
6022
  }
@@ -5691,61 +6031,88 @@ var generateInsertSingle2 = (db, tableName, table, tables, relationMap, baseType
5691
6031
  }
5692
6032
  };
5693
6033
  const pkNames = pgPrimaryKeyPropNames(table);
6034
+ const hooks = policies?.onWrite?.(tableName, "insert");
5694
6035
  return {
5695
6036
  name: fieldName,
5696
6037
  resolver: async (_source, args, context, info) => {
5697
6038
  try {
5698
- return await runMutation(db, context, info, txCtx, async (executor) => {
5699
- const entry = nested?.enabled(tableName) ? nested.split(tableName, args.values) : void 0;
5700
- const nestedEntry = entry && nested.hasOps(entry.ops) ? entry : void 0;
5701
- const contextColumns = policies?.contextValues?.(tableName);
5702
- const scope = policies?.scope?.(context);
5703
- const input = nestedEntry ? {} : applyContextValues(
5704
- remapFromGraphQLSingleInput(entry ? entry.columns : args.values, table),
5705
- contextColumns,
5706
- context
5707
- );
5708
- const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, {
5709
- deep: true
5710
- });
5711
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
5712
- relationMap,
5713
- tables,
5714
- tableName,
5715
- typeName,
5716
- typeNameMapper,
5717
- table,
5718
- pkNames,
5719
- parsedInfo,
5720
- limits,
5721
- scope
5722
- });
5723
- const returning = nestedEntry ? nested.withJoinColumns(tableName, nestedEntry.ops, { ...columns }, table) : columns;
5724
- const runInsert = async (target, values) => {
5725
- let query = target.insert(table).values(values).returning(returning);
5726
- if (conflictDoNothing) {
5727
- query = query.onConflictDoNothing();
5728
- }
5729
- return await query;
5730
- };
5731
- const result = nestedEntry ? await writeWithNestedOps({
5732
- executor,
5733
- runtime: nested,
5734
- tableName,
5735
- entries: [nestedEntry],
5736
- remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
5737
- write: runInsert,
5738
- context
5739
- }) : await runInsert(executor, input);
5740
- if (!result[0]) {
5741
- if (!conflictDoNothing) {
5742
- throw new import_graphql9.GraphQLError(`${fieldName}: the insert returned no row.`);
6039
+ return await runMutation(
6040
+ db,
6041
+ context,
6042
+ info,
6043
+ txCtx,
6044
+ async (executor) => {
6045
+ await runWriteHook(hooks, "before", {
6046
+ table: tableName,
6047
+ operation: "insert",
6048
+ single: true,
6049
+ args,
6050
+ context,
6051
+ info,
6052
+ tx: executor
6053
+ });
6054
+ const entry = nested?.enabled(tableName) ? nested.split(tableName, args.values) : void 0;
6055
+ const nestedEntry = entry && nested.hasOps(entry.ops) ? entry : void 0;
6056
+ const contextColumns = policies?.contextValues?.(tableName);
6057
+ const scope = policies?.scope?.(context);
6058
+ const input = nestedEntry ? {} : applyContextValues(
6059
+ remapFromGraphQLSingleInput(entry ? entry.columns : args.values, table),
6060
+ contextColumns,
6061
+ context
6062
+ );
6063
+ const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, {
6064
+ deep: true
6065
+ });
6066
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6067
+ relationMap,
6068
+ tables,
6069
+ tableName,
6070
+ typeName,
6071
+ typeNameMapper,
6072
+ table,
6073
+ pkNames,
6074
+ parsedInfo,
6075
+ limits,
6076
+ scope
6077
+ });
6078
+ const returning = nestedEntry ? nested.withJoinColumns(tableName, nestedEntry.ops, { ...columns }, table) : columns;
6079
+ const runInsert = async (target, values) => {
6080
+ let query = target.insert(table).values(values).returning(returning);
6081
+ if (conflictDoNothing) {
6082
+ query = query.onConflictDoNothing();
6083
+ }
6084
+ return await query;
6085
+ };
6086
+ const result = nestedEntry ? await writeWithNestedOps({
6087
+ executor,
6088
+ runtime: nested,
6089
+ tableName,
6090
+ entries: [nestedEntry],
6091
+ remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
6092
+ write: runInsert,
6093
+ context
6094
+ }) : await runInsert(executor, input);
6095
+ await runWriteHook(hooks, "after", {
6096
+ table: tableName,
6097
+ operation: "insert",
6098
+ single: true,
6099
+ args,
6100
+ rows: result,
6101
+ context,
6102
+ info,
6103
+ tx: executor
6104
+ });
6105
+ if (!result[0]) {
6106
+ if (!conflictDoNothing) {
6107
+ throw new import_graphql9.GraphQLError(`${fieldName}: the insert returned no row.`);
6108
+ }
6109
+ return void 0;
5743
6110
  }
5744
- return void 0;
5745
- }
5746
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
5747
- return remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap);
5748
- });
6111
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6112
+ return remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap);
6113
+ },
6114
+ !!hooks
6115
+ );
5749
6116
  } catch (e) {
5750
6117
  throw toGraphQLError(e);
5751
6118
  }
@@ -5764,69 +6131,96 @@ var generateUpsert2 = (db, tableName, table, tables, relationMap, baseType, onCo
5764
6131
  }
5765
6132
  };
5766
6133
  const pkNames = pgPrimaryKeyPropNames(table);
6134
+ const hooks = policies?.onWrite?.(tableName, "upsert");
5767
6135
  return {
5768
6136
  name: fieldName,
5769
6137
  resolver: async (_source, args, context, info) => {
5770
6138
  try {
5771
- return await runMutation(db, context, info, txCtx, async (executor) => {
5772
- const supplied = single ? [args.values] : args.values;
5773
- if (!supplied.length) {
5774
- throw new import_graphql9.GraphQLError("No values were provided!");
5775
- }
5776
- const entries = nested?.enabled(tableName) ? supplied.map((values) => nested.split(tableName, values)) : void 0;
5777
- const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
5778
- const contextColumns = policies?.contextValues?.(tableName);
5779
- const scope = policies?.scope?.(context);
5780
- const input = nestedEntries ? [] : applyContextValuesAll(
5781
- remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : supplied, table),
5782
- contextColumns,
5783
- context
5784
- );
5785
- const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, { deep: true });
5786
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
5787
- relationMap,
5788
- tables,
5789
- tableName,
5790
- typeName,
5791
- typeNameMapper,
5792
- table,
5793
- pkNames,
5794
- parsedInfo,
5795
- limits,
5796
- scope
5797
- });
5798
- const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
5799
- const runUpsert = async (target, values) => {
5800
- const plan = resolveConflictPlan({
5801
- table,
5802
- values,
5803
- onConflict: args.onConflict,
6139
+ return await runMutation(
6140
+ db,
6141
+ context,
6142
+ info,
6143
+ txCtx,
6144
+ async (executor) => {
6145
+ const supplied = single ? [args.values] : args.values;
6146
+ if (!supplied.length) {
6147
+ throw new import_graphql9.GraphQLError("No values were provided!");
6148
+ }
6149
+ await runWriteHook(hooks, "before", {
6150
+ table: tableName,
6151
+ operation: "upsert",
6152
+ single,
6153
+ args,
6154
+ context,
6155
+ info,
6156
+ tx: executor
6157
+ });
6158
+ const entries = nested?.enabled(tableName) ? supplied.map((values) => nested.split(tableName, values)) : void 0;
6159
+ const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
6160
+ const contextColumns = policies?.contextValues?.(tableName);
6161
+ const scope = policies?.scope?.(context);
6162
+ const input = nestedEntries ? [] : applyContextValuesAll(
6163
+ remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : supplied, table),
6164
+ contextColumns,
6165
+ context
6166
+ );
6167
+ const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, { deep: true });
6168
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6169
+ relationMap,
6170
+ tables,
6171
+ tableName,
6172
+ typeName,
6173
+ typeNameMapper,
6174
+ table,
5804
6175
  pkNames,
5805
- uniqueSets,
5806
- excludedRef: excludedColumnRef,
5807
- withTarget: true,
5808
- buildWhere: (where) => extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName))
6176
+ parsedInfo,
6177
+ limits,
6178
+ scope
5809
6179
  });
5810
- let query = target.insert(table).values(values).returning(returning);
5811
- const setWhere = withScope(scope, tableName, table, plan.setWhere);
5812
- query = plan.action === "NOTHING" ? query.onConflictDoNothing(plan.target ? { target: plan.target } : void 0) : query.onConflictDoUpdate({ target: plan.target, set: plan.set, setWhere });
5813
- return await query;
5814
- };
5815
- const result = nestedEntries ? await writeWithNestedOps({
5816
- executor,
5817
- runtime: nested,
5818
- tableName,
5819
- entries: nestedEntries,
5820
- remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
5821
- write: (tx, values) => runUpsert(tx, [values]),
5822
- context
5823
- }) : await runUpsert(executor, input);
5824
- if (single && !result[0]) {
5825
- return void 0;
5826
- }
5827
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
5828
- return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
5829
- });
6180
+ const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
6181
+ const runUpsert = async (target, values) => {
6182
+ const plan = resolveConflictPlan({
6183
+ table,
6184
+ values,
6185
+ onConflict: args.onConflict,
6186
+ pkNames,
6187
+ uniqueSets,
6188
+ excludedRef: excludedColumnRef,
6189
+ withTarget: true,
6190
+ buildWhere: (where) => extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName))
6191
+ });
6192
+ let query = target.insert(table).values(values).returning(returning);
6193
+ const setWhere = withScope(scope, tableName, table, plan.setWhere);
6194
+ query = plan.action === "NOTHING" ? query.onConflictDoNothing(plan.target ? { target: plan.target } : void 0) : query.onConflictDoUpdate({ target: plan.target, set: plan.set, setWhere });
6195
+ return await query;
6196
+ };
6197
+ const result = nestedEntries ? await writeWithNestedOps({
6198
+ executor,
6199
+ runtime: nested,
6200
+ tableName,
6201
+ entries: nestedEntries,
6202
+ remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
6203
+ write: (tx, values) => runUpsert(tx, [values]),
6204
+ context
6205
+ }) : await runUpsert(executor, input);
6206
+ await runWriteHook(hooks, "after", {
6207
+ table: tableName,
6208
+ operation: "upsert",
6209
+ single,
6210
+ args,
6211
+ rows: result,
6212
+ context,
6213
+ info,
6214
+ tx: executor
6215
+ });
6216
+ if (single && !result[0]) {
6217
+ return void 0;
6218
+ }
6219
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6220
+ return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
6221
+ },
6222
+ !!hooks
6223
+ );
5830
6224
  } catch (e) {
5831
6225
  throw toGraphQLError(e);
5832
6226
  }
@@ -5844,75 +6238,102 @@ var generateUpdate2 = (db, tableName, table, tables, relationMap, setArgs, filte
5844
6238
  }
5845
6239
  };
5846
6240
  const pkNames = pgPrimaryKeyPropNames(table);
6241
+ const hooks = policies?.onWrite?.(tableName, "update");
5847
6242
  return {
5848
6243
  name: fieldName,
5849
6244
  resolver: async (_source, args, context, info) => {
5850
6245
  try {
5851
- return await runMutation(db, context, info, txCtx, async (executor) => {
5852
- const { where, set } = args;
5853
- const scope = policies?.scope?.(context);
5854
- const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, {
5855
- deep: true
5856
- });
5857
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
5858
- relationMap,
5859
- tables,
5860
- tableName,
5861
- typeName,
5862
- typeNameMapper,
5863
- table,
5864
- pkNames,
5865
- parsedInfo,
5866
- limits,
5867
- scope
5868
- });
5869
- const entry = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
5870
- const nestedOps = entry && nested.hasOps(entry.ops) ? entry.ops : void 0;
5871
- const input = stripContextValues(
5872
- remapUpdateInput(entry ? entry.columns : set, table, tableName),
5873
- policies?.contextValues?.(tableName)
5874
- );
5875
- if (!Object.keys(input).length && !nestedOps) {
5876
- throw new import_graphql9.GraphQLError("Unable to update with no values specified!");
5877
- }
5878
- const relationCtx = relationFilterCtx(filterCtx, tableName);
5879
- const filters = withScope(
5880
- scope,
5881
- tableName,
5882
- table,
5883
- single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
5884
- );
5885
- if (single) {
5886
- await assertSingleMatch(executor, table, filters, fieldName);
5887
- }
5888
- const returning = nestedOps ? nested.withJoinColumns(tableName, nestedOps, { ...columns }, table) : columns;
5889
- const runUpdate = async (target, values) => {
5890
- const writes = Object.keys(values).length > 0;
5891
- let query = writes ? target.update(table).set(values) : target.select(returning).from(table);
5892
- if (filters) {
5893
- query = query.where(filters);
6246
+ return await runMutation(
6247
+ db,
6248
+ context,
6249
+ info,
6250
+ txCtx,
6251
+ async (executor) => {
6252
+ const { where, set } = args;
6253
+ const scope = policies?.scope?.(context);
6254
+ await runWriteHook(hooks, "before", {
6255
+ table: tableName,
6256
+ operation: "update",
6257
+ single,
6258
+ args,
6259
+ context,
6260
+ info,
6261
+ tx: executor
6262
+ });
6263
+ const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, {
6264
+ deep: true
6265
+ });
6266
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6267
+ relationMap,
6268
+ tables,
6269
+ tableName,
6270
+ typeName,
6271
+ typeNameMapper,
6272
+ table,
6273
+ pkNames,
6274
+ parsedInfo,
6275
+ limits,
6276
+ scope
6277
+ });
6278
+ const entry = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
6279
+ const nestedOps = entry && nested.hasOps(entry.ops) ? entry.ops : void 0;
6280
+ const input = stripContextValues(
6281
+ remapUpdateInput(entry ? entry.columns : set, table, tableName),
6282
+ policies?.contextValues?.(tableName)
6283
+ );
6284
+ if (!Object.keys(input).length && !nestedOps) {
6285
+ throw new import_graphql9.GraphQLError("Unable to update with no values specified!");
5894
6286
  }
5895
- return await (writes ? query.returning(returning) : query);
5896
- };
5897
- const result = nestedOps ? await updateWithNestedOps({
5898
- executor,
5899
- runtime: nested,
5900
- tableName,
5901
- columns: input,
5902
- ops: nestedOps,
5903
- remapValues: (values) => values,
5904
- write: runUpdate,
5905
- context
5906
- }) : await runUpdate(executor, input);
5907
- if (single && result.length > 1) {
5908
- throw new import_graphql9.GraphQLError(`${fieldName}: 'where' matched more than one row!`);
5909
- }
5910
- if (single && !result[0]) {
5911
- return void 0;
5912
- }
5913
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
5914
- return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
5915
- });
6287
+ const relationCtx = relationFilterCtx(filterCtx, tableName);
6288
+ const filters = withScope(
6289
+ scope,
6290
+ tableName,
6291
+ table,
6292
+ single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
6293
+ );
6294
+ if (single) {
6295
+ await assertSingleMatch(executor, table, filters, fieldName);
6296
+ }
6297
+ const returning = nestedOps ? nested.withJoinColumns(tableName, nestedOps, { ...columns }, table) : columns;
6298
+ const runUpdate = async (target, values) => {
6299
+ const writes = Object.keys(values).length > 0;
6300
+ let query = writes ? target.update(table).set(values) : target.select(returning).from(table);
6301
+ if (filters) {
6302
+ query = query.where(filters);
6303
+ }
6304
+ return await (writes ? query.returning(returning) : query);
6305
+ };
6306
+ const result = nestedOps ? await updateWithNestedOps({
6307
+ executor,
6308
+ runtime: nested,
6309
+ tableName,
6310
+ columns: input,
6311
+ ops: nestedOps,
6312
+ remapValues: (values) => values,
6313
+ write: runUpdate,
6314
+ context
6315
+ }) : await runUpdate(executor, input);
6316
+ await runWriteHook(hooks, "after", {
6317
+ table: tableName,
6318
+ operation: "update",
6319
+ single,
6320
+ args,
6321
+ rows: result,
6322
+ context,
6323
+ info,
6324
+ tx: executor
6325
+ });
6326
+ if (single && result.length > 1) {
6327
+ throw new import_graphql9.GraphQLError(`${fieldName}: 'where' matched more than one row!`);
6328
+ }
6329
+ if (single && !result[0]) {
6330
+ return void 0;
6331
+ }
6332
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6333
+ return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
6334
+ },
6335
+ !!hooks
6336
+ );
5916
6337
  } catch (e) {
5917
6338
  throw toGraphQLError(e);
5918
6339
  }
@@ -5927,94 +6348,121 @@ var generateUpdateMany2 = (db, tableName, table, tables, relationMap, updateMany
5927
6348
  }
5928
6349
  };
5929
6350
  const pkNames = pgPrimaryKeyPropNames(table);
6351
+ const hooks = policies?.onWrite?.(tableName, "updateMany");
5930
6352
  return {
5931
6353
  name: fieldName,
5932
6354
  resolver: async (_source, args, context, info) => {
5933
6355
  try {
5934
- return await runMutation(db, context, info, txCtx, async (executor) => {
5935
- const { updates } = args;
5936
- if (!updates.length) {
5937
- throw new import_graphql9.GraphQLError("No updates were provided!");
5938
- }
5939
- const scope = policies?.scope?.(context);
5940
- const contextColumns = policies?.contextValues?.(tableName);
5941
- const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, {
5942
- deep: true
5943
- });
5944
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
5945
- relationMap,
5946
- tables,
5947
- tableName,
5948
- typeName,
5949
- typeNameMapper,
5950
- table,
5951
- pkNames,
5952
- parsedInfo,
5953
- limits,
5954
- scope
5955
- });
5956
- const entries = updates.map(({ where, set }) => {
5957
- const split = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
5958
- const ops = split && nested.hasOps(split.ops) ? split.ops : void 0;
5959
- const input = stripContextValues(
5960
- remapUpdateInput(split ? split.columns : set, table, tableName),
5961
- contextColumns
5962
- );
5963
- if (!Object.keys(input).length && !ops) {
5964
- throw new import_graphql9.GraphQLError("Unable to update with no values specified!");
6356
+ return await runMutation(
6357
+ db,
6358
+ context,
6359
+ info,
6360
+ txCtx,
6361
+ async (executor) => {
6362
+ const { updates } = args;
6363
+ if (!updates.length) {
6364
+ throw new import_graphql9.GraphQLError("No updates were provided!");
5965
6365
  }
5966
- return {
5967
- set: input,
5968
- ops,
5969
- filters: withScope(
5970
- scope,
5971
- tableName,
5972
- table,
5973
- where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
5974
- )
5975
- };
5976
- });
5977
- const returning = entries.some((entry) => entry.ops) ? nested.withJoinColumns(
5978
- tableName,
5979
- mergedOps(entries.map((entry) => ({ ops: entry.ops ?? {} }))),
5980
- {
5981
- ...columns
5982
- },
5983
- table
5984
- ) : columns;
5985
- const perEntry = await executor.transaction(async (tx) => {
5986
- const results = [];
5987
- for (const entry of entries) {
5988
- const values = entry.ops ? { ...entry.set, ...await nested.applyParentSide(tx, tableName, entry.ops, context) } : entry.set;
5989
- const writes = Object.keys(values).length > 0;
5990
- let query = writes ? tx.update(table).set(values) : tx.select(returning).from(table);
5991
- if (entry.filters) {
5992
- query = query.where(entry.filters);
6366
+ await runWriteHook(hooks, "before", {
6367
+ table: tableName,
6368
+ operation: "updateMany",
6369
+ single: false,
6370
+ args,
6371
+ context,
6372
+ info,
6373
+ tx: executor
6374
+ });
6375
+ const scope = policies?.scope?.(context);
6376
+ const contextColumns = policies?.contextValues?.(tableName);
6377
+ const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, {
6378
+ deep: true
6379
+ });
6380
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6381
+ relationMap,
6382
+ tables,
6383
+ tableName,
6384
+ typeName,
6385
+ typeNameMapper,
6386
+ table,
6387
+ pkNames,
6388
+ parsedInfo,
6389
+ limits,
6390
+ scope
6391
+ });
6392
+ const entries = updates.map(({ where, set }) => {
6393
+ const split = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
6394
+ const ops = split && nested.hasOps(split.ops) ? split.ops : void 0;
6395
+ const input = stripContextValues(
6396
+ remapUpdateInput(split ? split.columns : set, table, tableName),
6397
+ contextColumns
6398
+ );
6399
+ if (!Object.keys(input).length && !ops) {
6400
+ throw new import_graphql9.GraphQLError("Unable to update with no values specified!");
5993
6401
  }
5994
- const rows = await (writes ? query.returning(returning) : query);
5995
- if (entry.ops) {
5996
- await nested.applyChildSide(tx, tableName, entry.ops, rows, context);
6402
+ return {
6403
+ set: input,
6404
+ ops,
6405
+ filters: withScope(
6406
+ scope,
6407
+ tableName,
6408
+ table,
6409
+ where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
6410
+ )
6411
+ };
6412
+ });
6413
+ const returning = entries.some((entry) => entry.ops) ? nested.withJoinColumns(
6414
+ tableName,
6415
+ mergedOps(entries.map((entry) => ({ ops: entry.ops ?? {} }))),
6416
+ {
6417
+ ...columns
6418
+ },
6419
+ table
6420
+ ) : columns;
6421
+ const perEntry = await executor.transaction(async (tx) => {
6422
+ const results = [];
6423
+ for (const entry of entries) {
6424
+ const values = entry.ops ? { ...entry.set, ...await nested.applyParentSide(tx, tableName, entry.ops, context) } : entry.set;
6425
+ const writes = Object.keys(values).length > 0;
6426
+ let query = writes ? tx.update(table).set(values) : tx.select(returning).from(table);
6427
+ if (entry.filters) {
6428
+ query = query.where(entry.filters);
6429
+ }
6430
+ const rows = await (writes ? query.returning(returning) : query);
6431
+ if (entry.ops) {
6432
+ await nested.applyChildSide(tx, tableName, entry.ops, rows, context);
6433
+ }
6434
+ results.push(rows);
5997
6435
  }
5998
- results.push(rows);
5999
- }
6000
- return results;
6001
- });
6002
- const flatRows = perEntry.flat();
6003
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, flatRows, pkNames, withParams) : flatRows;
6004
- const output = [];
6005
- let offset = 0;
6006
- for (const rows of perEntry) {
6007
- if (!rows.length) {
6008
- output.push(null);
6009
- continue;
6010
- }
6011
- for (let i = 0; i < rows.length; i++) {
6012
- output.push(remapToGraphQLSingleOutput(enriched[offset + i], tableName, table, relationMap));
6436
+ return results;
6437
+ });
6438
+ const flatRows = perEntry.flat();
6439
+ await runWriteHook(hooks, "after", {
6440
+ table: tableName,
6441
+ operation: "updateMany",
6442
+ single: false,
6443
+ args,
6444
+ rows: flatRows,
6445
+ context,
6446
+ info,
6447
+ tx: executor
6448
+ });
6449
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, flatRows, pkNames, withParams) : flatRows;
6450
+ const output = [];
6451
+ let offset = 0;
6452
+ for (const rows of perEntry) {
6453
+ if (!rows.length) {
6454
+ output.push(null);
6455
+ continue;
6456
+ }
6457
+ for (let i = 0; i < rows.length; i++) {
6458
+ output.push(remapToGraphQLSingleOutput(enriched[offset + i], tableName, table, relationMap));
6459
+ }
6460
+ offset += rows.length;
6013
6461
  }
6014
- offset += rows.length;
6015
- }
6016
- return output;
6017
- });
6462
+ return output;
6463
+ },
6464
+ !!hooks
6465
+ );
6018
6466
  } catch (e) {
6019
6467
  throw toGraphQLError(e);
6020
6468
  }
@@ -6024,6 +6472,8 @@ var generateUpdateMany2 = (db, tableName, table, tables, relationMap, updateMany
6024
6472
  };
6025
6473
  var generateDelete2 = (db, tableName, table, filterArgs, fieldName, typeName, single, requireWhere, filterCtx, selectionCtx, txCtx, policies, restore = false) => {
6026
6474
  const softDelete = policies?.softDelete?.(tableName);
6475
+ const operation = restore ? "restore" : "delete";
6476
+ const hooks = policies?.onWrite?.(tableName, operation);
6027
6477
  const queryArgs = {
6028
6478
  where: {
6029
6479
  type: single || requireWhere ? new import_graphql9.GraphQLNonNull(filterArgs) : filterArgs
@@ -6033,42 +6483,68 @@ var generateDelete2 = (db, tableName, table, filterArgs, fieldName, typeName, si
6033
6483
  name: fieldName,
6034
6484
  resolver: async (_source, args, context, info) => {
6035
6485
  try {
6036
- return await runMutation(db, context, info, txCtx, async (executor) => {
6037
- const { where } = args;
6038
- const scope = policies?.scope?.(context);
6039
- const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, {
6040
- deep: true
6041
- });
6042
- const columns = extractSelectedColumnsFromTreeSQLFormat(
6043
- parsedInfo.fieldsByTypeName[typeName],
6044
- table,
6045
- selectionCtx
6046
- );
6047
- const relationCtx = relationFilterCtx(filterCtx, tableName);
6048
- const filters = withScope(
6049
- scope,
6050
- tableName,
6051
- table,
6052
- single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0,
6053
- restore ? "ONLY" : void 0
6054
- );
6055
- if (single) {
6056
- await assertSingleMatch(executor, table, filters, fieldName);
6057
- }
6058
- let query = softDelete ? executor.update(table).set({ [softDelete.columnName]: restore ? softDelete.writeRestored : softDelete.writeDeleted() }) : executor.delete(table);
6059
- if (filters) {
6060
- query = query.where(filters);
6061
- }
6062
- query = query.returning(columns);
6063
- const result = await query;
6064
- if (single && result.length > 1) {
6065
- throw new import_graphql9.GraphQLError(`${fieldName}: 'where' matched more than one row!`);
6066
- }
6067
- if (single) {
6068
- return result[0] ? remapToGraphQLSingleOutput(result[0], tableName, table) : void 0;
6069
- }
6070
- return remapToGraphQLArrayOutput(result, tableName, table);
6071
- });
6486
+ return await runMutation(
6487
+ db,
6488
+ context,
6489
+ info,
6490
+ txCtx,
6491
+ async (executor) => {
6492
+ const { where } = args;
6493
+ const scope = policies?.scope?.(context);
6494
+ await runWriteHook(hooks, "before", {
6495
+ table: tableName,
6496
+ operation,
6497
+ single,
6498
+ args,
6499
+ context,
6500
+ info,
6501
+ tx: executor
6502
+ });
6503
+ const parsedInfo = (0, import_graphql_parse_resolve_info3.parseResolveInfo)(info, {
6504
+ deep: true
6505
+ });
6506
+ const columns = extractSelectedColumnsFromTreeSQLFormat(
6507
+ parsedInfo.fieldsByTypeName[typeName],
6508
+ table,
6509
+ selectionCtx
6510
+ );
6511
+ const relationCtx = relationFilterCtx(filterCtx, tableName);
6512
+ const filters = withScope(
6513
+ scope,
6514
+ tableName,
6515
+ table,
6516
+ single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0,
6517
+ restore ? "ONLY" : void 0
6518
+ );
6519
+ if (single) {
6520
+ await assertSingleMatch(executor, table, filters, fieldName);
6521
+ }
6522
+ let query = softDelete ? executor.update(table).set({ [softDelete.columnName]: restore ? softDelete.writeRestored : softDelete.writeDeleted() }) : executor.delete(table);
6523
+ if (filters) {
6524
+ query = query.where(filters);
6525
+ }
6526
+ query = query.returning(columns);
6527
+ const result = await query;
6528
+ await runWriteHook(hooks, "after", {
6529
+ table: tableName,
6530
+ operation,
6531
+ single,
6532
+ args,
6533
+ rows: result,
6534
+ context,
6535
+ info,
6536
+ tx: executor
6537
+ });
6538
+ if (single && result.length > 1) {
6539
+ throw new import_graphql9.GraphQLError(`${fieldName}: 'where' matched more than one row!`);
6540
+ }
6541
+ if (single) {
6542
+ return result[0] ? remapToGraphQLSingleOutput(result[0], tableName, table) : void 0;
6543
+ }
6544
+ return remapToGraphQLArrayOutput(result, tableName, table);
6545
+ },
6546
+ !!hooks
6547
+ );
6072
6548
  } catch (e) {
6073
6549
  throw toGraphQLError(e);
6074
6550
  }
@@ -6794,58 +7270,85 @@ var generateInsertArray3 = (db, tableName, table, tables, relationMap, baseType,
6794
7270
  }
6795
7271
  };
6796
7272
  const pkNames = sqlitePrimaryKeyPropNames(table);
7273
+ const hooks = policies?.onWrite?.(tableName, "insert");
6797
7274
  return {
6798
7275
  name: fieldName,
6799
7276
  resolver: async (_source, args, context, info) => {
6800
7277
  try {
6801
- return await runMutation(db, context, info, txCtx, async (executor) => {
6802
- if (!args.values.length) {
6803
- throw new import_graphql10.GraphQLError("No values were provided!");
6804
- }
6805
- const entries = nested?.enabled(tableName) ? args.values.map((values) => nested.split(tableName, values)) : void 0;
6806
- const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
6807
- const contextColumns = policies?.contextValues?.(tableName);
6808
- const scope = policies?.scope?.(context);
6809
- const input = nestedEntries ? [] : applyContextValuesAll(
6810
- remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : args.values, table),
6811
- contextColumns,
6812
- context
6813
- );
6814
- const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, {
6815
- deep: true
6816
- });
6817
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6818
- relationMap,
6819
- tables,
6820
- tableName,
6821
- typeName,
6822
- typeNameMapper,
6823
- table,
6824
- pkNames,
6825
- parsedInfo,
6826
- limits,
6827
- scope
6828
- });
6829
- const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
6830
- const runInsert = async (target, values) => {
6831
- let query = target.insert(table).values(values).returning(returning);
6832
- if (conflictDoNothing) {
6833
- query = query.onConflictDoNothing();
7278
+ return await runMutation(
7279
+ db,
7280
+ context,
7281
+ info,
7282
+ txCtx,
7283
+ async (executor) => {
7284
+ if (!args.values.length) {
7285
+ throw new import_graphql10.GraphQLError("No values were provided!");
6834
7286
  }
6835
- return await query;
6836
- };
6837
- const result = nestedEntries ? await writeWithNestedOps({
6838
- executor,
6839
- runtime: nested,
6840
- tableName,
6841
- entries: nestedEntries,
6842
- remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
6843
- write: (tx, values) => runInsert(tx, [values]),
6844
- context
6845
- }) : await runInsert(executor, input);
6846
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6847
- return remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
6848
- });
7287
+ await runWriteHook(hooks, "before", {
7288
+ table: tableName,
7289
+ operation: "insert",
7290
+ single: false,
7291
+ args,
7292
+ context,
7293
+ info,
7294
+ tx: executor
7295
+ });
7296
+ const entries = nested?.enabled(tableName) ? args.values.map((values) => nested.split(tableName, values)) : void 0;
7297
+ const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
7298
+ const contextColumns = policies?.contextValues?.(tableName);
7299
+ const scope = policies?.scope?.(context);
7300
+ const input = nestedEntries ? [] : applyContextValuesAll(
7301
+ remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : args.values, table),
7302
+ contextColumns,
7303
+ context
7304
+ );
7305
+ const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, {
7306
+ deep: true
7307
+ });
7308
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
7309
+ relationMap,
7310
+ tables,
7311
+ tableName,
7312
+ typeName,
7313
+ typeNameMapper,
7314
+ table,
7315
+ pkNames,
7316
+ parsedInfo,
7317
+ limits,
7318
+ scope
7319
+ });
7320
+ const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
7321
+ const runInsert = async (target, values) => {
7322
+ let query = target.insert(table).values(values).returning(returning);
7323
+ if (conflictDoNothing) {
7324
+ query = query.onConflictDoNothing();
7325
+ }
7326
+ return await query;
7327
+ };
7328
+ const result = nestedEntries ? await writeWithNestedOps({
7329
+ executor,
7330
+ runtime: nested,
7331
+ tableName,
7332
+ entries: nestedEntries,
7333
+ remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
7334
+ write: (tx, values) => runInsert(tx, [values]),
7335
+ context
7336
+ }) : await runInsert(executor, input);
7337
+ await runWriteHook(hooks, "after", {
7338
+ table: tableName,
7339
+ operation: "insert",
7340
+ single: false,
7341
+ args,
7342
+ rows: result,
7343
+ context,
7344
+ info,
7345
+ tx: executor
7346
+ });
7347
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
7348
+ return remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
7349
+ },
7350
+ !!hooks
7351
+ );
6849
7352
  } catch (e) {
6850
7353
  throw toGraphQLError(e);
6851
7354
  }
@@ -6860,61 +7363,88 @@ var generateInsertSingle3 = (db, tableName, table, tables, relationMap, baseType
6860
7363
  }
6861
7364
  };
6862
7365
  const pkNames = sqlitePrimaryKeyPropNames(table);
7366
+ const hooks = policies?.onWrite?.(tableName, "insert");
6863
7367
  return {
6864
7368
  name: fieldName,
6865
7369
  resolver: async (_source, args, context, info) => {
6866
7370
  try {
6867
- return await runMutation(db, context, info, txCtx, async (executor) => {
6868
- const entry = nested?.enabled(tableName) ? nested.split(tableName, args.values) : void 0;
6869
- const nestedEntry = entry && nested.hasOps(entry.ops) ? entry : void 0;
6870
- const contextColumns = policies?.contextValues?.(tableName);
6871
- const scope = policies?.scope?.(context);
6872
- const input = nestedEntry ? {} : applyContextValues(
6873
- remapFromGraphQLSingleInput(entry ? entry.columns : args.values, table),
6874
- contextColumns,
6875
- context
6876
- );
6877
- const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, {
6878
- deep: true
6879
- });
6880
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6881
- relationMap,
6882
- tables,
6883
- tableName,
6884
- typeName,
6885
- typeNameMapper,
6886
- table,
6887
- pkNames,
6888
- parsedInfo,
6889
- limits,
6890
- scope
6891
- });
6892
- const returning = nestedEntry ? nested.withJoinColumns(tableName, nestedEntry.ops, { ...columns }, table) : columns;
6893
- const runInsert = async (target, values) => {
6894
- let query = target.insert(table).values(values).returning(returning);
6895
- if (conflictDoNothing) {
6896
- query = query.onConflictDoNothing();
6897
- }
6898
- return await query;
6899
- };
6900
- const result = nestedEntry ? await writeWithNestedOps({
6901
- executor,
6902
- runtime: nested,
6903
- tableName,
6904
- entries: [nestedEntry],
6905
- remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
6906
- write: runInsert,
6907
- context
6908
- }) : await runInsert(executor, input);
6909
- if (!result[0]) {
6910
- if (!conflictDoNothing) {
6911
- throw new import_graphql10.GraphQLError(`${fieldName}: the insert returned no row.`);
7371
+ return await runMutation(
7372
+ db,
7373
+ context,
7374
+ info,
7375
+ txCtx,
7376
+ async (executor) => {
7377
+ await runWriteHook(hooks, "before", {
7378
+ table: tableName,
7379
+ operation: "insert",
7380
+ single: true,
7381
+ args,
7382
+ context,
7383
+ info,
7384
+ tx: executor
7385
+ });
7386
+ const entry = nested?.enabled(tableName) ? nested.split(tableName, args.values) : void 0;
7387
+ const nestedEntry = entry && nested.hasOps(entry.ops) ? entry : void 0;
7388
+ const contextColumns = policies?.contextValues?.(tableName);
7389
+ const scope = policies?.scope?.(context);
7390
+ const input = nestedEntry ? {} : applyContextValues(
7391
+ remapFromGraphQLSingleInput(entry ? entry.columns : args.values, table),
7392
+ contextColumns,
7393
+ context
7394
+ );
7395
+ const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, {
7396
+ deep: true
7397
+ });
7398
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
7399
+ relationMap,
7400
+ tables,
7401
+ tableName,
7402
+ typeName,
7403
+ typeNameMapper,
7404
+ table,
7405
+ pkNames,
7406
+ parsedInfo,
7407
+ limits,
7408
+ scope
7409
+ });
7410
+ const returning = nestedEntry ? nested.withJoinColumns(tableName, nestedEntry.ops, { ...columns }, table) : columns;
7411
+ const runInsert = async (target, values) => {
7412
+ let query = target.insert(table).values(values).returning(returning);
7413
+ if (conflictDoNothing) {
7414
+ query = query.onConflictDoNothing();
7415
+ }
7416
+ return await query;
7417
+ };
7418
+ const result = nestedEntry ? await writeWithNestedOps({
7419
+ executor,
7420
+ runtime: nested,
7421
+ tableName,
7422
+ entries: [nestedEntry],
7423
+ remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
7424
+ write: runInsert,
7425
+ context
7426
+ }) : await runInsert(executor, input);
7427
+ await runWriteHook(hooks, "after", {
7428
+ table: tableName,
7429
+ operation: "insert",
7430
+ single: true,
7431
+ args,
7432
+ rows: result,
7433
+ context,
7434
+ info,
7435
+ tx: executor
7436
+ });
7437
+ if (!result[0]) {
7438
+ if (!conflictDoNothing) {
7439
+ throw new import_graphql10.GraphQLError(`${fieldName}: the insert returned no row.`);
7440
+ }
7441
+ return void 0;
6912
7442
  }
6913
- return void 0;
6914
- }
6915
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6916
- return remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap);
6917
- });
7443
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
7444
+ return remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap);
7445
+ },
7446
+ !!hooks
7447
+ );
6918
7448
  } catch (e) {
6919
7449
  throw toGraphQLError(e);
6920
7450
  }
@@ -6933,69 +7463,96 @@ var generateUpsert3 = (db, tableName, table, tables, relationMap, baseType, onCo
6933
7463
  }
6934
7464
  };
6935
7465
  const pkNames = sqlitePrimaryKeyPropNames(table);
7466
+ const hooks = policies?.onWrite?.(tableName, "upsert");
6936
7467
  return {
6937
7468
  name: fieldName,
6938
7469
  resolver: async (_source, args, context, info) => {
6939
7470
  try {
6940
- return await runMutation(db, context, info, txCtx, async (executor) => {
6941
- const supplied = single ? [args.values] : args.values;
6942
- if (!supplied.length) {
6943
- throw new import_graphql10.GraphQLError("No values were provided!");
6944
- }
6945
- const entries = nested?.enabled(tableName) ? supplied.map((values) => nested.split(tableName, values)) : void 0;
6946
- const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
6947
- const contextColumns = policies?.contextValues?.(tableName);
6948
- const scope = policies?.scope?.(context);
6949
- const input = nestedEntries ? [] : applyContextValuesAll(
6950
- remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : supplied, table),
6951
- contextColumns,
6952
- context
6953
- );
6954
- const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, { deep: true });
6955
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
6956
- relationMap,
6957
- tables,
6958
- tableName,
6959
- typeName,
6960
- typeNameMapper,
6961
- table,
6962
- pkNames,
6963
- parsedInfo,
6964
- limits,
6965
- scope
6966
- });
6967
- const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
6968
- const runUpsert = async (target, values) => {
6969
- const plan = resolveConflictPlan({
7471
+ return await runMutation(
7472
+ db,
7473
+ context,
7474
+ info,
7475
+ txCtx,
7476
+ async (executor) => {
7477
+ const supplied = single ? [args.values] : args.values;
7478
+ if (!supplied.length) {
7479
+ throw new import_graphql10.GraphQLError("No values were provided!");
7480
+ }
7481
+ await runWriteHook(hooks, "before", {
7482
+ table: tableName,
7483
+ operation: "upsert",
7484
+ single,
7485
+ args,
7486
+ context,
7487
+ info,
7488
+ tx: executor
7489
+ });
7490
+ const entries = nested?.enabled(tableName) ? supplied.map((values) => nested.split(tableName, values)) : void 0;
7491
+ const nestedEntries = entries?.some((entry) => nested.hasOps(entry.ops)) ? entries : void 0;
7492
+ const contextColumns = policies?.contextValues?.(tableName);
7493
+ const scope = policies?.scope?.(context);
7494
+ const input = nestedEntries ? [] : applyContextValuesAll(
7495
+ remapFromGraphQLArrayInput(entries ? entries.map((entry) => entry.columns) : supplied, table),
7496
+ contextColumns,
7497
+ context
7498
+ );
7499
+ const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, { deep: true });
7500
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
7501
+ relationMap,
7502
+ tables,
7503
+ tableName,
7504
+ typeName,
7505
+ typeNameMapper,
6970
7506
  table,
6971
- values,
6972
- onConflict: args.onConflict,
6973
7507
  pkNames,
6974
- uniqueSets,
6975
- excludedRef: excludedColumnRef,
6976
- withTarget: true,
6977
- buildWhere: (where) => extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName))
7508
+ parsedInfo,
7509
+ limits,
7510
+ scope
6978
7511
  });
6979
- let query = target.insert(table).values(values).returning(returning);
6980
- const setWhere = withScope(scope, tableName, table, plan.setWhere);
6981
- query = plan.action === "NOTHING" ? query.onConflictDoNothing(plan.target ? { target: plan.target } : void 0) : query.onConflictDoUpdate({ target: plan.target, set: plan.set, setWhere });
6982
- return await query;
6983
- };
6984
- const result = nestedEntries ? await writeWithNestedOps({
6985
- executor,
6986
- runtime: nested,
6987
- tableName,
6988
- entries: nestedEntries,
6989
- remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
6990
- write: (tx, values) => runUpsert(tx, [values]),
6991
- context
6992
- }) : await runUpsert(executor, input);
6993
- if (single && !result[0]) {
6994
- return void 0;
6995
- }
6996
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
6997
- return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
6998
- });
7512
+ const returning = nestedEntries ? nested.withJoinColumns(tableName, mergedOps(nestedEntries), { ...columns }, table) : columns;
7513
+ const runUpsert = async (target, values) => {
7514
+ const plan = resolveConflictPlan({
7515
+ table,
7516
+ values,
7517
+ onConflict: args.onConflict,
7518
+ pkNames,
7519
+ uniqueSets,
7520
+ excludedRef: excludedColumnRef,
7521
+ withTarget: true,
7522
+ buildWhere: (where) => extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName))
7523
+ });
7524
+ let query = target.insert(table).values(values).returning(returning);
7525
+ const setWhere = withScope(scope, tableName, table, plan.setWhere);
7526
+ query = plan.action === "NOTHING" ? query.onConflictDoNothing(plan.target ? { target: plan.target } : void 0) : query.onConflictDoUpdate({ target: plan.target, set: plan.set, setWhere });
7527
+ return await query;
7528
+ };
7529
+ const result = nestedEntries ? await writeWithNestedOps({
7530
+ executor,
7531
+ runtime: nested,
7532
+ tableName,
7533
+ entries: nestedEntries,
7534
+ remapValues: (values) => applyContextValues(remapFromGraphQLSingleInput(values, table), contextColumns, context),
7535
+ write: (tx, values) => runUpsert(tx, [values]),
7536
+ context
7537
+ }) : await runUpsert(executor, input);
7538
+ await runWriteHook(hooks, "after", {
7539
+ table: tableName,
7540
+ operation: "upsert",
7541
+ single,
7542
+ args,
7543
+ rows: result,
7544
+ context,
7545
+ info,
7546
+ tx: executor
7547
+ });
7548
+ if (single && !result[0]) {
7549
+ return void 0;
7550
+ }
7551
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
7552
+ return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
7553
+ },
7554
+ !!hooks
7555
+ );
6999
7556
  } catch (e) {
7000
7557
  throw toGraphQLError(e);
7001
7558
  }
@@ -7013,75 +7570,102 @@ var generateUpdate3 = (db, tableName, table, tables, relationMap, setArgs, filte
7013
7570
  }
7014
7571
  };
7015
7572
  const pkNames = sqlitePrimaryKeyPropNames(table);
7573
+ const hooks = policies?.onWrite?.(tableName, "update");
7016
7574
  return {
7017
7575
  name: fieldName,
7018
7576
  resolver: async (_source, args, context, info) => {
7019
7577
  try {
7020
- return await runMutation(db, context, info, txCtx, async (executor) => {
7021
- const { where, set } = args;
7022
- const scope = policies?.scope?.(context);
7023
- const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, {
7024
- deep: true
7025
- });
7026
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
7027
- relationMap,
7028
- tables,
7029
- tableName,
7030
- typeName,
7031
- typeNameMapper,
7032
- table,
7033
- pkNames,
7034
- parsedInfo,
7035
- limits,
7036
- scope
7037
- });
7038
- const entry = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
7039
- const nestedOps = entry && nested.hasOps(entry.ops) ? entry.ops : void 0;
7040
- const input = stripContextValues(
7041
- remapUpdateInput(entry ? entry.columns : set, table, tableName),
7042
- policies?.contextValues?.(tableName)
7043
- );
7044
- if (!Object.keys(input).length && !nestedOps) {
7045
- throw new import_graphql10.GraphQLError("Unable to update with no values specified!");
7046
- }
7047
- const relationCtx = relationFilterCtx(filterCtx, tableName);
7048
- const filters = withScope(
7049
- scope,
7050
- tableName,
7051
- table,
7052
- single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
7053
- );
7054
- if (single) {
7055
- await assertSingleMatch(executor, table, filters, fieldName);
7056
- }
7057
- const returning = nestedOps ? nested.withJoinColumns(tableName, nestedOps, { ...columns }, table) : columns;
7058
- const runUpdate = async (target, values) => {
7059
- const writes = Object.keys(values).length > 0;
7060
- let query = writes ? target.update(table).set(values) : target.select(returning).from(table);
7061
- if (filters) {
7062
- query = query.where(filters);
7578
+ return await runMutation(
7579
+ db,
7580
+ context,
7581
+ info,
7582
+ txCtx,
7583
+ async (executor) => {
7584
+ await runWriteHook(hooks, "before", {
7585
+ table: tableName,
7586
+ operation: "update",
7587
+ single,
7588
+ args,
7589
+ context,
7590
+ info,
7591
+ tx: executor
7592
+ });
7593
+ const { where, set } = args;
7594
+ const scope = policies?.scope?.(context);
7595
+ const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, {
7596
+ deep: true
7597
+ });
7598
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
7599
+ relationMap,
7600
+ tables,
7601
+ tableName,
7602
+ typeName,
7603
+ typeNameMapper,
7604
+ table,
7605
+ pkNames,
7606
+ parsedInfo,
7607
+ limits,
7608
+ scope
7609
+ });
7610
+ const entry = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
7611
+ const nestedOps = entry && nested.hasOps(entry.ops) ? entry.ops : void 0;
7612
+ const input = stripContextValues(
7613
+ remapUpdateInput(entry ? entry.columns : set, table, tableName),
7614
+ policies?.contextValues?.(tableName)
7615
+ );
7616
+ if (!Object.keys(input).length && !nestedOps) {
7617
+ throw new import_graphql10.GraphQLError("Unable to update with no values specified!");
7063
7618
  }
7064
- return await (writes ? query.returning(returning) : query);
7065
- };
7066
- const result = nestedOps ? await updateWithNestedOps({
7067
- executor,
7068
- runtime: nested,
7069
- tableName,
7070
- columns: input,
7071
- ops: nestedOps,
7072
- remapValues: (values) => values,
7073
- write: runUpdate,
7074
- context
7075
- }) : await runUpdate(executor, input);
7076
- if (single && result.length > 1) {
7077
- throw new import_graphql10.GraphQLError(`${fieldName}: 'where' matched more than one row!`);
7078
- }
7079
- if (single && !result[0]) {
7080
- return void 0;
7081
- }
7082
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
7083
- return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
7084
- });
7619
+ const relationCtx = relationFilterCtx(filterCtx, tableName);
7620
+ const filters = withScope(
7621
+ scope,
7622
+ tableName,
7623
+ table,
7624
+ single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0
7625
+ );
7626
+ if (single) {
7627
+ await assertSingleMatch(executor, table, filters, fieldName);
7628
+ }
7629
+ const returning = nestedOps ? nested.withJoinColumns(tableName, nestedOps, { ...columns }, table) : columns;
7630
+ const runUpdate = async (target, values) => {
7631
+ const writes = Object.keys(values).length > 0;
7632
+ let query = writes ? target.update(table).set(values) : target.select(returning).from(table);
7633
+ if (filters) {
7634
+ query = query.where(filters);
7635
+ }
7636
+ return await (writes ? query.returning(returning) : query);
7637
+ };
7638
+ const result = nestedOps ? await updateWithNestedOps({
7639
+ executor,
7640
+ runtime: nested,
7641
+ tableName,
7642
+ columns: input,
7643
+ ops: nestedOps,
7644
+ remapValues: (values) => values,
7645
+ write: runUpdate,
7646
+ context
7647
+ }) : await runUpdate(executor, input);
7648
+ await runWriteHook(hooks, "after", {
7649
+ table: tableName,
7650
+ operation: "update",
7651
+ single,
7652
+ args,
7653
+ rows: result,
7654
+ context,
7655
+ info,
7656
+ tx: executor
7657
+ });
7658
+ if (single && result.length > 1) {
7659
+ throw new import_graphql10.GraphQLError(`${fieldName}: 'where' matched more than one row!`);
7660
+ }
7661
+ if (single && !result[0]) {
7662
+ return void 0;
7663
+ }
7664
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, result, pkNames, withParams) : result;
7665
+ return single ? remapToGraphQLSingleOutput(enriched[0], tableName, table, relationMap) : remapToGraphQLArrayOutput(enriched, tableName, table, relationMap);
7666
+ },
7667
+ !!hooks
7668
+ );
7085
7669
  } catch (e) {
7086
7670
  throw toGraphQLError(e);
7087
7671
  }
@@ -7096,122 +7680,149 @@ var generateUpdateMany3 = (db, tableName, table, tables, relationMap, updateMany
7096
7680
  }
7097
7681
  };
7098
7682
  const pkNames = sqlitePrimaryKeyPropNames(table);
7683
+ const hooks = policies?.onWrite?.(tableName, "updateMany");
7099
7684
  return {
7100
7685
  name: fieldName,
7101
7686
  resolver: async (_source, args, context, info) => {
7102
7687
  try {
7103
- return await runMutation(db, context, info, txCtx, async (executor) => {
7104
- const { updates } = args;
7105
- if (!updates.length) {
7106
- throw new import_graphql10.GraphQLError("No updates were provided!");
7107
- }
7108
- const scope = policies?.scope?.(context);
7109
- const contextColumns = policies?.contextValues?.(tableName);
7110
- const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, {
7111
- deep: true
7112
- });
7113
- const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
7114
- relationMap,
7115
- tables,
7116
- tableName,
7117
- typeName,
7118
- typeNameMapper,
7119
- table,
7120
- pkNames,
7121
- parsedInfo,
7122
- limits,
7123
- scope
7124
- });
7125
- const entries = updates.map(({ where, set }) => {
7126
- const split = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
7127
- const ops = split && nested.hasOps(split.ops) ? split.ops : void 0;
7128
- const input = stripContextValues(
7129
- remapUpdateInput(split ? split.columns : set, table, tableName),
7130
- contextColumns
7131
- );
7132
- if (!Object.keys(input).length && !ops) {
7133
- throw new import_graphql10.GraphQLError("Unable to update with no values specified!");
7688
+ return await runMutation(
7689
+ db,
7690
+ context,
7691
+ info,
7692
+ txCtx,
7693
+ async (executor) => {
7694
+ const { updates } = args;
7695
+ if (!updates.length) {
7696
+ throw new import_graphql10.GraphQLError("No updates were provided!");
7134
7697
  }
7135
- return {
7136
- set: input,
7137
- ops,
7138
- filters: withScope(
7139
- scope,
7140
- tableName,
7141
- table,
7142
- where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
7143
- )
7698
+ await runWriteHook(hooks, "before", {
7699
+ table: tableName,
7700
+ operation: "updateMany",
7701
+ single: false,
7702
+ args,
7703
+ context,
7704
+ info,
7705
+ tx: executor
7706
+ });
7707
+ const scope = policies?.scope?.(context);
7708
+ const contextColumns = policies?.contextValues?.(tableName);
7709
+ const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, {
7710
+ deep: true
7711
+ });
7712
+ const { columns, hasRelations, withParams } = prepareMutationRelationColumns({
7713
+ relationMap,
7714
+ tables,
7715
+ tableName,
7716
+ typeName,
7717
+ typeNameMapper,
7718
+ table,
7719
+ pkNames,
7720
+ parsedInfo,
7721
+ limits,
7722
+ scope
7723
+ });
7724
+ const entries = updates.map(({ where, set }) => {
7725
+ const split = nested?.enabled(tableName) ? nested.split(tableName, set) : void 0;
7726
+ const ops = split && nested.hasOps(split.ops) ? split.ops : void 0;
7727
+ const input = stripContextValues(
7728
+ remapUpdateInput(split ? split.columns : set, table, tableName),
7729
+ contextColumns
7730
+ );
7731
+ if (!Object.keys(input).length && !ops) {
7732
+ throw new import_graphql10.GraphQLError("Unable to update with no values specified!");
7733
+ }
7734
+ return {
7735
+ set: input,
7736
+ ops,
7737
+ filters: withScope(
7738
+ scope,
7739
+ tableName,
7740
+ table,
7741
+ where ? extractFilters(table, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0
7742
+ )
7743
+ };
7744
+ });
7745
+ const anyNested = entries.some((entry) => entry.ops);
7746
+ const returning = anyNested ? nested.withJoinColumns(
7747
+ tableName,
7748
+ mergedOps(entries.map((entry) => ({ ops: entry.ops ?? {} }))),
7749
+ { ...columns },
7750
+ table
7751
+ ) : columns;
7752
+ const runEntry = (tx, entry) => {
7753
+ let query = tx.update(table).set(entry.set);
7754
+ if (entry.filters) {
7755
+ query = query.where(entry.filters);
7756
+ }
7757
+ return query.returning(returning).all();
7144
7758
  };
7145
- });
7146
- const anyNested = entries.some((entry) => entry.ops);
7147
- const returning = anyNested ? nested.withJoinColumns(
7148
- tableName,
7149
- mergedOps(entries.map((entry) => ({ ops: entry.ops ?? {} }))),
7150
- { ...columns },
7151
- table
7152
- ) : columns;
7153
- const runEntry = (tx, entry) => {
7154
- let query = tx.update(table).set(entry.set);
7155
- if (entry.filters) {
7156
- query = query.where(entry.filters);
7157
- }
7158
- return query.returning(returning).all();
7159
- };
7160
- const runNestedEntry = async (tx, entry) => {
7161
- const values = entry.ops ? { ...entry.set, ...await nested.applyParentSide(tx, tableName, entry.ops, context) } : entry.set;
7162
- const writes = Object.keys(values).length > 0;
7163
- let query = writes ? tx.update(table).set(values) : tx.select(returning).from(table);
7164
- if (entry.filters) {
7165
- query = query.where(entry.filters);
7166
- }
7167
- const rows = await (writes ? query.returning(returning) : query);
7168
- if (entry.ops) {
7169
- await nested.applyChildSide(tx, tableName, entry.ops, rows, context);
7170
- }
7171
- return rows;
7172
- };
7173
- const perEntry = await executor.transaction((tx) => {
7174
- if (anyNested) {
7175
- return (async () => {
7176
- const results2 = [];
7177
- for (const entry of entries) {
7178
- results2.push(await runNestedEntry(tx, entry));
7179
- }
7180
- return results2;
7181
- })();
7182
- }
7183
- const first = runEntry(tx, entries[0]);
7184
- if (typeof first?.then === "function") {
7185
- return (async () => {
7186
- const results2 = [await first];
7187
- for (let i = 1; i < entries.length; i++) {
7188
- results2.push(await runEntry(tx, entries[i]));
7189
- }
7190
- return results2;
7191
- })();
7192
- }
7193
- const results = [first];
7194
- for (let i = 1; i < entries.length; i++) {
7195
- results.push(runEntry(tx, entries[i]));
7196
- }
7197
- return results;
7198
- });
7199
- const flatRows = perEntry.flat();
7200
- const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, flatRows, pkNames, withParams) : flatRows;
7201
- const output = [];
7202
- let offset = 0;
7203
- for (const rows of perEntry) {
7204
- if (!rows.length) {
7205
- output.push(null);
7206
- continue;
7207
- }
7208
- for (let i = 0; i < rows.length; i++) {
7209
- output.push(remapToGraphQLSingleOutput(enriched[offset + i], tableName, table, relationMap));
7759
+ const runNestedEntry = async (tx, entry) => {
7760
+ const values = entry.ops ? { ...entry.set, ...await nested.applyParentSide(tx, tableName, entry.ops, context) } : entry.set;
7761
+ const writes = Object.keys(values).length > 0;
7762
+ let query = writes ? tx.update(table).set(values) : tx.select(returning).from(table);
7763
+ if (entry.filters) {
7764
+ query = query.where(entry.filters);
7765
+ }
7766
+ const rows = await (writes ? query.returning(returning) : query);
7767
+ if (entry.ops) {
7768
+ await nested.applyChildSide(tx, tableName, entry.ops, rows, context);
7769
+ }
7770
+ return rows;
7771
+ };
7772
+ const perEntry = await executor.transaction((tx) => {
7773
+ if (anyNested) {
7774
+ return (async () => {
7775
+ const results2 = [];
7776
+ for (const entry of entries) {
7777
+ results2.push(await runNestedEntry(tx, entry));
7778
+ }
7779
+ return results2;
7780
+ })();
7781
+ }
7782
+ const first = runEntry(tx, entries[0]);
7783
+ if (typeof first?.then === "function") {
7784
+ return (async () => {
7785
+ const results2 = [await first];
7786
+ for (let i = 1; i < entries.length; i++) {
7787
+ results2.push(await runEntry(tx, entries[i]));
7788
+ }
7789
+ return results2;
7790
+ })();
7791
+ }
7792
+ const results = [first];
7793
+ for (let i = 1; i < entries.length; i++) {
7794
+ results.push(runEntry(tx, entries[i]));
7795
+ }
7796
+ return results;
7797
+ });
7798
+ const flatRows = perEntry.flat();
7799
+ await runWriteHook(hooks, "after", {
7800
+ table: tableName,
7801
+ operation: "updateMany",
7802
+ single: false,
7803
+ args,
7804
+ rows: flatRows,
7805
+ context,
7806
+ info,
7807
+ tx: executor
7808
+ });
7809
+ const enriched = hasRelations ? await eagerLoadMutationRelations(executor, tableName, flatRows, pkNames, withParams) : flatRows;
7810
+ const output = [];
7811
+ let offset = 0;
7812
+ for (const rows of perEntry) {
7813
+ if (!rows.length) {
7814
+ output.push(null);
7815
+ continue;
7816
+ }
7817
+ for (let i = 0; i < rows.length; i++) {
7818
+ output.push(remapToGraphQLSingleOutput(enriched[offset + i], tableName, table, relationMap));
7819
+ }
7820
+ offset += rows.length;
7210
7821
  }
7211
- offset += rows.length;
7212
- }
7213
- return output;
7214
- });
7822
+ return output;
7823
+ },
7824
+ !!hooks
7825
+ );
7215
7826
  } catch (e) {
7216
7827
  throw toGraphQLError(e);
7217
7828
  }
@@ -7221,51 +7832,79 @@ var generateUpdateMany3 = (db, tableName, table, tables, relationMap, updateMany
7221
7832
  };
7222
7833
  var generateDelete3 = (db, tableName, table, filterArgs, fieldName, typeName, single, requireWhere, filterCtx, selectionCtx, txCtx, policies, restore = false) => {
7223
7834
  const softDelete = policies?.softDelete?.(tableName);
7835
+ const operation = restore ? "restore" : "delete";
7224
7836
  const queryArgs = {
7225
7837
  where: {
7226
7838
  type: single || requireWhere ? new import_graphql10.GraphQLNonNull(filterArgs) : filterArgs
7227
7839
  }
7228
7840
  };
7841
+ const hooks = policies?.onWrite?.(tableName, operation);
7229
7842
  return {
7230
7843
  name: fieldName,
7231
7844
  resolver: async (_source, args, context, info) => {
7232
7845
  try {
7233
- return await runMutation(db, context, info, txCtx, async (executor) => {
7234
- const { where } = args;
7235
- const scope = policies?.scope?.(context);
7236
- const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, {
7237
- deep: true
7238
- });
7239
- const columns = extractSelectedColumnsFromTreeSQLFormat(
7240
- parsedInfo.fieldsByTypeName[typeName],
7241
- table,
7242
- selectionCtx
7243
- );
7244
- const relationCtx = relationFilterCtx(filterCtx, tableName);
7245
- const filters = withScope(
7246
- scope,
7247
- tableName,
7248
- table,
7249
- single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0,
7250
- restore ? "ONLY" : void 0
7251
- );
7252
- if (single) {
7253
- await assertSingleMatch(executor, table, filters, fieldName);
7254
- }
7255
- let query = softDelete ? executor.update(table).set({ [softDelete.columnName]: restore ? softDelete.writeRestored : softDelete.writeDeleted() }) : executor.delete(table);
7256
- if (filters) {
7257
- query = query.where(filters);
7258
- }
7259
- query = query.returning(columns);
7260
- const result = await query;
7261
- if (single && result.length > 1) {
7262
- throw new import_graphql10.GraphQLError(`${fieldName}: 'where' matched more than one row!`);
7263
- }
7264
- if (single) {
7265
- return result[0] ? remapToGraphQLSingleOutput(result[0], tableName, table) : void 0;
7266
- }
7267
- return remapToGraphQLArrayOutput(result, tableName, table);
7268
- });
7846
+ return await runMutation(
7847
+ db,
7848
+ context,
7849
+ info,
7850
+ txCtx,
7851
+ async (executor) => {
7852
+ await runWriteHook(hooks, "before", {
7853
+ table: tableName,
7854
+ operation,
7855
+ single,
7856
+ args,
7857
+ context,
7858
+ info,
7859
+ tx: executor
7860
+ });
7861
+ const { where } = args;
7862
+ const scope = policies?.scope?.(context);
7863
+ const parsedInfo = (0, import_graphql_parse_resolve_info4.parseResolveInfo)(info, {
7864
+ deep: true
7865
+ });
7866
+ const columns = extractSelectedColumnsFromTreeSQLFormat(
7867
+ parsedInfo.fieldsByTypeName[typeName],
7868
+ table,
7869
+ selectionCtx
7870
+ );
7871
+ const relationCtx = relationFilterCtx(filterCtx, tableName);
7872
+ const filters = withScope(
7873
+ scope,
7874
+ tableName,
7875
+ table,
7876
+ single || requireWhere ? extractRequiredFilters(table, tableName, where, fieldName, relationCtx) : where ? extractFilters(table, tableName, where, relationCtx) : void 0,
7877
+ restore ? "ONLY" : void 0
7878
+ );
7879
+ if (single) {
7880
+ await assertSingleMatch(executor, table, filters, fieldName);
7881
+ }
7882
+ let query = softDelete ? executor.update(table).set({ [softDelete.columnName]: restore ? softDelete.writeRestored : softDelete.writeDeleted() }) : executor.delete(table);
7883
+ if (filters) {
7884
+ query = query.where(filters);
7885
+ }
7886
+ query = query.returning(columns);
7887
+ const result = await query;
7888
+ await runWriteHook(hooks, "after", {
7889
+ table: tableName,
7890
+ operation,
7891
+ single,
7892
+ args,
7893
+ rows: result,
7894
+ context,
7895
+ info,
7896
+ tx: executor
7897
+ });
7898
+ if (single && result.length > 1) {
7899
+ throw new import_graphql10.GraphQLError(`${fieldName}: 'where' matched more than one row!`);
7900
+ }
7901
+ if (single) {
7902
+ return result[0] ? remapToGraphQLSingleOutput(result[0], tableName, table) : void 0;
7903
+ }
7904
+ return remapToGraphQLArrayOutput(result, tableName, table);
7905
+ },
7906
+ !!hooks
7907
+ );
7269
7908
  } catch (e) {
7270
7909
  throw toGraphQLError(e);
7271
7910
  }
@@ -8150,10 +8789,36 @@ var buildSchema = (db, config) => {
8150
8789
  }
8151
8790
  }
8152
8791
  }
8153
- const policies = scopeConfig || contextValuesConfig || softDeleteInfos.size ? {
8792
+ const onWriteConfig = config?.onWrite;
8793
+ const writeHooks = /* @__PURE__ */ new Map();
8794
+ let globalWriteHooks;
8795
+ if (onWriteConfig) {
8796
+ const isGlobal = typeof onWriteConfig === "function" || typeof onWriteConfig.before === "function" || typeof onWriteConfig.after === "function";
8797
+ if (isGlobal) {
8798
+ globalWriteHooks = normalizeWriteHooks(onWriteConfig);
8799
+ } else {
8800
+ const tableNames = new Set(tableKeys);
8801
+ for (const [tableName, hooks] of Object.entries(onWriteConfig)) {
8802
+ if (!tableNames.has(tableName)) {
8803
+ throw new Error(
8804
+ `Drizzle-GraphQL Error: config.onWrite names '${tableName}', which is not a table in the Drizzle schema.`
8805
+ );
8806
+ }
8807
+ if (typeof hooks !== "function" && typeof hooks !== "object") {
8808
+ throw new Error(
8809
+ `Drizzle-GraphQL Error: config.onWrite.${tableName} must be a function or an object with 'before' and/or 'after'.`
8810
+ );
8811
+ }
8812
+ writeHooks.set(tableName, normalizeWriteHooks(hooks));
8813
+ }
8814
+ }
8815
+ }
8816
+ const onWrite = globalWriteHooks || writeHooks.size ? (tableName) => globalWriteHooks ?? writeHooks.get(tableName) : void 0;
8817
+ const policies = scopeConfig || contextValuesConfig || softDeleteInfos.size || onWrite ? {
8154
8818
  scope: scopeConfig ? (tableName) => scopeConfig[tableName] : void 0,
8155
8819
  contextValues: contextValuesConfig ? (tableName) => contextValuesConfig[tableName] : void 0,
8156
- softDelete: softDeleteInfos.size ? (tableName) => softDeleteInfos.get(tableName) : void 0
8820
+ softDelete: softDeleteInfos.size ? (tableName) => softDeleteInfos.get(tableName) : void 0,
8821
+ onWrite
8157
8822
  } : void 0;
8158
8823
  const exclude = config?.exclude;
8159
8824
  if (exclude) {