@loradb/lora-graphql 0.23.0 → 0.24.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 (85) hide show
  1. package/README.md +42 -10
  2. package/dist/analyze/access-lint.d.ts +7 -0
  3. package/dist/analyze/access.d.ts +2 -30
  4. package/dist/analyze/assert-schema.d.ts +19 -0
  5. package/dist/analyze/diff.d.ts +1 -1
  6. package/dist/analyze/plans.d.ts +1 -1
  7. package/dist/analyze/principals.d.ts +25 -0
  8. package/dist/analyze/verdicts.d.ts +45 -0
  9. package/dist/cli.js +2 -2
  10. package/dist/cli.js.map +1 -1
  11. package/dist/compile/cache.d.ts +1 -1
  12. package/dist/compile/read/abstract.d.ts +19 -0
  13. package/dist/compile/read/aggregates.d.ts +34 -0
  14. package/dist/compile/read/by-keys.d.ts +10 -0
  15. package/dist/compile/read/common.d.ts +30 -0
  16. package/dist/compile/read/connection.d.ts +26 -0
  17. package/dist/compile/read/cypher-field.d.ts +21 -0
  18. package/dist/compile/read/match.d.ts +11 -0
  19. package/dist/compile/read/project.d.ts +15 -0
  20. package/dist/compile/read/root.d.ts +5 -0
  21. package/dist/compile/read/search.d.ts +16 -0
  22. package/dist/compile/read/sort.d.ts +42 -0
  23. package/dist/compile/read/types.d.ts +61 -0
  24. package/dist/{diff-B9iDdELF.js → diff-aDivqYqr.js} +2 -2
  25. package/dist/diff-aDivqYqr.js.map +1 -0
  26. package/dist/{driver-C5dSoSD8.js → driver-DSnytFsU.js} +11092 -10674
  27. package/dist/driver-DSnytFsU.js.map +1 -0
  28. package/dist/execute/cypher-mutation.d.ts +1 -1
  29. package/dist/execute/mutate/adjust.d.ts +5 -0
  30. package/dist/execute/mutate/checks.d.ts +21 -0
  31. package/dist/execute/mutate/delete.d.ts +7 -0
  32. package/dist/execute/mutate/env.d.ts +73 -0
  33. package/dist/execute/mutate/errors.d.ts +7 -0
  34. package/dist/execute/mutate/fragments.d.ts +37 -0
  35. package/dist/execute/mutate/plan.d.ts +95 -0
  36. package/dist/execute/mutate/runner.d.ts +129 -0
  37. package/dist/execute/mutate/unique-together.d.ts +18 -0
  38. package/dist/execute/mutate.d.ts +2 -79
  39. package/dist/index.d.ts +10 -6
  40. package/dist/index.js +2 -2
  41. package/dist/lora-graphql.d.ts +12 -326
  42. package/dist/model/build/authorization.d.ts +31 -0
  43. package/dist/model/build/cypher-field.d.ts +11 -0
  44. package/dist/model/build/directive-args.d.ts +16 -0
  45. package/dist/model/build/limits.d.ts +18 -0
  46. package/dist/model/build/relationship-field.d.ts +11 -0
  47. package/dist/model/build/rule-checks.d.ts +23 -0
  48. package/dist/model/build/rule-references.d.ts +28 -0
  49. package/dist/model/build/scalar-field.d.ts +7 -0
  50. package/dist/model/build/search.d.ts +4 -0
  51. package/dist/model/build/shapes.d.ts +18 -0
  52. package/dist/model/build/unique-together.d.ts +5 -0
  53. package/dist/model/build.d.ts +3 -20
  54. package/dist/model/directives.d.ts +1 -1
  55. package/dist/model/types.d.ts +12 -2
  56. package/dist/options.d.ts +255 -0
  57. package/dist/results.d.ts +70 -0
  58. package/dist/runtime/custom-requires.d.ts +7 -0
  59. package/dist/runtime/database-error.d.ts +10 -0
  60. package/dist/runtime/documents.d.ts +35 -0
  61. package/dist/runtime/limits.d.ts +37 -0
  62. package/dist/runtime/request.d.ts +33 -0
  63. package/dist/runtime/timing.d.ts +13 -0
  64. package/dist/schema/aggregates.d.ts +17 -0
  65. package/dist/schema/base.d.ts +17 -0
  66. package/dist/schema/build.d.ts +3 -42
  67. package/dist/schema/filters.d.ts +10 -0
  68. package/dist/schema/hooks.d.ts +42 -0
  69. package/dist/schema/objects.d.ts +36 -0
  70. package/dist/schema/query.d.ts +8 -0
  71. package/dist/schema/resolvers.d.ts +13 -0
  72. package/dist/schema/sorts.d.ts +5 -0
  73. package/dist/schema/subscriptions.d.ts +7 -0
  74. package/dist/subscriptions/change-hub.d.ts +50 -0
  75. package/dist/subscriptions/compiles.d.ts +23 -0
  76. package/dist/subscriptions/events.d.ts +9 -0
  77. package/dist/subscriptions/filter-cost.d.ts +15 -0
  78. package/dist/subscriptions/subscriptions.d.ts +73 -0
  79. package/dist/testing.d.ts +4 -1
  80. package/dist/testing.js +34 -29
  81. package/dist/testing.js.map +1 -1
  82. package/package.json +2 -2
  83. package/dist/compile/read.d.ts +0 -102
  84. package/dist/diff-B9iDdELF.js.map +0 -1
  85. package/dist/driver-C5dSoSD8.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import { FieldNode } from 'graphql';
2
2
  import { Statement } from '../driver.js';
3
3
  import { CypherField } from '../model/types.js';
4
- import { MutationEnv } from './mutate.js';
4
+ import { MutationEnv } from './mutate/env.js';
5
5
  export declare function executeCypherMutation(env: MutationEnv, field: CypherField, args: Record<string, unknown>, fieldNodes: readonly FieldNode[],
6
6
  /** The field's `viewer` rules, checked in the transaction first. */
7
7
  guard?: Statement): Promise<unknown>;
@@ -0,0 +1,5 @@
1
+ import { CompileContext } from '../../compile/context.js';
2
+ import { NodeType } from '../../model/types.js';
3
+ import { Input } from './plan.js';
4
+ /** `n.p = <expr>` assignments for an `adjust` input. */
5
+ export declare function compileAdjust(ctx: CompileContext, node: NodeType, adjust: Input | undefined): string[];
@@ -0,0 +1,21 @@
1
+ import { WritePlan } from './plan.js';
2
+ import { Runner } from './runner.js';
3
+ /**
4
+ * A single relationship field allows one related node, whichever side
5
+ * the relationship was created from. Check every node that gained one.
6
+ */
7
+ export declare function checkCardinality(runner: Runner, plan: WritePlan): Promise<void>;
8
+ /**
9
+ * `@uniqueTogether`: after every write of the mutation, no two nodes of
10
+ * a constrained type share a combination. Checks the nodes it created
11
+ * or updated and the nodes whose constrained relationships it connected
12
+ * or disconnected, from either side, against the nodes reachable
13
+ * through the same ends. For every caller: a data invariant, not a
14
+ * rule, so the bypass does not skip it.
15
+ */
16
+ export declare function checkUniqueTogether(runner: Runner): Promise<void>;
17
+ /**
18
+ * A required single relationship stays set: disconnecting from the other
19
+ * side must not leave the owner without one.
20
+ */
21
+ export declare function checkRequired(runner: Runner): Promise<void>;
@@ -0,0 +1,7 @@
1
+ import { NodeType } from '../../model/types.js';
2
+ import { Runner } from './runner.js';
3
+ /**
4
+ * Delete nodes by key, with what `onDelete: CASCADE` reaches. Checks
5
+ * BEFORE rules, RESTRICT, and required relationships of the survivors.
6
+ */
7
+ export declare function deleteNodes(runner: Runner, root: NodeType, keys: unknown[]): Promise<void>;
@@ -0,0 +1,73 @@
1
+ import { Statistics } from '../../analyze/statistics.js';
2
+ import { SelectionContext } from '../../compile/selection.js';
3
+ import { DriverTransaction, LoraDriver, QueryResult, Statement } from '../../driver.js';
4
+ import { GraphModel, NodeType } from '../../model/types.js';
5
+ import { WriteChange } from '../changes.js';
6
+ /** Computes a `@populatedBy` field. */
7
+ export type PopulatedByCallback = (args: {
8
+ operation: "CREATE" | "UPDATE";
9
+ /** Node type and field being populated. */
10
+ type: string;
11
+ field: string;
12
+ key: unknown;
13
+ /** The node's input in this mutation. */
14
+ input: Record<string, unknown>;
15
+ /** The GraphQL context. */
16
+ context: unknown;
17
+ }) => unknown;
18
+ export interface MutationEnv {
19
+ model: GraphModel;
20
+ driver: LoraDriver;
21
+ /** The caller's node's @key, for @key(scope: VIEWER) (see executeMutation). */
22
+ viewerKey?: unknown;
23
+ selection: SelectionContext;
24
+ jwt: Record<string, unknown> | undefined;
25
+ /** The GraphQL context, for `$context` in rules and for callbacks. */
26
+ requestContext: unknown;
27
+ timeoutMs: number;
28
+ signal: AbortSignal | undefined;
29
+ degrees: ReadonlyMap<string, number>;
30
+ /**
31
+ * Most nodes one mutation may create, update or delete, nested ones
32
+ * included; relationships written, ten times as many.
33
+ */
34
+ maxBatch: number;
35
+ /** Most items a @cypher list argument takes without `@size(max:)`. */
36
+ maxListArgument?: number | undefined;
37
+ /** Relationship levels one `where` may nest. */
38
+ maxFilterDepth?: number | undefined;
39
+ /** Items an `in` filter operand may hold. */
40
+ maxListFilter?: number | undefined;
41
+ /** Characters a string filter operand may hold. */
42
+ maxStringFilter?: number | undefined;
43
+ /** Statistics from `analyze()`, for filter costs. */
44
+ statistics?: Statistics | undefined;
45
+ callbacks: Readonly<Record<string, PopulatedByCallback>>;
46
+ /**
47
+ * A caller-owned transaction: run inside it and leave the commit to the
48
+ * caller. A failed mutation rolls it back, so it is never half-applied.
49
+ */
50
+ transaction?: DriverTransaction | undefined;
51
+ /** Observes every statement, for logging and tests. */
52
+ onStatement?: ((statement: Statement) => void) | undefined;
53
+ /** Runs a statement under timing, tracing and metrics, when configured. */
54
+ observe?: ((statement: Statement, run: () => Promise<QueryResult>) => Promise<QueryResult>) | undefined;
55
+ /**
56
+ * Called inside the transaction once a delete knows every node it
57
+ * removes, before any is removed: subscribers' checks of the nodes as
58
+ * they were (a deleted node cannot be checked after the commit).
59
+ */
60
+ beforeDelete?: ((change: WriteChange, doomed: ReadonlyArray<{
61
+ node: NodeType;
62
+ keys: unknown[];
63
+ }>, run: (statement: Statement) => Promise<QueryResult>) => Promise<void>) | undefined;
64
+ }
65
+ /** Run `statement` in `tx`, reporting it before and observing it during. */
66
+ export declare function runStatement(env: MutationEnv, tx: DriverTransaction, statement: Statement): Promise<QueryResult>;
67
+ export interface MutationInfo {
68
+ nodesCreated: number;
69
+ nodesUpdated: number;
70
+ nodesDeleted: number;
71
+ relationshipsCreated: number;
72
+ relationshipsDeleted: number;
73
+ }
@@ -0,0 +1,7 @@
1
+ import { GraphModel, NodeType, RelationshipField } from '../../model/types.js';
2
+ export declare function notConnected(owner: NodeType, rel: RelationshipField, target: NodeType, to: unknown): import('graphql').GraphQLError;
3
+ /** In place of the key of a node the caller cannot read, in messages. */
4
+ export declare const HIDDEN: unique symbol;
5
+ export declare function requiredMissing(owner: NodeType, field: RelationshipField, target: string, key: unknown): import('graphql').GraphQLError;
6
+ /** Turn engine constraint errors into CONSTRAINT_VIOLATION naming the field. */
7
+ export declare function mapWriteError(model: GraphModel, err: unknown): unknown;
@@ -0,0 +1,37 @@
1
+ import { CompileContext } from '../../compile/context.js';
2
+ import { Expr } from '../../compile/cypher.js';
3
+ import { Field, GraphModel, NodeType, RelationshipField } from '../../model/types.js';
4
+ import { RelationshipRef } from '../changes.js';
5
+ /** `@timestamp` fields for `op`: [property, now-expression]. */
6
+ export declare function timestamps(node: {
7
+ fields: ReadonlyMap<string, Field>;
8
+ }, op: "CREATE" | "UPDATE"): Array<[string, string]>;
9
+ /** `, r.p = <now>` for a relationship's `@timestamp(operations: [UPDATE])`s. */
10
+ export declare function edgeStamps(model: GraphModel, rel: RelationshipField): string;
11
+ /** `-[r:T]->(b:Target)` in the direction the owner's field declares. */
12
+ export declare function arrow(rel: RelationshipField, r: string, b: string, target: NodeType | undefined): string;
13
+ /**
14
+ * `b:Label`: LoraDB 0.15 ignores the labels of every node after the first
15
+ * in a MATCH pattern, so expansions test them explicitly.
16
+ */
17
+ export declare function labelTest(variable: string, node: NodeType): string;
18
+ /**
19
+ * `MATCH (a:Owner) WHERE a.key = <key>` then `MATCH (a)-[r:T]->(b:Target)
20
+ * WHERE b:Target`, for callers to extend with ` AND ...`. The key test gets
21
+ * its own MATCH so it becomes an index seek: LoraDB 0.15 only seeks when
22
+ * the test sits directly on a node scan, and one pattern with the test in
23
+ * its WHERE expands every relationship of the type first.
24
+ */
25
+ export declare function seekThenExpand(a: string, owner: NodeType, key: string, rel: RelationshipField, r: string, b: string, target: NodeType): string;
26
+ /** `row.fresh OR (<expr>)`: a filter that nodes this mutation creates skip. */
27
+ export declare const orFresh: (e: Expr | undefined) => Expr | undefined;
28
+ /** `AND (<expr>)`, or nothing. */
29
+ export declare const andText: (e: Expr | undefined) => string;
30
+ /** Fields on the other side of `rel`'s relationship type, pointing back. */
31
+ export declare function inverseFields(model: GraphModel, rel: RelationshipField): RelationshipField[];
32
+ /** Whether the caller can read `variable` (a `node`): a boolean expression. */
33
+ export declare function readable(ctx: CompileContext, node: NodeType, variable: string): string;
34
+ /** `x:A OR x:B`: the field's target types (one, or an abstract's members). */
35
+ export declare function memberTest(model: GraphModel, variable: string, field: RelationshipField): string;
36
+ export declare function dedupe(keys: unknown[]): unknown[];
37
+ export declare function relRef(rel: RelationshipField, from: unknown, to: unknown): RelationshipRef;
@@ -0,0 +1,95 @@
1
+ import { CompileContext } from '../../compile/context.js';
2
+ import { GraphModel, NodeType, RelationshipField, ScalarField } from '../../model/types.js';
3
+ import { MutationEnv } from './env.js';
4
+ export type Input = Record<string, unknown>;
5
+ export interface Link {
6
+ rel: RelationshipField;
7
+ /** @key of the node declaring the field. */
8
+ from: unknown;
9
+ /** @key of the target node. */
10
+ to: unknown;
11
+ /** Relationship properties the input sets. */
12
+ props: Record<string, unknown>;
13
+ /** `@default`s of the properties it leaves out: for a new relationship only. */
14
+ defaults: Record<string, unknown>;
15
+ /**
16
+ * Relationship properties the input sets whose rules refuse it when the
17
+ * relationship is new (`create`) or already exists (`update`): known
18
+ * only once the MERGE ran, so the check follows it (and rolls back).
19
+ */
20
+ refused?: {
21
+ create?: Error;
22
+ update?: Error;
23
+ };
24
+ }
25
+ export interface CreateRow {
26
+ key: unknown;
27
+ props: Input;
28
+ /**
29
+ * Fields the input sets. Field rules guard these only: a `@default`,
30
+ * `@populatedBy` value or generated key is the server's, not the
31
+ * caller's write (G-22).
32
+ */
33
+ written: Set<string>;
34
+ }
35
+ export interface EdgeUpdate {
36
+ rel: RelationshipField;
37
+ from: unknown;
38
+ /** Target key; undefined for a single relationship's current target. */
39
+ to: unknown;
40
+ set: Input;
41
+ remove: string[];
42
+ }
43
+ export interface NodeUpdate {
44
+ rel: RelationshipField;
45
+ from: unknown;
46
+ to: unknown;
47
+ input: Input;
48
+ }
49
+ /** Writes planned from one mutation's input. */
50
+ export declare class WritePlan {
51
+ readonly env: MutationEnv;
52
+ readonly ctx: CompileContext;
53
+ creates: Map<NodeType, CreateRow[]>;
54
+ links: Link[];
55
+ disconnects: Array<{
56
+ rel: RelationshipField;
57
+ from: unknown;
58
+ /** Keys to disconnect; undefined: every current target (single fields). */
59
+ to: unknown[] | undefined;
60
+ /**
61
+ * Replacing a single relationship: the target being connected, whose
62
+ * relationship (and its properties) stays if it is already there.
63
+ */
64
+ keep?: unknown;
65
+ }>;
66
+ edgeUpdates: EdgeUpdate[];
67
+ nodeUpdates: NodeUpdate[];
68
+ /** `update: { rel: { delete } }`: connected nodes to delete. */
69
+ nestedDeletes: Array<{
70
+ rel: RelationshipField;
71
+ from: unknown;
72
+ where: Input | undefined;
73
+ limit: number | undefined;
74
+ }>;
75
+ /** `@populatedBy` callbacks for planned creates. */
76
+ pending: Array<() => Promise<void>>;
77
+ constructor(env: MutationEnv, ctx: CompileContext);
78
+ get model(): GraphModel;
79
+ /**
80
+ * Whether this plan creates the `type` node with `key`. Creates run
81
+ * before links, so such a node has only the relationships this plan
82
+ * gives it.
83
+ */
84
+ isFresh(type: string, key: unknown): boolean;
85
+ /**
86
+ * Plan a node creation (and its nested writes); returns its key.
87
+ * `parent` is the relationship a nested create hangs from: it satisfies
88
+ * the new node's required field pointing back.
89
+ */
90
+ create(node: NodeType, input: Input, parent?: RelationshipField): unknown;
91
+ callback(f: ScalarField, operation: "CREATE" | "UPDATE", node: NodeType, key: unknown, input: Input): Promise<unknown>;
92
+ /** Plan connect / create / update / disconnect under one relationship field. */
93
+ relate(owner: NodeType, ownerKey: unknown, rel: RelationshipField, value: Input, update: boolean): void;
94
+ }
95
+ export declare function assertUniqueKeys(plan: WritePlan): void;
@@ -0,0 +1,129 @@
1
+ import { SelectionSetNode } from 'graphql';
2
+ import { CompileContext } from '../../compile/context.js';
3
+ import { DriverTransaction } from '../../driver.js';
4
+ import { NodeType, RelationshipField, ScalarField } from '../../model/types.js';
5
+ import { RelationshipRef, WriteChange } from '../changes.js';
6
+ import { MutationEnv, MutationInfo } from './env.js';
7
+ import { WritePlan, Input } from './plan.js';
8
+ export declare class Runner {
9
+ readonly env: MutationEnv;
10
+ readonly tx: DriverTransaction;
11
+ readonly change: WriteChange;
12
+ info: MutationInfo;
13
+ connected: RelationshipRef[];
14
+ disconnected: RelationshipRef[];
15
+ /** Nodes updated per type, for AFTER validation. */
16
+ updated: Map<NodeType, {
17
+ keys: unknown[];
18
+ fields: Set<string>;
19
+ }>;
20
+ /**
21
+ * A type this mutation creates under a key held by a node the caller
22
+ * cannot read; see {@link Runner.moveHiddenKeys}.
23
+ */
24
+ hiddenKey: NodeType | undefined;
25
+ /**
26
+ * A @unique value this mutation creates that another node holds; see
27
+ * {@link Runner.moveTakenUniques}.
28
+ */
29
+ takenUnique: {
30
+ node: NodeType;
31
+ field: ScalarField;
32
+ } | undefined;
33
+ constructor(env: MutationEnv, tx: DriverTransaction, change: WriteChange);
34
+ ctx(): CompileContext;
35
+ run(text: string, ctx: CompileContext): Promise<Array<Record<string, unknown>>>;
36
+ plan(): WritePlan;
37
+ /** Every planned write, in order, with the checks that follow them. */
38
+ apply(plan: WritePlan): Promise<void>;
39
+ /**
40
+ * `maxBatch` over the whole mutation: nodes created and updated so far
41
+ * plus those `plan` creates and updates (nested `update` entries
42
+ * included), and relationships written so far plus those `plan`
43
+ * connects, disconnects (each listed key) and updates, at most ten per
44
+ * node allowed. Checked before any of the plan's writes.
45
+ */
46
+ assertBatch(plan?: WritePlan, updating?: number): void;
47
+ /**
48
+ * A create under a key held by a node the caller cannot read must not
49
+ * tell them the key exists (G-20). The unique constraint would fail it
50
+ * with CONSTRAINT_VIOLATION before the rules run, while a free key gets
51
+ * the rules' answer: an existence oracle. So the hidden node moves to a
52
+ * placeholder key for the rest of the transaction, and the mutation runs
53
+ * on a graph that differs from the free-key case only in that hidden
54
+ * key: every error it raises is the one a free key gets. When it would
55
+ * succeed, `executeMutation` fails it with the FORBIDDEN of a denied
56
+ * create. Either way the transaction rolls back, and the key with it.
57
+ */
58
+ moveHiddenKeys(plan: WritePlan): Promise<void>;
59
+ /**
60
+ * The G-20 treatment for @unique values: a create under rules whose
61
+ * unique value another node holds would fail the constraint before the
62
+ * rules answer, telling a caller who may not create the node that the
63
+ * value is taken. The holder's value moves aside for the transaction,
64
+ * so every error is the one a free value gets; when the mutation would
65
+ * succeed, `executeMutation` fails it with the constraint violation a
66
+ * permitted caller gets. The transaction rolls back either way. Types
67
+ * without CREATE rules skip this: there is nothing to leak.
68
+ */
69
+ moveTakenUniques(plan: WritePlan): Promise<void>;
70
+ applyCreates(plan: WritePlan): Promise<void>;
71
+ applyDisconnects(plan: WritePlan): Promise<void>;
72
+ applyLinks(plan: WritePlan): Promise<void>;
73
+ /**
74
+ * Rules on the relationship field (and on a field declaring the same
75
+ * relationship from the other side) for `op`, on each pair's
76
+ * relationship as it stands: after the write for CONNECT and
77
+ * UPDATE_EDGE, before it for DISCONNECT. FORBIDDEN when any fails; the
78
+ * mutation rolls back.
79
+ */
80
+ checkRelationshipRules(op: "CONNECT" | "DISCONNECT" | "UPDATE_EDGE", rel: RelationshipField, pairs: Array<{
81
+ from: unknown;
82
+ to: unknown;
83
+ }>): Promise<void>;
84
+ /** Whether either end has validate rules for `op` at `when`. */
85
+ endRules(owner: NodeType, target: NodeType, op: "CREATE_RELATIONSHIP" | "DELETE_RELATIONSHIP", when: "BEFORE" | "AFTER"): boolean;
86
+ /**
87
+ * Validate rules for connecting or disconnecting, on both ends of each
88
+ * pair: FORBIDDEN when either end fails them.
89
+ */
90
+ checkEnds(op: "CREATE_RELATIONSHIP" | "DELETE_RELATIONSHIP", owner: NodeType, target: NodeType, pairs: Array<{
91
+ from: unknown;
92
+ to: unknown;
93
+ }>, when: "BEFORE" | "AFTER"): Promise<void>;
94
+ /** `update: [{ key, edge }]`: properties of connected pairs, in place. */
95
+ applyEdgeUpdates(plan: WritePlan): Promise<void>;
96
+ /**
97
+ * `delete: { where, limit }`: the connected nodes `where` matches, under
98
+ * their DELETE rules, deleted like a bulk delete (onDelete followed). More
99
+ * than `limit` (default `maxBatch`) is an error, not a truncation.
100
+ */
101
+ applyNestedDeletes(plan: WritePlan): Promise<void>;
102
+ /** `update: [{ key, node }]`: connected nodes, through the update path. */
103
+ applyNodeUpdates(plan: WritePlan): Promise<void>;
104
+ /**
105
+ * Update nodes by key: visibility (filter rules) and BEFORE rules, then
106
+ * one SET per shape of input. Returns the keys it updated; keys the
107
+ * caller cannot see are skipped. Relationship inputs go into `plan`.
108
+ */
109
+ update(plan: WritePlan, node: NodeType, rows: Array<{
110
+ key: unknown;
111
+ input: Input;
112
+ }>, adjust?: Input): Promise<unknown[]>;
113
+ /**
114
+ * AFTER rules on created nodes: the type's rules on every row, a field's
115
+ * rules on the rows whose input sets the field. Rows are checked in
116
+ * groups that set the same guarded fields.
117
+ */
118
+ validateCreated(plan: WritePlan): Promise<void>;
119
+ validateUpdated(): Promise<void>;
120
+ /** AFTER rules for `op` on the nodes with `keys` (and written fields). */
121
+ validateAfter(node: NodeType, keys: unknown[], op: "CREATE" | "UPDATE", fields: Set<string>): Promise<void>;
122
+ /** Nodes by key, projected for the payload, inside the transaction. */
123
+ payload(node: NodeType, keys: unknown[], sets: SelectionSetNode[]): Promise<unknown[]>;
124
+ /**
125
+ * Keys of the nodes `where` matches, under the filter rules for `op`.
126
+ * More than `limit` is an error: bulk writes are bounded, not truncated.
127
+ */
128
+ resolveKeys(node: NodeType, where: Input, op: "UPDATE" | "DELETE", limit: number): Promise<unknown[]>;
129
+ }
@@ -0,0 +1,18 @@
1
+ import { CompileContext } from '../../compile/context.js';
2
+ import { DriverTransaction } from '../../driver.js';
3
+ import { GraphModel, NodeType, UniqueTogether } from '../../model/types.js';
4
+ import { MutationEnv } from './env.js';
5
+ export declare function notUniqueTogether(node: NodeType, u: UniqueTogether, key: unknown): import('graphql').GraphQLError;
6
+ /**
7
+ * `@uniqueTogether` after a write whose write-set is unknown (a `@cypher`
8
+ * mutation): every node of each type in `nodes` is compared, in `tx`.
9
+ */
10
+ export declare function checkUniqueTogetherOf(env: MutationEnv, tx: DriverTransaction, nodes: readonly NodeType[]): Promise<void>;
11
+ /**
12
+ * The first node among `keys` whose `@uniqueTogether` combination another
13
+ * node of its type shares: seeks each by key, binds its combination, then
14
+ * compares it with the nodes that share its first relationship end (or,
15
+ * with scalars only, its first scalar's value) — never a scan of the type.
16
+ * Without `keys`, every node of the type is compared (a scan).
17
+ */
18
+ export declare function uniqueTogetherStatement(ctx: CompileContext, model: GraphModel, node: NodeType, u: UniqueTogether, keys: unknown[] | undefined): string;
@@ -1,87 +1,10 @@
1
1
  import { FieldNode } from 'graphql';
2
- import { Statistics } from '../analyze/statistics.js';
3
- import { SelectionContext } from '../compile/selection.js';
4
- import { DriverTransaction, LoraDriver, QueryResult, Statement } from '../driver.js';
5
- import { GraphModel, NodeType } from '../model/types.js';
2
+ import { NodeType } from '../model/types.js';
6
3
  import { MutationKind } from '../schema/mutations.js';
7
4
  import { WriteChange } from './changes.js';
8
- /** Computes a `@populatedBy` field. */
9
- export type PopulatedByCallback = (args: {
10
- operation: "CREATE" | "UPDATE";
11
- /** Node type and field being populated. */
12
- type: string;
13
- field: string;
14
- key: unknown;
15
- /** The node's input in this mutation. */
16
- input: Record<string, unknown>;
17
- /** The GraphQL context. */
18
- context: unknown;
19
- }) => unknown;
20
- export interface MutationEnv {
21
- model: GraphModel;
22
- driver: LoraDriver;
23
- /** The caller's node's @key, for @key(scope: VIEWER) (see executeMutation). */
24
- viewerKey?: unknown;
25
- selection: SelectionContext;
26
- jwt: Record<string, unknown> | undefined;
27
- /** The GraphQL context, for `$context` in rules and for callbacks. */
28
- requestContext: unknown;
29
- timeoutMs: number;
30
- signal: AbortSignal | undefined;
31
- degrees: ReadonlyMap<string, number>;
32
- /**
33
- * Most nodes one mutation may create, update or delete, nested ones
34
- * included; relationships written, ten times as many.
35
- */
36
- maxBatch: number;
37
- /** Most items a @cypher list argument takes without `@size(max:)`. */
38
- maxListArgument?: number | undefined;
39
- /** Relationship levels one `where` may nest. */
40
- maxFilterDepth?: number | undefined;
41
- /** Items an `in` filter operand may hold. */
42
- maxListFilter?: number | undefined;
43
- /** Characters a string filter operand may hold. */
44
- maxStringFilter?: number | undefined;
45
- /** Statistics from `analyze()`, for filter costs. */
46
- statistics?: Statistics | undefined;
47
- callbacks: Readonly<Record<string, PopulatedByCallback>>;
48
- /**
49
- * A caller-owned transaction: run inside it and leave the commit to the
50
- * caller. A failed mutation rolls it back, so it is never half-applied.
51
- */
52
- transaction?: DriverTransaction | undefined;
53
- /** Observes every statement, for logging and tests. */
54
- onStatement?: ((statement: Statement) => void) | undefined;
55
- /** Runs a statement under timing, tracing and metrics, when configured. */
56
- observe?: ((statement: Statement, run: () => Promise<QueryResult>) => Promise<QueryResult>) | undefined;
57
- /**
58
- * Called inside the transaction once a delete knows every node it
59
- * removes, before any is removed: subscribers' checks of the nodes as
60
- * they were (a deleted node cannot be checked after the commit).
61
- */
62
- beforeDelete?: ((change: WriteChange, doomed: ReadonlyArray<{
63
- node: NodeType;
64
- keys: unknown[];
65
- }>, run: (statement: Statement) => Promise<QueryResult>) => Promise<void>) | undefined;
66
- }
67
- /** Run `statement` in `tx`, reporting it before and observing it during. */
68
- export declare function runStatement(env: MutationEnv, tx: DriverTransaction, statement: Statement): Promise<QueryResult>;
69
- export interface MutationInfo {
70
- nodesCreated: number;
71
- nodesUpdated: number;
72
- nodesDeleted: number;
73
- relationshipsCreated: number;
74
- relationshipsDeleted: number;
75
- }
76
- /**
77
- * `@uniqueTogether` after a write whose write-set is unknown (a `@cypher`
78
- * mutation): every node of each type in `nodes` is compared, in `tx`.
79
- */
80
- export declare function checkUniqueTogetherOf(env: MutationEnv, tx: DriverTransaction, nodes: readonly NodeType[]): Promise<void>;
5
+ import { MutationEnv } from './mutate/env.js';
81
6
  export interface MutationResult {
82
7
  payload: unknown;
83
8
  change: WriteChange;
84
9
  }
85
10
  export declare function executeMutation(env: MutationEnv, op: MutationKind, node: NodeType, args: Record<string, unknown>, fieldNodes: readonly FieldNode[], fieldName: string): Promise<MutationResult>;
86
- /** Turn engine constraint errors into CONSTRAINT_VIOLATION naming the field. */
87
- export declare function mapWriteError(model: GraphModel, err: unknown): unknown;
package/dist/index.d.ts CHANGED
@@ -1,19 +1,23 @@
1
- export { LoraGraphQL, type AssertSchemaOptions, type CheckOptions, type CheckReport, type CostEvent, type DatabaseErrorEvent, type ExecuteArgs, type ExecutionTiming, type LoraGraphQLContext, type LoraGraphQLOptions, type LoraExecutionResult, type SchemaAssertion, type StatementEvent, } from './lora-graphql.js';
1
+ export { LoraGraphQL } from './lora-graphql.js';
2
+ export { type AssertSchemaOptions, type CheckOptions, type CostEvent, type DatabaseErrorEvent, type ExecuteArgs, type LoraGraphQLContext, type LoraGraphQLOptions, type StatementEvent, } from './options.js';
3
+ export { type CheckReport, type ExecutionTiming, type LoraExecutionResult, type SchemaAssertion, } from './results.js';
2
4
  export { loraDriver, type DriverTransaction, type LoraDatabaseLike, type LoraDriver, type QueryPlan, type QueryResult, type RunOptions, type Statement, } from './driver.js';
3
5
  export { directiveTypeDefs } from './model/directives.js';
4
- export { buildModel, type ModelOptions } from './model/build.js';
6
+ export { buildModel } from './model/build.js';
7
+ export { type ModelOptions } from './model/build/limits.js';
5
8
  export type * from './model/types.js';
6
9
  export { inferRequirements, requirementDdl, type SchemaRequirement, } from './analyze/indexes.js';
7
10
  export { checkPlans, scanExpands, type PlanFinding, type PlanReport, } from './analyze/plans.js';
8
11
  export type { DegreeStats, Statistics } from './analyze/statistics.js';
9
- export type { AccessEntry, AccessVerdict, OperationAccess, RootFieldAccess, } from './analyze/access.js';
12
+ export type { AccessEntry, AccessVerdict } from './analyze/verdicts.js';
13
+ export type { OperationAccess, RootFieldAccess } from './analyze/access.js';
10
14
  export type { CypherFinding } from './analyze/cypher-check.js';
11
15
  export { diffSchemas, type ApiChange, type SchemaDiff, } from './analyze/diff.js';
12
- export type { CompiledRead, ReadSet, SeekExpectation } from './compile/read.js';
16
+ export type { CompiledRead, ReadSet, SeekExpectation, } from './compile/read/types.js';
13
17
  export type { EntityRef, RelationshipRef, WriteChange, } from './execute/changes.js';
14
- export type { MutationInfo, PopulatedByCallback } from './execute/mutate.js';
18
+ export type { MutationInfo, PopulatedByCallback, } from './execute/mutate/env.js';
15
19
  export type { MutationKind } from './schema/mutations.js';
16
- export type { ChangeEvent } from './schema/build.js';
20
+ export type { ChangeEvent } from './schema/hooks.js';
17
21
  export { LoraTransaction } from './execute/transaction.js';
18
22
  export { ModelError, formatProblem, isLoraGraphQLError, LORA_GRAPHQL_ERROR_CODES, type LoraGraphQLErrorCode, type ModelProblem, } from './errors.js';
19
23
  export { toGlobalId, fromGlobalId } from './schema/global-id.js';
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { D as r, L as e, a as o, b as l, M as i, c as d, d as n, e as t, f as p, g as f, h as m, i as L, j as c, l as R, p as h, r as D, s as E, k as G, t as b, v as u } from "./driver-C5dSoSD8.js";
2
- import { d as A } from "./diff-B9iDdELF.js";
1
+ import { D as r, L as e, a as o, b as l, M as i, c as d, d as n, e as t, f as p, g as f, h as m, i as L, j as c, l as R, p as h, r as D, s as E, k as G, t as b, v as u } from "./driver-DSnytFsU.js";
2
+ import { d as A } from "./diff-aDivqYqr.js";
3
3
  export {
4
4
  r as DEFAULT_GUARDS,
5
5
  e as LORA_GRAPHQL_ERROR_CODES,