@databuddy/sdk 2.3.30 → 2.4.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/core/index.d.mts +2 -2
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.mjs +1 -1
- package/dist/node/index.d.mts +34 -80
- package/dist/node/index.d.ts +34 -80
- package/dist/node/index.mjs +12 -244
- package/dist/react/index.d.mts +4 -47
- package/dist/react/index.d.ts +4 -47
- package/dist/react/index.mjs +21 -103
- package/dist/shared/@databuddy/{sdk.iTv-9VIB.d.mts → sdk.DOtKE9NQ.d.mts} +5 -11
- package/dist/shared/@databuddy/{sdk.iTv-9VIB.d.ts → sdk.DOtKE9NQ.d.ts} +5 -11
- package/dist/shared/@databuddy/sdk.GM10R1Kp.mjs +46 -0
- package/dist/shared/@databuddy/{sdk.Clxo9P3a.mjs → sdk.P9GHqNXr.mjs} +1 -1
- package/dist/shared/@databuddy/sdk.Ugr1p2j9.mjs +601 -0
- package/dist/shared/@databuddy/sdk.rnu91OSC.d.mts +81 -0
- package/dist/shared/@databuddy/sdk.rnu91OSC.d.ts +81 -0
- package/dist/vue/index.d.mts +8 -28
- package/dist/vue/index.d.ts +8 -28
- package/dist/vue/index.mjs +29 -65
- package/package.json +1 -1
- package/dist/shared/@databuddy/sdk.CALvx07o.mjs +0 -208
- package/dist/shared/@databuddy/sdk.CdLp6SQb.d.mts +0 -132
- package/dist/shared/@databuddy/sdk.CdLp6SQb.d.ts +0 -132
- package/dist/shared/@databuddy/sdk.DE24-JrU.mjs +0 -559
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
interface FlagResult {
|
|
2
|
+
enabled: boolean;
|
|
3
|
+
value: boolean | string | number;
|
|
4
|
+
payload: Record<string, unknown> | null;
|
|
5
|
+
reason: string;
|
|
6
|
+
variant?: string;
|
|
7
|
+
}
|
|
8
|
+
interface UserContext {
|
|
9
|
+
userId?: string;
|
|
10
|
+
email?: string;
|
|
11
|
+
organizationId?: string;
|
|
12
|
+
teamId?: string;
|
|
13
|
+
properties?: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
interface FlagsConfig {
|
|
16
|
+
clientId: string;
|
|
17
|
+
apiUrl?: string;
|
|
18
|
+
user?: UserContext;
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
debug?: boolean;
|
|
21
|
+
/** Skip persistent storage (browser only) */
|
|
22
|
+
skipStorage?: boolean;
|
|
23
|
+
/** Defer evaluation until session resolves */
|
|
24
|
+
isPending?: boolean;
|
|
25
|
+
/** Auto-fetch all flags on init (default: true) */
|
|
26
|
+
autoFetch?: boolean;
|
|
27
|
+
environment?: string;
|
|
28
|
+
/** Cache TTL in ms (default: 60000) */
|
|
29
|
+
cacheTtl?: number;
|
|
30
|
+
/** Stale time in ms — revalidate in background after this (default: cacheTtl/2) */
|
|
31
|
+
staleTime?: number;
|
|
32
|
+
/** Default values by flag key */
|
|
33
|
+
defaults?: Record<string, boolean | string | number>;
|
|
34
|
+
}
|
|
35
|
+
type FlagStatus = "loading" | "ready" | "error" | "pending";
|
|
36
|
+
interface FlagState {
|
|
37
|
+
on: boolean;
|
|
38
|
+
status: FlagStatus;
|
|
39
|
+
loading: boolean;
|
|
40
|
+
value?: boolean | string | number;
|
|
41
|
+
variant?: string;
|
|
42
|
+
}
|
|
43
|
+
interface FlagsContext {
|
|
44
|
+
getFlag: (key: string) => FlagState;
|
|
45
|
+
getValue: <T extends boolean | string | number = boolean>(key: string, defaultValue?: T) => T;
|
|
46
|
+
isOn: (key: string) => boolean;
|
|
47
|
+
fetchFlag: (key: string) => Promise<FlagResult>;
|
|
48
|
+
fetchAllFlags: () => Promise<void>;
|
|
49
|
+
updateUser: (user: UserContext) => void;
|
|
50
|
+
refresh: (forceClear?: boolean) => Promise<void>;
|
|
51
|
+
isReady: boolean;
|
|
52
|
+
}
|
|
53
|
+
interface FlagsSnapshot {
|
|
54
|
+
flags: Record<string, FlagResult>;
|
|
55
|
+
isReady: boolean;
|
|
56
|
+
}
|
|
57
|
+
interface StorageInterface {
|
|
58
|
+
getAll(): Record<string, FlagResult>;
|
|
59
|
+
setAll(flags: Record<string, FlagResult>): void;
|
|
60
|
+
clear(): void;
|
|
61
|
+
}
|
|
62
|
+
interface FlagsManagerOptions {
|
|
63
|
+
config: FlagsConfig;
|
|
64
|
+
storage?: StorageInterface;
|
|
65
|
+
}
|
|
66
|
+
interface FlagsManager {
|
|
67
|
+
getFlag(key: string, user?: UserContext): Promise<FlagResult>;
|
|
68
|
+
isEnabled(key: string): FlagState;
|
|
69
|
+
getValue<T = boolean | string | number>(key: string, defaultValue?: T): T;
|
|
70
|
+
fetchAllFlags(user?: UserContext): Promise<void>;
|
|
71
|
+
updateUser(user: UserContext): void;
|
|
72
|
+
refresh(forceClear?: boolean): Promise<void>;
|
|
73
|
+
updateConfig(config: FlagsConfig): void;
|
|
74
|
+
getMemoryFlags(): Record<string, FlagResult>;
|
|
75
|
+
isReady(): boolean;
|
|
76
|
+
destroy(): void;
|
|
77
|
+
subscribe(callback: () => void): () => void;
|
|
78
|
+
getSnapshot(): FlagsSnapshot;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type { FlagsConfig as F, StorageInterface as S, UserContext as U, FlagState as a, FlagsContext as b, FlagResult as c, FlagsManager as d, FlagsManagerOptions as e, FlagsSnapshot as f };
|
package/dist/vue/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as vue from 'vue';
|
|
2
2
|
import { App, ComputedRef } from 'vue';
|
|
3
|
-
import { F as FlagsConfig,
|
|
4
|
-
export {
|
|
3
|
+
import { F as FlagsConfig, a as FlagState, c as FlagResult } from '../shared/@databuddy/sdk.rnu91OSC.mjs';
|
|
4
|
+
export { b as FlagsContext } from '../shared/@databuddy/sdk.rnu91OSC.mjs';
|
|
5
5
|
|
|
6
6
|
declare const Databuddy: vue.DefineComponent<{
|
|
7
7
|
clientId?: string | undefined;
|
|
@@ -16,7 +16,6 @@ declare const Databuddy: vue.DefineComponent<{
|
|
|
16
16
|
trackAttributes?: boolean | undefined;
|
|
17
17
|
trackOutgoingLinks?: boolean | undefined;
|
|
18
18
|
trackInteractions?: boolean | undefined;
|
|
19
|
-
trackScrollDepth?: boolean | undefined;
|
|
20
19
|
trackPerformance?: boolean | undefined;
|
|
21
20
|
trackWebVitals?: boolean | undefined;
|
|
22
21
|
trackErrors?: boolean | undefined;
|
|
@@ -45,7 +44,6 @@ declare const Databuddy: vue.DefineComponent<{
|
|
|
45
44
|
trackAttributes?: boolean | undefined;
|
|
46
45
|
trackOutgoingLinks?: boolean | undefined;
|
|
47
46
|
trackInteractions?: boolean | undefined;
|
|
48
|
-
trackScrollDepth?: boolean | undefined;
|
|
49
47
|
trackPerformance?: boolean | undefined;
|
|
50
48
|
trackWebVitals?: boolean | undefined;
|
|
51
49
|
trackErrors?: boolean | undefined;
|
|
@@ -66,39 +64,21 @@ declare const Databuddy: vue.DefineComponent<{
|
|
|
66
64
|
interface FlagsPluginOptions extends FlagsConfig {
|
|
67
65
|
}
|
|
68
66
|
declare function createFlagsPlugin(options: FlagsPluginOptions): {
|
|
69
|
-
install(
|
|
67
|
+
install(_app: App): void;
|
|
70
68
|
};
|
|
71
69
|
declare function useFlags(): {
|
|
72
|
-
|
|
70
|
+
getFlag: (key: string) => FlagState;
|
|
73
71
|
fetchAllFlags: () => Promise<void>;
|
|
74
72
|
updateUser: (user: FlagsConfig["user"]) => void;
|
|
75
|
-
refresh: (forceClear?: boolean) => void
|
|
73
|
+
refresh: (forceClear?: boolean) => Promise<void>;
|
|
76
74
|
updateConfig: (config: FlagsConfig) => void;
|
|
77
75
|
memoryFlags: Record<string, FlagResult>;
|
|
78
76
|
};
|
|
79
77
|
|
|
80
|
-
|
|
81
|
-
/** Whether the flag is on */
|
|
78
|
+
declare function useFlag(key: string): {
|
|
82
79
|
on: ComputedRef<boolean>;
|
|
83
|
-
/** @deprecated Use `on` instead */
|
|
84
|
-
enabled: ComputedRef<boolean>;
|
|
85
|
-
/** Whether the flag is loading */
|
|
86
80
|
loading: ComputedRef<boolean>;
|
|
87
|
-
/** @deprecated Use `loading` instead */
|
|
88
|
-
isLoading: ComputedRef<boolean>;
|
|
89
|
-
/** @deprecated Use `status === 'ready'` instead */
|
|
90
|
-
isReady: ComputedRef<boolean>;
|
|
91
|
-
/** Full flag state */
|
|
92
81
|
state: ComputedRef<FlagState>;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Vue composable for individual flag usage with reactivity
|
|
96
|
-
*/
|
|
97
|
-
declare function useFlag(key: string): UseFlagReturn;
|
|
98
|
-
/**
|
|
99
|
-
* Vue composable for boolean flag checking
|
|
100
|
-
* @deprecated Use `useFlag(key).on` instead
|
|
101
|
-
*/
|
|
102
|
-
declare function useBooleanFlag(key: string): ComputedRef<boolean>;
|
|
82
|
+
};
|
|
103
83
|
|
|
104
|
-
export { Databuddy, FlagResult, FlagState, FlagsConfig, createFlagsPlugin,
|
|
84
|
+
export { Databuddy, FlagResult, FlagState, FlagsConfig, createFlagsPlugin, useFlag, useFlags };
|
package/dist/vue/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as vue from 'vue';
|
|
2
2
|
import { App, ComputedRef } from 'vue';
|
|
3
|
-
import { F as FlagsConfig,
|
|
4
|
-
export {
|
|
3
|
+
import { F as FlagsConfig, a as FlagState, c as FlagResult } from '../shared/@databuddy/sdk.rnu91OSC.js';
|
|
4
|
+
export { b as FlagsContext } from '../shared/@databuddy/sdk.rnu91OSC.js';
|
|
5
5
|
|
|
6
6
|
declare const Databuddy: vue.DefineComponent<{
|
|
7
7
|
clientId?: string | undefined;
|
|
@@ -16,7 +16,6 @@ declare const Databuddy: vue.DefineComponent<{
|
|
|
16
16
|
trackAttributes?: boolean | undefined;
|
|
17
17
|
trackOutgoingLinks?: boolean | undefined;
|
|
18
18
|
trackInteractions?: boolean | undefined;
|
|
19
|
-
trackScrollDepth?: boolean | undefined;
|
|
20
19
|
trackPerformance?: boolean | undefined;
|
|
21
20
|
trackWebVitals?: boolean | undefined;
|
|
22
21
|
trackErrors?: boolean | undefined;
|
|
@@ -45,7 +44,6 @@ declare const Databuddy: vue.DefineComponent<{
|
|
|
45
44
|
trackAttributes?: boolean | undefined;
|
|
46
45
|
trackOutgoingLinks?: boolean | undefined;
|
|
47
46
|
trackInteractions?: boolean | undefined;
|
|
48
|
-
trackScrollDepth?: boolean | undefined;
|
|
49
47
|
trackPerformance?: boolean | undefined;
|
|
50
48
|
trackWebVitals?: boolean | undefined;
|
|
51
49
|
trackErrors?: boolean | undefined;
|
|
@@ -66,39 +64,21 @@ declare const Databuddy: vue.DefineComponent<{
|
|
|
66
64
|
interface FlagsPluginOptions extends FlagsConfig {
|
|
67
65
|
}
|
|
68
66
|
declare function createFlagsPlugin(options: FlagsPluginOptions): {
|
|
69
|
-
install(
|
|
67
|
+
install(_app: App): void;
|
|
70
68
|
};
|
|
71
69
|
declare function useFlags(): {
|
|
72
|
-
|
|
70
|
+
getFlag: (key: string) => FlagState;
|
|
73
71
|
fetchAllFlags: () => Promise<void>;
|
|
74
72
|
updateUser: (user: FlagsConfig["user"]) => void;
|
|
75
|
-
refresh: (forceClear?: boolean) => void
|
|
73
|
+
refresh: (forceClear?: boolean) => Promise<void>;
|
|
76
74
|
updateConfig: (config: FlagsConfig) => void;
|
|
77
75
|
memoryFlags: Record<string, FlagResult>;
|
|
78
76
|
};
|
|
79
77
|
|
|
80
|
-
|
|
81
|
-
/** Whether the flag is on */
|
|
78
|
+
declare function useFlag(key: string): {
|
|
82
79
|
on: ComputedRef<boolean>;
|
|
83
|
-
/** @deprecated Use `on` instead */
|
|
84
|
-
enabled: ComputedRef<boolean>;
|
|
85
|
-
/** Whether the flag is loading */
|
|
86
80
|
loading: ComputedRef<boolean>;
|
|
87
|
-
/** @deprecated Use `loading` instead */
|
|
88
|
-
isLoading: ComputedRef<boolean>;
|
|
89
|
-
/** @deprecated Use `status === 'ready'` instead */
|
|
90
|
-
isReady: ComputedRef<boolean>;
|
|
91
|
-
/** Full flag state */
|
|
92
81
|
state: ComputedRef<FlagState>;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Vue composable for individual flag usage with reactivity
|
|
96
|
-
*/
|
|
97
|
-
declare function useFlag(key: string): UseFlagReturn;
|
|
98
|
-
/**
|
|
99
|
-
* Vue composable for boolean flag checking
|
|
100
|
-
* @deprecated Use `useFlag(key).on` instead
|
|
101
|
-
*/
|
|
102
|
-
declare function useBooleanFlag(key: string): ComputedRef<boolean>;
|
|
82
|
+
};
|
|
103
83
|
|
|
104
|
-
export { Databuddy, FlagResult, FlagState, FlagsConfig, createFlagsPlugin,
|
|
84
|
+
export { Databuddy, FlagResult, FlagState, FlagsConfig, createFlagsPlugin, useFlag, useFlags };
|
package/dist/vue/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineComponent, ref, onMounted, onUnmounted, watch, reactive, watchEffect, computed } from 'vue';
|
|
2
|
-
import { i as isScriptInjected, c as createScript } from '../shared/@databuddy/sdk.
|
|
3
|
-
import { B as BrowserFlagStorage
|
|
4
|
-
import '../shared/@databuddy/sdk.
|
|
2
|
+
import { i as isScriptInjected, c as createScript } from '../shared/@databuddy/sdk.P9GHqNXr.mjs';
|
|
3
|
+
import { B as BrowserFlagStorage } from '../shared/@databuddy/sdk.GM10R1Kp.mjs';
|
|
4
|
+
import { B as BrowserFlagsManager } from '../shared/@databuddy/sdk.Ugr1p2j9.mjs';
|
|
5
5
|
|
|
6
6
|
const Databuddy = defineComponent({
|
|
7
7
|
props: {},
|
|
@@ -39,92 +39,56 @@ const Databuddy = defineComponent({
|
|
|
39
39
|
}
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
let
|
|
44
|
-
let globalManager = null;
|
|
42
|
+
let manager = null;
|
|
43
|
+
let state = null;
|
|
45
44
|
function createFlagsPlugin(options) {
|
|
46
45
|
return {
|
|
47
|
-
install(
|
|
46
|
+
install(_app) {
|
|
48
47
|
const storage = options.skipStorage ? void 0 : new BrowserFlagStorage();
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
config: options,
|
|
55
|
-
storage,
|
|
56
|
-
onFlagsUpdate: (flags) => {
|
|
57
|
-
state.memoryFlags = flags;
|
|
48
|
+
state = reactive({ flags: {} });
|
|
49
|
+
manager = new BrowserFlagsManager({ config: options, storage });
|
|
50
|
+
manager.subscribe(() => {
|
|
51
|
+
if (state) {
|
|
52
|
+
state.flags = manager?.getSnapshot().flags ?? {};
|
|
58
53
|
}
|
|
59
54
|
});
|
|
60
|
-
globalManager = manager;
|
|
61
|
-
globalState = state;
|
|
62
|
-
app.provide(FLAGS_SYMBOL, state);
|
|
63
55
|
}
|
|
64
56
|
};
|
|
65
57
|
}
|
|
66
58
|
function useFlags() {
|
|
67
|
-
if (!
|
|
59
|
+
if (!manager) {
|
|
68
60
|
throw new Error(
|
|
69
|
-
"Flags plugin not installed.
|
|
61
|
+
"Flags plugin not installed. Call app.use(createFlagsPlugin(config)) first."
|
|
70
62
|
);
|
|
71
63
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
"Flags manager not initialized. Please reinstall the plugin."
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
const state = globalState;
|
|
78
|
-
const manager = globalManager;
|
|
79
|
-
const isEnabled = (key) => manager.isEnabled(key);
|
|
80
|
-
const fetchAllFlags = () => manager.fetchAllFlags();
|
|
81
|
-
const updateUser = (user) => {
|
|
82
|
-
if (user) {
|
|
83
|
-
manager.updateUser(user);
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
const refresh = (forceClear = false) => {
|
|
87
|
-
manager.refresh(forceClear);
|
|
88
|
-
};
|
|
89
|
-
const updateConfig = (config) => {
|
|
90
|
-
manager.updateConfig(config);
|
|
91
|
-
};
|
|
64
|
+
const m = manager;
|
|
65
|
+
const s = state;
|
|
92
66
|
return {
|
|
93
|
-
isEnabled,
|
|
94
|
-
fetchAllFlags,
|
|
95
|
-
updateUser
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
67
|
+
getFlag: (key) => m.isEnabled(key),
|
|
68
|
+
fetchAllFlags: () => m.fetchAllFlags(),
|
|
69
|
+
updateUser: (user) => {
|
|
70
|
+
if (user) {
|
|
71
|
+
m.updateUser(user);
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
refresh: (forceClear = false) => m.refresh(forceClear),
|
|
75
|
+
updateConfig: (config) => m.updateConfig(config),
|
|
76
|
+
memoryFlags: s?.flags ?? {}
|
|
99
77
|
};
|
|
100
78
|
}
|
|
101
79
|
|
|
102
|
-
const defaultState = {
|
|
103
|
-
on: false,
|
|
104
|
-
enabled: false,
|
|
105
|
-
status: "loading",
|
|
106
|
-
loading: true,
|
|
107
|
-
isLoading: true,
|
|
108
|
-
isReady: false
|
|
109
|
-
};
|
|
80
|
+
const defaultState = { on: false, status: "loading", loading: true };
|
|
110
81
|
function useFlag(key) {
|
|
111
|
-
const {
|
|
82
|
+
const { getFlag } = useFlags();
|
|
112
83
|
const flagState = ref(defaultState);
|
|
113
84
|
watchEffect(() => {
|
|
114
|
-
flagState.value =
|
|
85
|
+
flagState.value = getFlag(key);
|
|
115
86
|
});
|
|
116
87
|
return {
|
|
117
88
|
on: computed(() => flagState.value.on),
|
|
118
|
-
enabled: computed(() => flagState.value.enabled),
|
|
119
89
|
loading: computed(() => flagState.value.loading),
|
|
120
|
-
isLoading: computed(() => flagState.value.isLoading),
|
|
121
|
-
isReady: computed(() => flagState.value.isReady),
|
|
122
90
|
state: computed(() => flagState.value)
|
|
123
91
|
};
|
|
124
92
|
}
|
|
125
|
-
function useBooleanFlag(key) {
|
|
126
|
-
const { on } = useFlag(key);
|
|
127
|
-
return on;
|
|
128
|
-
}
|
|
129
93
|
|
|
130
|
-
export { Databuddy, createFlagsPlugin,
|
|
94
|
+
export { Databuddy, createFlagsPlugin, useFlag, useFlags };
|
package/package.json
CHANGED
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
class Logger {
|
|
2
|
-
debugEnabled = false;
|
|
3
|
-
/**
|
|
4
|
-
* Enable or disable debug logging
|
|
5
|
-
*/
|
|
6
|
-
setDebug(enabled) {
|
|
7
|
-
this.debugEnabled = enabled;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Log debug messages (only when debug is enabled)
|
|
11
|
-
*/
|
|
12
|
-
debug(...args) {
|
|
13
|
-
if (this.debugEnabled) {
|
|
14
|
-
console.log("[Databuddy]", ...args);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Log info messages (always enabled)
|
|
19
|
-
*/
|
|
20
|
-
info(...args) {
|
|
21
|
-
console.info("[Databuddy]", ...args);
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Log warning messages (always enabled)
|
|
25
|
-
*/
|
|
26
|
-
warn(...args) {
|
|
27
|
-
console.warn("[Databuddy]", ...args);
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Log error messages (always enabled)
|
|
31
|
-
*/
|
|
32
|
-
error(...args) {
|
|
33
|
-
console.error("[Databuddy]", ...args);
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Log with table format (only when debug is enabled)
|
|
37
|
-
*/
|
|
38
|
-
table(data) {
|
|
39
|
-
if (this.debugEnabled) {
|
|
40
|
-
console.table(data);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Time a function execution (only when debug is enabled)
|
|
45
|
-
*/
|
|
46
|
-
time(label) {
|
|
47
|
-
if (this.debugEnabled) {
|
|
48
|
-
console.time(`[Databuddy] ${label}`);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* End timing a function execution (only when debug is enabled)
|
|
53
|
-
*/
|
|
54
|
-
timeEnd(label) {
|
|
55
|
-
if (this.debugEnabled) {
|
|
56
|
-
console.timeEnd(`[Databuddy] ${label}`);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Log JSON data (only when debug is enabled)
|
|
61
|
-
*/
|
|
62
|
-
json(data) {
|
|
63
|
-
if (this.debugEnabled) {
|
|
64
|
-
console.log("[Databuddy]", JSON.stringify(data, null, 2));
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
const logger = new Logger();
|
|
69
|
-
|
|
70
|
-
const DEFAULT_RESULT = {
|
|
71
|
-
enabled: false,
|
|
72
|
-
value: false,
|
|
73
|
-
payload: null,
|
|
74
|
-
reason: "DEFAULT"
|
|
75
|
-
};
|
|
76
|
-
function getCacheKey(key, user) {
|
|
77
|
-
if (!(user?.userId || user?.email)) {
|
|
78
|
-
return key;
|
|
79
|
-
}
|
|
80
|
-
return `${key}:${user.userId ?? ""}:${user.email ?? ""}`;
|
|
81
|
-
}
|
|
82
|
-
function buildQueryParams(config, user) {
|
|
83
|
-
const params = new URLSearchParams();
|
|
84
|
-
params.set("clientId", config.clientId);
|
|
85
|
-
const u = user ?? config.user;
|
|
86
|
-
if (u?.userId) {
|
|
87
|
-
params.set("userId", u.userId);
|
|
88
|
-
}
|
|
89
|
-
if (u?.email) {
|
|
90
|
-
params.set("email", u.email);
|
|
91
|
-
}
|
|
92
|
-
if (u?.organizationId) {
|
|
93
|
-
params.set("organizationId", u.organizationId);
|
|
94
|
-
}
|
|
95
|
-
if (u?.teamId) {
|
|
96
|
-
params.set("teamId", u.teamId);
|
|
97
|
-
}
|
|
98
|
-
if (u?.properties) {
|
|
99
|
-
params.set("properties", JSON.stringify(u.properties));
|
|
100
|
-
}
|
|
101
|
-
if (config.environment) {
|
|
102
|
-
params.set("environment", config.environment);
|
|
103
|
-
}
|
|
104
|
-
return params;
|
|
105
|
-
}
|
|
106
|
-
async function fetchFlags(apiUrl, keys, params) {
|
|
107
|
-
const batchParams = new URLSearchParams(params);
|
|
108
|
-
batchParams.set("keys", keys.join(","));
|
|
109
|
-
const url = `${apiUrl}/public/v1/flags/bulk?${batchParams}`;
|
|
110
|
-
const response = await fetch(url);
|
|
111
|
-
if (!response.ok) {
|
|
112
|
-
const result = {};
|
|
113
|
-
for (const key of keys) {
|
|
114
|
-
result[key] = { ...DEFAULT_RESULT, reason: "ERROR" };
|
|
115
|
-
}
|
|
116
|
-
return result;
|
|
117
|
-
}
|
|
118
|
-
const data = await response.json();
|
|
119
|
-
return data.flags ?? {};
|
|
120
|
-
}
|
|
121
|
-
async function fetchAllFlags(apiUrl, params) {
|
|
122
|
-
const url = `${apiUrl}/public/v1/flags/bulk?${params}`;
|
|
123
|
-
const response = await fetch(url);
|
|
124
|
-
if (!response.ok) {
|
|
125
|
-
return {};
|
|
126
|
-
}
|
|
127
|
-
const data = await response.json();
|
|
128
|
-
return data.flags ?? {};
|
|
129
|
-
}
|
|
130
|
-
function isCacheValid(entry) {
|
|
131
|
-
if (!entry) {
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
return Date.now() <= entry.expiresAt;
|
|
135
|
-
}
|
|
136
|
-
function isCacheStale(entry) {
|
|
137
|
-
return Date.now() > entry.staleAt;
|
|
138
|
-
}
|
|
139
|
-
function createCacheEntry(result, ttl, staleTime) {
|
|
140
|
-
const now = Date.now();
|
|
141
|
-
return {
|
|
142
|
-
result,
|
|
143
|
-
staleAt: now + (staleTime ?? ttl / 2),
|
|
144
|
-
expiresAt: now + ttl
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
class RequestBatcher {
|
|
148
|
-
pending = /* @__PURE__ */ new Map();
|
|
149
|
-
timer = null;
|
|
150
|
-
batchDelayMs;
|
|
151
|
-
apiUrl;
|
|
152
|
-
params;
|
|
153
|
-
constructor(apiUrl, params, batchDelayMs = 10) {
|
|
154
|
-
this.apiUrl = apiUrl;
|
|
155
|
-
this.params = params;
|
|
156
|
-
this.batchDelayMs = batchDelayMs;
|
|
157
|
-
}
|
|
158
|
-
request(key) {
|
|
159
|
-
return new Promise((resolve, reject) => {
|
|
160
|
-
const existing = this.pending.get(key);
|
|
161
|
-
if (existing) {
|
|
162
|
-
existing.push({ resolve, reject });
|
|
163
|
-
} else {
|
|
164
|
-
this.pending.set(key, [{ resolve, reject }]);
|
|
165
|
-
}
|
|
166
|
-
if (!this.timer) {
|
|
167
|
-
this.timer = setTimeout(() => this.flush(), this.batchDelayMs);
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
async flush() {
|
|
172
|
-
this.timer = null;
|
|
173
|
-
const keys = [...this.pending.keys()];
|
|
174
|
-
const callbacks = new Map(this.pending);
|
|
175
|
-
this.pending.clear();
|
|
176
|
-
if (keys.length === 0) {
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
try {
|
|
180
|
-
const results = await fetchFlags(this.apiUrl, keys, this.params);
|
|
181
|
-
for (const [key, cbs] of callbacks) {
|
|
182
|
-
const result = results[key] ?? {
|
|
183
|
-
...DEFAULT_RESULT,
|
|
184
|
-
reason: "NOT_FOUND"
|
|
185
|
-
};
|
|
186
|
-
for (const cb of cbs) {
|
|
187
|
-
cb.resolve(result);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
} catch (err) {
|
|
191
|
-
const error = err instanceof Error ? err : new Error("Fetch failed");
|
|
192
|
-
for (const cbs of callbacks.values()) {
|
|
193
|
-
for (const cb of cbs) {
|
|
194
|
-
cb.reject(error);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
destroy() {
|
|
200
|
-
if (this.timer) {
|
|
201
|
-
clearTimeout(this.timer);
|
|
202
|
-
this.timer = null;
|
|
203
|
-
}
|
|
204
|
-
this.pending.clear();
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
export { DEFAULT_RESULT as D, RequestBatcher as R, isCacheStale as a, buildQueryParams as b, createCacheEntry as c, fetchAllFlags as f, getCacheKey as g, isCacheValid as i, logger as l };
|