turbine-orm 0.16.0 → 0.19.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.
Files changed (43) hide show
  1. package/README.md +180 -12
  2. package/dist/adapters/cockroachdb.js +4 -2
  3. package/dist/adapters/index.js +4 -1
  4. package/dist/adapters/yugabytedb.js +4 -2
  5. package/dist/cjs/adapters/cockroachdb.js +4 -2
  6. package/dist/cjs/adapters/index.js +4 -1
  7. package/dist/cjs/adapters/yugabytedb.js +4 -2
  8. package/dist/cjs/cli/studio-ui.generated.js +1 -1
  9. package/dist/cjs/cli/studio.js +35 -73
  10. package/dist/cjs/client.js +164 -0
  11. package/dist/cjs/errors.js +35 -5
  12. package/dist/cjs/generate.js +14 -3
  13. package/dist/cjs/index.js +10 -2
  14. package/dist/cjs/introspect.js +81 -0
  15. package/dist/cjs/nested-write.js +70 -6
  16. package/dist/cjs/query/builder.js +581 -17
  17. package/dist/cjs/realtime.js +147 -0
  18. package/dist/cjs/schema-builder.js +86 -0
  19. package/dist/cjs/schema.js +10 -0
  20. package/dist/cjs/typed-sql.js +149 -0
  21. package/dist/cli/studio-ui.generated.js +1 -1
  22. package/dist/cli/studio.js +35 -73
  23. package/dist/client.d.ts +120 -0
  24. package/dist/client.js +165 -1
  25. package/dist/errors.js +35 -5
  26. package/dist/generate.js +14 -3
  27. package/dist/index.d.ts +4 -2
  28. package/dist/index.js +5 -1
  29. package/dist/introspect.js +81 -0
  30. package/dist/nested-write.js +70 -6
  31. package/dist/query/builder.d.ts +104 -1
  32. package/dist/query/builder.js +582 -18
  33. package/dist/query/index.d.ts +1 -1
  34. package/dist/query/types.d.ts +126 -2
  35. package/dist/realtime.d.ts +71 -0
  36. package/dist/realtime.js +144 -0
  37. package/dist/schema-builder.d.ts +68 -1
  38. package/dist/schema-builder.js +85 -0
  39. package/dist/schema.d.ts +18 -1
  40. package/dist/schema.js +10 -0
  41. package/dist/typed-sql.d.ts +101 -0
  42. package/dist/typed-sql.js +145 -0
  43. package/package.json +17 -15
@@ -144,17 +144,29 @@ async function executeNestedCreate(ctx, tableName, data, depth = 0, path = []) {
144
144
  }
145
145
  validateOps(relName, ops, false);
146
146
  }
147
- // Insert the parent row
148
- const parentRow = (await ctx.tx.table(tableName).create({ data: scalars }));
149
- // Process each relation
147
+ // belongsTo relations put the foreign key on the PARENT row, so they must be
148
+ // resolved BEFORE the parent is inserted — otherwise a NOT NULL FK column
149
+ // fails on the initial INSERT. We resolve each belongsTo op (create/connect/
150
+ // connectOrCreate) to its referenced row and fold the FK values into the
151
+ // parent's own INSERT.
152
+ const belongsToFks = {};
153
+ for (const [relName, ops] of Object.entries(relations)) {
154
+ const rel = tableMeta.relations[relName];
155
+ if (rel.type === 'belongsTo') {
156
+ Object.assign(belongsToFks, await resolveBelongsToForCreate(ctx, rel, ops, tableName, depth, path, relName));
157
+ }
158
+ }
159
+ // Insert the parent row (scalars + resolved belongsTo foreign keys)
160
+ const parentRow = (await ctx.tx.table(tableName).create({
161
+ data: { ...scalars, ...belongsToFks },
162
+ }));
163
+ // Process hasMany / hasOne relations — their FK lives on the CHILD, so they
164
+ // need the parent row to exist first.
150
165
  for (const [relName, ops] of Object.entries(relations)) {
151
166
  const rel = tableMeta.relations[relName];
152
167
  if (rel.type === 'hasMany' || rel.type === 'hasOne') {
153
168
  await processHasManyCreate(ctx, rel, ops, parentRow, depth, path, relName);
154
169
  }
155
- else if (rel.type === 'belongsTo') {
156
- await processBelongsToCreate(ctx, rel, ops, parentRow, tableName, depth, path, relName);
157
- }
158
170
  }
159
171
  // Build the `with` clause for the final read to return the full tree
160
172
  const withClause = {};
@@ -319,6 +331,58 @@ async function processHasManyCreate(ctx, rel, ops, parentRow, depth, path, relNa
319
331
  // ---------------------------------------------------------------------------
320
332
  // belongsTo create operations
321
333
  // ---------------------------------------------------------------------------
334
+ /**
335
+ * Resolve a belongsTo relation's create/connect/connectOrCreate op to the
336
+ * foreign-key value(s) that belong on the PARENT row, returning them keyed by
337
+ * the parent's own field names so they can be merged into the parent INSERT.
338
+ *
339
+ * Used by the create path only. (The update path uses processBelongsToCreate,
340
+ * which UPDATEs the FK after the parent already exists.)
341
+ */
342
+ async function resolveBelongsToForCreate(ctx, rel, ops, parentTable, depth, path, relName) {
343
+ const fks = (0, schema_js_1.normalizeKeyColumns)(rel.foreignKey);
344
+ const refs = (0, schema_js_1.normalizeKeyColumns)(rel.referenceKey);
345
+ const parentMeta = ctx.schema.tables[parentTable];
346
+ const relatedTable = ctx.schema.tables[rel.to];
347
+ let relatedRow = null;
348
+ if (ops.create !== undefined) {
349
+ const items = toArray(ops.create);
350
+ if (items.length > 0) {
351
+ relatedRow = (await executeNestedCreate(ctx, rel.to, items[0], depth + 1, [...path, relName]));
352
+ }
353
+ }
354
+ else if (ops.connect !== undefined) {
355
+ const items = toArray(ops.connect);
356
+ if (items.length > 0) {
357
+ const target = items[0];
358
+ relatedRow = (await ctx.tx.table(rel.to).findUnique({ where: target }));
359
+ if (!relatedRow) {
360
+ throw new errors_js_1.ValidationError(`[turbine] connect on "${relName}": no ${rel.to} row found matching ${JSON.stringify(target)}.`);
361
+ }
362
+ }
363
+ }
364
+ else if (ops.connectOrCreate !== undefined) {
365
+ const items = toArray(ops.connectOrCreate);
366
+ if (items.length > 0) {
367
+ const op = items[0];
368
+ relatedRow = (await ctx.tx.table(rel.to).findUnique({ where: op.where }));
369
+ if (!relatedRow) {
370
+ // For belongsTo the FK lives on the parent, so the related row is
371
+ // created plainly (no FK injection) and we read its reference key.
372
+ relatedRow = (await ctx.tx.table(rel.to).create({ data: op.create }));
373
+ }
374
+ }
375
+ }
376
+ const fkScalars = {};
377
+ if (relatedRow) {
378
+ for (let i = 0; i < fks.length; i++) {
379
+ const fkField = parentMeta.reverseColumnMap[fks[i]] ?? fks[i];
380
+ const refField = relatedTable?.reverseColumnMap[refs[i]] ?? refs[i];
381
+ fkScalars[fkField] = relatedRow[refField];
382
+ }
383
+ }
384
+ return fkScalars;
385
+ }
322
386
  async function processBelongsToCreate(ctx, rel, ops, parentRow, parentTable, depth, path, relName) {
323
387
  const fks = (0, schema_js_1.normalizeKeyColumns)(rel.foreignKey);
324
388
  const refs = (0, schema_js_1.normalizeKeyColumns)(rel.referenceKey);