serverless-ircd 0.6.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 +6 -1
- package/.gitmodules +3 -0
- package/CHANGELOG.md +214 -0
- package/README.md +110 -23
- package/apps/aws-stack/package.json +1 -1
- package/apps/cf-tcp-container/package.json +1 -1
- package/apps/cf-worker/package.json +1 -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/wrangler.test.toml +23 -0
- package/apps/cf-worker/wrangler.toml +54 -0
- package/apps/local-cli/package.json +1 -1
- 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 +1 -0
- package/docs/Home.md +1 -0
- package/docs/WebClientGuide.md +535 -0
- package/package.json +2 -2
- package/packages/aws-adapter/package.json +1 -1
- package/packages/cf-adapter/package.json +1 -1
- package/packages/cf-adapter/tests/cf-harness.ts +12 -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/tests/batch.test.ts +15 -0
- package/packages/irc-core/tests/numerics.test.ts +13 -0
- package/packages/irc-core/tests/parser.test.ts +109 -0
- package/packages/irc-server/package.json +1 -1
- package/packages/irc-test-support/package.json +1 -1
- package/packages/irc-test-support/src/scenarios.ts +24 -2
- package/tools/ci-hardening/package.json +1 -1
- package/tools/load-test/package.json +1 -1
- package/tools/tcp-ws-forwarder/package.json +1 -1
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'kiwi public';
|
|
2
|
+
|
|
3
|
+
import xhr from 'xhr';
|
|
4
|
+
import _ from 'lodash';
|
|
5
|
+
import JSON5 from 'json5';
|
|
6
|
+
import Logger from './Logger';
|
|
7
|
+
|
|
8
|
+
let log = Logger.namespace('ConfigLoader');
|
|
9
|
+
|
|
10
|
+
export default class ConfigLoader {
|
|
11
|
+
constructor() {
|
|
12
|
+
this.config = Object.create(null);
|
|
13
|
+
this.valReplacements = Object.create(null);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
addValueReplacement(key, value) {
|
|
17
|
+
this.valReplacements[key] = value;
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
loadFromUrl(configUrl) {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
xhr({ url: configUrl }, (err, response) => {
|
|
24
|
+
if (err) {
|
|
25
|
+
reject();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let configObj = null;
|
|
30
|
+
try {
|
|
31
|
+
configObj = JSON5.parse(response.body);
|
|
32
|
+
} catch (parseErr) {
|
|
33
|
+
log.error('Config ' + parseErr.message);
|
|
34
|
+
let errMsg = 'Config file error: ' + parseErr.message.replace('JSON5: ', '');
|
|
35
|
+
// Convert "at 22:16" to "at line 22, position 16"
|
|
36
|
+
/* eslint-disable arrow-body-style */
|
|
37
|
+
errMsg = errMsg.replace(/at (\d+):(\d+)/g, (m, m1, m2) => {
|
|
38
|
+
return `line ${m1}, position ${m2}`;
|
|
39
|
+
});
|
|
40
|
+
reject(errMsg);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
this.setConfig(configObj);
|
|
45
|
+
resolve(this.config);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
loadFromObj(configObj) {
|
|
51
|
+
return new Promise((resolve, reject) => {
|
|
52
|
+
this.setConfig(configObj);
|
|
53
|
+
resolve(this.config);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
setConfig(confObj) {
|
|
58
|
+
let walkObject = (obj, target) => {
|
|
59
|
+
_.each(obj, (_val, key) => {
|
|
60
|
+
let val = _val;
|
|
61
|
+
if (typeof val === 'string') {
|
|
62
|
+
val = this.insertReplacements(val);
|
|
63
|
+
target[key] = val;
|
|
64
|
+
} else if (typeof val === 'object') {
|
|
65
|
+
target[key] = _.isArray(val) ?
|
|
66
|
+
[] :
|
|
67
|
+
{};
|
|
68
|
+
walkObject(val, target[key]);
|
|
69
|
+
} else {
|
|
70
|
+
target[key] = val;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
this.config = Object.create(null);
|
|
76
|
+
walkObject(confObj, this.config);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
insertReplacements(input) {
|
|
80
|
+
let out = input;
|
|
81
|
+
let keys = Object.keys(this.valReplacements);
|
|
82
|
+
for (let i = 0; i < keys.length; i++) {
|
|
83
|
+
let k = keys[i];
|
|
84
|
+
if (input === '{{' + k + '}}') {
|
|
85
|
+
// If we have an exact match, return the exact replacement value we have as
|
|
86
|
+
// it may not be a string
|
|
87
|
+
return this.valReplacements[k];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
out = out.replace('{{' + k + '}}', this.valReplacements[k]);
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'kiwi public';
|
|
2
|
+
|
|
3
|
+
import { escape } from 'lodash';
|
|
4
|
+
import getState from '@/libs/state';
|
|
5
|
+
|
|
6
|
+
export function matchEmoji(word) {
|
|
7
|
+
const emojis = this.getEmojis(word);
|
|
8
|
+
if (!emojis.length) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
return [{
|
|
12
|
+
index: 0,
|
|
13
|
+
match: word,
|
|
14
|
+
type: 'emoji',
|
|
15
|
+
meta: {
|
|
16
|
+
emoji: emojis[0].code,
|
|
17
|
+
},
|
|
18
|
+
}];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function blockToHtml(block, isSingle, showEmoticons) {
|
|
22
|
+
if (!showEmoticons) {
|
|
23
|
+
return block.content;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const emojiLocation = getState().setting('emojiLocation');
|
|
27
|
+
const emoji = block.meta.emoji;
|
|
28
|
+
const classes = 'kiwi-messagelist-emoji' + (isSingle ? ' kiwi-messagelist-emoji--single' : '');
|
|
29
|
+
const src = `${emojiLocation}${emoji}`;
|
|
30
|
+
|
|
31
|
+
return `<img class="${classes}" src="${src}" alt="${escape(block.content)}" title="${escape(block.content)}" />`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getEmojis(word) {
|
|
35
|
+
const emojiList = getState().setting('emojis');
|
|
36
|
+
const emojiLocation = getState().setting('emojiLocation');
|
|
37
|
+
if (!emojiList.hasOwnProperty(word)) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
return [{
|
|
41
|
+
ascii: word,
|
|
42
|
+
code: emojiList[word],
|
|
43
|
+
url: emojiLocation + emojiList[word],
|
|
44
|
+
// imgProps allows setting properties of <img>
|
|
45
|
+
}];
|
|
46
|
+
}
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
'kiwi public';
|
|
2
|
+
|
|
3
|
+
/** @module */
|
|
4
|
+
|
|
5
|
+
import EventEmitter from 'eventemitter3';
|
|
6
|
+
import Vue from 'vue';
|
|
7
|
+
import JSON5 from 'json5';
|
|
8
|
+
import _ from 'lodash';
|
|
9
|
+
import { compareVersions } from 'compare-versions';
|
|
10
|
+
import * as Misc from '@/helpers/Misc';
|
|
11
|
+
import Logger from './Logger';
|
|
12
|
+
|
|
13
|
+
let singletonInstance = null;
|
|
14
|
+
let pluginsToInit = [];
|
|
15
|
+
let nextPluginId = 0;
|
|
16
|
+
|
|
17
|
+
/** The global kiwi API instance */
|
|
18
|
+
export default class GlobalApi extends EventEmitter {
|
|
19
|
+
constructor() {
|
|
20
|
+
super();
|
|
21
|
+
|
|
22
|
+
// Version information from DefinePlugin in vue.config.js
|
|
23
|
+
this.version = __VERSION__; // eslint-disable-line no-undef
|
|
24
|
+
this.commithash = __COMMITHASH__; // eslint-disable-line no-undef
|
|
25
|
+
|
|
26
|
+
/** A reference to the internal Vuejs instance */
|
|
27
|
+
this.Vue = Vue;
|
|
28
|
+
/** Expose JSON5 so that plugins can use the same config format */
|
|
29
|
+
this.JSON5 = JSON5;
|
|
30
|
+
/** The applications internal state */
|
|
31
|
+
this.state = null;
|
|
32
|
+
/** The applications ThemeManager */
|
|
33
|
+
this.themes = null;
|
|
34
|
+
/** Translations library */
|
|
35
|
+
this.i18n = null;
|
|
36
|
+
this.vueI18n = null;
|
|
37
|
+
this.translationUrls = Object.create(null);
|
|
38
|
+
|
|
39
|
+
this.controlInputPlugins = [];
|
|
40
|
+
this.stateBrowserPlugins = [];
|
|
41
|
+
this.channelHeaderPlugins = [];
|
|
42
|
+
this.queryHeaderPlugins = [];
|
|
43
|
+
this.sideBarPlugins = [];
|
|
44
|
+
this.userboxButtonPlugins = [];
|
|
45
|
+
this.userboxInfoPlugins = [];
|
|
46
|
+
this.userboxWhoisPlugins = [];
|
|
47
|
+
this.appSettingsPlugins = [];
|
|
48
|
+
this.serverViewPlugins = [];
|
|
49
|
+
this.aboutBufferPlugins = [];
|
|
50
|
+
this.tabs = Object.create(null);
|
|
51
|
+
this.isReady = false;
|
|
52
|
+
/* eslint-disable no-underscore-dangle */
|
|
53
|
+
this.exports = window._kiwi_exports || {};
|
|
54
|
+
|
|
55
|
+
this.on('init', () => {
|
|
56
|
+
this.isReady = true;
|
|
57
|
+
this.initPlugins();
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static singleton() {
|
|
62
|
+
singletonInstance = singletonInstance || new GlobalApi();
|
|
63
|
+
return singletonInstance;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
versionMatches(v) {
|
|
67
|
+
return compareVersions(this.version, v) >= 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Register a plugin with kiwi
|
|
72
|
+
*
|
|
73
|
+
* Plugins being loaded at startup will be registered once Kiwi is ready. At any
|
|
74
|
+
* other point the plugin will be registered instantly
|
|
75
|
+
* @param {String} pluginName The name of this plugin
|
|
76
|
+
* @param {Function} fn A callback function to start the plugin. function(kiwi, logger)
|
|
77
|
+
*/
|
|
78
|
+
plugin(pluginName, fn) {
|
|
79
|
+
let plugin = { name: pluginName, fn: fn };
|
|
80
|
+
if (this.isReady) {
|
|
81
|
+
this.initPlugin(plugin);
|
|
82
|
+
} else {
|
|
83
|
+
pluginsToInit.push(plugin);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Init any plugins that were added before we were ready
|
|
88
|
+
initPlugins() {
|
|
89
|
+
pluginsToInit.forEach((plugin) => this.initPlugin(plugin));
|
|
90
|
+
pluginsToInit = [];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
initPlugin(plugin) {
|
|
94
|
+
let pluginLogger = Logger.namespace(`Plugin ${plugin.name}`);
|
|
95
|
+
try {
|
|
96
|
+
plugin.fn(this, pluginLogger);
|
|
97
|
+
this.state.$emit('plugin.loaded', { name: plugin.name });
|
|
98
|
+
} catch (err) {
|
|
99
|
+
pluginLogger.error(err.stack);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Get a reference to an internal Kiwi module
|
|
105
|
+
*
|
|
106
|
+
* E.g. require('helpers/TextFormatting');
|
|
107
|
+
* @param {String} mod The module path
|
|
108
|
+
*/
|
|
109
|
+
require(modPath) {
|
|
110
|
+
let path = modPath.replace(/\//g, '.');
|
|
111
|
+
let mod = _.get(this.exports, path);
|
|
112
|
+
if (typeof mod === 'undefined') {
|
|
113
|
+
Logger.error('Module does not exist: ' + modPath);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return mod;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
setState(state) {
|
|
120
|
+
this.state = state;
|
|
121
|
+
|
|
122
|
+
// Hacky, but since Vues emitter doesnt support 'all', hijack its $emit call
|
|
123
|
+
// so that we can forward the event on to plugins
|
|
124
|
+
let stateEmit = this.state.$emit;
|
|
125
|
+
let thisEmit = this.emit;
|
|
126
|
+
|
|
127
|
+
this.state.$emit = (...args) => {
|
|
128
|
+
try {
|
|
129
|
+
thisEmit.call(this, 'all', args[0], ...args.slice(1));
|
|
130
|
+
thisEmit.call(this, ...args);
|
|
131
|
+
} catch (err) {
|
|
132
|
+
Logger.error(err.stack);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return stateEmit.call(this.state, ...args);
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// Let plugins emit events into the internal state
|
|
139
|
+
this.emit = (...args) => {
|
|
140
|
+
stateEmit.call(this.state, ...args);
|
|
141
|
+
thisEmit.call(this, ...args);
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
setThemeManager(themeManager) {
|
|
146
|
+
this.themes = themeManager;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Change the logging level output
|
|
151
|
+
* @param {number} newLevel The new logging level
|
|
152
|
+
*/
|
|
153
|
+
logLevel(newLevel) {
|
|
154
|
+
Logger.setLevel(newLevel);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
get log() {
|
|
158
|
+
return Logger;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Add a DOM element to different parts of the Kiwi UI
|
|
163
|
+
* - addUi('input', componentObject)
|
|
164
|
+
* - addUi('browser', componentObject)
|
|
165
|
+
* - addUi('header_channel', componentObject)
|
|
166
|
+
* - addUi('header_query', componentObject)
|
|
167
|
+
* - addUi('userbox_button', componentObject)
|
|
168
|
+
* - addUi('about_buffer', componentObject)
|
|
169
|
+
* @param {string} type Where this component should be added
|
|
170
|
+
* @param {object} component The vue.js component object
|
|
171
|
+
* @param {object} args Optional arguments for this plugin { title: '', props: {} }
|
|
172
|
+
*/
|
|
173
|
+
addUi(type, component, args = {}) {
|
|
174
|
+
const plugin = Misc.makePluginObject(nextPluginId++, component, args);
|
|
175
|
+
|
|
176
|
+
switch (type) {
|
|
177
|
+
case 'input':
|
|
178
|
+
this.controlInputPlugins.push(plugin);
|
|
179
|
+
break;
|
|
180
|
+
case 'browser':
|
|
181
|
+
this.stateBrowserPlugins.push(plugin);
|
|
182
|
+
break;
|
|
183
|
+
case 'header_channel':
|
|
184
|
+
this.channelHeaderPlugins.push(plugin);
|
|
185
|
+
break;
|
|
186
|
+
case 'header_query':
|
|
187
|
+
this.queryHeaderPlugins.push(plugin);
|
|
188
|
+
break;
|
|
189
|
+
case 'userbox_button':
|
|
190
|
+
this.userboxButtonPlugins.push(plugin);
|
|
191
|
+
break;
|
|
192
|
+
case 'userbox_info':
|
|
193
|
+
this.userboxInfoPlugins.push(plugin);
|
|
194
|
+
break;
|
|
195
|
+
case 'userbox_whois':
|
|
196
|
+
this.userboxWhoisPlugins.push(plugin);
|
|
197
|
+
break;
|
|
198
|
+
case 'about_buffer':
|
|
199
|
+
this.aboutBufferPlugins.push(plugin);
|
|
200
|
+
break;
|
|
201
|
+
default:
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Add a Vue component as a tab to different tabbed views in the Kiwi API
|
|
208
|
+
* - addTab('channel', 'title', component, props)
|
|
209
|
+
* - addTab('settings', 'title', component, props)
|
|
210
|
+
* - addTab('server', 'title', component, props)
|
|
211
|
+
* @param {String} type The type of tab to add. This determines where it will be shown
|
|
212
|
+
* @param {String} title The title shown on the tab
|
|
213
|
+
* @param {Object} component The vue.js component object that is displayed in this tab
|
|
214
|
+
* @param {Object} props Optional properties for the vue.js component
|
|
215
|
+
*/
|
|
216
|
+
addTab(type, title, component, props) {
|
|
217
|
+
const plugin = Misc.makePluginObject(nextPluginId++, component, { props, title });
|
|
218
|
+
|
|
219
|
+
switch (type) {
|
|
220
|
+
case 'channel':
|
|
221
|
+
this.sideBarPlugins.push(plugin);
|
|
222
|
+
break;
|
|
223
|
+
case 'settings':
|
|
224
|
+
this.appSettingsPlugins.push(plugin);
|
|
225
|
+
break;
|
|
226
|
+
case 'server':
|
|
227
|
+
this.serverViewPlugins.push(plugin);
|
|
228
|
+
break;
|
|
229
|
+
default:
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Register a Vue component that may be shown in future. It is shown over the entire
|
|
236
|
+
* client alongside the StateBrowser
|
|
237
|
+
* @param {String} name A name to reference this view in future
|
|
238
|
+
* @param {Object} component The vue.js component object to create the view
|
|
239
|
+
* @param {Object} props Optional properties the the vue.js component
|
|
240
|
+
*/
|
|
241
|
+
addView(name, component, props) {
|
|
242
|
+
const plugin = Misc.makePluginObject(nextPluginId++, component, { props });
|
|
243
|
+
this.tabs[name] = plugin;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Show a previously registered view
|
|
248
|
+
* @param {String} name The name of previously registered view to show
|
|
249
|
+
*/
|
|
250
|
+
showView(name) {
|
|
251
|
+
// null disables any active component and reverts the UI back to the buffers
|
|
252
|
+
let tab = this.tabs[name];
|
|
253
|
+
if (tab) {
|
|
254
|
+
this.state.$emit('active.component', tab.component, tab.props);
|
|
255
|
+
} else {
|
|
256
|
+
this.state.$emit('active.component', null);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Show a Vue.js component in the sidebar
|
|
262
|
+
* @param {Object} component The vue.js component object to render
|
|
263
|
+
* @param {Object} props Optional properties for the vue.js component
|
|
264
|
+
*/
|
|
265
|
+
showInSidebar(component, props) {
|
|
266
|
+
const plugin = Misc.makePluginObject(0, component, { props });
|
|
267
|
+
this.state.$emit('sidebar.component', plugin.component, plugin.props);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Add a custom startup screen that may be loaded by the configuration file
|
|
272
|
+
* @param {String} name The name of this startup screen
|
|
273
|
+
* @param {Object} component The vue.js component object
|
|
274
|
+
*/
|
|
275
|
+
addStartup(name, component) {
|
|
276
|
+
const plugin = Misc.makePluginObject(0, component);
|
|
277
|
+
let startups = this.state.getStartups();
|
|
278
|
+
startups[name] = plugin.component;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Replace an existing kiwiirc component/module with a custom one
|
|
283
|
+
* @param {String} dest The module path to replace
|
|
284
|
+
* @param {Object} source The new module to insert in place
|
|
285
|
+
*/
|
|
286
|
+
replaceModule(dest, source) {
|
|
287
|
+
let mod = this.require(dest);
|
|
288
|
+
if (!mod) {
|
|
289
|
+
throw new Error(`The module ${dest} does not exist`);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
Misc.replaceObjectProps(mod, source);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Add default config params for a plugin
|
|
297
|
+
* @param {String} namespace Base config location eg: 'plugin-conference'
|
|
298
|
+
* @param {Object} defaultConfig Configuration object
|
|
299
|
+
*/
|
|
300
|
+
setConfigDefaults(namespace, defaultConfig) {
|
|
301
|
+
let base = `settings.${namespace}`;
|
|
302
|
+
let getSetting = (key) => this.state.getSetting(`${base}.${key}`);
|
|
303
|
+
let setSetting = (key, value) => this.state.setSetting(`${base}.${key}`, value);
|
|
304
|
+
let walkConfig = (obj, _target) => {
|
|
305
|
+
_.each(obj, (val, key) => {
|
|
306
|
+
let target = [..._target, key];
|
|
307
|
+
let targetName = target.join('.');
|
|
308
|
+
if (typeof val === 'object' && !_.isArray(val)) {
|
|
309
|
+
walkConfig(val, target);
|
|
310
|
+
} else if (typeof getSetting(targetName) === 'undefined') {
|
|
311
|
+
setSetting(targetName, val);
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
};
|
|
315
|
+
walkConfig(defaultConfig, []);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Add translations for use in a plugin
|
|
320
|
+
* @param {String} namespace Translations namespace eg: 'plugin-conference'
|
|
321
|
+
* @param {Object} translations Translations object
|
|
322
|
+
*/
|
|
323
|
+
addTranslations(namespace, translations) {
|
|
324
|
+
Object.entries(translations).forEach(([lang, data]) => {
|
|
325
|
+
this.i18n.addResourceBundle(lang, namespace, data);
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Add url path to translation files
|
|
331
|
+
* @param {String} namespace Translations namespace eg: 'plugin-conference'
|
|
332
|
+
* @param {String} url Translations url path, supported replacements: {{lng}} {{ns}}
|
|
333
|
+
* @param {Object} fallbackLocale Optional fallback 'en-us' locale data from bundle
|
|
334
|
+
*/
|
|
335
|
+
addTranslationFiles(namespace, url, fallbackLocale) {
|
|
336
|
+
if (!url.includes('{{lng}}')) {
|
|
337
|
+
this.log.error('A plugin tried to addTranslationsFiles() without {{lng}} in the url');
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
this.translationUrls[namespace] = url;
|
|
341
|
+
if (fallbackLocale) {
|
|
342
|
+
this.i18n.addResourceBundle('en-us', namespace, fallbackLocale);
|
|
343
|
+
}
|
|
344
|
+
this.i18n.loadNamespaces(namespace);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'kiwi public';
|
|
2
|
+
|
|
3
|
+
import EventEmitter from 'eventemitter3';
|
|
4
|
+
|
|
5
|
+
class IpcBroadcastChannel extends EventEmitter {
|
|
6
|
+
init() {
|
|
7
|
+
try {
|
|
8
|
+
// Privacy settings in firefox 104~ block broadcast channels in iframes
|
|
9
|
+
this.bc = new BroadcastChannel('kiwi-ipc');
|
|
10
|
+
this.bc.onmessage = this.onmessage.bind(this);
|
|
11
|
+
} catch {
|
|
12
|
+
// catch
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
send(msg) {
|
|
17
|
+
if (this.bc) {
|
|
18
|
+
this.bc.postMessage(msg);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
onmessage(msg) {
|
|
23
|
+
this.emit('message', msg);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
class IpcLocalStorage extends EventEmitter {
|
|
28
|
+
init() {
|
|
29
|
+
window.addEventListener('storage', this.onmessage.bind(this));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
send(msg) {
|
|
33
|
+
localStorage.setItem('kiwi-ipc', JSON.stringify(msg));
|
|
34
|
+
localStorage.removeItem('kiwi-ipc');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
onmessage(msg) {
|
|
38
|
+
if (msg.key === 'kiwi-ipc' && msg.newValue) {
|
|
39
|
+
try {
|
|
40
|
+
let parsedMsg = {
|
|
41
|
+
data: JSON.parse(msg.newValue),
|
|
42
|
+
};
|
|
43
|
+
this.emit('message', parsedMsg);
|
|
44
|
+
} catch (e) {
|
|
45
|
+
this.emit('error');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default (window.BroadcastChannel ? new IpcBroadcastChannel() : new IpcLocalStorage());
|