@fixback/sdk 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/init.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type IdentityInputs } from "./boot";
2
- import { type BeforeBreadcrumb, type BreadcrumbLevel } from "./breadcrumbs";
2
+ import { type BeforeBreadcrumb, type BreadcrumbLevel, type TraceStream } from "./breadcrumbs";
3
3
  import type { BeforeSend } from "./scrub";
4
4
  /**
5
5
  * The hosted Fixback API origin the SDK talks to by default. A self-hosted or
@@ -8,17 +8,39 @@ import type { BeforeSend } from "./scrub";
8
8
  */
9
9
  export declare const DEFAULT_API_URL = "https://api.fixback.dev";
10
10
  /**
11
- * Trace buffer tuning (spec §C). These are configurable **starting points** from
12
- * research (N 30, `warn`/`error`/`assert`, optional ~60 s age cap)never
13
- * frozen magic numbers. Pass `false` for {@link InitOptions.trace} to disable
11
+ * Does `url` address one of the SDK's **own** API endpoints? Every request the SDK
12
+ * issues lives under two namespaces beneath the API origin: `/api/ingest/*`boot
13
+ * (`bootEndpoint`, boot.ts) and feedback (`feedbackEndpoint`, submit.ts) and
14
+ * `/api/invites/*` (`inviteStatusEndpoint` / `redeemEndpoint`, invite.ts).
15
+ *
16
+ * The trace's network capture ignores **only** these, never the whole origin. That
17
+ * distinction matters when the instrumented app and the Fixback API share an origin — a
18
+ * self-hosted deployment, or Fixback running its own SDK on its own dashboard — where
19
+ * ignoring the origin wholesale would swallow all of the app's own requests and leave the
20
+ * Network tab empty. When the app and the API sit on different origins (the common case)
21
+ * this matches nothing extra, since the app's origin is never `apiUrl`. `url` is the raw
22
+ * request URL — absolute for every call the SDK makes — so a prefix test over the
23
+ * normalised origin is exact.
24
+ */
25
+ export declare function isFixbackApiRequest(apiUrl: string, url: string): boolean;
26
+ /**
27
+ * Trace buffer tuning (spec #122 §A). These are configurable **starting points**
28
+ * from the grill — three per-stream ring budgets (network ≈ 100 / console ≈ 80 /
29
+ * breadcrumbs ≈ 40), a shared age cap (~3 min, on), and **all** console levels
30
+ * captured (eviction priority, not capture, keeps warn/error above the chatter) —
31
+ * never frozen magic numbers. Pass `false` for {@link InitOptions.trace} to disable
14
32
  * capture entirely.
15
33
  */
16
34
  export interface TraceOptions {
17
- /** Keep at most this many crumbs (oldest drop). Defaults to 30. */
18
- readonly maxBreadcrumbs?: number;
19
- /** Optional age cap in ms (e.g. `60000`); off by default. */
35
+ /**
36
+ * Per-stream ring budgets (network / console / breadcrumbs); each stream evicts
37
+ * independently, so a chatty stream can't drop another's lead-up. Any stream
38
+ * omitted uses its default.
39
+ */
40
+ readonly budgets?: Partial<Record<TraceStream, number>>;
41
+ /** Shared age cap in ms. Defaults to ~3 min (on); pass `0` to disable age pruning. */
20
42
  readonly maxAgeMs?: number;
21
- /** Console levels captured. Defaults to `warn`/`error`/`assert`. */
43
+ /** Console levels captured. Defaults to **all** levels (log/info/warn/error/assert/debug). */
22
44
  readonly consoleLevels?: readonly BreadcrumbLevel[];
23
45
  /** Per-crumb filter: mute a category, edit a crumb, or drop it (`null`). */
24
46
  readonly beforeBreadcrumb?: BeforeBreadcrumb;
@@ -47,6 +69,18 @@ export interface InitOptions extends IdentityInputs {
47
69
  readonly scrub?: boolean;
48
70
  /** Trace buffer tuning, or `false` to turn the buffer off entirely. */
49
71
  readonly trace?: TraceOptions | false;
72
+ /**
73
+ * The dev-side override of the Project's server-served capture config (spec #122
74
+ * §L; ticket #138). Capture is **default-on** and normally governed per-project
75
+ * from the dashboard, surfaced on the boot answer; set a stream here to override
76
+ * what the server serves for it — `{ network: false }` turns network capture off
77
+ * even where the Project leaves it on, and a stream left unset follows the served
78
+ * config. Independent of {@link trace} `false`, which turns the whole buffer off.
79
+ */
80
+ readonly capture?: {
81
+ readonly console?: boolean;
82
+ readonly network?: boolean;
83
+ };
50
84
  /**
51
85
  * Automatic error capture — the SDK files uncaught exceptions / unhandled
52
86
  * rejections as `source: auto` Feedback with no prompt (spec §E, ADR-0011).
@@ -1,15 +1,24 @@
1
1
  /**
2
- * Client-side masked screenshot capture (spec MVP §E; ticket #54).
2
+ * Client-side masked screenshot capture (spec MVP §E; ticket #54; ADR-0014).
3
3
  *
4
- * The capture is **private-by-default**: input values are masked and the SDK's
5
- * own UI is removed from a *clone* of the view **before** anything is rasterised,
6
- * so no unmasked text and none of Fixback's chrome ever reaches the image. The
7
- * approach is dependency-free — the cloned, masked DOM is serialised into an SVG
8
- * `<foreignObject>` and drawn onto a `<canvas>` honouring the SDK's "no runtime
9
- * dependencies / no host-page disturbance" constraints. The raster step is
10
- * injectable so the pipeline (and the masking-before-capture guarantee) is
11
- * testable without a real canvas, and it fails quietly: any problem resolves to
12
- * `null` and the report is simply sent without a screenshot.
4
+ * The capture is **private-by-default**: input values are masked and the SDK's own
5
+ * UI is excluded from a *clone* of the view **before** anything is rasterised, so no
6
+ * unmasked text and none of Fixback's chrome ever reaches the image.
7
+ *
8
+ * The rasterisation itself is delegated to `modern-screenshot` (ADR-0014): it clones
9
+ * the target, inlines the page's real styles **and** its fonts and images as data
10
+ * URIs, then draws the result through an SVG `<foreignObject>` onto a `<canvas>`.
11
+ * That closes the fidelity gap a hand-rolled `<foreignObject>` left open external
12
+ * stylesheets, web fonts, and same-origin images now render so the shot matches
13
+ * what the Reporter saw and lines up with the annotation's viewport-space marks.
14
+ *
15
+ * We keep the privacy guarantee by driving it through two of its hooks: `filter`
16
+ * drops Fixback's own host elements, and `onCloneNode` masks the clone's inputs —
17
+ * both run on the library's internal clone, before it embeds or serialises anything,
18
+ * so the live page is never touched and no real value reaches the raster. The backend
19
+ * is injectable so the pipeline (and the masking-before-capture guarantee) is testable
20
+ * without a real canvas, and it fails quietly: any problem resolves to `null` and the
21
+ * report is simply sent without a screenshot.
13
22
  */
14
23
  /** The character private input content is replaced with. */
15
24
  export declare const MASK_CHAR = "\u2022";
@@ -29,12 +38,19 @@ export interface Capture {
29
38
  }
30
39
  /** The screenshot filename extension for a {@link Capture}'s content type (defaults to `png`). */
31
40
  export declare function extensionFor(type: string): string;
32
- /** Turns a serialised SVG of the view into image bytes (injectable for tests). */
33
- export type Rasterize = (svg: string, meta: {
41
+ /** How the SDK prepares a capture: exclude Fixback's UI, mask the clone's inputs. */
42
+ interface CaptureHooks {
43
+ /** Keep a node in the shot? Returns `false` for Fixback's own host elements. */
44
+ readonly filter: (node: Node) => boolean;
45
+ /** Mask private input content on the library's internal clone, pre-raster. */
46
+ readonly onCloneNode: (clone: Node) => void;
47
+ }
48
+ /** Turns a DOM subtree into image bytes at a given size (injectable for tests). */
49
+ export type CaptureBackend = (target: Element, meta: {
34
50
  width: number;
35
51
  height: number;
36
52
  type: string;
37
- }) => Promise<Blob | null>;
53
+ } & CaptureHooks) => Promise<Blob | null>;
38
54
  /** Options for {@link captureView}. */
39
55
  export interface CaptureOptions {
40
56
  /** The element to capture. Defaults to the document element (the full view). */
@@ -43,12 +59,15 @@ export interface CaptureOptions {
43
59
  readonly win?: Window;
44
60
  /** Output content type. Defaults to `image/png`. */
45
61
  readonly type?: string;
46
- /** Override the raster step (the default draws via an SVG + `<canvas>`). */
47
- readonly rasterize?: Rasterize;
62
+ /** Override the raster backend (the default is `modern-screenshot`). */
63
+ readonly capture?: CaptureBackend;
48
64
  }
49
65
  /**
50
- * Capture the current view as a masked screenshot. Clones the target, removes the
51
- * SDK's own UI, masks input values **all before** serialising and rasterising —
52
- * then returns the image bytes, or `null` if capture wasn't possible.
66
+ * Capture the current view as a masked screenshot. Delegates the raster to the
67
+ * backend ({@link captureViaModernScreenshot} by default), which excludes the SDK's
68
+ * own UI and masks input values on its internal clone — **before** it embeds or
69
+ * serialises anything — then returns the image bytes, or `null` if capture wasn't
70
+ * possible.
53
71
  */
54
72
  export declare function captureView(options?: CaptureOptions): Promise<Capture | null>;
73
+ export {};
package/dist/scrub.d.ts CHANGED
@@ -35,10 +35,11 @@ export interface BeforeSendOptions {
35
35
  export declare function redactPii(text: string): string;
36
36
  /**
37
37
  * Strip the sensitive parts of a URL: userinfo credentials
38
- * (`scheme://user:pass@host`), the entire query string, and a token-bearing
39
- * fragment (one that carries `key=value`). Plain hash routes (`#/checkout`) are
40
- * kept. Works on absolute and relative URLs alike, with no dependency and no
41
- * throw.
38
+ * (`scheme://user:pass@host`), the entire query string, a token-bearing
39
+ * fragment (one that carries `key=value`), and PII (emails, long digit runs) in
40
+ * the **path segments** (#139). Plain hash routes (`#/checkout`) are kept. Works
41
+ * on absolute and relative URLs alike, with no dependency and no throw. The host
42
+ * (authority) is never redacted — only the path and any surviving fragment route.
42
43
  */
43
44
  export declare function scrubUrl(url: string): string;
44
45
  /**
package/dist/version.d.ts CHANGED
@@ -1,7 +1,13 @@
1
1
  /**
2
2
  * The SDK's own version string, reported to ingest as `environment.sdkVersion`
3
- * (spec MVP §B/§E). Kept as a hand-maintained constant rather than imported from
4
- * `package.json`, so the bundle stays a single self-contained file with no JSON
5
- * import — keep it in step with `package.json` and the Changesets bump.
3
+ * (spec MVP §B/§E). Kept as an inlined constant rather than a runtime `package.json`
4
+ * import, so the published bundle stays a single self-contained file.
5
+ *
6
+ * It must equal `package.json`'s `version`. Two things keep it there: the Changesets
7
+ * `version` step syncs it automatically (`scripts/sync-sdk-version.mjs`, wired into
8
+ * `version-packages`), and `version.test.ts` fails the build if the two ever drift —
9
+ * so a stale dogfood version (the SDK reporting an old number on our own dashboard)
10
+ * can't slip through. Do not hand-edit this line to a value other than
11
+ * `package.json`'s version.
6
12
  */
7
- export declare const SDK_VERSION = "0.1.0";
13
+ export declare const SDK_VERSION = "0.3.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fixback/sdk",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "The Fixback capture SDK — a boot-gated, self-isolating on-page feedback launcher.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -40,6 +40,9 @@
40
40
  "publishConfig": {
41
41
  "access": "public"
42
42
  },
43
+ "dependencies": {
44
+ "modern-screenshot": "^4.7.0"
45
+ },
43
46
  "devDependencies": {
44
47
  "jsdom": "^30.0.1",
45
48
  "typescript": "5.9.3",