@prisma-next/mongo-runtime 0.5.0-dev.6 → 0.5.0-dev.61

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/README.md CHANGED
@@ -2,18 +2,89 @@
2
2
 
3
3
  MongoDB runtime executor for Prisma Next.
4
4
 
5
+ ## Package Classification
6
+
7
+ - **Domain**: mongo
8
+ - **Layer**: runtime
9
+ - **Plane**: runtime
10
+
11
+ ## Overview
12
+
13
+ The Mongo runtime package implements the Mongo family runtime by extending the abstract `RuntimeCore` base class from `@prisma-next/framework-components/runtime` with Mongo-specific lowering and driver dispatch. It provides the public runtime API for MongoDB, layering Mongo concerns (adapter lowering and wire-command dispatch) on top of the shared middleware lifecycle.
14
+
15
+ ## Usage
16
+
17
+ The runtime takes a **`MongoExecutionContext`** built from a **`MongoExecutionStack`** (target + adapter + optional driver + extension packs). The context aggregates codec contributions from each stack component into a single registry — users do not construct or thread a `MongoCodecRegistry` themselves. This mirrors the SQL pattern (see `packages/2-sql/5-runtime/src/sql-context.ts`).
18
+
19
+ Typed reads that attach a **`resultShape`** on the query plan are decoded after the driver yields each row: scalars and scalar arrays run through their `codecId` entries; `kind: 'unknown'` subtrees are passed through unchanged; plans without `resultShape` (for example raw commands) leave rows as the driver returned them.
20
+
21
+ Example:
22
+
23
+ ```ts
24
+ import mongoRuntimeAdapter from '@prisma-next/adapter-mongo/runtime';
25
+ import { createMongoDriver } from '@prisma-next/driver-mongo';
26
+ import {
27
+ createMongoExecutionContext,
28
+ createMongoExecutionStack,
29
+ createMongoRuntime,
30
+ } from '@prisma-next/mongo-runtime';
31
+ import mongoRuntimeTarget from '@prisma-next/target-mongo/runtime';
32
+
33
+ const stack = createMongoExecutionStack({
34
+ target: mongoRuntimeTarget,
35
+ adapter: mongoRuntimeAdapter,
36
+ });
37
+ const context = createMongoExecutionContext({ contract, stack });
38
+
39
+ const runtime = createMongoRuntime({
40
+ context,
41
+ driver: await createMongoDriver(url, dbName),
42
+ });
43
+ ```
44
+
45
+ Custom or third-party codecs (encryption, vendor scalars) are contributed via an extension-pack descriptor whose `codecs` slot returns the codec descriptors; `createMongoExecutionContext` folds them into the same registry. Duplicate codec ids across contributors throw `RUNTIME.DUPLICATE_CODEC` at composition time.
46
+
5
47
  ## Responsibilities
6
48
 
7
- - **Runtime executor**: `createMongoRuntime()` composes adapter and driver into a `MongoRuntime` with a single `execute(plan)` entry point accepting `MongoQueryPlan<Row>` from `@prisma-next/mongo-query-ast`. Execution is one path for reads and writes: `adapter.lower(plan)` produces a wire command, then the driver runs it.
49
+ - **Stack/context composition**: `createMongoExecutionStack` and `createMongoExecutionContext` mirror SQL's `createSqlExecutionStack` / `createExecutionContext`. The context aggregates codec contributions from `[stack.target, stack.adapter, ...stack.extensionPacks]` into a single `MongoCodecRegistry`.
50
+ - **Runtime executor**: `createMongoRuntime({ context, driver, ... })` composes context and driver into a `MongoRuntime` with a single `execute(plan)` entry point accepting `MongoQueryPlan<Row>` from `@prisma-next/mongo-query-ast`. The adapter is reached via `context.stack.adapter` (instantiated lazily through the stack's `create(stack)` factory). Execution lowers the plan through the adapter, runs the wire command on the driver, then **optionally decodes** each row when `plan.resultShape` is present.
8
51
  - **Unified flow**: There is no separate `execute` vs `executeCommand`; all operations use `execute(plan)`.
9
- - **Lowering**: Happens in the adapter (`lower(plan)`), not inside the runtime.
10
- - **Lifecycle management**: Connection lifecycle via `close()`
52
+ - **Lowering**: Happens in the adapter (`lower(plan)`), wrapped by the runtime's `lower` override into a `MongoExecutionPlan`.
53
+ - **Middleware lifecycle inheritance**: `MongoRuntime` extends `RuntimeCore<MongoQueryPlan, MongoExecutionPlan, MongoMiddleware>` and inherits the `beforeExecute` / `onRow` / `afterExecute` lifecycle from the framework via `runWithMiddleware`. Mongo does **not** override `runBeforeCompile` (Mongo middleware has no `beforeCompile` hook today).
54
+ - **Lifecycle management**: Connection lifecycle via `close()`.
11
55
 
12
56
  ## Dependencies
13
57
 
14
58
  - **Depends on**:
59
+ - `@prisma-next/mongo-codec` (`MongoCodecRegistry` for decode)
15
60
  - `@prisma-next/mongo-lowering` (`MongoAdapter`, `MongoDriver` interfaces)
16
61
  - `@prisma-next/mongo-query-ast` (`MongoQueryPlan`, `AnyMongoCommand` — the typed plan shape)
17
- - `@prisma-next/runtime-executor` (`AsyncIterableResult` return type)
62
+ - `@prisma-next/framework-components` (`RuntimeCore` base class, `runWithMiddleware` helper, `RuntimeMiddleware` SPI, `AsyncIterableResult` return type, `RuntimeAdapterDescriptor` / `ExecutionStack` for the stack composition model)
18
63
  - **Depended on by**:
19
- - Integration tests (`test/integration/test/mongo/`)
64
+ - Integration tests (`test/integration/test/mongo/` and `test/integration/test/cross-package/cross-family-middleware.test.ts`)
65
+
66
+ ## Architecture
67
+
68
+ `MongoRuntimeImpl` extends `RuntimeCore<MongoQueryPlan, MongoExecutionPlan, MongoMiddleware>` and overrides:
69
+
70
+ - `lower(plan)` — calls the adapter's `lower(plan)` and wraps the resulting wire command into a `MongoExecutionPlan`.
71
+ - `runDriver(exec)` — dispatches the wire command to the Mongo driver via `driver.execute(exec.command)`.
72
+ - `close()` — closes the underlying driver.
73
+
74
+ `MongoRuntimeImpl` extends `RuntimeCore` but **overrides `execute`** so that after `runWithMiddleware` yields a raw driver row, the runtime can **`decodeMongoRow`** when the lowered plan carries `resultShape`, then yield the decoded row. `lower(plan)` copies `resultShape` from the query plan onto `MongoExecutionPlan`. Middleware `onRow` still sees the raw driver row (decode happens after the middleware loop for that row, before the consumer receives the value).
75
+
76
+ The execution template is: `lower` → `runWithMiddleware` (driver loop + middleware) → **per-row decode when `exec.resultShape` is set** → yield to consumer.
77
+
78
+ ```mermaid
79
+ flowchart LR
80
+ Plan[MongoQueryPlan] --> Runtime[MongoRuntime]
81
+ Runtime -.extends.-> Core[RuntimeCore]
82
+ Runtime --> Adapter[MongoAdapter.lower]
83
+ Adapter --> Exec[MongoExecutionPlan]
84
+ Runtime --> Driver[MongoDriver.execute]
85
+ ```
86
+
87
+ ## Related Subsystems
88
+
89
+ - **[Runtime & Middleware Framework](../../../../docs/architecture%20docs/subsystems/4.%20Runtime%20&%20Middleware%20Framework.md)** — Runtime execution pipeline
90
+ - **[Adapters & Targets](../../../../docs/architecture%20docs/subsystems/5.%20Adapters%20&%20Targets.md)** — Adapter and driver responsibilities
package/dist/index.d.mts CHANGED
@@ -1,30 +1,158 @@
1
- import { AfterExecuteResult, AsyncIterableResult, RuntimeMiddleware, RuntimeMiddlewareContext } from "@prisma-next/framework-components/runtime";
2
- import { MongoQueryPlan } from "@prisma-next/mongo-query-ast/execution";
1
+ import { RuntimeAdapterDescriptor, RuntimeAdapterInstance, RuntimeDriverDescriptor, RuntimeDriverInstance, RuntimeExtensionDescriptor, RuntimeExtensionInstance, RuntimeTargetDescriptor, RuntimeTargetInstance, RuntimeTargetInstance as RuntimeTargetInstance$1 } from "@prisma-next/framework-components/execution";
2
+ import { AfterExecuteResult, AsyncIterableResult, ExecutionPlan, RuntimeExecuteOptions, RuntimeMiddleware, RuntimeMiddlewareContext } from "@prisma-next/framework-components/runtime";
3
+ import { MongoCodec, MongoCodecRegistry } from "@prisma-next/mongo-codec";
4
+ import { MongoQueryPlan, MongoResultShape } from "@prisma-next/mongo-query-ast/execution";
5
+ import { AnyMongoWireCommand } from "@prisma-next/mongo-wire";
3
6
  import { MongoAdapter, MongoDriver } from "@prisma-next/mongo-lowering";
4
7
 
8
+ //#region src/mongo-execution-plan.d.ts
9
+
10
+ /**
11
+ * Mongo-domain execution plan: a query lowered to the wire-command shape
12
+ * that a Mongo driver can run.
13
+ *
14
+ * The plan carries:
15
+ * - `command` — the wire command (e.g. `InsertOneWireCommand`,
16
+ * `AggregateWireCommand`) produced by `MongoAdapter.lower(plan)`
17
+ * - `meta` — family-agnostic plan metadata (target, lane, hashes, ...)
18
+ * - `_row` — phantom row type, propagated from the originating
19
+ * `MongoQueryPlan`
20
+ *
21
+ * Extends the framework-level `ExecutionPlan<Row>` marker so generic SPIs
22
+ * (`RuntimeExecutor<MongoExecutionPlan>`,
23
+ * `RuntimeMiddleware<MongoExecutionPlan>`) can be parameterized over it.
24
+ *
25
+ * Lives in the runtime layer (alongside `MongoRuntime`) because the wire
26
+ * command shape lives in the transport layer (`@prisma-next/mongo-wire`),
27
+ * which the lanes layer (`mongo-query-ast`, where `MongoQueryPlan` lives)
28
+ * cannot depend on.
29
+ */
30
+ interface MongoExecutionPlan<Row = unknown> extends ExecutionPlan<Row> {
31
+ readonly command: AnyMongoWireCommand;
32
+ readonly resultShape?: MongoResultShape;
33
+ }
34
+ //#endregion
35
+ //#region src/mongo-execution-stack.d.ts
36
+ /**
37
+ * Mongo-specific static contributions a runtime descriptor declares.
38
+ *
39
+ * Mirrors `SqlStaticContributions` in shape: a `codecs()` getter that yields a `MongoCodecRegistry` populated with this contributor's codecs. The registry is then walked by `createMongoExecutionContext` and folded into the single per-execution registry the runtime reads from at decode time.
40
+ */
41
+ interface MongoStaticContributions {
42
+ readonly codecs: () => MongoCodecRegistry;
43
+ }
44
+ interface MongoRuntimeTargetDescriptor<TTargetId extends string = 'mongo', TTargetInstance extends RuntimeTargetInstance$1<'mongo', TTargetId> = RuntimeTargetInstance$1<'mongo', TTargetId>> extends RuntimeTargetDescriptor<'mongo', TTargetId, TTargetInstance>, MongoStaticContributions {}
45
+ interface MongoRuntimeAdapterInstance<TTargetId extends string = 'mongo'> extends RuntimeAdapterInstance<'mongo', TTargetId>, MongoAdapter {}
46
+ interface MongoRuntimeAdapterDescriptor<TTargetId extends string = 'mongo', TAdapterInstance extends RuntimeAdapterInstance<'mongo', TTargetId> = MongoRuntimeAdapterInstance<TTargetId>> extends RuntimeAdapterDescriptor<'mongo', TTargetId, TAdapterInstance>, MongoStaticContributions {}
47
+ interface MongoRuntimeExtensionInstance<TTargetId extends string = 'mongo'> extends RuntimeExtensionInstance<'mongo', TTargetId> {}
48
+ interface MongoRuntimeExtensionDescriptor<TTargetId extends string = 'mongo'> extends RuntimeExtensionDescriptor<'mongo', TTargetId, MongoRuntimeExtensionInstance<TTargetId>>, MongoStaticContributions {
49
+ create(): MongoRuntimeExtensionInstance<TTargetId>;
50
+ }
51
+ /**
52
+ * The Mongo execution stack: target + adapter + optional driver + extension packs. Mirrors `SqlExecutionStack`. Constructed via `createMongoExecutionStack`.
53
+ */
54
+ interface MongoExecutionStack<TTargetId extends string = 'mongo'> {
55
+ readonly target: MongoRuntimeTargetDescriptor<TTargetId>;
56
+ readonly adapter: MongoRuntimeAdapterDescriptor<TTargetId>;
57
+ readonly driver: RuntimeDriverDescriptor<'mongo', TTargetId, unknown, RuntimeDriverInstance<'mongo', TTargetId>> | undefined;
58
+ readonly extensionPacks: readonly MongoRuntimeExtensionDescriptor<TTargetId>[];
59
+ }
60
+ declare function createMongoExecutionStack<TTargetId extends string = 'mongo'>(options: {
61
+ readonly target: MongoRuntimeTargetDescriptor<TTargetId>;
62
+ readonly adapter: MongoRuntimeAdapterDescriptor<TTargetId>;
63
+ readonly driver?: RuntimeDriverDescriptor<'mongo', TTargetId, unknown, RuntimeDriverInstance<'mongo', TTargetId>> | undefined;
64
+ readonly extensionPacks?: readonly MongoRuntimeExtensionDescriptor<TTargetId>[] | undefined;
65
+ }): MongoExecutionStack<TTargetId>;
66
+ /**
67
+ * Read-only view of the codec registry exposed on `MongoExecutionContext`.
68
+ *
69
+ * Hides `register()` and the iterator from public surface — users do not mutate the per-execution codec registry. Internal aggregation in `createMongoExecutionContext` keeps using the full `MongoCodecRegistry` (it needs `register()`).
70
+ */
71
+ interface MongoCodecLookup {
72
+ get(id: string): MongoCodec<string> | undefined;
73
+ has(id: string): boolean;
74
+ }
75
+ /**
76
+ * Per-execution context aggregated from a `MongoExecutionStack`.
77
+ *
78
+ * Carries the user's contract, a read-only lookup over the codec registry composed from every stack contributor, and a back-reference to the stack itself so the runtime can reach the adapter without users threading it explicitly.
79
+ *
80
+ * Mirrors SQL's `ExecutionContext` in role; Mongo's flavour is leaner because there are no parameterised codecs, JSON-schema validators, or mutation-default generators in scope yet.
81
+ */
82
+ interface MongoExecutionContext<TTargetId extends string = 'mongo'> {
83
+ readonly contract: unknown;
84
+ readonly codecs: MongoCodecLookup;
85
+ readonly stack: MongoExecutionStack<TTargetId>;
86
+ }
87
+ declare function createMongoExecutionContext<TTargetId extends string = 'mongo'>(options: {
88
+ readonly contract: unknown;
89
+ readonly stack: MongoExecutionStack<TTargetId>;
90
+ }): MongoExecutionContext<TTargetId>;
91
+ //#endregion
5
92
  //#region src/mongo-middleware.d.ts
6
93
  interface MongoMiddlewareContext extends RuntimeMiddlewareContext {}
7
- interface MongoMiddleware extends RuntimeMiddleware {
8
- readonly familyId: 'mongo';
9
- beforeExecute?(plan: MongoQueryPlan, ctx: MongoMiddlewareContext): Promise<void>;
10
- onRow?(row: Record<string, unknown>, plan: MongoQueryPlan, ctx: MongoMiddlewareContext): Promise<void>;
11
- afterExecute?(plan: MongoQueryPlan, result: AfterExecuteResult, ctx: MongoMiddlewareContext): Promise<void>;
94
+ /**
95
+ * Mongo-domain middleware. Extends the framework `RuntimeMiddleware`
96
+ * parameterized over `MongoExecutionPlan` because `runWithMiddleware`
97
+ * (driven by `RuntimeCore`) invokes the lifecycle hooks with the
98
+ * post-lowering plan.
99
+ *
100
+ * `familyId` is optional so generic cross-family middleware (e.g.
101
+ * telemetry) — which carry no `familyId` — remain assignable. When
102
+ * present, it must be `'mongo'`; the runtime rejects mismatches at
103
+ * construction time via `checkMiddlewareCompatibility`.
104
+ */
105
+ interface MongoMiddleware extends RuntimeMiddleware<MongoExecutionPlan> {
106
+ readonly familyId?: 'mongo';
107
+ beforeExecute?(plan: MongoExecutionPlan, ctx: MongoMiddlewareContext): Promise<void>;
108
+ onRow?(row: Record<string, unknown>, plan: MongoExecutionPlan, ctx: MongoMiddlewareContext): Promise<void>;
109
+ afterExecute?(plan: MongoExecutionPlan, result: AfterExecuteResult, ctx: MongoMiddlewareContext): Promise<void>;
12
110
  }
13
111
  //#endregion
14
112
  //#region src/mongo-runtime.d.ts
113
+ /**
114
+ * Mongo runtime options.
115
+ *
116
+ * The runtime takes a {@link MongoExecutionContext} (built via
117
+ * `createMongoExecutionContext`) and a driver. Codec resolution flows from
118
+ * the context — there is no `codecs` field on this options bag. The adapter
119
+ * is reached via `context.stack.adapter` (instantiated lazily through the
120
+ * stack's `create(stack)` factory). See ADR — Mongo result-shape as a
121
+ * structural plan field, § Codec registry: stack aggregation, not user
122
+ * threading.
123
+ */
15
124
  interface MongoRuntimeOptions {
16
- readonly adapter: MongoAdapter;
125
+ readonly context: MongoExecutionContext;
17
126
  readonly driver: MongoDriver;
18
- readonly contract: unknown;
19
- readonly targetId: string;
20
- readonly middleware?: readonly RuntimeMiddleware[];
127
+ readonly middleware?: readonly MongoMiddleware[];
21
128
  readonly mode?: 'strict' | 'permissive';
22
129
  }
23
130
  interface MongoRuntime {
24
- execute<Row>(plan: MongoQueryPlan<Row>): AsyncIterableResult<Row>;
131
+ /**
132
+ * Execute a `MongoQueryPlan` and return an async iterable of rows.
133
+ *
134
+ * The optional `options.signal` is threaded through
135
+ * `lower → adapter.lower → resolveValue → codec.encode` so codec authors
136
+ * who forward the signal to their underlying SDK get true cancellation
137
+ * of in-flight network calls. The runtime additionally observes the
138
+ * signal at two boundaries:
139
+ *
140
+ * - **Already-aborted at entry** — first `next()` throws
141
+ * `RUNTIME.ABORTED { phase: 'stream' }` before any work is done.
142
+ * (Inherited from `RuntimeCore.execute`.)
143
+ * - **Mid-encode abort** — surfaces as
144
+ * `RUNTIME.ABORTED { phase: 'encode' }` from inside `resolveValue`'s
145
+ * per-level `Promise.all` race.
146
+ *
147
+ * Mongo's read path decodes rows via `resultShape` (per ADR 209). The
148
+ * same `CodecCallContext` is forwarded into each `codec.decode(wire, ctx)`
149
+ * call, so async decoders that respect the signal get cancellation; the
150
+ * runtime itself does not currently emit a `phase: 'decode'` envelope.
151
+ */
152
+ execute<Row>(plan: MongoQueryPlan<Row>, options?: RuntimeExecuteOptions): AsyncIterableResult<Row>;
25
153
  close(): Promise<void>;
26
154
  }
27
155
  declare function createMongoRuntime(options: MongoRuntimeOptions): MongoRuntime;
28
156
  //#endregion
29
- export { type MongoMiddleware, type MongoMiddlewareContext, type MongoRuntime, type MongoRuntimeOptions, createMongoRuntime };
157
+ export { type MongoCodecLookup, type MongoExecutionContext, type MongoExecutionPlan, type MongoExecutionStack, type MongoMiddleware, type MongoMiddlewareContext, type MongoRuntime, type MongoRuntimeAdapterDescriptor, type MongoRuntimeAdapterInstance, type MongoRuntimeExtensionDescriptor, type MongoRuntimeExtensionInstance, type MongoRuntimeOptions, type MongoRuntimeTargetDescriptor, type MongoStaticContributions, type RuntimeTargetInstance, createMongoExecutionContext, createMongoExecutionStack, createMongoRuntime };
30
158
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/mongo-middleware.ts","../src/mongo-runtime.ts"],"sourcesContent":[],"mappings":";;;;;UAOiB,sBAAA,SAA+B;UAE/B,eAAA,SAAwB;EAFxB,SAAA,QAAA,EAAA,OAAuB;EAEvB,aAAA,EAAA,IAAgB,EAEV,cAFU,EAAA,GAAA,EAEW,sBAFX,CAAA,EAEoC,OAFpC,CAAA,IAAA,CAAA;EAEV,KAAA,EAAA,GAAA,EAEd,MAFc,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,IAAA,EAGb,cAHa,EAAA,GAAA,EAId,sBAJc,CAAA,EAKlB,OALkB,CAAA,IAAA,CAAA;EAAqB,YAAA,EAAA,IAAA,EAOlC,cAPkC,EAAA,MAAA,EAQhC,kBARgC,EAAA,GAAA,EASnC,sBATmC,CAAA,EAUvC,OAVuC,CAAA,IAAA,CAAA;;;;UCK3B,mBAAA;EDTA,SAAA,OAAA,ECUG,YDVoB;EAEvB,SAAA,MAAA,ECSE,WDTc;EAEV,SAAA,QAAA,EAAA,OAAA;EAAqB,SAAA,QAAA,EAAA,MAAA;EAAyB,SAAA,UAAA,CAAA,EAAA,SCUpC,iBDVoC,EAAA;EAE5D,SAAA,IAAA,CAAA,EAAA,QAAA,GAAA,YAAA;;AAEA,UCUQ,YAAA,CDVR;EACJ,OAAA,CAAA,GAAA,CAAA,CAAA,IAAA,ECUgB,cDVhB,CCU+B,GDV/B,CAAA,CAAA,ECUsC,mBDVtC,CCU0D,GDV1D,CAAA;EAEK,KAAA,EAAA,ECSC,ODTD,CAAA,IAAA,CAAA;;AAED,iBCgGO,kBAAA,CDhGP,OAAA,ECgGmC,mBDhGnC,CAAA,ECgGyD,YDhGzD"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/mongo-execution-plan.ts","../src/mongo-execution-stack.ts","../src/mongo-middleware.ts","../src/mongo-runtime.ts"],"sourcesContent":[],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,77 +1,244 @@
1
- import { AsyncIterableResult, checkMiddlewareCompatibility } from "@prisma-next/framework-components/runtime";
1
+ import { createExecutionStack } from "@prisma-next/framework-components/execution";
2
+ import { AsyncIterableResult, RuntimeCore, checkAborted, checkMiddlewareCompatibility, runWithMiddleware, runtimeError } from "@prisma-next/framework-components/runtime";
3
+ import { newMongoCodecRegistry } from "@prisma-next/mongo-codec";
4
+ import { ifDefined } from "@prisma-next/utils/defined";
5
+ import { canonicalStringify } from "@prisma-next/utils/canonical-stringify";
6
+ import { hashContent } from "@prisma-next/utils/hash-content";
2
7
 
8
+ //#region src/mongo-execution-stack.ts
9
+ function createMongoExecutionStack(options) {
10
+ return createExecutionStack({
11
+ target: options.target,
12
+ adapter: options.adapter,
13
+ driver: options.driver,
14
+ extensionPacks: options.extensionPacks
15
+ });
16
+ }
17
+ function createMongoExecutionContext(options) {
18
+ const registry = newMongoCodecRegistry();
19
+ const owners = /* @__PURE__ */ new Map();
20
+ const contributors = [
21
+ options.stack.target,
22
+ options.stack.adapter,
23
+ ...options.stack.extensionPacks
24
+ ];
25
+ for (const contributor of contributors) {
26
+ const contributed = contributor.codecs();
27
+ for (const codec of iterateCodecs(contributed)) {
28
+ const existingOwner = owners.get(codec.id);
29
+ if (existingOwner !== void 0) throw runtimeError("RUNTIME.DUPLICATE_CODEC", `Duplicate Mongo codec id '${codec.id}' contributed by '${contributor.id}' (already registered by '${existingOwner}').`, {
30
+ codecId: codec.id,
31
+ existingOwner,
32
+ incomingOwner: contributor.id
33
+ });
34
+ registry.register(codec);
35
+ owners.set(codec.id, contributor.id);
36
+ }
37
+ }
38
+ return Object.freeze({
39
+ contract: options.contract,
40
+ codecs: registry,
41
+ stack: options.stack
42
+ });
43
+ }
44
+ function* iterateCodecs(registry) {
45
+ yield* registry.values();
46
+ }
47
+
48
+ //#endregion
49
+ //#region src/codecs/decoding.ts
50
+ const WIRE_PREVIEW_LIMIT = 100;
51
+ function previewWireValue(wireValue) {
52
+ if (typeof wireValue === "string") return wireValue.length > WIRE_PREVIEW_LIMIT ? `${wireValue.substring(0, WIRE_PREVIEW_LIMIT)}...` : wireValue;
53
+ return String(wireValue).substring(0, WIRE_PREVIEW_LIMIT);
54
+ }
55
+ function wrapDecodeFailure(error, collection, path, codecId, wireValue) {
56
+ const wrapped = runtimeError("RUNTIME.DECODE_FAILED", `Failed to decode field ${path} in collection '${collection}' with codec '${codecId}': ${error instanceof Error ? error.message : String(error)}`, {
57
+ collection,
58
+ path,
59
+ codec: codecId,
60
+ wirePreview: previewWireValue(wireValue)
61
+ });
62
+ wrapped.cause = error;
63
+ throw wrapped;
64
+ }
65
+ async function decodeMongoRow(row, shape, registry, collection, ctx = {}) {
66
+ if (shape.kind === "unknown") return row;
67
+ if (typeof row !== "object" || row === null) return row;
68
+ const rowObj = row;
69
+ const out = {};
70
+ const tasks = [];
71
+ function scheduleLeaf(path, codecId, wire, assign) {
72
+ const codec = registry.get(codecId);
73
+ if (!codec) {
74
+ assign(wire);
75
+ return;
76
+ }
77
+ tasks.push((async () => {
78
+ try {
79
+ assign(await codec.decode(wire, ctx));
80
+ } catch (error) {
81
+ wrapDecodeFailure(error, collection, path, codecId, wire);
82
+ }
83
+ })());
84
+ }
85
+ function walkField(value, fieldShape, path, assign) {
86
+ switch (fieldShape.kind) {
87
+ case "unknown":
88
+ assign(value);
89
+ return;
90
+ case "leaf":
91
+ if (value === null || value === void 0) {
92
+ assign(value);
93
+ return;
94
+ }
95
+ scheduleLeaf(path, fieldShape.codecId, value, assign);
96
+ return;
97
+ case "document": {
98
+ if (value === null || value === void 0) {
99
+ assign(value);
100
+ return;
101
+ }
102
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
103
+ assign(value);
104
+ return;
105
+ }
106
+ const vObj = value;
107
+ const nested = { ...vObj };
108
+ assign(nested);
109
+ for (const [fk, fShape] of Object.entries(fieldShape.fields)) walkField(vObj[fk], fShape, `${path}.${fk}`, (v) => {
110
+ nested[fk] = v;
111
+ });
112
+ return;
113
+ }
114
+ case "array": {
115
+ if (value === null || value === void 0) {
116
+ assign(value);
117
+ return;
118
+ }
119
+ if (!Array.isArray(value)) {
120
+ assign(value);
121
+ return;
122
+ }
123
+ const arr = [];
124
+ assign(arr);
125
+ for (let i = 0; i < value.length; i++) {
126
+ const el = value[i];
127
+ walkField(el, fieldShape.element, `${path}.${i}`, (v) => {
128
+ arr[i] = v;
129
+ });
130
+ }
131
+ return;
132
+ }
133
+ }
134
+ /* v8 ignore stop */
135
+ }
136
+ for (const [k, fShape] of Object.entries(shape.fields)) walkField(rowObj[k], fShape, k, (v) => {
137
+ out[k] = v;
138
+ });
139
+ for (const k of Object.keys(rowObj)) if (!Object.hasOwn(shape.fields, k)) out[k] = rowObj[k];
140
+ await Promise.all(tasks);
141
+ return out;
142
+ }
143
+
144
+ //#endregion
145
+ //#region src/content-hash.ts
146
+ /**
147
+ * Computes a stable content hash for a lowered Mongo execution plan.
148
+ *
149
+ * Internally builds an unambiguous canonical-stringified preimage from
150
+ * two components:
151
+ *
152
+ * 1. `meta.storageHash` — discriminates by schema. A migration changes the
153
+ * storage hash, which invalidates cached entries automatically (no
154
+ * per-app invalidation logic needed for schema changes).
155
+ * 2. `exec.command` — the wire command. `canonicalStringify` produces a
156
+ * deterministic serialization that is stable across object key
157
+ * insertion order and that distinguishes types JSON would otherwise
158
+ * conflate (e.g. `BigInt(1)` vs `1`, `Date` vs ISO string, `Buffer`
159
+ * vs number array). The spread converts the frozen wire-command
160
+ * class instance (`InsertOneWireCommand`, `AggregateWireCommand`, …)
161
+ * into a plain object exposing its own enumerable properties
162
+ * (`kind`, `collection`, plus the payload-specific fields like
163
+ * `document`/`filter`/`update`/`pipeline`/…), which is what
164
+ * `canonicalStringify` accepts; class instances are rejected
165
+ * outright to prevent silent collisions.
166
+ *
167
+ * Unlike SQL, there is no separate "rendered statement" component because
168
+ * a Mongo `MongoExecutionPlan.command` is the wire command itself —
169
+ * canonicalizing it captures both structure and parameters in one pass.
170
+ *
171
+ * The components are wrapped in an object and canonicalized as a single
172
+ * unit (rather than concatenated with a delimiter) so component
173
+ * boundaries are unambiguous and cannot collide with a different split
174
+ * of the same characters.
175
+ *
176
+ * The canonical string is then piped through `hashContent` to produce a
177
+ * bounded, opaque digest. See `@prisma-next/utils/hash-content` for the
178
+ * rationale.
179
+ *
180
+ * @internal
181
+ */
182
+ function computeMongoContentHash(exec) {
183
+ return hashContent(canonicalStringify({
184
+ storageHash: exec.meta.storageHash,
185
+ command: { ...exec.command }
186
+ }));
187
+ }
188
+
189
+ //#endregion
3
190
  //#region src/mongo-runtime.ts
4
191
  function noop() {}
5
- function now() {
6
- return Date.now();
7
- }
8
- var MongoRuntimeImpl = class {
192
+ var MongoRuntimeImpl = class extends RuntimeCore {
9
193
  #adapter;
10
194
  #driver;
11
- #middleware;
12
- #middlewareContext;
195
+ #codecs;
13
196
  constructor(options) {
14
- this.#adapter = options.adapter;
15
- this.#driver = options.driver;
16
197
  const middleware = options.middleware ? [...options.middleware] : [];
17
- for (const mw of middleware) checkMiddlewareCompatibility(mw, "mongo", options.targetId);
18
- this.#middleware = middleware;
19
- this.#middlewareContext = {
20
- contract: options.contract,
198
+ const targetId = options.context.stack.target.targetId;
199
+ for (const mw of middleware) checkMiddlewareCompatibility(mw, "mongo", targetId);
200
+ const ctx = {
201
+ contract: options.context.contract,
21
202
  mode: options.mode ?? "strict",
22
- now,
203
+ now: () => Date.now(),
23
204
  log: {
24
205
  info: noop,
25
206
  warn: noop,
26
207
  error: noop
27
- }
208
+ },
209
+ contentHash: (exec) => computeMongoContentHash(exec)
28
210
  };
211
+ super({
212
+ middleware,
213
+ ctx
214
+ });
215
+ this.#adapter = options.context.stack.adapter.create(options.context.stack);
216
+ this.#driver = options.driver;
217
+ this.#codecs = options.context.codecs;
29
218
  }
30
- execute(plan) {
31
- const adapter = this.#adapter;
32
- const driver = this.#driver;
33
- const middleware = this.#middleware;
34
- const ctx = this.#middlewareContext;
35
- const iterator = async function* () {
36
- const startedAt = ctx.now();
37
- let rowCount = 0;
38
- let completed = false;
39
- let failed = false;
40
- try {
41
- for (const mw of middleware) if (mw.beforeExecute) await mw.beforeExecute(plan, ctx);
42
- const wireCommand = adapter.lower(plan);
43
- for await (const row of driver.execute(wireCommand)) {
44
- for (const mw of middleware) if (mw.onRow) await mw.onRow(row, plan, ctx);
45
- rowCount++;
46
- yield row;
47
- }
48
- completed = true;
49
- } catch (error) {
50
- failed = true;
51
- throw error;
52
- } finally {
53
- const latencyMs = ctx.now() - startedAt;
54
- for (const mw of middleware) {
55
- if (!mw.afterExecute) continue;
56
- if (failed) {
57
- try {
58
- await mw.afterExecute(plan, {
59
- rowCount,
60
- latencyMs,
61
- completed
62
- }, ctx);
63
- } catch {}
64
- continue;
65
- }
66
- await mw.afterExecute(plan, {
67
- rowCount,
68
- latencyMs,
69
- completed
70
- }, ctx);
71
- }
72
- }
219
+ async lower(plan, ctx) {
220
+ return {
221
+ command: await this.#adapter.lower(plan, ctx),
222
+ meta: plan.meta,
223
+ ...ifDefined("resultShape", plan.resultShape)
224
+ };
225
+ }
226
+ runDriver(exec) {
227
+ return this.#driver.execute(exec.command);
228
+ }
229
+ execute(plan, options) {
230
+ const self = this;
231
+ const signal = options?.signal;
232
+ const codecCtx = signal === void 0 ? {} : { signal };
233
+ const generator = async function* () {
234
+ checkAborted(codecCtx, "stream");
235
+ const compiled = await self.runBeforeCompile(plan);
236
+ const exec = await self.lower(compiled, codecCtx);
237
+ const stream = runWithMiddleware(exec, self.middleware, self.ctx, () => self.runDriver(exec));
238
+ for await (const rawRow of stream) if (exec.resultShape === void 0) yield rawRow;
239
+ else yield await decodeMongoRow(rawRow, exec.resultShape, self.#codecs, exec.command.collection, codecCtx);
73
240
  };
74
- return new AsyncIterableResult(iterator());
241
+ return new AsyncIterableResult(generator());
75
242
  }
76
243
  async close() {
77
244
  await this.#driver.close();
@@ -82,5 +249,5 @@ function createMongoRuntime(options) {
82
249
  }
83
250
 
84
251
  //#endregion
85
- export { createMongoRuntime };
252
+ export { createMongoExecutionContext, createMongoExecutionStack, createMongoRuntime };
86
253
  //# sourceMappingURL=index.mjs.map