@dubsdotapp/node 0.1.0 → 0.1.1

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/dist/index.js CHANGED
@@ -133,9 +133,10 @@ var Dubs = class {
133
133
  });
134
134
  const json = await res.json();
135
135
  if (!res.ok) {
136
+ const msg = typeof json.message === "string" ? json.message : typeof json.error === "string" ? json.error : `Request failed with status ${res.status}: ${JSON.stringify(json)}`;
136
137
  throw new DubsApiError(
137
138
  json.code || "api_error",
138
- json.message || json.error || `Request failed with status ${res.status}`,
139
+ msg,
139
140
  res.status
140
141
  );
141
142
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/constants.ts","../src/errors.ts"],"sourcesContent":["export { Dubs } from './client';\nexport { DubsApiError } from './errors';\nexport type { DubsConfig, ResolveGameParams, ResolveGameResult, WebhookEvent } from './types';\nexport { DEFAULT_BASE_URL, NETWORK_CONFIG } from './constants';\nexport type { DubsNetwork } from './constants';\n","import crypto from 'crypto';\nimport { DEFAULT_BASE_URL, NETWORK_CONFIG } from './constants';\nimport { DubsApiError } from './errors';\nimport type { DubsConfig, ResolveGameParams, ResolveGameResult, WebhookEvent } from './types';\n\nexport class Dubs {\n private readonly apiKey: string;\n private readonly resolutionSecret?: string;\n private readonly baseUrl: string;\n\n constructor(config: DubsConfig) {\n this.apiKey = config.apiKey;\n this.resolutionSecret = config.resolutionSecret;\n\n if (config.baseUrl) {\n this.baseUrl = config.baseUrl;\n } else if (config.network) {\n this.baseUrl = NETWORK_CONFIG[config.network].baseUrl;\n } else {\n this.baseUrl = DEFAULT_BASE_URL;\n }\n }\n\n /**\n * Resolve a custom game (game_mode=6).\n *\n * Automatically computes the HMAC-SHA256 signature using your resolution secret.\n */\n async resolveGame(gameId: string, params: ResolveGameParams): Promise<ResolveGameResult> {\n if (!this.resolutionSecret) {\n throw new DubsApiError(\n 'missing_resolution_secret',\n 'resolutionSecret is required for resolveGame(). Pass it in the Dubs constructor.',\n 0,\n );\n }\n\n const body = JSON.stringify({\n winner: params.winner,\n ...(params.metadata && { metadata: params.metadata }),\n });\n\n const hmac = crypto.createHmac('sha256', this.resolutionSecret).update(body).digest('hex');\n\n return this.request<ResolveGameResult>('POST', `/games/${gameId}/resolve`, body, {\n 'x-dubs-signature': `sha256=${hmac}`,\n });\n }\n\n /**\n * Verify an incoming Dubs webhook request.\n *\n * @param rawBody - The raw request body (string or Buffer)\n * @param signature - The X-Dubs-Signature header value\n * @param secret - Your webhook secret (from the developer portal)\n * @returns The parsed webhook event\n * @throws DubsApiError if the signature is invalid\n */\n static verifyWebhook(rawBody: string | Buffer, signature: string, secret: string): WebhookEvent {\n const body = typeof rawBody === 'string' ? rawBody : rawBody.toString('utf-8');\n const expected = crypto.createHmac('sha256', secret).update(body).digest('hex');\n\n if (\n signature.length !== expected.length ||\n !crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))\n ) {\n throw new DubsApiError('invalid_signature', 'Webhook signature verification failed', 401);\n }\n\n return JSON.parse(body) as WebhookEvent;\n }\n\n // ── Private ──\n\n private async request<T>(\n method: string,\n path: string,\n body?: string,\n extraHeaders?: Record<string, string>,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n\n const headers: Record<string, string> = {\n 'x-api-key': this.apiKey,\n 'Content-Type': 'application/json',\n ...extraHeaders,\n };\n\n const res = await fetch(url, {\n method,\n headers,\n ...(body && { body }),\n });\n\n const json = (await res.json()) as Record<string, unknown>;\n\n if (!res.ok) {\n throw new DubsApiError(\n (json.code as string) || 'api_error',\n (json.message as string) || (json.error as string) || `Request failed with status ${res.status}`,\n res.status,\n );\n }\n\n return json as T;\n }\n}\n","export const DEFAULT_BASE_URL = 'https://dubs-server-prod-9c91d3f01199.herokuapp.com/api/developer/v1';\n\nexport type DubsNetwork = 'devnet' | 'mainnet-beta';\n\nexport const NETWORK_CONFIG: Record<DubsNetwork, { baseUrl: string }> = {\n 'mainnet-beta': {\n baseUrl: 'https://dubs-server-prod-9c91d3f01199.herokuapp.com/api/developer/v1',\n },\n devnet: {\n baseUrl: 'https://dubs-server-dev-55d1fba09a97.herokuapp.com/api/developer/v1',\n },\n};\n","export class DubsApiError extends Error {\n public readonly code: string;\n public readonly httpStatus: number;\n\n constructor(code: string, message: string, httpStatus: number) {\n super(message);\n this.name = 'DubsApiError';\n this.code = code;\n this.httpStatus = httpStatus;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAAmB;;;ACAZ,IAAM,mBAAmB;AAIzB,IAAM,iBAA2D;AAAA,EACtE,gBAAgB;AAAA,IACd,SAAS;AAAA,EACX;AAAA,EACA,QAAQ;AAAA,IACN,SAAS;AAAA,EACX;AACF;;;ACXO,IAAM,eAAN,cAA2B,MAAM;AAAA,EACtB;AAAA,EACA;AAAA,EAEhB,YAAY,MAAc,SAAiB,YAAoB;AAC7D,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,aAAa;AAAA,EACpB;AACF;;;AFLO,IAAM,OAAN,MAAW;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,QAAoB;AAC9B,SAAK,SAAS,OAAO;AACrB,SAAK,mBAAmB,OAAO;AAE/B,QAAI,OAAO,SAAS;AAClB,WAAK,UAAU,OAAO;AAAA,IACxB,WAAW,OAAO,SAAS;AACzB,WAAK,UAAU,eAAe,OAAO,OAAO,EAAE;AAAA,IAChD,OAAO;AACL,WAAK,UAAU;AAAA,IACjB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,YAAY,QAAgB,QAAuD;AACvF,QAAI,CAAC,KAAK,kBAAkB;AAC1B,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,OAAO,KAAK,UAAU;AAAA,MAC1B,QAAQ,OAAO;AAAA,MACf,GAAI,OAAO,YAAY,EAAE,UAAU,OAAO,SAAS;AAAA,IACrD,CAAC;AAED,UAAM,OAAO,cAAAA,QAAO,WAAW,UAAU,KAAK,gBAAgB,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK;AAEzF,WAAO,KAAK,QAA2B,QAAQ,UAAU,MAAM,YAAY,MAAM;AAAA,MAC/E,oBAAoB,UAAU,IAAI;AAAA,IACpC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,OAAO,cAAc,SAA0B,WAAmB,QAA8B;AAC9F,UAAM,OAAO,OAAO,YAAY,WAAW,UAAU,QAAQ,SAAS,OAAO;AAC7E,UAAM,WAAW,cAAAA,QAAO,WAAW,UAAU,MAAM,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK;AAE9E,QACE,UAAU,WAAW,SAAS,UAC9B,CAAC,cAAAA,QAAO,gBAAgB,OAAO,KAAK,SAAS,GAAG,OAAO,KAAK,QAAQ,CAAC,GACrE;AACA,YAAM,IAAI,aAAa,qBAAqB,yCAAyC,GAAG;AAAA,IAC1F;AAEA,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB;AAAA;AAAA,EAIA,MAAc,QACZ,QACA,MACA,MACA,cACY;AACZ,UAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI;AAElC,UAAM,UAAkC;AAAA,MACtC,aAAa,KAAK;AAAA,MAClB,gBAAgB;AAAA,MAChB,GAAG;AAAA,IACL;AAEA,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B;AAAA,MACA;AAAA,MACA,GAAI,QAAQ,EAAE,KAAK;AAAA,IACrB,CAAC;AAED,UAAM,OAAQ,MAAM,IAAI,KAAK;AAE7B,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI;AAAA,QACP,KAAK,QAAmB;AAAA,QACxB,KAAK,WAAuB,KAAK,SAAoB,8BAA8B,IAAI,MAAM;AAAA,QAC9F,IAAI;AAAA,MACN;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;","names":["crypto"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/constants.ts","../src/errors.ts"],"sourcesContent":["export { Dubs } from './client';\nexport { DubsApiError } from './errors';\nexport type { DubsConfig, ResolveGameParams, ResolveGameResult, WebhookEvent } from './types';\nexport { DEFAULT_BASE_URL, NETWORK_CONFIG } from './constants';\nexport type { DubsNetwork } from './constants';\n","import crypto from 'crypto';\nimport { DEFAULT_BASE_URL, NETWORK_CONFIG } from './constants';\nimport { DubsApiError } from './errors';\nimport type { DubsConfig, ResolveGameParams, ResolveGameResult, WebhookEvent } from './types';\n\nexport class Dubs {\n private readonly apiKey: string;\n private readonly resolutionSecret?: string;\n private readonly baseUrl: string;\n\n constructor(config: DubsConfig) {\n this.apiKey = config.apiKey;\n this.resolutionSecret = config.resolutionSecret;\n\n if (config.baseUrl) {\n this.baseUrl = config.baseUrl;\n } else if (config.network) {\n this.baseUrl = NETWORK_CONFIG[config.network].baseUrl;\n } else {\n this.baseUrl = DEFAULT_BASE_URL;\n }\n }\n\n /**\n * Resolve a custom game (game_mode=6).\n *\n * Automatically computes the HMAC-SHA256 signature using your resolution secret.\n */\n async resolveGame(gameId: string, params: ResolveGameParams): Promise<ResolveGameResult> {\n if (!this.resolutionSecret) {\n throw new DubsApiError(\n 'missing_resolution_secret',\n 'resolutionSecret is required for resolveGame(). Pass it in the Dubs constructor.',\n 0,\n );\n }\n\n const body = JSON.stringify({\n winner: params.winner,\n ...(params.metadata && { metadata: params.metadata }),\n });\n\n const hmac = crypto.createHmac('sha256', this.resolutionSecret).update(body).digest('hex');\n\n return this.request<ResolveGameResult>('POST', `/games/${gameId}/resolve`, body, {\n 'x-dubs-signature': `sha256=${hmac}`,\n });\n }\n\n /**\n * Verify an incoming Dubs webhook request.\n *\n * @param rawBody - The raw request body (string or Buffer)\n * @param signature - The X-Dubs-Signature header value\n * @param secret - Your webhook secret (from the developer portal)\n * @returns The parsed webhook event\n * @throws DubsApiError if the signature is invalid\n */\n static verifyWebhook(rawBody: string | Buffer, signature: string, secret: string): WebhookEvent {\n const body = typeof rawBody === 'string' ? rawBody : rawBody.toString('utf-8');\n const expected = crypto.createHmac('sha256', secret).update(body).digest('hex');\n\n if (\n signature.length !== expected.length ||\n !crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))\n ) {\n throw new DubsApiError('invalid_signature', 'Webhook signature verification failed', 401);\n }\n\n return JSON.parse(body) as WebhookEvent;\n }\n\n // ── Private ──\n\n private async request<T>(\n method: string,\n path: string,\n body?: string,\n extraHeaders?: Record<string, string>,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n\n const headers: Record<string, string> = {\n 'x-api-key': this.apiKey,\n 'Content-Type': 'application/json',\n ...extraHeaders,\n };\n\n const res = await fetch(url, {\n method,\n headers,\n ...(body && { body }),\n });\n\n const json = (await res.json()) as Record<string, unknown>;\n\n if (!res.ok) {\n const msg = typeof json.message === 'string' ? json.message\n : typeof json.error === 'string' ? json.error\n : `Request failed with status ${res.status}: ${JSON.stringify(json)}`;\n throw new DubsApiError(\n (json.code as string) || 'api_error',\n msg,\n res.status,\n );\n }\n\n return json as T;\n }\n}\n","export const DEFAULT_BASE_URL = 'https://dubs-server-prod-9c91d3f01199.herokuapp.com/api/developer/v1';\n\nexport type DubsNetwork = 'devnet' | 'mainnet-beta';\n\nexport const NETWORK_CONFIG: Record<DubsNetwork, { baseUrl: string }> = {\n 'mainnet-beta': {\n baseUrl: 'https://dubs-server-prod-9c91d3f01199.herokuapp.com/api/developer/v1',\n },\n devnet: {\n baseUrl: 'https://dubs-server-dev-55d1fba09a97.herokuapp.com/api/developer/v1',\n },\n};\n","export class DubsApiError extends Error {\n public readonly code: string;\n public readonly httpStatus: number;\n\n constructor(code: string, message: string, httpStatus: number) {\n super(message);\n this.name = 'DubsApiError';\n this.code = code;\n this.httpStatus = httpStatus;\n }\n}\n"],"mappings":"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","names":["crypto"]}
package/dist/index.mjs CHANGED
@@ -94,9 +94,10 @@ var Dubs = class {
94
94
  });
95
95
  const json = await res.json();
96
96
  if (!res.ok) {
97
+ const msg = typeof json.message === "string" ? json.message : typeof json.error === "string" ? json.error : `Request failed with status ${res.status}: ${JSON.stringify(json)}`;
97
98
  throw new DubsApiError(
98
99
  json.code || "api_error",
99
- json.message || json.error || `Request failed with status ${res.status}`,
100
+ msg,
100
101
  res.status
101
102
  );
102
103
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/constants.ts","../src/errors.ts"],"sourcesContent":["import crypto from 'crypto';\nimport { DEFAULT_BASE_URL, NETWORK_CONFIG } from './constants';\nimport { DubsApiError } from './errors';\nimport type { DubsConfig, ResolveGameParams, ResolveGameResult, WebhookEvent } from './types';\n\nexport class Dubs {\n private readonly apiKey: string;\n private readonly resolutionSecret?: string;\n private readonly baseUrl: string;\n\n constructor(config: DubsConfig) {\n this.apiKey = config.apiKey;\n this.resolutionSecret = config.resolutionSecret;\n\n if (config.baseUrl) {\n this.baseUrl = config.baseUrl;\n } else if (config.network) {\n this.baseUrl = NETWORK_CONFIG[config.network].baseUrl;\n } else {\n this.baseUrl = DEFAULT_BASE_URL;\n }\n }\n\n /**\n * Resolve a custom game (game_mode=6).\n *\n * Automatically computes the HMAC-SHA256 signature using your resolution secret.\n */\n async resolveGame(gameId: string, params: ResolveGameParams): Promise<ResolveGameResult> {\n if (!this.resolutionSecret) {\n throw new DubsApiError(\n 'missing_resolution_secret',\n 'resolutionSecret is required for resolveGame(). Pass it in the Dubs constructor.',\n 0,\n );\n }\n\n const body = JSON.stringify({\n winner: params.winner,\n ...(params.metadata && { metadata: params.metadata }),\n });\n\n const hmac = crypto.createHmac('sha256', this.resolutionSecret).update(body).digest('hex');\n\n return this.request<ResolveGameResult>('POST', `/games/${gameId}/resolve`, body, {\n 'x-dubs-signature': `sha256=${hmac}`,\n });\n }\n\n /**\n * Verify an incoming Dubs webhook request.\n *\n * @param rawBody - The raw request body (string or Buffer)\n * @param signature - The X-Dubs-Signature header value\n * @param secret - Your webhook secret (from the developer portal)\n * @returns The parsed webhook event\n * @throws DubsApiError if the signature is invalid\n */\n static verifyWebhook(rawBody: string | Buffer, signature: string, secret: string): WebhookEvent {\n const body = typeof rawBody === 'string' ? rawBody : rawBody.toString('utf-8');\n const expected = crypto.createHmac('sha256', secret).update(body).digest('hex');\n\n if (\n signature.length !== expected.length ||\n !crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))\n ) {\n throw new DubsApiError('invalid_signature', 'Webhook signature verification failed', 401);\n }\n\n return JSON.parse(body) as WebhookEvent;\n }\n\n // ── Private ──\n\n private async request<T>(\n method: string,\n path: string,\n body?: string,\n extraHeaders?: Record<string, string>,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n\n const headers: Record<string, string> = {\n 'x-api-key': this.apiKey,\n 'Content-Type': 'application/json',\n ...extraHeaders,\n };\n\n const res = await fetch(url, {\n method,\n headers,\n ...(body && { body }),\n });\n\n const json = (await res.json()) as Record<string, unknown>;\n\n if (!res.ok) {\n throw new DubsApiError(\n (json.code as string) || 'api_error',\n (json.message as string) || (json.error as string) || `Request failed with status ${res.status}`,\n res.status,\n );\n }\n\n return json as T;\n }\n}\n","export const DEFAULT_BASE_URL = 'https://dubs-server-prod-9c91d3f01199.herokuapp.com/api/developer/v1';\n\nexport type DubsNetwork = 'devnet' | 'mainnet-beta';\n\nexport const NETWORK_CONFIG: Record<DubsNetwork, { baseUrl: string }> = {\n 'mainnet-beta': {\n baseUrl: 'https://dubs-server-prod-9c91d3f01199.herokuapp.com/api/developer/v1',\n },\n devnet: {\n baseUrl: 'https://dubs-server-dev-55d1fba09a97.herokuapp.com/api/developer/v1',\n },\n};\n","export class DubsApiError extends Error {\n public readonly code: string;\n public readonly httpStatus: number;\n\n constructor(code: string, message: string, httpStatus: number) {\n super(message);\n this.name = 'DubsApiError';\n this.code = code;\n this.httpStatus = httpStatus;\n }\n}\n"],"mappings":";AAAA,OAAO,YAAY;;;ACAZ,IAAM,mBAAmB;AAIzB,IAAM,iBAA2D;AAAA,EACtE,gBAAgB;AAAA,IACd,SAAS;AAAA,EACX;AAAA,EACA,QAAQ;AAAA,IACN,SAAS;AAAA,EACX;AACF;;;ACXO,IAAM,eAAN,cAA2B,MAAM;AAAA,EACtB;AAAA,EACA;AAAA,EAEhB,YAAY,MAAc,SAAiB,YAAoB;AAC7D,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,aAAa;AAAA,EACpB;AACF;;;AFLO,IAAM,OAAN,MAAW;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,QAAoB;AAC9B,SAAK,SAAS,OAAO;AACrB,SAAK,mBAAmB,OAAO;AAE/B,QAAI,OAAO,SAAS;AAClB,WAAK,UAAU,OAAO;AAAA,IACxB,WAAW,OAAO,SAAS;AACzB,WAAK,UAAU,eAAe,OAAO,OAAO,EAAE;AAAA,IAChD,OAAO;AACL,WAAK,UAAU;AAAA,IACjB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,YAAY,QAAgB,QAAuD;AACvF,QAAI,CAAC,KAAK,kBAAkB;AAC1B,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,OAAO,KAAK,UAAU;AAAA,MAC1B,QAAQ,OAAO;AAAA,MACf,GAAI,OAAO,YAAY,EAAE,UAAU,OAAO,SAAS;AAAA,IACrD,CAAC;AAED,UAAM,OAAO,OAAO,WAAW,UAAU,KAAK,gBAAgB,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK;AAEzF,WAAO,KAAK,QAA2B,QAAQ,UAAU,MAAM,YAAY,MAAM;AAAA,MAC/E,oBAAoB,UAAU,IAAI;AAAA,IACpC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,OAAO,cAAc,SAA0B,WAAmB,QAA8B;AAC9F,UAAM,OAAO,OAAO,YAAY,WAAW,UAAU,QAAQ,SAAS,OAAO;AAC7E,UAAM,WAAW,OAAO,WAAW,UAAU,MAAM,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK;AAE9E,QACE,UAAU,WAAW,SAAS,UAC9B,CAAC,OAAO,gBAAgB,OAAO,KAAK,SAAS,GAAG,OAAO,KAAK,QAAQ,CAAC,GACrE;AACA,YAAM,IAAI,aAAa,qBAAqB,yCAAyC,GAAG;AAAA,IAC1F;AAEA,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB;AAAA;AAAA,EAIA,MAAc,QACZ,QACA,MACA,MACA,cACY;AACZ,UAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI;AAElC,UAAM,UAAkC;AAAA,MACtC,aAAa,KAAK;AAAA,MAClB,gBAAgB;AAAA,MAChB,GAAG;AAAA,IACL;AAEA,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B;AAAA,MACA;AAAA,MACA,GAAI,QAAQ,EAAE,KAAK;AAAA,IACrB,CAAC;AAED,UAAM,OAAQ,MAAM,IAAI,KAAK;AAE7B,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI;AAAA,QACP,KAAK,QAAmB;AAAA,QACxB,KAAK,WAAuB,KAAK,SAAoB,8BAA8B,IAAI,MAAM;AAAA,QAC9F,IAAI;AAAA,MACN;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/client.ts","../src/constants.ts","../src/errors.ts"],"sourcesContent":["import crypto from 'crypto';\nimport { DEFAULT_BASE_URL, NETWORK_CONFIG } from './constants';\nimport { DubsApiError } from './errors';\nimport type { DubsConfig, ResolveGameParams, ResolveGameResult, WebhookEvent } from './types';\n\nexport class Dubs {\n private readonly apiKey: string;\n private readonly resolutionSecret?: string;\n private readonly baseUrl: string;\n\n constructor(config: DubsConfig) {\n this.apiKey = config.apiKey;\n this.resolutionSecret = config.resolutionSecret;\n\n if (config.baseUrl) {\n this.baseUrl = config.baseUrl;\n } else if (config.network) {\n this.baseUrl = NETWORK_CONFIG[config.network].baseUrl;\n } else {\n this.baseUrl = DEFAULT_BASE_URL;\n }\n }\n\n /**\n * Resolve a custom game (game_mode=6).\n *\n * Automatically computes the HMAC-SHA256 signature using your resolution secret.\n */\n async resolveGame(gameId: string, params: ResolveGameParams): Promise<ResolveGameResult> {\n if (!this.resolutionSecret) {\n throw new DubsApiError(\n 'missing_resolution_secret',\n 'resolutionSecret is required for resolveGame(). Pass it in the Dubs constructor.',\n 0,\n );\n }\n\n const body = JSON.stringify({\n winner: params.winner,\n ...(params.metadata && { metadata: params.metadata }),\n });\n\n const hmac = crypto.createHmac('sha256', this.resolutionSecret).update(body).digest('hex');\n\n return this.request<ResolveGameResult>('POST', `/games/${gameId}/resolve`, body, {\n 'x-dubs-signature': `sha256=${hmac}`,\n });\n }\n\n /**\n * Verify an incoming Dubs webhook request.\n *\n * @param rawBody - The raw request body (string or Buffer)\n * @param signature - The X-Dubs-Signature header value\n * @param secret - Your webhook secret (from the developer portal)\n * @returns The parsed webhook event\n * @throws DubsApiError if the signature is invalid\n */\n static verifyWebhook(rawBody: string | Buffer, signature: string, secret: string): WebhookEvent {\n const body = typeof rawBody === 'string' ? rawBody : rawBody.toString('utf-8');\n const expected = crypto.createHmac('sha256', secret).update(body).digest('hex');\n\n if (\n signature.length !== expected.length ||\n !crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))\n ) {\n throw new DubsApiError('invalid_signature', 'Webhook signature verification failed', 401);\n }\n\n return JSON.parse(body) as WebhookEvent;\n }\n\n // ── Private ──\n\n private async request<T>(\n method: string,\n path: string,\n body?: string,\n extraHeaders?: Record<string, string>,\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n\n const headers: Record<string, string> = {\n 'x-api-key': this.apiKey,\n 'Content-Type': 'application/json',\n ...extraHeaders,\n };\n\n const res = await fetch(url, {\n method,\n headers,\n ...(body && { body }),\n });\n\n const json = (await res.json()) as Record<string, unknown>;\n\n if (!res.ok) {\n const msg = typeof json.message === 'string' ? json.message\n : typeof json.error === 'string' ? json.error\n : `Request failed with status ${res.status}: ${JSON.stringify(json)}`;\n throw new DubsApiError(\n (json.code as string) || 'api_error',\n msg,\n res.status,\n );\n }\n\n return json as T;\n }\n}\n","export const DEFAULT_BASE_URL = 'https://dubs-server-prod-9c91d3f01199.herokuapp.com/api/developer/v1';\n\nexport type DubsNetwork = 'devnet' | 'mainnet-beta';\n\nexport const NETWORK_CONFIG: Record<DubsNetwork, { baseUrl: string }> = {\n 'mainnet-beta': {\n baseUrl: 'https://dubs-server-prod-9c91d3f01199.herokuapp.com/api/developer/v1',\n },\n devnet: {\n baseUrl: 'https://dubs-server-dev-55d1fba09a97.herokuapp.com/api/developer/v1',\n },\n};\n","export class DubsApiError extends Error {\n public readonly code: string;\n public readonly httpStatus: number;\n\n constructor(code: string, message: string, httpStatus: number) {\n super(message);\n this.name = 'DubsApiError';\n this.code = code;\n this.httpStatus = httpStatus;\n }\n}\n"],"mappings":";AAAA,OAAO,YAAY;;;ACAZ,IAAM,mBAAmB;AAIzB,IAAM,iBAA2D;AAAA,EACtE,gBAAgB;AAAA,IACd,SAAS;AAAA,EACX;AAAA,EACA,QAAQ;AAAA,IACN,SAAS;AAAA,EACX;AACF;;;ACXO,IAAM,eAAN,cAA2B,MAAM;AAAA,EACtB;AAAA,EACA;AAAA,EAEhB,YAAY,MAAc,SAAiB,YAAoB;AAC7D,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,SAAK,aAAa;AAAA,EACpB;AACF;;;AFLO,IAAM,OAAN,MAAW;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,QAAoB;AAC9B,SAAK,SAAS,OAAO;AACrB,SAAK,mBAAmB,OAAO;AAE/B,QAAI,OAAO,SAAS;AAClB,WAAK,UAAU,OAAO;AAAA,IACxB,WAAW,OAAO,SAAS;AACzB,WAAK,UAAU,eAAe,OAAO,OAAO,EAAE;AAAA,IAChD,OAAO;AACL,WAAK,UAAU;AAAA,IACjB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,YAAY,QAAgB,QAAuD;AACvF,QAAI,CAAC,KAAK,kBAAkB;AAC1B,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,OAAO,KAAK,UAAU;AAAA,MAC1B,QAAQ,OAAO;AAAA,MACf,GAAI,OAAO,YAAY,EAAE,UAAU,OAAO,SAAS;AAAA,IACrD,CAAC;AAED,UAAM,OAAO,OAAO,WAAW,UAAU,KAAK,gBAAgB,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK;AAEzF,WAAO,KAAK,QAA2B,QAAQ,UAAU,MAAM,YAAY,MAAM;AAAA,MAC/E,oBAAoB,UAAU,IAAI;AAAA,IACpC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,OAAO,cAAc,SAA0B,WAAmB,QAA8B;AAC9F,UAAM,OAAO,OAAO,YAAY,WAAW,UAAU,QAAQ,SAAS,OAAO;AAC7E,UAAM,WAAW,OAAO,WAAW,UAAU,MAAM,EAAE,OAAO,IAAI,EAAE,OAAO,KAAK;AAE9E,QACE,UAAU,WAAW,SAAS,UAC9B,CAAC,OAAO,gBAAgB,OAAO,KAAK,SAAS,GAAG,OAAO,KAAK,QAAQ,CAAC,GACrE;AACA,YAAM,IAAI,aAAa,qBAAqB,yCAAyC,GAAG;AAAA,IAC1F;AAEA,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB;AAAA;AAAA,EAIA,MAAc,QACZ,QACA,MACA,MACA,cACY;AACZ,UAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI;AAElC,UAAM,UAAkC;AAAA,MACtC,aAAa,KAAK;AAAA,MAClB,gBAAgB;AAAA,MAChB,GAAG;AAAA,IACL;AAEA,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B;AAAA,MACA;AAAA,MACA,GAAI,QAAQ,EAAE,KAAK;AAAA,IACrB,CAAC;AAED,UAAM,OAAQ,MAAM,IAAI,KAAK;AAE7B,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,MAAM,OAAO,KAAK,YAAY,WAAW,KAAK,UAChD,OAAO,KAAK,UAAU,WAAW,KAAK,QACtC,8BAA8B,IAAI,MAAM,KAAK,KAAK,UAAU,IAAI,CAAC;AACrE,YAAM,IAAI;AAAA,QACP,KAAK,QAAmB;AAAA,QACzB;AAAA,QACA,IAAI;AAAA,MACN;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dubsdotapp/node",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Server-side Node.js SDK for the Dubs betting platform",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
package/src/client.ts CHANGED
@@ -95,9 +95,12 @@ export class Dubs {
95
95
  const json = (await res.json()) as Record<string, unknown>;
96
96
 
97
97
  if (!res.ok) {
98
+ const msg = typeof json.message === 'string' ? json.message
99
+ : typeof json.error === 'string' ? json.error
100
+ : `Request failed with status ${res.status}: ${JSON.stringify(json)}`;
98
101
  throw new DubsApiError(
99
102
  (json.code as string) || 'api_error',
100
- (json.message as string) || (json.error as string) || `Request failed with status ${res.status}`,
103
+ msg,
101
104
  res.status,
102
105
  );
103
106
  }