@feltdb/core 0.5.3 → 0.5.4

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-D8oLrr60.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;AAQH;;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;AAiED;;;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,255 @@
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
+ import { randomUUID } from 'crypto';
9
+ import { mkdirSync, readFileSync, writeFileSync, existsSync } from 'fs';
10
+ import { homedir, platform } from 'os';
11
+ import { join } from 'path';
12
+ import { release } from 'os';
13
+ /**
14
+ * Installation identity manager
15
+ * Generates and persists a unique, anonymous installation ID
16
+ */
17
+ class InstallationIdentity {
18
+ constructor() {
19
+ this.id = null;
20
+ const feltdbDir = join(homedir(), '.feltdb');
21
+ this.identityFile = join(feltdbDir, 'install-id');
22
+ }
23
+ /**
24
+ * Get or create installation ID
25
+ */
26
+ getId() {
27
+ if (this.id) {
28
+ return this.id;
29
+ }
30
+ try {
31
+ // Try to read existing ID
32
+ if (existsSync(this.identityFile)) {
33
+ const content = readFileSync(this.identityFile, 'utf-8').trim();
34
+ if (content && this.isValidUUID(content)) {
35
+ this.id = content;
36
+ return this.id;
37
+ }
38
+ }
39
+ // Create new ID
40
+ this.id = randomUUID();
41
+ this.persistId();
42
+ return this.id;
43
+ }
44
+ catch (error) {
45
+ // Fallback: generate in-memory ID if persistence fails
46
+ // This allows telemetry to work even if file system access fails
47
+ if (!this.id) {
48
+ this.id = randomUUID();
49
+ }
50
+ return this.id;
51
+ }
52
+ }
53
+ persistId() {
54
+ try {
55
+ const feltdbDir = join(homedir(), '.feltdb');
56
+ if (!existsSync(feltdbDir)) {
57
+ mkdirSync(feltdbDir, { recursive: true, mode: 0o700 });
58
+ }
59
+ writeFileSync(this.identityFile, this.id, { mode: 0o600 });
60
+ }
61
+ catch (error) {
62
+ // Silent fail - telemetry persistence failure should not affect operation
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.3') {
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 = process.env.FELTDB_TELEMETRY !== '0' &&
84
+ process.env.FELTDB_TELEMETRY !== 'false';
85
+ // Detect browser environment
86
+ if (typeof window !== 'undefined' && typeof process === 'undefined') {
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 = process.env.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
+ const platformName = platform();
191
+ const osVersion = release();
192
+ switch (platformName) {
193
+ case 'darwin':
194
+ return 'macos';
195
+ case 'linux':
196
+ return 'linux';
197
+ case 'win32':
198
+ return 'windows';
199
+ case 'freebsd':
200
+ return 'freebsd';
201
+ default:
202
+ return platformName;
203
+ }
204
+ }
205
+ /**
206
+ * Enable telemetry
207
+ */
208
+ enable() {
209
+ this.enabled = true;
210
+ }
211
+ /**
212
+ * Disable telemetry
213
+ */
214
+ disable() {
215
+ this.enabled = false;
216
+ this.eventQueue = [];
217
+ if (this.flushTimer) {
218
+ clearTimeout(this.flushTimer);
219
+ this.flushTimer = null;
220
+ }
221
+ }
222
+ /**
223
+ * Check if telemetry is enabled
224
+ */
225
+ isEnabled() {
226
+ return this.enabled;
227
+ }
228
+ /**
229
+ * Force flush all pending events
230
+ */
231
+ async forceFlush() {
232
+ while (this.eventQueue.length > 0) {
233
+ await this.flush();
234
+ }
235
+ }
236
+ }
237
+ /**
238
+ * Global telemetry client instance
239
+ */
240
+ let globalTelemetryClient = null;
241
+ /**
242
+ * Get or create global telemetry client
243
+ */
244
+ export function getTelemetryClient(coreVersion) {
245
+ if (!globalTelemetryClient) {
246
+ globalTelemetryClient = new TelemetryClient(coreVersion);
247
+ }
248
+ return globalTelemetryClient;
249
+ }
250
+ /**
251
+ * Emit a telemetry event using global client
252
+ */
253
+ export function emitTelemetry(eventType) {
254
+ getTelemetryClient().emit(eventType);
255
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feltdb/core",
3
- "version": "0.5.3",
3
+ "version": "0.5.4",
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",