@whaly/connector-sdk 0.3.3 → 0.3.5

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/dist/index.d.mts CHANGED
@@ -5,7 +5,7 @@ import { WriteStream } from 'fs';
5
5
  import { WorkBook } from 'xlsx';
6
6
  export { WorkBook } from 'xlsx';
7
7
  import Client from 'ssh2-sftp-client';
8
- export { default as SftpClient, ConnectOptions as SftpConnectOptions } from 'ssh2-sftp-client';
8
+ export { default as SftpClient, ConnectOptions as SftpConnectOptions, FileInfo as SftpFileInfo } from 'ssh2-sftp-client';
9
9
  import { File } from '@google-cloud/storage';
10
10
  import { BigQuery, Dataset, Table } from '@google-cloud/bigquery';
11
11
 
@@ -36,6 +36,33 @@ interface PostConfig {
36
36
  declare const postJSONApiCall: <T>(endpoint: string, config: PostConfig, payload: any) => Promise<T>;
37
37
  declare const postFormDataApiCall: <T>(endpoint: string, config: PostConfig, payload: any) => Promise<T>;
38
38
  declare const postUrlEncodedApiCall: (endpoint: string, config: PostConfig, payload: any) => Promise<any>;
39
+ /**
40
+ * Collect all records from a paginated HTTP endpoint.
41
+ *
42
+ * Eliminates the need for manual `while (!done)` loops in connector code,
43
+ * and avoids the TypeScript TS7022 circular-inference error that occurs when
44
+ * a `string | null` loop variable is reassigned from the response body.
45
+ *
46
+ * @param options.initialUrl - First page URL (always a `string`, never null)
47
+ * @param options.fetchPage - Fetches one page; receives the current URL, returns parsed response
48
+ * @param options.getRecords - Extracts the array of records from a page response
49
+ * @param options.getNextUrl - Extracts the next-page URL, or `undefined` when done
50
+ *
51
+ * @example
52
+ * // Salesforce SOQL pagination
53
+ * const records = await collectPaginated({
54
+ * initialUrl: `/services/data/v61.0/queryAll?q=${encodeURIComponent(soql)}`,
55
+ * fetchPage: (url) => http.get(url, { headers }).then(r => r.data),
56
+ * getRecords: (res) => res.records,
57
+ * getNextUrl: (res) => res.nextRecordsUrl,
58
+ * });
59
+ */
60
+ declare function collectPaginated<TResponse, TRecord>(options: {
61
+ initialUrl: string;
62
+ fetchPage: (url: string) => Promise<TResponse>;
63
+ getRecords: (response: TResponse) => TRecord[];
64
+ getNextUrl: (response: TResponse) => string | undefined;
65
+ }): Promise<TRecord[]>;
39
66
 
40
67
  declare const BATCH_INTERVAL_MS = 100;
41
68
  declare const logger: {
@@ -76,6 +103,51 @@ declare const haltAndCatchFire: (errorType: ErrorType, errorText: string, errorD
76
103
 
77
104
  declare function gracefulExit(exitCode: number): Promise<void>;
78
105
 
106
+ declare function isDryRun(): boolean;
107
+ declare function getDryRunLimit(): number | undefined;
108
+
109
+ declare function getApiEndpoint(explicit?: string): string;
110
+
111
+ declare function getServiceAccountKey(explicit?: string): string;
112
+
113
+ declare function getCdnId(explicit?: string): string;
114
+
115
+ /**
116
+ * Read a required environment variable. Throws if not set or empty.
117
+ * Automatically loads .env file on first call.
118
+ */
119
+ declare function requireEnv(name: string): string;
120
+ /**
121
+ * Read an optional environment variable, returning a default if not set.
122
+ * Automatically loads .env file on first call.
123
+ */
124
+ declare function optionalEnv(name: string, defaultValue: string): string;
125
+
126
+ /**
127
+ * Run an array of async task factories with a bounded concurrency limit.
128
+ * Results are returned in the same order as the input tasks.
129
+ */
130
+ declare function runWithConcurrency<T>(tasks: (() => Promise<T>)[], concurrency: number): Promise<T[]>;
131
+ /**
132
+ * Process items from an async iterable with bounded concurrency.
133
+ *
134
+ * Spawns `concurrency` workers that each pull from the shared iterator.
135
+ * - If `processor` returns `"stop"`, no new items are pulled (in-flight items finish).
136
+ * - If `processor` throws and no `onError` is provided, the error propagates and stops all workers.
137
+ * - If `onError` is provided, errors are swallowed (handled by callback) and processing continues.
138
+ */
139
+ declare function processFromAsyncIterable<T>(iterable: AsyncIterable<T>, processor: (item: T) => Promise<void | "continue" | "stop">, concurrency: number, onError?: (item: T, error: unknown) => void): Promise<void>;
140
+
141
+ /**
142
+ * Known MIME types indexed by lowercase extension (with leading dot).
143
+ */
144
+ declare const MIME_TYPES: Record<string, string>;
145
+ /**
146
+ * Look up the MIME type for a file path or extension.
147
+ * Returns `application/octet-stream` when the extension is unknown.
148
+ */
149
+ declare function getMimeType(filePathOrExt: string): string;
150
+
79
151
  declare enum ReplicationMethod {
80
152
  INCREMENTAL = "INCREMENTAL",
81
153
  FULL_TABLE = "FULL_TABLE",
@@ -193,6 +265,7 @@ interface BaseConfig {
193
265
  database: string;
194
266
  schema: string;
195
267
  syncedAtColumnName?: string;
268
+ syncedAtColumnUseLegacyStringType?: boolean;
196
269
  }
197
270
 
198
271
  interface ColumnMappingStoreUnsafeToSafe {
@@ -305,6 +378,7 @@ interface StateProvider {
305
378
  declare abstract class ITarget<C extends BaseConfig = BaseConfig> {
306
379
  config: C;
307
380
  syncedAtColumnName: string;
381
+ syncedAtColumnUseLegacyStringType: boolean;
308
382
  schemaHooks: TargetSchemaHook[];
309
383
  syncTime: Dayjs;
310
384
  stateProvider: StateProvider;
@@ -828,10 +902,17 @@ declare const checkCsvHeaderRow: (path: string, fileConfig: CsvFileConfig) => Pr
828
902
  * Writes an array of objects directly to a CSV file.
829
903
  */
830
904
  declare const writeDataToCsv: (fileName: string, data: any[]) => Promise<void>;
905
+ interface WriteGeneratorToCsvOptions {
906
+ /** Number of rows per write batch. Defaults to 10 000. */
907
+ batchSize?: number;
908
+ /** Wrap every field in quotes. Defaults to true. */
909
+ alwaysQuote?: boolean;
910
+ }
831
911
  /**
832
- * Writes data from an async generator to CSV with batch writing (10k row batches).
912
+ * Writes data from an async generator to CSV with batch writing.
913
+ * Headers are inferred from the first yielded record.
833
914
  */
834
- declare const writeGeneratorToCSV: (generator: AsyncGenerator, outputFileName: string) => Promise<number>;
915
+ declare const writeGeneratorToCSV: (generator: AsyncGenerator, outputFileName: string, options?: WriteGeneratorToCsvOptions) => Promise<number>;
835
916
 
836
917
  /**
837
918
  * Builder pattern for creating CSV configurations.
@@ -1008,6 +1089,27 @@ declare class FilePatterns {
1008
1089
  * @deprecated Prefer creating your own SftpClient instance for better lifecycle control.
1009
1090
  */
1010
1091
  declare const SftpService: Client;
1092
+ interface ListFilesOptions {
1093
+ /** Recurse into subdirectories. Defaults to true. */
1094
+ recursive?: boolean;
1095
+ /** Only include files whose extension (lowercased, with dot) is in this set. */
1096
+ extensions?: string[];
1097
+ /** Safety limit on the number of directories traversed. Defaults to 1000. */
1098
+ maxIterations?: number;
1099
+ }
1100
+ /**
1101
+ * List files on an SFTP server, optionally recursing into subdirectories.
1102
+ */
1103
+ declare function listFilesRecursively(client: Client, remotePath: string, options?: ListFilesOptions): Promise<Client.FileInfo[]>;
1104
+ interface DownloadFilesOptions {
1105
+ /** Max concurrent downloads. Defaults to 5. */
1106
+ concurrency?: number;
1107
+ }
1108
+ /**
1109
+ * Download a list of remote files to a local directory.
1110
+ * Returns the array of local file paths written.
1111
+ */
1112
+ declare function downloadFiles(client: Client, files: Client.FileInfo[], localDir: string, options?: DownloadFilesOptions): Promise<string[]>;
1011
1113
 
1012
1114
  interface CloudStorageServiceOptions {
1013
1115
  processedSuffix?: string;
@@ -1073,6 +1175,187 @@ declare class CloudStorageService {
1073
1175
  */
1074
1176
  declare const unzip: (zipFilePath: string, extractedPath: string) => Promise<string>;
1075
1177
 
1178
+ interface CdnServiceConfig {
1179
+ /** e.g. "https://org.my.whaly.io" */
1180
+ apiEndpoint?: string;
1181
+ /** e.g. "sk:xxxx" */
1182
+ serviceAccountKey?: string;
1183
+ }
1184
+ interface CdnUploadResult {
1185
+ /** "/org/{cdnId}/file/{fileName}" */
1186
+ filePath: string;
1187
+ }
1188
+ interface CdnFileMetadata {
1189
+ exists: boolean;
1190
+ /** GCS `updated` timestamp, null if file doesn't exist or header absent. */
1191
+ lastModified: Date | null;
1192
+ contentType: string | null;
1193
+ }
1194
+ declare class CdnService {
1195
+ private axiosClient;
1196
+ constructor(config: CdnServiceConfig);
1197
+ /**
1198
+ * Returns metadata for a CDN file via a HEAD request.
1199
+ * If the file does not exist, returns `{ exists: false, lastModified: null, contentType: null }`.
1200
+ * Falls back to not-existing if HEAD is not supported (405).
1201
+ */
1202
+ getFileMetadata(cdnId: string, fileName: string): Promise<CdnFileMetadata>;
1203
+ /**
1204
+ * Checks whether a file already exists in the CDN via a HEAD request.
1205
+ * Returns true if the server responds 2xx, false on 404.
1206
+ * Falls back to false (treat as not existing) if HEAD is not supported (405).
1207
+ * No body is transferred.
1208
+ */
1209
+ fileExists(cdnId: string, fileName: string): Promise<boolean>;
1210
+ /**
1211
+ * Uploads a file buffer to the Whaly CDN.
1212
+ * The PUT is idempotent — uploading the same fileName overwrites the existing file.
1213
+ */
1214
+ uploadFile(cdnId: string, fileName: string, fileBuffer: Buffer): Promise<CdnUploadResult>;
1215
+ private throwMeaningfulError;
1216
+ }
1217
+
1218
+ type AssetReplicationMode = "FULL" | "INCREMENTAL";
1219
+ interface AssetEntry {
1220
+ /** Path/identifier in the source system (e.g. /remote/images/logo.png) */
1221
+ sourcePath: string;
1222
+ /** Path in the destination (e.g. logos/logo.webp) */
1223
+ destinationPath: string;
1224
+ /** Source last-modified timestamp; undefined if unavailable */
1225
+ lastModified: Date | undefined;
1226
+ /** MIME content type */
1227
+ contentType: string;
1228
+ }
1229
+ interface ProcessedAsset {
1230
+ entry: AssetEntry;
1231
+ /** Where the original source file was downloaded (e.g. out/tmp/logo.jpg) */
1232
+ downloadedPath: string;
1233
+ /**
1234
+ * File path ready for upload.
1235
+ * - No transform: same reference as downloadedPath
1236
+ * - Transformed: a new path produced by transformFile()
1237
+ */
1238
+ uploadPath: string;
1239
+ /** True when a transform was applied (downloadedPath !== uploadPath) */
1240
+ wasTransformed: boolean;
1241
+ size: number;
1242
+ contentType: string;
1243
+ }
1244
+ interface AssetManifestEntry {
1245
+ sourcePath: string;
1246
+ destinationPath: string;
1247
+ /** Path in the downloaded/ folder */
1248
+ downloadedPath: string;
1249
+ /** Path in the transformed/ folder (empty string if not transformed) */
1250
+ transformedPath: string;
1251
+ size: number;
1252
+ contentType: string;
1253
+ status: "uploaded" | "skipped" | "error";
1254
+ transformed: boolean;
1255
+ error?: string;
1256
+ }
1257
+ interface StreamManifest {
1258
+ streamId: string;
1259
+ mode: AssetReplicationMode;
1260
+ syncedAt: string;
1261
+ assets: AssetManifestEntry[];
1262
+ summary: {
1263
+ total: number;
1264
+ uploaded: number;
1265
+ skipped: number;
1266
+ errors: number;
1267
+ };
1268
+ }
1269
+ interface AssetManifest {
1270
+ /** ISO 8601 timestamp of when the sync completed */
1271
+ syncedAt: string;
1272
+ mode: AssetReplicationMode;
1273
+ streams: StreamManifest[];
1274
+ summary: {
1275
+ total: number;
1276
+ uploaded: number;
1277
+ skipped: number;
1278
+ errors: number;
1279
+ };
1280
+ }
1281
+
1282
+ declare abstract class AssetStream<C> {
1283
+ readonly streamId: string;
1284
+ readonly config: C;
1285
+ replicationMode: AssetReplicationMode;
1286
+ constructor(streamId: string, config: C, replicationMode?: AssetReplicationMode);
1287
+ abstract listAssets(): AsyncIterable<AssetEntry>;
1288
+ /**
1289
+ * Download a single source entry to `destPath` on local disk.
1290
+ * Override this in concrete streams to pull from SFTP, API, etc.
1291
+ */
1292
+ abstract downloadEntry(entry: AssetEntry, destPath: string): Promise<void>;
1293
+ transformFile(downloadedPath: string, _entry: AssetEntry): Promise<string>;
1294
+ }
1295
+
1296
+ declare abstract class AssetTarget<C> {
1297
+ readonly config: C;
1298
+ constructor(config: C);
1299
+ abstract shouldSync(entry: AssetEntry): Promise<boolean>;
1300
+ abstract uploadAsset(asset: ProcessedAsset): Promise<void>;
1301
+ complete(): Promise<void>;
1302
+ }
1303
+
1304
+ declare abstract class AssetTap<C> {
1305
+ readonly config: C;
1306
+ readonly outputDir: string;
1307
+ readonly streams: AssetStream<unknown>[];
1308
+ readonly concurrency: number;
1309
+ private readonly target;
1310
+ constructor(target: AssetTarget<unknown>, config: C, outputDir?: string, concurrency?: number);
1311
+ /** Register streams. Called once at the start of sync(). */
1312
+ abstract init(): Promise<void>;
1313
+ sync(): Promise<AssetManifest>;
1314
+ }
1315
+
1316
+ interface WebpTransformOptions {
1317
+ /** Target width in pixels */
1318
+ width?: number;
1319
+ /** Target height in pixels */
1320
+ height?: number;
1321
+ /**
1322
+ * Background color for padding when using `extent`.
1323
+ * - "none" or "transparent" → fully transparent
1324
+ * - "white", "black", etc. → named CSS color
1325
+ * - "#rrggbb" or "#rrggbbaa" → hex color
1326
+ */
1327
+ background?: string;
1328
+ /**
1329
+ * How to position the image within the extent canvas.
1330
+ * Maps to sharp's `position` option on `resize`.
1331
+ * Common values: "center" (default), "top", "bottom", "left", "right",
1332
+ * "top left", "top right", "bottom left", "bottom right".
1333
+ */
1334
+ gravity?: string;
1335
+ /**
1336
+ * If true and width/height are set, the output will be padded to
1337
+ * exactly width×height (like ImageMagick's `-extent`).
1338
+ * The image is resized to fit inside the dimensions first,
1339
+ * then padded with the background color to fill the canvas.
1340
+ */
1341
+ extent?: boolean;
1342
+ /** WebP quality (1-100). Defaults to sharp's default (80). */
1343
+ quality?: number;
1344
+ }
1345
+ declare class ImageTransform {
1346
+ /**
1347
+ * Converts an image to WebP format using sharp (libvips).
1348
+ *
1349
+ * The output file is written to the same directory as the input,
1350
+ * with the extension replaced by `.webp`.
1351
+ *
1352
+ * @param inputPath Path to the source image file.
1353
+ * @param options Optional resize / padding parameters.
1354
+ * @returns Path to the produced `.webp` file.
1355
+ */
1356
+ static toWebp(inputPath: string, options: WebpTransformOptions): Promise<string>;
1357
+ }
1358
+
1076
1359
  interface BigQueryConfig extends BaseConfig {
1077
1360
  schema: string;
1078
1361
  project_id: string;
@@ -1132,6 +1415,18 @@ declare class BigQueryTarget extends ITarget<BigQueryConfig> {
1132
1415
  convertNumberIntoDecimal: (record: any, schema: FlattenedSchema) => any;
1133
1416
  }
1134
1417
 
1418
+ interface CdnAssetTargetConfig extends CdnServiceConfig {
1419
+ /** The CDN ID (organization CDN identifier) */
1420
+ cdnId?: string;
1421
+ }
1422
+
1423
+ declare class CdnAssetTarget extends AssetTarget<CdnAssetTargetConfig> {
1424
+ private readonly cdnService;
1425
+ constructor(config: CdnAssetTargetConfig);
1426
+ shouldSync(entry: AssetEntry): Promise<boolean>;
1427
+ uploadAsset(asset: ProcessedAsset): Promise<void>;
1428
+ }
1429
+
1135
1430
  declare class GCSStateProvider implements StateProvider {
1136
1431
  private bucketName;
1137
1432
  private connectorId;
@@ -1142,4 +1437,4 @@ declare class GCSStateProvider implements StateProvider {
1142
1437
  writeState(state: string): Promise<void>;
1143
1438
  }
1144
1439
 
1145
- export { ALL_STREAM_ID_KEYWORD, API_CALLS_METRIC_NAME, Authenticator, BATCH_INTERVAL_MS, type BaseConfig, type BigQueryConfig, BigQueryTarget, type BigqueryLocation, type Bookmark, type Bookmarks, CloudStorageService, type CloudStorageServiceOptions, type ColumnMappingStoreUnsafeToSafe, CounterMetric, CsvExtractionConfigBuilder, type CsvFieldsArrayConfig, type CsvFieldsConfig, type CsvFieldsDictConfig, type CsvFileConfig, type CsvStreamConfig, DEFAULT_MAX_CONCURRENT_STREAMS, DEFAULT_SYNCED_AT_COLUMN, EXECUTION_TIME_METRIC_NAME, type ErrorType, type ExcelCustomExtractorConfig, type ExcelDerivedField, type ExcelExtractionBaseConfig, type ExcelExtractionConfig, ExcelExtractionConfigBuilder, type ExcelFieldMapping, type ExcelFieldSpec, type ExcelSingleSheetExtractionConfig, type ExcelSourceColumn, type ExcelStreamConfig, type FieldType, FileFormat, FilePatterns, FileStream, type FileStreamConfig, type FileStreamEntry, FileTap, type FileTapConfig, type FlattenedSchema, GCSStateProvider, type GetConfig, type HTTPHeaders, type HTTPMethod, ITarget, type InputTapState, type InternalTableField, type JSONSchemaFieldDefinition, type Message, type MessageType, type MetricConfiguration, MissingFieldInSchemaError, MissingSchemaError, type PostConfig, type ProgressMarkers, type PropertiesMetadata, RESTStream, ROWS_SYNCED_METRIC_NAME, RecordMessage, RenameColumnStore, ReplicationMethod, ReplicationMethodMessage, type SafeColumnNameConverterFn, type Schema, SchemaMessage, SchemaValidationError, SftpService, type State, type StateHolder, StateMessage, type StateProvider, StateService, Stream, type StreamId, type StreamState, StreamWarehouseSyncService, type StreamWithTempFile, type SyncOptions, Tap, type TapState, type TargetSchemaHook, type TargetSchemaHookInput, type TempFile, type URLParams, VariableExtractors, type WarehouseTableField, _greaterThan, addWhalyFields, checkCsvHeaderRow, countCsvLines, createCellReference, createCsvStreamConfig, createExcelGenerator, createExcelStreamConfig, createTemporaryFileStream, csvFieldsToJsonSchema, excelColumnToIndex, excelFieldsToJsonSchema, excelRowToIndex, extractExcelRows, extractPrimaryKeysFromCsvConfig, extractPrimaryKeysFromExcelFields, extractStateForStream, fieldTypeToJsonSchema, finalizeStateProgressMarkers, findAllMatchingConfigs, flattenSchema, getAllMetrics, getAxiosInstance, getCounterMetric, getCounterMetrics, getDownloadFileApiCall, getJSONApiCall, getJSONApiCallWithFullResponse, getRawJSONApiCall, gracefulExit, haltAndCatchFire, incrementStreamState, indexToExcelColumn, indexToExcelRow, loadJson, loadSchema, logger, parseCellReference, postFormDataApiCall, postJSONApiCall, postUrlEncodedApiCall, printMemoryFootprint, processFileStreams, removeParasiteProperties, rowGeneratorFromCsv, safePath, unzip, validateExcelConfig, writeDataToCsv, writeGeneratorToCSV };
1440
+ export { ALL_STREAM_ID_KEYWORD, API_CALLS_METRIC_NAME, type AssetEntry, type AssetManifest, type AssetManifestEntry, type AssetReplicationMode, AssetStream, AssetTap, AssetTarget, Authenticator, BATCH_INTERVAL_MS, type BaseConfig, type BigQueryConfig, BigQueryTarget, type BigqueryLocation, type Bookmark, type Bookmarks, CdnAssetTarget, type CdnAssetTargetConfig, type CdnFileMetadata, CdnService, type CdnServiceConfig, type CdnUploadResult, CloudStorageService, type CloudStorageServiceOptions, type ColumnMappingStoreUnsafeToSafe, CounterMetric, CsvExtractionConfigBuilder, type CsvFieldsArrayConfig, type CsvFieldsConfig, type CsvFieldsDictConfig, type CsvFileConfig, type CsvStreamConfig, DEFAULT_MAX_CONCURRENT_STREAMS, DEFAULT_SYNCED_AT_COLUMN, type DownloadFilesOptions, EXECUTION_TIME_METRIC_NAME, type ErrorType, type ExcelCustomExtractorConfig, type ExcelDerivedField, type ExcelExtractionBaseConfig, type ExcelExtractionConfig, ExcelExtractionConfigBuilder, type ExcelFieldMapping, type ExcelFieldSpec, type ExcelSingleSheetExtractionConfig, type ExcelSourceColumn, type ExcelStreamConfig, type FieldType, FileFormat, FilePatterns, FileStream, type FileStreamConfig, type FileStreamEntry, FileTap, type FileTapConfig, type FlattenedSchema, GCSStateProvider, type GetConfig, type HTTPHeaders, type HTTPMethod, ITarget, ImageTransform, type InputTapState, type InternalTableField, type JSONSchemaFieldDefinition, type ListFilesOptions, MIME_TYPES, type Message, type MessageType, type MetricConfiguration, MissingFieldInSchemaError, MissingSchemaError, type PostConfig, type ProcessedAsset, type ProgressMarkers, type PropertiesMetadata, RESTStream, ROWS_SYNCED_METRIC_NAME, RecordMessage, RenameColumnStore, ReplicationMethod, ReplicationMethodMessage, type SafeColumnNameConverterFn, type Schema, SchemaMessage, SchemaValidationError, SftpService, type State, type StateHolder, StateMessage, type StateProvider, StateService, Stream, type StreamId, type StreamManifest, type StreamState, StreamWarehouseSyncService, type StreamWithTempFile, type SyncOptions, Tap, type TapState, type TargetSchemaHook, type TargetSchemaHookInput, type TempFile, type URLParams, VariableExtractors, type WarehouseTableField, type WebpTransformOptions, type WriteGeneratorToCsvOptions, _greaterThan, addWhalyFields, checkCsvHeaderRow, collectPaginated, countCsvLines, createCellReference, createCsvStreamConfig, createExcelGenerator, createExcelStreamConfig, createTemporaryFileStream, csvFieldsToJsonSchema, downloadFiles, excelColumnToIndex, excelFieldsToJsonSchema, excelRowToIndex, extractExcelRows, extractPrimaryKeysFromCsvConfig, extractPrimaryKeysFromExcelFields, extractStateForStream, fieldTypeToJsonSchema, finalizeStateProgressMarkers, findAllMatchingConfigs, flattenSchema, getAllMetrics, getApiEndpoint, getAxiosInstance, getCdnId, getCounterMetric, getCounterMetrics, getDownloadFileApiCall, getDryRunLimit, getJSONApiCall, getJSONApiCallWithFullResponse, getMimeType, getRawJSONApiCall, getServiceAccountKey, gracefulExit, haltAndCatchFire, incrementStreamState, indexToExcelColumn, indexToExcelRow, isDryRun, listFilesRecursively, loadJson, loadSchema, logger, optionalEnv, parseCellReference, postFormDataApiCall, postJSONApiCall, postUrlEncodedApiCall, printMemoryFootprint, processFileStreams, processFromAsyncIterable, removeParasiteProperties, requireEnv, rowGeneratorFromCsv, runWithConcurrency, safePath, unzip, validateExcelConfig, writeDataToCsv, writeGeneratorToCSV };