@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,620 @@
|
|
|
1
|
+
import type { FrameInit, Timeline } from "../../../entities/frame/index.js";
|
|
2
|
+
import type {
|
|
3
|
+
HttpOutcome,
|
|
4
|
+
HttpRequestView,
|
|
5
|
+
HttpRouter,
|
|
6
|
+
HttpRule,
|
|
7
|
+
HttpRuleHandle,
|
|
8
|
+
PassthroughResponse,
|
|
9
|
+
} from "../../../entities/plugin/index.js";
|
|
10
|
+
import type {
|
|
11
|
+
PagePort,
|
|
12
|
+
RequestPort,
|
|
13
|
+
RouteHandler,
|
|
14
|
+
RoutePort,
|
|
15
|
+
UrlPredicate,
|
|
16
|
+
} from "../../../shared/api/playwright/index.js";
|
|
17
|
+
import type { StreamServer } from "../../../shared/api/stream-server/index.js";
|
|
18
|
+
import { toBuffer, toBytes, utf8 } from "../../../shared/lib/bytes/index.js";
|
|
19
|
+
import { WirestageError } from "../../../shared/lib/errors/index.js";
|
|
20
|
+
import type { ScopedLogger } from "../../../shared/lib/logger/index.js";
|
|
21
|
+
import { compileUrlMatcher } from "../../../shared/lib/match/index.js";
|
|
22
|
+
import type { AttachedContext } from "./attached-context.js";
|
|
23
|
+
import { createResponseStream } from "./response-stream.js";
|
|
24
|
+
|
|
25
|
+
export interface HttpRouterDeps {
|
|
26
|
+
readonly timeline: Timeline;
|
|
27
|
+
readonly log: ScopedLogger;
|
|
28
|
+
readonly streams: Pick<StreamServer, "open">;
|
|
29
|
+
readonly pageId: (page: PagePort) => string;
|
|
30
|
+
readonly correlation: () => string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface HttpRouterEngine extends HttpRouter {
|
|
34
|
+
attach(attachment: AttachedContext): Promise<void>;
|
|
35
|
+
detach(attachment: AttachedContext): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Wait for the responses that already came back to be on the timeline:
|
|
38
|
+
* reading a passed-through response takes a round trip to the browser.
|
|
39
|
+
*/
|
|
40
|
+
settle(): Promise<void>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface Entry {
|
|
44
|
+
readonly rule: HttpRule;
|
|
45
|
+
readonly matchesUrl: (url: URL) => boolean;
|
|
46
|
+
readonly methods: ReadonlySet<string> | undefined;
|
|
47
|
+
remaining: number | undefined;
|
|
48
|
+
hits: number;
|
|
49
|
+
active: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* One request, as every rule that sees it shares it: one view, one
|
|
54
|
+
* correlation id, one raw request frame per protocol.
|
|
55
|
+
*/
|
|
56
|
+
interface Exchange {
|
|
57
|
+
readonly view: HttpRequestView;
|
|
58
|
+
readonly attachment: AttachedContext;
|
|
59
|
+
readonly route: RoutePort;
|
|
60
|
+
readonly request: RequestPort;
|
|
61
|
+
/** Protocol → endpoint of the raw request frames on the timeline. */
|
|
62
|
+
readonly recorded: Map<string, string>;
|
|
63
|
+
/** Rules that passed the request on, newest first. */
|
|
64
|
+
readonly passed: Entry[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface Installed {
|
|
68
|
+
readonly predicate: UrlPredicate;
|
|
69
|
+
readonly handler: RouteHandler;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface Listeners {
|
|
73
|
+
readonly finished: (request: RequestPort) => void;
|
|
74
|
+
readonly failed: (request: RequestPort) => void;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** A frame that ends an exchange, before the exchange's own fields. */
|
|
78
|
+
type Closing = Omit<
|
|
79
|
+
FrameInit,
|
|
80
|
+
"protocol" | "endpoint" | "page" | "context" | "correlation"
|
|
81
|
+
>;
|
|
82
|
+
|
|
83
|
+
type Answer = Exclude<HttpOutcome, { readonly type: "fallback" }>;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* How each outcome is carried out. A table keyed by the outcome's `type`,
|
|
87
|
+
* not a `switch`: an outcome added to the union and not here is a compile
|
|
88
|
+
* error, where a missing `case` would let the request hang.
|
|
89
|
+
*/
|
|
90
|
+
type Executors = {
|
|
91
|
+
readonly [Type in Answer["type"]]: (
|
|
92
|
+
outcome: Extract<Answer, { readonly type: Type }>,
|
|
93
|
+
exchange: Exchange,
|
|
94
|
+
entry: Entry,
|
|
95
|
+
) => Promise<void>;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Turns HTTP rules into ONE Playwright route per attached context and runs
|
|
100
|
+
* the chain itself: the newest rule that matches decides, and a `fallback`
|
|
101
|
+
* hands the request to the next.
|
|
102
|
+
*
|
|
103
|
+
* Running the chain here, rather than as one Playwright route per rule, is
|
|
104
|
+
* what makes a request one exchange however many rules see it: they share a
|
|
105
|
+
* view and a correlation id, `times` is claimed without an await between the
|
|
106
|
+
* check and the count, and a rule that passed a request on hears about the
|
|
107
|
+
* network's answer only when the network really gave one.
|
|
108
|
+
*/
|
|
109
|
+
export function createHttpRouter(deps: HttpRouterDeps): HttpRouterEngine {
|
|
110
|
+
/** Active rules, oldest first. */
|
|
111
|
+
const entries: Entry[] = [];
|
|
112
|
+
const attachments = new Map<AttachedContext, Listeners>();
|
|
113
|
+
const routes = new Map<AttachedContext, Promise<Installed>>();
|
|
114
|
+
const pending = new WeakMap<RequestPort, Exchange>();
|
|
115
|
+
const localNetwork = new WeakMap<AttachedContext, Promise<void>>();
|
|
116
|
+
/** Responses being read off the browser, on their way to the timeline. */
|
|
117
|
+
const settling = new Set<Promise<void>>();
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Chromium's Local Network Access blocks a page whose address space it
|
|
121
|
+
* cannot place — every page a route fulfilled, and any remote origin — from
|
|
122
|
+
* reaching the loopback stream server. Granted once per context, the first
|
|
123
|
+
* time a stream needs it, and every stream waits for that one grant; a
|
|
124
|
+
* browser without the permission simply refuses it, and then needs none.
|
|
125
|
+
*/
|
|
126
|
+
const allowLocalNetwork = (attachment: AttachedContext): Promise<void> => {
|
|
127
|
+
let granting = localNetwork.get(attachment);
|
|
128
|
+
if (granting === undefined) {
|
|
129
|
+
granting = Promise.resolve()
|
|
130
|
+
.then(() =>
|
|
131
|
+
attachment.context.grantPermissions?.(["local-network-access"]),
|
|
132
|
+
)
|
|
133
|
+
.catch((error: unknown) => {
|
|
134
|
+
deps.log.debug("local-network-access not granted", {}, error);
|
|
135
|
+
});
|
|
136
|
+
localNetwork.set(attachment, granting);
|
|
137
|
+
}
|
|
138
|
+
return granting;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const createView = async (
|
|
142
|
+
request: RequestPort,
|
|
143
|
+
attachment: AttachedContext,
|
|
144
|
+
url: URL,
|
|
145
|
+
): Promise<HttpRequestView> => {
|
|
146
|
+
let body: Uint8Array | undefined;
|
|
147
|
+
let correlation: string | undefined;
|
|
148
|
+
const getBody = () => {
|
|
149
|
+
body ??= toBytes(request.postDataBuffer() ?? new Uint8Array(0));
|
|
150
|
+
return body;
|
|
151
|
+
};
|
|
152
|
+
let page: string | undefined;
|
|
153
|
+
try {
|
|
154
|
+
page = deps.pageId(request.frame().page());
|
|
155
|
+
} catch {
|
|
156
|
+
// Service-worker requests have no frame.
|
|
157
|
+
page = undefined;
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
get correlation() {
|
|
161
|
+
correlation ??= deps.correlation();
|
|
162
|
+
return correlation;
|
|
163
|
+
},
|
|
164
|
+
method: request.method(),
|
|
165
|
+
url,
|
|
166
|
+
headers: await request.allHeaders(),
|
|
167
|
+
page,
|
|
168
|
+
context: attachment.id,
|
|
169
|
+
resourceType: request.resourceType(),
|
|
170
|
+
body: getBody,
|
|
171
|
+
text: () => utf8(getBody()),
|
|
172
|
+
json: <T>() => JSON.parse(utf8(getBody())) as T,
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const append = (
|
|
177
|
+
{ view }: Exchange,
|
|
178
|
+
protocol: string,
|
|
179
|
+
endpoint: string,
|
|
180
|
+
frame: Closing,
|
|
181
|
+
) =>
|
|
182
|
+
deps.timeline.append({
|
|
183
|
+
...frame,
|
|
184
|
+
protocol,
|
|
185
|
+
endpoint,
|
|
186
|
+
page: view.page,
|
|
187
|
+
context: view.context,
|
|
188
|
+
correlation: view.correlation,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
/** End the exchange for every protocol that recorded its request. */
|
|
192
|
+
const close = (exchange: Exchange, frame: Closing) => {
|
|
193
|
+
for (const [protocol, endpoint] of exchange.recorded)
|
|
194
|
+
append(exchange, protocol, endpoint, frame);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* A rule's URL test, which is the caller's code when the rule was given a
|
|
199
|
+
* predicate. Playwright calls this for every request of the context, so
|
|
200
|
+
* one that throws would break them all: it counts as "no match" instead.
|
|
201
|
+
*/
|
|
202
|
+
const wants = (entry: Entry, url: URL): boolean => {
|
|
203
|
+
try {
|
|
204
|
+
return entry.matchesUrl(url);
|
|
205
|
+
} catch (error) {
|
|
206
|
+
deps.log.error(
|
|
207
|
+
"rule's URL matcher failed",
|
|
208
|
+
{ rule: entry.rule.label, url: url.href },
|
|
209
|
+
error,
|
|
210
|
+
);
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
/** The rule's name for the request; the path when it has none to give. */
|
|
216
|
+
const nameOf = ({ rule }: Entry, view: HttpRequestView): string => {
|
|
217
|
+
try {
|
|
218
|
+
return rule.endpoint?.(view) ?? view.url.pathname;
|
|
219
|
+
} catch {
|
|
220
|
+
// Reached only while reporting a rule that already failed.
|
|
221
|
+
return view.url.pathname;
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const retire = (entry: Entry) => {
|
|
226
|
+
if (!entry.active) return;
|
|
227
|
+
entry.active = false;
|
|
228
|
+
entries.splice(entries.indexOf(entry), 1);
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Whether `entry` takes the request, and its decision if so. The checks
|
|
233
|
+
* and the count are one synchronous step: two requests in flight cannot
|
|
234
|
+
* both take the last of a rule's `times`.
|
|
235
|
+
*/
|
|
236
|
+
const decide = async (
|
|
237
|
+
entry: Entry,
|
|
238
|
+
exchange: Exchange,
|
|
239
|
+
): Promise<HttpOutcome | undefined> => {
|
|
240
|
+
const { rule } = entry;
|
|
241
|
+
const { view } = exchange;
|
|
242
|
+
if (!entry.active) return undefined;
|
|
243
|
+
if (entry.methods !== undefined && !entry.methods.has(view.method))
|
|
244
|
+
return undefined;
|
|
245
|
+
if (rule.match !== undefined && !rule.match(view)) return undefined;
|
|
246
|
+
entry.hits += 1;
|
|
247
|
+
if (entry.remaining !== undefined && --entry.remaining === 0) retire(entry);
|
|
248
|
+
if (rule.record !== false && !exchange.recorded.has(rule.protocol)) {
|
|
249
|
+
const endpoint = rule.endpoint?.(view) ?? view.url.pathname;
|
|
250
|
+
exchange.recorded.set(rule.protocol, endpoint);
|
|
251
|
+
append(exchange, rule.protocol, endpoint, {
|
|
252
|
+
kind: "request",
|
|
253
|
+
direction: "out",
|
|
254
|
+
source: "page",
|
|
255
|
+
data: () => view.body(),
|
|
256
|
+
meta: {
|
|
257
|
+
method: view.method,
|
|
258
|
+
url: view.url.href,
|
|
259
|
+
headers: view.headers,
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
return rule.handle(view);
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
/** The request goes to the network: remember who wants to hear the answer. */
|
|
267
|
+
const track = (exchange: Exchange) => {
|
|
268
|
+
if (
|
|
269
|
+
exchange.recorded.size > 0 ||
|
|
270
|
+
exchange.passed.some(
|
|
271
|
+
({ rule }) =>
|
|
272
|
+
rule.onPassthroughResponse !== undefined ||
|
|
273
|
+
rule.onPassthroughFailure !== undefined,
|
|
274
|
+
)
|
|
275
|
+
)
|
|
276
|
+
pending.set(exchange.request, exchange);
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
const execute: Executors = {
|
|
280
|
+
async fulfill(outcome, exchange) {
|
|
281
|
+
const status = outcome.status ?? 200;
|
|
282
|
+
const headers = { ...outcome.headers };
|
|
283
|
+
const body =
|
|
284
|
+
outcome.body === undefined ? undefined : toBytes(outcome.body);
|
|
285
|
+
await exchange.route.fulfill(
|
|
286
|
+
body === undefined
|
|
287
|
+
? { status, headers }
|
|
288
|
+
: { status, headers, body: toBuffer(body) },
|
|
289
|
+
);
|
|
290
|
+
close(exchange, {
|
|
291
|
+
kind: "response",
|
|
292
|
+
direction: "in",
|
|
293
|
+
source: "stub",
|
|
294
|
+
...(body === undefined ? {} : { data: body }),
|
|
295
|
+
meta: { status, headers },
|
|
296
|
+
});
|
|
297
|
+
},
|
|
298
|
+
async stream(outcome, exchange) {
|
|
299
|
+
const session = await deps.streams.open(
|
|
300
|
+
(request, response) => {
|
|
301
|
+
const stream = createResponseStream(request, response);
|
|
302
|
+
void stream.closed.then((end) =>
|
|
303
|
+
close(
|
|
304
|
+
exchange,
|
|
305
|
+
end === "cut"
|
|
306
|
+
? {
|
|
307
|
+
kind: "fault",
|
|
308
|
+
direction: "none",
|
|
309
|
+
source: "stub",
|
|
310
|
+
meta: { reason: "cut" },
|
|
311
|
+
}
|
|
312
|
+
: {
|
|
313
|
+
kind: "lifecycle",
|
|
314
|
+
direction: "none",
|
|
315
|
+
// The page let go of it, or the stub ended it.
|
|
316
|
+
source: end === "cancelled" ? "page" : "stub",
|
|
317
|
+
meta: { event: "close", end },
|
|
318
|
+
},
|
|
319
|
+
),
|
|
320
|
+
);
|
|
321
|
+
return outcome.open(stream);
|
|
322
|
+
},
|
|
323
|
+
{ secure: exchange.view.url.protocol === "https:" },
|
|
324
|
+
);
|
|
325
|
+
await allowLocalNetwork(exchange.attachment);
|
|
326
|
+
try {
|
|
327
|
+
await exchange.route.continue({ url: session.url });
|
|
328
|
+
} catch (error) {
|
|
329
|
+
session.dispose();
|
|
330
|
+
throw error;
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
async continue(outcome, exchange, entry) {
|
|
334
|
+
exchange.passed.push(entry);
|
|
335
|
+
track(exchange);
|
|
336
|
+
await exchange.route.continue({
|
|
337
|
+
...(outcome.url === undefined ? {} : { url: outcome.url }),
|
|
338
|
+
...(outcome.method === undefined ? {} : { method: outcome.method }),
|
|
339
|
+
...(outcome.headers === undefined
|
|
340
|
+
? {}
|
|
341
|
+
: { headers: { ...outcome.headers } }),
|
|
342
|
+
...(outcome.body === undefined
|
|
343
|
+
? {}
|
|
344
|
+
: { postData: toBuffer(toBytes(outcome.body)) }),
|
|
345
|
+
});
|
|
346
|
+
},
|
|
347
|
+
async abort(outcome, exchange) {
|
|
348
|
+
const error = outcome.error ?? "failed";
|
|
349
|
+
await exchange.route.abort(error);
|
|
350
|
+
close(exchange, {
|
|
351
|
+
kind: "fault",
|
|
352
|
+
direction: "none",
|
|
353
|
+
source: "stub",
|
|
354
|
+
meta: { reason: "abort", error },
|
|
355
|
+
});
|
|
356
|
+
},
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* A throwing stub is a bug in the test, and a stream that cannot open is a
|
|
361
|
+
* setup problem. Either way answer 500 so the page sees an error instead of
|
|
362
|
+
* hanging, and say which rule failed — in the log AND in the body the app
|
|
363
|
+
* may display.
|
|
364
|
+
*/
|
|
365
|
+
const fail = async (exchange: Exchange, entry: Entry, error: unknown) => {
|
|
366
|
+
const { rule } = entry;
|
|
367
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
368
|
+
// A request that outlived its context fails for that reason alone.
|
|
369
|
+
const level = attachments.has(exchange.attachment) ? "error" : "debug";
|
|
370
|
+
deps.log[level](
|
|
371
|
+
"rule failed",
|
|
372
|
+
{ rule: rule.label, url: exchange.view.url.href },
|
|
373
|
+
error,
|
|
374
|
+
);
|
|
375
|
+
append(exchange, rule.protocol, nameOf(entry, exchange.view), {
|
|
376
|
+
kind: "fault",
|
|
377
|
+
direction: "none",
|
|
378
|
+
source: "stub",
|
|
379
|
+
meta: { reason: "rule-error", error: message },
|
|
380
|
+
});
|
|
381
|
+
await exchange.route
|
|
382
|
+
.fulfill({
|
|
383
|
+
status: 500,
|
|
384
|
+
headers: { "content-type": "text/plain; charset=utf-8" },
|
|
385
|
+
body: `wirestage: rule "${rule.label}" failed: ${message}`,
|
|
386
|
+
})
|
|
387
|
+
.catch((secondary: unknown) => {
|
|
388
|
+
// The route was already answered before the failure.
|
|
389
|
+
deps.log.debug("could not answer a failed rule", {}, secondary);
|
|
390
|
+
});
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
const dispatch = async (
|
|
394
|
+
attachment: AttachedContext,
|
|
395
|
+
route: RoutePort,
|
|
396
|
+
request: RequestPort,
|
|
397
|
+
): Promise<void> => {
|
|
398
|
+
const url = new URL(request.url());
|
|
399
|
+
// Newest first. A snapshot: a rule added while this request is being
|
|
400
|
+
// decided does not join its chain.
|
|
401
|
+
const chain = entries.filter((entry) => wants(entry, url)).reverse();
|
|
402
|
+
if (chain.length === 0) return route.fallback();
|
|
403
|
+
const exchange: Exchange = {
|
|
404
|
+
view: await createView(request, attachment, url),
|
|
405
|
+
attachment,
|
|
406
|
+
route,
|
|
407
|
+
request,
|
|
408
|
+
recorded: new Map(),
|
|
409
|
+
passed: [],
|
|
410
|
+
};
|
|
411
|
+
for (const entry of chain) {
|
|
412
|
+
try {
|
|
413
|
+
const outcome = await decide(entry, exchange);
|
|
414
|
+
if (outcome === undefined) continue;
|
|
415
|
+
if (outcome.type === "fallback") {
|
|
416
|
+
exchange.passed.push(entry);
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
// The table has an executor for every answer; the cast is only
|
|
420
|
+
// because TypeScript cannot tie `outcome` to its own key.
|
|
421
|
+
await (execute[outcome.type] as Executors[Answer["type"]])(
|
|
422
|
+
outcome as never,
|
|
423
|
+
exchange,
|
|
424
|
+
entry,
|
|
425
|
+
);
|
|
426
|
+
} catch (error) {
|
|
427
|
+
await fail(exchange, entry, error);
|
|
428
|
+
}
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
track(exchange);
|
|
432
|
+
await route.fallback();
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
const onRoute = async (
|
|
436
|
+
attachment: AttachedContext,
|
|
437
|
+
route: RoutePort,
|
|
438
|
+
request: RequestPort,
|
|
439
|
+
): Promise<void> => {
|
|
440
|
+
try {
|
|
441
|
+
await dispatch(attachment, route, request);
|
|
442
|
+
} catch (error) {
|
|
443
|
+
// Reading the request or handing it on failed: the page or context
|
|
444
|
+
// closed under it. Never rejected to Playwright, where it would
|
|
445
|
+
// surface as an unhandled rejection in whichever test runs next.
|
|
446
|
+
const level = attachments.has(attachment) ? "warn" : "debug";
|
|
447
|
+
deps.log[level]("request abandoned", { url: request.url() }, error);
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
const ensureRoute = async (attachment: AttachedContext): Promise<void> => {
|
|
452
|
+
let installing = routes.get(attachment);
|
|
453
|
+
if (installing === undefined) {
|
|
454
|
+
const installed: Installed = {
|
|
455
|
+
predicate: (url) => entries.some((entry) => wants(entry, url)),
|
|
456
|
+
handler: (route, request) => onRoute(attachment, route, request),
|
|
457
|
+
};
|
|
458
|
+
installing = attachment.context
|
|
459
|
+
.route(installed.predicate, installed.handler)
|
|
460
|
+
.then(() => installed);
|
|
461
|
+
routes.set(attachment, installing);
|
|
462
|
+
// A failed install must not be remembered as done.
|
|
463
|
+
installing.catch(() => routes.delete(attachment));
|
|
464
|
+
}
|
|
465
|
+
await installing;
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
/** One observer failing must not starve the others, nor the timeline. */
|
|
469
|
+
const notify = (entry: Entry, call: (rule: HttpRule) => void) => {
|
|
470
|
+
try {
|
|
471
|
+
call(entry.rule);
|
|
472
|
+
} catch (error) {
|
|
473
|
+
deps.log.error(
|
|
474
|
+
"passthrough observer failed",
|
|
475
|
+
{ rule: entry.rule.label },
|
|
476
|
+
error,
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
const onFinished = async (request: RequestPort) => {
|
|
482
|
+
const exchange = pending.get(request);
|
|
483
|
+
if (exchange === undefined) return;
|
|
484
|
+
pending.delete(request);
|
|
485
|
+
try {
|
|
486
|
+
const response = await request.response();
|
|
487
|
+
if (response === null) return;
|
|
488
|
+
const body = await response.body().then(
|
|
489
|
+
(buffer) => toBytes(buffer),
|
|
490
|
+
() => undefined,
|
|
491
|
+
);
|
|
492
|
+
const passthrough: PassthroughResponse = {
|
|
493
|
+
status: response.status(),
|
|
494
|
+
headers: response.headers(),
|
|
495
|
+
body,
|
|
496
|
+
};
|
|
497
|
+
close(exchange, {
|
|
498
|
+
kind: "response",
|
|
499
|
+
direction: "in",
|
|
500
|
+
source: "network",
|
|
501
|
+
...(body === undefined ? {} : { data: body }),
|
|
502
|
+
meta: { status: passthrough.status, headers: passthrough.headers },
|
|
503
|
+
});
|
|
504
|
+
for (const entry of exchange.passed)
|
|
505
|
+
notify(entry, (rule) =>
|
|
506
|
+
rule.onPassthroughResponse?.(passthrough, exchange.view),
|
|
507
|
+
);
|
|
508
|
+
} catch (error) {
|
|
509
|
+
deps.log.warn(
|
|
510
|
+
"could not read a passed-through response",
|
|
511
|
+
{ url: exchange.view.url.href },
|
|
512
|
+
error,
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
const onFailed = (request: RequestPort) => {
|
|
518
|
+
const exchange = pending.get(request);
|
|
519
|
+
if (exchange === undefined) return;
|
|
520
|
+
pending.delete(request);
|
|
521
|
+
const error = request.failure()?.errorText;
|
|
522
|
+
close(exchange, {
|
|
523
|
+
kind: "fault",
|
|
524
|
+
direction: "none",
|
|
525
|
+
source: "network",
|
|
526
|
+
meta: { reason: "network", error },
|
|
527
|
+
});
|
|
528
|
+
for (const entry of exchange.passed)
|
|
529
|
+
notify(entry, (rule) =>
|
|
530
|
+
rule.onPassthroughFailure?.(error, exchange.view),
|
|
531
|
+
);
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
return {
|
|
535
|
+
async add(rule): Promise<HttpRuleHandle> {
|
|
536
|
+
if (
|
|
537
|
+
rule.times !== undefined &&
|
|
538
|
+
(!Number.isInteger(rule.times) || rule.times < 1)
|
|
539
|
+
)
|
|
540
|
+
throw new WirestageError(
|
|
541
|
+
"INVALID_ARGUMENT",
|
|
542
|
+
`rule "${rule.label}": times must be a positive integer, got ${rule.times}`,
|
|
543
|
+
);
|
|
544
|
+
const methods =
|
|
545
|
+
rule.method === undefined
|
|
546
|
+
? undefined
|
|
547
|
+
: new Set(
|
|
548
|
+
(typeof rule.method === "string"
|
|
549
|
+
? [rule.method]
|
|
550
|
+
: rule.method
|
|
551
|
+
).map((m) => m.toUpperCase()),
|
|
552
|
+
);
|
|
553
|
+
const entry: Entry = {
|
|
554
|
+
rule,
|
|
555
|
+
matchesUrl: compileUrlMatcher(rule.url),
|
|
556
|
+
methods,
|
|
557
|
+
remaining: rule.times,
|
|
558
|
+
hits: 0,
|
|
559
|
+
active: true,
|
|
560
|
+
};
|
|
561
|
+
entries.push(entry);
|
|
562
|
+
try {
|
|
563
|
+
await Promise.all([...attachments.keys()].map(ensureRoute));
|
|
564
|
+
} catch (error) {
|
|
565
|
+
// Half-installed is worse than not installed: the caller got an
|
|
566
|
+
// error, so the rule must not answer anywhere.
|
|
567
|
+
retire(entry);
|
|
568
|
+
throw error;
|
|
569
|
+
}
|
|
570
|
+
return {
|
|
571
|
+
get hits() {
|
|
572
|
+
return entry.hits;
|
|
573
|
+
},
|
|
574
|
+
get active() {
|
|
575
|
+
return entry.active;
|
|
576
|
+
},
|
|
577
|
+
async remove() {
|
|
578
|
+
retire(entry);
|
|
579
|
+
},
|
|
580
|
+
};
|
|
581
|
+
},
|
|
582
|
+
async attach(attachment) {
|
|
583
|
+
const own: Listeners = {
|
|
584
|
+
finished: (request) => {
|
|
585
|
+
const reading = onFinished(request).finally(() =>
|
|
586
|
+
settling.delete(reading),
|
|
587
|
+
);
|
|
588
|
+
settling.add(reading);
|
|
589
|
+
},
|
|
590
|
+
failed: onFailed,
|
|
591
|
+
};
|
|
592
|
+
attachments.set(attachment, own);
|
|
593
|
+
attachment.context.on("requestfinished", own.finished);
|
|
594
|
+
attachment.context.on("requestfailed", own.failed);
|
|
595
|
+
// Routing turns the browser's HTTP cache off and puts every request
|
|
596
|
+
// through Playwright, so a stage with no rules installs nothing.
|
|
597
|
+
if (entries.length > 0) await ensureRoute(attachment);
|
|
598
|
+
},
|
|
599
|
+
async detach(attachment) {
|
|
600
|
+
const own = attachments.get(attachment);
|
|
601
|
+
if (own === undefined) return;
|
|
602
|
+
attachments.delete(attachment);
|
|
603
|
+
attachment.context.off("requestfinished", own.finished);
|
|
604
|
+
attachment.context.off("requestfailed", own.failed);
|
|
605
|
+
const installing = routes.get(attachment);
|
|
606
|
+
routes.delete(attachment);
|
|
607
|
+
if (installing === undefined) return;
|
|
608
|
+
try {
|
|
609
|
+
const { predicate, handler } = await installing;
|
|
610
|
+
await attachment.context.unroute(predicate, handler);
|
|
611
|
+
} catch (error) {
|
|
612
|
+
// A context that closed first has no route left to remove.
|
|
613
|
+
deps.log.debug("unroute failed", { context: attachment.id }, error);
|
|
614
|
+
}
|
|
615
|
+
},
|
|
616
|
+
async settle() {
|
|
617
|
+
await Promise.all(settling);
|
|
618
|
+
},
|
|
619
|
+
};
|
|
620
|
+
}
|