@predictorsdk/client 0.3.1 → 0.3.2

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.
@@ -1,6 +1,7 @@
1
1
  import { BearerAuthProvider } from "./auth/BearerAuthProvider.js";
2
2
  import * as core from "./core/index.js";
3
3
  import type * as environments from "./environments.js";
4
+ export type AuthOption = false | core.AuthProvider["getAuthRequest"] | core.AuthProvider | BearerAuthProvider.AuthOptions;
4
5
  export type BaseClientOptions = {
5
6
  environment?: core.Supplier<environments.PredictorSDKEnvironment | string>;
6
7
  /** Specify a custom URL to connect the client to. */
@@ -15,6 +16,8 @@ export type BaseClientOptions = {
15
16
  fetch?: typeof fetch;
16
17
  /** Configure logging for the client. */
17
18
  logging?: core.logging.LogConfig | core.logging.Logger;
19
+ /** Override auth. Pass false to disable, a function returning auth headers, an AuthProvider, or auth options. */
20
+ auth?: AuthOption;
18
21
  } & BearerAuthProvider.AuthOptions;
19
22
  export interface BaseRequestOptions {
20
23
  /** The maximum time to wait for a response in seconds. */
@@ -16,6 +16,21 @@ export function normalizeClientOptions(options) {
16
16
  }
17
17
  export function normalizeClientOptionsWithAuth(options) {
18
18
  const normalized = normalizeClientOptions(options);
19
+ if (options.auth === false) {
20
+ normalized.authProvider = new core.NoOpAuthProvider();
21
+ return normalized;
22
+ }
23
+ if (options.auth != null) {
24
+ if (typeof options.auth === "function") {
25
+ normalized.authProvider = { getAuthRequest: options.auth };
26
+ return normalized;
27
+ }
28
+ if (core.isAuthProvider(options.auth)) {
29
+ normalized.authProvider = options.auth;
30
+ return normalized;
31
+ }
32
+ Object.assign(normalized, options.auth);
33
+ }
19
34
  const normalizedWithNoOpAuthProvider = withNoOpAuthProvider(normalized);
20
35
  normalized.authProvider ??= new BearerAuthProvider(normalizedWithNoOpAuthProvider);
21
36
  return normalized;
package/dist/Client.js CHANGED
@@ -50,7 +50,11 @@ export class PredictorSDKClient {
50
50
  environments.PredictorSDKEnvironment.Production, "v1/matching-markets/sports"),
51
51
  method: "GET",
52
52
  headers: _headers,
53
- queryParameters: { ..._queryParams, ...requestOptions?.queryParams },
53
+ queryString: core.url
54
+ .queryBuilder()
55
+ .addMany(_queryParams)
56
+ .mergeAdditional(requestOptions?.queryParams)
57
+ .build(),
54
58
  timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000,
55
59
  maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
56
60
  abortSignal: requestOptions?.abortSignal,
@@ -162,7 +166,11 @@ export class PredictorSDKClient {
162
166
  environments.PredictorSDKEnvironment.Production, "v1/markets"),
163
167
  method: "GET",
164
168
  headers: _headers,
165
- queryParameters: { ..._queryParams, ...requestOptions?.queryParams },
169
+ queryString: core.url
170
+ .queryBuilder()
171
+ .addMany(_queryParams)
172
+ .mergeAdditional(requestOptions?.queryParams)
173
+ .build(),
166
174
  timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000,
167
175
  maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
168
176
  abortSignal: requestOptions?.abortSignal,
@@ -280,7 +288,11 @@ export class PredictorSDKClient {
280
288
  environments.PredictorSDKEnvironment.Production, "v1/crypto-prices/binance"),
281
289
  method: "GET",
282
290
  headers: _headers,
283
- queryParameters: { ..._queryParams, ...requestOptions?.queryParams },
291
+ queryString: core.url
292
+ .queryBuilder()
293
+ .addMany(_queryParams)
294
+ .mergeAdditional(requestOptions?.queryParams)
295
+ .build(),
284
296
  timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1000,
285
297
  maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
286
298
  abortSignal: requestOptions?.abortSignal,
@@ -5,3 +5,4 @@ export interface AuthProvider {
5
5
  endpointMetadata?: EndpointMetadata;
6
6
  }): Promise<AuthRequest>;
7
7
  }
8
+ export declare function isAuthProvider(value: unknown): value is AuthProvider;
@@ -1 +1,6 @@
1
- export {};
1
+ export function isAuthProvider(value) {
2
+ return (typeof value === "object" &&
3
+ value !== null &&
4
+ "getAuthRequest" in value &&
5
+ typeof value.getAuthRequest === "function");
6
+ }
@@ -1,4 +1,4 @@
1
- export type { AuthProvider } from "./AuthProvider.js";
1
+ export { type AuthProvider, isAuthProvider } from "./AuthProvider.js";
2
2
  export type { AuthRequest } from "./AuthRequest.js";
3
3
  export { BasicAuth } from "./BasicAuth.js";
4
4
  export { BearerToken } from "./BearerToken.js";
@@ -1,3 +1,4 @@
1
+ export { isAuthProvider } from "./AuthProvider.js";
1
2
  export { BasicAuth } from "./BasicAuth.js";
2
3
  export { BearerToken } from "./BearerToken.js";
3
4
  export { NoOpAuthProvider } from "./NoOpAuthProvider.js";
@@ -8,7 +8,13 @@ export declare namespace Fetcher {
8
8
  method: string;
9
9
  contentType?: string;
10
10
  headers?: Record<string, unknown>;
11
+ /**
12
+ * @deprecated Prefer `queryString` (produced by `core.url.queryBuilder()`).
13
+ * Retained for backwards compatibility with custom fetchers and callers that
14
+ * still construct request args with a query-parameter object.
15
+ */
11
16
  queryParameters?: Record<string, unknown>;
17
+ queryString?: string;
12
18
  body?: unknown;
13
19
  timeoutMs?: number;
14
20
  maxRetries?: number;
@@ -62,16 +62,11 @@ const SENSITIVE_QUERY_PARAMS = new Set([
62
62
  ]);
63
63
  function redactQueryParameters(queryParameters) {
64
64
  if (queryParameters == null) {
65
- return queryParameters;
65
+ return undefined;
66
66
  }
67
67
  const redacted = {};
68
68
  for (const [key, value] of Object.entries(queryParameters)) {
69
- if (SENSITIVE_QUERY_PARAMS.has(key.toLowerCase())) {
70
- redacted[key] = "[REDACTED]";
71
- }
72
- else {
73
- redacted[key] = value;
74
- }
69
+ redacted[key] = SENSITIVE_QUERY_PARAMS.has(key.toLowerCase()) ? "[REDACTED]" : value;
75
70
  }
76
71
  return redacted;
77
72
  }
@@ -168,7 +163,13 @@ async function getHeaders(args) {
168
163
  return newHeaders;
169
164
  }
170
165
  export async function fetcherImpl(args) {
171
- const url = createRequestUrl(args.url, args.queryParameters);
166
+ let url = args.url;
167
+ if (args.queryString != null && args.queryString.length > 0) {
168
+ url = `${url}?${args.queryString}`;
169
+ }
170
+ else {
171
+ url = createRequestUrl(args.url, args.queryParameters);
172
+ }
172
173
  const requestBody = await getRequestBody({
173
174
  body: args.body,
174
175
  type: args.requestType ?? "other",
@@ -2,6 +2,9 @@ const INITIAL_RETRY_DELAY = 1000; // in milliseconds
2
2
  const MAX_RETRY_DELAY = 60000; // in milliseconds
3
3
  const DEFAULT_MAX_RETRIES = 2;
4
4
  const JITTER_FACTOR = 0.2; // 20% random jitter
5
+ function isRetryableStatusCode(statusCode) {
6
+ return [408, 429].includes(statusCode) || statusCode >= 500;
7
+ }
5
8
  function addPositiveJitter(delay) {
6
9
  const jitterMultiplier = 1 + Math.random() * JITTER_FACTOR;
7
10
  return delay * jitterMultiplier;
@@ -40,7 +43,7 @@ function getRetryDelayFromHeaders(response, retryAttempt) {
40
43
  export async function requestWithRetries(requestFn, maxRetries = DEFAULT_MAX_RETRIES) {
41
44
  let response = await requestFn();
42
45
  for (let i = 0; i < maxRetries; ++i) {
43
- if ([408, 429].includes(response.status) || response.status >= 500) {
46
+ if (isRetryableStatusCode(response.status)) {
44
47
  const delay = getRetryDelayFromHeaders(response, i);
45
48
  await new Promise((resolve) => setTimeout(resolve, delay));
46
49
  response = await requestFn();
@@ -1,2 +1,3 @@
1
1
  import { type Schema } from "../../Schema.js";
2
2
  export declare function enum_<U extends string, E extends U[]>(values: E): Schema<E[number], E[number]>;
3
+ export declare function forwardCompatibleEnum_<U extends string, E extends U[]>(values: E): Schema<E[number], string>;
@@ -33,3 +33,9 @@ export function enum_(values) {
33
33
  });
34
34
  return schemaCreator();
35
35
  }
36
+ export function forwardCompatibleEnum_(values) {
37
+ return enum_(values).transform({
38
+ transform: (val) => val,
39
+ untransform: (val) => val,
40
+ });
41
+ }
@@ -1 +1 @@
1
- export { enum_ } from "./enum.js";
1
+ export { enum_, forwardCompatibleEnum_ } from "./enum.js";
@@ -1 +1 @@
1
- export { enum_ } from "./enum.js";
1
+ export { enum_, forwardCompatibleEnum_ } from "./enum.js";
@@ -4,6 +4,7 @@ export class JsonError extends Error {
4
4
  constructor(errors) {
5
5
  super(errors.map(stringifyValidationError).join("; "));
6
6
  this.errors = errors;
7
- Object.setPrototypeOf(this, JsonError.prototype);
7
+ Object.setPrototypeOf(this, new.target.prototype);
8
+ this.name = this.constructor.name;
8
9
  }
9
10
  }
@@ -4,6 +4,7 @@ export class ParseError extends Error {
4
4
  constructor(errors) {
5
5
  super(errors.map(stringifyValidationError).join("; "));
6
6
  this.errors = errors;
7
- Object.setPrototypeOf(this, ParseError.prototype);
7
+ Object.setPrototypeOf(this, new.target.prototype);
8
+ this.name = this.constructor.name;
8
9
  }
9
10
  }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Creates a fluent builder for constructing URL query strings.
3
+ *
4
+ * Each `.add()` call serializes its value immediately (like C#'s builder),
5
+ * so no format tracking is needed — the style is applied at add-time.
6
+ *
7
+ * Usage (generated code):
8
+ *
9
+ * const qs = core.url.queryBuilder()
10
+ * .add("limit", limit)
11
+ * .add("tags", tags, { style: "comma" }) // explode: false
12
+ * .mergeAdditional(requestOptions?.queryParams)
13
+ * .build();
14
+ */
15
+ export declare function queryBuilder(): QueryStringBuilder;
16
+ declare class QueryStringBuilder {
17
+ private parts;
18
+ /**
19
+ * Adds a query parameter, serializing it immediately.
20
+ *
21
+ * By default arrays use "repeat" format (`key=a&key=b`).
22
+ * Pass `{ style: "comma" }` for OpenAPI `explode: false` parameters
23
+ * to get comma-separated values (`key=a,b,c`).
24
+ *
25
+ * Null / undefined values are silently skipped.
26
+ */
27
+ add(key: string, value: unknown, options?: {
28
+ style?: "comma";
29
+ }): this;
30
+ /**
31
+ * Adds multiple query parameters at once from a record.
32
+ * All parameters use the default "repeat" array format.
33
+ * Null / undefined values are silently skipped.
34
+ */
35
+ addMany(params: Record<string, unknown>): this;
36
+ /**
37
+ * Merges additional query parameters supplied at call-time via
38
+ * `requestOptions.queryParams`. Overrides existing keys (last-write-wins).
39
+ */
40
+ mergeAdditional(additionalParams?: Record<string, unknown>): this;
41
+ /**
42
+ * Returns the assembled query string (without the leading `?`).
43
+ * Returns an empty string when no parameters were added.
44
+ */
45
+ build(): string;
46
+ }
47
+ export {};
@@ -0,0 +1,78 @@
1
+ import { toQueryString } from "./qs.js";
2
+ /**
3
+ * Creates a fluent builder for constructing URL query strings.
4
+ *
5
+ * Each `.add()` call serializes its value immediately (like C#'s builder),
6
+ * so no format tracking is needed — the style is applied at add-time.
7
+ *
8
+ * Usage (generated code):
9
+ *
10
+ * const qs = core.url.queryBuilder()
11
+ * .add("limit", limit)
12
+ * .add("tags", tags, { style: "comma" }) // explode: false
13
+ * .mergeAdditional(requestOptions?.queryParams)
14
+ * .build();
15
+ */
16
+ export function queryBuilder() {
17
+ return new QueryStringBuilder();
18
+ }
19
+ class QueryStringBuilder {
20
+ parts = new Map();
21
+ /**
22
+ * Adds a query parameter, serializing it immediately.
23
+ *
24
+ * By default arrays use "repeat" format (`key=a&key=b`).
25
+ * Pass `{ style: "comma" }` for OpenAPI `explode: false` parameters
26
+ * to get comma-separated values (`key=a,b,c`).
27
+ *
28
+ * Null / undefined values are silently skipped.
29
+ */
30
+ add(key, value, options) {
31
+ if (value === undefined || value === null) {
32
+ return this;
33
+ }
34
+ const serialized = toQueryString({ [key]: value }, { arrayFormat: options?.style === "comma" ? "comma" : "repeat" });
35
+ if (serialized.length > 0) {
36
+ this.parts.set(key, serialized);
37
+ }
38
+ return this;
39
+ }
40
+ /**
41
+ * Adds multiple query parameters at once from a record.
42
+ * All parameters use the default "repeat" array format.
43
+ * Null / undefined values are silently skipped.
44
+ */
45
+ addMany(params) {
46
+ if (params != null) {
47
+ for (const [key, value] of Object.entries(params)) {
48
+ this.add(key, value);
49
+ }
50
+ }
51
+ return this;
52
+ }
53
+ /**
54
+ * Merges additional query parameters supplied at call-time via
55
+ * `requestOptions.queryParams`. Overrides existing keys (last-write-wins).
56
+ */
57
+ mergeAdditional(additionalParams) {
58
+ if (additionalParams != null) {
59
+ for (const [key, value] of Object.entries(additionalParams)) {
60
+ if (value === undefined || value === null) {
61
+ continue;
62
+ }
63
+ const serialized = toQueryString({ [key]: value }, { arrayFormat: "repeat" });
64
+ if (serialized.length > 0) {
65
+ this.parts.set(key, serialized);
66
+ }
67
+ }
68
+ }
69
+ return this;
70
+ }
71
+ /**
72
+ * Returns the assembled query string (without the leading `?`).
73
+ * Returns an empty string when no parameters were added.
74
+ */
75
+ build() {
76
+ return [...this.parts.values()].join("&");
77
+ }
78
+ }
@@ -1,3 +1,4 @@
1
1
  export { encodePathParam } from "./encodePathParam.js";
2
2
  export { join } from "./join.js";
3
+ export { queryBuilder } from "./QueryStringBuilder.js";
3
4
  export { toQueryString } from "./qs.js";
@@ -1,3 +1,4 @@
1
1
  export { encodePathParam } from "./encodePathParam.js";
2
2
  export { join } from "./join.js";
3
+ export { queryBuilder } from "./QueryStringBuilder.js";
3
4
  export { toQueryString } from "./qs.js";
@@ -1,5 +1,6 @@
1
+ type ArrayFormat = "indices" | "repeat" | "comma";
1
2
  interface QueryStringOptions {
2
- arrayFormat?: "indices" | "repeat";
3
+ arrayFormat?: ArrayFormat;
3
4
  encode?: boolean;
4
5
  }
5
6
  export declare function toQueryString(obj: unknown, options?: QueryStringOptions): string;
@@ -23,19 +23,31 @@ function stringifyObject(obj, prefix = "", options) {
23
23
  if (value.length === 0) {
24
24
  continue;
25
25
  }
26
- for (let i = 0; i < value.length; i++) {
27
- const item = value[i];
28
- if (item === undefined) {
29
- continue;
26
+ const effectiveFormat = options.arrayFormat;
27
+ if (effectiveFormat === "comma") {
28
+ const encodedKey = options.encode ? encodeURIComponent(fullKey) : fullKey;
29
+ const encodedValues = value
30
+ .filter((item) => item !== undefined && item !== null)
31
+ .map((item) => encodeValue(item, options.encode));
32
+ if (encodedValues.length > 0) {
33
+ parts.push(`${encodedKey}=${encodedValues.join(",")}`);
30
34
  }
31
- if (typeof item === "object" && !Array.isArray(item) && item !== null) {
32
- const arrayKey = options.arrayFormat === "indices" ? `${fullKey}[${i}]` : fullKey;
33
- parts.push(...stringifyObject(item, arrayKey, options));
34
- }
35
- else {
36
- const arrayKey = options.arrayFormat === "indices" ? `${fullKey}[${i}]` : fullKey;
37
- const encodedKey = options.encode ? encodeURIComponent(arrayKey) : arrayKey;
38
- parts.push(`${encodedKey}=${encodeValue(item, options.encode)}`);
35
+ }
36
+ else {
37
+ for (let i = 0; i < value.length; i++) {
38
+ const item = value[i];
39
+ if (item === undefined) {
40
+ continue;
41
+ }
42
+ if (typeof item === "object" && !Array.isArray(item) && item !== null) {
43
+ const arrayKey = effectiveFormat === "indices" ? `${fullKey}[${i}]` : fullKey;
44
+ parts.push(...stringifyObject(item, arrayKey, options));
45
+ }
46
+ else {
47
+ const arrayKey = effectiveFormat === "indices" ? `${fullKey}[${i}]` : fullKey;
48
+ const encodedKey = options.encode ? encodeURIComponent(arrayKey) : arrayKey;
49
+ parts.push(`${encodedKey}=${encodeValue(item, options.encode)}`);
50
+ }
39
51
  }
40
52
  }
41
53
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@predictorsdk/client",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "The official TypeScript/JavaScript client for the PredictorSDK matching markets API",
5
5
  "license": "MIT",
6
6
  "keywords": [