@@ -31,7 +31,9 @@ import {
3131 CIPHERSTASH_JSON_CODEC_ID ,
3232 CIPHERSTASH_STRING_CODEC_ID ,
3333 isCipherstashCodecId ,
34+ isCipherstashV3CodecId ,
3435} from '../extension-metadata/constants'
36+ import { type V3Index , v3CastAs } from '../v3/domain-map'
3537
3638/**
3739 * Structural shape of the subset of `contract.json` this derivation
@@ -98,7 +100,19 @@ export function deriveStackSchemas(
98100 const builders : Record < string , EncryptedColumn > = { }
99101 for ( const [ columnName , column ] of Object . entries ( columns ) ) {
100102 const codecId = column . codecId
101- if ( codecId == null || ! isCipherstashCodecId ( codecId ) ) continue
103+ if ( codecId == null || ! ( isCipherstashCodecId ( codecId ) || isCipherstashV3CodecId ( codecId ) ) ) continue
104+
105+ // v3 columns take a SINGLE index (one domain per column), carried as
106+ // `typeParams.index`; the v2 boolean-flag walk does not apply.
107+ if ( isCipherstashV3CodecId ( codecId ) ) {
108+ builders [ columnName ] = applyV3Index (
109+ encryptedColumn ( columnName ) . dataType ( v3CastAs ( 'text' ) ) ,
110+ column . typeParams ,
111+ tableName ,
112+ columnName ,
113+ )
114+ continue
115+ }
102116
103117 const dataType = CODEC_ID_TO_DATA_TYPE [ codecId ]
104118 builders [ columnName ] = applyTypeParams (
@@ -140,3 +154,33 @@ function applyTypeParams(
140154function isCipherstashFlag ( value : string ) : value is CipherstashFlag {
141155 return value in FLAG_DISPATCH
142156}
157+
158+ const V3_INDEX_VALUES = [ 'equality' , 'freeTextSearch' , 'orderAndRange' ] as const
159+
160+ function isV3Index ( value : unknown ) : value is V3Index {
161+ return typeof value === 'string' && ( V3_INDEX_VALUES as readonly string [ ] ) . includes ( value )
162+ }
163+
164+ /**
165+ * Apply a v3 column's single index. `typeParams.index` is `unknown` (the contract
166+ * view types typeParams as `Record<string, unknown> | null`), so narrow with
167+ * `isV3Index` before dispatching. A v3 column is exactly one domain ⇒ exactly one
168+ * index builder.
169+ */
170+ function applyV3Index (
171+ builder : EncryptedColumn ,
172+ typeParams : Readonly < Record < string , unknown > > | null | undefined ,
173+ tableName : string ,
174+ columnName : string ,
175+ ) : EncryptedColumn {
176+ const index = typeParams ?. [ 'index' ]
177+ if ( ! isV3Index ( index ) ) {
178+ throw new Error (
179+ `deriveStackSchemas: v3 column "${ tableName } "."${ columnName } " requires a valid 'index' typeParam ` +
180+ `(one of ${ V3_INDEX_VALUES . join ( ', ' ) } ), got ${ String ( index ) } .` ,
181+ )
182+ }
183+ if ( index === 'equality' ) return builder . equality ( )
184+ if ( index === 'freeTextSearch' ) return builder . freeTextSearch ( )
185+ return builder . orderAndRange ( )
186+ }
0 commit comments