@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/agui.cjs +13 -1
- package/dist/agui.cjs.map +1 -1
- package/dist/agui.d.cts +5 -1
- package/dist/agui.d.ts +5 -1
- package/dist/agui.js +12 -1
- package/dist/agui.js.map +1 -1
- package/dist/{chunk-QTJQTTIR.js → chunk-4B7T6MBH.js} +223 -7
- package/dist/chunk-4B7T6MBH.js.map +1 -0
- package/dist/{client-CefA5Mxe.d.ts → client-3xYGmF3t.d.ts} +1172 -10
- package/dist/{client-BLuz00Qr.d.cts → client-BhO_UFFz.d.cts} +1172 -10
- package/dist/helpers.d.cts +2 -2
- package/dist/helpers.d.ts +2 -2
- package/dist/index.cjs +545 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +164 -9
- package/dist/index.d.ts +164 -9
- package/dist/index.js +325 -22
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs +248 -6
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +301 -6
- package/dist/internal.d.ts +301 -6
- package/dist/internal.js +26 -1
- package/dist/internal.js.map +1 -1
- package/dist/{revision-state-DL1ViZ57.d.ts → revision-state-Dma10N9s.d.ts} +4 -2
- package/dist/{revision-state-axZ62kJk.d.cts → revision-state-Dx8gCQIj.d.cts} +4 -2
- package/dist/schemas.cjs +9 -1
- package/dist/schemas.cjs.map +1 -1
- package/dist/schemas.d.cts +2 -2
- package/dist/schemas.d.ts +2 -2
- package/dist/schemas.js +9 -1
- package/dist/schemas.js.map +1 -1
- package/dist/{tasks-Boi6AxGp.d.cts → tasks-vupYoJgG.d.cts} +11 -9
- package/dist/{tasks-Boi6AxGp.d.ts → tasks-vupYoJgG.d.ts} +11 -9
- package/package.json +2 -2
- package/dist/chunk-QTJQTTIR.js.map +0 -1
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-
|
|
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
|
-
/**
|
|
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
|
-
|
|
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
|
-
|
|
557
|
-
|
|
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
|
-
|
|
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 ??
|
|
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 ??
|
|
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
|
-
|
|
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
|
-
|
|
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: ${
|
|
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
|
-
|
|
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
|
-
|
|
1011
|
-
const
|
|
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
|
-
|
|
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,
|