Package not found. Please check the package name and try again.
@wool-so/node 0.3.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/package.json +28 -0
- package/src/index.d.ts +195 -0
- package/src/index.js +404 -0
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wool-so/node",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Server-side analytics SDK for Wool.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"main": "./src/index.js",
|
|
8
|
+
"types": "./src/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./src/index.d.ts",
|
|
12
|
+
"import": "./src/index.js",
|
|
13
|
+
"default": "./src/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"src"
|
|
18
|
+
],
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public",
|
|
22
|
+
"registry": "https://registry.npmjs.org/"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "node --check src/index.js",
|
|
26
|
+
"test": "node --test test/*.test.js"
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
export const VERSION: '0.3.0'
|
|
2
|
+
export const DEFAULT_ENDPOINT: 'https://wool-api.gurjas1882.workers.dev/v1/events'
|
|
3
|
+
|
|
4
|
+
export type WoolPropertyValue = string | number | boolean | null | undefined
|
|
5
|
+
export type WoolProperties = Record<string, WoolPropertyValue>
|
|
6
|
+
|
|
7
|
+
export interface WoolFetchResponse {
|
|
8
|
+
ok: boolean
|
|
9
|
+
status: number
|
|
10
|
+
text?: () => Promise<string>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type WoolFetch = (
|
|
14
|
+
input: string,
|
|
15
|
+
init: {
|
|
16
|
+
method: 'POST'
|
|
17
|
+
headers: Record<string, string>
|
|
18
|
+
body: string
|
|
19
|
+
}
|
|
20
|
+
) => Promise<WoolFetchResponse>
|
|
21
|
+
|
|
22
|
+
export interface WoolBaseOptions {
|
|
23
|
+
endpoint?: string
|
|
24
|
+
apiUrl?: string
|
|
25
|
+
apiHost?: string
|
|
26
|
+
fetch?: WoolFetch
|
|
27
|
+
enabled?: boolean
|
|
28
|
+
referrer?: string
|
|
29
|
+
referer?: string
|
|
30
|
+
retentionId?: string
|
|
31
|
+
retention_id?: string
|
|
32
|
+
subjectKey?: string
|
|
33
|
+
subject_key?: string
|
|
34
|
+
groupKey?: string
|
|
35
|
+
group_key?: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type WoolInitOptions = WoolBaseOptions &
|
|
39
|
+
(
|
|
40
|
+
| {
|
|
41
|
+
projectId: string
|
|
42
|
+
project_id?: string
|
|
43
|
+
}
|
|
44
|
+
| {
|
|
45
|
+
projectId?: string
|
|
46
|
+
project_id: string
|
|
47
|
+
}
|
|
48
|
+
) &
|
|
49
|
+
(
|
|
50
|
+
| {
|
|
51
|
+
apiKey: string
|
|
52
|
+
api_key?: string
|
|
53
|
+
}
|
|
54
|
+
| {
|
|
55
|
+
apiKey?: string
|
|
56
|
+
api_key: string
|
|
57
|
+
}
|
|
58
|
+
) &
|
|
59
|
+
(
|
|
60
|
+
| {
|
|
61
|
+
siteUrl: string
|
|
62
|
+
site_url?: string
|
|
63
|
+
url?: string
|
|
64
|
+
}
|
|
65
|
+
| {
|
|
66
|
+
siteUrl?: string
|
|
67
|
+
site_url: string
|
|
68
|
+
url?: string
|
|
69
|
+
}
|
|
70
|
+
| {
|
|
71
|
+
siteUrl?: string
|
|
72
|
+
site_url?: string
|
|
73
|
+
url: string
|
|
74
|
+
}
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
export interface WoolIdentifyOptions {
|
|
78
|
+
groupKey?: string
|
|
79
|
+
group_key?: string
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface WoolRequestLike {
|
|
83
|
+
url?: string
|
|
84
|
+
originalUrl?: string
|
|
85
|
+
path?: string
|
|
86
|
+
protocol?: string
|
|
87
|
+
hostname?: string
|
|
88
|
+
headers?: Headers | Record<string, string | string[] | undefined>
|
|
89
|
+
socket?: {
|
|
90
|
+
encrypted?: boolean
|
|
91
|
+
}
|
|
92
|
+
connection?: {
|
|
93
|
+
encrypted?: boolean
|
|
94
|
+
}
|
|
95
|
+
nextUrl?: {
|
|
96
|
+
pathname?: string
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface WoolRequestContextOptions {
|
|
101
|
+
url?: string
|
|
102
|
+
referrer?: string
|
|
103
|
+
referer?: string
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface WoolRequestContext {
|
|
107
|
+
url: string
|
|
108
|
+
referrer: string
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface WoolTrackOptions extends WoolRequestContextOptions {
|
|
112
|
+
request?: Request | WoolRequestLike
|
|
113
|
+
req?: Request | WoolRequestLike
|
|
114
|
+
source?: string
|
|
115
|
+
eventId?: string
|
|
116
|
+
id?: string
|
|
117
|
+
visitorKey?: string
|
|
118
|
+
visitor_key?: string
|
|
119
|
+
value?: number | string
|
|
120
|
+
currency?: string
|
|
121
|
+
duration?: number | string
|
|
122
|
+
nonInteractive?: boolean
|
|
123
|
+
non_interactive?: boolean
|
|
124
|
+
retentionId?: string
|
|
125
|
+
retention_id?: string
|
|
126
|
+
subjectKey?: string
|
|
127
|
+
subject_key?: string
|
|
128
|
+
anonymousId?: string
|
|
129
|
+
anonymous_id?: string
|
|
130
|
+
groupKey?: string
|
|
131
|
+
group_key?: string
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface WoolPageviewOptions extends WoolRequestContextOptions {
|
|
135
|
+
request?: Request | WoolRequestLike
|
|
136
|
+
req?: Request | WoolRequestLike
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface WoolResult {
|
|
140
|
+
ok: boolean
|
|
141
|
+
status: number
|
|
142
|
+
skipped?: boolean
|
|
143
|
+
response?: WoolFetchResponse
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface WoolClient {
|
|
147
|
+
identify(retentionId: string, options?: WoolIdentifyOptions): void
|
|
148
|
+
reset(): void
|
|
149
|
+
track(name: string, properties?: WoolProperties, options?: WoolTrackOptions): Promise<WoolResult>
|
|
150
|
+
pageview(options?: WoolPageviewOptions): Promise<WoolResult>
|
|
151
|
+
requestContext(input?: Request | WoolRequestLike | string, options?: WoolRequestContextOptions): WoolRequestContext
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export class WoolError extends Error {
|
|
155
|
+
status: number
|
|
156
|
+
response: WoolFetchResponse | null
|
|
157
|
+
constructor(message: string, status?: number, response?: WoolFetchResponse | null)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function createClient(options: WoolInitOptions): WoolClient
|
|
161
|
+
export function init(options: WoolInitOptions): WoolClient
|
|
162
|
+
export function identify(retentionId: string, options?: WoolIdentifyOptions): void
|
|
163
|
+
export function reset(): void
|
|
164
|
+
export function track(name: string, properties?: WoolProperties, options?: WoolTrackOptions): Promise<WoolResult>
|
|
165
|
+
export function pageview(options?: WoolPageviewOptions): Promise<WoolResult>
|
|
166
|
+
export function requestContext(
|
|
167
|
+
input?: Request | WoolRequestLike | string,
|
|
168
|
+
options?: WoolRequestContextOptions
|
|
169
|
+
): WoolRequestContext
|
|
170
|
+
|
|
171
|
+
export type ExpressMiddleware = (req: WoolRequestLike & { wool?: WoolClient }, res: unknown, next?: () => void) => void
|
|
172
|
+
export type NestMiddleware = ExpressMiddleware
|
|
173
|
+
export type FastifyPlugin = (
|
|
174
|
+
fastify: { decorate?: (name: string, value: WoolClient) => void; wool?: WoolClient },
|
|
175
|
+
options?: unknown,
|
|
176
|
+
done?: () => void
|
|
177
|
+
) => void
|
|
178
|
+
export type HonoMiddleware = (context: { set?: (name: string, value: WoolClient) => void; wool?: WoolClient }, next?: () => Promise<void> | void) => Promise<void>
|
|
179
|
+
|
|
180
|
+
export function expressMiddleware(options: WoolInitOptions | WoolClient): ExpressMiddleware
|
|
181
|
+
export function nestMiddleware(options: WoolInitOptions | WoolClient): NestMiddleware
|
|
182
|
+
export function fastifyPlugin(options: WoolInitOptions | WoolClient): FastifyPlugin
|
|
183
|
+
export function honoMiddleware(options: WoolInitOptions | WoolClient): HonoMiddleware
|
|
184
|
+
|
|
185
|
+
export const wool: WoolClient & {
|
|
186
|
+
init: typeof init
|
|
187
|
+
createClient: typeof createClient
|
|
188
|
+
requestContext: typeof requestContext
|
|
189
|
+
expressMiddleware: typeof expressMiddleware
|
|
190
|
+
fastifyPlugin: typeof fastifyPlugin
|
|
191
|
+
honoMiddleware: typeof honoMiddleware
|
|
192
|
+
nestMiddleware: typeof nestMiddleware
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export default wool
|
package/src/index.js
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
export const VERSION = '0.3.0'
|
|
2
|
+
export const DEFAULT_ENDPOINT = 'https://wool-api.gurjas1882.workers.dev/v1/events'
|
|
3
|
+
|
|
4
|
+
const IDENTITY_KEY_PATTERN =
|
|
5
|
+
/^(anonymousId|anonymous_id|retentionId|retention_id|subjectKey|subject_key|groupKey|group_key)$/i
|
|
6
|
+
const TRACK_OPTION_KEYS = new Set([
|
|
7
|
+
'source',
|
|
8
|
+
'eventId',
|
|
9
|
+
'id',
|
|
10
|
+
'visitorKey',
|
|
11
|
+
'visitor_key',
|
|
12
|
+
'value',
|
|
13
|
+
'currency',
|
|
14
|
+
'duration',
|
|
15
|
+
'nonInteractive',
|
|
16
|
+
'non_interactive',
|
|
17
|
+
'retentionId',
|
|
18
|
+
'retention_id',
|
|
19
|
+
'subjectKey',
|
|
20
|
+
'subject_key',
|
|
21
|
+
'anonymousId',
|
|
22
|
+
'anonymous_id',
|
|
23
|
+
'groupKey',
|
|
24
|
+
'group_key',
|
|
25
|
+
'url',
|
|
26
|
+
'referrer',
|
|
27
|
+
'referer',
|
|
28
|
+
'request',
|
|
29
|
+
'req'
|
|
30
|
+
])
|
|
31
|
+
|
|
32
|
+
export class WoolError extends Error {
|
|
33
|
+
constructor(message, status = 0, response = null) {
|
|
34
|
+
super(message)
|
|
35
|
+
this.name = 'WoolError'
|
|
36
|
+
this.status = status
|
|
37
|
+
this.response = response
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function cleanText(value, maxLength = 128) {
|
|
42
|
+
const text = String(value || '').trim()
|
|
43
|
+
return text.length > maxLength ? text.slice(0, maxLength) : text
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function endpointFromApiUrl(value) {
|
|
47
|
+
const text = cleanText(value, 2048)
|
|
48
|
+
if (!text) return ''
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
return new URL('/v1/events', text).toString()
|
|
52
|
+
} catch {
|
|
53
|
+
return ''
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function resolveEndpoint(options) {
|
|
58
|
+
return cleanText(options.endpoint, 2048) || endpointFromApiUrl(options.apiUrl || options.apiHost) || DEFAULT_ENDPOINT
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function isPlainObject(value) {
|
|
62
|
+
return Boolean(value && typeof value === 'object' && !Array.isArray(value))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function cleanProperties(properties) {
|
|
66
|
+
if (!isPlainObject(properties)) return {}
|
|
67
|
+
|
|
68
|
+
const clean = {}
|
|
69
|
+
Object.keys(properties)
|
|
70
|
+
.filter((key) => !IDENTITY_KEY_PATTERN.test(key))
|
|
71
|
+
.slice(0, 12)
|
|
72
|
+
.forEach((key) => {
|
|
73
|
+
const value = properties[key]
|
|
74
|
+
if (value === undefined || value === null) return
|
|
75
|
+
if (typeof value === 'object') return
|
|
76
|
+
clean[key] = value
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
return clean
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function identityValueFrom(source, keys) {
|
|
83
|
+
if (!isPlainObject(source)) return ''
|
|
84
|
+
|
|
85
|
+
for (const key of keys) {
|
|
86
|
+
const value = source[key]
|
|
87
|
+
if (value !== undefined && value !== null && String(value).trim()) return value
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return ''
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function applyIdentity(payload, state, properties, options) {
|
|
94
|
+
const retentionId =
|
|
95
|
+
identityValueFrom(options, ['retentionId', 'retention_id', 'subjectKey', 'subject_key', 'anonymousId', 'anonymous_id']) ||
|
|
96
|
+
identityValueFrom(properties, ['retentionId', 'retention_id', 'subjectKey', 'subject_key', 'anonymousId', 'anonymous_id']) ||
|
|
97
|
+
state.identity.retentionId
|
|
98
|
+
const groupKey =
|
|
99
|
+
identityValueFrom(options, ['groupKey', 'group_key']) ||
|
|
100
|
+
identityValueFrom(properties, ['groupKey', 'group_key']) ||
|
|
101
|
+
state.identity.groupKey
|
|
102
|
+
|
|
103
|
+
if (retentionId) payload.retentionId = cleanText(retentionId)
|
|
104
|
+
if (groupKey) payload.groupKey = cleanText(groupKey)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function createEventId() {
|
|
108
|
+
const crypto = globalThis.crypto
|
|
109
|
+
if (crypto?.randomUUID) return crypto.randomUUID()
|
|
110
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2)}`
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function headersFrom(value) {
|
|
114
|
+
if (!value) return null
|
|
115
|
+
|
|
116
|
+
if (value instanceof Headers) return value
|
|
117
|
+
if (value.headers instanceof Headers) return value.headers
|
|
118
|
+
if (value.raw instanceof Headers) return value.raw
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
get(name) {
|
|
122
|
+
const lower = String(name).toLowerCase()
|
|
123
|
+
const headers = value.headers || value
|
|
124
|
+
const direct = headers[name] ?? headers[lower]
|
|
125
|
+
if (Array.isArray(direct)) return direct.join(', ')
|
|
126
|
+
return direct === undefined || direct === null ? '' : String(direct)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function headerValue(headers, names) {
|
|
132
|
+
if (!headers) return ''
|
|
133
|
+
|
|
134
|
+
for (const name of names) {
|
|
135
|
+
const value = headers.get?.(name)
|
|
136
|
+
if (value) return String(value)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return ''
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function hostFromRequest(input, headers) {
|
|
143
|
+
const host = headerValue(headers, ['x-forwarded-host', 'host'])
|
|
144
|
+
if (host) return host.split(',')[0].trim()
|
|
145
|
+
|
|
146
|
+
const hostname = input?.hostname || input?.headers?.host || ''
|
|
147
|
+
return Array.isArray(hostname) ? hostname[0] || '' : String(hostname || '')
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function protocolFromRequest(input, headers) {
|
|
151
|
+
const protocol = headerValue(headers, ['x-forwarded-proto'])
|
|
152
|
+
if (protocol) return protocol.split(',')[0].trim()
|
|
153
|
+
|
|
154
|
+
if (input?.protocol) return String(input.protocol).replace(/:$/, '')
|
|
155
|
+
if (input?.socket?.encrypted || input?.connection?.encrypted) return 'https'
|
|
156
|
+
return 'https'
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function urlFromRequest(input, headers) {
|
|
160
|
+
if (!input) return ''
|
|
161
|
+
if (typeof input === 'string') return input
|
|
162
|
+
if (input.url && /^https?:\/\//i.test(String(input.url))) return String(input.url)
|
|
163
|
+
|
|
164
|
+
const path = input.originalUrl || input.url || input.path || input.nextUrl?.pathname || ''
|
|
165
|
+
if (!path) return ''
|
|
166
|
+
|
|
167
|
+
const host = hostFromRequest(input, headers)
|
|
168
|
+
if (!host) return ''
|
|
169
|
+
|
|
170
|
+
return `${protocolFromRequest(input, headers)}://${host}${path.startsWith('/') ? path : `/${path}`}`
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function requestContext(input = {}, options = {}) {
|
|
174
|
+
const headers = headersFrom(input)
|
|
175
|
+
const url = cleanText(options.url, 2048) || urlFromRequest(input, headers)
|
|
176
|
+
const referrer =
|
|
177
|
+
cleanText(options.referrer || options.referer, 2048) || headerValue(headers, ['referer', 'referrer']) || ''
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
url,
|
|
181
|
+
referrer
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function trackOptionsFrom(options) {
|
|
186
|
+
if (!isPlainObject(options)) return {}
|
|
187
|
+
|
|
188
|
+
const clean = {}
|
|
189
|
+
for (const key of TRACK_OPTION_KEYS) {
|
|
190
|
+
if (options[key] !== undefined) clean[key] = options[key]
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return clean
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function resolveFetch(options) {
|
|
197
|
+
const fetcher = options.fetch || globalThis.fetch
|
|
198
|
+
if (typeof fetcher !== 'function') {
|
|
199
|
+
throw new WoolError('A fetch implementation is required.')
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return fetcher
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function createState(options) {
|
|
206
|
+
const projectId = cleanText(options.projectId || options.project_id, 96)
|
|
207
|
+
const apiKey = cleanText(options.apiKey || options.api_key, 4096)
|
|
208
|
+
const siteUrl = cleanText(options.siteUrl || options.site_url || options.url, 2048)
|
|
209
|
+
|
|
210
|
+
if (!projectId) throw new WoolError('projectId is required.')
|
|
211
|
+
if (!apiKey) throw new WoolError('apiKey is required.')
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
projectId,
|
|
215
|
+
apiKey,
|
|
216
|
+
endpoint: resolveEndpoint(options),
|
|
217
|
+
defaultUrl: siteUrl,
|
|
218
|
+
defaultReferrer: cleanText(options.referrer || options.referer, 2048),
|
|
219
|
+
enabled: options.enabled !== false,
|
|
220
|
+
fetch: resolveFetch(options),
|
|
221
|
+
identity: {
|
|
222
|
+
retentionId: cleanText(options.retentionId || options.retention_id || options.subjectKey || options.subject_key),
|
|
223
|
+
groupKey: cleanText(options.groupKey || options.group_key)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function bodyFromResponse(response) {
|
|
229
|
+
return response?.text ? response.text().catch(() => '') : Promise.resolve('')
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function createClient(options = {}) {
|
|
233
|
+
const state = createState(options)
|
|
234
|
+
|
|
235
|
+
async function send(payload) {
|
|
236
|
+
if (!state.enabled) {
|
|
237
|
+
return { ok: true, status: 0, skipped: true }
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const response = await state.fetch(state.endpoint, {
|
|
241
|
+
method: 'POST',
|
|
242
|
+
headers: {
|
|
243
|
+
Authorization: `Bearer ${state.apiKey}`,
|
|
244
|
+
'Content-Type': 'application/json',
|
|
245
|
+
'X-Wool-SDK': `@wool-so/node/${VERSION}`
|
|
246
|
+
},
|
|
247
|
+
body: JSON.stringify(payload)
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
if (!response?.ok && response?.status !== 204) {
|
|
251
|
+
const body = await bodyFromResponse(response)
|
|
252
|
+
throw new WoolError(body || 'Unable to send Wool analytics event.', response?.status || 0, response || null)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return { ok: true, status: response?.status || 0, response }
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function basePayload(type, options = {}) {
|
|
259
|
+
const context = requestContext(options.request || options.req, options)
|
|
260
|
+
const url = cleanText(context.url || state.defaultUrl, 2048)
|
|
261
|
+
|
|
262
|
+
if (!url) throw new WoolError('url or siteUrl is required.')
|
|
263
|
+
|
|
264
|
+
const payload = {
|
|
265
|
+
projectId: state.projectId,
|
|
266
|
+
type,
|
|
267
|
+
url,
|
|
268
|
+
referrer: cleanText(context.referrer || state.defaultReferrer, 2048),
|
|
269
|
+
v: VERSION
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (state.identity.retentionId) payload.retentionId = state.identity.retentionId
|
|
273
|
+
if (state.identity.groupKey) payload.groupKey = state.identity.groupKey
|
|
274
|
+
|
|
275
|
+
return payload
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async function track(name, properties = {}, options = {}) {
|
|
279
|
+
const eventName = cleanText(name, 96)
|
|
280
|
+
if (!eventName) throw new WoolError('Event name is required.')
|
|
281
|
+
|
|
282
|
+
const opts = trackOptionsFrom(options)
|
|
283
|
+
const eventId = cleanText(opts.eventId || opts.id || createEventId(), 96)
|
|
284
|
+
const payload = {
|
|
285
|
+
...basePayload('event', opts),
|
|
286
|
+
name: eventName,
|
|
287
|
+
properties: cleanProperties(properties),
|
|
288
|
+
source: cleanText(opts.source || 'server', 32),
|
|
289
|
+
eventId,
|
|
290
|
+
visitorKey: cleanText(opts.visitorKey || opts.visitor_key || eventId, 96)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
applyIdentity(payload, state, properties, opts)
|
|
294
|
+
|
|
295
|
+
if (Number.isFinite(Number(opts.value))) payload.value = Number(opts.value)
|
|
296
|
+
if (opts.currency) payload.currency = cleanText(opts.currency, 16)
|
|
297
|
+
if (Number.isFinite(Number(opts.duration))) payload.duration = Number(opts.duration)
|
|
298
|
+
if (opts.nonInteractive || opts.non_interactive) payload.nonInteractive = true
|
|
299
|
+
|
|
300
|
+
return send(payload)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async function pageview(options = {}) {
|
|
304
|
+
return send(basePayload('pageview', options))
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function identify(retentionId, options = {}) {
|
|
308
|
+
state.identity = {
|
|
309
|
+
retentionId: cleanText(retentionId),
|
|
310
|
+
groupKey: cleanText(options.groupKey || options.group_key)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function reset() {
|
|
315
|
+
state.identity = {
|
|
316
|
+
retentionId: '',
|
|
317
|
+
groupKey: ''
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
identify,
|
|
323
|
+
pageview,
|
|
324
|
+
requestContext,
|
|
325
|
+
reset,
|
|
326
|
+
track
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function clientFrom(input) {
|
|
331
|
+
return typeof input?.track === 'function' ? input : createClient(input)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export function expressMiddleware(input) {
|
|
335
|
+
const client = clientFrom(input)
|
|
336
|
+
return function woolExpressMiddleware(req, _res, next) {
|
|
337
|
+
req.wool = client
|
|
338
|
+
if (typeof next === 'function') next()
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export const nestMiddleware = expressMiddleware
|
|
343
|
+
|
|
344
|
+
export function fastifyPlugin(input) {
|
|
345
|
+
const client = clientFrom(input)
|
|
346
|
+
return function woolFastifyPlugin(fastify, _options, done) {
|
|
347
|
+
if (typeof fastify?.decorate === 'function') fastify.decorate('wool', client)
|
|
348
|
+
else if (fastify && typeof fastify === 'object') fastify.wool = client
|
|
349
|
+
if (typeof done === 'function') done()
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export function honoMiddleware(input) {
|
|
354
|
+
const client = clientFrom(input)
|
|
355
|
+
return async function woolHonoMiddleware(c, next) {
|
|
356
|
+
if (typeof c?.set === 'function') c.set('wool', client)
|
|
357
|
+
else if (c && typeof c === 'object') c.wool = client
|
|
358
|
+
if (typeof next === 'function') await next()
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
let defaultClient = null
|
|
363
|
+
|
|
364
|
+
export function init(options = {}) {
|
|
365
|
+
defaultClient = createClient(options)
|
|
366
|
+
return defaultClient
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function requireDefaultClient() {
|
|
370
|
+
if (!defaultClient) throw new WoolError('Call init before using the default Wool client.')
|
|
371
|
+
return defaultClient
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export function identify(retentionId, options) {
|
|
375
|
+
return requireDefaultClient().identify(retentionId, options)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export function reset() {
|
|
379
|
+
return requireDefaultClient().reset()
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export function track(name, properties, options) {
|
|
383
|
+
return requireDefaultClient().track(name, properties, options)
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export function pageview(options) {
|
|
387
|
+
return requireDefaultClient().pageview(options)
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export const wool = {
|
|
391
|
+
init,
|
|
392
|
+
identify,
|
|
393
|
+
reset,
|
|
394
|
+
track,
|
|
395
|
+
pageview,
|
|
396
|
+
createClient,
|
|
397
|
+
requestContext,
|
|
398
|
+
expressMiddleware,
|
|
399
|
+
fastifyPlugin,
|
|
400
|
+
honoMiddleware,
|
|
401
|
+
nestMiddleware
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export default wool
|