docs: expand JavaScript SDK documentation

This commit is contained in:
Mo Elzubeir
2026-05-29 13:35:09 -05:00
parent 252ea713b1
commit c860cf6d88
11 changed files with 850 additions and 116 deletions
+12 -2
View File
@@ -1,12 +1,22 @@
/**
* Pluggable cache contract used by SocialhoseClient for GET responses.
* The SDK uses the full request URL as the key.
*/
export interface Cache {
/** Return a cached value, or `undefined` on miss/expiry. */
get(key: string): Promise<unknown | undefined>;
/** Store a value for the given TTL in milliseconds. */
set(key: string, value: unknown, ttlMs: number): Promise<void>;
/** Delete one cache entry. */
delete(key: string): Promise<void>;
}
type Entry = { at: number; value: unknown; ttlMs: number };
/** In-memory Map-backed cache with per-entry TTL. */
/**
* In-memory Map-backed cache with per-entry TTL.
* Best for tests, scripts, and single-process services.
*/
export class MemoryCache implements Cache {
private readonly map = new Map<string, Entry>();
@@ -29,7 +39,7 @@ export class MemoryCache implements Cache {
}
}
/** No-op cache — all operations are no-ops; useful for disabling caching. */
/** Cache implementation that never stores values; useful for disabling caching. */
export class NoopCache implements Cache {
async get(_key: string): Promise<undefined> {
return undefined;
+13
View File
@@ -2,11 +2,13 @@ export type { Cache } from './cache';
export { MemoryCache, NoopCache } from './cache';
import { type Cache, MemoryCache, NoopCache } from './cache';
/** Sentiment label assigned to a mention by Socialhose. */
export type Sentiment = 'positive' | 'negative' | 'neutral';
export type SentimentSplit = { positive: number; negative: number; neutral: number };
export type QueryValue = string | number | boolean | null | undefined;
export type QueryParams = Record<string, QueryValue>;
/** Configuration for {@link SocialhoseClient}. */
export interface SocialhoseClientOptions {
apiKey: string;
baseUrl?: string;
@@ -21,6 +23,7 @@ export interface SocialhoseClientOptions {
defaultHeaders?: Record<string, string>;
}
/** Per-call controls for cache TTL, cancellation, and extra headers. */
export interface RequestOptions {
/**
* Reserved for cache-implementation use; not forwarded to fetch.
@@ -226,6 +229,7 @@ export interface EntityStats extends EntityBrief {
sparkline: { date: string; count: number }[];
}
/** Structured error thrown for failed Socialhose requests. */
export class SocialhoseError extends Error {
readonly status?: number;
readonly path: string;
@@ -312,6 +316,7 @@ function platformMixOf(mentions: Mention[]): PlatformShare[] {
.sort((a, b) => b.count - a.count);
}
/** Authenticated server-side client for the Socialhose Public API. */
export class SocialhoseClient {
readonly apiKey: string;
readonly baseUrl: string;
@@ -326,6 +331,7 @@ export class SocialhoseClient {
private readonly cacheImpl: Cache;
private readonly usedCacheKeys = new Set<string>();
/** Create a client with authentication, retry, timeout, and cache settings. */
constructor(options: SocialhoseClientOptions) {
if (!options.fetch && typeof fetch === 'undefined') throw new Error('A fetch implementation is required');
this.apiKey = options.apiKey;
@@ -340,12 +346,14 @@ export class SocialhoseClient {
this.cacheImpl = options.cache ?? (this.cacheTtlMs > 0 ? new MemoryCache() : new NoopCache());
}
/** Delete cache entries used by this client instance. */
async clearCache(): Promise<void> {
const keys = [...this.usedCacheKeys];
this.usedCacheKeys.clear();
await Promise.all(keys.map((k) => this.cacheImpl.delete(k)));
}
/** Perform an authenticated cached GET request. */
async get<T>(path: string, params: QueryParams = {}, options: RequestOptions = {}): Promise<T> {
const query = encodeParams(params);
const url = `${joinUrl(this.baseUrl, path)}${query ? `?${query}` : ''}`;
@@ -360,20 +368,24 @@ export class SocialhoseClient {
return value;
}
/** Perform an authenticated JSON POST request. */
async post<T>(path: string, body: unknown, options: RequestOptions = {}): Promise<{ status: number; data: T | null }> {
const url = joinUrl(this.baseUrl, path);
return this.requestWithStatus<T>('POST', path, url, body, options);
}
/** Fetch the first page of campaigns. */
async getCampaigns(options: RequestOptions = {}): Promise<Campaign[]> {
const d = await this.get<Paginated<Campaign>>('/campaigns/', { page: 1 }, options);
return d.results;
}
/** Fetch a single campaign by ID. */
getCampaign(id: string, options: RequestOptions = {}): Promise<Campaign> {
return this.get<Campaign>(`/campaigns/${id}/`, {}, options);
}
/** Fetch aggregate overview analytics. */
async getOverview(filters: AnalyticsFilters = {}, options: RequestOptions = {}): Promise<Overview> {
const d = await this.get<Overview>('/analytics/overview/', filters as QueryParams, options);
return { ...d, total_mentions: num(d.total_mentions), total_authors: num(d.total_authors) };
@@ -745,6 +757,7 @@ export class SocialhoseClient {
}
}
/** Create a {@link SocialhoseClient}; convenience wrapper around the constructor. */
export function createSocialhoseClient(options: SocialhoseClientOptions): SocialhoseClient {
return new SocialhoseClient(options);
}