Skip to content

v0.17

Latest

Choose a tag to compare

@derstruct derstruct released this 10 Oct 11:58

Doors 0.17 Release Notes

Doors 0.17 adds deferred Door operations: show a placeholder right away and replace it with slow content once it is rendered. With doors.IsDocument, the same code sends the slow content complete in the page response.

Highlights

Deferred operations

DeferredInner, DeferredOuter, and DeferredStatic work like Inner, Outer, and Static, but wait until the Door's current content is rendered and scheduled for delivery. Use them to show a placeholder first and replace it with content that is slow to render:

func (p *Page) showReport(ctx context.Context, period string) {
	p.report.Inner(ctx, <div class="skeleton">Loading report...</div>)
	p.report.DeferredInner(ctx, ReportTable{period: period})
}

Each is also a package function that targets the Door ctx renders in, so content in a Bind or route can defer its own slow part:

elem (p *Page) Main() {
	~(p.period.Bind(elem(period string) {
		<section class="report">
			~(if doors.IsDocument(ctx) {
				~ReportTable{period: period}
			} else {
				~~
				doors.DeferredInner(ctx, ReportTable{period: period})
				~~
				<div class="skeleton">Loading report...</div>
			})
		</section>
	}))
}

See Door.

IsDocument

doors.IsDocument(ctx) is true only while rendering the page response: the initial page render and every Door placed during it. Use it, as above, to send slow content complete with the page instead of as a skeleton followed by an update.

Behavior Changes

  • Door updates follow SolitaireDisableGzip. It was documented to, but Door updates followed ServerDisableGzip. ServerDisableGzip now covers HTML and resources only.
  • A Door rendered in a new place leaves its old place once the new content is queued, instead of at once.

Fixes

  • Equality functions (NewSourceEqual, NewSourceEqualNoSkip, DeriveSourceEqual, DeriveBeamEqual, RouteDeriveEqual, TabStateEqual) received (new, old) on updates, while internals used a different order. They now always get (old, new).
  • Door renders during the page render could race on CSP collection.
  • A panic in cleanup code during a Door operation, such as in a beam watcher's Cancel, could leave the operation's channel open for good.
  • Update stream:
    • Ending an instance while updates were being written could crash the process or leave an operation's channel open.
    • A result or gap report for a re-sent update that arrived while it was being written was lost, so the instance later ended with a sync timeout, or the stream panicked.
    • Navigation actions (ActionLocation*) were not re-sent after the frame carrying them was lost.
    • A late report of a lost update that had already been re-delivered could end the instance when many updates followed in between.
    • A new stream could not take over while the previous one was stuck writing to a client that stopped reading.
    • A request context canceled with a cause from outside Doors, such as a BaseContext from signal.NotifyContext, made the stream panic.
    • A panic in one call's result callback left the other calls answered in the same report unresolved.
    • The instance ended on reaching SolitaireQueue instead of on exceeding it.
  • ActionScroll options that cannot be encoded to JSON failed inside the update stream; Call now reports the encoding error.

Breaking Changes

Old New
CSP.ScriptStrictDynamic Removed: 'strict-dynamic' made browsers block the Doors client script, so pages did not work
ServerDisableGzip turns off gzip for Door updates SolitaireDisableGzip does
Keys: on AKeyDown and AKeyUp Key:, named like Scope and Indicator

Migration

go get github.com/doors-dev/doors@v0.17.0

Rename Keys: to Key: on AKeyDown and AKeyUp. Remove ScriptStrictDynamic from doors.CSP. If you turned off gzip with ServerDisableGzip and want Door updates uncompressed too, also set SolitaireDisableGzip.