@flaggly/sdk 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -22,7 +22,9 @@ type FlagInput = {
22
22
  };
23
23
  };
24
24
  type FlagValue<FR extends FlagConfig = FlagConfig> = FR extends {
25
- type: "variant" | "payload";
25
+ type: "variant";
26
+ } ? FR["result"] : FR extends {
27
+ type: "payload";
26
28
  } ? FR["result"] : boolean;
27
29
  type FlagValues<FD extends FlagSchema = FlagSchema> = {
28
30
  [K in keyof FD]: FlagValue<FD[K]>;
@@ -39,21 +41,21 @@ type FlagglyOptions<TFlags extends FlagSchema = FlagSchema> = {
39
41
  */
40
42
  url: string;
41
43
  /**
42
- * The public `API_KEY` you set while installing the worker.
44
+ * The public `user` JWT.
43
45
  */
44
46
  apiKey: string;
45
47
  /**
46
- * Optional app for this instance.
48
+ * App for this instance.
47
49
  * @default "default"
48
50
  */
49
51
  app?: string;
50
52
  /**
51
- * Optional enviornment for this instance
53
+ * Enviornment for this instance
52
54
  * @default "production"
53
55
  */
54
56
  env?: string;
55
57
  /**
56
- * By default, flags are evaluated when you create the FlagglyClient instance.
58
+ * By default, flags are evaluated when you initialize the FlagglyClient instance.
57
59
  * Pass this as true to manually initiate the flag evaluations.
58
60
  * Useful if you just care about feature flags for authenticated users only,
59
61
  * and want to evaluate flags when the users log in.
@@ -61,16 +63,29 @@ type FlagglyOptions<TFlags extends FlagSchema = FlagSchema> = {
61
63
  */
62
64
  lazy?: boolean;
63
65
  /**
64
- * Partial default values for the feature flags.
66
+ * Default values for the feature flags.
65
67
  */
66
68
  bootstrap?: Partial<FlagValues<TFlags>>;
67
69
  /**
68
70
  * Optional method to generate a backup identifer for the flags,
69
71
  * for anonymous users when you don't have a stable ID.
70
- * By default, it generate and store a value in local host per app/env.
72
+ * By default, it generates and store a value in local storage per app/env.
71
73
  * Use this method to pass in your own ID for anonymous users.
74
+ * This is not available server side, where local storage is not available.
75
+ * In that case, this method will default to generate a random ID everytime.
72
76
  */
73
77
  getBackupId?: () => string;
78
+ /**
79
+ * Pass in the `fetch` instance to be used when interacting with the API.
80
+ * Used when evaluating flags inside workers.
81
+ * Use a service binding to attach the flaggly worker to your worker and then
82
+ * When passing in the fetch from your service binding, make sure you bind the correct context.
83
+ * Otherwise, use a more explicit approach
84
+ * @example workerFetch: env.FLAGGLY_SERVICE.fetch.bind(env.FLAGGLY_SERVICE)
85
+ * @example workerFetch: (url, init) => env.FLAGGLY_SERVICE.fetch(url, init)
86
+ * @see https://developers.cloudflare.com/workers/observability/errors/#illegal-invocation-errors
87
+ */
88
+ workerFetch?: typeof fetch;
74
89
  };
75
90
  declare class Flaggly<TFlags extends FlagSchema = FlagSchema> {
76
91
  #private;
@@ -80,24 +95,73 @@ declare class Flaggly<TFlags extends FlagSchema = FlagSchema> {
80
95
  private env;
81
96
  user?: unknown;
82
97
  id?: string;
98
+ workerFetch: typeof fetch;
83
99
  getBackupId?: () => string;
84
- constructor({ url, apiKey, app, env, lazy, bootstrap, getBackupId, }: FlagglyOptions<TFlags>);
100
+ constructor({ url, apiKey, app, env, lazy, bootstrap, getBackupId, workerFetch, }: FlagglyOptions<TFlags>);
101
+ /**
102
+ * Method to identify a user and persist the ID and details for evaluations.
103
+ * Calling this method will evaluate the flags and reset the state.
104
+ * @param id Unique identifier for the user
105
+ * @param user User properties used for flag evaluations
106
+ * @returns
107
+ */
85
108
  identify(id: string, user: unknown): Promise<EvaluatedFlags<TFlags>>;
86
- getPageUrl(): string | null;
109
+ /**
110
+ * Evaluates all flags for a user and updates local state.
111
+ * @param input
112
+ * @returns
113
+ */
87
114
  fetchFlags(input?: FlagInput): Promise<EvaluatedFlags<TFlags>>;
115
+ /**
116
+ * Evaluates a single flag
117
+ * @param key
118
+ * @param input
119
+ * @returns
120
+ */
88
121
  fetchFlag<K extends AllKeys<EvaluatedFlags<TFlags>>>(key: K, input?: FlagInput): Promise<EvaluatedFlags<TFlags>[K]>;
122
+ /**
123
+ * Get all flags
124
+ * @returns
125
+ */
89
126
  getFlags(): EvaluatedFlags<TFlags>;
127
+ /**
128
+ * Get a single flag result
129
+ * @param key
130
+ * @returns
131
+ */
90
132
  getFlag<K extends keyof TFlags>(key: K): FlagValue<TFlags[K]>;
133
+ /**
134
+ * Get a single boolean flag results. Only boolean flag keys are valid.
135
+ * @param key
136
+ * @returns
137
+ */
91
138
  getBooleanFlag<K extends keyof TFlags>(key: K & (TFlags[K] extends {
92
139
  type: "boolean";
93
140
  } ? K : never)): FlagValue<TFlags[K]> | false;
141
+ /**
142
+ * Get a single boolean flag results. Only variant flag keys are valid.
143
+ * @param key
144
+ * @returns
145
+ */
94
146
  getVariantFlag<K extends keyof TFlags>(key: K & (TFlags[K] extends {
95
147
  type: "variant";
96
148
  } ? K : never)): FlagValue<TFlags[K]> | null;
149
+ /**
150
+ * Get a single boolean flag results. Only payload flag keys are valid.
151
+ * @param key
152
+ * @returns
153
+ */
97
154
  getPayloadFlag<K extends keyof TFlags>(key: K & (TFlags[K] extends {
98
155
  type: "payload";
99
156
  } ? K : never)): FlagValue<TFlags[K]> | null;
157
+ /**
158
+ * Subscribe to changes in the flags.
159
+ */
100
160
  onChange(cb: (flags: EvaluatedFlags<TFlags>) => void): () => void;
161
+ /**
162
+ * Local nanostores `map` for interacting with state
163
+ * @see https://github.com/nanostores/nanostores?tab=readme-ov-file#maps
164
+ */
101
165
  get store(): MapStore<EvaluatedFlags<TFlags>>;
102
166
  }
103
167
 
package/dist/index.d.ts CHANGED
@@ -22,7 +22,9 @@ type FlagInput = {
22
22
  };
23
23
  };
24
24
  type FlagValue<FR extends FlagConfig = FlagConfig> = FR extends {
25
- type: "variant" | "payload";
25
+ type: "variant";
26
+ } ? FR["result"] : FR extends {
27
+ type: "payload";
26
28
  } ? FR["result"] : boolean;
27
29
  type FlagValues<FD extends FlagSchema = FlagSchema> = {
28
30
  [K in keyof FD]: FlagValue<FD[K]>;
@@ -39,21 +41,21 @@ type FlagglyOptions<TFlags extends FlagSchema = FlagSchema> = {
39
41
  */
40
42
  url: string;
41
43
  /**
42
- * The public `API_KEY` you set while installing the worker.
44
+ * The public `user` JWT.
43
45
  */
44
46
  apiKey: string;
45
47
  /**
46
- * Optional app for this instance.
48
+ * App for this instance.
47
49
  * @default "default"
48
50
  */
49
51
  app?: string;
50
52
  /**
51
- * Optional enviornment for this instance
53
+ * Enviornment for this instance
52
54
  * @default "production"
53
55
  */
54
56
  env?: string;
55
57
  /**
56
- * By default, flags are evaluated when you create the FlagglyClient instance.
58
+ * By default, flags are evaluated when you initialize the FlagglyClient instance.
57
59
  * Pass this as true to manually initiate the flag evaluations.
58
60
  * Useful if you just care about feature flags for authenticated users only,
59
61
  * and want to evaluate flags when the users log in.
@@ -61,16 +63,29 @@ type FlagglyOptions<TFlags extends FlagSchema = FlagSchema> = {
61
63
  */
62
64
  lazy?: boolean;
63
65
  /**
64
- * Partial default values for the feature flags.
66
+ * Default values for the feature flags.
65
67
  */
66
68
  bootstrap?: Partial<FlagValues<TFlags>>;
67
69
  /**
68
70
  * Optional method to generate a backup identifer for the flags,
69
71
  * for anonymous users when you don't have a stable ID.
70
- * By default, it generate and store a value in local host per app/env.
72
+ * By default, it generates and store a value in local storage per app/env.
71
73
  * Use this method to pass in your own ID for anonymous users.
74
+ * This is not available server side, where local storage is not available.
75
+ * In that case, this method will default to generate a random ID everytime.
72
76
  */
73
77
  getBackupId?: () => string;
78
+ /**
79
+ * Pass in the `fetch` instance to be used when interacting with the API.
80
+ * Used when evaluating flags inside workers.
81
+ * Use a service binding to attach the flaggly worker to your worker and then
82
+ * When passing in the fetch from your service binding, make sure you bind the correct context.
83
+ * Otherwise, use a more explicit approach
84
+ * @example workerFetch: env.FLAGGLY_SERVICE.fetch.bind(env.FLAGGLY_SERVICE)
85
+ * @example workerFetch: (url, init) => env.FLAGGLY_SERVICE.fetch(url, init)
86
+ * @see https://developers.cloudflare.com/workers/observability/errors/#illegal-invocation-errors
87
+ */
88
+ workerFetch?: typeof fetch;
74
89
  };
75
90
  declare class Flaggly<TFlags extends FlagSchema = FlagSchema> {
76
91
  #private;
@@ -80,24 +95,73 @@ declare class Flaggly<TFlags extends FlagSchema = FlagSchema> {
80
95
  private env;
81
96
  user?: unknown;
82
97
  id?: string;
98
+ workerFetch: typeof fetch;
83
99
  getBackupId?: () => string;
84
- constructor({ url, apiKey, app, env, lazy, bootstrap, getBackupId, }: FlagglyOptions<TFlags>);
100
+ constructor({ url, apiKey, app, env, lazy, bootstrap, getBackupId, workerFetch, }: FlagglyOptions<TFlags>);
101
+ /**
102
+ * Method to identify a user and persist the ID and details for evaluations.
103
+ * Calling this method will evaluate the flags and reset the state.
104
+ * @param id Unique identifier for the user
105
+ * @param user User properties used for flag evaluations
106
+ * @returns
107
+ */
85
108
  identify(id: string, user: unknown): Promise<EvaluatedFlags<TFlags>>;
86
- getPageUrl(): string | null;
109
+ /**
110
+ * Evaluates all flags for a user and updates local state.
111
+ * @param input
112
+ * @returns
113
+ */
87
114
  fetchFlags(input?: FlagInput): Promise<EvaluatedFlags<TFlags>>;
115
+ /**
116
+ * Evaluates a single flag
117
+ * @param key
118
+ * @param input
119
+ * @returns
120
+ */
88
121
  fetchFlag<K extends AllKeys<EvaluatedFlags<TFlags>>>(key: K, input?: FlagInput): Promise<EvaluatedFlags<TFlags>[K]>;
122
+ /**
123
+ * Get all flags
124
+ * @returns
125
+ */
89
126
  getFlags(): EvaluatedFlags<TFlags>;
127
+ /**
128
+ * Get a single flag result
129
+ * @param key
130
+ * @returns
131
+ */
90
132
  getFlag<K extends keyof TFlags>(key: K): FlagValue<TFlags[K]>;
133
+ /**
134
+ * Get a single boolean flag results. Only boolean flag keys are valid.
135
+ * @param key
136
+ * @returns
137
+ */
91
138
  getBooleanFlag<K extends keyof TFlags>(key: K & (TFlags[K] extends {
92
139
  type: "boolean";
93
140
  } ? K : never)): FlagValue<TFlags[K]> | false;
141
+ /**
142
+ * Get a single boolean flag results. Only variant flag keys are valid.
143
+ * @param key
144
+ * @returns
145
+ */
94
146
  getVariantFlag<K extends keyof TFlags>(key: K & (TFlags[K] extends {
95
147
  type: "variant";
96
148
  } ? K : never)): FlagValue<TFlags[K]> | null;
149
+ /**
150
+ * Get a single boolean flag results. Only payload flag keys are valid.
151
+ * @param key
152
+ * @returns
153
+ */
97
154
  getPayloadFlag<K extends keyof TFlags>(key: K & (TFlags[K] extends {
98
155
  type: "payload";
99
156
  } ? K : never)): FlagValue<TFlags[K]> | null;
157
+ /**
158
+ * Subscribe to changes in the flags.
159
+ */
100
160
  onChange(cb: (flags: EvaluatedFlags<TFlags>) => void): () => void;
161
+ /**
162
+ * Local nanostores `map` for interacting with state
163
+ * @see https://github.com/nanostores/nanostores?tab=readme-ov-file#maps
164
+ */
101
165
  get store(): MapStore<EvaluatedFlags<TFlags>>;
102
166
  }
103
167
 
package/dist/index.js CHANGED
@@ -133,6 +133,7 @@ var Flaggly = class {
133
133
  env;
134
134
  user;
135
135
  id;
136
+ workerFetch;
136
137
  getBackupId;
137
138
  #flags = map();
138
139
  constructor({
@@ -142,13 +143,15 @@ var Flaggly = class {
142
143
  env = "production",
143
144
  lazy = false,
144
145
  bootstrap,
145
- getBackupId
146
+ getBackupId,
147
+ workerFetch
146
148
  }) {
147
149
  this.url = url;
148
150
  this.apiKey = apiKey;
149
151
  this.app = app;
150
152
  this.env = env;
151
153
  this.getBackupId = getBackupId;
154
+ this.workerFetch = workerFetch ?? fetch;
152
155
  const defValues = bootstrap ? Object.entries(bootstrap).reduce(
153
156
  (acc, [flagKey, flagValue]) => {
154
157
  const type = typeof flagValue === "boolean" ? "boolean" : typeof flagValue === "string" ? "variant" : "payload";
@@ -167,12 +170,19 @@ var Flaggly = class {
167
170
  this.fetchFlags();
168
171
  }
169
172
  }
173
+ /**
174
+ * Method to identify a user and persist the ID and details for evaluations.
175
+ * Calling this method will evaluate the flags and reset the state.
176
+ * @param id Unique identifier for the user
177
+ * @param user User properties used for flag evaluations
178
+ * @returns
179
+ */
170
180
  async identify(id, user) {
171
181
  this.id = id;
172
182
  this.user = user;
173
183
  return await this.fetchFlags({ id, user });
174
184
  }
175
- getPageUrl() {
185
+ #getPageUrl() {
176
186
  if ("window" in globalThis) {
177
187
  return globalThis.window.location.href;
178
188
  }
@@ -197,7 +207,7 @@ var Flaggly = class {
197
207
  async #request(path, options = {}) {
198
208
  const { method = "POST", body } = options;
199
209
  const url = new URL(path, this.url);
200
- const response = await fetch(url, {
210
+ const response = await this.workerFetch(url, {
201
211
  method,
202
212
  headers: {
203
213
  Authorization: `Bearer ${this.apiKey}`,
@@ -218,6 +228,11 @@ var Flaggly = class {
218
228
  }
219
229
  return response.json();
220
230
  }
231
+ /**
232
+ * Evaluates all flags for a user and updates local state.
233
+ * @param input
234
+ * @returns
235
+ */
221
236
  async fetchFlags(input) {
222
237
  const result = await this.#request("/api/eval", {
223
238
  method: "POST",
@@ -225,7 +240,7 @@ var Flaggly = class {
225
240
  id: input?.id ?? this.id ?? this.#getBackupId(),
226
241
  user: input?.user ?? this.user,
227
242
  page: {
228
- url: this.getPageUrl()
243
+ url: this.#getPageUrl()
229
244
  }
230
245
  }
231
246
  });
@@ -234,6 +249,12 @@ var Flaggly = class {
234
249
  }
235
250
  return result;
236
251
  }
252
+ /**
253
+ * Evaluates a single flag
254
+ * @param key
255
+ * @param input
256
+ * @returns
257
+ */
237
258
  async fetchFlag(key, input) {
238
259
  const result = await this.#request(
239
260
  `/api/eval/${String(key)}`,
@@ -243,7 +264,7 @@ var Flaggly = class {
243
264
  id: input?.id ?? this.id ?? this.#getBackupId(),
244
265
  user: input?.user ?? this.user,
245
266
  page: {
246
- url: this.getPageUrl()
267
+ url: this.#getPageUrl()
247
268
  }
248
269
  }
249
270
  }
@@ -253,28 +274,59 @@ var Flaggly = class {
253
274
  }
254
275
  return result;
255
276
  }
277
+ /**
278
+ * Get all flags
279
+ * @returns
280
+ */
256
281
  getFlags() {
257
282
  return this.#flags.get();
258
283
  }
284
+ /**
285
+ * Get a single flag result
286
+ * @param key
287
+ * @returns
288
+ */
259
289
  getFlag(key) {
260
290
  const flags = this.#flags.get();
261
291
  return flags?.[key]?.result;
262
292
  }
293
+ /**
294
+ * Get a single boolean flag results. Only boolean flag keys are valid.
295
+ * @param key
296
+ * @returns
297
+ */
263
298
  getBooleanFlag(key) {
264
299
  const flags = this.#flags.get();
265
300
  return flags[key]?.result ?? false;
266
301
  }
302
+ /**
303
+ * Get a single boolean flag results. Only variant flag keys are valid.
304
+ * @param key
305
+ * @returns
306
+ */
267
307
  getVariantFlag(key) {
268
308
  const flags = this.#flags.get();
269
309
  return flags[key]?.result ?? null;
270
310
  }
311
+ /**
312
+ * Get a single boolean flag results. Only payload flag keys are valid.
313
+ * @param key
314
+ * @returns
315
+ */
271
316
  getPayloadFlag(key) {
272
317
  const flags = this.#flags.get();
273
318
  return flags[key]?.result ?? null;
274
319
  }
320
+ /**
321
+ * Subscribe to changes in the flags.
322
+ */
275
323
  onChange(cb) {
276
324
  return this.#flags.subscribe(cb);
277
325
  }
326
+ /**
327
+ * Local nanostores `map` for interacting with state
328
+ * @see https://github.com/nanostores/nanostores?tab=readme-ov-file#maps
329
+ */
278
330
  get store() {
279
331
  return this.#flags;
280
332
  }
package/dist/index.mjs CHANGED
@@ -107,6 +107,7 @@ var Flaggly = class {
107
107
  env;
108
108
  user;
109
109
  id;
110
+ workerFetch;
110
111
  getBackupId;
111
112
  #flags = map();
112
113
  constructor({
@@ -116,13 +117,15 @@ var Flaggly = class {
116
117
  env = "production",
117
118
  lazy = false,
118
119
  bootstrap,
119
- getBackupId
120
+ getBackupId,
121
+ workerFetch
120
122
  }) {
121
123
  this.url = url;
122
124
  this.apiKey = apiKey;
123
125
  this.app = app;
124
126
  this.env = env;
125
127
  this.getBackupId = getBackupId;
128
+ this.workerFetch = workerFetch ?? fetch;
126
129
  const defValues = bootstrap ? Object.entries(bootstrap).reduce(
127
130
  (acc, [flagKey, flagValue]) => {
128
131
  const type = typeof flagValue === "boolean" ? "boolean" : typeof flagValue === "string" ? "variant" : "payload";
@@ -141,12 +144,19 @@ var Flaggly = class {
141
144
  this.fetchFlags();
142
145
  }
143
146
  }
147
+ /**
148
+ * Method to identify a user and persist the ID and details for evaluations.
149
+ * Calling this method will evaluate the flags and reset the state.
150
+ * @param id Unique identifier for the user
151
+ * @param user User properties used for flag evaluations
152
+ * @returns
153
+ */
144
154
  async identify(id, user) {
145
155
  this.id = id;
146
156
  this.user = user;
147
157
  return await this.fetchFlags({ id, user });
148
158
  }
149
- getPageUrl() {
159
+ #getPageUrl() {
150
160
  if ("window" in globalThis) {
151
161
  return globalThis.window.location.href;
152
162
  }
@@ -171,7 +181,7 @@ var Flaggly = class {
171
181
  async #request(path, options = {}) {
172
182
  const { method = "POST", body } = options;
173
183
  const url = new URL(path, this.url);
174
- const response = await fetch(url, {
184
+ const response = await this.workerFetch(url, {
175
185
  method,
176
186
  headers: {
177
187
  Authorization: `Bearer ${this.apiKey}`,
@@ -192,6 +202,11 @@ var Flaggly = class {
192
202
  }
193
203
  return response.json();
194
204
  }
205
+ /**
206
+ * Evaluates all flags for a user and updates local state.
207
+ * @param input
208
+ * @returns
209
+ */
195
210
  async fetchFlags(input) {
196
211
  const result = await this.#request("/api/eval", {
197
212
  method: "POST",
@@ -199,7 +214,7 @@ var Flaggly = class {
199
214
  id: input?.id ?? this.id ?? this.#getBackupId(),
200
215
  user: input?.user ?? this.user,
201
216
  page: {
202
- url: this.getPageUrl()
217
+ url: this.#getPageUrl()
203
218
  }
204
219
  }
205
220
  });
@@ -208,6 +223,12 @@ var Flaggly = class {
208
223
  }
209
224
  return result;
210
225
  }
226
+ /**
227
+ * Evaluates a single flag
228
+ * @param key
229
+ * @param input
230
+ * @returns
231
+ */
211
232
  async fetchFlag(key, input) {
212
233
  const result = await this.#request(
213
234
  `/api/eval/${String(key)}`,
@@ -217,7 +238,7 @@ var Flaggly = class {
217
238
  id: input?.id ?? this.id ?? this.#getBackupId(),
218
239
  user: input?.user ?? this.user,
219
240
  page: {
220
- url: this.getPageUrl()
241
+ url: this.#getPageUrl()
221
242
  }
222
243
  }
223
244
  }
@@ -227,28 +248,59 @@ var Flaggly = class {
227
248
  }
228
249
  return result;
229
250
  }
251
+ /**
252
+ * Get all flags
253
+ * @returns
254
+ */
230
255
  getFlags() {
231
256
  return this.#flags.get();
232
257
  }
258
+ /**
259
+ * Get a single flag result
260
+ * @param key
261
+ * @returns
262
+ */
233
263
  getFlag(key) {
234
264
  const flags = this.#flags.get();
235
265
  return flags?.[key]?.result;
236
266
  }
267
+ /**
268
+ * Get a single boolean flag results. Only boolean flag keys are valid.
269
+ * @param key
270
+ * @returns
271
+ */
237
272
  getBooleanFlag(key) {
238
273
  const flags = this.#flags.get();
239
274
  return flags[key]?.result ?? false;
240
275
  }
276
+ /**
277
+ * Get a single boolean flag results. Only variant flag keys are valid.
278
+ * @param key
279
+ * @returns
280
+ */
241
281
  getVariantFlag(key) {
242
282
  const flags = this.#flags.get();
243
283
  return flags[key]?.result ?? null;
244
284
  }
285
+ /**
286
+ * Get a single boolean flag results. Only payload flag keys are valid.
287
+ * @param key
288
+ * @returns
289
+ */
245
290
  getPayloadFlag(key) {
246
291
  const flags = this.#flags.get();
247
292
  return flags[key]?.result ?? null;
248
293
  }
294
+ /**
295
+ * Subscribe to changes in the flags.
296
+ */
249
297
  onChange(cb) {
250
298
  return this.#flags.subscribe(cb);
251
299
  }
300
+ /**
301
+ * Local nanostores `map` for interacting with state
302
+ * @see https://github.com/nanostores/nanostores?tab=readme-ov-file#maps
303
+ */
252
304
  get store() {
253
305
  return this.#flags;
254
306
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flaggly/sdk",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Client SDK for Flaggly",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",