@prisma-next/adapter-mongo 0.5.0-dev.9 → 0.5.0
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/dist/codec-types.d.mts.map +1 -1
- package/dist/codec-types.mjs +1 -1
- package/dist/control.d.mts.map +1 -1
- package/dist/control.mjs +11 -18
- package/dist/control.mjs.map +1 -1
- package/dist/index.d.mts +17 -17
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +5 -13
- package/dist/index.mjs.map +1 -1
- package/dist/{mongo-adapter-B3Mh3rXi.mjs → mongo-adapter-DfCmEYHR.mjs} +170 -105
- package/dist/mongo-adapter-DfCmEYHR.mjs.map +1 -0
- package/dist/runtime.d.mts +16 -0
- package/dist/runtime.d.mts.map +1 -0
- package/dist/runtime.mjs +23 -0
- package/dist/runtime.mjs.map +1 -0
- package/package.json +23 -19
- package/src/core/codecs.ts +111 -23
- package/src/core/introspect-schema.ts +6 -1
- package/src/core/operations.ts +9 -10
- package/src/exports/control.ts +2 -18
- package/src/exports/runtime.ts +48 -0
- package/src/lowering.ts +25 -14
- package/src/mongo-adapter.ts +48 -47
- package/src/resolve-value.ts +41 -6
- package/dist/mongo-adapter-B3Mh3rXi.mjs.map +0 -1
package/package.json
CHANGED
|
@@ -1,34 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma-next/adapter-mongo",
|
|
3
|
-
"version": "0.5.0
|
|
3
|
+
"version": "0.5.0",
|
|
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/
|
|
11
|
-
"@prisma-next/target-mongo": "0.5.0
|
|
12
|
-
"@prisma-next/
|
|
13
|
-
"@prisma-next/
|
|
14
|
-
"@prisma-next/
|
|
15
|
-
"@prisma-next/mongo-
|
|
16
|
-
"@prisma-next/mongo-lowering": "0.5.0
|
|
17
|
-
"@prisma-next/mongo-
|
|
18
|
-
"@prisma-next/mongo-
|
|
19
|
-
"@prisma-next/mongo-
|
|
20
|
-
"@prisma-next/
|
|
21
|
-
"@prisma-next/
|
|
22
|
-
"@prisma-next/
|
|
11
|
+
"@prisma-next/config": "0.5.0",
|
|
12
|
+
"@prisma-next/target-mongo": "0.5.0",
|
|
13
|
+
"@prisma-next/contract": "0.5.0",
|
|
14
|
+
"@prisma-next/framework-components": "0.5.0",
|
|
15
|
+
"@prisma-next/mongo-contract": "0.5.0",
|
|
16
|
+
"@prisma-next/mongo-codec": "0.5.0",
|
|
17
|
+
"@prisma-next/mongo-lowering": "0.5.0",
|
|
18
|
+
"@prisma-next/mongo-query-ast": "0.5.0",
|
|
19
|
+
"@prisma-next/mongo-schema-ir": "0.5.0",
|
|
20
|
+
"@prisma-next/mongo-wire": "0.5.0",
|
|
21
|
+
"@prisma-next/operations": "0.5.0",
|
|
22
|
+
"@prisma-next/utils": "0.5.0",
|
|
23
|
+
"@prisma-next/mongo-value": "0.5.0"
|
|
23
24
|
},
|
|
24
25
|
"devDependencies": {
|
|
25
|
-
"mongodb-memory-server": "
|
|
26
|
-
"tsdown": "0.
|
|
26
|
+
"mongodb-memory-server": "11.0.1",
|
|
27
|
+
"tsdown": "0.22.0",
|
|
27
28
|
"typescript": "5.9.3",
|
|
28
|
-
"vitest": "4.
|
|
29
|
-
"@prisma-next/
|
|
30
|
-
"@prisma-next/
|
|
29
|
+
"vitest": "4.1.5",
|
|
30
|
+
"@prisma-next/driver-mongo": "0.5.0",
|
|
31
|
+
"@prisma-next/mongo-contract-psl": "0.5.0",
|
|
32
|
+
"@prisma-next/psl-parser": "0.5.0",
|
|
31
33
|
"@prisma-next/tsconfig": "0.0.0",
|
|
34
|
+
"@prisma-next/test-utils": "0.0.1",
|
|
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",
|
package/src/core/codecs.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
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);
|
package/src/core/operations.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import type { OperationDescriptor } from '@prisma-next/operations';
|
|
2
|
-
import {
|
|
1
|
+
import type { OperationDescriptor, OperationDescriptors } from '@prisma-next/operations';
|
|
2
|
+
import { MONGO_VECTOR_CODEC_ID } from './codec-ids';
|
|
3
3
|
|
|
4
|
-
export const mongoVectorNearOperation = Object.freeze({
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}) satisfies OperationDescriptor;
|
|
4
|
+
export const mongoVectorNearOperation: OperationDescriptor = Object.freeze({
|
|
5
|
+
self: { codecId: MONGO_VECTOR_CODEC_ID },
|
|
6
|
+
impl: () => undefined as never,
|
|
7
|
+
});
|
|
9
8
|
|
|
10
|
-
export const mongoVectorOperationDescriptors:
|
|
11
|
-
mongoVectorNearOperation,
|
|
12
|
-
|
|
9
|
+
export const mongoVectorOperationDescriptors: OperationDescriptors = {
|
|
10
|
+
near: mongoVectorNearOperation,
|
|
11
|
+
};
|
package/src/exports/control.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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]) =>
|
|
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
|
|
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
|
}
|
package/src/mongo-adapter.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
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
|
|
33
|
+
readonly #codecs: MongoCodecRegistry;
|
|
32
34
|
|
|
33
|
-
constructor(codecs
|
|
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(
|
|
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(
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
180
|
-
|
|
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
|
}
|