@guidelytech/shared 0.0.0-stage → 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,1001 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ADVANCE_ON: () => ADVANCE_ON,
24
+ AppearanceSchema: () => AppearanceSchema,
25
+ AuthoringPickSchema: () => AuthoringPickSchema,
26
+ BUTTON_ACTIONS: () => BUTTON_ACTIONS,
27
+ ButtonSchema: () => ButtonSchema,
28
+ ChecklistConfigSchema: () => ChecklistConfigSchema,
29
+ ChecklistProgressRequestSchema: () => ChecklistProgressRequestSchema,
30
+ ChecklistTaskSchema: () => ChecklistTaskSchema,
31
+ EVENT_NAMES: () => EVENT_NAMES,
32
+ EventBatchSchema: () => EventBatchSchema,
33
+ FrequencySchema: () => FrequencySchema,
34
+ GUIDE_KINDS: () => GUIDE_KINDS,
35
+ GUIDE_LIFECYCLE_EVENTS: () => GUIDE_LIFECYCLE_EVENTS,
36
+ GUIDE_STATUSES: () => GUIDE_STATUSES,
37
+ GUIDE_TEMPLATES: () => GUIDE_TEMPLATES,
38
+ GuideConfigSchema: () => GuideConfigSchema,
39
+ GuideStateUpdateSchema: () => GuideStateUpdateSchema,
40
+ IdentifyRequestSchema: () => IdentifyRequestSchema,
41
+ MAX_PROPERTY_BYTES: () => MAX_PROPERTY_BYTES,
42
+ PLACEMENTS: () => PLACEMENTS,
43
+ ProjectThemeSchema: () => ProjectThemeSchema,
44
+ PublishedGuideSchema: () => PublishedGuideSchema,
45
+ RULE_FIELDS: () => RULE_FIELDS,
46
+ RULE_OPERATORS: () => RULE_OPERATORS,
47
+ RuleGroupSchema: () => RuleGroupSchema,
48
+ RuleSchema: () => RuleSchema,
49
+ SDK_API_VERSION: () => SDK_API_VERSION,
50
+ SELECTOR_KINDS: () => SELECTOR_KINDS,
51
+ SENSITIVE_PROPERTY_PATTERN: () => SENSITIVE_PROPERTY_PATTERN,
52
+ SERVER_ASSISTED_FIELDS: () => SERVER_ASSISTED_FIELDS,
53
+ SHARED_VERSION: () => SHARED_VERSION,
54
+ STEP_TYPES: () => STEP_TYPES,
55
+ SURVEY_QUESTION_TYPES: () => SURVEY_QUESTION_TYPES,
56
+ SdkEventSchema: () => SdkEventSchema,
57
+ SelectorStrategySchema: () => SelectorStrategySchema,
58
+ StepBranchSchema: () => StepBranchSchema,
59
+ StepSchema: () => StepSchema,
60
+ SurveyConfigSchema: () => SurveyConfigSchema,
61
+ SurveyQuestionSchema: () => SurveyQuestionSchema,
62
+ SurveyResponseRequestSchema: () => SurveyResponseRequestSchema,
63
+ TRIGGER_TYPES: () => TRIGGER_TYPES,
64
+ TargetSchema: () => TargetSchema,
65
+ TriggerSchema: () => TriggerSchema,
66
+ clamp: () => clamp,
67
+ compare: () => compare,
68
+ defaultGuideConfig: () => defaultGuideConfig,
69
+ defaultStep: () => defaultStep,
70
+ describeRule: () => describeRule,
71
+ emptyRuleGroup: () => emptyRuleGroup,
72
+ evaluateRule: () => evaluateRule,
73
+ evaluateRules: () => evaluateRules,
74
+ fnv1a: () => fnv1a,
75
+ getPath: () => getPath,
76
+ isPlainObject: () => isPlainObject,
77
+ isRuleGroup: () => isRuleGroup,
78
+ matchUrlPattern: () => matchUrlPattern,
79
+ randomId: () => randomId,
80
+ rolloutBucket: () => rolloutBucket,
81
+ safeRegExp: () => safeRegExp,
82
+ sanitizeProperties: () => sanitizeProperties,
83
+ slugify: () => slugify,
84
+ stripOrigin: () => stripOrigin,
85
+ templateConfig: () => templateConfig,
86
+ validateGuideConfig: () => validateGuideConfig
87
+ });
88
+ module.exports = __toCommonJS(index_exports);
89
+
90
+ // src/utils.ts
91
+ function fnv1a(input) {
92
+ let h = 2166136261;
93
+ for (let i = 0; i < input.length; i++) {
94
+ h ^= input.charCodeAt(i);
95
+ h = Math.imul(h, 16777619) >>> 0;
96
+ }
97
+ return h >>> 0;
98
+ }
99
+ function rolloutBucket(subject, salt) {
100
+ return fnv1a(`${salt}:${subject}`) % 100;
101
+ }
102
+ function slugify(input) {
103
+ return input.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "untitled";
104
+ }
105
+ var ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
106
+ function randomId(size = 16) {
107
+ const g = typeof globalThis !== "undefined" ? globalThis : {};
108
+ let bytes;
109
+ if (g.crypto && typeof g.crypto.getRandomValues === "function") {
110
+ bytes = new Uint8Array(size);
111
+ g.crypto.getRandomValues(bytes);
112
+ } else {
113
+ bytes = new Uint8Array(size);
114
+ for (let i = 0; i < size; i++) bytes[i] = Math.floor(Math.random() * 256);
115
+ }
116
+ let out = "";
117
+ for (let i = 0; i < size; i++) out += ALPHABET[bytes[i] % ALPHABET.length];
118
+ return out;
119
+ }
120
+ function isPlainObject(v) {
121
+ return typeof v === "object" && v !== null && !Array.isArray(v);
122
+ }
123
+ function getPath(obj, path) {
124
+ if (!path) return obj;
125
+ let cur = obj;
126
+ for (const part of path.split(".")) {
127
+ if (cur == null) return void 0;
128
+ cur = cur[part];
129
+ }
130
+ return cur;
131
+ }
132
+ function clamp(n, min, max) {
133
+ return Math.min(max, Math.max(min, n));
134
+ }
135
+ function matchUrlPattern(pattern, url) {
136
+ if (!pattern) return true;
137
+ if (pattern === url) return true;
138
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "__GLOBSTAR__").replace(/\*/g, "[^/]*").replace(/__GLOBSTAR__/g, ".*");
139
+ const re = new RegExp(`^${escaped}$`);
140
+ return re.test(url) || re.test(stripOrigin(url));
141
+ }
142
+ function stripOrigin(url) {
143
+ try {
144
+ const u = new URL(url);
145
+ return u.pathname + u.search + u.hash;
146
+ } catch {
147
+ return url;
148
+ }
149
+ }
150
+ function safeRegExp(source, flags = "i") {
151
+ if (!source || source.length > 200) return null;
152
+ try {
153
+ return new RegExp(source, flags);
154
+ } catch {
155
+ return null;
156
+ }
157
+ }
158
+
159
+ // src/targeting.ts
160
+ var import_zod = require("zod");
161
+
162
+ // src/targeting-eval.ts
163
+ var RULE_OPERATORS = [
164
+ "equals",
165
+ "not_equals",
166
+ "contains",
167
+ "not_contains",
168
+ "starts_with",
169
+ "ends_with",
170
+ "exists",
171
+ "not_exists",
172
+ "gt",
173
+ "gte",
174
+ "lt",
175
+ "lte",
176
+ "in",
177
+ "not_in",
178
+ "regex",
179
+ "is_true",
180
+ "is_false",
181
+ "before",
182
+ "after"
183
+ ];
184
+ var RULE_FIELDS = [
185
+ "user.attribute",
186
+ "account.attribute",
187
+ "user.role",
188
+ "user.plan",
189
+ "url.path",
190
+ "url.full",
191
+ "url.query",
192
+ "device.type",
193
+ "device.browser",
194
+ "device.os",
195
+ "visitor.firstVisit",
196
+ "visitor.returning",
197
+ "event.seen",
198
+ "guide.completed",
199
+ "guide.dismissed",
200
+ "guide.seen",
201
+ "checklist.completed",
202
+ "audience.member",
203
+ "datetime.now",
204
+ "datetime.dayOfWeek",
205
+ "datetime.hour",
206
+ "rollout.percentage",
207
+ "environment",
208
+ "locale"
209
+ ];
210
+ function isRuleGroup(r) {
211
+ return r.rules !== void 0;
212
+ }
213
+ function emptyRuleGroup(id = "root") {
214
+ return { id, op: "and", rules: [] };
215
+ }
216
+ function has(set, v) {
217
+ if (!set) return false;
218
+ return set instanceof Set ? set.has(v) : set.includes(v);
219
+ }
220
+ function resolveField(rule, ctx) {
221
+ switch (rule.field) {
222
+ case "user.attribute":
223
+ return rule.key ? getPath(ctx.userAttributes, rule.key) : void 0;
224
+ case "account.attribute":
225
+ return rule.key ? getPath(ctx.accountAttributes, rule.key) : void 0;
226
+ case "user.role":
227
+ return ctx.userAttributes.role;
228
+ case "user.plan":
229
+ return ctx.userAttributes.plan ?? ctx.accountAttributes.plan;
230
+ case "url.path":
231
+ return ctx.path;
232
+ case "url.full":
233
+ return ctx.url;
234
+ case "url.query":
235
+ return rule.key ? ctx.query[rule.key] : void 0;
236
+ case "device.type":
237
+ return ctx.device.type;
238
+ case "device.browser":
239
+ return ctx.device.browser;
240
+ case "device.os":
241
+ return ctx.device.os;
242
+ case "visitor.firstVisit":
243
+ return ctx.firstVisit;
244
+ case "visitor.returning":
245
+ return !ctx.firstVisit;
246
+ case "event.seen":
247
+ return rule.key ? has(ctx.eventsSeen, rule.key) : false;
248
+ case "guide.completed":
249
+ return rule.key ? has(ctx.completedGuides, rule.key) : false;
250
+ case "guide.dismissed":
251
+ return rule.key ? has(ctx.dismissedGuides, rule.key) : false;
252
+ case "guide.seen":
253
+ return rule.key ? has(ctx.seenGuides, rule.key) : false;
254
+ case "checklist.completed":
255
+ return rule.key ? has(ctx.completedChecklists, rule.key) : false;
256
+ case "audience.member":
257
+ return rule.key ? has(ctx.audienceIds, rule.key) : false;
258
+ case "datetime.now":
259
+ return ctx.now.toISOString();
260
+ case "datetime.dayOfWeek":
261
+ return ctx.now.getDay();
262
+ case "datetime.hour":
263
+ return ctx.now.getHours();
264
+ case "rollout.percentage":
265
+ return rolloutBucket(ctx.userId || ctx.anonymousId, ctx.rolloutSalt || "guidely");
266
+ case "environment":
267
+ return ctx.environment;
268
+ case "locale":
269
+ return ctx.locale;
270
+ default:
271
+ return void 0;
272
+ }
273
+ }
274
+ function toNumber(v) {
275
+ if (typeof v === "number") return v;
276
+ if (typeof v === "string" && v.trim() !== "" && !Number.isNaN(Number(v))) return Number(v);
277
+ if (v instanceof Date) return v.getTime();
278
+ return null;
279
+ }
280
+ function toDateMs(v) {
281
+ if (v instanceof Date) return v.getTime();
282
+ if (typeof v === "string") {
283
+ const t = Date.parse(v);
284
+ return Number.isNaN(t) ? null : t;
285
+ }
286
+ if (typeof v === "number") return v;
287
+ return null;
288
+ }
289
+ function compare(op, actual, expected) {
290
+ switch (op) {
291
+ case "exists":
292
+ return actual !== void 0 && actual !== null && actual !== "";
293
+ case "not_exists":
294
+ return actual === void 0 || actual === null || actual === "";
295
+ case "is_true":
296
+ return actual === true || actual === "true";
297
+ case "is_false":
298
+ return actual === false || actual === "false";
299
+ case "equals":
300
+ if (typeof actual === "boolean") return String(actual) === String(expected);
301
+ return String(actual ?? "") === String(expected ?? "");
302
+ case "not_equals":
303
+ return String(actual ?? "") !== String(expected ?? "");
304
+ case "contains":
305
+ return typeof actual === "string" && actual.toLowerCase().includes(String(expected ?? "").toLowerCase());
306
+ case "not_contains":
307
+ return !(typeof actual === "string" && actual.toLowerCase().includes(String(expected ?? "").toLowerCase()));
308
+ case "starts_with":
309
+ return typeof actual === "string" && actual.startsWith(String(expected ?? ""));
310
+ case "ends_with":
311
+ return typeof actual === "string" && actual.endsWith(String(expected ?? ""));
312
+ case "gt":
313
+ case "gte":
314
+ case "lt":
315
+ case "lte": {
316
+ const a = toNumber(actual);
317
+ const b = toNumber(expected);
318
+ if (a === null || b === null) return false;
319
+ return op === "gt" ? a > b : op === "gte" ? a >= b : op === "lt" ? a < b : a <= b;
320
+ }
321
+ case "in": {
322
+ const list = Array.isArray(expected) ? expected : String(expected ?? "").split(",").map((s) => s.trim());
323
+ return list.includes(String(actual ?? ""));
324
+ }
325
+ case "not_in": {
326
+ const list = Array.isArray(expected) ? expected : String(expected ?? "").split(",").map((s) => s.trim());
327
+ return !list.includes(String(actual ?? ""));
328
+ }
329
+ case "regex": {
330
+ const re = safeRegExp(String(expected ?? ""));
331
+ return !!re && typeof actual === "string" && re.test(actual);
332
+ }
333
+ case "before": {
334
+ const a = toDateMs(actual);
335
+ const b = toDateMs(expected);
336
+ return a !== null && b !== null && a < b;
337
+ }
338
+ case "after": {
339
+ const a = toDateMs(actual);
340
+ const b = toDateMs(expected);
341
+ return a !== null && b !== null && a > b;
342
+ }
343
+ default:
344
+ return false;
345
+ }
346
+ }
347
+ function evaluateRule(rule, ctx) {
348
+ let result;
349
+ if (rule.field === "url.path" || rule.field === "url.full") {
350
+ const actual = String(resolveField(rule, ctx) ?? "");
351
+ if (rule.op === "equals") result = matchUrlPattern(String(rule.value ?? ""), actual);
352
+ else if (rule.op === "not_equals") result = !matchUrlPattern(String(rule.value ?? ""), actual);
353
+ else result = compare(rule.op, actual, rule.value);
354
+ } else {
355
+ result = compare(rule.op, resolveField(rule, ctx), rule.value);
356
+ }
357
+ return rule.negate ? !result : result;
358
+ }
359
+ function evaluateRules(group, ctx) {
360
+ if (!group || group.rules.length === 0) return group?.negate ? false : true;
361
+ const results = group.rules.map((r) => isRuleGroup(r) ? evaluateRules(r, ctx) : evaluateRule(r, ctx));
362
+ const value = group.op === "and" ? results.every(Boolean) : results.some(Boolean);
363
+ return group.negate ? !value : value;
364
+ }
365
+ var SERVER_ASSISTED_FIELDS = ["audience.member", "guide.completed", "guide.dismissed", "guide.seen", "checklist.completed"];
366
+ function describeRule(rule) {
367
+ const subject = rule.key ? `${rule.field}[${rule.key}]` : rule.field;
368
+ const v = Array.isArray(rule.value) ? rule.value.join(", ") : rule.value === void 0 ? "" : String(rule.value);
369
+ return `${rule.negate ? "NOT " : ""}${subject} ${rule.op.replace(/_/g, " ")}${v ? ` "${v}"` : ""}`;
370
+ }
371
+
372
+ // src/targeting.ts
373
+ var RuleSchema = import_zod.z.object({
374
+ id: import_zod.z.string().min(1),
375
+ field: import_zod.z.enum(RULE_FIELDS),
376
+ key: import_zod.z.string().max(200).optional(),
377
+ op: import_zod.z.enum(RULE_OPERATORS),
378
+ value: import_zod.z.union([import_zod.z.string(), import_zod.z.number(), import_zod.z.boolean(), import_zod.z.array(import_zod.z.string())]).optional(),
379
+ negate: import_zod.z.boolean().optional()
380
+ });
381
+ var RuleGroupSchema = import_zod.z.lazy(
382
+ () => import_zod.z.object({
383
+ id: import_zod.z.string().min(1),
384
+ op: import_zod.z.enum(["and", "or"]),
385
+ rules: import_zod.z.array(import_zod.z.union([RuleSchema, RuleGroupSchema])).max(100),
386
+ negate: import_zod.z.boolean().optional()
387
+ })
388
+ );
389
+
390
+ // src/guide.ts
391
+ var import_zod2 = require("zod");
392
+ var GUIDE_KINDS = ["flow", "modal", "tooltip", "banner", "checklist", "survey", "announcement"];
393
+ var GUIDE_STATUSES = ["draft", "published", "paused", "archived"];
394
+ var STEP_TYPES = ["modal", "tooltip", "popover", "spotlight", "hotspot", "banner", "slideout", "checklist", "custom", "survey"];
395
+ var PLACEMENTS = ["top", "top-start", "top-end", "right", "right-start", "right-end", "bottom", "bottom-start", "bottom-end", "left", "left-start", "left-end", "auto"];
396
+ var SELECTOR_KINDS = ["css", "id", "data-attr", "aria", "text", "xpath"];
397
+ var SelectorStrategySchema = import_zod2.z.object({
398
+ kind: import_zod2.z.enum(SELECTOR_KINDS),
399
+ value: import_zod2.z.string().min(1).max(1e3),
400
+ /** Optional human-readable label captured at pick time. */
401
+ label: import_zod2.z.string().max(200).optional()
402
+ });
403
+ var TargetSchema = import_zod2.z.object({
404
+ /** Ordered strategies; the first one resolving to exactly one element wins. */
405
+ selectors: import_zod2.z.array(SelectorStrategySchema).max(10),
406
+ /** How long to wait for the element before applying fallback. */
407
+ waitMs: import_zod2.z.number().int().min(0).max(6e4).default(5e3),
408
+ /** What to do when the element never shows up. */
409
+ fallback: import_zod2.z.enum(["skip", "show_as_modal", "end_guide", "wait"]).default("skip"),
410
+ /** Scroll the element into view before positioning. */
411
+ scrollIntoView: import_zod2.z.boolean().default(true),
412
+ /** Text snapshot captured at pick time; used for authoring warnings. */
413
+ textSnapshot: import_zod2.z.string().max(200).optional()
414
+ });
415
+ var BUTTON_ACTIONS = ["next", "back", "dismiss", "complete", "skip", "url", "start_guide", "event", "open_resource_center", "none"];
416
+ var ButtonSchema = import_zod2.z.object({
417
+ id: import_zod2.z.string().min(1),
418
+ label: import_zod2.z.string().min(1).max(80),
419
+ action: import_zod2.z.enum(BUTTON_ACTIONS),
420
+ variant: import_zod2.z.enum(["primary", "secondary", "ghost", "link"]).default("primary"),
421
+ /** url for `url`, guide id for `start_guide`, event name for `event`, step id for `next` branch override */
422
+ value: import_zod2.z.string().max(2e3).optional(),
423
+ newTab: import_zod2.z.boolean().optional()
424
+ });
425
+ var StepBranchSchema = import_zod2.z.object({
426
+ id: import_zod2.z.string(),
427
+ /** Rule group evaluated against targeting context when leaving the step. */
428
+ when: RuleGroupSchema,
429
+ /** Step id to jump to. */
430
+ goTo: import_zod2.z.string()
431
+ });
432
+ var ADVANCE_ON = ["button", "element_click", "element_visible", "event", "delay", "any"];
433
+ var StepSchema = import_zod2.z.object({
434
+ id: import_zod2.z.string().min(1).max(64),
435
+ type: import_zod2.z.enum(STEP_TYPES),
436
+ title: import_zod2.z.string().max(200).default(""),
437
+ /** Sanitized HTML body (links, images, lists, formatting). */
438
+ body: import_zod2.z.string().max(2e4).default(""),
439
+ imageUrl: import_zod2.z.string().url().max(2e3).optional().or(import_zod2.z.literal("")),
440
+ buttons: import_zod2.z.array(ButtonSchema).max(6).default([]),
441
+ target: TargetSchema.optional(),
442
+ placement: import_zod2.z.enum(PLACEMENTS).default("auto"),
443
+ /** Pixel offset between anchor and card. */
444
+ offset: import_zod2.z.number().int().min(0).max(64).default(10),
445
+ showOverlay: import_zod2.z.boolean().default(false),
446
+ showProgress: import_zod2.z.boolean().default(true),
447
+ showClose: import_zod2.z.boolean().default(true),
448
+ dismissOnOutsideClick: import_zod2.z.boolean().default(false),
449
+ /** Highlight padding for spotlight. */
450
+ spotlightPadding: import_zod2.z.number().int().min(0).max(64).default(6),
451
+ /** Banner/slideout position. */
452
+ position: import_zod2.z.enum(["top", "bottom", "top-left", "top-right", "bottom-left", "bottom-right", "center"]).default("bottom-right"),
453
+ /** URL pattern this step belongs to (for multi-page flows). */
454
+ url: import_zod2.z.string().max(500).optional(),
455
+ /** Automatic advancement. */
456
+ advanceOn: import_zod2.z.enum(ADVANCE_ON).default("button"),
457
+ advanceValue: import_zod2.z.string().max(500).optional(),
458
+ advanceDelayMs: import_zod2.z.number().int().min(0).max(6e5).optional(),
459
+ branches: import_zod2.z.array(StepBranchSchema).max(10).default([]),
460
+ width: import_zod2.z.number().int().min(200).max(900).optional(),
461
+ /** Step-level targeting: skip step when rules do not match. */
462
+ targeting: RuleGroupSchema.optional(),
463
+ /** Hotspot pulse dot settings. */
464
+ hotspot: import_zod2.z.object({ size: import_zod2.z.number().int().min(8).max(40).default(14), pulse: import_zod2.z.boolean().default(true), openOn: import_zod2.z.enum(["click", "hover"]).default("click") }).optional(),
465
+ /** Custom step: arbitrary sanitized HTML with no chrome. */
466
+ customHtml: import_zod2.z.string().max(5e4).optional()
467
+ });
468
+ var TRIGGER_TYPES = ["page_load", "url_match", "event", "element_click", "element_visible", "manual", "delay", "after_guide", "segment_enter"];
469
+ var TriggerSchema = import_zod2.z.object({
470
+ type: import_zod2.z.enum(TRIGGER_TYPES).default("page_load"),
471
+ /** url pattern, event name, selector, guide id, audience id depending on type */
472
+ value: import_zod2.z.string().max(1e3).optional(),
473
+ delayMs: import_zod2.z.number().int().min(0).max(6e5).default(0),
474
+ /** Also require URL to match this pattern (any trigger). */
475
+ urlPattern: import_zod2.z.string().max(500).optional()
476
+ });
477
+ var FrequencySchema = import_zod2.z.object({
478
+ mode: import_zod2.z.enum(["once", "once_per_session", "every_time", "interval"]).default("once"),
479
+ intervalHours: import_zod2.z.number().int().min(1).max(8760).optional(),
480
+ maxImpressions: import_zod2.z.number().int().min(1).max(1e3).optional(),
481
+ /** Hours to wait after dismissal before eligible again (when not `once`). */
482
+ cooldownHours: import_zod2.z.number().int().min(0).max(8760).default(0),
483
+ /** Lower number wins when multiple guides qualify. */
484
+ priority: import_zod2.z.number().int().min(0).max(1e3).default(100),
485
+ /** Guide ids that must not be active at the same time. */
486
+ mutuallyExclusiveWith: import_zod2.z.array(import_zod2.z.string()).max(50).default([]),
487
+ /** Re-show if dismissed (otherwise dismissal counts as completion for frequency). */
488
+ reshowAfterDismiss: import_zod2.z.boolean().default(false)
489
+ });
490
+ var AppearanceSchema = import_zod2.z.object({
491
+ theme: import_zod2.z.enum(["light", "dark", "auto"]).default("auto"),
492
+ primaryColor: import_zod2.z.string().regex(/^#[0-9a-fA-F]{6}$/).optional(),
493
+ backgroundColor: import_zod2.z.string().regex(/^#[0-9a-fA-F]{6}$/).optional(),
494
+ textColor: import_zod2.z.string().regex(/^#[0-9a-fA-F]{6}$/).optional(),
495
+ borderRadius: import_zod2.z.number().int().min(0).max(32).optional(),
496
+ fontFamily: import_zod2.z.string().max(200).optional(),
497
+ overlayOpacity: import_zod2.z.number().min(0).max(1).optional(),
498
+ zIndex: import_zod2.z.number().int().min(1).max(2147483647).optional(),
499
+ preset: import_zod2.z.string().max(50).optional(),
500
+ animation: import_zod2.z.enum(["fade", "slide", "none"]).default("fade")
501
+ });
502
+ var ChecklistTaskSchema = import_zod2.z.object({
503
+ id: import_zod2.z.string().min(1),
504
+ title: import_zod2.z.string().min(1).max(200),
505
+ description: import_zod2.z.string().max(2e3).default(""),
506
+ /** How the task completes. */
507
+ completion: import_zod2.z.enum(["manual", "event", "guide_completed", "url_visited", "click"]).default("manual"),
508
+ completionValue: import_zod2.z.string().max(500).optional(),
509
+ /** Optional action when clicking the task. */
510
+ action: import_zod2.z.enum(["none", "start_guide", "url", "event"]).default("none"),
511
+ actionValue: import_zod2.z.string().max(2e3).optional(),
512
+ required: import_zod2.z.boolean().default(true)
513
+ });
514
+ var ChecklistConfigSchema = import_zod2.z.object({
515
+ tasks: import_zod2.z.array(ChecklistTaskSchema).min(1).max(30),
516
+ ordered: import_zod2.z.boolean().default(false),
517
+ launcherLabel: import_zod2.z.string().max(60).default("Getting started"),
518
+ position: import_zod2.z.enum(["bottom-right", "bottom-left"]).default("bottom-right"),
519
+ completionTitle: import_zod2.z.string().max(200).default("You're all set!"),
520
+ completionBody: import_zod2.z.string().max(2e3).default(""),
521
+ autoOpen: import_zod2.z.boolean().default(true),
522
+ dismissible: import_zod2.z.boolean().default(true)
523
+ });
524
+ var SURVEY_QUESTION_TYPES = ["nps", "csat", "thumbs", "single_choice", "multiple_choice", "text", "rating"];
525
+ var SurveyQuestionSchema = import_zod2.z.object({
526
+ id: import_zod2.z.string().min(1),
527
+ type: import_zod2.z.enum(SURVEY_QUESTION_TYPES),
528
+ title: import_zod2.z.string().min(1).max(500),
529
+ description: import_zod2.z.string().max(1e3).default(""),
530
+ required: import_zod2.z.boolean().default(true),
531
+ options: import_zod2.z.array(import_zod2.z.object({ id: import_zod2.z.string(), label: import_zod2.z.string().max(200) })).max(20).default([]),
532
+ scaleMax: import_zod2.z.number().int().min(2).max(10).optional(),
533
+ lowLabel: import_zod2.z.string().max(60).optional(),
534
+ highLabel: import_zod2.z.string().max(60).optional(),
535
+ placeholder: import_zod2.z.string().max(200).optional(),
536
+ /** Conditional follow-up: show only when a previous answer matches. */
537
+ showWhen: import_zod2.z.object({ questionId: import_zod2.z.string(), op: import_zod2.z.enum(["equals", "lte", "gte", "in"]), value: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.number(), import_zod2.z.array(import_zod2.z.string())]) }).optional()
538
+ });
539
+ var SurveyConfigSchema = import_zod2.z.object({
540
+ questions: import_zod2.z.array(SurveyQuestionSchema).min(1).max(20),
541
+ thankYouTitle: import_zod2.z.string().max(200).default("Thanks for your feedback!"),
542
+ thankYouBody: import_zod2.z.string().max(2e3).default(""),
543
+ position: import_zod2.z.enum(["bottom-right", "bottom-left", "center"]).default("bottom-right"),
544
+ startAt: import_zod2.z.string().datetime().optional(),
545
+ endAt: import_zod2.z.string().datetime().optional()
546
+ });
547
+ var GuideConfigSchema = import_zod2.z.object({
548
+ steps: import_zod2.z.array(StepSchema).max(50).default([]),
549
+ targeting: RuleGroupSchema.default(() => emptyRuleGroup()),
550
+ trigger: TriggerSchema.default({}),
551
+ frequency: FrequencySchema.default({}),
552
+ appearance: AppearanceSchema.default({}),
553
+ checklist: ChecklistConfigSchema.optional(),
554
+ survey: SurveyConfigSchema.optional(),
555
+ /** Restrict guide to url pattern(s) (applied before trigger). */
556
+ urlPatterns: import_zod2.z.array(import_zod2.z.string().max(500)).max(20).default([])
557
+ });
558
+ function defaultGuideConfig(kind) {
559
+ return GuideConfigSchema.parse({
560
+ steps: kind === "checklist" || kind === "survey" ? [] : [defaultStep(kind === "flow" ? "tooltip" : kind === "banner" ? "banner" : kind === "tooltip" ? "tooltip" : "modal")],
561
+ checklist: kind === "checklist" ? { tasks: [{ id: "task-1", title: "Complete your profile" }] } : void 0,
562
+ survey: kind === "survey" ? { questions: [{ id: "q1", type: "nps", title: "How likely are you to recommend us to a friend?" }] } : void 0,
563
+ trigger: kind === "checklist" ? { type: "page_load" } : {}
564
+ });
565
+ }
566
+ var stepCounter = 0;
567
+ function defaultStep(type, overrides = {}) {
568
+ stepCounter += 1;
569
+ const base = {
570
+ id: `step-${Date.now().toString(36)}-${stepCounter}`,
571
+ type,
572
+ title: type === "modal" ? "Welcome!" : "Step title",
573
+ body: "<p>Describe what the user should do here.</p>",
574
+ buttons: [
575
+ { id: "btn-back", label: "Back", action: "back", variant: "ghost" },
576
+ { id: "btn-next", label: "Next", action: "next", variant: "primary" }
577
+ ],
578
+ placement: type === "banner" ? "top" : "auto",
579
+ showOverlay: type === "modal" || type === "spotlight",
580
+ ...overrides
581
+ };
582
+ if (["tooltip", "popover", "spotlight", "hotspot"].includes(type) && !base.target) {
583
+ base.target = { selectors: [], waitMs: 5e3, fallback: "skip", scrollIntoView: true };
584
+ }
585
+ return StepSchema.parse(base);
586
+ }
587
+ function validateGuideConfig(kind, config) {
588
+ const issues = [];
589
+ if (kind === "checklist") {
590
+ if (!config.checklist || config.checklist.tasks.length === 0) issues.push({ level: "error", message: "Checklist needs at least one task." });
591
+ } else if (kind === "survey") {
592
+ if (!config.survey || config.survey.questions.length === 0) issues.push({ level: "error", message: "Survey needs at least one question." });
593
+ config.survey?.questions.forEach((q) => {
594
+ if ((q.type === "single_choice" || q.type === "multiple_choice") && q.options.length < 2) issues.push({ level: "error", message: `Question "${q.title}" needs at least two options.` });
595
+ });
596
+ } else {
597
+ if (config.steps.length === 0) issues.push({ level: "error", message: "Add at least one step." });
598
+ }
599
+ const ids = /* @__PURE__ */ new Set();
600
+ config.steps.forEach((step, i) => {
601
+ if (ids.has(step.id)) issues.push({ level: "error", stepId: step.id, message: `Duplicate step id "${step.id}".` });
602
+ ids.add(step.id);
603
+ const anchored = ["tooltip", "popover", "spotlight", "hotspot"].includes(step.type);
604
+ if (anchored && (!step.target || step.target.selectors.length === 0)) issues.push({ level: "error", stepId: step.id, message: `Step ${i + 1} (${step.type}) needs a target element.` });
605
+ if (!step.title && !step.body && step.type !== "custom") issues.push({ level: "warning", stepId: step.id, message: `Step ${i + 1} has no title or body.` });
606
+ if (step.buttons.length === 0 && step.advanceOn === "button" && step.type !== "hotspot" && step.type !== "custom") issues.push({ level: "warning", stepId: step.id, message: `Step ${i + 1} has no buttons and advances on button; users can only close it.` });
607
+ step.buttons.forEach((b) => {
608
+ if (b.action === "url" && !/^https?:\/\//i.test(b.value || "") && !(b.value || "").startsWith("/")) issues.push({ level: "error", stepId: step.id, message: `Button "${b.label}" needs an http(s) or relative URL.` });
609
+ if (b.action === "start_guide" && !b.value) issues.push({ level: "error", stepId: step.id, message: `Button "${b.label}" needs a guide to start.` });
610
+ });
611
+ step.branches.forEach((br) => {
612
+ if (!ids.has(br.goTo) && !config.steps.some((s) => s.id === br.goTo)) issues.push({ level: "error", stepId: step.id, message: `Branch points to unknown step "${br.goTo}".` });
613
+ });
614
+ });
615
+ if (config.trigger.type === "event" && !config.trigger.value) issues.push({ level: "error", message: "Event trigger needs an event name." });
616
+ if (config.trigger.type === "element_click" && !config.trigger.value) issues.push({ level: "error", message: "Element click trigger needs a selector." });
617
+ if (config.trigger.type === "url_match" && !config.trigger.value) issues.push({ level: "error", message: "URL trigger needs a pattern." });
618
+ if (config.frequency.mode === "interval" && !config.frequency.intervalHours) issues.push({ level: "error", message: "Interval frequency needs an interval in hours." });
619
+ return issues;
620
+ }
621
+ var ProjectThemeSchema = import_zod2.z.object({
622
+ primaryColor: import_zod2.z.string().regex(/^#[0-9a-fA-F]{6}$/).default("#4f46e5"),
623
+ backgroundColor: import_zod2.z.string().regex(/^#[0-9a-fA-F]{6}$/).default("#ffffff"),
624
+ textColor: import_zod2.z.string().regex(/^#[0-9a-fA-F]{6}$/).default("#111827"),
625
+ borderRadius: import_zod2.z.number().int().min(0).max(32).default(12),
626
+ fontFamily: import_zod2.z.string().max(200).default("Inter, system-ui, -apple-system, sans-serif"),
627
+ logoUrl: import_zod2.z.string().max(2e3).optional(),
628
+ brandName: import_zod2.z.string().max(100).optional()
629
+ });
630
+
631
+ // src/events.ts
632
+ var import_zod3 = require("zod");
633
+
634
+ // src/events-util.ts
635
+ var SENSITIVE_PROPERTY_PATTERN = /(password|passwd|secret|token|credit|card|cvv|ssn|iban)/i;
636
+ var MAX_PROPERTY_BYTES = 8 * 1024;
637
+ function sanitizeProperties(props) {
638
+ if (!props) return {};
639
+ const out = {};
640
+ let size = 0;
641
+ for (const [k, v] of Object.entries(props)) {
642
+ if (SENSITIVE_PROPERTY_PATTERN.test(k)) continue;
643
+ if (typeof v === "function" || typeof v === "symbol") continue;
644
+ const str = typeof v === "string" ? v : JSON.stringify(v ?? null);
645
+ size += k.length + (str?.length ?? 0);
646
+ if (size > MAX_PROPERTY_BYTES) break;
647
+ out[k.slice(0, 100)] = typeof v === "string" ? v.slice(0, 1e3) : v;
648
+ }
649
+ return out;
650
+ }
651
+
652
+ // src/events.ts
653
+ var EVENT_NAMES = [
654
+ "guide_viewed",
655
+ "guide_started",
656
+ "step_viewed",
657
+ "step_completed",
658
+ "guide_completed",
659
+ "guide_skipped",
660
+ "guide_dismissed",
661
+ "guide_abandoned",
662
+ "checklist_task_completed",
663
+ "checklist_completed",
664
+ "checklist_dismissed",
665
+ "survey_displayed",
666
+ "survey_submitted",
667
+ "survey_dismissed",
668
+ "resource_center_opened",
669
+ "article_viewed",
670
+ "custom",
671
+ "page_view",
672
+ "identify"
673
+ ];
674
+ var GUIDE_LIFECYCLE_EVENTS = ["guide_viewed", "guide_started", "step_viewed", "step_completed", "guide_completed", "guide_skipped", "guide_dismissed", "guide_abandoned"];
675
+ var SdkEventSchema = import_zod3.z.object({
676
+ eventId: import_zod3.z.string().min(8).max(64),
677
+ name: import_zod3.z.enum(EVENT_NAMES),
678
+ /** For `custom` events: the application-defined name. */
679
+ customName: import_zod3.z.string().min(1).max(120).optional(),
680
+ occurredAt: import_zod3.z.string().datetime({ offset: true }),
681
+ anonymousId: import_zod3.z.string().min(1).max(128),
682
+ userId: import_zod3.z.string().max(256).optional().nullable(),
683
+ accountId: import_zod3.z.string().max(256).optional().nullable(),
684
+ sessionId: import_zod3.z.string().max(128).optional(),
685
+ guideId: import_zod3.z.string().max(64).optional(),
686
+ guideVersion: import_zod3.z.number().int().optional(),
687
+ stepId: import_zod3.z.string().max(64).optional(),
688
+ stepIndex: import_zod3.z.number().int().min(0).optional(),
689
+ url: import_zod3.z.string().max(2e3).optional(),
690
+ properties: import_zod3.z.record(import_zod3.z.unknown()).optional()
691
+ });
692
+ var EventBatchSchema = import_zod3.z.object({
693
+ events: import_zod3.z.array(SdkEventSchema).min(1).max(50),
694
+ sentAt: import_zod3.z.string().datetime({ offset: true }).optional(),
695
+ sdk: import_zod3.z.object({ name: import_zod3.z.string().max(60), version: import_zod3.z.string().max(30) }).optional()
696
+ });
697
+
698
+ // src/sdk-api.ts
699
+ var import_zod4 = require("zod");
700
+ var IdentifyRequestSchema = import_zod4.z.object({
701
+ anonymousId: import_zod4.z.string().min(1).max(128),
702
+ userId: import_zod4.z.string().min(1).max(256).optional().nullable(),
703
+ traits: import_zod4.z.record(import_zod4.z.unknown()).optional(),
704
+ account: import_zod4.z.object({ id: import_zod4.z.string().min(1).max(256), traits: import_zod4.z.record(import_zod4.z.unknown()).optional() }).optional().nullable(),
705
+ sessionId: import_zod4.z.string().max(128).optional(),
706
+ url: import_zod4.z.string().max(2e3).optional()
707
+ });
708
+ var ChecklistProgressRequestSchema = import_zod4.z.object({
709
+ anonymousId: import_zod4.z.string().min(1).max(128),
710
+ userId: import_zod4.z.string().max(256).optional().nullable(),
711
+ guideVersion: import_zod4.z.number().int().optional(),
712
+ completedTaskIds: import_zod4.z.array(import_zod4.z.string().max(64)).max(100),
713
+ dismissed: import_zod4.z.boolean().optional(),
714
+ completed: import_zod4.z.boolean().optional()
715
+ });
716
+ var SurveyResponseRequestSchema = import_zod4.z.object({
717
+ anonymousId: import_zod4.z.string().min(1).max(128),
718
+ userId: import_zod4.z.string().max(256).optional().nullable(),
719
+ accountId: import_zod4.z.string().max(256).optional().nullable(),
720
+ guideVersion: import_zod4.z.number().int(),
721
+ answers: import_zod4.z.record(import_zod4.z.union([import_zod4.z.string().max(5e3), import_zod4.z.number(), import_zod4.z.boolean(), import_zod4.z.array(import_zod4.z.string().max(500)).max(50)])),
722
+ responseId: import_zod4.z.string().min(8).max(64)
723
+ });
724
+ var GuideStateUpdateSchema = import_zod4.z.object({
725
+ anonymousId: import_zod4.z.string().min(1).max(128),
726
+ userId: import_zod4.z.string().max(256).optional().nullable(),
727
+ updates: import_zod4.z.array(
728
+ import_zod4.z.object({
729
+ guideId: import_zod4.z.string(),
730
+ impression: import_zod4.z.boolean().optional(),
731
+ completed: import_zod4.z.boolean().optional(),
732
+ dismissed: import_zod4.z.boolean().optional()
733
+ })
734
+ ).max(50)
735
+ });
736
+ var AuthoringPickSchema = import_zod4.z.object({
737
+ stepId: import_zod4.z.string().max(64).optional(),
738
+ selectors: import_zod4.z.array(import_zod4.z.object({ kind: import_zod4.z.string(), value: import_zod4.z.string().max(1e3), label: import_zod4.z.string().max(200).optional() })).max(10),
739
+ textSnapshot: import_zod4.z.string().max(200).optional(),
740
+ url: import_zod4.z.string().max(2e3).optional(),
741
+ rect: import_zod4.z.object({ x: import_zod4.z.number(), y: import_zod4.z.number(), width: import_zod4.z.number(), height: import_zod4.z.number() }).optional()
742
+ });
743
+ var PublishedGuideSchema = import_zod4.z.object({
744
+ id: import_zod4.z.string(),
745
+ key: import_zod4.z.string(),
746
+ name: import_zod4.z.string(),
747
+ kind: import_zod4.z.string(),
748
+ version: import_zod4.z.number(),
749
+ config: GuideConfigSchema,
750
+ publishedAt: import_zod4.z.string()
751
+ });
752
+
753
+ // src/templates.ts
754
+ var nav = (opts = {}) => [
755
+ ...opts.first ? [] : [{ id: "back", label: "Back", action: "back", variant: "ghost" }],
756
+ opts.last ? { id: "done", label: "Finish", action: "complete", variant: "primary" } : { id: "next", label: "Next", action: "next", variant: "primary" }
757
+ ];
758
+ var GUIDE_TEMPLATES = [
759
+ {
760
+ id: "new-user-onboarding",
761
+ name: "New-user onboarding tour",
762
+ description: "A welcome modal followed by three anchored steps that introduce the main navigation.",
763
+ kind: "flow",
764
+ category: "onboarding",
765
+ config: {
766
+ steps: [
767
+ { id: "welcome", type: "modal", title: "Welcome aboard \u{1F44B}", body: "<p>We'll show you around in under a minute. You can skip at any time.</p>", showOverlay: true, buttons: [{ id: "skip", label: "Skip tour", action: "dismiss", variant: "ghost" }, { id: "start", label: "Show me around", action: "next" }] },
768
+ { id: "nav", type: "tooltip", title: "Your navigation", body: "<p>Everything lives in the sidebar. Switch between dashboards, contacts and settings here.</p>", target: { selectors: [{ kind: "data-attr", value: "[data-tour='sidebar']" }], waitMs: 5e3, fallback: "skip", scrollIntoView: true }, placement: "right", buttons: nav() },
769
+ { id: "create", type: "spotlight", title: "Create your first record", body: "<p>Use the primary button to add something new. We'll highlight it for you.</p>", target: { selectors: [{ kind: "data-attr", value: "[data-tour='primary-action']" }], waitMs: 5e3, fallback: "skip", scrollIntoView: true }, placement: "bottom", showOverlay: true, buttons: nav() },
770
+ { id: "help", type: "tooltip", title: "Need help later?", body: "<p>Open the help menu any time to replay this tour or browse the resource center.</p>", target: { selectors: [{ kind: "data-attr", value: "[data-tour='help']" }], waitMs: 5e3, fallback: "skip", scrollIntoView: true }, placement: "bottom", buttons: nav({ last: true }) }
771
+ ],
772
+ trigger: { type: "page_load" },
773
+ frequency: { mode: "once", priority: 10 },
774
+ targeting: { id: "root", op: "and", rules: [{ id: "r1", field: "visitor.firstVisit", op: "is_true" }] }
775
+ }
776
+ },
777
+ {
778
+ id: "feature-discovery",
779
+ name: "First-time feature discovery",
780
+ description: "A hotspot that draws attention to a new capability and expands into a tooltip.",
781
+ kind: "tooltip",
782
+ category: "feature",
783
+ config: {
784
+ steps: [{ id: "hotspot", type: "hotspot", title: "New: smart filters", body: "<p>Filters now understand natural language. Try <em>\u201Cdeals closing this month\u201D</em>.</p>", target: { selectors: [{ kind: "data-attr", value: "[data-tour='filters']" }], waitMs: 8e3, fallback: "skip", scrollIntoView: true }, placement: "bottom", buttons: [{ id: "got-it", label: "Got it", action: "complete" }], hotspot: { size: 14, pulse: true, openOn: "click" }, showProgress: false }],
785
+ trigger: { type: "element_visible", value: "[data-tour='filters']" },
786
+ frequency: { mode: "once", maxImpressions: 3, priority: 50 }
787
+ }
788
+ },
789
+ {
790
+ id: "setup-wizard",
791
+ name: "Setup wizard",
792
+ description: "Multi-step modal wizard that walks a new admin through initial configuration with branching.",
793
+ kind: "flow",
794
+ category: "setup",
795
+ config: {
796
+ steps: [
797
+ { id: "intro", type: "modal", title: "Let's set up your workspace", body: "<p>Three quick steps: invite your team, connect a data source, and choose a plan.</p>", showOverlay: true, buttons: [{ id: "later", label: "Later", action: "dismiss", variant: "ghost" }, { id: "go", label: "Start setup", action: "next" }] },
798
+ { id: "team", type: "popover", title: "Invite teammates", body: "<p>Guidely works best with your whole team. Add a few emails here.</p>", target: { selectors: [{ kind: "data-attr", value: "[data-tour='invite']" }], waitMs: 5e3, fallback: "show_as_modal", scrollIntoView: true }, buttons: nav() },
799
+ { id: "data", type: "popover", title: "Connect a data source", body: "<p>Import contacts from a CSV or connect your CRM.</p>", target: { selectors: [{ kind: "data-attr", value: "[data-tour='import']" }], waitMs: 5e3, fallback: "show_as_modal", scrollIntoView: true }, buttons: nav() },
800
+ { id: "done", type: "modal", title: "You're ready \u{1F389}", body: "<p>That's it. Explore the dashboard, and open the help menu if you get stuck.</p>", showOverlay: true, buttons: nav({ last: true }) }
801
+ ],
802
+ trigger: { type: "page_load", urlPattern: "/dashboard" },
803
+ frequency: { mode: "once", priority: 20 },
804
+ targeting: { id: "root", op: "and", rules: [{ id: "r1", field: "user.role", op: "equals", value: "admin" }] }
805
+ }
806
+ },
807
+ {
808
+ id: "form-completion",
809
+ name: "Form completion helper",
810
+ description: "Tooltips that advance as the user fills out each field of a form.",
811
+ kind: "flow",
812
+ category: "forms",
813
+ config: {
814
+ steps: [
815
+ { id: "name", type: "tooltip", title: "Name the contact", body: "<p>Use their full name so search works well.</p>", target: { selectors: [{ kind: "css", value: "input[name='name']" }], waitMs: 5e3, fallback: "skip", scrollIntoView: true }, placement: "right", advanceOn: "element_click", advanceValue: "input[name='email']", buttons: [] },
816
+ { id: "email", type: "tooltip", title: "Add an email", body: "<p>We'll use it for sequences and reminders.</p>", target: { selectors: [{ kind: "css", value: "input[name='email']" }], waitMs: 5e3, fallback: "skip", scrollIntoView: true }, placement: "right", buttons: nav() },
817
+ { id: "save", type: "spotlight", title: "Save", body: "<p>Hit save, and your contact appears in the list.</p>", target: { selectors: [{ kind: "css", value: "button[type='submit']" }], waitMs: 5e3, fallback: "skip", scrollIntoView: true }, placement: "top", showOverlay: true, buttons: nav({ last: true }) }
818
+ ],
819
+ trigger: { type: "url_match", value: "/contacts/new" },
820
+ frequency: { mode: "once", priority: 40 }
821
+ }
822
+ },
823
+ {
824
+ id: "workflow-walkthrough",
825
+ name: "Complex workflow walkthrough",
826
+ description: "A multi-page flow that follows the user across routes with URL-aware steps.",
827
+ kind: "flow",
828
+ category: "onboarding",
829
+ config: {
830
+ steps: [
831
+ { id: "start", type: "modal", title: "Close your first deal", body: "<p>We'll go from pipeline to closed-won in four steps.</p>", showOverlay: true, buttons: [{ id: "skip", label: "Not now", action: "dismiss", variant: "ghost" }, { id: "next", label: "Let's go", action: "next" }] },
832
+ { id: "pipeline", type: "tooltip", title: "Open the pipeline", body: "<p>Click here to see your deals by stage.</p>", url: "/dashboard", target: { selectors: [{ kind: "data-attr", value: "[data-tour='nav-deals']" }], waitMs: 5e3, fallback: "skip", scrollIntoView: true }, placement: "right", advanceOn: "element_click", advanceValue: "[data-tour='nav-deals']", buttons: [] },
833
+ { id: "drag", type: "spotlight", title: "Move a deal", body: "<p>Drag a card to the next stage, or use the stage menu.</p>", url: "/deals", target: { selectors: [{ kind: "data-attr", value: "[data-tour='deal-card']" }], waitMs: 8e3, fallback: "skip", scrollIntoView: true }, placement: "bottom", showOverlay: true, buttons: nav() },
834
+ { id: "won", type: "tooltip", title: "Mark as won", body: "<p>When it closes, mark it won to update your forecast.</p>", url: "/deals", target: { selectors: [{ kind: "data-attr", value: "[data-tour='mark-won']" }], waitMs: 8e3, fallback: "skip", scrollIntoView: true }, placement: "left", buttons: nav({ last: true }) }
835
+ ],
836
+ trigger: { type: "manual" },
837
+ frequency: { mode: "every_time", priority: 30 }
838
+ }
839
+ },
840
+ {
841
+ id: "feature-announcement",
842
+ name: "New feature announcement",
843
+ description: "A centered modal with an image announcing a new capability.",
844
+ kind: "announcement",
845
+ category: "announcement",
846
+ config: {
847
+ steps: [{ id: "announce", type: "modal", title: "Introducing Reports 2.0", body: "<p>Build custom reports with drag-and-drop metrics, scheduled emails and shareable links.</p><ul><li>12 new chart types</li><li>Export to CSV and PDF</li></ul>", imageUrl: "https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=800&q=60", showOverlay: true, buttons: [{ id: "later", label: "Maybe later", action: "dismiss", variant: "ghost" }, { id: "try", label: "Try it now", action: "url", value: "/reports" }], showProgress: false }],
848
+ trigger: { type: "page_load", delayMs: 1500 },
849
+ frequency: { mode: "once", priority: 60 }
850
+ }
851
+ },
852
+ {
853
+ id: "contextual-tooltip",
854
+ name: "Contextual tooltip",
855
+ description: "A single tooltip that appears when a specific element becomes visible.",
856
+ kind: "tooltip",
857
+ category: "help",
858
+ config: {
859
+ steps: [{ id: "tip", type: "tooltip", title: "Pro tip", body: "<p>Press <strong>\u2318K</strong> to jump anywhere.</p>", target: { selectors: [{ kind: "data-attr", value: "[data-tour='search']" }], waitMs: 5e3, fallback: "skip", scrollIntoView: true }, placement: "bottom", buttons: [{ id: "ok", label: "Got it", action: "complete" }], showProgress: false }],
860
+ trigger: { type: "element_visible", value: "[data-tour='search']", delayMs: 500 },
861
+ frequency: { mode: "once", priority: 80 }
862
+ }
863
+ },
864
+ {
865
+ id: "product-update-banner",
866
+ name: "Product update notification",
867
+ description: "A dismissible top banner linking to release notes.",
868
+ kind: "banner",
869
+ category: "announcement",
870
+ config: {
871
+ steps: [{ id: "banner", type: "banner", title: "", body: "<p><strong>Scheduled maintenance</strong> on Saturday 02:00\u201303:00 UTC. <a href='/status'>Read more</a>.</p>", position: "top", placement: "top", buttons: [{ id: "dismiss", label: "Dismiss", action: "dismiss", variant: "ghost" }], showProgress: false }],
872
+ trigger: { type: "page_load" },
873
+ frequency: { mode: "once_per_session", priority: 90 }
874
+ }
875
+ },
876
+ {
877
+ id: "onboarding-checklist",
878
+ name: "User onboarding checklist",
879
+ description: "A persistent checklist with four tasks that complete automatically from events.",
880
+ kind: "checklist",
881
+ category: "onboarding",
882
+ config: {
883
+ steps: [],
884
+ checklist: {
885
+ tasks: [
886
+ { id: "profile", title: "Complete your profile", description: "Add a photo and your role.", completion: "event", completionValue: "profile_completed", action: "url", actionValue: "/settings/profile" },
887
+ { id: "contact", title: "Add your first contact", description: "Import or create a contact.", completion: "event", completionValue: "contact_created", action: "url", actionValue: "/contacts/new" },
888
+ { id: "deal", title: "Create a deal", description: "Track your first opportunity.", completion: "event", completionValue: "deal_created", action: "url", actionValue: "/deals" },
889
+ { id: "tour", title: "Take the product tour", description: "A 60-second walkthrough.", completion: "guide_completed", completionValue: "new-user-onboarding", action: "start_guide", actionValue: "new-user-onboarding", required: false }
890
+ ],
891
+ ordered: false,
892
+ launcherLabel: "Getting started",
893
+ position: "bottom-right",
894
+ completionTitle: "You're all set! \u{1F389}",
895
+ completionBody: "You've completed onboarding. Explore the rest of the app at your own pace.",
896
+ autoOpen: true,
897
+ dismissible: true
898
+ },
899
+ trigger: { type: "page_load" },
900
+ frequency: { mode: "every_time", priority: 200 }
901
+ }
902
+ },
903
+ {
904
+ id: "nps-survey",
905
+ name: "NPS survey",
906
+ description: "Net Promoter Score with a conditional follow-up question.",
907
+ kind: "survey",
908
+ category: "feedback",
909
+ config: {
910
+ steps: [],
911
+ survey: {
912
+ questions: [
913
+ { id: "nps", type: "nps", title: "How likely are you to recommend us to a colleague?", lowLabel: "Not likely", highLabel: "Very likely" },
914
+ { id: "why-low", type: "text", title: "What could we do better?", required: false, placeholder: "Tell us more\u2026", showWhen: { questionId: "nps", op: "lte", value: 6 } },
915
+ { id: "why-high", type: "text", title: "What do you like most?", required: false, placeholder: "Tell us more\u2026", showWhen: { questionId: "nps", op: "gte", value: 9 } }
916
+ ],
917
+ position: "bottom-right"
918
+ },
919
+ trigger: { type: "page_load", delayMs: 5e3 },
920
+ frequency: { mode: "interval", intervalHours: 2160, priority: 150 },
921
+ targeting: { id: "root", op: "and", rules: [{ id: "r1", field: "visitor.returning", op: "is_true" }] }
922
+ }
923
+ }
924
+ ];
925
+ function templateConfig(id) {
926
+ const t = GUIDE_TEMPLATES.find((x) => x.id === id);
927
+ if (!t) return null;
928
+ return { template: t, config: GuideConfigSchema.parse(t.config) };
929
+ }
930
+
931
+ // src/index.ts
932
+ var SDK_API_VERSION = "v1";
933
+ var SHARED_VERSION = "0.1.0";
934
+ // Annotate the CommonJS export names for ESM import in node:
935
+ 0 && (module.exports = {
936
+ ADVANCE_ON,
937
+ AppearanceSchema,
938
+ AuthoringPickSchema,
939
+ BUTTON_ACTIONS,
940
+ ButtonSchema,
941
+ ChecklistConfigSchema,
942
+ ChecklistProgressRequestSchema,
943
+ ChecklistTaskSchema,
944
+ EVENT_NAMES,
945
+ EventBatchSchema,
946
+ FrequencySchema,
947
+ GUIDE_KINDS,
948
+ GUIDE_LIFECYCLE_EVENTS,
949
+ GUIDE_STATUSES,
950
+ GUIDE_TEMPLATES,
951
+ GuideConfigSchema,
952
+ GuideStateUpdateSchema,
953
+ IdentifyRequestSchema,
954
+ MAX_PROPERTY_BYTES,
955
+ PLACEMENTS,
956
+ ProjectThemeSchema,
957
+ PublishedGuideSchema,
958
+ RULE_FIELDS,
959
+ RULE_OPERATORS,
960
+ RuleGroupSchema,
961
+ RuleSchema,
962
+ SDK_API_VERSION,
963
+ SELECTOR_KINDS,
964
+ SENSITIVE_PROPERTY_PATTERN,
965
+ SERVER_ASSISTED_FIELDS,
966
+ SHARED_VERSION,
967
+ STEP_TYPES,
968
+ SURVEY_QUESTION_TYPES,
969
+ SdkEventSchema,
970
+ SelectorStrategySchema,
971
+ StepBranchSchema,
972
+ StepSchema,
973
+ SurveyConfigSchema,
974
+ SurveyQuestionSchema,
975
+ SurveyResponseRequestSchema,
976
+ TRIGGER_TYPES,
977
+ TargetSchema,
978
+ TriggerSchema,
979
+ clamp,
980
+ compare,
981
+ defaultGuideConfig,
982
+ defaultStep,
983
+ describeRule,
984
+ emptyRuleGroup,
985
+ evaluateRule,
986
+ evaluateRules,
987
+ fnv1a,
988
+ getPath,
989
+ isPlainObject,
990
+ isRuleGroup,
991
+ matchUrlPattern,
992
+ randomId,
993
+ rolloutBucket,
994
+ safeRegExp,
995
+ sanitizeProperties,
996
+ slugify,
997
+ stripOrigin,
998
+ templateConfig,
999
+ validateGuideConfig
1000
+ });
1001
+ //# sourceMappingURL=index.cjs.map