@unireq/ftp 0.0.1
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 +21 -0
- package/dist/basic-ftp-DKCTDPH7.js +2 -0
- package/dist/basic-ftp-DKCTDPH7.js.map +1 -0
- package/dist/chunk-IDTRCBYG.js +2 -0
- package/dist/chunk-IDTRCBYG.js.map +1 -0
- package/dist/index.d.ts +322 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Olivier Orabona
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"basic-ftp-DKCTDPH7.js"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {Readable,Writable}from'stream';var c=class{options;client=null;capabilities={ftp:true,ftps:true,delete:true,rename:true,mkdir:true,rmdir:true};constructor(s={}){this.options=s;}async connect(s){let e=new URL(s),{Client:i}=await import('basic-ftp'),a=new i(this.options.timeout);this.client=a;let t=e.protocol==="ftps:";return await a.access({host:e.hostname,port:e.port?parseInt(e.port,10):t?990:21,user:e.username||"anonymous",password:e.password||"anonymous@",secure:t,secureOptions:this.options.secureOptions}),{connected:true,host:e.hostname,user:e.username||"anonymous",secure:t}}async request(s,e){if(!this.client||!s.connected)throw new Error("FTP session not connected");let i=e.operation,a=new URL(e.url),t=decodeURIComponent(a.pathname)||"/";try{let n;switch(i){case "list":{n=(await this.client.list(t)).map(o=>({name:o.name,type:o.type,size:o.size,modifiedAt:o.modifiedAt,permissions:o.permissions,owner:o.user,group:o.group}));break}case "get":{let r=[],o=new Writable({write(p,f,d){r.push(p),d();}});await this.client.downloadTo(o,t),n=Buffer.concat(r);break}case "put":{let r=typeof e.body=="string"?Buffer.from(e.body):e.body instanceof Buffer?e.body:Buffer.from(JSON.stringify(e.body)),o=Readable.from(r);await this.client.uploadFrom(o,t),n={uploaded:!0,path:t};break}case "delete":{await this.client.remove(t),n={deleted:!0,path:t};break}case "rename":{let r=e.destination;if(!r)throw new Error("Rename operation requires a destination path");await this.client.rename(t,r),n={renamed:!0,from:t,to:r};break}case "mkdir":{await this.client.ensureDir(t),n={created:!0,path:t};break}case "rmdir":{await this.client.removeDir(t),n={removed:!0,path:t};break}default:throw new Error(`Unsupported FTP operation: ${i}`)}return {status:200,statusText:"OK",headers:{},data:n,ok:!0}}catch(n){return {status:500,statusText:"Error",headers:{},data:{error:n instanceof Error?n.message:String(n)},ok:false}}}disconnect(s){this.client&&s.connected&&(this.client.close(),this.client=null);}};function w(u){return new c(u)}export{c as a,w as b};//# sourceMappingURL=chunk-IDTRCBYG.js.map
|
|
2
|
+
//# sourceMappingURL=chunk-IDTRCBYG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/connectors/basic-ftp.ts"],"names":["BasicFtpConnector","options","uri","url","Client","client","secure","session","ctx","operation","path","data","item","chunks","writable","Writable","chunk","_encoding","callback","content","readable","Readable","destination","error","createDefaultFtpConnector"],"mappings":"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","file":"chunk-IDTRCBYG.js","sourcesContent":["/**\n * BasicFtpConnector - Default FTP connector using basic-ftp library\n *\n * This connector implements the FTPConnector interface using the basic-ftp library.\n * It is the default connector used when no custom connector is provided.\n *\n * @requires basic-ftp - Install with: npm install basic-ftp\n */\n\nimport { Readable, Writable } from 'node:stream';\nimport type { RequestContext, Response } from '@unireq/core';\nimport type {\n FTPCapabilities,\n FTPConnector,\n FTPConnectorOptions,\n FTPFileEntry,\n FTPOperation,\n FTPSession,\n} from '../connector.js';\n\n/**\n * BasicFtpConnector using basic-ftp library\n *\n * @example\n * ```ts\n * import { ftp } from '@unireq/ftp';\n * import { BasicFtpConnector } from '@unireq/ftp/connectors/basic-ftp';\n *\n * // With default options\n * const { transport } = ftp('ftp://server.com');\n *\n * // With custom options\n * const connector = new BasicFtpConnector({ timeout: 30000 });\n * const { transport } = ftp('ftp://server.com', connector);\n * ```\n */\nexport class BasicFtpConnector implements FTPConnector {\n private readonly options: FTPConnectorOptions;\n private client: BasicFtpClient | null = null;\n\n readonly capabilities: FTPCapabilities = {\n ftp: true,\n ftps: true,\n delete: true,\n rename: true,\n mkdir: true,\n rmdir: true,\n };\n\n constructor(options: FTPConnectorOptions = {}) {\n this.options = options;\n }\n\n async connect(uri: string): Promise<FTPSession> {\n const url = new URL(uri);\n\n // Dynamically import basic-ftp\n const { Client } = await import('basic-ftp');\n // Cast to our minimal interface to avoid type conflicts with basic-ftp internals\n const client = new Client(this.options.timeout) as unknown as BasicFtpClient;\n this.client = client;\n\n const secure = url.protocol === 'ftps:';\n\n await client.access({\n host: url.hostname,\n port: url.port ? parseInt(url.port, 10) : secure ? 990 : 21,\n user: url.username || 'anonymous',\n password: url.password || 'anonymous@',\n secure,\n secureOptions: this.options.secureOptions,\n });\n\n return {\n connected: true,\n host: url.hostname,\n user: url.username || 'anonymous',\n secure,\n };\n }\n\n async request(session: FTPSession, ctx: RequestContext): Promise<Response> {\n if (!this.client || !session.connected) {\n throw new Error('FTP session not connected');\n }\n\n const operation = (ctx as Record<string, unknown>)['operation'] as FTPOperation | undefined;\n const url = new URL(ctx.url);\n const path = decodeURIComponent(url.pathname) || '/';\n\n try {\n let data: unknown;\n\n switch (operation) {\n case 'list': {\n const rawList = await this.client.list(path);\n data = rawList.map(\n (item): FTPFileEntry => ({\n name: item.name,\n type: item.type,\n size: item.size,\n modifiedAt: item.modifiedAt,\n permissions: item.permissions,\n owner: item.user,\n group: item.group,\n }),\n );\n break;\n }\n\n case 'get': {\n const chunks: Buffer[] = [];\n const writable = new Writable({\n write(chunk, _encoding, callback) {\n chunks.push(chunk);\n callback();\n },\n });\n await this.client.downloadTo(writable, path);\n data = Buffer.concat(chunks);\n break;\n }\n\n case 'put': {\n const content =\n typeof ctx.body === 'string'\n ? Buffer.from(ctx.body)\n : ctx.body instanceof Buffer\n ? ctx.body\n : Buffer.from(JSON.stringify(ctx.body));\n\n const readable = Readable.from(content);\n await this.client.uploadFrom(readable, path);\n data = { uploaded: true, path };\n break;\n }\n\n case 'delete': {\n await this.client.remove(path);\n data = { deleted: true, path };\n break;\n }\n\n case 'rename': {\n const destination = (ctx as Record<string, unknown>)['destination'] as string;\n if (!destination) {\n throw new Error('Rename operation requires a destination path');\n }\n await this.client.rename(path, destination);\n data = { renamed: true, from: path, to: destination };\n break;\n }\n\n case 'mkdir': {\n await this.client.ensureDir(path);\n data = { created: true, path };\n break;\n }\n\n case 'rmdir': {\n await this.client.removeDir(path);\n data = { removed: true, path };\n break;\n }\n\n default:\n throw new Error(`Unsupported FTP operation: ${operation}`);\n }\n\n return {\n status: 200,\n statusText: 'OK',\n headers: {},\n data,\n ok: true,\n };\n } catch (error) {\n return {\n status: 500,\n statusText: 'Error',\n headers: {},\n data: { error: error instanceof Error ? error.message : String(error) },\n ok: false,\n };\n }\n }\n\n disconnect(session: FTPSession): void {\n if (this.client && session.connected) {\n this.client.close();\n this.client = null;\n }\n }\n}\n\n/**\n * basic-ftp Client interface (minimal subset used by connector)\n * This allows the connector to work without requiring basic-ftp types at compile time\n */\ninterface BasicFtpClient {\n access(options: {\n host: string;\n port?: number;\n user?: string;\n password?: string;\n secure?: boolean;\n secureOptions?: unknown;\n }): Promise<void>;\n close(): void;\n list(path?: string): Promise<BasicFtpFileInfo[]>;\n downloadTo(destination: NodeJS.WritableStream, fromPath: string): Promise<void>;\n uploadFrom(source: NodeJS.ReadableStream, toPath: string): Promise<void>;\n remove(path: string): Promise<void>;\n rename(fromPath: string, toPath: string): Promise<void>;\n ensureDir(path: string): Promise<void>;\n removeDir(path: string): Promise<void>;\n}\n\ninterface BasicFtpFileInfo {\n name: string;\n type: number;\n size: number;\n modifiedAt?: Date;\n permissions?: string;\n user?: string;\n group?: string;\n}\n\n/**\n * Creates a default BasicFtpConnector instance\n * Used internally by the ftp() transport function\n */\nexport function createDefaultFtpConnector(options?: FTPConnectorOptions): FTPConnector {\n return new BasicFtpConnector(options);\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { Connector, RequestContext, Response, TransportWithCapabilities, Policy } from '@unireq/core';
|
|
2
|
+
export { Connector, RequestContext, Response, TransportWithCapabilities, policy } from '@unireq/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* FTP Connector interface for BYOC (Bring Your Own Connector) pattern
|
|
6
|
+
*
|
|
7
|
+
* This interface extends the core Connector with FTP-specific capabilities.
|
|
8
|
+
* Implement this interface to create custom FTP connectors.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { ftp, type FTPConnector } from '@unireq/ftp';
|
|
13
|
+
*
|
|
14
|
+
* class MyFtpConnector implements FTPConnector {
|
|
15
|
+
* // ... implementation
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* const { transport } = ftp('ftp://server.com', new MyFtpConnector());
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* FTP session representing an active connection
|
|
24
|
+
*/
|
|
25
|
+
interface FTPSession {
|
|
26
|
+
/** Whether the session is currently connected */
|
|
27
|
+
readonly connected: boolean;
|
|
28
|
+
/** FTP server hostname */
|
|
29
|
+
readonly host: string;
|
|
30
|
+
/** Authenticated username */
|
|
31
|
+
readonly user: string;
|
|
32
|
+
/** Whether the connection uses TLS (FTPS) */
|
|
33
|
+
readonly secure: boolean;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* FTP connector capabilities
|
|
37
|
+
*/
|
|
38
|
+
interface FTPCapabilities {
|
|
39
|
+
/** Supports FTP protocol */
|
|
40
|
+
readonly ftp: boolean;
|
|
41
|
+
/** Supports FTPS (FTP over TLS) */
|
|
42
|
+
readonly ftps: boolean;
|
|
43
|
+
/** Supports file deletion */
|
|
44
|
+
readonly delete: boolean;
|
|
45
|
+
/** Supports file/directory renaming */
|
|
46
|
+
readonly rename: boolean;
|
|
47
|
+
/** Supports directory creation */
|
|
48
|
+
readonly mkdir: boolean;
|
|
49
|
+
/** Supports directory removal */
|
|
50
|
+
readonly rmdir: boolean;
|
|
51
|
+
/** Index signature for TransportCapabilities compatibility */
|
|
52
|
+
readonly [key: string]: boolean | undefined;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* FTP connector options for connection configuration
|
|
56
|
+
*/
|
|
57
|
+
interface FTPConnectorOptions {
|
|
58
|
+
/** Connection timeout in milliseconds */
|
|
59
|
+
readonly timeout?: number;
|
|
60
|
+
/** Use passive mode (default: true) */
|
|
61
|
+
readonly passive?: boolean;
|
|
62
|
+
/** TLS options for secure connections */
|
|
63
|
+
readonly secureOptions?: {
|
|
64
|
+
/** Minimum TLS version */
|
|
65
|
+
readonly minVersion?: string;
|
|
66
|
+
/** Reject unauthorized certificates */
|
|
67
|
+
readonly rejectUnauthorized?: boolean;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* FTP operation types extracted from request context
|
|
72
|
+
*/
|
|
73
|
+
type FTPOperation = 'list' | 'get' | 'put' | 'delete' | 'rename' | 'mkdir' | 'rmdir';
|
|
74
|
+
/**
|
|
75
|
+
* Extended request context for FTP operations
|
|
76
|
+
*/
|
|
77
|
+
interface FTPRequestContext extends RequestContext {
|
|
78
|
+
/** FTP operation to perform */
|
|
79
|
+
readonly operation: FTPOperation;
|
|
80
|
+
/** Destination path for rename operations */
|
|
81
|
+
readonly destination?: string;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* FTP Connector interface
|
|
85
|
+
*
|
|
86
|
+
* Extends the core Connector interface with FTP-specific capabilities.
|
|
87
|
+
* The connector is responsible for:
|
|
88
|
+
* - Establishing and managing FTP connections
|
|
89
|
+
* - Translating RequestContext to FTP operations
|
|
90
|
+
* - Returning standardized Response objects
|
|
91
|
+
*/
|
|
92
|
+
interface FTPConnector extends Connector<FTPSession> {
|
|
93
|
+
/**
|
|
94
|
+
* Connect to an FTP server
|
|
95
|
+
* @param uri - FTP URI (e.g., 'ftp://user:pass@host:port')
|
|
96
|
+
* @returns FTP session
|
|
97
|
+
*/
|
|
98
|
+
connect(uri: string): Promise<FTPSession>;
|
|
99
|
+
/**
|
|
100
|
+
* Execute an FTP request
|
|
101
|
+
* @param session - Active FTP session
|
|
102
|
+
* @param ctx - Request context with FTP operation
|
|
103
|
+
* @returns Response with operation result
|
|
104
|
+
*/
|
|
105
|
+
request(session: FTPSession, ctx: RequestContext): Promise<Response>;
|
|
106
|
+
/**
|
|
107
|
+
* Disconnect from FTP server
|
|
108
|
+
* @param session - Active FTP session
|
|
109
|
+
*/
|
|
110
|
+
disconnect(session: FTPSession): Promise<void> | void;
|
|
111
|
+
/**
|
|
112
|
+
* Connector capabilities
|
|
113
|
+
*/
|
|
114
|
+
readonly capabilities: FTPCapabilities;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* FTP file entry from directory listing
|
|
118
|
+
*/
|
|
119
|
+
interface FTPFileEntry {
|
|
120
|
+
/** File or directory name */
|
|
121
|
+
readonly name: string;
|
|
122
|
+
/** Entry type: 0 = file, 1 = directory, 2 = symbolic link */
|
|
123
|
+
readonly type: number;
|
|
124
|
+
/** File size in bytes (for files only) */
|
|
125
|
+
readonly size?: number;
|
|
126
|
+
/** Last modification date */
|
|
127
|
+
readonly modifiedAt?: Date;
|
|
128
|
+
/** File permissions string (e.g., 'rwxr-xr-x') */
|
|
129
|
+
readonly permissions?: string;
|
|
130
|
+
/** Owner name */
|
|
131
|
+
readonly owner?: string;
|
|
132
|
+
/** Group name */
|
|
133
|
+
readonly group?: string;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* BasicFtpConnector - Default FTP connector using basic-ftp library
|
|
138
|
+
*
|
|
139
|
+
* This connector implements the FTPConnector interface using the basic-ftp library.
|
|
140
|
+
* It is the default connector used when no custom connector is provided.
|
|
141
|
+
*
|
|
142
|
+
* @requires basic-ftp - Install with: npm install basic-ftp
|
|
143
|
+
*/
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* BasicFtpConnector using basic-ftp library
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* ```ts
|
|
150
|
+
* import { ftp } from '@unireq/ftp';
|
|
151
|
+
* import { BasicFtpConnector } from '@unireq/ftp/connectors/basic-ftp';
|
|
152
|
+
*
|
|
153
|
+
* // With default options
|
|
154
|
+
* const { transport } = ftp('ftp://server.com');
|
|
155
|
+
*
|
|
156
|
+
* // With custom options
|
|
157
|
+
* const connector = new BasicFtpConnector({ timeout: 30000 });
|
|
158
|
+
* const { transport } = ftp('ftp://server.com', connector);
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
declare class BasicFtpConnector implements FTPConnector {
|
|
162
|
+
private readonly options;
|
|
163
|
+
private client;
|
|
164
|
+
readonly capabilities: FTPCapabilities;
|
|
165
|
+
constructor(options?: FTPConnectorOptions);
|
|
166
|
+
connect(uri: string): Promise<FTPSession>;
|
|
167
|
+
request(session: FTPSession, ctx: RequestContext): Promise<Response>;
|
|
168
|
+
disconnect(session: FTPSession): void;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Creates a default BasicFtpConnector instance
|
|
172
|
+
* Used internally by the ftp() transport function
|
|
173
|
+
*/
|
|
174
|
+
declare function createDefaultFtpConnector(options?: FTPConnectorOptions): FTPConnector;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* FTP Transport using BYOC (Bring Your Own Connector) pattern
|
|
178
|
+
*
|
|
179
|
+
* The ftp() function creates an FTP transport that can use:
|
|
180
|
+
* - The default BasicFtpConnector (requires basic-ftp package)
|
|
181
|
+
* - A custom connector implementing FTPConnector interface
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```ts
|
|
185
|
+
* import { ftp } from '@unireq/ftp';
|
|
186
|
+
* import { client } from '@unireq/core';
|
|
187
|
+
*
|
|
188
|
+
* // Using default connector (requires: npm install basic-ftp)
|
|
189
|
+
* const { transport } = ftp('ftp://user:pass@server.com');
|
|
190
|
+
* const api = client(transport);
|
|
191
|
+
*
|
|
192
|
+
* // Using custom connector (BYOC)
|
|
193
|
+
* import { ftp, type FTPConnector } from '@unireq/ftp';
|
|
194
|
+
* const { transport } = ftp('ftp://server.com', myCustomConnector);
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* FTP transport options
|
|
200
|
+
*/
|
|
201
|
+
interface FTPTransportOptions {
|
|
202
|
+
/** Connection timeout in milliseconds */
|
|
203
|
+
readonly timeout?: number;
|
|
204
|
+
/** Use passive mode (default: true) */
|
|
205
|
+
readonly passive?: boolean;
|
|
206
|
+
/** TLS options for FTPS connections */
|
|
207
|
+
readonly secureOptions?: {
|
|
208
|
+
readonly minVersion?: string;
|
|
209
|
+
readonly rejectUnauthorized?: boolean;
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Creates an FTP transport
|
|
214
|
+
*
|
|
215
|
+
* Supports BYOC (Bring Your Own Connector) pattern:
|
|
216
|
+
* - Without connector: Uses default BasicFtpConnector (requires basic-ftp)
|
|
217
|
+
* - With connector: Uses the provided FTPConnector implementation
|
|
218
|
+
*
|
|
219
|
+
* @param uri - FTP URI (e.g., 'ftp://user:pass@server.com/path')
|
|
220
|
+
* @param connectorOrOptions - Custom connector or transport options
|
|
221
|
+
* @returns Transport with FTP capabilities
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* ```ts
|
|
225
|
+
* // Simple usage with default connector
|
|
226
|
+
* const { transport } = ftp('ftp://server.com');
|
|
227
|
+
*
|
|
228
|
+
* // With credentials in URI
|
|
229
|
+
* const { transport } = ftp('ftp://user:password@server.com');
|
|
230
|
+
*
|
|
231
|
+
* // With FTPS (secure)
|
|
232
|
+
* const { transport } = ftp('ftps://secure.server.com');
|
|
233
|
+
*
|
|
234
|
+
* // With custom connector (BYOC)
|
|
235
|
+
* const { transport } = ftp('ftp://server.com', myConnector);
|
|
236
|
+
*
|
|
237
|
+
* // With options (uses default connector)
|
|
238
|
+
* const { transport } = ftp('ftp://server.com', { timeout: 30000 });
|
|
239
|
+
* ```
|
|
240
|
+
*/
|
|
241
|
+
declare function ftp(uri?: string, connectorOrOptions?: FTPConnector | FTPTransportOptions): TransportWithCapabilities;
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* @unireq/ftp - FTP transport with BYOC (Bring Your Own Connector) pattern
|
|
245
|
+
*
|
|
246
|
+
* This package provides an FTP transport that can use:
|
|
247
|
+
* - The default BasicFtpConnector (requires basic-ftp package as peer dependency)
|
|
248
|
+
* - A custom connector implementing FTPConnector interface (BYOC)
|
|
249
|
+
*
|
|
250
|
+
* @example Default usage (requires: npm install basic-ftp)
|
|
251
|
+
* ```ts
|
|
252
|
+
* import { ftp } from '@unireq/ftp';
|
|
253
|
+
* import { client } from '@unireq/core';
|
|
254
|
+
*
|
|
255
|
+
* const { transport } = ftp('ftp://user:pass@server.com');
|
|
256
|
+
* const api = client(transport);
|
|
257
|
+
*
|
|
258
|
+
* // List directory
|
|
259
|
+
* const files = await api.get('/path', ftpOperation('list'));
|
|
260
|
+
*
|
|
261
|
+
* // Download file
|
|
262
|
+
* const content = await api.get('/file.txt', ftpOperation('get'));
|
|
263
|
+
*
|
|
264
|
+
* // Upload file
|
|
265
|
+
* await api.put('/upload/file.txt', 'content', ftpOperation('put'));
|
|
266
|
+
* ```
|
|
267
|
+
*
|
|
268
|
+
* @example BYOC (Bring Your Own Connector)
|
|
269
|
+
* ```ts
|
|
270
|
+
* import { ftp, type FTPConnector } from '@unireq/ftp';
|
|
271
|
+
*
|
|
272
|
+
* class MyFtpConnector implements FTPConnector {
|
|
273
|
+
* // Custom implementation...
|
|
274
|
+
* }
|
|
275
|
+
*
|
|
276
|
+
* const { transport } = ftp('ftp://server.com', new MyFtpConnector());
|
|
277
|
+
* ```
|
|
278
|
+
*
|
|
279
|
+
* @packageDocumentation
|
|
280
|
+
*/
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* FTP operation policy factory
|
|
284
|
+
* Creates a policy that injects the FTP operation into the request context
|
|
285
|
+
*
|
|
286
|
+
* @param operation - FTP operation to perform
|
|
287
|
+
* @param extras - Additional context parameters (e.g., destination for rename)
|
|
288
|
+
* @returns Policy that injects operation into context
|
|
289
|
+
*
|
|
290
|
+
* @example
|
|
291
|
+
* ```ts
|
|
292
|
+
* import { ftp, ftpOperation } from '@unireq/ftp';
|
|
293
|
+
* import { client } from '@unireq/core';
|
|
294
|
+
*
|
|
295
|
+
* const { transport } = ftp('ftp://server.com');
|
|
296
|
+
* const api = client(transport);
|
|
297
|
+
*
|
|
298
|
+
* // List directory
|
|
299
|
+
* await api.get('/path', ftpOperation('list'));
|
|
300
|
+
*
|
|
301
|
+
* // Download file
|
|
302
|
+
* await api.get('/file.txt', ftpOperation('get'));
|
|
303
|
+
*
|
|
304
|
+
* // Upload file
|
|
305
|
+
* await api.put('/file.txt', content, ftpOperation('put'));
|
|
306
|
+
*
|
|
307
|
+
* // Delete file
|
|
308
|
+
* await api.delete('/file.txt', ftpOperation('delete'));
|
|
309
|
+
*
|
|
310
|
+
* // Rename file
|
|
311
|
+
* await api.get('/old.txt', ftpOperation('rename', { destination: '/new.txt' }));
|
|
312
|
+
*
|
|
313
|
+
* // Create directory
|
|
314
|
+
* await api.get('/newdir', ftpOperation('mkdir'));
|
|
315
|
+
*
|
|
316
|
+
* // Remove directory
|
|
317
|
+
* await api.get('/olddir', ftpOperation('rmdir'));
|
|
318
|
+
* ```
|
|
319
|
+
*/
|
|
320
|
+
declare function ftpOperation(operation: 'list' | 'get' | 'put' | 'delete' | 'rename' | 'mkdir' | 'rmdir', extras?: Record<string, unknown>): Policy;
|
|
321
|
+
|
|
322
|
+
export { BasicFtpConnector, type FTPCapabilities, type FTPConnector, type FTPConnectorOptions, type FTPFileEntry, type FTPOperation, type FTPRequestContext, type FTPSession, type FTPTransportOptions, createDefaultFtpConnector, ftp, ftpOperation };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export{a as BasicFtpConnector,b as createDefaultFtpConnector}from'./chunk-IDTRCBYG.js';import {policy}from'@unireq/core';export{policy}from'@unireq/core';async function y(e){try{let{createDefaultFtpConnector:n}=await import('./basic-ftp-DKCTDPH7.js');return n(e)}catch(n){let o=n instanceof Error?n.message:String(n);throw new Error(`Default FTP connector not available. Install basic-ftp or provide a custom connector.
|
|
2
|
+
Install: npm install basic-ftp
|
|
3
|
+
Or use BYOC: ftp('ftp://server.com', myConnector)
|
|
4
|
+
Cause: ${o}`)}}function P(e,n){let o=null,r=null,a=t=>t!==null&&typeof t=="object"&&"connect"in t&&"request"in t&&"disconnect"in t,p=a(n)?n:null,l=a(n)?void 0:n,c=policy(async t=>{r||(r=p??await y(l)),!o&&e&&(o=await r.connect(e));let i=t.url;if(e&&t.url.startsWith("/")){let s=new URL(e);s.pathname=t.url,i=s.toString();}else if(!t.url.startsWith("ftp://")&&!t.url.startsWith("ftps://")&&e){let s=new URL(e);s.pathname=t.url.startsWith("/")?t.url:`/${t.url}`,i=s.toString();}return r.request(o,{...t,url:i})},{name:"ftp",kind:"transport",options:e?{uri:e}:void 0}),u={ftp:true,ftps:true,delete:true,rename:true,mkdir:true,rmdir:true};return {transport:c,capabilities:p?.capabilities??u}}function R(e,n={}){return policy(async(o,r)=>r({...o,operation:e,...n}),{name:`ftp:${e}`,kind:"other"})}
|
|
5
|
+
export{P as ftp,R as ftpOperation};//# sourceMappingURL=index.js.map
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/transport.ts","../src/index.ts"],"names":["getDefaultConnector","options","createDefaultFtpConnector","error","cause","ftp","uri","connectorOrOptions","session","actualConnector","isConnector","arg","providedConnector","transport","policy","ctx","finalUrl","baseUrl","defaultCapabilities","ftpOperation","operation","extras","policyFn","next"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * FTP Transport using BYOC (Bring Your Own Connector) pattern\n *\n * The ftp() function creates an FTP transport that can use:\n * - The default BasicFtpConnector (requires basic-ftp package)\n * - A custom connector implementing FTPConnector interface\n *\n * @example\n * ```ts\n * import { ftp } from '@unireq/ftp';\n * import { client } from '@unireq/core';\n *\n * // Using default connector (requires: npm install basic-ftp)\n * const { transport } = ftp('ftp://user:pass@server.com');\n * const api = client(transport);\n *\n * // Using custom connector (BYOC)\n * import { ftp, type FTPConnector } from '@unireq/ftp';\n * const { transport } = ftp('ftp://server.com', myCustomConnector);\n * ```\n */\n\nimport type { RequestContext, Response, TransportWithCapabilities } from '@unireq/core';\nimport { policy } from '@unireq/core';\nimport type { FTPConnector, FTPConnectorOptions, FTPSession } from './connector.js';\n\n/**\n * Lazily loads the default FTP connector\n * Throws a helpful error if basic-ftp is not installed\n */\nasync function getDefaultConnector(options?: FTPConnectorOptions): Promise<FTPConnector> {\n try {\n const { createDefaultFtpConnector } = await import('./connectors/basic-ftp.js');\n return createDefaultFtpConnector(options);\n } catch (error) {\n const cause = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Default FTP connector not available. Install basic-ftp or provide a custom connector.\\n` +\n `Install: npm install basic-ftp\\n` +\n `Or use BYOC: ftp('ftp://server.com', myConnector)\\n` +\n `Cause: ${cause}`,\n );\n }\n}\n\n/**\n * FTP transport options\n */\nexport interface FTPTransportOptions {\n /** Connection timeout in milliseconds */\n readonly timeout?: number;\n /** Use passive mode (default: true) */\n readonly passive?: boolean;\n /** TLS options for FTPS connections */\n readonly secureOptions?: {\n readonly minVersion?: string;\n readonly rejectUnauthorized?: boolean;\n };\n}\n\n/**\n * Creates an FTP transport\n *\n * Supports BYOC (Bring Your Own Connector) pattern:\n * - Without connector: Uses default BasicFtpConnector (requires basic-ftp)\n * - With connector: Uses the provided FTPConnector implementation\n *\n * @param uri - FTP URI (e.g., 'ftp://user:pass@server.com/path')\n * @param connectorOrOptions - Custom connector or transport options\n * @returns Transport with FTP capabilities\n *\n * @example\n * ```ts\n * // Simple usage with default connector\n * const { transport } = ftp('ftp://server.com');\n *\n * // With credentials in URI\n * const { transport } = ftp('ftp://user:password@server.com');\n *\n * // With FTPS (secure)\n * const { transport } = ftp('ftps://secure.server.com');\n *\n * // With custom connector (BYOC)\n * const { transport } = ftp('ftp://server.com', myConnector);\n *\n * // With options (uses default connector)\n * const { transport } = ftp('ftp://server.com', { timeout: 30000 });\n * ```\n */\nexport function ftp(uri?: string, connectorOrOptions?: FTPConnector | FTPTransportOptions): TransportWithCapabilities {\n let session: FTPSession | null = null;\n let actualConnector: FTPConnector | null = null;\n\n // Determine if second argument is a connector or options\n const isConnector = (arg: unknown): arg is FTPConnector =>\n arg !== null && typeof arg === 'object' && 'connect' in arg && 'request' in arg && 'disconnect' in arg;\n\n const providedConnector = isConnector(connectorOrOptions) ? connectorOrOptions : null;\n const options = !isConnector(connectorOrOptions) ? connectorOrOptions : undefined;\n\n const transport = policy(\n async (ctx: RequestContext): Promise<Response> => {\n // Lazy load connector\n if (!actualConnector) {\n actualConnector = providedConnector ?? (await getDefaultConnector(options));\n }\n\n // Connect if not connected and URI provided\n if (!session && uri) {\n session = await actualConnector.connect(uri);\n }\n\n // Build final URL: combine base URI with relative path\n let finalUrl = ctx.url;\n if (uri && ctx.url.startsWith('/')) {\n const baseUrl = new URL(uri);\n baseUrl.pathname = ctx.url;\n finalUrl = baseUrl.toString();\n } else if (!ctx.url.startsWith('ftp://') && !ctx.url.startsWith('ftps://')) {\n // If no scheme, prepend the base URI\n if (uri) {\n const baseUrl = new URL(uri);\n baseUrl.pathname = ctx.url.startsWith('/') ? ctx.url : `/${ctx.url}`;\n finalUrl = baseUrl.toString();\n }\n }\n\n // biome-ignore lint/style/noNonNullAssertion: session is guaranteed by connect() or existing connection above\n return actualConnector.request(session!, { ...ctx, url: finalUrl });\n },\n {\n name: 'ftp',\n kind: 'transport',\n options: uri ? { uri } : undefined,\n },\n );\n\n // Get capabilities from connector if available, otherwise use defaults\n const defaultCapabilities = {\n ftp: true,\n ftps: true,\n delete: true,\n rename: true,\n mkdir: true,\n rmdir: true,\n };\n\n return {\n transport,\n capabilities: providedConnector?.capabilities ?? defaultCapabilities,\n };\n}\n","/**\n * @unireq/ftp - FTP transport with BYOC (Bring Your Own Connector) pattern\n *\n * This package provides an FTP transport that can use:\n * - The default BasicFtpConnector (requires basic-ftp package as peer dependency)\n * - A custom connector implementing FTPConnector interface (BYOC)\n *\n * @example Default usage (requires: npm install basic-ftp)\n * ```ts\n * import { ftp } from '@unireq/ftp';\n * import { client } from '@unireq/core';\n *\n * const { transport } = ftp('ftp://user:pass@server.com');\n * const api = client(transport);\n *\n * // List directory\n * const files = await api.get('/path', ftpOperation('list'));\n *\n * // Download file\n * const content = await api.get('/file.txt', ftpOperation('get'));\n *\n * // Upload file\n * await api.put('/upload/file.txt', 'content', ftpOperation('put'));\n * ```\n *\n * @example BYOC (Bring Your Own Connector)\n * ```ts\n * import { ftp, type FTPConnector } from '@unireq/ftp';\n *\n * class MyFtpConnector implements FTPConnector {\n * // Custom implementation...\n * }\n *\n * const { transport } = ftp('ftp://server.com', new MyFtpConnector());\n * ```\n *\n * @packageDocumentation\n */\n\n// Re-export core types for convenience\nexport type { Connector, RequestContext, Response, TransportWithCapabilities } from '@unireq/core';\nexport { policy } from '@unireq/core';\n// Connector interface for BYOC\nexport type {\n FTPCapabilities,\n FTPConnector,\n FTPConnectorOptions,\n FTPFileEntry,\n FTPOperation,\n FTPRequestContext,\n FTPSession,\n} from './connector.js';\n// Default connector (requires basic-ftp peer dependency)\nexport { BasicFtpConnector, createDefaultFtpConnector } from './connectors/basic-ftp.js';\n// Main transport function\nexport { type FTPTransportOptions, ftp } from './transport.js';\n\n// Import policy for internal use\nimport { type RequestContext as Ctx, type Policy, policy as policyFn, type Response as Res } from '@unireq/core';\n\n/**\n * FTP operation policy factory\n * Creates a policy that injects the FTP operation into the request context\n *\n * @param operation - FTP operation to perform\n * @param extras - Additional context parameters (e.g., destination for rename)\n * @returns Policy that injects operation into context\n *\n * @example\n * ```ts\n * import { ftp, ftpOperation } from '@unireq/ftp';\n * import { client } from '@unireq/core';\n *\n * const { transport } = ftp('ftp://server.com');\n * const api = client(transport);\n *\n * // List directory\n * await api.get('/path', ftpOperation('list'));\n *\n * // Download file\n * await api.get('/file.txt', ftpOperation('get'));\n *\n * // Upload file\n * await api.put('/file.txt', content, ftpOperation('put'));\n *\n * // Delete file\n * await api.delete('/file.txt', ftpOperation('delete'));\n *\n * // Rename file\n * await api.get('/old.txt', ftpOperation('rename', { destination: '/new.txt' }));\n *\n * // Create directory\n * await api.get('/newdir', ftpOperation('mkdir'));\n *\n * // Remove directory\n * await api.get('/olddir', ftpOperation('rmdir'));\n * ```\n */\nexport function ftpOperation(\n operation: 'list' | 'get' | 'put' | 'delete' | 'rename' | 'mkdir' | 'rmdir',\n extras: Record<string, unknown> = {},\n): Policy {\n return policyFn(\n async (ctx: Ctx, next: (ctx: Ctx) => Promise<Res>): Promise<Res> => next({ ...ctx, operation, ...extras }),\n { name: `ftp:${operation}`, kind: 'other' },\n );\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@unireq/ftp",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "FTP transport for unireq using basic-ftp",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"author": "Olivier Orabona",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@unireq/core": "0.0.1"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"basic-ftp": "^5.0.5"
|
|
24
|
+
},
|
|
25
|
+
"peerDependenciesMeta": {
|
|
26
|
+
"basic-ftp": {
|
|
27
|
+
"optional": true
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"typescript": "^5.9.3",
|
|
32
|
+
"tsup": "^8.5.1"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18.0.0"
|
|
36
|
+
},
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/oorabona/unireq",
|
|
40
|
+
"directory": "packages/ftp"
|
|
41
|
+
},
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/oorabona/unireq/issues"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/oorabona/unireq/tree/main/packages/ftp",
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "tsup",
|
|
48
|
+
"type-check": "tsc --noEmit",
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"clean": "rm -rf dist *.tsbuildinfo"
|
|
51
|
+
}
|
|
52
|
+
}
|