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