@alchemy/common 0.0.0-alpha.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.
Files changed (212) hide show
  1. package/LICENSE +21 -0
  2. package/dist/esm/actions/addBreadCrumb.d.ts +14 -0
  3. package/dist/esm/actions/addBreadCrumb.js +27 -0
  4. package/dist/esm/actions/addBreadCrumb.js.map +1 -0
  5. package/dist/esm/chains.d.ts +234 -0
  6. package/dist/esm/chains.js +113 -0
  7. package/dist/esm/chains.js.map +1 -0
  8. package/dist/esm/errors/AccountNotFoundError.d.ts +10 -0
  9. package/dist/esm/errors/AccountNotFoundError.js +19 -0
  10. package/dist/esm/errors/AccountNotFoundError.js.map +1 -0
  11. package/dist/esm/errors/BaseError.d.ts +23 -0
  12. package/dist/esm/errors/BaseError.js +40 -0
  13. package/dist/esm/errors/BaseError.js.map +1 -0
  14. package/dist/esm/errors/ChainNotFoundError.d.ts +11 -0
  15. package/dist/esm/errors/ChainNotFoundError.js +19 -0
  16. package/dist/esm/errors/ChainNotFoundError.js.map +1 -0
  17. package/dist/esm/errors/ConnectionConfigError.d.ts +13 -0
  18. package/dist/esm/errors/ConnectionConfigError.js +25 -0
  19. package/dist/esm/errors/ConnectionConfigError.js.map +1 -0
  20. package/dist/esm/errors/FetchError.d.ts +15 -0
  21. package/dist/esm/errors/FetchError.js +25 -0
  22. package/dist/esm/errors/FetchError.js.map +1 -0
  23. package/dist/esm/errors/InvalidRequestError.d.ts +13 -0
  24. package/dist/esm/errors/InvalidRequestError.js +22 -0
  25. package/dist/esm/errors/InvalidRequestError.js.map +1 -0
  26. package/dist/esm/errors/MethodUnsupportedError.d.ts +13 -0
  27. package/dist/esm/errors/MethodUnsupportedError.js +21 -0
  28. package/dist/esm/errors/MethodUnsupportedError.js.map +1 -0
  29. package/dist/esm/errors/ServerError.d.ts +15 -0
  30. package/dist/esm/errors/ServerError.js +25 -0
  31. package/dist/esm/errors/ServerError.js.map +1 -0
  32. package/dist/esm/index.d.ts +29 -0
  33. package/dist/esm/index.js +27 -0
  34. package/dist/esm/index.js.map +1 -0
  35. package/dist/esm/logging/config.d.ts +190 -0
  36. package/dist/esm/logging/config.js +279 -0
  37. package/dist/esm/logging/config.js.map +1 -0
  38. package/dist/esm/logging/index.d.ts +6 -0
  39. package/dist/esm/logging/index.js +5 -0
  40. package/dist/esm/logging/index.js.map +1 -0
  41. package/dist/esm/logging/local.d.ts +10 -0
  42. package/dist/esm/logging/local.js +35 -0
  43. package/dist/esm/logging/local.js.map +1 -0
  44. package/dist/esm/logging/logger.d.ts +80 -0
  45. package/dist/esm/logging/logger.js +111 -0
  46. package/dist/esm/logging/logger.js.map +1 -0
  47. package/dist/esm/logging/noop.d.ts +6 -0
  48. package/dist/esm/logging/noop.js +12 -0
  49. package/dist/esm/logging/noop.js.map +1 -0
  50. package/dist/esm/logging/sinks.d.ts +90 -0
  51. package/dist/esm/logging/sinks.js +111 -0
  52. package/dist/esm/logging/sinks.js.map +1 -0
  53. package/dist/esm/logging/types.d.ts +96 -0
  54. package/dist/esm/logging/types.js +2 -0
  55. package/dist/esm/logging/types.js.map +1 -0
  56. package/dist/esm/logging/utils.d.ts +7 -0
  57. package/dist/esm/logging/utils.js +21 -0
  58. package/dist/esm/logging/utils.js.map +1 -0
  59. package/dist/esm/rest/restClient.d.ts +34 -0
  60. package/dist/esm/rest/restClient.js +55 -0
  61. package/dist/esm/rest/restClient.js.map +1 -0
  62. package/dist/esm/rest/types.d.ts +24 -0
  63. package/dist/esm/rest/types.js +2 -0
  64. package/dist/esm/rest/types.js.map +1 -0
  65. package/dist/esm/tracing/traceHeader.d.ts +82 -0
  66. package/dist/esm/tracing/traceHeader.js +145 -0
  67. package/dist/esm/tracing/traceHeader.js.map +1 -0
  68. package/dist/esm/tracing/updateHeaders.d.ts +24 -0
  69. package/dist/esm/tracing/updateHeaders.js +61 -0
  70. package/dist/esm/tracing/updateHeaders.js.map +1 -0
  71. package/dist/esm/transport/alchemy.d.ts +110 -0
  72. package/dist/esm/transport/alchemy.js +164 -0
  73. package/dist/esm/transport/alchemy.js.map +1 -0
  74. package/dist/esm/transport/chainRegistry.d.ts +31 -0
  75. package/dist/esm/transport/chainRegistry.js +95 -0
  76. package/dist/esm/transport/chainRegistry.js.map +1 -0
  77. package/dist/esm/transport/connection.d.ts +20 -0
  78. package/dist/esm/transport/connection.js +2 -0
  79. package/dist/esm/transport/connection.js.map +1 -0
  80. package/dist/esm/transport/connectionSchema.d.ts +124 -0
  81. package/dist/esm/transport/connectionSchema.js +121 -0
  82. package/dist/esm/transport/connectionSchema.js.map +1 -0
  83. package/dist/esm/utils/assertNever.d.ts +8 -0
  84. package/dist/esm/utils/assertNever.js +12 -0
  85. package/dist/esm/utils/assertNever.js.map +1 -0
  86. package/dist/esm/utils/bigint.d.ts +24 -0
  87. package/dist/esm/utils/bigint.js +37 -0
  88. package/dist/esm/utils/bigint.js.map +1 -0
  89. package/dist/esm/utils/createEip1193HandlerFactory.d.ts +18 -0
  90. package/dist/esm/utils/createEip1193HandlerFactory.js +11 -0
  91. package/dist/esm/utils/createEip1193HandlerFactory.js.map +1 -0
  92. package/dist/esm/utils/headers.d.ts +7 -0
  93. package/dist/esm/utils/headers.js +29 -0
  94. package/dist/esm/utils/headers.js.map +1 -0
  95. package/dist/esm/utils/lowerAddress.d.ts +8 -0
  96. package/dist/esm/utils/lowerAddress.js +9 -0
  97. package/dist/esm/utils/lowerAddress.js.map +1 -0
  98. package/dist/esm/utils/raise.d.ts +8 -0
  99. package/dist/esm/utils/raise.js +14 -0
  100. package/dist/esm/utils/raise.js.map +1 -0
  101. package/dist/esm/utils/types.d.ts +10 -0
  102. package/dist/esm/utils/types.js +2 -0
  103. package/dist/esm/utils/types.js.map +1 -0
  104. package/dist/esm/version.d.ts +1 -0
  105. package/dist/esm/version.js +4 -0
  106. package/dist/esm/version.js.map +1 -0
  107. package/dist/types/actions/addBreadCrumb.d.ts +15 -0
  108. package/dist/types/actions/addBreadCrumb.d.ts.map +1 -0
  109. package/dist/types/chains.d.ts +235 -0
  110. package/dist/types/chains.d.ts.map +1 -0
  111. package/dist/types/errors/AccountNotFoundError.d.ts +11 -0
  112. package/dist/types/errors/AccountNotFoundError.d.ts.map +1 -0
  113. package/dist/types/errors/BaseError.d.ts +24 -0
  114. package/dist/types/errors/BaseError.d.ts.map +1 -0
  115. package/dist/types/errors/ChainNotFoundError.d.ts +12 -0
  116. package/dist/types/errors/ChainNotFoundError.d.ts.map +1 -0
  117. package/dist/types/errors/ConnectionConfigError.d.ts +14 -0
  118. package/dist/types/errors/ConnectionConfigError.d.ts.map +1 -0
  119. package/dist/types/errors/FetchError.d.ts +16 -0
  120. package/dist/types/errors/FetchError.d.ts.map +1 -0
  121. package/dist/types/errors/InvalidRequestError.d.ts +14 -0
  122. package/dist/types/errors/InvalidRequestError.d.ts.map +1 -0
  123. package/dist/types/errors/MethodUnsupportedError.d.ts +14 -0
  124. package/dist/types/errors/MethodUnsupportedError.d.ts.map +1 -0
  125. package/dist/types/errors/ServerError.d.ts +16 -0
  126. package/dist/types/errors/ServerError.d.ts.map +1 -0
  127. package/dist/types/index.d.ts +30 -0
  128. package/dist/types/index.d.ts.map +1 -0
  129. package/dist/types/logging/config.d.ts +191 -0
  130. package/dist/types/logging/config.d.ts.map +1 -0
  131. package/dist/types/logging/index.d.ts +7 -0
  132. package/dist/types/logging/index.d.ts.map +1 -0
  133. package/dist/types/logging/local.d.ts +11 -0
  134. package/dist/types/logging/local.d.ts.map +1 -0
  135. package/dist/types/logging/logger.d.ts +81 -0
  136. package/dist/types/logging/logger.d.ts.map +1 -0
  137. package/dist/types/logging/noop.d.ts +7 -0
  138. package/dist/types/logging/noop.d.ts.map +1 -0
  139. package/dist/types/logging/sinks.d.ts +91 -0
  140. package/dist/types/logging/sinks.d.ts.map +1 -0
  141. package/dist/types/logging/types.d.ts +97 -0
  142. package/dist/types/logging/types.d.ts.map +1 -0
  143. package/dist/types/logging/utils.d.ts +8 -0
  144. package/dist/types/logging/utils.d.ts.map +1 -0
  145. package/dist/types/rest/restClient.d.ts +35 -0
  146. package/dist/types/rest/restClient.d.ts.map +1 -0
  147. package/dist/types/rest/types.d.ts +25 -0
  148. package/dist/types/rest/types.d.ts.map +1 -0
  149. package/dist/types/tracing/traceHeader.d.ts +83 -0
  150. package/dist/types/tracing/traceHeader.d.ts.map +1 -0
  151. package/dist/types/tracing/updateHeaders.d.ts +25 -0
  152. package/dist/types/tracing/updateHeaders.d.ts.map +1 -0
  153. package/dist/types/transport/alchemy.d.ts +111 -0
  154. package/dist/types/transport/alchemy.d.ts.map +1 -0
  155. package/dist/types/transport/chainRegistry.d.ts +32 -0
  156. package/dist/types/transport/chainRegistry.d.ts.map +1 -0
  157. package/dist/types/transport/connection.d.ts +21 -0
  158. package/dist/types/transport/connection.d.ts.map +1 -0
  159. package/dist/types/transport/connectionSchema.d.ts +125 -0
  160. package/dist/types/transport/connectionSchema.d.ts.map +1 -0
  161. package/dist/types/utils/assertNever.d.ts +9 -0
  162. package/dist/types/utils/assertNever.d.ts.map +1 -0
  163. package/dist/types/utils/bigint.d.ts +25 -0
  164. package/dist/types/utils/bigint.d.ts.map +1 -0
  165. package/dist/types/utils/createEip1193HandlerFactory.d.ts +19 -0
  166. package/dist/types/utils/createEip1193HandlerFactory.d.ts.map +1 -0
  167. package/dist/types/utils/headers.d.ts +8 -0
  168. package/dist/types/utils/headers.d.ts.map +1 -0
  169. package/dist/types/utils/lowerAddress.d.ts +9 -0
  170. package/dist/types/utils/lowerAddress.d.ts.map +1 -0
  171. package/dist/types/utils/raise.d.ts +9 -0
  172. package/dist/types/utils/raise.d.ts.map +1 -0
  173. package/dist/types/utils/types.d.ts +11 -0
  174. package/dist/types/utils/types.d.ts.map +1 -0
  175. package/dist/types/version.d.ts +2 -0
  176. package/dist/types/version.d.ts.map +1 -0
  177. package/package.json +67 -0
  178. package/src/actions/addBreadCrumb.ts +38 -0
  179. package/src/chains.ts +118 -0
  180. package/src/errors/AccountNotFoundError.ts +16 -0
  181. package/src/errors/BaseError.ts +51 -0
  182. package/src/errors/ChainNotFoundError.ts +15 -0
  183. package/src/errors/ConnectionConfigError.ts +22 -0
  184. package/src/errors/FetchError.ts +21 -0
  185. package/src/errors/InvalidRequestError.ts +19 -0
  186. package/src/errors/MethodUnsupportedError.ts +17 -0
  187. package/src/errors/ServerError.ts +21 -0
  188. package/src/index.ts +60 -0
  189. package/src/logging/config.ts +365 -0
  190. package/src/logging/index.ts +20 -0
  191. package/src/logging/local.ts +39 -0
  192. package/src/logging/logger.ts +194 -0
  193. package/src/logging/noop.ts +13 -0
  194. package/src/logging/sinks.ts +115 -0
  195. package/src/logging/types.ts +111 -0
  196. package/src/logging/utils.ts +31 -0
  197. package/src/rest/restClient.ts +64 -0
  198. package/src/rest/types.ts +42 -0
  199. package/src/tracing/traceHeader.ts +154 -0
  200. package/src/tracing/updateHeaders.ts +66 -0
  201. package/src/transport/alchemy.ts +242 -0
  202. package/src/transport/chainRegistry.ts +115 -0
  203. package/src/transport/connection.ts +19 -0
  204. package/src/transport/connectionSchema.ts +145 -0
  205. package/src/utils/assertNever.ts +12 -0
  206. package/src/utils/bigint.ts +58 -0
  207. package/src/utils/createEip1193HandlerFactory.ts +25 -0
  208. package/src/utils/headers.ts +48 -0
  209. package/src/utils/lowerAddress.ts +10 -0
  210. package/src/utils/raise.ts +14 -0
  211. package/src/utils/types.ts +14 -0
  212. package/src/version.ts +3 -0
@@ -0,0 +1,115 @@
1
+ import type { LogEntry } from "./config.js";
2
+
3
+ /**
4
+ * In-memory sink for testing purposes.
5
+ * Captures all log entries in an array that can be inspected in tests.
6
+ *
7
+ * **Note:** This is primarily intended for testing SDK integrations.
8
+ * Most applications should use the default console sink.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { InMemorySink, setGlobalLoggerConfig, createLogger } from "@alchemy/common";
13
+ *
14
+ * // In tests
15
+ * const sink = new InMemorySink();
16
+ *
17
+ * setGlobalLoggerConfig({
18
+ * sinks: [sink.sink]
19
+ * });
20
+ *
21
+ * const logger = createLogger({
22
+ * package: "@alchemy/aa-infra",
23
+ * version: "1.0.0",
24
+ * namespace: "aa-infra"
25
+ * });
26
+ *
27
+ * logger.info("test message");
28
+ *
29
+ * // Inspect captured logs
30
+ * expect(sink.count).toBe(1);
31
+ * expect(sink.latest()?.message).toBe("test message");
32
+ * ```
33
+ */
34
+ export class InMemorySink {
35
+ /**
36
+ * Array of captured log entries
37
+ */
38
+ public entries: LogEntry[] = [];
39
+
40
+ /**
41
+ * Creates a new InMemorySink instance.
42
+ * Binds the sink method to the instance for use as a callback.
43
+ */
44
+ constructor() {
45
+ this.sink = this.sink.bind(this);
46
+ }
47
+
48
+ /**
49
+ * The sink function to pass to setGlobalLoggerConfig.
50
+ * Captures log entries into the internal array.
51
+ *
52
+ * @param {LogEntry} entry - The log entry to capture
53
+ * @returns {void}
54
+ */
55
+ sink(entry: LogEntry): void {
56
+ this.entries.push(entry);
57
+ }
58
+
59
+ /**
60
+ * Clear all captured entries
61
+ *
62
+ * @returns {void}
63
+ */
64
+ clear(): void {
65
+ this.entries = [];
66
+ }
67
+
68
+ /**
69
+ * Get entries filtered by log level
70
+ *
71
+ * @param {number} level - The log level to filter by (LogLevel.ERROR, LogLevel.INFO, etc.)
72
+ * @returns {LogEntry[]} Array of log entries matching the specified level
73
+ */
74
+ getByLevel(level: number): LogEntry[] {
75
+ return this.entries.filter((e) => e.level === level);
76
+ }
77
+
78
+ /**
79
+ * Get entries filtered by namespace
80
+ *
81
+ * @param {string} namespace - The namespace to filter by (e.g., "aa-infra", "wallet-apis")
82
+ * @returns {LogEntry[]} Array of log entries matching the specified namespace
83
+ */
84
+ getByNamespace(namespace: string): LogEntry[] {
85
+ return this.entries.filter((e) => e.namespace === namespace);
86
+ }
87
+
88
+ /**
89
+ * Get entries filtered by message substring
90
+ *
91
+ * @param {string} substring - The substring to search for in log messages
92
+ * @returns {LogEntry[]} Array of log entries containing the substring in their message
93
+ */
94
+ getByMessage(substring: string): LogEntry[] {
95
+ return this.entries.filter((e) => e.message.includes(substring));
96
+ }
97
+
98
+ /**
99
+ * Get the most recent log entry
100
+ *
101
+ * @returns {LogEntry | undefined} The latest log entry, or undefined if no entries have been captured
102
+ */
103
+ latest(): LogEntry | undefined {
104
+ return this.entries[this.entries.length - 1];
105
+ }
106
+
107
+ /**
108
+ * Get the number of captured entries
109
+ *
110
+ * @returns {number} The total count of captured log entries
111
+ */
112
+ get count(): number {
113
+ return this.entries.length;
114
+ }
115
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Schema definition for event logging.
3
+ * An array of event definitions with their names and optional data structures.
4
+ */
5
+ export type EventsSchema = readonly {
6
+ EventName: string;
7
+ EventData?: Record<string, any>;
8
+ }[];
9
+
10
+ type Prettify<T> = {
11
+ [K in keyof T]: T[K];
12
+ } & {};
13
+
14
+ /**
15
+ * Type-safe parameters for tracking events based on the provided schema.
16
+ * Ensures event names and data structures match the schema definition.
17
+ * When no schema is provided, allows any event with optional data.
18
+ *
19
+ * @template Schema - The events schema to validate against
20
+ */
21
+ export type TrackEventParameters<Schema extends EventsSchema> =
22
+ Schema extends readonly []
23
+ ? { name: string; data?: any }
24
+ : {
25
+ [K in keyof Schema]: Prettify<
26
+ { name: Schema[K]["EventName"] } & ([undefined] extends [
27
+ Schema[K]["EventData"],
28
+ ]
29
+ ? { data?: undefined }
30
+ : { data: Schema[K]["EventData"] })
31
+ >;
32
+ }[number];
33
+
34
+ /**
35
+ * Main event logger interface for type-safe event tracking and performance profiling.
36
+ *
37
+ * @template Schema - The events schema defining allowed events and their data structures
38
+ */
39
+ export interface EventLogger<Schema extends EventsSchema = []> {
40
+ /**
41
+ * Tracks an event with type-safe validation against the schema.
42
+ *
43
+ * @param params - Event parameters including name and optional data
44
+ * @returns Promise that resolves when the event is tracked
45
+ */
46
+ trackEvent(
47
+ params: TrackEventParameters<
48
+ Schema extends readonly [] ? [] : [...Schema, PerformanceEvent]
49
+ >,
50
+ ): Promise<void>;
51
+
52
+ /**
53
+ * Wraps a function to automatically track its execution time as a performance event.
54
+ *
55
+ * @template TArgs - Function argument types
56
+ * @template TRet - Function return type
57
+ * @param name - Name identifier for the profiled function
58
+ * @param func - Function to wrap with performance tracking
59
+ * @returns Wrapped function that tracks execution time
60
+ */
61
+ profiled<TArgs extends any[], TRet>(
62
+ name: string,
63
+ func: (...args: TArgs) => TRet,
64
+ ): (...args: TArgs) => TRet;
65
+
66
+ /** Internal properties for logger state and configuration */
67
+ _internal: {
68
+ /** Promise that resolves when logger is ready for use */
69
+ ready: Promise<unknown>;
70
+ /** Anonymous identifier for this logger instance */
71
+ anonId: string;
72
+ };
73
+ }
74
+
75
+ /**
76
+ * Internal logger interface without the profiled method.
77
+ * Used internally by different logger implementations.
78
+ *
79
+ * @template Schema - The events schema defining allowed events and their data structures
80
+ */
81
+ export type InnerLogger<Schema extends EventsSchema> = Omit<
82
+ EventLogger<Schema>,
83
+ "profiled"
84
+ >;
85
+
86
+ /**
87
+ * Context information attached to all logged events.
88
+ * Provides metadata about the package and version generating events.
89
+ */
90
+ export type LoggerContext = {
91
+ /** Name of the package generating events */
92
+ package: string;
93
+ /** Version of the package generating events */
94
+ version: string;
95
+ /** Additional context properties as key-value pairs */
96
+ [key: string]: string;
97
+ };
98
+
99
+ /**
100
+ * Built-in performance event schema for tracking function execution times.
101
+ * Automatically included in all event schemas for profiled function tracking.
102
+ */
103
+ export type PerformanceEvent = {
104
+ EventName: "performance";
105
+ EventData: {
106
+ /** Execution time in milliseconds */
107
+ executionTimeMs: number;
108
+ /** Name of the function being profiled */
109
+ functionName: string;
110
+ };
111
+ };
@@ -0,0 +1,31 @@
1
+ // just in case we're in a setting that doesn't have these types defined
2
+ declare const __DEV__: boolean | undefined;
3
+ declare const window: Window | undefined;
4
+
5
+ /**
6
+ * Detects if the current environment is in development mode.
7
+ * Checks multiple common development environment indicators.
8
+ *
9
+ * @returns {boolean} True if running in development mode
10
+ */
11
+ export function isClientDevMode() {
12
+ if (typeof __DEV__ !== "undefined" && __DEV__) {
13
+ return true;
14
+ }
15
+
16
+ if (
17
+ typeof process !== "undefined" &&
18
+ process.env.NODE_ENV === "development"
19
+ ) {
20
+ return true;
21
+ }
22
+
23
+ if (
24
+ typeof window !== "undefined" &&
25
+ window.location?.hostname?.includes("localhost")
26
+ ) {
27
+ return true;
28
+ }
29
+
30
+ return false;
31
+ }
@@ -0,0 +1,64 @@
1
+ import { FetchError } from "../errors/FetchError.js";
2
+ import { ServerError } from "../errors/ServerError.js";
3
+ import { withAlchemyHeaders } from "../utils/headers.js";
4
+ import type { RestRequestFn, RestRequestSchema } from "./types.js";
5
+
6
+ const ALCHEMY_API_URL = "https://api.g.alchemy.com";
7
+
8
+ /**
9
+ * Parameters for creating an AlchemyRestClient instance.
10
+ */
11
+ export type AlchemyRestClientParams = {
12
+ /** API key for Alchemy authentication */
13
+ apiKey?: string;
14
+ /** JWT token for Alchemy authentication */
15
+ jwt?: string;
16
+ /** Custom URL (optional - defaults to Alchemy's chain-agnostic URL, but can be used to override it) */
17
+ url?: string;
18
+ /** Custom headers to be sent with requests */
19
+ headers?: HeadersInit;
20
+ };
21
+
22
+ /**
23
+ * A client for making requests to Alchemy's non-JSON-RPC endpoints.
24
+ */
25
+ export class AlchemyRestClient<Schema extends RestRequestSchema> {
26
+ private readonly url: string;
27
+ private readonly headers: Headers;
28
+
29
+ /**
30
+ * Creates a new instance of AlchemyRestClient.
31
+ *
32
+ * @param {AlchemyRestClientParams} params - The parameters for configuring the client, including API key, JWT, custom URL, and headers.
33
+ */
34
+ constructor({ apiKey, jwt, url, headers }: AlchemyRestClientParams) {
35
+ this.url = url ?? ALCHEMY_API_URL;
36
+ this.headers = new Headers(withAlchemyHeaders({ headers, apiKey, jwt }));
37
+ }
38
+
39
+ /**
40
+ * Makes an HTTP request to an Alchemy non-JSON-RPC endpoint.
41
+ *
42
+ * @param {RestRequestFn<Schema>} params - The parameters for the request
43
+ * @returns {Promise<unknown>} The response from the request
44
+ */
45
+ public request: RestRequestFn<Schema> = async (params) => {
46
+ const response = await fetch(`${this.url}/${params.route}`, {
47
+ method: params.method,
48
+ body: params.body ? JSON.stringify(params.body) : undefined,
49
+ headers: this.headers,
50
+ }).catch((error) => {
51
+ throw new FetchError(params.route, params.method, error);
52
+ });
53
+
54
+ if (!response.ok) {
55
+ throw new ServerError(
56
+ await response.text(),
57
+ response.status,
58
+ new Error(response.statusText),
59
+ );
60
+ }
61
+
62
+ return response.json();
63
+ };
64
+ }
@@ -0,0 +1,42 @@
1
+ import type { Prettify } from "viem";
2
+
3
+ export type RestRequestSchema = readonly {
4
+ Route: string;
5
+ Method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS" | "HEAD";
6
+ Body?: unknown | undefined;
7
+ Response: unknown;
8
+ }[];
9
+
10
+ export type RestRequestParams<
11
+ Schema extends RestRequestSchema | undefined = undefined,
12
+ > = Schema extends RestRequestSchema
13
+ ? {
14
+ [K in keyof Schema]: Prettify<
15
+ {
16
+ route: Schema[K] extends Schema[number] ? Schema[K]["Route"] : never;
17
+ method: Schema[K] extends Schema[number]
18
+ ? Schema[K]["Method"]
19
+ : never;
20
+ } & (Schema[K] extends Schema[number]
21
+ ? Schema[K]["Body"] extends undefined
22
+ ? { body?: undefined }
23
+ : { body: Schema[K]["Body"] }
24
+ : never)
25
+ >;
26
+ }[number]
27
+ : {
28
+ route: string;
29
+ method: RestRequestSchema[number]["Method"];
30
+ body?: unknown | undefined;
31
+ };
32
+
33
+ export type RestRequestFn<
34
+ Schema extends RestRequestSchema | undefined = undefined,
35
+ > = <
36
+ _parameters extends RestRequestParams<Schema> = RestRequestParams<Schema>,
37
+ _returnType = Schema extends RestRequestSchema
38
+ ? Extract<Schema[number], { Route: _parameters["route"] }>["Response"]
39
+ : unknown,
40
+ >(
41
+ params: _parameters,
42
+ ) => Promise<_returnType>;
@@ -0,0 +1,154 @@
1
+ function generateRandomHexString(numBytes: number) {
2
+ const hexPairs = new Array(numBytes).fill(0).map(() =>
3
+ Math.floor(Math.random() * 16)
4
+ .toString(16)
5
+ .padStart(2, "0"),
6
+ );
7
+ return hexPairs.join("");
8
+ }
9
+
10
+ /**
11
+ * These are the headers that are used in the trace headers, could be found in the spec
12
+ *
13
+ * @see https://www.w3.org/TR/trace-context/#design-overview
14
+ */
15
+
16
+ export const TRACE_HEADER_NAME = "traceparent";
17
+ /**
18
+ * These are the headers that are used in the trace headers, could be found in the spec
19
+ *
20
+ * @see https://www.w3.org/TR/trace-context/#design-overview
21
+ */
22
+ export const TRACE_HEADER_STATE = "tracestate";
23
+
24
+ const clientTraceId = generateRandomHexString(16);
25
+ /**
26
+ * Some tools that are useful when dealing with the values
27
+ * of the trace header. Follows the W3C trace context standard.
28
+ *
29
+ * @see https://www.w3.org/TR/trace-context/
30
+ */
31
+ export class TraceHeader {
32
+ readonly traceId: string;
33
+ readonly parentId: string;
34
+ readonly traceFlags: string;
35
+ readonly traceState: Record<string, string>;
36
+
37
+ /**
38
+ * Initializes a new instance with the provided trace identifiers and state information.
39
+ *
40
+ * @param {string} traceId The unique identifier for the trace
41
+ * @param {string} parentId The identifier of the parent trace
42
+ * @param {string} traceFlags Flags containing trace-related options
43
+ * @param {TraceHeader["traceState"]} traceState The trace state information for additional trace context
44
+ */
45
+ constructor(
46
+ traceId: string,
47
+ parentId: string,
48
+ traceFlags: string,
49
+ traceState: TraceHeader["traceState"],
50
+ ) {
51
+ this.traceId = traceId;
52
+ this.parentId = parentId;
53
+ this.traceFlags = traceFlags;
54
+ this.traceState = traceState;
55
+ }
56
+
57
+ /**
58
+ * Creating a default trace id that is a random setup for both trace id and parent id
59
+ *
60
+ * @example ```ts
61
+ * const traceHeader = TraceHeader.fromTraceHeader(headers) || TraceHeader.default();
62
+ * ```
63
+ *
64
+ * @returns {TraceHeader} A default trace header
65
+ */
66
+ static default() {
67
+ return new TraceHeader(
68
+ clientTraceId,
69
+ generateRandomHexString(8),
70
+ "00", //Means no flag have been set, and no sampled state https://www.w3.org/TR/trace-context/#trace-flags
71
+ {},
72
+ );
73
+ }
74
+ /**
75
+ * Should be able to consume a trace header from the headers of an http request
76
+ *
77
+ * @example ```ts
78
+ * const traceHeader = TraceHeader.fromTraceHeader(headers);
79
+ * ```
80
+ *
81
+ * @param {Record<string,string>} headers The headers from the http request
82
+ * @returns {TraceHeader | undefined} The trace header object, or nothing if not found
83
+ */
84
+ static fromTraceHeader(
85
+ headers: Record<string, string>,
86
+ ): TraceHeader | undefined {
87
+ if (!headers[TRACE_HEADER_NAME]) {
88
+ return undefined;
89
+ }
90
+ const [version, traceId, parentId, traceFlags] =
91
+ headers[TRACE_HEADER_NAME]?.split("-");
92
+
93
+ const traceState =
94
+ headers[TRACE_HEADER_STATE]?.split(",").reduce(
95
+ (acc, curr) => {
96
+ const [key, value] = curr.split("=");
97
+ acc[key] = value;
98
+ return acc;
99
+ },
100
+ {} as Record<string, string>,
101
+ ) || {};
102
+ if (version !== "00") {
103
+ console.debug(
104
+ new Error(
105
+ `Invalid version for traceheader: ${headers[TRACE_HEADER_NAME]}`,
106
+ ),
107
+ );
108
+ return undefined;
109
+ }
110
+ return new TraceHeader(traceId, parentId, traceFlags, traceState);
111
+ }
112
+
113
+ /**
114
+ * Should be able to convert the trace header to the format that is used in the headers of an http request
115
+ *
116
+ * @example ```ts
117
+ * const traceHeader = TraceHeader.fromTraceHeader(headers) || TraceHeader.default();
118
+ * const headers = traceHeader.toTraceHeader();
119
+ * ```
120
+ *
121
+ * @returns {{traceparent: string, tracestate: string}} The trace header in the format of a record, used in our http client
122
+ */
123
+ toTraceHeader() {
124
+ return {
125
+ [TRACE_HEADER_NAME]: `00-${this.traceId}-${this.parentId}-${this.traceFlags}`,
126
+ [TRACE_HEADER_STATE]: Object.entries(this.traceState)
127
+ .map(([key, value]) => `${key}=${value}`)
128
+ .join(","),
129
+ } as const;
130
+ }
131
+
132
+ /**
133
+ * Should be able to create a new trace header with a new event in the trace state,
134
+ * as the key of the eventName as breadcrumbs appending onto previous breadcrumbs with the - infix if exists. And the
135
+ * trace parent gets updated as according to the docs
136
+ *
137
+ * @example ```ts
138
+ * const traceHeader = TraceHeader.fromTraceHeader(headers) || TraceHeader.default();
139
+ * const newTraceHeader = traceHeader.withEvent("newEvent");
140
+ * ```
141
+ *
142
+ * @param {string} eventName The key of the new event
143
+ * @returns {TraceHeader} The new trace header
144
+ */
145
+ withEvent(eventName: string): TraceHeader {
146
+ const breadcrumbs = this.traceState.breadcrumbs
147
+ ? `${this.traceState.breadcrumbs}-${eventName}`
148
+ : eventName;
149
+ return new TraceHeader(this.traceId, this.parentId, this.traceFlags, {
150
+ ...this.traceState,
151
+ breadcrumbs,
152
+ });
153
+ }
154
+ }
@@ -0,0 +1,66 @@
1
+ import {
2
+ TRACE_HEADER_NAME,
3
+ TRACE_HEADER_STATE,
4
+ TraceHeader,
5
+ } from "./traceHeader.js";
6
+
7
+ /**
8
+ * The header that is used to track the trace id.
9
+ * We use a client specific one to not mess with the span tracing of the servers.
10
+ *
11
+ * @see headersUpdate
12
+ */
13
+ const TRACKER_HEADER = "X-Alchemy-Client-Trace-Id";
14
+
15
+ /**
16
+ * The header that is used to track the breadcrumb.
17
+ *
18
+ * @see headersUpdate
19
+ */
20
+ const TRACKER_BREADCRUMB = "X-Alchemy-Client-Breadcrumb";
21
+
22
+ /**
23
+ * Remove the tracking headers. This is used in our split transport to ensure that we remove the headers that
24
+ * are not used by the other systems.
25
+ *
26
+ * @param {unknown} x The headers to remove the tracking headers from
27
+ */
28
+ export function mutateRemoveTrackingHeaders(x?: unknown) {
29
+ if (!x) return;
30
+ if (Array.isArray(x)) return;
31
+ if (typeof x !== "object") return;
32
+ TRACKER_HEADER in x && delete x[TRACKER_HEADER];
33
+ TRACKER_BREADCRUMB in x && delete x[TRACKER_BREADCRUMB];
34
+ TRACE_HEADER_NAME in x && delete x[TRACE_HEADER_NAME];
35
+ TRACE_HEADER_STATE in x && delete x[TRACE_HEADER_STATE];
36
+ }
37
+
38
+ function addCrumb(previous: string | undefined, crumb: string): string {
39
+ if (!previous) return crumb;
40
+ return `${previous} > ${crumb}`;
41
+ }
42
+ /**
43
+ * Update the headers with the trace header and breadcrumb.
44
+ *
45
+ * These trace headers are used in the imply ingestion pipeline to trace the request.
46
+ * And the breadcrumb is used to get finer grain details in the trace.
47
+ *
48
+ * Then there are the trace headers that are part of the W3C trace context standard.
49
+ *
50
+ * @param {string} crumb The crumb to add to the breadcrumb
51
+ * @returns {Function} A function that updates the headers
52
+ */
53
+ export function headersUpdate(crumb: string) {
54
+ const headerUpdate_ = (x: Record<string, string>) => {
55
+ const traceHeader = (
56
+ TraceHeader.fromTraceHeader(x) || TraceHeader.default()
57
+ ).withEvent(crumb);
58
+ return {
59
+ [TRACKER_HEADER]: traceHeader.parentId,
60
+ ...x,
61
+ [TRACKER_BREADCRUMB]: addCrumb(x[TRACKER_BREADCRUMB], crumb),
62
+ ...traceHeader.toTraceHeader(),
63
+ };
64
+ };
65
+ return headerUpdate_;
66
+ }