@cusuai/web-sdk 0.2.1 → 0.3.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.
Files changed (2) hide show
  1. package/README.md +33 -56
  2. package/package.json +3 -2
package/README.md CHANGED
@@ -20,7 +20,7 @@ npm install @cusuai/web-sdk
20
20
  - Dictation via `/v1/transcribe` (Bearer public key)
21
21
  - `identify` for logged-in customers (optional HMAC signature from your backend)
22
22
  - Locales: English (`en`), Bulgarian (`bg`), Czech (`cs`), Slovak (`sk`), Spanish (`es`), German (`de`), Estonian (`et`), French (`fr`), Polish (`pl`), Hungarian (`hu`), Italian (`it`), Lithuanian (`lt`), Latvian (`lv`), Dutch (`nl`), Norwegian Bokmål (`no`), Portuguese (`pt`), Danish (`da`), Slovenian (`sl`), Croatian (`hr`), Romanian (`ro`), Swedish (`sv`), and Finnish (`fi`); chrome follows group language from boot, or page/`config.locale` when reply locale is `auto`
23
- - Published builds: **ES module** (`dist/index.js`) + **IIFE** (`dist/cusu.iife.js`, global `Cusu`)
23
+ - Published builds: **ES module** (`dist/index.js`) + **IIFE** script tag on the Zerops CDN (`widget/<version>/cusu.js`, global `Cusu`)
24
24
 
25
25
  ---
26
26
 
@@ -84,12 +84,12 @@ import Cusu from '@cusuai/web-sdk';
84
84
 
85
85
  Types ship with the package (`dist/index.d.ts`).
86
86
 
87
- ### Script tag (IIFE)
87
+ ### Script tag (Shopify, Shoptet, plain HTML)
88
88
 
89
- After install (or from your CDN of the published `dist/`):
89
+ No package manager. Paste this before `</body>` in the theme (`theme.liquid` on Shopify). Pin the version — `latest` can stay cached for up to 30 days.
90
90
 
91
91
  ```html
92
- <script src="https://cdn.example.com/cusu.iife.js"></script>
92
+ <script src="https://storage.cdn.zerops.app/4gfpg-widgetcdn/widget/0.2.0/cusu.js"></script>
93
93
  <script>
94
94
  Cusu.initialize({
95
95
  group: 'grp_…',
@@ -98,7 +98,7 @@ After install (or from your CDN of the published `dist/`):
98
98
  </script>
99
99
  ```
100
100
 
101
- The IIFE build exposes a global `Cusu` with the same API as the default export.
101
+ The script exposes a global `Cusu` with the same API as the default export. The shop origin must be on the public key's allowed origins. Layout, cache, and how a release publishes the file: [docs/widget-cdn.md](./docs/widget-cdn.md).
102
102
 
103
103
  ---
104
104
 
@@ -181,73 +181,50 @@ Cusu.identify('user_123', {
181
181
 
182
182
  ### Signed identify (recommended in production)
183
183
 
184
- When the group has an identify secret (`isk_…`), unsigned `identify` is **rejected**. Sign on your **backend** only.
184
+ When the group has an identify secret (`isk_…`), unsigned `identify` is **rejected**. Sign on your **backend** only — use [`@cusuai/node`](https://github.com/cusuai/cusu-node-sdk) `identifySign` (do not put `isk_…` in the browser).
185
185
 
186
- 1. Read (or set) the first-party cookie `cusu_vid` — that value is the `visitorId`.
187
- 2. Build the canonical string (empty optional fields are empty segments):
188
-
189
- ```text
190
- v1\n{visitorId}\n{externalId}\n{signedAt}\n{name}\n{email}\n{phone}\n{gender}
186
+ ```bash
187
+ npm install @cusuai/node
191
188
  ```
192
189
 
193
- 3. `signature = hex(HMAC-SHA256(secret, canonical))` with `signedAt = Date.now()` (skew window ±5 minutes).
194
- 4. Return traits + `signedAt` + `signature` to the browser; pass them into `Cusu.identify`.
195
-
196
- **Node.js example** (Express / any backend):
190
+ ```ts
191
+ import { identifySign } from '@cusuai/node';
197
192
 
198
- ```js
199
- import { createHmac } from 'node:crypto';
200
-
201
- function signIdentify(secret, { visitorId, externalId, signedAt, name, email, phone, gender }) {
202
- const canonical = [
203
- 'v1',
204
- visitorId,
205
- externalId,
206
- String(signedAt),
207
- name ?? '',
208
- email ?? '',
209
- phone ?? '',
210
- gender ?? ''
211
- ].join('\n');
212
- return createHmac('sha256', secret).update(canonical, 'utf8').digest('hex');
213
- }
214
-
215
- // POST /api/cusu-identify
193
+ // POST /api/cusu-identify — body: { visitorId } from the browser (cusu_vid cookie).
194
+ // Take externalId + traits from your authenticated session / DB.
216
195
  app.post('/api/cusu-identify', (req, res) => {
217
- const externalId = String(req.body.externalId ?? '').trim();
218
- const traits = req.body.traits ?? {};
219
- const visitorId = req.cookies.cusu_vid ?? crypto.randomUUID();
220
- res.cookie('cusu_vid', visitorId, { path: '/', maxAge: 400 * 24 * 60 * 60 * 1000, sameSite: 'lax' });
221
-
222
- const signedAt = Date.now();
223
- const signature = signIdentify(process.env.CUSU_IDENTIFY_SECRET, {
224
- visitorId,
225
- externalId,
226
- signedAt,
227
- name: traits.name,
228
- email: traits.email,
229
- phone: traits.phone,
230
- gender: traits.gender
231
- });
232
-
233
- res.json({ ...traits, signedAt, signature });
196
+ const visitorId = String(req.body.visitorId ?? '').trim();
197
+ res.json(
198
+ identifySign({
199
+ isk: process.env.CUSU_IDENTIFY_SECRET!,
200
+ visitorId,
201
+ externalId: req.user.id,
202
+ traits: {
203
+ name: req.user.name,
204
+ email: req.user.email,
205
+ gender: req.user.gender
206
+ }
207
+ })
208
+ );
234
209
  });
235
210
  ```
236
211
 
237
212
  Browser:
238
213
 
239
214
  ```js
215
+ const visitorId = document.cookie
216
+ .split('; ')
217
+ .find((row) => row.startsWith('cusu_vid='))
218
+ ?.split('=')[1];
219
+
240
220
  const identity = await fetch('/api/cusu-identify', {
241
221
  method: 'POST',
242
222
  headers: { 'content-type': 'application/json' },
243
223
  credentials: 'same-origin',
244
- body: JSON.stringify({
245
- externalId: 'user_123',
246
- traits: { name: 'Jane Doe', email: 'jane@shop.test', gender: 'female' }
247
- })
224
+ body: JSON.stringify({ visitorId })
248
225
  }).then((r) => r.json());
249
226
 
250
- Cusu.identify('user_123', identity);
227
+ Cusu.identify(identity.externalId, identity);
251
228
  ```
252
229
 
253
230
  On **logout**, call `reset()` before the next `identify`. `destroy()` + `initialize()` is not enough: the same `cusu_vid` cookie and local conversation list would keep the previous customer's threads. After `identify`, the SDK replaces local history with that customer's threads from the server.
@@ -256,7 +233,7 @@ On **logout**, call `reset()` before the next `identify`. `destroy()` + `initial
256
233
  Cusu.reset();
257
234
  ```
258
235
 
259
- Full protocol notes: [docs/protocol.md](./docs/protocol.md). Integrator security checklist: [SECURITY.md](./SECURITY.md).
236
+ Package docs: [cusuai/cusu-node-sdk](https://github.com/cusuai/cusu-node-sdk). Full protocol notes: [docs/protocol.md](./docs/protocol.md). Integrator security checklist: [SECURITY.md](./SECURITY.md).
260
237
 
261
238
  ---
262
239
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cusuai/web-sdk",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Framework-agnostic Cusu customer chat widget (Shadow DOM, ES + IIFE).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -44,13 +44,14 @@
44
44
  "check": "svelte-check --tsconfig ./tsconfig.json",
45
45
  "lint": "biome check .",
46
46
  "format": "biome check --write .",
47
- "test": "bun test src",
47
+ "test": "bun test src scripts",
48
48
  "test:coverage": "bun test --coverage --coverage-reporter=text --coverage-reporter=lcov src",
49
49
  "test:coverage:check": "bun run scripts/check-coverage.ts --write-badge",
50
50
  "audit": "bun audit --audit-level=moderate",
51
51
  "ci": "bun run lint && bun run check && bun run test:coverage && bun run test:coverage:check && bun run build && bun run audit"
52
52
  },
53
53
  "devDependencies": {
54
+ "@aws-sdk/client-s3": "^3.1139.0",
54
55
  "@biomejs/biome": "^2.5.12",
55
56
  "@inlang/paraglide-js": "^2.25.0",
56
57
  "@sveltejs/vite-plugin-svelte": "^7.1.2",