feat(perf): reduce sdk.js bundle size (#15381)
This PR reduces the web SDK's gzipped bundle size by about 30%, lowering the amount of JavaScript downloaded and parsed on uncached SDK loads. It also serves precompressed Brotli or gzip from the existing SDK URL and adds repeatable bundle-analysis tooling to help catch future size regressions. ## What changed ### Use a lightweight synchronous user fingerprint The SDK used MD5 only as a local fingerprint for detecting whether `setUser` data had changed. It was not used as a conversation identifier, authentication mechanism, or part of the server API contract. This PR replaces the MD5 dependency with a small inline 128-bit [FNV-1a](https://www.rfc-editor.org/rfc/rfc9923.html) implementation. FNV-1a is synchronous and works in both secure and insecure embedding pages. This preserves the previous latest-call-wins behavior when integrations call `setUser` repeatedly, without relying on `SubtleCrypto`, which is unavailable on plain HTTP origins. The 128-bit output prevents practical 32-bit collisions from causing distinct identities to be treated as unchanged; regression coverage includes a known pair of identifiers that collide under FNV-1a 32-bit. Existing hash cookies produce a one-time mismatch, causing `setUser` to be sent once before the cookie is replaced with the new fingerprint. The ESLint environment now declares ES2020, matching the SDK's Vite build target and allowing the implementation to use `BigInt` directly. The SDK also used `date-fns` only to add one hour to the current time. Native timestamp arithmetic provides the same behavior for this fixed duration without bundling the associated date utilities. ### Minify inline SDK styles SDK styles now pass through [Vite's inline CSS pipeline](https://v6.vite.dev/guide/features#disabling-css-injection-into-the-page). Vite minifies the stylesheet at build time and returns it as a string, while the SDK continues injecting it into the embedding page as before. ### Serve precompressed bundles from the same URL The SDK build now emits Brotli and gzip sidecars. Rails 7.2 [automatically negotiates precompressed assets](https://api.rubyonrails.org/v7.2.3.1/classes/ActionDispatch/FileHandler.html) and serves the best format supported by the browser from the existing `/packs/js/sdk.js` URL. No embed-code or application URL changes are required. ## Bundle size | Metric | Before | After | Reduction | |---|---:|---:|---:| | Raw | 29.20 kB | 21.75 kB | 25.5% | | Gzip | 9.73 kB | 6.89 kB | 29.2% | | Brotli | — | 6.08 kB | — | ## How to test 1. Load the widget from `/packs/js/sdk.js` in a browser that supports Brotli or gzip and confirm the same URL is returned with the corresponding `Content-Encoding` header. 2. Embed the widget on HTTPS and plain HTTP pages, call `$chatwoot.setUser`, and confirm the user is identified in both contexts. 3. Call `setUser` several times consecutively with different identities and confirm the final call remains the active widget identity. 4. Reload the embedding page with unchanged user data and confirm the SDK does not send a redundant `setUser` update. 5. Confirm the widget layout and styles remain unchanged. ## Staging test #### Before <img width="2192" height="710" alt="CleanShot 2026-08-12 at 16 23 27@2x" src="https://github.com/user-attachments/assets/b60cf6dc-bb12-460c-9bed-d8720010ccf6" /> #### After <img width="2192" height="1014" alt="CleanShot 2026-08-12 at 16 57 53@2x" src="https://github.com/user-attachments/assets/4ce97fab-35a8-44cc-8add-20a0d59d5755" />
This commit is contained in:
@@ -247,6 +247,7 @@ module.exports = {
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
es2020: true,
|
||||
node: true,
|
||||
},
|
||||
globals: {
|
||||
|
||||
59
app/javascript/entrypoints/sdk.spec.js
Normal file
59
app/javascript/entrypoints/sdk.spec.js
Normal file
@@ -0,0 +1,59 @@
|
||||
import Cookies from 'js-cookie';
|
||||
import { IFrameHelper } from '../sdk/IFrameHelper';
|
||||
import './sdk';
|
||||
|
||||
vi.mock('../sdk/IFrameHelper', () => ({
|
||||
IFrameHelper: {
|
||||
createFrame: vi.fn(),
|
||||
sendMessage: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('$chatwoot.setUser', () => {
|
||||
beforeEach(() => {
|
||||
delete window.$chatwoot;
|
||||
window.chatwootSettings = {};
|
||||
vi.spyOn(Cookies, 'get').mockReturnValue(undefined);
|
||||
vi.spyOn(Cookies, 'set').mockImplementation(() => {});
|
||||
|
||||
window.chatwootSDK.run({
|
||||
baseUrl: 'https://app.chatwoot.com',
|
||||
websiteToken: 'website-token',
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
delete window.$chatwoot;
|
||||
delete window.chatwootSettings;
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('updates the SDK identity synchronously', () => {
|
||||
const user = { name: 'Pranav' };
|
||||
|
||||
const result = window.$chatwoot.setUser('first-user', user);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
expect(window.$chatwoot.identifier).toBe('first-user');
|
||||
expect(window.$chatwoot.user).toBe(user);
|
||||
expect(IFrameHelper.sendMessage).toHaveBeenCalledWith('set-user', {
|
||||
identifier: 'first-user',
|
||||
user,
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps the latest identity after consecutive calls', () => {
|
||||
const firstUser = { name: 'First user' };
|
||||
const secondUser = { name: 'Second user' };
|
||||
|
||||
window.$chatwoot.setUser('first-user', firstUser);
|
||||
window.$chatwoot.setUser('second-user', secondUser);
|
||||
|
||||
expect(window.$chatwoot.identifier).toBe('second-user');
|
||||
expect(window.$chatwoot.user).toBe(secondUser);
|
||||
expect(IFrameHelper.sendMessage).toHaveBeenLastCalledWith('set-user', {
|
||||
identifier: 'second-user',
|
||||
user: secondUser,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
import { SDK_CSS } from './sdk.js';
|
||||
import SDK_CSS from './sdk.css?inline';
|
||||
import { IFrameHelper } from './IFrameHelper';
|
||||
|
||||
export const loadCSS = () => {
|
||||
|
||||
@@ -35,7 +35,6 @@ import {
|
||||
} from 'shared/helpers/AudioNotificationHelper';
|
||||
import { isFlatWidgetStyle } from './settingsHelper';
|
||||
import { popoutChatWindow } from '../widget/helpers/popoutHelper';
|
||||
import addHours from 'date-fns/addHours';
|
||||
|
||||
const updateAuthCookie = (cookieContent, baseDomain = '') =>
|
||||
setCookieWithDomain('cw_conversation', cookieContent, {
|
||||
@@ -43,7 +42,7 @@ const updateAuthCookie = (cookieContent, baseDomain = '') =>
|
||||
});
|
||||
|
||||
const updateCampaignReadStatus = baseDomain => {
|
||||
const expireBy = addHours(new Date(), 1);
|
||||
const expireBy = new Date(Date.now() + 60 * 60 * 1000);
|
||||
setCookieWithDomain('cw_snooze_campaigns_till', Number(expireBy), {
|
||||
expires: expireBy,
|
||||
baseDomain,
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import md5 from 'md5';
|
||||
import Cookies from 'js-cookie';
|
||||
|
||||
const REQUIRED_USER_KEYS = ['avatar_url', 'email', 'name'];
|
||||
const ALLOWED_USER_ATTRIBUTES = [...REQUIRED_USER_KEYS, 'identifier_hash'];
|
||||
const FNV1A_128_OFFSET_BASIS = 0x6c62272e07bb014262b821756295c58dn;
|
||||
const FNV1A_128_PRIME = 0x1000000000000000000013bn;
|
||||
const CONTACT_INFORMATION_ATTRIBUTES = [
|
||||
'phone_number',
|
||||
'company_name',
|
||||
@@ -37,7 +38,20 @@ export const getUserString = ({ identifier = '', user }) => {
|
||||
return JSON.stringify(userAttributes);
|
||||
};
|
||||
|
||||
export const computeHashForUserData = (...args) => md5(getUserString(...args));
|
||||
export const fnv1a128 = value => {
|
||||
const data = new TextEncoder().encode(value);
|
||||
let hash = FNV1A_128_OFFSET_BASIS;
|
||||
|
||||
data.forEach(byte => {
|
||||
hash ^= BigInt(byte); // eslint-disable-line no-bitwise
|
||||
hash = BigInt.asUintN(128, hash * FNV1A_128_PRIME);
|
||||
});
|
||||
|
||||
return hash.toString(16).padStart(32, '0');
|
||||
};
|
||||
|
||||
export const computeHashForUserData = (...args) =>
|
||||
fnv1a128(getUserString(...args));
|
||||
|
||||
export const hasUserKeys = user =>
|
||||
REQUIRED_USER_KEYS.reduce((acc, key) => acc || !!user[key], false);
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
export const SDK_CSS = `
|
||||
:root {
|
||||
--b-100: #F2F3F7;
|
||||
--s-700: #37546D;
|
||||
--b-100: #f2f3f7;
|
||||
--s-700: #37546d;
|
||||
}
|
||||
|
||||
.woot-widget-holder {
|
||||
box-shadow: 0 5px 40px rgba(0, 0, 0, .16);
|
||||
box-shadow: 0 5px 40px rgba(0, 0, 0, 0.16);
|
||||
opacity: 1;
|
||||
will-change: transform, opacity;
|
||||
transform: translateY(0);
|
||||
overflow: hidden !important;
|
||||
position: fixed !important;
|
||||
transition: opacity 0.2s linear, transform 0.25s linear;
|
||||
transition:
|
||||
opacity 0.2s linear,
|
||||
transform 0.25s linear;
|
||||
z-index: 2147483000 !important;
|
||||
}
|
||||
|
||||
@@ -43,7 +44,7 @@ export const SDK_CSS = `
|
||||
border-radius: 100px;
|
||||
border-width: 0px;
|
||||
bottom: 20px;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, .16) !important;
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.16) !important;
|
||||
cursor: pointer;
|
||||
height: 64px;
|
||||
padding: 0px;
|
||||
@@ -107,7 +108,18 @@ export const SDK_CSS = `
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen-Sans, Ubuntu, Cantarell, Helvetica Neue, Arial, sans-serif;
|
||||
font-family:
|
||||
system-ui,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
Segoe UI,
|
||||
Roboto,
|
||||
Oxygen-Sans,
|
||||
Ubuntu,
|
||||
Cantarell,
|
||||
Helvetica Neue,
|
||||
Arial,
|
||||
sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
justify-content: center;
|
||||
@@ -115,7 +127,8 @@ export const SDK_CSS = `
|
||||
width: auto !important;
|
||||
}
|
||||
|
||||
.woot-widget-bubble.woot-widget--expanded.woot-widget-bubble-color--lighter div{
|
||||
.woot-widget-bubble.woot-widget--expanded.woot-widget-bubble-color--lighter
|
||||
div {
|
||||
color: var(--s-700);
|
||||
}
|
||||
|
||||
@@ -135,7 +148,7 @@ export const SDK_CSS = `
|
||||
|
||||
.woot-widget-bubble:hover {
|
||||
background: #1f93ff;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, .4) !important;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4) !important;
|
||||
}
|
||||
|
||||
.woot-widget-bubble svg {
|
||||
@@ -146,24 +159,25 @@ export const SDK_CSS = `
|
||||
width: 24px !important;
|
||||
}
|
||||
|
||||
.woot-widget-bubble.woot-widget-bubble-color--lighter path{
|
||||
.woot-widget-bubble.woot-widget-bubble-color--lighter path {
|
||||
fill: var(--s-700);
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 667px) {
|
||||
.woot-widget-holder.woot-elements--left {
|
||||
left: 20px;
|
||||
}
|
||||
}
|
||||
.woot-widget-holder.woot-elements--right {
|
||||
right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.woot--close:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.woot--close::before, .woot--close::after {
|
||||
.woot--close::before,
|
||||
.woot--close::after {
|
||||
background-color: #fff;
|
||||
content: ' ';
|
||||
display: inline;
|
||||
@@ -174,7 +188,8 @@ export const SDK_CSS = `
|
||||
width: 2px;
|
||||
}
|
||||
|
||||
.woot-widget-bubble-color--lighter.woot--close::before, .woot-widget-bubble-color--lighter.woot--close::after {
|
||||
.woot-widget-bubble-color--lighter.woot--close::before,
|
||||
.woot-widget-bubble-color--lighter.woot--close::after {
|
||||
background-color: var(--s-700);
|
||||
}
|
||||
|
||||
@@ -197,52 +212,92 @@ export const SDK_CSS = `
|
||||
.woot-widget--without-bubble {
|
||||
bottom: 20px !important;
|
||||
}
|
||||
.woot-widget-holder.woot--hide{
|
||||
.woot-widget-holder.woot--hide {
|
||||
transform: translateY(40px);
|
||||
}
|
||||
.woot-widget-bubble.woot--close {
|
||||
transform: translateX(0px) scale(1) rotate(0deg);
|
||||
transition: transform 300ms ease, opacity 100ms ease, visibility 0ms linear 0ms, bottom 0ms linear 0ms;
|
||||
transition:
|
||||
transform 300ms ease,
|
||||
opacity 100ms ease,
|
||||
visibility 0ms linear 0ms,
|
||||
bottom 0ms linear 0ms;
|
||||
}
|
||||
.woot-widget-bubble.woot--close.woot--hide {
|
||||
transform: translateX(8px) scale(.75) rotate(45deg);
|
||||
transition: transform 300ms ease, opacity 200ms ease, visibility 0ms linear 500ms, bottom 0ms ease 200ms;
|
||||
transform: translateX(8px) scale(0.75) rotate(45deg);
|
||||
transition:
|
||||
transform 300ms ease,
|
||||
opacity 200ms ease,
|
||||
visibility 0ms linear 500ms,
|
||||
bottom 0ms ease 200ms;
|
||||
}
|
||||
|
||||
.woot-widget-bubble {
|
||||
transform-origin: center;
|
||||
will-change: transform, opacity;
|
||||
transform: translateX(0) scale(1) rotate(0deg);
|
||||
transition: transform 300ms ease, opacity 100ms ease, visibility 0ms linear 0ms, bottom 0ms linear 0ms;
|
||||
transition:
|
||||
transform 300ms ease,
|
||||
opacity 100ms ease,
|
||||
visibility 0ms linear 0ms,
|
||||
bottom 0ms linear 0ms;
|
||||
}
|
||||
.woot-widget-bubble.woot--hide {
|
||||
transform: translateX(8px) scale(.75) rotate(-30deg);
|
||||
transition: transform 300ms ease, opacity 200ms ease, visibility 0ms linear 500ms, bottom 0ms ease 200ms;
|
||||
transform: translateX(8px) scale(0.75) rotate(-30deg);
|
||||
transition:
|
||||
transform 300ms ease,
|
||||
opacity 200ms ease,
|
||||
visibility 0ms linear 500ms,
|
||||
bottom 0ms ease 200ms;
|
||||
}
|
||||
|
||||
.woot-widget-bubble.woot-widget--expanded {
|
||||
transform: translateX(0px);
|
||||
transition: transform 300ms ease, opacity 100ms ease, visibility 0ms linear 0ms, bottom 0ms linear 0ms;
|
||||
transition:
|
||||
transform 300ms ease,
|
||||
opacity 100ms ease,
|
||||
visibility 0ms linear 0ms,
|
||||
bottom 0ms linear 0ms;
|
||||
}
|
||||
.woot-widget-bubble.woot-widget--expanded.woot--hide {
|
||||
transform: translateX(8px);
|
||||
transition: transform 300ms ease, opacity 200ms ease, visibility 0ms linear 500ms, bottom 0ms ease 200ms;
|
||||
transition:
|
||||
transform 300ms ease,
|
||||
opacity 200ms ease,
|
||||
visibility 0ms linear 500ms,
|
||||
bottom 0ms ease 200ms;
|
||||
}
|
||||
.woot-widget-bubble.woot-widget-bubble--flat.woot--close {
|
||||
transform: translateX(0px);
|
||||
transition: transform 300ms ease, opacity 10ms ease, visibility 0ms linear 0ms, bottom 0ms linear 0ms;
|
||||
transition:
|
||||
transform 300ms ease,
|
||||
opacity 10ms ease,
|
||||
visibility 0ms linear 0ms,
|
||||
bottom 0ms linear 0ms;
|
||||
}
|
||||
.woot-widget-bubble.woot-widget-bubble--flat.woot--close.woot--hide {
|
||||
transform: translateX(8px);
|
||||
transition: transform 300ms ease, opacity 200ms ease, visibility 0ms linear 500ms, bottom 0ms ease 200ms;
|
||||
transition:
|
||||
transform 300ms ease,
|
||||
opacity 200ms ease,
|
||||
visibility 0ms linear 500ms,
|
||||
bottom 0ms ease 200ms;
|
||||
}
|
||||
.woot-widget-bubble.woot-widget--expanded.woot-widget-bubble--flat {
|
||||
transform: translateX(0px);
|
||||
transition: transform 300ms ease, opacity 200ms ease, visibility 0ms linear 0ms, bottom 0ms linear 0ms;
|
||||
transition:
|
||||
transform 300ms ease,
|
||||
opacity 200ms ease,
|
||||
visibility 0ms linear 0ms,
|
||||
bottom 0ms linear 0ms;
|
||||
}
|
||||
.woot-widget-bubble.woot-widget--expanded.woot-widget-bubble--flat.woot--hide {
|
||||
transform: translateX(8px);
|
||||
transition: transform 300ms ease, opacity 200ms ease, visibility 0ms linear 500ms, bottom 0ms ease 200ms;
|
||||
transition:
|
||||
transform 300ms ease,
|
||||
opacity 200ms ease,
|
||||
visibility 0ms linear 500ms,
|
||||
bottom 0ms ease 200ms;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 667px) {
|
||||
@@ -251,14 +306,13 @@ export const SDK_CSS = `
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.woot-widget-holder iframe {
|
||||
.woot-widget-holder iframe {
|
||||
min-height: 100% !important;
|
||||
}
|
||||
|
||||
|
||||
.woot-widget-holder.has-unread-view {
|
||||
.woot-widget-holder.has-unread-view {
|
||||
height: auto;
|
||||
right: 0;
|
||||
width: auto;
|
||||
@@ -272,7 +326,7 @@ export const SDK_CSS = `
|
||||
min-height: unset !important;
|
||||
}
|
||||
|
||||
.woot-widget-holder.has-unread-view.woot-elements--left {
|
||||
.woot-widget-holder.has-unread-view.woot-elements--left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
@@ -292,10 +346,9 @@ export const SDK_CSS = `
|
||||
max-height: 640px !important;
|
||||
min-height: 250px !important;
|
||||
width: 400px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.woot-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
`;
|
||||
@@ -1,6 +1,7 @@
|
||||
import Cookies from 'js-cookie';
|
||||
import {
|
||||
computeHashForUserData,
|
||||
fnv1a128,
|
||||
getUserCookieName,
|
||||
getUserString,
|
||||
hasUserKeys,
|
||||
@@ -55,6 +56,155 @@ describe('#getUserString', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#fnv1a128', () => {
|
||||
// Adapted from https://github.com/sindresorhus/fnv1a/blob/main/test.js
|
||||
it.each([
|
||||
['', '6c62272e07bb014262b821756295c58d'],
|
||||
['h', 'd228cb69681a8caf78912b704e4a80c7'],
|
||||
['he', '08809533baab1be95aa0733055ac4756'],
|
||||
['hel', 'a68d42edea8b5822836dbc796afba45e'],
|
||||
['hell', '693c5663cb757277b806e966a3a30986'],
|
||||
['hello', 'e3e1efd54283d94f7081314b599d31b3'],
|
||||
['hello ', 'b25bb89a6b3c64bf6ef7a7b7446bffe1'],
|
||||
['hello w', '2e209201894ff78d8abb5e8130e37d92'],
|
||||
['hello wo', '43448b61f2659b29b48d48f727ec064f'],
|
||||
['hello wor', 'bc7f6d8b8005ec5129d8c81e1f6bad0f'],
|
||||
['hello worl', '0eeb3653ea49c7de7dbe3d10a97e58d1'],
|
||||
['hello world', '6c155799fdc8eec4b91523808e7726b7'],
|
||||
])('hashes %j to %s', (value, expectedHash) => {
|
||||
expect(fnv1a128(value)).toBe(expectedHash);
|
||||
});
|
||||
|
||||
it('hashes long input', () => {
|
||||
const loremIpsumParagraph = [
|
||||
'Lorem ipsum dolor sit amet, consectetuer adipiscing elit.',
|
||||
'Aenean commodo ligula eget dolor.',
|
||||
'Aenean massa.',
|
||||
'Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.',
|
||||
'Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem.',
|
||||
'Nulla consequat massa quis enim.',
|
||||
'Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu.',
|
||||
'In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo.',
|
||||
'Nullam dictum felis eu pede mollis pretium.',
|
||||
].join(' ');
|
||||
const value = Array(3).fill(loremIpsumParagraph).join(' ');
|
||||
|
||||
expect(fnv1a128(value)).toBe('4fedf302b27b5c4dbaf35bd87a91f589');
|
||||
});
|
||||
|
||||
it('hashes multi-byte UTF-8 characters', () => {
|
||||
expect(fnv1a128('🦄🌈')).toBe('0a25841ae4659905b36cb0d359fad39f');
|
||||
});
|
||||
|
||||
it('hashes characters from across the Unicode range', () => {
|
||||
const codePoints = [
|
||||
0x0000, 0x0080, 0x0100, 0x0180, 0x0250, 0x02b0, 0x0300, 0x0370, 0x0400,
|
||||
0x0500, 0x0530, 0x0590, 0x0600, 0x0700, 0x0780, 0x0900, 0x0980, 0x0a00,
|
||||
0x0a80, 0x0b00, 0x0b80, 0x0c00, 0x0c80, 0x0d00, 0x0d80, 0x0e00, 0x0e80,
|
||||
0x0f00, 0x1000, 0x10a0, 0x1100, 0x1200, 0x13a0, 0x1400, 0x1680, 0x16a0,
|
||||
0x1700, 0x1720, 0x1740, 0x1760, 0x1780, 0x1800, 0x1900, 0x1950, 0x19e0,
|
||||
0x1d00, 0x1e00, 0x1f00, 0x2000, 0x2070, 0x20a0, 0x20d0, 0x2100, 0x2150,
|
||||
0x2190, 0x2200, 0x2300, 0x2400, 0x2440, 0x2460, 0x2500, 0x2580, 0x25a0,
|
||||
0x2600, 0x2700, 0x27c0, 0x27f0, 0x2800, 0x2900, 0x2980, 0x2a00, 0x2b00,
|
||||
0x2e80, 0x2f00, 0x2ff0, 0x3000, 0x3040, 0x30a0, 0x3100, 0x3130, 0x3190,
|
||||
0x31a0, 0x31f0, 0x3200, 0x3300, 0x3400, 0x4dc0, 0x4e00, 0xa000, 0xa490,
|
||||
0xac00, 0xd800, 0xdc00, 0xe000, 0xf900, 0xfb00, 0xfb50, 0xfe00, 0xfe20,
|
||||
0xfe30, 0xfe50, 0xfe70, 0xff00, 0xfff0, 0x10000, 0x10080, 0x10100,
|
||||
0x10300, 0x10330, 0x10380, 0x10400, 0x10450, 0x10480, 0x10800, 0x1d000,
|
||||
0x1d100, 0x1d300, 0x1d400, 0x20000, 0x2f800, 0xe0000, 0xe0100,
|
||||
];
|
||||
const value = codePoints
|
||||
.map(codePoint => String.fromCodePoint(codePoint))
|
||||
.join('');
|
||||
|
||||
expect(fnv1a128(value)).toBe('ca8f3b27d9685fa16c01b04ca12aba2d');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#computeHashForUserData', () => {
|
||||
it.each([
|
||||
[{ user: {} }, '1aad4cb46d5db425dcc232d086a8b6be'],
|
||||
[
|
||||
{
|
||||
identifier: '12345',
|
||||
user: {
|
||||
name: 'Pranav',
|
||||
email: 'pranav@example.com',
|
||||
avatar_url: 'https://images.chatwoot.com/placeholder',
|
||||
identifier_hash: '12345',
|
||||
},
|
||||
},
|
||||
'73b97d28ad386b211c8eed2143752b6e',
|
||||
],
|
||||
[
|
||||
{
|
||||
identifier: 42,
|
||||
user: {
|
||||
name: 'Jöhn 🌈',
|
||||
email: '🦄@example.com',
|
||||
},
|
||||
},
|
||||
'ca2a97a04c3bcafc20d9cdeac1e5b3f4',
|
||||
],
|
||||
])('hashes canonical user data synchronously', (userData, expectedHash) => {
|
||||
expect(computeHashForUserData(userData)).toBe(expectedHash);
|
||||
});
|
||||
|
||||
it('is independent of user property insertion order', () => {
|
||||
const firstHash = computeHashForUserData({
|
||||
identifier: '12345',
|
||||
user: { name: 'Pranav', email: 'pranav@example.com' },
|
||||
});
|
||||
const secondHash = computeHashForUserData({
|
||||
identifier: '12345',
|
||||
user: { email: 'pranav@example.com', name: 'Pranav' },
|
||||
});
|
||||
|
||||
expect(firstHash).toBe(secondHash);
|
||||
});
|
||||
|
||||
it('ignores user properties that are not part of the SDK identity', () => {
|
||||
const firstHash = computeHashForUserData({
|
||||
identifier: '12345',
|
||||
user: { name: 'Pranav' },
|
||||
});
|
||||
const secondHash = computeHashForUserData({
|
||||
identifier: '12345',
|
||||
user: { name: 'Pranav', role: 'administrator' },
|
||||
});
|
||||
|
||||
expect(firstHash).toBe(secondHash);
|
||||
});
|
||||
|
||||
it('changes when an identity property changes', () => {
|
||||
const firstHash = computeHashForUserData({
|
||||
identifier: '12345',
|
||||
user: { name: 'Pranav' },
|
||||
});
|
||||
const secondHash = computeHashForUserData({
|
||||
identifier: '67890',
|
||||
user: { name: 'Pranav' },
|
||||
});
|
||||
|
||||
expect(firstHash).not.toBe(secondHash);
|
||||
});
|
||||
|
||||
it('distinguishes identities that collide under FNV-1a 32-bit', () => {
|
||||
const firstHash = computeHashForUserData({
|
||||
identifier: 'id-149599',
|
||||
user: { name: 'User' },
|
||||
});
|
||||
const secondHash = computeHashForUserData({
|
||||
identifier: 'id-312382',
|
||||
user: { name: 'User' },
|
||||
});
|
||||
|
||||
expect(firstHash).toHaveLength(32);
|
||||
expect(secondHash).toHaveLength(32);
|
||||
expect(firstHash).not.toBe(secondHash);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#computeHashForUserData', () => {
|
||||
const identifier = 'user-123';
|
||||
const user = {
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
"dev": "overmind start -f ./Procfile.dev",
|
||||
"ruby:prettier": "bundle exec rubocop -a",
|
||||
"build:sdk": "vite build --config vite.lib.config.ts",
|
||||
"analyze:bundles": "pnpm analyze:bundle:sdk && pnpm analyze:bundle:widget",
|
||||
"analyze:bundle:sdk": "vite build --config vite.sdk.analyze.config.mts",
|
||||
"analyze:bundle:widget": "vite build --config vite.widget.analyze.config.mts",
|
||||
"prepare": "husky install",
|
||||
"size": "size-limit",
|
||||
"story:dev": "histoire dev",
|
||||
@@ -82,7 +85,6 @@
|
||||
"libphonenumber-js": "^1.11.9",
|
||||
"markdown-it": "^14.1.1",
|
||||
"markdown-it-link-attributes": "^4.0.1",
|
||||
"md5": "^2.3.0",
|
||||
"mitt": "^3.0.1",
|
||||
"opus-recorder": "^8.0.5",
|
||||
"pinia": "^3.0.4",
|
||||
@@ -141,6 +143,7 @@
|
||||
"postcss-preset-env": "^8.5.1",
|
||||
"prettier": "^3.3.3",
|
||||
"prosemirror-model": "^1.22.3",
|
||||
"rollup-plugin-visualizer": "7.0.1",
|
||||
"size-limit": "^8.2.4",
|
||||
"tailwindcss": "^3.4.19",
|
||||
"vite": "6.4.2",
|
||||
|
||||
223
pnpm-lock.yaml
generated
223
pnpm-lock.yaml
generated
@@ -168,9 +168,6 @@ importers:
|
||||
markdown-it-link-attributes:
|
||||
specifier: ^4.0.1
|
||||
version: 4.0.1
|
||||
md5:
|
||||
specifier: ^2.3.0
|
||||
version: 2.3.0
|
||||
mitt:
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1
|
||||
@@ -340,6 +337,9 @@ importers:
|
||||
prosemirror-model:
|
||||
specifier: ^1.22.3
|
||||
version: 1.22.3
|
||||
rollup-plugin-visualizer:
|
||||
specifier: 7.0.1
|
||||
version: 7.0.1(rollup@4.59.0)
|
||||
size-limit:
|
||||
specifier: ^8.2.4
|
||||
version: 8.2.6
|
||||
@@ -1677,6 +1677,10 @@ packages:
|
||||
resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
ansi-regex@6.2.2:
|
||||
resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
ansi-styles@4.3.0:
|
||||
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -1815,6 +1819,10 @@ packages:
|
||||
buffer-from@1.1.2:
|
||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||
|
||||
bundle-name@4.1.0:
|
||||
resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
bytes-iec@3.1.1:
|
||||
resolution: {integrity: sha512-fey6+4jDK7TFtFg/klGSvNKJctyU7n2aQdnM+CO0ruLPbqqMOM8Tio0Pc+deqUeVKX1tL5DQep1zQ7+37aTAsA==}
|
||||
engines: {node: '>= 0.8'}
|
||||
@@ -1878,9 +1886,9 @@ packages:
|
||||
change-case@4.1.2:
|
||||
resolution: {integrity: sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==}
|
||||
|
||||
charenc@0.0.2:
|
||||
resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==}
|
||||
|
||||
chart.js@4.4.4:
|
||||
resolution: {integrity: sha512-emICKGBABnxhMjUjlYRR12PmOXhJ2eJjEHL2/dZlWjxRAZT1D8xplLFq5M0tMQK8ja+wBS/tuVEJB5C6r7VxJA==}
|
||||
engines: {pnpm: '>=8'}
|
||||
check-error@2.1.1:
|
||||
resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
|
||||
engines: {node: '>= 16'}
|
||||
@@ -1920,6 +1928,10 @@ packages:
|
||||
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
cliui@9.0.1:
|
||||
resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
color-convert@2.0.1:
|
||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||
engines: {node: '>=7.0.0'}
|
||||
@@ -1999,9 +2011,6 @@ packages:
|
||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
crypt@0.0.2:
|
||||
resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==}
|
||||
|
||||
css-blank-pseudo@5.0.2:
|
||||
resolution: {integrity: sha512-aCU4AZ7uEcVSUzagTlA9pHciz7aWPKA/YzrEkpdSopJ2pvhIxiQ5sYeMz1/KByxlIo4XBdvMNJAVKMg/GRnhfw==}
|
||||
engines: {node: ^14 || ^16 || >=18}
|
||||
@@ -2148,10 +2157,22 @@ packages:
|
||||
deep-is@0.1.4:
|
||||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
|
||||
|
||||
default-browser-id@5.0.1:
|
||||
resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
default-browser@5.5.0:
|
||||
resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
define-data-property@1.1.4:
|
||||
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
define-lazy-prop@3.0.0:
|
||||
resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
define-properties@1.2.0:
|
||||
resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -2322,10 +2343,6 @@ packages:
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
escalade@3.1.2:
|
||||
resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
escalade@3.2.0:
|
||||
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -2642,6 +2659,10 @@ packages:
|
||||
resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
get-east-asian-width@1.6.0:
|
||||
resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
get-intrinsic@1.2.4:
|
||||
resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -2899,9 +2920,6 @@ packages:
|
||||
resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
is-buffer@1.1.6:
|
||||
resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==}
|
||||
|
||||
is-callable@1.2.7:
|
||||
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -2918,6 +2936,11 @@ packages:
|
||||
resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
is-docker@3.0.0:
|
||||
resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
hasBin: true
|
||||
|
||||
is-extendable@0.1.1:
|
||||
resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -2941,6 +2964,15 @@ packages:
|
||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
is-in-ssh@1.0.0:
|
||||
resolution: {integrity: sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
is-inside-container@1.0.0:
|
||||
resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==}
|
||||
engines: {node: '>=14.16'}
|
||||
hasBin: true
|
||||
|
||||
is-installed-globally@1.0.0:
|
||||
resolution: {integrity: sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -3021,6 +3053,10 @@ packages:
|
||||
resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
is-wsl@3.1.1:
|
||||
resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
isarray@2.0.5:
|
||||
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
|
||||
|
||||
@@ -3362,9 +3398,6 @@ packages:
|
||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
md5@2.3.0:
|
||||
resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==}
|
||||
|
||||
mdn-data@2.12.2:
|
||||
resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==}
|
||||
|
||||
@@ -3550,6 +3583,10 @@ packages:
|
||||
resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
open@11.0.0:
|
||||
resolution: {integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
optionator@0.9.3:
|
||||
resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
@@ -3929,6 +3966,10 @@ packages:
|
||||
resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
powershell-utils@0.1.0:
|
||||
resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
prelude-ls@1.2.1:
|
||||
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
@@ -4090,6 +4131,19 @@ packages:
|
||||
deprecated: Rimraf versions prior to v4 are no longer supported
|
||||
hasBin: true
|
||||
|
||||
rollup-plugin-visualizer@7.0.1:
|
||||
resolution: {integrity: sha512-UJUT4+1Ho4OcWmPYU3sYXgUqI8B8Ayfe06MX7y0qCJ1K8aGoKtR/NDd/2nZqM7ADkrzny+I99Ul7GgyoiVNAgg==}
|
||||
engines: {node: '>=22'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
rolldown: 1.x || ^1.0.0-beta || ^1.0.0-rc
|
||||
rollup: '>=4.59.0'
|
||||
peerDependenciesMeta:
|
||||
rolldown:
|
||||
optional: true
|
||||
rollup:
|
||||
optional: true
|
||||
|
||||
rollup@4.59.0:
|
||||
resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
@@ -4098,6 +4152,10 @@ packages:
|
||||
rope-sequence@1.3.2:
|
||||
resolution: {integrity: sha512-ku6MFrwEVSVmXLvy3dYph3LAMNS0890K7fabn+0YIRQ2T96T9F4gkFf0vf0WW0JUraNWwGRtInEpH7yO4tbQZg==}
|
||||
|
||||
run-applescript@7.1.0:
|
||||
resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
run-parallel@1.2.0:
|
||||
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
|
||||
|
||||
@@ -4248,6 +4306,10 @@ packages:
|
||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
source-map@0.7.6:
|
||||
resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==}
|
||||
engines: {node: '>= 12'}
|
||||
|
||||
spark-md5@3.0.2:
|
||||
resolution: {integrity: sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==}
|
||||
|
||||
@@ -4292,6 +4354,10 @@ packages:
|
||||
resolution: {integrity: sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
string-width@8.2.2:
|
||||
resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
string.prototype.trim@1.2.8:
|
||||
resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -4321,6 +4387,10 @@ packages:
|
||||
resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
strip-ansi@7.2.0:
|
||||
resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
strip-bom-string@1.0.0:
|
||||
resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -4929,6 +4999,10 @@ packages:
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
wsl-utils@0.3.1:
|
||||
resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
xml-name-validator@4.0.0:
|
||||
resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -4972,6 +5046,10 @@ packages:
|
||||
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
yargs-parser@22.0.0:
|
||||
resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==}
|
||||
engines: {node: ^20.19.0 || ^22.12.0 || >=23}
|
||||
|
||||
yargs@15.4.1:
|
||||
resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -4980,6 +5058,10 @@ packages:
|
||||
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
yargs@18.1.0:
|
||||
resolution: {integrity: sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==}
|
||||
engines: {node: ^20.19.0 || ^22.12.0 || >=23}
|
||||
|
||||
yocto-queue@0.1.0:
|
||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -6418,6 +6500,8 @@ snapshots:
|
||||
|
||||
ansi-regex@6.0.1: {}
|
||||
|
||||
ansi-regex@6.2.2: {}
|
||||
|
||||
ansi-styles@4.3.0:
|
||||
dependencies:
|
||||
color-convert: 2.0.1
|
||||
@@ -6591,6 +6675,10 @@ snapshots:
|
||||
buffer-from@1.1.2:
|
||||
optional: true
|
||||
|
||||
bundle-name@4.1.0:
|
||||
dependencies:
|
||||
run-applescript: 7.1.0
|
||||
|
||||
bytes-iec@3.1.1: {}
|
||||
|
||||
cac@6.7.14: {}
|
||||
@@ -6671,8 +6759,9 @@ snapshots:
|
||||
snake-case: 3.0.4
|
||||
tslib: 2.7.0
|
||||
|
||||
charenc@0.0.2: {}
|
||||
|
||||
chart.js@4.4.4:
|
||||
dependencies:
|
||||
'@kurkle/color': 0.3.2
|
||||
check-error@2.1.1: {}
|
||||
|
||||
chokidar@3.5.3:
|
||||
@@ -6729,6 +6818,12 @@ snapshots:
|
||||
strip-ansi: 6.0.1
|
||||
wrap-ansi: 7.0.0
|
||||
|
||||
cliui@9.0.1:
|
||||
dependencies:
|
||||
string-width: 7.2.0
|
||||
strip-ansi: 7.1.0
|
||||
wrap-ansi: 9.0.0
|
||||
|
||||
color-convert@2.0.1:
|
||||
dependencies:
|
||||
color-name: 1.1.4
|
||||
@@ -6802,8 +6897,6 @@ snapshots:
|
||||
shebang-command: 2.0.0
|
||||
which: 2.0.2
|
||||
|
||||
crypt@0.0.2: {}
|
||||
|
||||
css-blank-pseudo@5.0.2(postcss@8.5.23):
|
||||
dependencies:
|
||||
postcss: 8.5.23
|
||||
@@ -6920,12 +7013,21 @@ snapshots:
|
||||
|
||||
deep-is@0.1.4: {}
|
||||
|
||||
default-browser-id@5.0.1: {}
|
||||
|
||||
default-browser@5.5.0:
|
||||
dependencies:
|
||||
bundle-name: 4.1.0
|
||||
default-browser-id: 5.0.1
|
||||
|
||||
define-data-property@1.1.4:
|
||||
dependencies:
|
||||
es-define-property: 1.0.1
|
||||
es-errors: 1.3.0
|
||||
gopd: 1.2.0
|
||||
|
||||
define-lazy-prop@3.0.0: {}
|
||||
|
||||
define-properties@1.2.0:
|
||||
dependencies:
|
||||
has-property-descriptors: 1.0.0
|
||||
@@ -7192,8 +7294,6 @@ snapshots:
|
||||
'@esbuild/win32-ia32': 0.25.12
|
||||
'@esbuild/win32-x64': 0.25.12
|
||||
|
||||
escalade@3.1.2: {}
|
||||
|
||||
escalade@3.2.0: {}
|
||||
|
||||
escape-html@1.0.3: {}
|
||||
@@ -7544,6 +7644,8 @@ snapshots:
|
||||
|
||||
get-east-asian-width@1.4.0: {}
|
||||
|
||||
get-east-asian-width@1.6.0: {}
|
||||
|
||||
get-intrinsic@1.2.4:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
@@ -7880,8 +7982,6 @@ snapshots:
|
||||
call-bind: 1.0.7
|
||||
has-tostringtag: 1.0.2
|
||||
|
||||
is-buffer@1.1.6: {}
|
||||
|
||||
is-callable@1.2.7: {}
|
||||
|
||||
is-core-module@2.15.1:
|
||||
@@ -7896,6 +7996,8 @@ snapshots:
|
||||
dependencies:
|
||||
has-tostringtag: 1.0.2
|
||||
|
||||
is-docker@3.0.0: {}
|
||||
|
||||
is-extendable@0.1.1: {}
|
||||
|
||||
is-extglob@2.1.1: {}
|
||||
@@ -7912,6 +8014,12 @@ snapshots:
|
||||
dependencies:
|
||||
is-extglob: 2.1.1
|
||||
|
||||
is-in-ssh@1.0.0: {}
|
||||
|
||||
is-inside-container@1.0.0:
|
||||
dependencies:
|
||||
is-docker: 3.0.0
|
||||
|
||||
is-installed-globally@1.0.0:
|
||||
dependencies:
|
||||
global-directory: 4.0.1
|
||||
@@ -7978,6 +8086,10 @@ snapshots:
|
||||
|
||||
is-what@5.5.0: {}
|
||||
|
||||
is-wsl@3.1.1:
|
||||
dependencies:
|
||||
is-inside-container: 1.0.0
|
||||
|
||||
isarray@2.0.5: {}
|
||||
|
||||
isexe@2.0.0: {}
|
||||
@@ -8364,12 +8476,6 @@ snapshots:
|
||||
|
||||
math-intrinsics@1.1.0: {}
|
||||
|
||||
md5@2.3.0:
|
||||
dependencies:
|
||||
charenc: 0.0.2
|
||||
crypt: 0.0.2
|
||||
is-buffer: 1.1.6
|
||||
|
||||
mdn-data@2.12.2: {}
|
||||
|
||||
mdurl@1.0.1: {}
|
||||
@@ -8542,6 +8648,15 @@ snapshots:
|
||||
dependencies:
|
||||
mimic-function: 5.0.1
|
||||
|
||||
open@11.0.0:
|
||||
dependencies:
|
||||
default-browser: 5.5.0
|
||||
define-lazy-prop: 3.0.0
|
||||
is-in-ssh: 1.0.0
|
||||
is-inside-container: 1.0.0
|
||||
powershell-utils: 0.1.0
|
||||
wsl-utils: 0.3.1
|
||||
|
||||
optionator@0.9.3:
|
||||
dependencies:
|
||||
'@aashutoshrathi/word-wrap': 1.2.6
|
||||
@@ -8940,6 +9055,8 @@ snapshots:
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
powershell-utils@0.1.0: {}
|
||||
|
||||
prelude-ls@1.2.1: {}
|
||||
|
||||
prettier-linter-helpers@1.0.0:
|
||||
@@ -9116,6 +9233,15 @@ snapshots:
|
||||
dependencies:
|
||||
glob: 7.2.3
|
||||
|
||||
rollup-plugin-visualizer@7.0.1(rollup@4.59.0):
|
||||
dependencies:
|
||||
open: 11.0.0
|
||||
picomatch: 4.0.4
|
||||
source-map: 0.7.6
|
||||
yargs: 18.1.0
|
||||
optionalDependencies:
|
||||
rollup: 4.59.0
|
||||
|
||||
rollup@4.59.0:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.8
|
||||
@@ -9149,6 +9275,8 @@ snapshots:
|
||||
|
||||
rope-sequence@1.3.2: {}
|
||||
|
||||
run-applescript@7.1.0: {}
|
||||
|
||||
run-parallel@1.2.0:
|
||||
dependencies:
|
||||
queue-microtask: 1.2.3
|
||||
@@ -9322,6 +9450,8 @@ snapshots:
|
||||
source-map@0.6.1:
|
||||
optional: true
|
||||
|
||||
source-map@0.7.6: {}
|
||||
|
||||
spark-md5@3.0.2: {}
|
||||
|
||||
speakingurl@14.0.1: {}
|
||||
@@ -9361,6 +9491,11 @@ snapshots:
|
||||
get-east-asian-width: 1.4.0
|
||||
strip-ansi: 7.1.0
|
||||
|
||||
string-width@8.2.2:
|
||||
dependencies:
|
||||
get-east-asian-width: 1.6.0
|
||||
strip-ansi: 7.2.0
|
||||
|
||||
string.prototype.trim@1.2.8:
|
||||
dependencies:
|
||||
call-bind: 1.0.2
|
||||
@@ -9406,6 +9541,10 @@ snapshots:
|
||||
dependencies:
|
||||
ansi-regex: 6.0.1
|
||||
|
||||
strip-ansi@7.2.0:
|
||||
dependencies:
|
||||
ansi-regex: 6.2.2
|
||||
|
||||
strip-bom-string@1.0.0: {}
|
||||
|
||||
strip-bom@3.0.0: {}
|
||||
@@ -9677,7 +9816,7 @@ snapshots:
|
||||
update-browserslist-db@1.0.16(browserslist@4.23.0):
|
||||
dependencies:
|
||||
browserslist: 4.23.0
|
||||
escalade: 3.1.2
|
||||
escalade: 3.2.0
|
||||
picocolors: 1.1.1
|
||||
|
||||
update-browserslist-db@1.1.0(browserslist@4.23.3):
|
||||
@@ -10059,6 +10198,11 @@ snapshots:
|
||||
|
||||
ws@8.18.3: {}
|
||||
|
||||
wsl-utils@0.3.1:
|
||||
dependencies:
|
||||
is-wsl: 3.1.1
|
||||
powershell-utils: 0.1.0
|
||||
|
||||
xml-name-validator@4.0.0: {}
|
||||
|
||||
xml-name-validator@5.0.0: {}
|
||||
@@ -10088,6 +10232,8 @@ snapshots:
|
||||
|
||||
yargs-parser@21.1.1: {}
|
||||
|
||||
yargs-parser@22.0.0: {}
|
||||
|
||||
yargs@15.4.1:
|
||||
dependencies:
|
||||
cliui: 6.0.0
|
||||
@@ -10112,6 +10258,15 @@ snapshots:
|
||||
y18n: 5.0.8
|
||||
yargs-parser: 21.1.1
|
||||
|
||||
yargs@18.1.0:
|
||||
dependencies:
|
||||
cliui: 9.0.1
|
||||
escalade: 3.2.0
|
||||
get-caller-file: 2.0.5
|
||||
string-width: 8.2.2
|
||||
y18n: 5.0.8
|
||||
yargs-parser: 22.0.0
|
||||
|
||||
yocto-queue@0.1.0: {}
|
||||
|
||||
yocto-queue@1.1.1: {}
|
||||
|
||||
@@ -9,11 +9,42 @@
|
||||
*
|
||||
* The `assets:precompile` rake task runs this alongside the main app build.
|
||||
*/
|
||||
import { defineConfig } from 'vite';
|
||||
import { brotliCompressSync, constants, gzipSync } from 'node:zlib';
|
||||
import { defineConfig, type Plugin } from 'vite';
|
||||
import path from 'path';
|
||||
import { aliases } from './vite.shared';
|
||||
|
||||
const compressedSdkPlugin = {
|
||||
name: 'compress-sdk',
|
||||
generateBundle(_options, bundle) {
|
||||
const sdkBundle = bundle['js/sdk.js'];
|
||||
|
||||
if (sdkBundle?.type !== 'chunk') {
|
||||
this.error('SDK bundle was not generated');
|
||||
}
|
||||
|
||||
this.emitFile({
|
||||
type: 'asset',
|
||||
fileName: 'js/sdk.js.br',
|
||||
source: brotliCompressSync(sdkBundle.code, {
|
||||
params: {
|
||||
[constants.BROTLI_PARAM_QUALITY]: constants.BROTLI_MAX_QUALITY,
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
this.emitFile({
|
||||
type: 'asset',
|
||||
fileName: 'js/sdk.js.gz',
|
||||
source: gzipSync(sdkBundle.code, {
|
||||
level: constants.Z_BEST_COMPRESSION,
|
||||
}),
|
||||
});
|
||||
},
|
||||
} satisfies Plugin;
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [compressedSdkPlugin],
|
||||
build: {
|
||||
rollupOptions: {
|
||||
output: {
|
||||
|
||||
34
vite.sdk.analyze.config.mts
Normal file
34
vite.sdk.analyze.config.mts
Normal file
@@ -0,0 +1,34 @@
|
||||
import path from 'path';
|
||||
import { defineConfig, type PluginOption } from 'vite';
|
||||
import { visualizer } from 'rollup-plugin-visualizer';
|
||||
import { aliases } from './vite.shared';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
visualizer({
|
||||
filename: path.resolve(__dirname, 'tmp/bundle-analysis/sdk.html'),
|
||||
title: 'Chatwoot SDK bundle analysis',
|
||||
template: 'treemap',
|
||||
gzipSize: true,
|
||||
brotliSize: true,
|
||||
projectRoot: __dirname,
|
||||
}) as PluginOption,
|
||||
],
|
||||
build: {
|
||||
outDir: 'tmp/bundle-analysis/sdk',
|
||||
emptyOutDir: true,
|
||||
copyPublicDir: false,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
entryFileNames: 'sdk.js',
|
||||
inlineDynamicImports: true,
|
||||
},
|
||||
},
|
||||
lib: {
|
||||
entry: path.resolve(__dirname, './app/javascript/entrypoints/sdk.js'),
|
||||
formats: ['iife'],
|
||||
name: 'sdk',
|
||||
},
|
||||
},
|
||||
resolve: { alias: aliases },
|
||||
});
|
||||
38
vite.widget.analyze.config.mts
Normal file
38
vite.widget.analyze.config.mts
Normal file
@@ -0,0 +1,38 @@
|
||||
import path from 'path';
|
||||
import yaml from '@rollup/plugin-yaml';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import { defineConfig, type PluginOption } from 'vite';
|
||||
import { visualizer } from 'rollup-plugin-visualizer';
|
||||
import { aliases, vueOptions } from './vite.shared';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(vueOptions),
|
||||
yaml(),
|
||||
visualizer({
|
||||
filename: path.resolve(__dirname, 'tmp/bundle-analysis/widget.html'),
|
||||
title: 'Chatwoot widget bundle analysis',
|
||||
template: 'treemap',
|
||||
gzipSize: true,
|
||||
brotliSize: true,
|
||||
projectRoot: __dirname,
|
||||
}) as PluginOption,
|
||||
],
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
api: 'modern-compiler',
|
||||
},
|
||||
},
|
||||
},
|
||||
publicDir: false,
|
||||
build: {
|
||||
outDir: 'tmp/bundle-analysis/widget',
|
||||
emptyOutDir: true,
|
||||
copyPublicDir: false,
|
||||
rollupOptions: {
|
||||
input: path.resolve(__dirname, './app/javascript/entrypoints/widget.js'),
|
||||
},
|
||||
},
|
||||
resolve: { alias: aliases },
|
||||
});
|
||||
Reference in New Issue
Block a user