@apibara/protocol 2.1.0-beta.3 → 2.1.0-beta.30

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 (36) hide show
  1. package/dist/codec.cjs +241 -0
  2. package/dist/codec.d.cts +81 -0
  3. package/dist/codec.d.mts +81 -0
  4. package/dist/codec.d.ts +81 -0
  5. package/dist/codec.mjs +223 -0
  6. package/dist/index.cjs +17 -29
  7. package/dist/index.d.cts +4 -5
  8. package/dist/index.d.mts +4 -5
  9. package/dist/index.d.ts +4 -5
  10. package/dist/index.mjs +15 -20
  11. package/dist/shared/{protocol.4b1cfe2c.d.cts → protocol.21b07506.d.mts} +399 -247
  12. package/dist/shared/{protocol.4b1cfe2c.d.mts → protocol.526c6532.d.ts} +399 -247
  13. package/dist/shared/{protocol.e39e40d6.cjs → protocol.53f81a1e.cjs} +176 -177
  14. package/dist/shared/{protocol.991ff9ad.mjs → protocol.68fdd897.mjs} +175 -171
  15. package/dist/shared/{protocol.4b1cfe2c.d.ts → protocol.a5762a90.d.cts} +399 -247
  16. package/dist/testing/index.cjs +25 -38
  17. package/dist/testing/index.d.cts +107 -54
  18. package/dist/testing/index.d.mts +107 -54
  19. package/dist/testing/index.d.ts +107 -54
  20. package/dist/testing/index.mjs +25 -38
  21. package/package.json +7 -3
  22. package/src/client.ts +8 -13
  23. package/src/codec.ts +662 -0
  24. package/src/common.ts +70 -53
  25. package/src/config.ts +4 -4
  26. package/src/proto/google/protobuf/duration.ts +8 -6
  27. package/src/proto/stream.ts +38 -24
  28. package/src/status.ts +9 -16
  29. package/src/stream.ts +145 -144
  30. package/src/testing/mock.ts +36 -38
  31. package/src/common.test.ts +0 -67
  32. package/src/status.test.ts +0 -51
  33. package/src/stream.test-d.ts +0 -33
  34. package/src/stream.test.ts +0 -254
  35. package/src/testing/client.test.ts +0 -97
  36. package/src/testing/mock.test.ts +0 -35
package/src/codec.ts ADDED
@@ -0,0 +1,662 @@
1
+ /*
2
+
3
+ █████████ █████
4
+ ███░░░░░███ ░░███
5
+ ███ ░░░ ██████ ███████ ██████ ██████
6
+ ░███ ███░░███ ███░░███ ███░░███ ███░░███
7
+ ░███ ░███ ░███░███ ░███ ░███████ ░███ ░░░
8
+ ░░███ ███░███ ░███░███ ░███ ░███░░░ ░███ ███
9
+ ░░█████████ ░░██████ ░░████████░░██████ ░░██████
10
+ ░░░░░░░░░ ░░░░░░ ░░░░░░░░ ░░░░░░ ░░░░░░
11
+
12
+ */
13
+
14
+ /** Codec to encode and decode protobuf messages */
15
+ export type Codec<TApp = unknown, TProto = unknown> = {
16
+ encode(app: TApp): TProto;
17
+ decode(proto: TProto): TApp;
18
+ };
19
+
20
+ /* Helper to get the high-level type of a codec */
21
+ export type CodecType<C extends Codec> = ReturnType<C["decode"]>;
22
+
23
+ /* Helper to get the protobuf type of a codec */
24
+ export type CodecProto<C extends Codec> = ReturnType<C["encode"]>;
25
+
26
+ /*
27
+
28
+ ██████ ██████
29
+ ░░██████ ██████
30
+ ░███░█████░███ ██████ █████ █████ ██████ ███████ ██████
31
+ ░███░░███ ░███ ███░░███ ███░░ ███░░ ░░░░░███ ███░░███ ███░░███
32
+ ░███ ░░░ ░███ ░███████ ░░█████ ░░█████ ███████ ░███ ░███░███████
33
+ ░███ ░███ ░███░░░ ░░░░███ ░░░░███ ███░░███ ░███ ░███░███░░░
34
+ █████ █████░░██████ ██████ ██████ ░░████████░░███████░░██████
35
+ ░░░░░ ░░░░░ ░░░░░░ ░░░░░░ ░░░░░░ ░░░░░░░░ ░░░░░███ ░░░░░░
36
+ ███ ░███
37
+ ░░██████
38
+ ░░░░░░
39
+ */
40
+
41
+ export type Evaluate<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
42
+
43
+ type TPropertyKey = string | symbol;
44
+ // Message properties as codec.
45
+ type TProperties = Record<TPropertyKey, Codec>;
46
+
47
+ // Optional properties in an object.
48
+ // Properties are optional when they have the `undefined` type.
49
+ type OptionalPropertyKeys<T> = {
50
+ [K in keyof T]: undefined extends T[K] ? K : never;
51
+ }[keyof T];
52
+
53
+ // Properties that are not optional are required.
54
+ type RequiredPropertyKeys<T> = keyof Omit<T, OptionalPropertyKeys<T>>;
55
+
56
+ // Helper to get the app type of a message codec.
57
+ type _MessageCodecType<T extends TProperties> = {
58
+ [K in keyof T]: CodecType<T[K]>;
59
+ };
60
+
61
+ // Helper to get the protobuf type of a message codec.
62
+ type _MessageCodecProto<T extends TProperties> = {
63
+ [K in keyof T]: CodecProto<T[K]>;
64
+ };
65
+
66
+ // Adjust the app type of the codec so that optional properties are optional.
67
+ type MessageCodecType<T extends TProperties> =
68
+ _MessageCodecType<T> extends infer R
69
+ ? Evaluate<Partial<R> & Required<Pick<R, RequiredPropertyKeys<R>>>>
70
+ : never;
71
+
72
+ // Adjust the protobuf type of the codec so that optional properties are optional.
73
+ type MessageCodecProto<T extends TProperties> =
74
+ _MessageCodecProto<T> extends infer R
75
+ ? Evaluate<Partial<R> & Required<Pick<R, RequiredPropertyKeys<R>>>>
76
+ : never;
77
+
78
+ export type MessageCodec<T extends TProperties = TProperties> = Codec<
79
+ MessageCodecType<T>,
80
+ MessageCodecProto<T>
81
+ >;
82
+
83
+ export function MessageCodec<T extends TProperties>(
84
+ schema: T,
85
+ ): MessageCodec<T> {
86
+ return {
87
+ encode(app) {
88
+ return new Proxy(app, {
89
+ get(target, property) {
90
+ if (!Object.hasOwn(target, property)) {
91
+ return Reflect.get(target, property);
92
+ }
93
+
94
+ const v = Reflect.get(target, property);
95
+ return schema[property].encode(v);
96
+ },
97
+ });
98
+ },
99
+ decode(proto) {
100
+ return new Proxy(proto, {
101
+ get(target, property) {
102
+ if (!Object.hasOwn(target, property)) {
103
+ return Reflect.get(target, property);
104
+ }
105
+
106
+ const v = Reflect.get(target, property);
107
+ return schema[property].decode(v);
108
+ },
109
+ });
110
+ },
111
+ };
112
+ }
113
+
114
+ /*
115
+ █████████
116
+ ███░░░░░███
117
+ ░███ ░███ ████████ ████████ ██████ █████ ████
118
+ ░███████████ ░░███░░███░░███░░███ ░░░░░███ ░░███ ░███
119
+ ░███░░░░░███ ░███ ░░░ ░███ ░░░ ███████ ░███ ░███
120
+ ░███ ░███ ░███ ░███ ███░░███ ░███ ░███
121
+ █████ █████ █████ █████ ░░████████ ░░███████
122
+ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░ ░░░░░███
123
+ ███ ░███
124
+ ░░██████
125
+ ░░░░░░
126
+
127
+ */
128
+
129
+ export type ArrayCodec<T extends Codec> = T extends Codec<
130
+ infer TApp,
131
+ infer TProto
132
+ >
133
+ ? Codec<readonly TApp[], readonly TProto[] | undefined>
134
+ : never;
135
+
136
+ export function ArrayCodec<T extends Codec<TApp, TProto>, TApp, TProto>(
137
+ t: T,
138
+ ): ArrayCodec<T> {
139
+ return {
140
+ encode(app) {
141
+ return app.map(t.encode) as readonly TProto[];
142
+ },
143
+ decode(proto) {
144
+ if (proto === undefined) return [];
145
+ return proto.map(t.decode) as readonly TApp[];
146
+ },
147
+ } as ArrayCodec<T>;
148
+ }
149
+
150
+ /*
151
+ ██████ ██████ █████ █████ ████ █████████ █████
152
+ ░░██████ ██████ ░░███ ░░███ ░░███ ███░░░░░███ ░░███
153
+ ░███░█████░███ █████ ████ ███████ ██████ ░███████ ░███ ██████ ███ ░░░ ██████ ███████ ██████ ██████
154
+ ░███░░███ ░███ ░░███ ░███ ░░░███░ ░░░░░███ ░███░░███ ░███ ███░░███░███ ███░░███ ███░░███ ███░░███ ███░░███
155
+ ░███ ░░░ ░███ ░███ ░███ ░███ ███████ ░███ ░███ ░███ ░███████ ░███ ░███ ░███░███ ░███ ░███████ ░███ ░░░
156
+ ░███ ░███ ░███ ░███ ░███ ███ ███░░███ ░███ ░███ ░███ ░███░░░ ░░███ ███░███ ░███░███ ░███ ░███░░░ ░███ ███
157
+ █████ █████ ░░████████ ░░█████ ░░████████ ████████ █████░░██████ ░░█████████ ░░██████ ░░████████░░██████ ░░██████
158
+ ░░░░░ ░░░░░ ░░░░░░░░ ░░░░░ ░░░░░░░░ ░░░░░░░░ ░░░░░ ░░░░░░ ░░░░░░░░░ ░░░░░░ ░░░░░░░░ ░░░░░░ ░░░░░░
159
+ */
160
+ export type MutableArrayCodec<
161
+ T extends Codec<TApp, TProto>,
162
+ TApp,
163
+ TProto,
164
+ > = T extends Codec<infer TApp, infer TProto> ? Codec<TApp[], TProto[]> : never;
165
+
166
+ export function MutableArrayCodec<T extends Codec<TApp, TProto>, TApp, TProto>(
167
+ t: T,
168
+ ): MutableArrayCodec<T, TApp, TProto> {
169
+ return {
170
+ encode(app) {
171
+ return app.map(t.encode) as TProto[];
172
+ },
173
+ decode(proto) {
174
+ if (proto === undefined) return [];
175
+ return proto.map(t.decode) as TApp[];
176
+ },
177
+ } as MutableArrayCodec<T, TApp, TProto>;
178
+ }
179
+
180
+ /*
181
+ ███████ █████ ███ ████
182
+ ███░░░░░███ ░░███ ░░░ ░░███
183
+ ███ ░░███ ████████ ███████ ████ ██████ ████████ ██████ ░███
184
+ ░███ ░███░░███░░███░░░███░ ░░███ ███░░███░░███░░███ ░░░░░███ ░███
185
+ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ███████ ░███
186
+ ░░███ ███ ░███ ░███ ░███ ███ ░███ ░███ ░███ ░███ ░███ ███░░███ ░███
187
+ ░░░███████░ ░███████ ░░█████ █████░░██████ ████ █████░░████████ █████
188
+ ░░░░░░░ ░███░░░ ░░░░░ ░░░░░ ░░░░░░ ░░░░ ░░░░░ ░░░░░░░░ ░░░░░
189
+ ░███
190
+ █████
191
+ ░░░░░
192
+ */
193
+
194
+ export type OptionalCodec<T extends Codec> = T extends Codec<
195
+ infer TApp,
196
+ infer TProto
197
+ >
198
+ ? Codec<TApp | undefined, TProto | undefined>
199
+ : never;
200
+
201
+ export function OptionalCodec<T extends Codec>(t: T): OptionalCodec<T> {
202
+ return {
203
+ encode(app) {
204
+ if (app === undefined) return undefined;
205
+ return t.encode(app);
206
+ },
207
+ decode(proto) {
208
+ if (proto === undefined) return undefined;
209
+ return t.decode(proto);
210
+ },
211
+ } as OptionalCodec<T>;
212
+ }
213
+
214
+ /*
215
+ ███████████ ███ █████
216
+ ░░███░░░░░███ ░░░ ░░███
217
+ ░███ ░███ ██████ ████████ █████ ████ ████ ████████ ██████ ███████
218
+ ░██████████ ███░░███ ███░░███ ░░███ ░███ ░░███ ░░███░░███ ███░░███ ███░░███
219
+ ░███░░░░░███ ░███████ ░███ ░███ ░███ ░███ ░███ ░███ ░░░ ░███████ ░███ ░███
220
+ ░███ ░███ ░███░░░ ░███ ░███ ░███ ░███ ░███ ░███ ░███░░░ ░███ ░███
221
+ █████ █████░░██████ ░░███████ ░░████████ █████ █████ ░░██████ ░░████████
222
+ ░░░░░ ░░░░░ ░░░░░░ ░░░░░███ ░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░ ░░░░░░░░
223
+ ░███
224
+ █████
225
+ ░░░░░
226
+ */
227
+
228
+ export type RequiredCodec<T extends Codec> = T extends Codec<
229
+ infer TApp,
230
+ infer TProto
231
+ >
232
+ ? TApp extends undefined
233
+ ? never
234
+ : Codec<TApp, TProto | undefined>
235
+ : never;
236
+
237
+ export function RequiredCodec<T extends Codec>(t: T): RequiredCodec<T> {
238
+ return {
239
+ encode(app) {
240
+ if (app === undefined) throw new Error("Value is required but undefined");
241
+ return t.encode(app);
242
+ },
243
+ decode(proto) {
244
+ if (proto === undefined)
245
+ throw new Error("Value is required but undefined");
246
+ return t.decode(proto);
247
+ },
248
+ } as RequiredCodec<T>;
249
+ }
250
+
251
+ /*
252
+ ██████ █████ ████ ████ ███████
253
+ ░░██████ ░░███ ░░███ ░░███ ███░░░░░███
254
+ ░███░███ ░███ █████ ████ ░███ ░███ ███ ░░███ ████████
255
+ ░███░░███░███ ░░███ ░███ ░███ ░███ ░███ ░███░░███░░███
256
+ ░███ ░░██████ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░░░
257
+ ░███ ░░█████ ░███ ░███ ░███ ░███ ░░███ ███ ░███
258
+ █████ ░░█████ ░░████████ █████ █████ ░░░███████░ █████
259
+ ░░░░░ ░░░░░ ░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░ ░░░░░
260
+ */
261
+
262
+ export type NullOrCodec<T extends Codec> = T extends Codec<
263
+ infer TApp,
264
+ infer TProto
265
+ >
266
+ ? Codec<TApp | null, TProto | null>
267
+ : never;
268
+
269
+ export function NullOrCodec<T extends Codec>(t: T): NullOrCodec<T> {
270
+ return {
271
+ encode(app) {
272
+ if (app === null) return null;
273
+ return t.encode(app);
274
+ },
275
+ decode(proto) {
276
+ if (proto === null) return null;
277
+ return t.decode(proto);
278
+ },
279
+ } as NullOrCodec<T>;
280
+ }
281
+
282
+ /*
283
+
284
+ ███████████ ███ █████ █████
285
+ ░░███░░░░░███ ░░░ ░░███ ░░███
286
+ ░███ ░███ ████ ███████ ░███ ████████ ███████
287
+ ░██████████ ░░███ ███░░███ ░███ ░░███░░███ ░░░███░
288
+ ░███░░░░░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███
289
+ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ███
290
+ ███████████ █████░░███████ █████ ████ █████ ░░█████
291
+ ░░░░░░░░░░░ ░░░░░ ░░░░░███░░░░░ ░░░░ ░░░░░ ░░░░░
292
+ ███ ░███
293
+ ░░██████
294
+ ░░░░░░
295
+ */
296
+
297
+ export type BigIntCodec = CodecType<typeof BigIntCodec>;
298
+
299
+ export const BigIntCodec: Codec<bigint, bigint> = {
300
+ encode(app) {
301
+ return app;
302
+ },
303
+ decode(proto) {
304
+ return proto;
305
+ },
306
+ };
307
+
308
+ /*
309
+
310
+ ██████ █████ █████
311
+ ░░██████ ░░███ ░░███
312
+ ░███░███ ░███ █████ ████ █████████████ ░███████ ██████ ████████
313
+ ░███░░███░███ ░░███ ░███ ░░███░░███░░███ ░███░░███ ███░░███░░███░░███
314
+ ░███ ░░██████ ░███ ░███ ░███ ░███ ░███ ░███ ░███░███████ ░███ ░░░
315
+ ░███ ░░█████ ░███ ░███ ░███ ░███ ░███ ░███ ░███░███░░░ ░███
316
+ █████ ░░█████ ░░████████ █████░███ █████ ████████ ░░██████ █████
317
+ ░░░░░ ░░░░░ ░░░░░░░░ ░░░░░ ░░░ ░░░░░ ░░░░░░░░ ░░░░░░ ░░░░░
318
+
319
+ */
320
+
321
+ export type NumberCodec = CodecType<typeof NumberCodec>;
322
+
323
+ export const NumberCodec: Codec<number, number> = {
324
+ encode(app) {
325
+ return app;
326
+ },
327
+ decode(proto) {
328
+ return proto;
329
+ },
330
+ };
331
+
332
+ /*
333
+ █████ █████ ███ █████ ████████ █████████
334
+ ░░███ ░░███ ░░░ ░░███ ███░░░░███ ███░░░░░███
335
+ ░███ ░███ ████ ████████ ███████ ░███ ░███ ░███ ░███ ████████ ████████ ██████ █████ ████
336
+ ░███ ░███ ░░███ ░░███░░███ ░░░███░ ░░████████ ░███████████ ░░███░░███░░███░░███ ░░░░░███ ░░███ ░███
337
+ ░███ ░███ ░███ ░███ ░███ ░███ ███░░░░███ ░███░░░░░███ ░███ ░░░ ░███ ░░░ ███████ ░███ ░███
338
+ ░███ ░███ ░███ ░███ ░███ ░███ ███░███ ░███ ░███ ░███ ░███ ░███ ███░░███ ░███ ░███
339
+ ░░████████ █████ ████ █████ ░░█████ ░░████████ █████ █████ █████ █████ ░░████████ ░░███████
340
+ ░░░░░░░░ ░░░░░ ░░░░ ░░░░░ ░░░░░ ░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░ ░░░░░███
341
+ ███ ░███
342
+ ░░██████
343
+ ░░░░░░
344
+ */
345
+
346
+ export type Uint8ArrayCodec = CodecType<typeof Uint8ArrayCodec>;
347
+
348
+ export const Uint8ArrayCodec: Codec<Uint8Array, Uint8Array> = {
349
+ encode(app) {
350
+ return app;
351
+ },
352
+ decode(proto) {
353
+ return proto;
354
+ },
355
+ };
356
+
357
+ /*
358
+
359
+ ██████████ █████
360
+ ░░███░░░░███ ░░███
361
+ ░███ ░░███ ██████ ███████ ██████
362
+ ░███ ░███ ░░░░░███ ░░░███░ ███░░███
363
+ ░███ ░███ ███████ ░███ ░███████
364
+ ░███ ███ ███░░███ ░███ ███░███░░░
365
+ ██████████ ░░████████ ░░█████ ░░██████
366
+ ░░░░░░░░░░ ░░░░░░░░ ░░░░░ ░░░░░░
367
+
368
+ */
369
+
370
+ export type DateCodec = CodecType<typeof DateCodec>;
371
+
372
+ export const DateCodec: Codec<Date, Date> = {
373
+ encode(app) {
374
+ return new Date(app);
375
+ },
376
+ decode(proto) {
377
+ return new Date(proto);
378
+ },
379
+ };
380
+
381
+ /*
382
+
383
+ ███████████ ████
384
+ ░░███░░░░░███ ░░███
385
+ ░███ ░███ ██████ ██████ ░███ ██████ ██████ ████████
386
+ ░██████████ ███░░███ ███░░███ ░███ ███░░███ ░░░░░███ ░░███░░███
387
+ ░███░░░░░███░███ ░███░███ ░███ ░███ ░███████ ███████ ░███ ░███
388
+ ░███ ░███░███ ░███░███ ░███ ░███ ░███░░░ ███░░███ ░███ ░███
389
+ ███████████ ░░██████ ░░██████ █████░░██████ ░░████████ ████ █████
390
+ ░░░░░░░░░░░ ░░░░░░ ░░░░░░ ░░░░░ ░░░░░░ ░░░░░░░░ ░░░░ ░░░░░
391
+
392
+ */
393
+
394
+ export type BooleanCodec = CodecType<typeof BooleanCodec>;
395
+
396
+ export const BooleanCodec: Codec<boolean, boolean> = {
397
+ encode(app) {
398
+ return app;
399
+ },
400
+ decode(proto) {
401
+ return proto;
402
+ },
403
+ };
404
+
405
+ /*
406
+
407
+ █████████ █████ ███
408
+ ███░░░░░███ ░░███ ░░░
409
+ ░███ ░░░ ███████ ████████ ████ ████████ ███████
410
+ ░░█████████ ░░░███░ ░░███░░███░░███ ░░███░░███ ███░░███
411
+ ░░░░░░░░███ ░███ ░███ ░░░ ░███ ░███ ░███ ░███ ░███
412
+ ███ ░███ ░███ ███ ░███ ░███ ░███ ░███ ░███ ░███
413
+ ░░█████████ ░░█████ █████ █████ ████ █████░░███████
414
+ ░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░ ░░░░░ ░░░░░███
415
+ ███ ░███
416
+ ░░██████
417
+ ░░░░░░
418
+ */
419
+
420
+ export type StringCodec = CodecType<typeof StringCodec>;
421
+
422
+ export const StringCodec: Codec<string, string> = {
423
+ encode(app) {
424
+ return app;
425
+ },
426
+ decode(proto) {
427
+ return proto;
428
+ },
429
+ };
430
+
431
+ /*
432
+
433
+ █████ █████ █████ ██████ ███ █████
434
+ ░░███ ░░███ ░░███ ███░░███ ░░░ ░░███
435
+ ░███ ░███ ████████ ███████ ██████ ░███ ░░░ ████ ████████ ██████ ███████
436
+ ░███ ░███ ░░███░░███ ███░░███ ███░░███ ███████ ░░███ ░░███░░███ ███░░███ ███░░███
437
+ ░███ ░███ ░███ ░███ ░███ ░███ ░███████ ░░░███░ ░███ ░███ ░███ ░███████ ░███ ░███
438
+ ░███ ░███ ░███ ░███ ░███ ░███ ░███░░░ ░███ ░███ ░███ ░███ ░███░░░ ░███ ░███
439
+ ░░████████ ████ █████░░████████░░██████ █████ █████ ████ █████░░██████ ░░████████
440
+ ░░░░░░░░ ░░░░ ░░░░░ ░░░░░░░░ ░░░░░░ ░░░░░ ░░░░░ ░░░░ ░░░░░ ░░░░░░ ░░░░░░░░
441
+
442
+ */
443
+
444
+ export type UndefinedCodec = CodecType<typeof UndefinedCodec>;
445
+
446
+ export const UndefinedCodec: Codec<undefined, undefined> = {
447
+ encode(app) {
448
+ return undefined;
449
+ },
450
+ decode(proto) {
451
+ return undefined;
452
+ },
453
+ };
454
+
455
+ /*
456
+ █████ ███ █████ ████
457
+ ░░███ ░░░ ░░███ ░░███
458
+ ░███ ████ ███████ ██████ ████████ ██████ ░███
459
+ ░███ ░░███ ░░░███░ ███░░███░░███░░███ ░░░░░███ ░███
460
+ ░███ ░███ ░███ ░███████ ░███ ░░░ ███████ ░███
461
+ ░███ █ ░███ ░███ ███░███░░░ ░███ ███░░███ ░███
462
+ ███████████ █████ ░░█████ ░░██████ █████ ░░████████ █████
463
+ ░░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░ ░░░░░ ░░░░░░░░ ░░░░░
464
+
465
+ */
466
+
467
+ type Literal = string | number | boolean | null | undefined;
468
+
469
+ export type LiteralCodec<T extends Codec, L extends Literal> = T extends Codec<
470
+ infer TApp,
471
+ infer TProto
472
+ >
473
+ ? Codec<TApp, TProto>
474
+ : never;
475
+
476
+ export const LiteralCodec = <const L extends Literal>(
477
+ value: L,
478
+ ): LiteralCodec<Codec<L, L>, L> => {
479
+ return {
480
+ encode(app) {
481
+ if (app !== value) {
482
+ throw new Error(`Expected ${String(value)}, got ${String(app)}`);
483
+ }
484
+ return app;
485
+ },
486
+ decode(proto) {
487
+ if (proto !== value) {
488
+ throw new Error(`Expected ${String(value)}, got ${String(proto)}`);
489
+ }
490
+ return proto;
491
+ },
492
+ } as LiteralCodec<Codec<L, L>, L>;
493
+ };
494
+
495
+ /*
496
+ █████ ███ █████ ████ █████ █████ ███
497
+ ░░███ ░░░ ░░███ ░░███ ░░███ ░░███ ░░░
498
+ ░███ ████ ███████ ██████ ████████ ██████ ░███ ░███ ░███ ████████ ████ ██████ ████████
499
+ ░███ ░░███ ░░░███░ ███░░███░░███░░███ ░░░░░███ ░███ ░███ ░███ ░░███░░███ ░░███ ███░░███░░███░░███
500
+ ░███ ░███ ░███ ░███████ ░███ ░░░ ███████ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███
501
+ ░███ █ ░███ ░███ ███░███░░░ ░███ ███░░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███
502
+ ███████████ █████ ░░█████ ░░██████ █████ ░░████████ █████ ░░████████ ████ █████ █████░░██████ ████ █████
503
+ ░░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░ ░░░░░ ░░░░░░░░ ░░░░░ ░░░░░░░░ ░░░░ ░░░░░ ░░░░░ ░░░░░░ ░░░░ ░░░░░
504
+ */
505
+
506
+ export type LiteralUnionCodec<
507
+ T extends Codec,
508
+ L extends readonly Literal[],
509
+ > = T extends Codec<infer TApp, infer TProto> ? Codec<TApp, TProto> : never;
510
+
511
+ export const LiteralUnionCodec = <const L extends readonly Literal[]>(
512
+ values: L,
513
+ ): LiteralUnionCodec<Codec<L[number], L[number]>, L> => {
514
+ return {
515
+ encode(app) {
516
+ if (!values.includes(app as L[number])) {
517
+ throw new Error(
518
+ `Expected one of [${values.join(", ")}], got ${String(app)}`,
519
+ );
520
+ }
521
+ return app;
522
+ },
523
+ decode(proto) {
524
+ if (!values.includes(proto as L[number])) {
525
+ throw new Error(
526
+ `Expected one of [${values.join(", ")}], got ${String(proto)}`,
527
+ );
528
+ }
529
+ return proto;
530
+ },
531
+ } as LiteralUnionCodec<Codec<L[number], L[number]>, L>;
532
+ };
533
+
534
+ /*
535
+ █████ █████ ███ █████
536
+ ░░███ ░░███ ░░░ ░░███
537
+ ░███ ░███ ██████ ████████ ████ ██████ ████████ ███████
538
+ ░███ ░███ ░░░░░███ ░░███░░███░░███ ░░░░░███ ░░███░░███ ░░░███░
539
+ ░░███ ███ ███████ ░███ ░░░ ░███ ███████ ░███ ░███ ░███
540
+ ░░░█████░ ███░░███ ░███ ░███ ███░░███ ░███ ░███ ░███ ███
541
+ ░░███ ░░████████ █████ █████░░████████ ████ █████ ░░█████
542
+ ░░░ ░░░░░░░░ ░░░░░ ░░░░░ ░░░░░░░░ ░░░░ ░░░░░ ░░░░░
543
+ */
544
+
545
+ // Maps variant keys to their corresponding decoded types, adding a tag field
546
+ // For example: { _tag: "declareV1", declareV1: { data: string } }
547
+ // if the variant is undefined type, it will be just the tag - { _tag: "heartbeat" }
548
+ type AppVariantMap<TTag extends TPropertyKey, TVariants extends TProperties> = {
549
+ [K in keyof TVariants]: {
550
+ [P in TTag]: K;
551
+ } & (CodecType<TVariants[K]> extends UndefinedCodec
552
+ ? // biome-ignore lint/complexity/noBannedTypes: had to return empty object to satisfy type
553
+ {}
554
+ : { [P in K & TPropertyKey]: CodecType<TVariants[K]> });
555
+ };
556
+ type VariantCodecType<
557
+ TTag extends TPropertyKey,
558
+ TVariants extends TProperties,
559
+ > = AppVariantMap<TTag, TVariants>[keyof TVariants];
560
+
561
+ // Maps variant keys to their corresponding encoded types, adding a discriminator field
562
+ // For example: { $case: "declareV1", declareV1: { data: string } }
563
+ type ProtoVariantMap<
564
+ TDiscriminator extends TPropertyKey,
565
+ TVariants extends TProperties,
566
+ > = {
567
+ [K in keyof TVariants]: {
568
+ [P in TDiscriminator]: K;
569
+ } & {
570
+ [P in K & TPropertyKey]: CodecProto<TVariants[K]>;
571
+ };
572
+ };
573
+
574
+ type VariantCodecProto<
575
+ TDiscriminator extends TPropertyKey,
576
+ TVariants extends TProperties,
577
+ > = ProtoVariantMap<TDiscriminator, TVariants>[keyof TVariants];
578
+
579
+ // Type helper for VariantCodec that preserves the input/output types
580
+ export type VariantCodec<
581
+ T extends Codec,
582
+ TTag extends TPropertyKey,
583
+ TDiscriminator extends TPropertyKey,
584
+ > = T extends Codec<infer TApp, infer TProto> ? Codec<TApp, TProto> : never;
585
+
586
+ export const VariantCodec = <
587
+ TTag extends TPropertyKey,
588
+ TDiscriminator extends TPropertyKey,
589
+ TVariants extends TProperties,
590
+ TCodec extends Codec<
591
+ VariantCodecType<TTag, TVariants>,
592
+ VariantCodecProto<TDiscriminator, TVariants>
593
+ >,
594
+ >(options: {
595
+ tag: TTag;
596
+ discriminator: TDiscriminator;
597
+ variants: TVariants;
598
+ }): VariantCodec<TCodec, TTag, TDiscriminator> => {
599
+ return {
600
+ encode(app) {
601
+ const tag = app[options.tag];
602
+ const codec = options.variants[tag];
603
+ if (!codec) {
604
+ throw new Error(`Unknown variant: ${String(tag)}`);
605
+ }
606
+
607
+ const variantData = app[tag as keyof typeof app];
608
+ const encodedData = codec.encode(variantData);
609
+
610
+ return {
611
+ [options.discriminator]: tag,
612
+ [tag]: encodedData,
613
+ };
614
+ },
615
+ decode(proto) {
616
+ const tag = proto[options.discriminator];
617
+ const codec = options.variants[tag];
618
+ if (!codec) {
619
+ throw new Error(`Unknown variant: ${String(tag)}`);
620
+ }
621
+
622
+ const variantData = proto[tag as keyof typeof proto];
623
+ const decodedData = codec.decode(variantData);
624
+
625
+ return {
626
+ [options.tag]: tag,
627
+ [tag]: decodedData,
628
+ };
629
+ },
630
+ } as VariantCodec<TCodec, TTag, TDiscriminator>;
631
+ };
632
+
633
+ /*
634
+ ███████ ███████ ██████
635
+ ███░░░░░███ ███░░░░░███ ███░░███
636
+ ███ ░░███ ████████ ██████ ███ ░░███ ░███ ░░░
637
+ ░███ ░███░░███░░███ ███░░███░███ ░███ ███████
638
+ ░███ ░███ ░███ ░███ ░███████ ░███ ░███░░░███░
639
+ ░░███ ███ ░███ ░███ ░███░░░ ░░███ ███ ░███
640
+ ░░░███████░ ████ █████░░██████ ░░░███████░ █████
641
+ ░░░░░░░ ░░░░ ░░░░░ ░░░░░░ ░░░░░░░ ░░░░░
642
+
643
+ */
644
+
645
+ export type OneOfCodec<TVariants extends TProperties> = VariantCodec<
646
+ Codec<
647
+ VariantCodecType<"_tag", TVariants>,
648
+ VariantCodecProto<"$case", TVariants>
649
+ >,
650
+ "_tag",
651
+ "$case"
652
+ >;
653
+
654
+ export function OneOfCodec<TVariants extends TProperties>(
655
+ variants: TVariants,
656
+ ): OneOfCodec<TVariants> {
657
+ return VariantCodec({
658
+ tag: "_tag",
659
+ discriminator: "$case",
660
+ variants,
661
+ });
662
+ }