@dataferry/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,433 @@
1
+ // src/resources/portalSessions.ts
2
+ var PortalSessions = class {
3
+ constructor(client) {
4
+ this.client = client;
5
+ }
6
+ /**
7
+ * Create a new portal session for an end user
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * const session = await ferry.portalSessions.create({
12
+ * profile: 'default',
13
+ * scopeId: 'user_123',
14
+ * expiresIn: 3600,
15
+ * returnUrl: 'https://yourapp.com/settings',
16
+ * });
17
+ *
18
+ * // Redirect user to session.url
19
+ * ```
20
+ */
21
+ async create(params) {
22
+ return this.client.request(
23
+ "POST",
24
+ "/api/v1/portal-sessions",
25
+ params
26
+ );
27
+ }
28
+ /**
29
+ * Get portal session details
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * const session = await ferry.portalSessions.retrieve('ps_abc123');
34
+ * console.log(session.scopeId, session.expiresAt);
35
+ * ```
36
+ */
37
+ async retrieve(sessionId) {
38
+ return this.client.request(
39
+ "GET",
40
+ `/api/v1/portal-sessions/${sessionId}`
41
+ );
42
+ }
43
+ /**
44
+ * Revoke a portal session
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * await ferry.portalSessions.revoke('ps_abc123');
49
+ * ```
50
+ */
51
+ async revoke(sessionId) {
52
+ return this.client.request(
53
+ "DELETE",
54
+ `/api/v1/portal-sessions/${sessionId}`
55
+ );
56
+ }
57
+ };
58
+
59
+ // src/resources/exports.ts
60
+ var Exports = class {
61
+ constructor(client) {
62
+ this.client = client;
63
+ }
64
+ /**
65
+ * Create a new export job
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * // Create export using primary connection
70
+ * const exportJob = await ferry.exports.create({
71
+ * scopeId: 'user_123',
72
+ * format: 'csv',
73
+ * tables: ['users', 'posts', 'comments'],
74
+ * });
75
+ *
76
+ * // Create export using specific connection
77
+ * const exportJob = await ferry.exports.create({
78
+ * scopeId: 'user_123',
79
+ * connectionId: 'conn_abc123',
80
+ * format: 'csv',
81
+ * tables: ['users', 'posts'],
82
+ * });
83
+ *
84
+ * console.log(exportJob.id, exportJob.status);
85
+ * ```
86
+ */
87
+ async create(params) {
88
+ return this.client.request(
89
+ "POST",
90
+ "/api/v1/exports",
91
+ params
92
+ );
93
+ }
94
+ /**
95
+ * Get export job details
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * const exportJob = await ferry.exports.retrieve('exp_abc123');
100
+ *
101
+ * if (exportJob.status === 'completed') {
102
+ * console.log('Download:', exportJob.downloadUrl);
103
+ * }
104
+ * ```
105
+ */
106
+ async retrieve(exportId) {
107
+ return this.client.request(
108
+ "GET",
109
+ `/api/v1/exports/${exportId}`
110
+ );
111
+ }
112
+ /**
113
+ * List exports
114
+ *
115
+ * @example
116
+ * ```ts
117
+ * // List all exports
118
+ * const { data, meta } = await ferry.exports.list();
119
+ *
120
+ * // Filter by scope
121
+ * const { data } = await ferry.exports.list({ scopeId: 'user_123' });
122
+ *
123
+ * // Filter by status
124
+ * const { data } = await ferry.exports.list({ status: 'completed' });
125
+ *
126
+ * // Filter by connection
127
+ * const { data } = await ferry.exports.list({ connectionId: 'conn_abc123' });
128
+ * ```
129
+ */
130
+ async list(params) {
131
+ return this.client.requestPaginated(
132
+ "/api/v1/exports",
133
+ params
134
+ );
135
+ }
136
+ /**
137
+ * Wait for an export to complete
138
+ *
139
+ * @example
140
+ * ```ts
141
+ * const exportJob = await ferry.exports.create({ scopeId: 'user_123' });
142
+ * const completed = await ferry.exports.poll(exportJob.id, { maxWait: 300000 });
143
+ *
144
+ * if (completed.status === 'completed') {
145
+ * console.log('Download:', completed.downloadUrl);
146
+ * }
147
+ * ```
148
+ */
149
+ async poll(exportId, options = {}) {
150
+ const maxWait = options.maxWait ?? 3e5;
151
+ const interval = options.interval ?? 2e3;
152
+ const startTime = Date.now();
153
+ while (Date.now() - startTime < maxWait) {
154
+ const exportJob = await this.retrieve(exportId);
155
+ if (exportJob.status === "completed" || exportJob.status === "failed" || exportJob.status === "expired") {
156
+ return exportJob;
157
+ }
158
+ await new Promise((resolve) => setTimeout(resolve, interval));
159
+ }
160
+ throw new Error(`Export ${exportId} did not complete within ${maxWait}ms`);
161
+ }
162
+ };
163
+
164
+ // src/resources/connections.ts
165
+ var Connections = class {
166
+ constructor(client) {
167
+ this.client = client;
168
+ }
169
+ /**
170
+ * List all database connections for the organization
171
+ *
172
+ * @example
173
+ * ```ts
174
+ * const connections = await ferry.connections.list();
175
+ * for (const conn of connections) {
176
+ * console.log(conn.name);
177
+ * }
178
+ * ```
179
+ */
180
+ async list() {
181
+ const result = await this.client.requestPaginated(
182
+ "/api/v1/connections"
183
+ );
184
+ return result.data;
185
+ }
186
+ /**
187
+ * Create a new database connection
188
+ *
189
+ * @example
190
+ * ```ts
191
+ * const connection = await ferry.connections.create({
192
+ * name: 'Production DB',
193
+ * host: 'db.example.com',
194
+ * port: '5432',
195
+ * database: 'myapp',
196
+ * user: 'ferry_readonly',
197
+ * password: 'secret',
198
+ * ssl: true,
199
+ * });
200
+ * ```
201
+ */
202
+ async create(params) {
203
+ return this.client.request(
204
+ "POST",
205
+ "/api/v1/connections",
206
+ params
207
+ );
208
+ }
209
+ /**
210
+ * Get a database connection by ID
211
+ *
212
+ * @example
213
+ * ```ts
214
+ * const connection = await ferry.connections.retrieve('conn_abc123');
215
+ * console.log(connection.host, connection.database);
216
+ * ```
217
+ */
218
+ async retrieve(connectionId) {
219
+ return this.client.request(
220
+ "GET",
221
+ `/api/v1/connections/${connectionId}`
222
+ );
223
+ }
224
+ /**
225
+ * Update a database connection
226
+ *
227
+ * @example
228
+ * ```ts
229
+ * const connection = await ferry.connections.update('conn_abc123', {
230
+ * name: 'Production DB (Updated)',
231
+ * });
232
+ * ```
233
+ */
234
+ async update(connectionId, params) {
235
+ return this.client.request(
236
+ "PUT",
237
+ `/api/v1/connections/${connectionId}`,
238
+ params
239
+ );
240
+ }
241
+ /**
242
+ * Delete a database connection
243
+ *
244
+ * @example
245
+ * ```ts
246
+ * const result = await ferry.connections.delete('conn_abc123');
247
+ * console.log(result.deleted); // true
248
+ * ```
249
+ */
250
+ async delete(connectionId) {
251
+ return this.client.request(
252
+ "DELETE",
253
+ `/api/v1/connections/${connectionId}`
254
+ );
255
+ }
256
+ /**
257
+ * Test a database connection
258
+ *
259
+ * @example
260
+ * ```ts
261
+ * const result = await ferry.connections.test('conn_abc123');
262
+ * if (result.connected) {
263
+ * console.log(`Connected to ${result.database} as ${result.user}`);
264
+ * } else {
265
+ * console.error(`Connection failed: ${result.error}`);
266
+ * }
267
+ * ```
268
+ */
269
+ async test(connectionId) {
270
+ return this.client.request(
271
+ "POST",
272
+ `/api/v1/connections/${connectionId}/test`
273
+ );
274
+ }
275
+ };
276
+
277
+ // src/client.ts
278
+ var Ferry = class {
279
+ apiKey;
280
+ baseUrl;
281
+ timeout;
282
+ /** Portal session management */
283
+ portalSessions;
284
+ /** Export management */
285
+ exports;
286
+ /** Database connection management */
287
+ connections;
288
+ constructor(config) {
289
+ this.apiKey = config.apiKey;
290
+ this.baseUrl = config.baseUrl ?? "https://api.dataferry.dev";
291
+ this.timeout = config.timeout ?? 3e4;
292
+ this.portalSessions = new PortalSessions(this);
293
+ this.exports = new Exports(this);
294
+ this.connections = new Connections(this);
295
+ }
296
+ /**
297
+ * Create a portal session for an end user to export their data.
298
+ *
299
+ * This is the primary method for initiating a data export. It creates a
300
+ * secure session and returns a URL where the user can select and download
301
+ * their data.
302
+ *
303
+ * @example
304
+ * ```typescript
305
+ * const ferry = new Ferry({ apiKey: 'your-api-key' });
306
+ *
307
+ * // Create a session for a specific user
308
+ * const session = await ferry.createPortalSession({
309
+ * profile: 'default',
310
+ * scopeId: 'user_123',
311
+ * returnUrl: 'https://app.example.com/settings',
312
+ * });
313
+ *
314
+ * // Redirect the user to the portal
315
+ * res.redirect(session.url);
316
+ * ```
317
+ *
318
+ * @example
319
+ * ```typescript
320
+ * // Single-tenant mode (no scopeId required)
321
+ * const session = await ferry.createPortalSession({
322
+ * profile: 'default',
323
+ * returnUrl: 'https://app.example.com/settings',
324
+ * });
325
+ * ```
326
+ */
327
+ async createPortalSession(params) {
328
+ const request = { profile: params.profile };
329
+ if (params.scopeId !== void 0) {
330
+ request.scopeId = params.scopeId;
331
+ }
332
+ if (params.returnUrl !== void 0) {
333
+ request.returnUrl = params.returnUrl;
334
+ }
335
+ if (params.expiresIn !== void 0) {
336
+ request.expiresIn = params.expiresIn;
337
+ }
338
+ if (params.email !== void 0) {
339
+ request.email = params.email;
340
+ }
341
+ if (params.metadata !== void 0) {
342
+ request.metadata = params.metadata;
343
+ }
344
+ const response = await this.portalSessions.create(request);
345
+ return {
346
+ id: response.id,
347
+ url: response.url,
348
+ expiresAt: response.expiresAt
349
+ };
350
+ }
351
+ /**
352
+ * Make an authenticated API request
353
+ */
354
+ async request(method, path, body) {
355
+ const url = `${this.baseUrl}${path}`;
356
+ const headers = {
357
+ "Content-Type": "application/json",
358
+ Authorization: `Basic ${Buffer.from(this.apiKey + ":").toString("base64")}`
359
+ };
360
+ const controller = new AbortController();
361
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
362
+ try {
363
+ const fetchOptions = {
364
+ method,
365
+ headers,
366
+ signal: controller.signal
367
+ };
368
+ if (body) {
369
+ fetchOptions.body = JSON.stringify(body);
370
+ }
371
+ const response = await fetch(url, fetchOptions);
372
+ const data = await response.json();
373
+ if (!response.ok || !data.success) {
374
+ const error = new FerryError(
375
+ data.error?.message ?? "An error occurred",
376
+ data.error?.code ?? "UNKNOWN_ERROR",
377
+ response.status
378
+ );
379
+ throw error;
380
+ }
381
+ return data.data;
382
+ } finally {
383
+ clearTimeout(timeoutId);
384
+ }
385
+ }
386
+ /**
387
+ * Make a paginated API request
388
+ */
389
+ async requestPaginated(path, params) {
390
+ const searchParams = new URLSearchParams();
391
+ if (params) {
392
+ for (const [key, value] of Object.entries(params)) {
393
+ if (value !== void 0) {
394
+ searchParams.set(key, String(value));
395
+ }
396
+ }
397
+ }
398
+ const url = searchParams.toString() ? `${path}?${searchParams}` : path;
399
+ const response = await fetch(`${this.baseUrl}${url}`, {
400
+ headers: {
401
+ Authorization: `Basic ${Buffer.from(this.apiKey + ":").toString("base64")}`
402
+ }
403
+ });
404
+ const result = await response.json();
405
+ if (!response.ok || !result.success) {
406
+ throw new FerryError(
407
+ result.error?.message ?? "An error occurred",
408
+ result.error?.code ?? "UNKNOWN_ERROR",
409
+ response.status
410
+ );
411
+ }
412
+ return {
413
+ data: result.data,
414
+ meta: result.meta
415
+ };
416
+ }
417
+ };
418
+ var FerryError = class extends Error {
419
+ constructor(message, code, status) {
420
+ super(message);
421
+ this.code = code;
422
+ this.status = status;
423
+ this.name = "FerryError";
424
+ }
425
+ };
426
+ export {
427
+ Connections,
428
+ Exports,
429
+ Ferry,
430
+ FerryError,
431
+ PortalSessions
432
+ };
433
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/resources/portalSessions.ts","../src/resources/exports.ts","../src/resources/connections.ts","../src/client.ts"],"sourcesContent":["/**\n * Portal Sessions resource\n */\n\nimport type { Ferry } from '../client.js';\nimport type {\n CreatePortalSessionRequest,\n CreatePortalSessionResponse,\n PortalSession,\n} from '../types.js';\n\nexport class PortalSessions {\n constructor(private readonly client: Ferry) {}\n\n /**\n * Create a new portal session for an end user\n *\n * @example\n * ```ts\n * const session = await ferry.portalSessions.create({\n * profile: 'default',\n * scopeId: 'user_123',\n * expiresIn: 3600,\n * returnUrl: 'https://yourapp.com/settings',\n * });\n *\n * // Redirect user to session.url\n * ```\n */\n async create(\n params: CreatePortalSessionRequest\n ): Promise<CreatePortalSessionResponse> {\n return this.client.request<CreatePortalSessionResponse>(\n 'POST',\n '/api/v1/portal-sessions',\n params\n );\n }\n\n /**\n * Get portal session details\n *\n * @example\n * ```ts\n * const session = await ferry.portalSessions.retrieve('ps_abc123');\n * console.log(session.scopeId, session.expiresAt);\n * ```\n */\n async retrieve(\n sessionId: string\n ): Promise<PortalSession & { isExpired: boolean }> {\n return this.client.request<PortalSession & { isExpired: boolean }>(\n 'GET',\n `/api/v1/portal-sessions/${sessionId}`\n );\n }\n\n /**\n * Revoke a portal session\n *\n * @example\n * ```ts\n * await ferry.portalSessions.revoke('ps_abc123');\n * ```\n */\n async revoke(sessionId: string): Promise<{ id: string }> {\n return this.client.request<{ id: string }>(\n 'DELETE',\n `/api/v1/portal-sessions/${sessionId}`\n );\n }\n}\n","/**\n * Exports resource\n */\n\nimport type { Ferry } from '../client.js';\nimport type {\n CreateExportRequest,\n ExportStatus,\n} from '../types.js';\n\ninterface ExportResponse {\n id: string;\n status: ExportStatus;\n scopeId: string;\n /** Database connection ID used for this export */\n connectionId?: string;\n /** Database connection name (if connection still exists) */\n connectionName?: string;\n format: string;\n tables: string[];\n downloadUrl?: string;\n downloadUrlExpiresAt?: string;\n fileSizeBytes?: number;\n totalRows?: number;\n errorMessage?: string;\n errorCode?: string;\n createdAt: string;\n startedAt?: string;\n completedAt?: string;\n}\n\ninterface ListExportsParams extends Record<string, string | number | undefined> {\n scopeId?: string;\n /** Filter by database connection ID */\n connectionId?: string;\n status?: ExportStatus;\n page?: number;\n limit?: number;\n}\n\nexport class Exports {\n constructor(private readonly client: Ferry) {}\n\n /**\n * Create a new export job\n *\n * @example\n * ```ts\n * // Create export using primary connection\n * const exportJob = await ferry.exports.create({\n * scopeId: 'user_123',\n * format: 'csv',\n * tables: ['users', 'posts', 'comments'],\n * });\n *\n * // Create export using specific connection\n * const exportJob = await ferry.exports.create({\n * scopeId: 'user_123',\n * connectionId: 'conn_abc123',\n * format: 'csv',\n * tables: ['users', 'posts'],\n * });\n *\n * console.log(exportJob.id, exportJob.status);\n * ```\n */\n async create(params: CreateExportRequest): Promise<ExportResponse> {\n return this.client.request<ExportResponse>(\n 'POST',\n '/api/v1/exports',\n params\n );\n }\n\n /**\n * Get export job details\n *\n * @example\n * ```ts\n * const exportJob = await ferry.exports.retrieve('exp_abc123');\n *\n * if (exportJob.status === 'completed') {\n * console.log('Download:', exportJob.downloadUrl);\n * }\n * ```\n */\n async retrieve(exportId: string): Promise<ExportResponse> {\n return this.client.request<ExportResponse>(\n 'GET',\n `/api/v1/exports/${exportId}`\n );\n }\n\n /**\n * List exports\n *\n * @example\n * ```ts\n * // List all exports\n * const { data, meta } = await ferry.exports.list();\n *\n * // Filter by scope\n * const { data } = await ferry.exports.list({ scopeId: 'user_123' });\n *\n * // Filter by status\n * const { data } = await ferry.exports.list({ status: 'completed' });\n *\n * // Filter by connection\n * const { data } = await ferry.exports.list({ connectionId: 'conn_abc123' });\n * ```\n */\n async list(params?: ListExportsParams): Promise<{\n data: ExportResponse[];\n meta: { page: number; limit: number; total: number; totalPages: number } | undefined;\n }> {\n return this.client.requestPaginated<ExportResponse>(\n '/api/v1/exports',\n params\n );\n }\n\n /**\n * Wait for an export to complete\n *\n * @example\n * ```ts\n * const exportJob = await ferry.exports.create({ scopeId: 'user_123' });\n * const completed = await ferry.exports.poll(exportJob.id, { maxWait: 300000 });\n *\n * if (completed.status === 'completed') {\n * console.log('Download:', completed.downloadUrl);\n * }\n * ```\n */\n async poll(\n exportId: string,\n options: { maxWait?: number; interval?: number } = {}\n ): Promise<ExportResponse> {\n const maxWait = options.maxWait ?? 300000; // 5 minutes\n const interval = options.interval ?? 2000; // 2 seconds\n const startTime = Date.now();\n\n while (Date.now() - startTime < maxWait) {\n const exportJob = await this.retrieve(exportId);\n\n if (\n exportJob.status === 'completed' ||\n exportJob.status === 'failed' ||\n exportJob.status === 'expired'\n ) {\n return exportJob;\n }\n\n await new Promise((resolve) => setTimeout(resolve, interval));\n }\n\n throw new Error(`Export ${exportId} did not complete within ${maxWait}ms`);\n }\n}\n","/**\n * Database Connections resource\n */\n\nimport type { Ferry } from '../client.js';\nimport type {\n DatabaseConnection,\n CreateConnectionRequest,\n UpdateConnectionRequest,\n TestConnectionResult,\n} from '../types.js';\n\nexport class Connections {\n constructor(private readonly client: Ferry) {}\n\n /**\n * List all database connections for the organization\n *\n * @example\n * ```ts\n * const connections = await ferry.connections.list();\n * for (const conn of connections) {\n * console.log(conn.name);\n * }\n * ```\n */\n async list(): Promise<DatabaseConnection[]> {\n const result = await this.client.requestPaginated<DatabaseConnection>(\n '/api/v1/connections'\n );\n return result.data;\n }\n\n /**\n * Create a new database connection\n *\n * @example\n * ```ts\n * const connection = await ferry.connections.create({\n * name: 'Production DB',\n * host: 'db.example.com',\n * port: '5432',\n * database: 'myapp',\n * user: 'ferry_readonly',\n * password: 'secret',\n * ssl: true,\n * });\n * ```\n */\n async create(params: CreateConnectionRequest): Promise<DatabaseConnection> {\n return this.client.request<DatabaseConnection>(\n 'POST',\n '/api/v1/connections',\n params\n );\n }\n\n /**\n * Get a database connection by ID\n *\n * @example\n * ```ts\n * const connection = await ferry.connections.retrieve('conn_abc123');\n * console.log(connection.host, connection.database);\n * ```\n */\n async retrieve(connectionId: string): Promise<DatabaseConnection> {\n return this.client.request<DatabaseConnection>(\n 'GET',\n `/api/v1/connections/${connectionId}`\n );\n }\n\n /**\n * Update a database connection\n *\n * @example\n * ```ts\n * const connection = await ferry.connections.update('conn_abc123', {\n * name: 'Production DB (Updated)',\n * });\n * ```\n */\n async update(\n connectionId: string,\n params: UpdateConnectionRequest\n ): Promise<DatabaseConnection> {\n return this.client.request<DatabaseConnection>(\n 'PUT',\n `/api/v1/connections/${connectionId}`,\n params\n );\n }\n\n /**\n * Delete a database connection\n *\n * @example\n * ```ts\n * const result = await ferry.connections.delete('conn_abc123');\n * console.log(result.deleted); // true\n * ```\n */\n async delete(connectionId: string): Promise<{ id: string; deleted: boolean }> {\n return this.client.request<{ id: string; deleted: boolean }>(\n 'DELETE',\n `/api/v1/connections/${connectionId}`\n );\n }\n\n /**\n * Test a database connection\n *\n * @example\n * ```ts\n * const result = await ferry.connections.test('conn_abc123');\n * if (result.connected) {\n * console.log(`Connected to ${result.database} as ${result.user}`);\n * } else {\n * console.error(`Connection failed: ${result.error}`);\n * }\n * ```\n */\n async test(connectionId: string): Promise<TestConnectionResult> {\n return this.client.request<TestConnectionResult>(\n 'POST',\n `/api/v1/connections/${connectionId}/test`\n );\n }\n\n}\n","/**\n * DataFerry SDK client\n */\n\nimport { PortalSessions } from './resources/portalSessions.js';\nimport { Exports } from './resources/exports.js';\nimport { Connections } from './resources/connections.js';\n\nexport interface FerryConfig {\n /** API key for authentication */\n apiKey: string;\n /** Base URL for the DataFerry API (default: https://api.dataferry.dev) */\n baseUrl?: string;\n /** Request timeout in milliseconds (default: 30000) */\n timeout?: number;\n}\n\ninterface ApiResponse<T> {\n success: boolean;\n data?: T;\n error?: {\n code: string;\n message: string;\n details?: Record<string, unknown>;\n };\n meta?: {\n page: number;\n limit: number;\n total: number;\n totalPages: number;\n };\n}\n\n/**\n * Parameters for creating a portal session\n */\nexport interface CreatePortalSessionParams {\n /** Export profile name (required) */\n profile: string;\n /** The scope ID for data filtering (optional for single-tenant mode) */\n scopeId?: string;\n /** Return URL after export completes */\n returnUrl?: string;\n /** Custom session duration in seconds (default: 3600) */\n expiresIn?: number;\n /** End-user email for export completion notifications */\n email?: string;\n /** Custom metadata to attach to the session */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Portal session response\n */\nexport interface PortalSessionResult {\n /** Session ID */\n id: string;\n /** URL to redirect the user to */\n url: string;\n /** When the session expires */\n expiresAt: Date;\n}\n\nexport class Ferry {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n\n /** Portal session management */\n public readonly portalSessions: PortalSessions;\n\n /** Export management */\n public readonly exports: Exports;\n\n /** Database connection management */\n public readonly connections: Connections;\n\n constructor(config: FerryConfig) {\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl ?? 'https://api.dataferry.dev';\n this.timeout = config.timeout ?? 30000;\n\n // Initialize resources\n this.portalSessions = new PortalSessions(this);\n this.exports = new Exports(this);\n this.connections = new Connections(this);\n }\n\n /**\n * Create a portal session for an end user to export their data.\n *\n * This is the primary method for initiating a data export. It creates a\n * secure session and returns a URL where the user can select and download\n * their data.\n *\n * @example\n * ```typescript\n * const ferry = new Ferry({ apiKey: 'your-api-key' });\n *\n * // Create a session for a specific user\n * const session = await ferry.createPortalSession({\n * profile: 'default',\n * scopeId: 'user_123',\n * returnUrl: 'https://app.example.com/settings',\n * });\n *\n * // Redirect the user to the portal\n * res.redirect(session.url);\n * ```\n *\n * @example\n * ```typescript\n * // Single-tenant mode (no scopeId required)\n * const session = await ferry.createPortalSession({\n * profile: 'default',\n * returnUrl: 'https://app.example.com/settings',\n * });\n * ```\n */\n async createPortalSession(\n params: CreatePortalSessionParams\n ): Promise<PortalSessionResult> {\n // Build request object, only including defined properties\n // (required by exactOptionalPropertyTypes)\n const request: {\n profile: string;\n scopeId?: string;\n returnUrl?: string;\n expiresIn?: number;\n email?: string;\n metadata?: Record<string, unknown>;\n } = { profile: params.profile };\n\n if (params.scopeId !== undefined) {\n request.scopeId = params.scopeId;\n }\n if (params.returnUrl !== undefined) {\n request.returnUrl = params.returnUrl;\n }\n if (params.expiresIn !== undefined) {\n request.expiresIn = params.expiresIn;\n }\n if (params.email !== undefined) {\n request.email = params.email;\n }\n if (params.metadata !== undefined) {\n request.metadata = params.metadata;\n }\n\n const response = await this.portalSessions.create(request);\n\n return {\n id: response.id,\n url: response.url,\n expiresAt: response.expiresAt,\n };\n }\n\n /**\n * Make an authenticated API request\n */\n async request<T>(\n method: string,\n path: string,\n body?: unknown\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Basic ${Buffer.from(this.apiKey + ':').toString('base64')}`,\n };\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const fetchOptions: RequestInit = {\n method,\n headers,\n signal: controller.signal,\n };\n if (body) {\n fetchOptions.body = JSON.stringify(body);\n }\n const response = await fetch(url, fetchOptions);\n\n const data = (await response.json()) as ApiResponse<T>;\n\n if (!response.ok || !data.success) {\n const error = new FerryError(\n data.error?.message ?? 'An error occurred',\n data.error?.code ?? 'UNKNOWN_ERROR',\n response.status\n );\n throw error;\n }\n\n return data.data!;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /**\n * Make a paginated API request\n */\n async requestPaginated<T>(\n path: string,\n params?: Record<string, string | number | undefined>\n ): Promise<{ data: T[]; meta: ApiResponse<T>['meta'] }> {\n const searchParams = new URLSearchParams();\n if (params) {\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined) {\n searchParams.set(key, String(value));\n }\n }\n }\n\n const url = searchParams.toString() ? `${path}?${searchParams}` : path;\n\n const response = await fetch(`${this.baseUrl}${url}`, {\n headers: {\n Authorization: `Basic ${Buffer.from(this.apiKey + ':').toString('base64')}`,\n },\n });\n\n const result = (await response.json()) as ApiResponse<T[]>;\n\n if (!response.ok || !result.success) {\n throw new FerryError(\n result.error?.message ?? 'An error occurred',\n result.error?.code ?? 'UNKNOWN_ERROR',\n response.status\n );\n }\n\n return {\n data: result.data!,\n meta: result.meta,\n };\n }\n}\n\n/**\n * DataFerry SDK error\n */\nexport class FerryError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly status: number\n ) {\n super(message);\n this.name = 'FerryError';\n }\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@dataferry/sdk",
3
+ "version": "0.1.0",
4
+ "description": "Node.js SDK for DataFerry - data export infrastructure for SaaS companies",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup",
21
+ "dev": "tsup --watch",
22
+ "test": "vitest run",
23
+ "test:watch": "vitest",
24
+ "clean": "rm -rf dist"
25
+ },
26
+ "dependencies": {},
27
+ "devDependencies": {
28
+ "tsup": "^8.5.1",
29
+ "typescript": "^5.3.3"
30
+ },
31
+ "keywords": [
32
+ "dataferry",
33
+ "ferry",
34
+ "data-export",
35
+ "gdpr",
36
+ "ccpa",
37
+ "data-portability",
38
+ "data-rights"
39
+ ]
40
+ }