@whaly/connector-sdk 0.3.10 → 0.3.11

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.js CHANGED
@@ -8830,7 +8830,7 @@ var require_mime_types = __commonJS({
8830
8830
  "node_modules/mime-types/index.js"(exports2) {
8831
8831
  "use strict";
8832
8832
  var db = require_mime_db();
8833
- var extname2 = require("path").extname;
8833
+ var extname3 = require("path").extname;
8834
8834
  var EXTRACT_TYPE_REGEXP = /^\s*([^;\s]*)(?:;|\s|$)/;
8835
8835
  var TEXT_TYPE_REGEXP = /^text\//i;
8836
8836
  exports2.charset = charset;
@@ -8884,7 +8884,7 @@ var require_mime_types = __commonJS({
8884
8884
  if (!path8 || typeof path8 !== "string") {
8885
8885
  return false;
8886
8886
  }
8887
- var extension2 = extname2("x." + path8).toLowerCase().substr(1);
8887
+ var extension2 = extname3("x." + path8).toLowerCase().substr(1);
8888
8888
  if (!extension2) {
8889
8889
  return false;
8890
8890
  }
@@ -10003,7 +10003,7 @@ var require_form_data = __commonJS({
10003
10003
  var http = require("http");
10004
10004
  var https = require("https");
10005
10005
  var parseUrl = require("url").parse;
10006
- var fs11 = require("fs");
10006
+ var fs12 = require("fs");
10007
10007
  var Stream2 = require("stream").Stream;
10008
10008
  var crypto = require("crypto");
10009
10009
  var mime = require_mime_types();
@@ -10070,7 +10070,7 @@ var require_form_data = __commonJS({
10070
10070
  if (value.end != void 0 && value.end != Infinity && value.start != void 0) {
10071
10071
  callback(null, value.end + 1 - (value.start ? value.start : 0));
10072
10072
  } else {
10073
- fs11.stat(value.path, function(err, stat) {
10073
+ fs12.stat(value.path, function(err, stat) {
10074
10074
  if (err) {
10075
10075
  callback(err);
10076
10076
  return;
@@ -10342,6 +10342,7 @@ __export(index_exports, {
10342
10342
  GCSStateProvider: () => GCSStateProvider,
10343
10343
  ITarget: () => ITarget,
10344
10344
  ImageTransform: () => ImageTransform,
10345
+ LocalStorageService: () => LocalStorageService,
10345
10346
  MIME_TYPES: () => MIME_TYPES,
10346
10347
  MissingFieldInSchemaError: () => MissingFieldInSchemaError,
10347
10348
  MissingSchemaError: () => MissingSchemaError,
@@ -10641,7 +10642,7 @@ var getDownloadFileApiCall = async (endpoint, config, output, privateLogging) =>
10641
10642
  ...config.headers ? { headers: config.headers } : {},
10642
10643
  paramsSerializer: { serialize: (params) => qs.stringify(params, { arrayFormat: "repeat" }) }
10643
10644
  }).then((response) => {
10644
- return new Promise((resolve, reject) => {
10645
+ return new Promise((resolve2, reject) => {
10645
10646
  response.data.pipe(writer);
10646
10647
  let error = null;
10647
10648
  writer.on("error", (err) => {
@@ -10651,7 +10652,7 @@ var getDownloadFileApiCall = async (endpoint, config, output, privateLogging) =>
10651
10652
  });
10652
10653
  writer.on("close", () => {
10653
10654
  if (!error) {
10654
- resolve({ outputDir: output });
10655
+ resolve2({ outputDir: output });
10655
10656
  }
10656
10657
  });
10657
10658
  });
@@ -10802,8 +10803,8 @@ var printMemoryFootprint = (prefix) => {
10802
10803
 
10803
10804
  // src/sdk/service/exit.ts
10804
10805
  async function gracefulExit(exitCode) {
10805
- const loggerFinish = new Promise((resolve, reject) => {
10806
- logger.closeWinston(resolve);
10806
+ const loggerFinish = new Promise((resolve2, reject) => {
10807
+ logger.closeWinston(resolve2);
10807
10808
  });
10808
10809
  await loggerFinish;
10809
10810
  process.exit(exitCode);
@@ -12573,13 +12574,13 @@ var ITarget = class _ITarget {
12573
12574
  const dbSyncInstance = stream.getDbSync();
12574
12575
  const recordWithrenamedColumns = dbSyncInstance.renameColumns(recordWithDecimal);
12575
12576
  const serializedRecord = this.getSerializedRecord(streamId, recordWithrenamedColumns);
12576
- return new Promise((resolve, reject) => {
12577
+ return new Promise((resolve2, reject) => {
12577
12578
  stream.getFileToLoad().stream.write(serializedRecord, (error) => {
12578
12579
  if (error) {
12579
12580
  reject(error);
12580
12581
  }
12581
12582
  stream.incrementBatchedRowCount();
12582
- resolve();
12583
+ resolve2();
12583
12584
  });
12584
12585
  });
12585
12586
  } catch (err) {
@@ -12720,11 +12721,11 @@ var ITarget = class _ITarget {
12720
12721
  const streamState = this.streams[streamId];
12721
12722
  if (!streamState || streamState.getBatchedRowCount() === 0) continue;
12722
12723
  const fileToLoad = streamState.getFileToLoad();
12723
- await new Promise((resolve, reject) => {
12724
+ await new Promise((resolve2, reject) => {
12724
12725
  fileToLoad.stream.end(() => {
12725
12726
  fileToLoad.stream.close((err) => {
12726
12727
  if (err) reject(err);
12727
- else resolve();
12728
+ else resolve2();
12728
12729
  });
12729
12730
  });
12730
12731
  });
@@ -13255,18 +13256,18 @@ async function* rowGeneratorFromCsv(path8, fileConfig) {
13255
13256
  }
13256
13257
  }
13257
13258
  async function countCsvLines(filePath) {
13258
- return new Promise((resolve, reject) => {
13259
+ return new Promise((resolve2, reject) => {
13259
13260
  let newlines = 0;
13260
13261
  (0, import_fs2.createReadStream)(filePath).on("data", (chunk2) => {
13261
13262
  const buf = Buffer.isBuffer(chunk2) ? chunk2 : Buffer.from(chunk2);
13262
13263
  for (let i = 0; i < buf.length; i++) {
13263
13264
  if (buf[i] === 10) newlines++;
13264
13265
  }
13265
- }).on("end", () => resolve(Math.max(0, newlines - 1))).on("error", reject);
13266
+ }).on("end", () => resolve2(Math.max(0, newlines - 1))).on("error", reject);
13266
13267
  });
13267
13268
  }
13268
13269
  var checkCsvHeaderRow = async (path8, fileConfig) => {
13269
- return new Promise((resolve, reject) => {
13270
+ return new Promise((resolve2, reject) => {
13270
13271
  const colsWithParsingConfig = Object.keys(fileConfig.fields).map((key) => key.trim());
13271
13272
  console.debug(`${logPrefix} CSV Expected columns:`, colsWithParsingConfig.map((col) => {
13272
13273
  const info = getStringHexInfo(col);
@@ -13341,7 +13342,7 @@ var checkCsvHeaderRow = async (path8, fileConfig) => {
13341
13342
  reject(new Error(`Error processing CSV file: ${error.message}`));
13342
13343
  });
13343
13344
  initialReadStream.on("close", () => {
13344
- resolve();
13345
+ resolve2();
13345
13346
  });
13346
13347
  });
13347
13348
  };
@@ -13922,6 +13923,16 @@ var CloudStorageService = class {
13922
13923
  throw new Error((0, import_util5.format)(`error writing GCS object gs://${this.bucket.name}/${objectPath}, err: %s`, err?.message));
13923
13924
  }
13924
13925
  }
13926
+ /**
13927
+ * Resolves a file URI to a local file path by downloading it from the configured GCS bucket.
13928
+ *
13929
+ * @example
13930
+ * const storage = new CloudStorageService("my-bucket", "my-folder");
13931
+ * const localPath = await storage.resolveFileUri("folder/file.xlsx");
13932
+ */
13933
+ async resolveFileUri(fileUri) {
13934
+ return this.downloadFile(fileUri, pathModule.basename(fileUri));
13935
+ }
13925
13936
  /**
13926
13937
  * Uploads a local file to the bucket with a unique name based on prefix, streamId, and UUID.
13927
13938
  * Files are stored under `<prefix>/<run-id>/` when the RUN_ID env var is set,
@@ -13950,6 +13961,108 @@ var CloudStorageService = class {
13950
13961
  }
13951
13962
  };
13952
13963
 
13964
+ // src/services/local-storage.ts
13965
+ var pathModule2 = __toESM(require("path"));
13966
+ var fs6 = __toESM(require("fs"));
13967
+ var import_crypto2 = require("crypto");
13968
+ var logPrefix5 = "[LocalStorageService]";
13969
+ var LocalStorageService = class {
13970
+ basePath;
13971
+ processedSuffix;
13972
+ supportedExtensions;
13973
+ constructor(basePath, opts) {
13974
+ this.basePath = pathModule2.resolve(basePath);
13975
+ this.processedSuffix = opts?.processedSuffix ?? ".processed";
13976
+ this.supportedExtensions = opts?.supportedExtensions ?? [];
13977
+ }
13978
+ async listFiles(prefix) {
13979
+ const dir = prefix ? pathModule2.resolve(this.basePath, prefix) : this.basePath;
13980
+ if (!fs6.existsSync(dir)) {
13981
+ return [];
13982
+ }
13983
+ const entries = fs6.readdirSync(dir, { recursive: true });
13984
+ const files = [];
13985
+ for (const entry of entries) {
13986
+ const rel = typeof entry === "string" ? entry : entry.toString();
13987
+ const full = pathModule2.join(dir, rel);
13988
+ if (fs6.statSync(full).isFile()) {
13989
+ files.push(pathModule2.relative(this.basePath, full));
13990
+ }
13991
+ }
13992
+ return files;
13993
+ }
13994
+ async getUnprocessedFiles() {
13995
+ const allFiles = await this.listFiles();
13996
+ const markerFiles = new Set(
13997
+ allFiles.filter((f) => f.endsWith(this.processedSuffix)).map((f) => f.replace(this.processedSuffix, ""))
13998
+ );
13999
+ return allFiles.filter((f) => {
14000
+ if (f.endsWith(this.processedSuffix)) return false;
14001
+ if (this.supportedExtensions.length > 0) {
14002
+ const ext = pathModule2.extname(f).toLowerCase();
14003
+ if (!this.supportedExtensions.includes(ext)) return false;
14004
+ }
14005
+ return !markerFiles.has(f);
14006
+ });
14007
+ }
14008
+ async createMarkerFile(fileName) {
14009
+ const markerPath = pathModule2.join(this.basePath, `${fileName}${this.processedSuffix}`);
14010
+ const dir = pathModule2.dirname(markerPath);
14011
+ if (!fs6.existsSync(dir)) {
14012
+ fs6.mkdirSync(dir, { recursive: true });
14013
+ }
14014
+ fs6.writeFileSync(markerPath, `Marked file ${fileName} as processed`);
14015
+ logger.info(`${logPrefix5} Marker file ${markerPath} created successfully.`);
14016
+ }
14017
+ async downloadFile(filePath, _fileName) {
14018
+ const resolved = pathModule2.resolve(this.basePath, filePath);
14019
+ if (!fs6.existsSync(resolved)) {
14020
+ throw new Error(`Local file not found: ${resolved}`);
14021
+ }
14022
+ logger.info(`${logPrefix5} Resolved local file: ${resolved}`);
14023
+ return resolved;
14024
+ }
14025
+ async resolveFileUri(fileUri) {
14026
+ const resolved = pathModule2.resolve(fileUri);
14027
+ if (!fs6.existsSync(resolved)) {
14028
+ throw new Error(`Local file not found: ${resolved}`);
14029
+ }
14030
+ logger.info(`${logPrefix5} File URI resolved to local path: ${resolved}`);
14031
+ return resolved;
14032
+ }
14033
+ async uploadFile(localPath, destPath) {
14034
+ const dest = pathModule2.resolve(this.basePath, destPath);
14035
+ const dir = pathModule2.dirname(dest);
14036
+ if (!fs6.existsSync(dir)) {
14037
+ fs6.mkdirSync(dir, { recursive: true });
14038
+ }
14039
+ fs6.copyFileSync(localPath, dest);
14040
+ logger.info(`${logPrefix5} Copied '%s' to '%s'`, localPath, dest);
14041
+ return { name: destPath };
14042
+ }
14043
+ async readObjectAsString(objectPath) {
14044
+ const fullPath = pathModule2.resolve(this.basePath, objectPath);
14045
+ if (!fs6.existsSync(fullPath)) {
14046
+ throw new Error(`Local file not found: ${fullPath}`);
14047
+ }
14048
+ return fs6.readFileSync(fullPath, "utf8");
14049
+ }
14050
+ async writeStringObject(objectPath, contents) {
14051
+ const fullPath = pathModule2.resolve(this.basePath, objectPath);
14052
+ const dir = pathModule2.dirname(fullPath);
14053
+ if (!fs6.existsSync(dir)) {
14054
+ fs6.mkdirSync(dir, { recursive: true });
14055
+ }
14056
+ fs6.writeFileSync(fullPath, contents, "utf8");
14057
+ logger.info(`${logPrefix5} Wrote object to ${fullPath}`);
14058
+ }
14059
+ async uploadFileWithUniqueName(filePath, prefix, streamId) {
14060
+ const runFolder = process.env.RUN_ID ?? "default";
14061
+ const destName = `${prefix}/${runFolder}/${streamId}-${(0, import_crypto2.randomUUID)()}.jsonnl`;
14062
+ return this.uploadFile(filePath, destName);
14063
+ }
14064
+ };
14065
+
13953
14066
  // src/services/zip.ts
13954
14067
  var fse = __toESM(require("fs-extra"));
13955
14068
  var import_decompress = __toESM(require("decompress"));
@@ -13962,7 +14075,7 @@ var unzip = async (zipFilePath, extractedPath) => {
13962
14075
  // src/services/cdn.ts
13963
14076
  var import_axios3 = __toESM(require("axios"));
13964
14077
  var import_axios_retry2 = __toESM(require("axios-retry"));
13965
- var logPrefix5 = "[CdnService]";
14078
+ var logPrefix6 = "[CdnService]";
13966
14079
  var MAX_RETRIES = 10;
13967
14080
  var CdnService = class {
13968
14081
  axiosClient;
@@ -13988,12 +14101,12 @@ var CdnService = class {
13988
14101
  if (retryAfter) {
13989
14102
  const seconds = Number(retryAfter);
13990
14103
  if (!isNaN(seconds) && seconds > 0) {
13991
- logger.info(`${logPrefix5} Rate limited \u2014 waiting ${seconds}s (Retry-After header), attempt ${retryCount}/${MAX_RETRIES}`);
14104
+ logger.info(`${logPrefix6} Rate limited \u2014 waiting ${seconds}s (Retry-After header), attempt ${retryCount}/${MAX_RETRIES}`);
13992
14105
  return seconds * 1e3;
13993
14106
  }
13994
14107
  }
13995
14108
  const delay = import_axios_retry2.default.exponentialDelay(retryCount);
13996
- logger.info(`${logPrefix5} Retrying in ${delay}ms (attempt ${retryCount}/${MAX_RETRIES}) for ${error.config?.method?.toUpperCase()} ${error.config?.url}`);
14109
+ logger.info(`${logPrefix6} Retrying in ${delay}ms (attempt ${retryCount}/${MAX_RETRIES}) for ${error.config?.method?.toUpperCase()} ${error.config?.url}`);
13997
14110
  return delay;
13998
14111
  }
13999
14112
  });
@@ -14019,7 +14132,7 @@ var CdnService = class {
14019
14132
  const status = err.response?.status;
14020
14133
  if (status === 404) return { exists: false, lastModified: null, contentType: null };
14021
14134
  if (status === 405) {
14022
- logger.warn(`${logPrefix5} HEAD not supported for ${url}, treating file as absent`);
14135
+ logger.warn(`${logPrefix6} HEAD not supported for ${url}, treating file as absent`);
14023
14136
  return { exists: false, lastModified: null, contentType: null };
14024
14137
  }
14025
14138
  this.throwMeaningfulError(err);
@@ -14043,11 +14156,11 @@ var CdnService = class {
14043
14156
  async uploadFile(cdnId, fileName, fileBuffer) {
14044
14157
  const url = `/v1/cdn/${cdnId}/files/${encodeURIComponent(fileName)}`;
14045
14158
  try {
14046
- logger.info(`${logPrefix5} Uploading ${fileName} (${fileBuffer.length} bytes) to CDN ${cdnId}`);
14159
+ logger.info(`${logPrefix6} Uploading ${fileName} (${fileBuffer.length} bytes) to CDN ${cdnId}`);
14047
14160
  const form = new FormData();
14048
14161
  form.append("file", new Blob([fileBuffer]), fileName);
14049
14162
  const response = await this.axiosClient.put(url, form);
14050
- logger.info(`${logPrefix5} Successfully uploaded ${fileName} (status ${response.status})`);
14163
+ logger.info(`${logPrefix6} Successfully uploaded ${fileName} (status ${response.status})`);
14051
14164
  return {
14052
14165
  filePath: response.data?.filePath ?? `/org/${cdnId}/file/${fileName}`
14053
14166
  };
@@ -14090,7 +14203,7 @@ var AssetStream = class {
14090
14203
  // src/sdk/models/asset-tap/asset-tap.ts
14091
14204
  var import_fs_extra4 = __toESM(require("fs-extra"));
14092
14205
  var import_node_path4 = __toESM(require("path"));
14093
- var logPrefix6 = "[AssetTap]";
14206
+ var logPrefix7 = "[AssetTap]";
14094
14207
  function inferContentType(filePath, fallback) {
14095
14208
  const mime = getMimeType(filePath);
14096
14209
  return mime !== "application/octet-stream" ? mime : fallback;
@@ -14098,7 +14211,7 @@ function inferContentType(filePath, fallback) {
14098
14211
  function deriveManifestMode(streams) {
14099
14212
  const modes = new Set(streams.map((s) => s.replicationMode));
14100
14213
  if (modes.size > 1) {
14101
- logger.warn(`${logPrefix6} Streams have mixed replication modes (${[...modes].join(", ")}). Manifest will report "FULL".`);
14214
+ logger.warn(`${logPrefix7} Streams have mixed replication modes (${[...modes].join(", ")}). Manifest will report "FULL".`);
14102
14215
  return "FULL";
14103
14216
  }
14104
14217
  return streams[0]?.replicationMode ?? "INCREMENTAL";
@@ -14120,13 +14233,13 @@ var AssetTap = class {
14120
14233
  const dryRun = isDryRun();
14121
14234
  const dryRunLimit = dryRun ? getDryRunLimit() : void 0;
14122
14235
  if (dryRun) {
14123
- logger.info(`${logPrefix6} [DRY_RUN] mode active \u2014 skipping CDN checks and uploads`);
14236
+ logger.info(`${logPrefix7} [DRY_RUN] mode active \u2014 skipping CDN checks and uploads`);
14124
14237
  await import_fs_extra4.default.emptyDir(this.outputDir);
14125
14238
  if (dryRunLimit !== void 0) {
14126
- logger.info(`${logPrefix6} [DRY_RUN] Limit: ${dryRunLimit} assets per stream`);
14239
+ logger.info(`${logPrefix7} [DRY_RUN] Limit: ${dryRunLimit} assets per stream`);
14127
14240
  }
14128
14241
  } else if (process.env["DRY_RUN_LIMIT"] !== void 0) {
14129
- logger.warn(`${logPrefix6} DRY_RUN_LIMIT is set but DRY_RUN is not active \u2014 limit will be ignored`);
14242
+ logger.warn(`${logPrefix7} DRY_RUN_LIMIT is set but DRY_RUN is not active \u2014 limit will be ignored`);
14130
14243
  }
14131
14244
  const tmpDir2 = import_node_path4.default.join(this.outputDir, "tmp");
14132
14245
  await import_fs_extra4.default.ensureDir(tmpDir2);
@@ -14134,7 +14247,7 @@ var AssetTap = class {
14134
14247
  let entryIndex = 0;
14135
14248
  let totalSummary = { total: 0, uploaded: 0, skipped: 0, errors: 0 };
14136
14249
  for (const stream of this.streams) {
14137
- logger.info(`${logPrefix6} Processing stream: ${stream.streamId} (mode=${stream.replicationMode}, concurrency=${this.concurrency})`);
14250
+ logger.info(`${logPrefix7} Processing stream: ${stream.streamId} (mode=${stream.replicationMode}, concurrency=${this.concurrency})`);
14138
14251
  const assetEntries = [];
14139
14252
  let streamAssetCount = 0;
14140
14253
  await processFromAsyncIterable(
@@ -14143,11 +14256,11 @@ var AssetTap = class {
14143
14256
  if (dryRunLimit !== void 0 && streamAssetCount >= dryRunLimit) {
14144
14257
  return "stop";
14145
14258
  }
14146
- logger.debug(`${logPrefix6} Processing entry: ${entry.sourcePath}`);
14259
+ logger.debug(`${logPrefix7} Processing entry: ${entry.sourcePath}`);
14147
14260
  if (stream.replicationMode === "INCREMENTAL" && !dryRun) {
14148
14261
  const shouldSync = await this.target.shouldSync(entry);
14149
14262
  if (!shouldSync) {
14150
- logger.debug(`${logPrefix6} Skipping ${entry.sourcePath} (up-to-date)`);
14263
+ logger.debug(`${logPrefix7} Skipping ${entry.sourcePath} (up-to-date)`);
14151
14264
  assetEntries.push({
14152
14265
  sourcePath: entry.sourcePath,
14153
14266
  destinationPath: entry.destinationPath,
@@ -14203,10 +14316,10 @@ var AssetTap = class {
14203
14316
  status: "uploaded",
14204
14317
  transformed: wasTransformed
14205
14318
  });
14206
- logger.info(`${logPrefix6} ${dryRun ? "[DRY_RUN] Processed" : "Uploaded"} ${entry.sourcePath} \u2192 ${entry.destinationPath}`);
14319
+ logger.info(`${logPrefix7} ${dryRun ? "[DRY_RUN] Processed" : "Uploaded"} ${entry.sourcePath} \u2192 ${entry.destinationPath}`);
14207
14320
  } catch (err) {
14208
14321
  const message = err instanceof Error ? err.message : String(err);
14209
- logger.error(`${logPrefix6} Failed to process ${entry.sourcePath}: ${message}`);
14322
+ logger.error(`${logPrefix7} Failed to process ${entry.sourcePath}: ${message}`);
14210
14323
  assetEntries.push({
14211
14324
  sourcePath: entry.sourcePath,
14212
14325
  destinationPath: entry.destinationPath,
@@ -14225,7 +14338,7 @@ var AssetTap = class {
14225
14338
  }
14226
14339
  }
14227
14340
  if (dryRunLimit !== void 0 && streamAssetCount >= dryRunLimit) {
14228
- logger.info(`${logPrefix6} [DRY_RUN] Reached limit of ${dryRunLimit} for stream "${stream.streamId}", stopping.`);
14341
+ logger.info(`${logPrefix7} [DRY_RUN] Reached limit of ${dryRunLimit} for stream "${stream.streamId}", stopping.`);
14229
14342
  return "stop";
14230
14343
  }
14231
14344
  return "continue";
@@ -14261,7 +14374,7 @@ var AssetTap = class {
14261
14374
  if (!dryRun) {
14262
14375
  await this.target.complete();
14263
14376
  }
14264
- logger.info(`${logPrefix6} Sync complete. Uploaded=${totalSummary.uploaded} Skipped=${totalSummary.skipped} Errors=${totalSummary.errors}`);
14377
+ logger.info(`${logPrefix7} Sync complete. Uploaded=${totalSummary.uploaded} Skipped=${totalSummary.skipped} Errors=${totalSummary.errors}`);
14265
14378
  return manifest;
14266
14379
  }
14267
14380
  };
@@ -14299,8 +14412,8 @@ var ImageTransform = class {
14299
14412
  */
14300
14413
  static async toWebp(inputPath, options) {
14301
14414
  const dir = import_node_path5.default.dirname(inputPath);
14302
- const basename = import_node_path5.default.basename(inputPath, import_node_path5.default.extname(inputPath));
14303
- const outputPath = import_node_path5.default.join(dir, `${basename}.webp`);
14415
+ const basename2 = import_node_path5.default.basename(inputPath, import_node_path5.default.extname(inputPath));
14416
+ const outputPath = import_node_path5.default.join(dir, `${basename2}.webp`);
14304
14417
  let pipeline = (0, import_sharp.default)(inputPath);
14305
14418
  const hasSize = options.width !== void 0 && options.height !== void 0;
14306
14419
  if (hasSize) {
@@ -14424,7 +14537,7 @@ async function getFileSizeLabel(filePath) {
14424
14537
  return "unknown size";
14425
14538
  }
14426
14539
  }
14427
- var logPrefix7 = "[DocumentTap]";
14540
+ var logPrefix8 = "[DocumentTap]";
14428
14541
  var DocumentTap = class {
14429
14542
  config;
14430
14543
  outputDir;
@@ -14443,32 +14556,32 @@ var DocumentTap = class {
14443
14556
  }
14444
14557
  async sync() {
14445
14558
  if (!this.target) {
14446
- throw new Error(`${logPrefix7} No target set. Assign a WhalyDocumentTarget before calling sync().`);
14559
+ throw new Error(`${logPrefix8} No target set. Assign a WhalyDocumentTarget before calling sync().`);
14447
14560
  }
14448
14561
  await this.init();
14449
14562
  if (!this.stream) {
14450
- throw new Error(`${logPrefix7} No stream set. Assign this.stream in init().`);
14563
+ throw new Error(`${logPrefix8} No stream set. Assign this.stream in init().`);
14451
14564
  }
14452
14565
  const dryRun = isDryRun();
14453
14566
  const dryRunLimit = dryRun ? getDryRunLimit() : void 0;
14454
14567
  if (dryRun) {
14455
- logger.info(`${logPrefix7} [DRY_RUN] mode active \u2014 skipping API calls and uploads`);
14568
+ logger.info(`${logPrefix8} [DRY_RUN] mode active \u2014 skipping API calls and uploads`);
14456
14569
  await import_fs_extra5.default.emptyDir(this.outputDir);
14457
14570
  if (dryRunLimit !== void 0) {
14458
- logger.info(`${logPrefix7} [DRY_RUN] Limit: ${dryRunLimit} documents per stream`);
14571
+ logger.info(`${logPrefix8} [DRY_RUN] Limit: ${dryRunLimit} documents per stream`);
14459
14572
  }
14460
14573
  } else if (process.env["DRY_RUN_LIMIT"] !== void 0) {
14461
- logger.warn(`${logPrefix7} DRY_RUN_LIMIT is set but DRY_RUN is not active \u2014 limit will be ignored`);
14574
+ logger.warn(`${logPrefix8} DRY_RUN_LIMIT is set but DRY_RUN is not active \u2014 limit will be ignored`);
14462
14575
  }
14463
14576
  const tmpDir2 = import_node_path6.default.join(this.outputDir, "tmp");
14464
14577
  await import_fs_extra5.default.ensureDir(tmpDir2);
14465
14578
  try {
14466
14579
  const stream = this.stream;
14467
- logger.info(`${logPrefix7} Processing stream: ${stream.streamId}`);
14580
+ logger.info(`${logPrefix8} Processing stream: ${stream.streamId}`);
14468
14581
  const sourceEntries = await this.collectSourceEntries(stream, dryRunLimit);
14469
14582
  const existingDocs = dryRun ? [] : await this.target.listExistingDocuments();
14470
14583
  const diff = this.computeDiff(stream, sourceEntries, existingDocs);
14471
- logger.info(`${logPrefix7} Stream ${stream.streamId} diff: create=${diff.toCreate.length} reupload=${diff.toReupload.length} updateMeta=${diff.toUpdateMetadata.length} delete=${diff.toDelete.length} skip=${diff.skipped.length}`);
14584
+ logger.info(`${logPrefix8} Stream ${stream.streamId} diff: create=${diff.toCreate.length} reupload=${diff.toReupload.length} updateMeta=${diff.toUpdateMetadata.length} delete=${diff.toDelete.length} skip=${diff.skipped.length}`);
14472
14585
  const entries = [];
14473
14586
  const createTasks = diff.toCreate.map((entry) => async () => {
14474
14587
  return this.executeCreate(stream, entry, tmpDir2, dryRun);
@@ -14502,7 +14615,7 @@ var DocumentTap = class {
14502
14615
  };
14503
14616
  await import_fs_extra5.default.ensureDir(this.outputDir);
14504
14617
  await import_fs_extra5.default.writeJson(import_node_path6.default.join(this.outputDir, "manifest.json"), manifest, { spaces: 2 });
14505
- logger.info(`${logPrefix7} Sync complete. Created=${streamSummary.created} Updated=${streamSummary.updated} Reuploaded=${streamSummary.reuploaded} Deleted=${streamSummary.deleted} Skipped=${streamSummary.skipped} Errors=${streamSummary.errors}`);
14618
+ logger.info(`${logPrefix8} Sync complete. Created=${streamSummary.created} Updated=${streamSummary.updated} Reuploaded=${streamSummary.reuploaded} Deleted=${streamSummary.deleted} Skipped=${streamSummary.skipped} Errors=${streamSummary.errors}`);
14506
14619
  return manifest;
14507
14620
  } finally {
14508
14621
  await import_fs_extra5.default.remove(tmpDir2).catch(() => void 0);
@@ -14570,12 +14683,12 @@ var DocumentTap = class {
14570
14683
  return { externalId: entry.externalId, fileName: entry.fileName, status: "created" };
14571
14684
  } catch (err) {
14572
14685
  if (err instanceof DocumentDownloadSkipError) {
14573
- logger.warn(`${logPrefix7} Skipped ${entry.externalId} (${entry.fileName}): ${err.message}`);
14686
+ logger.warn(`${logPrefix8} Skipped ${entry.externalId} (${entry.fileName}): ${err.message}`);
14574
14687
  return { externalId: entry.externalId, fileName: entry.fileName, status: "skipped" };
14575
14688
  }
14576
14689
  const message = err instanceof Error ? err.message : String(err);
14577
14690
  const sizeLabel = await getFileSizeLabel(downloadPath);
14578
- logger.error(`${logPrefix7} Failed to create ${entry.externalId} (${entry.fileName}, ${sizeLabel}): ${message}`);
14691
+ logger.error(`${logPrefix8} Failed to create ${entry.externalId} (${entry.fileName}, ${sizeLabel}): ${message}`);
14579
14692
  return { externalId: entry.externalId, fileName: entry.fileName, status: "error", error: message };
14580
14693
  } finally {
14581
14694
  await import_fs_extra5.default.remove(downloadPath).catch(() => void 0);
@@ -14596,12 +14709,12 @@ var DocumentTap = class {
14596
14709
  return { externalId: entry.externalId, fileName: entry.fileName, status: "reuploaded" };
14597
14710
  } catch (err) {
14598
14711
  if (err instanceof DocumentDownloadSkipError) {
14599
- logger.warn(`${logPrefix7} Skipped ${entry.externalId} (${entry.fileName}): ${err.message}`);
14712
+ logger.warn(`${logPrefix8} Skipped ${entry.externalId} (${entry.fileName}): ${err.message}`);
14600
14713
  return { externalId: entry.externalId, fileName: entry.fileName, status: "skipped" };
14601
14714
  }
14602
14715
  const message = err instanceof Error ? err.message : String(err);
14603
14716
  const sizeLabel = await getFileSizeLabel(downloadPath);
14604
- logger.error(`${logPrefix7} Failed to reupload ${entry.externalId} (${entry.fileName}, ${sizeLabel}): ${message}`);
14717
+ logger.error(`${logPrefix8} Failed to reupload ${entry.externalId} (${entry.fileName}, ${sizeLabel}): ${message}`);
14605
14718
  return { externalId: entry.externalId, fileName: entry.fileName, status: "error", error: message };
14606
14719
  } finally {
14607
14720
  await import_fs_extra5.default.remove(downloadPath).catch(() => void 0);
@@ -14615,7 +14728,7 @@ var DocumentTap = class {
14615
14728
  return { externalId: entry.externalId, fileName: entry.fileName, status: "updated" };
14616
14729
  } catch (err) {
14617
14730
  const message = err instanceof Error ? err.message : String(err);
14618
- logger.error(`${logPrefix7} Failed to update metadata for ${entry.externalId}: ${message}`);
14731
+ logger.error(`${logPrefix8} Failed to update metadata for ${entry.externalId}: ${message}`);
14619
14732
  return { externalId: entry.externalId, fileName: entry.fileName, status: "error", error: message };
14620
14733
  }
14621
14734
  }
@@ -14627,7 +14740,7 @@ var DocumentTap = class {
14627
14740
  return { externalId: doc.external_id, fileName: doc.file_name, status: "deleted" };
14628
14741
  } catch (err) {
14629
14742
  const message = err instanceof Error ? err.message : String(err);
14630
- logger.error(`${logPrefix7} Failed to delete ${doc.external_id}: ${message}`);
14743
+ logger.error(`${logPrefix8} Failed to delete ${doc.external_id}: ${message}`);
14631
14744
  return { externalId: doc.external_id, fileName: doc.file_name, status: "error", error: message };
14632
14745
  }
14633
14746
  }
@@ -14664,7 +14777,7 @@ function enrichAxiosError(err) {
14664
14777
  const detail = typeof body === "string" ? body : JSON.stringify(body ?? "");
14665
14778
  return new Error(`${method} ${url} failed with status ${status}: ${detail}`, { cause: err });
14666
14779
  }
14667
- var logPrefix8 = "[WhalyDocumentService]";
14780
+ var logPrefix9 = "[WhalyDocumentService]";
14668
14781
  var MAX_RETRIES2 = 10;
14669
14782
  var WhalyDocumentService = class {
14670
14783
  axiosClient;
@@ -14677,7 +14790,7 @@ var WhalyDocumentService = class {
14677
14790
  const gcsBucketName = process.env["WLY_GCS_BUCKET"];
14678
14791
  if (gcsBucketName) {
14679
14792
  this.gcsBucket = new import_storage2.Storage().bucket(gcsBucketName);
14680
- logger.info(`${logPrefix8} Using direct GCS upload to bucket: ${gcsBucketName}`);
14793
+ logger.info(`${logPrefix9} Using direct GCS upload to bucket: ${gcsBucketName}`);
14681
14794
  }
14682
14795
  this.axiosClient = import_axios4.default.create({
14683
14796
  baseURL: resolvedEndpoint,
@@ -14698,12 +14811,12 @@ var WhalyDocumentService = class {
14698
14811
  if (retryAfter) {
14699
14812
  const seconds = Number(retryAfter);
14700
14813
  if (!isNaN(seconds) && seconds > 0) {
14701
- logger.info(`${logPrefix8} Rate limited \u2014 waiting ${seconds}s, attempt ${retryCount}/${MAX_RETRIES2}`);
14814
+ logger.info(`${logPrefix9} Rate limited \u2014 waiting ${seconds}s, attempt ${retryCount}/${MAX_RETRIES2}`);
14702
14815
  return seconds * 1e3;
14703
14816
  }
14704
14817
  }
14705
14818
  const delay = import_axios_retry3.default.exponentialDelay(retryCount);
14706
- logger.info(`${logPrefix8} Retrying in ${delay}ms (attempt ${retryCount}/${MAX_RETRIES2}) for ${error.config?.method?.toUpperCase()} ${error.config?.url}`);
14819
+ logger.info(`${logPrefix9} Retrying in ${delay}ms (attempt ${retryCount}/${MAX_RETRIES2}) for ${error.config?.method?.toUpperCase()} ${error.config?.url}`);
14707
14820
  return delay;
14708
14821
  }
14709
14822
  });
@@ -14724,7 +14837,7 @@ var WhalyDocumentService = class {
14724
14837
  if (!nextAfter) break;
14725
14838
  after = nextAfter;
14726
14839
  }
14727
- logger.info(`${logPrefix8} Fetched ${documents.length} existing documents from Whaly`);
14840
+ logger.info(`${logPrefix9} Fetched ${documents.length} existing documents from Whaly`);
14728
14841
  return documents;
14729
14842
  }
14730
14843
  /** Upload a file to object storage. Returns storage path and size. */
@@ -14745,8 +14858,8 @@ var WhalyDocumentService = class {
14745
14858
  } catch (err) {
14746
14859
  if (attempt === maxAttempts) throw err;
14747
14860
  const delay = Math.min(1e3 * 2 ** (attempt - 1), 3e4);
14748
- logger.info(`${logPrefix8} GCS upload retry ${attempt}/${MAX_RETRIES2} in ${delay}ms for ${destinationPath}`);
14749
- await new Promise((resolve) => setTimeout(resolve, delay));
14861
+ logger.info(`${logPrefix9} GCS upload retry ${attempt}/${MAX_RETRIES2} in ${delay}ms for ${destinationPath}`);
14862
+ await new Promise((resolve2) => setTimeout(resolve2, delay));
14750
14863
  }
14751
14864
  }
14752
14865
  const stat = await import_node_fs.default.promises.stat(localFilePath);
@@ -14802,7 +14915,7 @@ var WhalyDocumentService = class {
14802
14915
  };
14803
14916
 
14804
14917
  // src/sdk/models/document-target/whaly-document-target.ts
14805
- var logPrefix9 = "[WhalyDocumentTarget]";
14918
+ var logPrefix10 = "[WhalyDocumentTarget]";
14806
14919
  var WhalyDocumentTarget = class {
14807
14920
  config;
14808
14921
  service;
@@ -14821,7 +14934,7 @@ var WhalyDocumentTarget = class {
14821
14934
  async createDocument(streamId, entry, localFilePath) {
14822
14935
  const destinationPath = `${streamId}/${entry.externalId}.${entry.extension}`;
14823
14936
  const fileName = `${entry.externalId}.${entry.extension}`;
14824
- logger.info(`${logPrefix9} Uploading file for new document: ${entry.externalId}`);
14937
+ logger.info(`${logPrefix10} Uploading file for new document: ${entry.externalId}`);
14825
14938
  const uploadResult = await this.service.uploadFile(destinationPath, localFilePath, fileName);
14826
14939
  const stat = await import_fs_extra6.default.stat(localFilePath);
14827
14940
  await this.service.createDocument({
@@ -14838,7 +14951,7 @@ var WhalyDocumentTarget = class {
14838
14951
  storage: uploadResult.storage,
14839
14952
  metadata: entry.metadata ?? {}
14840
14953
  });
14841
- logger.info(`${logPrefix9} Created document: ${entry.externalId} (${entry.fileName})`);
14954
+ logger.info(`${logPrefix10} Created document: ${entry.externalId} (${entry.fileName})`);
14842
14955
  }
14843
14956
  /**
14844
14957
  * Re-upload the file and update the document record.
@@ -14846,7 +14959,7 @@ var WhalyDocumentTarget = class {
14846
14959
  async reuploadDocument(streamId, docId, entry, localFilePath) {
14847
14960
  const destinationPath = `${streamId}/${entry.externalId}.${entry.extension}`;
14848
14961
  const fileName = `${entry.externalId}.${entry.extension}`;
14849
- logger.info(`${logPrefix9} Re-uploading file for document: ${entry.externalId}`);
14962
+ logger.info(`${logPrefix10} Re-uploading file for document: ${entry.externalId}`);
14850
14963
  const uploadResult = await this.service.uploadFile(destinationPath, localFilePath, fileName);
14851
14964
  const stat = await import_fs_extra6.default.stat(localFilePath);
14852
14965
  await this.service.updateDocument(docId, {
@@ -14863,7 +14976,7 @@ var WhalyDocumentTarget = class {
14863
14976
  storage: uploadResult.storage,
14864
14977
  metadata: entry.metadata ?? {}
14865
14978
  });
14866
- logger.info(`${logPrefix9} Re-uploaded document: ${entry.externalId} (${entry.fileName})`);
14979
+ logger.info(`${logPrefix10} Re-uploaded document: ${entry.externalId} (${entry.fileName})`);
14867
14980
  }
14868
14981
  /** Update only the metadata fields (no file re-upload). */
14869
14982
  async updateDocumentMetadata(docId, entry, existingDoc) {
@@ -14881,12 +14994,12 @@ var WhalyDocumentTarget = class {
14881
14994
  storage: existingDoc.storage,
14882
14995
  metadata: entry.metadata ?? {}
14883
14996
  });
14884
- logger.info(`${logPrefix9} Updated metadata for document: ${entry.externalId} (${entry.fileName})`);
14997
+ logger.info(`${logPrefix10} Updated metadata for document: ${entry.externalId} (${entry.fileName})`);
14885
14998
  }
14886
14999
  /** Delete a document record. */
14887
15000
  async deleteDocument(docId, externalId) {
14888
15001
  await this.service.deleteDocument(docId);
14889
- logger.info(`${logPrefix9} Deleted document: ${externalId} (id=${docId})`);
15002
+ logger.info(`${logPrefix10} Deleted document: ${externalId} (id=${docId})`);
14890
15003
  }
14891
15004
  };
14892
15005
 
@@ -15225,7 +15338,7 @@ var BigQueryDBSync = class extends StreamWarehouseSyncService {
15225
15338
  gracefulExit(1);
15226
15339
  } else {
15227
15340
  logger.error(`StreamId: ${this.streamId} - Error when checking if dataset already exists. Retrying... ${err.message} - ${err.code}`);
15228
- await new Promise((resolve, reject) => setTimeout(resolve, 1e3 * retryCount));
15341
+ await new Promise((resolve2, reject) => setTimeout(resolve2, 1e3 * retryCount));
15229
15342
  await this.createDatabaseAndSchemaIfNotExists(retryCount + 1);
15230
15343
  }
15231
15344
  }
@@ -15453,7 +15566,7 @@ var BigQueryTarget = class extends ITarget {
15453
15566
 
15454
15567
  // src/targets/cdn/main.ts
15455
15568
  var import_fs_extra7 = __toESM(require("fs-extra"));
15456
- var logPrefix10 = "[CdnAssetTarget]";
15569
+ var logPrefix11 = "[CdnAssetTarget]";
15457
15570
  var CdnAssetTarget = class extends AssetTarget {
15458
15571
  cdnService;
15459
15572
  constructor(config) {
@@ -15463,20 +15576,20 @@ var CdnAssetTarget = class extends AssetTarget {
15463
15576
  async shouldSync(entry) {
15464
15577
  const metadata = await this.cdnService.getFileMetadata(getCdnId(this.config.cdnId), entry.destinationPath);
15465
15578
  if (!metadata.exists) {
15466
- logger.debug(`${logPrefix10} ${entry.destinationPath} not in CDN \u2192 will sync`);
15579
+ logger.debug(`${logPrefix11} ${entry.destinationPath} not in CDN \u2192 will sync`);
15467
15580
  return true;
15468
15581
  }
15469
15582
  if (entry.lastModified === void 0) {
15470
- logger.debug(`${logPrefix10} ${entry.destinationPath} source has no lastModified \u2192 will sync`);
15583
+ logger.debug(`${logPrefix11} ${entry.destinationPath} source has no lastModified \u2192 will sync`);
15471
15584
  return true;
15472
15585
  }
15473
15586
  if (metadata.lastModified === null) {
15474
- logger.debug(`${logPrefix10} ${entry.destinationPath} CDN has no lastModified \u2192 will sync`);
15587
+ logger.debug(`${logPrefix11} ${entry.destinationPath} CDN has no lastModified \u2192 will sync`);
15475
15588
  return true;
15476
15589
  }
15477
15590
  const sourceIsNewer = entry.lastModified > metadata.lastModified;
15478
15591
  if (!sourceIsNewer) {
15479
- logger.debug(`${logPrefix10} Skipping ${entry.destinationPath} (CDN is up-to-date)`);
15592
+ logger.debug(`${logPrefix11} Skipping ${entry.destinationPath} (CDN is up-to-date)`);
15480
15593
  }
15481
15594
  return sourceIsNewer;
15482
15595
  }
@@ -15487,7 +15600,7 @@ var CdnAssetTarget = class extends AssetTarget {
15487
15600
  asset.entry.destinationPath,
15488
15601
  fileBuffer
15489
15602
  );
15490
- logger.info(`${logPrefix10} Uploaded ${asset.entry.destinationPath} (${fileBuffer.length} bytes)`);
15603
+ logger.info(`${logPrefix11} Uploaded ${asset.entry.destinationPath} (${fileBuffer.length} bytes)`);
15491
15604
  }
15492
15605
  };
15493
15606
 
@@ -15557,6 +15670,7 @@ var GCSStateProvider = class {
15557
15670
  GCSStateProvider,
15558
15671
  ITarget,
15559
15672
  ImageTransform,
15673
+ LocalStorageService,
15560
15674
  MIME_TYPES,
15561
15675
  MissingFieldInSchemaError,
15562
15676
  MissingSchemaError,