@proofchain/sdk 2.23.0 → 3.0.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.
- package/dist/index.d.mts +30 -10
- package/dist/index.d.ts +30 -10
- package/dist/index.js +152 -22
- package/dist/index.mjs +152 -22
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -16,6 +16,7 @@ declare class HttpClient {
|
|
|
16
16
|
private baseUrl;
|
|
17
17
|
private timeout;
|
|
18
18
|
private maxRetries;
|
|
19
|
+
private readonly inflight;
|
|
19
20
|
constructor(options: HttpClientOptions);
|
|
20
21
|
private getHeaders;
|
|
21
22
|
private handleResponse;
|
|
@@ -2238,7 +2239,7 @@ interface QuestStep {
|
|
|
2238
2239
|
name: string;
|
|
2239
2240
|
description?: string;
|
|
2240
2241
|
order: number;
|
|
2241
|
-
step_type: '
|
|
2242
|
+
step_type: 'event_count' | 'event_match' | 'unique_values' | 'cumulative' | 'streak' | 'manual' | 'threshold' | 'recency' | 'time_windowed_count' | 'wallet_holds' | 'milestone';
|
|
2242
2243
|
event_type?: string;
|
|
2243
2244
|
event_types?: string[];
|
|
2244
2245
|
criteria?: Record<string, any>;
|
|
@@ -2354,7 +2355,7 @@ interface CreateQuestStepRequest {
|
|
|
2354
2355
|
name: string;
|
|
2355
2356
|
description?: string;
|
|
2356
2357
|
order?: number;
|
|
2357
|
-
step_type?: '
|
|
2358
|
+
step_type?: 'event_count' | 'event_match' | 'unique_values' | 'cumulative' | 'streak' | 'manual' | 'threshold' | 'recency' | 'time_windowed_count' | 'wallet_holds' | 'milestone';
|
|
2358
2359
|
event_type?: string;
|
|
2359
2360
|
event_types?: string[];
|
|
2360
2361
|
criteria?: Record<string, any>;
|
|
@@ -2394,9 +2395,10 @@ declare class QuestsClient {
|
|
|
2394
2395
|
category?: string;
|
|
2395
2396
|
}): Promise<QuestWithProgress[]>;
|
|
2396
2397
|
/**
|
|
2397
|
-
*
|
|
2398
|
+
* REMOVED: getBySlug() — the server never implemented GET /quests/slug/{slug}.
|
|
2399
|
+
* There is no slug-based lookup route in the API. Use list() with a status/category
|
|
2400
|
+
* filter and find the quest client-side, or use get() with the quest ID.
|
|
2398
2401
|
*/
|
|
2399
|
-
getBySlug(slug: string): Promise<Quest>;
|
|
2400
2402
|
/**
|
|
2401
2403
|
* Create a quest
|
|
2402
2404
|
*/
|
|
@@ -2422,9 +2424,9 @@ declare class QuestsClient {
|
|
|
2422
2424
|
*/
|
|
2423
2425
|
pause(questId: string): Promise<Quest>;
|
|
2424
2426
|
/**
|
|
2425
|
-
*
|
|
2427
|
+
* REMOVED: archive() — the server never implemented POST /quests/{id}/archive.
|
|
2428
|
+
* To archive a quest, use update(questId, { status: 'archived' }) via PUT /quests/{id}.
|
|
2426
2429
|
*/
|
|
2427
|
-
archive(questId: string): Promise<Quest>;
|
|
2428
2430
|
/**
|
|
2429
2431
|
* Get quest with user progress
|
|
2430
2432
|
* Fetches the quest and user's progress separately and combines them
|
|
@@ -2469,19 +2471,37 @@ declare class QuestsClient {
|
|
|
2469
2471
|
*/
|
|
2470
2472
|
claimReward(questId: string, userId: string): Promise<ClaimRewardResult>;
|
|
2471
2473
|
/**
|
|
2472
|
-
* Add a step to a quest
|
|
2474
|
+
* Add a step to a quest.
|
|
2475
|
+
*
|
|
2476
|
+
* The server has no dedicated step-create endpoint — steps are managed via the
|
|
2477
|
+
* full-replace PUT /quests/{id}. This method fetches the quest, appends the new
|
|
2478
|
+
* step, and issues a full update. The returned QuestStep is synthesised from the
|
|
2479
|
+
* updated quest's steps array (matched by order or last position).
|
|
2473
2480
|
*/
|
|
2474
2481
|
addStep(questId: string, step: CreateQuestStepRequest): Promise<QuestStep>;
|
|
2475
2482
|
/**
|
|
2476
|
-
* Update a step
|
|
2483
|
+
* Update a step by step ID.
|
|
2484
|
+
*
|
|
2485
|
+
* The server has no dedicated step-update endpoint — steps are managed via the
|
|
2486
|
+
* full-replace PUT /quests/{id}. This method fetches the quest, replaces the
|
|
2487
|
+
* matching step, and issues a full update.
|
|
2477
2488
|
*/
|
|
2478
2489
|
updateStep(questId: string, stepId: string, data: Partial<CreateQuestStepRequest>): Promise<QuestStep>;
|
|
2479
2490
|
/**
|
|
2480
|
-
* Delete a step
|
|
2491
|
+
* Delete a step by step ID.
|
|
2492
|
+
*
|
|
2493
|
+
* The server has no dedicated step-delete endpoint — steps are managed via the
|
|
2494
|
+
* full-replace PUT /quests/{id}. This method fetches the quest, filters out the
|
|
2495
|
+
* target step, and issues a full update.
|
|
2481
2496
|
*/
|
|
2482
2497
|
deleteStep(questId: string, stepId: string): Promise<void>;
|
|
2483
2498
|
/**
|
|
2484
|
-
* Reorder steps
|
|
2499
|
+
* Reorder steps by providing step IDs in the desired order.
|
|
2500
|
+
*
|
|
2501
|
+
* The server has no dedicated reorder endpoint — steps are managed via the
|
|
2502
|
+
* full-replace PUT /quests/{id}. This method fetches the quest, reassigns
|
|
2503
|
+
* the `order` field according to the given stepIds sequence, and issues a
|
|
2504
|
+
* full update.
|
|
2485
2505
|
*/
|
|
2486
2506
|
reorderSteps(questId: string, stepIds: string[]): Promise<Quest>;
|
|
2487
2507
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ declare class HttpClient {
|
|
|
16
16
|
private baseUrl;
|
|
17
17
|
private timeout;
|
|
18
18
|
private maxRetries;
|
|
19
|
+
private readonly inflight;
|
|
19
20
|
constructor(options: HttpClientOptions);
|
|
20
21
|
private getHeaders;
|
|
21
22
|
private handleResponse;
|
|
@@ -2238,7 +2239,7 @@ interface QuestStep {
|
|
|
2238
2239
|
name: string;
|
|
2239
2240
|
description?: string;
|
|
2240
2241
|
order: number;
|
|
2241
|
-
step_type: '
|
|
2242
|
+
step_type: 'event_count' | 'event_match' | 'unique_values' | 'cumulative' | 'streak' | 'manual' | 'threshold' | 'recency' | 'time_windowed_count' | 'wallet_holds' | 'milestone';
|
|
2242
2243
|
event_type?: string;
|
|
2243
2244
|
event_types?: string[];
|
|
2244
2245
|
criteria?: Record<string, any>;
|
|
@@ -2354,7 +2355,7 @@ interface CreateQuestStepRequest {
|
|
|
2354
2355
|
name: string;
|
|
2355
2356
|
description?: string;
|
|
2356
2357
|
order?: number;
|
|
2357
|
-
step_type?: '
|
|
2358
|
+
step_type?: 'event_count' | 'event_match' | 'unique_values' | 'cumulative' | 'streak' | 'manual' | 'threshold' | 'recency' | 'time_windowed_count' | 'wallet_holds' | 'milestone';
|
|
2358
2359
|
event_type?: string;
|
|
2359
2360
|
event_types?: string[];
|
|
2360
2361
|
criteria?: Record<string, any>;
|
|
@@ -2394,9 +2395,10 @@ declare class QuestsClient {
|
|
|
2394
2395
|
category?: string;
|
|
2395
2396
|
}): Promise<QuestWithProgress[]>;
|
|
2396
2397
|
/**
|
|
2397
|
-
*
|
|
2398
|
+
* REMOVED: getBySlug() — the server never implemented GET /quests/slug/{slug}.
|
|
2399
|
+
* There is no slug-based lookup route in the API. Use list() with a status/category
|
|
2400
|
+
* filter and find the quest client-side, or use get() with the quest ID.
|
|
2398
2401
|
*/
|
|
2399
|
-
getBySlug(slug: string): Promise<Quest>;
|
|
2400
2402
|
/**
|
|
2401
2403
|
* Create a quest
|
|
2402
2404
|
*/
|
|
@@ -2422,9 +2424,9 @@ declare class QuestsClient {
|
|
|
2422
2424
|
*/
|
|
2423
2425
|
pause(questId: string): Promise<Quest>;
|
|
2424
2426
|
/**
|
|
2425
|
-
*
|
|
2427
|
+
* REMOVED: archive() — the server never implemented POST /quests/{id}/archive.
|
|
2428
|
+
* To archive a quest, use update(questId, { status: 'archived' }) via PUT /quests/{id}.
|
|
2426
2429
|
*/
|
|
2427
|
-
archive(questId: string): Promise<Quest>;
|
|
2428
2430
|
/**
|
|
2429
2431
|
* Get quest with user progress
|
|
2430
2432
|
* Fetches the quest and user's progress separately and combines them
|
|
@@ -2469,19 +2471,37 @@ declare class QuestsClient {
|
|
|
2469
2471
|
*/
|
|
2470
2472
|
claimReward(questId: string, userId: string): Promise<ClaimRewardResult>;
|
|
2471
2473
|
/**
|
|
2472
|
-
* Add a step to a quest
|
|
2474
|
+
* Add a step to a quest.
|
|
2475
|
+
*
|
|
2476
|
+
* The server has no dedicated step-create endpoint — steps are managed via the
|
|
2477
|
+
* full-replace PUT /quests/{id}. This method fetches the quest, appends the new
|
|
2478
|
+
* step, and issues a full update. The returned QuestStep is synthesised from the
|
|
2479
|
+
* updated quest's steps array (matched by order or last position).
|
|
2473
2480
|
*/
|
|
2474
2481
|
addStep(questId: string, step: CreateQuestStepRequest): Promise<QuestStep>;
|
|
2475
2482
|
/**
|
|
2476
|
-
* Update a step
|
|
2483
|
+
* Update a step by step ID.
|
|
2484
|
+
*
|
|
2485
|
+
* The server has no dedicated step-update endpoint — steps are managed via the
|
|
2486
|
+
* full-replace PUT /quests/{id}. This method fetches the quest, replaces the
|
|
2487
|
+
* matching step, and issues a full update.
|
|
2477
2488
|
*/
|
|
2478
2489
|
updateStep(questId: string, stepId: string, data: Partial<CreateQuestStepRequest>): Promise<QuestStep>;
|
|
2479
2490
|
/**
|
|
2480
|
-
* Delete a step
|
|
2491
|
+
* Delete a step by step ID.
|
|
2492
|
+
*
|
|
2493
|
+
* The server has no dedicated step-delete endpoint — steps are managed via the
|
|
2494
|
+
* full-replace PUT /quests/{id}. This method fetches the quest, filters out the
|
|
2495
|
+
* target step, and issues a full update.
|
|
2481
2496
|
*/
|
|
2482
2497
|
deleteStep(questId: string, stepId: string): Promise<void>;
|
|
2483
2498
|
/**
|
|
2484
|
-
* Reorder steps
|
|
2499
|
+
* Reorder steps by providing step IDs in the desired order.
|
|
2500
|
+
*
|
|
2501
|
+
* The server has no dedicated reorder endpoint — steps are managed via the
|
|
2502
|
+
* full-replace PUT /quests/{id}. This method fetches the quest, reassigns
|
|
2503
|
+
* the `order` field according to the given stepIds sequence, and issues a
|
|
2504
|
+
* full update.
|
|
2485
2505
|
*/
|
|
2486
2506
|
reorderSteps(questId: string, stepIds: string[]): Promise<Quest>;
|
|
2487
2507
|
}
|
package/dist/index.js
CHANGED
|
@@ -125,6 +125,9 @@ var DEFAULT_TIMEOUT = 3e4;
|
|
|
125
125
|
var USER_AGENT = "proofchain-js/0.1.0";
|
|
126
126
|
var HttpClient = class {
|
|
127
127
|
constructor(options) {
|
|
128
|
+
// In-flight GET de-duplication: concurrent identical GETs share one network
|
|
129
|
+
// request (and its response), cutting origin load for chatty UIs/pollers.
|
|
130
|
+
this.inflight = /* @__PURE__ */ new Map();
|
|
128
131
|
this.apiKey = options.apiKey || "";
|
|
129
132
|
this.userToken = options.userToken || "";
|
|
130
133
|
this.tenantId = options.tenantId || "";
|
|
@@ -193,7 +196,8 @@ var HttpClient = class {
|
|
|
193
196
|
clearTimeout(timeoutId);
|
|
194
197
|
if (error instanceof ProofChainError) {
|
|
195
198
|
if (error instanceof RateLimitError && retries < this.maxRetries) {
|
|
196
|
-
const
|
|
199
|
+
const base = Math.min((error.retryAfter || 1) * 1e3, 6e4);
|
|
200
|
+
const delay = base + Math.floor(Math.random() * 1e3);
|
|
197
201
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
198
202
|
return this.fetchWithRetry(url, options, retries + 1);
|
|
199
203
|
}
|
|
@@ -204,7 +208,9 @@ var HttpClient = class {
|
|
|
204
208
|
throw new TimeoutError();
|
|
205
209
|
}
|
|
206
210
|
if (retries < this.maxRetries) {
|
|
207
|
-
|
|
211
|
+
const base = 1e3 * (retries + 1);
|
|
212
|
+
const delay = base + Math.floor(Math.random() * 500);
|
|
213
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
208
214
|
return this.fetchWithRetry(url, options, retries + 1);
|
|
209
215
|
}
|
|
210
216
|
throw new NetworkError(error.message, error);
|
|
@@ -233,6 +239,17 @@ var HttpClient = class {
|
|
|
233
239
|
if (options.body) {
|
|
234
240
|
fetchOptions.body = JSON.stringify(options.body);
|
|
235
241
|
}
|
|
242
|
+
if (method === "GET") {
|
|
243
|
+
const existing = this.inflight.get(url);
|
|
244
|
+
if (existing) {
|
|
245
|
+
return existing;
|
|
246
|
+
}
|
|
247
|
+
const pending = this.fetchWithRetry(url, fetchOptions).finally(() => {
|
|
248
|
+
this.inflight.delete(url);
|
|
249
|
+
});
|
|
250
|
+
this.inflight.set(url, pending);
|
|
251
|
+
return pending;
|
|
252
|
+
}
|
|
236
253
|
return this.fetchWithRetry(url, fetchOptions);
|
|
237
254
|
}
|
|
238
255
|
async requestMultipart(path, formData) {
|
|
@@ -1726,11 +1743,10 @@ var QuestsClient = class {
|
|
|
1726
1743
|
return this.http.get(`/quests/available?${params.toString()}`);
|
|
1727
1744
|
}
|
|
1728
1745
|
/**
|
|
1729
|
-
*
|
|
1746
|
+
* REMOVED: getBySlug() — the server never implemented GET /quests/slug/{slug}.
|
|
1747
|
+
* There is no slug-based lookup route in the API. Use list() with a status/category
|
|
1748
|
+
* filter and find the quest client-side, or use get() with the quest ID.
|
|
1730
1749
|
*/
|
|
1731
|
-
async getBySlug(slug) {
|
|
1732
|
-
return this.http.get(`/quests/slug/${encodeURIComponent(slug)}`);
|
|
1733
|
-
}
|
|
1734
1750
|
/**
|
|
1735
1751
|
* Create a quest
|
|
1736
1752
|
*/
|
|
@@ -1771,11 +1787,9 @@ var QuestsClient = class {
|
|
|
1771
1787
|
return this.http.post(`/quests/${questId}/pause`, {});
|
|
1772
1788
|
}
|
|
1773
1789
|
/**
|
|
1774
|
-
*
|
|
1790
|
+
* REMOVED: archive() — the server never implemented POST /quests/{id}/archive.
|
|
1791
|
+
* To archive a quest, use update(questId, { status: 'archived' }) via PUT /quests/{id}.
|
|
1775
1792
|
*/
|
|
1776
|
-
async archive(questId) {
|
|
1777
|
-
return this.http.post(`/quests/${questId}/archive`, {});
|
|
1778
|
-
}
|
|
1779
1793
|
// ---------------------------------------------------------------------------
|
|
1780
1794
|
// User Progress
|
|
1781
1795
|
// ---------------------------------------------------------------------------
|
|
@@ -1855,28 +1869,144 @@ var QuestsClient = class {
|
|
|
1855
1869
|
// Quest Steps
|
|
1856
1870
|
// ---------------------------------------------------------------------------
|
|
1857
1871
|
/**
|
|
1858
|
-
* Add a step to a quest
|
|
1872
|
+
* Add a step to a quest.
|
|
1873
|
+
*
|
|
1874
|
+
* The server has no dedicated step-create endpoint — steps are managed via the
|
|
1875
|
+
* full-replace PUT /quests/{id}. This method fetches the quest, appends the new
|
|
1876
|
+
* step, and issues a full update. The returned QuestStep is synthesised from the
|
|
1877
|
+
* updated quest's steps array (matched by order or last position).
|
|
1859
1878
|
*/
|
|
1860
1879
|
async addStep(questId, step) {
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1880
|
+
const quest = await this.get(questId);
|
|
1881
|
+
const newOrder = step.order ?? (quest.steps.length > 0 ? Math.max(...quest.steps.map((s) => s.order)) + 1 : 0);
|
|
1882
|
+
const stepsPayload = [
|
|
1883
|
+
...quest.steps.map((s) => ({
|
|
1884
|
+
name: s.name,
|
|
1885
|
+
description: s.description,
|
|
1886
|
+
order: s.order,
|
|
1887
|
+
step_type: s.step_type,
|
|
1888
|
+
event_type: s.event_type,
|
|
1889
|
+
event_types: s.event_types,
|
|
1890
|
+
criteria: s.criteria,
|
|
1891
|
+
required_data_fields: s.required_data_fields,
|
|
1892
|
+
step_points: s.step_points,
|
|
1893
|
+
cta_text: s.cta_text,
|
|
1894
|
+
cta_url: s.cta_url,
|
|
1895
|
+
icon_url: s.icon_url,
|
|
1896
|
+
is_optional: s.is_optional
|
|
1897
|
+
})),
|
|
1898
|
+
{ ...step, order: newOrder }
|
|
1899
|
+
];
|
|
1900
|
+
const updated = await this.update(questId, { steps: stepsPayload });
|
|
1901
|
+
const added = updated.steps.find((s) => s.order === newOrder) ?? updated.steps[updated.steps.length - 1];
|
|
1902
|
+
return added;
|
|
1903
|
+
}
|
|
1904
|
+
/**
|
|
1905
|
+
* Update a step by step ID.
|
|
1906
|
+
*
|
|
1907
|
+
* The server has no dedicated step-update endpoint — steps are managed via the
|
|
1908
|
+
* full-replace PUT /quests/{id}. This method fetches the quest, replaces the
|
|
1909
|
+
* matching step, and issues a full update.
|
|
1865
1910
|
*/
|
|
1866
1911
|
async updateStep(questId, stepId, data) {
|
|
1867
|
-
|
|
1912
|
+
const quest = await this.get(questId);
|
|
1913
|
+
const stepsPayload = quest.steps.map((s) => {
|
|
1914
|
+
if (s.id !== stepId) {
|
|
1915
|
+
return {
|
|
1916
|
+
name: s.name,
|
|
1917
|
+
description: s.description,
|
|
1918
|
+
order: s.order,
|
|
1919
|
+
step_type: s.step_type,
|
|
1920
|
+
event_type: s.event_type,
|
|
1921
|
+
event_types: s.event_types,
|
|
1922
|
+
criteria: s.criteria,
|
|
1923
|
+
required_data_fields: s.required_data_fields,
|
|
1924
|
+
step_points: s.step_points,
|
|
1925
|
+
cta_text: s.cta_text,
|
|
1926
|
+
cta_url: s.cta_url,
|
|
1927
|
+
icon_url: s.icon_url,
|
|
1928
|
+
is_optional: s.is_optional
|
|
1929
|
+
};
|
|
1930
|
+
}
|
|
1931
|
+
return {
|
|
1932
|
+
name: s.name,
|
|
1933
|
+
description: s.description,
|
|
1934
|
+
order: s.order,
|
|
1935
|
+
step_type: s.step_type,
|
|
1936
|
+
event_type: s.event_type,
|
|
1937
|
+
event_types: s.event_types,
|
|
1938
|
+
criteria: s.criteria,
|
|
1939
|
+
required_data_fields: s.required_data_fields,
|
|
1940
|
+
step_points: s.step_points,
|
|
1941
|
+
cta_text: s.cta_text,
|
|
1942
|
+
cta_url: s.cta_url,
|
|
1943
|
+
icon_url: s.icon_url,
|
|
1944
|
+
is_optional: s.is_optional,
|
|
1945
|
+
...data
|
|
1946
|
+
};
|
|
1947
|
+
});
|
|
1948
|
+
const updated = await this.update(questId, { steps: stepsPayload });
|
|
1949
|
+
const found = updated.steps.find((s) => s.id === stepId);
|
|
1950
|
+
if (!found) throw new Error(`Step ${stepId} not found after update`);
|
|
1951
|
+
return found;
|
|
1868
1952
|
}
|
|
1869
1953
|
/**
|
|
1870
|
-
* Delete a step
|
|
1954
|
+
* Delete a step by step ID.
|
|
1955
|
+
*
|
|
1956
|
+
* The server has no dedicated step-delete endpoint — steps are managed via the
|
|
1957
|
+
* full-replace PUT /quests/{id}. This method fetches the quest, filters out the
|
|
1958
|
+
* target step, and issues a full update.
|
|
1871
1959
|
*/
|
|
1872
1960
|
async deleteStep(questId, stepId) {
|
|
1873
|
-
await this.
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1961
|
+
const quest = await this.get(questId);
|
|
1962
|
+
const stepsPayload = quest.steps.filter((s) => s.id !== stepId).map((s) => ({
|
|
1963
|
+
name: s.name,
|
|
1964
|
+
description: s.description,
|
|
1965
|
+
order: s.order,
|
|
1966
|
+
step_type: s.step_type,
|
|
1967
|
+
event_type: s.event_type,
|
|
1968
|
+
event_types: s.event_types,
|
|
1969
|
+
criteria: s.criteria,
|
|
1970
|
+
required_data_fields: s.required_data_fields,
|
|
1971
|
+
step_points: s.step_points,
|
|
1972
|
+
cta_text: s.cta_text,
|
|
1973
|
+
cta_url: s.cta_url,
|
|
1974
|
+
icon_url: s.icon_url,
|
|
1975
|
+
is_optional: s.is_optional
|
|
1976
|
+
}));
|
|
1977
|
+
await this.update(questId, { steps: stepsPayload });
|
|
1978
|
+
}
|
|
1979
|
+
/**
|
|
1980
|
+
* Reorder steps by providing step IDs in the desired order.
|
|
1981
|
+
*
|
|
1982
|
+
* The server has no dedicated reorder endpoint — steps are managed via the
|
|
1983
|
+
* full-replace PUT /quests/{id}. This method fetches the quest, reassigns
|
|
1984
|
+
* the `order` field according to the given stepIds sequence, and issues a
|
|
1985
|
+
* full update.
|
|
1877
1986
|
*/
|
|
1878
1987
|
async reorderSteps(questId, stepIds) {
|
|
1879
|
-
|
|
1988
|
+
const quest = await this.get(questId);
|
|
1989
|
+
const stepMap = new Map(quest.steps.map((s) => [s.id, s]));
|
|
1990
|
+
const stepsPayload = stepIds.map((id, index) => {
|
|
1991
|
+
const s = stepMap.get(id);
|
|
1992
|
+
if (!s) throw new Error(`Step ${id} not found in quest ${questId}`);
|
|
1993
|
+
return {
|
|
1994
|
+
name: s.name,
|
|
1995
|
+
description: s.description,
|
|
1996
|
+
order: index,
|
|
1997
|
+
step_type: s.step_type,
|
|
1998
|
+
event_type: s.event_type,
|
|
1999
|
+
event_types: s.event_types,
|
|
2000
|
+
criteria: s.criteria,
|
|
2001
|
+
required_data_fields: s.required_data_fields,
|
|
2002
|
+
step_points: s.step_points,
|
|
2003
|
+
cta_text: s.cta_text,
|
|
2004
|
+
cta_url: s.cta_url,
|
|
2005
|
+
icon_url: s.icon_url,
|
|
2006
|
+
is_optional: s.is_optional
|
|
2007
|
+
};
|
|
2008
|
+
});
|
|
2009
|
+
return this.update(questId, { steps: stepsPayload });
|
|
1880
2010
|
}
|
|
1881
2011
|
};
|
|
1882
2012
|
|
package/dist/index.mjs
CHANGED
|
@@ -65,6 +65,9 @@ var DEFAULT_TIMEOUT = 3e4;
|
|
|
65
65
|
var USER_AGENT = "proofchain-js/0.1.0";
|
|
66
66
|
var HttpClient = class {
|
|
67
67
|
constructor(options) {
|
|
68
|
+
// In-flight GET de-duplication: concurrent identical GETs share one network
|
|
69
|
+
// request (and its response), cutting origin load for chatty UIs/pollers.
|
|
70
|
+
this.inflight = /* @__PURE__ */ new Map();
|
|
68
71
|
this.apiKey = options.apiKey || "";
|
|
69
72
|
this.userToken = options.userToken || "";
|
|
70
73
|
this.tenantId = options.tenantId || "";
|
|
@@ -133,7 +136,8 @@ var HttpClient = class {
|
|
|
133
136
|
clearTimeout(timeoutId);
|
|
134
137
|
if (error instanceof ProofChainError) {
|
|
135
138
|
if (error instanceof RateLimitError && retries < this.maxRetries) {
|
|
136
|
-
const
|
|
139
|
+
const base = Math.min((error.retryAfter || 1) * 1e3, 6e4);
|
|
140
|
+
const delay = base + Math.floor(Math.random() * 1e3);
|
|
137
141
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
138
142
|
return this.fetchWithRetry(url, options, retries + 1);
|
|
139
143
|
}
|
|
@@ -144,7 +148,9 @@ var HttpClient = class {
|
|
|
144
148
|
throw new TimeoutError();
|
|
145
149
|
}
|
|
146
150
|
if (retries < this.maxRetries) {
|
|
147
|
-
|
|
151
|
+
const base = 1e3 * (retries + 1);
|
|
152
|
+
const delay = base + Math.floor(Math.random() * 500);
|
|
153
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
148
154
|
return this.fetchWithRetry(url, options, retries + 1);
|
|
149
155
|
}
|
|
150
156
|
throw new NetworkError(error.message, error);
|
|
@@ -173,6 +179,17 @@ var HttpClient = class {
|
|
|
173
179
|
if (options.body) {
|
|
174
180
|
fetchOptions.body = JSON.stringify(options.body);
|
|
175
181
|
}
|
|
182
|
+
if (method === "GET") {
|
|
183
|
+
const existing = this.inflight.get(url);
|
|
184
|
+
if (existing) {
|
|
185
|
+
return existing;
|
|
186
|
+
}
|
|
187
|
+
const pending = this.fetchWithRetry(url, fetchOptions).finally(() => {
|
|
188
|
+
this.inflight.delete(url);
|
|
189
|
+
});
|
|
190
|
+
this.inflight.set(url, pending);
|
|
191
|
+
return pending;
|
|
192
|
+
}
|
|
176
193
|
return this.fetchWithRetry(url, fetchOptions);
|
|
177
194
|
}
|
|
178
195
|
async requestMultipart(path, formData) {
|
|
@@ -1666,11 +1683,10 @@ var QuestsClient = class {
|
|
|
1666
1683
|
return this.http.get(`/quests/available?${params.toString()}`);
|
|
1667
1684
|
}
|
|
1668
1685
|
/**
|
|
1669
|
-
*
|
|
1686
|
+
* REMOVED: getBySlug() — the server never implemented GET /quests/slug/{slug}.
|
|
1687
|
+
* There is no slug-based lookup route in the API. Use list() with a status/category
|
|
1688
|
+
* filter and find the quest client-side, or use get() with the quest ID.
|
|
1670
1689
|
*/
|
|
1671
|
-
async getBySlug(slug) {
|
|
1672
|
-
return this.http.get(`/quests/slug/${encodeURIComponent(slug)}`);
|
|
1673
|
-
}
|
|
1674
1690
|
/**
|
|
1675
1691
|
* Create a quest
|
|
1676
1692
|
*/
|
|
@@ -1711,11 +1727,9 @@ var QuestsClient = class {
|
|
|
1711
1727
|
return this.http.post(`/quests/${questId}/pause`, {});
|
|
1712
1728
|
}
|
|
1713
1729
|
/**
|
|
1714
|
-
*
|
|
1730
|
+
* REMOVED: archive() — the server never implemented POST /quests/{id}/archive.
|
|
1731
|
+
* To archive a quest, use update(questId, { status: 'archived' }) via PUT /quests/{id}.
|
|
1715
1732
|
*/
|
|
1716
|
-
async archive(questId) {
|
|
1717
|
-
return this.http.post(`/quests/${questId}/archive`, {});
|
|
1718
|
-
}
|
|
1719
1733
|
// ---------------------------------------------------------------------------
|
|
1720
1734
|
// User Progress
|
|
1721
1735
|
// ---------------------------------------------------------------------------
|
|
@@ -1795,28 +1809,144 @@ var QuestsClient = class {
|
|
|
1795
1809
|
// Quest Steps
|
|
1796
1810
|
// ---------------------------------------------------------------------------
|
|
1797
1811
|
/**
|
|
1798
|
-
* Add a step to a quest
|
|
1812
|
+
* Add a step to a quest.
|
|
1813
|
+
*
|
|
1814
|
+
* The server has no dedicated step-create endpoint — steps are managed via the
|
|
1815
|
+
* full-replace PUT /quests/{id}. This method fetches the quest, appends the new
|
|
1816
|
+
* step, and issues a full update. The returned QuestStep is synthesised from the
|
|
1817
|
+
* updated quest's steps array (matched by order or last position).
|
|
1799
1818
|
*/
|
|
1800
1819
|
async addStep(questId, step) {
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1820
|
+
const quest = await this.get(questId);
|
|
1821
|
+
const newOrder = step.order ?? (quest.steps.length > 0 ? Math.max(...quest.steps.map((s) => s.order)) + 1 : 0);
|
|
1822
|
+
const stepsPayload = [
|
|
1823
|
+
...quest.steps.map((s) => ({
|
|
1824
|
+
name: s.name,
|
|
1825
|
+
description: s.description,
|
|
1826
|
+
order: s.order,
|
|
1827
|
+
step_type: s.step_type,
|
|
1828
|
+
event_type: s.event_type,
|
|
1829
|
+
event_types: s.event_types,
|
|
1830
|
+
criteria: s.criteria,
|
|
1831
|
+
required_data_fields: s.required_data_fields,
|
|
1832
|
+
step_points: s.step_points,
|
|
1833
|
+
cta_text: s.cta_text,
|
|
1834
|
+
cta_url: s.cta_url,
|
|
1835
|
+
icon_url: s.icon_url,
|
|
1836
|
+
is_optional: s.is_optional
|
|
1837
|
+
})),
|
|
1838
|
+
{ ...step, order: newOrder }
|
|
1839
|
+
];
|
|
1840
|
+
const updated = await this.update(questId, { steps: stepsPayload });
|
|
1841
|
+
const added = updated.steps.find((s) => s.order === newOrder) ?? updated.steps[updated.steps.length - 1];
|
|
1842
|
+
return added;
|
|
1843
|
+
}
|
|
1844
|
+
/**
|
|
1845
|
+
* Update a step by step ID.
|
|
1846
|
+
*
|
|
1847
|
+
* The server has no dedicated step-update endpoint — steps are managed via the
|
|
1848
|
+
* full-replace PUT /quests/{id}. This method fetches the quest, replaces the
|
|
1849
|
+
* matching step, and issues a full update.
|
|
1805
1850
|
*/
|
|
1806
1851
|
async updateStep(questId, stepId, data) {
|
|
1807
|
-
|
|
1852
|
+
const quest = await this.get(questId);
|
|
1853
|
+
const stepsPayload = quest.steps.map((s) => {
|
|
1854
|
+
if (s.id !== stepId) {
|
|
1855
|
+
return {
|
|
1856
|
+
name: s.name,
|
|
1857
|
+
description: s.description,
|
|
1858
|
+
order: s.order,
|
|
1859
|
+
step_type: s.step_type,
|
|
1860
|
+
event_type: s.event_type,
|
|
1861
|
+
event_types: s.event_types,
|
|
1862
|
+
criteria: s.criteria,
|
|
1863
|
+
required_data_fields: s.required_data_fields,
|
|
1864
|
+
step_points: s.step_points,
|
|
1865
|
+
cta_text: s.cta_text,
|
|
1866
|
+
cta_url: s.cta_url,
|
|
1867
|
+
icon_url: s.icon_url,
|
|
1868
|
+
is_optional: s.is_optional
|
|
1869
|
+
};
|
|
1870
|
+
}
|
|
1871
|
+
return {
|
|
1872
|
+
name: s.name,
|
|
1873
|
+
description: s.description,
|
|
1874
|
+
order: s.order,
|
|
1875
|
+
step_type: s.step_type,
|
|
1876
|
+
event_type: s.event_type,
|
|
1877
|
+
event_types: s.event_types,
|
|
1878
|
+
criteria: s.criteria,
|
|
1879
|
+
required_data_fields: s.required_data_fields,
|
|
1880
|
+
step_points: s.step_points,
|
|
1881
|
+
cta_text: s.cta_text,
|
|
1882
|
+
cta_url: s.cta_url,
|
|
1883
|
+
icon_url: s.icon_url,
|
|
1884
|
+
is_optional: s.is_optional,
|
|
1885
|
+
...data
|
|
1886
|
+
};
|
|
1887
|
+
});
|
|
1888
|
+
const updated = await this.update(questId, { steps: stepsPayload });
|
|
1889
|
+
const found = updated.steps.find((s) => s.id === stepId);
|
|
1890
|
+
if (!found) throw new Error(`Step ${stepId} not found after update`);
|
|
1891
|
+
return found;
|
|
1808
1892
|
}
|
|
1809
1893
|
/**
|
|
1810
|
-
* Delete a step
|
|
1894
|
+
* Delete a step by step ID.
|
|
1895
|
+
*
|
|
1896
|
+
* The server has no dedicated step-delete endpoint — steps are managed via the
|
|
1897
|
+
* full-replace PUT /quests/{id}. This method fetches the quest, filters out the
|
|
1898
|
+
* target step, and issues a full update.
|
|
1811
1899
|
*/
|
|
1812
1900
|
async deleteStep(questId, stepId) {
|
|
1813
|
-
await this.
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1901
|
+
const quest = await this.get(questId);
|
|
1902
|
+
const stepsPayload = quest.steps.filter((s) => s.id !== stepId).map((s) => ({
|
|
1903
|
+
name: s.name,
|
|
1904
|
+
description: s.description,
|
|
1905
|
+
order: s.order,
|
|
1906
|
+
step_type: s.step_type,
|
|
1907
|
+
event_type: s.event_type,
|
|
1908
|
+
event_types: s.event_types,
|
|
1909
|
+
criteria: s.criteria,
|
|
1910
|
+
required_data_fields: s.required_data_fields,
|
|
1911
|
+
step_points: s.step_points,
|
|
1912
|
+
cta_text: s.cta_text,
|
|
1913
|
+
cta_url: s.cta_url,
|
|
1914
|
+
icon_url: s.icon_url,
|
|
1915
|
+
is_optional: s.is_optional
|
|
1916
|
+
}));
|
|
1917
|
+
await this.update(questId, { steps: stepsPayload });
|
|
1918
|
+
}
|
|
1919
|
+
/**
|
|
1920
|
+
* Reorder steps by providing step IDs in the desired order.
|
|
1921
|
+
*
|
|
1922
|
+
* The server has no dedicated reorder endpoint — steps are managed via the
|
|
1923
|
+
* full-replace PUT /quests/{id}. This method fetches the quest, reassigns
|
|
1924
|
+
* the `order` field according to the given stepIds sequence, and issues a
|
|
1925
|
+
* full update.
|
|
1817
1926
|
*/
|
|
1818
1927
|
async reorderSteps(questId, stepIds) {
|
|
1819
|
-
|
|
1928
|
+
const quest = await this.get(questId);
|
|
1929
|
+
const stepMap = new Map(quest.steps.map((s) => [s.id, s]));
|
|
1930
|
+
const stepsPayload = stepIds.map((id, index) => {
|
|
1931
|
+
const s = stepMap.get(id);
|
|
1932
|
+
if (!s) throw new Error(`Step ${id} not found in quest ${questId}`);
|
|
1933
|
+
return {
|
|
1934
|
+
name: s.name,
|
|
1935
|
+
description: s.description,
|
|
1936
|
+
order: index,
|
|
1937
|
+
step_type: s.step_type,
|
|
1938
|
+
event_type: s.event_type,
|
|
1939
|
+
event_types: s.event_types,
|
|
1940
|
+
criteria: s.criteria,
|
|
1941
|
+
required_data_fields: s.required_data_fields,
|
|
1942
|
+
step_points: s.step_points,
|
|
1943
|
+
cta_text: s.cta_text,
|
|
1944
|
+
cta_url: s.cta_url,
|
|
1945
|
+
icon_url: s.icon_url,
|
|
1946
|
+
is_optional: s.is_optional
|
|
1947
|
+
};
|
|
1948
|
+
});
|
|
1949
|
+
return this.update(questId, { steps: stepsPayload });
|
|
1820
1950
|
}
|
|
1821
1951
|
};
|
|
1822
1952
|
|
package/package.json
CHANGED