@drakkar.software/sunglasses-react 0.2.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.
@@ -0,0 +1,122 @@
1
+ import React from 'react';
2
+ import { ISunglassesClient, ScreenTrackingOptions, ConsentStatus } from '@drakkar.software/sunglasses-core';
3
+ export { ConsentStatus, ISunglassesClient, ScreenTrackingOptions, SunglassesConfig, SunglassesCore, SunglassesEvent } from '@drakkar.software/sunglasses-core';
4
+
5
+ interface SunglassesProviderProps {
6
+ /** An initialized ISunglassesClient (from SunglassesCore.create()). */
7
+ client: ISunglassesClient;
8
+ /** Optional screen tracking configuration. */
9
+ screenTracking?: ScreenTrackingOptions;
10
+ children: React.ReactNode;
11
+ }
12
+ /**
13
+ * Provides a SunGlasses client to the React component tree.
14
+ *
15
+ * Place this at the root of your application, wrapping all components that
16
+ * need access to event tracking.
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * const client = await SunglassesCore.create({ ... });
21
+ *
22
+ * function App() {
23
+ * return (
24
+ * <SunglassesProvider client={client} screenTracking={{ useHistoryApi: true }}>
25
+ * <Router />
26
+ * </SunglassesProvider>
27
+ * );
28
+ * }
29
+ * ```
30
+ */
31
+ declare function SunglassesProvider({ client, screenTracking, children, }: SunglassesProviderProps): React.ReactElement;
32
+
33
+ /**
34
+ * Read UTM attribution parameters from the current URL and register them as
35
+ * super properties on the client.
36
+ *
37
+ * Call this once at app startup, immediately after `SunglassesCore.create()`.
38
+ * The registered properties are automatically merged into every subsequent event
39
+ * until `client.unregister()` or `client.reset()` is called.
40
+ *
41
+ * Captured properties (when present):
42
+ * - `utm_source` — traffic source (e.g. "google", "newsletter")
43
+ * - `utm_medium` — marketing channel (e.g. "cpc", "email")
44
+ * - `utm_campaign` — campaign name (e.g. "spring_sale")
45
+ * - `utm_content` — ad variant (e.g. "banner_v2")
46
+ * - `utm_term` — paid search keyword
47
+ * - `$referrer` — full URL of the referring page
48
+ * - `$referring_domain` — hostname of the referring page
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * const client = await SunglassesCore.create({ ... });
53
+ * captureUtmParams(client); // call once on initial load
54
+ * ```
55
+ */
56
+ declare function captureUtmParams(client: ISunglassesClient): void;
57
+
58
+ /**
59
+ * Access the SunGlasses client from React context.
60
+ * Throws if called outside of a SunglassesProvider.
61
+ */
62
+ declare function useSunglasses(): ISunglassesClient;
63
+
64
+ /**
65
+ * Web screen tracking via the History API.
66
+ *
67
+ * Monkey-patches history.pushState and history.replaceState, and listens to
68
+ * the popstate event. Fires client.screen() on every navigation.
69
+ *
70
+ * Must be called inside a component that has access to the SunGlasses client,
71
+ * typically inside SunglassesProvider. Only one instance of this hook should
72
+ * be active at a time — mounting it in multiple components simultaneously will
73
+ * result in the second instance being a no-op until the first unmounts.
74
+ */
75
+ declare function useScreenTracking(client: ISunglassesClient, options?: ScreenTrackingOptions): void;
76
+
77
+ /**
78
+ * Returns a bound `capture` function for the current SunGlasses client.
79
+ *
80
+ * Shorthand for `useSunglasses().capture` — avoids holding a reference to the
81
+ * entire client when you only need to fire events from a component.
82
+ *
83
+ * The returned function is stable across renders (same reference) as long as
84
+ * the client itself does not change.
85
+ *
86
+ * @example
87
+ * ```tsx
88
+ * function SubmitButton() {
89
+ * const capture = useCapture();
90
+ * return (
91
+ * <button onClick={() => capture('form_submitted', { formId: 'signup' })}>
92
+ * Submit
93
+ * </button>
94
+ * );
95
+ * }
96
+ * ```
97
+ */
98
+ declare function useCapture(): (eventName: string, properties?: Record<string, unknown>) => void;
99
+
100
+ /**
101
+ * Returns the current consent status as a snapshot.
102
+ *
103
+ * This is a convenience hook over `useSunglasses().getConsentStatus()`.
104
+ *
105
+ * **Important:** this hook returns a snapshot at render time and does not
106
+ * subscribe to future changes. Re-renders are not triggered automatically
107
+ * when `optIn()` or `optOut()` are called. If you need the UI to react to
108
+ * consent changes, lift the consent call into a parent component that also
109
+ * manages re-render state, or call `useSunglasses()` directly.
110
+ *
111
+ * @example
112
+ * ```tsx
113
+ * function ConsentBanner() {
114
+ * const status = useConsentStatus();
115
+ * if (status !== 'unknown') return null;
116
+ * return <Banner />;
117
+ * }
118
+ * ```
119
+ */
120
+ declare function useConsentStatus(): ConsentStatus;
121
+
122
+ export { SunglassesProvider, type SunglassesProviderProps, captureUtmParams, useCapture, useConsentStatus, useScreenTracking, useSunglasses };
@@ -0,0 +1,122 @@
1
+ import React from 'react';
2
+ import { ISunglassesClient, ScreenTrackingOptions, ConsentStatus } from '@drakkar.software/sunglasses-core';
3
+ export { ConsentStatus, ISunglassesClient, ScreenTrackingOptions, SunglassesConfig, SunglassesCore, SunglassesEvent } from '@drakkar.software/sunglasses-core';
4
+
5
+ interface SunglassesProviderProps {
6
+ /** An initialized ISunglassesClient (from SunglassesCore.create()). */
7
+ client: ISunglassesClient;
8
+ /** Optional screen tracking configuration. */
9
+ screenTracking?: ScreenTrackingOptions;
10
+ children: React.ReactNode;
11
+ }
12
+ /**
13
+ * Provides a SunGlasses client to the React component tree.
14
+ *
15
+ * Place this at the root of your application, wrapping all components that
16
+ * need access to event tracking.
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * const client = await SunglassesCore.create({ ... });
21
+ *
22
+ * function App() {
23
+ * return (
24
+ * <SunglassesProvider client={client} screenTracking={{ useHistoryApi: true }}>
25
+ * <Router />
26
+ * </SunglassesProvider>
27
+ * );
28
+ * }
29
+ * ```
30
+ */
31
+ declare function SunglassesProvider({ client, screenTracking, children, }: SunglassesProviderProps): React.ReactElement;
32
+
33
+ /**
34
+ * Read UTM attribution parameters from the current URL and register them as
35
+ * super properties on the client.
36
+ *
37
+ * Call this once at app startup, immediately after `SunglassesCore.create()`.
38
+ * The registered properties are automatically merged into every subsequent event
39
+ * until `client.unregister()` or `client.reset()` is called.
40
+ *
41
+ * Captured properties (when present):
42
+ * - `utm_source` — traffic source (e.g. "google", "newsletter")
43
+ * - `utm_medium` — marketing channel (e.g. "cpc", "email")
44
+ * - `utm_campaign` — campaign name (e.g. "spring_sale")
45
+ * - `utm_content` — ad variant (e.g. "banner_v2")
46
+ * - `utm_term` — paid search keyword
47
+ * - `$referrer` — full URL of the referring page
48
+ * - `$referring_domain` — hostname of the referring page
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * const client = await SunglassesCore.create({ ... });
53
+ * captureUtmParams(client); // call once on initial load
54
+ * ```
55
+ */
56
+ declare function captureUtmParams(client: ISunglassesClient): void;
57
+
58
+ /**
59
+ * Access the SunGlasses client from React context.
60
+ * Throws if called outside of a SunglassesProvider.
61
+ */
62
+ declare function useSunglasses(): ISunglassesClient;
63
+
64
+ /**
65
+ * Web screen tracking via the History API.
66
+ *
67
+ * Monkey-patches history.pushState and history.replaceState, and listens to
68
+ * the popstate event. Fires client.screen() on every navigation.
69
+ *
70
+ * Must be called inside a component that has access to the SunGlasses client,
71
+ * typically inside SunglassesProvider. Only one instance of this hook should
72
+ * be active at a time — mounting it in multiple components simultaneously will
73
+ * result in the second instance being a no-op until the first unmounts.
74
+ */
75
+ declare function useScreenTracking(client: ISunglassesClient, options?: ScreenTrackingOptions): void;
76
+
77
+ /**
78
+ * Returns a bound `capture` function for the current SunGlasses client.
79
+ *
80
+ * Shorthand for `useSunglasses().capture` — avoids holding a reference to the
81
+ * entire client when you only need to fire events from a component.
82
+ *
83
+ * The returned function is stable across renders (same reference) as long as
84
+ * the client itself does not change.
85
+ *
86
+ * @example
87
+ * ```tsx
88
+ * function SubmitButton() {
89
+ * const capture = useCapture();
90
+ * return (
91
+ * <button onClick={() => capture('form_submitted', { formId: 'signup' })}>
92
+ * Submit
93
+ * </button>
94
+ * );
95
+ * }
96
+ * ```
97
+ */
98
+ declare function useCapture(): (eventName: string, properties?: Record<string, unknown>) => void;
99
+
100
+ /**
101
+ * Returns the current consent status as a snapshot.
102
+ *
103
+ * This is a convenience hook over `useSunglasses().getConsentStatus()`.
104
+ *
105
+ * **Important:** this hook returns a snapshot at render time and does not
106
+ * subscribe to future changes. Re-renders are not triggered automatically
107
+ * when `optIn()` or `optOut()` are called. If you need the UI to react to
108
+ * consent changes, lift the consent call into a parent component that also
109
+ * manages re-render state, or call `useSunglasses()` directly.
110
+ *
111
+ * @example
112
+ * ```tsx
113
+ * function ConsentBanner() {
114
+ * const status = useConsentStatus();
115
+ * if (status !== 'unknown') return null;
116
+ * return <Banner />;
117
+ * }
118
+ * ```
119
+ */
120
+ declare function useConsentStatus(): ConsentStatus;
121
+
122
+ export { SunglassesProvider, type SunglassesProviderProps, captureUtmParams, useCapture, useConsentStatus, useScreenTracking, useSunglasses };
package/dist/index.js ADDED
@@ -0,0 +1,191 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ SunglassesCore: () => import_sunglasses_core.SunglassesCore,
24
+ SunglassesProvider: () => SunglassesProvider,
25
+ captureUtmParams: () => captureUtmParams,
26
+ useCapture: () => useCapture,
27
+ useConsentStatus: () => useConsentStatus,
28
+ useScreenTracking: () => useScreenTracking,
29
+ useSunglasses: () => useSunglasses
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/SunglassesProvider.tsx
34
+ var import_react3 = require("react");
35
+
36
+ // src/context.ts
37
+ var import_react = require("react");
38
+ var SunglassesContext = (0, import_react.createContext)(null);
39
+ function useSunglasses() {
40
+ const client = (0, import_react.useContext)(SunglassesContext);
41
+ if (client === null) {
42
+ throw new Error(
43
+ "[SunGlasses] useSunglasses() must be called inside a <SunglassesProvider>."
44
+ );
45
+ }
46
+ return client;
47
+ }
48
+
49
+ // src/useScreenTracking.ts
50
+ var import_react2 = require("react");
51
+ var historyPatched = false;
52
+ var previousPath = null;
53
+ function useScreenTracking(client, options = {}) {
54
+ const { useHistoryApi = true, screenNameMapper } = options;
55
+ const clientRef = (0, import_react2.useRef)(client);
56
+ clientRef.current = client;
57
+ const mapperRef = (0, import_react2.useRef)(screenNameMapper);
58
+ mapperRef.current = screenNameMapper;
59
+ (0, import_react2.useEffect)(() => {
60
+ if (!useHistoryApi || typeof window === "undefined") return;
61
+ if (historyPatched) {
62
+ console.warn(
63
+ "[SunGlasses] useScreenTracking: history is already patched by another instance \u2014 this instance will not track."
64
+ );
65
+ return;
66
+ }
67
+ historyPatched = true;
68
+ const handleRouteChange = (path) => {
69
+ const name = mapperRef.current ? mapperRef.current(path) : path;
70
+ const props = {
71
+ $path: path,
72
+ $url: window.location.href
73
+ };
74
+ if (document.title) props.$title = document.title;
75
+ if (previousPath !== null) {
76
+ props.$referrer = previousPath;
77
+ } else if (document.referrer) {
78
+ props.$referrer = document.referrer;
79
+ }
80
+ previousPath = path;
81
+ clientRef.current.screen(name, props);
82
+ };
83
+ handleRouteChange(window.location.pathname);
84
+ const originalPush = history.pushState.bind(history);
85
+ history.pushState = (...args) => {
86
+ originalPush(...args);
87
+ handleRouteChange(window.location.pathname);
88
+ };
89
+ const originalReplace = history.replaceState.bind(history);
90
+ history.replaceState = (...args) => {
91
+ originalReplace(...args);
92
+ handleRouteChange(window.location.pathname);
93
+ };
94
+ const onPopState = () => handleRouteChange(window.location.pathname);
95
+ window.addEventListener("popstate", onPopState);
96
+ return () => {
97
+ history.pushState = originalPush;
98
+ history.replaceState = originalReplace;
99
+ window.removeEventListener("popstate", onPopState);
100
+ historyPatched = false;
101
+ previousPath = null;
102
+ };
103
+ }, [useHistoryApi]);
104
+ }
105
+
106
+ // src/SunglassesProvider.tsx
107
+ var import_jsx_runtime = require("react/jsx-runtime");
108
+ function SunglassesProvider({
109
+ client,
110
+ screenTracking,
111
+ children
112
+ }) {
113
+ (0, import_react3.useEffect)(() => {
114
+ return () => {
115
+ client.shutdown().catch(() => {
116
+ });
117
+ };
118
+ }, [client]);
119
+ (0, import_react3.useEffect)(() => {
120
+ if (typeof document === "undefined") return;
121
+ const handleVisibilityChange = () => {
122
+ if (document.visibilityState === "hidden") {
123
+ client.flush().catch(() => {
124
+ });
125
+ }
126
+ };
127
+ document.addEventListener("visibilitychange", handleVisibilityChange);
128
+ return () => document.removeEventListener("visibilitychange", handleVisibilityChange);
129
+ }, [client]);
130
+ useScreenTracking(client, screenTracking ?? { useHistoryApi: false });
131
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SunglassesContext.Provider, { value: client, children });
132
+ }
133
+
134
+ // src/captureUtmParams.ts
135
+ var UTM_PARAMS = [
136
+ "utm_source",
137
+ "utm_medium",
138
+ "utm_campaign",
139
+ "utm_content",
140
+ "utm_term"
141
+ ];
142
+ function captureUtmParams(client) {
143
+ if (typeof window === "undefined") return;
144
+ const params = {};
145
+ const searchParams = new URLSearchParams(window.location.search);
146
+ for (const key of UTM_PARAMS) {
147
+ const value = searchParams.get(key);
148
+ if (value) params[key] = value;
149
+ }
150
+ if (document.referrer) {
151
+ params["$referrer"] = document.referrer;
152
+ try {
153
+ params["$referring_domain"] = new URL(document.referrer).hostname;
154
+ } catch {
155
+ }
156
+ }
157
+ if (Object.keys(params).length > 0) {
158
+ client.register(params);
159
+ }
160
+ }
161
+
162
+ // src/useCapture.ts
163
+ var import_react4 = require("react");
164
+ function useCapture() {
165
+ const client = useSunglasses();
166
+ return (0, import_react4.useCallback)(
167
+ (eventName, properties) => {
168
+ client.capture(eventName, properties);
169
+ },
170
+ [client]
171
+ );
172
+ }
173
+
174
+ // src/useConsentStatus.ts
175
+ function useConsentStatus() {
176
+ const client = useSunglasses();
177
+ return client.getConsentStatus();
178
+ }
179
+
180
+ // src/index.ts
181
+ var import_sunglasses_core = require("@drakkar.software/sunglasses-core");
182
+ // Annotate the CommonJS export names for ESM import in node:
183
+ 0 && (module.exports = {
184
+ SunglassesCore,
185
+ SunglassesProvider,
186
+ captureUtmParams,
187
+ useCapture,
188
+ useConsentStatus,
189
+ useScreenTracking,
190
+ useSunglasses
191
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,158 @@
1
+ // src/SunglassesProvider.tsx
2
+ import { useEffect as useEffect2 } from "react";
3
+
4
+ // src/context.ts
5
+ import { createContext, useContext } from "react";
6
+ var SunglassesContext = createContext(null);
7
+ function useSunglasses() {
8
+ const client = useContext(SunglassesContext);
9
+ if (client === null) {
10
+ throw new Error(
11
+ "[SunGlasses] useSunglasses() must be called inside a <SunglassesProvider>."
12
+ );
13
+ }
14
+ return client;
15
+ }
16
+
17
+ // src/useScreenTracking.ts
18
+ import { useEffect, useRef } from "react";
19
+ var historyPatched = false;
20
+ var previousPath = null;
21
+ function useScreenTracking(client, options = {}) {
22
+ const { useHistoryApi = true, screenNameMapper } = options;
23
+ const clientRef = useRef(client);
24
+ clientRef.current = client;
25
+ const mapperRef = useRef(screenNameMapper);
26
+ mapperRef.current = screenNameMapper;
27
+ useEffect(() => {
28
+ if (!useHistoryApi || typeof window === "undefined") return;
29
+ if (historyPatched) {
30
+ console.warn(
31
+ "[SunGlasses] useScreenTracking: history is already patched by another instance \u2014 this instance will not track."
32
+ );
33
+ return;
34
+ }
35
+ historyPatched = true;
36
+ const handleRouteChange = (path) => {
37
+ const name = mapperRef.current ? mapperRef.current(path) : path;
38
+ const props = {
39
+ $path: path,
40
+ $url: window.location.href
41
+ };
42
+ if (document.title) props.$title = document.title;
43
+ if (previousPath !== null) {
44
+ props.$referrer = previousPath;
45
+ } else if (document.referrer) {
46
+ props.$referrer = document.referrer;
47
+ }
48
+ previousPath = path;
49
+ clientRef.current.screen(name, props);
50
+ };
51
+ handleRouteChange(window.location.pathname);
52
+ const originalPush = history.pushState.bind(history);
53
+ history.pushState = (...args) => {
54
+ originalPush(...args);
55
+ handleRouteChange(window.location.pathname);
56
+ };
57
+ const originalReplace = history.replaceState.bind(history);
58
+ history.replaceState = (...args) => {
59
+ originalReplace(...args);
60
+ handleRouteChange(window.location.pathname);
61
+ };
62
+ const onPopState = () => handleRouteChange(window.location.pathname);
63
+ window.addEventListener("popstate", onPopState);
64
+ return () => {
65
+ history.pushState = originalPush;
66
+ history.replaceState = originalReplace;
67
+ window.removeEventListener("popstate", onPopState);
68
+ historyPatched = false;
69
+ previousPath = null;
70
+ };
71
+ }, [useHistoryApi]);
72
+ }
73
+
74
+ // src/SunglassesProvider.tsx
75
+ import { jsx } from "react/jsx-runtime";
76
+ function SunglassesProvider({
77
+ client,
78
+ screenTracking,
79
+ children
80
+ }) {
81
+ useEffect2(() => {
82
+ return () => {
83
+ client.shutdown().catch(() => {
84
+ });
85
+ };
86
+ }, [client]);
87
+ useEffect2(() => {
88
+ if (typeof document === "undefined") return;
89
+ const handleVisibilityChange = () => {
90
+ if (document.visibilityState === "hidden") {
91
+ client.flush().catch(() => {
92
+ });
93
+ }
94
+ };
95
+ document.addEventListener("visibilitychange", handleVisibilityChange);
96
+ return () => document.removeEventListener("visibilitychange", handleVisibilityChange);
97
+ }, [client]);
98
+ useScreenTracking(client, screenTracking ?? { useHistoryApi: false });
99
+ return /* @__PURE__ */ jsx(SunglassesContext.Provider, { value: client, children });
100
+ }
101
+
102
+ // src/captureUtmParams.ts
103
+ var UTM_PARAMS = [
104
+ "utm_source",
105
+ "utm_medium",
106
+ "utm_campaign",
107
+ "utm_content",
108
+ "utm_term"
109
+ ];
110
+ function captureUtmParams(client) {
111
+ if (typeof window === "undefined") return;
112
+ const params = {};
113
+ const searchParams = new URLSearchParams(window.location.search);
114
+ for (const key of UTM_PARAMS) {
115
+ const value = searchParams.get(key);
116
+ if (value) params[key] = value;
117
+ }
118
+ if (document.referrer) {
119
+ params["$referrer"] = document.referrer;
120
+ try {
121
+ params["$referring_domain"] = new URL(document.referrer).hostname;
122
+ } catch {
123
+ }
124
+ }
125
+ if (Object.keys(params).length > 0) {
126
+ client.register(params);
127
+ }
128
+ }
129
+
130
+ // src/useCapture.ts
131
+ import { useCallback } from "react";
132
+ function useCapture() {
133
+ const client = useSunglasses();
134
+ return useCallback(
135
+ (eventName, properties) => {
136
+ client.capture(eventName, properties);
137
+ },
138
+ [client]
139
+ );
140
+ }
141
+
142
+ // src/useConsentStatus.ts
143
+ function useConsentStatus() {
144
+ const client = useSunglasses();
145
+ return client.getConsentStatus();
146
+ }
147
+
148
+ // src/index.ts
149
+ import { SunglassesCore } from "@drakkar.software/sunglasses-core";
150
+ export {
151
+ SunglassesCore,
152
+ SunglassesProvider,
153
+ captureUtmParams,
154
+ useCapture,
155
+ useConsentStatus,
156
+ useScreenTracking,
157
+ useSunglasses
158
+ };
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@drakkar.software/sunglasses-react",
3
+ "version": "0.2.0",
4
+ "description": "React (web) provider and hooks for SunGlasses event tracking",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "dependencies": {
19
+ "@drakkar.software/sunglasses-core": "0.2.0"
20
+ },
21
+ "peerDependencies": {
22
+ "react": ">=18.0.0"
23
+ },
24
+ "devDependencies": {
25
+ "@types/react": "^18.3.12",
26
+ "happy-dom": "^15.11.7",
27
+ "react": "^18.3.1",
28
+ "react-dom": "^18.3.1",
29
+ "tsup": "^8.3.5",
30
+ "typescript": "^5.7.2",
31
+ "vitest": "^2.1.8",
32
+ "@drakkar.software/sunglasses-tsconfig": "0.1.0"
33
+ },
34
+ "scripts": {
35
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
36
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
37
+ "typecheck": "tsc --noEmit",
38
+ "lint": "eslint src/",
39
+ "test": "vitest run",
40
+ "clean": "rm -rf dist .tsbuildinfo"
41
+ }
42
+ }