@adartem/adlib-attributes 0.0.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.
@@ -0,0 +1,201 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ // src/core/index.ts
6
+ function createLogger(debug = false) {
7
+ return {
8
+ debug: (...args) => {
9
+ if (!debug) return;
10
+ console.warn("[debug]", ...args);
11
+ },
12
+ warn: (...args) => {
13
+ console.warn("[warn]", ...args);
14
+ },
15
+ error: (...args) => {
16
+ console.error("[error]", ...args);
17
+ }
18
+ };
19
+ }
20
+ function isElement(value) {
21
+ return typeof Element !== "undefined" && value instanceof Element;
22
+ }
23
+ function safeMatches(el, selector) {
24
+ try {
25
+ return el.matches(selector);
26
+ } catch {
27
+ return false;
28
+ }
29
+ }
30
+ function findClosestMatching(target, selector) {
31
+ let cur = target;
32
+ while (cur) {
33
+ if (isElement(cur) && safeMatches(cur, selector)) return cur;
34
+ cur = isElement(cur) ? cur.parentElement : null;
35
+ }
36
+ return null;
37
+ }
38
+ function createDelegatedListener(info) {
39
+ return (e) => {
40
+ const entries = info.entries.slice();
41
+ for (const entry of entries) {
42
+ const matched = findClosestMatching(e.target, entry.selector);
43
+ if (!matched) continue;
44
+ entry.handler(e, matched);
45
+ if (entry.once) {
46
+ const idx = info.entries.indexOf(entry);
47
+ if (idx >= 0) info.entries.splice(idx, 1);
48
+ }
49
+ }
50
+ };
51
+ }
52
+ function resolveObserverTarget(root) {
53
+ if (root instanceof Document) return root.documentElement ?? root;
54
+ return root;
55
+ }
56
+ function createCore(options = {}) {
57
+ const root = options.root ?? document;
58
+ const logger = createLogger(!!options.debug);
59
+ const defs = /* @__PURE__ */ new Map();
60
+ const instances = /* @__PURE__ */ new Map();
61
+ const listenersByType = /* @__PURE__ */ new Map();
62
+ function on(type, selector, handler, opts = {}) {
63
+ const capture = !!opts.capture;
64
+ const passive = !!opts.passive;
65
+ const existing = listenersByType.get(type);
66
+ if (existing) {
67
+ existing.entries.push({ selector, handler, once: !!opts.once });
68
+ return () => {
69
+ const idx = existing.entries.findIndex((e) => e.selector === selector && e.handler === handler);
70
+ if (idx >= 0) existing.entries.splice(idx, 1);
71
+ if (existing.entries.length === 0) {
72
+ root.removeEventListener(type, existing.listener, { capture: existing.capture });
73
+ listenersByType.delete(type);
74
+ }
75
+ };
76
+ }
77
+ const info = {
78
+ capture,
79
+ passive,
80
+ entries: [{ selector, handler, once: !!opts.once }],
81
+ listener: () => {
82
+ }
83
+ };
84
+ const listener = createDelegatedListener(info);
85
+ info.listener = listener;
86
+ listenersByType.set(type, info);
87
+ root.addEventListener(type, listener, { capture, passive });
88
+ return () => {
89
+ const cur = listenersByType.get(type);
90
+ if (!cur) return;
91
+ const idx = cur.entries.findIndex((e) => e.selector === selector && e.handler === handler);
92
+ if (idx >= 0) cur.entries.splice(idx, 1);
93
+ if (cur.entries.length === 0) {
94
+ root.removeEventListener(type, cur.listener, { capture: cur.capture });
95
+ listenersByType.delete(type);
96
+ }
97
+ };
98
+ }
99
+ function emit(el, name, detail) {
100
+ const ev = new CustomEvent(name, { detail, bubbles: true });
101
+ el.dispatchEvent(ev);
102
+ }
103
+ function register(def) {
104
+ defs.set(def.key, def);
105
+ }
106
+ function unregister(key) {
107
+ defs.delete(key);
108
+ }
109
+ function mount(key, _root) {
110
+ const def = defs.get(key);
111
+ if (!def) return;
112
+ if (instances.has(key)) return;
113
+ const inst = def.init(api);
114
+ instances.set(key, inst);
115
+ }
116
+ function unmount(key) {
117
+ const inst = instances.get(key);
118
+ if (!inst) return;
119
+ inst.destroy();
120
+ instances.delete(key);
121
+ }
122
+ function mountAll(_root) {
123
+ for (const key of defs.keys()) mount(key);
124
+ }
125
+ function unmountAll() {
126
+ for (const key of instances.keys()) unmount(key);
127
+ }
128
+ let observer = null;
129
+ function observe(observedRoot = root) {
130
+ if (observer) return () => {
131
+ };
132
+ observer = new MutationObserver((_mutations) => {
133
+ });
134
+ observer.observe(resolveObserverTarget(observedRoot), {
135
+ subtree: true,
136
+ childList: true,
137
+ attributes: false
138
+ });
139
+ return () => stopObserving();
140
+ }
141
+ function stopObserving() {
142
+ if (!observer) return;
143
+ observer.disconnect();
144
+ observer = null;
145
+ }
146
+ function destroy() {
147
+ stopObserving();
148
+ unmountAll();
149
+ for (const [type, info] of listenersByType.entries()) {
150
+ root.removeEventListener(type, info.listener, { capture: info.capture });
151
+ }
152
+ listenersByType.clear();
153
+ defs.clear();
154
+ instances.clear();
155
+ }
156
+ function getLogger() {
157
+ return logger;
158
+ }
159
+ const api = {
160
+ on,
161
+ emit,
162
+ register,
163
+ unregister,
164
+ mount,
165
+ unmount,
166
+ mountAll,
167
+ unmountAll,
168
+ observe,
169
+ stopObserving,
170
+ destroy,
171
+ getLogger
172
+ };
173
+ return api;
174
+ }
175
+ var singleton = null;
176
+ function getCoreSingleton(options = {}) {
177
+ if (singleton) return singleton;
178
+ singleton = createCore({ ...options });
179
+ const shouldAutoMount = options.autoMount ?? true;
180
+ if (shouldAutoMount && typeof document !== "undefined") {
181
+ if (document.readyState === "loading") {
182
+ document.addEventListener(
183
+ "DOMContentLoaded",
184
+ () => {
185
+ singleton?.mountAll();
186
+ },
187
+ { once: true }
188
+ );
189
+ } else {
190
+ singleton.mountAll();
191
+ }
192
+ }
193
+ return singleton;
194
+ }
195
+ var core_default = createCore;
196
+
197
+ exports.createCore = createCore;
198
+ exports.default = core_default;
199
+ exports.getCoreSingleton = getCoreSingleton;
200
+ //# sourceMappingURL=index.cjs.map
201
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/* --------------------------------------------------------------------------\n * AdLib Attributes · Core\n *\n * Primitives partagées utilisées par le runtime et les modules d’attributs :\n * - délégation d’événements (listeners uniques sur une racine)\n * - registre de modules (définitions + instances)\n * - cycle de vie (mount/unmount/destroy)\n * - observation DOM (signal de changements, orchestration pilotée par le runtime)\n * -------------------------------------------------------------------------- */\n\nexport type Disposer = () => void;\nexport type Cleanup = void | Disposer;\n\nexport type CoreLogger = {\n debug: (...args: unknown[]) => void;\n warn: (...args: unknown[]) => void;\n error: (...args: unknown[]) => void;\n};\n\nexport type CoreOptions = {\n /** Root document/shadow root to bind listeners & observer (defaults to document). */\n root?: Document | ShadowRoot;\n /** Enable debug logs (printed as console.warn with \"[debug]\"). */\n debug?: boolean;\n /** Auto-mount modules on DOMContentLoaded when using singleton. Default: true. */\n autoMount?: boolean;\n};\n\nexport type DelegatedHandler = (e: Event, matched: Element) => void;\n\nexport type OnOptions = {\n /** If true, this delegated handler will run once then auto-unsubscribe. */\n once?: boolean;\n /** Capture phase for the underlying listener (per event type, first call wins). */\n capture?: boolean;\n /** Passive flag for the underlying listener (per event type, first call wins). */\n passive?: boolean;\n};\n\nexport interface Core {\n /**\n * Event delegation: listen on root and invoke handler when an ancestor matches selector.\n * Returns a disposer to remove this specific handler.\n */\n on: (type: string, selector: string, handler: DelegatedHandler, options?: OnOptions) => Disposer;\n\n /** Emit a CustomEvent from a given element. */\n emit: (el: Element, name: string, detail?: unknown) => void;\n\n /** Register a module definition. */\n register: (def: AdLibModuleDefinition) => void;\n\n /** Unregister a module definition by key. */\n unregister: (key: ModuleKey) => void;\n\n /** Mount a module by key, optionally on a given root. */\n mount: (key: ModuleKey, root?: ParentNode) => void;\n\n /** Unmount a module instance by key. */\n unmount: (key: ModuleKey) => void;\n\n /** Mount all registered modules. */\n mountAll: (root?: ParentNode) => void;\n\n /** Unmount all mounted modules. */\n unmountAll: () => void;\n\n /**\n * Observe dynamic DOM changes on a given root.\n *\n * Rôle :\n * - fournir un signal de changement DOM (ajouts/retraits de nœuds)\n * - permettre au runtime d’orchestrer un rescan/remount si nécessaire\n *\n * Retourne un disposer pour arrêter l’observation.\n */\n observe: (root?: ParentNode) => Disposer;\n\n /** Stop observing dynamic DOM changes. */\n stopObserving: () => void;\n\n /** Destroy core: unmount all, remove listeners, disconnect observer. */\n destroy: () => void;\n\n /** Core logger (debug/warn/error). */\n getLogger: () => CoreLogger;\n}\n\n/** Module key type (ex: 'ad-click'). */\nexport type ModuleKey = `ad-${string}`;\n\nexport interface AdLibModuleInstance {\n key: ModuleKey;\n destroy(): void;\n restart?(options?: { rescan?: boolean }): void;\n}\n\nexport interface AdLibModuleDefinition {\n key: ModuleKey;\n init(core: Core): AdLibModuleInstance;\n}\n\n/* --------------------------------- Internals -------------------------------- */\n\ntype DelegatedEntry = {\n selector: string;\n handler: DelegatedHandler;\n once: boolean;\n};\n\ntype EventListenerInfo = {\n capture: boolean;\n passive: boolean;\n listener: (e: Event) => void;\n entries: DelegatedEntry[];\n};\n\nfunction createLogger(debug = false): CoreLogger {\n return {\n debug: (...args: unknown[]) => {\n if (!debug) return;\n console.warn('[debug]', ...args);\n },\n warn: (...args: unknown[]) => {\n console.warn('[warn]', ...args);\n },\n error: (...args: unknown[]) => {\n console.error('[error]', ...args);\n },\n };\n}\n\nfunction isElement(value: unknown): value is Element {\n // Garde-fou typé : vérifie l’existence de Element (tests / environnements non DOM).\n return typeof Element !== 'undefined' && value instanceof Element;\n}\n\nfunction safeMatches(el: Element, selector: string): boolean {\n try {\n return el.matches(selector);\n } catch {\n return false;\n }\n}\n\nfunction findClosestMatching(target: EventTarget | null, selector: string): Element | null {\n let cur: EventTarget | null = target;\n\n while (cur) {\n if (isElement(cur) && safeMatches(cur, selector)) return cur;\n cur = isElement(cur) ? cur.parentElement : null;\n }\n\n return null;\n}\n\nfunction createDelegatedListener(info: EventListenerInfo) {\n return (e: Event) => {\n // Copie défensive : la liste peut être modifiée par un handler \"once\".\n const entries = info.entries.slice();\n\n for (const entry of entries) {\n const matched = findClosestMatching(e.target, entry.selector);\n if (!matched) continue;\n\n entry.handler(e, matched);\n\n if (entry.once) {\n const idx = info.entries.indexOf(entry);\n if (idx >= 0) info.entries.splice(idx, 1);\n }\n }\n };\n}\n\nfunction resolveObserverTarget(root: ParentNode): Node {\n // MutationObserver attend un Node.\n // - Document => document.documentElement si disponible, sinon le document lui-même\n // - ShadowRoot / Element => observe directement\n if (root instanceof Document) return root.documentElement ?? root;\n return root as unknown as Node;\n}\n\n/* ---------------------------------- Core ---------------------------------- */\n\nexport function createCore(options: CoreOptions = {}): Core {\n const root: Document | ShadowRoot = options.root ?? document;\n const logger = createLogger(!!options.debug);\n\n const defs = new Map<ModuleKey, AdLibModuleDefinition>();\n const instances = new Map<ModuleKey, AdLibModuleInstance>();\n\n const listenersByType = new Map<string, EventListenerInfo>();\n\n function on(type: string, selector: string, handler: DelegatedHandler, opts: OnOptions = {}): Disposer {\n const capture = !!opts.capture;\n const passive = !!opts.passive;\n\n const existing = listenersByType.get(type);\n if (existing) {\n existing.entries.push({ selector, handler, once: !!opts.once });\n\n return () => {\n const idx = existing.entries.findIndex((e) => e.selector === selector && e.handler === handler);\n if (idx >= 0) existing.entries.splice(idx, 1);\n\n // ✅ Si c'était le dernier handler, on retire le listener racine et on nettoie la map\n if (existing.entries.length === 0) {\n root.removeEventListener(type, existing.listener, { capture: existing.capture });\n listenersByType.delete(type);\n }\n };\n }\n\n const info: EventListenerInfo = {\n capture,\n passive,\n entries: [{ selector, handler, once: !!opts.once }],\n listener: () => {},\n };\n\n const listener = createDelegatedListener(info);\n info.listener = listener;\n\n listenersByType.set(type, info);\n\n root.addEventListener(type, listener, { capture, passive });\n\n // ✅ Disposer “handler-level” (pas “type-level”)\n return () => {\n const cur = listenersByType.get(type);\n if (!cur) return;\n\n const idx = cur.entries.findIndex((e) => e.selector === selector && e.handler === handler);\n if (idx >= 0) cur.entries.splice(idx, 1);\n\n if (cur.entries.length === 0) {\n root.removeEventListener(type, cur.listener, { capture: cur.capture });\n listenersByType.delete(type);\n }\n };\n }\n\n function emit(el: Element, name: string, detail?: unknown) {\n const ev = new CustomEvent(name, { detail, bubbles: true });\n el.dispatchEvent(ev);\n }\n\n function register(def: AdLibModuleDefinition) {\n defs.set(def.key, def);\n }\n\n function unregister(key: ModuleKey) {\n defs.delete(key);\n }\n\n function mount(key: ModuleKey, _root?: ParentNode) {\n const def = defs.get(key);\n if (!def) return;\n if (instances.has(key)) return;\n\n const inst = def.init(api);\n instances.set(key, inst);\n }\n\n function unmount(key: ModuleKey) {\n const inst = instances.get(key);\n if (!inst) return;\n inst.destroy();\n instances.delete(key);\n }\n\n function mountAll(_root?: ParentNode) {\n for (const key of defs.keys()) mount(key);\n }\n\n function unmountAll() {\n for (const key of instances.keys()) unmount(key);\n }\n\n let observer: MutationObserver | null = null;\n\n function observe(observedRoot: ParentNode = root): Disposer {\n if (observer) return () => {};\n\n observer = new MutationObserver((_mutations) => {\n // Signal DOM : le runtime est responsable de décider quoi charger / recharger.\n // Le core garantit uniquement une observation stable et désinscriptible.\n });\n\n observer.observe(resolveObserverTarget(observedRoot), {\n subtree: true,\n childList: true,\n attributes: false,\n });\n\n return () => stopObserving();\n }\n\n function stopObserving() {\n if (!observer) return;\n observer.disconnect();\n observer = null;\n }\n\n function destroy() {\n stopObserving();\n unmountAll();\n\n for (const [type, info] of listenersByType.entries()) {\n root.removeEventListener(type, info.listener, { capture: info.capture });\n }\n\n listenersByType.clear();\n defs.clear();\n instances.clear();\n }\n\n function getLogger() {\n return logger;\n }\n\n const api: Core = {\n on,\n emit,\n register,\n unregister,\n mount,\n unmount,\n mountAll,\n unmountAll,\n observe,\n stopObserving,\n destroy,\n getLogger,\n };\n\n return api;\n}\n\n/* ----------------------------- Singleton helpers ---------------------------- */\n\nlet singleton: Core | null = null;\n\nexport function getCoreSingleton(options: CoreOptions = {}): Core {\n if (singleton) return singleton;\n singleton = createCore({ autoMount: true, ...options });\n\n const shouldAutoMount = options.autoMount ?? true;\n if (shouldAutoMount && typeof document !== 'undefined') {\n if (document.readyState === 'loading') {\n document.addEventListener(\n 'DOMContentLoaded',\n () => {\n singleton?.mountAll();\n },\n { once: true }\n );\n } else {\n singleton.mountAll();\n }\n }\n\n return singleton;\n}\n\nexport default createCore;"]}
@@ -0,0 +1,195 @@
1
+ // src/core/index.ts
2
+ function createLogger(debug = false) {
3
+ return {
4
+ debug: (...args) => {
5
+ if (!debug) return;
6
+ console.warn("[debug]", ...args);
7
+ },
8
+ warn: (...args) => {
9
+ console.warn("[warn]", ...args);
10
+ },
11
+ error: (...args) => {
12
+ console.error("[error]", ...args);
13
+ }
14
+ };
15
+ }
16
+ function isElement(value) {
17
+ return typeof Element !== "undefined" && value instanceof Element;
18
+ }
19
+ function safeMatches(el, selector) {
20
+ try {
21
+ return el.matches(selector);
22
+ } catch {
23
+ return false;
24
+ }
25
+ }
26
+ function findClosestMatching(target, selector) {
27
+ let cur = target;
28
+ while (cur) {
29
+ if (isElement(cur) && safeMatches(cur, selector)) return cur;
30
+ cur = isElement(cur) ? cur.parentElement : null;
31
+ }
32
+ return null;
33
+ }
34
+ function createDelegatedListener(info) {
35
+ return (e) => {
36
+ const entries = info.entries.slice();
37
+ for (const entry of entries) {
38
+ const matched = findClosestMatching(e.target, entry.selector);
39
+ if (!matched) continue;
40
+ entry.handler(e, matched);
41
+ if (entry.once) {
42
+ const idx = info.entries.indexOf(entry);
43
+ if (idx >= 0) info.entries.splice(idx, 1);
44
+ }
45
+ }
46
+ };
47
+ }
48
+ function resolveObserverTarget(root) {
49
+ if (root instanceof Document) return root.documentElement ?? root;
50
+ return root;
51
+ }
52
+ function createCore(options = {}) {
53
+ const root = options.root ?? document;
54
+ const logger = createLogger(!!options.debug);
55
+ const defs = /* @__PURE__ */ new Map();
56
+ const instances = /* @__PURE__ */ new Map();
57
+ const listenersByType = /* @__PURE__ */ new Map();
58
+ function on(type, selector, handler, opts = {}) {
59
+ const capture = !!opts.capture;
60
+ const passive = !!opts.passive;
61
+ const existing = listenersByType.get(type);
62
+ if (existing) {
63
+ existing.entries.push({ selector, handler, once: !!opts.once });
64
+ return () => {
65
+ const idx = existing.entries.findIndex((e) => e.selector === selector && e.handler === handler);
66
+ if (idx >= 0) existing.entries.splice(idx, 1);
67
+ if (existing.entries.length === 0) {
68
+ root.removeEventListener(type, existing.listener, { capture: existing.capture });
69
+ listenersByType.delete(type);
70
+ }
71
+ };
72
+ }
73
+ const info = {
74
+ capture,
75
+ passive,
76
+ entries: [{ selector, handler, once: !!opts.once }],
77
+ listener: () => {
78
+ }
79
+ };
80
+ const listener = createDelegatedListener(info);
81
+ info.listener = listener;
82
+ listenersByType.set(type, info);
83
+ root.addEventListener(type, listener, { capture, passive });
84
+ return () => {
85
+ const cur = listenersByType.get(type);
86
+ if (!cur) return;
87
+ const idx = cur.entries.findIndex((e) => e.selector === selector && e.handler === handler);
88
+ if (idx >= 0) cur.entries.splice(idx, 1);
89
+ if (cur.entries.length === 0) {
90
+ root.removeEventListener(type, cur.listener, { capture: cur.capture });
91
+ listenersByType.delete(type);
92
+ }
93
+ };
94
+ }
95
+ function emit(el, name, detail) {
96
+ const ev = new CustomEvent(name, { detail, bubbles: true });
97
+ el.dispatchEvent(ev);
98
+ }
99
+ function register(def) {
100
+ defs.set(def.key, def);
101
+ }
102
+ function unregister(key) {
103
+ defs.delete(key);
104
+ }
105
+ function mount(key, _root) {
106
+ const def = defs.get(key);
107
+ if (!def) return;
108
+ if (instances.has(key)) return;
109
+ const inst = def.init(api);
110
+ instances.set(key, inst);
111
+ }
112
+ function unmount(key) {
113
+ const inst = instances.get(key);
114
+ if (!inst) return;
115
+ inst.destroy();
116
+ instances.delete(key);
117
+ }
118
+ function mountAll(_root) {
119
+ for (const key of defs.keys()) mount(key);
120
+ }
121
+ function unmountAll() {
122
+ for (const key of instances.keys()) unmount(key);
123
+ }
124
+ let observer = null;
125
+ function observe(observedRoot = root) {
126
+ if (observer) return () => {
127
+ };
128
+ observer = new MutationObserver((_mutations) => {
129
+ });
130
+ observer.observe(resolveObserverTarget(observedRoot), {
131
+ subtree: true,
132
+ childList: true,
133
+ attributes: false
134
+ });
135
+ return () => stopObserving();
136
+ }
137
+ function stopObserving() {
138
+ if (!observer) return;
139
+ observer.disconnect();
140
+ observer = null;
141
+ }
142
+ function destroy() {
143
+ stopObserving();
144
+ unmountAll();
145
+ for (const [type, info] of listenersByType.entries()) {
146
+ root.removeEventListener(type, info.listener, { capture: info.capture });
147
+ }
148
+ listenersByType.clear();
149
+ defs.clear();
150
+ instances.clear();
151
+ }
152
+ function getLogger() {
153
+ return logger;
154
+ }
155
+ const api = {
156
+ on,
157
+ emit,
158
+ register,
159
+ unregister,
160
+ mount,
161
+ unmount,
162
+ mountAll,
163
+ unmountAll,
164
+ observe,
165
+ stopObserving,
166
+ destroy,
167
+ getLogger
168
+ };
169
+ return api;
170
+ }
171
+ var singleton = null;
172
+ function getCoreSingleton(options = {}) {
173
+ if (singleton) return singleton;
174
+ singleton = createCore({ ...options });
175
+ const shouldAutoMount = options.autoMount ?? true;
176
+ if (shouldAutoMount && typeof document !== "undefined") {
177
+ if (document.readyState === "loading") {
178
+ document.addEventListener(
179
+ "DOMContentLoaded",
180
+ () => {
181
+ singleton?.mountAll();
182
+ },
183
+ { once: true }
184
+ );
185
+ } else {
186
+ singleton.mountAll();
187
+ }
188
+ }
189
+ return singleton;
190
+ }
191
+ var core_default = createCore;
192
+
193
+ export { createCore, core_default as default, getCoreSingleton };
194
+ //# sourceMappingURL=index.js.map
195
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/* --------------------------------------------------------------------------\n * AdLib Attributes · Core\n *\n * Primitives partagées utilisées par le runtime et les modules d’attributs :\n * - délégation d’événements (listeners uniques sur une racine)\n * - registre de modules (définitions + instances)\n * - cycle de vie (mount/unmount/destroy)\n * - observation DOM (signal de changements, orchestration pilotée par le runtime)\n * -------------------------------------------------------------------------- */\n\nexport type Disposer = () => void;\nexport type Cleanup = void | Disposer;\n\nexport type CoreLogger = {\n debug: (...args: unknown[]) => void;\n warn: (...args: unknown[]) => void;\n error: (...args: unknown[]) => void;\n};\n\nexport type CoreOptions = {\n /** Root document/shadow root to bind listeners & observer (defaults to document). */\n root?: Document | ShadowRoot;\n /** Enable debug logs (printed as console.warn with \"[debug]\"). */\n debug?: boolean;\n /** Auto-mount modules on DOMContentLoaded when using singleton. Default: true. */\n autoMount?: boolean;\n};\n\nexport type DelegatedHandler = (e: Event, matched: Element) => void;\n\nexport type OnOptions = {\n /** If true, this delegated handler will run once then auto-unsubscribe. */\n once?: boolean;\n /** Capture phase for the underlying listener (per event type, first call wins). */\n capture?: boolean;\n /** Passive flag for the underlying listener (per event type, first call wins). */\n passive?: boolean;\n};\n\nexport interface Core {\n /**\n * Event delegation: listen on root and invoke handler when an ancestor matches selector.\n * Returns a disposer to remove this specific handler.\n */\n on: (type: string, selector: string, handler: DelegatedHandler, options?: OnOptions) => Disposer;\n\n /** Emit a CustomEvent from a given element. */\n emit: (el: Element, name: string, detail?: unknown) => void;\n\n /** Register a module definition. */\n register: (def: AdLibModuleDefinition) => void;\n\n /** Unregister a module definition by key. */\n unregister: (key: ModuleKey) => void;\n\n /** Mount a module by key, optionally on a given root. */\n mount: (key: ModuleKey, root?: ParentNode) => void;\n\n /** Unmount a module instance by key. */\n unmount: (key: ModuleKey) => void;\n\n /** Mount all registered modules. */\n mountAll: (root?: ParentNode) => void;\n\n /** Unmount all mounted modules. */\n unmountAll: () => void;\n\n /**\n * Observe dynamic DOM changes on a given root.\n *\n * Rôle :\n * - fournir un signal de changement DOM (ajouts/retraits de nœuds)\n * - permettre au runtime d’orchestrer un rescan/remount si nécessaire\n *\n * Retourne un disposer pour arrêter l’observation.\n */\n observe: (root?: ParentNode) => Disposer;\n\n /** Stop observing dynamic DOM changes. */\n stopObserving: () => void;\n\n /** Destroy core: unmount all, remove listeners, disconnect observer. */\n destroy: () => void;\n\n /** Core logger (debug/warn/error). */\n getLogger: () => CoreLogger;\n}\n\n/** Module key type (ex: 'ad-click'). */\nexport type ModuleKey = `ad-${string}`;\n\nexport interface AdLibModuleInstance {\n key: ModuleKey;\n destroy(): void;\n restart?(options?: { rescan?: boolean }): void;\n}\n\nexport interface AdLibModuleDefinition {\n key: ModuleKey;\n init(core: Core): AdLibModuleInstance;\n}\n\n/* --------------------------------- Internals -------------------------------- */\n\ntype DelegatedEntry = {\n selector: string;\n handler: DelegatedHandler;\n once: boolean;\n};\n\ntype EventListenerInfo = {\n capture: boolean;\n passive: boolean;\n listener: (e: Event) => void;\n entries: DelegatedEntry[];\n};\n\nfunction createLogger(debug = false): CoreLogger {\n return {\n debug: (...args: unknown[]) => {\n if (!debug) return;\n console.warn('[debug]', ...args);\n },\n warn: (...args: unknown[]) => {\n console.warn('[warn]', ...args);\n },\n error: (...args: unknown[]) => {\n console.error('[error]', ...args);\n },\n };\n}\n\nfunction isElement(value: unknown): value is Element {\n // Garde-fou typé : vérifie l’existence de Element (tests / environnements non DOM).\n return typeof Element !== 'undefined' && value instanceof Element;\n}\n\nfunction safeMatches(el: Element, selector: string): boolean {\n try {\n return el.matches(selector);\n } catch {\n return false;\n }\n}\n\nfunction findClosestMatching(target: EventTarget | null, selector: string): Element | null {\n let cur: EventTarget | null = target;\n\n while (cur) {\n if (isElement(cur) && safeMatches(cur, selector)) return cur;\n cur = isElement(cur) ? cur.parentElement : null;\n }\n\n return null;\n}\n\nfunction createDelegatedListener(info: EventListenerInfo) {\n return (e: Event) => {\n // Copie défensive : la liste peut être modifiée par un handler \"once\".\n const entries = info.entries.slice();\n\n for (const entry of entries) {\n const matched = findClosestMatching(e.target, entry.selector);\n if (!matched) continue;\n\n entry.handler(e, matched);\n\n if (entry.once) {\n const idx = info.entries.indexOf(entry);\n if (idx >= 0) info.entries.splice(idx, 1);\n }\n }\n };\n}\n\nfunction resolveObserverTarget(root: ParentNode): Node {\n // MutationObserver attend un Node.\n // - Document => document.documentElement si disponible, sinon le document lui-même\n // - ShadowRoot / Element => observe directement\n if (root instanceof Document) return root.documentElement ?? root;\n return root as unknown as Node;\n}\n\n/* ---------------------------------- Core ---------------------------------- */\n\nexport function createCore(options: CoreOptions = {}): Core {\n const root: Document | ShadowRoot = options.root ?? document;\n const logger = createLogger(!!options.debug);\n\n const defs = new Map<ModuleKey, AdLibModuleDefinition>();\n const instances = new Map<ModuleKey, AdLibModuleInstance>();\n\n const listenersByType = new Map<string, EventListenerInfo>();\n\n function on(type: string, selector: string, handler: DelegatedHandler, opts: OnOptions = {}): Disposer {\n const capture = !!opts.capture;\n const passive = !!opts.passive;\n\n const existing = listenersByType.get(type);\n if (existing) {\n existing.entries.push({ selector, handler, once: !!opts.once });\n\n return () => {\n const idx = existing.entries.findIndex((e) => e.selector === selector && e.handler === handler);\n if (idx >= 0) existing.entries.splice(idx, 1);\n\n // ✅ Si c'était le dernier handler, on retire le listener racine et on nettoie la map\n if (existing.entries.length === 0) {\n root.removeEventListener(type, existing.listener, { capture: existing.capture });\n listenersByType.delete(type);\n }\n };\n }\n\n const info: EventListenerInfo = {\n capture,\n passive,\n entries: [{ selector, handler, once: !!opts.once }],\n listener: () => {},\n };\n\n const listener = createDelegatedListener(info);\n info.listener = listener;\n\n listenersByType.set(type, info);\n\n root.addEventListener(type, listener, { capture, passive });\n\n // ✅ Disposer “handler-level” (pas “type-level”)\n return () => {\n const cur = listenersByType.get(type);\n if (!cur) return;\n\n const idx = cur.entries.findIndex((e) => e.selector === selector && e.handler === handler);\n if (idx >= 0) cur.entries.splice(idx, 1);\n\n if (cur.entries.length === 0) {\n root.removeEventListener(type, cur.listener, { capture: cur.capture });\n listenersByType.delete(type);\n }\n };\n }\n\n function emit(el: Element, name: string, detail?: unknown) {\n const ev = new CustomEvent(name, { detail, bubbles: true });\n el.dispatchEvent(ev);\n }\n\n function register(def: AdLibModuleDefinition) {\n defs.set(def.key, def);\n }\n\n function unregister(key: ModuleKey) {\n defs.delete(key);\n }\n\n function mount(key: ModuleKey, _root?: ParentNode) {\n const def = defs.get(key);\n if (!def) return;\n if (instances.has(key)) return;\n\n const inst = def.init(api);\n instances.set(key, inst);\n }\n\n function unmount(key: ModuleKey) {\n const inst = instances.get(key);\n if (!inst) return;\n inst.destroy();\n instances.delete(key);\n }\n\n function mountAll(_root?: ParentNode) {\n for (const key of defs.keys()) mount(key);\n }\n\n function unmountAll() {\n for (const key of instances.keys()) unmount(key);\n }\n\n let observer: MutationObserver | null = null;\n\n function observe(observedRoot: ParentNode = root): Disposer {\n if (observer) return () => {};\n\n observer = new MutationObserver((_mutations) => {\n // Signal DOM : le runtime est responsable de décider quoi charger / recharger.\n // Le core garantit uniquement une observation stable et désinscriptible.\n });\n\n observer.observe(resolveObserverTarget(observedRoot), {\n subtree: true,\n childList: true,\n attributes: false,\n });\n\n return () => stopObserving();\n }\n\n function stopObserving() {\n if (!observer) return;\n observer.disconnect();\n observer = null;\n }\n\n function destroy() {\n stopObserving();\n unmountAll();\n\n for (const [type, info] of listenersByType.entries()) {\n root.removeEventListener(type, info.listener, { capture: info.capture });\n }\n\n listenersByType.clear();\n defs.clear();\n instances.clear();\n }\n\n function getLogger() {\n return logger;\n }\n\n const api: Core = {\n on,\n emit,\n register,\n unregister,\n mount,\n unmount,\n mountAll,\n unmountAll,\n observe,\n stopObserving,\n destroy,\n getLogger,\n };\n\n return api;\n}\n\n/* ----------------------------- Singleton helpers ---------------------------- */\n\nlet singleton: Core | null = null;\n\nexport function getCoreSingleton(options: CoreOptions = {}): Core {\n if (singleton) return singleton;\n singleton = createCore({ autoMount: true, ...options });\n\n const shouldAutoMount = options.autoMount ?? true;\n if (shouldAutoMount && typeof document !== 'undefined') {\n if (document.readyState === 'loading') {\n document.addEventListener(\n 'DOMContentLoaded',\n () => {\n singleton?.mountAll();\n },\n { once: true }\n );\n } else {\n singleton.mountAll();\n }\n }\n\n return singleton;\n}\n\nexport default createCore;"]}