@eleven-am/golem-client 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +10 -2
- package/dist/analysis.d.ts +40 -0
- package/dist/analysis.js +471 -0
- package/dist/client.d.ts +49 -0
- package/dist/client.js +77 -0
- package/dist/coalescer.d.ts +14 -0
- package/dist/coalescer.js +37 -0
- package/dist/errors.d.ts +84 -0
- package/dist/errors.js +143 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +13 -0
- package/dist/live.d.ts +112 -0
- package/dist/live.js +583 -0
- package/dist/manifest.d.ts +26 -0
- package/dist/manifest.js +1 -0
- package/dist/merge.d.ts +41 -0
- package/dist/merge.js +62 -0
- package/dist/mutations.d.ts +20 -0
- package/dist/mutations.js +42 -0
- package/dist/pagination.d.ts +32 -0
- package/dist/pagination.js +133 -0
- package/dist/patcher.d.ts +26 -0
- package/dist/patcher.js +186 -0
- package/dist/protocol.d.ts +56 -0
- package/dist/protocol.js +109 -0
- package/dist/session.d.ts +61 -0
- package/dist/session.js +188 -0
- package/dist/transport.d.ts +27 -0
- package/dist/transport.js +62 -0
- package/package.json +47 -4
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export const COALESCE_WINDOW_MS = 50;
|
|
2
|
+
export const COALESCE_MAX_WAIT_MS = 500;
|
|
3
|
+
export class Coalescer {
|
|
4
|
+
fire;
|
|
5
|
+
window;
|
|
6
|
+
maxWait;
|
|
7
|
+
timer;
|
|
8
|
+
firstRequestAt;
|
|
9
|
+
constructor(fire, window = COALESCE_WINDOW_MS, maxWait = COALESCE_MAX_WAIT_MS) {
|
|
10
|
+
this.fire = fire;
|
|
11
|
+
this.window = window;
|
|
12
|
+
this.maxWait = maxWait;
|
|
13
|
+
}
|
|
14
|
+
get pending() {
|
|
15
|
+
return this.timer !== undefined;
|
|
16
|
+
}
|
|
17
|
+
request() {
|
|
18
|
+
const now = Date.now();
|
|
19
|
+
this.firstRequestAt ??= now;
|
|
20
|
+
if (this.timer !== undefined) {
|
|
21
|
+
clearTimeout(this.timer);
|
|
22
|
+
}
|
|
23
|
+
const remaining = this.firstRequestAt + this.maxWait - now;
|
|
24
|
+
this.timer = setTimeout(() => this.flush(), Math.max(0, Math.min(this.window, remaining)));
|
|
25
|
+
}
|
|
26
|
+
flush() {
|
|
27
|
+
this.cancel();
|
|
28
|
+
this.fire();
|
|
29
|
+
}
|
|
30
|
+
cancel() {
|
|
31
|
+
if (this.timer !== undefined) {
|
|
32
|
+
clearTimeout(this.timer);
|
|
33
|
+
}
|
|
34
|
+
this.timer = undefined;
|
|
35
|
+
this.firstRequestAt = undefined;
|
|
36
|
+
}
|
|
37
|
+
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
export type PathSegment = string | number;
|
|
2
|
+
export interface GraphQLErrorShape {
|
|
3
|
+
readonly message: string;
|
|
4
|
+
readonly path?: ReadonlyArray<PathSegment>;
|
|
5
|
+
readonly locations?: ReadonlyArray<{
|
|
6
|
+
readonly line: number;
|
|
7
|
+
readonly column: number;
|
|
8
|
+
}>;
|
|
9
|
+
readonly extensions?: Readonly<Record<string, unknown>>;
|
|
10
|
+
}
|
|
11
|
+
export type CommitState = 'PARTIAL' | 'UNKNOWN';
|
|
12
|
+
export declare const CODE_PROTOCOL_MISMATCH = "GOLEM_PROTOCOL_MISMATCH";
|
|
13
|
+
export declare const CODE_CONTRACT_MISMATCH = "GOLEM_CONTRACT_MISMATCH";
|
|
14
|
+
export declare const CODE_DEADLINE_REQUIRED = "GOLEM_PRINCIPAL_DEADLINE_REQUIRED";
|
|
15
|
+
export declare const CODE_UNCODED = "GOLEM_UNCODED";
|
|
16
|
+
export declare class GolemError extends Error {
|
|
17
|
+
readonly code: string;
|
|
18
|
+
readonly path: ReadonlyArray<PathSegment> | undefined;
|
|
19
|
+
readonly extensions: Readonly<Record<string, unknown>>;
|
|
20
|
+
readonly retryable: boolean;
|
|
21
|
+
constructor(shape: GraphQLErrorShape, retryable?: boolean);
|
|
22
|
+
}
|
|
23
|
+
export declare class GolemProtocolMismatchError extends GolemError {
|
|
24
|
+
constructor(shape: GraphQLErrorShape);
|
|
25
|
+
}
|
|
26
|
+
export declare class GolemContractMismatchError extends GolemError {
|
|
27
|
+
constructor(shape: GraphQLErrorShape);
|
|
28
|
+
}
|
|
29
|
+
export declare class GolemDeadlineRequiredError extends GolemError {
|
|
30
|
+
constructor(shape: GraphQLErrorShape);
|
|
31
|
+
}
|
|
32
|
+
export declare class GolemUnauthenticatedError extends GolemError {
|
|
33
|
+
constructor(message: string);
|
|
34
|
+
}
|
|
35
|
+
export declare class GolemHttpError extends GolemError {
|
|
36
|
+
readonly status: number;
|
|
37
|
+
constructor(status: number, message: string);
|
|
38
|
+
}
|
|
39
|
+
export declare abstract class GolemCommitError extends GolemError {
|
|
40
|
+
readonly errors: ReadonlyArray<GolemError>;
|
|
41
|
+
readonly data: unknown;
|
|
42
|
+
constructor(first: GolemError, errors: ReadonlyArray<GolemError>, data: unknown);
|
|
43
|
+
}
|
|
44
|
+
export declare class GolemPartialCommitError extends GolemCommitError {
|
|
45
|
+
}
|
|
46
|
+
export declare class GolemCommitUnknownError extends GolemCommitError {
|
|
47
|
+
}
|
|
48
|
+
export declare class GolemProtocolError extends Error {
|
|
49
|
+
constructor(message: string);
|
|
50
|
+
}
|
|
51
|
+
export declare class GolemConfigurationError extends Error {
|
|
52
|
+
constructor(message: string);
|
|
53
|
+
}
|
|
54
|
+
export type NotLiveReason = 'model-without-events' | 'opaque-root-without-models' | 'hidden-inverse' | 'non-row-root';
|
|
55
|
+
export declare class GolemNotLiveError extends Error {
|
|
56
|
+
readonly reason: NotLiveReason;
|
|
57
|
+
readonly subject: string;
|
|
58
|
+
constructor(reason: NotLiveReason, subject: string, message: string);
|
|
59
|
+
}
|
|
60
|
+
export declare class GolemNotFoundError extends Error {
|
|
61
|
+
readonly subjects: ReadonlyArray<string>;
|
|
62
|
+
constructor(subjects: ReadonlyArray<string>);
|
|
63
|
+
}
|
|
64
|
+
export declare class GolemDocumentError extends Error {
|
|
65
|
+
constructor(message: string);
|
|
66
|
+
}
|
|
67
|
+
export declare class GolemMultipleOperationsError extends GolemDocumentError {
|
|
68
|
+
readonly operations: number;
|
|
69
|
+
constructor(operations: number);
|
|
70
|
+
}
|
|
71
|
+
export declare function toGolemError(shape: GraphQLErrorShape): GolemError;
|
|
72
|
+
export interface GraphQLResponseBody {
|
|
73
|
+
readonly data?: unknown;
|
|
74
|
+
readonly errors?: ReadonlyArray<GraphQLErrorShape>;
|
|
75
|
+
readonly extensions?: {
|
|
76
|
+
readonly golem?: {
|
|
77
|
+
readonly commit?: unknown;
|
|
78
|
+
readonly live?: unknown;
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export declare function commitState(body: GraphQLResponseBody): CommitState | undefined;
|
|
83
|
+
export declare function errorsOf(body: GraphQLResponseBody): GolemError[];
|
|
84
|
+
export declare function failureOf(body: GraphQLResponseBody): GolemError | undefined;
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
export const CODE_PROTOCOL_MISMATCH = 'GOLEM_PROTOCOL_MISMATCH';
|
|
2
|
+
export const CODE_CONTRACT_MISMATCH = 'GOLEM_CONTRACT_MISMATCH';
|
|
3
|
+
export const CODE_DEADLINE_REQUIRED = 'GOLEM_PRINCIPAL_DEADLINE_REQUIRED';
|
|
4
|
+
export const CODE_UNCODED = 'GOLEM_UNCODED';
|
|
5
|
+
export class GolemError extends Error {
|
|
6
|
+
code;
|
|
7
|
+
path;
|
|
8
|
+
extensions;
|
|
9
|
+
retryable;
|
|
10
|
+
constructor(shape, retryable = true) {
|
|
11
|
+
super(shape.message);
|
|
12
|
+
this.name = new.target.name;
|
|
13
|
+
const extensions = shape.extensions ?? {};
|
|
14
|
+
this.code = typeof extensions.code === 'string' ? extensions.code : CODE_UNCODED;
|
|
15
|
+
this.path = shape.path;
|
|
16
|
+
this.extensions = extensions;
|
|
17
|
+
this.retryable = retryable;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export class GolemProtocolMismatchError extends GolemError {
|
|
21
|
+
constructor(shape) {
|
|
22
|
+
super(shape, false);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export class GolemContractMismatchError extends GolemError {
|
|
26
|
+
constructor(shape) {
|
|
27
|
+
super(shape, false);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export class GolemDeadlineRequiredError extends GolemError {
|
|
31
|
+
constructor(shape) {
|
|
32
|
+
super(shape, false);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export class GolemUnauthenticatedError extends GolemError {
|
|
36
|
+
constructor(message) {
|
|
37
|
+
super({ message, extensions: { code: 'GOLEM_UNAUTHENTICATED' } }, false);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export class GolemHttpError extends GolemError {
|
|
41
|
+
status;
|
|
42
|
+
constructor(status, message) {
|
|
43
|
+
super({ message, extensions: { code: `GOLEM_HTTP_${status}` } });
|
|
44
|
+
this.status = status;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export class GolemCommitError extends GolemError {
|
|
48
|
+
errors;
|
|
49
|
+
data;
|
|
50
|
+
constructor(first, errors, data) {
|
|
51
|
+
super({ message: first.message, path: first.path, extensions: first.extensions }, false);
|
|
52
|
+
this.errors = errors;
|
|
53
|
+
this.data = data;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export class GolemPartialCommitError extends GolemCommitError {
|
|
57
|
+
}
|
|
58
|
+
export class GolemCommitUnknownError extends GolemCommitError {
|
|
59
|
+
}
|
|
60
|
+
export class GolemProtocolError extends Error {
|
|
61
|
+
constructor(message) {
|
|
62
|
+
super(message);
|
|
63
|
+
this.name = 'GolemProtocolError';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export class GolemConfigurationError extends Error {
|
|
67
|
+
constructor(message) {
|
|
68
|
+
super(message);
|
|
69
|
+
this.name = 'GolemConfigurationError';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export class GolemNotLiveError extends Error {
|
|
73
|
+
reason;
|
|
74
|
+
subject;
|
|
75
|
+
constructor(reason, subject, message) {
|
|
76
|
+
super(message);
|
|
77
|
+
this.name = 'GolemNotLiveError';
|
|
78
|
+
this.reason = reason;
|
|
79
|
+
this.subject = subject;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export class GolemNotFoundError extends Error {
|
|
83
|
+
subjects;
|
|
84
|
+
constructor(subjects) {
|
|
85
|
+
super(`a row held in a single result slot left the result: ${subjects.join(', ')}`);
|
|
86
|
+
this.name = 'GolemNotFoundError';
|
|
87
|
+
this.subjects = subjects;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export class GolemDocumentError extends Error {
|
|
91
|
+
constructor(message) {
|
|
92
|
+
super(message);
|
|
93
|
+
this.name = 'GolemDocumentError';
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
export class GolemMultipleOperationsError extends GolemDocumentError {
|
|
97
|
+
operations;
|
|
98
|
+
constructor(operations) {
|
|
99
|
+
super(`the document holds ${operations} operations; golem takes exactly one operation per document`);
|
|
100
|
+
this.name = 'GolemMultipleOperationsError';
|
|
101
|
+
this.operations = operations;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
export function toGolemError(shape) {
|
|
105
|
+
switch (shape.extensions?.code) {
|
|
106
|
+
case CODE_PROTOCOL_MISMATCH:
|
|
107
|
+
return new GolemProtocolMismatchError(shape);
|
|
108
|
+
case CODE_CONTRACT_MISMATCH:
|
|
109
|
+
return new GolemContractMismatchError(shape);
|
|
110
|
+
case CODE_DEADLINE_REQUIRED:
|
|
111
|
+
return new GolemDeadlineRequiredError(shape);
|
|
112
|
+
default:
|
|
113
|
+
return new GolemError(shape);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
export function commitState(body) {
|
|
117
|
+
const commit = body.extensions?.golem?.commit;
|
|
118
|
+
if (commit === undefined) {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
if (commit === 'PARTIAL' || commit === 'UNKNOWN') {
|
|
122
|
+
return commit;
|
|
123
|
+
}
|
|
124
|
+
throw new GolemProtocolError(`unrecognised extensions.golem.commit value ${JSON.stringify(commit)}`);
|
|
125
|
+
}
|
|
126
|
+
export function errorsOf(body) {
|
|
127
|
+
return (body.errors ?? []).map(toGolemError);
|
|
128
|
+
}
|
|
129
|
+
export function failureOf(body) {
|
|
130
|
+
const errors = errorsOf(body);
|
|
131
|
+
const first = errors[0];
|
|
132
|
+
if (first === undefined) {
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
switch (commitState(body)) {
|
|
136
|
+
case 'PARTIAL':
|
|
137
|
+
return new GolemPartialCommitError(first, errors, body.data);
|
|
138
|
+
case 'UNKNOWN':
|
|
139
|
+
return new GolemCommitUnknownError(first, errors, body.data);
|
|
140
|
+
default:
|
|
141
|
+
return first;
|
|
142
|
+
}
|
|
143
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from './errors.js';
|
|
2
|
+
export * from './protocol.js';
|
|
3
|
+
export * from './session.js';
|
|
4
|
+
export * from './transport.js';
|
|
5
|
+
export * from './manifest.js';
|
|
6
|
+
export * from './analysis.js';
|
|
7
|
+
export * from './merge.js';
|
|
8
|
+
export * from './patcher.js';
|
|
9
|
+
export * from './coalescer.js';
|
|
10
|
+
export * from './live.js';
|
|
11
|
+
export * from './mutations.js';
|
|
12
|
+
export * from './pagination.js';
|
|
13
|
+
export * from './client.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from './errors.js';
|
|
2
|
+
export * from './protocol.js';
|
|
3
|
+
export * from './session.js';
|
|
4
|
+
export * from './transport.js';
|
|
5
|
+
export * from './manifest.js';
|
|
6
|
+
export * from './analysis.js';
|
|
7
|
+
export * from './merge.js';
|
|
8
|
+
export * from './patcher.js';
|
|
9
|
+
export * from './coalescer.js';
|
|
10
|
+
export * from './live.js';
|
|
11
|
+
export * from './mutations.js';
|
|
12
|
+
export * from './pagination.js';
|
|
13
|
+
export * from './client.js';
|
package/dist/live.d.ts
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { Query, QueryClient, QueryFunctionContext } from '@tanstack/query-core';
|
|
2
|
+
import { type DocumentAnalysis } from './analysis.js';
|
|
3
|
+
import type { ClientManifest } from './manifest.js';
|
|
4
|
+
import { type Cursor, type PageSpec } from './pagination.js';
|
|
5
|
+
import type { StreamHandlers, SubscriptionRequest } from './session.js';
|
|
6
|
+
import type { ExecuteRequest } from './transport.js';
|
|
7
|
+
export declare const CLOSE_GRACE_MS = 1000;
|
|
8
|
+
export declare const STABLE_STREAM_MS = 10000;
|
|
9
|
+
export declare const SubscriptionCode: {
|
|
10
|
+
readonly Cancelled: "GOLEM_SUBSCRIPTION_CANCELLED";
|
|
11
|
+
readonly Resync: "GOLEM_SUBSCRIPTION_RESYNC";
|
|
12
|
+
readonly Overflow: "GOLEM_SUBSCRIPTION_OVERFLOW";
|
|
13
|
+
readonly Revalidation: "GOLEM_SUBSCRIPTION_REVALIDATION";
|
|
14
|
+
readonly Forbidden: "GOLEM_SUBSCRIPTION_FORBIDDEN";
|
|
15
|
+
readonly Limit: "GOLEM_SUBSCRIPTION_LIMIT";
|
|
16
|
+
readonly SourceClosed: "GOLEM_SUBSCRIPTION_SOURCE_CLOSED";
|
|
17
|
+
};
|
|
18
|
+
export declare const REASON_DEPENDENCY_CHANGED = "DEPENDENCY_CHANGED";
|
|
19
|
+
export type LiveState = 'CONNECTING' | 'LIVE' | 'RECOVERING' | 'LIMIT' | 'FORBIDDEN' | 'NOT_FOUND' | 'FAILED';
|
|
20
|
+
export interface LiveStatus {
|
|
21
|
+
readonly state: LiveState;
|
|
22
|
+
readonly error: Error | undefined;
|
|
23
|
+
}
|
|
24
|
+
export interface GolemQueryMeta {
|
|
25
|
+
readonly analysis: DocumentAnalysis;
|
|
26
|
+
readonly variables: Readonly<Record<string, unknown>>;
|
|
27
|
+
readonly pagination?: PageSpec;
|
|
28
|
+
}
|
|
29
|
+
export interface LiveTransport {
|
|
30
|
+
execute<T = unknown>(request: ExecuteRequest): Promise<T>;
|
|
31
|
+
subscribe(request: SubscriptionRequest, handlers: StreamHandlers): () => void;
|
|
32
|
+
renew(): void;
|
|
33
|
+
}
|
|
34
|
+
export interface LiveSyncOptions {
|
|
35
|
+
readonly queryClient: QueryClient;
|
|
36
|
+
readonly transport: LiveTransport;
|
|
37
|
+
readonly manifest: ClientManifest;
|
|
38
|
+
readonly clock?: () => number;
|
|
39
|
+
readonly closeGraceMs?: number;
|
|
40
|
+
readonly windowMs?: number;
|
|
41
|
+
readonly maxWaitMs?: number;
|
|
42
|
+
readonly random?: () => number;
|
|
43
|
+
}
|
|
44
|
+
export declare function readMeta(query: Query): GolemQueryMeta | undefined;
|
|
45
|
+
export declare class LiveSync {
|
|
46
|
+
private readonly client;
|
|
47
|
+
private readonly transport;
|
|
48
|
+
private readonly patcher;
|
|
49
|
+
private readonly manifest;
|
|
50
|
+
private readonly clock;
|
|
51
|
+
private readonly closeGraceMs;
|
|
52
|
+
private readonly windowMs;
|
|
53
|
+
private readonly maxWaitMs;
|
|
54
|
+
private readonly random;
|
|
55
|
+
private readonly entries;
|
|
56
|
+
private readonly statuses;
|
|
57
|
+
private readonly listeners;
|
|
58
|
+
private unsubscribe;
|
|
59
|
+
constructor(options: LiveSyncOptions);
|
|
60
|
+
get started(): boolean;
|
|
61
|
+
start(): () => void;
|
|
62
|
+
stop(): void;
|
|
63
|
+
options(analysis: DocumentAnalysis, variables?: Readonly<Record<string, unknown>>): {
|
|
64
|
+
queryKey: readonly ["golem", string, Readonly<Record<string, unknown>>];
|
|
65
|
+
queryFn: (context: QueryFunctionContext) => Promise<unknown>;
|
|
66
|
+
meta: {
|
|
67
|
+
golem: GolemQueryMeta;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
infiniteOptions(analysis: DocumentAnalysis, variables?: Readonly<Record<string, unknown>>): {
|
|
71
|
+
queryKey: readonly ["golem", string, Readonly<Record<string, unknown>>, "infinite"];
|
|
72
|
+
queryFn: (context: QueryFunctionContext<readonly unknown[], Cursor | null>) => Promise<unknown>;
|
|
73
|
+
initialPageParam: Readonly<Record<string, unknown>> | null;
|
|
74
|
+
getNextPageParam: (lastPage: unknown) => Readonly<Record<string, unknown>> | undefined;
|
|
75
|
+
meta: {
|
|
76
|
+
golem: GolemQueryMeta;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
statusOf(query: Query): LiveStatus | undefined;
|
|
80
|
+
subscribeStatus(listener: () => void): () => void;
|
|
81
|
+
private onCacheEvent;
|
|
82
|
+
private reconcileHold;
|
|
83
|
+
private openEntry;
|
|
84
|
+
private closeEntry;
|
|
85
|
+
private stopStream;
|
|
86
|
+
private openStream;
|
|
87
|
+
private guarded;
|
|
88
|
+
private fail;
|
|
89
|
+
private settled;
|
|
90
|
+
private onAck;
|
|
91
|
+
private afterSettle;
|
|
92
|
+
private releaseWaiters;
|
|
93
|
+
private onEnd;
|
|
94
|
+
private onStreamError;
|
|
95
|
+
private endStream;
|
|
96
|
+
private endFatal;
|
|
97
|
+
private backoff;
|
|
98
|
+
private reopen;
|
|
99
|
+
private retryLimited;
|
|
100
|
+
private statusFor;
|
|
101
|
+
private publish;
|
|
102
|
+
private notify;
|
|
103
|
+
private onEvent;
|
|
104
|
+
private markNotFound;
|
|
105
|
+
private clearNotFound;
|
|
106
|
+
private write;
|
|
107
|
+
private refetch;
|
|
108
|
+
private refetchNow;
|
|
109
|
+
private gate;
|
|
110
|
+
private fetch;
|
|
111
|
+
private reconcile;
|
|
112
|
+
}
|