@kontsedal/olas-core 0.0.2 → 0.0.4
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.cjs +34 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +52 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +52 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +33 -2
- package/dist/index.mjs.map +1 -1
- package/dist/{root-De-6KWIZ.mjs → root-BBSlzvJ2.mjs} +251 -18
- package/dist/root-BBSlzvJ2.mjs.map +1 -0
- package/dist/{root-XKEsSmcd.cjs → root-CoafhkTg.cjs} +251 -18
- package/dist/root-CoafhkTg.cjs.map +1 -0
- package/dist/testing.cjs +23 -11
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +3 -1
- package/dist/testing.d.cts.map +1 -1
- package/dist/testing.d.mts +3 -1
- package/dist/testing.d.mts.map +1 -1
- package/dist/testing.mjs +23 -11
- package/dist/testing.mjs.map +1 -1
- package/dist/{types-C-zV1JZA.d.mts → types-BCf2nB2N.d.mts} +73 -8
- package/dist/types-BCf2nB2N.d.mts.map +1 -0
- package/dist/{types-DKfpkm17.d.cts → types-Ijeun3qo.d.cts} +73 -8
- package/dist/types-Ijeun3qo.d.cts.map +1 -0
- package/package.json +1 -1
- package/src/controller/types.ts +26 -2
- package/src/forms/field.ts +42 -9
- package/src/forms/form-types.ts +37 -0
- package/src/forms/form.ts +118 -0
- package/src/forms/index.ts +12 -1
- package/src/forms/standard-schema.ts +37 -0
- package/src/forms/validators.ts +31 -0
- package/src/index.ts +13 -3
- package/src/query/entry.ts +10 -3
- package/src/query/infinite.ts +8 -1
- package/src/query/local.ts +1 -0
- package/src/query/structural-share.ts +114 -0
- package/src/query/types.ts +22 -2
- package/src/query/use.ts +53 -13
- package/src/testing.ts +5 -0
- package/dist/root-De-6KWIZ.mjs.map +0 -1
- package/dist/root-XKEsSmcd.cjs.map +0 -1
- package/dist/types-C-zV1JZA.d.mts.map +0 -1
- package/dist/types-DKfpkm17.d.cts.map +0 -1
package/src/query/use.ts
CHANGED
|
@@ -2,17 +2,24 @@ import { computed, effect, type Signal, signal, untracked } from '../signals'
|
|
|
2
2
|
import type { ReadSignal } from '../signals/types'
|
|
3
3
|
import type { ClientEntry, InfiniteClientEntry, QueryClient } from './client'
|
|
4
4
|
import type { InfiniteQuery, InfiniteQuerySpec, InfiniteQuerySubscription } from './infinite'
|
|
5
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
AsyncStatus,
|
|
7
|
+
Query,
|
|
8
|
+
QuerySpec,
|
|
9
|
+
QuerySubscription,
|
|
10
|
+
UseInternalOptions,
|
|
11
|
+
UseOptions,
|
|
12
|
+
} from './types'
|
|
6
13
|
|
|
7
14
|
type QueryInternal<Args extends unknown[], T> = Query<Args, T> & {
|
|
8
15
|
readonly __spec: QuerySpec<Args, T>
|
|
9
16
|
}
|
|
10
17
|
|
|
11
|
-
class SubscriptionImpl<T> implements QuerySubscription<
|
|
18
|
+
class SubscriptionImpl<T, U = T> implements QuerySubscription<U> {
|
|
12
19
|
private readonly current$: Signal<ClientEntry<T> | null> = signal(null)
|
|
13
20
|
private readonly previousData$: Signal<T | undefined> = signal(undefined)
|
|
14
21
|
|
|
15
|
-
readonly data: ReadSignal<
|
|
22
|
+
readonly data: ReadSignal<U | undefined>
|
|
16
23
|
readonly error: ReadSignal<unknown | undefined>
|
|
17
24
|
readonly status: ReadSignal<AsyncStatus>
|
|
18
25
|
readonly isLoading: ReadSignal<boolean>
|
|
@@ -21,14 +28,30 @@ class SubscriptionImpl<T> implements QuerySubscription<T> {
|
|
|
21
28
|
readonly lastUpdatedAt: ReadSignal<number | undefined>
|
|
22
29
|
readonly hasPendingMutations: ReadSignal<boolean>
|
|
23
30
|
|
|
24
|
-
constructor(
|
|
25
|
-
|
|
31
|
+
constructor(
|
|
32
|
+
private readonly keepPreviousData: boolean,
|
|
33
|
+
private readonly select?: (data: T) => U,
|
|
34
|
+
) {
|
|
35
|
+
// The underlying entry stores `T`. The subscription's `data` is `U`
|
|
36
|
+
// (or `T` when no projection). We compute the raw `T` once, then layer
|
|
37
|
+
// `select` in a second computed so the projection's `Object.is` dedup
|
|
38
|
+
// applies BEFORE downstream subscribers run — combined with structural
|
|
39
|
+
// sharing on the entry, an unchanged payload + a stable `select`
|
|
40
|
+
// outputs the same `U` reference and doesn't churn the React tree.
|
|
41
|
+
const rawData = computed(() => {
|
|
26
42
|
const cur = this.current$.value
|
|
27
43
|
const curData = cur?.entry.data.value
|
|
28
44
|
if (curData !== undefined) return curData
|
|
29
45
|
if (keepPreviousData) return this.previousData$.value
|
|
30
46
|
return undefined
|
|
31
47
|
})
|
|
48
|
+
this.data =
|
|
49
|
+
select === undefined
|
|
50
|
+
? (rawData as unknown as ReadSignal<U | undefined>)
|
|
51
|
+
: computed<U | undefined>(() => {
|
|
52
|
+
const raw = rawData.value
|
|
53
|
+
return raw === undefined ? undefined : select(raw)
|
|
54
|
+
})
|
|
32
55
|
this.error = computed(() => this.current$.value?.entry.error.value)
|
|
33
56
|
this.status = computed<AsyncStatus>(() => this.current$.value?.entry.status.value ?? 'idle')
|
|
34
57
|
this.isLoading = computed(() => {
|
|
@@ -59,33 +82,45 @@ class SubscriptionImpl<T> implements QuerySubscription<T> {
|
|
|
59
82
|
this.current$.set(null)
|
|
60
83
|
}
|
|
61
84
|
|
|
62
|
-
refetch = (): Promise<
|
|
85
|
+
refetch = (): Promise<U> => {
|
|
63
86
|
const cur = this.current$.peek()
|
|
64
87
|
if (!cur) return Promise.reject(new Error('[olas] no active subscription'))
|
|
65
|
-
return cur.entry.refetch()
|
|
88
|
+
return cur.entry.refetch().then((v) => this.project(v))
|
|
66
89
|
}
|
|
67
90
|
|
|
68
91
|
reset = (): void => {
|
|
69
92
|
this.current$.peek()?.entry.reset()
|
|
70
93
|
}
|
|
71
94
|
|
|
72
|
-
firstValue = (): Promise<
|
|
95
|
+
firstValue = (): Promise<U> => {
|
|
73
96
|
const cur = this.current$.peek()
|
|
74
97
|
if (!cur) return Promise.reject(new Error('[olas] no active subscription'))
|
|
75
|
-
return cur.entry.firstValue()
|
|
98
|
+
return cur.entry.firstValue().then((v) => this.project(v))
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Alias surfaced on `AsyncState` for Suspense / React 19 `use(...)`.
|
|
102
|
+
promise = (): Promise<U> => this.firstValue()
|
|
103
|
+
|
|
104
|
+
private project(v: T): U {
|
|
105
|
+
return this.select === undefined ? (v as unknown as U) : this.select(v)
|
|
76
106
|
}
|
|
77
107
|
}
|
|
78
108
|
|
|
79
109
|
/**
|
|
80
110
|
* Build a subscription + the effect that keeps it bound to the right entry.
|
|
81
111
|
* The controller container wires the disposer into the lifecycle.
|
|
112
|
+
*
|
|
113
|
+
* `keyOrOptions` may carry an optional `select` projection that maps the
|
|
114
|
+
* underlying `T` to a view `U`; the returned subscription's data shape
|
|
115
|
+
* widens accordingly. Without `select`, `U = T` and the projection
|
|
116
|
+
* computed is skipped.
|
|
82
117
|
*/
|
|
83
|
-
export function createUse<Args extends unknown[], T>(
|
|
118
|
+
export function createUse<Args extends unknown[], T, U = T>(
|
|
84
119
|
client: QueryClient,
|
|
85
120
|
query: Query<Args, T>,
|
|
86
|
-
keyOrOptions?: (() => Args) |
|
|
121
|
+
keyOrOptions?: (() => Args) | UseInternalOptions<Args, T, U>,
|
|
87
122
|
): {
|
|
88
|
-
subscription: QuerySubscription<
|
|
123
|
+
subscription: QuerySubscription<U>
|
|
89
124
|
dispose: () => void
|
|
90
125
|
/** Suspend the subscription — release the entry (its refetchInterval +
|
|
91
126
|
* focus/online listeners pause) without disposing it. Spec §4.1. */
|
|
@@ -100,8 +135,10 @@ export function createUse<Args extends unknown[], T>(
|
|
|
100
135
|
const keyFn = typeof keyOrOptions === 'function' ? keyOrOptions : keyOrOptions?.key
|
|
101
136
|
const enabledFn =
|
|
102
137
|
typeof keyOrOptions === 'object' && keyOrOptions !== null ? keyOrOptions.enabled : undefined
|
|
138
|
+
const select =
|
|
139
|
+
typeof keyOrOptions === 'object' && keyOrOptions !== null ? keyOrOptions.select : undefined
|
|
103
140
|
|
|
104
|
-
const sub = new SubscriptionImpl<T>(keepPreviousData)
|
|
141
|
+
const sub = new SubscriptionImpl<T, U>(keepPreviousData, select)
|
|
105
142
|
let currentEntry: ClientEntry<T> | null = null
|
|
106
143
|
let suspended = false
|
|
107
144
|
|
|
@@ -291,6 +328,9 @@ class InfiniteSubscriptionImpl<TPage, TItem> implements InfiniteQuerySubscriptio
|
|
|
291
328
|
return cur.entry.firstValue()
|
|
292
329
|
}
|
|
293
330
|
|
|
331
|
+
// Alias of firstValue() for Suspense / React 19 `use(...)` ergonomics.
|
|
332
|
+
promise = (): Promise<TPage[]> => this.firstValue()
|
|
333
|
+
|
|
294
334
|
fetchNextPage = (): Promise<void> => {
|
|
295
335
|
const cur = this.current$.peek()
|
|
296
336
|
if (!cur) return Promise.resolve()
|
package/src/testing.ts
CHANGED
|
@@ -47,6 +47,7 @@ export function fakeField<T>(
|
|
|
47
47
|
reset: () => void
|
|
48
48
|
markTouched: () => void
|
|
49
49
|
revalidate: () => Promise<boolean>
|
|
50
|
+
setErrors: (errors: ReadonlyArray<string>) => void
|
|
50
51
|
dispose: () => void
|
|
51
52
|
}>,
|
|
52
53
|
): Field<T> {
|
|
@@ -85,6 +86,7 @@ export function fakeField<T>(
|
|
|
85
86
|
reset: overrides?.reset ?? (() => value$.set(currentInitial)),
|
|
86
87
|
markTouched: overrides?.markTouched ?? (() => touched$.set(true)),
|
|
87
88
|
revalidate: overrides?.revalidate ?? (async () => errors$.peek().length === 0),
|
|
89
|
+
setErrors: overrides?.setErrors ?? ((errs) => errors$.set([...errs])),
|
|
88
90
|
dispose: overrides?.dispose ?? (() => {}),
|
|
89
91
|
}
|
|
90
92
|
return fake
|
|
@@ -109,6 +111,7 @@ export function fakeAsyncState<T>(
|
|
|
109
111
|
refetch: () => Promise<T>
|
|
110
112
|
reset: () => void
|
|
111
113
|
firstValue: () => Promise<T>
|
|
114
|
+
promise: () => Promise<T>
|
|
112
115
|
}>,
|
|
113
116
|
): AsyncState<T> {
|
|
114
117
|
const data$: ReadSignal<T | undefined> = signal(overrides?.data)
|
|
@@ -125,6 +128,7 @@ export function fakeAsyncState<T>(
|
|
|
125
128
|
const refetch = overrides?.refetch ?? (async () => data$.peek() as T)
|
|
126
129
|
const reset = overrides?.reset ?? (() => {})
|
|
127
130
|
const firstValue = overrides?.firstValue ?? (async () => data$.peek() as T)
|
|
131
|
+
const promise = overrides?.promise ?? firstValue
|
|
128
132
|
|
|
129
133
|
return {
|
|
130
134
|
data: data$,
|
|
@@ -138,5 +142,6 @@ export function fakeAsyncState<T>(
|
|
|
138
142
|
refetch,
|
|
139
143
|
reset,
|
|
140
144
|
firstValue,
|
|
145
|
+
promise,
|
|
141
146
|
}
|
|
142
147
|
}
|