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
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ platform-agnostic core, and two thin adapters run it on **Cloudflare Workers**
|
|
|
6
6
|
|
|
7
7
|
One TypeScript codebase. Two serverless substrates.
|
|
8
8
|
|
|
9
|
-
> **Status:** **v0.
|
|
9
|
+
> **Status:** **v0.7.0 (preview).** The pure protocol core, the
|
|
10
10
|
> `IrcRuntime` port + in-memory runtime, a runnable local CLI server,
|
|
11
11
|
> the **Cloudflare Workers** adapter, and the **AWS** (API Gateway
|
|
12
12
|
> WebSocket + Lambda + DynamoDB + CDK) adapter are all functional and
|
|
@@ -15,14 +15,27 @@ One TypeScript codebase. Two serverless substrates.
|
|
|
15
15
|
> clients — Cloudflare via Spectrum + a Container origin, AWS via a
|
|
16
16
|
> Network Load Balancer + Lambda streaming — alongside the WebSocket
|
|
17
17
|
> path, plus **SASL `EXTERNAL` via mTLS** and a **Cloudflare D1-backed
|
|
18
|
-
> SASL account store**. v0.5.0
|
|
18
|
+
> SASL account store**. v0.5.0 completed the **protocol surface**: the
|
|
19
19
|
> deferred IRC verbs (`KILL`, `REHASH`, `LUSERS` + `STATS`, `TRACE`,
|
|
20
20
|
> `WALLOPS`, `SETNAME`) all land, and the S2S (`CONNECT` / `SQUIT` /
|
|
21
21
|
> `LINKS`) and obsolete RFC 2812 (`SERVICE` / `SUMMON` / `USERS`) verbs
|
|
22
|
-
> are formally dropped
|
|
23
|
-
>
|
|
24
|
-
>
|
|
25
|
-
>
|
|
22
|
+
> are formally dropped. v0.6.0 landed the **IRCv3 extension sweep
|
|
23
|
+
> (Phase 12)** — ten caps (`account-notify`, `msgid`,
|
|
24
|
+
> `standard-replies`, `MONITOR`, `labeled-response`, `sts`,
|
|
25
|
+
> `draft/typing`, `draft/multiline`, `draft/read-marker`,
|
|
26
|
+
> `draft/pre-away`), the ISUPPORT tokens for the new caps, and the
|
|
27
|
+
> read-only user mode `S` (TLS connected). **v0.7.0 lands the web
|
|
28
|
+
> client (Phase 14)**: a vendored **Kiwi IRC** SPA served at `/app/`
|
|
29
|
+
> and a static landing page at `/` directly by the Cloudflare Worker,
|
|
30
|
+
> with **Cross-Site WebSocket Hijacking (CSWSH) defense** wired into
|
|
31
|
+
> the WS upgrade path (same-origin auto-derive by default, optional
|
|
32
|
+
> `WEB_ORIGINS` allowlist for cross-origin deploys) — the browser
|
|
33
|
+
> opens a native `wss://` straight to the existing IRC-over-WebSocket
|
|
34
|
+
> endpoint. Remaining 0.x work: the client compatibility sweep
|
|
35
|
+
> (WeeChat / HexChat / IRCCloud / TheLounge), the Playwright
|
|
36
|
+
> browser-driven e2e for the SPA, IRC services (NickServ / ChanServ /
|
|
37
|
+
> HostServ / OperServ / MemoServ), and driving the remaining
|
|
38
|
+
> sub-100% packages to full coverage. See `CHANGELOG.md` for the
|
|
26
39
|
> per-release manifests.
|
|
27
40
|
|
|
28
41
|
---
|
|
@@ -124,12 +137,14 @@ ServerlessIRCd/
|
|
|
124
137
|
│ ├── cf-adapter/ Durable Objects + CfRuntime
|
|
125
138
|
│ └── aws-adapter/ Lambda + DynamoDB + AwsRuntime
|
|
126
139
|
├── apps/
|
|
127
|
-
│ ├── cf-worker/ worker entry, DO migrations, bindings
|
|
140
|
+
│ ├── cf-worker/ worker entry, DO migrations, bindings, [assets] + CSWSH gate
|
|
128
141
|
│ ├── cf-tcp-container/ Spectrum + Container origin for irc+tls :6697
|
|
129
142
|
│ ├── aws-stack/ CDK stack (APIGW WS + NLB + Lambda streaming + DynamoDB)
|
|
143
|
+
│ ├── web/ vendored Kiwi IRC SPA (→ /app/) + static landing page (→ /)
|
|
130
144
|
│ └── local-cli/ runnable WS + TCP server using in-memory-runtime
|
|
131
145
|
├── tools/
|
|
132
146
|
│ ├── tcp-ws-forwarder/ local TCP↔ws/wss bridge for stock IRC clients
|
|
147
|
+
│ ├── load-test/ synthetic WebSocket IRC client farm (10k conns, p50/p95/p99, drop rate)
|
|
133
148
|
│ ├── ci-hardening/ coverage-gate + mutation-config validators
|
|
134
149
|
│ ├── seed-aws-accounts.ts scrypt-hash SASL PLAIN accounts into DynamoDB
|
|
135
150
|
│ └── seed-cf-accounts.ts scrypt-hash SASL PLAIN accounts into Cloudflare D1
|
|
@@ -163,6 +178,7 @@ pin is resolved automatically:
|
|
|
163
178
|
```bash
|
|
164
179
|
corepack enable # lets the pinned pnpm@11 run on any Node ≥ 24
|
|
165
180
|
|
|
181
|
+
git submodule update --init # docs + apps/web Kiwi upstream sources
|
|
166
182
|
pnpm install # install workspace deps
|
|
167
183
|
|
|
168
184
|
pnpm build # build all packages (turbo)
|
|
@@ -180,6 +196,14 @@ pnpm mutation # Stryker spot-check on irc-core (protocol + commands)
|
|
|
180
196
|
pnpm clean # remove dist/coverage/.turbo + node_modules
|
|
181
197
|
```
|
|
182
198
|
|
|
199
|
+
The web client (`apps/web`) vendors Kiwi IRC as a git submodule. Building it
|
|
200
|
+
needs the upstream sources (`git submodule update --init apps/web/upstream`)
|
|
201
|
+
and `yarn` (Kiwi ships a `yarn.lock`; `corepack enable` provides it):
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
pnpm --filter web build # builds the Kiwi SPA into apps/web/dist/app/ (/app/)
|
|
205
|
+
```
|
|
206
|
+
|
|
183
207
|
Coverage reports are written to `packages/*/coverage/`. CI (`.github/workflows/ci.yml`)
|
|
184
208
|
runs lint, typecheck, the coverage gate, the parametrized contract suite, and a
|
|
185
209
|
Stryker mutation spot-check on every push and pull request. Coverage thresholds
|
|
@@ -230,6 +254,8 @@ On startup it logs a JSON line like:
|
|
|
230
254
|
| `--tcp-port <n>` | `<port> + 1` | TCP port for RFC-style IRC clients. |
|
|
231
255
|
| `--no-tcp` | | Disable the TCP listener (WebSocket only). |
|
|
232
256
|
| `--motd-file <p>` | built-in | Read MOTD lines from this file (one per line). |
|
|
257
|
+
| `--server-name <h>`| `irc.localhost` | Server hostname advertised in `001`/`005`. Production MUST override. |
|
|
258
|
+
| `--network-name <n>`| `LocalNet` | Network name advertised in `005 NETWORK=…`. |
|
|
233
259
|
| `-h, --help` | | Show help and exit. |
|
|
234
260
|
|
|
235
261
|
Pass `--host 0.0.0.0` to expose the server on all interfaces. `SIGINT` /
|
|
@@ -302,6 +328,115 @@ against any line-oriented WebSocket endpoint.
|
|
|
302
328
|
|
|
303
329
|
---
|
|
304
330
|
|
|
331
|
+
## Load testing (`tools/load-test`)
|
|
332
|
+
|
|
333
|
+
A synthetic WebSocket IRC client farm (PLAN §7, Phase 7). It opens N
|
|
334
|
+
connections to a `ws://` / `wss://` target, registers each (NICK/USER),
|
|
335
|
+
joins a channel, and optionally chats. Per-phase **p50 / p95 / p99
|
|
336
|
+
latency** (connect / register / join / message) and the **drop rate**
|
|
337
|
+
are captured, then printed as a markdown report to stdout (and written
|
|
338
|
+
to `--report <path>` when given).
|
|
339
|
+
|
|
340
|
+
Build the workspace, then start a run (from the repo root):
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
pnpm build
|
|
344
|
+
|
|
345
|
+
pnpm --filter load-test start -- \
|
|
346
|
+
--target wss://irc.staging.example.com/ \
|
|
347
|
+
--connections 10000 --concurrency 200 --ramp-ms 60000 \
|
|
348
|
+
--channel '#loadtest' --messages 3 --platform cf-staging \
|
|
349
|
+
--report reports/cf-staging-2026-08-03.md
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
The harness drives the WebSocket transport only (the serverless default);
|
|
353
|
+
a TCP IRC client is not what a serverless load test exercises. Each
|
|
354
|
+
connection is independent — the package ships no `@serverless-ircd/*`
|
|
355
|
+
runtime dependency (only the IRC line framing shared with the
|
|
356
|
+
forwarder), so it can be pointed at any WebSocket IRC endpoint.
|
|
357
|
+
|
|
358
|
+
### CLI flags
|
|
359
|
+
|
|
360
|
+
| Flag | Default | Description |
|
|
361
|
+
|-------------------------|-------------|----------------------------------------------------------|
|
|
362
|
+
| `--target <url>` | (required) | Upstream `ws://` or `wss://` URL to load test. |
|
|
363
|
+
| `--connections <n>` | `1000` | Total connections to open. |
|
|
364
|
+
| `--concurrency <n>` | `50` | Concurrent in-flight connection attempts during ramp-up. |
|
|
365
|
+
| `--ramp-ms <ms>` | `30000` | Ramp-up duration over which connections are opened. |
|
|
366
|
+
| `--channel <c>` | `#loadtest` | Channel every client JOINs. |
|
|
367
|
+
| `--messages <n>` | `0` | PRIVMSGs each client sends after JOIN (`0` = connect only). |
|
|
368
|
+
| `--echo-message` | | Negotiate `echo-message` so sent PRIVMSGs round-trip. |
|
|
369
|
+
| `--platform <name>` | `unknown` | Label baked into the report header. |
|
|
370
|
+
| `--report <path>` | | Write the markdown report to this path. |
|
|
371
|
+
| `-h, --help` | | Show help and exit. |
|
|
372
|
+
|
|
373
|
+
`SIGINT` / `SIGTERM` cancel an in-flight run gracefully (drains live
|
|
374
|
+
sockets, prints the partial report). The package ships its own vitest
|
|
375
|
+
suite at ≥90% coverage (config parsing, framing, harness, metrics,
|
|
376
|
+
report).
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## Web client (`apps/web`)
|
|
381
|
+
|
|
382
|
+
A vendored **Kiwi IRC** SPA served at `/app/` and a static project
|
|
383
|
+
landing page served at `/`, both baked out of `apps/web/dist/` and
|
|
384
|
+
served directly by the Cloudflare Worker via its `[assets]` binding.
|
|
385
|
+
The browser opens a native `wss://` straight to the Worker's existing
|
|
386
|
+
IRC-over-WebSocket endpoint — no proxy, gateway, or transport
|
|
387
|
+
adaptation in the hot path. See `docs/WebClientGuide.md` for the
|
|
388
|
+
end-to-end contributor/operator guide.
|
|
389
|
+
|
|
390
|
+
Build the SPA + landing page (needs the submodule + `yarn`, provided by
|
|
391
|
+
`corepack enable`):
|
|
392
|
+
|
|
393
|
+
```bash
|
|
394
|
+
git submodule update --init apps/web/upstream # one-time per clone
|
|
395
|
+
pnpm --filter web build:staging # or :prod / default
|
|
396
|
+
# → apps/web/dist/index.html (landing page, served at /)
|
|
397
|
+
# → apps/web/dist/app/index.html (Kiwi SPA, served at /app/)
|
|
398
|
+
# → apps/web/dist/app/static/config.json (baked, env-specific)
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Then run everything locally (Worker serves SPA + landing page + WS on
|
|
402
|
+
one origin):
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
pnpm build # workspace packages
|
|
406
|
+
pnpm --filter @serverless-ircd/cf-worker dev # http://localhost:8787
|
|
407
|
+
# / → landing page
|
|
408
|
+
# /app/ → Kiwi SPA (opens ws://localhost:8787/)
|
|
409
|
+
# /health → plaintext liveness
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
Deploy staging (Worker + assets in one command):
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
pnpm deploy:cf:staging # wrangler deploy --env staging
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### WebSocket Origin policy (CSWSH defense)
|
|
419
|
+
|
|
420
|
+
WebSocket upgrades do **not** follow the same-origin policy, so the
|
|
421
|
+
Worker rejects browser upgrades whose `Origin` is not allowed — see
|
|
422
|
+
`apps/cf-worker/src/origin-allowlist.ts` and the SPA guide §5. Two
|
|
423
|
+
modes, evaluated in order:
|
|
424
|
+
|
|
425
|
+
1. **Explicit allowlist** — set `WEB_ORIGINS` (comma-separated) for
|
|
426
|
+
cross-origin deploys (SPA on a different domain than the Worker,
|
|
427
|
+
e.g. Cloudflare Pages). Only listed origins proceed.
|
|
428
|
+
2. **Same-origin auto-derive (the default)** — when `WEB_ORIGINS` is
|
|
429
|
+
unset/empty, the Worker compares the browser's `Origin` against the
|
|
430
|
+
request's own origin. Match → proceed; mismatch → `403 Forbidden`.
|
|
431
|
+
|
|
432
|
+
`WEB_ORIGINS` is **optional** — same-origin auto-derive needs zero
|
|
433
|
+
per-env config and works for `*.workers.dev`, custom domains, and
|
|
434
|
+
preview URLs alike. Non-browser clients (curl, WeeChat, the
|
|
435
|
+
`tcp-ws-forwarder`, scripted harnesses) never send `Origin` and pass
|
|
436
|
+
through unchanged in both modes.
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
305
440
|
## WebSocket transport (IRCv3 subprotocols)
|
|
306
441
|
|
|
307
442
|
The WebSocket entry points (the local CLI, the Cloudflare Worker, and the
|
|
@@ -373,21 +508,23 @@ cleanly. Per-release manifests live in `CHANGELOG.md`.
|
|
|
373
508
|
- **Phase 2** — `IrcRuntime` port, in-memory runtime, `ConnectionActor`,
|
|
374
509
|
local CLI, parametrized contract suite. ✅ landed in **v0.1.0**.
|
|
375
510
|
- **Phase 3** — Cloudflare adapter (ConnectionDO / ChannelDO / RegistryDO).
|
|
376
|
-
✅ landed in **v0.1.0** (staging
|
|
511
|
+
✅ landed in **v0.1.0** (staging deploy via CI; prod deploy is manual).
|
|
377
512
|
- **Phase 4** — AWS adapter (APIGW WS + Lambda + DynamoDB + CDK).
|
|
378
|
-
✅ landed in **v0.2.0** (staging
|
|
513
|
+
✅ landed in **v0.2.0** (staging deploy via CI; prod deploy is manual).
|
|
379
514
|
- **Phase 5** — Observability, security hardening, config, CI gates.
|
|
380
515
|
✅ landed across v0.2.0–v0.4.0 (logger port, server-password, cloaking,
|
|
381
516
|
admission limits, coverage + mutation gates, OPER credential auth, the
|
|
382
|
-
Cloudflare config loader, and AWS max-clients admission)
|
|
517
|
+
Cloudflare config loader, and AWS max-clients admission); the
|
|
518
|
+
required-`serverName` gate closed in **v0.6.0**.
|
|
383
519
|
- **Phase 6** — TLS hardening & raw TCP transport (`irc+tls :6697`).
|
|
384
520
|
✅ landed in **v0.4.0**: generalized `ConnectionActor` transport seam,
|
|
385
521
|
Cloudflare Spectrum + Container origin, AWS NLB + Lambda streaming,
|
|
386
522
|
mTLS → SASL `EXTERNAL`, transport-parametrized contract suite, ADR-009.
|
|
387
|
-
- **Phase 7
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
523
|
+
- **Phase 7 — Load testing & compatibility sweep.** 🔄 partial as of
|
|
524
|
+
**v0.6.0**: the `tools/load-test` 10k-connection harness is in-tree;
|
|
525
|
+
the formal 10k-connection report per platform and the client
|
|
526
|
+
compatibility matrix (WeeChat / HexChat / IRCCloud / TheLounge) are
|
|
527
|
+
still pending.
|
|
391
528
|
- **Phase 8 — PLAN-FIXES remediation.** ✅ landed across v0.3.0–v0.4.0
|
|
392
529
|
(MessageStore wired into all three adapters, AccountStore end-to-end,
|
|
393
530
|
the DynamoDB `Accounts` table decided, CF enumeration RPCs, AWS QUIT
|
|
@@ -406,6 +543,33 @@ cleanly. Per-release manifests live in `CHANGELOG.md`.
|
|
|
406
543
|
the formal drop of the S2S (`CONNECT`/`SQUIT`/`LINKS`) and obsolete
|
|
407
544
|
RFC 2812 (`SERVICE`/`SUMMON`/`USERS`) verbs. The protocol surface is
|
|
408
545
|
feature-complete for modern clients.
|
|
546
|
+
- **Phase 12 — IRCv3 extension sweep.** ✅ landed in **v0.6.0**: ten
|
|
547
|
+
negotiated caps (`account-notify`, `msgid`, `standard-replies`,
|
|
548
|
+
`MONITOR`, `labeled-response`, `sts`, `draft/typing`,
|
|
549
|
+
`draft/multiline`, `draft/read-marker`, `draft/pre-away`), the
|
|
550
|
+
ISUPPORT tokens for the new caps (`MONITOR`/`MULTILINE`/`TYPING`/
|
|
551
|
+
`STATUSMSG`/`EXTBAN`/`ACCOUNTEXTBAN`), the read-only user mode `S`
|
|
552
|
+
(TLS connected), and the async push fanout seam (Registry hooks →
|
|
553
|
+
`MONITOR` online/offline, `BroadcastWallops`-style caps-gated
|
|
554
|
+
delivery).
|
|
555
|
+
- **Phase 13 — IRC services.** ⬳ pending: `ServicesStore` port +
|
|
556
|
+
NickServ / ChanServ / HostServ / OperServ / MemoServ. Adapter
|
|
557
|
+
backends (D1 / DynamoDB) ship alongside; until then
|
|
558
|
+
`draft/read-marker` and `draft/pre-away` use in-memory reference
|
|
559
|
+
stores and do not survive an adapter restart.
|
|
560
|
+
- **Phase 14 — Web client.** 🔄 partial as of **v0.7.0**: a vendored
|
|
561
|
+
Kiwi IRC SPA is served at `/app/` and a static landing page at `/`
|
|
562
|
+
by the Cloudflare Worker via an `[assets]` binding, with a Zod-
|
|
563
|
+
validated per-env config matrix and **CSWSH defense** (same-origin
|
|
564
|
+
auto-derive + optional `WEB_ORIGINS` allowlist). The Playwright
|
|
565
|
+
headless-browser e2e is the remaining open ticket.
|
|
566
|
+
See `docs/WebClientGuide.md` for the contributor/operator guide.
|
|
567
|
+
- **Coverage hardening.** 🔄 partial as of **v0.7.0**: `aws-adapter`
|
|
568
|
+
and `aws-stack` clear the 90% gate (v0.6.0); the CF packages run
|
|
569
|
+
under istanbul (v0.6.0). The remaining packages (`cf-adapter`,
|
|
570
|
+
`local-cli`, `load-test`, `cf-tcp-container`, `tcp-ws-forwarder`,
|
|
571
|
+
`irc-test-support`, `web`) are **above the gate but below 100%** —
|
|
572
|
+
follow-ups drive each to 100%.
|
|
409
573
|
|
|
410
574
|
---
|
|
411
575
|
|
|
@@ -416,6 +580,8 @@ cleanly. Per-release manifests live in `CHANGELOG.md`.
|
|
|
416
580
|
channel), `TOPIC`, `KICK`, `INVITE`, `NAMES`, `LIST`, `WHO`, `WHOIS`,
|
|
417
581
|
`WHOWAS`, `MOTD`, `AWAY`, `OPER` (credential auth → `o` user mode), the
|
|
418
582
|
query verbs `VERSION`, `TIME`, `ADMIN`, `INFO`, `USERHOST`, `ISON`, the
|
|
583
|
+
modern-online-presence verb `MONITOR` (`+`/`-`/`C`/`L`/`S` with the
|
|
584
|
+
`730`/`731`/`732`/`734` numerics and async online/offline push), the
|
|
419
585
|
network-statistics verbs `LUSERS` and `STATS` (`u` uptime + `l` link-info
|
|
420
586
|
letters; unknown letters get the charybdis-style empty body), the
|
|
421
587
|
routing-diagnostic verb `TRACE` (oper-gated per-connection detail), the
|
|
@@ -429,15 +595,31 @@ v0.5.0 — they return `421 ERR_UNKNOWNCOMMAND`, and their reserved
|
|
|
429
595
|
numerics have been removed. S2S linking is a PLAN non-goal.)
|
|
430
596
|
|
|
431
597
|
**Channel modes:** `o v b i k l t n m s p`.
|
|
432
|
-
**User modes:** `i`, `o` (local only), `w`, `s
|
|
598
|
+
**User modes:** `i`, `o` (local only), `w`, `s`, and the read-only `S`
|
|
599
|
+
(TLS connected — set by the transport at registration, surfaced in WHOIS
|
|
600
|
+
via `276 RPL_WHOISSECURE`, not settable via `MODE`).
|
|
433
601
|
|
|
434
602
|
**IRCv3 extensions (negotiated via `CAP`):** `message-tags` (incl. the
|
|
435
|
-
`TAGMSG` command), `server-time`, `account-tag`, `
|
|
603
|
+
`TAGMSG` command), `server-time`, `account-tag`, `account-notify`
|
|
604
|
+
(pushes `ACCOUNT` on SASL login/logout), `echo-message`, `batch`,
|
|
436
605
|
`sasl` (`PLAIN` always; `EXTERNAL` via mTLS when a client-cert trust store
|
|
437
|
-
is bound),
|
|
438
|
-
`
|
|
439
|
-
`draft/chathistory
|
|
440
|
-
|
|
606
|
+
is bound), `multi-prefix`, `away-notify`, `chghost`, `invite-notify`,
|
|
607
|
+
`extended-join`, `msgid` (`@+msgid=<id>` on every PRIVMSG/NOTICE/TAGMSG,
|
|
608
|
+
shared between live and `draft/chathistory` replay), `standard-replies`
|
|
609
|
+
(`FAIL`/`WARN`/`NOTE` replacements for a curated numeric subset),
|
|
610
|
+
`MONITOR=<n>` (config-driven ceiling, default 30), `labeled-response`
|
|
611
|
+
(`+label=<id>` → `BATCH +id labeled-response … BATCH -id` wrapping),
|
|
612
|
+
`sts` (Strict Transport Security — conditionally advertised with
|
|
613
|
+
`duration`/`port`/optional `preload` from `ServerConfig` when configured,
|
|
614
|
+
so compliant clients upgrade from plaintext to TLS and pin the secure
|
|
615
|
+
listener), `draft/chathistory`, `safelist`, `draft/typing` (typing-indicator
|
|
616
|
+
broadcast), `draft/multiline=<n>` (multi-line `BATCH`, default 4096-byte
|
|
617
|
+
budget), `draft/read-marker` (per-account persisted last-read), and
|
|
618
|
+
`draft/pre-away` (per-account persisted away reason, replayed at
|
|
619
|
+
identify). Case-insensitive nick/channel comparison uses **RFC 1459**
|
|
620
|
+
case-mapping (advertised via `005 CASEMAPPING=rfc1459`). The new
|
|
621
|
+
`MONITOR` / `MULTILINE` / `TYPING` / `STATUSMSG` / `EXTBAN` /
|
|
622
|
+
`ACCOUNTEXTBAN` ISUPPORT tokens are advertised from `ServerConfig`.
|
|
441
623
|
|
|
442
624
|
**Transport:** deployed stacks (Cloudflare Workers, AWS API Gateway) speak
|
|
443
625
|
WebSocket text frames by default (one IRC message per frame, with tolerance
|
|
@@ -457,9 +639,24 @@ bridges a stock TCP client to a deployed WebSocket endpoint.
|
|
|
457
639
|
gates), v0.3.0 (OPER, TAGMSG, RFC 1459 case-mapping, real MOTD,
|
|
458
640
|
ChannelRegistryDO, SASL account persistence), v0.4.0 (dual transport
|
|
459
641
|
`irc+tls :6697`, SASL `EXTERNAL`/mTLS, WHOWAS + remaining verbs, D1 account
|
|
460
|
-
store, Node 24/pnpm 11),
|
|
642
|
+
store, Node 24/pnpm 11), v0.5.0 (deferred IRC verbs `KILL` / `REHASH`
|
|
461
643
|
/ `LUSERS` + `STATS` / `TRACE` / `WALLOPS` / `SETNAME`, plus the formal
|
|
462
|
-
drop of the S2S and obsolete RFC 2812 verbs).
|
|
644
|
+
drop of the S2S and obsolete RFC 2812 verbs), v0.6.0 (the IRCv3
|
|
645
|
+
extension sweep — `account-notify`, `msgid`, `standard-replies`,
|
|
646
|
+
`MONITOR`, `labeled-response`, `sts`, `draft/typing`, `draft/multiline`,
|
|
647
|
+
`draft/read-marker`, `draft/pre-away`, plus user mode `S`; the
|
|
648
|
+
required-`serverName` gate; `tools/load-test`; aws-adapter / aws-stack
|
|
649
|
+
coverage unblocks; CF packages moved to istanbul; deploys now
|
|
650
|
+
manual-only), and **v0.7.0** (the web client — vendored Kiwi IRC SPA
|
|
651
|
+
at `/app/`, static landing page at `/`, Cloudflare Worker `[assets]`
|
|
652
|
+
binding, per-env Zod-validated config matrix, and CSWSH defense via
|
|
653
|
+
same-origin auto-derive + optional `WEB_ORIGINS`; `/health` becomes
|
|
654
|
+
an explicit route; mutation-killing tests for `irc-core`'s parser /
|
|
655
|
+
batch / numerics).
|
|
656
|
+
- `docs/WebClientGuide.md` — end-to-end contributor/operator doc for the
|
|
657
|
+
web client: build pipeline, per-env config matrix, CSWSH rationale and
|
|
658
|
+
the optional `WEB_ORIGINS` var, local dev, optional Cloudflare Pages
|
|
659
|
+
alternative, and troubleshooting.
|
|
463
660
|
- `docs/Cloudflare-TCP-Deployment.md` and `docs/AWS-TCP-Deployment.md` —
|
|
464
661
|
end-to-end guides for the `:6697` TCP+TLS variants (Spectrum/Container on
|
|
465
662
|
CF, NLB + Lambda streaming on AWS), including mTLS trust-store setup.
|
package/apps/aws-stack/README.md
CHANGED
|
@@ -60,6 +60,39 @@ export AWS_DEFAULT_REGION=us-east-1
|
|
|
60
60
|
LOCALSTACK=1 pnpm test
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
+
## Test-mode bundle stub (why coverage does not re-bundle)
|
|
64
|
+
|
|
65
|
+
The stack ships four `NodejsFunction` constructs (the wss `IrcHandler`,
|
|
66
|
+
`IrcSweeper`, `IrcPingChecker`, and the optional NLB `IrcNlbHandler`).
|
|
67
|
+
Each one invokes esbuild at synth time to bundle the shared handler
|
|
68
|
+
entry. Repeated across the ~80 synth assertions in `tests/stack.test.ts`,
|
|
69
|
+
that meant the bundler ran **dozens to hundreds of times per coverage
|
|
70
|
+
invocation**, exhausting CI memory before vitest could print its
|
|
71
|
+
coverage table — the failure mode presented as a wall of
|
|
72
|
+
`Bundling asset TestStack/IrcHandler/Code/Stage…` lines followed by
|
|
73
|
+
`[ELIFECYCLE] Command failed with exit code N` and no report.
|
|
74
|
+
|
|
75
|
+
The fix is a single env-var toggle consumed by `makeLambda()` in
|
|
76
|
+
`src/aws-stack.ts`:
|
|
77
|
+
|
|
78
|
+
| Env var | Effect on the synth |
|
|
79
|
+
|-----------------------------------|-----------------------------------------------------------------------|
|
|
80
|
+
| `IRC_AWS_STACK_TEST_NO_BUNDLE=1` | Every Lambda is emitted as an inline `Code.fromInline('void 0')` zip; esbuild is not invoked at all. CloudFormation shape (runtime, env vars, IAM grants, route wiring, …) is identical to the production synth. |
|
|
81
|
+
| _(unset)_ | Production path: every Lambda is a real `NodejsFunction` whose asset is esbuild-bundled at synth time. This is what `cdk synth` / `cdk deploy` run. |
|
|
82
|
+
|
|
83
|
+
`tests/vitest.setup.ts` sets the flag once per test file so the entire
|
|
84
|
+
`pnpm test` / `pnpm coverage` run stays on the stub path. Production
|
|
85
|
+
synth (`bin/aws.ts` via the CDK CLI) never loads vitest, so it cannot
|
|
86
|
+
accidentally inherit the flag.
|
|
87
|
+
|
|
88
|
+
A small `tests/synth-no-bundle.test.ts` locks both paths in place: it
|
|
89
|
+
asserts the stub emits `Code: { ZipFile }` when the flag is set, and
|
|
90
|
+
exercises the production `NodejsFunction` return once (with the flag
|
|
91
|
+
explicitly unset) so the production branch stays covered. The
|
|
92
|
+
end-to-end guard lives in `tests/coverage-command.test.ts`, which
|
|
93
|
+
spawns `pnpm --filter @serverless-ircd/aws-stack coverage` in a
|
|
94
|
+
subprocess and asserts the report prints an `All files` row.
|
|
95
|
+
|
|
63
96
|
## Stack outputs
|
|
64
97
|
|
|
65
98
|
| Output | Description |
|
|
@@ -37,8 +37,8 @@ import {
|
|
|
37
37
|
import { LambdaTarget } from 'aws-cdk-lib/aws-elasticloadbalancingv2-targets';
|
|
38
38
|
import { Rule, Schedule } from 'aws-cdk-lib/aws-events';
|
|
39
39
|
import { LambdaFunction } from 'aws-cdk-lib/aws-events-targets';
|
|
40
|
-
import { Runtime } from 'aws-cdk-lib/aws-lambda';
|
|
41
|
-
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
|
|
40
|
+
import { Code, Function as Lambda, Runtime } from 'aws-cdk-lib/aws-lambda';
|
|
41
|
+
import { NodejsFunction, type NodejsFunctionProps } from 'aws-cdk-lib/aws-lambda-nodejs';
|
|
42
42
|
import type { Construct } from 'constructs';
|
|
43
43
|
|
|
44
44
|
/** Stage name served by the auto-deploying WebSocket stage. */
|
|
@@ -64,8 +64,14 @@ const PING_CHECKER_SCHEDULE_RATE = Duration.minutes(1);
|
|
|
64
64
|
*/
|
|
65
65
|
const DEFAULT_ENVIRONMENT_NAME = 'staging';
|
|
66
66
|
|
|
67
|
-
/**
|
|
68
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Fallback server identity, used when the corresponding prop is omitted.
|
|
69
|
+
* Deliberately not `irc.example.com` (the retired placeholder) so a
|
|
70
|
+
* deployment that forgets to override advertises an obviously-local name
|
|
71
|
+
* instead of a deceptive example domain. Production stacks MUST pass
|
|
72
|
+
* `serverName` explicitly.
|
|
73
|
+
*/
|
|
74
|
+
const DEFAULT_SERVER_NAME = 'irc.localhost';
|
|
69
75
|
const DEFAULT_NETWORK_NAME = 'ExampleNet';
|
|
70
76
|
const DEFAULT_MOTD_LINES = [
|
|
71
77
|
'Welcome to the ServerlessIRCd deployment.',
|
|
@@ -74,6 +80,22 @@ const DEFAULT_MOTD_LINES = [
|
|
|
74
80
|
|
|
75
81
|
const HERE = fileURLToPath(new URL('.', import.meta.url));
|
|
76
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Env-var flag that flips {@link makeLambda} onto a stub code path which
|
|
85
|
+
* emits an inline Lambda zip and skips esbuild entirely. The IrcAwsStack
|
|
86
|
+
* test suite synthesizes the stack dozens of times per coverage run; if
|
|
87
|
+
* each synth invoked esbuild the runner exhausted its memory budget
|
|
88
|
+
* before the vitest coverage table could print. The stub preserves every
|
|
89
|
+
* CloudFormation shape the rest of the test suite asserts on (runtime,
|
|
90
|
+
* env vars, IAM grants, route wiring, …) while making the bundling step
|
|
91
|
+
* a no-op.
|
|
92
|
+
*
|
|
93
|
+
* Intentionally a plain `process.env` read (not a CDK context var or
|
|
94
|
+
* construct prop) so the toggle lives in one place — `tests/vitest.setup.ts`
|
|
95
|
+
* — and cannot leak into a production synth driven by `bin/aws.ts`.
|
|
96
|
+
*/
|
|
97
|
+
const TEST_NO_BUNDLE_FLAG = 'IRC_AWS_STACK_TEST_NO_BUNDLE';
|
|
98
|
+
|
|
77
99
|
/**
|
|
78
100
|
* Construct props for {@link IrcAwsStack}. Acts as the single
|
|
79
101
|
* configuration bag extended by deploy-time knobs (environment name,
|
|
@@ -93,7 +115,8 @@ export interface IrcStackProps extends StackProps {
|
|
|
93
115
|
|
|
94
116
|
/**
|
|
95
117
|
* Server name advertised to clients (`SERVER_NAME` / the `001`
|
|
96
|
-
* welcome source).
|
|
118
|
+
* welcome source). Falls back to `irc.localhost` when omitted; production
|
|
119
|
+
* stacks MUST pass an explicit value.
|
|
97
120
|
*/
|
|
98
121
|
readonly serverName?: string;
|
|
99
122
|
|
|
@@ -150,7 +173,7 @@ export class IrcAwsStack extends Stack {
|
|
|
150
173
|
}),
|
|
151
174
|
);
|
|
152
175
|
|
|
153
|
-
const handler =
|
|
176
|
+
const handler = makeLambda(this, 'IrcHandler', {
|
|
154
177
|
runtime: Runtime.NODEJS_24_X,
|
|
155
178
|
entry: handlerEntry(),
|
|
156
179
|
handler: 'handler',
|
|
@@ -166,7 +189,7 @@ export class IrcAwsStack extends Stack {
|
|
|
166
189
|
// bundle (tree-shaken to the sweeper path by esbuild at synth time).
|
|
167
190
|
// Catches connections that vanished without APIGW emitting $disconnect;
|
|
168
191
|
// see packages/aws-adapter/src/handlers/sweeper.ts.
|
|
169
|
-
const sweeper =
|
|
192
|
+
const sweeper = makeLambda(this, 'IrcSweeper', {
|
|
170
193
|
runtime: Runtime.NODEJS_24_X,
|
|
171
194
|
entry: handlerEntry(),
|
|
172
195
|
handler: 'sweeperHandler',
|
|
@@ -180,7 +203,7 @@ export class IrcAwsStack extends Stack {
|
|
|
180
203
|
// those that never answer; see
|
|
181
204
|
// packages/aws-adapter/src/handlers/ping-checker.ts. The Cloudflare
|
|
182
205
|
// adapter implements the equivalent flow inside `ConnectionDO.alarm()`.
|
|
183
|
-
const pingChecker =
|
|
206
|
+
const pingChecker = makeLambda(this, 'IrcPingChecker', {
|
|
184
207
|
runtime: Runtime.NODEJS_24_X,
|
|
185
208
|
entry: handlerEntry(),
|
|
186
209
|
handler: 'pingCheckerHandler',
|
|
@@ -318,7 +341,7 @@ export class IrcAwsStack extends Stack {
|
|
|
318
341
|
// Shares the bundled aws-adapter code (tree-shaken to the nlb-stream
|
|
319
342
|
// path by esbuild at synth time). Reuses the same DynamoDB tables,
|
|
320
343
|
// server identity, and MANAGEMENT_URL as the wss handler.
|
|
321
|
-
const nlbHandler =
|
|
344
|
+
const nlbHandler = makeLambda(this, 'IrcNlbHandler', {
|
|
322
345
|
runtime: Runtime.NODEJS_24_X,
|
|
323
346
|
entry: handlerEntry(),
|
|
324
347
|
handler: 'nlbStreamHandler',
|
|
@@ -391,6 +414,48 @@ function handlerEntry(): string {
|
|
|
391
414
|
return resolve(HERE, '..', '..', '..', 'packages', 'aws-adapter', 'src', 'handlers', 'index.ts');
|
|
392
415
|
}
|
|
393
416
|
|
|
417
|
+
/**
|
|
418
|
+
* Internal Lambda-construct props: `NodejsFunctionProps` narrowed so the
|
|
419
|
+
* runtime and handler are required. Every call site in the stack already
|
|
420
|
+
* passes both explicitly; this type just makes that invariant visible to
|
|
421
|
+
* the compiler so {@link makeLambda} can hand them straight through to
|
|
422
|
+
* the stub `Function` constructor without a non-null assertion.
|
|
423
|
+
*/
|
|
424
|
+
type StrictLambdaProps = NodejsFunctionProps & {
|
|
425
|
+
readonly runtime: Runtime;
|
|
426
|
+
readonly handler: string;
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Builds a Lambda function for the IRC stack, selecting between the real
|
|
431
|
+
* esbuild-bundled `NodejsFunction` and a no-op stub `Function` based on
|
|
432
|
+
* {@link TEST_NO_BUNDLE_FLAG}.
|
|
433
|
+
*
|
|
434
|
+
* Production synth (`bin/aws.ts`) never sets the flag, so it always lands
|
|
435
|
+
* on the `NodejsFunction` branch and produces a real bundled asset. The
|
|
436
|
+
* test suite sets the flag via `tests/vitest.setup.ts`, which routes
|
|
437
|
+
* every Lambda onto the inline-zip stub so the synth does not invoke
|
|
438
|
+
* esbuild and the coverage pipeline can complete.
|
|
439
|
+
*
|
|
440
|
+
* The stub mirrors only the fields the rest of the stack mutates
|
|
441
|
+
* post-construction (`runtime`, `handler`, env vars, IAM grants) — it
|
|
442
|
+
* deliberately ignores `entry` and `bundling` because both are
|
|
443
|
+
* meaningless once esbuild is bypassed.
|
|
444
|
+
*
|
|
445
|
+
* Returns the base `Function` type (not `NodejsFunction`) so callers
|
|
446
|
+
* cannot reach for bundler-only APIs on the stub path.
|
|
447
|
+
*/
|
|
448
|
+
function makeLambda(scope: Construct, id: string, props: StrictLambdaProps): Lambda {
|
|
449
|
+
if (process.env[TEST_NO_BUNDLE_FLAG] === '1') {
|
|
450
|
+
return new Lambda(scope, id, {
|
|
451
|
+
runtime: props.runtime,
|
|
452
|
+
handler: props.handler,
|
|
453
|
+
code: Code.fromInline('void 0'),
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
return new NodejsFunction(scope, id, props);
|
|
457
|
+
}
|
|
458
|
+
|
|
394
459
|
/**
|
|
395
460
|
* Builds the physical DynamoDB table name for a logical id under the
|
|
396
461
|
* given environment. Capitalises the environment's first letter so it
|