@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 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,2 @@
1
+ export{a as BasicFtpConnector,b as createDefaultFtpConnector}from'./chunk-IDTRCBYG.js';//# sourceMappingURL=basic-ftp-DKCTDPH7.js.map
2
+ //# sourceMappingURL=basic-ftp-DKCTDPH7.js.map
@@ -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":"2CAoCaA,CAAAA,CAAN,KAAgD,CACpC,OAAA,CACT,MAAA,CAAgC,IAAA,CAE/B,aAAgC,CACvC,GAAA,CAAK,IAAA,CACL,IAAA,CAAM,IAAA,CACN,MAAA,CAAQ,KACR,MAAA,CAAQ,IAAA,CACR,KAAA,CAAO,IAAA,CACP,KAAA,CAAO,IACT,EAEA,WAAA,CAAYC,CAAAA,CAA+B,EAAC,CAAG,CAC7C,IAAA,CAAK,QAAUA,EACjB,CAEA,MAAM,OAAA,CAAQC,CAAAA,CAAkC,CAC9C,IAAMC,CAAAA,CAAM,IAAI,GAAA,CAAID,CAAG,CAAA,CAGjB,CAAE,OAAAE,CAAO,CAAA,CAAI,MAAM,OAAO,WAAW,CAAA,CAErCC,EAAS,IAAID,CAAAA,CAAO,IAAA,CAAK,OAAA,CAAQ,OAAO,CAAA,CAC9C,KAAK,MAAA,CAASC,CAAAA,CAEd,IAAMC,CAAAA,CAASH,CAAAA,CAAI,QAAA,GAAa,QAEhC,OAAA,MAAME,CAAAA,CAAO,MAAA,CAAO,CAClB,IAAA,CAAMF,CAAAA,CAAI,SACV,IAAA,CAAMA,CAAAA,CAAI,IAAA,CAAO,QAAA,CAASA,CAAAA,CAAI,IAAA,CAAM,EAAE,CAAA,CAAIG,CAAAA,CAAS,GAAA,CAAM,EAAA,CACzD,IAAA,CAAMH,CAAAA,CAAI,UAAY,WAAA,CACtB,QAAA,CAAUA,CAAAA,CAAI,QAAA,EAAY,YAAA,CAC1B,MAAA,CAAAG,EACA,aAAA,CAAe,IAAA,CAAK,OAAA,CAAQ,aAC9B,CAAC,CAAA,CAEM,CACL,SAAA,CAAW,IAAA,CACX,IAAA,CAAMH,CAAAA,CAAI,QAAA,CACV,IAAA,CAAMA,EAAI,QAAA,EAAY,WAAA,CACtB,MAAA,CAAAG,CACF,CACF,CAEA,MAAM,OAAA,CAAQC,CAAAA,CAAqBC,CAAAA,CAAwC,CACzE,GAAI,CAAC,KAAK,MAAA,EAAU,CAACD,CAAAA,CAAQ,SAAA,CAC3B,MAAM,IAAI,MAAM,2BAA2B,CAAA,CAG7C,IAAME,CAAAA,CAAaD,CAAAA,CAAgC,SAAA,CAC7CL,EAAM,IAAI,GAAA,CAAIK,CAAAA,CAAI,GAAG,CAAA,CACrBE,CAAAA,CAAO,mBAAmBP,CAAAA,CAAI,QAAQ,CAAA,EAAK,GAAA,CAEjD,GAAI,CACF,IAAIQ,CAAAA,CAEJ,OAAQF,CAAAA,EACN,KAAK,MAAA,CAAQ,CAEXE,CAAAA,CAAAA,CADgB,MAAM,IAAA,CAAK,MAAA,CAAO,IAAA,CAAKD,CAAI,GAC5B,GAAA,CACZE,CAAAA,GAAwB,CACvB,IAAA,CAAMA,CAAAA,CAAK,IAAA,CACX,KAAMA,CAAAA,CAAK,IAAA,CACX,IAAA,CAAMA,CAAAA,CAAK,IAAA,CACX,UAAA,CAAYA,EAAK,UAAA,CACjB,WAAA,CAAaA,CAAAA,CAAK,WAAA,CAClB,KAAA,CAAOA,CAAAA,CAAK,KACZ,KAAA,CAAOA,CAAAA,CAAK,KACd,CAAA,CACF,CAAA,CACA,KACF,CAEA,KAAK,KAAA,CAAO,CACV,IAAMC,CAAAA,CAAmB,GACnBC,CAAAA,CAAW,IAAIC,QAAAA,CAAS,CAC5B,KAAA,CAAMC,CAAAA,CAAOC,EAAWC,CAAAA,CAAU,CAChCL,CAAAA,CAAO,IAAA,CAAKG,CAAK,CAAA,CACjBE,IACF,CACF,CAAC,CAAA,CACD,MAAM,IAAA,CAAK,OAAO,UAAA,CAAWJ,CAAAA,CAAUJ,CAAI,CAAA,CAC3CC,CAAAA,CAAO,MAAA,CAAO,OAAOE,CAAM,CAAA,CAC3B,KACF,CAEA,KAAK,KAAA,CAAO,CACV,IAAMM,CAAAA,CACJ,OAAOX,CAAAA,CAAI,IAAA,EAAS,QAAA,CAChB,OAAO,IAAA,CAAKA,CAAAA,CAAI,IAAI,CAAA,CACpBA,CAAAA,CAAI,IAAA,YAAgB,OAClBA,CAAAA,CAAI,IAAA,CACJ,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,SAAA,CAAUA,EAAI,IAAI,CAAC,CAAA,CAEtCY,CAAAA,CAAWC,QAAAA,CAAS,IAAA,CAAKF,CAAO,CAAA,CACtC,MAAM,IAAA,CAAK,MAAA,CAAO,UAAA,CAAWC,CAAAA,CAAUV,CAAI,CAAA,CAC3CC,CAAAA,CAAO,CAAE,QAAA,CAAU,CAAA,CAAA,CAAM,IAAA,CAAAD,CAAK,CAAA,CAC9B,KACF,CAEA,KAAK,QAAA,CAAU,CACb,MAAM,IAAA,CAAK,MAAA,CAAO,MAAA,CAAOA,CAAI,CAAA,CAC7BC,CAAAA,CAAO,CAAE,OAAA,CAAS,CAAA,CAAA,CAAM,IAAA,CAAAD,CAAK,CAAA,CAC7B,KACF,CAEA,KAAK,QAAA,CAAU,CACb,IAAMY,CAAAA,CAAed,CAAAA,CAAgC,YACrD,GAAI,CAACc,CAAAA,CACH,MAAM,IAAI,KAAA,CAAM,8CAA8C,CAAA,CAEhE,MAAM,IAAA,CAAK,MAAA,CAAO,MAAA,CAAOZ,CAAAA,CAAMY,CAAW,CAAA,CAC1CX,CAAAA,CAAO,CAAE,OAAA,CAAS,CAAA,CAAA,CAAM,IAAA,CAAMD,EAAM,EAAA,CAAIY,CAAY,CAAA,CACpD,KACF,CAEA,KAAK,QAAS,CACZ,MAAM,IAAA,CAAK,MAAA,CAAO,SAAA,CAAUZ,CAAI,EAChCC,CAAAA,CAAO,CAAE,OAAA,CAAS,CAAA,CAAA,CAAM,IAAA,CAAAD,CAAK,EAC7B,KACF,CAEA,KAAK,OAAA,CAAS,CACZ,MAAM,KAAK,MAAA,CAAO,SAAA,CAAUA,CAAI,CAAA,CAChCC,CAAAA,CAAO,CAAE,QAAS,CAAA,CAAA,CAAM,IAAA,CAAAD,CAAK,CAAA,CAC7B,KACF,CAEA,QACE,MAAM,IAAI,KAAA,CAAM,CAAA,2BAAA,EAA8BD,CAAS,CAAA,CAAE,CAC7D,CAEA,OAAO,CACL,MAAA,CAAQ,GAAA,CACR,UAAA,CAAY,KACZ,OAAA,CAAS,EAAC,CACV,IAAA,CAAAE,CAAAA,CACA,EAAA,CAAI,EACN,CACF,CAAA,MAASY,CAAAA,CAAO,CACd,OAAO,CACL,OAAQ,GAAA,CACR,UAAA,CAAY,OAAA,CACZ,OAAA,CAAS,EAAC,CACV,KAAM,CAAE,KAAA,CAAOA,CAAAA,YAAiB,KAAA,CAAQA,CAAAA,CAAM,OAAA,CAAU,OAAOA,CAAK,CAAE,CAAA,CACtE,EAAA,CAAI,KACN,CACF,CACF,CAEA,UAAA,CAAWhB,CAAAA,CAA2B,CAChC,IAAA,CAAK,MAAA,EAAUA,EAAQ,SAAA,GACzB,IAAA,CAAK,MAAA,CAAO,KAAA,EAAM,CAClB,IAAA,CAAK,OAAS,IAAA,EAElB,CACF,EAuCO,SAASiB,CAAAA,CAA0BvB,CAAAA,CAA6C,CACrF,OAAO,IAAID,CAAAA,CAAkBC,CAAO,CACtC","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"]}
@@ -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
+ }