@solana/subscribable 2.0.0-20241006045741

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.
@@ -0,0 +1,193 @@
1
+ import { SolanaError, SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_STATE_MISSING, SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_MUST_NOT_POLL_BEFORE_RESOLVING_EXISTING_MESSAGE_PROMISE } from '@solana/errors';
2
+
3
+ // src/async-iterable.ts
4
+ var EXPLICIT_ABORT_TOKEN;
5
+ function createExplicitAbortToken() {
6
+ return Symbol(
7
+ process.env.NODE_ENV !== "production" ? "This symbol is thrown from a socket's iterator when the connection is explicitly aborted by the user" : void 0
8
+ );
9
+ }
10
+ var UNINITIALIZED = Symbol();
11
+ function createAsyncIterableFromDataPublisher({
12
+ abortSignal,
13
+ dataChannelName,
14
+ dataPublisher,
15
+ errorChannelName
16
+ }) {
17
+ const iteratorState = /* @__PURE__ */ new Map();
18
+ function publishErrorToAllIterators(reason) {
19
+ for (const [iteratorKey, state] of iteratorState.entries()) {
20
+ if (state.__hasPolled) {
21
+ iteratorState.delete(iteratorKey);
22
+ state.onError(reason);
23
+ } else {
24
+ state.publishQueue.push({
25
+ __type: 1 /* ERROR */,
26
+ err: reason
27
+ });
28
+ }
29
+ }
30
+ }
31
+ const abortController = new AbortController();
32
+ abortSignal.addEventListener("abort", () => {
33
+ abortController.abort();
34
+ publishErrorToAllIterators(EXPLICIT_ABORT_TOKEN ||= createExplicitAbortToken());
35
+ });
36
+ const options = { signal: abortController.signal };
37
+ let firstError = UNINITIALIZED;
38
+ dataPublisher.on(
39
+ errorChannelName,
40
+ (err) => {
41
+ if (firstError === UNINITIALIZED) {
42
+ firstError = err;
43
+ abortController.abort();
44
+ publishErrorToAllIterators(err);
45
+ }
46
+ },
47
+ options
48
+ );
49
+ dataPublisher.on(
50
+ dataChannelName,
51
+ (data) => {
52
+ iteratorState.forEach((state, iteratorKey) => {
53
+ if (state.__hasPolled) {
54
+ const { onData } = state;
55
+ iteratorState.set(iteratorKey, { __hasPolled: false, publishQueue: [] });
56
+ onData(data);
57
+ } else {
58
+ state.publishQueue.push({
59
+ __type: 0 /* DATA */,
60
+ data
61
+ });
62
+ }
63
+ });
64
+ },
65
+ options
66
+ );
67
+ return {
68
+ async *[Symbol.asyncIterator]() {
69
+ if (abortSignal.aborted) {
70
+ return;
71
+ }
72
+ if (firstError !== UNINITIALIZED) {
73
+ throw firstError;
74
+ }
75
+ const iteratorKey = Symbol();
76
+ iteratorState.set(iteratorKey, { __hasPolled: false, publishQueue: [] });
77
+ try {
78
+ while (true) {
79
+ const state = iteratorState.get(iteratorKey);
80
+ if (!state) {
81
+ throw new SolanaError(SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_STATE_MISSING);
82
+ }
83
+ if (state.__hasPolled) {
84
+ throw new SolanaError(
85
+ SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_MUST_NOT_POLL_BEFORE_RESOLVING_EXISTING_MESSAGE_PROMISE
86
+ );
87
+ }
88
+ const publishQueue = state.publishQueue;
89
+ try {
90
+ if (publishQueue.length) {
91
+ state.publishQueue = [];
92
+ for (const item of publishQueue) {
93
+ if (item.__type === 0 /* DATA */) {
94
+ yield item.data;
95
+ } else {
96
+ throw item.err;
97
+ }
98
+ }
99
+ } else {
100
+ yield await new Promise((resolve, reject) => {
101
+ iteratorState.set(iteratorKey, {
102
+ __hasPolled: true,
103
+ onData: resolve,
104
+ onError: reject
105
+ });
106
+ });
107
+ }
108
+ } catch (e) {
109
+ if (e === (EXPLICIT_ABORT_TOKEN ||= createExplicitAbortToken())) {
110
+ return;
111
+ } else {
112
+ throw e;
113
+ }
114
+ }
115
+ }
116
+ } finally {
117
+ iteratorState.delete(iteratorKey);
118
+ }
119
+ }
120
+ };
121
+ }
122
+
123
+ // src/data-publisher.ts
124
+ function getDataPublisherFromEventEmitter(eventEmitter) {
125
+ return {
126
+ on(channelName, subscriber, options) {
127
+ function innerListener(ev) {
128
+ if (ev instanceof CustomEvent) {
129
+ const data = ev.detail;
130
+ subscriber(data);
131
+ } else {
132
+ subscriber();
133
+ }
134
+ }
135
+ eventEmitter.addEventListener(channelName, innerListener, options);
136
+ return () => {
137
+ eventEmitter.removeEventListener(channelName, innerListener);
138
+ };
139
+ }
140
+ };
141
+ }
142
+
143
+ // src/demultiplex.ts
144
+ function demultiplexDataPublisher(publisher, sourceChannelName, messageTransformer) {
145
+ let innerPublisherState;
146
+ const eventTarget = new EventTarget();
147
+ const demultiplexedDataPublisher = getDataPublisherFromEventEmitter(eventTarget);
148
+ return {
149
+ ...demultiplexedDataPublisher,
150
+ on(channelName, subscriber, options) {
151
+ if (!innerPublisherState) {
152
+ const innerPublisherUnsubscribe = publisher.on(sourceChannelName, (sourceMessage) => {
153
+ const transformResult = messageTransformer(sourceMessage);
154
+ if (!transformResult) {
155
+ return;
156
+ }
157
+ const [destinationChannelName, message] = transformResult;
158
+ eventTarget.dispatchEvent(
159
+ new CustomEvent(destinationChannelName, {
160
+ detail: message
161
+ })
162
+ );
163
+ });
164
+ innerPublisherState = {
165
+ dispose: innerPublisherUnsubscribe,
166
+ numSubscribers: 0
167
+ };
168
+ }
169
+ innerPublisherState.numSubscribers++;
170
+ const unsubscribe = demultiplexedDataPublisher.on(channelName, subscriber, options);
171
+ let isActive = true;
172
+ function handleUnsubscribe() {
173
+ if (!isActive) {
174
+ return;
175
+ }
176
+ isActive = false;
177
+ options?.signal.removeEventListener("abort", handleUnsubscribe);
178
+ innerPublisherState.numSubscribers--;
179
+ if (innerPublisherState.numSubscribers === 0) {
180
+ innerPublisherState.dispose();
181
+ innerPublisherState = void 0;
182
+ }
183
+ unsubscribe();
184
+ }
185
+ options?.signal.addEventListener("abort", handleUnsubscribe);
186
+ return handleUnsubscribe;
187
+ }
188
+ };
189
+ }
190
+
191
+ export { createAsyncIterableFromDataPublisher, demultiplexDataPublisher, getDataPublisherFromEventEmitter };
192
+ //# sourceMappingURL=index.native.mjs.map
193
+ //# sourceMappingURL=index.native.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/async-iterable.ts","../src/data-publisher.ts","../src/demultiplex.ts"],"names":[],"mappings":"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","file":"index.native.mjs","sourcesContent":["import {\n SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_MUST_NOT_POLL_BEFORE_RESOLVING_EXISTING_MESSAGE_PROMISE,\n SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_STATE_MISSING,\n SolanaError,\n} from '@solana/errors';\n\nimport { DataPublisher } from './data-publisher';\n\ntype Config = Readonly<{\n abortSignal: AbortSignal;\n dataChannelName: string;\n // FIXME: It would be nice to be able to constrain the type of `dataPublisher` to one that\n // definitely supports the `dataChannelName` and `errorChannelName` channels, and\n // furthermore publishes `TData` on the `dataChannelName` channel. This is more difficult\n // than it should be: https://tsplay.dev/NlZelW\n dataPublisher: DataPublisher;\n errorChannelName: string;\n}>;\n\nconst enum PublishType {\n DATA,\n ERROR,\n}\n\ntype IteratorKey = symbol;\ntype IteratorState<TData> =\n | {\n __hasPolled: false;\n publishQueue: (\n | {\n __type: PublishType.DATA;\n data: TData;\n }\n | {\n __type: PublishType.ERROR;\n err: unknown;\n }\n )[];\n }\n | {\n __hasPolled: true;\n onData: (data: TData) => void;\n onError: Parameters<ConstructorParameters<typeof Promise>[0]>[1];\n };\n\nlet EXPLICIT_ABORT_TOKEN: symbol;\nfunction createExplicitAbortToken() {\n // This function is an annoying workaround to prevent `process.env.NODE_ENV` from appearing at\n // the top level of this module and thwarting an optimizing compiler's attempt to tree-shake.\n return Symbol(\n process.env.NODE_ENV !== \"production\"\n ? \"This symbol is thrown from a socket's iterator when the connection is explicitly \" +\n 'aborted by the user'\n : undefined,\n );\n}\n\nconst UNINITIALIZED = Symbol();\n\nexport function createAsyncIterableFromDataPublisher<TData>({\n abortSignal,\n dataChannelName,\n dataPublisher,\n errorChannelName,\n}: Config): AsyncIterable<TData> {\n const iteratorState: Map<IteratorKey, IteratorState<TData>> = new Map();\n function publishErrorToAllIterators(reason: unknown) {\n for (const [iteratorKey, state] of iteratorState.entries()) {\n if (state.__hasPolled) {\n iteratorState.delete(iteratorKey);\n state.onError(reason);\n } else {\n state.publishQueue.push({\n __type: PublishType.ERROR,\n err: reason,\n });\n }\n }\n }\n const abortController = new AbortController();\n abortSignal.addEventListener('abort', () => {\n abortController.abort();\n publishErrorToAllIterators((EXPLICIT_ABORT_TOKEN ||= createExplicitAbortToken()));\n });\n const options = { signal: abortController.signal } as const;\n let firstError: unknown | typeof UNINITIALIZED = UNINITIALIZED;\n dataPublisher.on(\n errorChannelName,\n err => {\n if (firstError === UNINITIALIZED) {\n firstError = err;\n abortController.abort();\n publishErrorToAllIterators(err);\n }\n },\n options,\n );\n dataPublisher.on(\n dataChannelName,\n data => {\n iteratorState.forEach((state, iteratorKey) => {\n if (state.__hasPolled) {\n const { onData } = state;\n iteratorState.set(iteratorKey, { __hasPolled: false, publishQueue: [] });\n onData(data as TData);\n } else {\n state.publishQueue.push({\n __type: PublishType.DATA,\n data: data as TData,\n });\n }\n });\n },\n options,\n );\n return {\n async *[Symbol.asyncIterator]() {\n if (abortSignal.aborted) {\n return;\n }\n if (firstError !== UNINITIALIZED) {\n throw firstError;\n }\n const iteratorKey = Symbol();\n iteratorState.set(iteratorKey, { __hasPolled: false, publishQueue: [] });\n try {\n while (true) {\n const state = iteratorState.get(iteratorKey);\n if (!state) {\n // There should always be state by now.\n throw new SolanaError(SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_STATE_MISSING);\n }\n if (state.__hasPolled) {\n // You should never be able to poll twice in a row.\n throw new SolanaError(\n SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_MUST_NOT_POLL_BEFORE_RESOLVING_EXISTING_MESSAGE_PROMISE,\n );\n }\n const publishQueue = state.publishQueue;\n try {\n if (publishQueue.length) {\n state.publishQueue = [];\n for (const item of publishQueue) {\n if (item.__type === PublishType.DATA) {\n yield item.data;\n } else {\n throw item.err;\n }\n }\n } else {\n yield await new Promise<TData>((resolve, reject) => {\n iteratorState.set(iteratorKey, {\n __hasPolled: true,\n onData: resolve,\n onError: reject,\n });\n });\n }\n } catch (e) {\n if (e === (EXPLICIT_ABORT_TOKEN ||= createExplicitAbortToken())) {\n return;\n } else {\n throw e;\n }\n }\n }\n } finally {\n iteratorState.delete(iteratorKey);\n }\n },\n };\n}\n","import { TypedEventEmitter, TypedEventTarget } from './event-emitter';\n\ntype UnsubscribeFn = () => void;\n\nexport interface DataPublisher<TDataByChannelName extends Record<string, unknown> = Record<string, unknown>> {\n on<const TChannelName extends keyof TDataByChannelName>(\n channelName: TChannelName,\n subscriber: (data: TDataByChannelName[TChannelName]) => void,\n options?: { signal: AbortSignal },\n ): UnsubscribeFn;\n}\n\nexport function getDataPublisherFromEventEmitter<TEventMap extends Record<string, Event>>(\n eventEmitter: TypedEventEmitter<TEventMap> | TypedEventTarget<TEventMap>,\n): DataPublisher<{\n [TEventType in keyof TEventMap]: TEventMap[TEventType] extends CustomEvent ? TEventMap[TEventType]['detail'] : null;\n}> {\n return {\n on(channelName, subscriber, options) {\n function innerListener(ev: Event) {\n if (ev instanceof CustomEvent) {\n const data = (ev as CustomEvent<TEventMap[typeof channelName]>).detail;\n (subscriber as unknown as (data: TEventMap[typeof channelName]) => void)(data);\n } else {\n (subscriber as () => void)();\n }\n }\n eventEmitter.addEventListener(channelName, innerListener, options);\n return () => {\n eventEmitter.removeEventListener(channelName, innerListener);\n };\n },\n };\n}\n","import { DataPublisher, getDataPublisherFromEventEmitter } from './data-publisher';\n\nexport function demultiplexDataPublisher<\n TDataPublisher extends DataPublisher,\n const TChannelName extends Parameters<TDataPublisher['on']>[0],\n>(\n publisher: TDataPublisher,\n sourceChannelName: TChannelName,\n messageTransformer: (\n // FIXME: Deriving the type of the message from `TDataPublisher` and `TChannelName` would\n // help callers to constrain their transform functions.\n message: unknown,\n ) => [destinationChannelName: string, message: unknown] | void,\n): DataPublisher {\n let innerPublisherState:\n | {\n readonly dispose: () => void;\n numSubscribers: number;\n }\n | undefined;\n const eventTarget = new EventTarget();\n const demultiplexedDataPublisher = getDataPublisherFromEventEmitter(eventTarget);\n return {\n ...demultiplexedDataPublisher,\n on(channelName, subscriber, options) {\n if (!innerPublisherState) {\n const innerPublisherUnsubscribe = publisher.on(sourceChannelName, sourceMessage => {\n const transformResult = messageTransformer(sourceMessage);\n if (!transformResult) {\n return;\n }\n const [destinationChannelName, message] = transformResult;\n eventTarget.dispatchEvent(\n new CustomEvent(destinationChannelName, {\n detail: message,\n }),\n );\n });\n innerPublisherState = {\n dispose: innerPublisherUnsubscribe,\n numSubscribers: 0,\n };\n }\n innerPublisherState.numSubscribers++;\n const unsubscribe = demultiplexedDataPublisher.on(channelName, subscriber, options);\n let isActive = true;\n function handleUnsubscribe() {\n if (!isActive) {\n return;\n }\n isActive = false;\n options?.signal.removeEventListener('abort', handleUnsubscribe);\n innerPublisherState!.numSubscribers--;\n if (innerPublisherState!.numSubscribers === 0) {\n innerPublisherState!.dispose();\n innerPublisherState = undefined;\n }\n unsubscribe();\n }\n options?.signal.addEventListener('abort', handleUnsubscribe);\n return handleUnsubscribe;\n },\n };\n}\n"]}
@@ -0,0 +1,197 @@
1
+ 'use strict';
2
+
3
+ var errors = require('@solana/errors');
4
+
5
+ // src/async-iterable.ts
6
+ var EXPLICIT_ABORT_TOKEN;
7
+ function createExplicitAbortToken() {
8
+ return Symbol(
9
+ process.env.NODE_ENV !== "production" ? "This symbol is thrown from a socket's iterator when the connection is explicitly aborted by the user" : void 0
10
+ );
11
+ }
12
+ var UNINITIALIZED = Symbol();
13
+ function createAsyncIterableFromDataPublisher({
14
+ abortSignal,
15
+ dataChannelName,
16
+ dataPublisher,
17
+ errorChannelName
18
+ }) {
19
+ const iteratorState = /* @__PURE__ */ new Map();
20
+ function publishErrorToAllIterators(reason) {
21
+ for (const [iteratorKey, state] of iteratorState.entries()) {
22
+ if (state.__hasPolled) {
23
+ iteratorState.delete(iteratorKey);
24
+ state.onError(reason);
25
+ } else {
26
+ state.publishQueue.push({
27
+ __type: 1 /* ERROR */,
28
+ err: reason
29
+ });
30
+ }
31
+ }
32
+ }
33
+ const abortController = new AbortController();
34
+ abortSignal.addEventListener("abort", () => {
35
+ abortController.abort();
36
+ publishErrorToAllIterators(EXPLICIT_ABORT_TOKEN ||= createExplicitAbortToken());
37
+ });
38
+ const options = { signal: abortController.signal };
39
+ let firstError = UNINITIALIZED;
40
+ dataPublisher.on(
41
+ errorChannelName,
42
+ (err) => {
43
+ if (firstError === UNINITIALIZED) {
44
+ firstError = err;
45
+ abortController.abort();
46
+ publishErrorToAllIterators(err);
47
+ }
48
+ },
49
+ options
50
+ );
51
+ dataPublisher.on(
52
+ dataChannelName,
53
+ (data) => {
54
+ iteratorState.forEach((state, iteratorKey) => {
55
+ if (state.__hasPolled) {
56
+ const { onData } = state;
57
+ iteratorState.set(iteratorKey, { __hasPolled: false, publishQueue: [] });
58
+ onData(data);
59
+ } else {
60
+ state.publishQueue.push({
61
+ __type: 0 /* DATA */,
62
+ data
63
+ });
64
+ }
65
+ });
66
+ },
67
+ options
68
+ );
69
+ return {
70
+ async *[Symbol.asyncIterator]() {
71
+ if (abortSignal.aborted) {
72
+ return;
73
+ }
74
+ if (firstError !== UNINITIALIZED) {
75
+ throw firstError;
76
+ }
77
+ const iteratorKey = Symbol();
78
+ iteratorState.set(iteratorKey, { __hasPolled: false, publishQueue: [] });
79
+ try {
80
+ while (true) {
81
+ const state = iteratorState.get(iteratorKey);
82
+ if (!state) {
83
+ throw new errors.SolanaError(errors.SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_STATE_MISSING);
84
+ }
85
+ if (state.__hasPolled) {
86
+ throw new errors.SolanaError(
87
+ errors.SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_MUST_NOT_POLL_BEFORE_RESOLVING_EXISTING_MESSAGE_PROMISE
88
+ );
89
+ }
90
+ const publishQueue = state.publishQueue;
91
+ try {
92
+ if (publishQueue.length) {
93
+ state.publishQueue = [];
94
+ for (const item of publishQueue) {
95
+ if (item.__type === 0 /* DATA */) {
96
+ yield item.data;
97
+ } else {
98
+ throw item.err;
99
+ }
100
+ }
101
+ } else {
102
+ yield await new Promise((resolve, reject) => {
103
+ iteratorState.set(iteratorKey, {
104
+ __hasPolled: true,
105
+ onData: resolve,
106
+ onError: reject
107
+ });
108
+ });
109
+ }
110
+ } catch (e) {
111
+ if (e === (EXPLICIT_ABORT_TOKEN ||= createExplicitAbortToken())) {
112
+ return;
113
+ } else {
114
+ throw e;
115
+ }
116
+ }
117
+ }
118
+ } finally {
119
+ iteratorState.delete(iteratorKey);
120
+ }
121
+ }
122
+ };
123
+ }
124
+
125
+ // src/data-publisher.ts
126
+ function getDataPublisherFromEventEmitter(eventEmitter) {
127
+ return {
128
+ on(channelName, subscriber, options) {
129
+ function innerListener(ev) {
130
+ if (ev instanceof CustomEvent) {
131
+ const data = ev.detail;
132
+ subscriber(data);
133
+ } else {
134
+ subscriber();
135
+ }
136
+ }
137
+ eventEmitter.addEventListener(channelName, innerListener, options);
138
+ return () => {
139
+ eventEmitter.removeEventListener(channelName, innerListener);
140
+ };
141
+ }
142
+ };
143
+ }
144
+
145
+ // src/demultiplex.ts
146
+ function demultiplexDataPublisher(publisher, sourceChannelName, messageTransformer) {
147
+ let innerPublisherState;
148
+ const eventTarget = new EventTarget();
149
+ const demultiplexedDataPublisher = getDataPublisherFromEventEmitter(eventTarget);
150
+ return {
151
+ ...demultiplexedDataPublisher,
152
+ on(channelName, subscriber, options) {
153
+ if (!innerPublisherState) {
154
+ const innerPublisherUnsubscribe = publisher.on(sourceChannelName, (sourceMessage) => {
155
+ const transformResult = messageTransformer(sourceMessage);
156
+ if (!transformResult) {
157
+ return;
158
+ }
159
+ const [destinationChannelName, message] = transformResult;
160
+ eventTarget.dispatchEvent(
161
+ new CustomEvent(destinationChannelName, {
162
+ detail: message
163
+ })
164
+ );
165
+ });
166
+ innerPublisherState = {
167
+ dispose: innerPublisherUnsubscribe,
168
+ numSubscribers: 0
169
+ };
170
+ }
171
+ innerPublisherState.numSubscribers++;
172
+ const unsubscribe = demultiplexedDataPublisher.on(channelName, subscriber, options);
173
+ let isActive = true;
174
+ function handleUnsubscribe() {
175
+ if (!isActive) {
176
+ return;
177
+ }
178
+ isActive = false;
179
+ options?.signal.removeEventListener("abort", handleUnsubscribe);
180
+ innerPublisherState.numSubscribers--;
181
+ if (innerPublisherState.numSubscribers === 0) {
182
+ innerPublisherState.dispose();
183
+ innerPublisherState = void 0;
184
+ }
185
+ unsubscribe();
186
+ }
187
+ options?.signal.addEventListener("abort", handleUnsubscribe);
188
+ return handleUnsubscribe;
189
+ }
190
+ };
191
+ }
192
+
193
+ exports.createAsyncIterableFromDataPublisher = createAsyncIterableFromDataPublisher;
194
+ exports.demultiplexDataPublisher = demultiplexDataPublisher;
195
+ exports.getDataPublisherFromEventEmitter = getDataPublisherFromEventEmitter;
196
+ //# sourceMappingURL=index.node.cjs.map
197
+ //# sourceMappingURL=index.node.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/async-iterable.ts","../src/data-publisher.ts","../src/demultiplex.ts"],"names":["SolanaError","SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_STATE_MISSING","SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_MUST_NOT_POLL_BEFORE_RESOLVING_EXISTING_MESSAGE_PROMISE"],"mappings":"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","file":"index.node.cjs","sourcesContent":["import {\n SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_MUST_NOT_POLL_BEFORE_RESOLVING_EXISTING_MESSAGE_PROMISE,\n SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_STATE_MISSING,\n SolanaError,\n} from '@solana/errors';\n\nimport { DataPublisher } from './data-publisher';\n\ntype Config = Readonly<{\n abortSignal: AbortSignal;\n dataChannelName: string;\n // FIXME: It would be nice to be able to constrain the type of `dataPublisher` to one that\n // definitely supports the `dataChannelName` and `errorChannelName` channels, and\n // furthermore publishes `TData` on the `dataChannelName` channel. This is more difficult\n // than it should be: https://tsplay.dev/NlZelW\n dataPublisher: DataPublisher;\n errorChannelName: string;\n}>;\n\nconst enum PublishType {\n DATA,\n ERROR,\n}\n\ntype IteratorKey = symbol;\ntype IteratorState<TData> =\n | {\n __hasPolled: false;\n publishQueue: (\n | {\n __type: PublishType.DATA;\n data: TData;\n }\n | {\n __type: PublishType.ERROR;\n err: unknown;\n }\n )[];\n }\n | {\n __hasPolled: true;\n onData: (data: TData) => void;\n onError: Parameters<ConstructorParameters<typeof Promise>[0]>[1];\n };\n\nlet EXPLICIT_ABORT_TOKEN: symbol;\nfunction createExplicitAbortToken() {\n // This function is an annoying workaround to prevent `process.env.NODE_ENV` from appearing at\n // the top level of this module and thwarting an optimizing compiler's attempt to tree-shake.\n return Symbol(\n process.env.NODE_ENV !== \"production\"\n ? \"This symbol is thrown from a socket's iterator when the connection is explicitly \" +\n 'aborted by the user'\n : undefined,\n );\n}\n\nconst UNINITIALIZED = Symbol();\n\nexport function createAsyncIterableFromDataPublisher<TData>({\n abortSignal,\n dataChannelName,\n dataPublisher,\n errorChannelName,\n}: Config): AsyncIterable<TData> {\n const iteratorState: Map<IteratorKey, IteratorState<TData>> = new Map();\n function publishErrorToAllIterators(reason: unknown) {\n for (const [iteratorKey, state] of iteratorState.entries()) {\n if (state.__hasPolled) {\n iteratorState.delete(iteratorKey);\n state.onError(reason);\n } else {\n state.publishQueue.push({\n __type: PublishType.ERROR,\n err: reason,\n });\n }\n }\n }\n const abortController = new AbortController();\n abortSignal.addEventListener('abort', () => {\n abortController.abort();\n publishErrorToAllIterators((EXPLICIT_ABORT_TOKEN ||= createExplicitAbortToken()));\n });\n const options = { signal: abortController.signal } as const;\n let firstError: unknown | typeof UNINITIALIZED = UNINITIALIZED;\n dataPublisher.on(\n errorChannelName,\n err => {\n if (firstError === UNINITIALIZED) {\n firstError = err;\n abortController.abort();\n publishErrorToAllIterators(err);\n }\n },\n options,\n );\n dataPublisher.on(\n dataChannelName,\n data => {\n iteratorState.forEach((state, iteratorKey) => {\n if (state.__hasPolled) {\n const { onData } = state;\n iteratorState.set(iteratorKey, { __hasPolled: false, publishQueue: [] });\n onData(data as TData);\n } else {\n state.publishQueue.push({\n __type: PublishType.DATA,\n data: data as TData,\n });\n }\n });\n },\n options,\n );\n return {\n async *[Symbol.asyncIterator]() {\n if (abortSignal.aborted) {\n return;\n }\n if (firstError !== UNINITIALIZED) {\n throw firstError;\n }\n const iteratorKey = Symbol();\n iteratorState.set(iteratorKey, { __hasPolled: false, publishQueue: [] });\n try {\n while (true) {\n const state = iteratorState.get(iteratorKey);\n if (!state) {\n // There should always be state by now.\n throw new SolanaError(SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_STATE_MISSING);\n }\n if (state.__hasPolled) {\n // You should never be able to poll twice in a row.\n throw new SolanaError(\n SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_MUST_NOT_POLL_BEFORE_RESOLVING_EXISTING_MESSAGE_PROMISE,\n );\n }\n const publishQueue = state.publishQueue;\n try {\n if (publishQueue.length) {\n state.publishQueue = [];\n for (const item of publishQueue) {\n if (item.__type === PublishType.DATA) {\n yield item.data;\n } else {\n throw item.err;\n }\n }\n } else {\n yield await new Promise<TData>((resolve, reject) => {\n iteratorState.set(iteratorKey, {\n __hasPolled: true,\n onData: resolve,\n onError: reject,\n });\n });\n }\n } catch (e) {\n if (e === (EXPLICIT_ABORT_TOKEN ||= createExplicitAbortToken())) {\n return;\n } else {\n throw e;\n }\n }\n }\n } finally {\n iteratorState.delete(iteratorKey);\n }\n },\n };\n}\n","import { TypedEventEmitter, TypedEventTarget } from './event-emitter';\n\ntype UnsubscribeFn = () => void;\n\nexport interface DataPublisher<TDataByChannelName extends Record<string, unknown> = Record<string, unknown>> {\n on<const TChannelName extends keyof TDataByChannelName>(\n channelName: TChannelName,\n subscriber: (data: TDataByChannelName[TChannelName]) => void,\n options?: { signal: AbortSignal },\n ): UnsubscribeFn;\n}\n\nexport function getDataPublisherFromEventEmitter<TEventMap extends Record<string, Event>>(\n eventEmitter: TypedEventEmitter<TEventMap> | TypedEventTarget<TEventMap>,\n): DataPublisher<{\n [TEventType in keyof TEventMap]: TEventMap[TEventType] extends CustomEvent ? TEventMap[TEventType]['detail'] : null;\n}> {\n return {\n on(channelName, subscriber, options) {\n function innerListener(ev: Event) {\n if (ev instanceof CustomEvent) {\n const data = (ev as CustomEvent<TEventMap[typeof channelName]>).detail;\n (subscriber as unknown as (data: TEventMap[typeof channelName]) => void)(data);\n } else {\n (subscriber as () => void)();\n }\n }\n eventEmitter.addEventListener(channelName, innerListener, options);\n return () => {\n eventEmitter.removeEventListener(channelName, innerListener);\n };\n },\n };\n}\n","import { DataPublisher, getDataPublisherFromEventEmitter } from './data-publisher';\n\nexport function demultiplexDataPublisher<\n TDataPublisher extends DataPublisher,\n const TChannelName extends Parameters<TDataPublisher['on']>[0],\n>(\n publisher: TDataPublisher,\n sourceChannelName: TChannelName,\n messageTransformer: (\n // FIXME: Deriving the type of the message from `TDataPublisher` and `TChannelName` would\n // help callers to constrain their transform functions.\n message: unknown,\n ) => [destinationChannelName: string, message: unknown] | void,\n): DataPublisher {\n let innerPublisherState:\n | {\n readonly dispose: () => void;\n numSubscribers: number;\n }\n | undefined;\n const eventTarget = new EventTarget();\n const demultiplexedDataPublisher = getDataPublisherFromEventEmitter(eventTarget);\n return {\n ...demultiplexedDataPublisher,\n on(channelName, subscriber, options) {\n if (!innerPublisherState) {\n const innerPublisherUnsubscribe = publisher.on(sourceChannelName, sourceMessage => {\n const transformResult = messageTransformer(sourceMessage);\n if (!transformResult) {\n return;\n }\n const [destinationChannelName, message] = transformResult;\n eventTarget.dispatchEvent(\n new CustomEvent(destinationChannelName, {\n detail: message,\n }),\n );\n });\n innerPublisherState = {\n dispose: innerPublisherUnsubscribe,\n numSubscribers: 0,\n };\n }\n innerPublisherState.numSubscribers++;\n const unsubscribe = demultiplexedDataPublisher.on(channelName, subscriber, options);\n let isActive = true;\n function handleUnsubscribe() {\n if (!isActive) {\n return;\n }\n isActive = false;\n options?.signal.removeEventListener('abort', handleUnsubscribe);\n innerPublisherState!.numSubscribers--;\n if (innerPublisherState!.numSubscribers === 0) {\n innerPublisherState!.dispose();\n innerPublisherState = undefined;\n }\n unsubscribe();\n }\n options?.signal.addEventListener('abort', handleUnsubscribe);\n return handleUnsubscribe;\n },\n };\n}\n"]}
@@ -0,0 +1,193 @@
1
+ import { SolanaError, SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_STATE_MISSING, SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_MUST_NOT_POLL_BEFORE_RESOLVING_EXISTING_MESSAGE_PROMISE } from '@solana/errors';
2
+
3
+ // src/async-iterable.ts
4
+ var EXPLICIT_ABORT_TOKEN;
5
+ function createExplicitAbortToken() {
6
+ return Symbol(
7
+ process.env.NODE_ENV !== "production" ? "This symbol is thrown from a socket's iterator when the connection is explicitly aborted by the user" : void 0
8
+ );
9
+ }
10
+ var UNINITIALIZED = Symbol();
11
+ function createAsyncIterableFromDataPublisher({
12
+ abortSignal,
13
+ dataChannelName,
14
+ dataPublisher,
15
+ errorChannelName
16
+ }) {
17
+ const iteratorState = /* @__PURE__ */ new Map();
18
+ function publishErrorToAllIterators(reason) {
19
+ for (const [iteratorKey, state] of iteratorState.entries()) {
20
+ if (state.__hasPolled) {
21
+ iteratorState.delete(iteratorKey);
22
+ state.onError(reason);
23
+ } else {
24
+ state.publishQueue.push({
25
+ __type: 1 /* ERROR */,
26
+ err: reason
27
+ });
28
+ }
29
+ }
30
+ }
31
+ const abortController = new AbortController();
32
+ abortSignal.addEventListener("abort", () => {
33
+ abortController.abort();
34
+ publishErrorToAllIterators(EXPLICIT_ABORT_TOKEN ||= createExplicitAbortToken());
35
+ });
36
+ const options = { signal: abortController.signal };
37
+ let firstError = UNINITIALIZED;
38
+ dataPublisher.on(
39
+ errorChannelName,
40
+ (err) => {
41
+ if (firstError === UNINITIALIZED) {
42
+ firstError = err;
43
+ abortController.abort();
44
+ publishErrorToAllIterators(err);
45
+ }
46
+ },
47
+ options
48
+ );
49
+ dataPublisher.on(
50
+ dataChannelName,
51
+ (data) => {
52
+ iteratorState.forEach((state, iteratorKey) => {
53
+ if (state.__hasPolled) {
54
+ const { onData } = state;
55
+ iteratorState.set(iteratorKey, { __hasPolled: false, publishQueue: [] });
56
+ onData(data);
57
+ } else {
58
+ state.publishQueue.push({
59
+ __type: 0 /* DATA */,
60
+ data
61
+ });
62
+ }
63
+ });
64
+ },
65
+ options
66
+ );
67
+ return {
68
+ async *[Symbol.asyncIterator]() {
69
+ if (abortSignal.aborted) {
70
+ return;
71
+ }
72
+ if (firstError !== UNINITIALIZED) {
73
+ throw firstError;
74
+ }
75
+ const iteratorKey = Symbol();
76
+ iteratorState.set(iteratorKey, { __hasPolled: false, publishQueue: [] });
77
+ try {
78
+ while (true) {
79
+ const state = iteratorState.get(iteratorKey);
80
+ if (!state) {
81
+ throw new SolanaError(SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_STATE_MISSING);
82
+ }
83
+ if (state.__hasPolled) {
84
+ throw new SolanaError(
85
+ SOLANA_ERROR__INVARIANT_VIOLATION__SUBSCRIPTION_ITERATOR_MUST_NOT_POLL_BEFORE_RESOLVING_EXISTING_MESSAGE_PROMISE
86
+ );
87
+ }
88
+ const publishQueue = state.publishQueue;
89
+ try {
90
+ if (publishQueue.length) {
91
+ state.publishQueue = [];
92
+ for (const item of publishQueue) {
93
+ if (item.__type === 0 /* DATA */) {
94
+ yield item.data;
95
+ } else {
96
+ throw item.err;
97
+ }
98
+ }
99
+ } else {
100
+ yield await new Promise((resolve, reject) => {
101
+ iteratorState.set(iteratorKey, {
102
+ __hasPolled: true,
103
+ onData: resolve,
104
+ onError: reject
105
+ });
106
+ });
107
+ }
108
+ } catch (e) {
109
+ if (e === (EXPLICIT_ABORT_TOKEN ||= createExplicitAbortToken())) {
110
+ return;
111
+ } else {
112
+ throw e;
113
+ }
114
+ }
115
+ }
116
+ } finally {
117
+ iteratorState.delete(iteratorKey);
118
+ }
119
+ }
120
+ };
121
+ }
122
+
123
+ // src/data-publisher.ts
124
+ function getDataPublisherFromEventEmitter(eventEmitter) {
125
+ return {
126
+ on(channelName, subscriber, options) {
127
+ function innerListener(ev) {
128
+ if (ev instanceof CustomEvent) {
129
+ const data = ev.detail;
130
+ subscriber(data);
131
+ } else {
132
+ subscriber();
133
+ }
134
+ }
135
+ eventEmitter.addEventListener(channelName, innerListener, options);
136
+ return () => {
137
+ eventEmitter.removeEventListener(channelName, innerListener);
138
+ };
139
+ }
140
+ };
141
+ }
142
+
143
+ // src/demultiplex.ts
144
+ function demultiplexDataPublisher(publisher, sourceChannelName, messageTransformer) {
145
+ let innerPublisherState;
146
+ const eventTarget = new EventTarget();
147
+ const demultiplexedDataPublisher = getDataPublisherFromEventEmitter(eventTarget);
148
+ return {
149
+ ...demultiplexedDataPublisher,
150
+ on(channelName, subscriber, options) {
151
+ if (!innerPublisherState) {
152
+ const innerPublisherUnsubscribe = publisher.on(sourceChannelName, (sourceMessage) => {
153
+ const transformResult = messageTransformer(sourceMessage);
154
+ if (!transformResult) {
155
+ return;
156
+ }
157
+ const [destinationChannelName, message] = transformResult;
158
+ eventTarget.dispatchEvent(
159
+ new CustomEvent(destinationChannelName, {
160
+ detail: message
161
+ })
162
+ );
163
+ });
164
+ innerPublisherState = {
165
+ dispose: innerPublisherUnsubscribe,
166
+ numSubscribers: 0
167
+ };
168
+ }
169
+ innerPublisherState.numSubscribers++;
170
+ const unsubscribe = demultiplexedDataPublisher.on(channelName, subscriber, options);
171
+ let isActive = true;
172
+ function handleUnsubscribe() {
173
+ if (!isActive) {
174
+ return;
175
+ }
176
+ isActive = false;
177
+ options?.signal.removeEventListener("abort", handleUnsubscribe);
178
+ innerPublisherState.numSubscribers--;
179
+ if (innerPublisherState.numSubscribers === 0) {
180
+ innerPublisherState.dispose();
181
+ innerPublisherState = void 0;
182
+ }
183
+ unsubscribe();
184
+ }
185
+ options?.signal.addEventListener("abort", handleUnsubscribe);
186
+ return handleUnsubscribe;
187
+ }
188
+ };
189
+ }
190
+
191
+ export { createAsyncIterableFromDataPublisher, demultiplexDataPublisher, getDataPublisherFromEventEmitter };
192
+ //# sourceMappingURL=index.node.mjs.map
193
+ //# sourceMappingURL=index.node.mjs.map