@intrig/react 0.0.15-27 → 0.0.15-29

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.
@@ -0,0 +1,11 @@
1
+ function _extends() {
2
+ return _extends = Object.assign ? Object.assign.bind() : function (n) {
3
+ for (var e = 1; e < arguments.length; e++) {
4
+ var t = arguments[e];
5
+ for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
6
+ }
7
+ return n;
8
+ }, _extends.apply(null, arguments);
9
+ }
10
+
11
+ export { _extends as extends };
package/extra.js ADDED
@@ -0,0 +1,131 @@
1
+ import { isSuccess, isError, isValidationError, init, pending, success, error } from './network-state.js';
2
+ import { useRef, useEffect, useCallback, useId, useMemo, useState } from 'react';
3
+ import { useIntrigContext } from './intrig-context.js';
4
+
5
+ /**
6
+ * Converts a given hook into a promise-based function.
7
+ *
8
+ * @param {IntrigHook<P, B, T>} hook - The hook function to be converted.
9
+ * @param {string} [key='default'] - An optional key to uniquely identify the hook instance.
10
+ *
11
+ * @return {[(...params: Parameters<ReturnType<IntrigHook<P, B, T>>[1]>) => Promise<T>, () => void]}
12
+ * Returns a tuple containing a function that invokes the hook as a promise and a function to clear the state.
13
+ */
14
+
15
+ // **Implementation**
16
+ function useAsPromise(hook, options) {
17
+ // <- Compatible return type
18
+ const resolveRef = useRef(() => {
19
+ // intentionally kept empty
20
+ });
21
+ const rejectRef = useRef(() => {
22
+ // intentionally kept empty
23
+ });
24
+ const [state, dispatch, clear] = hook(options);
25
+ useEffect(() => {
26
+ if (isSuccess(state)) {
27
+ resolveRef.current == null || resolveRef.current(state.data);
28
+ clear();
29
+ } else if (isError(state)) {
30
+ rejectRef.current == null || rejectRef.current(state.error);
31
+ clear();
32
+ }
33
+ }, [state]);
34
+ const promiseFn = useCallback((...args) => {
35
+ return new Promise((resolve, reject) => {
36
+ resolveRef.current = resolve;
37
+ rejectRef.current = reject;
38
+ const dispatchState = dispatch(...args);
39
+ if (isValidationError(dispatchState)) {
40
+ reject(dispatchState.error);
41
+ }
42
+ });
43
+ }, [dispatch]);
44
+ return [promiseFn, clear];
45
+ }
46
+
47
+ /**
48
+ * A custom hook that manages and returns the network state of a promise-based function,
49
+ * providing a way to execute the function and clear its state.
50
+ *
51
+ * @param fn The promise-based function whose network state is to be managed. It should be a function that returns a promise.
52
+ * @param key An optional identifier for the network state. Defaults to 'default'.
53
+ * @return A tuple containing the current network state, a function to execute the promise, and a function to clear the state.
54
+ */
55
+ function useAsNetworkState(fn, key = 'default') {
56
+ var _context$state3;
57
+ const id = useId();
58
+ const context = useIntrigContext();
59
+ const networkState = useMemo(() => {
60
+ var _context$state, _context$state2;
61
+ return (_context$state = (_context$state2 = context.state) == null ? void 0 : _context$state2[`promiseState:${id}:${key}}`]) != null ? _context$state : init();
62
+ }, [(_context$state3 = context.state) == null ? void 0 : _context$state3[`promiseState:${id}:${key}}`]]);
63
+ const dispatch = useCallback(state => {
64
+ context.dispatch({
65
+ key,
66
+ operation: id,
67
+ source: 'promiseState',
68
+ state
69
+ });
70
+ }, [key, context.dispatch]);
71
+ const execute = useCallback((...args) => {
72
+ dispatch(pending());
73
+ return fn(...args).then(data => {
74
+ dispatch(success(data));
75
+ }, e => {
76
+ dispatch(error(e));
77
+ });
78
+ }, []);
79
+ const clear = useCallback(() => {
80
+ dispatch(init());
81
+ }, []);
82
+ return [networkState, execute, clear];
83
+ }
84
+
85
+ /**
86
+ * A custom hook that resolves the value from the provided hook's state and updates it whenever the state changes.
87
+ *
88
+ * @param {IntrigHook<P, B, T>} hook - The hook that provides the state to observe and resolve data from.
89
+ * @param options
90
+ * @return {T | undefined} The resolved value from the hook's state or undefined if the state is not successful.
91
+ */
92
+
93
+ // **Implementation**
94
+ function useResolvedValue(hook, options) {
95
+ const [value, setValue] = useState();
96
+ const [state] = hook(options); // Ensure compatibility with different hook types
97
+
98
+ useEffect(() => {
99
+ if (isSuccess(state)) {
100
+ setValue(state.data);
101
+ } else {
102
+ setValue(undefined);
103
+ }
104
+ }, [state]);
105
+ return value;
106
+ }
107
+
108
+ /**
109
+ * A custom hook that resolves and caches the value from a successful state provided by the given hook.
110
+ * The state is updated only when it is in a successful state.
111
+ *
112
+ * @param {IntrigHook<P, B, T>} hook - The hook that provides the state to observe and cache data from.
113
+ * @param options
114
+ * @return {T | undefined} The cached value from the hook's state or undefined if the state is not successful.
115
+ */
116
+
117
+ // **Implementation**
118
+ function useResolvedCachedValue(hook, options) {
119
+ const [cachedValue, setCachedValue] = useState();
120
+ const [state] = hook(options); // Ensure compatibility with different hook types
121
+
122
+ useEffect(() => {
123
+ if (isSuccess(state)) {
124
+ setCachedValue(state.data);
125
+ }
126
+ // Do not clear cached value if state is unsuccessful
127
+ }, [state]);
128
+ return cachedValue;
129
+ }
130
+
131
+ export { useAsNetworkState, useAsPromise, useResolvedCachedValue, useResolvedValue };
package/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { IntrigProvider, IntrigProviderStub, StatusTrap, useCentralError, useCentralPendingState, useNetworkState, useTransitionCall } from './intrig-provider.js';
2
+ export { error, init, isError, isInit, isNetworkError, isPending, isRequestValidationError, isResponseValidationError, isSuccess, isSuccessfulDispatch, isValidationError, networkError, pending, requestValidationError, responseValidationError, success, successfulDispatch, validationError } from './network-state.js';
3
+ export { useAsNetworkState, useAsPromise, useResolvedCachedValue, useResolvedValue } from './extra.js';
4
+ export { encode, transform, transformResponse } from './media-type-utils.js';
@@ -0,0 +1,34 @@
1
+ import { createContext, useContext } from 'react';
2
+
3
+ /**
4
+ * Defines the ContextType interface for managing global state, dispatching actions,
5
+ * and holding a collection of Axios instances.
6
+ *
7
+ * @interface ContextType
8
+ * @property {GlobalState} state - The global state of the application.
9
+ * @property {React.Dispatch<NetworkAction<unknown>>} dispatch - The dispatch function to send network actions.
10
+ * @property {Record<string, AxiosInstance>} axios - A record of Axios instances for making HTTP requests.
11
+ */
12
+
13
+ /**
14
+ * Context object created using `createContext` function. Provides a way to share state, dispatch functions,
15
+ * and axios instance across components without having to pass props down manually at every level.
16
+ *
17
+ * @type {ContextType}
18
+ */
19
+ const Context = /*#__PURE__*/createContext({
20
+ state: {},
21
+ filteredState: {},
22
+ dispatch() {
23
+ // intentionally kept empty
24
+ },
25
+ configs: {},
26
+ async execute() {
27
+ // intentionally kept empty
28
+ }
29
+ });
30
+ function useIntrigContext() {
31
+ return useContext(Context);
32
+ }
33
+
34
+ export { Context, useIntrigContext };
@@ -0,0 +1,407 @@
1
+ import { extends as _extends } from './_virtual/_rollupPluginBabelHelpers.js';
2
+ import { useReducer, useMemo, useContext, useState, useCallback, useRef, useEffect } from 'react';
3
+ import { isPending, isError, init, pending, isSuccess, error, success } from './network-state.js';
4
+ import axios, { isAxiosError } from 'axios';
5
+ import { logger } from './logger.js';
6
+ import { flushSync } from 'react-dom';
7
+ import { createParser } from 'eventsource-parser';
8
+ import { Context } from './intrig-context.js';
9
+ import { jsx } from 'react/jsx-runtime';
10
+
11
+ function requestReducer(state, action) {
12
+ return _extends({}, state, {
13
+ [`${action.source}:${action.operation}:${action.key}`]: action.state
14
+ });
15
+ }
16
+
17
+ /**
18
+ * IntrigProvider is a context provider component that sets up global state management
19
+ * and provides Axios instances for API requests.
20
+ *
21
+ * @param {Object} props - The properties object.
22
+ * @param {React.ReactNode} props.children - The child components to be wrapped by the provider.
23
+ * @param {Object} [props.configs={}] - Configuration object for Axios instances.
24
+ * @param {Object} [props.configs.defaults={}] - Default configuration for Axios.
25
+ * @param {Object} [props.configs.petstore={}] - Configuration specific to the petstore API.
26
+ * @return {JSX.Element} A context provider component that wraps the provided children.
27
+ */
28
+ function IntrigProvider({
29
+ children,
30
+ configs = {}
31
+ }) {
32
+ const [state, dispatch] = useReducer(requestReducer, {});
33
+ const axiosInstances = useMemo(() => {
34
+ return {};
35
+ }, [configs]);
36
+ const contextValue = useMemo(() => {
37
+ async function execute(request, dispatch, schema, errorSchema) {
38
+ try {
39
+ dispatch(pending());
40
+ const response = await axiosInstances[request.source].request(request);
41
+ if (response.status >= 200 && response.status < 300) {
42
+ var _response$headers;
43
+ if ((_response$headers = response.headers) != null && (_response$headers = _response$headers['content-type']) != null && _response$headers.includes('text/event-stream')) {
44
+ const reader = response.data.getReader();
45
+ const decoder = new TextDecoder();
46
+ let lastMessage;
47
+ const parser = createParser({
48
+ onEvent(message) {
49
+ let decoded = message.data;
50
+ try {
51
+ let parsed = JSON.parse(decoded);
52
+ if (schema) {
53
+ const validated = schema.safeParse(parsed);
54
+ if (!validated.success) {
55
+ dispatch(error(validated.error.issues, response.status, request));
56
+ return;
57
+ }
58
+ parsed = validated.data;
59
+ }
60
+ decoded = parsed;
61
+ } catch (e) {
62
+ console.error(e);
63
+ }
64
+ lastMessage = decoded;
65
+ flushSync(() => dispatch(pending(undefined, decoded)));
66
+ }
67
+ });
68
+ while (true) {
69
+ const {
70
+ done,
71
+ value
72
+ } = await reader.read();
73
+ if (done) {
74
+ flushSync(() => dispatch(success(lastMessage)));
75
+ break;
76
+ }
77
+ parser.feed(decoder.decode(value, {
78
+ stream: true
79
+ }));
80
+ }
81
+ } else if (schema) {
82
+ const data = schema.safeParse(response.data);
83
+ if (!data.success) {
84
+ dispatch(error(data.error.issues, response.status, request));
85
+ return;
86
+ }
87
+ dispatch(success(data.data));
88
+ } else {
89
+ dispatch(success(response.data));
90
+ }
91
+ } else {
92
+ var _errorSchema$safePars, _response$data, _ref;
93
+ const {
94
+ data
95
+ } = (_errorSchema$safePars = errorSchema == null ? void 0 : errorSchema.safeParse((_response$data = response.data) != null ? _response$data : {})) != null ? _errorSchema$safePars : {};
96
+ //todo: handle error validation error.
97
+ dispatch(error((_ref = data != null ? data : response.data) != null ? _ref : response.statusText, response.status));
98
+ }
99
+ } catch (e) {
100
+ if (isAxiosError(e)) {
101
+ var _errorSchema$safePars2, _e$response$data, _e$response, _e$response2, _e$response3;
102
+ const {
103
+ data
104
+ } = (_errorSchema$safePars2 = errorSchema == null ? void 0 : errorSchema.safeParse((_e$response$data = (_e$response = e.response) == null ? void 0 : _e$response.data) != null ? _e$response$data : {})) != null ? _errorSchema$safePars2 : {};
105
+ dispatch(error(data != null ? data : (_e$response2 = e.response) == null ? void 0 : _e$response2.data, (_e$response3 = e.response) == null ? void 0 : _e$response3.status, request));
106
+ } else {
107
+ dispatch(error(e));
108
+ }
109
+ }
110
+ }
111
+ return {
112
+ state,
113
+ dispatch,
114
+ filteredState: state,
115
+ configs,
116
+ execute
117
+ };
118
+ }, [state, axiosInstances]);
119
+ return /*#__PURE__*/jsx(Context.Provider, {
120
+ value: contextValue,
121
+ children: children
122
+ });
123
+ }
124
+ function IntrigProviderStub({
125
+ children,
126
+ configs = {},
127
+ stubs = () => {
128
+ // intentionally kept empty
129
+ }
130
+ }) {
131
+ const [state, dispatch] = useReducer(requestReducer, {});
132
+ const collectedStubs = useMemo(() => {
133
+ const fns = {};
134
+ function stub(hook, fn) {
135
+ fns[hook.key] = fn;
136
+ }
137
+ stubs(stub);
138
+ return fns;
139
+ }, [stubs]);
140
+ const contextValue = useMemo(() => {
141
+ async function execute(request, dispatch, schema) {
142
+ const stub = collectedStubs[request.key];
143
+ if (stub) {
144
+ try {
145
+ await stub(request.params, request.data, dispatch);
146
+ } catch (e) {
147
+ dispatch(error(e));
148
+ }
149
+ } else {
150
+ dispatch(init());
151
+ }
152
+ }
153
+ return {
154
+ state,
155
+ dispatch,
156
+ filteredState: state,
157
+ configs,
158
+ execute
159
+ };
160
+ }, [state, dispatch, configs, collectedStubs]);
161
+ return /*#__PURE__*/jsx(Context.Provider, {
162
+ value: contextValue,
163
+ children: children
164
+ });
165
+ }
166
+ /**
167
+ * StatusTrap component is used to track and manage network request states.
168
+ *
169
+ * @param {Object} props - The properties object.
170
+ * @param {React.ReactNode} props.children - The child elements to be rendered.
171
+ * @param {string} props.type - The type of network state to handle ("error", "pending", "pending + error").
172
+ * @param {boolean} [props.propagate=true] - Whether to propagate the event to the parent context.
173
+ * @return {React.ReactElement} The context provider component with filtered state and custom dispatch.
174
+ */
175
+ function StatusTrap({
176
+ children,
177
+ type,
178
+ propagate = true
179
+ }) {
180
+ const ctx = useContext(Context);
181
+ const [requests, setRequests] = useState([]);
182
+ const shouldHandleEvent = useCallback(state => {
183
+ switch (type) {
184
+ case 'error':
185
+ return isError(state);
186
+ case 'pending':
187
+ return isPending(state);
188
+ case 'pending + error':
189
+ return isPending(state) || isError(state);
190
+ default:
191
+ return false;
192
+ }
193
+ }, [type]);
194
+ const dispatch = useCallback(event => {
195
+ if (!event.handled) {
196
+ if (shouldHandleEvent(event.state)) {
197
+ setRequests(prev => [...prev, event.key]);
198
+ if (!propagate) {
199
+ ctx.dispatch(_extends({}, event, {
200
+ handled: true
201
+ }));
202
+ return;
203
+ }
204
+ } else {
205
+ setRequests(prev => prev.filter(k => k !== event.key));
206
+ }
207
+ }
208
+ ctx.dispatch(event);
209
+ }, [ctx, propagate, shouldHandleEvent]);
210
+ const filteredState = useMemo(() => {
211
+ return Object.fromEntries(Object.entries(ctx.state).filter(([key]) => requests.includes(key)));
212
+ }, [ctx.state, requests]);
213
+ return /*#__PURE__*/jsx(Context.Provider, {
214
+ value: _extends({}, ctx, {
215
+ dispatch,
216
+ filteredState
217
+ }),
218
+ children: children
219
+ });
220
+ }
221
+ /**
222
+ * useNetworkState is a custom hook that manages the network state within the specified context.
223
+ * It handles making network requests, dispatching appropriate states based on the request lifecycle,
224
+ * and allows aborting ongoing requests.
225
+ *
226
+ * @param {Object} params - The parameters required to configure and use the network state.
227
+ * @param {string} params.key - A unique identifier for the network request.
228
+ * @param {string} params.operation - The operation type related to the request.
229
+ * @param {string} params.source - The source or endpoint for the network request.
230
+ * @param {Object} params.schema - The schema used for validating the response data.
231
+ * @param {number} [params.debounceDelay] - The debounce delay for executing the network request.
232
+ *
233
+ * @return {[NetworkState<T>, (request: AxiosRequestConfig) => void, () => void]}
234
+ * Returns a state object representing the current network state,
235
+ * a function to execute the network request, and a function to clear the request.
236
+ */
237
+ function useNetworkState({
238
+ key,
239
+ operation,
240
+ source,
241
+ schema,
242
+ errorSchema,
243
+ debounceDelay: requestDebounceDelay
244
+ }) {
245
+ var _context$state3;
246
+ const context = useContext(Context);
247
+ const [abortController, setAbortController] = useState();
248
+ const networkState = useMemo(() => {
249
+ var _context$state, _ref2, _context$state2;
250
+ logger.info(`Updating status ${key} ${operation} ${source}`);
251
+ logger.debug('<=', (_context$state = context.state) == null ? void 0 : _context$state[`${source}:${operation}:${key}`]);
252
+ return (_ref2 = (_context$state2 = context.state) == null ? void 0 : _context$state2[`${source}:${operation}:${key}`]) != null ? _ref2 : init();
253
+ }, [JSON.stringify((_context$state3 = context.state) == null ? void 0 : _context$state3[`${source}:${operation}:${key}`])]);
254
+ const dispatch = useCallback(state => {
255
+ context.dispatch({
256
+ key,
257
+ operation,
258
+ source,
259
+ state
260
+ });
261
+ }, [key, operation, source, context.dispatch]);
262
+ const debounceDelay = useMemo(() => {
263
+ var _ref3, _context$configs;
264
+ return (_ref3 = requestDebounceDelay != null ? requestDebounceDelay : (_context$configs = context.configs) == null ? void 0 : _context$configs.debounceDelay) != null ? _ref3 : 0;
265
+ }, [context.configs, requestDebounceDelay]);
266
+ const execute = useCallback(async request => {
267
+ logger.info(`Executing request ${key} ${operation} ${source}`);
268
+ logger.debug('=>', request);
269
+ const abortController = new AbortController();
270
+ setAbortController(abortController);
271
+ const requestConfig = _extends({}, request, {
272
+ onUploadProgress(event) {
273
+ dispatch(pending({
274
+ type: 'upload',
275
+ loaded: event.loaded,
276
+ total: event.total
277
+ }));
278
+ request.onUploadProgress == null || request.onUploadProgress(event);
279
+ },
280
+ onDownloadProgress(event) {
281
+ dispatch(pending({
282
+ type: 'download',
283
+ loaded: event.loaded,
284
+ total: event.total
285
+ }));
286
+ request.onDownloadProgress == null || request.onDownloadProgress(event);
287
+ },
288
+ signal: abortController.signal
289
+ });
290
+ await context.execute(requestConfig, dispatch, schema, errorSchema);
291
+ }, [networkState, context.dispatch, axios]);
292
+ const deboundedExecute = useMemo(() => debounce(execute, debounceDelay != null ? debounceDelay : 0), [execute]);
293
+ const clear = useCallback(() => {
294
+ logger.info(`Clearing request ${key} ${operation} ${source}`);
295
+ dispatch(init());
296
+ setAbortController(abortController => {
297
+ logger.info(`Aborting request ${key} ${operation} ${source}`);
298
+ abortController == null || abortController.abort();
299
+ return undefined;
300
+ });
301
+ }, [dispatch, abortController]);
302
+ return [networkState, deboundedExecute, clear, dispatch];
303
+ }
304
+ function debounce(func, delay) {
305
+ let timeoutId;
306
+ return (...args) => {
307
+ if (timeoutId) {
308
+ clearTimeout(timeoutId);
309
+ }
310
+ timeoutId = setTimeout(() => {
311
+ func(...args);
312
+ }, delay);
313
+ };
314
+ }
315
+
316
+ /**
317
+ * Handles central error extraction from the provided context.
318
+ * It filters the state to retain error states and maps them to a structured error object with additional context information.
319
+ * @return {Object[]} An array of objects representing the error states with context information such as source, operation, and key.
320
+ */
321
+ function useCentralError() {
322
+ const ctx = useContext(Context);
323
+ return useMemo(() => {
324
+ return Object.entries(ctx.filteredState).filter(([, state]) => isError(state)).map(([k, state]) => {
325
+ const [source, operation, key] = k.split(':');
326
+ return _extends({}, state, {
327
+ source,
328
+ operation,
329
+ key
330
+ });
331
+ });
332
+ }, [ctx.filteredState]);
333
+ }
334
+
335
+ /**
336
+ * Uses central pending state handling by aggregating pending states from context.
337
+ * It calculates the overall progress of pending states if any, or returns an initial state otherwise.
338
+ *
339
+ * @return {NetworkState} The aggregated network state based on the pending states and their progress.
340
+ */
341
+ function useCentralPendingState() {
342
+ const ctx = useContext(Context);
343
+ const result = useMemo(() => {
344
+ const pendingStates = Object.values(ctx.filteredState).filter(isPending);
345
+ if (!pendingStates.length) {
346
+ return init();
347
+ }
348
+ const progress = pendingStates.filter(a => a.progress).reduce((progress, current) => {
349
+ var _current$progress$tot, _current$progress, _current$progress$loa, _current$progress2;
350
+ return {
351
+ total: progress.total + ((_current$progress$tot = (_current$progress = current.progress) == null ? void 0 : _current$progress.total) != null ? _current$progress$tot : 0),
352
+ loaded: progress.loaded + ((_current$progress$loa = (_current$progress2 = current.progress) == null ? void 0 : _current$progress2.loaded) != null ? _current$progress$loa : 0)
353
+ };
354
+ }, {
355
+ total: 0,
356
+ loaded: 0
357
+ });
358
+ return pending(progress.total ? progress : undefined);
359
+ }, [ctx.filteredState]);
360
+ return result;
361
+ }
362
+
363
+ /**
364
+ * A hook for making transient calls that can be aborted and validated against schemas.
365
+ * Returns a promise-based call function and an abort function.
366
+ *
367
+ * @param schema - Optional Zod schema for validating the response
368
+ * @param errorSchema - Optional Zod schema for validating error responses
369
+ * @returns A tuple of [call function, abort function]
370
+ */
371
+ function useTransitionCall({
372
+ schema,
373
+ errorSchema
374
+ }) {
375
+ const ctx = useContext(Context);
376
+ const controller = useRef(undefined);
377
+ const schemaRef = useRef(schema);
378
+ const errorSchemaRef = useRef(errorSchema);
379
+ useEffect(() => {
380
+ schemaRef.current = schema;
381
+ errorSchemaRef.current = errorSchema;
382
+ });
383
+ const call = useCallback(async request => {
384
+ var _controller$current;
385
+ (_controller$current = controller.current) == null || _controller$current.abort();
386
+ const abort = new AbortController();
387
+ controller.current = abort;
388
+ return new Promise((resolve, reject) => {
389
+ ctx.execute(_extends({}, request, {
390
+ signal: abort.signal
391
+ }), state => {
392
+ if (isSuccess(state)) {
393
+ resolve(state.data);
394
+ } else if (isError(state)) {
395
+ reject(state.error);
396
+ }
397
+ }, schemaRef.current, errorSchemaRef.current);
398
+ });
399
+ }, [ctx]);
400
+ const abort = useCallback(() => {
401
+ var _controller$current2;
402
+ (_controller$current2 = controller.current) == null || _controller$current2.abort();
403
+ }, []);
404
+ return [call, abort];
405
+ }
406
+
407
+ export { IntrigProvider, IntrigProviderStub, StatusTrap, useCentralError, useCentralPendingState, useNetworkState, useTransitionCall };
package/logger.js ADDED
@@ -0,0 +1,41 @@
1
+ import log from 'loglevel';
2
+
3
+ var _env, _ref, _ref2;
4
+
5
+ // Extend the global interfaces
6
+
7
+ // 1) Build-time default via Vite (if available)
8
+ // Cast import.meta to any to avoid TS errors if env isn't typed
9
+ const buildDefault = typeof import.meta !== 'undefined' ? (_env = import.meta.env) == null ? void 0 : _env.VITE_LOG_LEVEL : undefined;
10
+
11
+ // 2) Stored default in localStorage
12
+ const storedLevel = typeof localStorage !== 'undefined' ? localStorage.getItem('LOG_LEVEL') : null;
13
+
14
+ // Determine initial log level: build-time → stored → 'error'
15
+ const defaultLevel = (_ref = (_ref2 = buildDefault) != null ? _ref2 : storedLevel) != null ? _ref : 'error';
16
+
17
+ // Apply initial level
18
+ log.setLevel(defaultLevel);
19
+
20
+ // Expose a console setter to change level at runtime
21
+ if (typeof window !== 'undefined') {
22
+ window.setLogLevel = level => {
23
+ log.setLevel(level);
24
+ try {
25
+ localStorage.setItem('LOG_LEVEL', String(level));
26
+ } catch (_unused) {
27
+ // ignore if storage is unavailable
28
+ }
29
+ console.log(`✏️ loglevel set to '${level}'`);
30
+ };
31
+ }
32
+
33
+ // Consistent wrapper API
34
+ const logger = {
35
+ info: (msg, meta) => meta ? log.info(msg, meta) : log.info(msg),
36
+ warn: (msg, meta) => meta ? log.warn(msg, meta) : log.warn(msg),
37
+ error: (msg, meta) => meta ? log.error(msg, meta) : log.error(msg),
38
+ debug: (msg, meta) => meta ? log.debug(msg, meta) : log.debug(msg)
39
+ };
40
+
41
+ export { logger as default, logger };