@jackchuka/gql-ingest 3.1.9 → 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 +120 -71
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/index.js +98 -100
- package/dist/cli/templates/index.d.ts +1 -1
- package/dist/cli/templates/index.d.ts.map +1 -1
- package/dist/index.js +180 -142
- package/dist/index.js.map +3 -3
- package/dist/lib/config.d.ts +1 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/events.d.ts +0 -2
- package/dist/lib/events.d.ts.map +1 -1
- package/dist/lib/gql-ingest.d.ts +8 -12
- package/dist/lib/gql-ingest.d.ts.map +1 -1
- package/dist/lib/mapper.d.ts +15 -4
- package/dist/lib/mapper.d.ts.map +1 -1
- package/package.json +2 -1
|
@@ -25,5 +25,5 @@ export declare function generateConfigYaml(basePath: string, force: boolean, log
|
|
|
25
25
|
export declare function generateEntityFiles(basePath: string, entityName: string, options: EntityTemplateOptions, logger: Logger, force?: boolean): Promise<void>;
|
|
26
26
|
export declare function toPascalCase(str: string): string;
|
|
27
27
|
export declare function validateEntityName(name: string): boolean;
|
|
28
|
-
export declare function ensureDirectories(basePath: string,
|
|
28
|
+
export declare function ensureDirectories(basePath: string, _logger: Logger): void;
|
|
29
29
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/templates/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAG1C,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAI3D,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,UAAU,CAE/D;AAED,eAAO,MAAM,mBAAmB;;;;;;;;;;;;EAKtB,CAAC;AAEX,eAAO,MAAM,mBAAmB,EAAE,UAAkB,CAAC;AAErD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,UAAkB,GACzB,OAAO,CAAC,IAAI,CAAC,CAYf;AA+DD,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAWf;AAED,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,qBAAqB,EAC9B,MAAM,EAAE,MAAM,EACd,KAAK,UAAQ,GACZ,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/templates/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAG1C,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAI3D,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,UAAU,CAE/D;AAED,eAAO,MAAM,mBAAmB;;;;;;;;;;;;EAKtB,CAAC;AAEX,eAAO,MAAM,mBAAmB,EAAE,UAAkB,CAAC;AAErD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,UAAU,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,UAAkB,GACzB,OAAO,CAAC,IAAI,CAAC,CAYf;AA+DD,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAWf;AAED,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,qBAAqB,EAC9B,MAAM,EAAE,MAAM,EACd,KAAK,UAAQ,GACZ,OAAO,CAAC,IAAI,CAAC,CAmCf;AA+DD,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKhD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAIzE"}
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,6 @@ import { GraphQLClient } from "graphql-request";
|
|
|
12
12
|
|
|
13
13
|
// src/lib/config.ts
|
|
14
14
|
import fs from "fs";
|
|
15
|
-
import path from "path";
|
|
16
15
|
import * as yaml from "js-yaml";
|
|
17
16
|
|
|
18
17
|
// src/lib/logger.ts
|
|
@@ -13876,23 +13875,24 @@ var DEFAULT_CONFIG = {
|
|
|
13876
13875
|
entityConfig: {},
|
|
13877
13876
|
entityDependencies: {}
|
|
13878
13877
|
};
|
|
13879
|
-
function loadConfig(
|
|
13880
|
-
|
|
13878
|
+
function loadConfig(configFile, logger = noopLogger) {
|
|
13879
|
+
if (!configFile) {
|
|
13880
|
+
logger.info("No config file provided, using defaults");
|
|
13881
|
+
return DEFAULT_CONFIG;
|
|
13882
|
+
}
|
|
13881
13883
|
try {
|
|
13882
|
-
|
|
13883
|
-
|
|
13884
|
-
|
|
13885
|
-
}
|
|
13886
|
-
const fileContents = fs.readFileSync(configPath, "utf8");
|
|
13887
|
-
const yamlConfig = yaml.load(fileContents);
|
|
13888
|
-
if (yamlConfig === null || typeof yamlConfig !== "object" || Array.isArray(yamlConfig)) {
|
|
13889
|
-
logger.warn("Warning: config.yaml is not a valid object. Using defaults.");
|
|
13890
|
-
return DEFAULT_CONFIG;
|
|
13891
|
-
}
|
|
13884
|
+
const content = fs.readFileSync(configFile, "utf-8");
|
|
13885
|
+
const yamlConfig = yaml.load(content);
|
|
13886
|
+
logger.info(`Loaded config from ${configFile}`);
|
|
13892
13887
|
return mergeWithDefaults(yamlConfig);
|
|
13893
13888
|
} catch (error48) {
|
|
13894
|
-
const
|
|
13895
|
-
|
|
13889
|
+
const isNotFound = error48 instanceof Error && "code" in error48 && // oxlint-disable-next-line typescript-eslint/no-unsafe-type-assertion
|
|
13890
|
+
error48.code === "ENOENT";
|
|
13891
|
+
if (isNotFound) {
|
|
13892
|
+
logger.info(`Config file not found at ${configFile}, using defaults`);
|
|
13893
|
+
} else {
|
|
13894
|
+
logger.warn(`Failed to parse ${configFile}, using defaults`);
|
|
13895
|
+
}
|
|
13896
13896
|
return DEFAULT_CONFIG;
|
|
13897
13897
|
}
|
|
13898
13898
|
}
|
|
@@ -14038,7 +14038,7 @@ var GraphQLClientWrapper = class {
|
|
|
14038
14038
|
|
|
14039
14039
|
// src/lib/mapper.ts
|
|
14040
14040
|
import fs6 from "fs";
|
|
14041
|
-
import
|
|
14041
|
+
import path from "path";
|
|
14042
14042
|
import { parse as parse3, Kind } from "graphql";
|
|
14043
14043
|
|
|
14044
14044
|
// src/readers/data-reader.ts
|
|
@@ -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;
|
|
@@ -14322,38 +14328,6 @@ var DataMapper = class {
|
|
|
14322
14328
|
this.logger = logger;
|
|
14323
14329
|
this.formatOverride = formatOverride;
|
|
14324
14330
|
}
|
|
14325
|
-
discoverMappings(configDir, entityFilter) {
|
|
14326
|
-
const mappingsPath = path2.resolve(this.basePath, configDir, "mappings");
|
|
14327
|
-
try {
|
|
14328
|
-
const files = fs6.readdirSync(mappingsPath);
|
|
14329
|
-
let jsonFiles = files.filter((file2) => file2.endsWith(".json"));
|
|
14330
|
-
if (entityFilter && entityFilter.length > 0) {
|
|
14331
|
-
const requestedEntities = new Set(entityFilter);
|
|
14332
|
-
const foundEntities = /* @__PURE__ */ new Set();
|
|
14333
|
-
jsonFiles = jsonFiles.filter((file2) => {
|
|
14334
|
-
const entityName = path2.basename(file2, ".json");
|
|
14335
|
-
if (requestedEntities.has(entityName)) {
|
|
14336
|
-
foundEntities.add(entityName);
|
|
14337
|
-
return true;
|
|
14338
|
-
}
|
|
14339
|
-
return false;
|
|
14340
|
-
});
|
|
14341
|
-
const notFound = entityFilter.filter((e) => !foundEntities.has(e));
|
|
14342
|
-
if (notFound.length > 0) {
|
|
14343
|
-
this.logger.warn(
|
|
14344
|
-
`Warning: The following entities were not found in mappings: ${notFound.join(", ")}`
|
|
14345
|
-
);
|
|
14346
|
-
}
|
|
14347
|
-
}
|
|
14348
|
-
jsonFiles.sort();
|
|
14349
|
-
this.logger.info(`Discovered ${jsonFiles.length} mapping files: ${jsonFiles.join(", ")}`);
|
|
14350
|
-
return jsonFiles.map((file2) => path2.join(configDir, "mappings", file2));
|
|
14351
|
-
} catch (error48) {
|
|
14352
|
-
const message = error48 instanceof Error ? error48.message : String(error48);
|
|
14353
|
-
this.logger.error(`Error reading mappings directory ${mappingsPath}: ${message}`);
|
|
14354
|
-
return [];
|
|
14355
|
-
}
|
|
14356
|
-
}
|
|
14357
14331
|
/**
|
|
14358
14332
|
* Process an entity (backward-compatible method)
|
|
14359
14333
|
*/
|
|
@@ -14363,23 +14337,19 @@ var DataMapper = class {
|
|
|
14363
14337
|
/**
|
|
14364
14338
|
* Process an entity with event callbacks and abort support
|
|
14365
14339
|
*/
|
|
14366
|
-
async processEntityWithEvents(configPath, parallelConfig, retryConfig, signal, callbacks) {
|
|
14367
|
-
const entityName = path2.basename(configPath, ".json");
|
|
14340
|
+
async processEntityWithEvents(configPath, parallelConfig, retryConfig, signal, callbacks, outputStore) {
|
|
14368
14341
|
const entityStartTime = Date.now();
|
|
14369
|
-
this.
|
|
14370
|
-
this.metrics.startEntityProcessing(entityName);
|
|
14371
|
-
const configFullPath = path2.resolve(this.basePath, configPath);
|
|
14342
|
+
const configFullPath = path.resolve(this.basePath, configPath);
|
|
14372
14343
|
const config2 = JSON.parse(fs6.readFileSync(configFullPath, "utf8"));
|
|
14373
|
-
const
|
|
14374
|
-
|
|
14375
|
-
|
|
14376
|
-
|
|
14377
|
-
|
|
14378
|
-
const dataPath = path2.resolve(configDir, dataFile);
|
|
14344
|
+
const entityName = config2.name;
|
|
14345
|
+
this.logger.info(`Processing entity: ${entityName}`);
|
|
14346
|
+
this.metrics.startEntityProcessing(entityName);
|
|
14347
|
+
const entityDir = path.dirname(configFullPath);
|
|
14348
|
+
const dataPath = path.resolve(entityDir, config2.dataFile);
|
|
14379
14349
|
const format = this.formatOverride || config2.dataFormat;
|
|
14380
14350
|
const reader = DataReaderFactory.getReader(dataPath, format);
|
|
14381
14351
|
const data = await reader.readFile(dataPath);
|
|
14382
|
-
const graphqlPath =
|
|
14352
|
+
const graphqlPath = path.resolve(entityDir, config2.graphqlFile);
|
|
14383
14353
|
const mutation = fs6.readFileSync(graphqlPath, "utf8");
|
|
14384
14354
|
callbacks?.onEntityStart?.({
|
|
14385
14355
|
entityName,
|
|
@@ -14395,7 +14365,9 @@ var DataMapper = class {
|
|
|
14395
14365
|
parallelConfig,
|
|
14396
14366
|
retryConfig,
|
|
14397
14367
|
signal,
|
|
14398
|
-
callbacks
|
|
14368
|
+
callbacks,
|
|
14369
|
+
config2.outputCapture,
|
|
14370
|
+
outputStore
|
|
14399
14371
|
);
|
|
14400
14372
|
} else {
|
|
14401
14373
|
await this.processRowsSequentiallyWithEvents(
|
|
@@ -14405,7 +14377,9 @@ var DataMapper = class {
|
|
|
14405
14377
|
entityName,
|
|
14406
14378
|
retryConfig,
|
|
14407
14379
|
signal,
|
|
14408
|
-
callbacks
|
|
14380
|
+
callbacks,
|
|
14381
|
+
config2.outputCapture,
|
|
14382
|
+
outputStore
|
|
14409
14383
|
);
|
|
14410
14384
|
}
|
|
14411
14385
|
this.metrics.finishEntityProcessing(entityName);
|
|
@@ -14429,7 +14403,7 @@ var DataMapper = class {
|
|
|
14429
14403
|
durationMs: duration3
|
|
14430
14404
|
});
|
|
14431
14405
|
}
|
|
14432
|
-
async processRowsSequentiallyWithEvents(data, mutation, mapping, entityName, retryConfig, signal, callbacks) {
|
|
14406
|
+
async processRowsSequentiallyWithEvents(data, mutation, mapping, entityName, retryConfig, signal, callbacks, outputCapture, outputStore) {
|
|
14433
14407
|
const totalRows = data.length;
|
|
14434
14408
|
const variableTypes = this.extractVariableTypes(mutation);
|
|
14435
14409
|
for (let i = 0; i < data.length; i++) {
|
|
@@ -14438,11 +14412,12 @@ var DataMapper = class {
|
|
|
14438
14412
|
return;
|
|
14439
14413
|
}
|
|
14440
14414
|
const row = data[i];
|
|
14441
|
-
const variables = this.mapRowToVariables(row, mapping, variableTypes);
|
|
14442
14415
|
const rowStartTime = Date.now();
|
|
14443
14416
|
try {
|
|
14417
|
+
const variables = this.mapRowToVariables(row, mapping, variableTypes, outputStore);
|
|
14444
14418
|
const result = await this.client.executeMutation(mutation, variables, retryConfig, signal);
|
|
14445
14419
|
this.metrics.recordSuccess(entityName);
|
|
14420
|
+
this.captureOutput(row, result, entityName, outputCapture, outputStore);
|
|
14446
14421
|
callbacks?.onRowSuccess?.({
|
|
14447
14422
|
entityName,
|
|
14448
14423
|
rowIndex: i,
|
|
@@ -14471,7 +14446,7 @@ var DataMapper = class {
|
|
|
14471
14446
|
}
|
|
14472
14447
|
}
|
|
14473
14448
|
}
|
|
14474
|
-
async processRowsConcurrentlyWithEvents(data, mutation, mapping, entityName, parallelConfig, retryConfig, signal, callbacks) {
|
|
14449
|
+
async processRowsConcurrentlyWithEvents(data, mutation, mapping, entityName, parallelConfig, retryConfig, signal, callbacks, outputCapture, outputStore) {
|
|
14475
14450
|
const concurrency = parallelConfig.concurrency;
|
|
14476
14451
|
this.logger.info(`Processing ${data.length} rows with concurrency: ${concurrency}`);
|
|
14477
14452
|
const variableTypes = this.extractVariableTypes(mutation);
|
|
@@ -14487,9 +14462,9 @@ var DataMapper = class {
|
|
|
14487
14462
|
const chunkStartIndex = chunkIndex * concurrency;
|
|
14488
14463
|
const promises = chunk.map(async (row, index) => {
|
|
14489
14464
|
const rowIndex = chunkStartIndex + index;
|
|
14490
|
-
const variables = this.mapRowToVariables(row, mapping, variableTypes);
|
|
14491
14465
|
const rowStartTime = Date.now();
|
|
14492
14466
|
try {
|
|
14467
|
+
const variables = this.mapRowToVariables(row, mapping, variableTypes, outputStore);
|
|
14493
14468
|
const result = await this.client.executeMutation(
|
|
14494
14469
|
mutation,
|
|
14495
14470
|
variables,
|
|
@@ -14497,6 +14472,7 @@ var DataMapper = class {
|
|
|
14497
14472
|
signal
|
|
14498
14473
|
);
|
|
14499
14474
|
this.metrics.recordSuccess(entityName);
|
|
14475
|
+
this.captureOutput(row, result, entityName, outputCapture, outputStore);
|
|
14500
14476
|
callbacks?.onRowSuccess?.({
|
|
14501
14477
|
entityName,
|
|
14502
14478
|
rowIndex,
|
|
@@ -14552,14 +14528,14 @@ var DataMapper = class {
|
|
|
14552
14528
|
}
|
|
14553
14529
|
return chunks;
|
|
14554
14530
|
}
|
|
14555
|
-
mapRowToVariables(row, mapping, variableTypes) {
|
|
14531
|
+
mapRowToVariables(row, mapping, variableTypes, outputStore) {
|
|
14556
14532
|
const variables = {};
|
|
14557
14533
|
for (const [graphqlVar, mappingValue] of Object.entries(mapping)) {
|
|
14558
14534
|
if (mappingValue === "$") {
|
|
14559
14535
|
variables[graphqlVar] = row;
|
|
14560
14536
|
} else if (typeof mappingValue === "string" && mappingValue.startsWith("$.")) {
|
|
14561
|
-
const
|
|
14562
|
-
const value = this.getValueByPath(row,
|
|
14537
|
+
const dataPath = this.stripJsonPathPrefix(mappingValue);
|
|
14538
|
+
const value = this.getValueByPath(row, dataPath);
|
|
14563
14539
|
if (value !== void 0) {
|
|
14564
14540
|
const type = variableTypes[graphqlVar];
|
|
14565
14541
|
variables[graphqlVar] = this.convertValue(value, type, graphqlVar);
|
|
@@ -14568,14 +14544,16 @@ var DataMapper = class {
|
|
|
14568
14544
|
const rawValue = row[mappingValue];
|
|
14569
14545
|
const type = variableTypes[graphqlVar];
|
|
14570
14546
|
variables[graphqlVar] = this.convertValue(rawValue, type, graphqlVar);
|
|
14547
|
+
} else if (this.isRefMapping(mappingValue)) {
|
|
14548
|
+
variables[graphqlVar] = this.resolveRef(row, mappingValue, outputStore);
|
|
14571
14549
|
} else if (typeof mappingValue === "object" && mappingValue !== null) {
|
|
14572
|
-
variables[graphqlVar] = this.mapNestedObject(row, mappingValue, variableTypes);
|
|
14550
|
+
variables[graphqlVar] = this.mapNestedObject(row, mappingValue, variableTypes, outputStore);
|
|
14573
14551
|
}
|
|
14574
14552
|
}
|
|
14575
14553
|
return variables;
|
|
14576
14554
|
}
|
|
14577
|
-
getValueByPath(obj,
|
|
14578
|
-
const parts =
|
|
14555
|
+
getValueByPath(obj, dataPath) {
|
|
14556
|
+
const parts = dataPath.split(".");
|
|
14579
14557
|
let current = obj;
|
|
14580
14558
|
for (const part of parts) {
|
|
14581
14559
|
if (current && typeof current === "object" && part in current) {
|
|
@@ -14586,24 +14564,29 @@ var DataMapper = class {
|
|
|
14586
14564
|
}
|
|
14587
14565
|
return current;
|
|
14588
14566
|
}
|
|
14589
|
-
mapNestedObject(row, mappingObj, variableTypes) {
|
|
14567
|
+
mapNestedObject(row, mappingObj, variableTypes, outputStore) {
|
|
14590
14568
|
if (Array.isArray(mappingObj)) {
|
|
14591
|
-
return mappingObj.map((item) => this.mapNestedObject(row, item, variableTypes));
|
|
14569
|
+
return mappingObj.map((item) => this.mapNestedObject(row, item, variableTypes, outputStore));
|
|
14592
14570
|
}
|
|
14593
14571
|
if (typeof mappingObj === "object" && mappingObj !== null) {
|
|
14572
|
+
if (this.isRefMapping(mappingObj)) {
|
|
14573
|
+
return this.resolveRef(row, mappingObj, outputStore);
|
|
14574
|
+
}
|
|
14594
14575
|
const result = {};
|
|
14595
14576
|
for (const [key, value] of Object.entries(mappingObj)) {
|
|
14596
14577
|
if (typeof value === "string" && value.startsWith("$.")) {
|
|
14597
|
-
const
|
|
14598
|
-
let fieldValue = this.getValueByPath(row,
|
|
14578
|
+
const dataPath = this.stripJsonPathPrefix(value);
|
|
14579
|
+
let fieldValue = this.getValueByPath(row, dataPath);
|
|
14599
14580
|
if (key === "values" && typeof fieldValue === "string" && fieldValue.includes(",")) {
|
|
14600
14581
|
fieldValue = fieldValue.split(",").map((v) => v.trim());
|
|
14601
14582
|
}
|
|
14602
14583
|
result[key] = fieldValue;
|
|
14603
14584
|
} else if (typeof value === "string" && row[value] !== void 0) {
|
|
14604
14585
|
result[key] = row[value];
|
|
14586
|
+
} else if (this.isRefMapping(value)) {
|
|
14587
|
+
result[key] = this.resolveRef(row, value, outputStore);
|
|
14605
14588
|
} else if (typeof value === "object") {
|
|
14606
|
-
result[key] = this.mapNestedObject(row, value, variableTypes);
|
|
14589
|
+
result[key] = this.mapNestedObject(row, value, variableTypes, outputStore);
|
|
14607
14590
|
} else {
|
|
14608
14591
|
result[key] = value;
|
|
14609
14592
|
}
|
|
@@ -14612,6 +14595,68 @@ var DataMapper = class {
|
|
|
14612
14595
|
}
|
|
14613
14596
|
return mappingObj;
|
|
14614
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
|
+
}
|
|
14615
14660
|
extractVariableTypes(mutation) {
|
|
14616
14661
|
const types = {};
|
|
14617
14662
|
try {
|
|
@@ -14764,7 +14809,8 @@ var DependencyResolver = class {
|
|
|
14764
14809
|
};
|
|
14765
14810
|
|
|
14766
14811
|
// src/lib/gql-ingest.ts
|
|
14767
|
-
import
|
|
14812
|
+
import fs7 from "fs";
|
|
14813
|
+
import path2 from "path";
|
|
14768
14814
|
|
|
14769
14815
|
// src/lib/events.ts
|
|
14770
14816
|
var DEFAULT_EVENT_OPTIONS = {
|
|
@@ -14777,6 +14823,7 @@ var DEFAULT_EVENT_OPTIONS = {
|
|
|
14777
14823
|
var GQLIngest = class extends EventEmitter {
|
|
14778
14824
|
constructor(options) {
|
|
14779
14825
|
super();
|
|
14826
|
+
this.outputStore = /* @__PURE__ */ new Map();
|
|
14780
14827
|
// Cancellation state
|
|
14781
14828
|
this.abortController = null;
|
|
14782
14829
|
this.isProcessing = false;
|
|
@@ -14900,12 +14947,12 @@ var GQLIngest = class extends EventEmitter {
|
|
|
14900
14947
|
return controller.signal;
|
|
14901
14948
|
}
|
|
14902
14949
|
/**
|
|
14903
|
-
* Ingest
|
|
14904
|
-
* @param
|
|
14950
|
+
* Ingest entity files
|
|
14951
|
+
* @param entityFiles Array of paths to entity JSON mapping files
|
|
14905
14952
|
* @param options Optional ingestion options
|
|
14906
14953
|
* @returns Promise with ingestion result
|
|
14907
14954
|
*/
|
|
14908
|
-
async ingest(
|
|
14955
|
+
async ingest(entityFiles, options) {
|
|
14909
14956
|
const errors = [];
|
|
14910
14957
|
this.abortController = new AbortController();
|
|
14911
14958
|
const signal = options?.signal ? this.combineSignals(options.signal, this.abortController.signal) : this.abortController.signal;
|
|
@@ -14919,6 +14966,7 @@ var GQLIngest = class extends EventEmitter {
|
|
|
14919
14966
|
return this.handleCancellation(signal.reason);
|
|
14920
14967
|
}
|
|
14921
14968
|
this.metrics = new MetricsCollector();
|
|
14969
|
+
this.outputStore = /* @__PURE__ */ new Map();
|
|
14922
14970
|
this.client = new GraphQLClientWrapper(
|
|
14923
14971
|
this.endpoint,
|
|
14924
14972
|
this.headers,
|
|
@@ -14932,19 +14980,9 @@ var GQLIngest = class extends EventEmitter {
|
|
|
14932
14980
|
this.logger,
|
|
14933
14981
|
options?.format ?? this.formatOverride
|
|
14934
14982
|
);
|
|
14935
|
-
const config2 = loadConfig(
|
|
14936
|
-
|
|
14937
|
-
|
|
14938
|
-
if (typeof options.entities === "string") {
|
|
14939
|
-
entityFilter = options.entities.split(",").map((e) => e.trim());
|
|
14940
|
-
} else {
|
|
14941
|
-
entityFilter = options.entities;
|
|
14942
|
-
}
|
|
14943
|
-
}
|
|
14944
|
-
const mappingPaths = this.mapper.discoverMappings(configPath, entityFilter);
|
|
14945
|
-
if (mappingPaths.length === 0) {
|
|
14946
|
-
const filterMsg = entityFilter ? ` matching entities: ${entityFilter.join(", ")}` : "";
|
|
14947
|
-
const warning = `No mapping files found in ${configPath}/mappings${filterMsg}`;
|
|
14983
|
+
const config2 = loadConfig(options?.config, this.logger);
|
|
14984
|
+
if (entityFiles.length === 0) {
|
|
14985
|
+
const warning = "No entity files provided";
|
|
14948
14986
|
this.logger.warn(warning);
|
|
14949
14987
|
return {
|
|
14950
14988
|
metrics: this.metrics.getMetrics(),
|
|
@@ -14952,7 +14990,21 @@ var GQLIngest = class extends EventEmitter {
|
|
|
14952
14990
|
errors: [warning]
|
|
14953
14991
|
};
|
|
14954
14992
|
}
|
|
14955
|
-
const
|
|
14993
|
+
const entityFilter = options?.entities ? new Set(options.entities) : null;
|
|
14994
|
+
const entityNames = [];
|
|
14995
|
+
const pathMap = /* @__PURE__ */ new Map();
|
|
14996
|
+
for (const file2 of entityFiles) {
|
|
14997
|
+
const fullPath = path2.resolve(process.cwd(), file2);
|
|
14998
|
+
const entityConfig = JSON.parse(fs7.readFileSync(fullPath, "utf8"));
|
|
14999
|
+
if (!entityConfig.name) {
|
|
15000
|
+
throw new Error(`Missing "name" field in entity file: ${file2}`);
|
|
15001
|
+
}
|
|
15002
|
+
if (entityFilter && !entityFilter.has(entityConfig.name)) {
|
|
15003
|
+
continue;
|
|
15004
|
+
}
|
|
15005
|
+
entityNames.push(entityConfig.name);
|
|
15006
|
+
pathMap.set(entityConfig.name, file2);
|
|
15007
|
+
}
|
|
14956
15008
|
this.totalEntities = entityNames.length;
|
|
14957
15009
|
const relevantDependencies = {};
|
|
14958
15010
|
if (config2.entityDependencies) {
|
|
@@ -14962,35 +15014,23 @@ var GQLIngest = class extends EventEmitter {
|
|
|
14962
15014
|
}
|
|
14963
15015
|
}
|
|
14964
15016
|
}
|
|
14965
|
-
const resolver = new DependencyResolver(
|
|
14966
|
-
entityNames,
|
|
14967
|
-
relevantDependencies,
|
|
14968
|
-
!!entityFilter
|
|
14969
|
-
// Allow partial resolution when using --entities
|
|
14970
|
-
);
|
|
15017
|
+
const resolver = new DependencyResolver(entityNames, relevantDependencies, false);
|
|
14971
15018
|
const validationErrors = resolver.validateDependencies();
|
|
14972
15019
|
if (validationErrors.length > 0) {
|
|
14973
|
-
|
|
14974
|
-
|
|
14975
|
-
|
|
14976
|
-
|
|
14977
|
-
}
|
|
14978
|
-
|
|
14979
|
-
|
|
14980
|
-
|
|
14981
|
-
|
|
14982
|
-
|
|
14983
|
-
return {
|
|
14984
|
-
metrics: this.metrics.getMetrics(),
|
|
14985
|
-
success: false,
|
|
14986
|
-
errors
|
|
14987
|
-
};
|
|
14988
|
-
}
|
|
15020
|
+
this.logger.error("Dependency validation errors:");
|
|
15021
|
+
validationErrors.forEach((error48) => {
|
|
15022
|
+
this.logger.error(` - ${error48}`);
|
|
15023
|
+
errors.push(error48);
|
|
15024
|
+
});
|
|
15025
|
+
return {
|
|
15026
|
+
metrics: this.metrics.getMetrics(),
|
|
15027
|
+
success: false,
|
|
15028
|
+
errors
|
|
15029
|
+
};
|
|
14989
15030
|
}
|
|
14990
15031
|
const waves = resolver.resolveExecutionOrder();
|
|
14991
15032
|
this.totalWaves = waves.length;
|
|
14992
15033
|
const startedPayload = {
|
|
14993
|
-
configPath,
|
|
14994
15034
|
totalEntities: entityNames.length,
|
|
14995
15035
|
entityNames,
|
|
14996
15036
|
totalWaves: waves.length,
|
|
@@ -14999,12 +15039,13 @@ var GQLIngest = class extends EventEmitter {
|
|
|
14999
15039
|
this.safeEmit("started", startedPayload);
|
|
15000
15040
|
this.startProgressInterval();
|
|
15001
15041
|
await this.processEntitiesInWaves(
|
|
15002
|
-
|
|
15042
|
+
pathMap,
|
|
15003
15043
|
resolver,
|
|
15004
15044
|
this.mapper,
|
|
15005
15045
|
config2,
|
|
15006
15046
|
this.logger,
|
|
15007
|
-
signal
|
|
15047
|
+
signal,
|
|
15048
|
+
this.outputStore
|
|
15008
15049
|
);
|
|
15009
15050
|
if (signal.aborted) {
|
|
15010
15051
|
return this.handleCancellation(signal.reason);
|
|
@@ -15049,15 +15090,6 @@ var GQLIngest = class extends EventEmitter {
|
|
|
15049
15090
|
this.stopProgressInterval();
|
|
15050
15091
|
}
|
|
15051
15092
|
}
|
|
15052
|
-
/**
|
|
15053
|
-
* Ingest specific entities from a configuration directory
|
|
15054
|
-
* @param configPath Path to configuration directory
|
|
15055
|
-
* @param entities Array of entity names to process
|
|
15056
|
-
* @returns Promise with ingestion result
|
|
15057
|
-
*/
|
|
15058
|
-
async ingestEntities(configPath, entities) {
|
|
15059
|
-
return this.ingest(configPath, { entities });
|
|
15060
|
-
}
|
|
15061
15093
|
/**
|
|
15062
15094
|
* Get current processing metrics
|
|
15063
15095
|
* @returns Current metrics
|
|
@@ -15119,9 +15151,8 @@ var GQLIngest = class extends EventEmitter {
|
|
|
15119
15151
|
/**
|
|
15120
15152
|
* Process entities in dependency-aware waves with abort support
|
|
15121
15153
|
*/
|
|
15122
|
-
async processEntitiesInWaves(
|
|
15154
|
+
async processEntitiesInWaves(pathMap, resolver, mapper, config2, logger, signal, outputStore) {
|
|
15123
15155
|
const waves = resolver.resolveExecutionOrder();
|
|
15124
|
-
const pathMap = new Map(mappingPaths.map((path3) => [basename(path3, ".json"), path3]));
|
|
15125
15156
|
logger.info(`Processing ${waves.length} dependency waves...`);
|
|
15126
15157
|
for (const wave of waves) {
|
|
15127
15158
|
if (signal?.aborted) {
|
|
@@ -15145,18 +15176,25 @@ var GQLIngest = class extends EventEmitter {
|
|
|
15145
15176
|
try {
|
|
15146
15177
|
const entityConfig = getEntityConfig(entityName, config2, logger);
|
|
15147
15178
|
const retryConfig = getRetryConfig(entityName, config2);
|
|
15148
|
-
await mapper.processEntityWithEvents(
|
|
15149
|
-
|
|
15150
|
-
|
|
15151
|
-
|
|
15152
|
-
|
|
15153
|
-
|
|
15154
|
-
this.
|
|
15155
|
-
|
|
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
|
|
15156
15195
|
},
|
|
15157
|
-
|
|
15158
|
-
|
|
15159
|
-
});
|
|
15196
|
+
outputStore
|
|
15197
|
+
);
|
|
15160
15198
|
} catch (error48) {
|
|
15161
15199
|
const message = error48 instanceof Error ? error48.message : String(error48);
|
|
15162
15200
|
logger.warn(`Warning: Could not process ${configPath}: ${message}`);
|