@statorjs/stator 1.2.2 → 1.4.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.
@@ -0,0 +1,70 @@
1
+ export type ResourceStatus = 'pending' | 'fulfilled' | 'rejected'
2
+
3
+ /**
4
+ * A peekable async value. You cannot synchronously read a native promise's
5
+ * settled state (`.then` is always a microtask), so `defer` wraps a thunk's
6
+ * result in a resource that records its settlement as it happens — letting the
7
+ * fill pass read `status` synchronously ("peek") at the moment it splices HTML.
8
+ *
9
+ * A non-promise thunk result fulfills immediately, so `defer` degrades to plain
10
+ * inline render for synchronous data.
11
+ */
12
+ export interface Resource<T = unknown> {
13
+ readonly status: ResourceStatus
14
+ readonly value?: T
15
+ readonly reason?: unknown
16
+ /**
17
+ * Resolves when the resource settles (fulfilled OR rejected). Never rejects —
18
+ * a rejection is recorded as `status: 'rejected'` + `reason`, and `settled`
19
+ * still resolves, so the resolve phase can `await` a batch of resources with a
20
+ * plain `Promise.all` and never has to catch.
21
+ */
22
+ readonly settled: Promise<void>
23
+ }
24
+
25
+ function isPromiseLike(v: unknown): v is PromiseLike<unknown> {
26
+ return (
27
+ (typeof v === 'object' || typeof v === 'function') &&
28
+ v !== null &&
29
+ typeof (v as { then?: unknown }).then === 'function'
30
+ )
31
+ }
32
+
33
+ /**
34
+ * Wrap a thunk's result in a peekable {@link Resource}. Synchronous values (and
35
+ * synchronous throws) settle immediately; a promise records its settlement on a
36
+ * single attached continuation.
37
+ */
38
+ export function createResource<T>(produce: () => T | Promise<T>): Resource<T> {
39
+ let result: T | Promise<T>
40
+ try {
41
+ result = produce()
42
+ } catch (reason) {
43
+ // A thunk that throws synchronously is a rejected resource, not a crash.
44
+ return { status: 'rejected', reason, settled: Promise.resolve() }
45
+ }
46
+
47
+ if (!isPromiseLike(result)) {
48
+ return { status: 'fulfilled', value: result, settled: Promise.resolve() }
49
+ }
50
+
51
+ const resource: {
52
+ status: ResourceStatus
53
+ value?: T
54
+ reason?: unknown
55
+ settled: Promise<void>
56
+ } = { status: 'pending', settled: Promise.resolve() }
57
+
58
+ resource.settled = Promise.resolve(result).then(
59
+ (value) => {
60
+ resource.status = 'fulfilled'
61
+ resource.value = value
62
+ },
63
+ (reason) => {
64
+ resource.status = 'rejected'
65
+ resource.reason = reason
66
+ },
67
+ )
68
+
69
+ return resource
70
+ }