pmxt-core 2.46.14 → 2.47.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 (40) hide show
  1. package/dist/exchanges/baozi/normalizer.js +2 -0
  2. package/dist/exchanges/baozi/utils.js +28 -0
  3. package/dist/exchanges/gemini-titan/normalizer.js +31 -0
  4. package/dist/exchanges/hyperliquid/normalizer.js +20 -0
  5. package/dist/exchanges/kalshi/api.d.ts +1 -1
  6. package/dist/exchanges/kalshi/api.js +1 -1
  7. package/dist/exchanges/kalshi/normalizer.js +21 -0
  8. package/dist/exchanges/limitless/api.d.ts +1 -1
  9. package/dist/exchanges/limitless/api.js +1 -1
  10. package/dist/exchanges/limitless/normalizer.js +10 -0
  11. package/dist/exchanges/limitless/utils.js +17 -0
  12. package/dist/exchanges/metaculus/fetchEvents.js +2 -0
  13. package/dist/exchanges/metaculus/utils.d.ts +2 -1
  14. package/dist/exchanges/metaculus/utils.js +32 -3
  15. package/dist/exchanges/myriad/api.d.ts +1 -1
  16. package/dist/exchanges/myriad/api.js +1 -1
  17. package/dist/exchanges/myriad/normalizer.js +14 -0
  18. package/dist/exchanges/myriad/utils.js +14 -0
  19. package/dist/exchanges/opinion/api.d.ts +1 -1
  20. package/dist/exchanges/opinion/api.js +1 -1
  21. package/dist/exchanges/opinion/normalizer.js +26 -0
  22. package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
  23. package/dist/exchanges/polymarket/api-clob.js +1 -1
  24. package/dist/exchanges/polymarket/api-data.d.ts +1 -1
  25. package/dist/exchanges/polymarket/api-data.js +1 -1
  26. package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
  27. package/dist/exchanges/polymarket/api-gamma.js +1 -1
  28. package/dist/exchanges/polymarket/normalizer.js +15 -0
  29. package/dist/exchanges/polymarket/utils.js +28 -0
  30. package/dist/exchanges/polymarket_us/normalizer.js +15 -0
  31. package/dist/exchanges/probable/api.d.ts +1 -1
  32. package/dist/exchanges/probable/api.js +1 -1
  33. package/dist/exchanges/probable/utils.js +17 -0
  34. package/dist/exchanges/smarkets/normalizer.js +20 -0
  35. package/dist/exchanges/suibets/normalizer.js +20 -0
  36. package/dist/server/openapi.yaml +14 -0
  37. package/dist/types.d.ts +4 -0
  38. package/dist/utils/metadata.d.ts +14 -0
  39. package/dist/utils/metadata.js +33 -0
  40. package/package.json +3 -3
@@ -35,6 +35,7 @@ class BaoziNormalizer {
35
35
  image: market.image,
36
36
  category: market.category,
37
37
  tags: market.tags,
38
+ sourceMetadata: market.sourceMetadata,
38
39
  };
39
40
  }
40
41
  normalizeMarkets(rawMarkets, params) {
@@ -60,6 +61,7 @@ class BaoziNormalizer {
60
61
  image: m.image,
61
62
  category: m.category,
62
63
  tags: m.tags,
64
+ sourceMetadata: m.sourceMetadata,
63
65
  }));
64
66
  }
65
67
  normalizeOrderBook(raw, outcomeId) {
@@ -20,6 +20,7 @@ const web3_js_1 = require("@solana/web3.js");
20
20
  const bs58_1 = __importDefault(require("bs58"));
21
21
  const crypto_1 = require("crypto");
22
22
  const market_utils_1 = require("../../utils/market-utils");
23
+ const metadata_1 = require("../../utils/metadata");
23
24
  const price_1 = require("./price");
24
25
  // ---------------------------------------------------------------------------
25
26
  // Constants
@@ -285,6 +286,31 @@ function parseRacePosition(data) {
285
286
  return { user, marketId, bets, totalBet, claimed };
286
287
  }
287
288
  // ---------------------------------------------------------------------------
289
+ // Promoted key sets — fields already represented by first-class unified columns.
290
+ // These are excluded from sourceMetadata to avoid duplication.
291
+ // ---------------------------------------------------------------------------
292
+ // BaoziMarket fields promoted to unified columns:
293
+ // question -> title
294
+ // resolutionTime -> resolutionDate
295
+ // yesPool / noPool -> volume + liquidity
296
+ // status -> status
297
+ // pubkey is promoted to marketId; outcomeLabels / outcomePools -> outcomes
298
+ const BAOZI_BOOLEAN_PROMOTED_KEYS = [
299
+ 'question',
300
+ 'resolutionTime',
301
+ 'yesPool',
302
+ 'noPool',
303
+ 'status',
304
+ ];
305
+ const BAOZI_RACE_PROMOTED_KEYS = [
306
+ 'question',
307
+ 'resolutionTime',
308
+ 'totalPool',
309
+ 'status',
310
+ 'outcomeLabels',
311
+ 'outcomePools',
312
+ ];
313
+ // ---------------------------------------------------------------------------
288
314
  // Mapping to Unified Types
289
315
  // ---------------------------------------------------------------------------
290
316
  function mapBooleanToUnified(market, pubkey) {
@@ -326,6 +352,7 @@ function mapBooleanToUnified(market, pubkey) {
326
352
  url: `https://baozi.bet/market/${pubkey}`,
327
353
  category: undefined,
328
354
  tags: [`tier:${layerName(market.layer)}`, 'solana', 'pari-mutuel'],
355
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(market, BAOZI_BOOLEAN_PROMOTED_KEYS),
329
356
  };
330
357
  (0, market_utils_1.addBinaryOutcomes)(um);
331
358
  return um;
@@ -361,6 +388,7 @@ function mapRaceToUnified(market, pubkey) {
361
388
  url: `https://baozi.bet/market/${pubkey}`,
362
389
  category: undefined,
363
390
  tags: [`tier:${layerName(market.layer)}`, 'solana', 'pari-mutuel', 'race'],
391
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(market, BAOZI_RACE_PROMOTED_KEYS),
364
392
  };
365
393
  // For 2-outcome races, add binary convenience getters
366
394
  (0, market_utils_1.addBinaryOutcomes)(um);
@@ -2,8 +2,33 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GeminiNormalizer = void 0;
4
4
  const market_utils_1 = require("../../utils/market-utils");
5
+ const metadata_1 = require("../../utils/metadata");
5
6
  const utils_1 = require("./utils");
6
7
  const config_1 = require("./config");
8
+ // Raw GeminiRawEvent fields already promoted to first-class unified columns.
9
+ // Omitted from sourceMetadata so we capture only vendor data the unified shape
10
+ // would otherwise drop.
11
+ const GEMINI_PROMOTED_EVENT_KEYS = [
12
+ 'ticker', // -> id
13
+ 'title',
14
+ 'description',
15
+ 'slug',
16
+ 'contracts', // -> markets (nested child array, always omitted)
17
+ 'volume24h',
18
+ 'imageUrl', // -> image
19
+ 'category',
20
+ 'tags',
21
+ ];
22
+ // Raw GeminiRawContract fields already promoted to first-class unified columns.
23
+ const GEMINI_PROMOTED_CONTRACT_KEYS = [
24
+ 'instrumentSymbol', // -> marketId, slug
25
+ 'label', // -> title
26
+ 'description',
27
+ 'ticker', // parent event ticker feeds eventId
28
+ 'status', // -> status
29
+ 'prices', // -> outcomes prices
30
+ 'expiryDate', // -> resolutionDate
31
+ ];
7
32
  // ----------------------------------------------------------------------------
8
33
  // Helpers
9
34
  // ----------------------------------------------------------------------------
@@ -95,6 +120,7 @@ class GeminiNormalizer {
95
120
  category: raw.category,
96
121
  tags: raw.tags ?? [],
97
122
  image: raw.imageUrl,
123
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, GEMINI_PROMOTED_EVENT_KEYS),
98
124
  };
99
125
  }
100
126
  normalizeEventWithMarkets(raw) {
@@ -158,6 +184,10 @@ class GeminiNormalizer {
158
184
  if (event.category && !tags.includes(event.category)) {
159
185
  tags.push(event.category);
160
186
  }
187
+ const seriesExtra = {};
188
+ if (event.series != null) {
189
+ seriesExtra['series'] = event.series;
190
+ }
161
191
  const um = {
162
192
  marketId,
163
193
  eventId: event.ticker,
@@ -173,6 +203,7 @@ class GeminiNormalizer {
173
203
  tags,
174
204
  tickSize: config_1.TICK_SIZE,
175
205
  status: mapEventStatus(contract.status),
206
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(contract, GEMINI_PROMOTED_CONTRACT_KEYS, Object.keys(seriesExtra).length > 0 ? seriesExtra : undefined),
176
207
  };
177
208
  (0, market_utils_1.addBinaryOutcomes)(um);
178
209
  return um;
@@ -2,8 +2,26 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HyperliquidNormalizer = void 0;
4
4
  const market_utils_1 = require("../../utils/market-utils");
5
+ const metadata_1 = require("../../utils/metadata");
5
6
  const utils_1 = require("./utils");
6
7
  const config_1 = require("./config");
8
+ // Raw Hyperliquid outcome fields already promoted to first-class Unified columns —
9
+ // excluded from sourceMetadata so we capture only what the unified shape would drop.
10
+ const HL_PROMOTED_MARKET_KEYS = [
11
+ 'outcome', // -> marketId
12
+ 'name', // -> title
13
+ 'description', // -> description
14
+ 'sideSpecs', // -> outcomes (labels)
15
+ ];
16
+ // Raw Hyperliquid question fields already promoted to first-class Unified columns.
17
+ // namedOutcomes is omitted as well — it is the child-markets array and must not
18
+ // be duplicated in metadata.
19
+ const HL_PROMOTED_EVENT_KEYS = [
20
+ 'question', // -> id / eventId
21
+ 'name', // -> title
22
+ 'description', // -> description
23
+ 'namedOutcomes', // -> markets (child-markets array)
24
+ ];
7
25
  function parseDescription(description) {
8
26
  // Hyperliquid outcome descriptions use key:value pairs separated by |
9
27
  // e.g. "class:priceBinary|underlying:BTC|expiry:20260509-0600|targetPrice:79583|period:1d"
@@ -165,6 +183,7 @@ class HyperliquidNormalizer {
165
183
  tags,
166
184
  tickSize: 0.001,
167
185
  status: 'active',
186
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(outcome, HL_PROMOTED_MARKET_KEYS),
168
187
  };
169
188
  (0, market_utils_1.addBinaryOutcomes)(um);
170
189
  return um;
@@ -196,6 +215,7 @@ class HyperliquidNormalizer {
196
215
  url: buildEventUrl(raw.question),
197
216
  category: underlying ? 'Crypto' : undefined,
198
217
  tags,
218
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, HL_PROMOTED_EVENT_KEYS),
199
219
  };
200
220
  }
201
221
  normalizeEventWithMarkets(raw, outcomeMeta, mids) {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/kalshi/Kalshi.yaml
3
- * Generated at: 2026-05-26T15:22:20.006Z
3
+ * Generated at: 2026-05-30T12:19:28.168Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const kalshiApiSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.kalshiApiSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/kalshi/Kalshi.yaml
6
- * Generated at: 2026-05-26T15:22:20.006Z
6
+ * Generated at: 2026-05-30T12:19:28.168Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.kalshiApiSpec = {
@@ -3,7 +3,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.KalshiNormalizer = void 0;
4
4
  exports.sortRawEvents = sortRawEvents;
5
5
  const market_utils_1 = require("../../utils/market-utils");
6
+ const metadata_1 = require("../../utils/metadata");
6
7
  const price_1 = require("./price");
8
+ // Raw Kalshi fields already promoted to first-class Unified columns — excluded
9
+ // from sourceMetadata so we capture only what the unified shape would drop.
10
+ const KALSHI_PROMOTED_EVENT_KEYS = [
11
+ 'event_ticker', 'title', 'markets', 'category', 'image_url', 'tags',
12
+ ];
13
+ const KALSHI_PROMOTED_MARKET_KEYS = [
14
+ 'ticker', 'title', 'rules_primary', 'rules_secondary', 'expiration_time',
15
+ 'volume_24h_fp', 'volume_24h', 'volume', 'volume_fp',
16
+ 'liquidity_dollars', 'liquidity', 'open_interest_fp', 'open_interest',
17
+ 'status', 'last_price_dollars', 'previous_price_dollars',
18
+ 'yes_ask_dollars', 'yes_bid_dollars', 'last_price', 'yes_ask', 'yes_bid',
19
+ ];
7
20
  class KalshiNormalizer {
8
21
  normalizeMarket(raw) {
9
22
  // This normalizes a single-market event. For multi-market events, use normalizeMarketsFromEvent.
@@ -92,6 +105,11 @@ class KalshiNormalizer {
92
105
  category: event.category,
93
106
  tags: unifiedTags,
94
107
  status: this.cleanLabel(market.status) || undefined,
108
+ // series_ticker/series_title live on the parent event, not the raw
109
+ // market, and aren't promoted to a column — attach them here so
110
+ // markets are queryable by series. event_ticker is omitted (already
111
+ // promoted to eventId).
112
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(market, KALSHI_PROMOTED_MARKET_KEYS, { series_ticker: event.series_ticker, series_title: event.series_title }),
95
113
  };
96
114
  (0, market_utils_1.addBinaryOutcomes)(um);
97
115
  return um;
@@ -114,6 +132,9 @@ class KalshiNormalizer {
114
132
  image: raw.image_url ?? undefined,
115
133
  category: raw.category,
116
134
  tags: raw.tags || [],
135
+ // Keeps non-promoted event fields (series_ticker, series_title,
136
+ // sub_title, strike_period, ...); raw markets array is promoted.
137
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, KALSHI_PROMOTED_EVENT_KEYS),
117
138
  };
118
139
  }
119
140
  normalizeOHLCV(rawCandles, params) {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/limitless/Limitless.yaml
3
- * Generated at: 2026-05-26T15:22:20.044Z
3
+ * Generated at: 2026-05-30T12:19:28.209Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const limitlessApiSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.limitlessApiSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/limitless/Limitless.yaml
6
- * Generated at: 2026-05-26T15:22:20.044Z
6
+ * Generated at: 2026-05-30T12:19:28.209Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.limitlessApiSpec = {
@@ -2,6 +2,15 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LimitlessNormalizer = void 0;
4
4
  const utils_1 = require("./utils");
5
+ const metadata_1 = require("../../utils/metadata");
6
+ // Raw Limitless event fields already promoted to first-class Unified columns —
7
+ // excluded from sourceMetadata so we capture only what the unified shape drops.
8
+ const LIMITLESS_PROMOTED_EVENT_KEYS = [
9
+ 'slug', 'title', 'question', 'description',
10
+ 'logo',
11
+ 'categories', 'tags',
12
+ 'markets',
13
+ ];
5
14
  // Limitless uses USDC with 6 decimals
6
15
  const USDC_DECIMALS = 6;
7
16
  const USDC_SCALE = Math.pow(10, USDC_DECIMALS);
@@ -47,6 +56,7 @@ class LimitlessNormalizer {
47
56
  image: raw.logo || `https://limitless.exchange/api/og?slug=${raw.slug}`,
48
57
  category: raw.categories?.[0],
49
58
  tags: raw.tags || [],
59
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, LIMITLESS_PROMOTED_EVENT_KEYS),
50
60
  };
51
61
  }
52
62
  normalizeOHLCV(rawPrices, params) {
@@ -5,7 +5,23 @@ exports.mapMarketToUnified = mapMarketToUnified;
5
5
  exports.mapIntervalToFidelity = mapIntervalToFidelity;
6
6
  exports.paginateLimitlessMarkets = paginateLimitlessMarkets;
7
7
  const market_utils_1 = require("../../utils/market-utils");
8
+ const metadata_1 = require("../../utils/metadata");
8
9
  exports.DEFAULT_LIMITLESS_API_URL = 'https://api.limitless.exchange';
10
+ // Raw Limitless market fields already promoted to first-class Unified columns —
11
+ // excluded from sourceMetadata so we capture only what the unified shape drops.
12
+ // Also excludes __pmxt* internal injection keys (not raw vendor data).
13
+ const LIMITLESS_PROMOTED_MARKET_KEYS = [
14
+ 'slug', 'title', 'question', 'description',
15
+ 'tokens', 'prices',
16
+ 'expirationTimestamp',
17
+ 'volumeFormatted', 'volume',
18
+ 'logo',
19
+ 'categories', 'tags',
20
+ 'expired', 'status',
21
+ 'markets',
22
+ '__pmxtEventId', '__pmxtEventTitle', '__pmxtEventDescription',
23
+ '__pmxtCategories', '__pmxtTags',
24
+ ];
9
25
  function mapMarketToUnified(market, context = {}) {
10
26
  if (!market)
11
27
  return null;
@@ -76,6 +92,7 @@ function mapMarketToUnified(market, context = {}) {
76
92
  category: market.categories?.[0] || resolvedContext.categories?.[0],
77
93
  tags: market.tags || resolvedContext.tags || [],
78
94
  status,
95
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(market, LIMITLESS_PROMOTED_MARKET_KEYS),
79
96
  };
80
97
  (0, market_utils_1.addBinaryOutcomes)(um);
81
98
  return um;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.fetchEvents = fetchEvents;
4
4
  const utils_1 = require("./utils");
5
5
  const errors_1 = require("./errors");
6
+ const metadata_1 = require("../../utils/metadata");
6
7
  const BATCH_SIZE = 100;
7
8
  const MAX_PAGES = 200;
8
9
  /**
@@ -72,6 +73,7 @@ function postToEvent(post) {
72
73
  : post.projects.category[0]?.name
73
74
  : undefined,
74
75
  tags: markets[0]?.tags ?? [],
76
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(post, utils_1.METACULUS_PROMOTED_EVENT_KEYS),
75
77
  };
76
78
  }
77
79
  /**
@@ -1,4 +1,5 @@
1
1
  import { UnifiedMarket } from "../../types";
2
+ export declare const METACULUS_PROMOTED_EVENT_KEYS: readonly ["id", "slug", "url_title", "title", "question", "group_of_questions", "projects", "status"];
2
3
  /**
3
4
  * Base URL passed to parseOpenApiSpec to override the spec's servers[0].url.
4
5
  * The generated api.ts already has "https://www.metaculus.com/api" as its server URL,
@@ -23,7 +24,7 @@ export declare function mapStatus(status: string): "active" | "closed";
23
24
  * @param eventId Optional parent event ID (tournament slug) to override
24
25
  * the value derived from post.projects.tournament.
25
26
  */
26
- export declare function mapMarketToUnified(post: any, eventId?: string): UnifiedMarket | null;
27
+ export declare function mapMarketToUnified(post: any, eventId?: string, groupPostId?: number): UnifiedMarket | null;
27
28
  /**
28
29
  * Convert a raw Metaculus post into one or more `UnifiedMarket` objects.
29
30
  *
@@ -1,10 +1,38 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_BASE_URL = void 0;
3
+ exports.DEFAULT_BASE_URL = exports.METACULUS_PROMOTED_EVENT_KEYS = void 0;
4
4
  exports.mapStatus = mapStatus;
5
5
  exports.mapMarketToUnified = mapMarketToUnified;
6
6
  exports.expandPost = expandPost;
7
7
  const market_utils_1 = require("../../utils/market-utils");
8
+ const metadata_1 = require("../../utils/metadata");
9
+ // Raw Metaculus Post fields already promoted to first-class UnifiedMarket columns
10
+ // — excluded from sourceMetadata so we capture only what the unified shape drops.
11
+ const METACULUS_PROMOTED_MARKET_KEYS = [
12
+ // identity / slug
13
+ 'id', 'slug', 'url_title',
14
+ // title
15
+ 'title',
16
+ // description lives inside question / group_of_questions — those are excluded below
17
+ // resolution timing -> resolutionDate
18
+ 'scheduled_resolve_time', 'scheduled_close_time', 'actual_close_time',
19
+ // forecaster count -> liquidity / openInterest
20
+ 'nr_forecasters',
21
+ // child objects whose fields are promoted individually
22
+ 'question', 'group_of_questions',
23
+ // project sub-tree fields that map to image / category / tags / eventId
24
+ 'projects',
25
+ // status -> mapStatus
26
+ 'status',
27
+ ];
28
+ // Raw Metaculus Post fields already promoted to first-class UnifiedEvent columns.
29
+ exports.METACULUS_PROMOTED_EVENT_KEYS = [
30
+ 'id', 'slug', 'url_title',
31
+ 'title',
32
+ 'question', 'group_of_questions',
33
+ 'projects',
34
+ 'status',
35
+ ];
8
36
  /**
9
37
  * Base URL passed to parseOpenApiSpec to override the spec's servers[0].url.
10
38
  * The generated api.ts already has "https://www.metaculus.com/api" as its server URL,
@@ -185,7 +213,7 @@ function buildOutcomes(question, postId, medianProb) {
185
213
  * @param eventId Optional parent event ID (tournament slug) to override
186
214
  * the value derived from post.projects.tournament.
187
215
  */
188
- function mapMarketToUnified(post, eventId) {
216
+ function mapMarketToUnified(post, eventId, groupPostId) {
189
217
  if (!post || !post.id)
190
218
  return null;
191
219
  // Group-of-questions posts have no top-level question -- they must be
@@ -240,6 +268,7 @@ function mapMarketToUnified(post, eventId) {
240
268
  image: post.projects?.default_project?.header_image ?? undefined,
241
269
  category,
242
270
  tags,
271
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(post, METACULUS_PROMOTED_MARKET_KEYS, groupPostId !== undefined ? { group_post_id: groupPostId } : undefined),
243
272
  };
244
273
  (0, market_utils_1.addBinaryOutcomes)(um);
245
274
  return um;
@@ -281,7 +310,7 @@ function mapGroupPostToMarkets(post, eventId) {
281
310
  actual_close_time: subQuestion.actual_close_time ?? post.actual_close_time,
282
311
  status: post.status,
283
312
  };
284
- const market = mapMarketToUnified(syntheticPost, groupEventId);
313
+ const market = mapMarketToUnified(syntheticPost, groupEventId, Number(parentPostId));
285
314
  if (market) {
286
315
  // Tag each outcome with the parent group post ID for traceability
287
316
  for (const outcome of market.outcomes) {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/myriad/myriad.yaml
3
- * Generated at: 2026-05-26T15:22:20.057Z
3
+ * Generated at: 2026-05-30T12:19:28.221Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const myriadApiSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.myriadApiSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/myriad/myriad.yaml
6
- * Generated at: 2026-05-26T15:22:20.057Z
6
+ * Generated at: 2026-05-30T12:19:28.221Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.myriadApiSpec = {
@@ -2,8 +2,19 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MyriadNormalizer = void 0;
4
4
  const market_utils_1 = require("../../utils/market-utils");
5
+ const metadata_1 = require("../../utils/metadata");
5
6
  const price_1 = require("./price");
6
7
  const utils_1 = require("./utils");
8
+ // Raw Myriad fields already promoted to first-class Unified columns — excluded
9
+ // from sourceMetadata so we capture only what the unified shape would drop.
10
+ const MYRIAD_PROMOTED_MARKET_KEYS = [
11
+ 'id', 'networkId', 'title', 'description', 'slug', 'imageUrl',
12
+ 'expiresAt', 'volume24h', 'volume', 'liquidity', 'eventId',
13
+ 'topics', 'outcomes', 'state',
14
+ ];
15
+ const MYRIAD_PROMOTED_EVENT_KEYS = [
16
+ 'id', 'title', 'markets',
17
+ ];
7
18
  function selectTimeframe(interval) {
8
19
  switch (interval) {
9
20
  case '1m':
@@ -45,6 +56,7 @@ class MyriadNormalizer {
45
56
  image: raw.imageUrl,
46
57
  tags: raw.topics || [],
47
58
  status,
59
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, MYRIAD_PROMOTED_MARKET_KEYS),
48
60
  };
49
61
  (0, market_utils_1.addBinaryOutcomes)(um);
50
62
  return um;
@@ -97,6 +109,8 @@ class MyriadNormalizer {
97
109
  ? markets.reduce((sum, m) => sum + (m.volume ?? 0), 0)
98
110
  : undefined,
99
111
  url: `https://myriad.markets`,
112
+ // Keeps non-promoted question fields; raw markets array is promoted.
113
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, MYRIAD_PROMOTED_EVENT_KEYS),
100
114
  };
101
115
  }
102
116
  normalizeOHLCV(raw, params, outcomeId) {
@@ -6,6 +6,17 @@ exports.mapStatusToMyriad = mapStatusToMyriad;
6
6
  exports.mapMarketToUnified = mapMarketToUnified;
7
7
  exports.mapQuestionToEvent = mapQuestionToEvent;
8
8
  const market_utils_1 = require("../../utils/market-utils");
9
+ const metadata_1 = require("../../utils/metadata");
10
+ // Raw Myriad fields already promoted to first-class Unified columns — excluded
11
+ // from sourceMetadata so we capture only what the unified shape would drop.
12
+ const MYRIAD_PROMOTED_MARKET_KEYS = [
13
+ 'id', 'networkId', 'title', 'description', 'slug', 'imageUrl',
14
+ 'expiresAt', 'volume24h', 'volume', 'liquidity', 'eventId',
15
+ 'topics', 'outcomes', 'state',
16
+ ];
17
+ const MYRIAD_PROMOTED_EVENT_KEYS = [
18
+ 'id', 'title', 'markets',
19
+ ];
9
20
  exports.DEFAULT_BASE_URL = 'https://api-v2.myriadprotocol.com';
10
21
  // Mainnet network IDs
11
22
  exports.NETWORKS = {
@@ -76,6 +87,7 @@ function mapMarketToUnified(market) {
76
87
  url: `https://myriad.markets/markets/${market.slug || market.id}`,
77
88
  image: market.imageUrl,
78
89
  tags: market.topics || [],
90
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(market, MYRIAD_PROMOTED_MARKET_KEYS),
79
91
  };
80
92
  (0, market_utils_1.addBinaryOutcomes)(um);
81
93
  return um;
@@ -98,6 +110,8 @@ function mapQuestionToEvent(question) {
98
110
  volume24h: markets.reduce((sum, m) => sum + m.volume24h, 0),
99
111
  volume: markets.some(m => m.volume !== undefined) ? markets.reduce((sum, m) => sum + (m.volume ?? 0), 0) : undefined,
100
112
  url: `https://myriad.markets`,
113
+ // Keeps non-promoted question fields; raw markets array is promoted.
114
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(question, MYRIAD_PROMOTED_EVENT_KEYS),
101
115
  };
102
116
  return unifiedEvent;
103
117
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/opinion/opinion-openapi.yaml
3
- * Generated at: 2026-05-26T15:22:20.063Z
3
+ * Generated at: 2026-05-30T12:19:28.226Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const opinionApiSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.opinionApiSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/opinion/opinion-openapi.yaml
6
- * Generated at: 2026-05-26T15:22:20.063Z
6
+ * Generated at: 2026-05-30T12:19:28.226Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.opinionApiSpec = {
@@ -2,8 +2,24 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OpinionNormalizer = void 0;
4
4
  const market_utils_1 = require("../../utils/market-utils");
5
+ const metadata_1 = require("../../utils/metadata");
5
6
  const utils_1 = require("./utils");
6
7
  // ---------------------------------------------------------------------------
8
+ // Raw Opinion fields already promoted to first-class Unified columns — omit
9
+ // from sourceMetadata so we capture only what the unified shape would drop.
10
+ // ---------------------------------------------------------------------------
11
+ // Fields promoted on a market or child-market payload.
12
+ const OPINION_PROMOTED_MARKET_KEYS = [
13
+ 'marketId', 'marketTitle', 'rules', 'slug',
14
+ 'volume', 'volume24h', 'cutoffAt',
15
+ 'yesTokenId', 'noTokenId',
16
+ ];
17
+ // Fields promoted on the parent (event-level) payload.
18
+ const OPINION_PROMOTED_EVENT_KEYS = [
19
+ 'marketId', 'marketTitle', 'rules', 'slug',
20
+ 'volume', 'volume24h', 'childMarkets',
21
+ ];
22
+ // ---------------------------------------------------------------------------
7
23
  // Opinion Trade Normalizer
8
24
  // ---------------------------------------------------------------------------
9
25
  class OpinionNormalizer {
@@ -63,6 +79,9 @@ class OpinionNormalizer {
63
79
  volume24h,
64
80
  volume: totalVolume,
65
81
  url: `https://www.opinion.trade/market/${raw.slug || raw.marketId}`,
82
+ // collection carries series/recurring identifier (title, symbol,
83
+ // frequency, current period, next periods) — not a unified column.
84
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, OPINION_PROMOTED_EVENT_KEYS, raw.collection ? { collection: raw.collection } : undefined),
66
85
  };
67
86
  }
68
87
  // -- OHLCV ----------------------------------------------------------------
@@ -247,6 +266,7 @@ class OpinionNormalizer {
247
266
  volume: (0, utils_1.parseNumStr)(raw.volume),
248
267
  liquidity: 0,
249
268
  url: `https://www.opinion.trade/market/${raw.slug || raw.marketId}`,
269
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, OPINION_PROMOTED_MARKET_KEYS),
250
270
  };
251
271
  (0, market_utils_1.addBinaryOutcomes)(market);
252
272
  return market;
@@ -289,6 +309,12 @@ class OpinionNormalizer {
289
309
  volume: (0, utils_1.parseNumStr)(child.volume),
290
310
  liquidity: 0,
291
311
  url: `https://www.opinion.trade/market/${child.slug || child.marketId}`,
312
+ // parentMarketId links the child back to its categorical parent;
313
+ // collection carries the series/recurring context from the parent.
314
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(child, OPINION_PROMOTED_MARKET_KEYS, {
315
+ parentMarketId: parent.marketId,
316
+ ...(parent.collection ? { collection: parent.collection } : {}),
317
+ }),
292
318
  };
293
319
  (0, market_utils_1.addBinaryOutcomes)(market);
294
320
  return market;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketClobAPI.yaml
3
- * Generated at: 2026-05-26T15:22:20.013Z
3
+ * Generated at: 2026-05-30T12:19:28.174Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const polymarketClobSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.polymarketClobSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketClobAPI.yaml
6
- * Generated at: 2026-05-26T15:22:20.013Z
6
+ * Generated at: 2026-05-30T12:19:28.174Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.polymarketClobSpec = {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/Polymarket_Data_API.yaml
3
- * Generated at: 2026-05-26T15:22:20.026Z
3
+ * Generated at: 2026-05-30T12:19:28.190Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const polymarketDataSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.polymarketDataSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/Polymarket_Data_API.yaml
6
- * Generated at: 2026-05-26T15:22:20.026Z
6
+ * Generated at: 2026-05-30T12:19:28.190Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.polymarketDataSpec = {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketGammaAPI.yaml
3
- * Generated at: 2026-05-26T15:22:20.024Z
3
+ * Generated at: 2026-05-30T12:19:28.188Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const polymarketGammaSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.polymarketGammaSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketGammaAPI.yaml
6
- * Generated at: 2026-05-26T15:22:20.024Z
6
+ * Generated at: 2026-05-30T12:19:28.188Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.polymarketGammaSpec = {
@@ -1,7 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PolymarketNormalizer = void 0;
4
+ const metadata_1 = require("../../utils/metadata");
4
5
  const utils_1 = require("./utils");
6
+ // Raw Polymarket Gamma event fields already promoted to first-class Unified
7
+ // columns — excluded from sourceMetadata so we capture only what the unified
8
+ // shape would otherwise drop.
9
+ const POLYMARKET_PROMOTED_EVENT_KEYS = [
10
+ 'id', 'slug', 'title', 'description', 'image', 'category', 'tags',
11
+ // 'markets' is the child-markets array — promoted to UnifiedEvent.markets
12
+ 'markets',
13
+ ];
5
14
  class PolymarketNormalizer {
6
15
  normalizeMarket(raw) {
7
16
  if (!raw)
@@ -40,6 +49,12 @@ class PolymarketNormalizer {
40
49
  image: raw.image || `https://polymarket.com/api/og?slug=${raw.slug}`,
41
50
  category: raw.category || raw.tags?.[0]?.label,
42
51
  tags: raw.tags?.map((t) => t.label) || [],
52
+ // Keeps non-promoted Gamma event fields (e.g. active, closed, and
53
+ // any other vendor fields not surfaced as first-class columns).
54
+ // NOTE: Polymarket "series" data lives on a separate Gamma /series
55
+ // endpoint — it is NOT present in the event payload and therefore
56
+ // requires a separate /series fetch+join to populate here.
57
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, POLYMARKET_PROMOTED_EVENT_KEYS),
43
58
  };
44
59
  }
45
60
  normalizeOHLCV(raw, params) {
@@ -6,7 +6,28 @@ exports.mapIntervalToFidelity = mapIntervalToFidelity;
6
6
  exports.paginateParallel = paginateParallel;
7
7
  exports.paginateSearchParallel = paginateSearchParallel;
8
8
  const market_utils_1 = require("../../utils/market-utils");
9
+ const metadata_1 = require("../../utils/metadata");
9
10
  const logger_1 = require("../../utils/logger");
11
+ // Raw Polymarket Gamma market fields already promoted to first-class Unified
12
+ // columns — excluded from sourceMetadata so we capture only what the unified
13
+ // shape would otherwise drop.
14
+ const POLYMARKET_PROMOTED_MARKET_KEYS = [
15
+ 'id', 'question', 'description',
16
+ // outcomes array and prices are promoted to UnifiedMarket.outcomes
17
+ 'outcomes', 'outcomePrices', 'clobTokenIds',
18
+ // resolution date fields
19
+ 'endDate', 'end_date_iso', 'endDateIso',
20
+ // volume, liquidity, openInterest
21
+ 'volume24hr', 'volume_24h', 'volume', 'liquidity', 'rewards',
22
+ 'openInterest', 'open_interest',
23
+ // image, slug, contractAddress, tickSize, status fields
24
+ 'image', 'slug',
25
+ 'orderPriceMinTickSize',
26
+ 'conditionId',
27
+ 'active', 'closed', 'archived',
28
+ // parent event back-reference — eventId already promoted to UnifiedMarket.eventId
29
+ 'events',
30
+ ];
10
31
  exports.GAMMA_API_URL = process.env.POLYMARKET_GAMMA_URL || 'https://gamma-api.polymarket.com/events';
11
32
  exports.GAMMA_SEARCH_URL = process.env.POLYMARKET_GAMMA_SEARCH_URL || 'https://gamma-api.polymarket.com/public-search';
12
33
  exports.CLOB_API_URL = process.env.POLYMARKET_CLOB_URL || 'https://clob.polymarket.com';
@@ -109,6 +130,13 @@ function mapMarketToUnified(event, market, options = {}) {
109
130
  tickSize: market.orderPriceMinTickSize != null ? Number(market.orderPriceMinTickSize) : undefined,
110
131
  status,
111
132
  contractAddress: typeof market.conditionId === 'string' && market.conditionId.length > 0 ? market.conditionId : undefined,
133
+ // Keeps non-promoted Gamma market fields (e.g. groupItemTitle,
134
+ // oneDayPriceChange, and any other vendor fields not surfaced as
135
+ // first-class columns).
136
+ // NOTE: Polymarket "series" data lives on a separate Gamma /series
137
+ // endpoint — it is NOT present in the market payload and requires a
138
+ // separate /series fetch+join to populate here.
139
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(market, POLYMARKET_PROMOTED_MARKET_KEYS),
112
140
  };
113
141
  (0, market_utils_1.addBinaryOutcomes)(um);
114
142
  return um;
@@ -2,7 +2,20 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PolymarketUSNormalizer = void 0;
4
4
  const market_utils_1 = require("../../utils/market-utils");
5
+ const metadata_1 = require("../../utils/metadata");
5
6
  const price_1 = require("./price");
7
+ // Raw Polymarket US fields already promoted to first-class Unified columns —
8
+ // excluded from sourceMetadata so we capture only what the unified shape drops.
9
+ const POLYMARKET_US_PROMOTED_EVENT_KEYS = [
10
+ 'slug', 'title', 'description', 'category', 'tags', 'volume', 'markets',
11
+ ];
12
+ // marketSides and outcomePrices feed the outcomes array and are therefore
13
+ // treated as promoted. orderPriceMinTickSize maps to tickSize.
14
+ const POLYMARKET_US_PROMOTED_MARKET_KEYS = [
15
+ 'slug', 'question', 'title', 'description', 'category', 'tags',
16
+ 'endDate', 'marketSides', 'outcomePrices', 'orderPriceMinTickSize',
17
+ 'eventSlug', 'volume', 'liquidity',
18
+ ];
6
19
  // ----------------------------------------------------------------------------
7
20
  // Helpers
8
21
  // ----------------------------------------------------------------------------
@@ -226,6 +239,7 @@ class PolymarketUSNormalizer {
226
239
  url: buildEventUrl(real.slug),
227
240
  category,
228
241
  tags,
242
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(real, POLYMARKET_US_PROMOTED_EVENT_KEYS),
229
243
  };
230
244
  }
231
245
  /**
@@ -259,6 +273,7 @@ class PolymarketUSNormalizer {
259
273
  tickSize: typeof market.orderPriceMinTickSize === 'number'
260
274
  ? market.orderPriceMinTickSize
261
275
  : undefined,
276
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(market, POLYMARKET_US_PROMOTED_MARKET_KEYS),
262
277
  };
263
278
  (0, market_utils_1.addBinaryOutcomes)(um);
264
279
  return um;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/probable/probable.yaml
3
- * Generated at: 2026-05-26T15:22:20.050Z
3
+ * Generated at: 2026-05-30T12:19:28.214Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const probableApiSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.probableApiSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/probable/probable.yaml
6
- * Generated at: 2026-05-26T15:22:20.050Z
6
+ * Generated at: 2026-05-30T12:19:28.214Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.probableApiSpec = {
@@ -5,6 +5,16 @@ exports.mapMarketToUnified = mapMarketToUnified;
5
5
  exports.mapEventToUnified = mapEventToUnified;
6
6
  exports.enrichMarketsWithPrices = enrichMarketsWithPrices;
7
7
  const market_utils_1 = require("../../utils/market-utils");
8
+ const metadata_1 = require("../../utils/metadata");
9
+ // Raw Probable fields already promoted to first-class Unified columns — omitted
10
+ // from sourceMetadata so we capture only what the unified shape would drop.
11
+ const PROBABLE_PROMOTED_EVENT_KEYS = [
12
+ 'id', 'title', 'description', 'slug', 'icon', 'image', 'category', 'tags', 'markets',
13
+ ];
14
+ const PROBABLE_PROMOTED_MARKET_KEYS = [
15
+ 'id', 'question', 'title', 'description', 'slug', 'endDate',
16
+ 'volume24hr', 'volume', 'liquidity', 'icon', 'category', 'tags', 'tokens', 'event_id',
17
+ ];
8
18
  exports.DEFAULT_BASE_URL = 'https://market-api.probable.markets';
9
19
  exports.SEARCH_PATH = '/public/api/v1/public-search/';
10
20
  exports.EVENTS_PATH = '/public/api/v1/events/';
@@ -53,6 +63,10 @@ function mapMarketToUnified(market, event) {
53
63
  image: market.icon || event?.icon || event?.image || undefined,
54
64
  category: event?.category || market.category || undefined,
55
65
  tags: market.tags || event?.tags || [],
66
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(market, PROBABLE_PROMOTED_MARKET_KEYS,
67
+ // event_slug is not promoted to any first-class column — attach it so
68
+ // markets remain queryable by their parent event slug.
69
+ event ? { event_slug: event.slug } : undefined),
56
70
  };
57
71
  (0, market_utils_1.addBinaryOutcomes)(um);
58
72
  return um;
@@ -80,6 +94,9 @@ function mapEventToUnified(event) {
80
94
  image: event.icon || event.image || undefined,
81
95
  category: event.category || undefined,
82
96
  tags: event.tags || [],
97
+ // Captures non-promoted event fields; markets child array is already
98
+ // promoted to the unified markets column so it is excluded.
99
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(event, PROBABLE_PROMOTED_EVENT_KEYS),
83
100
  };
84
101
  return unifiedEvent;
85
102
  }
@@ -2,7 +2,20 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SmarketsNormalizer = void 0;
4
4
  const market_utils_1 = require("../../utils/market-utils");
5
+ const metadata_1 = require("../../utils/metadata");
5
6
  const price_1 = require("./price");
7
+ // Raw Smarkets event fields already promoted to first-class Unified columns —
8
+ // excluded from sourceMetadata so we capture only vendor data not in the
9
+ // unified shape.
10
+ const SMARKETS_PROMOTED_EVENT_KEYS = [
11
+ 'id', 'name', 'description', 'slug', 'full_slug',
12
+ 'start_datetime', 'end_date',
13
+ ];
14
+ // Raw Smarkets market fields already promoted to first-class Unified columns.
15
+ const SMARKETS_PROMOTED_MARKET_KEYS = [
16
+ 'id', 'event_id', 'name', 'slug', 'description',
17
+ 'category', 'categories',
18
+ ];
6
19
  // ----------------------------------------------------------------------------
7
20
  // Helpers
8
21
  // ----------------------------------------------------------------------------
@@ -94,6 +107,9 @@ class SmarketsNormalizer {
94
107
  url: buildEventUrl(event),
95
108
  category,
96
109
  tags,
110
+ // event_id is promoted to eventId; parent_id lives on the raw event
111
+ // (not a recurring/series market field), so no extra is needed.
112
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(market, SMARKETS_PROMOTED_MARKET_KEYS),
97
113
  };
98
114
  (0, market_utils_1.addBinaryOutcomes)(um);
99
115
  return um;
@@ -116,6 +132,10 @@ class SmarketsNormalizer {
116
132
  url: buildEventUrl(raw.event),
117
133
  category,
118
134
  tags: category ? [category] : [],
135
+ // Captures non-promoted event fields: state, type, parent_id,
136
+ // start_date, created, modified, bettable, hidden, inplay_enabled,
137
+ // short_name, seo_description, special_rules, chart_time_period, etc.
138
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw.event, SMARKETS_PROMOTED_EVENT_KEYS),
119
139
  };
120
140
  }
121
141
  normalizeOrderBook(raw, _id) {
@@ -1,7 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SuibetsNormalizer = void 0;
4
+ const metadata_1 = require("../../utils/metadata");
4
5
  const utils_1 = require("./utils");
6
+ // Raw SuiBets offer fields already promoted to first-class UnifiedMarket columns.
7
+ // Omit these from sourceMetadata to capture only vendor-specific data not
8
+ // represented by the unified shape.
9
+ const SUIBETS_PROMOTED_OFFER_KEYS = [
10
+ 'id', 'matchId', 'matchName', 'homeTeam', 'awayTeam',
11
+ 'creatorOdds', 'creatorStake', 'remainingStake', 'totalMatched',
12
+ 'matchDate', 'expiresAt', 'status', 'onchainOfferId',
13
+ 'leagueName', 'sport', 'isOnchain',
14
+ ];
15
+ // Raw SuiBets event fields already promoted to first-class UnifiedEvent columns.
16
+ const SUIBETS_PROMOTED_EVENT_KEYS = [
17
+ 'id', 'name', 'homeTeam', 'awayTeam', 'sport', 'leagueName', 'offers',
18
+ ];
5
19
  function liquidity(offer) {
6
20
  const remaining = offer.remainingStake ?? offer.creatorStake;
7
21
  return (0, utils_1.mistToSui)(remaining);
@@ -57,6 +71,9 @@ class SuibetsNormalizer {
57
71
  contractAddress: raw.onchainOfferId,
58
72
  yes: creatorOutcome,
59
73
  no: takerOutcome,
74
+ // Retains creatorWallet, creatorTeam, takerStake, currency \u2014 fields
75
+ // that are vendor-specific and not promoted to any unified column.
76
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, SUIBETS_PROMOTED_OFFER_KEYS),
60
77
  };
61
78
  return market;
62
79
  }
@@ -84,6 +101,9 @@ class SuibetsNormalizer {
84
101
  url: 'https://suibets.replit.app/p2p',
85
102
  category: 'Sports',
86
103
  tags: ['Sports', 'P2P', 'Sui', raw.sport, raw.leagueName].filter((t) => Boolean(t)),
104
+ // Retains matchDate and status \u2014 event-level fields not promoted to
105
+ // any first-class UnifiedEvent column.
106
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, SUIBETS_PROMOTED_EVENT_KEYS),
87
107
  };
88
108
  }
89
109
  normalizePosition(raw) {
@@ -2654,6 +2654,13 @@ components:
2654
2654
  contractAddress:
2655
2655
  type: string
2656
2656
  description: 'On-chain contract / condition identifier where applicable (Polymarket conditionId, etc.).'
2657
+ sourceMetadata:
2658
+ type: object
2659
+ additionalProperties: {}
2660
+ description: >-
2661
+ Raw venue-specific metadata not captured by first-class fields (e.g. Kalshi series_ticker / series_title
2662
+ from the parent event, Polymarket series). Passed through verbatim so downstream consumers can recover
2663
+ anything the unified shape omits. Each venue populates what it has.
2657
2664
  sourceExchange:
2658
2665
  type: string
2659
2666
  description: 'The exchange/venue this market originates from (e.g. ''polymarket'', ''kalshi''). Populated by the Router.'
@@ -2755,6 +2762,13 @@ components:
2755
2762
  Optional list of tags. More granular than category — e.g. ["Sports", "FIFA World Cup", "2026 FIFA World
2756
2763
  Cup"] or ["Politics", "Geopolitics", "Middle East"]. Tags vary by venue: Polymarket markets carry several,
2757
2764
  Kalshi typically one.
2765
+ sourceMetadata:
2766
+ type: object
2767
+ additionalProperties: {}
2768
+ description: >-
2769
+ Raw venue-specific metadata not captured by first-class fields (e.g. Kalshi series_ticker / series_title,
2770
+ Polymarket series). Passed through verbatim so downstream consumers can recover anything the unified shape
2771
+ omits. Each venue populates what it has.
2758
2772
  sourceExchange:
2759
2773
  type: string
2760
2774
  description: 'The exchange/venue this event originates from (e.g. ''polymarket'', ''kalshi''). Populated by the Router.'
package/dist/types.d.ts CHANGED
@@ -37,6 +37,8 @@ export interface UnifiedEvent {
37
37
  category?: string;
38
38
  /** Optional list of tags. More granular than category — e.g. ["Sports", "FIFA World Cup", "2026 FIFA World Cup"] or ["Politics", "Geopolitics", "Middle East"]. Tags vary by venue: Polymarket markets carry several, Kalshi typically one. */
39
39
  tags?: string[];
40
+ /** Raw venue-specific metadata not captured by first-class fields (e.g. Kalshi series_ticker / series_title, Polymarket series). Passed through verbatim so downstream consumers can recover anything the unified shape omits. Each venue populates what it has. */
41
+ sourceMetadata?: Record<string, unknown>;
40
42
  /** The exchange/venue this event originates from (e.g. 'polymarket', 'kalshi'). Populated by the Router. */
41
43
  sourceExchange?: string;
42
44
  }
@@ -75,6 +77,8 @@ export interface UnifiedMarket {
75
77
  status?: string;
76
78
  /** On-chain contract / condition identifier where applicable (Polymarket conditionId, etc.). */
77
79
  contractAddress?: string;
80
+ /** Raw venue-specific metadata not captured by first-class fields (e.g. Kalshi series_ticker / series_title from the parent event, Polymarket series). Passed through verbatim so downstream consumers can recover anything the unified shape omits. Each venue populates what it has. */
81
+ sourceMetadata?: Record<string, unknown>;
78
82
  /** The exchange/venue this market originates from (e.g. 'polymarket', 'kalshi'). Populated by the Router. */
79
83
  sourceExchange?: string;
80
84
  /** Convenience accessor for the YES outcome on a binary market. */
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Build a `sourceMetadata` object from a raw venue payload, capturing only the
3
+ * venue-specific data that the unified shape would otherwise drop.
4
+ *
5
+ * Keys listed in `promotedKeys` are omitted because they are already
6
+ * represented by first-class Unified / DB columns (price, volume, status, ...),
7
+ * so keeping them here would duplicate data. Everything else on the raw payload
8
+ * is preserved verbatim. `extra` adds non-promoted fields that live on a
9
+ * different raw object (e.g. a parent event's series identifiers attached to a
10
+ * market); `undefined` extras are skipped so we never store empty keys.
11
+ *
12
+ * Returns a new object — the inputs are never mutated.
13
+ */
14
+ export declare function buildSourceMetadata(raw: Record<string, unknown> | null | undefined, promotedKeys: readonly string[], extra?: Record<string, unknown>): Record<string, unknown>;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildSourceMetadata = buildSourceMetadata;
4
+ /**
5
+ * Build a `sourceMetadata` object from a raw venue payload, capturing only the
6
+ * venue-specific data that the unified shape would otherwise drop.
7
+ *
8
+ * Keys listed in `promotedKeys` are omitted because they are already
9
+ * represented by first-class Unified / DB columns (price, volume, status, ...),
10
+ * so keeping them here would duplicate data. Everything else on the raw payload
11
+ * is preserved verbatim. `extra` adds non-promoted fields that live on a
12
+ * different raw object (e.g. a parent event's series identifiers attached to a
13
+ * market); `undefined` extras are skipped so we never store empty keys.
14
+ *
15
+ * Returns a new object — the inputs are never mutated.
16
+ */
17
+ function buildSourceMetadata(raw, promotedKeys, extra) {
18
+ const promoted = new Set(promotedKeys);
19
+ const out = {};
20
+ if (raw && typeof raw === 'object') {
21
+ for (const [key, value] of Object.entries(raw)) {
22
+ if (!promoted.has(key))
23
+ out[key] = value;
24
+ }
25
+ }
26
+ if (extra) {
27
+ for (const [key, value] of Object.entries(extra)) {
28
+ if (value !== undefined)
29
+ out[key] = value;
30
+ }
31
+ }
32
+ return out;
33
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmxt-core",
3
- "version": "2.46.14",
3
+ "version": "2.47.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.46.14,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.46.14,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.47.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.47.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",