Skip to content

Releases: cloudflare/partykit

partyserver@0.5.5

28 Apr 18:45

Choose a tag to compare

Patch Changes

  • #394 9a927a3 Thanks @threepointone! - Don't reciprocate the WebSocket close handshake when the runtime delivers a reserved close code (1005, 1006, 1015). These codes are synthesized by the runtime when the peer didn't actually send a Close frame — there is no handshake to complete. The earlier 0.5.4 change ([#393](https://github.com/cloudflare/partykit/issues/393)) normalized these to 1000 and tried to send a reciprocating Close frame anyway; in cross-isolate transports (notably WebSocket pairs that flow back through Durable Object RPC, e.g. Cloudflare Agents sub-agents) the reciprocation would succeed synchronously but schedule an outbound write on a transport whose peer was already gone. The runtime then rejected that write asynchronously with Network connection lost, escaping closeQuietly's synchronous try/catch and surfacing as an unhandled promise rejection in tests and production logs. The fix is to skip the reciprocation for reserved codes — there's nothing to acknowledge to a peer that didn't speak. The narrow user-visible behavior change: a client that calls ws.close() with no code on compat dates < 2026-04-07 (no auto-reply) will now observe a non-clean close instead of a clean 1000 close, because the framework no longer fabricates a reciprocation. Clients that pass an explicit close code are unaffected.

partyserver@0.5.4

27 Apr 23:58

Choose a tag to compare

Patch Changes

  • #391 6273c96 Thanks @threepointone! - Persist a __ps_name fallback for name-based Durable Objects during initialization. This lets alarm handlers recover this.name even when firing on a stale on-disk alarm record that was scheduled by an older workerd version that didn't yet persist name into the alarm record. See #390.

  • #393 5335251 Thanks @threepointone! - Complete the WebSocket close handshake when a client initiates the close. Previously, both the hibernating webSocketClose handler and the non-hibernating close-event listener forwarded to user onClose but never sent a reciprocal Close frame, leaving clients stuck in CLOSING until they timed out and reported 1006 (abnormal closure). The framework now reciprocates the peer's Close frame in a finally block on both paths — required by the Hibernation API on every compat date, and required by the standard accept() API on compat dates before 2026-04-07 (where the runtime's web_socket_auto_reply_to_close flag isn't yet active). Calling close() on an already-closed socket is a silent no-op, so user code that already calls connection.close(...) from onClose is unaffected. Reserved close codes (1005, 1006, 1015) are normalized to 1000 before reciprocation so they don't throw InvalidAccessError. See #389.

partyserver@0.5.3

26 Apr 00:28

Choose a tag to compare

Patch Changes

  • #386 8a3bc02 Thanks @threepointone! - Document and test the supported pattern for using PartyServer with Durable Object Facets. No runtime behavior change.

    Background. Facets spawned via ctx.facets.get(name, factory) without an explicit id in FacetStartupOptions inherit the parent DO's ctx.id — including ctx.id.name. PartyServer's name getter reads ctx.id.name straight through, so on an implicit-id facet this.name returns the parent's name rather than the facet's logical name. This is a faithful reflection of the workerd contract, but it's almost never what framework authors expect.

    The fix is at the call site, not in PartyServer: pass id: someBoundDONamespace.idFromName(facetName) to ctx.facets.get(...). The facet then gets its own native ctx.id.name === facetName and PartyServer's name getter does the right thing automatically. No setName() is required, no __ps_name storage record is written, and cold-wake recovery happens for free because the factory re-runs and idFromName is deterministic.

    This release adds:

    • A "Using PartyServer with Durable Object Facets" section in the README that walks through the recommended pattern with a code example, calls out the implicit-id footgun explicitly, and documents that plain-string id values are not a substitute for idFromName(facetName) (workerd treats string ids as idFromString-like, so the resulting facet has no ctx.id.name).
    • setName() docstring updated to clarify that facets are NOT a setName() use case — point to the explicit-id pattern instead. The original setName() ctx.id.name mismatch throw is preserved as a typo guard for the idFromName happy path.
    • End-to-end facet test coverage against the real workerd ctx.facets.get(...) API. A FacetParent / FacetChild fixture exercises both the implicit-id path (pinning the runtime contract that this.name returns the parent's name in that flow — i.e., behavior-as-documentation so framework authors are unsurprised) and the explicit-id path (recommended; verifies that all reasonable id-construction strategies work and that cold wake recovers without any storage record). Plain-string id is also tested; the test asserts it does NOT carry a name, pinning the contract so callers don't get tempted by the type signature.

    The runtime behavior of Server (the name getter, setName(), the legacy __ps_name hydrate inside #ensureInitialized()) is unchanged from 0.5.2.

y-partyserver@2.2.0

24 Apr 20:17

Choose a tag to compare

Minor Changes

  • #378 f3ab44f Thanks @threepointone! - Use native ctx.id.name to populate this.name.

    Durable Objects now expose ctx.id.name on every entry point (constructor, fetch, alarm, hibernating websocket handlers) when the DO is addressed via idFromName()/getByName(). PartyServer now uses this as the primary source of this.name, which simplifies routing, eliminates storage writes, and makes this.name available inside the constructor.

    Changes in partyserver:

    • this.name resolves from this.ctx.id.name. The apologetic workerd#2240 error message is gone.
    • this.name is now available inside the constructor and from class field initializers, not just after setName()/fetch() has run.
    • routePartykitRequest no longer issues a setName()/_initAndFetch() RPC before fetch(). The WebSocket path goes from 2 RPCs to 1; the HTTP path remains 1 RPC. Props, when supplied, are delivered to the DO via the x-partykit-props request header, set after onBeforeConnect/onBeforeRequest hooks run.
    • getServerByName continues to perform a single RPC to ensure onStart() has completed before returning, so user-defined RPC methods on the returned stub can rely on initialization being done. That RPC is now cheaper internally (no storage write; name is read from ctx.id.name).
    • Server no longer writes the __ps_name record to storage. Existing records remain on disk for backward compatibility and are only read inside alarm() as a fallback for alarms that were scheduled before 2026-03-15 (where ctx.id.name is not carried into the alarm handler — see the Durable Objects ID docs).
    • setName() and _initAndFetch() are marked @deprecated. They continue to work for backward compatibility. setName(name) now throws if name does not match ctx.id.name.
    • The x-partykit-room header is still accepted as a fallback when ctx.id.name is not available.
    • Error message when the name cannot be resolved has been rewritten to list the three real causes (unsupported addressing via idFromString()/newUniqueId(), runtime too old to expose ctx.id.name, or direct stub.fetch() without routePartykitRequest/getServerByName).
    • When reading this.name throws, it is because ctx.id.name is undefined and no legacy fallback has populated the name: the DO was addressed via idFromString() or newUniqueId() (both unsupported), the runtime is too old to expose ctx.id.name, or a pre-2026-03-15 alarm fired before the legacy storage fallback ran.

    Changes in all affected packages (partyserver, partysub, partysync, y-partyserver, hono-party):

    • @cloudflare/workers-types peer dependency bumped from ^4.20240729.0 to ^4.20260424.1. The old range predates ctx.id.name in the type surface.

    Not supported: addressing PartyServer DOs via idFromString() or newUniqueId(). These paths return ctx.id.name === undefined inside the DO and will surface as a clear error from this.name. PartyServer has always assumed name-based addressing via getServerByName / routePartykitRequest; this release makes that assumption explicit.

partywhen@0.1.5

24 Apr 20:17

Choose a tag to compare

Patch Changes

  • Updated dependencies [f3ab44f]:
    • partyserver@0.5.0

partysync@2.1.0

24 Apr 20:17

Choose a tag to compare

Minor Changes

  • #378 f3ab44f Thanks @threepointone! - Use native ctx.id.name to populate this.name.

    Durable Objects now expose ctx.id.name on every entry point (constructor, fetch, alarm, hibernating websocket handlers) when the DO is addressed via idFromName()/getByName(). PartyServer now uses this as the primary source of this.name, which simplifies routing, eliminates storage writes, and makes this.name available inside the constructor.

    Changes in partyserver:

    • this.name resolves from this.ctx.id.name. The apologetic workerd#2240 error message is gone.
    • this.name is now available inside the constructor and from class field initializers, not just after setName()/fetch() has run.
    • routePartykitRequest no longer issues a setName()/_initAndFetch() RPC before fetch(). The WebSocket path goes from 2 RPCs to 1; the HTTP path remains 1 RPC. Props, when supplied, are delivered to the DO via the x-partykit-props request header, set after onBeforeConnect/onBeforeRequest hooks run.
    • getServerByName continues to perform a single RPC to ensure onStart() has completed before returning, so user-defined RPC methods on the returned stub can rely on initialization being done. That RPC is now cheaper internally (no storage write; name is read from ctx.id.name).
    • Server no longer writes the __ps_name record to storage. Existing records remain on disk for backward compatibility and are only read inside alarm() as a fallback for alarms that were scheduled before 2026-03-15 (where ctx.id.name is not carried into the alarm handler — see the Durable Objects ID docs).
    • setName() and _initAndFetch() are marked @deprecated. They continue to work for backward compatibility. setName(name) now throws if name does not match ctx.id.name.
    • The x-partykit-room header is still accepted as a fallback when ctx.id.name is not available.
    • Error message when the name cannot be resolved has been rewritten to list the three real causes (unsupported addressing via idFromString()/newUniqueId(), runtime too old to expose ctx.id.name, or direct stub.fetch() without routePartykitRequest/getServerByName).
    • When reading this.name throws, it is because ctx.id.name is undefined and no legacy fallback has populated the name: the DO was addressed via idFromString() or newUniqueId() (both unsupported), the runtime is too old to expose ctx.id.name, or a pre-2026-03-15 alarm fired before the legacy storage fallback ran.

    Changes in all affected packages (partyserver, partysub, partysync, y-partyserver, hono-party):

    • @cloudflare/workers-types peer dependency bumped from ^4.20240729.0 to ^4.20260424.1. The old range predates ctx.id.name in the type surface.

    Not supported: addressing PartyServer DOs via idFromString() or newUniqueId(). These paths return ctx.id.name === undefined inside the DO and will surface as a clear error from this.name. PartyServer has always assumed name-based addressing via getServerByName / routePartykitRequest; this release makes that assumption explicit.

partysub@2.1.0

24 Apr 20:17

Choose a tag to compare

Minor Changes

  • #378 f3ab44f Thanks @threepointone! - Use native ctx.id.name to populate this.name.

    Durable Objects now expose ctx.id.name on every entry point (constructor, fetch, alarm, hibernating websocket handlers) when the DO is addressed via idFromName()/getByName(). PartyServer now uses this as the primary source of this.name, which simplifies routing, eliminates storage writes, and makes this.name available inside the constructor.

    Changes in partyserver:

    • this.name resolves from this.ctx.id.name. The apologetic workerd#2240 error message is gone.
    • this.name is now available inside the constructor and from class field initializers, not just after setName()/fetch() has run.
    • routePartykitRequest no longer issues a setName()/_initAndFetch() RPC before fetch(). The WebSocket path goes from 2 RPCs to 1; the HTTP path remains 1 RPC. Props, when supplied, are delivered to the DO via the x-partykit-props request header, set after onBeforeConnect/onBeforeRequest hooks run.
    • getServerByName continues to perform a single RPC to ensure onStart() has completed before returning, so user-defined RPC methods on the returned stub can rely on initialization being done. That RPC is now cheaper internally (no storage write; name is read from ctx.id.name).
    • Server no longer writes the __ps_name record to storage. Existing records remain on disk for backward compatibility and are only read inside alarm() as a fallback for alarms that were scheduled before 2026-03-15 (where ctx.id.name is not carried into the alarm handler — see the Durable Objects ID docs).
    • setName() and _initAndFetch() are marked @deprecated. They continue to work for backward compatibility. setName(name) now throws if name does not match ctx.id.name.
    • The x-partykit-room header is still accepted as a fallback when ctx.id.name is not available.
    • Error message when the name cannot be resolved has been rewritten to list the three real causes (unsupported addressing via idFromString()/newUniqueId(), runtime too old to expose ctx.id.name, or direct stub.fetch() without routePartykitRequest/getServerByName).
    • When reading this.name throws, it is because ctx.id.name is undefined and no legacy fallback has populated the name: the DO was addressed via idFromString() or newUniqueId() (both unsupported), the runtime is too old to expose ctx.id.name, or a pre-2026-03-15 alarm fired before the legacy storage fallback ran.

    Changes in all affected packages (partyserver, partysub, partysync, y-partyserver, hono-party):

    • @cloudflare/workers-types peer dependency bumped from ^4.20240729.0 to ^4.20260424.1. The old range predates ctx.id.name in the type surface.

    Not supported: addressing PartyServer DOs via idFromString() or newUniqueId(). These paths return ctx.id.name === undefined inside the DO and will surface as a clear error from this.name. PartyServer has always assumed name-based addressing via getServerByName / routePartykitRequest; this release makes that assumption explicit.

partyserver@0.5.2

24 Apr 22:24

Choose a tag to compare

Patch Changes

  • #383 fe030f6 Thanks @threepointone! - setName() is now the sanctioned bootstrap API for non-idFromName DOs.

    When ctx.id.name is undefined (e.g. Cloudflare Agents facets, which are spawned via ctx.facets.get(...) rather than idFromName()), setName(name) now persists the name to the legacy __ps_name storage key in addition to stashing it in memory. This means cold-wake invocations of the DO recover the name through #ensureInitialized()'s legacy storage fallback without the framework having to reach into PartyServer's private storage layout.

    Frameworks that previously did:

    await this.ctx.storage.put("__ps_name", name);
    await this.__unsafe_ensureInitialized();

    can now do:

    await this.setName(name);

    Backward compatible:

    • For DOs addressed via idFromName() / getByName() (the happy path), setName() continues to NOT write storage — ctx.id.name is the source of truth and setName() is just a no-op-plus-onStart.
    • The pre-existing direct-storage-write pattern keeps working — the storage write becomes idempotent with what setName() would do.

    setName()'s @deprecated docblock has been clarified: it remains the supported API for framework-level bootstrap of non-idFromName DOs and for delivering initial props to onStart(). The deprecation only applies to redundant calls on DOs that were addressed via idFromName().

partyserver@0.5.1

24 Apr 20:56

Choose a tag to compare

Patch Changes

  • #381 0274658 Thanks @threepointone! - Fix: restore legacy __ps_name storage fallback for framework bootstrap patterns.

    0.5.0 moved the legacy storage hydrate into alarm() only, breaking Cloudflare Agents facets and any other framework that writes __ps_name directly before calling __unsafe_ensureInitialized(). Facet DOs are spawned via ctx.facets.get(...) rather than idFromName() and therefore have ctx.id.name === undefined; they relied on PartyServer reading the storage record back to populate this.name before onStart().

    Changes:

    • Move the legacy __ps_name hydrate from alarm() into #ensureInitialized(), still gated on !ctx.id.name && !#_name so it costs nothing on the happy path (normal idFromName()/getByName() DOs skip the storage read entirely).
    • Server.fetch() now delegates to #ensureInitialized() for the hydrate instead of doing its own. The x-partykit-room header fallback remains as a last resort when neither ctx.id.name nor a legacy storage record is available.
    • Server.alarm() is simplified — it no longer needs its own hydrate call since #ensureInitialized() handles it.
    • setName()'s @deprecated docblock is softened to clarify that it remains appropriate for framework-level bootstrap of non-idFromName DOs (e.g. Agents facets), not just a deprecated compatibility shim.

partyserver@0.5.0

24 Apr 20:17

Choose a tag to compare

Minor Changes

  • #378 f3ab44f Thanks @threepointone! - Use native ctx.id.name to populate this.name.

    Durable Objects now expose ctx.id.name on every entry point (constructor, fetch, alarm, hibernating websocket handlers) when the DO is addressed via idFromName()/getByName(). PartyServer now uses this as the primary source of this.name, which simplifies routing, eliminates storage writes, and makes this.name available inside the constructor.

    Changes in partyserver:

    • this.name resolves from this.ctx.id.name. The apologetic workerd#2240 error message is gone.
    • this.name is now available inside the constructor and from class field initializers, not just after setName()/fetch() has run.
    • routePartykitRequest no longer issues a setName()/_initAndFetch() RPC before fetch(). The WebSocket path goes from 2 RPCs to 1; the HTTP path remains 1 RPC. Props, when supplied, are delivered to the DO via the x-partykit-props request header, set after onBeforeConnect/onBeforeRequest hooks run.
    • getServerByName continues to perform a single RPC to ensure onStart() has completed before returning, so user-defined RPC methods on the returned stub can rely on initialization being done. That RPC is now cheaper internally (no storage write; name is read from ctx.id.name).
    • Server no longer writes the __ps_name record to storage. Existing records remain on disk for backward compatibility and are only read inside alarm() as a fallback for alarms that were scheduled before 2026-03-15 (where ctx.id.name is not carried into the alarm handler — see the Durable Objects ID docs).
    • setName() and _initAndFetch() are marked @deprecated. They continue to work for backward compatibility. setName(name) now throws if name does not match ctx.id.name.
    • The x-partykit-room header is still accepted as a fallback when ctx.id.name is not available.
    • Error message when the name cannot be resolved has been rewritten to list the three real causes (unsupported addressing via idFromString()/newUniqueId(), runtime too old to expose ctx.id.name, or direct stub.fetch() without routePartykitRequest/getServerByName).
    • When reading this.name throws, it is because ctx.id.name is undefined and no legacy fallback has populated the name: the DO was addressed via idFromString() or newUniqueId() (both unsupported), the runtime is too old to expose ctx.id.name, or a pre-2026-03-15 alarm fired before the legacy storage fallback ran.

    Changes in all affected packages (partyserver, partysub, partysync, y-partyserver, hono-party):

    • @cloudflare/workers-types peer dependency bumped from ^4.20240729.0 to ^4.20260424.1. The old range predates ctx.id.name in the type surface.

    Not supported: addressing PartyServer DOs via idFromString() or newUniqueId(). These paths return ctx.id.name === undefined inside the DO and will surface as a clear error from this.name. PartyServer has always assumed name-based addressing via getServerByName / routePartykitRequest; this release makes that assumption explicit.