@humanfirst-chat/js 0.1.2 → 0.1.5
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 +30 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +79 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +79 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,6 +37,36 @@ HFChat.identify({
|
|
|
37
37
|
});
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
**When to call `identify()`:**
|
|
41
|
+
|
|
42
|
+
- Can be called **at any time** — before or after the widget loads
|
|
43
|
+
- Call it when the user logs in, or whenever user data becomes available
|
|
44
|
+
- Can be called multiple times; new data is merged with existing metadata
|
|
45
|
+
- The visitor is immediately updated in the agent dashboard
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
// Example: Identify after login
|
|
49
|
+
import { useEffect } from "react";
|
|
50
|
+
import { useUser } from "./auth"; // your auth hook
|
|
51
|
+
import HFChat from "@humanfirst-chat/js";
|
|
52
|
+
|
|
53
|
+
export function ChatIdentifier() {
|
|
54
|
+
const user = useUser();
|
|
55
|
+
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
if (!user?.email) return;
|
|
58
|
+
|
|
59
|
+
HFChat.identify({
|
|
60
|
+
email: user.email,
|
|
61
|
+
name: user.name,
|
|
62
|
+
plan: user.plan,
|
|
63
|
+
});
|
|
64
|
+
}, [user]);
|
|
65
|
+
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
40
70
|
## Control visibility
|
|
41
71
|
|
|
42
72
|
```ts
|
package/dist/index.d.mts
CHANGED
|
@@ -9,6 +9,8 @@ interface HFChatConfig {
|
|
|
9
9
|
siteId?: string;
|
|
10
10
|
/** Start with widget hidden */
|
|
11
11
|
hidden?: boolean;
|
|
12
|
+
/** Hide the launcher after the chat window is closed */
|
|
13
|
+
hideOnClose?: boolean;
|
|
12
14
|
/** Log verbosity level */
|
|
13
15
|
logs?: HFChatLogLevel;
|
|
14
16
|
/** Override API base URL (for self-hosting/testing) */
|
|
@@ -20,6 +22,8 @@ interface HFChatWidgetSettings {
|
|
|
20
22
|
widgetColor?: string;
|
|
21
23
|
widgetPosition?: HFChatPosition;
|
|
22
24
|
widgetSize?: HFChatSize;
|
|
25
|
+
/** Hide the launcher after the chat window is closed */
|
|
26
|
+
hideOnClose?: boolean;
|
|
23
27
|
}
|
|
24
28
|
interface HFChatOpenOptions {
|
|
25
29
|
focus?: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ interface HFChatConfig {
|
|
|
9
9
|
siteId?: string;
|
|
10
10
|
/** Start with widget hidden */
|
|
11
11
|
hidden?: boolean;
|
|
12
|
+
/** Hide the launcher after the chat window is closed */
|
|
13
|
+
hideOnClose?: boolean;
|
|
12
14
|
/** Log verbosity level */
|
|
13
15
|
logs?: HFChatLogLevel;
|
|
14
16
|
/** Override API base URL (for self-hosting/testing) */
|
|
@@ -20,6 +22,8 @@ interface HFChatWidgetSettings {
|
|
|
20
22
|
widgetColor?: string;
|
|
21
23
|
widgetPosition?: HFChatPosition;
|
|
22
24
|
widgetSize?: HFChatSize;
|
|
25
|
+
/** Hide the launcher after the chat window is closed */
|
|
26
|
+
hideOnClose?: boolean;
|
|
23
27
|
}
|
|
24
28
|
interface HFChatOpenOptions {
|
|
25
29
|
focus?: boolean;
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,19 @@ __export(index_exports, {
|
|
|
26
26
|
module.exports = __toCommonJS(index_exports);
|
|
27
27
|
var DEFAULT_SCRIPT_SRC = "https://humanfirst.chat/widget.js";
|
|
28
28
|
var SCRIPT_DATA_ATTR = "data-hfchat-widget";
|
|
29
|
+
var logLevel = "none";
|
|
30
|
+
function log(message, ...args) {
|
|
31
|
+
if (logLevel === "none") return;
|
|
32
|
+
console.log(`[HFChat SDK] ${message}`, ...args);
|
|
33
|
+
}
|
|
34
|
+
function logFull(message, ...args) {
|
|
35
|
+
if (logLevel !== "full") return;
|
|
36
|
+
console.log(`[HFChat SDK] ${message}`, ...args);
|
|
37
|
+
}
|
|
38
|
+
function logError(message, ...args) {
|
|
39
|
+
if (logLevel === "none") return;
|
|
40
|
+
console.error(`[HFChat SDK] ${message}`, ...args);
|
|
41
|
+
}
|
|
29
42
|
var loaderPromise = null;
|
|
30
43
|
var lastInitOpts;
|
|
31
44
|
function getRealApi(selfProxy) {
|
|
@@ -33,16 +46,21 @@ function getRealApi(selfProxy) {
|
|
|
33
46
|
const candidate = window.HFChat;
|
|
34
47
|
if (!candidate) return null;
|
|
35
48
|
if (candidate === selfProxy) return null;
|
|
36
|
-
return candidate;
|
|
49
|
+
if (candidate.__HFCHAT_INSTANCE__ === true) return candidate;
|
|
50
|
+
return null;
|
|
37
51
|
}
|
|
38
52
|
function injectScript(src) {
|
|
39
53
|
return new Promise((resolve, reject) => {
|
|
40
54
|
if (typeof document === "undefined") return resolve();
|
|
41
|
-
if (window.HumanFirstChat)
|
|
55
|
+
if (window.HumanFirstChat) {
|
|
56
|
+
logFull("HumanFirstChat already exists, skipping script injection");
|
|
57
|
+
return resolve();
|
|
58
|
+
}
|
|
42
59
|
const existing = document.querySelector(
|
|
43
60
|
`script[${SCRIPT_DATA_ATTR}="1"]`
|
|
44
61
|
);
|
|
45
62
|
if (existing) {
|
|
63
|
+
logFull("Widget script already in DOM, waiting for load");
|
|
46
64
|
existing.addEventListener("load", () => resolve(), { once: true });
|
|
47
65
|
existing.addEventListener(
|
|
48
66
|
"error",
|
|
@@ -51,17 +69,26 @@ function injectScript(src) {
|
|
|
51
69
|
);
|
|
52
70
|
return;
|
|
53
71
|
}
|
|
72
|
+
log("Injecting widget script:", src);
|
|
54
73
|
window.__HFCHAT_DISABLE_AUTO_INIT = true;
|
|
55
74
|
const script = document.createElement("script");
|
|
56
75
|
script.async = true;
|
|
57
76
|
script.src = src;
|
|
58
77
|
script.setAttribute(SCRIPT_DATA_ATTR, "1");
|
|
59
78
|
script.setAttribute("data-auto-init", "false");
|
|
60
|
-
script.onerror = () =>
|
|
79
|
+
script.onerror = () => {
|
|
80
|
+
logError("Failed to load widget script:", src);
|
|
81
|
+
reject(new Error(`Failed to load widget script: ${src}`));
|
|
82
|
+
};
|
|
61
83
|
script.onload = () => {
|
|
62
84
|
setTimeout(() => {
|
|
63
|
-
if (window.HumanFirstChat)
|
|
64
|
-
|
|
85
|
+
if (window.HumanFirstChat) {
|
|
86
|
+
log("Widget script loaded successfully");
|
|
87
|
+
resolve();
|
|
88
|
+
} else {
|
|
89
|
+
logError("Widget script loaded but HumanFirstChat not found");
|
|
90
|
+
reject(new Error("Widget did not attach to window.HumanFirstChat"));
|
|
91
|
+
}
|
|
65
92
|
}, 0);
|
|
66
93
|
};
|
|
67
94
|
document.head.appendChild(script);
|
|
@@ -70,38 +97,69 @@ function injectScript(src) {
|
|
|
70
97
|
async function loadWidget(opts = {}) {
|
|
71
98
|
if (typeof window === "undefined") return {};
|
|
72
99
|
const scriptSrc = opts.scriptSrc ?? DEFAULT_SCRIPT_SRC;
|
|
100
|
+
logFull("loadWidget starting with options:", { siteId: opts.siteId, scriptSrc });
|
|
73
101
|
await injectScript(scriptSrc);
|
|
74
102
|
if (!window.HumanFirstChat?.init) {
|
|
75
103
|
throw new Error("HumanFirstChat.init is not available after script load");
|
|
76
104
|
}
|
|
105
|
+
log("Calling HumanFirstChat.init()");
|
|
77
106
|
const api = window.HumanFirstChat.init({
|
|
78
107
|
siteId: opts.siteId,
|
|
79
108
|
hidden: opts.hidden,
|
|
109
|
+
hideOnClose: opts.hideOnClose,
|
|
80
110
|
logs: opts.logs,
|
|
81
111
|
baseURL: opts.baseURL,
|
|
82
112
|
host: opts.host
|
|
83
113
|
});
|
|
114
|
+
logFull("HumanFirstChat.init() returned:", { hasInstance: api?.__HFCHAT_INSTANCE__ });
|
|
84
115
|
return api;
|
|
85
116
|
}
|
|
86
117
|
async function init(opts = {}) {
|
|
118
|
+
if (opts.logs) {
|
|
119
|
+
logLevel = opts.logs;
|
|
120
|
+
}
|
|
87
121
|
lastInitOpts = { ...lastInitOpts, ...opts };
|
|
122
|
+
log("init() called with options:", { siteId: opts.siteId, logs: opts.logs });
|
|
88
123
|
const existingApi = getRealApi(HFChat);
|
|
89
|
-
if (existingApi)
|
|
90
|
-
|
|
124
|
+
if (existingApi) {
|
|
125
|
+
log("Returning existing real API");
|
|
126
|
+
return existingApi;
|
|
127
|
+
}
|
|
128
|
+
if (loaderPromise) {
|
|
129
|
+
logFull("Returning existing loader promise");
|
|
130
|
+
return loaderPromise;
|
|
131
|
+
}
|
|
132
|
+
log("Starting widget load...");
|
|
91
133
|
loaderPromise = loadWidget(lastInitOpts);
|
|
92
134
|
return loaderPromise;
|
|
93
135
|
}
|
|
94
136
|
async function waitForMethod(methodName) {
|
|
137
|
+
logFull(`waitForMethod('${methodName}') starting...`);
|
|
95
138
|
await init();
|
|
139
|
+
logFull(`init() completed, checking for '${methodName}'`);
|
|
96
140
|
const existing = getRealApi(HFChat);
|
|
97
|
-
|
|
141
|
+
logFull("getRealApi returned:", existing ? "real API found" : "null");
|
|
142
|
+
if (existing && typeof existing[methodName] === "function") {
|
|
143
|
+
log(`Method '${methodName}' available immediately`);
|
|
144
|
+
return existing;
|
|
145
|
+
}
|
|
146
|
+
logFull(`Polling for '${methodName}'...`);
|
|
98
147
|
return new Promise((resolve, reject) => {
|
|
99
148
|
let attempts = 0;
|
|
100
149
|
const maxAttempts = 50;
|
|
101
150
|
const checkMethod = () => {
|
|
102
151
|
const api = getRealApi(HFChat);
|
|
103
152
|
const fn = api ? api[methodName] : null;
|
|
153
|
+
if (attempts % 10 === 0 && attempts > 0) {
|
|
154
|
+
logFull(`Polling attempt ${attempts}/50 for '${methodName}':`, {
|
|
155
|
+
hasApi: !!api,
|
|
156
|
+
hasMethod: !!fn,
|
|
157
|
+
windowHFChat: typeof window.HFChat,
|
|
158
|
+
isProxy: window.HFChat === HFChat
|
|
159
|
+
});
|
|
160
|
+
}
|
|
104
161
|
if (api && typeof fn === "function") {
|
|
162
|
+
log(`Method '${methodName}' found after ${attempts} attempts`);
|
|
105
163
|
resolve(api);
|
|
106
164
|
return;
|
|
107
165
|
}
|
|
@@ -110,6 +168,7 @@ async function waitForMethod(methodName) {
|
|
|
110
168
|
setTimeout(checkMethod, 100);
|
|
111
169
|
return;
|
|
112
170
|
}
|
|
171
|
+
logError(`Timeout: '${methodName}' not available after 5 seconds`);
|
|
113
172
|
reject(new Error(`HFChat method '${methodName}' not available after 5 seconds`));
|
|
114
173
|
};
|
|
115
174
|
setTimeout(checkMethod, 100);
|
|
@@ -122,6 +181,7 @@ var HFChat = new Proxy(
|
|
|
122
181
|
if (prop === "init") return init;
|
|
123
182
|
if (typeof window === "undefined") return () => {
|
|
124
183
|
};
|
|
184
|
+
if (prop === "__HFCHAT_INSTANCE__") return void 0;
|
|
125
185
|
if (prop === "ready") {
|
|
126
186
|
const api = getRealApi(HFChat);
|
|
127
187
|
return !!api?.ready;
|
|
@@ -134,18 +194,26 @@ var HFChat = new Proxy(
|
|
|
134
194
|
}
|
|
135
195
|
return (...args) => {
|
|
136
196
|
const methodName = String(prop);
|
|
197
|
+
logFull(`Proxy: '${methodName}' called, forwarding to waitForMethod`);
|
|
137
198
|
waitForMethod(methodName).then((api) => {
|
|
138
199
|
const fn = api[methodName];
|
|
139
|
-
if (typeof fn === "function")
|
|
200
|
+
if (typeof fn === "function") {
|
|
201
|
+
logFull(`Proxy: executing '${methodName}' on real API`);
|
|
202
|
+
fn.apply(api, args);
|
|
203
|
+
} else {
|
|
204
|
+
logError(`Method '${methodName}' not found on real API`);
|
|
205
|
+
}
|
|
140
206
|
}).catch((error) => {
|
|
141
|
-
|
|
207
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
142
208
|
});
|
|
143
209
|
};
|
|
144
210
|
}
|
|
145
211
|
}
|
|
146
212
|
);
|
|
147
213
|
if (typeof window !== "undefined") {
|
|
148
|
-
if (!window.HFChat)
|
|
214
|
+
if (!window.HFChat) {
|
|
215
|
+
window.HFChat = HFChat;
|
|
216
|
+
}
|
|
149
217
|
}
|
|
150
218
|
var index_default = HFChat;
|
|
151
219
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Re-export public types for consumers\nexport type {\n HFChatLogLevel,\n HFChatConfig,\n HFChatWidgetSettings,\n HFChatIdentifyMeta,\n HFChatOpenOptions,\n HFChatAPI,\n} from './types'\n\nimport type { HFChatConfig, HFChatAPI } from './types'\n\n// Extended config for the JS SDK loader (adds scriptSrc option)\nexport type HFChatLoaderConfig = HFChatConfig & {\n /** Override widget script source (for self-hosting / testing) */\n scriptSrc?: string\n}\n\nexport type HFChatGlobal = HFChatAPI & {\n /**\n * Load the widget script and initialize the widget.\n */\n init(opts?: HFChatLoaderConfig): Promise<HFChatAPI>\n}\n\ndeclare global {\n interface Window {\n HFChat?: HFChatAPI\n HumanFirstChat?: {\n init: (opts: unknown) => HFChatAPI\n }\n __HFCHAT_DISABLE_AUTO_INIT?: boolean\n }\n}\n\nconst DEFAULT_SCRIPT_SRC = 'https://humanfirst.chat/widget.js'\nconst SCRIPT_DATA_ATTR = 'data-hfchat-widget'\n\nlet loaderPromise: Promise<HFChatAPI> | null = null\nlet lastInitOpts: HFChatLoaderConfig | undefined\n\nfunction getRealApi(selfProxy: HFChatGlobal): HFChatAPI | null {\n if (typeof window === 'undefined') return null\n const candidate = window.HFChat\n if (!candidate) return null\n if (candidate === selfProxy) return null\n return candidate as HFChatAPI\n}\n\nfunction injectScript(src: string): Promise<void> {\n return new Promise((resolve, reject) => {\n if (typeof document === 'undefined') return resolve()\n if (window.HumanFirstChat) return resolve()\n\n const existing = document.querySelector(\n `script[${SCRIPT_DATA_ATTR}=\"1\"]`\n ) as HTMLScriptElement | null\n if (existing) {\n existing.addEventListener('load', () => resolve(), { once: true })\n existing.addEventListener(\n 'error',\n () => reject(new Error('Failed to load widget script')),\n { once: true }\n )\n return\n }\n\n window.__HFCHAT_DISABLE_AUTO_INIT = true\n const script = document.createElement('script')\n script.async = true\n script.src = src\n script.setAttribute(SCRIPT_DATA_ATTR, '1')\n script.setAttribute('data-auto-init', 'false')\n script.onerror = () => reject(new Error(`Failed to load widget script: ${src}`))\n script.onload = () => {\n setTimeout(() => {\n if (window.HumanFirstChat) resolve()\n else reject(new Error('Widget did not attach to window.HumanFirstChat'))\n }, 0)\n }\n document.head.appendChild(script)\n })\n}\n\nasync function loadWidget(opts: HFChatLoaderConfig = {}): Promise<HFChatAPI> {\n if (typeof window === 'undefined') return {} as HFChatAPI\n\n const scriptSrc = opts.scriptSrc ?? DEFAULT_SCRIPT_SRC\n await injectScript(scriptSrc)\n\n if (!window.HumanFirstChat?.init) {\n throw new Error('HumanFirstChat.init is not available after script load')\n }\n\n const api = window.HumanFirstChat.init({\n siteId: opts.siteId,\n hidden: opts.hidden,\n logs: opts.logs,\n baseURL: opts.baseURL,\n host: opts.host,\n })\n\n return api\n}\n\nasync function init(opts: HFChatLoaderConfig = {}): Promise<HFChatAPI> {\n lastInitOpts = { ...lastInitOpts, ...opts }\n\n const existingApi = getRealApi(HFChat)\n if (existingApi) return existingApi\n\n if (loaderPromise) return loaderPromise\n loaderPromise = loadWidget(lastInitOpts)\n return loaderPromise\n}\n\nasync function waitForMethod(methodName: string): Promise<HFChatAPI> {\n await init()\n\n const existing = getRealApi(HFChat)\n if (existing && typeof (existing as any)[methodName] === 'function') return existing\n\n return new Promise((resolve, reject) => {\n let attempts = 0\n const maxAttempts = 50\n\n const checkMethod = () => {\n const api = getRealApi(HFChat)\n const fn = api ? (api as any)[methodName] : null\n\n if (api && typeof fn === 'function') {\n resolve(api)\n return\n }\n\n if (attempts < maxAttempts) {\n attempts += 1\n setTimeout(checkMethod, 100)\n return\n }\n\n reject(new Error(`HFChat method '${methodName}' not available after 5 seconds`))\n }\n\n setTimeout(checkMethod, 100)\n })\n}\n\nexport const HFChat: HFChatGlobal = new Proxy(\n {},\n {\n get(_target, prop) {\n if (prop === 'init') return init\n\n if (typeof window === 'undefined') return () => {}\n\n if (prop === 'ready') {\n const api = getRealApi(HFChat)\n return !!api?.ready\n }\n\n if (prop === 'isOpen') {\n return () => {\n const api = getRealApi(HFChat)\n return api?.isOpen?.() ?? false\n }\n }\n\n return (...args: unknown[]) => {\n const methodName = String(prop)\n waitForMethod(methodName)\n .then((api) => {\n const fn = (api as any)[methodName]\n if (typeof fn === 'function') fn.apply(api, args)\n })\n .catch((error) => {\n console.error(error instanceof Error ? error.message : error)\n })\n }\n },\n }\n) as HFChatGlobal\n\nif (typeof window !== 'undefined') {\n // Expose a stable global singleton (like HEYO), without breaking when the real widget assigns window.HFChat.\n if (!window.HFChat) window.HFChat = HFChat\n}\n\nexport default HFChat\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Re-export public types for consumers\nexport type {\n HFChatLogLevel,\n HFChatConfig,\n HFChatWidgetSettings,\n HFChatIdentifyMeta,\n HFChatOpenOptions,\n HFChatAPI,\n} from './types'\n\nimport type { HFChatConfig, HFChatAPI, HFChatLogLevel } from './types'\n\n// Extended config for the JS SDK loader (adds scriptSrc option)\nexport type HFChatLoaderConfig = HFChatConfig & {\n /** Override widget script source (for self-hosting / testing) */\n scriptSrc?: string\n}\n\nexport type HFChatGlobal = HFChatAPI & {\n /**\n * Load the widget script and initialize the widget.\n */\n init(opts?: HFChatLoaderConfig): Promise<HFChatAPI>\n}\n\ndeclare global {\n interface Window {\n HFChat?: HFChatAPI\n HumanFirstChat?: {\n init: (opts: unknown) => HFChatAPI\n }\n __HFCHAT_DISABLE_AUTO_INIT?: boolean\n }\n}\n\nconst DEFAULT_SCRIPT_SRC = 'https://humanfirst.chat/widget.js'\nconst SCRIPT_DATA_ATTR = 'data-hfchat-widget'\n\n// Logging configuration\nlet logLevel: HFChatLogLevel = 'none'\n\nfunction log(message: string, ...args: unknown[]) {\n if (logLevel === 'none') return\n console.log(`[HFChat SDK] ${message}`, ...args)\n}\n\nfunction logFull(message: string, ...args: unknown[]) {\n if (logLevel !== 'full') return\n console.log(`[HFChat SDK] ${message}`, ...args)\n}\n\nfunction logError(message: string, ...args: unknown[]) {\n if (logLevel === 'none') return\n console.error(`[HFChat SDK] ${message}`, ...args)\n}\n\nlet loaderPromise: Promise<HFChatAPI> | null = null\nlet lastInitOpts: HFChatLoaderConfig | undefined\n\nfunction getRealApi(selfProxy: HFChatGlobal): HFChatAPI | null {\n if (typeof window === 'undefined') return null\n const candidate = window.HFChat\n if (!candidate) return null\n // Check if candidate is the proxy (same reference) - if so, real API not yet available\n if (candidate === selfProxy) return null\n // Check if candidate has the real instance marker\n if ((candidate as any).__HFCHAT_INSTANCE__ === true) return candidate\n return null\n}\n\nfunction injectScript(src: string): Promise<void> {\n return new Promise((resolve, reject) => {\n if (typeof document === 'undefined') return resolve()\n if (window.HumanFirstChat) {\n logFull('HumanFirstChat already exists, skipping script injection')\n return resolve()\n }\n\n const existing = document.querySelector(\n `script[${SCRIPT_DATA_ATTR}=\"1\"]`\n ) as HTMLScriptElement | null\n if (existing) {\n logFull('Widget script already in DOM, waiting for load')\n existing.addEventListener('load', () => resolve(), { once: true })\n existing.addEventListener(\n 'error',\n () => reject(new Error('Failed to load widget script')),\n { once: true }\n )\n return\n }\n\n log('Injecting widget script:', src)\n window.__HFCHAT_DISABLE_AUTO_INIT = true\n const script = document.createElement('script')\n script.async = true\n script.src = src\n script.setAttribute(SCRIPT_DATA_ATTR, '1')\n script.setAttribute('data-auto-init', 'false')\n script.onerror = () => {\n logError('Failed to load widget script:', src)\n reject(new Error(`Failed to load widget script: ${src}`))\n }\n script.onload = () => {\n setTimeout(() => {\n if (window.HumanFirstChat) {\n log('Widget script loaded successfully')\n resolve()\n } else {\n logError('Widget script loaded but HumanFirstChat not found')\n reject(new Error('Widget did not attach to window.HumanFirstChat'))\n }\n }, 0)\n }\n document.head.appendChild(script)\n })\n}\n\nasync function loadWidget(opts: HFChatLoaderConfig = {}): Promise<HFChatAPI> {\n if (typeof window === 'undefined') return {} as HFChatAPI\n\n const scriptSrc = opts.scriptSrc ?? DEFAULT_SCRIPT_SRC\n logFull('loadWidget starting with options:', { siteId: opts.siteId, scriptSrc })\n\n await injectScript(scriptSrc)\n\n if (!window.HumanFirstChat?.init) {\n throw new Error('HumanFirstChat.init is not available after script load')\n }\n\n log('Calling HumanFirstChat.init()')\n const api = window.HumanFirstChat.init({\n siteId: opts.siteId,\n hidden: opts.hidden,\n hideOnClose: opts.hideOnClose,\n logs: opts.logs,\n baseURL: opts.baseURL,\n host: opts.host,\n })\n\n logFull('HumanFirstChat.init() returned:', { hasInstance: (api as any)?.__HFCHAT_INSTANCE__ })\n return api\n}\n\nasync function init(opts: HFChatLoaderConfig = {}): Promise<HFChatAPI> {\n // Update log level from options\n if (opts.logs) {\n logLevel = opts.logs\n }\n\n lastInitOpts = { ...lastInitOpts, ...opts }\n log('init() called with options:', { siteId: opts.siteId, logs: opts.logs })\n\n const existingApi = getRealApi(HFChat)\n if (existingApi) {\n log('Returning existing real API')\n return existingApi\n }\n\n if (loaderPromise) {\n logFull('Returning existing loader promise')\n return loaderPromise\n }\n\n log('Starting widget load...')\n loaderPromise = loadWidget(lastInitOpts)\n return loaderPromise\n}\n\nasync function waitForMethod(methodName: string): Promise<HFChatAPI> {\n logFull(`waitForMethod('${methodName}') starting...`)\n\n // Ensure init is called (may already be in progress)\n await init()\n logFull(`init() completed, checking for '${methodName}'`)\n\n // Check if real API is already available\n const existing = getRealApi(HFChat)\n logFull('getRealApi returned:', existing ? 'real API found' : 'null')\n\n if (existing && typeof (existing as any)[methodName] === 'function') {\n log(`Method '${methodName}' available immediately`)\n return existing\n }\n\n // Poll for the real API to become available\n logFull(`Polling for '${methodName}'...`)\n return new Promise((resolve, reject) => {\n let attempts = 0\n const maxAttempts = 50 // 5 seconds total\n\n const checkMethod = () => {\n const api = getRealApi(HFChat)\n const fn = api ? (api as any)[methodName] : null\n\n // Log every 10 attempts (1 second)\n if (attempts % 10 === 0 && attempts > 0) {\n logFull(`Polling attempt ${attempts}/50 for '${methodName}':`, {\n hasApi: !!api,\n hasMethod: !!fn,\n windowHFChat: typeof window.HFChat,\n isProxy: window.HFChat === HFChat\n })\n }\n\n if (api && typeof fn === 'function') {\n log(`Method '${methodName}' found after ${attempts} attempts`)\n resolve(api)\n return\n }\n\n if (attempts < maxAttempts) {\n attempts += 1\n setTimeout(checkMethod, 100)\n return\n }\n\n logError(`Timeout: '${methodName}' not available after 5 seconds`)\n reject(new Error(`HFChat method '${methodName}' not available after 5 seconds`))\n }\n\n setTimeout(checkMethod, 100)\n })\n}\n\nexport const HFChat: HFChatGlobal = new Proxy(\n {},\n {\n get(_target, prop) {\n // Handle init specially - it's the entry point\n if (prop === 'init') return init\n\n // Server-side rendering guard\n if (typeof window === 'undefined') return () => {}\n\n // Handle __HFCHAT_INSTANCE__ check - return undefined so proxy isn't mistaken for real instance\n if (prop === '__HFCHAT_INSTANCE__') return undefined\n\n // Handle ready property\n if (prop === 'ready') {\n const api = getRealApi(HFChat)\n return !!api?.ready\n }\n\n // Handle isOpen method\n if (prop === 'isOpen') {\n return () => {\n const api = getRealApi(HFChat)\n return api?.isOpen?.() ?? false\n }\n }\n\n // For all other methods, queue them until the real API is ready\n return (...args: unknown[]) => {\n const methodName = String(prop)\n logFull(`Proxy: '${methodName}' called, forwarding to waitForMethod`)\n\n waitForMethod(methodName)\n .then((api) => {\n const fn = (api as any)[methodName]\n if (typeof fn === 'function') {\n logFull(`Proxy: executing '${methodName}' on real API`)\n fn.apply(api, args)\n } else {\n logError(`Method '${methodName}' not found on real API`)\n }\n })\n .catch((error) => {\n logError(error instanceof Error ? error.message : String(error))\n })\n }\n },\n }\n) as HFChatGlobal\n\nif (typeof window !== 'undefined') {\n // Expose a stable global singleton, without breaking when the real widget assigns window.HFChat\n if (!window.HFChat) {\n window.HFChat = HFChat\n }\n}\n\nexport default HFChat\n"],"mappings":"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","names":[]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
var DEFAULT_SCRIPT_SRC = "https://humanfirst.chat/widget.js";
|
|
3
3
|
var SCRIPT_DATA_ATTR = "data-hfchat-widget";
|
|
4
|
+
var logLevel = "none";
|
|
5
|
+
function log(message, ...args) {
|
|
6
|
+
if (logLevel === "none") return;
|
|
7
|
+
console.log(`[HFChat SDK] ${message}`, ...args);
|
|
8
|
+
}
|
|
9
|
+
function logFull(message, ...args) {
|
|
10
|
+
if (logLevel !== "full") return;
|
|
11
|
+
console.log(`[HFChat SDK] ${message}`, ...args);
|
|
12
|
+
}
|
|
13
|
+
function logError(message, ...args) {
|
|
14
|
+
if (logLevel === "none") return;
|
|
15
|
+
console.error(`[HFChat SDK] ${message}`, ...args);
|
|
16
|
+
}
|
|
4
17
|
var loaderPromise = null;
|
|
5
18
|
var lastInitOpts;
|
|
6
19
|
function getRealApi(selfProxy) {
|
|
@@ -8,16 +21,21 @@ function getRealApi(selfProxy) {
|
|
|
8
21
|
const candidate = window.HFChat;
|
|
9
22
|
if (!candidate) return null;
|
|
10
23
|
if (candidate === selfProxy) return null;
|
|
11
|
-
return candidate;
|
|
24
|
+
if (candidate.__HFCHAT_INSTANCE__ === true) return candidate;
|
|
25
|
+
return null;
|
|
12
26
|
}
|
|
13
27
|
function injectScript(src) {
|
|
14
28
|
return new Promise((resolve, reject) => {
|
|
15
29
|
if (typeof document === "undefined") return resolve();
|
|
16
|
-
if (window.HumanFirstChat)
|
|
30
|
+
if (window.HumanFirstChat) {
|
|
31
|
+
logFull("HumanFirstChat already exists, skipping script injection");
|
|
32
|
+
return resolve();
|
|
33
|
+
}
|
|
17
34
|
const existing = document.querySelector(
|
|
18
35
|
`script[${SCRIPT_DATA_ATTR}="1"]`
|
|
19
36
|
);
|
|
20
37
|
if (existing) {
|
|
38
|
+
logFull("Widget script already in DOM, waiting for load");
|
|
21
39
|
existing.addEventListener("load", () => resolve(), { once: true });
|
|
22
40
|
existing.addEventListener(
|
|
23
41
|
"error",
|
|
@@ -26,17 +44,26 @@ function injectScript(src) {
|
|
|
26
44
|
);
|
|
27
45
|
return;
|
|
28
46
|
}
|
|
47
|
+
log("Injecting widget script:", src);
|
|
29
48
|
window.__HFCHAT_DISABLE_AUTO_INIT = true;
|
|
30
49
|
const script = document.createElement("script");
|
|
31
50
|
script.async = true;
|
|
32
51
|
script.src = src;
|
|
33
52
|
script.setAttribute(SCRIPT_DATA_ATTR, "1");
|
|
34
53
|
script.setAttribute("data-auto-init", "false");
|
|
35
|
-
script.onerror = () =>
|
|
54
|
+
script.onerror = () => {
|
|
55
|
+
logError("Failed to load widget script:", src);
|
|
56
|
+
reject(new Error(`Failed to load widget script: ${src}`));
|
|
57
|
+
};
|
|
36
58
|
script.onload = () => {
|
|
37
59
|
setTimeout(() => {
|
|
38
|
-
if (window.HumanFirstChat)
|
|
39
|
-
|
|
60
|
+
if (window.HumanFirstChat) {
|
|
61
|
+
log("Widget script loaded successfully");
|
|
62
|
+
resolve();
|
|
63
|
+
} else {
|
|
64
|
+
logError("Widget script loaded but HumanFirstChat not found");
|
|
65
|
+
reject(new Error("Widget did not attach to window.HumanFirstChat"));
|
|
66
|
+
}
|
|
40
67
|
}, 0);
|
|
41
68
|
};
|
|
42
69
|
document.head.appendChild(script);
|
|
@@ -45,38 +72,69 @@ function injectScript(src) {
|
|
|
45
72
|
async function loadWidget(opts = {}) {
|
|
46
73
|
if (typeof window === "undefined") return {};
|
|
47
74
|
const scriptSrc = opts.scriptSrc ?? DEFAULT_SCRIPT_SRC;
|
|
75
|
+
logFull("loadWidget starting with options:", { siteId: opts.siteId, scriptSrc });
|
|
48
76
|
await injectScript(scriptSrc);
|
|
49
77
|
if (!window.HumanFirstChat?.init) {
|
|
50
78
|
throw new Error("HumanFirstChat.init is not available after script load");
|
|
51
79
|
}
|
|
80
|
+
log("Calling HumanFirstChat.init()");
|
|
52
81
|
const api = window.HumanFirstChat.init({
|
|
53
82
|
siteId: opts.siteId,
|
|
54
83
|
hidden: opts.hidden,
|
|
84
|
+
hideOnClose: opts.hideOnClose,
|
|
55
85
|
logs: opts.logs,
|
|
56
86
|
baseURL: opts.baseURL,
|
|
57
87
|
host: opts.host
|
|
58
88
|
});
|
|
89
|
+
logFull("HumanFirstChat.init() returned:", { hasInstance: api?.__HFCHAT_INSTANCE__ });
|
|
59
90
|
return api;
|
|
60
91
|
}
|
|
61
92
|
async function init(opts = {}) {
|
|
93
|
+
if (opts.logs) {
|
|
94
|
+
logLevel = opts.logs;
|
|
95
|
+
}
|
|
62
96
|
lastInitOpts = { ...lastInitOpts, ...opts };
|
|
97
|
+
log("init() called with options:", { siteId: opts.siteId, logs: opts.logs });
|
|
63
98
|
const existingApi = getRealApi(HFChat);
|
|
64
|
-
if (existingApi)
|
|
65
|
-
|
|
99
|
+
if (existingApi) {
|
|
100
|
+
log("Returning existing real API");
|
|
101
|
+
return existingApi;
|
|
102
|
+
}
|
|
103
|
+
if (loaderPromise) {
|
|
104
|
+
logFull("Returning existing loader promise");
|
|
105
|
+
return loaderPromise;
|
|
106
|
+
}
|
|
107
|
+
log("Starting widget load...");
|
|
66
108
|
loaderPromise = loadWidget(lastInitOpts);
|
|
67
109
|
return loaderPromise;
|
|
68
110
|
}
|
|
69
111
|
async function waitForMethod(methodName) {
|
|
112
|
+
logFull(`waitForMethod('${methodName}') starting...`);
|
|
70
113
|
await init();
|
|
114
|
+
logFull(`init() completed, checking for '${methodName}'`);
|
|
71
115
|
const existing = getRealApi(HFChat);
|
|
72
|
-
|
|
116
|
+
logFull("getRealApi returned:", existing ? "real API found" : "null");
|
|
117
|
+
if (existing && typeof existing[methodName] === "function") {
|
|
118
|
+
log(`Method '${methodName}' available immediately`);
|
|
119
|
+
return existing;
|
|
120
|
+
}
|
|
121
|
+
logFull(`Polling for '${methodName}'...`);
|
|
73
122
|
return new Promise((resolve, reject) => {
|
|
74
123
|
let attempts = 0;
|
|
75
124
|
const maxAttempts = 50;
|
|
76
125
|
const checkMethod = () => {
|
|
77
126
|
const api = getRealApi(HFChat);
|
|
78
127
|
const fn = api ? api[methodName] : null;
|
|
128
|
+
if (attempts % 10 === 0 && attempts > 0) {
|
|
129
|
+
logFull(`Polling attempt ${attempts}/50 for '${methodName}':`, {
|
|
130
|
+
hasApi: !!api,
|
|
131
|
+
hasMethod: !!fn,
|
|
132
|
+
windowHFChat: typeof window.HFChat,
|
|
133
|
+
isProxy: window.HFChat === HFChat
|
|
134
|
+
});
|
|
135
|
+
}
|
|
79
136
|
if (api && typeof fn === "function") {
|
|
137
|
+
log(`Method '${methodName}' found after ${attempts} attempts`);
|
|
80
138
|
resolve(api);
|
|
81
139
|
return;
|
|
82
140
|
}
|
|
@@ -85,6 +143,7 @@ async function waitForMethod(methodName) {
|
|
|
85
143
|
setTimeout(checkMethod, 100);
|
|
86
144
|
return;
|
|
87
145
|
}
|
|
146
|
+
logError(`Timeout: '${methodName}' not available after 5 seconds`);
|
|
88
147
|
reject(new Error(`HFChat method '${methodName}' not available after 5 seconds`));
|
|
89
148
|
};
|
|
90
149
|
setTimeout(checkMethod, 100);
|
|
@@ -97,6 +156,7 @@ var HFChat = new Proxy(
|
|
|
97
156
|
if (prop === "init") return init;
|
|
98
157
|
if (typeof window === "undefined") return () => {
|
|
99
158
|
};
|
|
159
|
+
if (prop === "__HFCHAT_INSTANCE__") return void 0;
|
|
100
160
|
if (prop === "ready") {
|
|
101
161
|
const api = getRealApi(HFChat);
|
|
102
162
|
return !!api?.ready;
|
|
@@ -109,18 +169,26 @@ var HFChat = new Proxy(
|
|
|
109
169
|
}
|
|
110
170
|
return (...args) => {
|
|
111
171
|
const methodName = String(prop);
|
|
172
|
+
logFull(`Proxy: '${methodName}' called, forwarding to waitForMethod`);
|
|
112
173
|
waitForMethod(methodName).then((api) => {
|
|
113
174
|
const fn = api[methodName];
|
|
114
|
-
if (typeof fn === "function")
|
|
175
|
+
if (typeof fn === "function") {
|
|
176
|
+
logFull(`Proxy: executing '${methodName}' on real API`);
|
|
177
|
+
fn.apply(api, args);
|
|
178
|
+
} else {
|
|
179
|
+
logError(`Method '${methodName}' not found on real API`);
|
|
180
|
+
}
|
|
115
181
|
}).catch((error) => {
|
|
116
|
-
|
|
182
|
+
logError(error instanceof Error ? error.message : String(error));
|
|
117
183
|
});
|
|
118
184
|
};
|
|
119
185
|
}
|
|
120
186
|
}
|
|
121
187
|
);
|
|
122
188
|
if (typeof window !== "undefined") {
|
|
123
|
-
if (!window.HFChat)
|
|
189
|
+
if (!window.HFChat) {
|
|
190
|
+
window.HFChat = HFChat;
|
|
191
|
+
}
|
|
124
192
|
}
|
|
125
193
|
var index_default = HFChat;
|
|
126
194
|
export {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Re-export public types for consumers\nexport type {\n HFChatLogLevel,\n HFChatConfig,\n HFChatWidgetSettings,\n HFChatIdentifyMeta,\n HFChatOpenOptions,\n HFChatAPI,\n} from './types'\n\nimport type { HFChatConfig, HFChatAPI } from './types'\n\n// Extended config for the JS SDK loader (adds scriptSrc option)\nexport type HFChatLoaderConfig = HFChatConfig & {\n /** Override widget script source (for self-hosting / testing) */\n scriptSrc?: string\n}\n\nexport type HFChatGlobal = HFChatAPI & {\n /**\n * Load the widget script and initialize the widget.\n */\n init(opts?: HFChatLoaderConfig): Promise<HFChatAPI>\n}\n\ndeclare global {\n interface Window {\n HFChat?: HFChatAPI\n HumanFirstChat?: {\n init: (opts: unknown) => HFChatAPI\n }\n __HFCHAT_DISABLE_AUTO_INIT?: boolean\n }\n}\n\nconst DEFAULT_SCRIPT_SRC = 'https://humanfirst.chat/widget.js'\nconst SCRIPT_DATA_ATTR = 'data-hfchat-widget'\n\nlet loaderPromise: Promise<HFChatAPI> | null = null\nlet lastInitOpts: HFChatLoaderConfig | undefined\n\nfunction getRealApi(selfProxy: HFChatGlobal): HFChatAPI | null {\n if (typeof window === 'undefined') return null\n const candidate = window.HFChat\n if (!candidate) return null\n if (candidate === selfProxy) return null\n return candidate as HFChatAPI\n}\n\nfunction injectScript(src: string): Promise<void> {\n return new Promise((resolve, reject) => {\n if (typeof document === 'undefined') return resolve()\n if (window.HumanFirstChat) return resolve()\n\n const existing = document.querySelector(\n `script[${SCRIPT_DATA_ATTR}=\"1\"]`\n ) as HTMLScriptElement | null\n if (existing) {\n existing.addEventListener('load', () => resolve(), { once: true })\n existing.addEventListener(\n 'error',\n () => reject(new Error('Failed to load widget script')),\n { once: true }\n )\n return\n }\n\n window.__HFCHAT_DISABLE_AUTO_INIT = true\n const script = document.createElement('script')\n script.async = true\n script.src = src\n script.setAttribute(SCRIPT_DATA_ATTR, '1')\n script.setAttribute('data-auto-init', 'false')\n script.onerror = () => reject(new Error(`Failed to load widget script: ${src}`))\n script.onload = () => {\n setTimeout(() => {\n if (window.HumanFirstChat) resolve()\n else reject(new Error('Widget did not attach to window.HumanFirstChat'))\n }, 0)\n }\n document.head.appendChild(script)\n })\n}\n\nasync function loadWidget(opts: HFChatLoaderConfig = {}): Promise<HFChatAPI> {\n if (typeof window === 'undefined') return {} as HFChatAPI\n\n const scriptSrc = opts.scriptSrc ?? DEFAULT_SCRIPT_SRC\n await injectScript(scriptSrc)\n\n if (!window.HumanFirstChat?.init) {\n throw new Error('HumanFirstChat.init is not available after script load')\n }\n\n const api = window.HumanFirstChat.init({\n siteId: opts.siteId,\n hidden: opts.hidden,\n logs: opts.logs,\n baseURL: opts.baseURL,\n host: opts.host,\n })\n\n return api\n}\n\nasync function init(opts: HFChatLoaderConfig = {}): Promise<HFChatAPI> {\n lastInitOpts = { ...lastInitOpts, ...opts }\n\n const existingApi = getRealApi(HFChat)\n if (existingApi) return existingApi\n\n if (loaderPromise) return loaderPromise\n loaderPromise = loadWidget(lastInitOpts)\n return loaderPromise\n}\n\nasync function waitForMethod(methodName: string): Promise<HFChatAPI> {\n await init()\n\n const existing = getRealApi(HFChat)\n if (existing && typeof (existing as any)[methodName] === 'function') return existing\n\n return new Promise((resolve, reject) => {\n let attempts = 0\n const maxAttempts = 50\n\n const checkMethod = () => {\n const api = getRealApi(HFChat)\n const fn = api ? (api as any)[methodName] : null\n\n if (api && typeof fn === 'function') {\n resolve(api)\n return\n }\n\n if (attempts < maxAttempts) {\n attempts += 1\n setTimeout(checkMethod, 100)\n return\n }\n\n reject(new Error(`HFChat method '${methodName}' not available after 5 seconds`))\n }\n\n setTimeout(checkMethod, 100)\n })\n}\n\nexport const HFChat: HFChatGlobal = new Proxy(\n {},\n {\n get(_target, prop) {\n if (prop === 'init') return init\n\n if (typeof window === 'undefined') return () => {}\n\n if (prop === 'ready') {\n const api = getRealApi(HFChat)\n return !!api?.ready\n }\n\n if (prop === 'isOpen') {\n return () => {\n const api = getRealApi(HFChat)\n return api?.isOpen?.() ?? false\n }\n }\n\n return (...args: unknown[]) => {\n const methodName = String(prop)\n waitForMethod(methodName)\n .then((api) => {\n const fn = (api as any)[methodName]\n if (typeof fn === 'function') fn.apply(api, args)\n })\n .catch((error) => {\n console.error(error instanceof Error ? error.message : error)\n })\n }\n },\n }\n) as HFChatGlobal\n\nif (typeof window !== 'undefined') {\n // Expose a stable global singleton (like HEYO), without breaking when the real widget assigns window.HFChat.\n if (!window.HFChat) window.HFChat = HFChat\n}\n\nexport default HFChat\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Re-export public types for consumers\nexport type {\n HFChatLogLevel,\n HFChatConfig,\n HFChatWidgetSettings,\n HFChatIdentifyMeta,\n HFChatOpenOptions,\n HFChatAPI,\n} from './types'\n\nimport type { HFChatConfig, HFChatAPI, HFChatLogLevel } from './types'\n\n// Extended config for the JS SDK loader (adds scriptSrc option)\nexport type HFChatLoaderConfig = HFChatConfig & {\n /** Override widget script source (for self-hosting / testing) */\n scriptSrc?: string\n}\n\nexport type HFChatGlobal = HFChatAPI & {\n /**\n * Load the widget script and initialize the widget.\n */\n init(opts?: HFChatLoaderConfig): Promise<HFChatAPI>\n}\n\ndeclare global {\n interface Window {\n HFChat?: HFChatAPI\n HumanFirstChat?: {\n init: (opts: unknown) => HFChatAPI\n }\n __HFCHAT_DISABLE_AUTO_INIT?: boolean\n }\n}\n\nconst DEFAULT_SCRIPT_SRC = 'https://humanfirst.chat/widget.js'\nconst SCRIPT_DATA_ATTR = 'data-hfchat-widget'\n\n// Logging configuration\nlet logLevel: HFChatLogLevel = 'none'\n\nfunction log(message: string, ...args: unknown[]) {\n if (logLevel === 'none') return\n console.log(`[HFChat SDK] ${message}`, ...args)\n}\n\nfunction logFull(message: string, ...args: unknown[]) {\n if (logLevel !== 'full') return\n console.log(`[HFChat SDK] ${message}`, ...args)\n}\n\nfunction logError(message: string, ...args: unknown[]) {\n if (logLevel === 'none') return\n console.error(`[HFChat SDK] ${message}`, ...args)\n}\n\nlet loaderPromise: Promise<HFChatAPI> | null = null\nlet lastInitOpts: HFChatLoaderConfig | undefined\n\nfunction getRealApi(selfProxy: HFChatGlobal): HFChatAPI | null {\n if (typeof window === 'undefined') return null\n const candidate = window.HFChat\n if (!candidate) return null\n // Check if candidate is the proxy (same reference) - if so, real API not yet available\n if (candidate === selfProxy) return null\n // Check if candidate has the real instance marker\n if ((candidate as any).__HFCHAT_INSTANCE__ === true) return candidate\n return null\n}\n\nfunction injectScript(src: string): Promise<void> {\n return new Promise((resolve, reject) => {\n if (typeof document === 'undefined') return resolve()\n if (window.HumanFirstChat) {\n logFull('HumanFirstChat already exists, skipping script injection')\n return resolve()\n }\n\n const existing = document.querySelector(\n `script[${SCRIPT_DATA_ATTR}=\"1\"]`\n ) as HTMLScriptElement | null\n if (existing) {\n logFull('Widget script already in DOM, waiting for load')\n existing.addEventListener('load', () => resolve(), { once: true })\n existing.addEventListener(\n 'error',\n () => reject(new Error('Failed to load widget script')),\n { once: true }\n )\n return\n }\n\n log('Injecting widget script:', src)\n window.__HFCHAT_DISABLE_AUTO_INIT = true\n const script = document.createElement('script')\n script.async = true\n script.src = src\n script.setAttribute(SCRIPT_DATA_ATTR, '1')\n script.setAttribute('data-auto-init', 'false')\n script.onerror = () => {\n logError('Failed to load widget script:', src)\n reject(new Error(`Failed to load widget script: ${src}`))\n }\n script.onload = () => {\n setTimeout(() => {\n if (window.HumanFirstChat) {\n log('Widget script loaded successfully')\n resolve()\n } else {\n logError('Widget script loaded but HumanFirstChat not found')\n reject(new Error('Widget did not attach to window.HumanFirstChat'))\n }\n }, 0)\n }\n document.head.appendChild(script)\n })\n}\n\nasync function loadWidget(opts: HFChatLoaderConfig = {}): Promise<HFChatAPI> {\n if (typeof window === 'undefined') return {} as HFChatAPI\n\n const scriptSrc = opts.scriptSrc ?? DEFAULT_SCRIPT_SRC\n logFull('loadWidget starting with options:', { siteId: opts.siteId, scriptSrc })\n\n await injectScript(scriptSrc)\n\n if (!window.HumanFirstChat?.init) {\n throw new Error('HumanFirstChat.init is not available after script load')\n }\n\n log('Calling HumanFirstChat.init()')\n const api = window.HumanFirstChat.init({\n siteId: opts.siteId,\n hidden: opts.hidden,\n hideOnClose: opts.hideOnClose,\n logs: opts.logs,\n baseURL: opts.baseURL,\n host: opts.host,\n })\n\n logFull('HumanFirstChat.init() returned:', { hasInstance: (api as any)?.__HFCHAT_INSTANCE__ })\n return api\n}\n\nasync function init(opts: HFChatLoaderConfig = {}): Promise<HFChatAPI> {\n // Update log level from options\n if (opts.logs) {\n logLevel = opts.logs\n }\n\n lastInitOpts = { ...lastInitOpts, ...opts }\n log('init() called with options:', { siteId: opts.siteId, logs: opts.logs })\n\n const existingApi = getRealApi(HFChat)\n if (existingApi) {\n log('Returning existing real API')\n return existingApi\n }\n\n if (loaderPromise) {\n logFull('Returning existing loader promise')\n return loaderPromise\n }\n\n log('Starting widget load...')\n loaderPromise = loadWidget(lastInitOpts)\n return loaderPromise\n}\n\nasync function waitForMethod(methodName: string): Promise<HFChatAPI> {\n logFull(`waitForMethod('${methodName}') starting...`)\n\n // Ensure init is called (may already be in progress)\n await init()\n logFull(`init() completed, checking for '${methodName}'`)\n\n // Check if real API is already available\n const existing = getRealApi(HFChat)\n logFull('getRealApi returned:', existing ? 'real API found' : 'null')\n\n if (existing && typeof (existing as any)[methodName] === 'function') {\n log(`Method '${methodName}' available immediately`)\n return existing\n }\n\n // Poll for the real API to become available\n logFull(`Polling for '${methodName}'...`)\n return new Promise((resolve, reject) => {\n let attempts = 0\n const maxAttempts = 50 // 5 seconds total\n\n const checkMethod = () => {\n const api = getRealApi(HFChat)\n const fn = api ? (api as any)[methodName] : null\n\n // Log every 10 attempts (1 second)\n if (attempts % 10 === 0 && attempts > 0) {\n logFull(`Polling attempt ${attempts}/50 for '${methodName}':`, {\n hasApi: !!api,\n hasMethod: !!fn,\n windowHFChat: typeof window.HFChat,\n isProxy: window.HFChat === HFChat\n })\n }\n\n if (api && typeof fn === 'function') {\n log(`Method '${methodName}' found after ${attempts} attempts`)\n resolve(api)\n return\n }\n\n if (attempts < maxAttempts) {\n attempts += 1\n setTimeout(checkMethod, 100)\n return\n }\n\n logError(`Timeout: '${methodName}' not available after 5 seconds`)\n reject(new Error(`HFChat method '${methodName}' not available after 5 seconds`))\n }\n\n setTimeout(checkMethod, 100)\n })\n}\n\nexport const HFChat: HFChatGlobal = new Proxy(\n {},\n {\n get(_target, prop) {\n // Handle init specially - it's the entry point\n if (prop === 'init') return init\n\n // Server-side rendering guard\n if (typeof window === 'undefined') return () => {}\n\n // Handle __HFCHAT_INSTANCE__ check - return undefined so proxy isn't mistaken for real instance\n if (prop === '__HFCHAT_INSTANCE__') return undefined\n\n // Handle ready property\n if (prop === 'ready') {\n const api = getRealApi(HFChat)\n return !!api?.ready\n }\n\n // Handle isOpen method\n if (prop === 'isOpen') {\n return () => {\n const api = getRealApi(HFChat)\n return api?.isOpen?.() ?? false\n }\n }\n\n // For all other methods, queue them until the real API is ready\n return (...args: unknown[]) => {\n const methodName = String(prop)\n logFull(`Proxy: '${methodName}' called, forwarding to waitForMethod`)\n\n waitForMethod(methodName)\n .then((api) => {\n const fn = (api as any)[methodName]\n if (typeof fn === 'function') {\n logFull(`Proxy: executing '${methodName}' on real API`)\n fn.apply(api, args)\n } else {\n logError(`Method '${methodName}' not found on real API`)\n }\n })\n .catch((error) => {\n logError(error instanceof Error ? error.message : String(error))\n })\n }\n },\n }\n) as HFChatGlobal\n\nif (typeof window !== 'undefined') {\n // Expose a stable global singleton, without breaking when the real widget assigns window.HFChat\n if (!window.HFChat) {\n window.HFChat = HFChat\n }\n}\n\nexport default HFChat\n"],"mappings":"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","names":[]}
|