@prisma-next/sql-contract-ts 0.5.0-dev.9 → 0.5.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,15 +1,14 @@
1
1
  import type {
2
2
  ColumnDefault,
3
3
  ColumnDefaultLiteralInputValue,
4
+ ExecutionMutationDefaultPhases,
4
5
  ExecutionMutationDefaultValue,
5
6
  } from '@prisma-next/contract/types';
6
- import type {
7
- ColumnTypeDescriptor,
8
- ForeignKeyDefaultsState,
9
- } from '@prisma-next/contract-authoring';
7
+ import { isColumnDefault } from '@prisma-next/contract/types';
8
+ import type { ForeignKeyDefaultsState } from '@prisma-next/contract-authoring';
10
9
  import type { AuthoringFieldPresetDescriptor } from '@prisma-next/framework-components/authoring';
11
10
  import { instantiateAuthoringFieldPreset } from '@prisma-next/framework-components/authoring';
12
- import type { CodecLookup } from '@prisma-next/framework-components/codec';
11
+ import type { CodecLookup, ColumnTypeDescriptor } from '@prisma-next/framework-components/codec';
13
12
  import type {
14
13
  ExtensionPackRef,
15
14
  FamilyPackRef,
@@ -46,7 +45,7 @@ export type ScalarFieldState<
46
45
  readonly nullable: Nullable;
47
46
  readonly columnName?: ColumnName | undefined;
48
47
  readonly default?: ColumnDefault | undefined;
49
- readonly executionDefault?: ExecutionMutationDefaultValue | undefined;
48
+ readonly executionDefaults?: ExecutionMutationDefaultPhases | undefined;
50
49
  } & (IdSpec extends NamedConstraintSpec ? { readonly id: IdSpec } : { readonly id?: undefined }) &
51
50
  (UniqueSpec extends NamedConstraintSpec
52
51
  ? { readonly unique: UniqueSpec }
@@ -59,7 +58,7 @@ type AnyScalarFieldState = {
59
58
  readonly nullable: boolean;
60
59
  readonly columnName?: string | undefined;
61
60
  readonly default?: ColumnDefault | undefined;
62
- readonly executionDefault?: ExecutionMutationDefaultValue | undefined;
61
+ readonly executionDefaults?: ExecutionMutationDefaultPhases | undefined;
63
62
  readonly id?: NamedConstraintSpec | undefined;
64
63
  readonly unique?: NamedConstraintSpec | undefined;
65
64
  };
@@ -136,12 +135,6 @@ export type GeneratedFieldSpec = {
136
135
  readonly generated: ExecutionMutationDefaultValue;
137
136
  };
138
137
 
139
- function isColumnDefault(value: unknown): value is ColumnDefault {
140
- if (typeof value !== 'object' || value === null) return false;
141
- const kind = (value as { kind?: unknown }).kind;
142
- return kind === 'literal' || kind === 'function';
143
- }
144
-
145
138
  function toColumnDefault(value: ColumnDefaultLiteralInputValue | ColumnDefault): ColumnDefault {
146
139
  if (isColumnDefault(value)) {
147
140
  return value;
@@ -149,10 +142,7 @@ function toColumnDefault(value: ColumnDefaultLiteralInputValue | ColumnDefault):
149
142
  return { kind: 'literal', value };
150
143
  }
151
144
 
152
- // Chaining methods use `as unknown as <ConditionalType>` because TypeScript cannot
153
- // narrow generic conditional return types through object spread. The runtime values
154
- // are correct — the casts bridge the gap between the spread result and the
155
- // compile-time conditional type that encodes the state transition.
145
+ // Chaining methods use `as unknown as <ConditionalType>` because TypeScript cannot narrow generic conditional return types through object spread. The runtime values are correct — the casts bridge the gap between the spread result and the compile-time conditional type that encodes the state transition.
156
146
  export class ScalarFieldBuilder<State extends AnyScalarFieldState = AnyScalarFieldState> {
157
147
  declare readonly __state: State;
158
148
 
@@ -348,7 +338,7 @@ function generatedField<Descriptor extends ColumnTypeDescriptor>(
348
338
  ...(spec.typeParams ? { typeParams: spec.typeParams } : {}),
349
339
  },
350
340
  nullable: false,
351
- executionDefault: spec.generated,
341
+ executionDefaults: { onCreate: spec.generated },
352
342
  });
353
343
  }
354
344
 
@@ -379,11 +369,8 @@ export function buildFieldPreset(
379
369
  kind: 'scalar',
380
370
  descriptor: preset.descriptor,
381
371
  nullable: preset.nullable,
382
- ...ifDefined('default', preset.default as ColumnDefault | undefined),
383
- ...ifDefined(
384
- 'executionDefault',
385
- preset.executionDefault as ExecutionMutationDefaultValue | undefined,
386
- ),
372
+ ...ifDefined('default', preset.default),
373
+ ...ifDefined('executionDefaults', preset.executionDefaults),
387
374
  ...(preset.id
388
375
  ? {
389
376
  id: namedConstraintOptions?.name ? { name: namedConstraintOptions.name } : {},
@@ -478,7 +465,7 @@ export type RelationState =
478
465
  | ManyToManyRelation;
479
466
 
480
467
  type AnyRelationState = RelationState;
481
- type AnyRelationBuilder = RelationBuilder<AnyRelationState>;
468
+ export type AnyRelationBuilder = RelationBuilder<AnyRelationState>;
482
469
 
483
470
  type ApplyBelongsToRelationSqlSpec<
484
471
  State extends RelationState,
@@ -550,11 +537,21 @@ type ConstraintOptions<Name extends string | undefined = string | undefined> = {
550
537
  readonly name?: Name;
551
538
  };
552
539
 
553
- type IndexOptions<Name extends string | undefined = string | undefined> =
554
- ConstraintOptions<Name> & {
555
- readonly using?: string;
556
- readonly config?: Record<string, unknown>;
557
- };
540
+ export type IndexTypeMap = Record<string, { readonly options: unknown }>;
541
+
542
+ type IndexInput<
543
+ Name extends string | undefined,
544
+ IndexTypes extends IndexTypeMap,
545
+ > = keyof IndexTypes extends never
546
+ ? ConstraintOptions<Name>
547
+ :
548
+ | (ConstraintOptions<Name> & { readonly type?: never; readonly options?: never })
549
+ | {
550
+ readonly [K in keyof IndexTypes & string]: ConstraintOptions<Name> & {
551
+ readonly type: K;
552
+ readonly options: IndexTypes[K]['options'];
553
+ };
554
+ }[keyof IndexTypes & string];
558
555
 
559
556
  type ForeignKeyOptions<Name extends string | undefined = string | undefined> =
560
557
  ConstraintOptions<Name> & {
@@ -590,8 +587,8 @@ export type IndexConstraint<
590
587
  readonly kind: 'index';
591
588
  readonly fields: FieldNames;
592
589
  readonly name?: Name;
593
- readonly using?: string;
594
- readonly config?: Record<string, unknown>;
590
+ readonly type?: string;
591
+ readonly options?: Record<string, unknown>;
595
592
  };
596
593
 
597
594
  export type ForeignKeyConstraint<
@@ -636,7 +633,7 @@ function normalizeTargetFieldRefInput(input: TargetFieldRef | readonly TargetFie
636
633
  };
637
634
  }
638
635
 
639
- function createConstraintsDsl() {
636
+ function createConstraintsDsl<IndexTypes extends IndexTypeMap = Record<never, never>>() {
640
637
  function ref<ModelName extends string, FieldName extends string>(
641
638
  modelName: ModelName,
642
639
  fieldName: FieldName,
@@ -687,24 +684,26 @@ function createConstraintsDsl() {
687
684
  };
688
685
  }
689
686
 
690
- function index<FieldName extends string, Name extends string | undefined = undefined>(
691
- field: ColumnRef<FieldName>,
692
- options?: IndexOptions<Name>,
693
- ): IndexConstraint<readonly [FieldName], Name>;
694
687
  function index<FieldNames extends readonly string[], Name extends string | undefined = undefined>(
695
688
  fields: { readonly [K in keyof FieldNames]: ColumnRef<FieldNames[K] & string> },
696
- options?: IndexOptions<Name>,
689
+ options?: IndexInput<Name, IndexTypes>,
697
690
  ): IndexConstraint<FieldNames, Name>;
698
691
  function index(
699
- fieldOrFields: ColumnRef | readonly ColumnRef[],
700
- options?: IndexOptions,
692
+ fields: readonly ColumnRef[],
693
+ options?: {
694
+ readonly name?: string;
695
+ readonly type?: string;
696
+ readonly options?: unknown;
697
+ },
701
698
  ): IndexConstraint {
702
699
  return {
703
700
  kind: 'index',
704
- fields: normalizeFieldRefInput(fieldOrFields),
705
- ...(options?.name ? { name: options.name } : {}),
706
- ...(options?.using ? { using: options.using } : {}),
707
- ...(options?.config ? { config: options.config } : {}),
701
+ fields: normalizeFieldRefInput(fields),
702
+ ...(options?.name !== undefined ? { name: options.name } : {}),
703
+ ...(options?.type !== undefined ? { type: options.type } : {}),
704
+ ...(options?.options !== undefined
705
+ ? { options: options.options as Record<string, unknown> }
706
+ : {}),
708
707
  };
709
708
  }
710
709
 
@@ -789,9 +788,26 @@ type AttributeContext<Fields extends Record<string, ScalarFieldBuilder>> = {
789
788
  readonly constraints: Pick<ConstraintsDsl, 'id' | 'unique'>;
790
789
  };
791
790
 
792
- type SqlContext<Fields extends Record<string, ScalarFieldBuilder>> = {
791
+ type PackAwareIndex<IndexTypes extends IndexTypeMap> = <
792
+ FieldNames extends readonly string[],
793
+ Name extends string | undefined = undefined,
794
+ >(
795
+ fields: { readonly [K in keyof FieldNames]: ColumnRef<FieldNames[K] & string> },
796
+ options?: IndexInput<Name, IndexTypes>,
797
+ ) => IndexConstraint<FieldNames, Name>;
798
+
799
+ type PackAwareSqlConstraints<IndexTypes extends IndexTypeMap> = {
800
+ readonly foreignKey: ConstraintsDsl['foreignKey'];
801
+ readonly ref: ConstraintsDsl['ref'];
802
+ readonly index: PackAwareIndex<IndexTypes>;
803
+ };
804
+
805
+ export type SqlContext<
806
+ Fields extends Record<string, ScalarFieldBuilder>,
807
+ IndexTypes extends IndexTypeMap = Record<never, never>,
808
+ > = {
793
809
  readonly cols: FieldRefs<Fields>;
794
- readonly constraints: Pick<ConstraintsDsl, 'index' | 'foreignKey' | 'ref'>;
810
+ readonly constraints: PackAwareSqlConstraints<IndexTypes>;
795
811
  };
796
812
 
797
813
  function createFieldRefs<Fields extends Record<string, ScalarFieldBuilder>>(
@@ -842,8 +858,10 @@ function createAttributeConstraintsDsl(): AttributeContext<
842
858
  };
843
859
  }
844
860
 
845
- function createSqlConstraintsDsl(): SqlContext<Record<string, ScalarFieldBuilder>>['constraints'] {
846
- const constraints = createConstraintsDsl();
861
+ function createSqlConstraintsDsl<
862
+ IndexTypes extends IndexTypeMap = Record<never, never>,
863
+ >(): SqlContext<Record<string, ScalarFieldBuilder>, IndexTypes>['constraints'] {
864
+ const constraints = createConstraintsDsl<IndexTypes>();
847
865
  return {
848
866
  index: constraints.index,
849
867
  foreignKey: constraints.foreignKey,
@@ -959,12 +977,14 @@ export class ContractModelBuilder<
959
977
  Relations extends Record<string, AnyRelationBuilder> = Record<never, never>,
960
978
  AttributesSpec extends ModelAttributesSpec | undefined = undefined,
961
979
  SqlSpec extends SqlStageSpec | undefined = undefined,
980
+ IndexTypes extends IndexTypeMap = Record<never, never>,
962
981
  > {
963
982
  declare readonly __name: ModelName;
964
983
  declare readonly __fields: Fields;
965
984
  declare readonly __relations: Relations;
966
985
  declare readonly __attributes: AttributesSpec;
967
986
  declare readonly __sql: SqlSpec;
987
+ declare readonly __indexTypes: IndexTypes;
968
988
  readonly refs: ModelName extends string ? ModelTokenRefs<ModelName, Fields> : never;
969
989
 
970
990
  constructor(
@@ -974,7 +994,7 @@ export class ContractModelBuilder<
974
994
  readonly relations: Relations;
975
995
  },
976
996
  readonly attributesFactory?: StageInput<AttributeContext<Fields>, AttributesSpec>,
977
- readonly sqlFactory?: StageInput<SqlContext<Fields>, SqlSpec>,
997
+ readonly sqlFactory?: StageInput<SqlContext<Fields, IndexTypes>, SqlSpec>,
978
998
  ) {
979
999
  this.refs = (
980
1000
  stageOne.modelName ? createModelTokenRefs(stageOne.modelName, stageOne.fields) : undefined
@@ -983,7 +1003,7 @@ export class ContractModelBuilder<
983
1003
 
984
1004
  ref<FieldName extends keyof Fields & string>(
985
1005
  this: ModelName extends string
986
- ? ContractModelBuilder<ModelName, Fields, Relations, AttributesSpec, SqlSpec>
1006
+ ? ContractModelBuilder<ModelName, Fields, Relations, AttributesSpec, SqlSpec, IndexTypes>
987
1007
  : never,
988
1008
  fieldName: FieldName,
989
1009
  ): TargetFieldRef<ModelName & string, FieldName> {
@@ -1002,7 +1022,14 @@ export class ContractModelBuilder<
1002
1022
 
1003
1023
  relations<const NextRelations extends Record<string, AnyRelationBuilder>>(
1004
1024
  relations: NextRelations,
1005
- ): ContractModelBuilder<ModelName, Fields, Relations & NextRelations, AttributesSpec, SqlSpec> {
1025
+ ): ContractModelBuilder<
1026
+ ModelName,
1027
+ Fields,
1028
+ Relations & NextRelations,
1029
+ AttributesSpec,
1030
+ SqlSpec,
1031
+ IndexTypes
1032
+ > {
1006
1033
  const duplicateRelationName = findDuplicateRelationName(this.stageOne.relations, relations);
1007
1034
  if (duplicateRelationName) {
1008
1035
  throw new Error(
@@ -1028,18 +1055,16 @@ export class ContractModelBuilder<
1028
1055
  AttributeContext<Fields>,
1029
1056
  ValidateAttributesStageSpec<Fields, SqlSpec, NextAttributesSpec>
1030
1057
  >,
1031
- ): ContractModelBuilder<ModelName, Fields, Relations, NextAttributesSpec, SqlSpec> {
1058
+ ): ContractModelBuilder<ModelName, Fields, Relations, NextAttributesSpec, SqlSpec, IndexTypes> {
1032
1059
  return new ContractModelBuilder(this.stageOne, specOrFactory, this.sqlFactory);
1033
1060
  }
1034
1061
 
1035
1062
  sql<const NextSqlSpec extends SqlStageSpec>(
1036
- specOrFactory: StageInput<SqlContext<Fields>, NextSqlSpec>,
1063
+ specOrFactory: StageInput<SqlContext<Fields, IndexTypes>, NextSqlSpec>,
1037
1064
  ): [ValidateSqlStageSpec<Fields, AttributesSpec, NextSqlSpec>] extends [never]
1038
- ? ContractModelBuilder<ModelName, Fields, Relations, AttributesSpec, never>
1039
- : ContractModelBuilder<ModelName, Fields, Relations, AttributesSpec, NextSqlSpec> {
1040
- // Conditional return type cannot be verified by the implementation; the
1041
- // runtime value is always a valid ContractModelBuilder regardless of the
1042
- // validation outcome (validation is type-level only).
1065
+ ? ContractModelBuilder<ModelName, Fields, Relations, AttributesSpec, never, IndexTypes>
1066
+ : ContractModelBuilder<ModelName, Fields, Relations, AttributesSpec, NextSqlSpec, IndexTypes> {
1067
+ // Conditional return type cannot be verified by the implementation; the runtime value is always a valid ContractModelBuilder regardless of the validation outcome (validation is type-level only).
1043
1068
  return new ContractModelBuilder(this.stageOne, this.attributesFactory, specOrFactory) as never;
1044
1069
  }
1045
1070
 
@@ -1060,7 +1085,7 @@ export class ContractModelBuilder<
1060
1085
  }
1061
1086
  return buildStageSpec(this.sqlFactory, {
1062
1087
  cols: createColumnRefs(this.stageOne.fields),
1063
- constraints: createSqlConstraintsDsl() as SqlContext<Fields>['constraints'],
1088
+ constraints: createSqlConstraintsDsl<IndexTypes>(),
1064
1089
  });
1065
1090
  }
1066
1091
  }
@@ -1,5 +1,6 @@
1
- import type { ColumnTypeDescriptor } from '@prisma-next/contract-authoring';
1
+ import type { ColumnTypeDescriptor } from '@prisma-next/framework-components/codec';
2
2
  import type { StorageTypeInstance } from '@prisma-next/sql-contract/types';
3
+ import { ifDefined } from '@prisma-next/utils/defined';
3
4
  import type {
4
5
  ContractDefinition,
5
6
  FieldNode,
@@ -562,7 +563,7 @@ function resolveModelNode(
562
563
  descriptor,
563
564
  nullable: fieldState.nullable,
564
565
  ...(fieldState.default ? { default: fieldState.default } : {}),
565
- ...(fieldState.executionDefault ? { executionDefault: fieldState.executionDefault } : {}),
566
+ ...(fieldState.executionDefaults ? { executionDefaults: fieldState.executionDefaults } : {}),
566
567
  });
567
568
  }
568
569
 
@@ -573,9 +574,9 @@ function resolveModelNode(
573
574
  })) satisfies readonly UniqueConstraintNode[];
574
575
  const indexes = (spec.sqlSpec?.indexes ?? []).map((index) => ({
575
576
  columns: mapFieldNamesToColumnNames(spec.modelName, index.fields, spec.fieldToColumn),
576
- ...(index.name ? { name: index.name } : {}),
577
- ...(index.using ? { using: index.using } : {}),
578
- ...(index.config ? { config: index.config } : {}),
577
+ ...ifDefined('name', index.name),
578
+ ...ifDefined('type', index.type),
579
+ ...ifDefined('options', index.options),
579
580
  })) satisfies readonly IndexNode[];
580
581
  const foreignKeys = resolveForeignKeyNodes(spec, allSpecs);
581
582
  const relations = Object.entries(spec.relations).map(([relationName, relationBuilder]) =>
@@ -5,6 +5,7 @@ import type {
5
5
  StorageHashBase,
6
6
  } from '@prisma-next/contract/types';
7
7
  import type { ExtensionPackRef, TargetPackRef } from '@prisma-next/framework-components/components';
8
+ import type { IndexTypeRegistration } from '@prisma-next/sql-contract/index-types';
8
9
  import type {
9
10
  ContractWithTypeMaps,
10
11
  Index,
@@ -34,6 +35,28 @@ type MergeExtensionCodecTypesSafe<Packs> =
34
35
  : MergeExtensionCodecTypes<Packs>
35
36
  : Record<string, never>;
36
37
 
38
+ export type ExtractIndexTypesFromPack<P> = P extends {
39
+ readonly indexTypes: IndexTypeRegistration<infer M>;
40
+ }
41
+ ? M
42
+ : Record<never, never>;
43
+
44
+ type AllIndexTypeLiterals<Packs> =
45
+ Packs extends Record<string, unknown>
46
+ ? { [K in keyof Packs]: keyof ExtractIndexTypesFromPack<Packs[K]> }[keyof Packs] & string
47
+ : never;
48
+
49
+ export type MergeExtensionIndexTypes<Packs extends Record<string, unknown>> = {
50
+ readonly [Lit in AllIndexTypeLiterals<Packs>]: Extract<
51
+ {
52
+ [K in keyof Packs]: Lit extends keyof ExtractIndexTypesFromPack<Packs[K]>
53
+ ? ExtractIndexTypesFromPack<Packs[K]>[Lit]
54
+ : never;
55
+ }[keyof Packs],
56
+ { readonly options: unknown }
57
+ >;
58
+ };
59
+
37
60
  export type MergeExtensionPackRefs<
38
61
  Existing extends Record<string, unknown> | undefined,
39
62
  Added extends Record<string, ExtensionPackRef<'sql', string>>,
@@ -64,6 +87,16 @@ type CodecTypesFromDefinition<Definition> = ExtractCodecTypesFromPack<
64
87
  > &
65
88
  MergeExtensionCodecTypesSafe<DefinitionExtensionPacks<Definition>>;
66
89
 
90
+ type DefinitionTarget<Definition> = Definition extends { readonly target: infer Target }
91
+ ? Target
92
+ : never;
93
+
94
+ type AllPacks<Definition> = DefinitionExtensionPacks<Definition> & {
95
+ readonly __target: DefinitionTarget<Definition>;
96
+ };
97
+
98
+ export type IndexTypesFromDefinition<Definition> = MergeExtensionIndexTypes<AllPacks<Definition>>;
99
+
67
100
  type DefinitionModels<Definition> = Definition extends {
68
101
  readonly models?: unknown;
69
102
  }