@jackchuka/gql-ingest 3.1.9 → 4.0.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
@@ -14322,38 +14322,6 @@ var DataMapper = class {
14322
14322
  this.logger = logger;
14323
14323
  this.formatOverride = formatOverride;
14324
14324
  }
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
14325
  /**
14358
14326
  * Process an entity (backward-compatible method)
14359
14327
  */
@@ -14364,22 +14332,18 @@ var DataMapper = class {
14364
14332
  * Process an entity with event callbacks and abort support
14365
14333
  */
14366
14334
  async processEntityWithEvents(configPath, parallelConfig, retryConfig, signal, callbacks) {
14367
- const entityName = path2.basename(configPath, ".json");
14368
14335
  const entityStartTime = Date.now();
14369
- this.logger.info(`Processing entity: ${configPath}`);
14370
- this.metrics.startEntityProcessing(entityName);
14371
- const configFullPath = path2.resolve(this.basePath, configPath);
14336
+ const configFullPath = path.resolve(this.basePath, configPath);
14372
14337
  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);
14338
+ const entityName = config2.name;
14339
+ this.logger.info(`Processing entity: ${entityName}`);
14340
+ this.metrics.startEntityProcessing(entityName);
14341
+ const entityDir = path.dirname(configFullPath);
14342
+ const dataPath = path.resolve(entityDir, config2.dataFile);
14379
14343
  const format = this.formatOverride || config2.dataFormat;
14380
14344
  const reader = DataReaderFactory.getReader(dataPath, format);
14381
14345
  const data = await reader.readFile(dataPath);
14382
- const graphqlPath = path2.resolve(configDir, config2.graphqlFile);
14346
+ const graphqlPath = path.resolve(entityDir, config2.graphqlFile);
14383
14347
  const mutation = fs6.readFileSync(graphqlPath, "utf8");
14384
14348
  callbacks?.onEntityStart?.({
14385
14349
  entityName,
@@ -14558,8 +14522,8 @@ var DataMapper = class {
14558
14522
  if (mappingValue === "$") {
14559
14523
  variables[graphqlVar] = row;
14560
14524
  } else if (typeof mappingValue === "string" && mappingValue.startsWith("$.")) {
14561
- const path3 = mappingValue.substring(2);
14562
- const value = this.getValueByPath(row, path3);
14525
+ const dataPath = mappingValue.substring(2);
14526
+ const value = this.getValueByPath(row, dataPath);
14563
14527
  if (value !== void 0) {
14564
14528
  const type = variableTypes[graphqlVar];
14565
14529
  variables[graphqlVar] = this.convertValue(value, type, graphqlVar);
@@ -14574,8 +14538,8 @@ var DataMapper = class {
14574
14538
  }
14575
14539
  return variables;
14576
14540
  }
14577
- getValueByPath(obj, path3) {
14578
- const parts = path3.split(".");
14541
+ getValueByPath(obj, dataPath) {
14542
+ const parts = dataPath.split(".");
14579
14543
  let current = obj;
14580
14544
  for (const part of parts) {
14581
14545
  if (current && typeof current === "object" && part in current) {
@@ -14594,8 +14558,8 @@ var DataMapper = class {
14594
14558
  const result = {};
14595
14559
  for (const [key, value] of Object.entries(mappingObj)) {
14596
14560
  if (typeof value === "string" && value.startsWith("$.")) {
14597
- const path3 = value.substring(2);
14598
- let fieldValue = this.getValueByPath(row, path3);
14561
+ const dataPath = value.substring(2);
14562
+ let fieldValue = this.getValueByPath(row, dataPath);
14599
14563
  if (key === "values" && typeof fieldValue === "string" && fieldValue.includes(",")) {
14600
14564
  fieldValue = fieldValue.split(",").map((v) => v.trim());
14601
14565
  }
@@ -14764,7 +14728,8 @@ var DependencyResolver = class {
14764
14728
  };
14765
14729
 
14766
14730
  // src/lib/gql-ingest.ts
14767
- import { basename } from "path";
14731
+ import fs7 from "fs";
14732
+ import path2 from "path";
14768
14733
 
14769
14734
  // src/lib/events.ts
14770
14735
  var DEFAULT_EVENT_OPTIONS = {
@@ -14900,12 +14865,12 @@ var GQLIngest = class extends EventEmitter {
14900
14865
  return controller.signal;
14901
14866
  }
14902
14867
  /**
14903
- * Ingest data from a configuration directory
14904
- * @param configPath Path to configuration directory (containing data/, graphql/, mappings/ subdirectories)
14868
+ * Ingest entity files
14869
+ * @param entityFiles Array of paths to entity JSON mapping files
14905
14870
  * @param options Optional ingestion options
14906
14871
  * @returns Promise with ingestion result
14907
14872
  */
14908
- async ingest(configPath, options) {
14873
+ async ingest(entityFiles, options) {
14909
14874
  const errors = [];
14910
14875
  this.abortController = new AbortController();
14911
14876
  const signal = options?.signal ? this.combineSignals(options.signal, this.abortController.signal) : this.abortController.signal;
@@ -14932,19 +14897,9 @@ var GQLIngest = class extends EventEmitter {
14932
14897
  this.logger,
14933
14898
  options?.format ?? this.formatOverride
14934
14899
  );
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}`;
14900
+ const config2 = loadConfig(options?.config, this.logger);
14901
+ if (entityFiles.length === 0) {
14902
+ const warning = "No entity files provided";
14948
14903
  this.logger.warn(warning);
14949
14904
  return {
14950
14905
  metrics: this.metrics.getMetrics(),
@@ -14952,7 +14907,21 @@ var GQLIngest = class extends EventEmitter {
14952
14907
  errors: [warning]
14953
14908
  };
14954
14909
  }
14955
- const entityNames = mappingPaths.map((path3) => basename(path3, ".json"));
14910
+ const entityFilter = options?.entities ? new Set(options.entities) : null;
14911
+ const entityNames = [];
14912
+ const pathMap = /* @__PURE__ */ new Map();
14913
+ for (const file2 of entityFiles) {
14914
+ const fullPath = path2.resolve(process.cwd(), file2);
14915
+ const entityConfig = JSON.parse(fs7.readFileSync(fullPath, "utf8"));
14916
+ if (!entityConfig.name) {
14917
+ throw new Error(`Missing "name" field in entity file: ${file2}`);
14918
+ }
14919
+ if (entityFilter && !entityFilter.has(entityConfig.name)) {
14920
+ continue;
14921
+ }
14922
+ entityNames.push(entityConfig.name);
14923
+ pathMap.set(entityConfig.name, file2);
14924
+ }
14956
14925
  this.totalEntities = entityNames.length;
14957
14926
  const relevantDependencies = {};
14958
14927
  if (config2.entityDependencies) {
@@ -14962,35 +14931,23 @@ var GQLIngest = class extends EventEmitter {
14962
14931
  }
14963
14932
  }
14964
14933
  }
14965
- const resolver = new DependencyResolver(
14966
- entityNames,
14967
- relevantDependencies,
14968
- !!entityFilter
14969
- // Allow partial resolution when using --entities
14970
- );
14934
+ const resolver = new DependencyResolver(entityNames, relevantDependencies, false);
14971
14935
  const validationErrors = resolver.validateDependencies();
14972
14936
  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
- }
14937
+ this.logger.error("Dependency validation errors:");
14938
+ validationErrors.forEach((error48) => {
14939
+ this.logger.error(` - ${error48}`);
14940
+ errors.push(error48);
14941
+ });
14942
+ return {
14943
+ metrics: this.metrics.getMetrics(),
14944
+ success: false,
14945
+ errors
14946
+ };
14989
14947
  }
14990
14948
  const waves = resolver.resolveExecutionOrder();
14991
14949
  this.totalWaves = waves.length;
14992
14950
  const startedPayload = {
14993
- configPath,
14994
14951
  totalEntities: entityNames.length,
14995
14952
  entityNames,
14996
14953
  totalWaves: waves.length,
@@ -14999,7 +14956,7 @@ var GQLIngest = class extends EventEmitter {
14999
14956
  this.safeEmit("started", startedPayload);
15000
14957
  this.startProgressInterval();
15001
14958
  await this.processEntitiesInWaves(
15002
- mappingPaths,
14959
+ pathMap,
15003
14960
  resolver,
15004
14961
  this.mapper,
15005
14962
  config2,
@@ -15049,15 +15006,6 @@ var GQLIngest = class extends EventEmitter {
15049
15006
  this.stopProgressInterval();
15050
15007
  }
15051
15008
  }
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
15009
  /**
15062
15010
  * Get current processing metrics
15063
15011
  * @returns Current metrics
@@ -15119,9 +15067,8 @@ var GQLIngest = class extends EventEmitter {
15119
15067
  /**
15120
15068
  * Process entities in dependency-aware waves with abort support
15121
15069
  */
15122
- async processEntitiesInWaves(mappingPaths, resolver, mapper, config2, logger, signal) {
15070
+ async processEntitiesInWaves(pathMap, resolver, mapper, config2, logger, signal) {
15123
15071
  const waves = resolver.resolveExecutionOrder();
15124
- const pathMap = new Map(mappingPaths.map((path3) => [basename(path3, ".json"), path3]));
15125
15072
  logger.info(`Processing ${waves.length} dependency waves...`);
15126
15073
  for (const wave of waves) {
15127
15074
  if (signal?.aborted) {