@loradb/lora-graphql 0.24.1 → 0.25.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/README.md +151 -31
- package/dist/analyze/input-schemas.d.ts +11 -0
- package/dist/analyze/plans.d.ts +8 -0
- package/dist/analyze/stored.d.ts +29 -0
- package/dist/cli.js +263 -244
- package/dist/cli.js.map +1 -1
- package/dist/compile/cache.d.ts +9 -1
- package/dist/compile/context.d.ts +3 -0
- package/dist/compile/read/common.d.ts +2 -0
- package/dist/compile/read/root.d.ts +7 -0
- package/dist/compile/selection.d.ts +5 -0
- package/dist/diff-CdZZG-N1.js +134 -0
- package/dist/diff-CdZZG-N1.js.map +1 -0
- package/dist/driver.d.ts +36 -1
- package/dist/errors.d.ts +21 -1
- package/dist/events/outbox.d.ts +14 -0
- package/dist/events/worker.d.ts +67 -0
- package/dist/execute/mutate/checks.d.ts +16 -0
- package/dist/execute/mutate/env.d.ts +50 -0
- package/dist/execute/mutate/errors.d.ts +6 -0
- package/dist/execute/mutate/links.d.ts +38 -0
- package/dist/execute/mutate/plan.d.ts +5 -0
- package/dist/execute/mutate/runner.d.ts +101 -25
- package/dist/execute/mutate/single-call.d.ts +92 -0
- package/dist/execute/mutate/triggers.d.ts +21 -0
- package/dist/execute/mutate/unique-together.d.ts +5 -1
- package/dist/execute/mutate/validate.d.ts +11 -0
- package/dist/execute/transaction.d.ts +13 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.js +16 -15
- package/dist/lora-graphql-D11QFUkf.js +17222 -0
- package/dist/lora-graphql-D11QFUkf.js.map +1 -0
- package/dist/lora-graphql.d.ts +30 -1
- package/dist/model/build/authorization.d.ts +11 -1
- package/dist/model/build/constraints.d.ts +22 -0
- package/dist/model/build/rule-checks.d.ts +6 -1
- package/dist/model/build/trigger.d.ts +9 -0
- package/dist/model/constraints.d.ts +112 -0
- package/dist/model/desugar.d.ts +7 -0
- package/dist/model/directives.d.ts +1 -1
- package/dist/model/types.d.ts +34 -0
- package/dist/options.d.ts +76 -3
- package/dist/results.d.ts +12 -1
- package/dist/schema/scalars.d.ts +8 -1
- package/dist/standard-schema.d.ts +37 -0
- package/dist/testing.js +1 -1
- package/package.json +2 -2
- package/dist/diff-K8-GSeKH.js +0 -100
- package/dist/diff-K8-GSeKH.js.map +0 -1
- package/dist/driver-CXKPLfkz.js +0 -14447
- package/dist/driver-CXKPLfkz.js.map +0 -1
package/dist/lora-graphql.d.ts
CHANGED
|
@@ -7,6 +7,9 @@ import { Statistics } from './analyze/statistics.js';
|
|
|
7
7
|
import { CompiledRead, ReadSet } from './compile/read/types.js';
|
|
8
8
|
import { WriteChange } from './execute/changes.js';
|
|
9
9
|
import { LoraTransaction } from './execute/transaction.js';
|
|
10
|
+
import { Worker, WorkerOptions } from './events/worker.js';
|
|
11
|
+
import { JsonSchema } from './analyze/input-schemas.js';
|
|
12
|
+
import { StoredReport } from './analyze/stored.js';
|
|
10
13
|
import { OperationManifest } from './codegen.js';
|
|
11
14
|
import { envelopPlugin } from './guards.js';
|
|
12
15
|
import { GraphModel } from './model/types.js';
|
|
@@ -79,6 +82,22 @@ export declare class LoraGraphQL {
|
|
|
79
82
|
* variable without a value is listed in the field's `unresolved`.
|
|
80
83
|
*/
|
|
81
84
|
operationAccess(document: string | DocumentNode, operationName?: string, variables?: Readonly<Record<string, unknown>>): OperationAccess;
|
|
85
|
+
/**
|
|
86
|
+
* The validation rules as JSON Schema (2020-12), one per generated
|
|
87
|
+
* create and update input type, for clients: introspection does not
|
|
88
|
+
* show directives. Build zod, yup or valibot schemas from it, or
|
|
89
|
+
* validate with any JSON Schema library.
|
|
90
|
+
*/
|
|
91
|
+
inputSchemas(): Record<string, JsonSchema>;
|
|
92
|
+
/**
|
|
93
|
+
* Stored values that break a validation rule of their field: rules are
|
|
94
|
+
* checked on write through the API, so a rule added later, or a value
|
|
95
|
+
* written with raw Cypher, is only found by reading. Reads at most
|
|
96
|
+
* `limit` nodes per type (default 10 000).
|
|
97
|
+
*/
|
|
98
|
+
storedViolations(options?: {
|
|
99
|
+
limit?: number;
|
|
100
|
+
}): Promise<StoredReport>;
|
|
82
101
|
/**
|
|
83
102
|
* The CI gate: @cypher statements plan, the database has what the API
|
|
84
103
|
* needs, and each operation's statements seek where they should.
|
|
@@ -162,6 +181,16 @@ export declare class LoraGraphQL {
|
|
|
162
181
|
* others, and no change event fires, until `tx.commit()`.
|
|
163
182
|
*/
|
|
164
183
|
begin(): Promise<LoraTransaction>;
|
|
165
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* Hand the nodes of an `@outbox` type to `options.handle`, after the
|
|
186
|
+
* commit that made them: for the side effects a transaction cannot
|
|
187
|
+
* hold (a push, an e-mail, a webhook). Delivery is at least once, with
|
|
188
|
+
* retries and backoff; work left by a restart is picked up when the
|
|
189
|
+
* worker starts. With `changeFeed: true` it also hears of nodes
|
|
190
|
+
* created outside this instance, otherwise it finds those on its next
|
|
191
|
+
* sweep. `close()` stops it.
|
|
192
|
+
*/
|
|
193
|
+
worker(type: string, options: WorkerOptions): Worker;
|
|
194
|
+
/** Stop the workers, and the engine change feed (with `changeFeed: true`). */
|
|
166
195
|
close(): void;
|
|
167
196
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AuthOperation, Authorization, RelationshipField } from '../types.js';
|
|
1
|
+
import { AuthOperation, Authorization, AuthorizationWhere, NodeType, RelationshipField } from '../types.js';
|
|
2
2
|
export declare function authOps(args: Record<string, unknown> | undefined): Set<AuthOperation> | undefined;
|
|
3
3
|
/**
|
|
4
4
|
* Field-level rules on a relationship or @cypher field: READ validate rules
|
|
@@ -28,4 +28,14 @@ export declare function relationshipRuleOperations(f: RelationshipField): Set<Au
|
|
|
28
28
|
export declare const isRelationshipRule: (rule: {
|
|
29
29
|
operations: ReadonlySet<AuthOperation>;
|
|
30
30
|
}) => boolean;
|
|
31
|
+
/**
|
|
32
|
+
* The authorization of a field to an interface or union, per member: its
|
|
33
|
+
* relationship rules hold per member, each with that member as its
|
|
34
|
+
* target. Sugar expands differently per member (`target: { isViewer:
|
|
35
|
+
* true }` is only ever true of one), so each member gets its own copy of
|
|
36
|
+
* the rules, which `desugar` expands in place.
|
|
37
|
+
*/
|
|
38
|
+
export declare function memberAuthorization(f: RelationshipField, nodes: ReadonlyMap<string, NodeType>, desugar: (rules: ReadonlyArray<{
|
|
39
|
+
where: AuthorizationWhere;
|
|
40
|
+
}>, target: NodeType) => void): Map<string, Authorization>;
|
|
31
41
|
export declare const isRecord: (x: unknown) => x is Record<string, unknown>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { GraphQLDirective, GraphQLScalarType } from 'graphql';
|
|
2
|
+
import { FieldConstraints } from '../constraints.js';
|
|
3
|
+
import { ScalarType } from '../types.js';
|
|
4
|
+
/** The value rules of field `f`, with every problem reported through `at`. */
|
|
5
|
+
export declare function readConstraints(d: (n: string) => GraphQLDirective, f: {
|
|
6
|
+
astNode?: unknown;
|
|
7
|
+
extensionASTNodes?: unknown;
|
|
8
|
+
}, at: (message: string) => void, field: {
|
|
9
|
+
type: ScalarType;
|
|
10
|
+
list: boolean;
|
|
11
|
+
vector: boolean;
|
|
12
|
+
timestamp: boolean;
|
|
13
|
+
defaultValue: {
|
|
14
|
+
value: unknown;
|
|
15
|
+
} | undefined;
|
|
16
|
+
/** The field's custom scalar, whose rules it inherits. */
|
|
17
|
+
scalar?: GraphQLScalarType | undefined;
|
|
18
|
+
/** How a problem names the default: an argument's is not a directive. */
|
|
19
|
+
defaultLabel?: string | undefined;
|
|
20
|
+
/** Directives another reader handles (an argument's plain @size, @range). */
|
|
21
|
+
skip?: ReadonlySet<string> | undefined;
|
|
22
|
+
}): FieldConstraints | undefined;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ModelProblem } from '../../errors.js';
|
|
2
|
-
import { AbstractType, NodeType, RelationshipPropertiesType } from '../types.js';
|
|
2
|
+
import { AbstractType, NodeType, RelationshipField, RelationshipPropertiesType } from '../types.js';
|
|
3
3
|
/** Check an `@authorization` where against the model, at startup. */
|
|
4
4
|
export declare function checkAuthorizationWhere(nodes: ReadonlyMap<string, NodeType>, props: ReadonlyMap<string, RelationshipPropertiesType>, node: NodeType, where: unknown, problems: ModelProblem[], jwtShape?: ReadonlyMap<string, string>, viewerNode?: NodeType): void;
|
|
5
5
|
/**
|
|
@@ -15,6 +15,11 @@ export interface RuleEnds {
|
|
|
15
15
|
edge: RelationshipPropertiesType | undefined;
|
|
16
16
|
}
|
|
17
17
|
export declare function checkRuleWhere(nodes: ReadonlyMap<string, NodeType>, props: ReadonlyMap<string, RelationshipPropertiesType>, node: NodeType | undefined, type: string, field: string | undefined, where: unknown, problems: ModelProblem[], jwtShape?: ReadonlyMap<string, string>, viewerNode?: NodeType, ends?: RuleEnds, nodeless?: string): void;
|
|
18
|
+
/**
|
|
19
|
+
* Check the relationship rules of a field to an interface or union: each
|
|
20
|
+
* member's copy, with that member as the target.
|
|
21
|
+
*/
|
|
22
|
+
export declare function checkMemberRules(nodes: ReadonlyMap<string, NodeType>, props: ReadonlyMap<string, RelationshipPropertiesType>, source: NodeType, f: RelationshipField, problems: ModelProblem[], jwtShape?: ReadonlyMap<string, string>, viewerNode?: NodeType): void;
|
|
18
23
|
export declare function checkNodeWhere(nodes: ReadonlyMap<string, NodeType>, props: ReadonlyMap<string, RelationshipPropertiesType>, node: NodeType, where: unknown, path: string, at: (message: string) => void): void;
|
|
19
24
|
/**
|
|
20
25
|
* Interfaces and unions of a model being built, by its node map: the rule
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { GraphQLDirective, GraphQLObjectType } from 'graphql';
|
|
2
|
+
import { ModelProblem } from '../../errors.js';
|
|
3
|
+
import { NodeType, Trigger } from '../types.js';
|
|
4
|
+
/** What a trigger statement may read as a `$parameter`. */
|
|
5
|
+
export declare const TRIGGER_PARAMS: readonly string[];
|
|
6
|
+
/** Every `@trigger` of a node type, in the order written, checked. */
|
|
7
|
+
export declare function readTriggers(t: GraphQLObjectType, node: NodeType, d: (name: string) => GraphQLDirective, problems: ModelProblem[],
|
|
8
|
+
/** Whether the @jwt type has a @viewer claim (enables `$viewer`). */
|
|
9
|
+
viewer: boolean): Trigger[];
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/** The rules of one field. Arguments map onto JSON Schema keywords. */
|
|
2
|
+
export interface FieldConstraints {
|
|
3
|
+
length?: {
|
|
4
|
+
min?: number;
|
|
5
|
+
max?: number;
|
|
6
|
+
message?: string;
|
|
7
|
+
};
|
|
8
|
+
pattern?: {
|
|
9
|
+
source: string;
|
|
10
|
+
regex: RegExp;
|
|
11
|
+
message?: string;
|
|
12
|
+
};
|
|
13
|
+
format?: {
|
|
14
|
+
name: string;
|
|
15
|
+
message?: string;
|
|
16
|
+
};
|
|
17
|
+
range?: {
|
|
18
|
+
min?: number;
|
|
19
|
+
max?: number;
|
|
20
|
+
exclusiveMin?: number;
|
|
21
|
+
exclusiveMax?: number;
|
|
22
|
+
message?: string;
|
|
23
|
+
};
|
|
24
|
+
multipleOf?: {
|
|
25
|
+
value: number;
|
|
26
|
+
message?: string;
|
|
27
|
+
};
|
|
28
|
+
size?: {
|
|
29
|
+
min?: number;
|
|
30
|
+
max?: number;
|
|
31
|
+
unique: boolean;
|
|
32
|
+
message?: string;
|
|
33
|
+
};
|
|
34
|
+
/** `@normalize`: applied before the rules, and to what is stored. */
|
|
35
|
+
normalize?: {
|
|
36
|
+
trim: boolean;
|
|
37
|
+
unicode?: "NFC" | "NFD" | "NFKC" | "NFKD";
|
|
38
|
+
case?: "LOWER" | "UPPER";
|
|
39
|
+
emptyToNull: boolean;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** One rule a value breaks. `rule` is the JSON Schema keyword. */
|
|
43
|
+
export interface RuleFailure {
|
|
44
|
+
rule: string;
|
|
45
|
+
/** The rule's own numbers and names, for clients that localise. */
|
|
46
|
+
params: Record<string, unknown>;
|
|
47
|
+
message: string;
|
|
48
|
+
/** The item of a list field the failure is about. */
|
|
49
|
+
index?: number;
|
|
50
|
+
}
|
|
51
|
+
interface Format {
|
|
52
|
+
test: (value: string) => boolean;
|
|
53
|
+
message: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The formats `@format(as:)` knows. Named after JSON Schema's where the
|
|
57
|
+
* meaning matches, and differently where it does not (`url` is the WHATWG
|
|
58
|
+
* parser with an http/https allow-list, not RFC 3986; `e164` is a shape,
|
|
59
|
+
* not proof a number exists).
|
|
60
|
+
*/
|
|
61
|
+
export declare const FORMATS: Readonly<Record<string, Format>>;
|
|
62
|
+
/**
|
|
63
|
+
* `value` as `@normalize` stores it: trimmed, normalised and case-mapped,
|
|
64
|
+
* in that order, and null for an empty string under `emptyToNull`. Items
|
|
65
|
+
* of a list are normalised one by one. Anything but a string is returned
|
|
66
|
+
* as it is.
|
|
67
|
+
*/
|
|
68
|
+
export declare function normalizeValue(c: FieldConstraints, value: unknown): unknown;
|
|
69
|
+
/** Unicode code points in `s`. */
|
|
70
|
+
export declare function codePoints(s: string): number;
|
|
71
|
+
/**
|
|
72
|
+
* The rules `value` breaks, in declaration order; empty when it is valid.
|
|
73
|
+
* A list is checked as a list (`@size`) and then item by item.
|
|
74
|
+
*/
|
|
75
|
+
export declare function checkValue(c: FieldConstraints, value: unknown): RuleFailure[];
|
|
76
|
+
/**
|
|
77
|
+
* Why `regex` is not safe to run on client input of up to `maxLength`
|
|
78
|
+
* characters, or undefined. JavaScript's engine backtracks, so a pattern
|
|
79
|
+
* such as `(a+)+` takes seconds on thirty characters. This runs the
|
|
80
|
+
* compiled pattern on inputs built to make it backtrack (a character it
|
|
81
|
+
* accepts, repeated, then one it rejects) and refuses a pattern that
|
|
82
|
+
* overruns the budget. A probe, not a proof: it finds the repetition
|
|
83
|
+
* mistakes people make, and `@length(max:)` bounds everything else.
|
|
84
|
+
*/
|
|
85
|
+
export declare function probePattern(regex: RegExp, source: string, maxLength: number): string | undefined;
|
|
86
|
+
/**
|
|
87
|
+
* The rules as JSON Schema 2020-12 keywords, for one value (`item`) and
|
|
88
|
+
* for the list holding it (`list`). A pattern is exported anchored, since
|
|
89
|
+
* here it must match the whole value and in JSON Schema it need not. When
|
|
90
|
+
* a format and a @pattern both give an expression, they are joined with
|
|
91
|
+
* `allOf`.
|
|
92
|
+
*/
|
|
93
|
+
export declare function jsonSchemaKeywords(c: FieldConstraints | undefined): {
|
|
94
|
+
item: Record<string, unknown>;
|
|
95
|
+
list: Record<string, unknown>;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* The rules in a sentence or two, for the description of a generated
|
|
99
|
+
* input field: what a client reads in GraphiQL and in the printed schema.
|
|
100
|
+
*/
|
|
101
|
+
export declare function describeConstraints(c: FieldConstraints | undefined, list: boolean): string | undefined;
|
|
102
|
+
/**
|
|
103
|
+
* What changed between two versions of a field's rules, in the terms a
|
|
104
|
+
* schema diff needs: `tightened` refuses input the old rules accepted,
|
|
105
|
+
* `changed` may, `loosened` only accepts more.
|
|
106
|
+
*/
|
|
107
|
+
export declare function compareConstraints(before: FieldConstraints | undefined, after: FieldConstraints | undefined): {
|
|
108
|
+
tightened: string[];
|
|
109
|
+
changed: string[];
|
|
110
|
+
loosened: string[];
|
|
111
|
+
};
|
|
112
|
+
export {};
|
package/dist/model/desugar.d.ts
CHANGED
|
@@ -27,6 +27,13 @@ export interface DesugarContext {
|
|
|
27
27
|
rules?: NamedRules;
|
|
28
28
|
/** Named rules being expanded, to report a cycle with its chain. */
|
|
29
29
|
expanding?: string[];
|
|
30
|
+
/**
|
|
31
|
+
* The member of an interface or union a relationship rule is expanded
|
|
32
|
+
* for, as its `target`. `isViewer` on a member that is not the viewer
|
|
33
|
+
* type is then never true, where elsewhere it is a mistake: the rule is
|
|
34
|
+
* written once for every member.
|
|
35
|
+
*/
|
|
36
|
+
member?: NodeType;
|
|
30
37
|
at: (message: string) => void;
|
|
31
38
|
}
|
|
32
39
|
/**
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export declare const directiveTypeDefs = "\n \"A node label set. Defaults to the type name.\"\n directive @node(labels: [String!], plural: String) on OBJECT\n\n \"Natural key: required, unique and immutable; the tie-breaker of every sort and the anchor of cursors, global ids, updates and deletes. With generate: true, creates fill it with a UUID when the input leaves it out.\"\n directive @key(\n generate: Boolean = false\n \"VIEWER: a created key must start with the caller's @viewer claim and the separator (lou:tomorrowland).\"\n scope: KeyScope\n separator: String = \":\"\n ) on FIELD_DEFINITION\n\n enum KeyScope {\n VIEWER\n }\n\n \"On an interface field: every implementation declares this relationship (with @relationship, possibly of different types or directions), so clients can select it on the interface.\"\n directive @declareRelationship on FIELD_DEFINITION\n\n \"Offer this field as a grouping key of <plural>Grouped (needs @query(aggregate: true)).\"\n directive @groupBy on FIELD_DEFINITION\n\n \"A field computed in JavaScript by the resolver passed in the resolvers option. requires is a selection on this type (for example name capacity) fetched in the same statement, so the resolver reads it from its source.\"\n directive @customResolver(requires: String) on FIELD_DEFINITION\n\n \"On a custom scalar: how its values are stored. Filters, sorts and indexes follow the storage type; the scalar is passed through unless the scalars option supplies an implementation.\"\n directive @storedAs(type: StorageType!) on SCALAR\n\n enum StorageType {\n STRING\n INT\n FLOAT\n BOOLEAN\n DATETIME\n DATE\n }\n\n \"A uniqueness constraint.\"\n directive @unique on FIELD_DEFINITION\n\n \"No two nodes of this type (among those matching where) share the values of fields: scalar fields, single relationships (by the target's @key) and at most one list relationship (by the set of target keys). Enforced by every generated mutation, for every caller.\"\n directive @uniqueTogether(\n fields: [String!]!\n where: NodeWhere\n ) repeatable on OBJECT\n\n \"An explicit index. Usually unnecessary: inferred from @filterable and @sortable.\"\n directive @index(kind: IndexKind!) on FIELD_DEFINITION\n\n \"An edge. The target type is the field's type.\"\n directive @relationship(\n type: String!\n direction: RelationshipDirection!\n properties: String\n \"UNDIRECTED reads follow the relationship both ways; writes use direction.\"\n queryDirection: QueryDirection = DIRECTED\n \"What deleting this node does to nodes reached through the field.\"\n onDelete: OnDelete = DETACH\n \"Which nested writes mutation inputs offer for the field.\"\n nestedOperations: [NestedOperation!] = [\n CREATE\n CONNECT\n DISCONNECT\n UPDATE\n DELETE\n ]\n \"false: no aggregate on the field's connection and no aggregate filter.\"\n aggregate: Boolean = true\n ) on FIELD_DEFINITION\n\n \"UPDATE changes a connected node and its relationship properties in place; UPDATE_EDGE only the properties.\"\n enum NestedOperation {\n CREATE\n CONNECT\n DISCONNECT\n UPDATE\n UPDATE_EDGE\n DELETE\n }\n\n enum QueryDirection {\n DIRECTED\n UNDIRECTED\n }\n\n \"DETACH removes the relationships; CASCADE deletes the related nodes too; RESTRICT refuses while any exist.\"\n enum OnDelete {\n DETACH\n CASCADE\n RESTRICT\n }\n\n \"Which mutations may set the field. @readonly is onCreate: false, onUpdate: false.\"\n directive @settable(\n onCreate: Boolean = true\n onUpdate: Boolean = true\n ) on FIELD_DEFINITION\n\n \"Whether the field can be read (onRead: false makes it write-only) or aggregated.\"\n directive @selectable(\n onRead: Boolean = true\n onAggregate: Boolean = true\n ) on FIELD_DEFINITION\n\n \"Set by a callback passed to LoraGraphQL({ callbacks }) on these operations. Client-settable only with an explicit @settable(onCreate: true) or (onUpdate: true) and a field-level @authorization(validate:) rule for that operation.\"\n directive @populatedBy(\n callback: String!\n operations: [TimestampOperation!]! = [CREATE, UPDATE]\n ) on FIELD_DEFINITION\n\n \"The shape of the request's claims. Rules may only test declared claims.\"\n directive @jwt on OBJECT\n\n \"Where a declared claim lives in the token, e.g. app_metadata.roles.\"\n directive @jwtClaim(path: String!) on FIELD_DEFINITION\n\n \"The claim that identifies the caller: the node of `type` whose `field` (a @key or @unique field) equals it. Enables isViewer and viewer in rules.\"\n directive @viewer(type: String!, field: String!) on FIELD_DEFINITION\n\n \"Properties carried by a relationship type.\"\n directive @relationshipProperties on OBJECT\n\n \"API name differs from the stored property.\"\n directive @alias(property: String!) on FIELD_DEFINITION\n\n \"Stored, never exposed.\"\n directive @private on FIELD_DEFINITION\n\n \"Declared upper bound on a relationship's fan-out, used by cost estimates.\"\n directive @cardinality(max: Int!) on FIELD_DEFINITION\n\n \"The most items a list argument of a @cypher field takes; more is BAD_USER_INPUT before the statement runs. Without it, maxListArgument applies.\"\n directive @size(max: Int!) on ARGUMENT_DEFINITION\n\n \"Bounds of an Int or Float argument of a @cypher field (each item, for a list); a value outside them is BAD_USER_INPUT before the statement runs.\"\n directive @range(min: Float, max: Float) on ARGUMENT_DEFINITION\n\n \"Generated read operations for a node type. Reads are on by default.\"\n directive @query(\n read: Boolean = true\n aggregate: Boolean = false\n ) on OBJECT | INTERFACE | UNION\n\n \"The plural of an interface or union, for its root field.\"\n directive @plural(value: String!) on INTERFACE | UNION\n\n \"Filter operators for a field. Without arguments: EQ and IN. On a relationship field: enables relationship filters.\"\n directive @filterable(byValue: [FilterOperator!]) on FIELD_DEFINITION\n\n \"Sort and keyset-paginate on this field.\"\n directive @sortable on FIELD_DEFINITION\n\n \"Page size bounds for lists of this type.\"\n directive @limit(\n default: Int\n max: Int\n ) on OBJECT | INTERFACE | UNION | FIELD_DEFINITION\n\n \"Expose an opaque global id derived from this @key field.\"\n directive @relayId on FIELD_DEFINITION\n\n \"Full-text search over String fields: a FULLTEXT index and a search root field per entry.\"\n directive @fulltext(indexes: [FulltextIndex!]!) on OBJECT\n\n \"A vector embedding ([Float!]): a VECTOR index and a similarity root field.\"\n directive @vector(\n dimensions: Int!\n similarity: VectorSimilarity = COSINE\n queryName: String\n ) on FIELD_DEFINITION\n\n input FulltextIndex {\n \"Index name. Default: <label>_search for the first, required after.\"\n name: String\n fields: [String!]!\n analyzer: FulltextAnalyzer = STANDARD\n \"Root field name. Default: search<Plural>, or search<Plural>By<Name>.\"\n queryName: String\n }\n\n enum FulltextAnalyzer {\n STANDARD\n SIMPLE\n }\n\n enum VectorSimilarity {\n COSINE\n EUCLIDEAN\n }\n\n \"Generated subscriptions to changes made through the library. None without this directive.\"\n directive @subscription(\n operations: [MutationOperation!]! = [CREATE, UPDATE, DELETE]\n \"Also send CONNECT and DISCONNECT events for the type's relationships.\"\n relationships: Boolean = false\n \"Send the stored values before an update or delete (one extra read per write).\"\n previousState: Boolean = false\n ) on OBJECT\n\n \"Generated mutations for a node type. None without this directive.\"\n directive @mutation(\n operations: [MutationOperation!]! = [CREATE, UPDATE, DELETE]\n ) on OBJECT\n\n \"Value stored on create when the input leaves the field out.\"\n directive @default(value: DefaultValue!) on FIELD_DEFINITION\n\n \"Set to the current time by the listed operations. Client-settable only with an explicit @settable(onCreate: true) or (onUpdate: true) and a field-level @authorization(validate:) rule for that operation.\"\n directive @timestamp(\n operations: [TimestampOperation!]! = [CREATE, UPDATE]\n ) on FIELD_DEFINITION\n\n \"Readable but never client-settable.\"\n directive @readonly on FIELD_DEFINITION\n\n \"A field backed by a Cypher statement. `this` is the parent node; arguments are $parameters, $jwt holds the request's claims, and $viewer the caller's @viewer node key.\"\n directive @cypher(statement: String!, columnName: String) on FIELD_DEFINITION\n\n \"Require an authenticated request (a jwt in the context) for these operations.\"\n directive @authentication(\n operations: [AuthOperation!]! = [\n READ\n CREATE\n UPDATE\n DELETE\n CREATE_RELATIONSHIP\n DELETE_RELATIONSHIP\n SUBSCRIBE\n ]\n \"Claims the request must also satisfy, such as a role in roles.\"\n jwt: AuthorizationWhere\n ) on OBJECT | FIELD_DEFINITION\n\n \"Row-level rules over the node and the request's claims, compiled into the statements.\"\n directive @authorization(\n filter: [AuthorizationFilterRule!]\n validate: [AuthorizationValidateRule!]\n \"On a type: false keeps the schema's bypass (see @authorizationDefaults) from skipping this type's rules; true lets it skip them, as without the argument, and tells check() that was meant.\"\n bypass: Boolean\n \"On a @mutation type: operations deliberately open to every caller, so check() does not report them as unguarded.\"\n public: [AuthOperation!]\n \"On a scalar field: a row failing unless reads the field as value (null when left out) instead of failing the request.\"\n mask: [AuthorizationMask!]\n ) on OBJECT | FIELD_DEFINITION\n\n \"Named rules over claims, usable in any rule as { rule: name }, on `extend schema`.\"\n directive @authorizationRules(\n rules: [AuthorizationRuleDefinition!]!\n ) on SCHEMA\n\n input AuthorizationRuleDefinition {\n name: String!\n where: AuthorizationWhere!\n }\n\n \"A named rule of this type, usable in its rules as { rule: name } and through a relationship to it as { rel: { rule: name } }.\"\n directive @authorizationRule(\n name: String!\n where: AuthorizationWhere!\n ) repeatable on OBJECT\n\n \"Schema-wide authorization settings, on `extend schema`.\"\n directive @authorizationDefaults(\n \"A claims-only test (jwt, AND, OR, NOT): a request passing it skips every filter and validate rule (not @authentication).\"\n bypass: AuthorizationWhere\n \"The write rule of every @mutation type that declares no CREATE, UPDATE or DELETE rule of its own.\"\n mutations: AuthorizationWhere\n \"Default true: without a token every rule denies. false: a rule with a branch that reads no claims decides that branch for anonymous requests too; a rule that needs claims still asks for a token.\"\n requireAuthentication: Boolean\n ) on SCHEMA\n\n input AuthorizationMask {\n unless: AuthorizationWhere!\n value: DefaultValue\n }\n\n input AuthorizationFilterRule {\n operations: [AuthOperation!]! = [READ, UPDATE, DELETE]\n where: AuthorizationWhere!\n }\n\n input AuthorizationValidateRule {\n operations: [AuthOperation!]! = [READ, CREATE, UPDATE, DELETE]\n when: [AuthorizationWhen!]! = [BEFORE, AFTER]\n where: AuthorizationWhere!\n }\n\n enum MutationOperation {\n CREATE\n UPDATE\n DELETE\n }\n\n enum TimestampOperation {\n CREATE\n UPDATE\n }\n\n enum AuthOperation {\n READ\n CREATE\n UPDATE\n DELETE\n CREATE_RELATIONSHIP\n DELETE_RELATIONSHIP\n SUBSCRIBE\n \"On a relationship field: creating one of its relationships (connect, nested create).\"\n CONNECT\n \"On a relationship field: removing one of its relationships (disconnect).\"\n DISCONNECT\n \"On a relationship field: setting properties on an existing relationship (edge update, re-connect).\"\n UPDATE_EDGE\n \"On a relationship field: reading, filtering, sorting or aggregating its properties.\"\n READ_EDGE\n }\n\n enum AuthorizationWhen {\n BEFORE\n AFTER\n }\n\n \"Any literal.\"\n scalar DefaultValue\n\n \"{ node: <Type>Where-shaped filter, jwt: claim filter, AND, OR, NOT }. String values starting with $jwt. are replaced by claims.\"\n scalar AuthorizationWhere\n\n \"A <Type>Where-shaped filter over the type the directive is on.\"\n scalar NodeWhere\n\n enum IndexKind {\n RANGE\n TEXT\n POINT\n }\n\n enum RelationshipDirection {\n IN\n OUT\n }\n\n enum FilterOperator {\n EQ\n IN\n LT\n LTE\n GT\n GTE\n CONTAINS\n STARTS_WITH\n ENDS_WITH\n WITHIN_BBOX\n DISTANCE\n INCLUDES\n IS_NULL\n CASE_INSENSITIVE\n }\n\n scalar BigInt\n scalar Date\n scalar Time\n scalar LocalTime\n scalar DateTime\n scalar LocalDateTime\n scalar Duration\n\n type Point {\n longitude: Float!\n latitude: Float!\n height: Float\n srid: Int!\n crs: String!\n }\n\n type CartesianPoint {\n x: Float!\n y: Float!\n z: Float\n srid: Int!\n crs: String!\n }\n";
|
|
1
|
+
export declare const directiveTypeDefs = "\n \"A node label set. Defaults to the type name.\"\n directive @node(labels: [String!], plural: String) on OBJECT\n\n \"Natural key: required, unique and immutable; the tie-breaker of every sort and the anchor of cursors, global ids, updates and deletes. With generate: true, creates fill it with a UUID when the input leaves it out.\"\n directive @key(\n generate: Boolean = false\n \"VIEWER: a created key must start with the caller's @viewer claim and the separator (lou:tomorrowland).\"\n scope: KeyScope\n separator: String = \":\"\n ) on FIELD_DEFINITION\n\n enum KeyScope {\n VIEWER\n }\n\n \"On an interface field: every implementation declares this relationship (with @relationship, possibly of different types or directions), so clients can select it on the interface.\"\n directive @declareRelationship on FIELD_DEFINITION\n\n \"Offer this field as a grouping key of <plural>Grouped (needs @query(aggregate: true)).\"\n directive @groupBy on FIELD_DEFINITION\n\n \"A field computed in JavaScript by the resolver passed in the resolvers option. requires is a selection on this type (for example name capacity) fetched in the same statement, so the resolver reads it from its source.\"\n directive @customResolver(requires: String) on FIELD_DEFINITION\n\n \"On a custom scalar: how its values are stored. Filters, sorts and indexes follow the storage type; the scalar is passed through unless the scalars option supplies an implementation.\"\n directive @storedAs(type: StorageType!) on SCALAR\n\n enum StorageType {\n STRING\n INT\n FLOAT\n BOOLEAN\n DATETIME\n DATE\n }\n\n \"A uniqueness constraint.\"\n directive @unique on FIELD_DEFINITION\n\n \"No two nodes of this type (among those matching where) share the values of fields: scalar fields, single relationships (by the target's @key) and at most one list relationship (by the set of target keys). Enforced by every generated mutation, for every caller.\"\n directive @uniqueTogether(\n fields: [String!]!\n where: NodeWhere\n ) repeatable on OBJECT\n\n \"An explicit index. Usually unnecessary: inferred from @filterable and @sortable.\"\n directive @index(kind: IndexKind!) on FIELD_DEFINITION\n\n \"An edge. The target type is the field's type.\"\n directive @relationship(\n type: String!\n direction: RelationshipDirection!\n properties: String\n \"UNDIRECTED reads follow the relationship both ways; writes use direction.\"\n queryDirection: QueryDirection = DIRECTED\n \"What deleting this node does to nodes reached through the field.\"\n onDelete: OnDelete = DETACH\n \"Which nested writes mutation inputs offer for the field.\"\n nestedOperations: [NestedOperation!] = [\n CREATE\n CONNECT\n DISCONNECT\n UPDATE\n DELETE\n ]\n \"false: no aggregate on the field's connection and no aggregate filter.\"\n aggregate: Boolean = true\n ) on FIELD_DEFINITION\n\n \"UPDATE changes a connected node and its relationship properties in place; UPDATE_EDGE only the properties.\"\n enum NestedOperation {\n CREATE\n CONNECT\n DISCONNECT\n UPDATE\n UPDATE_EDGE\n DELETE\n }\n\n enum QueryDirection {\n DIRECTED\n UNDIRECTED\n }\n\n \"DETACH removes the relationships; CASCADE deletes the related nodes too; RESTRICT refuses while any exist.\"\n enum OnDelete {\n DETACH\n CASCADE\n RESTRICT\n }\n\n \"Which mutations may set the field. @readonly is onCreate: false, onUpdate: false.\"\n directive @settable(\n onCreate: Boolean = true\n onUpdate: Boolean = true\n ) on FIELD_DEFINITION\n\n \"Whether the field can be read (onRead: false makes it write-only) or aggregated.\"\n directive @selectable(\n onRead: Boolean = true\n onAggregate: Boolean = true\n ) on FIELD_DEFINITION\n\n \"Set by a callback passed to LoraGraphQL({ callbacks }) on these operations. Client-settable only with an explicit @settable(onCreate: true) or (onUpdate: true) and a field-level @authorization(validate:) rule for that operation.\"\n directive @populatedBy(\n callback: String!\n operations: [TimestampOperation!]! = [CREATE, UPDATE]\n ) on FIELD_DEFINITION\n\n \"The shape of the request's claims. Rules may only test declared claims.\"\n directive @jwt on OBJECT\n\n \"Where a declared claim lives in the token, e.g. app_metadata.roles.\"\n directive @jwtClaim(path: String!) on FIELD_DEFINITION\n\n \"The claim that identifies the caller: the node of `type` whose `field` (a @key or @unique field) equals it. Enables isViewer and viewer in rules.\"\n directive @viewer(type: String!, field: String!) on FIELD_DEFINITION\n\n \"Properties carried by a relationship type.\"\n directive @relationshipProperties on OBJECT\n\n \"API name differs from the stored property.\"\n directive @alias(property: String!) on FIELD_DEFINITION\n\n \"Stored, never exposed.\"\n directive @private on FIELD_DEFINITION\n\n \"Declared upper bound on a relationship's fan-out, used by cost estimates.\"\n directive @cardinality(max: Int!) on FIELD_DEFINITION\n\n \"How many items a list holds, with unique refusing the same item twice. On a list field: checked on every write. On a list argument of a @cypher field: checked before the statement runs (without max, maxListArgument applies).\"\n directive @size(\n min: Int\n max: Int\n unique: Boolean\n \"Replaces the default message of this rule.\"\n message: String\n ) on ARGUMENT_DEFINITION | FIELD_DEFINITION\n\n \"Bounds of a number: min and max are inclusive, exclusiveMin and exclusiveMax are not. On an Int, Float or BigInt field (each item, for a list): checked on every write. On an argument of a @cypher field: checked before the statement runs.\"\n directive @range(\n min: Float\n max: Float\n exclusiveMin: Float\n exclusiveMax: Float\n \"Replaces the default message of this rule.\"\n message: String\n ) on ARGUMENT_DEFINITION | FIELD_DEFINITION | SCALAR\n\n \"How long a String or ID is, in Unicode code points (each item, for a list). On a custom scalar: for every field of that type.\"\n directive @length(\n min: Int\n max: Int\n \"Replaces the default message of this rule.\"\n message: String\n ) on FIELD_DEFINITION | SCALAR | ARGUMENT_DEFINITION\n\n \"An expression the whole value of a String or ID must match (each item, for a list). Needs @length(max:) on the same field.\"\n directive @pattern(\n regex: String!\n \"Replaces the default message of this rule.\"\n message: String\n ) on FIELD_DEFINITION | SCALAR | ARGUMENT_DEFINITION\n\n \"A named shape a String or ID must have: email, email-strict, url, uuid, hostname, ipv4, ipv6, slug, hex-color, ulid or e164.\"\n directive @format(\n as: String!\n \"Replaces the default message of this rule.\"\n message: String\n ) on FIELD_DEFINITION | SCALAR | ARGUMENT_DEFINITION\n\n \"Changes a String or ID before it is validated and stored (each item, for a list): trim removes surrounding whitespace, unicode normalises, case maps, in that order; emptyToNull then stores no value for an empty string. Opt in: nothing is changed without it.\"\n directive @normalize(\n trim: Boolean\n case: TextCase\n unicode: UnicodeForm\n emptyToNull: Boolean\n ) on FIELD_DEFINITION | SCALAR\n\n enum TextCase {\n LOWER\n UPPER\n }\n\n enum UnicodeForm {\n NFC\n NFD\n NFKC\n NFKD\n }\n\n \"An Int or Float must be a whole multiple of value (each item, for a list).\"\n directive @multipleOf(\n value: Float!\n \"Replaces the default message of this rule.\"\n message: String\n ) on FIELD_DEFINITION | SCALAR | ARGUMENT_DEFINITION\n\n \"Generated read operations for a node type. Reads are on by default.\"\n directive @query(\n read: Boolean = true\n aggregate: Boolean = false\n ) on OBJECT | INTERFACE | UNION\n\n \"The plural of an interface or union, for its root field.\"\n directive @plural(value: String!) on INTERFACE | UNION\n\n \"Filter operators for a field. Without arguments: EQ and IN. On a relationship field: enables relationship filters.\"\n directive @filterable(byValue: [FilterOperator!]) on FIELD_DEFINITION\n\n \"Sort and keyset-paginate on this field.\"\n directive @sortable on FIELD_DEFINITION\n\n \"Page size bounds for lists of this type.\"\n directive @limit(\n default: Int\n max: Int\n ) on OBJECT | INTERFACE | UNION | FIELD_DEFINITION\n\n \"Expose an opaque global id derived from this @key field.\"\n directive @relayId on FIELD_DEFINITION\n\n \"Full-text search over String fields: a FULLTEXT index and a search root field per entry.\"\n directive @fulltext(indexes: [FulltextIndex!]!) on OBJECT\n\n \"A vector embedding ([Float!]): a VECTOR index and a similarity root field.\"\n directive @vector(\n dimensions: Int!\n similarity: VectorSimilarity = COSINE\n queryName: String\n ) on FIELD_DEFINITION\n\n input FulltextIndex {\n \"Index name. Default: <label>_search for the first, required after.\"\n name: String\n fields: [String!]!\n analyzer: FulltextAnalyzer = STANDARD\n \"Root field name. Default: search<Plural>, or search<Plural>By<Name>.\"\n queryName: String\n }\n\n enum FulltextAnalyzer {\n STANDARD\n SIMPLE\n }\n\n enum VectorSimilarity {\n COSINE\n EUCLIDEAN\n }\n\n \"Generated subscriptions to changes made through the library. None without this directive.\"\n directive @subscription(\n operations: [MutationOperation!]! = [CREATE, UPDATE, DELETE]\n \"Also send CONNECT and DISCONNECT events for the type's relationships.\"\n relationships: Boolean = false\n \"Send the stored values before an update or delete (one extra read per write).\"\n previousState: Boolean = false\n ) on OBJECT\n\n \"A Cypher statement run inside the transaction of every generated mutation that creates, updates or deletes nodes of this type, before the commit. this is each written node; $jwt, $viewer and $operation are bound. Nodes it returns as created or updated join the mutation's write-set. Raw Cypher and @cypher mutations fire no trigger.\"\n directive @trigger(\n on: [MutationOperation!]!\n statement: String!\n \"Fire an UPDATE trigger only for nodes whose update set one of these fields.\"\n fields: [String!]\n ) repeatable on OBJECT\n\n \"Nodes of this type are work for lora.worker(): each one created by a generated mutation or returned as created by a @trigger is handed to the worker's handler after the commit, at least once, with retries. The delivery state is stored on the node, outside the API.\"\n directive @outbox on OBJECT\n\n \"Generated mutations for a node type. None without this directive.\"\n directive @mutation(\n operations: [MutationOperation!]! = [CREATE, UPDATE, DELETE]\n ) on OBJECT\n\n \"Value stored on create when the input leaves the field out.\"\n directive @default(value: DefaultValue!) on FIELD_DEFINITION\n\n \"Set to the current time by the listed operations. Client-settable only with an explicit @settable(onCreate: true) or (onUpdate: true) and a field-level @authorization(validate:) rule for that operation.\"\n directive @timestamp(\n operations: [TimestampOperation!]! = [CREATE, UPDATE]\n ) on FIELD_DEFINITION\n\n \"Readable but never client-settable.\"\n directive @readonly on FIELD_DEFINITION\n\n \"A field backed by a Cypher statement. `this` is the parent node; arguments are $parameters, $jwt holds the request's claims, and $viewer the caller's @viewer node key.\"\n directive @cypher(\n statement: String!\n columnName: String\n \"Rows the statement examines each time it runs, charged against maxCost: once for a root field, once per parent node for a field of a type. Without it only the rows the field returns are charged.\"\n cost: Int\n ) on FIELD_DEFINITION\n\n \"Require an authenticated request (a jwt in the context) for these operations.\"\n directive @authentication(\n operations: [AuthOperation!]! = [\n READ\n CREATE\n UPDATE\n DELETE\n CREATE_RELATIONSHIP\n DELETE_RELATIONSHIP\n SUBSCRIBE\n ]\n \"Claims the request must also satisfy, such as a role in roles.\"\n jwt: AuthorizationWhere\n ) on OBJECT | FIELD_DEFINITION\n\n \"Row-level rules over the node and the request's claims, compiled into the statements.\"\n directive @authorization(\n filter: [AuthorizationFilterRule!]\n validate: [AuthorizationValidateRule!]\n \"On a type: false keeps the schema's bypass (see @authorizationDefaults) from skipping this type's rules; true lets it skip them, as without the argument, and tells check() that was meant.\"\n bypass: Boolean\n \"On a @mutation type: operations deliberately open to every caller, so check() does not report them as unguarded.\"\n public: [AuthOperation!]\n \"On a scalar field: a row failing unless reads the field as value (null when left out) instead of failing the request.\"\n mask: [AuthorizationMask!]\n ) on OBJECT | FIELD_DEFINITION\n\n \"Named rules over claims, usable in any rule as { rule: name }, on `extend schema`.\"\n directive @authorizationRules(\n rules: [AuthorizationRuleDefinition!]!\n ) on SCHEMA\n\n input AuthorizationRuleDefinition {\n name: String!\n where: AuthorizationWhere!\n }\n\n \"A named rule of this type, usable in its rules as { rule: name } and through a relationship to it as { rel: { rule: name } }.\"\n directive @authorizationRule(\n name: String!\n where: AuthorizationWhere!\n ) repeatable on OBJECT\n\n \"Schema-wide authorization settings, on `extend schema`.\"\n directive @authorizationDefaults(\n \"A claims-only test (jwt, AND, OR, NOT): a request passing it skips every filter and validate rule (not @authentication).\"\n bypass: AuthorizationWhere\n \"The write rule of every @mutation type that declares no CREATE, UPDATE or DELETE rule of its own.\"\n mutations: AuthorizationWhere\n \"Default true: without a token every rule denies. false: a rule with a branch that reads no claims decides that branch for anonymous requests too; a rule that needs claims still asks for a token.\"\n requireAuthentication: Boolean\n ) on SCHEMA\n\n input AuthorizationMask {\n unless: AuthorizationWhere!\n value: DefaultValue\n }\n\n input AuthorizationFilterRule {\n operations: [AuthOperation!]! = [READ, UPDATE, DELETE]\n where: AuthorizationWhere!\n }\n\n input AuthorizationValidateRule {\n operations: [AuthOperation!]! = [READ, CREATE, UPDATE, DELETE]\n when: [AuthorizationWhen!]! = [BEFORE, AFTER]\n where: AuthorizationWhere!\n }\n\n enum MutationOperation {\n CREATE\n UPDATE\n DELETE\n }\n\n enum TimestampOperation {\n CREATE\n UPDATE\n }\n\n enum AuthOperation {\n READ\n CREATE\n UPDATE\n DELETE\n CREATE_RELATIONSHIP\n DELETE_RELATIONSHIP\n SUBSCRIBE\n \"On a relationship field: creating one of its relationships (connect, nested create).\"\n CONNECT\n \"On a relationship field: removing one of its relationships (disconnect).\"\n DISCONNECT\n \"On a relationship field: setting properties on an existing relationship (edge update, re-connect).\"\n UPDATE_EDGE\n \"On a relationship field: reading, filtering, sorting or aggregating its properties.\"\n READ_EDGE\n }\n\n enum AuthorizationWhen {\n BEFORE\n AFTER\n }\n\n \"Any literal.\"\n scalar DefaultValue\n\n \"{ node: <Type>Where-shaped filter, jwt: claim filter, AND, OR, NOT }. String values starting with $jwt. are replaced by claims.\"\n scalar AuthorizationWhere\n\n \"A <Type>Where-shaped filter over the type the directive is on.\"\n scalar NodeWhere\n\n enum IndexKind {\n RANGE\n TEXT\n POINT\n }\n\n enum RelationshipDirection {\n IN\n OUT\n }\n\n enum FilterOperator {\n EQ\n IN\n LT\n LTE\n GT\n GTE\n CONTAINS\n STARTS_WITH\n ENDS_WITH\n WITHIN_BBOX\n DISTANCE\n INCLUDES\n IS_NULL\n CASE_INSENSITIVE\n }\n\n scalar BigInt\n scalar Date\n scalar Time\n scalar LocalTime\n scalar DateTime\n scalar LocalDateTime\n scalar Duration\n\n type Point {\n longitude: Float!\n latitude: Float!\n height: Float\n srid: Int!\n crs: String!\n }\n\n type CartesianPoint {\n x: Float!\n y: Float!\n z: Float\n srid: Int!\n crs: String!\n }\n";
|
|
2
2
|
/** Names defined by the prelude; never treated as user types. */
|
|
3
3
|
export declare const PRELUDE_TYPES: Set<string>;
|
package/dist/model/types.d.ts
CHANGED
|
@@ -1,9 +1,26 @@
|
|
|
1
|
+
import { FieldConstraints } from './constraints.js';
|
|
1
2
|
export type ScalarType = "String" | "ID" | "Int" | "Float" | "Boolean" | "BigInt" | "Date" | "Time" | "LocalTime" | "DateTime" | "LocalDateTime" | "Duration" | "Point" | "CartesianPoint"
|
|
2
3
|
/** A user-declared enum, stored as its value name. */
|
|
3
4
|
| "Enum";
|
|
4
5
|
export type FilterOperator = "EQ" | "IN" | "LT" | "LTE" | "GT" | "GTE" | "CONTAINS" | "STARTS_WITH" | "ENDS_WITH" | "WITHIN_BBOX" | "DISTANCE" | "INCLUDES" | "IS_NULL" | "CASE_INSENSITIVE";
|
|
5
6
|
export type IndexKind = "RANGE" | "TEXT" | "POINT";
|
|
6
7
|
export type MutationOperation = "CREATE" | "UPDATE" | "DELETE";
|
|
8
|
+
/**
|
|
9
|
+
* `@trigger(on:, statement:, fields:)`: a statement run for the nodes of
|
|
10
|
+
* a type that a generated mutation wrote, inside its transaction.
|
|
11
|
+
*/
|
|
12
|
+
export interface Trigger {
|
|
13
|
+
operations: ReadonlySet<MutationOperation>;
|
|
14
|
+
statement: string;
|
|
15
|
+
/** `UPDATE` fires only for nodes whose update set one of these. */
|
|
16
|
+
fields: readonly string[] | undefined;
|
|
17
|
+
/** The `$parameters` the statement uses: `jwt`, `viewer`, `operation`. */
|
|
18
|
+
params: readonly string[];
|
|
19
|
+
/** Whether the statement has a write clause. */
|
|
20
|
+
writes: boolean;
|
|
21
|
+
/** Whether it returns a `created` or `updated` column. */
|
|
22
|
+
reports: boolean;
|
|
23
|
+
}
|
|
7
24
|
export type AuthOperation = "READ" | MutationOperation | "CREATE_RELATIONSHIP" | "DELETE_RELATIONSHIP" | "SUBSCRIBE" | RelationshipOperation;
|
|
8
25
|
/** Operations of rules on a relationship field, over `source`, `target`, `edge`. */
|
|
9
26
|
export type RelationshipOperation = "CONNECT" | "DISCONNECT" | "UPDATE_EDGE" | "READ_EDGE";
|
|
@@ -53,6 +70,8 @@ export interface ScalarField extends FieldBase {
|
|
|
53
70
|
defaultValue: {
|
|
54
71
|
value: unknown;
|
|
55
72
|
} | undefined;
|
|
73
|
+
/** `@length`, `@pattern`, `@format`, `@range`, `@multipleOf`, `@size`. */
|
|
74
|
+
constraints?: FieldConstraints | undefined;
|
|
56
75
|
/** `@timestamp`: set to the current time by these operations. */
|
|
57
76
|
timestamp: ReadonlySet<"CREATE" | "UPDATE"> | undefined;
|
|
58
77
|
/** Never client-settable (`@readonly`, `@timestamp`, `@private`). */
|
|
@@ -110,6 +129,13 @@ export interface RelationshipField extends FieldBase {
|
|
|
110
129
|
members: readonly string[];
|
|
111
130
|
/** For a member copy of a polymorphic field: the field it came from. */
|
|
112
131
|
via?: RelationshipField | undefined;
|
|
132
|
+
/**
|
|
133
|
+
* For a field to an interface or union with relationship rules (CONNECT,
|
|
134
|
+
* DISCONNECT, UPDATE_EDGE, READ_EDGE): the field's authorization per
|
|
135
|
+
* member, its rules expanded and checked with that member as their
|
|
136
|
+
* `target`. A member copy carries its own.
|
|
137
|
+
*/
|
|
138
|
+
memberAuthorization?: ReadonlyMap<string, Authorization> | undefined;
|
|
113
139
|
filterable: boolean;
|
|
114
140
|
/** UNDIRECTED: reads follow the relationship both ways. */
|
|
115
141
|
queryDirection: "DIRECTED" | "UNDIRECTED";
|
|
@@ -151,6 +177,8 @@ export interface CypherArgument {
|
|
|
151
177
|
min?: number | undefined;
|
|
152
178
|
max?: number | undefined;
|
|
153
179
|
} | undefined;
|
|
180
|
+
/** The other value rules of the argument, as on a field. */
|
|
181
|
+
constraints?: FieldConstraints | undefined;
|
|
154
182
|
}
|
|
155
183
|
export interface CypherField extends FieldBase {
|
|
156
184
|
kind: "cypher";
|
|
@@ -160,6 +188,8 @@ export interface CypherField extends FieldBase {
|
|
|
160
188
|
statement: string;
|
|
161
189
|
/** The result column holding the field's value. */
|
|
162
190
|
columnName: string;
|
|
191
|
+
/** `@cypher(cost:)`: rows the statement examines per run; 0 without. */
|
|
192
|
+
cost: number;
|
|
163
193
|
/** The field's type; `node` names a @node type when it returns nodes. */
|
|
164
194
|
type: TypeShape;
|
|
165
195
|
node: string | undefined;
|
|
@@ -281,6 +311,10 @@ export interface NodeType {
|
|
|
281
311
|
interfaces: readonly string[];
|
|
282
312
|
/** `@uniqueTogether`: combinations no two nodes of the type may share. */
|
|
283
313
|
uniqueTogether: readonly UniqueTogether[];
|
|
314
|
+
/** `@outbox`: nodes of the type are work for `lora.worker()`. */
|
|
315
|
+
outbox: boolean;
|
|
316
|
+
/** `@trigger`: statements run in the transaction of a mutation, in SDL order. */
|
|
317
|
+
triggers: readonly Trigger[];
|
|
284
318
|
description: string | undefined;
|
|
285
319
|
}
|
|
286
320
|
/**
|
package/dist/options.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { DocumentNode, GraphQLFieldResolver, GraphQLScalarType } from 'graphql';
|
|
2
2
|
import { LoraDriver, Statement } from './driver.js';
|
|
3
|
-
import { PopulatedByCallback } from './execute/mutate/env.js';
|
|
3
|
+
import { PopulatedByCallback, TriggerFunctions } from './execute/mutate/env.js';
|
|
4
4
|
import { LoraTransaction } from './execute/transaction.js';
|
|
5
5
|
import { DocumentGuards } from './guards.js';
|
|
6
6
|
import { ModelOptions } from './model/build/limits.js';
|
|
7
|
+
import { TypeValidator } from './standard-schema.js';
|
|
7
8
|
import { ObservabilityOptions } from './observe.js';
|
|
8
9
|
export interface LoraGraphQLOptions extends ModelOptions, ObservabilityOptions {
|
|
9
10
|
/** Annotated SDL: the graph model and the API in one document. */
|
|
@@ -61,6 +62,13 @@ export interface LoraGraphQLOptions extends ModelOptions, ObservabilityOptions {
|
|
|
61
62
|
* request. Also the default `limit` of bulk updates and deletes.
|
|
62
63
|
*/
|
|
63
64
|
maxBatch?: number;
|
|
65
|
+
/**
|
|
66
|
+
* Write transactions that may wait for the writer lock at once. One
|
|
67
|
+
* more fails at once with OVERLOADED (answer it with HTTP 503 and let
|
|
68
|
+
* the client back off) instead of joining a queue whose wait only
|
|
69
|
+
* grows: one writer runs at a time. Default 512; `Infinity` disables.
|
|
70
|
+
*/
|
|
71
|
+
maxQueuedWrites?: number;
|
|
64
72
|
/**
|
|
65
73
|
* Most items a list argument of a `@cypher` field takes, unless its
|
|
66
74
|
* `@size(max:)` says otherwise. More is BAD_USER_INPUT before the
|
|
@@ -85,6 +93,15 @@ export interface LoraGraphQLOptions extends ModelOptions, ObservabilityOptions {
|
|
|
85
93
|
* may hold. More is BAD_USER_INPUT before anything runs. Default 10 000.
|
|
86
94
|
*/
|
|
87
95
|
maxStringFilter?: number;
|
|
96
|
+
/**
|
|
97
|
+
* Most matches the `totalCount` of a root or search connection counts.
|
|
98
|
+
* A count stops there, so `totalCount` equal to this means "this many
|
|
99
|
+
* or more": enough for "1000+ results", at a bounded cost on a label
|
|
100
|
+
* of any size. Aggregates, and the `totalCount` of a connection nested
|
|
101
|
+
* under a node (bounded by that node's relationships), are not capped.
|
|
102
|
+
* Default: no cap.
|
|
103
|
+
*/
|
|
104
|
+
maxTotalCount?: number;
|
|
88
105
|
/**
|
|
89
106
|
* Changes a `changes()` consumer or subscriber may fall behind before it
|
|
90
107
|
* is ended with an error. Default 1000.
|
|
@@ -113,6 +130,50 @@ export interface LoraGraphQLOptions extends ModelOptions, ObservabilityOptions {
|
|
|
113
130
|
subscriptionTimeoutMs?: number;
|
|
114
131
|
/** Named callbacks for `@populatedBy(callback:)`. */
|
|
115
132
|
callbacks?: Record<string, PopulatedByCallback>;
|
|
133
|
+
/**
|
|
134
|
+
* Send a mutation whose statements are all known up front (today: a
|
|
135
|
+
* create with the nodes it creates and connects, or an update of one
|
|
136
|
+
* node by key that sets or adjusts scalar fields and connects or
|
|
137
|
+
* creates related nodes, on a schema without
|
|
138
|
+
* triggers) as
|
|
139
|
+
* one call that begins, runs and commits, with its rule checks as
|
|
140
|
+
* `assert()` statements: the writer lock is then never held across a
|
|
141
|
+
* hop to the JavaScript thread. Needs a LoraDB with `assert()`; asked
|
|
142
|
+
* once, and without it every mutation keeps its transaction. Default
|
|
143
|
+
* true; false keeps every mutation in a transaction.
|
|
144
|
+
*/
|
|
145
|
+
singleCallMutations?: boolean;
|
|
146
|
+
/**
|
|
147
|
+
* Trigger functions per node type: `{ Comment: { create, update,
|
|
148
|
+
* delete } }`. Each is called inside the transaction of a generated
|
|
149
|
+
* mutation that wrote nodes of the type, after the type's `@trigger`
|
|
150
|
+
* statements and before the commit, with the keys written, the
|
|
151
|
+
* write-set so far and the transaction. Throwing rolls the mutation
|
|
152
|
+
* back: a `GraphQLError` reaches the client as thrown, anything else
|
|
153
|
+
* is reported like a database error. Mutations a trigger runs in its
|
|
154
|
+
* transaction fire no trigger. A trigger holds the database's writer
|
|
155
|
+
* lock: keep network calls out of it.
|
|
156
|
+
*/
|
|
157
|
+
triggers?: Record<string, TriggerFunctions>;
|
|
158
|
+
/**
|
|
159
|
+
* Most time one trigger function may take, in milliseconds; past it
|
|
160
|
+
* the mutation fails with TIMEOUT and rolls back. Default 1000; 0
|
|
161
|
+
* disables.
|
|
162
|
+
*/
|
|
163
|
+
triggerTimeoutMs?: number;
|
|
164
|
+
/**
|
|
165
|
+
* A validator per node type, for what the validation directives do not
|
|
166
|
+
* say (a rule across two fields, a check against your own data): any
|
|
167
|
+
* Standard Schema object, which zod, valibot, arktype and yup schemas
|
|
168
|
+
* are. It is given each input of that type (a create's, an update's, a
|
|
169
|
+
* nested one's) after `@normalize` and after the field rules hold, and
|
|
170
|
+
* its issues are reported like theirs, in `extensions.violations` with
|
|
171
|
+
* `rule: "custom"`. What it returns is what is written, so its
|
|
172
|
+
* transforms apply. One schema validates every input; `{ create,
|
|
173
|
+
* update, upsert }` gives each kind its own, which matters because an
|
|
174
|
+
* update's input holds only the fields it sets.
|
|
175
|
+
*/
|
|
176
|
+
validators?: Record<string, TypeValidator>;
|
|
116
177
|
/**
|
|
117
178
|
* Implementations of custom scalars declared with `@storedAs`, e.g. one
|
|
118
179
|
* that validates e-mail addresses. Without one a scalar passes through.
|
|
@@ -190,8 +251,11 @@ export interface DatabaseErrorEvent {
|
|
|
190
251
|
/** Correlation id, also in the client error's `extensions.id`. */
|
|
191
252
|
id: string;
|
|
192
253
|
/**
|
|
193
|
-
* The root field that failed,
|
|
194
|
-
* change feed (it reopens from where it stopped after a backoff)
|
|
254
|
+
* The root field that failed, `changeFeed` for a failure of the
|
|
255
|
+
* change feed (it reopens from where it stopped after a backoff), or
|
|
256
|
+
* `worker:<Type>` for a worker: a node given up after `maxAttempts`
|
|
257
|
+
* (with the handler's last error), or a failed read or write of its
|
|
258
|
+
* own.
|
|
195
259
|
*/
|
|
196
260
|
field: string;
|
|
197
261
|
/** The engine's message, which may name labels, properties and Cypher. */
|
|
@@ -231,6 +295,15 @@ export interface CheckOptions {
|
|
|
231
295
|
* the GraphQL context to compile them under (e.g. `{ jwt }`, so rules
|
|
232
296
|
* compile as they do for a signed-in caller). Without one, an operation
|
|
233
297
|
* compiles as an anonymous request would.
|
|
298
|
+
*
|
|
299
|
+
* A query is compiled, not run. A mutation is run, because what it
|
|
300
|
+
* does depends on what it finds, in a transaction that is always rolled
|
|
301
|
+
* back: nothing is written and no change is reported, but its
|
|
302
|
+
* `@populatedBy` callbacks and trigger functions are called. Each
|
|
303
|
+
* statement it ran, a `@trigger`'s included, must start from keys: one
|
|
304
|
+
* that expands from a full scan is a finding. A mutation that fails is
|
|
305
|
+
* an error of the check, so give it variables that succeed against the
|
|
306
|
+
* database being checked.
|
|
234
307
|
*/
|
|
235
308
|
operations?: Array<{
|
|
236
309
|
name?: string;
|
package/dist/results.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { StoredViolation } from './analyze/stored.js';
|
|
1
2
|
import { ExecutionResult } from 'graphql';
|
|
2
3
|
import { CypherFinding } from './analyze/cypher-check.js';
|
|
3
4
|
import { SchemaRequirement } from './analyze/indexes.js';
|
|
@@ -8,7 +9,11 @@ import { ModelWarning } from './model/types.js';
|
|
|
8
9
|
export interface ExecutionTiming {
|
|
9
10
|
/** The whole `execute()` call: parse, validation, execution. */
|
|
10
11
|
totalMs: number;
|
|
11
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Time spent running statements in LoraDB, summed over the root fields.
|
|
14
|
+
* A query's root fields run concurrently, so this can exceed `totalMs`;
|
|
15
|
+
* within one field it never exceeds that field's `totalMs`.
|
|
16
|
+
*/
|
|
12
17
|
databaseMs: number;
|
|
13
18
|
/** Per root field, by response key (alias or name). */
|
|
14
19
|
fields: Record<string, {
|
|
@@ -67,4 +72,10 @@ export interface CheckReport {
|
|
|
67
72
|
operation: string;
|
|
68
73
|
message: string;
|
|
69
74
|
}>;
|
|
75
|
+
/**
|
|
76
|
+
* Stored values that break a validation rule of their field (not a
|
|
77
|
+
* failure): a rule added or tightened after they were written, or a
|
|
78
|
+
* value written with raw Cypher. At most 10 000 nodes per type are read.
|
|
79
|
+
*/
|
|
80
|
+
stored: StoredViolation[];
|
|
70
81
|
}
|
package/dist/schema/scalars.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import { GraphQLScalarType } from 'graphql';
|
|
1
|
+
import { GraphQLError, GraphQLScalarType, ValueNode } from 'graphql';
|
|
2
|
+
/**
|
|
3
|
+
* A value this scalar refuses. Coded here, so that it is BAD_USER_INPUT
|
|
4
|
+
* whichever way it arrived: as a variable, as a literal in the document
|
|
5
|
+
* (which fails GraphQL validation, before any resolver), through
|
|
6
|
+
* `execute()` or through another server on `getSchema()`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function badInput(message: string, nodes?: ValueNode): GraphQLError;
|
|
2
9
|
export declare const GraphQLDate: GraphQLScalarType<unknown, unknown>;
|
|
3
10
|
export declare const GraphQLTime: GraphQLScalarType<unknown, unknown>;
|
|
4
11
|
export declare const GraphQLLocalTime: GraphQLScalarType<unknown, unknown>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** A validator from any library that implements Standard Schema v1. */
|
|
2
|
+
export interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
3
|
+
readonly "~standard": {
|
|
4
|
+
readonly version: 1;
|
|
5
|
+
readonly vendor: string;
|
|
6
|
+
readonly validate: (value: unknown) => StandardResult<Output> | Promise<StandardResult<Output>>;
|
|
7
|
+
readonly types?: {
|
|
8
|
+
readonly input: Input;
|
|
9
|
+
readonly output: Output;
|
|
10
|
+
} | undefined;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export type StandardResult<Output> = {
|
|
14
|
+
readonly value: Output;
|
|
15
|
+
readonly issues?: undefined;
|
|
16
|
+
} | {
|
|
17
|
+
readonly issues: ReadonlyArray<StandardIssue>;
|
|
18
|
+
};
|
|
19
|
+
export interface StandardIssue {
|
|
20
|
+
readonly message: string;
|
|
21
|
+
/** Keys into the value; a segment is a key, or an object holding one. */
|
|
22
|
+
readonly path?: ReadonlyArray<PropertyKey | {
|
|
23
|
+
readonly key: PropertyKey;
|
|
24
|
+
}> | undefined;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The validator of one node type's mutation inputs: one schema for every
|
|
28
|
+
* input, or one per kind of input. An update's input holds only the
|
|
29
|
+
* fields it sets, and an upsert's only the key for certain, so a schema
|
|
30
|
+
* that requires fields belongs under `create`.
|
|
31
|
+
*/
|
|
32
|
+
export type TypeValidator = StandardSchemaV1 | {
|
|
33
|
+
create?: StandardSchemaV1;
|
|
34
|
+
update?: StandardSchemaV1;
|
|
35
|
+
upsert?: StandardSchemaV1;
|
|
36
|
+
};
|
|
37
|
+
export declare function isStandardSchema(value: unknown): value is StandardSchemaV1;
|
package/dist/testing.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loradb/lora-graphql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "Schema-first GraphQL for LoraDB: one annotated SDL becomes an executable, index-aware GraphQL API that compiles every operation to a single Cypher statement.",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"packageManager": "yarn@4.5.1",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"bench:load": "node bench/load/main.mjs"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
|
-
"@loradb/lora-node": "^0.
|
|
68
|
+
"@loradb/lora-node": "^0.25.0",
|
|
69
69
|
"graphql": "^16.8.0 || ^17.0.0"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|