@guidelytech/sdk-core 0.15.0 → 0.17.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/README.md +4 -1
- package/dist/{chunk-2E2YUQAG.js → chunk-GFLVZRTS.js} +28 -3
- package/dist/chunk-GFLVZRTS.js.map +1 -0
- package/dist/{types-Bkw8ErmO.d.cts → environment-D63OzdWf.d.cts} +172 -5
- package/dist/{types-Dy_uPu6S.d.ts → environment-DAT4iYvZ.d.ts} +172 -5
- package/dist/headless.cjs +27 -1
- package/dist/headless.cjs.map +1 -1
- package/dist/headless.d.cts +1 -1
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +3 -1
- package/dist/index.cjs +1155 -115
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +71 -10
- package/dist/index.d.ts +71 -10
- package/dist/index.js +1089 -72
- package/dist/index.js.map +1 -1
- package/package.json +7 -6
- package/dist/capture-CgN69Lxq.d.cts +0 -100
- package/dist/capture-Csz721-A.d.ts +0 -100
- package/dist/chunk-2E2YUQAG.js.map +0 -1
- package/dist/chunk-JUSM7J5N.js +0 -376
- package/dist/chunk-JUSM7J5N.js.map +0 -1
- package/dist/chunk-ZAF2KRJA.js +0 -334
- package/dist/chunk-ZAF2KRJA.js.map +0 -1
- package/dist/health-sbP9JwTE.d.cts +0 -90
- package/dist/health-sbP9JwTE.d.ts +0 -90
- package/dist/http-Cb6KYYFv.d.cts +0 -61
- package/dist/http-D047Ngxk.d.ts +0 -61
- package/dist/types-BDlebXrm.d.ts +0 -314
- package/dist/types-Mtlmlj5C.d.cts +0 -314
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ The module is safe to import during SSR (nothing touches `window` at module scop
|
|
|
33
33
|
| Option | Default | Description |
|
|
34
34
|
|---|---|---|
|
|
35
35
|
| `projectId` | — | Environment public key (`pk_dev_…`, `pk_stag_…`, `pk_prod_…`). Required. |
|
|
36
|
-
| `environment` |
|
|
36
|
+
| `environment` | the key's environment | `development`, `staging`, `production` or a key you added (`qa`); exposed to targeting (`environment` field). Read from the loaded config, so you rarely need to set it. |
|
|
37
37
|
| `apiUrl` | `https://api.guidely.app` | Base URL of the Guidely API. Demos use `http://localhost:3000`. |
|
|
38
38
|
| `debug` | `false` | Log trigger decisions and requests with `console.debug("[Guidely]", …)`. |
|
|
39
39
|
| `analytics` | `true` | Send analytics events. Guides still render when `false`. |
|
|
@@ -69,6 +69,8 @@ event, never thrown into the host application.
|
|
|
69
69
|
| `page(url?)` | Records a `page_view` and re-evaluates triggers. Called automatically on SPA navigation (History API patch + `popstate` + `hashchange`); adapters call it explicitly too. |
|
|
70
70
|
| `setConsent("granted" \| "denied" \| "pending")` | Runtime consent changes (anything but `granted` stops and discards a session recording). |
|
|
71
71
|
| `captureException(error, properties?)` | Reports a caught error as an `exception` event (`source: "manual"`, `handled: true`). A no-op when the project has error tracking off; `captureErrors: false` does not affect it. |
|
|
72
|
+
| `captureFeedback({ message, email?, name?, associatedEventId?, tags?, screenshot? })` | Sends a bug report to Monitor → User feedback, linked to the visitor, this session's recording and (unless `associatedEventId` says otherwise) the last error the page reported. `screenshot` is an image `Blob`/`File` (≤ 4 MB). Resolves with `{ id, linked }`. |
|
|
73
|
+
| `showBugReport({ message?, name?, email?, title? }?)` | Opens the built-in "Report a bug" dialog (message, name, email, an image or a capture of the tab), which sends with `captureFeedback`. Loaded on demand. |
|
|
72
74
|
| `startSessionRecording()` / `stopSessionRecording()` | Force recording on for this session (ignores sampling; needs replay enabled on the project) / stop it for the rest of the session. |
|
|
73
75
|
| `isRecording()` / `getSessionId()` | Whether the session is being recorded; the current analytics session id (also the replay id). |
|
|
74
76
|
| `startGuide(idOrKey, { stepId? })` | Starts a guide regardless of targeting/frequency, replacing any active guide. Checklists open their panel. Returns `false` for unknown guides. |
|
|
@@ -81,6 +83,7 @@ event, never thrown into the host application.
|
|
|
81
83
|
| `completeChecklistTask(idOrKey, taskId)` | Manual completion; syncs to `/sdk/checklists/:id/progress`. |
|
|
82
84
|
| `openChecklist(idOrKey)` / `closeChecklist(idOrKey)` | Show/hide the checklist panel (re-renders a dismissed checklist). |
|
|
83
85
|
| `openResourceCenter()` / `closeResourceCenter()` | Toggle the resource center panel. |
|
|
86
|
+
| `openArticle(articleId)` | Open one help article (Scout's Help tab when the messenger is on). Links to `#guidely-article:<id>` anywhere on the page, including guide bodies, do this. |
|
|
84
87
|
| `refresh()` | Re-fetch config now. |
|
|
85
88
|
| `on(event, cb): () => void` / `off(event, cb)` | Subscribe to events (below). |
|
|
86
89
|
| `getContext()` | The `TargetingContext` used for rule evaluation (debugging). |
|
|
@@ -6,6 +6,21 @@ import {
|
|
|
6
6
|
} from "./chunk-XML74K2P.js";
|
|
7
7
|
|
|
8
8
|
// src/queue.ts
|
|
9
|
+
var MAX_PERSISTED_QUEUE_CHARS = 1024 * 1024;
|
|
10
|
+
function fitPersisted(events) {
|
|
11
|
+
let size = JSON.stringify(events).length;
|
|
12
|
+
if (size <= MAX_PERSISTED_QUEUE_CHARS) return events;
|
|
13
|
+
const out = events.slice();
|
|
14
|
+
for (let i = 0; i < out.length && size > MAX_PERSISTED_QUEUE_CHARS; i++) {
|
|
15
|
+
const e = out[i];
|
|
16
|
+
if (!e.detail) continue;
|
|
17
|
+
size -= JSON.stringify(e.detail).length;
|
|
18
|
+
const rest = { ...e };
|
|
19
|
+
delete rest.detail;
|
|
20
|
+
out[i] = rest;
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
9
24
|
var EventQueue = class {
|
|
10
25
|
constructor(opts) {
|
|
11
26
|
__publicField(this, "opts", opts);
|
|
@@ -61,7 +76,7 @@ var EventQueue = class {
|
|
|
61
76
|
this.schedule();
|
|
62
77
|
}
|
|
63
78
|
persist() {
|
|
64
|
-
writeJson(this.opts.store, this.opts.storageKey, this.events);
|
|
79
|
+
writeJson(this.opts.store, this.opts.storageKey, fitPersisted(this.events));
|
|
65
80
|
}
|
|
66
81
|
schedule() {
|
|
67
82
|
if (this.timer || this.retryTimer || this.destroyed) return;
|
|
@@ -318,6 +333,15 @@ function dismissedAfterMerge(local, server) {
|
|
|
318
333
|
return local?.dismissedAt ?? server.dismissedAt ?? null;
|
|
319
334
|
}
|
|
320
335
|
|
|
336
|
+
// src/environment.ts
|
|
337
|
+
function environmentFromKey(key) {
|
|
338
|
+
const prefix = /^pk_([a-z0-9]+)_/.exec(key)?.[1];
|
|
339
|
+
if (!prefix || prefix === "dev") return "development";
|
|
340
|
+
if (prefix === "prod") return "production";
|
|
341
|
+
if (prefix === "stg" || prefix === "stag") return "staging";
|
|
342
|
+
return prefix;
|
|
343
|
+
}
|
|
344
|
+
|
|
321
345
|
export {
|
|
322
346
|
EventQueue,
|
|
323
347
|
Outbox,
|
|
@@ -329,6 +353,7 @@ export {
|
|
|
329
353
|
countsTowardThrottle,
|
|
330
354
|
isThrottleEligible,
|
|
331
355
|
pruneLaunches,
|
|
332
|
-
dismissedAfterMerge
|
|
356
|
+
dismissedAfterMerge,
|
|
357
|
+
environmentFromKey
|
|
333
358
|
};
|
|
334
|
-
//# sourceMappingURL=chunk-
|
|
359
|
+
//# sourceMappingURL=chunk-GFLVZRTS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/queue.ts","../src/outbox.ts","../src/emitter.ts","../src/frequency.ts","../src/environment.ts"],"sourcesContent":["import type { SdkEvent } from \"@guidelytech/shared\";\nimport type { ServerHealth } from \"./health\";\nimport { backoffMs } from \"./http\";\nimport { readJson, writeJson, type KeyValueStore } from \"./storage\";\n\nexport interface EventQueueOptions {\n store: KeyValueStore;\n storageKey: string;\n /** Sends one batch. Resolve `{ ok: true }` on success, `{ ok: false, retry }` otherwise. */\n send: (events: SdkEvent[]) => Promise<{ ok: boolean; retry?: boolean }>;\n /** Synchronous best-effort send used on page hide. */\n beacon?: (events: SdkEvent[]) => boolean;\n /** Should events be flushed right now? (analytics enabled + consent granted). */\n enabled: () => boolean;\n /** API reachability: sends wait while its circuit is open, and beaconed events are only forgotten while it is reachable. */\n health?: ServerHealth;\n flushIntervalMs?: number;\n maxQueue?: number;\n batchSize?: number;\n maxAttempts?: number;\n onError?: (err: unknown) => void;\n}\n\n/** Most of the queue persisted to storage (localStorage holds ~5 MB per origin, shared with the app). */\nexport const MAX_PERSISTED_QUEUE_CHARS = 1024 * 1024;\n\n/**\n * The events as persisted: when error details (up to 64 KB each) push the queue past\n * {@link MAX_PERSISTED_QUEUE_CHARS}, the oldest events lose their `detail` (the in-memory queue keeps it).\n */\nexport function fitPersisted(events: SdkEvent[]): SdkEvent[] {\n let size = JSON.stringify(events).length;\n if (size <= MAX_PERSISTED_QUEUE_CHARS) return events;\n const out = events.slice();\n for (let i = 0; i < out.length && size > MAX_PERSISTED_QUEUE_CHARS; i++) {\n const e = out[i]!;\n if (!e.detail) continue;\n size -= JSON.stringify(e.detail).length;\n const rest = { ...e };\n delete rest.detail;\n out[i] = rest;\n }\n return out;\n}\n\n/**\n * Bounded, persisted event queue. Batches ≤50 events every 2s (or on demand), dedupes by\n * `eventId`, and keeps failed events (also across page loads) until the server takes them:\n * retries back off 1s→60s, wait while the API's circuit is open, and start again as soon as it\n * recovers. Drops the oldest events once the queue exceeds 500.\n */\nexport class EventQueue {\n private events: SdkEvent[] = [];\n private timer: ReturnType<typeof setTimeout> | null = null;\n private retryTimer: ReturnType<typeof setTimeout> | null = null;\n private inflight = false;\n private inflightIds = new Set<string>();\n private attempt = 0;\n private destroyed = false;\n private offRecover: (() => void) | null = null;\n private readonly flushIntervalMs: number;\n private readonly maxQueue: number;\n private readonly batchSize: number;\n private readonly maxAttempts: number;\n\n constructor(private readonly opts: EventQueueOptions) {\n this.flushIntervalMs = opts.flushIntervalMs ?? 2000;\n this.maxQueue = opts.maxQueue ?? 500;\n this.batchSize = opts.batchSize ?? 50;\n this.maxAttempts = opts.maxAttempts ?? 5;\n const persisted = readJson<SdkEvent[]>(opts.store, opts.storageKey, []);\n if (Array.isArray(persisted)) this.events = persisted.filter((e) => e && typeof e.eventId === \"string\").slice(-this.maxQueue);\n // Events left over from a previous page load are flushed on the next tick.\n if (this.events.length) this.schedule();\n this.offRecover = opts.health?.onRecover(() => this.retryNow()) ?? null;\n }\n\n /** The API is back: drop any pending backoff and send what is queued. */\n private retryNow(): void {\n if (this.destroyed) return;\n if (this.retryTimer) clearTimeout(this.retryTimer);\n this.retryTimer = null;\n this.attempt = 0;\n void this.flush();\n }\n\n private retryIn(ms: number): void {\n if (this.retryTimer || this.destroyed) return;\n this.retryTimer = setTimeout(() => {\n this.retryTimer = null;\n void this.flush();\n }, ms);\n }\n\n get size(): number {\n return this.events.length;\n }\n\n /** Pending events (read-only copy, for debugging). */\n peek(): SdkEvent[] {\n return this.events.slice();\n }\n\n enqueue(event: SdkEvent): void {\n if (this.destroyed) return;\n if (this.events.some((e) => e.eventId === event.eventId)) return;\n this.events.push(event);\n if (this.events.length > this.maxQueue) this.events.splice(0, this.events.length - this.maxQueue);\n this.persist();\n this.schedule();\n }\n\n private persist(): void {\n writeJson(this.opts.store, this.opts.storageKey, fitPersisted(this.events));\n }\n\n private schedule(): void {\n if (this.timer || this.retryTimer || this.destroyed) return;\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.flush();\n }, this.flushIntervalMs);\n }\n\n /** Sends queued events now (one batch at a time). Safe to call at any time. */\n async flush(): Promise<void> {\n if (this.destroyed || this.inflight || !this.events.length || !this.opts.enabled()) return;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n const wait = this.opts.health?.waitMs() ?? 0;\n if (wait > 0) return this.retryIn(wait);\n this.inflight = true;\n const batch = this.events.slice(0, this.batchSize);\n this.inflightIds = new Set(batch.map((e) => e.eventId));\n let ok = false;\n let retry = true;\n try {\n const res = await this.opts.send(batch);\n ok = res.ok;\n retry = res.retry ?? !res.ok;\n } catch (err) {\n this.opts.onError?.(err);\n }\n this.inflight = false;\n this.inflightIds.clear();\n if (this.destroyed) return;\n if (ok || !retry) {\n const ids = new Set(batch.map((e) => e.eventId));\n this.events = this.events.filter((e) => !ids.has(e.eventId));\n this.attempt = 0;\n this.persist();\n if (this.events.length) this.schedule();\n return;\n }\n // Fast retries first, then about once a minute until the server takes the events (or the\n // API's circuit says to wait longer); a recovery or the next page load sends them sooner.\n this.attempt = Math.min(this.attempt + 1, this.maxAttempts);\n const delay = this.attempt >= this.maxAttempts ? 60_000 : backoffMs(this.attempt - 1);\n this.retryIn(Math.max(delay, this.opts.health?.waitMs() ?? 0));\n }\n\n /** Best-effort synchronous flush on unload using `sendBeacon`. */\n flushSync(): void {\n if (this.destroyed || !this.opts.enabled() || !this.opts.beacon) return;\n // Events already in flight (fetch with keepalive) survive unload; only beacon the rest to avoid duplicate batches.\n const batch = this.events.filter((e) => !this.inflightIds.has(e.eventId)).slice(0, this.batchSize);\n if (!batch.length) return;\n // A beacon that was queued may still never arrive (the server is down), and nothing reports\n // back. Only forget its events while the API is answering; otherwise they stay persisted and the\n // next page load sends them again (the server drops events it already has, by eventId).\n if (this.opts.beacon(batch) && (this.opts.health?.reachable ?? true)) {\n const ids = new Set(batch.map((e) => e.eventId));\n this.events = this.events.filter((e) => !ids.has(e.eventId));\n }\n this.persist();\n }\n\n clear(): void {\n this.events = [];\n this.persist();\n }\n\n destroy(): void {\n this.destroyed = true;\n this.offRecover?.();\n if (this.timer) clearTimeout(this.timer);\n if (this.retryTimer) clearTimeout(this.retryTimer);\n this.timer = null;\n this.retryTimer = null;\n this.persist();\n }\n}\n","import type { ServerHealth } from \"./health\";\nimport { backoffMs, type Api } from \"./http\";\nimport { readJson, writeJson, type KeyValueStore } from \"./storage\";\n\n/** One request waiting to reach the server. */\ninterface OutboxItem {\n /** Same key → the newer request replaces the older one (e.g. a checklist's latest progress). */\n key: string;\n path: string;\n body: unknown;\n createdAt: number;\n}\n\nexport interface OutboxOptions {\n api: Api;\n store: KeyValueStore;\n storageKey: string;\n health?: ServerHealth;\n maxItems?: number;\n maxAgeMs?: number;\n onError?: (err: unknown) => void;\n}\n\n/**\n * Persisted queue for writes that must not be lost when the API is down: survey answers, guide\n * completions and checklist progress. Each request is kept (also across page loads) until the\n * server answers it; 5xx, 429 and network errors are retried with backoff, waiting while the API's\n * circuit is open. Every request is idempotent on the server (survey answers dedupe by\n * `responseId`, progress merges), so sending one twice is harmless. Bounded to 50 requests and 7 days.\n */\nexport class Outbox {\n private items: OutboxItem[];\n private sending = false;\n private attempt = 0;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private destroyed = false;\n private readonly offRecover: (() => void) | null;\n private readonly maxItems: number;\n private readonly maxAgeMs: number;\n\n constructor(private readonly opts: OutboxOptions) {\n this.maxItems = opts.maxItems ?? 50;\n this.maxAgeMs = opts.maxAgeMs ?? 7 * 24 * 60 * 60 * 1000;\n const stored = readJson<OutboxItem[]>(opts.store, opts.storageKey, []);\n const cutoff = Date.now() - this.maxAgeMs;\n this.items = Array.isArray(stored) ? stored.filter((i) => i && typeof i.path === \"string\" && typeof i.key === \"string\" && i.createdAt > cutoff).slice(-this.maxItems) : [];\n this.offRecover = opts.health?.onRecover(() => {\n this.attempt = 0;\n this.retryIn(0);\n }) ?? null;\n if (this.items.length) this.retryIn(0);\n }\n\n get size(): number {\n return this.items.length;\n }\n\n /** Queues `POST path` and sends it now (or as soon as the API is reachable). */\n add(key: string, path: string, body: unknown): void {\n if (this.destroyed) return;\n this.items = this.items.filter((i) => i.key !== key);\n this.items.push({ key, path, body, createdAt: Date.now() });\n if (this.items.length > this.maxItems) this.items.splice(0, this.items.length - this.maxItems);\n this.persist();\n void this.flush();\n }\n\n private persist(): void {\n writeJson(this.opts.store, this.opts.storageKey, this.items);\n }\n\n private retryIn(ms: number): void {\n if (this.destroyed) return;\n if (this.timer) clearTimeout(this.timer);\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.flush();\n }, ms);\n }\n\n /** Sends queued requests in order, one at a time. */\n async flush(): Promise<void> {\n if (this.destroyed || this.sending || !this.items.length) return;\n const wait = this.opts.health?.waitMs() ?? 0;\n if (wait > 0) return this.retryIn(wait);\n this.sending = true;\n try {\n while (this.items.length && !this.destroyed) {\n const item = this.items[0] as OutboxItem;\n let retry = false;\n try {\n const res = await this.opts.api.post(item.path, item.body);\n retry = !res.ok && (res.status === 429 || res.status >= 500);\n } catch (err) {\n retry = true;\n this.opts.onError?.(err);\n }\n if (retry) {\n this.attempt += 1;\n this.retryIn(Math.max(backoffMs(this.attempt - 1, 1000, 60_000), this.opts.health?.waitMs() ?? 0));\n return;\n }\n // Answered (or refused for good, e.g. the survey was deleted): done with it. A newer request\n // under the same key may have replaced it while this one was in flight; keep that one.\n this.attempt = 0;\n this.items = this.items.filter((i) => i !== item);\n this.persist();\n }\n } finally {\n this.sending = false;\n }\n }\n\n destroy(): void {\n this.destroyed = true;\n this.offRecover?.();\n if (this.timer) clearTimeout(this.timer);\n this.timer = null;\n }\n}\n","type AnyFn = (...args: any[]) => void;\n\n/** Tiny typed event emitter. Listener errors are reported, never thrown into the caller. */\nexport class Emitter<Events extends { [K in keyof Events]: AnyFn }> {\n private listeners = new Map<keyof Events, Set<AnyFn>>();\n constructor(private readonly onError?: (err: unknown) => void) {}\n\n on<K extends keyof Events>(event: K, cb: Events[K]): () => void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(cb);\n return () => this.off(event, cb);\n }\n\n off<K extends keyof Events>(event: K, cb: Events[K]): void {\n this.listeners.get(event)?.delete(cb);\n }\n\n emit<K extends keyof Events>(event: K, ...args: Parameters<Events[K]>): void {\n const set = this.listeners.get(event);\n if (!set) return;\n for (const cb of Array.from(set)) {\n try {\n cb(...args);\n } catch (err) {\n this.onError?.(err);\n }\n }\n }\n\n clear(): void {\n this.listeners.clear();\n }\n}\n","import type { FrequencyConfig, GuideThrottlingSettings, PublishedGuide } from \"@guidelytech/shared\";\n\n/** Per-guide state kept locally and merged with the server's `IdentifyResponse.guideState`. */\nexport interface GuideLocalState {\n impressions: number;\n completedAt?: string | null;\n dismissedAt?: string | null;\n lastShownAt?: string | null;\n /** Last step reached in a multi-step flow (local only), so the messenger can offer to resume it. */\n lastStepId?: string | null;\n}\n\nconst HOUR = 3600 * 1000;\n\n/**\n * Pure frequency evaluation. `shownThisSession` comes from session storage.\n * - `once`: a single impression ever (unless `reshowAfterDismiss` and it was only dismissed).\n * - `once_per_session`: at most once per browser session.\n * - `every_time`: on every trigger (still blocked by completion/dismissal rules below).\n * - `interval`: at most once every `intervalHours`.\n * `maxImpressions` caps total impressions; dismissal blocks re-showing unless `reshowAfterDismiss`\n * (then `cooldownHours` must have elapsed); completion blocks unless mode is `every_time`.\n */\nexport function isFrequencyEligible(freq: FrequencyConfig, state: GuideLocalState | undefined, shownThisSession: boolean, now: number = Date.now()): boolean {\n const s = state ?? { impressions: 0 };\n if (freq.maxImpressions != null && s.impressions >= freq.maxImpressions) return false;\n if (s.completedAt && freq.mode !== \"every_time\") return false;\n if (s.dismissedAt) {\n if (!freq.reshowAfterDismiss) return false;\n if (freq.cooldownHours > 0 && now - Date.parse(s.dismissedAt) < freq.cooldownHours * HOUR) return false;\n }\n switch (freq.mode) {\n case \"once\":\n return s.impressions === 0 || (!!s.dismissedAt && freq.reshowAfterDismiss);\n case \"once_per_session\":\n return !shownThisSession;\n case \"interval\": {\n if (!s.lastShownAt) return true;\n const hours = freq.intervalHours ?? 24;\n return now - Date.parse(s.lastShownAt) >= hours * HOUR;\n }\n case \"every_time\":\n default:\n return true;\n }\n}\n\n/**\n * Exclusive guides (flows, modals, tooltips, announcements, surveys) run one at a time.\n * Banners, checklists and hotspot-only tooltips can coexist with an active flow.\n */\nexport function isExclusiveGuide(guide: PublishedGuide): boolean {\n if (guide.kind === \"banner\" || guide.kind === \"checklist\") return false;\n const steps = guide.config.steps;\n if (steps.length > 0 && steps.every((s) => s.type === \"hotspot\" || s.type === \"banner\")) return false;\n return true;\n}\n\n/** Is `a` mutually exclusive with `b` (by id or key, in either direction)? */\nexport function areMutuallyExclusive(a: PublishedGuide, b: PublishedGuide): boolean {\n const la = a.config.frequency.mutuallyExclusiveWith;\n const lb = b.config.frequency.mutuallyExclusiveWith;\n return la.includes(b.id) || la.includes(b.key) || lb.includes(a.id) || lb.includes(a.key);\n}\n\n/**\n * Scheduling window: the guide's `schedule` (any kind) and, for surveys, `survey.startAt` / `endAt`.\n * Unparsable dates are ignored.\n */\nexport function isWithinSchedule(guide: Pick<PublishedGuide, \"config\">, now: number = Date.now()): boolean {\n for (const w of [guide.config.schedule, guide.config.survey]) {\n if (!w) continue;\n if (w.startAt && now < Date.parse(w.startAt)) return false;\n if (w.endAt && now > Date.parse(w.endAt)) return false;\n }\n return true;\n}\n\nconst MINUTE = 60 * 1000;\n\n/**\n * Does showing this guide automatically count toward (and get limited by) the project's guide\n * throttling? Only exclusive guides (flows, modals, tooltips, announcements, surveys) interrupt;\n * banners, checklists and hotspot-only guides never count, nor does a guide set to ignore throttling.\n * Guides started by the visitor or the app (`startGuide`, buttons, Help) are never throttled.\n */\nexport function countsTowardThrottle(guide: PublishedGuide): boolean {\n return isExclusiveGuide(guide) && !guide.config.frequency.ignoreThrottling;\n}\n\n/**\n * Pure throttling check. `launches` = ISO times of this visitor's earlier automatic launches of\n * counted guides (any order). Disabled or missing settings never throttle.\n */\nexport function isThrottleEligible(settings: GuideThrottlingSettings | null | undefined, launches: readonly string[], now: number = Date.now()): boolean {\n if (!settings || !settings.enabled) return true;\n const times = launches.map((l) => Date.parse(l)).filter((t) => !Number.isNaN(t));\n const windowMs = Math.max(1, settings.windowHours) * HOUR;\n if (times.filter((t) => now - t < windowMs).length >= Math.max(1, settings.maxGuides)) return false;\n if (settings.minGapMinutes > 0 && times.some((t) => now - t < settings.minGapMinutes * MINUTE)) return false;\n return true;\n}\n\n/** Launch times worth keeping: those still inside the window or the minimum gap (at most 100). */\nexport function pruneLaunches(launches: readonly string[], settings: GuideThrottlingSettings | null | undefined, now: number = Date.now()): string[] {\n const keepMs = Math.max(settings?.windowHours ?? 24, 1) * HOUR + (settings?.minGapMinutes ?? 0) * MINUTE;\n return launches.filter((l) => { const t = Date.parse(l); return !Number.isNaN(t) && now - t < keepMs; }).slice(-100);\n}\n\n/**\n * A checklist's dismissal after merging server state. A local dismissal normally wins (it may not have\n * synced yet), but when the server reports a reset this device hasn't applied, the server's value wins:\n * null, or a dismissal made on another device after the reset. Compared as tokens, not times, so a\n * skewed device clock can't keep a reset dismissal.\n */\nexport function dismissedAfterMerge(local: { dismissedAt?: string | null; dismissResetAt?: string | null } | undefined, server: { dismissedAt?: string | null; dismissResetAt?: string | null }): string | null {\n if (server.dismissResetAt && server.dismissResetAt !== local?.dismissResetAt) return server.dismissedAt ?? null;\n return local?.dismissedAt ?? server.dismissedAt ?? null;\n}\n","import type { GuidelyEnvironment } from \"./types\";\n\n/**\n * Best guess at an environment from its public key, used until the config (which names it) loads.\n * `pk_prod_…` → production, `pk_stg_…` → staging, `pk_dev_…` → development; a custom environment's key\n * starts with its own key without dashes (`pk_qa_…` → `qa`). Anything else is development.\n */\nexport function environmentFromKey(key: string): GuidelyEnvironment {\n const prefix = /^pk_([a-z0-9]+)_/.exec(key)?.[1];\n if (!prefix || prefix === \"dev\") return \"development\";\n if (prefix === \"prod\") return \"production\";\n if (prefix === \"stg\" || prefix === \"stag\") return \"staging\";\n return prefix;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SdkEvent, PublishedGuide, FrequencyConfig, GuideThrottlingSettings, AutocaptureSettings, NetworkRequest, ProjectTheme, SdkConfigResponse, ChecklistTask, MessengerMessage, MessengerConversation } from '@guidelytech/shared';
|
|
2
2
|
import { K as KeyValueStore, S as ServerHealth, c as StorageKind } from './health-DjZ2gwYa.cjs';
|
|
3
3
|
import { FlagsClientOptions } from '@guidelytech/flags';
|
|
4
|
-
import { AgentTurnInput } from '@guidelytech/shared/runtime';
|
|
4
|
+
import { AgentTurnInput, ErrorLevelName, ExceptionProperties, ExceptionDetail, ErrorFilterPattern, Breadcrumb } from '@guidelytech/shared/runtime';
|
|
5
5
|
|
|
6
6
|
interface EventQueueOptions {
|
|
7
7
|
store: KeyValueStore;
|
|
@@ -129,7 +129,152 @@ declare function dismissedAfterMerge(local: {
|
|
|
129
129
|
/** Last say over each turn before it leaves the browser: return a changed copy, or null to drop it. */
|
|
130
130
|
type AgentTurnRedactor = (turn: AgentTurnInput) => AgentTurnInput | null | undefined;
|
|
131
131
|
|
|
132
|
-
type
|
|
132
|
+
type Measurements = Record<string, {
|
|
133
|
+
value: number;
|
|
134
|
+
unit: string;
|
|
135
|
+
}>;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Performance tracing: Sentry-protocol transaction events built by the SDK itself and sent to
|
|
139
|
+
* `POST /api/v1/sdk/traces`.
|
|
140
|
+
*
|
|
141
|
+
* - **pageload**: from navigation start (`performance.timeOrigin`) until the page is idle (no
|
|
142
|
+
* request in flight for `idleMs` after the load event; at most `finalMs`), or hidden. Child spans
|
|
143
|
+
* come from Navigation Timing (op `browser`), Resource Timing (`resource.*`), long tasks
|
|
144
|
+
* (`ui.long-task`), user timing marks / measures and fetch / XHR requests (`http.client`). It
|
|
145
|
+
* carries the Web Vitals known by then (`lcp`, `fcp`, `ttfb`) as `measurements`.
|
|
146
|
+
* - **navigation**: each SPA route change (a new path) starts one, ending on idle the same way; the
|
|
147
|
+
* open transaction ends when the next one starts.
|
|
148
|
+
* - **ui.webvitals**: CLS and INP are only final when the page is hidden, after the pageload has been
|
|
149
|
+
* sent. On the first hide we send a zero-length, span-less `ui.webvitals` transaction named like
|
|
150
|
+
* the pageload, in its trace, with `measurements` `{ cls, inp? }`. (Sentry keeps the pageload open
|
|
151
|
+
* or sends standalone spans instead; one late event per document is simpler and the server rolls
|
|
152
|
+
* `ui.webvitals` up as that page's vitals rather than counting it as a transaction.)
|
|
153
|
+
*
|
|
154
|
+
* Sampling is decided once per page load from the project's `performance` setting (off unless the
|
|
155
|
+
* project turns it on) and the `tracesSampleRate` option, which can only lower it. Until the config is
|
|
156
|
+
* known, transactions are measured and held; unsampled page loads still propagate trace headers
|
|
157
|
+
* (sampled flag 0) so backend traces stay connected. Requests to the Guidely API are never traced.
|
|
158
|
+
*/
|
|
159
|
+
|
|
160
|
+
interface TracingOptions {
|
|
161
|
+
/** Fraction of page loads traced (0–1, default 1). Can only lower the project's sample rate. */
|
|
162
|
+
tracesSampleRate?: number;
|
|
163
|
+
/**
|
|
164
|
+
* URLs that get `sentry-trace` / `baggage` / `traceparent` headers: a string matches as a
|
|
165
|
+
* substring, a RegExp when it matches. Default: same-origin requests only.
|
|
166
|
+
*/
|
|
167
|
+
tracePropagationTargets?: Array<string | RegExp>;
|
|
168
|
+
}
|
|
169
|
+
interface TraceSpan {
|
|
170
|
+
span_id: string;
|
|
171
|
+
parent_span_id: string;
|
|
172
|
+
trace_id: string;
|
|
173
|
+
op: string;
|
|
174
|
+
description?: string;
|
|
175
|
+
start_timestamp: number;
|
|
176
|
+
timestamp: number;
|
|
177
|
+
status?: string;
|
|
178
|
+
data?: Record<string, unknown>;
|
|
179
|
+
origin?: string;
|
|
180
|
+
}
|
|
181
|
+
interface TransactionEvent {
|
|
182
|
+
type: "transaction";
|
|
183
|
+
event_id: string;
|
|
184
|
+
platform: "javascript";
|
|
185
|
+
transaction: string;
|
|
186
|
+
transaction_info: {
|
|
187
|
+
source: "url" | "route";
|
|
188
|
+
};
|
|
189
|
+
start_timestamp: number;
|
|
190
|
+
timestamp: number;
|
|
191
|
+
release?: string;
|
|
192
|
+
environment: string;
|
|
193
|
+
contexts: {
|
|
194
|
+
trace: {
|
|
195
|
+
trace_id: string;
|
|
196
|
+
span_id: string;
|
|
197
|
+
parent_span_id?: string;
|
|
198
|
+
op: string;
|
|
199
|
+
status: string;
|
|
200
|
+
origin: string;
|
|
201
|
+
};
|
|
202
|
+
browser?: {
|
|
203
|
+
name: string;
|
|
204
|
+
version?: string;
|
|
205
|
+
};
|
|
206
|
+
guidely: {
|
|
207
|
+
anonymousId: string;
|
|
208
|
+
sessionId: string;
|
|
209
|
+
url?: string;
|
|
210
|
+
};
|
|
211
|
+
};
|
|
212
|
+
spans: TraceSpan[];
|
|
213
|
+
measurements?: Measurements;
|
|
214
|
+
request?: {
|
|
215
|
+
url: string;
|
|
216
|
+
};
|
|
217
|
+
user?: {
|
|
218
|
+
id: string;
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** What `beforeSend` gets (and may change): the indexed properties and the detail. */
|
|
223
|
+
interface ErrorEvent {
|
|
224
|
+
properties: ExceptionProperties & Record<string, unknown>;
|
|
225
|
+
detail: ExceptionDetail;
|
|
226
|
+
}
|
|
227
|
+
interface BreadcrumbHint {
|
|
228
|
+
/** The DOM event, console arguments, Response or XMLHttpRequest the breadcrumb came from. */
|
|
229
|
+
event?: unknown;
|
|
230
|
+
}
|
|
231
|
+
/** `createGuidely({ errors })`. */
|
|
232
|
+
interface ErrorMonitoringOptions {
|
|
233
|
+
/** Share of errors sent, 0–1 (default 1). */
|
|
234
|
+
sampleRate?: number;
|
|
235
|
+
/** Errors whose `"<type>: <message>"` or message contains a string / matches a RegExp are not sent. */
|
|
236
|
+
ignoreErrors?: ErrorFilterPattern[];
|
|
237
|
+
/** Errors thrown from a matching script URL (innermost frame) are not sent, e.g. `[/extensions\//]`. */
|
|
238
|
+
denyUrls?: ErrorFilterPattern[];
|
|
239
|
+
/** When set, only errors thrown from a matching script URL are sent. */
|
|
240
|
+
allowUrls?: ErrorFilterPattern[];
|
|
241
|
+
/** Last say over each error: return it (changed or not), or null to drop it. */
|
|
242
|
+
beforeSend?: (event: ErrorEvent) => ErrorEvent | null;
|
|
243
|
+
/** Last say over each breadcrumb: return it (changed or not), or null to drop it. */
|
|
244
|
+
beforeBreadcrumb?: (crumb: Breadcrumb, hint?: BreadcrumbHint) => Breadcrumb | null;
|
|
245
|
+
/** Automatic breadcrumbs (all on by default); `false` turns them all off. `console: "all"` also records `console.log`/`info`. */
|
|
246
|
+
breadcrumbs?: false | {
|
|
247
|
+
clicks?: boolean;
|
|
248
|
+
navigation?: boolean;
|
|
249
|
+
console?: boolean | "all";
|
|
250
|
+
http?: boolean;
|
|
251
|
+
guides?: boolean;
|
|
252
|
+
};
|
|
253
|
+
/** Breadcrumbs kept (default and maximum 100). */
|
|
254
|
+
maxBreadcrumbs?: number;
|
|
255
|
+
}
|
|
256
|
+
/** The second argument of `captureException`; any other key becomes an event property. */
|
|
257
|
+
interface CaptureContext {
|
|
258
|
+
tags?: Record<string, string | number | boolean>;
|
|
259
|
+
extra?: Record<string, unknown>;
|
|
260
|
+
level?: ErrorLevelName;
|
|
261
|
+
/** `["{{ default }}", "checkout"]` splits an issue, `["checkout-timeout"]` merges errors into one. */
|
|
262
|
+
fingerprint?: string[];
|
|
263
|
+
contexts?: Record<string, Record<string, unknown> | null>;
|
|
264
|
+
user?: {
|
|
265
|
+
id?: string;
|
|
266
|
+
email?: string;
|
|
267
|
+
username?: string;
|
|
268
|
+
};
|
|
269
|
+
/** How the error was caught, for integrations: `{ type: "react.errorboundary", handled: false }` (default `generic`, handled). */
|
|
270
|
+
mechanism?: {
|
|
271
|
+
type: string;
|
|
272
|
+
handled?: boolean;
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** `development`, `staging`, `production` or the key of an environment you added (`qa`, `uat`…). */
|
|
277
|
+
type GuidelyEnvironment = "development" | "staging" | "production" | (string & {});
|
|
133
278
|
type ConsentState = "granted" | "denied" | "pending";
|
|
134
279
|
interface GuidelyAccount {
|
|
135
280
|
id: string;
|
|
@@ -143,6 +288,7 @@ interface GuidelyUser {
|
|
|
143
288
|
interface GuidelyOptions {
|
|
144
289
|
/** Environment public key, e.g. `pk_dev_…`. Not a secret. */
|
|
145
290
|
projectId: string;
|
|
291
|
+
/** Used for guide and flag targeting. Defaults to the environment the key belongs to (named in the loaded config). */
|
|
146
292
|
environment?: GuidelyEnvironment;
|
|
147
293
|
/** Base URL of the Guidely API (default `https://api.guidely.app`; use `http://localhost:3000` locally). */
|
|
148
294
|
apiUrl?: string;
|
|
@@ -169,8 +315,10 @@ interface GuidelyOptions {
|
|
|
169
315
|
/** App version or commit SHA (≤100 chars), sent with every `exception` to pick the matching uploaded source maps. */
|
|
170
316
|
release?: string;
|
|
171
317
|
/**
|
|
172
|
-
* Version of
|
|
173
|
-
*
|
|
318
|
+
* Version of your app (e.g. `"2.4.1"`, a build number, or an Electron app's `app.getVersion()`). Sent
|
|
319
|
+
* with every event and recording so analytics, errors and replays can be filtered and broken down by
|
|
320
|
+
* version (Insights → App versions), and used by **App version** targeting rules and as the
|
|
321
|
+
* `appVersion` flag attribute. With the script tag: `data-app-version="2.4.1"`.
|
|
174
322
|
*/
|
|
175
323
|
appVersion?: string;
|
|
176
324
|
/**
|
|
@@ -181,6 +329,11 @@ interface GuidelyOptions {
|
|
|
181
329
|
pageUrl?: (href: string) => string;
|
|
182
330
|
/** `false` stops uncaught errors / unhandled rejections from being reported (`captureException` still works). */
|
|
183
331
|
captureErrors?: boolean;
|
|
332
|
+
/**
|
|
333
|
+
* Error monitoring options, as in Sentry SDKs: `sampleRate`, `ignoreErrors`, `denyUrls` / `allowUrls`,
|
|
334
|
+
* `beforeSend`, `beforeBreadcrumb` and which automatic breadcrumbs to record (docs/error-monitoring/sdks.md).
|
|
335
|
+
*/
|
|
336
|
+
errors?: ErrorMonitoringOptions;
|
|
184
337
|
/**
|
|
185
338
|
* `false` never records sessions on this installation. An object can only make masking stricter:
|
|
186
339
|
* `maskAllText` masks every text node, `blockSelectors` replaces matching elements with a placeholder.
|
|
@@ -223,6 +376,13 @@ interface GuidelyOptions {
|
|
|
223
376
|
agents?: {
|
|
224
377
|
redact?: AgentTurnRedactor;
|
|
225
378
|
};
|
|
379
|
+
/**
|
|
380
|
+
* Performance tracing (pageload / navigation transactions with Web Vitals, resource, long-task and
|
|
381
|
+
* fetch/XHR spans) runs when the project turns it on. `false` turns it off for this installation;
|
|
382
|
+
* an object can lower the sample rate (`tracesSampleRate`, 0–1) and choose which URLs get trace
|
|
383
|
+
* headers (`tracePropagationTargets`; default: same-origin requests only).
|
|
384
|
+
*/
|
|
385
|
+
tracing?: boolean | TracingOptions;
|
|
226
386
|
}
|
|
227
387
|
interface ChecklistProgressState {
|
|
228
388
|
completedTaskIds: string[];
|
|
@@ -311,4 +471,11 @@ interface GuidelyEvents {
|
|
|
311
471
|
}
|
|
312
472
|
type GuidelyEventName = keyof GuidelyEvents;
|
|
313
473
|
|
|
314
|
-
|
|
474
|
+
/**
|
|
475
|
+
* Best guess at an environment from its public key, used until the config (which names it) loads.
|
|
476
|
+
* `pk_prod_…` → production, `pk_stg_…` → staging, `pk_dev_…` → development; a custom environment's key
|
|
477
|
+
* starts with its own key without dashes (`pk_qa_…` → `qa`). Anything else is development.
|
|
478
|
+
*/
|
|
479
|
+
declare function environmentFromKey(key: string): GuidelyEnvironment;
|
|
480
|
+
|
|
481
|
+
export { type AgentTurnRedactor as A, type BreadcrumbHint as B, type ChecklistProgressState as C, EventQueue as E, type GuideLocalState as G, type PersistedState as P, type SessionState as S, type TraceSpan as T, type ConsentState as a, type EventQueueOptions as b, type GuidelyAccount as c, type GuidelyEnvironment as d, type GuidelyUser as e, areMutuallyExclusive as f, countsTowardThrottle as g, dismissedAfterMerge as h, environmentFromKey as i, isExclusiveGuide as j, isFrequencyEligible as k, isThrottleEligible as l, isWithinSchedule as m, type GuidelyOptions as n, type CaptureContext as o, pruneLaunches as p, type GuidelyEvents as q, type ErrorEvent as r, type ErrorMonitoringOptions as s, type GuideStepInfo as t, type GuidelyEventName as u, type TracingOptions as v, type TransactionEvent as w };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SdkEvent, PublishedGuide, FrequencyConfig, GuideThrottlingSettings, AutocaptureSettings, NetworkRequest, ProjectTheme, SdkConfigResponse, ChecklistTask, MessengerMessage, MessengerConversation } from '@guidelytech/shared';
|
|
2
2
|
import { K as KeyValueStore, S as ServerHealth, c as StorageKind } from './health-DjZ2gwYa.js';
|
|
3
3
|
import { FlagsClientOptions } from '@guidelytech/flags';
|
|
4
|
-
import { AgentTurnInput } from '@guidelytech/shared/runtime';
|
|
4
|
+
import { AgentTurnInput, ErrorLevelName, ExceptionProperties, ExceptionDetail, ErrorFilterPattern, Breadcrumb } from '@guidelytech/shared/runtime';
|
|
5
5
|
|
|
6
6
|
interface EventQueueOptions {
|
|
7
7
|
store: KeyValueStore;
|
|
@@ -129,7 +129,152 @@ declare function dismissedAfterMerge(local: {
|
|
|
129
129
|
/** Last say over each turn before it leaves the browser: return a changed copy, or null to drop it. */
|
|
130
130
|
type AgentTurnRedactor = (turn: AgentTurnInput) => AgentTurnInput | null | undefined;
|
|
131
131
|
|
|
132
|
-
type
|
|
132
|
+
type Measurements = Record<string, {
|
|
133
|
+
value: number;
|
|
134
|
+
unit: string;
|
|
135
|
+
}>;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Performance tracing: Sentry-protocol transaction events built by the SDK itself and sent to
|
|
139
|
+
* `POST /api/v1/sdk/traces`.
|
|
140
|
+
*
|
|
141
|
+
* - **pageload**: from navigation start (`performance.timeOrigin`) until the page is idle (no
|
|
142
|
+
* request in flight for `idleMs` after the load event; at most `finalMs`), or hidden. Child spans
|
|
143
|
+
* come from Navigation Timing (op `browser`), Resource Timing (`resource.*`), long tasks
|
|
144
|
+
* (`ui.long-task`), user timing marks / measures and fetch / XHR requests (`http.client`). It
|
|
145
|
+
* carries the Web Vitals known by then (`lcp`, `fcp`, `ttfb`) as `measurements`.
|
|
146
|
+
* - **navigation**: each SPA route change (a new path) starts one, ending on idle the same way; the
|
|
147
|
+
* open transaction ends when the next one starts.
|
|
148
|
+
* - **ui.webvitals**: CLS and INP are only final when the page is hidden, after the pageload has been
|
|
149
|
+
* sent. On the first hide we send a zero-length, span-less `ui.webvitals` transaction named like
|
|
150
|
+
* the pageload, in its trace, with `measurements` `{ cls, inp? }`. (Sentry keeps the pageload open
|
|
151
|
+
* or sends standalone spans instead; one late event per document is simpler and the server rolls
|
|
152
|
+
* `ui.webvitals` up as that page's vitals rather than counting it as a transaction.)
|
|
153
|
+
*
|
|
154
|
+
* Sampling is decided once per page load from the project's `performance` setting (off unless the
|
|
155
|
+
* project turns it on) and the `tracesSampleRate` option, which can only lower it. Until the config is
|
|
156
|
+
* known, transactions are measured and held; unsampled page loads still propagate trace headers
|
|
157
|
+
* (sampled flag 0) so backend traces stay connected. Requests to the Guidely API are never traced.
|
|
158
|
+
*/
|
|
159
|
+
|
|
160
|
+
interface TracingOptions {
|
|
161
|
+
/** Fraction of page loads traced (0–1, default 1). Can only lower the project's sample rate. */
|
|
162
|
+
tracesSampleRate?: number;
|
|
163
|
+
/**
|
|
164
|
+
* URLs that get `sentry-trace` / `baggage` / `traceparent` headers: a string matches as a
|
|
165
|
+
* substring, a RegExp when it matches. Default: same-origin requests only.
|
|
166
|
+
*/
|
|
167
|
+
tracePropagationTargets?: Array<string | RegExp>;
|
|
168
|
+
}
|
|
169
|
+
interface TraceSpan {
|
|
170
|
+
span_id: string;
|
|
171
|
+
parent_span_id: string;
|
|
172
|
+
trace_id: string;
|
|
173
|
+
op: string;
|
|
174
|
+
description?: string;
|
|
175
|
+
start_timestamp: number;
|
|
176
|
+
timestamp: number;
|
|
177
|
+
status?: string;
|
|
178
|
+
data?: Record<string, unknown>;
|
|
179
|
+
origin?: string;
|
|
180
|
+
}
|
|
181
|
+
interface TransactionEvent {
|
|
182
|
+
type: "transaction";
|
|
183
|
+
event_id: string;
|
|
184
|
+
platform: "javascript";
|
|
185
|
+
transaction: string;
|
|
186
|
+
transaction_info: {
|
|
187
|
+
source: "url" | "route";
|
|
188
|
+
};
|
|
189
|
+
start_timestamp: number;
|
|
190
|
+
timestamp: number;
|
|
191
|
+
release?: string;
|
|
192
|
+
environment: string;
|
|
193
|
+
contexts: {
|
|
194
|
+
trace: {
|
|
195
|
+
trace_id: string;
|
|
196
|
+
span_id: string;
|
|
197
|
+
parent_span_id?: string;
|
|
198
|
+
op: string;
|
|
199
|
+
status: string;
|
|
200
|
+
origin: string;
|
|
201
|
+
};
|
|
202
|
+
browser?: {
|
|
203
|
+
name: string;
|
|
204
|
+
version?: string;
|
|
205
|
+
};
|
|
206
|
+
guidely: {
|
|
207
|
+
anonymousId: string;
|
|
208
|
+
sessionId: string;
|
|
209
|
+
url?: string;
|
|
210
|
+
};
|
|
211
|
+
};
|
|
212
|
+
spans: TraceSpan[];
|
|
213
|
+
measurements?: Measurements;
|
|
214
|
+
request?: {
|
|
215
|
+
url: string;
|
|
216
|
+
};
|
|
217
|
+
user?: {
|
|
218
|
+
id: string;
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** What `beforeSend` gets (and may change): the indexed properties and the detail. */
|
|
223
|
+
interface ErrorEvent {
|
|
224
|
+
properties: ExceptionProperties & Record<string, unknown>;
|
|
225
|
+
detail: ExceptionDetail;
|
|
226
|
+
}
|
|
227
|
+
interface BreadcrumbHint {
|
|
228
|
+
/** The DOM event, console arguments, Response or XMLHttpRequest the breadcrumb came from. */
|
|
229
|
+
event?: unknown;
|
|
230
|
+
}
|
|
231
|
+
/** `createGuidely({ errors })`. */
|
|
232
|
+
interface ErrorMonitoringOptions {
|
|
233
|
+
/** Share of errors sent, 0–1 (default 1). */
|
|
234
|
+
sampleRate?: number;
|
|
235
|
+
/** Errors whose `"<type>: <message>"` or message contains a string / matches a RegExp are not sent. */
|
|
236
|
+
ignoreErrors?: ErrorFilterPattern[];
|
|
237
|
+
/** Errors thrown from a matching script URL (innermost frame) are not sent, e.g. `[/extensions\//]`. */
|
|
238
|
+
denyUrls?: ErrorFilterPattern[];
|
|
239
|
+
/** When set, only errors thrown from a matching script URL are sent. */
|
|
240
|
+
allowUrls?: ErrorFilterPattern[];
|
|
241
|
+
/** Last say over each error: return it (changed or not), or null to drop it. */
|
|
242
|
+
beforeSend?: (event: ErrorEvent) => ErrorEvent | null;
|
|
243
|
+
/** Last say over each breadcrumb: return it (changed or not), or null to drop it. */
|
|
244
|
+
beforeBreadcrumb?: (crumb: Breadcrumb, hint?: BreadcrumbHint) => Breadcrumb | null;
|
|
245
|
+
/** Automatic breadcrumbs (all on by default); `false` turns them all off. `console: "all"` also records `console.log`/`info`. */
|
|
246
|
+
breadcrumbs?: false | {
|
|
247
|
+
clicks?: boolean;
|
|
248
|
+
navigation?: boolean;
|
|
249
|
+
console?: boolean | "all";
|
|
250
|
+
http?: boolean;
|
|
251
|
+
guides?: boolean;
|
|
252
|
+
};
|
|
253
|
+
/** Breadcrumbs kept (default and maximum 100). */
|
|
254
|
+
maxBreadcrumbs?: number;
|
|
255
|
+
}
|
|
256
|
+
/** The second argument of `captureException`; any other key becomes an event property. */
|
|
257
|
+
interface CaptureContext {
|
|
258
|
+
tags?: Record<string, string | number | boolean>;
|
|
259
|
+
extra?: Record<string, unknown>;
|
|
260
|
+
level?: ErrorLevelName;
|
|
261
|
+
/** `["{{ default }}", "checkout"]` splits an issue, `["checkout-timeout"]` merges errors into one. */
|
|
262
|
+
fingerprint?: string[];
|
|
263
|
+
contexts?: Record<string, Record<string, unknown> | null>;
|
|
264
|
+
user?: {
|
|
265
|
+
id?: string;
|
|
266
|
+
email?: string;
|
|
267
|
+
username?: string;
|
|
268
|
+
};
|
|
269
|
+
/** How the error was caught, for integrations: `{ type: "react.errorboundary", handled: false }` (default `generic`, handled). */
|
|
270
|
+
mechanism?: {
|
|
271
|
+
type: string;
|
|
272
|
+
handled?: boolean;
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** `development`, `staging`, `production` or the key of an environment you added (`qa`, `uat`…). */
|
|
277
|
+
type GuidelyEnvironment = "development" | "staging" | "production" | (string & {});
|
|
133
278
|
type ConsentState = "granted" | "denied" | "pending";
|
|
134
279
|
interface GuidelyAccount {
|
|
135
280
|
id: string;
|
|
@@ -143,6 +288,7 @@ interface GuidelyUser {
|
|
|
143
288
|
interface GuidelyOptions {
|
|
144
289
|
/** Environment public key, e.g. `pk_dev_…`. Not a secret. */
|
|
145
290
|
projectId: string;
|
|
291
|
+
/** Used for guide and flag targeting. Defaults to the environment the key belongs to (named in the loaded config). */
|
|
146
292
|
environment?: GuidelyEnvironment;
|
|
147
293
|
/** Base URL of the Guidely API (default `https://api.guidely.app`; use `http://localhost:3000` locally). */
|
|
148
294
|
apiUrl?: string;
|
|
@@ -169,8 +315,10 @@ interface GuidelyOptions {
|
|
|
169
315
|
/** App version or commit SHA (≤100 chars), sent with every `exception` to pick the matching uploaded source maps. */
|
|
170
316
|
release?: string;
|
|
171
317
|
/**
|
|
172
|
-
* Version of
|
|
173
|
-
*
|
|
318
|
+
* Version of your app (e.g. `"2.4.1"`, a build number, or an Electron app's `app.getVersion()`). Sent
|
|
319
|
+
* with every event and recording so analytics, errors and replays can be filtered and broken down by
|
|
320
|
+
* version (Insights → App versions), and used by **App version** targeting rules and as the
|
|
321
|
+
* `appVersion` flag attribute. With the script tag: `data-app-version="2.4.1"`.
|
|
174
322
|
*/
|
|
175
323
|
appVersion?: string;
|
|
176
324
|
/**
|
|
@@ -181,6 +329,11 @@ interface GuidelyOptions {
|
|
|
181
329
|
pageUrl?: (href: string) => string;
|
|
182
330
|
/** `false` stops uncaught errors / unhandled rejections from being reported (`captureException` still works). */
|
|
183
331
|
captureErrors?: boolean;
|
|
332
|
+
/**
|
|
333
|
+
* Error monitoring options, as in Sentry SDKs: `sampleRate`, `ignoreErrors`, `denyUrls` / `allowUrls`,
|
|
334
|
+
* `beforeSend`, `beforeBreadcrumb` and which automatic breadcrumbs to record (docs/error-monitoring/sdks.md).
|
|
335
|
+
*/
|
|
336
|
+
errors?: ErrorMonitoringOptions;
|
|
184
337
|
/**
|
|
185
338
|
* `false` never records sessions on this installation. An object can only make masking stricter:
|
|
186
339
|
* `maskAllText` masks every text node, `blockSelectors` replaces matching elements with a placeholder.
|
|
@@ -223,6 +376,13 @@ interface GuidelyOptions {
|
|
|
223
376
|
agents?: {
|
|
224
377
|
redact?: AgentTurnRedactor;
|
|
225
378
|
};
|
|
379
|
+
/**
|
|
380
|
+
* Performance tracing (pageload / navigation transactions with Web Vitals, resource, long-task and
|
|
381
|
+
* fetch/XHR spans) runs when the project turns it on. `false` turns it off for this installation;
|
|
382
|
+
* an object can lower the sample rate (`tracesSampleRate`, 0–1) and choose which URLs get trace
|
|
383
|
+
* headers (`tracePropagationTargets`; default: same-origin requests only).
|
|
384
|
+
*/
|
|
385
|
+
tracing?: boolean | TracingOptions;
|
|
226
386
|
}
|
|
227
387
|
interface ChecklistProgressState {
|
|
228
388
|
completedTaskIds: string[];
|
|
@@ -311,4 +471,11 @@ interface GuidelyEvents {
|
|
|
311
471
|
}
|
|
312
472
|
type GuidelyEventName = keyof GuidelyEvents;
|
|
313
473
|
|
|
314
|
-
|
|
474
|
+
/**
|
|
475
|
+
* Best guess at an environment from its public key, used until the config (which names it) loads.
|
|
476
|
+
* `pk_prod_…` → production, `pk_stg_…` → staging, `pk_dev_…` → development; a custom environment's key
|
|
477
|
+
* starts with its own key without dashes (`pk_qa_…` → `qa`). Anything else is development.
|
|
478
|
+
*/
|
|
479
|
+
declare function environmentFromKey(key: string): GuidelyEnvironment;
|
|
480
|
+
|
|
481
|
+
export { type AgentTurnRedactor as A, type BreadcrumbHint as B, type ChecklistProgressState as C, EventQueue as E, type GuideLocalState as G, type PersistedState as P, type SessionState as S, type TraceSpan as T, type ConsentState as a, type EventQueueOptions as b, type GuidelyAccount as c, type GuidelyEnvironment as d, type GuidelyUser as e, areMutuallyExclusive as f, countsTowardThrottle as g, dismissedAfterMerge as h, environmentFromKey as i, isExclusiveGuide as j, isFrequencyEligible as k, isThrottleEligible as l, isWithinSchedule as m, type GuidelyOptions as n, type CaptureContext as o, pruneLaunches as p, type GuidelyEvents as q, type ErrorEvent as r, type ErrorMonitoringOptions as s, type GuideStepInfo as t, type GuidelyEventName as u, type TracingOptions as v, type TransactionEvent as w };
|
package/dist/headless.cjs
CHANGED
|
@@ -32,6 +32,7 @@ __export(headless_exports, {
|
|
|
32
32
|
backoffMs: () => backoffMs,
|
|
33
33
|
countsTowardThrottle: () => countsTowardThrottle,
|
|
34
34
|
dismissedAfterMerge: () => dismissedAfterMerge,
|
|
35
|
+
environmentFromKey: () => environmentFromKey,
|
|
35
36
|
isExclusiveGuide: () => isExclusiveGuide,
|
|
36
37
|
isFrequencyEligible: () => isFrequencyEligible,
|
|
37
38
|
isServerFailure: () => isServerFailure,
|
|
@@ -368,6 +369,21 @@ var KEYS = {
|
|
|
368
369
|
};
|
|
369
370
|
|
|
370
371
|
// src/queue.ts
|
|
372
|
+
var MAX_PERSISTED_QUEUE_CHARS = 1024 * 1024;
|
|
373
|
+
function fitPersisted(events) {
|
|
374
|
+
let size = JSON.stringify(events).length;
|
|
375
|
+
if (size <= MAX_PERSISTED_QUEUE_CHARS) return events;
|
|
376
|
+
const out = events.slice();
|
|
377
|
+
for (let i = 0; i < out.length && size > MAX_PERSISTED_QUEUE_CHARS; i++) {
|
|
378
|
+
const e = out[i];
|
|
379
|
+
if (!e.detail) continue;
|
|
380
|
+
size -= JSON.stringify(e.detail).length;
|
|
381
|
+
const rest = { ...e };
|
|
382
|
+
delete rest.detail;
|
|
383
|
+
out[i] = rest;
|
|
384
|
+
}
|
|
385
|
+
return out;
|
|
386
|
+
}
|
|
371
387
|
var EventQueue = class {
|
|
372
388
|
constructor(opts) {
|
|
373
389
|
__publicField(this, "opts", opts);
|
|
@@ -423,7 +439,7 @@ var EventQueue = class {
|
|
|
423
439
|
this.schedule();
|
|
424
440
|
}
|
|
425
441
|
persist() {
|
|
426
|
-
writeJson(this.opts.store, this.opts.storageKey, this.events);
|
|
442
|
+
writeJson(this.opts.store, this.opts.storageKey, fitPersisted(this.events));
|
|
427
443
|
}
|
|
428
444
|
schedule() {
|
|
429
445
|
if (this.timer || this.retryTimer || this.destroyed) return;
|
|
@@ -679,6 +695,15 @@ function dismissedAfterMerge(local, server) {
|
|
|
679
695
|
if (server.dismissResetAt && server.dismissResetAt !== local?.dismissResetAt) return server.dismissedAt ?? null;
|
|
680
696
|
return local?.dismissedAt ?? server.dismissedAt ?? null;
|
|
681
697
|
}
|
|
698
|
+
|
|
699
|
+
// src/environment.ts
|
|
700
|
+
function environmentFromKey(key) {
|
|
701
|
+
const prefix = /^pk_([a-z0-9]+)_/.exec(key)?.[1];
|
|
702
|
+
if (!prefix || prefix === "dev") return "development";
|
|
703
|
+
if (prefix === "prod") return "production";
|
|
704
|
+
if (prefix === "stg" || prefix === "stag") return "staging";
|
|
705
|
+
return prefix;
|
|
706
|
+
}
|
|
682
707
|
// Annotate the CommonJS export names for ESM import in node:
|
|
683
708
|
0 && (module.exports = {
|
|
684
709
|
Api,
|
|
@@ -691,6 +716,7 @@ function dismissedAfterMerge(local, server) {
|
|
|
691
716
|
backoffMs,
|
|
692
717
|
countsTowardThrottle,
|
|
693
718
|
dismissedAfterMerge,
|
|
719
|
+
environmentFromKey,
|
|
694
720
|
isExclusiveGuide,
|
|
695
721
|
isFrequencyEligible,
|
|
696
722
|
isServerFailure,
|