mvc-kit 2.9.0 → 2.10.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 (102) hide show
  1. package/README.md +2 -0
  2. package/agent-config/claude-code/skills/guide/anti-patterns.md +64 -0
  3. package/agent-config/claude-code/skills/guide/api-reference.md +33 -2
  4. package/agent-config/claude-code/skills/guide/patterns.md +38 -0
  5. package/agent-config/copilot/copilot-instructions.md +5 -1
  6. package/agent-config/cursor/cursorrules +5 -1
  7. package/dist/Channel.cjs +5 -0
  8. package/dist/Channel.cjs.map +1 -1
  9. package/dist/Channel.d.ts +1 -0
  10. package/dist/Channel.d.ts.map +1 -1
  11. package/dist/Channel.js +5 -0
  12. package/dist/Channel.js.map +1 -1
  13. package/dist/Collection.cjs +20 -17
  14. package/dist/Collection.cjs.map +1 -1
  15. package/dist/Collection.d.ts +2 -2
  16. package/dist/Collection.d.ts.map +1 -1
  17. package/dist/Collection.js +20 -17
  18. package/dist/Collection.js.map +1 -1
  19. package/dist/Controller.cjs +5 -0
  20. package/dist/Controller.cjs.map +1 -1
  21. package/dist/Controller.d.ts +1 -0
  22. package/dist/Controller.d.ts.map +1 -1
  23. package/dist/Controller.js +5 -0
  24. package/dist/Controller.js.map +1 -1
  25. package/dist/EventBus.cjs +5 -0
  26. package/dist/EventBus.cjs.map +1 -1
  27. package/dist/EventBus.d.ts +1 -0
  28. package/dist/EventBus.d.ts.map +1 -1
  29. package/dist/EventBus.js +5 -0
  30. package/dist/EventBus.js.map +1 -1
  31. package/dist/Feed.cjs +4 -0
  32. package/dist/Feed.cjs.map +1 -1
  33. package/dist/Feed.d.ts +1 -0
  34. package/dist/Feed.d.ts.map +1 -1
  35. package/dist/Feed.js +4 -0
  36. package/dist/Feed.js.map +1 -1
  37. package/dist/Model.cjs +6 -3
  38. package/dist/Model.cjs.map +1 -1
  39. package/dist/Model.d.ts +1 -1
  40. package/dist/Model.d.ts.map +1 -1
  41. package/dist/Model.js +6 -3
  42. package/dist/Model.js.map +1 -1
  43. package/dist/Pagination.cjs +2 -0
  44. package/dist/Pagination.cjs.map +1 -1
  45. package/dist/Pagination.d.ts.map +1 -1
  46. package/dist/Pagination.js +2 -0
  47. package/dist/Pagination.js.map +1 -1
  48. package/dist/Pending.cjs +301 -0
  49. package/dist/Pending.cjs.map +1 -0
  50. package/dist/Pending.d.ts +91 -0
  51. package/dist/Pending.d.ts.map +1 -0
  52. package/dist/Pending.js +301 -0
  53. package/dist/Pending.js.map +1 -0
  54. package/dist/PersistentCollection.cjs +1 -1
  55. package/dist/PersistentCollection.cjs.map +1 -1
  56. package/dist/PersistentCollection.d.ts.map +1 -1
  57. package/dist/PersistentCollection.js +1 -1
  58. package/dist/PersistentCollection.js.map +1 -1
  59. package/dist/Selection.cjs +4 -0
  60. package/dist/Selection.cjs.map +1 -1
  61. package/dist/Selection.d.ts +1 -0
  62. package/dist/Selection.d.ts.map +1 -1
  63. package/dist/Selection.js +4 -0
  64. package/dist/Selection.js.map +1 -1
  65. package/dist/Service.cjs +5 -0
  66. package/dist/Service.cjs.map +1 -1
  67. package/dist/Service.d.ts +1 -0
  68. package/dist/Service.d.ts.map +1 -1
  69. package/dist/Service.js +5 -0
  70. package/dist/Service.js.map +1 -1
  71. package/dist/Sorting.cjs +2 -0
  72. package/dist/Sorting.cjs.map +1 -1
  73. package/dist/Sorting.d.ts.map +1 -1
  74. package/dist/Sorting.js +2 -0
  75. package/dist/Sorting.js.map +1 -1
  76. package/dist/ViewModel.cjs +45 -17
  77. package/dist/ViewModel.cjs.map +1 -1
  78. package/dist/ViewModel.d.ts +13 -4
  79. package/dist/ViewModel.d.ts.map +1 -1
  80. package/dist/ViewModel.js +45 -17
  81. package/dist/ViewModel.js.map +1 -1
  82. package/dist/bindPublicMethods.cjs +27 -0
  83. package/dist/bindPublicMethods.cjs.map +1 -0
  84. package/dist/bindPublicMethods.d.ts +18 -0
  85. package/dist/bindPublicMethods.d.ts.map +1 -0
  86. package/dist/bindPublicMethods.js +27 -0
  87. package/dist/bindPublicMethods.js.map +1 -0
  88. package/dist/index.d.ts +2 -0
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/mvc-kit.cjs +2 -0
  91. package/dist/mvc-kit.cjs.map +1 -1
  92. package/dist/mvc-kit.js +2 -0
  93. package/dist/mvc-kit.js.map +1 -1
  94. package/dist/walkPrototypeChain.cjs.map +1 -1
  95. package/dist/walkPrototypeChain.d.ts +1 -1
  96. package/dist/walkPrototypeChain.js.map +1 -1
  97. package/dist/wrapAsyncMethods.cjs +15 -3
  98. package/dist/wrapAsyncMethods.cjs.map +1 -1
  99. package/dist/wrapAsyncMethods.d.ts.map +1 -1
  100. package/dist/wrapAsyncMethods.js +15 -3
  101. package/dist/wrapAsyncMethods.js.map +1 -1
  102. package/package.json +2 -1
@@ -0,0 +1,301 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const errors = require("./errors.cjs");
4
+ const bindPublicMethods = require("./bindPublicMethods.cjs");
5
+ const __DEV__ = typeof __MVC_KIT_DEV__ !== "undefined" && __MVC_KIT_DEV__;
6
+ class Pending {
7
+ // ── Static config (Channel pattern — override via subclass) ──
8
+ /** Maximum number of retry attempts before marking as failed. */
9
+ static MAX_RETRIES = 5;
10
+ /** Base delay (ms) for retry backoff. */
11
+ static RETRY_BASE = 1e3;
12
+ /** Maximum delay cap (ms) for retry backoff. */
13
+ static RETRY_MAX = 3e4;
14
+ /** Exponential backoff multiplier for retry delay. */
15
+ static RETRY_FACTOR = 2;
16
+ // ── Private state ──
17
+ _operations = /* @__PURE__ */ new Map();
18
+ _snapshots = /* @__PURE__ */ new Map();
19
+ _listeners = /* @__PURE__ */ new Set();
20
+ _disposed = false;
21
+ _entriesCache = null;
22
+ constructor() {
23
+ bindPublicMethods.bindPublicMethods(this);
24
+ }
25
+ // ── Readable state (reactive — auto-tracked by ViewModel getters) ──
26
+ /** Get the frozen status snapshot for an operation by ID, or null if not found. */
27
+ getStatus(id) {
28
+ return this._snapshots.get(id) ?? null;
29
+ }
30
+ /** Whether an operation exists for the given ID. */
31
+ has(id) {
32
+ return this._operations.has(id);
33
+ }
34
+ /** Number of operations (all statuses). */
35
+ get count() {
36
+ return this._operations.size;
37
+ }
38
+ /** Whether any operations are in-flight (active or retrying). */
39
+ get hasPending() {
40
+ for (const op of this._operations.values()) {
41
+ if (op.status !== "failed") return true;
42
+ }
43
+ return false;
44
+ }
45
+ /** Whether any operations are in a failed state. */
46
+ get hasFailed() {
47
+ for (const op of this._operations.values()) {
48
+ if (op.status === "failed") return true;
49
+ }
50
+ return false;
51
+ }
52
+ /** Number of operations in a failed state. */
53
+ get failedCount() {
54
+ let n = 0;
55
+ for (const op of this._operations.values()) {
56
+ if (op.status === "failed") n++;
57
+ }
58
+ return n;
59
+ }
60
+ /** All operations as a frozen array of entries (id + snapshot). Cached until next mutation. */
61
+ get entries() {
62
+ if (this._entriesCache === null) {
63
+ const result = [];
64
+ for (const [id, snapshot] of this._snapshots) {
65
+ result.push(Object.freeze({ ...snapshot, id }));
66
+ }
67
+ this._entriesCache = Object.freeze(result);
68
+ }
69
+ return this._entriesCache;
70
+ }
71
+ // ── Core API ──
72
+ /**
73
+ * Enqueue an operation for the given ID. Fire-and-forget (synchronous return).
74
+ * If the same ID already has a pending operation, it is superseded (aborted).
75
+ */
76
+ enqueue(id, operation, execute, meta) {
77
+ if (this._disposed) {
78
+ if (__DEV__) {
79
+ console.warn("[mvc-kit] Pending.enqueue() called after dispose — ignored.");
80
+ }
81
+ return;
82
+ }
83
+ const existing = this._operations.get(id);
84
+ if (existing) {
85
+ existing.abortController.abort();
86
+ if (existing.retryTimer !== null) {
87
+ clearTimeout(existing.retryTimer);
88
+ }
89
+ }
90
+ const op = {
91
+ id,
92
+ operation,
93
+ execute,
94
+ status: "active",
95
+ attempts: 0,
96
+ error: null,
97
+ errorCode: null,
98
+ nextRetryAt: null,
99
+ createdAt: Date.now(),
100
+ abortController: new AbortController(),
101
+ retryTimer: null,
102
+ meta: meta ?? null
103
+ };
104
+ this._operations.set(id, op);
105
+ this._snapshot(op);
106
+ this._notify();
107
+ queueMicrotask(() => this._process(id));
108
+ }
109
+ // ── Controls ──
110
+ /** Retry a failed operation. No-op if the operation is not in 'failed' status. */
111
+ retry(id) {
112
+ if (this._disposed) {
113
+ if (__DEV__) {
114
+ console.warn("[mvc-kit] Pending.retry() called after dispose — ignored.");
115
+ }
116
+ return;
117
+ }
118
+ const op = this._operations.get(id);
119
+ if (!op || op.status !== "failed") return;
120
+ op.attempts = 0;
121
+ op.error = null;
122
+ op.errorCode = null;
123
+ op.nextRetryAt = null;
124
+ op.abortController = new AbortController();
125
+ this._process(id);
126
+ }
127
+ /** Retry all failed operations. */
128
+ retryAll() {
129
+ if (this._disposed) {
130
+ if (__DEV__) {
131
+ console.warn("[mvc-kit] Pending.retryAll() called after dispose — ignored.");
132
+ }
133
+ return;
134
+ }
135
+ const failedIds = [];
136
+ for (const op of this._operations.values()) {
137
+ if (op.status === "failed") failedIds.push(op.id);
138
+ }
139
+ for (const id of failedIds) {
140
+ this.retry(id);
141
+ }
142
+ }
143
+ /** Cancel an in-flight operation by ID. Aborts the signal, clears timers, and removes it. */
144
+ cancel(id) {
145
+ const op = this._operations.get(id);
146
+ if (!op) return;
147
+ op.abortController.abort();
148
+ if (op.retryTimer !== null) {
149
+ clearTimeout(op.retryTimer);
150
+ }
151
+ this._operations.delete(id);
152
+ this._snapshots.delete(id);
153
+ this._notify();
154
+ }
155
+ /** Cancel all operations. */
156
+ cancelAll() {
157
+ for (const op of this._operations.values()) {
158
+ op.abortController.abort();
159
+ if (op.retryTimer !== null) {
160
+ clearTimeout(op.retryTimer);
161
+ }
162
+ }
163
+ this._operations.clear();
164
+ this._snapshots.clear();
165
+ if (this._listeners.size > 0) this._notify();
166
+ }
167
+ /** Remove a failed operation without retrying. No-op if the operation is not in 'failed' status. */
168
+ dismiss(id) {
169
+ const op = this._operations.get(id);
170
+ if (!op || op.status !== "failed") return;
171
+ this._operations.delete(id);
172
+ this._snapshots.delete(id);
173
+ this._notify();
174
+ }
175
+ /** Remove all failed operations without retrying. */
176
+ dismissAll() {
177
+ const failedIds = [];
178
+ for (const op of this._operations.values()) {
179
+ if (op.status === "failed") failedIds.push(op.id);
180
+ }
181
+ if (failedIds.length === 0) return;
182
+ for (const id of failedIds) {
183
+ this._operations.delete(id);
184
+ this._snapshots.delete(id);
185
+ }
186
+ this._notify();
187
+ }
188
+ // ── Hooks (overridable in subclass) ──
189
+ /**
190
+ * Determines whether an error is retryable. Override in a subclass to customize.
191
+ * Default: retries on network, timeout, and server_error codes.
192
+ * @protected
193
+ */
194
+ isRetryable(error) {
195
+ const code = errors.classifyError(error).code;
196
+ return code === "network" || code === "timeout" || code === "server_error";
197
+ }
198
+ // ── Subscribable interface (duck-typed — auto-tracked by ViewModel) ──
199
+ /** Subscribe to state changes. Returns an unsubscribe function. */
200
+ subscribe(cb) {
201
+ this._listeners.add(cb);
202
+ return () => {
203
+ this._listeners.delete(cb);
204
+ };
205
+ }
206
+ // ── Lifecycle ──
207
+ /** Whether this instance has been disposed. */
208
+ get disposed() {
209
+ return this._disposed;
210
+ }
211
+ /** Dispose: cancels all operations, clears listeners. */
212
+ dispose() {
213
+ if (this._disposed) return;
214
+ this._disposed = true;
215
+ this.cancelAll();
216
+ this._listeners.clear();
217
+ }
218
+ // ── Internals ──
219
+ _snapshot(op) {
220
+ const ctor = this.constructor;
221
+ this._snapshots.set(op.id, Object.freeze({
222
+ status: op.status,
223
+ operation: op.operation,
224
+ attempts: op.attempts,
225
+ maxRetries: ctor.MAX_RETRIES,
226
+ error: op.error,
227
+ errorCode: op.errorCode,
228
+ nextRetryAt: op.nextRetryAt,
229
+ createdAt: op.createdAt,
230
+ meta: op.meta
231
+ }));
232
+ }
233
+ _notify() {
234
+ this._entriesCache = null;
235
+ for (const cb of this._listeners) cb();
236
+ }
237
+ _process(id) {
238
+ const op = this._operations.get(id);
239
+ if (!op || this._disposed) return;
240
+ op.status = "active";
241
+ op.attempts++;
242
+ op.error = null;
243
+ op.errorCode = null;
244
+ op.nextRetryAt = null;
245
+ this._snapshot(op);
246
+ this._notify();
247
+ op.execute(op.abortController.signal).then(
248
+ () => {
249
+ if (this._operations.get(id) !== op) return;
250
+ const operation = op.operation;
251
+ this._operations.delete(id);
252
+ this._snapshots.delete(id);
253
+ this._notify();
254
+ this.onConfirmed?.(id, operation);
255
+ },
256
+ (error) => {
257
+ if (this._operations.get(id) !== op) return;
258
+ if (errors.isAbortError(error)) {
259
+ this._operations.delete(id);
260
+ this._snapshots.delete(id);
261
+ this._notify();
262
+ return;
263
+ }
264
+ const ctor = this.constructor;
265
+ const classified = errors.classifyError(error);
266
+ if (this.isRetryable(error) && op.attempts < ctor.MAX_RETRIES) {
267
+ op.status = "retrying";
268
+ const delay = this._calculateDelay(op.attempts - 1);
269
+ op.nextRetryAt = Date.now() + delay;
270
+ op.error = classified.message;
271
+ op.errorCode = classified.code;
272
+ this._snapshot(op);
273
+ this._notify();
274
+ op.retryTimer = setTimeout(() => {
275
+ op.retryTimer = null;
276
+ this._process(id);
277
+ }, delay);
278
+ } else {
279
+ op.status = "failed";
280
+ op.error = classified.message;
281
+ op.errorCode = classified.code;
282
+ op.nextRetryAt = null;
283
+ this._snapshot(op);
284
+ this._notify();
285
+ this.onFailed?.(id, op.operation, error);
286
+ }
287
+ }
288
+ );
289
+ }
290
+ /** Computes retry backoff delay with jitter (Channel formula). @private */
291
+ _calculateDelay(attempt) {
292
+ const ctor = this.constructor;
293
+ const capped = Math.min(
294
+ ctor.RETRY_BASE * Math.pow(ctor.RETRY_FACTOR, attempt),
295
+ ctor.RETRY_MAX
296
+ );
297
+ return Math.random() * capped;
298
+ }
299
+ }
300
+ exports.Pending = Pending;
301
+ //# sourceMappingURL=Pending.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Pending.cjs","sources":["../src/Pending.ts"],"sourcesContent":["import { classifyError, isAbortError } from './errors';\nimport type { AppError } from './errors';\nimport { bindPublicMethods } from './bindPublicMethods';\n\nconst __DEV__ = typeof __MVC_KIT_DEV__ !== 'undefined' && __MVC_KIT_DEV__;\n\n// ── Types ─────────────────────────────────────────────────────────\n\n/** Frozen snapshot of a single pending operation's state. */\nexport interface PendingOperation<Meta = unknown> {\n readonly status: 'active' | 'retrying' | 'failed';\n readonly operation: string;\n readonly attempts: number;\n readonly maxRetries: number;\n readonly error: string | null;\n readonly errorCode: AppError['code'] | null;\n readonly nextRetryAt: number | null;\n readonly createdAt: number;\n readonly meta: Meta | null;\n}\n\n/** A PendingOperation snapshot paired with its key, for iteration. */\nexport interface PendingEntry<K extends string | number, Meta = unknown>\n extends PendingOperation<Meta> {\n readonly id: K;\n}\n\n/** Mutable internal state for a pending operation. */\ninterface InternalOp<K, Meta> {\n id: K;\n operation: string;\n execute: (signal: AbortSignal) => Promise<void>;\n status: PendingOperation['status'];\n attempts: number;\n error: string | null;\n errorCode: AppError['code'] | null;\n nextRetryAt: number | null;\n createdAt: number;\n abortController: AbortController;\n retryTimer: ReturnType<typeof setTimeout> | null;\n meta: Meta | null;\n}\n\n// ── Pending ───────────────────────────────────────────────────────\n\n/**\n * Per-item operation queue with retry and status tracking.\n * Tracks operations by key with exponential backoff retry on transient errors.\n * Subscribable — auto-tracked when used as a ViewModel/Resource property.\n */\nexport class Pending<K extends string | number = string | number, Meta = unknown> {\n // ── Static config (Channel pattern — override via subclass) ──\n\n /** Maximum number of retry attempts before marking as failed. */\n static MAX_RETRIES = 5;\n /** Base delay (ms) for retry backoff. */\n static RETRY_BASE = 1000;\n /** Maximum delay cap (ms) for retry backoff. */\n static RETRY_MAX = 30000;\n /** Exponential backoff multiplier for retry delay. */\n static RETRY_FACTOR = 2;\n\n // ── Private state ──\n\n private _operations = new Map<K, InternalOp<K, Meta>>();\n private _snapshots = new Map<K, PendingOperation<Meta>>();\n private _listeners = new Set<() => void>();\n private _disposed = false;\n private _entriesCache: readonly PendingEntry<K, Meta>[] | null = null;\n\n constructor() {\n bindPublicMethods(this);\n }\n\n // ── Readable state (reactive — auto-tracked by ViewModel getters) ──\n\n /** Get the frozen status snapshot for an operation by ID, or null if not found. */\n getStatus(id: K): PendingOperation<Meta> | null {\n return this._snapshots.get(id) ?? null;\n }\n\n /** Whether an operation exists for the given ID. */\n has(id: K): boolean {\n return this._operations.has(id);\n }\n\n /** Number of operations (all statuses). */\n get count(): number {\n return this._operations.size;\n }\n\n /** Whether any operations are in-flight (active or retrying). */\n get hasPending(): boolean {\n for (const op of this._operations.values()) {\n if (op.status !== 'failed') return true;\n }\n return false;\n }\n\n /** Whether any operations are in a failed state. */\n get hasFailed(): boolean {\n for (const op of this._operations.values()) {\n if (op.status === 'failed') return true;\n }\n return false;\n }\n\n /** Number of operations in a failed state. */\n get failedCount(): number {\n let n = 0;\n for (const op of this._operations.values()) {\n if (op.status === 'failed') n++;\n }\n return n;\n }\n\n /** All operations as a frozen array of entries (id + snapshot). Cached until next mutation. */\n get entries(): readonly PendingEntry<K, Meta>[] {\n if (this._entriesCache === null) {\n const result: PendingEntry<K, Meta>[] = [];\n for (const [id, snapshot] of this._snapshots) {\n result.push(Object.freeze({ ...snapshot, id }));\n }\n this._entriesCache = Object.freeze(result) as readonly PendingEntry<K, Meta>[];\n }\n return this._entriesCache;\n }\n\n // ── Core API ──\n\n /**\n * Enqueue an operation for the given ID. Fire-and-forget (synchronous return).\n * If the same ID already has a pending operation, it is superseded (aborted).\n */\n enqueue(id: K, operation: string, execute: (signal: AbortSignal) => Promise<void>, meta?: Meta): void {\n if (this._disposed) {\n if (__DEV__) {\n console.warn('[mvc-kit] Pending.enqueue() called after dispose — ignored.');\n }\n return;\n }\n\n // Supersede existing operation for this ID\n const existing = this._operations.get(id);\n if (existing) {\n existing.abortController.abort();\n if (existing.retryTimer !== null) {\n clearTimeout(existing.retryTimer);\n }\n }\n\n const op: InternalOp<K, Meta> = {\n id,\n operation,\n execute,\n status: 'active',\n attempts: 0,\n error: null,\n errorCode: null,\n nextRetryAt: null,\n createdAt: Date.now(),\n abortController: new AbortController(),\n retryTimer: null,\n meta: meta ?? null,\n };\n\n this._operations.set(id, op);\n this._snapshot(op);\n this._notify();\n\n // Schedule processing via microtask (allows batching multiple enqueues)\n queueMicrotask(() => this._process(id));\n }\n\n // ── Controls ──\n\n /** Retry a failed operation. No-op if the operation is not in 'failed' status. */\n retry(id: K): void {\n if (this._disposed) {\n if (__DEV__) {\n console.warn('[mvc-kit] Pending.retry() called after dispose — ignored.');\n }\n return;\n }\n\n const op = this._operations.get(id);\n if (!op || op.status !== 'failed') return;\n\n op.attempts = 0;\n op.error = null;\n op.errorCode = null;\n op.nextRetryAt = null;\n op.abortController = new AbortController();\n this._process(id);\n }\n\n /** Retry all failed operations. */\n retryAll(): void {\n if (this._disposed) {\n if (__DEV__) {\n console.warn('[mvc-kit] Pending.retryAll() called after dispose — ignored.');\n }\n return;\n }\n\n const failedIds: K[] = [];\n for (const op of this._operations.values()) {\n if (op.status === 'failed') failedIds.push(op.id);\n }\n for (const id of failedIds) {\n this.retry(id);\n }\n }\n\n /** Cancel an in-flight operation by ID. Aborts the signal, clears timers, and removes it. */\n cancel(id: K): void {\n const op = this._operations.get(id);\n if (!op) return;\n\n op.abortController.abort();\n if (op.retryTimer !== null) {\n clearTimeout(op.retryTimer);\n }\n this._operations.delete(id);\n this._snapshots.delete(id);\n this._notify();\n }\n\n /** Cancel all operations. */\n cancelAll(): void {\n for (const op of this._operations.values()) {\n op.abortController.abort();\n if (op.retryTimer !== null) {\n clearTimeout(op.retryTimer);\n }\n }\n this._operations.clear();\n this._snapshots.clear();\n if (this._listeners.size > 0) this._notify();\n }\n\n /** Remove a failed operation without retrying. No-op if the operation is not in 'failed' status. */\n dismiss(id: K): void {\n const op = this._operations.get(id);\n if (!op || op.status !== 'failed') return;\n\n this._operations.delete(id);\n this._snapshots.delete(id);\n this._notify();\n }\n\n /** Remove all failed operations without retrying. */\n dismissAll(): void {\n const failedIds: K[] = [];\n for (const op of this._operations.values()) {\n if (op.status === 'failed') failedIds.push(op.id);\n }\n if (failedIds.length === 0) return;\n for (const id of failedIds) {\n this._operations.delete(id);\n this._snapshots.delete(id);\n }\n this._notify();\n }\n\n // ── Hooks (overridable in subclass) ──\n\n /**\n * Determines whether an error is retryable. Override in a subclass to customize.\n * Default: retries on network, timeout, and server_error codes.\n * @protected\n */\n protected isRetryable(error: unknown): boolean {\n const code = classifyError(error).code;\n return code === 'network' || code === 'timeout' || code === 'server_error';\n }\n\n /** Called when an operation succeeds. Override in a subclass for side effects. @protected */\n protected onConfirmed?(id: K, operation: string): void;\n /** Called when an operation fails permanently. Override in a subclass for side effects. @protected */\n protected onFailed?(id: K, operation: string, error: unknown): void;\n\n // ── Subscribable interface (duck-typed — auto-tracked by ViewModel) ──\n\n /** Subscribe to state changes. Returns an unsubscribe function. */\n subscribe(cb: () => void): () => void {\n this._listeners.add(cb);\n return () => { this._listeners.delete(cb); };\n }\n\n // ── Lifecycle ──\n\n /** Whether this instance has been disposed. */\n get disposed(): boolean {\n return this._disposed;\n }\n\n /** Dispose: cancels all operations, clears listeners. */\n dispose(): void {\n if (this._disposed) return;\n this._disposed = true;\n this.cancelAll();\n this._listeners.clear();\n }\n\n // ── Internals ──\n\n private _snapshot(op: InternalOp<K, Meta>): void {\n const ctor = this.constructor as typeof Pending;\n this._snapshots.set(op.id, Object.freeze({\n status: op.status,\n operation: op.operation,\n attempts: op.attempts,\n maxRetries: ctor.MAX_RETRIES,\n error: op.error,\n errorCode: op.errorCode,\n nextRetryAt: op.nextRetryAt,\n createdAt: op.createdAt,\n meta: op.meta,\n }));\n }\n\n private _notify(): void {\n this._entriesCache = null;\n for (const cb of this._listeners) cb();\n }\n\n private _process(id: K): void {\n const op = this._operations.get(id);\n if (!op || this._disposed) return;\n\n // Set active status\n op.status = 'active';\n op.attempts++;\n op.error = null;\n op.errorCode = null;\n op.nextRetryAt = null;\n this._snapshot(op);\n this._notify();\n\n op.execute(op.abortController.signal).then(\n () => {\n // Identity check: ignore if this op was superseded or cancelled\n if (this._operations.get(id) !== op) return;\n const operation = op.operation;\n this._operations.delete(id);\n this._snapshots.delete(id);\n this._notify();\n this.onConfirmed?.(id, operation);\n },\n (error: unknown) => {\n // Identity check: ignore if this op was superseded or cancelled\n if (this._operations.get(id) !== op) return;\n\n // AbortError — remove silently (cancel or supersede)\n if (isAbortError(error)) {\n this._operations.delete(id);\n this._snapshots.delete(id);\n this._notify();\n return;\n }\n\n const ctor = this.constructor as typeof Pending;\n const classified = classifyError(error);\n\n // Check if retryable and under max retries\n if (this.isRetryable(error) && op.attempts < ctor.MAX_RETRIES) {\n op.status = 'retrying';\n const delay = this._calculateDelay(op.attempts - 1);\n op.nextRetryAt = Date.now() + delay;\n op.error = classified.message;\n op.errorCode = classified.code;\n this._snapshot(op);\n this._notify();\n\n op.retryTimer = setTimeout(() => {\n op.retryTimer = null;\n this._process(id);\n }, delay);\n } else {\n // Non-retryable or max retries exceeded\n op.status = 'failed';\n op.error = classified.message;\n op.errorCode = classified.code;\n op.nextRetryAt = null;\n this._snapshot(op);\n this._notify();\n this.onFailed?.(id, op.operation, error);\n }\n },\n );\n }\n\n /** Computes retry backoff delay with jitter (Channel formula). @private */\n private _calculateDelay(attempt: number): number {\n const ctor = this.constructor as typeof Pending;\n const capped = Math.min(\n ctor.RETRY_BASE * Math.pow(ctor.RETRY_FACTOR, attempt),\n ctor.RETRY_MAX,\n );\n return Math.random() * capped;\n }\n}\n"],"names":["bindPublicMethods","classifyError","isAbortError"],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ import type { AppError } from './errors';
2
+ /** Frozen snapshot of a single pending operation's state. */
3
+ export interface PendingOperation<Meta = unknown> {
4
+ readonly status: 'active' | 'retrying' | 'failed';
5
+ readonly operation: string;
6
+ readonly attempts: number;
7
+ readonly maxRetries: number;
8
+ readonly error: string | null;
9
+ readonly errorCode: AppError['code'] | null;
10
+ readonly nextRetryAt: number | null;
11
+ readonly createdAt: number;
12
+ readonly meta: Meta | null;
13
+ }
14
+ /** A PendingOperation snapshot paired with its key, for iteration. */
15
+ export interface PendingEntry<K extends string | number, Meta = unknown> extends PendingOperation<Meta> {
16
+ readonly id: K;
17
+ }
18
+ /**
19
+ * Per-item operation queue with retry and status tracking.
20
+ * Tracks operations by key with exponential backoff retry on transient errors.
21
+ * Subscribable — auto-tracked when used as a ViewModel/Resource property.
22
+ */
23
+ export declare class Pending<K extends string | number = string | number, Meta = unknown> {
24
+ /** Maximum number of retry attempts before marking as failed. */
25
+ static MAX_RETRIES: number;
26
+ /** Base delay (ms) for retry backoff. */
27
+ static RETRY_BASE: number;
28
+ /** Maximum delay cap (ms) for retry backoff. */
29
+ static RETRY_MAX: number;
30
+ /** Exponential backoff multiplier for retry delay. */
31
+ static RETRY_FACTOR: number;
32
+ private _operations;
33
+ private _snapshots;
34
+ private _listeners;
35
+ private _disposed;
36
+ private _entriesCache;
37
+ constructor();
38
+ /** Get the frozen status snapshot for an operation by ID, or null if not found. */
39
+ getStatus(id: K): PendingOperation<Meta> | null;
40
+ /** Whether an operation exists for the given ID. */
41
+ has(id: K): boolean;
42
+ /** Number of operations (all statuses). */
43
+ get count(): number;
44
+ /** Whether any operations are in-flight (active or retrying). */
45
+ get hasPending(): boolean;
46
+ /** Whether any operations are in a failed state. */
47
+ get hasFailed(): boolean;
48
+ /** Number of operations in a failed state. */
49
+ get failedCount(): number;
50
+ /** All operations as a frozen array of entries (id + snapshot). Cached until next mutation. */
51
+ get entries(): readonly PendingEntry<K, Meta>[];
52
+ /**
53
+ * Enqueue an operation for the given ID. Fire-and-forget (synchronous return).
54
+ * If the same ID already has a pending operation, it is superseded (aborted).
55
+ */
56
+ enqueue(id: K, operation: string, execute: (signal: AbortSignal) => Promise<void>, meta?: Meta): void;
57
+ /** Retry a failed operation. No-op if the operation is not in 'failed' status. */
58
+ retry(id: K): void;
59
+ /** Retry all failed operations. */
60
+ retryAll(): void;
61
+ /** Cancel an in-flight operation by ID. Aborts the signal, clears timers, and removes it. */
62
+ cancel(id: K): void;
63
+ /** Cancel all operations. */
64
+ cancelAll(): void;
65
+ /** Remove a failed operation without retrying. No-op if the operation is not in 'failed' status. */
66
+ dismiss(id: K): void;
67
+ /** Remove all failed operations without retrying. */
68
+ dismissAll(): void;
69
+ /**
70
+ * Determines whether an error is retryable. Override in a subclass to customize.
71
+ * Default: retries on network, timeout, and server_error codes.
72
+ * @protected
73
+ */
74
+ protected isRetryable(error: unknown): boolean;
75
+ /** Called when an operation succeeds. Override in a subclass for side effects. @protected */
76
+ protected onConfirmed?(id: K, operation: string): void;
77
+ /** Called when an operation fails permanently. Override in a subclass for side effects. @protected */
78
+ protected onFailed?(id: K, operation: string, error: unknown): void;
79
+ /** Subscribe to state changes. Returns an unsubscribe function. */
80
+ subscribe(cb: () => void): () => void;
81
+ /** Whether this instance has been disposed. */
82
+ get disposed(): boolean;
83
+ /** Dispose: cancels all operations, clears listeners. */
84
+ dispose(): void;
85
+ private _snapshot;
86
+ private _notify;
87
+ private _process;
88
+ /** Computes retry backoff delay with jitter (Channel formula). @private */
89
+ private _calculateDelay;
90
+ }
91
+ //# sourceMappingURL=Pending.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Pending.d.ts","sourceRoot":"","sources":["../src/Pending.ts"],"names":[],"mappings":"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"}