@whaly/connector-sdk 0.3.10 → 0.3.12

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,20 +14777,22 @@ 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;
14671
14784
  objectStorageId;
14785
+ documentSourceId;
14672
14786
  gcsBucket;
14673
14787
  constructor(config) {
14674
14788
  const resolvedKey = getServiceAccountKey(config.serviceAccountKey);
14675
14789
  const resolvedEndpoint = getApiEndpoint(config.apiEndpoint);
14676
14790
  this.objectStorageId = config.objectStorageId;
14791
+ this.documentSourceId = config.documentSourceId;
14677
14792
  const gcsBucketName = process.env["WLY_GCS_BUCKET"];
14678
14793
  if (gcsBucketName) {
14679
14794
  this.gcsBucket = new import_storage2.Storage().bucket(gcsBucketName);
14680
- logger.info(`${logPrefix8} Using direct GCS upload to bucket: ${gcsBucketName}`);
14795
+ logger.info(`${logPrefix9} Using direct GCS upload to bucket: ${gcsBucketName}`);
14681
14796
  }
14682
14797
  this.axiosClient = import_axios4.default.create({
14683
14798
  baseURL: resolvedEndpoint,
@@ -14698,22 +14813,26 @@ var WhalyDocumentService = class {
14698
14813
  if (retryAfter) {
14699
14814
  const seconds = Number(retryAfter);
14700
14815
  if (!isNaN(seconds) && seconds > 0) {
14701
- logger.info(`${logPrefix8} Rate limited \u2014 waiting ${seconds}s, attempt ${retryCount}/${MAX_RETRIES2}`);
14816
+ logger.info(`${logPrefix9} Rate limited \u2014 waiting ${seconds}s, attempt ${retryCount}/${MAX_RETRIES2}`);
14702
14817
  return seconds * 1e3;
14703
14818
  }
14704
14819
  }
14705
14820
  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}`);
14821
+ logger.info(`${logPrefix9} Retrying in ${delay}ms (attempt ${retryCount}/${MAX_RETRIES2}) for ${error.config?.method?.toUpperCase()} ${error.config?.url}`);
14707
14822
  return delay;
14708
14823
  }
14709
14824
  });
14710
14825
  }
14711
- /** Fetch all documents from the API, handling cursor-based pagination. */
14826
+ /**
14827
+ * Fetch all documents belonging to the configured document source,
14828
+ * handling cursor-based pagination. The `document_source_id` server-side
14829
+ * filter scopes the result to this connector's source only.
14830
+ */
14712
14831
  async listAllDocuments() {
14713
14832
  const documents = [];
14714
14833
  let after;
14715
14834
  while (true) {
14716
- const params = {};
14835
+ const params = { document_source_id: this.documentSourceId };
14717
14836
  if (after) params["after"] = after;
14718
14837
  const response = await this.axiosClient.get(
14719
14838
  "/v1/documents",
@@ -14724,7 +14843,7 @@ var WhalyDocumentService = class {
14724
14843
  if (!nextAfter) break;
14725
14844
  after = nextAfter;
14726
14845
  }
14727
- logger.info(`${logPrefix8} Fetched ${documents.length} existing documents from Whaly`);
14846
+ logger.info(`${logPrefix9} Fetched ${documents.length} existing documents from Whaly`);
14728
14847
  return documents;
14729
14848
  }
14730
14849
  /** Upload a file to object storage. Returns storage path and size. */
@@ -14745,8 +14864,8 @@ var WhalyDocumentService = class {
14745
14864
  } catch (err) {
14746
14865
  if (attempt === maxAttempts) throw err;
14747
14866
  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));
14867
+ logger.info(`${logPrefix9} GCS upload retry ${attempt}/${MAX_RETRIES2} in ${delay}ms for ${destinationPath}`);
14868
+ await new Promise((resolve2) => setTimeout(resolve2, delay));
14750
14869
  }
14751
14870
  }
14752
14871
  const stat = await import_node_fs.default.promises.stat(localFilePath);
@@ -14773,19 +14892,26 @@ var WhalyDocumentService = class {
14773
14892
  throw enrichAxiosError(err);
14774
14893
  }
14775
14894
  }
14776
- /** Create a new document record. */
14895
+ /** Create a new document record, attached to the configured document source. */
14777
14896
  async createDocument(payload) {
14778
14897
  try {
14779
- const response = await this.axiosClient.post("/v1/documents", payload);
14898
+ const response = await this.axiosClient.post("/v1/documents", {
14899
+ ...payload,
14900
+ document_source_id: this.documentSourceId
14901
+ });
14780
14902
  return response.data.data ?? response.data;
14781
14903
  } catch (err) {
14782
14904
  throw enrichAxiosError(err);
14783
14905
  }
14784
14906
  }
14785
- /** Update an existing document record. */
14907
+ /** Update an existing document record, keeping it in the configured document source. */
14786
14908
  async updateDocument(id, payload) {
14787
14909
  try {
14788
- const response = await this.axiosClient.put(`/v1/documents/${id}`, { id, ...payload });
14910
+ const response = await this.axiosClient.put(`/v1/documents/${id}`, {
14911
+ id,
14912
+ ...payload,
14913
+ document_source_id: this.documentSourceId
14914
+ });
14789
14915
  return response.data.data ?? response.data;
14790
14916
  } catch (err) {
14791
14917
  throw enrichAxiosError(err);
@@ -14802,7 +14928,7 @@ var WhalyDocumentService = class {
14802
14928
  };
14803
14929
 
14804
14930
  // src/sdk/models/document-target/whaly-document-target.ts
14805
- var logPrefix9 = "[WhalyDocumentTarget]";
14931
+ var logPrefix10 = "[WhalyDocumentTarget]";
14806
14932
  var WhalyDocumentTarget = class {
14807
14933
  config;
14808
14934
  service;
@@ -14821,7 +14947,7 @@ var WhalyDocumentTarget = class {
14821
14947
  async createDocument(streamId, entry, localFilePath) {
14822
14948
  const destinationPath = `${streamId}/${entry.externalId}.${entry.extension}`;
14823
14949
  const fileName = `${entry.externalId}.${entry.extension}`;
14824
- logger.info(`${logPrefix9} Uploading file for new document: ${entry.externalId}`);
14950
+ logger.info(`${logPrefix10} Uploading file for new document: ${entry.externalId}`);
14825
14951
  const uploadResult = await this.service.uploadFile(destinationPath, localFilePath, fileName);
14826
14952
  const stat = await import_fs_extra6.default.stat(localFilePath);
14827
14953
  await this.service.createDocument({
@@ -14838,7 +14964,7 @@ var WhalyDocumentTarget = class {
14838
14964
  storage: uploadResult.storage,
14839
14965
  metadata: entry.metadata ?? {}
14840
14966
  });
14841
- logger.info(`${logPrefix9} Created document: ${entry.externalId} (${entry.fileName})`);
14967
+ logger.info(`${logPrefix10} Created document: ${entry.externalId} (${entry.fileName})`);
14842
14968
  }
14843
14969
  /**
14844
14970
  * Re-upload the file and update the document record.
@@ -14846,7 +14972,7 @@ var WhalyDocumentTarget = class {
14846
14972
  async reuploadDocument(streamId, docId, entry, localFilePath) {
14847
14973
  const destinationPath = `${streamId}/${entry.externalId}.${entry.extension}`;
14848
14974
  const fileName = `${entry.externalId}.${entry.extension}`;
14849
- logger.info(`${logPrefix9} Re-uploading file for document: ${entry.externalId}`);
14975
+ logger.info(`${logPrefix10} Re-uploading file for document: ${entry.externalId}`);
14850
14976
  const uploadResult = await this.service.uploadFile(destinationPath, localFilePath, fileName);
14851
14977
  const stat = await import_fs_extra6.default.stat(localFilePath);
14852
14978
  await this.service.updateDocument(docId, {
@@ -14863,7 +14989,7 @@ var WhalyDocumentTarget = class {
14863
14989
  storage: uploadResult.storage,
14864
14990
  metadata: entry.metadata ?? {}
14865
14991
  });
14866
- logger.info(`${logPrefix9} Re-uploaded document: ${entry.externalId} (${entry.fileName})`);
14992
+ logger.info(`${logPrefix10} Re-uploaded document: ${entry.externalId} (${entry.fileName})`);
14867
14993
  }
14868
14994
  /** Update only the metadata fields (no file re-upload). */
14869
14995
  async updateDocumentMetadata(docId, entry, existingDoc) {
@@ -14881,12 +15007,12 @@ var WhalyDocumentTarget = class {
14881
15007
  storage: existingDoc.storage,
14882
15008
  metadata: entry.metadata ?? {}
14883
15009
  });
14884
- logger.info(`${logPrefix9} Updated metadata for document: ${entry.externalId} (${entry.fileName})`);
15010
+ logger.info(`${logPrefix10} Updated metadata for document: ${entry.externalId} (${entry.fileName})`);
14885
15011
  }
14886
15012
  /** Delete a document record. */
14887
15013
  async deleteDocument(docId, externalId) {
14888
15014
  await this.service.deleteDocument(docId);
14889
- logger.info(`${logPrefix9} Deleted document: ${externalId} (id=${docId})`);
15015
+ logger.info(`${logPrefix10} Deleted document: ${externalId} (id=${docId})`);
14890
15016
  }
14891
15017
  };
14892
15018
 
@@ -15225,7 +15351,7 @@ var BigQueryDBSync = class extends StreamWarehouseSyncService {
15225
15351
  gracefulExit(1);
15226
15352
  } else {
15227
15353
  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));
15354
+ await new Promise((resolve2, reject) => setTimeout(resolve2, 1e3 * retryCount));
15229
15355
  await this.createDatabaseAndSchemaIfNotExists(retryCount + 1);
15230
15356
  }
15231
15357
  }
@@ -15453,7 +15579,7 @@ var BigQueryTarget = class extends ITarget {
15453
15579
 
15454
15580
  // src/targets/cdn/main.ts
15455
15581
  var import_fs_extra7 = __toESM(require("fs-extra"));
15456
- var logPrefix10 = "[CdnAssetTarget]";
15582
+ var logPrefix11 = "[CdnAssetTarget]";
15457
15583
  var CdnAssetTarget = class extends AssetTarget {
15458
15584
  cdnService;
15459
15585
  constructor(config) {
@@ -15463,20 +15589,20 @@ var CdnAssetTarget = class extends AssetTarget {
15463
15589
  async shouldSync(entry) {
15464
15590
  const metadata = await this.cdnService.getFileMetadata(getCdnId(this.config.cdnId), entry.destinationPath);
15465
15591
  if (!metadata.exists) {
15466
- logger.debug(`${logPrefix10} ${entry.destinationPath} not in CDN \u2192 will sync`);
15592
+ logger.debug(`${logPrefix11} ${entry.destinationPath} not in CDN \u2192 will sync`);
15467
15593
  return true;
15468
15594
  }
15469
15595
  if (entry.lastModified === void 0) {
15470
- logger.debug(`${logPrefix10} ${entry.destinationPath} source has no lastModified \u2192 will sync`);
15596
+ logger.debug(`${logPrefix11} ${entry.destinationPath} source has no lastModified \u2192 will sync`);
15471
15597
  return true;
15472
15598
  }
15473
15599
  if (metadata.lastModified === null) {
15474
- logger.debug(`${logPrefix10} ${entry.destinationPath} CDN has no lastModified \u2192 will sync`);
15600
+ logger.debug(`${logPrefix11} ${entry.destinationPath} CDN has no lastModified \u2192 will sync`);
15475
15601
  return true;
15476
15602
  }
15477
15603
  const sourceIsNewer = entry.lastModified > metadata.lastModified;
15478
15604
  if (!sourceIsNewer) {
15479
- logger.debug(`${logPrefix10} Skipping ${entry.destinationPath} (CDN is up-to-date)`);
15605
+ logger.debug(`${logPrefix11} Skipping ${entry.destinationPath} (CDN is up-to-date)`);
15480
15606
  }
15481
15607
  return sourceIsNewer;
15482
15608
  }
@@ -15487,7 +15613,7 @@ var CdnAssetTarget = class extends AssetTarget {
15487
15613
  asset.entry.destinationPath,
15488
15614
  fileBuffer
15489
15615
  );
15490
- logger.info(`${logPrefix10} Uploaded ${asset.entry.destinationPath} (${fileBuffer.length} bytes)`);
15616
+ logger.info(`${logPrefix11} Uploaded ${asset.entry.destinationPath} (${fileBuffer.length} bytes)`);
15491
15617
  }
15492
15618
  };
15493
15619
 
@@ -15557,6 +15683,7 @@ var GCSStateProvider = class {
15557
15683
  GCSStateProvider,
15558
15684
  ITarget,
15559
15685
  ImageTransform,
15686
+ LocalStorageService,
15560
15687
  MIME_TYPES,
15561
15688
  MissingFieldInSchemaError,
15562
15689
  MissingSchemaError,