@goliapkg/sentori-javascript 0.1.0 → 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.
@@ -1,9 +1,9 @@
1
- import type { Breadcrumb, BreadcrumbType } from './types.js';
1
+ import { clearBreadcrumbs, getBreadcrumbs } from '@goliapkg/sentori-core';
2
+ import type { BreadcrumbType } from './types.js';
2
3
  export type AddBreadcrumbInput = {
3
4
  data?: Record<string, unknown>;
4
5
  type: BreadcrumbType;
5
6
  };
6
7
  export declare function addBreadcrumb(input: AddBreadcrumbInput): void;
7
- export declare function getBreadcrumbs(): Breadcrumb[];
8
- export declare function clearBreadcrumbs(): void;
8
+ export { clearBreadcrumbs, getBreadcrumbs };
9
9
  //# sourceMappingURL=breadcrumbs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"breadcrumbs.d.ts","sourceRoot":"","sources":["../src/breadcrumbs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAK5D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,IAAI,EAAE,cAAc,CAAA;CACrB,CAAA;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAQ7D;AAED,wBAAgB,cAAc,IAAI,UAAU,EAAE,CAE7C;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}
1
+ {"version":3,"file":"breadcrumbs.d.ts","sourceRoot":"","sources":["../src/breadcrumbs.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,gBAAgB,EAChB,cAAc,EACf,MAAM,wBAAwB,CAAA;AAE/B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,IAAI,EAAE,cAAc,CAAA;CACrB,CAAA;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAE7D;AAED,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,CAAA"}
@@ -1,19 +1,9 @@
1
- const MAX = 100;
2
- const buf = [];
1
+ // Phase 21: ring buffer logic lives in @goliapkg/sentori-core. The
2
+ // public surface here keeps its object-form `addBreadcrumb({ type,
3
+ // data })` so existing callers don't break.
4
+ import { addBreadcrumb as addBreadcrumbCore, clearBreadcrumbs, getBreadcrumbs, } from '@goliapkg/sentori-core';
3
5
  export function addBreadcrumb(input) {
4
- const crumb = {
5
- data: input.data ?? {},
6
- timestamp: new Date().toISOString(),
7
- type: input.type,
8
- };
9
- buf.push(crumb);
10
- if (buf.length > MAX)
11
- buf.shift();
12
- }
13
- export function getBreadcrumbs() {
14
- return [...buf];
15
- }
16
- export function clearBreadcrumbs() {
17
- buf.length = 0;
6
+ addBreadcrumbCore(input.type, input.data ?? {});
18
7
  }
8
+ export { clearBreadcrumbs, getBreadcrumbs };
19
9
  //# sourceMappingURL=breadcrumbs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"breadcrumbs.js","sourceRoot":"","sources":["../src/breadcrumbs.ts"],"names":[],"mappings":"AAEA,MAAM,GAAG,GAAG,GAAG,CAAA;AACf,MAAM,GAAG,GAAiB,EAAE,CAAA;AAO5B,MAAM,UAAU,aAAa,CAAC,KAAyB;IACrD,MAAM,KAAK,GAAe;QACxB,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE;QACtB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,IAAI,EAAE,KAAK,CAAC,IAAI;KACjB,CAAA;IACD,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACf,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG;QAAE,GAAG,CAAC,KAAK,EAAE,CAAA;AACnC,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,CAAC,GAAG,GAAG,CAAC,CAAA;AACjB,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,GAAG,CAAC,MAAM,GAAG,CAAC,CAAA;AAChB,CAAC"}
1
+ {"version":3,"file":"breadcrumbs.js","sourceRoot":"","sources":["../src/breadcrumbs.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,mEAAmE;AACnE,4CAA4C;AAC5C,OAAO,EACL,aAAa,IAAI,iBAAiB,EAClC,gBAAgB,EAChB,cAAc,GACf,MAAM,wBAAwB,CAAA;AAS/B,MAAM,UAAU,aAAa,CAAC,KAAyB;IACrD,iBAAiB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,CAAA;AACjD,CAAC;AAED,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,CAAA"}
package/lib/capture.js CHANGED
@@ -56,13 +56,17 @@ function parseRelease(release) {
56
56
  return { build: m?.[2], version: m?.[1] ?? '0.0.0' };
57
57
  }
58
58
  function detectDevice() {
59
- // Browser: light-touch UA sniff. We deliberately avoid full
60
- // fingerprinting the field is for grouping context, not analytics.
59
+ // The server's device.os is a strict enum: `ios | android | web | other`
60
+ // (see docs/protocol.md). Browser web; Node + everything else → other.
61
+ // The pre-Phase-21 build sent free-form values like "macos" / "windows"
62
+ // which the server quietly rejected with `validationFailed`. Detail
63
+ // about the underlying OS family rides along in `model` instead.
61
64
  const w = globalThis.navigator;
62
65
  if (w?.userAgent) {
63
66
  return {
64
67
  locale: w.language,
65
- os: detectBrowserOs(w.userAgent),
68
+ model: detectBrowserOs(w.userAgent),
69
+ os: 'web',
66
70
  osVersion: '0',
67
71
  };
68
72
  }
@@ -70,11 +74,12 @@ function detectDevice() {
70
74
  const p = globalThis.process;
71
75
  if (p?.platform) {
72
76
  return {
73
- os: p.platform,
77
+ model: p.platform,
78
+ os: 'other',
74
79
  osVersion: p.version?.replace(/^v/, '') ?? '0',
75
80
  };
76
81
  }
77
- return { os: 'unknown', osVersion: '0' };
82
+ return { os: 'other', osVersion: '0' };
78
83
  }
79
84
  function detectBrowserOs(ua) {
80
85
  if (ua.includes('Mac OS X') || ua.includes('Macintosh'))
@@ -1 +1 @@
1
- {"version":3,"file":"capture.js","sourceRoot":"","sources":["../src/capture.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"capture.js","sourceRoot":"","sources":["../src/capture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAA;AAErC,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAElC,IAAI,KAAK,GAAgB,IAAI,CAAA;AAE7B;;;;;;GAMG;AACH,MAAM,UAAU,OAAO,CAAC,IAAiB;IACvC,KAAK,GAAG,IAAI,CAAA;AACd,CAAC;AAED,MAAM,UAAU,OAAO;IACrB,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAY,EAAE,MAAsB;IAC/D,IAAI,CAAC,aAAa,EAAE;QAAE,OAAM;IAC5B,MAAM,GAAG,GAAG,SAAS,EAAG,CAAA;IACxB,MAAM,KAAK,GAAU;QACnB,GAAG,EAAE,EAAE,OAAO,EAAE,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE;QACnD,WAAW,EAAE,cAAc,EAAE;QAC7B,MAAM,EAAE,YAAY,EAAE;QACtB,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC;QAC3B,WAAW,EAAE,MAAM,EAAE,WAAW;QAChC,EAAE,EAAE,MAAM,EAAE;QACZ,IAAI,EAAE,OAAO;QACb,QAAQ,EAAE,YAAY;QACtB,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,IAAI,EAAE,MAAM,EAAE,IAAI;QAClB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,IAAI,EAAE,MAAM,EAAE,IAAI,IAAI,KAAK;KAC5B,CAAA;IACD,KAAK,IAAI,CAAC,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,EAAE,KAAK,CAAC,CAAA;AAClE,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,YAAY,CAAA;AAE5C,SAAS,aAAa,CAAC,KAAY;IACjC,MAAM,QAAQ,GAAI,KAA6B,CAAC,KAAK,CAAA;IACrD,IAAI,KAAK,GAAwB,IAAI,CAAA;IACrC,IAAI,QAAQ,YAAY,KAAK;QAAE,KAAK,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAA;IAC9D,OAAO;QACL,KAAK;QACL,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC;QAC9B,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,OAAO;KAC5B,CAAA;AACH,CAAC;AAED,SAAS,YAAY,CAAC,OAAe;IACnC,MAAM,CAAC,GAAG,iCAAiC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACzD,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,OAAO,EAAE,CAAA;AACtD,CAAC;AAED,SAAS,YAAY;IACnB,yEAAyE;IACzE,yEAAyE;IACzE,wEAAwE;IACxE,oEAAoE;IACpE,iEAAiE;IACjE,MAAM,CAAC,GAAI,UAAwE,CAAC,SAAS,CAAA;IAC7F,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC;QACjB,OAAO;YACL,MAAM,EAAE,CAAC,CAAC,QAAQ;YAClB,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,SAAS,CAAC;YACnC,EAAE,EAAE,KAAK;YACT,SAAS,EAAE,GAAG;SACf,CAAA;IACH,CAAC;IACD,OAAO;IACP,MAAM,CAAC,GAAI,UAAoE,CAAC,OAAO,CAAA;IACvF,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC;QAChB,OAAO;YACL,KAAK,EAAE,CAAC,CAAC,QAAQ;YACjB,EAAE,EAAE,OAAO;YACX,SAAS,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG;SAC/C,CAAA;IACH,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,CAAA;AACxC,CAAC;AAED,SAAS,eAAe,CAAC,EAAU;IACjC,IAAI,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC;QAAE,OAAO,OAAO,CAAA;IACvE,IAAI,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAA;IAC5C,IAAI,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAA;IACxC,IAAI,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAA;IAC5C,IAAI,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAA;IAC9D,OAAO,KAAK,CAAA;AACd,CAAC"}
package/lib/stack.d.ts CHANGED
@@ -1,4 +1,9 @@
1
1
  import type { Frame } from './types.js';
2
- /** Best-effort parse of an `Error.stack` string into Sentori frames. */
2
+ /**
3
+ * JS-flavoured wrapper. The browser dashboard wants short filenames so
4
+ * "https://cdn.example.com/static/App.tsx" displays as "static/App.tsx".
5
+ * RN keeps the long path because Hermes paths are already short and
6
+ * symbolication needs the absolute form.
7
+ */
3
8
  export declare function parseStack(stack: string | undefined): Frame[];
4
9
  //# sourceMappingURL=stack.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../src/stack.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AASvC,wEAAwE;AACxE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,EAAE,CAoB7D"}
1
+ {"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../src/stack.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAEvC;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,KAAK,EAAE,CAE7D"}
package/lib/stack.js CHANGED
@@ -1,39 +1,11 @@
1
- // V8 / Node / Bun: "at fn (file:line:col)" or "at file:line:col" or "at fn file:line:col"
2
- // File can be a URL (https://…), so we anchor on the trailing `:\d+:\d+\)?$`
3
- // and let `(?<file>.+?)` swallow whatever comes before.
4
- const V8_RE = /^\s*at\s+(?:(?<fn>.+?)\s+)?\(?(?<file>.+?):(?<line>\d+):(?<col>\d+)\)?\s*$/;
5
- // SpiderMonkey / Safari: "fn@file:line:col" same trailing anchor.
6
- const SPIDER_RE = /^(?:(?<fn>[^@]*)@)?(?<file>.+?):(?<line>\d+):(?<col>\d+)\s*$/;
7
- /** Best-effort parse of an `Error.stack` string into Sentori frames. */
1
+ import { parseStack as parseStackCore } from '@goliapkg/sentori-core';
2
+ /**
3
+ * JS-flavoured wrapper. The browser dashboard wants short filenames so
4
+ * "https://cdn.example.com/static/App.tsx" displays as "static/App.tsx".
5
+ * RN keeps the long path because Hermes paths are already short and
6
+ * symbolication needs the absolute form.
7
+ */
8
8
  export function parseStack(stack) {
9
- if (!stack)
10
- return [];
11
- const lines = stack.split('\n');
12
- const out = [];
13
- for (const raw of lines) {
14
- const line = raw.trim();
15
- if (!line)
16
- continue;
17
- const m = V8_RE.exec(line) ?? SPIDER_RE.exec(line);
18
- if (!m?.groups)
19
- continue;
20
- const file = m.groups.file ?? '<anonymous>';
21
- out.push({
22
- absolutePath: file,
23
- column: Number(m.groups.col),
24
- file: shortFile(file),
25
- function: m.groups.fn?.trim(),
26
- inApp: !file.includes('node_modules') && !file.startsWith('node:'),
27
- line: Number(m.groups.line),
28
- });
29
- }
30
- return out;
31
- }
32
- function shortFile(absolute) {
33
- // Strip protocol + leading path noise so the dashboard shows
34
- // e.g. "App.tsx" instead of "https://example.com/static/App.tsx".
35
- const noProto = absolute.replace(/^https?:\/\/[^/]+\//, '');
36
- const tail = noProto.split('/').slice(-2).join('/');
37
- return tail || absolute;
9
+ return parseStackCore(stack, { shortFilenames: true });
38
10
  }
39
11
  //# sourceMappingURL=stack.js.map
package/lib/stack.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"stack.js","sourceRoot":"","sources":["../src/stack.ts"],"names":[],"mappings":"AAEA,0FAA0F;AAC1F,6EAA6E;AAC7E,wDAAwD;AACxD,MAAM,KAAK,GAAG,4EAA4E,CAAA;AAC1F,oEAAoE;AACpE,MAAM,SAAS,GAAG,8DAA8D,CAAA;AAEhF,wEAAwE;AACxE,MAAM,UAAU,UAAU,CAAC,KAAyB;IAClD,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAA;IACrB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC/B,MAAM,GAAG,GAAY,EAAE,CAAA;IACvB,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAA;QACvB,IAAI,CAAC,IAAI;YAAE,SAAQ;QACnB,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAClD,IAAI,CAAC,CAAC,EAAE,MAAM;YAAE,SAAQ;QACxB,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,aAAa,CAAA;QAC3C,GAAG,CAAC,IAAI,CAAC;YACP,YAAY,EAAE,IAAI;YAClB,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;YAC5B,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC;YACrB,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE;YAC7B,KAAK,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;YAClE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC;SAC5B,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,SAAS,SAAS,CAAC,QAAgB;IACjC,6DAA6D;IAC7D,kEAAkE;IAClE,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAA;IAC3D,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACnD,OAAO,IAAI,IAAI,QAAQ,CAAA;AACzB,CAAC"}
1
+ {"version":3,"file":"stack.js","sourceRoot":"","sources":["../src/stack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAIrE;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,KAAyB;IAClD,OAAO,cAAc,CAAC,KAAK,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAA;AACxD,CAAC"}
package/lib/types.d.ts CHANGED
@@ -1,78 +1,13 @@
1
1
  /**
2
- * Wire shape for an event sent to the Sentori `/v1/events` endpoint.
3
- * Identical to the protocol documented in `docs/protocol.md` and the
4
- * server's `event::Event` Rust type.
2
+ * Wire-format types now live in `@goliapkg/sentori-core`. This file is
3
+ * kept as a thin re-export so existing relative imports inside the
4
+ * package continue to work. JS-specific extras (the `InitOptions`
5
+ * shape with `enableGlobalHooks`) are declared here.
5
6
  */
6
- export type Event = {
7
- app: {
8
- build?: string;
9
- framework?: {
10
- name: string;
11
- version: string;
12
- };
13
- version: string;
14
- };
15
- breadcrumbs: Breadcrumb[];
16
- device: {
17
- locale?: string;
18
- model?: string;
19
- os: string;
20
- osVersion: string;
21
- };
22
- environment: string;
23
- error: SentoriError;
24
- fingerprint?: string[];
25
- id: string;
26
- kind: 'error';
27
- platform: 'javascript';
28
- release: string;
29
- spanId?: null | string;
30
- tags?: Tags;
31
- timestamp: string;
32
- traceId?: null | string;
33
- user?: null | User;
34
- };
35
- export type SentoriError = {
36
- cause: null | SentoriError;
37
- message: string;
38
- stack: Frame[];
39
- type: string;
40
- };
41
- export type Frame = {
42
- absolutePath?: string;
43
- column?: number;
44
- file: string;
45
- function?: string;
46
- inApp: boolean;
47
- line: number;
48
- };
49
- export type BreadcrumbType = 'custom' | 'log' | 'nav' | 'net' | 'user';
50
- export type Breadcrumb = {
51
- data: Record<string, unknown>;
52
- timestamp: string;
53
- type: BreadcrumbType;
54
- };
55
- /** PII-minimal — same shape as the RN SDK and the server schema. */
56
- export type User = {
57
- anonymous?: boolean;
58
- id?: string;
59
- };
60
- export type Tags = Record<string, string>;
61
- export type CaptureExtras = {
62
- fingerprint?: string[];
63
- tags?: Tags;
64
- user?: User;
65
- };
66
- export type InitOptions = {
7
+ export type { App, Breadcrumb, BreadcrumbType, CaptureExtras, Device, DeviceOS, Event, EventKind, Frame, Platform, SentoriError, Tags, User, } from '@goliapkg/sentori-core';
8
+ import type { CommonInitOptions } from '@goliapkg/sentori-core';
9
+ export type InitOptions = CommonInitOptions & {
67
10
  /** Override automatic global hooks. Default: true on browser + node. */
68
11
  enableGlobalHooks?: boolean;
69
- /** "prod" / "dev" / "staging" / whatever you want. */
70
- environment: string;
71
- /** e.g. https://ingest.sentori.golia.jp */
72
- ingestUrl: string;
73
- /** e.g. "myapp@1.2.3+456" */
74
- release: string;
75
- /** st_pk_<26 base32 chars> */
76
- token: string;
77
12
  };
78
13
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,KAAK,GAAG;IAClB,GAAG,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;IACvF,WAAW,EAAE,UAAU,EAAE,CAAA;IACzB,MAAM,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1E,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,YAAY,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,OAAO,CAAA;IACb,QAAQ,EAAE,YAAY,CAAA;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,IAAI,GAAG,MAAM,CAAA;IACtB,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,IAAI,GAAG,MAAM,CAAA;IACvB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,IAAI,GAAG,YAAY,CAAA;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,KAAK,EAAE,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,KAAK,GAAG;IAClB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,OAAO,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAA;AAEtE,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,cAAc,CAAA;CACrB,CAAA;AAED,oEAAoE;AACpE,MAAM,MAAM,IAAI,GAAG;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAEvD,MAAM,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAEzC,MAAM,MAAM,aAAa,GAAG;IAC1B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,IAAI,CAAC,EAAE,IAAI,CAAA;CACZ,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,wEAAwE;IACxE,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAA;IACnB,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAA;IACjB,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAA;CACd,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EACV,GAAG,EACH,UAAU,EACV,cAAc,EACd,aAAa,EACb,MAAM,EACN,QAAQ,EACR,KAAK,EACL,SAAS,EACT,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,IAAI,EACJ,IAAI,GACL,MAAM,wBAAwB,CAAA;AAE/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAE/D,MAAM,MAAM,WAAW,GAAG,iBAAiB,GAAG;IAC5C,wEAAwE;IACxE,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B,CAAA"}
package/lib/types.js CHANGED
@@ -1,2 +1,8 @@
1
+ /**
2
+ * Wire-format types now live in `@goliapkg/sentori-core`. This file is
3
+ * kept as a thin re-export so existing relative imports inside the
4
+ * package continue to work. JS-specific extras (the `InitOptions`
5
+ * shape with `enableGlobalHooks`) are declared here.
6
+ */
1
7
  export {};
2
8
  //# sourceMappingURL=types.js.map
package/lib/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
package/lib/uuid.d.ts CHANGED
@@ -1,10 +1,2 @@
1
- /**
2
- * uuid v7 (timestamp-prefixed). Modern Node ≥ 19 + browsers expose
3
- * `crypto.randomUUID()` for v4 — that gives us the entropy half;
4
- * v7 layout is cheap to assemble manually.
5
- *
6
- * Layout (RFC 9562 v7):
7
- * ms (48 bits) | ver=7 (4) | rand_a (12) | var=10 (2) | rand_b (62)
8
- */
9
- export declare function uuidV7(): string;
1
+ export { uuidV7 } from '@goliapkg/sentori-core';
10
2
  //# sourceMappingURL=uuid.d.ts.map
package/lib/uuid.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"uuid.d.ts","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,MAAM,IAAI,MAAM,CAgC/B"}
1
+ {"version":3,"file":"uuid.d.ts","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA"}
package/lib/uuid.js CHANGED
@@ -1,54 +1,4 @@
1
- /**
2
- * uuid v7 (timestamp-prefixed). Modern Node ≥ 19 + browsers expose
3
- * `crypto.randomUUID()` for v4 — that gives us the entropy half;
4
- * v7 layout is cheap to assemble manually.
5
- *
6
- * Layout (RFC 9562 v7):
7
- * ms (48 bits) | ver=7 (4) | rand_a (12) | var=10 (2) | rand_b (62)
8
- */
9
- export function uuidV7() {
10
- const ms = Date.now();
11
- const rand = new Uint8Array(10);
12
- cryptoRandomFill(rand);
13
- // 6 bytes of timestamp (ms), big-endian
14
- const t = new Uint8Array(6);
15
- let n = ms;
16
- for (let i = 5; i >= 0; i--) {
17
- t[i] = n & 0xff;
18
- n = Math.floor(n / 256);
19
- }
20
- // pack version + variant
21
- rand[0] = (rand[0] & 0x0f) | 0x70; // version 7 in high nibble of byte 6
22
- rand[2] = (rand[2] & 0x3f) | 0x80; // variant 10 in high two bits of byte 8
23
- const bytes = new Uint8Array(16);
24
- bytes.set(t, 0);
25
- bytes.set(rand, 6);
26
- return (hex(bytes.subarray(0, 4)) +
27
- '-' +
28
- hex(bytes.subarray(4, 6)) +
29
- '-' +
30
- hex(bytes.subarray(6, 8)) +
31
- '-' +
32
- hex(bytes.subarray(8, 10)) +
33
- '-' +
34
- hex(bytes.subarray(10, 16)));
35
- }
36
- function cryptoRandomFill(buf) {
37
- // Browser + Node 19+ + Bun all expose globalThis.crypto.
38
- const c = globalThis.crypto;
39
- if (c?.getRandomValues) {
40
- c.getRandomValues(buf);
41
- return;
42
- }
43
- // Last-resort Math.random (only hits in very old envs; entropy
44
- // quality is bad but we still want a unique-ish id).
45
- for (let i = 0; i < buf.length; i++)
46
- buf[i] = Math.floor(Math.random() * 256);
47
- }
48
- function hex(b) {
49
- let s = '';
50
- for (const x of b)
51
- s += x.toString(16).padStart(2, '0');
52
- return s;
53
- }
1
+ // Phase 21: moved to @goliapkg/sentori-core. Kept as a re-export so
2
+ // internal imports don't churn.
3
+ export { uuidV7 } from '@goliapkg/sentori-core';
54
4
  //# sourceMappingURL=uuid.js.map
package/lib/uuid.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"uuid.js","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,UAAU,MAAM;IACpB,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IACrB,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAA;IAC/B,gBAAgB,CAAC,IAAI,CAAC,CAAA;IAEtB,wCAAwC;IACxC,MAAM,CAAC,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAA;IAC3B,IAAI,CAAC,GAAG,EAAE,CAAA;IACV,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAA;QACf,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,CAAA;IACzB,CAAC;IAED,yBAAyB;IACzB,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAA,CAAC,qCAAqC;IACxE,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAA,CAAC,wCAAwC;IAE3E,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAA;IAChC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IACf,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;IAElB,OAAO,CACL,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACzB,GAAG;QACH,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACzB,GAAG;QACH,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACzB,GAAG;QACH,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1B,GAAG;QACH,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAC5B,CAAA;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAe;IACvC,yDAAyD;IACzD,MAAM,CAAC,GAAI,UAAyE,CAAC,MAAM,CAAA;IAC3F,IAAI,CAAC,EAAE,eAAe,EAAE,CAAC;QACvB,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CAAA;QACtB,OAAM;IACR,CAAC;IACD,+DAA+D;IAC/D,qDAAqD;IACrD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAA;AAC/E,CAAC;AAED,SAAS,GAAG,CAAC,CAAa;IACxB,IAAI,CAAC,GAAG,EAAE,CAAA;IACV,KAAK,MAAM,CAAC,IAAI,CAAC;QAAE,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IACvD,OAAO,CAAC,CAAA;AACV,CAAC"}
1
+ {"version":3,"file":"uuid.js","sourceRoot":"","sources":["../src/uuid.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,gCAAgC;AAChC,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goliapkg/sentori-javascript",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Sentori SDK for browsers and Node — error capture, breadcrumbs, and HTTP transport. Same wire protocol as @goliapkg/sentori-react-native.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://sentori.golia.jp",
@@ -35,6 +35,9 @@
35
35
  "src/",
36
36
  "README.md"
37
37
  ],
38
+ "dependencies": {
39
+ "@goliapkg/sentori-core": "0.1.0"
40
+ },
38
41
  "scripts": {
39
42
  "build": "tsc -p tsconfig.json",
40
43
  "typecheck": "tsc --noEmit",
@@ -1,7 +1,13 @@
1
- import type { Breadcrumb, BreadcrumbType } from './types.js'
1
+ // Phase 21: ring buffer logic lives in @goliapkg/sentori-core. The
2
+ // public surface here keeps its object-form `addBreadcrumb({ type,
3
+ // data })` so existing callers don't break.
4
+ import {
5
+ addBreadcrumb as addBreadcrumbCore,
6
+ clearBreadcrumbs,
7
+ getBreadcrumbs,
8
+ } from '@goliapkg/sentori-core'
2
9
 
3
- const MAX = 100
4
- const buf: Breadcrumb[] = []
10
+ import type { BreadcrumbType } from './types.js'
5
11
 
6
12
  export type AddBreadcrumbInput = {
7
13
  data?: Record<string, unknown>
@@ -9,19 +15,7 @@ export type AddBreadcrumbInput = {
9
15
  }
10
16
 
11
17
  export function addBreadcrumb(input: AddBreadcrumbInput): void {
12
- const crumb: Breadcrumb = {
13
- data: input.data ?? {},
14
- timestamp: new Date().toISOString(),
15
- type: input.type,
16
- }
17
- buf.push(crumb)
18
- if (buf.length > MAX) buf.shift()
18
+ addBreadcrumbCore(input.type, input.data ?? {})
19
19
  }
20
20
 
21
- export function getBreadcrumbs(): Breadcrumb[] {
22
- return [...buf]
23
- }
24
-
25
- export function clearBreadcrumbs(): void {
26
- buf.length = 0
27
- }
21
+ export { clearBreadcrumbs, getBreadcrumbs }
package/src/capture.ts CHANGED
@@ -63,13 +63,17 @@ function parseRelease(release: string): { build?: string; version: string } {
63
63
  }
64
64
 
65
65
  function detectDevice(): Event['device'] {
66
- // Browser: light-touch UA sniff. We deliberately avoid full
67
- // fingerprinting the field is for grouping context, not analytics.
66
+ // The server's device.os is a strict enum: `ios | android | web | other`
67
+ // (see docs/protocol.md). Browser web; Node + everything else → other.
68
+ // The pre-Phase-21 build sent free-form values like "macos" / "windows"
69
+ // which the server quietly rejected with `validationFailed`. Detail
70
+ // about the underlying OS family rides along in `model` instead.
68
71
  const w = (globalThis as { navigator?: { language?: string; userAgent?: string } }).navigator
69
72
  if (w?.userAgent) {
70
73
  return {
71
74
  locale: w.language,
72
- os: detectBrowserOs(w.userAgent),
75
+ model: detectBrowserOs(w.userAgent),
76
+ os: 'web',
73
77
  osVersion: '0',
74
78
  }
75
79
  }
@@ -77,11 +81,12 @@ function detectDevice(): Event['device'] {
77
81
  const p = (globalThis as { process?: { platform?: string; version?: string } }).process
78
82
  if (p?.platform) {
79
83
  return {
80
- os: p.platform,
84
+ model: p.platform,
85
+ os: 'other',
81
86
  osVersion: p.version?.replace(/^v/, '') ?? '0',
82
87
  }
83
88
  }
84
- return { os: 'unknown', osVersion: '0' }
89
+ return { os: 'other', osVersion: '0' }
85
90
  }
86
91
 
87
92
  function detectBrowserOs(ua: string): string {
package/src/stack.ts CHANGED
@@ -1,39 +1,13 @@
1
- import type { Frame } from './types.js'
1
+ import { parseStack as parseStackCore } from '@goliapkg/sentori-core'
2
2
 
3
- // V8 / Node / Bun: "at fn (file:line:col)" or "at file:line:col" or "at fn file:line:col"
4
- // File can be a URL (https://…), so we anchor on the trailing `:\d+:\d+\)?$`
5
- // and let `(?<file>.+?)` swallow whatever comes before.
6
- const V8_RE = /^\s*at\s+(?:(?<fn>.+?)\s+)?\(?(?<file>.+?):(?<line>\d+):(?<col>\d+)\)?\s*$/
7
- // SpiderMonkey / Safari: "fn@file:line:col" — same trailing anchor.
8
- const SPIDER_RE = /^(?:(?<fn>[^@]*)@)?(?<file>.+?):(?<line>\d+):(?<col>\d+)\s*$/
3
+ import type { Frame } from './types.js'
9
4
 
10
- /** Best-effort parse of an `Error.stack` string into Sentori frames. */
5
+ /**
6
+ * JS-flavoured wrapper. The browser dashboard wants short filenames so
7
+ * "https://cdn.example.com/static/App.tsx" displays as "static/App.tsx".
8
+ * RN keeps the long path because Hermes paths are already short and
9
+ * symbolication needs the absolute form.
10
+ */
11
11
  export function parseStack(stack: string | undefined): Frame[] {
12
- if (!stack) return []
13
- const lines = stack.split('\n')
14
- const out: Frame[] = []
15
- for (const raw of lines) {
16
- const line = raw.trim()
17
- if (!line) continue
18
- const m = V8_RE.exec(line) ?? SPIDER_RE.exec(line)
19
- if (!m?.groups) continue
20
- const file = m.groups.file ?? '<anonymous>'
21
- out.push({
22
- absolutePath: file,
23
- column: Number(m.groups.col),
24
- file: shortFile(file),
25
- function: m.groups.fn?.trim(),
26
- inApp: !file.includes('node_modules') && !file.startsWith('node:'),
27
- line: Number(m.groups.line),
28
- })
29
- }
30
- return out
31
- }
32
-
33
- function shortFile(absolute: string): string {
34
- // Strip protocol + leading path noise so the dashboard shows
35
- // e.g. "App.tsx" instead of "https://example.com/static/App.tsx".
36
- const noProto = absolute.replace(/^https?:\/\/[^/]+\//, '')
37
- const tail = noProto.split('/').slice(-2).join('/')
38
- return tail || absolute
12
+ return parseStackCore(stack, { shortFilenames: true })
39
13
  }
package/src/types.ts CHANGED
@@ -1,70 +1,29 @@
1
1
  /**
2
- * Wire shape for an event sent to the Sentori `/v1/events` endpoint.
3
- * Identical to the protocol documented in `docs/protocol.md` and the
4
- * server's `event::Event` Rust type.
2
+ * Wire-format types now live in `@goliapkg/sentori-core`. This file is
3
+ * kept as a thin re-export so existing relative imports inside the
4
+ * package continue to work. JS-specific extras (the `InitOptions`
5
+ * shape with `enableGlobalHooks`) are declared here.
5
6
  */
6
- export type Event = {
7
- app: { build?: string; framework?: { name: string; version: string }; version: string }
8
- breadcrumbs: Breadcrumb[]
9
- device: { locale?: string; model?: string; os: string; osVersion: string }
10
- environment: string
11
- error: SentoriError
12
- fingerprint?: string[]
13
- id: string
14
- kind: 'error'
15
- platform: 'javascript'
16
- release: string
17
- spanId?: null | string
18
- tags?: Tags
19
- timestamp: string
20
- traceId?: null | string
21
- user?: null | User
22
- }
23
-
24
- export type SentoriError = {
25
- cause: null | SentoriError
26
- message: string
27
- stack: Frame[]
28
- type: string
29
- }
30
-
31
- export type Frame = {
32
- absolutePath?: string
33
- column?: number
34
- file: string
35
- function?: string
36
- inApp: boolean
37
- line: number
38
- }
39
-
40
- export type BreadcrumbType = 'custom' | 'log' | 'nav' | 'net' | 'user'
41
-
42
- export type Breadcrumb = {
43
- data: Record<string, unknown>
44
- timestamp: string
45
- type: BreadcrumbType
46
- }
47
-
48
- /** PII-minimal — same shape as the RN SDK and the server schema. */
49
- export type User = { anonymous?: boolean; id?: string }
50
-
51
- export type Tags = Record<string, string>
52
-
53
- export type CaptureExtras = {
54
- fingerprint?: string[]
55
- tags?: Tags
56
- user?: User
57
- }
58
7
 
59
- export type InitOptions = {
8
+ export type {
9
+ App,
10
+ Breadcrumb,
11
+ BreadcrumbType,
12
+ CaptureExtras,
13
+ Device,
14
+ DeviceOS,
15
+ Event,
16
+ EventKind,
17
+ Frame,
18
+ Platform,
19
+ SentoriError,
20
+ Tags,
21
+ User,
22
+ } from '@goliapkg/sentori-core'
23
+
24
+ import type { CommonInitOptions } from '@goliapkg/sentori-core'
25
+
26
+ export type InitOptions = CommonInitOptions & {
60
27
  /** Override automatic global hooks. Default: true on browser + node. */
61
28
  enableGlobalHooks?: boolean
62
- /** "prod" / "dev" / "staging" / whatever you want. */
63
- environment: string
64
- /** e.g. https://ingest.sentori.golia.jp */
65
- ingestUrl: string
66
- /** e.g. "myapp@1.2.3+456" */
67
- release: string
68
- /** st_pk_<26 base32 chars> */
69
- token: string
70
29
  }
package/src/uuid.ts CHANGED
@@ -1,59 +1,3 @@
1
- /**
2
- * uuid v7 (timestamp-prefixed). Modern Node ≥ 19 + browsers expose
3
- * `crypto.randomUUID()` for v4 — that gives us the entropy half;
4
- * v7 layout is cheap to assemble manually.
5
- *
6
- * Layout (RFC 9562 v7):
7
- * ms (48 bits) | ver=7 (4) | rand_a (12) | var=10 (2) | rand_b (62)
8
- */
9
- export function uuidV7(): string {
10
- const ms = Date.now()
11
- const rand = new Uint8Array(10)
12
- cryptoRandomFill(rand)
13
-
14
- // 6 bytes of timestamp (ms), big-endian
15
- const t = new Uint8Array(6)
16
- let n = ms
17
- for (let i = 5; i >= 0; i--) {
18
- t[i] = n & 0xff
19
- n = Math.floor(n / 256)
20
- }
21
-
22
- // pack version + variant
23
- rand[0] = (rand[0]! & 0x0f) | 0x70 // version 7 in high nibble of byte 6
24
- rand[2] = (rand[2]! & 0x3f) | 0x80 // variant 10 in high two bits of byte 8
25
-
26
- const bytes = new Uint8Array(16)
27
- bytes.set(t, 0)
28
- bytes.set(rand, 6)
29
-
30
- return (
31
- hex(bytes.subarray(0, 4)) +
32
- '-' +
33
- hex(bytes.subarray(4, 6)) +
34
- '-' +
35
- hex(bytes.subarray(6, 8)) +
36
- '-' +
37
- hex(bytes.subarray(8, 10)) +
38
- '-' +
39
- hex(bytes.subarray(10, 16))
40
- )
41
- }
42
-
43
- function cryptoRandomFill(buf: Uint8Array): void {
44
- // Browser + Node 19+ + Bun all expose globalThis.crypto.
45
- const c = (globalThis as { crypto?: { getRandomValues?: (b: Uint8Array) => void } }).crypto
46
- if (c?.getRandomValues) {
47
- c.getRandomValues(buf)
48
- return
49
- }
50
- // Last-resort Math.random (only hits in very old envs; entropy
51
- // quality is bad but we still want a unique-ish id).
52
- for (let i = 0; i < buf.length; i++) buf[i] = Math.floor(Math.random() * 256)
53
- }
54
-
55
- function hex(b: Uint8Array): string {
56
- let s = ''
57
- for (const x of b) s += x.toString(16).padStart(2, '0')
58
- return s
59
- }
1
+ // Phase 21: moved to @goliapkg/sentori-core. Kept as a re-export so
2
+ // internal imports don't churn.
3
+ export { uuidV7 } from '@goliapkg/sentori-core'