From 8e3065171c55483a2eb6019a00e9a94e5af4bcda Mon Sep 17 00:00:00 2001 From: Josh Vlk Date: Sat, 18 Apr 2026 09:15:57 -0400 Subject: [PATCH 1/4] refactor(media): simplify media and device surfaces --- src/NotificationAPI/Notification.res | 16 +++++++-- src/PictureInPicture.res | 5 +++ src/WebAudioAPI/AudioContext.res | 23 +++++++----- src/WebAudioAPI/AudioDestinationNode.res | 4 ++- src/WebAudioAPI/AudioNode.res | 4 +++ src/WebAudioAPI/AudioScheduledSourceNode.res | 4 ++- src/WebAudioAPI/GainNode.res | 7 ++-- src/WebAudioAPI/OscillatorNode.res | 11 ++++-- src/WebMIDI.res | 4 +++ tests/NotificationsAPI/Notification__test.res | 6 ++-- .../AudioDestinationNode__.test.res | 35 +++++++++---------- 11 files changed, 78 insertions(+), 41 deletions(-) create mode 100644 src/PictureInPicture.res create mode 100644 src/WebMIDI.res diff --git a/src/NotificationAPI/Notification.res b/src/NotificationAPI/Notification.res index e484b6af..fa76fd19 100644 --- a/src/NotificationAPI/Notification.res +++ b/src/NotificationAPI/Notification.res @@ -1,12 +1,22 @@ open NotificationTypes +type t = notification = {...notification} + +type notificationDirection = NotificationTypes.notificationDirection +type notificationPermission = NotificationTypes.notificationPermission +type notificationAction = NotificationTypes.notificationAction +type notificationOptions = NotificationTypes.notificationOptions +type getNotificationOptions = NotificationTypes.getNotificationOptions +type notificationPermissionCallback = NotificationTypes.notificationPermissionCallback +type notificationEvent = NotificationTypes.notificationEvent + /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/Notification) */ @new -external make: (~title: string, ~options: notificationOptions=?) => notification = "Notification" +external make: (~title: string, ~options: notificationOptions=?) => t = "Notification" -include EventTarget.Impl({type t = notification}) +include EventTarget.Impl({type t = t}) /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/Notification/requestPermission_static) @@ -20,7 +30,7 @@ external requestPermission: ( [Read more on MDN](https://developer.mozilla.org/docs/Web/API/Notification/close) */ @send -external close: notification => unit = "close" +external close: t => unit = "close" /** [Read more on MDN](https://developer.mozilla.org/en-US/docs/Web/API/Notification/permission_static) diff --git a/src/PictureInPicture.res b/src/PictureInPicture.res new file mode 100644 index 00000000..05883e25 --- /dev/null +++ b/src/PictureInPicture.res @@ -0,0 +1,5 @@ +open PictureInPictureTypes + +type t = pictureInPictureWindow = {...pictureInPictureWindow} + +include EventTarget.Impl({type t = t}) diff --git a/src/WebAudioAPI/AudioContext.res b/src/WebAudioAPI/AudioContext.res index ad918096..55f1034c 100644 --- a/src/WebAudioAPI/AudioContext.res +++ b/src/WebAudioAPI/AudioContext.res @@ -2,55 +2,60 @@ open WebAudioTypes open DOMTypes open MediaCaptureAndStreamsTypes -include BaseAudioContext.Impl({type t = audioContext}) +type t = audioContext = {...audioContext} +type baseAudioContext = WebAudioTypes.baseAudioContext +type audioContextOptions = WebAudioTypes.audioContextOptions +type audioTimestamp = WebAudioTypes.audioTimestamp + +include BaseAudioContext.Impl({type t = t}) /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/AudioContext) */ @new -external make: (~contextOptions: audioContextOptions=?) => audioContext = "AudioContext" +external make: (~contextOptions: audioContextOptions=?) => t = "AudioContext" /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/AudioContext/getOutputTimestamp) */ @send -external getOutputTimestamp: audioContext => audioTimestamp = "getOutputTimestamp" +external getOutputTimestamp: t => audioTimestamp = "getOutputTimestamp" /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/AudioContext/resume) */ @send -external resume: audioContext => promise = "resume" +external resume: t => promise = "resume" /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/AudioContext/suspend) */ @send -external suspend: audioContext => promise = "suspend" +external suspend: t => promise = "suspend" /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/AudioContext/close) */ @send -external close: audioContext => promise = "close" +external close: t => promise = "close" /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/AudioContext/createMediaElementSource) */ @send -external createMediaElementSource: (audioContext, htmlMediaElement) => mediaElementAudioSourceNode = +external createMediaElementSource: (t, htmlMediaElement) => mediaElementAudioSourceNode = "createMediaElementSource" /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/AudioContext/createMediaStreamSource) */ @send -external createMediaStreamSource: (audioContext, mediaStream) => mediaStreamAudioSourceNode = +external createMediaStreamSource: (t, mediaStream) => mediaStreamAudioSourceNode = "createMediaStreamSource" /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/AudioContext/createMediaStreamDestination) */ @send -external createMediaStreamDestination: audioContext => mediaStreamAudioDestinationNode = +external createMediaStreamDestination: t => mediaStreamAudioDestinationNode = "createMediaStreamDestination" diff --git a/src/WebAudioAPI/AudioDestinationNode.res b/src/WebAudioAPI/AudioDestinationNode.res index debcc1aa..661fb7a6 100644 --- a/src/WebAudioAPI/AudioDestinationNode.res +++ b/src/WebAudioAPI/AudioDestinationNode.res @@ -1,3 +1,5 @@ open WebAudioTypes -include AudioNode.Impl({type t = audioDestinationNode}) +type t = audioDestinationNode = {...audioDestinationNode} + +include AudioNode.Impl({type t = t}) diff --git a/src/WebAudioAPI/AudioNode.res b/src/WebAudioAPI/AudioNode.res index b984bce6..76b480fb 100644 --- a/src/WebAudioAPI/AudioNode.res +++ b/src/WebAudioAPI/AudioNode.res @@ -1,5 +1,7 @@ open WebAudioTypes +type t = audioNode = {...audioNode} + module Impl = ( T: { type t @@ -65,3 +67,5 @@ module Impl = ( @send external disconnect7: (T.t, ~destinationParam: audioParam, ~output: int) => unit = "disconnect" } + +include Impl({type t = t}) diff --git a/src/WebAudioAPI/AudioScheduledSourceNode.res b/src/WebAudioAPI/AudioScheduledSourceNode.res index 4b77c787..33bcef66 100644 --- a/src/WebAudioAPI/AudioScheduledSourceNode.res +++ b/src/WebAudioAPI/AudioScheduledSourceNode.res @@ -1,5 +1,7 @@ open WebAudioTypes +type t = audioScheduledSourceNode = {...audioScheduledSourceNode} + module Impl = ( T: { type t @@ -22,4 +24,4 @@ module Impl = ( external stop: (T.t, ~when_: float=?) => unit = "stop" } -include Impl({type t = audioScheduledSourceNode}) +include Impl({type t = t}) diff --git a/src/WebAudioAPI/GainNode.res b/src/WebAudioAPI/GainNode.res index e37e66b4..dd33f282 100644 --- a/src/WebAudioAPI/GainNode.res +++ b/src/WebAudioAPI/GainNode.res @@ -1,9 +1,12 @@ open WebAudioTypes -include AudioNode.Impl({type t = gainNode}) +type t = gainNode = {...gainNode} +type gainOptions = WebAudioTypes.gainOptions + +include AudioNode.Impl({type t = t}) /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/GainNode) */ @new -external make: (~context: baseAudioContext, ~options: gainOptions=?) => gainNode = "GainNode" +external make: (~context: baseAudioContext, ~options: gainOptions=?) => t = "GainNode" diff --git a/src/WebAudioAPI/OscillatorNode.res b/src/WebAudioAPI/OscillatorNode.res index d75e9836..72267478 100644 --- a/src/WebAudioAPI/OscillatorNode.res +++ b/src/WebAudioAPI/OscillatorNode.res @@ -1,16 +1,21 @@ open WebAudioTypes -include AudioScheduledSourceNode.Impl({type t = oscillatorNode}) +type t = oscillatorNode = {...oscillatorNode} +type oscillatorOptions = WebAudioTypes.oscillatorOptions +type oscillatorType = WebAudioTypes.oscillatorType +type periodicWave = WebAudioTypes.periodicWave + +include AudioScheduledSourceNode.Impl({type t = t}) /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/OscillatorNode) */ @new -external make: (~context: baseAudioContext, ~options: oscillatorOptions=?) => oscillatorNode = +external make: (~context: baseAudioContext, ~options: oscillatorOptions=?) => t = "OscillatorNode" /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/OscillatorNode/setPeriodicWave) */ @send -external setPeriodicWave: (oscillatorNode, periodicWave) => unit = "setPeriodicWave" +external setPeriodicWave: (t, periodicWave) => unit = "setPeriodicWave" diff --git a/src/WebMIDI.res b/src/WebMIDI.res new file mode 100644 index 00000000..252edff9 --- /dev/null +++ b/src/WebMIDI.res @@ -0,0 +1,4 @@ +open WebMIDITypes + +type t = midiAccess = {...midiAccess} +type midiOptions = WebMIDITypes.midiOptions diff --git a/tests/NotificationsAPI/Notification__test.res b/tests/NotificationsAPI/Notification__test.res index e6f13dca..6a4fd67f 100644 --- a/tests/NotificationsAPI/Notification__test.res +++ b/tests/NotificationsAPI/Notification__test.res @@ -1,9 +1,9 @@ -open WebAPI.NotificationTypes +let current: Notification.notificationPermission = Notification.permission -let current = Notification.permission +let _notification: Notification.t = Notification.make(~title="Testing notifications") Notification.requestPermission() -->Promise.thenResolve(notificationPermission => { +->Promise.thenResolve((notificationPermission: Notification.notificationPermission) => { switch notificationPermission { | Granted => Console.log("Permission granted") | Denied => Console.log("Permission denied") diff --git a/tests/WebAudioAPI/AudioDestinationNode__.test.res b/tests/WebAudioAPI/AudioDestinationNode__.test.res index 11f7b5ea..5ea4bb9d 100644 --- a/tests/WebAudioAPI/AudioDestinationNode__.test.res +++ b/tests/WebAudioAPI/AudioDestinationNode__.test.res @@ -1,27 +1,24 @@ -open WebAudioTypes +let ctx: AudioContext.t = AudioContext.make() -let ctx = AudioContext.make() +let destinationNode: AudioNode.t = ctx.destination->AudioDestinationNode.asAudioNode +let context: AudioContext.baseAudioContext = AudioContext.asBaseAudioContext(ctx) -let destinationNode = ctx.destination->AudioDestinationNode.asAudioNode -let context = AudioContext.asBaseAudioContext(ctx) +let oscillatorOptions: OscillatorNode.oscillatorOptions = { + type_: Sine, + frequency: 440.0, +} +let osc: OscillatorNode.t = OscillatorNode.make(~context, ~options=oscillatorOptions) + +let gainOptions: GainNode.gainOptions = { + gain: 0.3, +} +let gain: GainNode.t = GainNode.make(~context, ~options=gainOptions) -let osc = OscillatorNode.make( - ~context, - ~options={ - type_: Sine, - frequency: 440.0, - }, -) -let gain = GainNode.make( - ~context, - ~options={ - gain: 0.3, - }, -) let _ = gain->GainNode.connect(~destinationNode) +let _scheduledSource: AudioScheduledSourceNode.t = + osc->OscillatorNode.asAudioScheduledSourceNode let _ = - osc - ->OscillatorNode.asAudioScheduledSourceNode + _scheduledSource ->AudioScheduledSourceNode.connect(~destinationNode=gain->GainNode.asAudioNode) osc->OscillatorNode.start From b8fa92f556c304d97e57759f6105c4ad9a20f2ac Mon Sep 17 00:00:00 2001 From: Josh Vlk Date: Sat, 18 Apr 2026 10:08:01 -0400 Subject: [PATCH 2/4] formatting --- src/WebAudioAPI/OscillatorNode.res | 3 +-- tests/WebAudioAPI/AudioDestinationNode__.test.res | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/WebAudioAPI/OscillatorNode.res b/src/WebAudioAPI/OscillatorNode.res index 72267478..183eb18d 100644 --- a/src/WebAudioAPI/OscillatorNode.res +++ b/src/WebAudioAPI/OscillatorNode.res @@ -11,8 +11,7 @@ include AudioScheduledSourceNode.Impl({type t = t}) [Read more on MDN](https://developer.mozilla.org/docs/Web/API/OscillatorNode) */ @new -external make: (~context: baseAudioContext, ~options: oscillatorOptions=?) => t = - "OscillatorNode" +external make: (~context: baseAudioContext, ~options: oscillatorOptions=?) => t = "OscillatorNode" /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/OscillatorNode/setPeriodicWave) diff --git a/tests/WebAudioAPI/AudioDestinationNode__.test.res b/tests/WebAudioAPI/AudioDestinationNode__.test.res index 5ea4bb9d..6cad0e88 100644 --- a/tests/WebAudioAPI/AudioDestinationNode__.test.res +++ b/tests/WebAudioAPI/AudioDestinationNode__.test.res @@ -15,10 +15,8 @@ let gainOptions: GainNode.gainOptions = { let gain: GainNode.t = GainNode.make(~context, ~options=gainOptions) let _ = gain->GainNode.connect(~destinationNode) -let _scheduledSource: AudioScheduledSourceNode.t = - osc->OscillatorNode.asAudioScheduledSourceNode +let _scheduledSource: AudioScheduledSourceNode.t = osc->OscillatorNode.asAudioScheduledSourceNode let _ = - _scheduledSource - ->AudioScheduledSourceNode.connect(~destinationNode=gain->GainNode.asAudioNode) + _scheduledSource->AudioScheduledSourceNode.connect(~destinationNode=gain->GainNode.asAudioNode) osc->OscillatorNode.start From 1d8ce1013ee19129142d228ee1577a9992637e93 Mon Sep 17 00:00:00 2001 From: Josh Vlk Date: Sat, 18 Apr 2026 10:14:24 -0400 Subject: [PATCH 3/4] refactor(media): spread exported root type aliases --- src/NotificationAPI/Notification.res | 16 ++++++++++++---- src/WebAudioAPI/AudioContext.res | 6 +++--- src/WebAudioAPI/GainNode.res | 2 +- src/WebAudioAPI/OscillatorNode.res | 4 ++-- src/WebMIDI.res | 2 +- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/NotificationAPI/Notification.res b/src/NotificationAPI/Notification.res index fa76fd19..40ba12a1 100644 --- a/src/NotificationAPI/Notification.res +++ b/src/NotificationAPI/Notification.res @@ -4,11 +4,19 @@ type t = notification = {...notification} type notificationDirection = NotificationTypes.notificationDirection type notificationPermission = NotificationTypes.notificationPermission -type notificationAction = NotificationTypes.notificationAction -type notificationOptions = NotificationTypes.notificationOptions -type getNotificationOptions = NotificationTypes.getNotificationOptions +type notificationAction = NotificationTypes.notificationAction = { + ...NotificationTypes.notificationAction, +} +type notificationOptions = NotificationTypes.notificationOptions = { + ...NotificationTypes.notificationOptions, +} +type getNotificationOptions = NotificationTypes.getNotificationOptions = { + ...NotificationTypes.getNotificationOptions, +} type notificationPermissionCallback = NotificationTypes.notificationPermissionCallback -type notificationEvent = NotificationTypes.notificationEvent +type notificationEvent = NotificationTypes.notificationEvent = { + ...NotificationTypes.notificationEvent, +} /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/Notification) diff --git a/src/WebAudioAPI/AudioContext.res b/src/WebAudioAPI/AudioContext.res index 55f1034c..491bf75c 100644 --- a/src/WebAudioAPI/AudioContext.res +++ b/src/WebAudioAPI/AudioContext.res @@ -3,9 +3,9 @@ open DOMTypes open MediaCaptureAndStreamsTypes type t = audioContext = {...audioContext} -type baseAudioContext = WebAudioTypes.baseAudioContext -type audioContextOptions = WebAudioTypes.audioContextOptions -type audioTimestamp = WebAudioTypes.audioTimestamp +type baseAudioContext = WebAudioTypes.baseAudioContext = {...WebAudioTypes.baseAudioContext} +type audioContextOptions = WebAudioTypes.audioContextOptions = {...WebAudioTypes.audioContextOptions} +type audioTimestamp = WebAudioTypes.audioTimestamp = {...WebAudioTypes.audioTimestamp} include BaseAudioContext.Impl({type t = t}) diff --git a/src/WebAudioAPI/GainNode.res b/src/WebAudioAPI/GainNode.res index dd33f282..21580bd9 100644 --- a/src/WebAudioAPI/GainNode.res +++ b/src/WebAudioAPI/GainNode.res @@ -1,7 +1,7 @@ open WebAudioTypes type t = gainNode = {...gainNode} -type gainOptions = WebAudioTypes.gainOptions +type gainOptions = WebAudioTypes.gainOptions = {...WebAudioTypes.gainOptions} include AudioNode.Impl({type t = t}) diff --git a/src/WebAudioAPI/OscillatorNode.res b/src/WebAudioAPI/OscillatorNode.res index 183eb18d..a24d6e2d 100644 --- a/src/WebAudioAPI/OscillatorNode.res +++ b/src/WebAudioAPI/OscillatorNode.res @@ -1,9 +1,9 @@ open WebAudioTypes type t = oscillatorNode = {...oscillatorNode} -type oscillatorOptions = WebAudioTypes.oscillatorOptions +type oscillatorOptions = WebAudioTypes.oscillatorOptions = {...WebAudioTypes.oscillatorOptions} type oscillatorType = WebAudioTypes.oscillatorType -type periodicWave = WebAudioTypes.periodicWave +type periodicWave = WebAudioTypes.periodicWave = {...WebAudioTypes.periodicWave} include AudioScheduledSourceNode.Impl({type t = t}) diff --git a/src/WebMIDI.res b/src/WebMIDI.res index 252edff9..3d39845f 100644 --- a/src/WebMIDI.res +++ b/src/WebMIDI.res @@ -1,4 +1,4 @@ open WebMIDITypes type t = midiAccess = {...midiAccess} -type midiOptions = WebMIDITypes.midiOptions +type midiOptions = WebMIDITypes.midiOptions = {...WebMIDITypes.midiOptions} From a780ab40750b2b5acff7782238d75e2eb8d05402 Mon Sep 17 00:00:00 2001 From: Josh Vlk Date: Sat, 18 Apr 2026 11:13:17 -0400 Subject: [PATCH 4/4] style: apply rescript formatter in issue-243 workspace --- src/WebAudioAPI/AudioContext.res | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/WebAudioAPI/AudioContext.res b/src/WebAudioAPI/AudioContext.res index 491bf75c..561de158 100644 --- a/src/WebAudioAPI/AudioContext.res +++ b/src/WebAudioAPI/AudioContext.res @@ -4,7 +4,9 @@ open MediaCaptureAndStreamsTypes type t = audioContext = {...audioContext} type baseAudioContext = WebAudioTypes.baseAudioContext = {...WebAudioTypes.baseAudioContext} -type audioContextOptions = WebAudioTypes.audioContextOptions = {...WebAudioTypes.audioContextOptions} +type audioContextOptions = WebAudioTypes.audioContextOptions = { + ...WebAudioTypes.audioContextOptions, +} type audioTimestamp = WebAudioTypes.audioTimestamp = {...WebAudioTypes.audioTimestamp} include BaseAudioContext.Impl({type t = t})