@usergist/sdk-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,1154 @@
1
+ 'use strict';
2
+
3
+ var zod = require('zod');
4
+
5
+ var __defProp = Object.defineProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+
11
+ // src/types/segment-dsl.ts
12
+ function emptySegmentDsl() {
13
+ return { version: 1, root: { combinator: "AND", predicates: [] } };
14
+ }
15
+
16
+ // src/types/recipient.ts
17
+ var RECIPIENTS_LIMIT = 100;
18
+
19
+ // src/types/api.ts
20
+ function ok(data, meta) {
21
+ return meta ? { success: true, data, meta } : { success: true, data };
22
+ }
23
+ function err(code, message, details) {
24
+ return {
25
+ success: false,
26
+ error: details === void 0 ? { code, message } : { code, message, details }
27
+ };
28
+ }
29
+
30
+ // src/types/survey.ts
31
+ var SURVEY_END_SENTINEL = "__end__";
32
+
33
+ // src/types/inapp-message.ts
34
+ var INAPP_SHOWN_EVENT_NAME = "$inapp_shown";
35
+ var INAPP_DISMISSED_EVENT_NAME = "$inapp_dismissed";
36
+ var INAPP_AUTO_DISMISSED_EVENT_NAME = "$inapp_auto_dismissed";
37
+ var INAPP_CTA_CLICKED_EVENT_NAME = "$inapp_cta_clicked";
38
+
39
+ // src/types/request.ts
40
+ var REQUEST_SUBMITTED_EVENT_NAME = "$request_submitted";
41
+ var REQUEST_UPVOTED_EVENT_NAME = "$request_upvoted";
42
+ var REQUEST_UNUPVOTED_EVENT_NAME = "$request_unupvoted";
43
+ var REQUEST_FOLLOWED_EVENT_NAME = "$request_followed";
44
+ var REQUEST_UNFOLLOWED_EVENT_NAME = "$request_unfollowed";
45
+ var REQUEST_STATUS_CHANGED_EVENT_NAME = "$request_status_changed";
46
+ var REQUEST_RESPONDED_EVENT_NAME = "$request_responded";
47
+ var REQUEST_COMMENTED_EVENT_NAME = "$request_commented";
48
+ var REQUEST_COMMENT_EDITED_EVENT_NAME = "$request_comment_edited";
49
+ var REQUEST_COMMENT_DELETED_EVENT_NAME = "$request_comment_deleted";
50
+
51
+ // src/types/targeting.ts
52
+ function emptyAudienceSpec() {
53
+ return { version: 1, mode: "everyone", combinator: "all", conditions: [] };
54
+ }
55
+ var APP_OPEN_EVENT_NAME = "$app_open";
56
+ var AUDIENCE_JOIN_EVENT_NAME = "$audience_join";
57
+ var AUDIENCE_JOIN_TRIGGER_KIND = "audience_join";
58
+ var APP_VERSION_CHANGED_EVENT_NAME = "$app_version_changed";
59
+ function defaultTriggerSpec() {
60
+ return { kind: "app_open" };
61
+ }
62
+ function defaultEventTrigger(eventName = "") {
63
+ return {
64
+ kind: "event",
65
+ eventName,
66
+ occurrence: { mode: "every" }
67
+ };
68
+ }
69
+
70
+ // src/types/themes.ts
71
+ var RADIUS_PRESETS = {
72
+ sharp: 4,
73
+ soft: 12,
74
+ rounded: 20,
75
+ pill: 999
76
+ };
77
+ var JAKARTA = "Plus Jakarta Sans";
78
+ var THEME_PRESETS = [
79
+ {
80
+ id: "minimal",
81
+ name: "Minimal",
82
+ description: "Clean neutral palette",
83
+ colors: {
84
+ primary: "#111827",
85
+ background: "#FFFFFF",
86
+ text: "#111827",
87
+ subtext: "#6B7280",
88
+ border: "#E5E7EB",
89
+ button: "#111827"
90
+ },
91
+ radius: 16,
92
+ fontFamily: JAKARTA,
93
+ swatches: ["#111827", "#FFFFFF", "#E5E7EB"]
94
+ },
95
+ {
96
+ id: "ocean",
97
+ name: "Ocean",
98
+ description: "Cool blue tones",
99
+ colors: {
100
+ primary: "#0EA5E9",
101
+ background: "#F0F9FF",
102
+ text: "#0C4A6E",
103
+ subtext: "#0369A1",
104
+ border: "#BAE6FD",
105
+ button: "#0EA5E9"
106
+ },
107
+ radius: 16,
108
+ fontFamily: JAKARTA,
109
+ swatches: ["#0EA5E9", "#0C4A6E", "#BAE6FD"]
110
+ },
111
+ {
112
+ id: "sunset",
113
+ name: "Sunset",
114
+ description: "Warm orange glow",
115
+ colors: {
116
+ primary: "#F97316",
117
+ background: "#FFF7ED",
118
+ text: "#7C2D12",
119
+ subtext: "#C2410C",
120
+ border: "#FED7AA",
121
+ button: "#F97316"
122
+ },
123
+ radius: 16,
124
+ fontFamily: JAKARTA,
125
+ swatches: ["#F97316", "#7C2D12", "#FED7AA"]
126
+ },
127
+ {
128
+ id: "forest",
129
+ name: "Forest",
130
+ description: "Natural green hues",
131
+ colors: {
132
+ primary: "#16A34A",
133
+ background: "#F0FDF4",
134
+ text: "#14532D",
135
+ subtext: "#166534",
136
+ border: "#BBF7D0",
137
+ button: "#16A34A"
138
+ },
139
+ radius: 16,
140
+ fontFamily: JAKARTA,
141
+ swatches: ["#16A34A", "#14532D", "#BBF7D0"]
142
+ },
143
+ {
144
+ id: "midnight",
145
+ name: "Midnight",
146
+ description: "Deep dark mode",
147
+ colors: {
148
+ primary: "#A78BFA",
149
+ background: "#0F172A",
150
+ text: "#F8FAFC",
151
+ subtext: "#94A3B8",
152
+ border: "#1E293B",
153
+ button: "#A78BFA"
154
+ },
155
+ radius: 16,
156
+ fontFamily: JAKARTA,
157
+ swatches: ["#A78BFA", "#0F172A", "#1E293B"]
158
+ },
159
+ {
160
+ id: "candy",
161
+ name: "Candy",
162
+ description: "Playful pink + violet",
163
+ colors: {
164
+ primary: "#EC4899",
165
+ background: "#FDF2F8",
166
+ text: "#831843",
167
+ subtext: "#BE185D",
168
+ border: "#FBCFE8",
169
+ button: "#EC4899"
170
+ },
171
+ radius: 20,
172
+ fontFamily: JAKARTA,
173
+ swatches: ["#EC4899", "#831843", "#FBCFE8"]
174
+ },
175
+ {
176
+ id: "mono",
177
+ name: "Mono",
178
+ description: "High-contrast B&W",
179
+ colors: {
180
+ primary: "#000000",
181
+ background: "#FFFFFF",
182
+ text: "#000000",
183
+ subtext: "#525252",
184
+ border: "#000000",
185
+ button: "#000000"
186
+ },
187
+ radius: 4,
188
+ fontFamily: JAKARTA,
189
+ swatches: ["#000000", "#FFFFFF", "#525252"]
190
+ },
191
+ {
192
+ id: "coral",
193
+ name: "Coral",
194
+ description: "Soft coral accent",
195
+ colors: {
196
+ primary: "#FB7185",
197
+ background: "#FFF1F2",
198
+ text: "#881337",
199
+ subtext: "#9F1239",
200
+ border: "#FECDD3",
201
+ button: "#FB7185"
202
+ },
203
+ radius: 16,
204
+ fontFamily: JAKARTA,
205
+ swatches: ["#FB7185", "#881337", "#FECDD3"]
206
+ },
207
+ {
208
+ id: "ink",
209
+ name: "Ink",
210
+ description: "Deep navy editorial",
211
+ colors: {
212
+ primary: "#1E3A8A",
213
+ background: "#F8FAFC",
214
+ text: "#0F172A",
215
+ subtext: "#475569",
216
+ border: "#CBD5E1",
217
+ button: "#1E3A8A"
218
+ },
219
+ radius: 12,
220
+ fontFamily: JAKARTA,
221
+ swatches: ["#1E3A8A", "#0F172A", "#CBD5E1"]
222
+ },
223
+ {
224
+ id: "mint",
225
+ name: "Mint",
226
+ description: "Fresh teal vibe",
227
+ colors: {
228
+ primary: "#14B8A6",
229
+ background: "#F0FDFA",
230
+ text: "#134E4A",
231
+ subtext: "#0F766E",
232
+ border: "#99F6E4",
233
+ button: "#14B8A6"
234
+ },
235
+ radius: 16,
236
+ fontFamily: JAKARTA,
237
+ swatches: ["#14B8A6", "#134E4A", "#99F6E4"]
238
+ },
239
+ {
240
+ id: "slate",
241
+ name: "Slate",
242
+ description: "Quiet professional",
243
+ colors: {
244
+ primary: "#475569",
245
+ background: "#F1F5F9",
246
+ text: "#0F172A",
247
+ subtext: "#64748B",
248
+ border: "#CBD5E1",
249
+ button: "#475569"
250
+ },
251
+ radius: 12,
252
+ fontFamily: JAKARTA,
253
+ swatches: ["#475569", "#0F172A", "#CBD5E1"]
254
+ },
255
+ {
256
+ id: "aurora",
257
+ name: "Aurora",
258
+ description: "Vibrant gradient pop",
259
+ colors: {
260
+ primary: "#8B5CF6",
261
+ background: "#FAF5FF",
262
+ text: "#3B0764",
263
+ subtext: "#7E22CE",
264
+ border: "#E9D5FF",
265
+ accent: "#EC4899",
266
+ button: "#8B5CF6"
267
+ },
268
+ radius: 20,
269
+ fontFamily: JAKARTA,
270
+ swatches: ["#8B5CF6", "#EC4899", "#E9D5FF"]
271
+ }
272
+ ];
273
+ function getThemePresetById(id) {
274
+ return THEME_PRESETS.find((preset) => preset.id === id);
275
+ }
276
+ function defaultThemePreset() {
277
+ return THEME_PRESETS[0];
278
+ }
279
+
280
+ // src/types/brand.ts
281
+ function brandTokensFromPreset(preset) {
282
+ return {
283
+ colors: {
284
+ primary: preset.colors.primary,
285
+ background: preset.colors.background,
286
+ text: preset.colors.text,
287
+ subtext: preset.colors.subtext,
288
+ border: preset.colors.border
289
+ },
290
+ radius: preset.radius,
291
+ fontFamily: preset.fontFamily
292
+ };
293
+ }
294
+ function promptThemeFromBrandTokens(tokens) {
295
+ return {
296
+ colors: { ...tokens.colors },
297
+ radius: tokens.radius,
298
+ fontFamily: tokens.fontFamily
299
+ };
300
+ }
301
+ function inAppColorsFromBrandTokens(tokens) {
302
+ return {
303
+ backgroundColor: tokens.colors.background,
304
+ accentColor: tokens.colors.primary
305
+ };
306
+ }
307
+ function requestAccentFromBrandTokens(tokens) {
308
+ return tokens.colors.primary;
309
+ }
310
+
311
+ // src/types/integration.ts
312
+ var INTEGRATION_PROVIDERS = ["amplitude", "mixpanel"];
313
+ var INTEGRATION_CATEGORIES = [
314
+ "feedback",
315
+ "surveys",
316
+ "inapp",
317
+ "push",
318
+ "requests",
319
+ "lifecycle"
320
+ ];
321
+ var USER_IDENTIFIED_EVENT_NAME = "$user_identified";
322
+ function camelize(value) {
323
+ return value.replace(/_([a-z])/g, (_match, letter) => letter.toUpperCase());
324
+ }
325
+ var ids = (...keys) => Object.fromEntries(keys.map((key) => [key, [key, camelize(key)]]));
326
+ var pushEvents = [
327
+ ["$push_sent", "UserGist Push Sent"],
328
+ ["$push_delivered", "UserGist Push Delivered"],
329
+ ["$push_received", "UserGist Push Received"],
330
+ ["$push_displayed", "UserGist Push Displayed"],
331
+ ["$push_opened", "UserGist Push Opened"],
332
+ ["$push_dismissed", "UserGist Push Dismissed"],
333
+ ["$push_clicked", "UserGist Push Clicked"],
334
+ ["$push_action_clicked", "UserGist Push Action Clicked"],
335
+ ["$push_bounced", "UserGist Push Bounced"]
336
+ ];
337
+ var requestEvents = [
338
+ ["$request_submitted", "UserGist Request Submitted", "actor"],
339
+ ["$request_upvoted", "UserGist Request Upvoted", "actor"],
340
+ ["$request_unupvoted", "UserGist Request Unupvoted", "actor"],
341
+ ["$request_followed", "UserGist Request Followed", "actor"],
342
+ ["$request_unfollowed", "UserGist Request Unfollowed", "actor"],
343
+ ["$request_status_changed", "UserGist Request Status Changed", "affected_user"],
344
+ ["$request_responded", "UserGist Request Responded", "affected_user"],
345
+ ["$request_commented", "UserGist Request Commented", "actor"],
346
+ ["$request_comment_edited", "UserGist Request Comment Edited", "actor"],
347
+ ["$request_comment_deleted", "UserGist Request Comment Deleted", "affected_user"]
348
+ ];
349
+ var answerProperties = {
350
+ ...ids(
351
+ "question_id",
352
+ "question_type",
353
+ "question_title",
354
+ "answer_format",
355
+ "answer_present",
356
+ "answer_numeric_value",
357
+ "answer_selection_count",
358
+ "answer_text_length"
359
+ ),
360
+ // These aliases are generated only by the trusted server response paths.
361
+ // The API delivery boundary removes them from public SDK events before this
362
+ // normalizer runs. The complete answer_value stays in UserGist; destinations
363
+ // receive a value only for server-verified structured data.
364
+ answer_value: ["__usergist_destination_answer_value"],
365
+ answer_display_value: ["__usergist_destination_answer_display_value"]
366
+ };
367
+ var CORE_INTEGRATION_EVENTS = [
368
+ { key: "$app_open", name: "UserGist App Opened", category: "lifecycle", subjectRole: "actor", properties: {} },
369
+ { key: "$app_version_changed", name: "UserGist App Version Changed", category: "lifecycle", subjectRole: "actor", properties: { ...ids("old_version", "new_version") } },
370
+ { key: USER_IDENTIFIED_EVENT_NAME, name: "UserGist User Identified", category: "identity", subjectRole: "actor", required: true, properties: {} },
371
+ { key: "$feedback_prompt_shown", name: "UserGist Feedback Prompt Shown", category: "feedback", subjectRole: "actor", properties: { ...ids("prompt_id") } },
372
+ { 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") } },
373
+ { key: "$feedback_answer_submitted", name: "UserGist Feedback Answer Submitted", category: "feedback", subjectRole: "actor", properties: { ...ids("response_id", "prompt_id", "prompt_name"), ...answerProperties } },
374
+ { key: "$survey_started", name: "UserGist Survey Started", category: "surveys", subjectRole: "actor", properties: { ...ids("campaign_id", "survey_name", "attempt_id", "language", "source") } },
375
+ { 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 } },
376
+ { key: "$survey_completed", name: "UserGist Survey Completed", category: "surveys", subjectRole: "actor", properties: { ...ids("campaign_id", "survey_name", "attempt_id", "language", "source", "duration_ms") } },
377
+ { key: "$survey_abandoned", name: "UserGist Survey Abandoned", category: "surveys", subjectRole: "actor", properties: { ...ids("campaign_id", "survey_name", "attempt_id", "language", "source", "duration_ms") } },
378
+ { key: "$inapp_shown", name: "UserGist In-App Message Shown", category: "inapp", subjectRole: "actor", properties: { ...ids("message_id") } },
379
+ { key: "$inapp_dismissed", name: "UserGist In-App Message Dismissed", category: "inapp", subjectRole: "actor", properties: { ...ids("message_id", "dismiss_reason") } },
380
+ { key: "$inapp_auto_dismissed", name: "UserGist In-App Message Auto-Dismissed", category: "inapp", subjectRole: "actor", properties: { ...ids("message_id", "dismiss_reason") } },
381
+ { 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"] } },
382
+ ...pushEvents.map(([key, name]) => ({
383
+ key,
384
+ name,
385
+ category: "push",
386
+ subjectRole: "actor",
387
+ properties: {
388
+ ...ids("delivery_id", "campaign_id", "delivery_state", "failure_code"),
389
+ action_id: ["action_id", "actionId", "action_button", "actionButton"]
390
+ }
391
+ })),
392
+ ...requestEvents.map(([key, name, subjectRole]) => ({ key, 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"] } }))
393
+ ];
394
+ var CORE_EVENT_BY_KEY = new Map(CORE_INTEGRATION_EVENTS.map((event) => [event.key, event]));
395
+ function getCoreIntegrationEvent(key) {
396
+ return CORE_EVENT_BY_KEY.get(key) ?? null;
397
+ }
398
+ function sanitizeCoreIntegrationProperties(definition, input) {
399
+ if (!input) return {};
400
+ const output = {};
401
+ for (const [outputKey, aliases] of Object.entries(definition.properties)) {
402
+ const alias = aliases.find((candidate) => Object.prototype.hasOwnProperty.call(input, candidate));
403
+ if (!alias) continue;
404
+ const value = input[alias];
405
+ if (typeof value === "string") output[outputKey] = value.slice(0, 1024);
406
+ else if (typeof value === "number" && Number.isFinite(value)) output[outputKey] = value;
407
+ else if (typeof value === "boolean" || value === null) output[outputKey] = value;
408
+ }
409
+ return output;
410
+ }
411
+
412
+ // src/constants/push-events.ts
413
+ var PUSH_EVENTS = {
414
+ SENT: "$push_sent",
415
+ DELIVERED: "$push_delivered",
416
+ DISPLAYED: "$push_displayed",
417
+ RECEIVED: "$push_received",
418
+ OPENED: "$push_opened",
419
+ DISMISSED: "$push_dismissed",
420
+ CLICKED: "$push_clicked",
421
+ ACTION_CLICKED: "$push_action_clicked",
422
+ BOUNCED: "$push_bounced",
423
+ SILENT_ACK: "$push_silent_ack"
424
+ };
425
+
426
+ // src/constants/billing-plans.ts
427
+ var COMMERCIAL_BILLING_PLANS = [
428
+ {
429
+ id: "starter",
430
+ name: "Starter",
431
+ publicSlug: "starter",
432
+ description: "For small teams getting started with product feedback.",
433
+ priceKind: "fixed",
434
+ selfServe: true,
435
+ baseAmountCents: 1999,
436
+ currency: "USD",
437
+ includedMau: 1e3,
438
+ overagePer1kCents: 200,
439
+ appLimit: 1,
440
+ billingInterval: "month",
441
+ sortOrder: 10,
442
+ accessRank: 10,
443
+ catalogVersion: 1
444
+ },
445
+ {
446
+ id: "pro",
447
+ name: "Pro",
448
+ publicSlug: "pro",
449
+ description: "For growing products with multiple applications.",
450
+ priceKind: "fixed",
451
+ selfServe: true,
452
+ baseAmountCents: 4900,
453
+ currency: "USD",
454
+ includedMau: 25e3,
455
+ overagePer1kCents: 100,
456
+ appLimit: 3,
457
+ billingInterval: "month",
458
+ sortOrder: 20,
459
+ accessRank: 20,
460
+ catalogVersion: 1
461
+ },
462
+ {
463
+ id: "scale",
464
+ name: "Scale",
465
+ publicSlug: "scale",
466
+ description: "Contract pricing, usage, and application limits for larger organizations.",
467
+ priceKind: "custom",
468
+ selfServe: false,
469
+ baseAmountCents: 0,
470
+ currency: "USD",
471
+ includedMau: 0,
472
+ overagePer1kCents: 0,
473
+ appLimit: null,
474
+ billingInterval: "month",
475
+ sortOrder: 30,
476
+ accessRank: 30,
477
+ catalogVersion: 1
478
+ }
479
+ ];
480
+ var TRIAL_DAYS = 14;
481
+ var BILLING_GRACE_DAYS = 7;
482
+ var TRIAL_APP_LIMIT = 1;
483
+ var STARTER_FOUNDING_OFFER_CODE = "starter_founding_100";
484
+ var STARTER_FOUNDING_CUSTOMER_LIMIT = 100;
485
+
486
+ // src/types/push-runtime.ts
487
+ function isSilentPushPayload(raw) {
488
+ if (!raw) return false;
489
+ return raw.usergist_silent === "1" && typeof raw.usergist_ping_id === "string";
490
+ }
491
+
492
+ // src/evaluate/segment.ts
493
+ function evaluateSegment(dsl, user, now = Date.now()) {
494
+ return evaluateGroup(dsl.root, user, now);
495
+ }
496
+ function evaluateGroup(group, user, now) {
497
+ if (group.predicates.length === 0) return true;
498
+ if (group.combinator === "AND") {
499
+ return group.predicates.every((p) => evaluateNode(p, user, now));
500
+ }
501
+ return group.predicates.some((p) => evaluateNode(p, user, now));
502
+ }
503
+ function evaluateNode(node, user, now) {
504
+ if ("combinator" in node) return evaluateGroup(node, user, now);
505
+ switch (node.kind) {
506
+ case "user_property":
507
+ return evaluateUserProperty(node, user);
508
+ case "event_count":
509
+ return evaluateEventCount(node, user);
510
+ case "event_occurred":
511
+ return evaluateEventOccurred(node, user, now);
512
+ }
513
+ }
514
+ function evaluateUserProperty(p, user) {
515
+ const actual = user.properties[p.key];
516
+ switch (p.op) {
517
+ case "exists":
518
+ return actual !== void 0 && actual !== null;
519
+ case "not_exists":
520
+ return actual === void 0 || actual === null;
521
+ case "eq":
522
+ return actual === p.value;
523
+ case "neq":
524
+ return actual !== p.value;
525
+ case "in":
526
+ return Array.isArray(p.value) && actual != null && p.value.includes(actual);
527
+ case "nin":
528
+ return Array.isArray(p.value) && actual != null && !p.value.includes(actual);
529
+ case "gt":
530
+ return typeof actual === "number" && typeof p.value === "number" && actual > p.value;
531
+ case "gte":
532
+ return typeof actual === "number" && typeof p.value === "number" && actual >= p.value;
533
+ case "lt":
534
+ return typeof actual === "number" && typeof p.value === "number" && actual < p.value;
535
+ case "lte":
536
+ return typeof actual === "number" && typeof p.value === "number" && actual <= p.value;
537
+ case "contains":
538
+ return typeof actual === "string" && typeof p.value === "string" && actual.includes(p.value);
539
+ case "starts_with":
540
+ return typeof actual === "string" && typeof p.value === "string" && actual.startsWith(p.value);
541
+ }
542
+ }
543
+ function evaluateEventCount(p, user) {
544
+ const eventBucket = user.eventCounts[p.eventName];
545
+ const count = eventBucket?.[p.windowDays] ?? 0;
546
+ switch (p.op) {
547
+ case "eq":
548
+ return count === p.count;
549
+ case "gte":
550
+ return count >= p.count;
551
+ case "lte":
552
+ return count <= p.count;
553
+ case "gt":
554
+ return count > p.count;
555
+ case "lt":
556
+ return count < p.count;
557
+ }
558
+ }
559
+ function evaluateEventOccurred(p, user, now) {
560
+ const last = user.lastEventAt[p.eventName];
561
+ if (p.ever) {
562
+ if (!last) return false;
563
+ if (p.windowDays == null) return true;
564
+ const cutoff2 = now - p.windowDays * 864e5;
565
+ return Date.parse(last) >= cutoff2;
566
+ }
567
+ if (!last) return true;
568
+ if (p.windowDays == null) return false;
569
+ const cutoff = now - p.windowDays * 864e5;
570
+ return Date.parse(last) < cutoff;
571
+ }
572
+ function evaluateSerializedSegmentRules(rules, user, now = Date.now()) {
573
+ if (!rules) return true;
574
+ const dsl = serializedRulesToDsl(rules);
575
+ return evaluateSegment(dsl, user, now);
576
+ }
577
+ function serializedRulesToDsl(rules) {
578
+ const predicates = [];
579
+ for (const rule of rules.userProperties ?? []) {
580
+ predicates.push(userPropertyRuleToPredicate(rule));
581
+ }
582
+ for (const rule of rules.eventCounts ?? []) {
583
+ predicates.push({
584
+ kind: "event_count",
585
+ eventName: rule.eventName,
586
+ op: rule.op,
587
+ count: rule.count,
588
+ windowDays: rule.windowDays
589
+ });
590
+ }
591
+ return {
592
+ version: 1,
593
+ root: { combinator: "AND", predicates }
594
+ };
595
+ }
596
+ function userPropertyRuleToPredicate(rule) {
597
+ return {
598
+ kind: "user_property",
599
+ key: rule.key,
600
+ op: rule.op,
601
+ value: rule.value
602
+ };
603
+ }
604
+
605
+ // src/evaluate/branch.ts
606
+ function evaluateBranchCondition(cond, answer) {
607
+ if (cond.op === "answered") return answer !== void 0 && answer !== null && answer !== "";
608
+ if (cond.op === "unanswered") return answer === void 0 || answer === null || answer === "";
609
+ if (answer === void 0 || answer === null) return false;
610
+ switch (cond.op) {
611
+ case "eq":
612
+ return answer === cond.value;
613
+ case "neq":
614
+ return answer !== cond.value;
615
+ case "lt":
616
+ return typeof answer === "number" && typeof cond.value === "number" && answer < cond.value;
617
+ case "lte":
618
+ return typeof answer === "number" && typeof cond.value === "number" && answer <= cond.value;
619
+ case "gt":
620
+ return typeof answer === "number" && typeof cond.value === "number" && answer > cond.value;
621
+ case "gte":
622
+ return typeof answer === "number" && typeof cond.value === "number" && answer >= cond.value;
623
+ case "includes":
624
+ return Array.isArray(answer) && answer.includes(cond.value);
625
+ case "not_includes":
626
+ return Array.isArray(answer) && !answer.includes(cond.value);
627
+ default:
628
+ return false;
629
+ }
630
+ }
631
+ function nextQuestionId(flow, currentQuestionId, answers) {
632
+ const branches = flow.branches.filter((b) => b.fromQuestionId === currentQuestionId);
633
+ const answer = answers[currentQuestionId];
634
+ for (const branch of branches) {
635
+ if (evaluateBranchCondition(branch.condition, answer)) {
636
+ if (branch.toQuestionId === SURVEY_END_SENTINEL) return null;
637
+ return branch.toQuestionId;
638
+ }
639
+ }
640
+ return defaultNext(flow, currentQuestionId);
641
+ }
642
+ function defaultNext(flow, currentQuestionId) {
643
+ const idx = flow.questions.findIndex((q) => q.id === currentQuestionId);
644
+ if (idx < 0 || idx >= flow.questions.length - 1) return null;
645
+ const next = flow.questions[idx + 1];
646
+ return next ? next.id : null;
647
+ }
648
+ function findQuestion(flow, questionId) {
649
+ return flow.questions.find((q) => q.id === questionId);
650
+ }
651
+ function findQuestionIndex(flow, questionId) {
652
+ return flow.questions.findIndex((q) => q.id === questionId);
653
+ }
654
+ function estimateProgress(flow, currentQuestionId) {
655
+ if (!currentQuestionId) return 1;
656
+ const idx = findQuestionIndex(flow, currentQuestionId);
657
+ if (idx < 0) return 0;
658
+ return (idx + 1) / Math.max(1, flow.questions.length);
659
+ }
660
+ function reachableQuestions(flow, startId) {
661
+ const start = startId ?? flow.startQuestionId;
662
+ const visited = /* @__PURE__ */ new Set();
663
+ const stack = [start];
664
+ while (stack.length > 0) {
665
+ const id = stack.pop();
666
+ if (id === void 0) continue;
667
+ if (visited.has(id)) continue;
668
+ visited.add(id);
669
+ const outgoing = flow.branches.filter((b) => b.fromQuestionId === id);
670
+ for (const b of outgoing) {
671
+ if (b.toQuestionId !== SURVEY_END_SENTINEL) stack.push(b.toQuestionId);
672
+ }
673
+ const idx = flow.questions.findIndex((q) => q.id === id);
674
+ if (idx >= 0 && idx < flow.questions.length - 1) {
675
+ const next = flow.questions[idx + 1];
676
+ if (next) stack.push(next.id);
677
+ }
678
+ }
679
+ return visited;
680
+ }
681
+
682
+ // src/evaluate/periodic.ts
683
+ function nextPeriodicFire(schedule, now = /* @__PURE__ */ new Date()) {
684
+ const tz = schedule.tz || "UTC";
685
+ const start = startCandidate(schedule, now, tz);
686
+ return walkForward(start, schedule, now, tz);
687
+ }
688
+ function tzOffsetMinutes(at, tz) {
689
+ if (tz === "UTC") return 0;
690
+ try {
691
+ const fmt = getFormatter(tz);
692
+ const parts = fmt.formatToParts(at).reduce((acc, p) => {
693
+ if (p.type !== "literal") acc[p.type] = p.value;
694
+ return acc;
695
+ }, {});
696
+ const utc = Date.UTC(
697
+ Number(parts.year),
698
+ Number(parts.month) - 1,
699
+ Number(parts.day),
700
+ Number(parts.hour) === 24 ? 0 : Number(parts.hour),
701
+ Number(parts.minute),
702
+ Number(parts.second)
703
+ );
704
+ return Math.round((utc - at.getTime()) / 6e4);
705
+ } catch {
706
+ return 0;
707
+ }
708
+ }
709
+ function startCandidate(schedule, now, tz) {
710
+ const localNow = wallClock(now, tz);
711
+ const target = new Date(localNow);
712
+ target.setHours(schedule.hourLocal, schedule.minuteLocal, 0, 0);
713
+ return localToUtc(target, tz);
714
+ }
715
+ function walkForward(candidate, schedule, now, tz) {
716
+ let cursor = candidate;
717
+ const max = schedule.frequency === "daily" ? 2 : schedule.frequency === "weekly" ? 8 : 13;
718
+ for (let i = 0; i < max; i++) {
719
+ const local = wallClock(cursor, tz);
720
+ if (matchesFrequency(local, schedule) && cursor.getTime() > now.getTime()) {
721
+ return cursor;
722
+ }
723
+ cursor = step(cursor, local, schedule, tz);
724
+ }
725
+ return cursor;
726
+ }
727
+ function matchesFrequency(local, schedule) {
728
+ if (schedule.frequency === "daily") return true;
729
+ if (schedule.frequency === "weekly") {
730
+ return schedule.weekday === void 0 || local.getDay() === schedule.weekday;
731
+ }
732
+ return schedule.dayOfMonth === void 0 || local.getDate() === schedule.dayOfMonth;
733
+ }
734
+ function step(cursor, cursorLocal, schedule, tz) {
735
+ if (schedule.frequency === "daily" || schedule.frequency === "weekly") {
736
+ return addDays(cursor, 1);
737
+ }
738
+ const next = new Date(cursorLocal);
739
+ next.setMonth(next.getMonth() + 1);
740
+ if (schedule.dayOfMonth !== void 0) {
741
+ const lastDay = lastDayOfMonth(next.getFullYear(), next.getMonth());
742
+ next.setDate(Math.min(schedule.dayOfMonth, lastDay));
743
+ }
744
+ next.setHours(schedule.hourLocal, schedule.minuteLocal, 0, 0);
745
+ return localToUtc(next, tz);
746
+ }
747
+ function addDays(d, days) {
748
+ const next = new Date(d.getTime());
749
+ next.setDate(next.getDate() + days);
750
+ return next;
751
+ }
752
+ function lastDayOfMonth(year, monthIndex) {
753
+ return new Date(year, monthIndex + 1, 0).getDate();
754
+ }
755
+ function wallClock(d, tz) {
756
+ const offsetMin = tzOffsetMinutes(d, tz);
757
+ return new Date(d.getTime() + offsetMin * 6e4);
758
+ }
759
+ function localToUtc(localView, tz) {
760
+ const offsetMin = tzOffsetMinutes(localView, tz);
761
+ return new Date(localView.getTime() - offsetMin * 6e4);
762
+ }
763
+ var FORMATTER_CACHE = /* @__PURE__ */ new Map();
764
+ function getFormatter(tz) {
765
+ const cached = FORMATTER_CACHE.get(tz);
766
+ if (cached) return cached;
767
+ const fmt = new Intl.DateTimeFormat("en-US", {
768
+ timeZone: tz,
769
+ hour12: false,
770
+ year: "numeric",
771
+ month: "2-digit",
772
+ day: "2-digit",
773
+ hour: "2-digit",
774
+ minute: "2-digit",
775
+ second: "2-digit"
776
+ });
777
+ FORMATTER_CACHE.set(tz, fmt);
778
+ return fmt;
779
+ }
780
+
781
+ // src/contract/endpoints.ts
782
+ var endpoints = {
783
+ "GET /v1/me": {},
784
+ "GET /v1/workspaces": {},
785
+ "POST /v1/workspaces": {},
786
+ "GET /v1/workspaces/:wid/members": {},
787
+ "GET /v1/workspaces/:wid/invites": {},
788
+ "POST /v1/workspaces/:wid/invites": {},
789
+ "DELETE /v1/workspaces/:wid/invites/:inviteId": {},
790
+ "POST /v1/invites/accept": {},
791
+ "GET /v1/workspaces/:wid/apps": {},
792
+ "POST /v1/workspaces/:wid/apps": {},
793
+ "GET /v1/workspaces/:wid/api-tokens": {},
794
+ "POST /v1/workspaces/:wid/api-tokens": {},
795
+ "DELETE /v1/workspaces/:wid/api-tokens/:tokenId": {},
796
+ "GET /v1/apps/:appId": {},
797
+ "PATCH /v1/apps/:appId": {},
798
+ "DELETE /v1/apps/:appId": {},
799
+ "POST /v1/apps/:appId/sdk/subject-tokens": {},
800
+ // ---------- outbound analytics integrations ----------
801
+ "GET /v1/apps/:appId/integrations": {},
802
+ "PUT /v1/apps/:appId/integrations/:provider": {},
803
+ "PATCH /v1/apps/:appId/integrations/:provider": {},
804
+ "POST /v1/apps/:appId/integrations/:provider/test": {},
805
+ "POST /v1/apps/:appId/integrations/:provider/pause": {},
806
+ "POST /v1/apps/:appId/integrations/:provider/resume": {},
807
+ "DELETE /v1/apps/:appId/integrations/:provider": {},
808
+ "GET /v1/apps/:appId/integrations/:provider/deliveries": {},
809
+ "GET /v1/apps/:appId/brand-settings": {},
810
+ "POST /v1/apps/:appId/brand-themes": {},
811
+ "PATCH /v1/apps/:appId/brand-themes/:themeId": {},
812
+ "DELETE /v1/apps/:appId/brand-themes/:themeId": {},
813
+ "PUT /v1/apps/:appId/brand-theme-defaults": {},
814
+ "GET /v1/apps/:appId/write-keys": {},
815
+ "POST /v1/apps/:appId/write-keys": {},
816
+ "POST /v1/apps/:appId/write-keys/:keyId/rotate": {},
817
+ "DELETE /v1/apps/:appId/write-keys/:keyId": {},
818
+ "GET /v1/apps/:appId/event-definitions": {},
819
+ "POST /v1/apps/:appId/event-definitions": {},
820
+ "PATCH /v1/apps/:appId/event-definitions/:defId": {},
821
+ "GET /v1/apps/:appId/segments": {},
822
+ "POST /v1/apps/:appId/segments": {},
823
+ "GET /v1/apps/:appId/segments/:segId": {},
824
+ "PATCH /v1/apps/:appId/segments/:segId": {},
825
+ "DELETE /v1/apps/:appId/segments/:segId": {},
826
+ "POST /v1/apps/:appId/segments/:segId/preview": {},
827
+ "POST /v1/apps/:appId/segments/:segId/rebuild": {},
828
+ "GET /v1/apps/:appId/users": {},
829
+ "GET /v1/apps/:appId/users/:anonymousId": {},
830
+ "GET /v1/apps/:appId/users/:anonymousId/events": {},
831
+ "GET /v1/apps/:appId/users/:anonymousId/push": {},
832
+ "POST /v1/apps/:appId/users/:anonymousId/push/test-send": {},
833
+ "POST /v1/apps/:appId/segments/ai-generate-rules": {},
834
+ "POST /v1/apps/:appId/segments/ai-generate-name": {},
835
+ "GET /v1/apps/:appId/prompts": {},
836
+ "POST /v1/apps/:appId/prompts": {},
837
+ "GET /v1/apps/:appId/prompts/:promptId": {},
838
+ "PATCH /v1/apps/:appId/prompts/:promptId": {},
839
+ "DELETE /v1/apps/:appId/prompts/:promptId": {},
840
+ "POST /v1/apps/:appId/prompts/:promptId/test-on-device": {},
841
+ "GET /v1/apps/:appId/prompts/:promptId/responses": {},
842
+ "GET /v1/apps/:appId/prompts/:promptId/recipients": {},
843
+ "GET /v1/apps/:appId/prompts/:promptId/analytics": {},
844
+ // App-level analytics rollup — daily (or hourly for 24h) buckets across all
845
+ // four pillars. Used by the dashboard overview to render "last N days"
846
+ // performance charts without spamming per-resource analytics endpoints.
847
+ "GET /v1/apps/:appId/analytics/overview": {},
848
+ // Inline AudienceSpec preview — count of users matching the (unsaved) spec.
849
+ // Used by the dashboard's TARGET step to power the live count badge.
850
+ "POST /v1/apps/:appId/audience/preview": {},
851
+ // SDK-facing (authenticated via write key)
852
+ "POST /v1/sdk/ingest": {},
853
+ "GET /v1/sdk/armed-triggers": {},
854
+ "POST /v1/sdk/consent": {},
855
+ "POST /v1/sdk/identify": {},
856
+ "POST /v1/sdk/responses": {},
857
+ // GDPR
858
+ "POST /v1/apps/:appId/gdpr/delete": {},
859
+ "POST /v1/apps/:appId/gdpr/export": {},
860
+ "GET /v1/apps/:appId/gdpr/exports/:exportId": {},
861
+ // Campaigns (push)
862
+ "GET /v1/apps/:appId/campaigns": {},
863
+ "POST /v1/apps/:appId/campaigns": {},
864
+ "GET /v1/apps/:appId/campaigns/:cid": {},
865
+ "PATCH /v1/apps/:appId/campaigns/:cid": {},
866
+ "DELETE /v1/apps/:appId/campaigns/:cid": {},
867
+ "POST /v1/apps/:appId/campaigns/:cid/activate": {},
868
+ "POST /v1/apps/:appId/campaigns/:cid/pause": {},
869
+ "POST /v1/apps/:appId/campaigns/:cid/resend": {},
870
+ "POST /v1/apps/:appId/campaigns/:cid/preview": {},
871
+ "POST /v1/apps/:appId/campaigns/:cid/test-send": {},
872
+ "GET /v1/apps/:appId/campaigns/:cid/analytics": {},
873
+ "GET /v1/apps/:appId/campaigns/:cid/deliveries": {},
874
+ // Push credentials
875
+ "GET /v1/apps/:appId/push/credentials": {},
876
+ "POST /v1/apps/:appId/push/credentials": {},
877
+ "DELETE /v1/apps/:appId/push/credentials/:credId": {},
878
+ "GET /v1/apps/:appId/push/stats": {},
879
+ "GET /v1/apps/:appId/push/audience-estimate": {},
880
+ "GET /v1/apps/:appId/push/reachability": {},
881
+ // Push channels (per-app NotificationChannel registry / iOS category id)
882
+ "GET /v1/apps/:appId/push/channels": {},
883
+ "PUT /v1/apps/:appId/push/channels": {},
884
+ "DELETE /v1/apps/:appId/push/channels/:channelId": {},
885
+ // Push outbound webhooks
886
+ "GET /v1/apps/:appId/push/webhooks": {},
887
+ "POST /v1/apps/:appId/push/webhooks": {},
888
+ "PATCH /v1/apps/:appId/push/webhooks/:webhookId": {},
889
+ "DELETE /v1/apps/:appId/push/webhooks/:webhookId": {},
890
+ "POST /v1/apps/:appId/push/webhooks/:webhookId/rotate": {},
891
+ "POST /v1/apps/:appId/push/webhooks/:webhookId/test": {},
892
+ // Push device-token registration (SDK-facing, write-key auth)
893
+ "POST /v1/sdk/push/register-token": {},
894
+ "POST /v1/sdk/push/update-token": {},
895
+ "POST /v1/sdk/push/invalidate-token": {},
896
+ "POST /v1/sdk/push/rebind": {},
897
+ "POST /v1/sdk/push/app-open": {},
898
+ "POST /v1/sdk/push/delivered": {},
899
+ "POST /v1/sdk/push/displayed": {},
900
+ "POST /v1/sdk/push/dismissed": {},
901
+ "POST /v1/sdk/push/silent-ack": {},
902
+ "GET /v1/sdk/push/channels": {},
903
+ "POST /v1/sdk/push/channels/subscription": {},
904
+ // Transactional push (server API token auth)
905
+ "POST /v1/apps/:appId/push/transactional": {},
906
+ // Surveys — dashboard CRUD
907
+ "GET /v1/apps/:appId/surveys": {},
908
+ "POST /v1/apps/:appId/surveys": {},
909
+ "GET /v1/apps/:appId/surveys/:sid": {},
910
+ "PATCH /v1/apps/:appId/surveys/:sid": {},
911
+ "DELETE /v1/apps/:appId/surveys/:sid": {},
912
+ "POST /v1/apps/:appId/surveys/:sid/activate": {},
913
+ "POST /v1/apps/:appId/surveys/:sid/pause": {},
914
+ "POST /v1/apps/:appId/surveys/:sid/archive": {},
915
+ "POST /v1/apps/:appId/surveys/:sid/preview": {},
916
+ "POST /v1/apps/:appId/surveys/:sid/test-on-device": {},
917
+ "GET /v1/apps/:appId/surveys/:sid/analytics": {},
918
+ "GET /v1/apps/:appId/surveys/:sid/responses": {},
919
+ "GET /v1/apps/:appId/surveys/:sid/recipients": {},
920
+ "GET /v1/apps/:appId/surveys/:sid/attempts": {},
921
+ "GET /v1/apps/:appId/surveys/:sid/share-link": {},
922
+ "GET /v1/apps/:appId/survey-templates": {},
923
+ "POST /v1/apps/:appId/surveys/from-template/:slug": {},
924
+ // Surveys — SDK-facing (write-key auth)
925
+ "GET /v1/sdk/surveys/available": {},
926
+ "POST /v1/sdk/surveys/resolve-link": {},
927
+ "GET /v1/sdk/surveys/:sid": {},
928
+ "POST /v1/sdk/surveys/:sid/attempts": {},
929
+ "PATCH /v1/sdk/surveys/attempts/:attemptId": {},
930
+ "POST /v1/sdk/surveys/attempts/:attemptId/responses": {},
931
+ "POST /v1/sdk/surveys/attempts/:attemptId/complete": {},
932
+ "POST /v1/sdk/surveys/attempts/:attemptId/abandon": {},
933
+ // ---------- in-app messages (4th pillar) ----------
934
+ "GET /v1/apps/:appId/inapp-messages": {},
935
+ "POST /v1/apps/:appId/inapp-messages": {},
936
+ "GET /v1/apps/:appId/inapp-messages/:id": {},
937
+ "PATCH /v1/apps/:appId/inapp-messages/:id": {},
938
+ "DELETE /v1/apps/:appId/inapp-messages/:id": {},
939
+ "POST /v1/apps/:appId/inapp-messages/:id/activate": {},
940
+ "POST /v1/apps/:appId/inapp-messages/:id/pause": {},
941
+ "GET /v1/apps/:appId/inapp-messages/:id/analytics": {},
942
+ "GET /v1/apps/:appId/inapp-messages/:id/recipients": {},
943
+ "GET /v1/sdk/armed-inapp-messages": {},
944
+ // ---------- feature requests (5th pillar) — dashboard ----------
945
+ "GET /v1/apps/:appId/requests": {},
946
+ "GET /v1/apps/:appId/requests/:requestId": {},
947
+ "PATCH /v1/apps/:appId/requests/:requestId/status": {},
948
+ "PUT /v1/apps/:appId/requests/:requestId/response": {},
949
+ "PATCH /v1/apps/:appId/requests/:requestId/moderation": {},
950
+ "POST /v1/apps/:appId/requests/merge": {},
951
+ "POST /v1/apps/:appId/requests/bulk/status": {},
952
+ "GET /v1/apps/:appId/requests/:requestId/upvoter-segments": {},
953
+ "GET /v1/apps/:appId/requests/:requestId/timeline": {},
954
+ "GET /v1/apps/:appId/requests/:requestId/analytics": {},
955
+ "GET /v1/apps/:appId/requests/:requestId/audience-spec": {},
956
+ "GET /v1/apps/:appId/request-settings": {},
957
+ "PUT /v1/apps/:appId/request-settings": {},
958
+ "GET /v1/apps/:appId/request-settings/slug-available": {},
959
+ "POST /v1/apps/:appId/requests/seed-segments": {},
960
+ // ---------- feature requests — SDK (write-key) ----------
961
+ "GET /v1/sdk/requests": {},
962
+ "GET /v1/sdk/request-branding": {},
963
+ "GET /v1/sdk/requests/search": {},
964
+ "GET /v1/sdk/requests/:requestId": {},
965
+ "POST /v1/sdk/requests": {},
966
+ "POST /v1/sdk/requests/:requestId/vote": {},
967
+ "POST /v1/sdk/requests/:requestId/follow": {},
968
+ // ---------- feature requests — comments ----------
969
+ // Dashboard (cookie auth)
970
+ "GET /v1/apps/:appId/requests/:requestId/comments": {},
971
+ "DELETE /v1/apps/:appId/requests/:requestId/comments/:commentId": {},
972
+ // SDK (write-key)
973
+ "GET /v1/sdk/requests/:requestId/comments": {},
974
+ "POST /v1/sdk/requests/:requestId/comments": {},
975
+ "PATCH /v1/sdk/requests/:requestId/comments/:commentId": {},
976
+ "DELETE /v1/sdk/requests/:requestId/comments/:commentId": {},
977
+ // ---------- billing (Paddle) ----------
978
+ "GET /v1/workspaces/:wid/billing/plans": {},
979
+ "GET /v1/workspaces/:wid/billing/subscription": {},
980
+ "POST /v1/workspaces/:wid/billing/checkout": {},
981
+ "POST /v1/workspaces/:wid/billing/portal": {},
982
+ "POST /v1/workspaces/:wid/billing/change-plan": {},
983
+ "GET /v1/workspaces/:wid/billing/periods": {},
984
+ // ---------- super admin ----------
985
+ "GET /v1/admin/session": {},
986
+ "GET /v1/admin/customers": {},
987
+ "GET /v1/admin/customers/:workspaceId": {},
988
+ "POST /v1/admin/customers/:workspaceId/billing-events/:eventId/replay": {},
989
+ "POST /v1/admin/customers/:workspaceId/grants": {},
990
+ "POST /v1/admin/customers/:workspaceId/grants/:grantId/extend": {},
991
+ "POST /v1/admin/customers/:workspaceId/grants/:grantId/revoke": {},
992
+ "GET /v1/admin/activity": {},
993
+ "GET /v1/admin/workspaces": {},
994
+ "GET /v1/admin/plans": {},
995
+ "GET /v1/admin/workspaces/:wid": {},
996
+ "POST /v1/admin/workspaces/:wid/plan": {},
997
+ // ---------- feature requests — public web roadmap (no auth) ----------
998
+ "GET /v1/public/roadmap/:slug": {},
999
+ "GET /v1/public/roadmap/:slug/r/:requestId": {},
1000
+ "GET /v1/public/roadmap/:slug/branding": {}
1001
+ };
1002
+
1003
+ // src/schemas/index.ts
1004
+ var schemas_exports = {};
1005
+ __export(schemas_exports, {
1006
+ acceptWorkspaceInviteSchema: () => acceptWorkspaceInviteSchema,
1007
+ apiTokenScopeSchema: () => apiTokenScopeSchema,
1008
+ createApiTokenSchema: () => createApiTokenSchema,
1009
+ createAppSchema: () => createAppSchema,
1010
+ createWorkspaceSchema: () => createWorkspaceSchema,
1011
+ createWriteKeySchema: () => createWriteKeySchema,
1012
+ emailSchema: () => emailSchema,
1013
+ inviteMemberSchema: () => inviteMemberSchema,
1014
+ isoDateTimeSchema: () => isoDateTimeSchema,
1015
+ loginMagicLinkConsumeRequestSchema: () => loginMagicLinkConsumeRequestSchema,
1016
+ loginMagicLinkRequestSchema: () => loginMagicLinkRequestSchema,
1017
+ loginPasswordRequestSchema: () => loginPasswordRequestSchema,
1018
+ passwordSchema: () => passwordSchema,
1019
+ platformSchema: () => platformSchema,
1020
+ rotateWriteKeySchema: () => rotateWriteKeySchema,
1021
+ signupRequestSchema: () => signupRequestSchema,
1022
+ slugSchema: () => slugSchema,
1023
+ updateAppSchema: () => updateAppSchema,
1024
+ uuidSchema: () => uuidSchema,
1025
+ workspaceRoleSchema: () => workspaceRoleSchema,
1026
+ writeKeyEnvironmentSchema: () => writeKeyEnvironmentSchema
1027
+ });
1028
+ var slugSchema = zod.z.string().min(1).max(64).regex(/^[a-z0-9-]+$/, "Must be lowercase, digits, and dashes only");
1029
+ var emailSchema = zod.z.string().email().max(320);
1030
+ var uuidSchema = zod.z.string().uuid();
1031
+ var isoDateTimeSchema = zod.z.string().datetime({ offset: true });
1032
+ var workspaceRoleSchema = zod.z.enum(["owner", "admin", "editor", "viewer"]);
1033
+
1034
+ // src/schemas/auth.ts
1035
+ var passwordSchema = zod.z.string().min(8).max(256);
1036
+ var signupRequestSchema = zod.z.object({
1037
+ email: emailSchema,
1038
+ password: passwordSchema,
1039
+ name: zod.z.string().min(1).max(120).optional(),
1040
+ workspaceName: zod.z.string().min(1).max(120)
1041
+ });
1042
+ var loginPasswordRequestSchema = zod.z.object({
1043
+ email: emailSchema,
1044
+ password: zod.z.string().min(1).max(256)
1045
+ });
1046
+ var loginMagicLinkRequestSchema = zod.z.object({
1047
+ email: emailSchema
1048
+ });
1049
+ var loginMagicLinkConsumeRequestSchema = zod.z.object({
1050
+ token: zod.z.string().min(16).max(512)
1051
+ });
1052
+ var platformSchema = zod.z.enum(["ios", "android", "react-native", "flutter"]);
1053
+ var writeKeyEnvironmentSchema = zod.z.enum(["production", "staging", "development"]);
1054
+ var apiTokenScopeSchema = zod.z.enum(["sdk:subjects", "push.transactional"]);
1055
+ var createAppSchema = zod.z.object({
1056
+ name: zod.z.string().min(1).max(120),
1057
+ slug: slugSchema.optional(),
1058
+ platforms: zod.z.array(platformSchema).min(1).max(8),
1059
+ environment: writeKeyEnvironmentSchema.default("production")
1060
+ });
1061
+ var updateAppSchema = zod.z.object({
1062
+ name: zod.z.string().min(1).max(120).optional(),
1063
+ platforms: zod.z.array(platformSchema).min(1).max(8).optional(),
1064
+ piiAllowList: zod.z.array(zod.z.string().min(1).max(120)).max(128).optional(),
1065
+ lifecycleEventsEnabled: zod.z.boolean().optional()
1066
+ }).refine((v) => Object.keys(v).length > 0, { message: "At least one field is required" });
1067
+ var createWriteKeySchema = zod.z.object({
1068
+ label: zod.z.string().min(1).max(120).optional(),
1069
+ environment: writeKeyEnvironmentSchema.default("production")
1070
+ });
1071
+ var rotateWriteKeySchema = zod.z.object({
1072
+ // Default 5 minutes; ceiling 24h to bound risk of forgotten old keys.
1073
+ graceSeconds: zod.z.number().int().min(0).max(86400).default(300)
1074
+ });
1075
+ var createApiTokenSchema = zod.z.object({
1076
+ name: zod.z.string().trim().min(1).max(120),
1077
+ scopes: zod.z.array(apiTokenScopeSchema).min(1).max(8).transform((scopes) => [...new Set(scopes)]),
1078
+ expiresInDays: zod.z.number().int().min(1).max(365).default(90)
1079
+ });
1080
+ var createWorkspaceSchema = zod.z.object({
1081
+ name: zod.z.string().min(1).max(120),
1082
+ slug: slugSchema.optional()
1083
+ });
1084
+ var inviteMemberSchema = zod.z.object({
1085
+ email: emailSchema,
1086
+ role: zod.z.enum(["admin", "editor", "viewer"])
1087
+ });
1088
+ var acceptWorkspaceInviteSchema = zod.z.object({
1089
+ token: zod.z.string().regex(/^[a-f0-9]{64}$/i, "Invalid invitation token")
1090
+ });
1091
+
1092
+ exports.APP_OPEN_EVENT_NAME = APP_OPEN_EVENT_NAME;
1093
+ exports.APP_VERSION_CHANGED_EVENT_NAME = APP_VERSION_CHANGED_EVENT_NAME;
1094
+ exports.AUDIENCE_JOIN_EVENT_NAME = AUDIENCE_JOIN_EVENT_NAME;
1095
+ exports.AUDIENCE_JOIN_TRIGGER_KIND = AUDIENCE_JOIN_TRIGGER_KIND;
1096
+ exports.BILLING_GRACE_DAYS = BILLING_GRACE_DAYS;
1097
+ exports.COMMERCIAL_BILLING_PLANS = COMMERCIAL_BILLING_PLANS;
1098
+ exports.CORE_INTEGRATION_EVENTS = CORE_INTEGRATION_EVENTS;
1099
+ exports.INAPP_AUTO_DISMISSED_EVENT_NAME = INAPP_AUTO_DISMISSED_EVENT_NAME;
1100
+ exports.INAPP_CTA_CLICKED_EVENT_NAME = INAPP_CTA_CLICKED_EVENT_NAME;
1101
+ exports.INAPP_DISMISSED_EVENT_NAME = INAPP_DISMISSED_EVENT_NAME;
1102
+ exports.INAPP_SHOWN_EVENT_NAME = INAPP_SHOWN_EVENT_NAME;
1103
+ exports.INTEGRATION_CATEGORIES = INTEGRATION_CATEGORIES;
1104
+ exports.INTEGRATION_PROVIDERS = INTEGRATION_PROVIDERS;
1105
+ exports.PUSH_EVENTS = PUSH_EVENTS;
1106
+ exports.RADIUS_PRESETS = RADIUS_PRESETS;
1107
+ exports.RECIPIENTS_LIMIT = RECIPIENTS_LIMIT;
1108
+ exports.REQUEST_COMMENTED_EVENT_NAME = REQUEST_COMMENTED_EVENT_NAME;
1109
+ exports.REQUEST_COMMENT_DELETED_EVENT_NAME = REQUEST_COMMENT_DELETED_EVENT_NAME;
1110
+ exports.REQUEST_COMMENT_EDITED_EVENT_NAME = REQUEST_COMMENT_EDITED_EVENT_NAME;
1111
+ exports.REQUEST_FOLLOWED_EVENT_NAME = REQUEST_FOLLOWED_EVENT_NAME;
1112
+ exports.REQUEST_RESPONDED_EVENT_NAME = REQUEST_RESPONDED_EVENT_NAME;
1113
+ exports.REQUEST_STATUS_CHANGED_EVENT_NAME = REQUEST_STATUS_CHANGED_EVENT_NAME;
1114
+ exports.REQUEST_SUBMITTED_EVENT_NAME = REQUEST_SUBMITTED_EVENT_NAME;
1115
+ exports.REQUEST_UNFOLLOWED_EVENT_NAME = REQUEST_UNFOLLOWED_EVENT_NAME;
1116
+ exports.REQUEST_UNUPVOTED_EVENT_NAME = REQUEST_UNUPVOTED_EVENT_NAME;
1117
+ exports.REQUEST_UPVOTED_EVENT_NAME = REQUEST_UPVOTED_EVENT_NAME;
1118
+ exports.STARTER_FOUNDING_CUSTOMER_LIMIT = STARTER_FOUNDING_CUSTOMER_LIMIT;
1119
+ exports.STARTER_FOUNDING_OFFER_CODE = STARTER_FOUNDING_OFFER_CODE;
1120
+ exports.SURVEY_END_SENTINEL = SURVEY_END_SENTINEL;
1121
+ exports.THEME_PRESETS = THEME_PRESETS;
1122
+ exports.TRIAL_APP_LIMIT = TRIAL_APP_LIMIT;
1123
+ exports.TRIAL_DAYS = TRIAL_DAYS;
1124
+ exports.USER_IDENTIFIED_EVENT_NAME = USER_IDENTIFIED_EVENT_NAME;
1125
+ exports.brandTokensFromPreset = brandTokensFromPreset;
1126
+ exports.defaultEventTrigger = defaultEventTrigger;
1127
+ exports.defaultThemePreset = defaultThemePreset;
1128
+ exports.defaultTriggerSpec = defaultTriggerSpec;
1129
+ exports.emptyAudienceSpec = emptyAudienceSpec;
1130
+ exports.emptySegmentDsl = emptySegmentDsl;
1131
+ exports.endpoints = endpoints;
1132
+ exports.err = err;
1133
+ exports.estimateProgress = estimateProgress;
1134
+ exports.evaluateBranchCondition = evaluateBranchCondition;
1135
+ exports.evaluateSegment = evaluateSegment;
1136
+ exports.evaluateSerializedSegmentRules = evaluateSerializedSegmentRules;
1137
+ exports.findQuestion = findQuestion;
1138
+ exports.findQuestionIndex = findQuestionIndex;
1139
+ exports.getCoreIntegrationEvent = getCoreIntegrationEvent;
1140
+ exports.getThemePresetById = getThemePresetById;
1141
+ exports.inAppColorsFromBrandTokens = inAppColorsFromBrandTokens;
1142
+ exports.isSilentPushPayload = isSilentPushPayload;
1143
+ exports.nextPeriodicFire = nextPeriodicFire;
1144
+ exports.nextQuestionId = nextQuestionId;
1145
+ exports.ok = ok;
1146
+ exports.promptThemeFromBrandTokens = promptThemeFromBrandTokens;
1147
+ exports.reachableQuestions = reachableQuestions;
1148
+ exports.requestAccentFromBrandTokens = requestAccentFromBrandTokens;
1149
+ exports.sanitizeCoreIntegrationProperties = sanitizeCoreIntegrationProperties;
1150
+ exports.schemas = schemas_exports;
1151
+ exports.serializedRulesToDsl = serializedRulesToDsl;
1152
+ exports.tzOffsetMinutes = tzOffsetMinutes;
1153
+ //# sourceMappingURL=index.cjs.map
1154
+ //# sourceMappingURL=index.cjs.map