@atsignal/js-core 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,393 @@
1
+ type MaybePromise<T> = T | Promise<T>;
2
+ type FlushMode = "immediate" | "scheduled";
3
+ type FlushReason = "scheduled" | "immediate" | "manual" | "shutdown" | "retry";
4
+ declare const EVENT_TIME_KEY = "@time";
5
+ declare const EVENT_INSERT_ID_KEY = "@insertid";
6
+ declare const EVENT_USER_ID_KEY = "@userid";
7
+ declare const EVENT_TYPE_KEY = "@event";
8
+ interface RetryConfig {
9
+ baseMs?: number;
10
+ maxMs?: number;
11
+ maxAttempts?: number;
12
+ jitter?: number;
13
+ }
14
+ interface TrackedEvent {
15
+ [EVENT_TYPE_KEY]: string;
16
+ [EVENT_TIME_KEY]: number;
17
+ [EVENT_USER_ID_KEY]: string | null;
18
+ [EVENT_INSERT_ID_KEY]: string;
19
+ [key: string]: unknown;
20
+ }
21
+ type TrackProperties = Record<string, unknown>;
22
+ interface TrackOptions {
23
+ flush?: FlushMode;
24
+ endpoint?: string;
25
+ timeoutMs?: number;
26
+ retries?: number;
27
+ }
28
+ interface ResolvedTrackOptions {
29
+ flush: FlushMode;
30
+ endpoint: string;
31
+ timeoutMs?: number;
32
+ retries: number;
33
+ }
34
+ interface PersistedQueueEntry {
35
+ id: string;
36
+ deviceId: string;
37
+ event: TrackedEvent;
38
+ options: ResolvedTrackOptions;
39
+ attempts: number;
40
+ }
41
+ interface TransportEventGroup {
42
+ deviceId: string;
43
+ events: TrackedEvent[];
44
+ }
45
+ interface KeyValueStorage {
46
+ get<T>(key: string): MaybePromise<T | null>;
47
+ set<T>(key: string, value: T): MaybePromise<void>;
48
+ remove(key: string): MaybePromise<void>;
49
+ }
50
+ /**
51
+ * Queue storage contract is intentionally queue-oriented (not key-value)
52
+ * so backends like IndexedDB/SQLite can persist deltas efficiently.
53
+ * `load` must return entries in enqueue order (oldest first).
54
+ */
55
+ interface QueueStorage {
56
+ load(limit: number): MaybePromise<PersistedQueueEntry[]>;
57
+ upsert(entries: PersistedQueueEntry[]): MaybePromise<void>;
58
+ remove(ids: string[]): MaybePromise<void>;
59
+ trim(maxEntries: number): MaybePromise<void>;
60
+ clear(): MaybePromise<void>;
61
+ }
62
+ type QueueStorageInput = QueueStorage | KeyValueStorage;
63
+ interface ClientStorageConfig {
64
+ identity: KeyValueStorage;
65
+ queue: QueueStorageInput;
66
+ }
67
+ interface TransportRequest {
68
+ apiKey: string;
69
+ endpoint: string;
70
+ /**
71
+ * Flattened view of the request payload for transports that want
72
+ * to inspect every event without regrouping by device ID.
73
+ */
74
+ events: TrackedEvent[];
75
+ /**
76
+ * Canonical transport payload grouped by request device ID.
77
+ */
78
+ eventGroups: TransportEventGroup[];
79
+ /**
80
+ * Request-level location-from-IP toggle.
81
+ * This is distinct from any event-level `@ip` value.
82
+ */
83
+ locationFromIp: boolean;
84
+ txTime: number;
85
+ timeoutMs?: number;
86
+ }
87
+ interface TransportResult {
88
+ ok: boolean;
89
+ status?: number;
90
+ retryable?: boolean;
91
+ error?: unknown;
92
+ }
93
+ interface Transport {
94
+ send(request: TransportRequest): Promise<TransportResult>;
95
+ flush?(): Promise<void>;
96
+ shutdown?(): Promise<void>;
97
+ }
98
+ interface ClientIdentity {
99
+ userId: string | null;
100
+ deviceId: string;
101
+ }
102
+ interface ResolvedClientConfig {
103
+ endpoint: string;
104
+ flushIntervalMs: number;
105
+ maxBatchSize: number;
106
+ maxQueueSize: number;
107
+ retry: Required<RetryConfig>;
108
+ /**
109
+ * Queue durability toggle.
110
+ * `true`: persist queue using configured queue storage.
111
+ * `false`: keep queue in memory only.
112
+ */
113
+ persistQueue: boolean;
114
+ /**
115
+ * Request-level location-from-IP toggle.
116
+ * This is distinct from any event-level `@ip` value.
117
+ */
118
+ locationFromIp: boolean;
119
+ optOut: boolean;
120
+ partId: string;
121
+ version?: string;
122
+ blockedProperties: readonly string[];
123
+ }
124
+ interface InitHookContext {
125
+ config: ResolvedClientConfig;
126
+ identity: Readonly<ClientIdentity>;
127
+ optOut: boolean;
128
+ }
129
+ interface EventCreatedHookContext {
130
+ event: TrackedEvent;
131
+ options: Readonly<ResolvedTrackOptions>;
132
+ drop: () => void;
133
+ }
134
+ interface BeforeEnqueueHookContext {
135
+ event: TrackedEvent;
136
+ options: Readonly<ResolvedTrackOptions>;
137
+ drop: () => void;
138
+ }
139
+ interface EventQueuedHookContext {
140
+ event: Readonly<TrackedEvent>;
141
+ options: Readonly<ResolvedTrackOptions>;
142
+ queuedEvents: number;
143
+ }
144
+ interface BeforeFlushHookContext {
145
+ reason: FlushReason;
146
+ queuedEvents: number;
147
+ }
148
+ interface AfterFlushHookContext {
149
+ reason: FlushReason;
150
+ attempted: number;
151
+ sent: number;
152
+ dropped: number;
153
+ queuedEvents: number;
154
+ }
155
+ interface BeforeSendHookContext {
156
+ reason: FlushReason;
157
+ event: TrackedEvent;
158
+ options: Readonly<ResolvedTrackOptions>;
159
+ attempt: number;
160
+ drop: () => void;
161
+ }
162
+ type SendOutcome = "sent" | "retry" | "dropped";
163
+ interface AfterSendHookContext {
164
+ reason: FlushReason;
165
+ event: Readonly<TrackedEvent>;
166
+ options: Readonly<ResolvedTrackOptions>;
167
+ attempt: number;
168
+ outcome: SendOutcome;
169
+ txTime?: number;
170
+ result?: Readonly<TransportResult>;
171
+ queuedEvents: number;
172
+ }
173
+ interface OptOutChangedHookContext {
174
+ previousOptOut: boolean;
175
+ optOut: boolean;
176
+ }
177
+ type IdentityChangeReason = "logged-in" | "logged-out" | "reset";
178
+ interface IdentityChangedHookContext {
179
+ reason: IdentityChangeReason;
180
+ previousIdentity: Readonly<ClientIdentity>;
181
+ identity: Readonly<ClientIdentity>;
182
+ }
183
+ interface ErrorHookContext {
184
+ stage: string;
185
+ error: unknown;
186
+ pluginName?: string;
187
+ }
188
+ type Unsubscribe = () => void;
189
+ interface ClientHooks {
190
+ onInit(listener: (context: InitHookContext) => MaybePromise<void>): Unsubscribe;
191
+ onEventCreated(listener: (context: EventCreatedHookContext) => MaybePromise<void>): Unsubscribe;
192
+ onBeforeEnqueue(listener: (context: BeforeEnqueueHookContext) => MaybePromise<void>): Unsubscribe;
193
+ onEventQueued(listener: (context: EventQueuedHookContext) => MaybePromise<void>): Unsubscribe;
194
+ onBeforeFlush(listener: (context: BeforeFlushHookContext) => MaybePromise<void>): Unsubscribe;
195
+ onAfterFlush(listener: (context: AfterFlushHookContext) => MaybePromise<void>): Unsubscribe;
196
+ onBeforeSend(listener: (context: BeforeSendHookContext) => MaybePromise<void>): Unsubscribe;
197
+ onAfterSend(listener: (context: AfterSendHookContext) => MaybePromise<void>): Unsubscribe;
198
+ onIdentityChanged(listener: (context: IdentityChangedHookContext) => MaybePromise<void>): Unsubscribe;
199
+ onOptOutChanged(listener: (context: OptOutChangedHookContext) => MaybePromise<void>): Unsubscribe;
200
+ onError(listener: (context: ErrorHookContext) => void): Unsubscribe;
201
+ }
202
+ interface PluginSetupContext {
203
+ hooks: ClientHooks;
204
+ config: ResolvedClientConfig;
205
+ }
206
+ interface ClientPlugin {
207
+ name: string;
208
+ setup?(context: PluginSetupContext): MaybePromise<void>;
209
+ flush?(): Promise<void>;
210
+ teardown?(): Promise<void>;
211
+ }
212
+ interface ClientConfig {
213
+ userId?: string | null;
214
+ deviceId?: string;
215
+ /**
216
+ * Request-level location-from-IP toggle.
217
+ * This is distinct from any event-level `@ip` value.
218
+ */
219
+ locationFromIp?: boolean;
220
+ optOut?: boolean;
221
+ version?: string | null;
222
+ endpoint?: string;
223
+ flushIntervalMs?: number;
224
+ maxBatchSize?: number;
225
+ maxQueueSize?: number;
226
+ retry?: RetryConfig;
227
+ /**
228
+ * Queue durability toggle.
229
+ * `true` (default): persist queue using configured `storage.queue`.
230
+ * `false`: keep queue in memory only.
231
+ */
232
+ persistQueue?: boolean;
233
+ partId?: string;
234
+ /**
235
+ * Event property names to omit from queued and transported events.
236
+ * The list is fixed for the lifetime of the client instance.
237
+ */
238
+ blockedProperties?: readonly string[];
239
+ plugins?: ClientPlugin[];
240
+ storage?: Partial<ClientStorageConfig>;
241
+ transport?: Transport;
242
+ }
243
+
244
+ declare class SignalClient<TInitOptions extends object = ClientConfig> {
245
+ private _apiKey;
246
+ private _config;
247
+ private _storage;
248
+ private _transport;
249
+ private _hooks;
250
+ private _insertIdGenerator;
251
+ private _pluginRuntime;
252
+ private _blockedProperties;
253
+ private _identity;
254
+ private _optOut;
255
+ private _queue;
256
+ private _ready;
257
+ private _state;
258
+ private _initError;
259
+ private _queuedMutations;
260
+ private _drainingQueuedMutations;
261
+ private _flushTask;
262
+ private _mutationQueue;
263
+ private _mutationDrainPromise;
264
+ private _automaticFlushPending;
265
+ private _activeAutomaticFlushReason;
266
+ private _acceptEvents;
267
+ private _shutdownPromise;
268
+ hooks(): ClientHooks;
269
+ init(apiKey: string, options?: TInitOptions): Promise<this>;
270
+ protected resolveInitConfig(options: TInitOptions): ClientConfig;
271
+ add(plugin: ClientPlugin): Promise<void>;
272
+ remove(pluginName: string): Promise<boolean>;
273
+ listPlugins(): ClientPlugin[];
274
+ loggedIn(userId: string): Promise<void>;
275
+ loggedOut(): Promise<void>;
276
+ reset(deviceId?: string): Promise<void>;
277
+ userId(): string | null;
278
+ optOut(): boolean;
279
+ setOptOut(optOut: boolean): Promise<void>;
280
+ track(event: string, properties?: TrackProperties, options?: TrackOptions): Promise<void>;
281
+ trackNow(event: string, properties?: TrackProperties, options?: Omit<TrackOptions, "flush">): Promise<void>;
282
+ flush(reason?: FlushReason): Promise<void>;
283
+ shutdown(): Promise<void>;
284
+ config(): Readonly<ResolvedClientConfig>;
285
+ private _applyNormalizedConfig;
286
+ private _requireReadyRuntime;
287
+ private _queueOrRunMutation;
288
+ private _queueOrRunMutationWithResult;
289
+ private _drainQueuedMutations;
290
+ private _rejectQueuedMutations;
291
+ private _shutdownError;
292
+ private _isShutdown;
293
+ private _enqueueMutation;
294
+ private _ensureMutationDrain;
295
+ private _drainMutations;
296
+ private _rejectMutationTask;
297
+ private _awaitMutationPrerequisites;
298
+ private _runMutationTask;
299
+ private _runIdentityMutationTask;
300
+ private _runOptOutMutationTask;
301
+ private _runTrackFamilyMutationTask;
302
+ private _runFlushMutationTask;
303
+ private _runShutdownMutationTask;
304
+ private _initialize;
305
+ private _hydrateIdentity;
306
+ private _applyInitialIdentityOverrides;
307
+ private _hydrateQueue;
308
+ private _createQueueEntry;
309
+ private _resolveTrackOptions;
310
+ private _enqueue;
311
+ private _scheduleAutomaticFlush;
312
+ private _performFlush;
313
+ private _computeBackoff;
314
+ private _persistIdentity;
315
+ private _emitIdentityChanged;
316
+ private _cancelScheduledFlush;
317
+ private _clearQueueStorage;
318
+ private _upsertQueueEntries;
319
+ private _removeQueueEntries;
320
+ private _trimQueueStorage;
321
+ private _emitError;
322
+ private _prepareBatchForTransport;
323
+ private _removeDroppedBeforeSendEntries;
324
+ private _createTransportRequest;
325
+ private _handleSuccessfulTransportBatch;
326
+ private _handleFailedTransportBatch;
327
+ private _emitTransportFailure;
328
+ private _stripBlockedProperties;
329
+ }
330
+
331
+ declare class HookRegistry implements ClientHooks {
332
+ private _onInit;
333
+ private _onEventCreated;
334
+ private _onBeforeEnqueue;
335
+ private _onEventQueued;
336
+ private _onBeforeFlush;
337
+ private _onAfterFlush;
338
+ private _onBeforeSend;
339
+ private _onAfterSend;
340
+ private _onIdentityChanged;
341
+ private _onOptOutChanged;
342
+ private _onError;
343
+ onInit(listener: (context: InitHookContext) => MaybePromise<void>): Unsubscribe;
344
+ onEventCreated(listener: (context: EventCreatedHookContext) => MaybePromise<void>): Unsubscribe;
345
+ onBeforeEnqueue(listener: (context: BeforeEnqueueHookContext) => MaybePromise<void>): Unsubscribe;
346
+ onEventQueued(listener: (context: EventQueuedHookContext) => MaybePromise<void>): Unsubscribe;
347
+ onBeforeFlush(listener: (context: BeforeFlushHookContext) => MaybePromise<void>): Unsubscribe;
348
+ onAfterFlush(listener: (context: AfterFlushHookContext) => MaybePromise<void>): Unsubscribe;
349
+ onBeforeSend(listener: (context: BeforeSendHookContext) => MaybePromise<void>): Unsubscribe;
350
+ onAfterSend(listener: (context: AfterSendHookContext) => MaybePromise<void>): Unsubscribe;
351
+ onIdentityChanged(listener: (context: IdentityChangedHookContext) => MaybePromise<void>): Unsubscribe;
352
+ onOptOutChanged(listener: (context: OptOutChangedHookContext) => MaybePromise<void>): Unsubscribe;
353
+ onError(listener: (context: ErrorHookContext) => void): Unsubscribe;
354
+ emitInit(context: InitHookContext): Promise<void>;
355
+ emitEventCreated(context: EventCreatedHookContext): Promise<void>;
356
+ emitBeforeEnqueue(context: BeforeEnqueueHookContext): Promise<void>;
357
+ emitEventQueued(context: EventQueuedHookContext): Promise<void>;
358
+ emitBeforeFlush(context: BeforeFlushHookContext): Promise<void>;
359
+ emitAfterFlush(context: AfterFlushHookContext): Promise<void>;
360
+ emitBeforeSend(context: BeforeSendHookContext): Promise<void>;
361
+ emitAfterSend(context: AfterSendHookContext): Promise<void>;
362
+ emitIdentityChanged(context: IdentityChangedHookContext): Promise<void>;
363
+ emitOptOutChanged(context: OptOutChangedHookContext): Promise<void>;
364
+ emitError(context: ErrorHookContext): void;
365
+ private _emitAsync;
366
+ }
367
+
368
+ declare class InMemoryStorage implements KeyValueStorage {
369
+ private _map;
370
+ get<T>(key: string): T | null;
371
+ set<T>(key: string, value: T): void;
372
+ remove(key: string): void;
373
+ }
374
+
375
+ interface FetchResponseLike {
376
+ ok: boolean;
377
+ status: number;
378
+ }
379
+ interface FetchRequestInitLike {
380
+ method?: string;
381
+ headers?: Record<string, string>;
382
+ body?: string;
383
+ keepalive?: boolean;
384
+ }
385
+ type FetchLike = (input: string, init?: FetchRequestInitLike) => Promise<FetchResponseLike>;
386
+ interface FetchTransportOptions {
387
+ fetch?: FetchLike;
388
+ keepalive?: boolean;
389
+ headers?: Record<string, string>;
390
+ }
391
+ declare function createFetchTransport(options?: FetchTransportOptions): Transport;
392
+
393
+ export { type ClientConfig, type ClientHooks, type ClientIdentity, type ClientPlugin, type FlushMode, type FlushReason, HookRegistry, InMemoryStorage, type KeyValueStorage, type PersistedQueueEntry, type PluginSetupContext, type QueueStorage, type QueueStorageInput, type ResolvedClientConfig, type RetryConfig, SignalClient, type TrackOptions, type TrackProperties, type TrackedEvent, type Transport, type TransportEventGroup, type TransportRequest, type TransportResult, createFetchTransport };