11import { invariant } from '@zenstackhq/common-helpers' ;
2- import type Decimal from 'decimal.js' ;
2+ import Decimal from 'decimal.js' ;
33import {
44 ExpressionWrapper ,
55 sql ,
@@ -9,9 +9,10 @@ import {
99 type SelectQueryBuilder ,
1010} from 'kysely' ;
1111import { match } from 'ts-pattern' ;
12- import type { BuiltinType , GetModels , SchemaDef } from '../../../schema' ;
12+ import type { BuiltinType , FieldDef , GetModels , SchemaDef } from '../../../schema' ;
1313import { DELEGATE_JOINED_FIELD_PREFIX } from '../../constants' ;
1414import type { FindArgs } from '../../crud-types' ;
15+ import { QueryError } from '../../errors' ;
1516import {
1617 getDelegateDescendantModels ,
1718 getManyToManyRelation ,
@@ -41,7 +42,13 @@ export class SqliteCrudDialect<Schema extends SchemaDef> extends BaseCrudDialect
4142 } else {
4243 return match ( type )
4344 . with ( 'Boolean' , ( ) => ( value ? 1 : 0 ) )
44- . with ( 'DateTime' , ( ) => ( value instanceof Date ? value . toISOString ( ) : value ) )
45+ . with ( 'DateTime' , ( ) =>
46+ value instanceof Date
47+ ? value . toISOString ( )
48+ : typeof value === 'string'
49+ ? new Date ( value ) . toISOString ( )
50+ : value ,
51+ )
4552 . with ( 'Decimal' , ( ) => ( value as Decimal ) . toString ( ) )
4653 . with ( 'Bytes' , ( ) => Buffer . from ( value as Uint8Array ) )
4754 . with ( 'Json' , ( ) => JSON . stringify ( value ) )
@@ -50,6 +57,68 @@ export class SqliteCrudDialect<Schema extends SchemaDef> extends BaseCrudDialect
5057 }
5158 }
5259
60+ override transformOutput ( value : unknown , type : BuiltinType ) {
61+ if ( this . schema . typeDefs && type in this . schema . typeDefs ) {
62+ // typed JSON field
63+ return this . transformOutputJson ( value ) ;
64+ } else {
65+ return match ( type )
66+ . with ( 'Boolean' , ( ) => this . transformOutputBoolean ( value ) )
67+ . with ( 'DateTime' , ( ) => this . transformOutputDate ( value ) )
68+ . with ( 'Bytes' , ( ) => this . transformOutputBytes ( value ) )
69+ . with ( 'Decimal' , ( ) => this . transformOutputDecimal ( value ) )
70+ . with ( 'BigInt' , ( ) => this . transformOutputBigInt ( value ) )
71+ . with ( 'Json' , ( ) => this . transformOutputJson ( value ) )
72+ . otherwise ( ( ) => super . transformOutput ( value , type ) ) ;
73+ }
74+ }
75+
76+ private transformOutputDecimal ( value : unknown ) {
77+ if ( value instanceof Decimal ) {
78+ return value ;
79+ }
80+ invariant (
81+ typeof value === 'string' || typeof value === 'number' || value instanceof Decimal ,
82+ `Expected string, number or Decimal, got ${ typeof value } ` ,
83+ ) ;
84+ return new Decimal ( value ) ;
85+ }
86+
87+ private transformOutputBigInt ( value : unknown ) {
88+ if ( typeof value === 'bigint' ) {
89+ return value ;
90+ }
91+ invariant (
92+ typeof value === 'string' || typeof value === 'number' ,
93+ `Expected string or number, got ${ typeof value } ` ,
94+ ) ;
95+ return BigInt ( value ) ;
96+ }
97+
98+ private transformOutputBoolean ( value : unknown ) {
99+ return ! ! value ;
100+ }
101+
102+ private transformOutputDate ( value : unknown ) {
103+ if ( typeof value === 'number' ) {
104+ return new Date ( value ) ;
105+ } else if ( typeof value === 'string' ) {
106+ return new Date ( value ) ;
107+ } else {
108+ return value ;
109+ }
110+ }
111+
112+ private transformOutputBytes ( value : unknown ) {
113+ return Buffer . isBuffer ( value ) ? Uint8Array . from ( value ) : value ;
114+ }
115+
116+ private transformOutputJson ( value : unknown ) {
117+ // better-sqlite3 returns JSON as string
118+ invariant ( typeof value === 'string' , 'Expected string, got ' + typeof value ) ;
119+ return JSON . parse ( value as string ) ;
120+ }
121+
53122 override buildRelationSelection (
54123 query : SelectQueryBuilder < any , any , any > ,
55124 model : string ,
@@ -301,4 +370,34 @@ export class SqliteCrudDialect<Schema extends SchemaDef> extends BaseCrudDialect
301370 override get supportInsertWithDefault ( ) {
302371 return false ;
303372 }
373+
374+ override getFieldSqlType ( fieldDef : FieldDef ) {
375+ // TODO: respect `@db.x` attributes
376+ if ( fieldDef . relation ) {
377+ throw new QueryError ( 'Cannot get SQL type of a relation field' ) ;
378+ }
379+ if ( fieldDef . array ) {
380+ throw new QueryError ( 'SQLite does not support scalar list type' ) ;
381+ }
382+
383+ if ( this . schema . enums ?. [ fieldDef . type ] ) {
384+ // enums are stored as text
385+ return 'text' ;
386+ }
387+
388+ return (
389+ match ( fieldDef . type )
390+ . with ( 'String' , ( ) => 'text' )
391+ . with ( 'Boolean' , ( ) => 'integer' )
392+ . with ( 'Int' , ( ) => 'integer' )
393+ . with ( 'BigInt' , ( ) => 'integer' )
394+ . with ( 'Float' , ( ) => 'real' )
395+ . with ( 'Decimal' , ( ) => 'decimal' )
396+ . with ( 'DateTime' , ( ) => 'numeric' )
397+ . with ( 'Bytes' , ( ) => 'blob' )
398+ . with ( 'Json' , ( ) => 'jsonb' )
399+ // fallback to text
400+ . otherwise ( ( ) => 'text' )
401+ ) ;
402+ }
304403}
0 commit comments