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
|
@@ -175,18 +175,18 @@ function buildChannelReducer(command: MessageCommand): Reducer<ChannelState> {
|
|
|
175
175
|
|
|
176
176
|
// All checks passed: persist the message for chathistory playback when a
|
|
177
177
|
// MessageStore is bound. The msgid/time are generated once here and travel
|
|
178
|
-
// on the StoredMessage
|
|
179
|
-
//
|
|
180
|
-
//
|
|
181
|
-
// change whatsoever.
|
|
178
|
+
// on the StoredMessage AND on the live fanout line (IRCv3 msgid). A single
|
|
179
|
+
// nonce is shared between the live tag and the record so the value a cap
|
|
180
|
+
// enabled peer sees live matches the value replayed by chathistory.
|
|
182
181
|
//
|
|
183
182
|
// By this point the sender's hostmask has already been computed and used
|
|
184
183
|
// in the `+b` ban check above, so the connection's nick/user/host are all
|
|
185
184
|
// defined for any registered PRIVMSG (registration sets all three); the
|
|
186
185
|
// fallbacks mirror `senderHostmask` purely for the type-checker.
|
|
186
|
+
const msgid = ctx.ids.nonce();
|
|
187
187
|
if (ctx.messages !== undefined) {
|
|
188
188
|
ctx.messages.record({
|
|
189
|
-
msgid
|
|
189
|
+
msgid,
|
|
190
190
|
time: ctx.clock.now(),
|
|
191
191
|
chan: state.nameLower,
|
|
192
192
|
command,
|
|
@@ -197,14 +197,21 @@ function buildChannelReducer(command: MessageCommand): Reducer<ChannelState> {
|
|
|
197
197
|
});
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
// All checks passed: broadcast to the channel, excluding the sender.
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
200
|
+
// All checks passed: broadcast to the channel, excluding the sender. The
|
|
201
|
+
// bare line goes to every member; the `@msgid=<id>` decorated line is sent
|
|
202
|
+
// only to members who negotiated the `msgid` cap (IRCv3 msgid). The
|
|
203
|
+
// dispatch layer's cap-split resolves per-recipient.
|
|
204
|
+
const body = `${command} ${state.name} :${text}`;
|
|
205
|
+
const line: RawLine = { text: `:${hostmask} ${body}` };
|
|
206
|
+
const msgidLine: RawLine = { text: `@msgid=${msgid} :${hostmask} ${body}` };
|
|
207
|
+
effects.push(Effect.broadcast(state.name, [line], ctx.connId, 'msgid', [msgidLine]));
|
|
208
|
+
|
|
209
|
+
// IRCv3 echo-message: echo the line back to the sender, after the
|
|
210
|
+
// broadcast effect so clients observe their own message last. A sender
|
|
211
|
+
// that also negotiated `msgid` sees the decorated echo.
|
|
206
212
|
if (ctx.connection.caps.has('echo-message')) {
|
|
207
|
-
|
|
213
|
+
const echoLine = ctx.connection.caps.has('msgid') ? msgidLine : line;
|
|
214
|
+
effects.push(Effect.send(ctx.connId, [echoLine]));
|
|
208
215
|
}
|
|
209
216
|
|
|
210
217
|
return { state, effects };
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers for IRCv3 `draft/read-marker` (persistent last-read marker).
|
|
3
|
+
*
|
|
4
|
+
* Spec (work-in-progress): https://ircv3.net/specs/extensions/read-marker
|
|
5
|
+
*
|
|
6
|
+
* The serverless-ircd deployment diverges from the work-in-progress draft:
|
|
7
|
+
* instead of the draft's `MARKREAD` command + server-time timestamp, this
|
|
8
|
+
* implementation is **msgid-based** so it composes with the
|
|
9
|
+
* per-`(connection, channel)` last-read marker the chathistory command
|
|
10
|
+
* already maintains in {@link ConnectionState.lastReadMarkers}.
|
|
11
|
+
*
|
|
12
|
+
* Flow:
|
|
13
|
+
* - A cap-enabled client marks a message read by sending a
|
|
14
|
+
* `TAGMSG <target>` carrying `+draft/read-marker=<msgid>`. The
|
|
15
|
+
* {@link tagmsgChannelReducer} detects the tag, calls
|
|
16
|
+
* {@link persistReadMarker} to advance the per-`(account, channel)`
|
|
17
|
+
* record in the bound {@link ReadMarkerStore}, and fans the TAGMSG out
|
|
18
|
+
* (carrying the tag) so the user's other connections learn the new
|
|
19
|
+
* read position.
|
|
20
|
+
* - On SASL identify, the actor calls {@link seedReadMarkers} so a fresh
|
|
21
|
+
* connection's `lastReadMarkers` is restored from the stored values and
|
|
22
|
+
* a reconnect resumes at the user's last read position.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { READ_MARKER_CAP_NAME } from '../caps/capabilities.js';
|
|
26
|
+
import type { ReadMarkerStore } from '../ports.js';
|
|
27
|
+
import type { ConnectionState } from '../state/connection.js';
|
|
28
|
+
import type { Ctx } from '../types.js';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Wire name of the IRCv3 `draft/read-marker` capability. Aliased from
|
|
32
|
+
* {@link READ_MARKER_CAP_NAME} (the canonical home in the cap data table)
|
|
33
|
+
* for ergonomic use inside the reducer.
|
|
34
|
+
*/
|
|
35
|
+
export const READ_MARKER_CAP = READ_MARKER_CAP_NAME;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The client-tag key that carries a read-marker update. Presence of this key
|
|
39
|
+
* (with a non-empty value) on a `TAGMSG` identifies a read-marker update the
|
|
40
|
+
* server persists and propagates.
|
|
41
|
+
*/
|
|
42
|
+
export const READ_MARKER_TAG = '+draft/read-marker';
|
|
43
|
+
|
|
44
|
+
/** Returns true iff `tags` carries the `+draft/read-marker` client tag. */
|
|
45
|
+
export function isReadMarkerTagmsg(tags: Readonly<Record<string, string>>): boolean {
|
|
46
|
+
return READ_MARKER_TAG in tags;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Returns the msgid carried by the `+draft/read-marker` tag, or `undefined`
|
|
51
|
+
* when the tag is absent or valueless. A valueless tag carries no msgid and
|
|
52
|
+
* therefore no marker to persist.
|
|
53
|
+
*/
|
|
54
|
+
export function readMarkerMsgid(tags: Readonly<Record<string, string>>): string | undefined {
|
|
55
|
+
const value = tags[READ_MARKER_TAG];
|
|
56
|
+
if (value === undefined || value === '') return undefined;
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Advances the per-`(account, channel)` last-read marker.
|
|
62
|
+
*
|
|
63
|
+
* Always advances the connection's in-memory {@link ConnectionState.lastReadMarkers}
|
|
64
|
+
* so the current session tracks the read position. When a {@link ReadMarkerStore}
|
|
65
|
+
* is bound AND the connection is identified (has an `account`), the marker is
|
|
66
|
+
* also persisted so it survives reconnects and is shared across the account's
|
|
67
|
+
* connections.
|
|
68
|
+
*
|
|
69
|
+
* `chanLower` is the lowercased channel name (the storage key); callers pass
|
|
70
|
+
* the authoritative `ChannelState.nameLower`.
|
|
71
|
+
*/
|
|
72
|
+
export function persistReadMarker(ctx: Ctx, chanLower: string, msgid: string): void {
|
|
73
|
+
if (ctx.connection.lastReadMarkers === undefined) {
|
|
74
|
+
ctx.connection.lastReadMarkers = new Map<string, string>();
|
|
75
|
+
}
|
|
76
|
+
ctx.connection.lastReadMarkers.set(chanLower, msgid);
|
|
77
|
+
|
|
78
|
+
const store = ctx.readMarkers;
|
|
79
|
+
const account = ctx.connection.account;
|
|
80
|
+
if (store !== undefined && account !== undefined) {
|
|
81
|
+
store.set(account, chanLower, msgid);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Seeds a fresh connection's `lastReadMarkers` from the persisted values for
|
|
87
|
+
* `account`. Called at SASL identify so a reconnect restores the user's last
|
|
88
|
+
* read position across every channel they have a marker for.
|
|
89
|
+
*
|
|
90
|
+
* Existing in-memory markers are preserved: a session may have already
|
|
91
|
+
* advanced past the stored value, and the in-memory (newer) marker wins.
|
|
92
|
+
*/
|
|
93
|
+
export function seedReadMarkers(
|
|
94
|
+
conn: ConnectionState,
|
|
95
|
+
store: ReadMarkerStore,
|
|
96
|
+
account: string,
|
|
97
|
+
): void {
|
|
98
|
+
const entries = store.forAccount(account);
|
|
99
|
+
if (entries.length === 0) return;
|
|
100
|
+
if (conn.lastReadMarkers === undefined) {
|
|
101
|
+
conn.lastReadMarkers = new Map<string, string>();
|
|
102
|
+
}
|
|
103
|
+
for (const entry of entries) {
|
|
104
|
+
if (!conn.lastReadMarkers.has(entry.channel)) {
|
|
105
|
+
conn.lastReadMarkers.set(entry.channel, entry.msgid);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -117,6 +117,14 @@ export function emitWelcomeIfReady(state: ConnectionState, ctx: Ctx): EffectType
|
|
|
117
117
|
// so the `001` hostmask and every downstream message uses the cloak.
|
|
118
118
|
applyCloakIfEnabled(state, ctx);
|
|
119
119
|
|
|
120
|
+
// Surface the transport's TLS fact as read-only user mode `S` so clients
|
|
121
|
+
// learn the connection is secure via `221` / `WHOIS`. The adapter sets
|
|
122
|
+
// `state.secure` from the platform's transport metadata at construction;
|
|
123
|
+
// the mode is not settable via `MODE` (the parser rejects `+S`/`-S`).
|
|
124
|
+
if (state.secure) {
|
|
125
|
+
state.userModes.tls = true;
|
|
126
|
+
}
|
|
127
|
+
|
|
120
128
|
state.registration = 'registered';
|
|
121
129
|
return [Effect.send(ctx.connId, buildWelcomeLines(state, ctx))];
|
|
122
130
|
}
|
|
@@ -35,6 +35,9 @@ import { Numerics } from '../protocol/numerics.js';
|
|
|
35
35
|
import { hostmaskOf } from '../state/connection.js';
|
|
36
36
|
import type { ConnectionState } from '../state/connection.js';
|
|
37
37
|
import type { Ctx, Reducer } from '../types.js';
|
|
38
|
+
import { emitAccountNotify } from './account-notify.js';
|
|
39
|
+
import { nowAwayLine, replayPersistedAway } from './pre-away.js';
|
|
40
|
+
import { seedReadMarkers } from './read-marker.js';
|
|
38
41
|
|
|
39
42
|
/** Per-ircdocs.horse: a single `AUTHENTICATE` payload chunk is at most 400 bytes. */
|
|
40
43
|
const SASL_CHUNK_BYTES = 400;
|
|
@@ -218,9 +221,29 @@ function applyResult(
|
|
|
218
221
|
|
|
219
222
|
if (result.ok) {
|
|
220
223
|
state.account = result.account;
|
|
224
|
+
// IRCv3 draft/read-marker: restore the user's per-channel last-read
|
|
225
|
+
// markers from the persisted store so a reconnect resumes at their
|
|
226
|
+
// last read position. No-op when no ReadMarkerStore is bound.
|
|
227
|
+
if (ctx.readMarkers !== undefined) {
|
|
228
|
+
seedReadMarkers(state, ctx.readMarkers, result.account);
|
|
229
|
+
}
|
|
230
|
+
// IRCv3 draft/pre-away: replay the user's persisted away reason onto
|
|
231
|
+
// the fresh connection so a reconnect restores the away state, and
|
|
232
|
+
// emit the 306 numeric so the user sees they are still away. No-op
|
|
233
|
+
// when no AwayStore is bound or no reason is stored.
|
|
234
|
+
const replayedAway =
|
|
235
|
+
ctx.away !== undefined ? replayPersistedAway(state, ctx.away, result.account) : undefined;
|
|
236
|
+
const awayEffects: EffectType[] =
|
|
237
|
+
replayedAway !== undefined
|
|
238
|
+
? [Effect.send(ctx.connId, [nowAwayLine(state, ctx.serverName)])]
|
|
239
|
+
: [];
|
|
221
240
|
return {
|
|
222
241
|
state,
|
|
223
|
-
effects: [
|
|
242
|
+
effects: [
|
|
243
|
+
Effect.send(ctx.connId, [loggedIn(ctx, result.account), saslSuccess(ctx)]),
|
|
244
|
+
...awayEffects,
|
|
245
|
+
...emitAccountNotify({ conn: state, account: result.account }),
|
|
246
|
+
],
|
|
224
247
|
};
|
|
225
248
|
}
|
|
226
249
|
return { state, effects: [Effect.send(ctx.connId, [saslFail(ctx)])] };
|
|
@@ -27,10 +27,58 @@ import type { ChannelState } 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 { Ctx, Reducer } from '../types.js';
|
|
30
|
+
import {
|
|
31
|
+
READ_MARKER_CAP,
|
|
32
|
+
isReadMarkerTagmsg,
|
|
33
|
+
persistReadMarker,
|
|
34
|
+
readMarkerMsgid,
|
|
35
|
+
} from './read-marker.js';
|
|
30
36
|
|
|
31
37
|
/** The cap name advertising message-tags support. */
|
|
32
38
|
const MESSAGE_TAGS_CAP = 'message-tags';
|
|
33
39
|
|
|
40
|
+
/**
|
|
41
|
+
* The cap name advertising IRCv3 `draft/typing` support. Typing TAGMSGs
|
|
42
|
+
* (those carrying the `+draft/typing` client tag) fan out to members that
|
|
43
|
+
* announced EITHER `message-tags` (the baseline for every TAGMSG) OR
|
|
44
|
+
* `draft/typing` (the spec's client-tag exception for peers that did not
|
|
45
|
+
* negotiate `message-tags`).
|
|
46
|
+
*/
|
|
47
|
+
const TYPING_CAP = 'draft/typing';
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The client-tag key that identifies a typing TAGMSG. Presence of this key
|
|
51
|
+
* (regardless of value, including the valueless form) triggers the OR-caps
|
|
52
|
+
* broadcast above.
|
|
53
|
+
*/
|
|
54
|
+
const TYPING_TAG = '+draft/typing';
|
|
55
|
+
|
|
56
|
+
/** Returns true iff `tags` carries the `+draft/typing` client tag. */
|
|
57
|
+
function isTypingTagmsg(tags: Readonly<Record<string, string>>): boolean {
|
|
58
|
+
return TYPING_TAG in tags;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Returns the OR-semantics cap list a TAGMSG fanout should use, or `undefined`
|
|
63
|
+
* when the plain single-cap (`message-tags`) broadcast path applies.
|
|
64
|
+
*
|
|
65
|
+
* A TAGMSG carrying `+draft/typing` widens to `message-tags` OR `draft/typing`;
|
|
66
|
+
* one carrying `+draft/read-marker` widens to `message-tags` OR
|
|
67
|
+
* `draft/read-marker`. Both spec exceptions let a peer that announced only the
|
|
68
|
+
* draft cap (not `message-tags`) still receive the notification. The baseline
|
|
69
|
+
* `message-tags` cap always leads the list so message-tags peers keep receiving
|
|
70
|
+
* every TAGMSG.
|
|
71
|
+
*/
|
|
72
|
+
function widenAudienceCaps(tags: Readonly<Record<string, string>>): readonly string[] | undefined {
|
|
73
|
+
const typing = isTypingTagmsg(tags);
|
|
74
|
+
const readMarker = isReadMarkerTagmsg(tags);
|
|
75
|
+
if (!typing && !readMarker) return undefined;
|
|
76
|
+
const caps = [MESSAGE_TAGS_CAP];
|
|
77
|
+
if (typing) caps.push(TYPING_CAP);
|
|
78
|
+
if (readMarker) caps.push(READ_MARKER_CAP);
|
|
79
|
+
return caps;
|
|
80
|
+
}
|
|
81
|
+
|
|
34
82
|
/**
|
|
35
83
|
* Returns true iff `hostmask` matches the IRC ban mask `mask`. Identical to
|
|
36
84
|
* the implementation in `privmsg.ts`; duplicated to keep the message path
|
|
@@ -149,10 +197,32 @@ export const tagmsgChannelReducer: Reducer<ChannelState> = (state, msg, ctx) =>
|
|
|
149
197
|
});
|
|
150
198
|
}
|
|
151
199
|
|
|
200
|
+
// IRCv3 draft/read-marker: a TAGMSG carrying `+draft/read-marker=<msgid>`
|
|
201
|
+
// marks `<msgid>` as the last read message for the sender's account in
|
|
202
|
+
// this channel. Persist the marker (advances the connection's in-memory
|
|
203
|
+
// marker too); a valueless tag carries no msgid and is dropped here. The
|
|
204
|
+
// TAGMSG still fans out below so the user's other connections learn it.
|
|
205
|
+
if (isReadMarkerTagmsg(msg.tags)) {
|
|
206
|
+
const msgid = readMarkerMsgid(msg.tags);
|
|
207
|
+
if (msgid !== undefined) {
|
|
208
|
+
persistReadMarker(ctx, state.nameLower, msgid);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
152
212
|
// Broadcast the tag-bearing line to message-tags-enabled members only.
|
|
213
|
+
// When the TAGMSG carries `+draft/typing` or `+draft/read-marker`, the
|
|
214
|
+
// audience widens to also include members that announced the matching
|
|
215
|
+
// draft cap (the spec's client-tag exception): such peers receive the
|
|
216
|
+
// notification even without `message-tags`. The caps are passed via
|
|
217
|
+
// `caps` so dispatch applies OR semantics per recipient.
|
|
153
218
|
const tagPrefix = serializeTags(msg.tags);
|
|
154
219
|
const line: RawLine = { text: `${tagPrefix}:${hostmask} TAGMSG ${state.name}` };
|
|
155
|
-
|
|
220
|
+
const widenCaps = widenAudienceCaps(msg.tags);
|
|
221
|
+
if (widenCaps !== undefined) {
|
|
222
|
+
effects.push(Effect.broadcast(state.name, [line], ctx.connId, undefined, undefined, widenCaps));
|
|
223
|
+
} else {
|
|
224
|
+
effects.push(Effect.broadcast(state.name, [line], ctx.connId, MESSAGE_TAGS_CAP));
|
|
225
|
+
}
|
|
156
226
|
|
|
157
227
|
// IRCv3 echo-message: echo the same line back to the sender, after
|
|
158
228
|
// the broadcast effect so clients observe their own message last.
|
|
@@ -134,6 +134,11 @@ export function whoisReducer(
|
|
|
134
134
|
text: `:${ctx.serverName} ${Numerics.RPL_WHOISOPERATOR.toString().padStart(3, '0')} ${requesterNick} ${tNick} :is an IRC operator`,
|
|
135
135
|
});
|
|
136
136
|
}
|
|
137
|
+
if (target.userModes.tls) {
|
|
138
|
+
lines.push({
|
|
139
|
+
text: `:${ctx.serverName} ${Numerics.RPL_WHOISSECURE.toString().padStart(3, '0')} ${requesterNick} ${tNick} :is using a secure connection`,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
137
142
|
if (target.account !== undefined) {
|
|
138
143
|
lines.push({
|
|
139
144
|
text: `:${ctx.serverName} ${Numerics.RPL_WHOISACCOUNT.toString().padStart(3, '0')} ${requesterNick} ${tNick} ${target.account} :is logged in as`,
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import { z } from 'zod';
|
|
20
20
|
import { BUILD_DATE } from './build-info.js';
|
|
21
|
+
import { DEFAULT_MULTILINE_MAX_BYTES } from './caps/capabilities.js';
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Schema for a single IRC operator credential.
|
|
@@ -61,6 +62,33 @@ export const CloakingConfigSchema = z.object({
|
|
|
61
62
|
cloakedSuffix: z.string().min(1).optional(),
|
|
62
63
|
});
|
|
63
64
|
|
|
65
|
+
/**
|
|
66
|
+
* IRCv3 `sts` (Strict Transport Security) policy. When supplied, the
|
|
67
|
+
* server advertises an `sts` capability whose value tells compliant
|
|
68
|
+
* clients how long to pin TLS and which port to upgrade to on a
|
|
69
|
+
* plaintext connection. Omit entirely (the default) to disable STS.
|
|
70
|
+
*
|
|
71
|
+
* Spec: https://ircv3.net/specs/extensions/sts
|
|
72
|
+
*/
|
|
73
|
+
export const StsConfigSchema = z.object({
|
|
74
|
+
/**
|
|
75
|
+
* Pin duration in seconds. Defaults to 30 days at the cap-builder layer
|
|
76
|
+
* when omitted; supply an explicit value in production.
|
|
77
|
+
*/
|
|
78
|
+
duration: z.number().int().positive(),
|
|
79
|
+
/**
|
|
80
|
+
* TLS listener port advertised on plaintext connections. The canonical
|
|
81
|
+
* IRC-over-TLS port is 6697 (RFC 7193). Defaults to 6697 at the
|
|
82
|
+
* cap-builder layer when omitted.
|
|
83
|
+
*/
|
|
84
|
+
port: z.number().int().positive(),
|
|
85
|
+
/**
|
|
86
|
+
* Optional preload directive. When true the policy may be hard-coded
|
|
87
|
+
* into a client preload list (only advertised on plaintext connections).
|
|
88
|
+
*/
|
|
89
|
+
preload: z.boolean().optional(),
|
|
90
|
+
});
|
|
91
|
+
|
|
64
92
|
/**
|
|
65
93
|
* Per-IP connection-rate limit. Caps how many new connections a single
|
|
66
94
|
* source IP may open within a sliding `windowMs` window before the
|
|
@@ -272,6 +300,43 @@ export const ServerConfigSchema = z.object({
|
|
|
272
300
|
* lower per deployment. Only consulted when a MessageStore is bound.
|
|
273
301
|
*/
|
|
274
302
|
chatHistoryPlaybackLimit: z.number().int().positive().default(50),
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* IRCv3 `sts` (Strict Transport Security) policy. Omit entirely (the
|
|
306
|
+
* default) to disable STS advertisement; supply `{ duration, port }`
|
|
307
|
+
* (plus optional `preload`) to advertise the cap and let compliant
|
|
308
|
+
* clients upgrade from plaintext to TLS and pin the secure listener.
|
|
309
|
+
*/
|
|
310
|
+
sts: StsConfigSchema.optional(),
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* IRCv3 `draft/multiline` byte budget. Advertised as
|
|
314
|
+
* `draft/multiline=<n>` in `CAP LS` and enforced on every finalized
|
|
315
|
+
* multi-line batch. Defaults to the IRCv3-recommended 4096; raise or
|
|
316
|
+
* lower per deployment.
|
|
317
|
+
*/
|
|
318
|
+
multilineMaxBytes: z.number().int().positive().default(DEFAULT_MULTILINE_MAX_BYTES),
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Per-connection MONITOR watchlist ceiling, advertised as `MONITOR=<n>`
|
|
322
|
+
* in `005 RPL_ISUPPORT`. Omit to inherit the monitor reducer's built-in
|
|
323
|
+
* ceiling (DEFAULT_MONITOR_LIMIT, 30).
|
|
324
|
+
*/
|
|
325
|
+
monitorLimit: z.number().int().positive().optional(),
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Extended-ban prefix sigil for `EXTBAN=<prefix>,…` in
|
|
329
|
+
* `005 RPL_ISUPPORT`. Omit for the IRC-standard `~`.
|
|
330
|
+
*/
|
|
331
|
+
extbanPrefix: z.string().min(1).optional(),
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Enables IRC-services-derived ISUPPORT tokens (currently
|
|
335
|
+
* `ACCOUNTEXTBAN=a`). Set to true once a `ServicesStore` is bound; until
|
|
336
|
+
* then account-name extended bans are suppressed because unregistered
|
|
337
|
+
* nicks make them meaningless.
|
|
338
|
+
*/
|
|
339
|
+
servicesEnabled: z.boolean().optional(),
|
|
275
340
|
});
|
|
276
341
|
|
|
277
342
|
/** Input type: what adapters supply (fields with defaults may be omitted). */
|
|
@@ -303,16 +368,6 @@ export function parseServerConfig(input: unknown): ParsedServerConfig {
|
|
|
303
368
|
return result.data;
|
|
304
369
|
}
|
|
305
370
|
|
|
306
|
-
/**
|
|
307
|
-
* Default server config: the result of parsing an empty input. Useful as a
|
|
308
|
-
* known-good baseline for tests and for adapters that want to overlay
|
|
309
|
-
* per-deployment overrides via object spread.
|
|
310
|
-
*/
|
|
311
|
-
export const DEFAULT_SERVER_CONFIG: ParsedServerConfig = ServerConfigSchema.parse({
|
|
312
|
-
serverName: 'irc.example.com',
|
|
313
|
-
networkName: 'ServerlessIRCd',
|
|
314
|
-
});
|
|
315
|
-
|
|
316
371
|
/**
|
|
317
372
|
* Resolves the effective server version for wire output: the configured
|
|
318
373
|
* value when present, otherwise {@link DEFAULT_SERVER_VERSION}. Used by the
|
|
@@ -43,6 +43,18 @@ export interface BroadcastEffect {
|
|
|
43
43
|
except?: ConnId;
|
|
44
44
|
cap?: string;
|
|
45
45
|
capLines?: RawLine[];
|
|
46
|
+
/**
|
|
47
|
+
* OR-semantics cap list: the recipient receives `lines` iff they
|
|
48
|
+
* negotiated ANY of the listed caps. Used by capabilities whose delivery
|
|
49
|
+
* rule is the union of two caps — e.g. `draft/typing` TAGMSGs reach
|
|
50
|
+
* peers that announced `draft/typing` OR `message-tags` (the latter
|
|
51
|
+
* because every TAGMSG goes to message-tags peers regardless of tag).
|
|
52
|
+
*
|
|
53
|
+
* Mutually exclusive with {@link cap}: set one or the other, not both.
|
|
54
|
+
* When neither is set, dispatch takes the runtime's bulk broadcast fast
|
|
55
|
+
* path.
|
|
56
|
+
*/
|
|
57
|
+
caps?: readonly string[];
|
|
46
58
|
}
|
|
47
59
|
|
|
48
60
|
export interface DisconnectEffect {
|
|
@@ -150,11 +162,13 @@ export const Effect = {
|
|
|
150
162
|
except?: ConnId,
|
|
151
163
|
cap?: string,
|
|
152
164
|
capLines?: RawLine[],
|
|
165
|
+
caps?: readonly string[],
|
|
153
166
|
): Effect {
|
|
154
167
|
const e: BroadcastEffect = { tag: 'Broadcast', chan, lines };
|
|
155
168
|
if (except !== undefined) e.except = except;
|
|
156
169
|
if (cap !== undefined) e.cap = cap;
|
|
157
170
|
if (capLines !== undefined) e.capLines = capLines;
|
|
171
|
+
if (caps !== undefined) e.caps = caps;
|
|
158
172
|
return e;
|
|
159
173
|
},
|
|
160
174
|
disconnect(conn: ConnId, reason?: string): Effect {
|