tempest-react-sdk 0.1.2 → 0.1.4

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 CHANGED
@@ -168,8 +168,8 @@ Every module is re-exported from the package root — `import { Button, useDebou
168
168
  | `theme` | `ThemeProvider`, `useTheme`, `getInitialTheme`, `themeInitScript`, types: `ThemeMode`, `ResolvedTheme` |
169
169
  | `i18n` | `createI18n`, `I18nProvider`, `useI18n`, `useTranslate`, types: `Catalog`, `Messages`, `I18n`, `InterpolationValues` |
170
170
  | `logger` | `createLogger`, `consoleSink`, types: `Logger`, `LogEntry`, `LogLevel`, `LoggerSink` |
171
- | `telemetry` | `TelemetryProvider`, `useTelemetry`, `consoleTelemetryAdapter`, types: `TelemetryAdapter`, `TelemetryEvent`, `TelemetryUser` |
172
- | `feature-flags` | `FeatureFlagsProvider`, `useFeatureFlag`, `useFlagValue`, `createInMemoryFlags`, types: `FeatureFlagsAdapter`, `FlagValue` |
171
+ | `telemetry` | `TelemetryProvider`, `useTelemetry`, `consoleTelemetryAdapter`, `createSentryTelemetryAdapter`, `createPostHogTelemetryAdapter`, types: `TelemetryAdapter`, `TelemetryEvent`, `TelemetryUser`, `CreateSentryTelemetryAdapterOptions`, `SentryLike`, `CreatePostHogTelemetryAdapterOptions`, `PostHogLike` |
172
+ | `feature-flags` | `FeatureFlagsProvider`, `useFeatureFlag`, `useFlagValue`, `createInMemoryFlags`, `createGrowthBookFeatureFlagsAdapter`, `createLaunchDarklyFeatureFlagsAdapter`, types: `FeatureFlagsAdapter`, `FlagValue`, `GrowthBookLike`, `LDClientLike` |
173
173
  | `share` | `share`, `isShareSupported`, types: `SharePayload`, `ShareResult` |
174
174
  | `utils` | `cn`, `formatCurrency`, `formatDate`, `formatDateTime`, `formatPhone`, `formatCPF`, `formatPercent`, `storage` |
175
175
 
@@ -1241,7 +1241,7 @@ function Header() {
1241
1241
 
1242
1242
  ### Feature flags recipe
1243
1243
 
1244
- `FeatureFlagsProvider` takes an `adapter` matching the `FeatureFlagsAdapter` interface (`isEnabled`, `get`, `subscribe`). Ship the `InMemory` adapter while you build, swap for GrowthBook / LaunchDarkly when you're ready.
1244
+ `FeatureFlagsProvider` takes an `adapter` matching the `FeatureFlagsAdapter` interface (`isEnabled`, `get`, `onChange?`). Ship the `InMemory` adapter while you build, swap for GrowthBook / LaunchDarkly when you're ready.
1245
1245
 
1246
1246
  ```tsx
1247
1247
  import {
@@ -1252,7 +1252,7 @@ import {
1252
1252
  } from "tempest-react-sdk";
1253
1253
 
1254
1254
  const flags = createInMemoryFlags({
1255
- flags: { "new-checkout": true, "max-items": 10 },
1255
+ initial: { "new-checkout": true, "max-items": 10 },
1256
1256
  });
1257
1257
 
1258
1258
  <FeatureFlagsProvider adapter={flags}>
@@ -1266,16 +1266,61 @@ function CheckoutButton() {
1266
1266
  }
1267
1267
  ```
1268
1268
 
1269
- The interface is intentionally tiny any third-party SDK can be wrapped into an adapter in ~20 lines.
1269
+ **GrowthBook adapter** wraps a `GrowthBook` instance. The app initialises GrowthBook (so it controls `apiHost`, `clientKey`, attributes, `loadFeatures()`), the adapter only routes lookups.
1270
+
1271
+ ```ts
1272
+ import { GrowthBook } from "@growthbook/growthbook";
1273
+ import { FeatureFlagsProvider, createGrowthBookFeatureFlagsAdapter } from "tempest-react-sdk";
1274
+
1275
+ const gb = new GrowthBook({
1276
+ apiHost: import.meta.env.VITE_GROWTHBOOK_API_HOST,
1277
+ clientKey: import.meta.env.VITE_GROWTHBOOK_KEY,
1278
+ attributes: { id: userId },
1279
+ });
1280
+ await gb.loadFeatures();
1281
+
1282
+ const adapter = createGrowthBookFeatureFlagsAdapter({ growthbook: gb });
1283
+
1284
+ <FeatureFlagsProvider adapter={adapter}>
1285
+ <App />
1286
+ </FeatureFlagsProvider>;
1287
+ ```
1288
+
1289
+ Mapping: `isEnabled(key)` → `growthbook.isOn(key)`; `get(key, default)` → `growthbook.getFeatureValue(key, default)`; `onChange(listener)` → `growthbook.setRenderer(...)` (installed lazily on first subscription, multiplexes to all listeners).
1290
+
1291
+ **LaunchDarkly adapter** — wraps `launchdarkly-js-client-sdk`.
1292
+
1293
+ ```ts
1294
+ import * as LDClient from "launchdarkly-js-client-sdk";
1295
+ import { FeatureFlagsProvider, createLaunchDarklyFeatureFlagsAdapter } from "tempest-react-sdk";
1296
+
1297
+ const client = LDClient.initialize(import.meta.env.VITE_LD_CLIENT_ID, {
1298
+ kind: "user",
1299
+ key: userId,
1300
+ });
1301
+ await client.waitUntilReady();
1302
+
1303
+ const adapter = createLaunchDarklyFeatureFlagsAdapter({ client });
1304
+
1305
+ <FeatureFlagsProvider adapter={adapter}>
1306
+ <App />
1307
+ </FeatureFlagsProvider>;
1308
+ ```
1309
+
1310
+ Mapping: `isEnabled(key)` → `client.variation(key, default) === true`; `get(key, default)` → `client.variation(key, default)`; `onChange(listener)` → `client.on("change", listener)` + `client.off` on unsubscribe.
1311
+
1312
+ Neither `@growthbook/growthbook` nor `launchdarkly-js-client-sdk` is declared as a peer dep — the adapter only touches the instance you hand it. Install whichever you opt into: `npm install @growthbook/growthbook` or `npm install launchdarkly-js-client-sdk`.
1313
+
1314
+ The `FeatureFlagsAdapter` interface is intentionally tiny — any third-party SDK can be wrapped into an adapter in ~20 lines.
1270
1315
 
1271
1316
  ### Telemetry recipe
1272
1317
 
1273
- `TelemetryProvider` accepts an adapter matching `TelemetryAdapter` (`identify`, `track`, `captureException`, `flush`). The default `consoleTelemetryAdapter` logs every event — useful for dev and tests.
1318
+ `TelemetryProvider` accepts an adapter matching `TelemetryAdapter` (`init?`, `identify`, `track`, `captureException`, `flush?`). The bundled `consoleTelemetryAdapter` logs every event — useful for dev and tests.
1274
1319
 
1275
1320
  ```tsx
1276
1321
  import { TelemetryProvider, useTelemetry, consoleTelemetryAdapter } from "tempest-react-sdk";
1277
1322
 
1278
- <TelemetryProvider adapter={consoleTelemetryAdapter()}>
1323
+ <TelemetryProvider adapter={consoleTelemetryAdapter}>
1279
1324
  <App />
1280
1325
  </TelemetryProvider>;
1281
1326
 
@@ -1284,7 +1329,7 @@ function CheckoutForm() {
1284
1329
  return (
1285
1330
  <Button
1286
1331
  onClick={() => {
1287
- telemetry.track("checkout.completed", { total: 100 });
1332
+ telemetry?.track({ name: "checkout.completed", properties: { total: 100 } });
1288
1333
  }}
1289
1334
  >
1290
1335
  Pagar
@@ -1293,7 +1338,74 @@ function CheckoutForm() {
1293
1338
  }
1294
1339
  ```
1295
1340
 
1296
- Concrete adapters for Sentry / PostHog / Datadog are part of the v0.2 roadmap for now you can write one in ~20 lines (see [`docs/telemetry.md`](./docs/telemetry.md)).
1341
+ `useTelemetry()` returns `null` when no provider is mountedcall sites should optional-chain (`telemetry?.track(...)`).
1342
+
1343
+ **Sentry adapter** — wraps `@sentry/browser` so the SDK never depends on Sentry directly. The Sentry namespace is supplied by the caller; if the app already initialises Sentry at startup, just pass that instance.
1344
+
1345
+ ```ts
1346
+ import * as Sentry from "@sentry/browser";
1347
+ import { createSentryTelemetryAdapter, TelemetryProvider } from "tempest-react-sdk";
1348
+
1349
+ const adapter = createSentryTelemetryAdapter({
1350
+ sentry: Sentry,
1351
+ initOptions: {
1352
+ dsn: import.meta.env.VITE_SENTRY_DSN,
1353
+ environment: import.meta.env.MODE,
1354
+ tracesSampleRate: 0.1,
1355
+ },
1356
+ flushTimeout: 2000,
1357
+ breadcrumbCategory: "app",
1358
+ });
1359
+
1360
+ <TelemetryProvider adapter={adapter}>
1361
+ <App />
1362
+ </TelemetryProvider>;
1363
+ ```
1364
+
1365
+ Mapping:
1366
+
1367
+ | `TelemetryAdapter` call | `@sentry/browser` API |
1368
+ | -------------------------------- | ------------------------------------------------------------------ |
1369
+ | `init()` | `Sentry.init(initOptions)` (only when `initOptions` is provided) |
1370
+ | `identify(user)` | `Sentry.setUser({ id, email, username, ...traits })` |
1371
+ | `track({ name, properties })` | `Sentry.addBreadcrumb({ category, message, level: "info", data })` |
1372
+ | `captureException(err, context)` | `Sentry.captureException(err, { extra: context })` |
1373
+ | `flush()` | `Sentry.flush(flushTimeout)` |
1374
+
1375
+ `@sentry/browser` is **not** declared as a peer dep — the adapter only ever touches the namespace you hand it, so apps that don't use Sentry never pay for it. Install Sentry yourself when you opt in: `npm install @sentry/browser`.
1376
+
1377
+ **PostHog adapter** — wraps `posthog-js`.
1378
+
1379
+ ```ts
1380
+ import posthog from "posthog-js";
1381
+ import { createPostHogTelemetryAdapter, TelemetryProvider } from "tempest-react-sdk";
1382
+
1383
+ const adapter = createPostHogTelemetryAdapter({
1384
+ posthog,
1385
+ init: {
1386
+ apiKey: import.meta.env.VITE_POSTHOG_KEY,
1387
+ options: { api_host: "https://us.i.posthog.com" },
1388
+ },
1389
+ });
1390
+
1391
+ <TelemetryProvider adapter={adapter}>
1392
+ <App />
1393
+ </TelemetryProvider>;
1394
+ ```
1395
+
1396
+ Mapping:
1397
+
1398
+ | `TelemetryAdapter` call | `posthog-js` API |
1399
+ | ----------------------------- | ------------------------------------------------------------------------------------------------ |
1400
+ | `init()` | `posthog.init(apiKey, options)` (only when `init` is provided) |
1401
+ | `identify({id, ...})` | `posthog.identify(id, { email, name, ...traits })` |
1402
+ | `identify(null)` | `posthog.reset()` |
1403
+ | `track({ name, properties })` | `posthog.capture(name, properties)` |
1404
+ | `captureException(err, ctx)` | `posthog.captureException(err, ctx)` when available, else `posthog.capture("$exception", { … })` |
1405
+
1406
+ `posthog-js` is **not** declared as a peer dep — install only when you opt in: `npm install posthog-js`.
1407
+
1408
+ Concrete adapters for Datadog / Amplitude / others are part of the v0.2 roadmap — for now you can write one in ~20 lines following the Sentry / PostHog adapters as templates.
1297
1409
 
1298
1410
  ### Logger recipe
1299
1411
 
package/dist/index.d.ts CHANGED
@@ -376,6 +376,33 @@ export declare interface CreateEventStreamOptions<T> {
376
376
  onStatusChange?: (status: EventStreamStatus) => void;
377
377
  }
378
378
 
379
+ /**
380
+ * Build a [[FeatureFlagsAdapter]] backed by a GrowthBook instance. Apps
381
+ * initialise GrowthBook themselves (so they can load features over the
382
+ * network, set attributes, etc.) — the adapter just routes lookups.
383
+ *
384
+ * Mapping:
385
+ * - `isEnabled(key, default)` → `growthbook.isOn(key)` (falls back to `default` if no value)
386
+ * - `get(key, default)` → `growthbook.getFeatureValue(key, default)`
387
+ * - `onChange(listener)` → `growthbook.setRenderer(listener)` (single-renderer SDK constraint)
388
+ *
389
+ * @example
390
+ * import { GrowthBook } from "@growthbook/growthbook";
391
+ * import { FeatureFlagsProvider, createGrowthBookFeatureFlagsAdapter } from "tempest-react-sdk";
392
+ *
393
+ * const gb = new GrowthBook({ apiHost: "...", clientKey: "..." });
394
+ * await gb.loadFeatures();
395
+ * const adapter = createGrowthBookFeatureFlagsAdapter({ growthbook: gb });
396
+ *
397
+ * <FeatureFlagsProvider adapter={adapter}><App /></FeatureFlagsProvider>;
398
+ */
399
+ export declare function createGrowthBookFeatureFlagsAdapter(options: CreateGrowthBookFeatureFlagsAdapterOptions): FeatureFlagsAdapter;
400
+
401
+ export declare interface CreateGrowthBookFeatureFlagsAdapterOptions {
402
+ /** The GrowthBook instance. Required. */
403
+ growthbook: GrowthBookLike;
404
+ }
405
+
379
406
  /**
380
407
  * Create an i18n object with translation, pluralization and Intl helpers.
381
408
  *
@@ -418,6 +445,33 @@ export declare function createInMemoryFlags(options?: InMemoryFlagsOptions): Fea
418
445
  set: (key: string, value: FlagValue) => void;
419
446
  };
420
447
 
448
+ /**
449
+ * Build a [[FeatureFlagsAdapter]] backed by a LaunchDarkly JS client. Apps
450
+ * initialise the client themselves (`LDClient.initialize(envKey, ctx)`) and
451
+ * pass it in.
452
+ *
453
+ * Mapping:
454
+ * - `isEnabled(key, default)` → `client.variation(key, default) === true`
455
+ * - `get(key, default)` → `client.variation(key, default)`
456
+ * - `onChange(listener)` → `client.on("change", listener)` / `client.off("change", listener)`
457
+ *
458
+ * @example
459
+ * import * as LDClient from "launchdarkly-js-client-sdk";
460
+ * import { FeatureFlagsProvider, createLaunchDarklyFeatureFlagsAdapter } from "tempest-react-sdk";
461
+ *
462
+ * const client = LDClient.initialize(import.meta.env.VITE_LD_CLIENT_ID, { kind: "user", key: userId });
463
+ * await client.waitUntilReady();
464
+ * const adapter = createLaunchDarklyFeatureFlagsAdapter({ client });
465
+ *
466
+ * <FeatureFlagsProvider adapter={adapter}><App /></FeatureFlagsProvider>;
467
+ */
468
+ export declare function createLaunchDarklyFeatureFlagsAdapter(options: CreateLaunchDarklyFeatureFlagsAdapterOptions): FeatureFlagsAdapter;
469
+
470
+ export declare interface CreateLaunchDarklyFeatureFlagsAdapterOptions {
471
+ /** The LaunchDarkly JS client. Required. */
472
+ client: LDClientLike;
473
+ }
474
+
421
475
  /**
422
476
  * Create a structured leveled logger. Plug arbitrary sinks (Sentry, Datadog,
423
477
  * remote ingestion) by implementing the `LoggerSink` interface.
@@ -455,6 +509,39 @@ export declare interface CreateLoggerOptions {
455
509
  */
456
510
  export declare function createOfflineStore<TItem, TKey extends string | number = string>(config: OfflineStoreConfig<TItem>): OfflineStore<TItem, TKey>;
457
511
 
512
+ /**
513
+ * Build a [[TelemetryAdapter]] backed by [`posthog-js`](https://posthog.com/docs/libraries/js).
514
+ * The PostHog client is supplied by the caller (not bundled).
515
+ *
516
+ * Mapping:
517
+ * - `identify(user)` → `posthog.identify(user.id, { email, name, ...traits })` (or `posthog.reset()` when `null`)
518
+ * - `track({ name, properties })` → `posthog.capture(name, properties)`
519
+ * - `captureException(err, ctx)` → `posthog.captureException(err, ctx)` when available, otherwise `posthog.capture("$exception", { …err, …ctx })`
520
+ * - `flush()` → no-op (PostHog batches automatically)
521
+ *
522
+ * @example
523
+ * import posthog from "posthog-js";
524
+ * import { createPostHogTelemetryAdapter, TelemetryProvider } from "tempest-react-sdk";
525
+ *
526
+ * const adapter = createPostHogTelemetryAdapter({
527
+ * posthog,
528
+ * init: { apiKey: import.meta.env.VITE_POSTHOG_KEY, options: { api_host: "https://us.i.posthog.com" } },
529
+ * });
530
+ *
531
+ * <TelemetryProvider adapter={adapter}><App /></TelemetryProvider>;
532
+ */
533
+ export declare function createPostHogTelemetryAdapter(options: CreatePostHogTelemetryAdapterOptions): TelemetryAdapter;
534
+
535
+ export declare interface CreatePostHogTelemetryAdapterOptions {
536
+ /** The PostHog JS client (the default export of `posthog-js`). Required. */
537
+ posthog: PostHogLike;
538
+ /** Project API key + options. When provided, `Provider.init` calls `posthog.init(apiKey, options)`. */
539
+ init?: {
540
+ apiKey: string;
541
+ options?: Record<string, unknown>;
542
+ };
543
+ }
544
+
458
545
  /**
459
546
  * Build a typed query-key factory for a domain. Each entry can be a static
460
547
  * tuple or a function returning a tuple. The returned object is `as const`
@@ -486,6 +573,42 @@ export declare function createQueryKeys<TKey extends string, TEntries extends Re
486
573
  */
487
574
  export declare function createRefreshQueue(refresh: () => Promise<void>): () => Promise<void>;
488
575
 
576
+ /**
577
+ * Build a [[TelemetryAdapter]] backed by `@sentry/browser`. The Sentry SDK
578
+ * is supplied by the caller (not bundled) so apps that already initialise
579
+ * Sentry at startup can share that instance — and apps that don't use
580
+ * Sentry never pay for it.
581
+ *
582
+ * Mapping:
583
+ * - `identify(user)` → `Sentry.setUser`
584
+ * - `track(event)` → `Sentry.addBreadcrumb({ category, message: event.name, data })`
585
+ * - `captureException(err, ctx)` → `Sentry.captureException(err, { extra: ctx })`
586
+ * - `flush()` → `Sentry.flush(flushTimeout)`
587
+ *
588
+ * @example
589
+ * import * as Sentry from "@sentry/browser";
590
+ * import { createSentryTelemetryAdapter, TelemetryProvider } from "tempest-react-sdk";
591
+ *
592
+ * const adapter = createSentryTelemetryAdapter({
593
+ * sentry: Sentry,
594
+ * initOptions: { dsn: import.meta.env.VITE_SENTRY_DSN, tracesSampleRate: 0.1 },
595
+ * });
596
+ *
597
+ * <TelemetryProvider adapter={adapter}><App /></TelemetryProvider>;
598
+ */
599
+ export declare function createSentryTelemetryAdapter(options: CreateSentryTelemetryAdapterOptions): TelemetryAdapter;
600
+
601
+ export declare interface CreateSentryTelemetryAdapterOptions {
602
+ /** The Sentry browser SDK namespace. Required. */
603
+ sentry: SentryLike;
604
+ /** Optional init payload — passed verbatim to `Sentry.init` when the provider mounts. */
605
+ initOptions?: Record<string, unknown>;
606
+ /** Flush timeout in ms (default `2000`). */
607
+ flushTimeout?: number;
608
+ /** Breadcrumb category for `track` events (default `"app"`). */
609
+ breadcrumbCategory?: string;
610
+ }
611
+
489
612
  /**
490
613
  * Open a WebSocket with automatic exponential-backoff reconnect, optional
491
614
  * heartbeat pings, and typed JSON parsing.
@@ -909,6 +1032,17 @@ export declare interface GridProps extends HTMLAttributes<HTMLDivElement> {
909
1032
  children?: ReactNode;
910
1033
  }
911
1034
 
1035
+ /**
1036
+ * Minimal subset of [`@growthbook/growthbook`](https://docs.growthbook.io/lib/js)
1037
+ * used by the adapter. Pass a `GrowthBook` instance directly.
1038
+ */
1039
+ export declare interface GrowthBookLike {
1040
+ isOn: (key: string) => boolean;
1041
+ getFeatureValue: <T>(key: string, defaultValue: T) => T;
1042
+ /** Registers a renderer fired whenever GrowthBook re-evaluates features. */
1043
+ setRenderer?: (renderer: () => void) => void;
1044
+ }
1045
+
912
1046
  export declare interface I18n {
913
1047
  /** Currently active locale. */
914
1048
  locale: string;
@@ -1080,6 +1214,16 @@ export declare interface LazyWithRetryOptions {
1080
1214
  reloadOnFinalFailure?: boolean;
1081
1215
  }
1082
1216
 
1217
+ /**
1218
+ * Minimal subset of [`launchdarkly-js-client-sdk`](https://docs.launchdarkly.com/sdk/client-side/javascript)
1219
+ * used by the adapter. Pass an existing `LDClient`.
1220
+ */
1221
+ export declare interface LDClientLike {
1222
+ variation: <T = unknown>(key: string, defaultValue: T) => T;
1223
+ on?: (event: string, handler: () => void) => void;
1224
+ off?: (event: string, handler: () => void) => void;
1225
+ }
1226
+
1083
1227
  export declare interface ListOptions<TItem> {
1084
1228
  /** Property to order by. Default: `keyPath`. */
1085
1229
  orderBy?: keyof TItem & string;
@@ -1262,6 +1406,19 @@ export declare interface PlayAudioOptions {
1262
1406
  onError?: (error: unknown) => void;
1263
1407
  }
1264
1408
 
1409
+ /**
1410
+ * Minimal subset of `posthog-js` used by the adapter. Pass either the real
1411
+ * default export (`import posthog from "posthog-js"`) or a stubbed object
1412
+ * in tests.
1413
+ */
1414
+ export declare interface PostHogLike {
1415
+ init?: (apiKey: string, options?: Record<string, unknown>) => void;
1416
+ identify: (distinctId: string, properties?: Record<string, unknown>) => void;
1417
+ capture: (eventName: string, properties?: Record<string, unknown>) => void;
1418
+ captureException?: (error: unknown, properties?: Record<string, unknown>) => void;
1419
+ reset?: () => void;
1420
+ }
1421
+
1265
1422
  /** Linear progress bar with determinate / indeterminate modes. */
1266
1423
  export declare function Progress({ value, max, variant, indeterminate, showLabel, label, className, }: ProgressProps): JSX.Element;
1267
1424
 
@@ -1478,6 +1635,27 @@ export declare interface SelectProps extends SelectHTMLAttributes<HTMLSelectElem
1478
1635
  wrapperClassName?: string;
1479
1636
  }
1480
1637
 
1638
+ /**
1639
+ * Minimal subset of `@sentry/browser` used by the adapter. Lets you pass
1640
+ * either the real SDK module (`import * as Sentry from "@sentry/browser"`)
1641
+ * or a stubbed object in tests.
1642
+ */
1643
+ export declare interface SentryLike {
1644
+ init?: (options: Record<string, unknown>) => void;
1645
+ setUser: (user: Record<string, unknown> | null) => void;
1646
+ addBreadcrumb: (breadcrumb: {
1647
+ category?: string;
1648
+ message?: string;
1649
+ level?: string;
1650
+ data?: Record<string, unknown>;
1651
+ }) => void;
1652
+ captureException: (error: unknown, hint?: {
1653
+ extra?: Record<string, unknown>;
1654
+ contexts?: Record<string, unknown>;
1655
+ }) => void;
1656
+ flush?: (timeout?: number) => Promise<boolean>;
1657
+ }
1658
+
1481
1659
  /**
1482
1660
  * Wrap the Web Share API with a uniform result object. Falls through to
1483
1661
  * `unsupported: true` when the browser lacks `navigator.share`, leaving the