Repository navigation
feat: run on webpack-dev-middleware's client, with compatibility kept here - #5750
alexander-akait wants to merge 24 commits into
Conversation
🦋 Changeset detectedLatest commit: 1ff3dde The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
1e729d2 to
14fa95c
Compare
|
One or more co-authors of this pull request were not found. You must specify co-authors in commit message trailer via: Supported
Alternatively, if the co-author should not be included, remove the Please update your commit message(s) by doing |
c78c27d to
420ee5c
Compare
First step of moving the hot clients into webpack-dev-middleware: this
package's `WebSocketClient` is a strict subset of the one that package now
ships, and worse in three ways. It has no `close()` at all, despite
declaring `@implements {CommunicationClient}`, which the interface requires.
It has no guard against an event the socket had already queued reporting
after the caller closed, so a close could schedule a reconnection nobody
asked for. And it hands the url to `new WebSocket` unresolved, which throws
on browsers before Chrome 125 / Firefox 124 / Safari 17.3 for a relative or
`http(s):` url — this package always builds an absolute `ws:` url, so that
one never bit here, but it is a trap for anyone reusing the class.
Re-exported rather than deleted: `client.webSocketTransport` resolves to
this path, so anything pointing at it keeps working.
`import/no-unresolved` is switched off for the file, following the exemption
already in place for `@changesets/get-github-info`: the import resolver
cannot follow an `exports` subpath. TypeScript does resolve it, so
`lint:types-client` still covers the import.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
692 lines of overlay deleted in favour of the one dev-middleware ships —
the state machine, the iframe, the rendering, the runtime-error listeners.
What is left is an adapter: this package's `createOverlay`/`send` shape on
top of that overlay's `showProblems`/`clear`, plus `formatProblem`, which
stays because the console uses it and because this package still receives
webpack's error objects rather than formatted strings.
Three things had to be carried across so nothing outward changes:
* the element id. It is what a test, a screenshot tool or an integration
finds the overlay by, so `webpack-dev-server-client-overlay` is passed
through dev-middleware's new `overlay.id` rather than renamed;
* the Trusted Types policy name. Under an enforced
`require-trusted-types-for 'script'` the page's CSP allowlists a policy
by name, and dev-middleware's default is a different one, so
`webpack-dev-server#overlay` is passed explicitly — including where
this package's option is `false`, which means "no name of my own";
* `/webpack-dev-server/open-editor`. dev-middleware leaves the endpoint
empty by default, having no route to point at, so the file references
in a problem would have stopped being clickable.
The first two came from the e2e suite failing, not from reading the code,
which is the argument for swapping under the tests rather than after.
What does change is the overlay's internal DOM, which is not an interface
this package documents: a clickable file reference is `[data-open-file]`
rather than `[data-can-open]`, and a problem is headed by its level and
origin rather than "Compiled with problems". Two tests assert on those and
move with the implementation.
Checked against a baseline taken first: the overlay suite fails exactly
the set it fails on a clean `main` in this container — no case newly
failing, none newly passing — and the client and web-socket-url suites are
unmoved.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
`client.webSocketTransport: "sse"` connects the page with `EventSource` instead of a WebSocket. The endpoint is webpack-dev-middleware's hot endpoint, which is an ordinary response rather than an upgrade, so there is no second server to start and it sits in the middleware chain where everything this package puts in front of it can still see it. Naming it as the client's transport picks the endpoint that serves it, so `webSocketServer` does not have to be set as well; `webSocketServer: "sse"` is accepted too. `lib/servers/EventSourceServer.js` is the adapter between that endpoint and the transport shape the rest of this package is written against, so nothing above the wire can tell which one it is talking to — same protocol, same messages, same options. All of the protection stays here: the endpoint hands over the request each client connected with, and `allowedHosts` and the origin check decide as they do for a socket. One difference in how that check reads a request. A browser puts an `Origin` on a WebSocket handshake whether or not it is cross-origin, so one without it is refused; `EventSource` sends no `Origin` on a same-origin request, so for a stream an absent one is taken as a page of this server's own. A stream that does carry an `Origin` — which is what a cross-origin page sends — is checked as before, and the `Host` check runs either way. Both transports are the middleware's now, re-exported from `client-src/clients/`, and each has a test of its own: the contract both answer, plus the silence watchdog only a stream needs. `test/e2e/event-source.test.js` drives a real browser through a hot update over the stream, the handshake it is greeted with, and both sides of the host check. `socket.js` no longer throws on a frame it cannot parse. A stream's keep-alive has to be a `data:` frame rather than a comment, or the client's own watchdog would count a quiet connection as a dead one, so something that is not JSON now arrives on the wire by design. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
`client-src/overlay.js` was the last of it: an adapter whose substance was
turning webpack's error objects into the shape the shared overlay renders, and
a `send({ type })` state machine over `showProblems`/`clear`. Both belong
elsewhere. The formatting is webpack-dev-middleware's `client/problem` now,
and the state machine was standing in for per-source slots that overlay
already has, so the events map onto two calls.
What stays is this package's identity on top of a shared overlay, as options:
the `webpack-dev-server-client-overlay` element id, so anything querying it is
unaffected; the `webpack-dev-server#overlay` Trusted Types policy name, which
a page's CSP allowlists by name; and the `/webpack-dev-server/open-editor`
route that makes a file reference clickable.
`test/client/ReactErrorBoundary.test.js` goes with it — the heuristic it
covers lives in webpack-dev-middleware, which tests it.
Requires webpack/webpack-dev-middleware#2438.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
This server has no client of its own any more. `client-src/` is gone — the runtime, the error overlay, the progress indicator, the logger, the socket wrapper and the WebSocket client, around two thousand lines that existed in near-duplicate in both projects — and the middleware's hot option drives all of it, including putting the entry and `HotModuleReplacementPlugin` into the compilation. Nothing about a configuration changes. Every option keeps its name and its meaning and is mapped onto the middleware's vocabulary in `lib/hotOptions.js`: `hot`/`liveReload` become one `apply` mode, `client.reconnect` becomes `connect.retries`, `client.webSocketURL` is already the shape the middleware's `hot.client.path` takes, and the overlay is passed through with this project's own element id so anything querying for it still finds it. The `webpack-dev-server/client/*` paths are generated re-exports of the middleware's client, so importing them resolves to what it always did. The paths that were only this bundle's internals are no longer published. Protection stays here. `allowedHosts`, the `Origin` checks and the same-origin rule are unchanged: the middleware is handed `cors: true` and no token, which is it applying no policy of its own, and every connection is judged here before anything is published to it. A handshake is refused before it completes rather than after, since the middleware exposes the upgrade rather than taking the server over. A `webSocketServer` of your own still works. A class, a module exporting one, or options giving the socket a port or a server of its own are wrapped into the shape the middleware asks a custom transport for, so there is one path from a build to a page either way, and `BaseServer` keeps its export. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
The adapter this branch added, `lib/servers/EventSourceServer.js`, existed to dress the middleware's hot endpoint up as the transport shape this server was written against. There is no such shape any more — the middleware owns both ends of both transports — so naming `sse` is now just choosing one of its transports, and the adapter and its special case in `normalizeOptions` are gone with it. `client.webSocketTransport: "sse"` and `webSocketServer: "sse"` keep working and keep picking each other, which is what the option values are for. The published `client/clients/EventSourceClient.js` path is generated alongside its WebSocket neighbour, so a transport named by module still resolves. One thing this caught: `"sse"` was being read as an implementation of this server's own, since the test for that was "not `ws`" — which sent it looking for a class to construct and failed before a page could connect. Both of the middleware's transports are named now, and a smoke run over each confirms the endpoint answers: `426` to a plain GET on the WebSocket one, an event stream on the other. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
Two things the browser used to say that the middleware's runtime had no way
to say for it.
The console prefix: every message read `[webpack-dev-server]`, and four
snapshots record exactly that, including `Invalid Host/Origin header` — a line
people search for. Taking the runtime as-is would relabel all of it with a
dependency's name and send anyone with a problem to the wrong repository, so
the name travels with the logging option.
The refusal itself: a client turned away by `allowedHosts` or the origin check
is told why, and the reason is logged in the page. That needs the handshake to
complete, so the upgrade is answered even for a client that will be refused
and `guardHotEndpoint` closes it immediately after saying so — which is what
this server has always done. Owning the `upgrade` listener is still what keeps
a proxied upgrade with whoever it belongs to; `isHotEndpoint` went with the
earlier attempt to refuse before the handshake, which would have made the
reason unreachable.
Verified in a browser: the console carries `[webpack-dev-server]`, and a
client from a disallowed origin receives
`{"action":"error","message":"Invalid Host/Origin header"}` and is closed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
Two proxy cases waited for `{ type: "hot" }`, which was this server telling a
client what to enable right after the handshake. That configuration is in the
client's entry query now, so the first thing the socket says is the build the
page is to run.
What the cases are about is untouched and still asserted: the HMR upgrade must
not reach the proxy target (`backendUpgradeCount` stays `0`), and must not
make the proxy log an error. Both hold.
`test/server/open-option.js` fails 33/33 here and on `main` alike — it drives
a real browser through `open`, which this environment has none of.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
`allowedHosts` and the origin check were not being enforced on the `sse` transport. The guard ran and decided to refuse, and then neither half of the refusal landed: the message went through `sendMessage`, which tests `readyState === 1` and calls `client.send` — a `ServerResponse` has neither, so the loop skipped it — and `client.close()`, which a `ServerResponse` also does not have, threw and was swallowed. The stream stayed open and kept receiving builds. The message goes through the middleware's `publishTo` now, which puts it on the wire the client is actually on, and the connection is dropped the way that client is dropped: a WebSocket closes, a response ends. Verified on both transports: a disallowed origin is told `Invalid Host/Origin header` and dropped, over a WebSocket and over a stream alike — and a same-origin page, which `EventSource` gives no `Origin` at all, is still let through. Found via a CodeRabbit review of the middleware's README example, which had the same two mistakes in it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
a4091d1 to
3c7c3ff
Compare
A `client.webSocketURL` written as a string is normalized by parsing it, which fills in every part — as `""` for the ones the url did not carry. Those were passed straight through to the middleware, which refuses an empty port: so `webSocketURL: "ws://example.test/ws"` stopped the server from starting at all. Empty has always meant "not said" here, and both the old client and the new one resolve such a part from the page, so it is left out rather than sent. The `allowed-hosts` snapshots are regenerated with it. Three changes, all consequences of the runtime moving rather than of anything going wrong: `connected` is logged where this server used to log which features were enabled, `[HMR] Waiting for update signal from WDS...` is gone with the `webpack/hot/dev-server` entry that printed it, and the prefix is still `[webpack-dev-server]`. The suite is 31 of 32 here; the one failure is an IPv6 case, and this container cannot listen on `::1` at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
…types Two things CI's Lint job reports that are not about the dependency. `test/client/clients/EventSourceClient.test.js` was added earlier on this branch and imports `client-src/clients/EventSourceClient.js`, which is gone. It never survived the rebase as a deletion because it did not exist on the side that deleted the rest of `test/client/`. Every case in it tests the middleware's own stream client through a re-export, and the middleware's `test/client-transports.test.js` already covers all six — plus the ones only a stream has — so nothing is lost. `types/` is tracked and the Lint job fails with "Missing types" when a build changes it. `Server.d.ts` still described the removed methods, and the two new modules had no declarations at all. The changeset now says the two public methods that went with them, `getClientEntry()` and `getClientHotEntry()`, are a breaking removal. It recorded the rest of the removal and not that. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
0104f53 to
d05d475
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #5750 +/- ##
==========================================
+ Coverage 90.74% 95.16% +4.42%
==========================================
Files 14 6 -8
Lines 6329 4981 -1348
==========================================
- Hits 5743 4740 -1003
+ Misses 586 241 -345 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
The suite reads the browser's console, which is now the middleware's client
talking. Every difference is one the migration makes on purpose:
- the startup banner, `Server started: Hot Module Replacement enabled, …`,
and `[HMR] Waiting for update signal from WDS...` are gone — the old client
printed the first from the configuration this server pushed after the
handshake, and the second came from the `webpack/hot/dev-server` entry this
server no longer adds;
- `Hey.` comes first, because the page no longer waits on a banner;
- the reconnect cases gain `Trying to reconnect...` and `Disconnected!`.
Regenerated only after reading what kind of failure each of the 34 was: all
snapshot mismatches, none of them an error.
One test here was worse than a mismatch. "should not work and output disconnect
wrong web socket URL" waits for `Disconnected!` with no timeout, and the
middleware's client did not print it, so it spun forever with its server and
browser open and took the whole file to the runner's 400 s limit. That is fixed
in webpack-dev-middleware, which now says so when the connection goes away;
with it the file runs in 53 s and passes twice running.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
…ddleware working - `client/index.js` is now CommonJS and leaves the query written on it where the middleware's client reads it, so `client/index.js?...` entries keep their options instead of connecting with defaults. - Name the client's page-url parameters after this package again, so `?webpack-dev-server-hot=false` and `?webpack-dev-server-live-reload=false` still opt a tab out. - `client: false` no longer throws while reading `client.webSocketTransport`. - A middleware mounted through `setupMiddlewares` has no hot endpoint of this server's, so there is nothing to guard. - Update the e2e expectations for the middleware's client: its messages, the order of `connected` after the page's own output, and a wait that no longer polls forever for a message the client does not log. - Drop the tests of the removed `getClientEntry()`/`getClientHotEntry()`. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
Every difference is one the migration makes on purpose, and the ones that were not are fixed: - Provide the transport through `__webpack_dev_server_client__` again for `ws` and `sse` too, as the global has always held the one in use, and have the `client/` re-exports hand back the default export of the modules that have one. - Point the overlay at this server's open-editor route. - Expect what the middleware's client logs, with the build's timing and absolute paths normalized, and its message for each way an update goes. - Port the tests that reached into the removed WebSocket server: the heartbeat, the client count, the host check on the event stream, and the federation entries, which are now checked on a browser target — a build for node is given no client. - Replace the tests of the old progress element with ones of what `client.progress` promises, the indicator's own being the middleware's. - Add unit tests for the options this server hands the middleware. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
…say what differs The hot option is now visible in the client's entry rather than in a webpack/hot entry, and a build for node is given neither. The changeset lists what a page and its console look like now, which it had called nothing. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
Snapshots regenerated in a sandbox carried its differences with them: - The overlay's style attributes came out with border: none, where the browser CI uses writes the four longhands the committed snapshots have. - A sandbox proxy refuses connections to the addresses the web socket URL tests use with a 403, so those entries recorded a refusal where CI connects or fails to resolve the name. - The IPv6 entry of the allowed hosts test could not run there, so its snapshot was dropped by the update. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
A rebuild's message names the file that changed, and a Windows machine writes that path with backslashes, so the snapshots that record it could only match on a machine that does not. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
How long a machine takes to say a name does not resolve is its own, and the page was idle before the answer came on one runner, so the snapshot was taken of "Hey." alone. The test now waits for the message that ends the sequence it records. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
1d35bb9 to
2821062
Compare
The worker's own client connects after the page has settled, so on a slow runner the snapshot could see one "connected" line instead of two. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
2821062 to
f563566
Compare
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
11ccd25 to
79a9d3f
Compare
dev-server no longer applies a ProvidePlugin or HotModuleReplacementPlugin,
or runs a WebSocket server of its own: `client: false`, a custom
`client.webSocketTransport`, `webSocketServer.options` (a port or server
of its own, verifyClient, compression) and the HMR plugin for every
compilation are webpack-dev-middleware's `hot` options now.
lib/servers/WebsocketServer.js and bridge.js are gone; BaseServer stays
as the contract for a `webSocketServer` implementation of your own.
And what a page or a tool written against 6.0 relies on keeps working:
- `client/socket.js` and this server's own `{ type }` messages, which
React Refresh's overlay reads off the socket
- `webSocketServer.clients` and `.implementation`, `sendMessage()`
(deprecated; `static-changed` reloads), `getClientEntry()` and
`getClientHotEntry()` (deprecated)
- the Trusted Types policy `webpack-dev-server#overlay`, progress off
unless asked, a one second ping, overlay filters handed `{ message }`
- a hand-written `client/index.js` entry connects to `/ws` as before
- a `setupMiddlewares` that replaces the dev middleware (a Hono app)
keeps the hot endpoint
- the editor is opened on the file resolved against webpack's context
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
9da5861 to
960f8e9
Compare
8d66ffe to
09c990b
Compare
…iversal builds working - `getClientEntry()` and `getClientHotEntry()` overridden by a subclass put their modules in `entry` again, the client one with the query this server always wrote, and the middleware then adds no client of its own. - `client/` keeps only the paths something outside this package uses: `index.js`, written into `entry` by hand; `socket.js`, which React Refresh reads; and `clients/WebSocketClient.js`, which a transport of someone else's extends, still with `default` for `require()`. - `client/index.js` hands its query over only where there is a `self`, so a universal build runs in Node without it throwing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
…t scenarios main's tests run as they are wherever they still pass; the rest only record what the middleware's client says and shows, in its console, overlay and progress indicator. A subclass's `getClientEntry()` and `getClientHotEntry()` keep main's test. React Refresh is tested with `@pmmmwh/react-refresh-webpack-plugin` itself: a component keeps its state across one edit and several, a child module updates in place, changed hooks remount in place, its overlay shows a build error and an error thrown while rendering and clears once fixed, an unaccepted edit reloads, `hot: "only"` does not, and all of it over Server-Sent Events too. A universal build is served to a page and run in Node. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
DO NOT MERGE THIS COMMIT. Revert it, and set the dependency back to a released `^8.4.0`, once webpack-dev-middleware ships. This branch needs what the published 8.3.0 does not have: `hot.protocol` with this server's runtime in `client/webpack-dev-server/`, `onConnect` with the request, `handleUpgrade`, `publishTo`, `hot.ws` and the `ws` server handed out as `hot.implementation`. CI installs from the registry, so without this `tsc` fails in the Build step and every job behind it is cancelled. This vendors a pack of webpack-dev-middleware `main` plus webpack/webpack-dev-middleware#2472 (feat/finish-dev-server-migration). The lockfile change is confined to that one dependency. To undo, with the release out: git revert <this commit> npm install webpack-dev-middleware@^8.4.0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA
09c990b to
1ff3dde
Compare
This package no longer has a browser client, an overlay or a socket server of its own: webpack-dev-middleware's are used, and there is only one of each. Everything this server needs to stay compatible with 6.x is here, not in the middleware.
What stays here for compatibility
{ type, data }messagessendStats, theinvalid/donehooks, the connect-time settings) still sendshot,liveReload,invalid,hash,ok/still-ok/warnings/errors,static-changedandprogress-updateover the middleware's socket. The middleware's client ignores them; React Refresh's overlay and anything else listening reads them as before.client/*client/index.js(written intoentryby hand, with this server's query spelling),client/socket.js(React Refresh readsclient.client),client/clients/WebSocketClient.js(what a transport of someone else's extends). Generated re-exports; the client's internal files are gone. TODO to remove them in the next major release.getClientEntry()/getClientHotEntry()entry, the client one with the query this server always wrote, and the middleware adds no client of its own. Deprecated, as aresendMessage()'s callers.allowedHosts,Originand same-origin checkswebSocketServer.*,client.*hotoption inlib/hotOptions.js.What users see differently
The console wording, the overlay and the progress indicator are the middleware's. The overlay keeps this server's element id and Trusted Types policy name, so selectors still find it. Updates are applied by the middleware's client, so a
target: "node"build no longer getswebpack/hot/dev-server, which did nothing there.Also
client.webSocketTransport: "sse"(orwebSocketServer: "sse") adds Server-Sent Events.target: "universal"or["web", "node"]) now runs in Node too:client/index.jshands its query over only where there is aself.Depends on
Tests
getClientEntry()override test is kept.test/e2e/react-refresh.test.js(9):@pmmmwh/react-refresh-webpack-plugin0.6.3 itself with React 19:client/socket, and clears once fixed;hot: "only"it does not;test/e2e/universal-target.test.js(a page, and the bundle run in Node),test/e2e/compatibility.test.js,test/e2e/event-source.test.js,test/hot-options.test.js.borderdifferently), the same set main's suite hits here.tsc, the schema check and the port check are clean.🤖 Generated with Claude Code
https://claude.ai/code/session_01UjuMAuk9o6UazjHzcAQCTA