@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.
- package/README.md +42 -10
- package/dist/analyze/access-lint.d.ts +7 -0
- package/dist/analyze/access.d.ts +2 -30
- package/dist/analyze/assert-schema.d.ts +19 -0
- package/dist/analyze/diff.d.ts +1 -1
- package/dist/analyze/plans.d.ts +1 -1
- package/dist/analyze/principals.d.ts +25 -0
- package/dist/analyze/verdicts.d.ts +45 -0
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/compile/cache.d.ts +1 -1
- package/dist/compile/read/abstract.d.ts +19 -0
- package/dist/compile/read/aggregates.d.ts +34 -0
- package/dist/compile/read/by-keys.d.ts +10 -0
- package/dist/compile/read/common.d.ts +30 -0
- package/dist/compile/read/connection.d.ts +26 -0
- package/dist/compile/read/cypher-field.d.ts +21 -0
- package/dist/compile/read/match.d.ts +11 -0
- package/dist/compile/read/project.d.ts +15 -0
- package/dist/compile/read/root.d.ts +5 -0
- package/dist/compile/read/search.d.ts +16 -0
- package/dist/compile/read/sort.d.ts +42 -0
- package/dist/compile/read/types.d.ts +61 -0
- package/dist/{diff-B9iDdELF.js → diff-aDivqYqr.js} +2 -2
- package/dist/diff-aDivqYqr.js.map +1 -0
- package/dist/{driver-C5dSoSD8.js → driver-DSnytFsU.js} +11092 -10674
- package/dist/driver-DSnytFsU.js.map +1 -0
- package/dist/execute/cypher-mutation.d.ts +1 -1
- package/dist/execute/mutate/adjust.d.ts +5 -0
- package/dist/execute/mutate/checks.d.ts +21 -0
- package/dist/execute/mutate/delete.d.ts +7 -0
- package/dist/execute/mutate/env.d.ts +73 -0
- package/dist/execute/mutate/errors.d.ts +7 -0
- package/dist/execute/mutate/fragments.d.ts +37 -0
- package/dist/execute/mutate/plan.d.ts +95 -0
- package/dist/execute/mutate/runner.d.ts +129 -0
- package/dist/execute/mutate/unique-together.d.ts +18 -0
- package/dist/execute/mutate.d.ts +2 -79
- package/dist/index.d.ts +10 -6
- package/dist/index.js +2 -2
- package/dist/lora-graphql.d.ts +12 -326
- package/dist/model/build/authorization.d.ts +31 -0
- package/dist/model/build/cypher-field.d.ts +11 -0
- package/dist/model/build/directive-args.d.ts +16 -0
- package/dist/model/build/limits.d.ts +18 -0
- package/dist/model/build/relationship-field.d.ts +11 -0
- package/dist/model/build/rule-checks.d.ts +23 -0
- package/dist/model/build/rule-references.d.ts +28 -0
- package/dist/model/build/scalar-field.d.ts +7 -0
- package/dist/model/build/search.d.ts +4 -0
- package/dist/model/build/shapes.d.ts +18 -0
- package/dist/model/build/unique-together.d.ts +5 -0
- package/dist/model/build.d.ts +3 -20
- package/dist/model/directives.d.ts +1 -1
- package/dist/model/types.d.ts +12 -2
- package/dist/options.d.ts +255 -0
- package/dist/results.d.ts +70 -0
- package/dist/runtime/custom-requires.d.ts +7 -0
- package/dist/runtime/database-error.d.ts +10 -0
- package/dist/runtime/documents.d.ts +35 -0
- package/dist/runtime/limits.d.ts +37 -0
- package/dist/runtime/request.d.ts +33 -0
- package/dist/runtime/timing.d.ts +13 -0
- package/dist/schema/aggregates.d.ts +17 -0
- package/dist/schema/base.d.ts +17 -0
- package/dist/schema/build.d.ts +3 -42
- package/dist/schema/filters.d.ts +10 -0
- package/dist/schema/hooks.d.ts +42 -0
- package/dist/schema/objects.d.ts +36 -0
- package/dist/schema/query.d.ts +8 -0
- package/dist/schema/resolvers.d.ts +13 -0
- package/dist/schema/sorts.d.ts +5 -0
- package/dist/schema/subscriptions.d.ts +7 -0
- package/dist/subscriptions/change-hub.d.ts +50 -0
- package/dist/subscriptions/compiles.d.ts +23 -0
- package/dist/subscriptions/events.d.ts +9 -0
- package/dist/subscriptions/filter-cost.d.ts +15 -0
- package/dist/subscriptions/subscriptions.d.ts +73 -0
- package/dist/testing.d.ts +4 -1
- package/dist/testing.js +34 -29
- package/dist/testing.js.map +1 -1
- package/package.json +2 -2
- package/dist/compile/read.d.ts +0 -102
- package/dist/diff-B9iDdELF.js.map +0 -1
- package/dist/driver-C5dSoSD8.js.map +0 -1
package/dist/model/types.d.ts
CHANGED
|
@@ -204,16 +204,26 @@ export declare const RULE_REFERENCES: Set<string>;
|
|
|
204
204
|
export type AuthorizationWhere = Record<string, unknown>;
|
|
205
205
|
export interface AuthorizationFilterRule {
|
|
206
206
|
operations: ReadonlySet<AuthOperation>;
|
|
207
|
+
/**
|
|
208
|
+
* Without a token the rule denies: the schema's
|
|
209
|
+
* `@authorizationDefaults(requireAuthentication:)`, and always true for
|
|
210
|
+
* a rule no anonymous request could pass.
|
|
211
|
+
*/
|
|
207
212
|
requireAuthentication: boolean;
|
|
208
|
-
/** `requireAuthentication`
|
|
213
|
+
/** The schema left `requireAuthentication` to its default (true). */
|
|
209
214
|
requireAuthenticationDefaulted?: boolean;
|
|
210
215
|
where: AuthorizationWhere;
|
|
211
216
|
}
|
|
212
217
|
export interface AuthorizationValidateRule {
|
|
213
218
|
operations: ReadonlySet<AuthOperation>;
|
|
214
219
|
when: ReadonlySet<"BEFORE" | "AFTER">;
|
|
220
|
+
/**
|
|
221
|
+
* Without a token the rule denies: the schema's
|
|
222
|
+
* `@authorizationDefaults(requireAuthentication:)`, and always true for
|
|
223
|
+
* a rule no anonymous request could pass.
|
|
224
|
+
*/
|
|
215
225
|
requireAuthentication: boolean;
|
|
216
|
-
/** `requireAuthentication`
|
|
226
|
+
/** The schema left `requireAuthentication` to its default (true). */
|
|
217
227
|
requireAuthenticationDefaulted?: boolean;
|
|
218
228
|
where: AuthorizationWhere;
|
|
219
229
|
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { DocumentNode, GraphQLFieldResolver, GraphQLScalarType } from 'graphql';
|
|
2
|
+
import { LoraDriver, Statement } from './driver.js';
|
|
3
|
+
import { PopulatedByCallback } from './execute/mutate/env.js';
|
|
4
|
+
import { LoraTransaction } from './execute/transaction.js';
|
|
5
|
+
import { DocumentGuards } from './guards.js';
|
|
6
|
+
import { ModelOptions } from './model/build/limits.js';
|
|
7
|
+
import { ObservabilityOptions } from './observe.js';
|
|
8
|
+
export interface LoraGraphQLOptions extends ModelOptions, ObservabilityOptions {
|
|
9
|
+
/** Annotated SDL: the graph model and the API in one document. */
|
|
10
|
+
typeDefs: string | DocumentNode;
|
|
11
|
+
driver: LoraDriver;
|
|
12
|
+
/** Timeout for every statement, in milliseconds. Default 10 000; 0 disables. */
|
|
13
|
+
timeoutMs?: number;
|
|
14
|
+
/**
|
|
15
|
+
* Time budget for all root fields of one query, in milliseconds: past
|
|
16
|
+
* it, the operation's statements are aborted and its unfinished fields
|
|
17
|
+
* fail with TIMEOUT. Default twice `timeoutMs` (20 000); 0 disables.
|
|
18
|
+
* Subscriptions are not bounded by it; mutations keep `timeoutMs` per
|
|
19
|
+
* statement.
|
|
20
|
+
*/
|
|
21
|
+
operationTimeoutMs?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Statements one operation runs at once: aliased root fields queue for
|
|
24
|
+
* a slot instead of taking every libuv worker, so one request cannot
|
|
25
|
+
* starve the others. Default 2.
|
|
26
|
+
*/
|
|
27
|
+
maxConcurrentStatements?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Approximate bytes the compile cache may hold, and again the parsed
|
|
30
|
+
* document cache: entries are evicted oldest first past it, besides the
|
|
31
|
+
* entry caps. Requests with more than 16 KiB of variables a field uses
|
|
32
|
+
* (long `in:` lists, embedding vectors) are compiled but not cached.
|
|
33
|
+
* Default 64 MiB.
|
|
34
|
+
*/
|
|
35
|
+
compileCacheBytes?: number;
|
|
36
|
+
/**
|
|
37
|
+
* Reject a root field whose estimated rows touched exceed this, before
|
|
38
|
+
* it runs. The estimate multiplies page sizes through nested lists,
|
|
39
|
+
* capped by relationship degrees from `analyze()` or `@cardinality`,
|
|
40
|
+
* and charges each filter the rows it examines: a label scan when no
|
|
41
|
+
* index answers it, the related nodes of every relationship it follows.
|
|
42
|
+
* Default 50 000; `Infinity` disables.
|
|
43
|
+
*/
|
|
44
|
+
maxCost?: number;
|
|
45
|
+
/**
|
|
46
|
+
* The cost limit for one request, from its context (for example by
|
|
47
|
+
* user or plan). Undefined falls back to `maxCost`.
|
|
48
|
+
*/
|
|
49
|
+
budget?: (context: unknown) => number | undefined;
|
|
50
|
+
/** Called with every root field's cost estimate, before it runs. */
|
|
51
|
+
onCost?: (event: CostEvent) => void;
|
|
52
|
+
/**
|
|
53
|
+
* The request's verified claims, from the GraphQL context. Default:
|
|
54
|
+
* `context.jwt`. The library never verifies tokens: do that in the
|
|
55
|
+
* server and put the claims in the context.
|
|
56
|
+
*/
|
|
57
|
+
jwt?: (context: unknown) => Record<string, unknown> | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* Most nodes one mutation may create, update or delete, nested ones
|
|
60
|
+
* included (and ten times as many relationships written). Default 1000: larger imports belong in a Cypher load, not a GraphQL
|
|
61
|
+
* request. Also the default `limit` of bulk updates and deletes.
|
|
62
|
+
*/
|
|
63
|
+
maxBatch?: number;
|
|
64
|
+
/**
|
|
65
|
+
* Most items a list argument of a `@cypher` field takes, unless its
|
|
66
|
+
* `@size(max:)` says otherwise. More is BAD_USER_INPUT before the
|
|
67
|
+
* statement runs. Default 1000.
|
|
68
|
+
*/
|
|
69
|
+
maxListArgument?: number;
|
|
70
|
+
/**
|
|
71
|
+
* Relationship levels one `where` may nest: quantifiers (`some`,
|
|
72
|
+
* `none`, `all`, `single`, `count`, `aggregate`), connection filters,
|
|
73
|
+
* `<field>Exists` and filters through a single relationship each count
|
|
74
|
+
* one. Deeper is BAD_USER_INPUT before anything runs: each level
|
|
75
|
+
* multiplies the work by the relationship's degree. Default 2.
|
|
76
|
+
*/
|
|
77
|
+
maxFilterDepth?: number;
|
|
78
|
+
/**
|
|
79
|
+
* Items an `in` filter operand may hold. More is BAD_USER_INPUT before
|
|
80
|
+
* anything runs. Default 1000.
|
|
81
|
+
*/
|
|
82
|
+
maxListFilter?: number;
|
|
83
|
+
/**
|
|
84
|
+
* Characters a string filter operand (`eq`, `contains`, an `in` item, …)
|
|
85
|
+
* may hold. More is BAD_USER_INPUT before anything runs. Default 10 000.
|
|
86
|
+
*/
|
|
87
|
+
maxStringFilter?: number;
|
|
88
|
+
/**
|
|
89
|
+
* Changes a `changes()` consumer or subscriber may fall behind before it
|
|
90
|
+
* is ended with an error. Default 1000.
|
|
91
|
+
*/
|
|
92
|
+
maxQueuedChanges?: number;
|
|
93
|
+
/**
|
|
94
|
+
* Live subscriptions one scope (see `subscriptionScope`) may hold; one
|
|
95
|
+
* more is LIMIT_EXCEEDED. Default 100.
|
|
96
|
+
*/
|
|
97
|
+
maxSubscriptions?: number;
|
|
98
|
+
/**
|
|
99
|
+
* What `maxSubscriptions` counts per: by default the context object, so
|
|
100
|
+
* a server that reuses one context per connection (graphql-ws) limits
|
|
101
|
+
* each connection. Return the connection (or user) otherwise.
|
|
102
|
+
*/
|
|
103
|
+
subscriptionScope?: (context: unknown) => object | undefined;
|
|
104
|
+
/**
|
|
105
|
+
* How deep relationship filters may nest in a subscription's `where`,
|
|
106
|
+
* which runs on every change. Deeper is LIMIT_EXCEEDED. Default 1.
|
|
107
|
+
*/
|
|
108
|
+
maxSubscriptionFilterDepth?: number;
|
|
109
|
+
/**
|
|
110
|
+
* Per statement a subscription runs to check a change (visibility,
|
|
111
|
+
* `where`, related nodes). Default 2000, or `timeoutMs` when lower.
|
|
112
|
+
*/
|
|
113
|
+
subscriptionTimeoutMs?: number;
|
|
114
|
+
/** Named callbacks for `@populatedBy(callback:)`. */
|
|
115
|
+
callbacks?: Record<string, PopulatedByCallback>;
|
|
116
|
+
/**
|
|
117
|
+
* Implementations of custom scalars declared with `@storedAs`, e.g. one
|
|
118
|
+
* that validates e-mail addresses. Without one a scalar passes through.
|
|
119
|
+
*/
|
|
120
|
+
scalars?: Record<string, GraphQLScalarType>;
|
|
121
|
+
/**
|
|
122
|
+
* Feed subscriptions and `changes()` from the engine's committed change
|
|
123
|
+
* feed (lora-node `db.changes()`): every write to the database, from
|
|
124
|
+
* any path in the process that owns it, in commit order. `onWrite`
|
|
125
|
+
* still reports this instance's mutations, and `previousState` needs
|
|
126
|
+
* them. Default false.
|
|
127
|
+
*/
|
|
128
|
+
changeFeed?: boolean;
|
|
129
|
+
/**
|
|
130
|
+
* Resolvers of `@customResolver` fields, by type and field name. The
|
|
131
|
+
* source holds the node's selected fields plus the field's `requires`.
|
|
132
|
+
*/
|
|
133
|
+
resolvers?: Record<string, Record<string, GraphQLFieldResolver<Record<string, unknown>, unknown>>>;
|
|
134
|
+
/** Called with every statement before it runs; for logging and tests. */
|
|
135
|
+
onStatement?: (event: StatementEvent) => void;
|
|
136
|
+
/**
|
|
137
|
+
* Hide database error details from clients: they get
|
|
138
|
+
* `extensions.code` `DATABASE_ERROR` and an `id`, and `onError` gets the
|
|
139
|
+
* engine's message. Default: on when `NODE_ENV` is `production`.
|
|
140
|
+
*/
|
|
141
|
+
maskErrors?: boolean;
|
|
142
|
+
/** Called with every database error, masked or not. */
|
|
143
|
+
onError?: (event: DatabaseErrorEvent) => void;
|
|
144
|
+
/**
|
|
145
|
+
* Limits on the documents `execute()`, `subscribe()` and `persist()`
|
|
146
|
+
* accept: depth,
|
|
147
|
+
* aliases, root fields, tokens, introspection. `false` turns them off.
|
|
148
|
+
* For other servers use `validationRules()` or `envelopPlugin()`.
|
|
149
|
+
*/
|
|
150
|
+
guards?: DocumentGuards | false;
|
|
151
|
+
/**
|
|
152
|
+
* Make `execute()` and `subscribe()` refuse `source` and run only
|
|
153
|
+
* persisted operations by `id`. Default false.
|
|
154
|
+
*/
|
|
155
|
+
persistedOnly?: boolean;
|
|
156
|
+
/**
|
|
157
|
+
* How `execute()` commits a mutation with several root fields.
|
|
158
|
+
* `"field"` (the default): each root field in its own transaction, so a
|
|
159
|
+
* later failure leaves the earlier ones committed. `"operation"`: every
|
|
160
|
+
* root field in one transaction, committed only when the operation
|
|
161
|
+
* reports no error, rolled back (with `data: null`) otherwise. A
|
|
162
|
+
* `transaction` in the context takes precedence. Envelop / Yoga servers
|
|
163
|
+
* on `getSchema()` get the same with `lora.envelopPlugin()`; other
|
|
164
|
+
* servers calling graphql-js directly put a `lora.begin()` transaction
|
|
165
|
+
* in the context (without one, a one-time warning says each root field
|
|
166
|
+
* committed on its own).
|
|
167
|
+
*/
|
|
168
|
+
mutationTransaction?: "field" | "operation";
|
|
169
|
+
/**
|
|
170
|
+
* Add `extensions.timing` to `execute()` results: the request's total
|
|
171
|
+
* time, the time spent in LoraDB, and both per root field, in
|
|
172
|
+
* milliseconds. `true` for every request, or a function deciding per
|
|
173
|
+
* request from the GraphQL context (for example only for admins or in
|
|
174
|
+
* development). Default off: timings sent to clients can act as a
|
|
175
|
+
* timing side channel.
|
|
176
|
+
*/
|
|
177
|
+
timing?: boolean | ((context: unknown) => boolean);
|
|
178
|
+
}
|
|
179
|
+
export interface CostEvent {
|
|
180
|
+
field: string;
|
|
181
|
+
/** Estimated rows this root field touches. */
|
|
182
|
+
cost: number;
|
|
183
|
+
/** Estimated rows of the operation so far, this field included. */
|
|
184
|
+
total: number;
|
|
185
|
+
/** The limit that applies: `budget(context)` or `maxCost`. */
|
|
186
|
+
limit: number;
|
|
187
|
+
context: unknown;
|
|
188
|
+
}
|
|
189
|
+
export interface DatabaseErrorEvent {
|
|
190
|
+
/** Correlation id, also in the client error's `extensions.id`. */
|
|
191
|
+
id: string;
|
|
192
|
+
/**
|
|
193
|
+
* The root field that failed, or `changeFeed` for a failure of the
|
|
194
|
+
* change feed (it reopens from where it stopped after a backoff).
|
|
195
|
+
*/
|
|
196
|
+
field: string;
|
|
197
|
+
/** The engine's message, which may name labels, properties and Cypher. */
|
|
198
|
+
message: string;
|
|
199
|
+
/** The error as thrown by the driver. */
|
|
200
|
+
error: unknown;
|
|
201
|
+
}
|
|
202
|
+
export interface StatementEvent {
|
|
203
|
+
field: string;
|
|
204
|
+
statement: Statement;
|
|
205
|
+
}
|
|
206
|
+
/** Per-request options read from the GraphQL context, when present. */
|
|
207
|
+
export interface LoraGraphQLContext {
|
|
208
|
+
/** Verified claims; enables @authentication and @authorization. */
|
|
209
|
+
jwt?: Record<string, unknown>;
|
|
210
|
+
/** Cancels the request's statements when aborted. */
|
|
211
|
+
signal?: AbortSignal;
|
|
212
|
+
/**
|
|
213
|
+
* Run in this transaction (from `lora.begin()`) instead of one per
|
|
214
|
+
* field: the caller commits, together with its own Cypher.
|
|
215
|
+
*/
|
|
216
|
+
transaction?: LoraTransaction;
|
|
217
|
+
}
|
|
218
|
+
export interface AssertSchemaOptions {
|
|
219
|
+
/** Create what is missing instead of only reporting it. */
|
|
220
|
+
create?: boolean;
|
|
221
|
+
}
|
|
222
|
+
export interface CheckOptions {
|
|
223
|
+
/**
|
|
224
|
+
* Flag statements whose largest engine row estimate (from graph
|
|
225
|
+
* statistics) exceeds this. Off by default: estimates ignore a LIMIT
|
|
226
|
+
* that stops an index-ordered scan early.
|
|
227
|
+
*/
|
|
228
|
+
rowBudget?: number;
|
|
229
|
+
/**
|
|
230
|
+
* Operations to compile and plan-check (S2), with example variables and
|
|
231
|
+
* the GraphQL context to compile them under (e.g. `{ jwt }`, so rules
|
|
232
|
+
* compile as they do for a signed-in caller). Without one, an operation
|
|
233
|
+
* compiles as an anonymous request would.
|
|
234
|
+
*/
|
|
235
|
+
operations?: Array<{
|
|
236
|
+
name?: string;
|
|
237
|
+
document: string | DocumentNode;
|
|
238
|
+
variables?: Record<string, unknown>;
|
|
239
|
+
context?: unknown;
|
|
240
|
+
}>;
|
|
241
|
+
}
|
|
242
|
+
export interface ExecuteArgs {
|
|
243
|
+
/** The document, or `id` of a persisted operation. */
|
|
244
|
+
source?: string;
|
|
245
|
+
id?: string;
|
|
246
|
+
variables?: Record<string, unknown>;
|
|
247
|
+
operationName?: string;
|
|
248
|
+
context?: unknown;
|
|
249
|
+
/**
|
|
250
|
+
* Attach the operation's read-set to the result as a non-enumerable
|
|
251
|
+
* `readSet` property (never serialized to the client), for a response
|
|
252
|
+
* cache to invalidate with `lora.affects(result.readSet, change)`.
|
|
253
|
+
*/
|
|
254
|
+
readSet?: boolean;
|
|
255
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { ExecutionResult } from 'graphql';
|
|
2
|
+
import { CypherFinding } from './analyze/cypher-check.js';
|
|
3
|
+
import { SchemaRequirement } from './analyze/indexes.js';
|
|
4
|
+
import { PlanReport } from './analyze/plans.js';
|
|
5
|
+
import { ReadSet } from './compile/read/types.js';
|
|
6
|
+
import { ModelWarning } from './model/types.js';
|
|
7
|
+
/** `extensions.timing` of an `execute()` result (see the `timing` option). */
|
|
8
|
+
export interface ExecutionTiming {
|
|
9
|
+
/** The whole `execute()` call: parse, validation, execution. */
|
|
10
|
+
totalMs: number;
|
|
11
|
+
/** Time spent running statements in LoraDB. */
|
|
12
|
+
databaseMs: number;
|
|
13
|
+
/** Per root field, by response key (alias or name). */
|
|
14
|
+
fields: Record<string, {
|
|
15
|
+
totalMs: number;
|
|
16
|
+
databaseMs: number;
|
|
17
|
+
}>;
|
|
18
|
+
}
|
|
19
|
+
/** `execute()`'s result; `readSet` is there when `ExecuteArgs.readSet` asked. */
|
|
20
|
+
export type LoraExecutionResult = ExecutionResult & {
|
|
21
|
+
readonly readSet?: ReadSet;
|
|
22
|
+
};
|
|
23
|
+
export interface SchemaAssertion {
|
|
24
|
+
required: SchemaRequirement[];
|
|
25
|
+
missing: SchemaRequirement[];
|
|
26
|
+
created: SchemaRequirement[];
|
|
27
|
+
/**
|
|
28
|
+
* Full-text and vector indexes present under their name but defined
|
|
29
|
+
* differently (labels, properties, kind): search would keep using the
|
|
30
|
+
* old definition. Without `create`; with it they are re-created.
|
|
31
|
+
*/
|
|
32
|
+
mismatched: SchemaRequirement[];
|
|
33
|
+
/** Indexes `create` dropped and created again with the model's definition. */
|
|
34
|
+
recreated: SchemaRequirement[];
|
|
35
|
+
}
|
|
36
|
+
export interface CheckReport {
|
|
37
|
+
/** True when nothing below is a failure. */
|
|
38
|
+
ok: boolean;
|
|
39
|
+
/** Model warnings, e.g. an unused @cypher argument. */
|
|
40
|
+
warnings: readonly ModelWarning[];
|
|
41
|
+
/** @cypher statements the engine rejects or that write from a query. */
|
|
42
|
+
cypher: CypherFinding[];
|
|
43
|
+
/** Constraints and indexes the database lacks. */
|
|
44
|
+
missing: SchemaRequirement[];
|
|
45
|
+
/** Indexes the database has that no part of the API needs. */
|
|
46
|
+
unused: Array<{
|
|
47
|
+
name: string;
|
|
48
|
+
type: string;
|
|
49
|
+
labels: string[];
|
|
50
|
+
properties: string[];
|
|
51
|
+
}>;
|
|
52
|
+
/** Schema lint: valid but costly or risky choices (not failures). */
|
|
53
|
+
lint: readonly ModelWarning[];
|
|
54
|
+
/**
|
|
55
|
+
* `@mutation` types with writes no rule guards (failures): declare them
|
|
56
|
+
* with `@authorization(public: [...])` when that is intended.
|
|
57
|
+
*/
|
|
58
|
+
security: readonly ModelWarning[];
|
|
59
|
+
/** Plan findings per operation and root field. */
|
|
60
|
+
plans: Array<{
|
|
61
|
+
operation: string;
|
|
62
|
+
field: string;
|
|
63
|
+
reports: PlanReport[];
|
|
64
|
+
}>;
|
|
65
|
+
/** Operations that failed to compile. */
|
|
66
|
+
errors: Array<{
|
|
67
|
+
operation: string;
|
|
68
|
+
message: string;
|
|
69
|
+
}>;
|
|
70
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { GraphQLSchema } from 'graphql';
|
|
2
|
+
import { GraphModel } from '../model/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Every `@customResolver(requires:)` is a valid selection on its type and
|
|
5
|
+
* does not require custom fields (they are computed after the read).
|
|
6
|
+
*/
|
|
7
|
+
export declare function checkCustomRequires(model: GraphModel, schema: GraphQLSchema): void;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { GraphModel } from '../model/types.js';
|
|
2
|
+
import { DatabaseErrorEvent } from '../options.js';
|
|
3
|
+
export interface DatabaseErrorEnv {
|
|
4
|
+
model: GraphModel;
|
|
5
|
+
/** Hide database error details from clients (the `maskErrors` option). */
|
|
6
|
+
maskErrors: boolean;
|
|
7
|
+
onError: ((event: DatabaseErrorEvent) => void) | undefined;
|
|
8
|
+
}
|
|
9
|
+
/** The error to throw for `err`, raised while `field` ran. */
|
|
10
|
+
export declare function databaseError(env: DatabaseErrorEnv, field: string, err: unknown): unknown;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { DocumentNode, ExecutionResult, GraphQLSchema, OperationDefinitionNode, ValidationRule } from 'graphql';
|
|
2
|
+
import { OperationManifest } from '../codegen.js';
|
|
3
|
+
import { DocumentGuards } from '../guards.js';
|
|
4
|
+
import { ExecuteArgs } from '../options.js';
|
|
5
|
+
export interface DocumentsOptions {
|
|
6
|
+
guards: DocumentGuards | false;
|
|
7
|
+
persistedOnly: boolean;
|
|
8
|
+
/** Approximate bytes the parsed document cache may hold. */
|
|
9
|
+
bytesMax: number;
|
|
10
|
+
/** The executable schema, built on first use. */
|
|
11
|
+
schema: () => GraphQLSchema;
|
|
12
|
+
/** The configured document guards as validation rules. */
|
|
13
|
+
rules: () => ValidationRule[];
|
|
14
|
+
}
|
|
15
|
+
export declare class Documents {
|
|
16
|
+
#private;
|
|
17
|
+
constructor(options: DocumentsOptions);
|
|
18
|
+
/**
|
|
19
|
+
* Register persisted operations by id. Every document is parsed and
|
|
20
|
+
* validated now, so a broken one fails at startup; at request time an
|
|
21
|
+
* id is looked up and executed without parsing or validating.
|
|
22
|
+
*/
|
|
23
|
+
persist(operations: Record<string, string>): void;
|
|
24
|
+
/** Register a manifest's operations, without parsing or validating. */
|
|
25
|
+
load(manifest: OperationManifest): void;
|
|
26
|
+
/** A persisted operation's document, by id. */
|
|
27
|
+
persisted(id: string): DocumentNode | undefined;
|
|
28
|
+
/** The persisted id an operation node was registered under. */
|
|
29
|
+
persistedId(operation: OperationDefinitionNode): string | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* The document `args` names: a persisted one by id, or `source` parsed
|
|
32
|
+
* and validated through the cache. An error result when there is none.
|
|
33
|
+
*/
|
|
34
|
+
document(args: ExecuteArgs): DocumentNode | ExecutionResult;
|
|
35
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { GraphQLResolveInfo } from 'graphql';
|
|
2
|
+
import { OperationBudget } from '../execute/budget.js';
|
|
3
|
+
import { LoraGraphQLOptions } from '../options.js';
|
|
4
|
+
export interface OperationLimitsOptions {
|
|
5
|
+
operationTimeoutMs: number;
|
|
6
|
+
maxConcurrentStatements: number;
|
|
7
|
+
maxCost: number;
|
|
8
|
+
budget: LoraGraphQLOptions["budget"];
|
|
9
|
+
onCost: LoraGraphQLOptions["onCost"];
|
|
10
|
+
}
|
|
11
|
+
export declare class OperationLimits {
|
|
12
|
+
#private;
|
|
13
|
+
constructor(options: OperationLimitsOptions);
|
|
14
|
+
/** Mark `execution` as a root value `execute()` made. */
|
|
15
|
+
track(execution: object): void;
|
|
16
|
+
/** The estimated cost charged to an execution so far. */
|
|
17
|
+
spent(execution: object): number | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Charge `cost` to the operation: the limit holds per operation, so
|
|
20
|
+
* aliasing a root field many times does not multiply it. A
|
|
21
|
+
* subscription is charged per event.
|
|
22
|
+
*/
|
|
23
|
+
charge(field: string, cost: number, context: unknown, info?: GraphQLResolveInfo): void;
|
|
24
|
+
/**
|
|
25
|
+
* The deadline and statement slots of the query `info` belongs to,
|
|
26
|
+
* started by its first root field. Subscriptions run indefinitely and
|
|
27
|
+
* are bounded per statement only.
|
|
28
|
+
*/
|
|
29
|
+
operationBudget(context: unknown, info: GraphQLResolveInfo | undefined): OperationBudget | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* A root field is done. When none is left in flight after the current
|
|
32
|
+
* turn (graphql-js starts a query's root fields together, and the next
|
|
33
|
+
* root field of a mutation in a microtask), the operation is over: a
|
|
34
|
+
* context reused for another request starts a fresh budget.
|
|
35
|
+
*/
|
|
36
|
+
leaveOperation(context: object, info: GraphQLResolveInfo, budget: OperationBudget): void;
|
|
37
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { GraphQLError, DocumentNode, ExecutionResult, GraphQLResolveInfo, OperationDefinitionNode } from 'graphql';
|
|
2
|
+
import { RootKind } from '../compile/read/types.js';
|
|
3
|
+
import { SelectionContext } from '../compile/selection.js';
|
|
4
|
+
import { QueryResult, Statement } from '../driver.js';
|
|
5
|
+
import { LoraTransaction } from '../execute/transaction.js';
|
|
6
|
+
import { GraphModel, NodeType, SearchIndex } from '../model/types.js';
|
|
7
|
+
/** The read root field of a node type that `fieldName` names, if any. */
|
|
8
|
+
export declare function rootOf(model: GraphModel, fieldName: string): {
|
|
9
|
+
kind: RootKind;
|
|
10
|
+
node: NodeType;
|
|
11
|
+
} | undefined;
|
|
12
|
+
export declare function searchOf(model: GraphModel, fieldName: string): {
|
|
13
|
+
node: NodeType;
|
|
14
|
+
index: SearchIndex;
|
|
15
|
+
connection: boolean;
|
|
16
|
+
} | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* A mutation with several root fields; a fragment at the root counts as
|
|
19
|
+
* several, since it may hold more than one.
|
|
20
|
+
*/
|
|
21
|
+
export declare function multiRoot(operation: OperationDefinitionNode): boolean;
|
|
22
|
+
export declare function isDocument(value: DocumentNode | ExecutionResult): value is DocumentNode;
|
|
23
|
+
/**
|
|
24
|
+
* The type of the operation `operationName` selects; undefined when it
|
|
25
|
+
* selects none, which graphql then reports.
|
|
26
|
+
*/
|
|
27
|
+
export declare function operationType(document: DocumentNode, operationName: string | null | undefined): "query" | "mutation" | "subscription" | undefined;
|
|
28
|
+
/** A variable graphql-js could not coerce: a request error with no code. */
|
|
29
|
+
export declare function isVariableError(e: GraphQLError): boolean;
|
|
30
|
+
export declare function findOperation(doc: DocumentNode, operationName: string | undefined): OperationDefinitionNode;
|
|
31
|
+
export declare function infoContext(info: GraphQLResolveInfo): SelectionContext;
|
|
32
|
+
/** Run statements one after the other in a caller-owned transaction. */
|
|
33
|
+
export declare function runInOrder(tx: LoraTransaction, statements: Statement[]): Promise<QueryResult[]>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { GraphQLResolveInfo } from 'graphql';
|
|
2
|
+
/** A request's timing entry for a root field, created on first use. */
|
|
3
|
+
export declare function timingEntry(timing: Map<string, {
|
|
4
|
+
totalMs: number;
|
|
5
|
+
databaseMs: number;
|
|
6
|
+
}>, key: string): {
|
|
7
|
+
totalMs: number;
|
|
8
|
+
databaseMs: number;
|
|
9
|
+
};
|
|
10
|
+
/** The response key of the root field `info` belongs to. */
|
|
11
|
+
export declare function rootKey(info: GraphQLResolveInfo | undefined): string | undefined;
|
|
12
|
+
/** Milliseconds, to a hundredth. */
|
|
13
|
+
export declare const ms: (value: number) => number;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { GraphQLEnumType, GraphQLObjectType } from 'graphql';
|
|
2
|
+
import { GraphModel, ScalarField } from '../model/types.js';
|
|
3
|
+
import { BaseTypes } from './base.js';
|
|
4
|
+
/** Fields whose values can be aggregated: ordered scalars, not lists. */
|
|
5
|
+
export declare const aggregatable: (f: ScalarField) => boolean;
|
|
6
|
+
export declare const isNumeric: (f: ScalarField) => boolean;
|
|
7
|
+
/** The memoized `<Scalar>Aggregate` type of a field, shared by every aggregate. */
|
|
8
|
+
export declare function buildAggregateFieldType(base: BaseTypes): (f: ScalarField) => GraphQLObjectType<any, any>;
|
|
9
|
+
export type AggregateFieldType = ReturnType<typeof buildAggregateFieldType>;
|
|
10
|
+
export declare function buildAggregates(model: GraphModel, base: BaseTypes, aggregateFieldType: AggregateFieldType): {
|
|
11
|
+
aggregates: Map<string, GraphQLObjectType<any, any>>;
|
|
12
|
+
groups: Map<string, {
|
|
13
|
+
field: GraphQLEnumType;
|
|
14
|
+
group: GraphQLObjectType;
|
|
15
|
+
}>;
|
|
16
|
+
};
|
|
17
|
+
export type AggregateTypes = ReturnType<typeof buildAggregates>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { GraphQLEnumType, GraphQLInputObjectType, GraphQLInterfaceType, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLInputType, GraphQLOutputType, GraphQLScalarType } from 'graphql';
|
|
2
|
+
import { GraphModel, ScalarField, ScalarType } from '../model/types.js';
|
|
3
|
+
import { SchemaHooks } from './hooks.js';
|
|
4
|
+
export declare const nonNull: <T extends GraphQLOutputType | GraphQLInputType>(t: T) => GraphQLNonNull<T>;
|
|
5
|
+
export declare const listOf: <T extends GraphQLOutputType | GraphQLInputType>(t: T) => GraphQLList<T>;
|
|
6
|
+
export declare function buildBaseTypes(model: GraphModel, hooks: SchemaHooks): {
|
|
7
|
+
sortDirection: GraphQLEnumType;
|
|
8
|
+
pageInfo: GraphQLObjectType<any, any>;
|
|
9
|
+
countFilter: GraphQLInputObjectType;
|
|
10
|
+
enums: Map<string, GraphQLEnumType>;
|
|
11
|
+
nodeInterface: GraphQLInterfaceType | undefined;
|
|
12
|
+
customScalar: (name: string) => GraphQLScalarType | undefined;
|
|
13
|
+
scalarType: (f: ScalarField) => GraphQLScalarType | GraphQLEnumType | GraphQLObjectType;
|
|
14
|
+
baseType: (type: ScalarType, enumName: string | undefined) => GraphQLScalarType | GraphQLEnumType | GraphQLObjectType;
|
|
15
|
+
scalarOutput: (f: ScalarField) => GraphQLOutputType;
|
|
16
|
+
};
|
|
17
|
+
export type BaseTypes = ReturnType<typeof buildBaseTypes>;
|
package/dist/schema/build.d.ts
CHANGED
|
@@ -1,43 +1,4 @@
|
|
|
1
|
-
import { GraphQLSchema
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { MutationResolver } from './mutations.js';
|
|
5
|
-
/** One event of a generated subscription. */
|
|
6
|
-
export interface ChangeEvent {
|
|
7
|
-
operation: "CREATE" | "UPDATE" | "DELETE" | "CONNECT" | "DISCONNECT";
|
|
8
|
-
key: unknown;
|
|
9
|
-
/** When the write was committed, ISO-8601. */
|
|
10
|
-
timestamp?: string | undefined;
|
|
11
|
-
/** For CONNECT / DISCONNECT: the relationship and its other end. */
|
|
12
|
-
relationship?: {
|
|
13
|
-
field: string;
|
|
14
|
-
type: string;
|
|
15
|
-
relatedType: string;
|
|
16
|
-
relatedKey: unknown;
|
|
17
|
-
} | undefined;
|
|
18
|
-
/** Stored properties before the write (`previousState: true`). */
|
|
19
|
-
previous?: Record<string, unknown> | undefined;
|
|
20
|
-
}
|
|
21
|
-
export interface SchemaHooks {
|
|
22
|
-
resolveAbstract: (abstract: AbstractType, info: GraphQLResolveInfo, context: unknown) => Promise<unknown>;
|
|
23
|
-
subscribe: (node: NodeType, args: Record<string, unknown>, context: unknown) => AsyncIterable<ChangeEvent>;
|
|
24
|
-
resolveChangedNode: (node: NodeType, event: ChangeEvent, info: GraphQLResolveInfo, context: unknown) => Promise<unknown>;
|
|
25
|
-
/**
|
|
26
|
-
* A `previousState` value as the subscriber may see it: throws when
|
|
27
|
-
* field-level @authentication refuses it, applies masks by the claims.
|
|
28
|
-
*/
|
|
29
|
-
previousValue: (node: NodeType, field: ScalarField, value: unknown, context: unknown) => unknown;
|
|
30
|
-
/** Implementations of custom scalars, by name (the scalars option). */
|
|
31
|
-
scalars?: Readonly<Record<string, GraphQLScalarType>> | undefined;
|
|
32
|
-
/** The resolver of a `@customResolver` field (from the resolvers option). */
|
|
33
|
-
customResolver: (node: NodeType, field: CustomField) => GraphQLFieldResolver<Record<string, unknown>, unknown>;
|
|
34
|
-
resolveSearch: (node: NodeType, index: SearchIndex, info: GraphQLResolveInfo, context: unknown, connection: boolean) => Promise<unknown>;
|
|
35
|
-
resolveRoot: RootResolver;
|
|
36
|
-
resolveNode: NodeResolver;
|
|
37
|
-
resolveCypher: CypherRootResolver;
|
|
38
|
-
resolveMutation: MutationResolver;
|
|
39
|
-
}
|
|
40
|
-
export type CypherRootResolver = (field: CypherField, info: GraphQLResolveInfo, context: unknown) => Promise<unknown>;
|
|
41
|
-
export type RootResolver = (kind: RootKind, node: NodeType, info: GraphQLResolveInfo, context: unknown) => Promise<unknown>;
|
|
42
|
-
export type NodeResolver = (id: string, info: GraphQLResolveInfo, context: unknown) => Promise<unknown>;
|
|
1
|
+
import { GraphQLSchema } from 'graphql';
|
|
2
|
+
import { GraphModel } from '../model/types.js';
|
|
3
|
+
import { SchemaHooks } from './hooks.js';
|
|
43
4
|
export declare function buildSchema(model: GraphModel, hooks: SchemaHooks): GraphQLSchema;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { GraphQLInputObjectType, GraphQLInputType } from 'graphql';
|
|
2
|
+
import { GraphModel, NodeType, RelationshipField, ScalarField } from '../model/types.js';
|
|
3
|
+
import { BaseTypes } from './base.js';
|
|
4
|
+
export declare function buildFilters(model: GraphModel, base: BaseTypes): {
|
|
5
|
+
inputTypeOf: (f: ScalarField) => GraphQLInputType;
|
|
6
|
+
whereOf: (typeName: string) => GraphQLInputObjectType;
|
|
7
|
+
polymorphic: (rel: RelationshipField) => boolean;
|
|
8
|
+
connectionWhere: (node: NodeType, rel: RelationshipField) => GraphQLInputObjectType;
|
|
9
|
+
};
|
|
10
|
+
export type Filters = ReturnType<typeof buildFilters>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { GraphQLFieldResolver, GraphQLResolveInfo, GraphQLScalarType } from 'graphql';
|
|
2
|
+
import { RootKind } from '../compile/read/types.js';
|
|
3
|
+
import { AbstractType, CustomField, CypherField, NodeType, ScalarField, SearchIndex } from '../model/types.js';
|
|
4
|
+
import { MutationResolver } from './mutations.js';
|
|
5
|
+
/** One event of a generated subscription. */
|
|
6
|
+
export interface ChangeEvent {
|
|
7
|
+
operation: "CREATE" | "UPDATE" | "DELETE" | "CONNECT" | "DISCONNECT";
|
|
8
|
+
key: unknown;
|
|
9
|
+
/** When the write was committed, ISO-8601. */
|
|
10
|
+
timestamp?: string | undefined;
|
|
11
|
+
/** For CONNECT / DISCONNECT: the relationship and its other end. */
|
|
12
|
+
relationship?: {
|
|
13
|
+
field: string;
|
|
14
|
+
type: string;
|
|
15
|
+
relatedType: string;
|
|
16
|
+
relatedKey: unknown;
|
|
17
|
+
} | undefined;
|
|
18
|
+
/** Stored properties before the write (`previousState: true`). */
|
|
19
|
+
previous?: Record<string, unknown> | undefined;
|
|
20
|
+
}
|
|
21
|
+
export interface SchemaHooks {
|
|
22
|
+
resolveAbstract: (abstract: AbstractType, info: GraphQLResolveInfo, context: unknown) => Promise<unknown>;
|
|
23
|
+
subscribe: (node: NodeType, args: Record<string, unknown>, context: unknown) => AsyncIterable<ChangeEvent>;
|
|
24
|
+
resolveChangedNode: (node: NodeType, event: ChangeEvent, info: GraphQLResolveInfo, context: unknown) => Promise<unknown>;
|
|
25
|
+
/**
|
|
26
|
+
* A `previousState` value as the subscriber may see it: throws when
|
|
27
|
+
* field-level @authentication refuses it, applies masks by the claims.
|
|
28
|
+
*/
|
|
29
|
+
previousValue: (node: NodeType, field: ScalarField, value: unknown, context: unknown) => unknown;
|
|
30
|
+
/** Implementations of custom scalars, by name (the scalars option). */
|
|
31
|
+
scalars?: Readonly<Record<string, GraphQLScalarType>> | undefined;
|
|
32
|
+
/** The resolver of a `@customResolver` field (from the resolvers option). */
|
|
33
|
+
customResolver: (node: NodeType, field: CustomField) => GraphQLFieldResolver<Record<string, unknown>, unknown>;
|
|
34
|
+
resolveSearch: (node: NodeType, index: SearchIndex, info: GraphQLResolveInfo, context: unknown, connection: boolean) => Promise<unknown>;
|
|
35
|
+
resolveRoot: RootResolver;
|
|
36
|
+
resolveNode: NodeResolver;
|
|
37
|
+
resolveCypher: CypherRootResolver;
|
|
38
|
+
resolveMutation: MutationResolver;
|
|
39
|
+
}
|
|
40
|
+
export type CypherRootResolver = (field: CypherField, info: GraphQLResolveInfo, context: unknown) => Promise<unknown>;
|
|
41
|
+
export type RootResolver = (kind: RootKind, node: NodeType, info: GraphQLResolveInfo, context: unknown) => Promise<unknown>;
|
|
42
|
+
export type NodeResolver = (id: string, info: GraphQLResolveInfo, context: unknown) => Promise<unknown>;
|