turbine-orm 0.14.0 → 0.15.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.
@@ -166,7 +166,7 @@ export const cockroachdb = {
166
166
  },
167
167
  statementTimeout(seconds) {
168
168
  // CockroachDB v23.1+ supports transaction_timeout
169
- return `SET transaction_timeout = '${seconds}s'`;
169
+ return { sql: `SET transaction_timeout = $1`, params: [`${seconds}s`] };
170
170
  },
171
171
  };
172
172
  //# sourceMappingURL=cockroachdb.js.map
@@ -55,13 +55,16 @@ export interface DatabaseAdapter {
55
55
  introspectionOverrides?: Partial<IntrospectionOverrides>;
56
56
  /**
57
57
  * Generate the SQL to set a statement timeout within a transaction.
58
- * PostgreSQL uses `SET LOCAL statement_timeout = '<n>s'`.
59
- * CockroachDB uses `SET transaction_timeout = '<n>s'` (v23.1+).
58
+ * PostgreSQL uses `SET LOCAL statement_timeout = $1`.
59
+ * CockroachDB uses `SET transaction_timeout = $1` (v23.1+).
60
60
  *
61
61
  * @param seconds — timeout in seconds
62
- * @returns the SQL string to execute
62
+ * @returns an object with the parameterized SQL and its bound values
63
63
  */
64
- statementTimeout?(seconds: number): string;
64
+ statementTimeout?(seconds: number): {
65
+ sql: string;
66
+ params: unknown[];
67
+ };
65
68
  /**
66
69
  * SQL to create the lock table used by table-based locking adapters.
67
70
  * Called during `ensureTrackingTable` when the adapter uses table locks.
@@ -31,7 +31,7 @@ export const postgresql = {
31
31
  await client.query(`SELECT pg_advisory_unlock($1)`, [lockId]);
32
32
  },
33
33
  statementTimeout(seconds) {
34
- return `SET LOCAL statement_timeout = '${seconds}s'`;
34
+ return { sql: `SET LOCAL statement_timeout = $1`, params: [`${seconds}s`] };
35
35
  },
36
36
  };
37
37
  // ---------------------------------------------------------------------------
@@ -150,7 +150,7 @@ export const yugabytedb = {
150
150
  },
151
151
  statementTimeout(seconds) {
152
152
  // YugabyteDB supports standard PostgreSQL statement_timeout
153
- return `SET LOCAL statement_timeout = '${seconds}s'`;
153
+ return { sql: `SET LOCAL statement_timeout = $1`, params: [`${seconds}s`] };
154
154
  },
155
155
  };
156
156
  //# sourceMappingURL=yugabytedb.js.map
@@ -169,6 +169,6 @@ exports.cockroachdb = {
169
169
  },
170
170
  statementTimeout(seconds) {
171
171
  // CockroachDB v23.1+ supports transaction_timeout
172
- return `SET transaction_timeout = '${seconds}s'`;
172
+ return { sql: `SET transaction_timeout = $1`, params: [`${seconds}s`] };
173
173
  },
174
174
  };
@@ -34,7 +34,7 @@ exports.postgresql = {
34
34
  await client.query(`SELECT pg_advisory_unlock($1)`, [lockId]);
35
35
  },
36
36
  statementTimeout(seconds) {
37
- return `SET LOCAL statement_timeout = '${seconds}s'`;
37
+ return { sql: `SET LOCAL statement_timeout = $1`, params: [`${seconds}s`] };
38
38
  },
39
39
  };
40
40
  // ---------------------------------------------------------------------------
@@ -153,6 +153,6 @@ exports.yugabytedb = {
153
153
  },
154
154
  statementTimeout(seconds) {
155
155
  // YugabyteDB supports standard PostgreSQL statement_timeout
156
- return `SET LOCAL statement_timeout = '${seconds}s'`;
156
+ return { sql: `SET LOCAL statement_timeout = $1`, params: [`${seconds}s`] };
157
157
  },
158
158
  };
@@ -74,8 +74,12 @@ async function startStudio(options) {
74
74
  });
75
75
  const authToken = (0, node_crypto_1.randomBytes)(24).toString('hex');
76
76
  const stateDir = (0, node_path_1.resolve)(options.stateDir ?? '.turbine');
77
- const statementTimeoutSQL = options.adapter?.statementTimeout?.(30) ?? `SET LOCAL statement_timeout = '30s'`;
78
- const ctx = { pool, metadata, options, authToken, stateDir, statementTimeoutSQL };
77
+ const statementTimeout = options.adapter?.statementTimeout?.(30) ?? {
78
+ sql: `SET LOCAL statement_timeout = $1`,
79
+ params: ['30s'],
80
+ };
81
+ const rateLimiter = new Map();
82
+ const ctx = { pool, metadata, options, authToken, stateDir, statementTimeout, rateLimiter };
79
83
  const server = (0, node_http_1.createServer)((req, res) => {
80
84
  handleRequest(req, res, ctx).catch((err) => {
81
85
  sendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });
@@ -152,6 +156,14 @@ async function handleRequest(req, res, ctx) {
152
156
  sendJson(res, 401, { error: 'unauthorized — use the URL printed in the terminal' });
153
157
  return;
154
158
  }
159
+ // Rate limiting — 100 requests per 60 seconds per authenticated session.
160
+ const rateLimitResult = checkRateLimit(ctx.rateLimiter, ctx.authToken);
161
+ if (!rateLimitResult.allowed) {
162
+ const retryAfter = Math.ceil((rateLimitResult.resetAt - Date.now()) / 1000);
163
+ res.setHeader('Retry-After', String(retryAfter));
164
+ sendJson(res, 429, { error: 'Rate limit exceeded', retryAfter });
165
+ return;
166
+ }
155
167
  if (pathname === '/api/schema' && req.method === 'GET') {
156
168
  return apiSchema(res, ctx);
157
169
  }
@@ -192,6 +204,26 @@ function isAuthorized(req, expectedToken) {
192
204
  }
193
205
  return false;
194
206
  }
207
+ // ---------------------------------------------------------------------------
208
+ // Rate limiting
209
+ // ---------------------------------------------------------------------------
210
+ const RATE_LIMIT_WINDOW_MS = 60_000; // 60 seconds
211
+ const RATE_LIMIT_MAX_REQUESTS = 100;
212
+ function checkRateLimit(limiter, token) {
213
+ const now = Date.now();
214
+ const entry = limiter.get(token);
215
+ if (!entry || now >= entry.resetAt) {
216
+ // Start a new window
217
+ const resetAt = now + RATE_LIMIT_WINDOW_MS;
218
+ limiter.set(token, { count: 1, resetAt });
219
+ return { allowed: true, resetAt };
220
+ }
221
+ entry.count++;
222
+ if (entry.count > RATE_LIMIT_MAX_REQUESTS) {
223
+ return { allowed: false, resetAt: entry.resetAt };
224
+ }
225
+ return { allowed: true, resetAt: entry.resetAt };
226
+ }
195
227
  function constantTimeEqual(a, b) {
196
228
  if (a.length !== b.length)
197
229
  return false;
@@ -277,7 +309,7 @@ async function apiTableRows(res, ctx, rawTableName, params) {
277
309
  let mainWhere = '';
278
310
  if (hasSearch && pattern !== null) {
279
311
  mainValues.push(pattern);
280
- const conds = textColumns.map((c) => `${(0, index_js_1.quoteIdent)(c)} ILIKE $3`);
312
+ const conds = textColumns.map((c) => `${(0, index_js_1.quoteIdent)(c)} ILIKE $3 ESCAPE '\\'`);
281
313
  mainWhere = `WHERE (${conds.join(' OR ')})`;
282
314
  }
283
315
  // Count query: $1 = pattern (if search)
@@ -285,7 +317,7 @@ async function apiTableRows(res, ctx, rawTableName, params) {
285
317
  let countWhere = '';
286
318
  if (hasSearch && pattern !== null) {
287
319
  countValues.push(pattern);
288
- const conds = textColumns.map((c) => `${(0, index_js_1.quoteIdent)(c)} ILIKE $1`);
320
+ const conds = textColumns.map((c) => `${(0, index_js_1.quoteIdent)(c)} ILIKE $1 ESCAPE '\\'`);
289
321
  countWhere = `WHERE (${conds.join(' OR ')})`;
290
322
  }
291
323
  const qualifiedTable = `${(0, index_js_1.quoteIdent)(ctx.options.schema)}.${(0, index_js_1.quoteIdent)(table.name)}`;
@@ -294,7 +326,7 @@ async function apiTableRows(res, ctx, rawTableName, params) {
294
326
  const client = await ctx.pool.connect();
295
327
  try {
296
328
  await client.query('BEGIN READ ONLY');
297
- await client.query(ctx.statementTimeoutSQL);
329
+ await client.query(ctx.statementTimeout.sql, ctx.statementTimeout.params);
298
330
  const result = await client.query(sql, mainValues);
299
331
  const countResult = await client.query(countSql, countValues);
300
332
  await client.query('COMMIT');
@@ -351,6 +383,10 @@ async function apiQuery(req, res, ctx) {
351
383
  sendJson(res, 400, { error: 'missing sql' });
352
384
  return;
353
385
  }
386
+ if (rawSql.length > 10_000) {
387
+ sendJson(res, 400, { error: 'query too long — maximum 10,000 characters allowed' });
388
+ return;
389
+ }
354
390
  if (!isReadOnlyStatement(rawSql)) {
355
391
  sendJson(res, 400, {
356
392
  error: 'only SELECT / WITH statements are allowed in Studio — use the CLI for writes',
@@ -360,7 +396,7 @@ async function apiQuery(req, res, ctx) {
360
396
  const client = await ctx.pool.connect();
361
397
  try {
362
398
  await client.query('BEGIN READ ONLY');
363
- await client.query(ctx.statementTimeoutSQL);
399
+ await client.query(ctx.statementTimeout.sql, ctx.statementTimeout.params);
364
400
  const started = Date.now();
365
401
  const result = await client.query(rawSql);
366
402
  const elapsedMs = Date.now() - started;
@@ -412,7 +448,7 @@ async function apiBuilder(req, res, ctx) {
412
448
  const client = await ctx.pool.connect();
413
449
  try {
414
450
  await client.query('BEGIN READ ONLY');
415
- await client.query(ctx.statementTimeoutSQL);
451
+ await client.query(ctx.statementTimeout.sql, ctx.statementTimeout.params);
416
452
  const started = Date.now();
417
453
  const result = await client.query(deferred.sql, deferred.params);
418
454
  const elapsedMs = Date.now() - started;
@@ -608,6 +644,7 @@ function sendJson(res, status, body) {
608
644
  'Cache-Control': 'no-store',
609
645
  'X-Content-Type-Options': 'nosniff',
610
646
  'Referrer-Policy': 'no-referrer',
647
+ 'Content-Security-Policy': "default-src 'none'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; font-src 'self'; frame-ancestors 'none'",
611
648
  });
612
649
  res.end(payload);
613
650
  }
@@ -626,6 +663,7 @@ function sendHtml(res, status, body) {
626
663
  'X-Content-Type-Options': 'nosniff',
627
664
  'X-Frame-Options': 'DENY',
628
665
  'Referrer-Policy': 'no-referrer',
666
+ 'Content-Security-Policy': "default-src 'none'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'; font-src 'self'; frame-ancestors 'none'",
629
667
  });
630
668
  res.end(body);
631
669
  }
@@ -27,10 +27,35 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.TurbineClient = exports.TransactionClient = void 0;
30
+ exports.withRetry = withRetry;
30
31
  const pg_1 = __importDefault(require("pg"));
31
32
  const errors_js_1 = require("./errors.js");
32
33
  const pipeline_js_1 = require("./pipeline.js");
33
34
  const index_js_1 = require("./query/index.js");
35
+ async function withRetry(fn, options) {
36
+ const maxAttempts = options?.maxAttempts ?? 3;
37
+ const baseDelay = options?.baseDelay ?? 50;
38
+ const maxDelay = options?.maxDelay ?? 5000;
39
+ let lastError;
40
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
41
+ try {
42
+ return await fn();
43
+ }
44
+ catch (err) {
45
+ lastError = err;
46
+ const isRetryable = err &&
47
+ typeof err === 'object' &&
48
+ 'isRetryable' in err &&
49
+ err.isRetryable === true;
50
+ if (!isRetryable || attempt === maxAttempts - 1)
51
+ throw err;
52
+ options?.onRetry?.(err, attempt + 1);
53
+ const delay = Math.min(baseDelay * 2 ** attempt + Math.random() * baseDelay, maxDelay);
54
+ await new Promise((resolve) => setTimeout(resolve, delay));
55
+ }
56
+ }
57
+ throw lastError;
58
+ }
34
59
  /** Maps isolation level names to SQL */
35
60
  const ISOLATION_LEVELS = {
36
61
  ReadUncommitted: 'READ UNCOMMITTED',
@@ -79,7 +104,8 @@ class TransactionClient {
79
104
  // Create a QueryInterface that uses the transaction client as its "pool"
80
105
  // We use a proxy pool that routes queries through the transaction client
81
106
  const txPool = this.createTxPool();
82
- qi = new index_js_1.QueryInterface(txPool, name, this.schema, this.middlewares, this.queryOptions);
107
+ const txOpts = { ...this.queryOptions, _txScoped: true };
108
+ qi = new index_js_1.QueryInterface(txPool, name, this.schema, this.middlewares, txOpts);
83
109
  this.tableCache.set(name, qi);
84
110
  }
85
111
  return qi;
@@ -542,6 +568,27 @@ class TurbineClient {
542
568
  }
543
569
  }
544
570
  // -------------------------------------------------------------------------
571
+ // Retry — automatic retry for retryable errors (deadlock, serialization)
572
+ // -------------------------------------------------------------------------
573
+ /**
574
+ * Execute an async function with automatic retry on retryable errors.
575
+ *
576
+ * Only errors with `isRetryable === true` (DeadlockError, SerializationFailureError)
577
+ * are retried. Uses exponential backoff with jitter.
578
+ *
579
+ * @example
580
+ * ```ts
581
+ * const result = await db.$retry(() =>
582
+ * db.$transaction(async (tx) => {
583
+ * // ... serializable transaction logic
584
+ * }, { isolationLevel: 'Serializable' })
585
+ * );
586
+ * ```
587
+ */
588
+ async $retry(fn, options) {
589
+ return withRetry(fn, options);
590
+ }
591
+ // -------------------------------------------------------------------------
545
592
  // Connection lifecycle
546
593
  // -------------------------------------------------------------------------
547
594
  /**
@@ -6,7 +6,7 @@
6
6
  * All Turbine errors extend TurbineError which includes a `code` property.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.PipelineError = exports.CheckConstraintError = exports.SerializationFailureError = exports.DeadlockError = exports.NotNullViolationError = exports.ForeignKeyError = exports.UniqueConstraintError = exports.CircularRelationError = exports.MigrationError = exports.RelationError = exports.ConnectionError = exports.ValidationError = exports.TimeoutError = exports.NotFoundError = exports.TurbineError = exports.TurbineErrorCode = void 0;
9
+ exports.OptimisticLockError = exports.PipelineError = exports.ExclusionConstraintError = exports.CheckConstraintError = exports.SerializationFailureError = exports.DeadlockError = exports.NotNullViolationError = exports.ForeignKeyError = exports.UniqueConstraintError = exports.CircularRelationError = exports.MigrationError = exports.RelationError = exports.ConnectionError = exports.ValidationError = exports.TimeoutError = exports.NotFoundError = exports.TurbineError = exports.TurbineErrorCode = void 0;
10
10
  exports.setErrorMessageMode = setErrorMessageMode;
11
11
  exports.getErrorMessageMode = getErrorMessageMode;
12
12
  exports.wrapPgError = wrapPgError;
@@ -26,6 +26,8 @@ exports.TurbineErrorCode = {
26
26
  DEADLOCK_DETECTED: 'TURBINE_E012',
27
27
  SERIALIZATION_FAILURE: 'TURBINE_E013',
28
28
  PIPELINE: 'TURBINE_E014',
29
+ OPTIMISTIC_LOCK: 'TURBINE_E015',
30
+ EXCLUSION_VIOLATION: 'TURBINE_E016',
29
31
  };
30
32
  /** Base error class for all Turbine errors */
31
33
  class TurbineError extends Error {
@@ -351,6 +353,26 @@ class CheckConstraintError extends TurbineError {
351
353
  }
352
354
  }
353
355
  exports.CheckConstraintError = CheckConstraintError;
356
+ class ExclusionConstraintError extends TurbineError {
357
+ constraint;
358
+ table;
359
+ constructor(opts = {}) {
360
+ const { constraint, table, cause } = opts;
361
+ let message = opts.message;
362
+ if (!message) {
363
+ const constraintPart = constraint ? ` on ${constraint}` : '';
364
+ message = `[turbine] Exclusion constraint violation${constraintPart}`;
365
+ const detail = detailFromCause(cause);
366
+ if (detail)
367
+ message += `: ${detail}`;
368
+ }
369
+ super(exports.TurbineErrorCode.EXCLUSION_VIOLATION, message, { cause });
370
+ this.name = 'ExclusionConstraintError';
371
+ this.constraint = constraint;
372
+ this.table = table;
373
+ }
374
+ }
375
+ exports.ExclusionConstraintError = ExclusionConstraintError;
354
376
  /**
355
377
  * Thrown when a non-transactional pipeline has partial failures.
356
378
  *
@@ -392,6 +414,20 @@ class PipelineError extends TurbineError {
392
414
  }
393
415
  }
394
416
  exports.PipelineError = PipelineError;
417
+ class OptimisticLockError extends TurbineError {
418
+ table;
419
+ versionField;
420
+ expectedVersion;
421
+ constructor(opts) {
422
+ super(exports.TurbineErrorCode.OPTIMISTIC_LOCK, `[turbine] Optimistic lock failed on "${opts.table}" — ` +
423
+ `expected ${opts.versionField} = ${opts.expectedVersion} but row was modified by another transaction`);
424
+ this.name = 'OptimisticLockError';
425
+ this.table = opts.table;
426
+ this.versionField = opts.versionField;
427
+ this.expectedVersion = opts.expectedVersion;
428
+ }
429
+ }
430
+ exports.OptimisticLockError = OptimisticLockError;
395
431
  /**
396
432
  * Parse column names out of a pg `detail` string like:
397
433
  * "Key (email)=(foo@bar) already exists."
@@ -412,6 +448,7 @@ function parseColumnsFromDetail(detail) {
412
448
  * 23503 (foreign_key_violation) -> ForeignKeyError
413
449
  * 23502 (not_null_violation) -> NotNullViolationError
414
450
  * 23514 (check_violation) -> CheckConstraintError
451
+ * 23P01 (exclusion_violation) -> ExclusionConstraintError
415
452
  * 40P01 (deadlock_detected) -> DeadlockError (retryable)
416
453
  * 40001 (serialization_failure) -> SerializationFailureError (retryable)
417
454
  *
@@ -451,6 +488,12 @@ function wrapPgError(err) {
451
488
  table: e.table,
452
489
  cause: err,
453
490
  });
491
+ case '23P01':
492
+ return new ExclusionConstraintError({
493
+ constraint: e.constraint,
494
+ table: e.table,
495
+ cause: err,
496
+ });
454
497
  case '40P01':
455
498
  return new DeadlockError({
456
499
  constraint: e.constraint,
@@ -181,6 +181,92 @@ function generateTypes(schema) {
181
181
  }
182
182
  }
183
183
  }
184
+ // ---------------------------------------------------------------------------
185
+ // Nested write types (WhereUnique, NestedCreateInput, NestedUpdateInput,
186
+ // ConnectOrCreate, CreateInput, UpdateInput)
187
+ // ---------------------------------------------------------------------------
188
+ for (const table of Object.values(schema.tables)) {
189
+ const typeName = entityName(table.name);
190
+ const hasRels = Object.keys(table.relations).length > 0;
191
+ // WhereUnique — union of unique constraint shapes, deduplicating PK
192
+ const seen = new Set();
193
+ const uniqueSets = [];
194
+ // Always include the primary key first
195
+ const pkKey = table.primaryKey.join(',');
196
+ seen.add(pkKey);
197
+ uniqueSets.push(table.primaryKey);
198
+ // Add unique indexes that aren't duplicates of the PK
199
+ for (const uc of table.uniqueColumns) {
200
+ const ucKey = uc.join(',');
201
+ if (!seen.has(ucKey)) {
202
+ seen.add(ucKey);
203
+ uniqueSets.push(uc);
204
+ }
205
+ }
206
+ if (uniqueSets.length > 0) {
207
+ const branches = uniqueSets.map((cols) => {
208
+ const fields = cols.map((colName) => {
209
+ const col = table.columns.find((c) => c.name === colName);
210
+ const field = col?.field ?? colName;
211
+ const tsType = col?.tsType ?? 'unknown';
212
+ return `${field}: ${tsType}`;
213
+ });
214
+ return `{ ${fields.join('; ')} }`;
215
+ });
216
+ lines.push(`export type ${typeName}WhereUnique = ${branches.join(' | ')};`);
217
+ lines.push('');
218
+ }
219
+ // CreateInput / UpdateInput — extends base type with optional relation fields
220
+ if (hasRels) {
221
+ lines.push(`export type ${typeName}CreateInput = ${typeName}Create & {`);
222
+ for (const [relName, rel] of Object.entries(table.relations)) {
223
+ const targetType = entityName(rel.to);
224
+ lines.push(` ${relName}?: ${targetType}NestedCreateInput;`);
225
+ }
226
+ lines.push('};');
227
+ lines.push('');
228
+ lines.push(`export type ${typeName}UpdateInput = ${typeName}Update & {`);
229
+ for (const [relName, rel] of Object.entries(table.relations)) {
230
+ const targetType = entityName(rel.to);
231
+ if (rel.type === 'hasMany') {
232
+ lines.push(` ${relName}?: ${targetType}NestedUpdateInput;`);
233
+ }
234
+ else {
235
+ lines.push(` ${relName}?: ${targetType}NestedCreateInput;`);
236
+ }
237
+ }
238
+ lines.push('};');
239
+ lines.push('');
240
+ }
241
+ }
242
+ // Emit NestedCreateInput, NestedUpdateInput, ConnectOrCreate for every table
243
+ for (const table of Object.values(schema.tables)) {
244
+ const typeName = entityName(table.name);
245
+ const hasRels = Object.keys(table.relations).length > 0;
246
+ // NestedCreateInput uses *CreateInput (which includes relation fields) when
247
+ // the table has relations, otherwise falls back to the plain *Create type.
248
+ const createRefType = hasRels ? `${typeName}CreateInput` : `${typeName}Create`;
249
+ lines.push(`export interface ${typeName}NestedCreateInput {`);
250
+ lines.push(` create?: ${createRefType} | ${createRefType}[];`);
251
+ lines.push(` connect?: ${typeName}WhereUnique | ${typeName}WhereUnique[];`);
252
+ lines.push(` connectOrCreate?: ${typeName}ConnectOrCreate | ${typeName}ConnectOrCreate[];`);
253
+ lines.push('}');
254
+ lines.push('');
255
+ lines.push(`export interface ${typeName}NestedUpdateInput {`);
256
+ lines.push(` create?: ${createRefType} | ${createRefType}[];`);
257
+ lines.push(` connect?: ${typeName}WhereUnique | ${typeName}WhereUnique[];`);
258
+ lines.push(` connectOrCreate?: ${typeName}ConnectOrCreate | ${typeName}ConnectOrCreate[];`);
259
+ lines.push(` disconnect?: ${typeName}WhereUnique | ${typeName}WhereUnique[];`);
260
+ lines.push(` set?: ${typeName}WhereUnique[];`);
261
+ lines.push(` delete?: ${typeName}WhereUnique | ${typeName}WhereUnique[];`);
262
+ lines.push('}');
263
+ lines.push('');
264
+ lines.push(`export interface ${typeName}ConnectOrCreate {`);
265
+ lines.push(` where: ${typeName}WhereUnique;`);
266
+ lines.push(` create: ${createRefType};`);
267
+ lines.push('}');
268
+ lines.push('');
269
+ }
184
270
  return lines.join('\n');
185
271
  }
186
272
  // ---------------------------------------------------------------------------
package/dist/cjs/index.js CHANGED
@@ -34,7 +34,8 @@
34
34
  * ```
35
35
  */
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
- exports.turbineHttp = exports.schemaToSQLString = exports.schemaToSQL = exports.schemaPush = exports.schemaDiff = exports.table = exports.defineSchema = exports.column = exports.ColumnBuilder = exports.snakeToPascal = exports.snakeToCamel = exports.singularize = exports.pgTypeToTs = exports.pgArrayType = exports.normalizeKeyColumns = exports.isDateType = exports.camelToSnake = exports.QueryInterface = exports.pipelineSupported = exports.executePipeline = exports.introspect = exports.generate = exports.wrapPgError = exports.ValidationError = exports.UniqueConstraintError = exports.TurbineErrorCode = exports.TurbineError = exports.TimeoutError = exports.setErrorMessageMode = exports.SerializationFailureError = exports.RelationError = exports.PipelineError = exports.NotNullViolationError = exports.NotFoundError = exports.MigrationError = exports.getErrorMessageMode = exports.ForeignKeyError = exports.DeadlockError = exports.ConnectionError = exports.CircularRelationError = exports.CheckConstraintError = exports.postgresDialect = exports.TurbineClient = exports.TransactionClient = exports.yugabytedb = exports.timescale = exports.postgresql = exports.cockroachdb = exports.alloydb = void 0;
37
+ exports.table = exports.defineSchema = exports.column = exports.ColumnBuilder = exports.snakeToPascal = exports.snakeToCamel = exports.singularize = exports.pgTypeToTs = exports.pgArrayType = exports.normalizeKeyColumns = exports.isDateType = exports.camelToSnake = exports.QueryInterface = exports.pipelineSupported = exports.executePipeline = exports.hasRelationFields = exports.executeNestedUpdate = exports.executeNestedCreate = exports.introspect = exports.generate = exports.wrapPgError = exports.ValidationError = exports.UniqueConstraintError = exports.TurbineErrorCode = exports.TurbineError = exports.TimeoutError = exports.setErrorMessageMode = exports.SerializationFailureError = exports.RelationError = exports.PipelineError = exports.OptimisticLockError = exports.NotNullViolationError = exports.NotFoundError = exports.MigrationError = exports.getErrorMessageMode = exports.ForeignKeyError = exports.ExclusionConstraintError = exports.DeadlockError = exports.ConnectionError = exports.CircularRelationError = exports.CheckConstraintError = exports.postgresDialect = exports.withRetry = exports.TurbineClient = exports.TransactionClient = exports.yugabytedb = exports.timescale = exports.postgresql = exports.cockroachdb = exports.alloydb = void 0;
38
+ exports.turbineHttp = exports.schemaToSQLString = exports.schemaToSQL = exports.schemaPush = exports.schemaDiff = void 0;
38
39
  var index_js_1 = require("./adapters/index.js");
39
40
  Object.defineProperty(exports, "alloydb", { enumerable: true, get: function () { return index_js_1.alloydb; } });
40
41
  Object.defineProperty(exports, "cockroachdb", { enumerable: true, get: function () { return index_js_1.cockroachdb; } });
@@ -45,6 +46,7 @@ Object.defineProperty(exports, "yugabytedb", { enumerable: true, get: function (
45
46
  var client_js_1 = require("./client.js");
46
47
  Object.defineProperty(exports, "TransactionClient", { enumerable: true, get: function () { return client_js_1.TransactionClient; } });
47
48
  Object.defineProperty(exports, "TurbineClient", { enumerable: true, get: function () { return client_js_1.TurbineClient; } });
49
+ Object.defineProperty(exports, "withRetry", { enumerable: true, get: function () { return client_js_1.withRetry; } });
48
50
  var dialect_js_1 = require("./dialect.js");
49
51
  Object.defineProperty(exports, "postgresDialect", { enumerable: true, get: function () { return dialect_js_1.postgresDialect; } });
50
52
  // Error types
@@ -53,11 +55,13 @@ Object.defineProperty(exports, "CheckConstraintError", { enumerable: true, get:
53
55
  Object.defineProperty(exports, "CircularRelationError", { enumerable: true, get: function () { return errors_js_1.CircularRelationError; } });
54
56
  Object.defineProperty(exports, "ConnectionError", { enumerable: true, get: function () { return errors_js_1.ConnectionError; } });
55
57
  Object.defineProperty(exports, "DeadlockError", { enumerable: true, get: function () { return errors_js_1.DeadlockError; } });
58
+ Object.defineProperty(exports, "ExclusionConstraintError", { enumerable: true, get: function () { return errors_js_1.ExclusionConstraintError; } });
56
59
  Object.defineProperty(exports, "ForeignKeyError", { enumerable: true, get: function () { return errors_js_1.ForeignKeyError; } });
57
60
  Object.defineProperty(exports, "getErrorMessageMode", { enumerable: true, get: function () { return errors_js_1.getErrorMessageMode; } });
58
61
  Object.defineProperty(exports, "MigrationError", { enumerable: true, get: function () { return errors_js_1.MigrationError; } });
59
62
  Object.defineProperty(exports, "NotFoundError", { enumerable: true, get: function () { return errors_js_1.NotFoundError; } });
60
63
  Object.defineProperty(exports, "NotNullViolationError", { enumerable: true, get: function () { return errors_js_1.NotNullViolationError; } });
64
+ Object.defineProperty(exports, "OptimisticLockError", { enumerable: true, get: function () { return errors_js_1.OptimisticLockError; } });
61
65
  Object.defineProperty(exports, "PipelineError", { enumerable: true, get: function () { return errors_js_1.PipelineError; } });
62
66
  Object.defineProperty(exports, "RelationError", { enumerable: true, get: function () { return errors_js_1.RelationError; } });
63
67
  Object.defineProperty(exports, "SerializationFailureError", { enumerable: true, get: function () { return errors_js_1.SerializationFailureError; } });
@@ -74,6 +78,11 @@ Object.defineProperty(exports, "generate", { enumerable: true, get: function ()
74
78
  // Introspection
75
79
  var introspect_js_1 = require("./introspect.js");
76
80
  Object.defineProperty(exports, "introspect", { enumerable: true, get: function () { return introspect_js_1.introspect; } });
81
+ // Nested writes
82
+ var nested_write_js_1 = require("./nested-write.js");
83
+ Object.defineProperty(exports, "executeNestedCreate", { enumerable: true, get: function () { return nested_write_js_1.executeNestedCreate; } });
84
+ Object.defineProperty(exports, "executeNestedUpdate", { enumerable: true, get: function () { return nested_write_js_1.executeNestedUpdate; } });
85
+ Object.defineProperty(exports, "hasRelationFields", { enumerable: true, get: function () { return nested_write_js_1.hasRelationFields; } });
77
86
  // Pipeline
78
87
  var pipeline_js_1 = require("./pipeline.js");
79
88
  Object.defineProperty(exports, "executePipeline", { enumerable: true, get: function () { return pipeline_js_1.executePipeline; } });