@loradb/lora-graphql 0.24.1 → 0.25.1
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 +201 -50
- 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-BZZNZs76.js +134 -0
- package/dist/diff-BZZNZs76.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/changes.d.ts +8 -0
- package/dist/execute/cypher-mutation.d.ts +5 -1
- 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/reported.d.ts +26 -0
- package/dist/execute/mutate/runner.d.ts +107 -25
- package/dist/execute/mutate/single-call.d.ts +92 -0
- package/dist/execute/mutate/triggers.d.ts +27 -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-RvZQdHNA.js +17411 -0
- package/dist/lora-graphql-RvZQdHNA.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 +16 -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 +55 -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/README.md
CHANGED
|
@@ -65,6 +65,7 @@ const yoga = createYoga({
|
|
|
65
65
|
- [The smart layer](#the-smart-layer)
|
|
66
66
|
- [Change tracking](#change-tracking)
|
|
67
67
|
- [Subscriptions](#subscriptions)
|
|
68
|
+
- [Triggers and workers](#triggers-and-workers)
|
|
68
69
|
- [Transactions](#transactions)
|
|
69
70
|
- [CLI](#cli)
|
|
70
71
|
- [Drivers, limits and errors](#drivers-limits-and-errors)
|
|
@@ -97,6 +98,13 @@ Relationship fields take `where`, `sort` and `limit`; list relationships
|
|
|
97
98
|
also get `…Connection`, whose edges carry the relationship properties and
|
|
98
99
|
filter on them (`where: { node, edge }`).
|
|
99
100
|
|
|
101
|
+
`@query(aggregate: true)` on a type also gives every relationship
|
|
102
|
+
connection that leads to it types of its own (`CityVenuesConnection` in
|
|
103
|
+
place of `VenueConnection`), since its `aggregate` covers that
|
|
104
|
+
relationship: turning it on renames types in the schema. A count does not
|
|
105
|
+
need it: `{ venuesConnection { totalCount } }` selects no page and reads
|
|
106
|
+
none.
|
|
107
|
+
|
|
100
108
|
The surface is restrictive: a field is filterable only with `@filterable`,
|
|
101
109
|
and only by the operators listed; sortable only with `@sortable`;
|
|
102
110
|
`printPublicSchema()` prints exactly what clients see, with no directives.
|
|
@@ -148,22 +156,26 @@ Model:
|
|
|
148
156
|
|
|
149
157
|
API:
|
|
150
158
|
|
|
151
|
-
| Directive | On
|
|
152
|
-
| ----------------------------------------------------- |
|
|
153
|
-
| `@query(read:, aggregate:)` | type, interface, union
|
|
154
|
-
| `@mutation(operations: [CREATE, UPDATE, DELETE])` | type
|
|
155
|
-
| `@subscription(operations: [CREATE, UPDATE, DELETE])` | type
|
|
156
|
-
| `@filterable(byValue: [...])` | field
|
|
157
|
-
| `@sortable` | field
|
|
158
|
-
| `@groupBy` | field
|
|
159
|
-
| `@limit(default:, max:)` | type, interface, union, list relationship
|
|
160
|
-
| `@
|
|
161
|
-
| `@
|
|
162
|
-
| `@
|
|
163
|
-
| `@
|
|
164
|
-
| `@
|
|
165
|
-
| `@
|
|
166
|
-
| `@
|
|
159
|
+
| Directive | On | Meaning |
|
|
160
|
+
| ----------------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
161
|
+
| `@query(read:, aggregate:)` | type, interface, union | Generated reads |
|
|
162
|
+
| `@mutation(operations: [CREATE, UPDATE, DELETE])` | type | Generated mutations; none without it |
|
|
163
|
+
| `@subscription(operations: [CREATE, UPDATE, DELETE])` | type | Generated subscriptions; none without it |
|
|
164
|
+
| `@filterable(byValue: [...])` | field | Filter operators. Bare: `EQ` and `IN` (lists: `INCLUDES`). On a relationship: enables relationship filters |
|
|
165
|
+
| `@sortable` | field | Sort and paginate by this field (on a relationship property: `sort: [{ edge: { ... } }]`) |
|
|
166
|
+
| `@groupBy` | field | A grouping key of `<plural>Grouped(by:)` (needs `@query(aggregate: true)`) |
|
|
167
|
+
| `@limit(default:, max:)` | type, interface, union, list relationship | Page size bounds |
|
|
168
|
+
| `@length(min:, max:)` | String / ID field | Its length in code points (each item of a list) |
|
|
169
|
+
| `@pattern(regex:)` | String / ID field | An expression the whole value must match; needs `@length(max:)` |
|
|
170
|
+
| `@format(as:)` | String / ID field | `email`, `email-strict`, `url`, `uuid`, `hostname`, `ipv4`, `ipv6`, `slug`, `hex-color`, `ulid`, `e164` |
|
|
171
|
+
| `@range(min:, max:, exclusiveMin:, exclusiveMax:)` | number field; argument of a `@cypher` field | Bounds of its value (each item of a list); on an argument, `min` and `max` |
|
|
172
|
+
| `@multipleOf(value:)` | Int / Float field | A whole multiple of `value` |
|
|
173
|
+
| `@size(min:, max:, unique:)` | list field; list argument of `@cypher`; list relationship | How many items it holds; on an argument, `max` (default `maxListArgument`); on a relationship, `max` items per nested `connect`, `create`, `disconnect` and `update` list |
|
|
174
|
+
| `@relayId` | `@key` field | Adds a global `id` and the `Node` interface |
|
|
175
|
+
| `@authentication(operations:, jwt:)` | type, field | Needs an authenticated request, whose claims satisfy `jwt` |
|
|
176
|
+
| `@authorization(filter:, validate:)` | type (filter and validate), field (validate) | Row-level rules, compiled into statements |
|
|
177
|
+
| `@jwt`, `@jwtClaim(path:)` | type, field | The claims shape; rules may only use declared claims |
|
|
178
|
+
| `@viewer(type:, field:)` | `@jwt` claim | The claim naming the caller's node (by a `@key` or `@unique` field): enables `isViewer` and `viewer` in rules |
|
|
167
179
|
|
|
168
180
|
`directiveTypeDefs` (or `lora-graphql directives`) prints these as SDL for
|
|
169
181
|
editors and codegen.
|
|
@@ -178,6 +190,30 @@ string), `Date`, `Time`, `LocalTime`, `DateTime`, `LocalDateTime`,
|
|
|
178
190
|
`Duration` (ISO-8601 strings), `Point` and `CartesianPoint` (objects; set with
|
|
179
191
|
`PointInput` / `CartesianPointInput`), and non-null lists of these.
|
|
180
192
|
|
|
193
|
+
The six rows from `@length` to `@size` are validation rules. On a field
|
|
194
|
+
they are checked wherever the field is set (create, update, upsert, nested
|
|
195
|
+
creates, relationship properties), in JavaScript before the transaction
|
|
196
|
+
opens, and a mutation that breaks any is `BAD_USER_INPUT` with every
|
|
197
|
+
violation in `extensions.violations` (`path`, `coordinate`, `rule`,
|
|
198
|
+
`params`, `message`; never the value). `null` and absent values pass; a
|
|
199
|
+
pattern must match the whole value; values computed during the write (an
|
|
200
|
+
`adjust`, a `@populatedBy` result) are checked in the transaction and fail
|
|
201
|
+
with `CONSTRAINT_VIOLATION`. Raw Cypher and `@cypher` mutations are not
|
|
202
|
+
checked. Each takes `message:`. `@normalize(trim:, case:, unicode:,
|
|
203
|
+
emptyToNull:)` changes a string before it is validated and stored (it is
|
|
204
|
+
what makes a `@unique` email treat `Ada@X.com ` and `ada@x.com` as one
|
|
205
|
+
value). The rules and `@normalize` may sit on a custom scalar (`scalar
|
|
206
|
+
Email @storedAs(type: STRING) @format(as: "email")`), for every field of
|
|
207
|
+
that type. For a rule across fields, the `validators` option takes a
|
|
208
|
+
Standard Schema validator per node type (zod, valibot, arktype, yup),
|
|
209
|
+
run on each input after the field rules. The same directives go on
|
|
210
|
+
`@cypher` arguments, `@normalize` included: it changes the argument
|
|
211
|
+
before its rules are checked and before the statement sees it. Each input field says its rules in its description,
|
|
212
|
+
`lora.inputSchemas()` (CLI: `json-schema`) exports them as JSON Schema
|
|
213
|
+
for clients, `diff` reports a tightened rule as breaking, and `check`
|
|
214
|
+
lists stored values that no longer pass. See the
|
|
215
|
+
[directive reference](https://loradb.com/docs/graphql/directives#validation).
|
|
216
|
+
|
|
181
217
|
## Queries
|
|
182
218
|
|
|
183
219
|
```graphql
|
|
@@ -514,7 +550,11 @@ entry), and a delete reaches at most `maxBatch` nodes through
|
|
|
514
550
|
`onDelete: CASCADE`. Relationships written (connects, each key of a
|
|
515
551
|
`disconnect` list, nested `update` entries) count up to ten times
|
|
516
552
|
`maxBatch`. Going over is `LIMIT_EXCEEDED`, before the writes that
|
|
517
|
-
would exceed it. `@
|
|
553
|
+
would exceed it. For a bound per field, `@size(max:)` on a list
|
|
554
|
+
relationship (`following: [Person!]! @relationship(...) @size(max: 50)`)
|
|
555
|
+
caps each nested `connect`, `create`, `disconnect` and `update` list of
|
|
556
|
+
one input; a longer list is `BAD_USER_INPUT` before the transaction
|
|
557
|
+
opens. `@key` is not updatable.
|
|
518
558
|
`info` reports `nodesCreated`, `nodesUpdated`, `nodesDeleted`,
|
|
519
559
|
`relationshipsCreated` and `relationshipsDeleted`.
|
|
520
560
|
|
|
@@ -636,6 +676,22 @@ inferred when the statement's last top-level `RETURN` has one item,
|
|
|
636
676
|
Fields returning `@node` types are projected with the selection like any
|
|
637
677
|
other node, and read filters apply to them.
|
|
638
678
|
|
|
679
|
+
A `Mutation` statement says what it wrote in two more columns, read as a
|
|
680
|
+
trigger statement's are: the nodes it returns as `created` and `updated`
|
|
681
|
+
are its write-set, so its change is not `broad` and subscriptions on
|
|
682
|
+
their types fire. Without either column the change is `broad`, and
|
|
683
|
+
`check()` lints the mutation.
|
|
684
|
+
|
|
685
|
+
```graphql
|
|
686
|
+
type Mutation {
|
|
687
|
+
addGenre(key: String!, name: String!): Genre
|
|
688
|
+
@cypher(
|
|
689
|
+
statement: "CREATE (g:Genre {key: $key, name: $name}) RETURN g, g AS created"
|
|
690
|
+
columnName: "g"
|
|
691
|
+
)
|
|
692
|
+
}
|
|
693
|
+
```
|
|
694
|
+
|
|
639
695
|
Statements are checked, not trusted:
|
|
640
696
|
|
|
641
697
|
- at startup: every `$parameter` must be an argument, `$jwt` or
|
|
@@ -717,7 +773,10 @@ createPost(key: String!, hashtags: [String!] = [] @size(max: 30)): Post
|
|
|
717
773
|
Int and Float arguments take bounds the same way: `@range(min:, max:)`
|
|
718
774
|
(either or both, inclusive) checks the value, or each item of a list,
|
|
719
775
|
before the statement runs, and a default outside the bounds is a model
|
|
720
|
-
error. Null is not checked; that is the type's business.
|
|
776
|
+
error. Null is not checked; that is the type's business. A String or ID
|
|
777
|
+
argument takes `@normalize` (`text: String! @normalize(trim: true)
|
|
778
|
+
@length(min: 1, max: 280)`): the trimmed value is what `@length` counts
|
|
779
|
+
and what the statement gets.
|
|
721
780
|
|
|
722
781
|
```graphql
|
|
723
782
|
nearby(lat: Float! @range(min: -90, max: 90), km: Int = 10 @range(min: 1, max: 500)): [Festival!]!
|
|
@@ -1245,8 +1304,8 @@ reported as an API break with no data migration.
|
|
|
1245
1304
|
```ts
|
|
1246
1305
|
lora.onWrite((change) => {
|
|
1247
1306
|
// Per type: lists, counts and connections of these types may have
|
|
1248
|
-
// changed, not only the entities. A broad change (a @cypher mutation
|
|
1249
|
-
//
|
|
1307
|
+
// changed, not only the entities. A broad change (a @cypher mutation
|
|
1308
|
+
// that does not say what it wrote) names nothing: every type goes.
|
|
1250
1309
|
const types = change.broad ? allNodeTypes : change.types;
|
|
1251
1310
|
cache.invalidate(types.map((typename) => ({ typename })));
|
|
1252
1311
|
});
|
|
@@ -1260,7 +1319,8 @@ relationships `connected` and `disconnected` (by field and both keys),
|
|
|
1260
1319
|
included), and the touched `types` and `relationshipTypes`. Every compiled
|
|
1261
1320
|
read carries a read-set (labels and relationship types);
|
|
1262
1321
|
`lora.affects(reads, change)` tells whether a cached read may be stale.
|
|
1263
|
-
`@cypher`
|
|
1322
|
+
A `@cypher` mutation's write-set is the nodes its statement returns as
|
|
1323
|
+
`created` and `updated`; one that returns neither is reported with
|
|
1264
1324
|
`broad: true`. Only writes made through the library are seen. A consumer
|
|
1265
1325
|
that falls `maxQueuedChanges` (default 1000) behind is ended with an error
|
|
1266
1326
|
rather than buffering without bound.
|
|
@@ -1369,6 +1429,91 @@ replay earlier writes. `onWrite` still reports
|
|
|
1369
1429
|
this instance's mutations, and `previousState` needs them: the feed carries
|
|
1370
1430
|
the state after the write. Call `lora.close()` to stop the feed.
|
|
1371
1431
|
|
|
1432
|
+
## Triggers and workers
|
|
1433
|
+
|
|
1434
|
+
Three things react to a write, at three moments:
|
|
1435
|
+
|
|
1436
|
+
- a **trigger** runs inside the mutation's transaction, before the
|
|
1437
|
+
commit: derived data that must never be out of step, or a refusal;
|
|
1438
|
+
- an **event** (`onWrite`, `changes()`, a subscription) reports what
|
|
1439
|
+
committed;
|
|
1440
|
+
- a **worker** delivers what a transaction cannot hold (a push, an
|
|
1441
|
+
e-mail, a webhook) after the commit, with retries.
|
|
1442
|
+
|
|
1443
|
+
```graphql
|
|
1444
|
+
type Comment
|
|
1445
|
+
@node
|
|
1446
|
+
@mutation
|
|
1447
|
+
@authentication
|
|
1448
|
+
@trigger(
|
|
1449
|
+
on: [CREATE]
|
|
1450
|
+
statement: """
|
|
1451
|
+
MATCH (this)-[:ON]->(post:Post)<-[:WROTE]-(author:Person)
|
|
1452
|
+
CREATE (author)<-[:FOR]-(n:Notification {
|
|
1453
|
+
key: 'comment:' + this.key, text: 'New comment on ' + post.title
|
|
1454
|
+
})
|
|
1455
|
+
RETURN n AS created
|
|
1456
|
+
"""
|
|
1457
|
+
) {
|
|
1458
|
+
key: String! @key
|
|
1459
|
+
text: String!
|
|
1460
|
+
post: Post! @relationship(type: "ON", direction: OUT)
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
type Notification @node @outbox @subscription(operations: [CREATE]) {
|
|
1464
|
+
key: String! @key
|
|
1465
|
+
text: String!
|
|
1466
|
+
}
|
|
1467
|
+
```
|
|
1468
|
+
|
|
1469
|
+
```ts
|
|
1470
|
+
const lora = new LoraGraphQL({
|
|
1471
|
+
typeDefs,
|
|
1472
|
+
driver: loraDriver(db),
|
|
1473
|
+
// What Cypher cannot say; throwing rolls the mutation back.
|
|
1474
|
+
triggers: {
|
|
1475
|
+
Comment: {
|
|
1476
|
+
create: async ({ keys, transaction }) => {
|
|
1477
|
+
/* transaction.execute(cypher, params) */
|
|
1478
|
+
},
|
|
1479
|
+
},
|
|
1480
|
+
},
|
|
1481
|
+
});
|
|
1482
|
+
|
|
1483
|
+
// After the commit, at least once, with retries: make it idempotent.
|
|
1484
|
+
lora.worker("Notification", {
|
|
1485
|
+
handle: async (notification, { key, attempt, signal }) => {
|
|
1486
|
+
await push.send(key, notification.text, { signal });
|
|
1487
|
+
},
|
|
1488
|
+
});
|
|
1489
|
+
```
|
|
1490
|
+
|
|
1491
|
+
- A `@trigger` statement runs once per mutation with `this` bound to
|
|
1492
|
+
every node of the type the mutation wrote; `$jwt`, `$viewer` and
|
|
1493
|
+
`$operation` are bound. `fields:` narrows an `UPDATE` trigger to
|
|
1494
|
+
updates that named one of them.
|
|
1495
|
+
- `@trigger(on: [CONNECT], field: "following", statement: ...)` runs
|
|
1496
|
+
once per relationship the mutation created through that field, from
|
|
1497
|
+
either side, with `this` and `target` bound: "tell a person who
|
|
1498
|
+
followed them" without reading any other relationship. `DISCONNECT`
|
|
1499
|
+
does the same for a relationship removed. A trigger function reads
|
|
1500
|
+
them in `change.connected` and `change.disconnected`.
|
|
1501
|
+
- Nodes a statement returns as `created` or `updated` join the
|
|
1502
|
+
mutation's write-set, so `onWrite` and subscriptions see them without
|
|
1503
|
+
`changeFeed`.
|
|
1504
|
+
- Triggers run only for a write every rule has passed, and what a
|
|
1505
|
+
trigger writes fires no trigger.
|
|
1506
|
+
- **Only generated mutations fire triggers.** Raw Cypher and `@cypher`
|
|
1507
|
+
mutations fire none.
|
|
1508
|
+
- A trigger function holds the writer lock, bounded by
|
|
1509
|
+
`triggerTimeoutMs` (default 1000). Network calls belong in a worker.
|
|
1510
|
+
- `@outbox` makes each node of a type work for `lora.worker()`. The
|
|
1511
|
+
delivery state lives on the node, outside the API, so the work is as
|
|
1512
|
+
durable as the data and is found again after a restart.
|
|
1513
|
+
|
|
1514
|
+
The full guide, with a tested example and every option, is on the
|
|
1515
|
+
[site](https://loradb.com/docs/graphql/triggers).
|
|
1516
|
+
|
|
1372
1517
|
## Transactions
|
|
1373
1518
|
|
|
1374
1519
|
```ts
|
|
@@ -1608,37 +1753,43 @@ transaction. Mutations need interactive transactions, which only the Node
|
|
|
1608
1753
|
binding has, so the WASM binding serves reads.
|
|
1609
1754
|
`explain()`, and so plan checks, need the Node binding too.
|
|
1610
1755
|
|
|
1611
|
-
| Option | Default | Meaning
|
|
1612
|
-
| ---------------------------- | ------------- |
|
|
1613
|
-
| `timeoutMs` | 10 000 | Per statement and lock wait; a `signal` cancels
|
|
1614
|
-
| `operationTimeoutMs` | 2 × timeout | All root fields of one query; then `TIMEOUT`
|
|
1615
|
-
| `maxConcurrentStatements` | 2 | Statements one operation runs at once
|
|
1616
|
-
| `maxCost` | 50 000 | Estimated rows per operation
|
|
1617
|
-
| `maxBatch` | 1000 | Nodes written per mutation; bulk `limit` by default |
|
|
1618
|
-
| `compileCacheBytes` | 64 MiB | Compile cache and document cache, each
|
|
1619
|
-
| `maxQueuedChanges` | 1000 | How far a change consumer may fall behind
|
|
1620
|
-
| `maxSubscriptions` | 100 | Live subscriptions per `subscriptionScope`
|
|
1621
|
-
| `
|
|
1622
|
-
| `
|
|
1623
|
-
| `
|
|
1624
|
-
| `
|
|
1625
|
-
| `
|
|
1626
|
-
| `
|
|
1627
|
-
| `
|
|
1628
|
-
| `
|
|
1629
|
-
| `
|
|
1630
|
-
| `
|
|
1631
|
-
| `
|
|
1632
|
-
| `
|
|
1633
|
-
| `
|
|
1634
|
-
| `
|
|
1635
|
-
| `
|
|
1636
|
-
| `
|
|
1637
|
-
| `
|
|
1638
|
-
| `
|
|
1756
|
+
| Option | Default | Meaning |
|
|
1757
|
+
| ---------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1758
|
+
| `timeoutMs` | 10 000 | Per statement and lock wait; a `signal` cancels |
|
|
1759
|
+
| `operationTimeoutMs` | 2 × timeout | All root fields of one query; then `TIMEOUT` |
|
|
1760
|
+
| `maxConcurrentStatements` | 2 | Statements one operation runs at once |
|
|
1761
|
+
| `maxCost` | 50 000 | Estimated rows per operation |
|
|
1762
|
+
| `maxBatch` | 1000 | Nodes written per mutation, and ten times as many relationships; bulk `limit` by default. A nested `connect` list is bounded per field by `@size(max:)` on the relationship |
|
|
1763
|
+
| `compileCacheBytes` | 64 MiB | Compile cache and document cache, each |
|
|
1764
|
+
| `maxQueuedChanges` | 1000 | How far a change consumer may fall behind |
|
|
1765
|
+
| `maxSubscriptions` | 100 | Live subscriptions per `subscriptionScope` |
|
|
1766
|
+
| `maxQueuedWrites` | 512 | Writes waiting for the writer lock; more is `OVERLOADED` |
|
|
1767
|
+
| `maxTotalCount` | none | Where a root connection's `totalCount` stops counting |
|
|
1768
|
+
| `triggerTimeoutMs` | 1000 | Per trigger function; past it the mutation fails |
|
|
1769
|
+
| `singleCallMutations` | true | A create is one call that begins, runs, commits |
|
|
1770
|
+
| `subscriptionScope` | the context | What `maxSubscriptions` counts per |
|
|
1771
|
+
| `maxSubscriptionFilterDepth` | 1 | Relationship filter nesting in a subscription where |
|
|
1772
|
+
| `subscriptionTimeoutMs` | 2000 | Per statement checking a change for a subscriber |
|
|
1773
|
+
| `defaultLimit` / `maxLimit` | 25 / 100 | Global page sizes; `@limit` may only lower `max` |
|
|
1774
|
+
| `callbacks` | | Named callbacks for `@populatedBy` |
|
|
1775
|
+
| `jwt` | `context.jwt` | Where the claims are |
|
|
1776
|
+
| `onStatement` | | Observe every statement |
|
|
1777
|
+
| `cursorSecret` | | Sign cursors; reject unsigned or forged ones |
|
|
1778
|
+
| `maskErrors` | production | Clients get `DATABASE_ERROR` and an `id` only |
|
|
1779
|
+
| `onError` | | Receives each database error's detail and `id` |
|
|
1780
|
+
| `guards` | see below | Document limits; `false` turns them off |
|
|
1781
|
+
| `persistedOnly` | false | `execute()`, `subscribe()` run persisted ops only |
|
|
1782
|
+
| `budget` | | Cost limit per request, from the context |
|
|
1783
|
+
| `onCost` | | Each root field's estimate, total and limit |
|
|
1784
|
+
| `onStatementEnd` | | Duration, rows and error of every statement call |
|
|
1785
|
+
| `timing` | false | `extensions.timing` in `execute()` results |
|
|
1786
|
+
| `tracer` / `traceStatements` | | OpenTelemetry-style spans; Cypher text on request |
|
|
1787
|
+
| `metrics` | | Counters and histograms (see below) |
|
|
1639
1788
|
|
|
1640
1789
|
Errors carry `extensions.code`: `BAD_USER_INPUT`, `INVALID_CURSOR`,
|
|
1641
|
-
`LIMIT_EXCEEDED`, `COST_EXCEEDED`, `
|
|
1790
|
+
`LIMIT_EXCEEDED`, `COST_EXCEEDED`, `OVERLOADED` (more than
|
|
1791
|
+
`maxQueuedWrites` mutations are waiting for the database: answer 503 and
|
|
1792
|
+
let the client back off), `TIMEOUT` (with `operationTimeoutMs`),
|
|
1642
1793
|
`UNAUTHENTICATED`, `FORBIDDEN`,
|
|
1643
1794
|
`NOT_FOUND`, `CONSTRAINT_VIOLATION` (with `type` and `field`),
|
|
1644
1795
|
`DATABASE_ERROR` (with an `id`, also given to `onError`),
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { GraphModel } from '../model/types.js';
|
|
2
|
+
export type JsonSchema = Record<string, unknown>;
|
|
3
|
+
/**
|
|
4
|
+
* A JSON Schema per generated create and update input, by input type
|
|
5
|
+
* name (`PersonCreateInput`), describing the scalar fields a client may
|
|
6
|
+
* set and the rules on them. A pattern is anchored, since here it must
|
|
7
|
+
* match the whole value; a format is given both by name and, where it is
|
|
8
|
+
* an expression, as a pattern, since validators treat `format` as a
|
|
9
|
+
* remark by default.
|
|
10
|
+
*/
|
|
11
|
+
export declare function inputSchemas(model: GraphModel): Record<string, JsonSchema>;
|
package/dist/analyze/plans.d.ts
CHANGED
|
@@ -35,3 +35,11 @@ export declare function checkPlans(driver: LoraDriver, compiled: CompiledRead, o
|
|
|
35
35
|
* that start from keys (every mutation statement) must have none.
|
|
36
36
|
*/
|
|
37
37
|
export declare function scanExpands(statement: Statement, plan: QueryPlan): PlanFinding[];
|
|
38
|
+
/**
|
|
39
|
+
* Plan the statements a mutation ran. Every one starts from keys, so none
|
|
40
|
+
* may expand from a full scan (`scanExpands`); the engine's row estimate
|
|
41
|
+
* is held to `rowBudget` when one is given.
|
|
42
|
+
*/
|
|
43
|
+
export declare function checkStatements(driver: LoraDriver, statements: readonly Statement[], options?: {
|
|
44
|
+
rowBudget?: number;
|
|
45
|
+
}): Promise<PlanReport[]>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { LoraDriver } from '../driver.js';
|
|
2
|
+
import { GraphModel } from '../model/types.js';
|
|
3
|
+
/** A stored value that breaks a rule of its field. */
|
|
4
|
+
export interface StoredViolation {
|
|
5
|
+
type: string;
|
|
6
|
+
/** The node's `@key`; for a relationship property, the declaring node's. */
|
|
7
|
+
key: unknown;
|
|
8
|
+
field: string;
|
|
9
|
+
/**
|
|
10
|
+
* The rule, as in a request's `violations`; `normalize` for a value not
|
|
11
|
+
* in its stored form; `fulltext` for one a full-text index skips.
|
|
12
|
+
*/
|
|
13
|
+
rule: string;
|
|
14
|
+
message: string;
|
|
15
|
+
}
|
|
16
|
+
export interface StoredReport {
|
|
17
|
+
violations: StoredViolation[];
|
|
18
|
+
/**
|
|
19
|
+
* Nodes read per type, and relationships per `Owner.field` whose
|
|
20
|
+
* properties have rules; one with more than `limit` is read in part.
|
|
21
|
+
*/
|
|
22
|
+
scanned: Record<string, number>;
|
|
23
|
+
/** Types with more nodes than were read. */
|
|
24
|
+
partial: string[];
|
|
25
|
+
}
|
|
26
|
+
export declare function scanStored(driver: LoraDriver, model: GraphModel, options?: {
|
|
27
|
+
limit?: number;
|
|
28
|
+
timeoutMs?: number;
|
|
29
|
+
}): Promise<StoredReport>;
|