@prisma-next/sql-orm-client 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.
@@ -1,7 +1,9 @@
1
1
  import type { Contract, PlanMeta } from '@prisma-next/contract/types';
2
+ import type { AnnotationValue, OperationKind } from '@prisma-next/framework-components/runtime';
2
3
  import type { SqlStorage } from '@prisma-next/sql-contract/types';
3
4
  import { type AnyQueryAst, collectOrderedParamRefs } from '@prisma-next/sql-relational-core/ast';
4
5
  import type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';
6
+ import { ifDefined } from '@prisma-next/utils/defined';
5
7
 
6
8
  export function deriveParamsFromAst(ast: AnyQueryAst): {
7
9
  params: unknown[];
@@ -19,12 +21,20 @@ export function resolveTableColumns(contract: Contract<SqlStorage>, tableName: s
19
21
  return Object.keys(table.columns);
20
22
  }
21
23
 
22
- export function buildOrmPlanMeta(contract: Contract<SqlStorage>): PlanMeta {
24
+ export function buildOrmPlanMeta(
25
+ contract: Contract<SqlStorage>,
26
+ annotations?: ReadonlyMap<string, AnnotationValue<unknown, OperationKind>>,
27
+ ): PlanMeta {
28
+ const annotationRecord =
29
+ annotations !== undefined && annotations.size > 0
30
+ ? Object.freeze(Object.fromEntries(annotations))
31
+ : undefined;
23
32
  return {
24
33
  target: contract.target,
25
34
  targetFamily: contract.targetFamily,
26
35
  storageHash: contract.storage.storageHash,
27
- ...(contract.profileHash !== undefined ? { profileHash: contract.profileHash } : {}),
36
+ ...ifDefined('profileHash', contract.profileHash),
37
+ ...ifDefined('annotations', annotationRecord),
28
38
  lane: 'orm-client',
29
39
  };
30
40
  }
@@ -33,10 +43,54 @@ export function buildOrmQueryPlan<Row>(
33
43
  contract: Contract<SqlStorage>,
34
44
  ast: AnyQueryAst,
35
45
  params: readonly unknown[],
46
+ annotations?: ReadonlyMap<string, AnnotationValue<unknown, OperationKind>>,
36
47
  ): SqlQueryPlan<Row> {
37
48
  return Object.freeze({
38
49
  ast,
39
50
  params: [...params],
40
- meta: buildOrmPlanMeta(contract),
51
+ meta: buildOrmPlanMeta(contract, annotations),
52
+ });
53
+ }
54
+
55
+ /**
56
+ * Merges annotations into an existing `SqlQueryPlan`'s
57
+ * `meta.annotations` and returns a new frozen plan.
58
+ *
59
+ * Used by the ORM dispatch path to attach terminal-call annotations to
60
+ * plans produced by mutation compile functions (which don't take
61
+ * annotations as parameters). Reads compile through `compileSelect`-
62
+ * family functions that pass `state.annotations` directly to
63
+ * `buildOrmQueryPlan`; this helper is the alternate path for write
64
+ * terminals where annotations arrive at the call site, not via state.
65
+ *
66
+ * Returns the input plan unchanged when `annotations` is undefined
67
+ * or empty. Reserved framework namespaces (`codecs`, `limit`) on the
68
+ * input plan win over caller-supplied entries under the same key —
69
+ * see the reserved-namespace policy on `defineAnnotation`.
70
+ */
71
+ export function mergeAnnotations<Row>(
72
+ plan: SqlQueryPlan<Row>,
73
+ annotations: ReadonlyMap<string, AnnotationValue<unknown, OperationKind>> | undefined,
74
+ ): SqlQueryPlan<Row> {
75
+ if (annotations === undefined || annotations.size === 0) {
76
+ return plan;
77
+ }
78
+ const callerEntries: Record<string, AnnotationValue<unknown, OperationKind>> = {};
79
+ for (const [namespace, value] of annotations) {
80
+ callerEntries[namespace] = value;
81
+ }
82
+ // Caller-supplied annotations go first so framework-reserved keys on
83
+ // the existing plan (codecs, limit) override any collision under the
84
+ // same namespace.
85
+ const mergedAnnotations = Object.freeze({
86
+ ...callerEntries,
87
+ ...(plan.meta.annotations ?? {}),
88
+ });
89
+ return Object.freeze({
90
+ ...plan,
91
+ meta: Object.freeze({
92
+ ...plan.meta,
93
+ annotations: mergedAnnotations,
94
+ }),
41
95
  });
42
96
  }
@@ -23,6 +23,7 @@ import {
23
23
  } from '@prisma-next/sql-relational-core/ast';
24
24
  import { codecRefForStorageColumn } from '@prisma-next/sql-relational-core/codec-descriptor-registry';
25
25
  import type { SqlQueryPlan } from '@prisma-next/sql-relational-core/plan';
26
+ import { ifDefined } from '@prisma-next/utils/defined';
26
27
  import {
27
28
  type PolymorphismInfo,
28
29
  resolvePolymorphismInfo,
@@ -451,7 +452,7 @@ export function compileSelect(
451
452
  );
452
453
 
453
454
  const { params } = deriveParamsFromAst(ast);
454
- return buildOrmQueryPlan(contract, ast, params);
455
+ return buildOrmQueryPlan(contract, ast, params, state.annotations);
455
456
  }
456
457
 
457
458
  export function compileRelationSelect(
@@ -522,10 +523,10 @@ export function compileSelectWithIncludeStrategy(
522
523
  {
523
524
  joins: includeJoins,
524
525
  includeProjection,
525
- ...(topLevelWhere ? { where: topLevelWhere } : {}),
526
+ ...ifDefined('where', topLevelWhere),
526
527
  },
527
528
  );
528
529
 
529
530
  const { params } = deriveParamsFromAst(ast);
530
- return buildOrmQueryPlan(contract, ast, params);
531
+ return buildOrmQueryPlan(contract, ast, params, state.annotations);
531
532
  }
package/src/query-plan.ts CHANGED
@@ -2,6 +2,7 @@ export {
2
2
  compileAggregate,
3
3
  compileGroupedAggregate,
4
4
  } from './query-plan-aggregate';
5
+ export { mergeAnnotations } from './query-plan-meta';
5
6
  export {
6
7
  compileDeleteCount,
7
8
  compileDeleteReturning,
package/src/types.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { Contract } from '@prisma-next/contract/types';
2
+ import type { AnnotationValue, OperationKind } from '@prisma-next/framework-components/runtime';
2
3
  import type {
3
4
  ExtractCodecTypes,
4
5
  ExtractQueryOperationTypes,
@@ -72,6 +73,14 @@ export interface CollectionState {
72
73
  readonly limit: number | undefined;
73
74
  readonly offset: number | undefined;
74
75
  readonly variantName: string | undefined;
76
+ /**
77
+ * Annotations attached to this query at terminal-call time.
78
+ * Populated transiently by terminal methods (`first`, `all`, `create`,
79
+ * etc.) just before dispatch — `Collection` itself has no chainable
80
+ * `.annotate()`. Stored as a `Map<namespace, AnnotationValue>` so
81
+ * duplicate namespaces last-write-win. Empty on a fresh state.
82
+ */
83
+ readonly annotations: ReadonlyMap<string, AnnotationValue<unknown, OperationKind>>;
75
84
  }
76
85
 
77
86
  export function emptyState(): CollectionState {
@@ -86,6 +95,7 @@ export function emptyState(): CollectionState {
86
95
  limit: undefined,
87
96
  offset: undefined,
88
97
  variantName: undefined,
98
+ annotations: new Map(),
89
99
  };
90
100
  }
91
101