@mrrocket/sdk 0.16.0 → 0.18.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.
Files changed (50) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +11 -2
  3. package/dist/api-client.d.ts +7 -1
  4. package/dist/api-client.js.map +1 -1
  5. package/dist/browser/mrrocket-flows.js +16 -2
  6. package/dist/browser/mrrocket-messenger.js +43 -44
  7. package/dist/browser/mrrocket.js +1 -1
  8. package/dist/browser.d.ts +1 -1
  9. package/dist/browser.js +2 -1
  10. package/dist/browser.js.map +1 -1
  11. package/dist/client.d.ts +23 -3
  12. package/dist/client.js +15 -1
  13. package/dist/client.js.map +1 -1
  14. package/dist/context.d.ts +3 -1
  15. package/dist/context.js.map +1 -1
  16. package/dist/core.d.ts +3 -1
  17. package/dist/core.js +1 -1
  18. package/dist/core.js.map +1 -1
  19. package/dist/finish-web.d.ts +18 -0
  20. package/dist/finish-web.js +139 -0
  21. package/dist/finish-web.js.map +1 -0
  22. package/dist/flows-ui.d.ts +1 -10
  23. package/dist/flows-ui.js +165 -16
  24. package/dist/flows-ui.js.map +1 -1
  25. package/dist/flows-web.d.ts +8 -1
  26. package/dist/flows-web.js +8 -0
  27. package/dist/flows-web.js.map +1 -1
  28. package/dist/flows.d.ts +203 -5
  29. package/dist/flows.js +306 -15
  30. package/dist/flows.js.map +1 -1
  31. package/dist/icons.d.ts +24 -0
  32. package/dist/icons.js +61 -0
  33. package/dist/icons.js.map +1 -0
  34. package/dist/index.d.ts +4 -2
  35. package/dist/index.js +10 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/messenger-ui.d.ts +10 -2
  38. package/dist/messenger-ui.js +134 -98
  39. package/dist/messenger-ui.js.map +1 -1
  40. package/dist/onboarding-state.d.ts +23 -0
  41. package/dist/onboarding-state.js +43 -0
  42. package/dist/onboarding-state.js.map +1 -0
  43. package/dist/react-native/index.d.cts +216 -15
  44. package/dist/react-native/index.d.ts +216 -15
  45. package/dist/react-native/index.js +880 -270
  46. package/dist/react-native/index.js.map +1 -1
  47. package/dist/styles-css.d.ts +1 -1
  48. package/dist/styles-css.js +40 -41
  49. package/dist/styles-css.js.map +1 -1
  50. package/package.json +1 -1
package/dist/flows.js CHANGED
@@ -22,6 +22,41 @@ var matchesTrigger = (trigger, event) => {
22
22
  return t === e || SIGNUP.has(t) && SIGNUP.has(e);
23
23
  };
24
24
  var startsOnLoad = (trigger) => /^(first (launch|visit|open|session|login|sign ?in|app open)|app (opened|launched)|on load|every visit|always)?$/i.test(trigger.trim());
25
+ var FROM_CHECKLIST = "From a checklist";
26
+ var isPageTrigger = (trigger) => trigger.trim().startsWith("/");
27
+ var FROM_CODE = "From your code";
28
+ var NEEDS_NEWER = "Needs tracking code 0.18.0";
29
+ var momentOf = (f) => f.when ?? f.trigger;
30
+ var manual = (trigger) => eventSlug(trigger) === "from_a_checklist" || eventSlug(trigger) === "from_your_code" || eventSlug(trigger) === eventSlug(NEEDS_NEWER);
31
+ var AFTER_PREFIX = "after:";
32
+ var afterOf = (trigger) => {
33
+ const t = (trigger ?? "").trim();
34
+ return t.toLowerCase().startsWith(AFTER_PREFIX) ? t.slice(AFTER_PREFIX.length).trim() || null : null;
35
+ };
36
+ var opensOf = (step) => step.opens ?? (step.startsTour ? { kind: "tour", id: step.startsTour } : void 0);
37
+ var answerOk = (q, value) => {
38
+ if (!q || value == null) return false;
39
+ if (q.type === "choice") return (q.options ?? []).some((o) => o.id === value);
40
+ return value.trim().length > 0 && value.length <= 500;
41
+ };
42
+ var answerLabel = (q, value) => q?.type === "choice" ? q.options?.find((o) => o.id === value)?.label ?? value : value.trim();
43
+ var videoEmbed = (url) => {
44
+ const u = (url ?? "").trim();
45
+ if (!/^https?:\/\//i.test(u)) return null;
46
+ let m = u.match(/^https?:\/\/(?:www\.|m\.)?(?:youtube\.com\/(?:watch\?(?:.*&)?v=|embed\/|shorts\/|live\/)|youtu\.be\/|youtube-nocookie\.com\/embed\/)([\w-]{6,20})/i);
47
+ if (m) return { kind: "iframe", src: `https://www.youtube-nocookie.com/embed/${m[1]}?rel=0` };
48
+ m = u.match(/^https?:\/\/(?:www\.|player\.)?vimeo\.com\/(?:video\/)?(\d{4,12})/i);
49
+ if (m) return { kind: "iframe", src: `https://player.vimeo.com/video/${m[1]}?dnt=1` };
50
+ m = u.match(/^https?:\/\/(?:www\.)?loom\.com\/(?:share|embed)\/([\w-]{8,64})/i);
51
+ if (m) return { kind: "iframe", src: `https://www.loom.com/embed/${m[1]}` };
52
+ if (/\.(mp4|webm|mov|m4v)(\?|#|$)/i.test(u) || /\/media\/uploads\//.test(u)) return { kind: "file", src: u };
53
+ return null;
54
+ };
55
+ var mediaOf = (step) => {
56
+ const m = step.media;
57
+ if (!m || m.kind !== "image" && m.kind !== "video" || !/^https?:\/\//i.test(m.url ?? "")) return void 0;
58
+ return m.kind === "video" && !videoEmbed(m.url) ? void 0 : m;
59
+ };
25
60
  var runId = () => `cf_${Math.random().toString(36).slice(2, 10)}${Date.now().toString(36)}`;
26
61
  var discounted = (amount, percent) => Math.round(amount * (1 - percent / 100) * 100) / 100;
27
62
  var money = (amount, currency) => {
@@ -69,7 +104,13 @@ var createFlows = (env) => {
69
104
  const seenEvents = /* @__PURE__ */ new Set();
70
105
  const now = () => env.now?.() ?? Date.now();
71
106
  let timer = null;
107
+ let returnTo = null;
108
+ const answers = /* @__PURE__ */ new Map();
109
+ const drafts = /* @__PURE__ */ new Map();
110
+ const celebrated = /* @__PURE__ */ new Set();
111
+ let finaleNow = null;
72
112
  const flows = () => config?.onboarding ?? [];
113
+ const drawn = () => flows().filter((f) => f.shownBy !== "code");
73
114
  const flowById = (id) => id ? flows().find((f) => f.id === id) : void 0;
74
115
  const prog = (id) => {
75
116
  let p = progress.get(id);
@@ -143,6 +184,14 @@ var createFlows = (env) => {
143
184
  };
144
185
  const keyOf = (pid) => `${PROGRESS_KEY}:${pid ?? "visitor"}`;
145
186
  const eventsKeyOf = (pid) => `${PROGRESS_KEY}:events:${pid ?? "visitor"}`;
187
+ const answersKeyOf = (pid) => `${PROGRESS_KEY}:answers:${pid ?? "visitor"}`;
188
+ const celebrate = (f) => {
189
+ if (!f.finish || f.finish === "none" || f.shownBy === "code" || celebrated.has(f.id)) return;
190
+ const p = progress.get(f.id);
191
+ if (!p || p.dismissed || walkLine(f, nodesOf(f), p, false).at !== "end") return;
192
+ celebrated.add(f.id);
193
+ finaleNow = { kind: f.finish, key: `${f.id}:${now()}` };
194
+ };
146
195
  const save = () => {
147
196
  dirty = false;
148
197
  const store = env.storage?.();
@@ -158,6 +207,7 @@ var createFlows = (env) => {
158
207
  };
159
208
  try {
160
209
  store.set(keyOf(env.personId()), JSON.stringify(out));
210
+ if (answers.size) store.set(answersKeyOf(env.personId()), JSON.stringify(Object.fromEntries(answers)));
161
211
  if (seenEvents.size) store.set(eventsKeyOf(env.personId()), JSON.stringify([...seenEvents].slice(-200)));
162
212
  } catch {
163
213
  }
@@ -206,7 +256,23 @@ var createFlows = (env) => {
206
256
  shown.add(`s:${i.id}`);
207
257
  env.track(ONBOARDING_EVENT, { flow: flow.id, step: i.id, kind: "checklist", action: "shown" });
208
258
  }
209
- return { type: "checklist", flow, items: items.map((s) => ({ step: s, done: p.done.has(s.id) })) };
259
+ return {
260
+ type: "checklist",
261
+ flow,
262
+ items: items.map((s) => {
263
+ const o = opensOf(s);
264
+ const t = o && (o.kind === "tour" || o.kind === "modal") && o.id !== flow.id ? flowById(o.id) : void 0;
265
+ const target = t && t.shownBy !== "code" && nodesOf(t).length ? t : void 0;
266
+ const opens = o && (o.kind === "page" ? !!o.target.trim() : o.kind === "code" || !!target) ? o : void 0;
267
+ return {
268
+ step: s,
269
+ done: p.done.has(s.id),
270
+ ...target && o?.kind === "tour" ? { tour: target } : {},
271
+ ...opens ? { opens } : {},
272
+ ...target ? { target } : {}
273
+ };
274
+ })
275
+ };
210
276
  }
211
277
  if (!shown.has(`s:${step.id}`)) {
212
278
  shown.add(`s:${step.id}`);
@@ -215,7 +281,16 @@ var createFlows = (env) => {
215
281
  const rest = line.slice(index + 1);
216
282
  const fork = rest.findIndex((n) => n.kind === "condition");
217
283
  const ahead = (fork < 0 ? rest : rest.slice(0, fork)).filter(isStep).length;
218
- return { type: "step", flow, step, index: w.before, total: w.before + 1 + ahead, last: !ahead && fork < 0 };
284
+ return {
285
+ type: "step",
286
+ flow,
287
+ step,
288
+ index: w.before,
289
+ total: w.before + 1 + ahead,
290
+ last: !ahead && fork < 0,
291
+ // 0.18.0: a question's answer so far, and whether there is one before it to go back to.
292
+ ...step.kind === "question" ? { answer: drafts.get(step.id) ?? (step.question ? answers.get(step.question.key) : void 0), back: w.before > 0 } : {}
293
+ };
219
294
  };
220
295
  const view = () => {
221
296
  if (cancel) {
@@ -240,7 +315,30 @@ var createFlows = (env) => {
240
315
  }
241
316
  const ob = onboardingView();
242
317
  if (ob) return ob;
243
- if (active && !holding) active = null;
318
+ if (active && !holding) {
319
+ const back = returnTo;
320
+ const ended2 = active;
321
+ returnTo = null;
322
+ active = null;
323
+ const list = back ? flowById(back.flow) : void 0;
324
+ if (back && list) {
325
+ const item = stepsIn(nodesOf(list)).find((s) => s.id === back.step);
326
+ const p = prog(list.id);
327
+ if (item && !item.event && !progress.get(ended2)?.dismissed && !p.done.has(item.id)) {
328
+ markDone(p, item.id);
329
+ record({ flow: list.id, step: item.id, kind: "checklist", action: "completed" });
330
+ celebrate(list);
331
+ }
332
+ active = list.id;
333
+ const again = onboardingView();
334
+ if (again) return again;
335
+ if (!holding) active = null;
336
+ } else if (known) {
337
+ const was = flowById(ended2);
338
+ const after = was ? momentOf(was) : void 0;
339
+ Promise.resolve().then(() => autoStart(after));
340
+ }
341
+ }
244
342
  const m = messages.find((x) => !handled.has(x.id));
245
343
  return m ? { type: "message", message: m } : null;
246
344
  };
@@ -265,19 +363,67 @@ var createFlows = (env) => {
265
363
  emit();
266
364
  return true;
267
365
  };
268
- const autoStart = () => {
366
+ const allowed = (f) => f.audience === "answered" ? !!f.answered && answers.get(f.answered.question) === f.answered.option : f.audience !== "signed_in" || !!env.personId();
367
+ const predecessorEnded = (id) => {
368
+ const g = flowById(id);
369
+ return !g || finished(g);
370
+ };
371
+ const due = (f, trigger = momentOf(f)) => {
372
+ const path = env.path?.();
373
+ const pred = afterOf(trigger);
374
+ if (pred) return predecessorEnded(pred);
375
+ if (manual(trigger)) return false;
376
+ if (isPageTrigger(trigger)) return path !== void 0 && pageMatches(trigger, path);
377
+ if (startsOnLoad(trigger)) return true;
378
+ for (const e of seenEvents) if (matchesTrigger(trigger, e)) return true;
379
+ return false;
380
+ };
381
+ const autoStart = (after) => {
269
382
  if (!auto || active || !known) return;
270
383
  if (!env.personId() && !env.storage?.()) return;
271
- const next = flows().find((f) => startsOnLoad(f.trigger) && nodesOf(f).length && !finished(f));
384
+ const path = env.path?.();
385
+ const same = after && !manual(after) ? eventSlug(after) : "";
386
+ const next = drawn().find(
387
+ (f) => (afterOf(momentOf(f)) ? predecessorEnded(afterOf(momentOf(f))) : isPageTrigger(momentOf(f)) ? path !== void 0 && pageMatches(momentOf(f), path) : startsOnLoad(momentOf(f)) || !!same && eventSlug(momentOf(f)) === same) && allowed(f) && nodesOf(f).length && !finished(f)
388
+ );
272
389
  if (next) start(next);
273
390
  };
391
+ const openItem = (list, stepId, target) => {
392
+ const p = prog(target.id);
393
+ p.done.clear();
394
+ p.at.clear();
395
+ p.dismissed = false;
396
+ returnTo = { flow: list.id, step: stepId };
397
+ active = target.id;
398
+ env.track(ONBOARDING_EVENT, { flow: target.id, action: "started", from: list.id });
399
+ save();
400
+ emit();
401
+ };
402
+ const answerStep = (flow, step, raw) => {
403
+ const q = step.question;
404
+ const value = q.type === "text" ? raw.trim().slice(0, 500) : raw;
405
+ answers.set(q.key, value);
406
+ drafts.delete(step.id);
407
+ markDone(prog(flow.id), step.id);
408
+ env.track(ONBOARDING_EVENT, { flow: flow.id, step: step.id, kind: "question", action: "completed", question: q.key, answer: value, label: answerLabel(q, value) });
409
+ celebrate(flow);
410
+ save();
411
+ };
412
+ const ended = (f) => {
413
+ save();
414
+ if (finished(f)) Promise.resolve().then(() => autoStart());
415
+ emit();
416
+ };
274
417
  const refresh = () => {
275
418
  const pid = env.personId();
276
419
  if (progressFor !== null && progressFor !== pid) {
277
420
  progress.clear();
278
421
  seenEvents.clear();
422
+ answers.clear();
423
+ drafts.clear();
279
424
  handled.clear();
280
425
  active = null;
426
+ returnTo = null;
281
427
  loadedFor = void 0;
282
428
  }
283
429
  progressFor = pid;
@@ -291,6 +437,10 @@ var createFlows = (env) => {
291
437
  if (seen) {
292
438
  for (const e of JSON.parse(seen)) if (typeof e === "string") seenEvents.add(e);
293
439
  }
440
+ const said = await store.get(answersKeyOf(pid));
441
+ if (said) {
442
+ for (const [k, v] of Object.entries(JSON.parse(said))) if (typeof v === "string") answers.set(k, v);
443
+ }
294
444
  } catch {
295
445
  }
296
446
  }
@@ -301,6 +451,7 @@ var createFlows = (env) => {
301
451
  if (state && env.personId() === pid) {
302
452
  merge(state.onboarding);
303
453
  for (const e of state.events ?? []) if (typeof e === "string") seenEvents.add(eventSlug(e));
454
+ for (const [k, v] of Object.entries(state.answers ?? {})) if (typeof v === "string" && !answers.has(k)) answers.set(k, v);
304
455
  messages = (state.messages ?? []).filter((m) => m && m.id && m.title);
305
456
  }
306
457
  }
@@ -387,33 +538,40 @@ var createFlows = (env) => {
387
538
  },
388
539
  refresh,
389
540
  onEvent: (name) => {
541
+ if (name === "$pageview") autoStart();
390
542
  if (!name || name.startsWith("$")) return;
391
543
  const slug = eventSlug(name);
392
544
  const fresh = !!slug && !seenEvents.has(slug);
393
545
  if (fresh) seenEvents.add(slug);
394
546
  const flow = flowById(active);
395
547
  let ticked = false;
396
- if (flow) {
397
- const p = prog(flow.id);
398
- for (const s of stepsIn(nodesOf(flow)))
548
+ const lists = [...flow ? [flow] : [], ...flows().filter((f) => f.shownBy === "code" && f.id !== flow?.id)];
549
+ for (const l of lists) {
550
+ const p = prog(l.id);
551
+ let here = false;
552
+ for (const s of stepsIn(nodesOf(l)))
399
553
  if (s.kind === "checklist" && s.event && !p.done.has(s.id) && matchesTrigger(s.event, name)) {
400
554
  markDone(p, s.id);
401
- ticked = true;
402
- env.track(ONBOARDING_EVENT, { flow: flow.id, step: s.id, kind: s.kind, action: "completed" });
555
+ here = true;
556
+ env.track(ONBOARDING_EVENT, { flow: l.id, step: s.id, kind: s.kind, action: "completed" });
403
557
  }
558
+ if (here) {
559
+ ticked = true;
560
+ celebrate(l);
561
+ }
404
562
  }
405
- if (ticked || fresh && flow) {
563
+ if (ticked || fresh && (flow || lists.length)) {
406
564
  save();
407
565
  emit();
408
566
  } else if (fresh) save();
409
567
  if (!auto || !env.personId() && !env.storage?.()) return;
410
- const next = flows().find((f) => nodesOf(f).length && matchesTrigger(f.trigger, name) && !finished(f));
568
+ const next = drawn().find((f) => nodesOf(f).length && !manual(momentOf(f)) && matchesTrigger(momentOf(f), name) && allowed(f) && !finished(f));
411
569
  if (next) start(next);
412
570
  },
413
571
  openCancelFlow,
414
572
  startOnboarding: (id) => {
415
- const f = id ? flowById(id) : flows().find((x) => nodesOf(x).length && !finished(x));
416
- if (!f || !nodesOf(f).length) return false;
573
+ const f = id ? flowById(id) : drawn().find((x) => nodesOf(x).length && !manual(momentOf(x)) && !afterOf(momentOf(x)) && !finished(x));
574
+ if (!f || !nodesOf(f).length || f.shownBy === "code") return false;
417
575
  const p = prog(f.id);
418
576
  p.dismissed = false;
419
577
  return start(f);
@@ -504,10 +662,21 @@ var createFlows = (env) => {
504
662
  next: () => {
505
663
  const v = onboardingView();
506
664
  if (v?.type !== "step") return;
665
+ if (v.step.kind === "question") {
666
+ const q = v.step.question;
667
+ const a = drafts.get(v.step.id) ?? (q ? answers.get(q.key) : void 0);
668
+ if (!q || !answerOk(q, a)) return;
669
+ answerStep(v.flow, v.step, a);
670
+ emit();
671
+ return;
672
+ }
507
673
  markDone(prog(v.flow.id), v.step.id);
508
674
  env.track(ONBOARDING_EVENT, { flow: v.flow.id, step: v.step.id, kind: v.step.kind, action: "completed" });
675
+ celebrate(v.flow);
509
676
  save();
510
677
  emit();
678
+ const url = v.step.kind === "modal" ? safeUrl(v.step.url) : void 0;
679
+ if (url) env.openUrl?.(url);
511
680
  },
512
681
  check: (stepId) => {
513
682
  const v = onboardingView();
@@ -516,6 +685,7 @@ var createFlows = (env) => {
516
685
  if (!item || item.done) return;
517
686
  markDone(prog(v.flow.id), stepId);
518
687
  env.track(ONBOARDING_EVENT, { flow: v.flow.id, step: stepId, kind: "checklist", action: "completed" });
688
+ celebrate(v.flow);
519
689
  save();
520
690
  emit();
521
691
  },
@@ -524,11 +694,120 @@ var createFlows = (env) => {
524
694
  if (!v || v.type !== "step" && v.type !== "checklist") return;
525
695
  prog(v.flow.id).dismissed = true;
526
696
  env.track(ONBOARDING_EVENT, { flow: v.flow.id, ...v.type === "step" ? { step: v.step.id } : {}, action: "dismissed" });
527
- active = null;
697
+ active = returnTo && returnTo.flow !== v.flow.id && flowById(returnTo.flow) ? returnTo.flow : null;
698
+ returnTo = null;
699
+ save();
700
+ emit();
701
+ },
702
+ startTour: (stepId) => {
703
+ const v = onboardingView();
704
+ if (v?.type !== "checklist") return false;
705
+ const target = v.items.find((i) => i.step.id === stepId)?.target;
706
+ if (!target) return false;
707
+ openItem(v.flow, stepId, target);
708
+ return true;
709
+ },
710
+ open: (stepId) => {
711
+ const v = onboardingView();
712
+ if (v?.type !== "checklist") return false;
713
+ const it = v.items.find((i) => i.step.id === stepId);
714
+ const o = it?.opens;
715
+ if (!it || !o) return false;
716
+ env.track(ONBOARDING_EVENT, { flow: v.flow.id, step: stepId, kind: "checklist", action: "opened", opens: o.kind });
717
+ if ((o.kind === "tour" || o.kind === "modal") && it.target) {
718
+ openItem(v.flow, stepId, it.target);
719
+ return true;
720
+ }
721
+ if (!it.done && !it.step.event) {
722
+ markDone(prog(v.flow.id), stepId);
723
+ env.track(ONBOARDING_EVENT, { flow: v.flow.id, step: stepId, kind: "checklist", action: "completed" });
724
+ celebrate(v.flow);
725
+ }
726
+ save();
727
+ emit();
728
+ if (o.kind === "page") env.navigate?.(o.target.trim());
729
+ else env.task?.(stepId, v.flow);
730
+ return true;
731
+ },
732
+ answer: (value) => {
733
+ const v = onboardingView();
734
+ if (v?.type !== "step" || v.step.kind !== "question") return;
735
+ drafts.set(v.step.id, String(value).slice(0, 500));
736
+ emit();
737
+ },
738
+ back: () => {
739
+ const v = onboardingView();
740
+ if (v?.type !== "step" || v.step.kind !== "question" || !v.back) return;
741
+ const line = stepsIn(nodesOf(v.flow));
742
+ const at = line.findIndex((s) => s.id === v.step.id);
743
+ const p = prog(v.flow.id);
744
+ for (let i = at - 1; i >= 0; i--)
745
+ if (p.done.has(line[i].id)) {
746
+ p.done.delete(line[i].id);
747
+ p.at.delete(line[i].id);
748
+ break;
749
+ }
528
750
  save();
529
751
  emit();
752
+ },
753
+ item: (id) => {
754
+ const f = flowById(id);
755
+ if (!f) return null;
756
+ const p = progress.get(f.id);
757
+ const steps = stepsIn(nodesOf(f)).map((s) => {
758
+ const a = s.question ? answers.get(s.question.key) : void 0;
759
+ return { ...s, done: !!p?.done.has(s.id), ...a !== void 0 ? { answer: a } : {} };
760
+ });
761
+ const done = steps.length > 0 && steps.every((s) => s.done);
762
+ const dismissed = !!p?.dismissed;
763
+ return { item: f, steps, done, dismissed, eligible: !done && !dismissed && allowed(f) && due(f) };
764
+ },
765
+ seen: (id) => {
766
+ const f = flowById(id);
767
+ if (!f || f.shownBy !== "code" || !known) return;
768
+ if (!shown.has(`f:${f.id}`)) {
769
+ shown.add(`f:${f.id}`);
770
+ env.track(ONBOARDING_EVENT, { flow: f.id, action: "started" });
771
+ }
772
+ const p = progress.get(f.id);
773
+ for (const s of stepsIn(nodesOf(f)))
774
+ if (!p?.done.has(s.id) && !shown.has(`s:${s.id}`)) {
775
+ shown.add(`s:${s.id}`);
776
+ env.track(ONBOARDING_EVENT, { flow: f.id, step: s.id, kind: s.kind, action: "shown" });
777
+ if (s.kind !== "checklist") break;
778
+ }
779
+ },
780
+ complete: (id, stepId) => {
781
+ const f = flowById(id);
782
+ const s = f && stepsIn(nodesOf(f)).find((x) => x.id === stepId);
783
+ if (!f || !s || s.kind === "question") return;
784
+ const p = prog(f.id);
785
+ if (p.done.has(s.id)) return;
786
+ markDone(p, s.id);
787
+ env.track(ONBOARDING_EVENT, { flow: f.id, step: s.id, kind: s.kind, action: "completed" });
788
+ ended(f);
789
+ },
790
+ dismissItem: (id) => {
791
+ const f = flowById(id);
792
+ if (!f) return;
793
+ const p = prog(f.id);
794
+ if (p.dismissed) return;
795
+ p.dismissed = true;
796
+ env.track(ONBOARDING_EVENT, { flow: f.id, action: "dismissed" });
797
+ if (active === f.id) active = null;
798
+ ended(f);
799
+ },
800
+ answerItem: (id, question, value) => {
801
+ const f = flowById(id);
802
+ const s = f && stepsIn(nodesOf(f)).find((x) => x.kind === "question" && (x.id === question || x.question?.key === question));
803
+ if (!f || !s?.question || !answerOk(s.question, value)) return;
804
+ answerStep(f, s, value);
805
+ ended(f);
530
806
  }
531
807
  },
808
+ finale: () => finaleNow,
809
+ accent: () => config?.onboardingAccent,
810
+ answers: () => Object.fromEntries(answers),
532
811
  message: {
533
812
  click: () => {
534
813
  const v = view();
@@ -551,24 +830,36 @@ var createFlows = (env) => {
551
830
  return controller;
552
831
  };
553
832
  export {
833
+ AFTER_PREFIX,
554
834
  CANCEL_EVENT,
835
+ FROM_CHECKLIST,
836
+ FROM_CODE,
555
837
  INAPP_EVENT,
838
+ NEEDS_NEWER,
556
839
  ONBOARDING_EVENT,
557
840
  PLAIN_CANCEL,
558
841
  REVIEW_EVENT,
842
+ afterOf,
843
+ answerLabel,
844
+ answerOk,
559
845
  createFlows,
560
846
  discounted,
561
847
  eventSlug,
848
+ isPageTrigger,
562
849
  matchesTrigger,
850
+ mediaOf,
851
+ momentOf,
563
852
  money,
564
853
  monthsSince,
565
854
  nodesOf,
566
855
  offerButton,
856
+ opensOf,
567
857
  pageMatches,
568
858
  ruleMatches,
569
859
  runId,
570
860
  safeUrl,
571
861
  startsOnLoad,
862
+ videoEmbed,
572
863
  waitMs
573
864
  };
574
865
  //# sourceMappingURL=flows.js.map