@edraj/sauron-browser 1.0.0 → 1.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/CHANGELOG.md +19 -0
- package/README.md +988 -53
- package/dist/index.cjs +181 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +91 -5
- package/dist/index.d.ts +91 -5
- package/dist/index.js +178 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -8,7 +8,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
8
8
|
|
|
9
9
|
// src/utils.ts
|
|
10
10
|
var SDK_NAME = "sauron.javascript";
|
|
11
|
-
var SDK_VERSION = "1.
|
|
11
|
+
var SDK_VERSION = "1.3.0";
|
|
12
12
|
function getGlobal() {
|
|
13
13
|
return globalThis;
|
|
14
14
|
}
|
|
@@ -836,6 +836,30 @@ var Scope = class {
|
|
|
836
836
|
}
|
|
837
837
|
};
|
|
838
838
|
|
|
839
|
+
// src/workflow.ts
|
|
840
|
+
var WORKFLOW_NAME_MAX = 120;
|
|
841
|
+
var WORKFLOW_REASON_MAX = 120;
|
|
842
|
+
var current = null;
|
|
843
|
+
function getWorkflow() {
|
|
844
|
+
return current;
|
|
845
|
+
}
|
|
846
|
+
function setWorkflowState(workflow) {
|
|
847
|
+
current = workflow;
|
|
848
|
+
}
|
|
849
|
+
function resetWorkflow() {
|
|
850
|
+
current = null;
|
|
851
|
+
}
|
|
852
|
+
function normalizeWorkflowName(name) {
|
|
853
|
+
if (typeof name !== "string") return null;
|
|
854
|
+
const trimmed = name.trim();
|
|
855
|
+
if (trimmed.length === 0 || trimmed.length > WORKFLOW_NAME_MAX) return null;
|
|
856
|
+
return trimmed;
|
|
857
|
+
}
|
|
858
|
+
function normalizeReason(reason) {
|
|
859
|
+
if (typeof reason !== "string" || reason.trim().length === 0) return "user";
|
|
860
|
+
return reason.trim().slice(0, WORKFLOW_REASON_MAX);
|
|
861
|
+
}
|
|
862
|
+
|
|
839
863
|
// src/api/product.ts
|
|
840
864
|
function track(name, properties = {}, options = {}) {
|
|
841
865
|
const client = getClient();
|
|
@@ -906,6 +930,86 @@ function trackTransaction(input) {
|
|
|
906
930
|
const item = buildTransactionItem(input, client.getDistinctId(), getSessionId());
|
|
907
931
|
client.captureItem(item);
|
|
908
932
|
}
|
|
933
|
+
var NOOP_LOGGER = makeLogger(false);
|
|
934
|
+
function emitWorkflowClose(active, eventName, reason, logger) {
|
|
935
|
+
const properties = {
|
|
936
|
+
workflow_id: active.workflowId,
|
|
937
|
+
workflow_name: active.name,
|
|
938
|
+
duration_ms: Math.max(0, Date.now() - Date.parse(active.startedAt))
|
|
939
|
+
};
|
|
940
|
+
if (eventName === "$workflow_cancel") {
|
|
941
|
+
properties.reason = normalizeReason(reason);
|
|
942
|
+
}
|
|
943
|
+
try {
|
|
944
|
+
track(eventName, properties);
|
|
945
|
+
} catch (err) {
|
|
946
|
+
logger.warn(`${eventName}: failed to emit the lifecycle event`, err);
|
|
947
|
+
} finally {
|
|
948
|
+
resetWorkflow();
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
function startWorkflow(name, options) {
|
|
952
|
+
let logger = NOOP_LOGGER;
|
|
953
|
+
try {
|
|
954
|
+
const client = getClient();
|
|
955
|
+
if (!client || !client.isEnabled()) return { status: "disabled" };
|
|
956
|
+
logger = makeLogger(client.options.debug);
|
|
957
|
+
const normalized = normalizeWorkflowName(name);
|
|
958
|
+
if (!normalized) {
|
|
959
|
+
logger.warn("startWorkflow: invalid name", name);
|
|
960
|
+
return { status: "invalid_name" };
|
|
961
|
+
}
|
|
962
|
+
const active = getWorkflow();
|
|
963
|
+
if (active && !options?.force) {
|
|
964
|
+
logger.warn(
|
|
965
|
+
`startWorkflow("${normalized}"): "${active.name}" is already active; pass { force: true } to replace it`
|
|
966
|
+
);
|
|
967
|
+
return { status: "already_active" };
|
|
968
|
+
}
|
|
969
|
+
const workflow = {
|
|
970
|
+
workflowId: uuidv4(),
|
|
971
|
+
name: normalized,
|
|
972
|
+
startedAt: nowIso()
|
|
973
|
+
};
|
|
974
|
+
if (active) emitWorkflowClose(active, "$workflow_cancel", "superseded", logger);
|
|
975
|
+
setWorkflowState(workflow);
|
|
976
|
+
try {
|
|
977
|
+
track("$workflow_start", { workflow_id: workflow.workflowId, workflow_name: workflow.name });
|
|
978
|
+
} catch (err) {
|
|
979
|
+
logger.warn("startWorkflow: failed to emit $workflow_start", err);
|
|
980
|
+
}
|
|
981
|
+
return { status: "ok", workflowId: workflow.workflowId };
|
|
982
|
+
} catch (err) {
|
|
983
|
+
logger.warn("startWorkflow failed", err);
|
|
984
|
+
return { status: "disabled" };
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
function closeWorkflow(eventName, name, reason) {
|
|
988
|
+
let logger = NOOP_LOGGER;
|
|
989
|
+
try {
|
|
990
|
+
const client = getClient();
|
|
991
|
+
if (!client || !client.isEnabled()) return { status: "disabled" };
|
|
992
|
+
logger = makeLogger(client.options.debug);
|
|
993
|
+
const active = getWorkflow();
|
|
994
|
+
if (!active) return { status: "not_active" };
|
|
995
|
+
if (name !== void 0 && normalizeWorkflowName(name) !== active.name) {
|
|
996
|
+
logger.warn(`${eventName}: "${name}" does not match active workflow "${active.name}"`);
|
|
997
|
+
return { status: "name_mismatch" };
|
|
998
|
+
}
|
|
999
|
+
const workflowId = active.workflowId;
|
|
1000
|
+
emitWorkflowClose(active, eventName, reason, logger);
|
|
1001
|
+
return { status: "ok", workflowId };
|
|
1002
|
+
} catch (err) {
|
|
1003
|
+
logger.warn(`${eventName} failed`, err);
|
|
1004
|
+
return { status: "disabled" };
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
function endWorkflow(name) {
|
|
1008
|
+
return closeWorkflow("$workflow_end", name);
|
|
1009
|
+
}
|
|
1010
|
+
function cancelWorkflow(name, options) {
|
|
1011
|
+
return closeWorkflow("$workflow_cancel", name, options?.reason);
|
|
1012
|
+
}
|
|
909
1013
|
|
|
910
1014
|
// src/integrations/performance.ts
|
|
911
1015
|
var PERF_FETCH = "__sauron_perf_fetch__";
|
|
@@ -1358,6 +1462,9 @@ var Transport = class {
|
|
|
1358
1462
|
__publicField(this, "pending", []);
|
|
1359
1463
|
__publicField(this, "timer", null);
|
|
1360
1464
|
__publicField(this, "onlineHandler", null);
|
|
1465
|
+
/** Permanent auto-disable latch, flipped by this transport itself the moment
|
|
1466
|
+
* it classifies a response as 401/403 — it is the source of truth for
|
|
1467
|
+
* {@link isEnabled}, not merely a mirror of something the client decided. */
|
|
1361
1468
|
__publicField(this, "disabled", false);
|
|
1362
1469
|
this.dsn = config.dsn;
|
|
1363
1470
|
this.makeEnvelope = config.makeEnvelope;
|
|
@@ -1405,6 +1512,10 @@ var Transport = class {
|
|
|
1405
1512
|
this.pending = [];
|
|
1406
1513
|
this.stop();
|
|
1407
1514
|
}
|
|
1515
|
+
/** Whether the transport still accepts items (false once auth-disabled by a 401/403). */
|
|
1516
|
+
isEnabled() {
|
|
1517
|
+
return !this.disabled;
|
|
1518
|
+
}
|
|
1408
1519
|
/** Queue an item for the next batch; flush eagerly once the batch is full. */
|
|
1409
1520
|
send(item) {
|
|
1410
1521
|
if (this.disabled) return;
|
|
@@ -1447,6 +1558,7 @@ var Transport = class {
|
|
|
1447
1558
|
return;
|
|
1448
1559
|
case "disable":
|
|
1449
1560
|
this.logger.warn("server rejected credentials; disabling client");
|
|
1561
|
+
this.disable();
|
|
1450
1562
|
this.onDisable();
|
|
1451
1563
|
return;
|
|
1452
1564
|
case "split": {
|
|
@@ -1484,6 +1596,7 @@ var Transport = class {
|
|
|
1484
1596
|
outcome = { action: "retry_backoff" };
|
|
1485
1597
|
}
|
|
1486
1598
|
if (outcome.action === "disable") {
|
|
1599
|
+
this.disable();
|
|
1487
1600
|
this.onDisable();
|
|
1488
1601
|
this.offline.enqueue(json);
|
|
1489
1602
|
return;
|
|
@@ -1649,8 +1762,19 @@ var SauronClient = class {
|
|
|
1649
1762
|
getScope() {
|
|
1650
1763
|
return this.scope;
|
|
1651
1764
|
}
|
|
1765
|
+
/**
|
|
1766
|
+
* False once this client was explicitly disabled/closed, OR once the
|
|
1767
|
+
* transport has auto-disabled itself on a 401/403 (revoked/invalid DSN
|
|
1768
|
+
* key) — computed from the transport's own state on every call, not a
|
|
1769
|
+
* separately mirrored flag, so a propagation regression there cannot leave
|
|
1770
|
+
* this predicate stale. `this.transport` always exists once a client
|
|
1771
|
+
* exists (it is constructed synchronously in the constructor); the
|
|
1772
|
+
* "nothing installed yet" case is instead handled one layer up, by every
|
|
1773
|
+
* module-level API (`startWorkflow`, `track`, ...) treating `getClient() ===
|
|
1774
|
+
* null` as the no-op/disabled case before it ever reaches here.
|
|
1775
|
+
*/
|
|
1652
1776
|
isEnabled() {
|
|
1653
|
-
return this.enabled;
|
|
1777
|
+
return this.enabled && this.transport.isEnabled();
|
|
1654
1778
|
}
|
|
1655
1779
|
/** The current distinct id: the user id when identified, else an anon id. */
|
|
1656
1780
|
getDistinctId() {
|
|
@@ -1672,7 +1796,6 @@ var SauronClient = class {
|
|
|
1672
1796
|
dsn: this.dsn.raw,
|
|
1673
1797
|
sdk: { name: SDK_NAME, version: SDK_VERSION },
|
|
1674
1798
|
sent_at: nowIso(),
|
|
1675
|
-
environment: this.options.environment,
|
|
1676
1799
|
release: this.options.release
|
|
1677
1800
|
};
|
|
1678
1801
|
const context = buildContext(this.options.release, this.scope.getUser());
|
|
@@ -1718,6 +1841,39 @@ var SauronClient = class {
|
|
|
1718
1841
|
item.user = this.scope.getUser();
|
|
1719
1842
|
}
|
|
1720
1843
|
}
|
|
1844
|
+
/**
|
|
1845
|
+
* Stamp the active workflow (if any) onto a signal item.
|
|
1846
|
+
*
|
|
1847
|
+
* Done HERE — the single choke point every capture path funnels through —
|
|
1848
|
+
* rather than at each item-construction site, so a capture path added later
|
|
1849
|
+
* is stamped by construction instead of by remembering to. The keys are
|
|
1850
|
+
* ASSIGNED ONLY when a workflow is active: an item with no workflow keeps
|
|
1851
|
+
* them absent entirely (not present-as-`undefined`), which is what makes
|
|
1852
|
+
* `JSON.stringify` omit them and keeps the no-workflow wire bytes identical
|
|
1853
|
+
* to pre-1.3.0.
|
|
1854
|
+
*
|
|
1855
|
+
* Only error/event/transaction carry `workflow_id`/`workflow_name` columns
|
|
1856
|
+
* server-side — identify and breadcrumb_batch items are deliberately left
|
|
1857
|
+
* alone. An item that already carries an explicit `workflow_id` is left
|
|
1858
|
+
* untouched, matching how `enrichErrorItem` defers to caller-set fields.
|
|
1859
|
+
*/
|
|
1860
|
+
stampWorkflow(item) {
|
|
1861
|
+
if (item.type !== "error" && item.type !== "event" && item.type !== "transaction") return;
|
|
1862
|
+
const hasId = item.workflow_id !== void 0;
|
|
1863
|
+
const hasName = item.workflow_name !== void 0;
|
|
1864
|
+
if (hasId || hasName) {
|
|
1865
|
+
if (hasId !== hasName) {
|
|
1866
|
+
this.logger.warn(
|
|
1867
|
+
"item sets only one of workflow_id/workflow_name; the server treats them as a pair and will drop this attribution. Set both, or neither."
|
|
1868
|
+
);
|
|
1869
|
+
}
|
|
1870
|
+
return;
|
|
1871
|
+
}
|
|
1872
|
+
const workflow = getWorkflow();
|
|
1873
|
+
if (!workflow) return;
|
|
1874
|
+
item.workflow_id = workflow.workflowId;
|
|
1875
|
+
item.workflow_name = workflow.name;
|
|
1876
|
+
}
|
|
1721
1877
|
/**
|
|
1722
1878
|
* Run an item through sampling (errors only) and `beforeSend`, then hand it to
|
|
1723
1879
|
* the transport. Returns silently when dropped.
|
|
@@ -1731,6 +1887,7 @@ var SauronClient = class {
|
|
|
1731
1887
|
}
|
|
1732
1888
|
this.enrichErrorItem(item, hint);
|
|
1733
1889
|
}
|
|
1890
|
+
this.stampWorkflow(item);
|
|
1734
1891
|
let processed = item;
|
|
1735
1892
|
if (this.options.beforeSend) {
|
|
1736
1893
|
try {
|
|
@@ -1767,6 +1924,7 @@ var SauronClient = class {
|
|
|
1767
1924
|
}
|
|
1768
1925
|
onNavigation(null);
|
|
1769
1926
|
resetScreen();
|
|
1927
|
+
resetWorkflow();
|
|
1770
1928
|
unpatchAll();
|
|
1771
1929
|
setDsnHost(null);
|
|
1772
1930
|
this.installed = false;
|
|
@@ -1789,7 +1947,6 @@ function resolveOptions(options) {
|
|
|
1789
1947
|
const t = options.transport ?? {};
|
|
1790
1948
|
return {
|
|
1791
1949
|
dsn: options.dsn,
|
|
1792
|
-
environment: options.environment ?? "production",
|
|
1793
1950
|
release: options.release ?? null,
|
|
1794
1951
|
sampleRate: clamp(options.sampleRate ?? 1, 0, 1),
|
|
1795
1952
|
maxBreadcrumbs: options.maxBreadcrumbs ?? 50,
|
|
@@ -1874,6 +2031,18 @@ function setScreen2(name) {
|
|
|
1874
2031
|
function getScreen2() {
|
|
1875
2032
|
return getScreen();
|
|
1876
2033
|
}
|
|
2034
|
+
function startWorkflow2(name, options) {
|
|
2035
|
+
return startWorkflow(name, options);
|
|
2036
|
+
}
|
|
2037
|
+
function endWorkflow2(name) {
|
|
2038
|
+
return endWorkflow(name);
|
|
2039
|
+
}
|
|
2040
|
+
function cancelWorkflow2(name, options) {
|
|
2041
|
+
return cancelWorkflow(name, options);
|
|
2042
|
+
}
|
|
2043
|
+
function getWorkflow2() {
|
|
2044
|
+
return getWorkflow();
|
|
2045
|
+
}
|
|
1877
2046
|
function addBreadcrumb2(breadcrumb, hint) {
|
|
1878
2047
|
addBreadcrumb(breadcrumb, hint);
|
|
1879
2048
|
}
|
|
@@ -1915,6 +2084,10 @@ var Sauron = {
|
|
|
1915
2084
|
setExtra,
|
|
1916
2085
|
setScreen: setScreen2,
|
|
1917
2086
|
getScreen: getScreen2,
|
|
2087
|
+
startWorkflow: startWorkflow2,
|
|
2088
|
+
endWorkflow: endWorkflow2,
|
|
2089
|
+
cancelWorkflow: cancelWorkflow2,
|
|
2090
|
+
getWorkflow: getWorkflow2,
|
|
1918
2091
|
flush,
|
|
1919
2092
|
close,
|
|
1920
2093
|
getClient
|
|
@@ -1928,13 +2101,16 @@ exports.Sauron = Sauron;
|
|
|
1928
2101
|
exports.SauronClient = SauronClient;
|
|
1929
2102
|
exports.addBreadcrumb = addBreadcrumb2;
|
|
1930
2103
|
exports.buildEnvelope = buildEnvelope;
|
|
2104
|
+
exports.cancelWorkflow = cancelWorkflow2;
|
|
1931
2105
|
exports.captureException = captureException2;
|
|
1932
2106
|
exports.captureMessage = captureMessage2;
|
|
1933
2107
|
exports.close = close;
|
|
1934
2108
|
exports.default = index_default;
|
|
2109
|
+
exports.endWorkflow = endWorkflow2;
|
|
1935
2110
|
exports.flush = flush;
|
|
1936
2111
|
exports.getClient = getClient;
|
|
1937
2112
|
exports.getScreen = getScreen2;
|
|
2113
|
+
exports.getWorkflow = getWorkflow2;
|
|
1938
2114
|
exports.identify = identify2;
|
|
1939
2115
|
exports.init = init2;
|
|
1940
2116
|
exports.isInAppFrame = isInAppFrame;
|
|
@@ -1947,6 +2123,7 @@ exports.setScreen = setScreen2;
|
|
|
1947
2123
|
exports.setTag = setTag;
|
|
1948
2124
|
exports.setTags = setTags;
|
|
1949
2125
|
exports.setUser = setUser;
|
|
2126
|
+
exports.startWorkflow = startWorkflow2;
|
|
1950
2127
|
exports.track = track2;
|
|
1951
2128
|
exports.trackTransaction = trackTransaction2;
|
|
1952
2129
|
//# sourceMappingURL=index.cjs.map
|