@guidelytech/sdk-core 0.6.2 → 0.7.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.d.cts CHANGED
@@ -1,15 +1,17 @@
1
1
  import * as _guidelytech_guide_runtime from '@guidelytech/guide-runtime';
2
2
  import { TargetingContext, PublishedGuide, ProjectTheme, SelectorStrategy, AuthoringPick, AppSnapshot } from '@guidelytech/shared';
3
3
  export { AppSnapshot, AutocaptureSettings, ExceptionProperties, GuideConfig, GuideStep, PublishedGuide, SdkConfigResponse, SdkEvent, StackFrame, TargetingContext } from '@guidelytech/shared';
4
+ import { FlagsClient, JsonValue, FlagValue, FlagDetails } from '@guidelytech/flags';
5
+ export { Attributes, EvaluationResult, FlagDetails, FlagsBootstrap, FlagsClient, FlagsClientOptions, FlagsPayload, JsonValue } from '@guidelytech/flags';
4
6
  import { MessengerTab } from '@guidelytech/guide-runtime/messenger';
5
- import { j as GuidelyOptions, c as GuidelyAccount, a as ConsentState, C as ChecklistProgressState, k as GuidelyEvents, e as GuidelyUser, G as GuideLocalState, S as SessionState } from './types-d4eOerxi.cjs';
6
- export { E as EventQueue, b as EventQueueOptions, l as GuideStepInfo, d as GuidelyEnvironment, m as GuidelyEventName, P as PersistedState, f as areMutuallyExclusive, i as isExclusiveGuide, g as isFrequencyEligible, h as isWithinSchedule } from './types-d4eOerxi.cjs';
7
+ import { j as GuidelyOptions, c as GuidelyAccount, a as ConsentState, C as ChecklistProgressState, k as GuidelyEvents, e as GuidelyUser, G as GuideLocalState, S as SessionState } from './types-DbKQPvFt.cjs';
8
+ export { E as EventQueue, b as EventQueueOptions, l as GuideStepInfo, d as GuidelyEnvironment, m as GuidelyEventName, P as PersistedState, f as areMutuallyExclusive, i as isExclusiveGuide, g as isFrequencyEligible, h as isWithinSchedule } from './types-DbKQPvFt.cjs';
7
9
  export { K as KEYS, b as KeyValueStore, S as StorageKind, d as createStorage } from './http-47tiAlOM.cjs';
8
10
  export { C as CaptureMarker, a as CaptureOutgoingMessage } from './capture-C3faAYb7.cjs';
9
11
  export { CAPTURE_PARAM } from '@guidelytech/shared/runtime';
10
12
 
11
13
  declare const SDK_NAME = "@guidelytech/sdk-core";
12
- declare const SDK_VERSION = "0.6.2";
14
+ declare const SDK_VERSION = "0.7.0";
13
15
  declare const DEFAULT_API_URL = "https://api.guidely.app";
14
16
  /**
15
17
  * Framework-independent Guidely client. Create one per page with {@link createGuidely} or
@@ -66,7 +68,12 @@ declare class GuidelyClient {
66
68
  private initialPageViewSent;
67
69
  private pageViewUrl;
68
70
  private pageLeft;
71
+ /** Feature flags for this environment: `guidely.flags.isEnabled("new-checkout")`. */
72
+ readonly flags: FlagsClient;
73
+ /** Experiments already reported this session (`experiment_viewed` is sent once per session). */
74
+ private exposures;
69
75
  constructor(options: GuidelyOptions);
76
+ private createFlags;
70
77
  /** Loads config (cache first, then network), wires listeners and evaluates triggers. Idempotent. */
71
78
  init(): Promise<void>;
72
79
  /** Resolves once the client is ready (alias of `init()`). */
@@ -209,6 +216,19 @@ declare class GuidelyClient {
209
216
  closeMessenger(): void;
210
217
  /** Opens a new messenger conversation, optionally pre-filling the message. */
211
218
  showNewMessage(text?: string): void;
219
+ /** Boolean flag value for the current user; `fallback` (default `false`) until loaded or when missing. */
220
+ isFeatureEnabled(key: string, fallback?: boolean): boolean;
221
+ /** Flag value for the current user; `fallback` when missing or of a different type. */
222
+ getFeatureValue<T extends JsonValue>(key: string, fallback: T): FlagValue<T>;
223
+ /** Flag value plus why it was served (reason, rule, variation, experiment). */
224
+ getFeatureDetails<T extends JsonValue>(key: string, fallback: T): FlagDetails<FlagValue<T>>;
225
+ /** Calls `cb` whenever flag values may have changed (new rules, identity, page, overrides). Returns an unsubscribe function. */
226
+ onFlagsChange(cb: () => void): () => void;
227
+ /** Attributes flags are evaluated with: identity, traits, account, audiences, page and device. */
228
+ private flagContext;
229
+ private syncFlagAttributes;
230
+ /** `experiment_viewed`, once per session and assignment, through the event queue (consent applies). */
231
+ private trackExposure;
212
232
  on<K extends keyof GuidelyEvents>(event: K, cb: GuidelyEvents[K]): () => void;
213
233
  off<K extends keyof GuidelyEvents>(event: K, cb: GuidelyEvents[K]): void;
214
234
  /** Snapshot for debugging / devtools. */
@@ -241,6 +261,7 @@ declare class GuidelyClient {
241
261
  pendingEvents: number;
242
262
  authoring: boolean;
243
263
  capture: boolean;
264
+ flagsReady: boolean;
244
265
  };
245
266
  private maybeStartAuthoring;
246
267
  /** Authoring or capture started / ended: pause everything a visitor would see or be recorded by. */
package/dist/index.d.ts CHANGED
@@ -1,15 +1,17 @@
1
1
  import * as _guidelytech_guide_runtime from '@guidelytech/guide-runtime';
2
2
  import { TargetingContext, PublishedGuide, ProjectTheme, SelectorStrategy, AuthoringPick, AppSnapshot } from '@guidelytech/shared';
3
3
  export { AppSnapshot, AutocaptureSettings, ExceptionProperties, GuideConfig, GuideStep, PublishedGuide, SdkConfigResponse, SdkEvent, StackFrame, TargetingContext } from '@guidelytech/shared';
4
+ import { FlagsClient, JsonValue, FlagValue, FlagDetails } from '@guidelytech/flags';
5
+ export { Attributes, EvaluationResult, FlagDetails, FlagsBootstrap, FlagsClient, FlagsClientOptions, FlagsPayload, JsonValue } from '@guidelytech/flags';
4
6
  import { MessengerTab } from '@guidelytech/guide-runtime/messenger';
5
- import { j as GuidelyOptions, c as GuidelyAccount, a as ConsentState, C as ChecklistProgressState, k as GuidelyEvents, e as GuidelyUser, G as GuideLocalState, S as SessionState } from './types-RyeRQd9f.js';
6
- export { E as EventQueue, b as EventQueueOptions, l as GuideStepInfo, d as GuidelyEnvironment, m as GuidelyEventName, P as PersistedState, f as areMutuallyExclusive, i as isExclusiveGuide, g as isFrequencyEligible, h as isWithinSchedule } from './types-RyeRQd9f.js';
7
+ import { j as GuidelyOptions, c as GuidelyAccount, a as ConsentState, C as ChecklistProgressState, k as GuidelyEvents, e as GuidelyUser, G as GuideLocalState, S as SessionState } from './types-DqhNl5Am.js';
8
+ export { E as EventQueue, b as EventQueueOptions, l as GuideStepInfo, d as GuidelyEnvironment, m as GuidelyEventName, P as PersistedState, f as areMutuallyExclusive, i as isExclusiveGuide, g as isFrequencyEligible, h as isWithinSchedule } from './types-DqhNl5Am.js';
7
9
  export { K as KEYS, b as KeyValueStore, S as StorageKind, d as createStorage } from './http-47tiAlOM.js';
8
10
  export { C as CaptureMarker, a as CaptureOutgoingMessage } from './capture-Dqd7up2n.js';
9
11
  export { CAPTURE_PARAM } from '@guidelytech/shared/runtime';
10
12
 
11
13
  declare const SDK_NAME = "@guidelytech/sdk-core";
12
- declare const SDK_VERSION = "0.6.2";
14
+ declare const SDK_VERSION = "0.7.0";
13
15
  declare const DEFAULT_API_URL = "https://api.guidely.app";
14
16
  /**
15
17
  * Framework-independent Guidely client. Create one per page with {@link createGuidely} or
@@ -66,7 +68,12 @@ declare class GuidelyClient {
66
68
  private initialPageViewSent;
67
69
  private pageViewUrl;
68
70
  private pageLeft;
71
+ /** Feature flags for this environment: `guidely.flags.isEnabled("new-checkout")`. */
72
+ readonly flags: FlagsClient;
73
+ /** Experiments already reported this session (`experiment_viewed` is sent once per session). */
74
+ private exposures;
69
75
  constructor(options: GuidelyOptions);
76
+ private createFlags;
70
77
  /** Loads config (cache first, then network), wires listeners and evaluates triggers. Idempotent. */
71
78
  init(): Promise<void>;
72
79
  /** Resolves once the client is ready (alias of `init()`). */
@@ -209,6 +216,19 @@ declare class GuidelyClient {
209
216
  closeMessenger(): void;
210
217
  /** Opens a new messenger conversation, optionally pre-filling the message. */
211
218
  showNewMessage(text?: string): void;
219
+ /** Boolean flag value for the current user; `fallback` (default `false`) until loaded or when missing. */
220
+ isFeatureEnabled(key: string, fallback?: boolean): boolean;
221
+ /** Flag value for the current user; `fallback` when missing or of a different type. */
222
+ getFeatureValue<T extends JsonValue>(key: string, fallback: T): FlagValue<T>;
223
+ /** Flag value plus why it was served (reason, rule, variation, experiment). */
224
+ getFeatureDetails<T extends JsonValue>(key: string, fallback: T): FlagDetails<FlagValue<T>>;
225
+ /** Calls `cb` whenever flag values may have changed (new rules, identity, page, overrides). Returns an unsubscribe function. */
226
+ onFlagsChange(cb: () => void): () => void;
227
+ /** Attributes flags are evaluated with: identity, traits, account, audiences, page and device. */
228
+ private flagContext;
229
+ private syncFlagAttributes;
230
+ /** `experiment_viewed`, once per session and assignment, through the event queue (consent applies). */
231
+ private trackExposure;
212
232
  on<K extends keyof GuidelyEvents>(event: K, cb: GuidelyEvents[K]): () => void;
213
233
  off<K extends keyof GuidelyEvents>(event: K, cb: GuidelyEvents[K]): void;
214
234
  /** Snapshot for debugging / devtools. */
@@ -241,6 +261,7 @@ declare class GuidelyClient {
241
261
  pendingEvents: number;
242
262
  authoring: boolean;
243
263
  capture: boolean;
264
+ flagsReady: boolean;
244
265
  };
245
266
  private maybeStartAuthoring;
246
267
  /** Authoring or capture started / ended: pause everything a visitor would see or be recorded by. */
package/dist/index.js CHANGED
@@ -31,6 +31,7 @@ import {
31
31
  waitForTarget
32
32
  } from "@guidelytech/guide-runtime";
33
33
  import { createScope } from "@guidelytech/guide-runtime";
34
+ import { createFlagsClient } from "@guidelytech/flags";
34
35
 
35
36
  // src/authoring.ts
36
37
  import { highlightSelectors, previewStep, runGuide, startAuthoring } from "@guidelytech/guide-runtime";
@@ -1012,7 +1013,7 @@ var loadRecorder = async () => (await import("./recorder.js")).recorder;
1012
1013
 
1013
1014
  // src/client.ts
1014
1015
  var SDK_NAME = "@guidelytech/sdk-core";
1015
- var SDK_VERSION = "0.6.2";
1016
+ var SDK_VERSION = "0.7.0";
1016
1017
  var DEFAULT_API_URL = "https://api.guidely.app";
1017
1018
  var PAGE_SCOPED = /* @__PURE__ */ new Set(["page_load", "url_match", "element_click", "element_visible", "delay"]);
1018
1019
  var DAY_MS = 24 * 60 * 60 * 1e3;
@@ -1082,6 +1083,10 @@ var GuidelyClient = class _GuidelyClient {
1082
1083
  __publicField(this, "initialPageViewSent", false);
1083
1084
  __publicField(this, "pageViewUrl", null);
1084
1085
  __publicField(this, "pageLeft", false);
1086
+ /** Feature flags for this environment: `guidely.flags.isEnabled("new-checkout")`. */
1087
+ __publicField(this, "flags");
1088
+ /** Experiments already reported this session (`experiment_viewed` is sent once per session). */
1089
+ __publicField(this, "exposures", /* @__PURE__ */ new Set());
1085
1090
  __publicField(this, "customListeners", /* @__PURE__ */ new Map());
1086
1091
  __publicField(this, "eventBus", {
1087
1092
  on: (name, cb) => {
@@ -1140,6 +1145,39 @@ var GuidelyClient = class _GuidelyClient {
1140
1145
  onError: (err) => this.reportError(err)
1141
1146
  });
1142
1147
  if (options.user) this.user = { ...options.user };
1148
+ this.flags = this.createFlags(options.flags);
1149
+ }
1150
+ createFlags(opts) {
1151
+ const base = { sdkKey: this.options.projectId, apiUrl: this.options.apiUrl, fetch: this.options.fetch, sdk: { name: SDK_NAME, version: SDK_VERSION } };
1152
+ if (opts === false) return createFlagsClient({ ...base, overrideBehaviour: "localOnly", offline: true, reportUsage: false, trackExposures: false, storage: false, logger: null });
1153
+ const o = opts ?? {};
1154
+ return createFlagsClient({
1155
+ ...base,
1156
+ storage: { getItem: (k) => this.store.get(k), setItem: (k, v) => this.store.set(k, v), removeItem: (k) => this.store.remove(k) },
1157
+ reportUsage: this.options.analytics,
1158
+ // Failures surface through onError (and `debug` logs); development warnings (unknown flag keys) reach the console.
1159
+ logger: { warn: (...a) => console.warn("[Guidely]", ...a), error: (...a) => this.log(...a) },
1160
+ ...o,
1161
+ contextAttributes: this.flagContext(),
1162
+ // Exposures travel through the event pipeline (with the session) as `experiment_viewed`.
1163
+ trackExposures: false,
1164
+ onFlagEvaluated: (result) => {
1165
+ this.trackExposure(result);
1166
+ o.onFlagEvaluated?.(result);
1167
+ },
1168
+ onReady: () => {
1169
+ this.emitter.emit("flags:ready");
1170
+ o.onReady?.();
1171
+ },
1172
+ onChange: (data) => {
1173
+ this.emitter.emit("flags:change");
1174
+ o.onChange?.(data);
1175
+ },
1176
+ onError: (err) => {
1177
+ this.reportError(err);
1178
+ o.onError?.(err);
1179
+ }
1180
+ });
1143
1181
  }
1144
1182
  // ---------------------------------------------------------------------------
1145
1183
  // Lifecycle
@@ -1340,6 +1378,7 @@ var GuidelyClient = class _GuidelyClient {
1340
1378
  this.replay = null;
1341
1379
  this.queue.flushSync();
1342
1380
  this.queue.destroy();
1381
+ this.flags.destroy();
1343
1382
  this.emitter.clear();
1344
1383
  destroyHost();
1345
1384
  if (current === this) current = null;
@@ -1353,6 +1392,7 @@ var GuidelyClient = class _GuidelyClient {
1353
1392
  this.identitySettled = true;
1354
1393
  this.user = { id: String(userId), traits: { ...this.user?.id === String(userId) ? this.user.traits : {}, ...traits || {} }, account: account ?? (this.user?.id === String(userId) ? this.user.account : void 0) };
1355
1394
  writeJson(this.store, KEYS.user(this.options.projectId), this.user);
1395
+ this.syncFlagAttributes();
1356
1396
  this.enqueue("identify", { properties: { traits: sanitizeProperties(this.user.traits) } });
1357
1397
  await this.syncIdentify();
1358
1398
  this.emitter.emit("identify", { userId: this.user.id, anonymousId: this.anonymousId });
@@ -1393,6 +1433,7 @@ var GuidelyClient = class _GuidelyClient {
1393
1433
  };
1394
1434
  if (data.firstSeenAt && Date.now() - Date.parse(data.firstSeenAt) > 6e4) this.firstVisit = false;
1395
1435
  this.persistState();
1436
+ this.syncFlagAttributes();
1396
1437
  this.widgets.forEach((w, id) => {
1397
1438
  const g = this.findGuide(id);
1398
1439
  if (w.checklist && g) w.checklist.setProgress(this.getChecklistProgress(g.id));
@@ -1404,6 +1445,7 @@ var GuidelyClient = class _GuidelyClient {
1404
1445
  if (!this.user) {
1405
1446
  this.user = { id: "", account };
1406
1447
  writeJson(this.store, KEYS.user(this.options.projectId), this.user);
1448
+ this.syncFlagAttributes();
1407
1449
  return;
1408
1450
  }
1409
1451
  await this.identify(this.user.id, void 0, account);
@@ -1412,6 +1454,7 @@ var GuidelyClient = class _GuidelyClient {
1412
1454
  async updateTraits(traits) {
1413
1455
  if (!this.user?.id) {
1414
1456
  this.user = { id: "", traits: { ...this.user?.traits || {}, ...traits } };
1457
+ this.syncFlagAttributes();
1415
1458
  return;
1416
1459
  }
1417
1460
  await this.identify(this.user.id, traits, this.user.account);
@@ -1437,6 +1480,8 @@ var GuidelyClient = class _GuidelyClient {
1437
1480
  this.widgets.clear();
1438
1481
  forgetConversations(this.store, this.options.projectId);
1439
1482
  this.destroyMessenger();
1483
+ this.exposures.clear();
1484
+ this.syncFlagAttributes();
1440
1485
  this.emitter.emit("identify", { userId: null, anonymousId: this.anonymousId });
1441
1486
  if (this.isReady) {
1442
1487
  this.renderPersistentWidgets();
@@ -1509,6 +1554,7 @@ var GuidelyClient = class _GuidelyClient {
1509
1554
  if (this.destroyed) return;
1510
1555
  const changed = url !== this.lastUrl;
1511
1556
  this.lastUrl = url;
1557
+ if (changed) this.syncFlagAttributes();
1512
1558
  if (!explicit && changed) {
1513
1559
  this.leavePage(url);
1514
1560
  this.trackPageView(url);
@@ -2440,6 +2486,58 @@ var GuidelyClient = class _GuidelyClient {
2440
2486
  if (!this.config?.messenger?.enabled) return;
2441
2487
  void this.loadMessenger().then((m) => m?.showNewMessage(text));
2442
2488
  }
2489
+ // ---------------------------------------------------------------------------
2490
+ // Feature flags
2491
+ // ---------------------------------------------------------------------------
2492
+ /** Boolean flag value for the current user; `fallback` (default `false`) until loaded or when missing. */
2493
+ isFeatureEnabled(key, fallback = false) {
2494
+ return this.flags.isEnabled(key, fallback);
2495
+ }
2496
+ /** Flag value for the current user; `fallback` when missing or of a different type. */
2497
+ getFeatureValue(key, fallback) {
2498
+ return this.flags.getValue(key, fallback);
2499
+ }
2500
+ /** Flag value plus why it was served (reason, rule, variation, experiment). */
2501
+ getFeatureDetails(key, fallback) {
2502
+ return this.flags.getDetails(key, fallback);
2503
+ }
2504
+ /** Calls `cb` whenever flag values may have changed (new rules, identity, page, overrides). Returns an unsubscribe function. */
2505
+ onFlagsChange(cb) {
2506
+ return this.flags.subscribe(cb);
2507
+ }
2508
+ /** Attributes flags are evaluated with: identity, traits, account, audiences, page and device. */
2509
+ flagContext() {
2510
+ const device = detectDevice(navigator.userAgent, window.innerWidth);
2511
+ const account = this.user?.account;
2512
+ return {
2513
+ url: location.href,
2514
+ path: location.pathname,
2515
+ deviceType: device.type,
2516
+ browser: device.browser,
2517
+ os: device.os,
2518
+ locale: navigator.language || void 0,
2519
+ environment: this.options.environment,
2520
+ platform: "web",
2521
+ ...this.user?.traits ?? {},
2522
+ id: this.user?.id || this.anonymousId,
2523
+ anonymousId: this.anonymousId,
2524
+ account: account ? { ...account.traits ?? {}, id: account.id } : void 0,
2525
+ $audiences: [...this.state.audienceIds]
2526
+ };
2527
+ }
2528
+ syncFlagAttributes() {
2529
+ if (!this.destroyed) this.flags?.setContextAttributes(this.flagContext());
2530
+ }
2531
+ /** `experiment_viewed`, once per session and assignment, through the event queue (consent applies). */
2532
+ trackExposure(result) {
2533
+ const exp = result.inExperiment ? result.experiment : void 0;
2534
+ if (!exp || this.destroyed) return;
2535
+ const key = `${this.session.id}|${exp.key}|${exp.hashValue}|${exp.variationId}`;
2536
+ if (this.exposures.has(key)) return;
2537
+ if (this.exposures.size > 500) this.exposures.clear();
2538
+ this.exposures.add(key);
2539
+ this.enqueue("experiment_viewed", { properties: { experimentKey: exp.key, variationId: exp.variationId, flagKey: result.key, hashAttribute: exp.hashAttribute, ...exp.variationName ? { variationName: exp.variationName } : {} } });
2540
+ }
2443
2541
  on(event, cb) {
2444
2542
  return this.emitter.on(event, cb);
2445
2543
  }
@@ -2465,7 +2563,8 @@ var GuidelyClient = class _GuidelyClient {
2465
2563
  session: this.session,
2466
2564
  pendingEvents: this.queue.size,
2467
2565
  authoring: !!this.authoring?.isActive(),
2468
- capture: !!this.capture?.isActive()
2566
+ capture: !!this.capture?.isActive(),
2567
+ flagsReady: this.flags.isReady()
2469
2568
  };
2470
2569
  }
2471
2570
  // ---------------------------------------------------------------------------