@14four/analytics 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +73 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +146 -0
- package/dist/index.mjs +120 -0
- package/package.json +30 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/** Options for server-side (API key) mode. */
|
|
2
|
+
type ServerOptions = {
|
|
3
|
+
/** Server-side ingest API key (format: sk_live_…). */
|
|
4
|
+
apiKey: string;
|
|
5
|
+
/** Base URL of the ingest API, e.g. "https://api.example.com". Defaults to "" (same-origin). */
|
|
6
|
+
baseUrl?: string;
|
|
7
|
+
};
|
|
8
|
+
/** Options for browser (public tracking) mode. */
|
|
9
|
+
type BrowserOptions = {
|
|
10
|
+
/** Public application ID. */
|
|
11
|
+
appId: string;
|
|
12
|
+
/** Base URL of the ingest API, e.g. "https://api.example.com". Defaults to "" (same-origin). */
|
|
13
|
+
baseUrl?: string;
|
|
14
|
+
};
|
|
15
|
+
type AnalyticsOptions = ServerOptions | BrowserOptions;
|
|
16
|
+
/**
|
|
17
|
+
* Payload for server-side event tracking.
|
|
18
|
+
* `user_id` is required and used directly as the visitor identifier (no hashing).
|
|
19
|
+
* Any additional top-level fields become individual Parquet columns.
|
|
20
|
+
*/
|
|
21
|
+
type ServerTrackOptions = {
|
|
22
|
+
user_id: string;
|
|
23
|
+
url?: string;
|
|
24
|
+
app_id?: string;
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Payload for browser (public) event tracking.
|
|
29
|
+
* `url`, `referrer`, `screen_width`, and `language` are auto-collected from the
|
|
30
|
+
* browser environment when omitted. UTM params are parsed from the current URL.
|
|
31
|
+
*/
|
|
32
|
+
type WebTrackOptions = {
|
|
33
|
+
url?: string;
|
|
34
|
+
referrer?: string;
|
|
35
|
+
screen_width?: number;
|
|
36
|
+
language?: string;
|
|
37
|
+
utm_source?: string;
|
|
38
|
+
utm_medium?: string;
|
|
39
|
+
utm_campaign?: string;
|
|
40
|
+
utm_term?: string;
|
|
41
|
+
utm_content?: string;
|
|
42
|
+
/** Custom properties. Max 20 keys; keys ≤50 chars; values ≤500 chars. */
|
|
43
|
+
props?: Record<string, string>;
|
|
44
|
+
};
|
|
45
|
+
declare class AnalyticsError extends Error {
|
|
46
|
+
readonly status: number;
|
|
47
|
+
constructor(status: number, message: string);
|
|
48
|
+
}
|
|
49
|
+
declare class Analytics {
|
|
50
|
+
private readonly baseUrl;
|
|
51
|
+
private readonly mode;
|
|
52
|
+
private readonly apiKey?;
|
|
53
|
+
private readonly appId?;
|
|
54
|
+
constructor(options: AnalyticsOptions);
|
|
55
|
+
/**
|
|
56
|
+
* Track an event.
|
|
57
|
+
*
|
|
58
|
+
* **Server mode** — requires `{ user_id }`. Any extra fields become Parquet columns.
|
|
59
|
+
* ```ts
|
|
60
|
+
* await analytics.track('signup', { user_id: 'u_123', plan: 'pro' })
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* **Browser mode** — auto-collects url/referrer/screen/language/utm from the browser.
|
|
64
|
+
* ```ts
|
|
65
|
+
* analytics.track('page_view').catch(() => {})
|
|
66
|
+
* analytics.track('cta_click', { props: { button: 'hero' } })
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
track(eventName: string, options?: ServerTrackOptions | WebTrackOptions): Promise<void>;
|
|
70
|
+
private _post;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export { Analytics, AnalyticsError, type AnalyticsOptions, type BrowserOptions, type ServerOptions, type ServerTrackOptions, type WebTrackOptions, Analytics as default };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/** Options for server-side (API key) mode. */
|
|
2
|
+
type ServerOptions = {
|
|
3
|
+
/** Server-side ingest API key (format: sk_live_…). */
|
|
4
|
+
apiKey: string;
|
|
5
|
+
/** Base URL of the ingest API, e.g. "https://api.example.com". Defaults to "" (same-origin). */
|
|
6
|
+
baseUrl?: string;
|
|
7
|
+
};
|
|
8
|
+
/** Options for browser (public tracking) mode. */
|
|
9
|
+
type BrowserOptions = {
|
|
10
|
+
/** Public application ID. */
|
|
11
|
+
appId: string;
|
|
12
|
+
/** Base URL of the ingest API, e.g. "https://api.example.com". Defaults to "" (same-origin). */
|
|
13
|
+
baseUrl?: string;
|
|
14
|
+
};
|
|
15
|
+
type AnalyticsOptions = ServerOptions | BrowserOptions;
|
|
16
|
+
/**
|
|
17
|
+
* Payload for server-side event tracking.
|
|
18
|
+
* `user_id` is required and used directly as the visitor identifier (no hashing).
|
|
19
|
+
* Any additional top-level fields become individual Parquet columns.
|
|
20
|
+
*/
|
|
21
|
+
type ServerTrackOptions = {
|
|
22
|
+
user_id: string;
|
|
23
|
+
url?: string;
|
|
24
|
+
app_id?: string;
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Payload for browser (public) event tracking.
|
|
29
|
+
* `url`, `referrer`, `screen_width`, and `language` are auto-collected from the
|
|
30
|
+
* browser environment when omitted. UTM params are parsed from the current URL.
|
|
31
|
+
*/
|
|
32
|
+
type WebTrackOptions = {
|
|
33
|
+
url?: string;
|
|
34
|
+
referrer?: string;
|
|
35
|
+
screen_width?: number;
|
|
36
|
+
language?: string;
|
|
37
|
+
utm_source?: string;
|
|
38
|
+
utm_medium?: string;
|
|
39
|
+
utm_campaign?: string;
|
|
40
|
+
utm_term?: string;
|
|
41
|
+
utm_content?: string;
|
|
42
|
+
/** Custom properties. Max 20 keys; keys ≤50 chars; values ≤500 chars. */
|
|
43
|
+
props?: Record<string, string>;
|
|
44
|
+
};
|
|
45
|
+
declare class AnalyticsError extends Error {
|
|
46
|
+
readonly status: number;
|
|
47
|
+
constructor(status: number, message: string);
|
|
48
|
+
}
|
|
49
|
+
declare class Analytics {
|
|
50
|
+
private readonly baseUrl;
|
|
51
|
+
private readonly mode;
|
|
52
|
+
private readonly apiKey?;
|
|
53
|
+
private readonly appId?;
|
|
54
|
+
constructor(options: AnalyticsOptions);
|
|
55
|
+
/**
|
|
56
|
+
* Track an event.
|
|
57
|
+
*
|
|
58
|
+
* **Server mode** — requires `{ user_id }`. Any extra fields become Parquet columns.
|
|
59
|
+
* ```ts
|
|
60
|
+
* await analytics.track('signup', { user_id: 'u_123', plan: 'pro' })
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* **Browser mode** — auto-collects url/referrer/screen/language/utm from the browser.
|
|
64
|
+
* ```ts
|
|
65
|
+
* analytics.track('page_view').catch(() => {})
|
|
66
|
+
* analytics.track('cta_click', { props: { button: 'hero' } })
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
track(eventName: string, options?: ServerTrackOptions | WebTrackOptions): Promise<void>;
|
|
70
|
+
private _post;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export { Analytics, AnalyticsError, type AnalyticsOptions, type BrowserOptions, type ServerOptions, type ServerTrackOptions, type WebTrackOptions, Analytics as default };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
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
|
+
Analytics: () => Analytics,
|
|
24
|
+
AnalyticsError: () => AnalyticsError,
|
|
25
|
+
default: () => index_default
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(index_exports);
|
|
28
|
+
var AnalyticsError = class extends Error {
|
|
29
|
+
constructor(status, message) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.status = status;
|
|
32
|
+
this.name = "AnalyticsError";
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
function isBrowser() {
|
|
36
|
+
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
37
|
+
}
|
|
38
|
+
function isServerOptions(opts) {
|
|
39
|
+
return "apiKey" in opts;
|
|
40
|
+
}
|
|
41
|
+
function extractUtm(search) {
|
|
42
|
+
const p = new URLSearchParams(search);
|
|
43
|
+
const result = {};
|
|
44
|
+
const keys = [
|
|
45
|
+
"utm_source",
|
|
46
|
+
"utm_medium",
|
|
47
|
+
"utm_campaign",
|
|
48
|
+
"utm_term",
|
|
49
|
+
"utm_content"
|
|
50
|
+
];
|
|
51
|
+
for (const key of keys) {
|
|
52
|
+
const val = p.get(key);
|
|
53
|
+
if (val) result[key] = val;
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
var Analytics = class {
|
|
58
|
+
constructor(options) {
|
|
59
|
+
this.baseUrl = options.baseUrl?.replace(/\/$/, "") ?? "";
|
|
60
|
+
if (isServerOptions(options)) {
|
|
61
|
+
this.mode = "server";
|
|
62
|
+
this.apiKey = options.apiKey;
|
|
63
|
+
} else {
|
|
64
|
+
this.mode = "browser";
|
|
65
|
+
this.appId = options.appId;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Track an event.
|
|
70
|
+
*
|
|
71
|
+
* **Server mode** — requires `{ user_id }`. Any extra fields become Parquet columns.
|
|
72
|
+
* ```ts
|
|
73
|
+
* await analytics.track('signup', { user_id: 'u_123', plan: 'pro' })
|
|
74
|
+
* ```
|
|
75
|
+
*
|
|
76
|
+
* **Browser mode** — auto-collects url/referrer/screen/language/utm from the browser.
|
|
77
|
+
* ```ts
|
|
78
|
+
* analytics.track('page_view').catch(() => {})
|
|
79
|
+
* analytics.track('cta_click', { props: { button: 'hero' } })
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
async track(eventName, options) {
|
|
83
|
+
const endpoint = `${this.baseUrl}/api/ingest/event`;
|
|
84
|
+
if (this.mode === "server") {
|
|
85
|
+
const { user_id, url, app_id, ...customProps } = options ?? {};
|
|
86
|
+
const body = {
|
|
87
|
+
event_name: eventName,
|
|
88
|
+
user_id,
|
|
89
|
+
...customProps
|
|
90
|
+
};
|
|
91
|
+
if (url !== void 0) body.url = url;
|
|
92
|
+
if (app_id !== void 0) body.app_id = app_id;
|
|
93
|
+
await this._post(endpoint, body, {
|
|
94
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
95
|
+
});
|
|
96
|
+
} else {
|
|
97
|
+
const opts = options ?? {};
|
|
98
|
+
const autoUrl = isBrowser() ? window.location.href : void 0;
|
|
99
|
+
const autoReferrer = isBrowser() ? document.referrer : void 0;
|
|
100
|
+
const autoScreenWidth = isBrowser() ? window.screen.width : void 0;
|
|
101
|
+
const autoLanguage = isBrowser() ? navigator.language : void 0;
|
|
102
|
+
const autoUtm = isBrowser() ? extractUtm(window.location.search) : {};
|
|
103
|
+
const body = {
|
|
104
|
+
app_id: this.appId,
|
|
105
|
+
event_name: eventName,
|
|
106
|
+
url: opts.url ?? autoUrl,
|
|
107
|
+
...opts.referrer !== void 0 ? { referrer: opts.referrer } : autoReferrer ? { referrer: autoReferrer } : {},
|
|
108
|
+
...opts.screen_width !== void 0 ? { screen_width: opts.screen_width } : autoScreenWidth !== void 0 ? { screen_width: autoScreenWidth } : {},
|
|
109
|
+
...opts.language !== void 0 ? { language: opts.language } : autoLanguage ? { language: autoLanguage } : {},
|
|
110
|
+
...autoUtm,
|
|
111
|
+
...opts.utm_source !== void 0 && { utm_source: opts.utm_source },
|
|
112
|
+
...opts.utm_medium !== void 0 && { utm_medium: opts.utm_medium },
|
|
113
|
+
...opts.utm_campaign !== void 0 && { utm_campaign: opts.utm_campaign },
|
|
114
|
+
...opts.utm_term !== void 0 && { utm_term: opts.utm_term },
|
|
115
|
+
...opts.utm_content !== void 0 && { utm_content: opts.utm_content },
|
|
116
|
+
...opts.props !== void 0 && { props: opts.props }
|
|
117
|
+
};
|
|
118
|
+
await this._post(endpoint, body, {});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async _post(url, body, headers) {
|
|
122
|
+
const res = await fetch(url, {
|
|
123
|
+
method: "POST",
|
|
124
|
+
headers: {
|
|
125
|
+
"Content-Type": "application/json",
|
|
126
|
+
...headers
|
|
127
|
+
},
|
|
128
|
+
body: JSON.stringify(body)
|
|
129
|
+
});
|
|
130
|
+
if (!res.ok) {
|
|
131
|
+
let message = res.statusText;
|
|
132
|
+
try {
|
|
133
|
+
const data = await res.json();
|
|
134
|
+
message = data.error ?? data.message ?? message;
|
|
135
|
+
} catch {
|
|
136
|
+
}
|
|
137
|
+
throw new AnalyticsError(res.status, message);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
var index_default = Analytics;
|
|
142
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
143
|
+
0 && (module.exports = {
|
|
144
|
+
Analytics,
|
|
145
|
+
AnalyticsError
|
|
146
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var AnalyticsError = class extends Error {
|
|
3
|
+
constructor(status, message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.status = status;
|
|
6
|
+
this.name = "AnalyticsError";
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
function isBrowser() {
|
|
10
|
+
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
11
|
+
}
|
|
12
|
+
function isServerOptions(opts) {
|
|
13
|
+
return "apiKey" in opts;
|
|
14
|
+
}
|
|
15
|
+
function extractUtm(search) {
|
|
16
|
+
const p = new URLSearchParams(search);
|
|
17
|
+
const result = {};
|
|
18
|
+
const keys = [
|
|
19
|
+
"utm_source",
|
|
20
|
+
"utm_medium",
|
|
21
|
+
"utm_campaign",
|
|
22
|
+
"utm_term",
|
|
23
|
+
"utm_content"
|
|
24
|
+
];
|
|
25
|
+
for (const key of keys) {
|
|
26
|
+
const val = p.get(key);
|
|
27
|
+
if (val) result[key] = val;
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
var Analytics = class {
|
|
32
|
+
constructor(options) {
|
|
33
|
+
this.baseUrl = options.baseUrl?.replace(/\/$/, "") ?? "";
|
|
34
|
+
if (isServerOptions(options)) {
|
|
35
|
+
this.mode = "server";
|
|
36
|
+
this.apiKey = options.apiKey;
|
|
37
|
+
} else {
|
|
38
|
+
this.mode = "browser";
|
|
39
|
+
this.appId = options.appId;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Track an event.
|
|
44
|
+
*
|
|
45
|
+
* **Server mode** — requires `{ user_id }`. Any extra fields become Parquet columns.
|
|
46
|
+
* ```ts
|
|
47
|
+
* await analytics.track('signup', { user_id: 'u_123', plan: 'pro' })
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* **Browser mode** — auto-collects url/referrer/screen/language/utm from the browser.
|
|
51
|
+
* ```ts
|
|
52
|
+
* analytics.track('page_view').catch(() => {})
|
|
53
|
+
* analytics.track('cta_click', { props: { button: 'hero' } })
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
async track(eventName, options) {
|
|
57
|
+
const endpoint = `${this.baseUrl}/api/ingest/event`;
|
|
58
|
+
if (this.mode === "server") {
|
|
59
|
+
const { user_id, url, app_id, ...customProps } = options ?? {};
|
|
60
|
+
const body = {
|
|
61
|
+
event_name: eventName,
|
|
62
|
+
user_id,
|
|
63
|
+
...customProps
|
|
64
|
+
};
|
|
65
|
+
if (url !== void 0) body.url = url;
|
|
66
|
+
if (app_id !== void 0) body.app_id = app_id;
|
|
67
|
+
await this._post(endpoint, body, {
|
|
68
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
69
|
+
});
|
|
70
|
+
} else {
|
|
71
|
+
const opts = options ?? {};
|
|
72
|
+
const autoUrl = isBrowser() ? window.location.href : void 0;
|
|
73
|
+
const autoReferrer = isBrowser() ? document.referrer : void 0;
|
|
74
|
+
const autoScreenWidth = isBrowser() ? window.screen.width : void 0;
|
|
75
|
+
const autoLanguage = isBrowser() ? navigator.language : void 0;
|
|
76
|
+
const autoUtm = isBrowser() ? extractUtm(window.location.search) : {};
|
|
77
|
+
const body = {
|
|
78
|
+
app_id: this.appId,
|
|
79
|
+
event_name: eventName,
|
|
80
|
+
url: opts.url ?? autoUrl,
|
|
81
|
+
...opts.referrer !== void 0 ? { referrer: opts.referrer } : autoReferrer ? { referrer: autoReferrer } : {},
|
|
82
|
+
...opts.screen_width !== void 0 ? { screen_width: opts.screen_width } : autoScreenWidth !== void 0 ? { screen_width: autoScreenWidth } : {},
|
|
83
|
+
...opts.language !== void 0 ? { language: opts.language } : autoLanguage ? { language: autoLanguage } : {},
|
|
84
|
+
...autoUtm,
|
|
85
|
+
...opts.utm_source !== void 0 && { utm_source: opts.utm_source },
|
|
86
|
+
...opts.utm_medium !== void 0 && { utm_medium: opts.utm_medium },
|
|
87
|
+
...opts.utm_campaign !== void 0 && { utm_campaign: opts.utm_campaign },
|
|
88
|
+
...opts.utm_term !== void 0 && { utm_term: opts.utm_term },
|
|
89
|
+
...opts.utm_content !== void 0 && { utm_content: opts.utm_content },
|
|
90
|
+
...opts.props !== void 0 && { props: opts.props }
|
|
91
|
+
};
|
|
92
|
+
await this._post(endpoint, body, {});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async _post(url, body, headers) {
|
|
96
|
+
const res = await fetch(url, {
|
|
97
|
+
method: "POST",
|
|
98
|
+
headers: {
|
|
99
|
+
"Content-Type": "application/json",
|
|
100
|
+
...headers
|
|
101
|
+
},
|
|
102
|
+
body: JSON.stringify(body)
|
|
103
|
+
});
|
|
104
|
+
if (!res.ok) {
|
|
105
|
+
let message = res.statusText;
|
|
106
|
+
try {
|
|
107
|
+
const data = await res.json();
|
|
108
|
+
message = data.error ?? data.message ?? message;
|
|
109
|
+
} catch {
|
|
110
|
+
}
|
|
111
|
+
throw new AnalyticsError(res.status, message);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
var index_default = Analytics;
|
|
116
|
+
export {
|
|
117
|
+
Analytics,
|
|
118
|
+
AnalyticsError,
|
|
119
|
+
index_default as default
|
|
120
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@14four/analytics",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "14four Analytics SDK — works in Node.js and browser",
|
|
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
|
+
"scripts": {
|
|
19
|
+
"build": "tsup",
|
|
20
|
+
"prepare": "tsup",
|
|
21
|
+
"release": "npm run build && npm publish --access public"
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"tsup": "^8.0.0",
|
|
28
|
+
"typescript": "^5.0.0"
|
|
29
|
+
}
|
|
30
|
+
}
|