@hanzo/event 0.3.25 → 0.3.27
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/LICENSE.md +21 -0
- package/dist/{core-DO8tBUy7.d.cts → core-usULsfUB.d.cts} +16 -4
- package/dist/{core-DO8tBUy7.d.ts → core-usULsfUB.d.ts} +16 -4
- package/dist/index.cjs +48 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +48 -9
- package/dist/index.mjs.map +1 -1
- package/dist/react.cjs +50 -14
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.mjs +51 -15
- package/dist/react.mjs.map +1 -1
- package/hz.js +19 -20
- package/package.json +10 -10
- package/src/core.ts +105 -21
- package/src/hz.test.ts +14 -3
- package/src/react.tsx +7 -7
- package/src/stream.test.ts +169 -0
- package/src/transport.test.ts +7 -1
- package/src/version.ts +1 -1
- package/src/stack.ts +0 -144
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 hanzo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -285,11 +285,19 @@ declare class Analytics {
|
|
|
285
285
|
private attribution;
|
|
286
286
|
private cohort;
|
|
287
287
|
private started;
|
|
288
|
+
/** The view pageview() last counted — path + location. */
|
|
289
|
+
private counted?;
|
|
288
290
|
/** Parsed error-plane DSN, or null when the plane is inert. */
|
|
289
291
|
private dsn;
|
|
290
292
|
/** Guards against an error thrown *inside* the error path re-entering it. */
|
|
291
293
|
private reentrant;
|
|
292
294
|
constructor(config: AnalyticsConfig);
|
|
295
|
+
/** adopt gives this client a credential it does not have. The key belongs to
|
|
296
|
+
* the stream, not to whichever caller happened to ask for the handle first,
|
|
297
|
+
* so a later caller carrying one hands it over. The error plane derives from
|
|
298
|
+
* the key, so it comes up here too when it was inert for want of one.
|
|
299
|
+
* Present fields are never overwritten: the first caller's key stays. */
|
|
300
|
+
adopt(config: AnalyticsConfig): void;
|
|
293
301
|
/** errorPlaneEnabled reports whether captured exceptions can actually reach the
|
|
294
302
|
* error host. False means a DSN was never configured — the documented
|
|
295
303
|
* fail-safe. Exposed so an app (or a test) can assert its wiring instead of
|
|
@@ -308,7 +316,9 @@ declare class Analytics {
|
|
|
308
316
|
/** group associates the visitor with an org/team (analytics grouping, not the
|
|
309
317
|
* server tenant — the server still derives tenant from the session). */
|
|
310
318
|
group(groupId: string, traits?: Record<string, unknown>): void;
|
|
311
|
-
/** pageview records a $pageview for the current (or given) location
|
|
319
|
+
/** pageview records a $pageview for the current (or given) location, once per
|
|
320
|
+
* view. A view is the path plus the full location, so a query or hash change
|
|
321
|
+
* is a new one and a repeat call for the same place is not. */
|
|
312
322
|
pageview(path?: string, properties?: Record<string, unknown>): void;
|
|
313
323
|
/** capture records a named product event with optional properties. Commerce
|
|
314
324
|
* fields (productId/quantity/revenue/currency) may be passed for order events. */
|
|
@@ -339,8 +349,8 @@ declare class Analytics {
|
|
|
339
349
|
* front door POST /v1/event, body { batch: [Event…] }. beacon=true selects the
|
|
340
350
|
* unload-safe transport. Auth is orthogonal to the wire:
|
|
341
351
|
*
|
|
342
|
-
* • publishable key set → rides
|
|
343
|
-
*
|
|
352
|
+
* • publishable key set → rides ?ingest_key=pk-… on both sends, keeping each
|
|
353
|
+
* a CORS-simple request that any origin may send.
|
|
344
354
|
* • else a bearer JWT rides Authorization (fetch only — sendBeacon cannot
|
|
345
355
|
* carry a header, so token apps fall back to keepalive fetch on unload).
|
|
346
356
|
* • else a cookie app rides same-origin credentials (beacon carries the
|
|
@@ -362,7 +372,9 @@ declare class Analytics {
|
|
|
362
372
|
private schedule;
|
|
363
373
|
private clearTimer;
|
|
364
374
|
}
|
|
365
|
-
/** createAnalytics
|
|
375
|
+
/** createAnalytics returns the client for a stream, building it on first ask.
|
|
376
|
+
* This is the ONE way to get a client: `new Analytics` bypasses the registry
|
|
377
|
+
* and is for tests and for a deliberately separate instance. */
|
|
366
378
|
declare function createAnalytics(config: AnalyticsConfig): Analytics;
|
|
367
379
|
|
|
368
380
|
export { type Attribution as A, type Cohort as C, type Dsn as D, type ExceptionEntry as E, type SentryEvent as S, type Transport as T, type WireEvent as W, type CaptureErrorOptions as a, type SentryFrame as b, type SentryLevel as c, type ExceptionProperties as d, Analytics as e, type AnalyticsConfig as f, type EventKind as g, type Exception as h, type ExceptionFrame as i, createAnalytics as j };
|
|
@@ -285,11 +285,19 @@ declare class Analytics {
|
|
|
285
285
|
private attribution;
|
|
286
286
|
private cohort;
|
|
287
287
|
private started;
|
|
288
|
+
/** The view pageview() last counted — path + location. */
|
|
289
|
+
private counted?;
|
|
288
290
|
/** Parsed error-plane DSN, or null when the plane is inert. */
|
|
289
291
|
private dsn;
|
|
290
292
|
/** Guards against an error thrown *inside* the error path re-entering it. */
|
|
291
293
|
private reentrant;
|
|
292
294
|
constructor(config: AnalyticsConfig);
|
|
295
|
+
/** adopt gives this client a credential it does not have. The key belongs to
|
|
296
|
+
* the stream, not to whichever caller happened to ask for the handle first,
|
|
297
|
+
* so a later caller carrying one hands it over. The error plane derives from
|
|
298
|
+
* the key, so it comes up here too when it was inert for want of one.
|
|
299
|
+
* Present fields are never overwritten: the first caller's key stays. */
|
|
300
|
+
adopt(config: AnalyticsConfig): void;
|
|
293
301
|
/** errorPlaneEnabled reports whether captured exceptions can actually reach the
|
|
294
302
|
* error host. False means a DSN was never configured — the documented
|
|
295
303
|
* fail-safe. Exposed so an app (or a test) can assert its wiring instead of
|
|
@@ -308,7 +316,9 @@ declare class Analytics {
|
|
|
308
316
|
/** group associates the visitor with an org/team (analytics grouping, not the
|
|
309
317
|
* server tenant — the server still derives tenant from the session). */
|
|
310
318
|
group(groupId: string, traits?: Record<string, unknown>): void;
|
|
311
|
-
/** pageview records a $pageview for the current (or given) location
|
|
319
|
+
/** pageview records a $pageview for the current (or given) location, once per
|
|
320
|
+
* view. A view is the path plus the full location, so a query or hash change
|
|
321
|
+
* is a new one and a repeat call for the same place is not. */
|
|
312
322
|
pageview(path?: string, properties?: Record<string, unknown>): void;
|
|
313
323
|
/** capture records a named product event with optional properties. Commerce
|
|
314
324
|
* fields (productId/quantity/revenue/currency) may be passed for order events. */
|
|
@@ -339,8 +349,8 @@ declare class Analytics {
|
|
|
339
349
|
* front door POST /v1/event, body { batch: [Event…] }. beacon=true selects the
|
|
340
350
|
* unload-safe transport. Auth is orthogonal to the wire:
|
|
341
351
|
*
|
|
342
|
-
* • publishable key set → rides
|
|
343
|
-
*
|
|
352
|
+
* • publishable key set → rides ?ingest_key=pk-… on both sends, keeping each
|
|
353
|
+
* a CORS-simple request that any origin may send.
|
|
344
354
|
* • else a bearer JWT rides Authorization (fetch only — sendBeacon cannot
|
|
345
355
|
* carry a header, so token apps fall back to keepalive fetch on unload).
|
|
346
356
|
* • else a cookie app rides same-origin credentials (beacon carries the
|
|
@@ -362,7 +372,9 @@ declare class Analytics {
|
|
|
362
372
|
private schedule;
|
|
363
373
|
private clearTimer;
|
|
364
374
|
}
|
|
365
|
-
/** createAnalytics
|
|
375
|
+
/** createAnalytics returns the client for a stream, building it on first ask.
|
|
376
|
+
* This is the ONE way to get a client: `new Analytics` bypasses the registry
|
|
377
|
+
* and is for tests and for a deliberately separate instance. */
|
|
366
378
|
declare function createAnalytics(config: AnalyticsConfig): Analytics;
|
|
367
379
|
|
|
368
380
|
export { type Attribution as A, type Cohort as C, type Dsn as D, type ExceptionEntry as E, type SentryEvent as S, type Transport as T, type WireEvent as W, type CaptureErrorOptions as a, type SentryFrame as b, type SentryLevel as c, type ExceptionProperties as d, Analytics as e, type AnalyticsConfig as f, type EventKind as g, type Exception as h, type ExceptionFrame as i, createAnalytics as j };
|
package/dist/index.cjs
CHANGED
|
@@ -319,7 +319,7 @@ function uuidv7Time(id) {
|
|
|
319
319
|
}
|
|
320
320
|
|
|
321
321
|
// src/version.ts
|
|
322
|
-
var VERSION = "0.3.
|
|
322
|
+
var VERSION = "0.3.27";
|
|
323
323
|
|
|
324
324
|
// src/sentry.ts
|
|
325
325
|
var MAX_FRAMES = 50;
|
|
@@ -736,17 +736,18 @@ var DefaultTransport = class {
|
|
|
736
736
|
}
|
|
737
737
|
}
|
|
738
738
|
if (typeof fetch !== "function") return;
|
|
739
|
+
const simple = opts.ingestKey !== void 0 && opts.contentType === void 0;
|
|
739
740
|
const headers = {
|
|
740
|
-
"Content-Type": opts.contentType ?? "application/json"
|
|
741
|
+
"Content-Type": simple ? BEACON_CONTENT_TYPE : opts.contentType ?? "application/json"
|
|
741
742
|
};
|
|
742
|
-
const bearer = opts.ingestKey ?? opts.token;
|
|
743
|
+
const bearer = simple ? void 0 : opts.ingestKey ?? opts.token;
|
|
743
744
|
if (bearer) headers.Authorization = `Bearer ${bearer}`;
|
|
744
|
-
void fetch(url, {
|
|
745
|
+
void fetch(simple ? appendQuery(url, "ingest_key", opts.ingestKey) : url, {
|
|
745
746
|
method: "POST",
|
|
746
747
|
headers,
|
|
747
748
|
body,
|
|
748
749
|
keepalive: true,
|
|
749
|
-
credentials: "include"
|
|
750
|
+
credentials: simple ? "omit" : "include"
|
|
750
751
|
}).then((res) => {
|
|
751
752
|
if (!res.ok && opts.debug) {
|
|
752
753
|
console.warn("[event] ingest rejected", res.status, url.split("?")[0]);
|
|
@@ -795,6 +796,20 @@ var Analytics = class {
|
|
|
795
796
|
config.dsn ?? readEnvDsn() ?? dsnForProduct(this.cfg.product, this.cfg.ingestKey)
|
|
796
797
|
);
|
|
797
798
|
}
|
|
799
|
+
/** adopt gives this client a credential it does not have. The key belongs to
|
|
800
|
+
* the stream, not to whichever caller happened to ask for the handle first,
|
|
801
|
+
* so a later caller carrying one hands it over. The error plane derives from
|
|
802
|
+
* the key, so it comes up here too when it was inert for want of one.
|
|
803
|
+
* Present fields are never overwritten: the first caller's key stays. */
|
|
804
|
+
adopt(config) {
|
|
805
|
+
if (config.ingestKey && !this.cfg.ingestKey) this.cfg.ingestKey = config.ingestKey;
|
|
806
|
+
if (config.getToken && !this.cfg.getToken) this.cfg.getToken = config.getToken;
|
|
807
|
+
if (!this.dsn) {
|
|
808
|
+
this.dsn = parseDsn(
|
|
809
|
+
config.dsn ?? readEnvDsn() ?? dsnForProduct(this.cfg.product, this.cfg.ingestKey)
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
798
813
|
/** errorPlaneEnabled reports whether captured exceptions can actually reach the
|
|
799
814
|
* error host. False means a DSN was never configured — the documented
|
|
800
815
|
* fail-safe. Exposed so an app (or a test) can assert its wiring instead of
|
|
@@ -845,9 +860,14 @@ var Analytics = class {
|
|
|
845
860
|
group(groupId, traits) {
|
|
846
861
|
this.enqueue("group", void 0, { groupId, properties: traits });
|
|
847
862
|
}
|
|
848
|
-
/** pageview records a $pageview for the current (or given) location
|
|
863
|
+
/** pageview records a $pageview for the current (or given) location, once per
|
|
864
|
+
* view. A view is the path plus the full location, so a query or hash change
|
|
865
|
+
* is a new one and a repeat call for the same place is not. */
|
|
849
866
|
pageview(path, properties) {
|
|
850
867
|
const p = path ?? (isBrowser() ? window.location.pathname : void 0);
|
|
868
|
+
const view = (p ?? "") + "\0" + (isBrowser() ? window.location.href : "");
|
|
869
|
+
if (view === this.counted) return;
|
|
870
|
+
this.counted = view;
|
|
851
871
|
this.enqueue("pageview", events.PAGEVIEW, { path: p, properties });
|
|
852
872
|
}
|
|
853
873
|
/** capture records a named product event with optional properties. Commerce
|
|
@@ -908,8 +928,8 @@ var Analytics = class {
|
|
|
908
928
|
* front door POST /v1/event, body { batch: [Event…] }. beacon=true selects the
|
|
909
929
|
* unload-safe transport. Auth is orthogonal to the wire:
|
|
910
930
|
*
|
|
911
|
-
* • publishable key set → rides
|
|
912
|
-
*
|
|
931
|
+
* • publishable key set → rides ?ingest_key=pk-… on both sends, keeping each
|
|
932
|
+
* a CORS-simple request that any origin may send.
|
|
913
933
|
* • else a bearer JWT rides Authorization (fetch only — sendBeacon cannot
|
|
914
934
|
* carry a header, so token apps fall back to keepalive fetch on unload).
|
|
915
935
|
* • else a cookie app rides same-origin credentials (beacon carries the
|
|
@@ -1032,8 +1052,27 @@ var Analytics = class {
|
|
|
1032
1052
|
}
|
|
1033
1053
|
}
|
|
1034
1054
|
};
|
|
1055
|
+
var CLIENTS = /* @__PURE__ */ Symbol.for("hanzo.event.clients");
|
|
1056
|
+
function registry() {
|
|
1057
|
+
const g = globalThis;
|
|
1058
|
+
const existing = g[CLIENTS];
|
|
1059
|
+
if (existing) return existing;
|
|
1060
|
+
const fresh = /* @__PURE__ */ new Map();
|
|
1061
|
+
g[CLIENTS] = fresh;
|
|
1062
|
+
return fresh;
|
|
1063
|
+
}
|
|
1035
1064
|
function createAnalytics(config) {
|
|
1036
|
-
return new Analytics(config);
|
|
1065
|
+
if (!isBrowser()) return new Analytics(config);
|
|
1066
|
+
const key = (config.host ?? DEFAULT_HOST) + "\0" + config.product;
|
|
1067
|
+
const clients = registry();
|
|
1068
|
+
const existing = clients.get(key);
|
|
1069
|
+
if (existing) {
|
|
1070
|
+
existing.adopt(config);
|
|
1071
|
+
return existing;
|
|
1072
|
+
}
|
|
1073
|
+
const fresh = new Analytics(config);
|
|
1074
|
+
clients.set(key, fresh);
|
|
1075
|
+
return fresh;
|
|
1037
1076
|
}
|
|
1038
1077
|
|
|
1039
1078
|
// src/goals.ts
|