@usergist/sdk-core 0.1.0 → 0.1.2

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/client.js ADDED
@@ -0,0 +1,1170 @@
1
+ import { z } from 'zod';
2
+
3
+ // src/types/segment-dsl.ts
4
+ function emptySegmentDsl() {
5
+ return { version: 1, root: { combinator: "AND", predicates: [] } };
6
+ }
7
+
8
+ // src/types/api.ts
9
+ function ok(data, meta) {
10
+ return meta ? { success: true, data, meta } : { success: true, data };
11
+ }
12
+ function err(code, message, details) {
13
+ return {
14
+ success: false,
15
+ error: details === void 0 ? { code, message } : { code, message, details }
16
+ };
17
+ }
18
+
19
+ // src/types/workspace.ts
20
+ function defaultOnboardingMessage(appName) {
21
+ return {
22
+ title: `Welcome to ${appName.trim() || "our app"}`,
23
+ body: "Glad you\u2019re here. Take a look around and make yourself at home.",
24
+ buttonLabel: "Got it",
25
+ format: "modal"
26
+ };
27
+ }
28
+
29
+ // src/types/survey.ts
30
+ var SURVEY_END_SENTINEL = "__end__";
31
+
32
+ // src/types/inapp-message.ts
33
+ var INAPP_SHOWN_EVENT_NAME = "$inapp_shown";
34
+ var INAPP_DISMISSED_EVENT_NAME = "$inapp_dismissed";
35
+ var INAPP_AUTO_DISMISSED_EVENT_NAME = "$inapp_auto_dismissed";
36
+ var INAPP_CTA_CLICKED_EVENT_NAME = "$inapp_cta_clicked";
37
+
38
+ // src/types/request.ts
39
+ var REQUEST_SUBMITTED_EVENT_NAME = "$request_submitted";
40
+ var REQUEST_UPVOTED_EVENT_NAME = "$request_upvoted";
41
+ var REQUEST_UNUPVOTED_EVENT_NAME = "$request_unupvoted";
42
+ var REQUEST_FOLLOWED_EVENT_NAME = "$request_followed";
43
+ var REQUEST_UNFOLLOWED_EVENT_NAME = "$request_unfollowed";
44
+ var REQUEST_STATUS_CHANGED_EVENT_NAME = "$request_status_changed";
45
+ var REQUEST_RESPONDED_EVENT_NAME = "$request_responded";
46
+ var REQUEST_COMMENTED_EVENT_NAME = "$request_commented";
47
+ var REQUEST_COMMENT_EDITED_EVENT_NAME = "$request_comment_edited";
48
+ var REQUEST_COMMENT_DELETED_EVENT_NAME = "$request_comment_deleted";
49
+
50
+ // src/types/targeting.ts
51
+ function emptyAudienceSpec() {
52
+ return { version: 1, mode: "everyone", combinator: "all", conditions: [] };
53
+ }
54
+ var APP_OPEN_EVENT_NAME = "$app_open";
55
+ var AUDIENCE_JOIN_EVENT_NAME = "$audience_join";
56
+ var AUDIENCE_JOIN_TRIGGER_KIND = "audience_join";
57
+ var APP_VERSION_CHANGED_EVENT_NAME = "$app_version_changed";
58
+ function defaultTriggerSpec() {
59
+ return { kind: "app_open" };
60
+ }
61
+ function defaultEventTrigger(eventName = "") {
62
+ return {
63
+ kind: "event",
64
+ eventName,
65
+ occurrence: { mode: "every" }
66
+ };
67
+ }
68
+
69
+ // src/types/themes.ts
70
+ var RADIUS_PRESETS = {
71
+ sharp: 4,
72
+ soft: 12,
73
+ rounded: 20,
74
+ pill: 999
75
+ };
76
+ var JAKARTA = "Plus Jakarta Sans";
77
+ var THEME_PRESETS = [
78
+ {
79
+ id: "minimal",
80
+ name: "Minimal",
81
+ description: "Clean neutral palette",
82
+ colors: {
83
+ primary: "#111827",
84
+ background: "#FFFFFF",
85
+ text: "#111827",
86
+ subtext: "#6B7280",
87
+ border: "#E5E7EB",
88
+ button: "#111827"
89
+ },
90
+ radius: 16,
91
+ fontFamily: JAKARTA,
92
+ swatches: ["#111827", "#FFFFFF", "#E5E7EB"]
93
+ },
94
+ {
95
+ id: "ocean",
96
+ name: "Ocean",
97
+ description: "Cool blue tones",
98
+ colors: {
99
+ primary: "#0EA5E9",
100
+ background: "#F0F9FF",
101
+ text: "#0C4A6E",
102
+ subtext: "#0369A1",
103
+ border: "#BAE6FD",
104
+ button: "#0EA5E9"
105
+ },
106
+ radius: 16,
107
+ fontFamily: JAKARTA,
108
+ swatches: ["#0EA5E9", "#0C4A6E", "#BAE6FD"]
109
+ },
110
+ {
111
+ id: "sunset",
112
+ name: "Sunset",
113
+ description: "Warm orange glow",
114
+ colors: {
115
+ primary: "#F97316",
116
+ background: "#FFF7ED",
117
+ text: "#7C2D12",
118
+ subtext: "#C2410C",
119
+ border: "#FED7AA",
120
+ button: "#F97316"
121
+ },
122
+ radius: 16,
123
+ fontFamily: JAKARTA,
124
+ swatches: ["#F97316", "#7C2D12", "#FED7AA"]
125
+ },
126
+ {
127
+ id: "forest",
128
+ name: "Forest",
129
+ description: "Natural green hues",
130
+ colors: {
131
+ primary: "#16A34A",
132
+ background: "#F0FDF4",
133
+ text: "#14532D",
134
+ subtext: "#166534",
135
+ border: "#BBF7D0",
136
+ button: "#16A34A"
137
+ },
138
+ radius: 16,
139
+ fontFamily: JAKARTA,
140
+ swatches: ["#16A34A", "#14532D", "#BBF7D0"]
141
+ },
142
+ {
143
+ id: "midnight",
144
+ name: "Midnight",
145
+ description: "Deep dark mode",
146
+ colors: {
147
+ primary: "#A78BFA",
148
+ background: "#0F172A",
149
+ text: "#F8FAFC",
150
+ subtext: "#94A3B8",
151
+ border: "#1E293B",
152
+ button: "#A78BFA"
153
+ },
154
+ radius: 16,
155
+ fontFamily: JAKARTA,
156
+ swatches: ["#A78BFA", "#0F172A", "#1E293B"]
157
+ },
158
+ {
159
+ id: "candy",
160
+ name: "Candy",
161
+ description: "Playful pink + violet",
162
+ colors: {
163
+ primary: "#EC4899",
164
+ background: "#FDF2F8",
165
+ text: "#831843",
166
+ subtext: "#BE185D",
167
+ border: "#FBCFE8",
168
+ button: "#EC4899"
169
+ },
170
+ radius: 20,
171
+ fontFamily: JAKARTA,
172
+ swatches: ["#EC4899", "#831843", "#FBCFE8"]
173
+ },
174
+ {
175
+ id: "mono",
176
+ name: "Mono",
177
+ description: "High-contrast B&W",
178
+ colors: {
179
+ primary: "#000000",
180
+ background: "#FFFFFF",
181
+ text: "#000000",
182
+ subtext: "#525252",
183
+ border: "#000000",
184
+ button: "#000000"
185
+ },
186
+ radius: 4,
187
+ fontFamily: JAKARTA,
188
+ swatches: ["#000000", "#FFFFFF", "#525252"]
189
+ },
190
+ {
191
+ id: "coral",
192
+ name: "Coral",
193
+ description: "Soft coral accent",
194
+ colors: {
195
+ primary: "#FB7185",
196
+ background: "#FFF1F2",
197
+ text: "#881337",
198
+ subtext: "#9F1239",
199
+ border: "#FECDD3",
200
+ button: "#FB7185"
201
+ },
202
+ radius: 16,
203
+ fontFamily: JAKARTA,
204
+ swatches: ["#FB7185", "#881337", "#FECDD3"]
205
+ },
206
+ {
207
+ id: "ink",
208
+ name: "Ink",
209
+ description: "Deep navy editorial",
210
+ colors: {
211
+ primary: "#1E3A8A",
212
+ background: "#F8FAFC",
213
+ text: "#0F172A",
214
+ subtext: "#475569",
215
+ border: "#CBD5E1",
216
+ button: "#1E3A8A"
217
+ },
218
+ radius: 12,
219
+ fontFamily: JAKARTA,
220
+ swatches: ["#1E3A8A", "#0F172A", "#CBD5E1"]
221
+ },
222
+ {
223
+ id: "mint",
224
+ name: "Mint",
225
+ description: "Fresh teal vibe",
226
+ colors: {
227
+ primary: "#14B8A6",
228
+ background: "#F0FDFA",
229
+ text: "#134E4A",
230
+ subtext: "#0F766E",
231
+ border: "#99F6E4",
232
+ button: "#14B8A6"
233
+ },
234
+ radius: 16,
235
+ fontFamily: JAKARTA,
236
+ swatches: ["#14B8A6", "#134E4A", "#99F6E4"]
237
+ },
238
+ {
239
+ id: "slate",
240
+ name: "Slate",
241
+ description: "Quiet professional",
242
+ colors: {
243
+ primary: "#475569",
244
+ background: "#F1F5F9",
245
+ text: "#0F172A",
246
+ subtext: "#64748B",
247
+ border: "#CBD5E1",
248
+ button: "#475569"
249
+ },
250
+ radius: 12,
251
+ fontFamily: JAKARTA,
252
+ swatches: ["#475569", "#0F172A", "#CBD5E1"]
253
+ },
254
+ {
255
+ id: "aurora",
256
+ name: "Aurora",
257
+ description: "Vibrant gradient pop",
258
+ colors: {
259
+ primary: "#8B5CF6",
260
+ background: "#FAF5FF",
261
+ text: "#3B0764",
262
+ subtext: "#7E22CE",
263
+ border: "#E9D5FF",
264
+ accent: "#EC4899",
265
+ button: "#8B5CF6"
266
+ },
267
+ radius: 20,
268
+ fontFamily: JAKARTA,
269
+ swatches: ["#8B5CF6", "#EC4899", "#E9D5FF"]
270
+ }
271
+ ];
272
+ function getThemePresetById(id2) {
273
+ return THEME_PRESETS.find((preset) => preset.id === id2);
274
+ }
275
+ function defaultThemePreset() {
276
+ return THEME_PRESETS[0];
277
+ }
278
+
279
+ // src/types/brand.ts
280
+ function brandTokensFromPreset(preset) {
281
+ return {
282
+ colors: {
283
+ primary: preset.colors.primary,
284
+ background: preset.colors.background,
285
+ text: preset.colors.text,
286
+ subtext: preset.colors.subtext,
287
+ border: preset.colors.border
288
+ },
289
+ radius: preset.radius,
290
+ fontFamily: preset.fontFamily
291
+ };
292
+ }
293
+ function promptThemeFromBrandTokens(tokens) {
294
+ return {
295
+ colors: { ...tokens.colors },
296
+ radius: tokens.radius,
297
+ fontFamily: tokens.fontFamily
298
+ };
299
+ }
300
+ function inAppColorsFromBrandTokens(tokens) {
301
+ return {
302
+ backgroundColor: tokens.colors.background,
303
+ accentColor: tokens.colors.primary
304
+ };
305
+ }
306
+ function requestAccentFromBrandTokens(tokens) {
307
+ return tokens.colors.primary;
308
+ }
309
+
310
+ // src/types/integration.ts
311
+ var INTEGRATION_PROVIDERS = ["amplitude", "mixpanel"];
312
+ var INTEGRATION_CATEGORIES = [
313
+ "feedback",
314
+ "surveys",
315
+ "inapp",
316
+ "push",
317
+ "requests",
318
+ "lifecycle"
319
+ ];
320
+ var USER_IDENTIFIED_EVENT_NAME = "$user_identified";
321
+ function camelize(value) {
322
+ return value.replace(/_([a-z])/g, (_match, letter) => letter.toUpperCase());
323
+ }
324
+ var ids = (...keys) => Object.fromEntries(keys.map((key2) => [key2, [key2, camelize(key2)]]));
325
+ var pushEvents = [
326
+ ["$push_sent", "UserGist Push Sent"],
327
+ ["$push_delivered", "UserGist Push Delivered"],
328
+ ["$push_received", "UserGist Push Received"],
329
+ ["$push_displayed", "UserGist Push Displayed"],
330
+ ["$push_opened", "UserGist Push Opened"],
331
+ ["$push_dismissed", "UserGist Push Dismissed"],
332
+ ["$push_clicked", "UserGist Push Clicked"],
333
+ ["$push_action_clicked", "UserGist Push Action Clicked"],
334
+ ["$push_bounced", "UserGist Push Bounced"]
335
+ ];
336
+ var requestEvents = [
337
+ ["$request_submitted", "UserGist Request Submitted", "actor"],
338
+ ["$request_upvoted", "UserGist Request Upvoted", "actor"],
339
+ ["$request_unupvoted", "UserGist Request Unupvoted", "actor"],
340
+ ["$request_followed", "UserGist Request Followed", "actor"],
341
+ ["$request_unfollowed", "UserGist Request Unfollowed", "actor"],
342
+ ["$request_status_changed", "UserGist Request Status Changed", "affected_user"],
343
+ ["$request_responded", "UserGist Request Responded", "affected_user"],
344
+ ["$request_commented", "UserGist Request Commented", "actor"],
345
+ ["$request_comment_edited", "UserGist Request Comment Edited", "actor"],
346
+ ["$request_comment_deleted", "UserGist Request Comment Deleted", "affected_user"]
347
+ ];
348
+ var answerProperties = {
349
+ ...ids(
350
+ "question_id",
351
+ "question_type",
352
+ "question_title",
353
+ "answer_format",
354
+ "answer_present",
355
+ "answer_numeric_value",
356
+ "answer_selection_count",
357
+ "answer_text_length"
358
+ ),
359
+ // These aliases are generated only by the trusted server response paths.
360
+ // The API delivery boundary removes them from public SDK events before this
361
+ // normalizer runs. The complete answer_value stays in UserGist; destinations
362
+ // receive a value only for server-verified structured data.
363
+ answer_value: ["__usergist_destination_answer_value"],
364
+ answer_display_value: ["__usergist_destination_answer_display_value"]
365
+ };
366
+ var CORE_INTEGRATION_EVENTS = [
367
+ { key: "$app_open", name: "UserGist App Opened", category: "lifecycle", subjectRole: "actor", properties: {} },
368
+ { key: "$app_version_changed", name: "UserGist App Version Changed", category: "lifecycle", subjectRole: "actor", properties: { ...ids("old_version", "new_version") } },
369
+ { key: USER_IDENTIFIED_EVENT_NAME, name: "UserGist User Identified", category: "identity", subjectRole: "actor", required: true, properties: {} },
370
+ { key: "$feedback_prompt_shown", name: "UserGist Feedback Prompt Shown", category: "feedback", subjectRole: "actor", properties: { ...ids("prompt_id") } },
371
+ { key: "$feedback_response", name: "UserGist Feedback Response Submitted", category: "feedback", subjectRole: "actor", properties: { ...ids("response_id", "prompt_id", "prompt_name", "dismissed", "latency_ms", "answer_count", "feedback_types") } },
372
+ { key: "$feedback_answer_submitted", name: "UserGist Feedback Answer Submitted", category: "feedback", subjectRole: "actor", properties: { ...ids("response_id", "prompt_id", "prompt_name"), ...answerProperties } },
373
+ { key: "$survey_started", name: "UserGist Survey Started", category: "surveys", subjectRole: "actor", properties: { ...ids("campaign_id", "survey_name", "attempt_id", "language", "source") } },
374
+ { key: "$survey_answer_submitted", name: "UserGist Survey Answer Submitted", category: "surveys", subjectRole: "actor", properties: { ...ids("response_id", "campaign_id", "survey_name", "attempt_id", "language"), ...answerProperties } },
375
+ { key: "$survey_completed", name: "UserGist Survey Completed", category: "surveys", subjectRole: "actor", properties: { ...ids("campaign_id", "survey_name", "attempt_id", "language", "source", "duration_ms") } },
376
+ { key: "$survey_abandoned", name: "UserGist Survey Abandoned", category: "surveys", subjectRole: "actor", properties: { ...ids("campaign_id", "survey_name", "attempt_id", "language", "source", "duration_ms") } },
377
+ { key: "$inapp_shown", name: "UserGist In-App Message Shown", category: "inapp", subjectRole: "actor", properties: { ...ids("message_id") } },
378
+ { key: "$inapp_dismissed", name: "UserGist In-App Message Dismissed", category: "inapp", subjectRole: "actor", properties: { ...ids("message_id", "dismiss_reason") } },
379
+ { key: "$inapp_auto_dismissed", name: "UserGist In-App Message Auto-Dismissed", category: "inapp", subjectRole: "actor", properties: { ...ids("message_id", "dismiss_reason") } },
380
+ { key: "$inapp_cta_clicked", name: "UserGist In-App Message CTA Clicked", category: "inapp", subjectRole: "actor", properties: { ...ids("message_id", "cta_id", "cta_index"), action: ["action", "cta_action"] } },
381
+ ...pushEvents.map(([key2, name]) => ({
382
+ key: key2,
383
+ name,
384
+ category: "push",
385
+ subjectRole: "actor",
386
+ properties: {
387
+ ...ids("delivery_id", "campaign_id", "delivery_state", "failure_code"),
388
+ action_id: ["action_id", "actionId", "action_button", "actionButton"]
389
+ }
390
+ })),
391
+ ...requestEvents.map(([key2, name, subjectRole]) => ({ key: key2, name, category: "requests", subjectRole, properties: { ...ids("request_id", "comment_id", "source", "old_status", "new_status", "deleted_by"), usergist_actor_type: ["usergist_actor_type", "actor_type"] } }))
392
+ ];
393
+ var CORE_EVENT_BY_KEY = new Map(CORE_INTEGRATION_EVENTS.map((event) => [event.key, event]));
394
+ function getCoreIntegrationEvent(key2) {
395
+ return CORE_EVENT_BY_KEY.get(key2) ?? null;
396
+ }
397
+ function sanitizeCoreIntegrationProperties(definition, input) {
398
+ if (!input) return {};
399
+ const output = {};
400
+ for (const [outputKey, aliases] of Object.entries(definition.properties)) {
401
+ const alias = aliases.find((candidate) => Object.prototype.hasOwnProperty.call(input, candidate));
402
+ if (!alias) continue;
403
+ const value = input[alias];
404
+ if (typeof value === "string") output[outputKey] = value.slice(0, 1024);
405
+ else if (typeof value === "number" && Number.isFinite(value)) output[outputKey] = value;
406
+ else if (typeof value === "boolean" || value === null) output[outputKey] = value;
407
+ }
408
+ return output;
409
+ }
410
+
411
+ // src/constants/push-events.ts
412
+ var PUSH_EVENTS = {
413
+ SENT: "$push_sent",
414
+ DELIVERED: "$push_delivered",
415
+ DISPLAYED: "$push_displayed",
416
+ RECEIVED: "$push_received",
417
+ OPENED: "$push_opened",
418
+ DISMISSED: "$push_dismissed",
419
+ CLICKED: "$push_clicked",
420
+ ACTION_CLICKED: "$push_action_clicked",
421
+ BOUNCED: "$push_bounced",
422
+ SILENT_ACK: "$push_silent_ack"
423
+ };
424
+
425
+ // src/types/push-runtime.ts
426
+ function isSilentPushPayload(raw) {
427
+ if (!raw) return false;
428
+ return raw.usergist_silent === "1" && typeof raw.usergist_ping_id === "string";
429
+ }
430
+
431
+ // src/evaluate/segment.ts
432
+ function evaluateSegment(dsl, user, now = Date.now()) {
433
+ return evaluateGroup(dsl.root, user, now);
434
+ }
435
+ function evaluateGroup(group, user, now) {
436
+ if (group.predicates.length === 0) return true;
437
+ if (group.combinator === "AND") {
438
+ return group.predicates.every((p) => evaluateNode(p, user, now));
439
+ }
440
+ return group.predicates.some((p) => evaluateNode(p, user, now));
441
+ }
442
+ function evaluateNode(node, user, now) {
443
+ if ("combinator" in node) return evaluateGroup(node, user, now);
444
+ switch (node.kind) {
445
+ case "user_property":
446
+ return evaluateUserProperty(node, user);
447
+ case "event_count":
448
+ return evaluateEventCount(node, user);
449
+ case "event_occurred":
450
+ return evaluateEventOccurred(node, user, now);
451
+ }
452
+ }
453
+ function evaluateUserProperty(p, user) {
454
+ const actual = user.properties[p.key];
455
+ switch (p.op) {
456
+ case "exists":
457
+ return actual !== void 0 && actual !== null;
458
+ case "not_exists":
459
+ return actual === void 0 || actual === null;
460
+ case "eq":
461
+ return actual === p.value;
462
+ case "neq":
463
+ return actual !== p.value;
464
+ case "in":
465
+ return Array.isArray(p.value) && actual != null && p.value.includes(actual);
466
+ case "nin":
467
+ return Array.isArray(p.value) && actual != null && !p.value.includes(actual);
468
+ case "gt":
469
+ return typeof actual === "number" && typeof p.value === "number" && actual > p.value;
470
+ case "gte":
471
+ return typeof actual === "number" && typeof p.value === "number" && actual >= p.value;
472
+ case "lt":
473
+ return typeof actual === "number" && typeof p.value === "number" && actual < p.value;
474
+ case "lte":
475
+ return typeof actual === "number" && typeof p.value === "number" && actual <= p.value;
476
+ case "contains":
477
+ return typeof actual === "string" && typeof p.value === "string" && actual.includes(p.value);
478
+ case "starts_with":
479
+ return typeof actual === "string" && typeof p.value === "string" && actual.startsWith(p.value);
480
+ }
481
+ }
482
+ function evaluateEventCount(p, user) {
483
+ const eventBucket = user.eventCounts[p.eventName];
484
+ const count = eventBucket?.[p.windowDays] ?? 0;
485
+ switch (p.op) {
486
+ case "eq":
487
+ return count === p.count;
488
+ case "gte":
489
+ return count >= p.count;
490
+ case "lte":
491
+ return count <= p.count;
492
+ case "gt":
493
+ return count > p.count;
494
+ case "lt":
495
+ return count < p.count;
496
+ }
497
+ }
498
+ function evaluateEventOccurred(p, user, now) {
499
+ const last = user.lastEventAt[p.eventName];
500
+ if (p.ever) {
501
+ if (!last) return false;
502
+ if (p.windowDays == null) return true;
503
+ const cutoff2 = now - p.windowDays * 864e5;
504
+ return Date.parse(last) >= cutoff2;
505
+ }
506
+ if (!last) return true;
507
+ if (p.windowDays == null) return false;
508
+ const cutoff = now - p.windowDays * 864e5;
509
+ return Date.parse(last) < cutoff;
510
+ }
511
+ function evaluateSerializedSegmentRules(rules, user, now = Date.now()) {
512
+ if (!rules) return true;
513
+ const dsl = serializedRulesToDsl(rules);
514
+ return evaluateSegment(dsl, user, now);
515
+ }
516
+ function serializedRulesToDsl(rules) {
517
+ const predicates = [];
518
+ for (const rule of rules.userProperties ?? []) {
519
+ predicates.push(userPropertyRuleToPredicate(rule));
520
+ }
521
+ for (const rule of rules.eventCounts ?? []) {
522
+ predicates.push({
523
+ kind: "event_count",
524
+ eventName: rule.eventName,
525
+ op: rule.op,
526
+ count: rule.count,
527
+ windowDays: rule.windowDays
528
+ });
529
+ }
530
+ return {
531
+ version: 1,
532
+ root: { combinator: "AND", predicates }
533
+ };
534
+ }
535
+ function userPropertyRuleToPredicate(rule) {
536
+ return {
537
+ kind: "user_property",
538
+ key: rule.key,
539
+ op: rule.op,
540
+ value: rule.value
541
+ };
542
+ }
543
+
544
+ // src/evaluate/branch.ts
545
+ function evaluateBranchCondition(cond, answer) {
546
+ if (cond.op === "answered") return answer !== void 0 && answer !== null && answer !== "";
547
+ if (cond.op === "unanswered") return answer === void 0 || answer === null || answer === "";
548
+ if (answer === void 0 || answer === null) return false;
549
+ switch (cond.op) {
550
+ case "eq":
551
+ return answer === cond.value;
552
+ case "neq":
553
+ return answer !== cond.value;
554
+ case "lt":
555
+ return typeof answer === "number" && typeof cond.value === "number" && answer < cond.value;
556
+ case "lte":
557
+ return typeof answer === "number" && typeof cond.value === "number" && answer <= cond.value;
558
+ case "gt":
559
+ return typeof answer === "number" && typeof cond.value === "number" && answer > cond.value;
560
+ case "gte":
561
+ return typeof answer === "number" && typeof cond.value === "number" && answer >= cond.value;
562
+ case "includes":
563
+ return Array.isArray(answer) && answer.includes(cond.value);
564
+ case "not_includes":
565
+ return Array.isArray(answer) && !answer.includes(cond.value);
566
+ default:
567
+ return false;
568
+ }
569
+ }
570
+ function nextQuestionId(flow, currentQuestionId, answers) {
571
+ const branches = flow.branches.filter((b) => b.fromQuestionId === currentQuestionId);
572
+ const answer = answers[currentQuestionId];
573
+ for (const branch of branches) {
574
+ if (evaluateBranchCondition(branch.condition, answer)) {
575
+ if (branch.toQuestionId === SURVEY_END_SENTINEL) return null;
576
+ return branch.toQuestionId;
577
+ }
578
+ }
579
+ return defaultNext(flow, currentQuestionId);
580
+ }
581
+ function defaultNext(flow, currentQuestionId) {
582
+ const idx = flow.questions.findIndex((q) => q.id === currentQuestionId);
583
+ if (idx < 0 || idx >= flow.questions.length - 1) return null;
584
+ const next = flow.questions[idx + 1];
585
+ return next ? next.id : null;
586
+ }
587
+ function findQuestion(flow, questionId) {
588
+ return flow.questions.find((q) => q.id === questionId);
589
+ }
590
+ function findQuestionIndex(flow, questionId) {
591
+ return flow.questions.findIndex((q) => q.id === questionId);
592
+ }
593
+ function estimateProgress(flow, currentQuestionId) {
594
+ if (!currentQuestionId) return 1;
595
+ const idx = findQuestionIndex(flow, currentQuestionId);
596
+ if (idx < 0) return 0;
597
+ return (idx + 1) / Math.max(1, flow.questions.length);
598
+ }
599
+ function reachableQuestions(flow, startId) {
600
+ const start = startId ?? flow.startQuestionId;
601
+ const visited = /* @__PURE__ */ new Set();
602
+ const stack = [start];
603
+ while (stack.length > 0) {
604
+ const id2 = stack.pop();
605
+ if (id2 === void 0) continue;
606
+ if (visited.has(id2)) continue;
607
+ visited.add(id2);
608
+ const outgoing = flow.branches.filter((b) => b.fromQuestionId === id2);
609
+ for (const b of outgoing) {
610
+ if (b.toQuestionId !== SURVEY_END_SENTINEL) stack.push(b.toQuestionId);
611
+ }
612
+ const idx = flow.questions.findIndex((q) => q.id === id2);
613
+ if (idx >= 0 && idx < flow.questions.length - 1) {
614
+ const next = flow.questions[idx + 1];
615
+ if (next) stack.push(next.id);
616
+ }
617
+ }
618
+ return visited;
619
+ }
620
+
621
+ // src/evaluate/periodic.ts
622
+ function nextPeriodicFire(schedule, now = /* @__PURE__ */ new Date()) {
623
+ const tz = schedule.tz || "UTC";
624
+ const start = startCandidate(schedule, now, tz);
625
+ return walkForward(start, schedule, now, tz);
626
+ }
627
+ function tzOffsetMinutes(at, tz) {
628
+ if (tz === "UTC") return 0;
629
+ try {
630
+ const fmt = getFormatter(tz);
631
+ const parts = fmt.formatToParts(at).reduce((acc, p) => {
632
+ if (p.type !== "literal") acc[p.type] = p.value;
633
+ return acc;
634
+ }, {});
635
+ const utc = Date.UTC(
636
+ Number(parts.year),
637
+ Number(parts.month) - 1,
638
+ Number(parts.day),
639
+ Number(parts.hour) === 24 ? 0 : Number(parts.hour),
640
+ Number(parts.minute),
641
+ Number(parts.second)
642
+ );
643
+ return Math.round((utc - at.getTime()) / 6e4);
644
+ } catch {
645
+ return 0;
646
+ }
647
+ }
648
+ function startCandidate(schedule, now, tz) {
649
+ const localNow = wallClock(now, tz);
650
+ const target = new Date(localNow);
651
+ target.setUTCHours(schedule.hourLocal, schedule.minuteLocal, 0, 0);
652
+ return localToUtc(target, tz);
653
+ }
654
+ function walkForward(candidate, schedule, now, tz) {
655
+ let cursor = candidate;
656
+ const max = schedule.frequency === "daily" ? 2 : schedule.frequency === "weekly" ? 8 : 13;
657
+ for (let i = 0; i < max; i++) {
658
+ const local = wallClock(cursor, tz);
659
+ if (matchesFrequency(local, schedule) && cursor.getTime() > now.getTime()) {
660
+ return cursor;
661
+ }
662
+ cursor = step(cursor, local, schedule, tz);
663
+ }
664
+ return cursor;
665
+ }
666
+ function matchesFrequency(local, schedule) {
667
+ if (schedule.frequency === "daily") return true;
668
+ if (schedule.frequency === "weekly") {
669
+ return schedule.weekday === void 0 || local.getUTCDay() === schedule.weekday;
670
+ }
671
+ return schedule.dayOfMonth === void 0 || local.getUTCDate() === schedule.dayOfMonth;
672
+ }
673
+ function step(cursor, cursorLocal, schedule, tz) {
674
+ if (schedule.frequency === "daily" || schedule.frequency === "weekly") {
675
+ return addDays(cursor, 1);
676
+ }
677
+ const next = new Date(cursorLocal);
678
+ next.setUTCMonth(next.getUTCMonth() + 1);
679
+ if (schedule.dayOfMonth !== void 0) {
680
+ const lastDay = lastDayOfMonth(next.getUTCFullYear(), next.getUTCMonth());
681
+ next.setUTCDate(Math.min(schedule.dayOfMonth, lastDay));
682
+ }
683
+ next.setUTCHours(schedule.hourLocal, schedule.minuteLocal, 0, 0);
684
+ return localToUtc(next, tz);
685
+ }
686
+ function addDays(d, days) {
687
+ const next = new Date(d.getTime());
688
+ next.setUTCDate(next.getUTCDate() + days);
689
+ return next;
690
+ }
691
+ function lastDayOfMonth(year, monthIndex) {
692
+ return new Date(Date.UTC(year, monthIndex + 1, 0)).getUTCDate();
693
+ }
694
+ function wallClock(d, tz) {
695
+ const offsetMin = tzOffsetMinutes(d, tz);
696
+ return new Date(d.getTime() + offsetMin * 6e4);
697
+ }
698
+ function localToUtc(localView, tz) {
699
+ const offsetMin = tzOffsetMinutes(localView, tz);
700
+ return new Date(localView.getTime() - offsetMin * 6e4);
701
+ }
702
+ var FORMATTER_CACHE = /* @__PURE__ */ new Map();
703
+ function getFormatter(tz) {
704
+ const cached = FORMATTER_CACHE.get(tz);
705
+ if (cached) return cached;
706
+ const fmt = new Intl.DateTimeFormat("en-US", {
707
+ timeZone: tz,
708
+ hour12: false,
709
+ year: "numeric",
710
+ month: "2-digit",
711
+ day: "2-digit",
712
+ hour: "2-digit",
713
+ minute: "2-digit",
714
+ second: "2-digit"
715
+ });
716
+ FORMATTER_CACHE.set(tz, fmt);
717
+ return fmt;
718
+ }
719
+
720
+ // src/types/web.ts
721
+ var DELIVERY_PROTOCOL_VERSION = 2;
722
+ function deliveryPlatformsForApp(platforms) {
723
+ const values = /* @__PURE__ */ new Set();
724
+ for (const platform of platforms) {
725
+ if (platform === "web" || platform === "ios" || platform === "android") values.add(platform);
726
+ if (platform === "expo" || platform === "react-native" || platform === "flutter") {
727
+ values.add("ios");
728
+ values.add("android");
729
+ }
730
+ }
731
+ return [...values];
732
+ }
733
+ function resolveWebPresentation(pillar, override) {
734
+ return {
735
+ layout: override?.layout ?? "modal",
736
+ size: override?.size ?? (pillar === "requests" ? "wide" : "standard"),
737
+ position: override?.position ?? "right",
738
+ backdrop: override?.backdrop ?? override?.layout !== "card"
739
+ };
740
+ }
741
+ var id = z.string().regex(/^[A-Za-z][A-Za-z0-9_]{0,63}$/);
742
+ var key = z.string().min(1).max(120).refine(
743
+ (v) => !["__proto__", "prototype", "constructor"].includes(v),
744
+ "Reserved property key"
745
+ );
746
+ var scalar = z.union([
747
+ z.string().max(8192),
748
+ z.number().finite(),
749
+ z.boolean(),
750
+ z.null()
751
+ ]);
752
+ var baseSource = { id, label: z.string().min(1).max(120) };
753
+ var filterSchema = z.object({
754
+ key,
755
+ op: z.enum([
756
+ "eq",
757
+ "neq",
758
+ "gt",
759
+ "gte",
760
+ "lt",
761
+ "lte",
762
+ "in",
763
+ "nin",
764
+ "contains",
765
+ "starts_with",
766
+ "exists",
767
+ "not_exists"
768
+ ]),
769
+ value: z.union([
770
+ z.string(),
771
+ z.number().finite(),
772
+ z.boolean(),
773
+ z.array(z.union([z.string(), z.number().finite()])).max(100)
774
+ ]).optional()
775
+ });
776
+ var personalizationSpecSchema = z.object({
777
+ version: z.literal(1),
778
+ sources: z.array(
779
+ z.discriminatedUnion("kind", [
780
+ z.object({ ...baseSource, kind: z.literal("user_property") }),
781
+ z.object({ ...baseSource, kind: z.literal("trigger_event") }),
782
+ z.object({ ...baseSource, kind: z.literal("send_data") }),
783
+ z.object({ ...baseSource, kind: z.literal("app") }),
784
+ z.object({ ...baseSource, kind: z.literal("now") }),
785
+ z.object({
786
+ ...baseSource,
787
+ kind: z.literal("latest_event"),
788
+ eventName: z.string().min(1).max(120),
789
+ lookbackDays: z.number().int().min(1).max(90),
790
+ filters: z.array(filterSchema).max(8).optional()
791
+ })
792
+ ])
793
+ ).max(16),
794
+ bindings: z.array(
795
+ z.object({
796
+ id,
797
+ label: z.string().min(1).max(120),
798
+ sourceId: id,
799
+ key,
800
+ type: z.enum(["string", "number", "boolean", "date"]),
801
+ fallback: scalar.optional()
802
+ })
803
+ ).max(64),
804
+ missingData: z.literal("skip")
805
+ }).superRefine((spec, ctx) => {
806
+ const sources = new Set(spec.sources.map((source) => source.id));
807
+ if (sources.size !== spec.sources.length)
808
+ ctx.addIssue({
809
+ code: "custom",
810
+ path: ["sources"],
811
+ message: "Source IDs must be unique"
812
+ });
813
+ if (spec.sources.filter((source) => source.kind === "latest_event").length > 4)
814
+ ctx.addIssue({
815
+ code: "custom",
816
+ path: ["sources"],
817
+ message: "Use at most four activity sources"
818
+ });
819
+ const bindings = /* @__PURE__ */ new Set();
820
+ spec.bindings.forEach((binding, index) => {
821
+ if (bindings.has(binding.id))
822
+ ctx.addIssue({
823
+ code: "custom",
824
+ path: ["bindings", index, "id"],
825
+ message: "Field IDs must be unique"
826
+ });
827
+ bindings.add(binding.id);
828
+ if (!sources.has(binding.sourceId))
829
+ ctx.addIssue({
830
+ code: "custom",
831
+ path: ["bindings", index, "sourceId"],
832
+ message: "Choose an existing source"
833
+ });
834
+ if (binding.fallback !== void 0 && (isMissing(binding.fallback) || !matchesType(binding.fallback, binding.type)))
835
+ ctx.addIssue({
836
+ code: "custom",
837
+ path: ["bindings", index, "fallback"],
838
+ message: "Fallback must be a non-empty value of the selected type"
839
+ });
840
+ });
841
+ });
842
+ var userPropertiesUpdateSchema = z.object({
843
+ mutationId: z.string().uuid(),
844
+ set: z.record(key, scalar).refine(
845
+ (v) => Object.keys(v).length <= 64,
846
+ "Update at most 64 properties"
847
+ ).optional(),
848
+ unset: z.array(key).max(64).optional()
849
+ }).superRefine((update, ctx) => {
850
+ if (!Object.keys(update.set ?? {}).length && !update.unset?.length)
851
+ ctx.addIssue({
852
+ code: "custom",
853
+ message: "Supply properties to set or remove"
854
+ });
855
+ if (update.unset?.some((name) => Object.hasOwn(update.set ?? {}, name)))
856
+ ctx.addIssue({
857
+ code: "custom",
858
+ message: "A property cannot be set and removed in the same update"
859
+ });
860
+ });
861
+ function isMissing(value) {
862
+ return value == null || typeof value === "string" && !value.trim();
863
+ }
864
+ function matchesType(value, type) {
865
+ if (type === "date")
866
+ return typeof value === "string" && !Number.isNaN(Date.parse(value));
867
+ return typeof value === type && (typeof value !== "number" || Number.isFinite(value));
868
+ }
869
+ function resolvePersonalizationBindings(spec, sources) {
870
+ const values = /* @__PURE__ */ Object.create(null);
871
+ const issues = [];
872
+ const fallbackBindingIds = [];
873
+ for (const binding of spec.bindings) {
874
+ const source = sources[binding.sourceId]?.values;
875
+ const value = source && Object.hasOwn(source, binding.key) ? source[binding.key] : void 0;
876
+ const missing = isMissing(value);
877
+ if (!missing && matchesType(value, binding.type))
878
+ values[binding.id] = value;
879
+ else if (missing && binding.fallback !== void 0) {
880
+ values[binding.id] = binding.fallback;
881
+ fallbackBindingIds.push(binding.id);
882
+ } else
883
+ issues.push({
884
+ code: missing ? "missing" : "type_mismatch",
885
+ bindingId: binding.id,
886
+ message: missing ? `${binding.label} is missing` : `${binding.label} must be ${binding.type}`
887
+ });
888
+ }
889
+ return {
890
+ status: issues.length ? "skipped" : fallbackBindingIds.length ? "using_fallback" : "ready",
891
+ values,
892
+ fallbackBindingIds,
893
+ issues,
894
+ sources
895
+ };
896
+ }
897
+ var TOKEN = /\{\{\s*p\.([A-Za-z][A-Za-z0-9_]*)\s*\}\}/g;
898
+ var WHOLE_TOKEN = /^\{\{\s*p\.([A-Za-z][A-Za-z0-9_]*)\s*\}\}$/;
899
+ function personalizationToken(bindingId) {
900
+ return `{{p.${bindingId}}}`;
901
+ }
902
+ function renderPersonalizedValue(input, values, issues, path = "", mode = "text") {
903
+ if (typeof input === "string") {
904
+ const whole = WHOLE_TOKEN.exec(input);
905
+ const lookup = (bindingId) => {
906
+ if (Object.hasOwn(values, bindingId)) return values[bindingId];
907
+ issues.push({
908
+ code: "unknown_binding",
909
+ bindingId,
910
+ path,
911
+ message: `No value is available for ${bindingId}`
912
+ });
913
+ return null;
914
+ };
915
+ if (mode === "json" && whole) return lookup(whole[1]);
916
+ if (/\{\{|\}\}/.test(input.replace(TOKEN, "")))
917
+ issues.push({
918
+ code: "invalid_template",
919
+ path,
920
+ message: "Insert a valid dynamic field"
921
+ });
922
+ const result = input.replace(TOKEN, (_, bindingId) => {
923
+ const value = lookup(bindingId);
924
+ return mode === "url" && !whole ? encodeURIComponent(String(value ?? "")) : String(value ?? "");
925
+ });
926
+ if (mode === "url" && result) {
927
+ try {
928
+ const url = new URL(result);
929
+ if (["javascript:", "data:", "file:", "vbscript:"].includes(url.protocol))
930
+ throw new Error("unsafe");
931
+ if (!whole && /\{\{/.test(input.split(/:\/\//)[0] ?? ""))
932
+ throw new Error("dynamic scheme");
933
+ if (!whole && /\{\{/.test(
934
+ input.match(/^[a-z][a-z0-9+.-]*:\/\/([^/?#]*)/i)?.[1] ?? ""
935
+ ))
936
+ throw new Error("dynamic authority");
937
+ } catch {
938
+ issues.push({
939
+ code: "invalid_destination",
940
+ path,
941
+ message: "The resolved destination must be a valid app link or web URL"
942
+ });
943
+ }
944
+ }
945
+ return result;
946
+ }
947
+ if (Array.isArray(input))
948
+ return input.map(
949
+ (item, index) => renderPersonalizedValue(item, values, issues, `${path}/${index}`, mode)
950
+ );
951
+ if (input && typeof input === "object") {
952
+ const result = /* @__PURE__ */ Object.create(null);
953
+ for (const [name, value] of Object.entries(input)) {
954
+ if (["__proto__", "constructor", "prototype"].includes(name)) {
955
+ issues.push({
956
+ code: "invalid_template",
957
+ path,
958
+ message: "Reserved object key"
959
+ });
960
+ continue;
961
+ }
962
+ result[name] = renderPersonalizedValue(
963
+ value,
964
+ values,
965
+ issues,
966
+ `${path}/${name}`,
967
+ mode
968
+ );
969
+ }
970
+ return result;
971
+ }
972
+ return input;
973
+ }
974
+ var TEXT_KEYS = /* @__PURE__ */ new Set([
975
+ "title",
976
+ "body",
977
+ "subtitle",
978
+ "headline",
979
+ "label",
980
+ "placeholder",
981
+ "followUp",
982
+ "lowLabel",
983
+ "highLabel",
984
+ "description",
985
+ "buttonText",
986
+ "submitLabel",
987
+ "nextLabel",
988
+ "backLabel",
989
+ "text"
990
+ ]);
991
+ var URL_KEYS = /* @__PURE__ */ new Set(["imageUrl", "deepLink", "target"]);
992
+ var CONTENT_KEYS = /* @__PURE__ */ new Set([
993
+ "questions",
994
+ "options",
995
+ "ctas",
996
+ "cta",
997
+ "actionButtons",
998
+ "flow",
999
+ "welcome",
1000
+ "thankYou",
1001
+ "completion",
1002
+ "endScreen",
1003
+ "openAction"
1004
+ ]);
1005
+ function personalizationUsage(content) {
1006
+ const usage = /* @__PURE__ */ new Map();
1007
+ const scan = (value, field) => {
1008
+ if (typeof value === "string") {
1009
+ for (const match of value.matchAll(TOKEN)) {
1010
+ const fields = usage.get(match[1]) ?? /* @__PURE__ */ new Set();
1011
+ fields.add(field);
1012
+ usage.set(match[1], fields);
1013
+ }
1014
+ } else if (Array.isArray(value)) value.forEach((item) => scan(item, field));
1015
+ else if (value && typeof value === "object")
1016
+ Object.values(value).forEach((item) => scan(item, field));
1017
+ };
1018
+ const walk = (value) => {
1019
+ if (Array.isArray(value)) {
1020
+ value.forEach(walk);
1021
+ return;
1022
+ }
1023
+ if (!value || typeof value !== "object") return;
1024
+ for (const [key2, item] of Object.entries(value)) {
1025
+ if (TEXT_KEYS.has(key2) || URL_KEYS.has(key2) || key2 === "actionJson" || key2 === "payloadExtras")
1026
+ scan(item, key2);
1027
+ else if (CONTENT_KEYS.has(key2)) walk(item);
1028
+ }
1029
+ };
1030
+ walk(content);
1031
+ return usage;
1032
+ }
1033
+ function renderPersonalizedContent(content, resolution) {
1034
+ const usage = personalizationUsage(content);
1035
+ const onlyOptionalImage = (id2) => usage.get(id2)?.size === 1 && usage.get(id2)?.has("imageUrl");
1036
+ const issues = resolution.issues.filter(
1037
+ (issue) => !issue.bindingId || usage.has(issue.bindingId) && !(issue.code === "missing" && onlyOptionalImage(issue.bindingId))
1038
+ );
1039
+ const fallbacks = resolution.fallbackBindingIds.filter((id2) => usage.has(id2));
1040
+ const walk = (value, path = "") => {
1041
+ if (Array.isArray(value))
1042
+ return value.map((item, index) => walk(item, `${path}/${index}`));
1043
+ if (!value || typeof value !== "object") return value;
1044
+ return Object.fromEntries(
1045
+ Object.entries(value).map(([name, item]) => {
1046
+ const childPath = `${path}/${name}`;
1047
+ if (TEXT_KEYS.has(name) && typeof item === "string")
1048
+ return [
1049
+ name,
1050
+ renderPersonalizedValue(item, resolution.values, issues, childPath)
1051
+ ];
1052
+ if (URL_KEYS.has(name) && typeof item === "string") {
1053
+ if (name === "imageUrl" && [...item.matchAll(TOKEN)].some(
1054
+ (match) => resolution.issues.some(
1055
+ (issue) => issue.bindingId === match[1] && issue.code === "missing"
1056
+ )
1057
+ ))
1058
+ return [name, null];
1059
+ const isEvent = name === "target" && value.action === "custom_event";
1060
+ const rendered2 = renderPersonalizedValue(
1061
+ item,
1062
+ resolution.values,
1063
+ issues,
1064
+ childPath,
1065
+ isEvent ? "text" : "url"
1066
+ );
1067
+ if (name === "imageUrl" && rendered2 && !String(rendered2).startsWith("https://"))
1068
+ issues.push({
1069
+ code: "invalid_destination",
1070
+ path: childPath,
1071
+ message: "Image URLs must use HTTPS"
1072
+ });
1073
+ return [name, rendered2];
1074
+ }
1075
+ if (name === "actionJson" || name === "payloadExtras")
1076
+ return [
1077
+ name,
1078
+ renderPersonalizedValue(
1079
+ item,
1080
+ resolution.values,
1081
+ issues,
1082
+ childPath,
1083
+ "json"
1084
+ )
1085
+ ];
1086
+ if (CONTENT_KEYS.has(name)) return [name, walk(item, childPath)];
1087
+ return [name, item];
1088
+ })
1089
+ );
1090
+ };
1091
+ const rendered = walk(content);
1092
+ return {
1093
+ content: rendered,
1094
+ resolution: {
1095
+ ...resolution,
1096
+ fallbackBindingIds: fallbacks,
1097
+ status: issues.length ? "skipped" : fallbacks.length ? "using_fallback" : "ready",
1098
+ issues
1099
+ }
1100
+ };
1101
+ }
1102
+
1103
+ // src/presentation.ts
1104
+ var PresentationGate = class {
1105
+ paused;
1106
+ identity = 0;
1107
+ revisions = { feedback: 0, survey: 0 };
1108
+ listeners = /* @__PURE__ */ new Set();
1109
+ constructor(paused = false) {
1110
+ this.paused = paused;
1111
+ }
1112
+ get isPaused() {
1113
+ return this.paused;
1114
+ }
1115
+ setPaused(paused) {
1116
+ this.paused = paused;
1117
+ this.notify();
1118
+ }
1119
+ invalidate(purpose) {
1120
+ if (purpose) this.revisions[purpose]++;
1121
+ else this.identity++;
1122
+ this.notify();
1123
+ }
1124
+ validator(purpose) {
1125
+ const identity = this.identity;
1126
+ const revision = this.revisions[purpose];
1127
+ return () => identity === this.identity && revision === this.revisions[purpose];
1128
+ }
1129
+ subscribe(listener) {
1130
+ this.listeners.add(listener);
1131
+ return () => {
1132
+ this.listeners.delete(listener);
1133
+ };
1134
+ }
1135
+ runWhenReady(run, valid, cancel = () => {
1136
+ }) {
1137
+ if (!valid()) {
1138
+ cancel();
1139
+ return;
1140
+ }
1141
+ if (!this.paused) {
1142
+ run();
1143
+ return;
1144
+ }
1145
+ const stop = this.subscribe(() => {
1146
+ if (valid() && this.paused) return;
1147
+ stop();
1148
+ if (valid()) run();
1149
+ else cancel();
1150
+ });
1151
+ }
1152
+ waitUntilReady(valid) {
1153
+ if (!valid()) return Promise.resolve(false);
1154
+ if (!this.paused) return Promise.resolve(true);
1155
+ return new Promise((resolve) => {
1156
+ const stop = this.subscribe(() => {
1157
+ if (valid() && this.paused) return;
1158
+ stop();
1159
+ resolve(valid());
1160
+ });
1161
+ });
1162
+ }
1163
+ notify() {
1164
+ for (const listener of [...this.listeners]) listener();
1165
+ }
1166
+ };
1167
+
1168
+ export { APP_OPEN_EVENT_NAME, APP_VERSION_CHANGED_EVENT_NAME, AUDIENCE_JOIN_EVENT_NAME, AUDIENCE_JOIN_TRIGGER_KIND, CORE_INTEGRATION_EVENTS, DELIVERY_PROTOCOL_VERSION, INAPP_AUTO_DISMISSED_EVENT_NAME, INAPP_CTA_CLICKED_EVENT_NAME, INAPP_DISMISSED_EVENT_NAME, INAPP_SHOWN_EVENT_NAME, INTEGRATION_CATEGORIES, INTEGRATION_PROVIDERS, PUSH_EVENTS, PresentationGate, RADIUS_PRESETS, REQUEST_COMMENTED_EVENT_NAME, REQUEST_COMMENT_DELETED_EVENT_NAME, REQUEST_COMMENT_EDITED_EVENT_NAME, REQUEST_FOLLOWED_EVENT_NAME, REQUEST_RESPONDED_EVENT_NAME, REQUEST_STATUS_CHANGED_EVENT_NAME, REQUEST_SUBMITTED_EVENT_NAME, REQUEST_UNFOLLOWED_EVENT_NAME, REQUEST_UNUPVOTED_EVENT_NAME, REQUEST_UPVOTED_EVENT_NAME, SURVEY_END_SENTINEL, THEME_PRESETS, USER_IDENTIFIED_EVENT_NAME, brandTokensFromPreset, defaultEventTrigger, defaultOnboardingMessage, defaultThemePreset, defaultTriggerSpec, deliveryPlatformsForApp, emptyAudienceSpec, emptySegmentDsl, err, estimateProgress, evaluateBranchCondition, evaluateSegment, evaluateSerializedSegmentRules, findQuestion, findQuestionIndex, getCoreIntegrationEvent, getThemePresetById, inAppColorsFromBrandTokens, isSilentPushPayload, nextPeriodicFire, nextQuestionId, ok, personalizationSpecSchema, personalizationToken, personalizationUsage, promptThemeFromBrandTokens, reachableQuestions, renderPersonalizedContent, renderPersonalizedValue, requestAccentFromBrandTokens, resolvePersonalizationBindings, resolveWebPresentation, sanitizeCoreIntegrationProperties, serializedRulesToDsl, tzOffsetMinutes, userPropertiesUpdateSchema };
1169
+ //# sourceMappingURL=client.js.map
1170
+ //# sourceMappingURL=client.js.map