appflare 0.2.16 → 0.2.17

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.
@@ -52,12 +52,15 @@ export function buildColumnHeaders(
52
52
  .join("");
53
53
  }
54
54
 
55
- export function buildRowCells(columns: string[]): string {
55
+ export function buildRowCells(columns: string[], primaryKey?: string): string {
56
56
  return columns
57
- .map(
58
- (column) =>
59
- `<td><div class="truncate max-w-[200px] text-sm" title="\${String((row as any).${column} ?? '')}">\${String((row as any).${column} ?? '')}</div></td>`,
60
- )
57
+ .map((column) => {
58
+ if (primaryKey && column === primaryKey) {
59
+ return `<td><button type="button" class="truncate max-w-[200px] text-sm font-mono text-xs opacity-70 hover:opacity-100 cursor-copy text-left" title="Click to copy: \${String((row as any).${column} ?? '')}" data-copy-value="\${String((row as any).${column} ?? '')}" onclick="navigator.clipboard?.writeText(this.dataset.copyValue || '')">\${String((row as any).${column} ?? '')}</button></td>`;
60
+ }
61
+
62
+ return `<td><div class="truncate max-w-[200px] text-sm" title="\${String((row as any).${column} ?? '')}">\${String((row as any).${column} ?? '')}</div></td>`;
63
+ })
61
64
  .join("");
62
65
  }
63
66
 
@@ -31,7 +31,7 @@ export function buildTableRoute(table: DiscoveredTable): string {
31
31
  );
32
32
  const searchConditions = buildSearchConditions(table);
33
33
  const headers = buildColumnHeaders(table, columns);
34
- const rowCells = buildRowCells(columns);
34
+ const rowCells = buildRowCells(columns, primaryKey);
35
35
  const createInputs = createColumns
36
36
  .map((columnName) => buildFieldInput(table, columnName, "create"))
37
37
  .join("");
@@ -55,7 +55,7 @@ export function buildUsersTableHtml(): string {
55
55
  \t\t\t\t\t\t<tbody>
56
56
  \t\t\t\t\t\t\t\${data.map((row) => html\`
57
57
  \t\t\t\t\t\t\t\t<tr class="hover:bg-base-200/30 transition-colors">
58
- \t\t\t\t\t\t\t\t\t<td><div class="truncate max-w-[220px] text-sm font-mono text-xs opacity-60" title="\${String((row as any).id ?? '')}">\${String((row as any).id ?? '')}</div></td>
58
+ \t\t\t\t\t\t\t\t\t<td><button type="button" class="truncate max-w-[220px] text-sm font-mono text-xs opacity-70 hover:opacity-100 cursor-copy text-left" title="Click to copy: \${String((row as any).id ?? '')}" data-copy-value="\${String((row as any).id ?? '')}" onclick="navigator.clipboard?.writeText(this.dataset.copyValue || '')">\${String((row as any).id ?? '')}</button></td>
59
59
  \t\t\t\t\t\t\t\t\t<td><div class="truncate max-w-[220px] text-sm" title="\${String((row as any).name ?? '')}">\${String((row as any).name ?? '')}</div></td>
60
60
  \t\t\t\t\t\t\t\t\t<td><div class="truncate max-w-[260px] text-sm" title="\${String((row as any).email ?? '')}">\${String((row as any).email ?? '')}</div></td>
61
61
  \t\t\t\t\t\t\t\t\t<td><span class="badge badge-sm \${String((row as any).role ?? '') === 'admin' ? 'badge-primary' : 'badge-ghost'}">\${String((row as any).role ?? '')}</span></td>
@@ -123,5 +123,6 @@ ${buildDeleteUserModal()}
123
123
  \t\t\t\t</div>
124
124
  \t\t\t\t${paginationHtml}
125
125
  \t\t\t</div>
126
- \t\t\`;`;
126
+ \t\t\`;
127
+ `;
127
128
  }
@@ -143,11 +143,11 @@ type QueryWithRelationInputForTable<
143
143
  TSourceTable extends TableName,
144
144
  TRelationName extends string,
145
145
  TEntry,
146
- > = ManyToManyTargetTableName<TSourceTable, TRelationName> extends infer TTarget
147
- ? TTarget extends TableName
148
- ? QueryManyToManyWithRelationInput<TTarget>
149
- : QueryNativeWithRelationInput<TEntry>
150
- : QueryNativeWithRelationInput<TEntry>;
146
+ > = [ManyToManyTargetTableName<TSourceTable, TRelationName>] extends [never]
147
+ ? QueryNativeWithRelationInput<TEntry>
148
+ : QueryManyToManyWithRelationInput<
149
+ Extract<ManyToManyTargetTableName<TSourceTable, TRelationName>, TableName>
150
+ >;
151
151
 
152
152
  type QueryWithInputForTable<
153
153
  TSourceTable extends TableName,
@@ -183,13 +183,6 @@ export type QueryFindFirstArgs<TName extends TableName> = Omit<
183
183
  with?: QueryWithInput<TName, NativeFindFirstWith<TName>>;
184
184
  };
185
185
 
186
- type SingularizeRelationName<TName extends string> =
187
- TName extends \`\${infer TRoot\}ies\`
188
- ? \`\${TRoot\}y\`
189
- : TName extends \`\${infer TRoot\}s\`
190
- ? TRoot
191
- : TName;
192
-
193
186
  type RelationInsertItem<TTargetTable extends TableName> =
194
187
  | TableModel<TTargetTable>
195
188
  | TableInsertModel<TTargetTable>
@@ -215,21 +208,38 @@ type RelationInsertFields<TName extends TableName> = {
215
208
  >;
216
209
  };
217
210
 
218
- type RelationInsertIdAliasFields<TName extends TableName> = {
219
- [TRelationName in RuntimeRelationName<TName> as \`\${TRelationName\}Id\`]?: RelationInsertValue<
220
- TName,
221
- TRelationName
222
- >;
223
- } & {
224
- [TRelationName in RuntimeRelationName<TName> as \`\${SingularizeRelationName<TRelationName>\}Id\`]?: RelationInsertValue<
225
- TName,
226
- TRelationName
227
- >;
228
- };
229
-
230
211
  type QueryInsertValue<TName extends TableName> = TableInsertScalarModel<TName> &
231
- RelationInsertFields<TName> &
232
- RelationInsertIdAliasFields<TName>;
212
+ RelationInsertFields<TName>;
213
+
214
+ type InsertItemValue<TArgs> = TArgs extends { values: infer TValue }
215
+ ? TValue extends Array<infer TItem>
216
+ ? TItem
217
+ : TValue
218
+ : never;
219
+
220
+ type InsertRelationNameFromArgs<
221
+ TName extends TableName,
222
+ TArgs,
223
+ > = Extract<keyof InsertItemValue<TArgs>, RuntimeRelationName<TName>>;
224
+
225
+ type InsertRelationResultValue<
226
+ TSourceTable extends TableName,
227
+ TRelationName extends RuntimeRelationName<TSourceTable>,
228
+ > = RuntimeRelationTargetTable<TSourceTable, TRelationName> extends infer TTargetTable
229
+ ? TTargetTable extends TableName
230
+ ? RuntimeRelationKind<TSourceTable, TRelationName> extends "one"
231
+ ? TableModel<TTargetTable> | null
232
+ : Array<TableModel<TTargetTable>>
233
+ : never
234
+ : never;
235
+
236
+ export type QueryInsertResultRow<
237
+ TName extends TableName,
238
+ TArgs extends QueryInsertArgs<TName>,
239
+ > = TableModel<TName> & {
240
+ [TRelationName in InsertRelationNameFromArgs<TName, TArgs>]:
241
+ InsertRelationResultValue<TName, TRelationName>;
242
+ };
233
243
 
234
244
  export type QueryInsertArgs<TName extends TableName> = {
235
245
  values: QueryInsertValue<TName> | Array<QueryInsertValue<TName>>;
@@ -105,7 +105,9 @@ export type QueryTableApi<TName extends TableName> = {
105
105
  >(
106
106
  args?: TArgs,
107
107
  ) => ApplyFindFirstWithAggregateResult<TableFindFirstResult<TName, TArgs>, TArgs>;
108
- insert: (args: QueryInsertArgs<TName>) => Promise<Array<TableModel<TName>>>;
108
+ insert: <TArgs extends QueryInsertArgs<TName>>(
109
+ args: TArgs,
110
+ ) => Promise<Array<QueryInsertResultRow<TName, TArgs>>>;
109
111
  update: (args: QueryUpdateArgs<TName>) => Promise<Array<TableModel<TName>>>;
110
112
  upsert: (args: QueryUpsertArgs<TName>) => Promise<Array<TableModel<TName>>>;
111
113
  delete: (args?: QueryDeleteArgs<TName>) => Promise<Array<TableModel<TName>>>;
@@ -48,45 +48,44 @@ type TableModel<TName extends TableName> = InferSelectModel<
48
48
  (typeof mergedSchema)[TName]
49
49
  >;
50
50
  type ManyToManySchemaMap = typeof schema extends {
51
- __appflareManyToMany: infer TMap;
51
+ __appflareManyToMany: infer TMap extends Record<string, unknown>;
52
52
  }
53
- ? TMap extends Record<string, Record<string, { targetTable: string }>>
54
- ? TMap
55
- : {}
53
+ ? TMap
56
54
  : {};
55
+ type ManyToManySourceTableMap<TSourceTable extends TableName> =
56
+ TSourceTable extends keyof ManyToManySchemaMap
57
+ ? ManyToManySchemaMap[TSourceTable] extends Record<string, unknown>
58
+ ? ManyToManySchemaMap[TSourceTable]
59
+ : {}
60
+ : {};
57
61
  type ManyToManyTargetTableName<
58
62
  TSourceTable extends TableName,
59
63
  TRelationName extends string,
60
- > = TSourceTable extends keyof ManyToManySchemaMap
61
- ? TRelationName extends keyof ManyToManySchemaMap[TSourceTable]
62
- ? ManyToManySchemaMap[TSourceTable][TRelationName] extends {
64
+ > = TRelationName extends keyof ManyToManySourceTableMap<TSourceTable>
65
+ ? ManyToManySourceTableMap<TSourceTable>[TRelationName] extends {
63
66
  targetTable: infer TTarget extends string;
64
67
  }
65
68
  ? Extract<TTarget, TableName>
66
69
  : never
67
- : never
68
70
  : never;
69
71
  type RuntimeRelationsSchemaMap = typeof schema extends {
70
- __appflareRelations: infer TMap;
72
+ __appflareRelations: infer TMap extends Record<string, unknown>;
71
73
  }
72
- ? TMap extends Record<
73
- string,
74
- Record<string, { kind: string; targetTable: string }>
75
- >
76
- ? TMap
77
- : {}
74
+ ? TMap
78
75
  : {};
79
- type RuntimeRelationName<TSourceTable extends TableName> =
76
+ type RuntimeRelationsSourceTableMap<TSourceTable extends TableName> =
80
77
  TSourceTable extends keyof RuntimeRelationsSchemaMap
81
- ? Extract<keyof RuntimeRelationsSchemaMap[TSourceTable], string>
82
- : never;
78
+ ? RuntimeRelationsSchemaMap[TSourceTable] extends Record<string, unknown>
79
+ ? RuntimeRelationsSchemaMap[TSourceTable]
80
+ : {}
81
+ : {};
82
+ type RuntimeRelationName<TSourceTable extends TableName> =
83
+ Extract<keyof RuntimeRelationsSourceTableMap<TSourceTable>, string>;
83
84
  type RuntimeRelationConfig<
84
85
  TSourceTable extends TableName,
85
86
  TRelationName extends string,
86
- > = TSourceTable extends keyof RuntimeRelationsSchemaMap
87
- ? TRelationName extends keyof RuntimeRelationsSchemaMap[TSourceTable]
88
- ? RuntimeRelationsSchemaMap[TSourceTable][TRelationName]
89
- : never
87
+ > = TRelationName extends keyof RuntimeRelationsSourceTableMap<TSourceTable>
88
+ ? RuntimeRelationsSourceTableMap<TSourceTable>[TRelationName]
90
89
  : never;
91
90
  type RuntimeRelationKind<
92
91
  TSourceTable extends TableName,
@@ -104,10 +103,7 @@ type RuntimeRelationTargetTable<
104
103
  }
105
104
  ? Extract<TTarget, TableName>
106
105
  : never;
107
- type TableInsertScalarModel<TName extends TableName> = Omit<
108
- TableInsertModel<TName>,
109
- RuntimeRelationName<TName>
110
- >;
106
+ type TableInsertScalarModel<TName extends TableName> = TableInsertModel<TName>;
111
107
  type FindManyWithFromArgs<TArgs> = TArgs extends { with?: infer TWith }
112
108
  ? TWith
113
109
  : undefined;
@@ -136,11 +132,12 @@ type ReplaceManyToManyRelationsInRow<
136
132
  [K in keyof TRow]: K extends string
137
133
  ? TWith extends Record<string, unknown>
138
134
  ? K extends keyof TWith
139
- ? ManyToManyTargetTableName<TSourceTable, K> extends infer TTarget
140
- ? TTarget extends TableName
141
- ? ManyToManyRelationRows<TTarget, TWith[K]>
142
- : TRow[K]
143
- : TRow[K]
135
+ ? [ManyToManyTargetTableName<TSourceTable, K>] extends [never]
136
+ ? TRow[K]
137
+ : ManyToManyRelationRows<
138
+ Extract<ManyToManyTargetTableName<TSourceTable, K>, TableName>,
139
+ TWith[K]
140
+ >
144
141
  : TRow[K]
145
142
  : TRow[K]
146
143
  : TRow[K];
@@ -202,10 +199,7 @@ type TableFindFirstArgs<TName extends TableName> = Omit<
202
199
  TableFindManyArgs<TName>,
203
200
  "limit"
204
201
  >;
205
- type NativeFindFirstWith<TName extends TableName> =
206
- NonNullable<TableFindFirstArgs<TName>> extends { with?: infer TWith }
207
- ? TWith
208
- : never;
202
+ type NativeFindFirstWith<TName extends TableName> = NativeFindManyWith<TName>;
209
203
  type ResolveNativeFindFirstWith<
210
204
  TName extends TableName,
211
205
  TArgs extends QueryFindFirstArgs<TName> | undefined,
@@ -1,5 +1,5 @@
1
1
  export function generateQueryRuntimeWriteSection(): string {
2
- return ` insert: async (args: QueryInsertArgs<TableName>) => {
2
+ return ` insert: async <TArgs extends QueryInsertArgs<TableName>>(args: TArgs) => {
3
3
  const transaction = ($db as any).transaction;
4
4
 
5
5
  const valuesArray = Array.isArray(args.values)
@@ -14,16 +14,6 @@ export function generateQueryRuntimeWriteSection(): string {
14
14
  }>
15
15
  > = [];
16
16
 
17
- const resolveSingularAlias = (relationName: string): string => {
18
- if (relationName.endsWith("ies")) {
19
- return relationName.slice(0, -3) + "yId";
20
- }
21
- if (relationName.endsWith("s") && relationName.length > 1) {
22
- return relationName.slice(0, -1) + "Id";
23
- }
24
- return relationName + "Id";
25
- };
26
-
27
17
  const toRelationItems = (
28
18
  relation: RuntimeRelation,
29
19
  relationName: string,
@@ -76,7 +66,7 @@ export function generateQueryRuntimeWriteSection(): string {
76
66
  const referenceField =
77
67
  relation.kind === "manyToMany"
78
68
  ? (relation.targetReferenceField ?? "id")
79
- : "id";
69
+ : (relation.referenceField ?? "id");
80
70
  const existingId = input[referenceField];
81
71
  if (existingId !== undefined && existingId !== null) {
82
72
  return existingId;
@@ -129,6 +119,74 @@ export function generateQueryRuntimeWriteSection(): string {
129
119
  return createdId;
130
120
  };
131
121
 
122
+ const getRelationTargetTable = (
123
+ relation: RuntimeRelation,
124
+ relationName: string,
125
+ ): any => {
126
+ const targetTable = (mergedSchema as Record<string, unknown>)[
127
+ relation.targetTable
128
+ ];
129
+ if (!targetTable) {
130
+ throw new Error(
131
+ "Unknown target table '" +
132
+ relation.targetTable +
133
+ "' for relation '" +
134
+ tableName +
135
+ "." +
136
+ relationName +
137
+ "'.",
138
+ );
139
+ }
140
+ return targetTable;
141
+ };
142
+
143
+ const getRelationReferenceField = (relation: RuntimeRelation): string => {
144
+ if (relation.kind === "manyToMany") {
145
+ return relation.targetReferenceField ?? "id";
146
+ }
147
+ return relation.referenceField ?? "id";
148
+ };
149
+
150
+ const fetchRelatedRowByIdentifier = async (
151
+ tx: any,
152
+ relation: RuntimeRelation,
153
+ relationName: string,
154
+ identifier: unknown,
155
+ ): Promise<Record<string, unknown> | null> => {
156
+ if (identifier === undefined || identifier === null) {
157
+ return null;
158
+ }
159
+
160
+ const targetTable = getRelationTargetTable(relation, relationName);
161
+ const referenceField = getRelationReferenceField(relation);
162
+ const referenceColumn = (targetTable as Record<string, unknown>)[
163
+ referenceField
164
+ ];
165
+ if (!referenceColumn) {
166
+ throw new Error(
167
+ "Target table '" +
168
+ relation.targetTable +
169
+ "' is missing column '" +
170
+ referenceField +
171
+ "' required by relation '" +
172
+ tableName +
173
+ "." +
174
+ relationName +
175
+ "'.",
176
+ );
177
+ }
178
+
179
+ let query: any = tx
180
+ .select()
181
+ .from(targetTable as any)
182
+ .where(eq(referenceColumn as any, identifier as any));
183
+ if (typeof query.limit === "function") {
184
+ query = query.limit(1);
185
+ }
186
+ const rows = (await query) as Array<Record<string, unknown>>;
187
+ return rows[0] ?? null;
188
+ };
189
+
132
190
  for (const inputValue of valuesArray) {
133
191
  const scalarValues: Record<string, unknown> = {};
134
192
  const relationInputs: Array<{
@@ -148,50 +206,16 @@ export function generateQueryRuntimeWriteSection(): string {
148
206
  });
149
207
  continue;
150
208
  }
151
-
152
- let matchedAlias = false;
153
- if (fieldName.endsWith("Id")) {
154
- for (const [relationName, relationValue] of Object.entries(
155
- runtimeRelationMap[tableName] ?? {},
156
- )) {
157
- const pluralAlias = relationName + "Id";
158
- const singularAlias = resolveSingularAlias(relationName);
159
- if (fieldName !== pluralAlias && fieldName !== singularAlias) {
160
- continue;
161
- }
162
-
163
- if (valueRecord[relationName] !== undefined) {
164
- throw new Error(
165
- "Insert payload for '" +
166
- tableName +
167
- "' cannot contain both '" +
168
- relationName +
169
- "' and '" +
170
- fieldName +
171
- "'.",
172
- );
173
- }
174
-
175
- relationInputs.push({
176
- relationName,
177
- relation: relationValue,
178
- value: fieldValue,
179
- });
180
- matchedAlias = true;
181
- break;
182
- }
183
- }
184
-
185
- if (!matchedAlias) {
186
- scalarValues[fieldName] = fieldValue;
187
- }
209
+ scalarValues[fieldName] = fieldValue;
188
210
  }
189
211
 
190
212
  baseValuesArray.push(scalarValues);
191
213
  postInsertRelations.push(relationInputs);
192
214
  }
193
215
 
194
- const executeInsertGraph = async (tx: any): Promise<Array<TableModel<TableName>>> => {
216
+ const executeInsertGraph = async (
217
+ tx: any,
218
+ ): Promise<Array<QueryInsertResultRow<TableName, TArgs>>> => {
195
219
  for (let index = 0; index < baseValuesArray.length; index += 1) {
196
220
  const relationInputs = postInsertRelations[index] ?? [];
197
221
  for (const relationInput of relationInputs) {
@@ -247,13 +271,46 @@ export function generateQueryRuntimeWriteSection(): string {
247
271
  insertQuery = insertQuery.returning();
248
272
  }
249
273
  const insertedRows = (await insertQuery) as Array<TableModel<TableName>>;
274
+ const hydratedRows: Array<Record<string, unknown>> = [];
250
275
 
251
276
  for (let index = 0; index < insertedRows.length; index += 1) {
252
277
  const parentRow = insertedRows[index] as unknown as Record<string, unknown>;
278
+ const hydratedRow: Record<string, unknown> = {
279
+ ...parentRow,
280
+ };
253
281
  const relationInputs = postInsertRelations[index] ?? [];
254
282
 
255
283
  for (const relationInput of relationInputs) {
256
284
  if (relationInput.relation.kind === "one") {
285
+ const sourceField = relationInput.relation.sourceField;
286
+ if (!sourceField) {
287
+ throw new Error(
288
+ "Relation '" +
289
+ tableName +
290
+ "." +
291
+ relationInput.relationName +
292
+ "' is missing sourceField metadata.",
293
+ );
294
+ }
295
+
296
+ const relationItems = toRelationItems(
297
+ relationInput.relation,
298
+ relationInput.relationName,
299
+ relationInput.value,
300
+ );
301
+ if (relationItems.length === 0) {
302
+ hydratedRow[relationInput.relationName] = null;
303
+ continue;
304
+ }
305
+
306
+ const sourceIdentifier = parentRow[sourceField];
307
+ hydratedRow[relationInput.relationName] =
308
+ (await fetchRelatedRowByIdentifier(
309
+ tx,
310
+ relationInput.relation,
311
+ relationInput.relationName,
312
+ sourceIdentifier,
313
+ )) ?? null;
257
314
  continue;
258
315
  }
259
316
 
@@ -274,6 +331,9 @@ export function generateQueryRuntimeWriteSection(): string {
274
331
  );
275
332
  }
276
333
 
334
+ const relatedRows: Array<Record<string, unknown>> = [];
335
+ hydratedRow[relationInput.relationName] = relatedRows;
336
+
277
337
  const relationItems = toRelationItems(
278
338
  relationInput.relation,
279
339
  relationInput.relationName,
@@ -284,29 +344,36 @@ export function generateQueryRuntimeWriteSection(): string {
284
344
  }
285
345
 
286
346
  if (relationInput.relation.kind === "many") {
287
- const targetTable = (mergedSchema as Record<string, unknown>)[
288
- relationInput.relation.targetTable
289
- ];
290
- if (!targetTable) {
347
+ const targetTable = getRelationTargetTable(
348
+ relationInput.relation,
349
+ relationInput.relationName,
350
+ );
351
+
352
+ const sourceField = relationInput.relation.sourceField;
353
+ const targetReferenceField =
354
+ relationInput.relation.referenceField ?? "id";
355
+ const targetReferenceColumn =
356
+ (targetTable as Record<string, unknown>)[targetReferenceField];
357
+ if (!sourceField) {
291
358
  throw new Error(
292
- "Unknown target table '" +
293
- relationInput.relation.targetTable +
294
- "' for relation '" +
359
+ "Relation '" +
295
360
  tableName +
296
361
  "." +
297
362
  relationInput.relationName +
298
- "'.",
363
+ "' is missing sourceField metadata.",
299
364
  );
300
365
  }
301
-
302
- const sourceField = relationInput.relation.sourceField;
303
- if (!sourceField) {
366
+ if (!targetReferenceColumn) {
304
367
  throw new Error(
305
- "Relation '" +
368
+ "Target table '" +
369
+ relationInput.relation.targetTable +
370
+ "' is missing column '" +
371
+ targetReferenceField +
372
+ "' required by relation '" +
306
373
  tableName +
307
374
  "." +
308
375
  relationInput.relationName +
309
- "' is missing sourceField metadata.",
376
+ "'.",
310
377
  );
311
378
  }
312
379
 
@@ -319,7 +386,18 @@ export function generateQueryRuntimeWriteSection(): string {
319
386
  await tx
320
387
  .update(targetTable as any)
321
388
  .set({ [sourceField]: parentReferenceValue } as any)
322
- .where(eq((targetTable as any).id, relationItem as any));
389
+ .where(
390
+ eq(targetReferenceColumn as any, relationItem as any),
391
+ );
392
+ const linkedRow = await fetchRelatedRowByIdentifier(
393
+ tx,
394
+ relationInput.relation,
395
+ relationInput.relationName,
396
+ relationItem,
397
+ );
398
+ if (linkedRow) {
399
+ relatedRows.push(linkedRow);
400
+ }
323
401
  continue;
324
402
  }
325
403
 
@@ -333,22 +411,48 @@ export function generateQueryRuntimeWriteSection(): string {
333
411
  );
334
412
  }
335
413
 
336
- const existingId = relationItem.id;
414
+ const existingId = relationItem[targetReferenceField];
337
415
  const payload = {
338
416
  ...relationItem,
339
417
  [sourceField]: parentReferenceValue,
340
418
  };
341
419
 
342
420
  if (existingId !== undefined && existingId !== null) {
343
- const { id: _ignored, ...setPayload } = payload;
421
+ const setPayload: Record<string, unknown> = {
422
+ ...payload,
423
+ };
424
+ delete setPayload[targetReferenceField];
344
425
  await tx
345
426
  .update(targetTable as any)
346
427
  .set(setPayload as any)
347
- .where(eq((targetTable as any).id, existingId as any));
428
+ .where(
429
+ eq(targetReferenceColumn as any, existingId as any),
430
+ );
431
+ const linkedRow = await fetchRelatedRowByIdentifier(
432
+ tx,
433
+ relationInput.relation,
434
+ relationInput.relationName,
435
+ existingId,
436
+ );
437
+ if (linkedRow) {
438
+ relatedRows.push(linkedRow);
439
+ }
348
440
  continue;
349
441
  }
350
442
 
351
- await tx.insert(targetTable as any).values(payload as any);
443
+ let createQuery: any = tx
444
+ .insert(targetTable as any)
445
+ .values(payload as any);
446
+ if (typeof createQuery.returning === "function") {
447
+ createQuery = createQuery.returning();
448
+ }
449
+ const createdRows = (await createQuery) as Array<
450
+ Record<string, unknown>
451
+ >;
452
+ const createdRow = createdRows[0];
453
+ if (createdRow) {
454
+ relatedRows.push(createdRow);
455
+ }
352
456
  }
353
457
  continue;
354
458
  }
@@ -381,6 +485,7 @@ export function generateQueryRuntimeWriteSection(): string {
381
485
  }
382
486
 
383
487
  const linkValues: Array<Record<string, unknown>> = [];
488
+ const targetIdentifiers: unknown[] = [];
384
489
  for (const relationItem of relationItems) {
385
490
  const targetId = await resolveRelationIdentifier(
386
491
  tx,
@@ -388,6 +493,7 @@ export function generateQueryRuntimeWriteSection(): string {
388
493
  relationInput.relationName,
389
494
  relationItem,
390
495
  );
496
+ targetIdentifiers.push(targetId);
391
497
 
392
498
  linkValues.push({
393
499
  [sourceField]: parentReferenceValue,
@@ -398,13 +504,27 @@ export function generateQueryRuntimeWriteSection(): string {
398
504
  if (linkValues.length > 0) {
399
505
  await tx.insert(junctionTable as any).values(linkValues as any);
400
506
  }
507
+
508
+ for (const targetId of targetIdentifiers) {
509
+ const linkedRow = await fetchRelatedRowByIdentifier(
510
+ tx,
511
+ relationInput.relation,
512
+ relationInput.relationName,
513
+ targetId,
514
+ );
515
+ if (linkedRow) {
516
+ relatedRows.push(linkedRow);
517
+ }
518
+ }
401
519
  }
520
+
521
+ hydratedRows.push(hydratedRow);
402
522
  }
403
523
 
404
- return insertedRows;
524
+ return hydratedRows as Array<QueryInsertResultRow<TableName, TArgs>>;
405
525
  };
406
526
 
407
- let rows: Array<TableModel<TableName>>;
527
+ let rows: Array<QueryInsertResultRow<TableName, TArgs>>;
408
528
  if (typeof transaction === "function") {
409
529
  try {
410
530
  rows = await transaction.call($db, (tx: any) =>