@loradb/lora-graphql 0.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/LICENSE +87 -0
  2. package/README.md +994 -0
  3. package/dist/analyze/cypher-check.d.ts +8 -0
  4. package/dist/analyze/diff.d.ts +32 -0
  5. package/dist/analyze/indexes.d.ts +61 -0
  6. package/dist/analyze/lint.d.ts +6 -0
  7. package/dist/analyze/plans.d.ts +31 -0
  8. package/dist/analyze/statistics.d.ts +19 -0
  9. package/dist/cli.d.ts +6 -0
  10. package/dist/cli.js +631 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/codegen.d.ts +32 -0
  13. package/dist/compile/aggregate.d.ts +17 -0
  14. package/dist/compile/auth.d.ts +46 -0
  15. package/dist/compile/context.d.ts +43 -0
  16. package/dist/compile/cursor.d.ts +4 -0
  17. package/dist/compile/cypher.d.ts +175 -0
  18. package/dist/compile/filter.d.ts +19 -0
  19. package/dist/compile/hmac.d.ts +4 -0
  20. package/dist/compile/read.d.ts +89 -0
  21. package/dist/compile/selection.d.ts +18 -0
  22. package/dist/diff-BVP1Jzh3.js +99 -0
  23. package/dist/diff-BVP1Jzh3.js.map +1 -0
  24. package/dist/driver-C8vA5fV-.js +9127 -0
  25. package/dist/driver-C8vA5fV-.js.map +1 -0
  26. package/dist/driver.d.ts +117 -0
  27. package/dist/errors.d.ts +20 -0
  28. package/dist/execute/changes.d.ts +61 -0
  29. package/dist/execute/cypher-mutation.d.ts +4 -0
  30. package/dist/execute/feed.d.ts +10 -0
  31. package/dist/execute/mutate.d.ts +57 -0
  32. package/dist/execute/transaction.d.ts +17 -0
  33. package/dist/guards.d.ts +42 -0
  34. package/dist/index.d.ts +21 -0
  35. package/dist/index.js +24 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/lora-graphql.d.ts +291 -0
  38. package/dist/migrate.d.ts +8 -0
  39. package/dist/model/build.d.ts +25 -0
  40. package/dist/model/cypher-lexer.d.ts +10 -0
  41. package/dist/model/directives.d.ts +3 -0
  42. package/dist/model/points.d.ts +12 -0
  43. package/dist/model/relations.d.ts +7 -0
  44. package/dist/model/types.d.ts +311 -0
  45. package/dist/observe.d.ts +72 -0
  46. package/dist/schema/build.d.ts +38 -0
  47. package/dist/schema/global-id.d.ts +6 -0
  48. package/dist/schema/guard.d.ts +5 -0
  49. package/dist/schema/mutations.d.ts +44 -0
  50. package/dist/schema/names.d.ts +37 -0
  51. package/dist/schema/scalars.d.ts +9 -0
  52. package/dist/testing.d.ts +36 -0
  53. package/dist/testing.js +56 -0
  54. package/dist/testing.js.map +1 -0
  55. package/package.json +85 -0
@@ -0,0 +1,8 @@
1
+ import { DocumentNode } from 'graphql';
2
+ export interface MigrationResult {
3
+ /** The rewritten SDL. */
4
+ typeDefs: string;
5
+ /** What has no equivalent or needs a decision, by type and field. */
6
+ todos: string[];
7
+ }
8
+ export declare function migrateNeo4j(sdl: string, operations?: DocumentNode[]): MigrationResult;
@@ -0,0 +1,25 @@
1
+ import { DocumentNode, GraphQLNamedType } from 'graphql';
2
+ import { GraphModel, ScalarType } from './types.js';
3
+ export interface ModelOptions {
4
+ /** Page size used when a list or connection gets no `limit` / `first`. */
5
+ defaultLimit?: number;
6
+ /** Hard cap on any page size; `@limit(max:)` may only lower it. */
7
+ maxLimit?: number;
8
+ /**
9
+ * Sign cursors with HMAC-SHA-256 under this secret, and reject cursors
10
+ * whose signature does not match. Without it cursors are only tagged
11
+ * with their sort. Changing the secret invalidates every cursor.
12
+ */
13
+ cursorSecret?: string;
14
+ }
15
+ export declare const DEFAULT_LIMIT = 25;
16
+ export declare const MAX_LIMIT = 100;
17
+ /**
18
+ * Parse and validate annotated type definitions into a `GraphModel`.
19
+ * Throws a `ModelError` listing every problem found.
20
+ */
21
+ export declare function buildModel(typeDefs: string | DocumentNode, options?: ModelOptions): GraphModel;
22
+ /** A custom scalar's storage type, from its `@storedAs(type:)`. */
23
+ export declare function storageOf(t: GraphQLNamedType): ScalarType | undefined;
24
+ export declare function lowerFirst(s: string): string;
25
+ export declare function defaultPlural(typeName: string): string;
@@ -0,0 +1,10 @@
1
+ export interface ParamRef {
2
+ name: string;
3
+ start: number;
4
+ end: number;
5
+ }
6
+ export declare function scanParams(text: string): ParamRef[];
7
+ /** Replace every `$name` with `$<rename(name)>`. */
8
+ export declare function renameParams(text: string, rename: (name: string) => string): string;
9
+ /** Code outside strings and comments, uppercased, for construct checks. */
10
+ export declare function codeOnly(text: string): string;
@@ -0,0 +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(generate: Boolean = false) on FIELD_DEFINITION\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 \"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 enum NestedOperation {\n CREATE\n CONNECT\n DISCONNECT\n UPDATE\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; never client-settable.\"\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 \"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 \"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; never client-settable.\"\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, and $jwt holds the request's claims.\"\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 OBJECT | FIELD_DEFINITION\n\n input AuthorizationFilterRule {\n operations: [AuthOperation!]! = [READ, UPDATE, DELETE]\n requireAuthentication: Boolean = true\n where: AuthorizationWhere!\n }\n\n input AuthorizationValidateRule {\n operations: [AuthOperation!]! = [READ, CREATE, UPDATE, DELETE]\n when: [AuthorizationWhen!]! = [BEFORE, AFTER]\n requireAuthentication: Boolean = true\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 }\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 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
+ /** Names defined by the prelude; never treated as user types. */
3
+ export declare const PRELUDE_TYPES: Set<string>;
@@ -0,0 +1,12 @@
1
+ export declare function toLoraPoint(input: unknown): unknown;
2
+ /**
3
+ * A stored value as the engine takes it: points converted, lists mapped,
4
+ * `@vector` fields tagged as VECTORs (a vector index skips plain lists).
5
+ */
6
+ export declare function toStored(field: {
7
+ name: string;
8
+ type: string;
9
+ vector?: {
10
+ dimensions: number;
11
+ } | undefined;
12
+ }, value: unknown): unknown;
@@ -0,0 +1,7 @@
1
+ import { AbstractType, GraphModel, RelationshipField } from './types.js';
2
+ export declare function isPolymorphic(model: GraphModel, rel: RelationshipField): boolean;
3
+ /** The field itself, or one concrete copy per member of its target. */
4
+ export declare function memberFields(model: GraphModel, rel: RelationshipField): RelationshipField[];
5
+ /** The declared field a member copy came from (or the field itself). */
6
+ export declare function declared(rel: RelationshipField): RelationshipField;
7
+ export declare function abstractOf(model: GraphModel, rel: RelationshipField): AbstractType | undefined;
@@ -0,0 +1,311 @@
1
+ export type ScalarType = "String" | "ID" | "Int" | "Float" | "Boolean" | "BigInt" | "Date" | "Time" | "LocalTime" | "DateTime" | "LocalDateTime" | "Duration" | "Point" | "CartesianPoint"
2
+ /** A user-declared enum, stored as its value name. */
3
+ | "Enum";
4
+ export type FilterOperator = "EQ" | "IN" | "LT" | "LTE" | "GT" | "GTE" | "CONTAINS" | "STARTS_WITH" | "ENDS_WITH" | "WITHIN_BBOX" | "DISTANCE" | "INCLUDES" | "IS_NULL" | "CASE_INSENSITIVE";
5
+ export type IndexKind = "RANGE" | "TEXT" | "POINT";
6
+ export type MutationOperation = "CREATE" | "UPDATE" | "DELETE";
7
+ export type AuthOperation = "READ" | MutationOperation | "CREATE_RELATIONSHIP" | "DELETE_RELATIONSHIP" | "SUBSCRIBE";
8
+ /** Field-level options shared by every field kind. */
9
+ interface FieldBase {
10
+ /** Operations on which reading this field needs an authenticated request. */
11
+ authentication: ReadonlySet<AuthOperation> | undefined;
12
+ /** Claims `@authentication(jwt:)` requires, when set. */
13
+ authenticationJwt?: AuthorizationWhere | undefined;
14
+ /** Field-level `@authorization` (validate rules only). */
15
+ authorization?: Authorization | undefined;
16
+ }
17
+ export interface ScalarField extends FieldBase {
18
+ kind: "scalar";
19
+ /** API field name. */
20
+ name: string;
21
+ /** Stored property name (differs from `name` under `@alias`). */
22
+ property: string;
23
+ type: ScalarType;
24
+ /** Enum type name when `type` is `Enum`. */
25
+ enumName: string | undefined;
26
+ /** The custom scalar clients see, when the field is one (`@storedAs`). */
27
+ customScalar?: string | undefined;
28
+ list: boolean;
29
+ required: boolean;
30
+ key: boolean;
31
+ unique: boolean;
32
+ private: boolean;
33
+ relayId: boolean;
34
+ filters: ReadonlySet<FilterOperator>;
35
+ sortable: boolean;
36
+ /** `@groupBy`: a grouping key of `<plural>Grouped`. */
37
+ groupBy: boolean;
38
+ /** Indexes requested explicitly with `@index`. */
39
+ indexes: readonly IndexKind[];
40
+ /** `@key(generate: true)`: creates fill a UUID when the input omits it. */
41
+ generate: boolean;
42
+ /** `@default(value:)`, stored on create when the input omits the field. */
43
+ defaultValue: {
44
+ value: unknown;
45
+ } | undefined;
46
+ /** `@timestamp`: set to the current time by these operations. */
47
+ timestamp: ReadonlySet<"CREATE" | "UPDATE"> | undefined;
48
+ /** Never client-settable (`@readonly`, `@timestamp`, `@private`). */
49
+ readonly: boolean;
50
+ /** `@settable`: which mutations may set it (both false when readonly). */
51
+ settableOn: {
52
+ create: boolean;
53
+ update: boolean;
54
+ };
55
+ /** `@selectable`: readable (false: write-only), and aggregatable. */
56
+ selectableOn: {
57
+ read: boolean;
58
+ aggregate: boolean;
59
+ };
60
+ /** `@populatedBy`: a named callback computes it on these operations. */
61
+ populatedBy: {
62
+ callback: string;
63
+ operations: ReadonlySet<"CREATE" | "UPDATE">;
64
+ } | undefined;
65
+ /**
66
+ * Set on the stand-in for a `@filterable` / `@sortable` `@cypher` field:
67
+ * its value is the statement's, computed per row, not a property.
68
+ */
69
+ computedBy?: CypherField;
70
+ /** `@vector`: stored as a VECTOR, searchable by similarity. */
71
+ vector: {
72
+ dimensions: number;
73
+ similarity: "COSINE" | "EUCLIDEAN";
74
+ } | undefined;
75
+ description: string | undefined;
76
+ }
77
+ export type NestedOperation = "CREATE" | "CONNECT" | "DISCONNECT" | "UPDATE" | "DELETE";
78
+ export interface RelationshipField extends FieldBase {
79
+ kind: "relationship";
80
+ name: string;
81
+ /** Node type that declares the field. */
82
+ owner: string;
83
+ /** Relationship type, e.g. `IN_GENRE`. */
84
+ type: string;
85
+ direction: "IN" | "OUT";
86
+ /** Target node type name. */
87
+ target: string;
88
+ list: boolean;
89
+ required: boolean;
90
+ /** `@relationshipProperties` type name. */
91
+ properties: string | undefined;
92
+ /**
93
+ * The concrete @node types the field reaches: the target itself, or an
94
+ * interface's implementations / a union's members.
95
+ */
96
+ members: readonly string[];
97
+ /** For a member copy of a polymorphic field: the field it came from. */
98
+ via?: RelationshipField | undefined;
99
+ filterable: boolean;
100
+ /** UNDIRECTED: reads follow the relationship both ways. */
101
+ queryDirection: "DIRECTED" | "UNDIRECTED";
102
+ /** What deleting the owner does to nodes reached through the field. */
103
+ onDelete: "DETACH" | "CASCADE" | "RESTRICT";
104
+ /** Nested writes mutation inputs offer for the field. */
105
+ nestedOperations: ReadonlySet<NestedOperation>;
106
+ /** Whether the field's connection and filters offer aggregates. */
107
+ aggregate: boolean;
108
+ cardinality: number | undefined;
109
+ limit: PageLimit | undefined;
110
+ description: string | undefined;
111
+ }
112
+ /** How a `@cypher` field's value, or one of its arguments, is typed. */
113
+ export interface TypeShape {
114
+ /** Named type: a scalar, enum or (for results) node type name. */
115
+ named: string;
116
+ list: boolean;
117
+ required: boolean;
118
+ itemRequired: boolean;
119
+ }
120
+ export interface CypherArgument {
121
+ name: string;
122
+ type: TypeShape;
123
+ defaultValue: unknown;
124
+ description: string | undefined;
125
+ }
126
+ export interface CypherField extends FieldBase {
127
+ kind: "cypher";
128
+ name: string;
129
+ /** Node type name, or `Query` / `Mutation` for root fields. */
130
+ owner: string;
131
+ statement: string;
132
+ /** The result column holding the field's value. */
133
+ columnName: string;
134
+ /** The field's type; `node` names a @node type when it returns nodes. */
135
+ type: TypeShape;
136
+ node: string | undefined;
137
+ /** An interface or union over @node types, when it returns those. */
138
+ abstract: string | undefined;
139
+ /** A plain object type (no @node), when it returns maps. */
140
+ object: string | undefined;
141
+ args: readonly CypherArgument[];
142
+ /** `$parameters` the statement uses, in order of first use. */
143
+ params: readonly string[];
144
+ /**
145
+ * `@filterable` / `@sortable`: a scalar stand-in for filters and sorts,
146
+ * whose value the statement computes per row (never through an index).
147
+ */
148
+ computed: ScalarField | undefined;
149
+ description: string | undefined;
150
+ }
151
+ /**
152
+ * A field computed in JavaScript (`@customResolver`): never stored or
153
+ * filtered; `requires` is fetched with the node so the resolver can use it.
154
+ */
155
+ export interface CustomField extends FieldBase {
156
+ kind: "custom";
157
+ name: string;
158
+ owner: string;
159
+ /** Selection on the owner type, e.g. `name genre { name }`. */
160
+ requires: string | undefined;
161
+ type: TypeShape;
162
+ description: string | undefined;
163
+ }
164
+ export type Field = ScalarField | RelationshipField | CypherField | CustomField;
165
+ /** `{ node, jwt, AND, OR, NOT }`, as written in `@authorization`. */
166
+ export type AuthorizationWhere = Record<string, unknown>;
167
+ export interface AuthorizationFilterRule {
168
+ operations: ReadonlySet<AuthOperation>;
169
+ requireAuthentication: boolean;
170
+ where: AuthorizationWhere;
171
+ }
172
+ export interface AuthorizationValidateRule {
173
+ operations: ReadonlySet<AuthOperation>;
174
+ when: ReadonlySet<"BEFORE" | "AFTER">;
175
+ requireAuthentication: boolean;
176
+ where: AuthorizationWhere;
177
+ }
178
+ export interface Authorization {
179
+ filter: readonly AuthorizationFilterRule[];
180
+ validate: readonly AuthorizationValidateRule[];
181
+ }
182
+ export interface PageLimit {
183
+ default: number;
184
+ max: number;
185
+ }
186
+ export interface NodeType {
187
+ name: string;
188
+ /** First label is the primary label every MATCH uses. */
189
+ labels: readonly string[];
190
+ /** Plural used for root fields, e.g. `festivals`. */
191
+ plural: string;
192
+ fields: ReadonlyMap<string, Field>;
193
+ key: ScalarField;
194
+ read: boolean;
195
+ aggregate: boolean;
196
+ /** Generated mutations; empty unless `@mutation`. */
197
+ mutations: ReadonlySet<MutationOperation>;
198
+ /** Change events to subscribe to; empty unless `@subscription`. */
199
+ subscriptions: ReadonlySet<MutationOperation>;
200
+ /** `@subscription(relationships:, previousState:)`. */
201
+ subscriptionOptions: {
202
+ relationships: boolean;
203
+ previousState: boolean;
204
+ };
205
+ limit: PageLimit;
206
+ /** Operations that need an authenticated request. */
207
+ authentication: ReadonlySet<AuthOperation> | undefined;
208
+ /** Claims `@authentication(jwt:)` requires, when set. */
209
+ authenticationJwt: AuthorizationWhere | undefined;
210
+ authorization: Authorization | undefined;
211
+ /** Full-text and vector indexes with their search root fields. */
212
+ search: readonly SearchIndex[];
213
+ /** Interfaces the type implements. */
214
+ interfaces: readonly string[];
215
+ description: string | undefined;
216
+ }
217
+ export type SearchIndex = {
218
+ kind: "fulltext";
219
+ name: string;
220
+ fields: readonly ScalarField[];
221
+ analyzer: "STANDARD" | "SIMPLE";
222
+ /** Root field, e.g. `searchFestivals`. */
223
+ queryName: string;
224
+ } | {
225
+ kind: "vector";
226
+ name: string;
227
+ field: ScalarField;
228
+ dimensions: number;
229
+ similarity: "COSINE" | "EUCLIDEAN";
230
+ queryName: string;
231
+ };
232
+ /** An interface over @node types, or a union of them. */
233
+ export interface AbstractType {
234
+ kind: "interface" | "union";
235
+ name: string;
236
+ /** Concrete @node types: implementations or members. */
237
+ members: readonly string[];
238
+ /** An interface's scalar fields, as declared on the interface. */
239
+ fields: ReadonlyMap<string, ScalarField>;
240
+ /**
241
+ * An interface's `@declareRelationship` fields: every implementation has
242
+ * a relationship field of that name, target and shape.
243
+ */
244
+ relationships: ReadonlyMap<string, DeclaredRelationship>;
245
+ plural: string;
246
+ read: boolean;
247
+ limit: PageLimit;
248
+ description: string | undefined;
249
+ }
250
+ export interface DeclaredRelationship {
251
+ name: string;
252
+ target: string;
253
+ list: boolean;
254
+ description: string | undefined;
255
+ }
256
+ /**
257
+ * An object type without @node, returned by @cypher fields as maps: its
258
+ * fields are read from the map's keys.
259
+ */
260
+ export interface PlainObjectType {
261
+ name: string;
262
+ fields: ReadonlyMap<string, {
263
+ name: string;
264
+ type: TypeShape;
265
+ }>;
266
+ description: string | undefined;
267
+ }
268
+ export interface RelationshipPropertiesType {
269
+ name: string;
270
+ fields: ReadonlyMap<string, ScalarField>;
271
+ description: string | undefined;
272
+ }
273
+ export interface EnumType {
274
+ name: string;
275
+ values: ReadonlyArray<{
276
+ name: string;
277
+ description: string | undefined;
278
+ }>;
279
+ description: string | undefined;
280
+ }
281
+ export interface GraphModel {
282
+ nodes: ReadonlyMap<string, NodeType>;
283
+ /** Interfaces and unions over @node types. */
284
+ abstracts: ReadonlyMap<string, AbstractType>;
285
+ enums: ReadonlyMap<string, EnumType>;
286
+ relationshipProperties: ReadonlyMap<string, RelationshipPropertiesType>;
287
+ /** `@cypher` fields declared on `Query` and `Mutation`. */
288
+ queries: readonly CypherField[];
289
+ mutations: readonly CypherField[];
290
+ /** Problems that do not stop the model, e.g. an unused @cypher argument. */
291
+ warnings: readonly ModelWarning[];
292
+ /** Custom scalars (`@storedAs`), by name: their storage type. */
293
+ scalars: ReadonlyMap<string, ScalarType>;
294
+ /** Object types without @node that @cypher fields return. */
295
+ objects: ReadonlyMap<string, PlainObjectType>;
296
+ /** The `@jwt` claims shape, when declared: claim name → token path. */
297
+ jwt: ReadonlyMap<string, string> | undefined;
298
+ /** Secret cursors are signed with, when configured. */
299
+ cursorSecret: string | undefined;
300
+ }
301
+ export interface ModelWarning {
302
+ type: string;
303
+ field?: string;
304
+ message: string;
305
+ }
306
+ export declare function scalarFields(t: {
307
+ fields: ReadonlyMap<string, Field>;
308
+ }): ScalarField[];
309
+ export declare function cypherFields(t: NodeType): CypherField[];
310
+ export declare function relationshipFields(t: NodeType): RelationshipField[];
311
+ export {};
@@ -0,0 +1,72 @@
1
+ import { QueryResult, Statement } from './driver.js';
2
+ export interface StatementEndEvent {
3
+ /** The root field the statements belong to. */
4
+ field: string;
5
+ /** Statements run in one call: reads batch theirs, writes run one by one. */
6
+ statements: Statement[];
7
+ mode: "read" | "write";
8
+ durationMs: number;
9
+ /** Rows returned, over every statement. */
10
+ rows: number;
11
+ /** Set when the statements failed. */
12
+ error?: unknown;
13
+ /** Estimated rows touched, for reads (the `maxCost` estimate). */
14
+ cost?: number;
15
+ operationName?: string;
16
+ /** Id of the persisted operation, when the request used one. */
17
+ persistedId?: string;
18
+ }
19
+ export interface SpanLike {
20
+ setAttribute(key: string, value: string | number | boolean): unknown;
21
+ setStatus(status: {
22
+ code: number;
23
+ message?: string;
24
+ }): unknown;
25
+ recordException(exception: Error | string): unknown;
26
+ end(): void;
27
+ }
28
+ export interface TracerLike {
29
+ startActiveSpan<T>(name: string, fn: (span: SpanLike) => T): T;
30
+ }
31
+ export interface MetricsLike {
32
+ counter(name: string, value: number, attributes?: Attributes): void;
33
+ histogram(name: string, value: number, attributes?: Attributes): void;
34
+ }
35
+ export type Attributes = Record<string, string | number | boolean>;
36
+ export interface ObservabilityOptions {
37
+ /** Called after every statement (or batch of read statements). */
38
+ onStatementEnd?: (event: StatementEndEvent) => void;
39
+ /**
40
+ * Emits a `lora.graphql.field` span per root field, containing a
41
+ * `lora.cypher` span per statement call.
42
+ */
43
+ tracer?: TracerLike;
44
+ /** Put the Cypher text in spans as `db.statement`. Default false. */
45
+ traceStatements?: boolean;
46
+ /**
47
+ * Receives `lora.graphql.statements` (counter), `lora.graphql.errors`
48
+ * (counter), `lora.graphql.statement.duration` (ms) and
49
+ * `lora.graphql.cost` (estimated rows) histograms.
50
+ */
51
+ metrics?: MetricsLike;
52
+ }
53
+ /** What a statement call is part of. */
54
+ export interface StatementMeta {
55
+ field: string;
56
+ mode: "read" | "write";
57
+ cost?: number | undefined;
58
+ operationName?: string | undefined;
59
+ persistedId?: string | undefined;
60
+ }
61
+ export declare class Observer {
62
+ #private;
63
+ constructor(options: ObservabilityOptions);
64
+ get active(): boolean;
65
+ /** Run a root field's resolver inside its span. */
66
+ field<T>(meta: {
67
+ field: string;
68
+ operationName?: string | undefined;
69
+ }, fn: () => Promise<T>): Promise<T>;
70
+ /** Run statements, timing them and reporting the outcome. */
71
+ statements<T extends QueryResult | QueryResult[]>(meta: StatementMeta, statements: Statement[], run: () => Promise<T>): Promise<T>;
72
+ }
@@ -0,0 +1,38 @@
1
+ import { GraphQLSchema, GraphQLFieldResolver, GraphQLResolveInfo, GraphQLScalarType } from 'graphql';
2
+ import { RootKind } from '../compile/read.js';
3
+ import { AbstractType, CustomField, CypherField, GraphModel, NodeType, 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
+ /** Implementations of custom scalars, by name (the scalars option). */
26
+ scalars?: Readonly<Record<string, GraphQLScalarType>> | undefined;
27
+ /** The resolver of a `@customResolver` field (from the resolvers option). */
28
+ customResolver: (node: NodeType, field: CustomField) => GraphQLFieldResolver<Record<string, unknown>, unknown>;
29
+ resolveSearch: (node: NodeType, index: SearchIndex, info: GraphQLResolveInfo, context: unknown, connection: boolean) => Promise<unknown>;
30
+ resolveRoot: RootResolver;
31
+ resolveNode: NodeResolver;
32
+ resolveCypher: CypherRootResolver;
33
+ resolveMutation: MutationResolver;
34
+ }
35
+ export type CypherRootResolver = (field: CypherField, info: GraphQLResolveInfo, context: unknown) => Promise<unknown>;
36
+ export type RootResolver = (kind: RootKind, node: NodeType, info: GraphQLResolveInfo, context: unknown) => Promise<unknown>;
37
+ export type NodeResolver = (id: string, info: GraphQLResolveInfo, context: unknown) => Promise<unknown>;
38
+ export declare function buildSchema(model: GraphModel, hooks: SchemaHooks): GraphQLSchema;
@@ -0,0 +1,6 @@
1
+ /** Opaque global id: the type name and @key value. */
2
+ export declare function toGlobalId(type: string, key: unknown): string;
3
+ export declare function fromGlobalId(id: string): {
4
+ type: string;
5
+ key: string | number;
6
+ } | undefined;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Nodes carrying `__authorized: false` failed an `@authorization`
3
+ * validate rule for READ: reading them is an error, not an empty result.
4
+ */
5
+ export declare function assertReadable<T>(value: T): T;
@@ -0,0 +1,44 @@
1
+ import { GraphQLObjectType, GraphQLFieldConfigMap, GraphQLInputType, GraphQLResolveInfo } from 'graphql';
2
+ import { GraphModel, MutationOperation, NodeType, ScalarField } from '../model/types.js';
3
+ /** A generated mutation: the single-node ones, and the bulk ones by where. */
4
+ export type MutationKind = MutationOperation | "UPSERT" | "UPDATE_MANY" | "DELETE_MANY";
5
+ export type MutationResolver = (operation: MutationKind, node: NodeType, info: GraphQLResolveInfo, context: unknown) => Promise<unknown>;
6
+ export declare const mutationNames: {
7
+ create: (t: NodeType) => string;
8
+ update: (t: NodeType) => string;
9
+ delete: (t: NodeType) => string;
10
+ upsert: (t: NodeType) => string;
11
+ updateMany: (t: NodeType) => string;
12
+ deleteMany: (t: NodeType) => string;
13
+ upsertInput: (t: string) => string;
14
+ upsertPayload: (t: NodeType) => string;
15
+ createInput: (t: string) => string;
16
+ updateInput: (t: string) => string;
17
+ adjustInput: (t: string) => string;
18
+ createPayload: (t: NodeType) => string;
19
+ updatePayload: (t: NodeType) => string;
20
+ updateManyPayload: (t: NodeType) => string;
21
+ relationCreate: (t: string, f: string) => string;
22
+ relationUpdate: (t: string, f: string) => string;
23
+ connect: (t: string, f: string) => string;
24
+ nestedCreate: (t: string, f: string) => string;
25
+ nestedUpdate: (t: string, f: string) => string;
26
+ /** Payload field holding the created nodes / the updated node. */
27
+ createdField: (t: NodeType) => string;
28
+ updatedField: (t: NodeType) => string;
29
+ };
30
+ export interface MutationSchemaContext {
31
+ model: GraphModel;
32
+ object: (typeName: string) => GraphQLObjectType;
33
+ inputType: (f: ScalarField) => GraphQLInputType;
34
+ where: (typeName: string) => GraphQLInputType;
35
+ }
36
+ /** Whether clients may set a field on `op` (`@settable`, `@readonly`, …). */
37
+ export declare function settable(f: ScalarField, op: "CREATE" | "UPDATE"): boolean;
38
+ /** Whether the create input must carry the field. */
39
+ export declare function requiredOnCreate(f: ScalarField): boolean;
40
+ /** A type with nothing settable after create has no update. */
41
+ export declare function isUpdatable(node: NodeType): boolean;
42
+ /** Numeric and list fields take operators in `adjust`. */
43
+ export declare function adjustable(f: ScalarField): boolean;
44
+ export declare function buildMutations(ctx: MutationSchemaContext, resolve: MutationResolver): GraphQLFieldConfigMap<unknown, unknown>;
@@ -0,0 +1,37 @@
1
+ import { NodeType, RelationshipField } from '../model/types.js';
2
+ export declare const upperFirst: (s: string) => string;
3
+ export declare const names: {
4
+ where: (type: string) => string;
5
+ fieldFilter: (type: string, field: string) => string;
6
+ sort: (type: string) => string;
7
+ connection: (type: string) => string;
8
+ edge: (type: string) => string;
9
+ aggregate: (type: string) => string;
10
+ match: (type: string) => string;
11
+ searchConnection: (type: string) => string;
12
+ searchEdge: (type: string) => string;
13
+ relationFilter: (type: string, field: string) => string;
14
+ relConnection: (type: string, field: string) => string;
15
+ relEdge: (type: string, field: string) => string;
16
+ relConnectionWhere: (type: string, field: string) => string;
17
+ connectionField: (field: string) => string;
18
+ listRoot: (t: NodeType) => string;
19
+ connectionRoot: (t: NodeType) => string;
20
+ aggregateRoot: (t: NodeType) => string;
21
+ groupedRoot: (t: NodeType) => string;
22
+ singleRoot: (t: NodeType) => string;
23
+ };
24
+ /**
25
+ * Whether a list relationship gets its own connection/edge types (and a
26
+ * `{ node, edge }` where): it has relationship properties.
27
+ */
28
+ export declare function hasOwnConnection(rel: RelationshipField): boolean;
29
+ /**
30
+ * The connection and edge type names of a list relationship's connection:
31
+ * its own when it has properties or aggregates (whose count covers
32
+ * relationships too) or opts out of them, the target's otherwise.
33
+ */
34
+ export declare function connectionTypeNames(rel: RelationshipField, target: NodeType): {
35
+ connection: string;
36
+ edge: string;
37
+ };
@@ -0,0 +1,9 @@
1
+ import { GraphQLScalarType } from 'graphql';
2
+ export declare const GraphQLDate: GraphQLScalarType<unknown, unknown>;
3
+ export declare const GraphQLTime: GraphQLScalarType<unknown, unknown>;
4
+ export declare const GraphQLLocalTime: GraphQLScalarType<unknown, unknown>;
5
+ export declare const GraphQLDateTime: GraphQLScalarType<unknown, unknown>;
6
+ export declare const GraphQLLocalDateTime: GraphQLScalarType<unknown, unknown>;
7
+ export declare const GraphQLDuration: GraphQLScalarType<unknown, unknown>;
8
+ export declare const GraphQLBigInt: GraphQLScalarType<number | bigint, string>;
9
+ export declare const CUSTOM_SCALARS: Record<string, GraphQLScalarType>;
@@ -0,0 +1,36 @@
1
+ import { DocumentNode, ExecutionResult, GraphQLSchema } from 'graphql';
2
+ import { LoraDatabaseLike } from './driver.js';
3
+ import { LoraGraphQL, LoraGraphQLOptions, StatementEvent } from './lora-graphql.js';
4
+ /** The node binding's database, as far as tests use it. */
5
+ export type TestDatabase = LoraDatabaseLike & {
6
+ execute(query: string, params?: never): Promise<unknown>;
7
+ dispose?(): void;
8
+ };
9
+ export interface TestLoraGraphQLOptions extends Omit<LoraGraphQLOptions, "driver"> {
10
+ /** Cypher to run after the schema is asserted, or a function given the database. */
11
+ seed?: string | string[] | ((db: TestDatabase) => Promise<void>);
12
+ }
13
+ export interface TestLoraGraphQL {
14
+ lora: LoraGraphQL;
15
+ db: TestDatabase;
16
+ schema: GraphQLSchema;
17
+ /** Every statement run so far (clear with `statements.length = 0`). */
18
+ statements: StatementEvent[];
19
+ /** Execute a document; errors stay in the result. */
20
+ run: (source: string, variables?: Record<string, unknown>, context?: Record<string, unknown>) => Promise<ExecutionResult>;
21
+ /** Execute a document and return its data; throws on the first error. */
22
+ data: <T = Record<string, unknown>>(source: string, variables?: Record<string, unknown>, context?: Record<string, unknown>) => Promise<T>;
23
+ /** Release the database. */
24
+ close: () => void;
25
+ }
26
+ /** A LoraGraphQL over a fresh in-memory database, schema asserted and seeded. */
27
+ export declare function createTestLoraGraphQL(options: TestLoraGraphQLOptions): Promise<TestLoraGraphQL>;
28
+ /**
29
+ * Assert that every statement of a query's root fields uses the index
30
+ * access it was compiled for (no label scans where a seek was expected,
31
+ * no mutating plans, no row-budget excess). Throws listing each finding.
32
+ */
33
+ export declare function expectSeeks(lora: LoraGraphQL, document: string | DocumentNode, variables?: Record<string, unknown>, options?: {
34
+ rowBudget?: number;
35
+ context?: unknown;
36
+ }): Promise<void>;