pmxtjs 2.49.1 → 2.49.4

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A unified TypeScript/Node.js SDK for prediction markets — The ccxt for prediction markets.
4
4
 
5
- > **Note**: Use with a PMXT API key (hosted, recommended) or self-host the sidecar locally. Get a key at [pmxt.dev/dashboard](https://pmxt.dev/dashboard).
5
+ > **Note**: Use with a PMXT API key (hosted, recommended) or run a local PMXT service. Get a key at [pmxt.dev/dashboard](https://pmxt.dev/dashboard).
6
6
 
7
7
  ## Installation
8
8
 
@@ -53,7 +53,7 @@ When you pass `pmxtApiKey`, the SDK talks to PMXT's hosted services: catalog req
53
53
 
54
54
  ### How it works (self-hosted)
55
55
 
56
- Omit `pmxtApiKey` to use the local sidecar. Install `pmxt-core` from npm and supply venue credentials directly. See [Self-hosted trading (advanced)](#self-hosted-trading-advanced) below.
56
+ Omit `pmxtApiKey` to use the local PMXT service. Install `pmxt-core` from npm and supply venue credentials directly. See [Self-hosted trading (advanced)](#self-hosted-trading-advanced) below.
57
57
 
58
58
  ## Core Methods
59
59
 
@@ -139,7 +139,7 @@ See the full [hosted trading guide](https://pmxt.dev/docs/concepts/hosted-tradin
139
139
 
140
140
  ### Self-hosted trading (advanced)
141
141
 
142
- When self-hosting, supply venue credentials directly — no `pmxtApiKey`. The SDK spawns a local sidecar process.
142
+ When self-hosting, supply venue credentials directly — no `pmxtApiKey`. The SDK spawns a local PMXT service.
143
143
 
144
144
  **Polymarket:**
145
145
  ```typescript
@@ -72,6 +72,9 @@ export interface ExchangeOptions {
72
72
  */
73
73
  export declare abstract class Exchange {
74
74
  private static readonly OBDATA_WATCH_ALL_SOURCES;
75
+ private static readonly _UUID_RE;
76
+ /** True iff `value` parses as a canonical catalog UUID string. */
77
+ protected static _isCatalogUuid(value: string): boolean;
75
78
  exchangeName: string;
76
79
  pmxtApiKey?: string;
77
80
  protected apiKey?: string;
@@ -148,6 +148,14 @@ export class Exchange {
148
148
  "kalshi",
149
149
  "opinion",
150
150
  ]);
151
+ // Match a canonical 8-4-4-4-12 UUID string. The hosted catalog emits
152
+ // UUIDs in this exact shape, so a regex is both faster and stricter
153
+ // than `crypto.randomUUID()`-style parsing.
154
+ static _UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
155
+ /** True iff `value` parses as a canonical catalog UUID string. */
156
+ static _isCatalogUuid(value) {
157
+ return Exchange._UUID_RE.test(value);
158
+ }
151
159
  // Public so structural interfaces like `HostedClientLike`
152
160
  // (./hosted-routing) can read the venue name and hosted credentials
153
161
  // without violating protected-access on this base class.
@@ -2202,12 +2210,17 @@ export class Exchange {
2202
2210
  throw new InvalidOrder("cannot specify both 'outcome' and 'marketId'/'outcomeId'");
2203
2211
  }
2204
2212
  const outcome = params.outcome;
2205
- if (!outcome.marketId) {
2206
- throw new InvalidOrder("outcome.marketId is not set; ensure the outcome comes from a fetched market");
2213
+ if (!outcome.outcomeId) {
2214
+ throw new InvalidOrder("outcome.outcomeId is not set; ensure the outcome comes from a fetched market");
2207
2215
  }
2208
- marketId = outcome.marketId;
2216
+ // marketId is optional in hosted mode -- backend derives it from
2217
+ // outcomeId (catalog UUID). Forward it when present for backcompat.
2218
+ marketId = outcome.marketId || undefined;
2209
2219
  outcomeId = outcome.outcomeId;
2210
2220
  }
2221
+ if (!outcomeId) {
2222
+ throw new InvalidOrder("outcomeId is required (or pass an 'outcome' from a fetched market)");
2223
+ }
2211
2224
  const side = String(params.side);
2212
2225
  const orderType = String(params.type ?? "market");
2213
2226
  const denom = params["denom"];
@@ -2244,15 +2257,37 @@ export class Exchange {
2244
2257
  }
2245
2258
  // to6dec throws InvalidOrder for sub-micro precision.
2246
2259
  const amount6dec = to6dec(params.amount).toString();
2260
+ // The supplied outcomeId may be a catalog UUID OR a venue-native id
2261
+ // (e.g. a Polymarket tokenId or an Opinion market hash). Catalog
2262
+ // UUIDs are forwarded as `outcome_id`; venue-native ids are
2263
+ // forwarded as `(venue, venue_outcome_id)` so the backend resolver
2264
+ // picks the right path. Either shape is accepted by the v0 trading
2265
+ // API.
2247
2266
  const body = {
2248
- market_id: marketId,
2249
- outcome_id: outcomeId,
2250
2267
  side,
2251
2268
  order_type: orderType,
2252
2269
  denom: resolvedDenom,
2253
2270
  amount: params.amount,
2254
2271
  amount_6dec: amount6dec,
2255
2272
  };
2273
+ if (Exchange._isCatalogUuid(outcomeId)) {
2274
+ body["outcome_id"] = outcomeId;
2275
+ // market_id is optional in hosted mode: backend derives it
2276
+ // from outcome_id (UUID) when omitted. Forward only when the
2277
+ // caller supplied a non-empty UUID -- "absent" and "null" are
2278
+ // not equivalent under some Pydantic configs on the backend.
2279
+ if (marketId && Exchange._isCatalogUuid(marketId)) {
2280
+ body["market_id"] = marketId;
2281
+ }
2282
+ }
2283
+ else {
2284
+ // Venue-native form: backend resolves the row from
2285
+ // (source_exchange, pmxt_id). marketId from a venue client is
2286
+ // itself venue-native and would fail backend UUID validation
2287
+ // if forwarded -- suppress it.
2288
+ body["venue"] = this.exchangeName;
2289
+ body["venue_outcome_id"] = outcomeId;
2290
+ }
2256
2291
  if (params.price !== undefined)
2257
2292
  body["price"] = params.price;
2258
2293
  const extra = params;
@@ -72,6 +72,9 @@ export interface ExchangeOptions {
72
72
  */
73
73
  export declare abstract class Exchange {
74
74
  private static readonly OBDATA_WATCH_ALL_SOURCES;
75
+ private static readonly _UUID_RE;
76
+ /** True iff `value` parses as a canonical catalog UUID string. */
77
+ protected static _isCatalogUuid(value: string): boolean;
75
78
  exchangeName: string;
76
79
  pmxtApiKey?: string;
77
80
  protected apiKey?: string;
@@ -151,6 +151,14 @@ class Exchange {
151
151
  "kalshi",
152
152
  "opinion",
153
153
  ]);
154
+ // Match a canonical 8-4-4-4-12 UUID string. The hosted catalog emits
155
+ // UUIDs in this exact shape, so a regex is both faster and stricter
156
+ // than `crypto.randomUUID()`-style parsing.
157
+ static _UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
158
+ /** True iff `value` parses as a canonical catalog UUID string. */
159
+ static _isCatalogUuid(value) {
160
+ return Exchange._UUID_RE.test(value);
161
+ }
154
162
  // Public so structural interfaces like `HostedClientLike`
155
163
  // (./hosted-routing) can read the venue name and hosted credentials
156
164
  // without violating protected-access on this base class.
@@ -2205,12 +2213,17 @@ class Exchange {
2205
2213
  throw new errors_js_1.InvalidOrder("cannot specify both 'outcome' and 'marketId'/'outcomeId'");
2206
2214
  }
2207
2215
  const outcome = params.outcome;
2208
- if (!outcome.marketId) {
2209
- throw new errors_js_1.InvalidOrder("outcome.marketId is not set; ensure the outcome comes from a fetched market");
2216
+ if (!outcome.outcomeId) {
2217
+ throw new errors_js_1.InvalidOrder("outcome.outcomeId is not set; ensure the outcome comes from a fetched market");
2210
2218
  }
2211
- marketId = outcome.marketId;
2219
+ // marketId is optional in hosted mode -- backend derives it from
2220
+ // outcomeId (catalog UUID). Forward it when present for backcompat.
2221
+ marketId = outcome.marketId || undefined;
2212
2222
  outcomeId = outcome.outcomeId;
2213
2223
  }
2224
+ if (!outcomeId) {
2225
+ throw new errors_js_1.InvalidOrder("outcomeId is required (or pass an 'outcome' from a fetched market)");
2226
+ }
2214
2227
  const side = String(params.side);
2215
2228
  const orderType = String(params.type ?? "market");
2216
2229
  const denom = params["denom"];
@@ -2247,15 +2260,37 @@ class Exchange {
2247
2260
  }
2248
2261
  // to6dec throws InvalidOrder for sub-micro precision.
2249
2262
  const amount6dec = (0, hosted_mappers_js_1.to6dec)(params.amount).toString();
2263
+ // The supplied outcomeId may be a catalog UUID OR a venue-native id
2264
+ // (e.g. a Polymarket tokenId or an Opinion market hash). Catalog
2265
+ // UUIDs are forwarded as `outcome_id`; venue-native ids are
2266
+ // forwarded as `(venue, venue_outcome_id)` so the backend resolver
2267
+ // picks the right path. Either shape is accepted by the v0 trading
2268
+ // API.
2250
2269
  const body = {
2251
- market_id: marketId,
2252
- outcome_id: outcomeId,
2253
2270
  side,
2254
2271
  order_type: orderType,
2255
2272
  denom: resolvedDenom,
2256
2273
  amount: params.amount,
2257
2274
  amount_6dec: amount6dec,
2258
2275
  };
2276
+ if (Exchange._isCatalogUuid(outcomeId)) {
2277
+ body["outcome_id"] = outcomeId;
2278
+ // market_id is optional in hosted mode: backend derives it
2279
+ // from outcome_id (UUID) when omitted. Forward only when the
2280
+ // caller supplied a non-empty UUID -- "absent" and "null" are
2281
+ // not equivalent under some Pydantic configs on the backend.
2282
+ if (marketId && Exchange._isCatalogUuid(marketId)) {
2283
+ body["market_id"] = marketId;
2284
+ }
2285
+ }
2286
+ else {
2287
+ // Venue-native form: backend resolves the row from
2288
+ // (source_exchange, pmxt_id). marketId from a venue client is
2289
+ // itself venue-native and would fail backend UUID validation
2290
+ // if forwarded -- suppress it.
2291
+ body["venue"] = this.exchangeName;
2292
+ body["venue_outcome_id"] = outcomeId;
2293
+ }
2259
2294
  if (params.price !== undefined)
2260
2295
  body["price"] = params.price;
2261
2296
  const extra = params;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmxtjs",
3
- "version": "2.49.1",
3
+ "version": "2.49.4",
4
4
  "description": "OpenAPI client for pmxtjs",
5
5
  "author": "OpenAPI-Generator",
6
6
  "repository": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmxtjs",
3
- "version": "2.49.1",
3
+ "version": "2.49.4",
4
4
  "description": "Unified prediction market data API - The ccxt for prediction markets",
5
5
  "author": "PMXT Contributors",
6
6
  "repository": {
@@ -43,7 +43,7 @@
43
43
  "unified"
44
44
  ],
45
45
  "dependencies": {
46
- "pmxt-core": "2.49.1",
46
+ "pmxt-core": "2.49.4",
47
47
  "ws": "^8.18.0"
48
48
  },
49
49
  "peerDependencies": {
package/pmxt/client.ts CHANGED
@@ -300,6 +300,17 @@ export abstract class Exchange {
300
300
  "opinion",
301
301
  ]);
302
302
 
303
+ // Match a canonical 8-4-4-4-12 UUID string. The hosted catalog emits
304
+ // UUIDs in this exact shape, so a regex is both faster and stricter
305
+ // than `crypto.randomUUID()`-style parsing.
306
+ private static readonly _UUID_RE =
307
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
308
+
309
+ /** True iff `value` parses as a canonical catalog UUID string. */
310
+ protected static _isCatalogUuid(value: string): boolean {
311
+ return Exchange._UUID_RE.test(value);
312
+ }
313
+
303
314
  // Public so structural interfaces like `HostedClientLike`
304
315
  // (./hosted-routing) can read the venue name and hosted credentials
305
316
  // without violating protected-access on this base class.
@@ -2434,8 +2445,8 @@ export abstract class Exchange {
2434
2445
  private _hostedBuildOrderBody(
2435
2446
  params: CreateOrderParams & { outcome?: MarketOutcome },
2436
2447
  ): Record<string, unknown> {
2437
- let marketId = params.marketId;
2438
- let outcomeId = params.outcomeId;
2448
+ let marketId: string | undefined = params.marketId;
2449
+ let outcomeId: string | undefined = params.outcomeId;
2439
2450
 
2440
2451
  if (params.outcome) {
2441
2452
  if (marketId !== undefined || outcomeId !== undefined) {
@@ -2444,15 +2455,23 @@ export abstract class Exchange {
2444
2455
  );
2445
2456
  }
2446
2457
  const outcome: MarketOutcome = params.outcome;
2447
- if (!outcome.marketId) {
2458
+ if (!outcome.outcomeId) {
2448
2459
  throw new InvalidOrder(
2449
- "outcome.marketId is not set; ensure the outcome comes from a fetched market",
2460
+ "outcome.outcomeId is not set; ensure the outcome comes from a fetched market",
2450
2461
  );
2451
2462
  }
2452
- marketId = outcome.marketId;
2463
+ // marketId is optional in hosted mode -- backend derives it from
2464
+ // outcomeId (catalog UUID). Forward it when present for backcompat.
2465
+ marketId = outcome.marketId || undefined;
2453
2466
  outcomeId = outcome.outcomeId;
2454
2467
  }
2455
2468
 
2469
+ if (!outcomeId) {
2470
+ throw new InvalidOrder(
2471
+ "outcomeId is required (or pass an 'outcome' from a fetched market)",
2472
+ );
2473
+ }
2474
+
2456
2475
  const side = String(params.side);
2457
2476
  const orderType = String(params.type ?? "market");
2458
2477
  const denom = (params as unknown as Record<string, unknown>)["denom"] as
@@ -2493,15 +2512,36 @@ export abstract class Exchange {
2493
2512
  // to6dec throws InvalidOrder for sub-micro precision.
2494
2513
  const amount6dec = to6dec(params.amount as number).toString();
2495
2514
 
2515
+ // The supplied outcomeId may be a catalog UUID OR a venue-native id
2516
+ // (e.g. a Polymarket tokenId or an Opinion market hash). Catalog
2517
+ // UUIDs are forwarded as `outcome_id`; venue-native ids are
2518
+ // forwarded as `(venue, venue_outcome_id)` so the backend resolver
2519
+ // picks the right path. Either shape is accepted by the v0 trading
2520
+ // API.
2496
2521
  const body: Record<string, unknown> = {
2497
- market_id: marketId,
2498
- outcome_id: outcomeId,
2499
2522
  side,
2500
2523
  order_type: orderType,
2501
2524
  denom: resolvedDenom,
2502
2525
  amount: params.amount,
2503
2526
  amount_6dec: amount6dec,
2504
2527
  };
2528
+ if (Exchange._isCatalogUuid(outcomeId)) {
2529
+ body["outcome_id"] = outcomeId;
2530
+ // market_id is optional in hosted mode: backend derives it
2531
+ // from outcome_id (UUID) when omitted. Forward only when the
2532
+ // caller supplied a non-empty UUID -- "absent" and "null" are
2533
+ // not equivalent under some Pydantic configs on the backend.
2534
+ if (marketId && Exchange._isCatalogUuid(marketId)) {
2535
+ body["market_id"] = marketId;
2536
+ }
2537
+ } else {
2538
+ // Venue-native form: backend resolves the row from
2539
+ // (source_exchange, pmxt_id). marketId from a venue client is
2540
+ // itself venue-native and would fail backend UUID validation
2541
+ // if forwarded -- suppress it.
2542
+ body["venue"] = this.exchangeName;
2543
+ body["venue_outcome_id"] = outcomeId;
2544
+ }
2505
2545
 
2506
2546
  if (params.price !== undefined) body["price"] = params.price;
2507
2547
  const extra = params as unknown as Record<string, unknown>;