@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.
@@ -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, logger: Logger): void;
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,CAgCf;AA8DD,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,MAAM,EAAE,MAAM,GAAG,IAAI,CASxE"}
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(configDir, logger = noopLogger) {
13880
- const configPath = path.join(configDir, "config.yaml");
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
- if (!fs.existsSync(configPath)) {
13883
- logger.info("No config.yaml found, using default sequential processing");
13884
- return DEFAULT_CONFIG;
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 errorMessage = error48 instanceof Error ? error48.message : String(error48);
13895
- logger.warn(`Warning: Failed to parse config.yaml: ${errorMessage}. Using defaults.`);
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 path2 from "path";
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.logger.info(`Processing entity: ${configPath}`);
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 configDir = path2.dirname(path2.dirname(configFullPath));
14374
- const dataFile = config2.dataFile || config2.csvFile;
14375
- if (!dataFile) {
14376
- throw new Error(`No data file specified in mapping config: ${configPath}`);
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 = path2.resolve(configDir, config2.graphqlFile);
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 path3 = mappingValue.substring(2);
14562
- const value = this.getValueByPath(row, path3);
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, path3) {
14578
- const parts = path3.split(".");
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 path3 = value.substring(2);
14598
- let fieldValue = this.getValueByPath(row, path3);
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 { basename } from "path";
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 data from a configuration directory
14904
- * @param configPath Path to configuration directory (containing data/, graphql/, mappings/ subdirectories)
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(configPath, options) {
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(configPath, this.logger);
14936
- let entityFilter;
14937
- if (options?.entities) {
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 entityNames = mappingPaths.map((path3) => basename(path3, ".json"));
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
- if (entityFilter) {
14974
- this.logger.warn("\n\u26A0\uFE0F Warning: Dependency validation issues:");
14975
- validationErrors.forEach((error48) => this.logger.warn(` - ${error48}`));
14976
- this.logger.warn("This may cause errors if the dependent data doesn't already exist.\n");
14977
- } else {
14978
- this.logger.error("Dependency validation errors:");
14979
- validationErrors.forEach((error48) => {
14980
- this.logger.error(` - ${error48}`);
14981
- errors.push(error48);
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
- mappingPaths,
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(mappingPaths, resolver, mapper, config2, logger, signal) {
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(configPath, entityConfig, retryConfig, signal, {
15149
- onEntityStart: (payload) => this.safeEmit("entityStart", {
15150
- ...payload,
15151
- waveIndex: wave.wave
15152
- }),
15153
- onEntityComplete: (payload) => {
15154
- this.entitiesCompleted++;
15155
- this.safeEmit("entityComplete", payload);
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
- onRowSuccess: this.eventOptions.emitRowEvents ? (payload) => this.safeEmit("rowSuccess", payload) : void 0,
15158
- onRowFailure: this.eventOptions.emitRowEvents ? (payload) => this.safeEmit("rowFailure", payload) : void 0
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}`);