@proveanything/smartlinks 1.9.2 → 1.9.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.
package/README.md CHANGED
@@ -22,6 +22,7 @@ If you're new to the SDK, this is the easiest path:
22
22
 
23
23
  - [docs/ai.md](docs/ai.md) — AI responses, chat, RAG, voice, streaming, and product assistants
24
24
  - [docs/analytics.md](docs/analytics.md) — fire-and-forget web analytics, tag scan telemetry, and dashboard queries
25
+ - [docs/translations.md](docs/translations.md) — runtime translation lookup, browser-side caching, and translation admin flows
25
26
  - [docs/widgets.md](docs/widgets.md) — embeddable React components
26
27
  - [docs/realtime.md](docs/realtime.md) — subscriptions and live updates
27
28
  - [docs/iframe-responder.md](docs/iframe-responder.md) — iframe integration and parent/child messaging
@@ -31,6 +32,7 @@ If you're new to the SDK, this is the easiest path:
31
32
 
32
33
  - **Build an AI assistant** → start with [docs/ai.md](docs/ai.md)
33
34
  - **Track page views, clicks, or tag scans** → start with [docs/analytics.md](docs/analytics.md)
35
+ - **Translate dynamic content with local browser caching** → start with [docs/translations.md](docs/translations.md)
34
36
  - **Fetch collections/products** → see [Quick start](README.md#quick-start)
35
37
  - **Authenticate admins or end users** → see [Authentication](README.md#authentication)
36
38
  - **Upload and manage files** → see [Assets](README.md#assets)
@@ -42,6 +44,7 @@ For the full list of functions and types, see the API summary:
42
44
  **Documentation:**
43
45
  - [AI & Chat Completions](docs/ai.md) - Chat completions, RAG, voice integration
44
46
  - [Analytics](docs/analytics.md) - Fire-and-forget analytics tracking, tag scans, and dashboard queries
47
+ - [Translations](docs/translations.md) - Runtime translation lookup, browser-side IndexedDB caching, and admin translation management
45
48
  - [Widgets](docs/widgets.md) - Embeddable React components
46
49
  - [Realtime](docs/realtime.md) - Realtime data updates
47
50
  - [iframe Responder](docs/iframe-responder.md) - iframe integration
@@ -35,3 +35,4 @@ export { app } from "./appObjects";
35
35
  export { attestations } from "./attestations";
36
36
  export { containers } from "./containers";
37
37
  export { loyalty } from "./loyalty";
38
+ export { translations } from "./translations";
package/dist/api/index.js CHANGED
@@ -38,3 +38,4 @@ export { app } from "./appObjects";
38
38
  export { attestations } from "./attestations";
39
39
  export { containers } from "./containers";
40
40
  export { loyalty } from "./loyalty";
41
+ export { translations } from "./translations";
@@ -0,0 +1,12 @@
1
+ import type { ResolvedTranslationResponse, TranslationHashOptions, TranslationListParams, TranslationListResponse, TranslationLookupRequest, TranslationLookupResponse, TranslationRecord, TranslationResolveOptions, TranslationUpdateRequest } from '../types/translations';
2
+ export declare namespace translations {
3
+ function hashText(text: string, options?: TranslationHashOptions): Promise<string>;
4
+ function hashTexts(texts: string[], options?: TranslationHashOptions): Promise<string[]>;
5
+ function normalizeText(text: string, options?: TranslationHashOptions): string;
6
+ function lookup(collectionId: string, body: TranslationLookupRequest): Promise<TranslationLookupResponse>;
7
+ function resolve(collectionId: string, body: TranslationLookupRequest, options?: TranslationResolveOptions): Promise<ResolvedTranslationResponse>;
8
+ function list(collectionId: string, params?: TranslationListParams): Promise<TranslationListResponse>;
9
+ function get(collectionId: string, translationId: string): Promise<TranslationRecord>;
10
+ function update(collectionId: string, translationId: string, body: TranslationUpdateRequest): Promise<TranslationRecord>;
11
+ function clearLocalCache(collectionId?: string): Promise<void>;
12
+ }
@@ -0,0 +1,217 @@
1
+ import { patch, post, request } from '../http';
2
+ import { clearCachedTranslations, deriveTranslationContextKey, getCachedTranslations, getDefaultTranslationCacheTtlMs, hashTranslationText, normalizeTranslationText, setCachedTranslations, } from '../translationCache';
3
+ function normalizeLookupRequest(body) {
4
+ var _a, _b, _c;
5
+ const texts = Array.isArray(body.texts)
6
+ ? [...(body.texts)]
7
+ : typeof body.text === 'string'
8
+ ? [body.text]
9
+ : [];
10
+ if (!body.targetLanguage) {
11
+ throw new Error('[smartlinks] translations.lookup requires targetLanguage');
12
+ }
13
+ if (texts.length === 0) {
14
+ throw new Error('[smartlinks] translations.lookup requires text or texts');
15
+ }
16
+ return {
17
+ targetLanguage: body.targetLanguage,
18
+ sourceLanguage: (_a = body.sourceLanguage) !== null && _a !== void 0 ? _a : 'en',
19
+ mode: (_b = body.mode) !== null && _b !== void 0 ? _b : 'cache-fill',
20
+ contentType: (_c = body.contentType) !== null && _c !== void 0 ? _c : 'text/plain',
21
+ contextKey: deriveTranslationContextKey(body.context),
22
+ requestBody: {
23
+ targetLanguage: body.targetLanguage,
24
+ sourceLanguage: body.sourceLanguage,
25
+ mode: body.mode,
26
+ contentType: body.contentType,
27
+ context: body.context,
28
+ returnMeta: body.returnMeta,
29
+ texts,
30
+ },
31
+ texts,
32
+ };
33
+ }
34
+ function buildTranslationListQuery(params = {}) {
35
+ const query = new URLSearchParams();
36
+ for (const [key, value] of Object.entries(params)) {
37
+ if (value === undefined || value === null)
38
+ continue;
39
+ query.append(key, String(value));
40
+ }
41
+ const queryString = query.toString();
42
+ return queryString ? `?${queryString}` : '';
43
+ }
44
+ function sameLanguage(left, right) {
45
+ return left.trim().toLowerCase() === right.trim().toLowerCase();
46
+ }
47
+ function toResolvedItem(item, index, cacheSource) {
48
+ return Object.assign(Object.assign({}, item), { index,
49
+ cacheSource });
50
+ }
51
+ export var translations;
52
+ (function (translations) {
53
+ async function hashText(text, options) {
54
+ return hashTranslationText(text, options);
55
+ }
56
+ translations.hashText = hashText;
57
+ async function hashTexts(texts, options) {
58
+ return Promise.all(texts.map((text) => hashTranslationText(text, options)));
59
+ }
60
+ translations.hashTexts = hashTexts;
61
+ function normalizeText(text, options) {
62
+ return normalizeTranslationText(text, options);
63
+ }
64
+ translations.normalizeText = normalizeText;
65
+ async function lookup(collectionId, body) {
66
+ const path = `/public/collection/${encodeURIComponent(collectionId)}/translations/lookup`;
67
+ const normalized = normalizeLookupRequest(body);
68
+ return post(path, normalized.requestBody);
69
+ }
70
+ translations.lookup = lookup;
71
+ async function resolve(collectionId, body, options = {}) {
72
+ var _a, _b, _c;
73
+ const normalized = normalizeLookupRequest(body);
74
+ const { useLocalCache = true, refreshLocalCache = false, localCacheTtlMs = getDefaultTranslationCacheTtlMs(), hashOptions, } = options;
75
+ const hashes = await Promise.all(normalized.texts.map((text) => hashTranslationText(text, hashOptions)));
76
+ const results = new Array(normalized.texts.length);
77
+ if (sameLanguage(normalized.sourceLanguage, normalized.targetLanguage)) {
78
+ const passthroughItems = normalized.texts.map((text, index) => ({
79
+ index,
80
+ hash: hashes[index],
81
+ sourceText: text,
82
+ translatedText: text,
83
+ status: 'passthrough',
84
+ quality: 'passthrough',
85
+ cacheSource: 'local',
86
+ expiresAt: Date.now() + localCacheTtlMs,
87
+ }));
88
+ if (useLocalCache) {
89
+ await setCachedTranslations({
90
+ collectionId,
91
+ sourceLanguage: normalized.sourceLanguage,
92
+ targetLanguage: normalized.targetLanguage,
93
+ contentType: normalized.contentType,
94
+ contextKey: normalized.contextKey,
95
+ ttlMs: localCacheTtlMs,
96
+ items: passthroughItems.map((item) => ({ hash: item.hash, item })),
97
+ });
98
+ }
99
+ return {
100
+ targetLanguage: normalized.targetLanguage,
101
+ sourceLanguage: normalized.sourceLanguage,
102
+ mode: normalized.mode,
103
+ items: passthroughItems,
104
+ };
105
+ }
106
+ const cachedByHash = useLocalCache && !refreshLocalCache
107
+ ? await getCachedTranslations({
108
+ collectionId,
109
+ sourceLanguage: normalized.sourceLanguage,
110
+ targetLanguage: normalized.targetLanguage,
111
+ contentType: normalized.contentType,
112
+ contextKey: normalized.contextKey,
113
+ hashes,
114
+ })
115
+ : new Map();
116
+ const missingIndexes = [];
117
+ for (let index = 0; index < normalized.texts.length; index += 1) {
118
+ const cached = cachedByHash.get(hashes[index]);
119
+ if ((_a = cached === null || cached === void 0 ? void 0 : cached.item) === null || _a === void 0 ? void 0 : _a.translatedText) {
120
+ results[index] = toResolvedItem(cached.item, index, 'local');
121
+ }
122
+ else {
123
+ missingIndexes.push(index);
124
+ }
125
+ }
126
+ if (missingIndexes.length === 0) {
127
+ return {
128
+ targetLanguage: normalized.targetLanguage,
129
+ sourceLanguage: normalized.sourceLanguage,
130
+ mode: normalized.mode,
131
+ items: results,
132
+ };
133
+ }
134
+ const uniqueMisses = [];
135
+ const uniqueMissLookup = new Map();
136
+ for (const index of missingIndexes) {
137
+ const hash = hashes[index];
138
+ const existing = uniqueMissLookup.get(hash);
139
+ if (existing) {
140
+ existing.indexes.push(index);
141
+ continue;
142
+ }
143
+ uniqueMissLookup.set(hash, { firstIndex: index, indexes: [index] });
144
+ uniqueMisses.push({ index, hash, text: normalized.texts[index] });
145
+ }
146
+ const remoteResponse = await lookup(collectionId, {
147
+ targetLanguage: normalized.targetLanguage,
148
+ sourceLanguage: body.sourceLanguage,
149
+ mode: body.mode,
150
+ contentType: body.contentType,
151
+ context: body.context,
152
+ returnMeta: body.returnMeta,
153
+ texts: uniqueMisses.map((item) => item.text),
154
+ });
155
+ const cacheableRemoteItems = [];
156
+ remoteResponse.items.forEach((remoteItem, remoteIndex) => {
157
+ var _a, _b;
158
+ const miss = uniqueMisses[remoteIndex];
159
+ if (!miss)
160
+ return;
161
+ const indexes = (_b = (_a = uniqueMissLookup.get(miss.hash)) === null || _a === void 0 ? void 0 : _a.indexes) !== null && _b !== void 0 ? _b : [miss.index];
162
+ for (const index of indexes) {
163
+ const resolvedItem = Object.assign(Object.assign({}, remoteItem), { index, hash: miss.hash, sourceText: normalized.texts[index], cacheSource: 'remote' });
164
+ results[index] = resolvedItem;
165
+ }
166
+ if (remoteItem.translatedText) {
167
+ cacheableRemoteItems.push({
168
+ hash: miss.hash,
169
+ item: Object.assign(Object.assign({}, remoteItem), { index: miss.index, hash: miss.hash, sourceText: miss.text, cacheSource: 'remote' }),
170
+ });
171
+ }
172
+ });
173
+ if (useLocalCache && cacheableRemoteItems.length > 0) {
174
+ await setCachedTranslations({
175
+ collectionId,
176
+ sourceLanguage: normalized.sourceLanguage,
177
+ targetLanguage: normalized.targetLanguage,
178
+ contentType: normalized.contentType,
179
+ contextKey: normalized.contextKey,
180
+ ttlMs: localCacheTtlMs,
181
+ items: cacheableRemoteItems,
182
+ });
183
+ }
184
+ return {
185
+ targetLanguage: remoteResponse.targetLanguage,
186
+ sourceLanguage: (_b = remoteResponse.sourceLanguage) !== null && _b !== void 0 ? _b : normalized.sourceLanguage,
187
+ mode: (_c = remoteResponse.mode) !== null && _c !== void 0 ? _c : normalized.mode,
188
+ items: results.map((item, index) => item !== null && item !== void 0 ? item : {
189
+ index,
190
+ hash: hashes[index],
191
+ sourceText: normalized.texts[index],
192
+ cacheSource: 'remote',
193
+ }),
194
+ };
195
+ }
196
+ translations.resolve = resolve;
197
+ async function list(collectionId, params) {
198
+ const query = buildTranslationListQuery(params);
199
+ const path = `/admin/collection/${encodeURIComponent(collectionId)}/translations${query}`;
200
+ return request(path);
201
+ }
202
+ translations.list = list;
203
+ async function get(collectionId, translationId) {
204
+ const path = `/admin/collection/${encodeURIComponent(collectionId)}/translations/${encodeURIComponent(translationId)}`;
205
+ return request(path);
206
+ }
207
+ translations.get = get;
208
+ async function update(collectionId, translationId, body) {
209
+ const path = `/admin/collection/${encodeURIComponent(collectionId)}/translations/${encodeURIComponent(translationId)}`;
210
+ return patch(path, body);
211
+ }
212
+ translations.update = update;
213
+ async function clearLocalCache(collectionId) {
214
+ await clearCachedTranslations(collectionId);
215
+ }
216
+ translations.clearLocalCache = clearLocalCache;
217
+ })(translations || (translations = {}));
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 1.9.2 | Generated: 2026-03-23T18:11:56.796Z
3
+ Version: 1.9.4 | Generated: 2026-03-23T19:52:46.756Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -110,6 +110,7 @@ The Smartlinks SDK is organized into the following namespaces:
110
110
  - **realtime** - Functions for realtime operations
111
111
  - **tags** - Functions for tags operations
112
112
  - **template** - Functions for template operations
113
+ - **translations** - Functions for translations operations
113
114
 
114
115
  ## HTTP Utilities
115
116
 
@@ -6343,6 +6344,153 @@ interface TemplateRenderSourceResponse {
6343
6344
 
6344
6345
  **TemplatePublic** = `TemplateBase`
6345
6346
 
6347
+ ### translations
6348
+
6349
+ **TranslationContext** (interface)
6350
+ ```typescript
6351
+ interface TranslationContext {
6352
+ surface?: string
6353
+ field?: string
6354
+ [key: string]: TranslationContextValue | undefined
6355
+ }
6356
+ ```
6357
+
6358
+ **TranslationLookupRequestBase** (interface)
6359
+ ```typescript
6360
+ interface TranslationLookupRequestBase {
6361
+ targetLanguage: string
6362
+ sourceLanguage?: string
6363
+ mode?: TranslationLookupMode
6364
+ contentType?: TranslationContentType
6365
+ context?: TranslationContext
6366
+ returnMeta?: boolean
6367
+ }
6368
+ ```
6369
+
6370
+ **TranslationLookupItem** (interface)
6371
+ ```typescript
6372
+ interface TranslationLookupItem {
6373
+ index: number
6374
+ hash: string
6375
+ sourceText: string
6376
+ translatedText?: string
6377
+ status?: TranslationItemStatus
6378
+ provider?: string
6379
+ model?: string
6380
+ isOverride?: boolean
6381
+ quality?: TranslationQuality
6382
+ createdAt?: string
6383
+ updatedAt?: string
6384
+ }
6385
+ ```
6386
+
6387
+ **TranslationLookupResponse** (interface)
6388
+ ```typescript
6389
+ interface TranslationLookupResponse {
6390
+ targetLanguage: string
6391
+ sourceLanguage?: string
6392
+ mode?: TranslationLookupMode
6393
+ items: TranslationLookupItem[]
6394
+ }
6395
+ ```
6396
+
6397
+ **ResolvedTranslationResponse** (interface)
6398
+ ```typescript
6399
+ interface ResolvedTranslationResponse {
6400
+ targetLanguage: string
6401
+ sourceLanguage?: string
6402
+ mode?: TranslationLookupMode
6403
+ items: ResolvedTranslationItem[]
6404
+ }
6405
+ ```
6406
+
6407
+ **TranslationHashOptions** (interface)
6408
+ ```typescript
6409
+ interface TranslationHashOptions {
6410
+ trim?: boolean
6411
+ collapseWhitespace?: boolean
6412
+ unicodeNormalization?: 'NFC' | 'NFKC' | false
6413
+ }
6414
+ ```
6415
+
6416
+ **TranslationResolveOptions** (interface)
6417
+ ```typescript
6418
+ interface TranslationResolveOptions {
6419
+ useLocalCache?: boolean
6420
+ refreshLocalCache?: boolean
6421
+ localCacheTtlMs?: number
6422
+ hashOptions?: TranslationHashOptions
6423
+ }
6424
+ ```
6425
+
6426
+ **TranslationRecord** (interface)
6427
+ ```typescript
6428
+ interface TranslationRecord {
6429
+ id: string
6430
+ collectionId: string
6431
+ sourceHash: string
6432
+ sourceText: string
6433
+ sourceLanguage?: string
6434
+ targetLanguage: string
6435
+ contentType: string
6436
+ contextKey?: string | null
6437
+ translatedText: string
6438
+ provider?: string | null
6439
+ model?: string | null
6440
+ quality: TranslationQuality
6441
+ isOverride: boolean
6442
+ metadata?: Record<string, any>
6443
+ createdAt: string
6444
+ updatedAt: string
6445
+ }
6446
+ ```
6447
+
6448
+ **TranslationListParams** (interface)
6449
+ ```typescript
6450
+ interface TranslationListParams {
6451
+ targetLanguage?: string
6452
+ sourceLanguage?: string
6453
+ contentType?: string
6454
+ contextKey?: string
6455
+ q?: string
6456
+ isOverride?: boolean
6457
+ limit?: number
6458
+ offset?: number
6459
+ }
6460
+ ```
6461
+
6462
+ **TranslationListResponse** (interface)
6463
+ ```typescript
6464
+ interface TranslationListResponse {
6465
+ items: TranslationRecord[]
6466
+ total?: number
6467
+ limit?: number
6468
+ offset?: number
6469
+ }
6470
+ ```
6471
+
6472
+ **TranslationUpdateRequest** (interface)
6473
+ ```typescript
6474
+ interface TranslationUpdateRequest {
6475
+ translatedText?: string
6476
+ isOverride?: boolean
6477
+ quality?: TranslationQuality
6478
+ metadata?: Record<string, any>
6479
+ }
6480
+ ```
6481
+
6482
+ **TranslationLookupMode** = `'cache-fill' | 'cache-only'`
6483
+
6484
+ **TranslationContentType** = `'text/plain' | 'text/html' | 'text/x-liquid' | (string & {})`
6485
+
6486
+ **TranslationQuality** = `'machine' | 'human' | 'passthrough' | (string & {})`
6487
+
6488
+ **TranslationItemStatus** = `'cached' | 'generated' | 'miss' | 'passthrough' | 'local-cache' | (string & {})`
6489
+
6490
+ **TranslationContextValue** = `string | number | boolean | null`
6491
+
6492
+ **TranslationLookupRequest** = `TranslationLookupSingleRequest | TranslationLookupBatchRequest`
6493
+
6346
6494
  ### variant
6347
6495
 
6348
6496
  **VariantResponse** = `any`
@@ -8169,6 +8317,33 @@ Reverse lookup by ref via POST (public). `POST /public/collection/:collectionId/
8169
8317
  **renderSource**(collectionId: string,
8170
8318
  body: TemplateRenderSourceRequest) → `Promise<TemplateRenderSourceResponse>`
8171
8319
 
8320
+ ### translations
8321
+
8322
+ **hashText**(text: string, options?: TranslationHashOptions) → `Promise<string>`
8323
+
8324
+ **hashTexts**(texts: string[], options?: TranslationHashOptions) → `Promise<string[]>`
8325
+
8326
+ **normalizeText**(text: string, options?: TranslationHashOptions) → `string`
8327
+
8328
+ **lookup**(collectionId: string,
8329
+ body: TranslationLookupRequest) → `Promise<TranslationLookupResponse>`
8330
+
8331
+ **resolve**(collectionId: string,
8332
+ body: TranslationLookupRequest,
8333
+ options: TranslationResolveOptions = {}) → `Promise<ResolvedTranslationResponse>`
8334
+
8335
+ **list**(collectionId: string,
8336
+ params?: TranslationListParams) → `Promise<TranslationListResponse>`
8337
+
8338
+ **get**(collectionId: string,
8339
+ translationId: string) → `Promise<TranslationRecord>`
8340
+
8341
+ **update**(collectionId: string,
8342
+ translationId: string,
8343
+ body: TranslationUpdateRequest) → `Promise<TranslationRecord>`
8344
+
8345
+ **clearLocalCache**(collectionId?: string) → `Promise<void>`
8346
+
8172
8347
  ### tts
8173
8348
 
8174
8349
  **generate**(collectionId: string,
@@ -49,6 +49,7 @@ The SmartLinks SDK (`@proveanything/smartlinks`) includes comprehensive document
49
49
  | **Multi-Page Architecture** | `docs/mpa.md` | Build pipeline, entry points, multi-page setup, content hashing |
50
50
  | **AI & Chat** | `docs/ai.md` | Chat completions, RAG, streaming, tool calling, voice, podcasts, TTS |
51
51
  | **Analytics** | `docs/analytics.md` | Fire-and-forget page/click/tag analytics plus admin dashboard queries |
52
+ | **Translations** | `docs/translations.md` | Runtime translation lookup, local-first IndexedDB caching, and translation admin APIs |
52
53
  | **Theming** | `docs/theme.system.md` | Implementing dynamic themes via URL params or postMessage |
53
54
  | **Theme Defaults** | `docs/theme-defaults.md` | Default colour values for light/dark modes |
54
55
  | **Internationalization** | `docs/i18n.md` | Adding multi-language support, translation patterns |