signalbird 1.4.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/README.md +576 -0
- package/dist/angular.d.mts +53 -0
- package/dist/angular.d.ts +53 -0
- package/dist/angular.js +578 -0
- package/dist/angular.js.map +1 -0
- package/dist/angular.mjs +574 -0
- package/dist/angular.mjs.map +1 -0
- package/dist/app.d.mts +343 -0
- package/dist/app.d.ts +343 -0
- package/dist/app.js +518 -0
- package/dist/app.js.map +1 -0
- package/dist/app.mjs +514 -0
- package/dist/app.mjs.map +1 -0
- package/dist/browser.d.mts +56 -0
- package/dist/browser.d.ts +56 -0
- package/dist/browser.js +113 -0
- package/dist/browser.js.map +1 -0
- package/dist/browser.mjs +109 -0
- package/dist/browser.mjs.map +1 -0
- package/dist/index.d.mts +1170 -0
- package/dist/index.d.ts +1170 -0
- package/dist/index.js +853 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +841 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react-native.d.mts +46 -0
- package/dist/react-native.d.ts +46 -0
- package/dist/react-native.js +576 -0
- package/dist/react-native.js.map +1 -0
- package/dist/react-native.mjs +569 -0
- package/dist/react-native.mjs.map +1 -0
- package/dist/react.d.mts +43 -0
- package/dist/react.d.ts +43 -0
- package/dist/react.js +587 -0
- package/dist/react.js.map +1 -0
- package/dist/react.mjs +580 -0
- package/dist/react.mjs.map +1 -0
- package/dist/signalbird.js +141 -0
- package/dist/vue.d.mts +46 -0
- package/dist/vue.d.ts +46 -0
- package/dist/vue.js +575 -0
- package/dist/vue.js.map +1 -0
- package/dist/vue.mjs +568 -0
- package/dist/vue.mjs.map +1 -0
- package/package.json +125 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* signalbird/browser — tarayıcı istemcisi.
|
|
3
|
+
*
|
|
4
|
+
* Ayrı bir giriş noktası olmasının sebebi teknik değil, GÜVENLİKTİR: sunucu
|
|
5
|
+
* anahtarı istemciye gömülemez ve sunucu `Origin` başlığı taşıyan bir istekte
|
|
6
|
+
* onu zaten reddeder. Tarayıcı, açık anahtarı (`sbr_pub_…`) kullanır ve
|
|
7
|
+
* güvenliği gizlilikten değil kısıttan gelir — yalnız izinli alan adlarından
|
|
8
|
+
* ve yalnız izin verilen kanallara yazabilir.
|
|
9
|
+
*
|
|
10
|
+
* React, Vue, Angular ve düz JS aynı istemciyi kullanır; çatıya özel sarmalayıcı
|
|
11
|
+
* yoktur çünkü gereken tek şey bir fonksiyon çağrısıdır.
|
|
12
|
+
*/
|
|
13
|
+
type Level = 'debug' | 'info' | 'warn' | 'error' | 'critical';
|
|
14
|
+
interface BrowserConfig {
|
|
15
|
+
/** Açık anahtar (`sbr_pub_…`). Gizli anahtar BURAYA YAZILMAZ. */
|
|
16
|
+
publicKey: string;
|
|
17
|
+
baseUrl?: string;
|
|
18
|
+
source?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Kayıtlar tek tek değil, toplu gider. Kullanıcının tarayıcısından saniyede
|
|
21
|
+
* onlarca istek çıkarmak hem yavaş hem pahalıdır.
|
|
22
|
+
*/
|
|
23
|
+
flushIntervalMs?: number;
|
|
24
|
+
maxQueue?: number;
|
|
25
|
+
debug?: boolean;
|
|
26
|
+
}
|
|
27
|
+
declare class SignalbirdBrowser {
|
|
28
|
+
private readonly config;
|
|
29
|
+
private queue;
|
|
30
|
+
private timer;
|
|
31
|
+
private readonly baseUrl;
|
|
32
|
+
private readonly maxQueue;
|
|
33
|
+
constructor(config: BrowserConfig);
|
|
34
|
+
log(channel: string, message: string, level?: Level, context?: Record<string, unknown>): void;
|
|
35
|
+
info(channel: string, message: string, context?: Record<string, unknown>): void;
|
|
36
|
+
warn(channel: string, message: string, context?: Record<string, unknown>): void;
|
|
37
|
+
error(channel: string, message: string, context?: Record<string, unknown>): void;
|
|
38
|
+
/**
|
|
39
|
+
* Tarayıcıdaki yakalanmamış hataları bağlar.
|
|
40
|
+
*
|
|
41
|
+
* Kritik kanala YAZMAZ: istemci tarafı kod herkesin elindedir, oradan
|
|
42
|
+
* kritik alarm tetiklemek kötü niyetli birine ekibin telefonunu çaldırma
|
|
43
|
+
* imkânı verirdi. Sunucu zaten `browser_channels` ile bunu kısıtlar.
|
|
44
|
+
*/
|
|
45
|
+
captureErrors(channel?: string): () => void;
|
|
46
|
+
flush(): Promise<void>;
|
|
47
|
+
/** Sayfa kapanırken son gönderim. */
|
|
48
|
+
private flushBeacon;
|
|
49
|
+
destroy(): void;
|
|
50
|
+
}
|
|
51
|
+
/** Uygulama başlangıcında bir kez çağrılır. */
|
|
52
|
+
declare function initSignalbird(config: BrowserConfig): SignalbirdBrowser;
|
|
53
|
+
/** Kurulmamışsa sessizce yok sayar — log çağrısı uygulamayı çökertmemeli. */
|
|
54
|
+
declare function signalbird(): SignalbirdBrowser | null;
|
|
55
|
+
|
|
56
|
+
export { type BrowserConfig, type Level, SignalbirdBrowser, initSignalbird, signalbird };
|
package/dist/browser.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/browser/index.ts
|
|
4
|
+
var DEFAULT_BASE_URL = "https://signalbird.io/api";
|
|
5
|
+
var SignalbirdBrowser = class {
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.config = config;
|
|
8
|
+
this.queue = [];
|
|
9
|
+
this.timer = null;
|
|
10
|
+
if (!config.publicKey?.startsWith("sbr_pub_")) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
"Signalbird: taray\u0131c\u0131 istemcisi a\xE7\u0131k anahtar ister (sbr_pub_\u2026). Gizli anahtar\u0131 (sbr_live_\u2026) istemci koduna KOYMAYIN."
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
16
|
+
this.maxQueue = config.maxQueue ?? 50;
|
|
17
|
+
if (typeof window !== "undefined") {
|
|
18
|
+
this.timer = setInterval(() => void this.flush(), config.flushIntervalMs ?? 3e3);
|
|
19
|
+
window.addEventListener("pagehide", () => this.flushBeacon());
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
log(channel, message, level, context) {
|
|
23
|
+
this.queue.push({ channel, message, level, context, source: this.config.source });
|
|
24
|
+
if (this.queue.length >= this.maxQueue) {
|
|
25
|
+
void this.flush();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
info(channel, message, context) {
|
|
29
|
+
this.log(channel, message, "info", context);
|
|
30
|
+
}
|
|
31
|
+
warn(channel, message, context) {
|
|
32
|
+
this.log(channel, message, "warn", context);
|
|
33
|
+
}
|
|
34
|
+
error(channel, message, context) {
|
|
35
|
+
this.log(channel, message, "error", context);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Tarayıcıdaki yakalanmamış hataları bağlar.
|
|
39
|
+
*
|
|
40
|
+
* Kritik kanala YAZMAZ: istemci tarafı kod herkesin elindedir, oradan
|
|
41
|
+
* kritik alarm tetiklemek kötü niyetli birine ekibin telefonunu çaldırma
|
|
42
|
+
* imkânı verirdi. Sunucu zaten `browser_channels` ile bunu kısıtlar.
|
|
43
|
+
*/
|
|
44
|
+
captureErrors(channel = "browser") {
|
|
45
|
+
const onError = (event) => {
|
|
46
|
+
this.error(channel, event.message, {
|
|
47
|
+
file: event.filename,
|
|
48
|
+
line: event.lineno,
|
|
49
|
+
column: event.colno,
|
|
50
|
+
url: window.location.href
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
const onRejection = (event) => {
|
|
54
|
+
this.error(channel, String(event.reason), { url: window.location.href });
|
|
55
|
+
};
|
|
56
|
+
window.addEventListener("error", onError);
|
|
57
|
+
window.addEventListener("unhandledrejection", onRejection);
|
|
58
|
+
return () => {
|
|
59
|
+
window.removeEventListener("error", onError);
|
|
60
|
+
window.removeEventListener("unhandledrejection", onRejection);
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
async flush() {
|
|
64
|
+
if (this.queue.length === 0) return;
|
|
65
|
+
const batch = this.queue.splice(0, 100);
|
|
66
|
+
try {
|
|
67
|
+
await fetch(`${this.baseUrl}/v1/radio/log/batch`, {
|
|
68
|
+
method: "POST",
|
|
69
|
+
headers: {
|
|
70
|
+
"Content-Type": "application/json",
|
|
71
|
+
"X-Signalbird-Key": this.config.publicKey
|
|
72
|
+
},
|
|
73
|
+
body: JSON.stringify({ events: batch }),
|
|
74
|
+
keepalive: true
|
|
75
|
+
});
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (this.config.debug) {
|
|
78
|
+
console.warn("[signalbird] g\xF6nderilemedi", error);
|
|
79
|
+
}
|
|
80
|
+
this.queue = [...batch, ...this.queue].slice(0, this.maxQueue);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** Sayfa kapanırken son gönderim. */
|
|
84
|
+
flushBeacon() {
|
|
85
|
+
if (this.queue.length === 0 || typeof navigator === "undefined") return;
|
|
86
|
+
const blob = new Blob(
|
|
87
|
+
[JSON.stringify({ events: this.queue.splice(0, 100) })],
|
|
88
|
+
{ type: "application/json" }
|
|
89
|
+
);
|
|
90
|
+
navigator.sendBeacon?.(
|
|
91
|
+
`${this.baseUrl}/v1/radio/log/batch?key=${encodeURIComponent(this.config.publicKey)}`,
|
|
92
|
+
blob
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
destroy() {
|
|
96
|
+
if (this.timer) clearInterval(this.timer);
|
|
97
|
+
void this.flush();
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
var singleton = null;
|
|
101
|
+
function initSignalbird(config) {
|
|
102
|
+
singleton = new SignalbirdBrowser(config);
|
|
103
|
+
return singleton;
|
|
104
|
+
}
|
|
105
|
+
function signalbird() {
|
|
106
|
+
return singleton;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
exports.SignalbirdBrowser = SignalbirdBrowser;
|
|
110
|
+
exports.initSignalbird = initSignalbird;
|
|
111
|
+
exports.signalbird = signalbird;
|
|
112
|
+
//# sourceMappingURL=browser.js.map
|
|
113
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/browser/index.ts"],"names":[],"mappings":"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","file":"browser.js","sourcesContent":["/**\n * signalbird/browser — tarayıcı istemcisi.\n *\n * Ayrı bir giriş noktası olmasının sebebi teknik değil, GÜVENLİKTİR: sunucu\n * anahtarı istemciye gömülemez ve sunucu `Origin` başlığı taşıyan bir istekte\n * onu zaten reddeder. Tarayıcı, açık anahtarı (`sbr_pub_…`) kullanır ve\n * güvenliği gizlilikten değil kısıttan gelir — yalnız izinli alan adlarından\n * ve yalnız izin verilen kanallara yazabilir.\n *\n * React, Vue, Angular ve düz JS aynı istemciyi kullanır; çatıya özel sarmalayıcı\n * yoktur çünkü gereken tek şey bir fonksiyon çağrısıdır.\n */\nexport type Level = 'debug' | 'info' | 'warn' | 'error' | 'critical';\n\nexport interface BrowserConfig {\n /** Açık anahtar (`sbr_pub_…`). Gizli anahtar BURAYA YAZILMAZ. */\n publicKey: string;\n baseUrl?: string;\n source?: string;\n /**\n * Kayıtlar tek tek değil, toplu gider. Kullanıcının tarayıcısından saniyede\n * onlarca istek çıkarmak hem yavaş hem pahalıdır.\n */\n flushIntervalMs?: number;\n maxQueue?: number;\n debug?: boolean;\n}\n\ninterface QueuedEvent {\n channel: string;\n message: string;\n level?: Level;\n context?: Record<string, unknown>;\n source?: string;\n}\n\nconst DEFAULT_BASE_URL = 'https://signalbird.io/api';\n\nexport class SignalbirdBrowser {\n private queue: QueuedEvent[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private readonly baseUrl: string;\n private readonly maxQueue: number;\n\n constructor(private readonly config: BrowserConfig) {\n if (!config.publicKey?.startsWith('sbr_pub_')) {\n throw new Error(\n 'Signalbird: tarayıcı istemcisi açık anahtar ister (sbr_pub_…). ' +\n 'Gizli anahtarı (sbr_live_…) istemci koduna KOYMAYIN.'\n );\n }\n\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n this.maxQueue = config.maxQueue ?? 50;\n\n if (typeof window !== 'undefined') {\n this.timer = setInterval(() => void this.flush(), config.flushIntervalMs ?? 3000);\n\n // Sekme kapanırken kuyruk boşaltılır. `sendBeacon` sayfa ölürken bile\n // gider; normal fetch yarıda kesilirdi.\n window.addEventListener('pagehide', () => this.flushBeacon());\n }\n }\n\n log(channel: string, message: string, level?: Level, context?: Record<string, unknown>): void {\n this.queue.push({ channel, message, level, context, source: this.config.source });\n\n // Kuyruk dolduysa beklemeden gönder: bellekte sonsuza kadar biriktirmek,\n // logu hiç göndermemekten kötüdür.\n if (this.queue.length >= this.maxQueue) {\n void this.flush();\n }\n }\n\n info(channel: string, message: string, context?: Record<string, unknown>) {\n this.log(channel, message, 'info', context);\n }\n\n warn(channel: string, message: string, context?: Record<string, unknown>) {\n this.log(channel, message, 'warn', context);\n }\n\n error(channel: string, message: string, context?: Record<string, unknown>) {\n this.log(channel, message, 'error', context);\n }\n\n /**\n * Tarayıcıdaki yakalanmamış hataları bağlar.\n *\n * Kritik kanala YAZMAZ: istemci tarafı kod herkesin elindedir, oradan\n * kritik alarm tetiklemek kötü niyetli birine ekibin telefonunu çaldırma\n * imkânı verirdi. Sunucu zaten `browser_channels` ile bunu kısıtlar.\n */\n captureErrors(channel = 'browser'): () => void {\n const onError = (event: ErrorEvent) => {\n this.error(channel, event.message, {\n file: event.filename,\n line: event.lineno,\n column: event.colno,\n url: window.location.href,\n });\n };\n\n const onRejection = (event: PromiseRejectionEvent) => {\n this.error(channel, String(event.reason), { url: window.location.href });\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onRejection);\n\n return () => {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onRejection);\n };\n }\n\n async flush(): Promise<void> {\n if (this.queue.length === 0) return;\n\n const batch = this.queue.splice(0, 100);\n\n try {\n await fetch(`${this.baseUrl}/v1/radio/log/batch`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Signalbird-Key': this.config.publicKey,\n },\n body: JSON.stringify({ events: batch }),\n keepalive: true,\n });\n } catch (error) {\n if (this.config.debug) {\n console.warn('[signalbird] gönderilemedi', error);\n }\n // Kayıtlar geri konur ama kuyruk sınırı aşılmaz: ağ uzun süre kapalıysa\n // sekme belleği şişmemeli.\n this.queue = [...batch, ...this.queue].slice(0, this.maxQueue);\n }\n }\n\n /** Sayfa kapanırken son gönderim. */\n private flushBeacon(): void {\n if (this.queue.length === 0 || typeof navigator === 'undefined') return;\n\n const blob = new Blob(\n [JSON.stringify({ events: this.queue.splice(0, 100) })],\n { type: 'application/json' }\n );\n\n // sendBeacon özel başlık taşıyamaz; anahtar sorgu dizesinden gider.\n navigator.sendBeacon?.(\n `${this.baseUrl}/v1/radio/log/batch?key=${encodeURIComponent(this.config.publicKey)}`,\n blob\n );\n }\n\n destroy(): void {\n if (this.timer) clearInterval(this.timer);\n void this.flush();\n }\n}\n\nlet singleton: SignalbirdBrowser | null = null;\n\n/** Uygulama başlangıcında bir kez çağrılır. */\nexport function initSignalbird(config: BrowserConfig): SignalbirdBrowser {\n singleton = new SignalbirdBrowser(config);\n return singleton;\n}\n\n/** Kurulmamışsa sessizce yok sayar — log çağrısı uygulamayı çökertmemeli. */\nexport function signalbird(): SignalbirdBrowser | null {\n return singleton;\n}\n"]}
|
package/dist/browser.mjs
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// src/browser/index.ts
|
|
2
|
+
var DEFAULT_BASE_URL = "https://signalbird.io/api";
|
|
3
|
+
var SignalbirdBrowser = class {
|
|
4
|
+
constructor(config) {
|
|
5
|
+
this.config = config;
|
|
6
|
+
this.queue = [];
|
|
7
|
+
this.timer = null;
|
|
8
|
+
if (!config.publicKey?.startsWith("sbr_pub_")) {
|
|
9
|
+
throw new Error(
|
|
10
|
+
"Signalbird: taray\u0131c\u0131 istemcisi a\xE7\u0131k anahtar ister (sbr_pub_\u2026). Gizli anahtar\u0131 (sbr_live_\u2026) istemci koduna KOYMAYIN."
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
14
|
+
this.maxQueue = config.maxQueue ?? 50;
|
|
15
|
+
if (typeof window !== "undefined") {
|
|
16
|
+
this.timer = setInterval(() => void this.flush(), config.flushIntervalMs ?? 3e3);
|
|
17
|
+
window.addEventListener("pagehide", () => this.flushBeacon());
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
log(channel, message, level, context) {
|
|
21
|
+
this.queue.push({ channel, message, level, context, source: this.config.source });
|
|
22
|
+
if (this.queue.length >= this.maxQueue) {
|
|
23
|
+
void this.flush();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
info(channel, message, context) {
|
|
27
|
+
this.log(channel, message, "info", context);
|
|
28
|
+
}
|
|
29
|
+
warn(channel, message, context) {
|
|
30
|
+
this.log(channel, message, "warn", context);
|
|
31
|
+
}
|
|
32
|
+
error(channel, message, context) {
|
|
33
|
+
this.log(channel, message, "error", context);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Tarayıcıdaki yakalanmamış hataları bağlar.
|
|
37
|
+
*
|
|
38
|
+
* Kritik kanala YAZMAZ: istemci tarafı kod herkesin elindedir, oradan
|
|
39
|
+
* kritik alarm tetiklemek kötü niyetli birine ekibin telefonunu çaldırma
|
|
40
|
+
* imkânı verirdi. Sunucu zaten `browser_channels` ile bunu kısıtlar.
|
|
41
|
+
*/
|
|
42
|
+
captureErrors(channel = "browser") {
|
|
43
|
+
const onError = (event) => {
|
|
44
|
+
this.error(channel, event.message, {
|
|
45
|
+
file: event.filename,
|
|
46
|
+
line: event.lineno,
|
|
47
|
+
column: event.colno,
|
|
48
|
+
url: window.location.href
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
const onRejection = (event) => {
|
|
52
|
+
this.error(channel, String(event.reason), { url: window.location.href });
|
|
53
|
+
};
|
|
54
|
+
window.addEventListener("error", onError);
|
|
55
|
+
window.addEventListener("unhandledrejection", onRejection);
|
|
56
|
+
return () => {
|
|
57
|
+
window.removeEventListener("error", onError);
|
|
58
|
+
window.removeEventListener("unhandledrejection", onRejection);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
async flush() {
|
|
62
|
+
if (this.queue.length === 0) return;
|
|
63
|
+
const batch = this.queue.splice(0, 100);
|
|
64
|
+
try {
|
|
65
|
+
await fetch(`${this.baseUrl}/v1/radio/log/batch`, {
|
|
66
|
+
method: "POST",
|
|
67
|
+
headers: {
|
|
68
|
+
"Content-Type": "application/json",
|
|
69
|
+
"X-Signalbird-Key": this.config.publicKey
|
|
70
|
+
},
|
|
71
|
+
body: JSON.stringify({ events: batch }),
|
|
72
|
+
keepalive: true
|
|
73
|
+
});
|
|
74
|
+
} catch (error) {
|
|
75
|
+
if (this.config.debug) {
|
|
76
|
+
console.warn("[signalbird] g\xF6nderilemedi", error);
|
|
77
|
+
}
|
|
78
|
+
this.queue = [...batch, ...this.queue].slice(0, this.maxQueue);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** Sayfa kapanırken son gönderim. */
|
|
82
|
+
flushBeacon() {
|
|
83
|
+
if (this.queue.length === 0 || typeof navigator === "undefined") return;
|
|
84
|
+
const blob = new Blob(
|
|
85
|
+
[JSON.stringify({ events: this.queue.splice(0, 100) })],
|
|
86
|
+
{ type: "application/json" }
|
|
87
|
+
);
|
|
88
|
+
navigator.sendBeacon?.(
|
|
89
|
+
`${this.baseUrl}/v1/radio/log/batch?key=${encodeURIComponent(this.config.publicKey)}`,
|
|
90
|
+
blob
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
destroy() {
|
|
94
|
+
if (this.timer) clearInterval(this.timer);
|
|
95
|
+
void this.flush();
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
var singleton = null;
|
|
99
|
+
function initSignalbird(config) {
|
|
100
|
+
singleton = new SignalbirdBrowser(config);
|
|
101
|
+
return singleton;
|
|
102
|
+
}
|
|
103
|
+
function signalbird() {
|
|
104
|
+
return singleton;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export { SignalbirdBrowser, initSignalbird, signalbird };
|
|
108
|
+
//# sourceMappingURL=browser.mjs.map
|
|
109
|
+
//# sourceMappingURL=browser.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/browser/index.ts"],"names":[],"mappings":";AAoCA,IAAM,gBAAA,GAAmB,2BAAA;AAElB,IAAM,oBAAN,MAAwB;AAAA,EAM7B,YAA6B,MAAA,EAAuB;AAAvB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAL7B,IAAA,IAAA,CAAQ,QAAuB,EAAC;AAChC,IAAA,IAAA,CAAQ,KAAA,GAA+C,IAAA;AAKrD,IAAA,IAAI,CAAC,MAAA,CAAO,SAAA,EAAW,UAAA,CAAW,UAAU,CAAA,EAAG;AAC7C,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OAEF;AAAA,IACF;AAEA,IAAA,IAAA,CAAK,WAAW,MAAA,CAAO,OAAA,IAAW,gBAAA,EAAkB,OAAA,CAAQ,OAAO,EAAE,CAAA;AACrE,IAAA,IAAA,CAAK,QAAA,GAAW,OAAO,QAAA,IAAY,EAAA;AAEnC,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,MAAA,IAAA,CAAK,KAAA,GAAQ,YAAY,MAAM,KAAK,KAAK,KAAA,EAAM,EAAG,MAAA,CAAO,eAAA,IAAmB,GAAI,CAAA;AAIhF,MAAA,MAAA,CAAO,gBAAA,CAAiB,UAAA,EAAY,MAAM,IAAA,CAAK,aAAa,CAAA;AAAA,IAC9D;AAAA,EACF;AAAA,EAEA,GAAA,CAAI,OAAA,EAAiB,OAAA,EAAiB,KAAA,EAAe,OAAA,EAAyC;AAC5F,IAAA,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,EAAE,OAAA,EAAS,OAAA,EAAS,KAAA,EAAO,OAAA,EAAS,MAAA,EAAQ,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,CAAA;AAIhF,IAAA,IAAI,IAAA,CAAK,KAAA,CAAM,MAAA,IAAU,IAAA,CAAK,QAAA,EAAU;AACtC,MAAA,KAAK,KAAK,KAAA,EAAM;AAAA,IAClB;AAAA,EACF;AAAA,EAEA,IAAA,CAAK,OAAA,EAAiB,OAAA,EAAiB,OAAA,EAAmC;AACxE,IAAA,IAAA,CAAK,GAAA,CAAI,OAAA,EAAS,OAAA,EAAS,MAAA,EAAQ,OAAO,CAAA;AAAA,EAC5C;AAAA,EAEA,IAAA,CAAK,OAAA,EAAiB,OAAA,EAAiB,OAAA,EAAmC;AACxE,IAAA,IAAA,CAAK,GAAA,CAAI,OAAA,EAAS,OAAA,EAAS,MAAA,EAAQ,OAAO,CAAA;AAAA,EAC5C;AAAA,EAEA,KAAA,CAAM,OAAA,EAAiB,OAAA,EAAiB,OAAA,EAAmC;AACzE,IAAA,IAAA,CAAK,GAAA,CAAI,OAAA,EAAS,OAAA,EAAS,OAAA,EAAS,OAAO,CAAA;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,aAAA,CAAc,UAAU,SAAA,EAAuB;AAC7C,IAAA,MAAM,OAAA,GAAU,CAAC,KAAA,KAAsB;AACrC,MAAA,IAAA,CAAK,KAAA,CAAM,OAAA,EAAS,KAAA,CAAM,OAAA,EAAS;AAAA,QACjC,MAAM,KAAA,CAAM,QAAA;AAAA,QACZ,MAAM,KAAA,CAAM,MAAA;AAAA,QACZ,QAAQ,KAAA,CAAM,KAAA;AAAA,QACd,GAAA,EAAK,OAAO,QAAA,CAAS;AAAA,OACtB,CAAA;AAAA,IACH,CAAA;AAEA,IAAA,MAAM,WAAA,GAAc,CAAC,KAAA,KAAiC;AACpD,MAAA,IAAA,CAAK,KAAA,CAAM,OAAA,EAAS,MAAA,CAAO,KAAA,CAAM,MAAM,CAAA,EAAG,EAAE,GAAA,EAAK,MAAA,CAAO,QAAA,CAAS,IAAA,EAAM,CAAA;AAAA,IACzE,CAAA;AAEA,IAAA,MAAA,CAAO,gBAAA,CAAiB,SAAS,OAAO,CAAA;AACxC,IAAA,MAAA,CAAO,gBAAA,CAAiB,sBAAsB,WAAW,CAAA;AAEzD,IAAA,OAAO,MAAM;AACX,MAAA,MAAA,CAAO,mBAAA,CAAoB,SAAS,OAAO,CAAA;AAC3C,MAAA,MAAA,CAAO,mBAAA,CAAoB,sBAAsB,WAAW,CAAA;AAAA,IAC9D,CAAA;AAAA,EACF;AAAA,EAEA,MAAM,KAAA,GAAuB;AAC3B,IAAA,IAAI,IAAA,CAAK,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG;AAE7B,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,GAAG,GAAG,CAAA;AAEtC,IAAA,IAAI;AACF,MAAA,MAAM,KAAA,CAAM,CAAA,EAAG,IAAA,CAAK,OAAO,CAAA,mBAAA,CAAA,EAAuB;AAAA,QAChD,MAAA,EAAQ,MAAA;AAAA,QACR,OAAA,EAAS;AAAA,UACP,cAAA,EAAgB,kBAAA;AAAA,UAChB,kBAAA,EAAoB,KAAK,MAAA,CAAO;AAAA,SAClC;AAAA,QACA,MAAM,IAAA,CAAK,SAAA,CAAU,EAAE,MAAA,EAAQ,OAAO,CAAA;AAAA,QACtC,SAAA,EAAW;AAAA,OACZ,CAAA;AAAA,IACH,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,IAAA,CAAK,OAAO,KAAA,EAAO;AACrB,QAAA,OAAA,CAAQ,IAAA,CAAK,iCAA8B,KAAK,CAAA;AAAA,MAClD;AAGA,MAAA,IAAA,CAAK,KAAA,GAAQ,CAAC,GAAG,KAAA,EAAO,GAAG,IAAA,CAAK,KAAK,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,IAAA,CAAK,QAAQ,CAAA;AAAA,IAC/D;AAAA,EACF;AAAA;AAAA,EAGQ,WAAA,GAAoB;AAC1B,IAAA,IAAI,KAAK,KAAA,CAAM,MAAA,KAAW,CAAA,IAAK,OAAO,cAAc,WAAA,EAAa;AAEjE,IAAA,MAAM,OAAO,IAAI,IAAA;AAAA,MACf,CAAC,IAAA,CAAK,SAAA,CAAU,EAAE,MAAA,EAAQ,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,CAAA,EAAG,GAAG,CAAA,EAAG,CAAC,CAAA;AAAA,MACtD,EAAE,MAAM,kBAAA;AAAmB,KAC7B;AAGA,IAAA,SAAA,CAAU,UAAA;AAAA,MACR,CAAA,EAAG,KAAK,OAAO,CAAA,wBAAA,EAA2B,mBAAmB,IAAA,CAAK,MAAA,CAAO,SAAS,CAAC,CAAA,CAAA;AAAA,MACnF;AAAA,KACF;AAAA,EACF;AAAA,EAEA,OAAA,GAAgB;AACd,IAAA,IAAI,IAAA,CAAK,KAAA,EAAO,aAAA,CAAc,IAAA,CAAK,KAAK,CAAA;AACxC,IAAA,KAAK,KAAK,KAAA,EAAM;AAAA,EAClB;AACF;AAEA,IAAI,SAAA,GAAsC,IAAA;AAGnC,SAAS,eAAe,MAAA,EAA0C;AACvE,EAAA,SAAA,GAAY,IAAI,kBAAkB,MAAM,CAAA;AACxC,EAAA,OAAO,SAAA;AACT;AAGO,SAAS,UAAA,GAAuC;AACrD,EAAA,OAAO,SAAA;AACT","file":"browser.mjs","sourcesContent":["/**\n * signalbird/browser — tarayıcı istemcisi.\n *\n * Ayrı bir giriş noktası olmasının sebebi teknik değil, GÜVENLİKTİR: sunucu\n * anahtarı istemciye gömülemez ve sunucu `Origin` başlığı taşıyan bir istekte\n * onu zaten reddeder. Tarayıcı, açık anahtarı (`sbr_pub_…`) kullanır ve\n * güvenliği gizlilikten değil kısıttan gelir — yalnız izinli alan adlarından\n * ve yalnız izin verilen kanallara yazabilir.\n *\n * React, Vue, Angular ve düz JS aynı istemciyi kullanır; çatıya özel sarmalayıcı\n * yoktur çünkü gereken tek şey bir fonksiyon çağrısıdır.\n */\nexport type Level = 'debug' | 'info' | 'warn' | 'error' | 'critical';\n\nexport interface BrowserConfig {\n /** Açık anahtar (`sbr_pub_…`). Gizli anahtar BURAYA YAZILMAZ. */\n publicKey: string;\n baseUrl?: string;\n source?: string;\n /**\n * Kayıtlar tek tek değil, toplu gider. Kullanıcının tarayıcısından saniyede\n * onlarca istek çıkarmak hem yavaş hem pahalıdır.\n */\n flushIntervalMs?: number;\n maxQueue?: number;\n debug?: boolean;\n}\n\ninterface QueuedEvent {\n channel: string;\n message: string;\n level?: Level;\n context?: Record<string, unknown>;\n source?: string;\n}\n\nconst DEFAULT_BASE_URL = 'https://signalbird.io/api';\n\nexport class SignalbirdBrowser {\n private queue: QueuedEvent[] = [];\n private timer: ReturnType<typeof setInterval> | null = null;\n private readonly baseUrl: string;\n private readonly maxQueue: number;\n\n constructor(private readonly config: BrowserConfig) {\n if (!config.publicKey?.startsWith('sbr_pub_')) {\n throw new Error(\n 'Signalbird: tarayıcı istemcisi açık anahtar ister (sbr_pub_…). ' +\n 'Gizli anahtarı (sbr_live_…) istemci koduna KOYMAYIN.'\n );\n }\n\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/$/, '');\n this.maxQueue = config.maxQueue ?? 50;\n\n if (typeof window !== 'undefined') {\n this.timer = setInterval(() => void this.flush(), config.flushIntervalMs ?? 3000);\n\n // Sekme kapanırken kuyruk boşaltılır. `sendBeacon` sayfa ölürken bile\n // gider; normal fetch yarıda kesilirdi.\n window.addEventListener('pagehide', () => this.flushBeacon());\n }\n }\n\n log(channel: string, message: string, level?: Level, context?: Record<string, unknown>): void {\n this.queue.push({ channel, message, level, context, source: this.config.source });\n\n // Kuyruk dolduysa beklemeden gönder: bellekte sonsuza kadar biriktirmek,\n // logu hiç göndermemekten kötüdür.\n if (this.queue.length >= this.maxQueue) {\n void this.flush();\n }\n }\n\n info(channel: string, message: string, context?: Record<string, unknown>) {\n this.log(channel, message, 'info', context);\n }\n\n warn(channel: string, message: string, context?: Record<string, unknown>) {\n this.log(channel, message, 'warn', context);\n }\n\n error(channel: string, message: string, context?: Record<string, unknown>) {\n this.log(channel, message, 'error', context);\n }\n\n /**\n * Tarayıcıdaki yakalanmamış hataları bağlar.\n *\n * Kritik kanala YAZMAZ: istemci tarafı kod herkesin elindedir, oradan\n * kritik alarm tetiklemek kötü niyetli birine ekibin telefonunu çaldırma\n * imkânı verirdi. Sunucu zaten `browser_channels` ile bunu kısıtlar.\n */\n captureErrors(channel = 'browser'): () => void {\n const onError = (event: ErrorEvent) => {\n this.error(channel, event.message, {\n file: event.filename,\n line: event.lineno,\n column: event.colno,\n url: window.location.href,\n });\n };\n\n const onRejection = (event: PromiseRejectionEvent) => {\n this.error(channel, String(event.reason), { url: window.location.href });\n };\n\n window.addEventListener('error', onError);\n window.addEventListener('unhandledrejection', onRejection);\n\n return () => {\n window.removeEventListener('error', onError);\n window.removeEventListener('unhandledrejection', onRejection);\n };\n }\n\n async flush(): Promise<void> {\n if (this.queue.length === 0) return;\n\n const batch = this.queue.splice(0, 100);\n\n try {\n await fetch(`${this.baseUrl}/v1/radio/log/batch`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'X-Signalbird-Key': this.config.publicKey,\n },\n body: JSON.stringify({ events: batch }),\n keepalive: true,\n });\n } catch (error) {\n if (this.config.debug) {\n console.warn('[signalbird] gönderilemedi', error);\n }\n // Kayıtlar geri konur ama kuyruk sınırı aşılmaz: ağ uzun süre kapalıysa\n // sekme belleği şişmemeli.\n this.queue = [...batch, ...this.queue].slice(0, this.maxQueue);\n }\n }\n\n /** Sayfa kapanırken son gönderim. */\n private flushBeacon(): void {\n if (this.queue.length === 0 || typeof navigator === 'undefined') return;\n\n const blob = new Blob(\n [JSON.stringify({ events: this.queue.splice(0, 100) })],\n { type: 'application/json' }\n );\n\n // sendBeacon özel başlık taşıyamaz; anahtar sorgu dizesinden gider.\n navigator.sendBeacon?.(\n `${this.baseUrl}/v1/radio/log/batch?key=${encodeURIComponent(this.config.publicKey)}`,\n blob\n );\n }\n\n destroy(): void {\n if (this.timer) clearInterval(this.timer);\n void this.flush();\n }\n}\n\nlet singleton: SignalbirdBrowser | null = null;\n\n/** Uygulama başlangıcında bir kez çağrılır. */\nexport function initSignalbird(config: BrowserConfig): SignalbirdBrowser {\n singleton = new SignalbirdBrowser(config);\n return singleton;\n}\n\n/** Kurulmamışsa sessizce yok sayar — log çağrısı uygulamayı çökertmemeli. */\nexport function signalbird(): SignalbirdBrowser | null {\n return singleton;\n}\n"]}
|