@prisma-next/framework-components 0.7.0 → 0.8.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/runtime.d.mts +336 -1
- package/dist/runtime.d.mts.map +1 -1
- package/dist/runtime.mjs +140 -1
- package/dist/runtime.mjs.map +1 -1
- package/package.json +9 -9
- package/src/annotations.ts +322 -0
- package/src/execution/runtime-middleware.ts +49 -0
- package/src/exports/runtime.ts +11 -0
- package/src/meta-builder.ts +101 -0
|
@@ -58,6 +58,27 @@ export interface RuntimeMiddlewareContext {
|
|
|
58
58
|
* cancels.
|
|
59
59
|
*/
|
|
60
60
|
readonly signal?: AbortSignal;
|
|
61
|
+
/**
|
|
62
|
+
* Identifies the queryable scope this execution is running under.
|
|
63
|
+
*
|
|
64
|
+
* - `'runtime'` — top-level `runtime.execute(plan)`. The default scope
|
|
65
|
+
* used by the standard read/write paths.
|
|
66
|
+
* - `'connection'` — `connection.execute(plan)` after
|
|
67
|
+
* `runtime.connection()` checked out a connection from the pool.
|
|
68
|
+
* - `'transaction'` — `transaction.execute(plan)` inside an explicit
|
|
69
|
+
* transaction, or a query routed through `withTransaction`.
|
|
70
|
+
*
|
|
71
|
+
* Middleware that should only act at the top level read this field to
|
|
72
|
+
* bypass non-runtime scopes. The cache middleware uses it to skip
|
|
73
|
+
* caching inside transactions (where read-after-write coherence is the
|
|
74
|
+
* caller's expectation) and dedicated connections (where the user has
|
|
75
|
+
* explicitly stepped outside the shared cache surface). Observers that
|
|
76
|
+
* don't care about the scope can ignore the field.
|
|
77
|
+
*
|
|
78
|
+
* Family runtimes populate this at context-construction time per
|
|
79
|
+
* scope. Existing middleware that ignore the field are unaffected.
|
|
80
|
+
*/
|
|
81
|
+
readonly scope: 'runtime' | 'connection' | 'transaction';
|
|
61
82
|
}
|
|
62
83
|
|
|
63
84
|
export interface AfterExecuteResult {
|
|
@@ -201,6 +222,33 @@ export interface RuntimeMiddleware<
|
|
|
201
222
|
): Promise<void>;
|
|
202
223
|
}
|
|
203
224
|
|
|
225
|
+
/**
|
|
226
|
+
* Cross-family middleware — one that doesn't constrain `familyId` or
|
|
227
|
+
* `targetId` and is therefore compatible with any family runtime's
|
|
228
|
+
* middleware array (`SqlMiddleware[]`, `MongoMiddleware[]`, etc.).
|
|
229
|
+
*
|
|
230
|
+
* The intersection `RuntimeMiddleware & { familyId?: undefined; targetId?: undefined }`
|
|
231
|
+
* pins both optional properties to exactly `undefined` (intersecting
|
|
232
|
+
* `string | undefined` with `undefined` collapses to `undefined`). Under
|
|
233
|
+
* `exactOptionalPropertyTypes: true`, the plain `RuntimeMiddleware` shape
|
|
234
|
+
* — with `familyId?: string` — is *not* assignable to `SqlMiddleware`
|
|
235
|
+
* (which narrows `familyId?: 'sql'`) because `string` is wider than
|
|
236
|
+
* `'sql'`. Pinning the property to `undefined` makes the value a subtype
|
|
237
|
+
* of every narrowed variant: `undefined` extends both `'sql' | undefined`
|
|
238
|
+
* and `'mongo' | undefined`, so a `CrossFamilyMiddleware` value drops
|
|
239
|
+
* into a SQL or Mongo middleware slot without a cast.
|
|
240
|
+
*
|
|
241
|
+
* Cross-family middleware factories (`createCacheMiddleware`, future
|
|
242
|
+
* `audit` / OTel middleware) declare this as their return type so the
|
|
243
|
+
* cross-family typing is named once rather than re-spelled at every call
|
|
244
|
+
* site.
|
|
245
|
+
*/
|
|
246
|
+
export type CrossFamilyMiddleware<TPlan extends QueryPlan = QueryPlan> =
|
|
247
|
+
RuntimeMiddleware<TPlan> & {
|
|
248
|
+
readonly familyId?: undefined;
|
|
249
|
+
readonly targetId?: undefined;
|
|
250
|
+
};
|
|
251
|
+
|
|
204
252
|
/**
|
|
205
253
|
* Optional per-`execute` options accepted by every family runtime.
|
|
206
254
|
*
|
|
@@ -212,6 +260,7 @@ export interface RuntimeMiddleware<
|
|
|
212
260
|
*/
|
|
213
261
|
export interface RuntimeExecuteOptions {
|
|
214
262
|
readonly signal?: AbortSignal;
|
|
263
|
+
readonly scope?: 'runtime' | 'connection' | 'transaction';
|
|
215
264
|
}
|
|
216
265
|
|
|
217
266
|
/**
|
package/src/exports/runtime.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
export type {
|
|
2
|
+
AnnotationHandle,
|
|
3
|
+
AnnotationValue,
|
|
4
|
+
DefineAnnotationOptions,
|
|
5
|
+
OperationKind,
|
|
6
|
+
ValidAnnotations,
|
|
7
|
+
} from '../annotations';
|
|
8
|
+
export { assertAnnotationsApplicable, defineAnnotation } from '../annotations';
|
|
1
9
|
export { AsyncIterableResult } from '../execution/async-iterable-result';
|
|
2
10
|
export { runBeforeExecuteChain } from '../execution/before-execute-chain';
|
|
3
11
|
export type { ExecutionPlan, QueryPlan, ResultType } from '../execution/query-plan';
|
|
@@ -14,6 +22,7 @@ export {
|
|
|
14
22
|
} from '../execution/runtime-error';
|
|
15
23
|
export type {
|
|
16
24
|
AfterExecuteResult,
|
|
25
|
+
CrossFamilyMiddleware,
|
|
17
26
|
InterceptResult,
|
|
18
27
|
ParamRefMutator,
|
|
19
28
|
RuntimeExecuteOptions,
|
|
@@ -23,3 +32,5 @@ export type {
|
|
|
23
32
|
RuntimeMiddlewareContext,
|
|
24
33
|
} from '../execution/runtime-middleware';
|
|
25
34
|
export { checkMiddlewareCompatibility } from '../execution/runtime-middleware';
|
|
35
|
+
export type { LaneMetaBuilder, MetaBuilder } from '../meta-builder';
|
|
36
|
+
export { createMetaBuilder } from '../meta-builder';
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AnnotationValue,
|
|
3
|
+
assertAnnotationsApplicable,
|
|
4
|
+
type OperationKind,
|
|
5
|
+
} from './annotations';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Per-terminal meta configurator handed to user callbacks. The terminal's
|
|
9
|
+
* operation kind `K` is fixed by the terminal that constructed the builder;
|
|
10
|
+
* `annotate(...)` accepts only annotations whose declared `Kinds` include
|
|
11
|
+
* `K`.
|
|
12
|
+
*
|
|
13
|
+
* The conditional parameter type
|
|
14
|
+
* `K extends Kinds ? AnnotationValue<P, Kinds> : never` collapses to `never`
|
|
15
|
+
* for inapplicable annotations, surfacing the mismatch as a type error at
|
|
16
|
+
* the call site of `meta.annotate(...)`. No variadic-tuple inference is
|
|
17
|
+
* involved — TypeScript infers `Kinds` from the annotation argument and
|
|
18
|
+
* checks the conditional directly.
|
|
19
|
+
*
|
|
20
|
+
* The runtime gate inside `annotate` (via
|
|
21
|
+
* `assertAnnotationsApplicable`) catches cast / `any` / dynamic bypasses
|
|
22
|
+
* and throws `RUNTIME.ANNOTATION_INAPPLICABLE`.
|
|
23
|
+
*
|
|
24
|
+
* `annotate` returns the builder for chaining; the return value of the
|
|
25
|
+
* configurator callback is unused, so both block-body and expression-body
|
|
26
|
+
* callbacks compile.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* await db.User.find({ id }, (meta) => meta.annotate(cacheAnnotation({ ttl: 60 })));
|
|
31
|
+
* await db.User.create(input, (meta) => {
|
|
32
|
+
* meta.annotate(auditAnnotation({ actor: 'system' }));
|
|
33
|
+
* meta.annotate(otelAnnotation({ traceId }));
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export interface MetaBuilder<K extends OperationKind> {
|
|
38
|
+
annotate<P, Kinds extends OperationKind>(
|
|
39
|
+
annotation: K extends Kinds ? AnnotationValue<P, Kinds> : never,
|
|
40
|
+
): this;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Lane-side view of a meta builder. Extends the public `MetaBuilder<K>`
|
|
45
|
+
* surface with `annotations` so lane terminals can read the recorded map
|
|
46
|
+
* after invoking the user configurator.
|
|
47
|
+
*
|
|
48
|
+
* Lane terminals construct one of these via `createMetaBuilder(kind, terminalName)`,
|
|
49
|
+
* pass it to the user callback as `MetaBuilder<K>` (the narrower public
|
|
50
|
+
* view), then read `meta.annotations` to thread the recorded values into
|
|
51
|
+
* `plan.meta.annotations`.
|
|
52
|
+
*/
|
|
53
|
+
export interface LaneMetaBuilder<K extends OperationKind> extends MetaBuilder<K> {
|
|
54
|
+
readonly annotations: ReadonlyMap<string, AnnotationValue<unknown, OperationKind>>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
class MetaBuilderImpl<K extends OperationKind> implements LaneMetaBuilder<K> {
|
|
58
|
+
readonly #kind: K;
|
|
59
|
+
readonly #terminalName: string;
|
|
60
|
+
readonly #annotations = new Map<string, AnnotationValue<unknown, OperationKind>>();
|
|
61
|
+
|
|
62
|
+
constructor(kind: K, terminalName: string) {
|
|
63
|
+
this.#kind = kind;
|
|
64
|
+
this.#terminalName = terminalName;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
get annotations(): ReadonlyMap<string, AnnotationValue<unknown, OperationKind>> {
|
|
68
|
+
return this.#annotations;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
annotate<P, Kinds extends OperationKind>(
|
|
72
|
+
annotation: K extends Kinds ? AnnotationValue<P, Kinds> : never,
|
|
73
|
+
): this {
|
|
74
|
+
// Inside the body, the conditional `K extends Kinds ? AnnotationValue<P, Kinds> : never`
|
|
75
|
+
// is opaque to TypeScript — it can't pick a branch without a concrete
|
|
76
|
+
// K. Widen to the structural shape so we can call into the runtime
|
|
77
|
+
// gate. The runtime gate (assertAnnotationsApplicable) is what
|
|
78
|
+
// catches cast bypasses where the conditional would have resolved to
|
|
79
|
+
// `never` had the type checker been allowed to specialise.
|
|
80
|
+
const value = annotation as AnnotationValue<unknown, OperationKind>;
|
|
81
|
+
assertAnnotationsApplicable([value], this.#kind, this.#terminalName);
|
|
82
|
+
this.#annotations.set(value.namespace, value);
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Construct a lane-side meta builder for a terminal of operation kind `K`.
|
|
89
|
+
*
|
|
90
|
+
* Lane terminals call this with their `kind` (`'read'` or `'write'`) and a
|
|
91
|
+
* `terminalName` for error messages, hand the resulting builder to the
|
|
92
|
+
* user-supplied configurator callback (typed as `MetaBuilder<K>`, the
|
|
93
|
+
* narrower public view), and read `meta.annotations` afterwards to thread
|
|
94
|
+
* the recorded values into `plan.meta.annotations`.
|
|
95
|
+
*/
|
|
96
|
+
export function createMetaBuilder<K extends OperationKind>(
|
|
97
|
+
kind: K,
|
|
98
|
+
terminalName: string,
|
|
99
|
+
): LaneMetaBuilder<K> {
|
|
100
|
+
return new MetaBuilderImpl(kind, terminalName);
|
|
101
|
+
}
|