serverless-ircd 0.5.0 → 0.7.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/.github/workflows/ci.yml +40 -3
- package/.github/workflows/deploy-aws.yml +1 -2
- package/.github/workflows/deploy-cf-tcp.yml +1 -5
- package/.github/workflows/deploy-cf.yml +1 -2
- package/.gitmodules +3 -0
- package/CHANGELOG.md +474 -0
- package/README.md +219 -22
- package/apps/aws-stack/README.md +33 -0
- package/apps/aws-stack/package.json +1 -1
- package/apps/aws-stack/src/aws-stack.ts +74 -9
- package/apps/aws-stack/tests/coverage-command.test.ts +155 -0
- package/apps/aws-stack/tests/stack.test.ts +2 -2
- package/apps/aws-stack/tests/synth-no-bundle.test.ts +129 -0
- package/apps/aws-stack/tests/vitest.setup.ts +18 -0
- package/apps/aws-stack/vitest.config.ts +3 -0
- package/apps/cf-tcp-container/package.json +1 -1
- package/apps/cf-tcp-container/src/config-loader.ts +6 -7
- package/apps/cf-tcp-container/src/container-server.ts +3 -0
- package/apps/cf-tcp-container/tests/config-loader.test.ts +10 -3
- package/apps/cf-tcp-container/wrangler.toml +1 -1
- package/apps/cf-worker/package.json +2 -1
- package/apps/cf-worker/src/origin-allowlist.ts +99 -0
- package/apps/cf-worker/src/worker.ts +89 -9
- package/apps/cf-worker/tests/fixtures/web-dist/app/index.html +18 -0
- package/apps/cf-worker/tests/fixtures/web-dist/index.html +18 -0
- package/apps/cf-worker/tests/origin-allowlist.test.ts +101 -0
- package/apps/cf-worker/tests/smoke.test.ts +28 -2
- package/apps/cf-worker/tests/ws-origin.test.ts +89 -0
- package/apps/cf-worker/vitest.config.ts +1 -1
- package/apps/cf-worker/wrangler.test.toml +23 -0
- package/apps/cf-worker/wrangler.toml +59 -3
- package/apps/local-cli/package.json +1 -1
- package/apps/local-cli/src/config-loader.ts +6 -6
- package/apps/local-cli/src/main.ts +20 -0
- package/apps/local-cli/src/server.ts +9 -7
- package/apps/local-cli/tests/e2e.test.ts +112 -28
- package/apps/local-cli/tests/tcp.test.ts +71 -19
- package/apps/local-cli/tests/ws-subprotocol.test.ts +12 -7
- package/apps/web/landing/index.html +217 -0
- package/apps/web/package.json +28 -0
- package/apps/web/scripts/build.mjs +125 -0
- package/apps/web/src/build-env.ts +43 -0
- package/apps/web/src/config-schema.ts +138 -0
- package/apps/web/static/config.json +28 -0
- package/apps/web/static/config.prod.json +28 -0
- package/apps/web/static/config.staging.json +28 -0
- package/apps/web/tests/build-env.test.ts +63 -0
- package/apps/web/tests/build-smoke.test.ts +103 -0
- package/apps/web/tests/config-schema.test.ts +432 -0
- package/apps/web/tests/workspace.test.ts +12 -0
- package/apps/web/tsconfig.json +10 -0
- package/apps/web/upstream/.browserslistrc +3 -0
- package/apps/web/upstream/.editorconfig +9 -0
- package/apps/web/upstream/.eslintignore +3 -0
- package/apps/web/upstream/.eslintrc.js +90 -0
- package/apps/web/upstream/.github/FUNDING.yml +12 -0
- package/apps/web/upstream/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
- package/apps/web/upstream/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/apps/web/upstream/.github/workflows/push.yml +26 -0
- package/apps/web/upstream/.github/workflows/release.yml +33 -0
- package/apps/web/upstream/.github/workflows/release_manual.yml +55 -0
- package/apps/web/upstream/.prettierrc.js +9 -0
- package/apps/web/upstream/.stylelintignore +5 -0
- package/apps/web/upstream/.stylelintrc.js +21 -0
- package/apps/web/upstream/LICENSE +201 -0
- package/apps/web/upstream/README.md +76 -0
- package/apps/web/upstream/babel.config.js +25 -0
- package/apps/web/upstream/crowdin.yml +3 -0
- package/apps/web/upstream/index.html +35 -0
- package/apps/web/upstream/jest.config.js +7 -0
- package/apps/web/upstream/jsdoc.json +22 -0
- package/apps/web/upstream/package.json +80 -0
- package/apps/web/upstream/scripts/pre-commit +43 -0
- package/apps/web/upstream/src/components/App.vue +516 -0
- package/apps/web/upstream/src/components/AppSettings.vue +587 -0
- package/apps/web/upstream/src/components/AutoComplete.vue +284 -0
- package/apps/web/upstream/src/components/Avatar.vue +105 -0
- package/apps/web/upstream/src/components/AwayStatusIndicator.vue +77 -0
- package/apps/web/upstream/src/components/BufferKey.vue +121 -0
- package/apps/web/upstream/src/components/BufferSettings.vue +97 -0
- package/apps/web/upstream/src/components/Captcha.vue +85 -0
- package/apps/web/upstream/src/components/ChannelBanlist.vue +96 -0
- package/apps/web/upstream/src/components/ChannelInfo.vue +159 -0
- package/apps/web/upstream/src/components/ChannelInvitelist.vue +276 -0
- package/apps/web/upstream/src/components/ChannelList.vue +345 -0
- package/apps/web/upstream/src/components/Container.vue +401 -0
- package/apps/web/upstream/src/components/ContainerHeader.vue +522 -0
- package/apps/web/upstream/src/components/ControlInput.vue +1016 -0
- package/apps/web/upstream/src/components/LoadingAnimation.vue +104 -0
- package/apps/web/upstream/src/components/MediaViewer.vue +199 -0
- package/apps/web/upstream/src/components/MessageInfo.vue +192 -0
- package/apps/web/upstream/src/components/MessageList.vue +1028 -0
- package/apps/web/upstream/src/components/MessageListAvatar.vue +20 -0
- package/apps/web/upstream/src/components/MessageListMessageCompact.vue +361 -0
- package/apps/web/upstream/src/components/MessageListMessageInline.vue +257 -0
- package/apps/web/upstream/src/components/MessageListMessageModern.vue +430 -0
- package/apps/web/upstream/src/components/NetworkSettings.vue +528 -0
- package/apps/web/upstream/src/components/Nicklist.vue +309 -0
- package/apps/web/upstream/src/components/NicklistUser.vue +181 -0
- package/apps/web/upstream/src/components/NotConnected.vue +271 -0
- package/apps/web/upstream/src/components/SelfUser.vue +283 -0
- package/apps/web/upstream/src/components/ServerSelector.vue +162 -0
- package/apps/web/upstream/src/components/ServerView.vue +126 -0
- package/apps/web/upstream/src/components/SettingsAdvanced.vue +232 -0
- package/apps/web/upstream/src/components/SettingsAliases.vue +121 -0
- package/apps/web/upstream/src/components/Sidebar.vue +429 -0
- package/apps/web/upstream/src/components/SidebarAboutBuffer.vue +288 -0
- package/apps/web/upstream/src/components/SidebarState.vue +145 -0
- package/apps/web/upstream/src/components/StartupError.vue +35 -0
- package/apps/web/upstream/src/components/StateBrowser.vue +420 -0
- package/apps/web/upstream/src/components/StateBrowserBuffer.vue +124 -0
- package/apps/web/upstream/src/components/StateBrowserNetwork.vue +823 -0
- package/apps/web/upstream/src/components/StateBrowserUsermenu.vue +162 -0
- package/apps/web/upstream/src/components/TypingStatusIndicator.vue +34 -0
- package/apps/web/upstream/src/components/TypingUsersList.vue +56 -0
- package/apps/web/upstream/src/components/UrlEmbed.vue +149 -0
- package/apps/web/upstream/src/components/UserBox.vue +728 -0
- package/apps/web/upstream/src/components/inputtools/Emoji.vue +67 -0
- package/apps/web/upstream/src/components/inputtools/TextStyle.vue +142 -0
- package/apps/web/upstream/src/components/startups/CommonLayout.vue +208 -0
- package/apps/web/upstream/src/components/startups/CustomServer.vue +495 -0
- package/apps/web/upstream/src/components/startups/KiwiBnc.vue +361 -0
- package/apps/web/upstream/src/components/startups/Personal.vue +317 -0
- package/apps/web/upstream/src/components/startups/Welcome.vue +544 -0
- package/apps/web/upstream/src/components/startups/ZncLogin.vue +229 -0
- package/apps/web/upstream/src/components/utils/InputConfirm.vue +68 -0
- package/apps/web/upstream/src/components/utils/InputPrompt.vue +118 -0
- package/apps/web/upstream/src/components/utils/InputText.vue +172 -0
- package/apps/web/upstream/src/components/utils/IrcInput.vue +579 -0
- package/apps/web/upstream/src/components/utils/PluginWrapper.vue +26 -0
- package/apps/web/upstream/src/components/utils/TabbedView.vue +149 -0
- package/apps/web/upstream/src/components/utils/TransitionExpand.vue +97 -0
- package/apps/web/upstream/src/helpers/Colours.js +128 -0
- package/apps/web/upstream/src/helpers/IrcdDiffs.js +26 -0
- package/apps/web/upstream/src/helpers/Md5.js +193 -0
- package/apps/web/upstream/src/helpers/Misc.js +401 -0
- package/apps/web/upstream/src/helpers/TextFormatting.js +237 -0
- package/apps/web/upstream/src/libs/AliasRewriter.js +157 -0
- package/apps/web/upstream/src/libs/AudioManager.js +60 -0
- package/apps/web/upstream/src/libs/BouncerMiddleware.js +247 -0
- package/apps/web/upstream/src/libs/BouncerProvider.js +551 -0
- package/apps/web/upstream/src/libs/ChathistoryMiddleware.js +153 -0
- package/apps/web/upstream/src/libs/ConfigLoader.js +94 -0
- package/apps/web/upstream/src/libs/EmojiProvider.js +46 -0
- package/apps/web/upstream/src/libs/GlobalApi.js +346 -0
- package/apps/web/upstream/src/libs/IPC.js +51 -0
- package/apps/web/upstream/src/libs/InputHandler.js +921 -0
- package/apps/web/upstream/src/libs/IrcClient.js +1506 -0
- package/apps/web/upstream/src/libs/Logger.js +71 -0
- package/apps/web/upstream/src/libs/Message.js +169 -0
- package/apps/web/upstream/src/libs/MessageFormatter.js +364 -0
- package/apps/web/upstream/src/libs/MessageParser.js +249 -0
- package/apps/web/upstream/src/libs/Notifications.js +78 -0
- package/apps/web/upstream/src/libs/ServerConnection.js +270 -0
- package/apps/web/upstream/src/libs/ServerSession.js +102 -0
- package/apps/web/upstream/src/libs/SoundBleep.js +20 -0
- package/apps/web/upstream/src/libs/StatePersistence.js +90 -0
- package/apps/web/upstream/src/libs/ThemeManager.js +128 -0
- package/apps/web/upstream/src/libs/TypingMiddleware.js +105 -0
- package/apps/web/upstream/src/libs/WindowTitle.js +63 -0
- package/apps/web/upstream/src/libs/batchedAdd.js +74 -0
- package/apps/web/upstream/src/libs/bufferTools.js +177 -0
- package/apps/web/upstream/src/libs/polyfill/Element.closest.js +18 -0
- package/apps/web/upstream/src/libs/renderers/Html.js +113 -0
- package/apps/web/upstream/src/libs/settingTools.js +31 -0
- package/apps/web/upstream/src/libs/state/BufferState.js +703 -0
- package/apps/web/upstream/src/libs/state/NetworkState.js +158 -0
- package/apps/web/upstream/src/libs/state/UserState.js +89 -0
- package/apps/web/upstream/src/libs/state/common.js +26 -0
- package/apps/web/upstream/src/libs/state.js +961 -0
- package/apps/web/upstream/src/libs/storage/Local.js +51 -0
- package/apps/web/upstream/src/main.js +566 -0
- package/apps/web/upstream/src/res/autocompleteCommands.js +31 -0
- package/apps/web/upstream/src/res/configTemplates.js +373 -0
- package/apps/web/upstream/src/res/globalStyle.css +277 -0
- package/apps/web/upstream/src/res/kiwiLoadingLogo.png +0 -0
- package/apps/web/upstream/src/res/locales/app.af-ZA.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ar-SA.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.bg-BG.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.bs-BA.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ca-ES.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.cs-CZ.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.da-DK.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.de-DE.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.dev.po +1246 -0
- package/apps/web/upstream/src/res/locales/app.el-GR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.en-US.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.es-419.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.es-AR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.es-EM.po +536 -0
- package/apps/web/upstream/src/res/locales/app.es-ES.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.es-US.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.eu-ES.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.fi-FI.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.fr-FR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.gl-ES.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.he-IL.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.hi-IN.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.hu-HU.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.id-ID.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.it-IT.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ja-JP.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ko-KR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.nl-NL.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.no-NO.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.pl-PL.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.pt-BR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.pt-PT.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ro-RO.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ru-RU.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.sl-SI.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.sq-AL.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.sr-SP.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.sv-SE.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.tr-TR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.uk-UA.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.vi-VN.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.zh-CN.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.zh-TW.po +1145 -0
- package/apps/web/upstream/src/res/localesList.json +27 -0
- package/apps/web/upstream/src/res/logo.png +0 -0
- package/apps/web/upstream/src/thirdparty/about.html +43 -0
- package/apps/web/upstream/src/thirdparty/index.js +5 -0
- package/apps/web/upstream/src/thirdparty/kiwiirccom.vue +185 -0
- package/apps/web/upstream/static/config.json +28 -0
- package/apps/web/upstream/static/emoticons/smile.png +0 -0
- package/apps/web/upstream/static/favicon.png +0 -0
- package/apps/web/upstream/static/highlight.mp3 +0 -0
- package/apps/web/upstream/static/highlight.ogg +0 -0
- package/apps/web/upstream/static/locales/.gitignore +2 -0
- package/apps/web/upstream/static/plugins/customise.html.example +10 -0
- package/apps/web/upstream/static/themes/coffee/theme.css +145 -0
- package/apps/web/upstream/static/themes/common/base.css +1064 -0
- package/apps/web/upstream/static/themes/dark/theme.css +196 -0
- package/apps/web/upstream/static/themes/default/theme.css +92 -0
- package/apps/web/upstream/static/themes/elite/theme.css +248 -0
- package/apps/web/upstream/static/themes/grayfox/theme.css +143 -0
- package/apps/web/upstream/static/themes/nightswatch/theme.css +367 -0
- package/apps/web/upstream/static/themes/osprey/theme.css +115 -0
- package/apps/web/upstream/static/themes/radioactive/theme.css +1211 -0
- package/apps/web/upstream/static/themes/sky/theme.css +107 -0
- package/apps/web/upstream/tests/unit/BatchAdd.spec.js +174 -0
- package/apps/web/upstream/tests/unit/MessageParser.spec.js +125 -0
- package/apps/web/upstream/tests/unit/Misc.spec.js +24 -0
- package/apps/web/upstream/tests/unit/NetworkState.spec.js +58 -0
- package/apps/web/upstream/tests/unit/StartupError.spec.js +20 -0
- package/apps/web/upstream/vue.config.js +150 -0
- package/apps/web/upstream/yarn.lock +9767 -0
- package/apps/web/vitest.config.ts +13 -0
- package/biome.json +2 -0
- package/docs/ADR-001-pure-reducers-and-effect-system.md +74 -0
- package/docs/ADR-002-location-of-authority.md +82 -0
- package/docs/ADR-003-durable-object-sharding.md +110 -0
- package/docs/ADR-004-dynamodb-schema.md +96 -0
- package/docs/ADR-005-wss-only-transport-v1.md +83 -0
- package/docs/ADR-006-sasl-mechanism-scope.md +96 -0
- package/docs/ADR-007-deterministic-ports.md +82 -0
- package/docs/ADR-008-monorepo-tooling.md +60 -0
- package/docs/ADR-009-dual-transport-wss-and-irc-tls.md +138 -0
- package/docs/ADR-Index.md +15 -0
- package/docs/AWS-Adapter-Architecture.md +496 -0
- package/docs/AWS-Deployment.md +1275 -0
- package/docs/AWS-TCP-Deployment.md +410 -0
- package/docs/Cloudflare-Deployment-Guide.md +719 -0
- package/docs/Cloudflare-TCP-Deployment.md +510 -0
- package/docs/Home.md +16 -0
- package/docs/Observability.md +87 -0
- package/docs/PlanExtensions.md +812 -0
- package/docs/PlanIRCv3Websocket.md +503 -0
- package/docs/PlanWebClient.md +451 -0
- package/docs/Release-Process.md +443 -0
- package/docs/WebClientGuide.md +535 -0
- package/package.json +2 -2
- package/packages/aws-adapter/package.json +1 -1
- package/packages/aws-adapter/src/handlers/default.ts +3 -0
- package/packages/aws-adapter/src/handlers/nlb-stream.ts +9 -0
- package/packages/aws-adapter/src/serialize.ts +29 -1
- package/packages/aws-adapter/tests/account-store-dynamo.test.ts +49 -8
- package/packages/aws-adapter/tests/account-store.test.ts +4 -7
- package/packages/aws-adapter/tests/aws-harness.ts +5 -2
- package/packages/aws-adapter/tests/aws-integration.test.ts +8 -4
- package/packages/aws-adapter/tests/aws-runtime.test.ts +79 -0
- package/packages/aws-adapter/tests/config-loader.test.ts +11 -2
- package/packages/aws-adapter/tests/connect.test.ts +9 -9
- package/packages/aws-adapter/tests/disconnect-fanout.test.ts +3 -4
- package/packages/aws-adapter/tests/global-setup.ts +55 -30
- package/packages/aws-adapter/tests/gone-exception.test.ts +4 -9
- package/packages/aws-adapter/tests/handlers.test.ts +90 -4
- package/packages/aws-adapter/tests/message-store.test.ts +6 -5
- package/packages/aws-adapter/tests/nlb-stream.test.ts +60 -8
- package/packages/cf-adapter/package.json +2 -1
- package/packages/cf-adapter/src/connection-do.ts +3 -0
- package/packages/cf-adapter/src/serialize.ts +25 -4
- package/packages/cf-adapter/tests/cf-harness.ts +12 -1
- package/packages/cf-adapter/tests/cf-runtime.test.ts +269 -0
- package/packages/cf-adapter/tests/channel-do.test.ts +117 -0
- package/packages/cf-adapter/tests/config-loader.test.ts +49 -0
- package/packages/cf-adapter/tests/connection-do-pure.test.ts +91 -0
- package/packages/cf-adapter/tests/d1-account-store.test.ts +48 -0
- package/packages/cf-adapter/tests/registry-do.test.ts +104 -0
- package/packages/cf-adapter/tests/serialize.test.ts +308 -0
- package/packages/cf-adapter/vitest.config.ts +1 -1
- package/packages/in-memory-runtime/package.json +1 -1
- package/packages/irc-core/package.json +1 -1
- package/packages/irc-core/reports/mutation/mutation.html +342 -0
- package/packages/irc-core/src/caps/capabilities.ts +37 -3
- package/packages/irc-core/src/caps/index.ts +1 -0
- package/packages/irc-core/src/caps/sts.ts +84 -0
- package/packages/irc-core/src/commands/account-notify.ts +53 -0
- package/packages/irc-core/src/commands/away.ts +9 -3
- package/packages/irc-core/src/commands/cap.ts +23 -5
- package/packages/irc-core/src/commands/chathistory.ts +40 -11
- package/packages/irc-core/src/commands/index.ts +34 -0
- package/packages/irc-core/src/commands/isupport.ts +53 -0
- package/packages/irc-core/src/commands/mode.ts +8 -1
- package/packages/irc-core/src/commands/monitor.ts +327 -0
- package/packages/irc-core/src/commands/multiline.ts +256 -0
- package/packages/irc-core/src/commands/pre-away.ts +112 -0
- package/packages/irc-core/src/commands/privmsg.ts +19 -12
- package/packages/irc-core/src/commands/read-marker.ts +108 -0
- package/packages/irc-core/src/commands/registration.ts +8 -0
- package/packages/irc-core/src/commands/sasl.ts +24 -1
- package/packages/irc-core/src/commands/tagmsg.ts +71 -1
- package/packages/irc-core/src/commands/whois.ts +5 -0
- package/packages/irc-core/src/config.ts +65 -10
- package/packages/irc-core/src/effects.ts +14 -0
- package/packages/irc-core/src/ports.ts +389 -0
- package/packages/irc-core/src/protocol/index.ts +14 -0
- package/packages/irc-core/src/protocol/labeled-response.ts +133 -0
- package/packages/irc-core/src/protocol/numerics.ts +15 -0
- package/packages/irc-core/src/protocol/outbound.ts +16 -1
- package/packages/irc-core/src/protocol/standard-replies.ts +277 -0
- package/packages/irc-core/src/state/connection.ts +32 -1
- package/packages/irc-core/src/types.ts +112 -0
- package/packages/irc-core/stryker.commands.conf.json +1 -2
- package/packages/irc-core/tests/away-store.test.ts +73 -0
- package/packages/irc-core/tests/batch.test.ts +15 -0
- package/packages/irc-core/tests/caps/capabilities.test.ts +119 -2
- package/packages/irc-core/tests/caps/sts.test.ts +123 -0
- package/packages/irc-core/tests/commands/account-notify.test.ts +171 -0
- package/packages/irc-core/tests/commands/away.test.ts +85 -2
- package/packages/irc-core/tests/commands/cap.test.ts +165 -0
- package/packages/irc-core/tests/commands/chathistory.test.ts +148 -1
- package/packages/irc-core/tests/commands/echo-message.test.ts +9 -3
- package/packages/irc-core/tests/commands/isupport.test.ts +80 -0
- package/packages/irc-core/tests/commands/mode.test.ts +72 -0
- package/packages/irc-core/tests/commands/monitor.test.ts +411 -0
- package/packages/irc-core/tests/commands/multiline.test.ts +428 -0
- package/packages/irc-core/tests/commands/pre-away.test.ts +241 -0
- package/packages/irc-core/tests/commands/privmsg.test.ts +154 -3
- package/packages/irc-core/tests/commands/read-marker.test.ts +211 -0
- package/packages/irc-core/tests/commands/registration.test.ts +64 -0
- package/packages/irc-core/tests/commands/sasl.test.ts +116 -2
- package/packages/irc-core/tests/commands/tagmsg.test.ts +357 -0
- package/packages/irc-core/tests/commands/whois.test.ts +34 -0
- package/packages/irc-core/tests/config.test.ts +103 -13
- package/packages/irc-core/tests/labeled-response.test.ts +181 -0
- package/packages/irc-core/tests/message-tags.test.ts +114 -0
- package/packages/irc-core/tests/monitor-store.test.ts +200 -0
- package/packages/irc-core/tests/numerics.test.ts +13 -0
- package/packages/irc-core/tests/parser.test.ts +109 -0
- package/packages/irc-core/tests/read-marker-store.test.ts +108 -0
- package/packages/irc-core/tests/standard-replies.test.ts +412 -0
- package/packages/irc-core/tests/types.test.ts +27 -0
- package/packages/irc-server/package.json +1 -1
- package/packages/irc-server/src/actor.ts +450 -3
- package/packages/irc-server/src/dispatch.ts +108 -16
- package/packages/irc-server/tests/actor.test.ts +1432 -39
- package/packages/irc-server/tests/dispatch.test.ts +263 -2
- package/packages/irc-test-support/package.json +1 -1
- package/packages/irc-test-support/src/index.ts +6 -0
- package/packages/irc-test-support/src/scenarios.ts +24 -2
- package/packages/irc-test-support/src/test-config.ts +54 -0
- package/packages/irc-test-support/tests/test-config.test.ts +51 -0
- package/tools/ci-hardening/package.json +1 -1
- package/tools/ci-hardening/tests/ddb-local.test.ts +82 -0
- package/tools/load-test/package.json +33 -0
- package/tools/load-test/src/client.ts +351 -0
- package/tools/load-test/src/config.ts +313 -0
- package/tools/load-test/src/harness.ts +116 -0
- package/tools/load-test/src/main.ts +120 -0
- package/tools/load-test/src/metrics.ts +168 -0
- package/tools/load-test/src/report.ts +106 -0
- package/tools/load-test/tests/client.test.ts +212 -0
- package/tools/load-test/tests/config.test.ts +152 -0
- package/tools/load-test/tests/framing.test.ts +37 -0
- package/tools/load-test/tests/harness.test.ts +165 -0
- package/tools/load-test/tests/metrics.test.ts +174 -0
- package/tools/load-test/tests/report.test.ts +161 -0
- package/tools/load-test/tests/smoke.test.ts +67 -0
- package/tools/load-test/tsconfig.build.json +12 -0
- package/tools/load-test/tsconfig.test.json +10 -0
- package/tools/load-test/vitest.config.ts +29 -0
- package/tools/tcp-ws-forwarder/package.json +1 -1
|
@@ -80,7 +80,12 @@ describe('local-cli e2e', () => {
|
|
|
80
80
|
let server: LocalServer;
|
|
81
81
|
|
|
82
82
|
beforeAll(async () => {
|
|
83
|
-
server = await startLocalServer({
|
|
83
|
+
server = await startLocalServer({
|
|
84
|
+
port: 0,
|
|
85
|
+
hostname: '127.0.0.1',
|
|
86
|
+
serverName: 'irc.local.example.com',
|
|
87
|
+
networkName: 'LocalNet',
|
|
88
|
+
});
|
|
84
89
|
});
|
|
85
90
|
|
|
86
91
|
afterAll(async () => {
|
|
@@ -103,15 +108,15 @@ describe('local-cli e2e', () => {
|
|
|
103
108
|
|
|
104
109
|
await alice.send('NICK alice');
|
|
105
110
|
await alice.send('USER alice 0 * :Alice');
|
|
106
|
-
await alice.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
111
|
+
await alice.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
107
112
|
|
|
108
113
|
await bob.send('NICK bob');
|
|
109
114
|
await bob.send('USER bob 0 * :Bob');
|
|
110
|
-
await bob.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
115
|
+
await bob.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
111
116
|
|
|
112
117
|
await alice.send('JOIN #e2e');
|
|
113
118
|
await alice.waitFor((l) => l.includes('JOIN #e2e'));
|
|
114
|
-
await alice.waitFor((l) => l.startsWith(':irc.example.com 353 '));
|
|
119
|
+
await alice.waitFor((l) => l.startsWith(':irc.local.example.com 353 '));
|
|
115
120
|
|
|
116
121
|
await bob.send('JOIN #e2e');
|
|
117
122
|
// Bob sees his own JOIN + NAMES.
|
|
@@ -130,7 +135,7 @@ describe('local-cli e2e', () => {
|
|
|
130
135
|
await client.opened();
|
|
131
136
|
await client.send('NICK charlie');
|
|
132
137
|
await client.send('USER charlie 0 * :Charlie');
|
|
133
|
-
await client.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
138
|
+
await client.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
134
139
|
|
|
135
140
|
await client.send('PING :tok-12345');
|
|
136
141
|
await client.waitFor((l) => l === 'PONG :tok-12345');
|
|
@@ -143,7 +148,7 @@ describe('local-cli e2e', () => {
|
|
|
143
148
|
await client.opened();
|
|
144
149
|
await client.send('NICK dave');
|
|
145
150
|
await client.send('USER dave 0 * :Dave');
|
|
146
|
-
await client.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
151
|
+
await client.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
147
152
|
|
|
148
153
|
await client.send('BOGUSCMD');
|
|
149
154
|
await client.waitFor((l) => l.includes('421') && l.includes('BOGUSCMD'));
|
|
@@ -156,7 +161,7 @@ describe('local-cli e2e', () => {
|
|
|
156
161
|
await client.opened();
|
|
157
162
|
await client.send('NICK eve');
|
|
158
163
|
await client.send('USER eve 0 * :Eve');
|
|
159
|
-
await client.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
164
|
+
await client.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
160
165
|
|
|
161
166
|
await client.send('QUIT :bye');
|
|
162
167
|
// The disconnect effect should close the socket.
|
|
@@ -166,18 +171,42 @@ describe('local-cli e2e', () => {
|
|
|
166
171
|
await next.opened();
|
|
167
172
|
await next.send('NICK eve');
|
|
168
173
|
await next.send('USER eve 0 * :Eve2');
|
|
169
|
-
await next.waitFor((l) => l.startsWith(':irc.example.com 001 '), 2000);
|
|
174
|
+
await next.waitFor((l) => l.startsWith(':irc.local.example.com 001 '), 2000);
|
|
170
175
|
await next.close();
|
|
171
176
|
});
|
|
172
177
|
});
|
|
173
178
|
|
|
174
179
|
describe('local-cli error paths', () => {
|
|
175
180
|
it('rejects startLocalServer when the port is already in use', async () => {
|
|
176
|
-
const first = await startLocalServer({
|
|
177
|
-
|
|
181
|
+
const first = await startLocalServer({
|
|
182
|
+
port: 0,
|
|
183
|
+
hostname: '127.0.0.1',
|
|
184
|
+
serverName: 'irc.local.example.com',
|
|
185
|
+
networkName: 'LocalNet',
|
|
186
|
+
});
|
|
187
|
+
await expect(
|
|
188
|
+
startLocalServer({
|
|
189
|
+
port: first.port,
|
|
190
|
+
hostname: '127.0.0.1',
|
|
191
|
+
serverName: 'irc.local.example.com',
|
|
192
|
+
networkName: 'LocalNet',
|
|
193
|
+
}),
|
|
194
|
+
).rejects.toThrow();
|
|
178
195
|
await first.close();
|
|
179
196
|
});
|
|
180
197
|
|
|
198
|
+
it('rejects when serverName is omitted so the placeholder never leaks', async () => {
|
|
199
|
+
await expect(startLocalServer({ port: 0, hostname: '127.0.0.1' })).rejects.toThrowError(
|
|
200
|
+
/serverName/u,
|
|
201
|
+
);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('rejects when networkName is omitted', async () => {
|
|
205
|
+
await expect(
|
|
206
|
+
startLocalServer({ port: 0, hostname: '127.0.0.1', serverName: 'irc.local.example.com' }),
|
|
207
|
+
).rejects.toThrowError(/networkName/u);
|
|
208
|
+
});
|
|
209
|
+
|
|
181
210
|
it('validates the supplied config through the shared ServerConfigSchema at boot', async () => {
|
|
182
211
|
// nickLen=0 is rejected by the schema (must be a positive integer).
|
|
183
212
|
await expect(
|
|
@@ -192,7 +221,12 @@ describe('local-cli error paths', () => {
|
|
|
192
221
|
});
|
|
193
222
|
|
|
194
223
|
it('logs and recovers from a per-socket error without crashing the server', async () => {
|
|
195
|
-
const srv = await startLocalServer({
|
|
224
|
+
const srv = await startLocalServer({
|
|
225
|
+
port: 0,
|
|
226
|
+
hostname: '127.0.0.1',
|
|
227
|
+
serverName: 'irc.local.example.com',
|
|
228
|
+
networkName: 'LocalNet',
|
|
229
|
+
});
|
|
196
230
|
const client = new TestClient(`ws://127.0.0.1:${srv.port}/`);
|
|
197
231
|
await client.opened();
|
|
198
232
|
// Synthesize a socket-level error on the SERVER-side ws via the
|
|
@@ -218,20 +252,22 @@ describe('local-cli error paths', () => {
|
|
|
218
252
|
const srv = await startLocalServer({
|
|
219
253
|
port: 0,
|
|
220
254
|
hostname: '127.0.0.1',
|
|
255
|
+
serverName: 'irc.local.example.com',
|
|
256
|
+
networkName: 'LocalNet',
|
|
221
257
|
motdLines: ['first line of motd', 'second line of motd'],
|
|
222
258
|
});
|
|
223
259
|
const client = new TestClient(`ws://127.0.0.1:${srv.port}/`);
|
|
224
260
|
await client.opened();
|
|
225
261
|
await client.send('NICK karl');
|
|
226
262
|
await client.send('USER karl 0 * :Karl');
|
|
227
|
-
await client.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
263
|
+
await client.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
228
264
|
// Drain registration burst so the explicit MOTD responses below aren't
|
|
229
265
|
// confused with the placeholder MOTD emitted at registration.
|
|
230
266
|
client.received.length = 0;
|
|
231
267
|
await client.send('MOTD');
|
|
232
268
|
// Wait for end-of-MOTD so all 372 lines are buffered in `received`.
|
|
233
|
-
await client.waitFor((l) => l.startsWith(':irc.example.com 376 karl'));
|
|
234
|
-
const motdBody = client.received.filter((l) => l.startsWith(':irc.example.com 372 karl'));
|
|
269
|
+
await client.waitFor((l) => l.startsWith(':irc.local.example.com 376 karl'));
|
|
270
|
+
const motdBody = client.received.filter((l) => l.startsWith(':irc.local.example.com 372 karl'));
|
|
235
271
|
expect(motdBody.some((l) => l.includes('first line of motd'))).toBe(true);
|
|
236
272
|
expect(motdBody.some((l) => l.includes('second line of motd'))).toBe(true);
|
|
237
273
|
await client.close();
|
|
@@ -239,7 +275,11 @@ describe('local-cli error paths', () => {
|
|
|
239
275
|
});
|
|
240
276
|
|
|
241
277
|
it('defaults hostname to 127.0.0.1 when omitted', async () => {
|
|
242
|
-
const srv = await startLocalServer({
|
|
278
|
+
const srv = await startLocalServer({
|
|
279
|
+
port: 0,
|
|
280
|
+
serverName: 'irc.local.example.com',
|
|
281
|
+
networkName: 'LocalNet',
|
|
282
|
+
});
|
|
243
283
|
expect(srv.hostname).toBe('127.0.0.1');
|
|
244
284
|
// Sanity-check that the server is actually reachable on the default.
|
|
245
285
|
const client = new TestClient(`ws://127.0.0.1:${srv.port}/`);
|
|
@@ -249,7 +289,12 @@ describe('local-cli error paths', () => {
|
|
|
249
289
|
});
|
|
250
290
|
|
|
251
291
|
it('close() forcibly closes any still-open client sockets', async () => {
|
|
252
|
-
const srv = await startLocalServer({
|
|
292
|
+
const srv = await startLocalServer({
|
|
293
|
+
port: 0,
|
|
294
|
+
hostname: '127.0.0.1',
|
|
295
|
+
serverName: 'irc.local.example.com',
|
|
296
|
+
networkName: 'LocalNet',
|
|
297
|
+
});
|
|
253
298
|
const client = new TestClient(`ws://127.0.0.1:${srv.port}/`);
|
|
254
299
|
await client.opened();
|
|
255
300
|
// Close the server WITHOUT first closing the client — exercises the
|
|
@@ -260,14 +305,24 @@ describe('local-cli error paths', () => {
|
|
|
260
305
|
});
|
|
261
306
|
|
|
262
307
|
it('close() is a no-op when no connections are live', async () => {
|
|
263
|
-
const srv = await startLocalServer({
|
|
308
|
+
const srv = await startLocalServer({
|
|
309
|
+
port: 0,
|
|
310
|
+
hostname: '127.0.0.1',
|
|
311
|
+
serverName: 'irc.local.example.com',
|
|
312
|
+
networkName: 'LocalNet',
|
|
313
|
+
});
|
|
264
314
|
await srv.close();
|
|
265
315
|
// Closing again should not throw (idempotent).
|
|
266
316
|
await expect(srv.close()).resolves.toBeUndefined();
|
|
267
317
|
});
|
|
268
318
|
|
|
269
319
|
it('logs an actor failure without crashing the server (Error path)', async () => {
|
|
270
|
-
const srv = await startLocalServer({
|
|
320
|
+
const srv = await startLocalServer({
|
|
321
|
+
port: 0,
|
|
322
|
+
hostname: '127.0.0.1',
|
|
323
|
+
serverName: 'irc.local.example.com',
|
|
324
|
+
networkName: 'LocalNet',
|
|
325
|
+
});
|
|
271
326
|
// Force the runtime's `send` to throw a real Error so the actor's
|
|
272
327
|
// reducer pipeline rejects with an Error instance — exercises
|
|
273
328
|
// `stringifyErr`'s `instanceof Error` branch and `logError`.
|
|
@@ -291,14 +346,19 @@ describe('local-cli error paths', () => {
|
|
|
291
346
|
await next.opened();
|
|
292
347
|
await next.send('NICK grace');
|
|
293
348
|
await next.send('USER grace 0 * :Grace');
|
|
294
|
-
await next.waitFor((l) => l.startsWith(':irc.example.com 001 '), 2000);
|
|
349
|
+
await next.waitFor((l) => l.startsWith(':irc.local.example.com 001 '), 2000);
|
|
295
350
|
await next.close();
|
|
296
351
|
await client.close();
|
|
297
352
|
await srv.close();
|
|
298
353
|
});
|
|
299
354
|
|
|
300
355
|
it('logs a non-Error throw from the actor pipeline (defensive path)', async () => {
|
|
301
|
-
const srv = await startLocalServer({
|
|
356
|
+
const srv = await startLocalServer({
|
|
357
|
+
port: 0,
|
|
358
|
+
hostname: '127.0.0.1',
|
|
359
|
+
serverName: 'irc.local.example.com',
|
|
360
|
+
networkName: 'LocalNet',
|
|
361
|
+
});
|
|
302
362
|
const original = srv.runtime.send.bind(srv.runtime);
|
|
303
363
|
srv.runtime.send = async () => {
|
|
304
364
|
// Throw a non-Error value to exercise stringifyErr's else branch.
|
|
@@ -316,12 +376,17 @@ describe('local-cli error paths', () => {
|
|
|
316
376
|
});
|
|
317
377
|
|
|
318
378
|
it('cleans up when a client terminates abruptly (server-side error path)', async () => {
|
|
319
|
-
const srv = await startLocalServer({
|
|
379
|
+
const srv = await startLocalServer({
|
|
380
|
+
port: 0,
|
|
381
|
+
hostname: '127.0.0.1',
|
|
382
|
+
serverName: 'irc.local.example.com',
|
|
383
|
+
networkName: 'LocalNet',
|
|
384
|
+
});
|
|
320
385
|
const client = new TestClient(`ws://127.0.0.1:${srv.port}/`);
|
|
321
386
|
await client.opened();
|
|
322
387
|
await client.send('NICK ivan');
|
|
323
388
|
await client.send('USER ivan 0 * :Ivan');
|
|
324
|
-
await client.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
389
|
+
await client.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
325
390
|
const connId = await srv.runtime.lookupNick('ivan');
|
|
326
391
|
expect(connId).not.toBeNull();
|
|
327
392
|
expect(srv.runtime.hasConnection(connId as string)).toBe(true);
|
|
@@ -338,12 +403,17 @@ describe('local-cli error paths', () => {
|
|
|
338
403
|
});
|
|
339
404
|
|
|
340
405
|
it('delivers an ERROR notice when a reducer supplies a disconnect reason', async () => {
|
|
341
|
-
const srv = await startLocalServer({
|
|
406
|
+
const srv = await startLocalServer({
|
|
407
|
+
port: 0,
|
|
408
|
+
hostname: '127.0.0.1',
|
|
409
|
+
serverName: 'irc.local.example.com',
|
|
410
|
+
networkName: 'LocalNet',
|
|
411
|
+
});
|
|
342
412
|
const client = new TestClient(`ws://127.0.0.1:${srv.port}/`);
|
|
343
413
|
await client.opened();
|
|
344
414
|
await client.send('NICK heidi');
|
|
345
415
|
await client.send('USER heidi 0 * :Heidi');
|
|
346
|
-
await client.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
416
|
+
await client.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
347
417
|
|
|
348
418
|
// Call the runtime's disconnect directly with a reason (simulates a
|
|
349
419
|
// future flood-control / oper-kill effect). The runtime takes a ConnId;
|
|
@@ -370,12 +440,17 @@ describe('local-cli chathistory end-to-end', () => {
|
|
|
370
440
|
client.send(`NICK ${nick}\r\n`);
|
|
371
441
|
client.send(`USER ${nick} 0 * :${nick}\r\n`);
|
|
372
442
|
client.send('CAP END\r\n');
|
|
373
|
-
await client.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
443
|
+
await client.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
374
444
|
return client;
|
|
375
445
|
}
|
|
376
446
|
|
|
377
447
|
it('records a PRIVMSG and replays it via CHATHISTORY LATEST in a BATCH', async () => {
|
|
378
|
-
const srv = await startLocalServer({
|
|
448
|
+
const srv = await startLocalServer({
|
|
449
|
+
port: 0,
|
|
450
|
+
hostname: '127.0.0.1',
|
|
451
|
+
serverName: 'irc.local.example.com',
|
|
452
|
+
networkName: 'LocalNet',
|
|
453
|
+
});
|
|
379
454
|
const alice = await registerWithCaps(srv, 'ch-alice');
|
|
380
455
|
|
|
381
456
|
alice.send('JOIN #chathist-e2e\r\n');
|
|
@@ -395,7 +470,12 @@ describe('local-cli chathistory end-to-end', () => {
|
|
|
395
470
|
});
|
|
396
471
|
|
|
397
472
|
it('prepends a chathistory BATCH on rejoin after messages were sent', async () => {
|
|
398
|
-
const srv = await startLocalServer({
|
|
473
|
+
const srv = await startLocalServer({
|
|
474
|
+
port: 0,
|
|
475
|
+
hostname: '127.0.0.1',
|
|
476
|
+
serverName: 'irc.local.example.com',
|
|
477
|
+
networkName: 'LocalNet',
|
|
478
|
+
});
|
|
399
479
|
|
|
400
480
|
// Seed history as alice: join + send a message that bob has missed.
|
|
401
481
|
const alice = await registerWithCaps(srv, 'ch-seed');
|
|
@@ -429,6 +509,8 @@ describe('local-cli SASL PLAIN end-to-end', () => {
|
|
|
429
509
|
const srv = await startLocalServer({
|
|
430
510
|
port: 0,
|
|
431
511
|
hostname: '127.0.0.1',
|
|
512
|
+
serverName: 'irc.local.example.com',
|
|
513
|
+
networkName: 'LocalNet',
|
|
432
514
|
saslAccounts: [{ username: 'sasl-alice', password: 's3cret' }],
|
|
433
515
|
});
|
|
434
516
|
|
|
@@ -445,7 +527,7 @@ describe('local-cli SASL PLAIN end-to-end', () => {
|
|
|
445
527
|
client.send('NICK sasl-alice\r\n');
|
|
446
528
|
client.send('USER sasl-alice 0 * :Sasl Alice\r\n');
|
|
447
529
|
client.send('CAP END\r\n');
|
|
448
|
-
await client.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
530
|
+
await client.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
449
531
|
await client.close();
|
|
450
532
|
await srv.close();
|
|
451
533
|
});
|
|
@@ -454,6 +536,8 @@ describe('local-cli SASL PLAIN end-to-end', () => {
|
|
|
454
536
|
const srv = await startLocalServer({
|
|
455
537
|
port: 0,
|
|
456
538
|
hostname: '127.0.0.1',
|
|
539
|
+
serverName: 'irc.local.example.com',
|
|
540
|
+
networkName: 'LocalNet',
|
|
457
541
|
saslAccounts: [{ username: 'sasl-bob', password: 'right' }],
|
|
458
542
|
});
|
|
459
543
|
|
|
@@ -92,7 +92,13 @@ class TcpTestClient extends EventEmitter {
|
|
|
92
92
|
|
|
93
93
|
describe('local-cli TCP transport — startLocalServer tcpPort option', () => {
|
|
94
94
|
it('exposes tcpPort and testTcpSockets when tcpPort is provided', async () => {
|
|
95
|
-
const srv = await startLocalServer({
|
|
95
|
+
const srv = await startLocalServer({
|
|
96
|
+
port: 0,
|
|
97
|
+
tcpPort: 0,
|
|
98
|
+
hostname: '127.0.0.1',
|
|
99
|
+
serverName: 'irc.local.example.com',
|
|
100
|
+
networkName: 'LocalNet',
|
|
101
|
+
});
|
|
96
102
|
expect(srv.tcpPort).toBeGreaterThan(0);
|
|
97
103
|
expect(srv.testTcpSockets).toBeDefined();
|
|
98
104
|
expect(srv.testTcpSockets?.size).toBe(0);
|
|
@@ -100,16 +106,33 @@ describe('local-cli TCP transport — startLocalServer tcpPort option', () => {
|
|
|
100
106
|
});
|
|
101
107
|
|
|
102
108
|
it('omits tcpPort / testTcpSockets when tcpPort is not provided', async () => {
|
|
103
|
-
const srv = await startLocalServer({
|
|
109
|
+
const srv = await startLocalServer({
|
|
110
|
+
port: 0,
|
|
111
|
+
hostname: '127.0.0.1',
|
|
112
|
+
serverName: 'irc.local.example.com',
|
|
113
|
+
networkName: 'LocalNet',
|
|
114
|
+
});
|
|
104
115
|
expect(srv.tcpPort).toBeUndefined();
|
|
105
116
|
expect(srv.testTcpSockets).toBeUndefined();
|
|
106
117
|
await srv.close();
|
|
107
118
|
});
|
|
108
119
|
|
|
109
120
|
it('rejects when the TCP port is already in use', async () => {
|
|
110
|
-
const first = await startLocalServer({
|
|
121
|
+
const first = await startLocalServer({
|
|
122
|
+
port: 0,
|
|
123
|
+
tcpPort: 0,
|
|
124
|
+
hostname: '127.0.0.1',
|
|
125
|
+
serverName: 'irc.local.example.com',
|
|
126
|
+
networkName: 'LocalNet',
|
|
127
|
+
});
|
|
111
128
|
await expect(
|
|
112
|
-
startLocalServer({
|
|
129
|
+
startLocalServer({
|
|
130
|
+
port: 0,
|
|
131
|
+
tcpPort: first.tcpPort,
|
|
132
|
+
hostname: '127.0.0.1',
|
|
133
|
+
serverName: 'irc.local.example.com',
|
|
134
|
+
networkName: 'LocalNet',
|
|
135
|
+
}),
|
|
113
136
|
).rejects.toThrow();
|
|
114
137
|
await first.close();
|
|
115
138
|
});
|
|
@@ -119,7 +142,13 @@ describe('local-cli TCP transport — IRC flows', () => {
|
|
|
119
142
|
let server: LocalServer;
|
|
120
143
|
|
|
121
144
|
beforeAll(async () => {
|
|
122
|
-
server = await startLocalServer({
|
|
145
|
+
server = await startLocalServer({
|
|
146
|
+
port: 0,
|
|
147
|
+
tcpPort: 0,
|
|
148
|
+
hostname: '127.0.0.1',
|
|
149
|
+
serverName: 'irc.local.example.com',
|
|
150
|
+
networkName: 'LocalNet',
|
|
151
|
+
});
|
|
123
152
|
});
|
|
124
153
|
|
|
125
154
|
afterAll(async () => {
|
|
@@ -141,11 +170,11 @@ describe('local-cli TCP transport — IRC flows', () => {
|
|
|
141
170
|
|
|
142
171
|
await alice.send('NICK alice');
|
|
143
172
|
await alice.send('USER alice 0 * :Alice');
|
|
144
|
-
await alice.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
173
|
+
await alice.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
145
174
|
|
|
146
175
|
await bob.send('NICK bob');
|
|
147
176
|
await bob.send('USER bob 0 * :Bob');
|
|
148
|
-
await bob.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
177
|
+
await bob.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
149
178
|
|
|
150
179
|
await alice.send('JOIN #tcp-e2e');
|
|
151
180
|
await alice.waitFor((l) => l.includes('JOIN #tcp-e2e'));
|
|
@@ -164,7 +193,7 @@ describe('local-cli TCP transport — IRC flows', () => {
|
|
|
164
193
|
await client.opened();
|
|
165
194
|
await client.send('NICK pingUser');
|
|
166
195
|
await client.send('USER pingUser 0 * :Ping');
|
|
167
|
-
await client.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
196
|
+
await client.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
168
197
|
|
|
169
198
|
await client.send('PING :tcp-tok');
|
|
170
199
|
await client.waitFor((l) => l === 'PONG :tcp-tok');
|
|
@@ -176,7 +205,7 @@ describe('local-cli TCP transport — IRC flows', () => {
|
|
|
176
205
|
await client.opened();
|
|
177
206
|
await client.send('NICK splitter');
|
|
178
207
|
await client.send('USER splitter 0 * :Splitter');
|
|
179
|
-
await client.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
208
|
+
await client.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
180
209
|
|
|
181
210
|
// Split a PING line mid-token across two writes. The server's LineScanner
|
|
182
211
|
// must buffer the partial line and emit one PONG when the terminator lands.
|
|
@@ -191,7 +220,7 @@ describe('local-cli TCP transport — IRC flows', () => {
|
|
|
191
220
|
await client.opened();
|
|
192
221
|
// NICK and USER bundled into one write, CRLF-separated.
|
|
193
222
|
await client.writeRaw('NICK bundler\r\nUSER bundler 0 * :Bundler\r\n');
|
|
194
|
-
await client.waitFor((l) => l.startsWith(':irc.example.com 001 bundler'));
|
|
223
|
+
await client.waitFor((l) => l.startsWith(':irc.local.example.com 001 bundler'));
|
|
195
224
|
await client.close();
|
|
196
225
|
});
|
|
197
226
|
|
|
@@ -200,7 +229,7 @@ describe('local-cli TCP transport — IRC flows', () => {
|
|
|
200
229
|
await client.opened();
|
|
201
230
|
await client.send('NICK termUser');
|
|
202
231
|
await client.send('USER termUser 0 * :Term');
|
|
203
|
-
await client.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
232
|
+
await client.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
204
233
|
|
|
205
234
|
const connId = await server.runtime.lookupNick('termUser');
|
|
206
235
|
expect(connId).not.toBeNull();
|
|
@@ -217,7 +246,7 @@ describe('local-cli TCP transport — IRC flows', () => {
|
|
|
217
246
|
await client.opened();
|
|
218
247
|
await client.send('NICK reasonUser');
|
|
219
248
|
await client.send('USER reasonUser 0 * :Reason');
|
|
220
|
-
await client.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
249
|
+
await client.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
221
250
|
|
|
222
251
|
// Synthesize a flood-control / oper-kill style disconnect with a reason.
|
|
223
252
|
const connId = await server.runtime.lookupNick('reasonUser');
|
|
@@ -247,7 +276,7 @@ describe('local-cli TCP transport — IRC flows', () => {
|
|
|
247
276
|
await next.opened();
|
|
248
277
|
await next.send('NICK tcpRecover');
|
|
249
278
|
await next.send('USER tcpRecover 0 * :TcpRecover');
|
|
250
|
-
await next.waitFor((l) => l.startsWith(':irc.example.com 001 '), 2000);
|
|
279
|
+
await next.waitFor((l) => l.startsWith(':irc.local.example.com 001 '), 2000);
|
|
251
280
|
await next.close();
|
|
252
281
|
await client.close();
|
|
253
282
|
});
|
|
@@ -255,7 +284,13 @@ describe('local-cli TCP transport — IRC flows', () => {
|
|
|
255
284
|
|
|
256
285
|
describe('local-cli TCP × WS cross-transport interop', () => {
|
|
257
286
|
it('delivers a TCP PRIVMSG to a WS client in the same channel', async () => {
|
|
258
|
-
const srv = await startLocalServer({
|
|
287
|
+
const srv = await startLocalServer({
|
|
288
|
+
port: 0,
|
|
289
|
+
tcpPort: 0,
|
|
290
|
+
hostname: '127.0.0.1',
|
|
291
|
+
serverName: 'irc.local.example.com',
|
|
292
|
+
networkName: 'LocalNet',
|
|
293
|
+
});
|
|
259
294
|
|
|
260
295
|
// WS side.
|
|
261
296
|
const wsClient = new WebSocket(`ws://127.0.0.1:${srv.port}/`);
|
|
@@ -288,7 +323,7 @@ describe('local-cli TCP × WS cross-transport interop', () => {
|
|
|
288
323
|
|
|
289
324
|
wsSend('NICK wsUser');
|
|
290
325
|
wsSend('USER wsUser 0 * :WS');
|
|
291
|
-
await wsWaitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
326
|
+
await wsWaitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
292
327
|
wsSend('JOIN #interop');
|
|
293
328
|
await wsWaitFor((l) => l.includes('JOIN #interop'));
|
|
294
329
|
|
|
@@ -297,7 +332,7 @@ describe('local-cli TCP × WS cross-transport interop', () => {
|
|
|
297
332
|
await tcp.opened();
|
|
298
333
|
await tcp.send('NICK tcpUser');
|
|
299
334
|
await tcp.send('USER tcpUser 0 * :TCP');
|
|
300
|
-
await tcp.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
335
|
+
await tcp.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
301
336
|
await tcp.send('JOIN #interop');
|
|
302
337
|
await tcp.waitFor((l) => l.includes('JOIN #interop'));
|
|
303
338
|
|
|
@@ -315,7 +350,13 @@ describe('local-cli TCP × WS cross-transport interop', () => {
|
|
|
315
350
|
|
|
316
351
|
describe('local-cli TCP transport — shutdown', () => {
|
|
317
352
|
it('close() tears down the TCP server and live TCP sockets', async () => {
|
|
318
|
-
const srv = await startLocalServer({
|
|
353
|
+
const srv = await startLocalServer({
|
|
354
|
+
port: 0,
|
|
355
|
+
tcpPort: 0,
|
|
356
|
+
hostname: '127.0.0.1',
|
|
357
|
+
serverName: 'irc.local.example.com',
|
|
358
|
+
networkName: 'LocalNet',
|
|
359
|
+
});
|
|
319
360
|
const client = new TcpTestClient('127.0.0.1', srv.tcpPort as number);
|
|
320
361
|
await client.opened();
|
|
321
362
|
// Closing the server destroys the server-side socket; the client side
|
|
@@ -327,12 +368,23 @@ describe('local-cli TCP transport — shutdown', () => {
|
|
|
327
368
|
});
|
|
328
369
|
|
|
329
370
|
it('close() is a no-op for TCP when tcpPort was never enabled', async () => {
|
|
330
|
-
const srv = await startLocalServer({
|
|
371
|
+
const srv = await startLocalServer({
|
|
372
|
+
port: 0,
|
|
373
|
+
hostname: '127.0.0.1',
|
|
374
|
+
serverName: 'irc.local.example.com',
|
|
375
|
+
networkName: 'LocalNet',
|
|
376
|
+
});
|
|
331
377
|
await expect(srv.close()).resolves.toBeUndefined();
|
|
332
378
|
});
|
|
333
379
|
|
|
334
380
|
it('logs and recovers from a per-TCP-socket error without crashing the server', async () => {
|
|
335
|
-
const srv = await startLocalServer({
|
|
381
|
+
const srv = await startLocalServer({
|
|
382
|
+
port: 0,
|
|
383
|
+
tcpPort: 0,
|
|
384
|
+
hostname: '127.0.0.1',
|
|
385
|
+
serverName: 'irc.local.example.com',
|
|
386
|
+
networkName: 'LocalNet',
|
|
387
|
+
});
|
|
336
388
|
const client = new TcpTestClient('127.0.0.1', srv.tcpPort as number);
|
|
337
389
|
await client.opened();
|
|
338
390
|
// Wait for the server-side 'connection' handler to register the socket
|
|
@@ -92,7 +92,12 @@ describe('local-cli IRCv3 WebSocket subprotocol', () => {
|
|
|
92
92
|
let server: LocalServer;
|
|
93
93
|
|
|
94
94
|
beforeAll(async () => {
|
|
95
|
-
server = await startLocalServer({
|
|
95
|
+
server = await startLocalServer({
|
|
96
|
+
port: 0,
|
|
97
|
+
hostname: '127.0.0.1',
|
|
98
|
+
serverName: 'irc.local.example.com',
|
|
99
|
+
networkName: 'LocalNet',
|
|
100
|
+
});
|
|
96
101
|
});
|
|
97
102
|
|
|
98
103
|
afterAll(async () => {
|
|
@@ -121,7 +126,7 @@ describe('local-cli IRCv3 WebSocket subprotocol', () => {
|
|
|
121
126
|
await client.opened();
|
|
122
127
|
await client.send('NICK spec-bin');
|
|
123
128
|
await client.send('USER spec-bin 0 * :Spec Bin');
|
|
124
|
-
const welcome = await client.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
129
|
+
const welcome = await client.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
125
130
|
expect(welcome).toContain('spec-bin');
|
|
126
131
|
|
|
127
132
|
await client.send('PING :bin-tok');
|
|
@@ -172,7 +177,7 @@ describe('local-cli IRCv3 WebSocket subprotocol', () => {
|
|
|
172
177
|
// One bare line per WS message (no trailing CRLF).
|
|
173
178
|
await client.send('NICK spec-alice');
|
|
174
179
|
await client.send('USER spec-alice 0 * :Spec Alice');
|
|
175
|
-
const welcome = await client.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
180
|
+
const welcome = await client.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
176
181
|
expect(welcome).toContain('spec-alice');
|
|
177
182
|
|
|
178
183
|
// Every received message must be a single IRC line with no trailing CRLF
|
|
@@ -189,7 +194,7 @@ describe('local-cli IRCv3 WebSocket subprotocol', () => {
|
|
|
189
194
|
await client.opened();
|
|
190
195
|
await client.send('NICK spec-bob');
|
|
191
196
|
await client.send('USER spec-bob 0 * :Spec Bob');
|
|
192
|
-
await client.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
197
|
+
await client.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
193
198
|
|
|
194
199
|
// The welcome sequence is many numerics (001..005, 375/372/376). In spec
|
|
195
200
|
// mode each arrives as its own WS message; a legacy batch would have
|
|
@@ -204,7 +209,7 @@ describe('local-cli IRCv3 WebSocket subprotocol', () => {
|
|
|
204
209
|
await client.opened();
|
|
205
210
|
await client.send('NICK spec-ping');
|
|
206
211
|
await client.send('USER spec-ping 0 * :Spec Ping');
|
|
207
|
-
await client.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
212
|
+
await client.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
208
213
|
|
|
209
214
|
await client.send('PING :spec-tok');
|
|
210
215
|
const pong = await client.waitFor((l) => l === 'PONG :spec-tok');
|
|
@@ -219,7 +224,7 @@ describe('local-cli IRCv3 WebSocket subprotocol', () => {
|
|
|
219
224
|
await client.send('USER spec-budget 0 * :Spec Budget');
|
|
220
225
|
// Drain the entire registration burst (001..376) so delayed numerics
|
|
221
226
|
// don't pollute the post-overlong-message assertion below.
|
|
222
|
-
await client.waitFor((l) => l.startsWith(':irc.example.com 376 '));
|
|
227
|
+
await client.waitFor((l) => l.startsWith(':irc.local.example.com 376 '));
|
|
223
228
|
|
|
224
229
|
client.received.length = 0;
|
|
225
230
|
// 511-byte payload: `PRIVMSG #x :` (12 bytes) + 499 chars = 511 total.
|
|
@@ -244,7 +249,7 @@ describe('local-cli IRCv3 WebSocket subprotocol', () => {
|
|
|
244
249
|
await client.opened();
|
|
245
250
|
await client.send(`NICK ${nick}`);
|
|
246
251
|
await client.send(`USER ${nick} 0 * :Spec Disc`);
|
|
247
|
-
await client.waitFor((l) => l.startsWith(':irc.example.com 001 '));
|
|
252
|
+
await client.waitFor((l) => l.startsWith(':irc.local.example.com 001 '));
|
|
248
253
|
|
|
249
254
|
const connId = await server.runtime.lookupNick(nick);
|
|
250
255
|
expect(connId).not.toBeNull();
|