@savvagent/solid 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -22,33 +22,93 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  FlagClient: () => import_sdk2.FlagClient,
24
24
  SavvagentProvider: () => SavvagentProvider,
25
+ createEnvironment: () => createEnvironment,
25
26
  createFlag: () => createFlag,
26
27
  createFlagValue: () => createFlagValue,
28
+ createFlags: () => createFlags,
29
+ createTrackError: () => createTrackError,
30
+ createUser: () => createUser,
27
31
  createUserSignals: () => createUserSignals,
32
+ createWithFlag: () => createWithFlag,
28
33
  trackError: () => trackError,
29
34
  useSavvagent: () => useSavvagent
30
35
  });
31
36
  module.exports = __toCommonJS(index_exports);
37
+ var import_web = require("solid-js/web");
32
38
  var import_solid_js = require("solid-js");
33
39
  var import_sdk = require("@savvagent/sdk");
34
40
  var import_sdk2 = require("@savvagent/sdk");
35
41
  var SavvagentContext = (0, import_solid_js.createContext)();
36
42
  function SavvagentProvider(props) {
37
- const client = new import_sdk.FlagClient(props.config);
43
+ const [isReady, setIsReady] = (0, import_solid_js.createSignal)(false);
44
+ let client;
45
+ try {
46
+ client = new import_sdk.FlagClient(props.config);
47
+ setIsReady(true);
48
+ } catch (error) {
49
+ console.error("[Savvagent] Failed to initialize client:", error);
50
+ props.config.onError?.(error);
51
+ client = new import_sdk.FlagClient({
52
+ ...props.config,
53
+ apiKey: ""
54
+ });
55
+ }
56
+ const normalizedDefaultContext = (0, import_solid_js.createMemo)(() => ({
57
+ application_id: props.defaultContext?.applicationId,
58
+ environment: props.defaultContext?.environment,
59
+ organization_id: props.defaultContext?.organizationId,
60
+ user_id: props.defaultContext?.userId,
61
+ anonymous_id: props.defaultContext?.anonymousId,
62
+ session_id: props.defaultContext?.sessionId,
63
+ language: props.defaultContext?.language,
64
+ attributes: props.defaultContext?.attributes
65
+ }));
66
+ if (props.defaultContext?.userId) {
67
+ client.setUserId(props.defaultContext.userId);
68
+ }
38
69
  (0, import_solid_js.onCleanup)(() => {
39
70
  client.close();
40
71
  });
41
- return /* @__PURE__ */ React.createElement(SavvagentContext.Provider, { value: client }, props.children);
72
+ const contextValue = {
73
+ client,
74
+ isReady,
75
+ defaultContext: normalizedDefaultContext
76
+ };
77
+ return (0, import_web.createComponent)(SavvagentContext.Provider, {
78
+ value: contextValue,
79
+ get children() {
80
+ return props.children;
81
+ }
82
+ });
42
83
  }
43
84
  function useSavvagent() {
44
- const client = (0, import_solid_js.useContext)(SavvagentContext);
45
- if (!client) {
85
+ const context = (0, import_solid_js.useContext)(SavvagentContext);
86
+ if (!context) {
46
87
  throw new Error("useSavvagent must be used within a SavvagentProvider");
47
88
  }
48
- return client;
89
+ return context;
90
+ }
91
+ function deepEqual(a, b) {
92
+ if (a === b) return true;
93
+ if (a === null || b === null) return a === b;
94
+ if (typeof a !== "object" || typeof b !== "object") return false;
95
+ const keysA = Object.keys(a);
96
+ const keysB = Object.keys(b);
97
+ if (keysA.length !== keysB.length) return false;
98
+ for (const key of keysA) {
99
+ if (!keysB.includes(key)) return false;
100
+ if (!deepEqual(a[key], b[key])) {
101
+ return false;
102
+ }
103
+ }
104
+ return true;
49
105
  }
50
106
  function createFlag(flagKey, options = {}) {
51
- const client = useSavvagent();
107
+ const {
108
+ client,
109
+ isReady,
110
+ defaultContext
111
+ } = useSavvagent();
52
112
  const {
53
113
  context,
54
114
  defaultValue = false,
@@ -56,23 +116,39 @@ function createFlag(flagKey, options = {}) {
56
116
  onError
57
117
  } = options;
58
118
  const [trigger, setTrigger] = (0, import_solid_js.createSignal)(0);
59
- const [result] = (0, import_solid_js.createResource)(
60
- trigger,
61
- async () => {
62
- try {
63
- return await client.evaluate(flagKey, context);
64
- } catch (err) {
65
- const error2 = err;
66
- onError?.(error2);
67
- throw error2;
119
+ const mergedContext = (0, import_solid_js.createMemo)(() => {
120
+ const def = defaultContext();
121
+ return {
122
+ ...def,
123
+ ...context,
124
+ attributes: {
125
+ ...def?.attributes,
126
+ ...context?.attributes
68
127
  }
128
+ };
129
+ });
130
+ let prevContext;
131
+ const [result] = (0, import_solid_js.createResource)(() => ({
132
+ trigger: trigger(),
133
+ context: mergedContext(),
134
+ ready: isReady()
135
+ }), async (source) => {
136
+ if (!source.ready) {
137
+ return null;
138
+ }
139
+ try {
140
+ return await client.evaluate(flagKey, source.context);
141
+ } catch (err) {
142
+ const error2 = err;
143
+ onError?.(error2);
144
+ throw error2;
69
145
  }
70
- );
146
+ });
71
147
  const value = () => result()?.value ?? defaultValue;
72
148
  const loading = () => result.loading;
73
149
  const error = () => result.error ?? null;
74
150
  (0, import_solid_js.createEffect)(() => {
75
- if (!realtime) return;
151
+ if (!realtime || !isReady()) return;
76
152
  const unsubscribe = client.subscribe(flagKey, () => {
77
153
  setTrigger((t) => t + 1);
78
154
  });
@@ -80,6 +156,22 @@ function createFlag(flagKey, options = {}) {
80
156
  unsubscribe();
81
157
  });
82
158
  });
159
+ (0, import_solid_js.createEffect)(() => {
160
+ if (!isReady()) return;
161
+ const unsubscribe = client.onOverrideChange(() => {
162
+ setTrigger((t) => t + 1);
163
+ });
164
+ (0, import_solid_js.onCleanup)(() => {
165
+ unsubscribe();
166
+ });
167
+ });
168
+ (0, import_solid_js.createEffect)(() => {
169
+ const currentContext = mergedContext();
170
+ if (prevContext !== void 0 && !deepEqual(prevContext, currentContext)) {
171
+ setTrigger((t) => t + 1);
172
+ }
173
+ prevContext = currentContext;
174
+ });
83
175
  return {
84
176
  value,
85
177
  loading,
@@ -92,8 +184,176 @@ function createFlagValue(flagKey, options = {}) {
92
184
  const flag = createFlag(flagKey, options);
93
185
  return flag.value;
94
186
  }
187
+ function createFlags(flagKeys, options = {}) {
188
+ const {
189
+ client,
190
+ isReady,
191
+ defaultContext
192
+ } = useSavvagent();
193
+ const {
194
+ context,
195
+ defaultValues = {},
196
+ realtime = true,
197
+ onError
198
+ } = options;
199
+ const [trigger, setTrigger] = (0, import_solid_js.createSignal)(0);
200
+ const mergedContext = (0, import_solid_js.createMemo)(() => {
201
+ const def = defaultContext();
202
+ return {
203
+ ...def,
204
+ ...context,
205
+ attributes: {
206
+ ...def?.attributes,
207
+ ...context?.attributes
208
+ }
209
+ };
210
+ });
211
+ const initialValues = {};
212
+ const initialErrors = {};
213
+ const initialResults = {};
214
+ for (const key of flagKeys) {
215
+ initialValues[key] = defaultValues[key] ?? false;
216
+ initialErrors[key] = null;
217
+ initialResults[key] = null;
218
+ }
219
+ const [values, setValues] = (0, import_solid_js.createSignal)(initialValues);
220
+ const [errors, setErrors] = (0, import_solid_js.createSignal)(initialErrors);
221
+ const [results, setResults] = (0, import_solid_js.createSignal)(initialResults);
222
+ const [loading, setLoading] = (0, import_solid_js.createSignal)(true);
223
+ let prevContext;
224
+ const evaluateFlags = async () => {
225
+ if (!isReady()) return;
226
+ setLoading(true);
227
+ const newValues = {};
228
+ const newErrors = {};
229
+ const newResults = {};
230
+ const ctx = mergedContext();
231
+ await Promise.all(flagKeys.map(async (flagKey) => {
232
+ try {
233
+ const evalResult = await client.evaluate(flagKey, ctx);
234
+ newValues[flagKey] = evalResult.value;
235
+ newErrors[flagKey] = null;
236
+ newResults[flagKey] = evalResult;
237
+ } catch (err) {
238
+ const error = err;
239
+ newValues[flagKey] = defaultValues[flagKey] ?? false;
240
+ newErrors[flagKey] = error;
241
+ newResults[flagKey] = null;
242
+ onError?.(error, flagKey);
243
+ }
244
+ }));
245
+ setValues(newValues);
246
+ setErrors(newErrors);
247
+ setResults(newResults);
248
+ setLoading(false);
249
+ };
250
+ (0, import_solid_js.createEffect)(() => {
251
+ trigger();
252
+ if (isReady()) {
253
+ evaluateFlags();
254
+ }
255
+ });
256
+ (0, import_solid_js.createEffect)(() => {
257
+ if (!realtime || !isReady()) return;
258
+ const unsubscribes = flagKeys.map((flagKey) => client.subscribe(flagKey, () => {
259
+ setTrigger((t) => t + 1);
260
+ }));
261
+ (0, import_solid_js.onCleanup)(() => {
262
+ unsubscribes.forEach((unsubscribe) => unsubscribe());
263
+ });
264
+ });
265
+ (0, import_solid_js.createEffect)(() => {
266
+ if (!isReady()) return;
267
+ const unsubscribe = client.onOverrideChange(() => {
268
+ setTrigger((t) => t + 1);
269
+ });
270
+ (0, import_solid_js.onCleanup)(() => {
271
+ unsubscribe();
272
+ });
273
+ });
274
+ (0, import_solid_js.createEffect)(() => {
275
+ const currentContext = mergedContext();
276
+ if (prevContext !== void 0 && !deepEqual(prevContext, currentContext)) {
277
+ setTrigger((t) => t + 1);
278
+ }
279
+ prevContext = currentContext;
280
+ });
281
+ return {
282
+ values,
283
+ loading,
284
+ errors,
285
+ results,
286
+ refetch: () => setTrigger((t) => t + 1)
287
+ };
288
+ }
289
+ function createWithFlag(flagKey, callback, options = {}) {
290
+ const {
291
+ client,
292
+ isReady
293
+ } = useSavvagent();
294
+ const {
295
+ context,
296
+ onError
297
+ } = options;
298
+ (0, import_solid_js.createEffect)(() => {
299
+ if (!isReady()) return;
300
+ client.withFlag(flagKey, callback, context).catch((error) => {
301
+ console.error(`[Savvagent] Error in withFlag callback for ${flagKey}:`, error);
302
+ onError?.(error);
303
+ });
304
+ });
305
+ }
306
+ function createUser() {
307
+ const {
308
+ client
309
+ } = useSavvagent();
310
+ const [userId, setUserIdSignal] = (0, import_solid_js.createSignal)(client.getUserId());
311
+ const [anonymousId, setAnonymousIdSignal] = (0, import_solid_js.createSignal)(client.getAnonymousId());
312
+ const setUserId = (id) => {
313
+ client.setUserId(id);
314
+ setUserIdSignal(id);
315
+ };
316
+ const getUserId = () => {
317
+ return client.getUserId();
318
+ };
319
+ const setAnonymousId = (id) => {
320
+ client.setAnonymousId(id);
321
+ setAnonymousIdSignal(id);
322
+ };
323
+ const getAnonymousId = () => {
324
+ return client.getAnonymousId();
325
+ };
326
+ return {
327
+ userId,
328
+ setUserId,
329
+ getUserId,
330
+ anonymousId,
331
+ setAnonymousId,
332
+ getAnonymousId
333
+ };
334
+ }
335
+ function createEnvironment() {
336
+ const {
337
+ client
338
+ } = useSavvagent();
339
+ const [environment, setEnvironmentSignal] = (0, import_solid_js.createSignal)(client.getEnvironment());
340
+ const setEnvironment = (env) => {
341
+ client.setEnvironment(env);
342
+ setEnvironmentSignal(env);
343
+ };
344
+ const getEnvironment = () => {
345
+ return client.getEnvironment();
346
+ };
347
+ return {
348
+ environment,
349
+ setEnvironment,
350
+ getEnvironment
351
+ };
352
+ }
95
353
  function createUserSignals() {
96
- const client = useSavvagent();
354
+ const {
355
+ client
356
+ } = useSavvagent();
97
357
  const [userId, setUserIdSignal] = (0, import_solid_js.createSignal)(client.getUserId());
98
358
  const setUserId = (id) => {
99
359
  client.setUserId(id);
@@ -101,17 +361,32 @@ function createUserSignals() {
101
361
  };
102
362
  return [userId, setUserId];
103
363
  }
364
+ function createTrackError(flagKey, context) {
365
+ const {
366
+ client
367
+ } = useSavvagent();
368
+ return (error) => {
369
+ client.trackError(flagKey, error, context);
370
+ };
371
+ }
104
372
  function trackError(flagKey, error, context) {
105
- const client = useSavvagent();
373
+ const {
374
+ client
375
+ } = useSavvagent();
106
376
  client.trackError(flagKey, error, context);
107
377
  }
108
378
  // Annotate the CommonJS export names for ESM import in node:
109
379
  0 && (module.exports = {
110
380
  FlagClient,
111
381
  SavvagentProvider,
382
+ createEnvironment,
112
383
  createFlag,
113
384
  createFlagValue,
385
+ createFlags,
386
+ createTrackError,
387
+ createUser,
114
388
  createUserSignals,
389
+ createWithFlag,
115
390
  trackError,
116
391
  useSavvagent
117
392
  });