@rasputin-ai/core 0.1.4 → 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.
Files changed (30) hide show
  1. package/dist/create-client/create-client-types.d.ts +35 -28
  2. package/dist/create-client/create-client-types.d.ts.map +1 -1
  3. package/dist/create-client/create-client.d.ts.map +1 -1
  4. package/dist/detect-release/detect-release.d.ts +5 -2
  5. package/dist/detect-release/detect-release.d.ts.map +1 -1
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +307 -79
  9. package/dist/normalize-frames/normalize-frames.d.ts +3 -3
  10. package/dist/normalize-frames/normalize-frames.d.ts.map +1 -1
  11. package/dist/repo-root/repo-root-from.d.ts +8 -0
  12. package/dist/repo-root/repo-root-from.d.ts.map +1 -0
  13. package/dist/repo-root/resolve-repo-root.d.ts +6 -0
  14. package/dist/repo-root/resolve-repo-root.d.ts.map +1 -0
  15. package/dist/repo-root/warn-repo-root-setup.d.ts +5 -0
  16. package/dist/repo-root/warn-repo-root-setup.d.ts.map +1 -0
  17. package/dist/sdk-meta.d.ts +1 -1
  18. package/dist/suppression/compute-burst-key.d.ts +0 -4
  19. package/dist/suppression/compute-burst-key.d.ts.map +1 -1
  20. package/dist/suppression/create-suppression.d.ts +9 -2
  21. package/dist/suppression/create-suppression.d.ts.map +1 -1
  22. package/dist/suppression/identity-needs-message-template.d.ts +3 -1
  23. package/dist/suppression/identity-needs-message-template.d.ts.map +1 -1
  24. package/dist/suppression/parameterize-message.d.ts.map +1 -1
  25. package/dist/transport/create-transport.d.ts.map +1 -1
  26. package/dist/transport/send-batch.d.ts +1 -0
  27. package/dist/transport/send-batch.d.ts.map +1 -1
  28. package/dist/transport/transport-types.d.ts +6 -2
  29. package/dist/transport/transport-types.d.ts.map +1 -1
  30. package/package.json +1 -1
@@ -1,19 +1,6 @@
1
1
  import type { RequestContext } from '../create-ingest-event/create-ingest-event-types';
2
2
  import type { TransportFetch } from '../transport/transport-types';
3
- /**
4
- * Options passed to `RasputinInit` / `createClient`.
5
- *
6
- * Typical setup — call once at startup, then let the SDK capture errors for you:
7
- *
8
- * ```ts
9
- * RasputinInit({
10
- * projectApiKey: 'rp_…', // from your Rasputin project settings
11
- * environment: 'production', // e.g. production | staging | preview
12
- * release: process.env.GIT_SHA, // optional — auto-detected from CI env when omitted
13
- * });
14
- * ```
15
- */
16
- export type RasputinOptions = {
3
+ type RasputinBaseOptions = {
17
4
  /** Project API key from the Rasputin dashboard. Required to send events. */
18
5
  projectApiKey: string;
19
6
  /**
@@ -22,11 +9,14 @@ export type RasputinOptions = {
22
9
  */
23
10
  environment: string;
24
11
  /**
25
- * Deploy identifier, usually a git commit SHA.
26
- * Links errors to a release so you can see what shipped when something broke.
12
+ * Git commit SHA for this deploy. Links errors to a release so you can see
13
+ * what shipped when something broke.
27
14
  *
28
- * When omitted, Rasputin reads common CI env vars (`VERCEL_GIT_COMMIT_SHA`,
29
- * `GIT_COMMIT`, etc.). Set explicitly if auto-detection does not match your setup.
15
+ * Prefer setting the env var (no code change needed on each deploy):
16
+ * `release: process.env.RASPUTIN_RELEASE`
17
+ *
18
+ * When omitted, Rasputin reads `RASPUTIN_RELEASE`, then platform vars
19
+ * (`VERCEL_GIT_COMMIT_SHA`, etc.), then `.git` HEAD. Must be 7–40 hex chars.
30
20
  */
31
21
  release?: string | null;
32
22
  /**
@@ -37,17 +27,33 @@ export type RasputinOptions = {
37
27
  */
38
28
  enabled?: boolean;
39
29
  apiUrl?: string;
40
- /**
41
- * Root of your app on disk. Stack traces are trimmed to paths relative to this
42
- * (e.g. `src/routes/users.ts` instead of `/app/src/routes/users.ts`).
43
- *
44
- * In a monorepo, pass the **repository root**, not the package directory, so
45
- * paths line up with GitHub.
46
- *
47
- * @default `process.cwd()`
48
- */
49
- repoRoot?: string;
50
30
  };
31
+ /**
32
+ * Options passed to `RasputinInit` / `createClient`.
33
+ *
34
+ * Typical setup — call once at startup, then let the SDK capture errors for you:
35
+ *
36
+ * ```ts
37
+ * RasputinInit({
38
+ * projectApiKey: 'rp_…', // from your Rasputin project settings
39
+ * environment: 'production', // e.g. production | staging | preview
40
+ * release: process.env.RASPUTIN_RELEASE,
41
+ * moduleUrl: import.meta.url, // from the file where you call this
42
+ * });
43
+ * ```
44
+ *
45
+ * Pass `moduleUrl: import.meta.url` from the file where you call this.
46
+ * Rasputin uses it to find your project folder so error paths match your source.
47
+ * If that isn't right (for example in Docker), set `repoRoot` to the folder
48
+ * you open in your editor instead.
49
+ */
50
+ export type RasputinOptions = RasputinBaseOptions & ({
51
+ repoRoot: string;
52
+ moduleUrl?: string;
53
+ } | {
54
+ moduleUrl: string;
55
+ repoRoot?: string;
56
+ });
51
57
  /**
52
58
  * Extra context for a captured error.
53
59
  * Framework plugins (Elysia, etc.) fill `request` automatically for route failures.
@@ -107,4 +113,5 @@ export type CreateClientHooks = {
107
113
  sdkVersion?: string;
108
114
  sdkName?: string;
109
115
  };
116
+ export {};
110
117
  //# sourceMappingURL=create-client-types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-client-types.d.ts","sourceRoot":"","sources":["../../src/create-client/create-client-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kDAAkD,CAAC;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAEnE;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,eAAe,GAAG;IAC7B,4EAA4E;IAC5E,aAAa,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAExB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG;IAC5B;;;OAGG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC;CACzB,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG;IAC5B;;;;;;;;OAQG;IACH,gBAAgB,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;IAErE;;;;;;;;;;;;OAYG;IACH,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7C;;;;;OAKG;IACH,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B,CAAC;AAEF,2FAA2F;AAC3F,MAAM,MAAM,iBAAiB,GAAG;IAC/B,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,8BAA8B,CAAC,EAAE,OAAO,CAAC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC"}
1
+ {"version":3,"file":"create-client-types.d.ts","sourceRoot":"","sources":["../../src/create-client/create-client-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kDAAkD,CAAC;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAEnE,KAAK,mBAAmB,GAAG;IAC1B,4EAA4E;IAC5E,aAAa,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAExB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,eAAe,GAAG,mBAAmB,GAChD,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEvF;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG;IAC5B;;;OAGG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC;CACzB,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG;IAC5B;;;;;;;;OAQG;IACH,gBAAgB,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,cAAc,KAAK,IAAI,CAAC;IAErE;;;;;;;;;;;;OAYG;IACH,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7C;;;;;OAKG;IACH,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B,CAAC;AAEF,2FAA2F;AAC3F,MAAM,MAAM,iBAAiB,GAAG;IAC/B,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,8BAA8B,CAAC,EAAE,OAAO,CAAC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"create-client.d.ts","sourceRoot":"","sources":["../../src/create-client/create-client.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAuBhG,0DAA0D;AAC1D,eAAO,MAAM,sBAAsB,YAGlC,CAAC;AAwBF;;;GAGG;AACH,eAAO,MAAM,YAAY,GACxB,SAAS,eAAe,EACxB,QAAO,iBAAsB,KAC3B,cA8GF,CAAC"}
1
+ {"version":3,"file":"create-client.d.ts","sourceRoot":"","sources":["../../src/create-client/create-client.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAuBhG,0DAA0D;AAC1D,eAAO,MAAM,sBAAsB,YAGlC,CAAC;AAaF;;;GAGG;AACH,eAAO,MAAM,YAAY,GACxB,SAAS,eAAe,EACxB,QAAO,iBAAsB,KAC3B,cAuJF,CAAC"}
@@ -1,12 +1,15 @@
1
1
  type DetectReleaseOptions = {
2
- /** Explicit release (e.g. from RasputinInit). Wins over env when non-empty. */
2
+ /** Explicit release (e.g. from RasputinInit). Wins over env when a valid SHA. */
3
3
  release?: string | null;
4
4
  /** Defaults to `process.env` — override in tests. */
5
5
  env?: Record<string, string | undefined>;
6
+ /** Directory to probe for `.git` — defaults to `process.cwd()`. Override in tests. */
7
+ cwd?: string;
6
8
  };
7
9
  /**
8
10
  * Resolve the deploy release / commit SHA.
9
- * Order: explicit option → platform env vars generic git env vars → undefined (warns).
11
+ * Order: explicit option → `RASPUTIN_RELEASE` / platform env → `.git` HEAD → undefined (warns).
12
+ * Non-SHA values are skipped with a warning so ancestry checks never see `v1.2.3` or branch names.
10
13
  */
11
14
  export declare const detectRelease: (options?: DetectReleaseOptions) => string | undefined;
12
15
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"detect-release.d.ts","sourceRoot":"","sources":["../../src/detect-release/detect-release.ts"],"names":[],"mappings":"AAAA,KAAK,oBAAoB,GAAG;IAC3B,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,qDAAqD;IACrD,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CACzC,CAAC;AAYF;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAI,UAAS,oBAAyB,KAAG,MAAM,GAAG,SAiB3E,CAAC"}
1
+ {"version":3,"file":"detect-release.d.ts","sourceRoot":"","sources":["../../src/detect-release/detect-release.ts"],"names":[],"mappings":"AAGA,KAAK,oBAAoB,GAAG;IAC3B,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,qDAAqD;IACrD,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,sFAAsF;IACtF,GAAG,CAAC,EAAE,MAAM,CAAC;CACb,CAAC;AAkDF;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,UAAS,oBAAyB,KAAG,MAAM,GAAG,SAyB3E,CAAC"}
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ export type { Frame } from './normalize-frames/normalize-frames';
9
9
  export { normalizeFrames } from './normalize-frames/normalize-frames';
10
10
  export { parseStack } from './parse-stack/parse-stack';
11
11
  export type { RawFrame } from './parse-stack/raw-frame';
12
+ export { repoRootFrom } from './repo-root/repo-root-from';
12
13
  export { resolveSourceMaps } from './resolve-source-maps/resolve-source-maps';
13
14
  export type { ResolvedFrame, ResolveSourceMapsResult, } from './resolve-source-maps/resolved-frame';
14
15
  export { createSuppression } from './suppression/create-suppression';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,YAAY,EACX,cAAc,EACd,cAAc,EACd,eAAe,GACf,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAC9E,YAAY,EAAE,cAAc,EAAE,MAAM,iDAAiD,CAAC;AACtF,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,YAAY,EAAE,KAAK,EAAE,MAAM,qCAAqC,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,YAAY,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAC9E,YAAY,EACX,aAAa,EACb,uBAAuB,GACvB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,YAAY,EACX,wBAAwB,EACxB,aAAa,EACb,oBAAoB,EACpB,eAAe,EACf,sBAAsB,EACtB,uBAAuB,GACvB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,YAAY,EACX,sBAAsB,EACtB,SAAS,EACT,iBAAiB,GACjB,MAAM,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,YAAY,EACX,cAAc,EACd,cAAc,EACd,eAAe,GACf,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAC9E,YAAY,EAAE,cAAc,EAAE,MAAM,iDAAiD,CAAC;AACtF,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,YAAY,EAAE,KAAK,EAAE,MAAM,qCAAqC,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,YAAY,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAC9E,YAAY,EACX,aAAa,EACb,uBAAuB,GACvB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AACrE,YAAY,EACX,wBAAwB,EACxB,aAAa,EACb,oBAAoB,EACpB,eAAe,EACf,sBAAsB,EACtB,uBAAuB,GACvB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,YAAY,EACX,sBAAsB,EACtB,SAAS,EACT,iBAAiB,GACjB,MAAM,6BAA6B,CAAC"}
package/dist/index.js CHANGED
@@ -111,25 +111,62 @@ var finalizeResolution = (resolution, stackFrames) => {
111
111
  };
112
112
 
113
113
  // src/detect-release/detect-release.ts
114
+ import { readFileSync } from "node:fs";
115
+ import { join } from "node:path";
114
116
  var ENV_KEYS = [
117
+ "RASPUTIN_RELEASE",
115
118
  "VERCEL_GIT_COMMIT_SHA",
116
119
  "RAILWAY_GIT_COMMIT_SHA",
117
120
  "RENDER_GIT_COMMIT",
118
- "SOURCE_VERSION",
121
+ "COMMIT_REF",
122
+ "CF_PAGES_COMMIT_SHA",
123
+ "HEROKU_SLUG_COMMIT",
119
124
  "GIT_COMMIT",
120
125
  "COMMIT_SHA",
121
126
  "GIT_SHA"
122
127
  ];
128
+ var SHA_RE = /^[0-9a-f]{7,40}$/i;
129
+ var isSha = (value) => SHA_RE.test(value);
130
+ var warnInvalid = (source, value) => {
131
+ console.warn(
132
+ `[rasputin] Ignoring invalid release from ${source}: "${value}". Expected a git commit SHA (7\u201340 hex characters).`
133
+ );
134
+ };
135
+ var readGitHead = (cwd) => {
136
+ try {
137
+ const gitDir = join(cwd, ".git");
138
+ const head = readFileSync(join(gitDir, "HEAD"), "utf8").trim();
139
+ if (isSha(head)) return head;
140
+ const ref = head.replace(/^ref:\s*/, "");
141
+ try {
142
+ const sha = readFileSync(join(gitDir, ref), "utf8").trim();
143
+ return isSha(sha) ? sha : void 0;
144
+ } catch {
145
+ const packed = readFileSync(join(gitDir, "packed-refs"), "utf8");
146
+ const match = packed.match(new RegExp(`^([0-9a-f]{40}) ${ref}$`, "m"));
147
+ return match?.[1];
148
+ }
149
+ } catch {
150
+ return void 0;
151
+ }
152
+ };
123
153
  var detectRelease = (options = {}) => {
124
154
  const explicit = options.release?.trim();
125
- if (explicit) return explicit;
155
+ if (explicit) {
156
+ if (isSha(explicit)) return explicit;
157
+ warnInvalid("release option", explicit);
158
+ }
126
159
  const env = options.env ?? process.env;
127
160
  for (const key of ENV_KEYS) {
128
161
  const value = env[key]?.trim();
129
- if (value) return value;
162
+ if (!value) continue;
163
+ if (isSha(value)) return value;
164
+ warnInvalid(key, value);
130
165
  }
166
+ const fromGit = readGitHead(options.cwd ?? process.cwd());
167
+ if (fromGit) return fromGit;
131
168
  console.warn(
132
- "[rasputin] No release detected. Set `release` in RasputinInit or a commit SHA env var (e.g. GIT_COMMIT, VERCEL_GIT_COMMIT_SHA). Without a release, deploy correlation and diagnosis confidence are degraded."
169
+ "[rasputin] No release detected. Set `RASPUTIN_RELEASE` to your git commit SHA, pass `release` in RasputinInit, or deploy on a platform that exposes a commit SHA env var (e.g. VERCEL_GIT_COMMIT_SHA). Without a release, deploy correlation and diagnosis confidence are degraded."
133
170
  );
134
171
  return void 0;
135
172
  };
@@ -168,8 +205,8 @@ var isInApp = (file, underRoot) => {
168
205
  if (/(^|\/)node_modules\//.test(file)) return false;
169
206
  return true;
170
207
  };
171
- var normalizeFrames = (frames, options = {}) => {
172
- const appRoot = options.appRoot ?? process.cwd();
208
+ var normalizeFrames = (frames, options) => {
209
+ const appRoot = options.appRoot;
173
210
  return frames.map((frame) => {
174
211
  const generated = normalizeFile(frame.generated.file, appRoot);
175
212
  const original = frame.original ? normalizeFile(frame.original.file, appRoot) : void 0;
@@ -250,10 +287,93 @@ var parseStack = (stack) => {
250
287
  return frames;
251
288
  };
252
289
 
290
+ // src/repo-root/repo-root-from.ts
291
+ import { existsSync } from "node:fs";
292
+ import { dirname, join as join2, resolve as resolve2 } from "node:path";
293
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
294
+ var isGitRoot = (dir) => existsSync(join2(dir, ".git"));
295
+ var hasPackageJson = (dir) => existsSync(join2(dir, "package.json"));
296
+ var repoRootFrom = (moduleUrl) => {
297
+ let current = resolve2(dirname(fileURLToPath2(moduleUrl)));
298
+ let packageRoot;
299
+ while (true) {
300
+ if (isGitRoot(current)) return current;
301
+ if (hasPackageJson(current)) packageRoot = current;
302
+ const parent = dirname(current);
303
+ if (parent === current) break;
304
+ current = parent;
305
+ }
306
+ return packageRoot ?? resolve2(dirname(fileURLToPath2(moduleUrl)));
307
+ };
308
+
309
+ // src/repo-root/resolve-repo-root.ts
310
+ var resolveRepoRoot = (options) => {
311
+ const repoRoot = options.repoRoot?.trim();
312
+ if (repoRoot) return repoRoot;
313
+ const moduleUrl = options.moduleUrl?.trim();
314
+ if (moduleUrl) return repoRootFrom(moduleUrl);
315
+ return void 0;
316
+ };
317
+
318
+ // src/repo-root/warn-repo-root-setup.ts
319
+ var BUILD_OUTPUT_SEGMENTS = /* @__PURE__ */ new Set(["dist", "build", "out", ".next", "coverage"]);
320
+ var warnedMissing = false;
321
+ var warnedSuspicious = false;
322
+ var BANNER = "======== [rasputin] WARNING";
323
+ var looksLikeBuildOutputRoot = (root) => {
324
+ const posix = root.replaceAll("\\", "/").replace(/\/+$/, "");
325
+ return posix.split("/").some((segment) => BUILD_OUTPUT_SEGMENTS.has(segment));
326
+ };
327
+ var warnIfMissingRepoRoot = () => {
328
+ if (warnedMissing) return;
329
+ warnedMissing = true;
330
+ console.warn(
331
+ `
332
+
333
+ ${BANNER}: we don't know where your project is ========
334
+ Rasputin needs to know which folder your app lives in so errors show
335
+ the right file paths in the dashboard.
336
+
337
+ Add this to RasputinInit (from the same file you call it in):
338
+
339
+ moduleUrl: import.meta.url
340
+
341
+ Or set repoRoot to the folder you open in your editor.
342
+
343
+ Nothing will be sent until this is set.
344
+ ======================================================================
345
+ `
346
+ );
347
+ };
348
+ var warnIfSuspiciousRepoRoot = (root) => {
349
+ if (warnedSuspicious || !looksLikeBuildOutputRoot(root)) return;
350
+ warnedSuspicious = true;
351
+ console.warn(
352
+ `
353
+
354
+ ${BANNER}: this looks like a build folder, not your project ========
355
+ Rasputin thinks your project is here:
356
+ ${root}
357
+
358
+ That's usually compiled output (dist, build, .next, \u2026), not the folder
359
+ you write code in. Error locations in the dashboard will be wrong.
360
+
361
+ Point Rasputin at the folder you open in your editor.
362
+
363
+ Easiest fix \u2014 from the file where you call RasputinInit:
364
+
365
+ moduleUrl: import.meta.url
366
+
367
+ Or set repoRoot yourself.
368
+ ======================================================================
369
+ `
370
+ );
371
+ };
372
+
253
373
  // src/resolve-source-maps/resolve-source-maps.ts
254
374
  import { readFile } from "node:fs/promises";
255
- import { dirname, isAbsolute as isAbsolute2, join } from "node:path";
256
- import { fileURLToPath as fileURLToPath2 } from "node:url";
375
+ import { dirname as dirname2, isAbsolute as isAbsolute2, join as join3 } from "node:path";
376
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
257
377
  import { LEAST_UPPER_BOUND, originalPositionFor, TraceMap } from "@jridgewell/trace-mapping";
258
378
  var SOURCE_EXT2 = /\.(ts|tsx|mts|cts)$/i;
259
379
  var SOURCEMAP_COMMENT = /(?:\/\/[#@][ \t]*sourceMappingURL=([^\s'"]+)|\/\*[#@][ \t]*sourceMappingURL=([^\s*'"]+)[ \t]*\*\/)\s*$/;
@@ -261,7 +381,7 @@ var mapCache = /* @__PURE__ */ new Map();
261
381
  var toPath = (file) => {
262
382
  if (file.startsWith("file://")) {
263
383
  try {
264
- return fileURLToPath2(file);
384
+ return fileURLToPath3(file);
265
385
  } catch {
266
386
  return file;
267
387
  }
@@ -302,7 +422,7 @@ var readMapPayload = async (generatedPath) => {
302
422
  if (comment?.startsWith("data:")) return decodeDataUrl(comment);
303
423
  if (comment) {
304
424
  try {
305
- return await readFile(join(dirname(generatedPath), comment), "utf8");
425
+ return await readFile(join3(dirname2(generatedPath), comment), "utf8");
306
426
  } catch {
307
427
  }
308
428
  }
@@ -349,7 +469,7 @@ var resolveFrame = async (raw) => {
349
469
  return {
350
470
  ...base,
351
471
  original: {
352
- file: isAbsolute2(pos.source) ? pos.source : join(dirname(generatedPath), pos.source),
472
+ file: isAbsolute2(pos.source) ? pos.source : join3(dirname2(generatedPath), pos.source),
353
473
  line: pos.line,
354
474
  column: pos.column ?? void 0,
355
475
  name: pos.name ?? void 0
@@ -369,7 +489,7 @@ var resolveSourceMaps = async (frames) => {
369
489
 
370
490
  // src/sdk-meta.ts
371
491
  var SDK_NAME = "@rasputin-ai/core";
372
- var SDK_VERSION = "0.1.4";
492
+ var SDK_VERSION = "0.2.0";
373
493
 
374
494
  // src/suppression/identity-needs-message-template.ts
375
495
  var identityNeedsMessageTemplate = (type, code) => {
@@ -431,14 +551,22 @@ var RULES = [
431
551
  pattern: /\b(?=[a-z0-9]*[a-z])(?=[a-z0-9]*\d)[a-z0-9]{8,}\b/giu
432
552
  }
433
553
  ];
434
- var parameterizeMessage = (rawMessage) => {
435
- if (rawMessage.length > MAX_INPUT_LENGTH) return rawMessage;
436
- const normalized = rawMessage.normalize("NFKC").replace(/\r\n?/g, "\n").trim();
554
+ var normalizeForIdentity = (normalized) => {
555
+ try {
556
+ return JSON.stringify(JSON.parse(normalized));
557
+ } catch {
558
+ }
437
559
  const lines = normalized.split("\n").filter((line) => line.trim());
438
560
  let result = lines.slice(0, 2).join("\n");
439
561
  if (result !== normalized) {
440
562
  result += "...";
441
563
  }
564
+ return result;
565
+ };
566
+ var parameterizeMessage = (rawMessage) => {
567
+ if (rawMessage.length > MAX_INPUT_LENGTH) return rawMessage;
568
+ const normalized = rawMessage.normalize("NFKC").replace(/\r\n?/g, "\n").trim();
569
+ let result = normalizeForIdentity(normalized);
442
570
  result = result.replace(/\b([A-Za-z_][\w.-]*)=(["'])(?:\\.|(?!\2).)*\2/gu, "$1=<string>");
443
571
  result = result.replace(/\b([A-Za-z_][\w.-]*)=(?:true|false)\b/giu, "$1=<bool>");
444
572
  for (const rule of RULES) {
@@ -454,21 +582,43 @@ var parameterizeMessage = (rawMessage) => {
454
582
  };
455
583
 
456
584
  // src/suppression/compute-burst-key.ts
585
+ var FRAME_COUNT = 4;
457
586
  var preferredFile = (frame) => (frame.original ?? frame.generated).file;
458
- var frameBurstParts = (frames) => {
459
- return frames.filter((frame) => frame.in_app).slice(0, 3).map((frame) => {
587
+ var normalizeFunction = (raw) => {
588
+ if (!raw) return "";
589
+ const name = raw.trim();
590
+ if (name === "<anonymous>" || name === "anonymous") return "";
591
+ return name.replace(/^(async|new|get|set|bound)\s+/, "").replace(/^Object\./, "").replace(/\s+\[as .+\]$/, "");
592
+ };
593
+ var collapseConsecutive = (frames) => {
594
+ const out = [];
595
+ let previousKey = null;
596
+ for (const frame of frames) {
460
597
  const file = preferredFile(frame);
461
- const fn = frame.function ?? frame.original?.name ?? "";
462
- if (!frame.original && frame.generated.line !== void 0) {
463
- return `${file}:${fn}:${frame.generated.line}:${frame.generated.column ?? 0}`;
598
+ const fn = normalizeFunction(frame.function ?? frame.original?.name);
599
+ const key = `${file}#${fn}`;
600
+ if (key !== previousKey) {
601
+ out.push(frame);
602
+ previousKey = key;
464
603
  }
604
+ }
605
+ return out;
606
+ };
607
+ var frameBurstParts = (frames) => {
608
+ const collapsed = collapseConsecutive(frames);
609
+ const inApp = collapsed.filter((frame) => frame.in_app);
610
+ const selected = (inApp.length > 0 ? inApp : collapsed).slice(0, FRAME_COUNT);
611
+ return selected.map((frame) => {
612
+ const file = preferredFile(frame);
613
+ const fn = normalizeFunction(frame.function ?? frame.original?.name);
465
614
  return `${file}:${fn}`;
466
615
  });
467
616
  };
468
617
  var computeBurstKey = (errorType, message, code, frames) => {
469
618
  const parts = frameBurstParts(frames);
470
- let key = `${errorType}\0${parts.join("\0")}`;
471
- if (identityNeedsMessageTemplate(errorType, code)) {
619
+ const codePart = code ?? "";
620
+ let key = `${errorType}\0${codePart}\0${parts.join("\0")}`;
621
+ if (parts.length === 0 || identityNeedsMessageTemplate(errorType, code)) {
472
622
  key += `\0${parameterizeMessage(message)}`;
473
623
  }
474
624
  return key;
@@ -579,7 +729,7 @@ var createSuppression = (options = {}) => {
579
729
  bucket.suppressed += 1;
580
730
  return { action: "suppress" };
581
731
  };
582
- const takeSummaries = async (now = Date.now(), prepareSummary) => {
732
+ const drainSummaries = (now = Date.now()) => {
583
733
  const drafts = pending.splice(0, pending.length);
584
734
  for (const bucket of burstBuckets.values()) {
585
735
  if (bucket.suppressed > 0) {
@@ -587,22 +737,32 @@ var createSuppression = (options = {}) => {
587
737
  bucket.suppressed = 0;
588
738
  }
589
739
  }
740
+ return drafts.map((draft) => ({
741
+ summary: toSummary(draft, draft.identity),
742
+ rawFrames: draft.captureRawFrames
743
+ }));
744
+ };
745
+ const takeSummaries = async (now = Date.now(), prepareSummary) => {
746
+ const drafts = drainSummaries(now);
747
+ if (!prepareSummary) return drafts.map((draft) => draft.summary);
590
748
  const out = [];
591
749
  for (const draft of drafts) {
592
- let identity = draft.identity;
593
- if (prepareSummary && draft.captureRawFrames.length > 0) {
594
- try {
595
- const resolved = await prepareSummary(draft.captureRawFrames);
596
- identity = { ...identity, ...resolved };
597
- } catch {
598
- }
750
+ if (draft.rawFrames.length === 0) {
751
+ out.push(draft.summary);
752
+ continue;
753
+ }
754
+ try {
755
+ const resolved = await prepareSummary(draft.rawFrames);
756
+ out.push({ ...draft.summary, ...resolved });
757
+ } catch {
758
+ out.push(draft.summary);
599
759
  }
600
- out.push(toSummary(draft, identity));
601
760
  }
602
761
  return out;
603
762
  };
604
763
  return {
605
764
  decide,
765
+ drainSummaries,
606
766
  takeSummaries,
607
767
  /** Test helper — number of active burst buckets. */
608
768
  size: () => burstBuckets.size
@@ -611,7 +771,21 @@ var createSuppression = (options = {}) => {
611
771
 
612
772
  // src/transport/send-batch.ts
613
773
  import { gzipSync } from "node:zlib";
614
- var sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
774
+ var sleep = (ms, signal) => new Promise((resolve3) => {
775
+ if (signal?.aborted) {
776
+ resolve3();
777
+ return;
778
+ }
779
+ const timer = setTimeout(resolve3, ms);
780
+ signal?.addEventListener(
781
+ "abort",
782
+ () => {
783
+ clearTimeout(timer);
784
+ resolve3();
785
+ },
786
+ { once: true }
787
+ );
788
+ });
615
789
  var backoffMs = (attempt) => {
616
790
  const base = Math.min(3e4, 500 * 2 ** attempt);
617
791
  return Math.floor(base * (0.5 + Math.random()));
@@ -634,6 +808,7 @@ var sendBatch = async (options) => {
634
808
  }
635
809
  if (options.sdkVersion) payload.sdk_version = options.sdkVersion;
636
810
  if (options.sdkName) payload.sdk_name = options.sdkName;
811
+ if (options.idempotencyKey) payload.idempotency_key = options.idempotencyKey;
637
812
  const json = JSON.stringify(payload);
638
813
  const headers = {
639
814
  Authorization: `Bearer ${options.projectApiKey}`,
@@ -661,16 +836,16 @@ var sendBatch = async (options) => {
661
836
  if (response.status === 429) {
662
837
  if (attempt === options.maxRetries) return "failed";
663
838
  const wait = retryAfterMs(response) ?? backoffMs(attempt);
664
- await sleep(wait);
839
+ await sleep(wait, options.signal);
665
840
  continue;
666
841
  }
667
842
  if (response.status >= 400 && response.status < 500) return "dropped";
668
843
  if (attempt === options.maxRetries) return "failed";
669
- await sleep(backoffMs(attempt));
844
+ await sleep(backoffMs(attempt), options.signal);
670
845
  } catch {
671
846
  if (options.signal?.aborted) return "failed";
672
847
  if (attempt === options.maxRetries) return "failed";
673
- await sleep(backoffMs(attempt));
848
+ await sleep(backoffMs(attempt), options.signal);
674
849
  }
675
850
  }
676
851
  return "failed";
@@ -725,6 +900,20 @@ var DEFAULTS2 = {
725
900
  maxRetries: 5,
726
901
  installSignalHandlers: true
727
902
  };
903
+ var removeProcessListener = process.off.bind(process);
904
+ var holdProcessExitUntil = (done) => {
905
+ const nativeExit = process.exit.bind(process);
906
+ let heldCode;
907
+ const restore = () => {
908
+ process.exit = nativeExit;
909
+ if (heldCode !== void 0) nativeExit(heldCode);
910
+ };
911
+ const holdExit = ((code) => {
912
+ heldCode = code ?? 0;
913
+ });
914
+ process.exit = holdExit;
915
+ void done.finally(restore);
916
+ };
728
917
  var createTransport = (options) => {
729
918
  const enabled = options.enabled ?? DEFAULTS2.enabled;
730
919
  const batchSize = options.batchSize ?? DEFAULTS2.batchSize;
@@ -736,6 +925,8 @@ var createTransport = (options) => {
736
925
  let timer;
737
926
  let flushing;
738
927
  let closed = false;
928
+ let shuttingDown = false;
929
+ let retryBatch = null;
739
930
  const schedule = () => {
740
931
  if (timer || !enabled || closed) return;
741
932
  timer = setInterval(() => {
@@ -748,9 +939,19 @@ var createTransport = (options) => {
748
939
  clearInterval(timer);
749
940
  timer = void 0;
750
941
  };
942
+ const prepareSummary = async (item) => {
943
+ if (!options.prepareSummary || item.rawFrames.length === 0) return item.summary;
944
+ try {
945
+ const resolved = await options.prepareSummary(item.rawFrames);
946
+ return { ...item.summary, ...resolved };
947
+ } catch {
948
+ return item.summary;
949
+ }
950
+ };
751
951
  const flush = async (timeoutMs = 1e4) => {
752
952
  if (!enabled || closed) {
753
953
  queue.clear();
954
+ retryBatch = null;
754
955
  return;
755
956
  }
756
957
  if (flushing) return flushing;
@@ -758,15 +959,17 @@ var createTransport = (options) => {
758
959
  const controller = new AbortController();
759
960
  const timeout = timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : void 0;
760
961
  try {
761
- while (queue.size() > 0) {
962
+ while (queue.size() > 0 || retryBatch) {
762
963
  if (controller.signal.aborted) break;
763
- const batch = queue.drain(batchSize);
964
+ const batch = retryBatch?.items ?? queue.drain(batchSize);
965
+ const idempotencyKey = retryBatch?.idempotencyKey ?? crypto.randomUUID();
966
+ retryBatch = null;
764
967
  if (batch.length === 0) break;
765
968
  const events = [];
766
969
  const summaries = [];
767
970
  for (const item of batch) {
768
971
  if (item.kind === "summary") {
769
- summaries.push(item.summary);
972
+ summaries.push(await prepareSummary(item));
770
973
  continue;
771
974
  }
772
975
  if (options.prepareEvent && item.rawFrames.length > 0) {
@@ -789,6 +992,7 @@ var createTransport = (options) => {
789
992
  projectApiKey: options.projectApiKey,
790
993
  events,
791
994
  summaries,
995
+ idempotencyKey,
792
996
  sdkVersion: options.sdkVersion,
793
997
  sdkName: options.sdkName,
794
998
  gzipThresholdBytes,
@@ -797,13 +1001,13 @@ var createTransport = (options) => {
797
1001
  signal: controller.signal
798
1002
  });
799
1003
  if (result === "failed") {
800
- for (const item of batch) queue.enqueue(item);
1004
+ retryBatch = { items: batch, idempotencyKey };
801
1005
  break;
802
1006
  }
803
1007
  }
804
1008
  } finally {
805
1009
  if (timeout) clearTimeout(timeout);
806
- if (queue.size() === 0) stopTimer();
1010
+ if (queue.size() === 0 && !retryBatch) stopTimer();
807
1011
  flushing = void 0;
808
1012
  }
809
1013
  })();
@@ -815,38 +1019,46 @@ var createTransport = (options) => {
815
1019
  schedule();
816
1020
  if (queue.size() >= batchSize) void flush();
817
1021
  };
818
- const enqueueSummary = (summary) => {
1022
+ const enqueueSummary = (summary, rawFrames = []) => {
819
1023
  if (!enabled || closed) return;
820
- queue.enqueue({ kind: "summary", summary, priority: "summary" });
1024
+ queue.enqueue({ kind: "summary", summary, priority: "summary", rawFrames });
821
1025
  schedule();
822
1026
  if (queue.size() >= batchSize) void flush();
823
1027
  };
1028
+ const onBeforeExit = () => {
1029
+ if (closed || shuttingDown) return;
1030
+ if (queue.size() === 0 && !retryBatch && !flushing) return;
1031
+ void flush(2e3).catch(() => {
1032
+ });
1033
+ };
824
1034
  const onSignal = () => {
825
- try {
826
- void flush(2e3);
827
- } catch {
828
- }
1035
+ if (shuttingDown) return;
1036
+ shuttingDown = true;
1037
+ stopTimer();
1038
+ holdProcessExitUntil(flush(2e3).catch(() => {
1039
+ }));
829
1040
  };
830
1041
  if (enabled && (options.installSignalHandlers ?? DEFAULTS2.installSignalHandlers)) {
831
- process.on("beforeExit", onSignal);
1042
+ process.on("beforeExit", onBeforeExit);
832
1043
  process.on("SIGTERM", onSignal);
833
1044
  process.on("SIGINT", onSignal);
834
1045
  }
835
1046
  const close = async () => {
836
- closed = true;
837
1047
  stopTimer();
838
- process.off("beforeExit", onSignal);
839
- process.off("SIGTERM", onSignal);
840
- process.off("SIGINT", onSignal);
1048
+ removeProcessListener("beforeExit", onBeforeExit);
1049
+ removeProcessListener("SIGTERM", onSignal);
1050
+ removeProcessListener("SIGINT", onSignal);
841
1051
  await flush(2e3);
1052
+ closed = true;
842
1053
  queue.clear();
1054
+ retryBatch = null;
843
1055
  };
844
1056
  return {
845
1057
  enqueue,
846
1058
  enqueueSummary,
847
1059
  flush,
848
1060
  close,
849
- size: () => queue.size()
1061
+ size: () => queue.size() + (retryBatch?.items.length ?? 0)
850
1062
  };
851
1063
  };
852
1064
 
@@ -888,12 +1100,6 @@ var warnPartialResolution = () => {
888
1100
  "[rasputin] Source map paths could not be normalized to the repo root; fingerprinting may be degraded"
889
1101
  );
890
1102
  };
891
- var prepareResolvedEvent = (event, normalized, resolution) => {
892
- const stackFrames = framesToStackFrames(normalized);
893
- const finalResolution = finalizeResolution(resolution, stackFrames);
894
- if (finalResolution === "partial") warnPartialResolution();
895
- return withResolvedFrames(event, normalized, finalResolution);
896
- };
897
1103
  var toResolvedFrames = (raw) => raw.map((frame) => ({
898
1104
  generated: { file: frame.file, line: frame.line, column: frame.column },
899
1105
  ...frame.function ? { function: frame.function } : {}
@@ -909,7 +1115,35 @@ var createClient = (options, hooks = {}) => {
909
1115
  const release = detectRelease({ release: options.release });
910
1116
  const environment = options.environment;
911
1117
  const { ingestUrl, deployUrl } = resolveEndpointUrls(options.apiUrl);
912
- const repoRoot = options.repoRoot;
1118
+ const repoRoot = resolveRepoRoot(options);
1119
+ if (!repoRoot) {
1120
+ warnIfMissingRepoRoot();
1121
+ return noopClient();
1122
+ }
1123
+ warnIfSuspiciousRepoRoot(repoRoot);
1124
+ const MAX_STACK_RESOLVE_CACHE = 32;
1125
+ const stackResolveCache = /* @__PURE__ */ new Map();
1126
+ const stackCacheKey = (rawFrames) => rawFrames.map((frame) => `${frame.file}\0${frame.line}\0${frame.column}\0${frame.function ?? ""}`).join("\n");
1127
+ const resolveStack = async (rawFrames) => {
1128
+ const key = stackCacheKey(rawFrames);
1129
+ const cached = stackResolveCache.get(key);
1130
+ if (cached) return cached;
1131
+ const pending = (async () => {
1132
+ const { frames, resolution, unresolvedGenerated } = await resolveSourceMaps(rawFrames);
1133
+ warnUnresolvedMaps(unresolvedGenerated);
1134
+ const normalized = normalizeFrames(frames, { appRoot: repoRoot });
1135
+ const stack_frames = framesToStackFrames(normalized);
1136
+ const finalResolution = finalizeResolution(resolution, stack_frames);
1137
+ if (finalResolution === "partial") warnPartialResolution();
1138
+ return { frames: normalized, stack_frames, resolution: finalResolution };
1139
+ })();
1140
+ stackResolveCache.set(key, pending);
1141
+ if (stackResolveCache.size > MAX_STACK_RESOLVE_CACHE) {
1142
+ const oldest = stackResolveCache.keys().next().value;
1143
+ if (oldest !== void 0) stackResolveCache.delete(oldest);
1144
+ }
1145
+ return pending;
1146
+ };
913
1147
  const transport = createTransport({
914
1148
  ingestUrl,
915
1149
  projectApiKey,
@@ -919,10 +1153,12 @@ var createClient = (options, hooks = {}) => {
919
1153
  sdkVersion: hooks.sdkVersion ?? SDK_VERSION,
920
1154
  sdkName: hooks.sdkName ?? SDK_NAME,
921
1155
  prepareEvent: async ({ event, rawFrames }) => {
922
- const { frames, resolution, unresolvedGenerated } = await resolveSourceMaps(rawFrames);
923
- warnUnresolvedMaps(unresolvedGenerated);
924
- const normalized = normalizeFrames(frames, { appRoot: repoRoot });
925
- return prepareResolvedEvent(event, normalized, resolution);
1156
+ const prepared = await resolveStack(rawFrames);
1157
+ return withResolvedFrames(event, prepared.frames, prepared.resolution);
1158
+ },
1159
+ prepareSummary: async (rawFrames) => {
1160
+ const prepared = await resolveStack(rawFrames);
1161
+ return { stack_frames: prepared.stack_frames, resolution: prepared.resolution };
926
1162
  }
927
1163
  });
928
1164
  const suppression = createSuppression();
@@ -966,30 +1202,21 @@ var createClient = (options, hooks = {}) => {
966
1202
  } catch {
967
1203
  }
968
1204
  };
1205
+ const enqueuePendingSummaries = () => {
1206
+ for (const { summary, rawFrames } of suppression.drainSummaries()) {
1207
+ transport.enqueueSummary(summary, rawFrames);
1208
+ }
1209
+ };
969
1210
  const flush = async (timeoutMs) => {
970
1211
  try {
971
- const summaries = await suppression.takeSummaries(Date.now(), async (raw) => {
972
- const { frames, resolution, unresolvedGenerated } = await resolveSourceMaps(raw);
973
- warnUnresolvedMaps(unresolvedGenerated);
974
- const normalized = normalizeFrames(frames, { appRoot: repoRoot });
975
- const stack_frames = framesToStackFrames(normalized);
976
- const finalResolution = finalizeResolution(resolution, stack_frames);
977
- if (finalResolution === "partial") warnPartialResolution();
978
- return {
979
- stack_frames,
980
- resolution: finalResolution
981
- };
982
- });
983
- for (const summary of summaries) {
984
- transport.enqueueSummary(summary);
985
- }
1212
+ enqueuePendingSummaries();
986
1213
  await transport.flush(timeoutMs);
987
1214
  } catch {
988
1215
  }
989
1216
  };
990
1217
  const close = async () => {
991
1218
  try {
992
- await flush(2e3);
1219
+ enqueuePendingSummaries();
993
1220
  await transport.close();
994
1221
  } catch {
995
1222
  }
@@ -1009,5 +1236,6 @@ export {
1009
1236
  isClientEnabled,
1010
1237
  normalizeFrames,
1011
1238
  parseStack,
1239
+ repoRootFrom,
1012
1240
  resolveSourceMaps
1013
1241
  };
@@ -1,7 +1,7 @@
1
1
  import type { ResolvedFrame } from '../resolve-source-maps/resolved-frame';
2
2
  type NormalizeFramesOptions = {
3
- /** Runtime project root — stripped to produce repo-relative paths. Default: process.cwd() */
4
- appRoot?: string;
3
+ /** Git/monorepo root — stripped to produce repo-relative paths. */
4
+ appRoot: string;
5
5
  };
6
6
  export type Frame = {
7
7
  generated: {
@@ -19,6 +19,6 @@ export type Frame = {
19
19
  in_app: boolean;
20
20
  };
21
21
  /** Normalize resolved frames: repo-relative paths + in_app (runs after source-map resolution). */
22
- export declare const normalizeFrames: (frames: ResolvedFrame[], options?: NormalizeFramesOptions) => Frame[];
22
+ export declare const normalizeFrames: (frames: ResolvedFrame[], options: NormalizeFramesOptions) => Frame[];
23
23
  export {};
24
24
  //# sourceMappingURL=normalize-frames.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"normalize-frames.d.ts","sourceRoot":"","sources":["../../src/normalize-frames/normalize-frames.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAC;AAE3E,KAAK,sBAAsB,GAAG;IAC7B,6FAA6F;IAC7F,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG;IACnB,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5D,QAAQ,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;CAChB,CAAC;AAgDF,kGAAkG;AAClG,eAAO,MAAM,eAAe,GAC3B,QAAQ,aAAa,EAAE,EACvB,UAAS,sBAA2B,KAClC,KAAK,EA2BP,CAAC"}
1
+ {"version":3,"file":"normalize-frames.d.ts","sourceRoot":"","sources":["../../src/normalize-frames/normalize-frames.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAC;AAE3E,KAAK,sBAAsB,GAAG;IAC7B,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG;IACnB,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5D,QAAQ,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;CAChB,CAAC;AAgDF,kGAAkG;AAClG,eAAO,MAAM,eAAe,GAC3B,QAAQ,aAAa,EAAE,EACvB,SAAS,sBAAsB,KAC7B,KAAK,EA2BP,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Resolve the repository (or package) root from a caller module URL.
3
+ *
4
+ * Walks up from the module file looking for a `.git` directory first (monorepo root).
5
+ * Falls back to the topmost `package.json` found while walking up.
6
+ */
7
+ export declare const repoRootFrom: (moduleUrl: string) => string;
8
+ //# sourceMappingURL=repo-root-from.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-root-from.d.ts","sourceRoot":"","sources":["../../src/repo-root/repo-root-from.ts"],"names":[],"mappings":"AAQA;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAI,WAAW,MAAM,KAAG,MAchD,CAAC"}
@@ -0,0 +1,6 @@
1
+ /** Prefer an explicit root; otherwise walk up from the caller module URL. */
2
+ export declare const resolveRepoRoot: (options: {
3
+ repoRoot?: string;
4
+ moduleUrl?: string;
5
+ }) => string | undefined;
6
+ //# sourceMappingURL=resolve-repo-root.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-repo-root.d.ts","sourceRoot":"","sources":["../../src/repo-root/resolve-repo-root.ts"],"names":[],"mappings":"AAEA,6EAA6E;AAC7E,eAAO,MAAM,eAAe,GAAI,SAAS;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB,KAAG,MAAM,GAAG,SAMZ,CAAC"}
@@ -0,0 +1,5 @@
1
+ /** Reset per-process warning state (for testing only). */
2
+ export declare const resetRepoRootSetupWarnings: () => void;
3
+ export declare const warnIfMissingRepoRoot: () => void;
4
+ export declare const warnIfSuspiciousRepoRoot: (root: string) => void;
5
+ //# sourceMappingURL=warn-repo-root-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"warn-repo-root-setup.d.ts","sourceRoot":"","sources":["../../src/repo-root/warn-repo-root-setup.ts"],"names":[],"mappings":"AASA,0DAA0D;AAC1D,eAAO,MAAM,0BAA0B,YAGtC,CAAC;AAOF,eAAO,MAAM,qBAAqB,YAajC,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAI,MAAM,MAAM,SAcpD,CAAC"}
@@ -1,4 +1,4 @@
1
1
  /** Generated by packages/sdk/scripts/generate-sdk-meta.ts — do not edit. */
2
2
  export declare const SDK_NAME = "@rasputin-ai/core";
3
- export declare const SDK_VERSION = "0.1.4";
3
+ export declare const SDK_VERSION = "0.2.0";
4
4
  //# sourceMappingURL=sdk-meta.d.ts.map
@@ -1,7 +1,3 @@
1
1
  import type { Frame } from '../normalize-frames/normalize-frames';
2
- /**
3
- * In-process burst bucket id — aligned with server fingerprint boundaries.
4
- * For generic `Error`, includes the same parameterized message template the API uses.
5
- */
6
2
  export declare const computeBurstKey: (errorType: string, message: string, code: string | null | undefined, frames: Frame[]) => string;
7
3
  //# sourceMappingURL=compute-burst-key.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"compute-burst-key.d.ts","sourceRoot":"","sources":["../../src/suppression/compute-burst-key.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,sCAAsC,CAAC;AAoBlE;;;GAGG;AACH,eAAO,MAAM,eAAe,GAC3B,WAAW,MAAM,EACjB,SAAS,MAAM,EACf,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,EAC/B,QAAQ,KAAK,EAAE,KACb,MAOF,CAAC"}
1
+ {"version":3,"file":"compute-burst-key.d.ts","sourceRoot":"","sources":["../../src/suppression/compute-burst-key.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,sCAAsC,CAAC;AA4DlE,eAAO,MAAM,eAAe,GAC3B,WAAW,MAAM,EACjB,SAAS,MAAM,EACf,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,EAC/B,QAAQ,KAAK,EAAE,KACb,MAUF,CAAC"}
@@ -1,10 +1,17 @@
1
+ import type { RawFrame } from '../parse-stack/raw-frame';
1
2
  import type { CreateSuppressionOptions, IngestSummary, PrepareSummaryFrames, SuppressionDecideInput, SuppressionDecideResult } from './suppression-types';
2
3
  /**
3
- * Local burst suppression (network protection). Burst keys mirror server fingerprint
4
- * boundaries for generic `Error` via parameterized message templates.
4
+ * Local burst suppression (network protection).
5
+ * ⚠️ FINGERPRINT IDENTITY LOCKSTEP computeBurstKey must stay on the same
6
+ * recipe as server createFingerprintIdentity. Drain does not remap; transport
7
+ * resolves source maps at send so summary stack_frames match the exemplar.
5
8
  */
6
9
  export declare const createSuppression: (options?: CreateSuppressionOptions) => {
7
10
  decide: (input: SuppressionDecideInput) => SuppressionDecideResult;
11
+ drainSummaries: (now?: number) => {
12
+ summary: IngestSummary;
13
+ rawFrames: RawFrame[];
14
+ }[];
8
15
  takeSummaries: (now?: number, prepareSummary?: PrepareSummaryFrames) => Promise<IngestSummary[]>;
9
16
  /** Test helper — number of active burst buckets. */
10
17
  size: () => number;
@@ -1 +1 @@
1
- {"version":3,"file":"create-suppression.d.ts","sourceRoot":"","sources":["../../src/suppression/create-suppression.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACX,wBAAwB,EACxB,aAAa,EACb,oBAAoB,EAEpB,sBAAsB,EACtB,uBAAuB,EACvB,MAAM,qBAAqB,CAAC;AAyC7B;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,UAAS,wBAA6B;oBAmDhD,sBAAsB,KAAG,uBAAuB;mDAiErD,oBAAoB,KACnC,OAAO,CAAC,aAAa,EAAE,CAAC;IA4B1B,oDAAoD;;CAGrD,CAAC"}
1
+ {"version":3,"file":"create-suppression.d.ts","sourceRoot":"","sources":["../../src/suppression/create-suppression.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD,OAAO,KAAK,EACX,wBAAwB,EACxB,aAAa,EACb,oBAAoB,EAEpB,sBAAsB,EACtB,uBAAuB,EACvB,MAAM,qBAAqB,CAAC;AAyC7B;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAAI,UAAS,wBAA6B;oBAmDhD,sBAAsB,KAAG,uBAAuB;sCAgEpE;QAAE,OAAO,EAAE,aAAa,CAAC;QAAC,SAAS,EAAE,QAAQ,EAAE,CAAA;KAAE,EAAE;mDAsBpC,oBAAoB,KACnC,OAAO,CAAC,aAAa,EAAE,CAAC;IA0B1B,oDAAoD;;CAGrD,CAAC"}
@@ -1,6 +1,8 @@
1
1
  /**
2
- * Keep in sync with `packages/~core/lib/identity-needs-message-template.ts`.
2
+ * ⚠️ FINGERPRINT IDENTITY LOCKSTEP — byte-identical to
3
+ * `packages/~core/lib/identity-needs-message-template.ts`.
3
4
  * SDK copy — no ~core dependency at publish time.
5
+ * Changing one side splits burst buckets from server groups.
4
6
  */
5
7
  export declare const identityNeedsMessageTemplate: (type: string, code: string | null | undefined) => boolean;
6
8
  //# sourceMappingURL=identity-needs-message-template.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"identity-needs-message-template.d.ts","sourceRoot":"","sources":["../../src/suppression/identity-needs-message-template.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,4BAA4B,GACxC,MAAM,MAAM,EACZ,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,KAC7B,OAGF,CAAC"}
1
+ {"version":3,"file":"identity-needs-message-template.d.ts","sourceRoot":"","sources":["../../src/suppression/identity-needs-message-template.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,GACxC,MAAM,MAAM,EACZ,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,KAC7B,OAGF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"parameterize-message.d.ts","sourceRoot":"","sources":["../../src/suppression/parameterize-message.ts"],"names":[],"mappings":"AAoEA,eAAO,MAAM,mBAAmB,GAAI,YAAY,MAAM,KAAG,MA8BxD,CAAC"}
1
+ {"version":3,"file":"parameterize-message.d.ts","sourceRoot":"","sources":["../../src/suppression/parameterize-message.ts"],"names":[],"mappings":"AAsFA,eAAO,MAAM,mBAAmB,GAAI,YAAY,MAAM,KAAG,MAyBxD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"create-transport.d.ts","sourceRoot":"","sources":["../../src/transport/create-transport.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,sBAAsB,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAY3E,eAAO,MAAM,eAAe,GAAI,SAAS,sBAAsB,KAAG,SA+IjE,CAAC"}
1
+ {"version":3,"file":"create-transport.d.ts","sourceRoot":"","sources":["../../src/transport/create-transport.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,sBAAsB,EAAe,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAoCxF,eAAO,MAAM,eAAe,GAAI,SAAS,sBAAsB,KAAG,SAsKjE,CAAC"}
@@ -6,6 +6,7 @@ type SendBatchOptions = {
6
6
  projectApiKey: string;
7
7
  events: IngestEvent[];
8
8
  summaries?: IngestSummary[];
9
+ idempotencyKey?: string;
9
10
  sdkVersion?: string;
10
11
  sdkName?: string;
11
12
  gzipThresholdBytes: number;
@@ -1 +1 @@
1
- {"version":3,"file":"send-batch.d.ts","sourceRoot":"","sources":["../../src/transport/send-batch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAC;AACpF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,KAAK,gBAAgB,GAAG;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB,CAAC;AAkBF,iGAAiG;AACjG,eAAO,MAAM,SAAS,GACrB,SAAS,gBAAgB,KACvB,OAAO,CAAC,IAAI,GAAG,SAAS,GAAG,QAAQ,CAoErC,CAAC"}
1
+ {"version":3,"file":"send-batch.d.ts","sourceRoot":"","sources":["../../src/transport/send-batch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAC;AACpF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,KAAK,gBAAgB,GAAG;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,CAAC,EAAE,WAAW,CAAC;CACrB,CAAC;AAkCF,iGAAiG;AACjG,eAAO,MAAM,SAAS,GACrB,SAAS,gBAAgB,KACvB,OAAO,CAAC,IAAI,GAAG,SAAS,GAAG,QAAQ,CAsErC,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import type { IngestEvent } from '../create-ingest-event/create-ingest-event-types';
2
2
  import type { RawFrame } from '../parse-stack/raw-frame';
3
- import type { IngestSummary } from '../suppression/suppression-types';
3
+ import type { IngestSummary, PrepareSummaryFrames } from '../suppression/suppression-types';
4
4
  /**
5
5
  * Drop order when the queue is full: repeat exemplars first, then summaries,
6
6
  * protect first-sighting last. Summaries carry tens of thousands of counts in
@@ -17,6 +17,8 @@ export type QueuedEvent = {
17
17
  kind: 'summary';
18
18
  summary: IngestSummary;
19
19
  priority: 'summary';
20
+ /** Same capture-time frames as the exemplar — remapped at send. */
21
+ rawFrames: RawFrame[];
20
22
  };
21
23
  export type TransportFetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
22
24
  export type PrepareQueuedEvent = (item: {
@@ -39,12 +41,14 @@ export type CreateTransportOptions = {
39
41
  installSignalHandlers?: boolean;
40
42
  /** Rewrite events before send (e.g. source-map resolve + re-normalize). */
41
43
  prepareEvent?: PrepareQueuedEvent;
44
+ /** Rewrite summary stacks with the same resolver as events. */
45
+ prepareSummary?: PrepareSummaryFrames;
42
46
  sdkVersion?: string;
43
47
  sdkName?: string;
44
48
  };
45
49
  export type Transport = {
46
50
  enqueue: (event: IngestEvent, priority?: 'first' | 'repeat', rawFrames?: RawFrame[]) => void;
47
- enqueueSummary: (summary: IngestSummary) => void;
51
+ enqueueSummary: (summary: IngestSummary, rawFrames?: RawFrame[]) => void;
48
52
  flush: (timeoutMs?: number) => Promise<void>;
49
53
  close: () => Promise<void>;
50
54
  /** Test helper */
@@ -1 +1 @@
1
- {"version":3,"file":"transport-types.d.ts","sourceRoot":"","sources":["../../src/transport/transport-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAC;AACpF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEtE;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE/D,MAAM,MAAM,WAAW,GACpB;IACA,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC7B,6DAA6D;IAC7D,SAAS,EAAE,QAAQ,EAAE,CAAC;CACrB,GACD;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,aAAa,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAA;CAAE,CAAC;AAEpE,MAAM,MAAM,cAAc,GAAG,CAC5B,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,EAC7B,IAAI,CAAC,EAAE,WAAW,KACd,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEvB,MAAM,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE;IACvC,KAAK,EAAE,WAAW,CAAC;IACnB,SAAS,EAAE,QAAQ,EAAE,CAAC;CACtB,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;AAE3B,MAAM,MAAM,sBAAsB,GAAG;IACpC,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,uEAAuE;IACvE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,2EAA2E;IAC3E,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACvB,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,QAAQ,EAAE,SAAS,CAAC,EAAE,QAAQ,EAAE,KAAK,IAAI,CAAC;IAC7F,cAAc,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;IACjD,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,kBAAkB;IAClB,IAAI,EAAE,MAAM,MAAM,CAAC;CACnB,CAAC"}
1
+ {"version":3,"file":"transport-types.d.ts","sourceRoot":"","sources":["../../src/transport/transport-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAC;AACpF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAE5F;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE/D,MAAM,MAAM,WAAW,GACpB;IACA,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC7B,6DAA6D;IAC7D,SAAS,EAAE,QAAQ,EAAE,CAAC;CACrB,GACD;IACA,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,aAAa,CAAC;IACvB,QAAQ,EAAE,SAAS,CAAC;IACpB,mEAAmE;IACnE,SAAS,EAAE,QAAQ,EAAE,CAAC;CACrB,CAAC;AAEL,MAAM,MAAM,cAAc,GAAG,CAC5B,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,EAC7B,IAAI,CAAC,EAAE,WAAW,KACd,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEvB,MAAM,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE;IACvC,KAAK,EAAE,WAAW,CAAC;IACnB,SAAS,EAAE,QAAQ,EAAE,CAAC;CACtB,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;AAE3B,MAAM,MAAM,sBAAsB,GAAG;IACpC,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,uEAAuE;IACvE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,2EAA2E;IAC3E,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAClC,+DAA+D;IAC/D,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACvB,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,QAAQ,EAAE,SAAS,CAAC,EAAE,QAAQ,EAAE,KAAK,IAAI,CAAC;IAC7F,cAAc,EAAE,CAAC,OAAO,EAAE,aAAa,EAAE,SAAS,CAAC,EAAE,QAAQ,EAAE,KAAK,IAAI,CAAC;IACzE,KAAK,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,kBAAkB;IAClB,IAAI,EAAE,MAAM,MAAM,CAAC;CACnB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rasputin-ai/core",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "description": "Core error capture, stack parsing, and ingest transport for Rasputin AI.",
5
5
  "type": "module",
6
6
  "exports": {