mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-26 06:34:20 +08:00
Add EventTarget, Event and CustomEvent
This commit is contained in:
@@ -2,6 +2,6 @@
|
||||
"schemaVersion": 3,
|
||||
"nodeVersion": "24.15.0",
|
||||
"nodeCommit": "848430679556aed0bd073f2bc263331ad84fa119",
|
||||
"artifactVersion": "d94b8161bbee649b9890",
|
||||
"artifactVersion": "ad22ef7f9c0a3e2d2f5a",
|
||||
"rowCount": 3662
|
||||
}
|
||||
|
||||
@@ -298,10 +298,10 @@
|
||||
"entries": 78,
|
||||
"apiEntries": 78,
|
||||
"static": {
|
||||
"supported": 0,
|
||||
"partial": 22,
|
||||
"supported": 18,
|
||||
"partial": 34,
|
||||
"refused": 0,
|
||||
"not-implemented": 56,
|
||||
"not-implemented": 26,
|
||||
"by-design": 0,
|
||||
"unreviewed": 0,
|
||||
"not-applicable": 0
|
||||
@@ -26433,9 +26433,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26456,9 +26456,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26502,9 +26502,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26525,9 +26525,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26548,9 +26548,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "supported",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26571,9 +26571,9 @@
|
||||
"inherited": false
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "supported",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26594,9 +26594,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "supported",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26617,9 +26617,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "supported",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26640,9 +26640,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "supported",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26663,9 +26663,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "supported",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26686,9 +26686,9 @@
|
||||
"inherited": false
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "supported",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26709,9 +26709,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "supported",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26732,9 +26732,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "supported",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26755,9 +26755,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "supported",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26778,9 +26778,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "supported",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26801,9 +26801,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "supported",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26824,9 +26824,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "supported",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26847,9 +26847,9 @@
|
||||
"inherited": false
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "supported",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26870,9 +26870,9 @@
|
||||
"inherited": false
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "supported",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26893,9 +26893,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "supported",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26916,9 +26916,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "supported",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26939,9 +26939,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "supported",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26962,9 +26962,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -26985,9 +26985,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -27008,9 +27008,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -27031,9 +27031,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -27054,9 +27054,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -27077,9 +27077,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -28066,9 +28066,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -28112,9 +28112,9 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"detail": "No static compiler lowering is registered for this API yet.",
|
||||
"verification": "registry-gap"
|
||||
"status": "partial",
|
||||
"detail": "Implemented for the documented scriptc module-loader subset.",
|
||||
"verification": "test-backed"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -3,21 +3,21 @@
|
||||
"nodeVersion": "24.15.0",
|
||||
"nodeCommit": "848430679556aed0bd073f2bc263331ad84fa119",
|
||||
"summary": {
|
||||
"taskCount": 3288,
|
||||
"tierItemCount": 3288,
|
||||
"taskCount": 3270,
|
||||
"tierItemCount": 3270,
|
||||
"tiers": {
|
||||
"static": {
|
||||
"replace-refusal": 40,
|
||||
"verify-gap": 2384,
|
||||
"audit-partial": 782,
|
||||
"verify-gap": 2354,
|
||||
"audit-partial": 794,
|
||||
"classify": 2,
|
||||
"implement": 80
|
||||
}
|
||||
},
|
||||
"priorities": {
|
||||
"high": 2239,
|
||||
"high": 2225,
|
||||
"normal": 673,
|
||||
"low": 376
|
||||
"low": 372
|
||||
}
|
||||
},
|
||||
"tasks": [
|
||||
@@ -22999,12 +22999,18 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:events.EventTarget",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5131-event-target-listener-errors.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts",
|
||||
"tests/corpus/5135-event-target-warnings.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -23024,12 +23030,18 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:events.EventTarget",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5131-event-target-listener-errors.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts",
|
||||
"tests/corpus/5135-event-target-warnings.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -23074,12 +23086,18 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:events.EventTarget",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5131-event-target-listener-errors.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts",
|
||||
"tests/corpus/5135-event-target-warnings.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -23099,462 +23117,15 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "events:e5c069095e5c",
|
||||
"chapter": "events",
|
||||
"label": "event.composedPath()",
|
||||
"signature": "`event.composedPath()`",
|
||||
"apiSymbol": "event.composedPath",
|
||||
"docsUrl": "https://nodejs.org/docs/v24.15.0/api/events.html#eventcomposedpath",
|
||||
"nodeStability": {
|
||||
"index": "2",
|
||||
"level": 2,
|
||||
"text": "Stable",
|
||||
"inherited": true
|
||||
},
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "events:85436cb5ea2b",
|
||||
"chapter": "events",
|
||||
"label": "event.initEvent(type[, bubbles[, cancelable]])",
|
||||
"signature": "`event.initEvent(type[, bubbles[, cancelable]])`",
|
||||
"apiSymbol": "event.initEvent",
|
||||
"docsUrl": "https://nodejs.org/docs/v24.15.0/api/events.html#eventiniteventtype-bubbles-cancelable",
|
||||
"nodeStability": {
|
||||
"index": "3",
|
||||
"level": 3,
|
||||
"text": "Legacy: The WHATWG spec considers it deprecated and users shouldn't use it at all.",
|
||||
"inherited": false
|
||||
},
|
||||
"priority": "low",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "events:5848ab30b2e5",
|
||||
"chapter": "events",
|
||||
"label": "event.preventDefault()",
|
||||
"signature": "`event.preventDefault()`",
|
||||
"apiSymbol": "event.preventDefault",
|
||||
"docsUrl": "https://nodejs.org/docs/v24.15.0/api/events.html#eventpreventdefault",
|
||||
"nodeStability": {
|
||||
"index": "2",
|
||||
"level": 2,
|
||||
"text": "Stable",
|
||||
"inherited": true
|
||||
},
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "events:c16743d778c4",
|
||||
"chapter": "events",
|
||||
"label": "event.stopImmediatePropagation()",
|
||||
"signature": "`event.stopImmediatePropagation()`",
|
||||
"apiSymbol": "event.stopImmediatePropagation",
|
||||
"docsUrl": "https://nodejs.org/docs/v24.15.0/api/events.html#eventstopimmediatepropagation",
|
||||
"nodeStability": {
|
||||
"index": "2",
|
||||
"level": 2,
|
||||
"text": "Stable",
|
||||
"inherited": true
|
||||
},
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "events:9bab844aa1c2",
|
||||
"chapter": "events",
|
||||
"label": "event.stopPropagation()",
|
||||
"signature": "`event.stopPropagation()`",
|
||||
"apiSymbol": "event.stopPropagation",
|
||||
"docsUrl": "https://nodejs.org/docs/v24.15.0/api/events.html#eventstoppropagation",
|
||||
"nodeStability": {
|
||||
"index": "2",
|
||||
"level": 2,
|
||||
"text": "Stable",
|
||||
"inherited": true
|
||||
},
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "events:abe0ea02a52c",
|
||||
"chapter": "events",
|
||||
"label": "bubbles",
|
||||
"signature": "`bubbles` Type: {boolean} Always returns `false`.",
|
||||
"apiSymbol": "bubbles",
|
||||
"docsUrl": "https://nodejs.org/docs/v24.15.0/api/events.html#class-event",
|
||||
"nodeStability": {
|
||||
"index": "2",
|
||||
"level": 2,
|
||||
"text": "Stable",
|
||||
"inherited": true
|
||||
},
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "events:44e2dc361428",
|
||||
"chapter": "events",
|
||||
"label": "cancelBubble",
|
||||
"signature": "`cancelBubble` Type: {boolean}",
|
||||
"apiSymbol": "cancelBubble",
|
||||
"docsUrl": "https://nodejs.org/docs/v24.15.0/api/events.html#class-event",
|
||||
"nodeStability": {
|
||||
"index": "3",
|
||||
"level": 3,
|
||||
"text": "Legacy: Use [`event.stopPropagation()`][] instead.",
|
||||
"inherited": false
|
||||
},
|
||||
"priority": "low",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "events:51ee094d86d3",
|
||||
"chapter": "events",
|
||||
"label": "cancelable",
|
||||
"signature": "`cancelable` Type: {boolean} True if the event was created with the `cancelable` option.",
|
||||
"apiSymbol": "cancelable",
|
||||
"docsUrl": "https://nodejs.org/docs/v24.15.0/api/events.html#class-event",
|
||||
"nodeStability": {
|
||||
"index": "2",
|
||||
"level": 2,
|
||||
"text": "Stable",
|
||||
"inherited": true
|
||||
},
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "events:369be877d737",
|
||||
"chapter": "events",
|
||||
"label": "composed",
|
||||
"signature": "`composed` Type: {boolean} Always returns `false`.",
|
||||
"apiSymbol": "composed",
|
||||
"docsUrl": "https://nodejs.org/docs/v24.15.0/api/events.html#class-event",
|
||||
"nodeStability": {
|
||||
"index": "2",
|
||||
"level": 2,
|
||||
"text": "Stable",
|
||||
"inherited": true
|
||||
},
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "events:4e0c88e53f49",
|
||||
"chapter": "events",
|
||||
"label": "currentTarget",
|
||||
"signature": "`currentTarget` Type: {EventTarget} The `EventTarget` dispatching the event.",
|
||||
"apiSymbol": "currentTarget",
|
||||
"docsUrl": "https://nodejs.org/docs/v24.15.0/api/events.html#class-event",
|
||||
"nodeStability": {
|
||||
"index": "2",
|
||||
"level": 2,
|
||||
"text": "Stable",
|
||||
"inherited": true
|
||||
},
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "events:1e8421d3cc04",
|
||||
"chapter": "events",
|
||||
"label": "defaultPrevented",
|
||||
"signature": "`defaultPrevented` Type: {boolean}",
|
||||
"apiSymbol": "defaultPrevented",
|
||||
"docsUrl": "https://nodejs.org/docs/v24.15.0/api/events.html#class-event",
|
||||
"nodeStability": {
|
||||
"index": "2",
|
||||
"level": 2,
|
||||
"text": "Stable",
|
||||
"inherited": true
|
||||
},
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "events:602700d21dcf",
|
||||
"chapter": "events",
|
||||
"label": "eventPhase",
|
||||
"signature": "`eventPhase` Type: {number} Returns `0` while an event is not being dispatched, `2` while it is being dispatched.",
|
||||
"apiSymbol": "eventPhase",
|
||||
"docsUrl": "https://nodejs.org/docs/v24.15.0/api/events.html#class-event",
|
||||
"nodeStability": {
|
||||
"index": "2",
|
||||
"level": 2,
|
||||
"text": "Stable",
|
||||
"inherited": true
|
||||
},
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "events:4e83ca3c5e4c",
|
||||
"chapter": "events",
|
||||
"label": "isTrusted",
|
||||
"signature": "`isTrusted` Type: {boolean}",
|
||||
"apiSymbol": "isTrusted",
|
||||
"docsUrl": "https://nodejs.org/docs/v24.15.0/api/events.html#class-event",
|
||||
"nodeStability": {
|
||||
"index": "2",
|
||||
"level": 2,
|
||||
"text": "Stable",
|
||||
"inherited": true
|
||||
},
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "events:100e8d286de2",
|
||||
"chapter": "events",
|
||||
"label": "returnValue",
|
||||
"signature": "`returnValue` Type: {boolean} True if the event has not been canceled.",
|
||||
"apiSymbol": "returnValue",
|
||||
"docsUrl": "https://nodejs.org/docs/v24.15.0/api/events.html#class-event",
|
||||
"nodeStability": {
|
||||
"index": "3",
|
||||
"level": 3,
|
||||
"text": "Legacy: Use [`event.defaultPrevented`][] instead.",
|
||||
"inherited": false
|
||||
},
|
||||
"priority": "low",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "events:3d3da3858847",
|
||||
"chapter": "events",
|
||||
"label": "srcElement",
|
||||
"signature": "`srcElement` Type: {EventTarget} The `EventTarget` dispatching the event.",
|
||||
"apiSymbol": "srcElement",
|
||||
"docsUrl": "https://nodejs.org/docs/v24.15.0/api/events.html#class-event",
|
||||
"nodeStability": {
|
||||
"index": "3",
|
||||
"level": 3,
|
||||
"text": "Legacy: Use [`event.target`][] instead.",
|
||||
"inherited": false
|
||||
},
|
||||
"priority": "low",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "events:fdfee26f8c22",
|
||||
"chapter": "events",
|
||||
"label": "target",
|
||||
"signature": "`target` Type: {EventTarget} The `EventTarget` dispatching the event.",
|
||||
"apiSymbol": "target",
|
||||
"docsUrl": "https://nodejs.org/docs/v24.15.0/api/events.html#class-event",
|
||||
"nodeStability": {
|
||||
"index": "2",
|
||||
"level": 2,
|
||||
"text": "Stable",
|
||||
"inherited": true
|
||||
},
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "events:051e03ef86de",
|
||||
"chapter": "events",
|
||||
"label": "timeStamp",
|
||||
"signature": "`timeStamp` Type: {number}",
|
||||
"apiSymbol": "timeStamp",
|
||||
"docsUrl": "https://nodejs.org/docs/v24.15.0/api/events.html#class-event",
|
||||
"nodeStability": {
|
||||
"index": "2",
|
||||
"level": 2,
|
||||
"text": "Stable",
|
||||
"inherited": true
|
||||
},
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "events:88b512802b39",
|
||||
"chapter": "events",
|
||||
"label": "type",
|
||||
"signature": "`type` Type: {string}",
|
||||
"apiSymbol": "type",
|
||||
"docsUrl": "https://nodejs.org/docs/v24.15.0/api/events.html#class-event",
|
||||
"nodeStability": {
|
||||
"index": "2",
|
||||
"level": 2,
|
||||
"text": "Stable",
|
||||
"inherited": true
|
||||
},
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:events.Event",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -23574,12 +23145,18 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:events.EventTarget",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5131-event-target-listener-errors.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts",
|
||||
"tests/corpus/5135-event-target-warnings.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -23599,12 +23176,18 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:events.eventTarget.addEventListener",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5131-event-target-listener-errors.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts",
|
||||
"tests/corpus/5135-event-target-warnings.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -23624,12 +23207,18 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:events.eventTarget.dispatchEvent",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5131-event-target-listener-errors.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts",
|
||||
"tests/corpus/5135-event-target-warnings.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -23649,12 +23238,18 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:events.eventTarget.removeEventListener",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5131-event-target-listener-errors.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts",
|
||||
"tests/corpus/5135-event-target-warnings.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -23674,12 +23269,15 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:events.CustomEvent",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -23699,12 +23297,15 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:events.detail",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -24837,12 +24438,14 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:events.events.addAbortListener",
|
||||
"tests": [
|
||||
"tests/corpus/5133-abort-signal-event-target.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -24887,12 +24490,14 @@
|
||||
"priority": "high",
|
||||
"tiers": {
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"action": "verify-gap",
|
||||
"verification": "registry-gap",
|
||||
"confidence": "low",
|
||||
"source": "compiler-unmatched:events",
|
||||
"tests": []
|
||||
"status": "partial",
|
||||
"action": "audit-partial",
|
||||
"verification": "test-backed",
|
||||
"confidence": "high",
|
||||
"source": "compiler-feature:events.events.errorMonitor",
|
||||
"tests": [
|
||||
"tests/corpus/5134-events-error-monitor.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -310,10 +310,10 @@
|
||||
"entries": 84,
|
||||
"apiEntries": 78,
|
||||
"static": {
|
||||
"supported": 0,
|
||||
"partial": 22,
|
||||
"supported": 18,
|
||||
"partial": 34,
|
||||
"refused": 0,
|
||||
"not-implemented": 56,
|
||||
"not-implemented": 26,
|
||||
"by-design": 0,
|
||||
"unreviewed": 0,
|
||||
"not-applicable": 0
|
||||
@@ -40005,7 +40005,13 @@
|
||||
"evidence": "derived:descendants",
|
||||
"tests": [
|
||||
"tests/corpus/1677-emitter-listeners.ts",
|
||||
"tests/corpus/2620-ee-override-once-order.ts"
|
||||
"tests/corpus/2620-ee-override-once-order.ts",
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5131-event-target-listener-errors.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts",
|
||||
"tests/corpus/5134-events-error-monitor.ts",
|
||||
"tests/corpus/5135-event-target-warnings.js"
|
||||
]
|
||||
},
|
||||
"anchorSource": "chapter"
|
||||
@@ -40143,8 +40149,15 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:events.EventTarget",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5131-event-target-listener-errors.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts",
|
||||
"tests/corpus/5135-event-target-warnings.js"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -40166,8 +40179,15 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:events.EventTarget",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5131-event-target-listener-errors.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts",
|
||||
"tests/corpus/5135-event-target-warnings.js"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -40235,8 +40255,15 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:events.EventTarget",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5131-event-target-listener-errors.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts",
|
||||
"tests/corpus/5135-event-target-warnings.js"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -40258,8 +40285,12 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:events.Event",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -40281,8 +40312,13 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "supported",
|
||||
"evidence": "compiler-feature:events.event.composedPath",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -40304,8 +40340,13 @@
|
||||
"inherited": false
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "supported",
|
||||
"evidence": "compiler-feature:events.event.initEvent",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -40327,8 +40368,13 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "supported",
|
||||
"evidence": "compiler-feature:events.event.preventDefault",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -40350,8 +40396,13 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "supported",
|
||||
"evidence": "compiler-feature:events.event.stopImmediatePropagation",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -40373,8 +40424,13 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "supported",
|
||||
"evidence": "compiler-feature:events.event.stopPropagation",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -40396,8 +40452,13 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "supported",
|
||||
"evidence": "compiler-feature:events.bubbles",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
},
|
||||
@@ -40419,8 +40480,13 @@
|
||||
"inherited": false
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "supported",
|
||||
"evidence": "compiler-feature:events.cancelBubble",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
},
|
||||
@@ -40442,8 +40508,13 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "supported",
|
||||
"evidence": "compiler-feature:events.cancelable",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
},
|
||||
@@ -40465,8 +40536,13 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "supported",
|
||||
"evidence": "compiler-feature:events.composed",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
},
|
||||
@@ -40488,8 +40564,13 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "supported",
|
||||
"evidence": "compiler-feature:events.currentTarget",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
},
|
||||
@@ -40511,8 +40592,13 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "supported",
|
||||
"evidence": "compiler-feature:events.defaultPrevented",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
},
|
||||
@@ -40534,8 +40620,13 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "supported",
|
||||
"evidence": "compiler-feature:events.eventPhase",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
},
|
||||
@@ -40557,8 +40648,13 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "supported",
|
||||
"evidence": "compiler-feature:events.isTrusted",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
},
|
||||
@@ -40580,8 +40676,13 @@
|
||||
"inherited": false
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "supported",
|
||||
"evidence": "compiler-feature:events.returnValue",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
},
|
||||
@@ -40603,8 +40704,13 @@
|
||||
"inherited": false
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "supported",
|
||||
"evidence": "compiler-feature:events.srcElement",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
},
|
||||
@@ -40626,8 +40732,13 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "supported",
|
||||
"evidence": "compiler-feature:events.target",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
},
|
||||
@@ -40649,8 +40760,13 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "supported",
|
||||
"evidence": "compiler-feature:events.timeStamp",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
},
|
||||
@@ -40672,8 +40788,13 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "supported",
|
||||
"evidence": "compiler-feature:events.type",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
},
|
||||
@@ -40695,8 +40816,15 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:events.EventTarget",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5131-event-target-listener-errors.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts",
|
||||
"tests/corpus/5135-event-target-warnings.js"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -40718,8 +40846,15 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:events.eventTarget.addEventListener",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5131-event-target-listener-errors.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts",
|
||||
"tests/corpus/5135-event-target-warnings.js"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -40741,8 +40876,15 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:events.eventTarget.dispatchEvent",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5131-event-target-listener-errors.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts",
|
||||
"tests/corpus/5135-event-target-warnings.js"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -40764,8 +40906,15 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:events.eventTarget.removeEventListener",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5131-event-target-listener-errors.js",
|
||||
"tests/corpus/5132-event-target-typed.ts",
|
||||
"tests/corpus/5133-abort-signal-event-target.ts",
|
||||
"tests/corpus/5135-event-target-warnings.js"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -40787,8 +40936,12 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:events.CustomEvent",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -40810,8 +40963,12 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:events.detail",
|
||||
"tests": [
|
||||
"tests/corpus/5130-event-target-dispatch.js",
|
||||
"tests/corpus/5132-event-target-typed.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "ancestor"
|
||||
},
|
||||
@@ -41906,8 +42063,11 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:events.events.addAbortListener",
|
||||
"tests": [
|
||||
"tests/corpus/5133-abort-signal-event-target.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
@@ -41952,8 +42112,11 @@
|
||||
"inherited": true
|
||||
},
|
||||
"static": {
|
||||
"status": "not-implemented",
|
||||
"evidence": "compiler-unmatched:events"
|
||||
"status": "partial",
|
||||
"evidence": "compiler-feature:events.events.errorMonitor",
|
||||
"tests": [
|
||||
"tests/corpus/5134-events-error-monitor.ts"
|
||||
]
|
||||
},
|
||||
"anchorSource": "exact"
|
||||
},
|
||||
|
||||
@@ -138,6 +138,12 @@
|
||||
},
|
||||
"features": [
|
||||
{ "chapter": "errors", "parent": "Class: `SystemError`", "symbols": ["code", "errno", "syscall", "path", "dest"], "status": "partial", "evidence": ["tests/corpus/caught-system-error-fields.ts", "tests/fixtures/node-types/terminal-error-fields.ts", "tests/harness/project-config.test.ts"] },
|
||||
{"chapter": "events", "symbols": ["EventTarget", "eventTarget.addEventListener", "eventTarget.removeEventListener", "eventTarget.dispatchEvent"], "status": "partial", "evidence": ["tests/corpus/5130-event-target-dispatch.js", "tests/corpus/5131-event-target-listener-errors.js", "tests/corpus/5132-event-target-typed.ts", "tests/corpus/5133-abort-signal-event-target.ts", "tests/corpus/5135-event-target-warnings.js"]},
|
||||
{"chapter": "events", "symbols": ["Event"], "status": "partial", "evidence": ["tests/corpus/5130-event-target-dispatch.js", "tests/corpus/5132-event-target-typed.ts"]},
|
||||
{"chapter": "events", "symbols": ["event.composedPath", "event.preventDefault", "event.stopImmediatePropagation", "event.stopPropagation", "event.initEvent", "bubbles", "cancelable", "cancelBubble", "composed", "currentTarget", "defaultPrevented", "eventPhase", "isTrusted", "returnValue", "srcElement", "target", "timeStamp", "type"], "status": "supported", "evidence": ["tests/corpus/5130-event-target-dispatch.js", "tests/corpus/5132-event-target-typed.ts", "tests/corpus/5133-abort-signal-event-target.ts"]},
|
||||
{"chapter": "events", "symbols": ["CustomEvent", "detail"], "status": "partial", "evidence": ["tests/corpus/5130-event-target-dispatch.js", "tests/corpus/5132-event-target-typed.ts"]},
|
||||
{"chapter": "events", "symbols": ["events.errorMonitor"], "status": "partial", "evidence": ["tests/corpus/5134-events-error-monitor.ts"]},
|
||||
{"chapter": "events", "symbols": ["events.addAbortListener"], "status": "partial", "evidence": ["tests/corpus/5133-abort-signal-event-target.ts"]},
|
||||
{"chapter": "worker_threads", "symbols": ["Worker"], "status": "partial", "evidence": ["tests/corpus/native-worker-messages.ts", "tests/corpus/native-worker-commonjs.cjs", "tests/corpus/native-worker-environment.ts", "tests/corpus/native-worker-roots/main.ts"]},
|
||||
{"chapter": "worker_threads", "symbols": ["MessagePort", "parentPort", "worker_threads.workerData", "isMainThread", "threadId"], "status": "partial", "evidence": ["tests/corpus/native-worker-commonjs.cjs", "tests/corpus/native-worker-port-lifetime.ts", "tests/corpus/native-worker-references.ts"]},
|
||||
{"chapter": "worker_threads", "symbols": ["worker.postMessage", "port.postMessage"], "status": "partial", "evidence": ["tests/corpus/native-worker-message-graphs.ts", "tests/corpus/native-worker-shared-views.ts", "tests/corpus/native-worker-clone-errors.ts", "tests/corpus/native-worker-delayed-messages.ts"]},
|
||||
|
||||
+14
-10
@@ -4469,16 +4469,20 @@ declare module "events" {
|
||||
class EventEmitter {
|
||||
constructor();
|
||||
static defaultMaxListeners: number;
|
||||
on(eventName: string, listener: (...args: any[]) => void): this;
|
||||
addListener(eventName: string, listener: (...args: any[]) => void): this;
|
||||
once(eventName: string, listener: (...args: any[]) => void): this;
|
||||
prependListener(eventName: string, listener: (...args: any[]) => void): this;
|
||||
prependOnceListener(eventName: string, listener: (...args: any[]) => void): this;
|
||||
off(eventName: string, listener: (...args: any[]) => void): this;
|
||||
removeListener(eventName: string, listener: (...args: any[]) => void): this;
|
||||
removeAllListeners(eventName?: string): this;
|
||||
emit(eventName: string, ...args: any[]): boolean;
|
||||
listenerCount(eventName: string, listener?: (...args: any[]) => void): number;
|
||||
/* Listeners registered under this symbol observe 'error' emits first
|
||||
* (an emit with no 'error' listener still throws). */
|
||||
static readonly errorMonitor: unique symbol;
|
||||
static addAbortListener(signal: AbortSignal, listener: (event: Event) => void): Disposable;
|
||||
on(eventName: string | symbol, listener: (...args: any[]) => void): this;
|
||||
addListener(eventName: string | symbol, listener: (...args: any[]) => void): this;
|
||||
once(eventName: string | symbol, listener: (...args: any[]) => void): this;
|
||||
prependListener(eventName: string | symbol, listener: (...args: any[]) => void): this;
|
||||
prependOnceListener(eventName: string | symbol, listener: (...args: any[]) => void): this;
|
||||
off(eventName: string | symbol, listener: (...args: any[]) => void): this;
|
||||
removeListener(eventName: string | symbol, listener: (...args: any[]) => void): this;
|
||||
removeAllListeners(eventName?: string | symbol): this;
|
||||
emit(eventName: string | symbol, ...args: any[]): boolean;
|
||||
listenerCount(eventName: string | symbol, listener?: (...args: any[]) => void): number;
|
||||
/* Declared as zero-parameter closures so the RESULT is statically
|
||||
* consumable (.length, indexing, identity): the lowering only admits
|
||||
* these calls for events whose argument tuple is empty — a listener
|
||||
|
||||
@@ -13,6 +13,7 @@ export function executableLinkFeatures(mod: IrModule): NativeLinkFeatures {
|
||||
textDecoderLegacy: features.legacyTextDecoder,
|
||||
fileHandle: features.fileHandle,
|
||||
fetch: features.fetch,
|
||||
...(features.eventTarget ? { eventTarget: true } : {}),
|
||||
zlib: features.zlib,
|
||||
assert: features.assert,
|
||||
inspect: features.inspect,
|
||||
|
||||
@@ -397,6 +397,8 @@ export function emitLibCall(host: LlvmEmitterContext, e: LibCallExpr): LlValue {
|
||||
case "buffer":
|
||||
case "bytes":
|
||||
case "tlsca":
|
||||
case "event":
|
||||
case "events":
|
||||
return host.emitGenericLibCall(e);
|
||||
case "test":
|
||||
return emitTestLibCall(host, e);
|
||||
|
||||
@@ -665,6 +665,11 @@ export const LIB_FN_SYMS: Record<string, string> = {
|
||||
"fetch.responseNew": "scr_fetch_response_new",
|
||||
"fetch.input": "scr_fetch_input",
|
||||
"abort.controllerNew": "scr_fetch_abort_controller_new",
|
||||
"event.targetNew": "scr_event_target_new",
|
||||
"event.new": "scr_event_new",
|
||||
"event.customNew": "scr_custom_event_new",
|
||||
"event.is": "scr_event_is",
|
||||
"events.addAbortListener": "scr_events_add_abort_listener",
|
||||
"abort.timeout": "scr_fetch_abort_timeout",
|
||||
"abort.now": "scr_fetch_abort_now",
|
||||
"abort.any": "scr_fetch_abort_any",
|
||||
|
||||
@@ -21,6 +21,7 @@ export interface NativeLinkFeatures {
|
||||
textDecoderLegacy: boolean;
|
||||
fileHandle: boolean;
|
||||
fetch: boolean;
|
||||
eventTarget?: boolean;
|
||||
zlib: boolean;
|
||||
assert: boolean;
|
||||
inspect: boolean;
|
||||
|
||||
@@ -107,6 +107,11 @@ export interface CcOptions {
|
||||
* reactions, AsyncLocalStorage, the unhandledRejection/warning
|
||||
* process events. */
|
||||
dynAsync?: boolean;
|
||||
/** The program constructs EventTarget/Event/CustomEvent values or
|
||||
* calls events.addAbortListener (the event.* libCalls), or uses the
|
||||
* native fetch AbortSignal built on the same engine: compiles
|
||||
* scr_event_target.c. */
|
||||
eventTarget?: boolean;
|
||||
/** The program uses the process-events surface (signal/exit listeners,
|
||||
* stdin events, for-await over stdin — moduleUsesProcessEvents on the
|
||||
* IR): compiles scr_events.c into the binary. Event-free binaries keep
|
||||
|
||||
@@ -486,6 +486,7 @@ export async function compileCInternal(
|
||||
: []),
|
||||
...(opts.dynInvoke || opts.workers || nativeFetch ? [rt(join(rtDir, "scr_dyn_invoke.c"))] : []),
|
||||
...(opts.dc ? [rt(join(rtDir, "scr_dc.c"))] : []),
|
||||
...(opts.eventTarget || nativeFetch ? [rt(join(rtDir, "scr_event_target.c"))] : []),
|
||||
...(opts.dynAsync || opts.dynInvoke || opts.workers || opts.dc || opts.fileHandle || nativeFetch
|
||||
? [rt(join(rtDir, "scr_async_dyn.c"))]
|
||||
: []),
|
||||
|
||||
@@ -80,6 +80,7 @@ export interface RuntimePackManifest {
|
||||
|
||||
export interface RuntimeFeatureSet extends NativeLinkFeatures {
|
||||
workers: boolean;
|
||||
eventTarget: boolean;
|
||||
nativeFetch: boolean;
|
||||
netEffective: boolean;
|
||||
httpEffective: boolean;
|
||||
@@ -264,6 +265,7 @@ export function effectiveRuntimeFeatures(
|
||||
return {
|
||||
...features,
|
||||
workers: features.workers === true,
|
||||
eventTarget: features.eventTarget === true,
|
||||
nativeFetch,
|
||||
netEffective,
|
||||
httpEffective,
|
||||
|
||||
@@ -54,6 +54,7 @@ export interface EarlyExecutableNativeFeatures {
|
||||
textDecoderLegacy: boolean;
|
||||
fileHandle: boolean;
|
||||
fetch: boolean;
|
||||
eventTarget?: boolean;
|
||||
zlib: boolean;
|
||||
assert: boolean;
|
||||
inspect: boolean;
|
||||
|
||||
@@ -1,11 +1,50 @@
|
||||
import * as ts from "../ts7/adapter.js";
|
||||
import type { IrExpr, SrcLoc } from "../../ir/ir.js";
|
||||
import { dynUndefinedExpr } from "../../ir/build.js";
|
||||
import { DYN, type IrExpr, type SrcLoc } from "../../ir/ir.js";
|
||||
import type { Lowerer } from "./lowerer.js";
|
||||
import { lowerTimersMemberCall } from "./lower-timers.js";
|
||||
import { lowerStreamModuleCall } from "./lower-stream.js";
|
||||
import { lowerNodeModuleCall } from "./builtins/module-resolution.js";
|
||||
import { builtinModuleFnOf, builtinFenceHintOf } from "./surfaces.js";
|
||||
|
||||
/** `events.addAbortListener(signal, listener)`: the runtime registers the
|
||||
* once/resist-stop-propagation abort listener (or queues the call for an
|
||||
* aborted signal) and answers the disposer; the Disposable is a fresh
|
||||
* object carrying it under Symbol.dispose. */
|
||||
export function lowerAddAbortListener(
|
||||
lowerer: Lowerer,
|
||||
expr: ts.CallExpression,
|
||||
loc: SrcLoc,
|
||||
): IrExpr {
|
||||
const args = expr.arguments;
|
||||
if (args.length > 2 || args.some(ts.isSpreadElement)) {
|
||||
lowerer.noLowering(`addAbortListener with this argument list`, expr);
|
||||
}
|
||||
const values = [0, 1].map((i) =>
|
||||
args[i] ? lowerer.lowerExprExpecting(args[i]!, DYN) : dynUndefinedExpr(loc),
|
||||
);
|
||||
const key: IrExpr = {
|
||||
kind: "dynFrom",
|
||||
value: {
|
||||
kind: "libCall",
|
||||
fn: "sym.wellKnown",
|
||||
args: [{ kind: "strLit", value: "dispose", type: { kind: "string" }, loc }],
|
||||
type: { kind: "symbol" },
|
||||
loc,
|
||||
},
|
||||
type: DYN,
|
||||
loc,
|
||||
};
|
||||
const dispose: IrExpr = {
|
||||
kind: "libCall",
|
||||
fn: "events.addAbortListener",
|
||||
args: values,
|
||||
type: DYN,
|
||||
loc,
|
||||
};
|
||||
return { kind: "dynObjLit", fields: [{ key, value: dispose }], type: DYN, loc };
|
||||
}
|
||||
|
||||
/** Named imports, namespace members and callable aliases share validation
|
||||
* and dispatch. Resolve diagnostic provenance only if no handler accepts. */
|
||||
export function lowerBuiltinCall(
|
||||
@@ -18,6 +57,9 @@ export function lowerBuiltinCall(
|
||||
if (bi.module === "child_process" && bi.member === "execFile") {
|
||||
return lowerer.lowerExecFileCall(expr, loc);
|
||||
}
|
||||
if (bi.module === "events" && bi.member === "addAbortListener") {
|
||||
return lowerAddAbortListener(lowerer, expr, loc);
|
||||
}
|
||||
if (bi.module === "timers") {
|
||||
const timersServed = lowerTimersMemberCall(lowerer, expr, bi.member, loc);
|
||||
if (timersServed) return timersServed;
|
||||
|
||||
@@ -124,3 +124,24 @@ export function lowerNumberStaticProperty(
|
||||
if (value === undefined) return null;
|
||||
return { kind: "numLit", value, type: F64, loc: locOf(expr) };
|
||||
}
|
||||
|
||||
/** The Event phase constants (Event.NONE ... Event.BUBBLING_PHASE),
|
||||
* which CustomEvent inherits through its constructor. */
|
||||
const EVENT_PHASES: Record<string, number | undefined> = {
|
||||
NONE: 0,
|
||||
CAPTURING_PHASE: 1,
|
||||
AT_TARGET: 2,
|
||||
BUBBLING_PHASE: 3,
|
||||
};
|
||||
|
||||
export function lowerEventStaticProperty(
|
||||
lowerer: Lowerer,
|
||||
expr: ts.PropertyAccessExpression,
|
||||
): IrExpr | null {
|
||||
const member =
|
||||
lowerer.stdlibGlobalMember(expr, "Event") ?? lowerer.stdlibGlobalMember(expr, "CustomEvent");
|
||||
if (member === null) return null;
|
||||
const value = own(EVENT_PHASES, member);
|
||||
if (value === undefined) return null;
|
||||
return { kind: "numLit", value, type: F64, loc: locOf(expr) };
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ import {
|
||||
import { STREAM_FORCED_EVENT_NAMES, streamForcedTuple, streamSidesOf } from "./lower-stream.js";
|
||||
import { boolLit, strLit, varRef } from "../../ir/build.js";
|
||||
import { tryLowerExpression } from "./expressions/try-lower-expression.js";
|
||||
import { lowerAddAbortListener } from "./builtin-calls.js";
|
||||
|
||||
/** True when the class descends from (or is) the runtime emitter. */
|
||||
export function emitterRooted(lowerer: Lowerer, info: ClassInfo | undefined | null): boolean {
|
||||
@@ -185,14 +186,17 @@ function emitterEvents(lowerer: Lowerer): Map<string, EventSig> {
|
||||
);
|
||||
return sig;
|
||||
};
|
||||
// The two forced tuples (see the header comment).
|
||||
table.set("error", {
|
||||
tuple: [{ kind: "object", className: "%Error" }],
|
||||
fromEmit: true,
|
||||
conflict: null,
|
||||
dynListener: false,
|
||||
hasListener: false,
|
||||
});
|
||||
// The forced tuples (see the header comment); errorMonitor listeners
|
||||
// receive the 'error' payload.
|
||||
for (const name of ["error", ERROR_MONITOR_EVENT]) {
|
||||
table.set(name, {
|
||||
tuple: [{ kind: "object", className: "%Error" }],
|
||||
fromEmit: true,
|
||||
conflict: null,
|
||||
dynListener: false,
|
||||
hasListener: false,
|
||||
});
|
||||
}
|
||||
table.set("newListener", {
|
||||
tuple: [STRING],
|
||||
fromEmit: true,
|
||||
@@ -295,7 +299,12 @@ function emitterEvents(lowerer: Lowerer): Map<string, EventSig> {
|
||||
}
|
||||
const info = scannedEmitterInfo(lowerer, recvT);
|
||||
if (!info) return;
|
||||
if (!nameT.isStringLiteralType()) {
|
||||
const literal = isErrorMonitorRef(lowerer, arg0)
|
||||
? ERROR_MONITOR_EVENT
|
||||
: nameT.isStringLiteralType()
|
||||
? nameT.value
|
||||
: null;
|
||||
if (literal === null) {
|
||||
const pattern = computedEventPatternOf(lowerer, arg0);
|
||||
if (
|
||||
pattern &&
|
||||
@@ -306,8 +315,8 @@ function emitterEvents(lowerer: Lowerer): Map<string, EventSig> {
|
||||
}
|
||||
return;
|
||||
}
|
||||
const name = nameT.value;
|
||||
if (META_EVENTS.has(name) || name === "error") return; // forced tuples
|
||||
const name = literal;
|
||||
if (META_EVENTS.has(name) || name === "error" || name === ERROR_MONITOR_EVENT) return; // forced tuples
|
||||
// Stream receivers' runtime-emitted events carry PER-BASE forced
|
||||
// tuples (lower-stream.ts) — their sites never join the program-
|
||||
// global table, so a stream's 'data' cannot collide with a user
|
||||
@@ -368,12 +377,69 @@ function computedPatternsOverlap(left: ComputedEventPattern, right: ComputedEven
|
||||
);
|
||||
}
|
||||
|
||||
/** The compile-time name for a literal-only operation, or a pointed fence
|
||||
* (string-literal TYPE, so const bindings and literal unions of one
|
||||
* member count — the staticHeaderKeyOf stance). */
|
||||
function eventNameOf(lowerer: Lowerer, member: string, arg: ts.Expression): string {
|
||||
/** The registry name of `events.errorMonitor`: a symbol in Node, so no
|
||||
* string event can collide with it (the NUL never appears in a literal
|
||||
* name the lowering admits as the same key — see literalEventName). */
|
||||
export const ERROR_MONITOR_EVENT = "\u0000events.errorMonitor";
|
||||
|
||||
/** `errorMonitor` / `EventEmitter.errorMonitor` / `events.errorMonitor`:
|
||||
* the events module's unique symbol, by declaration provenance. */
|
||||
function isErrorMonitorRef(lowerer: Lowerer, arg: ts.Expression): boolean {
|
||||
const isMonitorType = (t: ts.Type): boolean => {
|
||||
if (!(t.flags & ts.TypeFlags.UniqueESSymbol)) return false;
|
||||
const symbol = t.getSymbol();
|
||||
return symbol?.name === "errorMonitor" && lowerer.isStdlibSymbol(symbol);
|
||||
};
|
||||
// The unique symbol TYPE names its declaration through any alias.
|
||||
if (isMonitorType(lowerer.typeOf(arg))) return true;
|
||||
// A const destructuring (`const { errorMonitor } = events`) widens the
|
||||
// binding's type in JavaScript; its source property still names it.
|
||||
if (!ts.isIdentifier(arg)) return false;
|
||||
const symbol = lowerer.resolveValueSymbol(arg);
|
||||
const declaration = symbol ? lowerer.checker.valueDeclarationOf(symbol) : undefined;
|
||||
if (!declaration || !ts.isBindingElement(declaration)) return false;
|
||||
const pattern = declaration.parent;
|
||||
const variable = pattern?.parent;
|
||||
if (
|
||||
!pattern ||
|
||||
!variable ||
|
||||
!ts.isObjectBindingPattern(pattern) ||
|
||||
!ts.isVariableDeclaration(variable) ||
|
||||
!variable.initializer ||
|
||||
!(ts.getCombinedNodeFlags(variable) & ts.NodeFlags.Const)
|
||||
)
|
||||
return false;
|
||||
const key = declaration.propertyName ?? declaration.name;
|
||||
if (!ts.isIdentifier(key)) return false;
|
||||
const property = lowerer.checker.getPropertyOfType(
|
||||
lowerer.typeOf(variable.initializer),
|
||||
key.text,
|
||||
);
|
||||
return property !== undefined && isMonitorType(lowerer.checker.getTypeOfSymbol(property));
|
||||
}
|
||||
|
||||
/** A compile-time event name: a string-literal TYPE (const bindings and
|
||||
* literal unions of one member count — the staticHeaderKeyOf stance) or
|
||||
* the errorMonitor symbol. */
|
||||
function literalEventName(lowerer: Lowerer, arg: ts.Expression): string | null {
|
||||
if (isErrorMonitorRef(lowerer, arg)) return ERROR_MONITOR_EVENT;
|
||||
const t = lowerer.typeOf(arg);
|
||||
if (t.isStringLiteralType()) return t.value;
|
||||
if (!t.isStringLiteralType()) return null;
|
||||
// A string spelling of the reserved key stays a separate event in Node.
|
||||
if (t.value === ERROR_MONITOR_EVENT) {
|
||||
lowerer.noLowering(
|
||||
"an event name spelling the errorMonitor key",
|
||||
arg,
|
||||
"the runtime reserves this name for events.errorMonitor",
|
||||
);
|
||||
}
|
||||
return t.value;
|
||||
}
|
||||
|
||||
/** The compile-time name for a literal-only operation, or a pointed fence. */
|
||||
function eventNameOf(lowerer: Lowerer, member: string, arg: ts.Expression): string {
|
||||
const name = literalEventName(lowerer, arg);
|
||||
if (name !== null) return name;
|
||||
lowerer.noLowering(
|
||||
`${member} with a non-literal event name`,
|
||||
arg,
|
||||
@@ -987,6 +1053,7 @@ export function lowerEmitterMethodCall(
|
||||
superRecv === undefined &&
|
||||
lowerer.checker.getConstructSignatures(lowerer.typeOf(access.expression)).length > 0
|
||||
) {
|
||||
if (member === "addAbortListener") return lowerAddAbortListener(lowerer, call, loc);
|
||||
if (member === "setMaxListeners" && args.length === 1) {
|
||||
if (lowerer.mapTypeOf(lowerer.typeOf(args[0]!))?.kind !== "f64") {
|
||||
// Node's runtime ladder over the dyn value ("setMaxListeners" is
|
||||
@@ -1080,7 +1147,7 @@ export function lowerEmitterMethodCall(
|
||||
);
|
||||
}
|
||||
const registering = REGISTER_MEMBERS.has(member);
|
||||
if (!lowerer.typeOf(args[0]!).isStringLiteralType()) {
|
||||
if (literalEventName(lowerer, args[0]!) === null) {
|
||||
computedEventPattern(lowerer, info, member, args[0]!);
|
||||
const receiver = lowerReceiver();
|
||||
const name = lowerer.lowerExprExpecting(args[0]!, STRING);
|
||||
@@ -1230,8 +1297,7 @@ export function lowerEmitterMethodCall(
|
||||
if (args.length === 0) {
|
||||
lowerer.noLowering("emit without an event name", call);
|
||||
}
|
||||
const nameType = lowerer.typeOf(args[0]!);
|
||||
if (!nameType.isStringLiteralType()) {
|
||||
if (literalEventName(lowerer, args[0]!) === null) {
|
||||
const pattern = computedEventPatternOf(lowerer, args[0]!);
|
||||
const mayBeObserved =
|
||||
pattern === null ||
|
||||
@@ -1363,7 +1429,11 @@ export function lowerEmitterMethodCall(
|
||||
}
|
||||
const all = args.length === 0;
|
||||
const receiver = lowerReceiver();
|
||||
const name = all ? strLit("", loc) : lowerer.lowerExprExpecting(args[0]!, STRING);
|
||||
const name = all
|
||||
? strLit("", loc)
|
||||
: isErrorMonitorRef(lowerer, args[0]!)
|
||||
? strLit(ERROR_MONITOR_EVENT, loc)
|
||||
: lowerer.lowerExprExpecting(args[0]!, STRING);
|
||||
if (name.type.kind !== "string") {
|
||||
lowerer.noLowering(
|
||||
`removeAllListeners with a '${lowerer.fmt(name.type)}' event name`,
|
||||
@@ -1384,7 +1454,9 @@ export function lowerEmitterMethodCall(
|
||||
lowerer.noLowering(`listenerCount with ${args.length} arguments`, call);
|
||||
}
|
||||
const receiver = lowerReceiver();
|
||||
const name = lowerer.lowerExprExpecting(args[0]!, STRING);
|
||||
const name = isErrorMonitorRef(lowerer, args[0]!)
|
||||
? strLit(ERROR_MONITOR_EVENT, loc)
|
||||
: lowerer.lowerExprExpecting(args[0]!, STRING);
|
||||
if (name.type.kind !== "string") {
|
||||
lowerer.noLowering(`listenerCount with a '${lowerer.fmt(name.type)}' event name`, args[0]!);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { generatorDrain } from "./iterator-adapters.js";
|
||||
import { lowerEventStaticProperty } from "./builtins/numbers.js";
|
||||
import { lowerUnionEquality, tagEqualityMayMissAlias } from "./strict-equality.js";
|
||||
import { isOptionalProcessStreamProperty } from "./builtins/process.js";
|
||||
import { lowerWorkerMetadata } from "./builtins/workers.js";
|
||||
@@ -2147,6 +2148,7 @@ function lowerExprInner(lowerer: Lowerer, expr: ts.Expression): IrExpr {
|
||||
lowerer.lowerJsonProperty(expr) ??
|
||||
lowerer.lowerErrorCodeProperty(expr) ??
|
||||
lowerer.lowerNumberStaticProperty(expr) ??
|
||||
lowerEventStaticProperty(lowerer, expr) ??
|
||||
lowerer.lowerMathProperty(expr) ??
|
||||
lowerer.lowerIntrinsicProperty(expr) ??
|
||||
// The _readableState/_writableState scalar READS (the suite's
|
||||
@@ -11666,6 +11668,16 @@ export function lowerInstanceOf(lowerer: Lowerer, expr: ts.BinaryExpression, loc
|
||||
const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
|
||||
return { kind: "libCall", fn: "dyn.dataViewIs", args: [value], type: BOOL, loc };
|
||||
}
|
||||
{
|
||||
const eventKind = ["Event", "CustomEvent", "EventTarget"].findIndex((name) =>
|
||||
lowerer.isStdlibGlobal(expr.right, name),
|
||||
);
|
||||
if (eventKind >= 0) {
|
||||
const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
|
||||
const kind: IrExpr = { kind: "numLit", value: eventKind, type: F64, loc };
|
||||
return { kind: "libCall", fn: "event.is", args: [value, kind], type: BOOL, loc };
|
||||
}
|
||||
}
|
||||
if (lowerer.isStdlibGlobal(expr.right, "SharedArrayBuffer")) {
|
||||
const value = lowerer.coerceInto(expr.left, lowerer.lowerExpr(expr.left), DYN);
|
||||
return { kind: "libCall", fn: "sharedArrayBuffer.is", args: [value], type: BOOL, loc };
|
||||
|
||||
@@ -1771,7 +1771,7 @@ export function lowerStaticAbortSignalListenerCall(
|
||||
const receiverTs = lowerer.checker.getBaseTypeOfLiteralType(lowerer.typeOf(access.expression));
|
||||
const symbol = receiverTs.getAliasSymbol() ?? receiverTs.getSymbol();
|
||||
if (
|
||||
symbol?.name !== "AbortSignal" ||
|
||||
(symbol?.name !== "AbortSignal" && symbol?.name !== "EventTarget") ||
|
||||
!lowerer.checker
|
||||
.declarationsOf(symbol)
|
||||
.some((d) => ts.isInterfaceDeclaration(d) && lowerer.isStdlibFile(d.getSourceFile()))
|
||||
@@ -1803,6 +1803,40 @@ export function lowerStaticAbortSignalListenerCall(
|
||||
);
|
||||
}
|
||||
|
||||
/** `new EventTarget()`, `new Event(type, options?)` and `new
|
||||
* CustomEvent(type, options?)`: native handles built from the checked
|
||||
* argument vector (Node validates it in the constructor). */
|
||||
export function lowerEventTargetNew(lowerer: Lowerer, expr: ts.NewExpression): IrExpr | null {
|
||||
const name = stdlibGlobalNameOf(lowerer, expr.expression);
|
||||
if (name !== "EventTarget" && name !== "Event" && name !== "CustomEvent") return null;
|
||||
const args = expr.arguments ?? [];
|
||||
if (args.some(ts.isSpreadElement)) return null;
|
||||
const loc = locOf(expr);
|
||||
const values = args.map((arg) =>
|
||||
ts.isObjectLiteralExpression(arg)
|
||||
? lowerDynObjectLiteral(lowerer, arg)
|
||||
: lowerer.lowerExprExpecting(arg, DYN),
|
||||
);
|
||||
if (name === "EventTarget") {
|
||||
const result: IrExpr = { kind: "libCall", fn: "event.targetNew", args: [], type: DYN, loc };
|
||||
if (values.length === 0) return result;
|
||||
return {
|
||||
kind: "seqExpr",
|
||||
stmts: values.map((value): IrStmt => ({ kind: "exprStmt", expr: value, loc: value.loc })),
|
||||
result,
|
||||
type: DYN,
|
||||
loc,
|
||||
};
|
||||
}
|
||||
return {
|
||||
kind: "libCall",
|
||||
fn: name === "Event" ? "event.new" : "event.customNew",
|
||||
args: [{ kind: "dynArrLit", elems: values, type: DYN, loc }],
|
||||
type: DYN,
|
||||
loc,
|
||||
};
|
||||
}
|
||||
|
||||
export function lowerAbortControllerNew(lowerer: Lowerer, expr: ts.NewExpression): IrExpr | null {
|
||||
if (!ts.isIdentifier(expr.expression) || expr.expression.text !== "AbortController") {
|
||||
return null;
|
||||
|
||||
@@ -443,6 +443,7 @@ import {
|
||||
refuseUnsupportedApi,
|
||||
stdlibGlobalFnOf,
|
||||
lowerAbortControllerNew,
|
||||
lowerEventTargetNew,
|
||||
lowerDynamicImportCall,
|
||||
lowerFetchCall,
|
||||
lowerFetchElementMethodCall,
|
||||
@@ -10670,6 +10671,7 @@ export class Lowerer {
|
||||
const builtin =
|
||||
lowerLifetimeRegionNew(this, expr) ??
|
||||
lowerAbortControllerNew(this, expr) ??
|
||||
lowerEventTargetNew(this, expr) ??
|
||||
lowerFetchWebNew(this, expr) ??
|
||||
lowerResponseNew(this, expr) ??
|
||||
lowerStaticReadableStreamNew(this, expr) ??
|
||||
|
||||
@@ -127,7 +127,12 @@ export const FETCH_AMBIENT_TYPES = [
|
||||
"ResponseInit",
|
||||
"RequestInit",
|
||||
"Event",
|
||||
"CustomEvent",
|
||||
"EventTarget",
|
||||
"AddEventListenerOptions",
|
||||
"EventListenerOptions",
|
||||
"EventInit",
|
||||
"CustomEventInit",
|
||||
"AbortController",
|
||||
"AbortSignal",
|
||||
"Headers",
|
||||
@@ -2107,9 +2112,11 @@ function mapTypeInner(type: ts.Type, ctx: TypeMapperCtx): IrType | null {
|
||||
}
|
||||
// The stdlib Console interface (the global console and node:console
|
||||
// Console instances) is a native handle carried by the checked-dynamic
|
||||
// representation and consumed by the console method lowerings.
|
||||
// representation and consumed by the console method lowerings. The
|
||||
// stdlib Disposable (events.addAbortListener's result) is a checked
|
||||
// object carrying its Symbol.dispose method.
|
||||
if (
|
||||
psym?.name === "Console" &&
|
||||
(psym?.name === "Console" || psym?.name === "Disposable") &&
|
||||
checker
|
||||
.declarationsOf(psym)
|
||||
.some((d) => ts.isInterfaceDeclaration(d) && ctx.isStdlibFile(d.getSourceFile()))
|
||||
|
||||
@@ -507,6 +507,7 @@ async function compileExecutableNative(
|
||||
textDecoderLegacy: features.textDecoderLegacy,
|
||||
fileHandle: features.fileHandle,
|
||||
fetch: features.fetch,
|
||||
...(features.eventTarget ? { eventTarget: true } : {}),
|
||||
zlib: features.zlib,
|
||||
assert: features.assert,
|
||||
inspect: features.inspect,
|
||||
|
||||
@@ -88,6 +88,9 @@ export const MAY_THROW_LIB_FNS: ReadonlySet<IrLibFn> = new Set([
|
||||
"fetch.abortAny",
|
||||
"abort.timeout",
|
||||
"abort.any",
|
||||
"event.new",
|
||||
"event.customNew",
|
||||
"events.addAbortListener",
|
||||
"fetch.streamNew",
|
||||
"fetch.streamFrom",
|
||||
"num.toFixed",
|
||||
|
||||
@@ -64,6 +64,16 @@ export const LIB_FN_SIGS = defineLibFnSignatures({
|
||||
"fetch.responseNew": { argTypes: [DYN, DYN], result: DYN },
|
||||
"fetch.input": { argTypes: [DYN, DYN], result: { kind: "promise", inner: DYN } },
|
||||
"abort.controllerNew": { argTypes: [], result: DYN },
|
||||
/** EventTarget / Event / CustomEvent construction (scr_event_target.c):
|
||||
* the Event constructors take the argument vector as one dyn array and
|
||||
* throw Node's validation errors; `event.is` is `v instanceof Event`
|
||||
* (kind 0), `CustomEvent` (1), `EventTarget` (2). addAbortListener
|
||||
* answers the disposer (the Disposable's [Symbol.dispose]). */
|
||||
"event.targetNew": { argTypes: [], result: DYN },
|
||||
"event.new": { argTypes: [DYN], result: DYN },
|
||||
"event.customNew": { argTypes: [DYN], result: DYN },
|
||||
"event.is": { argTypes: [DYN, F64], result: BOOL },
|
||||
"events.addAbortListener": { argTypes: [DYN, DYN], result: DYN },
|
||||
"abort.timeout": { argTypes: [DYN], result: DYN },
|
||||
"abort.now": { argTypes: [DYN], result: DYN },
|
||||
"abort.any": { argTypes: [DYN], result: DYN },
|
||||
|
||||
@@ -9,6 +9,7 @@ export interface RuntimeFeatures {
|
||||
legacyTextDecoder: boolean;
|
||||
fileHandle: boolean;
|
||||
fetch: boolean;
|
||||
eventTarget: boolean;
|
||||
processEvents: boolean;
|
||||
emitter: boolean;
|
||||
stream: boolean;
|
||||
@@ -78,6 +79,7 @@ function scanRuntimeFeatures(mod: IrModule, stopAt?: keyof RuntimeFeatures): Run
|
||||
legacyTextDecoder: false,
|
||||
fileHandle: false,
|
||||
fetch: false,
|
||||
eventTarget: false,
|
||||
processEvents: false,
|
||||
emitter:
|
||||
mod.workers === true || (mod.classes ?? []).some((c) => c.name === RUNTIME_EMITTER_CLASS),
|
||||
@@ -124,6 +126,7 @@ function scanRuntimeFeatures(mod: IrModule, stopAt?: keyof RuntimeFeatures): Run
|
||||
)
|
||||
features.legacyTextDecoder = true;
|
||||
if (fn.startsWith("fetch.")) features.fetch = true;
|
||||
if (fn.startsWith("event.") || fn === "events.addAbortListener") features.eventTarget = true;
|
||||
if (PROCESS_EVENT_LIB_FNS.has(fn)) features.processEvents = true;
|
||||
if (fn.startsWith("emitter.")) features.emitter = true;
|
||||
if (
|
||||
|
||||
@@ -142,6 +142,7 @@ export function moduleLibAsyncSurface(mod: IrModule): { surface: string; loc: Sr
|
||||
[features.stream, "the node:stream surface"],
|
||||
[features.tls, "the node:tls surface"],
|
||||
[features.fetch, "fetch"],
|
||||
[features.eventTarget, "the EventTarget surface"],
|
||||
[features.nodeTest, "the node:test surface"],
|
||||
[features.dynAsync, "the checked-dynamic async surface"],
|
||||
[features.dc, "the diagnostics_channel surface"],
|
||||
|
||||
@@ -73,6 +73,7 @@ test.skipIf(process.platform !== "linux" && process.platform !== "darwin")(
|
||||
systemLibraries: [],
|
||||
features: {
|
||||
workers: false,
|
||||
eventTarget: false,
|
||||
regex: true,
|
||||
copying: false,
|
||||
textDecoderLegacy: false,
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5130-event-target-dispatch.js"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5131-event-target-listener-errors.js"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5132-event-target-typed.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5133-abort-signal-event-target.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5134-events-error-monitor.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/5135-event-target-warnings.js"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -52,6 +52,7 @@ const OPTIONAL = [
|
||||
["scr_console_native.c", "inspect"],
|
||||
["scr_dyn_invoke.c", any("dynInvoke", "nativeFetch")],
|
||||
["scr_dc.c", "dc"],
|
||||
["scr_event_target.c", any("eventTarget", "nativeFetch")],
|
||||
["scr_async_dyn.c", any("dynAsync", "dynInvoke", "dc", "fileHandle", "nativeFetch")],
|
||||
["scr_zlib.c", "zlib"],
|
||||
["scr_events.c", "events"],
|
||||
|
||||
@@ -3,10 +3,12 @@ import { fileURLToPath } from "node:url";
|
||||
process.env.SCRIPTC_RUNTIME_PACK_ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
||||
process.env.SCRIPTC_RUNTIME_PACK_CONFIG = JSON.stringify({
|
||||
platform: "wasi",
|
||||
extraRuntimeSources: ["scr_abort.c"],
|
||||
// AbortSignal is portable here and dispatches through the EventTarget unit.
|
||||
extraRuntimeSources: ["scr_abort.c", "scr_event_target.c"],
|
||||
runtimeDefines: ["_GNU_SOURCE", "_WASI_EMULATED_SIGNAL", "_WASI_EMULATED_PROCESS_CLOCKS"],
|
||||
omitRuntimeSources: ["scr_child.c"],
|
||||
omitOptionalSources: [
|
||||
"scr_event_target.c",
|
||||
"scr_loop_kqueue.c",
|
||||
"scr_loop_epoll.c",
|
||||
"scr_loop_wsapoll.c",
|
||||
|
||||
@@ -621,6 +621,7 @@ void scr_process_on_warning(ScrDyn *fn) {
|
||||
scr_throw_error_msg_code(SCR_ERR_TYPE, msg, strlen(msg), "ERR_INVALID_ARG_TYPE");
|
||||
return;
|
||||
}
|
||||
scr_warning_emit_hook = scr_emit_warning;
|
||||
if (scr_nwarn == scr_warn_cap) {
|
||||
scr_warn_cap = scr_warn_cap ? scr_warn_cap * 2 : 4;
|
||||
scr_warn_listeners = realloc(scr_warn_listeners, scr_warn_cap * sizeof *scr_warn_listeners);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -322,8 +322,30 @@ static bool scr_ee_emit_va(ScrEmitter *em, ScrStr *name, ...);
|
||||
/* Fires a meta event with the affected event's NAME as the argument
|
||||
* (listeners are fenced to arity <= 1, so the listener-function argument
|
||||
* Node passes second is never read). */
|
||||
/* events.errorMonitor's registry key (a symbol in Node; the compiler
|
||||
* spells it with a leading NUL no admitted literal name carries). */
|
||||
static const char scr_ee_error_monitor[] = "\0events.errorMonitor";
|
||||
#define SCR_EE_ERROR_MONITOR_LEN (sizeof scr_ee_error_monitor - 1)
|
||||
|
||||
static bool scr_ee_is_error_monitor(const ScrStr *name) {
|
||||
return name->len == SCR_EE_ERROR_MONITOR_LEN &&
|
||||
memcmp(name->data, scr_ee_error_monitor, SCR_EE_ERROR_MONITOR_LEN) == 0;
|
||||
}
|
||||
|
||||
static void scr_ee_symbol_name_fence(const char *what) {
|
||||
char message[160];
|
||||
snprintf(message, sizeof message, "%s with an events.errorMonitor listener is not supported yet",
|
||||
what);
|
||||
scr_throw_error(SCR_ERR_ERROR, scr_str_new(message, strlen(message)));
|
||||
}
|
||||
|
||||
static void scr_ee_emit_meta(ScrEmitter *em, const char *meta, ScrStr *name) {
|
||||
if (!scr_ee_has(em->reg, meta)) return;
|
||||
/* Node passes the symbol itself, which the string tuple cannot carry. */
|
||||
if (scr_ee_is_error_monitor(name)) {
|
||||
scr_ee_symbol_name_fence(meta);
|
||||
return;
|
||||
}
|
||||
ScrStr *m = scr_str_new(meta, strlen(meta));
|
||||
scr_ee_emit_va(em, m, name);
|
||||
scr_str_release(m);
|
||||
@@ -631,8 +653,26 @@ ScrEmitter *scr_emitter_remove_all(ScrEmitter *em, ScrStr *name, bool all) {
|
||||
* still holds it (the shared fired flag — Node's wrapper.fired). A
|
||||
* listener throw stops the pass (the exception stays pending for the
|
||||
* caller). Returns whether the bucket had listeners. */
|
||||
static bool scr_ee_emit_core(ScrEmitter *em, ScrStr *name, va_list *ap_template,
|
||||
ScrDyn *const *flex_args, size_t flex_argc);
|
||||
|
||||
/* emit('error', ...) first emits the same arguments to errorMonitor. */
|
||||
static void scr_ee_emit_error_monitor(ScrEmitter *em, va_list *ap_template,
|
||||
ScrDyn *const *flex_args, size_t flex_argc) {
|
||||
ScrEeBucket *b =
|
||||
em->reg ? scr_ee_bucket_find(em->reg, scr_ee_error_monitor, SCR_EE_ERROR_MONITOR_LEN) : NULL;
|
||||
if (!b || b->n == 0) return;
|
||||
ScrStr *monitor = scr_str_new(scr_ee_error_monitor, SCR_EE_ERROR_MONITOR_LEN);
|
||||
scr_ee_emit_core(em, monitor, ap_template, flex_args, flex_argc);
|
||||
scr_str_release(monitor);
|
||||
}
|
||||
|
||||
static bool scr_ee_emit_core(ScrEmitter *em, ScrStr *name, va_list *ap_template,
|
||||
ScrDyn *const *flex_args, size_t flex_argc) {
|
||||
if (name->len == 5 && memcmp(name->data, "error", 5) == 0) {
|
||||
scr_ee_emit_error_monitor(em, ap_template, flex_args, flex_argc);
|
||||
if (scr_exc_pending()) return true;
|
||||
}
|
||||
ScrEeBucket *b = em->reg ? scr_ee_bucket_find(em->reg, name->data, name->len) : NULL;
|
||||
if (!b || b->n == 0) return false;
|
||||
size_t n = b->n;
|
||||
@@ -719,6 +759,8 @@ bool scr_emitter_emit_flex(ScrEmitter *em, ScrStr *name, ScrDyn *const *args, si
|
||||
* errors. Keep this separate from the statically typed Error ABI above. */
|
||||
bool scr_emitter_emit_error_flex(ScrEmitter *em, ScrDyn *error) {
|
||||
if (!scr_ee_has(em->reg, "error")) {
|
||||
scr_ee_emit_error_monitor(em, NULL, &error, 1);
|
||||
if (scr_exc_pending()) return false;
|
||||
scr_dyn_throw(scr_dyn_retain(error));
|
||||
return false;
|
||||
}
|
||||
@@ -731,8 +773,17 @@ bool scr_emitter_emit_error_flex(ScrEmitter *em, ScrDyn *error) {
|
||||
/* emit('error', err) — Node's special event: with no 'error' listener the
|
||||
* payload THROWS (catchable; uncaught exits 1 with the error report).
|
||||
* err is borrowed; the throw takes its own +1. */
|
||||
static void scr_ee_emit_error_monitor_va(ScrEmitter *em, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, em);
|
||||
scr_ee_emit_error_monitor(em, &ap, NULL, 0);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
bool scr_emitter_emit_error(ScrEmitter *em, ScrStr *name, ScrError *err) {
|
||||
if (!scr_ee_has(em->reg, "error")) {
|
||||
scr_ee_emit_error_monitor_va(em, err);
|
||||
if (scr_exc_pending()) return false;
|
||||
scr_throw_obj(scr_error_retain(err), &scr_error_retain_v, &scr_error_release_v,
|
||||
scr_error_trace_arg());
|
||||
return false;
|
||||
@@ -781,7 +832,14 @@ ScrArr *scr_emitter_event_names(ScrEmitter *em) {
|
||||
ScrArr *out = scr_arr_new(SCR_ELEM_STR, 0);
|
||||
if (em->reg) {
|
||||
for (ScrEeBucket *b = em->reg->head; b; b = b->next) {
|
||||
if (b->n > 0) scr_arr_push_ref(out, scr_str_retain(b->name));
|
||||
if (b->n == 0) continue;
|
||||
/* Node lists the errorMonitor symbol, which a string[] cannot hold. */
|
||||
if (scr_ee_is_error_monitor(b->name)) {
|
||||
scr_arr_release(out);
|
||||
scr_ee_symbol_name_fence("eventNames()");
|
||||
return scr_arr_new(SCR_ELEM_STR, 0);
|
||||
}
|
||||
scr_arr_push_ref(out, scr_str_retain(b->name));
|
||||
}
|
||||
}
|
||||
return out;
|
||||
|
||||
@@ -26,7 +26,6 @@ static void sf_signal_install(void);
|
||||
typedef struct SfTransfer SfTransfer;
|
||||
typedef struct SfSignal SfSignal;
|
||||
typedef struct SfSignalWatch SfSignalWatch;
|
||||
typedef struct SfEvent SfEvent;
|
||||
typedef struct SfStream SfStream;
|
||||
typedef struct SfReader SfReader;
|
||||
typedef struct SfResponse SfResponse;
|
||||
@@ -53,17 +52,6 @@ struct SfTypedRef {
|
||||
SfTypedRef *next;
|
||||
};
|
||||
|
||||
typedef struct SfAbortListener {
|
||||
ScrDyn *listener;
|
||||
bool capture;
|
||||
bool once;
|
||||
size_t id;
|
||||
SfSignal *target; /* weak; the target owns this listener */
|
||||
SfSignal *option_signal; /* owned unless it is the target */
|
||||
SfSignalWatch *signal_watch;
|
||||
struct SfAbortListener *next;
|
||||
} SfAbortListener;
|
||||
|
||||
struct SfSignalWatch {
|
||||
SfSignal *source; /* weak; NULL after the source fires */
|
||||
void *owner; /* owner keeps source alive */
|
||||
@@ -80,26 +68,12 @@ struct SfSignal {
|
||||
ScrDynHandleTag reason_self_tag;
|
||||
bool reason_tracked;
|
||||
SfSignal *reason_next;
|
||||
ScrDyn *onabort;
|
||||
size_t onabort_order;
|
||||
SfAbortListener *listeners;
|
||||
size_t next_listener_id;
|
||||
ScrEventTargetCore core; /* listeners and the onabort attribute */
|
||||
SfSignalWatch *watchers;
|
||||
SfSignal **sources;
|
||||
SfSignalWatch **source_watches;
|
||||
size_t source_count;
|
||||
double timer_id;
|
||||
bool callbacks_tracked;
|
||||
SfSignal *callbacks_next;
|
||||
};
|
||||
|
||||
struct SfEvent {
|
||||
size_t rc;
|
||||
SfSignal *target;
|
||||
double time_stamp;
|
||||
bool dispatching;
|
||||
bool stop_propagation;
|
||||
bool stop_immediate;
|
||||
};
|
||||
|
||||
struct SfReader {
|
||||
@@ -248,7 +222,6 @@ static SfTransfer *sf_live;
|
||||
/* Weak registries: owners unlink themselves on destruction. They let the
|
||||
* fetch teardown sever opaque native→dyn edges before final cycle collection,
|
||||
* including otherwise-untraceable callback/reason→handle backedges. */
|
||||
static SfSignal *sf_callback_signals;
|
||||
static SfSignal *sf_reason_signals;
|
||||
#ifndef SCR_FETCH_SIGNAL_ONLY
|
||||
static SfStream *sf_callback_streams;
|
||||
@@ -340,26 +313,6 @@ static ScrDyn *sf_type_reason(const char *message, ScrError **error_out) {
|
||||
|
||||
/* ── AbortSignal ─────────────────────────────────────────────────── */
|
||||
|
||||
static void sf_signal_track_callbacks(SfSignal *s) {
|
||||
if (s->callbacks_tracked) return;
|
||||
s->callbacks_tracked = true;
|
||||
s->callbacks_next = sf_callback_signals;
|
||||
sf_callback_signals = s;
|
||||
}
|
||||
|
||||
static void sf_signal_untrack_callbacks(SfSignal *s) {
|
||||
if (!s->callbacks_tracked) return;
|
||||
for (SfSignal **at = &sf_callback_signals; *at;
|
||||
at = &(*at)->callbacks_next) {
|
||||
if (*at == s) {
|
||||
*at = s->callbacks_next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
s->callbacks_tracked = false;
|
||||
s->callbacks_next = NULL;
|
||||
}
|
||||
|
||||
static void sf_signal_track_reason(SfSignal *s) {
|
||||
if (s->reason_tracked) return;
|
||||
s->reason_tracked = true;
|
||||
@@ -414,6 +367,8 @@ static SfSignal *sf_signal_new(void) {
|
||||
SfSignal *s = calloc(1, sizeof *s);
|
||||
if (!s) sf_oom();
|
||||
s->rc = 1;
|
||||
/* AbortSignal sets kMaxEventTargetListeners to 0: no leak warning. */
|
||||
scr_et_core_init(&s->core, 0, "AbortSignal");
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -437,32 +392,6 @@ static void sf_watch_free(SfSignalWatch *w) {
|
||||
free(w);
|
||||
}
|
||||
|
||||
static void sf_abort_listener_free(SfAbortListener *l) {
|
||||
if (!l) return;
|
||||
sf_watch_free(l->signal_watch);
|
||||
if (l->option_signal && l->option_signal != l->target) {
|
||||
sf_signal_release(l->option_signal);
|
||||
}
|
||||
scr_dyn_release(l->listener);
|
||||
free(l);
|
||||
}
|
||||
|
||||
static void sf_signal_drop_callbacks(SfSignal *s) {
|
||||
SfAbortListener *listeners = s->listeners;
|
||||
ScrDyn *onabort = s->onabort;
|
||||
s->listeners = NULL; /* unlink before callback releases can re-enter */
|
||||
s->onabort = NULL;
|
||||
s->onabort_order = 0;
|
||||
sf_signal_untrack_callbacks(s);
|
||||
while (listeners) {
|
||||
SfAbortListener *next = listeners->next;
|
||||
listeners->next = NULL;
|
||||
sf_abort_listener_free(listeners);
|
||||
listeners = next;
|
||||
}
|
||||
scr_dyn_release(onabort);
|
||||
}
|
||||
|
||||
static void sf_signal_release(SfSignal *s) {
|
||||
if (!s || --s->rc > 0) return;
|
||||
for (size_t i = 0; i < s->source_count; i++) {
|
||||
@@ -471,7 +400,7 @@ static void sf_signal_release(SfSignal *s) {
|
||||
}
|
||||
free(s->source_watches);
|
||||
free(s->sources);
|
||||
sf_signal_drop_callbacks(s);
|
||||
scr_et_core_drop(&s->core);
|
||||
sf_signal_drop_reason(s);
|
||||
free(s);
|
||||
}
|
||||
@@ -483,148 +412,6 @@ static void sf_signal_release_v(void *p) {
|
||||
sf_signal_release((SfSignal *)p);
|
||||
}
|
||||
|
||||
static SfEvent *sf_event_new(SfSignal *target) {
|
||||
SfEvent *event = calloc(1, sizeof *event);
|
||||
if (!event) sf_oom();
|
||||
event->rc = 1;
|
||||
event->target = sf_signal_retain(target);
|
||||
event->time_stamp = scr_perf_now();
|
||||
event->dispatching = true;
|
||||
return event;
|
||||
}
|
||||
|
||||
static SfEvent *sf_event_retain(SfEvent *event) {
|
||||
event->rc++;
|
||||
return event;
|
||||
}
|
||||
|
||||
static void sf_event_release(SfEvent *event) {
|
||||
if (!event || --event->rc > 0) return;
|
||||
sf_signal_release(event->target);
|
||||
free(event);
|
||||
}
|
||||
|
||||
static void *sf_event_retain_v(void *ptr) {
|
||||
return sf_event_retain((SfEvent *)ptr);
|
||||
}
|
||||
|
||||
static void sf_event_release_v(void *ptr) {
|
||||
sf_event_release((SfEvent *)ptr);
|
||||
}
|
||||
|
||||
static void *sf_listener_error_retain_v(void *ptr) {
|
||||
return scr_caught_retain((ScrCaught *)ptr);
|
||||
}
|
||||
|
||||
static void sf_listener_error_release_v(void *ptr) {
|
||||
scr_caught_release((ScrCaught *)ptr);
|
||||
}
|
||||
|
||||
static void sf_listener_error_tick(ScrClosure *cb) {
|
||||
ScrCaught *caught = scr_box_get_ref(cb->caps[0]);
|
||||
scr_rethrow(caught);
|
||||
scr_caught_release(caught);
|
||||
}
|
||||
|
||||
/*
|
||||
* EventTarget reports listener exceptions as uncaught next-tick failures;
|
||||
* dispatchEvent itself keeps running listeners and returns its boolean.
|
||||
* Queue every failure separately so an uncaughtException handler that
|
||||
* recovers from one still observes later listener failures in order.
|
||||
*/
|
||||
static void sf_defer_listener_error(ScrCaught *caught) {
|
||||
ScrClosure *cb =
|
||||
scr_closure_new((void *)&sf_listener_error_tick, 1);
|
||||
cb->caps[0] = scr_box_new_obj(
|
||||
&sf_listener_error_retain_v, &sf_listener_error_release_v, NULL);
|
||||
scr_box_set_ref(cb->caps[0], caught); /* moves */
|
||||
scr_next_tick(cb);
|
||||
}
|
||||
|
||||
static ScrDyn *sf_event_invoke(void *ptr, ScrDyn *self,
|
||||
const char *method, ScrDyn *const *args,
|
||||
size_t argc, const char *what) {
|
||||
SfEvent *event = ptr;
|
||||
(void)self;
|
||||
(void)args;
|
||||
(void)argc;
|
||||
if (strcmp(method, "preventDefault") == 0) {
|
||||
/* Abort events are not cancelable. */
|
||||
return scr_dyn_retain(scr_dyn_undefined());
|
||||
}
|
||||
if (strcmp(method, "stopPropagation") == 0) {
|
||||
event->stop_propagation = true;
|
||||
return scr_dyn_retain(scr_dyn_undefined());
|
||||
}
|
||||
if (strcmp(method, "stopImmediatePropagation") == 0) {
|
||||
event->stop_propagation = true;
|
||||
event->stop_immediate = true;
|
||||
return scr_dyn_retain(scr_dyn_undefined());
|
||||
}
|
||||
if (strcmp(method, "composedPath") == 0) {
|
||||
ScrDyn *path = scr_dyn_new_arr();
|
||||
if (event->dispatching) {
|
||||
scr_dyn_arr_push(
|
||||
path,
|
||||
scr_dyn_new_handle(event->target, SCR_DYNH_ABORT_SIGNAL));
|
||||
}
|
||||
return path;
|
||||
}
|
||||
sf_not_function(what);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ScrDyn *sf_event_get(void *ptr, const char *key, size_t len) {
|
||||
SfEvent *event = ptr;
|
||||
if (sf_name(key, len, "type")) {
|
||||
ScrStr *type = scr_str_new("abort", 5);
|
||||
ScrDyn *out = scr_dyn_new_str(type);
|
||||
scr_str_release(type);
|
||||
return out;
|
||||
}
|
||||
if (sf_name(key, len, "bubbles") ||
|
||||
sf_name(key, len, "cancelable") ||
|
||||
sf_name(key, len, "composed") ||
|
||||
sf_name(key, len, "defaultPrevented")) {
|
||||
return scr_dyn_new_bool(false);
|
||||
}
|
||||
if (sf_name(key, len, "eventPhase")) {
|
||||
return scr_dyn_new_num(event->dispatching ? 2 : 0);
|
||||
}
|
||||
if (sf_name(key, len, "target") ||
|
||||
sf_name(key, len, "srcElement")) {
|
||||
return scr_dyn_new_handle(event->target, SCR_DYNH_ABORT_SIGNAL);
|
||||
}
|
||||
if (sf_name(key, len, "currentTarget")) {
|
||||
return event->dispatching
|
||||
? scr_dyn_new_handle(event->target, SCR_DYNH_ABORT_SIGNAL)
|
||||
: scr_dyn_new_null();
|
||||
}
|
||||
if (sf_name(key, len, "isTrusted")) return scr_dyn_new_bool(true);
|
||||
if (sf_name(key, len, "timeStamp")) {
|
||||
return scr_dyn_new_num(event->time_stamp);
|
||||
}
|
||||
if (sf_name(key, len, "cancelBubble")) {
|
||||
return scr_dyn_new_bool(event->stop_propagation);
|
||||
}
|
||||
if (sf_name(key, len, "returnValue")) return scr_dyn_new_bool(true);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool sf_event_set(void *ptr, const char *key, size_t len,
|
||||
const ScrDyn *value) {
|
||||
SfEvent *event = ptr;
|
||||
if (sf_name(key, len, "cancelBubble")) {
|
||||
if (scr_dyn_truthy(value)) event->stop_propagation = true;
|
||||
return true;
|
||||
}
|
||||
if (sf_name(key, len, "returnValue")) {
|
||||
/* Setting false only prevents a cancelable event's default action. */
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static SfSignalWatch *sf_signal_watch(SfSignal *source, void *owner,
|
||||
void (*fire)(SfSignalWatch *,
|
||||
SfSignal *)) {
|
||||
@@ -638,199 +425,12 @@ static SfSignalWatch *sf_signal_watch(SfSignal *source, void *owner,
|
||||
return w;
|
||||
}
|
||||
|
||||
static void sf_listener_signal_abort(SfSignalWatch *w,
|
||||
SfSignal *source) {
|
||||
(void)source;
|
||||
SfAbortListener *listener = w->owner;
|
||||
SfSignal *target = listener->target;
|
||||
for (SfAbortListener **at = &target->listeners; *at;
|
||||
at = &(*at)->next) {
|
||||
if (*at == listener) {
|
||||
*at = listener->next;
|
||||
sf_abort_listener_free(listener);
|
||||
return;
|
||||
}
|
||||
}
|
||||
sf_watch_free(w);
|
||||
}
|
||||
|
||||
static void sf_signal_dispatch_listeners(SfSignal *s, ScrDyn *provided_event) {
|
||||
SfEvent *event_state;
|
||||
ScrDyn *event;
|
||||
if (provided_event) {
|
||||
event = scr_dyn_retain(provided_event);
|
||||
event_state = (SfEvent *)provided_event->v.handle.ptr;
|
||||
SfSignal *old_target = event_state->target;
|
||||
event_state->target = sf_signal_retain(s);
|
||||
sf_signal_release(old_target);
|
||||
event_state->dispatching = true;
|
||||
} else {
|
||||
event_state = sf_event_new(s);
|
||||
event = scr_dyn_new_handle(event_state, SCR_DYNH_EVENT);
|
||||
sf_event_release(event_state);
|
||||
}
|
||||
/*
|
||||
* EventTarget dispatch snapshots the handlers present at the start:
|
||||
* additions wait for the next event, removals before a listener's turn
|
||||
* suppress it, and a listener may remove itself safely. `onabort` occupies
|
||||
* the position where it was first assigned, just like an event-handler
|
||||
* attribute in the DOM. Holding raw list nodes across the user callback is
|
||||
* not safe because removeEventListener frees those nodes.
|
||||
*/
|
||||
size_t count = s->onabort && s->onabort->kind == SCR_DYN_FUNC ? 1 : 0;
|
||||
for (SfAbortListener *l = s->listeners; l; l = l->next) count++;
|
||||
typedef struct {
|
||||
ScrDyn *listener;
|
||||
bool once;
|
||||
bool attribute;
|
||||
size_t id;
|
||||
} SfAbortDispatch;
|
||||
SfAbortDispatch *dispatch =
|
||||
count ? calloc(count, sizeof *dispatch) : NULL;
|
||||
if (count && !dispatch) sf_oom();
|
||||
size_t i = 0;
|
||||
if (s->onabort && s->onabort->kind == SCR_DYN_FUNC) {
|
||||
dispatch[i].listener = scr_dyn_retain(s->onabort);
|
||||
dispatch[i].attribute = true;
|
||||
dispatch[i].id = s->onabort_order;
|
||||
i++;
|
||||
}
|
||||
for (SfAbortListener *l = s->listeners; l; l = l->next) {
|
||||
dispatch[i].listener = scr_dyn_retain(l->listener);
|
||||
dispatch[i].once = l->once;
|
||||
dispatch[i].id = l->id;
|
||||
i++;
|
||||
}
|
||||
for (i = 1; i < count; i++) {
|
||||
SfAbortDispatch entry = dispatch[i];
|
||||
size_t j = i;
|
||||
while (j > 0 && dispatch[j - 1].id > entry.id) {
|
||||
dispatch[j] = dispatch[j - 1];
|
||||
j--;
|
||||
}
|
||||
dispatch[j] = entry;
|
||||
}
|
||||
|
||||
for (i = 0; i < count && !event_state->stop_immediate; i++) {
|
||||
if (dispatch[i].attribute) {
|
||||
if (!s->onabort || s->onabort_order != dispatch[i].id) {
|
||||
scr_dyn_release(dispatch[i].listener);
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* Assignment replaces the handler without moving its registration
|
||||
* position. Invoke the current value if an earlier callback replaced
|
||||
* it during this dispatch.
|
||||
*/
|
||||
ScrDyn *handler = scr_dyn_retain(s->onabort);
|
||||
ScrDyn *args[1] = {event};
|
||||
scr_dyn_this_push(s, SCR_DYNH_ABORT_SIGNAL);
|
||||
ScrDyn *r = scr_dyn_call(handler, args, 1, "signal.onabort");
|
||||
scr_dyn_this_pop();
|
||||
scr_dyn_release(r);
|
||||
scr_dyn_release(handler);
|
||||
scr_dyn_release(dispatch[i].listener);
|
||||
if (scr_exc_pending()) {
|
||||
sf_defer_listener_error(scr_exc_take());
|
||||
}
|
||||
if (event_state->stop_immediate) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
SfAbortListener **at = &s->listeners;
|
||||
while (*at && (*at)->id != dispatch[i].id) {
|
||||
at = &(*at)->next;
|
||||
}
|
||||
if (!*at) {
|
||||
scr_dyn_release(dispatch[i].listener);
|
||||
continue;
|
||||
}
|
||||
if (dispatch[i].once) {
|
||||
SfAbortListener *l = *at;
|
||||
*at = l->next;
|
||||
sf_abort_listener_free(l);
|
||||
}
|
||||
ScrDyn *callable = dispatch[i].listener;
|
||||
ScrDyn *current_listener = NULL;
|
||||
ScrDyn *owned_handle = NULL;
|
||||
ScrDyn *this_listener = NULL;
|
||||
if (callable->kind == SCR_DYN_OBJ) {
|
||||
/*
|
||||
* EventListener is a Web IDL callback interface: handleEvent is read
|
||||
* when the callback runs, not when it is registered. Typed records
|
||||
* normally cross into dyn as value snapshots, so explicitly refresh
|
||||
* an identity-carrying listener from its retained source here.
|
||||
*/
|
||||
current_listener =
|
||||
callable->v.obj.source_identity && callable->v.obj.source_access
|
||||
? callable->v.obj.source_access(
|
||||
callable->v.obj.source_identity, true)
|
||||
: scr_dyn_retain(callable);
|
||||
this_listener = current_listener;
|
||||
const ScrDyn *handle =
|
||||
scr_dyn_obj_get(current_listener, "handleEvent", 11);
|
||||
if (!handle) {
|
||||
scr_dyn_release(current_listener);
|
||||
scr_dyn_release(dispatch[i].listener);
|
||||
continue;
|
||||
}
|
||||
callable = (ScrDyn *)handle;
|
||||
} else if (callable->kind == SCR_DYN_TYPED_REF) {
|
||||
/* A live static record must remain the EventListener receiver so
|
||||
* handleEvent mutations commit to the original source. Arrays and
|
||||
* byte views are valid Web IDL objects but have no handleEvent in
|
||||
* the statically modeled surface, so dispatching them is a no-op. */
|
||||
current_listener = scr_dyn_typed_ref_materialize(callable);
|
||||
if (current_listener->kind != SCR_DYN_OBJ) {
|
||||
scr_dyn_release(current_listener);
|
||||
scr_dyn_release(dispatch[i].listener);
|
||||
continue;
|
||||
}
|
||||
const ScrDyn *handle =
|
||||
scr_dyn_obj_get(current_listener, "handleEvent", 11);
|
||||
if (!handle) {
|
||||
scr_dyn_release(current_listener);
|
||||
scr_dyn_release(dispatch[i].listener);
|
||||
continue;
|
||||
}
|
||||
this_listener = callable;
|
||||
callable = (ScrDyn *)handle;
|
||||
} else if (callable->kind != SCR_DYN_FUNC) {
|
||||
/* Web IDL accepts every object as an EventListener. Native arrays,
|
||||
* byte views, handles, and promises cannot carry an expando
|
||||
* handleEvent in the static runtime, so a missing method is a no-op. */
|
||||
scr_dyn_release(dispatch[i].listener);
|
||||
continue;
|
||||
}
|
||||
ScrDyn *args[1] = {event};
|
||||
if (this_listener) {
|
||||
scr_dyn_this_push_dyn(this_listener);
|
||||
} else {
|
||||
scr_dyn_this_push(s, SCR_DYNH_ABORT_SIGNAL);
|
||||
}
|
||||
ScrDyn *r =
|
||||
scr_dyn_call(callable, args, 1, "abort listener");
|
||||
scr_dyn_this_pop();
|
||||
scr_dyn_release(r);
|
||||
scr_dyn_release(owned_handle);
|
||||
scr_dyn_release(current_listener);
|
||||
scr_dyn_release(dispatch[i].listener);
|
||||
if (scr_exc_pending()) {
|
||||
sf_defer_listener_error(scr_exc_take());
|
||||
}
|
||||
if (event_state->stop_immediate) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (i < count) {
|
||||
scr_dyn_release(dispatch[i].listener);
|
||||
i++;
|
||||
}
|
||||
event_state->dispatching = false;
|
||||
free(dispatch);
|
||||
/* runAbort: a trusted 'abort' Event through the signal's EventTarget. */
|
||||
static void sf_signal_dispatch_abort(SfSignal *s) {
|
||||
ScrDyn *event = scr_event_new_trusted("abort");
|
||||
ScrDyn *self = scr_dyn_new_handle(s, SCR_DYNH_ABORT_SIGNAL);
|
||||
scr_et_dispatch(&s->core, self, event);
|
||||
scr_dyn_release(self);
|
||||
scr_dyn_release(event);
|
||||
}
|
||||
|
||||
@@ -855,7 +455,7 @@ static void sf_signal_abort_full(SfSignal *s, ScrDyn *reason,
|
||||
w->source = NULL;
|
||||
w->fire(w, s); /* the owner may free w */
|
||||
}
|
||||
sf_signal_dispatch_listeners(s, NULL);
|
||||
sf_signal_dispatch_abort(s);
|
||||
}
|
||||
|
||||
static void sf_signal_abort_default(SfSignal *s, bool timeout) {
|
||||
@@ -879,6 +479,14 @@ static SfSignal *sf_signal_of(const ScrDyn *d, const char *where) {
|
||||
return (SfSignal *)d->v.handle.ptr;
|
||||
}
|
||||
|
||||
/* options.signal for the EventTarget engine. */
|
||||
static ScrEventTargetCore *sf_signal_core_of(const ScrDyn *d, bool *aborted) {
|
||||
if (!d || d->kind != SCR_DYN_HANDLE || d->v.handle.tag != SCR_DYNH_ABORT_SIGNAL) return NULL;
|
||||
SfSignal *s = (SfSignal *)d->v.handle.ptr;
|
||||
*aborted = s->aborted;
|
||||
return &s->core;
|
||||
}
|
||||
|
||||
static void sf_signal_timer_fire(ScrClosure *cb) {
|
||||
SfSignal *s = (SfSignal *)scr_box_get_ref(cb->caps[0]);
|
||||
if (!s) return;
|
||||
@@ -1031,63 +639,6 @@ static ScrDyn *sf_abort_controller_get(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool sf_signal_listener_options(
|
||||
ScrDyn *const *args, size_t argc, bool *capture, bool *once,
|
||||
SfSignal **option_signal) {
|
||||
*capture = false;
|
||||
*once = false;
|
||||
if (option_signal) *option_signal = NULL;
|
||||
if (argc < 3) return true;
|
||||
if (args[2]->kind == SCR_DYN_BOOL) {
|
||||
/* EventTarget's boolean overload is `useCapture`, never `once`. */
|
||||
*capture = args[2]->v.b;
|
||||
return true;
|
||||
}
|
||||
if (args[2]->kind != SCR_DYN_OBJ) return true;
|
||||
const ScrDyn *capture_value =
|
||||
scr_dyn_obj_get(args[2], "capture", 7);
|
||||
const ScrDyn *once_value = scr_dyn_obj_get(args[2], "once", 4);
|
||||
*capture = capture_value && scr_dyn_truthy(capture_value);
|
||||
*once = once_value && scr_dyn_truthy(once_value);
|
||||
if (option_signal) {
|
||||
const ScrDyn *signal_value =
|
||||
scr_dyn_obj_get(args[2], "signal", 6);
|
||||
if (signal_value && signal_value->kind != SCR_DYN_UNDEF) {
|
||||
*option_signal = sf_signal_of(
|
||||
signal_value,
|
||||
"AddEventListenerOptions.signal must be an AbortSignal");
|
||||
if (!*option_signal) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sf_abort_listener_value(const ScrDyn *listener) {
|
||||
/* Web IDL callback interfaces accept any object. handleEvent is looked up
|
||||
* when the event is dispatched, where a non-callable value reports an
|
||||
* asynchronous listener error and a missing value is a no-op. */
|
||||
switch (listener->kind) {
|
||||
case SCR_DYN_FUNC:
|
||||
case SCR_DYN_OBJ:
|
||||
case SCR_DYN_ARR:
|
||||
case SCR_DYN_BYTES:
|
||||
case SCR_DYN_HANDLE:
|
||||
case SCR_DYN_PROMISE:
|
||||
return true;
|
||||
|
||||
case SCR_DYN_TYPED_REF:
|
||||
return scr_dyn_ref_typeof_is(listener, "function") ||
|
||||
scr_dyn_ref_typeof_is(listener, "object");
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool sf_abort_listener_equal(
|
||||
const ScrDyn *a, const ScrDyn *b) {
|
||||
return scr_dyn_strict_eq(a, b) || scr_dyn_obj_same_source(a, b);
|
||||
}
|
||||
|
||||
static ScrDyn *sf_signal_invoke(void *ptr, ScrDyn *self, const char *method,
|
||||
ScrDyn *const *args, size_t argc,
|
||||
const char *what) {
|
||||
@@ -1101,113 +652,11 @@ static ScrDyn *sf_signal_invoke(void *ptr, ScrDyn *self, const char *method,
|
||||
}
|
||||
return scr_dyn_retain(scr_dyn_undefined());
|
||||
}
|
||||
if (strcmp(method, "addEventListener") == 0) {
|
||||
if (argc < 2) {
|
||||
sf_type_error("AbortSignal.addEventListener requires an event name");
|
||||
return NULL;
|
||||
}
|
||||
ScrStr *event_name = scr_dyn_string_coerce_js(args[0]);
|
||||
if (!event_name) return NULL;
|
||||
bool is_abort_event =
|
||||
sf_name(event_name->data, event_name->len, "abort");
|
||||
scr_str_release(event_name);
|
||||
/* Web IDL's nullable EventListener argument: null is a no-op. */
|
||||
if (args[1]->kind == SCR_DYN_NULL ||
|
||||
args[1]->kind == SCR_DYN_UNDEF) {
|
||||
return scr_dyn_retain(scr_dyn_undefined());
|
||||
}
|
||||
if (!sf_abort_listener_value(args[1])) {
|
||||
sf_type_error(
|
||||
"AbortSignal.addEventListener listener must be a function, "
|
||||
"an object with handleEvent, or null");
|
||||
return NULL;
|
||||
}
|
||||
bool capture = false;
|
||||
bool once = false;
|
||||
SfSignal *option_signal = NULL;
|
||||
if (!sf_signal_listener_options(
|
||||
args, argc, &capture, &once, &option_signal)) {
|
||||
return NULL;
|
||||
}
|
||||
/* AbortSignal is an EventTarget: listeners for other event names are
|
||||
* valid registrations even though this target only dispatches abort. */
|
||||
if (!is_abort_event) {
|
||||
return scr_dyn_retain(scr_dyn_undefined());
|
||||
}
|
||||
if (option_signal && option_signal->aborted) {
|
||||
return scr_dyn_retain(scr_dyn_undefined());
|
||||
}
|
||||
SfAbortListener **tail = &s->listeners;
|
||||
while (*tail) {
|
||||
if ((*tail)->capture == capture &&
|
||||
sf_abort_listener_equal((*tail)->listener, args[1])) {
|
||||
return scr_dyn_retain(scr_dyn_undefined());
|
||||
}
|
||||
tail = &(*tail)->next;
|
||||
}
|
||||
SfAbortListener *l = calloc(1, sizeof *l);
|
||||
if (!l) sf_oom();
|
||||
l->listener = scr_dyn_retain(args[1]);
|
||||
l->capture = capture;
|
||||
l->once = once;
|
||||
l->id = ++s->next_listener_id;
|
||||
l->target = s;
|
||||
l->option_signal =
|
||||
option_signal && option_signal != s
|
||||
? sf_signal_retain(option_signal)
|
||||
: option_signal;
|
||||
*tail = l;
|
||||
if (option_signal) {
|
||||
l->signal_watch =
|
||||
sf_signal_watch(option_signal, l, &sf_listener_signal_abort);
|
||||
}
|
||||
sf_signal_track_callbacks(s);
|
||||
return scr_dyn_retain(scr_dyn_undefined());
|
||||
}
|
||||
if (strcmp(method, "removeEventListener") == 0) {
|
||||
if (argc < 2) {
|
||||
sf_type_error(
|
||||
"AbortSignal.removeEventListener requires an event name and listener");
|
||||
return NULL;
|
||||
}
|
||||
ScrStr *event_name = scr_dyn_string_coerce_js(args[0]);
|
||||
if (!event_name) return NULL;
|
||||
bool is_abort_event =
|
||||
sf_name(event_name->data, event_name->len, "abort");
|
||||
scr_str_release(event_name);
|
||||
if (is_abort_event) {
|
||||
bool capture = false;
|
||||
bool once = false;
|
||||
(void)sf_signal_listener_options(
|
||||
args, argc, &capture, &once, NULL);
|
||||
(void)once;
|
||||
for (SfAbortListener **at = &s->listeners; *at; at = &(*at)->next) {
|
||||
if ((*at)->capture == capture &&
|
||||
sf_abort_listener_equal((*at)->listener, args[1])) {
|
||||
SfAbortListener *l = *at;
|
||||
*at = l->next;
|
||||
sf_abort_listener_free(l);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return scr_dyn_retain(scr_dyn_undefined());
|
||||
}
|
||||
if (strcmp(method, "dispatchEvent") == 0) {
|
||||
if (argc != 1 || args[0]->kind != SCR_DYN_HANDLE ||
|
||||
args[0]->v.handle.tag != SCR_DYNH_EVENT) {
|
||||
sf_type_error("AbortSignal.dispatchEvent requires an Event");
|
||||
return NULL;
|
||||
}
|
||||
SfEvent *event = (SfEvent *)args[0]->v.handle.ptr;
|
||||
if (event->dispatching) {
|
||||
sf_type_error("The event is already being dispatched");
|
||||
return NULL;
|
||||
}
|
||||
sf_signal_dispatch_listeners(s, args[0]);
|
||||
if (scr_exc_pending()) return NULL;
|
||||
return scr_dyn_new_bool(true);
|
||||
}
|
||||
if (strcmp(method, "addEventListener") == 0) return scr_et_add_listener(&s->core, args, argc);
|
||||
if (strcmp(method, "removeEventListener") == 0)
|
||||
return scr_et_remove_listener(&s->core, args, argc);
|
||||
if (strcmp(method, "dispatchEvent") == 0)
|
||||
return scr_et_dispatch_event(&s->core, self, args, argc);
|
||||
(void)self;
|
||||
sf_not_function(what);
|
||||
return NULL;
|
||||
@@ -1220,9 +669,7 @@ static ScrDyn *sf_signal_get(void *ptr, const char *key, size_t len) {
|
||||
return s->aborted ? sf_signal_reason_ref(s)
|
||||
: scr_dyn_retain(scr_dyn_undefined());
|
||||
}
|
||||
if (sf_name(key, len, "onabort")) {
|
||||
return s->onabort ? scr_dyn_retain(s->onabort) : scr_dyn_new_null();
|
||||
}
|
||||
if (sf_name(key, len, "onabort")) return scr_et_handler_get(&s->core, "abort");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -1230,19 +677,7 @@ static bool sf_signal_set(void *ptr, const char *key, size_t len,
|
||||
const ScrDyn *value) {
|
||||
SfSignal *s = ptr;
|
||||
if (!sf_name(key, len, "onabort")) return false;
|
||||
bool had_handler = s->onabort && s->onabort->kind == SCR_DYN_FUNC;
|
||||
scr_dyn_release(s->onabort);
|
||||
if (value->kind != SCR_DYN_NULL && value->kind != SCR_DYN_UNDEF) {
|
||||
s->onabort = scr_dyn_retain((ScrDyn *)value);
|
||||
} else {
|
||||
s->onabort = NULL;
|
||||
}
|
||||
if (value->kind == SCR_DYN_FUNC) {
|
||||
if (!had_handler) s->onabort_order = ++s->next_listener_id;
|
||||
sf_signal_track_callbacks(s);
|
||||
} else {
|
||||
s->onabort_order = 0;
|
||||
}
|
||||
scr_et_handler_set(&s->core, "abort", value);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -5627,9 +5062,6 @@ static const ScrDynHandleOps sf_response_ops = {
|
||||
static const ScrDynHandleOps sf_headers_ops = {
|
||||
"Headers", &sf_headers_retain_v, &sf_headers_release_v,
|
||||
&sf_headers_invoke, &sf_headers_get, &sf_no_set, NULL};
|
||||
static const ScrDynHandleOps sf_event_ops = {
|
||||
"Event", &sf_event_retain_v, &sf_event_release_v,
|
||||
&sf_event_invoke, &sf_event_get, &sf_event_set, NULL};
|
||||
|
||||
static void sf_teardown(void) {
|
||||
while (sf_live) {
|
||||
@@ -5647,9 +5079,6 @@ static void sf_teardown(void) {
|
||||
while (sf_callback_streams) {
|
||||
sf_stream_drop_source_callbacks(sf_callback_streams, true);
|
||||
}
|
||||
while (sf_callback_signals) {
|
||||
sf_signal_drop_callbacks(sf_callback_signals);
|
||||
}
|
||||
while (sf_reason_signals) {
|
||||
sf_signal_drop_reason(sf_reason_signals);
|
||||
}
|
||||
@@ -5673,7 +5102,7 @@ void scr_fetch_install(void) {
|
||||
scr_dyn_handle_install(SCR_DYNH_FETCH_REQUEST, &sf_request_ops);
|
||||
scr_dyn_handle_install(SCR_DYNH_FETCH_RESPONSE, &sf_response_ops);
|
||||
scr_dyn_handle_install(SCR_DYNH_FETCH_HEADERS, &sf_headers_ops);
|
||||
scr_dyn_handle_install(SCR_DYNH_EVENT, &sf_event_ops);
|
||||
scr_et_signal_core_fn = sf_signal_core_of;
|
||||
scr_atexit(sf_teardown);
|
||||
}
|
||||
|
||||
@@ -5689,11 +5118,7 @@ static const ScrDynHandleOps sf_signal_ops = {
|
||||
static const ScrDynHandleOps sf_abort_controller_ops = {
|
||||
"AbortController", &sf_signal_retain_v, &sf_signal_release_v,
|
||||
&sf_abort_controller_invoke, &sf_abort_controller_get, &sf_no_set, NULL };
|
||||
static const ScrDynHandleOps sf_event_ops = {
|
||||
"Event", &sf_event_retain_v, &sf_event_release_v,
|
||||
&sf_event_invoke, &sf_event_get, &sf_event_set, NULL };
|
||||
static void sf_signal_teardown(void) {
|
||||
while (sf_callback_signals) sf_signal_drop_callbacks(sf_callback_signals);
|
||||
while (sf_reason_signals) sf_signal_drop_reason(sf_reason_signals);
|
||||
}
|
||||
static void sf_signal_install(void) {
|
||||
@@ -5702,7 +5127,7 @@ static void sf_signal_install(void) {
|
||||
installed = true;
|
||||
scr_dyn_handle_install(SCR_DYNH_ABORT_SIGNAL, &sf_signal_ops);
|
||||
scr_dyn_handle_install(SCR_DYNH_ABORT_CONTROLLER, &sf_abort_controller_ops);
|
||||
scr_dyn_handle_install(SCR_DYNH_EVENT, &sf_event_ops);
|
||||
scr_et_signal_core_fn = sf_signal_core_of;
|
||||
scr_atexit(sf_signal_teardown);
|
||||
}
|
||||
|
||||
|
||||
@@ -967,6 +967,21 @@ ScrStr *scr_insp_dyn(ScrDyn *d, double recurse, double depth) {
|
||||
d->v.handle.tag == SCR_DYNH_SHARED_ARRAY_BUFFER) {
|
||||
return scr_insp_array_buffer(d->v.handle.ptr, scr_dyn_handle_cls(d), recurse, depth);
|
||||
}
|
||||
const ScrDynHandleOps *ops = scr_dyn_handle_ops_of(d);
|
||||
if (ops->inspect_view) {
|
||||
/* Node's custom inspectors answer the bare class name beyond the
|
||||
* depth limit (`depth < 0`). */
|
||||
if (recurse > depth) return scr_str_new(ops->cls, strlen(ops->cls));
|
||||
ScrDyn *view = ops->inspect_view(d->v.handle.ptr);
|
||||
ScrStr *body = scr_insp_dyn(view, recurse, depth);
|
||||
scr_dyn_release(view);
|
||||
InspBuf out = {0};
|
||||
ib_cstr(&out, ops->cls);
|
||||
ib_char(&out, ' ');
|
||||
ib_bytes(&out, body->data, body->len);
|
||||
scr_str_release(body);
|
||||
return ib_take(&out);
|
||||
}
|
||||
/* Node prints the class's full property dump (ServerResponse {
|
||||
* ... }) — internals we do not model. Fence loudly instead of a
|
||||
* silent-wrong shape (SEMANTICS.md). */
|
||||
|
||||
@@ -4556,7 +4556,8 @@ ScrStr *scr_dyn_object_tag(const ScrDyn *d) {
|
||||
case SCR_DYN_OBJ: tag = "[object Object]"; break;
|
||||
case SCR_DYN_HANDLE:
|
||||
if ((d->v.handle.tag >= SCR_DYNH_ABORT_SIGNAL &&
|
||||
d->v.handle.tag <= SCR_DYNH_ABORT_CONTROLLER) || d->v.handle.tag == SCR_DYNH_ARRAY_BUFFER || d->v.handle.tag == SCR_DYNH_SHARED_ARRAY_BUFFER ||
|
||||
d->v.handle.tag <= SCR_DYNH_ABORT_CONTROLLER) || d->v.handle.tag == SCR_DYNH_EVENT_TARGET ||
|
||||
d->v.handle.tag == SCR_DYNH_CUSTOM_EVENT || d->v.handle.tag == SCR_DYNH_ARRAY_BUFFER || d->v.handle.tag == SCR_DYNH_SHARED_ARRAY_BUFFER ||
|
||||
d->v.handle.tag == SCR_DYNH_WEAK_MAP || d->v.handle.tag == SCR_DYNH_WEAK_SET ||
|
||||
d->v.handle.tag == SCR_DYNH_SET || d->v.handle.tag == SCR_DYNH_MAP || d->v.handle.tag == SCR_DYNH_REGEXP ||
|
||||
d->v.handle.tag == SCR_DYNH_DATE) {
|
||||
@@ -5093,7 +5094,8 @@ ScrStr *scr_dyn_to_string(const ScrDyn *d, const ScrStr *enc) {
|
||||
return out;
|
||||
}
|
||||
if ((d->v.handle.tag >= SCR_DYNH_ABORT_SIGNAL &&
|
||||
d->v.handle.tag <= SCR_DYNH_ABORT_CONTROLLER) || d->v.handle.tag == SCR_DYNH_ARRAY_BUFFER || d->v.handle.tag == SCR_DYNH_SHARED_ARRAY_BUFFER || d->v.handle.tag == SCR_DYNH_SET || d->v.handle.tag == SCR_DYNH_MAP) {
|
||||
d->v.handle.tag <= SCR_DYNH_ABORT_CONTROLLER) || d->v.handle.tag == SCR_DYNH_EVENT_TARGET ||
|
||||
d->v.handle.tag == SCR_DYNH_CUSTOM_EVENT || d->v.handle.tag == SCR_DYNH_ARRAY_BUFFER || d->v.handle.tag == SCR_DYNH_SHARED_ARRAY_BUFFER || d->v.handle.tag == SCR_DYNH_SET || d->v.handle.tag == SCR_DYNH_MAP) {
|
||||
ScrJsonBuf b;
|
||||
scr_jb_init(&b);
|
||||
scr_jb_puts(&b, "[object ");
|
||||
|
||||
@@ -1892,6 +1892,7 @@ void scr_os_ifaddrs_free(ScrIfaddrs *s) {
|
||||
/* The events-unit hooks (scr_events.c fills them at install; NULL in
|
||||
* event-free binaries and in the standalone runtime C tests). */
|
||||
SCR_TL void (*scr_process_exit_hook)(double code) = NULL;
|
||||
SCR_TL void (*scr_warning_emit_hook)(const char *name, const char *code, ScrStr *message) = NULL;
|
||||
SCR_TL void (*scr_stdin_destroy_hook)(void) = NULL;
|
||||
|
||||
void scr_process_set_exit_code(double code) {
|
||||
|
||||
@@ -4633,7 +4633,7 @@ typedef enum {
|
||||
SCR_DYNH_WEB_CONTROLLER, /* native ReadableStreamDefaultController */
|
||||
SCR_DYNH_FETCH_RESPONSE, /* native static-fetch Response */
|
||||
SCR_DYNH_FETCH_HEADERS, /* native static-fetch response Headers */
|
||||
SCR_DYNH_EVENT, /* native static-fetch abort Event */
|
||||
SCR_DYNH_EVENT, /* ScrEvent — new Event() and AbortSignal's abort events */
|
||||
SCR_DYNH_ABORT_CONTROLLER, /* native static-fetch AbortController */
|
||||
SCR_DYNH_CHILD, /* child_process.ChildProcess */
|
||||
SCR_DYNH_FILE_HANDLE, /* fs/promises.FileHandle */
|
||||
@@ -4658,6 +4658,8 @@ typedef enum {
|
||||
SCR_DYNH_MESSAGE_PORT,
|
||||
SCR_DYNH_SHARED_ARRAY_BUFFER,
|
||||
SCR_DYNH_CONSOLE_INSTANCE, /* node:console Console instance */
|
||||
SCR_DYNH_EVENT_TARGET, /* ScrEventTarget — new EventTarget() */
|
||||
SCR_DYNH_CUSTOM_EVENT, /* ScrEvent with detail — new CustomEvent() */
|
||||
SCR_DYNH_COUNT,
|
||||
} ScrDynHandleTag;
|
||||
|
||||
@@ -5406,9 +5408,81 @@ typedef struct ScrDynHandleOps {
|
||||
ScrDyn *(*iter_pack)(void *h);
|
||||
/* A live native iterator step; index advances only when a value is emitted. */
|
||||
ScrDyn *(*iter_step)(void *h, double *index, unsigned selection);
|
||||
/* util.inspect renders `<cls> <view>` for the dyn object this answers
|
||||
* (+1); NULL = the class has no inspection (the loud fence). */
|
||||
ScrDyn *(*inspect_view)(void *h);
|
||||
} ScrDynHandleOps;
|
||||
ScrDyn *scr_dyn_native_handle_iterator(ScrDyn *source, unsigned selection);
|
||||
|
||||
/* ── EventTarget / Event / CustomEvent (scr_event_target.c) ───────────
|
||||
* Node's internal/event_target over checked-dynamic values. A target's
|
||||
* listeners live in a ScrEventTargetCore: per event type, a linked list
|
||||
* in registration order whose nodes keep their `next` link after removal,
|
||||
* so a dispatch that cached a removed node continues where Node's does.
|
||||
* Plain `new EventTarget()` values are SCR_DYNH_EVENT_TARGET handles;
|
||||
* AbortSignal embeds a core of its own (MaxListeners 0, the `onabort`
|
||||
* event-handler attribute). Events are ScrEvent handles (SCR_DYNH_EVENT,
|
||||
* SCR_DYNH_CUSTOM_EVENT for CustomEvent). Listener exceptions, and
|
||||
* rejections of a listener's returned promise or of a handleEvent call,
|
||||
* are reported as uncaught exceptions from a nextTick, like Node. */
|
||||
typedef struct ScrEtRoot ScrEtRoot;
|
||||
typedef struct ScrEtHandler ScrEtHandler;
|
||||
typedef struct ScrEtHook ScrEtHook;
|
||||
typedef struct ScrEventTargetCore {
|
||||
ScrEtRoot *roots; /* per event type, in first-add order */
|
||||
ScrEtHandler *handlers; /* event-handler attributes (`onabort`) */
|
||||
ScrEtHook *hooks; /* options.signal removal hooks aimed here (weak) */
|
||||
double max_listeners; /* 0 = no leak warning (AbortSignal) */
|
||||
bool warned;
|
||||
const char *cls; /* the warning's target name ("EventTarget") */
|
||||
bool tracked; /* in the exit teardown registry */
|
||||
struct ScrEventTargetCore *tracked_prev, *tracked_next;
|
||||
} ScrEventTargetCore;
|
||||
|
||||
typedef struct ScrEvent {
|
||||
size_t rc;
|
||||
ScrStr *type;
|
||||
ScrDyn *target; /* +1, NULL until dispatched */
|
||||
ScrDyn *detail; /* CustomEvent's detail (+1); NULL for Event */
|
||||
double time_stamp;
|
||||
bool custom, bubbles, cancelable, composed, trusted;
|
||||
bool default_prevented, propagation_stopped, stop, dispatching, in_passive;
|
||||
bool tracked;
|
||||
struct ScrEvent *tracked_prev, *tracked_next;
|
||||
} ScrEvent;
|
||||
|
||||
void scr_et_core_init(ScrEventTargetCore *core, double max_listeners, const char *cls);
|
||||
/* Drops every listener and handler (teardown and target destruction). */
|
||||
void scr_et_core_drop(ScrEventTargetCore *core);
|
||||
/* The EventTarget methods over a core: `self` is the target's own dyn
|
||||
* value (borrowed — `this` for listeners and the events' target), args
|
||||
* borrowed. +1 result, or NULL with the exception pending. */
|
||||
ScrDyn *scr_et_add_listener(ScrEventTargetCore *core, ScrDyn *const *args, size_t argc);
|
||||
ScrDyn *scr_et_remove_listener(ScrEventTargetCore *core, ScrDyn *const *args, size_t argc);
|
||||
ScrDyn *scr_et_dispatch_event(ScrEventTargetCore *core, ScrDyn *self, ScrDyn *const *args, size_t argc);
|
||||
/* Dispatches an event handle (borrowed) without the argument checks. */
|
||||
void scr_et_dispatch(ScrEventTargetCore *core, ScrDyn *self, ScrDyn *event);
|
||||
/* `on<type>` attributes (Node's defineEventHandler): get answers +1 (null
|
||||
* when unset); set registers the wrapper listener on first assignment. */
|
||||
ScrDyn *scr_et_handler_get(ScrEventTargetCore *core, const char *type);
|
||||
void scr_et_handler_set(ScrEventTargetCore *core, const char *type, const ScrDyn *value);
|
||||
/* A trusted (isTrusted) Event handle of the given type, +1. */
|
||||
ScrDyn *scr_event_new_trusted(const char *type);
|
||||
/* new EventTarget() / new Event(type, options) / new CustomEvent(type,
|
||||
* options): `args` is the argument vector as one dyn array (borrowed).
|
||||
* +1 handle, or NULL with the exception pending. */
|
||||
ScrDyn *scr_event_target_new(void);
|
||||
ScrDyn *scr_event_new(ScrDyn *args);
|
||||
ScrDyn *scr_custom_event_new(ScrDyn *args);
|
||||
/* `v instanceof Event` (kind 0), `CustomEvent` (1), `EventTarget` (2). */
|
||||
bool scr_event_is(const ScrDyn *v, double kind);
|
||||
/* The AbortSignal unit's view of its handles for options.signal: the
|
||||
* signal's core and aborted flag, or NULL for a non-signal value.
|
||||
* Installed by the AbortSignal unit; NULL when no signal can exist. */
|
||||
extern SCR_TL ScrEventTargetCore *(*scr_et_signal_core_fn)(const ScrDyn *d, bool *aborted);
|
||||
/* events.addAbortListener(signal, listener): +1 disposable, or NULL. */
|
||||
ScrDyn *scr_events_add_abort_listener(ScrDyn *signal, ScrDyn *listener);
|
||||
|
||||
ScrDyn *scr_intl_segmenter_new(void);
|
||||
ScrStr *scr_process_builtin_id(ScrDyn *id, ScrArr *known);
|
||||
ScrDyn *scr_process_builtin_module(ScrStr *id, ScrDyn *getter);
|
||||
@@ -5973,6 +6047,10 @@ void scr_process_on_warning(ScrDyn *fn);
|
||||
void scr_process_off_warning(ScrDyn *fn);
|
||||
void scr_process_emit_warning(ScrDyn *args);
|
||||
void scr_emit_warning(const char *name, const char *code, ScrStr *message);
|
||||
/* scr_emit_warning once a 'warning' listener exists (scr_lib.c; set by the
|
||||
* listener registration): units that cannot link the dyn-async unit route
|
||||
* their warnings through it, and print the default report otherwise. */
|
||||
extern SCR_TL void (*scr_warning_emit_hook)(const char *name, const char *code, ScrStr *message);
|
||||
/* The promise-or-absent await's unit arm: one microtask hop, like JS's
|
||||
* await of any non-thenable (and like awaiting a settled promise). */
|
||||
void scr_await_hop(void);
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
// EventTarget dispatch follows Node's linked-list walk: listeners run in
|
||||
// registration order with the target as `this`, a listener appended while
|
||||
// a later one is still pending runs in the same dispatch, a removed
|
||||
// listener is skipped, `once` listeners leave before they run, and the
|
||||
// (listener, capture) pair is the identity. After the first callback the
|
||||
// event no longer reports itself as dispatching (eventPhase 0, null
|
||||
// currentTarget), exactly Node's kIsBeingDispatched reset.
|
||||
const t = new EventTarget();
|
||||
|
||||
function section(name) {
|
||||
console.log("--", name);
|
||||
}
|
||||
|
||||
section("order and this");
|
||||
function first(e) {
|
||||
console.log("first", e.type, this === t, e.eventPhase, e.currentTarget === t, e.target === t);
|
||||
t.addEventListener("x", () => console.log("appended during dispatch"));
|
||||
t.removeEventListener("x", third);
|
||||
}
|
||||
function second(e) {
|
||||
console.log("second", e.eventPhase, e.currentTarget === null, e.composedPath().length);
|
||||
}
|
||||
function third() {
|
||||
console.log("third (removed before its turn)");
|
||||
}
|
||||
t.addEventListener("x", first);
|
||||
t.addEventListener("x", second);
|
||||
t.addEventListener("x", third);
|
||||
t.addEventListener("x", first); // duplicate (listener, capture) is ignored
|
||||
const ev = new Event("x");
|
||||
console.log("result", t.dispatchEvent(ev));
|
||||
console.log("after", ev.eventPhase, ev.currentTarget, ev.target === t, ev.srcElement === t);
|
||||
console.log("again", t.dispatchEvent(ev));
|
||||
|
||||
section("tail append");
|
||||
const tail = new EventTarget();
|
||||
tail.addEventListener("y", () => {
|
||||
console.log("only listener appends");
|
||||
tail.addEventListener("y", () => console.log("appended after the last node"));
|
||||
});
|
||||
tail.dispatchEvent(new Event("y"));
|
||||
tail.dispatchEvent(new Event("y"));
|
||||
|
||||
section("once and capture");
|
||||
const o = new EventTarget();
|
||||
const shared = () => console.log("shared");
|
||||
o.addEventListener("o", () => console.log("once"), { once: true });
|
||||
o.addEventListener("o", shared, true);
|
||||
o.addEventListener("o", shared, { capture: false });
|
||||
o.dispatchEvent(new Event("o"));
|
||||
o.removeEventListener("o", shared, true); // a boolean options value reads no capture
|
||||
o.dispatchEvent(new Event("o"));
|
||||
o.removeEventListener("o", shared, { capture: true });
|
||||
o.dispatchEvent(new Event("o"));
|
||||
|
||||
section("cancelable and passive");
|
||||
const c = new EventTarget();
|
||||
c.addEventListener("p", (e) => {
|
||||
e.preventDefault();
|
||||
console.log("in passive", e.defaultPrevented);
|
||||
}, { passive: true });
|
||||
c.addEventListener("c", (e) => {
|
||||
e.preventDefault();
|
||||
console.log("prevented", e.defaultPrevented, e.returnValue);
|
||||
});
|
||||
c.addEventListener("r", (e) => {
|
||||
e.returnValue = false;
|
||||
});
|
||||
console.log("passive result", c.dispatchEvent(new Event("p", { cancelable: true })));
|
||||
const cancel = new Event("c", { cancelable: true });
|
||||
console.log("cancel result", c.dispatchEvent(cancel), cancel.defaultPrevented);
|
||||
console.log("not cancelable", c.dispatchEvent(new Event("c")));
|
||||
const rv = new Event("r", { cancelable: true });
|
||||
console.log("returnValue result", c.dispatchEvent(rv), rv.defaultPrevented);
|
||||
|
||||
section("stop propagation");
|
||||
const s = new EventTarget();
|
||||
s.addEventListener("s", (e) => {
|
||||
e.stopPropagation();
|
||||
console.log("stopPropagation keeps going", e.cancelBubble);
|
||||
});
|
||||
s.addEventListener("s", (e) => {
|
||||
e.stopImmediatePropagation();
|
||||
console.log("stopImmediatePropagation");
|
||||
});
|
||||
s.addEventListener("s", () => console.log("never"));
|
||||
s.dispatchEvent(new Event("s"));
|
||||
const cb = new Event("cb");
|
||||
cb.cancelBubble = true;
|
||||
console.log("cancelBubble", cb.cancelBubble);
|
||||
|
||||
section("handleEvent");
|
||||
const h = new EventTarget();
|
||||
const handler = {
|
||||
tag: "obj",
|
||||
handleEvent(e) {
|
||||
console.log("handleEvent", this.tag, e.type, this === handler);
|
||||
},
|
||||
};
|
||||
h.addEventListener("h", handler);
|
||||
h.dispatchEvent(new Event("h"));
|
||||
handler.handleEvent = (e) => console.log("replaced handleEvent", e.type);
|
||||
h.dispatchEvent(new Event("h"));
|
||||
h.addEventListener("missing", {});
|
||||
h.dispatchEvent(new Event("missing"));
|
||||
h.removeEventListener("h", handler);
|
||||
h.dispatchEvent(new Event("h"));
|
||||
|
||||
section("validation");
|
||||
// Values the checker would reject at these call sites.
|
||||
function loose(value) {
|
||||
return value;
|
||||
}
|
||||
function attempt(label, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(label, "ok");
|
||||
} catch (e) {
|
||||
console.log(label, e.name, e.code, e.message);
|
||||
}
|
||||
}
|
||||
// @ts-expect-error -- the missing argument is the point
|
||||
attempt("new Event()", () => new Event());
|
||||
attempt("bad options", () => new Event("x", loose(5)));
|
||||
attempt("dispatch non-event", () => t.dispatchEvent(loose({})));
|
||||
// @ts-expect-error -- the missing argument is the point
|
||||
attempt("dispatch nothing", () => t.dispatchEvent());
|
||||
attempt("listener number", () => t.addEventListener("x", loose(5)));
|
||||
// @ts-expect-error -- the missing argument is the point
|
||||
attempt("missing listener", () => t.addEventListener("x"));
|
||||
attempt("options number", () => t.addEventListener("x", () => {}, loose(5)));
|
||||
attempt("signal object", () => t.addEventListener("x", () => {}, { signal: loose(1) }));
|
||||
const rec = new EventTarget();
|
||||
rec.addEventListener("rec", (e) => {
|
||||
attempt("recursion", () => rec.dispatchEvent(e));
|
||||
});
|
||||
rec.dispatchEvent(new Event("rec"));
|
||||
|
||||
section("event fields");
|
||||
const fresh = new Event("f", { bubbles: true, cancelable: true, composed: true });
|
||||
console.log(fresh.type, fresh.bubbles, fresh.cancelable, fresh.composed, fresh.isTrusted);
|
||||
console.log(fresh.target, fresh.currentTarget, fresh.eventPhase, fresh.timeStamp >= 0);
|
||||
console.log(Event.NONE, Event.CAPTURING_PHASE, Event.AT_TARGET, Event.BUBBLING_PHASE);
|
||||
console.log(String(fresh), String(t), Object.prototype.toString.call(new CustomEvent("c")));
|
||||
fresh.initEvent("renamed", false, false);
|
||||
console.log(fresh.type, fresh.bubbles, fresh.cancelable);
|
||||
const custom = new CustomEvent("d", { detail: { n: 1 } });
|
||||
console.log(custom.detail.n, new CustomEvent("e").detail, custom instanceof Event, custom instanceof CustomEvent);
|
||||
console.log(fresh instanceof CustomEvent, t instanceof EventTarget, loose({}) instanceof Event);
|
||||
console.log(new Event(loose(42)).type, new Event(loose(null)).type);
|
||||
// util.inspect: EventTarget prints its (empty) custom view; beyond the
|
||||
// depth limit both print the bare class name.
|
||||
console.log([t], { deep: { deeper: { target: t, event: fresh } } });
|
||||
@@ -0,0 +1,51 @@
|
||||
// A throwing EventTarget listener does not stop the dispatch: Node reports
|
||||
// the exception as uncaught from a nextTick. A listener's returned promise
|
||||
// is caught with then(), so its rejection is reported after the reaction
|
||||
// runs; a handleEvent object goes through an async wrapper, so even its
|
||||
// synchronous throw (or a non-callable handleEvent) arrives one microtask
|
||||
// later. Each phase raises one failure and shows where its report lands
|
||||
// among a microtask and a tick queued right after the dispatch.
|
||||
process.on("uncaughtException", (err) => console.log("uncaught:", err.message));
|
||||
|
||||
async function phase(name, listener) {
|
||||
console.log("--", name);
|
||||
const t = new EventTarget();
|
||||
t.addEventListener("x", listener);
|
||||
t.addEventListener("x", () => console.log("next listener still runs"));
|
||||
console.log("dispatch result", t.dispatchEvent(new Event("x")));
|
||||
queueMicrotask(() => console.log("microtask"));
|
||||
process.nextTick(() => console.log("tick"));
|
||||
await new Promise((resolve) => setTimeout(resolve, 5));
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await phase("throwing function", () => {
|
||||
throw new Error("listener threw");
|
||||
});
|
||||
await phase("async function", async () => {
|
||||
throw new Error("async listener rejected");
|
||||
});
|
||||
await phase("async function after an await", async () => {
|
||||
await null;
|
||||
throw new Error("late rejection");
|
||||
});
|
||||
await phase("throwing handleEvent", {
|
||||
handleEvent() {
|
||||
throw new Error("handleEvent threw");
|
||||
},
|
||||
});
|
||||
await phase("non-callable handleEvent", { handleEvent: 5 });
|
||||
await phase("async handleEvent", {
|
||||
async handleEvent() {
|
||||
await null;
|
||||
throw new Error("async handleEvent rejected");
|
||||
},
|
||||
});
|
||||
await phase("thenable result", () => ({
|
||||
then(resolve, reject) {
|
||||
console.log("then called", typeof resolve, typeof reject);
|
||||
reject(new Error("thenable rejected"));
|
||||
},
|
||||
}));
|
||||
}
|
||||
main();
|
||||
@@ -0,0 +1,79 @@
|
||||
// EventTarget, Event and CustomEvent from typed code: listeners typed
|
||||
// `(event: Event) => void` or `{ handleEvent }` records, the dispatch
|
||||
// result, the event's target and phase during and after dispatch, the
|
||||
// instanceof tests, the phase constants, and CustomEvent's detail.
|
||||
const bus = new EventTarget();
|
||||
const seen: string[] = [];
|
||||
|
||||
function onPing(event: Event): void {
|
||||
seen.push(`${event.type}:${event.eventPhase}:${event.target === bus}:${event.currentTarget === bus}`);
|
||||
}
|
||||
|
||||
bus.addEventListener("ping", onPing);
|
||||
bus.addEventListener("ping", onPing, { once: true });
|
||||
const ping = new Event("ping", { cancelable: true });
|
||||
console.log(bus.dispatchEvent(ping), ping.eventPhase, ping.currentTarget === null, ping.target === bus);
|
||||
console.log(seen.join(" "));
|
||||
|
||||
// Options dictionaries held in typed bindings.
|
||||
const onceOptions: AddEventListenerOptions = { once: true };
|
||||
for (let i = 0; i < 3; i++) {
|
||||
bus.addEventListener("round", () => seen.push(`round listener ${i}`), onceOptions);
|
||||
bus.dispatchEvent(new Event("round"));
|
||||
}
|
||||
const captureOptions = { capture: true };
|
||||
const captured = (): void => {
|
||||
seen.push("captured");
|
||||
};
|
||||
bus.addEventListener("cap", captured, captureOptions);
|
||||
bus.removeEventListener("cap", captured);
|
||||
bus.dispatchEvent(new Event("cap"));
|
||||
bus.removeEventListener("cap", captured, captureOptions);
|
||||
bus.dispatchEvent(new Event("cap"));
|
||||
console.log(seen.splice(0).join(" "));
|
||||
|
||||
const canceller = (event: Event): void => {
|
||||
event.preventDefault();
|
||||
};
|
||||
bus.addEventListener("save", canceller);
|
||||
const save = new Event("save", { cancelable: true });
|
||||
console.log("save", bus.dispatchEvent(save), save.defaultPrevented, save.returnValue);
|
||||
bus.removeEventListener("save", canceller);
|
||||
console.log("save again", bus.dispatchEvent(new Event("save", { cancelable: true })));
|
||||
|
||||
interface Payload {
|
||||
id: number;
|
||||
tags: string[];
|
||||
}
|
||||
const data = new CustomEvent<Payload>("data", { detail: { id: 7, tags: ["a", "b"] } });
|
||||
bus.addEventListener("data", (event: Event) => {
|
||||
if (event instanceof CustomEvent) {
|
||||
const payload = event.detail as Payload;
|
||||
console.log("detail", payload.id, payload.tags.join("+"), event.detail === data.detail);
|
||||
}
|
||||
});
|
||||
bus.dispatchEvent(data);
|
||||
|
||||
const counter = {
|
||||
hits: 0,
|
||||
handleEvent(event: Event): void {
|
||||
this.hits = this.hits + 1;
|
||||
console.log("handleEvent", event.type, this.hits);
|
||||
},
|
||||
};
|
||||
bus.addEventListener("count", counter);
|
||||
bus.dispatchEvent(new Event("count"));
|
||||
bus.dispatchEvent(new Event("count"));
|
||||
console.log("hits", counter.hits);
|
||||
|
||||
console.log(ping instanceof Event, data instanceof CustomEvent, ping instanceof CustomEvent);
|
||||
console.log(bus instanceof EventTarget, Event.NONE, Event.AT_TARGET, Event.BUBBLING_PHASE);
|
||||
console.log(ping.bubbles, ping.composed, ping.isTrusted, typeof ping.timeStamp);
|
||||
|
||||
function relay(source: EventTarget, type: string): Promise<string> {
|
||||
return new Promise((resolve) => {
|
||||
source.addEventListener(type, (event: Event) => resolve(event.type), { once: true });
|
||||
});
|
||||
}
|
||||
relay(bus, "later").then((type) => console.log("relayed", type));
|
||||
setTimeout(() => bus.dispatchEvent(new Event("later")), 1);
|
||||
@@ -0,0 +1,88 @@
|
||||
// AbortSignal is an EventTarget on the same engine: `onabort` takes the
|
||||
// position of its first assignment, abort listeners and the removal hooks
|
||||
// that options.signal registers run in registration order (the hooks run
|
||||
// even after stopImmediatePropagation), any event type can be dispatched
|
||||
// on a signal, and events.addAbortListener registers a once listener or,
|
||||
// for an aborted signal, queues the call.
|
||||
import { addAbortListener } from "node:events";
|
||||
|
||||
const log: string[] = [];
|
||||
|
||||
// onabort is registered where it was first assigned, even as null.
|
||||
const first = new AbortController();
|
||||
first.signal.onabort = null;
|
||||
first.signal.addEventListener("abort", () => log.push("listener"));
|
||||
first.signal.onabort = (event: Event) => log.push(`onabort ${event.type} ${event.isTrusted}`);
|
||||
first.abort();
|
||||
console.log(log.splice(0).join(", "));
|
||||
console.log(first.signal.onabort !== null);
|
||||
|
||||
// options.signal: the removal hook is an abort listener of its own.
|
||||
const controller = new AbortController();
|
||||
const target = new EventTarget();
|
||||
controller.signal.addEventListener("abort", () => {
|
||||
log.push("early abort listener dispatches");
|
||||
target.dispatchEvent(new Event("work"));
|
||||
});
|
||||
target.addEventListener("work", () => log.push("work listener"), { signal: controller.signal });
|
||||
controller.signal.addEventListener("abort", (event: Event) => {
|
||||
log.push("stops the rest");
|
||||
event.stopImmediatePropagation();
|
||||
});
|
||||
controller.signal.addEventListener("abort", () => log.push("never runs"));
|
||||
target.dispatchEvent(new Event("work"));
|
||||
controller.abort();
|
||||
target.dispatchEvent(new Event("work"));
|
||||
console.log(log.splice(0).join(", "));
|
||||
|
||||
// An aborted signal registers nothing.
|
||||
const done = new AbortController();
|
||||
done.abort();
|
||||
target.addEventListener("work", () => log.push("not registered"), { signal: done.signal });
|
||||
target.dispatchEvent(new Event("work"));
|
||||
console.log("after aborted signal:", log.splice(0).join(", ") || "none");
|
||||
|
||||
// Signals dispatch any event type, with their own `this` and target.
|
||||
const custom = new AbortController();
|
||||
custom.signal.addEventListener("custom", (event: Event) => {
|
||||
log.push(`custom ${event.type} ${event.target === custom.signal} ${event.isTrusted}`);
|
||||
});
|
||||
console.log(custom.signal.dispatchEvent(new Event("custom")), log.splice(0).join(", "));
|
||||
custom.signal.addEventListener("abort", (event: Event) => log.push(`manual ${event.isTrusted}`));
|
||||
custom.signal.dispatchEvent(new Event("abort"));
|
||||
console.log(custom.signal.aborted, log.splice(0).join(", "));
|
||||
|
||||
// events.addAbortListener
|
||||
const viaHelper = new AbortController();
|
||||
const disposable = addAbortListener(viaHelper.signal, (event: Event) => {
|
||||
log.push(`helper ${event.type}`);
|
||||
});
|
||||
viaHelper.signal.addEventListener("abort", (event: Event) => event.stopImmediatePropagation());
|
||||
viaHelper.signal.addEventListener("abort", () => log.push("stopped"));
|
||||
viaHelper.abort();
|
||||
console.log(log.splice(0).join(", "));
|
||||
disposable[Symbol.dispose]();
|
||||
|
||||
const disposed = new AbortController();
|
||||
const handle = addAbortListener(disposed.signal, () => log.push("disposed listener"));
|
||||
handle[Symbol.dispose]();
|
||||
disposed.abort();
|
||||
console.log("after dispose:", log.splice(0).join(", ") || "none");
|
||||
|
||||
const already = new AbortController();
|
||||
already.abort();
|
||||
addAbortListener(already.signal, () => console.log("aborted-signal listener runs later"));
|
||||
queueMicrotask(() => console.log("microtask queued after it"));
|
||||
console.log("sync end");
|
||||
|
||||
function attempt(label: string, fn: () => void): void {
|
||||
try {
|
||||
fn();
|
||||
console.log(label, "ok");
|
||||
} catch (e) {
|
||||
const err = e as Error & { code?: string };
|
||||
console.log(label, err.name, err.code, err.message);
|
||||
}
|
||||
}
|
||||
attempt("not a signal", () => addAbortListener(target as unknown as AbortSignal, () => {}));
|
||||
attempt("not a function", () => addAbortListener(already.signal, 5 as unknown as () => void));
|
||||
@@ -0,0 +1,38 @@
|
||||
// events.errorMonitor: listeners registered under the symbol see every
|
||||
// 'error' emit first, with the same payload; they do not count as 'error'
|
||||
// listeners, so an emit with none still throws after the monitors ran.
|
||||
// Emitting the symbol itself reaches only the monitors.
|
||||
import { EventEmitter, errorMonitor } from "node:events";
|
||||
|
||||
class Job extends EventEmitter {
|
||||
readonly name: string;
|
||||
constructor(name: string) {
|
||||
super();
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
const job = new Job("build");
|
||||
job.on(errorMonitor, (err: Error) => console.log("monitor A", err.message));
|
||||
job.once(EventEmitter.errorMonitor, (err: Error) => console.log("monitor once", err.message));
|
||||
job.on("error", (err: Error) => console.log("error listener", err.message));
|
||||
job.on(errorMonitor, (err: Error) => console.log("monitor B", err.message));
|
||||
console.log(job.emit("error", new Error("first")));
|
||||
console.log(job.emit("error", new TypeError("second")));
|
||||
console.log(job.listenerCount(errorMonitor), job.listenerCount("error"));
|
||||
|
||||
const bare = new Job("bare");
|
||||
bare.on(errorMonitor, (err: Error) => console.log("bare monitor", err.message));
|
||||
try {
|
||||
bare.emit("error", new RangeError("unhandled"));
|
||||
} catch (err) {
|
||||
console.log("thrown", (err as Error).name, (err as Error).message);
|
||||
}
|
||||
console.log(bare.emit(errorMonitor, new Error("direct")));
|
||||
bare.removeAllListeners(errorMonitor);
|
||||
console.log(bare.listenerCount(errorMonitor));
|
||||
try {
|
||||
bare.emit("error", new Error("no monitors left"));
|
||||
} catch (err) {
|
||||
console.log("thrown", (err as Error).message);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// @exit: 1
|
||||
// The process warnings EventTarget emits: the one-time leak warning past
|
||||
// ten listeners of one type (none for AbortSignal, whose limit is 0) and
|
||||
// the no-op warning for a null or undefined listener. Both arrive through
|
||||
// process.emitWarning on a later tick; the 'warning' listener prints them
|
||||
// (the stderr report carries the pid, so the @exit directive keeps it out
|
||||
// of the comparison).
|
||||
process.on("warning", (w) => console.log("warning:", w.name, "|", w.message));
|
||||
|
||||
const t = new EventTarget();
|
||||
for (let i = 0; i < 11; i++) t.addEventListener("x", () => {});
|
||||
for (let i = 0; i < 12; i++) t.addEventListener("x", () => {});
|
||||
for (let i = 0; i < 11; i++) t.addEventListener("y", () => {});
|
||||
t.addEventListener("z", null);
|
||||
t.addEventListener("z", undefined);
|
||||
const ac = new AbortController();
|
||||
for (let i = 0; i < 12; i++) ac.signal.addEventListener("abort", () => {});
|
||||
for (let i = 0; i < 12; i++) new EventTarget().addEventListener("w", () => {}, { signal: ac.signal });
|
||||
console.log("sync end");
|
||||
process.nextTick(() => console.log("tick queued after the warnings"));
|
||||
setTimeout(() => {
|
||||
process.exitCode = 1;
|
||||
}, 1);
|
||||
@@ -74,14 +74,10 @@ async function runBinary(
|
||||
env?: NodeJS.ProcessEnv,
|
||||
timeout = 30_000,
|
||||
): Promise<RunResult> {
|
||||
// These fixtures intentionally exercise missing listeners, and some JS
|
||||
// entries rely on Node's module detection. Neither warning is program output.
|
||||
// Some JS entries rely on Node's module detection; that warning is not
|
||||
// program output.
|
||||
if (cmd === "node") {
|
||||
args = [
|
||||
"--disable-warning=MODULE_TYPELESS_PACKAGE_JSON",
|
||||
"--disable-warning=AddEventListenerArgumentTypeWarning",
|
||||
...args,
|
||||
];
|
||||
args = ["--disable-warning=MODULE_TYPELESS_PACKAGE_JSON", ...args];
|
||||
}
|
||||
try {
|
||||
const { stdout, stderr } = await execFileAsync(cmd, args, {
|
||||
@@ -98,13 +94,15 @@ async function runBinary(
|
||||
}
|
||||
}
|
||||
|
||||
// Keep sanitizer infrastructure noise separate from program stderr.
|
||||
// Keep sanitizer infrastructure noise separate from program stderr; the
|
||||
// process warnings carry each process's own pid.
|
||||
function expectCleanStderr(native: RunResult, node: RunResult): void {
|
||||
if (node.exitCode !== 0) return;
|
||||
const pid = (text: string): string => text.replace(/^\(node:\d+\) /gm, "(node:PID) ");
|
||||
const stderr = native.stderr
|
||||
.toString("utf8")
|
||||
.replace(/^==\d+==WARNING: ASan doesn't fully support makecontext\/swapcontext.*\n/gm, "");
|
||||
expect(stderr).toBe(node.stderr.toString("utf8"));
|
||||
expect(pid(stderr)).toBe(pid(node.stderr.toString("utf8")));
|
||||
}
|
||||
|
||||
async function build(entry: string, flavor = ""): Promise<string> {
|
||||
|
||||
@@ -139,6 +139,12 @@ describe.skipIf(!zigOnPath())("wasm32-wasi differential", () => {
|
||||
"js-promise-all-values.js",
|
||||
"js-promise-catch-handler.js",
|
||||
"js-abort-portable.js",
|
||||
"5130-event-target-dispatch.js",
|
||||
"5131-event-target-listener-errors.js",
|
||||
"5132-event-target-typed.ts",
|
||||
"5133-abort-signal-event-target.ts",
|
||||
"5134-events-error-monitor.ts",
|
||||
"5135-event-target-warnings.js",
|
||||
"4031-event-emitter-long-tuples.ts",
|
||||
"2700-wasi-core.ts",
|
||||
"local-union-storage.ts",
|
||||
|
||||
Reference in New Issue
Block a user