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
|
@@ -21,29 +21,63 @@ export interface Capability {
|
|
|
21
21
|
value?: string;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
/** Wire name of the SASL capability. */
|
|
25
|
+
export const SASL_CAP_NAME = 'sasl';
|
|
26
|
+
|
|
27
|
+
/** Wire name of the IRCv3 `draft/multiline` capability. */
|
|
28
|
+
export const MULTILINE_CAP_NAME = 'draft/multiline';
|
|
29
|
+
|
|
30
|
+
/** Wire name of the IRCv3 `draft/read-marker` capability. */
|
|
31
|
+
export const READ_MARKER_CAP_NAME = 'draft/read-marker';
|
|
32
|
+
|
|
33
|
+
/** Wire name of the IRCv3 `draft/pre-away` capability. */
|
|
34
|
+
export const PRE_AWAY_CAP_NAME = 'draft/pre-away';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Default byte budget advertised in `draft/multiline=<n>` and enforced on
|
|
38
|
+
* every finalized multi-line batch. Mirrors the IRCv3 spec's canonical
|
|
39
|
+
* recommendation; deployments override via `ServerConfig.multilineMaxBytes`.
|
|
40
|
+
*/
|
|
41
|
+
export const DEFAULT_MULTILINE_MAX_BYTES = 4096;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Builds the `draft/multiline` cap *value* (the substring after `=`) for the
|
|
45
|
+
* advertised byte budget. The {@link CapReducer} and the multi-line reducer
|
|
46
|
+
* share this helper so the wire token has one source of truth.
|
|
47
|
+
*/
|
|
48
|
+
export function multilineCapValue(maxBytes: number): string {
|
|
49
|
+
return String(maxBytes);
|
|
50
|
+
}
|
|
51
|
+
|
|
24
52
|
/**
|
|
25
53
|
* Every cap advertised by this server in PLAN §3, in canonical (alpha) order
|
|
26
54
|
* so wire output is deterministic across runs.
|
|
27
55
|
*/
|
|
28
56
|
export const SUPPORTED_CAPABILITIES: ReadonlyArray<Capability> = Object.freeze([
|
|
57
|
+
{ name: 'account-notify' },
|
|
29
58
|
{ name: 'account-tag' },
|
|
30
59
|
{ name: 'away-notify' },
|
|
31
60
|
{ name: 'batch' },
|
|
32
61
|
{ name: 'chghost' },
|
|
33
62
|
{ name: 'draft/chathistory' },
|
|
63
|
+
{ name: 'draft/multiline', value: multilineCapValue(DEFAULT_MULTILINE_MAX_BYTES) },
|
|
64
|
+
{ name: 'draft/pre-away' },
|
|
65
|
+
{ name: 'draft/read-marker' },
|
|
66
|
+
{ name: 'draft/typing' },
|
|
34
67
|
{ name: 'echo-message' },
|
|
35
68
|
{ name: 'extended-join' },
|
|
36
69
|
{ name: 'invite-notify' },
|
|
70
|
+
{ name: 'labeled-response' },
|
|
37
71
|
{ name: 'message-tags' },
|
|
72
|
+
{ name: 'msgid' },
|
|
73
|
+
{ name: 'monitor' },
|
|
38
74
|
{ name: 'multi-prefix' },
|
|
39
75
|
{ name: 'safelist' },
|
|
40
76
|
{ name: 'sasl', value: 'PLAIN' },
|
|
41
77
|
{ name: 'server-time' },
|
|
78
|
+
{ name: 'standard-replies' },
|
|
42
79
|
]);
|
|
43
80
|
|
|
44
|
-
/** Wire name of the SASL capability. */
|
|
45
|
-
export const SASL_CAP_NAME = 'sasl';
|
|
46
|
-
|
|
47
81
|
/**
|
|
48
82
|
* Returns the `sasl` cap value to advertise given whether an mTLS identity
|
|
49
83
|
* provider is bound for the connection.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IRCv3 `sts` (Strict Transport Security) policy.
|
|
3
|
+
*
|
|
4
|
+
* Spec: https://ircv3.net/specs/extensions/sts
|
|
5
|
+
*
|
|
6
|
+
* The server advertises an `sts` capability whose value is a
|
|
7
|
+
* comma-separated `key=value` policy. Compliant clients inspect the
|
|
8
|
+
* policy and:
|
|
9
|
+
*
|
|
10
|
+
* - On a plaintext connection: reconnect over TLS to the advertised
|
|
11
|
+
* `port` and pin it for `duration` seconds. `preload` (optional)
|
|
12
|
+
* signals the client may hard-code the policy into a preload list.
|
|
13
|
+
* - On a TLS connection: only the `duration` is locked in — `port`
|
|
14
|
+
* is meaningless once the client is already on the secure listener,
|
|
15
|
+
* and `preload` is an upgrade-time directive.
|
|
16
|
+
*
|
|
17
|
+
* Config-driven only: there is no per-connection protocol state. The
|
|
18
|
+
* deployment supplies the policy through {@link StsConfig} on
|
|
19
|
+
* {@link ServerConfig}; when omitted entirely the cap is not advertised.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* IRC-over-TLS port per RFC 7193 / IRCv3 STS spec examples. Used as the
|
|
24
|
+
* fallback `port` when a deployment turns on STS without pinning a port.
|
|
25
|
+
*/
|
|
26
|
+
export const DEFAULT_STS_PORT = 6697;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Default pin duration (30 days, in seconds). Mirrors the IRCv3 spec's
|
|
30
|
+
* canonical example and what mainstream IRC networks publish. Deployments
|
|
31
|
+
* override via {@link StsConfig.duration}.
|
|
32
|
+
*/
|
|
33
|
+
export const DEFAULT_STS_DURATION_SECONDS = 30 * 24 * 60 * 60;
|
|
34
|
+
|
|
35
|
+
/** Wire name of the STS capability. */
|
|
36
|
+
export const STS_CAP_NAME = 'sts';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Raw STS policy as supplied by {@link ServerConfig}. Every field is
|
|
40
|
+
* optional; {@link stsCapValue} applies the documented defaults when a
|
|
41
|
+
* field is omitted. `undefined` (the whole object) disables STS.
|
|
42
|
+
*
|
|
43
|
+
* Optional fields are widened to `T | undefined` to mirror Zod's
|
|
44
|
+
* `.optional()` output under `exactOptionalPropertyTypes: true` so the
|
|
45
|
+
* parsed config is structurally assignable here.
|
|
46
|
+
*/
|
|
47
|
+
export interface StsPolicy {
|
|
48
|
+
/** Pin duration in seconds. Defaults to 30 days. */
|
|
49
|
+
readonly duration?: number;
|
|
50
|
+
/** TLS listener port advertised on plaintext connections. Defaults to 6697. */
|
|
51
|
+
readonly port?: number;
|
|
52
|
+
/** Optional preload directive (plaintext advertisements only). */
|
|
53
|
+
readonly preload?: boolean | undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Builds the IRCv3 `sts` capability *value* (the substring after `sts=`)
|
|
58
|
+
* for the given policy and transport.
|
|
59
|
+
*
|
|
60
|
+
* Returns `undefined` when STS is disabled (`policy` undefined), so the cap
|
|
61
|
+
* table consumer can omit the token entirely from `CAP LS`.
|
|
62
|
+
*
|
|
63
|
+
* On a plaintext transport the full policy is advertised:
|
|
64
|
+
* `duration=<seconds>,port=<tls-port>[,preload]`
|
|
65
|
+
*
|
|
66
|
+
* On a TLS transport only `duration=<seconds>` is sent — the port is
|
|
67
|
+
* meaningless (the client is already on the secure listener) and
|
|
68
|
+
* `preload` is an upgrade-time directive that does not apply.
|
|
69
|
+
*
|
|
70
|
+
* Missing `duration`/`port` fall back to {@link DEFAULT_STS_DURATION_SECONDS}
|
|
71
|
+
* and {@link DEFAULT_STS_PORT} so a deployment can enable STS with a bare
|
|
72
|
+
* `{}` policy and still publish a sensible value.
|
|
73
|
+
*/
|
|
74
|
+
export function stsCapValue(policy: StsPolicy | undefined, secure: boolean): string | undefined {
|
|
75
|
+
if (policy === undefined) return undefined;
|
|
76
|
+
const duration = policy.duration ?? DEFAULT_STS_DURATION_SECONDS;
|
|
77
|
+
if (secure) {
|
|
78
|
+
return `duration=${duration}`;
|
|
79
|
+
}
|
|
80
|
+
const port = policy.port ?? DEFAULT_STS_PORT;
|
|
81
|
+
const parts = [`duration=${duration}`, `port=${port}`];
|
|
82
|
+
if (policy.preload === true) parts.push('preload');
|
|
83
|
+
return parts.join(',');
|
|
84
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Effect } from '../effects.js';
|
|
2
|
+
import type { Effect as EffectType, RawLine } from '../effects.js';
|
|
3
|
+
import type { ChanName } from '../state/channel.js';
|
|
4
|
+
import { hostmaskOf } from '../state/connection.js';
|
|
5
|
+
import type { ConnectionState } from '../state/connection.js';
|
|
6
|
+
|
|
7
|
+
export interface AccountNotifyInput {
|
|
8
|
+
conn: ConnectionState;
|
|
9
|
+
/**
|
|
10
|
+
* New account name on login. Pass `undefined` to signal a logout, which
|
|
11
|
+
* serializes the account field as `*` per IRCv3 `account-notify`.
|
|
12
|
+
*/
|
|
13
|
+
account?: string | undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Builds the cap-split Broadcast effects that announce an `ACCOUNT` login
|
|
18
|
+
* (or logout) to peers in every shared channel.
|
|
19
|
+
*
|
|
20
|
+
* Mirrors the `emitChghost` broadcast helper: each joined channel produces
|
|
21
|
+
* one {@link Effect.broadcast} carrying empty `lines` (so non-cap peers
|
|
22
|
+
* receive nothing) and the `ACCOUNT` line in `capLines` for peers that
|
|
23
|
+
* negotiated `account-notify`. The sender is excluded via `except` unless
|
|
24
|
+
* they negotiated `echo-message`, in which case a single echo
|
|
25
|
+
* {@link Effect.send} is appended (one echo regardless of how many shared
|
|
26
|
+
* channels the user occupies).
|
|
27
|
+
*
|
|
28
|
+
* Returns `[]` before registration completes or when the connection holds
|
|
29
|
+
* no joined channels — there is no audience to notify.
|
|
30
|
+
*/
|
|
31
|
+
export function emitAccountNotify(input: AccountNotifyInput): EffectType[] {
|
|
32
|
+
const { conn, account } = input;
|
|
33
|
+
|
|
34
|
+
if (conn.registration !== 'registered') return [];
|
|
35
|
+
if (conn.joinedChannels.size === 0) return [];
|
|
36
|
+
|
|
37
|
+
const source = hostmaskOf(conn) ?? conn.nick ?? '?';
|
|
38
|
+
const accountField = account ?? '*';
|
|
39
|
+
const line: RawLine = { text: `:${source} ACCOUNT ${accountField}` };
|
|
40
|
+
|
|
41
|
+
const effects: EffectType[] = [];
|
|
42
|
+
for (const chan of conn.joinedChannels) {
|
|
43
|
+
effects.push(Effect.broadcast(chan as ChanName, [], conn.id, 'account-notify', [line]));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// IRCv3 echo-message: the sender observes its own ACCOUNT change exactly
|
|
47
|
+
// once, regardless of how many channels it shares with peers.
|
|
48
|
+
if (conn.caps.has('echo-message')) {
|
|
49
|
+
effects.push(Effect.send(conn.id, [line]));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return effects;
|
|
53
|
+
}
|
|
@@ -27,6 +27,7 @@ import type { ChanName } from '../state/channel.js';
|
|
|
27
27
|
import { hostmaskOf } from '../state/connection.js';
|
|
28
28
|
import type { ConnectionState } from '../state/connection.js';
|
|
29
29
|
import type { Reducer } from '../types.js';
|
|
30
|
+
import { clearPersistedAway, persistAway } from './pre-away.js';
|
|
30
31
|
|
|
31
32
|
/**
|
|
32
33
|
* Handles `AWAY [:<reason>]`.
|
|
@@ -45,7 +46,10 @@ export const awayReducer: Reducer<ConnectionState> = (state, msg, ctx) => {
|
|
|
45
46
|
const nick = state.nick ?? '*';
|
|
46
47
|
|
|
47
48
|
if (isSettingAway) {
|
|
48
|
-
|
|
49
|
+
// IRCv3 draft/pre-away: persist the reason keyed by account when an
|
|
50
|
+
// AwayStore is bound and the connection is identified. The in-memory
|
|
51
|
+
// away is also set so the current session tracks the new reason.
|
|
52
|
+
persistAway(ctx, reason as string);
|
|
49
53
|
effects.push(
|
|
50
54
|
Effect.send(ctx.connId, [
|
|
51
55
|
{
|
|
@@ -54,8 +58,10 @@ export const awayReducer: Reducer<ConnectionState> = (state, msg, ctx) => {
|
|
|
54
58
|
]),
|
|
55
59
|
);
|
|
56
60
|
} else {
|
|
57
|
-
//
|
|
58
|
-
|
|
61
|
+
// IRCv3 draft/pre-away: clear the persisted reason on unset so a
|
|
62
|
+
// reconnect does not re-mark the user away. The in-memory away is
|
|
63
|
+
// also cleared so the session's view stays consistent.
|
|
64
|
+
clearPersistedAway(ctx);
|
|
59
65
|
effects.push(
|
|
60
66
|
Effect.send(ctx.connId, [
|
|
61
67
|
{
|
|
@@ -33,13 +33,17 @@
|
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
35
|
import {
|
|
36
|
+
DEFAULT_MULTILINE_MAX_BYTES,
|
|
37
|
+
MULTILINE_CAP_NAME,
|
|
36
38
|
SASL_CAP_NAME,
|
|
37
39
|
SUPPORTED_CAPABILITIES,
|
|
38
40
|
capToLsString,
|
|
39
41
|
isSupportedCap,
|
|
42
|
+
multilineCapValue,
|
|
40
43
|
saslCapValue,
|
|
41
44
|
splitCapsForWire,
|
|
42
45
|
} from '../caps/capabilities.js';
|
|
46
|
+
import { STS_CAP_NAME, stsCapValue } from '../caps/sts.js';
|
|
43
47
|
import { Effect } from '../effects.js';
|
|
44
48
|
import type { RawLine } from '../effects.js';
|
|
45
49
|
import { Numerics } from '../protocol/numerics.js';
|
|
@@ -91,11 +95,21 @@ function handleLs(state: ConnectionState, ctx: Ctx): ReducerResult<ConnectionSta
|
|
|
91
95
|
|
|
92
96
|
const target = state.nick ?? '*';
|
|
93
97
|
const hasMtls = ctx.mtlsIdentity !== undefined;
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
const multilineMax = ctx.serverConfig.multilineMaxBytes ?? DEFAULT_MULTILINE_MAX_BYTES;
|
|
99
|
+
const tokens = SUPPORTED_CAPABILITIES.map((cap) => {
|
|
100
|
+
if (cap.name === SASL_CAP_NAME) {
|
|
101
|
+
return capToLsString({ ...cap, value: saslCapValue(hasMtls) });
|
|
102
|
+
}
|
|
103
|
+
if (cap.name === MULTILINE_CAP_NAME) {
|
|
104
|
+
return capToLsString({ ...cap, value: multilineCapValue(multilineMax) });
|
|
105
|
+
}
|
|
106
|
+
return capToLsString(cap);
|
|
107
|
+
});
|
|
108
|
+
// STS is conditionally advertised: appended only when the deployment
|
|
109
|
+
// supplies a policy, and the value is per-transport (plaintext sees the
|
|
110
|
+
// full `duration,port[,preload]`; TLS sees only `duration`).
|
|
111
|
+
const stsValue = stsCapValue(ctx.serverConfig.sts, ctx.connection.secure);
|
|
112
|
+
if (stsValue !== undefined) tokens.push(`${STS_CAP_NAME}=${stsValue}`);
|
|
99
113
|
const maxLen = maxCapsContentLen(ctx, target);
|
|
100
114
|
const chunks = splitCapsForWire(tokens, maxLen);
|
|
101
115
|
|
|
@@ -164,8 +178,12 @@ function handleReq(
|
|
|
164
178
|
const requested = rawCaps.split(' ').filter((c) => c.length > 0);
|
|
165
179
|
const target = state.nick ?? '*';
|
|
166
180
|
|
|
181
|
+
// STS is conditionally supported: ACKed only when the deployment supplies
|
|
182
|
+
// a policy. Other caps consult the static table.
|
|
183
|
+
const stsConfigured = ctx.serverConfig.sts !== undefined;
|
|
167
184
|
const allSupported = requested.every((tok) => {
|
|
168
185
|
const name = tok.startsWith('-') ? tok.slice(1) : tok;
|
|
186
|
+
if (name === STS_CAP_NAME) return stsConfigured;
|
|
169
187
|
return isSupportedCap(name);
|
|
170
188
|
});
|
|
171
189
|
|
|
@@ -141,7 +141,17 @@ function handleChannelSub(
|
|
|
141
141
|
// LATEST / BEFORE / AFTER / AROUND
|
|
142
142
|
const pivot = msg.params[2];
|
|
143
143
|
const limitRaw = msg.params[3];
|
|
144
|
-
|
|
144
|
+
|
|
145
|
+
// IRCv3 draft/read-marker integration: BEFORE without an explicit pivot
|
|
146
|
+
// falls back to the connection's per-channel last-read marker so a client
|
|
147
|
+
// can resume "everything before my last read position" without re-supplying
|
|
148
|
+
// the msgid. AFTER/AROUND still require an explicit pivot.
|
|
149
|
+
const markerPivot =
|
|
150
|
+
sub === 'BEFORE' && pivot === undefined
|
|
151
|
+
? ctx.connection.lastReadMarkers?.get(chanLower)
|
|
152
|
+
: undefined;
|
|
153
|
+
|
|
154
|
+
if (sub !== 'LATEST' && pivot === undefined && markerPivot === undefined) {
|
|
145
155
|
return { effects: [Effect.send(ctx.connId, [needMoreParamsLine(ctx)])] };
|
|
146
156
|
}
|
|
147
157
|
const limit = parseLimit(limitRaw);
|
|
@@ -163,12 +173,13 @@ function handleChannelSub(
|
|
|
163
173
|
return { effects: buildChathistoryBatch(ctx, channel.name, result) };
|
|
164
174
|
}
|
|
165
175
|
|
|
166
|
-
// BEFORE / AFTER / AROUND — pivot is required.
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
176
|
+
// BEFORE / AFTER / AROUND — pivot is required. Resolve from the explicit
|
|
177
|
+
// msgid when supplied, otherwise from the marker fallback computed above.
|
|
178
|
+
// The guard near the top already guaranteed at least one is defined.
|
|
179
|
+
const pivotMsgid = (pivot ?? markerPivot) as string;
|
|
180
|
+
// An explicit pivot must reference a retained message; a marker fallback
|
|
181
|
+
// may point at an evicted message (→ empty result, not INVALID_PARAMS).
|
|
182
|
+
if (pivot !== undefined && !storeHas(ctx, chanLower, pivotMsgid)) {
|
|
172
183
|
return { effects: [Effect.send(ctx.connId, [invalidParamsLine(ctx)])] };
|
|
173
184
|
}
|
|
174
185
|
const result = storeQuery(ctx, {
|
|
@@ -228,16 +239,34 @@ export function buildChathistoryBatch(
|
|
|
228
239
|
/**
|
|
229
240
|
* Reconstructs the wire line for one stored message, decorated with the
|
|
230
241
|
* original server-time and msgid tags. PRIVMSG/NOTICE carry the trailing
|
|
231
|
-
* text; TAGMSG has no trailing parameter.
|
|
242
|
+
* text; TAGMSG has no trailing parameter. Optional `clientTags` recorded
|
|
243
|
+
* with the message (e.g. `+draft/multiline`) are appended to the tag
|
|
244
|
+
* section so chathistory clients can recognise the message kind.
|
|
232
245
|
*/
|
|
233
246
|
export function formatReplayLine(m: StoredMessage, displayChan: string): RawLine {
|
|
234
247
|
const stamp = formatServerTime(m.time);
|
|
235
248
|
const prefix = storedHostmask(m);
|
|
236
|
-
const
|
|
249
|
+
const tags = buildReplayTags(stamp, m.msgid, m.clientTags);
|
|
237
250
|
if (m.command === 'TAGMSG') {
|
|
238
|
-
return { text: `${
|
|
251
|
+
return { text: `${tags} :${prefix} TAGMSG ${displayChan}` };
|
|
252
|
+
}
|
|
253
|
+
return { text: `${tags} :${prefix} ${m.command} ${displayChan} :${m.text}` };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Builds the `@time=…;msgid=…[;+client-tag…]` tag section for a replay line.
|
|
258
|
+
* Client tags (when present) are appended after the server tags, matching the
|
|
259
|
+
* IRCv3 message-tags wire order.
|
|
260
|
+
*/
|
|
261
|
+
function buildReplayTags(
|
|
262
|
+
stamp: string,
|
|
263
|
+
msgid: string,
|
|
264
|
+
clientTags: readonly string[] | undefined,
|
|
265
|
+
): string {
|
|
266
|
+
if (clientTags === undefined || clientTags.length === 0) {
|
|
267
|
+
return `@time=${stamp};msgid=${msgid}`;
|
|
239
268
|
}
|
|
240
|
-
return {
|
|
269
|
+
return `@time=${stamp};msgid=${msgid};${clientTags.join(';')}`;
|
|
241
270
|
}
|
|
242
271
|
|
|
243
272
|
/** Builds the `nick!user@host` source for a stored message. */
|
|
@@ -46,6 +46,8 @@ export { generateIsupport } from './isupport.js';
|
|
|
46
46
|
export { capReducer } from './cap.js';
|
|
47
47
|
export { emitChghost } from './chghost.js';
|
|
48
48
|
export type { ChgHostInput } from './chghost.js';
|
|
49
|
+
export { emitAccountNotify } from './account-notify.js';
|
|
50
|
+
export type { AccountNotifyInput } from './account-notify.js';
|
|
49
51
|
export { awayReducer } from './away.js';
|
|
50
52
|
export { setnameReducer } from './setname.js';
|
|
51
53
|
export { authenticateReducer } from './sasl.js';
|
|
@@ -56,6 +58,18 @@ export {
|
|
|
56
58
|
CHATHISTORY_CAP,
|
|
57
59
|
} from './chathistory.js';
|
|
58
60
|
export { tagmsgChannelReducer, tagmsgUserReducer } from './tagmsg.js';
|
|
61
|
+
export {
|
|
62
|
+
DEFAULT_MULTILINE_MAX_BYTES,
|
|
63
|
+
MULTILINE_CAP,
|
|
64
|
+
type FinalizeMultilineResult,
|
|
65
|
+
type MultilineEntry,
|
|
66
|
+
type MultilineFanout,
|
|
67
|
+
buildMultilineFanout,
|
|
68
|
+
finalizeMultilineBatch,
|
|
69
|
+
joinMultiline,
|
|
70
|
+
multilineByteLength,
|
|
71
|
+
recordMultilineMessage,
|
|
72
|
+
} from './multiline.js';
|
|
59
73
|
export {
|
|
60
74
|
versionReducer,
|
|
61
75
|
timeReducer,
|
|
@@ -64,3 +78,23 @@ export {
|
|
|
64
78
|
} from './server-info.js';
|
|
65
79
|
export { userhostReducer, formatUserhostReply } from './userhost.js';
|
|
66
80
|
export { isonReducer } from './ison.js';
|
|
81
|
+
export {
|
|
82
|
+
monitorReducer,
|
|
83
|
+
MONITOR_CAP,
|
|
84
|
+
DEFAULT_MONITOR_LIMIT,
|
|
85
|
+
} from './monitor.js';
|
|
86
|
+
export {
|
|
87
|
+
READ_MARKER_CAP,
|
|
88
|
+
READ_MARKER_TAG,
|
|
89
|
+
isReadMarkerTagmsg,
|
|
90
|
+
persistReadMarker,
|
|
91
|
+
readMarkerMsgid,
|
|
92
|
+
seedReadMarkers,
|
|
93
|
+
} from './read-marker.js';
|
|
94
|
+
export {
|
|
95
|
+
PRE_AWAY_CAP,
|
|
96
|
+
clearPersistedAway,
|
|
97
|
+
nowAwayLine,
|
|
98
|
+
persistAway,
|
|
99
|
+
replayPersistedAway,
|
|
100
|
+
} from './pre-away.js';
|
|
@@ -15,8 +15,10 @@
|
|
|
15
15
|
* per-deployment (PLAN §10 capacity planning).
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
import { DEFAULT_MULTILINE_MAX_BYTES } from '../caps/capabilities.js';
|
|
18
19
|
import type { RawLine } from '../effects.js';
|
|
19
20
|
import type { ServerConfig } from '../types.js';
|
|
21
|
+
import { DEFAULT_MONITOR_LIMIT } from './monitor.js';
|
|
20
22
|
|
|
21
23
|
/** Max bytes per IRC wire line, including trailing `\r\n`. */
|
|
22
24
|
const LINE_MAX_BYTES = 510;
|
|
@@ -36,6 +38,32 @@ const CHANMODES = {
|
|
|
36
38
|
/** Channel-user prefix sigils, in PREFIX order (highest rank first). */
|
|
37
39
|
const PREFIX = '(ov)@+';
|
|
38
40
|
|
|
41
|
+
/**
|
|
42
|
+
* STATUSMSG delivery prefixes — the channel-user sigils a sender may prefix
|
|
43
|
+
* a target with (`@#channel` / `+#channel`) so only that rank and above
|
|
44
|
+
* receive the message. Mirrors the PREFIX sigil tail.
|
|
45
|
+
*/
|
|
46
|
+
const STATUSMSG_PREFIXES = '@+';
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Default extended-ban prefix when {@link ServerConfig.extbanPrefix} is
|
|
50
|
+
* unset. `~` is the de-facto IRC standard (matches nearly every daemon).
|
|
51
|
+
*/
|
|
52
|
+
const DEFAULT_EXTBAN_PREFIX = '~';
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Extended-ban types this server understands independent of services.
|
|
56
|
+
* `q` = quiet ban (prevents matching targets from speaking).
|
|
57
|
+
*/
|
|
58
|
+
const EXTBAN_TYPES = 'q';
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Account-name extended-ban type, advertised as a dedicated `ACCOUNTEXTBAN=a`
|
|
62
|
+
* token only when services are enabled, since account bans require registered
|
|
63
|
+
* nicks to be meaningful.
|
|
64
|
+
*/
|
|
65
|
+
const ACCOUNTEXTBAN_TYPE = 'a';
|
|
66
|
+
|
|
39
67
|
/** Channel name sigils this server recognises. */
|
|
40
68
|
const CHANTYPES = '#';
|
|
41
69
|
|
|
@@ -56,6 +84,9 @@ const TRAILER = 'are supported by this server';
|
|
|
56
84
|
* can diff between servers. Tokens are space-separated, never empty.
|
|
57
85
|
*/
|
|
58
86
|
function buildTokens(cfg: ServerConfig): string[] {
|
|
87
|
+
const monitorLimit = cfg.monitorLimit ?? DEFAULT_MONITOR_LIMIT;
|
|
88
|
+
const extbanPrefix = cfg.extbanPrefix ?? DEFAULT_EXTBAN_PREFIX;
|
|
89
|
+
const multilineMax = cfg.multilineMaxBytes ?? DEFAULT_MULTILINE_MAX_BYTES;
|
|
59
90
|
return [
|
|
60
91
|
`CHANTYPES=${CHANTYPES}`,
|
|
61
92
|
`CHANMODES=${CHANMODES.list},${CHANMODES.paramSet},${CHANMODES.paramAlways},${CHANMODES.boolean}`,
|
|
@@ -73,6 +104,28 @@ function buildTokens(cfg: ServerConfig): string[] {
|
|
|
73
104
|
// per-connection opt-in). LIST is always non-destructive here so the
|
|
74
105
|
// token is unconditional.
|
|
75
106
|
'SAFELIST',
|
|
107
|
+
// IRCv3 MONITOR cap ceiling. The value matches the per-connection
|
|
108
|
+
// watchlist size enforced by the monitor reducer; deployments lower it
|
|
109
|
+
// via `serverConfig.monitorLimit`.
|
|
110
|
+
`MONITOR=${monitorLimit}`,
|
|
111
|
+
// STATUSMSG — prefixes a sender may target (`@#chan`/`+#chan`) for
|
|
112
|
+
// rank-scoped delivery. The sigil set mirrors PREFIX.
|
|
113
|
+
`STATUSMSG=${STATUSMSG_PREFIXES}`,
|
|
114
|
+
// EXTBAN — extended-ban prefix and the base type list (`q` = quiet).
|
|
115
|
+
// The prefix is config-derived. The account-name extban is advertised
|
|
116
|
+
// as a separate `ACCOUNTEXTBAN=a` token (below) when services ship.
|
|
117
|
+
`EXTBAN=${extbanPrefix},${EXTBAN_TYPES}`,
|
|
118
|
+
// draft/typing advertisement — the cap is always supported, so the
|
|
119
|
+
// token is unconditional. (Draft caps have no RFC ISUPPORT token; this
|
|
120
|
+
// is the local marker clients can key on.)
|
|
121
|
+
'TYPING',
|
|
122
|
+
// draft/multiline byte budget — mirrors the `draft/multiline=<n>` cap
|
|
123
|
+
// value so the ceiling is visible pre-cap-negotiation.
|
|
124
|
+
`MULTILINE=${multilineMax}`,
|
|
125
|
+
// ACCOUNTEXTBAN — account-name extended ban. Meaningful only once
|
|
126
|
+
// services register nicks, so suppressed until a deployment wires
|
|
127
|
+
// services up via `serverConfig.servicesEnabled`.
|
|
128
|
+
...(cfg.servicesEnabled ? [`ACCOUNTEXTBAN=${ACCOUNTEXTBAN_TYPE}`] : []),
|
|
76
129
|
];
|
|
77
130
|
}
|
|
78
131
|
|
|
@@ -528,7 +528,7 @@ const USER_MODE_LETTERS: Readonly<Record<string, keyof ConnectionState['userMode
|
|
|
528
528
|
};
|
|
529
529
|
|
|
530
530
|
/** User-mode letters in canonical 221 output order. */
|
|
531
|
-
const USER_MODE_LETTER_ORDER = ['i', 'w', 's', 'o'] as const;
|
|
531
|
+
const USER_MODE_LETTER_ORDER = ['i', 'w', 's', 'o', 'S'] as const;
|
|
532
532
|
|
|
533
533
|
/**
|
|
534
534
|
* Handles `MODE <nick> [<modestring>]`.
|
|
@@ -640,6 +640,13 @@ export const userModeReducer: Reducer<ConnectionState> = (state, msg, ctx) => {
|
|
|
640
640
|
function buildUserModeString(state: ConnectionState): string {
|
|
641
641
|
let modeStr = '+';
|
|
642
642
|
for (const letter of USER_MODE_LETTER_ORDER) {
|
|
643
|
+
// `S` (TLS connected) is read-only: it is not in USER_MODE_LETTERS
|
|
644
|
+
// (the write-side map the parser consults) so `MODE nick +S` rejects
|
|
645
|
+
// with 501. Surface it on the read side directly from its field.
|
|
646
|
+
if (letter === 'S') {
|
|
647
|
+
if (state.userModes.tls) modeStr += 'S';
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
643
650
|
const field = USER_MODE_LETTERS[letter];
|
|
644
651
|
if (field !== undefined && state.userModes[field]) modeStr += letter;
|
|
645
652
|
}
|