@heojeongbo/wirestage 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/LICENSES.md +21 -0
- package/README.md +150 -2
- package/dist/app/playwright/index.d.ts +3 -0
- package/dist/app/playwright/index.d.ts.map +1 -0
- package/dist/app/playwright/index.js +2 -0
- package/dist/app/playwright/index.js.map +1 -0
- package/dist/app/playwright/lib/fixture.d.ts +61 -0
- package/dist/app/playwright/lib/fixture.d.ts.map +1 -0
- package/dist/app/playwright/lib/fixture.js +110 -0
- package/dist/app/playwright/lib/fixture.js.map +1 -0
- package/dist/connect.d.ts +7 -0
- package/dist/connect.d.ts.map +1 -0
- package/dist/connect.js +7 -0
- package/dist/connect.js.map +1 -0
- package/dist/entities/frame/@x/plugin.d.ts +9 -0
- package/dist/entities/frame/@x/plugin.d.ts.map +1 -0
- package/dist/entities/frame/@x/plugin.js +2 -0
- package/dist/entities/frame/@x/plugin.js.map +1 -0
- package/dist/entities/frame/index.d.ts +6 -0
- package/dist/entities/frame/index.d.ts.map +1 -0
- package/dist/entities/frame/index.js +4 -0
- package/dist/entities/frame/index.js.map +1 -0
- package/dist/entities/frame/lib/filter.d.ts +8 -0
- package/dist/entities/frame/lib/filter.d.ts.map +1 -0
- package/dist/entities/frame/lib/filter.js +62 -0
- package/dist/entities/frame/lib/filter.js.map +1 -0
- package/dist/entities/frame/lib/payload.d.ts +8 -0
- package/dist/entities/frame/lib/payload.d.ts.map +1 -0
- package/dist/entities/frame/lib/payload.js +42 -0
- package/dist/entities/frame/lib/payload.js.map +1 -0
- package/dist/entities/frame/lib/snapshot.d.ts +15 -0
- package/dist/entities/frame/lib/snapshot.d.ts.map +1 -0
- package/dist/entities/frame/lib/snapshot.js +90 -0
- package/dist/entities/frame/lib/snapshot.js.map +1 -0
- package/dist/entities/frame/lib/timeline.d.ts +23 -0
- package/dist/entities/frame/lib/timeline.d.ts.map +1 -0
- package/dist/entities/frame/lib/timeline.js +122 -0
- package/dist/entities/frame/lib/timeline.js.map +1 -0
- package/dist/entities/frame/model/frame.d.ts +182 -0
- package/dist/entities/frame/model/frame.d.ts.map +1 -0
- package/dist/entities/frame/model/frame.js +2 -0
- package/dist/entities/frame/model/frame.js.map +1 -0
- package/dist/entities/plugin/index.d.ts +9 -0
- package/dist/entities/plugin/index.d.ts.map +1 -0
- package/dist/entities/plugin/index.js +3 -0
- package/dist/entities/plugin/index.js.map +1 -0
- package/dist/entities/plugin/lib/define-plugin.d.ts +8 -0
- package/dist/entities/plugin/lib/define-plugin.d.ts.map +1 -0
- package/dist/entities/plugin/lib/define-plugin.js +9 -0
- package/dist/entities/plugin/lib/define-plugin.js.map +1 -0
- package/dist/entities/plugin/lib/named.d.ts +10 -0
- package/dist/entities/plugin/lib/named.d.ts.map +1 -0
- package/dist/entities/plugin/lib/named.js +17 -0
- package/dist/entities/plugin/lib/named.js.map +1 -0
- package/dist/entities/plugin/model/attachment.d.ts +18 -0
- package/dist/entities/plugin/model/attachment.d.ts.map +1 -0
- package/dist/entities/plugin/model/attachment.js +2 -0
- package/dist/entities/plugin/model/attachment.js.map +1 -0
- package/dist/entities/plugin/model/http.d.ts +135 -0
- package/dist/entities/plugin/model/http.d.ts.map +1 -0
- package/dist/entities/plugin/model/http.js +2 -0
- package/dist/entities/plugin/model/http.js.map +1 -0
- package/dist/entities/plugin/model/page.d.ts +100 -0
- package/dist/entities/plugin/model/page.d.ts.map +1 -0
- package/dist/entities/plugin/model/page.js +2 -0
- package/dist/entities/plugin/model/page.js.map +1 -0
- package/dist/entities/plugin/model/plugin.d.ts +83 -0
- package/dist/entities/plugin/model/plugin.d.ts.map +1 -0
- package/dist/entities/plugin/model/plugin.js +2 -0
- package/dist/entities/plugin/model/plugin.js.map +1 -0
- package/dist/entities/plugin/model/socket.d.ts +107 -0
- package/dist/entities/plugin/model/socket.d.ts.map +1 -0
- package/dist/entities/plugin/model/socket.js +2 -0
- package/dist/entities/plugin/model/socket.js.map +1 -0
- package/dist/entities/plugin/model/stream.d.ts +46 -0
- package/dist/entities/plugin/model/stream.d.ts.map +1 -0
- package/dist/entities/plugin/model/stream.js +2 -0
- package/dist/entities/plugin/model/stream.js.map +1 -0
- package/dist/features/connect/index.d.ts +5 -0
- package/dist/features/connect/index.d.ts.map +1 -0
- package/dist/features/connect/index.js +4 -0
- package/dist/features/connect/index.js.map +1 -0
- package/dist/features/connect/lib/calls.d.ts +9 -0
- package/dist/features/connect/lib/calls.d.ts.map +1 -0
- package/dist/features/connect/lib/calls.js +46 -0
- package/dist/features/connect/lib/calls.js.map +1 -0
- package/dist/features/connect/lib/client.d.ts +33 -0
- package/dist/features/connect/lib/client.d.ts.map +1 -0
- package/dist/features/connect/lib/client.js +147 -0
- package/dist/features/connect/lib/client.js.map +1 -0
- package/dist/features/connect/lib/create-connect.d.ts +14 -0
- package/dist/features/connect/lib/create-connect.d.ts.map +1 -0
- package/dist/features/connect/lib/create-connect.js +124 -0
- package/dist/features/connect/lib/create-connect.js.map +1 -0
- package/dist/features/connect/lib/faults.d.ts +18 -0
- package/dist/features/connect/lib/faults.d.ts.map +1 -0
- package/dist/features/connect/lib/faults.js +85 -0
- package/dist/features/connect/lib/faults.js.map +1 -0
- package/dist/features/connect/lib/protocol.d.ts +42 -0
- package/dist/features/connect/lib/protocol.d.ts.map +1 -0
- package/dist/features/connect/lib/protocol.js +228 -0
- package/dist/features/connect/lib/protocol.js.map +1 -0
- package/dist/features/connect/lib/record.d.ts +14 -0
- package/dist/features/connect/lib/record.d.ts.map +1 -0
- package/dist/features/connect/lib/record.js +36 -0
- package/dist/features/connect/lib/record.js.map +1 -0
- package/dist/features/connect/lib/recorder.d.ts +77 -0
- package/dist/features/connect/lib/recorder.d.ts.map +1 -0
- package/dist/features/connect/lib/recorder.js +209 -0
- package/dist/features/connect/lib/recorder.js.map +1 -0
- package/dist/features/connect/lib/rules.d.ts +51 -0
- package/dist/features/connect/lib/rules.d.ts.map +1 -0
- package/dist/features/connect/lib/rules.js +222 -0
- package/dist/features/connect/lib/rules.js.map +1 -0
- package/dist/features/connect/lib/serve.d.ts +10 -0
- package/dist/features/connect/lib/serve.d.ts.map +1 -0
- package/dist/features/connect/lib/serve.js +78 -0
- package/dist/features/connect/lib/serve.js.map +1 -0
- package/dist/features/connect/lib/status-of.d.ts +11 -0
- package/dist/features/connect/lib/status-of.d.ts.map +1 -0
- package/dist/features/connect/lib/status-of.js +22 -0
- package/dist/features/connect/lib/status-of.js.map +1 -0
- package/dist/features/connect/lib/stubs.d.ts +18 -0
- package/dist/features/connect/lib/stubs.d.ts.map +1 -0
- package/dist/features/connect/lib/stubs.js +174 -0
- package/dist/features/connect/lib/stubs.js.map +1 -0
- package/dist/features/connect/lib/universal.d.ts +19 -0
- package/dist/features/connect/lib/universal.d.ts.map +1 -0
- package/dist/features/connect/lib/universal.js +71 -0
- package/dist/features/connect/lib/universal.js.map +1 -0
- package/dist/features/connect/model/types.d.ts +247 -0
- package/dist/features/connect/model/types.d.ts.map +1 -0
- package/dist/features/connect/model/types.js +2 -0
- package/dist/features/connect/model/types.js.map +1 -0
- package/dist/features/rest/index.d.ts +5 -0
- package/dist/features/rest/index.d.ts.map +1 -0
- package/dist/features/rest/index.js +3 -0
- package/dist/features/rest/index.js.map +1 -0
- package/dist/features/rest/lib/calls.d.ts +8 -0
- package/dist/features/rest/lib/calls.d.ts.map +1 -0
- package/dist/features/rest/lib/calls.js +32 -0
- package/dist/features/rest/lib/calls.js.map +1 -0
- package/dist/features/rest/lib/create-rest.d.ts +8 -0
- package/dist/features/rest/lib/create-rest.d.ts.map +1 -0
- package/dist/features/rest/lib/create-rest.js +136 -0
- package/dist/features/rest/lib/create-rest.js.map +1 -0
- package/dist/features/rest/lib/path.d.ts +14 -0
- package/dist/features/rest/lib/path.d.ts.map +1 -0
- package/dist/features/rest/lib/path.js +58 -0
- package/dist/features/rest/lib/path.js.map +1 -0
- package/dist/features/rest/lib/reply.d.ts +25 -0
- package/dist/features/rest/lib/reply.d.ts.map +1 -0
- package/dist/features/rest/lib/reply.js +40 -0
- package/dist/features/rest/lib/reply.js.map +1 -0
- package/dist/features/rest/model/types.d.ts +74 -0
- package/dist/features/rest/model/types.d.ts.map +1 -0
- package/dist/features/rest/model/types.js +2 -0
- package/dist/features/rest/model/types.js.map +1 -0
- package/dist/features/sse/index.d.ts +4 -0
- package/dist/features/sse/index.d.ts.map +1 -0
- package/dist/features/sse/index.js +3 -0
- package/dist/features/sse/index.js.map +1 -0
- package/dist/features/sse/lib/create-sse.d.ts +9 -0
- package/dist/features/sse/lib/create-sse.d.ts.map +1 -0
- package/dist/features/sse/lib/create-sse.js +121 -0
- package/dist/features/sse/lib/create-sse.js.map +1 -0
- package/dist/features/sse/lib/encode.d.ts +8 -0
- package/dist/features/sse/lib/encode.d.ts.map +1 -0
- package/dist/features/sse/lib/encode.js +29 -0
- package/dist/features/sse/lib/encode.js.map +1 -0
- package/dist/features/sse/model/types.d.ts +68 -0
- package/dist/features/sse/model/types.d.ts.map +1 -0
- package/dist/features/sse/model/types.js +2 -0
- package/dist/features/sse/model/types.js.map +1 -0
- package/dist/features/webrtc/index.d.ts +3 -0
- package/dist/features/webrtc/index.d.ts.map +1 -0
- package/dist/features/webrtc/index.js +2 -0
- package/dist/features/webrtc/index.js.map +1 -0
- package/dist/features/webrtc/lib/create-webrtc.d.ts +9 -0
- package/dist/features/webrtc/lib/create-webrtc.d.ts.map +1 -0
- package/dist/features/webrtc/lib/create-webrtc.js +123 -0
- package/dist/features/webrtc/lib/create-webrtc.js.map +1 -0
- package/dist/features/webrtc/lib/rtc-page-module.d.ts +22 -0
- package/dist/features/webrtc/lib/rtc-page-module.d.ts.map +1 -0
- package/dist/features/webrtc/lib/rtc-page-module.js +161 -0
- package/dist/features/webrtc/lib/rtc-page-module.js.map +1 -0
- package/dist/features/webrtc/lib/to-frame.d.ts +9 -0
- package/dist/features/webrtc/lib/to-frame.d.ts.map +1 -0
- package/dist/features/webrtc/lib/to-frame.js +43 -0
- package/dist/features/webrtc/lib/to-frame.js.map +1 -0
- package/dist/features/webrtc/model/types.d.ts +70 -0
- package/dist/features/webrtc/model/types.d.ts.map +1 -0
- package/dist/features/webrtc/model/types.js +2 -0
- package/dist/features/webrtc/model/types.js.map +1 -0
- package/dist/features/websocket/index.d.ts +3 -0
- package/dist/features/websocket/index.d.ts.map +1 -0
- package/dist/features/websocket/index.js +2 -0
- package/dist/features/websocket/index.js.map +1 -0
- package/dist/features/websocket/lib/create-websocket.d.ts +9 -0
- package/dist/features/websocket/lib/create-websocket.d.ts.map +1 -0
- package/dist/features/websocket/lib/create-websocket.js +34 -0
- package/dist/features/websocket/lib/create-websocket.js.map +1 -0
- package/dist/features/websocket/lib/mock.d.ts +6 -0
- package/dist/features/websocket/lib/mock.d.ts.map +1 -0
- package/dist/features/websocket/lib/mock.js +96 -0
- package/dist/features/websocket/lib/mock.js.map +1 -0
- package/dist/features/websocket/lib/proxy.d.ts +9 -0
- package/dist/features/websocket/lib/proxy.d.ts.map +1 -0
- package/dist/features/websocket/lib/proxy.js +120 -0
- package/dist/features/websocket/lib/proxy.js.map +1 -0
- package/dist/features/websocket/lib/wire.d.ts +16 -0
- package/dist/features/websocket/lib/wire.d.ts.map +1 -0
- package/dist/features/websocket/lib/wire.js +46 -0
- package/dist/features/websocket/lib/wire.js.map +1 -0
- package/dist/features/websocket/model/types.d.ts +133 -0
- package/dist/features/websocket/model/types.d.ts.map +1 -0
- package/dist/features/websocket/model/types.js +2 -0
- package/dist/features/websocket/model/types.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/playwright.d.ts +4 -0
- package/dist/playwright.d.ts.map +1 -0
- package/dist/playwright.js +2 -0
- package/dist/playwright.js.map +1 -0
- package/dist/rest.d.ts +3 -0
- package/dist/rest.d.ts.map +1 -0
- package/dist/rest.js +3 -0
- package/dist/rest.js.map +1 -0
- package/dist/shared/api/http-server/index.d.ts +3 -0
- package/dist/shared/api/http-server/index.d.ts.map +1 -0
- package/dist/shared/api/http-server/index.js +2 -0
- package/dist/shared/api/http-server/index.js.map +1 -0
- package/dist/shared/api/http-server/listen.d.ts +25 -0
- package/dist/shared/api/http-server/listen.d.ts.map +1 -0
- package/dist/shared/api/http-server/listen.js +44 -0
- package/dist/shared/api/http-server/listen.js.map +1 -0
- package/dist/shared/api/playwright/index.d.ts +2 -0
- package/dist/shared/api/playwright/index.d.ts.map +1 -0
- package/dist/shared/api/playwright/index.js +2 -0
- package/dist/shared/api/playwright/index.js.map +1 -0
- package/dist/shared/api/playwright/ports.d.ts +126 -0
- package/dist/shared/api/playwright/ports.d.ts.map +1 -0
- package/dist/shared/api/playwright/ports.js +11 -0
- package/dist/shared/api/playwright/ports.js.map +1 -0
- package/dist/shared/api/stream-server/index.d.ts +3 -0
- package/dist/shared/api/stream-server/index.d.ts.map +1 -0
- package/dist/shared/api/stream-server/index.js +2 -0
- package/dist/shared/api/stream-server/index.js.map +1 -0
- package/dist/shared/api/stream-server/stream-server.d.ts +45 -0
- package/dist/shared/api/stream-server/stream-server.d.ts.map +1 -0
- package/dist/shared/api/stream-server/stream-server.js +167 -0
- package/dist/shared/api/stream-server/stream-server.js.map +1 -0
- package/dist/shared/lib/async/arrivals.d.ts +31 -0
- package/dist/shared/lib/async/arrivals.d.ts.map +1 -0
- package/dist/shared/lib/async/arrivals.js +57 -0
- package/dist/shared/lib/async/arrivals.js.map +1 -0
- package/dist/shared/lib/async/async-queue.d.ts +23 -0
- package/dist/shared/lib/async/async-queue.d.ts.map +1 -0
- package/dist/shared/lib/async/async-queue.js +80 -0
- package/dist/shared/lib/async/async-queue.js.map +1 -0
- package/dist/shared/lib/async/deferred.d.ts +12 -0
- package/dist/shared/lib/async/deferred.d.ts.map +1 -0
- package/dist/shared/lib/async/deferred.js +34 -0
- package/dist/shared/lib/async/deferred.js.map +1 -0
- package/dist/shared/lib/async/index.d.ts +8 -0
- package/dist/shared/lib/async/index.d.ts.map +1 -0
- package/dist/shared/lib/async/index.js +5 -0
- package/dist/shared/lib/async/index.js.map +1 -0
- package/dist/shared/lib/async/run-all.d.ts +8 -0
- package/dist/shared/lib/async/run-all.d.ts.map +1 -0
- package/dist/shared/lib/async/run-all.js +22 -0
- package/dist/shared/lib/async/run-all.js.map +1 -0
- package/dist/shared/lib/bytes/bytes.d.ts +18 -0
- package/dist/shared/lib/bytes/bytes.d.ts.map +1 -0
- package/dist/shared/lib/bytes/bytes.js +48 -0
- package/dist/shared/lib/bytes/bytes.js.map +1 -0
- package/dist/shared/lib/bytes/index.d.ts +3 -0
- package/dist/shared/lib/bytes/index.d.ts.map +1 -0
- package/dist/shared/lib/bytes/index.js +2 -0
- package/dist/shared/lib/bytes/index.js.map +1 -0
- package/dist/shared/lib/clock/clock.d.ts +29 -0
- package/dist/shared/lib/clock/clock.d.ts.map +1 -0
- package/dist/shared/lib/clock/clock.js +85 -0
- package/dist/shared/lib/clock/clock.js.map +1 -0
- package/dist/shared/lib/clock/index.d.ts +3 -0
- package/dist/shared/lib/clock/index.d.ts.map +1 -0
- package/dist/shared/lib/clock/index.js +2 -0
- package/dist/shared/lib/clock/index.js.map +1 -0
- package/dist/shared/lib/cookies/index.d.ts +3 -0
- package/dist/shared/lib/cookies/index.d.ts.map +1 -0
- package/dist/shared/lib/cookies/index.js +2 -0
- package/dist/shared/lib/cookies/index.js.map +1 -0
- package/dist/shared/lib/cookies/set-cookie.d.ts +22 -0
- package/dist/shared/lib/cookies/set-cookie.d.ts.map +1 -0
- package/dist/shared/lib/cookies/set-cookie.js +89 -0
- package/dist/shared/lib/cookies/set-cookie.js.map +1 -0
- package/dist/shared/lib/envelope/envelope.d.ts +50 -0
- package/dist/shared/lib/envelope/envelope.d.ts.map +1 -0
- package/dist/shared/lib/envelope/envelope.js +111 -0
- package/dist/shared/lib/envelope/envelope.js.map +1 -0
- package/dist/shared/lib/envelope/index.d.ts +3 -0
- package/dist/shared/lib/envelope/index.d.ts.map +1 -0
- package/dist/shared/lib/envelope/index.js +2 -0
- package/dist/shared/lib/envelope/index.js.map +1 -0
- package/dist/shared/lib/errors/index.d.ts +3 -0
- package/dist/shared/lib/errors/index.d.ts.map +1 -0
- package/dist/shared/lib/errors/index.js +2 -0
- package/dist/shared/lib/errors/index.js.map +1 -0
- package/dist/shared/lib/errors/wirestage-error.d.ts +28 -0
- package/dist/shared/lib/errors/wirestage-error.d.ts.map +1 -0
- package/dist/shared/lib/errors/wirestage-error.js +16 -0
- package/dist/shared/lib/errors/wirestage-error.js.map +1 -0
- package/dist/shared/lib/id/id.d.ts +10 -0
- package/dist/shared/lib/id/id.d.ts.map +1 -0
- package/dist/shared/lib/id/id.js +26 -0
- package/dist/shared/lib/id/id.js.map +1 -0
- package/dist/shared/lib/id/index.d.ts +2 -0
- package/dist/shared/lib/id/index.d.ts.map +1 -0
- package/dist/shared/lib/id/index.js +2 -0
- package/dist/shared/lib/id/index.js.map +1 -0
- package/dist/shared/lib/logger/index.d.ts +3 -0
- package/dist/shared/lib/logger/index.d.ts.map +1 -0
- package/dist/shared/lib/logger/index.js +2 -0
- package/dist/shared/lib/logger/index.js.map +1 -0
- package/dist/shared/lib/logger/logger.d.ts +46 -0
- package/dist/shared/lib/logger/logger.d.ts.map +1 -0
- package/dist/shared/lib/logger/logger.js +67 -0
- package/dist/shared/lib/logger/logger.js.map +1 -0
- package/dist/shared/lib/match/index.d.ts +3 -0
- package/dist/shared/lib/match/index.d.ts.map +1 -0
- package/dist/shared/lib/match/index.js +2 -0
- package/dist/shared/lib/match/index.js.map +1 -0
- package/dist/shared/lib/match/url-matcher.d.ts +27 -0
- package/dist/shared/lib/match/url-matcher.d.ts.map +1 -0
- package/dist/shared/lib/match/url-matcher.js +88 -0
- package/dist/shared/lib/match/url-matcher.js.map +1 -0
- package/dist/sse.d.ts +3 -0
- package/dist/sse.d.ts.map +1 -0
- package/dist/sse.js +3 -0
- package/dist/sse.js.map +1 -0
- package/dist/webrtc.d.ts +3 -0
- package/dist/webrtc.d.ts.map +1 -0
- package/dist/webrtc.js +3 -0
- package/dist/webrtc.js.map +1 -0
- package/dist/websocket.d.ts +3 -0
- package/dist/websocket.d.ts.map +1 -0
- package/dist/websocket.js +3 -0
- package/dist/websocket.js.map +1 -0
- package/dist/widgets/stage/index.d.ts +3 -0
- package/dist/widgets/stage/index.d.ts.map +1 -0
- package/dist/widgets/stage/index.js +2 -0
- package/dist/widgets/stage/index.js.map +1 -0
- package/dist/widgets/stage/lib/attached-context.d.ts +10 -0
- package/dist/widgets/stage/lib/attached-context.d.ts.map +1 -0
- package/dist/widgets/stage/lib/attached-context.js +2 -0
- package/dist/widgets/stage/lib/attached-context.js.map +1 -0
- package/dist/widgets/stage/lib/create-stage.d.ts +12 -0
- package/dist/widgets/stage/lib/create-stage.d.ts.map +1 -0
- package/dist/widgets/stage/lib/create-stage.js +250 -0
- package/dist/widgets/stage/lib/create-stage.js.map +1 -0
- package/dist/widgets/stage/lib/http-router.d.ts +44 -0
- package/dist/widgets/stage/lib/http-router.d.ts.map +1 -0
- package/dist/widgets/stage/lib/http-router.js +696 -0
- package/dist/widgets/stage/lib/http-router.js.map +1 -0
- package/dist/widgets/stage/lib/page-host.d.ts +36 -0
- package/dist/widgets/stage/lib/page-host.d.ts.map +1 -0
- package/dist/widgets/stage/lib/page-host.js +240 -0
- package/dist/widgets/stage/lib/page-host.js.map +1 -0
- package/dist/widgets/stage/lib/page-runtime.d.ts +93 -0
- package/dist/widgets/stage/lib/page-runtime.d.ts.map +1 -0
- package/dist/widgets/stage/lib/page-runtime.js +247 -0
- package/dist/widgets/stage/lib/page-runtime.js.map +1 -0
- package/dist/widgets/stage/lib/reads.d.ts +21 -0
- package/dist/widgets/stage/lib/reads.d.ts.map +1 -0
- package/dist/widgets/stage/lib/reads.js +97 -0
- package/dist/widgets/stage/lib/reads.js.map +1 -0
- package/dist/widgets/stage/lib/response-stream.d.ts +23 -0
- package/dist/widgets/stage/lib/response-stream.d.ts.map +1 -0
- package/dist/widgets/stage/lib/response-stream.js +108 -0
- package/dist/widgets/stage/lib/response-stream.js.map +1 -0
- package/dist/widgets/stage/lib/socket-router.d.ts +25 -0
- package/dist/widgets/stage/lib/socket-router.d.ts.map +1 -0
- package/dist/widgets/stage/lib/socket-router.js +233 -0
- package/dist/widgets/stage/lib/socket-router.js.map +1 -0
- package/dist/widgets/stage/model/stage.d.ts +88 -0
- package/dist/widgets/stage/model/stage.d.ts.map +1 -0
- package/dist/widgets/stage/model/stage.js +2 -0
- package/dist/widgets/stage/model/stage.js.map +1 -0
- package/package.json +140 -5
- package/src/app/playwright/index.ts +7 -0
- package/src/app/playwright/lib/fixture.ts +201 -0
- package/src/connect.ts +6 -0
- package/src/entities/frame/@x/plugin.ts +8 -0
- package/src/entities/frame/index.ts +19 -0
- package/src/entities/frame/lib/filter.ts +58 -0
- package/src/entities/frame/lib/payload.ts +42 -0
- package/src/entities/frame/lib/snapshot.ts +105 -0
- package/src/entities/frame/lib/timeline.ts +147 -0
- package/src/entities/frame/model/frame.ts +191 -0
- package/src/entities/plugin/index.ts +36 -0
- package/src/entities/plugin/lib/define-plugin.ts +12 -0
- package/src/entities/plugin/lib/named.ts +21 -0
- package/src/entities/plugin/model/attachment.ts +18 -0
- package/src/entities/plugin/model/http.ts +156 -0
- package/src/entities/plugin/model/page.ts +110 -0
- package/src/entities/plugin/model/plugin.ts +96 -0
- package/src/entities/plugin/model/socket.ts +113 -0
- package/src/entities/plugin/model/stream.ts +46 -0
- package/src/features/connect/index.ts +23 -0
- package/src/features/connect/lib/calls.ts +58 -0
- package/src/features/connect/lib/client.ts +203 -0
- package/src/features/connect/lib/create-connect.ts +164 -0
- package/src/features/connect/lib/faults.ts +132 -0
- package/src/features/connect/lib/protocol.ts +321 -0
- package/src/features/connect/lib/record.ts +48 -0
- package/src/features/connect/lib/recorder.ts +306 -0
- package/src/features/connect/lib/rules.ts +385 -0
- package/src/features/connect/lib/serve.ts +114 -0
- package/src/features/connect/lib/status-of.ts +20 -0
- package/src/features/connect/lib/stubs.ts +282 -0
- package/src/features/connect/lib/universal.ts +100 -0
- package/src/features/connect/model/types.ts +305 -0
- package/src/features/rest/index.ts +23 -0
- package/src/features/rest/lib/calls.ts +31 -0
- package/src/features/rest/lib/create-rest.ts +169 -0
- package/src/features/rest/lib/path.ts +70 -0
- package/src/features/rest/lib/reply.ts +74 -0
- package/src/features/rest/model/types.ts +113 -0
- package/src/features/sse/index.ts +10 -0
- package/src/features/sse/lib/create-sse.ts +129 -0
- package/src/features/sse/lib/encode.ts +28 -0
- package/src/features/sse/model/types.ts +72 -0
- package/src/features/webrtc/index.ts +6 -0
- package/src/features/webrtc/lib/create-webrtc.ts +149 -0
- package/src/features/webrtc/lib/rtc-page-module.ts +189 -0
- package/src/features/webrtc/lib/to-frame.ts +63 -0
- package/src/features/webrtc/model/types.ts +80 -0
- package/src/features/websocket/index.ts +15 -0
- package/src/features/websocket/lib/create-websocket.ts +41 -0
- package/src/features/websocket/lib/mock.ts +114 -0
- package/src/features/websocket/lib/proxy.ts +153 -0
- package/src/features/websocket/lib/wire.ts +78 -0
- package/src/features/websocket/model/types.ts +134 -0
- package/src/index.ts +111 -0
- package/src/playwright.ts +8 -0
- package/src/rest.ts +2 -0
- package/src/shared/api/http-server/index.ts +2 -0
- package/src/shared/api/http-server/listen.ts +71 -0
- package/src/shared/api/playwright/index.ts +17 -0
- package/src/shared/api/playwright/ports.ts +150 -0
- package/src/shared/api/stream-server/index.ts +7 -0
- package/src/shared/api/stream-server/stream-server.ts +254 -0
- package/src/shared/lib/async/arrivals.ts +91 -0
- package/src/shared/lib/async/async-queue.ts +101 -0
- package/src/shared/lib/async/deferred.ts +38 -0
- package/src/shared/lib/async/index.ts +11 -0
- package/src/shared/lib/async/run-all.ts +19 -0
- package/src/shared/lib/bytes/bytes.ts +53 -0
- package/src/shared/lib/bytes/index.ts +9 -0
- package/src/shared/lib/clock/clock.ts +114 -0
- package/src/shared/lib/clock/index.ts +2 -0
- package/src/shared/lib/cookies/index.ts +2 -0
- package/src/shared/lib/cookies/set-cookie.ts +105 -0
- package/src/shared/lib/envelope/envelope.ts +133 -0
- package/src/shared/lib/envelope/index.ts +9 -0
- package/src/shared/lib/errors/index.ts +5 -0
- package/src/shared/lib/errors/wirestage-error.ts +50 -0
- package/src/shared/lib/id/id.ts +29 -0
- package/src/shared/lib/id/index.ts +1 -0
- package/src/shared/lib/logger/index.ts +12 -0
- package/src/shared/lib/logger/logger.ts +126 -0
- package/src/shared/lib/match/index.ts +6 -0
- package/src/shared/lib/match/url-matcher.ts +107 -0
- package/src/sse.ts +2 -0
- package/src/webrtc.ts +2 -0
- package/src/websocket.ts +2 -0
- package/src/widgets/stage/index.ts +9 -0
- package/src/widgets/stage/lib/attached-context.ts +10 -0
- package/src/widgets/stage/lib/create-stage.ts +307 -0
- package/src/widgets/stage/lib/http-router.ts +954 -0
- package/src/widgets/stage/lib/page-host.ts +375 -0
- package/src/widgets/stage/lib/page-runtime.ts +314 -0
- package/src/widgets/stage/lib/reads.ts +128 -0
- package/src/widgets/stage/lib/response-stream.ts +139 -0
- package/src/widgets/stage/lib/socket-router.ts +300 -0
- package/src/widgets/stage/model/stage.ts +110 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { UrlMatcher } from "../../../shared/lib/match/index.js";
|
|
2
|
+
|
|
3
|
+
/** One end of a routed WebSocket: the page's, or the real server's. */
|
|
4
|
+
export interface SocketPeer {
|
|
5
|
+
/** Deliver a message to this end. */
|
|
6
|
+
send(data: string | Uint8Array): void;
|
|
7
|
+
/**
|
|
8
|
+
* Close this end. The page's end takes any code and reason, as a server
|
|
9
|
+
* may close with any code. The server's end is closed by the browser,
|
|
10
|
+
* which sends only 1000 and 3000-4999, no reason without a code, and no
|
|
11
|
+
* reason over 123 bytes of UTF-8: another code goes out as a normal
|
|
12
|
+
* closure, 1000, and the reason is cut to 123 bytes, so the close still
|
|
13
|
+
* happens.
|
|
14
|
+
*/
|
|
15
|
+
close(options?: {
|
|
16
|
+
readonly code?: number;
|
|
17
|
+
readonly reason?: string;
|
|
18
|
+
}): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Messages this end sends. One handler; a later call replaces it. Until
|
|
21
|
+
* one is set, what this end sends is dropped, not passed on to the other.
|
|
22
|
+
* A handler that throws, or rejects, fails the rule (see
|
|
23
|
+
* `SocketRule.open`).
|
|
24
|
+
*/
|
|
25
|
+
onMessage(handler: (data: string | Uint8Array) => void): void;
|
|
26
|
+
/**
|
|
27
|
+
* This end closed, by its own doing or through `close()` — and a close
|
|
28
|
+
* the page starts is reported again once `close()` completes it, so a
|
|
29
|
+
* handler must expect to run twice. Setting one turns off the automatic
|
|
30
|
+
* forwarding of a close to the other end: the page's socket then hangs in
|
|
31
|
+
* CLOSING until the handler closes the page's end itself. A handler that
|
|
32
|
+
* throws, or rejects, fails the rule.
|
|
33
|
+
*/
|
|
34
|
+
onClose(
|
|
35
|
+
handler: (code: number | undefined, reason: string | undefined) => void,
|
|
36
|
+
): void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** A WebSocket a page opened, as the rule that takes it sees it. */
|
|
40
|
+
export interface SocketView {
|
|
41
|
+
/** Links every frame of this connection. */
|
|
42
|
+
readonly correlation: string;
|
|
43
|
+
readonly url: URL;
|
|
44
|
+
readonly context: string;
|
|
45
|
+
/** The page's end. */
|
|
46
|
+
readonly page: SocketPeer;
|
|
47
|
+
/**
|
|
48
|
+
* Open the real server's end and return it. No message is forwarded for
|
|
49
|
+
* you: a rule that wants a proxy wires the two ends together itself, and
|
|
50
|
+
* a direction it does not wire is dropped — a rule that sets only the
|
|
51
|
+
* page's `onMessage` never lets the server's messages reach the page. A
|
|
52
|
+
* close is passed on to the other end unless the end that closed has an
|
|
53
|
+
* `onClose` handler.
|
|
54
|
+
*/
|
|
55
|
+
connectToServer(): SocketPeer;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface SocketRule {
|
|
59
|
+
/**
|
|
60
|
+
* The `protocol` of this rule's frames: those it records, and the fault
|
|
61
|
+
* the stage records when `open` throws.
|
|
62
|
+
*/
|
|
63
|
+
readonly protocol: string;
|
|
64
|
+
/** Names the rule in logs, diagnostics and the fault of a rule that throws. */
|
|
65
|
+
readonly label: string;
|
|
66
|
+
readonly url: UrlMatcher;
|
|
67
|
+
/** Stop taking connections after this many. */
|
|
68
|
+
readonly times?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Take a connection: called once per matching socket. The page's socket
|
|
71
|
+
* opens when this settles — or, for a rule that connects the server,
|
|
72
|
+
* when the server's end opens — and sooner if the rule sends to it. Set
|
|
73
|
+
* the handlers before awaiting anything: an end drops what it sends
|
|
74
|
+
* until it has one.
|
|
75
|
+
*
|
|
76
|
+
* A rule that throws, or rejects — here, or in a handler it set on
|
|
77
|
+
* either end — is a bug in the test: the stage logs it, records a
|
|
78
|
+
* `fault` frame (`meta.reason` `rule-error`, `meta.rule` the label,
|
|
79
|
+
* `meta.error` the message) and closes the page's end with 1011 and a
|
|
80
|
+
* reason naming the rule — and the server's end too, if the rule
|
|
81
|
+
* connected it. Once per socket: what fails after that is not reported
|
|
82
|
+
* again. The ends' `onClose` handlers hear those closes as they hear any
|
|
83
|
+
* other.
|
|
84
|
+
*/
|
|
85
|
+
open(socket: SocketView): void | Promise<void>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface SocketRuleHandle {
|
|
89
|
+
/** Connections this rule took. */
|
|
90
|
+
readonly hits: number;
|
|
91
|
+
readonly active: boolean;
|
|
92
|
+
/** Stop taking connections. Open ones stay as they are. */
|
|
93
|
+
remove(): Promise<void>;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Where plugins put WebSocket rules. Rules apply to every attached context,
|
|
98
|
+
* present and future, and the newest rule that matches a socket takes it; a
|
|
99
|
+
* socket no rule takes goes to the real server.
|
|
100
|
+
*
|
|
101
|
+
* Routing replaces `WebSocket` in every document of the context, so it
|
|
102
|
+
* starts with the first rule and a stage with none leaves the page alone.
|
|
103
|
+
* The replacement reaches a document as it loads: a rule added later applies
|
|
104
|
+
* to the documents loaded after it, unless the plugin called `prepare()`.
|
|
105
|
+
*/
|
|
106
|
+
export interface SocketRouter {
|
|
107
|
+
add(rule: SocketRule): Promise<SocketRuleHandle>;
|
|
108
|
+
/**
|
|
109
|
+
* Start routing as soon as a context is attached, before any rule exists,
|
|
110
|
+
* so a rule added mid-test reaches the pages already open. Call in `setup`.
|
|
111
|
+
*/
|
|
112
|
+
prepare(): void;
|
|
113
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** How an open response ended. */
|
|
2
|
+
export type StreamEnd =
|
|
3
|
+
/** The rule ended it cleanly. */
|
|
4
|
+
| "ended"
|
|
5
|
+
/** The rule reset the connection. */
|
|
6
|
+
| "cut"
|
|
7
|
+
/** The page went away: it closed the stream, navigated, or was closed. */
|
|
8
|
+
| "cancelled";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A response that stays open, written at the test's pace: what a server
|
|
12
|
+
* stream, an event stream or a long poll is made of. A rule gets one by
|
|
13
|
+
* answering `{ type: "stream" }`.
|
|
14
|
+
*/
|
|
15
|
+
export interface ResponseStream {
|
|
16
|
+
/**
|
|
17
|
+
* The request's headers as the server received them, names lower-cased.
|
|
18
|
+
* They include what the browser's network stack adds after Playwright's
|
|
19
|
+
* route has seen the request, such as `last-event-id`.
|
|
20
|
+
*/
|
|
21
|
+
readonly requestHeaders: Readonly<Record<string, string>>;
|
|
22
|
+
/**
|
|
23
|
+
* Set the status and headers. They go out with the first `write`, or at
|
|
24
|
+
* once on `flush`, and a later call replaces them until then. Without a
|
|
25
|
+
* call the response is a bare 200. A header may carry several values
|
|
26
|
+
* separated by a newline (`set-cookie`). `connection` is the stream
|
|
27
|
+
* server's: it closes every connection after one response.
|
|
28
|
+
*/
|
|
29
|
+
head(status: number, headers?: Readonly<Record<string, string>>): void;
|
|
30
|
+
/** Send the head now, before any body: an EventSource opens on it. */
|
|
31
|
+
flush(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Write a chunk. Resolves once it is handed to the socket; rejects with
|
|
34
|
+
* `CLOSED` when the response is over, the page letting go included.
|
|
35
|
+
*/
|
|
36
|
+
write(chunk: Uint8Array | string): Promise<void>;
|
|
37
|
+
/** End the response cleanly. Resolves once it is closed, as `closed` does. */
|
|
38
|
+
end(): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Reset the connection mid-response: no terminator, no trailer — what a
|
|
41
|
+
* network drop looks like to the page.
|
|
42
|
+
*/
|
|
43
|
+
cut(): void;
|
|
44
|
+
/** Resolves when the response is over, with how, once that is on the timeline. */
|
|
45
|
+
readonly closed: Promise<StreamEnd>;
|
|
46
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export { createRpcClient } from "./lib/client.js";
|
|
2
|
+
export { connect } from "./lib/create-connect.js";
|
|
3
|
+
export { statusOf } from "./lib/status-of.js";
|
|
4
|
+
export type {
|
|
5
|
+
ClientOptions,
|
|
6
|
+
ConnectApi,
|
|
7
|
+
ConnectOptions,
|
|
8
|
+
HeldCall,
|
|
9
|
+
HoldOptions,
|
|
10
|
+
HoldStub,
|
|
11
|
+
RpcCall,
|
|
12
|
+
RpcProtocol,
|
|
13
|
+
RpcServer,
|
|
14
|
+
ServeOptions,
|
|
15
|
+
StandaloneClientOptions,
|
|
16
|
+
StreamCall,
|
|
17
|
+
StreamEnd,
|
|
18
|
+
StreamStub,
|
|
19
|
+
StubHandle,
|
|
20
|
+
StubOptions,
|
|
21
|
+
UnaryReply,
|
|
22
|
+
UnaryStub,
|
|
23
|
+
} from "./model/types.js";
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { DescMessage, MessageShape } from "@bufbuild/protobuf";
|
|
2
|
+
|
|
3
|
+
import type { Frame } from "../../../entities/frame/index.js";
|
|
4
|
+
import type { RpcCall, RpcProtocol } from "../model/types.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* One method's frames as calls: one per correlation, oldest first. A frame
|
|
8
|
+
* another writer left without a correlation is a call of its own.
|
|
9
|
+
*/
|
|
10
|
+
export function assembleCalls<I extends DescMessage, O extends DescMessage>(
|
|
11
|
+
frames: readonly Frame[],
|
|
12
|
+
): RpcCall<I, O>[] {
|
|
13
|
+
const byCall = new Map<string, Frame[]>();
|
|
14
|
+
for (const frame of frames) {
|
|
15
|
+
const key = frame.correlation ?? `#${frame.seq}`;
|
|
16
|
+
const list = byCall.get(key) ?? [];
|
|
17
|
+
list.push(frame);
|
|
18
|
+
byCall.set(key, list);
|
|
19
|
+
}
|
|
20
|
+
return [...byCall].map(([correlation, own]) => {
|
|
21
|
+
const first = own[0] as Frame;
|
|
22
|
+
const requests = own.filter(
|
|
23
|
+
(frame) => frame.direction === "out" && frame.value !== undefined,
|
|
24
|
+
);
|
|
25
|
+
// Everything that is not the caller's is the answer: messages, the
|
|
26
|
+
// status, or the fault that stood in for one.
|
|
27
|
+
const answer = own.findLast((frame) => frame.direction !== "out");
|
|
28
|
+
const status = own.findLast((frame) => frame.kind === "response");
|
|
29
|
+
const fault = own.findLast((frame) => frame.kind === "fault");
|
|
30
|
+
return {
|
|
31
|
+
correlation,
|
|
32
|
+
caller: requests[0]?.source,
|
|
33
|
+
answeredBy: answer?.source,
|
|
34
|
+
page: first.page,
|
|
35
|
+
context: first.context,
|
|
36
|
+
protocol: first.meta["rpcProtocol"] as RpcProtocol | undefined,
|
|
37
|
+
request: requests[0]?.value as MessageShape<I> | undefined,
|
|
38
|
+
requests: requests.map((frame) => frame.value as MessageShape<I>),
|
|
39
|
+
responses: own
|
|
40
|
+
.filter(
|
|
41
|
+
(frame) =>
|
|
42
|
+
frame.direction === "in" &&
|
|
43
|
+
frame.kind === "message" &&
|
|
44
|
+
frame.value !== undefined,
|
|
45
|
+
)
|
|
46
|
+
.map((frame) => frame.value as MessageShape<O>),
|
|
47
|
+
status:
|
|
48
|
+
status === undefined
|
|
49
|
+
? undefined
|
|
50
|
+
: {
|
|
51
|
+
code: Number(status.meta["code"]),
|
|
52
|
+
message: String(status.meta["message"] ?? ""),
|
|
53
|
+
},
|
|
54
|
+
fault: fault === undefined ? undefined : String(fault.meta["reason"]),
|
|
55
|
+
frames: own,
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import type { DescService } from "@bufbuild/protobuf";
|
|
2
|
+
import {
|
|
3
|
+
type Client,
|
|
4
|
+
ConnectError,
|
|
5
|
+
createClient,
|
|
6
|
+
type Interceptor,
|
|
7
|
+
type Transport,
|
|
8
|
+
} from "@connectrpc/connect";
|
|
9
|
+
import {
|
|
10
|
+
createConnectTransport,
|
|
11
|
+
createGrpcTransport,
|
|
12
|
+
createGrpcWebTransport,
|
|
13
|
+
Http2SessionManager,
|
|
14
|
+
} from "@connectrpc/connect-node";
|
|
15
|
+
|
|
16
|
+
import type { CookieJarPort } from "../../../shared/api/playwright/index.js";
|
|
17
|
+
import { parseSetCookie } from "../../../shared/lib/cookies/index.js";
|
|
18
|
+
import type { RpcProtocol, StandaloneClientOptions } from "../model/types.js";
|
|
19
|
+
import { type CallInfo, type Recorder, tapIterable } from "./recorder.js";
|
|
20
|
+
|
|
21
|
+
export interface ClientDeps {
|
|
22
|
+
/** Resolved per call, so a context attached after the client exists still counts. */
|
|
23
|
+
readonly session: () => CookieJarPort | undefined;
|
|
24
|
+
/** The time cookies are stamped against; the stage's clock in a stage. */
|
|
25
|
+
readonly now?: () => number;
|
|
26
|
+
readonly recorder?: Recorder;
|
|
27
|
+
readonly correlation?: () => string;
|
|
28
|
+
/**
|
|
29
|
+
* Handed the HTTP/2 connection a gRPC client opens, so its owner can close
|
|
30
|
+
* it. connect-node keeps an idle one for 15 minutes, and a server in the
|
|
31
|
+
* same process cannot finish closing while it is open (Node 22 waits).
|
|
32
|
+
*/
|
|
33
|
+
readonly onConnection?: (connection: Http2SessionManager) => void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Sent with every call that did not set the header itself. */
|
|
37
|
+
function headersInterceptor(
|
|
38
|
+
headers: Readonly<Record<string, string>>,
|
|
39
|
+
): Interceptor {
|
|
40
|
+
return (next) => (request) => {
|
|
41
|
+
for (const [name, value] of Object.entries(headers))
|
|
42
|
+
if (!request.header.has(name)) request.header.set(name, value);
|
|
43
|
+
return next(request);
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Shares a context's cookie jar both ways, as Playwright's own request
|
|
49
|
+
* context does — the reason hand-rolled gRPC-web helpers go through it. The
|
|
50
|
+
* jar's cookies go out on the call, read per call: a sign-in during the test
|
|
51
|
+
* is picked up, and a cookie scoped to another host or path is not sent. The
|
|
52
|
+
* cookies a response sets come back into the jar — an error response's too,
|
|
53
|
+
* which is how a sign-out or a refused sign-in clears a session.
|
|
54
|
+
*
|
|
55
|
+
* Cookies are scoped to the host the request names: its `host` header when
|
|
56
|
+
* one was set, since that is the site the server takes it for.
|
|
57
|
+
*/
|
|
58
|
+
function sessionInterceptor(
|
|
59
|
+
session: () => CookieJarPort | undefined,
|
|
60
|
+
now: () => number,
|
|
61
|
+
): Interceptor {
|
|
62
|
+
return (next) => async (request) => {
|
|
63
|
+
const jar = session();
|
|
64
|
+
if (jar === undefined) return next(request);
|
|
65
|
+
const named = request.header.get("host");
|
|
66
|
+
const site = new URL(request.url);
|
|
67
|
+
if (named !== null) site.host = named;
|
|
68
|
+
const cookies = await jar.cookies(site.href);
|
|
69
|
+
if (cookies.length > 0 && !request.header.has("cookie"))
|
|
70
|
+
request.header.set(
|
|
71
|
+
"cookie",
|
|
72
|
+
cookies.map((c) => `${c.name}=${c.value}`).join("; "),
|
|
73
|
+
);
|
|
74
|
+
const keep = async (headers: Headers) => {
|
|
75
|
+
const set = headers
|
|
76
|
+
.getSetCookie()
|
|
77
|
+
.flatMap((line) => parseSetCookie(line, site.href, now()) ?? []);
|
|
78
|
+
if (set.length > 0) await jar.addCookies?.(set);
|
|
79
|
+
};
|
|
80
|
+
try {
|
|
81
|
+
const response = await next(request);
|
|
82
|
+
await keep(response.header);
|
|
83
|
+
return response;
|
|
84
|
+
} catch (error) {
|
|
85
|
+
// A failed call's headers ride on its error; one that never reached
|
|
86
|
+
// the server has none.
|
|
87
|
+
await keep(ConnectError.from(error).metadata);
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Records drive calls on the timeline: the test's requests, the backend's answers. */
|
|
94
|
+
function recordingInterceptor(
|
|
95
|
+
recorder: Recorder,
|
|
96
|
+
correlation: () => string,
|
|
97
|
+
protocol: RpcProtocol,
|
|
98
|
+
): Interceptor {
|
|
99
|
+
return (next) => async (request) => {
|
|
100
|
+
const call: CallInfo = {
|
|
101
|
+
correlation: correlation(),
|
|
102
|
+
from: "test",
|
|
103
|
+
answeredBy: "network",
|
|
104
|
+
rpcProtocol: protocol,
|
|
105
|
+
};
|
|
106
|
+
const method = request.method;
|
|
107
|
+
try {
|
|
108
|
+
if (!request.stream) recorder.out(call, method, request.message, true);
|
|
109
|
+
const response = await next(
|
|
110
|
+
request.stream
|
|
111
|
+
? {
|
|
112
|
+
...request,
|
|
113
|
+
message: tapIterable(request.message, (m, i) =>
|
|
114
|
+
recorder.out(call, method, m, i === 0),
|
|
115
|
+
),
|
|
116
|
+
}
|
|
117
|
+
: request,
|
|
118
|
+
);
|
|
119
|
+
if (!response.stream) {
|
|
120
|
+
recorder.in(call, method, response.message);
|
|
121
|
+
recorder.status(call, method);
|
|
122
|
+
return response;
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
...response,
|
|
126
|
+
message: (async function* () {
|
|
127
|
+
try {
|
|
128
|
+
for await (const message of response.message) {
|
|
129
|
+
recorder.in(call, method, message);
|
|
130
|
+
yield message;
|
|
131
|
+
}
|
|
132
|
+
recorder.status(call, method);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
recorder.status(call, method, ConnectError.from(error));
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
})(),
|
|
138
|
+
};
|
|
139
|
+
} catch (error) {
|
|
140
|
+
recorder.status(call, method, ConnectError.from(error));
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function createTransport(
|
|
147
|
+
options: StandaloneClientOptions,
|
|
148
|
+
deps: ClientDeps,
|
|
149
|
+
): Transport {
|
|
150
|
+
const protocol = options.protocol ?? "connect";
|
|
151
|
+
// Outermost first: the defaults, then the session (which reads the `host`
|
|
152
|
+
// the defaults may have named), then the recorder, closest to the wire.
|
|
153
|
+
const interceptors: Interceptor[] = [];
|
|
154
|
+
if (options.headers !== undefined)
|
|
155
|
+
interceptors.push(headersInterceptor(options.headers));
|
|
156
|
+
interceptors.push(sessionInterceptor(deps.session, deps.now ?? Date.now));
|
|
157
|
+
if (deps.recorder !== undefined && deps.correlation !== undefined)
|
|
158
|
+
interceptors.push(
|
|
159
|
+
recordingInterceptor(deps.recorder, deps.correlation, protocol),
|
|
160
|
+
);
|
|
161
|
+
const nodeOptions = options.ignoreHttpsErrors
|
|
162
|
+
? { rejectUnauthorized: false }
|
|
163
|
+
: undefined;
|
|
164
|
+
const common = {
|
|
165
|
+
baseUrl: options.baseURL,
|
|
166
|
+
interceptors,
|
|
167
|
+
useBinaryFormat: options.binary ?? true,
|
|
168
|
+
};
|
|
169
|
+
if (protocol === "grpc") {
|
|
170
|
+
// The transport's own session manager would be out of reach; this one
|
|
171
|
+
// is handed to whoever has to close it.
|
|
172
|
+
const sessionManager = new Http2SessionManager(
|
|
173
|
+
options.baseURL,
|
|
174
|
+
undefined,
|
|
175
|
+
nodeOptions,
|
|
176
|
+
);
|
|
177
|
+
deps.onConnection?.(sessionManager);
|
|
178
|
+
return createGrpcTransport({ ...common, sessionManager });
|
|
179
|
+
}
|
|
180
|
+
const http1 = { ...common, httpVersion: "1.1" as const, nodeOptions };
|
|
181
|
+
if (protocol === "grpc-web") return createGrpcWebTransport(http1);
|
|
182
|
+
return createConnectTransport(http1);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* A typed client for a real backend, outside any stage: the `rpc` helper an
|
|
187
|
+
* e2e suite otherwise writes by hand — framing, status parsing and all — per
|
|
188
|
+
* protocol. Errors arrive as `ConnectError` with the server's code.
|
|
189
|
+
*
|
|
190
|
+
* Over gRPC it holds an HTTP/2 connection, which connect-node keeps for 15
|
|
191
|
+
* minutes once idle. It does not keep the process alive, but a server in the
|
|
192
|
+
* same process waits for it before it finishes closing. A stage's client is
|
|
193
|
+
* closed with the stage.
|
|
194
|
+
*/
|
|
195
|
+
export function createRpcClient<S extends DescService>(
|
|
196
|
+
service: S,
|
|
197
|
+
options: StandaloneClientOptions,
|
|
198
|
+
): Client<S> {
|
|
199
|
+
return createClient(
|
|
200
|
+
service,
|
|
201
|
+
createTransport(options, { session: () => options.session }),
|
|
202
|
+
);
|
|
203
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import type { DescService } from "@bufbuild/protobuf";
|
|
2
|
+
import { type Client, createClient } from "@connectrpc/connect";
|
|
3
|
+
import type { Http2SessionManager } from "@connectrpc/connect-node";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
definePlugin,
|
|
7
|
+
type PluginContext,
|
|
8
|
+
type WirePlugin,
|
|
9
|
+
} from "../../../entities/plugin/index.js";
|
|
10
|
+
import { WirestageError } from "../../../shared/lib/errors/index.js";
|
|
11
|
+
import { createSequence } from "../../../shared/lib/id/index.js";
|
|
12
|
+
import type {
|
|
13
|
+
ClientOptions,
|
|
14
|
+
ConnectApi,
|
|
15
|
+
ConnectOptions,
|
|
16
|
+
RpcServer,
|
|
17
|
+
} from "../model/types.js";
|
|
18
|
+
import { assembleCalls } from "./calls.js";
|
|
19
|
+
import { createTransport } from "./client.js";
|
|
20
|
+
import { stubAbort, stubHold } from "./faults.js";
|
|
21
|
+
import { recordService } from "./record.js";
|
|
22
|
+
import { createRecorder, endpointOf } from "./recorder.js";
|
|
23
|
+
import { createRpcRules } from "./rules.js";
|
|
24
|
+
import { serveRoutes } from "./serve.js";
|
|
25
|
+
import { stubFailure, stubService, stubStream, stubUnary } from "./stubs.js";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Connect, gRPC-web and gRPC: stubs answered by connect-es's own protocol
|
|
29
|
+
* handlers, recording of real traffic, drive clients, and out-of-process
|
|
30
|
+
* servers.
|
|
31
|
+
*
|
|
32
|
+
* The stubs never encode a frame by hand. Each one is a one-method Connect
|
|
33
|
+
* router whose handler runs the real server-side protocol, so the bytes the
|
|
34
|
+
* page receives are exactly what a Connect server would send — errors,
|
|
35
|
+
* trailers, JSON and binary alike.
|
|
36
|
+
*/
|
|
37
|
+
export function connect(
|
|
38
|
+
options: ConnectOptions = {},
|
|
39
|
+
): WirePlugin<"connect", ConnectApi> {
|
|
40
|
+
return definePlugin({
|
|
41
|
+
name: "connect",
|
|
42
|
+
scope: "Connect",
|
|
43
|
+
setup: (context) => createConnect(context, options),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function createConnect(context: PluginContext, options: ConnectOptions) {
|
|
48
|
+
const protocol = context.name;
|
|
49
|
+
const live = (options.streaming ?? "live") === "live";
|
|
50
|
+
const recorder = createRecorder(context.timeline, protocol);
|
|
51
|
+
const rules = createRpcRules(context, options, recorder, protocol);
|
|
52
|
+
const testCorrelation = createSequence("t");
|
|
53
|
+
const serverCorrelation = createSequence("s");
|
|
54
|
+
const servers = new Set<RpcServer>();
|
|
55
|
+
/** Lets go of each call a hold still has, when the stage goes away. */
|
|
56
|
+
const heldCalls = new Set<() => void>();
|
|
57
|
+
/** The drive clients' HTTP/2 connections, closed with the stage. */
|
|
58
|
+
const connections = new Set<Http2SessionManager>();
|
|
59
|
+
/** The record rules being added, once: every context gets them. */
|
|
60
|
+
let recording: Promise<unknown> | undefined;
|
|
61
|
+
|
|
62
|
+
const api: ConnectApi = {
|
|
63
|
+
service: (service, impl, stubOptions = {}) =>
|
|
64
|
+
stubService(rules, service, impl, stubOptions),
|
|
65
|
+
unary: (method, reply, stubOptions = {}) =>
|
|
66
|
+
stubUnary(context, rules, method, reply, stubOptions),
|
|
67
|
+
stream: (method, stubOptions = {}) =>
|
|
68
|
+
stubStream(context, rules, recorder, method, stubOptions, live),
|
|
69
|
+
fail: (method, code, message, stubOptions = {}) =>
|
|
70
|
+
stubFailure(rules, method, code, message, stubOptions),
|
|
71
|
+
abort: (method, abortOptions = {}) =>
|
|
72
|
+
stubAbort(rules, recorder, method, abortOptions),
|
|
73
|
+
hold: (method, holdOptions = {}) =>
|
|
74
|
+
stubHold(context, rules, recorder, method, holdOptions, heldCalls),
|
|
75
|
+
record: (service) => recordService(rules, service),
|
|
76
|
+
async calls(method) {
|
|
77
|
+
await context.sync();
|
|
78
|
+
return assembleCalls(
|
|
79
|
+
context.timeline.frames({ protocol, endpoint: endpointOf(method) }),
|
|
80
|
+
);
|
|
81
|
+
},
|
|
82
|
+
client<S extends DescService>(
|
|
83
|
+
service: S,
|
|
84
|
+
clientOptions: ClientOptions = {},
|
|
85
|
+
): Client<S> {
|
|
86
|
+
const {
|
|
87
|
+
session,
|
|
88
|
+
baseURL: requested,
|
|
89
|
+
...transportOptions
|
|
90
|
+
} = clientOptions;
|
|
91
|
+
const base = requested ?? options.baseURL ?? "/";
|
|
92
|
+
const origin = context.attachments()[0]?.baseURL ?? context.baseURL;
|
|
93
|
+
if (!/^https?:\/\//.test(base) && origin === undefined)
|
|
94
|
+
throw new WirestageError(
|
|
95
|
+
"INVALID_ARGUMENT",
|
|
96
|
+
`client(${service.typeName}): "${base}" is relative and the stage has no baseURL; pass an absolute baseURL or give the stage one`,
|
|
97
|
+
);
|
|
98
|
+
return createClient(
|
|
99
|
+
service,
|
|
100
|
+
createTransport(
|
|
101
|
+
{
|
|
102
|
+
...transportOptions,
|
|
103
|
+
baseURL: new URL(base, origin).href.replace(/\/+$/, ""),
|
|
104
|
+
protocol:
|
|
105
|
+
transportOptions.protocol ?? options.clientProtocol ?? "connect",
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
// Resolved per call: a context attached after the client was made
|
|
109
|
+
// still lends it its session.
|
|
110
|
+
session: () =>
|
|
111
|
+
session === null
|
|
112
|
+
? undefined
|
|
113
|
+
: (session ?? context.attachments()[0]?.cookies),
|
|
114
|
+
now: () => context.clock.now(),
|
|
115
|
+
recorder,
|
|
116
|
+
correlation: testCorrelation,
|
|
117
|
+
onConnection: (connection) => connections.add(connection),
|
|
118
|
+
},
|
|
119
|
+
),
|
|
120
|
+
);
|
|
121
|
+
},
|
|
122
|
+
async serve(routes, serveOptions = {}) {
|
|
123
|
+
const server = await serveRoutes(routes, serveOptions, {
|
|
124
|
+
recorder,
|
|
125
|
+
correlation: serverCorrelation,
|
|
126
|
+
});
|
|
127
|
+
const tracked: RpcServer = {
|
|
128
|
+
url: server.url,
|
|
129
|
+
port: server.port,
|
|
130
|
+
close: async () => {
|
|
131
|
+
servers.delete(tracked);
|
|
132
|
+
await server.close();
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
servers.add(tracked);
|
|
136
|
+
return tracked;
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
api,
|
|
142
|
+
async attach() {
|
|
143
|
+
const services = options.record;
|
|
144
|
+
if (services === undefined) return;
|
|
145
|
+
recording ??= (async () => {
|
|
146
|
+
for (const service of services) await api.record(service);
|
|
147
|
+
})().catch((error: unknown) => {
|
|
148
|
+
// Only the first rule can fail, installing the context's route, and
|
|
149
|
+
// the router takes it back: the next attach starts over.
|
|
150
|
+
recording = undefined;
|
|
151
|
+
throw error;
|
|
152
|
+
});
|
|
153
|
+
await recording;
|
|
154
|
+
},
|
|
155
|
+
async dispose() {
|
|
156
|
+
for (const letGo of [...heldCalls]) letGo();
|
|
157
|
+
// A drive client's connection outlives its calls; left open, a
|
|
158
|
+
// server in the test's own process would wait for it on close.
|
|
159
|
+
for (const connection of connections) connection.abort();
|
|
160
|
+
connections.clear();
|
|
161
|
+
await Promise.all([...servers].map((server) => server.close()));
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
}
|