@livestore/common 0.4.0-dev.0 → 0.4.0-dev.1

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.
@@ -1,13 +1,16 @@
1
- import { type Nullable, shouldNeverHappen, type Writeable } from '@livestore/utils'
2
- import { Option, Schema, SchemaAST, type Types } from '@livestore/utils/effect'
1
+ import { type Nullable, shouldNeverHappen } from '@livestore/utils'
2
+ import { Option, type Schema, SchemaAST, type Types } from '@livestore/utils/effect'
3
3
 
4
- import { AutoIncrement, ColumnType, Default, PrimaryKeyId, Unique } from './column-annotations.ts'
4
+ import { getColumnDefForSchema, schemaFieldsToColumns } from './column-def.ts'
5
5
  import { SqliteDsl } from './db-schema/mod.ts'
6
6
  import type { QueryBuilder } from './query-builder/mod.ts'
7
7
  import { makeQueryBuilder, QueryBuilderAstSymbol, QueryBuilderTypeId } from './query-builder/mod.ts'
8
8
 
9
9
  export const { blob, boolean, column, datetime, integer, isColumnDefinition, json, real, text } = SqliteDsl
10
10
 
11
+ // Re-export the column definition function
12
+ export { getColumnDefForSchema }
13
+
11
14
  export type StateType = 'singleton' | 'dynamic'
12
15
 
13
16
  export type DefaultSqliteTableDef = SqliteDsl.TableDefinition<string, SqliteDsl.Columns>
@@ -398,256 +401,3 @@ export declare namespace TableDefInput {
398
401
  SchemaToColumns.FromTypes<TType, TEncoded>
399
402
  >
400
403
  }
401
-
402
- /**
403
- * Checks if a property signature has a specific annotation, checking both
404
- * the property signature itself and its type AST.
405
- */
406
- const hasPropertyAnnotation = <T>(
407
- propertySignature: SchemaAST.PropertySignature,
408
- annotationId: symbol,
409
- ): Option.Option<T> => {
410
- // When using Schema.optional(Schema.String).pipe(withPrimaryKey) in a struct,
411
- // the annotation ends up on a PropertySignatureDeclaration, not the Union type
412
- // Check if this is a PropertySignatureDeclaration with annotations
413
- if ('annotations' in propertySignature && propertySignature.annotations) {
414
- const annotation = SchemaAST.getAnnotation<T>(annotationId)(propertySignature as any)
415
- if (Option.isSome(annotation)) {
416
- return annotation
417
- }
418
- }
419
-
420
- // Otherwise check the type AST
421
- return SchemaAST.getAnnotation<T>(annotationId)(propertySignature.type)
422
- }
423
-
424
- /**
425
- * Maps schema property signatures to SQLite column definitions.
426
- * Returns both columns and unique column names for index creation.
427
- */
428
- const schemaFieldsToColumns = (
429
- propertySignatures: ReadonlyArray<SchemaAST.PropertySignature>,
430
- ): { columns: SqliteDsl.Columns; uniqueColumns: string[] } => {
431
- const columns: SqliteDsl.Columns = {}
432
- const uniqueColumns: string[] = []
433
-
434
- for (const prop of propertySignatures) {
435
- if (typeof prop.name === 'string') {
436
- // Create a schema from the AST
437
- const fieldSchema = Schema.make(prop.type)
438
- // Check if property has primary key annotation
439
- const hasPrimaryKey = hasPropertyAnnotation<boolean>(prop, PrimaryKeyId).pipe(Option.getOrElse(() => false))
440
- // Check if property has unique annotation
441
- const hasUnique = hasPropertyAnnotation<boolean>(prop, Unique).pipe(Option.getOrElse(() => false))
442
-
443
- columns[prop.name] = schemaFieldToColumn(fieldSchema, prop, hasPrimaryKey)
444
-
445
- if (hasUnique) {
446
- uniqueColumns.push(prop.name)
447
- }
448
- }
449
- }
450
-
451
- return { columns, uniqueColumns }
452
- }
453
-
454
- /**
455
- * Converts a schema field and its property signature to a SQLite column definition.
456
- */
457
- const schemaFieldToColumn = (
458
- fieldSchema: Schema.Schema.AnyNoContext,
459
- propertySignature: SchemaAST.PropertySignature,
460
- forceHasPrimaryKey?: boolean,
461
- ): SqliteDsl.ColumnDefinition.Any => {
462
- // Determine column type based on schema type
463
- const columnDef = getColumnDefForSchema(fieldSchema, propertySignature)
464
-
465
- // Create a new object with appropriate properties
466
- const result: Writeable<SqliteDsl.ColumnDefinition.Any> = {
467
- columnType: columnDef.columnType,
468
- schema: columnDef.schema,
469
- default: columnDef.default,
470
- nullable: columnDef.nullable,
471
- primaryKey: columnDef.primaryKey,
472
- autoIncrement: columnDef.autoIncrement,
473
- }
474
-
475
- // Set primaryKey property explicitly
476
- if (forceHasPrimaryKey || columnDef.primaryKey) {
477
- result.primaryKey = true
478
- } else {
479
- result.primaryKey = false
480
- }
481
-
482
- // Check for invalid primary key + nullable combination
483
- if (result.primaryKey && (propertySignature.isOptional || columnDef.nullable)) {
484
- return shouldNeverHappen(
485
- `Primary key columns cannot be nullable. Found nullable primary key for column. ` +
486
- `Either remove the primary key annotation or use a non-nullable schema.`,
487
- )
488
- }
489
-
490
- // Set nullable property explicitly
491
- if (propertySignature.isOptional) {
492
- result.nullable = true
493
- } else if (columnDef.nullable) {
494
- result.nullable = true
495
- } else {
496
- result.nullable = false
497
- }
498
-
499
- // Only add autoIncrement if it's true
500
- if (columnDef.autoIncrement) {
501
- result.autoIncrement = true
502
- }
503
-
504
- return result as SqliteDsl.ColumnDefinition.Any
505
- }
506
-
507
- /**
508
- * Maps a schema to a SQLite column definition, respecting column annotations.
509
- */
510
- export const getColumnDefForSchema = (
511
- schema: Schema.Schema.AnyNoContext,
512
- propertySignature?: SchemaAST.PropertySignature,
513
- ): SqliteDsl.ColumnDefinition.Any => {
514
- const ast = schema.ast
515
-
516
- // Check for annotations
517
- const hasPrimaryKey = propertySignature
518
- ? hasPropertyAnnotation<boolean>(propertySignature, PrimaryKeyId).pipe(Option.getOrElse(() => false))
519
- : SchemaAST.getAnnotation<boolean>(PrimaryKeyId)(ast).pipe(Option.getOrElse(() => false))
520
-
521
- const hasAutoIncrement = propertySignature
522
- ? hasPropertyAnnotation<boolean>(propertySignature, AutoIncrement).pipe(Option.getOrElse(() => false))
523
- : SchemaAST.getAnnotation<boolean>(AutoIncrement)(ast).pipe(Option.getOrElse(() => false))
524
-
525
- const defaultValue = propertySignature
526
- ? hasPropertyAnnotation<unknown>(propertySignature, Default)
527
- : SchemaAST.getAnnotation<unknown>(Default)(ast)
528
-
529
- /** Adds annotations to a column definition if they are present. */
530
- const withAnnotationsIfNeeded = (columnDef: SqliteDsl.ColumnDefinition.Any): SqliteDsl.ColumnDefinition.Any => {
531
- const result = { ...columnDef }
532
-
533
- if (hasPrimaryKey) {
534
- result.primaryKey = true
535
- }
536
-
537
- if (hasAutoIncrement) {
538
- result.autoIncrement = true
539
- }
540
-
541
- if (Option.isSome(defaultValue)) {
542
- result.default = Option.some(defaultValue.value)
543
- }
544
-
545
- return result
546
- }
547
-
548
- // Check for custom column type annotation
549
- const columnTypeAnnotation = SchemaAST.getAnnotation<SqliteDsl.FieldColumnType>(ColumnType)(ast)
550
- if (Option.isSome(columnTypeAnnotation)) {
551
- const columnType = columnTypeAnnotation.value
552
- let columnDef: SqliteDsl.ColumnDefinition.Any
553
- switch (columnType) {
554
- case 'text':
555
- columnDef = SqliteDsl.text()
556
- break
557
- case 'integer':
558
- columnDef = SqliteDsl.integer()
559
- break
560
- case 'real':
561
- columnDef = SqliteDsl.real()
562
- break
563
- case 'blob':
564
- columnDef = SqliteDsl.blob()
565
- break
566
- default:
567
- return shouldNeverHappen(`Unsupported column type annotation: ${columnType}`)
568
- }
569
-
570
- return withAnnotationsIfNeeded(columnDef)
571
- }
572
-
573
- // Check for refinements (e.g., Schema.Int)
574
- if (SchemaAST.isRefinement(ast)) {
575
- // Check if this is specifically Schema.Int by looking at the identifier annotation
576
- const identifier = SchemaAST.getIdentifierAnnotation(ast).pipe(Option.getOrElse(() => ''))
577
- if (identifier === 'Int') {
578
- return withAnnotationsIfNeeded(SqliteDsl.integer())
579
- }
580
- // For other refinements, check the underlying type
581
- return getColumnDefForSchema(Schema.make(ast.from), propertySignature)
582
- }
583
-
584
- // Check for string types
585
- if (SchemaAST.isStringKeyword(ast)) {
586
- return withAnnotationsIfNeeded(SqliteDsl.text())
587
- }
588
-
589
- // Check for number types
590
- if (SchemaAST.isNumberKeyword(ast)) {
591
- return withAnnotationsIfNeeded(SqliteDsl.real())
592
- }
593
-
594
- // Check for boolean types
595
- if (SchemaAST.isBooleanKeyword(ast)) {
596
- return withAnnotationsIfNeeded(SqliteDsl.boolean())
597
- }
598
-
599
- // Check for unions (like optional or nullable)
600
- if (SchemaAST.isUnion(ast)) {
601
- // Check if this union contains null or undefined (making it nullable/optional)
602
- let hasNull = false
603
- let hasUndefined = false
604
- let nonNullableType: SchemaAST.AST | undefined
605
-
606
- for (const type of ast.types) {
607
- if (SchemaAST.isUndefinedKeyword(type)) {
608
- hasUndefined = true
609
- } else if (SchemaAST.isLiteral(type) && type.literal === null) {
610
- hasNull = true
611
- } else {
612
- nonNullableType = type
613
- }
614
- }
615
-
616
- // If we found a non-nullable type, use it for the column definition
617
- if (nonNullableType) {
618
- const innerSchema = Schema.make(nonNullableType)
619
- const innerColumnDef = getColumnDefForSchema(innerSchema, propertySignature)
620
-
621
- // If the union contains null or undefined, mark as nullable
622
- if (hasNull || hasUndefined) {
623
- return withAnnotationsIfNeeded({
624
- ...innerColumnDef,
625
- nullable: true,
626
- })
627
- }
628
-
629
- return withAnnotationsIfNeeded(innerColumnDef)
630
- }
631
- }
632
-
633
- // Check for Date types
634
- if (SchemaAST.isTransformation(ast)) {
635
- // Try to map the transformation's target type
636
- return getColumnDefForSchema(Schema.make(ast.to), propertySignature)
637
- }
638
-
639
- // Check for literal types
640
- if (SchemaAST.isLiteral(ast)) {
641
- const value = ast.literal
642
- if (typeof value === 'string') {
643
- return withAnnotationsIfNeeded(SqliteDsl.text())
644
- } else if (typeof value === 'number') {
645
- return withAnnotationsIfNeeded(SqliteDsl.real())
646
- } else if (typeof value === 'boolean') {
647
- return withAnnotationsIfNeeded(SqliteDsl.boolean())
648
- }
649
- }
650
-
651
- // Default to JSON column for complex types
652
- return withAnnotationsIfNeeded(SqliteDsl.json({ schema }))
653
- }
package/src/version.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  // import packageJson from '../package.json' with { type: 'json' }
3
3
  // export const liveStoreVersion = packageJson.version
4
4
 
5
- export const liveStoreVersion = '0.4.0-dev.0' as const
5
+ export const liveStoreVersion = '0.4.0-dev.1' as const
6
6
 
7
7
  /**
8
8
  * This version number is incremented whenever the internal storage format changes in a breaking way.