react-native-avo-inspector
Version:
[](https://badge.fury.io/js/react-native-avo-inspector)
141 lines (140 loc) • 5.21 kB
JavaScript
import AvoGuid from "./AvoGuid";
import { AvoInspector } from "./AvoInspector";
/**
* AvoStreamId manages an ephemeral stream identifier.
*
* The stream ID is generated and stored persistently, but resets after:
* - 4 hours of total age AND
* - 2 hours of idle time (no activity)
*
* This ensures the ID is truly anonymous by not persisting indefinitely,
* while still maintaining session continuity during active usage.
*/
export class AvoStreamId {
/**
* Get the stream ID. If it doesn't exist or has expired, generates a new one.
* Returns "unknown" if storage is not initialized.
*/
static get streamId() {
// Return cached value if available and not expired
if (AvoStreamId._streamId !== null && !AvoStreamId.shouldReset()) {
AvoStreamId.updateLastActivity();
return AvoStreamId._streamId;
}
if (!AvoInspector.avoStorage.isInitialized()) {
return "unknown";
}
// Load from storage
AvoStreamId.loadFromStorage();
// Check if we should reset
if (AvoStreamId._streamId !== null && AvoStreamId.shouldReset()) {
return AvoStreamId.generateAndStoreNew();
}
// If no ID exists, generate new one
if (AvoStreamId._streamId === null) {
return AvoStreamId.generateAndStoreNew();
}
// Update last activity and return existing
AvoStreamId.updateLastActivity();
return AvoStreamId._streamId;
}
/**
* Check if the stream ID should be reset based on age and idle time.
*/
static shouldReset() {
if (AvoStreamId._createdAt === null || AvoStreamId._lastActivityAt === null) {
return false;
}
const now = Date.now();
const age = now - AvoStreamId._createdAt;
const idleTime = now - AvoStreamId._lastActivityAt;
// Reset if older than 4 hours AND idle for 2+ hours
return age > AvoStreamId.MAX_AGE_MS && idleTime > AvoStreamId.IDLE_THRESHOLD_MS;
}
/**
* Load stream ID and timestamps from storage into cache.
*/
static loadFromStorage() {
try {
const storedId = AvoInspector.avoStorage.getItem(AvoStreamId.STREAM_ID_KEY);
const createdAt = AvoInspector.avoStorage.getItem(AvoStreamId.CREATED_AT_KEY);
const lastActivityAt = AvoInspector.avoStorage.getItem(AvoStreamId.LAST_ACTIVITY_KEY);
if (storedId !== null && storedId !== undefined) {
AvoStreamId._streamId = storedId;
AvoStreamId._createdAt = createdAt ?? Date.now();
AvoStreamId._lastActivityAt = lastActivityAt ?? Date.now();
}
}
catch (e) {
console.error("Avo Inspector: Error reading stream ID from storage. Please report to support@avo.app.", e);
}
}
/**
* Generate a new stream ID and store it with timestamps.
*/
static generateAndStoreNew() {
const now = Date.now();
AvoStreamId._streamId = AvoGuid.newGuid();
AvoStreamId._createdAt = now;
AvoStreamId._lastActivityAt = now;
try {
AvoInspector.avoStorage.setItem(AvoStreamId.STREAM_ID_KEY, AvoStreamId._streamId);
AvoInspector.avoStorage.setItem(AvoStreamId.CREATED_AT_KEY, AvoStreamId._createdAt);
AvoInspector.avoStorage.setItem(AvoStreamId.LAST_ACTIVITY_KEY, AvoStreamId._lastActivityAt);
}
catch (e) {
console.error("Avo Inspector: Error saving stream ID to storage. Please report to support@avo.app.", e);
}
return AvoStreamId._streamId;
}
/**
* Update the last activity timestamp.
*/
static updateLastActivity() {
const now = Date.now();
AvoStreamId._lastActivityAt = now;
try {
AvoInspector.avoStorage.setItem(AvoStreamId.LAST_ACTIVITY_KEY, now);
}
catch (e) {
// Silently fail - not critical
}
}
/**
* The storage key used to persist the stream ID.
*/
static get storageKey() {
return AvoStreamId.STREAM_ID_KEY;
}
/**
* The storage key for created at timestamp.
*/
static get createdAtKey() {
return AvoStreamId.CREATED_AT_KEY;
}
/**
* The storage key for last activity timestamp.
*/
static get lastActivityKey() {
return AvoStreamId.LAST_ACTIVITY_KEY;
}
/**
* Clear the cached stream ID. The next access will reload from storage.
* This is primarily useful for testing.
*/
static clearCache() {
AvoStreamId._streamId = null;
AvoStreamId._createdAt = null;
AvoStreamId._lastActivityAt = null;
}
}
AvoStreamId._streamId = null;
AvoStreamId._createdAt = null;
AvoStreamId._lastActivityAt = null;
// Ephemeral reset thresholds
AvoStreamId.MAX_AGE_MS = 4 * 60 * 60 * 1000; // 4 hours
AvoStreamId.IDLE_THRESHOLD_MS = 2 * 60 * 60 * 1000; // 2 hours
// Storage keys
AvoStreamId.STREAM_ID_KEY = "AvoInspectorStreamId";
AvoStreamId.CREATED_AT_KEY = "AvoInspectorStreamIdCreatedAt";
AvoStreamId.LAST_ACTIVITY_KEY = "AvoInspectorStreamIdLastActivityAt";