@critical-path/svelte 0.17.11 → 0.18.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +13 -0
  3. package/dist/activity-state.svelte.d.ts +3 -0
  4. package/dist/activity-state.svelte.d.ts.map +1 -1
  5. package/dist/attachment-state.svelte.d.ts +3 -0
  6. package/dist/attachment-state.svelte.d.ts.map +1 -1
  7. package/dist/cancellation.test.d.ts +2 -0
  8. package/dist/cancellation.test.d.ts.map +1 -0
  9. package/dist/comment-state.svelte.d.ts +3 -0
  10. package/dist/comment-state.svelte.d.ts.map +1 -1
  11. package/dist/critical-path-state.svelte.d.ts +3 -0
  12. package/dist/critical-path-state.svelte.d.ts.map +1 -1
  13. package/dist/deliverable-state.svelte.d.ts +3 -0
  14. package/dist/deliverable-state.svelte.d.ts.map +1 -1
  15. package/dist/deliverable-summary-state.svelte.d.ts +3 -0
  16. package/dist/deliverable-summary-state.svelte.d.ts.map +1 -1
  17. package/dist/index.js +645 -551
  18. package/dist/latest-request.d.ts +15 -0
  19. package/dist/latest-request.d.ts.map +1 -0
  20. package/dist/project-state.svelte.d.ts +3 -0
  21. package/dist/project-state.svelte.d.ts.map +1 -1
  22. package/dist/task-metrics-state.svelte.d.ts +3 -0
  23. package/dist/task-metrics-state.svelte.d.ts.map +1 -1
  24. package/dist/task-state.svelte.d.ts +3 -0
  25. package/dist/task-state.svelte.d.ts.map +1 -1
  26. package/dist/task-transitions-state.svelte.d.ts +3 -0
  27. package/dist/task-transitions-state.svelte.d.ts.map +1 -1
  28. package/dist/timeline-ladder-state.svelte.d.ts +3 -0
  29. package/dist/timeline-ladder-state.svelte.d.ts.map +1 -1
  30. package/dist/workflow-state.svelte.d.ts +3 -0
  31. package/dist/workflow-state.svelte.d.ts.map +1 -1
  32. package/dist/workload-state.svelte.d.ts +3 -0
  33. package/dist/workload-state.svelte.d.ts.map +1 -1
  34. package/package.json +4 -4
  35. package/src/activity-state.svelte.ts +13 -3
  36. package/src/attachment-state.svelte.ts +12 -2
  37. package/src/cancellation.test.ts +43 -0
  38. package/src/comment-state.svelte.ts +12 -2
  39. package/src/critical-path-state.svelte.ts +12 -2
  40. package/src/deliverable-state.svelte.ts +12 -2
  41. package/src/deliverable-summary-state.svelte.ts +12 -2
  42. package/src/latest-request.ts +24 -0
  43. package/src/project-state.svelte.ts +12 -2
  44. package/src/task-metrics-state.svelte.ts +27 -7
  45. package/src/task-state.svelte.ts +12 -2
  46. package/src/task-transitions-state.svelte.ts +12 -2
  47. package/src/timeline-ladder-state.svelte.ts +12 -2
  48. package/src/workflow-state.svelte.ts +12 -2
  49. package/src/workload-state.svelte.ts +12 -2
@@ -1,5 +1,6 @@
1
1
  /// <reference types="svelte" />
2
2
  import type { CriticalPathClient } from '@critical-path/client';
3
+ import { LatestRequest } from './latest-request.js';
3
4
  import type {
4
5
  TaskMetrics,
5
6
  TaskProgressHistory,
@@ -11,6 +12,9 @@ import type {
11
12
  } from '@critical-path/core';
12
13
 
13
14
  export class TaskMetricsState {
15
+ #fetchMetricsRequest = new LatestRequest();
16
+ #fetchAllRequest = new LatestRequest();
17
+ #fetchHistoryRequest = new LatestRequest();
14
18
  metrics = $state<TaskMetrics | null>(null);
15
19
  history = $state<TaskProgressHistory | null>(null);
16
20
  loading = $state<boolean>(false);
@@ -28,51 +32,67 @@ export class TaskMetricsState {
28
32
  ) {}
29
33
 
30
34
  async fetchMetrics(): Promise<TaskMetrics | null> {
35
+ const { api, signal } = this.#fetchMetricsRequest.begin(this.client);
31
36
  this.loading = true;
32
37
  this.error = null;
33
38
  try {
34
- this.metrics = await this.client.getTaskMetrics(this.taskId);
39
+ this.metrics = await api.getTaskMetrics(this.taskId);
40
+ if (signal.aborted) return null;
35
41
  return this.metrics;
36
42
  } catch (err) {
43
+ if (signal.aborted) return null;
37
44
  this.error = err instanceof Error ? err : new Error(String(err));
38
45
  return null;
39
46
  } finally {
40
- this.loading = false;
47
+ if (!signal.aborted) this.loading = false;
41
48
  }
42
49
  }
43
50
 
44
51
  async fetchHistory(): Promise<TaskProgressHistory | null> {
52
+ const { api, signal } = this.#fetchHistoryRequest.begin(this.client);
45
53
  this.loading = true;
46
54
  this.error = null;
47
55
  try {
48
- this.history = await this.client.getTaskProgressHistory(this.taskId);
56
+ this.history = await api.getTaskProgressHistory(this.taskId);
57
+ if (signal.aborted) return null;
49
58
  return this.history;
50
59
  } catch (err) {
60
+ if (signal.aborted) return null;
51
61
  this.error = err instanceof Error ? err : new Error(String(err));
52
62
  return null;
53
63
  } finally {
54
- this.loading = false;
64
+ if (!signal.aborted) this.loading = false;
55
65
  }
56
66
  }
57
67
 
58
68
  async fetchAll(): Promise<{ metrics: TaskMetrics | null; history: TaskProgressHistory | null }> {
69
+ const { api, signal } = this.#fetchAllRequest.begin(this.client);
59
70
  this.loading = true;
60
71
  this.error = null;
61
72
  try {
62
73
  const [m, h] = await Promise.all([
63
- this.client.getTaskMetrics(this.taskId),
64
- this.client.getTaskProgressHistory(this.taskId)
74
+ api.getTaskMetrics(this.taskId),
75
+ api.getTaskProgressHistory(this.taskId)
65
76
  ]);
77
+ if (signal.aborted) return { metrics: null, history: null };
66
78
  this.metrics = m;
67
79
  this.history = h;
68
80
  return { metrics: m, history: h };
69
81
  } catch (err) {
82
+ if (signal.aborted) return { metrics: null, history: null };
70
83
  this.error = err instanceof Error ? err : new Error(String(err));
71
84
  return { metrics: null, history: null };
72
85
  } finally {
73
- this.loading = false;
86
+ if (!signal.aborted) this.loading = false;
74
87
  }
75
88
  }
89
+
90
+ /** Cancels in-flight requests, e.g. from a component's onDestroy. */
91
+ destroy() {
92
+ this.#fetchMetricsRequest.cancel();
93
+ this.#fetchAllRequest.cancel();
94
+ this.#fetchHistoryRequest.cancel();
95
+ }
76
96
  }
77
97
 
78
98
  export function createTaskMetricsState(
@@ -2,8 +2,10 @@
2
2
  import type { CreateTaskBody, UpdateTaskBody } from '@critical-path/core/schemas';
3
3
  import type { CriticalPathClient } from '@critical-path/client';
4
4
  import { type Task, type TaskStatus, isTempTaskId } from '@critical-path/core';
5
+ import { LatestRequest } from './latest-request.js';
5
6
 
6
7
  export class TaskState {
8
+ #fetchRequest = new LatestRequest();
7
9
  data = $state<Task[]>([]);
8
10
  loading = $state<boolean>(false);
9
11
  error = $state<Error | null>(null);
@@ -17,14 +19,17 @@ export class TaskState {
17
19
  ) {}
18
20
 
19
21
  async fetch() {
22
+ const { api, signal } = this.#fetchRequest.begin(this.client);
20
23
  this.loading = true;
21
24
  this.error = null;
22
25
  try {
23
- this.data = await this.client.getTasks(this.projectId);
26
+ this.data = await api.getTasks(this.projectId);
27
+ if (signal.aborted) return;
24
28
  } catch (err) {
29
+ if (signal.aborted) return;
25
30
  this.error = err instanceof Error ? err : new Error(String(err));
26
31
  } finally {
27
- this.loading = false;
32
+ if (!signal.aborted) this.loading = false;
28
33
  }
29
34
  }
30
35
 
@@ -185,6 +190,11 @@ export class TaskState {
185
190
  throw errorObj;
186
191
  }
187
192
  }
193
+
194
+ /** Cancels in-flight requests, e.g. from a component's onDestroy. */
195
+ destroy() {
196
+ this.#fetchRequest.cancel();
197
+ }
188
198
  }
189
199
 
190
200
  export function createTaskState(
@@ -1,7 +1,9 @@
1
1
  /// <reference types="svelte" />
2
2
  import type { CriticalPathClient } from '@critical-path/client';
3
+ import { LatestRequest } from './latest-request.js';
3
4
 
4
5
  export class TaskTransitionsState {
6
+ #fetchRequest = new LatestRequest();
5
7
  allowedTransitions = $state<string[]>([]);
6
8
  loading = $state<boolean>(false);
7
9
  error = $state<Error | null>(null);
@@ -9,6 +11,7 @@ export class TaskTransitionsState {
9
11
  constructor(private client: CriticalPathClient, public taskId?: string) {}
10
12
 
11
13
  async fetch(taskId?: string) {
14
+ const { api, signal } = this.#fetchRequest.begin(this.client);
12
15
  const targetTaskId = taskId || this.taskId;
13
16
  if (!targetTaskId) {
14
17
  this.allowedTransitions = [];
@@ -19,13 +22,20 @@ export class TaskTransitionsState {
19
22
  this.loading = true;
20
23
  this.error = null;
21
24
  try {
22
- this.allowedTransitions = await this.client.getAllowedTaskTransitions(targetTaskId);
25
+ this.allowedTransitions = await api.getAllowedTaskTransitions(targetTaskId);
26
+ if (signal.aborted) return;
23
27
  } catch (err) {
28
+ if (signal.aborted) return;
24
29
  this.error = err instanceof Error ? err : new Error(String(err));
25
30
  } finally {
26
- this.loading = false;
31
+ if (!signal.aborted) this.loading = false;
27
32
  }
28
33
  }
34
+
35
+ /** Cancels in-flight requests, e.g. from a component's onDestroy. */
36
+ destroy() {
37
+ this.#fetchRequest.cancel();
38
+ }
29
39
  }
30
40
 
31
41
  export function createTaskTransitionsState(
@@ -1,5 +1,6 @@
1
1
  /// <reference types="svelte" />
2
2
  import type { CriticalPathClient } from '@critical-path/client';
3
+ import { LatestRequest } from './latest-request.js';
3
4
  import type {
4
5
  TimelineLadder,
5
6
  TimelineLadderOptions,
@@ -9,6 +10,7 @@ import type {
9
10
  } from '@critical-path/core';
10
11
 
11
12
  export class TimelineLadderState {
13
+ #fetchRequest = new LatestRequest();
12
14
  data = $state<TimelineLadder | null>(null);
13
15
  level = $state<AbstractionLevel>('all');
14
16
  loading = $state<boolean>(false);
@@ -34,19 +36,27 @@ export class TimelineLadderState {
34
36
  }
35
37
 
36
38
  async fetch(options?: TimelineLadderOptions) {
39
+ const { api, signal } = this.#fetchRequest.begin(this.client);
37
40
  this.loading = true;
38
41
  this.error = null;
39
42
  try {
40
- this.data = await this.client.getTimelineLadder(this.projectId, {
43
+ this.data = await api.getTimelineLadder(this.projectId, {
41
44
  level: this.level,
42
45
  ...options
43
46
  });
47
+ if (signal.aborted) return;
44
48
  } catch (err) {
49
+ if (signal.aborted) return;
45
50
  this.error = err instanceof Error ? err : new Error(String(err));
46
51
  } finally {
47
- this.loading = false;
52
+ if (!signal.aborted) this.loading = false;
48
53
  }
49
54
  }
55
+
56
+ /** Cancels in-flight requests, e.g. from a component's onDestroy. */
57
+ destroy() {
58
+ this.#fetchRequest.cancel();
59
+ }
50
60
  }
51
61
 
52
62
  export function createTimelineLadderState(
@@ -1,8 +1,10 @@
1
1
  /// <reference types="svelte" />
2
2
  import type { CriticalPathClient } from '@critical-path/client';
3
3
  import type { Workflow } from '@critical-path/core';
4
+ import { LatestRequest } from './latest-request.js';
4
5
 
5
6
  export class WorkflowState {
7
+ #fetchRequest = new LatestRequest();
6
8
  data = $state<Workflow[]>([]);
7
9
  loading = $state<boolean>(false);
8
10
  error = $state<Error | null>(null);
@@ -10,14 +12,17 @@ export class WorkflowState {
10
12
  constructor(private client: CriticalPathClient) {}
11
13
 
12
14
  async fetch() {
15
+ const { api, signal } = this.#fetchRequest.begin(this.client);
13
16
  this.loading = true;
14
17
  this.error = null;
15
18
  try {
16
- this.data = await this.client.getWorkflows();
19
+ this.data = await api.getWorkflows();
20
+ if (signal.aborted) return;
17
21
  } catch (err) {
22
+ if (signal.aborted) return;
18
23
  this.error = err instanceof Error ? err : new Error(String(err));
19
24
  } finally {
20
- this.loading = false;
25
+ if (!signal.aborted) this.loading = false;
21
26
  }
22
27
  }
23
28
 
@@ -55,6 +60,11 @@ export class WorkflowState {
55
60
  throw errorObj;
56
61
  }
57
62
  }
63
+
64
+ /** Cancels in-flight requests, e.g. from a component's onDestroy. */
65
+ destroy() {
66
+ this.#fetchRequest.cancel();
67
+ }
58
68
  }
59
69
 
60
70
  export function createWorkflowState(client: CriticalPathClient): WorkflowState {
@@ -1,5 +1,6 @@
1
1
  /// <reference types="svelte" />
2
2
  import type { CriticalPathClient } from '@critical-path/client';
3
+ import { LatestRequest } from './latest-request.js';
3
4
  import type {
4
5
  WorkloadDistribution,
5
6
  WorkloadDistributionOptions,
@@ -10,6 +11,7 @@ import type {
10
11
  } from '@critical-path/core';
11
12
 
12
13
  export class WorkloadState {
14
+ #fetchRequest = new LatestRequest();
13
15
  data = $state<WorkloadDistribution | null>(null);
14
16
  interval = $state<WorkloadInterval>('week');
15
17
  groupBy = $state<WorkloadGroupBy>('assignee');
@@ -56,22 +58,30 @@ export class WorkloadState {
56
58
  }
57
59
 
58
60
  async fetch(options?: WorkloadDistributionOptions) {
61
+ const { api, signal } = this.#fetchRequest.begin(this.client);
59
62
  this.loading = true;
60
63
  this.error = null;
61
64
  try {
62
- this.data = await this.client.getWorkloadDistribution(this.projectId, {
65
+ this.data = await api.getWorkloadDistribution(this.projectId, {
63
66
  ...this.initialOptions,
64
67
  interval: this.interval,
65
68
  groupBy: this.groupBy,
66
69
  metric: this.metric,
67
70
  ...options
68
71
  });
72
+ if (signal.aborted) return;
69
73
  } catch (err) {
74
+ if (signal.aborted) return;
70
75
  this.error = err instanceof Error ? err : new Error(String(err));
71
76
  } finally {
72
- this.loading = false;
77
+ if (!signal.aborted) this.loading = false;
73
78
  }
74
79
  }
80
+
81
+ /** Cancels in-flight requests, e.g. from a component's onDestroy. */
82
+ destroy() {
83
+ this.#fetchRequest.cancel();
84
+ }
75
85
  }
76
86
 
77
87
  export function createWorkloadState(