lifecycleion 0.0.20 → 0.0.21

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 (72) hide show
  1. package/README.md +39 -36
  2. package/dist/lib/event-emitter.cjs +195 -9
  3. package/dist/lib/event-emitter.cjs.map +1 -1
  4. package/dist/lib/event-emitter.js +195 -9
  5. package/dist/lib/event-emitter.js.map +1 -1
  6. package/dist/lib/global-event-target.cjs +217 -0
  7. package/dist/lib/global-event-target.cjs.map +1 -0
  8. package/dist/lib/global-event-target.d.cts +70 -0
  9. package/dist/lib/global-event-target.d.ts +70 -0
  10. package/dist/lib/global-event-target.js +189 -0
  11. package/dist/lib/global-event-target.js.map +1 -0
  12. package/dist/lib/http-client/index.cjs +554 -193
  13. package/dist/lib/http-client/index.cjs.map +1 -1
  14. package/dist/lib/http-client/index.d.cts +9 -3
  15. package/dist/lib/http-client/index.d.ts +9 -3
  16. package/dist/lib/http-client/index.js +554 -193
  17. package/dist/lib/http-client/index.js.map +1 -1
  18. package/dist/lib/http-client-mock/index.cjs +71 -10
  19. package/dist/lib/http-client-mock/index.cjs.map +1 -1
  20. package/dist/lib/http-client-mock/index.d.cts +50 -3
  21. package/dist/lib/http-client-mock/index.d.ts +50 -3
  22. package/dist/lib/http-client-mock/index.js +71 -10
  23. package/dist/lib/http-client-mock/index.js.map +1 -1
  24. package/dist/lib/http-client-node/index.cjs +98 -19
  25. package/dist/lib/http-client-node/index.cjs.map +1 -1
  26. package/dist/lib/http-client-node/index.d.cts +2 -2
  27. package/dist/lib/http-client-node/index.d.ts +2 -2
  28. package/dist/lib/http-client-node/index.js +98 -19
  29. package/dist/lib/http-client-node/index.js.map +1 -1
  30. package/dist/lib/http-client-xhr/index.cjs.map +1 -1
  31. package/dist/lib/http-client-xhr/index.d.cts +2 -2
  32. package/dist/lib/http-client-xhr/index.d.ts +2 -2
  33. package/dist/lib/http-client-xhr/index.js.map +1 -1
  34. package/dist/lib/lifecycle-manager/index.cjs +199 -13
  35. package/dist/lib/lifecycle-manager/index.cjs.map +1 -1
  36. package/dist/lib/lifecycle-manager/index.js +199 -13
  37. package/dist/lib/lifecycle-manager/index.js.map +1 -1
  38. package/dist/lib/logger/index.cjs +203 -21
  39. package/dist/lib/logger/index.cjs.map +1 -1
  40. package/dist/lib/logger/index.d.cts +5 -1
  41. package/dist/lib/logger/index.d.ts +5 -1
  42. package/dist/lib/logger/index.js +203 -21
  43. package/dist/lib/logger/index.js.map +1 -1
  44. package/dist/lib/lru-cache/index.cjs +195 -9
  45. package/dist/lib/lru-cache/index.cjs.map +1 -1
  46. package/dist/lib/lru-cache/index.js +195 -9
  47. package/dist/lib/lru-cache/index.js.map +1 -1
  48. package/dist/lib/process-signal-manager.cjs +199 -13
  49. package/dist/lib/process-signal-manager.cjs.map +1 -1
  50. package/dist/lib/process-signal-manager.js +199 -13
  51. package/dist/lib/process-signal-manager.js.map +1 -1
  52. package/dist/lib/promise-protected-resolver.cjs +195 -9
  53. package/dist/lib/promise-protected-resolver.cjs.map +1 -1
  54. package/dist/lib/promise-protected-resolver.js +195 -9
  55. package/dist/lib/promise-protected-resolver.js.map +1 -1
  56. package/dist/lib/retry-utils/index.cjs +195 -9
  57. package/dist/lib/retry-utils/index.cjs.map +1 -1
  58. package/dist/lib/retry-utils/index.js +195 -9
  59. package/dist/lib/retry-utils/index.js.map +1 -1
  60. package/dist/lib/safe-handle-callback.cjs +196 -18
  61. package/dist/lib/safe-handle-callback.cjs.map +1 -1
  62. package/dist/lib/safe-handle-callback.d.cts +14 -4
  63. package/dist/lib/safe-handle-callback.d.ts +14 -4
  64. package/dist/lib/safe-handle-callback.js +196 -18
  65. package/dist/lib/safe-handle-callback.js.map +1 -1
  66. package/dist/lib/single-event-observer.cjs +195 -9
  67. package/dist/lib/single-event-observer.cjs.map +1 -1
  68. package/dist/lib/single-event-observer.js +195 -9
  69. package/dist/lib/single-event-observer.js.map +1 -1
  70. package/dist/{types-6G59m8U9.d.cts → types-BFvpJPMt.d.cts} +71 -10
  71. package/dist/{types-CG10j6Lc.d.ts → types-BONtIhQ5.d.ts} +71 -10
  72. package/package.json +12 -2
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/global-event-target.ts"],"sourcesContent":["/**\n * Global event target polyfill.\n *\n * Some Lifecycleion libraries (`safe-handle-callback`, `logger`) report errors using\n * Lifecycleion's `'reportError'` convention: an `ErrorEvent` dispatched through\n * `globalThis.dispatchEvent()` and observed with `globalThis.addEventListener('reportError', ...)`.\n *\n * Browsers, Bun, and Deno expose the `EventTarget` methods on the global object, so the\n * convention works there out of the box. Node.js does not: as of Node 25 the `ErrorEvent`\n * constructor is a global, but `globalThis` is still not an `EventTarget`, so\n * `globalThis.addEventListener` / `removeEventListener` / `dispatchEvent` are all `undefined`.\n *\n * This module fills that gap by backing the three missing methods with a single shared\n * `EventTarget` instance. It is deliberately conservative:\n *\n * - It only installs when all three methods are absent, so native or user-installed\n * implementations are never overwritten.\n * - A partial/foreign implementation (some methods present, others missing) is left alone\n * rather than mixing methods from unrelated targets.\n * - Installation is idempotent, and the backing target is shared across repeated\n * initialization and across multiple copies of Lifecycleion.\n * - Installed properties are non-enumerable and bound to the backing target.\n */\n\n// Stored in globalThis so state survives module re-evaluation and works across\n// module boundaries, the same way `dev-mode` and `process-signal-manager` do it.\nconst GLOBAL_KEY = '__lifecycleion_global_event_target__';\n\nconst METHOD_NAMES = [\n 'addEventListener',\n 'removeEventListener',\n 'dispatchEvent',\n] as const;\n\n/**\n * Shared state: the backing target, plus whether the global methods in place are ours.\n */\ninterface GlobalEventTargetState {\n target: EventTarget;\n isInstalled: boolean;\n}\n\n/**\n * Outcome of {@link installGlobalEventTarget}.\n *\n * - `native` - the environment already provides all three methods (browser, Bun, Deno).\n * - `installed` - this call installed the polyfill.\n * - `already-installed` - a previous call (or another copy of Lifecycleion) installed it.\n * - `partial` - the environment provides some but not all of the methods; nothing was\n * installed, because mixing a foreign implementation with a polyfilled one would\n * dispatch to a different target than listeners registered through it.\n * - `unsupported` - the methods are missing and there is nothing to back them with:\n * either no `EventTarget` constructor at all, or one that cannot produce an\n * instance able to service the three methods (it throws, or hands back\n * something unusable).\n * - `blocked` - the global object refused the definition (it is non-extensible, or one of\n * the properties already exists and is non-configurable). Nothing was installed.\n */\nexport type GlobalEventTargetInstallResult =\n | 'native'\n | 'installed'\n | 'already-installed'\n | 'partial'\n | 'unsupported'\n | 'blocked';\n\n// Cast once at module level — avoids repeating the assertion in every function.\nconst g = globalThis as typeof globalThis & Record<string, unknown>;\n\nfunction getState(): GlobalEventTargetState | undefined {\n const value = readGlobal(GLOBAL_KEY);\n\n return value === UNREADABLE ? undefined : asUsableState(value);\n}\n\n/**\n * Narrow a value found at {@link GLOBAL_KEY} to state this module can reuse.\n *\n * The value comes from a shared global, so it may be anything: another copy of\n * Lifecycleion, a test stub, or an object built to misbehave. Every field is read\n * through {@link readMember}, since a getter can throw. A value that does not\n * check out is treated as absent, so installation replaces it — this key is\n * Lifecycleion's own namespace.\n */\nfunction asUsableState(value: unknown): GlobalEventTargetState | undefined {\n if (typeof value !== 'object' || value === null) {\n return undefined;\n }\n\n return asUsableTarget(readMember(value, 'target')) === null\n ? undefined\n : (value as GlobalEventTargetState);\n}\n\n/**\n * Narrow a value read from the `target` field to a usable backing target.\n *\n * Applied on every read rather than once: the field belongs to an object this\n * module does not own, so a getter may return a real `EventTarget` the first time\n * and something else the next.\n */\nfunction asUsableTarget(value: unknown): EventTarget | null {\n if (typeof value !== 'object' || value === null) {\n return null;\n }\n\n // A usable backing target is one that can actually service all three methods.\n for (const name of METHOD_NAMES) {\n if (typeof readMember(value, name) !== 'function') {\n return null;\n }\n }\n\n return value as EventTarget;\n}\n\n/**\n * Read the three methods off a candidate target and bind them, in one pass.\n *\n * The values installed on the global object come from here rather than from a\n * later `target[name]` in the definition loop — that would reopen the\n * validate-once-use-a-later-read gap {@link asUsableTarget} closes, one level in.\n *\n * `null` means this target cannot back an install: a method missing, no longer\n * callable, whose read or `bind` threw, or whose `bind` returned a non-function.\n * The caller falls back to a fresh target rather than failing.\n *\n * @returns The bound methods by name, or `null` if any could not be taken.\n */\nfunction bindTargetMethods(\n target: EventTarget,\n): Record<string, (...args: unknown[]) => unknown> | null {\n const bound: Record<string, (...args: unknown[]) => unknown> = {};\n\n for (const name of METHOD_NAMES) {\n const method = readMember(target, name);\n\n if (typeof method !== 'function') {\n return null;\n }\n\n let boundMethod: unknown;\n\n try {\n boundMethod = (method as (...args: unknown[]) => unknown).bind(target);\n } catch {\n // `bind` is itself a property read on somebody else's function.\n return null;\n }\n\n // What `bind` handed back is checked, not assumed: `bind` is an ordinary\n // property, so a hostile target can replace it with something that returns a\n // non-function without throwing. Guarding only the throw would leave\n // `globalThis.addEventListener` set to a non-callable and report\n // `'installed'` — worse than not installing, since the methods were absent.\n if (typeof boundMethod !== 'function') {\n return null;\n }\n\n bound[name] = boundMethod as (...args: unknown[]) => unknown;\n }\n\n return bound;\n}\n\n/** Whether shared state records a completed install by this module. */\nfunction isStateInstalled(state: GlobalEventTargetState | undefined): boolean {\n return state !== undefined && readMember(state, 'isInstalled') === true;\n}\n\n/**\n * The backing target from shared state, or `null` when there is none to vouch for.\n *\n * {@link asUsableState} validated this field but returns the original object, so\n * this is a fresh read of somebody else's property and is re-validated.\n */\nfunction readStateTarget(\n state: GlobalEventTargetState | undefined,\n): EventTarget | null {\n if (state === undefined) {\n return null;\n }\n\n return asUsableTarget(readMember(state, 'target'));\n}\n\n/** Returned by {@link readGlobal} when the read threw, so it cannot collide with a value. */\nconst UNREADABLE = Symbol('lifecycleion.unreadable');\n\n/**\n * Read a global without trusting it.\n *\n * A plain property read invokes any getter defined for it, and a getter is free to\n * throw. Every read here runs during module initialization, so an unguarded one would\n * crash the import of `safe-handle-callback` and everything downstream of it.\n *\n * @returns The value, or {@link UNREADABLE} if reading it threw.\n */\nfunction readGlobal(name: string): unknown {\n try {\n return g[name];\n } catch {\n return UNREADABLE;\n }\n}\n\n/**\n * Read a property from an object that may be hostile, without trusting it. Same\n * reasoning as {@link readGlobal}, one level in: the shared state object is\n * reachable by anyone, so its accessors are not ours to rely on.\n */\nfunction readMember(source: object, key: string): unknown {\n try {\n return (source as Record<string, unknown>)[key];\n } catch {\n return UNREADABLE;\n }\n}\n\n/**\n * What a global event method currently holds.\n *\n * - `usable` - a callable value.\n * - `occupied` - present, but something we cannot use (`null`, an object, ...).\n * - `absent` - `undefined`, which is indistinguishable from never having been set.\n * - `hostile` - reading it threw, so the property is an accessor belonging to somebody\n * else. It cannot be probed, and must not be touched.\n */\ntype MemberState = 'usable' | 'occupied' | 'absent' | 'hostile';\n\nfunction probeMember(name: string): MemberState {\n const value = readGlobal(name);\n\n if (value === UNREADABLE) {\n return 'hostile';\n }\n\n if (typeof value === 'function') {\n return 'usable';\n }\n\n return value === undefined ? 'absent' : 'occupied';\n}\n\n/**\n * Whether a property can be defined on the global object without throwing.\n *\n * `Object.defineProperty` throws on a non-extensible target when the property does not\n * already exist, and on any target when an existing property is non-configurable and\n * cannot be redefined. Both are possible after `Object.freeze(globalThis)` or\n * `Object.preventExtensions(globalThis)`, and this function runs during module\n * initialization — a throw here would break the import of every dependent module.\n */\nfunction canDefineProperty(key: string): boolean {\n const descriptor = Object.getOwnPropertyDescriptor(g, key);\n\n if (descriptor === undefined) {\n return Object.isExtensible(g);\n }\n\n return descriptor.configurable === true;\n}\n\n/**\n * Install `addEventListener`, `removeEventListener`, and `dispatchEvent` on `globalThis`\n * when — and only when — all three are missing.\n *\n * Safe to call any number of times; repeated calls keep the same backing target.\n *\n * @returns What the call did, or why it did nothing. See {@link GlobalEventTargetInstallResult}.\n */\nexport function installGlobalEventTarget(): GlobalEventTargetInstallResult {\n // Occupied-but-unusable is somebody else's doing and must not be overwritten; a plain\n // `undefined` is treated as absent, since assigning `undefined` is the ordinary way to\n // clear a global and is indistinguishable from never having set it.\n const members = METHOD_NAMES.map((name) => probeMember(name));\n\n if (members.every((member) => member === 'usable')) {\n // Never overwrite an existing implementation, ours or the environment's.\n return isStateInstalled(getState()) ? 'already-installed' : 'native';\n }\n\n if (members.some((member) => member !== 'absent')) {\n // Partial/incompatible surface: some member exists but the set is unusable as a\n // whole. Filling in the gaps would split listeners and dispatches across two\n // unrelated targets, and clobbering what is there would destroy someone else's\n // work — so leave the environment exactly as it is. A `hostile` member lands here\n // too: it cannot be read, so it certainly cannot be replaced safely.\n return 'partial';\n }\n\n // Captured rather than read twice: the constructor is a global like any other, so it\n // can be an accessor that throws, and re-reading it could yield something else.\n const eventTargetConstructor = readGlobal('EventTarget');\n\n if (typeof eventTargetConstructor !== 'function') {\n // Covers UNREADABLE too: a constructor we cannot read is one we cannot use.\n return 'unsupported';\n }\n\n // Preflight every property before touching anything, so a frozen or sealed global\n // object leaves with nothing half-installed — and, more importantly, without throwing\n // out of the module initialization that calls this.\n const isDefinable = [...METHOD_NAMES, GLOBAL_KEY].every((key) =>\n canDefineProperty(key),\n );\n\n if (!isDefinable) {\n return 'blocked';\n }\n\n const existingState = getState();\n const defined: string[] = [];\n\n // The state key as found, so a rollback can put back what was there.\n //\n // This call cannot be the first to throw: the preflight above already read\n // this exact descriptor through `canDefineProperty`, so an object hostile\n // enough to reject the read has already returned `'blocked'`.\n const priorStateDescriptor = Object.getOwnPropertyDescriptor(g, GLOBAL_KEY);\n\n /** Undo a partial installation when a later definition is rejected after all. */\n const rollback = (): void => {\n for (const key of defined) {\n // The state key may have held somebody else's object that this call\n // overwrote. Deleting it is not an undo there — a failed install would have\n // destroyed that object as a side effect, the one thing rollback exists to\n // prevent — so the original descriptor goes back instead.\n //\n // Restored in place rather than deleted first: whatever rejected the\n // definition may have sealed the global object, and re-adding a key to a\n // non-extensible object throws, while redefining a configurable one that\n // never left is always allowed.\n if (key === GLOBAL_KEY && priorStateDescriptor !== undefined) {\n try {\n Object.defineProperty(g, GLOBAL_KEY, priorStateDescriptor);\n } catch {\n // Nothing better to do here: the original could not be put back.\n }\n\n continue;\n }\n\n try {\n Reflect.deleteProperty(g, key);\n } catch {\n // Nothing better to do here: the property could not be defined *or* removed.\n }\n }\n };\n\n // Re-read `target` rather than reusing what `asUsableState` saw, and take the\n // three methods off it in the same pass. These are the values the globals are\n // actually bound to, so these are the reads that have to validate; trusting the\n // earlier probe would bind them to something that never passed a check.\n //\n // A target that no longer vouches for itself is treated as absent, so a fresh\n // one is made. Nothing is orphaned: a target that cannot service the three\n // methods never had listeners registered through this module.\n const reusableTarget = readStateTarget(existingState);\n const reusableMethods =\n reusableTarget === null ? null : bindTargetMethods(reusableTarget);\n\n let state: GlobalEventTargetState;\n let methods: Record<string, (...args: unknown[]) => unknown>;\n\n try {\n if (existingState === undefined || reusableMethods === null) {\n let target: EventTarget;\n\n try {\n target = new (eventTargetConstructor as new () => EventTarget)();\n } catch {\n // The constructor read from the global object cannot produce an\n // instance at all (a shim exposing `EventTarget` as non-constructible\n // throws here), so there is nothing to back the methods with. Reported\n // the same way as a constructor whose instances are unusable: nothing\n // was ever offered to the global object, so `'blocked'` would be the\n // wrong story, and nothing has been defined yet to undo.\n return 'unsupported';\n }\n\n const freshMethods = bindTargetMethods(target);\n\n if (freshMethods === null) {\n // The constructor read from the global object produced something that\n // cannot service the three methods, so there is nothing to back them\n // with. Nothing has been defined yet, so there is nothing to undo.\n return 'unsupported';\n }\n\n state = { target, isInstalled: false };\n methods = freshMethods;\n\n Object.defineProperty(g, GLOBAL_KEY, {\n value: state,\n enumerable: false,\n writable: false,\n configurable: true,\n });\n\n defined.push(GLOBAL_KEY);\n } else {\n // Another copy of Lifecycleion already made the target; reuse it so listeners\n // registered through it keep working.\n state = existingState;\n methods = reusableMethods;\n }\n\n for (const name of METHOD_NAMES) {\n Object.defineProperty(g, name, {\n value: methods[name],\n enumerable: false,\n writable: true,\n configurable: true,\n });\n\n defined.push(name);\n }\n\n // Flipped last, so the state only claims an install once all three are in\n // place, and inside the guarded block because a shared state object may be\n // frozen or have a throwing setter, which must not escape module init.\n //\n // Skipped when the flag already reads `true`: a non-writable property throws\n // on assignment in strict mode even for a same-value write. Otherwise a\n // frozen state left by a copy that already installed would roll back the\n // three methods just defined and report `blocked`, leaving the global with no\n // event methods. A frozen state still reading `false` keeps failing, as it\n // must: nothing can record that install.\n if (readMember(state, 'isInstalled') !== true) {\n state.isInstalled = true;\n }\n } catch {\n // The preflight passed but a definition or the state write was still rejected\n // (an exotic global object, a Proxy trap, a host restriction, frozen shared\n // state). Leave nothing behind and report no install.\n rollback();\n\n return 'blocked';\n }\n\n return 'installed';\n}\n\n/**\n * Get the shared `EventTarget` backing the polyfilled global methods.\n *\n * @returns The backing target, or `null` when the polyfill is not in use (native\n * environments, or environments where installation was skipped).\n */\nexport function getGlobalEventTarget(): EventTarget | null {\n return readStateTarget(getState());\n}\n\n/**\n * Whether the global event methods currently in place were installed by this polyfill.\n *\n * Reflects what was installed, not what is callable right now: an application that\n * replaces the global methods afterwards does not reset this.\n */\nexport function isGlobalEventTargetPolyfilled(): boolean {\n return isStateInstalled(getState());\n}\n\n/**\n * Whether `globalThis` exposes everything Lifecycleion's `'reportError'` convention needs:\n * the three `EventTarget` methods plus the `ErrorEvent` constructor.\n */\nexport function isGlobalEventTargetAvailable(): boolean {\n return (\n METHOD_NAMES.every((name) => probeMember(name) === 'usable') &&\n probeMember('ErrorEvent') === 'usable'\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0BA,IAAM,aAAa;AAEnB,IAAM,eAAe;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AACF;AAmCA,IAAM,IAAI;AAEV,SAAS,WAA+C;AACtD,QAAM,QAAQ,WAAW,UAAU;AAEnC,SAAO,UAAU,aAAa,SAAY,cAAc,KAAK;AAC/D;AAWA,SAAS,cAAc,OAAoD;AACzE,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,WAAO;AAAA,EACT;AAEA,SAAO,eAAe,WAAW,OAAO,QAAQ,CAAC,MAAM,OACnD,SACC;AACP;AASA,SAAS,eAAe,OAAoC;AAC1D,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,WAAO;AAAA,EACT;AAGA,aAAW,QAAQ,cAAc;AAC/B,QAAI,OAAO,WAAW,OAAO,IAAI,MAAM,YAAY;AACjD,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAeA,SAAS,kBACP,QACwD;AACxD,QAAM,QAAyD,CAAC;AAEhE,aAAW,QAAQ,cAAc;AAC/B,UAAM,SAAS,WAAW,QAAQ,IAAI;AAEtC,QAAI,OAAO,WAAW,YAAY;AAChC,aAAO;AAAA,IACT;AAEA,QAAI;AAEJ,QAAI;AACF,oBAAe,OAA2C,KAAK,MAAM;AAAA,IACvE,QAAQ;AAEN,aAAO;AAAA,IACT;AAOA,QAAI,OAAO,gBAAgB,YAAY;AACrC,aAAO;AAAA,IACT;AAEA,UAAM,IAAI,IAAI;AAAA,EAChB;AAEA,SAAO;AACT;AAGA,SAAS,iBAAiB,OAAoD;AAC5E,SAAO,UAAU,UAAa,WAAW,OAAO,aAAa,MAAM;AACrE;AAQA,SAAS,gBACP,OACoB;AACpB,MAAI,UAAU,QAAW;AACvB,WAAO;AAAA,EACT;AAEA,SAAO,eAAe,WAAW,OAAO,QAAQ,CAAC;AACnD;AAGA,IAAM,aAAa,uBAAO,yBAAyB;AAWnD,SAAS,WAAW,MAAuB;AACzC,MAAI;AACF,WAAO,EAAE,IAAI;AAAA,EACf,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOA,SAAS,WAAW,QAAgB,KAAsB;AACxD,MAAI;AACF,WAAQ,OAAmC,GAAG;AAAA,EAChD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAaA,SAAS,YAAY,MAA2B;AAC9C,QAAM,QAAQ,WAAW,IAAI;AAE7B,MAAI,UAAU,YAAY;AACxB,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,UAAU,YAAY;AAC/B,WAAO;AAAA,EACT;AAEA,SAAO,UAAU,SAAY,WAAW;AAC1C;AAWA,SAAS,kBAAkB,KAAsB;AAC/C,QAAM,aAAa,OAAO,yBAAyB,GAAG,GAAG;AAEzD,MAAI,eAAe,QAAW;AAC5B,WAAO,OAAO,aAAa,CAAC;AAAA,EAC9B;AAEA,SAAO,WAAW,iBAAiB;AACrC;AAUO,SAAS,2BAA2D;AAIzE,QAAM,UAAU,aAAa,IAAI,CAAC,SAAS,YAAY,IAAI,CAAC;AAE5D,MAAI,QAAQ,MAAM,CAAC,WAAW,WAAW,QAAQ,GAAG;AAElD,WAAO,iBAAiB,SAAS,CAAC,IAAI,sBAAsB;AAAA,EAC9D;AAEA,MAAI,QAAQ,KAAK,CAAC,WAAW,WAAW,QAAQ,GAAG;AAMjD,WAAO;AAAA,EACT;AAIA,QAAM,yBAAyB,WAAW,aAAa;AAEvD,MAAI,OAAO,2BAA2B,YAAY;AAEhD,WAAO;AAAA,EACT;AAKA,QAAM,cAAc,CAAC,GAAG,cAAc,UAAU,EAAE;AAAA,IAAM,CAAC,QACvD,kBAAkB,GAAG;AAAA,EACvB;AAEA,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,EACT;AAEA,QAAM,gBAAgB,SAAS;AAC/B,QAAM,UAAoB,CAAC;AAO3B,QAAM,uBAAuB,OAAO,yBAAyB,GAAG,UAAU;AAG1E,QAAM,WAAW,MAAY;AAC3B,eAAW,OAAO,SAAS;AAUzB,UAAI,QAAQ,cAAc,yBAAyB,QAAW;AAC5D,YAAI;AACF,iBAAO,eAAe,GAAG,YAAY,oBAAoB;AAAA,QAC3D,QAAQ;AAAA,QAER;AAEA;AAAA,MACF;AAEA,UAAI;AACF,gBAAQ,eAAe,GAAG,GAAG;AAAA,MAC/B,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAUA,QAAM,iBAAiB,gBAAgB,aAAa;AACpD,QAAM,kBACJ,mBAAmB,OAAO,OAAO,kBAAkB,cAAc;AAEnE,MAAI;AACJ,MAAI;AAEJ,MAAI;AACF,QAAI,kBAAkB,UAAa,oBAAoB,MAAM;AAC3D,UAAI;AAEJ,UAAI;AACF,iBAAS,IAAK,uBAAiD;AAAA,MACjE,QAAQ;AAON,eAAO;AAAA,MACT;AAEA,YAAM,eAAe,kBAAkB,MAAM;AAE7C,UAAI,iBAAiB,MAAM;AAIzB,eAAO;AAAA,MACT;AAEA,cAAQ,EAAE,QAAQ,aAAa,MAAM;AACrC,gBAAU;AAEV,aAAO,eAAe,GAAG,YAAY;AAAA,QACnC,OAAO;AAAA,QACP,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,cAAc;AAAA,MAChB,CAAC;AAED,cAAQ,KAAK,UAAU;AAAA,IACzB,OAAO;AAGL,cAAQ;AACR,gBAAU;AAAA,IACZ;AAEA,eAAW,QAAQ,cAAc;AAC/B,aAAO,eAAe,GAAG,MAAM;AAAA,QAC7B,OAAO,QAAQ,IAAI;AAAA,QACnB,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,cAAc;AAAA,MAChB,CAAC;AAED,cAAQ,KAAK,IAAI;AAAA,IACnB;AAYA,QAAI,WAAW,OAAO,aAAa,MAAM,MAAM;AAC7C,YAAM,cAAc;AAAA,IACtB;AAAA,EACF,QAAQ;AAIN,aAAS;AAET,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAQO,SAAS,uBAA2C;AACzD,SAAO,gBAAgB,SAAS,CAAC;AACnC;AAQO,SAAS,gCAAyC;AACvD,SAAO,iBAAiB,SAAS,CAAC;AACpC;AAMO,SAAS,+BAAwC;AACtD,SACE,aAAa,MAAM,CAAC,SAAS,YAAY,IAAI,MAAM,QAAQ,KAC3D,YAAY,YAAY,MAAM;AAElC;","names":[]}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Global event target polyfill.
3
+ *
4
+ * Some Lifecycleion libraries (`safe-handle-callback`, `logger`) report errors using
5
+ * Lifecycleion's `'reportError'` convention: an `ErrorEvent` dispatched through
6
+ * `globalThis.dispatchEvent()` and observed with `globalThis.addEventListener('reportError', ...)`.
7
+ *
8
+ * Browsers, Bun, and Deno expose the `EventTarget` methods on the global object, so the
9
+ * convention works there out of the box. Node.js does not: as of Node 25 the `ErrorEvent`
10
+ * constructor is a global, but `globalThis` is still not an `EventTarget`, so
11
+ * `globalThis.addEventListener` / `removeEventListener` / `dispatchEvent` are all `undefined`.
12
+ *
13
+ * This module fills that gap by backing the three missing methods with a single shared
14
+ * `EventTarget` instance. It is deliberately conservative:
15
+ *
16
+ * - It only installs when all three methods are absent, so native or user-installed
17
+ * implementations are never overwritten.
18
+ * - A partial/foreign implementation (some methods present, others missing) is left alone
19
+ * rather than mixing methods from unrelated targets.
20
+ * - Installation is idempotent, and the backing target is shared across repeated
21
+ * initialization and across multiple copies of Lifecycleion.
22
+ * - Installed properties are non-enumerable and bound to the backing target.
23
+ */
24
+ /**
25
+ * Outcome of {@link installGlobalEventTarget}.
26
+ *
27
+ * - `native` - the environment already provides all three methods (browser, Bun, Deno).
28
+ * - `installed` - this call installed the polyfill.
29
+ * - `already-installed` - a previous call (or another copy of Lifecycleion) installed it.
30
+ * - `partial` - the environment provides some but not all of the methods; nothing was
31
+ * installed, because mixing a foreign implementation with a polyfilled one would
32
+ * dispatch to a different target than listeners registered through it.
33
+ * - `unsupported` - the methods are missing and there is nothing to back them with:
34
+ * either no `EventTarget` constructor at all, or one that cannot produce an
35
+ * instance able to service the three methods (it throws, or hands back
36
+ * something unusable).
37
+ * - `blocked` - the global object refused the definition (it is non-extensible, or one of
38
+ * the properties already exists and is non-configurable). Nothing was installed.
39
+ */
40
+ type GlobalEventTargetInstallResult = 'native' | 'installed' | 'already-installed' | 'partial' | 'unsupported' | 'blocked';
41
+ /**
42
+ * Install `addEventListener`, `removeEventListener`, and `dispatchEvent` on `globalThis`
43
+ * when — and only when — all three are missing.
44
+ *
45
+ * Safe to call any number of times; repeated calls keep the same backing target.
46
+ *
47
+ * @returns What the call did, or why it did nothing. See {@link GlobalEventTargetInstallResult}.
48
+ */
49
+ declare function installGlobalEventTarget(): GlobalEventTargetInstallResult;
50
+ /**
51
+ * Get the shared `EventTarget` backing the polyfilled global methods.
52
+ *
53
+ * @returns The backing target, or `null` when the polyfill is not in use (native
54
+ * environments, or environments where installation was skipped).
55
+ */
56
+ declare function getGlobalEventTarget(): EventTarget | null;
57
+ /**
58
+ * Whether the global event methods currently in place were installed by this polyfill.
59
+ *
60
+ * Reflects what was installed, not what is callable right now: an application that
61
+ * replaces the global methods afterwards does not reset this.
62
+ */
63
+ declare function isGlobalEventTargetPolyfilled(): boolean;
64
+ /**
65
+ * Whether `globalThis` exposes everything Lifecycleion's `'reportError'` convention needs:
66
+ * the three `EventTarget` methods plus the `ErrorEvent` constructor.
67
+ */
68
+ declare function isGlobalEventTargetAvailable(): boolean;
69
+
70
+ export { type GlobalEventTargetInstallResult, getGlobalEventTarget, installGlobalEventTarget, isGlobalEventTargetAvailable, isGlobalEventTargetPolyfilled };
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Global event target polyfill.
3
+ *
4
+ * Some Lifecycleion libraries (`safe-handle-callback`, `logger`) report errors using
5
+ * Lifecycleion's `'reportError'` convention: an `ErrorEvent` dispatched through
6
+ * `globalThis.dispatchEvent()` and observed with `globalThis.addEventListener('reportError', ...)`.
7
+ *
8
+ * Browsers, Bun, and Deno expose the `EventTarget` methods on the global object, so the
9
+ * convention works there out of the box. Node.js does not: as of Node 25 the `ErrorEvent`
10
+ * constructor is a global, but `globalThis` is still not an `EventTarget`, so
11
+ * `globalThis.addEventListener` / `removeEventListener` / `dispatchEvent` are all `undefined`.
12
+ *
13
+ * This module fills that gap by backing the three missing methods with a single shared
14
+ * `EventTarget` instance. It is deliberately conservative:
15
+ *
16
+ * - It only installs when all three methods are absent, so native or user-installed
17
+ * implementations are never overwritten.
18
+ * - A partial/foreign implementation (some methods present, others missing) is left alone
19
+ * rather than mixing methods from unrelated targets.
20
+ * - Installation is idempotent, and the backing target is shared across repeated
21
+ * initialization and across multiple copies of Lifecycleion.
22
+ * - Installed properties are non-enumerable and bound to the backing target.
23
+ */
24
+ /**
25
+ * Outcome of {@link installGlobalEventTarget}.
26
+ *
27
+ * - `native` - the environment already provides all three methods (browser, Bun, Deno).
28
+ * - `installed` - this call installed the polyfill.
29
+ * - `already-installed` - a previous call (or another copy of Lifecycleion) installed it.
30
+ * - `partial` - the environment provides some but not all of the methods; nothing was
31
+ * installed, because mixing a foreign implementation with a polyfilled one would
32
+ * dispatch to a different target than listeners registered through it.
33
+ * - `unsupported` - the methods are missing and there is nothing to back them with:
34
+ * either no `EventTarget` constructor at all, or one that cannot produce an
35
+ * instance able to service the three methods (it throws, or hands back
36
+ * something unusable).
37
+ * - `blocked` - the global object refused the definition (it is non-extensible, or one of
38
+ * the properties already exists and is non-configurable). Nothing was installed.
39
+ */
40
+ type GlobalEventTargetInstallResult = 'native' | 'installed' | 'already-installed' | 'partial' | 'unsupported' | 'blocked';
41
+ /**
42
+ * Install `addEventListener`, `removeEventListener`, and `dispatchEvent` on `globalThis`
43
+ * when — and only when — all three are missing.
44
+ *
45
+ * Safe to call any number of times; repeated calls keep the same backing target.
46
+ *
47
+ * @returns What the call did, or why it did nothing. See {@link GlobalEventTargetInstallResult}.
48
+ */
49
+ declare function installGlobalEventTarget(): GlobalEventTargetInstallResult;
50
+ /**
51
+ * Get the shared `EventTarget` backing the polyfilled global methods.
52
+ *
53
+ * @returns The backing target, or `null` when the polyfill is not in use (native
54
+ * environments, or environments where installation was skipped).
55
+ */
56
+ declare function getGlobalEventTarget(): EventTarget | null;
57
+ /**
58
+ * Whether the global event methods currently in place were installed by this polyfill.
59
+ *
60
+ * Reflects what was installed, not what is callable right now: an application that
61
+ * replaces the global methods afterwards does not reset this.
62
+ */
63
+ declare function isGlobalEventTargetPolyfilled(): boolean;
64
+ /**
65
+ * Whether `globalThis` exposes everything Lifecycleion's `'reportError'` convention needs:
66
+ * the three `EventTarget` methods plus the `ErrorEvent` constructor.
67
+ */
68
+ declare function isGlobalEventTargetAvailable(): boolean;
69
+
70
+ export { type GlobalEventTargetInstallResult, getGlobalEventTarget, installGlobalEventTarget, isGlobalEventTargetAvailable, isGlobalEventTargetPolyfilled };
@@ -0,0 +1,189 @@
1
+ // src/lib/global-event-target.ts
2
+ var GLOBAL_KEY = "__lifecycleion_global_event_target__";
3
+ var METHOD_NAMES = [
4
+ "addEventListener",
5
+ "removeEventListener",
6
+ "dispatchEvent"
7
+ ];
8
+ var g = globalThis;
9
+ function getState() {
10
+ const value = readGlobal(GLOBAL_KEY);
11
+ return value === UNREADABLE ? void 0 : asUsableState(value);
12
+ }
13
+ function asUsableState(value) {
14
+ if (typeof value !== "object" || value === null) {
15
+ return void 0;
16
+ }
17
+ return asUsableTarget(readMember(value, "target")) === null ? void 0 : value;
18
+ }
19
+ function asUsableTarget(value) {
20
+ if (typeof value !== "object" || value === null) {
21
+ return null;
22
+ }
23
+ for (const name of METHOD_NAMES) {
24
+ if (typeof readMember(value, name) !== "function") {
25
+ return null;
26
+ }
27
+ }
28
+ return value;
29
+ }
30
+ function bindTargetMethods(target) {
31
+ const bound = {};
32
+ for (const name of METHOD_NAMES) {
33
+ const method = readMember(target, name);
34
+ if (typeof method !== "function") {
35
+ return null;
36
+ }
37
+ let boundMethod;
38
+ try {
39
+ boundMethod = method.bind(target);
40
+ } catch {
41
+ return null;
42
+ }
43
+ if (typeof boundMethod !== "function") {
44
+ return null;
45
+ }
46
+ bound[name] = boundMethod;
47
+ }
48
+ return bound;
49
+ }
50
+ function isStateInstalled(state) {
51
+ return state !== void 0 && readMember(state, "isInstalled") === true;
52
+ }
53
+ function readStateTarget(state) {
54
+ if (state === void 0) {
55
+ return null;
56
+ }
57
+ return asUsableTarget(readMember(state, "target"));
58
+ }
59
+ var UNREADABLE = /* @__PURE__ */ Symbol("lifecycleion.unreadable");
60
+ function readGlobal(name) {
61
+ try {
62
+ return g[name];
63
+ } catch {
64
+ return UNREADABLE;
65
+ }
66
+ }
67
+ function readMember(source, key) {
68
+ try {
69
+ return source[key];
70
+ } catch {
71
+ return UNREADABLE;
72
+ }
73
+ }
74
+ function probeMember(name) {
75
+ const value = readGlobal(name);
76
+ if (value === UNREADABLE) {
77
+ return "hostile";
78
+ }
79
+ if (typeof value === "function") {
80
+ return "usable";
81
+ }
82
+ return value === void 0 ? "absent" : "occupied";
83
+ }
84
+ function canDefineProperty(key) {
85
+ const descriptor = Object.getOwnPropertyDescriptor(g, key);
86
+ if (descriptor === void 0) {
87
+ return Object.isExtensible(g);
88
+ }
89
+ return descriptor.configurable === true;
90
+ }
91
+ function installGlobalEventTarget() {
92
+ const members = METHOD_NAMES.map((name) => probeMember(name));
93
+ if (members.every((member) => member === "usable")) {
94
+ return isStateInstalled(getState()) ? "already-installed" : "native";
95
+ }
96
+ if (members.some((member) => member !== "absent")) {
97
+ return "partial";
98
+ }
99
+ const eventTargetConstructor = readGlobal("EventTarget");
100
+ if (typeof eventTargetConstructor !== "function") {
101
+ return "unsupported";
102
+ }
103
+ const isDefinable = [...METHOD_NAMES, GLOBAL_KEY].every(
104
+ (key) => canDefineProperty(key)
105
+ );
106
+ if (!isDefinable) {
107
+ return "blocked";
108
+ }
109
+ const existingState = getState();
110
+ const defined = [];
111
+ const priorStateDescriptor = Object.getOwnPropertyDescriptor(g, GLOBAL_KEY);
112
+ const rollback = () => {
113
+ for (const key of defined) {
114
+ if (key === GLOBAL_KEY && priorStateDescriptor !== void 0) {
115
+ try {
116
+ Object.defineProperty(g, GLOBAL_KEY, priorStateDescriptor);
117
+ } catch {
118
+ }
119
+ continue;
120
+ }
121
+ try {
122
+ Reflect.deleteProperty(g, key);
123
+ } catch {
124
+ }
125
+ }
126
+ };
127
+ const reusableTarget = readStateTarget(existingState);
128
+ const reusableMethods = reusableTarget === null ? null : bindTargetMethods(reusableTarget);
129
+ let state;
130
+ let methods;
131
+ try {
132
+ if (existingState === void 0 || reusableMethods === null) {
133
+ let target;
134
+ try {
135
+ target = new eventTargetConstructor();
136
+ } catch {
137
+ return "unsupported";
138
+ }
139
+ const freshMethods = bindTargetMethods(target);
140
+ if (freshMethods === null) {
141
+ return "unsupported";
142
+ }
143
+ state = { target, isInstalled: false };
144
+ methods = freshMethods;
145
+ Object.defineProperty(g, GLOBAL_KEY, {
146
+ value: state,
147
+ enumerable: false,
148
+ writable: false,
149
+ configurable: true
150
+ });
151
+ defined.push(GLOBAL_KEY);
152
+ } else {
153
+ state = existingState;
154
+ methods = reusableMethods;
155
+ }
156
+ for (const name of METHOD_NAMES) {
157
+ Object.defineProperty(g, name, {
158
+ value: methods[name],
159
+ enumerable: false,
160
+ writable: true,
161
+ configurable: true
162
+ });
163
+ defined.push(name);
164
+ }
165
+ if (readMember(state, "isInstalled") !== true) {
166
+ state.isInstalled = true;
167
+ }
168
+ } catch {
169
+ rollback();
170
+ return "blocked";
171
+ }
172
+ return "installed";
173
+ }
174
+ function getGlobalEventTarget() {
175
+ return readStateTarget(getState());
176
+ }
177
+ function isGlobalEventTargetPolyfilled() {
178
+ return isStateInstalled(getState());
179
+ }
180
+ function isGlobalEventTargetAvailable() {
181
+ return METHOD_NAMES.every((name) => probeMember(name) === "usable") && probeMember("ErrorEvent") === "usable";
182
+ }
183
+ export {
184
+ getGlobalEventTarget,
185
+ installGlobalEventTarget,
186
+ isGlobalEventTargetAvailable,
187
+ isGlobalEventTargetPolyfilled
188
+ };
189
+ //# sourceMappingURL=global-event-target.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lib/global-event-target.ts"],"sourcesContent":["/**\n * Global event target polyfill.\n *\n * Some Lifecycleion libraries (`safe-handle-callback`, `logger`) report errors using\n * Lifecycleion's `'reportError'` convention: an `ErrorEvent` dispatched through\n * `globalThis.dispatchEvent()` and observed with `globalThis.addEventListener('reportError', ...)`.\n *\n * Browsers, Bun, and Deno expose the `EventTarget` methods on the global object, so the\n * convention works there out of the box. Node.js does not: as of Node 25 the `ErrorEvent`\n * constructor is a global, but `globalThis` is still not an `EventTarget`, so\n * `globalThis.addEventListener` / `removeEventListener` / `dispatchEvent` are all `undefined`.\n *\n * This module fills that gap by backing the three missing methods with a single shared\n * `EventTarget` instance. It is deliberately conservative:\n *\n * - It only installs when all three methods are absent, so native or user-installed\n * implementations are never overwritten.\n * - A partial/foreign implementation (some methods present, others missing) is left alone\n * rather than mixing methods from unrelated targets.\n * - Installation is idempotent, and the backing target is shared across repeated\n * initialization and across multiple copies of Lifecycleion.\n * - Installed properties are non-enumerable and bound to the backing target.\n */\n\n// Stored in globalThis so state survives module re-evaluation and works across\n// module boundaries, the same way `dev-mode` and `process-signal-manager` do it.\nconst GLOBAL_KEY = '__lifecycleion_global_event_target__';\n\nconst METHOD_NAMES = [\n 'addEventListener',\n 'removeEventListener',\n 'dispatchEvent',\n] as const;\n\n/**\n * Shared state: the backing target, plus whether the global methods in place are ours.\n */\ninterface GlobalEventTargetState {\n target: EventTarget;\n isInstalled: boolean;\n}\n\n/**\n * Outcome of {@link installGlobalEventTarget}.\n *\n * - `native` - the environment already provides all three methods (browser, Bun, Deno).\n * - `installed` - this call installed the polyfill.\n * - `already-installed` - a previous call (or another copy of Lifecycleion) installed it.\n * - `partial` - the environment provides some but not all of the methods; nothing was\n * installed, because mixing a foreign implementation with a polyfilled one would\n * dispatch to a different target than listeners registered through it.\n * - `unsupported` - the methods are missing and there is nothing to back them with:\n * either no `EventTarget` constructor at all, or one that cannot produce an\n * instance able to service the three methods (it throws, or hands back\n * something unusable).\n * - `blocked` - the global object refused the definition (it is non-extensible, or one of\n * the properties already exists and is non-configurable). Nothing was installed.\n */\nexport type GlobalEventTargetInstallResult =\n | 'native'\n | 'installed'\n | 'already-installed'\n | 'partial'\n | 'unsupported'\n | 'blocked';\n\n// Cast once at module level — avoids repeating the assertion in every function.\nconst g = globalThis as typeof globalThis & Record<string, unknown>;\n\nfunction getState(): GlobalEventTargetState | undefined {\n const value = readGlobal(GLOBAL_KEY);\n\n return value === UNREADABLE ? undefined : asUsableState(value);\n}\n\n/**\n * Narrow a value found at {@link GLOBAL_KEY} to state this module can reuse.\n *\n * The value comes from a shared global, so it may be anything: another copy of\n * Lifecycleion, a test stub, or an object built to misbehave. Every field is read\n * through {@link readMember}, since a getter can throw. A value that does not\n * check out is treated as absent, so installation replaces it — this key is\n * Lifecycleion's own namespace.\n */\nfunction asUsableState(value: unknown): GlobalEventTargetState | undefined {\n if (typeof value !== 'object' || value === null) {\n return undefined;\n }\n\n return asUsableTarget(readMember(value, 'target')) === null\n ? undefined\n : (value as GlobalEventTargetState);\n}\n\n/**\n * Narrow a value read from the `target` field to a usable backing target.\n *\n * Applied on every read rather than once: the field belongs to an object this\n * module does not own, so a getter may return a real `EventTarget` the first time\n * and something else the next.\n */\nfunction asUsableTarget(value: unknown): EventTarget | null {\n if (typeof value !== 'object' || value === null) {\n return null;\n }\n\n // A usable backing target is one that can actually service all three methods.\n for (const name of METHOD_NAMES) {\n if (typeof readMember(value, name) !== 'function') {\n return null;\n }\n }\n\n return value as EventTarget;\n}\n\n/**\n * Read the three methods off a candidate target and bind them, in one pass.\n *\n * The values installed on the global object come from here rather than from a\n * later `target[name]` in the definition loop — that would reopen the\n * validate-once-use-a-later-read gap {@link asUsableTarget} closes, one level in.\n *\n * `null` means this target cannot back an install: a method missing, no longer\n * callable, whose read or `bind` threw, or whose `bind` returned a non-function.\n * The caller falls back to a fresh target rather than failing.\n *\n * @returns The bound methods by name, or `null` if any could not be taken.\n */\nfunction bindTargetMethods(\n target: EventTarget,\n): Record<string, (...args: unknown[]) => unknown> | null {\n const bound: Record<string, (...args: unknown[]) => unknown> = {};\n\n for (const name of METHOD_NAMES) {\n const method = readMember(target, name);\n\n if (typeof method !== 'function') {\n return null;\n }\n\n let boundMethod: unknown;\n\n try {\n boundMethod = (method as (...args: unknown[]) => unknown).bind(target);\n } catch {\n // `bind` is itself a property read on somebody else's function.\n return null;\n }\n\n // What `bind` handed back is checked, not assumed: `bind` is an ordinary\n // property, so a hostile target can replace it with something that returns a\n // non-function without throwing. Guarding only the throw would leave\n // `globalThis.addEventListener` set to a non-callable and report\n // `'installed'` — worse than not installing, since the methods were absent.\n if (typeof boundMethod !== 'function') {\n return null;\n }\n\n bound[name] = boundMethod as (...args: unknown[]) => unknown;\n }\n\n return bound;\n}\n\n/** Whether shared state records a completed install by this module. */\nfunction isStateInstalled(state: GlobalEventTargetState | undefined): boolean {\n return state !== undefined && readMember(state, 'isInstalled') === true;\n}\n\n/**\n * The backing target from shared state, or `null` when there is none to vouch for.\n *\n * {@link asUsableState} validated this field but returns the original object, so\n * this is a fresh read of somebody else's property and is re-validated.\n */\nfunction readStateTarget(\n state: GlobalEventTargetState | undefined,\n): EventTarget | null {\n if (state === undefined) {\n return null;\n }\n\n return asUsableTarget(readMember(state, 'target'));\n}\n\n/** Returned by {@link readGlobal} when the read threw, so it cannot collide with a value. */\nconst UNREADABLE = Symbol('lifecycleion.unreadable');\n\n/**\n * Read a global without trusting it.\n *\n * A plain property read invokes any getter defined for it, and a getter is free to\n * throw. Every read here runs during module initialization, so an unguarded one would\n * crash the import of `safe-handle-callback` and everything downstream of it.\n *\n * @returns The value, or {@link UNREADABLE} if reading it threw.\n */\nfunction readGlobal(name: string): unknown {\n try {\n return g[name];\n } catch {\n return UNREADABLE;\n }\n}\n\n/**\n * Read a property from an object that may be hostile, without trusting it. Same\n * reasoning as {@link readGlobal}, one level in: the shared state object is\n * reachable by anyone, so its accessors are not ours to rely on.\n */\nfunction readMember(source: object, key: string): unknown {\n try {\n return (source as Record<string, unknown>)[key];\n } catch {\n return UNREADABLE;\n }\n}\n\n/**\n * What a global event method currently holds.\n *\n * - `usable` - a callable value.\n * - `occupied` - present, but something we cannot use (`null`, an object, ...).\n * - `absent` - `undefined`, which is indistinguishable from never having been set.\n * - `hostile` - reading it threw, so the property is an accessor belonging to somebody\n * else. It cannot be probed, and must not be touched.\n */\ntype MemberState = 'usable' | 'occupied' | 'absent' | 'hostile';\n\nfunction probeMember(name: string): MemberState {\n const value = readGlobal(name);\n\n if (value === UNREADABLE) {\n return 'hostile';\n }\n\n if (typeof value === 'function') {\n return 'usable';\n }\n\n return value === undefined ? 'absent' : 'occupied';\n}\n\n/**\n * Whether a property can be defined on the global object without throwing.\n *\n * `Object.defineProperty` throws on a non-extensible target when the property does not\n * already exist, and on any target when an existing property is non-configurable and\n * cannot be redefined. Both are possible after `Object.freeze(globalThis)` or\n * `Object.preventExtensions(globalThis)`, and this function runs during module\n * initialization — a throw here would break the import of every dependent module.\n */\nfunction canDefineProperty(key: string): boolean {\n const descriptor = Object.getOwnPropertyDescriptor(g, key);\n\n if (descriptor === undefined) {\n return Object.isExtensible(g);\n }\n\n return descriptor.configurable === true;\n}\n\n/**\n * Install `addEventListener`, `removeEventListener`, and `dispatchEvent` on `globalThis`\n * when — and only when — all three are missing.\n *\n * Safe to call any number of times; repeated calls keep the same backing target.\n *\n * @returns What the call did, or why it did nothing. See {@link GlobalEventTargetInstallResult}.\n */\nexport function installGlobalEventTarget(): GlobalEventTargetInstallResult {\n // Occupied-but-unusable is somebody else's doing and must not be overwritten; a plain\n // `undefined` is treated as absent, since assigning `undefined` is the ordinary way to\n // clear a global and is indistinguishable from never having set it.\n const members = METHOD_NAMES.map((name) => probeMember(name));\n\n if (members.every((member) => member === 'usable')) {\n // Never overwrite an existing implementation, ours or the environment's.\n return isStateInstalled(getState()) ? 'already-installed' : 'native';\n }\n\n if (members.some((member) => member !== 'absent')) {\n // Partial/incompatible surface: some member exists but the set is unusable as a\n // whole. Filling in the gaps would split listeners and dispatches across two\n // unrelated targets, and clobbering what is there would destroy someone else's\n // work — so leave the environment exactly as it is. A `hostile` member lands here\n // too: it cannot be read, so it certainly cannot be replaced safely.\n return 'partial';\n }\n\n // Captured rather than read twice: the constructor is a global like any other, so it\n // can be an accessor that throws, and re-reading it could yield something else.\n const eventTargetConstructor = readGlobal('EventTarget');\n\n if (typeof eventTargetConstructor !== 'function') {\n // Covers UNREADABLE too: a constructor we cannot read is one we cannot use.\n return 'unsupported';\n }\n\n // Preflight every property before touching anything, so a frozen or sealed global\n // object leaves with nothing half-installed — and, more importantly, without throwing\n // out of the module initialization that calls this.\n const isDefinable = [...METHOD_NAMES, GLOBAL_KEY].every((key) =>\n canDefineProperty(key),\n );\n\n if (!isDefinable) {\n return 'blocked';\n }\n\n const existingState = getState();\n const defined: string[] = [];\n\n // The state key as found, so a rollback can put back what was there.\n //\n // This call cannot be the first to throw: the preflight above already read\n // this exact descriptor through `canDefineProperty`, so an object hostile\n // enough to reject the read has already returned `'blocked'`.\n const priorStateDescriptor = Object.getOwnPropertyDescriptor(g, GLOBAL_KEY);\n\n /** Undo a partial installation when a later definition is rejected after all. */\n const rollback = (): void => {\n for (const key of defined) {\n // The state key may have held somebody else's object that this call\n // overwrote. Deleting it is not an undo there — a failed install would have\n // destroyed that object as a side effect, the one thing rollback exists to\n // prevent — so the original descriptor goes back instead.\n //\n // Restored in place rather than deleted first: whatever rejected the\n // definition may have sealed the global object, and re-adding a key to a\n // non-extensible object throws, while redefining a configurable one that\n // never left is always allowed.\n if (key === GLOBAL_KEY && priorStateDescriptor !== undefined) {\n try {\n Object.defineProperty(g, GLOBAL_KEY, priorStateDescriptor);\n } catch {\n // Nothing better to do here: the original could not be put back.\n }\n\n continue;\n }\n\n try {\n Reflect.deleteProperty(g, key);\n } catch {\n // Nothing better to do here: the property could not be defined *or* removed.\n }\n }\n };\n\n // Re-read `target` rather than reusing what `asUsableState` saw, and take the\n // three methods off it in the same pass. These are the values the globals are\n // actually bound to, so these are the reads that have to validate; trusting the\n // earlier probe would bind them to something that never passed a check.\n //\n // A target that no longer vouches for itself is treated as absent, so a fresh\n // one is made. Nothing is orphaned: a target that cannot service the three\n // methods never had listeners registered through this module.\n const reusableTarget = readStateTarget(existingState);\n const reusableMethods =\n reusableTarget === null ? null : bindTargetMethods(reusableTarget);\n\n let state: GlobalEventTargetState;\n let methods: Record<string, (...args: unknown[]) => unknown>;\n\n try {\n if (existingState === undefined || reusableMethods === null) {\n let target: EventTarget;\n\n try {\n target = new (eventTargetConstructor as new () => EventTarget)();\n } catch {\n // The constructor read from the global object cannot produce an\n // instance at all (a shim exposing `EventTarget` as non-constructible\n // throws here), so there is nothing to back the methods with. Reported\n // the same way as a constructor whose instances are unusable: nothing\n // was ever offered to the global object, so `'blocked'` would be the\n // wrong story, and nothing has been defined yet to undo.\n return 'unsupported';\n }\n\n const freshMethods = bindTargetMethods(target);\n\n if (freshMethods === null) {\n // The constructor read from the global object produced something that\n // cannot service the three methods, so there is nothing to back them\n // with. Nothing has been defined yet, so there is nothing to undo.\n return 'unsupported';\n }\n\n state = { target, isInstalled: false };\n methods = freshMethods;\n\n Object.defineProperty(g, GLOBAL_KEY, {\n value: state,\n enumerable: false,\n writable: false,\n configurable: true,\n });\n\n defined.push(GLOBAL_KEY);\n } else {\n // Another copy of Lifecycleion already made the target; reuse it so listeners\n // registered through it keep working.\n state = existingState;\n methods = reusableMethods;\n }\n\n for (const name of METHOD_NAMES) {\n Object.defineProperty(g, name, {\n value: methods[name],\n enumerable: false,\n writable: true,\n configurable: true,\n });\n\n defined.push(name);\n }\n\n // Flipped last, so the state only claims an install once all three are in\n // place, and inside the guarded block because a shared state object may be\n // frozen or have a throwing setter, which must not escape module init.\n //\n // Skipped when the flag already reads `true`: a non-writable property throws\n // on assignment in strict mode even for a same-value write. Otherwise a\n // frozen state left by a copy that already installed would roll back the\n // three methods just defined and report `blocked`, leaving the global with no\n // event methods. A frozen state still reading `false` keeps failing, as it\n // must: nothing can record that install.\n if (readMember(state, 'isInstalled') !== true) {\n state.isInstalled = true;\n }\n } catch {\n // The preflight passed but a definition or the state write was still rejected\n // (an exotic global object, a Proxy trap, a host restriction, frozen shared\n // state). Leave nothing behind and report no install.\n rollback();\n\n return 'blocked';\n }\n\n return 'installed';\n}\n\n/**\n * Get the shared `EventTarget` backing the polyfilled global methods.\n *\n * @returns The backing target, or `null` when the polyfill is not in use (native\n * environments, or environments where installation was skipped).\n */\nexport function getGlobalEventTarget(): EventTarget | null {\n return readStateTarget(getState());\n}\n\n/**\n * Whether the global event methods currently in place were installed by this polyfill.\n *\n * Reflects what was installed, not what is callable right now: an application that\n * replaces the global methods afterwards does not reset this.\n */\nexport function isGlobalEventTargetPolyfilled(): boolean {\n return isStateInstalled(getState());\n}\n\n/**\n * Whether `globalThis` exposes everything Lifecycleion's `'reportError'` convention needs:\n * the three `EventTarget` methods plus the `ErrorEvent` constructor.\n */\nexport function isGlobalEventTargetAvailable(): boolean {\n return (\n METHOD_NAMES.every((name) => probeMember(name) === 'usable') &&\n probeMember('ErrorEvent') === 'usable'\n );\n}\n"],"mappings":"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","names":[]}