@jackchuka/gql-ingest 4.0.0 → 4.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/README.md +48 -0
- package/dist/cli/index.js +62 -62
- package/dist/index.js +118 -27
- package/dist/index.js.map +2 -2
- package/dist/lib/gql-ingest.d.ts +1 -0
- package/dist/lib/gql-ingest.d.ts.map +1 -1
- package/dist/lib/mapper.d.ts +13 -1
- package/dist/lib/mapper.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14314,6 +14314,12 @@ var MetricsCollector = class {
|
|
|
14314
14314
|
};
|
|
14315
14315
|
|
|
14316
14316
|
// src/lib/mapper.ts
|
|
14317
|
+
var RefResolutionError = class extends Error {
|
|
14318
|
+
constructor(message) {
|
|
14319
|
+
super(message);
|
|
14320
|
+
this.name = "RefResolutionError";
|
|
14321
|
+
}
|
|
14322
|
+
};
|
|
14317
14323
|
var DataMapper = class {
|
|
14318
14324
|
constructor(client, basePath = process.cwd(), metrics, logger = noopLogger, formatOverride) {
|
|
14319
14325
|
this.client = client;
|
|
@@ -14331,7 +14337,7 @@ var DataMapper = class {
|
|
|
14331
14337
|
/**
|
|
14332
14338
|
* Process an entity with event callbacks and abort support
|
|
14333
14339
|
*/
|
|
14334
|
-
async processEntityWithEvents(configPath, parallelConfig, retryConfig, signal, callbacks) {
|
|
14340
|
+
async processEntityWithEvents(configPath, parallelConfig, retryConfig, signal, callbacks, outputStore) {
|
|
14335
14341
|
const entityStartTime = Date.now();
|
|
14336
14342
|
const configFullPath = path.resolve(this.basePath, configPath);
|
|
14337
14343
|
const config2 = JSON.parse(fs6.readFileSync(configFullPath, "utf8"));
|
|
@@ -14359,7 +14365,9 @@ var DataMapper = class {
|
|
|
14359
14365
|
parallelConfig,
|
|
14360
14366
|
retryConfig,
|
|
14361
14367
|
signal,
|
|
14362
|
-
callbacks
|
|
14368
|
+
callbacks,
|
|
14369
|
+
config2.outputCapture,
|
|
14370
|
+
outputStore
|
|
14363
14371
|
);
|
|
14364
14372
|
} else {
|
|
14365
14373
|
await this.processRowsSequentiallyWithEvents(
|
|
@@ -14369,7 +14377,9 @@ var DataMapper = class {
|
|
|
14369
14377
|
entityName,
|
|
14370
14378
|
retryConfig,
|
|
14371
14379
|
signal,
|
|
14372
|
-
callbacks
|
|
14380
|
+
callbacks,
|
|
14381
|
+
config2.outputCapture,
|
|
14382
|
+
outputStore
|
|
14373
14383
|
);
|
|
14374
14384
|
}
|
|
14375
14385
|
this.metrics.finishEntityProcessing(entityName);
|
|
@@ -14393,7 +14403,7 @@ var DataMapper = class {
|
|
|
14393
14403
|
durationMs: duration3
|
|
14394
14404
|
});
|
|
14395
14405
|
}
|
|
14396
|
-
async processRowsSequentiallyWithEvents(data, mutation, mapping, entityName, retryConfig, signal, callbacks) {
|
|
14406
|
+
async processRowsSequentiallyWithEvents(data, mutation, mapping, entityName, retryConfig, signal, callbacks, outputCapture, outputStore) {
|
|
14397
14407
|
const totalRows = data.length;
|
|
14398
14408
|
const variableTypes = this.extractVariableTypes(mutation);
|
|
14399
14409
|
for (let i = 0; i < data.length; i++) {
|
|
@@ -14402,11 +14412,12 @@ var DataMapper = class {
|
|
|
14402
14412
|
return;
|
|
14403
14413
|
}
|
|
14404
14414
|
const row = data[i];
|
|
14405
|
-
const variables = this.mapRowToVariables(row, mapping, variableTypes);
|
|
14406
14415
|
const rowStartTime = Date.now();
|
|
14407
14416
|
try {
|
|
14417
|
+
const variables = this.mapRowToVariables(row, mapping, variableTypes, outputStore);
|
|
14408
14418
|
const result = await this.client.executeMutation(mutation, variables, retryConfig, signal);
|
|
14409
14419
|
this.metrics.recordSuccess(entityName);
|
|
14420
|
+
this.captureOutput(row, result, entityName, outputCapture, outputStore);
|
|
14410
14421
|
callbacks?.onRowSuccess?.({
|
|
14411
14422
|
entityName,
|
|
14412
14423
|
rowIndex: i,
|
|
@@ -14435,7 +14446,7 @@ var DataMapper = class {
|
|
|
14435
14446
|
}
|
|
14436
14447
|
}
|
|
14437
14448
|
}
|
|
14438
|
-
async processRowsConcurrentlyWithEvents(data, mutation, mapping, entityName, parallelConfig, retryConfig, signal, callbacks) {
|
|
14449
|
+
async processRowsConcurrentlyWithEvents(data, mutation, mapping, entityName, parallelConfig, retryConfig, signal, callbacks, outputCapture, outputStore) {
|
|
14439
14450
|
const concurrency = parallelConfig.concurrency;
|
|
14440
14451
|
this.logger.info(`Processing ${data.length} rows with concurrency: ${concurrency}`);
|
|
14441
14452
|
const variableTypes = this.extractVariableTypes(mutation);
|
|
@@ -14451,9 +14462,9 @@ var DataMapper = class {
|
|
|
14451
14462
|
const chunkStartIndex = chunkIndex * concurrency;
|
|
14452
14463
|
const promises = chunk.map(async (row, index) => {
|
|
14453
14464
|
const rowIndex = chunkStartIndex + index;
|
|
14454
|
-
const variables = this.mapRowToVariables(row, mapping, variableTypes);
|
|
14455
14465
|
const rowStartTime = Date.now();
|
|
14456
14466
|
try {
|
|
14467
|
+
const variables = this.mapRowToVariables(row, mapping, variableTypes, outputStore);
|
|
14457
14468
|
const result = await this.client.executeMutation(
|
|
14458
14469
|
mutation,
|
|
14459
14470
|
variables,
|
|
@@ -14461,6 +14472,7 @@ var DataMapper = class {
|
|
|
14461
14472
|
signal
|
|
14462
14473
|
);
|
|
14463
14474
|
this.metrics.recordSuccess(entityName);
|
|
14475
|
+
this.captureOutput(row, result, entityName, outputCapture, outputStore);
|
|
14464
14476
|
callbacks?.onRowSuccess?.({
|
|
14465
14477
|
entityName,
|
|
14466
14478
|
rowIndex,
|
|
@@ -14516,13 +14528,13 @@ var DataMapper = class {
|
|
|
14516
14528
|
}
|
|
14517
14529
|
return chunks;
|
|
14518
14530
|
}
|
|
14519
|
-
mapRowToVariables(row, mapping, variableTypes) {
|
|
14531
|
+
mapRowToVariables(row, mapping, variableTypes, outputStore) {
|
|
14520
14532
|
const variables = {};
|
|
14521
14533
|
for (const [graphqlVar, mappingValue] of Object.entries(mapping)) {
|
|
14522
14534
|
if (mappingValue === "$") {
|
|
14523
14535
|
variables[graphqlVar] = row;
|
|
14524
14536
|
} else if (typeof mappingValue === "string" && mappingValue.startsWith("$.")) {
|
|
14525
|
-
const dataPath =
|
|
14537
|
+
const dataPath = this.stripJsonPathPrefix(mappingValue);
|
|
14526
14538
|
const value = this.getValueByPath(row, dataPath);
|
|
14527
14539
|
if (value !== void 0) {
|
|
14528
14540
|
const type = variableTypes[graphqlVar];
|
|
@@ -14532,8 +14544,10 @@ var DataMapper = class {
|
|
|
14532
14544
|
const rawValue = row[mappingValue];
|
|
14533
14545
|
const type = variableTypes[graphqlVar];
|
|
14534
14546
|
variables[graphqlVar] = this.convertValue(rawValue, type, graphqlVar);
|
|
14547
|
+
} else if (this.isRefMapping(mappingValue)) {
|
|
14548
|
+
variables[graphqlVar] = this.resolveRef(row, mappingValue, outputStore);
|
|
14535
14549
|
} else if (typeof mappingValue === "object" && mappingValue !== null) {
|
|
14536
|
-
variables[graphqlVar] = this.mapNestedObject(row, mappingValue, variableTypes);
|
|
14550
|
+
variables[graphqlVar] = this.mapNestedObject(row, mappingValue, variableTypes, outputStore);
|
|
14537
14551
|
}
|
|
14538
14552
|
}
|
|
14539
14553
|
return variables;
|
|
@@ -14550,15 +14564,18 @@ var DataMapper = class {
|
|
|
14550
14564
|
}
|
|
14551
14565
|
return current;
|
|
14552
14566
|
}
|
|
14553
|
-
mapNestedObject(row, mappingObj, variableTypes) {
|
|
14567
|
+
mapNestedObject(row, mappingObj, variableTypes, outputStore) {
|
|
14554
14568
|
if (Array.isArray(mappingObj)) {
|
|
14555
|
-
return mappingObj.map((item) => this.mapNestedObject(row, item, variableTypes));
|
|
14569
|
+
return mappingObj.map((item) => this.mapNestedObject(row, item, variableTypes, outputStore));
|
|
14556
14570
|
}
|
|
14557
14571
|
if (typeof mappingObj === "object" && mappingObj !== null) {
|
|
14572
|
+
if (this.isRefMapping(mappingObj)) {
|
|
14573
|
+
return this.resolveRef(row, mappingObj, outputStore);
|
|
14574
|
+
}
|
|
14558
14575
|
const result = {};
|
|
14559
14576
|
for (const [key, value] of Object.entries(mappingObj)) {
|
|
14560
14577
|
if (typeof value === "string" && value.startsWith("$.")) {
|
|
14561
|
-
const dataPath =
|
|
14578
|
+
const dataPath = this.stripJsonPathPrefix(value);
|
|
14562
14579
|
let fieldValue = this.getValueByPath(row, dataPath);
|
|
14563
14580
|
if (key === "values" && typeof fieldValue === "string" && fieldValue.includes(",")) {
|
|
14564
14581
|
fieldValue = fieldValue.split(",").map((v) => v.trim());
|
|
@@ -14566,8 +14583,10 @@ var DataMapper = class {
|
|
|
14566
14583
|
result[key] = fieldValue;
|
|
14567
14584
|
} else if (typeof value === "string" && row[value] !== void 0) {
|
|
14568
14585
|
result[key] = row[value];
|
|
14586
|
+
} else if (this.isRefMapping(value)) {
|
|
14587
|
+
result[key] = this.resolveRef(row, value, outputStore);
|
|
14569
14588
|
} else if (typeof value === "object") {
|
|
14570
|
-
result[key] = this.mapNestedObject(row, value, variableTypes);
|
|
14589
|
+
result[key] = this.mapNestedObject(row, value, variableTypes, outputStore);
|
|
14571
14590
|
} else {
|
|
14572
14591
|
result[key] = value;
|
|
14573
14592
|
}
|
|
@@ -14576,6 +14595,68 @@ var DataMapper = class {
|
|
|
14576
14595
|
}
|
|
14577
14596
|
return mappingObj;
|
|
14578
14597
|
}
|
|
14598
|
+
stripJsonPathPrefix(jsonPath) {
|
|
14599
|
+
return jsonPath.startsWith("$.") ? jsonPath.substring(2) : jsonPath;
|
|
14600
|
+
}
|
|
14601
|
+
isRefMapping(value) {
|
|
14602
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && "$ref" in value && "key" in value && "field" in value;
|
|
14603
|
+
}
|
|
14604
|
+
resolveRef(row, ref, outputStore) {
|
|
14605
|
+
if (!outputStore) {
|
|
14606
|
+
throw new RefResolutionError(`$ref to "${ref.$ref}" found but no output store is available`);
|
|
14607
|
+
}
|
|
14608
|
+
const keyPath = this.stripJsonPathPrefix(ref.key);
|
|
14609
|
+
const lookupKeyValue = this.getValueByPath(row, keyPath);
|
|
14610
|
+
if (lookupKeyValue === void 0) {
|
|
14611
|
+
throw new RefResolutionError(`$ref lookup key "${ref.key}" not found in current row`);
|
|
14612
|
+
}
|
|
14613
|
+
const keyStr = String(lookupKeyValue);
|
|
14614
|
+
const entityStore = outputStore.get(ref.$ref);
|
|
14615
|
+
if (!entityStore) {
|
|
14616
|
+
throw new RefResolutionError(`$ref entity "${ref.$ref}" not found in output store`);
|
|
14617
|
+
}
|
|
14618
|
+
const captured = entityStore.get(keyStr);
|
|
14619
|
+
if (!captured) {
|
|
14620
|
+
throw new RefResolutionError(
|
|
14621
|
+
`$ref key "${keyStr}" not found in entity "${ref.$ref}" output store`
|
|
14622
|
+
);
|
|
14623
|
+
}
|
|
14624
|
+
const value = captured[ref.field];
|
|
14625
|
+
if (value === void 0) {
|
|
14626
|
+
throw new RefResolutionError(
|
|
14627
|
+
`$ref field "${ref.field}" not found in captured output for "${ref.$ref}[${keyStr}]"`
|
|
14628
|
+
);
|
|
14629
|
+
}
|
|
14630
|
+
return value;
|
|
14631
|
+
}
|
|
14632
|
+
captureOutput(row, result, entityName, outputCapture, outputStore) {
|
|
14633
|
+
if (!outputCapture || !outputStore) return;
|
|
14634
|
+
const keyPath = this.stripJsonPathPrefix(outputCapture.key);
|
|
14635
|
+
const keyValue = this.getValueByPath(row, keyPath);
|
|
14636
|
+
if (keyValue === void 0) {
|
|
14637
|
+
this.logger.warn(
|
|
14638
|
+
`outputCapture key "${outputCapture.key}" not found in input row for entity "${entityName}". Skipping capture.`
|
|
14639
|
+
);
|
|
14640
|
+
return;
|
|
14641
|
+
}
|
|
14642
|
+
const keyStr = String(keyValue);
|
|
14643
|
+
const captured = {};
|
|
14644
|
+
for (const [fieldName, responsePath] of Object.entries(outputCapture.fields)) {
|
|
14645
|
+
const dataPath = this.stripJsonPathPrefix(responsePath);
|
|
14646
|
+
captured[fieldName] = this.getValueByPath(result, dataPath);
|
|
14647
|
+
}
|
|
14648
|
+
let entityStore = outputStore.get(entityName);
|
|
14649
|
+
if (!entityStore) {
|
|
14650
|
+
entityStore = /* @__PURE__ */ new Map();
|
|
14651
|
+
outputStore.set(entityName, entityStore);
|
|
14652
|
+
}
|
|
14653
|
+
if (entityStore.has(keyStr)) {
|
|
14654
|
+
this.logger.warn(
|
|
14655
|
+
`outputCapture: duplicate key "${keyStr}" for entity "${entityName}" \u2014 overwriting previous value`
|
|
14656
|
+
);
|
|
14657
|
+
}
|
|
14658
|
+
entityStore.set(keyStr, captured);
|
|
14659
|
+
}
|
|
14579
14660
|
extractVariableTypes(mutation) {
|
|
14580
14661
|
const types = {};
|
|
14581
14662
|
try {
|
|
@@ -14742,6 +14823,7 @@ var DEFAULT_EVENT_OPTIONS = {
|
|
|
14742
14823
|
var GQLIngest = class extends EventEmitter {
|
|
14743
14824
|
constructor(options) {
|
|
14744
14825
|
super();
|
|
14826
|
+
this.outputStore = /* @__PURE__ */ new Map();
|
|
14745
14827
|
// Cancellation state
|
|
14746
14828
|
this.abortController = null;
|
|
14747
14829
|
this.isProcessing = false;
|
|
@@ -14884,6 +14966,7 @@ var GQLIngest = class extends EventEmitter {
|
|
|
14884
14966
|
return this.handleCancellation(signal.reason);
|
|
14885
14967
|
}
|
|
14886
14968
|
this.metrics = new MetricsCollector();
|
|
14969
|
+
this.outputStore = /* @__PURE__ */ new Map();
|
|
14887
14970
|
this.client = new GraphQLClientWrapper(
|
|
14888
14971
|
this.endpoint,
|
|
14889
14972
|
this.headers,
|
|
@@ -14961,7 +15044,8 @@ var GQLIngest = class extends EventEmitter {
|
|
|
14961
15044
|
this.mapper,
|
|
14962
15045
|
config2,
|
|
14963
15046
|
this.logger,
|
|
14964
|
-
signal
|
|
15047
|
+
signal,
|
|
15048
|
+
this.outputStore
|
|
14965
15049
|
);
|
|
14966
15050
|
if (signal.aborted) {
|
|
14967
15051
|
return this.handleCancellation(signal.reason);
|
|
@@ -15067,7 +15151,7 @@ var GQLIngest = class extends EventEmitter {
|
|
|
15067
15151
|
/**
|
|
15068
15152
|
* Process entities in dependency-aware waves with abort support
|
|
15069
15153
|
*/
|
|
15070
|
-
async processEntitiesInWaves(pathMap, resolver, mapper, config2, logger, signal) {
|
|
15154
|
+
async processEntitiesInWaves(pathMap, resolver, mapper, config2, logger, signal, outputStore) {
|
|
15071
15155
|
const waves = resolver.resolveExecutionOrder();
|
|
15072
15156
|
logger.info(`Processing ${waves.length} dependency waves...`);
|
|
15073
15157
|
for (const wave of waves) {
|
|
@@ -15092,18 +15176,25 @@ var GQLIngest = class extends EventEmitter {
|
|
|
15092
15176
|
try {
|
|
15093
15177
|
const entityConfig = getEntityConfig(entityName, config2, logger);
|
|
15094
15178
|
const retryConfig = getRetryConfig(entityName, config2);
|
|
15095
|
-
await mapper.processEntityWithEvents(
|
|
15096
|
-
|
|
15097
|
-
|
|
15098
|
-
|
|
15099
|
-
|
|
15100
|
-
|
|
15101
|
-
this.
|
|
15102
|
-
|
|
15179
|
+
await mapper.processEntityWithEvents(
|
|
15180
|
+
configPath,
|
|
15181
|
+
entityConfig,
|
|
15182
|
+
retryConfig,
|
|
15183
|
+
signal,
|
|
15184
|
+
{
|
|
15185
|
+
onEntityStart: (payload) => this.safeEmit("entityStart", {
|
|
15186
|
+
...payload,
|
|
15187
|
+
waveIndex: wave.wave
|
|
15188
|
+
}),
|
|
15189
|
+
onEntityComplete: (payload) => {
|
|
15190
|
+
this.entitiesCompleted++;
|
|
15191
|
+
this.safeEmit("entityComplete", payload);
|
|
15192
|
+
},
|
|
15193
|
+
onRowSuccess: this.eventOptions.emitRowEvents ? (payload) => this.safeEmit("rowSuccess", payload) : void 0,
|
|
15194
|
+
onRowFailure: this.eventOptions.emitRowEvents ? (payload) => this.safeEmit("rowFailure", payload) : void 0
|
|
15103
15195
|
},
|
|
15104
|
-
|
|
15105
|
-
|
|
15106
|
-
});
|
|
15196
|
+
outputStore
|
|
15197
|
+
);
|
|
15107
15198
|
} catch (error48) {
|
|
15108
15199
|
const message = error48 instanceof Error ? error48.message : String(error48);
|
|
15109
15200
|
logger.warn(`Warning: Could not process ${configPath}: ${message}`);
|