@jackchuka/gql-ingest 2.2.2 → 3.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.
Files changed (69) hide show
  1. package/README.md +259 -32
  2. package/dist/cli/commands/add.d.ts +3 -0
  3. package/dist/cli/commands/add.d.ts.map +1 -0
  4. package/dist/cli/commands/init.d.ts +3 -0
  5. package/dist/cli/commands/init.d.ts.map +1 -0
  6. package/dist/cli/index.d.ts +2 -0
  7. package/dist/cli/index.d.ts.map +1 -0
  8. package/dist/cli/index.js +288 -0
  9. package/dist/cli/templates/index.d.ts +14 -0
  10. package/dist/cli/templates/index.d.ts.map +1 -0
  11. package/dist/index.d.ts +16 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +15204 -0
  14. package/dist/index.js.map +7 -0
  15. package/dist/lib/config-schema.d.ts +103 -0
  16. package/dist/lib/config-schema.d.ts.map +1 -0
  17. package/dist/lib/config.d.ts +9 -0
  18. package/dist/lib/config.d.ts.map +1 -0
  19. package/dist/lib/dependency-resolver.d.ts.map +1 -0
  20. package/dist/lib/events.d.ts +167 -0
  21. package/dist/lib/events.d.ts.map +1 -0
  22. package/dist/lib/gql-ingest.d.ts +155 -0
  23. package/dist/lib/gql-ingest.d.ts.map +1 -0
  24. package/dist/{graphql-client.d.ts → lib/graphql-client.d.ts} +8 -4
  25. package/dist/lib/graphql-client.d.ts.map +1 -0
  26. package/dist/lib/index.d.ts +8 -0
  27. package/dist/lib/index.d.ts.map +1 -0
  28. package/dist/lib/logger.d.ts +29 -0
  29. package/dist/lib/logger.d.ts.map +1 -0
  30. package/dist/lib/mapper.d.ts +49 -0
  31. package/dist/lib/mapper.d.ts.map +1 -0
  32. package/dist/{metrics.d.ts → lib/metrics.d.ts} +22 -8
  33. package/dist/lib/metrics.d.ts.map +1 -0
  34. package/dist/readers/csv.d.ts +0 -4
  35. package/dist/readers/csv.d.ts.map +1 -1
  36. package/dist/readers/index.d.ts +1 -1
  37. package/dist/readers/index.d.ts.map +1 -1
  38. package/package.json +19 -7
  39. package/dist/cli.d.ts +0 -2
  40. package/dist/cli.d.ts.map +0 -1
  41. package/dist/cli.js +0 -237
  42. package/dist/config.d.ts +0 -32
  43. package/dist/config.d.ts.map +0 -1
  44. package/dist/config.test.d.ts +0 -2
  45. package/dist/config.test.d.ts.map +0 -1
  46. package/dist/dependency-resolver.d.ts.map +0 -1
  47. package/dist/dependency-resolver.test.d.ts +0 -2
  48. package/dist/dependency-resolver.test.d.ts.map +0 -1
  49. package/dist/graphql-client.d.ts.map +0 -1
  50. package/dist/graphql-client.test.d.ts +0 -2
  51. package/dist/graphql-client.test.d.ts.map +0 -1
  52. package/dist/mapper.d.ts +0 -31
  53. package/dist/mapper.d.ts.map +0 -1
  54. package/dist/mapper.test.d.ts +0 -2
  55. package/dist/mapper.test.d.ts.map +0 -1
  56. package/dist/metrics.d.ts.map +0 -1
  57. package/dist/metrics.test.d.ts +0 -2
  58. package/dist/metrics.test.d.ts.map +0 -1
  59. package/dist/readers/csv.test.d.ts +0 -2
  60. package/dist/readers/csv.test.d.ts.map +0 -1
  61. package/dist/readers/data-reader.test.d.ts +0 -2
  62. package/dist/readers/data-reader.test.d.ts.map +0 -1
  63. package/dist/readers/json.test.d.ts +0 -2
  64. package/dist/readers/json.test.d.ts.map +0 -1
  65. package/dist/readers/jsonl.test.d.ts +0 -2
  66. package/dist/readers/jsonl.test.d.ts.map +0 -1
  67. package/dist/readers/yaml.test.d.ts +0 -2
  68. package/dist/readers/yaml.test.d.ts.map +0 -1
  69. /package/dist/{dependency-resolver.d.ts → lib/dependency-resolver.d.ts} +0 -0
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![npm version](https://badge.fury.io/js/%40jackchuka%2Fgql-ingest.svg)](https://badge.fury.io/js/%40jackchuka%2Fgql-ingest)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- A TypeScript CLI tool that reads data from multiple formats (CSV, JSON, YAML, JSONL) and ingests it into GraphQL APIs through configurable mutations.
6
+ A TypeScript library and CLI tool that reads data from multiple formats (CSV, JSON, YAML, JSONL) and ingests it into GraphQL APIs through configurable mutations.
7
7
 
8
8
  ## Features
9
9
 
@@ -16,6 +16,8 @@ A TypeScript CLI tool that reads data from multiple formats (CSV, JSON, YAML, JS
16
16
  - ✅ Entity-level and row-level concurrency control
17
17
  - ✅ **Retry capabilities** with exponential backoff and configurable error handling
18
18
  - ✅ Comprehensive metrics and progress tracking
19
+ - ✅ **Event-based progress monitoring** with real-time callbacks
20
+ - ✅ **Cancellation support** via AbortController pattern
19
21
 
20
22
  ## Installation
21
23
 
@@ -38,55 +40,280 @@ pnpm install
38
40
  pnpm run build
39
41
  ```
40
42
 
43
+ ## Quick Start
44
+
45
+ Initialize a new configuration and start ingesting data in minutes:
46
+
47
+ ```bash
48
+ # Create a new configuration directory
49
+ gql-ingest init ./my-config
50
+
51
+ # Add a new entity
52
+ gql-ingest add users -p ./my-config -f json --fields "id,name,email"
53
+
54
+ # Run ingestion
55
+ gql-ingest -e https://your-api.com/graphql -c ./my-config
56
+ ```
57
+
41
58
  ## Usage
42
59
 
43
- ### CLI Options
60
+ ### CLI Commands
61
+
62
+ #### Initialize Configuration
63
+
64
+ Create a new configuration directory with example files:
65
+
66
+ ```bash
67
+ gql-ingest init [path] [options]
68
+
69
+ Options:
70
+ --no-example Skip creating example entity files
71
+ --no-config Skip creating config.yaml
72
+ -f, --force Overwrite existing files
73
+ -q, --quiet Suppress output
74
+ ```
75
+
76
+ This creates:
77
+
78
+ - `data/` - Data files directory
79
+ - `graphql/` - GraphQL mutation files
80
+ - `mappings/` - Mapping configuration files
81
+ - `config.yaml` - Processing configuration
82
+ - Example entity files (by default)
83
+
84
+ #### Add Entity
85
+
86
+ Add a new entity to an existing configuration:
87
+
88
+ ```bash
89
+ gql-ingest add <entity-name> [options]
90
+
91
+ Options:
92
+ -p, --path <path> Config directory path (default: current directory)
93
+ -f, --format <format> Data format (csv, json, yaml, jsonl)
94
+ --fields <fields> Comma-separated field names
95
+ --mutation <name> GraphQL mutation name
96
+ --no-interactive Skip prompts, use defaults only
97
+ -q, --quiet Suppress output
98
+ ```
99
+
100
+ Interactive mode prompts for format, fields, and mutation name. Use `--no-interactive` with flags for CI/CD.
101
+
102
+ #### Run Ingestion
103
+
104
+ Ingest data from configuration into GraphQL API:
44
105
 
45
106
  ```bash
46
107
  gql-ingest [options]
47
108
 
48
109
  Options:
49
- -V, --version output the version number
50
110
  -e, --endpoint <url> GraphQL endpoint URL (required)
51
111
  -c, --config <path> Path to configuration directory (required)
52
- -n, --entities <list> Comma-separated list of specific entities to process
53
- -h, --headers <headers> JSON string of headers to include in requests
54
- -f, --format <format> Override data format detection (csv, json, yaml, jsonl)
55
- -v, --verbose Show detailed request results and responses
56
- --help display help for command
112
+ -n, --entities <list> Comma-separated list of entities to process
113
+ -h, --headers <headers> JSON string of headers
114
+ -f, --format <format> Override data format detection
115
+ -q, --quiet Suppress output
57
116
  ```
58
117
 
59
- ### Examples
118
+ ### CLI Examples
60
119
 
61
120
  ```bash
62
121
  # Basic usage
63
- npx @jackchuka/gql-ingest \
64
- --endpoint https://your-graphql-api.com/graphql \
65
- --config ./examples/demo
122
+ gql-ingest \
123
+ -e https://your-graphql-api.com/graphql \
124
+ -c ./examples/demo
66
125
 
67
126
  # With authentication headers
68
- npx @jackchuka/gql-ingest \
69
- --endpoint https://your-graphql-api.com/graphql \
70
- --config ./examples/demo \
71
- --headers '{"Authorization": "Bearer YOUR_TOKEN"}'
72
-
73
- # With custom headers
74
- npx @jackchuka/gql-ingest \
75
- --endpoint https://api.example.com/graphql \
76
- --config ./my-config \
77
- --headers '{"X-API-Key": "your-api-key", "Content-Type": "application/json"}'
127
+ gql-ingest \
128
+ -e https://your-graphql-api.com/graphql \
129
+ -c ./examples/demo \
130
+ -h '{"Authorization": "Bearer YOUR_TOKEN"}'
78
131
 
79
132
  # Process specific entities only
80
- npx @jackchuka/gql-ingest \
81
- --endpoint https://your-graphql-api.com/graphql \
82
- --config ./examples/demo \
83
- --entities users,products
84
-
85
- # Process a single entity
86
- npx @jackchuka/gql-ingest \
87
- --endpoint https://your-graphql-api.com/graphql \
88
- --config ./examples/demo \
89
- --entities items
133
+ gql-ingest \
134
+ -e https://your-graphql-api.com/graphql \
135
+ -c ./examples/demo \
136
+ -n users,products
137
+ ```
138
+
139
+ ### Programmatic API
140
+
141
+ GQL Ingest provides a full programmatic API for integration into your Node.js applications.
142
+
143
+ #### Installation for API Usage
144
+
145
+ ```bash
146
+ npm install @jackchuka/gql-ingest
147
+ ```
148
+
149
+ #### Basic API Usage
150
+
151
+ ```typescript
152
+ import { GQLIngest, createConsoleLogger } from "@jackchuka/gql-ingest";
153
+
154
+ // Initialize the client
155
+ const client = new GQLIngest({
156
+ endpoint: "https://your-graphql-api.com/graphql",
157
+ headers: {
158
+ Authorization: "Bearer YOUR_TOKEN",
159
+ },
160
+ logger: createConsoleLogger({ prefix: "my-app" }), // Optional: enable logging with prefix
161
+ });
162
+
163
+ // Ingest all data from a configuration
164
+ const result = await client.ingest("./config");
165
+
166
+ // Check if ingestion was successful
167
+ if (result.success) {
168
+ console.log("Ingestion completed successfully");
169
+ console.log("Metrics:", result.metrics);
170
+ } else {
171
+ console.error("Ingestion failed:", result.errors);
172
+ }
173
+ ```
174
+
175
+ #### Processing Specific Entities
176
+
177
+ ```typescript
178
+ // Process only specific entities
179
+ const result = await client.ingestEntities("./config", ["users", "products"]);
180
+
181
+ // Or using the ingest method with options
182
+ const result = await client.ingest("./config", {
183
+ entities: ["users", "products"],
184
+ format: "csv", // Optional: override format detection
185
+ });
186
+ ```
187
+
188
+ #### Advanced API Usage
189
+
190
+ For more control, you can access the underlying components directly:
191
+
192
+ ```typescript
193
+ import {
194
+ GraphQLClientWrapper,
195
+ DataMapper,
196
+ DependencyResolver,
197
+ MetricsCollector,
198
+ loadConfig,
199
+ createConsoleLogger,
200
+ } from "@jackchuka/gql-ingest";
201
+
202
+ // Create your own custom workflow
203
+ const logger = createConsoleLogger();
204
+ const metrics = new MetricsCollector();
205
+ const client = new GraphQLClientWrapper(endpoint, headers, metrics, logger);
206
+ const mapper = new DataMapper(client, basePath, metrics, logger);
207
+
208
+ // Load configuration
209
+ const config = loadConfig("./config");
210
+
211
+ // Process entities with custom logic
212
+ // ... your custom implementation
213
+ ```
214
+
215
+ #### API Methods
216
+
217
+ **GQLIngest Class Methods:**
218
+
219
+ - `constructor(options: GQLIngestOptions)` - Initialize the client
220
+ - `ingest(configPath: string, options?: IngestOptions)` - Ingest data from a configuration
221
+ - `ingestEntities(configPath: string, entities: string[])` - Process specific entities
222
+ - `getMetrics()` - Get current processing metrics
223
+ - `getMetricsSummary()` - Get formatted metrics summary
224
+ - `setLogger(logger: Logger)` - Set custom logger
225
+ - `setHeaders(headers: Record<string, string>)` - Update request headers
226
+ - `cancel(reason?: string)` - Cancel in-progress ingestion
227
+ - `processing` - Property indicating if ingestion is in progress
228
+
229
+ #### Event-Based Progress Monitoring
230
+
231
+ GQLIngest extends EventEmitter, enabling real-time progress tracking and cancellation:
232
+
233
+ ```typescript
234
+ import { GQLIngest } from "@jackchuka/gql-ingest";
235
+
236
+ const client = new GQLIngest({
237
+ endpoint: "https://your-api.com/graphql",
238
+ eventOptions: {
239
+ emitRowEvents: true, // Emit events for each row
240
+ emitProgressEvents: true, // Emit periodic progress
241
+ progressInterval: 1000, // Progress every 1 second
242
+ },
243
+ });
244
+
245
+ // Listen for events
246
+ client.on("started", (p) => console.log(`Starting ${p.totalEntities} entities`));
247
+ client.on("progress", (p) => console.log(`${p.progressPercent.toFixed(1)}% complete`));
248
+ client.on("entityStart", (p) => console.log(`Processing ${p.entityName}`));
249
+ client.on("entityComplete", (p) =>
250
+ console.log(`${p.entityName}: ${p.metrics.successfulRows} rows`),
251
+ );
252
+ client.on("rowSuccess", (p) => console.log(`Row ${p.rowIndex} OK`));
253
+ client.on("rowFailure", (p) => console.error(`Row ${p.rowIndex} failed: ${p.error.message}`));
254
+ client.on("finished", (p) => console.log(`Done in ${p.durationMs}ms`));
255
+ client.on("errored", (p) => console.error(`Error: ${p.error.message}`));
256
+ client.on("cancelled", (p) => console.log(`Cancelled: ${p.reason}`));
257
+
258
+ // Handle graceful shutdown
259
+ process.on("SIGINT", () => client.cancel("User interrupted"));
260
+
261
+ await client.ingest("./config");
262
+ ```
263
+
264
+ **Available Events:**
265
+
266
+ | Event | When Emitted | Key Payload Fields |
267
+ | ---------------- | ------------------------ | ------------------------------------------------- |
268
+ | `started` | Ingestion begins | `configPath`, `entityNames`, `totalWaves` |
269
+ | `progress` | Periodic interval | `progressPercent`, `successfulRows`, `failedRows` |
270
+ | `entityStart` | Entity processing begins | `entityName`, `totalRows`, `waveIndex` |
271
+ | `entityComplete` | Entity processing ends | `entityName`, `metrics`, `success` |
272
+ | `rowSuccess` | Row mutation succeeds | `entityName`, `rowIndex`, `row`, `result` |
273
+ | `rowFailure` | Row mutation fails | `entityName`, `rowIndex`, `error` |
274
+ | `cancelled` | Processing cancelled | `reason`, `metrics`, `elapsedMs` |
275
+ | `finished` | Processing completes | `metrics`, `durationMs`, `allSuccessful` |
276
+ | `errored` | Fatal error occurs | `error`, `metrics`, `elapsedMs` |
277
+
278
+ #### Cancellation Support
279
+
280
+ Cancel in-progress ingestion using the `cancel()` method or external AbortController:
281
+
282
+ ```typescript
283
+ // Method 1: Using cancel()
284
+ const client = new GQLIngest({ endpoint: "..." });
285
+ process.on("SIGINT", () => client.cancel("User interrupted"));
286
+ await client.ingest("./config");
287
+
288
+ // Method 2: Using external AbortController
289
+ const controller = new AbortController();
290
+ setTimeout(() => controller.abort("Timeout"), 60000);
291
+ await client.ingest("./config", { signal: controller.signal });
292
+ ```
293
+
294
+ #### TypeScript Support
295
+
296
+ Full TypeScript support is included with comprehensive type definitions:
297
+
298
+ ```typescript
299
+ import type {
300
+ GQLIngestOptions,
301
+ IngestOptions,
302
+ IngestResult,
303
+ ProcessingMetrics,
304
+ EntityMetrics,
305
+ // Event types
306
+ EventOptions,
307
+ StartedEventPayload,
308
+ ProgressEventPayload,
309
+ EntityStartEventPayload,
310
+ EntityCompleteEventPayload,
311
+ RowSuccessEventPayload,
312
+ RowFailureEventPayload,
313
+ CancelledEventPayload,
314
+ FinishedEventPayload,
315
+ ErroredEventPayload,
316
+ } from "@jackchuka/gql-ingest";
90
317
  ```
91
318
 
92
319
  ## Parallel Processing 🚀
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function registerAddCommand(program: Command): void;
3
+ //# sourceMappingURL=add.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAepC,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkGzD"}
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function registerInitCommand(program: Command): void;
3
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYpC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAuC1D"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}