@feltdb/core 0.5.3 → 0.5.5

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.
@@ -1,3 +1,3 @@
1
- <!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#17211b"/><title>FeltDB Studio</title> <script type="module" crossorigin src="/assets/index-Z92yFC4z.js"></script>
1
+ <!doctype html><html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#17211b"/><title>FeltDB Studio</title> <script type="module" crossorigin src="/assets/index-DwII1ThH.js"></script>
2
2
  <link rel="stylesheet" crossorigin href="/assets/index-C1GWyazR.css">
3
3
  </head><body><div id="root"></div></body></html>
@@ -0,0 +1,82 @@
1
+ /**
2
+ * FeltDB Adoption Telemetry
3
+ *
4
+ * Privacy-preserving, anonymous telemetry for adoption analytics.
5
+ * No source code, project names, filesystem paths, or PII collected.
6
+ * Fail-open: telemetry failures never affect FeltDB operation.
7
+ */
8
+ /**
9
+ * Telemetry event types
10
+ */
11
+ export type TelemetryEventType = 'initialize' | 'write' | 'query' | 'sync' | 'agent_operation' | 'production_activity';
12
+ /**
13
+ * Telemetry event payload
14
+ */
15
+ export interface TelemetryEvent {
16
+ event: TelemetryEventType;
17
+ installation_id: string;
18
+ core_version: string;
19
+ runtime: 'node' | 'browser';
20
+ os: string;
21
+ timestamp: string;
22
+ }
23
+ /**
24
+ * Telemetry client
25
+ * Handles event collection and transport
26
+ */
27
+ export declare class TelemetryClient {
28
+ private identity;
29
+ private enabled;
30
+ private readonly batchSize;
31
+ private eventQueue;
32
+ private flushTimer;
33
+ private coreVersion;
34
+ private runtime;
35
+ constructor(coreVersion?: string);
36
+ /**
37
+ * Emit a telemetry event
38
+ */
39
+ emit(eventType: TelemetryEventType): void;
40
+ /**
41
+ * Flush pending events to telemetry server
42
+ */
43
+ private flush;
44
+ /**
45
+ * Schedule a flush for pending events
46
+ */
47
+ private scheduleFlush;
48
+ /**
49
+ * Send events to telemetry server
50
+ * Uses fire-and-forget with short timeout
51
+ */
52
+ private sendEvents;
53
+ /**
54
+ * Get normalized OS name
55
+ */
56
+ private getOS;
57
+ /**
58
+ * Enable telemetry
59
+ */
60
+ enable(): void;
61
+ /**
62
+ * Disable telemetry
63
+ */
64
+ disable(): void;
65
+ /**
66
+ * Check if telemetry is enabled
67
+ */
68
+ isEnabled(): boolean;
69
+ /**
70
+ * Force flush all pending events
71
+ */
72
+ forceFlush(): Promise<void>;
73
+ }
74
+ /**
75
+ * Get or create global telemetry client
76
+ */
77
+ export declare function getTelemetryClient(coreVersion?: string): TelemetryClient;
78
+ /**
79
+ * Emit a telemetry event using global client
80
+ */
81
+ export declare function emitTelemetry(eventType: TelemetryEventType): void;
82
+ //# sourceMappingURL=telemetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../src/telemetry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAyBH;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAC1B,YAAY,GACZ,OAAO,GACP,OAAO,GACP,MAAM,GACN,iBAAiB,GACjB,qBAAqB,CAAC;AAE1B;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,kBAAkB,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;CACnB;AA8CD;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAM;IAChC,OAAO,CAAC,UAAU,CAAwB;IAC1C,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,OAAO,CAA8B;gBAEjC,WAAW,GAAE,MAAgB;IAczC;;OAEG;IACH,IAAI,CAAC,SAAS,EAAE,kBAAkB,GAAG,IAAI;IA6BzC;;OAEG;YACW,KAAK;IAqBnB;;OAEG;IACH,OAAO,CAAC,aAAa;IAarB;;;OAGG;YACW,UAAU;IA6BxB;;OAEG;IACH,OAAO,CAAC,KAAK;IAkBb;;OAEG;IACH,MAAM,IAAI,IAAI;IAId;;OAEG;IACH,OAAO,IAAI,IAAI;IASf;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CAKlC;AAOD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,eAAe,CAKxE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,kBAAkB,GAAG,IAAI,CAEjE"}
@@ -0,0 +1,256 @@
1
+ /**
2
+ * FeltDB Adoption Telemetry
3
+ *
4
+ * Privacy-preserving, anonymous telemetry for adoption analytics.
5
+ * No source code, project names, filesystem paths, or PII collected.
6
+ * Fail-open: telemetry failures never affect FeltDB operation.
7
+ */
8
+ const isBrowserRuntime = () => typeof window !== 'undefined' && typeof window.document !== 'undefined';
9
+ const environmentValue = (name) => typeof process !== 'undefined' ? process.env?.[name] : undefined;
10
+ const createUUID = () => {
11
+ const browserCrypto = globalThis.crypto;
12
+ if (typeof browserCrypto?.randomUUID === 'function')
13
+ return browserCrypto.randomUUID();
14
+ const bytes = new Uint8Array(16);
15
+ if (typeof browserCrypto?.getRandomValues === 'function') {
16
+ browserCrypto.getRandomValues(bytes);
17
+ }
18
+ else {
19
+ for (let index = 0; index < bytes.length; index += 1) {
20
+ bytes[index] = Math.floor(Math.random() * 256);
21
+ }
22
+ }
23
+ bytes[6] = (bytes[6] & 0x0f) | 0x40;
24
+ bytes[8] = (bytes[8] & 0x3f) | 0x80;
25
+ const hex = Array.from(bytes, value => value.toString(16).padStart(2, '0')).join('');
26
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
27
+ };
28
+ /**
29
+ * Installation identity manager
30
+ * Generates and persists a unique, anonymous installation ID
31
+ */
32
+ class InstallationIdentity {
33
+ constructor() {
34
+ this.id = null;
35
+ this.identityFile = null;
36
+ }
37
+ /**
38
+ * Get or create installation ID
39
+ */
40
+ getId() {
41
+ if (this.id) {
42
+ return this.id;
43
+ }
44
+ try {
45
+ if (isBrowserRuntime()) {
46
+ const stored = globalThis.localStorage?.getItem('feltdb.install-id');
47
+ this.id = stored && this.isValidUUID(stored) ? stored : createUUID();
48
+ globalThis.localStorage?.setItem('feltdb.install-id', this.id);
49
+ return this.id;
50
+ }
51
+ // Node and non-DOM runtimes keep an in-memory identity. Telemetry must
52
+ // never pull filesystem-only modules into the universal SDK entrypoint.
53
+ this.id = createUUID();
54
+ return this.id;
55
+ }
56
+ catch (error) {
57
+ // Fallback: generate in-memory ID if persistence fails
58
+ // This allows telemetry to work even if file system access fails
59
+ if (!this.id) {
60
+ this.id = createUUID();
61
+ }
62
+ return this.id;
63
+ }
64
+ }
65
+ isValidUUID(str) {
66
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
67
+ return uuidRegex.test(str);
68
+ }
69
+ }
70
+ /**
71
+ * Telemetry client
72
+ * Handles event collection and transport
73
+ */
74
+ export class TelemetryClient {
75
+ constructor(coreVersion = '0.5.5') {
76
+ this.batchSize = 10;
77
+ this.eventQueue = [];
78
+ this.flushTimer = null;
79
+ this.runtime = 'node';
80
+ this.coreVersion = coreVersion;
81
+ this.identity = new InstallationIdentity();
82
+ // Check if telemetry is disabled
83
+ this.enabled = environmentValue('FELTDB_TELEMETRY') !== '0' &&
84
+ environmentValue('FELTDB_TELEMETRY') !== 'false';
85
+ // Detect browser environment
86
+ if (isBrowserRuntime()) {
87
+ this.runtime = 'browser';
88
+ }
89
+ }
90
+ /**
91
+ * Emit a telemetry event
92
+ */
93
+ emit(eventType) {
94
+ if (!this.enabled) {
95
+ return;
96
+ }
97
+ try {
98
+ const event = {
99
+ event: eventType,
100
+ installation_id: this.identity.getId(),
101
+ core_version: this.coreVersion,
102
+ runtime: this.runtime,
103
+ os: this.getOS(),
104
+ timestamp: new Date().toISOString(),
105
+ };
106
+ this.eventQueue.push(event);
107
+ // Flush if batch size reached
108
+ if (this.eventQueue.length >= this.batchSize) {
109
+ this.flush();
110
+ }
111
+ else {
112
+ // Schedule flush if not already scheduled
113
+ this.scheduleFlush();
114
+ }
115
+ }
116
+ catch (error) {
117
+ // Silent fail - telemetry errors should never affect application
118
+ }
119
+ }
120
+ /**
121
+ * Flush pending events to telemetry server
122
+ */
123
+ async flush() {
124
+ if (this.eventQueue.length === 0 || !this.enabled) {
125
+ return;
126
+ }
127
+ // Clear timer
128
+ if (this.flushTimer) {
129
+ clearTimeout(this.flushTimer);
130
+ this.flushTimer = null;
131
+ }
132
+ const events = this.eventQueue.splice(0, this.batchSize);
133
+ try {
134
+ await this.sendEvents(events);
135
+ }
136
+ catch (error) {
137
+ // Silent fail - put events back if send fails (fire-and-forget)
138
+ // Don't retry to avoid blocking
139
+ }
140
+ }
141
+ /**
142
+ * Schedule a flush for pending events
143
+ */
144
+ scheduleFlush() {
145
+ if (this.flushTimer || !this.enabled) {
146
+ return;
147
+ }
148
+ // Flush after 5 seconds or when batch fills
149
+ this.flushTimer = setTimeout(() => {
150
+ this.flush().catch(() => {
151
+ // Silent fail
152
+ });
153
+ }, 5000);
154
+ }
155
+ /**
156
+ * Send events to telemetry server
157
+ * Uses fire-and-forget with short timeout
158
+ */
159
+ async sendEvents(events) {
160
+ const endpoint = environmentValue('FELTDB_TELEMETRY_ENDPOINT') ||
161
+ 'https://kendel.feltdb.com/api/v1/telemetry/events';
162
+ // Timeout after 3 seconds - don't block application
163
+ const controller = new AbortController();
164
+ const timeoutId = setTimeout(() => controller.abort(), 3000);
165
+ try {
166
+ const response = await fetch(endpoint, {
167
+ method: 'POST',
168
+ headers: {
169
+ 'Content-Type': 'application/json',
170
+ },
171
+ body: JSON.stringify({ events }),
172
+ signal: controller.signal,
173
+ });
174
+ if (!response.ok) {
175
+ // Server rejected events - silent fail
176
+ }
177
+ }
178
+ catch (error) {
179
+ // Network error, timeout, or abort - silent fail
180
+ // Telemetry should never block or crash the application
181
+ }
182
+ finally {
183
+ clearTimeout(timeoutId);
184
+ }
185
+ }
186
+ /**
187
+ * Get normalized OS name
188
+ */
189
+ getOS() {
190
+ if (isBrowserRuntime())
191
+ return 'browser';
192
+ const platformName = typeof process !== 'undefined' ? process.platform : 'unknown';
193
+ switch (platformName) {
194
+ case 'darwin':
195
+ return 'macos';
196
+ case 'linux':
197
+ return 'linux';
198
+ case 'win32':
199
+ return 'windows';
200
+ case 'freebsd':
201
+ return 'freebsd';
202
+ default:
203
+ return platformName;
204
+ }
205
+ }
206
+ /**
207
+ * Enable telemetry
208
+ */
209
+ enable() {
210
+ this.enabled = true;
211
+ }
212
+ /**
213
+ * Disable telemetry
214
+ */
215
+ disable() {
216
+ this.enabled = false;
217
+ this.eventQueue = [];
218
+ if (this.flushTimer) {
219
+ clearTimeout(this.flushTimer);
220
+ this.flushTimer = null;
221
+ }
222
+ }
223
+ /**
224
+ * Check if telemetry is enabled
225
+ */
226
+ isEnabled() {
227
+ return this.enabled;
228
+ }
229
+ /**
230
+ * Force flush all pending events
231
+ */
232
+ async forceFlush() {
233
+ while (this.eventQueue.length > 0) {
234
+ await this.flush();
235
+ }
236
+ }
237
+ }
238
+ /**
239
+ * Global telemetry client instance
240
+ */
241
+ let globalTelemetryClient = null;
242
+ /**
243
+ * Get or create global telemetry client
244
+ */
245
+ export function getTelemetryClient(coreVersion) {
246
+ if (!globalTelemetryClient) {
247
+ globalTelemetryClient = new TelemetryClient(coreVersion);
248
+ }
249
+ return globalTelemetryClient;
250
+ }
251
+ /**
252
+ * Emit a telemetry event using global client
253
+ */
254
+ export function emitTelemetry(eventType) {
255
+ getTelemetryClient().emit(eventType);
256
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feltdb/core",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "description": "FeltDB Core - Application-facing database with Browser, Local, and Server runtimes. Durable state, reactive APIs.",
5
5
  "license": "MIT",
6
6
  "type": "module",