@hamak/ui-remote-fs-impl 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/dist/actions/index.d.ts +5 -0
  2. package/dist/actions/index.d.ts.map +1 -0
  3. package/dist/actions/index.js +4 -0
  4. package/dist/actions/remote-fs-actions.d.ts +189 -0
  5. package/dist/actions/remote-fs-actions.d.ts.map +1 -0
  6. package/dist/actions/remote-fs-actions.js +146 -0
  7. package/dist/es2015/actions/index.js +20 -0
  8. package/dist/es2015/actions/remote-fs-actions.js +147 -0
  9. package/dist/es2015/index.js +25 -0
  10. package/dist/es2015/middleware/index.js +21 -0
  11. package/dist/es2015/middleware/remote-fs-middleware.js +205 -0
  12. package/dist/es2015/middleware/store-sync-middleware.js +138 -0
  13. package/dist/es2015/plugin/index.js +20 -0
  14. package/dist/es2015/plugin/remote-fs-plugin-factory.js +169 -0
  15. package/dist/es2015/providers/http-workspace-client.js +199 -0
  16. package/dist/es2015/providers/index.js +20 -0
  17. package/dist/es2015/services/index.js +20 -0
  18. package/dist/es2015/services/remote-fs-service.js +16 -0
  19. package/dist/index.d.ts +10 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +9 -0
  22. package/dist/middleware/index.d.ts +6 -0
  23. package/dist/middleware/index.d.ts.map +1 -0
  24. package/dist/middleware/index.js +5 -0
  25. package/dist/middleware/remote-fs-middleware.d.ts +50 -0
  26. package/dist/middleware/remote-fs-middleware.d.ts.map +1 -0
  27. package/dist/middleware/remote-fs-middleware.js +192 -0
  28. package/dist/middleware/store-sync-middleware.d.ts +35 -0
  29. package/dist/middleware/store-sync-middleware.d.ts.map +1 -0
  30. package/dist/middleware/store-sync-middleware.js +134 -0
  31. package/dist/plugin/index.d.ts +5 -0
  32. package/dist/plugin/index.d.ts.map +1 -0
  33. package/dist/plugin/index.js +4 -0
  34. package/dist/plugin/remote-fs-plugin-factory.d.ts +84 -0
  35. package/dist/plugin/remote-fs-plugin-factory.d.ts.map +1 -0
  36. package/dist/plugin/remote-fs-plugin-factory.js +150 -0
  37. package/dist/providers/http-workspace-client.d.ts +99 -0
  38. package/dist/providers/http-workspace-client.d.ts.map +1 -0
  39. package/dist/providers/http-workspace-client.js +171 -0
  40. package/dist/providers/index.d.ts +5 -0
  41. package/dist/providers/index.d.ts.map +1 -0
  42. package/dist/providers/index.js +4 -0
  43. package/dist/services/index.d.ts +5 -0
  44. package/dist/services/index.d.ts.map +1 -0
  45. package/dist/services/index.js +4 -0
  46. package/dist/services/remote-fs-service.d.ts +10 -0
  47. package/dist/services/remote-fs-service.d.ts.map +1 -0
  48. package/dist/services/remote-fs-service.js +12 -0
  49. package/package.json +56 -0
  50. package/project.json +24 -0
  51. package/src/actions/index.ts +5 -0
  52. package/src/actions/remote-fs-actions.ts +302 -0
  53. package/src/index.ts +10 -0
  54. package/src/middleware/index.ts +6 -0
  55. package/src/middleware/remote-fs-middleware.ts +244 -0
  56. package/src/middleware/store-sync-middleware.ts +175 -0
  57. package/src/plugin/index.ts +5 -0
  58. package/src/plugin/remote-fs-plugin-factory.ts +238 -0
  59. package/src/providers/http-workspace-client.ts +232 -0
  60. package/src/providers/index.ts +5 -0
  61. package/src/services/index.ts +5 -0
  62. package/src/services/remote-fs-service.ts +13 -0
  63. package/tsconfig.es2015.json +21 -0
  64. package/tsconfig.json +19 -0
@@ -0,0 +1,175 @@
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;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Remote FS Plugin Factory
3
+ */
4
+
5
+ export * from './remote-fs-plugin-factory';
@@ -0,0 +1,238 @@
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;
@@ -0,0 +1,232 @@
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
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Remote FS Providers
3
+ */
4
+
5
+ export * from './http-workspace-client';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Remote FS Services
3
+ */
4
+
5
+ export * from './remote-fs-service';
@@ -0,0 +1,13 @@
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
+ }
@@ -0,0 +1,21 @@
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
+ }