@dvina/sdk 3.3.124
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/LICENSE +21 -0
- package/README.md +449 -0
- package/dist/_generated_documents-DF34ZUJ3.js +4 -0
- package/dist/_generated_documents-DF34ZUJ3.js.map +1 -0
- package/dist/_generated_documents-W4RHWJ4B.cjs +2041 -0
- package/dist/_generated_documents-W4RHWJ4B.cjs.map +1 -0
- package/dist/adapters/angular/index.cjs +163 -0
- package/dist/adapters/angular/index.cjs.map +1 -0
- package/dist/adapters/angular/index.d.cts +254 -0
- package/dist/adapters/angular/index.d.ts +254 -0
- package/dist/adapters/angular/index.js +159 -0
- package/dist/adapters/angular/index.js.map +1 -0
- package/dist/adapters/react/index.cjs +51 -0
- package/dist/adapters/react/index.cjs.map +1 -0
- package/dist/adapters/react/index.d.cts +62 -0
- package/dist/adapters/react/index.d.ts +62 -0
- package/dist/adapters/react/index.js +49 -0
- package/dist/adapters/react/index.js.map +1 -0
- package/dist/billing/index.cjs +26 -0
- package/dist/billing/index.cjs.map +1 -0
- package/dist/billing/index.d.cts +338 -0
- package/dist/billing/index.d.ts +338 -0
- package/dist/billing/index.js +5 -0
- package/dist/billing/index.js.map +1 -0
- package/dist/chunk-4QKWHQT2.cjs +104 -0
- package/dist/chunk-4QKWHQT2.cjs.map +1 -0
- package/dist/chunk-5WRI5ZAA.js +29 -0
- package/dist/chunk-5WRI5ZAA.js.map +1 -0
- package/dist/chunk-7JUBGIQ2.js +1008 -0
- package/dist/chunk-7JUBGIQ2.js.map +1 -0
- package/dist/chunk-7XBJ77RJ.js +103 -0
- package/dist/chunk-7XBJ77RJ.js.map +1 -0
- package/dist/chunk-DHUFVGJR.cjs +20389 -0
- package/dist/chunk-DHUFVGJR.cjs.map +1 -0
- package/dist/chunk-DZUJEN5N.cjs +32 -0
- package/dist/chunk-DZUJEN5N.cjs.map +1 -0
- package/dist/chunk-GDK2HJTF.cjs +1346 -0
- package/dist/chunk-GDK2HJTF.cjs.map +1 -0
- package/dist/chunk-KG3GNTYI.js +1339 -0
- package/dist/chunk-KG3GNTYI.js.map +1 -0
- package/dist/chunk-KV5SP7RP.cjs +114 -0
- package/dist/chunk-KV5SP7RP.cjs.map +1 -0
- package/dist/chunk-NPIKZKLU.cjs +27 -0
- package/dist/chunk-NPIKZKLU.cjs.map +1 -0
- package/dist/chunk-OAAZZUYL.cjs +1517 -0
- package/dist/chunk-OAAZZUYL.cjs.map +1 -0
- package/dist/chunk-P4BYTXZ7.js +98 -0
- package/dist/chunk-P4BYTXZ7.js.map +1 -0
- package/dist/chunk-PDM2KR7T.js +392 -0
- package/dist/chunk-PDM2KR7T.js.map +1 -0
- package/dist/chunk-QOZI2HY5.js +24 -0
- package/dist/chunk-QOZI2HY5.js.map +1 -0
- package/dist/chunk-RFBLNKW6.js +20035 -0
- package/dist/chunk-RFBLNKW6.js.map +1 -0
- package/dist/chunk-UELAE75E.cjs +408 -0
- package/dist/chunk-UELAE75E.cjs.map +1 -0
- package/dist/client-9Rwj21T9.d.cts +77 -0
- package/dist/client-Bskq7sc_.d.ts +77 -0
- package/dist/client-CLhQragb.d.cts +446 -0
- package/dist/client-CLhQragb.d.ts +446 -0
- package/dist/error-CsVoUTY8.d.cts +95 -0
- package/dist/error-CsVoUTY8.d.ts +95 -0
- package/dist/index-BrGSfyXq.d.cts +79 -0
- package/dist/index-zPcVLagO.d.ts +79 -0
- package/dist/index.cjs +1675 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +9943 -0
- package/dist/index.d.ts +9943 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/inference/index.cjs +70 -0
- package/dist/inference/index.cjs.map +1 -0
- package/dist/inference/index.d.cts +80 -0
- package/dist/inference/index.d.ts +80 -0
- package/dist/inference/index.js +5 -0
- package/dist/inference/index.js.map +1 -0
- package/dist/pagination/index.cjs +34 -0
- package/dist/pagination/index.cjs.map +1 -0
- package/dist/pagination/index.d.cts +6 -0
- package/dist/pagination/index.d.ts +6 -0
- package/dist/pagination/index.js +5 -0
- package/dist/pagination/index.js.map +1 -0
- package/dist/sync-engine-DVKQO-1-.d.cts +423 -0
- package/dist/sync-engine-cLwTqdjv.d.ts +423 -0
- package/dist/types-2RotD0Ab.d.cts +102 -0
- package/dist/types-2RotD0Ab.d.ts +102 -0
- package/package.json +143 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { Signal, DestroyRef, EnvironmentProviders } from '@angular/core';
|
|
2
|
+
import { D as DvinaSdkOptions } from '../../client-Bskq7sc_.js';
|
|
3
|
+
import { D as DvinaQueryRef } from '../../types-2RotD0Ab.js';
|
|
4
|
+
import '../../error-CsVoUTY8.js';
|
|
5
|
+
import '../../sync-engine-cLwTqdjv.js';
|
|
6
|
+
import 'graphql';
|
|
7
|
+
import 'dexie';
|
|
8
|
+
import '../../client-CLhQragb.js';
|
|
9
|
+
import '../../billing/index.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Angular adapter for @dvina/sdk.
|
|
13
|
+
*
|
|
14
|
+
* Provides primitives for integrating the Dvina SDK with Angular's DI and signal system:
|
|
15
|
+
*
|
|
16
|
+
* - {@link provideDvina} — register a singleton `DvinaSdk` instance in the Angular DI system
|
|
17
|
+
* - {@link toSignal} — lightweight converter from `DvinaQueryRef` to a readonly `Signal`
|
|
18
|
+
* - {@link dvnResource} — full resource primitive inspired by Angular's `resource()` API,
|
|
19
|
+
* supporting both `Promise` and `AsyncIterable` loaders with reactive params tracking,
|
|
20
|
+
* status/error signals, and automatic cleanup.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* import { provideDvina, toSignal, dvnResource } from '@dvina/sdk/angular';
|
|
25
|
+
* import { DvinaSdk } from '@dvina/sdk';
|
|
26
|
+
*
|
|
27
|
+
* // app.config.ts
|
|
28
|
+
* export const appConfig: ApplicationConfig = {
|
|
29
|
+
* providers: [
|
|
30
|
+
* provideDvina(() => ({
|
|
31
|
+
* getToken: (opts) => inject(AuthService).getAccessTokenSilently(opts),
|
|
32
|
+
* language: () => inject(TranslocoService).getActiveLang(),
|
|
33
|
+
* })),
|
|
34
|
+
* ],
|
|
35
|
+
* };
|
|
36
|
+
*
|
|
37
|
+
* // component.ts
|
|
38
|
+
* @Component({ ... })
|
|
39
|
+
* class ReportsComponent {
|
|
40
|
+
* private sdk = inject(DvinaSdk);
|
|
41
|
+
*
|
|
42
|
+
* // Simple signal conversion
|
|
43
|
+
* reports = toSignal(this.sdk.reports({ first: 20 }).watch());
|
|
44
|
+
*
|
|
45
|
+
* // Full resource with status tracking
|
|
46
|
+
* reportResource = dvnResource({
|
|
47
|
+
* params: () => ({ first: 20 }),
|
|
48
|
+
* loader: ({ params }) => this.sdk.reports(params).watch(),
|
|
49
|
+
* });
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @packageDocumentation
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
interface ToSignalOptions {
|
|
57
|
+
/**
|
|
58
|
+
* Optional DestroyRef for automatic cleanup. If not provided, `inject(DestroyRef)`
|
|
59
|
+
* is called — which requires an injection context (constructor, field initializer,
|
|
60
|
+
* or `runInInjectionContext`).
|
|
61
|
+
*
|
|
62
|
+
* Pass explicitly when calling outside an injection context.
|
|
63
|
+
*
|
|
64
|
+
* Follows the same convention as Angular's `takeUntilDestroyed()`.
|
|
65
|
+
*/
|
|
66
|
+
destroyRef?: DestroyRef;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Convert a `DvinaQueryRef` (AsyncIterable) into a readonly Angular Signal.
|
|
70
|
+
*
|
|
71
|
+
* The signal updates automatically whenever the underlying Dexie-based SyncEngine emits new data.
|
|
72
|
+
* Cleanup (disposal of the async iterable) happens automatically when the component/service
|
|
73
|
+
* is destroyed via `DestroyRef`.
|
|
74
|
+
*
|
|
75
|
+
* Must be called in an **injection context** (field initializer, constructor,
|
|
76
|
+
* or `runInInjectionContext`) unless `options.destroyRef` is provided explicitly.
|
|
77
|
+
*
|
|
78
|
+
* @param ref - A DvinaQueryRef returned by `.watch()` on any SDK query
|
|
79
|
+
* @param options - Optional configuration
|
|
80
|
+
* @returns A readonly Signal that reflects the latest value from the query ref
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* // Field initializer (most common)
|
|
85
|
+
* reports = toSignal(this.sdk.reports({ first: 20 }).watch());
|
|
86
|
+
*
|
|
87
|
+
* // With explicit DestroyRef (for use outside injection context)
|
|
88
|
+
* const destroyRef = inject(DestroyRef);
|
|
89
|
+
* // ... later, in a method:
|
|
90
|
+
* this.reports = toSignal(ref, { destroyRef });
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
declare function toSignal<T>(ref: DvinaQueryRef<T>, options?: ToSignalOptions): Signal<T | undefined>;
|
|
94
|
+
/**
|
|
95
|
+
* Status of a {@link DvnResourceRef}.
|
|
96
|
+
*
|
|
97
|
+
* Mirrors Angular's `ResourceStatus` with the statuses relevant to
|
|
98
|
+
* Dvina SDK operations.
|
|
99
|
+
*
|
|
100
|
+
* | Status | `value()` | Description |
|
|
101
|
+
* |---------------|-------------------|-------------|
|
|
102
|
+
* | `'idle'` | `undefined` | `params` returned `undefined`; loader has not run. |
|
|
103
|
+
* | `'loading'` | `undefined` | Loader is running for the first time. |
|
|
104
|
+
* | `'reloading'` | Previous value | Loader is re-running (params changed or `reload()` called). |
|
|
105
|
+
* | `'resolved'` | Resolved value | Loader completed successfully. |
|
|
106
|
+
* | `'error'` | `undefined` | Loader threw an error. |
|
|
107
|
+
*/
|
|
108
|
+
type DvnResourceStatus = 'idle' | 'loading' | 'reloading' | 'resolved' | 'error';
|
|
109
|
+
/**
|
|
110
|
+
* The object returned by {@link dvnResource}.
|
|
111
|
+
*
|
|
112
|
+
* Provides reactive access to the loader's value, status, and error,
|
|
113
|
+
* plus imperative `reload()` and `destroy()` methods.
|
|
114
|
+
*/
|
|
115
|
+
interface DvnResourceRef<TResult> {
|
|
116
|
+
/** The most recently resolved value, or `undefined` while loading / on error / idle. */
|
|
117
|
+
readonly value: Signal<TResult | undefined>;
|
|
118
|
+
/** Current lifecycle status of the resource. */
|
|
119
|
+
readonly status: Signal<DvnResourceStatus>;
|
|
120
|
+
/** `true` when status is `'loading'` or `'reloading'`. */
|
|
121
|
+
readonly isLoading: Signal<boolean>;
|
|
122
|
+
/** The last error thrown by the loader, or `undefined`. */
|
|
123
|
+
readonly error: Signal<Error | undefined>;
|
|
124
|
+
/** Whether `value()` is not `undefined`. */
|
|
125
|
+
hasValue(): boolean;
|
|
126
|
+
/** Re-run the loader with the current params. Status transitions to `'reloading'`. */
|
|
127
|
+
reload(): void;
|
|
128
|
+
/** Abort / dispose any active loader and reset to `'idle'`. */
|
|
129
|
+
destroy(): void;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* A loader function that returns either a `Promise` (one-shot fetch) or an
|
|
133
|
+
* `AsyncIterable` / `DvinaQueryRef` (live stream).
|
|
134
|
+
*
|
|
135
|
+
* The runtime inspects the return value:
|
|
136
|
+
* - If it has `Symbol.asyncIterator` → consumed as a stream (`for await…of`)
|
|
137
|
+
* - Otherwise → awaited as a `Promise`
|
|
138
|
+
*/
|
|
139
|
+
type DvnResourceLoader<TParams, TResult> = (ctx: DvnResourceLoaderCtx<TParams>) => Promise<TResult> | AsyncIterable<TResult>;
|
|
140
|
+
/**
|
|
141
|
+
* Context object passed to the loader function.
|
|
142
|
+
*
|
|
143
|
+
* When the resource has `params`, `ctx.params` contains the current
|
|
144
|
+
* non-`undefined` params value. `abortSignal` can be forwarded to `fetch()`
|
|
145
|
+
* or any other cancellable API.
|
|
146
|
+
*/
|
|
147
|
+
interface DvnResourceLoaderCtx<TParams> {
|
|
148
|
+
/** Current params value (never `undefined`). Only present when `params` is configured. */
|
|
149
|
+
params: TParams;
|
|
150
|
+
/** Aborted when params change, `reload()` is called, or the resource is destroyed. */
|
|
151
|
+
abortSignal: AbortSignal;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Create a Dvina resource **without reactive params**.
|
|
155
|
+
*
|
|
156
|
+
* The loader runs immediately and can be re-triggered via `reload()`.
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```typescript
|
|
160
|
+
* config = dvnResource({
|
|
161
|
+
* loader: () => this.sdk.config().fetch(),
|
|
162
|
+
* });
|
|
163
|
+
* ```
|
|
164
|
+
*/
|
|
165
|
+
declare function dvnResource<TResult>(config: {
|
|
166
|
+
params?: never;
|
|
167
|
+
loader: (ctx: {
|
|
168
|
+
abortSignal: AbortSignal;
|
|
169
|
+
}) => Promise<TResult> | AsyncIterable<TResult>;
|
|
170
|
+
}): DvnResourceRef<TResult>;
|
|
171
|
+
/**
|
|
172
|
+
* Create a Dvina resource **with reactive params**.
|
|
173
|
+
*
|
|
174
|
+
* The loader re-runs every time `params()` produces a new non-`undefined`
|
|
175
|
+
* value. When `params()` returns `undefined` the resource enters `'idle'`.
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* ```typescript
|
|
179
|
+
* // Promise-based (one-shot fetch)
|
|
180
|
+
* userResource = dvnResource({
|
|
181
|
+
* params: () => ({ id: this.userId() }),
|
|
182
|
+
* loader: ({ params }) => this.sdk.user(params).fetch(),
|
|
183
|
+
* });
|
|
184
|
+
*
|
|
185
|
+
* // AsyncIterable-based (live query)
|
|
186
|
+
* reportsResource = dvnResource({
|
|
187
|
+
* params: () => ({ first: 20 }),
|
|
188
|
+
* loader: ({ params }) => this.sdk.reports(params).watch(),
|
|
189
|
+
* });
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
declare function dvnResource<TParams, TResult>(config: {
|
|
193
|
+
params: () => TParams | undefined;
|
|
194
|
+
loader: (ctx: DvnResourceLoaderCtx<TParams>) => Promise<TResult> | AsyncIterable<TResult>;
|
|
195
|
+
}): DvnResourceRef<TResult>;
|
|
196
|
+
/**
|
|
197
|
+
* Register a singleton {@link DvinaSdk} instance in the Angular dependency injection system.
|
|
198
|
+
*
|
|
199
|
+
* Accepts either a static options object or a **factory function**. The factory
|
|
200
|
+
* variant runs inside an injection context, so Angular's `inject()` can be used
|
|
201
|
+
* to resolve auth services, language providers, and other dependencies.
|
|
202
|
+
*
|
|
203
|
+
* The SDK instance is automatically destroyed (WebSocket terminated, SSE stopped,
|
|
204
|
+
* IndexedDB closed) when the environment injector is torn down.
|
|
205
|
+
*
|
|
206
|
+
* @param optionsOrFactory - A {@link DvinaSdkOptions} object, or a factory
|
|
207
|
+
* function that returns one. The factory is called within an injection context.
|
|
208
|
+
* @returns `EnvironmentProviders` to be added to the application config.
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* ```typescript
|
|
212
|
+
* // app.config.ts
|
|
213
|
+
* import { provideDvina } from '@dvina/sdk/angular';
|
|
214
|
+
*
|
|
215
|
+
* export const appConfig: ApplicationConfig = {
|
|
216
|
+
* providers: [
|
|
217
|
+
* provideDvina(() => {
|
|
218
|
+
* const auth = inject(AuthService);
|
|
219
|
+
* const transloco = inject(TranslocoService);
|
|
220
|
+
*
|
|
221
|
+
* return {
|
|
222
|
+
* getToken: (opts) => auth.getAccessTokenSilently(opts),
|
|
223
|
+
* language: () => transloco.getActiveLang(),
|
|
224
|
+
* };
|
|
225
|
+
* }),
|
|
226
|
+
* ],
|
|
227
|
+
* };
|
|
228
|
+
* ```
|
|
229
|
+
*
|
|
230
|
+
* @example
|
|
231
|
+
* ```typescript
|
|
232
|
+
* // Static token (scripts, tests)
|
|
233
|
+
* provideDvina({
|
|
234
|
+
* token: 'my-static-token',
|
|
235
|
+
* baseUrl: 'localhost:4000',
|
|
236
|
+
* })
|
|
237
|
+
* ```
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* ```typescript
|
|
241
|
+
* // Consuming the SDK in a service
|
|
242
|
+
* import { DvinaSdk } from '@dvina/sdk';
|
|
243
|
+
*
|
|
244
|
+
* @Injectable({ providedIn: 'root' })
|
|
245
|
+
* export class ReportService {
|
|
246
|
+
* private readonly sdk = inject(DvinaSdk);
|
|
247
|
+
*
|
|
248
|
+
* reports = toSignal(this.sdk.reports({ first: 20 }).watch());
|
|
249
|
+
* }
|
|
250
|
+
* ```
|
|
251
|
+
*/
|
|
252
|
+
declare function provideDvina(optionsOrFactory: DvinaSdkOptions | (() => DvinaSdkOptions)): EnvironmentProviders;
|
|
253
|
+
|
|
254
|
+
export { type DvnResourceLoader, type DvnResourceLoaderCtx, type DvnResourceRef, type DvnResourceStatus, type ToSignalOptions, dvnResource, provideDvina, toSignal };
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { DvinaSdk } from '../../chunk-RFBLNKW6.js';
|
|
2
|
+
import '../../chunk-P4BYTXZ7.js';
|
|
3
|
+
import '../../chunk-PDM2KR7T.js';
|
|
4
|
+
import '../../chunk-KG3GNTYI.js';
|
|
5
|
+
import '../../chunk-7XBJ77RJ.js';
|
|
6
|
+
import '../../chunk-7JUBGIQ2.js';
|
|
7
|
+
import '../../chunk-5WRI5ZAA.js';
|
|
8
|
+
import { inject, DestroyRef, signal, effect, computed, makeEnvironmentProviders, untracked } from '@angular/core';
|
|
9
|
+
|
|
10
|
+
function toSignal(ref, options) {
|
|
11
|
+
const destroyRef = options?.destroyRef ?? inject(DestroyRef);
|
|
12
|
+
const sig = signal(ref.current);
|
|
13
|
+
let running = true;
|
|
14
|
+
(async () => {
|
|
15
|
+
try {
|
|
16
|
+
for await (const value of ref) {
|
|
17
|
+
if (!running) break;
|
|
18
|
+
sig.set(value);
|
|
19
|
+
}
|
|
20
|
+
} catch {
|
|
21
|
+
}
|
|
22
|
+
})();
|
|
23
|
+
destroyRef.onDestroy(() => {
|
|
24
|
+
running = false;
|
|
25
|
+
ref.dispose();
|
|
26
|
+
});
|
|
27
|
+
return sig.asReadonly();
|
|
28
|
+
}
|
|
29
|
+
function dvnResource(config) {
|
|
30
|
+
const destroyRef = inject(DestroyRef);
|
|
31
|
+
const hasParams = "params" in config && config.params !== void 0;
|
|
32
|
+
const _value = signal(void 0);
|
|
33
|
+
const _status = signal("idle");
|
|
34
|
+
const _error = signal(void 0);
|
|
35
|
+
let abortController = null;
|
|
36
|
+
let streamRunning = false;
|
|
37
|
+
let currentQueryRef = null;
|
|
38
|
+
const cleanup = () => {
|
|
39
|
+
abortController?.abort();
|
|
40
|
+
abortController = null;
|
|
41
|
+
streamRunning = false;
|
|
42
|
+
currentQueryRef?.dispose();
|
|
43
|
+
currentQueryRef = null;
|
|
44
|
+
};
|
|
45
|
+
const load = (params) => {
|
|
46
|
+
cleanup();
|
|
47
|
+
_error.set(void 0);
|
|
48
|
+
const hadValue = untracked(() => _value() !== void 0);
|
|
49
|
+
_status.set(hadValue ? "reloading" : "loading");
|
|
50
|
+
abortController = new AbortController();
|
|
51
|
+
const ctx = hasParams ? { params, abortSignal: abortController.signal } : { abortSignal: abortController.signal };
|
|
52
|
+
const result = config.loader(ctx);
|
|
53
|
+
if (isAsyncIterable(result)) {
|
|
54
|
+
if (hasDispose(result)) {
|
|
55
|
+
currentQueryRef = result;
|
|
56
|
+
}
|
|
57
|
+
streamRunning = true;
|
|
58
|
+
const capturedController = abortController;
|
|
59
|
+
(async () => {
|
|
60
|
+
try {
|
|
61
|
+
for await (const val of result) {
|
|
62
|
+
if (!streamRunning) break;
|
|
63
|
+
_value.set(val);
|
|
64
|
+
_status.set("resolved");
|
|
65
|
+
_error.set(void 0);
|
|
66
|
+
}
|
|
67
|
+
} catch (err) {
|
|
68
|
+
if (!streamRunning || capturedController.signal.aborted) return;
|
|
69
|
+
const wrapped = err instanceof Error ? err : new Error(String(err));
|
|
70
|
+
_error.set(wrapped);
|
|
71
|
+
_status.set("error");
|
|
72
|
+
}
|
|
73
|
+
})();
|
|
74
|
+
} else {
|
|
75
|
+
const capturedController = abortController;
|
|
76
|
+
result.then(
|
|
77
|
+
(val) => {
|
|
78
|
+
if (capturedController.signal.aborted) return;
|
|
79
|
+
_value.set(val);
|
|
80
|
+
_status.set("resolved");
|
|
81
|
+
},
|
|
82
|
+
(err) => {
|
|
83
|
+
if (capturedController.signal.aborted) return;
|
|
84
|
+
if (err instanceof DOMException && err.name === "AbortError") return;
|
|
85
|
+
const wrapped = err instanceof Error ? err : new Error(String(err));
|
|
86
|
+
_error.set(wrapped);
|
|
87
|
+
_status.set("error");
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
if (hasParams) {
|
|
93
|
+
effect(() => {
|
|
94
|
+
const params = config.params();
|
|
95
|
+
if (params !== void 0) {
|
|
96
|
+
load(params);
|
|
97
|
+
} else {
|
|
98
|
+
cleanup();
|
|
99
|
+
_value.set(void 0);
|
|
100
|
+
_status.set("idle");
|
|
101
|
+
_error.set(void 0);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
} else {
|
|
105
|
+
load();
|
|
106
|
+
}
|
|
107
|
+
destroyRef.onDestroy(() => {
|
|
108
|
+
cleanup();
|
|
109
|
+
});
|
|
110
|
+
const status = _status.asReadonly();
|
|
111
|
+
const isLoading = computed(() => {
|
|
112
|
+
const s = _status();
|
|
113
|
+
return s === "loading" || s === "reloading";
|
|
114
|
+
});
|
|
115
|
+
return {
|
|
116
|
+
value: _value.asReadonly(),
|
|
117
|
+
status,
|
|
118
|
+
isLoading,
|
|
119
|
+
error: _error.asReadonly(),
|
|
120
|
+
hasValue: () => _value() !== void 0,
|
|
121
|
+
reload() {
|
|
122
|
+
if (hasParams) {
|
|
123
|
+
const params = config.params();
|
|
124
|
+
if (params !== void 0) {
|
|
125
|
+
load(params);
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
load();
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
destroy() {
|
|
132
|
+
cleanup();
|
|
133
|
+
_value.set(void 0);
|
|
134
|
+
_status.set("idle");
|
|
135
|
+
_error.set(void 0);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function isAsyncIterable(value) {
|
|
140
|
+
return value != null && typeof value === "object" && Symbol.asyncIterator in value;
|
|
141
|
+
}
|
|
142
|
+
function hasDispose(value) {
|
|
143
|
+
return value != null && typeof value === "object" && "dispose" in value && typeof value.dispose === "function";
|
|
144
|
+
}
|
|
145
|
+
function provideDvina(optionsOrFactory) {
|
|
146
|
+
return makeEnvironmentProviders([
|
|
147
|
+
{
|
|
148
|
+
provide: DvinaSdk,
|
|
149
|
+
useFactory: () => {
|
|
150
|
+
const options = typeof optionsOrFactory === "function" ? optionsOrFactory() : optionsOrFactory;
|
|
151
|
+
return new DvinaSdk(options);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
]);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export { dvnResource, provideDvina, toSignal };
|
|
158
|
+
//# sourceMappingURL=index.js.map
|
|
159
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/adapters/angular/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Angular adapter for @dvina/sdk.\n *\n * Provides primitives for integrating the Dvina SDK with Angular's DI and signal system:\n *\n * - {@link provideDvina} — register a singleton `DvinaSdk` instance in the Angular DI system\n * - {@link toSignal} — lightweight converter from `DvinaQueryRef` to a readonly `Signal`\n * - {@link dvnResource} — full resource primitive inspired by Angular's `resource()` API,\n * supporting both `Promise` and `AsyncIterable` loaders with reactive params tracking,\n * status/error signals, and automatic cleanup.\n *\n * @example\n * ```typescript\n * import { provideDvina, toSignal, dvnResource } from '@dvina/sdk/angular';\n * import { DvinaSdk } from '@dvina/sdk';\n *\n * // app.config.ts\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideDvina(() => ({\n * getToken: (opts) => inject(AuthService).getAccessTokenSilently(opts),\n * language: () => inject(TranslocoService).getActiveLang(),\n * })),\n * ],\n * };\n *\n * // component.ts\n * @Component({ ... })\n * class ReportsComponent {\n * private sdk = inject(DvinaSdk);\n *\n * // Simple signal conversion\n * reports = toSignal(this.sdk.reports({ first: 20 }).watch());\n *\n * // Full resource with status tracking\n * reportResource = dvnResource({\n * params: () => ({ first: 20 }),\n * loader: ({ params }) => this.sdk.reports(params).watch(),\n * });\n * }\n * ```\n *\n * @packageDocumentation\n */\n\nimport {\n\ttype EnvironmentProviders,\n\ttype Signal,\n\tcomputed,\n\tDestroyRef,\n\teffect,\n\tinject,\n\tmakeEnvironmentProviders,\n\tsignal,\n\tuntracked,\n} from '@angular/core';\nimport { DvinaSdk } from '../../_generated_sdk.js';\nimport type { DvinaSdkOptions } from '../../client.js';\nimport type { DvinaQueryRef } from '../../types.js';\n\nexport interface ToSignalOptions {\n\t/**\n\t * Optional DestroyRef for automatic cleanup. If not provided, `inject(DestroyRef)`\n\t * is called — which requires an injection context (constructor, field initializer,\n\t * or `runInInjectionContext`).\n\t *\n\t * Pass explicitly when calling outside an injection context.\n\t *\n\t * Follows the same convention as Angular's `takeUntilDestroyed()`.\n\t */\n\tdestroyRef?: DestroyRef;\n}\n\n/**\n * Convert a `DvinaQueryRef` (AsyncIterable) into a readonly Angular Signal.\n *\n * The signal updates automatically whenever the underlying Dexie-based SyncEngine emits new data.\n * Cleanup (disposal of the async iterable) happens automatically when the component/service\n * is destroyed via `DestroyRef`.\n *\n * Must be called in an **injection context** (field initializer, constructor,\n * or `runInInjectionContext`) unless `options.destroyRef` is provided explicitly.\n *\n * @param ref - A DvinaQueryRef returned by `.watch()` on any SDK query\n * @param options - Optional configuration\n * @returns A readonly Signal that reflects the latest value from the query ref\n *\n * @example\n * ```typescript\n * // Field initializer (most common)\n * reports = toSignal(this.sdk.reports({ first: 20 }).watch());\n *\n * // With explicit DestroyRef (for use outside injection context)\n * const destroyRef = inject(DestroyRef);\n * // ... later, in a method:\n * this.reports = toSignal(ref, { destroyRef });\n * ```\n */\nexport function toSignal<T>(ref: DvinaQueryRef<T>, options?: ToSignalOptions): Signal<T | undefined> {\n\tconst destroyRef = options?.destroyRef ?? inject(DestroyRef);\n\tconst sig = signal<T | undefined>(ref.current);\n\n\tlet running = true;\n\n\t// Consume the AsyncIterable in the background, updating the signal on each emission\n\t(async () => {\n\t\ttry {\n\t\t\tfor await (const value of ref) {\n\t\t\t\tif (!running) break;\n\t\t\t\tsig.set(value);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Query ref was disposed or errored — signal keeps its last value\n\t\t}\n\t})();\n\n\t// Cleanup when the Angular component/service is destroyed\n\tdestroyRef.onDestroy(() => {\n\t\trunning = false;\n\t\tref.dispose();\n\t});\n\n\treturn sig.asReadonly();\n}\n\n// ---------------------------------------------------------------------------\n// dvnResource\n// ---------------------------------------------------------------------------\n\n/**\n * Status of a {@link DvnResourceRef}.\n *\n * Mirrors Angular's `ResourceStatus` with the statuses relevant to\n * Dvina SDK operations.\n *\n * | Status | `value()` | Description |\n * |---------------|-------------------|-------------|\n * | `'idle'` | `undefined` | `params` returned `undefined`; loader has not run. |\n * | `'loading'` | `undefined` | Loader is running for the first time. |\n * | `'reloading'` | Previous value | Loader is re-running (params changed or `reload()` called). |\n * | `'resolved'` | Resolved value | Loader completed successfully. |\n * | `'error'` | `undefined` | Loader threw an error. |\n */\nexport type DvnResourceStatus = 'idle' | 'loading' | 'reloading' | 'resolved' | 'error';\n\n/**\n * The object returned by {@link dvnResource}.\n *\n * Provides reactive access to the loader's value, status, and error,\n * plus imperative `reload()` and `destroy()` methods.\n */\nexport interface DvnResourceRef<TResult> {\n\t/** The most recently resolved value, or `undefined` while loading / on error / idle. */\n\treadonly value: Signal<TResult | undefined>;\n\t/** Current lifecycle status of the resource. */\n\treadonly status: Signal<DvnResourceStatus>;\n\t/** `true` when status is `'loading'` or `'reloading'`. */\n\treadonly isLoading: Signal<boolean>;\n\t/** The last error thrown by the loader, or `undefined`. */\n\treadonly error: Signal<Error | undefined>;\n\t/** Whether `value()` is not `undefined`. */\n\thasValue(): boolean;\n\t/** Re-run the loader with the current params. Status transitions to `'reloading'`. */\n\treload(): void;\n\t/** Abort / dispose any active loader and reset to `'idle'`. */\n\tdestroy(): void;\n}\n\n/**\n * A loader function that returns either a `Promise` (one-shot fetch) or an\n * `AsyncIterable` / `DvinaQueryRef` (live stream).\n *\n * The runtime inspects the return value:\n * - If it has `Symbol.asyncIterator` → consumed as a stream (`for await…of`)\n * - Otherwise → awaited as a `Promise`\n */\nexport type DvnResourceLoader<TParams, TResult> = (\n\tctx: DvnResourceLoaderCtx<TParams>,\n) => Promise<TResult> | AsyncIterable<TResult>;\n\n/**\n * Context object passed to the loader function.\n *\n * When the resource has `params`, `ctx.params` contains the current\n * non-`undefined` params value. `abortSignal` can be forwarded to `fetch()`\n * or any other cancellable API.\n */\nexport interface DvnResourceLoaderCtx<TParams> {\n\t/** Current params value (never `undefined`). Only present when `params` is configured. */\n\tparams: TParams;\n\t/** Aborted when params change, `reload()` is called, or the resource is destroyed. */\n\tabortSignal: AbortSignal;\n}\n\n// ---- Overloads ------------------------------------------------------------\n\n/**\n * Create a Dvina resource **without reactive params**.\n *\n * The loader runs immediately and can be re-triggered via `reload()`.\n *\n * @example\n * ```typescript\n * config = dvnResource({\n * loader: () => this.sdk.config().fetch(),\n * });\n * ```\n */\nexport function dvnResource<TResult>(config: {\n\tparams?: never;\n\tloader: (ctx: { abortSignal: AbortSignal }) => Promise<TResult> | AsyncIterable<TResult>;\n}): DvnResourceRef<TResult>;\n\n/**\n * Create a Dvina resource **with reactive params**.\n *\n * The loader re-runs every time `params()` produces a new non-`undefined`\n * value. When `params()` returns `undefined` the resource enters `'idle'`.\n *\n * @example\n * ```typescript\n * // Promise-based (one-shot fetch)\n * userResource = dvnResource({\n * params: () => ({ id: this.userId() }),\n * loader: ({ params }) => this.sdk.user(params).fetch(),\n * });\n *\n * // AsyncIterable-based (live query)\n * reportsResource = dvnResource({\n * params: () => ({ first: 20 }),\n * loader: ({ params }) => this.sdk.reports(params).watch(),\n * });\n * ```\n */\nexport function dvnResource<TParams, TResult>(config: {\n\tparams: () => TParams | undefined;\n\tloader: (ctx: DvnResourceLoaderCtx<TParams>) => Promise<TResult> | AsyncIterable<TResult>;\n}): DvnResourceRef<TResult>;\n\n// ---- Implementation -------------------------------------------------------\n\nexport function dvnResource<TParams, TResult>(config: {\n\tparams?: () => TParams | undefined;\n\tloader: (ctx: any) => Promise<TResult> | AsyncIterable<TResult>;\n}): DvnResourceRef<TResult> {\n\tconst destroyRef = inject(DestroyRef);\n\tconst hasParams = 'params' in config && config.params !== undefined;\n\n\t// Internal writable signals\n\tconst _value = signal<TResult | undefined>(undefined);\n\tconst _status = signal<DvnResourceStatus>('idle');\n\tconst _error = signal<Error | undefined>(undefined);\n\n\t// Cleanup handles\n\tlet abortController: AbortController | null = null;\n\tlet streamRunning = false;\n\tlet currentQueryRef: { dispose(): void } | null = null;\n\n\t/**\n\t * Cancel any in-flight loader (Promise abort + AsyncIterable dispose).\n\t */\n\tconst cleanup = () => {\n\t\tabortController?.abort();\n\t\tabortController = null;\n\t\tstreamRunning = false;\n\t\tcurrentQueryRef?.dispose();\n\t\tcurrentQueryRef = null;\n\t};\n\n\t/**\n\t * Run the loader for the given params.\n\t */\n\tconst load = (params?: Exclude<TParams, undefined>) => {\n\t\tcleanup();\n\t\t_error.set(undefined);\n\n\t\tconst hadValue = untracked(() => _value() !== undefined);\n\t\t_status.set(hadValue ? 'reloading' : 'loading');\n\n\t\tabortController = new AbortController();\n\t\tconst ctx = hasParams\n\t\t\t? { params: params!, abortSignal: abortController.signal }\n\t\t\t: { abortSignal: abortController.signal };\n\n\t\tconst result = config.loader(ctx);\n\n\t\tif (isAsyncIterable(result)) {\n\t\t\t// --- Stream mode (AsyncIterable / DvinaQueryRef) ---\n\t\t\t// Keep a reference for disposal if it quacks like a DvinaQueryRef\n\t\t\tif (hasDispose(result)) {\n\t\t\t\tcurrentQueryRef = result;\n\t\t\t}\n\n\t\t\tstreamRunning = true;\n\t\t\tconst capturedController = abortController;\n\n\t\t\t(async () => {\n\t\t\t\ttry {\n\t\t\t\t\tfor await (const val of result) {\n\t\t\t\t\t\tif (!streamRunning) break;\n\t\t\t\t\t\t_value.set(val);\n\t\t\t\t\t\t_status.set('resolved');\n\t\t\t\t\t\t_error.set(undefined);\n\t\t\t\t\t}\n\t\t\t\t} catch (err: unknown) {\n\t\t\t\t\tif (!streamRunning || capturedController.signal.aborted) return;\n\t\t\t\t\tconst wrapped = err instanceof Error ? err : new Error(String(err));\n\t\t\t\t\t_error.set(wrapped);\n\t\t\t\t\t_status.set('error');\n\t\t\t\t}\n\t\t\t})();\n\t\t} else {\n\t\t\t// --- Promise mode (one-shot) ---\n\t\t\tconst capturedController = abortController;\n\n\t\t\t(result as Promise<TResult>).then(\n\t\t\t\t(val) => {\n\t\t\t\t\tif (capturedController.signal.aborted) return;\n\t\t\t\t\t_value.set(val);\n\t\t\t\t\t_status.set('resolved');\n\t\t\t\t},\n\t\t\t\t(err: unknown) => {\n\t\t\t\t\tif (capturedController.signal.aborted) return;\n\t\t\t\t\tif (err instanceof DOMException && err.name === 'AbortError') return;\n\t\t\t\t\tconst wrapped = err instanceof Error ? err : new Error(String(err));\n\t\t\t\t\t_error.set(wrapped);\n\t\t\t\t\t_status.set('error');\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t};\n\n\t// ---- Reactive params tracking (same pattern as yokaResource) ----\n\n\tif (hasParams) {\n\t\teffect(() => {\n\t\t\tconst params = config.params!();\n\n\t\t\tif (params !== undefined) {\n\t\t\t\tload(params as Exclude<TParams, undefined>);\n\t\t\t} else {\n\t\t\t\tcleanup();\n\t\t\t\t_value.set(undefined);\n\t\t\t\t_status.set('idle');\n\t\t\t\t_error.set(undefined);\n\t\t\t}\n\t\t});\n\t} else {\n\t\t// No params — run immediately\n\t\tload();\n\t}\n\n\t// Auto-cleanup on destroy\n\tdestroyRef.onDestroy(() => {\n\t\tcleanup();\n\t});\n\n\t// ---- Public API ----\n\n\tconst status = _status.asReadonly();\n\tconst isLoading = computed(() => {\n\t\tconst s = _status();\n\t\treturn s === 'loading' || s === 'reloading';\n\t});\n\n\treturn {\n\t\tvalue: _value.asReadonly(),\n\t\tstatus,\n\t\tisLoading,\n\t\terror: _error.asReadonly(),\n\t\thasValue: () => _value() !== undefined,\n\n\t\treload() {\n\t\t\tif (hasParams) {\n\t\t\t\tconst params = config.params!();\n\t\t\t\tif (params !== undefined) {\n\t\t\t\t\tload(params as Exclude<TParams, undefined>);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tload();\n\t\t\t}\n\t\t},\n\n\t\tdestroy() {\n\t\t\tcleanup();\n\t\t\t_value.set(undefined);\n\t\t\t_status.set('idle');\n\t\t\t_error.set(undefined);\n\t\t},\n\t};\n}\n\n// ---- Type guards ----------------------------------------------------------\n\nfunction isAsyncIterable(value: unknown): value is AsyncIterable<unknown> {\n\treturn value != null && typeof value === 'object' && Symbol.asyncIterator in value;\n}\n\nfunction hasDispose(value: unknown): value is { dispose(): void } {\n\treturn (\n\t\tvalue != null && typeof value === 'object' && 'dispose' in value && typeof (value as any).dispose === 'function'\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// provideDvina\n// ---------------------------------------------------------------------------\n\n/**\n * Register a singleton {@link DvinaSdk} instance in the Angular dependency injection system.\n *\n * Accepts either a static options object or a **factory function**. The factory\n * variant runs inside an injection context, so Angular's `inject()` can be used\n * to resolve auth services, language providers, and other dependencies.\n *\n * The SDK instance is automatically destroyed (WebSocket terminated, SSE stopped,\n * IndexedDB closed) when the environment injector is torn down.\n *\n * @param optionsOrFactory - A {@link DvinaSdkOptions} object, or a factory\n * function that returns one. The factory is called within an injection context.\n * @returns `EnvironmentProviders` to be added to the application config.\n *\n * @example\n * ```typescript\n * // app.config.ts\n * import { provideDvina } from '@dvina/sdk/angular';\n *\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideDvina(() => {\n * const auth = inject(AuthService);\n * const transloco = inject(TranslocoService);\n *\n * return {\n * getToken: (opts) => auth.getAccessTokenSilently(opts),\n * language: () => transloco.getActiveLang(),\n * };\n * }),\n * ],\n * };\n * ```\n *\n * @example\n * ```typescript\n * // Static token (scripts, tests)\n * provideDvina({\n * token: 'my-static-token',\n * baseUrl: 'localhost:4000',\n * })\n * ```\n *\n * @example\n * ```typescript\n * // Consuming the SDK in a service\n * import { DvinaSdk } from '@dvina/sdk';\n *\n * @Injectable({ providedIn: 'root' })\n * export class ReportService {\n * private readonly sdk = inject(DvinaSdk);\n *\n * reports = toSignal(this.sdk.reports({ first: 20 }).watch());\n * }\n * ```\n */\nexport function provideDvina(optionsOrFactory: DvinaSdkOptions | (() => DvinaSdkOptions)): EnvironmentProviders {\n\treturn makeEnvironmentProviders([\n\t\t{\n\t\t\tprovide: DvinaSdk,\n\t\t\tuseFactory: () => {\n\t\t\t\tconst options = typeof optionsOrFactory === 'function' ? optionsOrFactory() : optionsOrFactory;\n\t\t\t\treturn new DvinaSdk(options);\n\t\t\t},\n\t\t},\n\t]);\n}\n"]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
require('../../chunk-DZUJEN5N.cjs');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
|
|
6
|
+
function useLiveQuery(factory, deps) {
|
|
7
|
+
const ref = react.useMemo(factory, deps);
|
|
8
|
+
const [value, setValue] = react.useState(ref.current);
|
|
9
|
+
if (process.env.NODE_ENV !== "production") {
|
|
10
|
+
const changeCount = react.useRef(0);
|
|
11
|
+
const lastChangeTime = react.useRef(Date.now());
|
|
12
|
+
const now = Date.now();
|
|
13
|
+
if (ref !== void 0) {
|
|
14
|
+
const elapsed = now - lastChangeTime.current;
|
|
15
|
+
if (elapsed < 1e3) {
|
|
16
|
+
changeCount.current++;
|
|
17
|
+
} else {
|
|
18
|
+
changeCount.current = 1;
|
|
19
|
+
}
|
|
20
|
+
lastChangeTime.current = now;
|
|
21
|
+
if (changeCount.current > 10) {
|
|
22
|
+
console.warn(
|
|
23
|
+
"useLiveQuery: query ref was recreated more than 10 times within 1 second. This usually means the dependency array is unstable (e.g. a new object/array created every render). Ensure deps contain only primitive values or stable references."
|
|
24
|
+
);
|
|
25
|
+
changeCount.current = 0;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
react.useEffect(() => {
|
|
30
|
+
let active = true;
|
|
31
|
+
setValue(ref.current);
|
|
32
|
+
(async () => {
|
|
33
|
+
try {
|
|
34
|
+
for await (const v of ref) {
|
|
35
|
+
if (!active) break;
|
|
36
|
+
setValue(v);
|
|
37
|
+
}
|
|
38
|
+
} catch {
|
|
39
|
+
}
|
|
40
|
+
})();
|
|
41
|
+
return () => {
|
|
42
|
+
active = false;
|
|
43
|
+
ref.dispose();
|
|
44
|
+
};
|
|
45
|
+
}, [ref]);
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
exports.useLiveQuery = useLiveQuery;
|
|
50
|
+
//# sourceMappingURL=index.cjs.map
|
|
51
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/adapters/react/index.ts"],"names":["useMemo","useState","useRef","useEffect"],"mappings":";;;;;AA2DO,SAAS,YAAA,CAAgB,SAAiC,IAAA,EAAyC;AAEzG,EAAA,MAAM,GAAA,GAAMA,aAAA,CAAQ,OAAA,EAAS,IAAI,CAAA;AAEjC,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAIC,cAAA,CAAwB,IAAI,OAAO,CAAA;AAG7D,EAAA,IAAI,OAAA,CAAQ,GAAA,CAAI,QAAA,KAAa,YAAA,EAAc;AAE1C,IAAA,MAAM,WAAA,GAAcC,aAAO,CAAC,CAAA;AAE5B,IAAA,MAAM,cAAA,GAAiBA,YAAA,CAAO,IAAA,CAAK,GAAA,EAAK,CAAA;AAExC,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,IAAI,QAAQ,MAAA,EAAW;AACtB,MAAA,MAAM,OAAA,GAAU,MAAM,cAAA,CAAe,OAAA;AACrC,MAAA,IAAI,UAAU,GAAA,EAAM;AACnB,QAAA,WAAA,CAAY,OAAA,EAAA;AAAA,MACb,CAAA,MAAO;AACN,QAAA,WAAA,CAAY,OAAA,GAAU,CAAA;AAAA,MACvB;AACA,MAAA,cAAA,CAAe,OAAA,GAAU,GAAA;AAEzB,MAAA,IAAI,WAAA,CAAY,UAAU,EAAA,EAAI;AAC7B,QAAA,OAAA,CAAQ,IAAA;AAAA,UACP;AAAA,SAGD;AACA,QAAA,WAAA,CAAY,OAAA,GAAU,CAAA;AAAA,MACvB;AAAA,IACD;AAAA,EACD;AAEA,EAAAC,eAAA,CAAU,MAAM;AACf,IAAA,IAAI,MAAA,GAAS,IAAA;AAGb,IAAA,QAAA,CAAS,IAAI,OAAO,CAAA;AAEpB,IAAA,CAAC,YAAY;AACZ,MAAA,IAAI;AACH,QAAA,WAAA,MAAiB,KAAK,GAAA,EAAK;AAC1B,UAAA,IAAI,CAAC,MAAA,EAAQ;AACb,UAAA,QAAA,CAAS,CAAC,CAAA;AAAA,QACX;AAAA,MACD,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACD,CAAA,GAAG;AAEH,IAAA,OAAO,MAAM;AACZ,MAAA,MAAA,GAAS,KAAA;AACT,MAAA,GAAA,CAAI,OAAA,EAAQ;AAAA,IACb,CAAA;AAAA,EACD,CAAA,EAAG,CAAC,GAAG,CAAC,CAAA;AAER,EAAA,OAAO,KAAA;AACR","file":"index.cjs","sourcesContent":["/**\n * React adapter for @dvina/sdk.\n *\n * Converts DvinaQueryRef (AsyncIterable) into React state via hooks.\n *\n * @example\n * ```tsx\n * import { useLiveQuery } from '@dvina/sdk/react';\n *\n * function ReportsPage() {\n * const reports = useLiveQuery(() => sdk.reports({ first: 20 }).watch(), []);\n * return (\n * <ul>\n * {reports?.nodes.map(r => <li key={r.id}>{r.name}</li>)}\n * </ul>\n * );\n * }\n * ```\n *\n * @packageDocumentation\n */\n\nimport { useState, useEffect, useMemo, useRef } from 'react';\nimport type { DvinaQueryRef } from '../../types.js';\n\n/**\n * React hook that subscribes to a `DvinaQueryRef` (AsyncIterable) and\n * returns its latest value as React state.\n *\n * Accepts a **factory function** that creates the query ref, along with a\n * dependency array. The factory is memoized internally — the ref is only\n * recreated when the dependencies change, preventing infinite\n * dispose-subscribe loops that would occur with inline `.watch()` calls.\n *\n * The component re-renders whenever the query ref emits a new value.\n * Cleanup (disposal of the subscription) happens automatically when the\n * component unmounts or when dependencies change.\n *\n * @param factory - A function that returns a DvinaQueryRef (e.g. `() => sdk.reports().watch()`)\n * @param deps - Dependency array controlling when the ref is recreated (same semantics as `useMemo`)\n * @returns The latest value from the query ref, or `undefined` if not yet loaded\n *\n * @example\n * ```tsx\n * // Static query — ref created once\n * function ReportsPage() {\n * const reports = useLiveQuery(() => sdk.reports({ first: 20 }).watch(), []);\n * if (!reports) return <div>Loading...</div>;\n * return <ul>{reports.nodes.map(r => <li key={r.id}>{r.name}</li>)}</ul>;\n * }\n *\n * // Reactive query — ref recreated when `id` changes\n * function ReportDetail({ id }: { id: string }) {\n * const report = useLiveQuery(() => sdk.report({ id }).watch(), [id]);\n * if (!report) return <div>Loading...</div>;\n * return <h1>{report.name}</h1>;\n * }\n * ```\n */\nexport function useLiveQuery<T>(factory: () => DvinaQueryRef<T>, deps: readonly unknown[]): T | undefined {\n\t// eslint-disable-next-line react-hooks/exhaustive-deps -- deps are forwarded from the caller\n\tconst ref = useMemo(factory, deps);\n\n\tconst [value, setValue] = useState<T | undefined>(ref.current);\n\n\t// Track rapid ref changes in dev mode to warn about missing deps\n\tif (process.env.NODE_ENV !== 'production') {\n\t\t// eslint-disable-next-line react-hooks/rules-of-hooks -- dev-only, always runs in dev\n\t\tconst changeCount = useRef(0);\n\t\t// eslint-disable-next-line react-hooks/rules-of-hooks\n\t\tconst lastChangeTime = useRef(Date.now());\n\n\t\tconst now = Date.now();\n\t\tif (ref !== undefined) {\n\t\t\tconst elapsed = now - lastChangeTime.current;\n\t\t\tif (elapsed < 1000) {\n\t\t\t\tchangeCount.current++;\n\t\t\t} else {\n\t\t\t\tchangeCount.current = 1;\n\t\t\t}\n\t\t\tlastChangeTime.current = now;\n\n\t\t\tif (changeCount.current > 10) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t'useLiveQuery: query ref was recreated more than 10 times within 1 second. ' +\n\t\t\t\t\t\t'This usually means the dependency array is unstable (e.g. a new object/array ' +\n\t\t\t\t\t\t'created every render). Ensure deps contain only primitive values or stable references.',\n\t\t\t\t);\n\t\t\t\tchangeCount.current = 0;\n\t\t\t}\n\t\t}\n\t}\n\n\tuseEffect(() => {\n\t\tlet active = true;\n\n\t\t// Sync initial value in case it changed between render and effect\n\t\tsetValue(ref.current);\n\n\t\t(async () => {\n\t\t\ttry {\n\t\t\t\tfor await (const v of ref) {\n\t\t\t\t\tif (!active) break;\n\t\t\t\t\tsetValue(v);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Query ref was disposed or errored — state keeps its last value\n\t\t\t}\n\t\t})();\n\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\tref.dispose();\n\t\t};\n\t}, [ref]);\n\n\treturn value;\n}\n"]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { D as DvinaQueryRef } from '../../types-2RotD0Ab.cjs';
|
|
2
|
+
import 'graphql';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* React adapter for @dvina/sdk.
|
|
6
|
+
*
|
|
7
|
+
* Converts DvinaQueryRef (AsyncIterable) into React state via hooks.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* import { useLiveQuery } from '@dvina/sdk/react';
|
|
12
|
+
*
|
|
13
|
+
* function ReportsPage() {
|
|
14
|
+
* const reports = useLiveQuery(() => sdk.reports({ first: 20 }).watch(), []);
|
|
15
|
+
* return (
|
|
16
|
+
* <ul>
|
|
17
|
+
* {reports?.nodes.map(r => <li key={r.id}>{r.name}</li>)}
|
|
18
|
+
* </ul>
|
|
19
|
+
* );
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @packageDocumentation
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* React hook that subscribes to a `DvinaQueryRef` (AsyncIterable) and
|
|
28
|
+
* returns its latest value as React state.
|
|
29
|
+
*
|
|
30
|
+
* Accepts a **factory function** that creates the query ref, along with a
|
|
31
|
+
* dependency array. The factory is memoized internally — the ref is only
|
|
32
|
+
* recreated when the dependencies change, preventing infinite
|
|
33
|
+
* dispose-subscribe loops that would occur with inline `.watch()` calls.
|
|
34
|
+
*
|
|
35
|
+
* The component re-renders whenever the query ref emits a new value.
|
|
36
|
+
* Cleanup (disposal of the subscription) happens automatically when the
|
|
37
|
+
* component unmounts or when dependencies change.
|
|
38
|
+
*
|
|
39
|
+
* @param factory - A function that returns a DvinaQueryRef (e.g. `() => sdk.reports().watch()`)
|
|
40
|
+
* @param deps - Dependency array controlling when the ref is recreated (same semantics as `useMemo`)
|
|
41
|
+
* @returns The latest value from the query ref, or `undefined` if not yet loaded
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```tsx
|
|
45
|
+
* // Static query — ref created once
|
|
46
|
+
* function ReportsPage() {
|
|
47
|
+
* const reports = useLiveQuery(() => sdk.reports({ first: 20 }).watch(), []);
|
|
48
|
+
* if (!reports) return <div>Loading...</div>;
|
|
49
|
+
* return <ul>{reports.nodes.map(r => <li key={r.id}>{r.name}</li>)}</ul>;
|
|
50
|
+
* }
|
|
51
|
+
*
|
|
52
|
+
* // Reactive query — ref recreated when `id` changes
|
|
53
|
+
* function ReportDetail({ id }: { id: string }) {
|
|
54
|
+
* const report = useLiveQuery(() => sdk.report({ id }).watch(), [id]);
|
|
55
|
+
* if (!report) return <div>Loading...</div>;
|
|
56
|
+
* return <h1>{report.name}</h1>;
|
|
57
|
+
* }
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
declare function useLiveQuery<T>(factory: () => DvinaQueryRef<T>, deps: readonly unknown[]): T | undefined;
|
|
61
|
+
|
|
62
|
+
export { useLiveQuery };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { D as DvinaQueryRef } from '../../types-2RotD0Ab.js';
|
|
2
|
+
import 'graphql';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* React adapter for @dvina/sdk.
|
|
6
|
+
*
|
|
7
|
+
* Converts DvinaQueryRef (AsyncIterable) into React state via hooks.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* import { useLiveQuery } from '@dvina/sdk/react';
|
|
12
|
+
*
|
|
13
|
+
* function ReportsPage() {
|
|
14
|
+
* const reports = useLiveQuery(() => sdk.reports({ first: 20 }).watch(), []);
|
|
15
|
+
* return (
|
|
16
|
+
* <ul>
|
|
17
|
+
* {reports?.nodes.map(r => <li key={r.id}>{r.name}</li>)}
|
|
18
|
+
* </ul>
|
|
19
|
+
* );
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @packageDocumentation
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* React hook that subscribes to a `DvinaQueryRef` (AsyncIterable) and
|
|
28
|
+
* returns its latest value as React state.
|
|
29
|
+
*
|
|
30
|
+
* Accepts a **factory function** that creates the query ref, along with a
|
|
31
|
+
* dependency array. The factory is memoized internally — the ref is only
|
|
32
|
+
* recreated when the dependencies change, preventing infinite
|
|
33
|
+
* dispose-subscribe loops that would occur with inline `.watch()` calls.
|
|
34
|
+
*
|
|
35
|
+
* The component re-renders whenever the query ref emits a new value.
|
|
36
|
+
* Cleanup (disposal of the subscription) happens automatically when the
|
|
37
|
+
* component unmounts or when dependencies change.
|
|
38
|
+
*
|
|
39
|
+
* @param factory - A function that returns a DvinaQueryRef (e.g. `() => sdk.reports().watch()`)
|
|
40
|
+
* @param deps - Dependency array controlling when the ref is recreated (same semantics as `useMemo`)
|
|
41
|
+
* @returns The latest value from the query ref, or `undefined` if not yet loaded
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```tsx
|
|
45
|
+
* // Static query — ref created once
|
|
46
|
+
* function ReportsPage() {
|
|
47
|
+
* const reports = useLiveQuery(() => sdk.reports({ first: 20 }).watch(), []);
|
|
48
|
+
* if (!reports) return <div>Loading...</div>;
|
|
49
|
+
* return <ul>{reports.nodes.map(r => <li key={r.id}>{r.name}</li>)}</ul>;
|
|
50
|
+
* }
|
|
51
|
+
*
|
|
52
|
+
* // Reactive query — ref recreated when `id` changes
|
|
53
|
+
* function ReportDetail({ id }: { id: string }) {
|
|
54
|
+
* const report = useLiveQuery(() => sdk.report({ id }).watch(), [id]);
|
|
55
|
+
* if (!report) return <div>Loading...</div>;
|
|
56
|
+
* return <h1>{report.name}</h1>;
|
|
57
|
+
* }
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
declare function useLiveQuery<T>(factory: () => DvinaQueryRef<T>, deps: readonly unknown[]): T | undefined;
|
|
61
|
+
|
|
62
|
+
export { useLiveQuery };
|