@apibara/protocol 0.4.8 → 2.0.0-beta.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/CHANGELOG.md +103 -0
- package/buf.gen.yaml +14 -0
- package/build.config.ts +8 -0
- package/dist/index.d.mts +610 -0
- package/dist/index.d.ts +610 -4
- package/dist/index.mjs +1172 -0
- package/package.json +45 -24
- package/proto/common.proto +22 -0
- package/proto/stream.proto +83 -0
- package/proto/testing.proto +11 -0
- package/src/client.ts +110 -309
- package/src/common.test.ts +67 -0
- package/src/common.ts +65 -0
- package/src/config.ts +38 -0
- package/src/index.ts +11 -4
- package/src/proto/common.ts +279 -0
- package/src/proto/index.ts +3 -1
- package/src/proto/stream.ts +728 -17
- package/src/proto/testing.ts +143 -8
- package/src/rate.ts +54 -0
- package/src/status.test.ts +51 -0
- package/src/status.ts +22 -0
- package/src/stream.test-d.ts +33 -0
- package/src/stream.test.ts +238 -0
- package/src/stream.ts +128 -0
- package/src/testing/client.test.ts +71 -0
- package/src/testing/client.ts +49 -0
- package/src/testing/index.ts +2 -0
- package/src/testing/mock.test.ts +35 -0
- package/src/testing/mock.ts +71 -0
- package/tsconfig.json +12 -0
- package/dist/client.d.ts +0 -130
- package/dist/client.js +0 -203
- package/dist/client.js.map +0 -1
- package/dist/cursor.d.ts +0 -35
- package/dist/cursor.js +0 -67
- package/dist/cursor.js.map +0 -1
- package/dist/cursor.test.d.ts +0 -1
- package/dist/cursor.test.js +0 -22
- package/dist/cursor.test.js.map +0 -1
- package/dist/index.js +0 -21
- package/dist/index.js.map +0 -1
- package/dist/proto/apibara/node/v1alpha2/Cursor.d.ts +0 -10
- package/dist/proto/apibara/node/v1alpha2/Cursor.js +0 -4
- package/dist/proto/apibara/node/v1alpha2/Cursor.js.map +0 -1
- package/dist/proto/apibara/node/v1alpha2/Data.d.ts +0 -15
- package/dist/proto/apibara/node/v1alpha2/Data.js +0 -4
- package/dist/proto/apibara/node/v1alpha2/Data.js.map +0 -1
- package/dist/proto/apibara/node/v1alpha2/DataFinality.d.ts +0 -6
- package/dist/proto/apibara/node/v1alpha2/DataFinality.js +0 -12
- package/dist/proto/apibara/node/v1alpha2/DataFinality.js.map +0 -1
- package/dist/proto/apibara/node/v1alpha2/Heartbeat.d.ts +0 -4
- package/dist/proto/apibara/node/v1alpha2/Heartbeat.js +0 -4
- package/dist/proto/apibara/node/v1alpha2/Heartbeat.js.map +0 -1
- package/dist/proto/apibara/node/v1alpha2/Invalidate.d.ts +0 -7
- package/dist/proto/apibara/node/v1alpha2/Invalidate.js +0 -4
- package/dist/proto/apibara/node/v1alpha2/Invalidate.js.map +0 -1
- package/dist/proto/apibara/node/v1alpha2/Stream.d.ts +0 -16
- package/dist/proto/apibara/node/v1alpha2/Stream.js +0 -4
- package/dist/proto/apibara/node/v1alpha2/Stream.js.map +0 -1
- package/dist/proto/apibara/node/v1alpha2/StreamDataRequest.d.ts +0 -24
- package/dist/proto/apibara/node/v1alpha2/StreamDataRequest.js +0 -4
- package/dist/proto/apibara/node/v1alpha2/StreamDataRequest.js.map +0 -1
- package/dist/proto/apibara/node/v1alpha2/StreamDataResponse.d.ts +0 -18
- package/dist/proto/apibara/node/v1alpha2/StreamDataResponse.js +0 -4
- package/dist/proto/apibara/node/v1alpha2/StreamDataResponse.js.map +0 -1
- package/dist/proto/index.d.ts +0 -1
- package/dist/proto/index.js +0 -28
- package/dist/proto/index.js.map +0 -1
- package/dist/proto/stream.d.ts +0 -25
- package/dist/proto/stream.js +0 -3
- package/dist/proto/stream.js.map +0 -1
- package/dist/proto/stream.proto +0 -76
- package/dist/proto/testing.d.ts +0 -5
- package/dist/proto/testing.js +0 -22
- package/dist/proto/testing.js.map +0 -1
- package/dist/proto/v1alpha2.d.ts +0 -11
- package/dist/proto/v1alpha2.js +0 -15
- package/dist/proto/v1alpha2.js.map +0 -1
- package/dist/request.d.ts +0 -36
- package/dist/request.js +0 -57
- package/dist/request.js.map +0 -1
- package/dist/request.test.d.ts +0 -1
- package/dist/request.test.js +0 -29
- package/dist/request.test.js.map +0 -1
- package/src/cursor.test.ts +0 -20
- package/src/cursor.ts +0 -65
- package/src/proto/apibara/node/v1alpha2/Cursor.ts +0 -13
- package/src/proto/apibara/node/v1alpha2/Data.ts +0 -18
- package/src/proto/apibara/node/v1alpha2/DataFinality.ts +0 -8
- package/src/proto/apibara/node/v1alpha2/Heartbeat.ts +0 -8
- package/src/proto/apibara/node/v1alpha2/Invalidate.ts +0 -11
- package/src/proto/apibara/node/v1alpha2/Stream.ts +0 -23
- package/src/proto/apibara/node/v1alpha2/StreamDataRequest.ts +0 -27
- package/src/proto/apibara/node/v1alpha2/StreamDataResponse.ts +0 -22
- package/src/proto/stream.proto +0 -76
- package/src/proto/v1alpha2.ts +0 -22
- package/src/request.test.ts +0 -31
- package/src/request.ts +0 -64
package/src/proto/testing.ts
CHANGED
|
@@ -1,11 +1,146 @@
|
|
|
1
|
-
|
|
1
|
+
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
|
2
|
+
// versions:
|
|
3
|
+
// protoc-gen-ts_proto v1.176.0
|
|
4
|
+
// protoc unknown
|
|
5
|
+
// source: testing.proto
|
|
2
6
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
// @ts-ignore
|
|
6
|
-
public num: number
|
|
7
|
+
/* eslint-disable */
|
|
8
|
+
import _m0 from "protobufjs/minimal";
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
export const protobufPackage = "dna.v2.testing";
|
|
11
|
+
|
|
12
|
+
export interface MockFilter {
|
|
13
|
+
readonly filter?: string | undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface MockBlock {
|
|
17
|
+
readonly data?: string | undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function createBaseMockFilter(): MockFilter {
|
|
21
|
+
return { filter: undefined };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const MockFilter = {
|
|
25
|
+
encode(message: MockFilter, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
|
|
26
|
+
if (message.filter !== undefined) {
|
|
27
|
+
writer.uint32(10).string(message.filter);
|
|
28
|
+
}
|
|
29
|
+
return writer;
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
decode(input: _m0.Reader | Uint8Array, length?: number): MockFilter {
|
|
33
|
+
const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input);
|
|
34
|
+
let end = length === undefined ? reader.len : reader.pos + length;
|
|
35
|
+
const message = createBaseMockFilter() as any;
|
|
36
|
+
while (reader.pos < end) {
|
|
37
|
+
const tag = reader.uint32();
|
|
38
|
+
switch (tag >>> 3) {
|
|
39
|
+
case 1:
|
|
40
|
+
if (tag !== 10) {
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
message.filter = reader.string();
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if ((tag & 7) === 4 || tag === 0) {
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
reader.skipType(tag & 7);
|
|
51
|
+
}
|
|
52
|
+
return message;
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
fromJSON(object: any): MockFilter {
|
|
56
|
+
return { filter: isSet(object.filter) ? globalThis.String(object.filter) : undefined };
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
toJSON(message: MockFilter): unknown {
|
|
60
|
+
const obj: any = {};
|
|
61
|
+
if (message.filter !== undefined) {
|
|
62
|
+
obj.filter = message.filter;
|
|
63
|
+
}
|
|
64
|
+
return obj;
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
create(base?: DeepPartial<MockFilter>): MockFilter {
|
|
68
|
+
return MockFilter.fromPartial(base ?? {});
|
|
69
|
+
},
|
|
70
|
+
fromPartial(object: DeepPartial<MockFilter>): MockFilter {
|
|
71
|
+
const message = createBaseMockFilter() as any;
|
|
72
|
+
message.filter = object.filter ?? undefined;
|
|
73
|
+
return message;
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
function createBaseMockBlock(): MockBlock {
|
|
78
|
+
return { data: undefined };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export const MockBlock = {
|
|
82
|
+
encode(message: MockBlock, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
|
|
83
|
+
if (message.data !== undefined) {
|
|
84
|
+
writer.uint32(10).string(message.data);
|
|
85
|
+
}
|
|
86
|
+
return writer;
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
decode(input: _m0.Reader | Uint8Array, length?: number): MockBlock {
|
|
90
|
+
const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input);
|
|
91
|
+
let end = length === undefined ? reader.len : reader.pos + length;
|
|
92
|
+
const message = createBaseMockBlock() as any;
|
|
93
|
+
while (reader.pos < end) {
|
|
94
|
+
const tag = reader.uint32();
|
|
95
|
+
switch (tag >>> 3) {
|
|
96
|
+
case 1:
|
|
97
|
+
if (tag !== 10) {
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
message.data = reader.string();
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if ((tag & 7) === 4 || tag === 0) {
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
reader.skipType(tag & 7);
|
|
108
|
+
}
|
|
109
|
+
return message;
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
fromJSON(object: any): MockBlock {
|
|
113
|
+
return { data: isSet(object.data) ? globalThis.String(object.data) : undefined };
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
toJSON(message: MockBlock): unknown {
|
|
117
|
+
const obj: any = {};
|
|
118
|
+
if (message.data !== undefined) {
|
|
119
|
+
obj.data = message.data;
|
|
120
|
+
}
|
|
121
|
+
return obj;
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
create(base?: DeepPartial<MockBlock>): MockBlock {
|
|
125
|
+
return MockBlock.fromPartial(base ?? {});
|
|
126
|
+
},
|
|
127
|
+
fromPartial(object: DeepPartial<MockBlock>): MockBlock {
|
|
128
|
+
const message = createBaseMockBlock() as any;
|
|
129
|
+
message.data = object.data ?? undefined;
|
|
130
|
+
return message;
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
type Builtin = Date | Function | Uint8Array | string | number | boolean | bigint | undefined;
|
|
135
|
+
|
|
136
|
+
export type DeepPartial<T> = T extends Builtin ? T
|
|
137
|
+
: T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>>
|
|
138
|
+
: T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>>
|
|
139
|
+
: T extends { readonly $case: string }
|
|
140
|
+
? { [K in keyof Omit<T, "$case">]?: DeepPartial<T[K]> } & { readonly $case: T["$case"] }
|
|
141
|
+
: T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> }
|
|
142
|
+
: Partial<T>;
|
|
143
|
+
|
|
144
|
+
function isSet(value: any): boolean {
|
|
145
|
+
return value !== null && value !== undefined;
|
|
11
146
|
}
|
package/src/rate.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** Track data rate using high precision timers. */
|
|
2
|
+
export class RateGauge {
|
|
3
|
+
private interval: number;
|
|
4
|
+
private prev?: bigint;
|
|
5
|
+
private rateMs?: number;
|
|
6
|
+
private var: number;
|
|
7
|
+
|
|
8
|
+
constructor(intervalSeconds: number) {
|
|
9
|
+
// Convert seconds to milliseconds.
|
|
10
|
+
this.interval = intervalSeconds * 1_000;
|
|
11
|
+
this.var = 0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public record(items: number) {
|
|
15
|
+
// Compute the exponential moving average of the rate.
|
|
16
|
+
const prev = this.prev;
|
|
17
|
+
const now = process.hrtime.bigint();
|
|
18
|
+
this.prev = now;
|
|
19
|
+
|
|
20
|
+
if (!prev) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const deltaMs = Number(now - prev) / 1_000_000;
|
|
25
|
+
// rate in items/ms.
|
|
26
|
+
const rateMs = items / deltaMs;
|
|
27
|
+
|
|
28
|
+
if (this.rateMs === undefined) {
|
|
29
|
+
this.rateMs = rateMs;
|
|
30
|
+
this.var = 0;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const alpha = 1 - Math.exp(-deltaMs / this.interval);
|
|
35
|
+
this.rateMs = alpha * rateMs + (1 - alpha) * this.rateMs;
|
|
36
|
+
|
|
37
|
+
const diff = rateMs - this.rateMs;
|
|
38
|
+
const incr = alpha * diff;
|
|
39
|
+
this.var = (1 - alpha) * (this.var + incr * diff);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Returns the average rate per second. */
|
|
43
|
+
public average() {
|
|
44
|
+
if (this.rateMs === undefined) {
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
return this.rateMs * 1_000;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Returns the variance. */
|
|
51
|
+
public variance() {
|
|
52
|
+
return this.var;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
StatusResponse,
|
|
5
|
+
statusRequestFromProto,
|
|
6
|
+
statusRequestToProto,
|
|
7
|
+
statusResponseFromProto,
|
|
8
|
+
statusResponseToProto,
|
|
9
|
+
} from "./status";
|
|
10
|
+
|
|
11
|
+
describe("StatusRequest", () => {
|
|
12
|
+
describe("proto", () => {
|
|
13
|
+
it("should encode and decode", () => {
|
|
14
|
+
const proto = statusRequestToProto({});
|
|
15
|
+
expect(proto).toMatchInlineSnapshot("{}");
|
|
16
|
+
const back = statusRequestFromProto(proto);
|
|
17
|
+
expect(back).toEqual({});
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe("StatusResponse", () => {
|
|
23
|
+
describe("proto", () => {
|
|
24
|
+
it("should encode and decode", () => {
|
|
25
|
+
const response = StatusResponse.make({
|
|
26
|
+
currentHead: {
|
|
27
|
+
orderKey: 123n,
|
|
28
|
+
},
|
|
29
|
+
lastIngested: {
|
|
30
|
+
orderKey: 123n,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const proto = statusResponseToProto(response);
|
|
35
|
+
expect(proto).toMatchInlineSnapshot(`
|
|
36
|
+
{
|
|
37
|
+
"currentHead": {
|
|
38
|
+
"orderKey": 123n,
|
|
39
|
+
"uniqueKey": Uint8Array [],
|
|
40
|
+
},
|
|
41
|
+
"lastIngested": {
|
|
42
|
+
"orderKey": 123n,
|
|
43
|
+
"uniqueKey": Uint8Array [],
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
`);
|
|
47
|
+
const back = statusResponseFromProto(proto);
|
|
48
|
+
expect(back).toEqual(response);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|
package/src/status.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Schema } from "@effect/schema";
|
|
2
|
+
|
|
3
|
+
import { Cursor } from "./common";
|
|
4
|
+
|
|
5
|
+
/** The request to the `status` endpoint. */
|
|
6
|
+
export const StatusRequest = Schema.Struct({});
|
|
7
|
+
|
|
8
|
+
export type StatusRequest = typeof StatusRequest.Type;
|
|
9
|
+
|
|
10
|
+
export const statusRequestToProto = Schema.encodeSync(StatusRequest);
|
|
11
|
+
export const statusRequestFromProto = Schema.decodeSync(StatusRequest);
|
|
12
|
+
|
|
13
|
+
/** The response from the `status` endpoint. */
|
|
14
|
+
export const StatusResponse = Schema.Struct({
|
|
15
|
+
currentHead: Schema.optional(Cursor),
|
|
16
|
+
lastIngested: Schema.optional(Cursor),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export type StatusResponse = typeof StatusResponse.Type;
|
|
20
|
+
|
|
21
|
+
export const statusResponseToProto = Schema.encodeSync(StatusResponse);
|
|
22
|
+
export const statusResponseFromProto = Schema.decodeSync(StatusResponse);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Schema } from "@effect/schema";
|
|
2
|
+
import { test } from "vitest";
|
|
3
|
+
|
|
4
|
+
import { Data } from "./stream";
|
|
5
|
+
|
|
6
|
+
const Inner = Schema.Struct({
|
|
7
|
+
data: Schema.String,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const Good = Schema.transform(Schema.Uint8ArrayFromSelf, Schema.NullOr(Inner), {
|
|
11
|
+
decode(value) {
|
|
12
|
+
throw new Error("not implemented");
|
|
13
|
+
},
|
|
14
|
+
encode(value) {
|
|
15
|
+
throw new Error("not implemented");
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const Bad = Schema.transform(Schema.Uint8ArrayFromSelf, Inner, {
|
|
20
|
+
decode(value) {
|
|
21
|
+
throw new Error("not implemented");
|
|
22
|
+
},
|
|
23
|
+
encode(value) {
|
|
24
|
+
throw new Error("not implemented");
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("Data", () => {
|
|
29
|
+
const GoodData = Data(Good);
|
|
30
|
+
|
|
31
|
+
// @ts-expect-error
|
|
32
|
+
const BadData = Data(Bad);
|
|
33
|
+
});
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { Schema } from "@effect/schema";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
Heartbeat,
|
|
7
|
+
Invalidate,
|
|
8
|
+
StreamDataRequest,
|
|
9
|
+
StreamDataResponse,
|
|
10
|
+
SystemMessage,
|
|
11
|
+
} from "./stream";
|
|
12
|
+
|
|
13
|
+
const InnerData = Schema.Struct({
|
|
14
|
+
value: Schema.String,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const TestData = Schema.transform(
|
|
18
|
+
Schema.Uint8ArrayFromSelf,
|
|
19
|
+
Schema.NullOr(InnerData),
|
|
20
|
+
{
|
|
21
|
+
decode(bytes) {
|
|
22
|
+
const value = new TextDecoder().decode(bytes);
|
|
23
|
+
return { value };
|
|
24
|
+
},
|
|
25
|
+
encode(value) {
|
|
26
|
+
if (value === null) {
|
|
27
|
+
return new Uint8Array();
|
|
28
|
+
}
|
|
29
|
+
return new TextEncoder().encode(value.value);
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const TestStreamDataRequest = StreamDataRequest(TestData);
|
|
35
|
+
|
|
36
|
+
const encodeTestRequest = Schema.encodeSync(TestStreamDataRequest);
|
|
37
|
+
const decodeTestRequest = Schema.decodeSync(TestStreamDataRequest);
|
|
38
|
+
|
|
39
|
+
const TestStreamDataResponse = StreamDataResponse(TestData);
|
|
40
|
+
|
|
41
|
+
type TestStreamDataResponse = typeof TestStreamDataResponse.Type;
|
|
42
|
+
|
|
43
|
+
const encodeTestResponse = Schema.encodeSync(TestStreamDataResponse);
|
|
44
|
+
const decodeTestResponse = Schema.decodeSync(TestStreamDataResponse);
|
|
45
|
+
|
|
46
|
+
describe("StreamDataRequest", () => {
|
|
47
|
+
it("encodes and decodes", () => {
|
|
48
|
+
const request = TestStreamDataRequest.make({
|
|
49
|
+
finality: "accepted",
|
|
50
|
+
startingCursor: {
|
|
51
|
+
orderKey: 5_000_000n,
|
|
52
|
+
},
|
|
53
|
+
filter: [{ value: "hello" }, { value: "world" }],
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const proto = encodeTestRequest(request);
|
|
57
|
+
expect(proto).toMatchInlineSnapshot(`
|
|
58
|
+
{
|
|
59
|
+
"filter": [
|
|
60
|
+
Uint8Array [
|
|
61
|
+
104,
|
|
62
|
+
101,
|
|
63
|
+
108,
|
|
64
|
+
108,
|
|
65
|
+
111,
|
|
66
|
+
],
|
|
67
|
+
Uint8Array [
|
|
68
|
+
119,
|
|
69
|
+
111,
|
|
70
|
+
114,
|
|
71
|
+
108,
|
|
72
|
+
100,
|
|
73
|
+
],
|
|
74
|
+
],
|
|
75
|
+
"finality": 2,
|
|
76
|
+
"startingCursor": {
|
|
77
|
+
"orderKey": 5000000n,
|
|
78
|
+
"uniqueKey": Uint8Array [],
|
|
79
|
+
},
|
|
80
|
+
}
|
|
81
|
+
`);
|
|
82
|
+
const back = decodeTestRequest(proto);
|
|
83
|
+
expect(request).toEqual(back);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe("StreamDataResponse", () => {
|
|
88
|
+
const encode = encodeTestResponse;
|
|
89
|
+
const decode = decodeTestResponse;
|
|
90
|
+
|
|
91
|
+
describe(".data", () => {
|
|
92
|
+
it("encodes and decodes", () => {
|
|
93
|
+
const message: TestStreamDataResponse = {
|
|
94
|
+
_tag: "data",
|
|
95
|
+
data: {
|
|
96
|
+
finality: "accepted",
|
|
97
|
+
data: [{ value: "hello" }, { value: "world" }],
|
|
98
|
+
},
|
|
99
|
+
} as const;
|
|
100
|
+
|
|
101
|
+
const proto = encode(message);
|
|
102
|
+
expect(proto).toMatchInlineSnapshot(`
|
|
103
|
+
{
|
|
104
|
+
"$case": "data",
|
|
105
|
+
"data": {
|
|
106
|
+
"data": [
|
|
107
|
+
Uint8Array [
|
|
108
|
+
104,
|
|
109
|
+
101,
|
|
110
|
+
108,
|
|
111
|
+
108,
|
|
112
|
+
111,
|
|
113
|
+
],
|
|
114
|
+
Uint8Array [
|
|
115
|
+
119,
|
|
116
|
+
111,
|
|
117
|
+
114,
|
|
118
|
+
108,
|
|
119
|
+
100,
|
|
120
|
+
],
|
|
121
|
+
],
|
|
122
|
+
"finality": 2,
|
|
123
|
+
},
|
|
124
|
+
}
|
|
125
|
+
`);
|
|
126
|
+
const back = decode(proto);
|
|
127
|
+
expect(back).toEqual(message);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe(".invalidate", () => {
|
|
132
|
+
it("encodes and decodes", () => {
|
|
133
|
+
const invalidate = Invalidate.make({
|
|
134
|
+
_tag: "invalidate",
|
|
135
|
+
invalidate: {
|
|
136
|
+
cursor: {
|
|
137
|
+
orderKey: 5_000_000n,
|
|
138
|
+
uniqueKey: "0x1234567890",
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const proto = encode(invalidate);
|
|
144
|
+
expect(proto).toMatchInlineSnapshot(`
|
|
145
|
+
{
|
|
146
|
+
"$case": "invalidate",
|
|
147
|
+
"invalidate": {
|
|
148
|
+
"cursor": {
|
|
149
|
+
"orderKey": 5000000n,
|
|
150
|
+
"uniqueKey": Uint8Array [
|
|
151
|
+
18,
|
|
152
|
+
52,
|
|
153
|
+
86,
|
|
154
|
+
120,
|
|
155
|
+
144,
|
|
156
|
+
],
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
}
|
|
160
|
+
`);
|
|
161
|
+
const back = decode(proto);
|
|
162
|
+
expect(back).toEqual(invalidate);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe(".heartbeat", () => {
|
|
167
|
+
it("encodes and decodes", () => {
|
|
168
|
+
const heartbeat = Heartbeat.make({
|
|
169
|
+
_tag: "heartbeat",
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const proto = encode(heartbeat);
|
|
173
|
+
expect(proto).toMatchInlineSnapshot(`
|
|
174
|
+
{
|
|
175
|
+
"$case": "heartbeat",
|
|
176
|
+
}
|
|
177
|
+
`);
|
|
178
|
+
const back = decode(proto);
|
|
179
|
+
expect(back).toEqual(heartbeat);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
describe(".systemMessage", () => {
|
|
184
|
+
it("encodes and decodes stdout", () => {
|
|
185
|
+
const message = SystemMessage.make({
|
|
186
|
+
_tag: "systemMessage",
|
|
187
|
+
systemMessage: {
|
|
188
|
+
output: {
|
|
189
|
+
_tag: "stdout",
|
|
190
|
+
stdout: "hello",
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const proto = encode(message);
|
|
196
|
+
expect(proto).toMatchInlineSnapshot(`
|
|
197
|
+
{
|
|
198
|
+
"$case": "systemMessage",
|
|
199
|
+
"systemMessage": {
|
|
200
|
+
"output": {
|
|
201
|
+
"$case": "stdout",
|
|
202
|
+
"stdout": "hello",
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
}
|
|
206
|
+
`);
|
|
207
|
+
const back = decode(proto);
|
|
208
|
+
expect(back).toEqual(message);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("encodes and decodes stderr", () => {
|
|
212
|
+
const message = SystemMessage.make({
|
|
213
|
+
_tag: "systemMessage",
|
|
214
|
+
systemMessage: {
|
|
215
|
+
output: {
|
|
216
|
+
_tag: "stderr",
|
|
217
|
+
stderr: "hello",
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
const proto = encode(message);
|
|
223
|
+
expect(proto).toMatchInlineSnapshot(`
|
|
224
|
+
{
|
|
225
|
+
"$case": "systemMessage",
|
|
226
|
+
"systemMessage": {
|
|
227
|
+
"output": {
|
|
228
|
+
"$case": "stderr",
|
|
229
|
+
"stderr": "hello",
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
}
|
|
233
|
+
`);
|
|
234
|
+
const back = decode(proto);
|
|
235
|
+
expect(back).toEqual(message);
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
});
|
package/src/stream.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { Schema } from "@effect/schema";
|
|
2
|
+
|
|
3
|
+
import { Cursor } from "./common";
|
|
4
|
+
import * as proto from "./proto";
|
|
5
|
+
|
|
6
|
+
/** Data finality. */
|
|
7
|
+
export const DataFinality = Schema.transform(
|
|
8
|
+
Schema.Enums(proto.stream.DataFinality),
|
|
9
|
+
Schema.Literal("finalized", "accepted", "pending", "unknown"),
|
|
10
|
+
{
|
|
11
|
+
decode(value) {
|
|
12
|
+
const enumMap = {
|
|
13
|
+
[proto.stream.DataFinality.FINALIZED]: "finalized",
|
|
14
|
+
[proto.stream.DataFinality.ACCEPTED]: "accepted",
|
|
15
|
+
[proto.stream.DataFinality.PENDING]: "pending",
|
|
16
|
+
[proto.stream.DataFinality.UNKNOWN]: "unknown",
|
|
17
|
+
[proto.stream.DataFinality.UNRECOGNIZED]: "unknown",
|
|
18
|
+
} as const;
|
|
19
|
+
|
|
20
|
+
return enumMap[value] ?? "unknown";
|
|
21
|
+
},
|
|
22
|
+
encode(value) {
|
|
23
|
+
const enumMap = {
|
|
24
|
+
finalized: proto.stream.DataFinality.FINALIZED,
|
|
25
|
+
accepted: proto.stream.DataFinality.ACCEPTED,
|
|
26
|
+
pending: proto.stream.DataFinality.PENDING,
|
|
27
|
+
unknown: proto.stream.DataFinality.UNKNOWN,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
return enumMap[value] ?? proto.stream.DataFinality.UNKNOWN;
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
export type DataFinality = typeof DataFinality.Type;
|
|
36
|
+
|
|
37
|
+
/** Create a `StreamDataRequest` with the given filter schema. */
|
|
38
|
+
export const StreamDataRequest = <TA, TR>(
|
|
39
|
+
filter: Schema.Schema<TA, Uint8Array, TR>,
|
|
40
|
+
) =>
|
|
41
|
+
Schema.Struct({
|
|
42
|
+
finality: Schema.optional(DataFinality),
|
|
43
|
+
startingCursor: Schema.optional(Cursor),
|
|
44
|
+
filter: Schema.mutable(Schema.Array(filter)),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export type StreamDataRequest<TA> = {
|
|
48
|
+
finality?: DataFinality | undefined;
|
|
49
|
+
startingCursor?: Cursor | undefined;
|
|
50
|
+
filter: TA[];
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const Invalidate = Schema.Struct({
|
|
54
|
+
_tag: tag("invalidate"),
|
|
55
|
+
invalidate: Schema.Struct({
|
|
56
|
+
cursor: Schema.optional(Cursor),
|
|
57
|
+
}),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
export type Invalidate = typeof Invalidate.Type;
|
|
61
|
+
|
|
62
|
+
export const Heartbeat = Schema.Struct({
|
|
63
|
+
_tag: tag("heartbeat"),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
export type Heartbeat = typeof Heartbeat.Type;
|
|
67
|
+
|
|
68
|
+
export const StdOut = Schema.Struct({
|
|
69
|
+
_tag: tag("stdout"),
|
|
70
|
+
stdout: Schema.String,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
export type StdOut = typeof StdOut.Type;
|
|
74
|
+
|
|
75
|
+
export const StdErr = Schema.Struct({
|
|
76
|
+
_tag: tag("stderr"),
|
|
77
|
+
stderr: Schema.String,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
export type StdErr = typeof StdErr.Type;
|
|
81
|
+
|
|
82
|
+
export const SystemMessage = Schema.Struct({
|
|
83
|
+
_tag: tag("systemMessage"),
|
|
84
|
+
systemMessage: Schema.Struct({
|
|
85
|
+
output: Schema.optional(Schema.Union(StdOut, StdErr)),
|
|
86
|
+
}),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
export type SystemMessage = typeof SystemMessage.Type;
|
|
90
|
+
|
|
91
|
+
export const Data = <TA, TR>(
|
|
92
|
+
schema: Schema.Schema<TA | null, Uint8Array, TR>,
|
|
93
|
+
) =>
|
|
94
|
+
Schema.Struct({
|
|
95
|
+
_tag: tag("data"),
|
|
96
|
+
data: Schema.Struct({
|
|
97
|
+
cursor: Schema.optional(Cursor),
|
|
98
|
+
endCursor: Schema.optional(Cursor),
|
|
99
|
+
finality: DataFinality,
|
|
100
|
+
data: Schema.Array(schema),
|
|
101
|
+
}),
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
export const StreamDataResponse = <TA, TR>(
|
|
105
|
+
data: Schema.Schema<TA | null, Uint8Array, TR>,
|
|
106
|
+
) => Schema.Union(Data(data), Invalidate, Heartbeat, SystemMessage);
|
|
107
|
+
|
|
108
|
+
const ResponseWithoutData = Schema.Union(Invalidate, Heartbeat, SystemMessage);
|
|
109
|
+
type ResponseWithoutData = typeof ResponseWithoutData.Type;
|
|
110
|
+
|
|
111
|
+
export type StreamDataResponse<TA> =
|
|
112
|
+
| ResponseWithoutData
|
|
113
|
+
| {
|
|
114
|
+
_tag: "data";
|
|
115
|
+
data: {
|
|
116
|
+
cursor?: Cursor | undefined;
|
|
117
|
+
endCursor?: Cursor | undefined;
|
|
118
|
+
finality: DataFinality;
|
|
119
|
+
data: readonly (TA | null)[];
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
function tag<T extends string>(tag: T) {
|
|
124
|
+
return Schema.Literal(tag).pipe(
|
|
125
|
+
Schema.propertySignature,
|
|
126
|
+
Schema.fromKey("$case"),
|
|
127
|
+
);
|
|
128
|
+
}
|