@aventara/core 0.1.0-pilot.4 → 1.0.0-rc.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.
Files changed (108) hide show
  1. package/CHANGELOG.md +338 -0
  2. package/README.md +100 -3
  3. package/dist/adapters/adapter-model.d.ts +8 -0
  4. package/dist/adapters/adapter.interface.d.ts +6 -2
  5. package/dist/compiler/contract-compiler-diagnostic.d.ts +6 -0
  6. package/dist/compiler/contract.compiler.js +67 -0
  7. package/dist/config/config.resolver.d.ts +3 -0
  8. package/dist/config/config.resolver.js +1 -0
  9. package/dist/config/config.validator.js +6 -0
  10. package/dist/config/define-fields.d.ts +10 -3
  11. package/dist/config/framework-config.d.ts +4 -1
  12. package/dist/config/framework-fields-config.d.ts +6 -0
  13. package/dist/contracts/client-contract-structure.validator.d.ts +21 -1
  14. package/dist/contracts/client-contract-structure.validator.js +99 -2
  15. package/dist/contracts/contract-canonicalizer.js +32 -0
  16. package/dist/contracts/contract.d.ts +7 -0
  17. package/dist/contracts/field.d.ts +10 -0
  18. package/dist/contracts/json-shape.d.ts +65 -0
  19. package/dist/contracts/json-shape.js +1 -0
  20. package/dist/contracts/reference-write-limit.d.ts +7 -0
  21. package/dist/contracts/reference-write-limit.js +1 -0
  22. package/dist/contracts/scalar-value-type.d.ts +12 -1
  23. package/dist/framework/contract-view.d.ts +126 -21
  24. package/dist/framework/framework.d.ts +5 -3
  25. package/dist/framework/framework.factory.js +56 -13
  26. package/dist/index.d.ts +8 -40
  27. package/dist/index.js +0 -20
  28. package/dist/internal.d.ts +35 -0
  29. package/dist/internal.js +16 -0
  30. package/dist/json/avz.d.ts +96 -0
  31. package/dist/json/avz.js +212 -0
  32. package/dist/json/index.d.ts +6 -0
  33. package/dist/json/index.js +1 -0
  34. package/dist/json/json-shape.registry.d.ts +22 -0
  35. package/dist/json/json-shape.registry.js +16 -0
  36. package/dist/json/zod-issue.translator.d.ts +51 -0
  37. package/dist/json/zod-issue.translator.js +142 -0
  38. package/dist/operations/operation-arguments.d.ts +151 -101
  39. package/dist/operations/operation-identity.d.ts +2 -0
  40. package/dist/operations/operation-identity.js +3 -0
  41. package/dist/operations/operation-validator-core.d.ts +1 -1
  42. package/dist/operations/operation-validator-core.js +28 -10
  43. package/dist/operations/operation-validator.d.ts +20 -5
  44. package/dist/operations/operation-validator.js +26 -10
  45. package/dist/operations/projection-scope.resolver.d.ts +1 -1
  46. package/dist/operations/structural-limits.walker.d.ts +19 -0
  47. package/dist/operations/structural-limits.walker.js +344 -0
  48. package/dist/protocol/absent-route.responder.d.ts +2 -1
  49. package/dist/protocol/absent-route.responder.js +32 -6
  50. package/dist/protocol/http-request.decoder.d.ts +6 -0
  51. package/dist/protocol/http-request.decoder.js +11 -3
  52. package/dist/protocol/protocol.binder.d.ts +4 -2
  53. package/dist/protocol/protocol.binder.js +3 -1
  54. package/dist/protocol/protocol.handler.d.ts +6 -2
  55. package/dist/protocol/protocol.handler.js +14 -4
  56. package/dist/protocol/unmounted-route.error.d.ts +19 -0
  57. package/dist/protocol/unmounted-route.error.js +10 -0
  58. package/dist/runtime/caller-data.copier.d.ts +59 -0
  59. package/dist/runtime/caller-data.copier.js +470 -0
  60. package/dist/runtime/caller-data.door.d.ts +56 -0
  61. package/dist/runtime/caller-data.door.js +65 -0
  62. package/dist/runtime/computed-read-runtime.js +3 -10
  63. package/dist/runtime/computed-value.error.d.ts +13 -0
  64. package/dist/runtime/computed-value.error.js +9 -0
  65. package/dist/runtime/create-requirement.d.ts +43 -7
  66. package/dist/runtime/create-requirement.js +89 -9
  67. package/dist/runtime/diagnostics-reporter.d.ts +20 -4
  68. package/dist/runtime/diagnostics-reporter.js +11 -4
  69. package/dist/runtime/error-boundary.d.ts +8 -0
  70. package/dist/runtime/error-boundary.js +65 -35
  71. package/dist/runtime/field-behavior.d.ts +2 -2
  72. package/dist/runtime/framework-error.d.ts +12 -0
  73. package/dist/runtime/misuse-reporter.d.ts +32 -0
  74. package/dist/runtime/misuse-reporter.js +19 -0
  75. package/dist/runtime/mutation-planner.js +13 -11
  76. package/dist/runtime/operation-request.d.ts +4 -11
  77. package/dist/runtime/operation-runtime.d.ts +14 -7
  78. package/dist/runtime/operation-runtime.js +37 -18
  79. package/dist/runtime/outcome-mapper.js +12 -2
  80. package/dist/runtime/output-validator.d.ts +10 -0
  81. package/dist/runtime/output-validator.js +11 -0
  82. package/dist/runtime/owned-execution.registry.d.ts +13 -0
  83. package/dist/runtime/owned-execution.registry.js +7 -0
  84. package/dist/runtime/pipeline.d.ts +19 -7
  85. package/dist/runtime/referential-action.d.ts +7 -0
  86. package/dist/runtime/request-envelope.validator.d.ts +5 -0
  87. package/dist/runtime/request-envelope.validator.js +118 -0
  88. package/dist/runtime/scalar-runtime.d.ts +30 -2
  89. package/dist/runtime/scalar-runtime.js +162 -79
  90. package/dist/transactions/transaction-plan-assembler.d.ts +5 -1
  91. package/dist/transactions/transaction-plan-assembler.js +74 -75
  92. package/dist/transactions/transaction-plan-validator.d.ts +30 -7
  93. package/dist/transactions/transaction-plan-validator.js +204 -153
  94. package/dist/transactions/transaction-reference-collector.d.ts +30 -0
  95. package/dist/transactions/transaction-reference-collector.js +204 -0
  96. package/dist/transactions/transaction-reference-resolver.js +10 -5
  97. package/dist/transactions/transaction-reference-validator.js +24 -17
  98. package/dist/transactions/transaction-reference.d.ts +23 -51
  99. package/dist/transactions/transaction-request.d.ts +3 -3
  100. package/dist/transactions/transaction-response.d.ts +6 -1
  101. package/dist/transactions/transaction-response.js +24 -1
  102. package/dist/transactions/transaction-runtime.d.ts +4 -2
  103. package/dist/transactions/transaction-runtime.js +4 -3
  104. package/package.json +17 -3
  105. package/dist/operations/operation-boolean-nodes.d.ts +0 -11
  106. package/dist/operations/operation-boolean-nodes.js +0 -283
  107. package/dist/operations/operation-nesting.d.ts +0 -8
  108. package/dist/operations/operation-nesting.js +0 -307
@@ -1,8 +1,9 @@
1
1
  import type { ProjectionScope, RelationMutationDirective, ScalarFilterOperator, ScalarMutationDirective, WriteScope } from "../contracts/capability.js";
2
2
  import type { Contract } from "../contracts/contract.js";
3
3
  import type { ProjectionScopes, ScalarField } from "../contracts/field.js";
4
- import type { ScalarAtom, ScalarForms, ScalarValueType } from "../contracts/scalar-value-type.js";
4
+ import type { FieldAtom, ScalarForms, ScalarValueType } from "../contracts/scalar-value-type.js";
5
5
  import type { JsonValue } from "../runtime/json-value.js";
6
+ import type { TransactionReference, TransactionSlotShape } from "../transactions/transaction-reference.js";
6
7
  import type { OperationIdentity } from "./operation-identity.js";
7
8
  /** The Resource names of Contract `C`. */
8
9
  export type ResourceKey<C extends Contract> = Extract<keyof C["resources"], string>;
@@ -69,7 +70,13 @@ type UnstatedForms = ScalarForms;
69
70
  /** Field `F`'s value under `Forms`, the same as in results. */
70
71
  type ScalarValueAt<C extends Contract, K extends ResourceKey<C>, F extends FieldKey<C, K>, Forms extends ScalarForms> = ScalarFieldAt<C, K, F> extends infer Field extends ScalarField<C> ? ScalarValueType<C, Field, Forms> : never;
71
72
  /** One entry of field `F`'s value — the whole value when `F` is not a list. */
72
- type ScalarAtomAt<C extends Contract, K extends ResourceKey<C>, F extends FieldKey<C, K>, Forms extends ScalarForms> = ScalarAtom<C, ScalarFieldAt<C, K, F>["type"], Forms>;
73
+ type ScalarAtomAt<C extends Contract, K extends ResourceKey<C>, F extends FieldKey<C, K>, Forms extends ScalarForms> = FieldAtom<C, ScalarFieldAt<C, K, F>, Forms>;
74
+ /**
75
+ * A `$ref` to a source whose value fits slot shape `Shape` of field `F`, typed in
76
+ * `ReferenceForms` — the forms a source's final data is read in. `never` when
77
+ * `ReferenceForms` is `never`: no position takes one outside a transaction plan.
78
+ */
79
+ type SlotReference<C extends Contract, K extends ResourceKey<C>, F extends FieldKey<C, K>, ReferenceForms extends ScalarForms, Shape extends TransactionSlotShape> = [ReferenceForms] extends [never] ? never : TransactionReference<Shape extends "value" ? ScalarValueAt<C, K, F, ReferenceForms> : Shape extends "nonNullValue" ? Exclude<ScalarValueAt<C, K, F, ReferenceForms>, null> : Shape extends "entry" ? ScalarAtomAt<C, K, F, ReferenceForms> : readonly ScalarAtomAt<C, K, F, ReferenceForms>[]>;
73
80
  /**
74
81
  * A JSON object. Positions that accept both a value and an object of operators
75
82
  * read a JSON object as the operators, so their value forms leave it out.
@@ -87,71 +94,92 @@ export type ScalarFilterInput = unknown | Readonly<Partial<Record<ScalarFilterOp
87
94
  * object.
88
95
  */
89
96
  type ScalarFilterShorthand<Value> = Exclude<Value, JsonObject>;
97
+ /**
98
+ * The `$ref` each slot shape of field `F` takes, by shape: every member `never`
99
+ * outside a transaction plan.
100
+ */
101
+ type FieldSlotReferences<C extends Contract, K extends ResourceKey<C>, F extends FieldKey<C, K>, ReferenceForms extends ScalarForms> = {
102
+ readonly [Shape in TransactionSlotShape]: SlotReference<C, K, F, ReferenceForms, Shape>;
103
+ };
90
104
  /**
91
105
  * A scalar filter's operator object: a comparison operand is the field's whole
92
106
  * value; `in`, `notIn`, `hasEvery` and `hasSome` take a list of entries; `has`
93
107
  * one entry; a `json` field's `string*`/`array*` operand a {@link JsonValue};
94
108
  * `not` a value or a nested operator object. `mode`, `isEmpty` and `path` are
95
- * passed to the adapter as given.
109
+ * passed to the adapter as given. Only `equals`, `not` and the `json` `array*`
110
+ * operands take `null`. Inside a transaction plan every operand but `mode`,
111
+ * `isEmpty`, `path` and the `json` ones may be a `$ref` of the shape it holds
112
+ * (`References`).
96
113
  */
97
- type ScalarFilterOperators<Value, Atom, Json> = Readonly<{
98
- equals?: Value;
99
- not?: Exclude<Value, JsonObject> | ScalarFilterOperators<Value, Atom, Json>;
100
- in?: readonly Atom[];
101
- notIn?: readonly Atom[];
102
- lt?: Value;
103
- lte?: Value;
104
- gt?: Value;
105
- gte?: Value;
106
- contains?: Value;
107
- startsWith?: Value;
108
- endsWith?: Value;
114
+ type ScalarFilterOperators<Value, Atom, Json, References extends Readonly<Record<TransactionSlotShape, unknown>>> = Readonly<{
115
+ equals?: Value | References["value"];
116
+ not?: Exclude<Value, JsonObject> | References["value"] | ScalarFilterOperators<Value, Atom, Json, References>;
117
+ in?: readonly Atom[] | References["entries"];
118
+ notIn?: readonly Atom[] | References["entries"];
119
+ lt?: Exclude<Value, null> | References["nonNullValue"];
120
+ lte?: Exclude<Value, null> | References["nonNullValue"];
121
+ gt?: Exclude<Value, null> | References["nonNullValue"];
122
+ gte?: Exclude<Value, null> | References["nonNullValue"];
123
+ contains?: Exclude<Value, null> | References["nonNullValue"];
124
+ startsWith?: Exclude<Value, null> | References["nonNullValue"];
125
+ endsWith?: Exclude<Value, null> | References["nonNullValue"];
109
126
  mode?: unknown;
110
- has?: Atom;
111
- hasEvery?: readonly Atom[];
112
- hasSome?: readonly Atom[];
127
+ has?: Atom | References["entry"];
128
+ hasEvery?: readonly Atom[] | References["entries"];
129
+ hasSome?: readonly Atom[] | References["entries"];
113
130
  isEmpty?: unknown;
114
131
  path?: unknown;
115
- stringContains?: Json;
116
- stringStartsWith?: Json;
117
- stringEndsWith?: Json;
132
+ stringContains?: Exclude<Json, null>;
133
+ stringStartsWith?: Exclude<Json, null>;
134
+ stringEndsWith?: Exclude<Json, null>;
118
135
  arrayContains?: Json;
119
136
  arrayStartsWith?: Json;
120
137
  arrayEndsWith?: Json;
121
138
  }>;
122
- /** One scalar field's filter under `Forms`: a shorthand value or an operator object. */
123
- type ScalarFilterFor<C extends Contract, K extends ResourceKey<C>, F extends FieldKey<C, K>, Forms extends ScalarForms> = ScalarFilterShorthand<ScalarValueAt<C, K, F, Forms>> | ScalarFilterOperators<ScalarValueAt<C, K, F, Forms>, ScalarAtomAt<C, K, F, Forms>, ScalarFieldAt<C, K, F>["type"] extends {
139
+ /**
140
+ * One scalar field's filter under `Forms`: a shorthand value or an operator object.
141
+ * Inside a transaction plan the value and every operand that holds a field value may
142
+ * be a `$ref` (see {@link TransactionSlotShape}): equality to a source nullable only
143
+ * where the field is, a comparison to a non-null source, `in` and the list operators
144
+ * to a list source, `has` to one entry.
145
+ */
146
+ type ScalarFilterFor<C extends Contract, K extends ResourceKey<C>, F extends FieldKey<C, K>, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = ScalarFilterShorthand<ScalarValueAt<C, K, F, Forms>> | SlotReference<C, K, F, ReferenceForms, "value"> | ScalarFilterOperators<ScalarValueAt<C, K, F, Forms>, ScalarAtomAt<C, K, F, Forms>, ScalarFieldAt<C, K, F>["type"] extends {
124
147
  readonly scalar: "json";
125
- } ? JsonValue : ScalarValueAt<C, K, F, Forms>>;
148
+ } ? JsonValue : ScalarValueAt<C, K, F, Forms>, FieldSlotReferences<C, K, F, ReferenceForms>>;
126
149
  type ScalarFilterableKey<C extends Contract, K extends ResourceKey<C>> = KeysWithCapability<C, K, ScalarFieldKeyOf<C, K>, "filter", UnrestrictedProjection>;
127
150
  type RelationParentFilterKey<C extends Contract, K extends ResourceKey<C>> = {
128
151
  [F in RelationFieldKeyOf<C, K>]: RelationCardinality<C, K, F> extends "one" ? HasCapabilityToken<C, K, F, "filter", UnrestrictedProjection, "match"> extends true ? F : HasCapabilityToken<C, K, F, "filter", UnrestrictedProjection, "null"> extends true ? F : never : Extract<CapabilityTokens<C, K, F, "filter", UnrestrictedProjection>, "some" | "every" | "none"> extends never ? never : F;
129
152
  }[RelationFieldKeyOf<C, K>];
130
- type ToManyRelationWhereInput<C extends Contract, K extends ResourceKey<C>, F extends RelationParentFilterKey<C, K> & RelationFieldKeyOf<C, K>, Forms extends ScalarForms> = Readonly<Partial<Record<Extract<CapabilityTokens<C, K, F, "filter", UnrestrictedProjection>, "some" | "every" | "none">, WhereInput<C, RelationTarget<C, K, F>, Forms>>>>;
131
- type RelationWhereInput<C extends Contract, K extends ResourceKey<C>, F extends RelationParentFilterKey<C, K> & RelationFieldKeyOf<C, K>, Forms extends ScalarForms> = RelationCardinality<C, K, F> extends "one" ? (HasCapabilityToken<C, K, F, "filter", UnrestrictedProjection, "match"> extends true ? WhereInput<C, RelationTarget<C, K, F>, Forms> : never) | (RelationNullable<C, K, F> extends true ? HasCapabilityToken<C, K, F, "filter", UnrestrictedProjection, "null"> extends true ? null : never : never) : ToManyRelationWhereInput<C, K, F, Forms>;
132
- type ScalarWhereFields<C extends Contract, K extends ResourceKey<C>, Forms extends ScalarForms> = Readonly<{
133
- [F in ScalarFilterableKey<C, K>]?: ScalarFilterFor<C, K, F, Forms>;
153
+ type ToManyRelationWhereInput<C extends Contract, K extends ResourceKey<C>, F extends RelationParentFilterKey<C, K> & RelationFieldKeyOf<C, K>, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = Readonly<Partial<Record<Extract<CapabilityTokens<C, K, F, "filter", UnrestrictedProjection>, "some" | "every" | "none">, WhereInput<C, RelationTarget<C, K, F>, Forms, ReferenceForms>>>>;
154
+ type RelationWhereInput<C extends Contract, K extends ResourceKey<C>, F extends RelationParentFilterKey<C, K> & RelationFieldKeyOf<C, K>, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = RelationCardinality<C, K, F> extends "one" ? (HasCapabilityToken<C, K, F, "filter", UnrestrictedProjection, "match"> extends true ? WhereInput<C, RelationTarget<C, K, F>, Forms, ReferenceForms> : never) | (RelationNullable<C, K, F> extends true ? HasCapabilityToken<C, K, F, "filter", UnrestrictedProjection, "null"> extends true ? null : never : never) : ToManyRelationWhereInput<C, K, F, Forms, ReferenceForms>;
155
+ type ScalarWhereFields<C extends Contract, K extends ResourceKey<C>, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = Readonly<{
156
+ [F in ScalarFilterableKey<C, K>]?: ScalarFilterFor<C, K, F, Forms, ReferenceForms>;
134
157
  }>;
135
- type RelationWhereFields<C extends Contract, K extends ResourceKey<C>, Forms extends ScalarForms> = Readonly<{
136
- [F in RelationParentFilterKey<C, K>]?: RelationWhereInput<C, K, F & RelationFieldKeyOf<C, K>, Forms>;
158
+ type RelationWhereFields<C extends Contract, K extends ResourceKey<C>, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = Readonly<{
159
+ [F in RelationParentFilterKey<C, K>]?: RelationWhereInput<C, K, F & RelationFieldKeyOf<C, K>, Forms, ReferenceForms>;
137
160
  }>;
138
161
  /**
139
162
  * A `where` argument. `Forms` is the scalar-form map its scalar values are read
140
- * through; see {@link UnstatedForms} for the default.
163
+ * through; see {@link UnstatedForms} for the default. `ReferenceForms` opens a
164
+ * transaction step's own `where` to `$ref`s, at every depth of it.
141
165
  */
142
- export type WhereInput<C extends Contract, K extends ResourceKey<C>, Forms extends ScalarForms = UnstatedForms> = Readonly<{
143
- AND?: readonly WhereInput<C, K, Forms>[];
144
- OR?: readonly WhereInput<C, K, Forms>[];
145
- NOT?: WhereInput<C, K, Forms>;
146
- } & ScalarWhereFields<C, K, Forms> & RelationWhereFields<C, K, Forms>>;
166
+ export type WhereInput<C extends Contract, K extends ResourceKey<C>, Forms extends ScalarForms = UnstatedForms, ReferenceForms extends ScalarForms = never> = Readonly<{
167
+ AND?: readonly WhereInput<C, K, Forms, ReferenceForms>[];
168
+ OR?: readonly WhereInput<C, K, Forms, ReferenceForms>[];
169
+ NOT?: WhereInput<C, K, Forms, ReferenceForms>;
170
+ } & ScalarWhereFields<C, K, Forms, ReferenceForms> & RelationWhereFields<C, K, Forms, ReferenceForms>>;
147
171
  type ActiveIdentifier<C extends Contract, K extends ResourceKey<C>> = C["resources"][K]["identifiers"][number];
148
- type IdentifierObject<C extends Contract, K extends ResourceKey<C>, I extends readonly string[], Forms extends ScalarForms> = Readonly<{
149
- [F in Extract<I[number], FieldKey<C, K>>]: ScalarValueAt<C, K, F, Forms>;
172
+ type IdentifierObject<C extends Contract, K extends ResourceKey<C>, I extends readonly string[], Forms extends ScalarForms, ReferenceForms extends ScalarForms> = Readonly<{
173
+ [F in Extract<I[number], FieldKey<C, K>>]: ScalarValueAt<C, K, F, Forms> | SlotReference<C, K, F, ReferenceForms, "nonNullValue">;
150
174
  } & {
151
175
  [F in Exclude<FieldKey<C, K>, I[number]>]?: never;
152
176
  }>;
153
- /** A `where` argument for a unique operation: the values of one identifier. */
154
- export type UniqueWhereInput<C extends Contract, K extends ResourceKey<C>, Forms extends ScalarForms = UnstatedForms> = ActiveIdentifier<C, K> extends infer I ? I extends readonly string[] ? IdentifierObject<C, K, I, Forms> : never : never;
177
+ /**
178
+ * A `where` argument for a unique operation: the values of one identifier. Inside a
179
+ * transaction plan (`ReferenceForms` stated) each value may be a `$ref` to a non-null
180
+ * source of the same type.
181
+ */
182
+ export type UniqueWhereInput<C extends Contract, K extends ResourceKey<C>, Forms extends ScalarForms = UnstatedForms, ReferenceForms extends ScalarForms = never> = ActiveIdentifier<C, K> extends infer I ? I extends readonly string[] ? IdentifierObject<C, K, I, Forms, ReferenceForms> : never : never;
155
183
  /** `"asc"` or `"desc"`. */
156
184
  export type SortDirection = "asc" | "desc";
157
185
  /** Where `null`s sort: `"first"` or `"last"`. */
@@ -184,9 +212,12 @@ export type OrderByEntry<C extends Contract, K extends ResourceKey<C>> = {
184
212
  }[OrderableFieldKey<C, K>];
185
213
  /** An `orderBy` argument: one entry, or a list in order of precedence. */
186
214
  export type OrderByInput<C extends Contract, K extends ResourceKey<C>> = OrderByEntry<C, K> | readonly OrderByEntry<C, K>[];
187
- /** A `cursor` argument: scalar field values of the row the page starts from. */
188
- export type CursorInput<C extends Contract, K extends ResourceKey<C>, Forms extends ScalarForms = UnstatedForms> = Readonly<{
189
- [F in ScalarFieldKeyOf<C, K>]?: ScalarValueAt<C, K, F, Forms>;
215
+ /**
216
+ * A `cursor` argument: scalar field values of the row the page starts from. Inside
217
+ * a transaction plan each may be a `$ref` to a non-null source of the same type.
218
+ */
219
+ export type CursorInput<C extends Contract, K extends ResourceKey<C>, Forms extends ScalarForms = UnstatedForms, ReferenceForms extends ScalarForms = never> = Readonly<{
220
+ [F in ScalarFieldKeyOf<C, K>]?: ScalarValueAt<C, K, F, Forms> | SlotReference<C, K, F, ReferenceForms, "nonNullValue">;
190
221
  }>;
191
222
  /** The scalar fields selectable at one projection position. */
192
223
  type SelectableScalarKey<C extends Contract, K extends ResourceKey<C>, Scope extends ProjectionScope> = KeysWithCapability<C, K, ScalarFieldKeyOf<C, K>, "select", Scope>;
@@ -264,35 +295,51 @@ type WritableScalarKey<C extends Contract, K extends ResourceKey<C>, Stage exten
264
295
  */
265
296
  export type WritableRelationKey<C extends Contract, K extends ResourceKey<C>, Stage extends "create" | "update", Scope extends WriteScope = UnrestrictedWrite> = KeysWithCapability<C, K, RelationFieldKeyOf<C, K>, Stage, Scope>;
266
297
  type ManyOrOne<C extends Contract, K extends ResourceKey<C>, F extends RelationFieldKeyOf<C, K>, Value> = RelationCardinality<C, K, F> extends "many" ? Value | readonly Value[] : Value;
267
- type ConnectOrCreateEntry<C extends Contract, K extends ResourceKey<C>, F extends RelationFieldKeyOf<C, K>, Forms extends ScalarForms> = Readonly<{
268
- where: UniqueWhereInput<C, RelationTarget<C, K, F>, Forms>;
269
- create: CreateDataInput<C, RelationTarget<C, K, F>, UnrestrictedWrite, Forms>;
298
+ /**
299
+ * A `where` of plain fields: each scalar field's filter, combined with
300
+ * `AND`/`OR`/`NOT`, and no relation filter at any depth.
301
+ */
302
+ type ScalarOnlyWhereInput<C extends Contract, K extends ResourceKey<C>, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = Readonly<{
303
+ AND?: readonly ScalarOnlyWhereInput<C, K, Forms, ReferenceForms>[];
304
+ OR?: readonly ScalarOnlyWhereInput<C, K, Forms, ReferenceForms>[];
305
+ NOT?: ScalarOnlyWhereInput<C, K, Forms, ReferenceForms>;
306
+ } & ScalarWhereFields<C, K, Forms, ReferenceForms>>;
307
+ /**
308
+ * The `where` of a nested `$update` or `$delete` through relation `F`: plain fields
309
+ * only where the relation states `nestedWriteFilter: "scalar"`, any filter otherwise.
310
+ */
311
+ type NestedWriteWhereInput<C extends Contract, K extends ResourceKey<C>, F extends RelationFieldKeyOf<C, K>, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = CapabilitiesOf<C, K, F> extends {
312
+ readonly nestedWriteFilter: "scalar";
313
+ } ? ScalarOnlyWhereInput<C, RelationTarget<C, K, F>, Forms, ReferenceForms> : WhereInput<C, RelationTarget<C, K, F>, Forms, ReferenceForms>;
314
+ type ConnectOrCreateEntry<C extends Contract, K extends ResourceKey<C>, F extends RelationFieldKeyOf<C, K>, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = Readonly<{
315
+ where: UniqueWhereInput<C, RelationTarget<C, K, F>, Forms, ReferenceForms>;
316
+ create: CreateDataInput<C, RelationTarget<C, K, F>, UnrestrictedWrite, Forms, ReferenceForms>;
270
317
  }>;
271
- type ToOneUpdateEntry<C extends Contract, K extends ResourceKey<C>, F extends RelationFieldKeyOf<C, K>, Forms extends ScalarForms> = Readonly<{
272
- where?: WhereInput<C, RelationTarget<C, K, F>, Forms>;
273
- data: UpdateDataInput<C, RelationTarget<C, K, F>, UnrestrictedWrite, Forms>;
318
+ type ToOneUpdateEntry<C extends Contract, K extends ResourceKey<C>, F extends RelationFieldKeyOf<C, K>, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = Readonly<{
319
+ where?: NestedWriteWhereInput<C, K, F, Forms, ReferenceForms>;
320
+ data: UpdateDataInput<C, RelationTarget<C, K, F>, UnrestrictedWrite, Forms, ReferenceForms>;
274
321
  }>;
275
- type ToManyUpdateEntry<C extends Contract, K extends ResourceKey<C>, F extends RelationFieldKeyOf<C, K>, Forms extends ScalarForms> = Readonly<{
276
- where: WhereInput<C, RelationTarget<C, K, F>, Forms>;
277
- data: UpdateDataInput<C, RelationTarget<C, K, F>, UnrestrictedWrite, Forms>;
322
+ type ToManyUpdateEntry<C extends Contract, K extends ResourceKey<C>, F extends RelationFieldKeyOf<C, K>, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = Readonly<{
323
+ where: NestedWriteWhereInput<C, K, F, Forms, ReferenceForms>;
324
+ data: UpdateDataInput<C, RelationTarget<C, K, F>, UnrestrictedWrite, Forms, ReferenceForms>;
278
325
  }>;
279
- type ToOneDeleteEntry<C extends Contract, K extends ResourceKey<C>, F extends RelationFieldKeyOf<C, K>, Forms extends ScalarForms> = true | Readonly<{
280
- where?: WhereInput<C, RelationTarget<C, K, F>, Forms>;
326
+ type ToOneDeleteEntry<C extends Contract, K extends ResourceKey<C>, F extends RelationFieldKeyOf<C, K>, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = true | Readonly<{
327
+ where?: NestedWriteWhereInput<C, K, F, Forms, ReferenceForms>;
281
328
  }>;
282
- type ToManyDeleteEntry<C extends Contract, K extends ResourceKey<C>, F extends RelationFieldKeyOf<C, K>, Forms extends ScalarForms> = Readonly<{
283
- where: WhereInput<C, RelationTarget<C, K, F>, Forms>;
329
+ type ToManyDeleteEntry<C extends Contract, K extends ResourceKey<C>, F extends RelationFieldKeyOf<C, K>, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = Readonly<{
330
+ where: NestedWriteWhereInput<C, K, F, Forms, ReferenceForms>;
284
331
  }>;
285
- type ToOneUpsertEntry<C extends Contract, K extends ResourceKey<C>, F extends RelationFieldKeyOf<C, K>, Forms extends ScalarForms> = Readonly<{
286
- where?: UniqueWhereInput<C, RelationTarget<C, K, F>, Forms>;
287
- create: CreateDataInput<C, RelationTarget<C, K, F>, UnrestrictedWrite, Forms>;
288
- update: UpdateDataInput<C, RelationTarget<C, K, F>, UnrestrictedWrite, Forms>;
332
+ type ToOneUpsertEntry<C extends Contract, K extends ResourceKey<C>, F extends RelationFieldKeyOf<C, K>, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = Readonly<{
333
+ where?: UniqueWhereInput<C, RelationTarget<C, K, F>, Forms, ReferenceForms>;
334
+ create: CreateDataInput<C, RelationTarget<C, K, F>, UnrestrictedWrite, Forms, ReferenceForms>;
335
+ update: UpdateDataInput<C, RelationTarget<C, K, F>, UnrestrictedWrite, Forms, ReferenceForms>;
289
336
  }>;
290
- type ToManyUpsertEntry<C extends Contract, K extends ResourceKey<C>, F extends RelationFieldKeyOf<C, K>, Forms extends ScalarForms> = Readonly<{
291
- where: UniqueWhereInput<C, RelationTarget<C, K, F>, Forms>;
292
- create: CreateDataInput<C, RelationTarget<C, K, F>, UnrestrictedWrite, Forms>;
293
- update: UpdateDataInput<C, RelationTarget<C, K, F>, UnrestrictedWrite, Forms>;
337
+ type ToManyUpsertEntry<C extends Contract, K extends ResourceKey<C>, F extends RelationFieldKeyOf<C, K>, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = Readonly<{
338
+ where: UniqueWhereInput<C, RelationTarget<C, K, F>, Forms, ReferenceForms>;
339
+ create: CreateDataInput<C, RelationTarget<C, K, F>, UnrestrictedWrite, Forms, ReferenceForms>;
340
+ update: UpdateDataInput<C, RelationTarget<C, K, F>, UnrestrictedWrite, Forms, ReferenceForms>;
294
341
  }>;
295
- type RelationDirectiveValue<C extends Contract, K extends ResourceKey<C>, F extends RelationFieldKeyOf<C, K>, Directive extends RelationMutationDirective, Forms extends ScalarForms> = Directive extends "$create" ? ManyOrOne<C, K, F, CreateDataInput<C, RelationTarget<C, K, F>, UnrestrictedWrite, Forms>> : Directive extends "$connect" ? ManyOrOne<C, K, F, UniqueWhereInput<C, RelationTarget<C, K, F>, Forms>> : Directive extends "$connectOrCreate" ? ManyOrOne<C, K, F, ConnectOrCreateEntry<C, K, F, Forms>> : Directive extends "$disconnect" ? RelationCardinality<C, K, F> extends "one" ? true : UniqueWhereInput<C, RelationTarget<C, K, F>, Forms> | readonly UniqueWhereInput<C, RelationTarget<C, K, F>, Forms>[] : Directive extends "$set" ? RelationCardinality<C, K, F> extends "one" ? UniqueWhereInput<C, RelationTarget<C, K, F>, Forms> | null : readonly UniqueWhereInput<C, RelationTarget<C, K, F>, Forms>[] : Directive extends "$update" ? RelationCardinality<C, K, F> extends "one" ? ToOneUpdateEntry<C, K, F, Forms> : ManyOrOne<C, K, F, ToManyUpdateEntry<C, K, F, Forms>> : Directive extends "$delete" ? RelationCardinality<C, K, F> extends "one" ? ToOneDeleteEntry<C, K, F, Forms> : ManyOrOne<C, K, F, ToManyDeleteEntry<C, K, F, Forms>> : Directive extends "$upsert" ? RelationCardinality<C, K, F> extends "one" ? ToOneUpsertEntry<C, K, F, Forms> : ManyOrOne<C, K, F, ToManyUpsertEntry<C, K, F, Forms>> : never;
342
+ type RelationDirectiveValue<C extends Contract, K extends ResourceKey<C>, F extends RelationFieldKeyOf<C, K>, Directive extends RelationMutationDirective, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = Directive extends "$create" ? ManyOrOne<C, K, F, CreateDataInput<C, RelationTarget<C, K, F>, UnrestrictedWrite, Forms, ReferenceForms>> : Directive extends "$connect" ? ManyOrOne<C, K, F, UniqueWhereInput<C, RelationTarget<C, K, F>, Forms, ReferenceForms>> : Directive extends "$connectOrCreate" ? ManyOrOne<C, K, F, ConnectOrCreateEntry<C, K, F, Forms, ReferenceForms>> : Directive extends "$disconnect" ? RelationCardinality<C, K, F> extends "one" ? true : UniqueWhereInput<C, RelationTarget<C, K, F>, Forms, ReferenceForms> | readonly UniqueWhereInput<C, RelationTarget<C, K, F>, Forms, ReferenceForms>[] : Directive extends "$set" ? RelationCardinality<C, K, F> extends "one" ? UniqueWhereInput<C, RelationTarget<C, K, F>, Forms, ReferenceForms> | null : readonly UniqueWhereInput<C, RelationTarget<C, K, F>, Forms, ReferenceForms>[] : Directive extends "$update" ? RelationCardinality<C, K, F> extends "one" ? ToOneUpdateEntry<C, K, F, Forms, ReferenceForms> : ManyOrOne<C, K, F, ToManyUpdateEntry<C, K, F, Forms, ReferenceForms>> : Directive extends "$delete" ? RelationCardinality<C, K, F> extends "one" ? ToOneDeleteEntry<C, K, F, Forms, ReferenceForms> : ManyOrOne<C, K, F, ToManyDeleteEntry<C, K, F, Forms, ReferenceForms>> : Directive extends "$upsert" ? RelationCardinality<C, K, F> extends "one" ? ToOneUpsertEntry<C, K, F, Forms, ReferenceForms> : ManyOrOne<C, K, F, ToManyUpsertEntry<C, K, F, Forms, ReferenceForms>> : never;
296
343
  declare const NO_ADVERTISED_MEMBER: unique symbol;
297
344
  /**
298
345
  * A write member that advertises nothing: it accepts `{}` and refuses every key.
@@ -302,8 +349,8 @@ type NoAdvertisedMember = {
302
349
  };
303
350
  /** `Write`, or {@link NoAdvertisedMember} when `Write` has no keys at all. */
304
351
  type RefuseEveryKeyWhenEmpty<Write> = [keyof Write] extends [never] ? NoAdvertisedMember : Write;
305
- type RelationMutationActionFor<C extends Contract, K extends ResourceKey<C>, F extends RelationFieldKeyOf<C, K>, Stage extends "create" | "update", Scope extends WriteScope, Forms extends ScalarForms> = RefuseEveryKeyWhenEmpty<Readonly<Partial<{
306
- [Directive in Extract<CapabilityTokens<C, K, F, Stage, Scope>, RelationMutationDirective>]: RelationDirectiveValue<C, K, F, Directive, Forms>;
352
+ type RelationMutationActionFor<C extends Contract, K extends ResourceKey<C>, F extends RelationFieldKeyOf<C, K>, Stage extends "create" | "update", Scope extends WriteScope, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = RefuseEveryKeyWhenEmpty<Readonly<Partial<{
353
+ [Directive in Extract<CapabilityTokens<C, K, F, Stage, Scope>, RelationMutationDirective>]: RelationDirectiveValue<C, K, F, Directive, Forms, ReferenceForms>;
307
354
  }>>>;
308
355
  /**
309
356
  * One scalar mutation directive's operand: one entry of the field (`$push` on a
@@ -322,103 +369,106 @@ type ScalarMutationActionFor<C extends Contract, K extends ResourceKey<C>, F ext
322
369
  }>;
323
370
  /**
324
371
  * Each writable scalar takes its value under `Forms`, or a directive object of
325
- * the directives it advertises at this stage.
372
+ * the directives it advertises at this stage — or, in a transaction plan's
373
+ * top-level data, a `$ref` to a source of the same type.
326
374
  */
327
- type ScalarMutationFields<C extends Contract, K extends ResourceKey<C>, Stage extends "create" | "update", Scope extends WriteScope, Forms extends ScalarForms> = Readonly<{
328
- [F in WritableScalarKey<C, K, Stage, Scope>]?: ScalarValueAt<C, K, F, Forms> | ScalarMutationActionFor<C, K, F, Stage, Scope, Forms>;
375
+ type ScalarMutationFields<C extends Contract, K extends ResourceKey<C>, Stage extends "create" | "update", Scope extends WriteScope, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = Readonly<{
376
+ [F in WritableScalarKey<C, K, Stage, Scope>]?: ScalarValueAt<C, K, F, Forms> | ScalarMutationActionFor<C, K, F, Stage, Scope, Forms> | SlotReference<C, K, F, ReferenceForms, "value">;
329
377
  }>;
330
- type RelationMutationFields<C extends Contract, K extends ResourceKey<C>, Stage extends "create" | "update", Scope extends WriteScope, Forms extends ScalarForms> = Readonly<{
331
- [F in WritableRelationKey<C, K, Stage, Scope>]?: RelationMutationActionFor<C, K, F & RelationFieldKeyOf<C, K>, Stage, Scope, Forms>;
378
+ type RelationMutationFields<C extends Contract, K extends ResourceKey<C>, Stage extends "create" | "update", Scope extends WriteScope, Forms extends ScalarForms, ReferenceForms extends ScalarForms> = Readonly<{
379
+ [F in WritableRelationKey<C, K, Stage, Scope>]?: RelationMutationActionFor<C, K, F & RelationFieldKeyOf<C, K>, Stage, Scope, Forms, ReferenceForms>;
332
380
  }>;
333
381
  /**
334
382
  * A `create` data argument, at one write position. A write position applies
335
383
  * to the top level only; nested writes use the unrestricted position (see
336
- * {@link WriteScope}).
384
+ * {@link WriteScope}). `ReferenceForms` opens a transaction plan's reference
385
+ * positions — its scalar values and the unique keys its relation directives name
386
+ * — and is never passed to nested data, which holds none.
337
387
  */
338
- export type CreateDataInput<C extends Contract, K extends ResourceKey<C>, Scope extends WriteScope = UnrestrictedWrite, Forms extends ScalarForms = UnstatedForms> = RefuseEveryKeyWhenEmpty<Readonly<ScalarMutationFields<C, K, "create", Scope, Forms> & RelationMutationFields<C, K, "create", Scope, Forms>>>;
388
+ export type CreateDataInput<C extends Contract, K extends ResourceKey<C>, Scope extends WriteScope = UnrestrictedWrite, Forms extends ScalarForms = UnstatedForms, ReferenceForms extends ScalarForms = never> = RefuseEveryKeyWhenEmpty<Readonly<ScalarMutationFields<C, K, "create", Scope, Forms, ReferenceForms> & RelationMutationFields<C, K, "create", Scope, Forms, ReferenceForms>>>;
339
389
  /** An `update` data argument, at one write position. See {@link CreateDataInput}. */
340
- export type UpdateDataInput<C extends Contract, K extends ResourceKey<C>, Scope extends WriteScope = UnrestrictedWrite, Forms extends ScalarForms = UnstatedForms> = RefuseEveryKeyWhenEmpty<Readonly<ScalarMutationFields<C, K, "update", Scope, Forms> & RelationMutationFields<C, K, "update", Scope, Forms>>>;
390
+ export type UpdateDataInput<C extends Contract, K extends ResourceKey<C>, Scope extends WriteScope = UnrestrictedWrite, Forms extends ScalarForms = UnstatedForms, ReferenceForms extends ScalarForms = never> = RefuseEveryKeyWhenEmpty<Readonly<ScalarMutationFields<C, K, "update", Scope, Forms, ReferenceForms> & RelationMutationFields<C, K, "update", Scope, Forms, ReferenceForms>>>;
341
391
  type QueryProjectionArgs<C extends Contract, K extends ResourceKey<C>, Scope extends ProjectionScope, Forms extends ScalarForms> = ProjectionChoice<C, K, Scope, false, Forms>;
342
392
  /**
343
393
  * Every standard operation's argument object, with each argument at its
344
394
  * projection or write position.
345
395
  */
346
- export type OperationArgumentMap<C extends Contract, K extends ResourceKey<C>, Forms extends ScalarForms = UnstatedForms> = {
396
+ export type OperationArgumentMap<C extends Contract, K extends ResourceKey<C>, Forms extends ScalarForms = UnstatedForms, ReferenceForms extends ScalarForms = never> = {
347
397
  find: {
348
398
  first: QueryProjectionArgs<C, K, "record", Forms> & {
349
- where?: WhereInput<C, K, Forms>;
399
+ where?: WhereInput<C, K, Forms, ReferenceForms>;
350
400
  orderBy?: OrderByInput<C, K>;
351
401
  };
352
402
  unique: QueryProjectionArgs<C, K, "record", Forms> & {
353
- where: UniqueWhereInput<C, K, Forms>;
403
+ where: UniqueWhereInput<C, K, Forms, ReferenceForms>;
354
404
  };
355
405
  many: QueryProjectionArgs<C, K, "record", Forms> & {
356
- where?: WhereInput<C, K, Forms>;
406
+ where?: WhereInput<C, K, Forms, ReferenceForms>;
357
407
  orderBy?: OrderByInput<C, K>;
358
408
  limit?: number;
359
409
  offset?: number;
360
- cursor?: CursorInput<C, K, Forms>;
410
+ cursor?: CursorInput<C, K, Forms, ReferenceForms>;
361
411
  };
362
412
  count: {
363
- where?: WhereInput<C, K, Forms>;
413
+ where?: WhereInput<C, K, Forms, ReferenceForms>;
364
414
  };
365
415
  };
366
416
  create: {
367
417
  one: QueryProjectionArgs<C, K, "record", Forms> & {
368
- data: CreateDataInput<C, K, "nested", Forms>;
418
+ data: CreateDataInput<C, K, "nested", Forms, ReferenceForms>;
369
419
  };
370
420
  many: QueryProjectionArgs<C, K, "bulkWrite", Forms> & {
371
- data: readonly CreateDataInput<C, K, "bulkWrite", Forms>[];
421
+ data: readonly CreateDataInput<C, K, "bulkWrite", Forms, ReferenceForms>[];
372
422
  };
373
423
  count: {
374
- data: readonly CreateDataInput<C, K, "bulkWrite", Forms>[];
424
+ data: readonly CreateDataInput<C, K, "bulkWrite", Forms, ReferenceForms>[];
375
425
  };
376
426
  };
377
427
  update: {
378
428
  first: QueryProjectionArgs<C, K, "record", Forms> & {
379
- where?: WhereInput<C, K, Forms>;
429
+ where?: WhereInput<C, K, Forms, ReferenceForms>;
380
430
  orderBy?: OrderByInput<C, K>;
381
- data: UpdateDataInput<C, K, "nested", Forms>;
431
+ data: UpdateDataInput<C, K, "nested", Forms, ReferenceForms>;
382
432
  };
383
433
  unique: QueryProjectionArgs<C, K, "record", Forms> & {
384
- where: UniqueWhereInput<C, K, Forms>;
385
- data: UpdateDataInput<C, K, "nested", Forms>;
434
+ where: UniqueWhereInput<C, K, Forms, ReferenceForms>;
435
+ data: UpdateDataInput<C, K, "nested", Forms, ReferenceForms>;
386
436
  };
387
437
  many: QueryProjectionArgs<C, K, "bulkWrite", Forms> & {
388
- where?: WhereInput<C, K, Forms>;
389
- data: UpdateDataInput<C, K, "bulkWrite", Forms>;
438
+ where?: WhereInput<C, K, Forms, ReferenceForms>;
439
+ data: UpdateDataInput<C, K, "bulkWrite", Forms, ReferenceForms>;
390
440
  };
391
441
  count: {
392
- where?: WhereInput<C, K, Forms>;
393
- data: UpdateDataInput<C, K, "bulkWrite", Forms>;
442
+ where?: WhereInput<C, K, Forms, ReferenceForms>;
443
+ data: UpdateDataInput<C, K, "bulkWrite", Forms, ReferenceForms>;
394
444
  };
395
445
  };
396
446
  delete: {
397
447
  first: QueryProjectionArgs<C, K, "record", Forms> & {
398
- where?: WhereInput<C, K, Forms>;
448
+ where?: WhereInput<C, K, Forms, ReferenceForms>;
399
449
  orderBy?: OrderByInput<C, K>;
400
450
  };
401
451
  unique: QueryProjectionArgs<C, K, "record", Forms> & {
402
- where: UniqueWhereInput<C, K, Forms>;
452
+ where: UniqueWhereInput<C, K, Forms, ReferenceForms>;
403
453
  };
404
454
  many: QueryProjectionArgs<C, K, "record", Forms> & {
405
- where?: WhereInput<C, K, Forms>;
455
+ where?: WhereInput<C, K, Forms, ReferenceForms>;
406
456
  };
407
457
  count: {
408
- where?: WhereInput<C, K, Forms>;
458
+ where?: WhereInput<C, K, Forms, ReferenceForms>;
409
459
  };
410
460
  };
411
461
  upsert: {
412
462
  unique: QueryProjectionArgs<C, K, "record", Forms> & {
413
- where: UniqueWhereInput<C, K, Forms>;
414
- create: CreateDataInput<C, K, "nested", Forms>;
415
- update: UpdateDataInput<C, K, "nested", Forms>;
463
+ where: UniqueWhereInput<C, K, Forms, ReferenceForms>;
464
+ create: CreateDataInput<C, K, "nested", Forms, ReferenceForms>;
465
+ update: UpdateDataInput<C, K, "nested", Forms, ReferenceForms>;
416
466
  };
417
467
  };
418
468
  };
419
469
  /** One standard operation's argument object. */
420
- export type OperationArgumentsFor<C extends Contract, K extends ResourceKey<C>, I extends OperationIdentity, Forms extends ScalarForms = UnstatedForms> = I extends {
470
+ export type OperationArgumentsFor<C extends Contract, K extends ResourceKey<C>, I extends OperationIdentity, Forms extends ScalarForms = UnstatedForms, ReferenceForms extends ScalarForms = never> = I extends {
421
471
  family: infer F;
422
472
  variant: infer V;
423
- } ? F extends keyof OperationArgumentMap<C, K, Forms> ? V extends keyof OperationArgumentMap<C, K, Forms>[F] ? OperationArgumentMap<C, K, Forms>[F][V] : never : never : never;
473
+ } ? F extends keyof OperationArgumentMap<C, K, Forms, ReferenceForms> ? V extends keyof OperationArgumentMap<C, K, Forms, ReferenceForms>[F] ? OperationArgumentMap<C, K, Forms, ReferenceForms>[F][V] : never : never : never;
424
474
  export {};
@@ -14,3 +14,5 @@ export type OperationIdentity = {
14
14
  }[OperationFamily];
15
15
  /** Parses a family and variant into an {@link OperationIdentity}, or `undefined` when they name no standard operation. */
16
16
  export declare function parseOperationIdentity(family: string, variant: string): OperationIdentity | undefined;
17
+ /** Whether `family` names one of the standard operation families. */
18
+ export declare function isOperationFamily(family: string): boolean;
@@ -23,3 +23,6 @@ export function parseOperationIdentity(family, variant) {
23
23
  }
24
24
  return { family, variant };
25
25
  }
26
+ export function isOperationFamily(family) {
27
+ return Object.keys(STANDARD_OPERATIONS).some((key) => key.startsWith(`${family}.`));
28
+ }
@@ -1,5 +1,5 @@
1
1
  import type { Contract } from "../contracts/contract.js";
2
- import type { OperationIdentity } from "./operation-identity.js";
2
+ import { type OperationIdentity } from "./operation-identity.js";
3
3
  import type { ValidationIssue } from "./validation-code.js";
4
4
  /** An operation's arguments as an object, before validation. */
5
5
  export type SemanticOperationArguments = Readonly<Record<string, unknown>>;
@@ -2,6 +2,7 @@ import { lookupField, lookupResource } from "../contracts/contract-lookup.js";
2
2
  import { advertisedDirectivesAt, advertisedReducers, advertisedReducersAt, holdsAt, } from "../contracts/field.js";
3
3
  import { isPlainRecord } from "../runtime/scalar-runtime.js";
4
4
  import { routeTransactionReferenceCandidate } from "../transactions/transaction-reference.js";
5
+ import { isOperationFamily, } from "./operation-identity.js";
5
6
  import { NESTED_PROJECTION_SCOPE, resolveProjectionScope, } from "./projection-scope.resolver.js";
6
7
  import { NESTED_WRITE_SCOPE, resolveWriteScope, } from "./write-scope.resolver.js";
7
8
  const OPERATION_ARGUMENT_RULES = {
@@ -111,7 +112,7 @@ function validateTopLevelArguments(request, args) {
111
112
  const rule = OPERATION_ARGUMENT_RULES[key];
112
113
  const issues = [];
113
114
  if (rule === undefined) {
114
- addIssue(issues, "V1001", ["operation"], `Operation "${key}" is not part of the standard operation grammar.`);
115
+ addIssue(issues, "V1001", [isOperationFamily(request.family) ? "variant" : "family"], `Operation "${key}" is not part of the standard operation grammar.`);
115
116
  return issues;
116
117
  }
117
118
  for (const required of rule.required) {
@@ -168,6 +169,13 @@ function validateScalarFilter(fieldName, field, value, path, state) {
168
169
  }
169
170
  return;
170
171
  }
172
+ if (state.transactionReferences === true &&
173
+ routeTransactionReferenceCandidate(value).kind !== "ordinary") {
174
+ if (!includes(supported, "equals")) {
175
+ addIssue(state.issues, "V1006", path, `Equality filtering is not available on field "${fieldName}".`);
176
+ }
177
+ return;
178
+ }
171
179
  if (!isPlainRecord(value)) {
172
180
  if (!includes(supported, "equals")) {
173
181
  addIssue(state.issues, "V1006", path, `Equality filtering is not available on field "${fieldName}".`);
@@ -270,6 +278,9 @@ function validateWhere(contract, resourceName, where, path, state, depth) {
270
278
  if (field.kind === "scalar") {
271
279
  validateScalarFilter(key, field, value, fieldPath, state);
272
280
  }
281
+ else if (state.scalarOnly === true) {
282
+ addIssue(state.issues, "V1006", fieldPath, `Relation filter "${key}" is not available here: a nested $update or $delete filter on this relation takes scalar fields only.`);
283
+ }
273
284
  else {
274
285
  validateRelationFilter(contract, key, field, value, fieldPath, state, depth);
275
286
  }
@@ -570,7 +581,7 @@ function validateScalarMutation(fieldName, field, value, stage, path, issues, tr
570
581
  }
571
582
  }
572
583
  }
573
- function validateRelationMutationEntry(contract, edge, directive, value, path, issues) {
584
+ function validateRelationMutationEntry(contract, edge, directive, value, path, issues, transactionReferences) {
574
585
  const target = edge.target;
575
586
  if (edge.cardinality === "one" && Array.isArray(value)) {
576
587
  addIssue(issues, "V1012", path, `To-one relation directive "${directive}" does not accept arrays.`);
@@ -619,7 +630,7 @@ function validateRelationMutationEntry(contract, edge, directive, value, path, i
619
630
  if (directive === "$create") {
620
631
  const entries = edge.cardinality === "many" && Array.isArray(value) ? value : [value];
621
632
  for (const entry of entries) {
622
- validateMutationData(contract, target, entry, "create", NESTED_WRITE_SCOPE, path, issues);
633
+ validateMutationData(contract, target, entry, "create", NESTED_WRITE_SCOPE, path, issues, transactionReferences);
623
634
  }
624
635
  return;
625
636
  }
@@ -631,7 +642,7 @@ function validateRelationMutationEntry(contract, edge, directive, value, path, i
631
642
  continue;
632
643
  }
633
644
  validateUniqueSelector(contract, target, entry.where, [...path, "where"], issues);
634
- validateMutationData(contract, target, entry.create, "create", NESTED_WRITE_SCOPE, [...path, "create"], issues);
645
+ validateMutationData(contract, target, entry.create, "create", NESTED_WRITE_SCOPE, [...path, "create"], issues, transactionReferences);
635
646
  }
636
647
  return;
637
648
  }
@@ -650,13 +661,15 @@ function validateRelationMutationEntry(contract, edge, directive, value, path, i
650
661
  const state = {
651
662
  issues,
652
663
  structuralRoot: [...path, "where"],
664
+ transactionReferences,
665
+ scalarOnly: edge.capabilities.nestedWriteFilter === "scalar",
653
666
  booleanFilterNodes: 0,
654
667
  nestingExceeded: false,
655
668
  booleanNodesExceeded: false,
656
669
  };
657
670
  validateWhere(contract, target, entry.where, [...path, "where"], state, 1);
658
671
  }
659
- validateMutationData(contract, target, entry.data, "update", NESTED_WRITE_SCOPE, [...path, "data"], issues);
672
+ validateMutationData(contract, target, entry.data, "update", NESTED_WRITE_SCOPE, [...path, "data"], issues, transactionReferences);
660
673
  }
661
674
  return;
662
675
  }
@@ -671,6 +684,8 @@ function validateRelationMutationEntry(contract, edge, directive, value, path, i
671
684
  const state = {
672
685
  issues,
673
686
  structuralRoot: [...path, "where"],
687
+ transactionReferences,
688
+ scalarOnly: edge.capabilities.nestedWriteFilter === "scalar",
674
689
  booleanFilterNodes: 0,
675
690
  nestingExceeded: false,
676
691
  booleanNodesExceeded: false,
@@ -685,6 +700,8 @@ function validateRelationMutationEntry(contract, edge, directive, value, path, i
685
700
  const state = {
686
701
  issues,
687
702
  structuralRoot: [...path, "where"],
703
+ transactionReferences,
704
+ scalarOnly: edge.capabilities.nestedWriteFilter === "scalar",
688
705
  booleanFilterNodes: 0,
689
706
  nestingExceeded: false,
690
707
  booleanNodesExceeded: false,
@@ -707,12 +724,12 @@ function validateRelationMutationEntry(contract, edge, directive, value, path, i
707
724
  if ("where" in entry) {
708
725
  validateUniqueSelector(contract, target, entry.where, [...path, "where"], issues);
709
726
  }
710
- validateMutationData(contract, target, entry.create, "create", NESTED_WRITE_SCOPE, [...path, "create"], issues);
711
- validateMutationData(contract, target, entry.update, "update", NESTED_WRITE_SCOPE, [...path, "update"], issues);
727
+ validateMutationData(contract, target, entry.create, "create", NESTED_WRITE_SCOPE, [...path, "create"], issues, transactionReferences);
728
+ validateMutationData(contract, target, entry.update, "update", NESTED_WRITE_SCOPE, [...path, "update"], issues, transactionReferences);
712
729
  }
713
730
  }
714
731
  }
715
- function validateRelationMutation(contract, fieldName, field, value, stage, scope, path, issues) {
732
+ function validateRelationMutation(contract, fieldName, field, value, stage, scope, path, issues, transactionReferences) {
716
733
  const directives = advertisedDirectivesAt(field.capabilities[stage], scope);
717
734
  if (directives === undefined) {
718
735
  addIssue(issues, "V1006", path, `Relation "${fieldName}" is not writable during ${stage}.`);
@@ -728,7 +745,7 @@ function validateRelationMutation(contract, fieldName, field, value, stage, scop
728
745
  addIssue(issues, "V1006", directivePath, `Relation mutation directive "${directive}" is not available on relation "${fieldName}".`);
729
746
  continue;
730
747
  }
731
- validateRelationMutationEntry(contract, field, directive, directiveValue, directivePath, issues);
748
+ validateRelationMutationEntry(contract, field, directive, directiveValue, directivePath, issues, transactionReferences);
732
749
  }
733
750
  }
734
751
  function validateMutationData(contract, resourceName, data, stage, scope, path, issues, transactionReferences = false) {
@@ -752,7 +769,7 @@ function validateMutationData(contract, resourceName, data, stage, scope, path,
752
769
  validateScalarMutation(fieldName, field, value, stage, fieldPath, issues, transactionReferences);
753
770
  }
754
771
  else {
755
- validateRelationMutation(contract, fieldName, field, value, stage, scope, fieldPath, issues);
772
+ validateRelationMutation(contract, fieldName, field, value, stage, scope, fieldPath, issues, transactionReferences);
756
773
  }
757
774
  }
758
775
  }
@@ -830,6 +847,7 @@ export function validateOperationArguments(contract, request, options) {
830
847
  const state = {
831
848
  issues,
832
849
  structuralRoot: ["arguments", "where"],
850
+ transactionReferences: options?.transactionReferences === true,
833
851
  booleanFilterNodes: 0,
834
852
  nestingExceeded: false,
835
853
  booleanNodesExceeded: false,