@humanfirst-chat/js 0.1.6 → 0.2.1

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 CHANGED
@@ -58,6 +58,10 @@ type HFChatGlobal = HFChatAPI & {
58
58
  */
59
59
  init(opts?: HFChatLoaderConfig): Promise<HFChatAPI>;
60
60
  };
61
+ type QueuedCall = {
62
+ method: keyof HFChatAPI;
63
+ args: unknown[];
64
+ };
61
65
  declare global {
62
66
  interface Window {
63
67
  HFChat?: HFChatAPI;
@@ -65,8 +69,13 @@ declare global {
65
69
  init: (opts: unknown) => HFChatAPI;
66
70
  };
67
71
  __HFCHAT_DISABLE_AUTO_INIT?: boolean;
72
+ __HFCHAT_QUEUE__?: QueuedCall[];
73
+ __HFCHAT_REAL__?: HFChatAPI;
68
74
  }
69
75
  }
70
- declare const HFChat: HFChatGlobal;
76
+ declare const HFChat: HFChatGlobal & {
77
+ __HFCHAT_STUB__?: true;
78
+ __real?: HFChatAPI;
79
+ };
71
80
 
72
81
  export { HFChat, type HFChatAPI, type HFChatConfig, type HFChatGlobal, type HFChatIdentifyMeta, type HFChatLoaderConfig, type HFChatLogLevel, type HFChatOpenOptions, type HFChatWidgetSettings, HFChat as default };
package/dist/index.d.ts CHANGED
@@ -58,6 +58,10 @@ type HFChatGlobal = HFChatAPI & {
58
58
  */
59
59
  init(opts?: HFChatLoaderConfig): Promise<HFChatAPI>;
60
60
  };
61
+ type QueuedCall = {
62
+ method: keyof HFChatAPI;
63
+ args: unknown[];
64
+ };
61
65
  declare global {
62
66
  interface Window {
63
67
  HFChat?: HFChatAPI;
@@ -65,8 +69,13 @@ declare global {
65
69
  init: (opts: unknown) => HFChatAPI;
66
70
  };
67
71
  __HFCHAT_DISABLE_AUTO_INIT?: boolean;
72
+ __HFCHAT_QUEUE__?: QueuedCall[];
73
+ __HFCHAT_REAL__?: HFChatAPI;
68
74
  }
69
75
  }
70
- declare const HFChat: HFChatGlobal;
76
+ declare const HFChat: HFChatGlobal & {
77
+ __HFCHAT_STUB__?: true;
78
+ __real?: HFChatAPI;
79
+ };
71
80
 
72
81
  export { HFChat, type HFChatAPI, type HFChatConfig, type HFChatGlobal, type HFChatIdentifyMeta, type HFChatLoaderConfig, type HFChatLogLevel, type HFChatOpenOptions, type HFChatWidgetSettings, HFChat as default };
package/dist/index.js CHANGED
@@ -41,14 +41,44 @@ function logError(message, ...args) {
41
41
  }
42
42
  var loaderPromise = null;
43
43
  var lastInitOpts;
44
- function getRealApi(selfProxy) {
44
+ function getQueue() {
45
+ if (typeof window === "undefined") return [];
46
+ if (!window.__HFCHAT_QUEUE__) {
47
+ window.__HFCHAT_QUEUE__ = [];
48
+ }
49
+ return window.__HFCHAT_QUEUE__;
50
+ }
51
+ function enqueue(method, args) {
52
+ if (typeof window === "undefined") return;
53
+ getQueue().push({ method, args });
54
+ }
55
+ function getRealApi() {
45
56
  if (typeof window === "undefined") return null;
57
+ const globalReal = window.__HFCHAT_REAL__;
58
+ if (globalReal && globalReal.__HFCHAT_INSTANCE__ === true) {
59
+ return globalReal;
60
+ }
46
61
  const candidate = window.HFChat;
47
- if (!candidate) return null;
48
- if (candidate === selfProxy) return null;
49
- if (candidate.__HFCHAT_INSTANCE__ === true) return candidate;
62
+ if (candidate && candidate.__HFCHAT_INSTANCE__ === true) {
63
+ return candidate;
64
+ }
65
+ if (candidate && candidate.__real && candidate.__real.__HFCHAT_INSTANCE__ === true) {
66
+ return candidate.__real;
67
+ }
50
68
  return null;
51
69
  }
70
+ function callOrQueue(method, args) {
71
+ if (typeof window === "undefined") return;
72
+ const api = getRealApi();
73
+ const fn = api ? api[method] : null;
74
+ if (api && typeof fn === "function") {
75
+ logFull(`Calling '${String(method)}' on real API`);
76
+ fn.apply(api, args);
77
+ return;
78
+ }
79
+ logFull(`Queueing '${String(method)}' until real API is ready`);
80
+ enqueue(method, args);
81
+ }
52
82
  function injectScript(src) {
53
83
  return new Promise((resolve, reject) => {
54
84
  if (typeof document === "undefined") return resolve();
@@ -111,8 +141,9 @@ async function loadWidget(opts = {}) {
111
141
  baseURL: opts.baseURL,
112
142
  host: opts.host
113
143
  });
114
- logFull("HumanFirstChat.init() returned:", { hasInstance: api?.__HFCHAT_INSTANCE__ });
115
- return api;
144
+ const realApi = getRealApi();
145
+ logFull("HumanFirstChat.init() returned:", { hasInstance: realApi?.__HFCHAT_INSTANCE__ });
146
+ return realApi ?? api;
116
147
  }
117
148
  async function init(opts = {}) {
118
149
  if (opts.logs) {
@@ -120,7 +151,7 @@ async function init(opts = {}) {
120
151
  }
121
152
  lastInitOpts = { ...lastInitOpts, ...opts };
122
153
  log("init() called with options:", { siteId: opts.siteId, logs: opts.logs });
123
- const existingApi = getRealApi(HFChat);
154
+ const existingApi = getRealApi();
124
155
  if (existingApi) {
125
156
  log("Returning existing real API");
126
157
  return existingApi;
@@ -133,84 +164,46 @@ async function init(opts = {}) {
133
164
  loaderPromise = loadWidget(lastInitOpts);
134
165
  return loaderPromise;
135
166
  }
136
- async function waitForMethod(methodName) {
137
- logFull(`waitForMethod('${methodName}') starting...`);
138
- await init();
139
- logFull(`init() completed, checking for '${methodName}'`);
140
- const existing = getRealApi(HFChat);
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}'...`);
147
- return new Promise((resolve, reject) => {
148
- let attempts = 0;
149
- const maxAttempts = 50;
150
- const checkMethod = () => {
151
- const api = getRealApi(HFChat);
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
- });
167
+ var HFChat = {
168
+ init,
169
+ show: () => callOrQueue("show", []),
170
+ hide: () => callOrQueue("hide", []),
171
+ open: (options) => callOrQueue("open", [options]),
172
+ close: () => callOrQueue("close", []),
173
+ toggle: () => callOrQueue("toggle", []),
174
+ isOpen: () => {
175
+ const api = getRealApi();
176
+ return api?.isOpen?.() ?? false;
177
+ },
178
+ identify: (meta) => callOrQueue("identify", [meta]),
179
+ configure: (settings) => callOrQueue("configure", [settings]),
180
+ onReady: (callback) => {
181
+ if (typeof callback !== "function") return;
182
+ const api = getRealApi();
183
+ if (api?.ready) {
184
+ try {
185
+ callback();
186
+ } catch (error) {
187
+ console.error("[HFChat SDK] Error in onReady callback:", error);
160
188
  }
161
- if (api && typeof fn === "function") {
162
- log(`Method '${methodName}' found after ${attempts} attempts`);
163
- resolve(api);
164
- return;
165
- }
166
- if (attempts < maxAttempts) {
167
- attempts += 1;
168
- setTimeout(checkMethod, 100);
169
- return;
170
- }
171
- logError(`Timeout: '${methodName}' not available after 5 seconds`);
172
- reject(new Error(`HFChat method '${methodName}' not available after 5 seconds`));
173
- };
174
- setTimeout(checkMethod, 100);
175
- });
176
- }
177
- var HFChat = new Proxy(
178
- {},
179
- {
180
- get(_target, prop) {
181
- if (prop === "init") return init;
182
- if (typeof window === "undefined") return () => {
183
- };
184
- if (prop === "__HFCHAT_INSTANCE__") return void 0;
185
- if (prop === "then") return void 0;
186
- if (prop === "ready") {
187
- const api = getRealApi(HFChat);
188
- return !!api?.ready;
189
- }
190
- if (prop === "isOpen") {
191
- return () => {
192
- const api = getRealApi(HFChat);
193
- return api?.isOpen?.() ?? false;
194
- };
195
- }
196
- return (...args) => {
197
- const methodName = String(prop);
198
- logFull(`Proxy: '${methodName}' called, forwarding to waitForMethod`);
199
- waitForMethod(methodName).then((api) => {
200
- const fn = api[methodName];
201
- if (typeof fn === "function") {
202
- logFull(`Proxy: executing '${methodName}' on real API`);
203
- fn.apply(api, args);
204
- } else {
205
- logError(`Method '${methodName}' not found on real API`);
206
- }
207
- }).catch((error) => {
208
- logError(error instanceof Error ? error.message : String(error));
209
- });
210
- };
189
+ return;
211
190
  }
191
+ callOrQueue("onReady", [callback]);
192
+ },
193
+ onOpen: (callback) => {
194
+ if (typeof callback !== "function") return;
195
+ callOrQueue("onOpen", [callback]);
196
+ },
197
+ onClose: (callback) => {
198
+ if (typeof callback !== "function") return;
199
+ callOrQueue("onClose", [callback]);
200
+ },
201
+ get ready() {
202
+ const api = getRealApi();
203
+ return api?.ready ?? false;
212
204
  }
213
- );
205
+ };
206
+ HFChat.__HFCHAT_STUB__ = true;
214
207
  if (typeof window !== "undefined") {
215
208
  if (!window.HFChat) {
216
209
  window.HFChat = HFChat;
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, 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 'then' - return undefined so proxy isn't treated as a thenable/Promise\n // This is critical because await/Promise.resolve() check for .then()\n if (prop === 'then') 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":[]}
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\ntype QueuedCall = {\n method: keyof HFChatAPI\n args: unknown[]\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 __HFCHAT_QUEUE__?: QueuedCall[]\n __HFCHAT_REAL__?: HFChatAPI\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 getQueue(): QueuedCall[] {\n if (typeof window === 'undefined') return []\n if (!window.__HFCHAT_QUEUE__) {\n window.__HFCHAT_QUEUE__ = []\n }\n return window.__HFCHAT_QUEUE__\n}\n\nfunction enqueue(method: keyof HFChatAPI, args: unknown[]) {\n if (typeof window === 'undefined') return\n getQueue().push({ method, args })\n}\n\nfunction getRealApi(): HFChatAPI | null {\n if (typeof window === 'undefined') return null\n\n const globalReal = window.__HFCHAT_REAL__\n if (globalReal && (globalReal as any).__HFCHAT_INSTANCE__ === true) {\n return globalReal\n }\n\n const candidate = window.HFChat as any\n if (candidate && candidate.__HFCHAT_INSTANCE__ === true) {\n return candidate as HFChatAPI\n }\n\n if (candidate && candidate.__real && candidate.__real.__HFCHAT_INSTANCE__ === true) {\n return candidate.__real as HFChatAPI\n }\n\n return null\n}\n\nfunction callOrQueue(method: keyof HFChatAPI, args: unknown[]) {\n if (typeof window === 'undefined') return\n const api = getRealApi()\n const fn = api ? (api as any)[method] : null\n\n if (api && typeof fn === 'function') {\n logFull(`Calling '${String(method)}' on real API`)\n fn.apply(api, args)\n return\n }\n\n logFull(`Queueing '${String(method)}' until real API is ready`)\n enqueue(method, args)\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 const realApi = getRealApi()\n logFull('HumanFirstChat.init() returned:', { hasInstance: (realApi as any)?.__HFCHAT_INSTANCE__ })\n return realApi ?? api\n}\n\nasync function init(opts: HFChatLoaderConfig = {}): Promise<HFChatAPI> {\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()\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\nconst HFChat: HFChatGlobal & { __HFCHAT_STUB__?: true; __real?: HFChatAPI } = {\n init,\n\n show: () => callOrQueue('show', []),\n hide: () => callOrQueue('hide', []),\n open: (options) => callOrQueue('open', [options]),\n close: () => callOrQueue('close', []),\n toggle: () => callOrQueue('toggle', []),\n isOpen: () => {\n const api = getRealApi()\n return api?.isOpen?.() ?? false\n },\n identify: (meta) => callOrQueue('identify', [meta]),\n configure: (settings) => callOrQueue('configure', [settings]),\n onReady: (callback) => {\n if (typeof callback !== 'function') return\n const api = getRealApi()\n if (api?.ready) {\n try {\n callback()\n } catch (error) {\n console.error('[HFChat SDK] Error in onReady callback:', error)\n }\n return\n }\n callOrQueue('onReady', [callback])\n },\n onOpen: (callback) => {\n if (typeof callback !== 'function') return\n callOrQueue('onOpen', [callback])\n },\n onClose: (callback) => {\n if (typeof callback !== 'function') return\n callOrQueue('onClose', [callback])\n },\n get ready() {\n const api = getRealApi()\n return api?.ready ?? false\n },\n}\n\nHFChat.__HFCHAT_STUB__ = true\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 { HFChat }\nexport default HFChat\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0CA,IAAM,qBAAqB;AAC3B,IAAM,mBAAmB;AAGzB,IAAI,WAA2B;AAE/B,SAAS,IAAI,YAAoB,MAAiB;AAChD,MAAI,aAAa,OAAQ;AACzB,UAAQ,IAAI,gBAAgB,OAAO,IAAI,GAAG,IAAI;AAChD;AAEA,SAAS,QAAQ,YAAoB,MAAiB;AACpD,MAAI,aAAa,OAAQ;AACzB,UAAQ,IAAI,gBAAgB,OAAO,IAAI,GAAG,IAAI;AAChD;AAEA,SAAS,SAAS,YAAoB,MAAiB;AACrD,MAAI,aAAa,OAAQ;AACzB,UAAQ,MAAM,gBAAgB,OAAO,IAAI,GAAG,IAAI;AAClD;AAEA,IAAI,gBAA2C;AAC/C,IAAI;AAEJ,SAAS,WAAyB;AAChC,MAAI,OAAO,WAAW,YAAa,QAAO,CAAC;AAC3C,MAAI,CAAC,OAAO,kBAAkB;AAC5B,WAAO,mBAAmB,CAAC;AAAA,EAC7B;AACA,SAAO,OAAO;AAChB;AAEA,SAAS,QAAQ,QAAyB,MAAiB;AACzD,MAAI,OAAO,WAAW,YAAa;AACnC,WAAS,EAAE,KAAK,EAAE,QAAQ,KAAK,CAAC;AAClC;AAEA,SAAS,aAA+B;AACtC,MAAI,OAAO,WAAW,YAAa,QAAO;AAE1C,QAAM,aAAa,OAAO;AAC1B,MAAI,cAAe,WAAmB,wBAAwB,MAAM;AAClE,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,OAAO;AACzB,MAAI,aAAa,UAAU,wBAAwB,MAAM;AACvD,WAAO;AAAA,EACT;AAEA,MAAI,aAAa,UAAU,UAAU,UAAU,OAAO,wBAAwB,MAAM;AAClF,WAAO,UAAU;AAAA,EACnB;AAEA,SAAO;AACT;AAEA,SAAS,YAAY,QAAyB,MAAiB;AAC7D,MAAI,OAAO,WAAW,YAAa;AACnC,QAAM,MAAM,WAAW;AACvB,QAAM,KAAK,MAAO,IAAY,MAAM,IAAI;AAExC,MAAI,OAAO,OAAO,OAAO,YAAY;AACnC,YAAQ,YAAY,OAAO,MAAM,CAAC,eAAe;AACjD,OAAG,MAAM,KAAK,IAAI;AAClB;AAAA,EACF;AAEA,UAAQ,aAAa,OAAO,MAAM,CAAC,2BAA2B;AAC9D,UAAQ,QAAQ,IAAI;AACtB;AAEA,SAAS,aAAa,KAA4B;AAChD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,OAAO,aAAa,YAAa,QAAO,QAAQ;AACpD,QAAI,OAAO,gBAAgB;AACzB,cAAQ,0DAA0D;AAClE,aAAO,QAAQ;AAAA,IACjB;AAEA,UAAM,WAAW,SAAS;AAAA,MACxB,UAAU,gBAAgB;AAAA,IAC5B;AACA,QAAI,UAAU;AACZ,cAAQ,gDAAgD;AACxD,eAAS,iBAAiB,QAAQ,MAAM,QAAQ,GAAG,EAAE,MAAM,KAAK,CAAC;AACjE,eAAS;AAAA,QACP;AAAA,QACA,MAAM,OAAO,IAAI,MAAM,8BAA8B,CAAC;AAAA,QACtD,EAAE,MAAM,KAAK;AAAA,MACf;AACA;AAAA,IACF;AAEA,QAAI,4BAA4B,GAAG;AACnC,WAAO,6BAA6B;AACpC,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,QAAQ;AACf,WAAO,MAAM;AACb,WAAO,aAAa,kBAAkB,GAAG;AACzC,WAAO,aAAa,kBAAkB,OAAO;AAC7C,WAAO,UAAU,MAAM;AACrB,eAAS,iCAAiC,GAAG;AAC7C,aAAO,IAAI,MAAM,iCAAiC,GAAG,EAAE,CAAC;AAAA,IAC1D;AACA,WAAO,SAAS,MAAM;AACpB,iBAAW,MAAM;AACf,YAAI,OAAO,gBAAgB;AACzB,cAAI,mCAAmC;AACvC,kBAAQ;AAAA,QACV,OAAO;AACL,mBAAS,mDAAmD;AAC5D,iBAAO,IAAI,MAAM,gDAAgD,CAAC;AAAA,QACpE;AAAA,MACF,GAAG,CAAC;AAAA,IACN;AACA,aAAS,KAAK,YAAY,MAAM;AAAA,EAClC,CAAC;AACH;AAEA,eAAe,WAAW,OAA2B,CAAC,GAAuB;AAC3E,MAAI,OAAO,WAAW,YAAa,QAAO,CAAC;AAE3C,QAAM,YAAY,KAAK,aAAa;AACpC,UAAQ,qCAAqC,EAAE,QAAQ,KAAK,QAAQ,UAAU,CAAC;AAE/E,QAAM,aAAa,SAAS;AAE5B,MAAI,CAAC,OAAO,gBAAgB,MAAM;AAChC,UAAM,IAAI,MAAM,wDAAwD;AAAA,EAC1E;AAEA,MAAI,+BAA+B;AACnC,QAAM,MAAM,OAAO,eAAe,KAAK;AAAA,IACrC,QAAQ,KAAK;AAAA,IACb,QAAQ,KAAK;AAAA,IACb,aAAa,KAAK;AAAA,IAClB,MAAM,KAAK;AAAA,IACX,SAAS,KAAK;AAAA,IACd,MAAM,KAAK;AAAA,EACb,CAAC;AAED,QAAM,UAAU,WAAW;AAC3B,UAAQ,mCAAmC,EAAE,aAAc,SAAiB,oBAAoB,CAAC;AACjG,SAAO,WAAW;AACpB;AAEA,eAAe,KAAK,OAA2B,CAAC,GAAuB;AACrE,MAAI,KAAK,MAAM;AACb,eAAW,KAAK;AAAA,EAClB;AAEA,iBAAe,EAAE,GAAG,cAAc,GAAG,KAAK;AAC1C,MAAI,+BAA+B,EAAE,QAAQ,KAAK,QAAQ,MAAM,KAAK,KAAK,CAAC;AAE3E,QAAM,cAAc,WAAW;AAC/B,MAAI,aAAa;AACf,QAAI,6BAA6B;AACjC,WAAO;AAAA,EACT;AAEA,MAAI,eAAe;AACjB,YAAQ,mCAAmC;AAC3C,WAAO;AAAA,EACT;AAEA,MAAI,yBAAyB;AAC7B,kBAAgB,WAAW,YAAY;AACvC,SAAO;AACT;AAEA,IAAM,SAAwE;AAAA,EAC5E;AAAA,EAEA,MAAM,MAAM,YAAY,QAAQ,CAAC,CAAC;AAAA,EAClC,MAAM,MAAM,YAAY,QAAQ,CAAC,CAAC;AAAA,EAClC,MAAM,CAAC,YAAY,YAAY,QAAQ,CAAC,OAAO,CAAC;AAAA,EAChD,OAAO,MAAM,YAAY,SAAS,CAAC,CAAC;AAAA,EACpC,QAAQ,MAAM,YAAY,UAAU,CAAC,CAAC;AAAA,EACtC,QAAQ,MAAM;AACZ,UAAM,MAAM,WAAW;AACvB,WAAO,KAAK,SAAS,KAAK;AAAA,EAC5B;AAAA,EACA,UAAU,CAAC,SAAS,YAAY,YAAY,CAAC,IAAI,CAAC;AAAA,EAClD,WAAW,CAAC,aAAa,YAAY,aAAa,CAAC,QAAQ,CAAC;AAAA,EAC5D,SAAS,CAAC,aAAa;AACrB,QAAI,OAAO,aAAa,WAAY;AACpC,UAAM,MAAM,WAAW;AACvB,QAAI,KAAK,OAAO;AACd,UAAI;AACF,iBAAS;AAAA,MACX,SAAS,OAAO;AACd,gBAAQ,MAAM,2CAA2C,KAAK;AAAA,MAChE;AACA;AAAA,IACF;AACA,gBAAY,WAAW,CAAC,QAAQ,CAAC;AAAA,EACnC;AAAA,EACA,QAAQ,CAAC,aAAa;AACpB,QAAI,OAAO,aAAa,WAAY;AACpC,gBAAY,UAAU,CAAC,QAAQ,CAAC;AAAA,EAClC;AAAA,EACA,SAAS,CAAC,aAAa;AACrB,QAAI,OAAO,aAAa,WAAY;AACpC,gBAAY,WAAW,CAAC,QAAQ,CAAC;AAAA,EACnC;AAAA,EACA,IAAI,QAAQ;AACV,UAAM,MAAM,WAAW;AACvB,WAAO,KAAK,SAAS;AAAA,EACvB;AACF;AAEA,OAAO,kBAAkB;AAEzB,IAAI,OAAO,WAAW,aAAa;AAEjC,MAAI,CAAC,OAAO,QAAQ;AAClB,WAAO,SAAS;AAAA,EAClB;AACF;AAGA,IAAO,gBAAQ;","names":[]}
package/dist/index.mjs CHANGED
@@ -16,14 +16,44 @@ function logError(message, ...args) {
16
16
  }
17
17
  var loaderPromise = null;
18
18
  var lastInitOpts;
19
- function getRealApi(selfProxy) {
19
+ function getQueue() {
20
+ if (typeof window === "undefined") return [];
21
+ if (!window.__HFCHAT_QUEUE__) {
22
+ window.__HFCHAT_QUEUE__ = [];
23
+ }
24
+ return window.__HFCHAT_QUEUE__;
25
+ }
26
+ function enqueue(method, args) {
27
+ if (typeof window === "undefined") return;
28
+ getQueue().push({ method, args });
29
+ }
30
+ function getRealApi() {
20
31
  if (typeof window === "undefined") return null;
32
+ const globalReal = window.__HFCHAT_REAL__;
33
+ if (globalReal && globalReal.__HFCHAT_INSTANCE__ === true) {
34
+ return globalReal;
35
+ }
21
36
  const candidate = window.HFChat;
22
- if (!candidate) return null;
23
- if (candidate === selfProxy) return null;
24
- if (candidate.__HFCHAT_INSTANCE__ === true) return candidate;
37
+ if (candidate && candidate.__HFCHAT_INSTANCE__ === true) {
38
+ return candidate;
39
+ }
40
+ if (candidate && candidate.__real && candidate.__real.__HFCHAT_INSTANCE__ === true) {
41
+ return candidate.__real;
42
+ }
25
43
  return null;
26
44
  }
45
+ function callOrQueue(method, args) {
46
+ if (typeof window === "undefined") return;
47
+ const api = getRealApi();
48
+ const fn = api ? api[method] : null;
49
+ if (api && typeof fn === "function") {
50
+ logFull(`Calling '${String(method)}' on real API`);
51
+ fn.apply(api, args);
52
+ return;
53
+ }
54
+ logFull(`Queueing '${String(method)}' until real API is ready`);
55
+ enqueue(method, args);
56
+ }
27
57
  function injectScript(src) {
28
58
  return new Promise((resolve, reject) => {
29
59
  if (typeof document === "undefined") return resolve();
@@ -86,8 +116,9 @@ async function loadWidget(opts = {}) {
86
116
  baseURL: opts.baseURL,
87
117
  host: opts.host
88
118
  });
89
- logFull("HumanFirstChat.init() returned:", { hasInstance: api?.__HFCHAT_INSTANCE__ });
90
- return api;
119
+ const realApi = getRealApi();
120
+ logFull("HumanFirstChat.init() returned:", { hasInstance: realApi?.__HFCHAT_INSTANCE__ });
121
+ return realApi ?? api;
91
122
  }
92
123
  async function init(opts = {}) {
93
124
  if (opts.logs) {
@@ -95,7 +126,7 @@ async function init(opts = {}) {
95
126
  }
96
127
  lastInitOpts = { ...lastInitOpts, ...opts };
97
128
  log("init() called with options:", { siteId: opts.siteId, logs: opts.logs });
98
- const existingApi = getRealApi(HFChat);
129
+ const existingApi = getRealApi();
99
130
  if (existingApi) {
100
131
  log("Returning existing real API");
101
132
  return existingApi;
@@ -108,84 +139,46 @@ async function init(opts = {}) {
108
139
  loaderPromise = loadWidget(lastInitOpts);
109
140
  return loaderPromise;
110
141
  }
111
- async function waitForMethod(methodName) {
112
- logFull(`waitForMethod('${methodName}') starting...`);
113
- await init();
114
- logFull(`init() completed, checking for '${methodName}'`);
115
- const existing = getRealApi(HFChat);
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}'...`);
122
- return new Promise((resolve, reject) => {
123
- let attempts = 0;
124
- const maxAttempts = 50;
125
- const checkMethod = () => {
126
- const api = getRealApi(HFChat);
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
- });
142
+ var HFChat = {
143
+ init,
144
+ show: () => callOrQueue("show", []),
145
+ hide: () => callOrQueue("hide", []),
146
+ open: (options) => callOrQueue("open", [options]),
147
+ close: () => callOrQueue("close", []),
148
+ toggle: () => callOrQueue("toggle", []),
149
+ isOpen: () => {
150
+ const api = getRealApi();
151
+ return api?.isOpen?.() ?? false;
152
+ },
153
+ identify: (meta) => callOrQueue("identify", [meta]),
154
+ configure: (settings) => callOrQueue("configure", [settings]),
155
+ onReady: (callback) => {
156
+ if (typeof callback !== "function") return;
157
+ const api = getRealApi();
158
+ if (api?.ready) {
159
+ try {
160
+ callback();
161
+ } catch (error) {
162
+ console.error("[HFChat SDK] Error in onReady callback:", error);
135
163
  }
136
- if (api && typeof fn === "function") {
137
- log(`Method '${methodName}' found after ${attempts} attempts`);
138
- resolve(api);
139
- return;
140
- }
141
- if (attempts < maxAttempts) {
142
- attempts += 1;
143
- setTimeout(checkMethod, 100);
144
- return;
145
- }
146
- logError(`Timeout: '${methodName}' not available after 5 seconds`);
147
- reject(new Error(`HFChat method '${methodName}' not available after 5 seconds`));
148
- };
149
- setTimeout(checkMethod, 100);
150
- });
151
- }
152
- var HFChat = new Proxy(
153
- {},
154
- {
155
- get(_target, prop) {
156
- if (prop === "init") return init;
157
- if (typeof window === "undefined") return () => {
158
- };
159
- if (prop === "__HFCHAT_INSTANCE__") return void 0;
160
- if (prop === "then") return void 0;
161
- if (prop === "ready") {
162
- const api = getRealApi(HFChat);
163
- return !!api?.ready;
164
- }
165
- if (prop === "isOpen") {
166
- return () => {
167
- const api = getRealApi(HFChat);
168
- return api?.isOpen?.() ?? false;
169
- };
170
- }
171
- return (...args) => {
172
- const methodName = String(prop);
173
- logFull(`Proxy: '${methodName}' called, forwarding to waitForMethod`);
174
- waitForMethod(methodName).then((api) => {
175
- const fn = api[methodName];
176
- if (typeof fn === "function") {
177
- logFull(`Proxy: executing '${methodName}' on real API`);
178
- fn.apply(api, args);
179
- } else {
180
- logError(`Method '${methodName}' not found on real API`);
181
- }
182
- }).catch((error) => {
183
- logError(error instanceof Error ? error.message : String(error));
184
- });
185
- };
164
+ return;
186
165
  }
166
+ callOrQueue("onReady", [callback]);
167
+ },
168
+ onOpen: (callback) => {
169
+ if (typeof callback !== "function") return;
170
+ callOrQueue("onOpen", [callback]);
171
+ },
172
+ onClose: (callback) => {
173
+ if (typeof callback !== "function") return;
174
+ callOrQueue("onClose", [callback]);
175
+ },
176
+ get ready() {
177
+ const api = getRealApi();
178
+ return api?.ready ?? false;
187
179
  }
188
- );
180
+ };
181
+ HFChat.__HFCHAT_STUB__ = true;
189
182
  if (typeof window !== "undefined") {
190
183
  if (!window.HFChat) {
191
184
  window.HFChat = HFChat;
@@ -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, 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 'then' - return undefined so proxy isn't treated as a thenable/Promise\n // This is critical because await/Promise.resolve() check for .then()\n if (prop === 'then') 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":[]}
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\ntype QueuedCall = {\n method: keyof HFChatAPI\n args: unknown[]\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 __HFCHAT_QUEUE__?: QueuedCall[]\n __HFCHAT_REAL__?: HFChatAPI\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 getQueue(): QueuedCall[] {\n if (typeof window === 'undefined') return []\n if (!window.__HFCHAT_QUEUE__) {\n window.__HFCHAT_QUEUE__ = []\n }\n return window.__HFCHAT_QUEUE__\n}\n\nfunction enqueue(method: keyof HFChatAPI, args: unknown[]) {\n if (typeof window === 'undefined') return\n getQueue().push({ method, args })\n}\n\nfunction getRealApi(): HFChatAPI | null {\n if (typeof window === 'undefined') return null\n\n const globalReal = window.__HFCHAT_REAL__\n if (globalReal && (globalReal as any).__HFCHAT_INSTANCE__ === true) {\n return globalReal\n }\n\n const candidate = window.HFChat as any\n if (candidate && candidate.__HFCHAT_INSTANCE__ === true) {\n return candidate as HFChatAPI\n }\n\n if (candidate && candidate.__real && candidate.__real.__HFCHAT_INSTANCE__ === true) {\n return candidate.__real as HFChatAPI\n }\n\n return null\n}\n\nfunction callOrQueue(method: keyof HFChatAPI, args: unknown[]) {\n if (typeof window === 'undefined') return\n const api = getRealApi()\n const fn = api ? (api as any)[method] : null\n\n if (api && typeof fn === 'function') {\n logFull(`Calling '${String(method)}' on real API`)\n fn.apply(api, args)\n return\n }\n\n logFull(`Queueing '${String(method)}' until real API is ready`)\n enqueue(method, args)\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 const realApi = getRealApi()\n logFull('HumanFirstChat.init() returned:', { hasInstance: (realApi as any)?.__HFCHAT_INSTANCE__ })\n return realApi ?? api\n}\n\nasync function init(opts: HFChatLoaderConfig = {}): Promise<HFChatAPI> {\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()\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\nconst HFChat: HFChatGlobal & { __HFCHAT_STUB__?: true; __real?: HFChatAPI } = {\n init,\n\n show: () => callOrQueue('show', []),\n hide: () => callOrQueue('hide', []),\n open: (options) => callOrQueue('open', [options]),\n close: () => callOrQueue('close', []),\n toggle: () => callOrQueue('toggle', []),\n isOpen: () => {\n const api = getRealApi()\n return api?.isOpen?.() ?? false\n },\n identify: (meta) => callOrQueue('identify', [meta]),\n configure: (settings) => callOrQueue('configure', [settings]),\n onReady: (callback) => {\n if (typeof callback !== 'function') return\n const api = getRealApi()\n if (api?.ready) {\n try {\n callback()\n } catch (error) {\n console.error('[HFChat SDK] Error in onReady callback:', error)\n }\n return\n }\n callOrQueue('onReady', [callback])\n },\n onOpen: (callback) => {\n if (typeof callback !== 'function') return\n callOrQueue('onOpen', [callback])\n },\n onClose: (callback) => {\n if (typeof callback !== 'function') return\n callOrQueue('onClose', [callback])\n },\n get ready() {\n const api = getRealApi()\n return api?.ready ?? false\n },\n}\n\nHFChat.__HFCHAT_STUB__ = true\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 { HFChat }\nexport default HFChat\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,27 +1,27 @@
1
1
  {
2
- "name": "@humanfirst-chat/js",
3
- "version": "0.1.6",
4
- "private": false,
5
- "description": "HumanFirst.chat JavaScript SDK",
6
- "main": "./dist/index.js",
7
- "module": "./dist/index.mjs",
8
- "types": "./dist/index.d.ts",
9
- "type": "commonjs",
10
- "files": [
11
- "dist",
12
- "README.md"
13
- ],
14
- "scripts": {
15
- "build": "tsup",
16
- "prepublishOnly": "pnpm build",
17
- "type-check": "tsc --noEmit"
18
- },
19
- "publishConfig": {
20
- "access": "public"
21
- },
22
- "devDependencies": {
23
- "@types/node": "^25.0.8",
24
- "tsup": "^8.5.1",
25
- "typescript": "^5.9.3"
26
- }
2
+ "name": "@humanfirst-chat/js",
3
+ "version": "0.2.1",
4
+ "private": false,
5
+ "description": "HumanFirst.chat JavaScript SDK",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "type": "commonjs",
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsup",
16
+ "prepublishOnly": "pnpm build",
17
+ "type-check": "tsc --noEmit"
18
+ },
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^25.0.8",
24
+ "tsup": "^8.5.1",
25
+ "typescript": "^5.9.3"
26
+ }
27
27
  }