@herazgo/sdk 0.0.0-stage → 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/README.md +54 -2
- package/lib/host-api-types.d.ts +36 -0
- package/lib/host-api-types.js +13 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.js +1 -0
- package/lib/load-hg.d.ts +18 -0
- package/lib/load-hg.js +84 -0
- package/package.json +34 -3
package/README.md
CHANGED
|
@@ -1,3 +1,55 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @herazgo/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Typings and install helpers for the Herazgo embeddable script. The widget
|
|
4
|
+
runtime always loads from the Herazgo CDN (`static.herazgo.com/hg.js`) — this
|
|
5
|
+
package ships no widget code, so the snippet stays the canonical install.
|
|
6
|
+
|
|
7
|
+
## What's in it
|
|
8
|
+
|
|
9
|
+
- `HGGlobal` / `HGModuleApi` — the typed `window.HG` contract; a global
|
|
10
|
+
`Window.HG` declaration ships with the types.
|
|
11
|
+
- `loadHG({ key })` — injects the same snippet and resolves when the loader
|
|
12
|
+
marker (`HG.__herazgo`) or a requested module sub-API exists.
|
|
13
|
+
- `seedHG({ feedback: { hidden: true } })` — typed pre-seed for first-render
|
|
14
|
+
suppression; call before the snippet / `loadHG()`.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
Snippet-and-types only (no code import):
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
pnpm add @herazgo/sdk
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```jsonc
|
|
25
|
+
// tsconfig.json
|
|
26
|
+
"compilerOptions": { "types": ["@herazgo/sdk"] }
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Managed loading:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { loadHG } from "@herazgo/sdk";
|
|
33
|
+
await loadHG({ key: "hz-XXXXXXXXXX", module: "feedback" });
|
|
34
|
+
HG.feedback?.open(); // typed
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Suppressed launcher (before loading):
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { seedHG, loadHG } from "@herazgo/sdk";
|
|
41
|
+
seedHG({ feedback: { hidden: true } });
|
|
42
|
+
await loadHG({ key: "hz-XXXXXXXXXX", module: "feedback" });
|
|
43
|
+
HG.feedback?.show();
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Notes
|
|
47
|
+
|
|
48
|
+
- `loadHG` is idempotent; the loader itself dedupes double loads
|
|
49
|
+
(`window.__herazgoLoaded`). If a plain snippet is already present, tagging
|
|
50
|
+
it `data-hg-key` is optional — a race injects a second script that no-ops.
|
|
51
|
+
- `module: "feedback"` resolves when `HG.feedback` exists — enabled modules
|
|
52
|
+
only; a wrong or disabled module name times out (default 10s).
|
|
53
|
+
- The published contract is compiled against the loader's own sub-API
|
|
54
|
+
(`satisfies HGModuleApi` in the sdk repo's `src/host-api.ts`), so loader
|
|
55
|
+
drift fails the sdk repo's typecheck before release.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `window.HG` — the host-facing contract (`platform/tech-spec.md` § Embeddable
|
|
3
|
+
* Script). Canonical source for the typings customers install via
|
|
4
|
+
* `@herazgo/sdk` (this package); the loader compiles its own sub-API against
|
|
5
|
+
* `HGModuleApi` (`satisfies` in `src/host-api.ts` of the sdk repo), so a
|
|
6
|
+
* loader change that drifts from the published contract fails typecheck.
|
|
7
|
+
*
|
|
8
|
+
* One key per enabled module; a disabled module has no key at all. Hosts
|
|
9
|
+
* pre-seed `window.HG = { <module>: { hidden: true } }` before the snippet
|
|
10
|
+
* loads — through `seedHG()` rather than this type (the seed grammar is
|
|
11
|
+
* wider than the upgraded API).
|
|
12
|
+
*/
|
|
13
|
+
/** One module's sub-API on `HG.<module>`, exposed after the loader upgrade. */
|
|
14
|
+
export interface HGModuleApi {
|
|
15
|
+
/** Host-led hidden state (seed / `hide()`); presentation rules own the launcher display only. */
|
|
16
|
+
readonly hidden: boolean;
|
|
17
|
+
/** Live panel state; false while the module is unmounted. */
|
|
18
|
+
readonly opened: boolean;
|
|
19
|
+
/** Kill switch: unmounts instantly, mid-flight mounts included. */
|
|
20
|
+
hide(): void;
|
|
21
|
+
/** Re-mount (booting first when the module was seed-hidden). */
|
|
22
|
+
show(): void;
|
|
23
|
+
/** Open the panel: mounts if needed, ignoring rules and the hidden state. */
|
|
24
|
+
open(): void;
|
|
25
|
+
/** Close the panel; visibility returns to the rules/host state. */
|
|
26
|
+
close(): void;
|
|
27
|
+
/** Panel open/close events; returns the unsubscribe. */
|
|
28
|
+
on(type: "open" | "close", cb: () => void): () => void;
|
|
29
|
+
}
|
|
30
|
+
/** The upgraded host namespace (set once the snippet claims `window.HG`). */
|
|
31
|
+
export interface HGGlobal {
|
|
32
|
+
/** Marker the loader sets when it owns the name. */
|
|
33
|
+
__herazgo: true;
|
|
34
|
+
/** One entry per enabled product module (feedback today; extended as modules ship). */
|
|
35
|
+
feedback?: HGModuleApi;
|
|
36
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `window.HG` — the host-facing contract (`platform/tech-spec.md` § Embeddable
|
|
3
|
+
* Script). Canonical source for the typings customers install via
|
|
4
|
+
* `@herazgo/sdk` (this package); the loader compiles its own sub-API against
|
|
5
|
+
* `HGModuleApi` (`satisfies` in `src/host-api.ts` of the sdk repo), so a
|
|
6
|
+
* loader change that drifts from the published contract fails typecheck.
|
|
7
|
+
*
|
|
8
|
+
* One key per enabled module; a disabled module has no key at all. Hosts
|
|
9
|
+
* pre-seed `window.HG = { <module>: { hidden: true } }` before the snippet
|
|
10
|
+
* loads — through `seedHG()` rather than this type (the seed grammar is
|
|
11
|
+
* wider than the upgraded API).
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { loadHG, seedHG } from "./load-hg.js";
|
|
2
|
+
export type { HGSeedEntry, LoadHGOptions } from "./load-hg.js";
|
|
3
|
+
import type { HGGlobal } from "./host-api-types.js";
|
|
4
|
+
export type { HGGlobal, HGModuleApi } from "./host-api-types.js";
|
|
5
|
+
declare global {
|
|
6
|
+
interface Window {
|
|
7
|
+
/** Herazgo host API, set by the embeddable script (hg.js). */
|
|
8
|
+
HG?: HGGlobal | undefined;
|
|
9
|
+
}
|
|
10
|
+
}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { loadHG, seedHG } from "./load-hg.js";
|
package/lib/load-hg.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Pre-seed entry: first-render suppression before the snippet loads. */
|
|
2
|
+
export interface HGSeedEntry {
|
|
3
|
+
hidden?: boolean;
|
|
4
|
+
}
|
|
5
|
+
export interface LoadHGOptions {
|
|
6
|
+
/** Product public key from the studio snippet (`hz-…`). */
|
|
7
|
+
key: string;
|
|
8
|
+
/** Wait for this module's sub-API (e.g. "feedback"); default: the loader marker. */
|
|
9
|
+
module?: string;
|
|
10
|
+
/** Snippet URL override (dev/self-host); default: the prod CDN. */
|
|
11
|
+
scriptUrl?: string;
|
|
12
|
+
/** Ready-wait budget in ms; default 10000. Non-positive: check once, no wait. */
|
|
13
|
+
timeoutMs?: number;
|
|
14
|
+
}
|
|
15
|
+
/** Typed pre-seed for `window.HG` — call before the snippet / loadHG(). */
|
|
16
|
+
export declare function seedHG(modules: Record<string, HGSeedEntry>): void;
|
|
17
|
+
/** Injects the snippet for `key` and resolves when the widget is usable. */
|
|
18
|
+
export declare function loadHG(options: LoadHGOptions): Promise<void>;
|
package/lib/load-hg.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const DEFAULT_SCRIPT_URL = "https://static.herazgo.com/hg.js";
|
|
2
|
+
/** Mirrors the loader's key check (loader.ts main()). */
|
|
3
|
+
const KEY_PATTERN = /^[\w-]{3,64}$/;
|
|
4
|
+
/** Typed pre-seed for `window.HG` — call before the snippet / loadHG(). */
|
|
5
|
+
export function seedHG(modules) {
|
|
6
|
+
if (typeof window === "undefined") {
|
|
7
|
+
throw new Error("seedHG: browser-only; there is no window here");
|
|
8
|
+
}
|
|
9
|
+
const seed = {};
|
|
10
|
+
for (const [name, entry] of Object.entries(modules)) {
|
|
11
|
+
seed[name] = { hidden: entry.hidden === true };
|
|
12
|
+
}
|
|
13
|
+
window.HG = seed;
|
|
14
|
+
}
|
|
15
|
+
/** Injects the snippet for `key` and resolves when the widget is usable. */
|
|
16
|
+
export function loadHG(options) {
|
|
17
|
+
if (typeof window === "undefined") {
|
|
18
|
+
return Promise.reject(new Error("loadHG: browser-only; there is no window here"));
|
|
19
|
+
}
|
|
20
|
+
if (!KEY_PATTERN.test(options.key)) {
|
|
21
|
+
return Promise.reject(new Error(`loadHG: key "${options.key}" is not a usable product key (3-64 word chars)`));
|
|
22
|
+
}
|
|
23
|
+
const w = window;
|
|
24
|
+
const scriptUrl = options.scriptUrl ?? DEFAULT_SCRIPT_URL;
|
|
25
|
+
const timeoutMs = options.timeoutMs ?? 10000;
|
|
26
|
+
const module = options.module;
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
let settled = false;
|
|
29
|
+
let poll;
|
|
30
|
+
let deadline;
|
|
31
|
+
const finish = (err) => {
|
|
32
|
+
if (settled)
|
|
33
|
+
return;
|
|
34
|
+
settled = true;
|
|
35
|
+
if (poll != null)
|
|
36
|
+
clearInterval(poll);
|
|
37
|
+
if (deadline != null)
|
|
38
|
+
clearTimeout(deadline);
|
|
39
|
+
if (err != null)
|
|
40
|
+
reject(err);
|
|
41
|
+
else
|
|
42
|
+
resolve();
|
|
43
|
+
};
|
|
44
|
+
const ready = () => {
|
|
45
|
+
if (module != null) {
|
|
46
|
+
// Sub-APIs exist only for enabled modules after boot; a wrong or
|
|
47
|
+
// disabled module name times out (the error says which).
|
|
48
|
+
return w.HG != null && w.HG[module] != null;
|
|
49
|
+
}
|
|
50
|
+
return w.HG?.__herazgo === true;
|
|
51
|
+
};
|
|
52
|
+
if (ready())
|
|
53
|
+
return finish();
|
|
54
|
+
const src = `${scriptUrl}?key=${encodeURIComponent(options.key)}`;
|
|
55
|
+
// Idempotent per key: a snippet (with or without our marker attr) or an
|
|
56
|
+
// earlier loadHG() may already be loading; the loader dedupes via
|
|
57
|
+
// window.__herazgoLoaded regardless. A pre-existing tagged snippet shares
|
|
58
|
+
// its load error with this caller too.
|
|
59
|
+
const existing = document.querySelector(`script[data-hg-key="${options.key}"]`);
|
|
60
|
+
if (existing == null) {
|
|
61
|
+
const snippet = document.createElement("script");
|
|
62
|
+
snippet.src = src;
|
|
63
|
+
snippet.async = true;
|
|
64
|
+
snippet.dataset.hgKey = options.key;
|
|
65
|
+
snippet.addEventListener("error", () => {
|
|
66
|
+
finish(new Error(`loadHG: failed to load ${src} (blocked or unreachable)`));
|
|
67
|
+
});
|
|
68
|
+
document.head.appendChild(snippet);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
existing.addEventListener("error", () => {
|
|
72
|
+
finish(new Error(`loadHG: failed to load ${src} (blocked or unreachable)`));
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
poll = setInterval(() => {
|
|
76
|
+
if (ready())
|
|
77
|
+
finish();
|
|
78
|
+
}, 50);
|
|
79
|
+
deadline = setTimeout(() => {
|
|
80
|
+
finish(new Error(`loadHG: timed out after ${timeoutMs}ms waiting for ${module ?? "the loader"} — ` +
|
|
81
|
+
`check the key${module != null ? `, that "${module}" is enabled` : ", network reachability"}`));
|
|
82
|
+
}, Math.max(0, timeoutMs));
|
|
83
|
+
});
|
|
84
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@herazgo/sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Typings and install helpers for the Herazgo embeddable script (hg.js). The widget runtime always loads from the Herazgo CDN.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://gitlab.com/herazgo/sdk.git",
|
|
10
|
+
"directory": "packages/sdk"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"herazgo",
|
|
14
|
+
"feedback",
|
|
15
|
+
"widget",
|
|
16
|
+
"embeddable",
|
|
17
|
+
"sdk"
|
|
18
|
+
],
|
|
19
|
+
"main": "./lib/index.js",
|
|
20
|
+
"types": "./lib/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./lib/index.d.ts",
|
|
24
|
+
"default": "./lib/index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"lib"
|
|
29
|
+
],
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"typescript": "^7.0.2"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsc -p tsconfig.json",
|
|
35
|
+
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
36
|
+
}
|
|
6
37
|
}
|