@flaggly/sdk 0.0.5 → 0.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.d.mts CHANGED
@@ -35,6 +35,14 @@ type EvaluatedFlags<FD extends FlagSchema = FlagSchema> = {
35
35
  result: FlagValue<FD[K]>;
36
36
  };
37
37
  };
38
+ /**
39
+ * Defines a custom storage interface for handling backup identifiers.
40
+ * This allows for persistence in environments where localStorage is not available (e.g., server-side).
41
+ */
42
+ type CustomStorage = {
43
+ getItem(key: string): string | null;
44
+ setItem(key: string, value: string): void;
45
+ };
38
46
  type FlagglyOptions<TFlags extends FlagSchema = FlagSchema> = {
39
47
  /**
40
48
  * The base URL of your Flaggly worker.
@@ -86,6 +94,21 @@ type FlagglyOptions<TFlags extends FlagSchema = FlagSchema> = {
86
94
  * @see https://developers.cloudflare.com/workers/observability/errors/#illegal-invocation-errors
87
95
  */
88
96
  workerFetch?: typeof fetch;
97
+ /**
98
+ * Optional custom storage implementation for backup IDs, for anonymous users.
99
+ * By default, it will use localStorage in browser environments.
100
+ * If localStorage is not available, a new ID will be generated on each request.
101
+ */
102
+ customStorage?: CustomStorage;
103
+ /**
104
+ * Optional method to provide the current route/URL in non-browser environments.
105
+ * By default, it uses `window.location.href`.
106
+ */
107
+ getCurrentRoute?: () => string | null;
108
+ /**
109
+ * Optional method to stub for `crypo.randomUUID` method in non-browser environments.
110
+ */
111
+ getRandomId?: () => string;
89
112
  };
90
113
  declare class Flaggly<TFlags extends FlagSchema = FlagSchema> {
91
114
  #private;
@@ -93,11 +116,14 @@ declare class Flaggly<TFlags extends FlagSchema = FlagSchema> {
93
116
  private apiKey;
94
117
  private app;
95
118
  private env;
119
+ private storage;
120
+ private getCurrentRoute?;
96
121
  user?: unknown;
97
122
  id?: string;
98
123
  workerFetch: typeof fetch;
99
124
  getBackupId?: () => string;
100
- constructor({ url, apiKey, app, env, lazy, bootstrap, getBackupId, workerFetch, }: FlagglyOptions<TFlags>);
125
+ getRandomId: () => string;
126
+ constructor({ url, apiKey, app, env, lazy, bootstrap, getBackupId, workerFetch, customStorage, getCurrentRoute, getRandomId, }: FlagglyOptions<TFlags>);
101
127
  /**
102
128
  * Method to identify a user and persist the ID and details for evaluations.
103
129
  * Calling this method will evaluate the flags and reset the state.
@@ -165,4 +191,4 @@ declare class Flaggly<TFlags extends FlagSchema = FlagSchema> {
165
191
  get store(): MapStore<EvaluatedFlags<TFlags>>;
166
192
  }
167
193
 
168
- export { type EvaluatedFlags, type FlagConfig, type FlagInput, type FlagSchema, type FlagValue, type FlagValues, Flaggly, type FlagglyOptions };
194
+ export { type CustomStorage, type EvaluatedFlags, type FlagConfig, type FlagInput, type FlagSchema, type FlagValue, type FlagValues, Flaggly, type FlagglyOptions };
package/dist/index.d.ts CHANGED
@@ -35,6 +35,14 @@ type EvaluatedFlags<FD extends FlagSchema = FlagSchema> = {
35
35
  result: FlagValue<FD[K]>;
36
36
  };
37
37
  };
38
+ /**
39
+ * Defines a custom storage interface for handling backup identifiers.
40
+ * This allows for persistence in environments where localStorage is not available (e.g., server-side).
41
+ */
42
+ type CustomStorage = {
43
+ getItem(key: string): string | null;
44
+ setItem(key: string, value: string): void;
45
+ };
38
46
  type FlagglyOptions<TFlags extends FlagSchema = FlagSchema> = {
39
47
  /**
40
48
  * The base URL of your Flaggly worker.
@@ -86,6 +94,21 @@ type FlagglyOptions<TFlags extends FlagSchema = FlagSchema> = {
86
94
  * @see https://developers.cloudflare.com/workers/observability/errors/#illegal-invocation-errors
87
95
  */
88
96
  workerFetch?: typeof fetch;
97
+ /**
98
+ * Optional custom storage implementation for backup IDs, for anonymous users.
99
+ * By default, it will use localStorage in browser environments.
100
+ * If localStorage is not available, a new ID will be generated on each request.
101
+ */
102
+ customStorage?: CustomStorage;
103
+ /**
104
+ * Optional method to provide the current route/URL in non-browser environments.
105
+ * By default, it uses `window.location.href`.
106
+ */
107
+ getCurrentRoute?: () => string | null;
108
+ /**
109
+ * Optional method to stub for `crypo.randomUUID` method in non-browser environments.
110
+ */
111
+ getRandomId?: () => string;
89
112
  };
90
113
  declare class Flaggly<TFlags extends FlagSchema = FlagSchema> {
91
114
  #private;
@@ -93,11 +116,14 @@ declare class Flaggly<TFlags extends FlagSchema = FlagSchema> {
93
116
  private apiKey;
94
117
  private app;
95
118
  private env;
119
+ private storage;
120
+ private getCurrentRoute?;
96
121
  user?: unknown;
97
122
  id?: string;
98
123
  workerFetch: typeof fetch;
99
124
  getBackupId?: () => string;
100
- constructor({ url, apiKey, app, env, lazy, bootstrap, getBackupId, workerFetch, }: FlagglyOptions<TFlags>);
125
+ getRandomId: () => string;
126
+ constructor({ url, apiKey, app, env, lazy, bootstrap, getBackupId, workerFetch, customStorage, getCurrentRoute, getRandomId, }: FlagglyOptions<TFlags>);
101
127
  /**
102
128
  * Method to identify a user and persist the ID and details for evaluations.
103
129
  * Calling this method will evaluate the flags and reset the state.
@@ -165,4 +191,4 @@ declare class Flaggly<TFlags extends FlagSchema = FlagSchema> {
165
191
  get store(): MapStore<EvaluatedFlags<TFlags>>;
166
192
  }
167
193
 
168
- export { type EvaluatedFlags, type FlagConfig, type FlagInput, type FlagSchema, type FlagValue, type FlagValues, Flaggly, type FlagglyOptions };
194
+ export { type CustomStorage, type EvaluatedFlags, type FlagConfig, type FlagInput, type FlagSchema, type FlagValue, type FlagValues, Flaggly, type FlagglyOptions };
package/dist/index.js CHANGED
@@ -131,10 +131,13 @@ var Flaggly = class {
131
131
  apiKey;
132
132
  app;
133
133
  env;
134
+ storage;
135
+ getCurrentRoute;
134
136
  user;
135
137
  id;
136
138
  workerFetch;
137
139
  getBackupId;
140
+ getRandomId;
138
141
  #flags = map();
139
142
  constructor({
140
143
  url,
@@ -144,14 +147,32 @@ var Flaggly = class {
144
147
  lazy = false,
145
148
  bootstrap,
146
149
  getBackupId,
147
- workerFetch
150
+ workerFetch,
151
+ customStorage,
152
+ getCurrentRoute,
153
+ getRandomId
148
154
  }) {
149
155
  this.url = url;
150
156
  this.apiKey = apiKey;
151
157
  this.app = app;
152
158
  this.env = env;
153
159
  this.getBackupId = getBackupId;
160
+ this.getCurrentRoute = getCurrentRoute;
154
161
  this.workerFetch = workerFetch ?? fetch;
162
+ this.getRandomId = getRandomId ?? globalThis.crypto.randomUUID;
163
+ this.storage = customStorage ?? {
164
+ getItem: (key) => {
165
+ if ("localStorage" in globalThis) {
166
+ return globalThis.localStorage.getItem(key);
167
+ }
168
+ return null;
169
+ },
170
+ setItem: (key, value) => {
171
+ if ("localStorage" in globalThis) {
172
+ globalThis.localStorage.setItem(key, value);
173
+ }
174
+ }
175
+ };
155
176
  const defValues = bootstrap ? Object.entries(bootstrap).reduce(
156
177
  (acc, [flagKey, flagValue]) => {
157
178
  const type = typeof flagValue === "boolean" ? "boolean" : typeof flagValue === "string" ? "variant" : "payload";
@@ -183,6 +204,9 @@ var Flaggly = class {
183
204
  return await this.fetchFlags({ id, user });
184
205
  }
185
206
  #getPageUrl() {
207
+ if (this.getCurrentRoute) {
208
+ return this.getCurrentRoute();
209
+ }
186
210
  if ("window" in globalThis) {
187
211
  return globalThis.window.location.href;
188
212
  }
@@ -192,17 +216,14 @@ var Flaggly = class {
192
216
  if (this.getBackupId) {
193
217
  return this.getBackupId();
194
218
  }
195
- if ("window" in globalThis) {
196
- const key = `__flaggly_id.${this.app}.${this.env}`;
197
- const storage = globalThis.window.localStorage.getItem(key);
198
- if (!storage) {
199
- const id = globalThis.crypto.randomUUID();
200
- globalThis.window.localStorage.setItem(key, id);
201
- return id;
202
- }
203
- return storage;
219
+ const key = `__flaggly_id.${this.app}.${this.env}`;
220
+ const storedId = this.storage.getItem(key);
221
+ if (!storedId) {
222
+ const newId = this.getRandomId();
223
+ this.storage.setItem(key, newId);
224
+ return newId;
204
225
  }
205
- return globalThis.crypto.randomUUID();
226
+ return storedId;
206
227
  }
207
228
  async #request(path, options = {}) {
208
229
  const { method = "POST", body } = options;
package/dist/index.mjs CHANGED
@@ -105,10 +105,13 @@ var Flaggly = class {
105
105
  apiKey;
106
106
  app;
107
107
  env;
108
+ storage;
109
+ getCurrentRoute;
108
110
  user;
109
111
  id;
110
112
  workerFetch;
111
113
  getBackupId;
114
+ getRandomId;
112
115
  #flags = map();
113
116
  constructor({
114
117
  url,
@@ -118,14 +121,32 @@ var Flaggly = class {
118
121
  lazy = false,
119
122
  bootstrap,
120
123
  getBackupId,
121
- workerFetch
124
+ workerFetch,
125
+ customStorage,
126
+ getCurrentRoute,
127
+ getRandomId
122
128
  }) {
123
129
  this.url = url;
124
130
  this.apiKey = apiKey;
125
131
  this.app = app;
126
132
  this.env = env;
127
133
  this.getBackupId = getBackupId;
134
+ this.getCurrentRoute = getCurrentRoute;
128
135
  this.workerFetch = workerFetch ?? fetch;
136
+ this.getRandomId = getRandomId ?? globalThis.crypto.randomUUID;
137
+ this.storage = customStorage ?? {
138
+ getItem: (key) => {
139
+ if ("localStorage" in globalThis) {
140
+ return globalThis.localStorage.getItem(key);
141
+ }
142
+ return null;
143
+ },
144
+ setItem: (key, value) => {
145
+ if ("localStorage" in globalThis) {
146
+ globalThis.localStorage.setItem(key, value);
147
+ }
148
+ }
149
+ };
129
150
  const defValues = bootstrap ? Object.entries(bootstrap).reduce(
130
151
  (acc, [flagKey, flagValue]) => {
131
152
  const type = typeof flagValue === "boolean" ? "boolean" : typeof flagValue === "string" ? "variant" : "payload";
@@ -157,6 +178,9 @@ var Flaggly = class {
157
178
  return await this.fetchFlags({ id, user });
158
179
  }
159
180
  #getPageUrl() {
181
+ if (this.getCurrentRoute) {
182
+ return this.getCurrentRoute();
183
+ }
160
184
  if ("window" in globalThis) {
161
185
  return globalThis.window.location.href;
162
186
  }
@@ -166,17 +190,14 @@ var Flaggly = class {
166
190
  if (this.getBackupId) {
167
191
  return this.getBackupId();
168
192
  }
169
- if ("window" in globalThis) {
170
- const key = `__flaggly_id.${this.app}.${this.env}`;
171
- const storage = globalThis.window.localStorage.getItem(key);
172
- if (!storage) {
173
- const id = globalThis.crypto.randomUUID();
174
- globalThis.window.localStorage.setItem(key, id);
175
- return id;
176
- }
177
- return storage;
193
+ const key = `__flaggly_id.${this.app}.${this.env}`;
194
+ const storedId = this.storage.getItem(key);
195
+ if (!storedId) {
196
+ const newId = this.getRandomId();
197
+ this.storage.setItem(key, newId);
198
+ return newId;
178
199
  }
179
- return globalThis.crypto.randomUUID();
200
+ return storedId;
180
201
  }
181
202
  async #request(path, options = {}) {
182
203
  const { method = "POST", body } = options;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flaggly/sdk",
3
- "version": "0.0.5",
3
+ "version": "0.1.0",
4
4
  "description": "Client SDK for Flaggly",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",