@prisma/orm-extension-middleware-cache 8.0.0-rc.1-dev.41 → 8.0.0-rc.1-dev.42

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/index.d.mts CHANGED
@@ -169,17 +169,17 @@ interface CacheMiddlewareOptions {
169
169
  *
170
170
  * The middleware uses three hooks:
171
171
  *
172
- * - `intercept` — on each execution, checks the cache. On a hit, returns
172
+ * - `interceptQuery` — on each execution, checks the cache. On a hit, returns
173
173
  * the cached raw rows; the runtime skips `runDriver` and `onRow`
174
- * (`beforeExecute` is not affected — it has already run for every
175
- * middleware before any `intercept` is consulted) and yields the
174
+ * (`beforeQuery` is not affected — it has already run for every
175
+ * middleware before any `interceptQuery` is consulted) and yields the
176
176
  * cached rows to the consumer (which, in the SQL runtime, sees them
177
177
  * after the standard `decodeRow` pass — i.e. the cache stores
178
178
  * wire-format values). On a miss, records a pending buffer keyed on
179
179
  * the `exec` object identity and returns `undefined` (passthrough).
180
180
  * - `onRow` — on the miss path, appends each row yielded by the driver
181
181
  * to the pending buffer.
182
- * - `afterExecute` — on the miss path, commits the buffer to the store
182
+ * - `afterQuery` — on the miss path, commits the buffer to the store
183
183
  * if and only if `result.completed === true && result.source === 'driver'`.
184
184
  * Failed executions and middleware-served executions never populate
185
185
  * the cache. The pending buffer is cleared in all branches so a stale
package/dist/index.mjs CHANGED
@@ -135,17 +135,17 @@ async function resolveCacheKey(payload, exec, ctx) {
135
135
  *
136
136
  * The middleware uses three hooks:
137
137
  *
138
- * - `intercept` — on each execution, checks the cache. On a hit, returns
138
+ * - `interceptQuery` — on each execution, checks the cache. On a hit, returns
139
139
  * the cached raw rows; the runtime skips `runDriver` and `onRow`
140
- * (`beforeExecute` is not affected — it has already run for every
141
- * middleware before any `intercept` is consulted) and yields the
140
+ * (`beforeQuery` is not affected — it has already run for every
141
+ * middleware before any `interceptQuery` is consulted) and yields the
142
142
  * cached rows to the consumer (which, in the SQL runtime, sees them
143
143
  * after the standard `decodeRow` pass — i.e. the cache stores
144
144
  * wire-format values). On a miss, records a pending buffer keyed on
145
145
  * the `exec` object identity and returns `undefined` (passthrough).
146
146
  * - `onRow` — on the miss path, appends each row yielded by the driver
147
147
  * to the pending buffer.
148
- * - `afterExecute` — on the miss path, commits the buffer to the store
148
+ * - `afterQuery` — on the miss path, commits the buffer to the store
149
149
  * if and only if `result.completed === true && result.source === 'driver'`.
150
150
  * Failed executions and middleware-served executions never populate
151
151
  * the cache. The pending buffer is cleared in all branches so a stale
@@ -183,7 +183,7 @@ function createCacheMiddleware(options) {
183
183
  const store = options?.store ?? createInMemoryCacheStore({ maxEntries: options?.maxEntries ?? DEFAULT_MAX_ENTRIES });
184
184
  const clock = options?.clock ?? Date.now;
185
185
  const pending = /* @__PURE__ */ new WeakMap();
186
- async function intercept(exec, ctx) {
186
+ async function interceptQuery(exec, ctx) {
187
187
  if (ctx.scope !== "runtime") return;
188
188
  const payload = readCachePayload(exec);
189
189
  if (payload === void 0) return;
@@ -215,7 +215,7 @@ function createCacheMiddleware(options) {
215
215
  if (slot === void 0) return;
216
216
  slot.buffer.push(row);
217
217
  }
218
- async function afterExecute(exec, result, ctx) {
218
+ async function afterQuery(exec, result, ctx) {
219
219
  const slot = pending.get(exec);
220
220
  if (slot === void 0) return;
221
221
  pending.delete(exec);
@@ -232,9 +232,9 @@ function createCacheMiddleware(options) {
232
232
  }
233
233
  return {
234
234
  name: "cache",
235
- intercept,
235
+ interceptQuery,
236
236
  onRow,
237
- afterExecute
237
+ afterQuery
238
238
  };
239
239
  }
240
240
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../3-extensions/middleware-cache/dist/index.mjs"],"sourcesContent":["import { defineAnnotation } from \"@internal/framework-components/runtime\";\n//#region src/cache-annotation.ts\n/**\n* Read-only annotation handle for the cache middleware.\n*\n* Declared with `applicableTo: ['read']`. Write terminals supply\n* `K = 'write'` to the type-level `ValidAnnotations<'write', As>` gate\n* (and the runtime `assertAnnotationsApplicable(annotations, 'write', ...)`\n* check); the join `K extends Kinds` fails for this annotation, making\n* \"cache a mutation\" structurally impossible without an `as any` cast\n* bypass at both type *and* runtime levels.\n*\n* Stored under namespace `'cache'` in `plan.meta.annotations`. The cache\n* middleware reads it via `cacheAnnotation.read(plan)`.\n*\n* @example\n* ```typescript\n* import { cacheAnnotation } from '@internal/middleware-cache';\n*\n* // ORM read terminal — accepts the read-only annotation via the meta callback.\n* const user = await db.User.first(\n* { id },\n* (meta) => meta.annotate(cacheAnnotation({ ttl: 60_000 })),\n* );\n*\n* // SQL DSL select builder — chainable.\n* const plan = db.sql\n* .from(tables.user)\n* .annotate(cacheAnnotation({ ttl: 60_000 }))\n* .select({ id: tables.user.columns.id })\n* .build();\n* ```\n*/\nconst cacheAnnotation = defineAnnotation()({\n\tnamespace: \"cache\",\n\tapplicableTo: [\"read\"]\n});\n//#endregion\n//#region src/cache-store.ts\n/**\n* Default cache backend. An LRU with per-entry TTL, backed by a `Map`.\n*\n* Eviction policy:\n*\n* - On `set` of a fresh key whose insertion would push the live count\n* above `maxEntries`, the least recently used entry is evicted.\n* Setting an existing key updates the entry in place and refreshes its\n* recency without changing the live count.\n* - On `get` of an existing key, recency is bumped (so the entry is no\n* longer the LRU candidate).\n* - On `get` of an expired entry, the entry is removed from the map and\n* `undefined` is returned. The slot becomes available for new writes\n* without counting against `maxEntries`.\n*\n* `Map` insertion order is the LRU order: the first key is the LRU\n* candidate; the last key is the most recently used. Bumping recency is\n* a delete-then-set on the underlying map.\n*\n* The default store is **not** coherent across processes or replicas —\n* each process holds its own Map. Users who need a shared cache supply\n* their own `CacheStore` (Redis, Memcached, etc.).\n*/\nfunction createInMemoryCacheStore(options) {\n\tconst maxEntries = options.maxEntries;\n\tconst clock = options.clock ?? Date.now;\n\tconst map = /* @__PURE__ */ new Map();\n\tfunction get(key) {\n\t\tconst record = map.get(key);\n\t\tif (record === void 0) return Promise.resolve(void 0);\n\t\tif (clock() >= record.expiresAt) {\n\t\t\tmap.delete(key);\n\t\t\treturn Promise.resolve(void 0);\n\t\t}\n\t\tmap.delete(key);\n\t\tmap.set(key, record);\n\t\treturn Promise.resolve(record.entry);\n\t}\n\tfunction set(key, entry, ttlMs) {\n\t\tconst expiresAt = clock() + ttlMs;\n\t\tif (map.has(key)) map.delete(key);\n\t\tmap.set(key, {\n\t\t\tentry,\n\t\t\texpiresAt\n\t\t});\n\t\twhile (map.size > maxEntries) {\n\t\t\tconst oldest = map.keys().next();\n\t\t\tif (oldest.done) break;\n\t\t\tmap.delete(oldest.value);\n\t\t}\n\t\treturn Promise.resolve();\n\t}\n\treturn {\n\t\tget,\n\t\tset\n\t};\n}\n//#endregion\n//#region src/cache-middleware.ts\n/**\n* Default `maxEntries` for the built-in in-memory store. Bounded so a\n* runaway producer cannot exhaust process memory; users who need\n* different bounds supply a custom `CacheStore`.\n*/\nconst DEFAULT_MAX_ENTRIES = 1e3;\n/**\n* Reads the cache payload from the plan, if present and branded.\n*\n* Returns `undefined` when:\n* - the plan has no `meta.annotations`, or\n* - the `cache` namespace key is absent, or\n* - the value under `cache` is not a branded `AnnotationValue` (the\n* `cacheAnnotation.read` defensive check covers this).\n*/\nfunction readCachePayload(plan) {\n\treturn cacheAnnotation.read(plan);\n}\n/**\n* Computes the cache key for an execution.\n*\n* Two-tier resolution:\n*\n* 1. Per-query override: `cacheAnnotation({ key })` — the supplied\n* string is used verbatim. Not rehashed; the user is responsible for\n* keeping the string bounded and free of sensitive data.\n* 2. Default: `ctx.contentHash(exec)` — the family runtime owns this and\n* returns an opaque, bounded digest (SHA-512 in the SQL and Mongo\n* runtimes today).\n*\n* The returned string is consumed directly as the `Map<string, …>` key\n* by the underlying `CacheStore`; the cache middleware does not perform\n* any further transformation.\n*/\nasync function resolveCacheKey(payload, exec, ctx) {\n\tif (payload.key !== void 0) return payload.key;\n\treturn ctx.contentHash(exec);\n}\n/**\n* Creates a family-agnostic caching middleware.\n*\n* The middleware uses three hooks:\n*\n* - `intercept` — on each execution, checks the cache. On a hit, returns\n* the cached raw rows; the runtime skips `runDriver` and `onRow`\n* (`beforeExecute` is not affected — it has already run for every\n* middleware before any `intercept` is consulted) and yields the\n* cached rows to the consumer (which, in the SQL runtime, sees them\n* after the standard `decodeRow` pass — i.e. the cache stores\n* wire-format values). On a miss, records a pending buffer keyed on\n* the `exec` object identity and returns `undefined` (passthrough).\n* - `onRow` — on the miss path, appends each row yielded by the driver\n* to the pending buffer.\n* - `afterExecute` — on the miss path, commits the buffer to the store\n* if and only if `result.completed === true && result.source === 'driver'`.\n* Failed executions and middleware-served executions never populate\n* the cache. The pending buffer is cleared in all branches so a stale\n* `WeakMap` entry cannot leak between executions sharing an `exec`.\n*\n* The middleware bypasses the cache entirely when:\n* - the plan has no `cache` annotation, or\n* - the annotation has `skip: true`, or\n* - the annotation has no `ttl`, or\n* - `ctx.scope !== 'runtime'` (connection / transaction scopes opt out).\n*\n* Returns a cross-family `RuntimeMiddleware` (no `familyId` /\n* `targetId`). The package depends only on\n* `@internal/framework-components/runtime`; cache keys come from\n* `ctx.contentHash(exec)`, populated by the family runtime, so SQL and\n* Mongo runtimes both work out of the box.\n*\n* @example\n* ```typescript\n* import { createCacheMiddleware, cacheAnnotation } from '@internal/middleware-cache';\n*\n* const db = postgres({\n* contractJson,\n* url: process.env['DATABASE_URL']!,\n* middleware: [createCacheMiddleware({ maxEntries: 1000 })],\n* });\n*\n* const user = await db.User.first(\n* { id },\n* (meta) => meta.annotate(cacheAnnotation({ ttl: 60_000 })),\n* );\n* ```\n*/\nfunction createCacheMiddleware(options) {\n\tconst store = options?.store ?? createInMemoryCacheStore({ maxEntries: options?.maxEntries ?? DEFAULT_MAX_ENTRIES });\n\tconst clock = options?.clock ?? Date.now;\n\tconst pending = /* @__PURE__ */ new WeakMap();\n\tasync function intercept(exec, ctx) {\n\t\tif (ctx.scope !== \"runtime\") return;\n\t\tconst payload = readCachePayload(exec);\n\t\tif (payload === void 0) return;\n\t\tif (payload.skip === true) return;\n\t\tif (payload.ttl === void 0) return;\n\t\tconst key = await resolveCacheKey(payload, exec, ctx);\n\t\tconst hit = await store.get(key);\n\t\tif (hit !== void 0) {\n\t\t\tctx.log.debug?.({\n\t\t\t\tevent: \"middleware.cache.hit\",\n\t\t\t\tmiddleware: \"cache\",\n\t\t\t\tkey\n\t\t\t});\n\t\t\treturn { rows: hit.rows };\n\t\t}\n\t\tpending.set(exec, {\n\t\t\tkey,\n\t\t\tttlMs: payload.ttl,\n\t\t\tbuffer: []\n\t\t});\n\t\tctx.log.debug?.({\n\t\t\tevent: \"middleware.cache.miss\",\n\t\t\tmiddleware: \"cache\",\n\t\t\tkey\n\t\t});\n\t}\n\tasync function onRow(row, exec, _ctx) {\n\t\tconst slot = pending.get(exec);\n\t\tif (slot === void 0) return;\n\t\tslot.buffer.push(row);\n\t}\n\tasync function afterExecute(exec, result, ctx) {\n\t\tconst slot = pending.get(exec);\n\t\tif (slot === void 0) return;\n\t\tpending.delete(exec);\n\t\tif (!result.completed || result.source !== \"driver\") return;\n\t\tawait store.set(slot.key, {\n\t\t\trows: slot.buffer,\n\t\t\tstoredAt: clock()\n\t\t}, slot.ttlMs);\n\t\tctx.log.debug?.({\n\t\t\tevent: \"middleware.cache.store\",\n\t\t\tmiddleware: \"cache\",\n\t\t\tkey: slot.key\n\t\t});\n\t}\n\treturn {\n\t\tname: \"cache\",\n\t\tintercept,\n\t\tonRow,\n\t\tafterExecute\n\t};\n}\n//#endregion\nexport { cacheAnnotation, createCacheMiddleware, createInMemoryCacheStore };\n\n//# sourceMappingURL=index.mjs.map"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../3-extensions/middleware-cache/dist/index.mjs"],"sourcesContent":["import { defineAnnotation } from \"@internal/framework-components/runtime\";\n//#region src/cache-annotation.ts\n/**\n* Read-only annotation handle for the cache middleware.\n*\n* Declared with `applicableTo: ['read']`. Write terminals supply\n* `K = 'write'` to the type-level `ValidAnnotations<'write', As>` gate\n* (and the runtime `assertAnnotationsApplicable(annotations, 'write', ...)`\n* check); the join `K extends Kinds` fails for this annotation, making\n* \"cache a mutation\" structurally impossible without an `as any` cast\n* bypass at both type *and* runtime levels.\n*\n* Stored under namespace `'cache'` in `plan.meta.annotations`. The cache\n* middleware reads it via `cacheAnnotation.read(plan)`.\n*\n* @example\n* ```typescript\n* import { cacheAnnotation } from '@internal/middleware-cache';\n*\n* // ORM read terminal — accepts the read-only annotation via the meta callback.\n* const user = await db.User.first(\n* { id },\n* (meta) => meta.annotate(cacheAnnotation({ ttl: 60_000 })),\n* );\n*\n* // SQL DSL select builder — chainable.\n* const plan = db.sql\n* .from(tables.user)\n* .annotate(cacheAnnotation({ ttl: 60_000 }))\n* .select({ id: tables.user.columns.id })\n* .build();\n* ```\n*/\nconst cacheAnnotation = defineAnnotation()({\n\tnamespace: \"cache\",\n\tapplicableTo: [\"read\"]\n});\n//#endregion\n//#region src/cache-store.ts\n/**\n* Default cache backend. An LRU with per-entry TTL, backed by a `Map`.\n*\n* Eviction policy:\n*\n* - On `set` of a fresh key whose insertion would push the live count\n* above `maxEntries`, the least recently used entry is evicted.\n* Setting an existing key updates the entry in place and refreshes its\n* recency without changing the live count.\n* - On `get` of an existing key, recency is bumped (so the entry is no\n* longer the LRU candidate).\n* - On `get` of an expired entry, the entry is removed from the map and\n* `undefined` is returned. The slot becomes available for new writes\n* without counting against `maxEntries`.\n*\n* `Map` insertion order is the LRU order: the first key is the LRU\n* candidate; the last key is the most recently used. Bumping recency is\n* a delete-then-set on the underlying map.\n*\n* The default store is **not** coherent across processes or replicas —\n* each process holds its own Map. Users who need a shared cache supply\n* their own `CacheStore` (Redis, Memcached, etc.).\n*/\nfunction createInMemoryCacheStore(options) {\n\tconst maxEntries = options.maxEntries;\n\tconst clock = options.clock ?? Date.now;\n\tconst map = /* @__PURE__ */ new Map();\n\tfunction get(key) {\n\t\tconst record = map.get(key);\n\t\tif (record === void 0) return Promise.resolve(void 0);\n\t\tif (clock() >= record.expiresAt) {\n\t\t\tmap.delete(key);\n\t\t\treturn Promise.resolve(void 0);\n\t\t}\n\t\tmap.delete(key);\n\t\tmap.set(key, record);\n\t\treturn Promise.resolve(record.entry);\n\t}\n\tfunction set(key, entry, ttlMs) {\n\t\tconst expiresAt = clock() + ttlMs;\n\t\tif (map.has(key)) map.delete(key);\n\t\tmap.set(key, {\n\t\t\tentry,\n\t\t\texpiresAt\n\t\t});\n\t\twhile (map.size > maxEntries) {\n\t\t\tconst oldest = map.keys().next();\n\t\t\tif (oldest.done) break;\n\t\t\tmap.delete(oldest.value);\n\t\t}\n\t\treturn Promise.resolve();\n\t}\n\treturn {\n\t\tget,\n\t\tset\n\t};\n}\n//#endregion\n//#region src/cache-middleware.ts\n/**\n* Default `maxEntries` for the built-in in-memory store. Bounded so a\n* runaway producer cannot exhaust process memory; users who need\n* different bounds supply a custom `CacheStore`.\n*/\nconst DEFAULT_MAX_ENTRIES = 1e3;\n/**\n* Reads the cache payload from the plan, if present and branded.\n*\n* Returns `undefined` when:\n* - the plan has no `meta.annotations`, or\n* - the `cache` namespace key is absent, or\n* - the value under `cache` is not a branded `AnnotationValue` (the\n* `cacheAnnotation.read` defensive check covers this).\n*/\nfunction readCachePayload(plan) {\n\treturn cacheAnnotation.read(plan);\n}\n/**\n* Computes the cache key for an execution.\n*\n* Two-tier resolution:\n*\n* 1. Per-query override: `cacheAnnotation({ key })` — the supplied\n* string is used verbatim. Not rehashed; the user is responsible for\n* keeping the string bounded and free of sensitive data.\n* 2. Default: `ctx.contentHash(exec)` — the family runtime owns this and\n* returns an opaque, bounded digest (SHA-512 in the SQL and Mongo\n* runtimes today).\n*\n* The returned string is consumed directly as the `Map<string, …>` key\n* by the underlying `CacheStore`; the cache middleware does not perform\n* any further transformation.\n*/\nasync function resolveCacheKey(payload, exec, ctx) {\n\tif (payload.key !== void 0) return payload.key;\n\treturn ctx.contentHash(exec);\n}\n/**\n* Creates a family-agnostic caching middleware.\n*\n* The middleware uses three hooks:\n*\n* - `interceptQuery` — on each execution, checks the cache. On a hit, returns\n* the cached raw rows; the runtime skips `runDriver` and `onRow`\n* (`beforeQuery` is not affected — it has already run for every\n* middleware before any `interceptQuery` is consulted) and yields the\n* cached rows to the consumer (which, in the SQL runtime, sees them\n* after the standard `decodeRow` pass — i.e. the cache stores\n* wire-format values). On a miss, records a pending buffer keyed on\n* the `exec` object identity and returns `undefined` (passthrough).\n* - `onRow` — on the miss path, appends each row yielded by the driver\n* to the pending buffer.\n* - `afterQuery` — on the miss path, commits the buffer to the store\n* if and only if `result.completed === true && result.source === 'driver'`.\n* Failed executions and middleware-served executions never populate\n* the cache. The pending buffer is cleared in all branches so a stale\n* `WeakMap` entry cannot leak between executions sharing an `exec`.\n*\n* The middleware bypasses the cache entirely when:\n* - the plan has no `cache` annotation, or\n* - the annotation has `skip: true`, or\n* - the annotation has no `ttl`, or\n* - `ctx.scope !== 'runtime'` (connection / transaction scopes opt out).\n*\n* Returns a cross-family `RuntimeMiddleware` (no `familyId` /\n* `targetId`). The package depends only on\n* `@internal/framework-components/runtime`; cache keys come from\n* `ctx.contentHash(exec)`, populated by the family runtime, so SQL and\n* Mongo runtimes both work out of the box.\n*\n* @example\n* ```typescript\n* import { createCacheMiddleware, cacheAnnotation } from '@internal/middleware-cache';\n*\n* const db = postgres({\n* contractJson,\n* url: process.env['DATABASE_URL']!,\n* middleware: [createCacheMiddleware({ maxEntries: 1000 })],\n* });\n*\n* const user = await db.User.first(\n* { id },\n* (meta) => meta.annotate(cacheAnnotation({ ttl: 60_000 })),\n* );\n* ```\n*/\nfunction createCacheMiddleware(options) {\n\tconst store = options?.store ?? createInMemoryCacheStore({ maxEntries: options?.maxEntries ?? DEFAULT_MAX_ENTRIES });\n\tconst clock = options?.clock ?? Date.now;\n\tconst pending = /* @__PURE__ */ new WeakMap();\n\tasync function interceptQuery(exec, ctx) {\n\t\tif (ctx.scope !== \"runtime\") return;\n\t\tconst payload = readCachePayload(exec);\n\t\tif (payload === void 0) return;\n\t\tif (payload.skip === true) return;\n\t\tif (payload.ttl === void 0) return;\n\t\tconst key = await resolveCacheKey(payload, exec, ctx);\n\t\tconst hit = await store.get(key);\n\t\tif (hit !== void 0) {\n\t\t\tctx.log.debug?.({\n\t\t\t\tevent: \"middleware.cache.hit\",\n\t\t\t\tmiddleware: \"cache\",\n\t\t\t\tkey\n\t\t\t});\n\t\t\treturn { rows: hit.rows };\n\t\t}\n\t\tpending.set(exec, {\n\t\t\tkey,\n\t\t\tttlMs: payload.ttl,\n\t\t\tbuffer: []\n\t\t});\n\t\tctx.log.debug?.({\n\t\t\tevent: \"middleware.cache.miss\",\n\t\t\tmiddleware: \"cache\",\n\t\t\tkey\n\t\t});\n\t}\n\tasync function onRow(row, exec, _ctx) {\n\t\tconst slot = pending.get(exec);\n\t\tif (slot === void 0) return;\n\t\tslot.buffer.push(row);\n\t}\n\tasync function afterQuery(exec, result, ctx) {\n\t\tconst slot = pending.get(exec);\n\t\tif (slot === void 0) return;\n\t\tpending.delete(exec);\n\t\tif (!result.completed || result.source !== \"driver\") return;\n\t\tawait store.set(slot.key, {\n\t\t\trows: slot.buffer,\n\t\t\tstoredAt: clock()\n\t\t}, slot.ttlMs);\n\t\tctx.log.debug?.({\n\t\t\tevent: \"middleware.cache.store\",\n\t\t\tmiddleware: \"cache\",\n\t\t\tkey: slot.key\n\t\t});\n\t}\n\treturn {\n\t\tname: \"cache\",\n\t\tinterceptQuery,\n\t\tonRow,\n\t\tafterQuery\n\t};\n}\n//#endregion\nexport { cacheAnnotation, createCacheMiddleware, createInMemoryCacheStore };\n\n//# sourceMappingURL=index.mjs.map"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/orm-extension-middleware-cache",
3
- "version": "8.0.0-rc.1-dev.41",
3
+ "version": "8.0.0-rc.1-dev.42",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -9,12 +9,12 @@
9
9
  "dist"
10
10
  ],
11
11
  "dependencies": {
12
- "@prisma/orm-framework": "8.0.0-rc.1-dev.41"
12
+ "@prisma/orm-framework": "8.0.0-rc.1-dev.42"
13
13
  },
14
14
  "devDependencies": {
15
- "@internal/middleware-cache": "8.0.0-rc.1-dev.41",
16
- "@repo/tsconfig": "8.0.0-rc.1-dev.41",
17
- "@repo/tsdown": "8.0.0-rc.1-dev.41",
15
+ "@internal/middleware-cache": "8.0.0-rc.1-dev.42",
16
+ "@repo/tsconfig": "8.0.0-rc.1-dev.42",
17
+ "@repo/tsdown": "8.0.0-rc.1-dev.42",
18
18
  "tsdown": "0.22.14",
19
19
  "typescript": "5.9.3"
20
20
  },