@gravity-rail/sdk 0.15.0 → 0.16.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.js CHANGED
@@ -65,6 +65,7 @@ import {
65
65
  PIDS_SOURCES,
66
66
  POSITIVE_RATING_THRESHOLD,
67
67
  ProductType,
68
+ PromoCodeStatus,
68
69
  QUERY_VALUELESS_OPS,
69
70
  QueriesApi,
70
71
  REALTIME_MODEL_CUSTOM,
@@ -137,7 +138,7 @@ import {
137
138
  roleWorkflowAccess,
138
139
  usesBuiltinAssistant,
139
140
  validateFeatureDependencies
140
- } from "./chunk-QTJQTTIR.js";
141
+ } from "./chunk-4B7T6MBH.js";
141
142
  import {
142
143
  AttendeeResponseStatus,
143
144
  AttendeeStatus,
@@ -195,6 +196,14 @@ var WorkspaceBuilderResource = class {
195
196
  }
196
197
  return this._uuid;
197
198
  }
199
+ /**
200
+ * True for resources the server creates on our behalf (currently a
201
+ * journey's `Start` step). They are kept in the resource list so edges can
202
+ * reference them, but they are never created, printed, or counted.
203
+ */
204
+ get isImplicit() {
205
+ return false;
206
+ }
198
207
  matchKey() {
199
208
  return void 0;
200
209
  }
@@ -217,6 +226,19 @@ function resolveRefValue(value) {
217
226
  }
218
227
  return out;
219
228
  }
229
+ function resolveIdRefs(value) {
230
+ return resolveIdRefValue(value);
231
+ }
232
+ function resolveIdRefValue(value) {
233
+ if (value instanceof WorkspaceBuilderResource) return value.id;
234
+ if (Array.isArray(value)) return value.map(resolveIdRefValue);
235
+ if (!isPlainRecord(value)) return value;
236
+ const out = {};
237
+ for (const [key, nestedValue] of Object.entries(value)) {
238
+ out[key] = resolveIdRefValue(nestedValue);
239
+ }
240
+ return out;
241
+ }
220
242
 
221
243
  // src/workspace-builder/automation.ts
222
244
  var MemberFilterBuilder = class extends WorkspaceBuilderResource {
@@ -230,7 +252,12 @@ var MemberFilterBuilder = class extends WorkspaceBuilderResource {
230
252
  description;
231
253
  /** Query-engine predicate (JSON IR) authored on MemberFilter.query. */
232
254
  query = null;
233
- /** Set the filter's query-engine predicate (JSON IR). */
255
+ /**
256
+ * Set the filter's query-engine predicate (JSON IR).
257
+ *
258
+ * Anywhere the IR wants a numeric id you may pass the builder that will own
259
+ * it (e.g. a label in a `member.labelId` value); refs resolve at apply time.
260
+ */
234
261
  setQuery(query) {
235
262
  this.query = query;
236
263
  return this;
@@ -239,10 +266,12 @@ var MemberFilterBuilder = class extends WorkspaceBuilderResource {
239
266
  return `MemberFilter: ${this.name}${this.query ? " (query)" : ""}`;
240
267
  }
241
268
  async apply(client) {
269
+ if (this._existed) return;
242
270
  const payload = {
243
271
  name: this.name,
244
272
  description: this.description,
245
- query: this.query
273
+ // Builder refs inside the IR become ids here; see resolveIdRefs.
274
+ query: resolveIdRefs(this.query)
246
275
  };
247
276
  const result = await client.createMemberFilter(payload);
248
277
  this._id = result.id;
@@ -537,35 +566,134 @@ var MemberLabelBuilder = class extends WorkspaceBuilderResource {
537
566
  };
538
567
 
539
568
  // src/workspace-builder/journeys.ts
569
+ var SECONDS_PER_DAY = 86400;
570
+ function edgeKey(parentId, childId) {
571
+ return `${parentId}:${childId}`;
572
+ }
573
+ function edgeCondition(options) {
574
+ const clauses = [];
575
+ if (options.afterDays !== void 0) {
576
+ clauses.push(`days_since(parent_step.completed_at) >= ${options.afterDays}`);
577
+ }
578
+ if (options.when) clauses.push(`(${options.when})`);
579
+ if (!clauses.length) return void 0;
580
+ return clauses.join(" && ");
581
+ }
540
582
  var JourneyStepBuilder = class extends WorkspaceBuilderResource {
541
583
  // journey-decoupling Phase 7: a Journey step no longer links to a Workflow.
542
584
  // A step nudges via EventRule actions instead of running a Workflow, so the
543
585
  // builder no longer accepts or emits a workflow reference.
544
- constructor(name, journey, order = 0, description = "") {
586
+ constructor(name, journey, order = 0, description = "", options = {}) {
545
587
  super();
546
588
  this.name = name;
547
589
  this.journey = journey;
548
590
  this.order = order;
549
591
  this.description = description;
592
+ this.assigneeId = options.assigneeId;
593
+ this.posX = options.posX;
594
+ this.posY = options.posY;
550
595
  }
551
596
  name;
552
597
  journey;
553
598
  order;
554
599
  description;
555
600
  connections = [];
556
- connectsTo(child, condition, required = false, timeoutSeconds) {
557
- this.connections.push({ parent: this, child, condition, required, timeoutSeconds });
601
+ rules = [];
602
+ assigneeId;
603
+ posX;
604
+ posY;
605
+ /** Wire an edge to `child` and return the CHILD, so chains read linearly. */
606
+ connectsTo(child, options = {}) {
607
+ if (child === this) {
608
+ throw new Error(`Journey step '${this.name}' cannot connect to itself`);
609
+ }
610
+ const abandonSeconds = options.abandonAfterDays !== void 0 ? options.abandonAfterDays * SECONDS_PER_DAY : options.timeoutSeconds;
611
+ this.connections.push({
612
+ parent: this,
613
+ child,
614
+ condition: edgeCondition(options),
615
+ required: options.required ?? false,
616
+ timeoutSeconds: abandonSeconds,
617
+ order: options.order,
618
+ filterId: options.filterId
619
+ });
620
+ return child;
621
+ }
622
+ /**
623
+ * Attach a `JOURNEY_STEP_EXECUTE` action — what this step actually DOES
624
+ * when a member reaches it. A step with no rules is inert: the member
625
+ * lands on it, nothing is sent, and the journey moves on.
626
+ */
627
+ onExecute(actionType, params = {}, options = {}) {
628
+ this.rules.push({
629
+ uuid: crypto.randomUUID(),
630
+ actionType,
631
+ actionParams: params,
632
+ options
633
+ });
558
634
  return this;
559
635
  }
636
+ /**
637
+ * Text the member. `phoneNumberUUID` is a workspace PhoneNumber with SMS
638
+ * enabled. Pass `workflowUUID` to start an SMS workflow chat, or
639
+ * `messageTemplate` for a one-off send.
640
+ */
641
+ sendsSms(params, options = {}) {
642
+ return this.onExecute("sms:send" /* SMS_SEND */, params, options);
643
+ }
644
+ /**
645
+ * Call the member. Both the workspace PhoneNumber uuid (voice enabled) and
646
+ * the Workflow uuid that drives the call are required.
647
+ */
648
+ callsMember(params, options = {}) {
649
+ return this.onExecute("phone:call" /* PHONE_CALL */, params, options);
650
+ }
651
+ /** Email the member from a workspace inbox. */
652
+ sendsEmail(params, options = {}) {
653
+ return this.onExecute("email:send" /* EMAIL_SEND */, params, options);
654
+ }
655
+ /** Label the member on arrival — pass label builders, not ids. */
656
+ addsLabels(labels, options = {}) {
657
+ return this.onExecute(
658
+ "member:label" /* MEMBER_LABEL_ACTION */,
659
+ { addLabelIds: labels },
660
+ options
661
+ );
662
+ }
663
+ /** True once this step has something to do when a member reaches it. */
664
+ get hasOutreach() {
665
+ return this.rules.length > 0;
666
+ }
667
+ eventRuleInputs() {
668
+ return this.rules.map((rule) => ({
669
+ uuid: rule.uuid,
670
+ eventType: "journey_step:execute" /* JOURNEY_STEP_EXECUTE */,
671
+ actionType: rule.actionType,
672
+ actionParams: resolveRefs(rule.actionParams),
673
+ ...rule.options.name ? { name: rule.options.name } : {},
674
+ ...rule.options.when ? { conditions: rule.options.when } : {},
675
+ ...rule.options.delaySeconds ? { delay: rule.options.delaySeconds } : {},
676
+ active: rule.options.active ?? true
677
+ }));
678
+ }
560
679
  planLine() {
561
- return `Step: ${this.name}`;
680
+ const actions = this.rules.map((rule) => rule.actionType).join(" + ");
681
+ return `Step: ${this.name}${actions ? ` (${actions})` : ""}`;
562
682
  }
563
683
  async apply(client) {
684
+ if (this._existed) return;
564
685
  if (this.journey._id === void 0) return;
686
+ const rules = this.eventRuleInputs();
565
687
  const payload = {
566
688
  name: this.name,
567
689
  description: this.description,
568
- order: this.order
690
+ order: this.order,
691
+ ...this.assigneeId !== void 0 ? { assigneeId: this.assigneeId } : {},
692
+ ...this.posX !== void 0 ? { posX: this.posX } : {},
693
+ ...this.posY !== void 0 ? { posY: this.posY } : {},
694
+ // Saved atomically with the step, so a partial failure cannot leave
695
+ // an inert step behind.
696
+ ...rules.length ? { eventRules: rules } : {}
569
697
  };
570
698
  const result = await client.createStep(this.journey._id, payload);
571
699
  this._id = result.id;
@@ -573,26 +701,59 @@ var JourneyStepBuilder = class extends WorkspaceBuilderResource {
573
701
  this._created = true;
574
702
  }
575
703
  };
704
+ var StartStepBuilder = class extends JourneyStepBuilder {
705
+ get isImplicit() {
706
+ return true;
707
+ }
708
+ planLine() {
709
+ return "Step: Start (auto-created)";
710
+ }
711
+ async apply() {
712
+ }
713
+ };
576
714
  var JourneyBuilder = class extends WorkspaceBuilderResource {
577
715
  constructor(name, options = {}) {
578
716
  super();
579
717
  this.name = name;
580
718
  this.description = options.description ?? "";
719
+ this.start = new StartStepBuilder("Start", this, 0);
720
+ this.steps.push(this.start);
581
721
  }
582
722
  name;
583
723
  description;
584
724
  steps = [];
585
725
  goals = [];
726
+ /** The auto-created `Start` step — connect the first real step to it. */
727
+ start;
728
+ /** `parentId:childId` for every edge the server already has (see `adopt`). */
729
+ existingEdges = /* @__PURE__ */ new Set();
586
730
  step(name, options = {}) {
587
731
  const step = new JourneyStepBuilder(
588
732
  name,
589
733
  this,
590
- options.order ?? 0,
591
- options.description ?? ""
734
+ options.order ?? this.steps.length,
735
+ options.description ?? "",
736
+ options
592
737
  );
593
738
  this.steps.push(step);
594
739
  return step;
595
740
  }
741
+ /**
742
+ * Declare a linear cadence: `Start → steps[0] → steps[1] → …`, each edge
743
+ * carrying the matching entry from `spacing`.
744
+ *
745
+ * Chaining is not optional polish. A step created with no incoming edge is
746
+ * a ROOT, and every root fires the moment a member enrols — a "three
747
+ * follow-ups over four days" journey built without edges sends all three
748
+ * at once.
749
+ */
750
+ sequence(steps, spacing = []) {
751
+ let parent = this.start;
752
+ steps.forEach((step, index) => {
753
+ parent = parent.connectsTo(step, spacing[index] ?? {});
754
+ });
755
+ return this;
756
+ }
596
757
  goal(name, options = {}) {
597
758
  this.goals.push({
598
759
  name,
@@ -600,17 +761,85 @@ var JourneyBuilder = class extends WorkspaceBuilderResource {
600
761
  step: options.step,
601
762
  completionCel: options.completionCel ?? options.completion_cel,
602
763
  value: options.value,
603
- order: options.order ?? 0
764
+ order: options.order ?? this.goals.length
604
765
  });
605
766
  return this;
606
767
  }
768
+ /**
769
+ * Authoring problems that make a journey silently misbehave at runtime.
770
+ * Reported at plan/preview time, before anything is written.
771
+ */
772
+ validate() {
773
+ const problems = [];
774
+ const withParents = /* @__PURE__ */ new Set();
775
+ for (const step of this.steps) {
776
+ for (const connection of step.connections) withParents.add(connection.child);
777
+ }
778
+ for (const step of this.steps) {
779
+ if (step === this.start) continue;
780
+ if (!withParents.has(step)) {
781
+ problems.push(
782
+ `step '${step.name}' has no incoming edge \u2014 it is a root and fires for every member the moment they enrol. Connect it from journey.start or a prior step.`
783
+ );
784
+ }
785
+ if (!step.hasOutreach) {
786
+ problems.push(
787
+ `step '${step.name}' has no JOURNEY_STEP_EXECUTE action \u2014 a member reaching it gets nothing. Add one with .sendsSms / .callsMember / .sendsEmail / .onExecute.`
788
+ );
789
+ }
790
+ }
791
+ if (!this.goals.length) {
792
+ problems.push(`journey '${this.name}' has no goals \u2014 nothing measures whether it works.`);
793
+ }
794
+ return problems;
795
+ }
796
+ /**
797
+ * Adopt an existing journey, its steps, and its EDGES, matched by name.
798
+ *
799
+ * Re-running a builder script is normal — a program is usually built in
800
+ * stages as provisioning lands — and without adoption the second run would
801
+ * create a duplicate journey and a duplicate set of steps beside the live
802
+ * one, with members enrolled in neither.
803
+ *
804
+ * The edge set is recorded from the server's own `childConnections` rather
805
+ * than inferred from "both steps exist". A first run that created the steps
806
+ * and then failed before writing an edge leaves exactly that state, and
807
+ * inferring would skip the missing edge on every subsequent run — leaving a
808
+ * permanent root step that fires for every member at enrolment, silently.
809
+ * The journey detail read eager-loads connections, so the real answer is
810
+ * already in hand; there is no reason to guess.
811
+ */
812
+ adopt(existing) {
813
+ super.adopt(existing);
814
+ const existingSteps = existing.steps ?? [];
815
+ const byName = new Map(existingSteps.map((step) => [step.name, step]));
816
+ const startStep = existingSteps.find((step) => step.id === existing.startStepId) ?? byName.get("Start");
817
+ if (startStep) this.start.adopt(startStep);
818
+ for (const step of this.steps) {
819
+ if (step === this.start) continue;
820
+ const match = byName.get(step.name);
821
+ if (match) step.adopt(match);
822
+ }
823
+ for (const step of existingSteps) {
824
+ for (const edge of step.childConnections ?? []) {
825
+ this.existingEdges.add(edgeKey(edge.parentStepId, edge.childStepId));
826
+ }
827
+ }
828
+ }
829
+ /** True when the server already has this exact edge. */
830
+ hasEdge(parent, child) {
831
+ if (parent._id === void 0 || child._id === void 0) return false;
832
+ return this.existingEdges.has(edgeKey(parent._id, child._id));
833
+ }
607
834
  planLine() {
608
835
  const parts = [];
609
- if (this.steps.length) parts.push(`${this.steps.length} steps`);
836
+ const real = this.steps.filter((step) => step !== this.start);
837
+ if (real.length) parts.push(`${real.length} steps`);
610
838
  if (this.goals.length) parts.push(`${this.goals.length} goals`);
611
839
  return `Journey: ${this.name}${parts.length ? ` (${parts.join(", ")})` : ""}`;
612
840
  }
613
841
  async apply(client) {
842
+ if (this._existed) return;
614
843
  const result = await client.createJourney({
615
844
  name: this.name,
616
845
  description: this.description
@@ -618,6 +847,15 @@ var JourneyBuilder = class extends WorkspaceBuilderResource {
618
847
  this._id = result.id;
619
848
  this._uuid = result.uuid;
620
849
  this._created = true;
850
+ const startStep = (result.steps ?? []).find(
851
+ (step) => step.id === result.startStepId || step.name === "Start"
852
+ );
853
+ if (!startStep) {
854
+ throw new Error(
855
+ `Journey '${this.name}' was created without a Start step; edges from journey.start cannot be wired.`
856
+ );
857
+ }
858
+ this.start.adopt(startStep);
621
859
  }
622
860
  };
623
861
  function buildJourneyConnectionPayload(connection) {
@@ -627,7 +865,9 @@ function buildJourneyConnectionPayload(connection) {
627
865
  childStepUuid: connection.child._uuid,
628
866
  ...connection.condition ? { celCondition: connection.condition } : {},
629
867
  ...connection.required ? { required: true } : {},
630
- ...connection.timeoutSeconds ? { timeoutSeconds: connection.timeoutSeconds } : {}
868
+ ...connection.timeoutSeconds ? { timeoutSeconds: connection.timeoutSeconds } : {},
869
+ ...connection.order !== void 0 ? { order: connection.order } : {},
870
+ ...connection.filterId !== void 0 ? { filterId: connection.filterId } : {}
631
871
  };
632
872
  }
633
873
  function buildJourneyGoalPayload(goal) {
@@ -913,6 +1153,15 @@ var PHASES = /* @__PURE__ */ new Map([
913
1153
  [JourneyBuilder, 7],
914
1154
  [JourneyStepBuilder, 8]
915
1155
  ]);
1156
+ function phaseFor(resource) {
1157
+ let ctor = resource.constructor;
1158
+ while (ctor) {
1159
+ const phase = PHASES.get(ctor);
1160
+ if (phase !== void 0) return phase;
1161
+ ctor = Object.getPrototypeOf(ctor);
1162
+ }
1163
+ return 99;
1164
+ }
916
1165
  var WorkspaceBuilder = class {
917
1166
  constructor(client) {
918
1167
  this.client = client;
@@ -975,7 +1224,8 @@ var WorkspaceBuilder = class {
975
1224
  ${"=".repeat(50)}`);
976
1225
  writeLine("Plan");
977
1226
  writeLine("=".repeat(50));
978
- for (const resource of resources) {
1227
+ const declared = resources.filter((resource) => !resource.isImplicit);
1228
+ for (const resource of declared) {
979
1229
  writeLine(` + ${resource.planLine()}`);
980
1230
  }
981
1231
  const wiring = this.collectWiringSummary(resources);
@@ -983,8 +1233,9 @@ ${"=".repeat(50)}`);
983
1233
  writeLine("\n Wiring:");
984
1234
  for (const line of wiring) writeLine(` ${line}`);
985
1235
  }
1236
+ this.reportJourneyWarnings();
986
1237
  writeLine(`
987
- Total: ${resources.length} resources, ${wiring.length} connections`);
1238
+ Total: ${declared.length} resources, ${wiring.length} connections`);
988
1239
  writeLine(`${"=".repeat(50)}
989
1240
  `);
990
1241
  }
@@ -995,7 +1246,8 @@ ${"=".repeat(50)}`);
995
1246
  ${"=".repeat(50)}`);
996
1247
  writeLine("Preview");
997
1248
  writeLine("=".repeat(50));
998
- for (const resource of resources) {
1249
+ const declared = resources.filter((resource) => !resource.isImplicit);
1250
+ for (const resource of declared) {
999
1251
  if (resource._existed) {
1000
1252
  writeLine(` = ${resource.planLine()} (exists, id=${resource._id})`);
1001
1253
  } else {
@@ -1007,8 +1259,9 @@ ${"=".repeat(50)}`);
1007
1259
  writeLine("\n Wiring:");
1008
1260
  for (const line of wiring) writeLine(` ${line}`);
1009
1261
  }
1010
- const toCreate = resources.filter((resource) => !resource._existed);
1011
- const existing = resources.filter((resource) => resource._existed);
1262
+ this.reportJourneyWarnings();
1263
+ const toCreate = declared.filter((resource) => !resource._existed);
1264
+ const existing = declared.filter((resource) => resource._existed);
1012
1265
  writeLine(
1013
1266
  `
1014
1267
  Total: ${toCreate.length} to create, ${existing.length} existing, ${wiring.length} connections`
@@ -1016,6 +1269,18 @@ ${"=".repeat(50)}`);
1016
1269
  writeLine(`${"=".repeat(50)}
1017
1270
  `);
1018
1271
  }
1272
+ /**
1273
+ * Print journey authoring problems that only bite at runtime — a step with
1274
+ * no incoming edge fires for every member at enrolment, and a step with no
1275
+ * action does nothing at all. Both look fine in the editor, so they are
1276
+ * surfaced before the write rather than discovered from a member's phone.
1277
+ */
1278
+ reportJourneyWarnings() {
1279
+ const problems = this.resources.filter((resource) => resource instanceof JourneyBuilder).flatMap((journey) => journey.validate());
1280
+ if (!problems.length) return;
1281
+ writeLine("\n Journey warnings:");
1282
+ for (const problem of problems) writeLine(` ! ${problem}`);
1283
+ }
1019
1284
  async apply() {
1020
1285
  const resources = this.sortedResources();
1021
1286
  await this.resolveExisting(resources);
@@ -1023,6 +1288,7 @@ ${"=".repeat(50)}`);
1023
1288
  writeLine("Applying...");
1024
1289
  for (const resource of resources) {
1025
1290
  try {
1291
+ if (resource.isImplicit) continue;
1026
1292
  if (resource._existed) {
1027
1293
  const key = resourceKey(resource);
1028
1294
  summary[key] ??= [];
@@ -1080,6 +1346,29 @@ Done! Created ${count} resources.`);
1080
1346
  }
1081
1347
  }
1082
1348
  }
1349
+ const filterNames = new Map(
1350
+ resources.filter(
1351
+ (resource) => resource instanceof MemberFilterBuilder
1352
+ ).map((resource) => [resource.name, resource])
1353
+ );
1354
+ if (filterNames.size) {
1355
+ for (const existing of await this.client.getMemberFilters()) {
1356
+ if (existing.name && filterNames.has(existing.name)) {
1357
+ filterNames.get(existing.name).adopt(existing);
1358
+ }
1359
+ }
1360
+ }
1361
+ const journeyNames = new Map(
1362
+ resources.filter((resource) => resource instanceof JourneyBuilder).map((resource) => [resource.name, resource])
1363
+ );
1364
+ if (journeyNames.size) {
1365
+ const existingJourneys = await this.client.getJourneys();
1366
+ for (const existing of existingJourneys.items) {
1367
+ if (!existing.name || !journeyNames.has(existing.name)) continue;
1368
+ const detail = await this.client.getJourney(existing.id);
1369
+ journeyNames.get(existing.name).adopt(detail);
1370
+ }
1371
+ }
1083
1372
  if (!workflowNames.size) return;
1084
1373
  const existingWorkflows = await this.client.getWorkflows();
1085
1374
  for (const { workflow: existing } of existingWorkflows.items) {
@@ -1130,9 +1419,7 @@ Done! Created ${count} resources.`);
1130
1419
  all.push(...resource.steps);
1131
1420
  }
1132
1421
  }
1133
- return all.sort(
1134
- (a, b) => (PHASES.get(a.constructor) ?? 99) - (PHASES.get(b.constructor) ?? 99)
1135
- );
1422
+ return all.sort((a, b) => phaseFor(a) - phaseFor(b));
1136
1423
  }
1137
1424
  collectWiringSummary(resources) {
1138
1425
  const lines = [];
@@ -1153,7 +1440,11 @@ Done! Created ${count} resources.`);
1153
1440
  lines.push(`${rule.actionType} rule on '${resource.name}'`);
1154
1441
  } else if (resource instanceof JourneyStepBuilder) {
1155
1442
  for (const connection of resource.connections) {
1156
- lines.push(`${connection.parent.name} -> ${connection.child.name}`);
1443
+ const guard = connection.condition ? ` [${connection.condition}]` : "";
1444
+ lines.push(`${connection.parent.name} -> ${connection.child.name}${guard}`);
1445
+ }
1446
+ for (const rule of resource.rules) {
1447
+ lines.push(`${rule.actionType} on step '${resource.name}'`);
1157
1448
  }
1158
1449
  } else if (resource instanceof JourneyBuilder) {
1159
1450
  for (const goal of resource.goals) lines.push(`Goal: ${goal.name}`);
@@ -1269,6 +1560,10 @@ Done! Created ${count} resources.`);
1269
1560
  if (!(resource instanceof JourneyStepBuilder)) continue;
1270
1561
  for (const connection of resource.connections) {
1271
1562
  if (resource.journey._id === void 0) continue;
1563
+ if (resource.journey.hasEdge(connection.parent, connection.child)) {
1564
+ writeLine(` Exists: ${connection.parent.name} -> ${connection.child.name}`);
1565
+ continue;
1566
+ }
1272
1567
  const payload = buildJourneyConnectionPayload(connection);
1273
1568
  if (!payload) continue;
1274
1569
  await this.client.createConnection(resource.journey._id, payload);
@@ -1279,6 +1574,12 @@ Done! Created ${count} resources.`);
1279
1574
  async applyJourneyGoals(resources) {
1280
1575
  for (const resource of resources) {
1281
1576
  if (!(resource instanceof JourneyBuilder) || resource._id === void 0) continue;
1577
+ if (resource._existed) {
1578
+ for (const goal of resource.goals) {
1579
+ writeLine(` Skipped (journey already exists): Goal: ${goal.name}`);
1580
+ }
1581
+ continue;
1582
+ }
1282
1583
  for (const goal of resource.goals) {
1283
1584
  await this.client.createGoal(resource._id, buildJourneyGoalPayload(goal));
1284
1585
  writeLine(` Created goal: ${goal.name}`);
@@ -1579,6 +1880,7 @@ export {
1579
1880
  PIDS_SOURCES,
1580
1881
  POSITIVE_RATING_THRESHOLD,
1581
1882
  ProductType,
1883
+ PromoCodeStatus,
1582
1884
  QUERY_VALUELESS_OPS,
1583
1885
  QueriesApi,
1584
1886
  REALTIME_MODEL_CUSTOM,
@@ -1620,6 +1922,7 @@ export {
1620
1922
  WorkflowBuilder,
1621
1923
  WorkflowAccessWarningResourceType as WorkflowResourceType,
1622
1924
  WorkspaceBuilder,
1925
+ WorkspaceBuilderResource,
1623
1926
  WorkspaceFeature,
1624
1927
  WorkspaceProvisionStatus,
1625
1928
  areDependenciesSatisfied,