pmxt-core 2.19.5 → 2.20.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 (57) hide show
  1. package/dist/BaseExchange.d.ts +69 -30
  2. package/dist/BaseExchange.js +124 -82
  3. package/dist/exchanges/baozi/index.d.ts +2 -0
  4. package/dist/exchanges/baozi/index.js +2 -0
  5. package/dist/exchanges/baozi/price.d.ts +3 -0
  6. package/dist/exchanges/baozi/price.js +16 -0
  7. package/dist/exchanges/baozi/price.test.d.ts +1 -0
  8. package/dist/exchanges/baozi/price.test.js +33 -0
  9. package/dist/exchanges/baozi/utils.js +5 -9
  10. package/dist/exchanges/kalshi/api.d.ts +1 -1
  11. package/dist/exchanges/kalshi/api.js +1 -1
  12. package/dist/exchanges/kalshi/fetchOHLCV.js +5 -4
  13. package/dist/exchanges/kalshi/fetchOrderBook.js +21 -21
  14. package/dist/exchanges/kalshi/fetchTrades.js +2 -1
  15. package/dist/exchanges/kalshi/index.d.ts +3 -1
  16. package/dist/exchanges/kalshi/index.js +19 -16
  17. package/dist/exchanges/kalshi/price.d.ts +3 -0
  18. package/dist/exchanges/kalshi/price.js +14 -0
  19. package/dist/exchanges/kalshi/price.test.d.ts +1 -0
  20. package/dist/exchanges/kalshi/price.test.js +24 -0
  21. package/dist/exchanges/kalshi/utils.js +5 -4
  22. package/dist/exchanges/limitless/api.d.ts +1 -1
  23. package/dist/exchanges/limitless/api.js +1 -1
  24. package/dist/exchanges/limitless/index.d.ts +58 -19
  25. package/dist/exchanges/limitless/index.js +169 -101
  26. package/dist/exchanges/limitless/websocket.d.ts +10 -3
  27. package/dist/exchanges/limitless/websocket.js +71 -52
  28. package/dist/exchanges/myriad/api.d.ts +1 -1
  29. package/dist/exchanges/myriad/api.js +1 -1
  30. package/dist/exchanges/myriad/index.d.ts +3 -1
  31. package/dist/exchanges/myriad/index.js +7 -4
  32. package/dist/exchanges/myriad/price.d.ts +1 -0
  33. package/dist/exchanges/myriad/price.js +7 -0
  34. package/dist/exchanges/myriad/price.test.d.ts +1 -0
  35. package/dist/exchanges/myriad/price.test.js +17 -0
  36. package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
  37. package/dist/exchanges/polymarket/api-clob.js +1 -1
  38. package/dist/exchanges/polymarket/api-data.d.ts +1 -1
  39. package/dist/exchanges/polymarket/api-data.js +1 -1
  40. package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
  41. package/dist/exchanges/polymarket/api-gamma.js +1 -1
  42. package/dist/exchanges/polymarket/index.d.ts +28 -15
  43. package/dist/exchanges/polymarket/index.js +217 -137
  44. package/dist/exchanges/polymarket/websocket.d.ts +11 -4
  45. package/dist/exchanges/polymarket/websocket.js +58 -36
  46. package/dist/exchanges/probable/api.d.ts +1 -1
  47. package/dist/exchanges/probable/api.js +1 -1
  48. package/dist/exchanges/probable/index.d.ts +2 -0
  49. package/dist/exchanges/probable/index.js +2 -0
  50. package/dist/subscriber/base.d.ts +82 -0
  51. package/dist/subscriber/base.js +2 -0
  52. package/dist/subscriber/external/goldsky.d.ts +96 -0
  53. package/dist/subscriber/external/goldsky.js +412 -0
  54. package/dist/subscriber/watcher.d.ts +85 -0
  55. package/dist/subscriber/watcher.js +178 -0
  56. package/dist/types.d.ts +5 -0
  57. package/package.json +3 -3
@@ -0,0 +1,85 @@
1
+ import { Trade } from '../types';
2
+ import { BaseSubscriber, SubscribedActivityBuilder, SubscribedAddressSnapshot, SubscriberConfig, SubscriptionOption } from './base';
3
+ export type FetchFn = (address: string, types: SubscriptionOption[]) => Promise<SubscribedAddressSnapshot>;
4
+ export interface AddressWatcherConfig {
5
+ /**
6
+ * Subscriber. Responsible for polling or pushing on-chain events
7
+ * for each watched address. When an event arrives, the watcher fetches only
8
+ * the activity types that could not be derived from the event data.
9
+ */
10
+ subscriber: BaseSubscriber;
11
+ /**
12
+ * Optional function to extract partial activity directly from the raw
13
+ * subscription event data, avoiding REST/RPC calls for the types
14
+ * it can populate.
15
+ *
16
+ * Pair with the matching subscription builder in GoldSkySubscriber:
17
+ * - `POLYMARKET_TRADES_SUBSCRIPTION` + `buildPolymarketTradesActivity`
18
+ * - `LIMITLESS_DEFAULT_SUBSCRIPTION` + `buildLimitlessBalanceActivity`
19
+ *
20
+ * Return `null` to fall back to a full REST/RPC fetch for every event.
21
+ */
22
+ buildActivity?: SubscribedActivityBuilder;
23
+ }
24
+ export interface WatcherConfig extends Omit<SubscriberConfig, 'buildSubscription'> {
25
+ }
26
+ /**
27
+ * Resolves waiting promises whenever a subscriber reports an on-chain change.
28
+ *
29
+ * The watcher is purely reactive — it does not poll on its own. The supplied
30
+ * `BaseSubscriber` is responsible for all polling or push delivery. When the
31
+ * subscriber fires an event, the watcher:
32
+ * - Calls `buildActivity` to derive what it can from the raw event data.
33
+ * - Fetches only the missing types via `fetchFn` (REST/RPC).
34
+ * - Dispatches to any waiting `watch()` promises if the snapshot changed.
35
+ *
36
+ * CCXT Pro streaming pattern:
37
+ * - First `watch()` call → initial snapshot returned immediately via `fetchFn`.
38
+ * - Subsequent calls → block until the next subscriber-driven update.
39
+ */
40
+ export declare class AddressWatcher {
41
+ private lastState;
42
+ private watchedTypes;
43
+ private assetIdResolvers;
44
+ private resolvers;
45
+ private readonly fetchFn;
46
+ private readonly subscriber;
47
+ private readonly buildActivity?;
48
+ constructor(fetchFn: FetchFn, config: AddressWatcherConfig);
49
+ /**
50
+ * Watch an address for activity changes (CCXT Pro pattern).
51
+ *
52
+ * @param address - Public wallet address to watch
53
+ * @param types - Subset of activity to watch
54
+ * @param assetId - Optional asset id to filter activity changes.
55
+ * @returns Promise that resolves with the latest SubscribedAddressSnapshot snapshot
56
+ */
57
+ watch(address: string, types: SubscriptionOption[], assetId: string): Promise<Trade[]>;
58
+ watch(address: string, types: SubscriptionOption[]): Promise<SubscribedAddressSnapshot>;
59
+ /**
60
+ * Stop watching an address, cancel its poll timer, unsubscribe from the
61
+ * subscriber, and reject any pending callers.
62
+ *
63
+ * @param address - Public wallet address to unwatch
64
+ */
65
+ unwatch(address: string): void;
66
+ /** Stop all active watchers and close the underlying trigger. */
67
+ close(): void;
68
+ /**
69
+ * Handle raw event data from the subscriber.
70
+ *
71
+ * @param address - Public wallet address to watch
72
+ * @param data - Raw event payload from the subscriber
73
+ *
74
+ * Calls `buildActivity` to attempt constructing a partial result from the
75
+ * event payload. Fetches only the types that are missing from the partial,
76
+ * then resolves any waiting promises if a change is detected.
77
+ *
78
+ * Falls back to a full `poll()` when no `buildActivity` is configured or
79
+ * when it returns `null`.
80
+ */
81
+ private handleSubscriptionData;
82
+ private dispatchAssetResolvers;
83
+ private getChanged;
84
+ private hasChanges;
85
+ }
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AddressWatcher = void 0;
4
+ // ----------------------------------------------------------------------------
5
+ // AddressWatcher Class
6
+ // ----------------------------------------------------------------------------
7
+ /**
8
+ * Resolves waiting promises whenever a subscriber reports an on-chain change.
9
+ *
10
+ * The watcher is purely reactive — it does not poll on its own. The supplied
11
+ * `BaseSubscriber` is responsible for all polling or push delivery. When the
12
+ * subscriber fires an event, the watcher:
13
+ * - Calls `buildActivity` to derive what it can from the raw event data.
14
+ * - Fetches only the missing types via `fetchFn` (REST/RPC).
15
+ * - Dispatches to any waiting `watch()` promises if the snapshot changed.
16
+ *
17
+ * CCXT Pro streaming pattern:
18
+ * - First `watch()` call → initial snapshot returned immediately via `fetchFn`.
19
+ * - Subsequent calls → block until the next subscriber-driven update.
20
+ */
21
+ class AddressWatcher {
22
+ lastState = new Map();
23
+ watchedTypes = new Map();
24
+ assetIdResolvers = new Map();
25
+ resolvers = new Map();
26
+ fetchFn;
27
+ subscriber;
28
+ buildActivity;
29
+ constructor(fetchFn, config) {
30
+ this.fetchFn = fetchFn;
31
+ this.subscriber = config.subscriber;
32
+ this.buildActivity = config.buildActivity;
33
+ }
34
+ async watch(address, types, assetId) {
35
+ const key = address.toLowerCase();
36
+ const currTypes = this.watchedTypes.get(key) ?? [];
37
+ const newTypes = [...new Set([...currTypes, ...types])];
38
+ this.watchedTypes.set(key, newTypes);
39
+ const diff = newTypes.filter(x => !currTypes.includes(x));
40
+ if (diff.length > 0) {
41
+ await this.subscriber.subscribe(address, newTypes, (data) => this.handleSubscriptionData(address, data));
42
+ }
43
+ if (assetId) {
44
+ const assetKey = `${key} ${assetId}`;
45
+ return new Promise((resolve, reject) => {
46
+ if (!this.assetIdResolvers.has(assetKey)) {
47
+ this.assetIdResolvers.set(assetKey, []);
48
+ }
49
+ this.assetIdResolvers.get(assetKey).push({ resolve, reject });
50
+ });
51
+ }
52
+ return new Promise((resolve, reject) => {
53
+ if (!this.resolvers.has(key)) {
54
+ this.resolvers.set(key, []);
55
+ }
56
+ this.resolvers.get(key).push({ resolve, reject });
57
+ });
58
+ }
59
+ /**
60
+ * Stop watching an address, cancel its poll timer, unsubscribe from the
61
+ * subscriber, and reject any pending callers.
62
+ *
63
+ * @param address - Public wallet address to unwatch
64
+ */
65
+ unwatch(address) {
66
+ const key = address.toLowerCase();
67
+ this.subscriber?.unsubscribe(address);
68
+ const resolvers = this.resolvers.get(key);
69
+ if (resolvers) {
70
+ resolvers.forEach(r => r.reject(new Error(`Stopped watching ${address}`)));
71
+ this.resolvers.delete(key);
72
+ }
73
+ this.lastState.delete(key);
74
+ this.watchedTypes.delete(key);
75
+ for (const [k, v] of this.assetIdResolvers.entries()) {
76
+ if (k.startsWith(`${key} `)) {
77
+ v.forEach((r) => r.reject(new Error(`Stopped watching ${address}`)));
78
+ this.assetIdResolvers.delete(k);
79
+ }
80
+ }
81
+ }
82
+ /** Stop all active watchers and close the underlying trigger. */
83
+ close() {
84
+ for (const address of [...this.watchedTypes.keys()]) {
85
+ this.unwatch(address);
86
+ }
87
+ this.subscriber?.close();
88
+ }
89
+ /**
90
+ * Handle raw event data from the subscriber.
91
+ *
92
+ * @param address - Public wallet address to watch
93
+ * @param data - Raw event payload from the subscriber
94
+ *
95
+ * Calls `buildActivity` to attempt constructing a partial result from the
96
+ * event payload. Fetches only the types that are missing from the partial,
97
+ * then resolves any waiting promises if a change is detected.
98
+ *
99
+ * Falls back to a full `poll()` when no `buildActivity` is configured or
100
+ * when it returns `null`.
101
+ */
102
+ async handleSubscriptionData(address, data) {
103
+ const key = address.toLowerCase();
104
+ const types = this.watchedTypes.get(key);
105
+ if (!types)
106
+ return;
107
+ try {
108
+ const lastActivity = this.lastState.get(key);
109
+ const partial = this.buildActivity
110
+ ? this.buildActivity(data, address, types, lastActivity)
111
+ : null;
112
+ let merged;
113
+ if (partial === null) {
114
+ merged = await this.fetchFn(address, types);
115
+ }
116
+ else {
117
+ const missingTypes = types.filter(t => !(t in partial));
118
+ if (missingTypes.length > 0) {
119
+ const fetched = await this.fetchFn(address, missingTypes);
120
+ merged = { ...fetched, ...partial, address, timestamp: Date.now() };
121
+ }
122
+ else {
123
+ merged = { address, timestamp: Date.now(), ...partial };
124
+ }
125
+ }
126
+ const last = this.lastState.get(key);
127
+ const value = last ? this.getChanged(last, merged) : merged;
128
+ this.lastState.set(key, merged);
129
+ // Ignore the fist snapshot and only deliver the changed fields
130
+ if (last && this.hasChanges(value)) {
131
+ const resolvers = this.resolvers.get(key);
132
+ if (resolvers?.length) {
133
+ resolvers.forEach(r => r.resolve(value));
134
+ this.resolvers.set(key, []);
135
+ }
136
+ this.dispatchAssetResolvers(key, value);
137
+ }
138
+ }
139
+ catch {
140
+ }
141
+ }
142
+ dispatchAssetResolvers(addrKey, activity) {
143
+ for (const [assetKey, resolvers] of this.assetIdResolvers) {
144
+ if (!assetKey.startsWith(`${addrKey} `) || !resolvers.length)
145
+ continue;
146
+ const assetId = assetKey.slice(addrKey.length + 1);
147
+ const matching = (activity.trades ?? []).filter(t => t.outcomeId === assetId);
148
+ if (matching.length > 0) {
149
+ resolvers.forEach(r => r.resolve(matching));
150
+ this.assetIdResolvers.set(assetKey, []);
151
+ }
152
+ }
153
+ }
154
+ getChanged(prev, curr) {
155
+ const changed = { address: curr.address, timestamp: curr.timestamp };
156
+ if (curr.trades !== undefined) {
157
+ const prevIds = new Set(prev.trades?.map(t => t.id) ?? []);
158
+ changed.trades = curr.trades.filter(t => !prevIds.has(t.id));
159
+ }
160
+ if (curr.positions !== undefined) {
161
+ const prevSizeByOutcome = new Map(prev.positions?.map(p => [p.outcomeId, p.size]) ?? []);
162
+ changed.positions = curr.positions.filter(p => !prevSizeByOutcome.has(p.outcomeId) || prevSizeByOutcome.get(p.outcomeId) !== p.size);
163
+ }
164
+ if (curr.balances !== undefined) {
165
+ const prevTotalByCurrency = new Map(prev.balances?.map(b => [b.currency, b.total]) ?? []);
166
+ changed.balances = curr.balances.filter(b => !prevTotalByCurrency.has(b.currency) || prevTotalByCurrency.get(b.currency) !== b.total);
167
+ }
168
+ return changed;
169
+ }
170
+ hasChanges(value) {
171
+ if (value.trades !== undefined && value.trades.length > 0)
172
+ return true;
173
+ if (value.positions !== undefined && value.positions.length > 0)
174
+ return true;
175
+ return value.balances !== undefined && value.balances.length > 0;
176
+ }
177
+ }
178
+ exports.AddressWatcher = AddressWatcher;
package/dist/types.d.ts CHANGED
@@ -68,10 +68,15 @@ export interface Trade {
68
68
  price: number;
69
69
  amount: number;
70
70
  side: 'buy' | 'sell' | 'unknown';
71
+ outcomeId?: string;
71
72
  }
72
73
  export interface UserTrade extends Trade {
73
74
  orderId?: string;
74
75
  }
76
+ export interface QueuedPromise<T> {
77
+ resolve: (value: T | PromiseLike<T>) => void;
78
+ reject: (reason?: any) => void;
79
+ }
75
80
  export interface Order {
76
81
  id: string;
77
82
  marketId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmxt-core",
3
- "version": "2.19.5",
3
+ "version": "2.20.0",
4
4
  "description": "pmxt is a unified prediction market data API. The ccxt for prediction markets.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -29,8 +29,8 @@
29
29
  "test": "jest -c jest.config.js",
30
30
  "server": "tsx watch src/server/index.ts",
31
31
  "server:prod": "node dist/server/index.js",
32
- "generate:sdk:python": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g python -o ../sdks/python/generated --package-name pmxt_internal --additional-properties=projectName=pmxt-internal,packageVersion=2.19.5,library=urllib3",
33
- "generate:sdk:typescript": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g typescript-fetch -o ../sdks/typescript/generated --additional-properties=npmName=pmxtjs,npmVersion=2.19.5,supportsES6=true,typescriptThreePlus=true && node ../sdks/typescript/scripts/fix-generated.js",
32
+ "generate:sdk:python": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g python -o ../sdks/python/generated --package-name pmxt_internal --additional-properties=projectName=pmxt-internal,packageVersion=2.20.0,library=urllib3",
33
+ "generate:sdk:typescript": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g typescript-fetch -o ../sdks/typescript/generated --additional-properties=npmName=pmxtjs,npmVersion=2.20.0,supportsES6=true,typescriptThreePlus=true && node ../sdks/typescript/scripts/fix-generated.js",
34
34
  "fetch:openapi": "node scripts/fetch-openapi-specs.js",
35
35
  "extract:jsdoc": "node ../scripts/extract-jsdoc.js",
36
36
  "generate:docs": "npm run extract:jsdoc && node ../scripts/generate-api-docs.js",