@xnetjs/react 0.0.2 → 0.0.3

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/core.d.ts ADDED
@@ -0,0 +1,372 @@
1
+ import { Q as QueryFilter, j as QueryBaseResult, F as FlatNode, M as MigrationWarning } from './useQuery-D7ajycrc.js';
2
+ export { c as QueryListResult, d as QuerySingleResult, S as SortDirection, u as useQuery } from './useQuery-D7ajycrc.js';
3
+ import { PropertyBuilder, DefinedSchema, InferCreateProps, NodeState, NodeBatchWriteInput, NodeBatchWriteResult } from '@xnetjs/data';
4
+ import { QueryPageInfo } from '@xnetjs/data-bridge';
5
+ import { Awareness } from 'y-protocols/awareness';
6
+ import * as Y from 'yjs';
7
+ import { Identity } from '@xnetjs/identity';
8
+ export { f as XNetConfig, X as XNetContextValue, e as XNetProvider, g as XNetProviderProps, h as XNetRuntimeConfig, i as XNetRuntimeFallback, j as XNetRuntimeMode, k as XNetRuntimePhase, d as XNetRuntimeStatus, l as XNetRuntimeWorkerConfig, a as useXNet } from './context-CFu9i136.js';
9
+ import { Component, ReactNode, ErrorInfo } from 'react';
10
+ import '@xnetjs/core';
11
+ import '@xnetjs/runtime';
12
+ import '@xnetjs/crypto';
13
+ import '@xnetjs/sync';
14
+ import '@xnetjs/history';
15
+ import '@xnetjs/plugins';
16
+ import './telemetry-context-B7r6H1KW.js';
17
+
18
+ /**
19
+ * useInfiniteQuery - incremental "load more" wrapper over useQuery.
20
+ *
21
+ * Unlike a cursor-paged implementation (which freezes earlier pages and keeps
22
+ * the live page on `after` cursor semantics — a shape that falls off the
23
+ * bridge's incremental delta path and re-executes storage on every matching
24
+ * edit), this hook models the loaded region as a single GROWING
25
+ * `limit + orderBy` window. That descriptor stays on the bounded-delta fast
26
+ * path, so:
27
+ *
28
+ * - edits to any already-loaded row update in place without re-querying, and
29
+ * - every loaded row stays live (no stale frozen pages).
30
+ *
31
+ * Calling `fetchNextPage()` grows the window by `pageSize`; the previous rows
32
+ * are retained during the (single) read for the larger window so the list
33
+ * does not flicker.
34
+ */
35
+
36
+ interface InfiniteQueryFilter<P extends Record<string, PropertyBuilder> = Record<string, PropertyBuilder>> extends Omit<QueryFilter<P>, 'limit' | 'offset' | 'page'> {
37
+ /** Number of rows to grow the window by on each `fetchNextPage()`. */
38
+ pageSize?: number;
39
+ /** Optional page metadata. `first` defaults to `pageSize` when omitted. */
40
+ page?: Omit<NonNullable<QueryFilter<P>['page']>, 'first'> & {
41
+ first?: number;
42
+ };
43
+ /**
44
+ * Upper bound on the live window size. Once reached, `fetchNextPage()` is a
45
+ * no-op and `hasMore` is false. Defaults to unbounded — set this for
46
+ * virtualized lists so the overfetch buffer and live snapshot stay bounded.
47
+ */
48
+ maxLoaded?: number;
49
+ }
50
+ interface InfiniteQueryPage<P extends Record<string, PropertyBuilder>> {
51
+ cursor: string | null;
52
+ data: FlatNode<P>[];
53
+ pageInfo: QueryPageInfo;
54
+ }
55
+ interface InfiniteQueryResult<P extends Record<string, PropertyBuilder>> extends QueryBaseResult {
56
+ /** All loaded rows in descriptor order. */
57
+ data: FlatNode<P>[];
58
+ /** Loaded rows grouped into `pageSize` chunks for grouped/virtualized rendering. */
59
+ pages: InfiniteQueryPage<P>[];
60
+ /** Aggregated pagination metadata for the loaded window. */
61
+ pageInfo: QueryPageInfo;
62
+ /** Total matching count when known. Null means unavailable or intentionally not counted. */
63
+ totalCount: number | null;
64
+ /** Whether the window can grow further. */
65
+ hasMore: boolean;
66
+ /** Whether `fetchNextPage()` has grown the window and is awaiting the larger read. */
67
+ isFetchingNextPage: boolean;
68
+ /** Grow the loaded window by `pageSize` (bounded by `maxLoaded`). */
69
+ fetchNextPage: () => Promise<void>;
70
+ /** Shrink the window back to the first page. */
71
+ reset: () => void;
72
+ /** Migration warnings across the loaded window. */
73
+ migrationWarnings: MigrationWarning[];
74
+ }
75
+ declare function useInfiniteQuery<P extends Record<string, PropertyBuilder>>(schema: DefinedSchema<P>, filter?: InfiniteQueryFilter<P>): InfiniteQueryResult<P>;
76
+
77
+ /**
78
+ * useMutate - Unified write hook for Nodes via DataBridge
79
+ *
80
+ * A single hook for all write operations:
81
+ * - Create nodes (requires schema)
82
+ * - Update nodes (requires schema for type safety)
83
+ * - Delete nodes (by ID)
84
+ * - Atomic transactions (multiple operations)
85
+ *
86
+ * Uses DataBridge for off-main-thread data access.
87
+ *
88
+ * Features:
89
+ * - Immediate local updates (bridge updates UI subscribers synchronously)
90
+ * - Type-safe schema-bound mutations
91
+ * - Transaction support for atomic multi-node operations
92
+ * - Pending state tracking
93
+ *
94
+ * @example
95
+ * ```tsx
96
+ * const { create, update, remove, mutate, isPending } = useMutate()
97
+ *
98
+ * // Simple operations
99
+ * await create(TaskSchema, { title: 'New Task', status: 'todo' })
100
+ * await update(TaskSchema, taskId, { status: 'done' }) // Type-safe!
101
+ * await remove(taskId)
102
+ *
103
+ * // Atomic transaction
104
+ * await mutate([
105
+ * { type: 'create', schema: TaskSchema, data: { title: 'Task 1' } },
106
+ * { type: 'update', id: taskId, data: { status: 'done' } },
107
+ * { type: 'delete', id: oldTaskId }
108
+ * ])
109
+ * ```
110
+ */
111
+
112
+ /**
113
+ * Create operation for mutate
114
+ */
115
+ interface MutateCreate<P extends Record<string, PropertyBuilder> = Record<string, PropertyBuilder>> {
116
+ type: 'create';
117
+ schema: DefinedSchema<P>;
118
+ data: InferCreateProps<P>;
119
+ id?: string;
120
+ }
121
+ /**
122
+ * Update operation for mutate
123
+ */
124
+ interface MutateUpdate {
125
+ type: 'update';
126
+ id: string;
127
+ data: Record<string, unknown>;
128
+ }
129
+ /**
130
+ * Delete operation for mutate
131
+ */
132
+ interface MutateDelete {
133
+ type: 'delete';
134
+ id: string;
135
+ }
136
+ /**
137
+ * Restore operation for mutate
138
+ */
139
+ interface MutateRestore {
140
+ type: 'restore';
141
+ id: string;
142
+ }
143
+ /**
144
+ * All possible mutate operations
145
+ */
146
+ type MutateOp = MutateCreate<Record<string, PropertyBuilder>> | MutateUpdate | MutateDelete | MutateRestore;
147
+ /**
148
+ * Result from a mutate transaction
149
+ */
150
+ interface MutateResult {
151
+ /** Results for each operation (NodeState or null for delete) */
152
+ results: (NodeState | null)[];
153
+ /** Batch ID when transaction backend is available */
154
+ batchId?: string;
155
+ /** Temp ID mapping when transaction backend is available */
156
+ tempIds?: Record<string, string>;
157
+ }
158
+ /**
159
+ * Result from useMutate hook
160
+ */
161
+ interface UseMutateResult {
162
+ /**
163
+ * Create a new node.
164
+ * Requires a schema to know what type to create.
165
+ * Optionally specify a custom ID (otherwise auto-generated).
166
+ *
167
+ * @returns The created node (flattened), or null if creation failed
168
+ */
169
+ create: <P extends Record<string, PropertyBuilder>>(schema: DefinedSchema<P>, data: InferCreateProps<P>, id?: string) => Promise<FlatNode<P> | null>;
170
+ /**
171
+ * Update an existing node by ID.
172
+ * Requires schema for type-safe property checking.
173
+ *
174
+ * @example
175
+ * ```tsx
176
+ * await update(TaskSchema, taskId, { status: 'done' }) // OK
177
+ * await update(TaskSchema, taskId, { typo: 'x' }) // Type error!
178
+ * ```
179
+ *
180
+ * @returns The updated node (flattened), or null if update failed
181
+ */
182
+ update: <P extends Record<string, PropertyBuilder>>(schema: DefinedSchema<P>, id: string, data: Partial<InferCreateProps<P>>) => Promise<FlatNode<P> | null>;
183
+ /**
184
+ * Delete a node by ID (soft delete).
185
+ */
186
+ remove: (id: string) => Promise<void>;
187
+ /**
188
+ * Restore a deleted node by ID.
189
+ *
190
+ * @returns The restored node (flattened), or null if restore failed
191
+ */
192
+ restore: (id: string) => Promise<FlatNode<Record<string, PropertyBuilder>> | null>;
193
+ /**
194
+ * Execute multiple operations atomically.
195
+ * All operations succeed or fail together.
196
+ *
197
+ * Note: Atomicity requires a bridge implementing DataBridge.transaction
198
+ * (MainThreadBridge, WorkerBridge, NativeBridge all do). Bridges without
199
+ * it execute operations sequentially (not truly atomic).
200
+ */
201
+ mutate: (ops: MutateOp[]) => Promise<MutateResult | null>;
202
+ /**
203
+ * Execute a storage-owned bulk write.
204
+ */
205
+ bulk: (input: NodeBatchWriteInput) => Promise<NodeBatchWriteResult | null>;
206
+ /**
207
+ * Whether any mutation is currently in progress.
208
+ */
209
+ isPending: boolean;
210
+ /**
211
+ * Number of pending mutations.
212
+ */
213
+ pendingCount: number;
214
+ }
215
+ /**
216
+ * Hook for all write operations on Nodes via DataBridge.
217
+ *
218
+ * Provides both convenience methods (create, update, remove) and
219
+ * a full transaction API (mutate) for atomic multi-node operations.
220
+ *
221
+ * All operations update the local cache immediately, and subscribers
222
+ * see changes synchronously. Background sync handles persistence.
223
+ */
224
+ declare function useMutate(): UseMutateResult;
225
+
226
+ /**
227
+ * Sync connection status
228
+ */
229
+ type SyncStatus = 'offline' | 'connecting' | 'connected' | 'error';
230
+ interface PresenceUser {
231
+ did: string;
232
+ name?: string;
233
+ color?: string;
234
+ lastSeen?: number;
235
+ isStale?: boolean;
236
+ }
237
+ /**
238
+ * Options for useNode
239
+ */
240
+ interface UseNodeOptions<P extends Record<string, PropertyBuilder>> {
241
+ /** Signaling servers for y-webrtc (default: localhost for dev) */
242
+ signalingServers?: string[];
243
+ /** Disable auto-sync (default: false) */
244
+ disableSync?: boolean;
245
+ /** Debounce persistence delay in ms (default: 1000) */
246
+ persistDebounce?: number;
247
+ /**
248
+ * Auto-create the node if it doesn't exist.
249
+ * Provide the default properties to use for creation.
250
+ */
251
+ createIfMissing?: InferCreateProps<P>;
252
+ /**
253
+ * User's DID for presence/cursors. If provided, broadcasts awareness state.
254
+ */
255
+ did?: string;
256
+ }
257
+ /**
258
+ * Result from useNode hook
259
+ */
260
+ interface UseNodeResult<P extends Record<string, PropertyBuilder>> {
261
+ /** Node properties (flattened - access directly: data.title) */
262
+ data: FlatNode<P> | null;
263
+ /** Yjs document instance (null if schema has no document type) */
264
+ doc: Y.Doc | null;
265
+ /**
266
+ * Update node properties (type-safe).
267
+ * Only properties defined in the schema are allowed.
268
+ */
269
+ update: (properties: Partial<InferCreateProps<P>>) => Promise<void>;
270
+ /**
271
+ * Soft-delete the node.
272
+ */
273
+ remove: () => Promise<void>;
274
+ /** Whether currently loading */
275
+ loading: boolean;
276
+ /** Any error that occurred */
277
+ error: Error | null;
278
+ /** Whether document has unsaved changes */
279
+ isDirty: boolean;
280
+ /** Last persistence timestamp */
281
+ lastSavedAt: number | null;
282
+ /** Whether the node was auto-created (via createIfMissing) */
283
+ wasCreated: boolean;
284
+ /** Sync connection status */
285
+ syncStatus: SyncStatus;
286
+ /** Sync error message (if syncStatus is 'error') */
287
+ syncError: string | null;
288
+ /** Connected peer count */
289
+ peerCount: number;
290
+ /** Presence list (live awareness + hub snapshot) */
291
+ presence: PresenceUser[];
292
+ /** Yjs Awareness instance (for TipTap CollaborationCursor) */
293
+ awareness: Awareness | null;
294
+ /** Manually trigger save */
295
+ save: () => Promise<void>;
296
+ /** Reload from storage */
297
+ reload: () => Promise<void>;
298
+ }
299
+ /**
300
+ * Load a Node with its CRDT document.
301
+ *
302
+ * This is the primary hook for editing content. It combines:
303
+ * - Data loading (with FlatNode for ergonomic access)
304
+ * - Y.Doc for collaborative rich text
305
+ * - Type-safe mutations
306
+ * - Real-time sync and presence
307
+ */
308
+ declare function useNode<P extends Record<string, PropertyBuilder>>(schema: DefinedSchema<P>, id: string | null, options?: UseNodeOptions<P>): UseNodeResult<P>;
309
+
310
+ /**
311
+ * useIdentity hook for identity access
312
+ */
313
+
314
+ /**
315
+ * Result from useIdentity hook
316
+ */
317
+ interface UseIdentityResult {
318
+ identity: Identity | null;
319
+ isAuthenticated: boolean;
320
+ did: string | null;
321
+ }
322
+ /**
323
+ * Hook for accessing current identity
324
+ */
325
+ declare function useIdentity(): UseIdentityResult;
326
+
327
+ /**
328
+ * Global error boundary for xNet React apps.
329
+ *
330
+ * Catches unhandled React render errors and displays a recovery UI
331
+ * instead of crashing the entire app tree.
332
+ */
333
+
334
+ type ErrorBoundaryFallbackProps = {
335
+ error: Error;
336
+ reset: () => void;
337
+ };
338
+ type ErrorBoundaryProps = {
339
+ children: ReactNode;
340
+ /** Custom fallback UI. Receives error + reset function. If omitted, a default error screen is shown. */
341
+ fallback?: ReactNode | ((props: ErrorBoundaryFallbackProps) => ReactNode);
342
+ /** Callback fired when an error is caught. */
343
+ onError?: (error: Error, errorInfo: ErrorInfo) => void;
344
+ /** Change this value to force a reset (e.g. after navigation). */
345
+ resetKey?: string | number;
346
+ };
347
+ type ErrorBoundaryState = {
348
+ hasError: boolean;
349
+ error: Error | null;
350
+ };
351
+ declare class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
352
+ state: ErrorBoundaryState;
353
+ static getDerivedStateFromError(error: Error): ErrorBoundaryState;
354
+ componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
355
+ private handleReset;
356
+ componentDidUpdate(prevProps: ErrorBoundaryProps): void;
357
+ render(): ReactNode;
358
+ }
359
+
360
+ type OfflineIndicatorProps = {
361
+ /** Custom message. Default: 'You are offline. Changes will sync when reconnected.' */
362
+ message?: string;
363
+ /** Additional CSS class. */
364
+ className?: string;
365
+ /** Position. Default: 'bottom'. */
366
+ position?: 'top' | 'bottom';
367
+ };
368
+ /** Returns true when the browser is offline. */
369
+ declare function useIsOffline(): boolean;
370
+ declare function OfflineIndicator({ message, className, position }: OfflineIndicatorProps): JSX.Element | null;
371
+
372
+ export { ErrorBoundary, type ErrorBoundaryProps, FlatNode, type InfiniteQueryFilter, type InfiniteQueryPage, type InfiniteQueryResult, MigrationWarning, type MutateCreate, type MutateDelete, type MutateOp, type MutateRestore, type MutateUpdate, OfflineIndicator, type OfflineIndicatorProps, type PresenceUser, QueryFilter, type SyncStatus, type UseIdentityResult, type UseMutateResult, type UseNodeOptions, type UseNodeResult, useIdentity, useInfiniteQuery, useIsOffline, useMutate, useNode };
package/dist/core.js ADDED
@@ -0,0 +1,29 @@
1
+ import {
2
+ ErrorBoundary,
3
+ OfflineIndicator,
4
+ useIdentity,
5
+ useInfiniteQuery,
6
+ useIsOffline,
7
+ useNode
8
+ } from "./chunk-QHNYQVUM.js";
9
+ import {
10
+ useMutate,
11
+ useQuery
12
+ } from "./chunk-6VOICQZ3.js";
13
+ import "./chunk-JCOFKBOB.js";
14
+ import {
15
+ XNetProvider,
16
+ useXNet
17
+ } from "./chunk-IHTMVTTE.js";
18
+ export {
19
+ ErrorBoundary,
20
+ OfflineIndicator,
21
+ XNetProvider,
22
+ useIdentity,
23
+ useInfiniteQuery,
24
+ useIsOffline,
25
+ useMutate,
26
+ useNode,
27
+ useQuery,
28
+ useXNet
29
+ };