@cetapod/react-native-smb 1.0.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.
- package/LICENSE +20 -0
- package/README.md +72 -0
- package/ReactNativeSmb.podspec +59 -0
- package/cpp/ios/OnLoad.mm +24 -0
- package/cpp/shared/HybridSMB.cpp +324 -0
- package/cpp/shared/HybridSMB.hpp +102 -0
- package/cpp/shared/ReactNativeSmb.hpp +428 -0
- package/cpp/shared/connection/ContextRequest.hpp +27 -0
- package/cpp/shared/connection/ContextRequestQueue.cpp +85 -0
- package/cpp/shared/connection/ContextRequestQueue.hpp +28 -0
- package/cpp/shared/connection/MetadataContextLease.hpp +33 -0
- package/cpp/shared/connection/PoolContextHandle.cpp +35 -0
- package/cpp/shared/connection/PoolContextHandle.hpp +44 -0
- package/cpp/shared/connection/PoolSlot.cpp +73 -0
- package/cpp/shared/connection/PoolSlot.hpp +46 -0
- package/cpp/shared/connection/PoolTypes.hpp +41 -0
- package/cpp/shared/connection/SmbConnection.cpp +385 -0
- package/cpp/shared/connection/SmbConnection.hpp +93 -0
- package/cpp/shared/connection/SmbConnectionPool.cpp +316 -0
- package/cpp/shared/connection/SmbConnectionPool.hpp +84 -0
- package/cpp/shared/core/CancellationToken.hpp +22 -0
- package/cpp/shared/core/OperatorContext.hpp +24 -0
- package/cpp/shared/core/SmbEnums.hpp +126 -0
- package/cpp/shared/core/SmbTask.cpp +131 -0
- package/cpp/shared/core/SmbTask.hpp +52 -0
- package/cpp/shared/core/SmbTaskCore.cpp +222 -0
- package/cpp/shared/core/SmbTaskCore.hpp +107 -0
- package/cpp/shared/core/SmbTypes.hpp +35 -0
- package/cpp/shared/core/TaskObserverHub.cpp +166 -0
- package/cpp/shared/core/TaskObserverHub.hpp +66 -0
- package/cpp/shared/core/TaskSnapshotCodec.cpp +32 -0
- package/cpp/shared/core/TaskSnapshotCodec.hpp +41 -0
- package/cpp/shared/io/SmbCopyTree.hpp +114 -0
- package/cpp/shared/io/SmbDirScan.hpp +80 -0
- package/cpp/shared/io/SmbFileIO.cpp +1355 -0
- package/cpp/shared/io/SmbFileIO.hpp +45 -0
- package/cpp/shared/io/SmbPathUtil.hpp +75 -0
- package/cpp/shared/io/SmbSecurity.cpp +308 -0
- package/cpp/shared/io/SmbSecurity.hpp +32 -0
- package/cpp/shared/io/SmbTreeOps.hpp +112 -0
- package/cpp/shared/operators/Operator.hpp +35 -0
- package/cpp/shared/operators/OperatorBase.cpp +57 -0
- package/cpp/shared/operators/OperatorBase.hpp +57 -0
- package/cpp/shared/operators/connection/ConnectOperator.cpp +19 -0
- package/cpp/shared/operators/connection/ConnectOperator.hpp +22 -0
- package/cpp/shared/operators/connection/ConnectShareOperator.cpp +19 -0
- package/cpp/shared/operators/connection/ConnectShareOperator.hpp +21 -0
- package/cpp/shared/operators/connection/DisconnectOperator.cpp +12 -0
- package/cpp/shared/operators/connection/DisconnectOperator.hpp +15 -0
- package/cpp/shared/operators/connection/InitializeOperator.cpp +12 -0
- package/cpp/shared/operators/connection/InitializeOperator.hpp +22 -0
- package/cpp/shared/operators/connection/ListSharesOperator.cpp +13 -0
- package/cpp/shared/operators/connection/ListSharesOperator.hpp +18 -0
- package/cpp/shared/operators/listing/GetPathInfoOperator.cpp +43 -0
- package/cpp/shared/operators/listing/GetPathInfoOperator.hpp +21 -0
- package/cpp/shared/operators/listing/GetSecurityDescriptorOperator.cpp +202 -0
- package/cpp/shared/operators/listing/GetSecurityDescriptorOperator.hpp +22 -0
- package/cpp/shared/operators/listing/ListDirectoryOperator.cpp +20 -0
- package/cpp/shared/operators/listing/ListDirectoryOperator.hpp +24 -0
- package/cpp/shared/operators/mutate/CopyItemOperator.cpp +19 -0
- package/cpp/shared/operators/mutate/CopyItemOperator.hpp +25 -0
- package/cpp/shared/operators/mutate/CreateDirectoryOperator.cpp +28 -0
- package/cpp/shared/operators/mutate/CreateDirectoryOperator.hpp +20 -0
- package/cpp/shared/operators/mutate/DeleteItemOperator.cpp +96 -0
- package/cpp/shared/operators/mutate/DeleteItemOperator.hpp +23 -0
- package/cpp/shared/operators/mutate/DuplicateItemOperator.cpp +48 -0
- package/cpp/shared/operators/mutate/DuplicateItemOperator.hpp +23 -0
- package/cpp/shared/operators/mutate/MoveItemOperator.cpp +32 -0
- package/cpp/shared/operators/mutate/MoveItemOperator.hpp +21 -0
- package/cpp/shared/operators/mutate/RenameItemOperator.cpp +37 -0
- package/cpp/shared/operators/mutate/RenameItemOperator.hpp +21 -0
- package/cpp/shared/operators/transfer/DownloadFileOperator.cpp +31 -0
- package/cpp/shared/operators/transfer/DownloadFileOperator.hpp +21 -0
- package/cpp/shared/operators/transfer/UploadFileOperator.cpp +27 -0
- package/cpp/shared/operators/transfer/UploadFileOperator.hpp +21 -0
- package/cpp/shared/util/SmbErrorMapper.hpp +229 -0
- package/cpp/shared/util/SmbLog.hpp +20 -0
- package/docs/API.md +105 -0
- package/docs/ARCHITECTURE.md +70 -0
- package/docs/TASK_MODEL.md +149 -0
- package/ios/include/smb2/libsmb2-dcerpc-lsa.h +172 -0
- package/ios/include/smb2/libsmb2-dcerpc-srvsvc.h +180 -0
- package/ios/include/smb2/libsmb2-dcerpc.h +155 -0
- package/ios/include/smb2/libsmb2-raw.h +497 -0
- package/ios/include/smb2/libsmb2.h +1384 -0
- package/ios/include/smb2/smb2-errors.h +549 -0
- package/ios/include/smb2/smb2.h +1251 -0
- package/ios/libs/device/libsmb2-device.a +0 -0
- package/ios/libs/libsmb2.xcframework/Info.plist +48 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64/Headers/smb2/libsmb2-dcerpc-lsa.h +172 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64/Headers/smb2/libsmb2-dcerpc-srvsvc.h +180 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64/Headers/smb2/libsmb2-dcerpc.h +155 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64/Headers/smb2/libsmb2-raw.h +497 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64/Headers/smb2/libsmb2.h +1384 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64/Headers/smb2/smb2-errors.h +549 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64/Headers/smb2/smb2.h +1251 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64/libsmb2.a +0 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/Headers/smb2/libsmb2-dcerpc-lsa.h +172 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/Headers/smb2/libsmb2-dcerpc-srvsvc.h +180 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/Headers/smb2/libsmb2-dcerpc.h +155 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/Headers/smb2/libsmb2-raw.h +497 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/Headers/smb2/libsmb2.h +1384 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/Headers/smb2/smb2-errors.h +549 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/Headers/smb2/smb2.h +1251 -0
- package/ios/libs/libsmb2.xcframework/ios-arm64_x86_64-simulator/libsmb2.a +0 -0
- package/ios/libs/simulator/libsmb2-simulator.a +0 -0
- package/lib/ReactNativeSmb.d.ts +200 -0
- package/lib/ReactNativeSmb.js +1 -0
- package/lib/bridge/decode.d.ts +4 -0
- package/lib/bridge/decode.js +92 -0
- package/lib/core/SMB.d.ts +3 -0
- package/lib/core/SMB.js +4 -0
- package/lib/core/SmbClient.d.ts +35 -0
- package/lib/core/SmbClient.js +99 -0
- package/lib/core/SmbTask.d.ts +41 -0
- package/lib/core/SmbTask.js +74 -0
- package/lib/core/TransferStore.d.ts +22 -0
- package/lib/core/TransferStore.js +88 -0
- package/lib/core/ids.d.ts +1 -0
- package/lib/core/ids.js +3 -0
- package/lib/core/taskResult.d.ts +3 -0
- package/lib/core/taskResult.js +52 -0
- package/lib/hooks/useSubscribe.d.ts +20 -0
- package/lib/hooks/useSubscribe.js +36 -0
- package/lib/hooks/useTransfers.d.ts +7 -0
- package/lib/hooks/useTransfers.js +29 -0
- package/lib/index.d.ts +7 -0
- package/lib/index.js +7 -0
- package/lib/labels.d.ts +6 -0
- package/lib/labels.js +71 -0
- package/lib/types.d.ts +141 -0
- package/lib/types.js +59 -0
- package/lib/utils.d.ts +79 -0
- package/lib/utils.js +153 -0
- package/package.json +63 -0
- package/scripts/build-libsmb2.sh +143 -0
- package/scripts/create-xcframework.sh +84 -0
|
Binary file
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import type { HybridObject } from 'react-native-nitro-modules';
|
|
2
|
+
import type { SmbCredentials, NativeSmbTask } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* A Nitro module that provides SMB client functionality using libsmb2
|
|
5
|
+
*
|
|
6
|
+
* This interface provides low-level access to SMB operations with proper
|
|
7
|
+
* TypeScript typing for React Native applications.
|
|
8
|
+
*/
|
|
9
|
+
export interface ReactNativeSmb extends HybridObject<{
|
|
10
|
+
ios: 'c++';
|
|
11
|
+
android: 'c++';
|
|
12
|
+
}> {
|
|
13
|
+
/**
|
|
14
|
+
* Checks connection status
|
|
15
|
+
* @returns True if currently connected to an SMB server
|
|
16
|
+
*/
|
|
17
|
+
isConnected(): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Checks if the SMB client is initialized
|
|
20
|
+
* @returns True if initialized, false otherwise
|
|
21
|
+
*/
|
|
22
|
+
isInitialized(): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Initializes connection to an SMB server (without connecting to a specific share)
|
|
25
|
+
* @param url Server URL (e.g., "smb://server-ip")
|
|
26
|
+
* @param credentials Authentication credentials
|
|
27
|
+
* @returns SmbTask that resolves when initialized
|
|
28
|
+
*/
|
|
29
|
+
initialize(taskId: string, url: string, credentials: SmbCredentials): NativeSmbTask;
|
|
30
|
+
/**
|
|
31
|
+
* Establishes a connection to an SMB server
|
|
32
|
+
* @param url The SMB server URL (e.g., "smb://server-ip/share")
|
|
33
|
+
* @param credentials Authentication credentials
|
|
34
|
+
* @returns SmbTask with connection information
|
|
35
|
+
* @throws {Error} When connection fails
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* await smb.connect('smb://192.168.1.100', {
|
|
39
|
+
* username: 'admin',
|
|
40
|
+
* password: 'secret'
|
|
41
|
+
* });
|
|
42
|
+
*/
|
|
43
|
+
connect(taskId: string, url: string, credentials: SmbCredentials): NativeSmbTask;
|
|
44
|
+
/**
|
|
45
|
+
* Connects to a specific share
|
|
46
|
+
* @param share Name of the share to connect to
|
|
47
|
+
* @returns SmbTask that resolves when connected
|
|
48
|
+
* @throws {Error} If not initialized or share doesn't exist
|
|
49
|
+
*/
|
|
50
|
+
connectShare(taskId: string, share: string): NativeSmbTask;
|
|
51
|
+
/**
|
|
52
|
+
* Disconnects from the current SMB server
|
|
53
|
+
* @returns SmbTask that resolves when disconnected
|
|
54
|
+
*/
|
|
55
|
+
disconnect(taskId: string): NativeSmbTask;
|
|
56
|
+
/**
|
|
57
|
+
* Lists available shares on the server
|
|
58
|
+
* @returns SmbTask resolving to array of share information
|
|
59
|
+
* @throws {Error} If not initialized
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* const shares = await smb.listShares();
|
|
63
|
+
* shares.forEach(share => console.log(share.name));
|
|
64
|
+
*/
|
|
65
|
+
listShares(taskId: string): NativeSmbTask;
|
|
66
|
+
/**
|
|
67
|
+
* Lists contents of a directory
|
|
68
|
+
* @param path Directory path (use "/" for root)
|
|
69
|
+
* @param recursive Whether to list recursively (default: false)
|
|
70
|
+
* @param maxDepth Maximum recursion depth (-1 = unlimited, 0 = current only)
|
|
71
|
+
* @param taskId Unique ID for the task
|
|
72
|
+
* @param onStatusChange Callback for status updates
|
|
73
|
+
* @param onProgress Callback for progress updates
|
|
74
|
+
* @returns SmbTask resolving to array of file/directory info
|
|
75
|
+
*/
|
|
76
|
+
listDirectory(taskId: string, path: string, recursive: boolean, maxDepth: number): NativeSmbTask;
|
|
77
|
+
/**
|
|
78
|
+
* Gets detailed information about a path (file or directory).
|
|
79
|
+
* Generic version that handles both file/directory types.
|
|
80
|
+
* @param path Path to the item
|
|
81
|
+
* @param taskId Unique ID for the task
|
|
82
|
+
* @param onStatusChange Callback for status updates
|
|
83
|
+
* @param onProgress Callback for progress updates
|
|
84
|
+
* @returns SmbTask resolving to file information
|
|
85
|
+
*/
|
|
86
|
+
getPathInfo(taskId: string, path: string): NativeSmbTask;
|
|
87
|
+
/**
|
|
88
|
+
* Retrieves security descriptor for a file/directory
|
|
89
|
+
* @param path Path to the item
|
|
90
|
+
* @param taskId Unique ID for the task
|
|
91
|
+
* @returns SmbTask resolving to security descriptor
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* const security = await smb.getSecurityDescriptor('/important.txt');
|
|
95
|
+
* console.log('Owner:', security.ownerSid);
|
|
96
|
+
*/
|
|
97
|
+
getSecurityDescriptor(taskId: string, path: string): NativeSmbTask;
|
|
98
|
+
downloadFile(taskId: string, remotePath: string, localPath: string): NativeSmbTask;
|
|
99
|
+
/**
|
|
100
|
+
* Uploads a file from local storage to the SMB server
|
|
101
|
+
* @param localPath Local path to the file
|
|
102
|
+
* @param remotePath Destination path on the server
|
|
103
|
+
* @param progressHandler Optional callback to track upload progress
|
|
104
|
+
* @returns SmbTask that resolves when upload completes
|
|
105
|
+
*/
|
|
106
|
+
uploadFile(taskId: string, localPath: string, remotePath: string): NativeSmbTask;
|
|
107
|
+
/**
|
|
108
|
+
* Creates a new directory
|
|
109
|
+
* @param path Path of the directory to create
|
|
110
|
+
* @returns SmbTask that resolves when directory is created
|
|
111
|
+
*/
|
|
112
|
+
createDirectory(taskId: string, path: string): NativeSmbTask;
|
|
113
|
+
/**
|
|
114
|
+
* Deletes a file or directory (automatically handles recursive deletion)
|
|
115
|
+
* @param path Path to the item to delete
|
|
116
|
+
* @returns SmbTask that resolves when deletion completes
|
|
117
|
+
*/
|
|
118
|
+
deleteItem(taskId: string, path: string): NativeSmbTask;
|
|
119
|
+
/**
|
|
120
|
+
* Moves or renames a file/directory
|
|
121
|
+
* @param fromPath Current path
|
|
122
|
+
* @param toPath New path
|
|
123
|
+
* @returns SmbTask that resolves when move completes
|
|
124
|
+
*/
|
|
125
|
+
moveItem(taskId: string, fromPath: string, toPath: string): NativeSmbTask;
|
|
126
|
+
/**
|
|
127
|
+
* Renames a file/directory (in the same directory)
|
|
128
|
+
* @param currentPath Current path
|
|
129
|
+
* @param newName New name (without path)
|
|
130
|
+
* @returns SmbTask that resolves when rename completes
|
|
131
|
+
*/
|
|
132
|
+
renameItem(taskId: string, currentPath: string, newName: string): NativeSmbTask;
|
|
133
|
+
/**
|
|
134
|
+
* Copies a file or directory on the SMB server
|
|
135
|
+
* @param fromPath Source path
|
|
136
|
+
* @param toPath Destination path
|
|
137
|
+
* @param recursive Whether to copy recursively
|
|
138
|
+
* @param taskId Unique ID for the task
|
|
139
|
+
* @param onStatusChange Callback for status updates
|
|
140
|
+
* @param onProgress Callback for progress updates
|
|
141
|
+
*/
|
|
142
|
+
copyItem(taskId: string, fromPath: string, toPath: string, recursive: boolean): NativeSmbTask;
|
|
143
|
+
/**
|
|
144
|
+
* Duplicates an item giving it a unique name
|
|
145
|
+
* @param path Path to the item
|
|
146
|
+
* @returns SmbTask resolving to the new path
|
|
147
|
+
*/
|
|
148
|
+
duplicateItem(taskId: string, path: string): NativeSmbTask;
|
|
149
|
+
/**
|
|
150
|
+
* Subscribe to task events (created, status, progress, settled, removed)
|
|
151
|
+
* @param listener Callback that receives task events
|
|
152
|
+
* @returns Subscription ID for unsubscribing
|
|
153
|
+
*/
|
|
154
|
+
subscribeTaskEvents(listener: (event: Record<string, string>) => void): string;
|
|
155
|
+
/**
|
|
156
|
+
* Unsubscribe from task events
|
|
157
|
+
* @param subscriptionId The subscription ID returned from subscribeTaskEvents
|
|
158
|
+
*/
|
|
159
|
+
unsubscribeTaskEvents(subscriptionId: string): void;
|
|
160
|
+
/**
|
|
161
|
+
* Get a specific task snapshot by ID
|
|
162
|
+
* @param taskId The task ID to retrieve
|
|
163
|
+
* @returns Task snapshot as string-keyed map, or {exists: '0'} if not found
|
|
164
|
+
*/
|
|
165
|
+
getTask(taskId: string): Record<string, string>;
|
|
166
|
+
/**
|
|
167
|
+
* Get all currently active (pending/running) tasks
|
|
168
|
+
* @returns Array of task snapshots
|
|
169
|
+
*/
|
|
170
|
+
getActiveTasks(): Array<Record<string, string>>;
|
|
171
|
+
/**
|
|
172
|
+
* Get task history (settled tasks)
|
|
173
|
+
* @param limit Maximum number of tasks to return
|
|
174
|
+
* @param offset Number of tasks to skip
|
|
175
|
+
* @returns Array of task snapshots
|
|
176
|
+
*/
|
|
177
|
+
getTaskHistory(limit: number, offset: number): Array<Record<string, string>>;
|
|
178
|
+
/**
|
|
179
|
+
* Cancels a specific task by ID
|
|
180
|
+
*/
|
|
181
|
+
cancelTask(taskId: string): void;
|
|
182
|
+
/**
|
|
183
|
+
* Clear task history
|
|
184
|
+
* @param beforeTs Clear tasks that ended before this timestamp (0 = clear all)
|
|
185
|
+
*/
|
|
186
|
+
clearTaskHistory(beforeTs: bigint): void;
|
|
187
|
+
subscribePoolInfo(listener: (snapshot: Array<{
|
|
188
|
+
[key: string]: string;
|
|
189
|
+
}>) => void): string;
|
|
190
|
+
unsubscribePoolInfo(subscriptionId: string): void;
|
|
191
|
+
getPoolInfo(): Array<{
|
|
192
|
+
[key: string]: string;
|
|
193
|
+
}>;
|
|
194
|
+
/**
|
|
195
|
+
* Force-disconnects every connection-pool slot and forgets cached config.
|
|
196
|
+
* Use for recovery when slots become stuck (`isConnected:false` but
|
|
197
|
+
* `inUse:true`). Subsequent operations must re-connect from scratch.
|
|
198
|
+
*/
|
|
199
|
+
resetPool(): void;
|
|
200
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { TaskStatus, type PoolInfo, type SmbTaskState } from '../types';
|
|
2
|
+
export declare function shapeTaskSnapshot(raw: Record<string, string>): SmbTaskState | null;
|
|
3
|
+
export declare function shapePoolInfo(raw: Array<Record<string, string>>): PoolInfo;
|
|
4
|
+
export declare function terminalStatus(raw: Record<string, string>): TaskStatus | null;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { SmbOperatorKind, SlotState, TaskStatus, } from '../types';
|
|
2
|
+
function parseIntField(raw, key) {
|
|
3
|
+
const v = raw[key];
|
|
4
|
+
if (v === undefined || v === '') {
|
|
5
|
+
throw new Error(`Missing bridge field: ${key}`);
|
|
6
|
+
}
|
|
7
|
+
const n = parseInt(v, 10);
|
|
8
|
+
if (!Number.isFinite(n)) {
|
|
9
|
+
throw new Error(`Invalid bridge field ${key}: ${v}`);
|
|
10
|
+
}
|
|
11
|
+
return n;
|
|
12
|
+
}
|
|
13
|
+
function parseIntOptional(raw, key, fallback = 0) {
|
|
14
|
+
const v = raw[key];
|
|
15
|
+
if (v === undefined || v === '')
|
|
16
|
+
return fallback;
|
|
17
|
+
const n = parseInt(v, 10);
|
|
18
|
+
return Number.isFinite(n) ? n : fallback;
|
|
19
|
+
}
|
|
20
|
+
function parseFloatField(raw, key, fallback = 0) {
|
|
21
|
+
const v = raw[key];
|
|
22
|
+
if (v === undefined || v === '')
|
|
23
|
+
return fallback;
|
|
24
|
+
const n = parseFloat(v);
|
|
25
|
+
return Number.isFinite(n) ? n : fallback;
|
|
26
|
+
}
|
|
27
|
+
function parseBoolField(raw, key) {
|
|
28
|
+
const v = raw[key];
|
|
29
|
+
return v === '1' || v === 'true';
|
|
30
|
+
}
|
|
31
|
+
function enumFromInt(value, enumObj) {
|
|
32
|
+
const valid = Object.values(enumObj).filter((v) => typeof v === 'number');
|
|
33
|
+
if (valid.includes(value)) {
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
throw new Error(`Invalid enum value: ${value}`);
|
|
37
|
+
}
|
|
38
|
+
export function shapeTaskSnapshot(raw) {
|
|
39
|
+
if (raw.exists === '0')
|
|
40
|
+
return null;
|
|
41
|
+
const kind = enumFromInt(parseIntField(raw, 'kind'), SmbOperatorKind);
|
|
42
|
+
const status = enumFromInt(parseIntField(raw, 'status'), TaskStatus);
|
|
43
|
+
return {
|
|
44
|
+
taskId: raw.taskId ?? '',
|
|
45
|
+
kind,
|
|
46
|
+
status,
|
|
47
|
+
determinate: parseBoolField(raw, 'determinate'),
|
|
48
|
+
progress: parseFloatField(raw, 'progress'),
|
|
49
|
+
bytesDone: parseIntOptional(raw, 'bytesDone'),
|
|
50
|
+
totalBytes: parseIntOptional(raw, 'totalBytes'),
|
|
51
|
+
bytesPerSecond: parseFloatField(raw, 'bytesPerSecond'),
|
|
52
|
+
etaSeconds: parseFloatField(raw, 'etaSeconds'),
|
|
53
|
+
sourcePath: raw.sourcePath ?? '',
|
|
54
|
+
destinationPath: raw.destinationPath ?? '',
|
|
55
|
+
errorCode: parseIntOptional(raw, 'errorCode'),
|
|
56
|
+
errorMessage: raw.errorMessage ?? '',
|
|
57
|
+
startedAt: parseIntOptional(raw, 'startedAt'),
|
|
58
|
+
updatedAt: parseIntOptional(raw, 'updatedAt'),
|
|
59
|
+
endedAt: parseIntOptional(raw, 'endedAt'),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function shapePoolSlot(raw) {
|
|
63
|
+
return {
|
|
64
|
+
index: parseIntField(raw, 'index'),
|
|
65
|
+
interactiveOnly: parseBoolField(raw, 'interactiveOnly'),
|
|
66
|
+
state: enumFromInt(parseIntField(raw, 'state'), SlotState),
|
|
67
|
+
isConnected: parseBoolField(raw, 'isConnected'),
|
|
68
|
+
shareName: raw.shareName ?? '',
|
|
69
|
+
taskId: raw.taskId ?? '',
|
|
70
|
+
kind: enumFromInt(parseIntField(raw, 'kind'), SmbOperatorKind),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export function shapePoolInfo(raw) {
|
|
74
|
+
if (!raw || raw.length === 0)
|
|
75
|
+
return { poolSize: 0, slots: [] };
|
|
76
|
+
const poolSize = parseIntField(raw[0], 'poolSize');
|
|
77
|
+
return {
|
|
78
|
+
poolSize,
|
|
79
|
+
slots: raw.map(shapePoolSlot),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export function terminalStatus(raw) {
|
|
83
|
+
if (raw.exists === '0')
|
|
84
|
+
return null;
|
|
85
|
+
const status = enumFromInt(parseIntField(raw, 'status'), TaskStatus);
|
|
86
|
+
if (status === TaskStatus.Success ||
|
|
87
|
+
status === TaskStatus.Error ||
|
|
88
|
+
status === TaskStatus.Cancelled) {
|
|
89
|
+
return status;
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
package/lib/core/SMB.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ReactNativeSmb } from '../ReactNativeSmb';
|
|
2
|
+
import type { PoolInfo, SmbConnectionInfo, SmbCredentials, SmbFileInfo, SmbSecurityDescriptor, SmbShareList } from '../types';
|
|
3
|
+
import { SmbTask } from './SmbTask';
|
|
4
|
+
import { TransferStore } from './TransferStore';
|
|
5
|
+
export declare class SmbClient {
|
|
6
|
+
private readonly native;
|
|
7
|
+
private transfers_?;
|
|
8
|
+
constructor(native?: ReactNativeSmb);
|
|
9
|
+
private op;
|
|
10
|
+
transferStore(): TransferStore;
|
|
11
|
+
dispose(): void;
|
|
12
|
+
isConnected(): boolean;
|
|
13
|
+
isInitialized(): boolean;
|
|
14
|
+
initialize(url: string, creds: SmbCredentials): SmbTask<void>;
|
|
15
|
+
connect(url: string, creds: SmbCredentials): SmbTask<SmbConnectionInfo>;
|
|
16
|
+
connectShare(share: string): SmbTask<SmbConnectionInfo>;
|
|
17
|
+
disconnect(): SmbTask<void>;
|
|
18
|
+
listShares(): SmbTask<SmbShareList[]>;
|
|
19
|
+
listDirectory(path: string, recursive?: boolean, maxDepth?: number): SmbTask<SmbFileInfo[]>;
|
|
20
|
+
getPathInfo(path: string): SmbTask<SmbFileInfo>;
|
|
21
|
+
getSecurityDescriptor(path: string): SmbTask<SmbSecurityDescriptor>;
|
|
22
|
+
downloadFile(remotePath: string, localPath: string): SmbTask<void>;
|
|
23
|
+
uploadFile(localPath: string, remotePath: string): SmbTask<void>;
|
|
24
|
+
createDirectory(path: string): SmbTask<void>;
|
|
25
|
+
deleteItem(path: string): SmbTask<void>;
|
|
26
|
+
moveItem(from: string, to: string): SmbTask<void>;
|
|
27
|
+
renameItem(path: string, newName: string): SmbTask<void>;
|
|
28
|
+
copyItem(from: string, to: string, recursive?: boolean): SmbTask<void>;
|
|
29
|
+
duplicateItem(path: string): SmbTask<string>;
|
|
30
|
+
cancelTask(taskId: string): void;
|
|
31
|
+
getPoolInfo(): PoolInfo;
|
|
32
|
+
subscribePoolInfo(cb: (info: PoolInfo) => void): string;
|
|
33
|
+
unsubscribePoolInfo(id: string | null): void;
|
|
34
|
+
resetPool(): void;
|
|
35
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { NitroModules } from 'react-native-nitro-modules';
|
|
2
|
+
import { shapePoolInfo } from '../bridge/decode';
|
|
3
|
+
import { generateTaskId } from './ids';
|
|
4
|
+
import { SmbTask } from './SmbTask';
|
|
5
|
+
import { TransferStore } from './TransferStore';
|
|
6
|
+
export class SmbClient {
|
|
7
|
+
constructor(native) {
|
|
8
|
+
this.native =
|
|
9
|
+
native ?? NitroModules.createHybridObject('ReactNativeSmb');
|
|
10
|
+
}
|
|
11
|
+
op(call) {
|
|
12
|
+
const id = generateTaskId();
|
|
13
|
+
return new SmbTask(id, call(id));
|
|
14
|
+
}
|
|
15
|
+
transferStore() {
|
|
16
|
+
if (!this.transfers_) {
|
|
17
|
+
this.transfers_ = new TransferStore(this.native);
|
|
18
|
+
}
|
|
19
|
+
return this.transfers_;
|
|
20
|
+
}
|
|
21
|
+
dispose() {
|
|
22
|
+
this.transfers_?.stop();
|
|
23
|
+
this.transfers_ = undefined;
|
|
24
|
+
}
|
|
25
|
+
isConnected() {
|
|
26
|
+
return this.native.isConnected();
|
|
27
|
+
}
|
|
28
|
+
isInitialized() {
|
|
29
|
+
return this.native.isInitialized();
|
|
30
|
+
}
|
|
31
|
+
initialize(url, creds) {
|
|
32
|
+
return this.op((id) => this.native.initialize(id, url, creds));
|
|
33
|
+
}
|
|
34
|
+
connect(url, creds) {
|
|
35
|
+
return this.op((id) => this.native.connect(id, url, creds));
|
|
36
|
+
}
|
|
37
|
+
connectShare(share) {
|
|
38
|
+
return this.op((id) => this.native.connectShare(id, share));
|
|
39
|
+
}
|
|
40
|
+
disconnect() {
|
|
41
|
+
return this.op((id) => this.native.disconnect(id));
|
|
42
|
+
}
|
|
43
|
+
listShares() {
|
|
44
|
+
return this.op((id) => this.native.listShares(id));
|
|
45
|
+
}
|
|
46
|
+
listDirectory(path, recursive = false, maxDepth = -1) {
|
|
47
|
+
return this.op((id) => this.native.listDirectory(id, path, recursive, maxDepth));
|
|
48
|
+
}
|
|
49
|
+
getPathInfo(path) {
|
|
50
|
+
return this.op((id) => this.native.getPathInfo(id, path));
|
|
51
|
+
}
|
|
52
|
+
getSecurityDescriptor(path) {
|
|
53
|
+
return this.op((id) => this.native.getSecurityDescriptor(id, path));
|
|
54
|
+
}
|
|
55
|
+
downloadFile(remotePath, localPath) {
|
|
56
|
+
return this.op((id) => this.native.downloadFile(id, remotePath, localPath));
|
|
57
|
+
}
|
|
58
|
+
uploadFile(localPath, remotePath) {
|
|
59
|
+
return this.op((id) => this.native.uploadFile(id, localPath, remotePath));
|
|
60
|
+
}
|
|
61
|
+
createDirectory(path) {
|
|
62
|
+
return this.op((id) => this.native.createDirectory(id, path));
|
|
63
|
+
}
|
|
64
|
+
deleteItem(path) {
|
|
65
|
+
return this.op((id) => this.native.deleteItem(id, path));
|
|
66
|
+
}
|
|
67
|
+
moveItem(from, to) {
|
|
68
|
+
return this.op((id) => this.native.moveItem(id, from, to));
|
|
69
|
+
}
|
|
70
|
+
renameItem(path, newName) {
|
|
71
|
+
return this.op((id) => this.native.renameItem(id, path, newName));
|
|
72
|
+
}
|
|
73
|
+
copyItem(from, to, recursive = false) {
|
|
74
|
+
return this.op((id) => this.native.copyItem(id, from, to, recursive));
|
|
75
|
+
}
|
|
76
|
+
duplicateItem(path) {
|
|
77
|
+
return this.op((id) => this.native.duplicateItem(id, path));
|
|
78
|
+
}
|
|
79
|
+
cancelTask(taskId) {
|
|
80
|
+
this.native.cancelTask(taskId);
|
|
81
|
+
}
|
|
82
|
+
getPoolInfo() {
|
|
83
|
+
return shapePoolInfo(this.native.getPoolInfo());
|
|
84
|
+
}
|
|
85
|
+
subscribePoolInfo(cb) {
|
|
86
|
+
const id = this.native.subscribePoolInfo((raw) => {
|
|
87
|
+
cb(shapePoolInfo(raw));
|
|
88
|
+
});
|
|
89
|
+
cb(this.getPoolInfo());
|
|
90
|
+
return id;
|
|
91
|
+
}
|
|
92
|
+
unsubscribePoolInfo(id) {
|
|
93
|
+
if (id)
|
|
94
|
+
this.native.unsubscribePoolInfo(id);
|
|
95
|
+
}
|
|
96
|
+
resetPool() {
|
|
97
|
+
this.native.resetPool();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { TaskStatus, type NativeSmbTask, type SmbTaskState } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Handle for one native async operation.
|
|
4
|
+
*
|
|
5
|
+
* Creating a task does not subscribe. Three optional actions:
|
|
6
|
+
* - {@link subscribe} — live progress / status
|
|
7
|
+
* - {@link result} — await completion and get the typed value
|
|
8
|
+
* - {@link cancel} — abort
|
|
9
|
+
*/
|
|
10
|
+
export declare class SmbTask<T = void> {
|
|
11
|
+
private readonly native;
|
|
12
|
+
readonly id: string;
|
|
13
|
+
private snapshot_;
|
|
14
|
+
constructor(id: string, native: NativeSmbTask);
|
|
15
|
+
/** 0–1 after {@link subscribe} has been called. */
|
|
16
|
+
get progress(): number;
|
|
17
|
+
/** Latest status after {@link subscribe}, or a one-shot read via {@link get}. */
|
|
18
|
+
get status(): TaskStatus;
|
|
19
|
+
cancel(): void;
|
|
20
|
+
/** One-shot snapshot without subscribing. */
|
|
21
|
+
get(): SmbTaskState | null;
|
|
22
|
+
/**
|
|
23
|
+
* Subscribe to live updates. Optional — call only when you need progress UI.
|
|
24
|
+
* Returns unsubscribe. Updates {@link progress} and {@link status} getters.
|
|
25
|
+
*/
|
|
26
|
+
subscribe(listener?: (snapshot: SmbTaskState) => void): () => void;
|
|
27
|
+
/**
|
|
28
|
+
* Await the typed result. Subscribes only when called — not at task creation.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* const task = smb.listDirectory('/', false, -1);
|
|
32
|
+
* const files = await task.result();
|
|
33
|
+
*/
|
|
34
|
+
result(): Promise<T>;
|
|
35
|
+
/** Like `Promise.allSettled` over {@link result}. */
|
|
36
|
+
static settleAll<T>(tasks: SmbTask<T>[]): Promise<PromiseSettledResult<T>[]>;
|
|
37
|
+
/** @internal */
|
|
38
|
+
getRaw(): Record<string, string>;
|
|
39
|
+
/** @internal */
|
|
40
|
+
getResultValue(): T;
|
|
41
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { shapeTaskSnapshot } from '../bridge/decode';
|
|
2
|
+
import { TaskStatus } from '../types';
|
|
3
|
+
import { taskResult } from './taskResult';
|
|
4
|
+
/**
|
|
5
|
+
* Handle for one native async operation.
|
|
6
|
+
*
|
|
7
|
+
* Creating a task does not subscribe. Three optional actions:
|
|
8
|
+
* - {@link subscribe} — live progress / status
|
|
9
|
+
* - {@link result} — await completion and get the typed value
|
|
10
|
+
* - {@link cancel} — abort
|
|
11
|
+
*/
|
|
12
|
+
export class SmbTask {
|
|
13
|
+
constructor(id, native) {
|
|
14
|
+
this.native = native;
|
|
15
|
+
this.snapshot_ = null;
|
|
16
|
+
this.id = id;
|
|
17
|
+
}
|
|
18
|
+
/** 0–1 after {@link subscribe} has been called. */
|
|
19
|
+
get progress() {
|
|
20
|
+
return this.snapshot_?.progress ?? this.get()?.progress ?? 0;
|
|
21
|
+
}
|
|
22
|
+
/** Latest status after {@link subscribe}, or a one-shot read via {@link get}. */
|
|
23
|
+
get status() {
|
|
24
|
+
return this.snapshot_?.status ?? this.get()?.status ?? TaskStatus.Pending;
|
|
25
|
+
}
|
|
26
|
+
cancel() {
|
|
27
|
+
this.native.cancel();
|
|
28
|
+
}
|
|
29
|
+
/** One-shot snapshot without subscribing. */
|
|
30
|
+
get() {
|
|
31
|
+
return shapeTaskSnapshot(this.native.get());
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Subscribe to live updates. Optional — call only when you need progress UI.
|
|
35
|
+
* Returns unsubscribe. Updates {@link progress} and {@link status} getters.
|
|
36
|
+
*/
|
|
37
|
+
subscribe(listener) {
|
|
38
|
+
const subId = this.native.subscribe((raw) => {
|
|
39
|
+
const snap = shapeTaskSnapshot(raw);
|
|
40
|
+
if (!snap)
|
|
41
|
+
return;
|
|
42
|
+
this.snapshot_ = snap;
|
|
43
|
+
listener?.(snap);
|
|
44
|
+
});
|
|
45
|
+
const initial = this.get();
|
|
46
|
+
if (initial) {
|
|
47
|
+
this.snapshot_ = initial;
|
|
48
|
+
listener?.(initial);
|
|
49
|
+
}
|
|
50
|
+
return () => this.native.unsubscribe(subId);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Await the typed result. Subscribes only when called — not at task creation.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* const task = smb.listDirectory('/', false, -1);
|
|
57
|
+
* const files = await task.result();
|
|
58
|
+
*/
|
|
59
|
+
result() {
|
|
60
|
+
return taskResult(this);
|
|
61
|
+
}
|
|
62
|
+
/** Like `Promise.allSettled` over {@link result}. */
|
|
63
|
+
static settleAll(tasks) {
|
|
64
|
+
return Promise.allSettled(tasks.map((task) => task.result()));
|
|
65
|
+
}
|
|
66
|
+
/** @internal */
|
|
67
|
+
getRaw() {
|
|
68
|
+
return this.native.get();
|
|
69
|
+
}
|
|
70
|
+
/** @internal */
|
|
71
|
+
getResultValue() {
|
|
72
|
+
return this.native.getResultValue();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ReactNativeSmb } from '../ReactNativeSmb';
|
|
2
|
+
import { type SmbTaskState } from '../types';
|
|
3
|
+
type Listener = () => void;
|
|
4
|
+
export declare class TransferStore {
|
|
5
|
+
private readonly native;
|
|
6
|
+
private tasks;
|
|
7
|
+
private hidden;
|
|
8
|
+
private listeners;
|
|
9
|
+
private subId;
|
|
10
|
+
private started;
|
|
11
|
+
private snapshot;
|
|
12
|
+
constructor(native: ReactNativeSmb);
|
|
13
|
+
start(): void;
|
|
14
|
+
stop(): void;
|
|
15
|
+
subscribe(listener: Listener): () => void;
|
|
16
|
+
getSnapshot(): SmbTaskState[];
|
|
17
|
+
private rebuildSnapshot;
|
|
18
|
+
clearCompleted(): void;
|
|
19
|
+
hide(taskId: string): void;
|
|
20
|
+
private notify;
|
|
21
|
+
}
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { shapeTaskSnapshot } from '../bridge/decode';
|
|
2
|
+
import { isTransferKind } from '../labels';
|
|
3
|
+
import { TaskStatus } from '../types';
|
|
4
|
+
const EMPTY_SNAPSHOT = [];
|
|
5
|
+
export class TransferStore {
|
|
6
|
+
constructor(native) {
|
|
7
|
+
this.native = native;
|
|
8
|
+
this.tasks = new Map();
|
|
9
|
+
this.hidden = new Set();
|
|
10
|
+
this.listeners = new Set();
|
|
11
|
+
this.subId = null;
|
|
12
|
+
this.started = false;
|
|
13
|
+
this.snapshot = EMPTY_SNAPSHOT;
|
|
14
|
+
}
|
|
15
|
+
start() {
|
|
16
|
+
if (this.started)
|
|
17
|
+
return;
|
|
18
|
+
this.started = true;
|
|
19
|
+
for (const raw of this.native.getActiveTasks()) {
|
|
20
|
+
const snap = shapeTaskSnapshot(raw);
|
|
21
|
+
if (snap && isTransferKind(snap.kind)) {
|
|
22
|
+
this.tasks.set(snap.taskId, snap);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
this.rebuildSnapshot();
|
|
26
|
+
this.subId = this.native.subscribeTaskEvents((raw) => {
|
|
27
|
+
try {
|
|
28
|
+
const snap = shapeTaskSnapshot(raw);
|
|
29
|
+
if (!snap || !isTransferKind(snap.kind))
|
|
30
|
+
return;
|
|
31
|
+
if (this.hidden.has(snap.taskId))
|
|
32
|
+
return;
|
|
33
|
+
this.tasks.set(snap.taskId, snap);
|
|
34
|
+
this.notify();
|
|
35
|
+
}
|
|
36
|
+
catch { }
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
stop() {
|
|
40
|
+
if (this.subId) {
|
|
41
|
+
this.native.unsubscribeTaskEvents(this.subId);
|
|
42
|
+
this.subId = null;
|
|
43
|
+
}
|
|
44
|
+
this.started = false;
|
|
45
|
+
}
|
|
46
|
+
subscribe(listener) {
|
|
47
|
+
this.start();
|
|
48
|
+
this.listeners.add(listener);
|
|
49
|
+
return () => this.listeners.delete(listener);
|
|
50
|
+
}
|
|
51
|
+
getSnapshot() {
|
|
52
|
+
this.start();
|
|
53
|
+
return this.snapshot;
|
|
54
|
+
}
|
|
55
|
+
rebuildSnapshot() {
|
|
56
|
+
if (this.tasks.size === 0) {
|
|
57
|
+
this.snapshot = EMPTY_SNAPSHOT;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
this.snapshot = Array.from(this.tasks.values())
|
|
61
|
+
.filter((t) => !this.hidden.has(t.taskId))
|
|
62
|
+
.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
63
|
+
}
|
|
64
|
+
clearCompleted() {
|
|
65
|
+
for (const [id, snap] of this.tasks) {
|
|
66
|
+
if (snap.status === TaskStatus.Success ||
|
|
67
|
+
snap.status === TaskStatus.Error ||
|
|
68
|
+
snap.status === TaskStatus.Cancelled) {
|
|
69
|
+
this.tasks.delete(id);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
this.notify();
|
|
73
|
+
}
|
|
74
|
+
hide(taskId) {
|
|
75
|
+
this.hidden.add(taskId);
|
|
76
|
+
this.tasks.delete(taskId);
|
|
77
|
+
this.notify();
|
|
78
|
+
}
|
|
79
|
+
notify() {
|
|
80
|
+
this.rebuildSnapshot();
|
|
81
|
+
this.listeners.forEach((l) => {
|
|
82
|
+
try {
|
|
83
|
+
l();
|
|
84
|
+
}
|
|
85
|
+
catch { }
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function generateTaskId(): string;
|
package/lib/core/ids.js
ADDED