@hamak/ui-remote-fs-impl 0.4.7 → 0.4.16
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/package.json +11 -8
- package/project.json +0 -24
- package/src/actions/index.ts +0 -5
- package/src/actions/remote-fs-actions.ts +0 -302
- package/src/index.ts +0 -10
- package/src/middleware/index.ts +0 -6
- package/src/middleware/remote-fs-middleware.ts +0 -244
- package/src/middleware/store-sync-middleware.ts +0 -175
- package/src/plugin/index.ts +0 -5
- package/src/plugin/remote-fs-plugin-factory.ts +0 -238
- package/src/providers/http-workspace-client.ts +0 -232
- package/src/providers/index.ts +0 -5
- package/src/services/index.ts +0 -5
- package/src/services/remote-fs-service.ts +0 -13
- package/tsconfig.es2015.json +0 -21
- package/tsconfig.json +0 -19
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hamak/ui-remote-fs-impl",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.16",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Remote FS Implementation - HTTP-based remote filesystem",
|
|
7
7
|
"main": "dist/index.js",
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
9
9
|
"sideEffects": false,
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
10
13
|
"repository": {
|
|
11
14
|
"type": "git",
|
|
12
15
|
"url": "https://github.com/amah/app-framework.git",
|
|
@@ -34,18 +37,18 @@
|
|
|
34
37
|
}
|
|
35
38
|
},
|
|
36
39
|
"dependencies": {
|
|
37
|
-
"@hamak/ui-remote-fs-api": "
|
|
38
|
-
"@hamak/ui-remote-fs-spi": "
|
|
39
|
-
"@hamak/ui-store-api": "
|
|
40
|
-
"@hamak/ui-store-impl": "
|
|
41
|
-
"@hamak/microkernel-api": "
|
|
42
|
-
"@hamak/microkernel-spi": "
|
|
40
|
+
"@hamak/ui-remote-fs-api": "*",
|
|
41
|
+
"@hamak/ui-remote-fs-spi": "*",
|
|
42
|
+
"@hamak/ui-store-api": "*",
|
|
43
|
+
"@hamak/ui-store-impl": "*",
|
|
44
|
+
"@hamak/microkernel-api": "*",
|
|
45
|
+
"@hamak/microkernel-spi": "*",
|
|
43
46
|
"@reduxjs/toolkit": "^2.0.0",
|
|
44
47
|
"axios": "^1.6.0",
|
|
45
48
|
"redux": "^5.0.1"
|
|
46
49
|
},
|
|
47
50
|
"peerDependencies": {
|
|
48
|
-
"@hamak/shared-utils": "
|
|
51
|
+
"@hamak/shared-utils": "*"
|
|
49
52
|
},
|
|
50
53
|
"devDependencies": {
|
|
51
54
|
"typescript": "~5.4.0",
|
package/project.json
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@hamak/ui-remote-fs-impl",
|
|
3
|
-
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
-
"sourceRoot": "packages/ui-remote-fs/ui-remote-fs-impl/src",
|
|
5
|
-
"projectType": "library",
|
|
6
|
-
"targets": {
|
|
7
|
-
"build": {
|
|
8
|
-
"executor": "nx:run-commands",
|
|
9
|
-
"outputs": ["{projectRoot}/dist"],
|
|
10
|
-
"options": {
|
|
11
|
-
"command": "tsc -p tsconfig.json && tsc -p tsconfig.es2015.json",
|
|
12
|
-
"cwd": "{projectRoot}"
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
"clean": {
|
|
16
|
-
"executor": "nx:run-commands",
|
|
17
|
-
"options": {
|
|
18
|
-
"command": "rm -rf dist",
|
|
19
|
-
"cwd": "{projectRoot}"
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
"tags": ["type:library", "scope:ui-remote-fs"]
|
|
24
|
-
}
|
package/src/actions/index.ts
DELETED
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Remote FS Actions
|
|
3
|
-
*
|
|
4
|
-
* Action creators for remote filesystem operations.
|
|
5
|
-
* Migrated from amk/libs/ui/core/remote-fs/src/lib/remote-fs-actions.ts
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { RemoteFsActionTypes, ErrorObject } from '@hamak/ui-remote-fs-api';
|
|
9
|
-
import { FileInfo } from '@hamak/shared-utils';
|
|
10
|
-
|
|
11
|
-
// Interfaces for action payloads
|
|
12
|
-
export interface LsRequestAction {
|
|
13
|
-
type: RemoteFsActionTypes.LS_REQUEST;
|
|
14
|
-
payload: { path: string | string[] };
|
|
15
|
-
[key: string]: any;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface LsCompletedAction {
|
|
19
|
-
type: RemoteFsActionTypes.LS_COMPLETED;
|
|
20
|
-
payload: { data: FileInfo[]; request: LsRequestAction };
|
|
21
|
-
[key: string]: any;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface LsFailedAction {
|
|
25
|
-
type: RemoteFsActionTypes.LS_FAILED;
|
|
26
|
-
payload: { error: ErrorObject; request: LsRequestAction };
|
|
27
|
-
[key: string]: any;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface MkdirRequestAction {
|
|
31
|
-
type: RemoteFsActionTypes.MKDIR_REQUEST;
|
|
32
|
-
payload: { path: string | string[] };
|
|
33
|
-
[key: string]: any;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface MkdirCompletedAction {
|
|
37
|
-
type: RemoteFsActionTypes.MKDIR_COMPLETED;
|
|
38
|
-
payload: { data: FileInfo; request: MkdirRequestAction };
|
|
39
|
-
[key: string]: any;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface MkdirFailedAction {
|
|
43
|
-
type: RemoteFsActionTypes.MKDIR_FAILED;
|
|
44
|
-
payload: { error: ErrorObject; request: MkdirRequestAction };
|
|
45
|
-
[key: string]: any;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface GetRequestAction {
|
|
49
|
-
type: RemoteFsActionTypes.GET_REQUEST;
|
|
50
|
-
payload: { path: string | string[] };
|
|
51
|
-
[key: string]: any;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface GetCompletedAction {
|
|
55
|
-
type: RemoteFsActionTypes.GET_COMPLETED;
|
|
56
|
-
payload: { data: FileInfo; request: GetRequestAction };
|
|
57
|
-
[key: string]: any;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export interface GetFailedAction {
|
|
61
|
-
type: RemoteFsActionTypes.GET_FAILED;
|
|
62
|
-
payload: { error: ErrorObject; request: GetRequestAction };
|
|
63
|
-
[key: string]: any;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export interface PostRequestAction {
|
|
67
|
-
type: RemoteFsActionTypes.POST_REQUEST;
|
|
68
|
-
payload: { path: string | string[]; content: any };
|
|
69
|
-
[key: string]: any;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export interface PostCompletedAction {
|
|
73
|
-
type: RemoteFsActionTypes.POST_COMPLETED;
|
|
74
|
-
payload: { data: FileInfo; request: PostRequestAction };
|
|
75
|
-
[key: string]: any;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export interface PostFailedAction {
|
|
79
|
-
type: RemoteFsActionTypes.POST_FAILED;
|
|
80
|
-
payload: { error: ErrorObject; request: PostRequestAction };
|
|
81
|
-
[key: string]: any;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export interface PutRequestAction {
|
|
85
|
-
type: RemoteFsActionTypes.PUT_REQUEST;
|
|
86
|
-
payload: { path: string | string[]; content: any };
|
|
87
|
-
[key: string]: any;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export interface PutCompletedAction {
|
|
91
|
-
type: RemoteFsActionTypes.PUT_COMPLETED;
|
|
92
|
-
payload: { data: FileInfo; request: PutRequestAction };
|
|
93
|
-
[key: string]: any;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export interface PutFailedAction {
|
|
97
|
-
type: RemoteFsActionTypes.PUT_FAILED;
|
|
98
|
-
payload: { error: ErrorObject; request: PutRequestAction };
|
|
99
|
-
[key: string]: any;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export interface DeleteRequestAction {
|
|
103
|
-
type: RemoteFsActionTypes.DELETE_REQUEST;
|
|
104
|
-
payload: { path: string | string[] };
|
|
105
|
-
[key: string]: any;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export interface DeleteCompletedAction {
|
|
109
|
-
type: RemoteFsActionTypes.DELETE_COMPLETED;
|
|
110
|
-
payload: { data: FileInfo; request: DeleteRequestAction };
|
|
111
|
-
[key: string]: any;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export interface DeleteFailedAction {
|
|
115
|
-
type: RemoteFsActionTypes.DELETE_FAILED;
|
|
116
|
-
payload: { error: ErrorObject; request: DeleteRequestAction };
|
|
117
|
-
[key: string]: any;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Union type for all remote FS actions
|
|
122
|
-
*/
|
|
123
|
-
export type RemoteFsAction =
|
|
124
|
-
| LsRequestAction
|
|
125
|
-
| LsCompletedAction
|
|
126
|
-
| LsFailedAction
|
|
127
|
-
| MkdirRequestAction
|
|
128
|
-
| MkdirCompletedAction
|
|
129
|
-
| MkdirFailedAction
|
|
130
|
-
| GetRequestAction
|
|
131
|
-
| GetCompletedAction
|
|
132
|
-
| GetFailedAction
|
|
133
|
-
| PostRequestAction
|
|
134
|
-
| PostCompletedAction
|
|
135
|
-
| PostFailedAction
|
|
136
|
-
| PutRequestAction
|
|
137
|
-
| PutCompletedAction
|
|
138
|
-
| PutFailedAction
|
|
139
|
-
| DeleteRequestAction
|
|
140
|
-
| DeleteCompletedAction
|
|
141
|
-
| DeleteFailedAction;
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Action factory class for creating remote filesystem actions
|
|
145
|
-
*/
|
|
146
|
-
export class RemoteFsActionFactory {
|
|
147
|
-
public types = RemoteFsActionTypes;
|
|
148
|
-
|
|
149
|
-
// LS (List) operations
|
|
150
|
-
ofLsRequest(path: string | string[]): LsRequestAction {
|
|
151
|
-
return {
|
|
152
|
-
type: this.types.LS_REQUEST,
|
|
153
|
-
payload: { path },
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
ofLsCompleted(data: FileInfo[], request: LsRequestAction): LsCompletedAction {
|
|
158
|
-
return {
|
|
159
|
-
type: this.types.LS_COMPLETED,
|
|
160
|
-
payload: { data, request },
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
ofLsFailed(error: ErrorObject, request: LsRequestAction): LsFailedAction {
|
|
165
|
-
return {
|
|
166
|
-
type: this.types.LS_FAILED,
|
|
167
|
-
payload: { error, request },
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// MKDIR (Make Directory) operations
|
|
172
|
-
ofMkdirRequest(path: string | string[]): MkdirRequestAction {
|
|
173
|
-
return {
|
|
174
|
-
type: this.types.MKDIR_REQUEST,
|
|
175
|
-
payload: { path },
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
ofMkdirCompleted(data: FileInfo, request: MkdirRequestAction): MkdirCompletedAction {
|
|
180
|
-
return {
|
|
181
|
-
type: this.types.MKDIR_COMPLETED,
|
|
182
|
-
payload: { data, request },
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
ofMkdirFailed(error: ErrorObject, request: MkdirRequestAction): MkdirFailedAction {
|
|
187
|
-
return {
|
|
188
|
-
type: this.types.MKDIR_FAILED,
|
|
189
|
-
payload: { error, request },
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// GET (Read File) operations
|
|
194
|
-
ofGetRequest(path: string | string[]): GetRequestAction {
|
|
195
|
-
return {
|
|
196
|
-
type: this.types.GET_REQUEST,
|
|
197
|
-
payload: { path },
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
ofGetCompleted(data: FileInfo, request: GetRequestAction): GetCompletedAction {
|
|
202
|
-
return {
|
|
203
|
-
type: this.types.GET_COMPLETED,
|
|
204
|
-
payload: { data, request },
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
ofGetFailed(error: ErrorObject, request: GetRequestAction): GetFailedAction {
|
|
209
|
-
return {
|
|
210
|
-
type: this.types.GET_FAILED,
|
|
211
|
-
payload: { error, request },
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// POST (Create File) operations
|
|
216
|
-
ofPostRequest(path: string | string[], content: any): PostRequestAction {
|
|
217
|
-
return {
|
|
218
|
-
type: this.types.POST_REQUEST,
|
|
219
|
-
payload: { path, content },
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
ofPostCompleted(data: FileInfo, request: PostRequestAction): PostCompletedAction {
|
|
224
|
-
return {
|
|
225
|
-
type: this.types.POST_COMPLETED,
|
|
226
|
-
payload: { data, request },
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
ofPostFailed(error: ErrorObject, request: PostRequestAction): PostFailedAction {
|
|
231
|
-
return {
|
|
232
|
-
type: this.types.POST_FAILED,
|
|
233
|
-
payload: { error, request },
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// PUT (Update File) operations
|
|
238
|
-
ofPutRequest(path: string | string[], content: any): PutRequestAction {
|
|
239
|
-
return {
|
|
240
|
-
type: this.types.PUT_REQUEST,
|
|
241
|
-
payload: { path, content },
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
ofPutCompleted(data: FileInfo, request: PutRequestAction): PutCompletedAction {
|
|
246
|
-
return {
|
|
247
|
-
type: this.types.PUT_COMPLETED,
|
|
248
|
-
payload: { data, request },
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
ofPutFailed(error: ErrorObject, request: PutRequestAction): PutFailedAction {
|
|
253
|
-
return {
|
|
254
|
-
type: this.types.PUT_FAILED,
|
|
255
|
-
payload: { error, request },
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// DELETE operations
|
|
260
|
-
ofDeleteRequest(path: string | string[]): DeleteRequestAction {
|
|
261
|
-
return {
|
|
262
|
-
type: this.types.DELETE_REQUEST,
|
|
263
|
-
payload: { path },
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
ofDeleteCompleted(data: FileInfo, request: DeleteRequestAction): DeleteCompletedAction {
|
|
268
|
-
return {
|
|
269
|
-
type: this.types.DELETE_COMPLETED,
|
|
270
|
-
payload: { data, request },
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
ofDeleteFailed(error: ErrorObject, request: DeleteRequestAction): DeleteFailedAction {
|
|
275
|
-
return {
|
|
276
|
-
type: this.types.DELETE_FAILED,
|
|
277
|
-
payload: { error, request },
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Type guard to check if an action is a remote FS action
|
|
283
|
-
*/
|
|
284
|
-
isRemoteFsAction(action: any): action is RemoteFsAction {
|
|
285
|
-
return (
|
|
286
|
-
action &&
|
|
287
|
-
typeof action === 'object' &&
|
|
288
|
-
'type' in action &&
|
|
289
|
-
Object.values(RemoteFsActionTypes).includes(action.type)
|
|
290
|
-
);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* Singleton instance of the action factory
|
|
296
|
-
*/
|
|
297
|
-
export const rfsActions = new RemoteFsActionFactory();
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Re-export the action types enum for convenience
|
|
301
|
-
*/
|
|
302
|
-
export { RemoteFsActionTypes };
|
package/src/index.ts
DELETED
package/src/middleware/index.ts
DELETED
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Remote FS Middleware
|
|
3
|
-
*
|
|
4
|
-
* Redux middleware for handling remote filesystem operations via HTTP client.
|
|
5
|
-
* Migrated from amk/libs/ui/core/remote-fs/src/lib/remote-fs-middleware.ts
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { AnyAction, Middleware } from '@reduxjs/toolkit';
|
|
9
|
-
import { Pathway } from '@hamak/shared-utils';
|
|
10
|
-
import { IWorkspaceClient, IPathTranslator } from '@hamak/ui-remote-fs-spi';
|
|
11
|
-
import { RemoteFsActionTypes, ErrorObject } from '@hamak/ui-remote-fs-api';
|
|
12
|
-
import {
|
|
13
|
-
rfsActions,
|
|
14
|
-
LsRequestAction,
|
|
15
|
-
MkdirRequestAction,
|
|
16
|
-
GetRequestAction,
|
|
17
|
-
PostRequestAction,
|
|
18
|
-
PutRequestAction,
|
|
19
|
-
DeleteRequestAction,
|
|
20
|
-
} from '../actions/remote-fs-actions';
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Configuration for remote FS middleware
|
|
24
|
-
*/
|
|
25
|
-
export interface RemoteFsMiddlewareConfig {
|
|
26
|
-
/**
|
|
27
|
-
* Workspace client for remote operations
|
|
28
|
-
*/
|
|
29
|
-
client: IWorkspaceClient;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Path translator for mount point translation
|
|
33
|
-
*/
|
|
34
|
-
pathTranslator: IPathTranslator;
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Optional error handler
|
|
38
|
-
*/
|
|
39
|
-
onError?: (error: ErrorObject, action: AnyAction) => void;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Optional success handler
|
|
43
|
-
*/
|
|
44
|
-
onSuccess?: (result: any, action: AnyAction) => void;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Create remote filesystem middleware
|
|
49
|
-
*
|
|
50
|
-
* This middleware intercepts remote FS actions and executes them via the provided
|
|
51
|
-
* workspace client. It handles path translation from local mount point to remote paths.
|
|
52
|
-
*
|
|
53
|
-
* @param config Middleware configuration
|
|
54
|
-
* @returns Redux middleware
|
|
55
|
-
*
|
|
56
|
-
* @example
|
|
57
|
-
* ```typescript
|
|
58
|
-
* const middleware = createRemoteFsMiddleware({
|
|
59
|
-
* client: new HttpWorkspaceClient({ workspaceId: '0' }),
|
|
60
|
-
* pathTranslator: new PathTranslator(Pathway.ofRoot().resolve('remote'))
|
|
61
|
-
* });
|
|
62
|
-
* ```
|
|
63
|
-
*/
|
|
64
|
-
export function createRemoteFsMiddleware<S = any>(
|
|
65
|
-
config: RemoteFsMiddlewareConfig
|
|
66
|
-
): Middleware<{}, S> {
|
|
67
|
-
const { client, pathTranslator, onError, onSuccess } = config;
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Translate local path to remote path
|
|
71
|
-
*/
|
|
72
|
-
const toRemotePath = (path: string | string[]): Pathway | undefined => {
|
|
73
|
-
const pathway = Array.isArray(path) ? Pathway.of(path) : Pathway.of([path]);
|
|
74
|
-
return pathTranslator.toRemotePath(pathway);
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Handle errors and convert to ErrorObject
|
|
79
|
-
*/
|
|
80
|
-
const handleError = (error: any): ErrorObject => {
|
|
81
|
-
if (error && typeof error === 'object' && 'code' in error && 'message' in error) {
|
|
82
|
-
return error as ErrorObject;
|
|
83
|
-
}
|
|
84
|
-
return {
|
|
85
|
-
code: error?.code || 'UNKNOWN',
|
|
86
|
-
message: error?.message || 'Unknown error',
|
|
87
|
-
};
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const remoteFsMiddleware: Middleware<{}, S> = (store) => (next) => (action) => {
|
|
91
|
-
const result = next(action);
|
|
92
|
-
const dispatch = store.dispatch;
|
|
93
|
-
const anyAction = action as AnyAction;
|
|
94
|
-
|
|
95
|
-
switch (anyAction.type) {
|
|
96
|
-
case RemoteFsActionTypes.LS_REQUEST: {
|
|
97
|
-
const requestAction = anyAction as LsRequestAction;
|
|
98
|
-
const remotePath = toRemotePath(requestAction.payload.path);
|
|
99
|
-
if (remotePath === undefined) {
|
|
100
|
-
break;
|
|
101
|
-
}
|
|
102
|
-
void (async () => {
|
|
103
|
-
try {
|
|
104
|
-
const data = await client.listFiles(remotePath.getSegments());
|
|
105
|
-
const completedAction = rfsActions.ofLsCompleted(data, requestAction);
|
|
106
|
-
dispatch(completedAction);
|
|
107
|
-
onSuccess?.(data, completedAction);
|
|
108
|
-
} catch (error) {
|
|
109
|
-
const errorObject = handleError(error);
|
|
110
|
-
const failedAction = rfsActions.ofLsFailed(errorObject, requestAction);
|
|
111
|
-
dispatch(failedAction);
|
|
112
|
-
onError?.(errorObject, failedAction);
|
|
113
|
-
}
|
|
114
|
-
})();
|
|
115
|
-
break;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
case RemoteFsActionTypes.MKDIR_REQUEST: {
|
|
119
|
-
const requestAction = anyAction as MkdirRequestAction;
|
|
120
|
-
const remotePath = toRemotePath(requestAction.payload.path);
|
|
121
|
-
if (remotePath === undefined) {
|
|
122
|
-
break;
|
|
123
|
-
}
|
|
124
|
-
void (async () => {
|
|
125
|
-
try {
|
|
126
|
-
const data = await client.createDirectory(remotePath.getSegments());
|
|
127
|
-
const completedAction = rfsActions.ofMkdirCompleted(data, requestAction);
|
|
128
|
-
dispatch(completedAction);
|
|
129
|
-
onSuccess?.(data, completedAction);
|
|
130
|
-
} catch (error) {
|
|
131
|
-
const errorObject = handleError(error);
|
|
132
|
-
const failedAction = rfsActions.ofMkdirFailed(errorObject, requestAction);
|
|
133
|
-
dispatch(failedAction);
|
|
134
|
-
onError?.(errorObject, failedAction);
|
|
135
|
-
}
|
|
136
|
-
})();
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
case RemoteFsActionTypes.GET_REQUEST: {
|
|
141
|
-
const requestAction = anyAction as GetRequestAction;
|
|
142
|
-
const remotePath = toRemotePath(requestAction.payload.path);
|
|
143
|
-
if (remotePath === undefined) {
|
|
144
|
-
break;
|
|
145
|
-
}
|
|
146
|
-
void (async () => {
|
|
147
|
-
try {
|
|
148
|
-
const data = await client.readFile(remotePath.getSegments());
|
|
149
|
-
const completedAction = rfsActions.ofGetCompleted(data, requestAction);
|
|
150
|
-
dispatch(completedAction);
|
|
151
|
-
onSuccess?.(data, completedAction);
|
|
152
|
-
} catch (error) {
|
|
153
|
-
const errorObject = handleError(error);
|
|
154
|
-
const failedAction = rfsActions.ofGetFailed(errorObject, requestAction);
|
|
155
|
-
dispatch(failedAction);
|
|
156
|
-
onError?.(errorObject, failedAction);
|
|
157
|
-
}
|
|
158
|
-
})();
|
|
159
|
-
break;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
case RemoteFsActionTypes.POST_REQUEST: {
|
|
163
|
-
const requestAction = anyAction as PostRequestAction;
|
|
164
|
-
const remotePath = toRemotePath(requestAction.payload.path);
|
|
165
|
-
if (remotePath === undefined) {
|
|
166
|
-
break;
|
|
167
|
-
}
|
|
168
|
-
void (async () => {
|
|
169
|
-
try {
|
|
170
|
-
const data = await client.putFile(
|
|
171
|
-
remotePath.getSegments(),
|
|
172
|
-
requestAction.payload.content
|
|
173
|
-
);
|
|
174
|
-
const completedAction = rfsActions.ofPostCompleted(data, requestAction);
|
|
175
|
-
dispatch(completedAction);
|
|
176
|
-
onSuccess?.(data, completedAction);
|
|
177
|
-
} catch (error) {
|
|
178
|
-
const errorObject = handleError(error);
|
|
179
|
-
const failedAction = rfsActions.ofPostFailed(errorObject, requestAction);
|
|
180
|
-
dispatch(failedAction);
|
|
181
|
-
onError?.(errorObject, failedAction);
|
|
182
|
-
}
|
|
183
|
-
})();
|
|
184
|
-
break;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
case RemoteFsActionTypes.PUT_REQUEST: {
|
|
188
|
-
const requestAction = anyAction as PutRequestAction;
|
|
189
|
-
const remotePath = toRemotePath(requestAction.payload.path);
|
|
190
|
-
if (remotePath === undefined) {
|
|
191
|
-
break;
|
|
192
|
-
}
|
|
193
|
-
void (async () => {
|
|
194
|
-
try {
|
|
195
|
-
const data = await client.putFile(
|
|
196
|
-
remotePath.getSegments(),
|
|
197
|
-
requestAction.payload.content
|
|
198
|
-
);
|
|
199
|
-
const completedAction = rfsActions.ofPutCompleted(data, requestAction);
|
|
200
|
-
dispatch(completedAction);
|
|
201
|
-
onSuccess?.(data, completedAction);
|
|
202
|
-
} catch (error) {
|
|
203
|
-
const errorObject = handleError(error);
|
|
204
|
-
const failedAction = rfsActions.ofPutFailed(errorObject, requestAction);
|
|
205
|
-
dispatch(failedAction);
|
|
206
|
-
onError?.(errorObject, failedAction);
|
|
207
|
-
}
|
|
208
|
-
})();
|
|
209
|
-
break;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
case RemoteFsActionTypes.DELETE_REQUEST: {
|
|
213
|
-
const requestAction = anyAction as DeleteRequestAction;
|
|
214
|
-
const remotePath = toRemotePath(requestAction.payload.path);
|
|
215
|
-
if (remotePath === undefined) {
|
|
216
|
-
break;
|
|
217
|
-
}
|
|
218
|
-
void (async () => {
|
|
219
|
-
try {
|
|
220
|
-
const data = await client.deleteFile(remotePath.getSegments());
|
|
221
|
-
const completedAction = rfsActions.ofDeleteCompleted(data, requestAction);
|
|
222
|
-
dispatch(completedAction);
|
|
223
|
-
onSuccess?.(data, completedAction);
|
|
224
|
-
} catch (error) {
|
|
225
|
-
const errorObject = handleError(error);
|
|
226
|
-
const failedAction = rfsActions.ofDeleteFailed(errorObject, requestAction);
|
|
227
|
-
dispatch(failedAction);
|
|
228
|
-
onError?.(errorObject, failedAction);
|
|
229
|
-
}
|
|
230
|
-
})();
|
|
231
|
-
break;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
default:
|
|
235
|
-
break;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
return result;
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
return remoteFsMiddleware;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
export default createRemoteFsMiddleware;
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Store Sync Middleware
|
|
3
|
-
*
|
|
4
|
-
* Redux middleware for synchronizing remote filesystem operations to Redux Store FS.
|
|
5
|
-
* This middleware listens to remote FS completion actions and updates the Store FS accordingly.
|
|
6
|
-
* Migrated from amk/libs/ui/core/remote-fs/src/lib/store-fs-adapter-middleware.ts
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { AnyAction, Middleware } from '@reduxjs/toolkit';
|
|
10
|
-
import { Pathway } from '@hamak/shared-utils';
|
|
11
|
-
import { RemoteFsActionTypes } from '@hamak/ui-remote-fs-api';
|
|
12
|
-
import { StoreSyncMiddlewareConfig } from '@hamak/ui-remote-fs-spi';
|
|
13
|
-
import {
|
|
14
|
-
rfsActions,
|
|
15
|
-
LsCompletedAction,
|
|
16
|
-
MkdirCompletedAction,
|
|
17
|
-
GetCompletedAction,
|
|
18
|
-
PostCompletedAction,
|
|
19
|
-
PutCompletedAction,
|
|
20
|
-
DeleteCompletedAction,
|
|
21
|
-
} from '../actions/remote-fs-actions';
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Create store sync middleware
|
|
25
|
-
*
|
|
26
|
-
* This middleware intercepts remote FS completion actions and synchronizes them
|
|
27
|
-
* to the Redux Store FileSystem. It handles:
|
|
28
|
-
* - LS_COMPLETED: Creates directories and file entries in Store FS
|
|
29
|
-
* - MKDIR_COMPLETED: Creates directory in Store FS
|
|
30
|
-
* - GET_COMPLETED: Creates/updates file content in Store FS
|
|
31
|
-
* - POST/PUT_COMPLETED: Reloads file content from remote
|
|
32
|
-
* - DELETE_COMPLETED: Removes node from Store FS
|
|
33
|
-
*
|
|
34
|
-
* @param config Middleware configuration
|
|
35
|
-
* @returns Redux middleware
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
* ```typescript
|
|
39
|
-
* const middleware = createStoreSyncMiddleware({
|
|
40
|
-
* fileSystemAdapter: storeFs,
|
|
41
|
-
* pathTranslator: new PathTranslator(Pathway.ofRoot().resolve('remote')),
|
|
42
|
-
* autoReload: true
|
|
43
|
-
* });
|
|
44
|
-
* ```
|
|
45
|
-
*/
|
|
46
|
-
export function createStoreSyncMiddleware<S = any>(
|
|
47
|
-
config: StoreSyncMiddlewareConfig
|
|
48
|
-
): Middleware<{}, S> {
|
|
49
|
-
const { fileSystemAdapter, pathTranslator, autoReload = true, transformContent } = config;
|
|
50
|
-
const storeFsActions = fileSystemAdapter.getActions();
|
|
51
|
-
|
|
52
|
-
const storeSyncMiddleware: Middleware<{}, S> = (store) => (next) => (action) => {
|
|
53
|
-
const result = next(action);
|
|
54
|
-
const anyAction = action as AnyAction;
|
|
55
|
-
|
|
56
|
-
// Only process remote FS actions
|
|
57
|
-
if (!rfsActions.isRemoteFsAction(anyAction)) {
|
|
58
|
-
return result;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const dispatch = store.dispatch;
|
|
62
|
-
|
|
63
|
-
switch (anyAction.type) {
|
|
64
|
-
case RemoteFsActionTypes.LS_COMPLETED: {
|
|
65
|
-
const completedAction = anyAction as LsCompletedAction;
|
|
66
|
-
const { request, data } = completedAction.payload;
|
|
67
|
-
|
|
68
|
-
// Iterate through file list and create entries in Store FS
|
|
69
|
-
data?.forEach((fileInfo) => {
|
|
70
|
-
// Build local path: request path + file name
|
|
71
|
-
const localPath = Pathway.of(request.payload.path)
|
|
72
|
-
.resolve(fileInfo.name)
|
|
73
|
-
.getSegments();
|
|
74
|
-
|
|
75
|
-
if (fileInfo.isDirectory) {
|
|
76
|
-
// Create directory with parents flag
|
|
77
|
-
dispatch(storeFsActions.mkdir(localPath, true));
|
|
78
|
-
} else {
|
|
79
|
-
// Create file entry without content (contentIsPresent: false)
|
|
80
|
-
dispatch(
|
|
81
|
-
storeFsActions.setFile(localPath, undefined, 'xs:any', {
|
|
82
|
-
override: true,
|
|
83
|
-
contentIsPresent: false,
|
|
84
|
-
})
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
break;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
case RemoteFsActionTypes.MKDIR_COMPLETED: {
|
|
92
|
-
const completedAction = anyAction as MkdirCompletedAction;
|
|
93
|
-
const { request } = completedAction.payload;
|
|
94
|
-
const localPath = Pathway.of(request.payload.path).getSegments();
|
|
95
|
-
|
|
96
|
-
// Create directory in Store FS
|
|
97
|
-
dispatch(storeFsActions.mkdir(localPath, true));
|
|
98
|
-
break;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
case RemoteFsActionTypes.GET_COMPLETED: {
|
|
102
|
-
const completedAction = anyAction as GetCompletedAction;
|
|
103
|
-
const { request, data } = completedAction.payload;
|
|
104
|
-
const localPath = Pathway.of(request.payload.path).getSegments();
|
|
105
|
-
|
|
106
|
-
// Parse content
|
|
107
|
-
let content =
|
|
108
|
-
data.content === undefined || data.content === null
|
|
109
|
-
? undefined
|
|
110
|
-
: data.content === ''
|
|
111
|
-
? undefined
|
|
112
|
-
: JSON.parse(data.content);
|
|
113
|
-
|
|
114
|
-
// Apply content transform if provided
|
|
115
|
-
if (content !== undefined && transformContent) {
|
|
116
|
-
content = transformContent(content);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Check if file exists in Store FS
|
|
120
|
-
const fileSystemNode = fileSystemAdapter.createSelector(
|
|
121
|
-
(state: S) => (state as any).fileSystem,
|
|
122
|
-
localPath
|
|
123
|
-
)(store.getState());
|
|
124
|
-
|
|
125
|
-
if (fileSystemNode === undefined) {
|
|
126
|
-
// File doesn't exist, create it with content
|
|
127
|
-
dispatch(
|
|
128
|
-
storeFsActions.setFile(localPath, content, 'xs:any', {
|
|
129
|
-
override: true,
|
|
130
|
-
contentIsPresent: true,
|
|
131
|
-
})
|
|
132
|
-
);
|
|
133
|
-
} else {
|
|
134
|
-
// File exists, update content only if it's a file
|
|
135
|
-
if (fileSystemNode?.type === 'file') {
|
|
136
|
-
dispatch(storeFsActions.setFileContent(localPath, content, true));
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
break;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
case RemoteFsActionTypes.POST_COMPLETED:
|
|
143
|
-
case RemoteFsActionTypes.PUT_COMPLETED: {
|
|
144
|
-
const completedAction = anyAction as PostCompletedAction | PutCompletedAction;
|
|
145
|
-
const { request } = completedAction.payload;
|
|
146
|
-
const localPath = Pathway.of(request.payload.path).getSegments();
|
|
147
|
-
|
|
148
|
-
// Reload file content from remote if autoReload is enabled
|
|
149
|
-
if (autoReload) {
|
|
150
|
-
dispatch(rfsActions.ofGetRequest(localPath));
|
|
151
|
-
}
|
|
152
|
-
break;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
case RemoteFsActionTypes.DELETE_COMPLETED: {
|
|
156
|
-
const completedAction = anyAction as DeleteCompletedAction;
|
|
157
|
-
const { request } = completedAction.payload;
|
|
158
|
-
const localPath = Pathway.of(request.payload.path).getSegments();
|
|
159
|
-
|
|
160
|
-
// Remove node from Store FS
|
|
161
|
-
dispatch(storeFsActions.removeNode(localPath));
|
|
162
|
-
break;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
default:
|
|
166
|
-
break;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
return result;
|
|
170
|
-
};
|
|
171
|
-
|
|
172
|
-
return storeSyncMiddleware;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export default createStoreSyncMiddleware;
|
package/src/plugin/index.ts
DELETED
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Remote FS Plugin Factory
|
|
3
|
-
*
|
|
4
|
-
* Creates a microkernel plugin for remote filesystem operations.
|
|
5
|
-
* Integrates HTTP-based remote filesystem with Redux Store FS.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { PluginModule } from '@hamak/microkernel-spi';
|
|
9
|
-
import type { Pathway } from '@hamak/shared-utils';
|
|
10
|
-
import {
|
|
11
|
-
WORKSPACE_CLIENT_TOKEN,
|
|
12
|
-
PATH_TRANSLATOR_TOKEN,
|
|
13
|
-
REMOTE_FS_SERVICE_TOKEN,
|
|
14
|
-
} from '@hamak/ui-remote-fs-api';
|
|
15
|
-
import { IWorkspaceClient, IPathTranslator, PathTranslator } from '@hamak/ui-remote-fs-spi';
|
|
16
|
-
import { HttpWorkspaceClient, HttpWorkspaceClientConfig } from '../providers/http-workspace-client';
|
|
17
|
-
import { createRemoteFsMiddleware } from '../middleware/remote-fs-middleware';
|
|
18
|
-
import { createStoreSyncMiddleware } from '../middleware/store-sync-middleware';
|
|
19
|
-
import {
|
|
20
|
-
STORE_MANAGER_TOKEN,
|
|
21
|
-
STORE_EXTENSIONS_TOKEN,
|
|
22
|
-
type StoreMiddlewareExtension,
|
|
23
|
-
} from '@hamak/ui-store-api';
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Configuration for Remote FS plugin
|
|
27
|
-
*/
|
|
28
|
-
export interface RemoteFsPluginConfig {
|
|
29
|
-
/**
|
|
30
|
-
* Workspace identifier
|
|
31
|
-
*/
|
|
32
|
-
workspaceId: string;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Mount point for remote filesystem
|
|
36
|
-
* This is the local path where remote files will be mounted
|
|
37
|
-
* @example Pathway.ofRoot().resolve('remote')
|
|
38
|
-
*/
|
|
39
|
-
mountPoint: Pathway;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Base URL for the workspace API
|
|
43
|
-
* If not provided, defaults to current origin + '/api'
|
|
44
|
-
* @example 'http://localhost:3000/api'
|
|
45
|
-
*/
|
|
46
|
-
baseUrl?: string;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Request timeout in milliseconds
|
|
50
|
-
* Default: 30000 (30 seconds)
|
|
51
|
-
*/
|
|
52
|
-
timeout?: number;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Whether to automatically reload file content after PUT/POST
|
|
56
|
-
* Default: true
|
|
57
|
-
*/
|
|
58
|
-
autoReload?: boolean;
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Optional transform function for file content before storing
|
|
62
|
-
*/
|
|
63
|
-
transformContent?: (content: any) => any;
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Enable development logging
|
|
67
|
-
* Default: false
|
|
68
|
-
*/
|
|
69
|
-
debug?: boolean;
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Custom workspace client (for testing or advanced scenarios)
|
|
73
|
-
*/
|
|
74
|
-
customClient?: IWorkspaceClient;
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Custom path translator (for advanced scenarios)
|
|
78
|
-
*/
|
|
79
|
-
customPathTranslator?: IPathTranslator;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Create Remote FS plugin
|
|
84
|
-
*
|
|
85
|
-
* This plugin provides remote filesystem capabilities through HTTP API.
|
|
86
|
-
* It registers two middleware:
|
|
87
|
-
* - Remote FS Middleware: Handles remote operations via HTTP client
|
|
88
|
-
* - Store Sync Middleware: Synchronizes remote data to Redux Store FS
|
|
89
|
-
*
|
|
90
|
-
* @param config Plugin configuration
|
|
91
|
-
* @returns PluginModule for microkernel integration
|
|
92
|
-
*
|
|
93
|
-
* @example
|
|
94
|
-
* ```typescript
|
|
95
|
-
* import { Pathway } from '@hamak/shared-utils';
|
|
96
|
-
* import { createRemoteFsPlugin } from '@hamak/ui-remote-fs-impl';
|
|
97
|
-
*
|
|
98
|
-
* const plugin = createRemoteFsPlugin({
|
|
99
|
-
* workspaceId: '0',
|
|
100
|
-
* mountPoint: Pathway.ofRoot().resolve('remote'),
|
|
101
|
-
* baseUrl: 'http://localhost:3000/api',
|
|
102
|
-
* autoReload: true
|
|
103
|
-
* });
|
|
104
|
-
* ```
|
|
105
|
-
*/
|
|
106
|
-
export function createRemoteFsPlugin(config: RemoteFsPluginConfig): PluginModule {
|
|
107
|
-
let workspaceClient: IWorkspaceClient;
|
|
108
|
-
let pathTranslator: IPathTranslator;
|
|
109
|
-
let storeManager: any;
|
|
110
|
-
|
|
111
|
-
const log = (message: string, ...args: any[]) => {
|
|
112
|
-
if (config.debug) {
|
|
113
|
-
console.log(`[ui-remote-fs] ${message}`, ...args);
|
|
114
|
-
}
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
return {
|
|
118
|
-
async initialize(ctx) {
|
|
119
|
-
log('Initializing plugin...');
|
|
120
|
-
|
|
121
|
-
// Create or use custom workspace client
|
|
122
|
-
if (config.customClient) {
|
|
123
|
-
workspaceClient = config.customClient;
|
|
124
|
-
log('Using custom workspace client');
|
|
125
|
-
} else {
|
|
126
|
-
const clientConfig: HttpWorkspaceClientConfig = {
|
|
127
|
-
workspaceId: config.workspaceId,
|
|
128
|
-
baseUrl: config.baseUrl,
|
|
129
|
-
timeout: config.timeout,
|
|
130
|
-
};
|
|
131
|
-
workspaceClient = new HttpWorkspaceClient(clientConfig);
|
|
132
|
-
log('Created HTTP workspace client', { baseUrl: config.baseUrl, workspaceId: config.workspaceId });
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Create or use custom path translator
|
|
136
|
-
if (config.customPathTranslator) {
|
|
137
|
-
pathTranslator = config.customPathTranslator;
|
|
138
|
-
log('Using custom path translator');
|
|
139
|
-
} else {
|
|
140
|
-
pathTranslator = new PathTranslator(config.mountPoint);
|
|
141
|
-
log('Created path translator', { mountPoint: config.mountPoint.getSegments() });
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Register services via DI
|
|
145
|
-
ctx.provide({ provide: WORKSPACE_CLIENT_TOKEN, useValue: workspaceClient });
|
|
146
|
-
ctx.provide({ provide: PATH_TRANSLATOR_TOKEN, useValue: pathTranslator });
|
|
147
|
-
|
|
148
|
-
// Note: IRemoteFsService implementation will be added in future phases
|
|
149
|
-
// ctx.provide({ provide: REMOTE_FS_SERVICE_TOKEN, useValue: remoteFsService });
|
|
150
|
-
|
|
151
|
-
log('Plugin initialized');
|
|
152
|
-
},
|
|
153
|
-
|
|
154
|
-
async activate(ctx) {
|
|
155
|
-
log('Activating plugin...');
|
|
156
|
-
|
|
157
|
-
// Resolve store manager from DI
|
|
158
|
-
try {
|
|
159
|
-
storeManager = ctx.resolve(STORE_MANAGER_TOKEN);
|
|
160
|
-
} catch (error) {
|
|
161
|
-
throw new Error(
|
|
162
|
-
'[ui-remote-fs] Store manager not found. Ensure @hamak/ui-store plugin is loaded before ui-remote-fs.'
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Get file system adapter from store
|
|
167
|
-
const fileSystemAdapter = storeManager.getFileSystemAdapter();
|
|
168
|
-
if (!fileSystemAdapter) {
|
|
169
|
-
throw new Error('[ui-remote-fs] File system adapter not found in store manager.');
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Get store extensions collector
|
|
173
|
-
const extensionsCollector = ctx.resolve(STORE_EXTENSIONS_TOKEN) as any;
|
|
174
|
-
|
|
175
|
-
// Create and register remote FS middleware
|
|
176
|
-
const remoteFsMiddleware = createRemoteFsMiddleware({
|
|
177
|
-
client: workspaceClient,
|
|
178
|
-
pathTranslator,
|
|
179
|
-
onError: (error, action) => {
|
|
180
|
-
log('Remote FS error:', error, action);
|
|
181
|
-
ctx.hooks.emit('ui-remote-fs:error', { error, action });
|
|
182
|
-
},
|
|
183
|
-
onSuccess: (result, action) => {
|
|
184
|
-
log('Remote FS success:', action.type);
|
|
185
|
-
ctx.hooks.emit('ui-remote-fs:success', { result, action });
|
|
186
|
-
},
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
// Create and register store sync middleware
|
|
190
|
-
const storeSyncMiddleware = createStoreSyncMiddleware({
|
|
191
|
-
fileSystemAdapter,
|
|
192
|
-
pathTranslator,
|
|
193
|
-
autoReload: config.autoReload,
|
|
194
|
-
transformContent: config.transformContent,
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
// Register middleware with store extensions
|
|
198
|
-
const middlewareExtensions: StoreMiddlewareExtension[] = [
|
|
199
|
-
{
|
|
200
|
-
id: 'remote-fs',
|
|
201
|
-
middleware: remoteFsMiddleware,
|
|
202
|
-
priority: 100,
|
|
203
|
-
plugin: 'ui-remote-fs',
|
|
204
|
-
description: 'Handles remote filesystem operations via HTTP',
|
|
205
|
-
},
|
|
206
|
-
{
|
|
207
|
-
id: 'store-sync',
|
|
208
|
-
middleware: storeSyncMiddleware,
|
|
209
|
-
priority: 50,
|
|
210
|
-
plugin: 'ui-remote-fs',
|
|
211
|
-
description: 'Synchronizes remote FS to Redux Store FS',
|
|
212
|
-
},
|
|
213
|
-
];
|
|
214
|
-
|
|
215
|
-
extensionsCollector.register('ui-remote-fs:middleware', {
|
|
216
|
-
middleware: middlewareExtensions,
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
log('Middleware registered');
|
|
220
|
-
|
|
221
|
-
// Emit ready event
|
|
222
|
-
ctx.hooks.emit('ui-remote-fs:ready', {
|
|
223
|
-
workspaceClient,
|
|
224
|
-
pathTranslator,
|
|
225
|
-
mountPoint: config.mountPoint,
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
log('Plugin activated');
|
|
229
|
-
},
|
|
230
|
-
|
|
231
|
-
async deactivate() {
|
|
232
|
-
log('Plugin deactivated');
|
|
233
|
-
// Cleanup if needed
|
|
234
|
-
},
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
export default createRemoteFsPlugin;
|
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* HTTP Workspace Client
|
|
3
|
-
*
|
|
4
|
-
* HTTP-based implementation of IWorkspaceClient using Axios.
|
|
5
|
-
* Migrated from amk/libs/server/ws/ws-client/src/lib/server-ws-ws-client.ts
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import axios, { AxiosInstance, AxiosError } from 'axios';
|
|
9
|
-
import { IWorkspaceClient } from '@hamak/ui-remote-fs-spi';
|
|
10
|
-
import { FileInfo } from '@hamak/shared-utils';
|
|
11
|
-
import { ErrorObject } from '@hamak/ui-remote-fs-api';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Configuration for HTTP workspace client
|
|
15
|
-
*/
|
|
16
|
-
export interface HttpWorkspaceClientConfig {
|
|
17
|
-
/**
|
|
18
|
-
* Base URL for the API (e.g., 'http://localhost:3000/api')
|
|
19
|
-
* If not provided, defaults to current origin + '/api'
|
|
20
|
-
*/
|
|
21
|
-
baseUrl?: string;
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Workspace identifier
|
|
25
|
-
*/
|
|
26
|
-
workspaceId: string;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Request timeout in milliseconds
|
|
30
|
-
* Default: 30000 (30 seconds)
|
|
31
|
-
*/
|
|
32
|
-
timeout?: number;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Custom axios instance (for advanced configuration)
|
|
36
|
-
*/
|
|
37
|
-
axiosInstance?: AxiosInstance;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* HTTP-based implementation of IWorkspaceClient
|
|
42
|
-
*
|
|
43
|
-
* This implementation uses Axios to communicate with a REST API backend.
|
|
44
|
-
* Supports configurable base URL, timeout, and custom axios instances.
|
|
45
|
-
*
|
|
46
|
-
* @example
|
|
47
|
-
* ```typescript
|
|
48
|
-
* const client = new HttpWorkspaceClient({
|
|
49
|
-
* workspaceId: '0',
|
|
50
|
-
* baseUrl: 'http://localhost:3000/api'
|
|
51
|
-
* });
|
|
52
|
-
*
|
|
53
|
-
* const files = await client.listFiles(['folder', 'subfolder']);
|
|
54
|
-
* ```
|
|
55
|
-
*/
|
|
56
|
-
export class HttpWorkspaceClient implements IWorkspaceClient {
|
|
57
|
-
private readonly workspaceId: string;
|
|
58
|
-
private readonly baseUrl: string;
|
|
59
|
-
private readonly axios: AxiosInstance;
|
|
60
|
-
|
|
61
|
-
constructor(config: HttpWorkspaceClientConfig) {
|
|
62
|
-
this.workspaceId = config.workspaceId;
|
|
63
|
-
|
|
64
|
-
// Default base URL to current origin if not provided
|
|
65
|
-
this.baseUrl = config.baseUrl ||
|
|
66
|
-
(typeof globalThis !== 'undefined' && 'location' in globalThis
|
|
67
|
-
? `${(globalThis as any).location.protocol}//${(globalThis as any).location.host}/api`
|
|
68
|
-
: '/api');
|
|
69
|
-
|
|
70
|
-
// Use provided axios instance or create new one
|
|
71
|
-
this.axios = config.axiosInstance || axios.create({
|
|
72
|
-
baseURL: this.baseUrl,
|
|
73
|
-
timeout: config.timeout || 30000,
|
|
74
|
-
headers: {
|
|
75
|
-
'Content-Type': 'application/json',
|
|
76
|
-
},
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Format path segments to URL path string
|
|
82
|
-
*/
|
|
83
|
-
private formatPath(path: string[]): string {
|
|
84
|
-
return path.join('/');
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Handle axios errors and convert to ErrorObject
|
|
89
|
-
*/
|
|
90
|
-
private handleError(error: unknown): never {
|
|
91
|
-
if (axios.isAxiosError(error)) {
|
|
92
|
-
const axiosError = error as AxiosError<{ error?: ErrorObject }>;
|
|
93
|
-
|
|
94
|
-
// Try to extract error from response
|
|
95
|
-
if (axiosError.response?.data?.error) {
|
|
96
|
-
throw axiosError.response.data.error;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Create error from axios error
|
|
100
|
-
const errorObj: ErrorObject = {
|
|
101
|
-
code: axiosError.code || 'UNKNOWN',
|
|
102
|
-
message: axiosError.message || 'Unknown error occurred',
|
|
103
|
-
params: {
|
|
104
|
-
status: axiosError.response?.status,
|
|
105
|
-
statusText: axiosError.response?.statusText,
|
|
106
|
-
},
|
|
107
|
-
};
|
|
108
|
-
throw errorObj;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Handle non-axios errors
|
|
112
|
-
const errorObj: ErrorObject = {
|
|
113
|
-
code: 'UNKNOWN',
|
|
114
|
-
message: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
115
|
-
};
|
|
116
|
-
throw errorObj;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Serialize content to string for transmission
|
|
121
|
-
*/
|
|
122
|
-
private serializeContent(content: any): string {
|
|
123
|
-
if (typeof content === 'string') {
|
|
124
|
-
return content;
|
|
125
|
-
}
|
|
126
|
-
return JSON.stringify(content);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* List files and directories at the specified path
|
|
131
|
-
*/
|
|
132
|
-
async listFiles(path: string[]): Promise<FileInfo[]> {
|
|
133
|
-
try {
|
|
134
|
-
const formattedPath = this.formatPath(path);
|
|
135
|
-
const response = await this.axios.get<FileInfo[]>(
|
|
136
|
-
`/workspaces/${this.workspaceId}/files/${formattedPath}`
|
|
137
|
-
);
|
|
138
|
-
return response.data;
|
|
139
|
-
} catch (error) {
|
|
140
|
-
this.handleError(error);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Create a directory at the specified path
|
|
146
|
-
*/
|
|
147
|
-
async createDirectory(path: string[]): Promise<FileInfo> {
|
|
148
|
-
try {
|
|
149
|
-
const formattedPath = this.formatPath(path);
|
|
150
|
-
const response = await this.axios.post<FileInfo>(
|
|
151
|
-
`/workspaces/${this.workspaceId}/mkdir/${formattedPath}`
|
|
152
|
-
);
|
|
153
|
-
return response.data;
|
|
154
|
-
} catch (error) {
|
|
155
|
-
this.handleError(error);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Read a file's metadata and content
|
|
161
|
-
*/
|
|
162
|
-
async readFile(path: string[]): Promise<FileInfo> {
|
|
163
|
-
try {
|
|
164
|
-
const formattedPath = this.formatPath(path);
|
|
165
|
-
const response = await this.axios.get<FileInfo>(
|
|
166
|
-
`/workspaces/${this.workspaceId}/read/${formattedPath}`
|
|
167
|
-
);
|
|
168
|
-
return response.data;
|
|
169
|
-
} catch (error) {
|
|
170
|
-
this.handleError(error);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Write or update a file's content (uses PUT method)
|
|
176
|
-
*/
|
|
177
|
-
async putFile(path: string[], content: any): Promise<FileInfo> {
|
|
178
|
-
try {
|
|
179
|
-
const formattedPath = this.formatPath(path);
|
|
180
|
-
const serializedContent = this.serializeContent(content);
|
|
181
|
-
const response = await this.axios.put<FileInfo>(
|
|
182
|
-
`/workspaces/${this.workspaceId}/put/${formattedPath}`,
|
|
183
|
-
{ content: serializedContent }
|
|
184
|
-
);
|
|
185
|
-
return response.data;
|
|
186
|
-
} catch (error) {
|
|
187
|
-
this.handleError(error);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Delete a file or directory
|
|
193
|
-
*/
|
|
194
|
-
async deleteFile(path: string[]): Promise<FileInfo> {
|
|
195
|
-
try {
|
|
196
|
-
const formattedPath = this.formatPath(path);
|
|
197
|
-
const response = await this.axios.delete<FileInfo>(
|
|
198
|
-
`/workspaces/${this.workspaceId}/delete/${formattedPath}`
|
|
199
|
-
);
|
|
200
|
-
return response.data;
|
|
201
|
-
} catch (error) {
|
|
202
|
-
this.handleError(error);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Get the workspace ID
|
|
208
|
-
*/
|
|
209
|
-
getWorkspaceId(): string {
|
|
210
|
-
return this.workspaceId;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Get the base URL
|
|
215
|
-
*/
|
|
216
|
-
getBaseUrl(): string {
|
|
217
|
-
return this.baseUrl;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Factory function to create HTTP workspace client with default configuration
|
|
223
|
-
*/
|
|
224
|
-
export function createHttpWorkspaceClient(
|
|
225
|
-
workspaceId: string,
|
|
226
|
-
baseUrl?: string
|
|
227
|
-
): HttpWorkspaceClient {
|
|
228
|
-
return new HttpWorkspaceClient({
|
|
229
|
-
workspaceId,
|
|
230
|
-
baseUrl,
|
|
231
|
-
});
|
|
232
|
-
}
|
package/src/providers/index.ts
DELETED
package/src/services/index.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Remote FS Service
|
|
3
|
-
*
|
|
4
|
-
* Service implementation for remote filesystem operations.
|
|
5
|
-
* Will be implemented in future phases.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
// Placeholder - will be implemented in future phases
|
|
9
|
-
export class RemoteFsService {
|
|
10
|
-
constructor() {
|
|
11
|
-
throw new Error('Not yet implemented');
|
|
12
|
-
}
|
|
13
|
-
}
|
package/tsconfig.es2015.json
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2015",
|
|
4
|
-
"module": "CommonJS",
|
|
5
|
-
"lib": ["ES2015"],
|
|
6
|
-
"skipLibCheck": true,
|
|
7
|
-
"moduleResolution": "node",
|
|
8
|
-
"resolveJsonModule": true,
|
|
9
|
-
"strict": true,
|
|
10
|
-
"outDir": "./dist/es2015",
|
|
11
|
-
"rootDir": "./src",
|
|
12
|
-
"declaration": false,
|
|
13
|
-
"declarationMap": false,
|
|
14
|
-
"sourceMap": false,
|
|
15
|
-
"downlevelIteration": true,
|
|
16
|
-
"esModuleInterop": true,
|
|
17
|
-
"allowSyntheticDefaultImports": true
|
|
18
|
-
},
|
|
19
|
-
"include": ["src/**/*"],
|
|
20
|
-
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
|
21
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2020",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"lib": ["ES2020"],
|
|
6
|
-
"skipLibCheck": true,
|
|
7
|
-
"moduleResolution": "bundler",
|
|
8
|
-
"resolveJsonModule": true,
|
|
9
|
-
"isolatedModules": true,
|
|
10
|
-
"strict": true,
|
|
11
|
-
"outDir": "./dist",
|
|
12
|
-
"rootDir": "./src",
|
|
13
|
-
"declaration": true,
|
|
14
|
-
"declarationMap": true,
|
|
15
|
-
"allowImportingTsExtensions": false
|
|
16
|
-
},
|
|
17
|
-
"include": ["src/**/*"],
|
|
18
|
-
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
|
19
|
-
}
|