@humanfirst-chat/js 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +24 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +153 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +128 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +29 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { HFChatAPI, HFChatConfig } from '@chat/shared';
|
|
2
|
+
export { HFChatAPI, HFChatConfig, HFChatIdentifyMeta, HFChatLogLevel, HFChatWidgetSettings } from '@chat/shared';
|
|
3
|
+
|
|
4
|
+
type HFChatLoaderConfig = HFChatConfig & {
|
|
5
|
+
/** Override widget script source (for self-hosting / testing) */
|
|
6
|
+
scriptSrc?: string;
|
|
7
|
+
};
|
|
8
|
+
type HFChatGlobal = HFChatAPI & {
|
|
9
|
+
/**
|
|
10
|
+
* Load the widget script and initialize the widget.
|
|
11
|
+
*/
|
|
12
|
+
init(opts?: HFChatLoaderConfig): Promise<HFChatAPI>;
|
|
13
|
+
};
|
|
14
|
+
declare global {
|
|
15
|
+
interface Window {
|
|
16
|
+
HFChat?: HFChatAPI;
|
|
17
|
+
HumanFirstChat?: {
|
|
18
|
+
init: (opts: unknown) => HFChatAPI;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
declare const HFChat: HFChatGlobal;
|
|
23
|
+
|
|
24
|
+
export { HFChat, type HFChatGlobal, type HFChatLoaderConfig, HFChat as default };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { HFChatAPI, HFChatConfig } from '@chat/shared';
|
|
2
|
+
export { HFChatAPI, HFChatConfig, HFChatIdentifyMeta, HFChatLogLevel, HFChatWidgetSettings } from '@chat/shared';
|
|
3
|
+
|
|
4
|
+
type HFChatLoaderConfig = HFChatConfig & {
|
|
5
|
+
/** Override widget script source (for self-hosting / testing) */
|
|
6
|
+
scriptSrc?: string;
|
|
7
|
+
};
|
|
8
|
+
type HFChatGlobal = HFChatAPI & {
|
|
9
|
+
/**
|
|
10
|
+
* Load the widget script and initialize the widget.
|
|
11
|
+
*/
|
|
12
|
+
init(opts?: HFChatLoaderConfig): Promise<HFChatAPI>;
|
|
13
|
+
};
|
|
14
|
+
declare global {
|
|
15
|
+
interface Window {
|
|
16
|
+
HFChat?: HFChatAPI;
|
|
17
|
+
HumanFirstChat?: {
|
|
18
|
+
init: (opts: unknown) => HFChatAPI;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
declare const HFChat: HFChatGlobal;
|
|
23
|
+
|
|
24
|
+
export { HFChat, type HFChatGlobal, type HFChatLoaderConfig, HFChat as default };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
HFChat: () => HFChat,
|
|
24
|
+
default: () => index_default
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
var DEFAULT_SCRIPT_SRC = "https://humanfirst.chat/widget.js";
|
|
28
|
+
var SCRIPT_DATA_ATTR = "data-hfchat-widget";
|
|
29
|
+
var loaderPromise = null;
|
|
30
|
+
var lastInitOpts;
|
|
31
|
+
function getRealApi(selfProxy) {
|
|
32
|
+
if (typeof window === "undefined") return null;
|
|
33
|
+
const candidate = window.HFChat;
|
|
34
|
+
if (!candidate) return null;
|
|
35
|
+
if (candidate === selfProxy) return null;
|
|
36
|
+
return candidate;
|
|
37
|
+
}
|
|
38
|
+
function injectScript(src) {
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
if (typeof document === "undefined") return resolve();
|
|
41
|
+
if (window.HumanFirstChat) return resolve();
|
|
42
|
+
const existing = document.querySelector(
|
|
43
|
+
`script[${SCRIPT_DATA_ATTR}="1"]`
|
|
44
|
+
);
|
|
45
|
+
if (existing) {
|
|
46
|
+
existing.addEventListener("load", () => resolve(), { once: true });
|
|
47
|
+
existing.addEventListener(
|
|
48
|
+
"error",
|
|
49
|
+
() => reject(new Error("Failed to load widget script")),
|
|
50
|
+
{ once: true }
|
|
51
|
+
);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const script = document.createElement("script");
|
|
55
|
+
script.async = true;
|
|
56
|
+
script.src = src;
|
|
57
|
+
script.setAttribute(SCRIPT_DATA_ATTR, "1");
|
|
58
|
+
script.onerror = () => reject(new Error(`Failed to load widget script: ${src}`));
|
|
59
|
+
script.onload = () => {
|
|
60
|
+
setTimeout(() => {
|
|
61
|
+
if (window.HumanFirstChat) resolve();
|
|
62
|
+
else reject(new Error("Widget did not attach to window.HumanFirstChat"));
|
|
63
|
+
}, 0);
|
|
64
|
+
};
|
|
65
|
+
document.head.appendChild(script);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
async function loadWidget(opts = {}) {
|
|
69
|
+
if (typeof window === "undefined") return {};
|
|
70
|
+
const scriptSrc = opts.scriptSrc ?? DEFAULT_SCRIPT_SRC;
|
|
71
|
+
await injectScript(scriptSrc);
|
|
72
|
+
if (!window.HumanFirstChat?.init) {
|
|
73
|
+
throw new Error("HumanFirstChat.init is not available after script load");
|
|
74
|
+
}
|
|
75
|
+
const api = window.HumanFirstChat.init({
|
|
76
|
+
siteId: opts.siteId,
|
|
77
|
+
hidden: opts.hidden,
|
|
78
|
+
logs: opts.logs,
|
|
79
|
+
baseURL: opts.baseURL,
|
|
80
|
+
host: opts.host
|
|
81
|
+
});
|
|
82
|
+
return api;
|
|
83
|
+
}
|
|
84
|
+
async function init(opts = {}) {
|
|
85
|
+
lastInitOpts = { ...lastInitOpts, ...opts };
|
|
86
|
+
const existingApi = getRealApi(HFChat);
|
|
87
|
+
if (existingApi) return existingApi;
|
|
88
|
+
if (loaderPromise) return loaderPromise;
|
|
89
|
+
loaderPromise = loadWidget(lastInitOpts);
|
|
90
|
+
return loaderPromise;
|
|
91
|
+
}
|
|
92
|
+
async function waitForMethod(methodName) {
|
|
93
|
+
await init();
|
|
94
|
+
const existing = getRealApi(HFChat);
|
|
95
|
+
if (existing && typeof existing[methodName] === "function") return existing;
|
|
96
|
+
return new Promise((resolve, reject) => {
|
|
97
|
+
let attempts = 0;
|
|
98
|
+
const maxAttempts = 50;
|
|
99
|
+
const checkMethod = () => {
|
|
100
|
+
const api = getRealApi(HFChat);
|
|
101
|
+
const fn = api ? api[methodName] : null;
|
|
102
|
+
if (api && typeof fn === "function") {
|
|
103
|
+
resolve(api);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (attempts < maxAttempts) {
|
|
107
|
+
attempts += 1;
|
|
108
|
+
setTimeout(checkMethod, 100);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
reject(new Error(`HFChat method '${methodName}' not available after 5 seconds`));
|
|
112
|
+
};
|
|
113
|
+
setTimeout(checkMethod, 100);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
var HFChat = new Proxy(
|
|
117
|
+
{},
|
|
118
|
+
{
|
|
119
|
+
get(_target, prop) {
|
|
120
|
+
if (prop === "init") return init;
|
|
121
|
+
if (typeof window === "undefined") return () => {
|
|
122
|
+
};
|
|
123
|
+
if (prop === "ready") {
|
|
124
|
+
const api = getRealApi(HFChat);
|
|
125
|
+
return !!api?.ready;
|
|
126
|
+
}
|
|
127
|
+
if (prop === "isOpen") {
|
|
128
|
+
return () => {
|
|
129
|
+
const api = getRealApi(HFChat);
|
|
130
|
+
return api?.isOpen?.() ?? false;
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return (...args) => {
|
|
134
|
+
const methodName = String(prop);
|
|
135
|
+
waitForMethod(methodName).then((api) => {
|
|
136
|
+
const fn = api[methodName];
|
|
137
|
+
if (typeof fn === "function") fn.apply(api, args);
|
|
138
|
+
}).catch((error) => {
|
|
139
|
+
console.error(error instanceof Error ? error.message : error);
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
if (typeof window !== "undefined") {
|
|
146
|
+
if (!window.HFChat) window.HFChat = HFChat;
|
|
147
|
+
}
|
|
148
|
+
var index_default = HFChat;
|
|
149
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
150
|
+
0 && (module.exports = {
|
|
151
|
+
HFChat
|
|
152
|
+
});
|
|
153
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Re-export shared types for consumers\nexport type {\n HFChatLogLevel,\n HFChatConfig,\n HFChatWidgetSettings,\n HFChatIdentifyMeta,\n HFChatAPI,\n} from '@chat/shared'\n\nimport type {\n HFChatConfig,\n HFChatAPI,\n} from '@chat/shared'\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 }\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 const script = document.createElement('script')\n script.async = true\n script.src = src\n script.setAttribute(SCRIPT_DATA_ATTR, '1')\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":[]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
var DEFAULT_SCRIPT_SRC = "https://humanfirst.chat/widget.js";
|
|
3
|
+
var SCRIPT_DATA_ATTR = "data-hfchat-widget";
|
|
4
|
+
var loaderPromise = null;
|
|
5
|
+
var lastInitOpts;
|
|
6
|
+
function getRealApi(selfProxy) {
|
|
7
|
+
if (typeof window === "undefined") return null;
|
|
8
|
+
const candidate = window.HFChat;
|
|
9
|
+
if (!candidate) return null;
|
|
10
|
+
if (candidate === selfProxy) return null;
|
|
11
|
+
return candidate;
|
|
12
|
+
}
|
|
13
|
+
function injectScript(src) {
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
if (typeof document === "undefined") return resolve();
|
|
16
|
+
if (window.HumanFirstChat) return resolve();
|
|
17
|
+
const existing = document.querySelector(
|
|
18
|
+
`script[${SCRIPT_DATA_ATTR}="1"]`
|
|
19
|
+
);
|
|
20
|
+
if (existing) {
|
|
21
|
+
existing.addEventListener("load", () => resolve(), { once: true });
|
|
22
|
+
existing.addEventListener(
|
|
23
|
+
"error",
|
|
24
|
+
() => reject(new Error("Failed to load widget script")),
|
|
25
|
+
{ once: true }
|
|
26
|
+
);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const script = document.createElement("script");
|
|
30
|
+
script.async = true;
|
|
31
|
+
script.src = src;
|
|
32
|
+
script.setAttribute(SCRIPT_DATA_ATTR, "1");
|
|
33
|
+
script.onerror = () => reject(new Error(`Failed to load widget script: ${src}`));
|
|
34
|
+
script.onload = () => {
|
|
35
|
+
setTimeout(() => {
|
|
36
|
+
if (window.HumanFirstChat) resolve();
|
|
37
|
+
else reject(new Error("Widget did not attach to window.HumanFirstChat"));
|
|
38
|
+
}, 0);
|
|
39
|
+
};
|
|
40
|
+
document.head.appendChild(script);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async function loadWidget(opts = {}) {
|
|
44
|
+
if (typeof window === "undefined") return {};
|
|
45
|
+
const scriptSrc = opts.scriptSrc ?? DEFAULT_SCRIPT_SRC;
|
|
46
|
+
await injectScript(scriptSrc);
|
|
47
|
+
if (!window.HumanFirstChat?.init) {
|
|
48
|
+
throw new Error("HumanFirstChat.init is not available after script load");
|
|
49
|
+
}
|
|
50
|
+
const api = window.HumanFirstChat.init({
|
|
51
|
+
siteId: opts.siteId,
|
|
52
|
+
hidden: opts.hidden,
|
|
53
|
+
logs: opts.logs,
|
|
54
|
+
baseURL: opts.baseURL,
|
|
55
|
+
host: opts.host
|
|
56
|
+
});
|
|
57
|
+
return api;
|
|
58
|
+
}
|
|
59
|
+
async function init(opts = {}) {
|
|
60
|
+
lastInitOpts = { ...lastInitOpts, ...opts };
|
|
61
|
+
const existingApi = getRealApi(HFChat);
|
|
62
|
+
if (existingApi) return existingApi;
|
|
63
|
+
if (loaderPromise) return loaderPromise;
|
|
64
|
+
loaderPromise = loadWidget(lastInitOpts);
|
|
65
|
+
return loaderPromise;
|
|
66
|
+
}
|
|
67
|
+
async function waitForMethod(methodName) {
|
|
68
|
+
await init();
|
|
69
|
+
const existing = getRealApi(HFChat);
|
|
70
|
+
if (existing && typeof existing[methodName] === "function") return existing;
|
|
71
|
+
return new Promise((resolve, reject) => {
|
|
72
|
+
let attempts = 0;
|
|
73
|
+
const maxAttempts = 50;
|
|
74
|
+
const checkMethod = () => {
|
|
75
|
+
const api = getRealApi(HFChat);
|
|
76
|
+
const fn = api ? api[methodName] : null;
|
|
77
|
+
if (api && typeof fn === "function") {
|
|
78
|
+
resolve(api);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (attempts < maxAttempts) {
|
|
82
|
+
attempts += 1;
|
|
83
|
+
setTimeout(checkMethod, 100);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
reject(new Error(`HFChat method '${methodName}' not available after 5 seconds`));
|
|
87
|
+
};
|
|
88
|
+
setTimeout(checkMethod, 100);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
var HFChat = new Proxy(
|
|
92
|
+
{},
|
|
93
|
+
{
|
|
94
|
+
get(_target, prop) {
|
|
95
|
+
if (prop === "init") return init;
|
|
96
|
+
if (typeof window === "undefined") return () => {
|
|
97
|
+
};
|
|
98
|
+
if (prop === "ready") {
|
|
99
|
+
const api = getRealApi(HFChat);
|
|
100
|
+
return !!api?.ready;
|
|
101
|
+
}
|
|
102
|
+
if (prop === "isOpen") {
|
|
103
|
+
return () => {
|
|
104
|
+
const api = getRealApi(HFChat);
|
|
105
|
+
return api?.isOpen?.() ?? false;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return (...args) => {
|
|
109
|
+
const methodName = String(prop);
|
|
110
|
+
waitForMethod(methodName).then((api) => {
|
|
111
|
+
const fn = api[methodName];
|
|
112
|
+
if (typeof fn === "function") fn.apply(api, args);
|
|
113
|
+
}).catch((error) => {
|
|
114
|
+
console.error(error instanceof Error ? error.message : error);
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
if (typeof window !== "undefined") {
|
|
121
|
+
if (!window.HFChat) window.HFChat = HFChat;
|
|
122
|
+
}
|
|
123
|
+
var index_default = HFChat;
|
|
124
|
+
export {
|
|
125
|
+
HFChat,
|
|
126
|
+
index_default as default
|
|
127
|
+
};
|
|
128
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["// Re-export shared types for consumers\nexport type {\n HFChatLogLevel,\n HFChatConfig,\n HFChatWidgetSettings,\n HFChatIdentifyMeta,\n HFChatAPI,\n} from '@chat/shared'\n\nimport type {\n HFChatConfig,\n HFChatAPI,\n} from '@chat/shared'\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 }\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 const script = document.createElement('script')\n script.async = true\n script.src = src\n script.setAttribute(SCRIPT_DATA_ATTR, '1')\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":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@humanfirst-chat/js",
|
|
3
|
+
"version": "0.1.0",
|
|
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
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsup",
|
|
15
|
+
"prepublishOnly": "pnpm build",
|
|
16
|
+
"type-check": "tsc --noEmit"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@chat/shared": "workspace:*"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^25.0.3",
|
|
26
|
+
"tsup": "^8.5.1",
|
|
27
|
+
"typescript": "^5.9.3"
|
|
28
|
+
}
|
|
29
|
+
}
|