@recapi/client 0.0.0-stage → 0.1.2

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.
package/index.d.ts ADDED
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Type declarations for `@recapi/client`. Hand-written; keep in sync with
3
+ * `src/`.
4
+ */
5
+
6
+ /** Synchronous key/value store with the `localStorage` shape. */
7
+ export interface StorageAdapter {
8
+ getItem(key: string): string | null;
9
+ setItem(key: string, value: string): void;
10
+ removeItem(key: string): void;
11
+ }
12
+
13
+ /** History parameters from docs/history-spec.md. */
14
+ export interface HistoryConfig {
15
+ halfLifeSeconds: number;
16
+ topK: number;
17
+ recentSize: number;
18
+ }
19
+
20
+ export interface TopEntry {
21
+ /** Score valid at time `t`. */
22
+ s: number;
23
+ /** Unix ms of the last in-order interaction. */
24
+ t: number;
25
+ }
26
+
27
+ export interface RecentEntry {
28
+ item: string;
29
+ t: number;
30
+ }
31
+
32
+ export interface HistoryState {
33
+ top: Map<string, TopEntry>;
34
+ /** Oldest to newest. */
35
+ recent: RecentEntry[];
36
+ /** Rejected items and the unix ms of each rejection. */
37
+ rejected: Map<string, number>;
38
+ /** Newest event applied to the recent list, or `null` when unknown. */
39
+ lastT: number | null;
40
+ }
41
+
42
+ /** `[item_id, weight rounded to 4 decimals, age in seconds]`. */
43
+ export type WireEntry = [item: string, weight: number, ageSeconds: number];
44
+
45
+ export interface WireHistory {
46
+ history: WireEntry[];
47
+ recent: string[];
48
+ /** Rejected items, oldest first; `recommend()` sends them in `exclude`. */
49
+ rejected: string[];
50
+ }
51
+
52
+ /** Server-side history snapshot (`history` field of a recommendation response). */
53
+ export interface ServerSnapshot {
54
+ top: Array<[item: string, weight: number]>;
55
+ recent: string[];
56
+ /** Absent from servers that predate rejections. */
57
+ rejected?: string[];
58
+ as_of: number;
59
+ }
60
+
61
+ export interface ClientOptions {
62
+ /** Public key (`rk_pub_…`) in browsers. */
63
+ apiKey: string;
64
+ /** Your recapi API origin, e.g. `https://api.example.com`. */
65
+ baseUrl: string;
66
+ /** Defaults to localStorage when usable, otherwise in-memory. */
67
+ storage?: StorageAdapter;
68
+ /** Storage namespace; defaults to the first 16 characters of the API key. */
69
+ project?: string;
70
+ /** Default placement for `recommend()`. */
71
+ placement?: string;
72
+ /** Default 2000. */
73
+ flushIntervalMs?: number;
74
+ /** Default 100, at most 1000. */
75
+ maxBatch?: number;
76
+ /** Per-request timeout, default 5000; `recommend()` waits at most 1500. */
77
+ timeoutMs?: number;
78
+ historyConfig?: Partial<HistoryConfig>;
79
+ /** Initial storage consent, default `true`. */
80
+ consent?: boolean;
81
+ /** Start without consent when Do Not Track / Global Privacy Control is on. */
82
+ respectDoNotTrack?: boolean;
83
+ /** Receives errors from background work (flushes, storage) and those `recommend()` fell back from. */
84
+ onError?: (error: unknown) => void;
85
+ /** Clock in unix ms. */
86
+ now?: () => number;
87
+ fetch?: typeof fetch;
88
+ /** Backoff sleep, for tests. */
89
+ sleep?: (ms: number) => Promise<void>;
90
+ }
91
+
92
+ export interface RecommendParams {
93
+ count?: number;
94
+ tagsAny?: string[];
95
+ tagsAll?: string[];
96
+ exclude?: string[];
97
+ contextItemId?: string;
98
+ placement?: string;
99
+ discovery?: number;
100
+ }
101
+
102
+ export interface RecommendedItem {
103
+ itemId: string;
104
+ score: number;
105
+ slot: 'exploit' | 'explore' | 'backfill' | string;
106
+ }
107
+
108
+ export interface RecommendResult {
109
+ /** `null` when `fallback` is true: there is nothing to attribute. */
110
+ recommendationId: string | null;
111
+ /**
112
+ * True when recapi was unreachable, slow (1.5 s) or failing (5xx) and
113
+ * `items` are popular items (slot `backfill`) instead. When recapi is down
114
+ * altogether (the popular request failed too), `items` is empty: hide the
115
+ * slot.
116
+ */
117
+ fallback: boolean;
118
+ items: RecommendedItem[];
119
+ }
120
+
121
+ export interface PopularParams {
122
+ tagsAny?: string[];
123
+ tagsAll?: string[];
124
+ count?: number;
125
+ }
126
+
127
+ export interface PopularResult {
128
+ items: Array<{ itemId: string; score: number }>;
129
+ }
130
+
131
+ /**
132
+ * Shop names for the recommended scale: view 3, cart 5, purchase 10. Any catalog can pass the integers instead (3 opened,
133
+ * 5 strong interest, 10 the action that matters most). `reject` (-10) is an explicit "not interested", never a skip.
134
+ */
135
+ export type LevelName = 'view' | 'cart' | 'purchase' | 'reject';
136
+
137
+ export interface RecapiClient {
138
+ track(itemId: string, level: number | LevelName, options?: { recommendationId?: string | null }): void;
139
+ /** Falls back to popular items on a network error, a timeout or a 5xx (an empty list if those fail too); rejects on 4xx and 429. */
140
+ recommend(params?: RecommendParams): Promise<RecommendResult>;
141
+ popular(params?: PopularParams): Promise<PopularResult>;
142
+ identify(userId: string, options?: { history?: ServerSnapshot }): void;
143
+ reset(): void;
144
+ setConsent(value: boolean): void;
145
+ /** Never rejects; failures go to `onError`. */
146
+ flush(): Promise<void>;
147
+ getHistory(): WireHistory;
148
+ destroy(): Promise<void>;
149
+ }
150
+
151
+ export function createClient(options: ClientOptions): RecapiClient;
152
+
153
+ export class RecapiError extends Error {
154
+ constructor(
155
+ code: string,
156
+ message: string,
157
+ extra?: { status?: number; retryAfterMs?: number; details?: unknown; cause?: unknown }
158
+ );
159
+ readonly name: 'RecapiError';
160
+ /** API code (`invalid_request`, `rate_limited`, …) or client code (`network`, `timeout`, `invalid_response`, `event_rejected`, `queue_overflow`). */
161
+ code: string;
162
+ /** HTTP status, 0 when no response was received. */
163
+ status: number;
164
+ retryAfterMs?: number;
165
+ details?: unknown;
166
+ readonly retryable: boolean;
167
+ }
168
+
169
+ export function memoryStorage(): StorageAdapter;
170
+ export function localStorageAdapter(onError?: (error: unknown) => void): StorageAdapter | null;
171
+
172
+ export const DEFAULT_CONFIG: Readonly<HistoryConfig>;
173
+ export function resolveConfig(partial?: Partial<HistoryConfig>): HistoryConfig;
174
+ export function emptyHistory(): HistoryState;
175
+ export function applyEvent(
176
+ state: HistoryState,
177
+ event: { item: string; level: number; t: number },
178
+ config: HistoryConfig
179
+ ): HistoryState;
180
+ export function weightAt(entry: TopEntry, T: number, config: HistoryConfig): number;
181
+ export function sortedEntries(
182
+ state: HistoryState,
183
+ T: number,
184
+ config: HistoryConfig
185
+ ): Array<{ item: string; weight: number; t: number }>;
186
+ export function toWire(state: HistoryState, T: number, config: HistoryConfig): WireHistory;
187
+ export function fromSnapshot(snapshot: ServerSnapshot, config: HistoryConfig): HistoryState;
188
+ export function merge(a: HistoryState, b: HistoryState, T: number, config: HistoryConfig): HistoryState;
package/package.json CHANGED
@@ -1,6 +1,28 @@
1
1
  {
2
2
  "name": "@recapi/client",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.2",
4
+ "description": "Browser and Node client for the recapi recommendation API: local decayed history, event batching, attribution.",
5
+ "type": "module",
6
+ "main": "./src/index.js",
7
+ "types": "./index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./index.d.ts",
11
+ "default": "./src/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "files": [
16
+ "src",
17
+ "index.d.ts",
18
+ "README.md"
19
+ ],
20
+ "sideEffects": false,
21
+ "engines": {
22
+ "node": ">=18"
23
+ },
24
+ "scripts": {
25
+ "test": "node --test test/*.test.js"
26
+ },
27
+ "license": "MIT"
28
+ }