use-abcd 1.4.0 → 1.4.2
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/README.md +217 -232
- package/dist/collection.d.ts +12 -3
- package/dist/examples/TreeEditor.d.ts +2 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +1624 -968
- package/dist/index.js.map +1 -1
- package/dist/item-cache.test.d.ts +1 -0
- package/dist/item.d.ts +1 -5
- package/dist/node.d.ts +50 -0
- package/dist/node.test.d.ts +1 -0
- package/dist/sync-queue.d.ts +36 -0
- package/dist/types.d.ts +4 -1
- package/dist/useCrud.d.ts +1 -1
- package/dist/useCrud.test.d.ts +1 -0
- package/dist/useCrudTree.d.ts +45 -0
- package/dist/useCrudTree.test.d.ts +1 -0
- package/dist/useItem.d.ts +1 -1
- package/dist/useNode.d.ts +25 -0
- package/dist/useSelectedNode.d.ts +12 -0
- package/dist/useSyncState.d.ts +20 -0
- package/package.json +2 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/item.d.ts
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
import { Draft } from 'mutative';
|
|
2
2
|
import { Collection } from './collection';
|
|
3
3
|
import { ItemStatus } from './types';
|
|
4
|
-
export declare class Item<T, C = unknown> {
|
|
4
|
+
export declare class Item<T extends object, C = unknown> {
|
|
5
5
|
private _collection;
|
|
6
6
|
private _id;
|
|
7
7
|
private _cachedStatus;
|
|
8
|
-
private _refCount;
|
|
9
8
|
constructor(collection: Collection<T, C>, id: string);
|
|
10
|
-
_retain(): void;
|
|
11
|
-
_release(): void;
|
|
12
|
-
get refCount(): number;
|
|
13
9
|
get id(): string;
|
|
14
10
|
get data(): T | undefined;
|
|
15
11
|
update(mutate: (draft: Draft<T>) => void): void;
|
package/dist/node.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Draft } from 'mutative';
|
|
2
|
+
import { Collection } from './collection';
|
|
3
|
+
import { ItemStatus } from './types';
|
|
4
|
+
export type TreeNode<T, NodeType = string> = {
|
|
5
|
+
id: string;
|
|
6
|
+
position: number;
|
|
7
|
+
value: T;
|
|
8
|
+
type: NodeType;
|
|
9
|
+
};
|
|
10
|
+
export declare class Node<T extends object, C = unknown, NodeType = string> {
|
|
11
|
+
private _id;
|
|
12
|
+
private _collection;
|
|
13
|
+
private _cachedStatus;
|
|
14
|
+
constructor(collection: Collection<TreeNode<T, NodeType>, C>, id: string);
|
|
15
|
+
private get _separator();
|
|
16
|
+
get id(): string;
|
|
17
|
+
get data(): TreeNode<T, NodeType> | undefined;
|
|
18
|
+
get collection(): Collection<TreeNode<T, NodeType>, C>;
|
|
19
|
+
get depth(): number;
|
|
20
|
+
exists(): boolean;
|
|
21
|
+
getStatus(): ItemStatus;
|
|
22
|
+
getParent(): Node<T, C, NodeType> | null;
|
|
23
|
+
getChildren(): Node<T, C, NodeType>[];
|
|
24
|
+
private _generateChildId;
|
|
25
|
+
append(value: T, type?: NodeType): string;
|
|
26
|
+
prepend(value: T, type?: NodeType): string;
|
|
27
|
+
/**
|
|
28
|
+
* Move node to a new position within the same parent (reordering)
|
|
29
|
+
* or to a different parent (reparenting).
|
|
30
|
+
*
|
|
31
|
+
* @param targetPosition - The target position among siblings
|
|
32
|
+
* @param targetParent - Optional target parent node. If not provided, moves within current parent.
|
|
33
|
+
*/
|
|
34
|
+
move(targetPosition: number, targetParent?: Node<T, C, NodeType>): void;
|
|
35
|
+
/**
|
|
36
|
+
* Clone this node and all its descendants into a Map with remapped IDs.
|
|
37
|
+
* The root of the clone has a temporary ID that will be remapped when merged.
|
|
38
|
+
* @returns Map of cloned nodes keyed by their relative path from clone root
|
|
39
|
+
*/
|
|
40
|
+
clone(): Map<string, TreeNode<T, NodeType>>;
|
|
41
|
+
/**
|
|
42
|
+
* Internal method to merge a cloned subset under this node.
|
|
43
|
+
* The cloned nodes will have their IDs prefixed with this node's ID.
|
|
44
|
+
*/
|
|
45
|
+
_mergeClonedSubset(clonedSubset: Map<string, TreeNode<T, NodeType>>, position?: number): string | undefined;
|
|
46
|
+
updateProp(mutate: (draft: Draft<T>) => void): void;
|
|
47
|
+
remove(): void;
|
|
48
|
+
select(): void;
|
|
49
|
+
_updateId(newId: string): void;
|
|
50
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/sync-queue.d.ts
CHANGED
|
@@ -2,6 +2,12 @@ import { Change, SyncQueueState, SyncResult, IdMapping } from './types';
|
|
|
2
2
|
export type SyncQueueConfig<T> = {
|
|
3
3
|
debounce: number;
|
|
4
4
|
maxRetries: number;
|
|
5
|
+
/**
|
|
6
|
+
* Maximum number of changes to send per sync call.
|
|
7
|
+
* Useful for rate limiting or API constraints.
|
|
8
|
+
* Default: Infinity (send all queued changes)
|
|
9
|
+
*/
|
|
10
|
+
batchSize?: number;
|
|
5
11
|
onSync: (changes: Change<T>[], signal: AbortSignal) => Promise<SyncResult[]>;
|
|
6
12
|
onIdRemap?: (mappings: IdMapping[]) => void;
|
|
7
13
|
};
|
|
@@ -24,7 +30,37 @@ export declare class SyncQueue<T> {
|
|
|
24
30
|
private _updateState;
|
|
25
31
|
private _scheduleFlush;
|
|
26
32
|
private _clearTimer;
|
|
33
|
+
/**
|
|
34
|
+
* Flush pending changes to the server.
|
|
35
|
+
*
|
|
36
|
+
* Flow:
|
|
37
|
+
* 1. Move queued items to inFlight (atomic swap prevents duplicate sends)
|
|
38
|
+
* 2. Flatten all changes and optionally batch by batchSize
|
|
39
|
+
* 3. Send to server via onSync callback
|
|
40
|
+
* 4. Process results (handle success, retries, ID remapping)
|
|
41
|
+
* 5. Schedule next flush if more items queued
|
|
42
|
+
*/
|
|
27
43
|
private _flush;
|
|
44
|
+
/**
|
|
45
|
+
* Process sync results and update state accordingly.
|
|
46
|
+
*
|
|
47
|
+
* For each item in flight:
|
|
48
|
+
* - If ALL operations succeeded: clear errors, collect ID remappings
|
|
49
|
+
* - If ANY operation failed: increment retry counter, re-queue if under maxRetries
|
|
50
|
+
*
|
|
51
|
+
* ID Remapping: When server assigns new IDs to created items (e.g., temp ID -> DB ID),
|
|
52
|
+
* we collect these mappings and notify via onIdRemap callback so the collection
|
|
53
|
+
* can update its local state.
|
|
54
|
+
*/
|
|
28
55
|
private _processResults;
|
|
56
|
+
/**
|
|
57
|
+
* Handle network or unexpected errors during sync.
|
|
58
|
+
*
|
|
59
|
+
* Two scenarios:
|
|
60
|
+
* 1. Aborted (e.g., SyncQueue destroyed): Re-queue all items without incrementing retries
|
|
61
|
+
* 2. Actual error: Increment retries, re-queue if under limit, record error
|
|
62
|
+
*
|
|
63
|
+
* This ensures no data loss - failed operations are preserved for retry.
|
|
64
|
+
*/
|
|
29
65
|
private _handleError;
|
|
30
66
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -45,7 +45,7 @@ export type SyncQueueState<T> = {
|
|
|
45
45
|
isSyncing: boolean;
|
|
46
46
|
};
|
|
47
47
|
export type FetchState = "idle" | "fetching" | "error";
|
|
48
|
-
export type Config<T, C> = {
|
|
48
|
+
export type Config<T extends object, C> = {
|
|
49
49
|
id: string;
|
|
50
50
|
initialContext: C;
|
|
51
51
|
getId: (item: T) => string;
|
|
@@ -56,6 +56,9 @@ export type Config<T, C> = {
|
|
|
56
56
|
cacheCapacity?: number;
|
|
57
57
|
cacheTtl?: number;
|
|
58
58
|
fetchRetries?: number;
|
|
59
|
+
rootId?: string;
|
|
60
|
+
getNodeId?: () => string;
|
|
61
|
+
nodeSeparator?: string;
|
|
59
62
|
onFetch: (context: C, signal: AbortSignal) => Promise<T[]>;
|
|
60
63
|
onSync?: (changes: Change<T>[], signal: AbortSignal) => Promise<SyncResult[]>;
|
|
61
64
|
};
|
package/dist/useCrud.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Draft } from 'mutative';
|
|
2
2
|
import { Config, Mutator } from './types';
|
|
3
3
|
export type { Config, Change, SyncResult, ItemStatus, SyncQueueState, SyncState, ChangeType, ItemSyncStatus, FetchState, } from './types';
|
|
4
|
-
export declare function useCrud<T, C>(config: Config<T, C>): {
|
|
4
|
+
export declare function useCrud<T extends object, C>(config: Config<T, C>): {
|
|
5
5
|
items: Map<string, T>;
|
|
6
6
|
context: C;
|
|
7
7
|
syncState: import('./types').SyncState;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Node, TreeNode } from './node';
|
|
2
|
+
import { Config, Mutator } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Config for useCrudTree - same as Config but with rootId required
|
|
5
|
+
*/
|
|
6
|
+
export type TreeConfig<T extends object, C, NodeType = string> = Omit<Config<TreeNode<T, NodeType>, C>, "rootId"> & {
|
|
7
|
+
rootId: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Hook for tree-based CRUD operations.
|
|
11
|
+
* This is a replacement for useCrud when working with tree structures.
|
|
12
|
+
* It requires rootId in the config and returns the root node along with
|
|
13
|
+
* all the standard useCrud properties.
|
|
14
|
+
*
|
|
15
|
+
* @param config - Tree collection configuration with required rootId
|
|
16
|
+
* @returns Object with crud operations, state, and the root node
|
|
17
|
+
*/
|
|
18
|
+
export declare function useCrudTree<T extends object, C, NodeType = string>(config: TreeConfig<T, C, NodeType>): {
|
|
19
|
+
rootNode: Node<T, C, NodeType>;
|
|
20
|
+
items: Map<string, TreeNode<T, NodeType>>;
|
|
21
|
+
context: C;
|
|
22
|
+
syncState: import('./types').SyncState;
|
|
23
|
+
syncQueue: import('./types').SyncQueueState<TreeNode<T, NodeType>>;
|
|
24
|
+
loading: boolean;
|
|
25
|
+
syncing: boolean;
|
|
26
|
+
fetchStatus: import('./types').FetchState;
|
|
27
|
+
fetchError: string;
|
|
28
|
+
getNode: (id: string) => Node<T, C, NodeType>;
|
|
29
|
+
getNodeStatus: (id: string) => {
|
|
30
|
+
type: import('./types').ChangeType;
|
|
31
|
+
status: import('./types').ItemSyncStatus;
|
|
32
|
+
retries: number;
|
|
33
|
+
error?: string;
|
|
34
|
+
};
|
|
35
|
+
setContext: (patchContext: Mutator<C>) => void;
|
|
36
|
+
refresh: () => Promise<void>;
|
|
37
|
+
pauseSync: () => void;
|
|
38
|
+
resumeSync: () => void;
|
|
39
|
+
retrySync: (id?: string) => void;
|
|
40
|
+
selectNode: (id: string) => void;
|
|
41
|
+
deselectNode: () => void;
|
|
42
|
+
selectedNodeId: string;
|
|
43
|
+
selectedNode: Node<T, C, NodeType>;
|
|
44
|
+
toJson: () => object;
|
|
45
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/useItem.d.ts
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Draft } from 'mutative';
|
|
2
|
+
import { Node, TreeNode } from './node';
|
|
3
|
+
import { ItemStatus } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* Result type for useNode hook - contains all node data and operations
|
|
6
|
+
*/
|
|
7
|
+
export type UseNodeResult<T extends object, C, NodeType = string> = {
|
|
8
|
+
isPresent: boolean;
|
|
9
|
+
data: TreeNode<T, NodeType> | undefined;
|
|
10
|
+
status: ItemStatus;
|
|
11
|
+
exists: boolean;
|
|
12
|
+
isSelected: boolean;
|
|
13
|
+
depth: number;
|
|
14
|
+
getParent: () => Node<T, C, NodeType> | null;
|
|
15
|
+
children: Node<T, C, NodeType>[];
|
|
16
|
+
append: (value: T, type?: NodeType) => string;
|
|
17
|
+
prepend: (value: T, type?: NodeType) => string;
|
|
18
|
+
move: (targetPosition: number, targetParent?: Node<T, C, NodeType>) => void;
|
|
19
|
+
clone: () => Map<string, TreeNode<T, NodeType>>;
|
|
20
|
+
updateProp: (mutate: (draft: Draft<T>) => void) => void;
|
|
21
|
+
remove: () => void;
|
|
22
|
+
select: () => void;
|
|
23
|
+
deselect: () => void;
|
|
24
|
+
};
|
|
25
|
+
export declare function useNode<T extends object, C, NodeType = string>(node: Node<T, C, NodeType>): UseNodeResult<T, C, NodeType>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { UseNodeResult } from './useNode';
|
|
2
|
+
/**
|
|
3
|
+
* Hook for accessing and operating on the currently selected node in a collection.
|
|
4
|
+
*
|
|
5
|
+
* This is a wrapper around useNode that automatically tracks the selected node.
|
|
6
|
+
* Use this in components that need to operate on whatever node is currently selected
|
|
7
|
+
* (e.g., toolbars, property panels, context menus).
|
|
8
|
+
*
|
|
9
|
+
* @param collectionId - The ID of the collection to get the selected node from
|
|
10
|
+
* @returns UseNodeResult with isPresent=true when a node is selected, or isPresent=false with empty data
|
|
11
|
+
*/
|
|
12
|
+
export declare function useSelectedNode<T extends object, C, NodeType = string>(collectionId: string): UseNodeResult<T, C, NodeType>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SyncState, Change, SyncError } from './types';
|
|
2
|
+
export type UseSyncStateResult<T = unknown> = {
|
|
3
|
+
syncState: SyncState;
|
|
4
|
+
syncing: boolean;
|
|
5
|
+
queue: Map<string, Change<T>[]>;
|
|
6
|
+
inFlight: Map<string, Change<T>[]>;
|
|
7
|
+
errors: Map<string, SyncError<T>>;
|
|
8
|
+
isPaused: boolean;
|
|
9
|
+
isSyncing: boolean;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Hook for accessing the sync state of a collection by ID.
|
|
13
|
+
*
|
|
14
|
+
* Use this when you need to display sync status in a component that
|
|
15
|
+
* doesn't have access to the full useCrud hook.
|
|
16
|
+
*
|
|
17
|
+
* @param collectionId - The ID of the collection
|
|
18
|
+
* @returns Flat object with syncState and all syncQueue properties
|
|
19
|
+
*/
|
|
20
|
+
export declare function useSyncState<T = unknown>(collectionId: string): UseSyncStateResult<T>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "use-abcd",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.2",
|
|
4
4
|
"description": "Simple react hook for CRUD type apps",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "https://github.com/smtrd3/use-abcd"
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"@vitejs/plugin-react": "^5.0.0",
|
|
54
54
|
"@vitest/coverage-v8": "^3.2.4",
|
|
55
55
|
"ajv": "^8.17.1",
|
|
56
|
+
"baseline-browser-mapping": "^2.9.14",
|
|
56
57
|
"eslint": "^9.33.0",
|
|
57
58
|
"eslint-config-prettier": "^10.1.8",
|
|
58
59
|
"eslint-plugin-prettier": "^5.5.4",
|