Skip to content

refactor: clean up public owners and singleton APIs#270

Draft
jderochervlk wants to merge 35 commits into
mainfrom
vlk/base
Draft

refactor: clean up public owners and singleton APIs#270
jderochervlk wants to merge 35 commits into
mainfrom
vlk/base

Conversation

@jderochervlk

@jderochervlk jderochervlk commented May 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR continues the WebAPI package split by moving object ownership toward public leaf modules and making global/singleton APIs callable directly from their leaf modules.

// before: get the host object and pass it to the module function
let before = Dom.crypto->Crypto.randomUUID

// after: call the leaf API directly
let after = Crypto.randomUUID()

The package now models focused feature relationships in rescript.json, removes the residual DOMExtended source bucket, and keeps public modules registered through source public lists. Consumers can depend on narrower feature slices instead of compiling the whole DOM/WebAPI surface.

{
  "dependencies": [
    {
      "name": "@rescript/webapi",
      "features": ["WebAPI.Crypto", "WebAPI.Location"]
    }
  ]
}

Public Owner Cleanup

  • Moved many public object APIs toward local type t ownership and pipeable @send functions.
  • Added scripts/audit-method-style.mjs and tests/unmonorepo/method-style.test.mjs to track remaining type-bucket method receivers.
  • Removed DOMExtended in favor of explicit leaf source directories and feature dependencies.
  • Kept follow-up work in one active plan: docs/superpowers/plans/2026-06-09-public-owner-follow-ups.md.

Event And DOM Shape

  • Keeps DOM, Event, and EventTarget interoperable through shared hidden base owners.
  • Keeps DOM.event opaque and exposes event behavior through Event getters.
  • Keeps the DOM feature thin for React-oriented consumers while moving broader API behavior to leaf features.

Testing

Passed locally during this branch:

  • npm run build
  • npm test
  • npm run format:check
  • node_modules/.bin/rescript build --features WebAPI.DOM --prod
  • node_modules/.bin/rescript build --features WebAPI.Event --prod
  • node_modules/.bin/rescript build --features WebAPI.Fetch --prod
  • node_modules/.bin/rescript build --features WebAPI.File --prod
  • node_modules/.bin/rescript build --features WebAPI.CredentialManagement --prod
  • node_modules/.bin/rescript build --features WebAPI.Notification --prod
  • node_modules/.bin/rescript build --features WebAPI.Push --prod
  • node_modules/.bin/rescript build --features WebAPI.Locks --prod

@jderochervlk jderochervlk marked this pull request as draft May 20, 2026 14:23
@jderochervlk

Copy link
Copy Markdown
Collaborator Author

@codex

@jderochervlk jderochervlk marked this pull request as ready for review May 30, 2026 15:37

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 49420e4362

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/WebCrypto/Crypto.res Outdated
@jderochervlk jderochervlk marked this pull request as draft May 30, 2026 15:38
@jderochervlk jderochervlk changed the title refactor: move DOM file out of base folder refactor: split DOM bindings into focused feature modules May 30, 2026
@jderochervlk jderochervlk marked this pull request as ready for review May 30, 2026 15:38
@jderochervlk

Copy link
Copy Markdown
Collaborator Author

@codex

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c526dd58cc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/WebCrypto/Crypto.res Outdated
@jderochervlk jderochervlk requested review from brnrdog and tsnobip May 31, 2026 13:35

@tsnobip tsnobip left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the rest looks ok to me but I have two main comments on the general structure

Comment thread src/DOM/DOM.res Outdated
Comment thread src/Base/Base.res Outdated
Comment on lines +1 to +2
type element = Base__Element.element
type document = Base__Document.document

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why don't we do:

Suggested change
type element = Base__Element.element
type document = Base__Document.document
module Element = Base__Element
module Document = Base__Document

as we agreed on?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, that's much better.

@jderochervlk jderochervlk requested a review from tsnobip June 2, 2026 00:48

@tsnobip tsnobip left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok I re-reviewed it, and really I don't understand this PR, we had quite a nice structure where DOM was in base package, this way we could make rescript-react and other dependencies depend on DOM.element for example, now it would depend on Base.Element.element and these types are not really usable anymore given they use abstract types that are duplicated.

Comment thread src/DOM/DomTypes.res Outdated
Comment on lines 3 to 79
@@ -13,37 +14,37 @@ type structuredSerializeOptions = ChannelMessagingTypes.structuredSerializeOptio
type htmlElement = DOM.htmlElement
type mediaError = DOM.mediaError
type timeRanges = DOM.timeRanges
type textTrackList = DOM.textTrackList
type textTrackList = TextTrackList.t = private {...TextTrackList.t}
type htmlFormElement = DOM.htmlFormElement
type htmlCollection<'a> = DOM.htmlCollection<'a>
type element = DOM.element
type element = Base.Element.element
type validityState = DOM.validityState
type document = DOM.document
type document = Base.Document.document
type cssStyleSheet = DOM.cssStyleSheet
type nodeList<'a> = DOM.nodeList<'a>
type htmlLabelElement = DOM.htmlLabelElement
type documentFragment = DOM.documentFragment
type node = DOM.node
type cssStyleDeclaration = DOM.cssStyleDeclaration
type domRectReadOnly = DOM.domRectReadOnly
type domRectReadOnly = DOMRectReadOnly.t = private {...DOMRectReadOnly.t}
type shadowRoot = DOM.shadowRoot
type styleSheet = DOM.styleSheet
type mediaQueryList = DOM.mediaQueryList
type domRect = DOM.domRect
type domRect = DOMRect.t = private {...DOMRect.t}
type range = DOM.range
type documentType = DOM.documentType
type cssStyleValue = DOM.cssStyleValue
type cssStyleValue = CSSStyleValue.t = private {...CSSStyleValue.t}
type treeWalker = DOM.treeWalker
type selection = DOM.selection
type abstractRange = DOM.abstractRange
type htmlOptionsCollection = DOM.htmlOptionsCollection
type styleSheetList = DOM.styleSheetList
type elementInternals = DOM.elementInternals
type nodeFilter = DOM.nodeFilter
type fileList = DOM.fileList
type fileList = FileList.t = private {...FileList.t}
type cssRule = DOM.cssRule
type attr = DOM.attr
type domRectList = DOM.domRectList
type domRectList = DOMRectList.t = private {...DOMRectList.t}
type htmlFormControlsCollection = DOM.htmlFormControlsCollection
type domImplementation = DOM.domImplementation
type nodeIterator = DOM.nodeIterator
@@ -74,7 +75,6 @@ type htmlTableRowElement = DOM.htmlTableRowElement
type htmlImageElement = DOM.htmlImageElement
type htmlAreaElement = DOM.htmlAreaElement
type videoPlaybackQuality = DOM.videoPlaybackQuality
type idleDeadline = DOM.idleDeadline
type cssRuleList = DOM.cssRuleList
type mediaKeySystemConfiguration = BaseEncryptedMediaExtensions.mediaKeySystemConfiguration

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to be consistent here in terms of type spreading

Comment thread src/Base/Base__Document.res Outdated
Comment on lines +3 to +7
type node
type htmlElement
type nodeList<'tNode>
type domImplementation
type documentType

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get this, why do we redefine those as abstract types?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't, I am cleaning this up.

Comment thread src/Base/Base__Element.res Outdated
Comment on lines +3 to +11
type document
type node
type htmlElement
type nodeList<'tNode>
type domTokenList
type namedNodeMap
type shadowRoot
type htmlCollection<'t>
type htmlSlotElement

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same question

Comment thread src/Base/Base__Element.res Outdated
[See Element on MDN](https://developer.mozilla.org/docs/Web/API/Element)
TODO: mark as private once mutating fields of private records is allowed
*/
@editor.completeFrom(DOM.Element)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same, doesn't exist

Comment thread src/Base/Base__Document.res Outdated
[See Document on MDN](https://developer.mozilla.org/docs/Web/API/Document)
TODO: mark as private once mutating fields of private records is allowed
*/
@editor.completeFrom(DOM.Document)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DOM.Document module doesn't exist, does it?

Comment thread src/DOM/DOM.res Outdated
NodeList objects are collections of nodes, usually returned by properties such as Node.childNodes and methods such as document.querySelectorAll().
[See NodeList on MDN](https://developer.mozilla.org/docs/Web/API/NodeList)
*/
@editor.completeFrom(DOM.NodeList) and nodeList<'tNode> = private {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

@jderochervlk jderochervlk marked this pull request as draft June 2, 2026 15:30
Comment thread src/DOM/Document.res Outdated
Comment on lines +14 to +15
@scope("globalThis.document")
external getElementById: string => null<DomTypes.element> = "getElementById"

@brnrdog brnrdog Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason getElementById is the only Document method scoped to globalThis.document? Everything else in this module is still @send.

This means iframeDocument->Document.getElementById(id) would break, since it always reads from the global doc (so no iframes/XML docs, for example). Should we keep using @send for flexibility?

@jderochervlk jderochervlk Jun 2, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My goal is to rely on @scope instead of @send. This means you can avoid having to get the object and pipe it to a function and then you can just call the functions directly on the module, but I am not sure that will actually work in practice for everything.

It depends really on what we want the public API to be.

let e: Event.t = {}

let value = e->Event.target

// or
let e: Event.t = {
  target: /* */
}
let value = e.target

Comment on lines +67 to +68
For shared DOM base interfaces such as `Element`, the structural record can live in a Base-owned module such as `Base__Element.res`. `Base.res` should re-export that implementation as a module, for example `module Element = Base__Element`, so the DOM API module can keep the familiar `DOM.element` alias while method modules use `DomTypes.element`.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is no longer accurate.

Base__ should only be used if it needs to be separate from a Types* module. For example, if there is a type t that is used by DOM and Module but module has other types or functions that DOM does not need there should be a Base__Module and a ModuleTypes file. If DOM does not access the Module or all types are used, then there should just be a ModuleTypes file.

And DomTypes has been removed in favor of keeping things simple and one directional.

Comment on lines +80 to +82

`Element.res` is a method module. It does not define the `element` record type directly. The shared DOM element type is owned by Base and threaded back into DOM through aliases:

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not 100% on this one. It is similar to how the old webapi bindings worked and does not expose keys on an object and instead relies on piping to access values using @get or @send.

This means you have to do this:

let title = element->Element.title

instead of this:

let title = element.title

By using the pipe option we can have the types not required by other libraries such as rescript-react which can make it more lightweight, but if Event and Element are lightweight enough I don't see why they can't be a requirement of rescript-react?


location->WebAPI.Location.reload
WebAPI.Location.reload()
```

@jderochervlk jderochervlk Jun 3, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should use a simpler way to get the location instead of using current. I don't know how I feel about that pattern actually, but I suppose we need some way to pass around location? I actually don't know why you would need to do that...

let href = WebAPI.Location.href

WebAPI.Location.reload()

With `"-open WebAPI"`, the same modules can be referenced without the `WebAPI.` prefix:

```ReScript
let location = Location.current

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how I feel about that pattern actually, but I suppose we need some way to pass around location? I actually don't know why you would need to do that...

jderochervlk and others added 2 commits June 3, 2026 12:20
* refactor: establish option 2 DOM API shape

* docs: document option 2 DOM API shape

* refactor: remove DOM compatibility aliases

* fix: expose document APIs through Document.t

* fix: include DOM owner modules in DOM feature

* fix: shrink DOM feature for React consumers

* Simplify internal feature groups

* Address PR review for shared DOM base types

* Add method style audit plan

* Move CanvasRenderingContext2D methods to public receiver

* Move DOMExtended methods to public receivers

* Move IndexedDB methods to public receivers

* Move Window methods to public receiver

* Move Canvas methods to public receivers

* Move DOM utility methods to public receivers

* Move HTML methods to public receivers

* working with AI and it's not doing great

* fix DOM casing and cleanup CSSStyleSheet

* remove DomExtended

* Rename DOMTypes module to DOM

---------

Co-authored-by: Codex <codex@openai.com>
@jderochervlk jderochervlk changed the title refactor: split DOM bindings into focused feature modules refactor: clean up public owners and singleton APIs Jun 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants