@defender-dev/sdk 0.1.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.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ import { Breadcrumb } from '@defender/shared';
2
+ export declare function addBreadcrumb(crumb: Breadcrumb): void;
3
+ export declare function getBreadcrumbs(): Breadcrumb[];
4
+ export declare function clearBreadcrumbs(): void;
@@ -0,0 +1,39 @@
1
+ export interface SdkConfig {
2
+ endpoint: string;
3
+ apiKey: string;
4
+ release: string;
5
+ maxBreadcrumbs: number;
6
+ breadcrumbMaxAge: number;
7
+ flushInterval: number;
8
+ maxBatchSize: number;
9
+ debug: boolean;
10
+ replayEnabled: boolean;
11
+ replayMaxEvents: number;
12
+ replayScreenshotEnabled: boolean;
13
+ replayScreenshotMaxCount: number;
14
+ replayScreenshotMaxBytes: number;
15
+ }
16
+ export interface ReplayScreenshotOptions {
17
+ enabled?: boolean;
18
+ maxCount?: number;
19
+ maxBytes?: number;
20
+ }
21
+ export interface ReplayInitOptions {
22
+ enabled?: boolean;
23
+ maxEvents?: number;
24
+ screenshots?: ReplayScreenshotOptions;
25
+ }
26
+ export interface SdkInitOptions {
27
+ apiKey: string;
28
+ endpoint?: string;
29
+ release?: string;
30
+ maxBreadcrumbs?: number;
31
+ breadcrumbMaxAge?: number;
32
+ flushInterval?: number;
33
+ maxBatchSize?: number;
34
+ debug?: boolean;
35
+ replay?: ReplayInitOptions;
36
+ }
37
+ export declare function loadConfig(options: SdkInitOptions): void;
38
+ export declare function getConfig(): SdkConfig;
39
+ export declare function resetConfig(): void;
@@ -0,0 +1,2 @@
1
+ export declare function patchConsole(): void;
2
+ export declare function unpatchConsole(): void;
@@ -0,0 +1,16 @@
1
+ import { ErrorEventPayload, Breadcrumb } from '@defender/shared';
2
+ interface UserIdentity {
3
+ id: string;
4
+ email?: string;
5
+ account?: {
6
+ id: string;
7
+ name?: string;
8
+ };
9
+ }
10
+ export declare function setUser(user: UserIdentity): void;
11
+ export declare function clearUser(): void;
12
+ export declare function buildPayload(errorType: string, errorMessage: string, stack: string, breadcrumb: Breadcrumb): ErrorEventPayload;
13
+ export declare function captureException(input: unknown): void;
14
+ export declare function installGlobalHandlers(): void;
15
+ export declare function uninstallGlobalHandlers(): void;
16
+ export {};
@@ -0,0 +1,15 @@
1
+ import { SdkInitOptions } from './config';
2
+ import { captureException, setUser, clearUser } from './core';
3
+ export { defenderVuePlugin } from './vue';
4
+ export type { SdkInitOptions } from './config';
5
+ export { captureException, setUser, clearUser };
6
+ export declare function init(options: SdkInitOptions): void;
7
+ export declare function destroy(): void;
8
+ export declare const DefenderSDK: {
9
+ init: typeof init;
10
+ destroy: typeof destroy;
11
+ captureException: typeof captureException;
12
+ setUser: typeof setUser;
13
+ clearUser: typeof clearUser;
14
+ };
15
+ export { DefenderSDK as Defender };
@@ -0,0 +1,4 @@
1
+ export declare function patchFetch(): void;
2
+ export declare function unpatchFetch(): void;
3
+ export declare function patchXHR(): void;
4
+ export declare function unpatchXHR(): void;
@@ -0,0 +1,19 @@
1
+ export type ReplayTriggerType = 'uncaught_error' | 'capture_exception';
2
+ interface ReplayEvent {
3
+ timestamp: string;
4
+ type: string;
5
+ data: Record<string, unknown>;
6
+ }
7
+ interface ReplayUploadInput {
8
+ triggerType: ReplayTriggerType;
9
+ errorType: string;
10
+ errorMessage: string;
11
+ eventId?: string;
12
+ errorGroupId?: string;
13
+ }
14
+ export declare function startReplayCapture(): void;
15
+ export declare function stopReplayCapture(): void;
16
+ export declare function uploadReplayForTrigger(input: ReplayUploadInput): Promise<void>;
17
+ export declare function _resetReplayState(): void;
18
+ export declare function _getReplayBuffer(): ReplayEvent[];
19
+ export {};
@@ -0,0 +1,18 @@
1
+ export interface FocusRect {
2
+ x: number;
3
+ y: number;
4
+ width: number;
5
+ height: number;
6
+ }
7
+ export interface ReplayScreenshotArtifact {
8
+ kind: 'crash_viewport' | 'last_interaction_focus';
9
+ captured_at: string;
10
+ width: number;
11
+ height: number;
12
+ content_type: 'image/webp';
13
+ data_base64: string;
14
+ }
15
+ export interface CaptureReplayScreenshotOptions {
16
+ focusRect?: FocusRect;
17
+ }
18
+ export declare function captureReplayScreenshots(options?: CaptureReplayScreenshotOptions): Promise<ReplayScreenshotArtifact[]>;
@@ -0,0 +1,3 @@
1
+ export declare function ensureSessionID(): string;
2
+ export declare function getSessionId(): string;
3
+ export declare function resetSessionId(): void;
@@ -0,0 +1,8 @@
1
+ import { ErrorEventPayload } from '@defender/shared';
2
+ import { ReplayTriggerType } from './replay';
3
+ export declare function enqueueEvent(event: ErrorEventPayload, replayTrigger?: ReplayTriggerType): void;
4
+ export declare function flushEvents(): Promise<void>;
5
+ export declare function startTransport(): void;
6
+ export declare function stopTransport(): void;
7
+ export declare function getQueueLength(): number;
8
+ export declare function _resetQueue(): void;
@@ -0,0 +1,9 @@
1
+ interface VueApp {
2
+ config: {
3
+ errorHandler: ((err: unknown, instance: unknown, info: string) => void) | null;
4
+ };
5
+ }
6
+ export declare const defenderVuePlugin: {
7
+ install(app: VueApp): void;
8
+ };
9
+ export {};
@@ -0,0 +1,23 @@
1
+ export interface SourceMapPluginOptions {
2
+ endpoint: string;
3
+ apiKey: string;
4
+ release?: string;
5
+ }
6
+ interface BundleAsset {
7
+ type: string;
8
+ source?: string;
9
+ fileName: string;
10
+ }
11
+ export declare function defenderSourceMapPlugin(options: SourceMapPluginOptions): {
12
+ name: string;
13
+ apply: "build";
14
+ enforce: "post";
15
+ config(): {
16
+ build: {
17
+ sourcemap: "hidden";
18
+ };
19
+ };
20
+ generateBundle(_outputOptions: unknown, bundle: Record<string, BundleAsset>): void;
21
+ closeBundle(): Promise<void>;
22
+ };
23
+ export {};
@@ -0,0 +1,69 @@
1
+ function f(o) {
2
+ const n = [];
3
+ return {
4
+ name: "defender-source-map",
5
+ apply: "build",
6
+ enforce: "post",
7
+ config() {
8
+ return {
9
+ build: {
10
+ sourcemap: "hidden"
11
+ }
12
+ };
13
+ },
14
+ generateBundle(a, e) {
15
+ const t = Object.keys(e).filter(
16
+ (r) => r.endsWith(".map")
17
+ );
18
+ for (const r of t) {
19
+ const s = e[r], c = typeof s.source == "string" ? s.source : String(s.source || "");
20
+ n.push({
21
+ file_path: i(s.fileName),
22
+ source_map: c
23
+ }), delete e[r];
24
+ }
25
+ },
26
+ async closeBundle() {
27
+ if (n.length === 0) return;
28
+ const a = o.release || p(n);
29
+ for (const e of n)
30
+ try {
31
+ const t = new FormData();
32
+ t.append("release", a), t.append("file", new Blob([e.source_map], { type: "application/json" }), e.file_path + ".map");
33
+ const r = await fetch(
34
+ `${o.endpoint}/api/v1/sourcemaps`,
35
+ {
36
+ method: "POST",
37
+ headers: {
38
+ "X-API-Key": o.apiKey
39
+ },
40
+ body: t
41
+ }
42
+ );
43
+ r.ok || console.warn(
44
+ `[defender] Source map upload failed for ${e.file_path}: ${r.status} ${r.statusText}`
45
+ );
46
+ } catch (t) {
47
+ console.warn(
48
+ `[defender] Source map upload failed for ${e.file_path}: ${t instanceof Error ? t.message : String(t)}`
49
+ );
50
+ }
51
+ }
52
+ };
53
+ }
54
+ function p(o) {
55
+ const n = o.map((e) => e.file_path).sort().join("|");
56
+ let a = 0;
57
+ for (let e = 0; e < n.length; e++) {
58
+ const t = n.charCodeAt(e);
59
+ a = (a << 5) - a + t | 0;
60
+ }
61
+ return Math.abs(a).toString(36);
62
+ }
63
+ function i(o) {
64
+ return o.endsWith(".map") ? o.slice(0, -4) : o;
65
+ }
66
+ export {
67
+ f as defenderSourceMapPlugin
68
+ };
69
+ //# sourceMappingURL=vite-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-plugin.js","sources":["../vite-plugin/index.ts"],"sourcesContent":["export interface SourceMapPluginOptions {\n endpoint: string;\n apiKey: string;\n release?: string;\n}\n\ninterface BundleAsset {\n type: string;\n source?: string;\n fileName: string;\n}\n\ninterface CollectedMap {\n file_path: string;\n source_map: string;\n}\n\nexport function defenderSourceMapPlugin(options: SourceMapPluginOptions) {\n const collectedMaps: CollectedMap[] = [];\n\n return {\n name: 'defender-source-map',\n apply: 'build' as const,\n enforce: 'post' as const,\n\n config() {\n return {\n build: {\n sourcemap: 'hidden' as const,\n },\n };\n },\n\n generateBundle(\n _outputOptions: unknown,\n bundle: Record<string, BundleAsset>\n ): void {\n const mapKeys = Object.keys(bundle).filter((key) =>\n key.endsWith('.map')\n );\n\n for (const key of mapKeys) {\n const asset = bundle[key];\n const source =\n typeof asset.source === 'string'\n ? asset.source\n : String(asset.source || '');\n\n collectedMaps.push({\n file_path: stripMapSuffix(asset.fileName),\n source_map: source,\n });\n\n // Remove .map file from output so it is not deployed\n delete bundle[key];\n }\n },\n\n async closeBundle(): Promise<void> {\n if (collectedMaps.length === 0) return;\n\n const release = options.release || generateVersion(collectedMaps);\n\n for (const map of collectedMaps) {\n try {\n const formData = new FormData();\n formData.append('release', release);\n formData.append('file', new Blob([map.source_map], { type: 'application/json' }), map.file_path + '.map');\n\n const response = await fetch(\n `${options.endpoint}/api/v1/sourcemaps`,\n {\n method: 'POST',\n headers: {\n 'X-API-Key': options.apiKey,\n },\n body: formData,\n }\n );\n\n if (!response.ok) {\n console.warn(\n `[defender] Source map upload failed for ${map.file_path}: ${response.status} ${response.statusText}`\n );\n }\n } catch (error) {\n console.warn(\n `[defender] Source map upload failed for ${map.file_path}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n },\n };\n}\n\nfunction generateVersion(files: CollectedMap[]): string {\n const input = files.map((f) => f.file_path).sort().join('|');\n let hash = 0;\n for (let i = 0; i < input.length; i++) {\n const char = input.charCodeAt(i);\n hash = ((hash << 5) - hash + char) | 0;\n }\n return Math.abs(hash).toString(36);\n}\n\nfunction stripMapSuffix(filePath: string): string {\n return filePath.endsWith('.map') ? filePath.slice(0, -4) : filePath;\n}\n"],"names":["defenderSourceMapPlugin","options","collectedMaps","_outputOptions","bundle","mapKeys","key","asset","source","stripMapSuffix","release","generateVersion","map","formData","response","error","files","input","f","hash","i","char","filePath"],"mappings":"AAiBO,SAASA,EAAwBC,GAAiC;AACvE,QAAMC,IAAgC,CAAA;AAEtC,SAAO;AAAA,IACL,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAAA,IAET,SAAS;AACP,aAAO;AAAA,QACL,OAAO;AAAA,UACL,WAAW;AAAA,QAAA;AAAA,MACb;AAAA,IAEJ;AAAA,IAEA,eACEC,GACAC,GACM;AACN,YAAMC,IAAU,OAAO,KAAKD,CAAM,EAAE;AAAA,QAAO,CAACE,MAC1CA,EAAI,SAAS,MAAM;AAAA,MAAA;AAGrB,iBAAWA,KAAOD,GAAS;AACzB,cAAME,IAAQH,EAAOE,CAAG,GAClBE,IACJ,OAAOD,EAAM,UAAW,WACpBA,EAAM,SACN,OAAOA,EAAM,UAAU,EAAE;AAE/B,QAAAL,EAAc,KAAK;AAAA,UACjB,WAAWO,EAAeF,EAAM,QAAQ;AAAA,UACxC,YAAYC;AAAA,QAAA,CACb,GAGD,OAAOJ,EAAOE,CAAG;AAAA,MACnB;AAAA,IACF;AAAA,IAEA,MAAM,cAA6B;AACjC,UAAIJ,EAAc,WAAW,EAAG;AAEhC,YAAMQ,IAAUT,EAAQ,WAAWU,EAAgBT,CAAa;AAEhE,iBAAWU,KAAOV;AAChB,YAAI;AACF,gBAAMW,IAAW,IAAI,SAAA;AACrB,UAAAA,EAAS,OAAO,WAAWH,CAAO,GAClCG,EAAS,OAAO,QAAQ,IAAI,KAAK,CAACD,EAAI,UAAU,GAAG,EAAE,MAAM,mBAAA,CAAoB,GAAGA,EAAI,YAAY,MAAM;AAExG,gBAAME,IAAW,MAAM;AAAA,YACrB,GAAGb,EAAQ,QAAQ;AAAA,YACnB;AAAA,cACE,QAAQ;AAAA,cACR,SAAS;AAAA,gBACP,aAAaA,EAAQ;AAAA,cAAA;AAAA,cAEvB,MAAMY;AAAA,YAAA;AAAA,UACR;AAGF,UAAKC,EAAS,MACZ,QAAQ;AAAA,YACN,2CAA2CF,EAAI,SAAS,KAAKE,EAAS,MAAM,IAAIA,EAAS,UAAU;AAAA,UAAA;AAAA,QAGzG,SAASC,GAAO;AACd,kBAAQ;AAAA,YACN,2CAA2CH,EAAI,SAAS,KAAKG,aAAiB,QAAQA,EAAM,UAAU,OAAOA,CAAK,CAAC;AAAA,UAAA;AAAA,QAEvH;AAAA,IAEJ;AAAA,EAAA;AAEJ;AAEA,SAASJ,EAAgBK,GAA+B;AACtD,QAAMC,IAAQD,EAAM,IAAI,CAACE,MAAMA,EAAE,SAAS,EAAE,OAAO,KAAK,GAAG;AAC3D,MAAIC,IAAO;AACX,WAASC,IAAI,GAAGA,IAAIH,EAAM,QAAQG,KAAK;AACrC,UAAMC,IAAOJ,EAAM,WAAWG,CAAC;AAC/B,IAAAD,KAASA,KAAQ,KAAKA,IAAOE,IAAQ;AAAA,EACvC;AACA,SAAO,KAAK,IAAIF,CAAI,EAAE,SAAS,EAAE;AACnC;AAEA,SAASV,EAAea,GAA0B;AAChD,SAAOA,EAAS,SAAS,MAAM,IAAIA,EAAS,MAAM,GAAG,EAAE,IAAIA;AAC7D;"}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@defender-dev/sdk",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Browser error monitoring SDK for Defender",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/opslane/defender.git",
10
+ "directory": "packages/sdk"
11
+ },
12
+ "main": "dist/index.js",
13
+ "module": "dist/index.js",
14
+ "types": "dist/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "import": "./dist/index.js",
18
+ "types": "./dist/index.d.ts"
19
+ },
20
+ "./vite-plugin": {
21
+ "import": "./dist/vite-plugin.js",
22
+ "types": "./dist/vite-plugin.d.ts"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "scripts": {
29
+ "build": "vite build",
30
+ "test": "vitest run",
31
+ "test:watch": "vitest",
32
+ "prepublishOnly": "vite build"
33
+ },
34
+ "dependencies": {
35
+ "html2canvas": "^1.4.1"
36
+ },
37
+ "devDependencies": {
38
+ "@defender/shared": "workspace:*",
39
+ "@playwright/test": "^1.40.0",
40
+ "jsdom": "^28.0.0",
41
+ "typescript": "^5.4.0",
42
+ "vite": "^6.0.0",
43
+ "vite-plugin-dts": "^4.0.0",
44
+ "vitest": "^3.0.0"
45
+ },
46
+ "peerDependencies": {
47
+ "vite": "^6.0.0",
48
+ "vue": "^3.0.0"
49
+ },
50
+ "peerDependenciesMeta": {
51
+ "vue": {
52
+ "optional": true
53
+ },
54
+ "vite": {
55
+ "optional": true
56
+ }
57
+ }
58
+ }