11import { LockContext , QueryResult } from '@powersync/common' ;
2- import { Column , DriverValueDecoder , getTableName , SQL } from 'drizzle-orm' ;
2+ import { Column , DriverValueDecoder , getTableName , NoopLogger , SQL } from 'drizzle-orm' ;
33import { entityKind , is } from 'drizzle-orm/entity' ;
44import type { Logger } from 'drizzle-orm/logger' ;
55import { fillPlaceholders , type Query } from 'drizzle-orm/sql/sql' ;
@@ -8,24 +8,31 @@ import type { SelectedFieldsOrdered } from 'drizzle-orm/sqlite-core/query-builde
88import {
99 type PreparedQueryConfig as PreparedQueryConfigBase ,
1010 type SQLiteExecuteMethod ,
11- SQLitePreparedQuery
11+ SQLitePreparedQuery ,
12+ type Result ,
13+ ExecuteResultSync
1214} from 'drizzle-orm/sqlite-core/session' ;
1315
14- type PreparedQueryConfig = Omit < PreparedQueryConfigBase , 'statement' | 'run' > ;
16+ type PreparedQueryConfig = Omit < PreparedQueryConfigBase , 'statement' > ;
1517
16- export class PowerSyncSQLitePreparedQuery <
17- T extends PreparedQueryConfig = PreparedQueryConfig
18- > extends SQLitePreparedQuery < {
19- type : 'async' ;
18+ // Define the result types for both sync and async modes
19+ type PowerSyncPreparedQueryConfig < TSync extends 'sync' | 'async' , T > = {
20+ type : TSync ;
2021 run : QueryResult ;
21- all : T [ 'all' ] ;
22- get : T [ 'get' ] ;
23- values : T [ 'values' ] ;
24- execute : T [ 'execute' ] ;
25- } > {
22+ all : QueryResult [ ] ;
23+ get : T ;
24+ values : T [ ] [ ] ;
25+ execute : T ;
26+ } ;
27+
28+ export class PowerSyncSQLitePreparedQuery <
29+ T extends PreparedQueryConfig = PreparedQueryConfig ,
30+ TSync extends 'async' | 'sync' = 'async'
31+ > extends SQLitePreparedQuery < PowerSyncPreparedQueryConfig < TSync , T > & T > {
2632 static readonly [ entityKind ] : string = 'PowerSyncSQLitePreparedQuery' ;
2733
2834 constructor (
35+ private mode : TSync ,
2936 private db : LockContext ,
3037 query : Query ,
3138 private logger : Logger ,
@@ -34,62 +41,108 @@ export class PowerSyncSQLitePreparedQuery<
3441 private _isResponseInArrayMode : boolean ,
3542 private customResultMapper ?: ( rows : unknown [ ] [ ] ) => unknown
3643 ) {
37- super ( 'async' , executeMethod , query ) ;
44+ super ( mode , executeMethod , query ) ;
3845 }
3946
40- async run ( placeholderValues ?: Record < string , unknown > ) : Promise < QueryResult > {
41- const params = fillPlaceholders ( this . query . params , placeholderValues ?? { } ) ;
42- this . logger . logQuery ( this . query . sql , params ) ;
43- const rs = await this . db . execute ( this . query . sql , params ) ;
44- return rs ;
45- }
47+ run ( placeholderValues ?: Record < string , unknown > ) : Result < TSync , QueryResult & T [ 'run' ] > {
48+ const runFn = async ( ) : Promise < QueryResult > => {
49+ const params = fillPlaceholders ( this . query . params , placeholderValues ?? { } ) ;
50+ this . logger . logQuery ( this . query . sql , params ) ;
51+ return await this . db . execute ( this . query . sql , params ) ;
52+ } ;
4653
47- async all ( placeholderValues ?: Record < string , unknown > ) : Promise < T [ 'all' ] > {
48- const { fields, query, logger, customResultMapper } = this ;
49- if ( ! fields && ! customResultMapper ) {
50- const params = fillPlaceholders ( query . params , placeholderValues ?? { } ) ;
51- logger . logQuery ( query . sql , params ) ;
52- const rs = await this . db . execute ( this . query . sql , params ) ;
53- return rs . rows ?. _array ?? [ ] ;
54+ if ( this . mode === 'sync' ) {
55+ return new ExecuteResultSync ( ( ) => {
56+ // For sync mode, you'd need a synchronous version of your db operations
57+ // This is just a placeholder - you'd need to implement sync versions
58+ throw new Error ( 'Sync mode not implemented for PowerSync' ) ;
59+ } ) as unknown as Result < TSync , QueryResult > ;
5460 }
5561
56- const rows = ( await this . values ( placeholderValues ) ) as unknown [ ] [ ] ;
62+ return runFn ( ) as Result < TSync , QueryResult > ;
63+ }
64+
65+ all ( placeholderValues ?: Record < string , unknown > ) : Result < TSync , QueryResult [ ] & T [ 'all' ] > {
66+ const allFn = async ( ) : Promise < T [ 'all' ] > => {
67+ const { fields, query, logger, customResultMapper } = this ;
68+
69+ if ( ! fields && ! customResultMapper ) {
70+ const params = fillPlaceholders ( query . params , placeholderValues ?? { } ) ;
71+ logger . logQuery ( query . sql , params ) ;
72+ const rs = await this . db . execute ( this . query . sql , params ) ;
73+ return rs . rows ?. _array ?? [ ] ;
74+ }
75+
76+ const rows = ( await this . valuesAsync ( placeholderValues ) ) as unknown [ ] [ ] ;
5777
58- if ( customResultMapper ) {
59- const mapped = customResultMapper ( rows ) as T [ 'all' ] ;
60- return mapped ;
78+ if ( customResultMapper ) {
79+ return customResultMapper ( rows ) as T [ 'all' ] ;
80+ }
81+
82+ return rows . map ( ( row ) => mapResultRow ( fields ! , row , ( this as any ) . joinsNotNullableMap ) ) ;
83+ } ;
84+
85+ if ( this . mode === 'sync' ) {
86+ return new ExecuteResultSync ( ( ) => {
87+ throw new Error ( 'Sync mode not implemented for PowerSync' ) ;
88+ } ) as unknown as Result < TSync , QueryResult [ ] & T [ 'all' ] > ;
6189 }
62- return rows . map ( ( row ) => mapResultRow ( fields ! , row , ( this as any ) . joinsNotNullableMap ) ) ;
90+
91+ return allFn ( ) as Result < TSync , QueryResult [ ] & T [ 'all' ] > ;
6392 }
6493
65- async get ( placeholderValues ?: Record < string , unknown > ) : Promise < T [ 'get' ] > {
66- const params = fillPlaceholders ( this . query . params , placeholderValues ?? { } ) ;
67- this . logger . logQuery ( this . query . sql , params ) ;
94+ get ( placeholderValues ?: Record < string , unknown > ) : Result < TSync , T & T [ 'get' ] > {
95+ const getFn = async ( ) : Promise < T [ 'get' ] > => {
96+ const params = fillPlaceholders ( this . query . params , placeholderValues ?? { } ) ;
97+ this . logger . logQuery ( this . query . sql , params ) ;
6898
69- const { fields, customResultMapper } = this ;
70- const joinsNotNullableMap = ( this as any ) . joinsNotNullableMap ;
71- if ( ! fields && ! customResultMapper ) {
72- return this . db . get ( this . query . sql , params ) ;
73- }
99+ const { fields, customResultMapper } = this ;
100+ const joinsNotNullableMap = ( this as any ) . joinsNotNullableMap ;
101+
102+ if ( ! fields && ! customResultMapper ) {
103+ return this . db . get ( this . query . sql , params ) ;
104+ }
105+
106+ const rows = ( await this . valuesAsync ( placeholderValues ) ) as unknown [ ] [ ] ;
107+ const row = rows [ 0 ] ;
108+
109+ if ( ! row ) {
110+ return undefined ;
111+ }
112+
113+ if ( customResultMapper ) {
114+ return customResultMapper ( rows ) as T [ 'get' ] ;
115+ }
74116
75- const rows = ( await this . values ( placeholderValues ) ) as unknown [ ] [ ] ;
76- const row = rows [ 0 ] ;
117+ return mapResultRow ( fields ! , row , joinsNotNullableMap ) ;
118+ } ;
77119
78- if ( ! row ) {
79- return undefined ;
120+ if ( this . mode === 'sync' ) {
121+ return new ExecuteResultSync ( ( ) => {
122+ throw new Error ( 'Sync mode not implemented for PowerSync' ) ;
123+ } ) as unknown as Result < TSync , T & T [ 'get' ] > ;
80124 }
81125
82- if ( customResultMapper ) {
83- return customResultMapper ( rows ) as T [ 'get' ] ;
126+ return getFn ( ) as Result < TSync , T & T [ 'get' ] > ;
127+ }
128+
129+ values ( placeholderValues ?: Record < string , unknown > ) : Result < TSync , T [ ] [ ] & T [ 'values' ] > {
130+ const valuesFn = async ( ) : Promise < T [ 'values' ] > => {
131+ return this . valuesAsync ( placeholderValues ) ;
132+ } ;
133+
134+ if ( this . mode === 'sync' ) {
135+ return new ExecuteResultSync ( ( ) => {
136+ throw new Error ( 'Sync mode not implemented for PowerSync' ) ;
137+ } ) as unknown as Result < TSync , T [ ] [ ] & T [ 'values' ] > ;
84138 }
85139
86- return mapResultRow ( fields ! , row , joinsNotNullableMap ) ;
140+ return valuesFn ( ) as Result < TSync , T [ ] [ ] & T [ 'values' ] > ;
87141 }
88142
89- async values ( placeholderValues ?: Record < string , unknown > ) : Promise < T [ 'values' ] > {
143+ private async valuesAsync ( placeholderValues ?: Record < string , unknown > ) : Promise < T [ 'values' ] > {
90144 const params = fillPlaceholders ( this . query . params , placeholderValues ?? { } ) ;
91145 this . logger . logQuery ( this . query . sql , params ) ;
92-
93146 return await this . db . executeRaw ( this . query . sql , params ) ;
94147 }
95148
@@ -98,6 +151,39 @@ export class PowerSyncSQLitePreparedQuery<
98151 }
99152}
100153
154+ // If you want to create specific sync/async versions for easier usage:
155+ export class PowerSyncSQLiteAsyncPreparedQuery <
156+ T extends PreparedQueryConfig = PreparedQueryConfig
157+ > extends PowerSyncSQLitePreparedQuery < T , 'async' > {
158+ constructor (
159+ db : LockContext ,
160+ query : Query ,
161+ logger : Logger ,
162+ fields : SelectedFieldsOrdered | undefined ,
163+ executeMethod : SQLiteExecuteMethod ,
164+ isResponseInArrayMode : boolean ,
165+ customResultMapper ?: ( rows : unknown [ ] [ ] ) => unknown
166+ ) {
167+ super ( 'async' , db , query , logger , fields , executeMethod , isResponseInArrayMode , customResultMapper ) ;
168+ }
169+ }
170+
171+ export class PowerSyncSQLiteSyncPreparedQuery <
172+ T extends PreparedQueryConfig = PreparedQueryConfig
173+ > extends PowerSyncSQLitePreparedQuery < T , 'sync' > {
174+ constructor (
175+ db : LockContext , // You'd need a sync version of LockContext
176+ query : Query ,
177+ logger : Logger ,
178+ fields : SelectedFieldsOrdered | undefined ,
179+ executeMethod : SQLiteExecuteMethod ,
180+ isResponseInArrayMode : boolean ,
181+ customResultMapper ?: ( rows : unknown [ ] [ ] ) => unknown
182+ ) {
183+ super ( 'sync' , db , query , logger , fields , executeMethod , isResponseInArrayMode , customResultMapper ) ;
184+ }
185+ }
186+
101187/**
102188 * Maps a flat array of database row values to a result object based on the provided column definitions.
103189 * It reconstructs the hierarchical structure of the result by following the specified paths for each field.
0 commit comments