@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 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: 'event' | 'manual' | 'external' | 'compound';
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?: 'event' | 'manual' | 'external' | 'compound';
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
- * Get a quest by slug
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
- * Archive a quest
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: 'event' | 'manual' | 'external' | 'compound';
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?: 'event' | 'manual' | 'external' | 'compound';
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
- * Get a quest by slug
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
- * Archive a quest
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 delay = Math.min((error.retryAfter || 1) * 1e3, 6e4);
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
- await new Promise((resolve) => setTimeout(resolve, 1e3 * (retries + 1)));
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
- * Get a quest by slug
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
- * Archive a quest
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
- return this.http.post(`/quests/${questId}/steps`, step);
1862
- }
1863
- /**
1864
- * Update a step
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
- return this.http.put(`/quests/${questId}/steps/${stepId}`, data);
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.http.delete(`/quests/${questId}/steps/${stepId}`);
1874
- }
1875
- /**
1876
- * Reorder steps
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
- return this.http.post(`/quests/${questId}/steps/reorder`, { step_ids: stepIds });
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 delay = Math.min((error.retryAfter || 1) * 1e3, 6e4);
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
- await new Promise((resolve) => setTimeout(resolve, 1e3 * (retries + 1)));
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
- * Get a quest by slug
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
- * Archive a quest
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
- return this.http.post(`/quests/${questId}/steps`, step);
1802
- }
1803
- /**
1804
- * Update a step
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
- return this.http.put(`/quests/${questId}/steps/${stepId}`, data);
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.http.delete(`/quests/${questId}/steps/${stepId}`);
1814
- }
1815
- /**
1816
- * Reorder steps
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
- return this.http.post(`/quests/${questId}/steps/reorder`, { step_ids: stepIds });
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proofchain/sdk",
3
- "version": "2.23.0",
3
+ "version": "3.0.0",
4
4
  "description": "Official JavaScript/TypeScript SDK for ProofChain - blockchain-anchored document attestation",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",