@heojeongbo/wirestage 0.0.0-stage → 0.1.0
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 +126 -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 +102 -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 +43 -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 +120 -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 +62 -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 +107 -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 +129 -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 +117 -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 +43 -0
- package/dist/shared/api/stream-server/stream-server.d.ts.map +1 -0
- package/dist/shared/api/stream-server/stream-server.js +160 -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 +248 -0
- package/dist/widgets/stage/lib/create-stage.js.map +1 -0
- package/dist/widgets/stage/lib/http-router.d.ts +35 -0
- package/dist/widgets/stage/lib/http-router.d.ts.map +1 -0
- package/dist/widgets/stage/lib/http-router.js +455 -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 +9 -0
- package/dist/widgets/stage/lib/response-stream.d.ts.map +1 -0
- package/dist/widgets/stage/lib/response-stream.js +56 -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 +204 -0
- package/dist/widgets/stage/lib/socket-router.js.map +1 -0
- package/dist/widgets/stage/model/stage.d.ts +86 -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 +147 -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 +108 -0
- package/src/entities/plugin/model/stream.ts +43 -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 +128 -0
- package/src/features/sse/lib/encode.ts +28 -0
- package/src/features/sse/model/types.ts +66 -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 +141 -0
- package/src/features/websocket/lib/wire.ts +78 -0
- package/src/features/websocket/model/types.ts +130 -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 +145 -0
- package/src/shared/api/stream-server/index.ts +7 -0
- package/src/shared/api/stream-server/stream-server.ts +246 -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 +305 -0
- package/src/widgets/stage/lib/http-router.ts +620 -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 +66 -0
- package/src/widgets/stage/lib/socket-router.ts +271 -0
- package/src/widgets/stage/model/stage.ts +108 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Frame,
|
|
3
|
+
FrameFilter,
|
|
4
|
+
Timeline,
|
|
5
|
+
} from "../../../entities/frame/index.js";
|
|
6
|
+
import type { WaitForOptions } from "../../../entities/plugin/index.js";
|
|
7
|
+
import type { Clock } from "../../../shared/lib/clock/index.js";
|
|
8
|
+
import { WirestageError } from "../../../shared/lib/errors/index.js";
|
|
9
|
+
import type { StageCore } from "../model/stage.js";
|
|
10
|
+
|
|
11
|
+
export interface ReadsDeps {
|
|
12
|
+
readonly timeline: Timeline;
|
|
13
|
+
readonly clock: Clock;
|
|
14
|
+
/** Flush every page buffer into the timeline. */
|
|
15
|
+
readonly sync: () => Promise<void>;
|
|
16
|
+
/** Defaults for `waitFor`, in milliseconds. */
|
|
17
|
+
readonly wait: { readonly timeout: number; readonly interval: number };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Frames a report holds unless asked otherwise. A report is read from its
|
|
22
|
+
* end, and an unbounded one grows with the test: a long run would attach
|
|
23
|
+
* tens of megabytes, or fail to serialize at all.
|
|
24
|
+
*/
|
|
25
|
+
const REPORT_LIMIT = 1_000;
|
|
26
|
+
|
|
27
|
+
export type StageReads = Pick<
|
|
28
|
+
StageCore,
|
|
29
|
+
"frames" | "drain" | "mark" | "since" | "between" | "waitFor" | "report"
|
|
30
|
+
>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The stage's reads of its timeline. Every one flushes the page buffers
|
|
34
|
+
* first: that flush is what makes arrival order the order things happened in.
|
|
35
|
+
*/
|
|
36
|
+
export function createReads(deps: ReadsDeps): StageReads {
|
|
37
|
+
const { timeline, clock, sync } = deps;
|
|
38
|
+
const drainCursor = timeline.cursor();
|
|
39
|
+
|
|
40
|
+
const requireMarker = (name: string): Frame => {
|
|
41
|
+
const marker = timeline.marker(name);
|
|
42
|
+
if (marker === undefined)
|
|
43
|
+
throw new WirestageError(
|
|
44
|
+
"INVALID_ARGUMENT",
|
|
45
|
+
timeline.evicted > 0
|
|
46
|
+
? `no marker "${name}" on the timeline: it was never placed, or it was among the ${timeline.evicted} frame(s) dropped at capacity`
|
|
47
|
+
: `no marker "${name}" on the timeline; place one with stage.mark("${name}")`,
|
|
48
|
+
);
|
|
49
|
+
return marker;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
async frames(filter) {
|
|
54
|
+
await sync();
|
|
55
|
+
return timeline.frames(filter);
|
|
56
|
+
},
|
|
57
|
+
async drain(filter) {
|
|
58
|
+
await sync();
|
|
59
|
+
return drainCursor.take(filter);
|
|
60
|
+
},
|
|
61
|
+
async mark(name, meta) {
|
|
62
|
+
await sync();
|
|
63
|
+
return timeline.append({
|
|
64
|
+
protocol: "wirestage",
|
|
65
|
+
kind: "marker",
|
|
66
|
+
direction: "none",
|
|
67
|
+
endpoint: name,
|
|
68
|
+
source: "test",
|
|
69
|
+
...(meta === undefined ? {} : { meta }),
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
async since(marker, filter) {
|
|
73
|
+
await sync();
|
|
74
|
+
const after = requireMarker(marker).seq;
|
|
75
|
+
return timeline.frames({
|
|
76
|
+
...filter,
|
|
77
|
+
after: Math.max(after, filter?.after ?? 0),
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
async between(from, to, filter) {
|
|
81
|
+
await sync();
|
|
82
|
+
const start = requireMarker(from).seq;
|
|
83
|
+
const end = requireMarker(to).seq;
|
|
84
|
+
const where = filter?.where;
|
|
85
|
+
return timeline.frames({
|
|
86
|
+
...filter,
|
|
87
|
+
after: Math.max(start, filter?.after ?? 0),
|
|
88
|
+
where: (frame) =>
|
|
89
|
+
frame.seq < end && (where === undefined || where(frame)),
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
async waitFor(filter: FrameFilter, options: WaitForOptions = {}) {
|
|
93
|
+
const count = options.count ?? 1;
|
|
94
|
+
const timeout = options.timeout ?? deps.wait.timeout;
|
|
95
|
+
const interval = options.interval ?? deps.wait.interval;
|
|
96
|
+
const deadline = clock.now() + timeout;
|
|
97
|
+
for (;;) {
|
|
98
|
+
await sync();
|
|
99
|
+
const found = timeline.frames(filter);
|
|
100
|
+
if (found.length >= count) return found;
|
|
101
|
+
const remaining = deadline - clock.now();
|
|
102
|
+
if (remaining <= 0)
|
|
103
|
+
throw new WirestageError(
|
|
104
|
+
"TIMEOUT",
|
|
105
|
+
`waited ${timeout} ms for ${count} frame(s) matching ${describeFilter(filter)}; ${found.length} matched. Last frames:\n${timeline.format(undefined, { limit: 20 })}`,
|
|
106
|
+
{ details: { count, matched: found.length, timeout } },
|
|
107
|
+
);
|
|
108
|
+
await clock.sleep(Math.min(interval, remaining));
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
report(filter, options = {}) {
|
|
112
|
+
const limit = options.limit ?? REPORT_LIMIT;
|
|
113
|
+
return {
|
|
114
|
+
snapshot: timeline.snapshot(filter, { limit }),
|
|
115
|
+
text: timeline.format(filter, { limit }),
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** A filter as JSON, with RegExps and callbacks spelled out instead of lost. */
|
|
122
|
+
function describeFilter(filter: FrameFilter): string {
|
|
123
|
+
return JSON.stringify(filter, (_key, value: unknown) => {
|
|
124
|
+
if (value instanceof RegExp) return value.toString();
|
|
125
|
+
if (typeof value === "function") return "[function]";
|
|
126
|
+
return value;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
ResponseStream,
|
|
5
|
+
StreamEnd,
|
|
6
|
+
} from "../../../entities/plugin/index.js";
|
|
7
|
+
import { createDeferred } from "../../../shared/lib/async/index.js";
|
|
8
|
+
import { toBytes } from "../../../shared/lib/bytes/index.js";
|
|
9
|
+
import { WirestageError } from "../../../shared/lib/errors/index.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A Node response as the stream a rule writes: the adapter between the
|
|
13
|
+
* stream server's sockets and the plugin contract, which knows nothing of
|
|
14
|
+
* Node.
|
|
15
|
+
*/
|
|
16
|
+
export function createResponseStream(
|
|
17
|
+
request: IncomingMessage,
|
|
18
|
+
response: ServerResponse,
|
|
19
|
+
): ResponseStream {
|
|
20
|
+
let state: "open" | "ended" | "cut" = "open";
|
|
21
|
+
const closed = createDeferred<StreamEnd>();
|
|
22
|
+
response.once("close", () =>
|
|
23
|
+
// Not ended and not cut: the page let go of it.
|
|
24
|
+
closed.resolve(state === "open" ? "cancelled" : state),
|
|
25
|
+
);
|
|
26
|
+
const requestHeaders: Record<string, string> = {};
|
|
27
|
+
for (const [name, value] of Object.entries(request.headers))
|
|
28
|
+
requestHeaders[name] = String(value);
|
|
29
|
+
return {
|
|
30
|
+
requestHeaders,
|
|
31
|
+
head(status, headers = {}) {
|
|
32
|
+
response.writeHead(status, { ...headers });
|
|
33
|
+
},
|
|
34
|
+
flush() {
|
|
35
|
+
response.flushHeaders();
|
|
36
|
+
},
|
|
37
|
+
write(chunk) {
|
|
38
|
+
return new Promise<void>((resolve, reject) => {
|
|
39
|
+
if (state !== "open" || response.destroyed) {
|
|
40
|
+
reject(new WirestageError("CLOSED", "the response stream is closed"));
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
response.write(toBytes(chunk), (error) =>
|
|
44
|
+
error ? reject(error) : resolve(),
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
async end() {
|
|
49
|
+
if (state !== "open") return;
|
|
50
|
+
state = "ended";
|
|
51
|
+
// A response the page already let go of never calls back from
|
|
52
|
+
// end(): resolve on whichever comes first.
|
|
53
|
+
if (!response.destroyed)
|
|
54
|
+
await new Promise<void>((resolve) => {
|
|
55
|
+
response.once("close", resolve);
|
|
56
|
+
response.end(resolve);
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
cut() {
|
|
60
|
+
if (state !== "open") return;
|
|
61
|
+
state = "cut";
|
|
62
|
+
response.destroy();
|
|
63
|
+
},
|
|
64
|
+
closed: closed.promise,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import type { Timeline } from "../../../entities/frame/index.js";
|
|
2
|
+
import type {
|
|
3
|
+
SocketPeer,
|
|
4
|
+
SocketRouter,
|
|
5
|
+
SocketRule,
|
|
6
|
+
SocketRuleHandle,
|
|
7
|
+
SocketView,
|
|
8
|
+
} from "../../../entities/plugin/index.js";
|
|
9
|
+
import type { WebSocketRoutePort } from "../../../shared/api/playwright/index.js";
|
|
10
|
+
import { toBuffer } from "../../../shared/lib/bytes/index.js";
|
|
11
|
+
import { WirestageError } from "../../../shared/lib/errors/index.js";
|
|
12
|
+
import type { ScopedLogger } from "../../../shared/lib/logger/index.js";
|
|
13
|
+
import { compileUrlMatcher } from "../../../shared/lib/match/index.js";
|
|
14
|
+
import type { AttachedContext } from "./attached-context.js";
|
|
15
|
+
|
|
16
|
+
export interface SocketRouterDeps {
|
|
17
|
+
readonly timeline: Timeline;
|
|
18
|
+
readonly log: ScopedLogger;
|
|
19
|
+
readonly correlation: () => string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface SocketRouterEngine extends SocketRouter {
|
|
23
|
+
attach(attachment: AttachedContext): Promise<void>;
|
|
24
|
+
detach(attachment: AttachedContext): void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface Entry {
|
|
28
|
+
readonly rule: SocketRule;
|
|
29
|
+
readonly matchesUrl: (url: URL) => boolean;
|
|
30
|
+
remaining: number | undefined;
|
|
31
|
+
hits: number;
|
|
32
|
+
active: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type CloseOptions = Parameters<SocketPeer["close"]>[0];
|
|
36
|
+
|
|
37
|
+
/** What a script may pass to `WebSocket.close()`: 1000, or 3000-4999. */
|
|
38
|
+
const sendable = (code: number): boolean =>
|
|
39
|
+
code === 1000 || (code >= 3000 && code <= 4999);
|
|
40
|
+
|
|
41
|
+
const encoder = new TextEncoder();
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A close reason cut to what a close frame carries: 123 bytes of UTF-8 (the
|
|
45
|
+
* frame's 125, less the code), ending on a whole character.
|
|
46
|
+
*/
|
|
47
|
+
const fitReason = (reason: string): string =>
|
|
48
|
+
// `encodeInto` stops before the first character that does not fit whole.
|
|
49
|
+
reason.slice(0, encoder.encodeInto(reason, new Uint8Array(123)).read);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* A close as the server's end can carry it out. Playwright closes that end
|
|
53
|
+
* with the browser's own `WebSocket.close(code, reason)`, which throws on a
|
|
54
|
+
* code a script may not send, on a reason without a code and on a reason
|
|
55
|
+
* over 123 bytes — and Playwright swallows the error, which leaves the
|
|
56
|
+
* connection open for good. Such a close goes out as a normal closure, 1000,
|
|
57
|
+
* with its reason cut to fit: the close is the point.
|
|
58
|
+
*/
|
|
59
|
+
const forServer = (options: CloseOptions): CloseOptions => {
|
|
60
|
+
const { code, reason } = options ?? {};
|
|
61
|
+
if (code === undefined && reason === undefined) return options;
|
|
62
|
+
return {
|
|
63
|
+
code: code !== undefined && sendable(code) ? code : 1000,
|
|
64
|
+
...(reason === undefined ? {} : { reason: fitReason(reason) }),
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/** The message handler of an end whose messages the rule does not take. */
|
|
69
|
+
const drop = (): void => {};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Turns WebSocket rules into ONE Playwright WebSocket route per attached
|
|
73
|
+
* context, whose predicate asks the live rule list.
|
|
74
|
+
*
|
|
75
|
+
* One route, because Playwright cannot take a WebSocket route out again: a
|
|
76
|
+
* removed rule, or a detached context, simply stops matching, which sends the
|
|
77
|
+
* socket to the real server. And not before it is needed, because routing
|
|
78
|
+
* replaces `WebSocket` in every document of the context — the first rule
|
|
79
|
+
* installs it, or `prepare()` does at attach.
|
|
80
|
+
*/
|
|
81
|
+
export function createSocketRouter(deps: SocketRouterDeps): SocketRouterEngine {
|
|
82
|
+
/** Active rules, oldest first. */
|
|
83
|
+
const entries: Entry[] = [];
|
|
84
|
+
const attachments = new Set<AttachedContext>();
|
|
85
|
+
const routes = new Map<AttachedContext, Promise<void>>();
|
|
86
|
+
let prepared = false;
|
|
87
|
+
|
|
88
|
+
/** The newest rule that wants a socket to `url`. */
|
|
89
|
+
const taker = (url: URL): Entry | undefined =>
|
|
90
|
+
entries.findLast((entry) => {
|
|
91
|
+
try {
|
|
92
|
+
return entry.matchesUrl(url);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
// The caller's predicate; one that throws must not break every
|
|
95
|
+
// socket of the context.
|
|
96
|
+
deps.log.error(
|
|
97
|
+
"rule's URL matcher failed",
|
|
98
|
+
{ rule: entry.rule.label, url: url.href },
|
|
99
|
+
error,
|
|
100
|
+
);
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const retire = (entry: Entry) => {
|
|
106
|
+
if (!entry.active) return;
|
|
107
|
+
entry.active = false;
|
|
108
|
+
entries.splice(entries.indexOf(entry), 1);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const peer = (
|
|
112
|
+
route: WebSocketRoutePort,
|
|
113
|
+
close: (options: CloseOptions) => CloseOptions = (options) => options,
|
|
114
|
+
): SocketPeer => ({
|
|
115
|
+
send: (data) =>
|
|
116
|
+
route.send(typeof data === "string" ? data : toBuffer(data)),
|
|
117
|
+
close: (options) => route.close(close(options)),
|
|
118
|
+
onMessage: (handler) => route.onMessage(handler),
|
|
119
|
+
onClose: (handler) => route.onClose(handler),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* A rule that throws is a bug in the test. Say which rule failed — in the
|
|
124
|
+
* log, on the timeline, and in the reason the page's socket closes with —
|
|
125
|
+
* and close both ends: the page's socket would otherwise sit open with
|
|
126
|
+
* nobody answering it, and a server the rule connected would stay
|
|
127
|
+
* connected. 1011 is a server's "internal error"; the server's end gets
|
|
128
|
+
* 1000, as a script may not send 1011.
|
|
129
|
+
*/
|
|
130
|
+
const fail = async (
|
|
131
|
+
rule: SocketRule,
|
|
132
|
+
view: SocketView,
|
|
133
|
+
server: SocketPeer | undefined,
|
|
134
|
+
error: unknown,
|
|
135
|
+
): Promise<void> => {
|
|
136
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
137
|
+
deps.log.error(
|
|
138
|
+
"socket rule failed",
|
|
139
|
+
{ rule: rule.label, url: view.url.href },
|
|
140
|
+
error,
|
|
141
|
+
);
|
|
142
|
+
deps.timeline.append({
|
|
143
|
+
protocol: rule.protocol,
|
|
144
|
+
kind: "fault",
|
|
145
|
+
direction: "none",
|
|
146
|
+
endpoint: view.url.pathname,
|
|
147
|
+
source: "stub",
|
|
148
|
+
correlation: view.correlation,
|
|
149
|
+
context: view.context,
|
|
150
|
+
meta: { reason: "rule-error", rule: rule.label, error: message },
|
|
151
|
+
});
|
|
152
|
+
const closing = {
|
|
153
|
+
code: 1011,
|
|
154
|
+
// Cut like the server's end, though the page's would take it whole:
|
|
155
|
+
// no real server could send the page a longer one.
|
|
156
|
+
reason: fitReason(`wirestage: rule "${rule.label}" failed: ${message}`),
|
|
157
|
+
};
|
|
158
|
+
await Promise.all([view.page.close(closing), server?.close(closing)]);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const onSocket = async (
|
|
162
|
+
attachment: AttachedContext,
|
|
163
|
+
route: WebSocketRoutePort,
|
|
164
|
+
): Promise<void> => {
|
|
165
|
+
const url = new URL(route.url());
|
|
166
|
+
const entry = attachments.has(attachment) ? taker(url) : undefined;
|
|
167
|
+
// The rule went, or the context was detached, between Playwright's
|
|
168
|
+
// check and this call: the socket is the real server's.
|
|
169
|
+
if (entry === undefined) {
|
|
170
|
+
route.connectToServer();
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
entry.hits += 1;
|
|
174
|
+
if (entry.remaining !== undefined && --entry.remaining === 0) retire(entry);
|
|
175
|
+
// Playwright passes the messages of an end with no handler on to the
|
|
176
|
+
// other end, unrecorded: a rule that wires one direction would leak
|
|
177
|
+
// the other. So each end starts with a handler that drops them, which
|
|
178
|
+
// the rule's own replaces.
|
|
179
|
+
route.onMessage(drop);
|
|
180
|
+
let server: SocketPeer | undefined;
|
|
181
|
+
const view: SocketView = {
|
|
182
|
+
correlation: deps.correlation(),
|
|
183
|
+
url,
|
|
184
|
+
context: attachment.id,
|
|
185
|
+
page: peer(route),
|
|
186
|
+
connectToServer: () => {
|
|
187
|
+
if (server === undefined) {
|
|
188
|
+
const upstream = route.connectToServer();
|
|
189
|
+
upstream.onMessage(drop);
|
|
190
|
+
server = peer(upstream, forServer);
|
|
191
|
+
}
|
|
192
|
+
return server;
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
try {
|
|
196
|
+
// Awaited: Playwright opens the page's end of a socket the rule
|
|
197
|
+
// did not connect once this handler settles, so an async rule is
|
|
198
|
+
// wired up before the first message.
|
|
199
|
+
await entry.rule.open(view);
|
|
200
|
+
} catch (error) {
|
|
201
|
+
// Never rejected to Playwright, where it would surface as an
|
|
202
|
+
// unhandled rejection in whichever test runs next.
|
|
203
|
+
await fail(entry.rule, view, server, error);
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const ensureRoute = (attachment: AttachedContext): Promise<void> => {
|
|
208
|
+
let installing = routes.get(attachment);
|
|
209
|
+
if (installing === undefined) {
|
|
210
|
+
installing = attachment.context.routeWebSocket(
|
|
211
|
+
(url) => attachments.has(attachment) && taker(url) !== undefined,
|
|
212
|
+
(route) => onSocket(attachment, route),
|
|
213
|
+
);
|
|
214
|
+
routes.set(attachment, installing);
|
|
215
|
+
// A failed install must not be remembered as done.
|
|
216
|
+
installing.catch(() => routes.delete(attachment));
|
|
217
|
+
}
|
|
218
|
+
return installing;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
async add(rule): Promise<SocketRuleHandle> {
|
|
223
|
+
if (
|
|
224
|
+
rule.times !== undefined &&
|
|
225
|
+
(!Number.isInteger(rule.times) || rule.times < 1)
|
|
226
|
+
)
|
|
227
|
+
throw new WirestageError(
|
|
228
|
+
"INVALID_ARGUMENT",
|
|
229
|
+
`rule "${rule.label}": times must be a positive integer, got ${rule.times}`,
|
|
230
|
+
);
|
|
231
|
+
const entry: Entry = {
|
|
232
|
+
rule,
|
|
233
|
+
matchesUrl: compileUrlMatcher(rule.url),
|
|
234
|
+
remaining: rule.times,
|
|
235
|
+
hits: 0,
|
|
236
|
+
active: true,
|
|
237
|
+
};
|
|
238
|
+
entries.push(entry);
|
|
239
|
+
try {
|
|
240
|
+
await Promise.all([...attachments].map(ensureRoute));
|
|
241
|
+
} catch (error) {
|
|
242
|
+
retire(entry);
|
|
243
|
+
throw error;
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
get hits() {
|
|
247
|
+
return entry.hits;
|
|
248
|
+
},
|
|
249
|
+
get active() {
|
|
250
|
+
return entry.active;
|
|
251
|
+
},
|
|
252
|
+
async remove() {
|
|
253
|
+
retire(entry);
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
},
|
|
257
|
+
prepare() {
|
|
258
|
+
prepared = true;
|
|
259
|
+
},
|
|
260
|
+
async attach(attachment) {
|
|
261
|
+
attachments.add(attachment);
|
|
262
|
+
if (prepared || entries.length > 0) await ensureRoute(attachment);
|
|
263
|
+
},
|
|
264
|
+
detach(attachment) {
|
|
265
|
+
attachments.delete(attachment);
|
|
266
|
+
// The route stays in the browser; a context attached again gets a
|
|
267
|
+
// new one, and this one's predicate never matches again.
|
|
268
|
+
routes.delete(attachment);
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Frame,
|
|
3
|
+
FrameFilter,
|
|
4
|
+
Timeline,
|
|
5
|
+
TimelineSnapshot,
|
|
6
|
+
} from "../../../entities/frame/index.js";
|
|
7
|
+
import type {
|
|
8
|
+
AnyWirePlugin,
|
|
9
|
+
Attachment,
|
|
10
|
+
PluginApis,
|
|
11
|
+
WaitForOptions,
|
|
12
|
+
} from "../../../entities/plugin/index.js";
|
|
13
|
+
import type {
|
|
14
|
+
ContextPort,
|
|
15
|
+
PagePort,
|
|
16
|
+
} from "../../../shared/api/playwright/index.js";
|
|
17
|
+
import type { StreamServerOptions } from "../../../shared/api/stream-server/index.js";
|
|
18
|
+
import type { Clock } from "../../../shared/lib/clock/index.js";
|
|
19
|
+
|
|
20
|
+
export interface StageOptions<Plugins extends readonly AnyWirePlugin[]> {
|
|
21
|
+
/** The protocols this stage handles. Their APIs appear as `stage.<name>`. */
|
|
22
|
+
readonly plugins: Plugins;
|
|
23
|
+
/** Used by drive clients that are not given a URL of their own. */
|
|
24
|
+
readonly baseURL?: string;
|
|
25
|
+
/** Frames the timeline keeps. Default 100,000. */
|
|
26
|
+
readonly capacity?: number;
|
|
27
|
+
/**
|
|
28
|
+
* How long a page batches what it records before sending it on. Default
|
|
29
|
+
* 25 ms. The first entry after a quiet spell is sent at once.
|
|
30
|
+
*/
|
|
31
|
+
readonly pageFlushMs?: number;
|
|
32
|
+
/**
|
|
33
|
+
* How long a page may take to answer a flush or a command before a read
|
|
34
|
+
* goes on without it — and Playwright to take the page hooks out before a
|
|
35
|
+
* detach goes on. Default 2000 ms.
|
|
36
|
+
*/
|
|
37
|
+
readonly pageTimeoutMs?: number;
|
|
38
|
+
/** TLS for live streams on https pages, and the bind host. */
|
|
39
|
+
readonly streamServer?: Pick<StreamServerOptions, "tls" | "host">;
|
|
40
|
+
/** Defaults for `waitFor`. */
|
|
41
|
+
readonly wait?: Pick<WaitForOptions, "timeout" | "interval">;
|
|
42
|
+
/** Replace time, for tests of the stage itself. */
|
|
43
|
+
readonly clock?: Clock;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface AttachOptions {
|
|
47
|
+
/** The context's base URL, for drive clients bound to it. */
|
|
48
|
+
readonly baseURL?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface StageReport {
|
|
52
|
+
readonly snapshot: TimelineSnapshot;
|
|
53
|
+
readonly text: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface ReportOptions {
|
|
57
|
+
/** Frames kept, counting back from the newest. Default 1,000. */
|
|
58
|
+
readonly limit?: number;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The stage itself, before plugin APIs are mounted on it. */
|
|
62
|
+
export interface StageCore {
|
|
63
|
+
readonly id: string;
|
|
64
|
+
/** The raw timeline. Reads here do NOT flush page buffers first; prefer the methods below. */
|
|
65
|
+
readonly timeline: Timeline;
|
|
66
|
+
readonly attachments: readonly Attachment[];
|
|
67
|
+
readonly disposed: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Start handling a browser context: install every plugin's routes and page
|
|
70
|
+
* hooks. Call before the first navigation. Returns a function that
|
|
71
|
+
* detaches. A failed attach is undone, so it can be tried again.
|
|
72
|
+
*/
|
|
73
|
+
attach(
|
|
74
|
+
context: ContextPort,
|
|
75
|
+
options?: AttachOptions,
|
|
76
|
+
): Promise<() => Promise<void>>;
|
|
77
|
+
/** Detach one context, or all of them. */
|
|
78
|
+
detach(context?: ContextPort): Promise<void>;
|
|
79
|
+
/** The id frames use for this page. */
|
|
80
|
+
pageId(page: PagePort): string;
|
|
81
|
+
/**
|
|
82
|
+
* Bring the timeline up to date: flush every page's buffer and wait for
|
|
83
|
+
* the responses already back from the network. The reads below do it for
|
|
84
|
+
* you.
|
|
85
|
+
*/
|
|
86
|
+
sync(): Promise<void>;
|
|
87
|
+
frames(filter?: FrameFilter): Promise<Frame[]>;
|
|
88
|
+
/** Frames since the previous `drain` (any filter), narrowed by `filter`. */
|
|
89
|
+
drain(filter?: FrameFilter): Promise<Frame[]>;
|
|
90
|
+
/** Put a named marker on the timeline, after everything that happened so far. */
|
|
91
|
+
mark(name: string, meta?: Readonly<Record<string, unknown>>): Promise<Frame>;
|
|
92
|
+
/** Frames after the most recent marker called `marker`. */
|
|
93
|
+
since(marker: string, filter?: FrameFilter): Promise<Frame[]>;
|
|
94
|
+
/** Frames between two markers, exclusive. */
|
|
95
|
+
between(from: string, to: string, filter?: FrameFilter): Promise<Frame[]>;
|
|
96
|
+
/** Resolve with the matching frames once `count` of them exist. */
|
|
97
|
+
waitFor(filter: FrameFilter, options?: WaitForOptions): Promise<Frame[]>;
|
|
98
|
+
/** The newest frames as JSON and as text. Does not flush: `sync()` first. */
|
|
99
|
+
report(filter?: FrameFilter, options?: ReportOptions): StageReport;
|
|
100
|
+
/**
|
|
101
|
+
* Detach everything, stop servers, release plugins. Idempotent. Every
|
|
102
|
+
* step runs even when one fails; what failed is thrown at the end.
|
|
103
|
+
*/
|
|
104
|
+
dispose(): Promise<void>;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export type Stage<Plugins extends readonly AnyWirePlugin[] = readonly []> =
|
|
108
|
+
StageCore & PluginApis<Plugins>;
|