@favish/staffbase-utils 0.4.0 → 0.5.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.
Files changed (39) hide show
  1. package/dist/renderWidgets-CeIczubt.mjs +81 -0
  2. package/dist/renderWidgets-CeIczubt.mjs.map +1 -0
  3. package/dist/renderWidgets-EbJq9Wn6.js +2 -0
  4. package/dist/renderWidgets-EbJq9Wn6.js.map +1 -0
  5. package/dist/src/types/widgets/RenderWidgetsOptions.d.ts +21 -0
  6. package/dist/src/types/widgets/RenderWidgetsOptions.d.ts.map +1 -0
  7. package/dist/src/types/widgets/RenderWidgetsResult.d.ts +13 -0
  8. package/dist/src/types/widgets/RenderWidgetsResult.d.ts.map +1 -0
  9. package/dist/src/types/widgets/StaffbaseWidgetManagerConstructor.d.ts +9 -0
  10. package/dist/src/types/widgets/StaffbaseWidgetManagerConstructor.d.ts.map +1 -0
  11. package/dist/src/types/widgets/StaffbaseWidgetManagerPrototype.d.ts +11 -0
  12. package/dist/src/types/widgets/StaffbaseWidgetManagerPrototype.d.ts.map +1 -0
  13. package/dist/src/types/widgets/UseRenderWidgetsOptions.d.ts +16 -0
  14. package/dist/src/types/widgets/UseRenderWidgetsOptions.d.ts.map +1 -0
  15. package/dist/src/widgets/defaultWidgetOnError.d.ts +10 -0
  16. package/dist/src/widgets/defaultWidgetOnError.d.ts.map +1 -0
  17. package/dist/src/widgets/getWidgetManagerConstructor.d.ts +8 -0
  18. package/dist/src/widgets/getWidgetManagerConstructor.d.ts.map +1 -0
  19. package/dist/src/widgets/getWidgetManagerPrototype.d.ts +8 -0
  20. package/dist/src/widgets/getWidgetManagerPrototype.d.ts.map +1 -0
  21. package/dist/src/widgets/hasRequiredWidgetManagerMethods.d.ts +12 -0
  22. package/dist/src/widgets/hasRequiredWidgetManagerMethods.d.ts.map +1 -0
  23. package/dist/src/widgets/index.d.ts +2 -0
  24. package/dist/src/widgets/index.d.ts.map +1 -0
  25. package/dist/src/widgets/isKnownStaffbaseRenderError.d.ts +9 -0
  26. package/dist/src/widgets/isKnownStaffbaseRenderError.d.ts.map +1 -0
  27. package/dist/src/widgets/react/index.d.ts +2 -0
  28. package/dist/src/widgets/react/index.d.ts.map +1 -0
  29. package/dist/src/widgets/react/useRenderWidgets.d.ts +14 -0
  30. package/dist/src/widgets/react/useRenderWidgets.d.ts.map +1 -0
  31. package/dist/src/widgets/renderWidgets.d.ts +16 -0
  32. package/dist/src/widgets/renderWidgets.d.ts.map +1 -0
  33. package/dist/widgets/react.cjs.js +2 -0
  34. package/dist/widgets/react.cjs.js.map +1 -0
  35. package/dist/widgets/react.es.mjs +28 -0
  36. package/dist/widgets/react.es.mjs.map +1 -0
  37. package/dist/widgets.cjs.js +1 -0
  38. package/dist/widgets.es.mjs +2 -0
  39. package/package.json +12 -1
@@ -0,0 +1,81 @@
1
+ //#region src/widgets/defaultWidgetOnError.ts
2
+ var e = (e, t) => {
3
+ t === "manager-unavailable" && console.warn("[staffbase-utils] Staffbase widget manager unavailable; embedded widgets were not rendered.", e);
4
+ }, t = () => {
5
+ if (typeof window > "u") return null;
6
+ let e = window.staffbase?.content?.widgetMgr;
7
+ return typeof e == "function" ? e : null;
8
+ }, n = () => typeof window > "u" ? null : window.staffbase?.content?.widgetMgr?.prototype ?? null, r = (e) => typeof e._extractWidgets == "function" && typeof e._renderWidget == "function", i = (e) => e instanceof TypeError && (e.message?.includes("each") || e.message?.includes("undefined is not an object")), a = Promise.resolve(), o = /* @__PURE__ */ new WeakMap(), s = !1, c = async (e, a, c, l, u, d) => {
9
+ let f = 0, p = !1;
10
+ for (; f < l;) {
11
+ if (f++, o.get(a) !== c) return {
12
+ ok: !1,
13
+ reason: "cancelled"
14
+ };
15
+ if (typeof e.querySelectorAll != "function") return {
16
+ ok: !1,
17
+ reason: "no-container"
18
+ };
19
+ let s = t();
20
+ if (s) {
21
+ p = !0;
22
+ try {
23
+ let t = new s(void 0, !1);
24
+ if (typeof t.render == "function") return await t.render(e), {
25
+ ok: !0,
26
+ rendered: 0
27
+ };
28
+ } catch {}
29
+ }
30
+ let m = n();
31
+ if (m && r(m)) {
32
+ p = !0, Array.isArray(m._widgets) || (m._widgets = []);
33
+ let t;
34
+ try {
35
+ t = m._extractWidgets(e);
36
+ } catch (e) {
37
+ d(e, "extract"), t = [];
38
+ }
39
+ if (t.length > 0) {
40
+ let n = 0;
41
+ for (let r of t) {
42
+ if (o.get(a) !== c) return {
43
+ ok: !1,
44
+ reason: "cancelled"
45
+ };
46
+ try {
47
+ m._renderWidget.call(m, e, r), n++;
48
+ } catch (e) {
49
+ i(e) || d(e, "render-widget");
50
+ }
51
+ }
52
+ return {
53
+ ok: !0,
54
+ rendered: n
55
+ };
56
+ }
57
+ }
58
+ if (f >= l) break;
59
+ await new Promise((e) => setTimeout(e, u));
60
+ }
61
+ return p ? {
62
+ ok: !1,
63
+ reason: "no-widgets"
64
+ } : (s || (s = !0, d(/* @__PURE__ */ Error("Staffbase widget manager unavailable after retries."), "manager-unavailable")), {
65
+ ok: !1,
66
+ reason: "manager-unavailable"
67
+ });
68
+ }, l = (t, n = {}) => {
69
+ if (!t) return Promise.resolve({
70
+ ok: !1,
71
+ reason: "no-container"
72
+ });
73
+ let { maxRetries: r = 10, retryDelay: i = 300, onError: s = e, cancelKey: l = t } = n, u = (o.get(l) ?? 0) + 1;
74
+ o.set(l, u);
75
+ let d = a.then(() => c(t, l, u, r, i, s));
76
+ return a = d.then(() => void 0, () => void 0), d;
77
+ };
78
+ //#endregion
79
+ export { l as t };
80
+
81
+ //# sourceMappingURL=renderWidgets-CeIczubt.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderWidgets-CeIczubt.mjs","names":[],"sources":["../src/widgets/defaultWidgetOnError.ts","../src/widgets/getWidgetManagerConstructor.ts","../src/widgets/getWidgetManagerPrototype.ts","../src/widgets/hasRequiredWidgetManagerMethods.ts","../src/widgets/isKnownStaffbaseRenderError.ts","../src/widgets/renderWidgets.ts"],"sourcesContent":["/**\n * Default onError for renderWidgets: warns only on the critical\n * 'manager-unavailable' context so a platform regression stays visible in logs\n * without spamming per-widget noise. Consumers can pass their own onError.\n * @param {unknown} error - The swallowed error.\n * @param {string} context - Where it happened.\n * @returns {void}\n */\nexport const defaultWidgetOnError = (error: unknown, context: string): void => {\n if (context === 'manager-unavailable') {\n console.warn(\n '[staffbase-utils] Staffbase widget manager unavailable; embedded widgets were not rendered.',\n error,\n )\n }\n}\n","import type { StaffbaseWidgetManagerConstructor } from '../types/widgets/StaffbaseWidgetManagerConstructor'\n\n/**\n * Resolves the host widget-manager constructor from the global Staffbase object,\n * or null when it is not a function (older runtimes / platform regression).\n * @returns {StaffbaseWidgetManagerConstructor | null} The constructor or null.\n */\nexport const getWidgetManagerConstructor =\n (): StaffbaseWidgetManagerConstructor | null => {\n if (typeof window === 'undefined') return null\n const ctor = (\n window as unknown as {\n staffbase?: { content?: { widgetMgr?: unknown } }\n }\n ).staffbase?.content?.widgetMgr\n return typeof ctor === 'function'\n ? (ctor as StaffbaseWidgetManagerConstructor)\n : null\n }\n","import type { StaffbaseWidgetManagerPrototype } from '../types/widgets/StaffbaseWidgetManagerPrototype'\n\n/**\n * Resolves the host widget-manager prototype (the private fallback API) from the\n * global Staffbase object, or null when it is unavailable.\n * @returns {StaffbaseWidgetManagerPrototype | null} The prototype or null.\n */\nexport const getWidgetManagerPrototype =\n (): StaffbaseWidgetManagerPrototype | null => {\n if (typeof window === 'undefined') return null\n const proto = (\n window as unknown as {\n staffbase?: {\n content?: {\n widgetMgr?: { prototype?: StaffbaseWidgetManagerPrototype }\n }\n }\n }\n ).staffbase?.content?.widgetMgr?.prototype\n return proto ?? null\n }\n","import type { StaffbaseWidgetManagerPrototype } from '../types/widgets/StaffbaseWidgetManagerPrototype'\n\n/**\n * Type guard asserting the widget-manager prototype exposes the methods the\n * prototype render path needs.\n * @param {StaffbaseWidgetManagerPrototype} widgetMgr - The resolved prototype.\n * @returns {boolean} True when both _extractWidgets and _renderWidget are functions.\n */\nexport const hasRequiredWidgetManagerMethods = (\n widgetMgr: StaffbaseWidgetManagerPrototype,\n): widgetMgr is StaffbaseWidgetManagerPrototype & {\n _extractWidgets: (container: HTMLElement) => unknown[]\n _renderWidget: (\n this: StaffbaseWidgetManagerPrototype,\n container: HTMLElement,\n widget: unknown,\n ) => void\n} =>\n typeof widgetMgr._extractWidgets === 'function' &&\n typeof widgetMgr._renderWidget === 'function'\n","/**\n * Detects known, benign internal Staffbase errors thrown by `_renderWidget` for\n * individual widgets, so they can be swallowed without aborting the batch.\n * Promoted from unacknowledged-bulletins (`each` / `undefined is not an object`).\n * @param {unknown} error - The thrown value.\n * @returns {boolean} True when the error is a known internal render error.\n */\nexport const isKnownStaffbaseRenderError = (error: unknown): boolean =>\n error instanceof TypeError &&\n (error.message?.includes('each') ||\n error.message?.includes('undefined is not an object'))\n","import type { RenderWidgetsOptions } from '../types/widgets/RenderWidgetsOptions'\nimport type { RenderWidgetsResult } from '../types/widgets/RenderWidgetsResult'\n\nimport { defaultWidgetOnError } from './defaultWidgetOnError'\nimport { getWidgetManagerConstructor } from './getWidgetManagerConstructor'\nimport { getWidgetManagerPrototype } from './getWidgetManagerPrototype'\nimport { hasRequiredWidgetManagerMethods } from './hasRequiredWidgetManagerMethods'\nimport { isKnownStaffbaseRenderError } from './isKnownStaffbaseRenderError'\n\n// Global serialization chain: all renders run one at a time so they never race\n// on the host's shared `_widgets` array (unacknowledged-bulletins' lock, made\n// queue-based). Per-cancelKey run ids drop superseded renders (alerts' WeakMap\n// cancellation; no AbortController, for old webviews). warn-once keeps a missing\n// manager observable without spam (alerts).\nlet queue: Promise<unknown> = Promise.resolve()\nconst cancelTokens = new WeakMap<object, number>()\nlet hasWarnedManagerUnavailable = false\n\n/**\n * Runs the retry loop for a single render: constructor path first, prototype\n * fallback, with cancellation checks and known-error swallowing.\n * @param {HTMLElement} container - The container to render widgets into.\n * @param {object} cancelKey - The cancellation key for this render.\n * @param {number} runId - This render's run id for the cancel key.\n * @param {number} maxRetries - Maximum render attempts.\n * @param {number} retryDelay - Delay between attempts, in milliseconds.\n * @param {(error: unknown, context: string) => void} onError - Error reporter.\n * @returns {Promise<RenderWidgetsResult>} The typed render result.\n */\nconst executeRender = async (\n container: HTMLElement,\n cancelKey: object,\n runId: number,\n maxRetries: number,\n retryDelay: number,\n onError: (error: unknown, context: string) => void,\n): Promise<RenderWidgetsResult> => {\n let attempts = 0\n let managerSeen = false\n\n while (attempts < maxRetries) {\n attempts++\n if (cancelTokens.get(cancelKey) !== runId)\n return { ok: false, reason: 'cancelled' }\n if (typeof container.querySelectorAll !== 'function') {\n return { ok: false, reason: 'no-container' }\n }\n\n // Prefer Staffbase's real widget manager (closest to host behavior).\n const ctor = getWidgetManagerConstructor()\n if (ctor) {\n managerSeen = true\n try {\n const instance = new ctor(undefined, false)\n if (typeof instance.render === 'function') {\n await instance.render(container)\n return { ok: true, rendered: 0 }\n }\n } catch {\n // Fall back to the private prototype path below.\n }\n }\n\n const proto = getWidgetManagerPrototype()\n if (proto && hasRequiredWidgetManagerMethods(proto)) {\n managerSeen = true\n if (!Array.isArray(proto._widgets)) proto._widgets = []\n\n let widgets: unknown[]\n try {\n widgets = proto._extractWidgets(container)\n } catch (error) {\n onError(error, 'extract')\n widgets = []\n }\n\n if (widgets.length > 0) {\n let rendered = 0\n for (const widget of widgets) {\n if (cancelTokens.get(cancelKey) !== runId) {\n return { ok: false, reason: 'cancelled' }\n }\n try {\n proto._renderWidget.call(proto, container, widget)\n rendered++\n } catch (error) {\n if (!isKnownStaffbaseRenderError(error))\n onError(error, 'render-widget')\n }\n }\n return { ok: true, rendered }\n }\n }\n\n if (attempts >= maxRetries) break\n await new Promise((resolve) => setTimeout(resolve, retryDelay))\n }\n\n if (!managerSeen) {\n if (!hasWarnedManagerUnavailable) {\n hasWarnedManagerUnavailable = true\n onError(\n new Error('Staffbase widget manager unavailable after retries.'),\n 'manager-unavailable',\n )\n }\n return { ok: false, reason: 'manager-unavailable' }\n }\n return { ok: false, reason: 'no-widgets' }\n}\n\n/**\n * Renders the widgets embedded in `container` using the host's private widget\n * manager. Superset of the four widget services: constructor path first with a\n * prototype fallback, a global queue that serializes renders, per-cancelKey\n * cancellation of superseded renders, configurable retries, swallowing of known\n * internal Staffbase errors, and a once-per-session warning when the manager is\n * missing. Returns a typed result so callers can react (e.g. mark a\n * data-widget-render-error attribute).\n * @param {HTMLElement | null} container - The element whose embedded widgets are rendered.\n * @param {RenderWidgetsOptions} options - Retry, error and cancellation options.\n * @returns {Promise<RenderWidgetsResult>} The typed render result.\n */\nexport const renderWidgets = (\n container: HTMLElement | null,\n options: RenderWidgetsOptions = {},\n): Promise<RenderWidgetsResult> => {\n if (!container) return Promise.resolve({ ok: false, reason: 'no-container' })\n\n const {\n maxRetries = 10,\n retryDelay = 300,\n onError = defaultWidgetOnError,\n cancelKey = container,\n } = options\n\n // Bump the run id synchronously so any older in-flight/queued render for the\n // same key sees itself as superseded.\n const runId = (cancelTokens.get(cancelKey) ?? 0) + 1\n cancelTokens.set(cancelKey, runId)\n\n const run = queue.then(() =>\n executeRender(container, cancelKey, runId, maxRetries, retryDelay, onError),\n )\n // Keep the queue chain alive regardless of individual outcomes.\n queue = run.then(\n () => undefined,\n () => undefined,\n )\n return run\n}\n"],"mappings":";AAQA,IAAa,KAAwB,GAAgB,MAA0B;CAC7E,AAAI,MAAY,yBACd,QAAQ,KACN,+FACA,CACF;AAEJ,GCRa,UACqC;CAC9C,IAAI,OAAO,SAAW,KAAa,OAAO;CAC1C,IAAM,IACJ,OAGA,WAAW,SAAS;CACtB,OAAO,OAAO,KAAS,aAClB,IACD;AACN,GCXW,UAEL,OAAO,SAAW,MAAoB,OAExC,OAOA,WAAW,SAAS,WAAW,aACjB,MCXP,KACX,MASA,OAAO,EAAU,mBAAoB,cACrC,OAAO,EAAU,iBAAkB,YCZxB,KAA+B,MAC1C,aAAiB,cAChB,EAAM,SAAS,SAAS,MAAM,KAC7B,EAAM,SAAS,SAAS,4BAA4B,ICIpD,IAA0B,QAAQ,QAAQ,GACxC,oBAAe,IAAI,QAAwB,GAC7C,IAA8B,IAa5B,IAAgB,OACpB,GACA,GACA,GACA,GACA,GACA,MACiC;CACjC,IAAI,IAAW,GACX,IAAc;CAElB,OAAO,IAAW,IAAY;EAE5B,IADA,KACI,EAAa,IAAI,CAAS,MAAM,GAClC,OAAO;GAAE,IAAI;GAAO,QAAQ;EAAY;EAC1C,IAAI,OAAO,EAAU,oBAAqB,YACxC,OAAO;GAAE,IAAI;GAAO,QAAQ;EAAe;EAI7C,IAAM,IAAO,EAA4B;EACzC,IAAI,GAAM;GACR,IAAc;GACd,IAAI;IACF,IAAM,IAAW,IAAI,EAAK,KAAA,GAAW,EAAK;IAC1C,IAAI,OAAO,EAAS,UAAW,YAE7B,OADA,MAAM,EAAS,OAAO,CAAS,GACxB;KAAE,IAAI;KAAM,UAAU;IAAE;GAEnC,QAAQ,CAER;EACF;EAEA,IAAM,IAAQ,EAA0B;EACxC,IAAI,KAAS,EAAgC,CAAK,GAAG;GAEnD,AADA,IAAc,IACT,MAAM,QAAQ,EAAM,QAAQ,MAAG,EAAM,WAAW,CAAC;GAEtD,IAAI;GACJ,IAAI;IACF,IAAU,EAAM,gBAAgB,CAAS;GAC3C,SAAS,GAAO;IAEd,AADA,EAAQ,GAAO,SAAS,GACxB,IAAU,CAAC;GACb;GAEA,IAAI,EAAQ,SAAS,GAAG;IACtB,IAAI,IAAW;IACf,KAAK,IAAM,KAAU,GAAS;KAC5B,IAAI,EAAa,IAAI,CAAS,MAAM,GAClC,OAAO;MAAE,IAAI;MAAO,QAAQ;KAAY;KAE1C,IAAI;MAEF,AADA,EAAM,cAAc,KAAK,GAAO,GAAW,CAAM,GACjD;KACF,SAAS,GAAO;MACd,AAAK,EAA4B,CAAK,KACpC,EAAQ,GAAO,eAAe;KAClC;IACF;IACA,OAAO;KAAE,IAAI;KAAM;IAAS;GAC9B;EACF;EAEA,IAAI,KAAY,GAAY;EAC5B,MAAM,IAAI,SAAS,MAAY,WAAW,GAAS,CAAU,CAAC;CAChE;CAYA,OAVK,IAUE;EAAE,IAAI;EAAO,QAAQ;CAAa,KATlC,MACH,IAA8B,IAC9B,EACE,gBAAI,MAAM,qDAAqD,GAC/D,qBACF,IAEK;EAAE,IAAI;EAAO,QAAQ;CAAsB;AAGtD,GAca,KACX,GACA,IAAgC,CAAC,MACA;CACjC,IAAI,CAAC,GAAW,OAAO,QAAQ,QAAQ;EAAE,IAAI;EAAO,QAAQ;CAAe,CAAC;CAE5E,IAAM,EACJ,gBAAa,IACb,gBAAa,KACb,aAAU,GACV,eAAY,MACV,GAIE,KAAS,EAAa,IAAI,CAAS,KAAK,KAAK;CACnD,EAAa,IAAI,GAAW,CAAK;CAEjC,IAAM,IAAM,EAAM,WAChB,EAAc,GAAW,GAAW,GAAO,GAAY,GAAY,CAAO,CAC5E;CAMA,OAJA,IAAQ,EAAI,WACJ,KAAA,SACA,KAAA,CACR,GACO;AACT"}
@@ -0,0 +1,2 @@
1
+ var e=(e,t)=>{t===`manager-unavailable`&&console.warn(`[staffbase-utils] Staffbase widget manager unavailable; embedded widgets were not rendered.`,e)},t=()=>{if(typeof window>`u`)return null;let e=window.staffbase?.content?.widgetMgr;return typeof e==`function`?e:null},n=()=>typeof window>`u`?null:window.staffbase?.content?.widgetMgr?.prototype??null,r=e=>typeof e._extractWidgets==`function`&&typeof e._renderWidget==`function`,i=e=>e instanceof TypeError&&(e.message?.includes(`each`)||e.message?.includes(`undefined is not an object`)),a=Promise.resolve(),o=new WeakMap,s=!1,c=async(e,a,c,l,u,d)=>{let f=0,p=!1;for(;f<l;){if(f++,o.get(a)!==c)return{ok:!1,reason:`cancelled`};if(typeof e.querySelectorAll!=`function`)return{ok:!1,reason:`no-container`};let s=t();if(s){p=!0;try{let t=new s(void 0,!1);if(typeof t.render==`function`)return await t.render(e),{ok:!0,rendered:0}}catch{}}let m=n();if(m&&r(m)){p=!0,Array.isArray(m._widgets)||(m._widgets=[]);let t;try{t=m._extractWidgets(e)}catch(e){d(e,`extract`),t=[]}if(t.length>0){let n=0;for(let r of t){if(o.get(a)!==c)return{ok:!1,reason:`cancelled`};try{m._renderWidget.call(m,e,r),n++}catch(e){i(e)||d(e,`render-widget`)}}return{ok:!0,rendered:n}}}if(f>=l)break;await new Promise(e=>setTimeout(e,u))}return p?{ok:!1,reason:`no-widgets`}:(s||(s=!0,d(Error(`Staffbase widget manager unavailable after retries.`),`manager-unavailable`)),{ok:!1,reason:`manager-unavailable`})},l=(t,n={})=>{if(!t)return Promise.resolve({ok:!1,reason:`no-container`});let{maxRetries:r=10,retryDelay:i=300,onError:s=e,cancelKey:l=t}=n,u=(o.get(l)??0)+1;o.set(l,u);let d=a.then(()=>c(t,l,u,r,i,s));return a=d.then(()=>void 0,()=>void 0),d};Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return l}});
2
+ //# sourceMappingURL=renderWidgets-EbJq9Wn6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderWidgets-EbJq9Wn6.js","names":[],"sources":["../src/widgets/defaultWidgetOnError.ts","../src/widgets/getWidgetManagerConstructor.ts","../src/widgets/getWidgetManagerPrototype.ts","../src/widgets/hasRequiredWidgetManagerMethods.ts","../src/widgets/isKnownStaffbaseRenderError.ts","../src/widgets/renderWidgets.ts"],"sourcesContent":["/**\n * Default onError for renderWidgets: warns only on the critical\n * 'manager-unavailable' context so a platform regression stays visible in logs\n * without spamming per-widget noise. Consumers can pass their own onError.\n * @param {unknown} error - The swallowed error.\n * @param {string} context - Where it happened.\n * @returns {void}\n */\nexport const defaultWidgetOnError = (error: unknown, context: string): void => {\n if (context === 'manager-unavailable') {\n console.warn(\n '[staffbase-utils] Staffbase widget manager unavailable; embedded widgets were not rendered.',\n error,\n )\n }\n}\n","import type { StaffbaseWidgetManagerConstructor } from '../types/widgets/StaffbaseWidgetManagerConstructor'\n\n/**\n * Resolves the host widget-manager constructor from the global Staffbase object,\n * or null when it is not a function (older runtimes / platform regression).\n * @returns {StaffbaseWidgetManagerConstructor | null} The constructor or null.\n */\nexport const getWidgetManagerConstructor =\n (): StaffbaseWidgetManagerConstructor | null => {\n if (typeof window === 'undefined') return null\n const ctor = (\n window as unknown as {\n staffbase?: { content?: { widgetMgr?: unknown } }\n }\n ).staffbase?.content?.widgetMgr\n return typeof ctor === 'function'\n ? (ctor as StaffbaseWidgetManagerConstructor)\n : null\n }\n","import type { StaffbaseWidgetManagerPrototype } from '../types/widgets/StaffbaseWidgetManagerPrototype'\n\n/**\n * Resolves the host widget-manager prototype (the private fallback API) from the\n * global Staffbase object, or null when it is unavailable.\n * @returns {StaffbaseWidgetManagerPrototype | null} The prototype or null.\n */\nexport const getWidgetManagerPrototype =\n (): StaffbaseWidgetManagerPrototype | null => {\n if (typeof window === 'undefined') return null\n const proto = (\n window as unknown as {\n staffbase?: {\n content?: {\n widgetMgr?: { prototype?: StaffbaseWidgetManagerPrototype }\n }\n }\n }\n ).staffbase?.content?.widgetMgr?.prototype\n return proto ?? null\n }\n","import type { StaffbaseWidgetManagerPrototype } from '../types/widgets/StaffbaseWidgetManagerPrototype'\n\n/**\n * Type guard asserting the widget-manager prototype exposes the methods the\n * prototype render path needs.\n * @param {StaffbaseWidgetManagerPrototype} widgetMgr - The resolved prototype.\n * @returns {boolean} True when both _extractWidgets and _renderWidget are functions.\n */\nexport const hasRequiredWidgetManagerMethods = (\n widgetMgr: StaffbaseWidgetManagerPrototype,\n): widgetMgr is StaffbaseWidgetManagerPrototype & {\n _extractWidgets: (container: HTMLElement) => unknown[]\n _renderWidget: (\n this: StaffbaseWidgetManagerPrototype,\n container: HTMLElement,\n widget: unknown,\n ) => void\n} =>\n typeof widgetMgr._extractWidgets === 'function' &&\n typeof widgetMgr._renderWidget === 'function'\n","/**\n * Detects known, benign internal Staffbase errors thrown by `_renderWidget` for\n * individual widgets, so they can be swallowed without aborting the batch.\n * Promoted from unacknowledged-bulletins (`each` / `undefined is not an object`).\n * @param {unknown} error - The thrown value.\n * @returns {boolean} True when the error is a known internal render error.\n */\nexport const isKnownStaffbaseRenderError = (error: unknown): boolean =>\n error instanceof TypeError &&\n (error.message?.includes('each') ||\n error.message?.includes('undefined is not an object'))\n","import type { RenderWidgetsOptions } from '../types/widgets/RenderWidgetsOptions'\nimport type { RenderWidgetsResult } from '../types/widgets/RenderWidgetsResult'\n\nimport { defaultWidgetOnError } from './defaultWidgetOnError'\nimport { getWidgetManagerConstructor } from './getWidgetManagerConstructor'\nimport { getWidgetManagerPrototype } from './getWidgetManagerPrototype'\nimport { hasRequiredWidgetManagerMethods } from './hasRequiredWidgetManagerMethods'\nimport { isKnownStaffbaseRenderError } from './isKnownStaffbaseRenderError'\n\n// Global serialization chain: all renders run one at a time so they never race\n// on the host's shared `_widgets` array (unacknowledged-bulletins' lock, made\n// queue-based). Per-cancelKey run ids drop superseded renders (alerts' WeakMap\n// cancellation; no AbortController, for old webviews). warn-once keeps a missing\n// manager observable without spam (alerts).\nlet queue: Promise<unknown> = Promise.resolve()\nconst cancelTokens = new WeakMap<object, number>()\nlet hasWarnedManagerUnavailable = false\n\n/**\n * Runs the retry loop for a single render: constructor path first, prototype\n * fallback, with cancellation checks and known-error swallowing.\n * @param {HTMLElement} container - The container to render widgets into.\n * @param {object} cancelKey - The cancellation key for this render.\n * @param {number} runId - This render's run id for the cancel key.\n * @param {number} maxRetries - Maximum render attempts.\n * @param {number} retryDelay - Delay between attempts, in milliseconds.\n * @param {(error: unknown, context: string) => void} onError - Error reporter.\n * @returns {Promise<RenderWidgetsResult>} The typed render result.\n */\nconst executeRender = async (\n container: HTMLElement,\n cancelKey: object,\n runId: number,\n maxRetries: number,\n retryDelay: number,\n onError: (error: unknown, context: string) => void,\n): Promise<RenderWidgetsResult> => {\n let attempts = 0\n let managerSeen = false\n\n while (attempts < maxRetries) {\n attempts++\n if (cancelTokens.get(cancelKey) !== runId)\n return { ok: false, reason: 'cancelled' }\n if (typeof container.querySelectorAll !== 'function') {\n return { ok: false, reason: 'no-container' }\n }\n\n // Prefer Staffbase's real widget manager (closest to host behavior).\n const ctor = getWidgetManagerConstructor()\n if (ctor) {\n managerSeen = true\n try {\n const instance = new ctor(undefined, false)\n if (typeof instance.render === 'function') {\n await instance.render(container)\n return { ok: true, rendered: 0 }\n }\n } catch {\n // Fall back to the private prototype path below.\n }\n }\n\n const proto = getWidgetManagerPrototype()\n if (proto && hasRequiredWidgetManagerMethods(proto)) {\n managerSeen = true\n if (!Array.isArray(proto._widgets)) proto._widgets = []\n\n let widgets: unknown[]\n try {\n widgets = proto._extractWidgets(container)\n } catch (error) {\n onError(error, 'extract')\n widgets = []\n }\n\n if (widgets.length > 0) {\n let rendered = 0\n for (const widget of widgets) {\n if (cancelTokens.get(cancelKey) !== runId) {\n return { ok: false, reason: 'cancelled' }\n }\n try {\n proto._renderWidget.call(proto, container, widget)\n rendered++\n } catch (error) {\n if (!isKnownStaffbaseRenderError(error))\n onError(error, 'render-widget')\n }\n }\n return { ok: true, rendered }\n }\n }\n\n if (attempts >= maxRetries) break\n await new Promise((resolve) => setTimeout(resolve, retryDelay))\n }\n\n if (!managerSeen) {\n if (!hasWarnedManagerUnavailable) {\n hasWarnedManagerUnavailable = true\n onError(\n new Error('Staffbase widget manager unavailable after retries.'),\n 'manager-unavailable',\n )\n }\n return { ok: false, reason: 'manager-unavailable' }\n }\n return { ok: false, reason: 'no-widgets' }\n}\n\n/**\n * Renders the widgets embedded in `container` using the host's private widget\n * manager. Superset of the four widget services: constructor path first with a\n * prototype fallback, a global queue that serializes renders, per-cancelKey\n * cancellation of superseded renders, configurable retries, swallowing of known\n * internal Staffbase errors, and a once-per-session warning when the manager is\n * missing. Returns a typed result so callers can react (e.g. mark a\n * data-widget-render-error attribute).\n * @param {HTMLElement | null} container - The element whose embedded widgets are rendered.\n * @param {RenderWidgetsOptions} options - Retry, error and cancellation options.\n * @returns {Promise<RenderWidgetsResult>} The typed render result.\n */\nexport const renderWidgets = (\n container: HTMLElement | null,\n options: RenderWidgetsOptions = {},\n): Promise<RenderWidgetsResult> => {\n if (!container) return Promise.resolve({ ok: false, reason: 'no-container' })\n\n const {\n maxRetries = 10,\n retryDelay = 300,\n onError = defaultWidgetOnError,\n cancelKey = container,\n } = options\n\n // Bump the run id synchronously so any older in-flight/queued render for the\n // same key sees itself as superseded.\n const runId = (cancelTokens.get(cancelKey) ?? 0) + 1\n cancelTokens.set(cancelKey, runId)\n\n const run = queue.then(() =>\n executeRender(container, cancelKey, runId, maxRetries, retryDelay, onError),\n )\n // Keep the queue chain alive regardless of individual outcomes.\n queue = run.then(\n () => undefined,\n () => undefined,\n )\n return run\n}\n"],"mappings":"AAQA,IAAa,GAAwB,EAAgB,IAA0B,CACzE,IAAY,uBACd,QAAQ,KACN,8FACA,CACF,CAEJ,ECRa,MACqC,CAC9C,GAAI,OAAO,OAAW,IAAa,OAAO,KAC1C,IAAM,EACJ,OAGA,WAAW,SAAS,UACtB,OAAO,OAAO,GAAS,WAClB,EACD,IACN,ECXW,MAEL,OAAO,OAAW,IAAoB,KAExC,OAOA,WAAW,SAAS,WAAW,WACjB,KCXP,EACX,GASA,OAAO,EAAU,iBAAoB,YACrC,OAAO,EAAU,eAAkB,WCZxB,EAA+B,GAC1C,aAAiB,YAChB,EAAM,SAAS,SAAS,MAAM,GAC7B,EAAM,SAAS,SAAS,4BAA4B,GCIpD,EAA0B,QAAQ,QAAQ,EACxC,EAAe,IAAI,QACrB,EAA8B,GAa5B,EAAgB,MACpB,EACA,EACA,EACA,EACA,EACA,IACiC,CACjC,IAAI,EAAW,EACX,EAAc,GAElB,KAAO,EAAW,GAAY,CAE5B,GADA,IACI,EAAa,IAAI,CAAS,IAAM,EAClC,MAAO,CAAE,GAAI,GAAO,OAAQ,WAAY,EAC1C,GAAI,OAAO,EAAU,kBAAqB,WACxC,MAAO,CAAE,GAAI,GAAO,OAAQ,cAAe,EAI7C,IAAM,EAAO,EAA4B,EACzC,GAAI,EAAM,CACR,EAAc,GACd,GAAI,CACF,IAAM,EAAW,IAAI,EAAK,IAAA,GAAW,EAAK,EAC1C,GAAI,OAAO,EAAS,QAAW,WAE7B,OADA,MAAM,EAAS,OAAO,CAAS,EACxB,CAAE,GAAI,GAAM,SAAU,CAAE,CAEnC,MAAQ,CAER,CACF,CAEA,IAAM,EAAQ,EAA0B,EACxC,GAAI,GAAS,EAAgC,CAAK,EAAG,CACnD,EAAc,GACT,MAAM,QAAQ,EAAM,QAAQ,IAAG,EAAM,SAAW,CAAC,GAEtD,IAAI,EACJ,GAAI,CACF,EAAU,EAAM,gBAAgB,CAAS,CAC3C,OAAS,EAAO,CACd,EAAQ,EAAO,SAAS,EACxB,EAAU,CAAC,CACb,CAEA,GAAI,EAAQ,OAAS,EAAG,CACtB,IAAI,EAAW,EACf,IAAK,IAAM,KAAU,EAAS,CAC5B,GAAI,EAAa,IAAI,CAAS,IAAM,EAClC,MAAO,CAAE,GAAI,GAAO,OAAQ,WAAY,EAE1C,GAAI,CACF,EAAM,cAAc,KAAK,EAAO,EAAW,CAAM,EACjD,GACF,OAAS,EAAO,CACT,EAA4B,CAAK,GACpC,EAAQ,EAAO,eAAe,CAClC,CACF,CACA,MAAO,CAAE,GAAI,GAAM,UAAS,CAC9B,CACF,CAEA,GAAI,GAAY,EAAY,MAC5B,MAAM,IAAI,QAAS,GAAY,WAAW,EAAS,CAAU,CAAC,CAChE,CAYA,OAVK,EAUE,CAAE,GAAI,GAAO,OAAQ,YAAa,GATlC,IACH,EAA8B,GAC9B,EACM,MAAM,qDAAqD,EAC/D,qBACF,GAEK,CAAE,GAAI,GAAO,OAAQ,qBAAsB,EAGtD,EAca,GACX,EACA,EAAgC,CAAC,IACA,CACjC,GAAI,CAAC,EAAW,OAAO,QAAQ,QAAQ,CAAE,GAAI,GAAO,OAAQ,cAAe,CAAC,EAE5E,GAAM,CACJ,aAAa,GACb,aAAa,IACb,UAAU,EACV,YAAY,GACV,EAIE,GAAS,EAAa,IAAI,CAAS,GAAK,GAAK,EACnD,EAAa,IAAI,EAAW,CAAK,EAEjC,IAAM,EAAM,EAAM,SAChB,EAAc,EAAW,EAAW,EAAO,EAAY,EAAY,CAAO,CAC5E,EAMA,MAJA,GAAQ,EAAI,SACJ,IAAA,OACA,IAAA,EACR,EACO,CACT"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Options for renderWidgets.
3
+ */
4
+ export interface RenderWidgetsOptions {
5
+ /** Maximum render attempts before giving up. Default 10. */
6
+ maxRetries?: number;
7
+ /** Delay between attempts, in milliseconds. Default 300. */
8
+ retryDelay?: number;
9
+ /**
10
+ * Called when an error is swallowed. `context` labels where it happened
11
+ * (e.g. 'manager-unavailable', 'extract', 'render-widget'). Default: warn on
12
+ * critical context only.
13
+ */
14
+ onError?: (error: unknown, context: string) => void;
15
+ /**
16
+ * Token used to dedupe/cancel repeated renders. A newer render with the same
17
+ * key cancels older in-flight/queued ones. Default: the container element.
18
+ */
19
+ cancelKey?: object;
20
+ }
21
+ //# sourceMappingURL=RenderWidgetsOptions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RenderWidgetsOptions.d.ts","sourceRoot":"","sources":["../../../../src/types/widgets/RenderWidgetsOptions.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACnD;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Result of a renderWidgets call. `rendered` is the number of widgets rendered
3
+ * via the prototype path; it is 0 when the host constructor path handled
4
+ * rendering (the count is not observable there).
5
+ */
6
+ export type RenderWidgetsResult = {
7
+ ok: true;
8
+ rendered: number;
9
+ } | {
10
+ ok: false;
11
+ reason: 'no-container' | 'manager-unavailable' | 'no-widgets' | 'cancelled';
12
+ };
13
+ //# sourceMappingURL=RenderWidgetsResult.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RenderWidgetsResult.d.ts","sourceRoot":"","sources":["../../../../src/types/widgets/RenderWidgetsResult.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAC3B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC9B;IACE,EAAE,EAAE,KAAK,CAAA;IACT,MAAM,EACF,cAAc,GACd,qBAAqB,GACrB,YAAY,GACZ,WAAW,CAAA;CAChB,CAAA"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The host's widget-manager constructor (`window.staffbase.content.widgetMgr`).
3
+ * The signature is internal; most builds accept `(unused, isEditor?)` and the
4
+ * instance exposes a `render(container)` method.
5
+ */
6
+ export type StaffbaseWidgetManagerConstructor = new (...args: unknown[]) => {
7
+ render?: (container: HTMLElement) => Promise<void> | void;
8
+ };
9
+ //# sourceMappingURL=StaffbaseWidgetManagerConstructor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StaffbaseWidgetManagerConstructor.d.ts","sourceRoot":"","sources":["../../../../src/types/widgets/StaffbaseWidgetManagerConstructor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,iCAAiC,GAAG,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK;IAC1E,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;CAC1D,CAAA"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The host's private widget-manager prototype
3
+ * (`window.staffbase.content.widgetMgr.prototype`). All members are optional
4
+ * because this is an undocumented API that may change.
5
+ */
6
+ export interface StaffbaseWidgetManagerPrototype {
7
+ _widgets?: unknown[];
8
+ _extractWidgets?: (container: HTMLElement) => unknown[];
9
+ _renderWidget?: (this: StaffbaseWidgetManagerPrototype, container: HTMLElement, widget: unknown) => void;
10
+ }
11
+ //# sourceMappingURL=StaffbaseWidgetManagerPrototype.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StaffbaseWidgetManagerPrototype.d.ts","sourceRoot":"","sources":["../../../../src/types/widgets/StaffbaseWidgetManagerPrototype.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAA;IACpB,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,WAAW,KAAK,OAAO,EAAE,CAAA;IACvD,aAAa,CAAC,EAAE,CACd,IAAI,EAAE,+BAA+B,EACrC,SAAS,EAAE,WAAW,EACtB,MAAM,EAAE,OAAO,KACZ,IAAI,CAAA;CACV"}
@@ -0,0 +1,16 @@
1
+ import { DependencyList } from 'react';
2
+ import { RenderWidgetsOptions } from './RenderWidgetsOptions';
3
+ /**
4
+ * Options for the useRenderWidgets hook: renderWidgets options plus optional
5
+ * MutationObserver wiring.
6
+ */
7
+ export interface UseRenderWidgetsOptions extends RenderWidgetsOptions {
8
+ /**
9
+ * When true, a debounced MutationObserver (childList + subtree) re-renders on
10
+ * article HTML changes (accordions, "View more"), cleaned up on unmount.
11
+ */
12
+ observe?: boolean;
13
+ /** Effect dependency list controlling when a render re-runs. Default []. */
14
+ deps?: DependencyList;
15
+ }
16
+ //# sourceMappingURL=UseRenderWidgetsOptions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UseRenderWidgetsOptions.d.ts","sourceRoot":"","sources":["../../../../src/types/widgets/UseRenderWidgetsOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAE3C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAElE;;;GAGG;AACH,MAAM,WAAW,uBAAwB,SAAQ,oBAAoB;IACnE;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,cAAc,CAAA;CACtB"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Default onError for renderWidgets: warns only on the critical
3
+ * 'manager-unavailable' context so a platform regression stays visible in logs
4
+ * without spamming per-widget noise. Consumers can pass their own onError.
5
+ * @param {unknown} error - The swallowed error.
6
+ * @param {string} context - Where it happened.
7
+ * @returns {void}
8
+ */
9
+ export declare const defaultWidgetOnError: (error: unknown, context: string) => void;
10
+ //# sourceMappingURL=defaultWidgetOnError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaultWidgetOnError.d.ts","sourceRoot":"","sources":["../../../src/widgets/defaultWidgetOnError.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAAI,OAAO,OAAO,EAAE,SAAS,MAAM,KAAG,IAOtE,CAAA"}
@@ -0,0 +1,8 @@
1
+ import { StaffbaseWidgetManagerConstructor } from '../types/widgets/StaffbaseWidgetManagerConstructor';
2
+ /**
3
+ * Resolves the host widget-manager constructor from the global Staffbase object,
4
+ * or null when it is not a function (older runtimes / platform regression).
5
+ * @returns {StaffbaseWidgetManagerConstructor | null} The constructor or null.
6
+ */
7
+ export declare const getWidgetManagerConstructor: () => StaffbaseWidgetManagerConstructor | null;
8
+ //# sourceMappingURL=getWidgetManagerConstructor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getWidgetManagerConstructor.d.ts","sourceRoot":"","sources":["../../../src/widgets/getWidgetManagerConstructor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iCAAiC,EAAE,MAAM,oDAAoD,CAAA;AAE3G;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,QAClC,iCAAiC,GAAG,IAUvC,CAAA"}
@@ -0,0 +1,8 @@
1
+ import { StaffbaseWidgetManagerPrototype } from '../types/widgets/StaffbaseWidgetManagerPrototype';
2
+ /**
3
+ * Resolves the host widget-manager prototype (the private fallback API) from the
4
+ * global Staffbase object, or null when it is unavailable.
5
+ * @returns {StaffbaseWidgetManagerPrototype | null} The prototype or null.
6
+ */
7
+ export declare const getWidgetManagerPrototype: () => StaffbaseWidgetManagerPrototype | null;
8
+ //# sourceMappingURL=getWidgetManagerPrototype.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getWidgetManagerPrototype.d.ts","sourceRoot":"","sources":["../../../src/widgets/getWidgetManagerPrototype.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,kDAAkD,CAAA;AAEvG;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,QAChC,+BAA+B,GAAG,IAYrC,CAAA"}
@@ -0,0 +1,12 @@
1
+ import { StaffbaseWidgetManagerPrototype } from '../types/widgets/StaffbaseWidgetManagerPrototype';
2
+ /**
3
+ * Type guard asserting the widget-manager prototype exposes the methods the
4
+ * prototype render path needs.
5
+ * @param {StaffbaseWidgetManagerPrototype} widgetMgr - The resolved prototype.
6
+ * @returns {boolean} True when both _extractWidgets and _renderWidget are functions.
7
+ */
8
+ export declare const hasRequiredWidgetManagerMethods: (widgetMgr: StaffbaseWidgetManagerPrototype) => widgetMgr is StaffbaseWidgetManagerPrototype & {
9
+ _extractWidgets: (container: HTMLElement) => unknown[];
10
+ _renderWidget: (this: StaffbaseWidgetManagerPrototype, container: HTMLElement, widget: unknown) => void;
11
+ };
12
+ //# sourceMappingURL=hasRequiredWidgetManagerMethods.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hasRequiredWidgetManagerMethods.d.ts","sourceRoot":"","sources":["../../../src/widgets/hasRequiredWidgetManagerMethods.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,kDAAkD,CAAA;AAEvG;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B,GAC1C,WAAW,+BAA+B,KACzC,SAAS,IAAI,+BAA+B,GAAG;IAChD,eAAe,EAAE,CAAC,SAAS,EAAE,WAAW,KAAK,OAAO,EAAE,CAAA;IACtD,aAAa,EAAE,CACb,IAAI,EAAE,+BAA+B,EACrC,SAAS,EAAE,WAAW,EACtB,MAAM,EAAE,OAAO,KACZ,IAAI,CAAA;CAGoC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export { renderWidgets } from './renderWidgets';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/widgets/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Detects known, benign internal Staffbase errors thrown by `_renderWidget` for
3
+ * individual widgets, so they can be swallowed without aborting the batch.
4
+ * Promoted from unacknowledged-bulletins (`each` / `undefined is not an object`).
5
+ * @param {unknown} error - The thrown value.
6
+ * @returns {boolean} True when the error is a known internal render error.
7
+ */
8
+ export declare const isKnownStaffbaseRenderError: (error: unknown) => boolean;
9
+ //# sourceMappingURL=isKnownStaffbaseRenderError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isKnownStaffbaseRenderError.d.ts","sourceRoot":"","sources":["../../../src/widgets/isKnownStaffbaseRenderError.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,GAAI,OAAO,OAAO,KAAG,OAGH,CAAA"}
@@ -0,0 +1,2 @@
1
+ export { useRenderWidgets } from './useRenderWidgets';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/widgets/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA"}
@@ -0,0 +1,14 @@
1
+ import { RefObject } from 'react';
2
+ import { UseRenderWidgetsOptions } from '../../types/widgets/UseRenderWidgetsOptions';
3
+ /**
4
+ * React adapter for renderWidgets. Renders on mount and whenever `deps` change,
5
+ * using `ref.current` as the cancelKey. With `observe: true` it mounts a
6
+ * debounced MutationObserver (childList + subtree) that re-renders on article
7
+ * HTML changes and cleans up on unmount. Absorbs the render/observe glue that
8
+ * the widgets repeat in their Article loaders.
9
+ * @param {RefObject<HTMLElement | null>} ref - Ref to the widget container.
10
+ * @param {UseRenderWidgetsOptions} options - renderWidgets options plus observe/deps.
11
+ * @returns {void}
12
+ */
13
+ export declare const useRenderWidgets: (ref: RefObject<HTMLElement | null>, options?: UseRenderWidgetsOptions) => void;
14
+ //# sourceMappingURL=useRenderWidgets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useRenderWidgets.d.ts","sourceRoot":"","sources":["../../../../src/widgets/react/useRenderWidgets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,6CAA6C,CAAA;AAG1F;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,GAC3B,KAAK,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EAClC,UAAS,uBAA4B,KACpC,IAwBF,CAAA"}
@@ -0,0 +1,16 @@
1
+ import { RenderWidgetsOptions } from '../types/widgets/RenderWidgetsOptions';
2
+ import { RenderWidgetsResult } from '../types/widgets/RenderWidgetsResult';
3
+ /**
4
+ * Renders the widgets embedded in `container` using the host's private widget
5
+ * manager. Superset of the four widget services: constructor path first with a
6
+ * prototype fallback, a global queue that serializes renders, per-cancelKey
7
+ * cancellation of superseded renders, configurable retries, swallowing of known
8
+ * internal Staffbase errors, and a once-per-session warning when the manager is
9
+ * missing. Returns a typed result so callers can react (e.g. mark a
10
+ * data-widget-render-error attribute).
11
+ * @param {HTMLElement | null} container - The element whose embedded widgets are rendered.
12
+ * @param {RenderWidgetsOptions} options - Retry, error and cancellation options.
13
+ * @returns {Promise<RenderWidgetsResult>} The typed render result.
14
+ */
15
+ export declare const renderWidgets: (container: HTMLElement | null, options?: RenderWidgetsOptions) => Promise<RenderWidgetsResult>;
16
+ //# sourceMappingURL=renderWidgets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderWidgets.d.ts","sourceRoot":"","sources":["../../../src/widgets/renderWidgets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAA;AACjF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAA;AA8G/E;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,GACxB,WAAW,WAAW,GAAG,IAAI,EAC7B,UAAS,oBAAyB,KACjC,OAAO,CAAC,mBAAmB,CAwB7B,CAAA"}
@@ -0,0 +1,2 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../renderWidgets-EbJq9Wn6.js");let t=require("react");var n=(n,r={})=>{let{observe:i=!1,deps:a=[],...o}=r;(0,t.useEffect)(()=>{let t=n.current;if(!t)return;let r={...o,cancelKey:t};if(e.t(t,r),!i)return;let a,s=new MutationObserver(()=>{clearTimeout(a),a=setTimeout(()=>void e.t(n.current,r),100)});return s.observe(t,{childList:!0,subtree:!0}),()=>{clearTimeout(a),s.disconnect()}},a)};exports.useRenderWidgets=n;
2
+ //# sourceMappingURL=react.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.cjs.js","names":[],"sources":["../../src/widgets/react/useRenderWidgets.ts"],"sourcesContent":["import type { RefObject } from 'react'\nimport { useEffect } from 'react'\n\nimport type { UseRenderWidgetsOptions } from '../../types/widgets/UseRenderWidgetsOptions'\nimport { renderWidgets } from '../renderWidgets'\n\n/**\n * React adapter for renderWidgets. Renders on mount and whenever `deps` change,\n * using `ref.current` as the cancelKey. With `observe: true` it mounts a\n * debounced MutationObserver (childList + subtree) that re-renders on article\n * HTML changes and cleans up on unmount. Absorbs the render/observe glue that\n * the widgets repeat in their Article loaders.\n * @param {RefObject<HTMLElement | null>} ref - Ref to the widget container.\n * @param {UseRenderWidgetsOptions} options - renderWidgets options plus observe/deps.\n * @returns {void}\n */\nexport const useRenderWidgets = (\n ref: RefObject<HTMLElement | null>,\n options: UseRenderWidgetsOptions = {},\n): void => {\n const { observe = false, deps = [], ...renderOptions } = options\n\n useEffect(() => {\n const el = ref.current\n if (!el) return\n\n const opts = { ...renderOptions, cancelKey: el }\n void renderWidgets(el, opts)\n\n if (!observe) return\n\n let timer: ReturnType<typeof setTimeout>\n const observer = new MutationObserver(() => {\n clearTimeout(timer)\n timer = setTimeout(() => void renderWidgets(ref.current, opts), 100)\n })\n observer.observe(el, { childList: true, subtree: true })\n\n return () => {\n clearTimeout(timer)\n observer.disconnect()\n }\n }, deps)\n}\n"],"mappings":"0IAgBA,IAAa,GACX,EACA,EAAmC,CAAC,IAC3B,CACT,GAAM,CAAE,UAAU,GAAO,OAAO,CAAC,EAAG,GAAG,GAAkB,GAEzD,EAAA,EAAA,eAAgB,CACd,IAAM,EAAK,EAAI,QACf,GAAI,CAAC,EAAI,OAET,IAAM,EAAO,CAAE,GAAG,EAAe,UAAW,CAAG,EAG/C,GAFA,EAAK,EAAc,EAAI,CAAI,EAEvB,CAAC,EAAS,OAEd,IAAI,EACE,EAAW,IAAI,qBAAuB,CAC1C,aAAa,CAAK,EAClB,EAAQ,eAAiB,KAAK,EAAA,EAAc,EAAI,QAAS,CAAI,EAAG,GAAG,CACrE,CAAC,EAGD,OAFA,EAAS,QAAQ,EAAI,CAAE,UAAW,GAAM,QAAS,EAAK,CAAC,MAE1C,CACX,aAAa,CAAK,EAClB,EAAS,WAAW,CACtB,CACF,EAAG,CAAI,CACT"}
@@ -0,0 +1,28 @@
1
+ import { t as e } from "../renderWidgets-CeIczubt.mjs";
2
+ import { useEffect as t } from "react";
3
+ //#region src/widgets/react/useRenderWidgets.ts
4
+ var n = (n, r = {}) => {
5
+ let { observe: i = !1, deps: a = [], ...o } = r;
6
+ t(() => {
7
+ let t = n.current;
8
+ if (!t) return;
9
+ let r = {
10
+ ...o,
11
+ cancelKey: t
12
+ };
13
+ if (e(t, r), !i) return;
14
+ let a, s = new MutationObserver(() => {
15
+ clearTimeout(a), a = setTimeout(() => void e(n.current, r), 100);
16
+ });
17
+ return s.observe(t, {
18
+ childList: !0,
19
+ subtree: !0
20
+ }), () => {
21
+ clearTimeout(a), s.disconnect();
22
+ };
23
+ }, a);
24
+ };
25
+ //#endregion
26
+ export { n as useRenderWidgets };
27
+
28
+ //# sourceMappingURL=react.es.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.es.mjs","names":[],"sources":["../../src/widgets/react/useRenderWidgets.ts"],"sourcesContent":["import type { RefObject } from 'react'\nimport { useEffect } from 'react'\n\nimport type { UseRenderWidgetsOptions } from '../../types/widgets/UseRenderWidgetsOptions'\nimport { renderWidgets } from '../renderWidgets'\n\n/**\n * React adapter for renderWidgets. Renders on mount and whenever `deps` change,\n * using `ref.current` as the cancelKey. With `observe: true` it mounts a\n * debounced MutationObserver (childList + subtree) that re-renders on article\n * HTML changes and cleans up on unmount. Absorbs the render/observe glue that\n * the widgets repeat in their Article loaders.\n * @param {RefObject<HTMLElement | null>} ref - Ref to the widget container.\n * @param {UseRenderWidgetsOptions} options - renderWidgets options plus observe/deps.\n * @returns {void}\n */\nexport const useRenderWidgets = (\n ref: RefObject<HTMLElement | null>,\n options: UseRenderWidgetsOptions = {},\n): void => {\n const { observe = false, deps = [], ...renderOptions } = options\n\n useEffect(() => {\n const el = ref.current\n if (!el) return\n\n const opts = { ...renderOptions, cancelKey: el }\n void renderWidgets(el, opts)\n\n if (!observe) return\n\n let timer: ReturnType<typeof setTimeout>\n const observer = new MutationObserver(() => {\n clearTimeout(timer)\n timer = setTimeout(() => void renderWidgets(ref.current, opts), 100)\n })\n observer.observe(el, { childList: true, subtree: true })\n\n return () => {\n clearTimeout(timer)\n observer.disconnect()\n }\n }, deps)\n}\n"],"mappings":";;;AAgBA,IAAa,KACX,GACA,IAAmC,CAAC,MAC3B;CACT,IAAM,EAAE,aAAU,IAAO,UAAO,CAAC,GAAG,GAAG,MAAkB;CAEzD,QAAgB;EACd,IAAM,IAAK,EAAI;EACf,IAAI,CAAC,GAAI;EAET,IAAM,IAAO;GAAE,GAAG;GAAe,WAAW;EAAG;EAG/C,IAFA,EAAmB,GAAI,CAAI,GAEvB,CAAC,GAAS;EAEd,IAAI,GACE,IAAW,IAAI,uBAAuB;GAE1C,AADA,aAAa,CAAK,GAClB,IAAQ,iBAAiB,KAAK,EAAc,EAAI,SAAS,CAAI,GAAG,GAAG;EACrE,CAAC;EAGD,OAFA,EAAS,QAAQ,GAAI;GAAE,WAAW;GAAM,SAAS;EAAK,CAAC,SAE1C;GAEX,AADA,aAAa,CAAK,GAClB,EAAS,WAAW;EACtB;CACF,GAAG,CAAI;AACT"}
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./renderWidgets-EbJq9Wn6.js");exports.renderWidgets=e.t;
@@ -0,0 +1,2 @@
1
+ import { t as e } from "./renderWidgets-CeIczubt.mjs";
2
+ export { e as renderWidgets };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@favish/staffbase-utils",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Shared internal/host utilities for Staffbase widgets",
5
5
  "author": "Favish <dev@favish.com>",
6
6
  "license": "UNLICENSED",
@@ -27,6 +27,16 @@
27
27
  "types": "./dist/src/links/index.d.ts",
28
28
  "import": "./dist/links.es.mjs",
29
29
  "require": "./dist/links.cjs.js"
30
+ },
31
+ "./widgets": {
32
+ "types": "./dist/src/widgets/index.d.ts",
33
+ "import": "./dist/widgets.es.mjs",
34
+ "require": "./dist/widgets.cjs.js"
35
+ },
36
+ "./widgets/react": {
37
+ "types": "./dist/src/widgets/react/index.d.ts",
38
+ "import": "./dist/widgets/react.es.mjs",
39
+ "require": "./dist/widgets/react.cjs.js"
30
40
  }
31
41
  },
32
42
  "repository": {
@@ -55,6 +65,7 @@
55
65
  "@testing-library/jest-dom": "^6.9.1",
56
66
  "@types/jest": "^30.0.0",
57
67
  "@types/node": "^25",
68
+ "@types/react": "19.2.16",
58
69
  "@typescript-eslint/eslint-plugin": "^8.60.0",
59
70
  "@typescript-eslint/parser": "^8.60.0",
60
71
  "eslint": "^10.4.0",