@synnaxlabs/freighter 0.2.0 → 0.4.1

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 (175) hide show
  1. package/.eslintrc.cjs +18 -0
  2. package/LICENSE +4 -21
  3. package/dist/alamos.d.ts +3 -0
  4. package/{build/module/lib → dist}/errors.d.ts +35 -40
  5. package/dist/freighter.cjs.js +11828 -0
  6. package/dist/freighter.cjs.js.map +1 -0
  7. package/dist/freighter.es.js +11828 -0
  8. package/dist/freighter.es.js.map +1 -0
  9. package/dist/http.d.ts +20 -0
  10. package/dist/index.d.ts +8 -0
  11. package/{build/module/lib → dist}/middleware.d.ts +11 -8
  12. package/{build/main/lib → dist}/stream.d.ts +11 -11
  13. package/{build/main/lib → dist}/transport.d.ts +2 -2
  14. package/dist/unary.d.ts +16 -0
  15. package/dist/websocket.d.ts +24 -0
  16. package/package.json +30 -98
  17. package/src/alamos.ts +40 -0
  18. package/src/errors.spec.ts +94 -0
  19. package/src/errors.ts +205 -0
  20. package/src/http.spec.ts +67 -0
  21. package/src/http.ts +115 -0
  22. package/src/index.ts +21 -20
  23. package/src/{lib/middleware.ts → middleware.ts} +29 -19
  24. package/src/{lib/stream.ts → stream.ts} +23 -14
  25. package/src/transport.ts +23 -0
  26. package/src/unary.ts +44 -0
  27. package/src/websocket.spec.ts +119 -0
  28. package/src/websocket.ts +203 -0
  29. package/tsconfig.json +5 -42
  30. package/tsconfig.vite.json +4 -0
  31. package/vite.config.ts +16 -0
  32. package/.editorconfig +0 -15
  33. package/.eslintrc.json +0 -33
  34. package/.gitignore +0 -9
  35. package/.nyc_output/3238f10e-9572-49ec-ab9d-28cbcaa6152a.json +0 -1
  36. package/.nyc_output/4e78a5c9-c0ca-4664-aa04-f478522608eb.json +0 -1
  37. package/.nyc_output/6a2244f2-5aea-45c7-8eeb-e14b454f0096.json +0 -1
  38. package/.nyc_output/dd1075a0-827b-4154-a75e-9bc90a4e16d0.json +0 -1
  39. package/.nyc_output/f829ad27-9bcd-4604-ae57-aae8c6f28d51.json +0 -1
  40. package/.nyc_output/fabc60f1-8fc5-4a1e-bea0-dc1fbcc31c9c.json +0 -1
  41. package/.nyc_output/processinfo/3238f10e-9572-49ec-ab9d-28cbcaa6152a.json +0 -1
  42. package/.nyc_output/processinfo/4e78a5c9-c0ca-4664-aa04-f478522608eb.json +0 -1
  43. package/.nyc_output/processinfo/6a2244f2-5aea-45c7-8eeb-e14b454f0096.json +0 -1
  44. package/.nyc_output/processinfo/dd1075a0-827b-4154-a75e-9bc90a4e16d0.json +0 -1
  45. package/.nyc_output/processinfo/f829ad27-9bcd-4604-ae57-aae8c6f28d51.json +0 -1
  46. package/.nyc_output/processinfo/fabc60f1-8fc5-4a1e-bea0-dc1fbcc31c9c.json +0 -1
  47. package/.nyc_output/processinfo/index.json +0 -1
  48. package/.prettierignore +0 -2
  49. package/.prettierrc +0 -3
  50. package/.vscode/settings.json +0 -4
  51. package/build/main/index.d.ts +0 -9
  52. package/build/main/index.js +0 -29
  53. package/build/main/lib/caseconv.d.ts +0 -2
  54. package/build/main/lib/caseconv.js +0 -14
  55. package/build/main/lib/encoder.d.ts +0 -59
  56. package/build/main/lib/encoder.js +0 -57
  57. package/build/main/lib/encoder.spec.d.ts +0 -1
  58. package/build/main/lib/encoder.spec.js +0 -26
  59. package/build/main/lib/errors.d.ts +0 -87
  60. package/build/main/lib/errors.js +0 -189
  61. package/build/main/lib/errors.spec.js +0 -88
  62. package/build/main/lib/http.d.ts +0 -50
  63. package/build/main/lib/http.js +0 -114
  64. package/build/main/lib/http.spec.js +0 -59
  65. package/build/main/lib/middleware.d.ts +0 -45
  66. package/build/main/lib/middleware.js +0 -38
  67. package/build/main/lib/runtime.d.ts +0 -5
  68. package/build/main/lib/runtime.js +0 -24
  69. package/build/main/lib/stream.js +0 -3
  70. package/build/main/lib/transport.js +0 -3
  71. package/build/main/lib/unary.d.ts +0 -15
  72. package/build/main/lib/unary.js +0 -3
  73. package/build/main/lib/url.d.ts +0 -38
  74. package/build/main/lib/url.js +0 -65
  75. package/build/main/lib/url.spec.d.ts +0 -1
  76. package/build/main/lib/url.spec.js +0 -41
  77. package/build/main/lib/util/log.d.ts +0 -2
  78. package/build/main/lib/util/log.js +0 -15
  79. package/build/main/lib/websocket.d.ts +0 -25
  80. package/build/main/lib/websocket.js +0 -172
  81. package/build/main/lib/websocket.spec.js +0 -86
  82. package/build/main/lib/ws.spec.d.ts +0 -1
  83. package/build/main/lib/ws.spec.js +0 -115
  84. package/build/module/index.d.ts +0 -9
  85. package/build/module/index.js +0 -7
  86. package/build/module/lib/caseconv.d.ts +0 -2
  87. package/build/module/lib/caseconv.js +0 -12
  88. package/build/module/lib/encoder.d.ts +0 -59
  89. package/build/module/lib/encoder.js +0 -47
  90. package/build/module/lib/encoder.spec.d.ts +0 -1
  91. package/build/module/lib/encoder.spec.js +0 -21
  92. package/build/module/lib/errors.js +0 -164
  93. package/build/module/lib/errors.spec.d.ts +0 -1
  94. package/build/module/lib/errors.spec.js +0 -85
  95. package/build/module/lib/http.d.ts +0 -50
  96. package/build/module/lib/http.js +0 -108
  97. package/build/module/lib/http.spec.d.ts +0 -1
  98. package/build/module/lib/http.spec.js +0 -54
  99. package/build/module/lib/middleware.js +0 -32
  100. package/build/module/lib/runtime.d.ts +0 -5
  101. package/build/module/lib/runtime.js +0 -21
  102. package/build/module/lib/stream.d.ts +0 -76
  103. package/build/module/lib/stream.js +0 -2
  104. package/build/module/lib/transport.d.ts +0 -13
  105. package/build/module/lib/transport.js +0 -2
  106. package/build/module/lib/unary.d.ts +0 -15
  107. package/build/module/lib/unary.js +0 -2
  108. package/build/module/lib/url.d.ts +0 -38
  109. package/build/module/lib/url.js +0 -65
  110. package/build/module/lib/url.spec.d.ts +0 -1
  111. package/build/module/lib/url.spec.js +0 -34
  112. package/build/module/lib/util/log.d.ts +0 -2
  113. package/build/module/lib/util/log.js +0 -11
  114. package/build/module/lib/websocket.d.ts +0 -25
  115. package/build/module/lib/websocket.js +0 -181
  116. package/build/module/lib/websocket.spec.d.ts +0 -1
  117. package/build/module/lib/websocket.spec.js +0 -82
  118. package/build/module/lib/ws.spec.d.ts +0 -1
  119. package/build/module/lib/ws.spec.js +0 -94
  120. package/build/tsconfig.module.tsbuildinfo +0 -1
  121. package/build/tsconfig.tsbuildinfo +0 -1
  122. package/coverage/base.css +0 -224
  123. package/coverage/block-navigation.js +0 -87
  124. package/coverage/caseconv.ts.html +0 -124
  125. package/coverage/encoder.ts.html +0 -403
  126. package/coverage/errors.ts.html +0 -727
  127. package/coverage/favicon.png +0 -0
  128. package/coverage/http.ts.html +0 -532
  129. package/coverage/index.html +0 -221
  130. package/coverage/lcov-report/base.css +0 -224
  131. package/coverage/lcov-report/block-navigation.js +0 -87
  132. package/coverage/lcov-report/caseconv.ts.html +0 -124
  133. package/coverage/lcov-report/encoder.ts.html +0 -403
  134. package/coverage/lcov-report/errors.ts.html +0 -727
  135. package/coverage/lcov-report/favicon.png +0 -0
  136. package/coverage/lcov-report/http.ts.html +0 -532
  137. package/coverage/lcov-report/index.html +0 -221
  138. package/coverage/lcov-report/middleware.ts.html +0 -286
  139. package/coverage/lcov-report/prettify.css +0 -1
  140. package/coverage/lcov-report/prettify.js +0 -2
  141. package/coverage/lcov-report/runtime.ts.html +0 -154
  142. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  143. package/coverage/lcov-report/sorter.js +0 -196
  144. package/coverage/lcov-report/url.ts.html +0 -322
  145. package/coverage/lcov-report/websocket.ts.html +0 -760
  146. package/coverage/lcov.info +0 -552
  147. package/coverage/middleware.ts.html +0 -286
  148. package/coverage/prettify.css +0 -1
  149. package/coverage/prettify.js +0 -2
  150. package/coverage/runtime.ts.html +0 -154
  151. package/coverage/sort-arrow-sprite.png +0 -0
  152. package/coverage/sorter.js +0 -196
  153. package/coverage/url.ts.html +0 -322
  154. package/coverage/websocket.ts.html +0 -760
  155. package/src/lib/caseconv.ts +0 -13
  156. package/src/lib/encoder.spec.ts +0 -23
  157. package/src/lib/encoder.ts +0 -105
  158. package/src/lib/errors.spec.ts +0 -98
  159. package/src/lib/errors.ts +0 -214
  160. package/src/lib/http.spec.ts +0 -85
  161. package/src/lib/http.ts +0 -149
  162. package/src/lib/runtime.ts +0 -23
  163. package/src/lib/transport.ts +0 -14
  164. package/src/lib/unary.ts +0 -21
  165. package/src/lib/url.spec.ts +0 -37
  166. package/src/lib/url.ts +0 -79
  167. package/src/lib/util/log.ts +0 -12
  168. package/src/lib/websocket.spec.ts +0 -106
  169. package/src/lib/websocket.ts +0 -225
  170. package/src/types/example.d.ts +0 -24
  171. package/tsconfig.module.json +0 -9
  172. package/yarn.lock +0 -5878
  173. /package/{build/main/lib → dist}/errors.spec.d.ts +0 -0
  174. /package/{build/main/lib → dist}/http.spec.d.ts +0 -0
  175. /package/{build/main/lib → dist}/websocket.spec.d.ts +0 -0
@@ -1,13 +0,0 @@
1
- import {
2
- camelKeys as _camelKeys,
3
- snakeKeys as _snakeKeys,
4
- } from 'js-convert-case';
5
-
6
- const options = {
7
- recursive: true,
8
- recursiveInArray: true,
9
- keepTypesOnRecursion: [Number, String, Uint8Array],
10
- };
11
-
12
- export const snakeKeys = (entity: unknown) => _snakeKeys(entity, options);
13
- export const camelKeys = (entity: unknown) => _camelKeys(entity, options);
@@ -1,23 +0,0 @@
1
- import test from 'ava';
2
- import { z } from 'zod';
3
-
4
- import { ENCODERS } from './encoder';
5
-
6
- const SampleSchema = z.object({
7
- channelKey: z.string(),
8
- timeStamp: z.number(),
9
- value: z.unknown(),
10
- });
11
-
12
- ENCODERS.forEach((encoder) => {
13
- test(`[encoder] - encoder ${encoder.contentType}`, (t) => {
14
- const sample = {
15
- channelKey: 'test',
16
- timeStamp: 123,
17
- value: new Uint8Array([1, 2, 3]),
18
- };
19
- const encoded = encoder.encode(sample);
20
- const decoded = encoder.decode(encoded, SampleSchema);
21
- t.deepEqual(decoded, decoded);
22
- });
23
- });
@@ -1,105 +0,0 @@
1
- import { addExtension, pack, unpack } from 'msgpackr';
2
- import { ZodSchema } from 'zod';
3
-
4
- import { camelKeys, snakeKeys } from './caseconv';
5
-
6
- /**
7
- * CustomTypeEncoder is an interface for a class that needs to transform its
8
- * value before encoding.
9
- */
10
- interface CustomTypeEncoder {
11
- /** The Class the custom encoder is set for */
12
- // eslint-disable-next-line @typescript-eslint/ban-types
13
- Class: Function;
14
-
15
- /**
16
- * The function that transforms the value before encoding;
17
- *
18
- * @param instance - The instance of the class to transform.
19
- * @returns The transformed value.
20
- */
21
- write<P>(instance: P): unknown;
22
- }
23
-
24
- /**
25
- * EncoderDecoder is an entity that encodes and decodes messages to and from a
26
- * binary format.
27
- */
28
- export interface EncoderDecoder {
29
- /** The HTTP content type of the encoder */
30
- contentType: string;
31
-
32
- /**
33
- * Encodes the given payload into a binary representation.
34
- *
35
- * @param payload - The payload to encode.
36
- * @returns An ArrayBuffer containing the encoded payload.
37
- */
38
- encode(payload: unknown): ArrayBuffer;
39
-
40
- /**
41
- * Decodes the given binary representation into a type checked payload.
42
- *
43
- * @param data - The data to decode.
44
- * @param schema - The schema to decode the data with.
45
- */
46
- decode<P>(data: Uint8Array | ArrayBuffer, schema: ZodSchema<P>): P;
47
- }
48
-
49
- interface StaticEncoderDecoder {
50
- registerCustomType(encoder: CustomTypeEncoder): void;
51
- }
52
-
53
- /** MsgpackEncoderDecoder is a msgpack implementation of EncoderDecoder. */
54
- export class MsgpackEncoderDecoder implements EncoderDecoder {
55
- contentType = 'application/msgpack';
56
-
57
- encode(payload: unknown): ArrayBuffer {
58
- return pack(snakeKeys(payload));
59
- }
60
-
61
- decode<P>(data: Uint8Array, schema: ZodSchema<P>): P {
62
- return schema.parse(camelKeys(unpack(new Uint8Array(data))));
63
- }
64
-
65
- static registerCustomType(encoder: CustomTypeEncoder): void {
66
- addExtension({ type: 0, ...encoder });
67
- }
68
- }
69
-
70
- /** JSONEncoderDecoder is a JSON implementation of EncoderDecoder. */
71
- export class JSONEncoderDecoder implements EncoderDecoder {
72
- contentType = 'application/json';
73
-
74
- encode(payload: unknown): ArrayBuffer {
75
- const json = JSON.stringify(snakeKeys(payload), (_, v) => {
76
- if (ArrayBuffer.isView(v)) return Array.from(v as Uint8Array);
77
- return v;
78
- });
79
- return new TextEncoder().encode(json);
80
- }
81
-
82
- decode<P>(data: Uint8Array, schema: ZodSchema<P>): P {
83
- return schema.parse(camelKeys(JSON.parse(new TextDecoder().decode(data))));
84
- }
85
-
86
- static registerCustomType(): void {
87
- return;
88
- }
89
- }
90
-
91
- export const ENCODERS: EncoderDecoder[] = [
92
- new MsgpackEncoderDecoder(),
93
- new JSONEncoderDecoder(),
94
- ];
95
-
96
- export const ENCODER_CLASSES: StaticEncoderDecoder[] = [
97
- MsgpackEncoderDecoder,
98
- JSONEncoderDecoder,
99
- ];
100
-
101
- export const registerCustomTypeEncoder = (encoder: CustomTypeEncoder): void => {
102
- ENCODER_CLASSES.forEach((encoderClass) => {
103
- encoderClass.registerCustomType(encoder);
104
- });
105
- };
@@ -1,98 +0,0 @@
1
- import test from 'ava';
2
-
3
- import {
4
- assertErrorType,
5
- BaseTypedError,
6
- decodeError,
7
- encodeError,
8
- EOF,
9
- FREIGHTER,
10
- isTypedError,
11
- NONE,
12
- registerError,
13
- StreamClosed,
14
- TypedError,
15
- UNKNOWN,
16
- UnknownError,
17
- Unreachable,
18
- } from './errors';
19
-
20
- class MyCustomError extends BaseTypedError {
21
- constructor(message: string) {
22
- super(message, 'MyCustomError');
23
- }
24
- }
25
-
26
- const myCustomErrorEncoder = (error: MyCustomError): string => {
27
- return error.message;
28
- };
29
-
30
- const myCustomErrorDecoder = (encoded: string): TypedError => {
31
- return new MyCustomError(encoded);
32
- };
33
-
34
- test('isTypedError', (t) => {
35
- const error = new MyCustomError('test');
36
- const fError = isTypedError(error);
37
- t.is(fError, true);
38
- t.is(error.type, 'MyCustomError');
39
- });
40
-
41
- test('encoding and decoding a custom error through registry', (t) => {
42
- registerError({
43
- type: 'MyCustomError',
44
- encode: myCustomErrorEncoder,
45
- decode: myCustomErrorDecoder,
46
- });
47
- const error = new MyCustomError('test');
48
- const encoded = encodeError(error);
49
- t.is(encoded.type, 'MyCustomError');
50
- t.is(encoded.data, 'test');
51
- const decoded = assertErrorType<MyCustomError>(
52
- 'MyCustomError',
53
- decodeError(encoded)
54
- );
55
- t.is(decoded.message, 'test');
56
- });
57
-
58
- test('encoding and decoding a null error', (t) => {
59
- const encoded = encodeError(null);
60
- t.is(encoded.type, NONE);
61
- t.is(encoded.data, '');
62
- const decoded = decodeError(encoded);
63
- t.is(decoded, undefined);
64
- });
65
-
66
- test('encoding and decoding an unrecognized error', (t) => {
67
- const error = new Error('test');
68
- const encoded = encodeError(error);
69
- t.is(encoded.type, UNKNOWN);
70
- t.is(encoded.data, '{}');
71
- const decoded = decodeError(encoded);
72
- t.deepEqual(decoded, new UnknownError('{}'));
73
- });
74
-
75
- test('registering duplicate error should throw', (t) => {
76
- registerError({
77
- type: 'MyDuplicateError',
78
- encode: myCustomErrorEncoder,
79
- decode: myCustomErrorDecoder,
80
- });
81
- t.throws(() => {
82
- registerError({
83
- type: 'MyDuplicateError',
84
- encode: myCustomErrorEncoder,
85
- decode: myCustomErrorDecoder,
86
- });
87
- });
88
- });
89
-
90
- test('encoding and decoding freighter errors', (t) => {
91
- [new EOF(), new StreamClosed(), new Unreachable()].forEach((error) => {
92
- const encoded = encodeError(error);
93
- t.is(encoded.type, FREIGHTER);
94
- t.is(encoded.data, error.message);
95
- const decoded = decodeError(encoded);
96
- t.deepEqual(decoded, error);
97
- });
98
- });
package/src/lib/errors.ts DELETED
@@ -1,214 +0,0 @@
1
- import { z } from 'zod';
2
-
3
- export interface TypedError extends Error {
4
- discriminator: 'FreighterError';
5
- /**
6
- * @description Returns a unique type identifier for the error. Freighter uses this to
7
- * determine the correct decoder to use on the other end of the freighter.
8
- */
9
- type: string;
10
- }
11
-
12
- export class BaseTypedError extends Error implements TypedError {
13
- discriminator: 'FreighterError' = 'FreighterError';
14
- type: string;
15
-
16
- constructor(message: string, type: string) {
17
- super(message);
18
- this.type = type;
19
- }
20
- }
21
-
22
- type ErrorDecoder = (encoded: string) => Error | undefined;
23
- type ErrorEncoder = (error: TypedError) => string;
24
-
25
- export const isTypedError = (error: unknown): error is TypedError => {
26
- if (!error || typeof error !== 'object') {
27
- return false;
28
- }
29
- const typedError = error as TypedError;
30
- if (typedError.discriminator !== 'FreighterError') {
31
- return false;
32
- }
33
- if (!('type' in typedError)) {
34
- throw new Error(
35
- `Freighter error is missing its type property: ${typedError}`
36
- );
37
- }
38
- return true;
39
- };
40
-
41
- export const assertErrorType = <T>(type: string, error?: Error): T => {
42
- if (!error) {
43
- throw new Error(`Expected error of type ${type} but got nothing instead`);
44
- }
45
- if (!isTypedError(error)) {
46
- throw new Error(`Expected a typed error, got: ${error}`);
47
- }
48
- if (error.type !== type) {
49
- throw new Error(
50
- `Expected error of type ${type}, got ${error.type}: ${error}`
51
- );
52
- }
53
- return error as unknown as T;
54
- };
55
-
56
- export const UNKNOWN = 'unknown';
57
- export const NONE = 'nil';
58
- export const FREIGHTER = 'freighter';
59
-
60
- export const ErrorPayloadSchema = z.object({
61
- type: z.string(),
62
- data: z.string(),
63
- });
64
-
65
- export type ErrorPayload = z.infer<typeof ErrorPayloadSchema>;
66
-
67
- type errorProvider = {
68
- encode: ErrorEncoder;
69
- decode: ErrorDecoder;
70
- };
71
-
72
- class Registry {
73
- private readonly entries: { [type: string]: errorProvider };
74
-
75
- constructor() {
76
- this.entries = {};
77
- }
78
-
79
- register(_type: string, provider: errorProvider) {
80
- if (this.entries[_type]) {
81
- throw new Error(`Error type ${_type} is already registered`);
82
- }
83
- this.entries[_type] = provider;
84
- }
85
-
86
- encode(error: unknown): ErrorPayload {
87
- if (!error) {
88
- return { type: NONE, data: '' };
89
- }
90
- if (isTypedError(error) && this.entries[error.type]) {
91
- return { type: error.type, data: this.entries[error.type].encode(error) };
92
- }
93
- return { type: UNKNOWN, data: JSON.stringify(error) };
94
- }
95
-
96
- decode(payload: ErrorPayload): Error | undefined {
97
- if (payload.type === NONE) {
98
- return undefined;
99
- }
100
-
101
- if (payload.type === UNKNOWN) {
102
- return new UnknownError(payload.data);
103
- }
104
-
105
- const provider = this.entries[payload.type];
106
- if (!provider) {
107
- return new UnknownError(payload.data);
108
- }
109
- return provider.decode(payload.data);
110
- }
111
- }
112
-
113
- const REGISTRY = new Registry();
114
-
115
- /**
116
- * Registers a custom error type with the error registry, which allows it to be
117
- * encoded/decoded and sent over the network.
118
- *
119
- * @param type - A unique string identifier for the error type.
120
- * @param encode - A function that encodes the error into a string.
121
- * @param decode - A function that decodes the error from a string.
122
- */
123
- export const registerError = ({
124
- type,
125
- encode,
126
- decode,
127
- }: {
128
- type: string;
129
- encode: ErrorEncoder;
130
- decode: ErrorDecoder;
131
- }) => {
132
- REGISTRY.register(type, { encode, decode });
133
- };
134
-
135
- /**
136
- * Encodes an error into a payload that can be sent between a freighter server
137
- * and client.
138
- * @param error - The error to encode.
139
- * @returns The encoded error.
140
- */
141
- export const encodeError = (error: unknown): ErrorPayload => {
142
- return REGISTRY.encode(error);
143
- };
144
-
145
- /**
146
- * Decodes an error payload into an exception. If a custom decoder can be found
147
- * for the error type, it will be used. Otherwise, a generic Error containing
148
- * the error data is returned.
149
- *
150
- * @param payload - The encoded error payload.
151
- * @returns The decoded error.
152
- */
153
- export const decodeError = (payload: ErrorPayload): Error | undefined => {
154
- return REGISTRY.decode(payload);
155
- };
156
-
157
- export class UnknownError extends BaseTypedError implements TypedError {
158
- constructor(message: string) {
159
- super(message, UNKNOWN);
160
- }
161
- }
162
-
163
- /** Thrown/returned when a stream closed normally. */
164
- export class EOF extends BaseTypedError implements TypedError {
165
- constructor() {
166
- super('EOF', FREIGHTER);
167
- }
168
- }
169
-
170
- /** Thrown/returned when a stream is closed abnormally. */
171
- export class StreamClosed extends BaseTypedError implements TypedError {
172
- constructor() {
173
- super('StreamClosed', FREIGHTER);
174
- }
175
- }
176
-
177
- /** Thrown when a target is unreachable. */
178
- export class Unreachable extends BaseTypedError implements TypedError {
179
- constructor() {
180
- super('Unreachable', FREIGHTER);
181
- }
182
- }
183
-
184
- const freighterErrorEncoder: ErrorEncoder = (error: TypedError) => {
185
- if (error instanceof EOF) {
186
- return 'EOF';
187
- }
188
- if (error instanceof StreamClosed) {
189
- return 'StreamClosed';
190
- }
191
- if (error instanceof Unreachable) {
192
- return 'Unreachable';
193
- }
194
- throw new Error(`Unknown error type: ${error}`);
195
- };
196
-
197
- const freighterErrorDecoder: ErrorDecoder = (encoded: string) => {
198
- switch (encoded) {
199
- case 'EOF':
200
- return new EOF();
201
- case 'StreamClosed':
202
- return new StreamClosed();
203
- case 'Unreachable':
204
- return new Unreachable();
205
- default:
206
- throw new Error(`Unknown error type: ${encoded}`);
207
- }
208
- };
209
-
210
- registerError({
211
- type: FREIGHTER,
212
- encode: freighterErrorEncoder,
213
- decode: freighterErrorDecoder,
214
- });
@@ -1,85 +0,0 @@
1
- import test from 'ava';
2
- import { z } from 'zod';
3
-
4
- import { JSONEncoderDecoder } from './encoder';
5
- import { HTTPClientFactory } from './http';
6
- import URL from './url';
7
-
8
- const ENDPOINT = new URL({
9
- host: '127.0.0.1',
10
- port: 8080,
11
- pathPrefix: 'unary',
12
- });
13
-
14
- const factory = new HTTPClientFactory(ENDPOINT, new JSONEncoderDecoder());
15
-
16
- const MessageSchema = z.object({
17
- id: z.number().optional(),
18
- message: z.string().optional(),
19
- });
20
-
21
- type Message = z.infer<typeof MessageSchema>;
22
-
23
- const getClient = factory.getClient();
24
- const postClient = factory.postClient();
25
-
26
- test('[http] - post echo', async (t) => {
27
- const [response, error] = await postClient.send<Message, Message>(
28
- '/echo',
29
- {
30
- id: 1,
31
- message: 'hello',
32
- },
33
- MessageSchema
34
- );
35
- t.is(error, undefined);
36
- t.deepEqual(response, { id: 2, message: 'hello' });
37
- });
38
-
39
- test('[http] - get echo', async (t) => {
40
- const [response, error] = await getClient.send<Message, Message>(
41
- '/echo',
42
- {
43
- id: 1,
44
- message: 'hello',
45
- },
46
- MessageSchema
47
- );
48
- t.is(error, undefined);
49
- t.deepEqual(response, { id: 2, message: 'hello' });
50
- });
51
-
52
- test('[http] - get not found', async (t) => {
53
- const [response, error] = await getClient.send<Message, Message>(
54
- '/not-found',
55
- {},
56
- MessageSchema
57
- );
58
- t.is(error?.message, 'Cannot GET /unary/not-found');
59
- t.is(response, undefined);
60
- });
61
-
62
- test('[http] - post not found', async (t) => {
63
- const [response, error] = await postClient.send<Message, Message>(
64
- '/not-found',
65
- {},
66
- MessageSchema
67
- );
68
- t.is(error?.message, 'Cannot POST /unary/not-found');
69
- t.is(response, undefined);
70
- });
71
-
72
- test('[http] - middleware', async (t) => {
73
- const client = factory.getClient();
74
- client.use(async (md, next) => {
75
- md.params['Test'] = 'test';
76
- return await next(md);
77
- });
78
- const [response, error] = await client.send<Message, Message>(
79
- '/middlewareCheck',
80
- {},
81
- MessageSchema
82
- );
83
- t.is(error, undefined);
84
- t.is(response?.message, '');
85
- });
package/src/lib/http.ts DELETED
@@ -1,149 +0,0 @@
1
- import axios, { AxiosRequestConfig } from 'axios';
2
- import { ZodSchema } from 'zod';
3
-
4
- import { EncoderDecoder } from './encoder';
5
- import { decodeError, ErrorPayloadSchema } from './errors';
6
- import { MetaData, MiddlewareCollector } from './middleware';
7
- import { UnaryClient } from './unary';
8
- import URL from './url';
9
-
10
- /**
11
- * HTTPClientFactory provides a POST and GET implementation of the Unary
12
- * protocol.
13
- *
14
- * @param url - The base URL of the API.
15
- * @param encoder - The encoder/decoder to use for the request/response.
16
- */
17
- export class HTTPClientFactory extends MiddlewareCollector {
18
- endpoint: URL;
19
- encoder: EncoderDecoder;
20
-
21
- constructor(endpoint: URL, encoder: EncoderDecoder) {
22
- super();
23
- this.endpoint = endpoint;
24
- this.encoder = encoder;
25
- }
26
-
27
- getClient(): GETClient {
28
- const gc = new GETClient(this.endpoint, this.encoder);
29
- gc.use(...this.middleware);
30
- return gc;
31
- }
32
-
33
- postClient(): POSTClient {
34
- const pc = new POSTClient(this.endpoint, this.encoder);
35
- pc.use(...this.middleware);
36
- return pc;
37
- }
38
- }
39
-
40
- export const CONTENT_TYPE_HEADER_KEY = 'Content-Type';
41
-
42
- class Core extends MiddlewareCollector {
43
- endpoint: URL;
44
- encoder: EncoderDecoder;
45
-
46
- constructor(endpoint: URL, encoder: EncoderDecoder) {
47
- super();
48
- this.endpoint = endpoint.replace({ protocol: 'http' });
49
- this.encoder = encoder;
50
- }
51
-
52
- get headers() {
53
- return {
54
- [CONTENT_TYPE_HEADER_KEY]: this.encoder.contentType,
55
- };
56
- }
57
-
58
- requestConfig(): AxiosRequestConfig {
59
- return {
60
- headers: this.headers,
61
- responseType: 'arraybuffer',
62
- withCredentials: false,
63
- validateStatus: () => true,
64
- };
65
- }
66
-
67
- async execute<RS>(
68
- request: AxiosRequestConfig,
69
- resSchema: ZodSchema<RS>
70
- ): Promise<[RS | undefined, Error | undefined]> {
71
- let rs: RS | undefined = undefined;
72
-
73
- if (!request.url)
74
- throw new Error('[freighter.http] - expected valid request url');
75
-
76
- const err = await this.executeMiddleware(
77
- { target: request.url, protocol: 'http', params: {} },
78
- async (md: MetaData): Promise<Error | undefined> => {
79
- request.headers = { ...request.headers, ...this.headers, ...md.params };
80
- const httpRes = await axios.request(request);
81
- if (httpRes.status < 200 || httpRes.status >= 300) {
82
- try {
83
- const err = this.encoder.decode(httpRes.data, ErrorPayloadSchema);
84
- return decodeError(err);
85
- } catch {
86
- return new Error(httpRes.data);
87
- }
88
- }
89
- rs = this.encoder.decode(httpRes.data, resSchema);
90
- return undefined;
91
- }
92
- );
93
-
94
- return [rs, err];
95
- }
96
- }
97
-
98
- /**
99
- * Implementation of the UnaryClient protocol backed by HTTP GET requests. It
100
- * should not be instantiated directly, but through the HTTPClientFactory.
101
- */
102
- export class GETClient extends Core implements UnaryClient {
103
- async send<RQ, RS>(
104
- target: string,
105
- req: RQ,
106
- resSchema: ZodSchema<RS>
107
- ): Promise<[RS | undefined, Error | undefined]> {
108
- const request = this.requestConfig();
109
- request.method = 'GET';
110
- request.url =
111
- this.endpoint.child(target).stringify() +
112
- buildQueryString({ request: req as Record<string, unknown> });
113
- return await this.execute(request, resSchema);
114
- }
115
- }
116
-
117
- /**
118
- * Implementation of the UnaryClient protocol backed by HTTP POST requests. It
119
- * should not be instantiated directly, but through the HTTPClientFactory.
120
- */
121
- export class POSTClient extends Core implements UnaryClient {
122
- async send<RQ, RS>(
123
- target: string,
124
- req: RQ,
125
- resSchema: ZodSchema<RS>
126
- ): Promise<[RS | undefined, Error | undefined]> {
127
- const url = this.endpoint.child(target).stringify();
128
- const request = this.requestConfig();
129
- request.method = 'POST';
130
- request.url = url;
131
- request.data = this.encoder.encode(req);
132
- return await this.execute(request, resSchema);
133
- }
134
- }
135
-
136
- export const buildQueryString = ({
137
- request,
138
- prefix = '',
139
- }: {
140
- request: Record<string, unknown>;
141
- prefix?: string;
142
- }) => {
143
- return (
144
- '?' +
145
- Object.keys(request)
146
- .map((key) => `${prefix}${key}=${request[key]}`)
147
- .join('&')
148
- );
149
- };
@@ -1,23 +0,0 @@
1
- export enum Runtime {
2
- Browser = 'browser',
3
- Node = 'node',
4
- }
5
-
6
- const detectRuntime = (): Runtime => {
7
- if (
8
- typeof process !== 'undefined' &&
9
- process.versions != null &&
10
- process.versions.node != null
11
- ) {
12
- return Runtime.Node;
13
- }
14
-
15
- if (typeof window !== 'undefined' && typeof window.document !== 'undefined') {
16
- return Runtime.Browser;
17
- }
18
-
19
- console.warn('Freighter unable to safely detect runtime, assuming browser');
20
- return Runtime.Browser;
21
- };
22
-
23
- export const RUNTIME = detectRuntime();