@prisma-next/adapter-mongo 0.5.0-dev.8 → 0.5.0-dev.81

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/package.json CHANGED
@@ -1,34 +1,37 @@
1
1
  {
2
2
  "name": "@prisma-next/adapter-mongo",
3
- "version": "0.5.0-dev.8",
3
+ "version": "0.5.0-dev.81",
4
+ "license": "Apache-2.0",
4
5
  "type": "module",
5
6
  "sideEffects": false,
6
7
  "description": "MongoDB adapter for Prisma Next (lowers commands to wire format)",
7
8
  "dependencies": {
8
9
  "arktype": "^2.1.29",
9
10
  "mongodb": "^6.16.0",
10
- "@prisma-next/contract": "0.5.0-dev.8",
11
- "@prisma-next/framework-components": "0.5.0-dev.8",
12
- "@prisma-next/target-mongo": "0.5.0-dev.8",
13
- "@prisma-next/config": "0.5.0-dev.8",
14
- "@prisma-next/mongo-codec": "0.5.0-dev.8",
15
- "@prisma-next/mongo-lowering": "0.5.0-dev.8",
16
- "@prisma-next/mongo-schema-ir": "0.5.0-dev.8",
17
- "@prisma-next/mongo-value": "0.5.0-dev.8",
18
- "@prisma-next/mongo-wire": "0.5.0-dev.8",
19
- "@prisma-next/mongo-contract": "0.5.0-dev.8",
20
- "@prisma-next/mongo-query-ast": "0.5.0-dev.8",
21
- "@prisma-next/operations": "0.5.0-dev.8",
22
- "@prisma-next/utils": "0.5.0-dev.8"
11
+ "@prisma-next/config": "0.5.0-dev.81",
12
+ "@prisma-next/contract": "0.5.0-dev.81",
13
+ "@prisma-next/target-mongo": "0.5.0-dev.81",
14
+ "@prisma-next/framework-components": "0.5.0-dev.81",
15
+ "@prisma-next/mongo-codec": "0.5.0-dev.81",
16
+ "@prisma-next/mongo-contract": "0.5.0-dev.81",
17
+ "@prisma-next/mongo-lowering": "0.5.0-dev.81",
18
+ "@prisma-next/mongo-query-ast": "0.5.0-dev.81",
19
+ "@prisma-next/mongo-value": "0.5.0-dev.81",
20
+ "@prisma-next/mongo-wire": "0.5.0-dev.81",
21
+ "@prisma-next/operations": "0.5.0-dev.81",
22
+ "@prisma-next/utils": "0.5.0-dev.81",
23
+ "@prisma-next/mongo-schema-ir": "0.5.0-dev.81"
23
24
  },
24
25
  "devDependencies": {
25
- "mongodb-memory-server": "10.4.3",
26
- "tsdown": "0.18.4",
26
+ "mongodb-memory-server": "11.0.1",
27
+ "tsdown": "0.22.0",
27
28
  "typescript": "5.9.3",
28
- "vitest": "4.0.17",
29
- "@prisma-next/driver-mongo": "0.5.0-dev.8",
30
- "@prisma-next/tsconfig": "0.0.0",
29
+ "vitest": "4.1.5",
30
+ "@prisma-next/driver-mongo": "0.5.0-dev.81",
31
+ "@prisma-next/mongo-contract-psl": "0.5.0-dev.81",
32
+ "@prisma-next/psl-parser": "0.5.0-dev.81",
31
33
  "@prisma-next/test-utils": "0.0.1",
34
+ "@prisma-next/tsconfig": "0.0.0",
32
35
  "@prisma-next/tsdown": "0.0.0"
33
36
  },
34
37
  "files": [
@@ -39,6 +42,7 @@
39
42
  ".": "./dist/index.mjs",
40
43
  "./codec-types": "./dist/codec-types.mjs",
41
44
  "./control": "./dist/control.mjs",
45
+ "./runtime": "./dist/runtime.mjs",
42
46
  "./package.json": "./package.json"
43
47
  },
44
48
  "main": "./dist/index.mjs",
@@ -1,4 +1,11 @@
1
- import { mongoCodec } from '@prisma-next/mongo-codec';
1
+ import type { CodecDescriptor, CodecTrait } from '@prisma-next/framework-components/codec';
2
+ import { voidParamsSchema } from '@prisma-next/framework-components/codec';
3
+ import {
4
+ type MongoCodec,
5
+ type MongoCodecRegistry,
6
+ mongoCodec,
7
+ newMongoCodecRegistry,
8
+ } from '@prisma-next/mongo-codec';
2
9
  import { ObjectId } from 'mongodb';
3
10
  import {
4
11
  MONGO_BOOLEAN_CODEC_ID,
@@ -12,48 +19,36 @@ import {
12
19
 
13
20
  export const mongoObjectIdCodec = mongoCodec({
14
21
  typeId: MONGO_OBJECTID_CODEC_ID,
15
- targetTypes: ['objectId'],
16
- traits: ['equality'],
17
22
  decode: (wire: ObjectId) => wire.toHexString(),
18
23
  encode: (value: string) => new ObjectId(value),
19
24
  });
20
25
 
21
26
  export const mongoStringCodec = mongoCodec({
22
27
  typeId: MONGO_STRING_CODEC_ID,
23
- targetTypes: ['string'],
24
- traits: ['equality', 'order', 'textual'],
25
28
  decode: (wire: string) => wire,
26
29
  encode: (value: string) => value,
27
30
  });
28
31
 
29
32
  export const mongoDoubleCodec = mongoCodec({
30
33
  typeId: MONGO_DOUBLE_CODEC_ID,
31
- targetTypes: ['double'],
32
- traits: ['equality', 'order', 'numeric'],
33
34
  decode: (wire: number) => wire,
34
35
  encode: (value: number) => value,
35
36
  });
36
37
 
37
38
  export const mongoInt32Codec = mongoCodec({
38
39
  typeId: MONGO_INT32_CODEC_ID,
39
- targetTypes: ['int'],
40
- traits: ['equality', 'order', 'numeric'],
41
40
  decode: (wire: number) => wire,
42
41
  encode: (value: number) => value,
43
42
  });
44
43
 
45
44
  export const mongoBooleanCodec = mongoCodec({
46
45
  typeId: MONGO_BOOLEAN_CODEC_ID,
47
- targetTypes: ['bool'],
48
- traits: ['equality', 'boolean'],
49
46
  decode: (wire: boolean) => wire,
50
47
  encode: (value: boolean) => value,
51
48
  });
52
49
 
53
50
  export const mongoDateCodec = mongoCodec({
54
51
  typeId: MONGO_DATE_CODEC_ID,
55
- targetTypes: ['date'],
56
- traits: ['equality', 'order'],
57
52
  decode: (wire: Date) => wire,
58
53
  encode: (value: Date) => value,
59
54
  encodeJson: (value: Date) => value.toISOString(),
@@ -65,16 +60,109 @@ export const mongoDateCodec = mongoCodec({
65
60
 
66
61
  export const mongoVectorCodec = mongoCodec({
67
62
  typeId: MONGO_VECTOR_CODEC_ID,
68
- targetTypes: ['vector'],
69
- traits: ['equality'],
70
63
  decode: (wire: readonly number[]) => wire,
71
64
  encode: (value: readonly number[]) => value,
72
- renderOutputType: (typeParams) => {
73
- const length = typeParams['length'];
74
- if (length === undefined) return undefined;
75
- if (typeof length !== 'number' || !Number.isFinite(length) || !Number.isInteger(length)) {
76
- throw new Error('renderOutputType: expected positive integer "length" for Vector');
77
- }
78
- return `Vector<${length}>`;
79
- },
80
65
  });
66
+
67
+ /**
68
+ * The canonical set of Mongo wire-type codecs.
69
+ *
70
+ * Single source of truth for both control- and runtime-plane adapter descriptors. Don't duplicate this list — import it.
71
+ */
72
+ export const mongoStandardCodecs = [
73
+ mongoObjectIdCodec,
74
+ mongoStringCodec,
75
+ mongoDoubleCodec,
76
+ mongoInt32Codec,
77
+ mongoBooleanCodec,
78
+ mongoDateCodec,
79
+ mongoVectorCodec,
80
+ ] as const;
81
+
82
+ /**
83
+ * Build a {@link CodecDescriptor} for a Mongo wire-type codec.
84
+ *
85
+ * Wraps an existing {@link MongoCodec} instance into a descriptor whose factory hands out the same shared codec. Mongo's full migration to descriptor-first authoring is tracked under TML-2324; for now the descriptor view is composed from the existing `mongoCodec()` outputs.
86
+ */
87
+ function descriptorFor<Id extends string>(
88
+ codec: MongoCodec<Id, readonly CodecTrait[]>,
89
+ metadata: {
90
+ readonly traits: readonly CodecTrait[];
91
+ readonly targetTypes: readonly string[];
92
+ readonly renderOutputType?: (typeParams: Record<string, unknown>) => string | undefined;
93
+ },
94
+ ): CodecDescriptor {
95
+ // The descriptor's `P` is structurally `Record<string, unknown>` for codecs that take params (Mongo `vector`); non-parameterized codecs ignore the slot. Cast through `unknown` to fit the `CodecDescriptor` slot's `(params: P) => …` typing without leaking a per-codec `P` into the heterogeneous descriptor list.
96
+ const renderOutputType = metadata.renderOutputType as
97
+ | CodecDescriptor['renderOutputType']
98
+ | undefined;
99
+ return {
100
+ codecId: codec.id,
101
+ traits: metadata.traits,
102
+ targetTypes: metadata.targetTypes,
103
+ paramsSchema: voidParamsSchema as CodecDescriptor['paramsSchema'],
104
+ isParameterized: false,
105
+ factory: (() => () => codec) as CodecDescriptor['factory'],
106
+ ...(renderOutputType !== undefined ? { renderOutputType } : {}),
107
+ };
108
+ }
109
+
110
+ const renderVectorOutputType = (typeParams: Record<string, unknown>): string | undefined => {
111
+ const length = typeParams['length'];
112
+ if (length === undefined) return undefined;
113
+ if (
114
+ typeof length !== 'number' ||
115
+ !Number.isFinite(length) ||
116
+ !Number.isInteger(length) ||
117
+ length <= 0
118
+ ) {
119
+ throw new Error('renderOutputType: expected positive integer "length" for Vector');
120
+ }
121
+ return `Vector<${length}>`;
122
+ };
123
+
124
+ /**
125
+ * Mongo wire-type codec descriptors. Static metadata for `traits`, `targetTypes`, and `renderOutputType` lives here (the descriptor shape) — `MongoCodec` itself is narrow and only carries the four conversion methods (TML-2357).
126
+ */
127
+ export const mongoCodecDescriptors: ReadonlyArray<CodecDescriptor> = [
128
+ descriptorFor(mongoObjectIdCodec, { traits: ['equality'], targetTypes: ['objectId'] }),
129
+ descriptorFor(mongoStringCodec, {
130
+ traits: ['equality', 'order', 'textual'],
131
+ targetTypes: ['string'],
132
+ }),
133
+ descriptorFor(mongoDoubleCodec, {
134
+ traits: ['equality', 'order', 'numeric'],
135
+ targetTypes: ['double'],
136
+ }),
137
+ descriptorFor(mongoInt32Codec, {
138
+ traits: ['equality', 'order', 'numeric'],
139
+ targetTypes: ['int'],
140
+ }),
141
+ descriptorFor(mongoBooleanCodec, { traits: ['equality', 'boolean'], targetTypes: ['bool'] }),
142
+ descriptorFor(mongoDateCodec, { traits: ['equality', 'order'], targetTypes: ['date'] }),
143
+ descriptorFor(mongoVectorCodec, {
144
+ traits: ['equality'],
145
+ targetTypes: ['vector'],
146
+ renderOutputType: renderVectorOutputType,
147
+ }),
148
+ ];
149
+
150
+ /**
151
+ * Lookup descriptor metadata by codec id — used by tests and for descriptor-side reads of static metadata.
152
+ */
153
+ export function mongoDescriptorById(codecId: string): CodecDescriptor | undefined {
154
+ return mongoCodecDescriptors.find((d) => d.codecId === codecId);
155
+ }
156
+
157
+ /**
158
+ * Build a {@link MongoCodecRegistry} preloaded with the standard Mongo wire-type codecs.
159
+ *
160
+ * Single point of truth for adapter-side codec construction: used by the legacy synchronous `createMongoAdapter()` factory and by the runtime adapter descriptor's `codecs()` getter. Userland code obtains a registry via the framework's execution-stack composition (see `createMongoExecutionContext`) instead of calling this directly.
161
+ */
162
+ export function buildStandardCodecRegistry(): MongoCodecRegistry {
163
+ const registry = newMongoCodecRegistry();
164
+ for (const codec of mongoStandardCodecs) {
165
+ registry.register(codec);
166
+ }
167
+ return registry;
168
+ }
@@ -18,7 +18,12 @@ function parseIndexKeys(keySpec: Record<string, unknown>): MongoIndexKey[] {
18
18
  return keys;
19
19
  }
20
20
 
21
- function isDefaultIdIndex(doc: Document): boolean {
21
+ /**
22
+ * Exported for unit tests to exercise the defensive `!key` guard; not part of
23
+ * the public API. Callers in this package use it via the `introspectSchema`
24
+ * pipeline only.
25
+ */
26
+ export function isDefaultIdIndex(doc: Document): boolean {
22
27
  const key = doc['key'] as Record<string, unknown> | undefined;
23
28
  if (!key) return false;
24
29
  const entries = Object.entries(key);
@@ -1,10 +1,10 @@
1
1
  import type { OperationDescriptor } from '@prisma-next/operations';
2
- import { MONGO_INT32_CODEC_ID, MONGO_VECTOR_CODEC_ID } from './codec-ids';
2
+ import { MONGO_VECTOR_CODEC_ID } from './codec-ids';
3
3
 
4
4
  export const mongoVectorNearOperation = Object.freeze({
5
5
  method: 'near',
6
- args: [{ codecId: MONGO_VECTOR_CODEC_ID, nullable: false }],
7
- returns: { codecId: MONGO_INT32_CODEC_ID, nullable: false },
6
+ self: { codecId: MONGO_VECTOR_CODEC_ID },
7
+ impl: () => undefined as never,
8
8
  }) satisfies OperationDescriptor;
9
9
 
10
10
  export const mongoVectorOperationDescriptors: readonly OperationDescriptor[] = [
@@ -8,15 +8,7 @@ export {
8
8
  } from '../core/mongo-control-driver';
9
9
  export { createMongoRunnerDeps, extractDb } from '../core/runner-deps';
10
10
 
11
- import {
12
- mongoBooleanCodec,
13
- mongoDateCodec,
14
- mongoDoubleCodec,
15
- mongoInt32Codec,
16
- mongoObjectIdCodec,
17
- mongoStringCodec,
18
- mongoVectorCodec,
19
- } from '../core/codecs';
11
+ import { mongoCodecDescriptors } from '../core/codecs';
20
12
 
21
13
  const mongoAdapterDescriptor: ControlAdapterDescriptor<'mongo', 'mongo'> = {
22
14
  kind: 'adapter',
@@ -34,15 +26,7 @@ const mongoAdapterDescriptor: ControlAdapterDescriptor<'mongo', 'mongo'> = {
34
26
  ]),
35
27
  types: {
36
28
  codecTypes: {
37
- codecInstances: [
38
- mongoObjectIdCodec,
39
- mongoStringCodec,
40
- mongoDoubleCodec,
41
- mongoInt32Codec,
42
- mongoBooleanCodec,
43
- mongoDateCodec,
44
- mongoVectorCodec,
45
- ],
29
+ codecDescriptors: mongoCodecDescriptors,
46
30
  import: {
47
31
  package: '@prisma-next/adapter-mongo/codec-types',
48
32
  named: 'CodecTypes',
@@ -0,0 +1,48 @@
1
+ import type {
2
+ ExecutionStack,
3
+ RuntimeAdapterDescriptor,
4
+ RuntimeAdapterInstance,
5
+ } from '@prisma-next/framework-components/execution';
6
+ import type { MongoCodecRegistry } from '@prisma-next/mongo-codec';
7
+ import type { MongoAdapter } from '@prisma-next/mongo-lowering';
8
+ import { buildStandardCodecRegistry, mongoCodecDescriptors } from '../core/codecs';
9
+ import { createMongoAdapter } from '../mongo-adapter';
10
+
11
+ /**
12
+ * adapter-mongo deliberately does NOT import the `MongoRuntimeAdapterDescriptor` type alias from `@prisma-next/mongo-runtime`. The adapter package is downstream of the Mongo runtime package only conceptually; introducing a hard import would create a workspace dependency cycle (`mongo-runtime` consumes the runtime descriptor's `create(stack)` factory; `adapter-mongo` would then need `mongo-runtime` to type the
13
+ * descriptor). The descriptor is shaped to satisfy the framework's `RuntimeAdapterDescriptor` plus the structural `MongoStaticContributions` (`codecs()`) that `@prisma-next/mongo-runtime` narrows to at composition time. This mirrors the `target-postgres` ↔ `sql-runtime` decoupling pattern.
14
+ */
15
+
16
+ interface MongoRuntimeAdapterInstance
17
+ extends RuntimeAdapterInstance<'mongo', 'mongo'>,
18
+ MongoAdapter {}
19
+
20
+ const mongoRuntimeAdapterDescriptor: RuntimeAdapterDescriptor<
21
+ 'mongo',
22
+ 'mongo',
23
+ MongoRuntimeAdapterInstance
24
+ > & {
25
+ readonly codecs: () => MongoCodecRegistry;
26
+ } = {
27
+ kind: 'adapter',
28
+ id: 'mongo',
29
+ familyId: 'mongo',
30
+ targetId: 'mongo',
31
+ version: '0.0.1',
32
+ types: {
33
+ codecTypes: {
34
+ codecDescriptors: mongoCodecDescriptors,
35
+ },
36
+ },
37
+ codecs: buildStandardCodecRegistry,
38
+ create(_stack: ExecutionStack<'mongo', 'mongo'>): MongoRuntimeAdapterInstance {
39
+ const adapter = createMongoAdapter();
40
+ return {
41
+ familyId: 'mongo' as const,
42
+ targetId: 'mongo' as const,
43
+ lower: adapter.lower.bind(adapter),
44
+ };
45
+ },
46
+ };
47
+
48
+ export default mongoRuntimeAdapterDescriptor;
package/src/lowering.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { CodecCallContext } from '@prisma-next/framework-components/codec';
1
2
  import type { MongoCodecRegistry } from '@prisma-next/mongo-codec';
2
3
  import type {
3
4
  MongoAggExpr,
@@ -139,17 +140,18 @@ export function lowerAggExpr(expr: MongoAggExpr): unknown {
139
140
 
140
141
  export async function lowerFilter(
141
142
  filter: MongoFilterExpr,
142
- codecs?: MongoCodecRegistry,
143
+ codecs: MongoCodecRegistry,
144
+ ctx: CodecCallContext,
143
145
  ): Promise<Document> {
144
146
  switch (filter.kind) {
145
147
  case 'field':
146
- return { [filter.field]: { [filter.op]: await resolveValue(filter.value, codecs) } };
148
+ return { [filter.field]: { [filter.op]: await resolveValue(filter.value, codecs, ctx) } };
147
149
  case 'and':
148
- return { $and: await Promise.all(filter.exprs.map((e) => lowerFilter(e, codecs))) };
150
+ return { $and: await Promise.all(filter.exprs.map((e) => lowerFilter(e, codecs, ctx))) };
149
151
  case 'or':
150
- return { $or: await Promise.all(filter.exprs.map((e) => lowerFilter(e, codecs))) };
152
+ return { $or: await Promise.all(filter.exprs.map((e) => lowerFilter(e, codecs, ctx))) };
151
153
  case 'not':
152
- return { $nor: [await lowerFilter(filter.expr, codecs)] };
154
+ return { $nor: [await lowerFilter(filter.expr, codecs, ctx)] };
153
155
  case 'exists':
154
156
  return { [filter.field]: { $exists: filter.exists } };
155
157
  case 'expr':
@@ -204,11 +206,12 @@ function lowerWindowField(wf: MongoWindowField): Record<string, unknown> {
204
206
 
205
207
  export async function lowerStage(
206
208
  stage: MongoPipelineStage,
207
- codecs?: MongoCodecRegistry,
209
+ codecs: MongoCodecRegistry,
210
+ ctx: CodecCallContext,
208
211
  ): Promise<Record<string, unknown>> {
209
212
  switch (stage.kind) {
210
213
  case 'match':
211
- return { $match: await lowerFilter(stage.filter, codecs) };
214
+ return { $match: await lowerFilter(stage.filter, codecs, ctx) };
212
215
  case 'project': {
213
216
  const projection: Record<string, unknown> = {};
214
217
  for (const [key, val] of Object.entries(stage.projection)) {
@@ -230,7 +233,9 @@ export async function lowerStage(
230
233
  if (stage.localField !== undefined) lookup['localField'] = stage.localField;
231
234
  if (stage.foreignField !== undefined) lookup['foreignField'] = stage.foreignField;
232
235
  if (stage.pipeline) {
233
- lookup['pipeline'] = await Promise.all(stage.pipeline.map((s) => lowerStage(s, codecs)));
236
+ lookup['pipeline'] = await Promise.all(
237
+ stage.pipeline.map((s) => lowerStage(s, codecs, ctx)),
238
+ );
234
239
  }
235
240
  if (stage.let_) {
236
241
  lookup['let'] = lowerExprRecord(stage.let_);
@@ -271,7 +276,9 @@ export async function lowerStage(
271
276
  case 'unionWith': {
272
277
  const unionWith: Record<string, unknown> = { coll: stage.collection };
273
278
  if (stage.pipeline) {
274
- unionWith['pipeline'] = await Promise.all(stage.pipeline.map((s) => lowerStage(s, codecs)));
279
+ unionWith['pipeline'] = await Promise.all(
280
+ stage.pipeline.map((s) => lowerStage(s, codecs, ctx)),
281
+ );
275
282
  }
276
283
  return { $unionWith: unionWith };
277
284
  }
@@ -301,7 +308,7 @@ export async function lowerStage(
301
308
  if (stage.spherical !== undefined) geoNear['spherical'] = stage.spherical;
302
309
  if (stage.maxDistance !== undefined) geoNear['maxDistance'] = stage.maxDistance;
303
310
  if (stage.minDistance !== undefined) geoNear['minDistance'] = stage.minDistance;
304
- if (stage.query) geoNear['query'] = await lowerFilter(stage.query, codecs);
311
+ if (stage.query) geoNear['query'] = await lowerFilter(stage.query, codecs, ctx);
305
312
  if (stage.key !== undefined) geoNear['key'] = stage.key;
306
313
  if (stage.distanceMultiplier !== undefined)
307
314
  geoNear['distanceMultiplier'] = stage.distanceMultiplier;
@@ -311,7 +318,9 @@ export async function lowerStage(
311
318
  case 'facet': {
312
319
  const facetEntries = Object.entries(stage.facets);
313
320
  const facetPipelines = await Promise.all(
314
- facetEntries.map(([, pipeline]) => Promise.all(pipeline.map((s) => lowerStage(s, codecs)))),
321
+ facetEntries.map(([, pipeline]) =>
322
+ Promise.all(pipeline.map((s) => lowerStage(s, codecs, ctx))),
323
+ ),
315
324
  );
316
325
  const facet: Record<string, unknown> = {};
317
326
  for (let i = 0; i < facetEntries.length; i++) {
@@ -336,6 +345,7 @@ export async function lowerStage(
336
345
  graphLookup['restrictSearchWithMatch'] = await lowerFilter(
337
346
  stage.restrictSearchWithMatch,
338
347
  codecs,
348
+ ctx,
339
349
  );
340
350
  return { $graphLookup: graphLookup };
341
351
  }
@@ -344,7 +354,7 @@ export async function lowerStage(
344
354
  if (stage.on !== undefined) merge['on'] = stage.on;
345
355
  if (stage.whenMatched !== undefined) {
346
356
  merge['whenMatched'] = Array.isArray(stage.whenMatched)
347
- ? await Promise.all(stage.whenMatched.map((s) => lowerStage(s, codecs)))
357
+ ? await Promise.all(stage.whenMatched.map((s) => lowerStage(s, codecs, ctx)))
348
358
  : stage.whenMatched;
349
359
  }
350
360
  if (stage.whenNotMatched !== undefined) merge['whenNotMatched'] = stage.whenNotMatched;
@@ -414,7 +424,8 @@ export async function lowerStage(
414
424
 
415
425
  export async function lowerPipeline(
416
426
  stages: ReadonlyArray<MongoPipelineStage>,
417
- codecs?: MongoCodecRegistry,
427
+ codecs: MongoCodecRegistry,
428
+ ctx: CodecCallContext,
418
429
  ): Promise<Array<Record<string, unknown>>> {
419
- return Promise.all(stages.map((s) => lowerStage(s, codecs)));
430
+ return Promise.all(stages.map((s) => lowerStage(s, codecs, ctx)));
420
431
  }
@@ -1,4 +1,5 @@
1
- import { createMongoCodecRegistry, type MongoCodecRegistry } from '@prisma-next/mongo-codec';
1
+ import type { CodecCallContext } from '@prisma-next/framework-components/codec';
2
+ import type { MongoCodecRegistry } from '@prisma-next/mongo-codec';
2
3
  import type { MongoAdapter } from '@prisma-next/mongo-lowering';
3
4
  import type {
4
5
  MongoQueryPlan,
@@ -18,6 +19,7 @@ import {
18
19
  UpdateManyWireCommand,
19
20
  UpdateOneWireCommand,
20
21
  } from '@prisma-next/mongo-wire';
22
+ import { buildStandardCodecRegistry } from './core/codecs';
21
23
  import { lowerFilter, lowerPipeline, lowerStage } from './lowering';
22
24
  import { resolveValue } from './resolve-value';
23
25
 
@@ -28,15 +30,17 @@ function isUpdatePipeline(
28
30
  }
29
31
 
30
32
  class MongoAdapterImpl implements MongoAdapter {
31
- readonly #codecs: MongoCodecRegistry | undefined;
33
+ readonly #codecs: MongoCodecRegistry;
32
34
 
33
- constructor(codecs?: MongoCodecRegistry) {
35
+ constructor(codecs: MongoCodecRegistry) {
34
36
  this.#codecs = codecs;
35
37
  }
36
38
 
37
- async #resolveDocument(expr: MongoExpr): Promise<Document> {
39
+ async #resolveDocument(expr: MongoExpr, ctx: CodecCallContext): Promise<Document> {
38
40
  const entries = Object.entries(expr);
39
- const resolved = await Promise.all(entries.map(([, val]) => resolveValue(val, this.#codecs)));
41
+ const resolved = await Promise.all(
42
+ entries.map(([, val]) => resolveValue(val, this.#codecs, ctx)),
43
+ );
40
44
  const result: Record<string, unknown> = {};
41
45
  for (let i = 0; i < entries.length; i++) {
42
46
  const entry = entries[i];
@@ -47,54 +51,57 @@ class MongoAdapterImpl implements MongoAdapter {
47
51
  return result;
48
52
  }
49
53
 
50
- async #lowerUpdate(update: MongoUpdateSpec): Promise<Document | ReadonlyArray<Document>> {
54
+ async #lowerUpdate(
55
+ update: MongoUpdateSpec,
56
+ ctx: CodecCallContext,
57
+ ): Promise<Document | ReadonlyArray<Document>> {
51
58
  if (isUpdatePipeline(update)) {
52
- return Promise.all(update.map((stage) => lowerStage(stage, this.#codecs)));
59
+ return Promise.all(update.map((stage) => lowerStage(stage, this.#codecs, ctx)));
53
60
  }
54
- return this.#resolveDocument(update);
61
+ return this.#resolveDocument(update, ctx);
55
62
  }
56
63
 
57
- async lower(plan: MongoQueryPlan): Promise<AnyMongoWireCommand> {
64
+ async lower(plan: MongoQueryPlan, ctx: CodecCallContext): Promise<AnyMongoWireCommand> {
58
65
  const { command } = plan;
59
66
  switch (command.kind) {
60
67
  case 'insertOne':
61
68
  return new InsertOneWireCommand(
62
69
  command.collection,
63
- await this.#resolveDocument(command.document),
70
+ await this.#resolveDocument(command.document, ctx),
64
71
  );
65
72
  case 'updateOne': {
66
73
  const [filter, update] = await Promise.all([
67
- lowerFilter(command.filter, this.#codecs),
68
- this.#lowerUpdate(command.update),
74
+ lowerFilter(command.filter, this.#codecs, ctx),
75
+ this.#lowerUpdate(command.update, ctx),
69
76
  ]);
70
77
  return new UpdateOneWireCommand(command.collection, filter, update, command.upsert);
71
78
  }
72
79
  case 'insertMany':
73
80
  return new InsertManyWireCommand(
74
81
  command.collection,
75
- await Promise.all(command.documents.map((doc) => this.#resolveDocument(doc))),
82
+ await Promise.all(command.documents.map((doc) => this.#resolveDocument(doc, ctx))),
76
83
  );
77
84
  case 'updateMany': {
78
85
  const [filter, update] = await Promise.all([
79
- lowerFilter(command.filter, this.#codecs),
80
- this.#lowerUpdate(command.update),
86
+ lowerFilter(command.filter, this.#codecs, ctx),
87
+ this.#lowerUpdate(command.update, ctx),
81
88
  ]);
82
89
  return new UpdateManyWireCommand(command.collection, filter, update, command.upsert);
83
90
  }
84
91
  case 'deleteOne':
85
92
  return new DeleteOneWireCommand(
86
93
  command.collection,
87
- await lowerFilter(command.filter, this.#codecs),
94
+ await lowerFilter(command.filter, this.#codecs, ctx),
88
95
  );
89
96
  case 'deleteMany':
90
97
  return new DeleteManyWireCommand(
91
98
  command.collection,
92
- await lowerFilter(command.filter, this.#codecs),
99
+ await lowerFilter(command.filter, this.#codecs, ctx),
93
100
  );
94
101
  case 'findOneAndUpdate': {
95
102
  const [filter, update] = await Promise.all([
96
- lowerFilter(command.filter, this.#codecs),
97
- this.#lowerUpdate(command.update),
103
+ lowerFilter(command.filter, this.#codecs, ctx),
104
+ this.#lowerUpdate(command.update, ctx),
98
105
  ]);
99
106
  return new FindOneAndUpdateWireCommand(
100
107
  command.collection,
@@ -108,13 +115,13 @@ class MongoAdapterImpl implements MongoAdapter {
108
115
  case 'findOneAndDelete':
109
116
  return new FindOneAndDeleteWireCommand(
110
117
  command.collection,
111
- await lowerFilter(command.filter, this.#codecs),
118
+ await lowerFilter(command.filter, this.#codecs, ctx),
112
119
  command.sort,
113
120
  );
114
121
  case 'aggregate':
115
122
  return new AggregateWireCommand(
116
123
  command.collection,
117
- await lowerPipeline(command.pipeline, this.#codecs),
124
+ await lowerPipeline(command.pipeline, this.#codecs, ctx),
118
125
  );
119
126
  case 'rawAggregate':
120
127
  return new AggregateWireCommand(command.collection, command.pipeline);
@@ -150,32 +157,26 @@ class MongoAdapterImpl implements MongoAdapter {
150
157
  }
151
158
  }
152
159
 
153
- import {
154
- mongoBooleanCodec,
155
- mongoDateCodec,
156
- mongoDoubleCodec,
157
- mongoInt32Codec,
158
- mongoObjectIdCodec,
159
- mongoStringCodec,
160
- mongoVectorCodec,
161
- } from './core/codecs';
162
-
163
- function defaultCodecRegistry(): MongoCodecRegistry {
164
- const registry = createMongoCodecRegistry();
165
- for (const codec of [
166
- mongoObjectIdCodec,
167
- mongoStringCodec,
168
- mongoDoubleCodec,
169
- mongoInt32Codec,
170
- mongoBooleanCodec,
171
- mongoDateCodec,
172
- mongoVectorCodec,
173
- ]) {
174
- registry.register(codec);
175
- }
176
- return registry;
160
+ /**
161
+ * Construct a Mongo adapter with the standard wire-type codecs registered
162
+ * for encode-side dispatch (`MongoParamRef.codecId` lookups).
163
+ *
164
+ * The runtime-side codec registry the runtime decodes against is composed
165
+ * separately by `createMongoExecutionContext`. This factory exists for
166
+ * direct adapter use (the runtime descriptor's `create(stack)` calls
167
+ * through it). User code should compose a stack/context instead.
168
+ */
169
+ export function createMongoAdapter(): MongoAdapter {
170
+ return new MongoAdapterImpl(buildStandardCodecRegistry());
177
171
  }
178
172
 
179
- export function createMongoAdapter(codecs?: MongoCodecRegistry): MongoAdapter {
180
- return new MongoAdapterImpl(codecs ?? defaultCodecRegistry());
173
+ /**
174
+ * Internal escape hatch — direct adapter construction with a caller-supplied
175
+ * codec registry, used only by adapter unit tests that exercise the
176
+ * encode-side codec-dispatch path with synthetic codecs. Not re-exported
177
+ * from the package's public surface and not for production use; production
178
+ * callers compose a `MongoExecutionStack` and `MongoExecutionContext`.
179
+ */
180
+ export function _unstable_createMongoAdapterWithCodecs(codecs: MongoCodecRegistry): MongoAdapter {
181
+ return new MongoAdapterImpl(codecs);
181
182
  }