@amboras-dev/tidio 0.2.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.
- package/dist/index.d.mts +31 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +100 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +64 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +38 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
|
|
3
|
+
interface TidioWidgetProps {
|
|
4
|
+
/**
|
|
5
|
+
* The merchant's Tidio public key (from Tidio → Settings → Channels →
|
|
6
|
+
* Live Chat → Installation). When null/undefined or `enabled` is false,
|
|
7
|
+
* the widget renders nothing.
|
|
8
|
+
*/
|
|
9
|
+
publicKey?: string | null;
|
|
10
|
+
/** Master on/off from the merchant's plugin config. */
|
|
11
|
+
enabled?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Null-rendering side-effect component for the `bodyEnd` plugin slot. Injects
|
|
16
|
+
* the merchant's Tidio chat widget when enabled + a public key is available
|
|
17
|
+
* AND the shopper has granted consent; renders nothing otherwise. Never throws.
|
|
18
|
+
*
|
|
19
|
+
* Config resolution (in order):
|
|
20
|
+
* 1. Props passed by the host layout via the manifest's `propsFromConfig`
|
|
21
|
+
* (fast path — used when the storefront already fetched plugin configs).
|
|
22
|
+
* 2. Self-fetch from the Medusa Store API `/store/integrations/active` when
|
|
23
|
+
* no `publicKey` prop is supplied. This keeps the widget working even when
|
|
24
|
+
* the host layout only fetches configs for head/rootProviders slots (a
|
|
25
|
+
* `bodyEnd`-only install otherwise receives empty props). Reads the
|
|
26
|
+
* storefront's standard NEXT_PUBLIC_MEDUSA_* env vars — same source the
|
|
27
|
+
* Klaviyo server helper uses.
|
|
28
|
+
*/
|
|
29
|
+
declare function TidioWidget({ publicKey: publicKeyProp, enabled: enabledProp, }: TidioWidgetProps): ReactElement | null;
|
|
30
|
+
|
|
31
|
+
export { TidioWidget, type TidioWidgetProps };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
|
|
3
|
+
interface TidioWidgetProps {
|
|
4
|
+
/**
|
|
5
|
+
* The merchant's Tidio public key (from Tidio → Settings → Channels →
|
|
6
|
+
* Live Chat → Installation). When null/undefined or `enabled` is false,
|
|
7
|
+
* the widget renders nothing.
|
|
8
|
+
*/
|
|
9
|
+
publicKey?: string | null;
|
|
10
|
+
/** Master on/off from the merchant's plugin config. */
|
|
11
|
+
enabled?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Null-rendering side-effect component for the `bodyEnd` plugin slot. Injects
|
|
16
|
+
* the merchant's Tidio chat widget when enabled + a public key is available
|
|
17
|
+
* AND the shopper has granted consent; renders nothing otherwise. Never throws.
|
|
18
|
+
*
|
|
19
|
+
* Config resolution (in order):
|
|
20
|
+
* 1. Props passed by the host layout via the manifest's `propsFromConfig`
|
|
21
|
+
* (fast path — used when the storefront already fetched plugin configs).
|
|
22
|
+
* 2. Self-fetch from the Medusa Store API `/store/integrations/active` when
|
|
23
|
+
* no `publicKey` prop is supplied. This keeps the widget working even when
|
|
24
|
+
* the host layout only fetches configs for head/rootProviders slots (a
|
|
25
|
+
* `bodyEnd`-only install otherwise receives empty props). Reads the
|
|
26
|
+
* storefront's standard NEXT_PUBLIC_MEDUSA_* env vars — same source the
|
|
27
|
+
* Klaviyo server helper uses.
|
|
28
|
+
*/
|
|
29
|
+
declare function TidioWidget({ publicKey: publicKeyProp, enabled: enabledProp, }: TidioWidgetProps): ReactElement | null;
|
|
30
|
+
|
|
31
|
+
export { TidioWidget, type TidioWidgetProps };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
|
|
31
|
+
// src/index.ts
|
|
32
|
+
var src_exports = {};
|
|
33
|
+
__export(src_exports, {
|
|
34
|
+
TidioWidget: () => TidioWidget
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(src_exports);
|
|
37
|
+
|
|
38
|
+
// src/provider.tsx
|
|
39
|
+
var import_script = __toESM(require("next/script"));
|
|
40
|
+
var import_react = require("react");
|
|
41
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
42
|
+
function hasAnalyticsConsent() {
|
|
43
|
+
if (typeof document === "undefined") return false;
|
|
44
|
+
const match = document.cookie.match(/(?:^|;\s*)amboras_consent=([^;]*)/);
|
|
45
|
+
if (!match) return true;
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(decodeURIComponent(match[1]))?.analytics === true;
|
|
48
|
+
} catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function TidioWidget({
|
|
53
|
+
publicKey: publicKeyProp,
|
|
54
|
+
enabled: enabledProp
|
|
55
|
+
}) {
|
|
56
|
+
const propProvided = publicKeyProp != null;
|
|
57
|
+
const [fetched, setFetched] = (0, import_react.useState)(
|
|
58
|
+
null
|
|
59
|
+
);
|
|
60
|
+
const [consented, setConsented] = (0, import_react.useState)(false);
|
|
61
|
+
(0, import_react.useEffect)(() => {
|
|
62
|
+
setConsented(hasAnalyticsConsent());
|
|
63
|
+
const handler = () => setConsented(hasAnalyticsConsent());
|
|
64
|
+
window.addEventListener("amboras-consent-changed", handler);
|
|
65
|
+
return () => window.removeEventListener("amboras-consent-changed", handler);
|
|
66
|
+
}, []);
|
|
67
|
+
(0, import_react.useEffect)(() => {
|
|
68
|
+
if (propProvided) return;
|
|
69
|
+
const baseUrl = process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL;
|
|
70
|
+
const publishableKey = process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY;
|
|
71
|
+
if (!baseUrl || !publishableKey) return;
|
|
72
|
+
let cancelled = false;
|
|
73
|
+
fetch(`${baseUrl}/store/integrations/active`, {
|
|
74
|
+
headers: { "x-publishable-api-key": publishableKey }
|
|
75
|
+
}).then((res) => res.ok ? res.json() : null).then((data) => {
|
|
76
|
+
if (cancelled || !data || typeof data !== "object") return;
|
|
77
|
+
const tidio = data.tidio;
|
|
78
|
+
if (!tidio) {
|
|
79
|
+
setFetched({ publicKey: null, enabled: false });
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const publicKey2 = typeof tidio.publicKey === "string" ? tidio.publicKey : null;
|
|
83
|
+
setFetched({ publicKey: publicKey2, enabled: tidio.enabled !== false });
|
|
84
|
+
}).catch(() => {
|
|
85
|
+
});
|
|
86
|
+
return () => {
|
|
87
|
+
cancelled = true;
|
|
88
|
+
};
|
|
89
|
+
}, [propProvided]);
|
|
90
|
+
const publicKey = propProvided ? publicKeyProp : fetched?.publicKey;
|
|
91
|
+
const enabled = propProvided ? enabledProp !== false : fetched?.enabled === true;
|
|
92
|
+
if (!enabled || !publicKey || !consented) return null;
|
|
93
|
+
const src = `https://code.tidio.co/${encodeURIComponent(publicKey)}.js`;
|
|
94
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_script.default, { id: "tidio-chat", src, strategy: "afterInteractive" });
|
|
95
|
+
}
|
|
96
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
97
|
+
0 && (module.exports = {
|
|
98
|
+
TidioWidget
|
|
99
|
+
});
|
|
100
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/provider.tsx"],"sourcesContent":["export { TidioWidget } from './provider'\nexport type { TidioWidgetProps } from './types'\n","'use client'\n\nimport Script from 'next/script'\nimport { useEffect, useState, type ReactElement } from 'react'\nimport type { TidioWidgetProps } from './types'\n\n/**\n * Reads the storefront's standard `amboras_consent` cookie — the same gate\n * meta-ads and GA4 use. Tidio + Lyro set tracking cookies and profile\n * visitors, so the chat script must not load until the shopper has accepted.\n * Falls back to `true` only when the cookie is absent entirely (storefronts\n * that never wired a consent banner).\n */\nfunction hasAnalyticsConsent(): boolean {\n if (typeof document === 'undefined') return false\n const match = document.cookie.match(/(?:^|;\\s*)amboras_consent=([^;]*)/)\n if (!match) return true\n try {\n return JSON.parse(decodeURIComponent(match[1]))?.analytics === true\n } catch {\n return false\n }\n}\n\n/**\n * Null-rendering side-effect component for the `bodyEnd` plugin slot. Injects\n * the merchant's Tidio chat widget when enabled + a public key is available\n * AND the shopper has granted consent; renders nothing otherwise. Never throws.\n *\n * Config resolution (in order):\n * 1. Props passed by the host layout via the manifest's `propsFromConfig`\n * (fast path — used when the storefront already fetched plugin configs).\n * 2. Self-fetch from the Medusa Store API `/store/integrations/active` when\n * no `publicKey` prop is supplied. This keeps the widget working even when\n * the host layout only fetches configs for head/rootProviders slots (a\n * `bodyEnd`-only install otherwise receives empty props). Reads the\n * storefront's standard NEXT_PUBLIC_MEDUSA_* env vars — same source the\n * Klaviyo server helper uses.\n */\nexport function TidioWidget({\n publicKey: publicKeyProp,\n enabled: enabledProp,\n}: TidioWidgetProps): ReactElement | null {\n const propProvided = publicKeyProp != null\n const [fetched, setFetched] = useState<{ publicKey: string | null; enabled: boolean } | null>(\n null,\n )\n const [consented, setConsented] = useState(false)\n\n // Re-check consent whenever the banner fires its change event, so the chat\n // loads the moment the shopper accepts — no page reload required.\n useEffect(() => {\n setConsented(hasAnalyticsConsent())\n const handler = () => setConsented(hasAnalyticsConsent())\n window.addEventListener('amboras-consent-changed', handler)\n return () => window.removeEventListener('amboras-consent-changed', handler)\n }, [])\n\n useEffect(() => {\n // Props win — only self-fetch when the host layout didn't supply config.\n if (propProvided) return\n\n const baseUrl = process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL\n const publishableKey = process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY\n if (!baseUrl || !publishableKey) return\n\n let cancelled = false\n fetch(`${baseUrl}/store/integrations/active`, {\n headers: { 'x-publishable-api-key': publishableKey },\n })\n .then((res) => (res.ok ? res.json() : null))\n .then((data) => {\n if (cancelled || !data || typeof data !== 'object') return\n const tidio = (data as Record<string, { publicKey?: unknown; enabled?: unknown }>).tidio\n if (!tidio) {\n setFetched({ publicKey: null, enabled: false })\n return\n }\n // Guard the shape — a non-string publicKey must never reach the <Script> src.\n const publicKey = typeof tidio.publicKey === 'string' ? tidio.publicKey : null\n setFetched({ publicKey, enabled: tidio.enabled !== false })\n })\n .catch(() => {\n // Network/API failure must never break the storefront — render nothing.\n })\n\n return () => {\n cancelled = true\n }\n }, [propProvided])\n\n const publicKey = propProvided ? publicKeyProp : fetched?.publicKey\n const enabled = propProvided ? enabledProp !== false : fetched?.enabled === true\n\n if (!enabled || !publicKey || !consented) return null\n\n const src = `https://code.tidio.co/${encodeURIComponent(publicKey)}.js`\n return <Script id=\"tidio-chat\" src={src} strategy=\"afterInteractive\" />\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEA,oBAAmB;AACnB,mBAAuD;AA8F9C;AApFT,SAAS,sBAA+B;AACtC,MAAI,OAAO,aAAa,YAAa,QAAO;AAC5C,QAAM,QAAQ,SAAS,OAAO,MAAM,mCAAmC;AACvE,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI;AACF,WAAO,KAAK,MAAM,mBAAmB,MAAM,CAAC,CAAC,CAAC,GAAG,cAAc;AAAA,EACjE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAiBO,SAAS,YAAY;AAAA,EAC1B,WAAW;AAAA,EACX,SAAS;AACX,GAA0C;AACxC,QAAM,eAAe,iBAAiB;AACtC,QAAM,CAAC,SAAS,UAAU,QAAI;AAAA,IAC5B;AAAA,EACF;AACA,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAS,KAAK;AAIhD,8BAAU,MAAM;AACd,iBAAa,oBAAoB,CAAC;AAClC,UAAM,UAAU,MAAM,aAAa,oBAAoB,CAAC;AACxD,WAAO,iBAAiB,2BAA2B,OAAO;AAC1D,WAAO,MAAM,OAAO,oBAAoB,2BAA2B,OAAO;AAAA,EAC5E,GAAG,CAAC,CAAC;AAEL,8BAAU,MAAM;AAEd,QAAI,aAAc;AAElB,UAAM,UAAU,QAAQ,IAAI;AAC5B,UAAM,iBAAiB,QAAQ,IAAI;AACnC,QAAI,CAAC,WAAW,CAAC,eAAgB;AAEjC,QAAI,YAAY;AAChB,UAAM,GAAG,OAAO,8BAA8B;AAAA,MAC5C,SAAS,EAAE,yBAAyB,eAAe;AAAA,IACrD,CAAC,EACE,KAAK,CAAC,QAAS,IAAI,KAAK,IAAI,KAAK,IAAI,IAAK,EAC1C,KAAK,CAAC,SAAS;AACd,UAAI,aAAa,CAAC,QAAQ,OAAO,SAAS,SAAU;AACpD,YAAM,QAAS,KAAoE;AACnF,UAAI,CAAC,OAAO;AACV,mBAAW,EAAE,WAAW,MAAM,SAAS,MAAM,CAAC;AAC9C;AAAA,MACF;AAEA,YAAMA,aAAY,OAAO,MAAM,cAAc,WAAW,MAAM,YAAY;AAC1E,iBAAW,EAAE,WAAAA,YAAW,SAAS,MAAM,YAAY,MAAM,CAAC;AAAA,IAC5D,CAAC,EACA,MAAM,MAAM;AAAA,IAEb,CAAC;AAEH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,YAAY,CAAC;AAEjB,QAAM,YAAY,eAAe,gBAAgB,SAAS;AAC1D,QAAM,UAAU,eAAe,gBAAgB,QAAQ,SAAS,YAAY;AAE5E,MAAI,CAAC,WAAW,CAAC,aAAa,CAAC,UAAW,QAAO;AAEjD,QAAM,MAAM,yBAAyB,mBAAmB,SAAS,CAAC;AAClE,SAAO,4CAAC,cAAAC,SAAA,EAAO,IAAG,cAAa,KAAU,UAAS,oBAAmB;AACvE;","names":["publicKey","Script"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/provider.tsx
|
|
4
|
+
import Script from "next/script";
|
|
5
|
+
import { useEffect, useState } from "react";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
function hasAnalyticsConsent() {
|
|
8
|
+
if (typeof document === "undefined") return false;
|
|
9
|
+
const match = document.cookie.match(/(?:^|;\s*)amboras_consent=([^;]*)/);
|
|
10
|
+
if (!match) return true;
|
|
11
|
+
try {
|
|
12
|
+
return JSON.parse(decodeURIComponent(match[1]))?.analytics === true;
|
|
13
|
+
} catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function TidioWidget({
|
|
18
|
+
publicKey: publicKeyProp,
|
|
19
|
+
enabled: enabledProp
|
|
20
|
+
}) {
|
|
21
|
+
const propProvided = publicKeyProp != null;
|
|
22
|
+
const [fetched, setFetched] = useState(
|
|
23
|
+
null
|
|
24
|
+
);
|
|
25
|
+
const [consented, setConsented] = useState(false);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
setConsented(hasAnalyticsConsent());
|
|
28
|
+
const handler = () => setConsented(hasAnalyticsConsent());
|
|
29
|
+
window.addEventListener("amboras-consent-changed", handler);
|
|
30
|
+
return () => window.removeEventListener("amboras-consent-changed", handler);
|
|
31
|
+
}, []);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (propProvided) return;
|
|
34
|
+
const baseUrl = process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL;
|
|
35
|
+
const publishableKey = process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY;
|
|
36
|
+
if (!baseUrl || !publishableKey) return;
|
|
37
|
+
let cancelled = false;
|
|
38
|
+
fetch(`${baseUrl}/store/integrations/active`, {
|
|
39
|
+
headers: { "x-publishable-api-key": publishableKey }
|
|
40
|
+
}).then((res) => res.ok ? res.json() : null).then((data) => {
|
|
41
|
+
if (cancelled || !data || typeof data !== "object") return;
|
|
42
|
+
const tidio = data.tidio;
|
|
43
|
+
if (!tidio) {
|
|
44
|
+
setFetched({ publicKey: null, enabled: false });
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const publicKey2 = typeof tidio.publicKey === "string" ? tidio.publicKey : null;
|
|
48
|
+
setFetched({ publicKey: publicKey2, enabled: tidio.enabled !== false });
|
|
49
|
+
}).catch(() => {
|
|
50
|
+
});
|
|
51
|
+
return () => {
|
|
52
|
+
cancelled = true;
|
|
53
|
+
};
|
|
54
|
+
}, [propProvided]);
|
|
55
|
+
const publicKey = propProvided ? publicKeyProp : fetched?.publicKey;
|
|
56
|
+
const enabled = propProvided ? enabledProp !== false : fetched?.enabled === true;
|
|
57
|
+
if (!enabled || !publicKey || !consented) return null;
|
|
58
|
+
const src = `https://code.tidio.co/${encodeURIComponent(publicKey)}.js`;
|
|
59
|
+
return /* @__PURE__ */ jsx(Script, { id: "tidio-chat", src, strategy: "afterInteractive" });
|
|
60
|
+
}
|
|
61
|
+
export {
|
|
62
|
+
TidioWidget
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/provider.tsx"],"sourcesContent":["'use client'\n\nimport Script from 'next/script'\nimport { useEffect, useState, type ReactElement } from 'react'\nimport type { TidioWidgetProps } from './types'\n\n/**\n * Reads the storefront's standard `amboras_consent` cookie — the same gate\n * meta-ads and GA4 use. Tidio + Lyro set tracking cookies and profile\n * visitors, so the chat script must not load until the shopper has accepted.\n * Falls back to `true` only when the cookie is absent entirely (storefronts\n * that never wired a consent banner).\n */\nfunction hasAnalyticsConsent(): boolean {\n if (typeof document === 'undefined') return false\n const match = document.cookie.match(/(?:^|;\\s*)amboras_consent=([^;]*)/)\n if (!match) return true\n try {\n return JSON.parse(decodeURIComponent(match[1]))?.analytics === true\n } catch {\n return false\n }\n}\n\n/**\n * Null-rendering side-effect component for the `bodyEnd` plugin slot. Injects\n * the merchant's Tidio chat widget when enabled + a public key is available\n * AND the shopper has granted consent; renders nothing otherwise. Never throws.\n *\n * Config resolution (in order):\n * 1. Props passed by the host layout via the manifest's `propsFromConfig`\n * (fast path — used when the storefront already fetched plugin configs).\n * 2. Self-fetch from the Medusa Store API `/store/integrations/active` when\n * no `publicKey` prop is supplied. This keeps the widget working even when\n * the host layout only fetches configs for head/rootProviders slots (a\n * `bodyEnd`-only install otherwise receives empty props). Reads the\n * storefront's standard NEXT_PUBLIC_MEDUSA_* env vars — same source the\n * Klaviyo server helper uses.\n */\nexport function TidioWidget({\n publicKey: publicKeyProp,\n enabled: enabledProp,\n}: TidioWidgetProps): ReactElement | null {\n const propProvided = publicKeyProp != null\n const [fetched, setFetched] = useState<{ publicKey: string | null; enabled: boolean } | null>(\n null,\n )\n const [consented, setConsented] = useState(false)\n\n // Re-check consent whenever the banner fires its change event, so the chat\n // loads the moment the shopper accepts — no page reload required.\n useEffect(() => {\n setConsented(hasAnalyticsConsent())\n const handler = () => setConsented(hasAnalyticsConsent())\n window.addEventListener('amboras-consent-changed', handler)\n return () => window.removeEventListener('amboras-consent-changed', handler)\n }, [])\n\n useEffect(() => {\n // Props win — only self-fetch when the host layout didn't supply config.\n if (propProvided) return\n\n const baseUrl = process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL\n const publishableKey = process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY\n if (!baseUrl || !publishableKey) return\n\n let cancelled = false\n fetch(`${baseUrl}/store/integrations/active`, {\n headers: { 'x-publishable-api-key': publishableKey },\n })\n .then((res) => (res.ok ? res.json() : null))\n .then((data) => {\n if (cancelled || !data || typeof data !== 'object') return\n const tidio = (data as Record<string, { publicKey?: unknown; enabled?: unknown }>).tidio\n if (!tidio) {\n setFetched({ publicKey: null, enabled: false })\n return\n }\n // Guard the shape — a non-string publicKey must never reach the <Script> src.\n const publicKey = typeof tidio.publicKey === 'string' ? tidio.publicKey : null\n setFetched({ publicKey, enabled: tidio.enabled !== false })\n })\n .catch(() => {\n // Network/API failure must never break the storefront — render nothing.\n })\n\n return () => {\n cancelled = true\n }\n }, [propProvided])\n\n const publicKey = propProvided ? publicKeyProp : fetched?.publicKey\n const enabled = propProvided ? enabledProp !== false : fetched?.enabled === true\n\n if (!enabled || !publicKey || !consented) return null\n\n const src = `https://code.tidio.co/${encodeURIComponent(publicKey)}.js`\n return <Script id=\"tidio-chat\" src={src} strategy=\"afterInteractive\" />\n}\n"],"mappings":";;;AAEA,OAAO,YAAY;AACnB,SAAS,WAAW,gBAAmC;AA8F9C;AApFT,SAAS,sBAA+B;AACtC,MAAI,OAAO,aAAa,YAAa,QAAO;AAC5C,QAAM,QAAQ,SAAS,OAAO,MAAM,mCAAmC;AACvE,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI;AACF,WAAO,KAAK,MAAM,mBAAmB,MAAM,CAAC,CAAC,CAAC,GAAG,cAAc;AAAA,EACjE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAiBO,SAAS,YAAY;AAAA,EAC1B,WAAW;AAAA,EACX,SAAS;AACX,GAA0C;AACxC,QAAM,eAAe,iBAAiB;AACtC,QAAM,CAAC,SAAS,UAAU,IAAI;AAAA,IAC5B;AAAA,EACF;AACA,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAIhD,YAAU,MAAM;AACd,iBAAa,oBAAoB,CAAC;AAClC,UAAM,UAAU,MAAM,aAAa,oBAAoB,CAAC;AACxD,WAAO,iBAAiB,2BAA2B,OAAO;AAC1D,WAAO,MAAM,OAAO,oBAAoB,2BAA2B,OAAO;AAAA,EAC5E,GAAG,CAAC,CAAC;AAEL,YAAU,MAAM;AAEd,QAAI,aAAc;AAElB,UAAM,UAAU,QAAQ,IAAI;AAC5B,UAAM,iBAAiB,QAAQ,IAAI;AACnC,QAAI,CAAC,WAAW,CAAC,eAAgB;AAEjC,QAAI,YAAY;AAChB,UAAM,GAAG,OAAO,8BAA8B;AAAA,MAC5C,SAAS,EAAE,yBAAyB,eAAe;AAAA,IACrD,CAAC,EACE,KAAK,CAAC,QAAS,IAAI,KAAK,IAAI,KAAK,IAAI,IAAK,EAC1C,KAAK,CAAC,SAAS;AACd,UAAI,aAAa,CAAC,QAAQ,OAAO,SAAS,SAAU;AACpD,YAAM,QAAS,KAAoE;AACnF,UAAI,CAAC,OAAO;AACV,mBAAW,EAAE,WAAW,MAAM,SAAS,MAAM,CAAC;AAC9C;AAAA,MACF;AAEA,YAAMA,aAAY,OAAO,MAAM,cAAc,WAAW,MAAM,YAAY;AAC1E,iBAAW,EAAE,WAAAA,YAAW,SAAS,MAAM,YAAY,MAAM,CAAC;AAAA,IAC5D,CAAC,EACA,MAAM,MAAM;AAAA,IAEb,CAAC;AAEH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,YAAY,CAAC;AAEjB,QAAM,YAAY,eAAe,gBAAgB,SAAS;AAC1D,QAAM,UAAU,eAAe,gBAAgB,QAAQ,SAAS,YAAY;AAE5E,MAAI,CAAC,WAAW,CAAC,aAAa,CAAC,UAAW,QAAO;AAEjD,QAAM,MAAM,yBAAyB,mBAAmB,SAAS,CAAC;AAClE,SAAO,oBAAC,UAAO,IAAG,cAAa,KAAU,UAAS,oBAAmB;AACvE;","names":["publicKey"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@amboras-dev/tidio",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Tidio AI chatbot storefront integration for Amboras — widget embed component",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"react": ">=18",
|
|
21
|
+
"next": ">=14"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"tsup": "^8.0.0",
|
|
25
|
+
"typescript": "^5.0.0",
|
|
26
|
+
"next": "^15.0.0",
|
|
27
|
+
"react": "^19.0.0",
|
|
28
|
+
"@types/react": "^19.0.0",
|
|
29
|
+
"@types/node": "^20.0.0"
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsup",
|
|
36
|
+
"typecheck": "tsc --noEmit"
|
|
37
|
+
}
|
|
38
|
+
}
|