@guidelytech/react 0.6.2 → 0.8.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Guidely
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -33,7 +33,40 @@ function HelpMenu() {
33
33
  | `useGuidelyEvent(event, handler)` | Subscribes for the component lifetime (latest handler is used). |
34
34
  | `useChecklistProgress(idOrKey)` | Live `{ completedTaskIds, completedAt, dismissedAt }`. |
35
35
  | `useGuideState(idOrKey)` | Live `{ active, completed, stepId, stepIndex, guide }`. |
36
- | `noopClient` | The no-op client instance. |
36
+ | `noopClient` | The no-op client instance (flag getters return their fallbacks). |
37
+ | `useFlag(key, fallback = false)` | Is the boolean flag on? |
38
+ | `useFeatureValue(key, fallback)` | A flag's value (string, number, JSON); `fallback` when missing or of another type. |
39
+ | `useFlagDetails(key, fallback)` | Value plus `reason`, `ruleId`, `variationId`, `experiment`. |
40
+ | `useFlags()` / `useFlagsReady()` / `useFlagsClient()` | Every flag's value / loaded yet / the flags client (`null` on the server). |
41
+ | `<Feature flag="x" fallback={…}>` (alias `<FeatureFlag>`) | Renders children when the flag is on, else `fallback`. |
42
+ | `<FeatureValue flag="x" value="green" fallback={…}>` | Renders children when the flag's value equals `value`. |
43
+ | `<FlagsProvider sdkKey="pk_…" attributes={…} payload={…}>` | Flags only, without guides or analytics (accepts every flags client option). |
44
+
45
+ ## Feature flags
46
+
47
+ ```tsx
48
+ import { Feature, useFeatureValue, useFlag } from "@guidelytech/react";
49
+
50
+ function Checkout() {
51
+ const newCheckout = useFlag("new-checkout");
52
+ const color = useFeatureValue("checkout-button-color", "blue");
53
+ return (
54
+ <Feature flag="new-dashboard-banner" fallback={<OldBanner />}>
55
+ <NewBanner />
56
+ </Feature>
57
+ );
58
+ }
59
+ ```
60
+
61
+ The hooks read the `GuidelyProvider`'s client (`client.flags`, same public key, identity/page/device
62
+ attributes maintained for you) or a `<FlagsProvider>`. They subscribe with `useSyncExternalStore`
63
+ and re-render only when a value changes. Before the client exists (server rendering, hydration)
64
+ they read the bootstrap payload — `<GuidelyProvider flagsPayload={…} flagAttributes={…}>` or
65
+ `<FlagsProvider payload={…}>` — evaluated with `flagAttributes` plus the `user` prop (`id`, traits,
66
+ `account`), so server HTML and hydration match; without a payload they return fallbacks.
67
+ `GuidelyProvider` also accepts `flags={false}` to turn flags off or `flags={{ … }}` for client options.
37
68
 
38
69
  Types re-exported: `GuidelyClient`, `GuidelyOptions`, `GuidelyEvents`, `GuidelyUser`,
39
- `ChecklistProgressState`, `PublishedGuide`, `GuideState`, `GuidelyProviderProps`.
70
+ `ChecklistProgressState`, `PublishedGuide`, `GuideState`, `GuidelyProviderProps`, and from
71
+ `@guidelytech/flags`: `FlagsClient`, `FlagsClientOptions`, `FlagDetails`, `FlagsBootstrap`,
72
+ `FlagsPayload`, `Attributes`, `JsonValue`, `EvaluationResult`.
package/dist/index.cjs CHANGED
@@ -21,9 +21,19 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  // src/index.tsx
22
22
  var index_exports = {};
23
23
  __export(index_exports, {
24
+ Feature: () => Feature,
25
+ FeatureFlag: () => FeatureFlag,
26
+ FeatureValue: () => FeatureValue,
27
+ FlagsProvider: () => FlagsProvider,
24
28
  GuidelyProvider: () => GuidelyProvider,
25
29
  noopClient: () => noopClient,
26
30
  useChecklistProgress: () => useChecklistProgress,
31
+ useFeatureValue: () => useFeatureValue,
32
+ useFlag: () => useFlag,
33
+ useFlagDetails: () => useFlagDetails,
34
+ useFlags: () => useFlags,
35
+ useFlagsClient: () => useFlagsClient,
36
+ useFlagsReady: () => useFlagsReady,
27
37
  useGuideState: () => useGuideState,
28
38
  useGuidely: () => useGuidely,
29
39
  useGuidelyEvent: () => useGuidelyEvent,
@@ -31,27 +41,133 @@ __export(index_exports, {
31
41
  useGuidelyReady: () => useGuidelyReady
32
42
  });
33
43
  module.exports = __toCommonJS(index_exports);
44
+ var import_flags2 = require("@guidelytech/flags");
34
45
  var import_sdk_core = require("@guidelytech/sdk-core");
46
+ var import_react2 = require("react");
47
+
48
+ // src/flags.tsx
49
+ var import_flags = require("@guidelytech/flags");
35
50
  var import_react = require("react");
36
51
  var import_jsx_runtime = require("react/jsx-runtime");
37
- var GuidelyContext = (0, import_react.createContext)(null);
38
- function GuidelyProvider({ children, user, ...options }) {
39
- const [client, setClient] = (0, import_react.useState)(null);
52
+ var FlagsContext = (0, import_react.createContext)(null);
53
+ var EMPTY = (0, import_flags.createStaticFlags)();
54
+ function useReader() {
55
+ const src = (0, import_react.useContext)(FlagsContext);
56
+ return src?.client ?? src?.bootstrap ?? EMPTY;
57
+ }
58
+ function useFlagsValue(select) {
59
+ const reader = useReader();
60
+ const cache = (0, import_react.useRef)(null);
61
+ const get = () => {
62
+ const value = select(reader);
63
+ const c = cache.current;
64
+ if (c && c.reader === reader && (0, import_flags.jsonEquals)(c.value, value)) return c.value;
65
+ cache.current = { reader, value };
66
+ return value;
67
+ };
68
+ return (0, import_react.useSyncExternalStore)(reader.subscribe, get, get);
69
+ }
70
+ function useFlag(key, fallback = false) {
71
+ return useFlagsValue((r) => r.isEnabled(key, fallback));
72
+ }
73
+ function useFeatureValue(key, fallback) {
74
+ return useFlagsValue((r) => r.getValue(key, fallback));
75
+ }
76
+ function useFlagDetails(key, fallback) {
77
+ return useFlagsValue((r) => r.getDetails(key, fallback));
78
+ }
79
+ function useFlags() {
80
+ return useFlagsValue((r) => r.getAll());
81
+ }
82
+ function useFlagsReady() {
83
+ return useFlagsValue((r) => r.isReady());
84
+ }
85
+ function useFlagsClient() {
86
+ return (0, import_react.useContext)(FlagsContext)?.client ?? null;
87
+ }
88
+ function Feature({ flag, children, fallback = null }) {
89
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: useFlag(flag) ? children : fallback });
90
+ }
91
+ var FeatureFlag = Feature;
92
+ function FeatureValue({ flag, value, children, fallback = null }) {
93
+ const current = useFeatureValue(flag, null);
94
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: (0, import_flags.jsonEquals)(current, value) ? children : fallback });
95
+ }
96
+ function useBootstrap(payload, attributes) {
97
+ const key = (0, import_flags.stableStringify)(attributes);
98
+ return (0, import_react.useMemo)(() => payload ? (0, import_flags.createStaticFlags)({ payload, attributes }) : EMPTY, [payload, key]);
99
+ }
100
+ function FlagsProvider({ children, client: external, payload, ...options }) {
101
+ const [created, setCreated] = (0, import_react.useState)(null);
40
102
  const optionsRef = (0, import_react.useRef)(options);
41
103
  optionsRef.current = options;
42
- const { projectId, environment, apiUrl } = options;
104
+ const sdkKey = options.sdkKey ?? options.projectId;
105
+ const { apiUrl, environment } = options;
106
+ const payloadRef = (0, import_react.useRef)(payload);
107
+ payloadRef.current = payload;
43
108
  (0, import_react.useEffect)(() => {
109
+ if (external || !sdkKey || typeof window === "undefined") return;
110
+ let c;
111
+ try {
112
+ c = (0, import_flags.createFlagsClient)({ ...optionsRef.current, sdkKey, payload: payloadRef.current ?? void 0 });
113
+ } catch (err) {
114
+ optionsRef.current.onError?.(err instanceof Error ? err : new Error(String(err)));
115
+ return;
116
+ }
117
+ setCreated(c);
118
+ return () => {
119
+ c.destroy();
120
+ setCreated((cur) => cur === c ? null : cur);
121
+ };
122
+ }, [external, sdkKey, apiUrl, environment]);
123
+ const client = external ?? created;
124
+ const attributes = options.attributes ?? {};
125
+ const attrsKey = (0, import_flags.stableStringify)(attributes);
126
+ (0, import_react.useEffect)(() => {
127
+ if (client && options.attributes) client.setAttributes(options.attributes);
128
+ }, [client, attrsKey]);
129
+ const bootstrap = useBootstrap(payload, attributes);
130
+ const value = (0, import_react.useMemo)(() => ({ client, bootstrap }), [client, bootstrap]);
131
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FlagsContext.Provider, { value, children });
132
+ }
133
+
134
+ // src/index.tsx
135
+ var import_jsx_runtime2 = require("react/jsx-runtime");
136
+ var GuidelyContext = (0, import_react2.createContext)(null);
137
+ function userAttributes(user) {
138
+ if (!user?.id) return {};
139
+ return { ...user.traits ?? {}, id: user.id, ...user.account ? { account: { ...user.account.traits ?? {}, id: user.account.id } } : {} };
140
+ }
141
+ function GuidelyProvider({ children, user, flagsPayload, flagAttributes, ...options }) {
142
+ const [client, setClient] = (0, import_react2.useState)(null);
143
+ const optionsRef = (0, import_react2.useRef)(options);
144
+ optionsRef.current = options;
145
+ const userRef = (0, import_react2.useRef)(user);
146
+ userRef.current = user;
147
+ const flagsOpts = options.flags === false ? null : options.flags ?? {};
148
+ const payload = flagsPayload ?? flagsOpts?.payload ?? null;
149
+ const payloadRef = (0, import_react2.useRef)(payload);
150
+ payloadRef.current = payload;
151
+ const appAttributes = (0, import_react2.useMemo)(() => ({ ...flagsOpts?.attributes ?? {}, ...flagAttributes ?? {} }), [(0, import_flags2.stableStringify)(flagsOpts?.attributes ?? {}), (0, import_flags2.stableStringify)(flagAttributes ?? {})]);
152
+ const appAttributesRef = (0, import_react2.useRef)(appAttributes);
153
+ appAttributesRef.current = appAttributes;
154
+ const { projectId, environment, apiUrl } = options;
155
+ (0, import_react2.useEffect)(() => {
44
156
  if (typeof window === "undefined" || !projectId) return;
45
- const c = (0, import_sdk_core.createGuidely)({ ...optionsRef.current, projectId, environment, apiUrl });
157
+ const o = optionsRef.current;
158
+ const c = (0, import_sdk_core.createGuidely)({ ...o, projectId, environment, apiUrl, user: userRef.current?.id ? userRef.current : void 0, flags: o.flags === false ? false : { ...o.flags ?? {}, payload: payloadRef.current ?? void 0, attributes: appAttributesRef.current } });
46
159
  setClient(c);
47
160
  return () => {
48
161
  c.destroy();
49
162
  setClient((cur) => cur === c ? null : cur);
50
163
  };
51
164
  }, [projectId, environment, apiUrl]);
165
+ (0, import_react2.useEffect)(() => {
166
+ client?.flags.setAttributes(appAttributes);
167
+ }, [client, appAttributes]);
52
168
  const userKey = user ? JSON.stringify([user.id, user.traits ?? null, user.account ?? null]) : "";
53
- const hadUser = (0, import_react.useRef)(false);
54
- (0, import_react.useEffect)(() => {
169
+ const hadUser = (0, import_react2.useRef)(false);
170
+ (0, import_react2.useEffect)(() => {
55
171
  if (!client) return;
56
172
  if (user && user.id) {
57
173
  hadUser.current = true;
@@ -61,7 +177,9 @@ function GuidelyProvider({ children, user, ...options }) {
61
177
  client.reset();
62
178
  }
63
179
  }, [client, userKey]);
64
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GuidelyContext.Provider, { value: client, children });
180
+ const bootstrap = useBootstrap(payload, { ...userAttributes(user), ...appAttributes });
181
+ const flags = (0, import_react2.useMemo)(() => ({ client: client?.flags ?? null, bootstrap }), [client, bootstrap]);
182
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GuidelyContext.Provider, { value: client, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FlagsContext.Provider, { value: flags, children }) });
65
183
  }
66
184
  var NOOP_RESULTS = {
67
185
  init: Promise.resolve(),
@@ -82,20 +200,38 @@ var NOOP_RESULTS = {
82
200
  getSessionId: "",
83
201
  isRecording: false
84
202
  };
203
+ var noopFlags = new Proxy((0, import_flags2.createStaticFlags)(), {
204
+ get(target, prop, receiver) {
205
+ if (prop in target) return Reflect.get(target, prop, receiver);
206
+ if (prop === "ready" || prop === "refresh" || prop === "flush") return () => Promise.resolve();
207
+ if (prop === "getPayload") return () => null;
208
+ if (prop === "forUser") return () => target;
209
+ return typeof prop === "string" ? () => void 0 : void 0;
210
+ }
211
+ });
212
+ var missing = (key, fallback) => ({ key, value: fallback, reason: "FLAG_NOT_FOUND", inExperiment: false, usedFallback: true });
213
+ var NOOP_FUNCTIONS = {
214
+ isFeatureEnabled: (_key, fallback = false) => fallback,
215
+ getFeatureValue: (_key, fallback) => fallback,
216
+ getFeatureDetails: (key, fallback) => missing(key, fallback),
217
+ onFlagsChange: () => () => {
218
+ }
219
+ };
85
220
  var noopClient = new Proxy({}, {
86
221
  get(_target, prop) {
87
222
  if (prop === "options") return { projectId: "" };
223
+ if (prop === "flags") return noopFlags;
88
224
  if (typeof prop !== "string") return void 0;
89
- return () => NOOP_RESULTS[prop];
225
+ return NOOP_FUNCTIONS[prop] ?? (() => NOOP_RESULTS[prop]);
90
226
  }
91
227
  });
92
228
  function useGuidely() {
93
- return (0, import_react.useContext)(GuidelyContext) ?? noopClient;
229
+ return (0, import_react2.useContext)(GuidelyContext) ?? noopClient;
94
230
  }
95
231
  function useGuidelyReady() {
96
- const client = (0, import_react.useContext)(GuidelyContext);
97
- const [ready, setReady] = (0, import_react.useState)(false);
98
- (0, import_react.useEffect)(() => {
232
+ const client = (0, import_react2.useContext)(GuidelyContext);
233
+ const [ready, setReady] = (0, import_react2.useState)(false);
234
+ (0, import_react2.useEffect)(() => {
99
235
  if (!client) {
100
236
  setReady(false);
101
237
  return;
@@ -106,27 +242,27 @@ function useGuidelyReady() {
106
242
  return ready;
107
243
  }
108
244
  function useGuidelyIdentify(userId, traits, account) {
109
- const client = (0, import_react.useContext)(GuidelyContext);
245
+ const client = (0, import_react2.useContext)(GuidelyContext);
110
246
  const key = JSON.stringify([userId ?? null, traits ?? null, account ?? null]);
111
- (0, import_react.useEffect)(() => {
247
+ (0, import_react2.useEffect)(() => {
112
248
  if (!client || !userId) return;
113
249
  void client.identify(userId, traits, account);
114
250
  }, [client, key]);
115
251
  }
116
252
  function useGuidelyEvent(event, handler) {
117
- const client = (0, import_react.useContext)(GuidelyContext);
118
- const ref = (0, import_react.useRef)(handler);
253
+ const client = (0, import_react2.useContext)(GuidelyContext);
254
+ const ref = (0, import_react2.useRef)(handler);
119
255
  ref.current = handler;
120
- (0, import_react.useEffect)(() => {
256
+ (0, import_react2.useEffect)(() => {
121
257
  if (!client) return;
122
258
  const cb = ((...args) => ref.current(...args));
123
259
  return client.on(event, cb);
124
260
  }, [client, event]);
125
261
  }
126
262
  function useChecklistProgress(idOrKey) {
127
- const client = (0, import_react.useContext)(GuidelyContext);
128
- const [progress, setProgress] = (0, import_react.useState)({ completedTaskIds: [], completedAt: null, dismissedAt: null });
129
- (0, import_react.useEffect)(() => {
263
+ const client = (0, import_react2.useContext)(GuidelyContext);
264
+ const [progress, setProgress] = (0, import_react2.useState)({ completedTaskIds: [], completedAt: null, dismissedAt: null });
265
+ (0, import_react2.useEffect)(() => {
130
266
  if (!client) return;
131
267
  const update = () => setProgress(client.getChecklistProgress(idOrKey));
132
268
  update();
@@ -136,9 +272,9 @@ function useChecklistProgress(idOrKey) {
136
272
  return progress;
137
273
  }
138
274
  function useGuideState(idOrKey) {
139
- const client = (0, import_react.useContext)(GuidelyContext);
140
- const [state, setState] = (0, import_react.useState)({ active: false, completed: false, stepId: null, stepIndex: -1, guide: null });
141
- (0, import_react.useEffect)(() => {
275
+ const client = (0, import_react2.useContext)(GuidelyContext);
276
+ const [state, setState] = (0, import_react2.useState)({ active: false, completed: false, stepId: null, stepIndex: -1, guide: null });
277
+ (0, import_react2.useEffect)(() => {
142
278
  if (!client) return;
143
279
  const matches = (g) => g.id === idOrKey || g.key === idOrKey;
144
280
  const compute = () => {
@@ -155,9 +291,19 @@ function useGuideState(idOrKey) {
155
291
  }
156
292
  // Annotate the CommonJS export names for ESM import in node:
157
293
  0 && (module.exports = {
294
+ Feature,
295
+ FeatureFlag,
296
+ FeatureValue,
297
+ FlagsProvider,
158
298
  GuidelyProvider,
159
299
  noopClient,
160
300
  useChecklistProgress,
301
+ useFeatureValue,
302
+ useFlag,
303
+ useFlagDetails,
304
+ useFlags,
305
+ useFlagsClient,
306
+ useFlagsReady,
161
307
  useGuideState,
162
308
  useGuidely,
163
309
  useGuidelyEvent,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.tsx"],"sourcesContent":["import { createGuidely, type ChecklistProgressState, type GuidelyClient, type GuidelyEvents, type GuidelyOptions, type GuidelyUser, type PublishedGuide } from \"@guidelytech/sdk-core\";\nimport { createContext, useContext, useEffect, useRef, useState, type ReactNode } from \"react\";\n\nexport type { GuidelyClient, GuidelyOptions, GuidelyEvents, GuidelyUser, ChecklistProgressState, PublishedGuide } from \"@guidelytech/sdk-core\";\n\nconst GuidelyContext = createContext<GuidelyClient | null>(null);\n\nexport interface GuidelyProviderProps extends Omit<GuidelyOptions, \"user\"> {\n /** Identify this user as soon as the client is ready; `undefined` resets the identity. */\n user?: GuidelyUser | null;\n children?: ReactNode;\n}\n\n/**\n * Creates one Guidely client for the subtree. The client is created inside an effect (never\n * during SSR) and destroyed on unmount, so React Strict Mode's double-invoke is harmless.\n */\nexport function GuidelyProvider({ children, user, ...options }: GuidelyProviderProps) {\n const [client, setClient] = useState<GuidelyClient | null>(null);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n const { projectId, environment, apiUrl } = options;\n\n useEffect(() => {\n if (typeof window === \"undefined\" || !projectId) return;\n const c = createGuidely({ ...optionsRef.current, projectId, environment, apiUrl });\n setClient(c);\n return () => {\n c.destroy();\n setClient((cur) => (cur === c ? null : cur));\n };\n }, [projectId, environment, apiUrl]);\n\n const userKey = user ? JSON.stringify([user.id, user.traits ?? null, user.account ?? null]) : \"\";\n const hadUser = useRef(false);\n useEffect(() => {\n if (!client) return;\n if (user && user.id) {\n hadUser.current = true;\n void client.identify(user.id, user.traits, user.account);\n } else if (hadUser.current) {\n hadUser.current = false;\n client.reset();\n }\n }, [client, userKey]);\n\n return <GuidelyContext.Provider value={client}>{children}</GuidelyContext.Provider>;\n}\n\nconst NOOP_RESULTS: Record<string, unknown> = {\n init: Promise.resolve(),\n ready: Promise.resolve(),\n identify: Promise.resolve(),\n setAccount: Promise.resolve(),\n updateTraits: Promise.resolve(),\n refresh: Promise.resolve(),\n startGuide: false,\n getActiveGuide: null,\n getGuides: [],\n getChecklistProgress: { completedTaskIds: [], completedAt: null, dismissedAt: null },\n isGuideCompleted: false,\n on: () => {},\n getState: { ready: false, destroyed: false },\n getContext: null,\n getSessionId: \"\",\n isRecording: false,\n};\n\n/** A client that ignores every call; returned by `useGuidely()` on the server and before the provider mounts. */\nexport const noopClient: GuidelyClient = new Proxy({} as GuidelyClient, {\n get(_target, prop) {\n if (prop === \"options\") return { projectId: \"\" };\n if (typeof prop !== \"string\") return undefined;\n return () => NOOP_RESULTS[prop];\n },\n});\n\n/** The Guidely client, or a no-op client during SSR / before the provider has mounted. */\nexport function useGuidely(): GuidelyClient {\n return useContext(GuidelyContext) ?? noopClient;\n}\n\n/** True once the client has loaded its configuration. */\nexport function useGuidelyReady(): boolean {\n const client = useContext(GuidelyContext);\n const [ready, setReady] = useState(false);\n useEffect(() => {\n if (!client) {\n setReady(false);\n return;\n }\n if (client.getState().ready) setReady(true);\n return client.on(\"ready\", () => setReady(true));\n }, [client]);\n return ready;\n}\n\n/** Identifies the user whenever the arguments change. */\nexport function useGuidelyIdentify(userId: string | null | undefined, traits?: Record<string, unknown>, account?: GuidelyUser[\"account\"]): void {\n const client = useContext(GuidelyContext);\n const key = JSON.stringify([userId ?? null, traits ?? null, account ?? null]);\n useEffect(() => {\n if (!client || !userId) return;\n void client.identify(userId, traits, account);\n }, [client, key]);\n}\n\n/** Subscribes to a client event for the lifetime of the component. */\nexport function useGuidelyEvent<K extends keyof GuidelyEvents>(event: K, handler: GuidelyEvents[K]): void {\n const client = useContext(GuidelyContext);\n const ref = useRef(handler);\n ref.current = handler;\n useEffect(() => {\n if (!client) return;\n const cb = ((...args: unknown[]) => (ref.current as (...a: unknown[]) => void)(...args)) as GuidelyEvents[K];\n return client.on(event, cb);\n }, [client, event]);\n}\n\n/** Live checklist progress for a checklist guide (by id or key). */\nexport function useChecklistProgress(idOrKey: string): ChecklistProgressState {\n const client = useContext(GuidelyContext);\n const [progress, setProgress] = useState<ChecklistProgressState>({ completedTaskIds: [], completedAt: null, dismissedAt: null });\n useEffect(() => {\n if (!client) return;\n const update = () => setProgress(client.getChecklistProgress(idOrKey));\n update();\n const offs = [client.on(\"ready\", update), client.on(\"identify\", update), client.on(\"checklist:task\", update), client.on(\"checklist:complete\", update)];\n return () => offs.forEach((off) => off());\n }, [client, idOrKey]);\n return progress;\n}\n\nexport interface GuideState {\n /** The guide is currently showing. */\n active: boolean;\n completed: boolean;\n stepId: string | null;\n stepIndex: number;\n guide: PublishedGuide | null;\n}\n\n/** Live state of a guide (by id or key): active, completed, current step. */\nexport function useGuideState(idOrKey: string): GuideState {\n const client = useContext(GuidelyContext);\n const [state, setState] = useState<GuideState>({ active: false, completed: false, stepId: null, stepIndex: -1, guide: null });\n useEffect(() => {\n if (!client) return;\n const matches = (g: PublishedGuide) => g.id === idOrKey || g.key === idOrKey;\n const compute = () => {\n const guide = client.getGuides().find(matches) ?? null;\n const active = client.getActiveGuide();\n const s = client.getState().activeGuide;\n setState({ active: !!active && matches(active), completed: client.isGuideCompleted(idOrKey), stepId: active && matches(active) ? ((s as { stepId?: string | null } | null)?.stepId ?? null) : null, stepIndex: active && matches(active) ? ((s as { stepIndex?: number } | null)?.stepIndex ?? -1) : -1, guide });\n };\n compute();\n const offs = ([\"ready\", \"identify\", \"guide:start\", \"guide:step\", \"guide:complete\", \"guide:dismiss\", \"guide:skip\"] as const).map((e) => client.on(e, compute));\n return () => offs.forEach((off) => off());\n }, [client, idOrKey]);\n return state;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.tsx","../src/flags.tsx"],"sourcesContent":["import { createStaticFlags, stableStringify, type Attributes, type FlagDetails, type FlagsBootstrap, type FlagsClient } from \"@guidelytech/flags\";\nimport { createGuidely, type ChecklistProgressState, type GuidelyClient, type GuidelyEvents, type GuidelyOptions, type GuidelyUser, type PublishedGuide } from \"@guidelytech/sdk-core\";\nimport { createContext, useContext, useEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport { FlagsContext, useBootstrap, type FlagsSource } from \"./flags\";\n\nexport type { GuidelyClient, GuidelyOptions, GuidelyEvents, GuidelyUser, ChecklistProgressState, PublishedGuide } from \"@guidelytech/sdk-core\";\nexport { useFlag, useFeatureValue, useFlagDetails, useFlags, useFlagsReady, useFlagsClient, Feature, FeatureFlag, FeatureValue, FlagsProvider } from \"./flags\";\nexport type { FeatureProps, FeatureValueProps, FlagsProviderProps } from \"./flags\";\nexport type { FlagsClient, FlagsClientOptions, FlagDetails, FlagsBootstrap, FlagsPayload, Attributes, JsonValue, EvaluationResult } from \"@guidelytech/flags\";\n\nconst GuidelyContext = createContext<GuidelyClient | null>(null);\n\nexport interface GuidelyProviderProps extends Omit<GuidelyOptions, \"user\"> {\n /** Identify this user as soon as the client is ready; `undefined` resets the identity. */\n user?: GuidelyUser | null;\n /**\n * Flags bootstrap from the server (`getFlagsBootstrap()` / `client.getPayload()`): flag hooks\n * render the same values on the server, during hydration and after.\n */\n flagsPayload?: FlagsBootstrap | null;\n /** Extra flag attributes (merged over the automatic identity, page and device attributes). */\n flagAttributes?: Attributes;\n children?: ReactNode;\n}\n\n/** The attributes the server knows about a user: what bootstrap snapshots evaluate with. */\nfunction userAttributes(user: GuidelyUser | null | undefined): Attributes {\n if (!user?.id) return {};\n return { ...(user.traits ?? {}), id: user.id, ...(user.account ? { account: { ...(user.account.traits ?? {}), id: user.account.id } } : {}) };\n}\n\n/**\n * Creates one Guidely client for the subtree. The client is created inside an effect (never\n * during SSR) and destroyed on unmount, so React Strict Mode's double-invoke is harmless.\n */\nexport function GuidelyProvider({ children, user, flagsPayload, flagAttributes, ...options }: GuidelyProviderProps) {\n const [client, setClient] = useState<GuidelyClient | null>(null);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n const userRef = useRef(user);\n userRef.current = user;\n const flagsOpts = options.flags === false ? null : (options.flags ?? {});\n const payload = flagsPayload ?? flagsOpts?.payload ?? null;\n const payloadRef = useRef(payload);\n payloadRef.current = payload;\n const appAttributes = useMemo(() => ({ ...(flagsOpts?.attributes ?? {}), ...(flagAttributes ?? {}) }), [stableStringify(flagsOpts?.attributes ?? {}), stableStringify(flagAttributes ?? {})]);\n const appAttributesRef = useRef(appAttributes);\n appAttributesRef.current = appAttributes;\n const { projectId, environment, apiUrl } = options;\n\n useEffect(() => {\n if (typeof window === \"undefined\" || !projectId) return;\n const o = optionsRef.current;\n // The user is known up front so flags evaluate for them from the first render (no flicker).\n const c = createGuidely({ ...o, projectId, environment, apiUrl, user: userRef.current?.id ? userRef.current : undefined, flags: o.flags === false ? false : { ...(o.flags ?? {}), payload: payloadRef.current ?? undefined, attributes: appAttributesRef.current } });\n setClient(c);\n return () => {\n c.destroy();\n setClient((cur) => (cur === c ? null : cur));\n };\n }, [projectId, environment, apiUrl]);\n\n useEffect(() => {\n client?.flags.setAttributes(appAttributes);\n }, [client, appAttributes]);\n\n const userKey = user ? JSON.stringify([user.id, user.traits ?? null, user.account ?? null]) : \"\";\n const hadUser = useRef(false);\n useEffect(() => {\n if (!client) return;\n if (user && user.id) {\n hadUser.current = true;\n void client.identify(user.id, user.traits, user.account);\n } else if (hadUser.current) {\n hadUser.current = false;\n client.reset();\n }\n }, [client, userKey]);\n\n const bootstrap = useBootstrap(payload, { ...userAttributes(user), ...appAttributes });\n const flags = useMemo<FlagsSource>(() => ({ client: client?.flags ?? null, bootstrap }), [client, bootstrap]);\n return (\n <GuidelyContext.Provider value={client}>\n <FlagsContext.Provider value={flags}>{children}</FlagsContext.Provider>\n </GuidelyContext.Provider>\n );\n}\n\nconst NOOP_RESULTS: Record<string, unknown> = {\n init: Promise.resolve(),\n ready: Promise.resolve(),\n identify: Promise.resolve(),\n setAccount: Promise.resolve(),\n updateTraits: Promise.resolve(),\n refresh: Promise.resolve(),\n startGuide: false,\n getActiveGuide: null,\n getGuides: [],\n getChecklistProgress: { completedTaskIds: [], completedAt: null, dismissedAt: null },\n isGuideCompleted: false,\n on: () => {},\n getState: { ready: false, destroyed: false },\n getContext: null,\n getSessionId: \"\",\n isRecording: false,\n};\n\n/** Flags that serve every fallback; `useGuidely().flags` before the provider has a client. */\nconst noopFlags = new Proxy(createStaticFlags() as unknown as FlagsClient, {\n get(target, prop, receiver) {\n if (prop in target) return Reflect.get(target, prop, receiver);\n if (prop === \"ready\" || prop === \"refresh\" || prop === \"flush\") return () => Promise.resolve();\n if (prop === \"getPayload\") return () => null;\n if (prop === \"forUser\") return () => target;\n return typeof prop === \"string\" ? () => undefined : undefined;\n },\n});\n\nconst missing = <T,>(key: string, fallback: T): FlagDetails<T> => ({ key, value: fallback, reason: \"FLAG_NOT_FOUND\", inExperiment: false, usedFallback: true });\n\n/** Methods whose no-op answer depends on the arguments (flag getters return the fallback). */\nconst NOOP_FUNCTIONS: Record<string, (...args: any[]) => unknown> = {\n isFeatureEnabled: (_key: string, fallback = false) => fallback,\n getFeatureValue: (_key: string, fallback: unknown) => fallback,\n getFeatureDetails: (key: string, fallback: unknown) => missing(key, fallback),\n onFlagsChange: () => () => {},\n};\n\n/** A client that ignores every call; returned by `useGuidely()` on the server and before the provider mounts. */\nexport const noopClient: GuidelyClient = new Proxy({} as GuidelyClient, {\n get(_target, prop) {\n if (prop === \"options\") return { projectId: \"\" };\n if (prop === \"flags\") return noopFlags;\n if (typeof prop !== \"string\") return undefined;\n return NOOP_FUNCTIONS[prop] ?? (() => NOOP_RESULTS[prop]);\n },\n});\n\n/** The Guidely client, or a no-op client during SSR / before the provider has mounted. */\nexport function useGuidely(): GuidelyClient {\n return useContext(GuidelyContext) ?? noopClient;\n}\n\n/** True once the client has loaded its configuration. */\nexport function useGuidelyReady(): boolean {\n const client = useContext(GuidelyContext);\n const [ready, setReady] = useState(false);\n useEffect(() => {\n if (!client) {\n setReady(false);\n return;\n }\n if (client.getState().ready) setReady(true);\n return client.on(\"ready\", () => setReady(true));\n }, [client]);\n return ready;\n}\n\n/** Identifies the user whenever the arguments change. */\nexport function useGuidelyIdentify(userId: string | null | undefined, traits?: Record<string, unknown>, account?: GuidelyUser[\"account\"]): void {\n const client = useContext(GuidelyContext);\n const key = JSON.stringify([userId ?? null, traits ?? null, account ?? null]);\n useEffect(() => {\n if (!client || !userId) return;\n void client.identify(userId, traits, account);\n }, [client, key]);\n}\n\n/** Subscribes to a client event for the lifetime of the component. */\nexport function useGuidelyEvent<K extends keyof GuidelyEvents>(event: K, handler: GuidelyEvents[K]): void {\n const client = useContext(GuidelyContext);\n const ref = useRef(handler);\n ref.current = handler;\n useEffect(() => {\n if (!client) return;\n const cb = ((...args: unknown[]) => (ref.current as (...a: unknown[]) => void)(...args)) as GuidelyEvents[K];\n return client.on(event, cb);\n }, [client, event]);\n}\n\n/** Live checklist progress for a checklist guide (by id or key). */\nexport function useChecklistProgress(idOrKey: string): ChecklistProgressState {\n const client = useContext(GuidelyContext);\n const [progress, setProgress] = useState<ChecklistProgressState>({ completedTaskIds: [], completedAt: null, dismissedAt: null });\n useEffect(() => {\n if (!client) return;\n const update = () => setProgress(client.getChecklistProgress(idOrKey));\n update();\n const offs = [client.on(\"ready\", update), client.on(\"identify\", update), client.on(\"checklist:task\", update), client.on(\"checklist:complete\", update)];\n return () => offs.forEach((off) => off());\n }, [client, idOrKey]);\n return progress;\n}\n\nexport interface GuideState {\n /** The guide is currently showing. */\n active: boolean;\n completed: boolean;\n stepId: string | null;\n stepIndex: number;\n guide: PublishedGuide | null;\n}\n\n/** Live state of a guide (by id or key): active, completed, current step. */\nexport function useGuideState(idOrKey: string): GuideState {\n const client = useContext(GuidelyContext);\n const [state, setState] = useState<GuideState>({ active: false, completed: false, stepId: null, stepIndex: -1, guide: null });\n useEffect(() => {\n if (!client) return;\n const matches = (g: PublishedGuide) => g.id === idOrKey || g.key === idOrKey;\n const compute = () => {\n const guide = client.getGuides().find(matches) ?? null;\n const active = client.getActiveGuide();\n const s = client.getState().activeGuide;\n setState({ active: !!active && matches(active), completed: client.isGuideCompleted(idOrKey), stepId: active && matches(active) ? ((s as { stepId?: string | null } | null)?.stepId ?? null) : null, stepIndex: active && matches(active) ? ((s as { stepIndex?: number } | null)?.stepIndex ?? -1) : -1, guide });\n };\n compute();\n const offs = ([\"ready\", \"identify\", \"guide:start\", \"guide:step\", \"guide:complete\", \"guide:dismiss\", \"guide:skip\"] as const).map((e) => client.on(e, compute));\n return () => offs.forEach((off) => off());\n }, [client, idOrKey]);\n return state;\n}\n","import { createFlagsClient, createStaticFlags, jsonEquals, stableStringify, type Attributes, type FlagDetails, type FlagValue, type FlagsBootstrap, type FlagsClient, type FlagsClientOptions, type FlagsReader, type JsonValue } from \"@guidelytech/flags\";\nimport { createContext, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore, type ReactNode } from \"react\";\n\n/** What the flag hooks read: the live client once created, else a static snapshot of the bootstrap payload. */\nexport interface FlagsSource {\n client: FlagsClient | null;\n bootstrap: FlagsReader;\n}\n\nexport const FlagsContext = createContext<FlagsSource | null>(null);\n\nconst EMPTY = createStaticFlags();\n\nfunction useReader(): FlagsReader {\n const src = useContext(FlagsContext);\n return src?.client ?? src?.bootstrap ?? EMPTY;\n}\n\n/**\n * Subscribes to the flags with `useSyncExternalStore`. Values are compared deeply so object\n * flags keep their identity between renders. The server snapshot is the bootstrap payload, so\n * server-rendered HTML and hydration agree.\n */\nfunction useFlagsValue<T>(select: (reader: FlagsReader) => T): T {\n const reader = useReader();\n const cache = useRef<{ reader: FlagsReader; value: T } | null>(null);\n const get = () => {\n const value = select(reader);\n const c = cache.current;\n if (c && c.reader === reader && jsonEquals(c.value, value)) return c.value;\n cache.current = { reader, value };\n return value;\n };\n return useSyncExternalStore(reader.subscribe, get, get);\n}\n\n/** Is the boolean flag on? `fallback` (default `false`) until flags load, or when missing. */\nexport function useFlag(key: string, fallback = false): boolean {\n return useFlagsValue((r) => r.isEnabled(key, fallback));\n}\n\n/** A flag's value (string, number, JSON…); `fallback` when missing or of a different type. */\nexport function useFeatureValue<T extends JsonValue>(key: string, fallback: T): FlagValue<T> {\n return useFlagsValue((r) => r.getValue(key, fallback));\n}\n\n/** A flag's value plus why it was served (reason, rule, variation, experiment). */\nexport function useFlagDetails<T extends JsonValue>(key: string, fallback: T): FlagDetails<FlagValue<T>> {\n return useFlagsValue((r) => r.getDetails(key, fallback));\n}\n\n/** Every flag's value (records no exposures). */\nexport function useFlags(): Record<string, JsonValue> {\n return useFlagsValue((r) => r.getAll());\n}\n\n/** True once flags have loaded (or failed to and serve fallbacks); true from the start with a bootstrap payload. */\nexport function useFlagsReady(): boolean {\n return useFlagsValue((r) => r.isReady());\n}\n\n/** The flags client (`null` on the server and before the provider has created it). */\nexport function useFlagsClient(): FlagsClient | null {\n return useContext(FlagsContext)?.client ?? null;\n}\n\nexport interface FeatureProps {\n flag: string;\n children?: ReactNode;\n /** Rendered while the flag is off (default: nothing). */\n fallback?: ReactNode;\n}\n\n/** Renders `children` when the boolean flag is on, else `fallback`. */\nexport function Feature({ flag, children, fallback = null }: FeatureProps) {\n return <>{useFlag(flag) ? children : fallback}</>;\n}\n\n/** Alias of {@link Feature}. */\nexport const FeatureFlag = Feature;\n\nexport interface FeatureValueProps extends FeatureProps {\n /** Renders `children` when the flag's value equals this (deep equality). */\n value: JsonValue;\n}\n\n/** Renders `children` when the flag's value equals `value` (e.g. an experiment variation), else `fallback`. */\nexport function FeatureValue({ flag, value, children, fallback = null }: FeatureValueProps) {\n const current = useFeatureValue<JsonValue>(flag, null);\n return <>{jsonEquals(current, value) ? children : fallback}</>;\n}\n\n/** A static snapshot for SSR and hydration, rebuilt only when the payload or attributes change. */\nexport function useBootstrap(payload: FlagsBootstrap | null | undefined, attributes: Attributes): FlagsReader {\n const key = stableStringify(attributes);\n return useMemo(() => (payload ? createStaticFlags({ payload, attributes }) : EMPTY), [payload, key]);\n}\n\nexport interface FlagsProviderProps extends Omit<FlagsClientOptions, \"payload\"> {\n /** Bootstrap from the server (`getPayload()`): renders the same values on the server and in the browser. */\n payload?: FlagsBootstrap | null;\n /** Use an existing client instead of creating one. */\n client?: FlagsClient;\n children?: ReactNode;\n}\n\n/**\n * Feature flags only (no guides or analytics). Creates a flags client in an effect (never during\n * SSR) and destroys it on unmount; until then hooks read the `payload` snapshot. Changing\n * `attributes` re-evaluates without recreating the client.\n *\n * ```tsx\n * <FlagsProvider sdkKey=\"pk_prod_…\" attributes={{ id: user.id, plan: user.plan }} payload={bootstrap}>\n * ```\n */\nexport function FlagsProvider({ children, client: external, payload, ...options }: FlagsProviderProps) {\n const [created, setCreated] = useState<FlagsClient | null>(null);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n const sdkKey = options.sdkKey ?? options.projectId;\n const { apiUrl, environment } = options;\n const payloadRef = useRef(payload);\n payloadRef.current = payload;\n\n useEffect(() => {\n if (external || !sdkKey || typeof window === \"undefined\") return;\n let c: FlagsClient;\n try {\n c = createFlagsClient({ ...optionsRef.current, sdkKey, payload: payloadRef.current ?? undefined });\n } catch (err) {\n optionsRef.current.onError?.(err instanceof Error ? err : new Error(String(err)));\n return;\n }\n setCreated(c);\n return () => {\n c.destroy();\n setCreated((cur) => (cur === c ? null : cur));\n };\n }, [external, sdkKey, apiUrl, environment]);\n\n const client = external ?? created;\n const attributes = options.attributes ?? {};\n const attrsKey = stableStringify(attributes);\n useEffect(() => {\n if (client && options.attributes) client.setAttributes(options.attributes);\n }, [client, attrsKey]);\n\n const bootstrap = useBootstrap(payload, attributes);\n const value = useMemo<FlagsSource>(() => ({ client, bootstrap }), [client, bootstrap]);\n return <FlagsContext.Provider value={value}>{children}</FlagsContext.Provider>;\n}\n"],"mappings":"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","names":["import_flags","import_react","import_jsx_runtime"]}
package/dist/index.d.cts CHANGED
@@ -1,18 +1,73 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
+ import { JsonValue, FlagsClientOptions, FlagsBootstrap, FlagsClient, FlagValue, FlagDetails, Attributes } from '@guidelytech/flags';
4
+ export { Attributes, EvaluationResult, FlagDetails, FlagsBootstrap, FlagsClient, FlagsClientOptions, FlagsPayload, JsonValue } from '@guidelytech/flags';
3
5
  import { PublishedGuide, GuidelyOptions, GuidelyUser, GuidelyClient, ChecklistProgressState, GuidelyEvents } from '@guidelytech/sdk-core';
4
6
  export { ChecklistProgressState, GuidelyClient, GuidelyEvents, GuidelyOptions, GuidelyUser, PublishedGuide } from '@guidelytech/sdk-core';
5
7
 
8
+ /** Is the boolean flag on? `fallback` (default `false`) until flags load, or when missing. */
9
+ declare function useFlag(key: string, fallback?: boolean): boolean;
10
+ /** A flag's value (string, number, JSON…); `fallback` when missing or of a different type. */
11
+ declare function useFeatureValue<T extends JsonValue>(key: string, fallback: T): FlagValue<T>;
12
+ /** A flag's value plus why it was served (reason, rule, variation, experiment). */
13
+ declare function useFlagDetails<T extends JsonValue>(key: string, fallback: T): FlagDetails<FlagValue<T>>;
14
+ /** Every flag's value (records no exposures). */
15
+ declare function useFlags(): Record<string, JsonValue>;
16
+ /** True once flags have loaded (or failed to and serve fallbacks); true from the start with a bootstrap payload. */
17
+ declare function useFlagsReady(): boolean;
18
+ /** The flags client (`null` on the server and before the provider has created it). */
19
+ declare function useFlagsClient(): FlagsClient | null;
20
+ interface FeatureProps {
21
+ flag: string;
22
+ children?: ReactNode;
23
+ /** Rendered while the flag is off (default: nothing). */
24
+ fallback?: ReactNode;
25
+ }
26
+ /** Renders `children` when the boolean flag is on, else `fallback`. */
27
+ declare function Feature({ flag, children, fallback }: FeatureProps): react.JSX.Element;
28
+ /** Alias of {@link Feature}. */
29
+ declare const FeatureFlag: typeof Feature;
30
+ interface FeatureValueProps extends FeatureProps {
31
+ /** Renders `children` when the flag's value equals this (deep equality). */
32
+ value: JsonValue;
33
+ }
34
+ /** Renders `children` when the flag's value equals `value` (e.g. an experiment variation), else `fallback`. */
35
+ declare function FeatureValue({ flag, value, children, fallback }: FeatureValueProps): react.JSX.Element;
36
+ interface FlagsProviderProps extends Omit<FlagsClientOptions, "payload"> {
37
+ /** Bootstrap from the server (`getPayload()`): renders the same values on the server and in the browser. */
38
+ payload?: FlagsBootstrap | null;
39
+ /** Use an existing client instead of creating one. */
40
+ client?: FlagsClient;
41
+ children?: ReactNode;
42
+ }
43
+ /**
44
+ * Feature flags only (no guides or analytics). Creates a flags client in an effect (never during
45
+ * SSR) and destroys it on unmount; until then hooks read the `payload` snapshot. Changing
46
+ * `attributes` re-evaluates without recreating the client.
47
+ *
48
+ * ```tsx
49
+ * <FlagsProvider sdkKey="pk_prod_…" attributes={{ id: user.id, plan: user.plan }} payload={bootstrap}>
50
+ * ```
51
+ */
52
+ declare function FlagsProvider({ children, client: external, payload, ...options }: FlagsProviderProps): react.JSX.Element;
53
+
6
54
  interface GuidelyProviderProps extends Omit<GuidelyOptions, "user"> {
7
55
  /** Identify this user as soon as the client is ready; `undefined` resets the identity. */
8
56
  user?: GuidelyUser | null;
57
+ /**
58
+ * Flags bootstrap from the server (`getFlagsBootstrap()` / `client.getPayload()`): flag hooks
59
+ * render the same values on the server, during hydration and after.
60
+ */
61
+ flagsPayload?: FlagsBootstrap | null;
62
+ /** Extra flag attributes (merged over the automatic identity, page and device attributes). */
63
+ flagAttributes?: Attributes;
9
64
  children?: ReactNode;
10
65
  }
11
66
  /**
12
67
  * Creates one Guidely client for the subtree. The client is created inside an effect (never
13
68
  * during SSR) and destroyed on unmount, so React Strict Mode's double-invoke is harmless.
14
69
  */
15
- declare function GuidelyProvider({ children, user, ...options }: GuidelyProviderProps): react.JSX.Element;
70
+ declare function GuidelyProvider({ children, user, flagsPayload, flagAttributes, ...options }: GuidelyProviderProps): react.JSX.Element;
16
71
  /** A client that ignores every call; returned by `useGuidely()` on the server and before the provider mounts. */
17
72
  declare const noopClient: GuidelyClient;
18
73
  /** The Guidely client, or a no-op client during SSR / before the provider has mounted. */
@@ -36,4 +91,4 @@ interface GuideState {
36
91
  /** Live state of a guide (by id or key): active, completed, current step. */
37
92
  declare function useGuideState(idOrKey: string): GuideState;
38
93
 
39
- export { type GuideState, GuidelyProvider, type GuidelyProviderProps, noopClient, useChecklistProgress, useGuideState, useGuidely, useGuidelyEvent, useGuidelyIdentify, useGuidelyReady };
94
+ export { Feature, FeatureFlag, type FeatureProps, FeatureValue, type FeatureValueProps, FlagsProvider, type FlagsProviderProps, type GuideState, GuidelyProvider, type GuidelyProviderProps, noopClient, useChecklistProgress, useFeatureValue, useFlag, useFlagDetails, useFlags, useFlagsClient, useFlagsReady, useGuideState, useGuidely, useGuidelyEvent, useGuidelyIdentify, useGuidelyReady };
package/dist/index.d.ts CHANGED
@@ -1,18 +1,73 @@
1
1
  import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
+ import { JsonValue, FlagsClientOptions, FlagsBootstrap, FlagsClient, FlagValue, FlagDetails, Attributes } from '@guidelytech/flags';
4
+ export { Attributes, EvaluationResult, FlagDetails, FlagsBootstrap, FlagsClient, FlagsClientOptions, FlagsPayload, JsonValue } from '@guidelytech/flags';
3
5
  import { PublishedGuide, GuidelyOptions, GuidelyUser, GuidelyClient, ChecklistProgressState, GuidelyEvents } from '@guidelytech/sdk-core';
4
6
  export { ChecklistProgressState, GuidelyClient, GuidelyEvents, GuidelyOptions, GuidelyUser, PublishedGuide } from '@guidelytech/sdk-core';
5
7
 
8
+ /** Is the boolean flag on? `fallback` (default `false`) until flags load, or when missing. */
9
+ declare function useFlag(key: string, fallback?: boolean): boolean;
10
+ /** A flag's value (string, number, JSON…); `fallback` when missing or of a different type. */
11
+ declare function useFeatureValue<T extends JsonValue>(key: string, fallback: T): FlagValue<T>;
12
+ /** A flag's value plus why it was served (reason, rule, variation, experiment). */
13
+ declare function useFlagDetails<T extends JsonValue>(key: string, fallback: T): FlagDetails<FlagValue<T>>;
14
+ /** Every flag's value (records no exposures). */
15
+ declare function useFlags(): Record<string, JsonValue>;
16
+ /** True once flags have loaded (or failed to and serve fallbacks); true from the start with a bootstrap payload. */
17
+ declare function useFlagsReady(): boolean;
18
+ /** The flags client (`null` on the server and before the provider has created it). */
19
+ declare function useFlagsClient(): FlagsClient | null;
20
+ interface FeatureProps {
21
+ flag: string;
22
+ children?: ReactNode;
23
+ /** Rendered while the flag is off (default: nothing). */
24
+ fallback?: ReactNode;
25
+ }
26
+ /** Renders `children` when the boolean flag is on, else `fallback`. */
27
+ declare function Feature({ flag, children, fallback }: FeatureProps): react.JSX.Element;
28
+ /** Alias of {@link Feature}. */
29
+ declare const FeatureFlag: typeof Feature;
30
+ interface FeatureValueProps extends FeatureProps {
31
+ /** Renders `children` when the flag's value equals this (deep equality). */
32
+ value: JsonValue;
33
+ }
34
+ /** Renders `children` when the flag's value equals `value` (e.g. an experiment variation), else `fallback`. */
35
+ declare function FeatureValue({ flag, value, children, fallback }: FeatureValueProps): react.JSX.Element;
36
+ interface FlagsProviderProps extends Omit<FlagsClientOptions, "payload"> {
37
+ /** Bootstrap from the server (`getPayload()`): renders the same values on the server and in the browser. */
38
+ payload?: FlagsBootstrap | null;
39
+ /** Use an existing client instead of creating one. */
40
+ client?: FlagsClient;
41
+ children?: ReactNode;
42
+ }
43
+ /**
44
+ * Feature flags only (no guides or analytics). Creates a flags client in an effect (never during
45
+ * SSR) and destroys it on unmount; until then hooks read the `payload` snapshot. Changing
46
+ * `attributes` re-evaluates without recreating the client.
47
+ *
48
+ * ```tsx
49
+ * <FlagsProvider sdkKey="pk_prod_…" attributes={{ id: user.id, plan: user.plan }} payload={bootstrap}>
50
+ * ```
51
+ */
52
+ declare function FlagsProvider({ children, client: external, payload, ...options }: FlagsProviderProps): react.JSX.Element;
53
+
6
54
  interface GuidelyProviderProps extends Omit<GuidelyOptions, "user"> {
7
55
  /** Identify this user as soon as the client is ready; `undefined` resets the identity. */
8
56
  user?: GuidelyUser | null;
57
+ /**
58
+ * Flags bootstrap from the server (`getFlagsBootstrap()` / `client.getPayload()`): flag hooks
59
+ * render the same values on the server, during hydration and after.
60
+ */
61
+ flagsPayload?: FlagsBootstrap | null;
62
+ /** Extra flag attributes (merged over the automatic identity, page and device attributes). */
63
+ flagAttributes?: Attributes;
9
64
  children?: ReactNode;
10
65
  }
11
66
  /**
12
67
  * Creates one Guidely client for the subtree. The client is created inside an effect (never
13
68
  * during SSR) and destroyed on unmount, so React Strict Mode's double-invoke is harmless.
14
69
  */
15
- declare function GuidelyProvider({ children, user, ...options }: GuidelyProviderProps): react.JSX.Element;
70
+ declare function GuidelyProvider({ children, user, flagsPayload, flagAttributes, ...options }: GuidelyProviderProps): react.JSX.Element;
16
71
  /** A client that ignores every call; returned by `useGuidely()` on the server and before the provider mounts. */
17
72
  declare const noopClient: GuidelyClient;
18
73
  /** The Guidely client, or a no-op client during SSR / before the provider has mounted. */
@@ -36,4 +91,4 @@ interface GuideState {
36
91
  /** Live state of a guide (by id or key): active, completed, current step. */
37
92
  declare function useGuideState(idOrKey: string): GuideState;
38
93
 
39
- export { type GuideState, GuidelyProvider, type GuidelyProviderProps, noopClient, useChecklistProgress, useGuideState, useGuidely, useGuidelyEvent, useGuidelyIdentify, useGuidelyReady };
94
+ export { Feature, FeatureFlag, type FeatureProps, FeatureValue, type FeatureValueProps, FlagsProvider, type FlagsProviderProps, type GuideState, GuidelyProvider, type GuidelyProviderProps, noopClient, useChecklistProgress, useFeatureValue, useFlag, useFlagDetails, useFlags, useFlagsClient, useFlagsReady, useGuideState, useGuidely, useGuidelyEvent, useGuidelyIdentify, useGuidelyReady };
package/dist/index.js CHANGED
@@ -1,27 +1,133 @@
1
1
  "use client";
2
2
 
3
3
  // src/index.tsx
4
+ import { createStaticFlags as createStaticFlags2, stableStringify as stableStringify2 } from "@guidelytech/flags";
4
5
  import { createGuidely } from "@guidelytech/sdk-core";
5
- import { createContext, useContext, useEffect, useRef, useState } from "react";
6
- import { jsx } from "react/jsx-runtime";
7
- var GuidelyContext = createContext(null);
8
- function GuidelyProvider({ children, user, ...options }) {
9
- const [client, setClient] = useState(null);
6
+ import { createContext as createContext2, useContext as useContext2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
7
+
8
+ // src/flags.tsx
9
+ import { createFlagsClient, createStaticFlags, jsonEquals, stableStringify } from "@guidelytech/flags";
10
+ import { createContext, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
11
+ import { Fragment, jsx } from "react/jsx-runtime";
12
+ var FlagsContext = createContext(null);
13
+ var EMPTY = createStaticFlags();
14
+ function useReader() {
15
+ const src = useContext(FlagsContext);
16
+ return src?.client ?? src?.bootstrap ?? EMPTY;
17
+ }
18
+ function useFlagsValue(select) {
19
+ const reader = useReader();
20
+ const cache = useRef(null);
21
+ const get = () => {
22
+ const value = select(reader);
23
+ const c = cache.current;
24
+ if (c && c.reader === reader && jsonEquals(c.value, value)) return c.value;
25
+ cache.current = { reader, value };
26
+ return value;
27
+ };
28
+ return useSyncExternalStore(reader.subscribe, get, get);
29
+ }
30
+ function useFlag(key, fallback = false) {
31
+ return useFlagsValue((r) => r.isEnabled(key, fallback));
32
+ }
33
+ function useFeatureValue(key, fallback) {
34
+ return useFlagsValue((r) => r.getValue(key, fallback));
35
+ }
36
+ function useFlagDetails(key, fallback) {
37
+ return useFlagsValue((r) => r.getDetails(key, fallback));
38
+ }
39
+ function useFlags() {
40
+ return useFlagsValue((r) => r.getAll());
41
+ }
42
+ function useFlagsReady() {
43
+ return useFlagsValue((r) => r.isReady());
44
+ }
45
+ function useFlagsClient() {
46
+ return useContext(FlagsContext)?.client ?? null;
47
+ }
48
+ function Feature({ flag, children, fallback = null }) {
49
+ return /* @__PURE__ */ jsx(Fragment, { children: useFlag(flag) ? children : fallback });
50
+ }
51
+ var FeatureFlag = Feature;
52
+ function FeatureValue({ flag, value, children, fallback = null }) {
53
+ const current = useFeatureValue(flag, null);
54
+ return /* @__PURE__ */ jsx(Fragment, { children: jsonEquals(current, value) ? children : fallback });
55
+ }
56
+ function useBootstrap(payload, attributes) {
57
+ const key = stableStringify(attributes);
58
+ return useMemo(() => payload ? createStaticFlags({ payload, attributes }) : EMPTY, [payload, key]);
59
+ }
60
+ function FlagsProvider({ children, client: external, payload, ...options }) {
61
+ const [created, setCreated] = useState(null);
10
62
  const optionsRef = useRef(options);
11
63
  optionsRef.current = options;
12
- const { projectId, environment, apiUrl } = options;
64
+ const sdkKey = options.sdkKey ?? options.projectId;
65
+ const { apiUrl, environment } = options;
66
+ const payloadRef = useRef(payload);
67
+ payloadRef.current = payload;
13
68
  useEffect(() => {
69
+ if (external || !sdkKey || typeof window === "undefined") return;
70
+ let c;
71
+ try {
72
+ c = createFlagsClient({ ...optionsRef.current, sdkKey, payload: payloadRef.current ?? void 0 });
73
+ } catch (err) {
74
+ optionsRef.current.onError?.(err instanceof Error ? err : new Error(String(err)));
75
+ return;
76
+ }
77
+ setCreated(c);
78
+ return () => {
79
+ c.destroy();
80
+ setCreated((cur) => cur === c ? null : cur);
81
+ };
82
+ }, [external, sdkKey, apiUrl, environment]);
83
+ const client = external ?? created;
84
+ const attributes = options.attributes ?? {};
85
+ const attrsKey = stableStringify(attributes);
86
+ useEffect(() => {
87
+ if (client && options.attributes) client.setAttributes(options.attributes);
88
+ }, [client, attrsKey]);
89
+ const bootstrap = useBootstrap(payload, attributes);
90
+ const value = useMemo(() => ({ client, bootstrap }), [client, bootstrap]);
91
+ return /* @__PURE__ */ jsx(FlagsContext.Provider, { value, children });
92
+ }
93
+
94
+ // src/index.tsx
95
+ import { jsx as jsx2 } from "react/jsx-runtime";
96
+ var GuidelyContext = createContext2(null);
97
+ function userAttributes(user) {
98
+ if (!user?.id) return {};
99
+ return { ...user.traits ?? {}, id: user.id, ...user.account ? { account: { ...user.account.traits ?? {}, id: user.account.id } } : {} };
100
+ }
101
+ function GuidelyProvider({ children, user, flagsPayload, flagAttributes, ...options }) {
102
+ const [client, setClient] = useState2(null);
103
+ const optionsRef = useRef2(options);
104
+ optionsRef.current = options;
105
+ const userRef = useRef2(user);
106
+ userRef.current = user;
107
+ const flagsOpts = options.flags === false ? null : options.flags ?? {};
108
+ const payload = flagsPayload ?? flagsOpts?.payload ?? null;
109
+ const payloadRef = useRef2(payload);
110
+ payloadRef.current = payload;
111
+ const appAttributes = useMemo2(() => ({ ...flagsOpts?.attributes ?? {}, ...flagAttributes ?? {} }), [stableStringify2(flagsOpts?.attributes ?? {}), stableStringify2(flagAttributes ?? {})]);
112
+ const appAttributesRef = useRef2(appAttributes);
113
+ appAttributesRef.current = appAttributes;
114
+ const { projectId, environment, apiUrl } = options;
115
+ useEffect2(() => {
14
116
  if (typeof window === "undefined" || !projectId) return;
15
- const c = createGuidely({ ...optionsRef.current, projectId, environment, apiUrl });
117
+ const o = optionsRef.current;
118
+ const c = createGuidely({ ...o, projectId, environment, apiUrl, user: userRef.current?.id ? userRef.current : void 0, flags: o.flags === false ? false : { ...o.flags ?? {}, payload: payloadRef.current ?? void 0, attributes: appAttributesRef.current } });
16
119
  setClient(c);
17
120
  return () => {
18
121
  c.destroy();
19
122
  setClient((cur) => cur === c ? null : cur);
20
123
  };
21
124
  }, [projectId, environment, apiUrl]);
125
+ useEffect2(() => {
126
+ client?.flags.setAttributes(appAttributes);
127
+ }, [client, appAttributes]);
22
128
  const userKey = user ? JSON.stringify([user.id, user.traits ?? null, user.account ?? null]) : "";
23
- const hadUser = useRef(false);
24
- useEffect(() => {
129
+ const hadUser = useRef2(false);
130
+ useEffect2(() => {
25
131
  if (!client) return;
26
132
  if (user && user.id) {
27
133
  hadUser.current = true;
@@ -31,7 +137,9 @@ function GuidelyProvider({ children, user, ...options }) {
31
137
  client.reset();
32
138
  }
33
139
  }, [client, userKey]);
34
- return /* @__PURE__ */ jsx(GuidelyContext.Provider, { value: client, children });
140
+ const bootstrap = useBootstrap(payload, { ...userAttributes(user), ...appAttributes });
141
+ const flags = useMemo2(() => ({ client: client?.flags ?? null, bootstrap }), [client, bootstrap]);
142
+ return /* @__PURE__ */ jsx2(GuidelyContext.Provider, { value: client, children: /* @__PURE__ */ jsx2(FlagsContext.Provider, { value: flags, children }) });
35
143
  }
36
144
  var NOOP_RESULTS = {
37
145
  init: Promise.resolve(),
@@ -52,20 +160,38 @@ var NOOP_RESULTS = {
52
160
  getSessionId: "",
53
161
  isRecording: false
54
162
  };
163
+ var noopFlags = new Proxy(createStaticFlags2(), {
164
+ get(target, prop, receiver) {
165
+ if (prop in target) return Reflect.get(target, prop, receiver);
166
+ if (prop === "ready" || prop === "refresh" || prop === "flush") return () => Promise.resolve();
167
+ if (prop === "getPayload") return () => null;
168
+ if (prop === "forUser") return () => target;
169
+ return typeof prop === "string" ? () => void 0 : void 0;
170
+ }
171
+ });
172
+ var missing = (key, fallback) => ({ key, value: fallback, reason: "FLAG_NOT_FOUND", inExperiment: false, usedFallback: true });
173
+ var NOOP_FUNCTIONS = {
174
+ isFeatureEnabled: (_key, fallback = false) => fallback,
175
+ getFeatureValue: (_key, fallback) => fallback,
176
+ getFeatureDetails: (key, fallback) => missing(key, fallback),
177
+ onFlagsChange: () => () => {
178
+ }
179
+ };
55
180
  var noopClient = new Proxy({}, {
56
181
  get(_target, prop) {
57
182
  if (prop === "options") return { projectId: "" };
183
+ if (prop === "flags") return noopFlags;
58
184
  if (typeof prop !== "string") return void 0;
59
- return () => NOOP_RESULTS[prop];
185
+ return NOOP_FUNCTIONS[prop] ?? (() => NOOP_RESULTS[prop]);
60
186
  }
61
187
  });
62
188
  function useGuidely() {
63
- return useContext(GuidelyContext) ?? noopClient;
189
+ return useContext2(GuidelyContext) ?? noopClient;
64
190
  }
65
191
  function useGuidelyReady() {
66
- const client = useContext(GuidelyContext);
67
- const [ready, setReady] = useState(false);
68
- useEffect(() => {
192
+ const client = useContext2(GuidelyContext);
193
+ const [ready, setReady] = useState2(false);
194
+ useEffect2(() => {
69
195
  if (!client) {
70
196
  setReady(false);
71
197
  return;
@@ -76,27 +202,27 @@ function useGuidelyReady() {
76
202
  return ready;
77
203
  }
78
204
  function useGuidelyIdentify(userId, traits, account) {
79
- const client = useContext(GuidelyContext);
205
+ const client = useContext2(GuidelyContext);
80
206
  const key = JSON.stringify([userId ?? null, traits ?? null, account ?? null]);
81
- useEffect(() => {
207
+ useEffect2(() => {
82
208
  if (!client || !userId) return;
83
209
  void client.identify(userId, traits, account);
84
210
  }, [client, key]);
85
211
  }
86
212
  function useGuidelyEvent(event, handler) {
87
- const client = useContext(GuidelyContext);
88
- const ref = useRef(handler);
213
+ const client = useContext2(GuidelyContext);
214
+ const ref = useRef2(handler);
89
215
  ref.current = handler;
90
- useEffect(() => {
216
+ useEffect2(() => {
91
217
  if (!client) return;
92
218
  const cb = ((...args) => ref.current(...args));
93
219
  return client.on(event, cb);
94
220
  }, [client, event]);
95
221
  }
96
222
  function useChecklistProgress(idOrKey) {
97
- const client = useContext(GuidelyContext);
98
- const [progress, setProgress] = useState({ completedTaskIds: [], completedAt: null, dismissedAt: null });
99
- useEffect(() => {
223
+ const client = useContext2(GuidelyContext);
224
+ const [progress, setProgress] = useState2({ completedTaskIds: [], completedAt: null, dismissedAt: null });
225
+ useEffect2(() => {
100
226
  if (!client) return;
101
227
  const update = () => setProgress(client.getChecklistProgress(idOrKey));
102
228
  update();
@@ -106,9 +232,9 @@ function useChecklistProgress(idOrKey) {
106
232
  return progress;
107
233
  }
108
234
  function useGuideState(idOrKey) {
109
- const client = useContext(GuidelyContext);
110
- const [state, setState] = useState({ active: false, completed: false, stepId: null, stepIndex: -1, guide: null });
111
- useEffect(() => {
235
+ const client = useContext2(GuidelyContext);
236
+ const [state, setState] = useState2({ active: false, completed: false, stepId: null, stepIndex: -1, guide: null });
237
+ useEffect2(() => {
112
238
  if (!client) return;
113
239
  const matches = (g) => g.id === idOrKey || g.key === idOrKey;
114
240
  const compute = () => {
@@ -124,9 +250,19 @@ function useGuideState(idOrKey) {
124
250
  return state;
125
251
  }
126
252
  export {
253
+ Feature,
254
+ FeatureFlag,
255
+ FeatureValue,
256
+ FlagsProvider,
127
257
  GuidelyProvider,
128
258
  noopClient,
129
259
  useChecklistProgress,
260
+ useFeatureValue,
261
+ useFlag,
262
+ useFlagDetails,
263
+ useFlags,
264
+ useFlagsClient,
265
+ useFlagsReady,
130
266
  useGuideState,
131
267
  useGuidely,
132
268
  useGuidelyEvent,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.tsx"],"sourcesContent":["import { createGuidely, type ChecklistProgressState, type GuidelyClient, type GuidelyEvents, type GuidelyOptions, type GuidelyUser, type PublishedGuide } from \"@guidelytech/sdk-core\";\nimport { createContext, useContext, useEffect, useRef, useState, type ReactNode } from \"react\";\n\nexport type { GuidelyClient, GuidelyOptions, GuidelyEvents, GuidelyUser, ChecklistProgressState, PublishedGuide } from \"@guidelytech/sdk-core\";\n\nconst GuidelyContext = createContext<GuidelyClient | null>(null);\n\nexport interface GuidelyProviderProps extends Omit<GuidelyOptions, \"user\"> {\n /** Identify this user as soon as the client is ready; `undefined` resets the identity. */\n user?: GuidelyUser | null;\n children?: ReactNode;\n}\n\n/**\n * Creates one Guidely client for the subtree. The client is created inside an effect (never\n * during SSR) and destroyed on unmount, so React Strict Mode's double-invoke is harmless.\n */\nexport function GuidelyProvider({ children, user, ...options }: GuidelyProviderProps) {\n const [client, setClient] = useState<GuidelyClient | null>(null);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n const { projectId, environment, apiUrl } = options;\n\n useEffect(() => {\n if (typeof window === \"undefined\" || !projectId) return;\n const c = createGuidely({ ...optionsRef.current, projectId, environment, apiUrl });\n setClient(c);\n return () => {\n c.destroy();\n setClient((cur) => (cur === c ? null : cur));\n };\n }, [projectId, environment, apiUrl]);\n\n const userKey = user ? JSON.stringify([user.id, user.traits ?? null, user.account ?? null]) : \"\";\n const hadUser = useRef(false);\n useEffect(() => {\n if (!client) return;\n if (user && user.id) {\n hadUser.current = true;\n void client.identify(user.id, user.traits, user.account);\n } else if (hadUser.current) {\n hadUser.current = false;\n client.reset();\n }\n }, [client, userKey]);\n\n return <GuidelyContext.Provider value={client}>{children}</GuidelyContext.Provider>;\n}\n\nconst NOOP_RESULTS: Record<string, unknown> = {\n init: Promise.resolve(),\n ready: Promise.resolve(),\n identify: Promise.resolve(),\n setAccount: Promise.resolve(),\n updateTraits: Promise.resolve(),\n refresh: Promise.resolve(),\n startGuide: false,\n getActiveGuide: null,\n getGuides: [],\n getChecklistProgress: { completedTaskIds: [], completedAt: null, dismissedAt: null },\n isGuideCompleted: false,\n on: () => {},\n getState: { ready: false, destroyed: false },\n getContext: null,\n getSessionId: \"\",\n isRecording: false,\n};\n\n/** A client that ignores every call; returned by `useGuidely()` on the server and before the provider mounts. */\nexport const noopClient: GuidelyClient = new Proxy({} as GuidelyClient, {\n get(_target, prop) {\n if (prop === \"options\") return { projectId: \"\" };\n if (typeof prop !== \"string\") return undefined;\n return () => NOOP_RESULTS[prop];\n },\n});\n\n/** The Guidely client, or a no-op client during SSR / before the provider has mounted. */\nexport function useGuidely(): GuidelyClient {\n return useContext(GuidelyContext) ?? noopClient;\n}\n\n/** True once the client has loaded its configuration. */\nexport function useGuidelyReady(): boolean {\n const client = useContext(GuidelyContext);\n const [ready, setReady] = useState(false);\n useEffect(() => {\n if (!client) {\n setReady(false);\n return;\n }\n if (client.getState().ready) setReady(true);\n return client.on(\"ready\", () => setReady(true));\n }, [client]);\n return ready;\n}\n\n/** Identifies the user whenever the arguments change. */\nexport function useGuidelyIdentify(userId: string | null | undefined, traits?: Record<string, unknown>, account?: GuidelyUser[\"account\"]): void {\n const client = useContext(GuidelyContext);\n const key = JSON.stringify([userId ?? null, traits ?? null, account ?? null]);\n useEffect(() => {\n if (!client || !userId) return;\n void client.identify(userId, traits, account);\n }, [client, key]);\n}\n\n/** Subscribes to a client event for the lifetime of the component. */\nexport function useGuidelyEvent<K extends keyof GuidelyEvents>(event: K, handler: GuidelyEvents[K]): void {\n const client = useContext(GuidelyContext);\n const ref = useRef(handler);\n ref.current = handler;\n useEffect(() => {\n if (!client) return;\n const cb = ((...args: unknown[]) => (ref.current as (...a: unknown[]) => void)(...args)) as GuidelyEvents[K];\n return client.on(event, cb);\n }, [client, event]);\n}\n\n/** Live checklist progress for a checklist guide (by id or key). */\nexport function useChecklistProgress(idOrKey: string): ChecklistProgressState {\n const client = useContext(GuidelyContext);\n const [progress, setProgress] = useState<ChecklistProgressState>({ completedTaskIds: [], completedAt: null, dismissedAt: null });\n useEffect(() => {\n if (!client) return;\n const update = () => setProgress(client.getChecklistProgress(idOrKey));\n update();\n const offs = [client.on(\"ready\", update), client.on(\"identify\", update), client.on(\"checklist:task\", update), client.on(\"checklist:complete\", update)];\n return () => offs.forEach((off) => off());\n }, [client, idOrKey]);\n return progress;\n}\n\nexport interface GuideState {\n /** The guide is currently showing. */\n active: boolean;\n completed: boolean;\n stepId: string | null;\n stepIndex: number;\n guide: PublishedGuide | null;\n}\n\n/** Live state of a guide (by id or key): active, completed, current step. */\nexport function useGuideState(idOrKey: string): GuideState {\n const client = useContext(GuidelyContext);\n const [state, setState] = useState<GuideState>({ active: false, completed: false, stepId: null, stepIndex: -1, guide: null });\n useEffect(() => {\n if (!client) return;\n const matches = (g: PublishedGuide) => g.id === idOrKey || g.key === idOrKey;\n const compute = () => {\n const guide = client.getGuides().find(matches) ?? null;\n const active = client.getActiveGuide();\n const s = client.getState().activeGuide;\n setState({ active: !!active && matches(active), completed: client.isGuideCompleted(idOrKey), stepId: active && matches(active) ? ((s as { stepId?: string | null } | null)?.stepId ?? null) : null, stepIndex: active && matches(active) ? ((s as { stepIndex?: number } | null)?.stepIndex ?? -1) : -1, guide });\n };\n compute();\n const offs = ([\"ready\", \"identify\", \"guide:start\", \"guide:step\", \"guide:complete\", \"guide:dismiss\", \"guide:skip\"] as const).map((e) => client.on(e, compute));\n return () => offs.forEach((off) => off());\n }, [client, idOrKey]);\n return state;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.tsx","../src/flags.tsx"],"sourcesContent":["import { createStaticFlags, stableStringify, type Attributes, type FlagDetails, type FlagsBootstrap, type FlagsClient } from \"@guidelytech/flags\";\nimport { createGuidely, type ChecklistProgressState, type GuidelyClient, type GuidelyEvents, type GuidelyOptions, type GuidelyUser, type PublishedGuide } from \"@guidelytech/sdk-core\";\nimport { createContext, useContext, useEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport { FlagsContext, useBootstrap, type FlagsSource } from \"./flags\";\n\nexport type { GuidelyClient, GuidelyOptions, GuidelyEvents, GuidelyUser, ChecklistProgressState, PublishedGuide } from \"@guidelytech/sdk-core\";\nexport { useFlag, useFeatureValue, useFlagDetails, useFlags, useFlagsReady, useFlagsClient, Feature, FeatureFlag, FeatureValue, FlagsProvider } from \"./flags\";\nexport type { FeatureProps, FeatureValueProps, FlagsProviderProps } from \"./flags\";\nexport type { FlagsClient, FlagsClientOptions, FlagDetails, FlagsBootstrap, FlagsPayload, Attributes, JsonValue, EvaluationResult } from \"@guidelytech/flags\";\n\nconst GuidelyContext = createContext<GuidelyClient | null>(null);\n\nexport interface GuidelyProviderProps extends Omit<GuidelyOptions, \"user\"> {\n /** Identify this user as soon as the client is ready; `undefined` resets the identity. */\n user?: GuidelyUser | null;\n /**\n * Flags bootstrap from the server (`getFlagsBootstrap()` / `client.getPayload()`): flag hooks\n * render the same values on the server, during hydration and after.\n */\n flagsPayload?: FlagsBootstrap | null;\n /** Extra flag attributes (merged over the automatic identity, page and device attributes). */\n flagAttributes?: Attributes;\n children?: ReactNode;\n}\n\n/** The attributes the server knows about a user: what bootstrap snapshots evaluate with. */\nfunction userAttributes(user: GuidelyUser | null | undefined): Attributes {\n if (!user?.id) return {};\n return { ...(user.traits ?? {}), id: user.id, ...(user.account ? { account: { ...(user.account.traits ?? {}), id: user.account.id } } : {}) };\n}\n\n/**\n * Creates one Guidely client for the subtree. The client is created inside an effect (never\n * during SSR) and destroyed on unmount, so React Strict Mode's double-invoke is harmless.\n */\nexport function GuidelyProvider({ children, user, flagsPayload, flagAttributes, ...options }: GuidelyProviderProps) {\n const [client, setClient] = useState<GuidelyClient | null>(null);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n const userRef = useRef(user);\n userRef.current = user;\n const flagsOpts = options.flags === false ? null : (options.flags ?? {});\n const payload = flagsPayload ?? flagsOpts?.payload ?? null;\n const payloadRef = useRef(payload);\n payloadRef.current = payload;\n const appAttributes = useMemo(() => ({ ...(flagsOpts?.attributes ?? {}), ...(flagAttributes ?? {}) }), [stableStringify(flagsOpts?.attributes ?? {}), stableStringify(flagAttributes ?? {})]);\n const appAttributesRef = useRef(appAttributes);\n appAttributesRef.current = appAttributes;\n const { projectId, environment, apiUrl } = options;\n\n useEffect(() => {\n if (typeof window === \"undefined\" || !projectId) return;\n const o = optionsRef.current;\n // The user is known up front so flags evaluate for them from the first render (no flicker).\n const c = createGuidely({ ...o, projectId, environment, apiUrl, user: userRef.current?.id ? userRef.current : undefined, flags: o.flags === false ? false : { ...(o.flags ?? {}), payload: payloadRef.current ?? undefined, attributes: appAttributesRef.current } });\n setClient(c);\n return () => {\n c.destroy();\n setClient((cur) => (cur === c ? null : cur));\n };\n }, [projectId, environment, apiUrl]);\n\n useEffect(() => {\n client?.flags.setAttributes(appAttributes);\n }, [client, appAttributes]);\n\n const userKey = user ? JSON.stringify([user.id, user.traits ?? null, user.account ?? null]) : \"\";\n const hadUser = useRef(false);\n useEffect(() => {\n if (!client) return;\n if (user && user.id) {\n hadUser.current = true;\n void client.identify(user.id, user.traits, user.account);\n } else if (hadUser.current) {\n hadUser.current = false;\n client.reset();\n }\n }, [client, userKey]);\n\n const bootstrap = useBootstrap(payload, { ...userAttributes(user), ...appAttributes });\n const flags = useMemo<FlagsSource>(() => ({ client: client?.flags ?? null, bootstrap }), [client, bootstrap]);\n return (\n <GuidelyContext.Provider value={client}>\n <FlagsContext.Provider value={flags}>{children}</FlagsContext.Provider>\n </GuidelyContext.Provider>\n );\n}\n\nconst NOOP_RESULTS: Record<string, unknown> = {\n init: Promise.resolve(),\n ready: Promise.resolve(),\n identify: Promise.resolve(),\n setAccount: Promise.resolve(),\n updateTraits: Promise.resolve(),\n refresh: Promise.resolve(),\n startGuide: false,\n getActiveGuide: null,\n getGuides: [],\n getChecklistProgress: { completedTaskIds: [], completedAt: null, dismissedAt: null },\n isGuideCompleted: false,\n on: () => {},\n getState: { ready: false, destroyed: false },\n getContext: null,\n getSessionId: \"\",\n isRecording: false,\n};\n\n/** Flags that serve every fallback; `useGuidely().flags` before the provider has a client. */\nconst noopFlags = new Proxy(createStaticFlags() as unknown as FlagsClient, {\n get(target, prop, receiver) {\n if (prop in target) return Reflect.get(target, prop, receiver);\n if (prop === \"ready\" || prop === \"refresh\" || prop === \"flush\") return () => Promise.resolve();\n if (prop === \"getPayload\") return () => null;\n if (prop === \"forUser\") return () => target;\n return typeof prop === \"string\" ? () => undefined : undefined;\n },\n});\n\nconst missing = <T,>(key: string, fallback: T): FlagDetails<T> => ({ key, value: fallback, reason: \"FLAG_NOT_FOUND\", inExperiment: false, usedFallback: true });\n\n/** Methods whose no-op answer depends on the arguments (flag getters return the fallback). */\nconst NOOP_FUNCTIONS: Record<string, (...args: any[]) => unknown> = {\n isFeatureEnabled: (_key: string, fallback = false) => fallback,\n getFeatureValue: (_key: string, fallback: unknown) => fallback,\n getFeatureDetails: (key: string, fallback: unknown) => missing(key, fallback),\n onFlagsChange: () => () => {},\n};\n\n/** A client that ignores every call; returned by `useGuidely()` on the server and before the provider mounts. */\nexport const noopClient: GuidelyClient = new Proxy({} as GuidelyClient, {\n get(_target, prop) {\n if (prop === \"options\") return { projectId: \"\" };\n if (prop === \"flags\") return noopFlags;\n if (typeof prop !== \"string\") return undefined;\n return NOOP_FUNCTIONS[prop] ?? (() => NOOP_RESULTS[prop]);\n },\n});\n\n/** The Guidely client, or a no-op client during SSR / before the provider has mounted. */\nexport function useGuidely(): GuidelyClient {\n return useContext(GuidelyContext) ?? noopClient;\n}\n\n/** True once the client has loaded its configuration. */\nexport function useGuidelyReady(): boolean {\n const client = useContext(GuidelyContext);\n const [ready, setReady] = useState(false);\n useEffect(() => {\n if (!client) {\n setReady(false);\n return;\n }\n if (client.getState().ready) setReady(true);\n return client.on(\"ready\", () => setReady(true));\n }, [client]);\n return ready;\n}\n\n/** Identifies the user whenever the arguments change. */\nexport function useGuidelyIdentify(userId: string | null | undefined, traits?: Record<string, unknown>, account?: GuidelyUser[\"account\"]): void {\n const client = useContext(GuidelyContext);\n const key = JSON.stringify([userId ?? null, traits ?? null, account ?? null]);\n useEffect(() => {\n if (!client || !userId) return;\n void client.identify(userId, traits, account);\n }, [client, key]);\n}\n\n/** Subscribes to a client event for the lifetime of the component. */\nexport function useGuidelyEvent<K extends keyof GuidelyEvents>(event: K, handler: GuidelyEvents[K]): void {\n const client = useContext(GuidelyContext);\n const ref = useRef(handler);\n ref.current = handler;\n useEffect(() => {\n if (!client) return;\n const cb = ((...args: unknown[]) => (ref.current as (...a: unknown[]) => void)(...args)) as GuidelyEvents[K];\n return client.on(event, cb);\n }, [client, event]);\n}\n\n/** Live checklist progress for a checklist guide (by id or key). */\nexport function useChecklistProgress(idOrKey: string): ChecklistProgressState {\n const client = useContext(GuidelyContext);\n const [progress, setProgress] = useState<ChecklistProgressState>({ completedTaskIds: [], completedAt: null, dismissedAt: null });\n useEffect(() => {\n if (!client) return;\n const update = () => setProgress(client.getChecklistProgress(idOrKey));\n update();\n const offs = [client.on(\"ready\", update), client.on(\"identify\", update), client.on(\"checklist:task\", update), client.on(\"checklist:complete\", update)];\n return () => offs.forEach((off) => off());\n }, [client, idOrKey]);\n return progress;\n}\n\nexport interface GuideState {\n /** The guide is currently showing. */\n active: boolean;\n completed: boolean;\n stepId: string | null;\n stepIndex: number;\n guide: PublishedGuide | null;\n}\n\n/** Live state of a guide (by id or key): active, completed, current step. */\nexport function useGuideState(idOrKey: string): GuideState {\n const client = useContext(GuidelyContext);\n const [state, setState] = useState<GuideState>({ active: false, completed: false, stepId: null, stepIndex: -1, guide: null });\n useEffect(() => {\n if (!client) return;\n const matches = (g: PublishedGuide) => g.id === idOrKey || g.key === idOrKey;\n const compute = () => {\n const guide = client.getGuides().find(matches) ?? null;\n const active = client.getActiveGuide();\n const s = client.getState().activeGuide;\n setState({ active: !!active && matches(active), completed: client.isGuideCompleted(idOrKey), stepId: active && matches(active) ? ((s as { stepId?: string | null } | null)?.stepId ?? null) : null, stepIndex: active && matches(active) ? ((s as { stepIndex?: number } | null)?.stepIndex ?? -1) : -1, guide });\n };\n compute();\n const offs = ([\"ready\", \"identify\", \"guide:start\", \"guide:step\", \"guide:complete\", \"guide:dismiss\", \"guide:skip\"] as const).map((e) => client.on(e, compute));\n return () => offs.forEach((off) => off());\n }, [client, idOrKey]);\n return state;\n}\n","import { createFlagsClient, createStaticFlags, jsonEquals, stableStringify, type Attributes, type FlagDetails, type FlagValue, type FlagsBootstrap, type FlagsClient, type FlagsClientOptions, type FlagsReader, type JsonValue } from \"@guidelytech/flags\";\nimport { createContext, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore, type ReactNode } from \"react\";\n\n/** What the flag hooks read: the live client once created, else a static snapshot of the bootstrap payload. */\nexport interface FlagsSource {\n client: FlagsClient | null;\n bootstrap: FlagsReader;\n}\n\nexport const FlagsContext = createContext<FlagsSource | null>(null);\n\nconst EMPTY = createStaticFlags();\n\nfunction useReader(): FlagsReader {\n const src = useContext(FlagsContext);\n return src?.client ?? src?.bootstrap ?? EMPTY;\n}\n\n/**\n * Subscribes to the flags with `useSyncExternalStore`. Values are compared deeply so object\n * flags keep their identity between renders. The server snapshot is the bootstrap payload, so\n * server-rendered HTML and hydration agree.\n */\nfunction useFlagsValue<T>(select: (reader: FlagsReader) => T): T {\n const reader = useReader();\n const cache = useRef<{ reader: FlagsReader; value: T } | null>(null);\n const get = () => {\n const value = select(reader);\n const c = cache.current;\n if (c && c.reader === reader && jsonEquals(c.value, value)) return c.value;\n cache.current = { reader, value };\n return value;\n };\n return useSyncExternalStore(reader.subscribe, get, get);\n}\n\n/** Is the boolean flag on? `fallback` (default `false`) until flags load, or when missing. */\nexport function useFlag(key: string, fallback = false): boolean {\n return useFlagsValue((r) => r.isEnabled(key, fallback));\n}\n\n/** A flag's value (string, number, JSON…); `fallback` when missing or of a different type. */\nexport function useFeatureValue<T extends JsonValue>(key: string, fallback: T): FlagValue<T> {\n return useFlagsValue((r) => r.getValue(key, fallback));\n}\n\n/** A flag's value plus why it was served (reason, rule, variation, experiment). */\nexport function useFlagDetails<T extends JsonValue>(key: string, fallback: T): FlagDetails<FlagValue<T>> {\n return useFlagsValue((r) => r.getDetails(key, fallback));\n}\n\n/** Every flag's value (records no exposures). */\nexport function useFlags(): Record<string, JsonValue> {\n return useFlagsValue((r) => r.getAll());\n}\n\n/** True once flags have loaded (or failed to and serve fallbacks); true from the start with a bootstrap payload. */\nexport function useFlagsReady(): boolean {\n return useFlagsValue((r) => r.isReady());\n}\n\n/** The flags client (`null` on the server and before the provider has created it). */\nexport function useFlagsClient(): FlagsClient | null {\n return useContext(FlagsContext)?.client ?? null;\n}\n\nexport interface FeatureProps {\n flag: string;\n children?: ReactNode;\n /** Rendered while the flag is off (default: nothing). */\n fallback?: ReactNode;\n}\n\n/** Renders `children` when the boolean flag is on, else `fallback`. */\nexport function Feature({ flag, children, fallback = null }: FeatureProps) {\n return <>{useFlag(flag) ? children : fallback}</>;\n}\n\n/** Alias of {@link Feature}. */\nexport const FeatureFlag = Feature;\n\nexport interface FeatureValueProps extends FeatureProps {\n /** Renders `children` when the flag's value equals this (deep equality). */\n value: JsonValue;\n}\n\n/** Renders `children` when the flag's value equals `value` (e.g. an experiment variation), else `fallback`. */\nexport function FeatureValue({ flag, value, children, fallback = null }: FeatureValueProps) {\n const current = useFeatureValue<JsonValue>(flag, null);\n return <>{jsonEquals(current, value) ? children : fallback}</>;\n}\n\n/** A static snapshot for SSR and hydration, rebuilt only when the payload or attributes change. */\nexport function useBootstrap(payload: FlagsBootstrap | null | undefined, attributes: Attributes): FlagsReader {\n const key = stableStringify(attributes);\n return useMemo(() => (payload ? createStaticFlags({ payload, attributes }) : EMPTY), [payload, key]);\n}\n\nexport interface FlagsProviderProps extends Omit<FlagsClientOptions, \"payload\"> {\n /** Bootstrap from the server (`getPayload()`): renders the same values on the server and in the browser. */\n payload?: FlagsBootstrap | null;\n /** Use an existing client instead of creating one. */\n client?: FlagsClient;\n children?: ReactNode;\n}\n\n/**\n * Feature flags only (no guides or analytics). Creates a flags client in an effect (never during\n * SSR) and destroys it on unmount; until then hooks read the `payload` snapshot. Changing\n * `attributes` re-evaluates without recreating the client.\n *\n * ```tsx\n * <FlagsProvider sdkKey=\"pk_prod_…\" attributes={{ id: user.id, plan: user.plan }} payload={bootstrap}>\n * ```\n */\nexport function FlagsProvider({ children, client: external, payload, ...options }: FlagsProviderProps) {\n const [created, setCreated] = useState<FlagsClient | null>(null);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n const sdkKey = options.sdkKey ?? options.projectId;\n const { apiUrl, environment } = options;\n const payloadRef = useRef(payload);\n payloadRef.current = payload;\n\n useEffect(() => {\n if (external || !sdkKey || typeof window === \"undefined\") return;\n let c: FlagsClient;\n try {\n c = createFlagsClient({ ...optionsRef.current, sdkKey, payload: payloadRef.current ?? undefined });\n } catch (err) {\n optionsRef.current.onError?.(err instanceof Error ? err : new Error(String(err)));\n return;\n }\n setCreated(c);\n return () => {\n c.destroy();\n setCreated((cur) => (cur === c ? null : cur));\n };\n }, [external, sdkKey, apiUrl, environment]);\n\n const client = external ?? created;\n const attributes = options.attributes ?? {};\n const attrsKey = stableStringify(attributes);\n useEffect(() => {\n if (client && options.attributes) client.setAttributes(options.attributes);\n }, [client, attrsKey]);\n\n const bootstrap = useBootstrap(payload, attributes);\n const value = useMemo<FlagsSource>(() => ({ client, bootstrap }), [client, bootstrap]);\n return <FlagsContext.Provider value={value}>{children}</FlagsContext.Provider>;\n}\n"],"mappings":"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","names":["createStaticFlags","stableStringify","createContext","useContext","useEffect","useMemo","useRef","useState","jsx","createContext","useState","useRef","useMemo","stableStringify","useEffect","createStaticFlags","useContext"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guidelytech/react",
3
- "version": "0.6.2",
3
+ "version": "0.8.0",
4
4
  "description": "React provider and hooks for Guidely",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -20,8 +20,9 @@
20
20
  "README.md"
21
21
  ],
22
22
  "dependencies": {
23
- "@guidelytech/sdk-core": "0.6.2",
24
- "@guidelytech/shared": "0.6.2"
23
+ "@guidelytech/flags": "0.8.0",
24
+ "@guidelytech/shared": "0.8.0",
25
+ "@guidelytech/sdk-core": "0.8.0"
25
26
  },
26
27
  "devDependencies": {
27
28
  "tsup": "^8.5.1",