@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.
- package/LICENSE +20 -0
- package/README.md +124 -0
- package/dist/index.browser.cjs +197 -0
- package/dist/index.browser.cjs.map +1 -0
- package/dist/index.browser.mjs +193 -0
- package/dist/index.browser.mjs.map +1 -0
- package/dist/index.native.mjs +193 -0
- package/dist/index.native.mjs.map +1 -0
- package/dist/index.node.cjs +197 -0
- package/dist/index.node.cjs.map +1 -0
- package/dist/index.node.mjs +193 -0
- package/dist/index.node.mjs.map +1 -0
- package/dist/types/async-iterable.d.ts +10 -0
- package/dist/types/async-iterable.d.ts.map +1 -0
- package/dist/types/data-publisher.d.ts +12 -0
- package/dist/types/data-publisher.d.ts.map +1 -0
- package/dist/types/demultiplex.d.ts +3 -0
- package/dist/types/demultiplex.d.ts.map +1 -0
- package/dist/types/event-emitter.d.ts +19 -0
- package/dist/types/event-emitter.d.ts.map +1 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -0
- package/package.json +89 -0
|
@@ -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":";;;;;AA6CA,IAAI,oBAAA,CAAA;AACJ,SAAS,wBAA2B,GAAA;AAGhC,EAAO,OAAA,MAAA;AAAA,IACH,OAAA,CAAA,GAAA,CAAA,QAAA,KAAyB,eACnB,sGAEA,GAAA,KAAA,CAAA;AAAA,GACV,CAAA;AACJ,CAAA;AAEA,IAAM,gBAAgB,MAAO,EAAA,CAAA;AAEtB,SAAS,oCAA4C,CAAA;AAAA,EACxD,WAAA;AAAA,EACA,eAAA;AAAA,EACA,aAAA;AAAA,EACA,gBAAA;AACJ,CAAiC,EAAA;AAC7B,EAAM,MAAA,aAAA,uBAA4D,GAAI,EAAA,CAAA;AACtE,EAAA,SAAS,2BAA2B,MAAiB,EAAA;AACjD,IAAA,KAAA,MAAW,CAAC,WAAa,EAAA,KAAK,CAAK,IAAA,aAAA,CAAc,SAAW,EAAA;AACxD,MAAA,IAAI,MAAM,WAAa,EAAA;AACnB,QAAA,aAAA,CAAc,OAAO,WAAW,CAAA,CAAA;AAChC,QAAA,KAAA,CAAM,QAAQ,MAAM,CAAA,CAAA;AAAA,OACjB,MAAA;AACH,QAAA,KAAA,CAAM,aAAa,IAAK,CAAA;AAAA,UACpB,MAAQ,EAAA,CAAA;AAAA,UACR,GAAK,EAAA,MAAA;AAAA,SACR,CAAA,CAAA;AAAA,OACL;AAAA,KACJ;AAAA,GACJ;AACA,EAAM,MAAA,eAAA,GAAkB,IAAI,eAAgB,EAAA,CAAA;AAC5C,EAAY,WAAA,CAAA,gBAAA,CAAiB,SAAS,MAAM;AACxC,IAAA,eAAA,CAAgB,KAAM,EAAA,CAAA;AACtB,IAA4B,0BAAA,CAAA,oBAAA,KAAyB,0BAA2B,CAAA,CAAA;AAAA,GACnF,CAAA,CAAA;AACD,EAAA,MAAM,OAAU,GAAA,EAAE,MAAQ,EAAA,eAAA,CAAgB,MAAO,EAAA,CAAA;AACjD,EAAA,IAAI,UAA6C,GAAA,aAAA,CAAA;AACjD,EAAc,aAAA,CAAA,EAAA;AAAA,IACV,gBAAA;AAAA,IACA,CAAO,GAAA,KAAA;AACH,MAAA,IAAI,eAAe,aAAe,EAAA;AAC9B,QAAa,UAAA,GAAA,GAAA,CAAA;AACb,QAAA,eAAA,CAAgB,KAAM,EAAA,CAAA;AACtB,QAAA,0BAAA,CAA2B,GAAG,CAAA,CAAA;AAAA,OAClC;AAAA,KACJ;AAAA,IACA,OAAA;AAAA,GACJ,CAAA;AACA,EAAc,aAAA,CAAA,EAAA;AAAA,IACV,eAAA;AAAA,IACA,CAAQ,IAAA,KAAA;AACJ,MAAc,aAAA,CAAA,OAAA,CAAQ,CAAC,KAAA,EAAO,WAAgB,KAAA;AAC1C,QAAA,IAAI,MAAM,WAAa,EAAA;AACnB,UAAM,MAAA,EAAE,QAAW,GAAA,KAAA,CAAA;AACnB,UAAc,aAAA,CAAA,GAAA,CAAI,aAAa,EAAE,WAAA,EAAa,OAAO,YAAc,EAAA,IAAI,CAAA,CAAA;AACvE,UAAA,MAAA,CAAO,IAAa,CAAA,CAAA;AAAA,SACjB,MAAA;AACH,UAAA,KAAA,CAAM,aAAa,IAAK,CAAA;AAAA,YACpB,MAAQ,EAAA,CAAA;AAAA,YACR,IAAA;AAAA,WACH,CAAA,CAAA;AAAA,SACL;AAAA,OACH,CAAA,CAAA;AAAA,KACL;AAAA,IACA,OAAA;AAAA,GACJ,CAAA;AACA,EAAO,OAAA;AAAA,IACH,QAAQ,MAAO,CAAA,aAAa,CAAI,GAAA;AAC5B,MAAA,IAAI,YAAY,OAAS,EAAA;AACrB,QAAA,OAAA;AAAA,OACJ;AACA,MAAA,IAAI,eAAe,aAAe,EAAA;AAC9B,QAAM,MAAA,UAAA,CAAA;AAAA,OACV;AACA,MAAA,MAAM,cAAc,MAAO,EAAA,CAAA;AAC3B,MAAc,aAAA,CAAA,GAAA,CAAI,aAAa,EAAE,WAAA,EAAa,OAAO,YAAc,EAAA,IAAI,CAAA,CAAA;AACvE,MAAI,IAAA;AACA,QAAA,OAAO,IAAM,EAAA;AACT,UAAM,MAAA,KAAA,GAAQ,aAAc,CAAA,GAAA,CAAI,WAAW,CAAA,CAAA;AAC3C,UAAA,IAAI,CAAC,KAAO,EAAA;AAER,YAAM,MAAA,IAAIA,mBAAYC,6EAAsE,CAAA,CAAA;AAAA,WAChG;AACA,UAAA,IAAI,MAAM,WAAa,EAAA;AAEnB,YAAA,MAAM,IAAID,kBAAA;AAAA,cACNE,uHAAA;AAAA,aACJ,CAAA;AAAA,WACJ;AACA,UAAA,MAAM,eAAe,KAAM,CAAA,YAAA,CAAA;AAC3B,UAAI,IAAA;AACA,YAAA,IAAI,aAAa,MAAQ,EAAA;AACrB,cAAA,KAAA,CAAM,eAAe,EAAC,CAAA;AACtB,cAAA,KAAA,MAAW,QAAQ,YAAc,EAAA;AAC7B,gBAAI,IAAA,IAAA,CAAK,WAAW,CAAkB,aAAA;AAClC,kBAAA,MAAM,IAAK,CAAA,IAAA,CAAA;AAAA,iBACR,MAAA;AACH,kBAAA,MAAM,IAAK,CAAA,GAAA,CAAA;AAAA,iBACf;AAAA,eACJ;AAAA,aACG,MAAA;AACH,cAAA,MAAM,MAAM,IAAI,OAAe,CAAA,CAAC,SAAS,MAAW,KAAA;AAChD,gBAAA,aAAA,CAAc,IAAI,WAAa,EAAA;AAAA,kBAC3B,WAAa,EAAA,IAAA;AAAA,kBACb,MAAQ,EAAA,OAAA;AAAA,kBACR,OAAS,EAAA,MAAA;AAAA,iBACZ,CAAA,CAAA;AAAA,eACJ,CAAA,CAAA;AAAA,aACL;AAAA,mBACK,CAAG,EAAA;AACR,YAAI,IAAA,CAAA,MAAO,oBAAyB,KAAA,wBAAA,EAA6B,CAAA,EAAA;AAC7D,cAAA,OAAA;AAAA,aACG,MAAA;AACH,cAAM,MAAA,CAAA,CAAA;AAAA,aACV;AAAA,WACJ;AAAA,SACJ;AAAA,OACF,SAAA;AACE,QAAA,aAAA,CAAc,OAAO,WAAW,CAAA,CAAA;AAAA,OACpC;AAAA,KACJ;AAAA,GACJ,CAAA;AACJ,CAAA;;;AC/JO,SAAS,iCACZ,YAGD,EAAA;AACC,EAAO,OAAA;AAAA,IACH,EAAA,CAAG,WAAa,EAAA,UAAA,EAAY,OAAS,EAAA;AACjC,MAAA,SAAS,cAAc,EAAW,EAAA;AAC9B,QAAA,IAAI,cAAc,WAAa,EAAA;AAC3B,UAAA,MAAM,OAAQ,EAAkD,CAAA,MAAA,CAAA;AAChE,UAAC,WAAwE,IAAI,CAAA,CAAA;AAAA,SAC1E,MAAA;AACH,UAAC,UAA0B,EAAA,CAAA;AAAA,SAC/B;AAAA,OACJ;AACA,MAAa,YAAA,CAAA,gBAAA,CAAiB,WAAa,EAAA,aAAA,EAAe,OAAO,CAAA,CAAA;AACjE,MAAA,OAAO,MAAM;AACT,QAAa,YAAA,CAAA,mBAAA,CAAoB,aAAa,aAAa,CAAA,CAAA;AAAA,OAC/D,CAAA;AAAA,KACJ;AAAA,GACJ,CAAA;AACJ,CAAA;;;AC/BO,SAAS,wBAAA,CAIZ,SACA,EAAA,iBAAA,EACA,kBAKa,EAAA;AACb,EAAI,IAAA,mBAAA,CAAA;AAMJ,EAAM,MAAA,WAAA,GAAc,IAAI,WAAY,EAAA,CAAA;AACpC,EAAM,MAAA,0BAAA,GAA6B,iCAAiC,WAAW,CAAA,CAAA;AAC/E,EAAO,OAAA;AAAA,IACH,GAAG,0BAAA;AAAA,IACH,EAAA,CAAG,WAAa,EAAA,UAAA,EAAY,OAAS,EAAA;AACjC,MAAA,IAAI,CAAC,mBAAqB,EAAA;AACtB,QAAA,MAAM,yBAA4B,GAAA,SAAA,CAAU,EAAG,CAAA,iBAAA,EAAmB,CAAiB,aAAA,KAAA;AAC/E,UAAM,MAAA,eAAA,GAAkB,mBAAmB,aAAa,CAAA,CAAA;AACxD,UAAA,IAAI,CAAC,eAAiB,EAAA;AAClB,YAAA,OAAA;AAAA,WACJ;AACA,UAAM,MAAA,CAAC,sBAAwB,EAAA,OAAO,CAAI,GAAA,eAAA,CAAA;AAC1C,UAAY,WAAA,CAAA,aAAA;AAAA,YACR,IAAI,YAAY,sBAAwB,EAAA;AAAA,cACpC,MAAQ,EAAA,OAAA;AAAA,aACX,CAAA;AAAA,WACL,CAAA;AAAA,SACH,CAAA,CAAA;AACD,QAAsB,mBAAA,GAAA;AAAA,UAClB,OAAS,EAAA,yBAAA;AAAA,UACT,cAAgB,EAAA,CAAA;AAAA,SACpB,CAAA;AAAA,OACJ;AACA,MAAoB,mBAAA,CAAA,cAAA,EAAA,CAAA;AACpB,MAAA,MAAM,WAAc,GAAA,0BAAA,CAA2B,EAAG,CAAA,WAAA,EAAa,YAAY,OAAO,CAAA,CAAA;AAClF,MAAA,IAAI,QAAW,GAAA,IAAA,CAAA;AACf,MAAA,SAAS,iBAAoB,GAAA;AACzB,QAAA,IAAI,CAAC,QAAU,EAAA;AACX,UAAA,OAAA;AAAA,SACJ;AACA,QAAW,QAAA,GAAA,KAAA,CAAA;AACX,QAAS,OAAA,EAAA,MAAA,CAAO,mBAAoB,CAAA,OAAA,EAAS,iBAAiB,CAAA,CAAA;AAC9D,QAAqB,mBAAA,CAAA,cAAA,EAAA,CAAA;AACrB,QAAI,IAAA,mBAAA,CAAqB,mBAAmB,CAAG,EAAA;AAC3C,UAAA,mBAAA,CAAqB,OAAQ,EAAA,CAAA;AAC7B,UAAsB,mBAAA,GAAA,KAAA,CAAA,CAAA;AAAA,SAC1B;AACA,QAAY,WAAA,EAAA,CAAA;AAAA,OAChB;AACA,MAAS,OAAA,EAAA,MAAA,CAAO,gBAAiB,CAAA,OAAA,EAAS,iBAAiB,CAAA,CAAA;AAC3D,MAAO,OAAA,iBAAA,CAAA;AAAA,KACX;AAAA,GACJ,CAAA;AACJ","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
|