@squide/firefly 16.0.3 → 16.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/AppRouter.js +1 -1
  3. package/dist/AppRouter.js.map +1 -1
  4. package/dist/AppRouterContext.js +2 -2
  5. package/dist/AppRouterContext.js.map +1 -1
  6. package/dist/AppRouterReducer.d.ts +3 -1
  7. package/dist/AppRouterReducer.js +35 -4
  8. package/dist/AppRouterReducer.js.map +1 -1
  9. package/dist/AppRouterStore.js +8 -0
  10. package/dist/AppRouterStore.js.map +1 -1
  11. package/dist/FireflyRuntime.d.ts +32 -16
  12. package/dist/FireflyRuntime.js +61 -19
  13. package/dist/FireflyRuntime.js.map +1 -1
  14. package/dist/honeycomb/activeSpan.js +2 -2
  15. package/dist/honeycomb/activeSpan.js.map +1 -1
  16. package/dist/index.d.ts +2 -1
  17. package/dist/index.js +5 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/initializeFirefly.d.ts +4 -2
  20. package/dist/initializeFirefly.js +15 -4
  21. package/dist/initializeFirefly.js.map +1 -1
  22. package/dist/useCanUpdateDeferredRegistrations.js +2 -2
  23. package/dist/useCanUpdateDeferredRegistrations.js.map +1 -1
  24. package/dist/useDeferredRegistrations.d.ts +1 -1
  25. package/dist/useDeferredRegistrations.js +6 -5
  26. package/dist/useDeferredRegistrations.js.map +1 -1
  27. package/dist/useRegisterDeferredRegistrations.d.ts +1 -2
  28. package/dist/useRegisterDeferredRegistrations.js +9 -2
  29. package/dist/useRegisterDeferredRegistrations.js.map +1 -1
  30. package/dist/useStrictRegistrationMode.js +10 -8
  31. package/dist/useStrictRegistrationMode.js.map +1 -1
  32. package/dist/useUpdateDeferredRegistrations.d.ts +1 -2
  33. package/dist/useUpdateDeferredRegistrations.js +7 -1
  34. package/dist/useUpdateDeferredRegistrations.js.map +1 -1
  35. package/package.json +19 -17
  36. package/src/AppRouter.tsx +1 -1
  37. package/src/AppRouterContext.ts +1 -1
  38. package/src/AppRouterReducer.ts +38 -3
  39. package/src/AppRouterStore.ts +8 -0
  40. package/src/FireflyRuntime.tsx +85 -30
  41. package/src/honeycomb/activeSpan.ts +1 -1
  42. package/src/index.ts +18 -2
  43. package/src/initializeFirefly.ts +38 -27
  44. package/src/useCanUpdateDeferredRegistrations.ts +3 -1
  45. package/src/useDeferredRegistrations.ts +15 -6
  46. package/src/useRegisterDeferredRegistrations.ts +5 -3
  47. package/src/useStrictRegistrationMode.ts +11 -11
  48. package/src/useUpdateDeferredRegistrations.ts +4 -3
@@ -1,4 +1,4 @@
1
- import { isNil } from "@squide/core";
1
+ import { isNil } from "@squide/core/internal";
2
2
  import { createContext, useContext } from "react";
3
3
  import type { AppRouterDispatch, AppRouterState } from "./AppRouterReducer.ts";
4
4
 
@@ -1,4 +1,5 @@
1
1
  import { useEventBus, useLogger, useRuntime } from "@squide/core";
2
+ import type { FeatureFlagSetSnapshotChangedListener } from "@squide/launch-darkly";
2
3
  import { useCallback, useEffect, useMemo, useReducer, type Dispatch } from "react";
3
4
  import type { FireflyRuntime } from "./FireflyRuntime.tsx";
4
5
  import { useAppRouterStore } from "./useAppRouterStore.ts";
@@ -21,6 +22,7 @@ export interface AppRouterState extends AppRouterWaitState {
21
22
  isProtectedDataReady: boolean;
22
23
  publicDataUpdatedAt?: number;
23
24
  protectedDataUpdatedAt?: number;
25
+ featureFlagsUpdatedAt?: number;
24
26
  deferredRegistrationsUpdatedAt?: number;
25
27
  activeRouteVisibility: ActiveRouteVisiblity;
26
28
  isUnauthorized: boolean;
@@ -34,6 +36,7 @@ export type AppRouterActionType =
34
36
  | "protected-data-ready"
35
37
  | "public-data-updated"
36
38
  | "protected-data-updated"
39
+ | "feature-flags-updated"
37
40
  | "deferred-registrations-updated"
38
41
  | "active-route-is-public"
39
42
  | "active-route-is-protected"
@@ -124,6 +127,14 @@ function reducer(state: AppRouterState, action: AppRouterAction) {
124
127
 
125
128
  break;
126
129
  }
130
+ case "feature-flags-updated": {
131
+ newState = {
132
+ ...newState,
133
+ featureFlagsUpdatedAt: Date.now()
134
+ };
135
+
136
+ break;
137
+ }
127
138
  case "deferred-registrations-updated": {
128
139
  newState = {
129
140
  ...newState,
@@ -225,16 +236,39 @@ export function useMswStatusDispatcher(runtime: FireflyRuntime, isMswReadyValue:
225
236
  useEffect(() => {
226
237
  if (runtime.isMswEnabled) {
227
238
  if (!isMswReadyValue) {
228
- runtime.getMswState().addMswReadyListener(dispatchMswReady);
239
+ runtime.mswState.addMswReadyListener(dispatchMswReady);
229
240
  }
230
241
 
231
242
  return () => {
232
- runtime.getMswState().removeMswReadyListener(dispatchMswReady);
243
+ runtime.mswState.removeMswReadyListener(dispatchMswReady);
233
244
  };
234
245
  }
235
246
  }, [runtime, isMswReadyValue, dispatchMswReady]);
236
247
  }
237
248
 
249
+ export function useFeatureFlagsUpdatedDispatcher(runtime: FireflyRuntime, dispatch: AppRouterDispatch) {
250
+ const logger = useLogger();
251
+
252
+ const dispatchFeatureFlagsUpdated = useCallback((changes => {
253
+ dispatch({ type: "feature-flags-updated" });
254
+
255
+ logger
256
+ .withText("[squide] Feature flags has been updated to:")
257
+ .withObject(changes)
258
+ .debug();
259
+ }) satisfies FeatureFlagSetSnapshotChangedListener, [dispatch, logger]);
260
+
261
+ useEffect(() => {
262
+ if (runtime.isLaunchDarklyEnabled) {
263
+ runtime.featureFlagSetSnapshot.addSnapshotChangedListener(dispatchFeatureFlagsUpdated);
264
+
265
+ return () => {
266
+ runtime.featureFlagSetSnapshot.removeSnapshotChangedListener(dispatchFeatureFlagsUpdated);
267
+ };
268
+ }
269
+ }, [runtime]);
270
+ }
271
+
238
272
  function useBootstrappingCompletedDispatcher(waitState: AppRouterWaitState, state: AppRouterState) {
239
273
  const eventBus = useEventBus();
240
274
 
@@ -296,7 +330,7 @@ export function useAppRouterReducer(waitForPublicData: boolean, waitForProtected
296
330
  const isMswEnabled = runtime.isMswEnabled;
297
331
  const areModulesInitiallyRegistered = runtime.moduleManager.getAreModulesRegistered();
298
332
  const areModulesInitiallyReady = runtime.moduleManager.getAreModulesReady();
299
- const isMswInitiallyReady = runtime.isMswEnabled ? runtime.getMswState().isReady : false;
333
+ const isMswInitiallyReady = runtime.isMswEnabled ? runtime.mswState.isReady : false;
300
334
 
301
335
  const waitState = useMemo(() => ({
302
336
  waitForMsw: isMswEnabled,
@@ -366,6 +400,7 @@ export function useAppRouterReducer(waitForPublicData: boolean, waitForProtected
366
400
 
367
401
  useModuleRegistrationStatusDispatcher(runtime, areModulesRegisteredValue, areModulesReadyValue, dispatch);
368
402
  useMswStatusDispatcher(runtime, isMswReadyValue, dispatch);
403
+ useFeatureFlagsUpdatedDispatcher(runtime, dispatch);
369
404
  useBootstrappingCompletedDispatcher(waitState, state);
370
405
 
371
406
  return [state, dispatch];
@@ -115,6 +115,14 @@ export class AppRouterStore {
115
115
 
116
116
  break;
117
117
  }
118
+ case "feature-flags-updated": {
119
+ newState = {
120
+ ...newState,
121
+ featureFlagsUpdatedAt: Date.now()
122
+ };
123
+
124
+ break;
125
+ }
118
126
  case "deferred-registrations-updated": {
119
127
  newState = {
120
128
  ...newState,
@@ -1,9 +1,11 @@
1
1
  import type { RegisterRouteOptions, RuntimeMethodOptions, RuntimeOptions } from "@squide/core";
2
- import { EnvironmentVariableKey, EnvironmentVariables, EnvironmentVariableValue, getEnvironmentVariablesPlugin } from "@squide/env-vars";
2
+ import { EnvironmentVariableKey, EnvironmentVariables, getEnvironmentVariablesPlugin } from "@squide/env-vars";
3
+ import { FeatureFlagKey, FeatureFlags, FeatureFlagSetSnapshot, getLaunchDarklyPlugin, LaunchDarklyPluginName } from "@squide/launch-darkly";
3
4
  import { getMswPlugin, MswPluginName, MswState } from "@squide/msw";
4
5
  import { type IReactRouterRuntime, ReactRouterRuntime, ReactRouterRuntimeScope, type Route } from "@squide/react-router";
5
6
  import type { HoneycombInstrumentationPartialClient } from "@workleap-telemetry/core";
6
7
  import type { Logger } from "@workleap/logging";
8
+ import { LDClient } from "launchdarkly-js-client-sdk";
7
9
  import type { RequestHandler } from "msw";
8
10
  import { type AppRouterStore, createAppRouterStore } from "./AppRouterStore.ts";
9
11
 
@@ -14,22 +16,29 @@ export interface FireflyRuntimeOptions<TRuntime extends FireflyRuntime = Firefly
14
16
  export interface RegisterRequestHandlersOptions extends RuntimeMethodOptions {}
15
17
 
16
18
  export interface IFireflyRuntime extends IReactRouterRuntime {
17
- getMswState(): MswState;
19
+ get isMswEnabled(): boolean;
20
+ get mswState(): MswState;
18
21
  registerRequestHandlers: (handlers: RequestHandler[]) => void;
19
22
  get requestHandlers(): RequestHandler[];
20
- get isMswEnabled(): boolean;
21
- registerEnvironmentVariable(key: EnvironmentVariableKey, value: EnvironmentVariableValue): void;
23
+ registerEnvironmentVariable<T extends EnvironmentVariableKey>(key: T, value: EnvironmentVariables[T]): void;
22
24
  registerEnvironmentVariables(variables: Partial<EnvironmentVariables>): void;
23
- getEnvironmentVariable(key: EnvironmentVariableKey): EnvironmentVariableValue;
24
- getEnvironmentVariables(): EnvironmentVariables;
25
+ getEnvironmentVariable<T extends EnvironmentVariableKey>(key: T): EnvironmentVariables[T];
26
+ get environmentVariables(): EnvironmentVariables;
25
27
  get appRouterStore(): AppRouterStore;
26
28
  get honeycombInstrumentationClient(): HoneycombInstrumentationPartialClient | undefined;
29
+ get isLaunchDarklyEnabled(): boolean;
30
+ get launchDarklyClient(): LDClient;
31
+ get featureFlags(): FeatureFlags;
32
+ getFeatureFlag(key: string, defaultValue?: unknown): unknown;
33
+ get featureFlagSetSnapshot(): FeatureFlagSetSnapshot;
27
34
  }
28
35
 
29
36
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
37
  export class FireflyRuntime<TRuntime extends FireflyRuntime = any> extends ReactRouterRuntime<TRuntime> implements IFireflyRuntime {
31
- protected _appRouterStore: AppRouterStore;
32
- protected _honeycombInstrumentationClient: HoneycombInstrumentationPartialClient | undefined;
38
+ readonly #appRouterStore: AppRouterStore;
39
+ readonly #honeycombInstrumentationClient: HoneycombInstrumentationPartialClient | undefined;
40
+ readonly #isMswEnabled: boolean;
41
+ readonly #isLaunchDarklyEnabled: boolean;
33
42
 
34
43
  constructor(options: FireflyRuntimeOptions = {}) {
35
44
  const {
@@ -38,8 +47,10 @@ export class FireflyRuntime<TRuntime extends FireflyRuntime = any> extends React
38
47
 
39
48
  super(options);
40
49
 
41
- this._appRouterStore = createAppRouterStore(this._logger);
42
- this._honeycombInstrumentationClient = honeycombInstrumentationClient;
50
+ this.#appRouterStore = createAppRouterStore(this._logger);
51
+ this.#honeycombInstrumentationClient = honeycombInstrumentationClient;
52
+ this.#isMswEnabled = this._plugins.some(x => x.name === MswPluginName);
53
+ this.#isLaunchDarklyEnabled = this._plugins.some(x => x.name === LaunchDarklyPluginName);
43
54
  }
44
55
 
45
56
  registerRoute(route: Route, options: RegisterRouteOptions = {}) {
@@ -50,7 +61,11 @@ export class FireflyRuntime<TRuntime extends FireflyRuntime = any> extends React
50
61
  super.registerRoute(route, options);
51
62
  }
52
63
 
53
- getMswState() {
64
+ get isMswEnabled() {
65
+ return this.#isMswEnabled;
66
+ }
67
+
68
+ get mswState() {
54
69
  const plugin = getMswPlugin(this);
55
70
 
56
71
  return plugin.mswState;
@@ -76,23 +91,19 @@ export class FireflyRuntime<TRuntime extends FireflyRuntime = any> extends React
76
91
  return plugin.requestHandlers;
77
92
  }
78
93
 
79
- get isMswEnabled() {
80
- return this._plugins.some(x => x.name === MswPluginName);
81
- }
82
-
83
94
  getEnvironmentVariable(key: EnvironmentVariableKey) {
84
95
  const plugin = getEnvironmentVariablesPlugin(this);
85
96
 
86
97
  return plugin.getVariable(key);
87
98
  }
88
99
 
89
- getEnvironmentVariables() {
100
+ get environmentVariables() {
90
101
  const plugin = getEnvironmentVariablesPlugin(this);
91
102
 
92
103
  return plugin.getVariables();
93
104
  }
94
105
 
95
- registerEnvironmentVariable(key: EnvironmentVariableKey, value: EnvironmentVariableValue) {
106
+ registerEnvironmentVariable<T extends EnvironmentVariableKey>(key: T, value: EnvironmentVariables[T]) {
96
107
  const plugin = getEnvironmentVariablesPlugin(this);
97
108
 
98
109
  return plugin.registerVariable(key, value);
@@ -105,11 +116,31 @@ export class FireflyRuntime<TRuntime extends FireflyRuntime = any> extends React
105
116
  }
106
117
 
107
118
  get appRouterStore() {
108
- return this._appRouterStore;
119
+ return this.#appRouterStore;
109
120
  }
110
121
 
111
122
  get honeycombInstrumentationClient() {
112
- return this._honeycombInstrumentationClient;
123
+ return this.#honeycombInstrumentationClient;
124
+ }
125
+
126
+ get isLaunchDarklyEnabled() {
127
+ return this.#isLaunchDarklyEnabled;
128
+ }
129
+
130
+ get launchDarklyClient() {
131
+ return getLaunchDarklyPlugin(this).client;
132
+ }
133
+
134
+ get featureFlags() {
135
+ return this.featureFlagSetSnapshot.value;
136
+ }
137
+
138
+ getFeatureFlag<T extends FeatureFlagKey>(key: T, defaultValue?: FeatureFlags[T]) {
139
+ return getLaunchDarklyPlugin(this).getFeatureFlag(key, defaultValue);
140
+ }
141
+
142
+ get featureFlagSetSnapshot() {
143
+ return getLaunchDarklyPlugin(this).featureFlagSetSnapshot;
113
144
  }
114
145
 
115
146
  startScope(logger: Logger): TRuntime {
@@ -118,8 +149,12 @@ export class FireflyRuntime<TRuntime extends FireflyRuntime = any> extends React
118
149
  }
119
150
 
120
151
  export class FireflyRuntimeScope<TRuntime extends FireflyRuntime = FireflyRuntime> extends ReactRouterRuntimeScope<TRuntime> implements IFireflyRuntime {
121
- getMswState() {
122
- return this._runtime.getMswState();
152
+ get isMswEnabled() {
153
+ return this._runtime.isMswEnabled;
154
+ }
155
+
156
+ get mswState() {
157
+ return this._runtime.mswState;
123
158
  }
124
159
 
125
160
  registerRequestHandlers(handlers: RequestHandler[], options: RegisterRequestHandlersOptions = {}) {
@@ -134,19 +169,15 @@ export class FireflyRuntimeScope<TRuntime extends FireflyRuntime = FireflyRuntim
134
169
  return this._runtime.requestHandlers;
135
170
  }
136
171
 
137
- get isMswEnabled() {
138
- return this._runtime.isMswEnabled;
139
- }
140
-
141
- getEnvironmentVariables() {
142
- return this._runtime.getEnvironmentVariables();
143
- }
144
-
145
172
  getEnvironmentVariable(key: EnvironmentVariableKey) {
146
173
  return this._runtime.getEnvironmentVariable(key);
147
174
  }
148
175
 
149
- registerEnvironmentVariable(key: EnvironmentVariableKey, value: EnvironmentVariableValue) {
176
+ get environmentVariables() {
177
+ return this._runtime.environmentVariables;
178
+ }
179
+
180
+ registerEnvironmentVariable<T extends EnvironmentVariableKey>(key: T, value: EnvironmentVariables[T]) {
150
181
  this._runtime.registerEnvironmentVariable(key, value);
151
182
  }
152
183
 
@@ -161,4 +192,28 @@ export class FireflyRuntimeScope<TRuntime extends FireflyRuntime = FireflyRuntim
161
192
  get honeycombInstrumentationClient(): HoneycombInstrumentationPartialClient {
162
193
  throw new Error("[squide] Cannot retrieve the Honeycomb instrumentation client from a runtime scope instance.");
163
194
  }
195
+
196
+ get isLaunchDarklyEnabled() {
197
+ return this._runtime.isLaunchDarklyEnabled;
198
+ }
199
+
200
+ get launchDarklyClient() {
201
+ return this._runtime.launchDarklyClient;
202
+ }
203
+
204
+ get featureFlags() {
205
+ return this._runtime.featureFlags;
206
+ }
207
+
208
+ getFeatureFlag<T extends FeatureFlagKey>(key: T, defaultValue?: FeatureFlags[T]) {
209
+ // The error is because the FeatureFlags interface is empty as it is expected to be augmented by the
210
+ // consumer application.
211
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
212
+ // @ts-ignore
213
+ return this._runtime.getFeatureFlag(key, defaultValue);
214
+ }
215
+
216
+ get featureFlagSetSnapshot() {
217
+ return this._runtime.featureFlagSetSnapshot;
218
+ }
164
219
  }
@@ -1,5 +1,5 @@
1
1
  import type { Span } from "@opentelemetry/api";
2
- import { isPlainObject } from "@squide/core";
2
+ import { isPlainObject } from "@squide/core/internal";
3
3
  import type { Logger } from "@workleap/logging";
4
4
  import { v4 as uuidv4 } from "uuid";
5
5
  import { createTraceContextId } from "./createTraceContextId.ts";
package/src/index.ts CHANGED
@@ -7,9 +7,25 @@ export {
7
7
  useEnvironmentVariables,
8
8
  type EnvironmentVariableKey,
9
9
  type EnvironmentVariables,
10
- type EnvironmentVariablesPluginOptions,
11
- type EnvironmentVariableValue
10
+ type EnvironmentVariablesPluginOptions
12
11
  } from "@squide/env-vars";
12
+ export {
13
+ FeatureFlagSetSnapshot,
14
+ getFeatureFlag,
15
+ getLaunchDarklyPlugin,
16
+ InMemoryLaunchDarklyClient,
17
+ LaunchDarklyClientNotifier,
18
+ LaunchDarklyPlugin,
19
+ LaunchDarklyPluginName,
20
+ useFeatureFlag,
21
+ useFeatureFlags,
22
+ useLaunchDarklyClient,
23
+ type FeatureFlagKey,
24
+ type FeatureFlags,
25
+ type FeatureFlagSetSnapshotChangedListener,
26
+ type InMemoryLaunchDarklyClientOptions,
27
+ type LaunchDarklyClientListener
28
+ } from "@squide/launch-darkly";
13
29
  export {
14
30
  getMswPlugin,
15
31
  MswPlugin,
@@ -1,7 +1,10 @@
1
- import { isFunction, ModuleDefinition, toLocalModuleDefinitions, type ModuleRegisterFunction, type RegisterModulesOptions } from "@squide/core";
1
+ import { ModuleDefinition, toLocalModuleDefinitions, type ModuleRegisterFunction, type RegisterModulesOptions } from "@squide/core";
2
+ import { isFunction } from "@squide/core/internal";
2
3
  import { EnvironmentVariables, EnvironmentVariablesPlugin } from "@squide/env-vars";
4
+ import { LaunchDarklyPlugin } from "@squide/launch-darkly";
3
5
  import { MswPlugin } from "@squide/msw";
4
6
  import type { HoneycombInstrumentationPartialClient } from "@workleap-telemetry/core";
7
+ import { LDClient } from "launchdarkly-js-client-sdk";
5
8
  import { FireflyRuntime, type FireflyRuntimeOptions } from "./FireflyRuntime.tsx";
6
9
  import { initializeHoneycomb } from "./honeycomb/initializeHoneycomb.ts";
7
10
 
@@ -12,18 +15,19 @@ export type OnInitializationErrorFunction = (error: unknown) => void;
12
15
  export type StartMswFunction<TRuntime = FireflyRuntime> = (runtime: TRuntime) => Promise<void>;
13
16
 
14
17
  export interface InitializeFireflyOptions<TRuntime extends FireflyRuntime, TContext = unknown, TData = unknown> extends RegisterModulesOptions<TContext>, FireflyRuntimeOptions {
15
- localModules?: ModuleRegisterFunction<TRuntime, TContext, TData>[];
18
+ localModules?: (ModuleRegisterFunction<TRuntime, TContext, TData> | undefined)[];
16
19
  moduleDefinitions?: ModuleDefinition<TRuntime, TContext, TData>[];
17
20
  useMsw?: boolean;
18
21
  environmentVariables?: Partial<EnvironmentVariables>;
19
22
  honeycombInstrumentationClient?: HoneycombInstrumentationPartialClient;
23
+ launchDarklyClient?: LDClient;
20
24
  startMsw?: StartMswFunction<FireflyRuntime>;
21
25
  onError?: OnInitializationErrorFunction;
22
26
  }
23
27
 
24
28
  export function bootstrap<TRuntime extends FireflyRuntime = FireflyRuntime, TContext = unknown, TData = unknown>(
25
29
  runtime: TRuntime,
26
- modulesDefinitions: ModuleDefinition<TRuntime, TContext, TData>[],
30
+ modulesDefinitions: (ModuleDefinition<TRuntime, TContext, TData> | undefined)[],
27
31
  options: Omit<InitializeFireflyOptions<TRuntime, TContext, TData>, "localModules" | "moduleDefinitions"> = {}
28
32
  ) {
29
33
  const {
@@ -34,33 +38,35 @@ export function bootstrap<TRuntime extends FireflyRuntime = FireflyRuntime, TCon
34
38
 
35
39
  runtime.eventBus.dispatch(ApplicationBootstrappingStartedEvent);
36
40
 
37
- runtime.moduleManager.registerModules(modulesDefinitions, { context })
38
- .then(results => {
39
- if (runtime.isMswEnabled) {
40
- if (!isFunction(startMsw)) {
41
- throw new Error("[squide] When MSW is enabled, the \"startMsw\" function must be provided.");
42
- }
43
-
44
- startMsw(runtime)
45
- .then(() => {
46
- if (runtime.isMswEnabled) {
47
- runtime.getMswState().setAsReady();
48
- }
49
- })
50
- .catch((error: unknown) => {
51
- runtime.logger
52
- .withText("[squide] An error occured while starting MSW.")
53
- .withError(error as Error)
54
- .error();
55
- });
41
+ runtime.moduleManager.registerModules(
42
+ modulesDefinitions.filter((x): x is ModuleDefinition => Boolean(x)),
43
+ { context }
44
+ ).then(results => {
45
+ if (runtime.isMswEnabled) {
46
+ if (!isFunction(startMsw)) {
47
+ throw new Error("[squide] When MSW is enabled, the \"startMsw\" function must be provided.");
56
48
  }
57
49
 
58
- if (onError) {
59
- results.forEach(error => {
60
- onError(error);
50
+ startMsw(runtime)
51
+ .then(() => {
52
+ if (runtime.isMswEnabled) {
53
+ runtime.mswState.setAsReady();
54
+ }
55
+ })
56
+ .catch((error: unknown) => {
57
+ runtime.logger
58
+ .withText("[squide] An error occured while starting MSW.")
59
+ .withError(error as Error)
60
+ .error();
61
61
  });
62
- }
63
- });
62
+ }
63
+
64
+ if (onError) {
65
+ results.forEach(error => {
66
+ onError(error);
67
+ });
68
+ }
69
+ });
64
70
  }
65
71
 
66
72
  let hasExecuted = false;
@@ -79,6 +85,7 @@ export function initializeFirefly<TContext = unknown, TData = unknown>(options:
79
85
  plugins = [],
80
86
  environmentVariables,
81
87
  honeycombInstrumentationClient,
88
+ launchDarklyClient,
82
89
  loggers,
83
90
  onError
84
91
  } = options;
@@ -93,6 +100,10 @@ export function initializeFirefly<TContext = unknown, TData = unknown>(options:
93
100
  plugins.push(x => new MswPlugin(x));
94
101
  }
95
102
 
103
+ if (launchDarklyClient) {
104
+ plugins.push(x => new LaunchDarklyPlugin(x, launchDarklyClient));
105
+ }
106
+
96
107
  const runtime = new FireflyRuntime({
97
108
  mode,
98
109
  honeycombInstrumentationClient,
@@ -5,6 +5,7 @@ export function useCanUpdateDeferredRegistrations() {
5
5
  areModulesReady,
6
6
  publicDataUpdatedAt,
7
7
  protectedDataUpdatedAt,
8
+ featureFlagsUpdatedAt,
8
9
  deferredRegistrationsUpdatedAt
9
10
  } = useAppRouterState();
10
11
 
@@ -17,7 +18,8 @@ export function useCanUpdateDeferredRegistrations() {
17
18
  // If either the public data or the protected data has been updated, update the deferred registrations.
18
19
  && (
19
20
  (publicDataUpdatedAt && publicDataUpdatedAt > deferredRegistrationsUpdatedAt) ||
20
- (protectedDataUpdatedAt && protectedDataUpdatedAt > deferredRegistrationsUpdatedAt)
21
+ (protectedDataUpdatedAt && protectedDataUpdatedAt > deferredRegistrationsUpdatedAt) ||
22
+ (featureFlagsUpdatedAt && featureFlagsUpdatedAt > deferredRegistrationsUpdatedAt)
21
23
  )
22
24
  );
23
25
  }
@@ -1,5 +1,6 @@
1
1
  import { useRuntime, type ModuleRegistrationError } from "@squide/core";
2
2
  import { useEffect } from "react";
3
+ import { FireflyRuntime } from "./FireflyRuntime.tsx";
3
4
  import { useCanRegisterDeferredRegistrations } from "./useCanRegisterDeferredRegistrations.ts";
4
5
  import { useCanUpdateDeferredRegistrations } from "./useCanUpdateDeferredRegistrations.ts";
5
6
  import { useRegisterDeferredRegistrations } from "./useRegisterDeferredRegistrations.ts";
@@ -11,8 +12,8 @@ export interface UseDeferredRegistrationsOptions {
11
12
  onError?: DeferredRegistrationsErrorCallback;
12
13
  }
13
14
 
14
- export function useDeferredRegistrations(data: unknown, { onError }: UseDeferredRegistrationsOptions = {}) {
15
- const runtime = useRuntime();
15
+ export function useDeferredRegistrations(data?: unknown, { onError }: UseDeferredRegistrationsOptions = {}) {
16
+ const runtime = useRuntime() as FireflyRuntime;
16
17
 
17
18
  const canRegisterDeferredRegistrations = useCanRegisterDeferredRegistrations();
18
19
  const canUpdateDeferredRegistrations = useCanUpdateDeferredRegistrations();
@@ -23,7 +24,7 @@ export function useDeferredRegistrations(data: unknown, { onError }: UseDeferred
23
24
  useEffect(() => {
24
25
  if (canRegisterDeferredRegistrations) {
25
26
  const register = async () => {
26
- const errors = await registerDeferredRegistrations(data, runtime);
27
+ const errors = await registerDeferredRegistrations(data);
27
28
 
28
29
  if (errors.length > 0 && onError) {
29
30
  onError(errors);
@@ -32,12 +33,12 @@ export function useDeferredRegistrations(data: unknown, { onError }: UseDeferred
32
33
 
33
34
  register();
34
35
  }
35
- }, [canRegisterDeferredRegistrations, registerDeferredRegistrations, data, onError, runtime]);
36
+ }, [canRegisterDeferredRegistrations, registerDeferredRegistrations, data, onError]);
36
37
 
37
38
  useEffect(() => {
38
39
  if (canUpdateDeferredRegistrations) {
39
40
  const update = async () => {
40
- const errors = await updateDeferredRegistrations(data, runtime);
41
+ const errors = await updateDeferredRegistrations(data);
41
42
 
42
43
  if (errors.length > 0 && onError) {
43
44
  onError(errors);
@@ -46,5 +47,13 @@ export function useDeferredRegistrations(data: unknown, { onError }: UseDeferred
46
47
 
47
48
  update();
48
49
  }
49
- }, [canUpdateDeferredRegistrations, updateDeferredRegistrations, data, onError, runtime]);
50
+ }, [
51
+ canUpdateDeferredRegistrations,
52
+ updateDeferredRegistrations,
53
+ data,
54
+ onError,
55
+ // Trigger this closure when the feature flags changed. Using the timestamp because the
56
+ // actual feature flags are not forwarded to the deferred registrations.
57
+ runtime.appRouterStore.state.featureFlagsUpdatedAt
58
+ ]);
50
59
  }
@@ -1,8 +1,10 @@
1
- import type { Runtime } from "@squide/core";
1
+ import { useRuntime } from "@squide/core";
2
2
  import { useCallback } from "react";
3
3
 
4
4
  export function useRegisterDeferredRegistrations() {
5
- return useCallback(<TData = unknown, TRuntime extends Runtime = Runtime>(data: TData, runtime: TRuntime) => {
5
+ const runtime = useRuntime();
6
+
7
+ return useCallback(<TData = unknown>(data?: TData) => {
6
8
  return runtime.moduleManager.registerDeferredRegistrations(data);
7
- }, []);
9
+ }, [runtime]);
8
10
  }
@@ -1,13 +1,5 @@
1
- import { Runtime, useRuntime } from "@squide/core";
2
- import { useEffect, useSyncExternalStore } from "react";
3
-
4
- function subscribeToModulesReady(runtime: Runtime) {
5
- return (callback: () => void) => {
6
- runtime.moduleManager.registerModulesReadyListener(callback);
7
-
8
- return () => runtime.moduleManager.removeModulesReadyListener(callback);
9
- };
10
- }
1
+ import { useRuntime } from "@squide/core";
2
+ import { useCallback, useEffect, useSyncExternalStore } from "react";
11
3
 
12
4
  export interface UseStrictRegistrationModeOptions {
13
5
  isEnabled?: boolean;
@@ -20,8 +12,16 @@ export function useStrictRegistrationMode(options: UseStrictRegistrationModeOpti
20
12
 
21
13
  const runtime = useRuntime();
22
14
 
15
+ const subscribe = useCallback((callback: () => void) => {
16
+ runtime.moduleManager.registerModulesReadyListener(callback);
17
+
18
+ return () => {
19
+ runtime.moduleManager.removeModulesReadyListener(callback);
20
+ };
21
+ }, [runtime]);
22
+
23
23
  // This listener is only executed if the modules are ready.
24
- const areModulesReady = useSyncExternalStore(subscribeToModulesReady(runtime), () => runtime.moduleManager.getAreModulesReady());
24
+ const areModulesReady = useSyncExternalStore(subscribe, () => runtime.moduleManager.getAreModulesReady());
25
25
 
26
26
  useEffect(() => {
27
27
  if (areModulesReady && isEnabled) {
@@ -1,4 +1,4 @@
1
- import type { Runtime } from "@squide/core";
1
+ import { useRuntime } from "@squide/core";
2
2
  import { useCallback } from "react";
3
3
  import { useAppRouterDispatcher } from "./AppRouterContext.ts";
4
4
 
@@ -6,9 +6,10 @@ export const DeferredRegistrationsUpdateStartedEvent = "squide-deferred-registra
6
6
  export const DeferredRegistrationsUpdateCompletedEvent = "squide-deferred-registrations-update-completed-started";
7
7
 
8
8
  export function useUpdateDeferredRegistrations() {
9
+ const runtime = useRuntime();
9
10
  const dispatch = useAppRouterDispatcher();
10
11
 
11
- return useCallback(async <TData = unknown, TRuntime extends Runtime = Runtime>(data: TData, runtime: TRuntime) => {
12
+ return useCallback(async <TData = unknown>(data?: TData) => {
12
13
  runtime.eventBus.dispatch(DeferredRegistrationsUpdateStartedEvent);
13
14
 
14
15
  const errors = await runtime.moduleManager.updateDeferredRegistrations(data);
@@ -18,5 +19,5 @@ export function useUpdateDeferredRegistrations() {
18
19
  runtime.eventBus.dispatch(DeferredRegistrationsUpdateCompletedEvent);
19
20
 
20
21
  return errors;
21
- }, [dispatch]);
22
+ }, [runtime, dispatch]);
22
23
  }