@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.
- package/LICENSE +87 -0
- package/README.md +994 -0
- package/dist/analyze/cypher-check.d.ts +8 -0
- package/dist/analyze/diff.d.ts +32 -0
- package/dist/analyze/indexes.d.ts +61 -0
- package/dist/analyze/lint.d.ts +6 -0
- package/dist/analyze/plans.d.ts +31 -0
- package/dist/analyze/statistics.d.ts +19 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +631 -0
- package/dist/cli.js.map +1 -0
- package/dist/codegen.d.ts +32 -0
- package/dist/compile/aggregate.d.ts +17 -0
- package/dist/compile/auth.d.ts +46 -0
- package/dist/compile/context.d.ts +43 -0
- package/dist/compile/cursor.d.ts +4 -0
- package/dist/compile/cypher.d.ts +175 -0
- package/dist/compile/filter.d.ts +19 -0
- package/dist/compile/hmac.d.ts +4 -0
- package/dist/compile/read.d.ts +89 -0
- package/dist/compile/selection.d.ts +18 -0
- package/dist/diff-BVP1Jzh3.js +99 -0
- package/dist/diff-BVP1Jzh3.js.map +1 -0
- package/dist/driver-C8vA5fV-.js +9127 -0
- package/dist/driver-C8vA5fV-.js.map +1 -0
- package/dist/driver.d.ts +117 -0
- package/dist/errors.d.ts +20 -0
- package/dist/execute/changes.d.ts +61 -0
- package/dist/execute/cypher-mutation.d.ts +4 -0
- package/dist/execute/feed.d.ts +10 -0
- package/dist/execute/mutate.d.ts +57 -0
- package/dist/execute/transaction.d.ts +17 -0
- package/dist/guards.d.ts +42 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/lora-graphql.d.ts +291 -0
- package/dist/migrate.d.ts +8 -0
- package/dist/model/build.d.ts +25 -0
- package/dist/model/cypher-lexer.d.ts +10 -0
- package/dist/model/directives.d.ts +3 -0
- package/dist/model/points.d.ts +12 -0
- package/dist/model/relations.d.ts +7 -0
- package/dist/model/types.d.ts +311 -0
- package/dist/observe.d.ts +72 -0
- package/dist/schema/build.d.ts +38 -0
- package/dist/schema/global-id.d.ts +6 -0
- package/dist/schema/guard.d.ts +5 -0
- package/dist/schema/mutations.d.ts +44 -0
- package/dist/schema/names.d.ts +37 -0
- package/dist/schema/scalars.d.ts +9 -0
- package/dist/testing.d.ts +36 -0
- package/dist/testing.js +56 -0
- package/dist/testing.js.map +1 -0
- package/package.json +85 -0
package/dist/driver.d.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
export type LoraParams = Record<string, unknown>;
|
|
2
|
+
export interface Statement {
|
|
3
|
+
text: string;
|
|
4
|
+
params: LoraParams;
|
|
5
|
+
}
|
|
6
|
+
export interface QueryResult {
|
|
7
|
+
columns: string[];
|
|
8
|
+
rows: Array<Record<string, unknown>>;
|
|
9
|
+
}
|
|
10
|
+
export interface PlanNode {
|
|
11
|
+
id: number;
|
|
12
|
+
operator: string;
|
|
13
|
+
details: Record<string, string>;
|
|
14
|
+
estimatedRows: number | null;
|
|
15
|
+
children: PlanNode[];
|
|
16
|
+
}
|
|
17
|
+
export interface QueryPlan {
|
|
18
|
+
query: string;
|
|
19
|
+
shape: "readOnly" | "mutating";
|
|
20
|
+
resultColumns: string[];
|
|
21
|
+
tree: PlanNode;
|
|
22
|
+
}
|
|
23
|
+
export interface RunOptions {
|
|
24
|
+
/** `read` runs in a read-only transaction: the engine rejects writes. */
|
|
25
|
+
mode: "read" | "write";
|
|
26
|
+
timeoutMs?: number | undefined;
|
|
27
|
+
signal?: AbortSignal | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* A read the library vouches for: generated, or a @cypher statement
|
|
30
|
+
* checked read-only at startup. The driver may stream it outside a
|
|
31
|
+
* read-only transaction.
|
|
32
|
+
*/
|
|
33
|
+
verified?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/** An open interactive transaction: statements see earlier writes. */
|
|
36
|
+
export interface DriverTransaction {
|
|
37
|
+
execute(statement: Statement): Promise<QueryResult>;
|
|
38
|
+
commit(): Promise<void>;
|
|
39
|
+
rollback(): Promise<void>;
|
|
40
|
+
/** False once committed, rolled back, or failed (a failure rolls back). */
|
|
41
|
+
readonly isOpen: boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface LoraDriver {
|
|
44
|
+
/** Run statements atomically, in one transaction, in order. */
|
|
45
|
+
run(statements: Statement[], options: RunOptions): Promise<QueryResult[]>;
|
|
46
|
+
/**
|
|
47
|
+
* Open an interactive transaction. Needed for mutations, which check
|
|
48
|
+
* their writes (connect targets, cardinality, authorization) before
|
|
49
|
+
* committing. Optional: the WASM binding has none, so it serves reads.
|
|
50
|
+
*/
|
|
51
|
+
begin?(options: RunOptions): Promise<DriverTransaction>;
|
|
52
|
+
/** Plan a statement without running it. Optional: the WASM binding has no `explain()`. */
|
|
53
|
+
explain?(statement: Statement): Promise<QueryPlan>;
|
|
54
|
+
/** The engine's committed change feed (lora-node `db.changes()`). */
|
|
55
|
+
changes?(options: {
|
|
56
|
+
fromLsn?: number;
|
|
57
|
+
signal?: AbortSignal;
|
|
58
|
+
}): AsyncIterable<DriverChangeBatch> & {
|
|
59
|
+
ready: Promise<void>;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/** One change of a committed write, as lora-node reports it. */
|
|
63
|
+
export interface DriverChange {
|
|
64
|
+
kind: "nodeCreated" | "nodeUpdated" | "nodeDeleted" | "relationshipCreated" | "relationshipUpdated" | "relationshipDeleted" | "reset";
|
|
65
|
+
id?: number;
|
|
66
|
+
labels?: string[];
|
|
67
|
+
type?: string;
|
|
68
|
+
startId?: number;
|
|
69
|
+
endId?: number;
|
|
70
|
+
properties?: Record<string, unknown>;
|
|
71
|
+
}
|
|
72
|
+
export interface DriverChangeBatch {
|
|
73
|
+
lsn: number;
|
|
74
|
+
changes: DriverChange[];
|
|
75
|
+
}
|
|
76
|
+
/** The subset of a LoraDB `Database` the adapter calls. */
|
|
77
|
+
export interface LoraDatabaseLike {
|
|
78
|
+
transaction(statements: Array<{
|
|
79
|
+
query: string;
|
|
80
|
+
params?: never;
|
|
81
|
+
}>, mode?: "read_write" | "read_only", options?: {
|
|
82
|
+
timeoutMs?: number;
|
|
83
|
+
signal?: AbortSignal;
|
|
84
|
+
}): Promise<QueryResult[]>;
|
|
85
|
+
explain?(query: string, params?: never): Promise<QueryPlan>;
|
|
86
|
+
changes?(options?: {
|
|
87
|
+
fromLsn?: number;
|
|
88
|
+
signal?: AbortSignal;
|
|
89
|
+
}): AsyncIterable<DriverChangeBatch> & {
|
|
90
|
+
ready: Promise<void>;
|
|
91
|
+
};
|
|
92
|
+
stream?(query: string, params?: never, options?: {
|
|
93
|
+
timeoutMs?: number;
|
|
94
|
+
signal?: AbortSignal;
|
|
95
|
+
}): {
|
|
96
|
+
columns(): string[];
|
|
97
|
+
toArray(): Promise<Array<Record<string, unknown>>>;
|
|
98
|
+
};
|
|
99
|
+
begin?(mode?: "read_write" | "read_only"): Promise<{
|
|
100
|
+
execute(query: string, params?: never, options?: {
|
|
101
|
+
timeoutMs?: number;
|
|
102
|
+
signal?: AbortSignal;
|
|
103
|
+
}): Promise<QueryResult>;
|
|
104
|
+
commit(): Promise<void>;
|
|
105
|
+
rollback(): Promise<void>;
|
|
106
|
+
readonly isOpen: boolean;
|
|
107
|
+
}>;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Wrap a `Database` from `@loradb/lora-node` or `@loradb/lora-wasm`.
|
|
111
|
+
*
|
|
112
|
+
* ```ts
|
|
113
|
+
* const db = await createDatabase();
|
|
114
|
+
* const lora = new LoraGraphQL({ typeDefs, driver: loraDriver(db) });
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export declare function loraDriver(db: LoraDatabaseLike): LoraDriver;
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { GraphQLError } from 'graphql';
|
|
2
|
+
export interface ModelProblem {
|
|
3
|
+
/** Type the problem is in, when it belongs to one. */
|
|
4
|
+
type?: string;
|
|
5
|
+
/** Field the problem is in, when it belongs to one. */
|
|
6
|
+
field?: string;
|
|
7
|
+
message: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The annotated SDL is invalid. Carries every problem found, not just the
|
|
11
|
+
* first, each located by type and field.
|
|
12
|
+
*/
|
|
13
|
+
export declare class ModelError extends Error {
|
|
14
|
+
readonly problems: readonly ModelProblem[];
|
|
15
|
+
constructor(problems: ModelProblem[]);
|
|
16
|
+
}
|
|
17
|
+
export declare function formatProblem(p: ModelProblem): string;
|
|
18
|
+
export type LoraGraphQLErrorCode = "BAD_USER_INPUT" | "INVALID_CURSOR" | "LIMIT_EXCEEDED" | "COST_EXCEEDED" | "UNAUTHENTICATED" | "FORBIDDEN" | "NOT_FOUND" | "CONSTRAINT_VIOLATION" | "DATABASE_ERROR";
|
|
19
|
+
/** A request-time error with a stable `extensions.code`. */
|
|
20
|
+
export declare function requestError(code: LoraGraphQLErrorCode, message: string, cause?: unknown, extensions?: Record<string, unknown>): GraphQLError;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { MutationOperation } from '../model/types.js';
|
|
2
|
+
export interface EntityRef {
|
|
3
|
+
/** Node type name. */
|
|
4
|
+
type: string;
|
|
5
|
+
/** Its @key value. */
|
|
6
|
+
key: unknown;
|
|
7
|
+
}
|
|
8
|
+
export interface RelationshipRef {
|
|
9
|
+
/** Relationship type, e.g. `FOLLOWS`. */
|
|
10
|
+
type: string;
|
|
11
|
+
/** `Owner.field` that declares it. */
|
|
12
|
+
field: string;
|
|
13
|
+
from: EntityRef;
|
|
14
|
+
to: EntityRef;
|
|
15
|
+
}
|
|
16
|
+
export interface WriteChange {
|
|
17
|
+
/** `EXTERNAL`: from the engine's change feed (`changeFeed: true`). */
|
|
18
|
+
operation: MutationOperation | "UPSERT" | "CYPHER" | "EXTERNAL";
|
|
19
|
+
/** When the change was committed (ISO-8601), set when it is emitted. */
|
|
20
|
+
timestamp?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Stored properties before the write, for updated and deleted nodes of
|
|
23
|
+
* types with `@subscription(previousState: true)`.
|
|
24
|
+
*/
|
|
25
|
+
before?: Array<{
|
|
26
|
+
type: string;
|
|
27
|
+
key: unknown;
|
|
28
|
+
properties: Record<string, unknown>;
|
|
29
|
+
}>;
|
|
30
|
+
/** The Mutation field that made the change. */
|
|
31
|
+
field: string;
|
|
32
|
+
created: EntityRef[];
|
|
33
|
+
updated: EntityRef[];
|
|
34
|
+
deleted: EntityRef[];
|
|
35
|
+
connected: RelationshipRef[];
|
|
36
|
+
disconnected: RelationshipRef[];
|
|
37
|
+
/**
|
|
38
|
+
* Every node whose observable state changed: created, updated and
|
|
39
|
+
* deleted nodes, and both ends of every relationship written. A
|
|
40
|
+
* response cache should invalidate by `types` too: lists, counts and
|
|
41
|
+
* connections change without naming an entity (see the Yoga example).
|
|
42
|
+
*/
|
|
43
|
+
entities: EntityRef[];
|
|
44
|
+
/** Node types in `entities`: lists of these types may have changed. */
|
|
45
|
+
types: string[];
|
|
46
|
+
relationshipTypes: string[];
|
|
47
|
+
/**
|
|
48
|
+
* The write-set is unknown (a `@cypher` mutation): treat everything as
|
|
49
|
+
* changed.
|
|
50
|
+
*/
|
|
51
|
+
broad: boolean;
|
|
52
|
+
}
|
|
53
|
+
export interface ReadSetLike {
|
|
54
|
+
labels: readonly string[];
|
|
55
|
+
relationships: readonly string[];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Whether a read with this read-set may observe the change. Label-level:
|
|
59
|
+
* a read of `:Festival` nodes is affected by any Festival write.
|
|
60
|
+
*/
|
|
61
|
+
export declare function affects(reads: ReadSetLike, change: WriteChange, labelsOf: (type: string) => readonly string[]): boolean;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { FieldNode } from 'graphql';
|
|
2
|
+
import { CypherField } from '../model/types.js';
|
|
3
|
+
import { MutationEnv } from './mutate.js';
|
|
4
|
+
export declare function executeCypherMutation(env: MutationEnv, field: CypherField, args: Record<string, unknown>, fieldNodes: readonly FieldNode[]): Promise<unknown>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { LoraDriver } from '../driver.js';
|
|
2
|
+
import { GraphModel } from '../model/types.js';
|
|
3
|
+
import { WriteChange } from './changes.js';
|
|
4
|
+
export declare class EngineFeed {
|
|
5
|
+
#private;
|
|
6
|
+
constructor(driver: LoraDriver, model: GraphModel, emit: (change: WriteChange) => void);
|
|
7
|
+
/** Subscribe; resolves once every later commit will be delivered. */
|
|
8
|
+
start(): Promise<void>;
|
|
9
|
+
stop(): void;
|
|
10
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { FieldNode } from 'graphql';
|
|
2
|
+
import { SelectionContext } from '../compile/selection.js';
|
|
3
|
+
import { DriverTransaction, LoraDriver, QueryResult, Statement } from '../driver.js';
|
|
4
|
+
import { GraphModel, NodeType } from '../model/types.js';
|
|
5
|
+
import { MutationKind } from '../schema/mutations.js';
|
|
6
|
+
import { WriteChange } from './changes.js';
|
|
7
|
+
/** Computes a `@populatedBy` field. */
|
|
8
|
+
export type PopulatedByCallback = (args: {
|
|
9
|
+
operation: "CREATE" | "UPDATE";
|
|
10
|
+
/** Node type and field being populated. */
|
|
11
|
+
type: string;
|
|
12
|
+
field: string;
|
|
13
|
+
key: unknown;
|
|
14
|
+
/** The node's input in this mutation. */
|
|
15
|
+
input: Record<string, unknown>;
|
|
16
|
+
/** The GraphQL context. */
|
|
17
|
+
context: unknown;
|
|
18
|
+
}) => unknown;
|
|
19
|
+
export interface MutationEnv {
|
|
20
|
+
model: GraphModel;
|
|
21
|
+
driver: LoraDriver;
|
|
22
|
+
selection: SelectionContext;
|
|
23
|
+
jwt: Record<string, unknown> | undefined;
|
|
24
|
+
/** The GraphQL context, for `$context` in rules and for callbacks. */
|
|
25
|
+
requestContext: unknown;
|
|
26
|
+
timeoutMs: number;
|
|
27
|
+
signal: AbortSignal | undefined;
|
|
28
|
+
degrees: ReadonlyMap<string, number>;
|
|
29
|
+
/** Most nodes one mutation may create or delete, nested ones included. */
|
|
30
|
+
maxBatch: number;
|
|
31
|
+
callbacks: Readonly<Record<string, PopulatedByCallback>>;
|
|
32
|
+
/**
|
|
33
|
+
* A caller-owned transaction: run inside it and leave the commit to the
|
|
34
|
+
* caller. A failed mutation rolls it back, so it is never half-applied.
|
|
35
|
+
*/
|
|
36
|
+
transaction?: DriverTransaction | undefined;
|
|
37
|
+
/** Observes every statement, for logging and tests. */
|
|
38
|
+
onStatement?: ((statement: Statement) => void) | undefined;
|
|
39
|
+
/** Runs a statement under timing, tracing and metrics, when configured. */
|
|
40
|
+
observe?: ((statement: Statement, run: () => Promise<QueryResult>) => Promise<QueryResult>) | undefined;
|
|
41
|
+
}
|
|
42
|
+
/** Run `statement` in `tx`, reporting it before and observing it during. */
|
|
43
|
+
export declare function runStatement(env: MutationEnv, tx: DriverTransaction, statement: Statement): Promise<QueryResult>;
|
|
44
|
+
export interface MutationInfo {
|
|
45
|
+
nodesCreated: number;
|
|
46
|
+
nodesUpdated: number;
|
|
47
|
+
nodesDeleted: number;
|
|
48
|
+
relationshipsCreated: number;
|
|
49
|
+
relationshipsDeleted: number;
|
|
50
|
+
}
|
|
51
|
+
export interface MutationResult {
|
|
52
|
+
payload: unknown;
|
|
53
|
+
change: WriteChange;
|
|
54
|
+
}
|
|
55
|
+
export declare function executeMutation(env: MutationEnv, op: MutationKind, node: NodeType, args: Record<string, unknown>, fieldNodes: readonly FieldNode[], fieldName: string): Promise<MutationResult>;
|
|
56
|
+
/** Turn engine constraint errors into CONSTRAINT_VIOLATION naming the field. */
|
|
57
|
+
export declare function mapWriteError(model: GraphModel, err: unknown): unknown;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { DriverTransaction, QueryResult } from '../driver.js';
|
|
2
|
+
import { WriteChange } from './changes.js';
|
|
3
|
+
export declare class LoraTransaction {
|
|
4
|
+
#private;
|
|
5
|
+
/** @internal Created by `LoraGraphQL.begin()`. */
|
|
6
|
+
constructor(tx: DriverTransaction, emit: (change: WriteChange) => void);
|
|
7
|
+
/** @internal The driver transaction statements run in. */
|
|
8
|
+
get driverTransaction(): DriverTransaction;
|
|
9
|
+
/** False once committed, rolled back, or failed. */
|
|
10
|
+
get isOpen(): boolean;
|
|
11
|
+
/** Run the application's own Cypher in the transaction. */
|
|
12
|
+
execute(query: string, params?: Record<string, unknown>): Promise<QueryResult>;
|
|
13
|
+
/** @internal A mutation's write-set, reported when the transaction commits. */
|
|
14
|
+
record(change: WriteChange): void;
|
|
15
|
+
commit(): Promise<void>;
|
|
16
|
+
rollback(): Promise<void>;
|
|
17
|
+
}
|
package/dist/guards.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ParseOptions, ValidationRule } from 'graphql';
|
|
2
|
+
export interface DocumentGuards {
|
|
3
|
+
/** Deepest field nesting, fragments followed. Default 12. */
|
|
4
|
+
maxDepth?: number;
|
|
5
|
+
/** Aliased fields in one document. Default 30. */
|
|
6
|
+
maxAliases?: number;
|
|
7
|
+
/** Root fields in one operation. Default 20. */
|
|
8
|
+
maxRootFields?: number;
|
|
9
|
+
/** Lexer tokens in one document, checked while parsing. Default 5000. */
|
|
10
|
+
maxTokens?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Allow `__schema` and `__type`. Default: off when `NODE_ENV` is
|
|
13
|
+
* `production`, on otherwise. `__typename` is always allowed.
|
|
14
|
+
*/
|
|
15
|
+
introspection?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare const DEFAULT_GUARDS: {
|
|
18
|
+
readonly maxDepth: 12;
|
|
19
|
+
readonly maxAliases: 30;
|
|
20
|
+
readonly maxRootFields: 20;
|
|
21
|
+
readonly maxTokens: 5000;
|
|
22
|
+
};
|
|
23
|
+
export declare function resolveGuards(guards?: DocumentGuards): Required<DocumentGuards>;
|
|
24
|
+
/** Validation rules for the guards, to add to the standard ones. */
|
|
25
|
+
export declare function validationRules(guards?: DocumentGuards): ValidationRule[];
|
|
26
|
+
/** Parse options for the guards (the token limit). */
|
|
27
|
+
export declare function parseOptions(guards?: DocumentGuards): ParseOptions;
|
|
28
|
+
/**
|
|
29
|
+
* The guards as an Envelop plugin, for GraphQL Yoga and other Envelop
|
|
30
|
+
* servers: the token limit wraps `parse`, the rest are validation rules.
|
|
31
|
+
* Typed structurally, so the package does not depend on Envelop.
|
|
32
|
+
*/
|
|
33
|
+
export declare function envelopPlugin(guards?: DocumentGuards): {
|
|
34
|
+
onParse({ parseFn, setParseFn, }: {
|
|
35
|
+
parseFn: (source: unknown, options?: ParseOptions) => unknown;
|
|
36
|
+
setParseFn: (fn: (source: unknown, options?: ParseOptions) => unknown) => void;
|
|
37
|
+
}): void;
|
|
38
|
+
onValidate({ addValidationRule, }: {
|
|
39
|
+
addValidationRule: (rule: ValidationRule) => void;
|
|
40
|
+
}): void;
|
|
41
|
+
};
|
|
42
|
+
export declare function nodeEnv(): string | undefined;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export { LoraGraphQL, type AssertSchemaOptions, type CheckOptions, type CheckReport, type CostEvent, type DatabaseErrorEvent, type ExecuteArgs, type LoraGraphQLContext, type LoraGraphQLOptions, type SchemaAssertion, type StatementEvent, } from './lora-graphql.js';
|
|
2
|
+
export { loraDriver, type DriverTransaction, type LoraDatabaseLike, type LoraDriver, type QueryPlan, type QueryResult, type RunOptions, type Statement, } from './driver.js';
|
|
3
|
+
export { directiveTypeDefs } from './model/directives.js';
|
|
4
|
+
export { buildModel, type ModelOptions } from './model/build.js';
|
|
5
|
+
export type * from './model/types.js';
|
|
6
|
+
export { inferRequirements, requirementDdl, type SchemaRequirement, } from './analyze/indexes.js';
|
|
7
|
+
export { checkPlans, scanExpands, type PlanFinding, type PlanReport, } from './analyze/plans.js';
|
|
8
|
+
export type { DegreeStats, Statistics } from './analyze/statistics.js';
|
|
9
|
+
export type { CypherFinding } from './analyze/cypher-check.js';
|
|
10
|
+
export { diffSchemas, type ApiChange, type SchemaDiff, } from './analyze/diff.js';
|
|
11
|
+
export type { CompiledRead, ReadSet, SeekExpectation } from './compile/read.js';
|
|
12
|
+
export type { EntityRef, RelationshipRef, WriteChange, } from './execute/changes.js';
|
|
13
|
+
export type { MutationInfo, PopulatedByCallback } from './execute/mutate.js';
|
|
14
|
+
export type { MutationKind } from './schema/mutations.js';
|
|
15
|
+
export type { ChangeEvent } from './schema/build.js';
|
|
16
|
+
export { LoraTransaction } from './execute/transaction.js';
|
|
17
|
+
export { ModelError, formatProblem, type LoraGraphQLErrorCode, type ModelProblem, } from './errors.js';
|
|
18
|
+
export { toGlobalId, fromGlobalId } from './schema/global-id.js';
|
|
19
|
+
export type { Attributes, MetricsLike, ObservabilityOptions, SpanLike, StatementEndEvent, TracerLike, } from './observe.js';
|
|
20
|
+
export { schemaHash, type ManifestOperation, type OperationManifest, } from './codegen.js';
|
|
21
|
+
export { envelopPlugin, parseOptions, validationRules, DEFAULT_GUARDS, type DocumentGuards, } from './guards.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { D as e, L as r, a as o, M as l, b as i, c as d, d as n, e as t, f, g as m, i as p, l as c, p as h, r as D, s as b, h as u, t as v, v as L } from "./driver-C8vA5fV-.js";
|
|
2
|
+
import { d as x } from "./diff-BVP1Jzh3.js";
|
|
3
|
+
export {
|
|
4
|
+
e as DEFAULT_GUARDS,
|
|
5
|
+
r as LoraGraphQL,
|
|
6
|
+
o as LoraTransaction,
|
|
7
|
+
l as ModelError,
|
|
8
|
+
i as buildModel,
|
|
9
|
+
d as checkPlans,
|
|
10
|
+
x as diffSchemas,
|
|
11
|
+
n as directiveTypeDefs,
|
|
12
|
+
t as envelopPlugin,
|
|
13
|
+
f as formatProblem,
|
|
14
|
+
m as fromGlobalId,
|
|
15
|
+
p as inferRequirements,
|
|
16
|
+
c as loraDriver,
|
|
17
|
+
h as parseOptions,
|
|
18
|
+
D as requirementDdl,
|
|
19
|
+
b as scanExpands,
|
|
20
|
+
u as schemaHash,
|
|
21
|
+
v as toGlobalId,
|
|
22
|
+
L as validationRules
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { DocumentNode, ExecutionResult, GraphQLSchema, GraphQLFieldResolver, GraphQLScalarType, ValidationRule } 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 { Statistics } from './analyze/statistics.js';
|
|
6
|
+
import { CompiledRead, ReadSet } from './compile/read.js';
|
|
7
|
+
import { LoraDriver, Statement } from './driver.js';
|
|
8
|
+
import { WriteChange } from './execute/changes.js';
|
|
9
|
+
import { LoraTransaction } from './execute/transaction.js';
|
|
10
|
+
import { PopulatedByCallback } from './execute/mutate.js';
|
|
11
|
+
import { OperationManifest } from './codegen.js';
|
|
12
|
+
import { envelopPlugin, DocumentGuards } from './guards.js';
|
|
13
|
+
import { ModelOptions } from './model/build.js';
|
|
14
|
+
import { ObservabilityOptions } from './observe.js';
|
|
15
|
+
import { GraphModel, ModelWarning } from './model/types.js';
|
|
16
|
+
export interface LoraGraphQLOptions extends ModelOptions, ObservabilityOptions {
|
|
17
|
+
/** Annotated SDL: the graph model and the API in one document. */
|
|
18
|
+
typeDefs: string | DocumentNode;
|
|
19
|
+
driver: LoraDriver;
|
|
20
|
+
/** Timeout for every statement, in milliseconds. Default 10 000; 0 disables. */
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Reject a root field whose estimated rows touched exceed this, before
|
|
24
|
+
* it runs. The estimate multiplies page sizes through nested lists,
|
|
25
|
+
* capped by relationship degrees from `analyze()` or `@cardinality`.
|
|
26
|
+
* Default 50 000; `Infinity` disables.
|
|
27
|
+
*/
|
|
28
|
+
maxCost?: number;
|
|
29
|
+
/**
|
|
30
|
+
* The cost limit for one request, from its context (for example by
|
|
31
|
+
* user or plan). Undefined falls back to `maxCost`.
|
|
32
|
+
*/
|
|
33
|
+
budget?: (context: unknown) => number | undefined;
|
|
34
|
+
/** Called with every root field's cost estimate, before it runs. */
|
|
35
|
+
onCost?: (event: CostEvent) => void;
|
|
36
|
+
/**
|
|
37
|
+
* The request's verified claims, from the GraphQL context. Default:
|
|
38
|
+
* `context.jwt`. The library never verifies tokens: do that in the
|
|
39
|
+
* server and put the claims in the context.
|
|
40
|
+
*/
|
|
41
|
+
jwt?: (context: unknown) => Record<string, unknown> | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Most nodes one mutation may create or delete, nested ones included.
|
|
44
|
+
* Default 1000: larger imports belong in a Cypher load, not a GraphQL
|
|
45
|
+
* request. Also the default `limit` of bulk updates and deletes.
|
|
46
|
+
*/
|
|
47
|
+
maxBatch?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Changes a `changes()` consumer or subscriber may fall behind before it
|
|
50
|
+
* is ended with an error. Default 1000.
|
|
51
|
+
*/
|
|
52
|
+
maxQueuedChanges?: number;
|
|
53
|
+
/** Named callbacks for `@populatedBy(callback:)`. */
|
|
54
|
+
callbacks?: Record<string, PopulatedByCallback>;
|
|
55
|
+
/**
|
|
56
|
+
* Implementations of custom scalars declared with `@storedAs`, e.g. one
|
|
57
|
+
* that validates e-mail addresses. Without one a scalar passes through.
|
|
58
|
+
*/
|
|
59
|
+
scalars?: Record<string, GraphQLScalarType>;
|
|
60
|
+
/**
|
|
61
|
+
* Feed subscriptions and `changes()` from the engine's committed change
|
|
62
|
+
* feed (lora-node `db.changes()`): writes from every path and process,
|
|
63
|
+
* in commit order. `onWrite` still reports this instance's mutations,
|
|
64
|
+
* and `previousState` needs them. Default false.
|
|
65
|
+
*/
|
|
66
|
+
changeFeed?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Resolvers of `@customResolver` fields, by type and field name. The
|
|
69
|
+
* source holds the node's selected fields plus the field's `requires`.
|
|
70
|
+
*/
|
|
71
|
+
resolvers?: Record<string, Record<string, GraphQLFieldResolver<Record<string, unknown>, unknown>>>;
|
|
72
|
+
/** Called with every statement before it runs; for logging and tests. */
|
|
73
|
+
onStatement?: (event: StatementEvent) => void;
|
|
74
|
+
/**
|
|
75
|
+
* Hide database error details from clients: they get
|
|
76
|
+
* `extensions.code` `DATABASE_ERROR` and an `id`, and `onError` gets the
|
|
77
|
+
* engine's message. Default: on when `NODE_ENV` is `production`.
|
|
78
|
+
*/
|
|
79
|
+
maskErrors?: boolean;
|
|
80
|
+
/** Called with every database error, masked or not. */
|
|
81
|
+
onError?: (event: DatabaseErrorEvent) => void;
|
|
82
|
+
/**
|
|
83
|
+
* Limits on the documents `execute()` and `persist()` accept: depth,
|
|
84
|
+
* aliases, root fields, tokens, introspection. `false` turns them off.
|
|
85
|
+
* For other servers use `validationRules()` or `envelopPlugin()`.
|
|
86
|
+
*/
|
|
87
|
+
guards?: DocumentGuards | false;
|
|
88
|
+
/**
|
|
89
|
+
* Make `execute()` refuse `source` and run only persisted operations
|
|
90
|
+
* by `id`. Default false.
|
|
91
|
+
*/
|
|
92
|
+
persistedOnly?: boolean;
|
|
93
|
+
}
|
|
94
|
+
export interface CostEvent {
|
|
95
|
+
field: string;
|
|
96
|
+
/** Estimated rows this root field touches. */
|
|
97
|
+
cost: number;
|
|
98
|
+
/** Estimated rows of the operation so far, this field included. */
|
|
99
|
+
total: number;
|
|
100
|
+
/** The limit that applies: `budget(context)` or `maxCost`. */
|
|
101
|
+
limit: number;
|
|
102
|
+
context: unknown;
|
|
103
|
+
}
|
|
104
|
+
export interface DatabaseErrorEvent {
|
|
105
|
+
/** Correlation id, also in the client error's `extensions.id`. */
|
|
106
|
+
id: string;
|
|
107
|
+
/** The root field that failed. */
|
|
108
|
+
field: string;
|
|
109
|
+
/** The engine's message, which may name labels, properties and Cypher. */
|
|
110
|
+
message: string;
|
|
111
|
+
/** The error as thrown by the driver. */
|
|
112
|
+
error: unknown;
|
|
113
|
+
}
|
|
114
|
+
export interface StatementEvent {
|
|
115
|
+
field: string;
|
|
116
|
+
statement: Statement;
|
|
117
|
+
}
|
|
118
|
+
/** Per-request options read from the GraphQL context, when present. */
|
|
119
|
+
export interface LoraGraphQLContext {
|
|
120
|
+
/** Verified claims; enables @authentication and @authorization. */
|
|
121
|
+
jwt?: Record<string, unknown>;
|
|
122
|
+
/** Cancels the request's statements when aborted. */
|
|
123
|
+
signal?: AbortSignal;
|
|
124
|
+
/**
|
|
125
|
+
* Run in this transaction (from `lora.begin()`) instead of one per
|
|
126
|
+
* field: the caller commits, together with its own Cypher.
|
|
127
|
+
*/
|
|
128
|
+
transaction?: LoraTransaction;
|
|
129
|
+
}
|
|
130
|
+
export interface AssertSchemaOptions {
|
|
131
|
+
/** Create what is missing instead of only reporting it. */
|
|
132
|
+
create?: boolean;
|
|
133
|
+
}
|
|
134
|
+
export interface SchemaAssertion {
|
|
135
|
+
required: SchemaRequirement[];
|
|
136
|
+
missing: SchemaRequirement[];
|
|
137
|
+
created: SchemaRequirement[];
|
|
138
|
+
}
|
|
139
|
+
export interface CheckOptions {
|
|
140
|
+
/**
|
|
141
|
+
* Flag statements whose largest engine row estimate (from graph
|
|
142
|
+
* statistics) exceeds this. Off by default: estimates ignore a LIMIT
|
|
143
|
+
* that stops an index-ordered scan early.
|
|
144
|
+
*/
|
|
145
|
+
rowBudget?: number;
|
|
146
|
+
/** Operations to compile and plan-check (S2), with example variables. */
|
|
147
|
+
operations?: Array<{
|
|
148
|
+
name?: string;
|
|
149
|
+
document: string | DocumentNode;
|
|
150
|
+
variables?: Record<string, unknown>;
|
|
151
|
+
}>;
|
|
152
|
+
}
|
|
153
|
+
export interface CheckReport {
|
|
154
|
+
/** True when nothing below is a failure. */
|
|
155
|
+
ok: boolean;
|
|
156
|
+
/** Model warnings, e.g. an unused @cypher argument. */
|
|
157
|
+
warnings: readonly ModelWarning[];
|
|
158
|
+
/** @cypher statements the engine rejects or that write from a query. */
|
|
159
|
+
cypher: CypherFinding[];
|
|
160
|
+
/** Constraints and indexes the database lacks. */
|
|
161
|
+
missing: SchemaRequirement[];
|
|
162
|
+
/** Indexes the database has that no part of the API needs. */
|
|
163
|
+
unused: Array<{
|
|
164
|
+
name: string;
|
|
165
|
+
type: string;
|
|
166
|
+
labels: string[];
|
|
167
|
+
properties: string[];
|
|
168
|
+
}>;
|
|
169
|
+
/** Schema lint: valid but costly or risky choices (not failures). */
|
|
170
|
+
lint: readonly ModelWarning[];
|
|
171
|
+
/** Plan findings per operation and root field. */
|
|
172
|
+
plans: Array<{
|
|
173
|
+
operation: string;
|
|
174
|
+
field: string;
|
|
175
|
+
reports: PlanReport[];
|
|
176
|
+
}>;
|
|
177
|
+
/** Operations that failed to compile. */
|
|
178
|
+
errors: Array<{
|
|
179
|
+
operation: string;
|
|
180
|
+
message: string;
|
|
181
|
+
}>;
|
|
182
|
+
}
|
|
183
|
+
export interface ExecuteArgs {
|
|
184
|
+
/** The document, or `id` of a persisted operation. */
|
|
185
|
+
source?: string;
|
|
186
|
+
id?: string;
|
|
187
|
+
variables?: Record<string, unknown>;
|
|
188
|
+
operationName?: string;
|
|
189
|
+
context?: unknown;
|
|
190
|
+
}
|
|
191
|
+
export declare class LoraGraphQL {
|
|
192
|
+
#private;
|
|
193
|
+
readonly model: GraphModel;
|
|
194
|
+
constructor(options: LoraGraphQLOptions);
|
|
195
|
+
/** The configured document guards as validation rules. */
|
|
196
|
+
validationRules(): ValidationRule[];
|
|
197
|
+
/** The configured document guards as an Envelop / Yoga plugin. */
|
|
198
|
+
envelopPlugin(): ReturnType<typeof envelopPlugin>;
|
|
199
|
+
/** The executable schema, for any graphql-js server. */
|
|
200
|
+
getSchema(): GraphQLSchema;
|
|
201
|
+
/** The client-facing SDL: generated types only, no model directives. */
|
|
202
|
+
printPublicSchema(): string;
|
|
203
|
+
/** Constraints and indexes the API needs (S1), with the reason for each. */
|
|
204
|
+
requirements(): SchemaRequirement[];
|
|
205
|
+
/**
|
|
206
|
+
* Verify the database has every constraint and index the API needs;
|
|
207
|
+
* with `create`, add what is missing. Idempotent. Runs one DDL
|
|
208
|
+
* statement at a time: LoraDB rejects schema commands in transactions.
|
|
209
|
+
*/
|
|
210
|
+
assertSchema(options?: AssertSchemaOptions): Promise<SchemaAssertion>;
|
|
211
|
+
/**
|
|
212
|
+
* Sample node counts and relationship degrees (S6). Cost estimates then
|
|
213
|
+
* use each relationship's p99 degree instead of its page limit.
|
|
214
|
+
*/
|
|
215
|
+
analyze(options?: {
|
|
216
|
+
sample?: number;
|
|
217
|
+
}): Promise<Statistics>;
|
|
218
|
+
/** Use statistics gathered earlier, e.g. by the CLI. */
|
|
219
|
+
useStatistics(stats: Statistics): void;
|
|
220
|
+
get statistics(): Statistics | undefined;
|
|
221
|
+
/**
|
|
222
|
+
* The CI gate: @cypher statements plan, the database has what the API
|
|
223
|
+
* needs, and each operation's statements seek where they should.
|
|
224
|
+
*/
|
|
225
|
+
check(options?: CheckOptions): Promise<CheckReport>;
|
|
226
|
+
/**
|
|
227
|
+
* Compile a query without running it: one entry per root field, with its
|
|
228
|
+
* statements, parameters, read-set, cost and expected access path.
|
|
229
|
+
*/
|
|
230
|
+
compile(document: string | DocumentNode, variables?: Record<string, unknown>, options?: {
|
|
231
|
+
operationName?: string;
|
|
232
|
+
context?: unknown;
|
|
233
|
+
}): Array<{
|
|
234
|
+
field: string;
|
|
235
|
+
compiled: CompiledRead;
|
|
236
|
+
}>;
|
|
237
|
+
/** Compile a query and check every statement's plan (S2). */
|
|
238
|
+
explain(document: string | DocumentNode, variables?: Record<string, unknown>, options?: {
|
|
239
|
+
operationName?: string;
|
|
240
|
+
context?: unknown;
|
|
241
|
+
rowBudget?: number | undefined;
|
|
242
|
+
}): Promise<Array<{
|
|
243
|
+
field: string;
|
|
244
|
+
reports: PlanReport[];
|
|
245
|
+
}>>;
|
|
246
|
+
/**
|
|
247
|
+
* Register persisted operations by id. Every document is parsed and
|
|
248
|
+
* validated now, so a broken one fails at startup; at request time an
|
|
249
|
+
* id is looked up and executed without parsing or validating.
|
|
250
|
+
*/
|
|
251
|
+
persist(operations: Record<string, string>): void;
|
|
252
|
+
/**
|
|
253
|
+
* Validate persisted operations (id → source) into a manifest for
|
|
254
|
+
* `loadManifest()`, built once at build time (`lora-graphql compile`).
|
|
255
|
+
*/
|
|
256
|
+
buildManifest(operations: Record<string, string>): OperationManifest;
|
|
257
|
+
/** TypeScript types for a manifest's variables and results. */
|
|
258
|
+
generateTypes(manifest: OperationManifest): string;
|
|
259
|
+
/**
|
|
260
|
+
* Register a manifest's operations as persisted operations, without
|
|
261
|
+
* parsing or validating them again. Refused when the manifest was built
|
|
262
|
+
* for another schema.
|
|
263
|
+
*/
|
|
264
|
+
loadManifest(manifest: OperationManifest): void;
|
|
265
|
+
/**
|
|
266
|
+
* Execute an operation against the schema. Parsed and validated
|
|
267
|
+
* documents are cached by source text; persisted ones by id.
|
|
268
|
+
*/
|
|
269
|
+
execute(args: ExecuteArgs): Promise<ExecutionResult>;
|
|
270
|
+
/** Called after every committed mutation with its exact write-set. */
|
|
271
|
+
onWrite(listener: (change: WriteChange) => void): () => void;
|
|
272
|
+
/**
|
|
273
|
+
* Committed writes as an async iterator, e.g. for a subscription
|
|
274
|
+
* resolver. Only writes made through this instance are seen.
|
|
275
|
+
*/
|
|
276
|
+
changes(options?: {
|
|
277
|
+
signal?: AbortSignal;
|
|
278
|
+
maxQueued?: number;
|
|
279
|
+
}): AsyncIterableIterator<WriteChange>;
|
|
280
|
+
/** Whether a read with this read-set may observe the change. */
|
|
281
|
+
affects(reads: ReadSet, change: WriteChange): boolean;
|
|
282
|
+
/**
|
|
283
|
+
* Open a transaction the caller owns. Put it in the GraphQL context as
|
|
284
|
+
* `transaction`: every operation of those requests runs in it, next to
|
|
285
|
+
* the application's own `tx.execute(cypher)`. Nothing is visible to
|
|
286
|
+
* others, and no change event fires, until `tx.commit()`.
|
|
287
|
+
*/
|
|
288
|
+
begin(): Promise<LoraTransaction>;
|
|
289
|
+
/** Stop the engine change feed (with `changeFeed: true`). */
|
|
290
|
+
close(): void;
|
|
291
|
+
}
|