@guidelytech/svelte 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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,3 +33,28 @@ pnpm add @guidelytech/svelte
33
33
  | `setGuidelyContext(client)` / `getGuidelyContext()` | Component context helpers (fall back to the store). |
34
34
  | `use:guideTrigger={"guide-key"}` | Clicking the element starts the guide. |
35
35
  | `use:guidelyTarget={"id"}` | Marks the element with `data-tour="id"` (no runtime behaviour). |
36
+ | `flag(key, fallback = false, source?)` | `Readable<boolean>`: is the boolean flag on? |
37
+ | `featureValue(key, fallback, source?)` | `Readable` of a flag's value (string, number, JSON); `fallback` when missing or of another type. |
38
+ | `flagDetails(key, fallback, source?)` | `Readable` of the value plus `reason`, `ruleId`, `variationId`, `experiment`. |
39
+ | `allFlags(source?)` / `flagsReady(source?)` | Every flag's value / whether flags have loaded. |
40
+ | `createFlags(options)` | A flags-only client (no guides or analytics); pass it as `source` or to `setFlagsContext`. |
41
+ | `setFlagsContext(source)` | Flags for this component tree: a Guidely client, a flags client, `createStaticFlags(…)` or a store of one. |
42
+
43
+ ## Feature flags
44
+
45
+ ```svelte
46
+ <script lang="ts">
47
+ import { flag, featureValue } from "@guidelytech/svelte";
48
+ const newBanner = flag("new-dashboard-banner");
49
+ const color = featureValue("checkout-button-color", "blue");
50
+ </script>
51
+
52
+ {#if $newBanner}<NewBanner />{/if}
53
+ <button class={$color === "green" ? "bg-emerald-600" : "bg-blue-600"}>Checkout</button>
54
+ ```
55
+
56
+ The stores read, in order: the `source` argument, the component context (`setFlagsContext`, or
57
+ `initGuidely` in SvelteKit), then the global `guidely` client's flags — the same public key, with
58
+ identity, page and device attributes maintained for you. They notify only when a value changes
59
+ and give the fallback until flags load. Call them during component initialisation (or pass a
60
+ `source`) and subscribe with `$`. Works with Svelte 4 and 5.
package/dist/index.cjs CHANGED
@@ -21,26 +21,110 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  activeGuide: () => activeGuide,
24
+ allFlags: () => allFlags,
24
25
  checklistProgress: () => checklistProgress,
26
+ createFlags: () => createFlags,
25
27
  createGuidely: () => createGuidely,
28
+ createStaticFlags: () => import_flags3.createStaticFlags,
26
29
  destroyGuidely: () => destroyGuidely,
30
+ featureValue: () => featureValue,
31
+ flag: () => flag,
32
+ flagDetails: () => flagDetails,
33
+ flagsReady: () => flagsReady,
27
34
  getGuidelyContext: () => getGuidelyContext,
28
35
  guideCompleted: () => guideCompleted,
29
36
  guideTrigger: () => guideTrigger,
30
37
  guidely: () => guidely,
31
38
  guidelyReady: () => guidelyReady,
32
39
  guidelyTarget: () => guidelyTarget,
40
+ setFlagsContext: () => setFlagsContext,
33
41
  setGuidelyContext: () => setGuidelyContext,
34
42
  syncGuidelyUser: () => syncGuidelyUser
35
43
  });
36
44
  module.exports = __toCommonJS(index_exports);
37
45
  var import_sdk_core = require("@guidelytech/sdk-core");
38
- var import_svelte = require("svelte");
46
+ var import_svelte2 = require("svelte");
47
+ var import_store3 = require("svelte/store");
48
+
49
+ // src/client-store.ts
39
50
  var import_store = require("svelte/store");
40
51
  var guidely = (0, import_store.writable)(null);
52
+
53
+ // src/flags.ts
54
+ var import_flags = require("@guidelytech/flags");
55
+ var import_svelte = require("svelte");
56
+ var import_store2 = require("svelte/store");
57
+ var FLAGS_KEY = /* @__PURE__ */ Symbol.for("guidely.flags");
58
+ function readerOf(source) {
59
+ if (!source) return null;
60
+ return "isFeatureEnabled" in source && "flags" in source ? source.flags : source;
61
+ }
62
+ function setFlagsContext(source) {
63
+ const store = source && "subscribe" in source && !("isEnabled" in source) && !("isFeatureEnabled" in source) ? (0, import_store2.derived)(source, readerOf) : (0, import_store2.readable)(readerOf(source));
64
+ (0, import_svelte.setContext)(FLAGS_KEY, store);
65
+ }
66
+ function sourceStore(source) {
67
+ if (source) return (0, import_store2.readable)(readerOf(source));
68
+ try {
69
+ const ctx = (0, import_svelte.getContext)(FLAGS_KEY);
70
+ if (ctx) return ctx;
71
+ } catch {
72
+ }
73
+ return (0, import_store2.derived)(guidely, (c) => c?.flags ?? null);
74
+ }
75
+ function flagStore(source, initial, select) {
76
+ const src = sourceStore(source);
77
+ return (0, import_store2.readable)(initial, (set) => {
78
+ let off = () => {
79
+ };
80
+ let last = null;
81
+ const unsub = src.subscribe((reader) => {
82
+ off();
83
+ off = () => {
84
+ };
85
+ if (!reader) {
86
+ last = { value: initial };
87
+ return set(initial);
88
+ }
89
+ const update = () => {
90
+ const value = select(reader);
91
+ if (last && (0, import_flags.jsonEquals)(last.value, value)) return;
92
+ last = { value };
93
+ set(value);
94
+ };
95
+ update();
96
+ off = reader.subscribe(update);
97
+ });
98
+ return () => {
99
+ off();
100
+ unsub();
101
+ };
102
+ });
103
+ }
104
+ function flag(key, fallback = false, source) {
105
+ return flagStore(source, fallback, (r) => r.isEnabled(key, fallback));
106
+ }
107
+ function featureValue(key, fallback, source) {
108
+ return flagStore(source, fallback, (r) => r.getValue(key, fallback));
109
+ }
110
+ function flagDetails(key, fallback, source) {
111
+ return flagStore(source, { key, value: fallback, reason: "FLAG_NOT_FOUND", inExperiment: false, usedFallback: true }, (r) => r.getDetails(key, fallback));
112
+ }
113
+ function allFlags(source) {
114
+ return flagStore(source, {}, (r) => r.getAll());
115
+ }
116
+ function flagsReady(source) {
117
+ return flagStore(source, false, (r) => r.isReady());
118
+ }
119
+ function createFlags(options) {
120
+ return (0, import_flags.createFlagsClient)(options);
121
+ }
122
+
123
+ // src/index.ts
124
+ var import_flags3 = require("@guidelytech/flags");
41
125
  function createGuidely(options) {
42
126
  if (typeof window === "undefined") throw new Error("[Guidely] createGuidely() can only run in the browser. Call it inside onMount(), or use initGuidely() from @guidelytech/sveltekit.");
43
- const existing = (0, import_store.get)(guidely);
127
+ const existing = (0, import_store3.get)(guidely);
44
128
  if (existing && !existing.getState().destroyed && existing.options.projectId === options.projectId) return existing;
45
129
  existing?.destroy();
46
130
  const client = (0, import_sdk_core.createGuidely)(options);
@@ -53,7 +137,7 @@ var appliedUser = /* @__PURE__ */ new WeakMap();
53
137
  var pendingUser;
54
138
  function syncGuidelyUser(user) {
55
139
  if (typeof window === "undefined") return;
56
- const client = (0, import_store.get)(guidely);
140
+ const client = (0, import_store3.get)(guidely);
57
141
  if (client) applyUser(client, user);
58
142
  else pendingUser = user;
59
143
  }
@@ -66,18 +150,18 @@ function applyUser(client, user) {
66
150
  else if (client.getState().user?.id) client.reset();
67
151
  }
68
152
  function destroyGuidely() {
69
- (0, import_store.get)(guidely)?.destroy();
153
+ (0, import_store3.get)(guidely)?.destroy();
70
154
  guidely.set(null);
71
155
  }
72
156
  var CONTEXT_KEY = /* @__PURE__ */ Symbol.for("guidely");
73
157
  function setGuidelyContext(client) {
74
- (0, import_svelte.setContext)(CONTEXT_KEY, client);
158
+ (0, import_svelte2.setContext)(CONTEXT_KEY, client);
75
159
  }
76
160
  function getGuidelyContext() {
77
- return (0, import_svelte.getContext)(CONTEXT_KEY) ?? (0, import_store.get)(guidely);
161
+ return (0, import_svelte2.getContext)(CONTEXT_KEY) ?? (0, import_store3.get)(guidely);
78
162
  }
79
163
  function fromClient(initial, compute, events) {
80
- return (0, import_store.readable)(initial, (set) => {
164
+ return (0, import_store3.readable)(initial, (set) => {
81
165
  let offs = [];
82
166
  const unsub = guidely.subscribe((client) => {
83
167
  offs.forEach((off) => off());
@@ -99,13 +183,13 @@ function checklistProgress(idOrKey) {
99
183
  return fromClient({ completedTaskIds: [], completedAt: null, dismissedAt: null }, (c) => c.getChecklistProgress(idOrKey), ["ready", "identify", "checklist:task", "checklist:complete"]);
100
184
  }
101
185
  function guideCompleted(idOrKey) {
102
- return (0, import_store.derived)(fromClient(false, (c) => c.isGuideCompleted(idOrKey), ["ready", "identify", "guide:complete"]), (v) => v);
186
+ return (0, import_store3.derived)(fromClient(false, (c) => c.isGuideCompleted(idOrKey), ["ready", "identify", "guide:complete"]), (v) => v);
103
187
  }
104
188
  function guideTrigger(node, key) {
105
189
  let current = key;
106
190
  const onClick = (e) => {
107
191
  e.preventDefault();
108
- (0, import_store.get)(guidely)?.startGuide(current);
192
+ (0, import_store3.get)(guidely)?.startGuide(current);
109
193
  };
110
194
  node.addEventListener("click", onClick);
111
195
  node.setAttribute("data-guidely-trigger", current);
@@ -130,15 +214,23 @@ function guidelyTarget(node, id) {
130
214
  // Annotate the CommonJS export names for ESM import in node:
131
215
  0 && (module.exports = {
132
216
  activeGuide,
217
+ allFlags,
133
218
  checklistProgress,
219
+ createFlags,
134
220
  createGuidely,
221
+ createStaticFlags,
135
222
  destroyGuidely,
223
+ featureValue,
224
+ flag,
225
+ flagDetails,
226
+ flagsReady,
136
227
  getGuidelyContext,
137
228
  guideCompleted,
138
229
  guideTrigger,
139
230
  guidely,
140
231
  guidelyReady,
141
232
  guidelyTarget,
233
+ setFlagsContext,
142
234
  setGuidelyContext,
143
235
  syncGuidelyUser
144
236
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { createGuidely as createCore, type ChecklistProgressState, type GuidelyClient, type GuidelyOptions, type GuidelyUser, type PublishedGuide } from \"@guidelytech/sdk-core\";\nimport { getContext, setContext } from \"svelte\";\nimport { derived, get, readable, writable, type Readable } from \"svelte/store\";\n\nexport type { GuidelyClient, GuidelyOptions, GuidelyEvents, GuidelyUser, ChecklistProgressState, PublishedGuide } from \"@guidelytech/sdk-core\";\n\n/** The current client (null on the server and before `createGuidely`). */\nexport const guidely = writable<GuidelyClient | null>(null);\n\n/**\n * Creates the client and stores it in the `guidely` store. Browser-only: throws a helpful error\n * when called during SSR (call it from `onMount` or use `@guidelytech/sveltekit`'s `initGuidely`).\n */\nexport function createGuidely(options: GuidelyOptions): GuidelyClient {\n if (typeof window === \"undefined\") throw new Error(\"[Guidely] createGuidely() can only run in the browser. Call it inside onMount(), or use initGuidely() from @guidelytech/sveltekit.\");\n const existing = get(guidely);\n if (existing && !existing.getState().destroyed && existing.options.projectId === options.projectId) return existing;\n existing?.destroy();\n const client = createCore(options);\n guidely.set(client);\n if (pendingUser !== undefined) applyUser(client, pendingUser);\n pendingUser = undefined;\n return client;\n}\n\nconst appliedUser = new WeakMap<GuidelyClient, string | null>();\nlet pendingUser: GuidelyUser | null | undefined;\n\n/**\n * Keeps the Guidely user in step with your app's signed-in user. Idempotent: call it whenever your\n * user data may have changed (a `$effect` / `$:` on layout data). A user identifies (again only when\n * id, traits or account change); `null` resets a previously identified user, including one restored\n * from storage; `undefined` means \"not known yet\" and changes nothing. No-op on the server; called\n * before `createGuidely`, it is applied when the client is created.\n */\nexport function syncGuidelyUser(user: GuidelyUser | null | undefined): void {\n if (typeof window === \"undefined\") return;\n const client = get(guidely);\n if (client) applyUser(client, user);\n else pendingUser = user;\n}\n\nfunction applyUser(client: GuidelyClient, user: GuidelyUser | null | undefined): void {\n if (user === undefined) return;\n const key = user ? JSON.stringify([String(user.id), user.traits ?? null, user.account ?? null]) : null;\n if (appliedUser.has(client) && appliedUser.get(client) === key) return;\n appliedUser.set(client, key);\n if (user) void client.identify(user.id, user.traits, user.account);\n else if (client.getState().user?.id) client.reset();\n}\n\n/** Destroys the current client and clears the store. */\nexport function destroyGuidely(): void {\n get(guidely)?.destroy();\n guidely.set(null);\n}\n\nconst CONTEXT_KEY = Symbol.for(\"guidely\");\n\nexport function setGuidelyContext(client: GuidelyClient | null): void {\n setContext(CONTEXT_KEY, client);\n}\n\nexport function getGuidelyContext(): GuidelyClient | null {\n return (getContext(CONTEXT_KEY) as GuidelyClient | null | undefined) ?? get(guidely);\n}\n\nfunction fromClient<T>(initial: T, compute: (client: GuidelyClient) => T, events: Array<Parameters<GuidelyClient[\"on\"]>[0]>): Readable<T> {\n return readable<T>(initial, (set) => {\n let offs: Array<() => void> = [];\n const unsub = guidely.subscribe((client) => {\n offs.forEach((off) => off());\n offs = [];\n if (!client) return set(initial);\n const update = () => set(compute(client));\n update();\n offs = events.map((e) => client.on(e, update as never));\n });\n return () => {\n offs.forEach((off) => off());\n unsub();\n };\n });\n}\n\n/** True once the configuration has loaded. */\nexport const guidelyReady: Readable<boolean> = fromClient(false, (c) => c.getState().ready, [\"ready\"]);\n\n/** The guide currently showing (flows, modals, surveys), or null. */\nexport const activeGuide: Readable<PublishedGuide | null> = fromClient<PublishedGuide | null>(null, (c) => c.getActiveGuide(), [\"ready\", \"guide:start\", \"guide:complete\", \"guide:dismiss\", \"guide:skip\", \"identify\"]);\n\n/** Live checklist progress for a checklist guide (by id or key). */\nexport function checklistProgress(idOrKey: string): Readable<ChecklistProgressState> {\n return fromClient<ChecklistProgressState>({ completedTaskIds: [], completedAt: null, dismissedAt: null }, (c) => c.getChecklistProgress(idOrKey), [\"ready\", \"identify\", \"checklist:task\", \"checklist:complete\"]);\n}\n\n/** Is the given guide completed for the current user? */\nexport function guideCompleted(idOrKey: string): Readable<boolean> {\n return derived(fromClient<boolean>(false, (c) => c.isGuideCompleted(idOrKey), [\"ready\", \"identify\", \"guide:complete\"]), (v) => v);\n}\n\n/** `use:guideTrigger={\"guide-key\"}` — clicking the element starts the guide. */\nexport function guideTrigger(node: HTMLElement, key: string): { update(key: string): void; destroy(): void } {\n let current = key;\n const onClick = (e: Event) => {\n e.preventDefault();\n get(guidely)?.startGuide(current);\n };\n node.addEventListener(\"click\", onClick);\n node.setAttribute(\"data-guidely-trigger\", current);\n return {\n update(next: string) {\n current = next;\n node.setAttribute(\"data-guidely-trigger\", next);\n },\n destroy() {\n node.removeEventListener(\"click\", onClick);\n },\n };\n}\n\n/** `use:guidelyTarget={\"id\"}` — marks an element as a guide target (`data-tour`). No runtime behaviour. */\nexport function guidelyTarget(node: HTMLElement, id?: string): { update(id?: string): void; destroy(): void } {\n const apply = (v?: string) => {\n if (v) node.setAttribute(\"data-tour\", v);\n };\n apply(id);\n return { update: apply, destroy() {} };\n}\n"],"mappings":"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","names":["createCore"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/client-store.ts","../src/flags.ts"],"sourcesContent":["import { createGuidely as createCore, type ChecklistProgressState, type GuidelyClient, type GuidelyOptions, type GuidelyUser, type PublishedGuide } from \"@guidelytech/sdk-core\";\nimport { getContext, setContext } from \"svelte\";\nimport { derived, get, readable, type Readable } from \"svelte/store\";\nimport { guidely } from \"./client-store\";\n\nexport type { GuidelyClient, GuidelyOptions, GuidelyEvents, GuidelyUser, ChecklistProgressState, PublishedGuide } from \"@guidelytech/sdk-core\";\nexport { guidely };\nexport { flag, featureValue, flagDetails, allFlags, flagsReady, createFlags, setFlagsContext } from \"./flags\";\nexport type { FlagsSource } from \"./flags\";\nexport { createStaticFlags } from \"@guidelytech/flags\";\nexport type { FlagsClient, FlagsClientOptions, FlagDetails, FlagsBootstrap, FlagsPayload, FlagsReader, Attributes, JsonValue, EvaluationResult } from \"@guidelytech/flags\";\n\n/**\n * Creates the client and stores it in the `guidely` store. Browser-only: throws a helpful error\n * when called during SSR (call it from `onMount` or use `@guidelytech/sveltekit`'s `initGuidely`).\n */\nexport function createGuidely(options: GuidelyOptions): GuidelyClient {\n if (typeof window === \"undefined\") throw new Error(\"[Guidely] createGuidely() can only run in the browser. Call it inside onMount(), or use initGuidely() from @guidelytech/sveltekit.\");\n const existing = get(guidely);\n if (existing && !existing.getState().destroyed && existing.options.projectId === options.projectId) return existing;\n existing?.destroy();\n const client = createCore(options);\n guidely.set(client);\n if (pendingUser !== undefined) applyUser(client, pendingUser);\n pendingUser = undefined;\n return client;\n}\n\nconst appliedUser = new WeakMap<GuidelyClient, string | null>();\nlet pendingUser: GuidelyUser | null | undefined;\n\n/**\n * Keeps the Guidely user in step with your app's signed-in user. Idempotent: call it whenever your\n * user data may have changed (a `$effect` / `$:` on layout data). A user identifies (again only when\n * id, traits or account change); `null` resets a previously identified user, including one restored\n * from storage; `undefined` means \"not known yet\" and changes nothing. No-op on the server; called\n * before `createGuidely`, it is applied when the client is created.\n */\nexport function syncGuidelyUser(user: GuidelyUser | null | undefined): void {\n if (typeof window === \"undefined\") return;\n const client = get(guidely);\n if (client) applyUser(client, user);\n else pendingUser = user;\n}\n\nfunction applyUser(client: GuidelyClient, user: GuidelyUser | null | undefined): void {\n if (user === undefined) return;\n const key = user ? JSON.stringify([String(user.id), user.traits ?? null, user.account ?? null]) : null;\n if (appliedUser.has(client) && appliedUser.get(client) === key) return;\n appliedUser.set(client, key);\n if (user) void client.identify(user.id, user.traits, user.account);\n else if (client.getState().user?.id) client.reset();\n}\n\n/** Destroys the current client and clears the store. */\nexport function destroyGuidely(): void {\n get(guidely)?.destroy();\n guidely.set(null);\n}\n\nconst CONTEXT_KEY = Symbol.for(\"guidely\");\n\nexport function setGuidelyContext(client: GuidelyClient | null): void {\n setContext(CONTEXT_KEY, client);\n}\n\nexport function getGuidelyContext(): GuidelyClient | null {\n return (getContext(CONTEXT_KEY) as GuidelyClient | null | undefined) ?? get(guidely);\n}\n\nfunction fromClient<T>(initial: T, compute: (client: GuidelyClient) => T, events: Array<Parameters<GuidelyClient[\"on\"]>[0]>): Readable<T> {\n return readable<T>(initial, (set) => {\n let offs: Array<() => void> = [];\n const unsub = guidely.subscribe((client) => {\n offs.forEach((off) => off());\n offs = [];\n if (!client) return set(initial);\n const update = () => set(compute(client));\n update();\n offs = events.map((e) => client.on(e, update as never));\n });\n return () => {\n offs.forEach((off) => off());\n unsub();\n };\n });\n}\n\n/** True once the configuration has loaded. */\nexport const guidelyReady: Readable<boolean> = fromClient(false, (c) => c.getState().ready, [\"ready\"]);\n\n/** The guide currently showing (flows, modals, surveys), or null. */\nexport const activeGuide: Readable<PublishedGuide | null> = fromClient<PublishedGuide | null>(null, (c) => c.getActiveGuide(), [\"ready\", \"guide:start\", \"guide:complete\", \"guide:dismiss\", \"guide:skip\", \"identify\"]);\n\n/** Live checklist progress for a checklist guide (by id or key). */\nexport function checklistProgress(idOrKey: string): Readable<ChecklistProgressState> {\n return fromClient<ChecklistProgressState>({ completedTaskIds: [], completedAt: null, dismissedAt: null }, (c) => c.getChecklistProgress(idOrKey), [\"ready\", \"identify\", \"checklist:task\", \"checklist:complete\"]);\n}\n\n/** Is the given guide completed for the current user? */\nexport function guideCompleted(idOrKey: string): Readable<boolean> {\n return derived(fromClient<boolean>(false, (c) => c.isGuideCompleted(idOrKey), [\"ready\", \"identify\", \"guide:complete\"]), (v) => v);\n}\n\n/** `use:guideTrigger={\"guide-key\"}` — clicking the element starts the guide. */\nexport function guideTrigger(node: HTMLElement, key: string): { update(key: string): void; destroy(): void } {\n let current = key;\n const onClick = (e: Event) => {\n e.preventDefault();\n get(guidely)?.startGuide(current);\n };\n node.addEventListener(\"click\", onClick);\n node.setAttribute(\"data-guidely-trigger\", current);\n return {\n update(next: string) {\n current = next;\n node.setAttribute(\"data-guidely-trigger\", next);\n },\n destroy() {\n node.removeEventListener(\"click\", onClick);\n },\n };\n}\n\n/** `use:guidelyTarget={\"id\"}` — marks an element as a guide target (`data-tour`). No runtime behaviour. */\nexport function guidelyTarget(node: HTMLElement, id?: string): { update(id?: string): void; destroy(): void } {\n const apply = (v?: string) => {\n if (v) node.setAttribute(\"data-tour\", v);\n };\n apply(id);\n return { update: apply, destroy() {} };\n}\n","import type { GuidelyClient } from \"@guidelytech/sdk-core\";\nimport { writable } from \"svelte/store\";\n\n/** The current client (null on the server and before `createGuidely`). */\nexport const guidely = writable<GuidelyClient | null>(null);\n","import { createFlagsClient, jsonEquals, type FlagDetails, type FlagValue, type FlagsClient, type FlagsClientOptions, type FlagsReader, type JsonValue } from \"@guidelytech/flags\";\nimport type { GuidelyClient } from \"@guidelytech/sdk-core\";\nimport { getContext, setContext } from \"svelte\";\nimport { derived, readable, type Readable } from \"svelte/store\";\nimport { guidely } from \"./client-store\";\n\n/** Where flag stores read from: a Guidely client, a flags client, or a static snapshot. */\nexport type FlagsSource = GuidelyClient | FlagsReader;\n\nconst FLAGS_KEY = Symbol.for(\"guidely.flags\");\n\nfunction readerOf(source: FlagsSource | null | undefined): FlagsReader | null {\n if (!source) return null;\n return \"isFeatureEnabled\" in source && \"flags\" in source ? (source as GuidelyClient).flags : (source as FlagsReader);\n}\n\n/**\n * Makes `source` the flags for this component and its children (call during component\n * initialisation). Accepts a client, a flags client, a static snapshot or a store of one.\n */\nexport function setFlagsContext(source: FlagsSource | Readable<FlagsSource | null> | null): void {\n const store = source && \"subscribe\" in source && !(\"isEnabled\" in source) && !(\"isFeatureEnabled\" in source) ? derived(source as Readable<FlagsSource | null>, readerOf) : readable(readerOf(source as FlagsSource | null));\n setContext(FLAGS_KEY, store);\n}\n\n/** An explicit source, else the component context (`setFlagsContext` / `initGuidely`), else the global `guidely` client. */\nfunction sourceStore(source?: FlagsSource | null): Readable<FlagsReader | null> {\n if (source) return readable(readerOf(source));\n try {\n const ctx = getContext<Readable<FlagsReader | null> | undefined>(FLAGS_KEY);\n if (ctx) return ctx;\n } catch {\n /* not during component initialisation */\n }\n return derived(guidely, (c) => c?.flags ?? null);\n}\n\nfunction flagStore<T>(source: FlagsSource | null | undefined, initial: T, select: (r: FlagsReader) => T): Readable<T> {\n const src = sourceStore(source);\n return readable<T>(initial, (set) => {\n let off = () => {};\n let last: { value: T } | null = null;\n const unsub = src.subscribe((reader) => {\n off();\n off = () => {};\n if (!reader) {\n last = { value: initial };\n return set(initial);\n }\n const update = () => {\n const value = select(reader);\n if (last && jsonEquals(last.value, value)) return; // object flags keep their identity\n last = { value };\n set(value);\n };\n update();\n off = reader.subscribe(update);\n });\n return () => {\n off();\n unsub();\n };\n });\n}\n\n/** `$flag` is true when the boolean flag is on; `fallback` (default `false`) until flags load, or when missing. */\nexport function flag(key: string, fallback = false, source?: FlagsSource | null): Readable<boolean> {\n return flagStore(source, fallback, (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 featureValue<T extends JsonValue>(key: string, fallback: T, source?: FlagsSource | null): Readable<FlagValue<T>> {\n return flagStore(source, fallback as FlagValue<T>, (r) => r.getValue(key, fallback));\n}\n\n/** A flag's value plus why it was served (reason, rule, variation, experiment). */\nexport function flagDetails<T extends JsonValue>(key: string, fallback: T, source?: FlagsSource | null): Readable<FlagDetails<FlagValue<T>>> {\n return flagStore<FlagDetails<FlagValue<T>>>(source, { key, value: fallback as FlagValue<T>, reason: \"FLAG_NOT_FOUND\", inExperiment: false, usedFallback: true }, (r) => r.getDetails(key, fallback));\n}\n\n/** Every flag's value (records no exposures). */\nexport function allFlags(source?: FlagsSource | null): Readable<Record<string, JsonValue>> {\n return flagStore<Record<string, JsonValue>>(source, {}, (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 flagsReady(source?: FlagsSource | null): Readable<boolean> {\n return flagStore(source, false, (r) => r.isReady());\n}\n\n/**\n * Feature flags only (no guides or analytics): a flags client to pass to the stores\n * (`flag(\"x\", false, flags)`) or to `setFlagsContext(flags)`. Runs in the browser and on servers.\n */\nexport function createFlags(options: FlagsClientOptions): FlagsClient {\n return createFlagsClient(options);\n}\n"],"mappings":"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","names":["import_svelte","import_store","import_store","import_flags","createCore"]}
package/dist/index.d.cts CHANGED
@@ -1,10 +1,36 @@
1
+ import { GuidelyClient, PublishedGuide, ChecklistProgressState, GuidelyOptions, GuidelyUser } from '@guidelytech/sdk-core';
2
+ export { ChecklistProgressState, GuidelyClient, GuidelyEvents, GuidelyOptions, GuidelyUser, PublishedGuide } from '@guidelytech/sdk-core';
1
3
  import * as svelte_store from 'svelte/store';
2
4
  import { Readable } from 'svelte/store';
3
- import { PublishedGuide, ChecklistProgressState, GuidelyOptions, GuidelyClient, GuidelyUser } from '@guidelytech/sdk-core';
4
- export { ChecklistProgressState, GuidelyClient, GuidelyEvents, GuidelyOptions, GuidelyUser, PublishedGuide } from '@guidelytech/sdk-core';
5
+ import { FlagsReader, JsonValue, FlagsClientOptions, FlagsClient, FlagValue, FlagDetails } from '@guidelytech/flags';
6
+ export { Attributes, EvaluationResult, FlagDetails, FlagsBootstrap, FlagsClient, FlagsClientOptions, FlagsPayload, FlagsReader, JsonValue, createStaticFlags } from '@guidelytech/flags';
5
7
 
6
8
  /** The current client (null on the server and before `createGuidely`). */
7
9
  declare const guidely: svelte_store.Writable<GuidelyClient | null>;
10
+
11
+ /** Where flag stores read from: a Guidely client, a flags client, or a static snapshot. */
12
+ type FlagsSource = GuidelyClient | FlagsReader;
13
+ /**
14
+ * Makes `source` the flags for this component and its children (call during component
15
+ * initialisation). Accepts a client, a flags client, a static snapshot or a store of one.
16
+ */
17
+ declare function setFlagsContext(source: FlagsSource | Readable<FlagsSource | null> | null): void;
18
+ /** `$flag` is true when the boolean flag is on; `fallback` (default `false`) until flags load, or when missing. */
19
+ declare function flag(key: string, fallback?: boolean, source?: FlagsSource | null): Readable<boolean>;
20
+ /** A flag's value (string, number, JSON…); `fallback` when missing or of a different type. */
21
+ declare function featureValue<T extends JsonValue>(key: string, fallback: T, source?: FlagsSource | null): Readable<FlagValue<T>>;
22
+ /** A flag's value plus why it was served (reason, rule, variation, experiment). */
23
+ declare function flagDetails<T extends JsonValue>(key: string, fallback: T, source?: FlagsSource | null): Readable<FlagDetails<FlagValue<T>>>;
24
+ /** Every flag's value (records no exposures). */
25
+ declare function allFlags(source?: FlagsSource | null): Readable<Record<string, JsonValue>>;
26
+ /** True once flags have loaded (or failed to and serve fallbacks); true from the start with a bootstrap payload. */
27
+ declare function flagsReady(source?: FlagsSource | null): Readable<boolean>;
28
+ /**
29
+ * Feature flags only (no guides or analytics): a flags client to pass to the stores
30
+ * (`flag("x", false, flags)`) or to `setFlagsContext(flags)`. Runs in the browser and on servers.
31
+ */
32
+ declare function createFlags(options: FlagsClientOptions): FlagsClient;
33
+
8
34
  /**
9
35
  * Creates the client and stores it in the `guidely` store. Browser-only: throws a helpful error
10
36
  * when called during SSR (call it from `onMount` or use `@guidelytech/sveltekit`'s `initGuidely`).
@@ -41,4 +67,4 @@ declare function guidelyTarget(node: HTMLElement, id?: string): {
41
67
  destroy(): void;
42
68
  };
43
69
 
44
- export { activeGuide, checklistProgress, createGuidely, destroyGuidely, getGuidelyContext, guideCompleted, guideTrigger, guidely, guidelyReady, guidelyTarget, setGuidelyContext, syncGuidelyUser };
70
+ export { type FlagsSource, activeGuide, allFlags, checklistProgress, createFlags, createGuidely, destroyGuidely, featureValue, flag, flagDetails, flagsReady, getGuidelyContext, guideCompleted, guideTrigger, guidely, guidelyReady, guidelyTarget, setFlagsContext, setGuidelyContext, syncGuidelyUser };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,36 @@
1
+ import { GuidelyClient, PublishedGuide, ChecklistProgressState, GuidelyOptions, GuidelyUser } from '@guidelytech/sdk-core';
2
+ export { ChecklistProgressState, GuidelyClient, GuidelyEvents, GuidelyOptions, GuidelyUser, PublishedGuide } from '@guidelytech/sdk-core';
1
3
  import * as svelte_store from 'svelte/store';
2
4
  import { Readable } from 'svelte/store';
3
- import { PublishedGuide, ChecklistProgressState, GuidelyOptions, GuidelyClient, GuidelyUser } from '@guidelytech/sdk-core';
4
- export { ChecklistProgressState, GuidelyClient, GuidelyEvents, GuidelyOptions, GuidelyUser, PublishedGuide } from '@guidelytech/sdk-core';
5
+ import { FlagsReader, JsonValue, FlagsClientOptions, FlagsClient, FlagValue, FlagDetails } from '@guidelytech/flags';
6
+ export { Attributes, EvaluationResult, FlagDetails, FlagsBootstrap, FlagsClient, FlagsClientOptions, FlagsPayload, FlagsReader, JsonValue, createStaticFlags } from '@guidelytech/flags';
5
7
 
6
8
  /** The current client (null on the server and before `createGuidely`). */
7
9
  declare const guidely: svelte_store.Writable<GuidelyClient | null>;
10
+
11
+ /** Where flag stores read from: a Guidely client, a flags client, or a static snapshot. */
12
+ type FlagsSource = GuidelyClient | FlagsReader;
13
+ /**
14
+ * Makes `source` the flags for this component and its children (call during component
15
+ * initialisation). Accepts a client, a flags client, a static snapshot or a store of one.
16
+ */
17
+ declare function setFlagsContext(source: FlagsSource | Readable<FlagsSource | null> | null): void;
18
+ /** `$flag` is true when the boolean flag is on; `fallback` (default `false`) until flags load, or when missing. */
19
+ declare function flag(key: string, fallback?: boolean, source?: FlagsSource | null): Readable<boolean>;
20
+ /** A flag's value (string, number, JSON…); `fallback` when missing or of a different type. */
21
+ declare function featureValue<T extends JsonValue>(key: string, fallback: T, source?: FlagsSource | null): Readable<FlagValue<T>>;
22
+ /** A flag's value plus why it was served (reason, rule, variation, experiment). */
23
+ declare function flagDetails<T extends JsonValue>(key: string, fallback: T, source?: FlagsSource | null): Readable<FlagDetails<FlagValue<T>>>;
24
+ /** Every flag's value (records no exposures). */
25
+ declare function allFlags(source?: FlagsSource | null): Readable<Record<string, JsonValue>>;
26
+ /** True once flags have loaded (or failed to and serve fallbacks); true from the start with a bootstrap payload. */
27
+ declare function flagsReady(source?: FlagsSource | null): Readable<boolean>;
28
+ /**
29
+ * Feature flags only (no guides or analytics): a flags client to pass to the stores
30
+ * (`flag("x", false, flags)`) or to `setFlagsContext(flags)`. Runs in the browser and on servers.
31
+ */
32
+ declare function createFlags(options: FlagsClientOptions): FlagsClient;
33
+
8
34
  /**
9
35
  * Creates the client and stores it in the `guidely` store. Browser-only: throws a helpful error
10
36
  * when called during SSR (call it from `onMount` or use `@guidelytech/sveltekit`'s `initGuidely`).
@@ -41,4 +67,4 @@ declare function guidelyTarget(node: HTMLElement, id?: string): {
41
67
  destroy(): void;
42
68
  };
43
69
 
44
- export { activeGuide, checklistProgress, createGuidely, destroyGuidely, getGuidelyContext, guideCompleted, guideTrigger, guidely, guidelyReady, guidelyTarget, setGuidelyContext, syncGuidelyUser };
70
+ export { type FlagsSource, activeGuide, allFlags, checklistProgress, createFlags, createGuidely, destroyGuidely, featureValue, flag, flagDetails, flagsReady, getGuidelyContext, guideCompleted, guideTrigger, guidely, guidelyReady, guidelyTarget, setFlagsContext, setGuidelyContext, syncGuidelyUser };
package/dist/index.js CHANGED
@@ -1,8 +1,84 @@
1
1
  // src/index.ts
2
2
  import { createGuidely as createCore } from "@guidelytech/sdk-core";
3
- import { getContext, setContext } from "svelte";
4
- import { derived, get, readable, writable } from "svelte/store";
3
+ import { getContext as getContext2, setContext as setContext2 } from "svelte";
4
+ import { derived as derived2, get, readable as readable2 } from "svelte/store";
5
+
6
+ // src/client-store.ts
7
+ import { writable } from "svelte/store";
5
8
  var guidely = writable(null);
9
+
10
+ // src/flags.ts
11
+ import { createFlagsClient, jsonEquals } from "@guidelytech/flags";
12
+ import { getContext, setContext } from "svelte";
13
+ import { derived, readable } from "svelte/store";
14
+ var FLAGS_KEY = /* @__PURE__ */ Symbol.for("guidely.flags");
15
+ function readerOf(source) {
16
+ if (!source) return null;
17
+ return "isFeatureEnabled" in source && "flags" in source ? source.flags : source;
18
+ }
19
+ function setFlagsContext(source) {
20
+ const store = source && "subscribe" in source && !("isEnabled" in source) && !("isFeatureEnabled" in source) ? derived(source, readerOf) : readable(readerOf(source));
21
+ setContext(FLAGS_KEY, store);
22
+ }
23
+ function sourceStore(source) {
24
+ if (source) return readable(readerOf(source));
25
+ try {
26
+ const ctx = getContext(FLAGS_KEY);
27
+ if (ctx) return ctx;
28
+ } catch {
29
+ }
30
+ return derived(guidely, (c) => c?.flags ?? null);
31
+ }
32
+ function flagStore(source, initial, select) {
33
+ const src = sourceStore(source);
34
+ return readable(initial, (set) => {
35
+ let off = () => {
36
+ };
37
+ let last = null;
38
+ const unsub = src.subscribe((reader) => {
39
+ off();
40
+ off = () => {
41
+ };
42
+ if (!reader) {
43
+ last = { value: initial };
44
+ return set(initial);
45
+ }
46
+ const update = () => {
47
+ const value = select(reader);
48
+ if (last && jsonEquals(last.value, value)) return;
49
+ last = { value };
50
+ set(value);
51
+ };
52
+ update();
53
+ off = reader.subscribe(update);
54
+ });
55
+ return () => {
56
+ off();
57
+ unsub();
58
+ };
59
+ });
60
+ }
61
+ function flag(key, fallback = false, source) {
62
+ return flagStore(source, fallback, (r) => r.isEnabled(key, fallback));
63
+ }
64
+ function featureValue(key, fallback, source) {
65
+ return flagStore(source, fallback, (r) => r.getValue(key, fallback));
66
+ }
67
+ function flagDetails(key, fallback, source) {
68
+ return flagStore(source, { key, value: fallback, reason: "FLAG_NOT_FOUND", inExperiment: false, usedFallback: true }, (r) => r.getDetails(key, fallback));
69
+ }
70
+ function allFlags(source) {
71
+ return flagStore(source, {}, (r) => r.getAll());
72
+ }
73
+ function flagsReady(source) {
74
+ return flagStore(source, false, (r) => r.isReady());
75
+ }
76
+ function createFlags(options) {
77
+ return createFlagsClient(options);
78
+ }
79
+
80
+ // src/index.ts
81
+ import { createStaticFlags } from "@guidelytech/flags";
6
82
  function createGuidely(options) {
7
83
  if (typeof window === "undefined") throw new Error("[Guidely] createGuidely() can only run in the browser. Call it inside onMount(), or use initGuidely() from @guidelytech/sveltekit.");
8
84
  const existing = get(guidely);
@@ -36,13 +112,13 @@ function destroyGuidely() {
36
112
  }
37
113
  var CONTEXT_KEY = /* @__PURE__ */ Symbol.for("guidely");
38
114
  function setGuidelyContext(client) {
39
- setContext(CONTEXT_KEY, client);
115
+ setContext2(CONTEXT_KEY, client);
40
116
  }
41
117
  function getGuidelyContext() {
42
- return getContext(CONTEXT_KEY) ?? get(guidely);
118
+ return getContext2(CONTEXT_KEY) ?? get(guidely);
43
119
  }
44
120
  function fromClient(initial, compute, events) {
45
- return readable(initial, (set) => {
121
+ return readable2(initial, (set) => {
46
122
  let offs = [];
47
123
  const unsub = guidely.subscribe((client) => {
48
124
  offs.forEach((off) => off());
@@ -64,7 +140,7 @@ function checklistProgress(idOrKey) {
64
140
  return fromClient({ completedTaskIds: [], completedAt: null, dismissedAt: null }, (c) => c.getChecklistProgress(idOrKey), ["ready", "identify", "checklist:task", "checklist:complete"]);
65
141
  }
66
142
  function guideCompleted(idOrKey) {
67
- return derived(fromClient(false, (c) => c.isGuideCompleted(idOrKey), ["ready", "identify", "guide:complete"]), (v) => v);
143
+ return derived2(fromClient(false, (c) => c.isGuideCompleted(idOrKey), ["ready", "identify", "guide:complete"]), (v) => v);
68
144
  }
69
145
  function guideTrigger(node, key) {
70
146
  let current = key;
@@ -94,15 +170,23 @@ function guidelyTarget(node, id) {
94
170
  }
95
171
  export {
96
172
  activeGuide,
173
+ allFlags,
97
174
  checklistProgress,
175
+ createFlags,
98
176
  createGuidely,
177
+ createStaticFlags,
99
178
  destroyGuidely,
179
+ featureValue,
180
+ flag,
181
+ flagDetails,
182
+ flagsReady,
100
183
  getGuidelyContext,
101
184
  guideCompleted,
102
185
  guideTrigger,
103
186
  guidely,
104
187
  guidelyReady,
105
188
  guidelyTarget,
189
+ setFlagsContext,
106
190
  setGuidelyContext,
107
191
  syncGuidelyUser
108
192
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { createGuidely as createCore, type ChecklistProgressState, type GuidelyClient, type GuidelyOptions, type GuidelyUser, type PublishedGuide } from \"@guidelytech/sdk-core\";\nimport { getContext, setContext } from \"svelte\";\nimport { derived, get, readable, writable, type Readable } from \"svelte/store\";\n\nexport type { GuidelyClient, GuidelyOptions, GuidelyEvents, GuidelyUser, ChecklistProgressState, PublishedGuide } from \"@guidelytech/sdk-core\";\n\n/** The current client (null on the server and before `createGuidely`). */\nexport const guidely = writable<GuidelyClient | null>(null);\n\n/**\n * Creates the client and stores it in the `guidely` store. Browser-only: throws a helpful error\n * when called during SSR (call it from `onMount` or use `@guidelytech/sveltekit`'s `initGuidely`).\n */\nexport function createGuidely(options: GuidelyOptions): GuidelyClient {\n if (typeof window === \"undefined\") throw new Error(\"[Guidely] createGuidely() can only run in the browser. Call it inside onMount(), or use initGuidely() from @guidelytech/sveltekit.\");\n const existing = get(guidely);\n if (existing && !existing.getState().destroyed && existing.options.projectId === options.projectId) return existing;\n existing?.destroy();\n const client = createCore(options);\n guidely.set(client);\n if (pendingUser !== undefined) applyUser(client, pendingUser);\n pendingUser = undefined;\n return client;\n}\n\nconst appliedUser = new WeakMap<GuidelyClient, string | null>();\nlet pendingUser: GuidelyUser | null | undefined;\n\n/**\n * Keeps the Guidely user in step with your app's signed-in user. Idempotent: call it whenever your\n * user data may have changed (a `$effect` / `$:` on layout data). A user identifies (again only when\n * id, traits or account change); `null` resets a previously identified user, including one restored\n * from storage; `undefined` means \"not known yet\" and changes nothing. No-op on the server; called\n * before `createGuidely`, it is applied when the client is created.\n */\nexport function syncGuidelyUser(user: GuidelyUser | null | undefined): void {\n if (typeof window === \"undefined\") return;\n const client = get(guidely);\n if (client) applyUser(client, user);\n else pendingUser = user;\n}\n\nfunction applyUser(client: GuidelyClient, user: GuidelyUser | null | undefined): void {\n if (user === undefined) return;\n const key = user ? JSON.stringify([String(user.id), user.traits ?? null, user.account ?? null]) : null;\n if (appliedUser.has(client) && appliedUser.get(client) === key) return;\n appliedUser.set(client, key);\n if (user) void client.identify(user.id, user.traits, user.account);\n else if (client.getState().user?.id) client.reset();\n}\n\n/** Destroys the current client and clears the store. */\nexport function destroyGuidely(): void {\n get(guidely)?.destroy();\n guidely.set(null);\n}\n\nconst CONTEXT_KEY = Symbol.for(\"guidely\");\n\nexport function setGuidelyContext(client: GuidelyClient | null): void {\n setContext(CONTEXT_KEY, client);\n}\n\nexport function getGuidelyContext(): GuidelyClient | null {\n return (getContext(CONTEXT_KEY) as GuidelyClient | null | undefined) ?? get(guidely);\n}\n\nfunction fromClient<T>(initial: T, compute: (client: GuidelyClient) => T, events: Array<Parameters<GuidelyClient[\"on\"]>[0]>): Readable<T> {\n return readable<T>(initial, (set) => {\n let offs: Array<() => void> = [];\n const unsub = guidely.subscribe((client) => {\n offs.forEach((off) => off());\n offs = [];\n if (!client) return set(initial);\n const update = () => set(compute(client));\n update();\n offs = events.map((e) => client.on(e, update as never));\n });\n return () => {\n offs.forEach((off) => off());\n unsub();\n };\n });\n}\n\n/** True once the configuration has loaded. */\nexport const guidelyReady: Readable<boolean> = fromClient(false, (c) => c.getState().ready, [\"ready\"]);\n\n/** The guide currently showing (flows, modals, surveys), or null. */\nexport const activeGuide: Readable<PublishedGuide | null> = fromClient<PublishedGuide | null>(null, (c) => c.getActiveGuide(), [\"ready\", \"guide:start\", \"guide:complete\", \"guide:dismiss\", \"guide:skip\", \"identify\"]);\n\n/** Live checklist progress for a checklist guide (by id or key). */\nexport function checklistProgress(idOrKey: string): Readable<ChecklistProgressState> {\n return fromClient<ChecklistProgressState>({ completedTaskIds: [], completedAt: null, dismissedAt: null }, (c) => c.getChecklistProgress(idOrKey), [\"ready\", \"identify\", \"checklist:task\", \"checklist:complete\"]);\n}\n\n/** Is the given guide completed for the current user? */\nexport function guideCompleted(idOrKey: string): Readable<boolean> {\n return derived(fromClient<boolean>(false, (c) => c.isGuideCompleted(idOrKey), [\"ready\", \"identify\", \"guide:complete\"]), (v) => v);\n}\n\n/** `use:guideTrigger={\"guide-key\"}` — clicking the element starts the guide. */\nexport function guideTrigger(node: HTMLElement, key: string): { update(key: string): void; destroy(): void } {\n let current = key;\n const onClick = (e: Event) => {\n e.preventDefault();\n get(guidely)?.startGuide(current);\n };\n node.addEventListener(\"click\", onClick);\n node.setAttribute(\"data-guidely-trigger\", current);\n return {\n update(next: string) {\n current = next;\n node.setAttribute(\"data-guidely-trigger\", next);\n },\n destroy() {\n node.removeEventListener(\"click\", onClick);\n },\n };\n}\n\n/** `use:guidelyTarget={\"id\"}` — marks an element as a guide target (`data-tour`). No runtime behaviour. */\nexport function guidelyTarget(node: HTMLElement, id?: string): { update(id?: string): void; destroy(): void } {\n const apply = (v?: string) => {\n if (v) node.setAttribute(\"data-tour\", v);\n };\n apply(id);\n return { update: apply, destroy() {} };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/client-store.ts","../src/flags.ts"],"sourcesContent":["import { createGuidely as createCore, type ChecklistProgressState, type GuidelyClient, type GuidelyOptions, type GuidelyUser, type PublishedGuide } from \"@guidelytech/sdk-core\";\nimport { getContext, setContext } from \"svelte\";\nimport { derived, get, readable, type Readable } from \"svelte/store\";\nimport { guidely } from \"./client-store\";\n\nexport type { GuidelyClient, GuidelyOptions, GuidelyEvents, GuidelyUser, ChecklistProgressState, PublishedGuide } from \"@guidelytech/sdk-core\";\nexport { guidely };\nexport { flag, featureValue, flagDetails, allFlags, flagsReady, createFlags, setFlagsContext } from \"./flags\";\nexport type { FlagsSource } from \"./flags\";\nexport { createStaticFlags } from \"@guidelytech/flags\";\nexport type { FlagsClient, FlagsClientOptions, FlagDetails, FlagsBootstrap, FlagsPayload, FlagsReader, Attributes, JsonValue, EvaluationResult } from \"@guidelytech/flags\";\n\n/**\n * Creates the client and stores it in the `guidely` store. Browser-only: throws a helpful error\n * when called during SSR (call it from `onMount` or use `@guidelytech/sveltekit`'s `initGuidely`).\n */\nexport function createGuidely(options: GuidelyOptions): GuidelyClient {\n if (typeof window === \"undefined\") throw new Error(\"[Guidely] createGuidely() can only run in the browser. Call it inside onMount(), or use initGuidely() from @guidelytech/sveltekit.\");\n const existing = get(guidely);\n if (existing && !existing.getState().destroyed && existing.options.projectId === options.projectId) return existing;\n existing?.destroy();\n const client = createCore(options);\n guidely.set(client);\n if (pendingUser !== undefined) applyUser(client, pendingUser);\n pendingUser = undefined;\n return client;\n}\n\nconst appliedUser = new WeakMap<GuidelyClient, string | null>();\nlet pendingUser: GuidelyUser | null | undefined;\n\n/**\n * Keeps the Guidely user in step with your app's signed-in user. Idempotent: call it whenever your\n * user data may have changed (a `$effect` / `$:` on layout data). A user identifies (again only when\n * id, traits or account change); `null` resets a previously identified user, including one restored\n * from storage; `undefined` means \"not known yet\" and changes nothing. No-op on the server; called\n * before `createGuidely`, it is applied when the client is created.\n */\nexport function syncGuidelyUser(user: GuidelyUser | null | undefined): void {\n if (typeof window === \"undefined\") return;\n const client = get(guidely);\n if (client) applyUser(client, user);\n else pendingUser = user;\n}\n\nfunction applyUser(client: GuidelyClient, user: GuidelyUser | null | undefined): void {\n if (user === undefined) return;\n const key = user ? JSON.stringify([String(user.id), user.traits ?? null, user.account ?? null]) : null;\n if (appliedUser.has(client) && appliedUser.get(client) === key) return;\n appliedUser.set(client, key);\n if (user) void client.identify(user.id, user.traits, user.account);\n else if (client.getState().user?.id) client.reset();\n}\n\n/** Destroys the current client and clears the store. */\nexport function destroyGuidely(): void {\n get(guidely)?.destroy();\n guidely.set(null);\n}\n\nconst CONTEXT_KEY = Symbol.for(\"guidely\");\n\nexport function setGuidelyContext(client: GuidelyClient | null): void {\n setContext(CONTEXT_KEY, client);\n}\n\nexport function getGuidelyContext(): GuidelyClient | null {\n return (getContext(CONTEXT_KEY) as GuidelyClient | null | undefined) ?? get(guidely);\n}\n\nfunction fromClient<T>(initial: T, compute: (client: GuidelyClient) => T, events: Array<Parameters<GuidelyClient[\"on\"]>[0]>): Readable<T> {\n return readable<T>(initial, (set) => {\n let offs: Array<() => void> = [];\n const unsub = guidely.subscribe((client) => {\n offs.forEach((off) => off());\n offs = [];\n if (!client) return set(initial);\n const update = () => set(compute(client));\n update();\n offs = events.map((e) => client.on(e, update as never));\n });\n return () => {\n offs.forEach((off) => off());\n unsub();\n };\n });\n}\n\n/** True once the configuration has loaded. */\nexport const guidelyReady: Readable<boolean> = fromClient(false, (c) => c.getState().ready, [\"ready\"]);\n\n/** The guide currently showing (flows, modals, surveys), or null. */\nexport const activeGuide: Readable<PublishedGuide | null> = fromClient<PublishedGuide | null>(null, (c) => c.getActiveGuide(), [\"ready\", \"guide:start\", \"guide:complete\", \"guide:dismiss\", \"guide:skip\", \"identify\"]);\n\n/** Live checklist progress for a checklist guide (by id or key). */\nexport function checklistProgress(idOrKey: string): Readable<ChecklistProgressState> {\n return fromClient<ChecklistProgressState>({ completedTaskIds: [], completedAt: null, dismissedAt: null }, (c) => c.getChecklistProgress(idOrKey), [\"ready\", \"identify\", \"checklist:task\", \"checklist:complete\"]);\n}\n\n/** Is the given guide completed for the current user? */\nexport function guideCompleted(idOrKey: string): Readable<boolean> {\n return derived(fromClient<boolean>(false, (c) => c.isGuideCompleted(idOrKey), [\"ready\", \"identify\", \"guide:complete\"]), (v) => v);\n}\n\n/** `use:guideTrigger={\"guide-key\"}` — clicking the element starts the guide. */\nexport function guideTrigger(node: HTMLElement, key: string): { update(key: string): void; destroy(): void } {\n let current = key;\n const onClick = (e: Event) => {\n e.preventDefault();\n get(guidely)?.startGuide(current);\n };\n node.addEventListener(\"click\", onClick);\n node.setAttribute(\"data-guidely-trigger\", current);\n return {\n update(next: string) {\n current = next;\n node.setAttribute(\"data-guidely-trigger\", next);\n },\n destroy() {\n node.removeEventListener(\"click\", onClick);\n },\n };\n}\n\n/** `use:guidelyTarget={\"id\"}` — marks an element as a guide target (`data-tour`). No runtime behaviour. */\nexport function guidelyTarget(node: HTMLElement, id?: string): { update(id?: string): void; destroy(): void } {\n const apply = (v?: string) => {\n if (v) node.setAttribute(\"data-tour\", v);\n };\n apply(id);\n return { update: apply, destroy() {} };\n}\n","import type { GuidelyClient } from \"@guidelytech/sdk-core\";\nimport { writable } from \"svelte/store\";\n\n/** The current client (null on the server and before `createGuidely`). */\nexport const guidely = writable<GuidelyClient | null>(null);\n","import { createFlagsClient, jsonEquals, type FlagDetails, type FlagValue, type FlagsClient, type FlagsClientOptions, type FlagsReader, type JsonValue } from \"@guidelytech/flags\";\nimport type { GuidelyClient } from \"@guidelytech/sdk-core\";\nimport { getContext, setContext } from \"svelte\";\nimport { derived, readable, type Readable } from \"svelte/store\";\nimport { guidely } from \"./client-store\";\n\n/** Where flag stores read from: a Guidely client, a flags client, or a static snapshot. */\nexport type FlagsSource = GuidelyClient | FlagsReader;\n\nconst FLAGS_KEY = Symbol.for(\"guidely.flags\");\n\nfunction readerOf(source: FlagsSource | null | undefined): FlagsReader | null {\n if (!source) return null;\n return \"isFeatureEnabled\" in source && \"flags\" in source ? (source as GuidelyClient).flags : (source as FlagsReader);\n}\n\n/**\n * Makes `source` the flags for this component and its children (call during component\n * initialisation). Accepts a client, a flags client, a static snapshot or a store of one.\n */\nexport function setFlagsContext(source: FlagsSource | Readable<FlagsSource | null> | null): void {\n const store = source && \"subscribe\" in source && !(\"isEnabled\" in source) && !(\"isFeatureEnabled\" in source) ? derived(source as Readable<FlagsSource | null>, readerOf) : readable(readerOf(source as FlagsSource | null));\n setContext(FLAGS_KEY, store);\n}\n\n/** An explicit source, else the component context (`setFlagsContext` / `initGuidely`), else the global `guidely` client. */\nfunction sourceStore(source?: FlagsSource | null): Readable<FlagsReader | null> {\n if (source) return readable(readerOf(source));\n try {\n const ctx = getContext<Readable<FlagsReader | null> | undefined>(FLAGS_KEY);\n if (ctx) return ctx;\n } catch {\n /* not during component initialisation */\n }\n return derived(guidely, (c) => c?.flags ?? null);\n}\n\nfunction flagStore<T>(source: FlagsSource | null | undefined, initial: T, select: (r: FlagsReader) => T): Readable<T> {\n const src = sourceStore(source);\n return readable<T>(initial, (set) => {\n let off = () => {};\n let last: { value: T } | null = null;\n const unsub = src.subscribe((reader) => {\n off();\n off = () => {};\n if (!reader) {\n last = { value: initial };\n return set(initial);\n }\n const update = () => {\n const value = select(reader);\n if (last && jsonEquals(last.value, value)) return; // object flags keep their identity\n last = { value };\n set(value);\n };\n update();\n off = reader.subscribe(update);\n });\n return () => {\n off();\n unsub();\n };\n });\n}\n\n/** `$flag` is true when the boolean flag is on; `fallback` (default `false`) until flags load, or when missing. */\nexport function flag(key: string, fallback = false, source?: FlagsSource | null): Readable<boolean> {\n return flagStore(source, fallback, (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 featureValue<T extends JsonValue>(key: string, fallback: T, source?: FlagsSource | null): Readable<FlagValue<T>> {\n return flagStore(source, fallback as FlagValue<T>, (r) => r.getValue(key, fallback));\n}\n\n/** A flag's value plus why it was served (reason, rule, variation, experiment). */\nexport function flagDetails<T extends JsonValue>(key: string, fallback: T, source?: FlagsSource | null): Readable<FlagDetails<FlagValue<T>>> {\n return flagStore<FlagDetails<FlagValue<T>>>(source, { key, value: fallback as FlagValue<T>, reason: \"FLAG_NOT_FOUND\", inExperiment: false, usedFallback: true }, (r) => r.getDetails(key, fallback));\n}\n\n/** Every flag's value (records no exposures). */\nexport function allFlags(source?: FlagsSource | null): Readable<Record<string, JsonValue>> {\n return flagStore<Record<string, JsonValue>>(source, {}, (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 flagsReady(source?: FlagsSource | null): Readable<boolean> {\n return flagStore(source, false, (r) => r.isReady());\n}\n\n/**\n * Feature flags only (no guides or analytics): a flags client to pass to the stores\n * (`flag(\"x\", false, flags)`) or to `setFlagsContext(flags)`. Runs in the browser and on servers.\n */\nexport function createFlags(options: FlagsClientOptions): FlagsClient {\n return createFlagsClient(options);\n}\n"],"mappings":"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","names":["getContext","setContext","derived","readable","setContext","getContext","readable","derived"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guidelytech/svelte",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "Svelte stores, actions and client 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.1",
24
- "@guidelytech/shared": "0.6.1"
23
+ "@guidelytech/flags": "0.7.0",
24
+ "@guidelytech/shared": "0.7.0",
25
+ "@guidelytech/sdk-core": "0.7.0"
25
26
  },
26
27
  "devDependencies": {
27
28
  "tsup": "^8.5.1",