zubbl-sdk 1.1.8 → 1.1.11

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.
@@ -11,6 +11,72 @@ let config = {
11
11
  workerSecret: null,
12
12
  };
13
13
 
14
+ // ---- Telemetry State ----
15
+ let sampleRatio = 0.1; // default 1 in 10
16
+ let endpointBuffer = [];
17
+ let debounceTimer = null;
18
+
19
+ function shouldSample() {
20
+ return Math.random() < sampleRatio;
21
+ }
22
+
23
+ function setSamplingRatio(ratio) {
24
+ if (ratio <= 0 || ratio > 1) throw new Error("Sampling ratio must be between 0 and 1");
25
+ sampleRatio = ratio;
26
+ }
27
+
28
+ function emitEndpoint(payload) {
29
+ if (!shouldSample()) return;
30
+ endpointBuffer.push({ ...payload, ts: Date.now(), source: "sdk" });
31
+ if (!debounceTimer) {
32
+ debounceTimer = setTimeout(flushEndpointBuffer, 200);
33
+ }
34
+ }
35
+
36
+ async function flushEndpointBuffer() {
37
+ if (endpointBuffer.length === 0) {
38
+ debounceTimer = null;
39
+ return;
40
+ }
41
+ const batch = [...endpointBuffer];
42
+ endpointBuffer = [];
43
+ debounceTimer = null;
44
+ try {
45
+ await fetch("/api/sdk/telemetry/endpoints", {
46
+ method: "POST",
47
+ headers: { "Content-Type": "application/json" },
48
+ body: JSON.stringify(batch),
49
+ });
50
+ } catch (err) {
51
+ console.error("[Zubbl SDK] Failed to send endpoint telemetry", err);
52
+ }
53
+ }
54
+
55
+ async function zubblFetch(input, init = {}, context = {}) {
56
+ const start = performance.now();
57
+ let response;
58
+ try {
59
+ response = await fetch(input, init);
60
+ return response;
61
+ } finally {
62
+ const latency_ms = performance.now() - start;
63
+ try {
64
+ const url = new URL(input, window.location.origin);
65
+ emitEndpoint({
66
+ tenant_id: context.tenant_id || config.tenantId,
67
+ app_id: context.app_id || config.appId,
68
+ external_user_id: context.external_user_id,
69
+ method: init.method || "GET",
70
+ pathname: url.pathname,
71
+ status: response?.status ?? 0,
72
+ latency_ms,
73
+ });
74
+ } catch (e) {
75
+ console.warn("[Zubbl SDK] Failed to record endpoint telemetry", e);
76
+ }
77
+ }
78
+ }
79
+
14
80
  /**
15
81
  * Initialize SDK with API credentials.
16
82
  */
@@ -113,6 +179,43 @@ async function getTiles({ external_user_id, app_id = null }) {
113
179
  return response.data;
114
180
  }
115
181
 
182
+ async function enforce({ external_user_id, app_id = null }) {
183
+ if (!config.apiKey || !config.tenantId || !config.appId) {
184
+ throw new Error("Zubbl SDK not initialized");
185
+ }
186
+ if (!external_user_id) throw new Error("external_user_id is required");
187
+
188
+ const headers = {
189
+ Authorization: `Bearer ${config.apiKey}`,
190
+ "X-Tenant-Id": config.tenantId,
191
+ "X-App-Id": app_id || config.appId,
192
+ "X-External-User-Id": external_user_id,
193
+ "Content-Type": "application/json",
194
+ };
195
+ if (config.injectWorkerHeaders && config.workerSecret) {
196
+ headers["X-Zubbl-Worker-Secret"] = config.workerSecret;
197
+ headers["X-Zubbl-Internal-Call"] = "true";
198
+ }
199
+
200
+ try {
201
+ const resp = await axios.post(
202
+ `${config.baseUrl.replace(/\/$/, "")}/sdk/test-enforcement`,
203
+ {},
204
+ { headers }
205
+ );
206
+ return { decision: "allow", status: resp.status, data: resp.data };
207
+ } catch (e) {
208
+ if (e.response && e.response.status === 429) {
209
+ return { decision: "block", status: 429, data: e.response.data };
210
+ }
211
+ throw e;
212
+ }
213
+ }
214
+
215
+ exports.emitEndpoint = emitEndpoint;
216
+ exports.enforce = enforce;
116
217
  exports.getTiles = getTiles;
117
218
  exports.identifyUser = identifyUser;
118
219
  exports.init = init;
220
+ exports.setSamplingRatio = setSamplingRatio;
221
+ exports.zubblFetch = zubblFetch;
@@ -9,6 +9,72 @@ let config = {
9
9
  workerSecret: null,
10
10
  };
11
11
 
12
+ // ---- Telemetry State ----
13
+ let sampleRatio = 0.1; // default 1 in 10
14
+ let endpointBuffer = [];
15
+ let debounceTimer = null;
16
+
17
+ function shouldSample() {
18
+ return Math.random() < sampleRatio;
19
+ }
20
+
21
+ function setSamplingRatio(ratio) {
22
+ if (ratio <= 0 || ratio > 1) throw new Error("Sampling ratio must be between 0 and 1");
23
+ sampleRatio = ratio;
24
+ }
25
+
26
+ function emitEndpoint(payload) {
27
+ if (!shouldSample()) return;
28
+ endpointBuffer.push({ ...payload, ts: Date.now(), source: "sdk" });
29
+ if (!debounceTimer) {
30
+ debounceTimer = setTimeout(flushEndpointBuffer, 200);
31
+ }
32
+ }
33
+
34
+ async function flushEndpointBuffer() {
35
+ if (endpointBuffer.length === 0) {
36
+ debounceTimer = null;
37
+ return;
38
+ }
39
+ const batch = [...endpointBuffer];
40
+ endpointBuffer = [];
41
+ debounceTimer = null;
42
+ try {
43
+ await fetch("/api/sdk/telemetry/endpoints", {
44
+ method: "POST",
45
+ headers: { "Content-Type": "application/json" },
46
+ body: JSON.stringify(batch),
47
+ });
48
+ } catch (err) {
49
+ console.error("[Zubbl SDK] Failed to send endpoint telemetry", err);
50
+ }
51
+ }
52
+
53
+ async function zubblFetch(input, init = {}, context = {}) {
54
+ const start = performance.now();
55
+ let response;
56
+ try {
57
+ response = await fetch(input, init);
58
+ return response;
59
+ } finally {
60
+ const latency_ms = performance.now() - start;
61
+ try {
62
+ const url = new URL(input, window.location.origin);
63
+ emitEndpoint({
64
+ tenant_id: context.tenant_id || config.tenantId,
65
+ app_id: context.app_id || config.appId,
66
+ external_user_id: context.external_user_id,
67
+ method: init.method || "GET",
68
+ pathname: url.pathname,
69
+ status: response?.status ?? 0,
70
+ latency_ms,
71
+ });
72
+ } catch (e) {
73
+ console.warn("[Zubbl SDK] Failed to record endpoint telemetry", e);
74
+ }
75
+ }
76
+ }
77
+
12
78
  /**
13
79
  * Initialize SDK with API credentials.
14
80
  */
@@ -111,4 +177,37 @@ async function getTiles({ external_user_id, app_id = null }) {
111
177
  return response.data;
112
178
  }
113
179
 
114
- export { getTiles, identifyUser, init };
180
+ async function enforce({ external_user_id, app_id = null }) {
181
+ if (!config.apiKey || !config.tenantId || !config.appId) {
182
+ throw new Error("Zubbl SDK not initialized");
183
+ }
184
+ if (!external_user_id) throw new Error("external_user_id is required");
185
+
186
+ const headers = {
187
+ Authorization: `Bearer ${config.apiKey}`,
188
+ "X-Tenant-Id": config.tenantId,
189
+ "X-App-Id": app_id || config.appId,
190
+ "X-External-User-Id": external_user_id,
191
+ "Content-Type": "application/json",
192
+ };
193
+ if (config.injectWorkerHeaders && config.workerSecret) {
194
+ headers["X-Zubbl-Worker-Secret"] = config.workerSecret;
195
+ headers["X-Zubbl-Internal-Call"] = "true";
196
+ }
197
+
198
+ try {
199
+ const resp = await axios.post(
200
+ `${config.baseUrl.replace(/\/$/, "")}/sdk/test-enforcement`,
201
+ {},
202
+ { headers }
203
+ );
204
+ return { decision: "allow", status: resp.status, data: resp.data };
205
+ } catch (e) {
206
+ if (e.response && e.response.status === 429) {
207
+ return { decision: "block", status: 429, data: e.response.data };
208
+ }
209
+ throw e;
210
+ }
211
+ }
212
+
213
+ export { emitEndpoint, enforce, getTiles, identifyUser, init, setSamplingRatio, zubblFetch };
@@ -13,6 +13,72 @@
13
13
  workerSecret: null,
14
14
  };
15
15
 
16
+ // ---- Telemetry State ----
17
+ let sampleRatio = 0.1; // default 1 in 10
18
+ let endpointBuffer = [];
19
+ let debounceTimer = null;
20
+
21
+ function shouldSample() {
22
+ return Math.random() < sampleRatio;
23
+ }
24
+
25
+ function setSamplingRatio(ratio) {
26
+ if (ratio <= 0 || ratio > 1) throw new Error("Sampling ratio must be between 0 and 1");
27
+ sampleRatio = ratio;
28
+ }
29
+
30
+ function emitEndpoint(payload) {
31
+ if (!shouldSample()) return;
32
+ endpointBuffer.push({ ...payload, ts: Date.now(), source: "sdk" });
33
+ if (!debounceTimer) {
34
+ debounceTimer = setTimeout(flushEndpointBuffer, 200);
35
+ }
36
+ }
37
+
38
+ async function flushEndpointBuffer() {
39
+ if (endpointBuffer.length === 0) {
40
+ debounceTimer = null;
41
+ return;
42
+ }
43
+ const batch = [...endpointBuffer];
44
+ endpointBuffer = [];
45
+ debounceTimer = null;
46
+ try {
47
+ await fetch("/api/sdk/telemetry/endpoints", {
48
+ method: "POST",
49
+ headers: { "Content-Type": "application/json" },
50
+ body: JSON.stringify(batch),
51
+ });
52
+ } catch (err) {
53
+ console.error("[Zubbl SDK] Failed to send endpoint telemetry", err);
54
+ }
55
+ }
56
+
57
+ async function zubblFetch(input, init = {}, context = {}) {
58
+ const start = performance.now();
59
+ let response;
60
+ try {
61
+ response = await fetch(input, init);
62
+ return response;
63
+ } finally {
64
+ const latency_ms = performance.now() - start;
65
+ try {
66
+ const url = new URL(input, window.location.origin);
67
+ emitEndpoint({
68
+ tenant_id: context.tenant_id || config.tenantId,
69
+ app_id: context.app_id || config.appId,
70
+ external_user_id: context.external_user_id,
71
+ method: init.method || "GET",
72
+ pathname: url.pathname,
73
+ status: response?.status ?? 0,
74
+ latency_ms,
75
+ });
76
+ } catch (e) {
77
+ console.warn("[Zubbl SDK] Failed to record endpoint telemetry", e);
78
+ }
79
+ }
80
+ }
81
+
16
82
  /**
17
83
  * Initialize SDK with API credentials.
18
84
  */
@@ -115,8 +181,45 @@
115
181
  return response.data;
116
182
  }
117
183
 
184
+ async function enforce({ external_user_id, app_id = null }) {
185
+ if (!config.apiKey || !config.tenantId || !config.appId) {
186
+ throw new Error("Zubbl SDK not initialized");
187
+ }
188
+ if (!external_user_id) throw new Error("external_user_id is required");
189
+
190
+ const headers = {
191
+ Authorization: `Bearer ${config.apiKey}`,
192
+ "X-Tenant-Id": config.tenantId,
193
+ "X-App-Id": app_id || config.appId,
194
+ "X-External-User-Id": external_user_id,
195
+ "Content-Type": "application/json",
196
+ };
197
+ if (config.injectWorkerHeaders && config.workerSecret) {
198
+ headers["X-Zubbl-Worker-Secret"] = config.workerSecret;
199
+ headers["X-Zubbl-Internal-Call"] = "true";
200
+ }
201
+
202
+ try {
203
+ const resp = await axios.post(
204
+ `${config.baseUrl.replace(/\/$/, "")}/sdk/test-enforcement`,
205
+ {},
206
+ { headers }
207
+ );
208
+ return { decision: "allow", status: resp.status, data: resp.data };
209
+ } catch (e) {
210
+ if (e.response && e.response.status === 429) {
211
+ return { decision: "block", status: 429, data: e.response.data };
212
+ }
213
+ throw e;
214
+ }
215
+ }
216
+
217
+ exports.emitEndpoint = emitEndpoint;
218
+ exports.enforce = enforce;
118
219
  exports.getTiles = getTiles;
119
220
  exports.identifyUser = identifyUser;
120
221
  exports.init = init;
222
+ exports.setSamplingRatio = setSamplingRatio;
223
+ exports.zubblFetch = zubblFetch;
121
224
 
122
225
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zubbl-sdk",
3
- "version": "1.1.8",
3
+ "version": "1.1.11",
4
4
  "description": "Zubbl SDK for secure policy enforcement (browser, Node, universal)",
5
5
  "main": "dist/zubbl-sdk.cjs.js",
6
6
  "module": "dist/zubbl-sdk.esm.js",