@saykit/carbon 0.0.0 → 0.2.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/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # @saykit/carbon
2
+
3
+ > [Carbon](https://carbon.buape.com) Discord bot integration for [SayKit](https://saykit.js.org).
4
+
5
+ Registers a shared `Say` with your Carbon client, helps command and component classes expose translated metadata, and adds locale-aware `interaction.say` and `guild.say` properties.
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ pnpm add @saykit/carbon saykit @buape/carbon
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```ts
16
+ import { Client, Command, type CommandInteraction } from '@buape/carbon';
17
+ import { SayPlugin, withSay } from '@saykit/carbon';
18
+ import { type Say } from 'saykit';
19
+ import say from './i18n.js';
20
+
21
+ class PingCommand extends withSay(Command) {
22
+ constructor(say: Say) {
23
+ super(say, (say) => ({
24
+ name: say`ping`,
25
+ description: say`Ping the bot!`,
26
+ }));
27
+ }
28
+
29
+ async run(interaction: CommandInteraction) {
30
+ await interaction.reply({ content: interaction.say`Pong!` });
31
+ }
32
+ }
33
+
34
+ const client = new Client(
35
+ {
36
+ /* options */
37
+ },
38
+ { commands: [new PingCommand(say)] },
39
+ [new SayPlugin(say)],
40
+ );
41
+ ```
42
+
43
+ ## Documentation
44
+
45
+ [Carbon integration guide](https://saykit.js.org/integrations/carbon) at [saykit.js.org](https://saykit.js.org).
@@ -0,0 +1,65 @@
1
+ import { APIInteraction, BaseCommand, BaseComponent, Modal, Plugin } from "@buape/carbon";
2
+ import { Say } from "saykit";
3
+
4
+ //#region src/constants.d.ts
5
+ declare const kSay: unique symbol;
6
+ //#endregion
7
+ //#region src/extensions/base-interaction.d.ts
8
+ declare module '@buape/carbon' {
9
+ interface BaseInteraction<T extends APIInteraction> {
10
+ get say(): Say;
11
+ [kSay]: Say;
12
+ }
13
+ }
14
+ declare function applyBaseInteractionExtension(): () => void;
15
+ //#endregion
16
+ //#region src/extensions/guild.d.ts
17
+ declare module '@buape/carbon' {
18
+ interface Guild {
19
+ get say(): Say;
20
+ [kSay]: Say;
21
+ }
22
+ }
23
+ declare function applyGuildExtension(): () => void;
24
+ //#endregion
25
+ //#region src/mixers/with-say.d.ts
26
+ type Keys = 'name' | 'description' | 'label' | 'title' | 'placeholder' | 'content' | 'options' | 'components' | 'subcommands' | 'subcommandGroups';
27
+ type AbstractConstructor<Args extends any[] = any[], Instance extends object = object> = abstract new (...args: Args) => Instance;
28
+ type SayProps<T> = Pick<T, Extract<keyof T, Keys>>;
29
+ /**
30
+ * Enhances a {@link BaseCommand} subclass with support for localisation.
31
+ *
32
+ * @param Base Abstract command constructor to extend.
33
+ * @returns A new constructor that accepts a {@link Say} instance, a
34
+ * properties-mapping function, and the original constructor arguments.
35
+ */
36
+ declare function withSay<Args extends unknown[], Instance extends BaseCommand>(Base: AbstractConstructor<Args, Instance>): AbstractConstructor<[say: Say, properties: (say: Say) => SayProps<Instance>, ...args: Args], Instance & Partial<Record<Keys, unknown>>>;
37
+ /**
38
+ * Enhances a {@link BaseComponent} or {@link Modal} subclass with
39
+ * support for localisation.
40
+ *
41
+ * @param Base Abstract component or modal constructor to extend.
42
+ * @returns A new constructor that accepts a set of properties.
43
+ */
44
+ declare function withSay<Args extends unknown[], Instance extends BaseComponent | Modal>(Base: AbstractConstructor<Args, Instance>): AbstractConstructor<[properties: SayProps<Instance>, ...args: Args], Instance & Partial<Record<Keys, unknown>>>;
45
+ //#endregion
46
+ //#region src/plugin.d.ts
47
+ /**
48
+ * A Carbon plugin that provides a singleton {@link Say} instance.
49
+ *
50
+ * `SayPlugin` registers a {@link Say} instance globally and
51
+ * applies interaction and guild-level extensions so that commands and
52
+ * other handlers can access localisation utilities directly.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * const say = new Say({ ... });
57
+ * const client = new Client({ ... }, { ... }, [new SayPlugin(say)]);
58
+ * ```
59
+ */
60
+ declare class SayPlugin extends Plugin {
61
+ id: string;
62
+ constructor(say: Say);
63
+ }
64
+ //#endregion
65
+ export { SayPlugin, applyBaseInteractionExtension, applyGuildExtension, withSay };
package/dist/index.mjs ADDED
@@ -0,0 +1,127 @@
1
+ import { BaseCommand, BaseComponent, BaseInteraction, Guild, Locale, Modal, Plugin } from "@buape/carbon";
2
+ //#region src/utils/combine-command-options.ts
3
+ const ALLOWED_LOCALES = Object.values(Locale);
4
+ function combineCommandOptions(mappedOptions, baseLocale) {
5
+ const options = mappedOptions[baseLocale];
6
+ const availableLocales = Object.keys(mappedOptions).filter((l) => l !== baseLocale && ALLOWED_LOCALES.includes(l));
7
+ for (const [key, value] of Object.entries(options)) if (key === "name" || key === "description") {
8
+ options[`${key}Localizations`] = {};
9
+ for (const locale of availableLocales) {
10
+ const other = mappedOptions[locale][key];
11
+ options[`${key}Localizations`][locale] = other;
12
+ }
13
+ options[`${key}_localizations`] = options[`${key}Localizations`];
14
+ } else if ((key === "options" || key === "choices") && Array.isArray(value)) for (const [index, option] of Object.entries(value)) {
15
+ const mapped = { [baseLocale]: option };
16
+ for (const locale of availableLocales) mapped[locale] = mappedOptions[locale][key][index];
17
+ const combined = combineCommandOptions(mapped, baseLocale);
18
+ options[key][index] = combined;
19
+ }
20
+ return options;
21
+ }
22
+ //#endregion
23
+ //#region src/mixers/with-say.ts
24
+ const ClassMap = /* @__PURE__ */ new WeakMap();
25
+ /**
26
+ * Factory function that creates a "withSay" wrapper around a base class.
27
+ *
28
+ * @param Base The base class constructor.
29
+ * @returns A subclass of the given base class with extra for localisation.
30
+ * @throws If the base class is neither a {@link BaseCommand} nor a
31
+ * {@link BaseComponent}.
32
+ */
33
+ function withSay(Base) {
34
+ if (ClassMap.has(Base)) return ClassMap.get(Base);
35
+ if (Base.prototype instanceof BaseCommand) {
36
+ const Derived = createSayCommand(Base);
37
+ ClassMap.set(Base, Derived);
38
+ return Derived;
39
+ }
40
+ if (Base.prototype instanceof BaseComponent || Base === Modal) {
41
+ const Derived = createSayComponent(Base);
42
+ ClassMap.set(Base, Derived);
43
+ return Derived;
44
+ }
45
+ throw new Error("Invalid base class");
46
+ }
47
+ function createSayCommand(Base) {
48
+ class SayCommand extends Base {
49
+ constructor(say, properties, ...args) {
50
+ super(...args);
51
+ const options = combineCommandOptions(Array.from(say).reduce((acc, [s, l]) => {
52
+ acc[l] = properties(s);
53
+ return acc;
54
+ }, {}), say.locale);
55
+ Object.assign(this, options);
56
+ }
57
+ }
58
+ return SayCommand;
59
+ }
60
+ function createSayComponent(Base) {
61
+ class SayComponent extends Base {
62
+ constructor(properties, ...args) {
63
+ super(...args);
64
+ if (properties) Object.assign(this, properties);
65
+ }
66
+ }
67
+ return SayComponent;
68
+ }
69
+ //#endregion
70
+ //#region src/constants.ts
71
+ const kSay = Symbol.for("saykit.say");
72
+ //#endregion
73
+ //#region src/extensions/base-interaction.ts
74
+ function applyBaseInteractionExtension() {
75
+ Object.defineProperty(BaseInteraction.prototype, "say", { get() {
76
+ const say = Reflect.get(globalThis, kSay);
77
+ if (!say) throw new Error("No `say` instance available");
78
+ this[kSay] ??= say.clone();
79
+ const locale = this[kSay].match([this.rawData.locale]);
80
+ this[kSay].activate(locale);
81
+ return this[kSay];
82
+ } });
83
+ return () => {
84
+ Object.defineProperty(BaseInteraction.prototype, "say", { value: void 0 });
85
+ };
86
+ }
87
+ //#endregion
88
+ //#region src/extensions/guild.ts
89
+ function applyGuildExtension() {
90
+ Object.defineProperty(Guild.prototype, "say", { get() {
91
+ const say = Reflect.get(globalThis, kSay);
92
+ if (!say) throw new Error("No `say` instance available");
93
+ this[kSay] ??= say.clone();
94
+ const locale = this[kSay].match([this.rawData.preferred_locale]);
95
+ this[kSay].activate(locale);
96
+ return this[kSay];
97
+ } });
98
+ return () => {
99
+ Object.defineProperty(Guild.prototype, "say", { value: void 0 });
100
+ };
101
+ }
102
+ //#endregion
103
+ //#region src/plugin.ts
104
+ /**
105
+ * A Carbon plugin that provides a singleton {@link Say} instance.
106
+ *
107
+ * `SayPlugin` registers a {@link Say} instance globally and
108
+ * applies interaction and guild-level extensions so that commands and
109
+ * other handlers can access localisation utilities directly.
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * const say = new Say({ ... });
114
+ * const client = new Client({ ... }, { ... }, [new SayPlugin(say)]);
115
+ * ```
116
+ */
117
+ var SayPlugin = class extends Plugin {
118
+ id = "saykit";
119
+ constructor(say) {
120
+ super();
121
+ Reflect.set(globalThis, kSay, say);
122
+ applyBaseInteractionExtension();
123
+ applyGuildExtension();
124
+ }
125
+ };
126
+ //#endregion
127
+ export { SayPlugin, withSay };
package/package.json CHANGED
@@ -1,4 +1,49 @@
1
- {
2
- "name": "@saykit/carbon",
3
- "version": "0.0.0"
1
+ {
2
+ "name": "@saykit/carbon",
3
+ "version": "0.2.0",
4
+ "description": "Carbon Discord framework integration for saykit",
5
+ "keywords": [
6
+ "bot",
7
+ "carbon",
8
+ "discord",
9
+ "i18n",
10
+ "saykit"
11
+ ],
12
+ "homepage": "https://github.com/k0d13/saykit#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/k0d13/saykit/issues"
15
+ },
16
+ "license": "MIT",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/k0d13/saykit.git",
20
+ "directory": "packages/integration-carbon"
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "!dist/**/*.map"
25
+ ],
26
+ "type": "module",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.mts",
30
+ "default": "./dist/index.mjs"
31
+ }
32
+ },
33
+ "publishConfig": {
34
+ "access": "public",
35
+ "provenance": true
36
+ },
37
+ "devDependencies": {
38
+ "@buape/carbon": "0.16.0",
39
+ "saykit": "^0.2.0"
40
+ },
41
+ "peerDependencies": {
42
+ "@buape/carbon": "*",
43
+ "saykit": "*"
44
+ },
45
+ "scripts": {
46
+ "check": "tsc --noEmit",
47
+ "build": "tsdown"
48
+ }
4
49
  }