prisma-sql 1.52.0 → 1.54.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/generator.js CHANGED
@@ -54,7 +54,7 @@ var require_package = __commonJS({
54
54
  "package.json"(exports$1, module) {
55
55
  module.exports = {
56
56
  name: "prisma-sql",
57
- version: "1.52.0",
57
+ version: "1.54.0",
58
58
  description: "Convert Prisma queries to optimized SQL with type safety. 2-7x faster than Prisma Client.",
59
59
  main: "dist/index.cjs",
60
60
  module: "dist/index.js",
@@ -4951,12 +4951,98 @@ function generateCode(models, queries, dialect, datamodel) {
4951
4951
  }));
4952
4952
  const { mappings, fieldTypes } = extractEnumMappings(datamodel);
4953
4953
  return `// Generated by @prisma-sql/generator - DO NOT EDIT
4954
- import { buildSQL, transformQueryResults, type PrismaMethod, type Model } from 'prisma-sql'
4954
+ import { buildSQL, buildBatchSql, parseBatchResults, buildBatchCountSql, parseBatchCountResults, createTransactionExecutor, transformQueryResults, type PrismaMethod, type Model, type BatchQuery, type TransactionQuery, type TransactionOptions } from 'prisma-sql'
4955
+
4956
+ export interface BatchCountQuery {
4957
+ model: string
4958
+ method: 'count'
4959
+ args?: { where?: Record<string, unknown> }
4960
+ }
4961
+
4962
+ export interface TransactionQuery {
4963
+ model: string
4964
+ method: PrismaMethod
4965
+ args?: Record<string, unknown>
4966
+ }
4967
+
4968
+ export interface TransactionOptions {
4969
+ isolationLevel?: 'ReadCommitted' | 'RepeatableRead' | 'Serializable'
4970
+ timeout?: number
4971
+ }
4972
+
4973
+ class DeferredQuery {
4974
+ constructor(
4975
+ public readonly model: string,
4976
+ public readonly method: PrismaMethod,
4977
+ public readonly args: any,
4978
+ ) {}
4979
+
4980
+ then(onfulfilled?: any, onrejected?: any): any {
4981
+ throw new Error(
4982
+ 'Cannot await a batch query. Batch queries must not be awaited inside the $batch callback.',
4983
+ )
4984
+ }
4985
+ }
4986
+
4987
+ interface BatchProxy {
4988
+ [modelName: string]: {
4989
+ findMany: (args?: any) => DeferredQuery
4990
+ findFirst: (args?: any) => DeferredQuery
4991
+ findUnique: (args?: any) => DeferredQuery
4992
+ count: (args?: any) => DeferredQuery
4993
+ aggregate: (args?: any) => DeferredQuery
4994
+ groupBy: (args?: any) => DeferredQuery
4995
+ }
4996
+ }
4997
+
4998
+ const ACCELERATED_METHODS = new Set<PrismaMethod>([
4999
+ 'findMany',
5000
+ 'findFirst',
5001
+ 'findUnique',
5002
+ 'count',
5003
+ 'aggregate',
5004
+ 'groupBy',
5005
+ ])
5006
+
5007
+ function createBatchProxy(): BatchProxy {
5008
+ return new Proxy(
5009
+ {},
5010
+ {
5011
+ get(_target, modelName: string): any {
5012
+ if (typeof modelName === 'symbol') return undefined
5013
+
5014
+ const model = MODEL_MAP.get(modelName)
5015
+ if (!model) {
5016
+ throw new Error(
5017
+ \`Model '\${modelName}' not found. Available: \${[...MODEL_MAP.keys()].join(', ')}\`,
5018
+ )
5019
+ }
5020
+
5021
+ return new Proxy(
5022
+ {},
5023
+ {
5024
+ get(_target, method: string): (args?: any) => DeferredQuery {
5025
+ if (!ACCELERATED_METHODS.has(method as PrismaMethod)) {
5026
+ throw new Error(
5027
+ \`Method '\${method}' not supported in batch. Supported: \${[...ACCELERATED_METHODS].join(', ')}\`,
5028
+ )
5029
+ }
5030
+
5031
+ return (args?: any): DeferredQuery => {
5032
+ return new DeferredQuery(
5033
+ modelName,
5034
+ method as PrismaMethod,
5035
+ args,
5036
+ )
5037
+ }
5038
+ },
5039
+ },
5040
+ )
5041
+ },
5042
+ },
5043
+ ) as BatchProxy
5044
+ }
4955
5045
 
4956
- /**
4957
- * Normalize values for SQL params.
4958
- * Synced from src/utils/normalize-value.ts
4959
- */
4960
5046
  function normalizeValue(value: unknown, seen = new WeakSet<object>(), depth = 0): unknown {
4961
5047
  const MAX_DEPTH = 20
4962
5048
  if (depth > MAX_DEPTH) {
@@ -5003,9 +5089,6 @@ function normalizeValue(value: unknown, seen = new WeakSet<object>(), depth = 0)
5003
5089
  return value
5004
5090
  }
5005
5091
 
5006
- /**
5007
- * Get nested value from object using dot notation path
5008
- */
5009
5092
  function getByPath(obj: any, path: string): unknown {
5010
5093
  if (!obj || !path) return undefined
5011
5094
  const keys = path.split('.')
@@ -5017,9 +5100,6 @@ function getByPath(obj: any, path: string): unknown {
5017
5100
  return result
5018
5101
  }
5019
5102
 
5020
- /**
5021
- * Normalize all params in array
5022
- */
5023
5103
  function normalizeParams(params: unknown[]): unknown[] {
5024
5104
  return params.map(p => normalizeValue(p))
5025
5105
  }
@@ -5040,6 +5120,8 @@ const QUERIES: Record<string, Record<string, Record<string, {
5040
5120
 
5041
5121
  const DIALECT = ${JSON.stringify(dialect)}
5042
5122
 
5123
+ const MODEL_MAP = new Map(MODELS.map(m => [m.name, m]))
5124
+
5043
5125
  function isDynamicParam(key: string): boolean {
5044
5126
  return key === 'skip' || key === 'take' || key === 'cursor'
5045
5127
  }
@@ -5206,6 +5288,13 @@ async function executeQuery(client: any, sql: string, params: unknown[]): Promis
5206
5288
  return stmt.all(...normalizedParams)
5207
5289
  }
5208
5290
 
5291
+ async function executeRaw(client: any, sql: string, params?: unknown[]): Promise<unknown[]> {
5292
+ if (DIALECT === 'postgres') {
5293
+ return await client.unsafe(sql, (params || []) as any[])
5294
+ }
5295
+ throw new Error('Raw execution for sqlite not supported in transactions')
5296
+ }
5297
+
5209
5298
  export function speedExtension(config: {
5210
5299
  postgres?: any
5211
5300
  sqlite?: any
@@ -5233,6 +5322,14 @@ export function speedExtension(config: {
5233
5322
  }
5234
5323
 
5235
5324
  return (prisma: any) => {
5325
+ const txExecutor = createTransactionExecutor({
5326
+ modelMap: MODEL_MAP,
5327
+ allModels: MODELS,
5328
+ dialect: DIALECT,
5329
+ executeRaw: (sql: string, params?: unknown[]) => executeRaw(client, sql, params),
5330
+ postgresClient: postgres,
5331
+ })
5332
+
5236
5333
  const handleMethod = async function(this: any, method: PrismaMethod, args: any) {
5237
5334
  const modelName = this?.name || this?.$name
5238
5335
  const startTime = Date.now()
@@ -5286,11 +5383,130 @@ export function speedExtension(config: {
5286
5383
  return transformQueryResults(method, results)
5287
5384
  }
5288
5385
 
5386
+ async function batch<T extends Record<string, DeferredQuery>>(
5387
+ callback: (batch: BatchProxy) => T | Promise<T>,
5388
+ ): Promise<{ [K in keyof T]: any }> {
5389
+ const batchProxy = createBatchProxy()
5390
+ const queries = await callback(batchProxy)
5391
+
5392
+ const batchQueries: Record<string, BatchQuery> = {}
5393
+
5394
+ for (const [key, deferred] of Object.entries(queries)) {
5395
+ if (!(deferred instanceof DeferredQuery)) {
5396
+ throw new Error(
5397
+ \`Batch query '\${key}' must be a deferred query. Did you await it?\`,
5398
+ )
5399
+ }
5400
+
5401
+ batchQueries[key] = {
5402
+ model: deferred.model,
5403
+ method: deferred.method,
5404
+ args: deferred.args || {},
5405
+ }
5406
+ }
5407
+
5408
+ const startTime = Date.now()
5409
+ const { sql, params, keys } = buildBatchSql(
5410
+ batchQueries,
5411
+ MODEL_MAP,
5412
+ MODELS,
5413
+ DIALECT,
5414
+ )
5415
+
5416
+ if (debug) {
5417
+ console.log(\`[\${DIALECT}] $batch (\${keys.length} queries)\`)
5418
+ console.log('SQL:', sql)
5419
+ console.log('Params:', params)
5420
+ }
5421
+
5422
+ const normalizedParams = normalizeParams(params)
5423
+ const rows = await client.unsafe(sql, normalizedParams as any[])
5424
+ const row = rows[0] as Record<string, unknown>
5425
+ const results = parseBatchResults(row, keys, batchQueries)
5426
+ const duration = Date.now() - startTime
5427
+
5428
+ onQuery?.({
5429
+ model: '_batch',
5430
+ method: 'batch',
5431
+ sql,
5432
+ params: normalizedParams,
5433
+ duration,
5434
+ prebaked: false,
5435
+ })
5436
+
5437
+ return results as { [K in keyof T]: any }
5438
+ }
5439
+
5440
+ async function batchCount(queries: BatchCountQuery[]): Promise<number[]> {
5441
+ if (queries.length === 0) return []
5442
+
5443
+ const startTime = Date.now()
5444
+ const { sql, params } = buildBatchCountSql(
5445
+ queries,
5446
+ MODEL_MAP,
5447
+ MODELS,
5448
+ DIALECT,
5449
+ )
5450
+
5451
+ if (debug) {
5452
+ console.log(\`[\${DIALECT}] $batchCount (\${queries.length} queries)\`)
5453
+ console.log('SQL:', sql)
5454
+ console.log('Params:', params)
5455
+ }
5456
+
5457
+ const normalizedParams = normalizeParams(params)
5458
+ const rows = await client.unsafe(sql, normalizedParams as any[])
5459
+ const row = rows[0] as Record<string, unknown>
5460
+ const results = parseBatchCountResults(row, queries.length)
5461
+ const duration = Date.now() - startTime
5462
+
5463
+ onQuery?.({
5464
+ model: '_batch',
5465
+ method: 'count',
5466
+ sql,
5467
+ params: normalizedParams,
5468
+ duration,
5469
+ prebaked: false,
5470
+ })
5471
+
5472
+ return results
5473
+ }
5474
+
5475
+ async function transaction(
5476
+ queries: TransactionQuery[],
5477
+ options?: TransactionOptions,
5478
+ ): Promise<unknown[]> {
5479
+ const startTime = Date.now()
5480
+
5481
+ if (debug) {
5482
+ console.log(\`[\${DIALECT}] $transaction (\${queries.length} queries)\`)
5483
+ }
5484
+
5485
+ const results = await txExecutor.execute(queries, options)
5486
+ const duration = Date.now() - startTime
5487
+
5488
+ onQuery?.({
5489
+ model: '_transaction',
5490
+ method: 'count',
5491
+ sql: \`TRANSACTION(\${queries.length})\`,
5492
+ params: [],
5493
+ duration,
5494
+ prebaked: false,
5495
+ })
5496
+
5497
+ return results
5498
+ }
5499
+
5289
5500
  return prisma.$extends({
5290
5501
  name: 'prisma-sql-generated',
5291
5502
 
5292
5503
  client: {
5293
5504
  $original: prisma,
5505
+ $batch: batch as <T extends Record<string, DeferredQuery>>(
5506
+ callback: (batch: BatchProxy) => T | Promise<T>,
5507
+ ) => Promise<{ [K in keyof T]: any }>,
5508
+ $batchCount: batchCount as (...args: any[]) => Promise<number[]>,
5509
+ $transaction: transaction as (...args: any[]) => Promise<unknown[]>,
5294
5510
  },
5295
5511
 
5296
5512
  model: {
@@ -5318,6 +5534,16 @@ export function speedExtension(config: {
5318
5534
  })
5319
5535
  }
5320
5536
  }
5537
+
5538
+ type SpeedExtensionReturn = ReturnType<ReturnType<typeof speedExtension>>
5539
+
5540
+ export type SpeedClient<T> = T & {
5541
+ $batch<T extends Record<string, DeferredQuery>>(
5542
+ callback: (batch: BatchProxy) => T | Promise<T>,
5543
+ ): Promise<{ [K in keyof T]: any }>
5544
+ $batchCount(queries: BatchCountQuery[]): Promise<number[]>
5545
+ $transaction(queries: TransactionQuery[], options?: TransactionOptions): Promise<unknown[]>
5546
+ }
5321
5547
  `;
5322
5548
  }
5323
5549
  function formatQueries(queries) {