@heojeongbo/wirestage 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/LICENSES.md +21 -0
- package/README.md +150 -2
- package/dist/app/playwright/index.d.ts +3 -0
- package/dist/app/playwright/index.d.ts.map +1 -0
- package/dist/app/playwright/index.js +2 -0
- package/dist/app/playwright/index.js.map +1 -0
- package/dist/app/playwright/lib/fixture.d.ts +61 -0
- package/dist/app/playwright/lib/fixture.d.ts.map +1 -0
- package/dist/app/playwright/lib/fixture.js +110 -0
- package/dist/app/playwright/lib/fixture.js.map +1 -0
- package/dist/connect.d.ts +7 -0
- package/dist/connect.d.ts.map +1 -0
- package/dist/connect.js +7 -0
- package/dist/connect.js.map +1 -0
- package/dist/entities/frame/@x/plugin.d.ts +9 -0
- package/dist/entities/frame/@x/plugin.d.ts.map +1 -0
- package/dist/entities/frame/@x/plugin.js +2 -0
- package/dist/entities/frame/@x/plugin.js.map +1 -0
- package/dist/entities/frame/index.d.ts +6 -0
- package/dist/entities/frame/index.d.ts.map +1 -0
- package/dist/entities/frame/index.js +4 -0
- package/dist/entities/frame/index.js.map +1 -0
- package/dist/entities/frame/lib/filter.d.ts +8 -0
- package/dist/entities/frame/lib/filter.d.ts.map +1 -0
- package/dist/entities/frame/lib/filter.js +62 -0
- package/dist/entities/frame/lib/filter.js.map +1 -0
- package/dist/entities/frame/lib/payload.d.ts +8 -0
- package/dist/entities/frame/lib/payload.d.ts.map +1 -0
- package/dist/entities/frame/lib/payload.js +42 -0
- package/dist/entities/frame/lib/payload.js.map +1 -0
- package/dist/entities/frame/lib/snapshot.d.ts +15 -0
- package/dist/entities/frame/lib/snapshot.d.ts.map +1 -0
- package/dist/entities/frame/lib/snapshot.js +90 -0
- package/dist/entities/frame/lib/snapshot.js.map +1 -0
- package/dist/entities/frame/lib/timeline.d.ts +23 -0
- package/dist/entities/frame/lib/timeline.d.ts.map +1 -0
- package/dist/entities/frame/lib/timeline.js +122 -0
- package/dist/entities/frame/lib/timeline.js.map +1 -0
- package/dist/entities/frame/model/frame.d.ts +182 -0
- package/dist/entities/frame/model/frame.d.ts.map +1 -0
- package/dist/entities/frame/model/frame.js +2 -0
- package/dist/entities/frame/model/frame.js.map +1 -0
- package/dist/entities/plugin/index.d.ts +9 -0
- package/dist/entities/plugin/index.d.ts.map +1 -0
- package/dist/entities/plugin/index.js +3 -0
- package/dist/entities/plugin/index.js.map +1 -0
- package/dist/entities/plugin/lib/define-plugin.d.ts +8 -0
- package/dist/entities/plugin/lib/define-plugin.d.ts.map +1 -0
- package/dist/entities/plugin/lib/define-plugin.js +9 -0
- package/dist/entities/plugin/lib/define-plugin.js.map +1 -0
- package/dist/entities/plugin/lib/named.d.ts +10 -0
- package/dist/entities/plugin/lib/named.d.ts.map +1 -0
- package/dist/entities/plugin/lib/named.js +17 -0
- package/dist/entities/plugin/lib/named.js.map +1 -0
- package/dist/entities/plugin/model/attachment.d.ts +18 -0
- package/dist/entities/plugin/model/attachment.d.ts.map +1 -0
- package/dist/entities/plugin/model/attachment.js +2 -0
- package/dist/entities/plugin/model/attachment.js.map +1 -0
- package/dist/entities/plugin/model/http.d.ts +135 -0
- package/dist/entities/plugin/model/http.d.ts.map +1 -0
- package/dist/entities/plugin/model/http.js +2 -0
- package/dist/entities/plugin/model/http.js.map +1 -0
- package/dist/entities/plugin/model/page.d.ts +100 -0
- package/dist/entities/plugin/model/page.d.ts.map +1 -0
- package/dist/entities/plugin/model/page.js +2 -0
- package/dist/entities/plugin/model/page.js.map +1 -0
- package/dist/entities/plugin/model/plugin.d.ts +83 -0
- package/dist/entities/plugin/model/plugin.d.ts.map +1 -0
- package/dist/entities/plugin/model/plugin.js +2 -0
- package/dist/entities/plugin/model/plugin.js.map +1 -0
- package/dist/entities/plugin/model/socket.d.ts +107 -0
- package/dist/entities/plugin/model/socket.d.ts.map +1 -0
- package/dist/entities/plugin/model/socket.js +2 -0
- package/dist/entities/plugin/model/socket.js.map +1 -0
- package/dist/entities/plugin/model/stream.d.ts +46 -0
- package/dist/entities/plugin/model/stream.d.ts.map +1 -0
- package/dist/entities/plugin/model/stream.js +2 -0
- package/dist/entities/plugin/model/stream.js.map +1 -0
- package/dist/features/connect/index.d.ts +5 -0
- package/dist/features/connect/index.d.ts.map +1 -0
- package/dist/features/connect/index.js +4 -0
- package/dist/features/connect/index.js.map +1 -0
- package/dist/features/connect/lib/calls.d.ts +9 -0
- package/dist/features/connect/lib/calls.d.ts.map +1 -0
- package/dist/features/connect/lib/calls.js +46 -0
- package/dist/features/connect/lib/calls.js.map +1 -0
- package/dist/features/connect/lib/client.d.ts +33 -0
- package/dist/features/connect/lib/client.d.ts.map +1 -0
- package/dist/features/connect/lib/client.js +147 -0
- package/dist/features/connect/lib/client.js.map +1 -0
- package/dist/features/connect/lib/create-connect.d.ts +14 -0
- package/dist/features/connect/lib/create-connect.d.ts.map +1 -0
- package/dist/features/connect/lib/create-connect.js +124 -0
- package/dist/features/connect/lib/create-connect.js.map +1 -0
- package/dist/features/connect/lib/faults.d.ts +18 -0
- package/dist/features/connect/lib/faults.d.ts.map +1 -0
- package/dist/features/connect/lib/faults.js +85 -0
- package/dist/features/connect/lib/faults.js.map +1 -0
- package/dist/features/connect/lib/protocol.d.ts +42 -0
- package/dist/features/connect/lib/protocol.d.ts.map +1 -0
- package/dist/features/connect/lib/protocol.js +228 -0
- package/dist/features/connect/lib/protocol.js.map +1 -0
- package/dist/features/connect/lib/record.d.ts +14 -0
- package/dist/features/connect/lib/record.d.ts.map +1 -0
- package/dist/features/connect/lib/record.js +36 -0
- package/dist/features/connect/lib/record.js.map +1 -0
- package/dist/features/connect/lib/recorder.d.ts +77 -0
- package/dist/features/connect/lib/recorder.d.ts.map +1 -0
- package/dist/features/connect/lib/recorder.js +209 -0
- package/dist/features/connect/lib/recorder.js.map +1 -0
- package/dist/features/connect/lib/rules.d.ts +51 -0
- package/dist/features/connect/lib/rules.d.ts.map +1 -0
- package/dist/features/connect/lib/rules.js +222 -0
- package/dist/features/connect/lib/rules.js.map +1 -0
- package/dist/features/connect/lib/serve.d.ts +10 -0
- package/dist/features/connect/lib/serve.d.ts.map +1 -0
- package/dist/features/connect/lib/serve.js +78 -0
- package/dist/features/connect/lib/serve.js.map +1 -0
- package/dist/features/connect/lib/status-of.d.ts +11 -0
- package/dist/features/connect/lib/status-of.d.ts.map +1 -0
- package/dist/features/connect/lib/status-of.js +22 -0
- package/dist/features/connect/lib/status-of.js.map +1 -0
- package/dist/features/connect/lib/stubs.d.ts +18 -0
- package/dist/features/connect/lib/stubs.d.ts.map +1 -0
- package/dist/features/connect/lib/stubs.js +174 -0
- package/dist/features/connect/lib/stubs.js.map +1 -0
- package/dist/features/connect/lib/universal.d.ts +19 -0
- package/dist/features/connect/lib/universal.d.ts.map +1 -0
- package/dist/features/connect/lib/universal.js +71 -0
- package/dist/features/connect/lib/universal.js.map +1 -0
- package/dist/features/connect/model/types.d.ts +247 -0
- package/dist/features/connect/model/types.d.ts.map +1 -0
- package/dist/features/connect/model/types.js +2 -0
- package/dist/features/connect/model/types.js.map +1 -0
- package/dist/features/rest/index.d.ts +5 -0
- package/dist/features/rest/index.d.ts.map +1 -0
- package/dist/features/rest/index.js +3 -0
- package/dist/features/rest/index.js.map +1 -0
- package/dist/features/rest/lib/calls.d.ts +8 -0
- package/dist/features/rest/lib/calls.d.ts.map +1 -0
- package/dist/features/rest/lib/calls.js +32 -0
- package/dist/features/rest/lib/calls.js.map +1 -0
- package/dist/features/rest/lib/create-rest.d.ts +8 -0
- package/dist/features/rest/lib/create-rest.d.ts.map +1 -0
- package/dist/features/rest/lib/create-rest.js +136 -0
- package/dist/features/rest/lib/create-rest.js.map +1 -0
- package/dist/features/rest/lib/path.d.ts +14 -0
- package/dist/features/rest/lib/path.d.ts.map +1 -0
- package/dist/features/rest/lib/path.js +58 -0
- package/dist/features/rest/lib/path.js.map +1 -0
- package/dist/features/rest/lib/reply.d.ts +25 -0
- package/dist/features/rest/lib/reply.d.ts.map +1 -0
- package/dist/features/rest/lib/reply.js +40 -0
- package/dist/features/rest/lib/reply.js.map +1 -0
- package/dist/features/rest/model/types.d.ts +74 -0
- package/dist/features/rest/model/types.d.ts.map +1 -0
- package/dist/features/rest/model/types.js +2 -0
- package/dist/features/rest/model/types.js.map +1 -0
- package/dist/features/sse/index.d.ts +4 -0
- package/dist/features/sse/index.d.ts.map +1 -0
- package/dist/features/sse/index.js +3 -0
- package/dist/features/sse/index.js.map +1 -0
- package/dist/features/sse/lib/create-sse.d.ts +9 -0
- package/dist/features/sse/lib/create-sse.d.ts.map +1 -0
- package/dist/features/sse/lib/create-sse.js +121 -0
- package/dist/features/sse/lib/create-sse.js.map +1 -0
- package/dist/features/sse/lib/encode.d.ts +8 -0
- package/dist/features/sse/lib/encode.d.ts.map +1 -0
- package/dist/features/sse/lib/encode.js +29 -0
- package/dist/features/sse/lib/encode.js.map +1 -0
- package/dist/features/sse/model/types.d.ts +68 -0
- package/dist/features/sse/model/types.d.ts.map +1 -0
- package/dist/features/sse/model/types.js +2 -0
- package/dist/features/sse/model/types.js.map +1 -0
- package/dist/features/webrtc/index.d.ts +3 -0
- package/dist/features/webrtc/index.d.ts.map +1 -0
- package/dist/features/webrtc/index.js +2 -0
- package/dist/features/webrtc/index.js.map +1 -0
- package/dist/features/webrtc/lib/create-webrtc.d.ts +9 -0
- package/dist/features/webrtc/lib/create-webrtc.d.ts.map +1 -0
- package/dist/features/webrtc/lib/create-webrtc.js +123 -0
- package/dist/features/webrtc/lib/create-webrtc.js.map +1 -0
- package/dist/features/webrtc/lib/rtc-page-module.d.ts +22 -0
- package/dist/features/webrtc/lib/rtc-page-module.d.ts.map +1 -0
- package/dist/features/webrtc/lib/rtc-page-module.js +161 -0
- package/dist/features/webrtc/lib/rtc-page-module.js.map +1 -0
- package/dist/features/webrtc/lib/to-frame.d.ts +9 -0
- package/dist/features/webrtc/lib/to-frame.d.ts.map +1 -0
- package/dist/features/webrtc/lib/to-frame.js +43 -0
- package/dist/features/webrtc/lib/to-frame.js.map +1 -0
- package/dist/features/webrtc/model/types.d.ts +70 -0
- package/dist/features/webrtc/model/types.d.ts.map +1 -0
- package/dist/features/webrtc/model/types.js +2 -0
- package/dist/features/webrtc/model/types.js.map +1 -0
- package/dist/features/websocket/index.d.ts +3 -0
- package/dist/features/websocket/index.d.ts.map +1 -0
- package/dist/features/websocket/index.js +2 -0
- package/dist/features/websocket/index.js.map +1 -0
- package/dist/features/websocket/lib/create-websocket.d.ts +9 -0
- package/dist/features/websocket/lib/create-websocket.d.ts.map +1 -0
- package/dist/features/websocket/lib/create-websocket.js +34 -0
- package/dist/features/websocket/lib/create-websocket.js.map +1 -0
- package/dist/features/websocket/lib/mock.d.ts +6 -0
- package/dist/features/websocket/lib/mock.d.ts.map +1 -0
- package/dist/features/websocket/lib/mock.js +96 -0
- package/dist/features/websocket/lib/mock.js.map +1 -0
- package/dist/features/websocket/lib/proxy.d.ts +9 -0
- package/dist/features/websocket/lib/proxy.d.ts.map +1 -0
- package/dist/features/websocket/lib/proxy.js +120 -0
- package/dist/features/websocket/lib/proxy.js.map +1 -0
- package/dist/features/websocket/lib/wire.d.ts +16 -0
- package/dist/features/websocket/lib/wire.d.ts.map +1 -0
- package/dist/features/websocket/lib/wire.js +46 -0
- package/dist/features/websocket/lib/wire.js.map +1 -0
- package/dist/features/websocket/model/types.d.ts +133 -0
- package/dist/features/websocket/model/types.d.ts.map +1 -0
- package/dist/features/websocket/model/types.js +2 -0
- package/dist/features/websocket/model/types.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/playwright.d.ts +4 -0
- package/dist/playwright.d.ts.map +1 -0
- package/dist/playwright.js +2 -0
- package/dist/playwright.js.map +1 -0
- package/dist/rest.d.ts +3 -0
- package/dist/rest.d.ts.map +1 -0
- package/dist/rest.js +3 -0
- package/dist/rest.js.map +1 -0
- package/dist/shared/api/http-server/index.d.ts +3 -0
- package/dist/shared/api/http-server/index.d.ts.map +1 -0
- package/dist/shared/api/http-server/index.js +2 -0
- package/dist/shared/api/http-server/index.js.map +1 -0
- package/dist/shared/api/http-server/listen.d.ts +25 -0
- package/dist/shared/api/http-server/listen.d.ts.map +1 -0
- package/dist/shared/api/http-server/listen.js +44 -0
- package/dist/shared/api/http-server/listen.js.map +1 -0
- package/dist/shared/api/playwright/index.d.ts +2 -0
- package/dist/shared/api/playwright/index.d.ts.map +1 -0
- package/dist/shared/api/playwright/index.js +2 -0
- package/dist/shared/api/playwright/index.js.map +1 -0
- package/dist/shared/api/playwright/ports.d.ts +126 -0
- package/dist/shared/api/playwright/ports.d.ts.map +1 -0
- package/dist/shared/api/playwright/ports.js +11 -0
- package/dist/shared/api/playwright/ports.js.map +1 -0
- package/dist/shared/api/stream-server/index.d.ts +3 -0
- package/dist/shared/api/stream-server/index.d.ts.map +1 -0
- package/dist/shared/api/stream-server/index.js +2 -0
- package/dist/shared/api/stream-server/index.js.map +1 -0
- package/dist/shared/api/stream-server/stream-server.d.ts +45 -0
- package/dist/shared/api/stream-server/stream-server.d.ts.map +1 -0
- package/dist/shared/api/stream-server/stream-server.js +167 -0
- package/dist/shared/api/stream-server/stream-server.js.map +1 -0
- package/dist/shared/lib/async/arrivals.d.ts +31 -0
- package/dist/shared/lib/async/arrivals.d.ts.map +1 -0
- package/dist/shared/lib/async/arrivals.js +57 -0
- package/dist/shared/lib/async/arrivals.js.map +1 -0
- package/dist/shared/lib/async/async-queue.d.ts +23 -0
- package/dist/shared/lib/async/async-queue.d.ts.map +1 -0
- package/dist/shared/lib/async/async-queue.js +80 -0
- package/dist/shared/lib/async/async-queue.js.map +1 -0
- package/dist/shared/lib/async/deferred.d.ts +12 -0
- package/dist/shared/lib/async/deferred.d.ts.map +1 -0
- package/dist/shared/lib/async/deferred.js +34 -0
- package/dist/shared/lib/async/deferred.js.map +1 -0
- package/dist/shared/lib/async/index.d.ts +8 -0
- package/dist/shared/lib/async/index.d.ts.map +1 -0
- package/dist/shared/lib/async/index.js +5 -0
- package/dist/shared/lib/async/index.js.map +1 -0
- package/dist/shared/lib/async/run-all.d.ts +8 -0
- package/dist/shared/lib/async/run-all.d.ts.map +1 -0
- package/dist/shared/lib/async/run-all.js +22 -0
- package/dist/shared/lib/async/run-all.js.map +1 -0
- package/dist/shared/lib/bytes/bytes.d.ts +18 -0
- package/dist/shared/lib/bytes/bytes.d.ts.map +1 -0
- package/dist/shared/lib/bytes/bytes.js +48 -0
- package/dist/shared/lib/bytes/bytes.js.map +1 -0
- package/dist/shared/lib/bytes/index.d.ts +3 -0
- package/dist/shared/lib/bytes/index.d.ts.map +1 -0
- package/dist/shared/lib/bytes/index.js +2 -0
- package/dist/shared/lib/bytes/index.js.map +1 -0
- package/dist/shared/lib/clock/clock.d.ts +29 -0
- package/dist/shared/lib/clock/clock.d.ts.map +1 -0
- package/dist/shared/lib/clock/clock.js +85 -0
- package/dist/shared/lib/clock/clock.js.map +1 -0
- package/dist/shared/lib/clock/index.d.ts +3 -0
- package/dist/shared/lib/clock/index.d.ts.map +1 -0
- package/dist/shared/lib/clock/index.js +2 -0
- package/dist/shared/lib/clock/index.js.map +1 -0
- package/dist/shared/lib/cookies/index.d.ts +3 -0
- package/dist/shared/lib/cookies/index.d.ts.map +1 -0
- package/dist/shared/lib/cookies/index.js +2 -0
- package/dist/shared/lib/cookies/index.js.map +1 -0
- package/dist/shared/lib/cookies/set-cookie.d.ts +22 -0
- package/dist/shared/lib/cookies/set-cookie.d.ts.map +1 -0
- package/dist/shared/lib/cookies/set-cookie.js +89 -0
- package/dist/shared/lib/cookies/set-cookie.js.map +1 -0
- package/dist/shared/lib/envelope/envelope.d.ts +50 -0
- package/dist/shared/lib/envelope/envelope.d.ts.map +1 -0
- package/dist/shared/lib/envelope/envelope.js +111 -0
- package/dist/shared/lib/envelope/envelope.js.map +1 -0
- package/dist/shared/lib/envelope/index.d.ts +3 -0
- package/dist/shared/lib/envelope/index.d.ts.map +1 -0
- package/dist/shared/lib/envelope/index.js +2 -0
- package/dist/shared/lib/envelope/index.js.map +1 -0
- package/dist/shared/lib/errors/index.d.ts +3 -0
- package/dist/shared/lib/errors/index.d.ts.map +1 -0
- package/dist/shared/lib/errors/index.js +2 -0
- package/dist/shared/lib/errors/index.js.map +1 -0
- package/dist/shared/lib/errors/wirestage-error.d.ts +28 -0
- package/dist/shared/lib/errors/wirestage-error.d.ts.map +1 -0
- package/dist/shared/lib/errors/wirestage-error.js +16 -0
- package/dist/shared/lib/errors/wirestage-error.js.map +1 -0
- package/dist/shared/lib/id/id.d.ts +10 -0
- package/dist/shared/lib/id/id.d.ts.map +1 -0
- package/dist/shared/lib/id/id.js +26 -0
- package/dist/shared/lib/id/id.js.map +1 -0
- package/dist/shared/lib/id/index.d.ts +2 -0
- package/dist/shared/lib/id/index.d.ts.map +1 -0
- package/dist/shared/lib/id/index.js +2 -0
- package/dist/shared/lib/id/index.js.map +1 -0
- package/dist/shared/lib/logger/index.d.ts +3 -0
- package/dist/shared/lib/logger/index.d.ts.map +1 -0
- package/dist/shared/lib/logger/index.js +2 -0
- package/dist/shared/lib/logger/index.js.map +1 -0
- package/dist/shared/lib/logger/logger.d.ts +46 -0
- package/dist/shared/lib/logger/logger.d.ts.map +1 -0
- package/dist/shared/lib/logger/logger.js +67 -0
- package/dist/shared/lib/logger/logger.js.map +1 -0
- package/dist/shared/lib/match/index.d.ts +3 -0
- package/dist/shared/lib/match/index.d.ts.map +1 -0
- package/dist/shared/lib/match/index.js +2 -0
- package/dist/shared/lib/match/index.js.map +1 -0
- package/dist/shared/lib/match/url-matcher.d.ts +27 -0
- package/dist/shared/lib/match/url-matcher.d.ts.map +1 -0
- package/dist/shared/lib/match/url-matcher.js +88 -0
- package/dist/shared/lib/match/url-matcher.js.map +1 -0
- package/dist/sse.d.ts +3 -0
- package/dist/sse.d.ts.map +1 -0
- package/dist/sse.js +3 -0
- package/dist/sse.js.map +1 -0
- package/dist/webrtc.d.ts +3 -0
- package/dist/webrtc.d.ts.map +1 -0
- package/dist/webrtc.js +3 -0
- package/dist/webrtc.js.map +1 -0
- package/dist/websocket.d.ts +3 -0
- package/dist/websocket.d.ts.map +1 -0
- package/dist/websocket.js +3 -0
- package/dist/websocket.js.map +1 -0
- package/dist/widgets/stage/index.d.ts +3 -0
- package/dist/widgets/stage/index.d.ts.map +1 -0
- package/dist/widgets/stage/index.js +2 -0
- package/dist/widgets/stage/index.js.map +1 -0
- package/dist/widgets/stage/lib/attached-context.d.ts +10 -0
- package/dist/widgets/stage/lib/attached-context.d.ts.map +1 -0
- package/dist/widgets/stage/lib/attached-context.js +2 -0
- package/dist/widgets/stage/lib/attached-context.js.map +1 -0
- package/dist/widgets/stage/lib/create-stage.d.ts +12 -0
- package/dist/widgets/stage/lib/create-stage.d.ts.map +1 -0
- package/dist/widgets/stage/lib/create-stage.js +250 -0
- package/dist/widgets/stage/lib/create-stage.js.map +1 -0
- package/dist/widgets/stage/lib/http-router.d.ts +44 -0
- package/dist/widgets/stage/lib/http-router.d.ts.map +1 -0
- package/dist/widgets/stage/lib/http-router.js +696 -0
- package/dist/widgets/stage/lib/http-router.js.map +1 -0
- package/dist/widgets/stage/lib/page-host.d.ts +36 -0
- package/dist/widgets/stage/lib/page-host.d.ts.map +1 -0
- package/dist/widgets/stage/lib/page-host.js +240 -0
- package/dist/widgets/stage/lib/page-host.js.map +1 -0
- package/dist/widgets/stage/lib/page-runtime.d.ts +93 -0
- package/dist/widgets/stage/lib/page-runtime.d.ts.map +1 -0
- package/dist/widgets/stage/lib/page-runtime.js +247 -0
- package/dist/widgets/stage/lib/page-runtime.js.map +1 -0
- package/dist/widgets/stage/lib/reads.d.ts +21 -0
- package/dist/widgets/stage/lib/reads.d.ts.map +1 -0
- package/dist/widgets/stage/lib/reads.js +97 -0
- package/dist/widgets/stage/lib/reads.js.map +1 -0
- package/dist/widgets/stage/lib/response-stream.d.ts +23 -0
- package/dist/widgets/stage/lib/response-stream.d.ts.map +1 -0
- package/dist/widgets/stage/lib/response-stream.js +108 -0
- package/dist/widgets/stage/lib/response-stream.js.map +1 -0
- package/dist/widgets/stage/lib/socket-router.d.ts +25 -0
- package/dist/widgets/stage/lib/socket-router.d.ts.map +1 -0
- package/dist/widgets/stage/lib/socket-router.js +233 -0
- package/dist/widgets/stage/lib/socket-router.js.map +1 -0
- package/dist/widgets/stage/model/stage.d.ts +88 -0
- package/dist/widgets/stage/model/stage.d.ts.map +1 -0
- package/dist/widgets/stage/model/stage.js +2 -0
- package/dist/widgets/stage/model/stage.js.map +1 -0
- package/package.json +140 -5
- package/src/app/playwright/index.ts +7 -0
- package/src/app/playwright/lib/fixture.ts +201 -0
- package/src/connect.ts +6 -0
- package/src/entities/frame/@x/plugin.ts +8 -0
- package/src/entities/frame/index.ts +19 -0
- package/src/entities/frame/lib/filter.ts +58 -0
- package/src/entities/frame/lib/payload.ts +42 -0
- package/src/entities/frame/lib/snapshot.ts +105 -0
- package/src/entities/frame/lib/timeline.ts +147 -0
- package/src/entities/frame/model/frame.ts +191 -0
- package/src/entities/plugin/index.ts +36 -0
- package/src/entities/plugin/lib/define-plugin.ts +12 -0
- package/src/entities/plugin/lib/named.ts +21 -0
- package/src/entities/plugin/model/attachment.ts +18 -0
- package/src/entities/plugin/model/http.ts +156 -0
- package/src/entities/plugin/model/page.ts +110 -0
- package/src/entities/plugin/model/plugin.ts +96 -0
- package/src/entities/plugin/model/socket.ts +113 -0
- package/src/entities/plugin/model/stream.ts +46 -0
- package/src/features/connect/index.ts +23 -0
- package/src/features/connect/lib/calls.ts +58 -0
- package/src/features/connect/lib/client.ts +203 -0
- package/src/features/connect/lib/create-connect.ts +164 -0
- package/src/features/connect/lib/faults.ts +132 -0
- package/src/features/connect/lib/protocol.ts +321 -0
- package/src/features/connect/lib/record.ts +48 -0
- package/src/features/connect/lib/recorder.ts +306 -0
- package/src/features/connect/lib/rules.ts +385 -0
- package/src/features/connect/lib/serve.ts +114 -0
- package/src/features/connect/lib/status-of.ts +20 -0
- package/src/features/connect/lib/stubs.ts +282 -0
- package/src/features/connect/lib/universal.ts +100 -0
- package/src/features/connect/model/types.ts +305 -0
- package/src/features/rest/index.ts +23 -0
- package/src/features/rest/lib/calls.ts +31 -0
- package/src/features/rest/lib/create-rest.ts +169 -0
- package/src/features/rest/lib/path.ts +70 -0
- package/src/features/rest/lib/reply.ts +74 -0
- package/src/features/rest/model/types.ts +113 -0
- package/src/features/sse/index.ts +10 -0
- package/src/features/sse/lib/create-sse.ts +129 -0
- package/src/features/sse/lib/encode.ts +28 -0
- package/src/features/sse/model/types.ts +72 -0
- package/src/features/webrtc/index.ts +6 -0
- package/src/features/webrtc/lib/create-webrtc.ts +149 -0
- package/src/features/webrtc/lib/rtc-page-module.ts +189 -0
- package/src/features/webrtc/lib/to-frame.ts +63 -0
- package/src/features/webrtc/model/types.ts +80 -0
- package/src/features/websocket/index.ts +15 -0
- package/src/features/websocket/lib/create-websocket.ts +41 -0
- package/src/features/websocket/lib/mock.ts +114 -0
- package/src/features/websocket/lib/proxy.ts +153 -0
- package/src/features/websocket/lib/wire.ts +78 -0
- package/src/features/websocket/model/types.ts +134 -0
- package/src/index.ts +111 -0
- package/src/playwright.ts +8 -0
- package/src/rest.ts +2 -0
- package/src/shared/api/http-server/index.ts +2 -0
- package/src/shared/api/http-server/listen.ts +71 -0
- package/src/shared/api/playwright/index.ts +17 -0
- package/src/shared/api/playwright/ports.ts +150 -0
- package/src/shared/api/stream-server/index.ts +7 -0
- package/src/shared/api/stream-server/stream-server.ts +254 -0
- package/src/shared/lib/async/arrivals.ts +91 -0
- package/src/shared/lib/async/async-queue.ts +101 -0
- package/src/shared/lib/async/deferred.ts +38 -0
- package/src/shared/lib/async/index.ts +11 -0
- package/src/shared/lib/async/run-all.ts +19 -0
- package/src/shared/lib/bytes/bytes.ts +53 -0
- package/src/shared/lib/bytes/index.ts +9 -0
- package/src/shared/lib/clock/clock.ts +114 -0
- package/src/shared/lib/clock/index.ts +2 -0
- package/src/shared/lib/cookies/index.ts +2 -0
- package/src/shared/lib/cookies/set-cookie.ts +105 -0
- package/src/shared/lib/envelope/envelope.ts +133 -0
- package/src/shared/lib/envelope/index.ts +9 -0
- package/src/shared/lib/errors/index.ts +5 -0
- package/src/shared/lib/errors/wirestage-error.ts +50 -0
- package/src/shared/lib/id/id.ts +29 -0
- package/src/shared/lib/id/index.ts +1 -0
- package/src/shared/lib/logger/index.ts +12 -0
- package/src/shared/lib/logger/logger.ts +126 -0
- package/src/shared/lib/match/index.ts +6 -0
- package/src/shared/lib/match/url-matcher.ts +107 -0
- package/src/sse.ts +2 -0
- package/src/webrtc.ts +2 -0
- package/src/websocket.ts +2 -0
- package/src/widgets/stage/index.ts +9 -0
- package/src/widgets/stage/lib/attached-context.ts +10 -0
- package/src/widgets/stage/lib/create-stage.ts +307 -0
- package/src/widgets/stage/lib/http-router.ts +954 -0
- package/src/widgets/stage/lib/page-host.ts +375 -0
- package/src/widgets/stage/lib/page-runtime.ts +314 -0
- package/src/widgets/stage/lib/reads.ts +128 -0
- package/src/widgets/stage/lib/response-stream.ts +139 -0
- package/src/widgets/stage/lib/socket-router.ts +300 -0
- package/src/widgets/stage/model/stage.ts +110 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Direction,
|
|
3
|
+
FrameInit,
|
|
4
|
+
FrameKind,
|
|
5
|
+
FrameSource,
|
|
6
|
+
} from "../../../entities/frame/index.js";
|
|
7
|
+
import type { PageEntry, PageSource } from "../../../entities/plugin/index.js";
|
|
8
|
+
import { fromBase64 } from "../../../shared/lib/bytes/index.js";
|
|
9
|
+
|
|
10
|
+
interface Shape {
|
|
11
|
+
readonly kind: FrameKind;
|
|
12
|
+
readonly direction: Direction;
|
|
13
|
+
/** Who produced it: the app, the peer, or the stub that dropped it. */
|
|
14
|
+
readonly source: FrameSource;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Page entry types, as the page module emits them. */
|
|
18
|
+
const SHAPES: Readonly<Record<string, Shape>> = {
|
|
19
|
+
out: { kind: "message", direction: "out", source: "page" },
|
|
20
|
+
in: { kind: "message", direction: "in", source: "network" },
|
|
21
|
+
open: { kind: "lifecycle", direction: "none", source: "page" },
|
|
22
|
+
close: { kind: "lifecycle", direction: "none", source: "page" },
|
|
23
|
+
suppressed: { kind: "fault", direction: "in", source: "stub" },
|
|
24
|
+
blocked: { kind: "fault", direction: "out", source: "stub" },
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/** An entry type nobody declared: the page runtime's own `error`. */
|
|
28
|
+
const BROKEN: Shape = { kind: "fault", direction: "none", source: "page" };
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* One page entry as a frame. A lifecycle frame says which `event` it was, a
|
|
32
|
+
* fault says its `reason`, as every protocol's do, and an unknown type (the
|
|
33
|
+
* runtime's own `error`) still lands as a fault instead of vanishing.
|
|
34
|
+
*/
|
|
35
|
+
export function toFrame(
|
|
36
|
+
protocol: string,
|
|
37
|
+
entry: PageEntry,
|
|
38
|
+
origin: PageSource,
|
|
39
|
+
): FrameInit {
|
|
40
|
+
const shape = SHAPES[entry.type] ?? BROKEN;
|
|
41
|
+
const label = entry.meta?.["label"];
|
|
42
|
+
return {
|
|
43
|
+
protocol,
|
|
44
|
+
kind: shape.kind,
|
|
45
|
+
direction: shape.direction,
|
|
46
|
+
endpoint: typeof label === "string" ? label : "",
|
|
47
|
+
// A message the test injected is the test's, not the peer's.
|
|
48
|
+
source: entry.meta?.["injected"] === true ? "test" : shape.source,
|
|
49
|
+
at: entry.at,
|
|
50
|
+
...(entry.data === undefined
|
|
51
|
+
? entry.text === undefined
|
|
52
|
+
? {}
|
|
53
|
+
: { data: entry.text }
|
|
54
|
+
: { data: fromBase64(entry.data) }),
|
|
55
|
+
meta: {
|
|
56
|
+
...entry.meta,
|
|
57
|
+
...(shape.kind === "lifecycle" ? { event: entry.type } : {}),
|
|
58
|
+
...(shape.kind === "fault" ? { reason: entry.type } : {}),
|
|
59
|
+
},
|
|
60
|
+
page: origin.page,
|
|
61
|
+
context: origin.context,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { Frame, FrameFilter } from "../../../entities/frame/index.js";
|
|
2
|
+
import type { PagePort } from "../../../shared/api/playwright/index.js";
|
|
3
|
+
|
|
4
|
+
export interface WebRtcOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Data channel labels to record. Every channel when omitted. Recording a
|
|
7
|
+
* 60 Hz channel nobody asserts on costs a base64 copy per frame.
|
|
8
|
+
*/
|
|
9
|
+
readonly labels?: readonly string[];
|
|
10
|
+
/** Directions to record. Both by default. */
|
|
11
|
+
readonly record?: readonly ("in" | "out")[];
|
|
12
|
+
/** Record channel open/close as lifecycle frames. Default true. */
|
|
13
|
+
readonly lifecycle?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type RtcDirection = "in" | "out" | "both";
|
|
17
|
+
|
|
18
|
+
export interface WebRtcApi {
|
|
19
|
+
/** Every WebRTC frame (messages, lifecycle, faults), narrowed by `filter`. */
|
|
20
|
+
frames(filter?: Omit<FrameFilter, "protocol">): Promise<Frame[]>;
|
|
21
|
+
/** Messages on one channel, oldest first. */
|
|
22
|
+
messages(
|
|
23
|
+
label: string,
|
|
24
|
+
options?: { readonly direction?: "in" | "out" },
|
|
25
|
+
): Promise<Frame[]>;
|
|
26
|
+
/**
|
|
27
|
+
* Drop messages on `label` while the channel stays open — the lossy-link
|
|
28
|
+
* case no server-side knob can produce, because the server is behaving.
|
|
29
|
+
* `in` (the default) swallows what the peer sends the page; `out` swallows
|
|
30
|
+
* what the page sends. Injected messages still get through, so a test can
|
|
31
|
+
* silence the real peer and speak in its place.
|
|
32
|
+
*
|
|
33
|
+
* Returns a function that undoes exactly this call: the label stays
|
|
34
|
+
* suppressed for as long as another call on it is still in force.
|
|
35
|
+
*/
|
|
36
|
+
suppress(
|
|
37
|
+
label: string,
|
|
38
|
+
options?: { readonly direction?: RtcDirection },
|
|
39
|
+
): Promise<() => Promise<void>>;
|
|
40
|
+
/** Undo every suppression on `label`, or on every label. */
|
|
41
|
+
restore(label?: string): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Deliver a message to the page as if the remote peer sent it, on every
|
|
44
|
+
* open channel with this label (or one page's). Returns how many channels
|
|
45
|
+
* received it.
|
|
46
|
+
*/
|
|
47
|
+
inject(
|
|
48
|
+
label: string,
|
|
49
|
+
data: Uint8Array | string,
|
|
50
|
+
options?: { readonly page?: PagePort },
|
|
51
|
+
): Promise<number>;
|
|
52
|
+
/**
|
|
53
|
+
* Resolve with the channel's open frame. Needs `lifecycle` recording, and
|
|
54
|
+
* says so at once when it is off.
|
|
55
|
+
*/
|
|
56
|
+
waitForChannel(
|
|
57
|
+
label: string,
|
|
58
|
+
options?: { readonly timeout?: number },
|
|
59
|
+
): Promise<Frame>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** The page module's fixed configuration. */
|
|
63
|
+
export interface RtcPageConfig {
|
|
64
|
+
readonly labels: readonly string[] | null;
|
|
65
|
+
readonly recordIn: boolean;
|
|
66
|
+
readonly recordOut: boolean;
|
|
67
|
+
readonly lifecycle: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** The page module's live state, pushed to every page on change. */
|
|
71
|
+
export interface RtcPageState {
|
|
72
|
+
readonly suppressIn: readonly string[];
|
|
73
|
+
readonly blockOut: readonly string[];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface RtcInjectArgs {
|
|
77
|
+
readonly label: string;
|
|
78
|
+
readonly data?: string;
|
|
79
|
+
readonly text?: string;
|
|
80
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { websocket } from "./lib/create-websocket.js";
|
|
2
|
+
export type {
|
|
3
|
+
WebSocketApi,
|
|
4
|
+
WebSocketOptions,
|
|
5
|
+
WsCloseInfo,
|
|
6
|
+
WsCloseOptions,
|
|
7
|
+
WsConnection,
|
|
8
|
+
WsData,
|
|
9
|
+
WsMessage,
|
|
10
|
+
WsMock,
|
|
11
|
+
WsMockOptions,
|
|
12
|
+
WsProxy,
|
|
13
|
+
WsProxyConnection,
|
|
14
|
+
WsProxyOptions,
|
|
15
|
+
} from "./model/types.js";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
definePlugin,
|
|
3
|
+
type WirePlugin,
|
|
4
|
+
} from "../../../entities/plugin/index.js";
|
|
5
|
+
import type { WebSocketApi, WebSocketOptions } from "../model/types.js";
|
|
6
|
+
import { createMock } from "./mock.js";
|
|
7
|
+
import { createProxy } from "./proxy.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* WebSockets: fake servers driven from the test, and pass-through proxies
|
|
11
|
+
* that record and can drop messages. Both are rules on the stage's socket
|
|
12
|
+
* router, which owns the browser's side of it.
|
|
13
|
+
*/
|
|
14
|
+
export function websocket(
|
|
15
|
+
options: WebSocketOptions = {},
|
|
16
|
+
): WirePlugin<"websocket", WebSocketApi> {
|
|
17
|
+
return definePlugin({
|
|
18
|
+
name: "websocket",
|
|
19
|
+
scope: "Ws",
|
|
20
|
+
setup(context) {
|
|
21
|
+
const { name } = context;
|
|
22
|
+
if (!options.lazy) context.sockets.prepare();
|
|
23
|
+
const api: WebSocketApi = {
|
|
24
|
+
mock: (url, mockOptions = {}) =>
|
|
25
|
+
createMock(context, name, url, mockOptions),
|
|
26
|
+
proxy: (url, proxyOptions = {}) =>
|
|
27
|
+
createProxy(context, name, url, proxyOptions),
|
|
28
|
+
async frames(filter = {}) {
|
|
29
|
+
await context.sync();
|
|
30
|
+
return context.timeline.frames({
|
|
31
|
+
protocol: name,
|
|
32
|
+
...(filter.endpoint === undefined
|
|
33
|
+
? {}
|
|
34
|
+
: { endpoint: filter.endpoint }),
|
|
35
|
+
});
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
return { api };
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { PluginContext } from "../../../entities/plugin/index.js";
|
|
2
|
+
import {
|
|
3
|
+
createArrivals,
|
|
4
|
+
createDeferred,
|
|
5
|
+
} from "../../../shared/lib/async/index.js";
|
|
6
|
+
import type { UrlMatcher } from "../../../shared/lib/match/index.js";
|
|
7
|
+
import type {
|
|
8
|
+
WsCloseInfo,
|
|
9
|
+
WsConnection,
|
|
10
|
+
WsMessage,
|
|
11
|
+
WsMock,
|
|
12
|
+
WsMockOptions,
|
|
13
|
+
} from "../model/types.js";
|
|
14
|
+
import {
|
|
15
|
+
closedError,
|
|
16
|
+
createSocketLog,
|
|
17
|
+
encodeOutgoing,
|
|
18
|
+
toMessage,
|
|
19
|
+
} from "./wire.js";
|
|
20
|
+
|
|
21
|
+
/** A fake server: every matching socket becomes a connection the test drives. */
|
|
22
|
+
export async function createMock(
|
|
23
|
+
context: PluginContext,
|
|
24
|
+
protocol: string,
|
|
25
|
+
url: UrlMatcher,
|
|
26
|
+
options: WsMockOptions,
|
|
27
|
+
): Promise<WsMock> {
|
|
28
|
+
const connections = createArrivals<WsConnection>({
|
|
29
|
+
clock: context.clock,
|
|
30
|
+
label: "WebSocket connection",
|
|
31
|
+
});
|
|
32
|
+
/** Each connection, with whether it is over: `broadcast` skips those. */
|
|
33
|
+
const ended = new Map<WsConnection, () => boolean>();
|
|
34
|
+
const rule = await context.sockets.add({
|
|
35
|
+
protocol,
|
|
36
|
+
label: `mock ${String(url)}`,
|
|
37
|
+
url,
|
|
38
|
+
...(options.times === undefined ? {} : { times: options.times }),
|
|
39
|
+
async open(socket) {
|
|
40
|
+
const log = createSocketLog(context, protocol, socket);
|
|
41
|
+
const messages = createArrivals<WsMessage>({
|
|
42
|
+
clock: context.clock,
|
|
43
|
+
label: `message on ${socket.url.pathname}`,
|
|
44
|
+
});
|
|
45
|
+
const closed = createDeferred<WsCloseInfo>();
|
|
46
|
+
// The page hears of a close the stub starts through the same event
|
|
47
|
+
// as its own; whoever asked first is who closed.
|
|
48
|
+
let closedBy: "page" | "stub" = "page";
|
|
49
|
+
log("lifecycle", "none", "page", { event: "open", url: socket.url.href });
|
|
50
|
+
socket.page.onMessage((raw) => {
|
|
51
|
+
messages.push(toMessage("out", raw, context.clock.now()));
|
|
52
|
+
log("message", "out", "page", {}, raw);
|
|
53
|
+
});
|
|
54
|
+
socket.page.onClose((code, reason) => {
|
|
55
|
+
// Once per connection: a close is reported again when the route
|
|
56
|
+
// completes it, which the close below does.
|
|
57
|
+
if (closed.settled) return;
|
|
58
|
+
closed.resolve({ code, reason });
|
|
59
|
+
log("lifecycle", "none", closedBy, { event: "close", code, reason });
|
|
60
|
+
// A handler here turns off Playwright's own answer to the page's
|
|
61
|
+
// close, and without one the page's socket would hang in CLOSING.
|
|
62
|
+
if (closedBy === "page")
|
|
63
|
+
void socket.page.close({
|
|
64
|
+
...(code === undefined ? {} : { code }),
|
|
65
|
+
...(reason === undefined ? {} : { reason }),
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
// Over from the moment the stub closes it, before the page hears.
|
|
69
|
+
const over = () => closed.settled || closedBy === "stub";
|
|
70
|
+
const connection: WsConnection = {
|
|
71
|
+
url: socket.url.href,
|
|
72
|
+
get messages() {
|
|
73
|
+
return messages.items;
|
|
74
|
+
},
|
|
75
|
+
nextMessage: (waitOptions) => messages.next(waitOptions),
|
|
76
|
+
send(data) {
|
|
77
|
+
if (over()) throw closedError(socket);
|
|
78
|
+
const encoded = encodeOutgoing(data);
|
|
79
|
+
socket.page.send(encoded);
|
|
80
|
+
log("message", "in", "stub", {}, encoded);
|
|
81
|
+
},
|
|
82
|
+
async close(closeOptions) {
|
|
83
|
+
closedBy = "stub";
|
|
84
|
+
await socket.page.close(closeOptions);
|
|
85
|
+
},
|
|
86
|
+
closed: closed.promise,
|
|
87
|
+
};
|
|
88
|
+
connections.push(connection);
|
|
89
|
+
ended.set(connection, over);
|
|
90
|
+
try {
|
|
91
|
+
await options.onConnect?.(connection);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
// The stage closes the socket of a rule that throws: the close
|
|
94
|
+
// the page then hears of is the stub's doing.
|
|
95
|
+
closedBy = "stub";
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
return {
|
|
101
|
+
get hits() {
|
|
102
|
+
return rule.hits;
|
|
103
|
+
},
|
|
104
|
+
get connections() {
|
|
105
|
+
return connections.items;
|
|
106
|
+
},
|
|
107
|
+
next: (waitOptions) => connections.next(waitOptions),
|
|
108
|
+
broadcast(data) {
|
|
109
|
+
for (const [connection, isOver] of ended)
|
|
110
|
+
if (!isOver()) connection.send(data);
|
|
111
|
+
},
|
|
112
|
+
remove: () => rule.remove(),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PluginContext,
|
|
3
|
+
SocketPeer,
|
|
4
|
+
} from "../../../entities/plugin/index.js";
|
|
5
|
+
import {
|
|
6
|
+
createArrivals,
|
|
7
|
+
createDeferred,
|
|
8
|
+
} from "../../../shared/lib/async/index.js";
|
|
9
|
+
import type { UrlMatcher } from "../../../shared/lib/match/index.js";
|
|
10
|
+
import type {
|
|
11
|
+
WsCloseInfo,
|
|
12
|
+
WsMessage,
|
|
13
|
+
WsProxy,
|
|
14
|
+
WsProxyConnection,
|
|
15
|
+
WsProxyOptions,
|
|
16
|
+
} from "../model/types.js";
|
|
17
|
+
import {
|
|
18
|
+
closedError,
|
|
19
|
+
createSocketLog,
|
|
20
|
+
encodeOutgoing,
|
|
21
|
+
toMessage,
|
|
22
|
+
} from "./wire.js";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A pass-through to the real server that records both directions and can
|
|
26
|
+
* drop or inject messages on the way.
|
|
27
|
+
*/
|
|
28
|
+
export async function createProxy(
|
|
29
|
+
context: PluginContext,
|
|
30
|
+
protocol: string,
|
|
31
|
+
url: UrlMatcher,
|
|
32
|
+
options: WsProxyOptions,
|
|
33
|
+
): Promise<WsProxy> {
|
|
34
|
+
const connections = createArrivals<WsProxyConnection>({
|
|
35
|
+
clock: context.clock,
|
|
36
|
+
label: "WebSocket connection",
|
|
37
|
+
});
|
|
38
|
+
const rule = await context.sockets.add({
|
|
39
|
+
protocol,
|
|
40
|
+
label: `proxy ${String(url)}`,
|
|
41
|
+
url,
|
|
42
|
+
...(options.times === undefined ? {} : { times: options.times }),
|
|
43
|
+
open(socket) {
|
|
44
|
+
const log = createSocketLog(context, protocol, socket);
|
|
45
|
+
const page = socket.page;
|
|
46
|
+
const server = socket.connectToServer();
|
|
47
|
+
const messages = createArrivals<WsMessage>({
|
|
48
|
+
clock: context.clock,
|
|
49
|
+
label: `message on ${socket.url.pathname}`,
|
|
50
|
+
});
|
|
51
|
+
const closed = createDeferred<WsCloseInfo>();
|
|
52
|
+
// Who closed, when neither end did: the test, or the stub — the
|
|
53
|
+
// stage closes the socket of a rule that throws.
|
|
54
|
+
let closedBy: "test" | "stub" | undefined;
|
|
55
|
+
log("lifecycle", "none", "page", { event: "open", url: socket.url.href });
|
|
56
|
+
const forward = (
|
|
57
|
+
direction: "in" | "out",
|
|
58
|
+
raw: string | Uint8Array,
|
|
59
|
+
to: SocketPeer,
|
|
60
|
+
) => {
|
|
61
|
+
const passing = toMessage(direction, raw, context.clock.now());
|
|
62
|
+
messages.push(passing);
|
|
63
|
+
let dropped: boolean | undefined;
|
|
64
|
+
try {
|
|
65
|
+
dropped = options.drop?.(passing);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
// The stage fails a rule that throws and closes both ends: the
|
|
68
|
+
// closes they report are the stub's, not the page's or server's.
|
|
69
|
+
closedBy ??= "stub";
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
if (dropped) {
|
|
73
|
+
log("fault", direction, "stub", { reason: "dropped" }, raw);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
log(
|
|
77
|
+
"message",
|
|
78
|
+
direction,
|
|
79
|
+
direction === "out" ? "page" : "network",
|
|
80
|
+
{},
|
|
81
|
+
raw,
|
|
82
|
+
);
|
|
83
|
+
to.send(raw);
|
|
84
|
+
};
|
|
85
|
+
page.onMessage((raw) => forward("out", raw, server));
|
|
86
|
+
server.onMessage((raw) => forward("in", raw, page));
|
|
87
|
+
// A close handler turns off Playwright's own forwarding of the close,
|
|
88
|
+
// so whichever end closes first, both are closed by hand: the other
|
|
89
|
+
// end to pass the close on, and the end that started it to complete
|
|
90
|
+
// it — the page's socket hangs in CLOSING until its end is closed.
|
|
91
|
+
// Once: each of those closes is reported back through a handler.
|
|
92
|
+
const relay =
|
|
93
|
+
(from: "page" | "network") =>
|
|
94
|
+
(code: number | undefined, reason: string | undefined) => {
|
|
95
|
+
if (closed.settled) return;
|
|
96
|
+
closed.resolve({ code, reason });
|
|
97
|
+
log("lifecycle", "none", closedBy ?? from, {
|
|
98
|
+
event: "close",
|
|
99
|
+
code,
|
|
100
|
+
reason,
|
|
101
|
+
});
|
|
102
|
+
const passed = {
|
|
103
|
+
...(code === undefined ? {} : { code }),
|
|
104
|
+
...(reason === undefined ? {} : { reason }),
|
|
105
|
+
};
|
|
106
|
+
void page.close(passed);
|
|
107
|
+
void server.close(passed);
|
|
108
|
+
};
|
|
109
|
+
page.onClose(relay("page"));
|
|
110
|
+
server.onClose(relay("network"));
|
|
111
|
+
// Over from the moment the test closes it or the rule fails, before
|
|
112
|
+
// either end hears.
|
|
113
|
+
const over = () => closed.settled || closedBy !== undefined;
|
|
114
|
+
connections.push({
|
|
115
|
+
url: socket.url.href,
|
|
116
|
+
get messages() {
|
|
117
|
+
return messages.items;
|
|
118
|
+
},
|
|
119
|
+
nextMessage: (waitOptions) => messages.next(waitOptions),
|
|
120
|
+
toPage(data) {
|
|
121
|
+
if (over()) throw closedError(socket);
|
|
122
|
+
const encoded = encodeOutgoing(data);
|
|
123
|
+
page.send(encoded);
|
|
124
|
+
log("message", "in", "test", { injected: true }, encoded);
|
|
125
|
+
},
|
|
126
|
+
toServer(data) {
|
|
127
|
+
if (over()) throw closedError(socket);
|
|
128
|
+
const encoded = encodeOutgoing(data);
|
|
129
|
+
server.send(encoded);
|
|
130
|
+
log("message", "out", "test", { injected: true }, encoded);
|
|
131
|
+
},
|
|
132
|
+
async close(closeOptions) {
|
|
133
|
+
closedBy = "test";
|
|
134
|
+
await Promise.all([
|
|
135
|
+
page.close(closeOptions),
|
|
136
|
+
server.close(closeOptions),
|
|
137
|
+
]);
|
|
138
|
+
},
|
|
139
|
+
closed: closed.promise,
|
|
140
|
+
});
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
return {
|
|
144
|
+
get hits() {
|
|
145
|
+
return rule.hits;
|
|
146
|
+
},
|
|
147
|
+
get connections() {
|
|
148
|
+
return connections.items;
|
|
149
|
+
},
|
|
150
|
+
next: (waitOptions) => connections.next(waitOptions),
|
|
151
|
+
remove: () => rule.remove(),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Direction,
|
|
3
|
+
FrameKind,
|
|
4
|
+
FrameSource,
|
|
5
|
+
} from "../../../entities/frame/index.js";
|
|
6
|
+
import type {
|
|
7
|
+
PluginContext,
|
|
8
|
+
SocketView,
|
|
9
|
+
} from "../../../entities/plugin/index.js";
|
|
10
|
+
import { toBytes, utf8 } from "../../../shared/lib/bytes/index.js";
|
|
11
|
+
import { WirestageError } from "../../../shared/lib/errors/index.js";
|
|
12
|
+
import type { WsData, WsMessage } from "../model/types.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* What a send to a connection that is over throws. Playwright would drop
|
|
16
|
+
* the message without a word, and the timeline would show it delivered.
|
|
17
|
+
*/
|
|
18
|
+
export function closedError(socket: SocketView): WirestageError {
|
|
19
|
+
return new WirestageError(
|
|
20
|
+
"CLOSED",
|
|
21
|
+
`the WebSocket connection to ${socket.url.href} is closed`,
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** What a test passes to `send`, as the wire carries it. */
|
|
26
|
+
export function encodeOutgoing(data: WsData): string | Uint8Array {
|
|
27
|
+
if (typeof data === "string") return data;
|
|
28
|
+
// Any binary form goes out as bytes; only what is left is a value.
|
|
29
|
+
if (data instanceof ArrayBuffer || ArrayBuffer.isView(data))
|
|
30
|
+
return toBytes(data);
|
|
31
|
+
return JSON.stringify(data);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function toMessage(
|
|
35
|
+
direction: "in" | "out",
|
|
36
|
+
raw: string | Uint8Array,
|
|
37
|
+
at: number,
|
|
38
|
+
): WsMessage {
|
|
39
|
+
const text = () => (typeof raw === "string" ? raw : utf8(raw));
|
|
40
|
+
return {
|
|
41
|
+
direction,
|
|
42
|
+
data: raw,
|
|
43
|
+
at,
|
|
44
|
+
text,
|
|
45
|
+
json: <T>() => JSON.parse(text()) as T,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Writes one connection's frames: same endpoint, correlation and context. */
|
|
50
|
+
export type SocketLog = (
|
|
51
|
+
kind: FrameKind,
|
|
52
|
+
direction: Direction,
|
|
53
|
+
source: FrameSource,
|
|
54
|
+
meta: Readonly<Record<string, unknown>>,
|
|
55
|
+
data?: string | Uint8Array,
|
|
56
|
+
) => void;
|
|
57
|
+
|
|
58
|
+
export function createSocketLog(
|
|
59
|
+
context: Pick<PluginContext, "timeline">,
|
|
60
|
+
protocol: string,
|
|
61
|
+
socket: SocketView,
|
|
62
|
+
): SocketLog {
|
|
63
|
+
return (kind, direction, source, meta, data) => {
|
|
64
|
+
context.timeline.append({
|
|
65
|
+
protocol,
|
|
66
|
+
kind,
|
|
67
|
+
direction,
|
|
68
|
+
// The path, like every HTTP frame: it reads the same on the dev
|
|
69
|
+
// server, the preview build and CI, whose origins all differ.
|
|
70
|
+
endpoint: socket.url.pathname,
|
|
71
|
+
source,
|
|
72
|
+
correlation: socket.correlation,
|
|
73
|
+
context: socket.context,
|
|
74
|
+
meta,
|
|
75
|
+
...(data === undefined ? {} : { data }),
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { Frame } from "../../../entities/frame/index.js";
|
|
2
|
+
import type { UrlMatcher } from "../../../shared/lib/match/index.js";
|
|
3
|
+
|
|
4
|
+
export interface WebSocketOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Leave `WebSocket` alone until the first `mock()` or `proxy()`.
|
|
7
|
+
*
|
|
8
|
+
* By default the plugin routes WebSockets from the moment a context is
|
|
9
|
+
* attached, so a mock added mid-test reaches the pages that are already
|
|
10
|
+
* open. Playwright routes by replacing `WebSocket` in every document as it
|
|
11
|
+
* loads, and a socket no rule takes is passed through to the real server.
|
|
12
|
+
* With `lazy`, nothing is replaced until the first rule exists — and that
|
|
13
|
+
* rule then applies only to the documents loaded after it.
|
|
14
|
+
*/
|
|
15
|
+
readonly lazy?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** What a test may send: text, bytes in any binary form, or a value sent as JSON. */
|
|
19
|
+
export type WsData = string | ArrayBuffer | ArrayBufferView | object;
|
|
20
|
+
|
|
21
|
+
export interface WsMessage {
|
|
22
|
+
/** `out` came from the page, `in` went to it. */
|
|
23
|
+
readonly direction: "in" | "out";
|
|
24
|
+
readonly data: string | Uint8Array;
|
|
25
|
+
readonly at: number;
|
|
26
|
+
text(): string;
|
|
27
|
+
json<T = unknown>(): T;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface WsCloseInfo {
|
|
31
|
+
readonly code: number | undefined;
|
|
32
|
+
readonly reason: string | undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface WsCloseOptions {
|
|
36
|
+
/**
|
|
37
|
+
* The code the page's socket closes with: any a server may send. A proxy
|
|
38
|
+
* also closes the real server's end, through the browser, which sends
|
|
39
|
+
* only 1000 and 3000-4999 — another code reaches the page as given and
|
|
40
|
+
* the server as a normal closure, 1000.
|
|
41
|
+
*/
|
|
42
|
+
readonly code?: number;
|
|
43
|
+
/**
|
|
44
|
+
* The page gets it whole. A proxy's real server gets at most 123 bytes
|
|
45
|
+
* of it, cut on a character: all a close frame carries, and all the
|
|
46
|
+
* browser will send.
|
|
47
|
+
*/
|
|
48
|
+
readonly reason?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface WsConnection {
|
|
52
|
+
readonly url: string;
|
|
53
|
+
/** Messages the page sent, oldest first. */
|
|
54
|
+
readonly messages: readonly WsMessage[];
|
|
55
|
+
/** The next message the page sends (or sent and nobody took). */
|
|
56
|
+
nextMessage(options?: { readonly timeout?: number }): Promise<WsMessage>;
|
|
57
|
+
/**
|
|
58
|
+
* Send to the page. Throws `CLOSED` once the connection is over — from
|
|
59
|
+
* the moment `close()` is called — since the page would never get it.
|
|
60
|
+
*/
|
|
61
|
+
send(data: WsData): void;
|
|
62
|
+
close(options?: WsCloseOptions): Promise<void>;
|
|
63
|
+
/** Resolves when the connection is over, whoever closed it. */
|
|
64
|
+
readonly closed: Promise<WsCloseInfo>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface WsMockOptions {
|
|
68
|
+
/** Answer this many connections, then leave the rest to the next rule. */
|
|
69
|
+
readonly times?: number;
|
|
70
|
+
/**
|
|
71
|
+
* Called for each new connection, e.g. to send a greeting. The page's
|
|
72
|
+
* socket opens once it settles. If it throws, the stage closes the
|
|
73
|
+
* page's socket with 1011 and records a `rule-error` fault.
|
|
74
|
+
*/
|
|
75
|
+
readonly onConnect?: (connection: WsConnection) => void | Promise<void>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface WsMock {
|
|
79
|
+
/** Connections this mock took. */
|
|
80
|
+
readonly hits: number;
|
|
81
|
+
readonly connections: readonly WsConnection[];
|
|
82
|
+
/** The next connection the page opens (or opened and nobody took). */
|
|
83
|
+
next(options?: { readonly timeout?: number }): Promise<WsConnection>;
|
|
84
|
+
/** Send to every connection that is not over yet. */
|
|
85
|
+
broadcast(data: WsData): void;
|
|
86
|
+
/** New connections go to the real server again. */
|
|
87
|
+
remove(): Promise<void>;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface WsProxyOptions {
|
|
91
|
+
/** Proxy this many connections, then leave the rest to the next rule. */
|
|
92
|
+
readonly times?: number;
|
|
93
|
+
/**
|
|
94
|
+
* Drop a message instead of forwarding it. Dropped messages are recorded
|
|
95
|
+
* as faults. If it throws, the stage closes both ends, the page's with
|
|
96
|
+
* 1011, and records a `rule-error` fault.
|
|
97
|
+
*/
|
|
98
|
+
readonly drop?: (message: WsMessage) => boolean;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface WsProxyConnection {
|
|
102
|
+
readonly url: string;
|
|
103
|
+
/** Messages in both directions, oldest first. */
|
|
104
|
+
readonly messages: readonly WsMessage[];
|
|
105
|
+
nextMessage(options?: { readonly timeout?: number }): Promise<WsMessage>;
|
|
106
|
+
/**
|
|
107
|
+
* Inject a message to the page, as if the server sent it. Throws
|
|
108
|
+
* `CLOSED` once the connection is over, as `toServer` does.
|
|
109
|
+
*/
|
|
110
|
+
toPage(data: WsData): void;
|
|
111
|
+
/** Inject a message to the server, as if the page sent it. */
|
|
112
|
+
toServer(data: WsData): void;
|
|
113
|
+
/** Close both sides. */
|
|
114
|
+
close(options?: WsCloseOptions): Promise<void>;
|
|
115
|
+
/** Resolves when the connection is over, whoever closed it. */
|
|
116
|
+
readonly closed: Promise<WsCloseInfo>;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface WsProxy {
|
|
120
|
+
/** Connections this proxy took. */
|
|
121
|
+
readonly hits: number;
|
|
122
|
+
readonly connections: readonly WsProxyConnection[];
|
|
123
|
+
next(options?: { readonly timeout?: number }): Promise<WsProxyConnection>;
|
|
124
|
+
remove(): Promise<void>;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface WebSocketApi {
|
|
128
|
+
/** Answer matching WebSockets with a fake server the test drives. */
|
|
129
|
+
mock(url: UrlMatcher, options?: WsMockOptions): Promise<WsMock>;
|
|
130
|
+
/** Pass matching WebSockets through to the real server, recording and optionally dropping messages. */
|
|
131
|
+
proxy(url: UrlMatcher, options?: WsProxyOptions): Promise<WsProxy>;
|
|
132
|
+
/** Every recorded frame: opens, messages, drops, closes. */
|
|
133
|
+
frames(filter?: { readonly endpoint?: string | RegExp }): Promise<Frame[]>;
|
|
134
|
+
}
|