@prisma-next/mongo-runtime 0.3.0-dev.128 → 0.3.0-dev.162

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
@@ -4,15 +4,16 @@ MongoDB runtime executor for Prisma Next.
4
4
 
5
5
  ## Responsibilities
6
6
 
7
- - **Runtime executor**: `createMongoRuntime()` composes adapter and driver into a `MongoRuntime` that executes query plans
8
- - **Command lowering**: Translates ORM query plans into MongoDB commands (`FindCommand`, `AggregateCommand`)
9
- - **`$lookup` pipeline**: Builds aggregation pipelines for reference-relation includes
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.
8
+ - **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
10
  - **Lifecycle management**: Connection lifecycle via `close()`
11
11
 
12
12
  ## Dependencies
13
13
 
14
14
  - **Depends on**:
15
- - `@prisma-next/mongo-core` (contract types, query plan types, lowering context)
15
+ - `@prisma-next/mongo-lowering` (`MongoAdapter`, `MongoDriver` interfaces)
16
+ - `@prisma-next/mongo-query-ast` (`MongoQueryPlan`, `AnyMongoCommand` — the typed plan shape)
16
17
  - `@prisma-next/runtime-executor` (`AsyncIterableResult` return type)
17
18
  - **Depended on by**:
18
19
  - Integration tests (`test/integration/test/mongo/`)
package/dist/index.d.mts CHANGED
@@ -1,11 +1,24 @@
1
- import { AsyncIterableResult } from "@prisma-next/runtime-executor";
2
- import { MongoAdapter, MongoDriver, MongoLoweringContext, MongoQueryPlan } from "@prisma-next/mongo-core";
1
+ import { AfterExecuteResult, AsyncIterableResult, RuntimeMiddleware, RuntimeMiddlewareContext } from "@prisma-next/framework-components/runtime";
2
+ import { MongoQueryPlan } from "@prisma-next/mongo-query-ast/execution";
3
+ import { MongoAdapter, MongoDriver } from "@prisma-next/mongo-lowering";
3
4
 
5
+ //#region src/mongo-middleware.d.ts
6
+ 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>;
12
+ }
13
+ //#endregion
4
14
  //#region src/mongo-runtime.d.ts
5
15
  interface MongoRuntimeOptions {
6
16
  readonly adapter: MongoAdapter;
7
17
  readonly driver: MongoDriver;
8
- readonly loweringContext: MongoLoweringContext;
18
+ readonly contract: unknown;
19
+ readonly targetId: string;
20
+ readonly middleware?: readonly RuntimeMiddleware[];
21
+ readonly mode?: 'strict' | 'permissive';
9
22
  }
10
23
  interface MongoRuntime {
11
24
  execute<Row>(plan: MongoQueryPlan<Row>): AsyncIterableResult<Row>;
@@ -13,5 +26,5 @@ interface MongoRuntime {
13
26
  }
14
27
  declare function createMongoRuntime(options: MongoRuntimeOptions): MongoRuntime;
15
28
  //#endregion
16
- export { type MongoRuntime, type MongoRuntimeOptions, createMongoRuntime };
29
+ export { type MongoMiddleware, type MongoMiddlewareContext, type MongoRuntime, type MongoRuntimeOptions, createMongoRuntime };
17
30
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/mongo-runtime.ts"],"sourcesContent":[],"mappings":";;;;UAQiB,mBAAA;oBACG;EADH,SAAA,MAAA,EAEE,WAFiB;EAChB,SAAA,eAAA,EAEQ,oBAFR;;AAEQ,UAGX,YAAA,CAHW;EAAoB,OAAA,CAAA,GAAA,CAAA,CAAA,IAAA,EAI3B,cAJ2B,CAIZ,GAJY,CAAA,CAAA,EAIL,mBAJK,CAIe,GAJf,CAAA;EAG/B,KAAA,EAAA,EAEN,OAFkB,CAAA,IAAA,CAAA;;AACR,iBA+BL,kBAAA,CA/BK,OAAA,EA+BuB,mBA/BvB,CAAA,EA+B6C,YA/B7C"}
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"}
package/dist/index.mjs CHANGED
@@ -1,22 +1,77 @@
1
- import { AsyncIterableResult } from "@prisma-next/runtime-executor";
1
+ import { AsyncIterableResult, checkMiddlewareCompatibility } from "@prisma-next/framework-components/runtime";
2
2
 
3
3
  //#region src/mongo-runtime.ts
4
+ function noop() {}
5
+ function now() {
6
+ return Date.now();
7
+ }
4
8
  var MongoRuntimeImpl = class {
5
9
  #adapter;
6
10
  #driver;
7
- #loweringContext;
11
+ #middleware;
12
+ #middlewareContext;
8
13
  constructor(options) {
9
14
  this.#adapter = options.adapter;
10
15
  this.#driver = options.driver;
11
- this.#loweringContext = options.loweringContext;
16
+ 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,
21
+ mode: options.mode ?? "strict",
22
+ now,
23
+ log: {
24
+ info: noop,
25
+ warn: noop,
26
+ error: noop
27
+ }
28
+ };
12
29
  }
13
30
  execute(plan) {
14
- const executionPlan = this.#adapter.lower(plan, this.#loweringContext);
15
- const iterable = this.#driver.execute(executionPlan.wireCommand);
16
- async function* toGenerator() {
17
- yield* iterable;
18
- }
19
- return new AsyncIterableResult(toGenerator());
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
+ }
73
+ };
74
+ return new AsyncIterableResult(iterator());
20
75
  }
21
76
  async close() {
22
77
  await this.#driver.close();
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["#adapter","#driver","#loweringContext"],"sources":["../src/mongo-runtime.ts"],"sourcesContent":["import type {\n MongoAdapter,\n MongoDriver,\n MongoLoweringContext,\n MongoQueryPlan,\n} from '@prisma-next/mongo-core';\nimport { AsyncIterableResult } from '@prisma-next/runtime-executor';\n\nexport interface MongoRuntimeOptions {\n readonly adapter: MongoAdapter;\n readonly driver: MongoDriver;\n readonly loweringContext: MongoLoweringContext;\n}\n\nexport interface MongoRuntime {\n execute<Row>(plan: MongoQueryPlan<Row>): AsyncIterableResult<Row>;\n close(): Promise<void>;\n}\n\nclass MongoRuntimeImpl implements MongoRuntime {\n readonly #adapter: MongoAdapter;\n readonly #driver: MongoDriver;\n readonly #loweringContext: MongoLoweringContext;\n\n constructor(options: MongoRuntimeOptions) {\n this.#adapter = options.adapter;\n this.#driver = options.driver;\n this.#loweringContext = options.loweringContext;\n }\n\n execute<Row>(plan: MongoQueryPlan<Row>): AsyncIterableResult<Row> {\n const executionPlan = this.#adapter.lower(plan, this.#loweringContext);\n const iterable = this.#driver.execute<Row>(executionPlan.wireCommand);\n\n async function* toGenerator(): AsyncGenerator<Row, void, unknown> {\n yield* iterable;\n }\n\n return new AsyncIterableResult(toGenerator());\n }\n\n async close(): Promise<void> {\n await this.#driver.close();\n }\n}\n\nexport function createMongoRuntime(options: MongoRuntimeOptions): MongoRuntime {\n return new MongoRuntimeImpl(options);\n}\n"],"mappings":";;;AAmBA,IAAM,mBAAN,MAA+C;CAC7C,CAASA;CACT,CAASC;CACT,CAASC;CAET,YAAY,SAA8B;AACxC,QAAKF,UAAW,QAAQ;AACxB,QAAKC,SAAU,QAAQ;AACvB,QAAKC,kBAAmB,QAAQ;;CAGlC,QAAa,MAAqD;EAChE,MAAM,gBAAgB,MAAKF,QAAS,MAAM,MAAM,MAAKE,gBAAiB;EACtE,MAAM,WAAW,MAAKD,OAAQ,QAAa,cAAc,YAAY;EAErE,gBAAgB,cAAkD;AAChE,UAAO;;AAGT,SAAO,IAAI,oBAAoB,aAAa,CAAC;;CAG/C,MAAM,QAAuB;AAC3B,QAAM,MAAKA,OAAQ,OAAO;;;AAI9B,SAAgB,mBAAmB,SAA4C;AAC7E,QAAO,IAAI,iBAAiB,QAAQ"}
1
+ {"version":3,"file":"index.mjs","names":["#adapter","#driver","#middleware","#middlewareContext"],"sources":["../src/mongo-runtime.ts"],"sourcesContent":["import type {\n RuntimeMiddleware,\n RuntimeMiddlewareContext,\n} from '@prisma-next/framework-components/runtime';\nimport {\n AsyncIterableResult,\n checkMiddlewareCompatibility,\n} from '@prisma-next/framework-components/runtime';\nimport type { MongoAdapter, MongoDriver } from '@prisma-next/mongo-lowering';\nimport type { MongoQueryPlan } from '@prisma-next/mongo-query-ast/execution';\n\nfunction noop() {}\nfunction now() {\n return Date.now();\n}\n\nexport interface MongoRuntimeOptions {\n readonly adapter: MongoAdapter;\n readonly driver: MongoDriver;\n readonly contract: unknown;\n readonly targetId: string;\n readonly middleware?: readonly RuntimeMiddleware[];\n readonly mode?: 'strict' | 'permissive';\n}\n\nexport interface MongoRuntime {\n execute<Row>(plan: MongoQueryPlan<Row>): AsyncIterableResult<Row>;\n close(): Promise<void>;\n}\n\nclass MongoRuntimeImpl implements MongoRuntime {\n readonly #adapter: MongoAdapter;\n readonly #driver: MongoDriver;\n readonly #middleware: readonly RuntimeMiddleware[];\n readonly #middlewareContext: RuntimeMiddlewareContext;\n\n constructor(options: MongoRuntimeOptions) {\n this.#adapter = options.adapter;\n this.#driver = options.driver;\n\n const middleware = options.middleware ? [...options.middleware] : [];\n for (const mw of middleware) {\n checkMiddlewareCompatibility(mw, 'mongo', options.targetId);\n }\n this.#middleware = middleware;\n\n this.#middlewareContext = {\n contract: options.contract,\n mode: options.mode ?? 'strict',\n now,\n log: { info: noop, warn: noop, error: noop },\n };\n }\n\n execute<Row>(plan: MongoQueryPlan<Row>): AsyncIterableResult<Row> {\n const adapter = this.#adapter;\n const driver = this.#driver;\n const middleware = this.#middleware;\n const ctx = this.#middlewareContext;\n\n const iterator = async function* (): AsyncGenerator<Row, void, unknown> {\n const startedAt = ctx.now();\n let rowCount = 0;\n let completed = false;\n let failed = false;\n\n try {\n for (const mw of middleware) {\n if (mw.beforeExecute) {\n await mw.beforeExecute(plan, ctx);\n }\n }\n\n const wireCommand = adapter.lower(plan);\n\n for await (const row of driver.execute<Row>(wireCommand)) {\n for (const mw of middleware) {\n if (mw.onRow) {\n await mw.onRow(row as Record<string, unknown>, plan, ctx);\n }\n }\n rowCount++;\n yield row;\n }\n\n completed = true;\n } catch (error) {\n failed = true;\n throw error;\n } finally {\n const latencyMs = ctx.now() - startedAt;\n for (const mw of middleware) {\n if (!mw.afterExecute) continue;\n\n if (failed) {\n try {\n await mw.afterExecute(plan, { rowCount, latencyMs, completed }, ctx);\n } catch {\n // Ignore errors from afterExecute during error handling\n }\n continue;\n }\n\n await mw.afterExecute(plan, { rowCount, latencyMs, completed }, ctx);\n }\n }\n };\n\n return new AsyncIterableResult(iterator());\n }\n\n async close(): Promise<void> {\n await this.#driver.close();\n }\n}\n\nexport function createMongoRuntime(options: MongoRuntimeOptions): MongoRuntime {\n return new MongoRuntimeImpl(options);\n}\n"],"mappings":";;;AAWA,SAAS,OAAO;AAChB,SAAS,MAAM;AACb,QAAO,KAAK,KAAK;;AAiBnB,IAAM,mBAAN,MAA+C;CAC7C,CAASA;CACT,CAASC;CACT,CAASC;CACT,CAASC;CAET,YAAY,SAA8B;AACxC,QAAKH,UAAW,QAAQ;AACxB,QAAKC,SAAU,QAAQ;EAEvB,MAAM,aAAa,QAAQ,aAAa,CAAC,GAAG,QAAQ,WAAW,GAAG,EAAE;AACpE,OAAK,MAAM,MAAM,WACf,8BAA6B,IAAI,SAAS,QAAQ,SAAS;AAE7D,QAAKC,aAAc;AAEnB,QAAKC,oBAAqB;GACxB,UAAU,QAAQ;GAClB,MAAM,QAAQ,QAAQ;GACtB;GACA,KAAK;IAAE,MAAM;IAAM,MAAM;IAAM,OAAO;IAAM;GAC7C;;CAGH,QAAa,MAAqD;EAChE,MAAM,UAAU,MAAKH;EACrB,MAAM,SAAS,MAAKC;EACpB,MAAM,aAAa,MAAKC;EACxB,MAAM,MAAM,MAAKC;EAEjB,MAAM,WAAW,mBAAuD;GACtE,MAAM,YAAY,IAAI,KAAK;GAC3B,IAAI,WAAW;GACf,IAAI,YAAY;GAChB,IAAI,SAAS;AAEb,OAAI;AACF,SAAK,MAAM,MAAM,WACf,KAAI,GAAG,cACL,OAAM,GAAG,cAAc,MAAM,IAAI;IAIrC,MAAM,cAAc,QAAQ,MAAM,KAAK;AAEvC,eAAW,MAAM,OAAO,OAAO,QAAa,YAAY,EAAE;AACxD,UAAK,MAAM,MAAM,WACf,KAAI,GAAG,MACL,OAAM,GAAG,MAAM,KAAgC,MAAM,IAAI;AAG7D;AACA,WAAM;;AAGR,gBAAY;YACL,OAAO;AACd,aAAS;AACT,UAAM;aACE;IACR,MAAM,YAAY,IAAI,KAAK,GAAG;AAC9B,SAAK,MAAM,MAAM,YAAY;AAC3B,SAAI,CAAC,GAAG,aAAc;AAEtB,SAAI,QAAQ;AACV,UAAI;AACF,aAAM,GAAG,aAAa,MAAM;QAAE;QAAU;QAAW;QAAW,EAAE,IAAI;cAC9D;AAGR;;AAGF,WAAM,GAAG,aAAa,MAAM;MAAE;MAAU;MAAW;MAAW,EAAE,IAAI;;;;AAK1E,SAAO,IAAI,oBAAoB,UAAU,CAAC;;CAG5C,MAAM,QAAuB;AAC3B,QAAM,MAAKF,OAAQ,OAAO;;;AAI9B,SAAgB,mBAAmB,SAA4C;AAC7E,QAAO,IAAI,iBAAiB,QAAQ"}
package/package.json CHANGED
@@ -1,25 +1,34 @@
1
1
  {
2
2
  "name": "@prisma-next/mongo-runtime",
3
- "version": "0.3.0-dev.128",
3
+ "version": "0.3.0-dev.162",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "MongoDB runtime implementation for Prisma Next",
7
7
  "dependencies": {
8
- "@prisma-next/contract": "0.3.0-dev.128",
9
- "@prisma-next/runtime-executor": "0.3.0-dev.128",
10
- "@prisma-next/mongo-core": "0.3.0-dev.128"
8
+ "@prisma-next/contract": "0.3.0-dev.162",
9
+ "@prisma-next/mongo-lowering": "0.3.0-dev.162",
10
+ "@prisma-next/mongo-query-ast": "0.3.0-dev.162",
11
+ "@prisma-next/framework-components": "0.3.0-dev.162"
11
12
  },
12
13
  "devDependencies": {
13
14
  "mongodb": "^6.16.0",
14
- "mongodb-memory-server": "^10.4.0",
15
+ "mongodb-memory-server": "10.4.3",
15
16
  "tsdown": "0.18.4",
16
17
  "typescript": "5.9.3",
17
18
  "vitest": "4.0.17",
18
- "@prisma-next/driver-mongo": "0.3.0-dev.128",
19
- "@prisma-next/adapter-mongo": "0.3.0-dev.128",
19
+ "@prisma-next/adapter-mongo": "0.3.0-dev.162",
20
+ "@prisma-next/middleware-telemetry": "0.3.0-dev.162",
21
+ "@prisma-next/runtime-executor": "0.3.0-dev.162",
22
+ "@prisma-next/mongo-contract-ts": "0.3.0-dev.162",
23
+ "@prisma-next/mongo-pipeline-builder": "0.3.0-dev.162",
24
+ "@prisma-next/mongo-value": "0.3.0-dev.162",
25
+ "@prisma-next/family-mongo": "0.3.0-dev.162",
26
+ "@prisma-next/driver-mongo": "0.3.0-dev.162",
27
+ "@prisma-next/mongo-contract": "0.3.0-dev.162",
20
28
  "@prisma-next/test-utils": "0.0.1",
29
+ "@prisma-next/tsdown": "0.0.0",
21
30
  "@prisma-next/tsconfig": "0.0.0",
22
- "@prisma-next/tsdown": "0.0.0"
31
+ "@prisma-next/target-mongo": "0.3.0-dev.162"
23
32
  },
24
33
  "files": [
25
34
  "dist",
@@ -35,7 +44,7 @@
35
44
  "repository": {
36
45
  "type": "git",
37
46
  "url": "https://github.com/prisma/prisma-next.git",
38
- "directory": "packages/2-mongo-family/5-runtime"
47
+ "directory": "packages/2-mongo-family/7-runtime"
39
48
  },
40
49
  "scripts": {
41
50
  "build": "tsdown",
@@ -1,2 +1,3 @@
1
+ export type { MongoMiddleware, MongoMiddlewareContext } from '../mongo-middleware';
1
2
  export type { MongoRuntime, MongoRuntimeOptions } from '../mongo-runtime';
2
3
  export { createMongoRuntime } from '../mongo-runtime';
@@ -0,0 +1,23 @@
1
+ import type {
2
+ AfterExecuteResult,
3
+ RuntimeMiddleware,
4
+ RuntimeMiddlewareContext,
5
+ } from '@prisma-next/framework-components/runtime';
6
+ import type { MongoQueryPlan } from '@prisma-next/mongo-query-ast/execution';
7
+
8
+ export interface MongoMiddlewareContext extends RuntimeMiddlewareContext {}
9
+
10
+ export interface MongoMiddleware extends RuntimeMiddleware {
11
+ readonly familyId: 'mongo';
12
+ beforeExecute?(plan: MongoQueryPlan, ctx: MongoMiddlewareContext): Promise<void>;
13
+ onRow?(
14
+ row: Record<string, unknown>,
15
+ plan: MongoQueryPlan,
16
+ ctx: MongoMiddlewareContext,
17
+ ): Promise<void>;
18
+ afterExecute?(
19
+ plan: MongoQueryPlan,
20
+ result: AfterExecuteResult,
21
+ ctx: MongoMiddlewareContext,
22
+ ): Promise<void>;
23
+ }
@@ -1,15 +1,26 @@
1
1
  import type {
2
- MongoAdapter,
3
- MongoDriver,
4
- MongoLoweringContext,
5
- MongoQueryPlan,
6
- } from '@prisma-next/mongo-core';
7
- import { AsyncIterableResult } from '@prisma-next/runtime-executor';
2
+ RuntimeMiddleware,
3
+ RuntimeMiddlewareContext,
4
+ } from '@prisma-next/framework-components/runtime';
5
+ import {
6
+ AsyncIterableResult,
7
+ checkMiddlewareCompatibility,
8
+ } from '@prisma-next/framework-components/runtime';
9
+ import type { MongoAdapter, MongoDriver } from '@prisma-next/mongo-lowering';
10
+ import type { MongoQueryPlan } from '@prisma-next/mongo-query-ast/execution';
11
+
12
+ function noop() {}
13
+ function now() {
14
+ return Date.now();
15
+ }
8
16
 
9
17
  export interface MongoRuntimeOptions {
10
18
  readonly adapter: MongoAdapter;
11
19
  readonly driver: MongoDriver;
12
- readonly loweringContext: MongoLoweringContext;
20
+ readonly contract: unknown;
21
+ readonly targetId: string;
22
+ readonly middleware?: readonly RuntimeMiddleware[];
23
+ readonly mode?: 'strict' | 'permissive';
13
24
  }
14
25
 
15
26
  export interface MongoRuntime {
@@ -20,23 +31,82 @@ export interface MongoRuntime {
20
31
  class MongoRuntimeImpl implements MongoRuntime {
21
32
  readonly #adapter: MongoAdapter;
22
33
  readonly #driver: MongoDriver;
23
- readonly #loweringContext: MongoLoweringContext;
34
+ readonly #middleware: readonly RuntimeMiddleware[];
35
+ readonly #middlewareContext: RuntimeMiddlewareContext;
24
36
 
25
37
  constructor(options: MongoRuntimeOptions) {
26
38
  this.#adapter = options.adapter;
27
39
  this.#driver = options.driver;
28
- this.#loweringContext = options.loweringContext;
40
+
41
+ const middleware = options.middleware ? [...options.middleware] : [];
42
+ for (const mw of middleware) {
43
+ checkMiddlewareCompatibility(mw, 'mongo', options.targetId);
44
+ }
45
+ this.#middleware = middleware;
46
+
47
+ this.#middlewareContext = {
48
+ contract: options.contract,
49
+ mode: options.mode ?? 'strict',
50
+ now,
51
+ log: { info: noop, warn: noop, error: noop },
52
+ };
29
53
  }
30
54
 
31
55
  execute<Row>(plan: MongoQueryPlan<Row>): AsyncIterableResult<Row> {
32
- const executionPlan = this.#adapter.lower(plan, this.#loweringContext);
33
- const iterable = this.#driver.execute<Row>(executionPlan.wireCommand);
56
+ const adapter = this.#adapter;
57
+ const driver = this.#driver;
58
+ const middleware = this.#middleware;
59
+ const ctx = this.#middlewareContext;
34
60
 
35
- async function* toGenerator(): AsyncGenerator<Row, void, unknown> {
36
- yield* iterable;
37
- }
61
+ const iterator = async function* (): AsyncGenerator<Row, void, unknown> {
62
+ const startedAt = ctx.now();
63
+ let rowCount = 0;
64
+ let completed = false;
65
+ let failed = false;
66
+
67
+ try {
68
+ for (const mw of middleware) {
69
+ if (mw.beforeExecute) {
70
+ await mw.beforeExecute(plan, ctx);
71
+ }
72
+ }
73
+
74
+ const wireCommand = adapter.lower(plan);
75
+
76
+ for await (const row of driver.execute<Row>(wireCommand)) {
77
+ for (const mw of middleware) {
78
+ if (mw.onRow) {
79
+ await mw.onRow(row as Record<string, unknown>, plan, ctx);
80
+ }
81
+ }
82
+ rowCount++;
83
+ yield row;
84
+ }
85
+
86
+ completed = true;
87
+ } catch (error) {
88
+ failed = true;
89
+ throw error;
90
+ } finally {
91
+ const latencyMs = ctx.now() - startedAt;
92
+ for (const mw of middleware) {
93
+ if (!mw.afterExecute) continue;
94
+
95
+ if (failed) {
96
+ try {
97
+ await mw.afterExecute(plan, { rowCount, latencyMs, completed }, ctx);
98
+ } catch {
99
+ // Ignore errors from afterExecute during error handling
100
+ }
101
+ continue;
102
+ }
103
+
104
+ await mw.afterExecute(plan, { rowCount, latencyMs, completed }, ctx);
105
+ }
106
+ }
107
+ };
38
108
 
39
- return new AsyncIterableResult(toGenerator());
109
+ return new AsyncIterableResult(iterator());
40
110
  }
41
111
 
42
112
  async close(): Promise<void> {