@prisma-next/mongo-orm 0.16.0-dev.3 → 0.16.0-dev.31

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.
package/src/collection.ts CHANGED
@@ -32,6 +32,7 @@ import {
32
32
  } from '@prisma-next/mongo-query-ast/execution';
33
33
  import type { MongoValue } from '@prisma-next/mongo-value';
34
34
  import { MongoParamRef } from '@prisma-next/mongo-value';
35
+ import { blindCast, castAs } from '@prisma-next/utils/casts';
35
36
  import { InternalError } from '@prisma-next/utils/internal-error';
36
37
  import type { MongoIncludeExpr } from './collection-state';
37
38
  import { emptyCollectionState, type MongoCollectionState } from './collection-state';
@@ -105,7 +106,7 @@ export interface MongoCollection<
105
106
  data: ReadonlyArray<ResolvedCreateInput<TContract, ModelName, TVariant>>,
106
107
  ): AsyncIterableResult<IncludedRow<TContract, ModelName, TIncludes>>;
107
108
  /** Inserts multiple documents and returns the number inserted. */
108
- createCount(
109
+ createAndCount(
109
110
  data: ReadonlyArray<ResolvedCreateInput<TContract, ModelName, TVariant>>,
110
111
  ): Promise<number>;
111
112
  /** Updates one matching document via `findOneAndUpdate`. Returns the updated document or `null`. Requires `.where()`. */
@@ -125,9 +126,9 @@ export interface MongoCollection<
125
126
  callback: (u: FieldAccessor<TContract, ModelName>) => FieldOperation[],
126
127
  ): AsyncIterableResult<IncludedRow<TContract, ModelName, TIncludes>>;
127
128
  /** Updates all matching documents and returns the number modified. Requires `.where()`. */
128
- updateCount(data: Partial<DefaultModelRow<TContract, ModelName>>): Promise<number>;
129
+ updateAndCount(data: Partial<DefaultModelRow<TContract, ModelName>>): Promise<number>;
129
130
  /** Updates all matching documents using field operations and returns the number modified. Requires `.where()`. */
130
- updateCount(
131
+ updateAndCount(
131
132
  callback: (u: FieldAccessor<TContract, ModelName>) => FieldOperation[],
132
133
  ): Promise<number>;
133
134
  /** Deletes one matching document via `findOneAndDelete`. Returns the deleted document or `null`. Requires `.where()`. */
@@ -135,7 +136,7 @@ export interface MongoCollection<
135
136
  /** Non-atomic: reads matching docs then deletes them. Concurrent writes may cause stale results. Requires `.where()`. */
136
137
  deleteAll(): AsyncIterableResult<IncludedRow<TContract, ModelName, TIncludes>>;
137
138
  /** Deletes all matching documents and returns the number deleted. Requires `.where()`. */
138
- deleteCount(): Promise<number>;
139
+ deleteAndCount(): Promise<number>;
139
140
  /**
140
141
  * On insert: `update` fields are applied via `$set`, remaining `create` fields via `$setOnInsert`.
141
142
  * This means `update` values take precedence over `create` for overlapping fields on insert.
@@ -156,6 +157,10 @@ function resolveCollectionName(model: MongoModelDefinition, modelName: string):
156
157
  return model.storage.collection ?? modelName;
157
158
  }
158
159
 
160
+ function isUnknownRecord(value: unknown): value is Record<string, unknown> {
161
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
162
+ }
163
+
159
164
  class MongoCollectionImpl<
160
165
  TContract extends MongoContractWithTypeMaps<MongoContract, AnyMongoTypeMaps>,
161
166
  ModelName extends string & keyof MongoModelsMap<TContract>,
@@ -174,9 +179,10 @@ class MongoCollectionImpl<
174
179
  this.#contract = contract;
175
180
  this.#modelName = modelName;
176
181
  this.#executor = executor;
177
- const model = domainModelsAtDefaultNamespace(contract.domain)[
178
- modelName
179
- ] as MongoModelDefinition;
182
+ const model = blindCast<
183
+ MongoModelDefinition,
184
+ 'modelName is constrained to Mongo contract model keys but namespace lookup erases storage type'
185
+ >(domainModelsAtDefaultNamespace(contract.domain)[modelName]);
180
186
  this.#collectionName = resolveCollectionName(model, modelName);
181
187
  this.#state = emptyCollectionState();
182
188
  }
@@ -184,29 +190,33 @@ class MongoCollectionImpl<
184
190
  variant<V extends VariantNames<TContract, ModelName>>(
185
191
  variantName: V,
186
192
  ): MongoCollection<TContract, ModelName, TIncludes, V> {
187
- const model = domainModelsAtDefaultNamespace(this.#contract.domain)[this.#modelName] as
188
- | MongoModelDefinition
189
- | undefined;
193
+ const model = blindCast<
194
+ MongoModelDefinition | undefined,
195
+ 'Mongo contract model lookup preserves target storage metadata erased by the namespace helper'
196
+ >(domainModelsAtDefaultNamespace(this.#contract.domain)[this.#modelName]);
190
197
  if (!model?.discriminator || !model.variants) {
191
198
  // No polymorphism metadata on this model — return unchanged. Cast required
192
199
  // because TS cannot verify TVariant (the current variant) is assignable to V.
193
- return this as unknown as MongoCollection<TContract, ModelName, TIncludes, V>;
200
+ return blindCast<
201
+ MongoCollection<TContract, ModelName, TIncludes, V>,
202
+ 'no-op variant refinement preserves runtime state while changing only the type-level variant'
203
+ >(this);
194
204
  }
195
205
 
196
- const variantEntry = model.variants[variantName as string];
206
+ const variantEntry = model.variants[variantName];
197
207
  if (!variantEntry) {
198
208
  // Unknown variant name at runtime — return unchanged. Same cast rationale.
199
- return this as unknown as MongoCollection<TContract, ModelName, TIncludes, V>;
209
+ return blindCast<
210
+ MongoCollection<TContract, ModelName, TIncludes, V>,
211
+ 'unknown variant fallback preserves runtime state while changing only the type-level variant'
212
+ >(this);
200
213
  }
201
214
 
202
215
  const filter = MongoFieldFilter.eq(
203
216
  model.discriminator.field,
204
217
  new MongoParamRef(variantEntry.value),
205
218
  );
206
- return this.#cloneWithVariant<V>(
207
- { filters: [...this.#state.filters, filter] },
208
- variantName as string,
209
- );
219
+ return this.#cloneWithVariant<V>({ filters: [...this.#state.filters, filter] }, variantName);
210
220
  }
211
221
 
212
222
  where(
@@ -215,7 +225,12 @@ class MongoCollectionImpl<
215
225
  if (isMongoFilterExpr(filter)) {
216
226
  return this.#clone({ filters: [...this.#state.filters, filter] });
217
227
  }
218
- const compiled = this.#compileWhereObject(filter as Record<string, unknown>);
228
+ const compiled = this.#compileWhereObject(
229
+ blindCast<
230
+ Record<string, unknown>,
231
+ 'typed Mongo where input is a model-field value record after filter-expression narrowing'
232
+ >(filter),
233
+ );
219
234
  return this.#clone({ filters: [...this.#state.filters, ...compiled] });
220
235
  }
221
236
 
@@ -228,14 +243,15 @@ class MongoCollectionImpl<
228
243
  include<K extends ReferenceRelationKeys<TContract, ModelName> & string>(
229
244
  relationName: K,
230
245
  ): MongoCollection<TContract, ModelName, TIncludes & Record<K, true>, TVariant> {
231
- const model = domainModelsAtDefaultNamespace(this.#contract.domain)[
232
- this.#modelName
233
- ] as MongoModelDefinition;
246
+ const model = blindCast<
247
+ MongoModelDefinition,
248
+ 'modelName is constrained to Mongo contract model keys but namespace lookup erases storage type'
249
+ >(domainModelsAtDefaultNamespace(this.#contract.domain)[this.#modelName]);
234
250
  const relation = model.relations?.[relationName];
235
251
  if (!relation) {
236
252
  throw ormError(
237
253
  'ORM.RELATION_UNKNOWN',
238
- `Unknown relation "${relationName}" on model "${this.#modelName as string}"`,
254
+ `Unknown relation "${relationName}" on model "${this.#modelName}"`,
239
255
  { meta: { model: this.#modelName, relation: relationName } },
240
256
  );
241
257
  }
@@ -248,7 +264,7 @@ class MongoCollectionImpl<
248
264
  );
249
265
  }
250
266
 
251
- const ref = relation as ContractReferenceRelation;
267
+ const ref: ContractReferenceRelation = relation;
252
268
  const localField = ref.on.localFields[0];
253
269
  const foreignField = ref.on.targetFields[0];
254
270
  if (
@@ -265,9 +281,9 @@ class MongoCollectionImpl<
265
281
  }
266
282
 
267
283
  const targetModelName = ref.to.model;
268
- const targetModel = domainModelsAtDefaultNamespace(this.#contract.domain)[targetModelName] as
269
- | MongoModelDefinition
270
- | undefined;
284
+ const targetModel = castAs<MongoModelDefinition | undefined>(
285
+ domainModelsAtDefaultNamespace(this.#contract.domain)[targetModelName],
286
+ );
271
287
  if (!targetModel) {
272
288
  throw new InternalError(
273
289
  `Target model "${targetModelName}" not found for relation "${relationName}"`,
@@ -282,20 +298,20 @@ class MongoCollectionImpl<
282
298
  cardinality: ref.cardinality,
283
299
  };
284
300
 
285
- return this.#clone({
286
- includes: [...this.#state.includes, includeExpr],
287
- }) as unknown as MongoCollectionImpl<
288
- TContract,
289
- ModelName,
290
- TIncludes & Record<K, true>,
291
- TVariant
292
- >;
301
+ return blindCast<
302
+ MongoCollection<TContract, ModelName, TIncludes & Record<K, true>, TVariant>,
303
+ 'include clone state contains the appended relation but the generic include refinement is not inferred'
304
+ >(
305
+ this.#clone({
306
+ includes: [...this.#state.includes, includeExpr],
307
+ }),
308
+ );
293
309
  }
294
310
 
295
311
  orderBy(
296
312
  spec: Partial<Record<ModelFieldKeys<TContract, ModelName>, 1 | -1>>,
297
313
  ): MongoCollection<TContract, ModelName, TIncludes, TVariant> {
298
- const merged = { ...this.#state.orderBy, ...(spec as Readonly<Record<string, 1 | -1>>) };
314
+ const merged: Readonly<Record<string, 1 | -1>> = { ...this.#state.orderBy, ...spec };
299
315
  return this.#clone({ orderBy: merged });
300
316
  }
301
317
 
@@ -325,17 +341,24 @@ class MongoCollectionImpl<
325
341
  ): Promise<IncludedRow<TContract, ModelName, TIncludes>> {
326
342
  this.#rejectIncludes('create');
327
343
  const normalized = this.#injectDiscriminator(
328
- this.#stripUndefined(data as Record<string, unknown>),
344
+ this.#stripUndefined(
345
+ blindCast<
346
+ Record<string, unknown>,
347
+ 'resolved Mongo create input is a model-field value record'
348
+ >(data),
349
+ ),
329
350
  );
330
351
  const document = this.#toDocument(normalized);
331
352
  const command = new InsertOneCommand(this.#collectionName, document);
332
353
  const results = await this.#drainPlan(command);
333
- const insertedId = (results[0] as { insertedId: unknown }).insertedId;
334
- return { _id: insertedId, ...normalized } as unknown as IncludedRow<
335
- TContract,
336
- ModelName,
337
- TIncludes
338
- >;
354
+ const insertedId = blindCast<
355
+ { insertedId: unknown },
356
+ 'InsertOneCommand runtime result exposes the server-assigned insertedId'
357
+ >(results[0]).insertedId;
358
+ return blindCast<
359
+ IncludedRow<TContract, ModelName, TIncludes>,
360
+ 'created row combines resolved model input with the server-assigned _id'
361
+ >({ _id: insertedId, ...normalized });
339
362
  }
340
363
 
341
364
  createAll(
@@ -345,33 +368,52 @@ class MongoCollectionImpl<
345
368
  const self = this;
346
369
  async function* gen(): AsyncGenerator<IncludedRow<TContract, ModelName, TIncludes>> {
347
370
  const normalizedRows = data.map((d) =>
348
- self.#injectDiscriminator(self.#stripUndefined(d as Record<string, unknown>)),
371
+ self.#injectDiscriminator(
372
+ self.#stripUndefined(
373
+ blindCast<
374
+ Record<string, unknown>,
375
+ 'resolved Mongo create-all input is a model-field value record'
376
+ >(d),
377
+ ),
378
+ ),
349
379
  );
350
380
  const documents = normalizedRows.map((d) => self.#toDocument(d));
351
381
  const command = new InsertManyCommand(self.#collectionName, documents);
352
382
  const results = await self.#drainPlan(command);
353
- const insertedIds = (results[0] as { insertedIds: readonly unknown[] }).insertedIds;
383
+ const insertedIds = blindCast<
384
+ { insertedIds: readonly unknown[] },
385
+ 'InsertManyCommand runtime result exposes insertedIds in input order'
386
+ >(results[0]).insertedIds;
354
387
  for (let i = 0; i < normalizedRows.length; i++) {
355
- yield { _id: insertedIds[i], ...normalizedRows[i] } as unknown as IncludedRow<
356
- TContract,
357
- ModelName,
358
- TIncludes
359
- >;
388
+ yield blindCast<
389
+ IncludedRow<TContract, ModelName, TIncludes>,
390
+ 'created row combines resolved model input with its server-assigned _id'
391
+ >({ _id: insertedIds[i], ...normalizedRows[i] });
360
392
  }
361
393
  }
362
394
  return new AsyncIterableResult(gen());
363
395
  }
364
396
 
365
- async createCount(
397
+ async createAndCount(
366
398
  data: ReadonlyArray<ResolvedCreateInput<TContract, ModelName, TVariant>>,
367
399
  ): Promise<number> {
368
- this.#rejectIncludes('createCount');
400
+ this.#rejectIncludes('createAndCount');
369
401
  const documents = data.map((d) =>
370
- this.#toDocument(this.#injectDiscriminator(d as Record<string, unknown>)),
402
+ this.#toDocument(
403
+ this.#injectDiscriminator(
404
+ blindCast<
405
+ Record<string, unknown>,
406
+ 'resolved Mongo create-and-count input is a model-field value record'
407
+ >(d),
408
+ ),
409
+ ),
371
410
  );
372
411
  const command = new InsertManyCommand(this.#collectionName, documents);
373
412
  const results = await this.#drainPlan(command);
374
- return (results[0] as { insertedCount: number }).insertedCount;
413
+ return blindCast<
414
+ { insertedCount: number },
415
+ 'InsertManyCommand runtime result exposes insertedCount'
416
+ >(results[0]).insertedCount;
375
417
  }
376
418
 
377
419
  async update(
@@ -386,7 +428,13 @@ class MongoCollectionImpl<
386
428
  const updateDoc = this.#resolveUpdateDoc(dataOrCallback);
387
429
  const command = new FindOneAndUpdateCommand(this.#collectionName, filter, updateDoc, false);
388
430
  const results = await this.#drainPlan(command);
389
- return (results[0] as IncludedRow<TContract, ModelName, TIncludes>) ?? null;
431
+ const result = results[0];
432
+ return result === undefined
433
+ ? null
434
+ : blindCast<
435
+ IncludedRow<TContract, ModelName, TIncludes>,
436
+ 'FindOneAndUpdateCommand plan has no resultShape; collection update exposes its raw driver document as IncludedRow'
437
+ >(result);
390
438
  }
391
439
 
392
440
  updateAll(
@@ -415,19 +463,22 @@ class MongoCollectionImpl<
415
463
  return new AsyncIterableResult(gen());
416
464
  }
417
465
 
418
- async updateCount(
466
+ async updateAndCount(
419
467
  dataOrCallback:
420
468
  | Partial<DefaultModelRow<TContract, ModelName>>
421
469
  | ((u: FieldAccessor<TContract, ModelName>) => FieldOperation[]),
422
470
  ): Promise<number> {
423
- this.#requireFilters('updateCount');
424
- this.#rejectWindowing('updateCount');
425
- this.#rejectIncludes('updateCount');
471
+ this.#requireFilters('updateAndCount');
472
+ this.#rejectWindowing('updateAndCount');
473
+ this.#rejectIncludes('updateAndCount');
426
474
  const filter = this.#mergeFilters();
427
475
  const updateDoc = this.#resolveUpdateDoc(dataOrCallback);
428
476
  const command = new UpdateManyCommand(this.#collectionName, filter, updateDoc);
429
477
  const results = await this.#drainPlan(command);
430
- return (results[0] as { modifiedCount: number }).modifiedCount;
478
+ return blindCast<
479
+ { modifiedCount: number },
480
+ 'UpdateManyCommand runtime result exposes modifiedCount'
481
+ >(results[0]).modifiedCount;
431
482
  }
432
483
 
433
484
  async delete(): Promise<IncludedRow<TContract, ModelName, TIncludes> | null> {
@@ -437,7 +488,13 @@ class MongoCollectionImpl<
437
488
  const filter = this.#mergeFilters();
438
489
  const command = new FindOneAndDeleteCommand(this.#collectionName, filter);
439
490
  const results = await this.#drainPlan(command);
440
- return (results[0] as IncludedRow<TContract, ModelName, TIncludes>) ?? null;
491
+ const result = results[0];
492
+ return result === undefined
493
+ ? null
494
+ : blindCast<
495
+ IncludedRow<TContract, ModelName, TIncludes>,
496
+ 'FindOneAndDeleteCommand plan has no resultShape; collection delete exposes its raw driver document as IncludedRow'
497
+ >(result);
441
498
  }
442
499
 
443
500
  deleteAll(): AsyncIterableResult<IncludedRow<TContract, ModelName, TIncludes>> {
@@ -457,14 +514,17 @@ class MongoCollectionImpl<
457
514
  return new AsyncIterableResult(gen());
458
515
  }
459
516
 
460
- async deleteCount(): Promise<number> {
461
- this.#requireFilters('deleteCount');
462
- this.#rejectWindowing('deleteCount');
463
- this.#rejectIncludes('deleteCount');
517
+ async deleteAndCount(): Promise<number> {
518
+ this.#requireFilters('deleteAndCount');
519
+ this.#rejectWindowing('deleteAndCount');
520
+ this.#rejectIncludes('deleteAndCount');
464
521
  const filter = this.#mergeFilters();
465
522
  const command = new DeleteManyCommand(this.#collectionName, filter);
466
523
  const results = await this.#drainPlan(command);
467
- return (results[0] as { deletedCount: number }).deletedCount;
524
+ return blindCast<
525
+ { deletedCount: number },
526
+ 'DeleteManyCommand runtime result exposes deletedCount'
527
+ >(results[0]).deletedCount;
468
528
  }
469
529
 
470
530
  async upsert(input: {
@@ -479,7 +539,12 @@ class MongoCollectionImpl<
479
539
  const filter = this.#mergeFilters();
480
540
 
481
541
  const allCreateFields = this.#toDocument(
482
- this.#injectDiscriminator(input.create as Record<string, unknown>),
542
+ this.#injectDiscriminator(
543
+ blindCast<
544
+ Record<string, unknown>,
545
+ 'resolved Mongo upsert create input is a model-field value record'
546
+ >(input.create),
547
+ ),
483
548
  );
484
549
 
485
550
  let updateDoc: Record<string, Record<string, MongoValue>>;
@@ -506,7 +571,12 @@ class MongoCollectionImpl<
506
571
  this.#wrapFieldOpValue(field, value, operator),
507
572
  );
508
573
  } else {
509
- const setFields = this.#toSetFields(input.update as Record<string, unknown>);
574
+ const setFields = this.#toSetFields(
575
+ blindCast<
576
+ Record<string, unknown>,
577
+ 'resolved Mongo upsert update input is a partial model-field value record'
578
+ >(input.update),
579
+ );
510
580
  updateDoc = {};
511
581
  if (Object.keys(setFields).length > 0) {
512
582
  updateDoc['$set'] = setFields;
@@ -531,7 +601,10 @@ class MongoCollectionImpl<
531
601
 
532
602
  const command = new FindOneAndUpdateCommand(this.#collectionName, filter, updateDoc, true);
533
603
  const results = await this.#drainPlan(command);
534
- return results[0] as IncludedRow<TContract, ModelName, TIncludes>;
604
+ return blindCast<
605
+ IncludedRow<TContract, ModelName, TIncludes>,
606
+ 'FindOneAndUpdateCommand upsert plan has no resultShape; collection upsert exposes its raw driver document as IncludedRow'
607
+ >(results[0]);
535
608
  }
536
609
 
537
610
  async #readMatchingIds(): Promise<unknown[]> {
@@ -551,7 +624,11 @@ class MongoCollectionImpl<
551
624
  // re-read flow depends on it.
552
625
  const { resultShape: _rs, ...planWithoutShape } = idQuery.#compile();
553
626
  for await (const row of this.#executor.execute(planWithoutShape)) {
554
- ids.push((row as Record<string, unknown>)['_id']);
627
+ const storageRow = blindCast<
628
+ Record<string, unknown>,
629
+ 'Mongo id-prefetch plan without resultShape yields a raw storage row containing _id'
630
+ >(row);
631
+ ids.push(storageRow['_id']);
555
632
  }
556
633
  return ids;
557
634
  }
@@ -562,9 +639,10 @@ class MongoCollectionImpl<
562
639
  }
563
640
 
564
641
  #compile(): MongoQueryPlan<IncludedRow<TContract, ModelName, TIncludes>> {
565
- const model = domainModelsAtDefaultNamespace(this.#contract.domain)[this.#modelName] as
566
- | MongoModelDefinition
567
- | undefined;
642
+ const model = blindCast<
643
+ MongoModelDefinition | undefined,
644
+ 'Mongo contract model lookup preserves target storage metadata erased by the namespace helper'
645
+ >(domainModelsAtDefaultNamespace(this.#contract.domain)[this.#modelName]);
568
646
  if (!model) {
569
647
  throw ormError('ORM.MODEL_UNKNOWN', `Unknown model: "${this.#modelName}".`, {
570
648
  meta: { model: this.#modelName },
@@ -578,7 +656,7 @@ class MongoCollectionImpl<
578
656
  );
579
657
  }
580
658
 
581
- #wrapCommand(command: AnyMongoCommand): MongoQueryPlan {
659
+ #wrapCommand(command: AnyMongoCommand): MongoQueryPlan<unknown> {
582
660
  return { collection: this.#collectionName, command, meta: this.#planMeta() };
583
661
  }
584
662
 
@@ -593,9 +671,10 @@ class MongoCollectionImpl<
593
671
  }
594
672
 
595
673
  #modelFields(): Record<string, ContractField> {
596
- const model = domainModelsAtDefaultNamespace(this.#contract.domain)[this.#modelName] as
597
- | MongoModelDefinition
598
- | undefined;
674
+ const model = blindCast<
675
+ MongoModelDefinition | undefined,
676
+ 'Mongo contract model lookup preserves target storage metadata erased by the namespace helper'
677
+ >(domainModelsAtDefaultNamespace(this.#contract.domain)[this.#modelName]);
599
678
  return model?.fields ?? {};
600
679
  }
601
680
 
@@ -624,10 +703,22 @@ class MongoCollectionImpl<
624
703
 
625
704
  if (field.many && Array.isArray(value)) {
626
705
  return value.map((item) =>
627
- this.#wrapValueObject(item as Record<string, unknown>, voDef),
628
- ) as unknown as MongoValue;
706
+ this.#wrapValueObject(
707
+ blindCast<
708
+ Record<string, unknown>,
709
+ 'contract-typed value-object array elements are field-value records'
710
+ >(item),
711
+ voDef,
712
+ ),
713
+ );
629
714
  }
630
- return this.#wrapValueObject(value as Record<string, unknown>, voDef);
715
+ return this.#wrapValueObject(
716
+ blindCast<
717
+ Record<string, unknown>,
718
+ 'contract-typed value-object input is a field-value record'
719
+ >(value),
720
+ voDef,
721
+ );
631
722
  }
632
723
 
633
724
  return new MongoParamRef(value);
@@ -708,7 +799,12 @@ class MongoCollectionImpl<
708
799
  this.#wrapFieldOpValue(field, value, operator),
709
800
  );
710
801
  }
711
- return this.#toUpdateDocument(dataOrCallback as Record<string, unknown>);
802
+ return this.#toUpdateDocument(
803
+ blindCast<
804
+ Record<string, unknown>,
805
+ 'partial Mongo update input is a model-field value record after callback narrowing'
806
+ >(dataOrCallback),
807
+ );
712
808
  }
713
809
 
714
810
  #wrapFieldOpValue(field: string, value: MongoValue, operator?: string): MongoValue {
@@ -729,11 +825,11 @@ class MongoCollectionImpl<
729
825
 
730
826
  if (contractField.type.kind === 'valueObject' && value instanceof MongoParamRef) {
731
827
  const raw = value.value;
732
- if (typeof raw === 'object' && raw !== null && !Array.isArray(raw)) {
828
+ if (isUnknownRecord(raw)) {
733
829
  const voName = contractField.type.name;
734
830
  const voDef = domainValueObjectsAtDefaultNamespace(this.#contract.domain)?.[voName];
735
831
  if (voDef) {
736
- return this.#wrapValueObject(raw as Record<string, unknown>, voDef);
832
+ return this.#wrapValueObject(raw, voDef);
737
833
  }
738
834
  }
739
835
  }
@@ -761,11 +857,11 @@ class MongoCollectionImpl<
761
857
 
762
858
  if (currentField?.type.kind === 'valueObject' && value instanceof MongoParamRef) {
763
859
  const raw = value.value;
764
- if (typeof raw === 'object' && raw !== null && !Array.isArray(raw)) {
860
+ if (isUnknownRecord(raw)) {
765
861
  const voName = currentField.type.name;
766
862
  const voDef = domainValueObjectsAtDefaultNamespace(this.#contract.domain)?.[voName];
767
863
  if (voDef) {
768
- return this.#wrapValueObject(raw as Record<string, unknown>, voDef);
864
+ return this.#wrapValueObject(raw, voDef);
769
865
  }
770
866
  }
771
867
  }
@@ -825,9 +921,10 @@ class MongoCollectionImpl<
825
921
 
826
922
  #injectDiscriminator(data: Record<string, unknown>): Record<string, unknown> {
827
923
  if (!this.#variantName) return data;
828
- const model = domainModelsAtDefaultNamespace(this.#contract.domain)[this.#modelName] as
829
- | MongoModelDefinition
830
- | undefined;
924
+ const model = blindCast<
925
+ MongoModelDefinition | undefined,
926
+ 'Mongo contract model lookup preserves target storage metadata erased by the namespace helper'
927
+ >(domainModelsAtDefaultNamespace(this.#contract.domain)[this.#modelName]);
831
928
  if (!model?.discriminator || !model.variants) return data;
832
929
  const variantEntry = model.variants[this.#variantName];
833
930
  if (!variantEntry) return data;