@whaly/connector-sdk 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -2,180 +2,10 @@
2
2
  import { readFileSync } from "fs";
3
3
 
4
4
  // src/sdk/service/logger.ts
5
- import * as winston2 from "winston";
6
-
7
- // src/sdk/models/resolver.ts
8
- var Resolver = class {
9
- constructor() {
10
- }
11
- };
12
-
13
- // src/resolvers/local-file/services/files.ts
14
- import fs from "fs";
15
- var writeFile = (path, content) => {
16
- return Promise.resolve(fs.writeFileSync(path, content));
17
- };
18
- var readAndParseJSONFile = (path) => {
19
- if (!fs.existsSync(path)) {
20
- throw new Error(`File \`${path}\` wasn't found at: ${process.cwd()}`);
21
- }
22
- const raw = fs.readFileSync(path);
23
- const parsed = JSON.parse(raw.toString());
24
- return parsed;
25
- };
26
-
27
- // src/resolvers/local-file/hook/schemaHook.ts
28
- import fsExtra from "fs-extra";
29
- var LocalSchemaHook = class {
30
- async writeSchema(input) {
31
- await fsExtra.appendFile("./schema.tmp", JSON.stringify(input) + "\n");
32
- }
33
- };
34
-
35
- // src/resolvers/local-file/resolver.ts
36
- import winston from "winston";
37
-
38
- // src/sdk/service/metric.ts
39
- var metricStore = {};
40
- var ALL_STREAM_ID_KEYWORD = "all";
41
- var ROWS_SYNCED_METRIC_NAME = "rows_synced_count";
42
- var API_CALLS_METRIC_NAME = "api_calls_count";
43
- var EXECUTION_TIME_METRIC_NAME = "execution_time_ms";
44
- var getCounterMetrics = (name, streamIds) => {
45
- return streamIds.map((streamId) => {
46
- return getCounterMetric(name, streamId);
47
- });
48
- };
49
- var getCounterMetric = (name, streamId = ALL_STREAM_ID_KEYWORD) => {
50
- if (!metricStore[streamId]) {
51
- metricStore[streamId] = {};
52
- }
53
- const streamMetricStore = metricStore[streamId];
54
- if (!streamMetricStore[name]) {
55
- streamMetricStore[name] = new CounterMetric(name, streamId);
56
- }
57
- return streamMetricStore[name];
58
- };
59
- var getAllMetrics = () => {
60
- return Object.keys(metricStore).flatMap((streamId) => {
61
- const streamMetricStore = metricStore[streamId];
62
- if (!streamMetricStore) {
63
- return [];
64
- }
65
- return Object.keys(streamMetricStore).map((metricName) => streamMetricStore[metricName]).filter((m) => m !== void 0);
66
- });
67
- };
68
- var CounterMetric = class {
69
- value = 0;
70
- name;
71
- streamId;
72
- constructor(name, streamId) {
73
- this.name = name;
74
- this.streamId = streamId;
75
- }
76
- increment(inc = 1) {
77
- this.value = this.value + inc;
78
- }
79
- getStreamId() {
80
- return this.streamId;
81
- }
82
- getName() {
83
- return this.name;
84
- }
85
- getValue() {
86
- return this.value;
87
- }
88
- };
89
-
90
- // src/resolvers/local-file/resolver.ts
91
- var { combine, prettyPrint, timestamp, errors, splat } = winston.format;
92
- var statePath = "./state.json";
93
- var metricsPath = "./metrics.json";
94
- var LocalFilesResolver = class extends Resolver {
95
- checkIfCanSync() {
96
- return Promise.resolve(true);
97
- }
98
- markSyncStarted() {
99
- return Promise.resolve();
100
- }
101
- onError(errorType, errorText, errorDebugText) {
102
- const path = `./error.json`;
103
- return writeFile(path, JSON.stringify({
104
- errorType,
105
- errorText,
106
- errorDebugText
107
- }));
108
- }
109
- getSchemaHooks() {
110
- const wlyHook = new LocalSchemaHook();
111
- return [wlyHook];
112
- }
113
- writeSchema() {
114
- return Promise.resolve();
115
- }
116
- constructor() {
117
- super();
118
- }
119
- getState() {
120
- logger.info(`\u{1F4C1} Using the LOCAL resolver`);
121
- const state = readAndParseJSONFile(statePath);
122
- return Promise.resolve({
123
- state
124
- });
125
- }
126
- writeCatalog(catalog) {
127
- return Promise.resolve();
128
- }
129
- writeState(state) {
130
- return writeFile(statePath, state);
131
- }
132
- markSyncFailed() {
133
- logger.info(`\u{1F622} Sync has failed.`);
134
- return Promise.resolve();
135
- }
136
- markSyncComplete() {
137
- logger.info(`\u{1F389} Sync is complete.`);
138
- return Promise.resolve();
139
- }
140
- markDiscoveryComplete() {
141
- logger.info(`\u{1F389} Discovery is complete.`);
142
- return Promise.resolve();
143
- }
144
- async flushMetrics() {
145
- const allMetrics = getAllMetrics();
146
- const metricsOuput = allMetrics.map((metric) => {
147
- return {
148
- streamId: metric.getStreamId(),
149
- name: metric.getName(),
150
- value: metric.getValue()
151
- };
152
- }).map((obj) => JSON.stringify(obj)).join(`
153
- `);
154
- return writeFile(metricsPath, metricsOuput);
155
- }
156
- updateSourceValue(optionKey, optionValue) {
157
- logger.info(`Updating source value ${optionKey} with value ${optionValue}`, { private: true });
158
- return Promise.resolve();
159
- }
160
- getLogFormat() {
161
- return combine(
162
- timestamp(),
163
- errors({ stack: true }),
164
- splat(),
165
- prettyPrint()
166
- );
167
- }
168
- };
169
-
170
- // src/sdk/service/resolver.ts
171
- var loadResolver = () => {
172
- return new LocalFilesResolver();
173
- };
174
-
175
- // src/sdk/service/logger.ts
5
+ import * as winston from "winston";
176
6
  var BATCH_INTERVAL_MS = 100;
177
7
  var transports2 = {
178
- console: new winston2.transports.Console({
8
+ console: new winston.transports.Console({
179
9
  stderrLevels: [],
180
10
  level: "info"
181
11
  })
@@ -185,8 +15,14 @@ var getTransports = () => {
185
15
  transports2.console
186
16
  ];
187
17
  };
188
- var winstonLogger = winston2.createLogger({
189
- format: loadResolver().getLogFormat(),
18
+ var { combine, prettyPrint, timestamp, errors, splat } = winston.format;
19
+ var winstonLogger = winston.createLogger({
20
+ format: combine(
21
+ timestamp(),
22
+ errors({ stack: true }),
23
+ splat(),
24
+ prettyPrint()
25
+ ),
190
26
  transports: getTransports()
191
27
  });
192
28
  var isWinstonOpen = true;
@@ -215,10 +51,6 @@ var logger = {
215
51
  };
216
52
 
217
53
  // src/sdk/utils.ts
218
- var writeStateFile = (resolver, state) => {
219
- logger.info(`\u{1F4DD} Writing the state file.`);
220
- return resolver.writeState(state);
221
- };
222
54
  function readFile(fileName, filePath) {
223
55
  try {
224
56
  return readFileSync(filePath);
@@ -248,7 +80,7 @@ var loadSchema = (streamId) => {
248
80
  // src/sdk/service/network.ts
249
81
  import axios from "axios";
250
82
  import { URLSearchParams } from "url";
251
- import * as fs2 from "fs";
83
+ import * as fs from "fs";
252
84
  import * as qs from "qs";
253
85
  import axiosRetry, { isNetworkOrIdempotentRequestError } from "axios-retry";
254
86
  var shouldRetry = (error) => {
@@ -361,7 +193,7 @@ var getJSONApiCall = async (endpoint, config, privateLogging) => {
361
193
  };
362
194
  var getDownloadFileApiCall = async (endpoint, config, output, privateLogging) => {
363
195
  const instance = getAxiosInstance(config.retryCount);
364
- const writer = fs2.createWriteStream(output);
196
+ const writer = fs.createWriteStream(output);
365
197
  const paramsSerializer = (params) => {
366
198
  return qs.stringify(params, { arrayFormat: "repeat" });
367
199
  };
@@ -455,6 +287,58 @@ var postUrlEncodedApiCall = async (endpoint, config, payload) => {
455
287
  }
456
288
  };
457
289
 
290
+ // src/sdk/service/metric.ts
291
+ var metricStore = {};
292
+ var ALL_STREAM_ID_KEYWORD = "all";
293
+ var ROWS_SYNCED_METRIC_NAME = "rows_synced_count";
294
+ var API_CALLS_METRIC_NAME = "api_calls_count";
295
+ var EXECUTION_TIME_METRIC_NAME = "execution_time_ms";
296
+ var getCounterMetrics = (name, streamIds) => {
297
+ return streamIds.map((streamId) => {
298
+ return getCounterMetric(name, streamId);
299
+ });
300
+ };
301
+ var getCounterMetric = (name, streamId = ALL_STREAM_ID_KEYWORD) => {
302
+ if (!metricStore[streamId]) {
303
+ metricStore[streamId] = {};
304
+ }
305
+ const streamMetricStore = metricStore[streamId];
306
+ if (!streamMetricStore[name]) {
307
+ streamMetricStore[name] = new CounterMetric(name, streamId);
308
+ }
309
+ return streamMetricStore[name];
310
+ };
311
+ var getAllMetrics = () => {
312
+ return Object.keys(metricStore).flatMap((streamId) => {
313
+ const streamMetricStore = metricStore[streamId];
314
+ if (!streamMetricStore) {
315
+ return [];
316
+ }
317
+ return Object.keys(streamMetricStore).map((metricName) => streamMetricStore[metricName]).filter((m) => m !== void 0);
318
+ });
319
+ };
320
+ var CounterMetric = class {
321
+ value = 0;
322
+ name;
323
+ streamId;
324
+ constructor(name, streamId) {
325
+ this.name = name;
326
+ this.streamId = streamId;
327
+ }
328
+ increment(inc = 1) {
329
+ this.value = this.value + inc;
330
+ }
331
+ getStreamId() {
332
+ return this.streamId;
333
+ }
334
+ getName() {
335
+ return this.name;
336
+ }
337
+ getValue() {
338
+ return this.value;
339
+ }
340
+ };
341
+
458
342
  // src/sdk/service/memory.ts
459
343
  var formatMemoryUsage = (data) => `${Math.round(data / 1024 / 1024 * 100) / 100} MB`;
460
344
  var printMemoryFootprint = (prefix) => {
@@ -470,9 +354,6 @@ var printMemoryFootprint = (prefix) => {
470
354
 
471
355
  // src/sdk/service/exit.ts
472
356
  async function gracefulExit(exitCode) {
473
- if (exitCode !== 0) {
474
- await loadResolver().markSyncFailed();
475
- }
476
357
  const loggerFinish = new Promise((resolve, reject) => {
477
358
  logger.closeWinston(resolve);
478
359
  });
@@ -492,34 +373,16 @@ var haltAndCatchFire = async (errorType, errorText, errorDebugText) => {
492
373
  errorText,
493
374
  errorDebugText
494
375
  );
495
- const resolver = loadResolver();
496
- await resolver.onError(errorType, errorText, errorDebugText);
497
376
  await gracefulExit(0);
498
377
  };
499
378
 
500
- // src/sdk/service/relationship.ts
501
- var findRelatedRelationships = (streamId, allRels) => {
502
- return allRels.reduce((acc, rel) => {
503
- const base = {
504
- type: rel.type,
505
- from: rel.from,
506
- to: rel.to
507
- };
508
- if (rel.left === streamId) {
509
- acc.right.push({
510
- streamId: rel.right,
511
- ...base
512
- });
513
- }
514
- if (rel.right === streamId) {
515
- acc.left.push({
516
- streamId: rel.left,
517
- ...base
518
- });
519
- }
520
- return acc;
521
- }, { left: [], right: [] });
522
- };
379
+ // src/sdk/models/replication.ts
380
+ var ReplicationMethod = /* @__PURE__ */ ((ReplicationMethod2) => {
381
+ ReplicationMethod2["INCREMENTAL"] = "INCREMENTAL";
382
+ ReplicationMethod2["FULL_TABLE"] = "FULL_TABLE";
383
+ ReplicationMethod2["APPEND"] = "APPEND";
384
+ return ReplicationMethod2;
385
+ })(ReplicationMethod || {});
523
386
 
524
387
  // src/sdk/models/messages.ts
525
388
  var RecordMessage = class {
@@ -579,7 +442,6 @@ var SchemaMessage = class {
579
442
  displayLabel;
580
443
  description;
581
444
  propertiesMetadata;
582
- relationships;
583
445
  constructor(opts) {
584
446
  const {
585
447
  stream,
@@ -588,7 +450,6 @@ var SchemaMessage = class {
588
450
  bookmarkProperties,
589
451
  displayLabel,
590
452
  description,
591
- relationships,
592
453
  propertiesMetadata
593
454
  } = opts;
594
455
  this.stream = stream;
@@ -597,7 +458,6 @@ var SchemaMessage = class {
597
458
  this.bookmarkProperties = bookmarkProperties;
598
459
  this.displayLabel = displayLabel;
599
460
  this.description = description;
600
- this.relationships = relationships;
601
461
  this.propertiesMetadata = propertiesMetadata;
602
462
  }
603
463
  toString() {
@@ -607,8 +467,7 @@ var SchemaMessage = class {
607
467
  schema: this.schema,
608
468
  key_properties: this.keyProperties,
609
469
  label: this.displayLabel,
610
- description: this.description,
611
- relationships: this.relationships
470
+ description: this.description
612
471
  };
613
472
  if (this.bookmarkProperties) {
614
473
  result["bookmark_properties"] = this.bookmarkProperties;
@@ -617,148 +476,28 @@ var SchemaMessage = class {
617
476
  }
618
477
  };
619
478
 
620
- // src/sdk/models/metadata.ts
621
- var MetadataMap = class extends Map {
622
- areEquals(array1, array2) {
623
- return array1.length === array2.length && array1.every(function(value, index) {
624
- return value === array2[index];
625
- });
626
- }
627
- findExistingEntry(key) {
628
- return Array.from(super.entries()).find((entry) => {
629
- if (this.areEquals(entry[0], key)) {
630
- return true;
631
- }
632
- });
633
- }
634
- get(key) {
635
- const existingEntry = this.findExistingEntry(key);
636
- return existingEntry?.[1];
637
- }
638
- set(key, value) {
639
- const existingKey = this.findExistingEntry(key);
640
- if (existingKey) {
641
- return super.set(existingKey?.[0], value);
642
- } else {
643
- return super.set(key, value);
644
- }
645
- }
646
- };
647
- var StreamMetadata = class {
648
- metadataByBreadcrumb;
649
- constructor() {
650
- this.metadataByBreadcrumb = new MetadataMap();
651
- }
652
- fromCatalogStreamMetadata(metadataArr) {
653
- metadataArr.forEach((metadata) => {
654
- this.metadataByBreadcrumb.set(metadata.breadcrumb, metadata.metadata);
655
- });
656
- return this;
657
- }
658
- toString() {
659
- return this.metadataByBreadcrumb;
660
- }
661
- get(breadcrumb) {
662
- return this.metadataByBreadcrumb.get(breadcrumb);
663
- }
664
- write(breadcrumb, key, value) {
665
- if (value === void 0) {
666
- throw new Error(`The value you're trying to write on the metadata at breadcrumb: \`${breadcrumb}\` and key: \`${key}\` is undefined.
667
-
668
- Writing an undefined value is not a valid operation. Is there something wrong with your value?`);
669
- }
670
- if (!this.metadataByBreadcrumb.get(breadcrumb)) {
671
- this.metadataByBreadcrumb.set(breadcrumb, {});
672
- }
673
- const previousValue = this.metadataByBreadcrumb.get(breadcrumb);
674
- const newValue = {};
675
- newValue[key] = value;
676
- this.metadataByBreadcrumb.set(breadcrumb, { ...previousValue, ...newValue });
677
- return this;
678
- }
679
- getRootBreadcrumbMetadata() {
680
- const rootBreadcrumbMetadata = this.metadataByBreadcrumb.get([]);
681
- if (!rootBreadcrumbMetadata) {
682
- throw new Error(`No metadata was attached to the root breadcrumb \`[\`] while we need one to store metadata at the table level.
683
-
684
- Is your catalog properly generated?`);
685
- }
686
- return rootBreadcrumbMetadata;
687
- }
688
- isStreamSelected() {
689
- const rootBreadcrumbMetadata = this.getRootBreadcrumbMetadata();
690
- if (rootBreadcrumbMetadata.selected === false) {
691
- return false;
692
- }
693
- if (rootBreadcrumbMetadata.selected === true || rootBreadcrumbMetadata["selected-by-default"] === true) {
694
- return true;
695
- }
696
- return false;
697
- }
698
- getKeyProperties() {
699
- const rootBreadcrumbMetadata = this.getRootBreadcrumbMetadata();
700
- if (!rootBreadcrumbMetadata["table-key-properties"]) {
701
- return [];
702
- }
703
- return rootBreadcrumbMetadata["table-key-properties"];
704
- }
705
- getReplicationKey() {
706
- const rootBreadcrumbMetadata = this.getRootBreadcrumbMetadata();
707
- if (!rootBreadcrumbMetadata["replication-key"]) {
708
- return void 0;
709
- }
710
- return rootBreadcrumbMetadata["replication-key"];
711
- }
712
- getReplicationMethod() {
713
- const rootBreadcrumbMetadata = this.getRootBreadcrumbMetadata();
714
- if (!rootBreadcrumbMetadata["replication-method"]) {
715
- return void 0;
716
- }
717
- return rootBreadcrumbMetadata["replication-method"];
718
- }
719
- getForcedReplicationMethod() {
720
- const rootBreadcrumbMetadata = this.getRootBreadcrumbMetadata();
721
- if (!rootBreadcrumbMetadata["forced-replication-method"]) {
722
- return void 0;
723
- }
724
- return rootBreadcrumbMetadata["forced-replication-method"];
725
- }
726
- toList() {
727
- return Array.from(this.metadataByBreadcrumb.keys()).map((breadcrumb) => {
728
- const metadata = this.metadataByBreadcrumb.get(breadcrumb);
729
- if (!metadata) {
730
- throw new Error(`There was no metadata entry for breadcrumb: \`${breadcrumb}\` in metadata map: ${this.metadataByBreadcrumb.toString()}`);
731
- }
732
- return {
733
- breadcrumb,
734
- metadata
735
- };
736
- });
737
- }
738
- };
739
-
740
479
  // src/sdk/models/state.ts
741
- import moment from "moment";
480
+ import dayjs from "dayjs";
742
481
 
743
482
  // src/sdk/constants/date.ts
744
483
  var defaultDateTimeFormat = "YYYY-MM-DDTHH:mm:ss.SSSSSSZ";
745
484
 
746
485
  // src/sdk/models/state.ts
747
- import { format } from "util";
486
+ import { format as format2 } from "util";
748
487
  var PROGRESS_MARKERS = "progressMarkers";
749
488
  var PROGRESS_MARKER_NOTE = "Note";
750
489
  var SIGNPOST_MARKER = "replicationKeySignpost";
751
490
  var incrementStreamState = (state, replicationKey, latestRecord, isSorted) => {
752
491
  let newRkValue = latestRecord[replicationKey];
753
492
  if (!newRkValue) {
754
- throw new Error(format(`No value for replicationKey: ${replicationKey} in record with keys: %j`, Object.keys(latestRecord)));
493
+ throw new Error(format2(`No value for replicationKey: ${replicationKey} in record with keys: %j`, Object.keys(latestRecord)));
755
494
  }
756
- let newRkValueMoment = moment(newRkValue);
495
+ let newRkValueMoment = dayjs(newRkValue);
757
496
  let prevRkValue;
758
497
  let prevRkValueMoment;
759
498
  if (isSorted) {
760
499
  prevRkValue = state["replicationKeyValue"];
761
- prevRkValueMoment = moment(prevRkValue);
500
+ prevRkValueMoment = dayjs(prevRkValue);
762
501
  if (prevRkValue && prevRkValueMoment.isAfter(newRkValueMoment)) {
763
502
  throw new Error(
764
503
  `Unsorted data detected in stream. Latest value '${newRkValue}' is
@@ -773,24 +512,24 @@ var incrementStreamState = (state, replicationKey, latestRecord, isSorted) => {
773
512
  state[PROGRESS_MARKERS] = marker;
774
513
  }
775
514
  prevRkValue = state[PROGRESS_MARKERS]["replicationKeyValue"];
776
- prevRkValueMoment = moment(prevRkValue || state["replicationKeyValue"]);
515
+ prevRkValueMoment = dayjs(prevRkValue || state["replicationKeyValue"]);
777
516
  if (prevRkValue && prevRkValueMoment.isAfter(newRkValueMoment)) {
778
517
  newRkValueMoment = prevRkValueMoment;
779
518
  newRkValue = prevRkValue;
780
519
  }
781
520
  }
782
521
  const signpostMarker = state[SIGNPOST_MARKER];
783
- const replicationKeySignpostMoment = moment(signpostMarker);
522
+ const replicationKeySignpostMoment = dayjs(signpostMarker);
784
523
  if (replicationKeySignpostMoment && replicationKeySignpostMoment.isBefore(newRkValueMoment)) {
785
524
  newRkValueMoment = replicationKeySignpostMoment;
786
525
  newRkValue = replicationKeySignpostMoment.format(defaultDateTimeFormat);
787
526
  }
788
527
  if (isSorted === false) {
789
528
  state[PROGRESS_MARKERS]["replicationKey"] = replicationKey;
790
- state[PROGRESS_MARKERS]["replicationKeyValue"] = moment(newRkValueMoment).format(defaultDateTimeFormat);
529
+ state[PROGRESS_MARKERS]["replicationKeyValue"] = dayjs(newRkValueMoment).format(defaultDateTimeFormat);
791
530
  } else {
792
531
  state["replicationKey"] = replicationKey;
793
- state["replicationKeyValue"] = moment(newRkValueMoment).format(defaultDateTimeFormat);
532
+ state["replicationKeyValue"] = dayjs(newRkValueMoment).format(defaultDateTimeFormat);
794
533
  }
795
534
  return state;
796
535
  };
@@ -812,7 +551,7 @@ var finalizeStateProgressMarkers = (state) => {
812
551
  return state;
813
552
  };
814
553
  var _greaterThan = (aValue, bValue) => {
815
- return moment(aValue).isAfter(moment(bValue));
554
+ return dayjs(aValue).isAfter(dayjs(bValue));
816
555
  };
817
556
  var extractStateForStream = (state, streamId) => {
818
557
  if (!state.bookmarks) {
@@ -835,26 +574,16 @@ var StateService = class {
835
574
  }
836
575
  return this.instance;
837
576
  }
838
- // To be deprecated
839
- setBookmark(streamId, ts) {
840
- this.bookmarks[streamId] = ts.format(defaultDateTimeFormat);
841
- }
842
- setBookmarkV2(streamId, streamState) {
577
+ setBookmark(streamId, streamState) {
843
578
  this.bookmarks[streamId] = streamState;
844
579
  }
845
- setBookmarkSignpostV2(streamId, signpostValue) {
580
+ setBookmarkSignpost(streamId, signpostValue) {
846
581
  if (!this.bookmarks[streamId]) {
847
582
  this.bookmarks[streamId] = {};
848
583
  }
849
584
  this.bookmarks[streamId][SIGNPOST_MARKER] = signpostValue;
850
585
  }
851
586
  getBookmark(streamId) {
852
- if (!this.bookmarks[streamId]) {
853
- return moment(0);
854
- }
855
- return moment(this.bookmarks[streamId]);
856
- }
857
- getBookmarkV2(streamId) {
858
587
  if (this.bookmarks[streamId]) {
859
588
  return this.bookmarks[streamId];
860
589
  } else {
@@ -894,7 +623,7 @@ import axios2 from "axios";
894
623
  import * as qs2 from "qs";
895
624
 
896
625
  // src/sdk/models/tap/stream.ts
897
- import moment2 from "moment";
626
+ import dayjs2 from "dayjs";
898
627
 
899
628
  // src/sdk/helpers/typing.ts
900
629
  var isDatetimeType = (typeDef) => {
@@ -917,10 +646,11 @@ var isDatetimeType = (typeDef) => {
917
646
  };
918
647
 
919
648
  // src/sdk/models/tap/stream.ts
920
- import cloneDeep from "lodash/cloneDeep.js";
649
+ import cloneDeep2 from "lodash/cloneDeep.js";
921
650
  import Bluebird2 from "bluebird";
922
651
 
923
652
  // src/sdk/models/tap/tap.ts
653
+ import cloneDeep from "lodash/cloneDeep.js";
924
654
  import Bluebird from "bluebird";
925
655
  var DEFAULT_MAX_CONCURRENT_STREAMS = 5;
926
656
  var Tap = class {
@@ -928,12 +658,21 @@ var Tap = class {
928
658
  config;
929
659
  streams;
930
660
  concurrency = DEFAULT_MAX_CONCURRENT_STREAMS;
931
- constructor(target, config) {
661
+ stateProvider;
662
+ tapState;
663
+ constructor(target, config, stateProvider) {
932
664
  this.streams = [];
933
665
  this.target = target;
934
666
  this.config = config;
667
+ this.stateProvider = stateProvider;
668
+ this.tapState = { bookmarks: {} };
935
669
  }
936
670
  sync = async (options) => {
671
+ const initialState = await this.stateProvider.getState();
672
+ if (initialState.state && initialState.state.bookmarks) {
673
+ this.tapState = cloneDeep(initialState.state);
674
+ }
675
+ await this.init();
937
676
  logger.info(`\u{1F680} Start syncing`);
938
677
  const state = StateService.getInstance().get();
939
678
  logger.info(`\u{1F4CD} Received state: %j`, state);
@@ -964,15 +703,13 @@ var Tap = class {
964
703
  };
965
704
 
966
705
  // src/sdk/models/tap/stream.ts
967
- import { format as format2 } from "util";
706
+ import { format as format3 } from "util";
968
707
  var Stream = class {
969
708
  // Runtime values, can't be overriden
970
709
  config;
971
710
  tapState;
972
711
  target;
973
- // Compile time values, can be overriden
974
- // Replication method that can be forced in the stream implemenation. Will prevail over any user/catalog choice
975
- forcedReplicationMethod = void 0;
712
+ replicationMethod = void 0;
976
713
  selectedByDefault = true;
977
714
  STATE_MSG_FREQUENCY = 10;
978
715
  streamId = "default";
@@ -980,10 +717,9 @@ var Stream = class {
980
717
  displayLabel = void 0;
981
718
  description = void 0;
982
719
  primaryKey = [];
983
- forcedReplicationKey = void 0;
720
+ replicationKey = void 0;
984
721
  // When set, use the state from another stream for this stream
985
722
  useStateFromStreamId = void 0;
986
- relationships = [];
987
723
  children = [];
988
724
  // If true, it means that the records in this stream are sorted by replicationKey.
989
725
  // If false, then the records are coming in an unsorted manner.
@@ -995,9 +731,9 @@ var Stream = class {
995
731
  // Metrics configuration
996
732
  rowsSyncedMetricsConf;
997
733
  executionTimeMetricsConf;
998
- constructor(config, state, target, childConcurrency = DEFAULT_MAX_CONCURRENT_STREAMS) {
734
+ constructor(config, tapState, target, childConcurrency = DEFAULT_MAX_CONCURRENT_STREAMS) {
999
735
  this.config = config;
1000
- this.tapState = cloneDeep(state);
736
+ this.tapState = cloneDeep2(tapState);
1001
737
  this.target = target;
1002
738
  this.childConcurrency = childConcurrency;
1003
739
  this.rowsSyncedMetricsConf = {
@@ -1029,7 +765,7 @@ var Stream = class {
1029
765
  return this.flush();
1030
766
  } catch (err) {
1031
767
  if (err instanceof Error) {
1032
- throw new Error(format2(`got an error while syncing streamId=%s, err: %s`, this.streamId, err.message));
768
+ throw new Error(format3(`got an error while syncing streamId=%s, err: %s`, this.streamId, err.message));
1033
769
  }
1034
770
  throw err;
1035
771
  }
@@ -1070,13 +806,11 @@ var Stream = class {
1070
806
  if (!this.isSilent) {
1071
807
  const schema = await this.getSchema();
1072
808
  if (schema !== void 0) {
1073
- const relatedRels = findRelatedRelationships(this.streamId, this.relationships);
1074
809
  const replicationConfig = this.configuredReplicationConfig();
1075
810
  const message = new SchemaMessage({
1076
811
  keyProperties: this.primaryKey,
1077
812
  stream: this.streamId,
1078
813
  schema: schema.jsonSchema,
1079
- relationships: relatedRels,
1080
814
  ...replicationConfig.replicationKey ? { bookmarkProperties: [replicationConfig.replicationKey] } : {},
1081
815
  ...this.displayLabel ? { displayLabel: this.displayLabel } : {},
1082
816
  ...this.description ? { description: this.description } : {},
@@ -1096,7 +830,7 @@ var Stream = class {
1096
830
  const signpostMoment = await this.getReplicationKeySignpost();
1097
831
  const signpostValue = signpostMoment?.format(defaultDateTimeFormat);
1098
832
  if (signpostValue) {
1099
- StateService.getInstance().setBookmarkSignpostV2(this.streamId, signpostValue);
833
+ StateService.getInstance().setBookmarkSignpost(this.streamId, signpostValue);
1100
834
  }
1101
835
  };
1102
836
  /**
@@ -1112,7 +846,7 @@ var Stream = class {
1112
846
  if (this.useStateFromStreamId) {
1113
847
  return;
1114
848
  }
1115
- if (this.forcedReplicationMethod && !this.forcedReplicationKey) {
849
+ if (this.replicationMethod && !this.replicationKey) {
1116
850
  return;
1117
851
  }
1118
852
  if (!replicationConfig.replicationKey) {
@@ -1122,14 +856,14 @@ var Stream = class {
1122
856
  );
1123
857
  }
1124
858
  const stateServiceInst = StateService.getInstance();
1125
- const state = stateServiceInst.getBookmarkV2(this.streamId);
859
+ const state = stateServiceInst.getBookmark(this.streamId);
1126
860
  const newState = incrementStreamState(
1127
861
  state,
1128
862
  replicationConfig.replicationKey,
1129
863
  latestRecord,
1130
864
  this.isSorted
1131
865
  );
1132
- StateService.getInstance().setBookmarkV2(this.streamId, newState);
866
+ StateService.getInstance().setBookmark(this.streamId, newState);
1133
867
  }
1134
868
  }
1135
869
  };
@@ -1169,19 +903,19 @@ var Stream = class {
1169
903
  this.replicationKey: ${replicationConfig.replicationKey}`);
1170
904
  if (state.replicationKeyValue) {
1171
905
  logger.debug(`${this.streamId} - getStartingTimestamp -> Returning replication key value: ${state.replicationKeyValue}`);
1172
- return moment2(state.replicationKeyValue);
906
+ return dayjs2(state.replicationKeyValue);
1173
907
  } else if (startDate) {
1174
908
  logger.debug(`${this.streamId} - getStartingTimestamp -> Returning config start date: ${startDate}`);
1175
- return moment2(startDate);
909
+ return dayjs2(startDate);
1176
910
  } else {
1177
911
  logger.debug(`${this.streamId} - getStartingTimestamp -> Returning EPOCH (1970)`);
1178
- return moment2(0);
912
+ return dayjs2(0);
1179
913
  }
1180
914
  }
1181
915
  /**
1182
916
  * Return the max allowable bookmark value for this stream's replication key.
1183
917
  *
1184
- * For timestamp-based replication keys, this defaults to `moment()`. For
918
+ * For timestamp-based replication keys, this defaults to `dayjs()`. For
1185
919
  * non-timestamp replication keys, default to `undefined`.
1186
920
  *
1187
921
  * Override this value to prevent bookmarks from being advanced in cases where we
@@ -1189,7 +923,7 @@ var Stream = class {
1189
923
  */
1190
924
  getReplicationKeySignpost = async () => {
1191
925
  if (await this.isTimestampReplicationKey()) {
1192
- return moment2();
926
+ return dayjs2();
1193
927
  }
1194
928
  return void 0;
1195
929
  };
@@ -1197,22 +931,22 @@ var Stream = class {
1197
931
  * Return the default replication method for the stream that will be used to write the catalog.
1198
932
  */
1199
933
  defaultReplicationConfig = () => {
1200
- if (this.forcedReplicationMethod) {
934
+ if (this.replicationMethod) {
1201
935
  return {
1202
- replicationMethod: this.forcedReplicationMethod,
1203
- replicationKey: this.forcedReplicationKey,
936
+ replicationMethod: this.replicationMethod,
937
+ replicationKey: this.replicationKey,
1204
938
  isForced: true
1205
939
  };
1206
940
  }
1207
- if (this.forcedReplicationKey || this.useStateFromStreamId) {
941
+ if (this.replicationKey || this.useStateFromStreamId) {
1208
942
  return {
1209
- replicationMethod: "INCREMENTAL",
1210
- replicationKey: this.forcedReplicationKey,
943
+ replicationMethod: "INCREMENTAL" /* INCREMENTAL */,
944
+ replicationKey: this.replicationKey,
1211
945
  isForced: true
1212
946
  };
1213
947
  }
1214
948
  return {
1215
- replicationMethod: "FULL_TABLE",
949
+ replicationMethod: "FULL_TABLE" /* FULL_TABLE */,
1216
950
  replicationKey: void 0,
1217
951
  isForced: false
1218
952
  };
@@ -1273,9 +1007,9 @@ var Stream = class {
1273
1007
  rowsSent += 1;
1274
1008
  }
1275
1009
  const stateServiceInst = StateService.getInstance();
1276
- const state = stateServiceInst.getBookmarkV2(this.streamId);
1010
+ const state = stateServiceInst.getBookmark(this.streamId);
1277
1011
  const newState = finalizeStateProgressMarkers(state);
1278
- stateServiceInst.setBookmarkV2(this.streamId, newState);
1012
+ stateServiceInst.setBookmark(this.streamId, newState);
1279
1013
  if (!parent) {
1280
1014
  logger.info(`\u2705 Completed sync for stream: ${this.streamId} (${rowsSent} records)`);
1281
1015
  } else {
@@ -1381,7 +1115,7 @@ function getTruncatedParamsForLog(params) {
1381
1115
  }
1382
1116
 
1383
1117
  // src/sdk/models/tap/restStream.ts
1384
- import cloneDeep2 from "lodash/cloneDeep.js";
1118
+ import cloneDeep3 from "lodash/cloneDeep.js";
1385
1119
  import isEqual from "lodash/isEqual.js";
1386
1120
  var RESTStream = class extends Stream {
1387
1121
  axiosInstance;
@@ -1402,13 +1136,24 @@ var RESTStream = class extends Stream {
1402
1136
  */
1403
1137
  _prepareRequest = async (nextPageToken, parent) => {
1404
1138
  const method = this.httpMethod;
1405
- const url = this.getUrl(parent);
1139
+ const url = this.getNextUrl(nextPageToken, parent);
1406
1140
  if (!url) {
1407
1141
  throw new Error(`StreamId: ${this.streamId} - URL is undefined. Did your properly define the URL for this stream?`);
1408
1142
  }
1409
1143
  const authenticator = this.authenticator;
1144
+ let finalUrl = url;
1145
+ let urlQueryParams = {};
1146
+ const queryIndex = url.indexOf("?");
1147
+ if (queryIndex !== -1) {
1148
+ finalUrl = url.substring(0, queryIndex);
1149
+ const rawQueryString = url.substring(queryIndex + 1);
1150
+ urlQueryParams = qs2.parse(rawQueryString);
1151
+ }
1410
1152
  const params = {
1411
1153
  ...this.getNextUrlParams(nextPageToken, parent),
1154
+ // Query params embedded in URL should override computed pagination params
1155
+ // but should not override auth params which are appended last
1156
+ ...urlQueryParams,
1412
1157
  ...await authenticator?.getAuthQS(this.config)
1413
1158
  };
1414
1159
  const requestBody = this.getRequestBodyForNextCall(nextPageToken, parent);
@@ -1427,7 +1172,7 @@ var RESTStream = class extends Stream {
1427
1172
  return qs2.stringify(params2, { arrayFormat: "repeat" });
1428
1173
  };
1429
1174
  const request = {
1430
- url,
1175
+ url: finalUrl,
1431
1176
  method,
1432
1177
  data: requestBody,
1433
1178
  params,
@@ -1500,7 +1245,7 @@ var RESTStream = class extends Stream {
1500
1245
  for await (const row of rows) {
1501
1246
  yield row;
1502
1247
  }
1503
- const previousToken = cloneDeep2(nextPageToken);
1248
+ const previousToken = cloneDeep3(nextPageToken);
1504
1249
  nextPageToken = this.getNextPageToken(resp, previousToken);
1505
1250
  if (nextPageToken && isEqual(nextPageToken, previousToken)) {
1506
1251
  throw new Error(
@@ -1579,7 +1324,7 @@ var RESTStream = class extends Stream {
1579
1324
  * TODO: Make URL + Path templatisable and replace those with Extractor (or Tap) config
1580
1325
  * @returns
1581
1326
  */
1582
- getUrl(parent) {
1327
+ getNextUrl(previousToken, parent) {
1583
1328
  const urlPattern = [this.baseUrl, this.path].join("");
1584
1329
  return urlPattern;
1585
1330
  }
@@ -1760,6 +1505,8 @@ var StreamWarehouseSyncService = class {
1760
1505
  mergeQueries = this.getReplaceQueries();
1761
1506
  } else if (replicationMethod === "INCREMENTAL") {
1762
1507
  mergeQueries = this.getMergeQueries();
1508
+ } else if (replicationMethod === "APPEND") {
1509
+ mergeQueries = this.getAppendQueries();
1763
1510
  } else if (replicationMethod === "LOG_BASED") {
1764
1511
  mergeQueries = this.getMergeQueries();
1765
1512
  }
@@ -1931,25 +1678,25 @@ var flattenSchema = (streamId, schema) => {
1931
1678
 
1932
1679
  // src/sdk/models/target/target.ts
1933
1680
  import { Validator } from "jsonschema";
1934
- import moment4 from "moment";
1681
+ import dayjs4 from "dayjs";
1935
1682
 
1936
1683
  // src/sdk/models/target/streamDbState.ts
1937
- import moment3 from "moment";
1684
+ import dayjs3 from "dayjs";
1938
1685
 
1939
1686
  // src/sdk/models/target/temporaryFile.ts
1940
- import fs3 from "fs-extra";
1687
+ import fs2 from "fs-extra";
1941
1688
  import uniqueId from "lodash/uniqueId.js";
1942
1689
  var createTemporaryFileStream = (streamId) => {
1943
- fs3.ensureDirSync("./tmp");
1944
- const path = `./tmp/${safePath(streamId)}-${uniqueId()}.tmp`;
1945
- const writeStream = fs3.createWriteStream(path, { flags: "w" });
1690
+ fs2.ensureDirSync("./tmp");
1691
+ const path2 = `./tmp/${safePath(streamId)}-${uniqueId()}.tmp`;
1692
+ const writeStream = fs2.createWriteStream(path2, { flags: "w" });
1946
1693
  return {
1947
1694
  stream: writeStream,
1948
- path
1695
+ path: path2
1949
1696
  };
1950
1697
  };
1951
- function safePath(path) {
1952
- let formattedPath = path;
1698
+ function safePath(path2) {
1699
+ let formattedPath = path2;
1953
1700
  formattedPath = formattedPath.replace(/^The\s+/gi, "");
1954
1701
  formattedPath = formattedPath.replace(/\s+(?:\(|\[)?Dis(?:c|k) (?:One|Two|Three|Four|Five|Six|Seven|Eight|Nine|Ten|\d)+(?:\)|\])?/gi, "");
1955
1702
  formattedPath = formattedPath.replace(/\s+/gi, "_");
@@ -1990,7 +1737,7 @@ function replaceSymbols(str) {
1990
1737
  }
1991
1738
 
1992
1739
  // src/sdk/models/target/streamDbState.ts
1993
- import * as fs4 from "fs";
1740
+ import * as fs3 from "fs";
1994
1741
  var StreamState = class {
1995
1742
  streamId;
1996
1743
  schema;
@@ -2015,7 +1762,7 @@ var StreamState = class {
2015
1762
  this.fileToLoad = createTemporaryFileStream(streamId);
2016
1763
  this.syncedRowCount = 0;
2017
1764
  this.keyProperties = [];
2018
- this.batchDate = moment3();
1765
+ this.batchDate = dayjs3();
2019
1766
  this.replicationMethod = replicationMethod;
2020
1767
  this._hasBeenLoadedYetDuringThisSync = false;
2021
1768
  }
@@ -2040,7 +1787,7 @@ var StreamState = class {
2040
1787
  resetFileToLoad() {
2041
1788
  this.fileToLoad.stream.close();
2042
1789
  const oldPath = this.fileToLoad.path;
2043
- fs4.unlinkSync(oldPath);
1790
+ fs3.unlinkSync(oldPath);
2044
1791
  this.fileToLoad = createTemporaryFileStream(this.streamId);
2045
1792
  this.batchedRowCount = 0;
2046
1793
  }
@@ -2071,8 +1818,8 @@ var semaphore = new Semaphore(1);
2071
1818
  var ITarget = class _ITarget {
2072
1819
  config;
2073
1820
  schemaHooks;
2074
- resolver;
2075
1821
  syncTime;
1822
+ stateProvider;
2076
1823
  // Latest state message received from the Tap
2077
1824
  // Not yet flushed as we didn't upload the stream data since receiving it
2078
1825
  batchedState;
@@ -2083,15 +1830,15 @@ var ITarget = class _ITarget {
2083
1830
  streams;
2084
1831
  // JSON Schema validator
2085
1832
  validator;
2086
- constructor(config, resolver) {
1833
+ constructor(config, stateProvider) {
2087
1834
  this.config = config;
2088
- this.resolver = resolver;
2089
- this.schemaHooks = resolver.getSchemaHooks();
1835
+ this.stateProvider = stateProvider;
1836
+ this.schemaHooks = [];
2090
1837
  this.streams = {};
2091
1838
  this.batchedState = {};
2092
1839
  this.flushedState = {};
2093
1840
  this.validator = new Validator();
2094
- this.syncTime = moment4();
1841
+ this.syncTime = dayjs4();
2095
1842
  this.renameColumnStore = new RenameColumnStore();
2096
1843
  }
2097
1844
  ///////////////////////////////////////////
@@ -2200,7 +1947,7 @@ var ITarget = class _ITarget {
2200
1947
  this.streams[streamId] = new StreamState(
2201
1948
  streamId,
2202
1949
  dbSyncInstance,
2203
- replicationMethod || "FULL_TABLE"
1950
+ replicationMethod || "FULL_TABLE" /* FULL_TABLE */
2204
1951
  );
2205
1952
  }
2206
1953
  replicationMethod = (message) => {
@@ -2258,7 +2005,8 @@ var ITarget = class _ITarget {
2258
2005
  }
2259
2006
  });
2260
2007
  streamState.setSchema(schema);
2261
- if (message.keyProperties.length === 0) {
2008
+ const streamReplicationMethod = this.streams[streamId]?.getReplicationMethod();
2009
+ if (message.keyProperties.length === 0 && streamReplicationMethod !== "APPEND" /* APPEND */) {
2262
2010
  throw new Error(`StreamId: ${message.stream} - \`key_properties\` field is required and can't be empty in SCHEMA message.
2263
2011
  Received SCHEMA message: ${JSON.stringify(message)}`);
2264
2012
  }
@@ -2277,39 +2025,7 @@ var ITarget = class _ITarget {
2277
2025
  return translation;
2278
2026
  }
2279
2027
  return k;
2280
- }),
2281
- relationships: {
2282
- left: message.relationships.left.flatMap((l) => {
2283
- if (this.renameColumnStore.isReady(l.streamId) && this.renameColumnStore.isReady(message.stream)) {
2284
- const from = this.renameColumnStore.getColumnTranslation(l.streamId, l.from);
2285
- const to = this.renameColumnStore.getColumnTranslation(message.stream, l.to);
2286
- if (from && to) {
2287
- return [{
2288
- type: l.type,
2289
- streamId: l.streamId,
2290
- from,
2291
- to
2292
- }];
2293
- }
2294
- }
2295
- return [];
2296
- }),
2297
- right: message.relationships.right.flatMap((r) => {
2298
- if (this.renameColumnStore.isReady(message.stream) && this.renameColumnStore.isReady(r.streamId)) {
2299
- const from = this.renameColumnStore.getColumnTranslation(message.stream, r.from);
2300
- const to = this.renameColumnStore.getColumnTranslation(r.streamId, r.to);
2301
- if (from && to) {
2302
- return [{
2303
- type: r.type,
2304
- streamId: r.streamId,
2305
- from,
2306
- to
2307
- }];
2308
- }
2309
- }
2310
- return [];
2311
- })
2312
- }
2028
+ })
2313
2029
  };
2314
2030
  await Bluebird3.map(this.schemaHooks, async (hook) => {
2315
2031
  const input = {
@@ -2342,9 +2058,6 @@ var ITarget = class _ITarget {
2342
2058
  try {
2343
2059
  logger.info(`\u{1F44D} Data Extraction is complete. Will load remaining batched stream records.`);
2344
2060
  await this.loadAllStreamsInWarehouse({ isFinalLoad: true });
2345
- const configResolver = loadResolver();
2346
- await configResolver.markSyncComplete();
2347
- await configResolver.flushMetrics();
2348
2061
  return Promise.resolve();
2349
2062
  } catch (err) {
2350
2063
  logger.error(`Error while handling complete event.`);
@@ -2357,7 +2070,8 @@ var ITarget = class _ITarget {
2357
2070
  return false;
2358
2071
  }
2359
2072
  const batchedRecords = stream.getBatchedRowCount();
2360
- if (stream.getReplicationMethod() === "INCREMENTAL" && batchedRecords > 1e6) {
2073
+ const method = stream.getReplicationMethod();
2074
+ if ((method === "INCREMENTAL" || method === "APPEND") && batchedRecords > 1e6) {
2361
2075
  logger.info(`\u{1F9CA} Stream=${streamId} has reached the maximum amount of batched records. We'll trigger a flush of ALL streams in the Warehouse.`);
2362
2076
  return true;
2363
2077
  } else {
@@ -2387,8 +2101,7 @@ var ITarget = class _ITarget {
2387
2101
  }
2388
2102
  };
2389
2103
  emitState = (state) => {
2390
- const configResolver = loadResolver();
2391
- return writeStateFile(configResolver, JSON.stringify(state));
2104
+ return this.stateProvider.writeState(JSON.stringify(state));
2392
2105
  };
2393
2106
  uploadStreamsToWarehouse = async (streamsToUpload) => {
2394
2107
  try {
@@ -2427,24 +2140,1089 @@ var ITarget = class _ITarget {
2427
2140
  };
2428
2141
  };
2429
2142
 
2430
- // src/targets/bigquery/helpers.ts
2431
- var safeColumnName = (key) => {
2432
- let returnString = key;
2433
- const shouldNotStartWith = ["_TABLE_", "_FILE_", "_PARTITION"];
2434
- shouldNotStartWith.forEach((snm) => {
2435
- if (returnString.startsWith(snm)) {
2436
- returnString = returnString.replace(snm, "");
2143
+ // src/sdk/file-processing/types.ts
2144
+ var FileFormat = /* @__PURE__ */ ((FileFormat2) => {
2145
+ FileFormat2["CSV"] = "CSV";
2146
+ FileFormat2["EXCEL"] = "EXCEL";
2147
+ return FileFormat2;
2148
+ })(FileFormat || {});
2149
+
2150
+ // src/sdk/file-processing/schema-utils.ts
2151
+ function fieldTypeToJsonSchema(fieldType) {
2152
+ switch (fieldType) {
2153
+ case "STRING":
2154
+ return { type: ["null", "string"] };
2155
+ case "FLOAT":
2156
+ return { type: ["null", "number"] };
2157
+ case "TIMESTAMP":
2158
+ return { type: ["null", "string"], format: "date-time" };
2159
+ default:
2160
+ return { type: ["null", "string"] };
2161
+ }
2162
+ }
2163
+ function excelFieldsToJsonSchema(fields) {
2164
+ const properties = {};
2165
+ for (const [key, field] of Object.entries(fields)) {
2166
+ properties[key] = fieldTypeToJsonSchema(field.type);
2167
+ }
2168
+ return { type: "object", properties };
2169
+ }
2170
+ function csvFieldsToJsonSchema(config) {
2171
+ const properties = {};
2172
+ if (Array.isArray(config)) {
2173
+ for (const field of config) {
2174
+ properties[field.key] = fieldTypeToJsonSchema(field.type);
2175
+ }
2176
+ } else {
2177
+ for (const [, field] of Object.entries(config)) {
2178
+ properties[field.key] = fieldTypeToJsonSchema(field.type);
2179
+ }
2180
+ }
2181
+ return { type: "object", properties };
2182
+ }
2183
+ function extractPrimaryKeysFromCsvConfig(config) {
2184
+ if (Array.isArray(config)) {
2185
+ return config.map((f) => f.key);
2186
+ }
2187
+ return Object.values(config).map((f) => f.key);
2188
+ }
2189
+ function extractPrimaryKeysFromExcelFields(fields) {
2190
+ return Object.entries(fields).filter(([, field]) => {
2191
+ return field.primaryKey === true || field.primaryKey === true;
2192
+ }).map(([key]) => key);
2193
+ }
2194
+
2195
+ // src/sdk/file-processing/excel/reader.ts
2196
+ import XLSX from "xlsx";
2197
+ import path from "path";
2198
+ var excelColumnToIndex = (columnLetter) => {
2199
+ const letters = columnLetter.toUpperCase();
2200
+ let index = 0;
2201
+ for (let i = 0; i < letters.length; i++) {
2202
+ const charCode = letters.charCodeAt(i);
2203
+ if (charCode < 65 || charCode > 90) {
2204
+ throw new Error(`Invalid column letter: ${columnLetter}`);
2205
+ }
2206
+ index = index * 26 + (charCode - 64);
2207
+ }
2208
+ return index - 1;
2209
+ };
2210
+ var indexToExcelColumn = (columnIndex) => {
2211
+ if (columnIndex < 0) {
2212
+ throw new Error(`Invalid column index: ${columnIndex}. Must be >= 0`);
2213
+ }
2214
+ let result = "";
2215
+ let index = columnIndex;
2216
+ while (true) {
2217
+ result = String.fromCharCode(65 + index % 26) + result;
2218
+ index = Math.floor(index / 26);
2219
+ if (index === 0) break;
2220
+ index--;
2221
+ }
2222
+ return result;
2223
+ };
2224
+ var excelRowToIndex = (rowNumber) => {
2225
+ const rowNum = typeof rowNumber === "string" ? parseInt(rowNumber, 10) : rowNumber;
2226
+ if (isNaN(rowNum) || rowNum < 1) {
2227
+ throw new Error(`Invalid row number: ${rowNumber}. Must be >= 1`);
2228
+ }
2229
+ return rowNum - 1;
2230
+ };
2231
+ var indexToExcelRow = (index) => {
2232
+ if (index < 0) {
2233
+ throw new Error(`Invalid index: ${index}. Must be >= 0`);
2234
+ }
2235
+ return index + 1;
2236
+ };
2237
+ var parseCellReference = (cellRef) => {
2238
+ const match = cellRef.match(/^([A-Z]+)(\d+)$/);
2239
+ if (!match) {
2240
+ throw new Error(`Invalid cell reference: ${cellRef}. Expected format like "A1", "Z24", "AA100"`);
2241
+ }
2242
+ const [, columnLetter, rowNumber] = match;
2243
+ return {
2244
+ columnIndex: excelColumnToIndex(columnLetter),
2245
+ rowIndex: excelRowToIndex(rowNumber)
2246
+ };
2247
+ };
2248
+ var createCellReference = (columnIndex, rowIndex) => {
2249
+ return indexToExcelColumn(columnIndex) + indexToExcelRow(rowIndex);
2250
+ };
2251
+ var extractSingleSheetRows = async (fileName, conf) => {
2252
+ console.log("Extracting single sheet rows from file: %s", fileName);
2253
+ const workbook = XLSX.readFile(fileName, { dense: true });
2254
+ const sheetKeys = Object.keys(workbook.Sheets);
2255
+ const sheet = conf.sheetName ? workbook.Sheets[conf.sheetName] : workbook.Sheets[sheetKeys[0]];
2256
+ if (!sheet) {
2257
+ throw new Error(`Sheet ${conf.sheetName} not found in workbook`);
2258
+ }
2259
+ const sheetData = sheet["!data"];
2260
+ if (!sheetData) {
2261
+ throw new Error(`No data in ${conf.sheetName || "first"} sheet`);
2262
+ }
2263
+ if (sheetData.length === 0) {
2264
+ throw new Error(`Sheet ${conf.sheetName || "first"} is empty`);
2265
+ }
2266
+ const variables = conf.fileNameVariablesExtractor(fileName, workbook);
2267
+ const data = [];
2268
+ let rowIdx = 0;
2269
+ const minColumnIndex = Object.values(conf.columns).reduce((min, column) => {
2270
+ return column.column ? Math.min(min, excelColumnToIndex(column.column)) : min;
2271
+ }, Number.MAX_SAFE_INTEGER);
2272
+ for (const row of sheetData) {
2273
+ if (rowIdx < conf.numberOfRowsToSkip) {
2274
+ rowIdx++;
2275
+ continue;
2276
+ }
2277
+ if (!row) {
2278
+ console.log("Empty row at index: %s, skipped processing.", rowIdx);
2279
+ continue;
2280
+ }
2281
+ if (minColumnIndex !== Number.MAX_SAFE_INTEGER && !row[minColumnIndex]?.v) {
2282
+ console.log("stopping processing because of empty value in the first data column (idx: %s)", minColumnIndex);
2283
+ break;
2284
+ }
2285
+ const rowData = Object.entries(conf.columns).reduce((acc, [key, column]) => {
2286
+ if (column.variableName) {
2287
+ if (!variables) {
2288
+ throw new Error(`No variables extracted from filename, cannot extract derived field ${key}`);
2289
+ }
2290
+ acc[key] = variables[column.variableName] || "";
2291
+ } else {
2292
+ const colIndex = excelColumnToIndex(column.column);
2293
+ acc[key] = row[colIndex]?.v || "";
2294
+ }
2295
+ return acc;
2296
+ }, {});
2297
+ data.push(rowData);
2298
+ rowIdx++;
2299
+ }
2300
+ console.log("Extracted %s rows from sheet %s", data.length, conf.sheetName || "first");
2301
+ return data;
2302
+ };
2303
+ var extractExcelRows = async (localFilePath, conf) => {
2304
+ let data;
2305
+ if (conf.type === "single-sheet-extraction") {
2306
+ data = await extractSingleSheetRows(localFilePath, conf);
2307
+ } else if (conf.type === "processor") {
2308
+ const workbook = XLSX.readFile(localFilePath, { dense: true });
2309
+ data = await conf.processor(workbook);
2310
+ } else {
2311
+ throw new Error(`Unsupported configuration type: ${conf.type}`);
2312
+ }
2313
+ return data;
2314
+ };
2315
+ var validateExcelConfig = (config) => {
2316
+ if (!config.extension) {
2317
+ throw new Error("Extension is required");
2318
+ }
2319
+ if (!config.tableName) {
2320
+ throw new Error("Table name is required");
2321
+ }
2322
+ if (!config.fileNameValidator || typeof config.fileNameValidator !== "function") {
2323
+ throw new Error("File name validator function is required");
2324
+ }
2325
+ if (!config.fileNameVariablesExtractor || typeof config.fileNameVariablesExtractor !== "function") {
2326
+ throw new Error("File name variables extractor function is required");
2327
+ }
2328
+ if (config.type === "single-sheet-extraction" && (!config.columns || Object.keys(config.columns).length === 0)) {
2329
+ throw new Error("At least one column configuration is required for single-sheet-extraction");
2330
+ }
2331
+ if (config.type === "single-sheet-extraction") {
2332
+ Object.entries(config.columns).forEach(([key, column]) => {
2333
+ if (!column.type) {
2334
+ throw new Error(`Column ${key} must have a type`);
2335
+ }
2336
+ if (column.column) {
2337
+ const colLetter = column.column;
2338
+ if (!/^[A-Z]+$/i.test(colLetter)) {
2339
+ throw new Error(`Invalid column letter format: ${colLetter}`);
2340
+ }
2341
+ } else if (column.variableName) {
2342
+ } else {
2343
+ throw new Error(`Column ${key} must specify either 'column' or 'variableName'`);
2344
+ }
2345
+ });
2346
+ if (config.numberOfRowsToSkip === void 0 || config.numberOfRowsToSkip < 0) {
2347
+ throw new Error("Number of rows to skip must be specified and non-negative");
2348
+ }
2349
+ } else if (config.type === "processor") {
2350
+ if (!config.processor || typeof config.processor !== "function") {
2351
+ throw new Error("Processor function is required for processor type");
2352
+ }
2353
+ }
2354
+ };
2355
+ var findAllMatchingConfigs = (filename, configs) => {
2356
+ const fileInfo = path.parse(filename);
2357
+ return configs.filter(
2358
+ (config) => config.fileNameValidator(fileInfo.base) && config.extension === fileInfo.ext.replace(".", "")
2359
+ );
2360
+ };
2361
+ async function* createExcelGenerator(data) {
2362
+ for (const row of data) {
2363
+ yield row;
2364
+ }
2365
+ }
2366
+
2367
+ // src/sdk/file-processing/excel/config-builder.ts
2368
+ var ExcelExtractionConfigBuilder = class _ExcelExtractionConfigBuilder {
2369
+ config = {
2370
+ fileNameValidator: () => true,
2371
+ fileNameVariablesExtractor: () => ({}),
2372
+ replicationMethod: "FULL_TABLE" /* FULL_TABLE */
2373
+ };
2374
+ static create() {
2375
+ return new _ExcelExtractionConfigBuilder();
2376
+ }
2377
+ extension(ext) {
2378
+ this.config.extension = ext;
2379
+ return this;
2380
+ }
2381
+ tableName(name) {
2382
+ this.config.tableName = name;
2383
+ return this;
2384
+ }
2385
+ fileValidator(validator) {
2386
+ this.config.fileNameValidator = validator;
2387
+ return this;
2388
+ }
2389
+ variablesExtractor(extractor) {
2390
+ this.config.fileNameVariablesExtractor = extractor;
2391
+ return this;
2392
+ }
2393
+ replicationMethod(method) {
2394
+ this.config.replicationMethod = method;
2395
+ return this;
2396
+ }
2397
+ columns(cols) {
2398
+ if (this.config.type === "single-sheet-extraction") {
2399
+ this.config.columns = cols;
2400
+ } else {
2401
+ throw new Error("Columns can only be set for single-sheet-extraction");
2402
+ }
2403
+ return this;
2404
+ }
2405
+ singleSheet(sheetName, skipRows = 0) {
2406
+ this.config.type = "single-sheet-extraction";
2407
+ if (sheetName !== void 0) {
2408
+ this.config.sheetName = sheetName;
2409
+ }
2410
+ this.config.numberOfRowsToSkip = skipRows;
2411
+ return this;
2412
+ }
2413
+ processor(processorFn) {
2414
+ this.config.type = "processor";
2415
+ this.config.processor = processorFn;
2416
+ return this;
2417
+ }
2418
+ build() {
2419
+ if (!this.config.type) {
2420
+ throw new Error("Configuration type must be specified (singleSheet or processor)");
2421
+ }
2422
+ if (!this.config.extension) {
2423
+ throw new Error("Extension must be set");
2424
+ }
2425
+ if (!this.config.tableName) {
2426
+ throw new Error("Table name must be set");
2427
+ }
2428
+ if (!this.config.replicationMethod) {
2429
+ throw new Error("Replication method must be set");
2430
+ }
2431
+ if (typeof this.config.fileNameValidator !== "function") {
2432
+ throw new Error("fileNameValidator must be a function");
2433
+ }
2434
+ if (typeof this.config.fileNameVariablesExtractor !== "function") {
2435
+ throw new Error("fileNameVariablesExtractor must be a function");
2436
+ }
2437
+ if (this.config.type === "single-sheet-extraction") {
2438
+ const singleSheetConfig = this.config;
2439
+ if (!singleSheetConfig.columns || Object.keys(singleSheetConfig.columns).length === 0) {
2440
+ throw new Error("Columns must be set with at least one column for single-sheet-extraction");
2441
+ }
2442
+ }
2443
+ if (this.config.type === "processor") {
2444
+ const processorConfig = this.config;
2445
+ if (typeof processorConfig.processor !== "function") {
2446
+ throw new Error("Processor function must be set for processor configs");
2447
+ }
2448
+ }
2449
+ return this.config;
2450
+ }
2451
+ };
2452
+
2453
+ // src/sdk/file-processing/csv/reader.ts
2454
+ import csv from "csv-parser";
2455
+ import { createReadStream } from "fs";
2456
+ import { format as format4 } from "util";
2457
+ import { Readable, Transform } from "stream";
2458
+ import * as iconv from "iconv-lite";
2459
+ var logPrefix = "[csvReader]";
2460
+ var createStripBomTransform = () => {
2461
+ let isFirstChunk = true;
2462
+ return new Transform({
2463
+ transform(chunk2, encoding, callback) {
2464
+ if (isFirstChunk) {
2465
+ isFirstChunk = false;
2466
+ if (chunk2.length >= 3 && chunk2[0] === 239 && chunk2[1] === 187 && chunk2[2] === 191) {
2467
+ chunk2 = chunk2.slice(3);
2468
+ }
2469
+ }
2470
+ callback(null, chunk2);
2471
+ }
2472
+ });
2473
+ };
2474
+ var getStringHexInfo = (str) => {
2475
+ const bytes = Buffer.from(str, "utf8");
2476
+ const hex = bytes.toString("hex").match(/.{2}/g)?.join(" ") || "";
2477
+ return {
2478
+ original: str,
2479
+ length: str.length,
2480
+ hex,
2481
+ trimmed: str.trim()
2482
+ };
2483
+ };
2484
+ var formatHexComparison = (expected, actual) => {
2485
+ const expectedInfo = getStringHexInfo(expected);
2486
+ const actualInfo = getStringHexInfo(actual);
2487
+ return `
2488
+ Expected: "${expected}" (${expectedInfo.length} chars) [${expectedInfo.hex}]
2489
+ Actual: "${actual}" (${actualInfo.length} chars) [${actualInfo.hex}]`;
2490
+ };
2491
+ async function* rowGeneratorFromCsv(path2, fileConfig) {
2492
+ const isGeneratorConfigArray = Array.isArray(fileConfig.fields);
2493
+ const csvOptions = { separator: fileConfig.separator };
2494
+ if (isGeneratorConfigArray) {
2495
+ csvOptions.headers = false;
2496
+ }
2497
+ const csvStream = csv(csvOptions);
2498
+ const syncedAt = (/* @__PURE__ */ new Date()).toISOString();
2499
+ let initialReadStream;
2500
+ let finalInputStream;
2501
+ const encodingToUse = fileConfig.encoding ? fileConfig.encoding.toLowerCase() : null;
2502
+ if (encodingToUse && encodingToUse !== "utf-8" && encodingToUse !== "utf8" && iconv.encodingExists(encodingToUse)) {
2503
+ initialReadStream = createReadStream(path2);
2504
+ finalInputStream = initialReadStream.pipe(iconv.decodeStream(encodingToUse));
2505
+ } else {
2506
+ initialReadStream = createReadStream(path2);
2507
+ finalInputStream = initialReadStream.pipe(createStripBomTransform());
2508
+ }
2509
+ finalInputStream.pipe(csvStream);
2510
+ try {
2511
+ for await (const row of Readable.from(csvStream)) {
2512
+ if (!isGeneratorConfigArray) {
2513
+ const rowData = Object.keys(fileConfig.fields).reduce((acc, key) => {
2514
+ const keyGenerator = fileConfig.fields[key.trim()];
2515
+ if (!keyGenerator) {
2516
+ return acc;
2517
+ }
2518
+ return {
2519
+ ...acc,
2520
+ [keyGenerator.key]: keyGenerator.valueTransformer ? keyGenerator.valueTransformer(row[key]) : row[key]
2521
+ };
2522
+ }, fileConfig.addSyncedAtColumn ? { _wly_synced_at: syncedAt } : {});
2523
+ yield rowData;
2524
+ } else {
2525
+ const rowData = Object.keys(row).reduce((acc, key, i) => {
2526
+ const keyGenerator = fileConfig.fields[i];
2527
+ if (!keyGenerator) {
2528
+ return acc;
2529
+ }
2530
+ return {
2531
+ ...acc,
2532
+ [keyGenerator.key]: keyGenerator.valueTransformer ? keyGenerator.valueTransformer(row[i]) : row[i]
2533
+ };
2534
+ }, fileConfig.addSyncedAtColumn ? { _wly_synced_at: syncedAt } : {});
2535
+ yield rowData;
2536
+ }
2537
+ }
2538
+ } finally {
2539
+ initialReadStream.destroy();
2540
+ }
2541
+ }
2542
+ var checkCsvHeaderRow = async (path2, fileConfig) => {
2543
+ return new Promise((resolve, reject) => {
2544
+ const colsWithParsingConfig = Object.keys(fileConfig.fields).map((key) => key.trim());
2545
+ console.debug(`${logPrefix} CSV Expected columns:`, colsWithParsingConfig.map((col) => {
2546
+ const info = getStringHexInfo(col);
2547
+ return `"${info.original}" [${info.hex}]`;
2548
+ }));
2549
+ const isGeneratorConfigArray = Array.isArray(fileConfig.fields);
2550
+ const csvOptions2 = { separator: fileConfig.separator };
2551
+ if (isGeneratorConfigArray) {
2552
+ csvOptions2.headers = false;
2553
+ }
2554
+ const csvStream = csv(csvOptions2);
2555
+ let initialReadStream;
2556
+ let finalInputStream;
2557
+ const encodingToUse = fileConfig.encoding ? fileConfig.encoding.toLowerCase() : null;
2558
+ if (encodingToUse && encodingToUse !== "utf-8" && encodingToUse !== "utf8" && iconv.encodingExists(encodingToUse)) {
2559
+ initialReadStream = createReadStream(path2);
2560
+ finalInputStream = initialReadStream.pipe(iconv.decodeStream(encodingToUse));
2561
+ } else {
2562
+ initialReadStream = createReadStream(path2);
2563
+ finalInputStream = initialReadStream.pipe(createStripBomTransform());
2564
+ }
2565
+ let isFirstLine = true;
2566
+ finalInputStream.pipe(csvStream).on("data", (row) => {
2567
+ if (isFirstLine) {
2568
+ let headers = [];
2569
+ if (isGeneratorConfigArray) {
2570
+ headers = Object.values(row).filter((r) => !!r);
2571
+ } else {
2572
+ headers = Object.keys(row).filter((r) => !!r);
2573
+ }
2574
+ console.debug(`${logPrefix} CSV Detected headers:`, headers.map((h, idx) => {
2575
+ const info = getStringHexInfo(h);
2576
+ return `[${idx}]: "${info.original}" [${info.hex}]`;
2577
+ }));
2578
+ if (isGeneratorConfigArray) {
2579
+ colsWithParsingConfig.forEach((col, i) => {
2580
+ if (col !== headers[i]) {
2581
+ const hexComparison = formatHexComparison(col, headers[i] || "<undefined>");
2582
+ console.error(`${logPrefix} CSV Header Mismatch:${hexComparison}`);
2583
+ throw new Error(format4(
2584
+ `CSV header mismatch at position %s: expected '%s' but got '%s' in file %s`,
2585
+ i,
2586
+ col,
2587
+ headers[i] || "<undefined>",
2588
+ path2
2589
+ ));
2590
+ }
2591
+ });
2592
+ } else {
2593
+ colsWithParsingConfig.forEach((col) => {
2594
+ if (!headers.includes(col)) {
2595
+ const colInfo = getStringHexInfo(col);
2596
+ console.error(`CSV Missing column: "${col}" [${colInfo.hex}]`);
2597
+ console.error(`Available headers:`, headers.map((h) => `"${h}" [${getStringHexInfo(h).hex}]`));
2598
+ const trimmedMatch = headers.find((h) => h.trim() === col.trim());
2599
+ if (trimmedMatch) {
2600
+ console.error(`Potential trimmed match: "${trimmedMatch}" [${getStringHexInfo(trimmedMatch).hex}]`);
2601
+ }
2602
+ throw new Error(format4(
2603
+ `CSV missing expected column '%s' in file: %s`,
2604
+ col,
2605
+ path2
2606
+ ));
2607
+ }
2608
+ });
2609
+ }
2610
+ isFirstLine = false;
2611
+ } else {
2612
+ initialReadStream.destroy();
2613
+ }
2614
+ }).on("error", (error) => {
2615
+ reject(new Error(`Error processing CSV file: ${error.message}`));
2616
+ });
2617
+ initialReadStream.on("close", () => {
2618
+ resolve();
2619
+ });
2620
+ });
2621
+ };
2622
+
2623
+ // src/sdk/file-processing/csv/writer.ts
2624
+ import { createObjectCsvWriter } from "csv-writer";
2625
+ import { format as format5 } from "util";
2626
+ var logPrefix2 = "[csvWriter]";
2627
+ var writeDataToCsv = async (fileName, data) => {
2628
+ try {
2629
+ const firstRow = data[0];
2630
+ if (!firstRow) {
2631
+ console.log(`${logPrefix2} No data to write to CSV file=%s`, fileName);
2632
+ return;
2633
+ }
2634
+ const csvWriter = createObjectCsvWriter({
2635
+ path: fileName,
2636
+ header: Object.keys(firstRow).map((col) => {
2637
+ return { id: col, title: col };
2638
+ })
2639
+ });
2640
+ await csvWriter.writeRecords(data);
2641
+ console.log(`${logPrefix2} CSV file=%s written successfully`, fileName);
2642
+ } catch (err) {
2643
+ if (err instanceof Error) {
2644
+ throw new Error(format5(`error while writing csv file=%s, err: %s`, fileName, err.message));
2645
+ }
2646
+ throw err;
2647
+ }
2648
+ };
2649
+ var writeGeneratorToCSV = async (generator, outputFileName) => {
2650
+ try {
2651
+ let rowCount = 0;
2652
+ const firstDataRow = (await generator.next()).value;
2653
+ if (!firstDataRow) {
2654
+ console.log(`${logPrefix2} [%s] first data row of csv empty, skipping`, outputFileName);
2655
+ return 0;
2656
+ }
2657
+ console.log(`${logPrefix2} [%s] inferring headers from first data row`, outputFileName);
2658
+ const headers = Object.keys(firstDataRow).map((col) => {
2659
+ return {
2660
+ id: col,
2661
+ title: col
2662
+ };
2663
+ });
2664
+ const csvWriter = createObjectCsvWriter({
2665
+ path: outputFileName,
2666
+ header: headers,
2667
+ alwaysQuote: true
2668
+ });
2669
+ console.log(`${logPrefix2} [%s] writing csv`, outputFileName);
2670
+ let batch = [firstDataRow];
2671
+ for await (const data of generator) {
2672
+ batch.push(data);
2673
+ rowCount++;
2674
+ if (batch.length > 1e4) {
2675
+ try {
2676
+ await csvWriter.writeRecords(batch);
2677
+ } catch (err) {
2678
+ if (err instanceof Error) {
2679
+ throw new Error(
2680
+ format5(
2681
+ `error while writing batch to csv batch (first 3 records)=%j, err: %s`,
2682
+ batch.slice(0, 3),
2683
+ err.message
2684
+ )
2685
+ );
2686
+ }
2687
+ throw err;
2688
+ }
2689
+ batch = [];
2690
+ }
2691
+ }
2692
+ await csvWriter.writeRecords(batch);
2693
+ console.log(`${logPrefix2} [%s] csv written successfully with %d rows`, outputFileName, rowCount);
2694
+ return rowCount;
2695
+ } catch (err) {
2696
+ if (err instanceof Error) {
2697
+ throw new Error(format5(`error while writing csv file=%s, err: %s`, outputFileName, err.message));
2698
+ }
2699
+ throw err;
2700
+ }
2701
+ };
2702
+
2703
+ // src/sdk/file-processing/file-stream.ts
2704
+ import XLSX2 from "xlsx";
2705
+ var FileStream = class extends Stream {
2706
+ localFilePaths;
2707
+ constructor(config, localFilePath, tapState, target) {
2708
+ super(config, tapState, target);
2709
+ this.localFilePaths = Array.isArray(localFilePath) ? localFilePath : [localFilePath];
2710
+ this.streamId = config.streamId;
2711
+ this.primaryKey = config.primaryKeys;
2712
+ this.replicationMethod = config.replicationMethod;
2713
+ }
2714
+ /**
2715
+ * Build JSON Schema from the file config's field definitions.
2716
+ */
2717
+ async getSchema() {
2718
+ if (this.config.format === "EXCEL" /* EXCEL */) {
2719
+ const excelConfig = this.config.excel;
2720
+ if (excelConfig.type === "single-sheet-extraction") {
2721
+ const jsonSchema = excelFieldsToJsonSchema(excelConfig.columns);
2722
+ return { jsonSchema };
2723
+ }
2724
+ return void 0;
2725
+ }
2726
+ if (this.config.format === "CSV" /* CSV */) {
2727
+ const jsonSchema = csvFieldsToJsonSchema(this.config.csv.fields);
2728
+ return { jsonSchema };
2729
+ }
2730
+ throw new Error(`FileStream: No valid format config for stream ${this.streamId}`);
2731
+ }
2732
+ /**
2733
+ * Yield records from all file(s).
2734
+ * When multiple file paths are provided, records are yielded sequentially from each file.
2735
+ */
2736
+ async *_getRecords() {
2737
+ for (const filePath of this.localFilePaths) {
2738
+ if (this.config.format === "EXCEL" /* EXCEL */) {
2739
+ const data = await extractExcelRows(filePath, this.config.excel);
2740
+ for (const row of data) {
2741
+ yield row;
2742
+ }
2743
+ } else if (this.config.format === "CSV" /* CSV */) {
2744
+ yield* rowGeneratorFromCsv(filePath, this.config.csv);
2745
+ } else {
2746
+ throw new Error(`FileStream: Unsupported format for stream ${this.streamId}`);
2747
+ }
2748
+ }
2749
+ }
2750
+ };
2751
+ function createExcelStreamConfig(excelConfig, fileName, localFilePath) {
2752
+ if (excelConfig.type === "single-sheet-extraction") {
2753
+ const primaryKeys = extractPrimaryKeysFromExcelFields(excelConfig.columns);
2754
+ let tableName2;
2755
+ if (typeof excelConfig.tableName === "function") {
2756
+ const variables = excelConfig.fileNameVariablesExtractor(fileName);
2757
+ tableName2 = excelConfig.tableName(fileName, void 0, variables);
2758
+ } else {
2759
+ tableName2 = excelConfig.tableName;
2760
+ }
2761
+ return {
2762
+ format: "EXCEL" /* EXCEL */,
2763
+ streamId: tableName2,
2764
+ replicationMethod: excelConfig.replicationMethod,
2765
+ primaryKeys,
2766
+ excel: excelConfig
2767
+ };
2768
+ }
2769
+ let tableName;
2770
+ if (typeof excelConfig.tableName === "function") {
2771
+ if (!localFilePath) {
2772
+ throw new Error("localFilePath is required for processor configs with dynamic table names");
2773
+ }
2774
+ const workbook = XLSX2.readFile(localFilePath, { dense: true });
2775
+ const variables = excelConfig.fileNameVariablesExtractor(fileName, workbook);
2776
+ tableName = excelConfig.tableName(fileName, workbook, variables);
2777
+ } else {
2778
+ tableName = excelConfig.tableName;
2779
+ }
2780
+ return {
2781
+ format: "EXCEL" /* EXCEL */,
2782
+ streamId: tableName,
2783
+ replicationMethod: excelConfig.replicationMethod,
2784
+ primaryKeys: [],
2785
+ excel: excelConfig
2786
+ };
2787
+ }
2788
+ function createCsvStreamConfig(streamId, csvConfig, options) {
2789
+ return {
2790
+ format: "CSV" /* CSV */,
2791
+ streamId,
2792
+ replicationMethod: options?.replicationMethod ?? "FULL_TABLE" /* FULL_TABLE */,
2793
+ primaryKeys: options?.primaryKeys ?? extractPrimaryKeysFromCsvConfig(csvConfig.fields),
2794
+ csv: csvConfig
2795
+ };
2796
+ }
2797
+ async function processFileStreams(entries, tapState, target) {
2798
+ for (const entry of entries) {
2799
+ const stream = new FileStream(entry.config, entry.filePath, tapState, target);
2800
+ await stream.sync();
2801
+ }
2802
+ await target.complete();
2803
+ }
2804
+
2805
+ // src/sdk/file-processing/csv/config-builder.ts
2806
+ var CsvExtractionConfigBuilder = class _CsvExtractionConfigBuilder {
2807
+ _separator = ",";
2808
+ _encoding;
2809
+ _addSyncedAtColumn;
2810
+ _fields;
2811
+ _fieldsMode;
2812
+ _replicationMethod;
2813
+ _primaryKeys;
2814
+ static create() {
2815
+ return new _CsvExtractionConfigBuilder();
2816
+ }
2817
+ separator(sep) {
2818
+ this._separator = sep;
2819
+ return this;
2820
+ }
2821
+ encoding(enc) {
2822
+ this._encoding = enc;
2823
+ return this;
2824
+ }
2825
+ addSyncedAtColumn(enabled = true) {
2826
+ this._addSyncedAtColumn = enabled;
2827
+ return this;
2828
+ }
2829
+ fieldsFromDict(mapping) {
2830
+ if (this._fieldsMode === "array") {
2831
+ throw new Error("Cannot use fieldsFromDict after fieldsFromArray");
2832
+ }
2833
+ this._fieldsMode = "dict";
2834
+ this._fields = mapping;
2835
+ return this;
2836
+ }
2837
+ fieldsFromArray(fields) {
2838
+ if (this._fieldsMode === "dict") {
2839
+ throw new Error("Cannot use fieldsFromArray after fieldsFromDict");
2840
+ }
2841
+ this._fieldsMode = "array";
2842
+ this._fields = fields;
2843
+ return this;
2844
+ }
2845
+ replicationMethod(method) {
2846
+ this._replicationMethod = method;
2847
+ return this;
2848
+ }
2849
+ primaryKeys(keys) {
2850
+ this._primaryKeys = keys;
2851
+ return this;
2852
+ }
2853
+ build() {
2854
+ if (!this._fields) {
2855
+ throw new Error("Fields must be configured (use fieldsFromDict or fieldsFromArray)");
2856
+ }
2857
+ if (Array.isArray(this._fields) && this._fields.length === 0) {
2858
+ throw new Error("Fields must not be empty");
2859
+ }
2860
+ if (!Array.isArray(this._fields) && Object.keys(this._fields).length === 0) {
2861
+ throw new Error("Fields must not be empty");
2862
+ }
2863
+ const config = {
2864
+ separator: this._separator,
2865
+ fields: this._fields
2866
+ };
2867
+ if (this._encoding !== void 0) {
2868
+ config.encoding = this._encoding;
2869
+ }
2870
+ if (this._addSyncedAtColumn !== void 0) {
2871
+ config.addSyncedAtColumn = this._addSyncedAtColumn;
2872
+ }
2873
+ return config;
2874
+ }
2875
+ buildStreamConfig(streamId) {
2876
+ const csvConfig = this.build();
2877
+ const options = {};
2878
+ if (this._replicationMethod !== void 0) {
2879
+ options.replicationMethod = this._replicationMethod;
2880
+ }
2881
+ if (this._primaryKeys !== void 0) {
2882
+ options.primaryKeys = this._primaryKeys;
2883
+ }
2884
+ return createCsvStreamConfig(streamId, csvConfig, options);
2885
+ }
2886
+ };
2887
+
2888
+ // src/sdk/file-processing/file-tap.ts
2889
+ var FileTap = class _FileTap extends Tap {
2890
+ entries;
2891
+ constructor(target, config, stateProvider, entries) {
2892
+ super(target, config, stateProvider);
2893
+ this.entries = entries;
2894
+ }
2895
+ /**
2896
+ * Convenience constructor for the common case where all configs share the same file path.
2897
+ */
2898
+ static fromConfigs(target, config, stateProvider, fileConfigs, sharedFilePath) {
2899
+ const entries = fileConfigs.map((c) => ({
2900
+ config: c,
2901
+ filePath: sharedFilePath
2902
+ }));
2903
+ return new _FileTap(target, config, stateProvider, entries);
2904
+ }
2905
+ async init() {
2906
+ for (const entry of this.entries) {
2907
+ const stream = new FileStream(
2908
+ entry.config,
2909
+ entry.filePath,
2910
+ this.tapState,
2911
+ this.target
2912
+ );
2913
+ this.streams.push(stream);
2914
+ }
2915
+ }
2916
+ };
2917
+
2918
+ // src/sdk/file-processing/file-patterns.ts
2919
+ var VariableExtractors = class {
2920
+ /**
2921
+ * Returns the filename as a variable.
2922
+ */
2923
+ static filename() {
2924
+ return (filename) => ({
2925
+ fileName: filename
2926
+ });
2927
+ }
2928
+ /**
2929
+ * Extracts named capture groups from a regex pattern.
2930
+ */
2931
+ static regex(pattern) {
2932
+ return (filename) => {
2933
+ const match = filename.match(pattern);
2934
+ return match?.groups ?? {};
2935
+ };
2936
+ }
2937
+ /**
2938
+ * Combines multiple extraction functions.
2939
+ */
2940
+ static combine(...extractors) {
2941
+ return (filename) => {
2942
+ return extractors.reduce((acc, extractor) => {
2943
+ return { ...acc, ...extractor(filename) };
2944
+ }, {});
2945
+ };
2946
+ }
2947
+ };
2948
+ var FilePatterns = class {
2949
+ /**
2950
+ * Creates a validator for files starting with a specific prefix (case-insensitive).
2951
+ */
2952
+ static startsWith(prefix) {
2953
+ return (filename) => filename.toLowerCase().startsWith(prefix.toLowerCase());
2954
+ }
2955
+ /**
2956
+ * Creates a validator for files matching a regex pattern.
2957
+ */
2958
+ static regex(pattern) {
2959
+ return (filename) => pattern.test(filename);
2960
+ }
2961
+ /**
2962
+ * Combines multiple validators with AND logic.
2963
+ */
2964
+ static and(...validators) {
2965
+ return (filename) => validators.every((validator) => validator(filename));
2966
+ }
2967
+ /**
2968
+ * Combines multiple validators with OR logic.
2969
+ */
2970
+ static or(...validators) {
2971
+ return (filename) => validators.some((validator) => validator(filename));
2972
+ }
2973
+ };
2974
+
2975
+ // src/services/sftp.ts
2976
+ import Client from "ssh2-sftp-client";
2977
+ import { default as default2 } from "ssh2-sftp-client";
2978
+ var SftpService = new Client();
2979
+
2980
+ // src/services/cloud-storage.ts
2981
+ import { Storage } from "@google-cloud/storage";
2982
+ import { format as format6 } from "util";
2983
+ import * as pathModule from "path";
2984
+ import { existsSync, mkdirSync, unlinkSync as unlinkSync2 } from "fs";
2985
+
2986
+ // node_modules/uuid/dist/esm-node/rng.js
2987
+ import crypto from "crypto";
2988
+ var rnds8Pool = new Uint8Array(256);
2989
+ var poolPtr = rnds8Pool.length;
2990
+ function rng() {
2991
+ if (poolPtr > rnds8Pool.length - 16) {
2992
+ crypto.randomFillSync(rnds8Pool);
2993
+ poolPtr = 0;
2994
+ }
2995
+ return rnds8Pool.slice(poolPtr, poolPtr += 16);
2996
+ }
2997
+
2998
+ // node_modules/uuid/dist/esm-node/regex.js
2999
+ var regex_default = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;
3000
+
3001
+ // node_modules/uuid/dist/esm-node/validate.js
3002
+ function validate(uuid) {
3003
+ return typeof uuid === "string" && regex_default.test(uuid);
3004
+ }
3005
+ var validate_default = validate;
3006
+
3007
+ // node_modules/uuid/dist/esm-node/stringify.js
3008
+ var byteToHex = [];
3009
+ for (let i = 0; i < 256; ++i) {
3010
+ byteToHex.push((i + 256).toString(16).substr(1));
3011
+ }
3012
+ function stringify3(arr, offset = 0) {
3013
+ const uuid = (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
3014
+ if (!validate_default(uuid)) {
3015
+ throw TypeError("Stringified UUID is invalid");
3016
+ }
3017
+ return uuid;
3018
+ }
3019
+ var stringify_default = stringify3;
3020
+
3021
+ // node_modules/uuid/dist/esm-node/v4.js
3022
+ function v4(options, buf, offset) {
3023
+ options = options || {};
3024
+ const rnds = options.random || (options.rng || rng)();
3025
+ rnds[6] = rnds[6] & 15 | 64;
3026
+ rnds[8] = rnds[8] & 63 | 128;
3027
+ if (buf) {
3028
+ offset = offset || 0;
3029
+ for (let i = 0; i < 16; ++i) {
3030
+ buf[offset + i] = rnds[i];
3031
+ }
3032
+ return buf;
3033
+ }
3034
+ return stringify_default(rnds);
3035
+ }
3036
+ var v4_default = v4;
3037
+
3038
+ // src/services/cloud-storage.ts
3039
+ var logPrefix3 = "[CloudStorageService]";
3040
+ var tmpDir = "tmp";
3041
+ var CloudStorageService = class {
3042
+ storage;
3043
+ bucket;
3044
+ processedSuffix;
3045
+ supportedExtensions;
3046
+ path;
3047
+ constructor(bucketName, path2, opts) {
3048
+ this.storage = new Storage({ retryOptions: { autoRetry: true, maxRetries: 20 } });
3049
+ this.bucket = this.storage.bucket(bucketName);
3050
+ this.path = path2;
3051
+ this.processedSuffix = opts?.processedSuffix ?? ".processed";
3052
+ this.supportedExtensions = opts?.supportedExtensions ?? [];
3053
+ }
3054
+ /**
3055
+ * Lists all files in the bucket with an optional prefix.
3056
+ */
3057
+ async listFiles(prefix) {
3058
+ const effectivePrefix = prefix || this.path;
3059
+ const [files] = await this.bucket.getFiles(
3060
+ effectivePrefix ? { prefix: effectivePrefix } : void 0
3061
+ );
3062
+ return files.map((f) => f.name);
3063
+ }
3064
+ /**
3065
+ * Returns files that haven't been marked as processed.
3066
+ * Filters by supported extensions if configured.
3067
+ */
3068
+ async getUnprocessedFiles() {
3069
+ const allFiles = await this.listFiles();
3070
+ const markerFiles = new Set(
3071
+ allFiles.filter((f) => f.endsWith(this.processedSuffix)).map((f) => f.replace(this.processedSuffix, ""))
3072
+ );
3073
+ return allFiles.filter((f) => {
3074
+ if (f.endsWith(this.processedSuffix)) return false;
3075
+ if (f.endsWith("/")) return false;
3076
+ if (this.supportedExtensions.length > 0) {
3077
+ const ext = pathModule.extname(f).toLowerCase();
3078
+ if (!this.supportedExtensions.includes(ext)) return false;
3079
+ }
3080
+ return !markerFiles.has(f);
3081
+ });
3082
+ }
3083
+ /**
3084
+ * Creates a marker file to indicate a file has been processed.
3085
+ */
3086
+ async createMarkerFile(fileName) {
3087
+ const markerFileName = `${fileName}${this.processedSuffix}`;
3088
+ try {
3089
+ const file = this.bucket.file(markerFileName);
3090
+ await file.save(format6("Marked file %s as processed", fileName));
3091
+ logger.info(`${logPrefix3} Marker file ${markerFileName} created successfully.`);
3092
+ } catch (err) {
3093
+ if (err instanceof Error) {
3094
+ throw new Error(format6(`error while writing marker file=%s, err:%s`, markerFileName, err.message));
3095
+ }
3096
+ throw err;
3097
+ }
3098
+ }
3099
+ /**
3100
+ * Downloads a file from GCS to a local tmp directory.
3101
+ * Returns the local path of the downloaded file.
3102
+ */
3103
+ async downloadFile(filePath, fileName) {
3104
+ const file = this.bucket.file(filePath);
3105
+ const tmpDirPath = pathModule.join(process.cwd(), tmpDir);
3106
+ if (!existsSync(tmpDirPath)) {
3107
+ mkdirSync(tmpDirPath);
3108
+ }
3109
+ const destFilename = pathModule.join(process.cwd(), tmpDir, fileName);
3110
+ try {
3111
+ if (existsSync(destFilename)) {
3112
+ unlinkSync2(destFilename);
3113
+ }
3114
+ await file.download({ destination: destFilename });
3115
+ logger.info(`${logPrefix3} Downloaded ${fileName} to ${destFilename}`);
3116
+ return destFilename;
3117
+ } catch (err) {
3118
+ if (err instanceof Error) {
3119
+ throw new Error(format6(
3120
+ `can't download file=%s from bucket, err:%s`,
3121
+ fileName,
3122
+ err.message
3123
+ ));
3124
+ }
3125
+ throw err;
3126
+ }
3127
+ }
3128
+ /**
3129
+ * Uploads a local file to GCS.
3130
+ */
3131
+ async uploadFile(localPath, destPath) {
3132
+ try {
3133
+ logger.info(`${logPrefix3} preparing to upload '%s' to '%s'`, localPath, destPath);
3134
+ const [file] = await this.bucket.upload(localPath, { destination: destPath });
3135
+ logger.info(`${logPrefix3} file %s has been successfully uploaded into %s`, localPath, destPath);
3136
+ return file;
3137
+ } catch (err) {
3138
+ if (err instanceof Error) {
3139
+ throw new Error(format6(`error while uploading file file=%s into path=%s, err:%s`, localPath, destPath, err.message));
3140
+ }
3141
+ throw err;
3142
+ }
3143
+ }
3144
+ /**
3145
+ * Reads a GCS object and returns its contents as a UTF-8 string.
3146
+ */
3147
+ async readObjectAsString(objectPath) {
3148
+ try {
3149
+ await this.bucket.get({ autoCreate: false });
3150
+ const fileRef = this.bucket.file(objectPath);
3151
+ const [exists] = await fileRef.exists();
3152
+ if (!exists) {
3153
+ throw new Error(`GCS object not found: gs://${this.bucket.name}/${objectPath}`);
3154
+ }
3155
+ const [contents] = await fileRef.download();
3156
+ return contents.toString("utf8");
3157
+ } catch (err) {
3158
+ throw new Error(format6(`error reading GCS object gs://${this.bucket.name}/${objectPath}, err: %s`, err?.message));
3159
+ }
3160
+ }
3161
+ /**
3162
+ * Writes a string to a GCS object with JSON content type.
3163
+ */
3164
+ async writeStringObject(objectPath, contents) {
3165
+ try {
3166
+ await this.bucket.get({ autoCreate: false });
3167
+ const fileRef = this.bucket.file(objectPath);
3168
+ await fileRef.save(contents, { contentType: "application/json" });
3169
+ logger.info(`Uploaded object to gs://${this.bucket.name}/${objectPath}`);
3170
+ } catch (err) {
3171
+ throw new Error(format6(`error writing GCS object gs://${this.bucket.name}/${objectPath}, err: %s`, err?.message));
3172
+ }
3173
+ }
3174
+ /**
3175
+ * Uploads a local file to the bucket with a unique name based on prefix, streamId, and UUID.
3176
+ * Returns the GCS File reference.
3177
+ */
3178
+ async uploadFileWithUniqueName(filePath, prefix, streamId) {
3179
+ try {
3180
+ await this.bucket.get({ autoCreate: false });
3181
+ const destinationFileName = `${prefix}/${streamId}-${v4_default()}.jsonnl`;
3182
+ await this.bucket.upload(filePath, {
3183
+ destination: destinationFileName
3184
+ });
3185
+ logger.info(`Uploaded ${filePath} into ${this.bucket.name}/${destinationFileName} GCS File`);
3186
+ return this.bucket.file(destinationFileName);
3187
+ } catch (err) {
3188
+ logger.error(`Issue when uploading file into GCS bucket for stream: ${streamId}
3189
+
3190
+ Error: ${err.message}
3191
+ Stack: ${err.stack}
3192
+ Code: ${err.code}
3193
+ `);
3194
+ throw err;
2437
3195
  }
2438
- });
2439
- returnString = returnString.replace(/^[-\d\s]*/g, "");
2440
- returnString = returnString.toLowerCase();
2441
- returnString = returnString.replace("`", "");
2442
- returnString = returnString.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
2443
- if (returnString.length > 300) {
2444
- returnString = returnString.substr(0, 300);
2445
3196
  }
2446
- const pattern = /[^a-zA-Z0-9_]/gm;
2447
- return returnString.replace(pattern, "_");
3197
+ };
3198
+
3199
+ // src/services/zip.ts
3200
+ import * as fse from "fs-extra";
3201
+ import decompress from "decompress";
3202
+ var unzip = async (zipFilePath, extractedPath) => {
3203
+ await fse.ensureDir(extractedPath);
3204
+ await decompress(zipFilePath, extractedPath);
3205
+ return extractedPath;
3206
+ };
3207
+
3208
+ // src/targets/bigquery/helpers.ts
3209
+ var safeColumnName = (key) => {
3210
+ let returnString = key;
3211
+ const shouldNotStartWith = ["_TABLE_", "_FILE_", "_PARTITION"];
3212
+ shouldNotStartWith.forEach((snm) => {
3213
+ if (returnString.startsWith(snm)) {
3214
+ returnString = returnString.replace(snm, "");
3215
+ }
3216
+ });
3217
+ returnString = returnString.replace(/^[-\d\s]*/g, "");
3218
+ returnString = returnString.toLowerCase();
3219
+ returnString = returnString.replace("`", "");
3220
+ returnString = returnString.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
3221
+ if (returnString.length > 300) {
3222
+ returnString = returnString.substr(0, 300);
3223
+ }
3224
+ const pattern = /[^a-zA-Z0-9_]/gm;
3225
+ return returnString.replace(pattern, "_");
2448
3226
  };
2449
3227
  var safeTableName = (key) => {
2450
3228
  let returnString = key;
@@ -2460,15 +3238,15 @@ var safeTableName = (key) => {
2460
3238
 
2461
3239
  // src/targets/bigquery/service/record.ts
2462
3240
  import Decimal from "decimal.js";
2463
- import moment5 from "moment";
3241
+ import dayjs5 from "dayjs";
2464
3242
  var validateDateRange = (record, schema) => {
2465
3243
  Object.keys(schema).forEach((key) => {
2466
- const { format: format3 } = schema[key];
2467
- if (format3 === "date-time") {
3244
+ const { format: format8 } = schema[key];
3245
+ if (format8 === "date-time") {
2468
3246
  if (record[key]) {
2469
- const formattedDate = moment5(record[key]).format(defaultDateTimeFormat);
2470
- const isNotTooMuchInTheFuture = moment5(record[key]).isBefore(moment5("9999-12-31 23:59:59.999999"));
2471
- const isNotTooMuchInThePast = moment5(record[key]).isAfter(moment5("0001-01-01 00:00:00"));
3247
+ const formattedDate = dayjs5(record[key]).format(defaultDateTimeFormat);
3248
+ const isNotTooMuchInTheFuture = dayjs5(record[key]).isBefore(dayjs5("9999-12-31 23:59:59.999999"));
3249
+ const isNotTooMuchInThePast = dayjs5(record[key]).isAfter(dayjs5("0001-01-01 00:00:00"));
2472
3250
  if (formattedDate !== "Invalid date" && isNotTooMuchInTheFuture && isNotTooMuchInThePast) {
2473
3251
  record[key] = formattedDate;
2474
3252
  } else {
@@ -2523,7 +3301,7 @@ var convertNumberIntoDecimal = (record, schema) => {
2523
3301
  };
2524
3302
 
2525
3303
  // src/targets/bigquery/service/dbSync.ts
2526
- import moment6 from "moment";
3304
+ import dayjs6 from "dayjs";
2527
3305
 
2528
3306
  // src/targets/bigquery/service/bigquery.ts
2529
3307
  import { BigQuery } from "@google-cloud/bigquery";
@@ -2548,96 +3326,6 @@ var BqClientHolder = class {
2548
3326
  }
2549
3327
  };
2550
3328
 
2551
- // src/targets/bigquery/service/cloudStorage.ts
2552
- import { Storage } from "@google-cloud/storage";
2553
-
2554
- // node_modules/uuid/dist/esm-node/rng.js
2555
- import crypto from "crypto";
2556
- var rnds8Pool = new Uint8Array(256);
2557
- var poolPtr = rnds8Pool.length;
2558
- function rng() {
2559
- if (poolPtr > rnds8Pool.length - 16) {
2560
- crypto.randomFillSync(rnds8Pool);
2561
- poolPtr = 0;
2562
- }
2563
- return rnds8Pool.slice(poolPtr, poolPtr += 16);
2564
- }
2565
-
2566
- // node_modules/uuid/dist/esm-node/regex.js
2567
- var regex_default = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;
2568
-
2569
- // node_modules/uuid/dist/esm-node/validate.js
2570
- function validate(uuid) {
2571
- return typeof uuid === "string" && regex_default.test(uuid);
2572
- }
2573
- var validate_default = validate;
2574
-
2575
- // node_modules/uuid/dist/esm-node/stringify.js
2576
- var byteToHex = [];
2577
- for (let i = 0; i < 256; ++i) {
2578
- byteToHex.push((i + 256).toString(16).substr(1));
2579
- }
2580
- function stringify3(arr, offset = 0) {
2581
- const uuid = (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
2582
- if (!validate_default(uuid)) {
2583
- throw TypeError("Stringified UUID is invalid");
2584
- }
2585
- return uuid;
2586
- }
2587
- var stringify_default = stringify3;
2588
-
2589
- // node_modules/uuid/dist/esm-node/v4.js
2590
- function v4(options, buf, offset) {
2591
- options = options || {};
2592
- const rnds = options.random || (options.rng || rng)();
2593
- rnds[6] = rnds[6] & 15 | 64;
2594
- rnds[8] = rnds[8] & 63 | 128;
2595
- if (buf) {
2596
- offset = offset || 0;
2597
- for (let i = 0; i < 16; ++i) {
2598
- buf[offset + i] = rnds[i];
2599
- }
2600
- return buf;
2601
- }
2602
- return stringify_default(rnds);
2603
- }
2604
- var v4_default = v4;
2605
-
2606
- // src/targets/bigquery/service/cloudStorage.ts
2607
- var uploadFileInBucket = async (streamId, gcsBuckerName, filePath) => {
2608
- try {
2609
- const retryOptions = { autoRetry: true, maxRetries: 20 };
2610
- let storage = new Storage({ retryOptions });
2611
- const bucketRef = storage.bucket(gcsBuckerName);
2612
- await bucketRef.get({ autoCreate: false });
2613
- const destinationFileName = `${streamId}-${v4_default()}.jsonnl`;
2614
- await bucketRef.upload(filePath, {
2615
- destination: destinationFileName
2616
- });
2617
- logger.info(`\u{1F5F3} Uploaded ${filePath} into ${gcsBuckerName}/${destinationFileName} GCS File`);
2618
- return bucketRef.file(destinationFileName);
2619
- } catch (err) {
2620
- logger.error(`Issue when uploading file into GCS bucket for stream: ${streamId}
2621
-
2622
- Error: ${err.message}
2623
- Stack: ${err.stack}
2624
- Code: ${err.code}
2625
- `);
2626
- if (err.code < 500) {
2627
- await haltAndCatchFire(
2628
- `unauthorized`,
2629
- `We couldn't connect to your Cloud Storage bucket \u{1F614}
2630
-
2631
- The error from Google is: '${err.message}' \u{1F440}
2632
-
2633
- Could you troubleshoot your Cloud Storage configuration in Google Cloud and sync again the source? \u{1F64F}`,
2634
- `Got error from cloud storage lib`
2635
- );
2636
- }
2637
- throw err;
2638
- }
2639
- };
2640
-
2641
3329
  // src/targets/bigquery/service/dbSync.ts
2642
3330
  import chunk from "lodash/chunk.js";
2643
3331
  import { Semaphore as Semaphore2 } from "async-mutex";
@@ -2678,21 +3366,21 @@ var BigQueryDBSync = class extends StreamWarehouseSyncService {
2678
3366
  safeColumnName = safeColumnName;
2679
3367
  getWarehouseTypeFromJSONSchema = (definition) => {
2680
3368
  const type = definition.type;
2681
- const format3 = definition.format;
3369
+ const format8 = definition.format;
2682
3370
  let typeArr;
2683
3371
  if (type instanceof Array) {
2684
3372
  typeArr = type;
2685
3373
  } else {
2686
3374
  typeArr = [type];
2687
3375
  }
2688
- if (format3) {
2689
- switch (format3) {
3376
+ if (format8) {
3377
+ switch (format8) {
2690
3378
  case "date-time":
2691
3379
  return "TIMESTAMP";
2692
3380
  case "json":
2693
3381
  return "STRING";
2694
3382
  default:
2695
- throw new Error(`StreamId: ${this.streamId} - Unsupported format: ${format3}`);
3383
+ throw new Error(`StreamId: ${this.streamId} - Unsupported format: ${format8}`);
2696
3384
  }
2697
3385
  } else if (typeArr.includes("number")) {
2698
3386
  return "NUMERIC";
@@ -2788,6 +3476,13 @@ var BigQueryDBSync = class extends StreamWarehouseSyncService {
2788
3476
  ...this.getMergeQueries()
2789
3477
  ];
2790
3478
  };
3479
+ getAppendQueries = () => {
3480
+ const columnUnsafeToSafeMapping = this.renamedColumnStore.getUnsafeToSafeColumnMapping(this.streamId);
3481
+ const columns = Object.keys(columnUnsafeToSafeMapping).map((unsafeKey) => `\`${columnUnsafeToSafeMapping[unsafeKey]}\``).join(`, `);
3482
+ return [
3483
+ `INSERT INTO ${this.sqlTableId} (${columns}) SELECT ${columns} FROM ${this.sqlStagingTableId};`
3484
+ ];
3485
+ };
2791
3486
  createDatabaseAndSchemaIfNotExists = async (retryCount) => {
2792
3487
  try {
2793
3488
  await semaphore2.runExclusive(async () => {
@@ -2892,7 +3587,7 @@ var BigQueryDBSync = class extends StreamWarehouseSyncService {
2892
3587
  await this.deleteStagingArea();
2893
3588
  }
2894
3589
  const schema = this.generateBigquerySchema();
2895
- const expirationTime = moment6().add("1", "day").valueOf().toString();
3590
+ const expirationTime = dayjs6().add(1, "day").valueOf().toString();
2896
3591
  const createTableOptions = {
2897
3592
  schema,
2898
3593
  expirationTime
@@ -2912,10 +3607,11 @@ var BigQueryDBSync = class extends StreamWarehouseSyncService {
2912
3607
  };
2913
3608
  loadStreamInStagingArea = async (localFilePath) => {
2914
3609
  try {
2915
- const file = await uploadFileInBucket(
2916
- this.streamId,
2917
- this.config.loading_deck_gcs_bucket_name,
2918
- localFilePath
3610
+ const gcsService = new CloudStorageService(this.config.loading_deck_gcs_bucket_name);
3611
+ const file = await gcsService.uploadFileWithUniqueName(
3612
+ localFilePath,
3613
+ this.config.connector_id,
3614
+ this.streamId
2919
3615
  );
2920
3616
  const metadata = {
2921
3617
  sourceFormat: "NEWLINE_DELIMITED_JSON",
@@ -2924,6 +3620,17 @@ var BigQueryDBSync = class extends StreamWarehouseSyncService {
2924
3620
  await this.loadGCSFileInTable(this.sqlStagingTableId, file, metadata);
2925
3621
  } catch (err) {
2926
3622
  logger.error(`StreamId: ${this.streamId} - Error while uploading stream.`);
3623
+ if (err.code < 500) {
3624
+ await haltAndCatchFire(
3625
+ `unauthorized`,
3626
+ `We couldn't connect to your Cloud Storage bucket \u{1F614}
3627
+
3628
+ The error from Google is: '${err.message}' \u{1F440}
3629
+
3630
+ Could you troubleshoot your Cloud Storage configuration in Google Cloud and sync again the source? \u{1F64F}`,
3631
+ `Got error from cloud storage lib`
3632
+ );
3633
+ }
2927
3634
  throw err;
2928
3635
  }
2929
3636
  };
@@ -3026,8 +3733,8 @@ var BigQueryDBSync = class extends StreamWarehouseSyncService {
3026
3733
 
3027
3734
  // src/targets/bigquery/main.ts
3028
3735
  var BigQueryTarget = class extends ITarget {
3029
- constructor(config, resolver) {
3030
- super(config, resolver);
3736
+ constructor(config, stateProvider) {
3737
+ super(config, stateProvider);
3031
3738
  this.renameColumnStore.setSafeColumnNameConverter(this.safeColumnNameConverter);
3032
3739
  }
3033
3740
  static requiredConfigKeys = [
@@ -3050,15 +3757,61 @@ var BigQueryTarget = class extends ITarget {
3050
3757
  validateDateRange = validateDateRange;
3051
3758
  convertNumberIntoDecimal = convertNumberIntoDecimal;
3052
3759
  };
3760
+
3761
+ // src/state-providers/gcs/main.ts
3762
+ import { format as format7 } from "util";
3763
+ var GCSStateProvider = class {
3764
+ bucketName;
3765
+ connectorId;
3766
+ service;
3767
+ constructor(connectorId, bucketName) {
3768
+ this.connectorId = connectorId;
3769
+ this.bucketName = bucketName;
3770
+ this.service = new CloudStorageService(bucketName);
3771
+ }
3772
+ getObjectPath() {
3773
+ return `${this.connectorId}/state.json`;
3774
+ }
3775
+ async getState() {
3776
+ const objectPath = this.getObjectPath();
3777
+ try {
3778
+ logger.info(`\u{1F4C1} Loading state from gs://${this.bucketName}/${objectPath}`);
3779
+ const raw = await this.service.readObjectAsString(objectPath);
3780
+ const parsed = JSON.parse(raw);
3781
+ return { state: parsed };
3782
+ } catch (err) {
3783
+ logger.warn(format7(`error loading state from gs://${this.bucketName}/${objectPath}, will start with empty state, err: %s`, err?.message));
3784
+ return { state: void 0 };
3785
+ }
3786
+ }
3787
+ async writeState(state) {
3788
+ const objectPath = this.getObjectPath();
3789
+ try {
3790
+ logger.info(`\u{1F4DD} Writing state to gs://${this.bucketName}/${objectPath}`);
3791
+ await this.service.writeStringObject(objectPath, state);
3792
+ } catch (err) {
3793
+ logger.error(format7(`\u{1F4A5} Error while writing state to gs://${this.bucketName}/${objectPath}, err: %s`, err?.message));
3794
+ throw new Error(format7(`error writing state to gs://${this.bucketName}/${objectPath}, err: %s`, err?.message));
3795
+ }
3796
+ }
3797
+ };
3053
3798
  export {
3054
3799
  ALL_STREAM_ID_KEYWORD,
3055
3800
  API_CALLS_METRIC_NAME,
3056
3801
  Authenticator,
3057
3802
  BATCH_INTERVAL_MS,
3058
3803
  BigQueryTarget,
3804
+ CloudStorageService,
3059
3805
  CounterMetric,
3806
+ CsvExtractionConfigBuilder,
3060
3807
  DEFAULT_MAX_CONCURRENT_STREAMS,
3061
3808
  EXECUTION_TIME_METRIC_NAME,
3809
+ ExcelExtractionConfigBuilder,
3810
+ FileFormat,
3811
+ FilePatterns,
3812
+ FileStream,
3813
+ FileTap,
3814
+ GCSStateProvider,
3062
3815
  ITarget,
3063
3816
  MissingFieldInSchemaError,
3064
3817
  MissingSchemaError,
@@ -3066,22 +3819,37 @@ export {
3066
3819
  ROWS_SYNCED_METRIC_NAME,
3067
3820
  RecordMessage,
3068
3821
  RenameColumnStore,
3822
+ ReplicationMethod,
3069
3823
  ReplicationMethodMessage,
3070
- Resolver,
3071
3824
  SchemaMessage,
3072
3825
  SchemaValidationError,
3826
+ default2 as SftpClient,
3827
+ SftpService,
3073
3828
  StateMessage,
3074
3829
  StateService,
3075
3830
  Stream,
3076
- StreamMetadata,
3077
3831
  StreamWarehouseSyncService,
3078
3832
  Tap,
3833
+ VariableExtractors,
3079
3834
  _greaterThan,
3080
3835
  addWhalyFields,
3836
+ checkCsvHeaderRow,
3837
+ createCellReference,
3838
+ createCsvStreamConfig,
3839
+ createExcelGenerator,
3840
+ createExcelStreamConfig,
3081
3841
  createTemporaryFileStream,
3842
+ csvFieldsToJsonSchema,
3843
+ excelColumnToIndex,
3844
+ excelFieldsToJsonSchema,
3845
+ excelRowToIndex,
3846
+ extractExcelRows,
3847
+ extractPrimaryKeysFromCsvConfig,
3848
+ extractPrimaryKeysFromExcelFields,
3082
3849
  extractStateForStream,
3850
+ fieldTypeToJsonSchema,
3083
3851
  finalizeStateProgressMarkers,
3084
- findRelatedRelationships,
3852
+ findAllMatchingConfigs,
3085
3853
  flattenSchema,
3086
3854
  getAllMetrics,
3087
3855
  getAxiosInstance,
@@ -3094,16 +3862,23 @@ export {
3094
3862
  gracefulExit,
3095
3863
  haltAndCatchFire,
3096
3864
  incrementStreamState,
3865
+ indexToExcelColumn,
3866
+ indexToExcelRow,
3097
3867
  loadJson,
3098
- loadResolver,
3099
3868
  loadSchema,
3100
3869
  logger,
3870
+ parseCellReference,
3101
3871
  postFormDataApiCall,
3102
3872
  postJSONApiCall,
3103
3873
  postUrlEncodedApiCall,
3104
3874
  printMemoryFootprint,
3875
+ processFileStreams,
3105
3876
  removeParasiteProperties,
3877
+ rowGeneratorFromCsv,
3106
3878
  safePath,
3107
- writeStateFile
3879
+ unzip,
3880
+ validateExcelConfig,
3881
+ writeDataToCsv,
3882
+ writeGeneratorToCSV
3108
3883
  };
3109
3884
  //# sourceMappingURL=index.mjs.map