Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Move the web adapter to stable Effect 4.0.x (0.6.0)

GitHub: https://github.com/misofm/engine-web-adapter/issues/131

## Product outcome

Release `@misofm/engine-web-adapter@0.6.0` on stable Effect `4.0.x` with `@misofm/codec@0.2.0`, so an application on the Effect 4.0.4 train decodes FLAC in the mixer. `0.5.13` depends on `effect@4.0.0-rc.112`; when the codec resolves the application's Effect 4.x copy while the adapter keeps its nested rc.112 copy (codec 0.2.0, or codec 0.1.1 under bun's loose peers or `npm --legacy-peer-deps`), the adapter's `Layer.build(makeFlacDecoderLayer(bytes))` fails with `TypeError: Cannot read properties of undefined (reading 'scheduler')` and no stem decodes. Unit tests resolve one Effect copy and did not catch it.

Base: origin/main `602b7c7` (adapter 0.5.13). Branch `effect-4.0.4`, draft PR https://github.com/misofm/engine-web-adapter/pull/130. Codec prerequisite: https://github.com/misofm/codec/issues/10 (PR https://github.com/misofm/codec/pull/9). Effect guidance: [skills revision 0507930](https://github.com/unconfirmedlabs/skills/tree/0507930b6dd13f39739aba40b86a6afb63d32e09/effect-ts) (`references/migration.md`, "Effect v4 release candidates to 4.0.x").

## Smallest bounded implementation

- `package.json`: version 0.6.0; `effect` moves from a dependency (exact rc.112) to a peer `>=4.0.3 <4.1` with exact dev dependency 4.0.4, so the adapter, codec, and application share one copy; `@misofm/codec` exact `0.2.0`; `CHANGELOG.md` in `files`. Engine stays `0.4.3`.
- Codec 0.2.0 was published (https://github.com/misofm/codec/releases/tag/v0.2.0, registry integrity `sha512-nbhiEKPB…ksUmqGA==`), so the dependency is the registry package and the lockfile resolves its registry tarball with that integrity; during development it was a git ref to the throwaway branch `misofm/codec:effect-4.0.4-ci-dist`. `npm-publish.yml` refuses to run unless `dependencies['@misofm/codec'] == "0.2.0"` and the peer range matches, and repeats both checks against the registry artifact.
- `effect/unstable/http` becomes `effect/http` (three modules). No other compile change. Built `.d.ts` differ only in that import in `stems/sparse-response.d.ts`.
- Preserve rc.112 behaviour: `preflightExpected` and `preflightSpan` check `Reflect.ownKeys` because Effect 4.0.x (#8423) strips non-enumerable extras under `onExcessProperty: "error"` (tests added).
- Deliberate behaviour change: the sparse full GET disables `HttpClient.TracerPropagationEnabled`, as the FLAC range lane does, so `b3`/`traceparent` headers no longer force a CORS preflight (test added).
- Browser gate scripts no longer copy `fast-check`/`pure-rand`/`msgpackr`; `NOTICE` names the codec decoder asset hash instead of codec 0.1.0; workflow job name and `PACKAGE_VERSION` are 0.6.0; `CHANGELOG.md` records the audit and migration note.
- Do not change exports, workers, AudioWorklet code, delivery policy, or the Wasm assets.

## Objective gates

1. `npm ci --ignore-scripts` and `npm run check` (383 tests, SHA-256/BLAKE3 Wasm reproducibility, package policy). Also the full suite with `effect@4.0.3` installed.
2. `npm run test:browser`, `node scripts/browser-packed.mjs --indexed-sparse`, and the Chromium part of `npm run test:browser:opfs` with the installed Chromium; PR workflow Packed OPFS qualification (macOS Chromium and WebKit) green.
3. Crash repro in a consumer with `effect@4.0.4`: codec 0.1.1 with adapter 0.5.13 fails with the `scheduler` TypeError; codec 0.2.0 with the packed adapter 0.6.0 compiles the decoder module. `npm install --strict-peer-deps` and `npm ls` valid on 4.0.4 and 4.0.3; 4.0.0-rc.115 rejected.
4. Release only from `main` with the published codec 0.2.0 dependency.

## Release contract and blockers

Run `.github/workflows/npm-publish.yml` on `main` (`mode=publish`, the exact 40-character merge SHA); the workflow publishes through OIDC only, refuses an existing version, and verifies the registry artifact. Record the run URL, registry integrity, and archive hashes here. Blockers: PR #130 awaits review and merge. Publication has not been performed.
8 changes: 6 additions & 2 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ concurrency:

env:
PACKAGE_NAME: "@misofm/engine-web-adapter"
PACKAGE_VERSION: "0.5.13"
PACKAGE_VERSION: "0.6.0"
NPM_CONFIG_REGISTRY: "https://registry.npmjs.org"

jobs:
publish:
name: ${{ inputs.mode }} @misofm/engine-web-adapter 0.5.13
name: ${{ inputs.mode }} @misofm/engine-web-adapter 0.6.0
runs-on: ubuntu-24.04
steps:
- name: Refuse a non-main or mismatched dispatch
Expand Down Expand Up @@ -64,6 +64,8 @@ jobs:
[[ "$(node -p "require('./package-lock.json').name")" == "$PACKAGE_NAME" ]]
[[ "$(node -p "require('./package-lock.json').version")" == "$PACKAGE_VERSION" ]]
[[ "$(node -p "require('./package.json').dependencies['@misofm/engine']")" == "0.4.3" ]]
[[ "$(node -p "require('./package.json').dependencies['@misofm/codec']")" == "0.2.0" ]]
[[ "$(node -p "require('./package.json').peerDependencies.effect")" == ">=4.0.3 <4.1" ]]
[[ "$(node -p "require('./package.json').repository.url")" == "git+https://github.com/misofm/engine-web-adapter.git" ]]

- name: Install dependencies
Expand Down Expand Up @@ -139,6 +141,8 @@ jobs:
sleep 5
done
[[ "$(npm view "$PACKAGE_NAME@$PACKAGE_VERSION" dependencies.@misofm/engine)" == "0.4.3" ]]
[[ "$(npm view "$PACKAGE_NAME@$PACKAGE_VERSION" dependencies.@misofm/codec)" == "0.2.0" ]]
[[ "$(npm view "$PACKAGE_NAME@$PACKAGE_VERSION" peerDependencies.effect)" == ">=4.0.3 <4.1" ]]
consumer=$(mktemp -d)
trap 'rm -rf -- "$consumer"' EXIT
cd "$consumer"
Expand Down
108 changes: 108 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Changelog

All notable changes to `@misofm/engine-web-adapter`, newest first.

## 0.6.0

Moves the package from Effect `4.0.0-rc.112` to stable Effect `4.0.x` (developed and
tested on `4.0.4`, peer floor `4.0.3`) and from `@misofm/codec@0.1.1` to `0.2.0`.
The runtime API is unchanged: no export was added, removed or re-typed, the session,
resolver, delivery, store and error contracts are untouched, and the only difference
in the built `.d.ts` files is that `stems/sparse-response.d.ts` imports `HttpClient`
from `effect/http` instead of `effect/unstable/http` (Effect removed the `unstable`
path in rc.118).

### Why this release exists

`0.5.13` depended on `effect@4.0.0-rc.112` directly, so it always ran its own rc.112
Effect copy. The failure appears whenever the codec resolves the application's Effect
4.x copy while the adapter keeps that nested rc.112 copy: with `@misofm/codec@0.2.0`
(whose peer is satisfied by the application's 4.0.x), and also with `@misofm/codec@0.1.1`
under package managers that accept an out-of-range peer, such as bun's loose peer
resolution or `npm --legacy-peer-deps` (npm's strict peer check refuses that tree).
The adapter's rc.112 runtime then runs a `Layer` built by the codec's 4.0.x copy
(`Effect.runPromise(Effect.scoped(Layer.build(makeFlacDecoderLayer(bytes))))` while
loading the decoder module) and fails with `TypeError: Cannot read properties of
undefined (reading 'scheduler')`, so FLAC decoding never starts. The adapter's unit tests
did not catch this because they resolve a single Effect copy.

### Changed (breaking)

- **`effect` is now a peer dependency, `>=4.0.3 <4.1`** (was a hard dependency on exactly
`4.0.0-rc.112`). The adapter, `@misofm/codec` and your application now share the one
Effect copy you install. Release candidates are no longer supported; stay on 0.5.x for
`effect@4.0.0-rc.112`.
- **`@misofm/codec` is pinned to exactly `0.2.0`** (was `0.1.1`), whose own peer is
`effect >=4.0.3 <4.1`.
- The `fast-check`, `pure-rand` and `msgpackr` packages that Effect rc.112 pulled in are
no longer installed: Effect 4.0.x has no runtime dependencies.

### Behaviour changes

- **Sparse full GET no longer sends trace-propagation headers.** `openSparseResponse`
now provides `HttpClient.TracerPropagationEnabled = false`, as the FLAC range lane
already did. The request always runs under the `SparseResponse.open` span, and with
propagation enabled Effect adds `b3` and `traceparent` headers, which turn a
cross-origin GET into a CORS preflight. A regression test captures the headers (they
were sent before this change). If you relied on those headers reaching your stem host,
that no longer happens.
- **Unknown properties on a sparse expectation or span are still rejected, including
non-enumerable ones.** Effect 4.0.x (#8423) silently strips a non-enumerable extra key
under `onExcessProperty: "error"`, where rc.112 rejected it. `preflightExpected` and
`preflightSpan` now check `Reflect.ownKeys`, so `installSource`/`openSource`
expectations and resolver spans behave as in 0.5.13 (tests added).
- **Cause shape on cancelled FLAC range reads.** The download-admission acquire in
`flac-delivery.ts` (`Effect.acquireRelease`, uninterruptible) is the one region where an
interruption can meet a failure. From Effect 4.0.1 (#8652) an interrupt that arrives
while it is failing is kept in the cause: the cause is `Fail, Interrupt` where rc.112
produced `Fail` only (checked on both versions). The public error is unchanged
(`stem.cancelled` for an aborted operation, otherwise the same typed delivery error);
only the nested `cause` aggregate may now list one extra `AbortError` entry.

### Audited without a behaviour change

The package uses `Effect.timeout`/`timeoutOrElse`/`raceFirst`, `Effect.acquireUseRelease`,
`Effect.uninterruptible`, `Scope.make`/`close`, `Effect.callback`,
`Stream.fromPull`/`transformPull`/`paginate`/`timeoutOrElse`, `Channel.toPullScoped`,
`Queue.bounded`, `Layer.build`, `HttpClient.withScope`, `FetchHttpClient`, `Cause.*`,
`Exit.*`, `Schema.TaggedError` and Schema decoding, and no `partition`, `Schedule`,
`Effect.retry` or Effect `Worker` module.

- `Scope.close` finalizers run uninterruptibly (Effect rc.118). None of the adapter's
scope closes (sparse acquisition scope, async-iterator bridge scope, response scopes)
is reached by interrupting the closer, so nothing changes here.
- Finalizer order (reverse of registration) and the one concurrent `Effect.forEach`
(sparse source preparation, #8585 keeps sibling cleanup failures alongside an
interruption) were checked; the cancellation, cleanup-failure and ordering tests pass
unchanged.
- `Effect.acquireUseRelease` releases when the use callback throws synchronously
(rc.113) and `Effect.race*` interrupts losers that are still starting (4.0.0). In the
abort helper (`abortEffect`), a loser's `Effect.callback` cleanup removes the `abort`
listener from the operation signal, and a winner's listener fires once (`{ once: true }`).
- The `store.openSession` call inside `Effect.uninterruptible` (sparse acquisition)
already awaited the store's own cancellation and is unchanged.
- `Effect.timeout` still fails with a `TimeoutError` whose `_tag` the adapter matches with
`catchTag`; no adapter code reads the message text.
- A `SchemaError` is serialized as a compact `{ _tag, message }` object in JSON and Node
inspection (4.0.3). The adapter never serializes one; its errors are
`EngineWebAdapterError` values.
- The `internal/*.js` Worker bundles embed Effect `4.0.4` and `@misofm/codec@0.2.0` at
build time, whatever Effect version your application installs inside the peer range.
The main-thread modules import your Effect copy.

Checks run for this release are listed in the pull request: the full `npm run check`
(383 tests), the packed Chromium consumer with real OPFS and a Worker FLAC decode, the
indexed sparse packed lane, and the macOS OPFS qualification workflow.

### Migrating from 0.5.x

1. Install Effect yourself and move the whole Effect release train to stable together,
pinned to one `4.0.x` version, for example `"effect": "4.0.4"` plus every `@effect/*`
package you use at `4.0.4`. A mixed release-candidate and stable install fails to
resolve `effect/*` module paths, and two copies of `effect` fail at runtime with the
`reading 'scheduler'` error above. Do not rely on the peer range alone: it admits
`>=4.0.3 <4.1`, but each `@effect/*` package peers on its own `effect` floor.
2. Upgrade `@misofm/codec` to `0.2.0` if you import it directly, and move every other
`@misofm/*` package that depends on `effect` to a stable-Effect release in the same
change.
3. Nothing to change in code that imports from `@misofm/engine-web-adapter`.
4 changes: 3 additions & 1 deletion NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ misofm/app commit 7485693e9bbcf2f65a91a4e5950e22d678d99062; their retained
attribution is shipped with the SDK that now owns these implementations.

This product redistributes the pinned libFLAC 1.5.0 WebAssembly decoder owned
and published by `@misofm/codec@0.1.0`. The packed adapter also bundles the
and published by `@misofm/codec` (decoder asset SHA-256
`70caf38185675dff89498e89f98171d49ec6f143a56c6895088d93c35e2018cd`, first
released in 0.1.1 and shipped unchanged in 0.2.0). The packed adapter also bundles the
public codec's Effect-powered worker implementation. The complete codec,
Effect, and hash-wasm license texts, together with the codec's third-party notices and
licenses for the bundled Wasm asset, are included in `dist/codec-licenses/`.
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
# @misofm/engine-web-adapter

Headless, framework-neutral browser session hosting for
`@misofm/engine@0.4.3`. Version 0.5 identifies canonical PCM with BLAKE3-256 and streams standards-compliant native FLAC
through bounded HTTP ranges and a one-stem universal `@misofm/codec@0.1.1`
`@misofm/engine@0.4.3`. Canonical PCM is identified with BLAKE3-256, and standards-compliant native FLAC streams
through bounded HTTP ranges and a one-stem universal `@misofm/codec@0.2.0`
Wasm Worker, verifies
canonical PCM into OPFS, then feeds the Engine through bounded shared-memory
rings. URL, authentication, and request mapping remain caller-owned.

## Install

```sh
npm install @misofm/engine-web-adapter@0.5.13 @misofm/engine@0.4.3
npm install @misofm/engine-web-adapter@0.6.0 @misofm/engine@0.4.3 effect@4.0.4
```

Effect is a peer dependency (`effect >=4.0.3 <4.1`): the adapter, the codec, and
your application must share one Effect copy, so pin the whole Effect release train
you use to one exact `4.0.x` version. See the [changelog](CHANGELOG.md) for the
migration from 0.5.x (Effect `4.0.0-rc.112`).

The package is ESM-only and remains pinned to exactly Engine `0.4.3` and
`@misofm/codec` `0.1.1`. The codec currently supports Node `>=22.23.2 <23`
`@misofm/codec` `0.2.0`. The codec currently supports Node `>=22.23.2 <23`
and Bun `>=1.4.2 <1.5`; browser consumers use the bundled public codec asset.
The integration uses the published Engine 0.4.3 archive
from commit `a3d71148e4144a396aa1ceb7fb83b6e671a0eced`, SHA256
Expand Down
Loading
Loading