@playbasis-ai/qwikcard-sdk 2.3.14 → 2.3.16
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/CHANGELOG.md +12 -0
- package/dist/PlaybasisProvider.d.ts +3 -1
- package/dist/PlaybasisProvider.d.ts.map +1 -1
- package/dist/PlaybasisProvider.js +8 -2
- package/dist/QwikCardApp.d.ts +2 -0
- package/dist/QwikCardApp.d.ts.map +1 -1
- package/dist/api/client.d.ts +4 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +65 -38
- package/dist/web/widgetAssets.d.ts +2 -2
- package/dist/web/widgetAssets.d.ts.map +1 -1
- package/dist/web/widgetAssets.js +3 -3
- package/package.json +1 -1
- package/src/PlaybasisProvider.tsx +13 -2
- package/src/QwikCardApp.tsx +2 -0
- package/src/api/client.ts +70 -39
- package/src/web/widgetAssets.ts +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -67,6 +67,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
67
67
|
|
|
68
68
|
- Fallback badge image mapping for Qwik slugs when API omits image URLs.
|
|
69
69
|
|
|
70
|
+
## [2.3.15] - 2026-01-30
|
|
71
|
+
|
|
72
|
+
### Added
|
|
73
|
+
|
|
74
|
+
- Lightweight request retries/timeouts with opt-in configuration for smoother client UX.
|
|
75
|
+
|
|
76
|
+
## [2.3.16] - 2026-01-30
|
|
77
|
+
|
|
78
|
+
### Changed
|
|
79
|
+
|
|
80
|
+
- Improved empty-state guidance and skeletons in dashboard quest/badge/leaderboard panels.
|
|
81
|
+
|
|
70
82
|
## [2.3.11] - 2026-01-29
|
|
71
83
|
|
|
72
84
|
### Changed
|
|
@@ -12,8 +12,10 @@ interface PlaybasisProviderProps {
|
|
|
12
12
|
tenantId?: string;
|
|
13
13
|
playerId?: string;
|
|
14
14
|
baseUrl?: string;
|
|
15
|
+
requestTimeoutMs?: number;
|
|
16
|
+
requestRetries?: number;
|
|
15
17
|
}
|
|
16
|
-
export declare function PlaybasisProvider({ children, apiKey, tenantId, playerId, baseUrl, }: PlaybasisProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
export declare function PlaybasisProvider({ children, apiKey, tenantId, playerId, baseUrl, requestTimeoutMs, requestRetries, }: PlaybasisProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
17
19
|
export declare function usePlaybasis(): PlaybasisContextValue;
|
|
18
20
|
export default PlaybasisProvider;
|
|
19
21
|
//# sourceMappingURL=PlaybasisProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PlaybasisProvider.d.ts","sourceRoot":"","sources":["../src/PlaybasisProvider.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAO/C,UAAU,qBAAqB;IAC7B,MAAM,EAAE,eAAe,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB;AAQD,UAAU,sBAAsB;IAC9B,QAAQ,EAAE,SAAS,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"PlaybasisProvider.d.ts","sourceRoot":"","sources":["../src/PlaybasisProvider.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAO/C,UAAU,qBAAqB;IAC7B,MAAM,EAAE,eAAe,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB;AAQD,UAAU,sBAAsB;IAC9B,QAAQ,EAAE,SAAS,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAiBD,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,gBAAgB,EAChB,cAAc,GACf,EAAE,sBAAsB,2CA+BxB;AAMD,wBAAgB,YAAY,IAAI,qBAAqB,CAMpD;AAED,eAAe,iBAAiB,CAAC"}
|
|
@@ -17,10 +17,16 @@ const resolveBaseUrl = (tenantId, baseUrl) => {
|
|
|
17
17
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
18
18
|
// Provider Component
|
|
19
19
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
20
|
-
export function PlaybasisProvider({ children, apiKey, tenantId, playerId, baseUrl, }) {
|
|
20
|
+
export function PlaybasisProvider({ children, apiKey, tenantId, playerId, baseUrl, requestTimeoutMs, requestRetries, }) {
|
|
21
21
|
const resolvedTenantId = tenantId ?? (baseUrl && baseUrl.includes('production') ? 'qwik-prod' : 'qwikcard');
|
|
22
22
|
const resolvedBaseUrl = resolveBaseUrl(resolvedTenantId, baseUrl);
|
|
23
|
-
const client = useMemo(() => new PlaybasisClient({
|
|
23
|
+
const client = useMemo(() => new PlaybasisClient({
|
|
24
|
+
apiKey,
|
|
25
|
+
tenantId: resolvedTenantId,
|
|
26
|
+
baseUrl: resolvedBaseUrl,
|
|
27
|
+
timeoutMs: requestTimeoutMs,
|
|
28
|
+
retries: requestRetries,
|
|
29
|
+
}), [apiKey, resolvedTenantId, resolvedBaseUrl, requestTimeoutMs, requestRetries]);
|
|
24
30
|
const value = useMemo(() => ({
|
|
25
31
|
client,
|
|
26
32
|
tenantId: resolvedTenantId,
|
package/dist/QwikCardApp.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export interface QwikCardAppProps {
|
|
|
4
4
|
playerId: string;
|
|
5
5
|
baseUrl?: string;
|
|
6
6
|
leaderboardId?: string;
|
|
7
|
+
requestTimeoutMs?: number;
|
|
8
|
+
requestRetries?: number;
|
|
7
9
|
}
|
|
8
10
|
export declare function QwikCardApp(props: QwikCardAppProps): import("react/jsx-runtime").JSX.Element;
|
|
9
11
|
//# sourceMappingURL=QwikCardApp.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QwikCardApp.d.ts","sourceRoot":"","sources":["../src/QwikCardApp.tsx"],"names":[],"mappings":"AAUA,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"QwikCardApp.d.ts","sourceRoot":"","sources":["../src/QwikCardApp.tsx"],"names":[],"mappings":"AAUA,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAwRD,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,2CAMlD"}
|
package/dist/api/client.d.ts
CHANGED
|
@@ -4,12 +4,16 @@ interface ClientConfig {
|
|
|
4
4
|
tenantId: string;
|
|
5
5
|
baseUrl?: string;
|
|
6
6
|
fetchImpl?: typeof fetch;
|
|
7
|
+
timeoutMs?: number;
|
|
8
|
+
retries?: number;
|
|
7
9
|
}
|
|
8
10
|
export declare class PlaybasisClient {
|
|
9
11
|
private readonly baseUrl;
|
|
10
12
|
private readonly fetchImpl;
|
|
11
13
|
private readonly apiKey;
|
|
12
14
|
private readonly tenantId;
|
|
15
|
+
private readonly timeoutMs;
|
|
16
|
+
private readonly retries;
|
|
13
17
|
constructor(config: ClientConfig);
|
|
14
18
|
/**
|
|
15
19
|
* Generate a unique ID for idempotency keys.
|
package/dist/api/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,KAAK,EACL,gBAAgB,EAChB,MAAM,EACN,UAAU,EACV,YAAY,EACZ,KAAK,EACL,gBAAgB,EAChB,MAAM,EACN,eAAe,EAChB,MAAM,UAAU,CAAC;AAMlB,UAAU,YAAY;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}
|
package/dist/api/client.js
CHANGED
|
@@ -8,6 +8,8 @@ export class PlaybasisClient {
|
|
|
8
8
|
this.baseUrl = (config.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, '');
|
|
9
9
|
this.fetchImpl = config.fetchImpl ?? fetch;
|
|
10
10
|
this.apiKey = config.apiKey;
|
|
11
|
+
this.timeoutMs = config.timeoutMs ?? 10000;
|
|
12
|
+
this.retries = config.retries ?? 2;
|
|
11
13
|
}
|
|
12
14
|
/**
|
|
13
15
|
* Generate a unique ID for idempotency keys.
|
|
@@ -22,49 +24,74 @@ export class PlaybasisClient {
|
|
|
22
24
|
}
|
|
23
25
|
async request(method, path, options) {
|
|
24
26
|
const url = `${this.baseUrl}${path.startsWith('/') ? '' : '/'}${path}`;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
body: options?.body !== undefined ? JSON.stringify(options.body) : undefined,
|
|
39
|
-
};
|
|
40
|
-
const response = await this.fetchImpl(url, init);
|
|
41
|
-
const text = await response.text();
|
|
42
|
-
// Safely parse JSON - server may return non-JSON (e.g., HTML error page)
|
|
43
|
-
let json;
|
|
44
|
-
if (text) {
|
|
27
|
+
const attemptRequest = async () => {
|
|
28
|
+
const headers = {
|
|
29
|
+
'Ocp-Apim-Subscription-Key': this.apiKey,
|
|
30
|
+
'X-Tenant-ID': this.tenantId,
|
|
31
|
+
...options?.headers,
|
|
32
|
+
};
|
|
33
|
+
if (options?.body !== undefined) {
|
|
34
|
+
headers['Content-Type'] = headers['Content-Type'] ?? 'application/json';
|
|
35
|
+
}
|
|
36
|
+
const controller = typeof AbortController !== 'undefined' ? new AbortController() : null;
|
|
37
|
+
const timeoutId = this.timeoutMs
|
|
38
|
+
? setTimeout(() => controller?.abort(), this.timeoutMs)
|
|
39
|
+
: null;
|
|
45
40
|
try {
|
|
46
|
-
|
|
41
|
+
const init = {
|
|
42
|
+
method,
|
|
43
|
+
headers,
|
|
44
|
+
body: options?.body !== undefined ? JSON.stringify(options.body) : undefined,
|
|
45
|
+
signal: controller?.signal,
|
|
46
|
+
};
|
|
47
|
+
const response = await this.fetchImpl(url, init);
|
|
48
|
+
const text = await response.text();
|
|
49
|
+
let json;
|
|
50
|
+
if (text) {
|
|
51
|
+
try {
|
|
52
|
+
json = JSON.parse(text);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
json = undefined;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const getErrorMessage = (value) => {
|
|
59
|
+
if (!value || typeof value !== 'object')
|
|
60
|
+
return undefined;
|
|
61
|
+
if (!('message' in value))
|
|
62
|
+
return undefined;
|
|
63
|
+
const messageValue = value.message;
|
|
64
|
+
return typeof messageValue === 'string' ? messageValue : undefined;
|
|
65
|
+
};
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
const errorMessage = getErrorMessage(json) ||
|
|
68
|
+
(text && !json ? text.slice(0, 200) : null) ||
|
|
69
|
+
`Request failed: ${response.status} ${response.statusText}`;
|
|
70
|
+
const error = new Error(String(errorMessage));
|
|
71
|
+
error.status = response.status;
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
return json;
|
|
47
75
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
76
|
+
finally {
|
|
77
|
+
if (timeoutId)
|
|
78
|
+
clearTimeout(timeoutId);
|
|
51
79
|
}
|
|
52
|
-
}
|
|
53
|
-
const getErrorMessage = (value) => {
|
|
54
|
-
if (!value || typeof value !== 'object')
|
|
55
|
-
return undefined;
|
|
56
|
-
if (!('message' in value))
|
|
57
|
-
return undefined;
|
|
58
|
-
const messageValue = value.message;
|
|
59
|
-
return typeof messageValue === 'string' ? messageValue : undefined;
|
|
60
80
|
};
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
81
|
+
for (let attempt = 0; attempt <= this.retries; attempt += 1) {
|
|
82
|
+
try {
|
|
83
|
+
return await attemptRequest();
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
const status = error.status;
|
|
87
|
+
const shouldRetry = attempt < this.retries &&
|
|
88
|
+
(status === undefined || status === 429 || (status >= 500 && status <= 599));
|
|
89
|
+
if (!shouldRetry)
|
|
90
|
+
throw error;
|
|
91
|
+
await new Promise((resolve) => setTimeout(resolve, 400 * (attempt + 1)));
|
|
92
|
+
}
|
|
66
93
|
}
|
|
67
|
-
|
|
94
|
+
throw new Error('Request failed after retries');
|
|
68
95
|
}
|
|
69
96
|
// ─────────────────────────────────────────────────────────────────────────
|
|
70
97
|
// Players
|