commandkit 1.2.0-rc.3 → 1.2.0-rc.4
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 +4 -12
- package/dist/ActionRow-CmTHbo2t.js.map +1 -1
- package/dist/CommandKitEventsChannel-Bgw0XCOl.js.map +1 -1
- package/dist/CommandsRouter-tMAivEfv.js.map +1 -1
- package/dist/CompilerPlugin-CjoZAAR8.js.map +1 -1
- package/dist/{DefaultLogger-Cy55-uPl.d.ts → DefaultLogger-XCOl5nLd.d.ts} +19 -12
- package/dist/EventInterceptor-CQ4PBpBJ.js.map +1 -1
- package/dist/{EventWorkerContext-C7CG7hIP.d.ts → EventWorkerContext-BEezHh5-.d.ts} +2 -2
- package/dist/{EventWorkerContext-CSlD9rbx.js → EventWorkerContext-TZIKxVCu.js} +1 -1
- package/dist/{EventWorkerContext-CSlD9rbx.js.map → EventWorkerContext-TZIKxVCu.js.map} +1 -1
- package/dist/EventsRouter-C_J0M3uO.js.map +1 -1
- package/dist/ILogger-BW8GM-YZ.d.ts +64 -0
- package/dist/{Logger-iSjA-DWN.d.ts → Logger-DyfkPk7u.d.ts} +2 -2
- package/dist/{MessageCommandParser-B_BEQ3p8.js → MessageCommandParser-TPEPhjvx.js} +2 -2
- package/dist/{MessageCommandParser-B_BEQ3p8.js.map → MessageCommandParser-TPEPhjvx.js.map} +1 -1
- package/dist/PluginCommon-Di1xIa8d.js.map +1 -1
- package/dist/RuntimePlugin-CBgBLXTG.js.map +1 -1
- package/dist/analytics/analytics-engine.d.ts +2 -2
- package/dist/analytics/analytics-engine.js +6 -6
- package/dist/analytics/analytics-provider.d.ts +2 -2
- package/dist/analytics/constants.js +1 -1
- package/dist/analytics/utils.js +6 -6
- package/dist/{analytics-engine-nkyqvYAE.d.ts → analytics-engine-Iu_1uJop.d.ts} +83 -26
- package/dist/app/commands/AppCommandRunner.d.ts +2 -2
- package/dist/app/commands/AppCommandRunner.js +6 -6
- package/dist/app/commands/Context.d.ts +2 -2
- package/dist/app/commands/Context.js +6 -6
- package/dist/app/commands/MessageCommandParser.js +2 -2
- package/dist/app/events/EventWorkerContext.d.ts +3 -3
- package/dist/app/events/EventWorkerContext.js +1 -1
- package/dist/app/handlers/AppCommandHandler.d.ts +2 -2
- package/dist/app/handlers/AppCommandHandler.js +6 -6
- package/dist/app/handlers/AppEventsHandler.d.ts +2 -2
- package/dist/app/handlers/AppEventsHandler.js +6 -6
- package/dist/app/index.d.ts +3 -3
- package/dist/app/index.js +8 -8
- package/dist/app/interrupt/signals.d.ts +2 -2
- package/dist/app/interrupt/signals.js +5 -5
- package/dist/app/middlewares/permissions.d.ts +42 -0
- package/dist/app/middlewares/permissions.js +38 -0
- package/dist/app/register/CommandRegistrar.d.ts +2 -2
- package/dist/app/register/CommandRegistrar.js +6 -6
- package/dist/app-process-CjMovpoZ.js.map +1 -1
- package/dist/{build-BoGqWPIS.js → build-Yhb1VjD0.js} +2 -2
- package/dist/build-Yhb1VjD0.js.map +1 -0
- package/dist/cli/build.d.ts +2 -2
- package/dist/cli/build.js +7 -7
- package/dist/cli/common.d.ts +2 -2
- package/dist/cli/development.js +8 -8
- package/dist/cli/development.js.map +1 -1
- package/dist/cli/generators.js.map +1 -1
- package/dist/cli/information.js +1 -1
- package/dist/cli/information.js.map +1 -1
- package/dist/cli/init.js +7 -7
- package/dist/cli/production.js +7 -7
- package/dist/cli/production.js.map +1 -1
- package/dist/{commandkit-5VMG2thU.js → commandkit-c0sAaK0G.js} +349 -199
- package/dist/commandkit-c0sAaK0G.js.map +1 -0
- package/dist/commandkit.d.ts +2 -2
- package/dist/commandkit.js +6 -6
- package/dist/common-DNADUU3_.js.map +1 -1
- package/dist/common-vnMIelAE.js.map +1 -1
- package/dist/components/index.js +6 -6
- package/dist/components/v1/button/Button.js +6 -6
- package/dist/components/v1/button/ButtonKit.js +6 -6
- package/dist/components/v1/modal/Modal.js +6 -6
- package/dist/components/v1/modal/ModalKit.js +6 -6
- package/dist/components/v1/select-menu/ChannelSelectMenuKit.js +6 -6
- package/dist/components/v1/select-menu/MentionableSelectMenuKit.js +6 -6
- package/dist/components/v1/select-menu/RoleSelectMenuKit.js +6 -6
- package/dist/components/v1/select-menu/SelectMenu.js +6 -6
- package/dist/components/v1/select-menu/StringSelectMenuKit.js +6 -6
- package/dist/components/v1/select-menu/UserSelectMenuKit.js +6 -6
- package/dist/config/config.d.ts +3 -3
- package/dist/config/config.js +6 -6
- package/dist/config/default.d.ts +2 -2
- package/dist/config/default.js +6 -6
- package/dist/config/loader.d.ts +2 -2
- package/dist/config/loader.js +6 -6
- package/dist/config/types.d.ts +2 -2
- package/dist/config/utils.d.ts +2 -2
- package/dist/{config-C1msSu1G.d.ts → config-ClEaYnf1.d.ts} +2 -2
- package/dist/constants-4oxxvaJA.js.map +1 -1
- package/dist/constants-B5_Ta7PR.js.map +1 -1
- package/dist/{constants-CUND8XkG.js → constants-DxfYtA6t.js} +1 -1
- package/dist/{constants-CUND8XkG.js.map → constants-DxfYtA6t.js.map} +1 -1
- package/dist/container-z621KfH5.js.map +1 -1
- package/dist/context/async-context.d.ts +2 -2
- package/dist/context/async-context.js +6 -6
- package/dist/context/environment.d.ts +2 -2
- package/dist/context/environment.js +6 -6
- package/dist/dotprops-C_4L7RPD.js.map +1 -1
- package/dist/element-Bak9llw_.js.map +1 -1
- package/dist/env-_68PRRoA.js.map +1 -1
- package/dist/{error-codes-BzoEctmD.d.ts → error-codes-B4TyW4Ct.d.ts} +2 -2
- package/dist/{error-codes-C-ViHyu-.js → error-codes-Ds0bnPvT.js} +2 -2
- package/dist/error-codes-Ds0bnPvT.js.map +1 -0
- package/dist/events/CommandKitEventsChannel.d.ts +2 -2
- package/dist/{feature-flags-C44dggkN.js → feature-flags-Dxi9TI2e.js} +5 -5
- package/dist/{feature-flags-C44dggkN.js.map → feature-flags-Dxi9TI2e.js.map} +1 -1
- package/dist/file-DVZC0QXI.js.map +1 -1
- package/dist/flags/FlagProvider.d.ts +2 -2
- package/dist/flags/FlagProvider.js.map +1 -1
- package/dist/flags/feature-flags.d.ts +2 -2
- package/dist/flags/feature-flags.js +7 -7
- package/dist/flags/store.d.ts +2 -2
- package/dist/helpers-BUlN3lIz.js.map +1 -1
- package/dist/index.d.ts +9 -9
- package/dist/index.js +19 -14
- package/dist/index.js.map +1 -0
- package/dist/{init-ZjceQTSt.js → init-CrFvqdxZ.js} +2 -2
- package/dist/{init-ZjceQTSt.js.map → init-CrFvqdxZ.js.map} +1 -1
- package/dist/kv/kv.js.map +1 -1
- package/dist/logger/DefaultLogger.d.ts +2 -2
- package/dist/logger/DefaultLogger.js +6 -6
- package/dist/logger/ILogger.d.ts +1 -1
- package/dist/logger/Logger.d.ts +2 -2
- package/dist/logger/Logger.js +6 -6
- package/dist/logger/NoopLogger.d.ts +41 -11
- package/dist/logger/NoopLogger.js +5 -25
- package/dist/logger/NoopLogger.js.map +1 -1
- package/dist/media-gallery-CIKypjbJ.js.map +1 -1
- package/dist/plugins/CompilerPlugin.d.ts +2 -2
- package/dist/plugins/PluginCommon.d.ts +2 -2
- package/dist/plugins/RuntimePlugin.d.ts +2 -2
- package/dist/plugins/index.d.ts +2 -2
- package/dist/plugins/index.js +6 -6
- package/dist/plugins/plugin-runtime/CommandKitPluginRuntime.d.ts +2 -2
- package/dist/plugins/plugin-runtime/CommandKitPluginRuntime.js +6 -6
- package/dist/plugins/plugin-runtime/CompilerPluginRuntime.d.ts +2 -2
- package/dist/plugins/plugin-runtime/CompilerPluginRuntime.js +6 -6
- package/dist/plugins/plugin-runtime/builtin/CommonDirectiveTransformer.d.ts +2 -2
- package/dist/plugins/plugin-runtime/builtin/CommonDirectiveTransformer.js +6 -6
- package/dist/plugins/plugin-runtime/builtin/MacroPlugin.d.ts +2 -2
- package/dist/plugins/plugin-runtime/builtin/MacroPlugin.js +6 -6
- package/dist/plugins/plugin-runtime/runtime.d.ts +2 -2
- package/dist/plugins/types.d.ts +2 -2
- package/dist/resolve-file-url-DHTQj2mU.js.map +1 -1
- package/dist/section-CuYr0Inu.js.map +1 -1
- package/dist/separator-DLdWBjSN.js.map +1 -1
- package/dist/serde-CHySNpFr.js.map +1 -1
- package/dist/{signals-BQbEfy3X.d.ts → signals-SHg7J1U_.d.ts} +4 -7
- package/dist/{signals-DHdYrd-n.js → signals-r7qPAvOR.js} +12 -15
- package/dist/signals-r7qPAvOR.js.map +1 -0
- package/dist/store-CiqLHedg.js.map +1 -1
- package/dist/text-display--p2-BoUa.js.map +1 -1
- package/dist/type-checker-1Iu1jJcy.js.map +1 -1
- package/dist/types-package-DrnIdWLg.js.map +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/utils/dev-hooks.d.ts +2 -2
- package/dist/utils/dev-hooks.js +6 -6
- package/dist/utils/error-codes.d.ts +1 -1
- package/dist/utils/error-codes.js +1 -1
- package/dist/utils/useful-stuff/async-queue.js.map +1 -1
- package/dist/utils/useful-stuff/mutex.js.map +1 -1
- package/dist/utils/useful-stuff/ratelimiter.js.map +1 -1
- package/dist/utils/useful-stuff/semaphore.js.map +1 -1
- package/dist/utils/utilities.js +6 -6
- package/dist/utils/warn-unstable.js.map +1 -1
- package/dist/utils-BnXM4eKk.js.map +1 -1
- package/dist/utils-DCSnVAZ6.js.map +1 -1
- package/dist/{version-D8HlFOxx.js → version-BoHnO-Go.js} +2 -2
- package/dist/{version-D8HlFOxx.js.map → version-BoHnO-Go.js.map} +1 -1
- package/dist/version.js +1 -1
- package/package.json +60 -59
- package/dist/ILogger-DBGON5wx.d.ts +0 -34
- package/dist/build-BoGqWPIS.js.map +0 -1
- package/dist/commandkit-5VMG2thU.js.map +0 -1
- package/dist/error-codes-C-ViHyu-.js.map +0 -1
- package/dist/signals-DHdYrd-n.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CompilerPlugin, MaybeFalsey, PluginTransformParameters, TransformedResult } from "../../../analytics-engine-
|
|
1
|
+
import { CompilerPlugin, MaybeFalsey, PluginTransformParameters, TransformedResult } from "../../../analytics-engine-Iu_1uJop.js";
|
|
2
2
|
import "../../../EventInterceptor-x-R2qshQ.js";
|
|
3
3
|
import "../../../ButtonKit-C7r7RPNf.js";
|
|
4
4
|
import "../../../ModalKit-Btz58KYn.js";
|
|
@@ -23,7 +23,7 @@ import "../../../text-display-DRw3r_lK.js";
|
|
|
23
23
|
import "../../../index-DSBMRX9A.js";
|
|
24
24
|
import "../../../index-DdqikUQD.js";
|
|
25
25
|
import "../../../MessageCommandParser-ROLibZPX.js";
|
|
26
|
-
import "../../../signals-
|
|
26
|
+
import "../../../signals-SHg7J1U_.js";
|
|
27
27
|
import "../../../helpers-Wg6P5fRQ.js";
|
|
28
28
|
import "../../../CommandsRouter-CoOA7hkf.js";
|
|
29
29
|
import "../../../EventsRouter-BacqK6z3.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require('../../../colors-Cd4Oz-r-.js');
|
|
2
2
|
require('../../../ActionRow-CmTHbo2t.js');
|
|
3
|
-
require('../../../error-codes-
|
|
4
|
-
const require_commandkit = require('../../../commandkit-
|
|
3
|
+
require('../../../error-codes-Ds0bnPvT.js');
|
|
4
|
+
const require_commandkit = require('../../../commandkit-c0sAaK0G.js');
|
|
5
5
|
require('../../../common-CcfjYnPG.js');
|
|
6
6
|
require('../../../common-vnMIelAE.js');
|
|
7
7
|
require('../../../container-z621KfH5.js');
|
|
@@ -23,10 +23,10 @@ require('../../../runtime-5fxB4uhe.js');
|
|
|
23
23
|
require('../../../utils-BnXM4eKk.js');
|
|
24
24
|
require('../../../resolve-file-url-DHTQj2mU.js');
|
|
25
25
|
require('../../../types-package-DrnIdWLg.js');
|
|
26
|
-
require('../../../
|
|
27
|
-
require('../../../
|
|
28
|
-
require('../../../
|
|
29
|
-
require('../../../MessageCommandParser-
|
|
26
|
+
require('../../../constants-DxfYtA6t.js');
|
|
27
|
+
require('../../../EventWorkerContext-TZIKxVCu.js');
|
|
28
|
+
require('../../../signals-r7qPAvOR.js');
|
|
29
|
+
require('../../../MessageCommandParser-TPEPhjvx.js');
|
|
30
30
|
require('../../../CommandsRouter-tMAivEfv.js');
|
|
31
31
|
require('../../../EventsRouter-C_J0M3uO.js');
|
|
32
32
|
require('../../../router-DHnFRADH.js');
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CommonPluginRuntime } from "../../analytics-engine-
|
|
1
|
+
import { CommonPluginRuntime } from "../../analytics-engine-Iu_1uJop.js";
|
|
2
2
|
import "../../EventInterceptor-x-R2qshQ.js";
|
|
3
3
|
import "../../ButtonKit-C7r7RPNf.js";
|
|
4
4
|
import "../../ModalKit-Btz58KYn.js";
|
|
@@ -23,7 +23,7 @@ import "../../text-display-DRw3r_lK.js";
|
|
|
23
23
|
import "../../index-DSBMRX9A.js";
|
|
24
24
|
import "../../index-DdqikUQD.js";
|
|
25
25
|
import "../../MessageCommandParser-ROLibZPX.js";
|
|
26
|
-
import "../../signals-
|
|
26
|
+
import "../../signals-SHg7J1U_.js";
|
|
27
27
|
import "../../helpers-Wg6P5fRQ.js";
|
|
28
28
|
import "../../CommandsRouter-CoOA7hkf.js";
|
|
29
29
|
import "../../EventsRouter-BacqK6z3.js";
|
package/dist/plugins/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CommandKitEventDispatch, CommandKitPlugin, MaybeFalsey, TemplateHandler } from "../analytics-engine-
|
|
1
|
+
import { CommandKitEventDispatch, CommandKitPlugin, MaybeFalsey, TemplateHandler } from "../analytics-engine-Iu_1uJop.js";
|
|
2
2
|
import "../EventInterceptor-x-R2qshQ.js";
|
|
3
3
|
import "../ButtonKit-C7r7RPNf.js";
|
|
4
4
|
import "../ModalKit-Btz58KYn.js";
|
|
@@ -23,7 +23,7 @@ import "../text-display-DRw3r_lK.js";
|
|
|
23
23
|
import "../index-DSBMRX9A.js";
|
|
24
24
|
import "../index-DdqikUQD.js";
|
|
25
25
|
import "../MessageCommandParser-ROLibZPX.js";
|
|
26
|
-
import "../signals-
|
|
26
|
+
import "../signals-SHg7J1U_.js";
|
|
27
27
|
import "../helpers-Wg6P5fRQ.js";
|
|
28
28
|
import "../CommandsRouter-CoOA7hkf.js";
|
|
29
29
|
import "../EventsRouter-BacqK6z3.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve-file-url-DHTQj2mU.js","names":[],"sources":["../src/utils/resolve-file-url.ts"],"sourcesContent":["import path from 'path';\n\n/**\n * Convert a local file path to a file URL.\n * @param filePath - The local file's path.\n * @param withTs - Whether to append a timestamp to the URL.\n * @returns - The converted file URL.\n */\nexport function toFileURL(filePath: string, withTs = false) {\n const resolvedPath = path.resolve(filePath);\n return `${'file://' + resolvedPath.replace(/\\\\\\\\|\\\\/g, '/')}${withTs ? `?ts=${Date.now()}` : ''}`;\n}\n"],"mappings":";;;;;;;;;;AAQA,SAAgB,UAAU,UAAkB,SAAS,OAAO;CAC1D,MAAM,eAAe,aAAK,QAAQ;AAClC,QAAO,GAAG,YAAY,aAAa,QAAQ,YAAY,OAAO,SAAS,OAAO,KAAK,UAAU
|
|
1
|
+
{"version":3,"file":"resolve-file-url-DHTQj2mU.js","names":[],"sources":["../src/utils/resolve-file-url.ts"],"sourcesContent":["import path from 'path';\n\n/**\n * Convert a local file path to a file URL.\n * @param filePath - The local file's path.\n * @param withTs - Whether to append a timestamp to the URL.\n * @returns - The converted file URL.\n */\nexport function toFileURL(filePath: string, withTs = false) {\n const resolvedPath = path.resolve(filePath);\n return `${'file://' + resolvedPath.replace(/\\\\\\\\|\\\\/g, '/')}${withTs ? `?ts=${Date.now()}` : ''}`;\n}\n"],"mappings":";;;;;;;;;;AAQA,SAAgB,UAAU,UAAkB,SAAS,OAAO;CAC1D,MAAM,eAAe,aAAK,QAAQ;AAClC,QAAO,GAAG,YAAY,aAAa,QAAQ,YAAY,OAAO,SAAS,OAAO,KAAK,UAAU"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"section-CuYr0Inu.js","names":[],"sources":["../src/components/v2/section.ts"],"sourcesContent":["import {\n ButtonBuilder,\n SectionBuilder,\n TextDisplayBuilder,\n ThumbnailBuilder,\n} from 'discord.js';\nimport { applyId } from './common';\n\n/**\n * Represents the properties for a section component.\n */\nexport interface SectionProps {\n children?: (ThumbnailBuilder | ButtonBuilder | TextDisplayBuilder)[];\n id?: number;\n}\n\n/**\n * The components v2 section component\n * @param props The properties for the section component.\n * @returns The section builder instance.\n * @example ```tsx\n * import { Section } from 'commandkit';\n *\n * const section = <Section>...</Section>;\n * ```\n */\nexport function Section(props: SectionProps): SectionBuilder {\n const section = new SectionBuilder();\n\n applyId(props, section);\n\n if (props.children != null) {\n if (!Array.isArray(props.children)) props.children = [props.children];\n for (const accessory of props.children.flat()) {\n if (accessory instanceof ThumbnailBuilder) {\n section.setThumbnailAccessory(accessory);\n } else if (accessory instanceof ButtonBuilder) {\n section.setButtonAccessory(accessory);\n } else if (accessory instanceof TextDisplayBuilder) {\n section.addTextDisplayComponents(accessory);\n }\n }\n }\n\n return section;\n}\n\n/**\n * Represents the properties for a thumbnail component.\n */\nexport interface ThumbnailProps {\n id?: number;\n description?: string;\n spoiler?: boolean;\n url: string;\n}\n\n/**\n * The thumbnail component\n * @param props The properties for the thumbnail component.\n * @returns The thumbnail builder instance.\n * @example ```tsx\n * import { Thumbnail } from 'commandkit';\n *\n * const thumbnail = <Thumbnail url=\"https://example.com/image.png\" description=\"An image\" />;\n * ```\n */\nexport function Thumbnail(props: ThumbnailProps) {\n const thumbnail = new ThumbnailBuilder({\n description: props.description,\n spoiler: props.spoiler,\n id: props.id,\n media: { url: props.url },\n });\n\n return thumbnail;\n}\n"],"mappings":";;;;;;;;;;;;;;;AA0BA,SAAgB,QAAQ,OAAqC;CAC3D,MAAM,UAAU,IAAI;AAEpB,wBAAQ,OAAO;AAEf,KAAI,MAAM,YAAY,MAAM;AAC1B,MAAI,CAAC,MAAM,QAAQ,MAAM,UAAW,OAAM,WAAW,CAAC,MAAM
|
|
1
|
+
{"version":3,"file":"section-CuYr0Inu.js","names":[],"sources":["../src/components/v2/section.ts"],"sourcesContent":["import {\n ButtonBuilder,\n SectionBuilder,\n TextDisplayBuilder,\n ThumbnailBuilder,\n} from 'discord.js';\nimport { applyId } from './common';\n\n/**\n * Represents the properties for a section component.\n */\nexport interface SectionProps {\n children?: (ThumbnailBuilder | ButtonBuilder | TextDisplayBuilder)[];\n id?: number;\n}\n\n/**\n * The components v2 section component\n * @param props The properties for the section component.\n * @returns The section builder instance.\n * @example ```tsx\n * import { Section } from 'commandkit';\n *\n * const section = <Section>...</Section>;\n * ```\n */\nexport function Section(props: SectionProps): SectionBuilder {\n const section = new SectionBuilder();\n\n applyId(props, section);\n\n if (props.children != null) {\n if (!Array.isArray(props.children)) props.children = [props.children];\n for (const accessory of props.children.flat()) {\n if (accessory instanceof ThumbnailBuilder) {\n section.setThumbnailAccessory(accessory);\n } else if (accessory instanceof ButtonBuilder) {\n section.setButtonAccessory(accessory);\n } else if (accessory instanceof TextDisplayBuilder) {\n section.addTextDisplayComponents(accessory);\n }\n }\n }\n\n return section;\n}\n\n/**\n * Represents the properties for a thumbnail component.\n */\nexport interface ThumbnailProps {\n id?: number;\n description?: string;\n spoiler?: boolean;\n url: string;\n}\n\n/**\n * The thumbnail component\n * @param props The properties for the thumbnail component.\n * @returns The thumbnail builder instance.\n * @example ```tsx\n * import { Thumbnail } from 'commandkit';\n *\n * const thumbnail = <Thumbnail url=\"https://example.com/image.png\" description=\"An image\" />;\n * ```\n */\nexport function Thumbnail(props: ThumbnailProps) {\n const thumbnail = new ThumbnailBuilder({\n description: props.description,\n spoiler: props.spoiler,\n id: props.id,\n media: { url: props.url },\n });\n\n return thumbnail;\n}\n"],"mappings":";;;;;;;;;;;;;;;AA0BA,SAAgB,QAAQ,OAAqC;CAC3D,MAAM,UAAU,IAAI;AAEpB,wBAAQ,OAAO;AAEf,KAAI,MAAM,YAAY,MAAM;AAC1B,MAAI,CAAC,MAAM,QAAQ,MAAM,UAAW,OAAM,WAAW,CAAC,MAAM;AAC5D,OAAK,MAAM,aAAa,MAAM,SAAS,OACrC,KAAI,qBAAqB,4BACvB,SAAQ,sBAAsB;WACrB,qBAAqB,yBAC9B,SAAQ,mBAAmB;WAClB,qBAAqB,8BAC9B,SAAQ,yBAAyB;;AAKvC,QAAO;;;;;;;;;;;;AAuBT,SAAgB,UAAU,OAAuB;CAC/C,MAAM,YAAY,IAAI,4BAAiB;EACrC,aAAa,MAAM;EACnB,SAAS,MAAM;EACf,IAAI,MAAM;EACV,OAAO,EAAE,KAAK,MAAM;;AAGtB,QAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"separator-DLdWBjSN.js","names":[],"sources":["../src/components/v2/separator.ts"],"sourcesContent":["import { SeparatorBuilder, SeparatorComponentData } from 'discord.js';\n\n/**\n * Represents the properties for a separator component.\n */\nexport interface SeparatorProps extends Omit<SeparatorComponentData, 'type'> {}\n\n/**\n * The components v2 separator component\n * @param props The properties for the separator component.\n * @returns The separator builder instance.\n * @example ```tsx\n * import { Separator } from 'commandkit';\n *\n * const separator = <Separator />;\n * ```\n */\nexport function Separator(props: SeparatorProps) {\n const separator = new SeparatorBuilder(props);\n return separator;\n}\n"],"mappings":";;;;;;;;;;;;;;AAiBA,SAAgB,UAAU,OAAuB;CAC/C,MAAM,YAAY,IAAI,4BAAiB;AACvC,QAAO
|
|
1
|
+
{"version":3,"file":"separator-DLdWBjSN.js","names":[],"sources":["../src/components/v2/separator.ts"],"sourcesContent":["import { SeparatorBuilder, SeparatorComponentData } from 'discord.js';\n\n/**\n * Represents the properties for a separator component.\n */\nexport interface SeparatorProps extends Omit<SeparatorComponentData, 'type'> {}\n\n/**\n * The components v2 separator component\n * @param props The properties for the separator component.\n * @returns The separator builder instance.\n * @example ```tsx\n * import { Separator } from 'commandkit';\n *\n * const separator = <Separator />;\n * ```\n */\nexport function Separator(props: SeparatorProps) {\n const separator = new SeparatorBuilder(props);\n return separator;\n}\n"],"mappings":";;;;;;;;;;;;;;AAiBA,SAAgB,UAAU,OAAuB;CAC/C,MAAM,YAAY,IAAI,4BAAiB;AACvC,QAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serde-CHySNpFr.js","names":[],"sources":["../src/kv/serde.ts"],"sourcesContent":["import { SerializerType } from './constants';\n\nexport interface SerializedValue {\n t: SerializerType;\n v: any;\n}\n\nexport function serializer(value: any): SerializedValue {\n if (value === null) {\n return { t: SerializerType.Null, v: null };\n }\n\n if (value === undefined) {\n return { t: SerializerType.Undefined, v: undefined };\n }\n\n if (typeof value === 'string') {\n return { t: SerializerType.String, v: value };\n }\n\n if (typeof value === 'number') {\n return { t: SerializerType.Number, v: value };\n }\n\n if (typeof value === 'boolean') {\n return { t: SerializerType.Boolean, v: value };\n }\n\n if (typeof value === 'bigint') {\n return { t: SerializerType.BigInt, v: value.toString() };\n }\n\n if (value instanceof Date) {\n return { t: SerializerType.Date, v: value.toISOString() };\n }\n\n if (Array.isArray(value)) {\n return { t: SerializerType.Array, v: value };\n }\n\n if (value instanceof Map) {\n return { t: SerializerType.Map, v: Array.from(value.entries()) };\n }\n\n if (value instanceof Set) {\n return { t: SerializerType.Set, v: Array.from(value) };\n }\n\n if (Buffer.isBuffer(value)) {\n return { t: SerializerType.Buffer, v: value.toString('base64') };\n }\n\n if (value instanceof RegExp) {\n return {\n t: SerializerType.RegExp,\n v: { source: value.source, flags: value.flags },\n };\n }\n\n if (typeof value === 'object') {\n return { t: SerializerType.Object, v: value };\n }\n\n // Fallback to string\n return { t: SerializerType.String, v: String(value) };\n}\n\nexport function deserializer(serialized: SerializedValue): any {\n switch (serialized.t) {\n case SerializerType.Null:\n return null;\n case SerializerType.Undefined:\n return undefined;\n case SerializerType.String:\n return serialized.v;\n case SerializerType.Number:\n return serialized.v;\n case SerializerType.Boolean:\n return serialized.v;\n case SerializerType.BigInt:\n return BigInt(serialized.v);\n case SerializerType.Date:\n return new Date(serialized.v);\n case SerializerType.Array:\n return serialized.v;\n case SerializerType.Map:\n return new Map(serialized.v);\n case SerializerType.Set:\n return new Set(serialized.v);\n case SerializerType.Buffer:\n return Buffer.from(serialized.v, 'base64');\n case SerializerType.RegExp:\n return new RegExp(serialized.v.source, serialized.v.flags);\n case SerializerType.Object:\n return serialized.v;\n default:\n return serialized.v;\n }\n}\n"],"mappings":";;;AAOA,SAAgB,WAAW,OAA6B;AACtD,KAAI,UAAU,KACZ,QAAO;EAAE,GAAG,iCAAe;EAAM,GAAG
|
|
1
|
+
{"version":3,"file":"serde-CHySNpFr.js","names":[],"sources":["../src/kv/serde.ts"],"sourcesContent":["import { SerializerType } from './constants';\n\nexport interface SerializedValue {\n t: SerializerType;\n v: any;\n}\n\nexport function serializer(value: any): SerializedValue {\n if (value === null) {\n return { t: SerializerType.Null, v: null };\n }\n\n if (value === undefined) {\n return { t: SerializerType.Undefined, v: undefined };\n }\n\n if (typeof value === 'string') {\n return { t: SerializerType.String, v: value };\n }\n\n if (typeof value === 'number') {\n return { t: SerializerType.Number, v: value };\n }\n\n if (typeof value === 'boolean') {\n return { t: SerializerType.Boolean, v: value };\n }\n\n if (typeof value === 'bigint') {\n return { t: SerializerType.BigInt, v: value.toString() };\n }\n\n if (value instanceof Date) {\n return { t: SerializerType.Date, v: value.toISOString() };\n }\n\n if (Array.isArray(value)) {\n return { t: SerializerType.Array, v: value };\n }\n\n if (value instanceof Map) {\n return { t: SerializerType.Map, v: Array.from(value.entries()) };\n }\n\n if (value instanceof Set) {\n return { t: SerializerType.Set, v: Array.from(value) };\n }\n\n if (Buffer.isBuffer(value)) {\n return { t: SerializerType.Buffer, v: value.toString('base64') };\n }\n\n if (value instanceof RegExp) {\n return {\n t: SerializerType.RegExp,\n v: { source: value.source, flags: value.flags },\n };\n }\n\n if (typeof value === 'object') {\n return { t: SerializerType.Object, v: value };\n }\n\n // Fallback to string\n return { t: SerializerType.String, v: String(value) };\n}\n\nexport function deserializer(serialized: SerializedValue): any {\n switch (serialized.t) {\n case SerializerType.Null:\n return null;\n case SerializerType.Undefined:\n return undefined;\n case SerializerType.String:\n return serialized.v;\n case SerializerType.Number:\n return serialized.v;\n case SerializerType.Boolean:\n return serialized.v;\n case SerializerType.BigInt:\n return BigInt(serialized.v);\n case SerializerType.Date:\n return new Date(serialized.v);\n case SerializerType.Array:\n return serialized.v;\n case SerializerType.Map:\n return new Map(serialized.v);\n case SerializerType.Set:\n return new Set(serialized.v);\n case SerializerType.Buffer:\n return Buffer.from(serialized.v, 'base64');\n case SerializerType.RegExp:\n return new RegExp(serialized.v.source, serialized.v.flags);\n case SerializerType.Object:\n return serialized.v;\n default:\n return serialized.v;\n }\n}\n"],"mappings":";;;AAOA,SAAgB,WAAW,OAA6B;AACtD,KAAI,UAAU,KACZ,QAAO;EAAE,GAAG,iCAAe;EAAM,GAAG;;AAGtC,KAAI,UAAU,OACZ,QAAO;EAAE,GAAG,iCAAe;EAAW,GAAG;;AAG3C,KAAI,OAAO,UAAU,SACnB,QAAO;EAAE,GAAG,iCAAe;EAAQ,GAAG;;AAGxC,KAAI,OAAO,UAAU,SACnB,QAAO;EAAE,GAAG,iCAAe;EAAQ,GAAG;;AAGxC,KAAI,OAAO,UAAU,UACnB,QAAO;EAAE,GAAG,iCAAe;EAAS,GAAG;;AAGzC,KAAI,OAAO,UAAU,SACnB,QAAO;EAAE,GAAG,iCAAe;EAAQ,GAAG,MAAM;;AAG9C,KAAI,iBAAiB,KACnB,QAAO;EAAE,GAAG,iCAAe;EAAM,GAAG,MAAM;;AAG5C,KAAI,MAAM,QAAQ,OAChB,QAAO;EAAE,GAAG,iCAAe;EAAO,GAAG;;AAGvC,KAAI,iBAAiB,IACnB,QAAO;EAAE,GAAG,iCAAe;EAAK,GAAG,MAAM,KAAK,MAAM;;AAGtD,KAAI,iBAAiB,IACnB,QAAO;EAAE,GAAG,iCAAe;EAAK,GAAG,MAAM,KAAK;;AAGhD,KAAI,OAAO,SAAS,OAClB,QAAO;EAAE,GAAG,iCAAe;EAAQ,GAAG,MAAM,SAAS;;AAGvD,KAAI,iBAAiB,OACnB,QAAO;EACL,GAAG,iCAAe;EAClB,GAAG;GAAE,QAAQ,MAAM;GAAQ,OAAO,MAAM;;;AAI5C,KAAI,OAAO,UAAU,SACnB,QAAO;EAAE,GAAG,iCAAe;EAAQ,GAAG;;AAIxC,QAAO;EAAE,GAAG,iCAAe;EAAQ,GAAG,OAAO;;;AAG/C,SAAgB,aAAa,YAAkC;AAC7D,SAAQ,WAAW,GAAnB;EACE,KAAK,iCAAe,KAClB,QAAO;EACT,KAAK,iCAAe,UAClB,QAAO;EACT,KAAK,iCAAe,OAClB,QAAO,WAAW;EACpB,KAAK,iCAAe,OAClB,QAAO,WAAW;EACpB,KAAK,iCAAe,QAClB,QAAO,WAAW;EACpB,KAAK,iCAAe,OAClB,QAAO,OAAO,WAAW;EAC3B,KAAK,iCAAe,KAClB,QAAO,IAAI,KAAK,WAAW;EAC7B,KAAK,iCAAe,MAClB,QAAO,WAAW;EACpB,KAAK,iCAAe,IAClB,QAAO,IAAI,IAAI,WAAW;EAC5B,KAAK,iCAAe,IAClB,QAAO,IAAI,IAAI,WAAW;EAC5B,KAAK,iCAAe,OAClB,QAAO,OAAO,KAAK,WAAW,GAAG;EACnC,KAAK,iCAAe,OAClB,QAAO,IAAI,OAAO,WAAW,EAAE,QAAQ,WAAW,EAAE;EACtD,KAAK,iCAAe,OAClB,QAAO,WAAW;EACpB,QACE,QAAO,WAAW"}
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
//#region src/app/interrupt/signals.d.ts
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* If this is called inside pre-stage middleware, the next run will be the actual command, skipping all other pre-stage middlewares.
|
|
5
|
-
* If this is called inside a command itself, it will skip all post-stage middlewares.
|
|
6
|
-
* If this is called inside post-stage middleware, it will skip all other post-stage middlewares.
|
|
3
|
+
* Stop upcoming middlewares and command execution.
|
|
7
4
|
*/
|
|
8
|
-
declare function
|
|
5
|
+
declare function stopMiddlewares(): never;
|
|
9
6
|
/**
|
|
10
7
|
* Rethrow the error if it is a CommandKit error.
|
|
11
8
|
* @param error The error to rethrow.
|
|
@@ -37,5 +34,5 @@ declare function redirect(): never;
|
|
|
37
34
|
*/
|
|
38
35
|
declare function stopEvents(): never;
|
|
39
36
|
//#endregion
|
|
40
|
-
export {
|
|
41
|
-
//# sourceMappingURL=signals-
|
|
37
|
+
export { redirect, rethrow, stopEvents, stopMiddlewares };
|
|
38
|
+
//# sourceMappingURL=signals-SHg7J1U_.d.ts.map
|
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
const require_error_codes = require('./error-codes-
|
|
2
|
-
const require_EventWorkerContext = require('./EventWorkerContext-
|
|
1
|
+
const require_error_codes = require('./error-codes-Ds0bnPvT.js');
|
|
2
|
+
const require_EventWorkerContext = require('./EventWorkerContext-TZIKxVCu.js');
|
|
3
3
|
|
|
4
4
|
//#region src/app/interrupt/signals.ts
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
* If this is called inside pre-stage middleware, the next run will be the actual command, skipping all other pre-stage middlewares.
|
|
8
|
-
* If this is called inside a command itself, it will skip all post-stage middlewares.
|
|
9
|
-
* If this is called inside post-stage middleware, it will skip all other post-stage middlewares.
|
|
6
|
+
* Stop upcoming middlewares and command execution.
|
|
10
7
|
*/
|
|
11
|
-
function
|
|
12
|
-
throw require_error_codes.createCommandKitError(require_error_codes.CommandKitErrorCodes.
|
|
8
|
+
function stopMiddlewares() {
|
|
9
|
+
throw require_error_codes.createCommandKitError(require_error_codes.CommandKitErrorCodes.StopMiddlewares);
|
|
13
10
|
}
|
|
14
11
|
/**
|
|
15
12
|
* Rethrow the error if it is a CommandKit error.
|
|
@@ -50,12 +47,6 @@ function stopEvents() {
|
|
|
50
47
|
}
|
|
51
48
|
|
|
52
49
|
//#endregion
|
|
53
|
-
Object.defineProperty(exports, 'exitMiddleware', {
|
|
54
|
-
enumerable: true,
|
|
55
|
-
get: function () {
|
|
56
|
-
return exitMiddleware;
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
50
|
Object.defineProperty(exports, 'redirect', {
|
|
60
51
|
enumerable: true,
|
|
61
52
|
get: function () {
|
|
@@ -74,4 +65,10 @@ Object.defineProperty(exports, 'stopEvents', {
|
|
|
74
65
|
return stopEvents;
|
|
75
66
|
}
|
|
76
67
|
});
|
|
77
|
-
|
|
68
|
+
Object.defineProperty(exports, 'stopMiddlewares', {
|
|
69
|
+
enumerable: true,
|
|
70
|
+
get: function () {
|
|
71
|
+
return stopMiddlewares;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
//# sourceMappingURL=signals-r7qPAvOR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signals-r7qPAvOR.js","names":[],"sources":["../src/app/interrupt/signals.ts"],"sourcesContent":["import {\n CommandKitErrorCodes,\n createCommandKitError,\n isCommandKitError,\n} from '../../utils/error-codes';\nimport { eventWorkerContext } from '../events/EventWorkerContext';\n\n/**\n * Stop upcoming middlewares and command execution.\n */\nexport function stopMiddlewares(): never {\n throw createCommandKitError(CommandKitErrorCodes.StopMiddlewares);\n}\n\n/**\n * Rethrow the error if it is a CommandKit error.\n * @param error The error to rethrow.\n * @example try {\n * doSomething();\n * } catch(e) {\n * // do something\n *\n * // throw the error if it's a commandkit error\n * rethrow(e)\n * }\n */\nexport function rethrow(error: unknown): void {\n if (isCommandKitError(error)) throw error;\n}\n\n/**\n * Stops current command assuming it has been redirected to another command.\n */\nexport function redirect(): never {\n throw createCommandKitError(CommandKitErrorCodes.ForwardedCommand);\n}\n\n/**\n * Stops event propagation. This function should be called inside an event handler\n * to prevent further event handling.\n * @example // src/app/events/messageCreate/handler.ts\n * import { stopEvents } from 'commandkit';\n *\n * export default async function messageCreateHandler() {\n * console.log('Message created');\n * // Stop further event propagation\n * stopEvents();\n * }\n */\nexport function stopEvents(): never {\n if (!eventWorkerContext.getStore()) {\n throw new Error('stopEvents() may only be called inside an event handler');\n }\n\n throw createCommandKitError(CommandKitErrorCodes.StopEvents);\n}\n"],"mappings":";;;;;;;AAUA,SAAgB,kBAAyB;AACvC,OAAM,0CAAsB,yCAAqB;;;;;;;;;;;;;;AAenD,SAAgB,QAAQ,OAAsB;AAC5C,KAAI,sCAAkB,OAAQ,OAAM;;;;;AAMtC,SAAgB,WAAkB;AAChC,OAAM,0CAAsB,yCAAqB;;;;;;;;;;;;;;AAenD,SAAgB,aAAoB;AAClC,KAAI,CAAC,8CAAmB,WACtB,OAAM,IAAI,MAAM;AAGlB,OAAM,0CAAsB,yCAAqB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store-CiqLHedg.js","names":[],"sources":["../src/flags/store.ts"],"sourcesContent":["import type { FeatureFlag } from './feature-flags';\nimport { Collection } from 'discord.js';\n\n/**\n * Represents a store for feature flags.\n * This store extends the Collection class to manage feature flags by their keys.\n */\nexport class FlagStore extends Collection<string, FeatureFlag<any, any>> {}\n"],"mappings":";;;;;;;;AAOA,IAAa,YAAb,cAA+B,sBAA0C
|
|
1
|
+
{"version":3,"file":"store-CiqLHedg.js","names":[],"sources":["../src/flags/store.ts"],"sourcesContent":["import type { FeatureFlag } from './feature-flags';\nimport { Collection } from 'discord.js';\n\n/**\n * Represents a store for feature flags.\n * This store extends the Collection class to manage feature flags by their keys.\n */\nexport class FlagStore extends Collection<string, FeatureFlag<any, any>> {}\n"],"mappings":";;;;;;;;AAOA,IAAa,YAAb,cAA+B,sBAA0C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text-display--p2-BoUa.js","names":[],"sources":["../src/components/v2/text-display.ts"],"sourcesContent":["import { TextDisplayBuilder, TextDisplayComponentData } from 'discord.js';\n\n/**\n * Represents a type that can be encoded as a string.\n */\nexport type StringEncodable = string | number | boolean;\n\n/**\n * Represents the properties for a text display component.\n * This interface extends the TextDisplayComponentData type from discord.js,\n * excluding the 'type' and 'content' properties.\n */\nexport interface TextDisplayProps\n extends Omit<TextDisplayComponentData, 'type' | 'content'> {\n children?: StringEncodable | StringEncodable[];\n content?: string;\n}\n\n/**\n * The components v2 text display component\n * @param props The properties for the text display component.\n * @returns The text display builder instance.\n * @example ```tsx\n * import { TextDisplay } from 'commandkit';\n *\n * const textDisplay = <TextDisplay content=\"Hello, world!\" />;\n * // or\n * const textDisplay = <TextDisplay>Some text content</TextDisplay>;\n * ```\n */\nexport function TextDisplay(props: TextDisplayProps) {\n const textDisplay = new TextDisplayBuilder(props);\n\n if (!props.content && props.children) {\n if (Array.isArray(props.children)) {\n textDisplay.setContent(props.children.join(' '));\n } else {\n textDisplay.setContent(\n typeof props.children === 'string'\n ? props.children\n : String(props.children),\n );\n }\n }\n\n return textDisplay;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA8BA,SAAgB,YAAY,OAAyB;CACnD,MAAM,cAAc,IAAI,8BAAmB;AAE3C,KAAI,CAAC,MAAM,WAAW,MAAM,SAC1B,KAAI,MAAM,QAAQ,MAAM,UACtB,aAAY,WAAW,MAAM,SAAS,KAAK;KAE3C,aAAY,WACV,OAAO,MAAM,aAAa,WAC1B,MAAM,WACN,OAAO,MAAM;AAKnB,QAAO
|
|
1
|
+
{"version":3,"file":"text-display--p2-BoUa.js","names":[],"sources":["../src/components/v2/text-display.ts"],"sourcesContent":["import { TextDisplayBuilder, TextDisplayComponentData } from 'discord.js';\n\n/**\n * Represents a type that can be encoded as a string.\n */\nexport type StringEncodable = string | number | boolean;\n\n/**\n * Represents the properties for a text display component.\n * This interface extends the TextDisplayComponentData type from discord.js,\n * excluding the 'type' and 'content' properties.\n */\nexport interface TextDisplayProps\n extends Omit<TextDisplayComponentData, 'type' | 'content'> {\n children?: StringEncodable | StringEncodable[];\n content?: string;\n}\n\n/**\n * The components v2 text display component\n * @param props The properties for the text display component.\n * @returns The text display builder instance.\n * @example ```tsx\n * import { TextDisplay } from 'commandkit';\n *\n * const textDisplay = <TextDisplay content=\"Hello, world!\" />;\n * // or\n * const textDisplay = <TextDisplay>Some text content</TextDisplay>;\n * ```\n */\nexport function TextDisplay(props: TextDisplayProps) {\n const textDisplay = new TextDisplayBuilder(props);\n\n if (!props.content && props.children) {\n if (Array.isArray(props.children)) {\n textDisplay.setContent(props.children.join(' '));\n } else {\n textDisplay.setContent(\n typeof props.children === 'string'\n ? props.children\n : String(props.children),\n );\n }\n }\n\n return textDisplay;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA8BA,SAAgB,YAAY,OAAyB;CACnD,MAAM,cAAc,IAAI,8BAAmB;AAE3C,KAAI,CAAC,MAAM,WAAW,MAAM,SAC1B,KAAI,MAAM,QAAQ,MAAM,UACtB,aAAY,WAAW,MAAM,SAAS,KAAK;KAE3C,aAAY,WACV,OAAO,MAAM,aAAa,WAC1B,MAAM,WACN,OAAO,MAAM;AAKnB,QAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"type-checker-1Iu1jJcy.js","names":[],"sources":["../src/cli/type-checker.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { loadTypeScript } from './common';\nimport { join, relative } from 'node:path';\nimport colors from '../utils/colors';\n\nconst TS_NOT_FOUND_ERR = `TypeScript must be installed in order to use the type checker. Please install it using \\`npm install typescript\\` or \\`yarn add typescript\\``;\n\n/**\n * Formats a TypeScript diagnostic message in a pretty, readable format\n * @private\n * @internal\n */\nfunction formatDiagnostic(\n ts: typeof import('typescript'),\n diagnostic: import('typescript').Diagnostic,\n cwd: string,\n): string {\n const messageText = ts.flattenDiagnosticMessageText(\n diagnostic.messageText,\n '\\n',\n );\n\n if (!diagnostic.file) {\n return `${colors.red('error')}: ${messageText}`;\n }\n\n const { line, character } = ts.getLineAndCharacterOfPosition(\n diagnostic.file,\n diagnostic.start!,\n );\n const fileName = relative(cwd, diagnostic.file.fileName);\n const position = `${line + 1}:${character + 1}`;\n const errorCode = diagnostic.code ? `TS${diagnostic.code}` : '';\n\n // Format the error message nicely\n return [\n `${colors.bold(colors.cyan(fileName))}:${colors.bold(colors.yellow(position))} - ${colors.red('error')} ${colors.gray(errorCode)}`,\n `${messageText}`,\n ].join('\\n');\n}\n\n/**\n * Performs a type check on the project using TypeScript.\n * @param path The project root or cwd\n * @private\n * @internal\n */\nexport async function performTypeCheck(path: string) {\n const tsconfigPath = join(path, 'tsconfig.json');\n\n if (!existsSync(tsconfigPath)) return;\n\n const ts = await loadTypeScript(TS_NOT_FOUND_ERR);\n\n // Format host for error reporting\n const formatHost = {\n getCanonicalFileName: (path: string) => path,\n getCurrentDirectory: ts.sys.getCurrentDirectory,\n getNewLine: () => ts.sys.newLine,\n };\n\n // Read tsconfig.json\n const configFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile);\n\n if (configFile.error) {\n console.error(\n colors.red(\n `Error reading tsconfig.json: ${ts.formatDiagnostic(configFile.error, formatHost)}`,\n ),\n );\n process.exit(1);\n }\n\n // Parse the config file\n const parsedConfig = ts.parseJsonConfigFileContent(\n configFile.config,\n ts.sys,\n path,\n );\n\n // Force noEmit to true as we only want type checking\n parsedConfig.options.noEmit = true;\n\n // Create a program\n const program = ts.createProgram({\n rootNames: parsedConfig.fileNames,\n options: parsedConfig.options,\n projectReferences: parsedConfig.projectReferences,\n });\n\n // Get the diagnostics\n const diagnostics = [\n ...program.getOptionsDiagnostics(),\n ...program.getGlobalDiagnostics(),\n ...program.getSyntacticDiagnostics(),\n ...program.getSemanticDiagnostics(),\n ];\n\n // Report any errors\n if (diagnostics.length > 0) {\n console.log('');\n console.error(\n colors.bold(\n colors.red('Type checking failed with the following errors:'),\n ),\n );\n console.log('');\n\n // Group diagnostics by file for better readability\n const byFile = new Map<string, import('typescript').Diagnostic[]>();\n\n for (const diagnostic of diagnostics) {\n const fileName = diagnostic.file ? diagnostic.file.fileName : 'Global';\n if (!byFile.has(fileName)) {\n byFile.set(fileName, []);\n }\n byFile.get(fileName)!.push(diagnostic);\n }\n\n // Pretty-print each diagnostic\n const totalErrors = diagnostics.length;\n let counter = 0;\n\n for (const [_, fileDiags] of byFile) {\n for (const diagnostic of fileDiags) {\n counter++;\n console.log(formatDiagnostic(ts, diagnostic, path));\n // Add separator between errors for better readability, except for the last one\n if (counter < totalErrors) {\n console.log('');\n }\n }\n }\n\n console.log('');\n console.error(\n colors.bold(\n colors.red(`Found ${totalErrors} error${totalErrors > 1 ? 's' : ''}`),\n ),\n );\n console.log('');\n\n process.exit(1);\n }\n\n console.log(colors.green('✓ Type checking completed successfully.'));\n}\n"],"mappings":";;;;;;;AAKA,MAAM,mBAAmB;;;;;;AAOzB,SAAS,iBACT,IACA,YACA,KACS;CACP,MAAM,cAAc,GAAG,6BACrB,WAAW,aACX;AAGF,KAAI,CAAC,WAAW,KACd,QAAO,GAAG,8BAAO,IAAI,SAAS,IAAI;CAGpC,MAAM,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"type-checker-1Iu1jJcy.js","names":[],"sources":["../src/cli/type-checker.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { loadTypeScript } from './common';\nimport { join, relative } from 'node:path';\nimport colors from '../utils/colors';\n\nconst TS_NOT_FOUND_ERR = `TypeScript must be installed in order to use the type checker. Please install it using \\`npm install typescript\\` or \\`yarn add typescript\\``;\n\n/**\n * Formats a TypeScript diagnostic message in a pretty, readable format\n * @private\n * @internal\n */\nfunction formatDiagnostic(\n ts: typeof import('typescript'),\n diagnostic: import('typescript').Diagnostic,\n cwd: string,\n): string {\n const messageText = ts.flattenDiagnosticMessageText(\n diagnostic.messageText,\n '\\n',\n );\n\n if (!diagnostic.file) {\n return `${colors.red('error')}: ${messageText}`;\n }\n\n const { line, character } = ts.getLineAndCharacterOfPosition(\n diagnostic.file,\n diagnostic.start!,\n );\n const fileName = relative(cwd, diagnostic.file.fileName);\n const position = `${line + 1}:${character + 1}`;\n const errorCode = diagnostic.code ? `TS${diagnostic.code}` : '';\n\n // Format the error message nicely\n return [\n `${colors.bold(colors.cyan(fileName))}:${colors.bold(colors.yellow(position))} - ${colors.red('error')} ${colors.gray(errorCode)}`,\n `${messageText}`,\n ].join('\\n');\n}\n\n/**\n * Performs a type check on the project using TypeScript.\n * @param path The project root or cwd\n * @private\n * @internal\n */\nexport async function performTypeCheck(path: string) {\n const tsconfigPath = join(path, 'tsconfig.json');\n\n if (!existsSync(tsconfigPath)) return;\n\n const ts = await loadTypeScript(TS_NOT_FOUND_ERR);\n\n // Format host for error reporting\n const formatHost = {\n getCanonicalFileName: (path: string) => path,\n getCurrentDirectory: ts.sys.getCurrentDirectory,\n getNewLine: () => ts.sys.newLine,\n };\n\n // Read tsconfig.json\n const configFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile);\n\n if (configFile.error) {\n console.error(\n colors.red(\n `Error reading tsconfig.json: ${ts.formatDiagnostic(configFile.error, formatHost)}`,\n ),\n );\n process.exit(1);\n }\n\n // Parse the config file\n const parsedConfig = ts.parseJsonConfigFileContent(\n configFile.config,\n ts.sys,\n path,\n );\n\n // Force noEmit to true as we only want type checking\n parsedConfig.options.noEmit = true;\n\n // Create a program\n const program = ts.createProgram({\n rootNames: parsedConfig.fileNames,\n options: parsedConfig.options,\n projectReferences: parsedConfig.projectReferences,\n });\n\n // Get the diagnostics\n const diagnostics = [\n ...program.getOptionsDiagnostics(),\n ...program.getGlobalDiagnostics(),\n ...program.getSyntacticDiagnostics(),\n ...program.getSemanticDiagnostics(),\n ];\n\n // Report any errors\n if (diagnostics.length > 0) {\n console.log('');\n console.error(\n colors.bold(\n colors.red('Type checking failed with the following errors:'),\n ),\n );\n console.log('');\n\n // Group diagnostics by file for better readability\n const byFile = new Map<string, import('typescript').Diagnostic[]>();\n\n for (const diagnostic of diagnostics) {\n const fileName = diagnostic.file ? diagnostic.file.fileName : 'Global';\n if (!byFile.has(fileName)) {\n byFile.set(fileName, []);\n }\n byFile.get(fileName)!.push(diagnostic);\n }\n\n // Pretty-print each diagnostic\n const totalErrors = diagnostics.length;\n let counter = 0;\n\n for (const [_, fileDiags] of byFile) {\n for (const diagnostic of fileDiags) {\n counter++;\n console.log(formatDiagnostic(ts, diagnostic, path));\n // Add separator between errors for better readability, except for the last one\n if (counter < totalErrors) {\n console.log('');\n }\n }\n }\n\n console.log('');\n console.error(\n colors.bold(\n colors.red(`Found ${totalErrors} error${totalErrors > 1 ? 's' : ''}`),\n ),\n );\n console.log('');\n\n process.exit(1);\n }\n\n console.log(colors.green('✓ Type checking completed successfully.'));\n}\n"],"mappings":";;;;;;;AAKA,MAAM,mBAAmB;;;;;;AAOzB,SAAS,iBACT,IACA,YACA,KACS;CACP,MAAM,cAAc,GAAG,6BACrB,WAAW,aACX;AAGF,KAAI,CAAC,WAAW,KACd,QAAO,GAAG,8BAAO,IAAI,SAAS,IAAI;CAGpC,MAAM,EAAE,MAAM,cAAc,GAAG,8BAC7B,WAAW,MACX,WAAW;CAEb,MAAM,mCAAoB,KAAK,WAAW,KAAK;CAC/C,MAAM,WAAW,GAAG,OAAO,EAAE,GAAG,YAAY;CAC5C,MAAM,YAAY,WAAW,OAAO,KAAK,WAAW,SAAS;AAG7D,QAAO,CACP,GAAG,8BAAO,KAAK,8BAAO,KAAK,WAAW,GAAG,8BAAO,KAAK,8BAAO,OAAO,WAAW,KAAK,8BAAO,IAAI,SAAS,GAAG,8BAAO,KAAK,cACtH,GAAG,eACH,KAAK;;;;;;;;AASP,eAAsB,iBAAiB,MAAc;CACnD,MAAM,mCAAoB,MAAM;AAEhC,KAAI,yBAAY,cAAe;CAE/B,MAAM,KAAK,MAAM,8BAAe;CAGhC,MAAM,aAAa;EACjB,uBAAuB,WAAiB;EACxC,qBAAqB,GAAG,IAAI;EAC5B,kBAAkB,GAAG,IAAI;;CAI3B,MAAM,aAAa,GAAG,eAAe,cAAc,GAAG,IAAI;AAE1D,KAAI,WAAW,OAAO;AACpB,UAAQ,MACN,8BAAO,IACL,gCAAgC,GAAG,iBAAiB,WAAW,OAAO;AAG1E,UAAQ,KAAK;;CAIf,MAAM,eAAe,GAAG,2BACtB,WAAW,QACX,GAAG,KACH;AAIF,cAAa,QAAQ,SAAS;CAG9B,MAAM,UAAU,GAAG,cAAc;EAC/B,WAAW,aAAa;EACxB,SAAS,aAAa;EACtB,mBAAmB,aAAa;;CAIlC,MAAM,cAAc;EACpB,GAAG,QAAQ;EACX,GAAG,QAAQ;EACX,GAAG,QAAQ;EACX,GAAG,QAAQ;;AAIX,KAAI,YAAY,SAAS,GAAG;AAC1B,UAAQ,IAAI;AACZ,UAAQ,MACN,8BAAO,KACL,8BAAO,IAAI;AAGf,UAAQ,IAAI;EAGZ,MAAM,yBAAS,IAAI;AAEnB,OAAK,MAAM,cAAc,aAAa;GACpC,MAAM,WAAW,WAAW,OAAO,WAAW,KAAK,WAAW;AAC9D,OAAI,CAAC,OAAO,IAAI,UACd,QAAO,IAAI,UAAU;AAEvB,UAAO,IAAI,UAAW,KAAK;;EAI7B,MAAM,cAAc,YAAY;EAChC,IAAI,UAAU;AAEd,OAAK,MAAM,CAAC,GAAG,cAAc,OAC3B,MAAK,MAAM,cAAc,WAAW;AAClC;AACA,WAAQ,IAAI,iBAAiB,IAAI,YAAY;AAE7C,OAAI,UAAU,YACZ,SAAQ,IAAI;;AAKlB,UAAQ,IAAI;AACZ,UAAQ,MACN,8BAAO,KACL,8BAAO,IAAI,SAAS,YAAY,QAAQ,cAAc,IAAI,MAAM;AAGpE,UAAQ,IAAI;AAEZ,UAAQ,KAAK;;AAGf,SAAQ,IAAI,8BAAO,MAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types-package-DrnIdWLg.js","names":[],"sources":["../src/utils/types-package.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { COMMANDKIT_CWD, COMMANDKIT_IS_DEV } from './constants';\nimport { existsSync } from 'node:fs';\n\n/**\n * @private\n */\nexport async function generateTypesPackage(force = false) {\n const location = join(COMMANDKIT_CWD, 'node_modules', 'commandkit-types');\n if (!COMMANDKIT_IS_DEV && !force) return location;\n const packageJSON = join(location, 'package.json');\n const index = join(location, 'index.js');\n const types = join(location, 'index.d.ts');\n const command = join(location, 'command.d.ts');\n\n const packageJSONContent = {\n name: 'commandkit-types',\n version: '1.0.0',\n description: 'CommandKit types package',\n type: 'commonjs',\n main: 'index.js',\n types: 'index.d.ts',\n };\n\n const indexContent = `module.exports = {};`;\n\n // Restructuring the type declarations to properly extend rather than replace types\n const typesContent = `// Main types index file - imports all type declarations\nimport './command';\nexport {};\n`;\n\n // Properly set up command types to extend the CommandTypeData interface\n const commandTypesContent = `// Auto-generated command types\n export {};\ndeclare module 'commandkit' {\n type CommandTypeData = string\n}`;\n\n await mkdir(location, { recursive: true }).catch(() => {});\n await writeFile(packageJSON, JSON.stringify(packageJSONContent, null, 2));\n await writeFile(index, indexContent);\n await writeFile(types, typesContent);\n await writeFile(command, commandTypesContent);\n\n return location;\n}\n\n/**\n * @private\n */\nexport async function rewriteCommandDeclaration(data: string) {\n const commandTypesContent = `// Auto-generated command types\n declare module 'commandkit' {\n ${data}\n}\n export {};\n`;\n\n const location = join(COMMANDKIT_CWD, 'node_modules', 'commandkit-types');\n\n if (!existsSync(location)) return;\n\n const type = join(location, 'command.d.ts');\n\n await writeFile(type, commandTypesContent, { encoding: 'utf-8' });\n}\n"],"mappings":";;;;;;;;;;AAQA,eAAsB,qBAAqB,QAAQ,OAAO;CACxD,MAAM,+BAAgB,kCAAgB,gBAAgB;AACtD,KAAI,CAAC,uCAAqB,CAAC,MAAO,QAAO;CACzC,MAAM,kCAAmB,UAAU;CACnC,MAAM,4BAAa,UAAU;CAC7B,MAAM,4BAAa,UAAU;CAC7B,MAAM,8BAAe,UAAU;CAE/B,MAAM,qBAAqB;EACzB,MAAM;EACN,SAAS;EACT,aAAa;EACb,MAAM;EACN,MAAM;EACN,OAAO
|
|
1
|
+
{"version":3,"file":"types-package-DrnIdWLg.js","names":[],"sources":["../src/utils/types-package.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { COMMANDKIT_CWD, COMMANDKIT_IS_DEV } from './constants';\nimport { existsSync } from 'node:fs';\n\n/**\n * @private\n */\nexport async function generateTypesPackage(force = false) {\n const location = join(COMMANDKIT_CWD, 'node_modules', 'commandkit-types');\n if (!COMMANDKIT_IS_DEV && !force) return location;\n const packageJSON = join(location, 'package.json');\n const index = join(location, 'index.js');\n const types = join(location, 'index.d.ts');\n const command = join(location, 'command.d.ts');\n\n const packageJSONContent = {\n name: 'commandkit-types',\n version: '1.0.0',\n description: 'CommandKit types package',\n type: 'commonjs',\n main: 'index.js',\n types: 'index.d.ts',\n };\n\n const indexContent = `module.exports = {};`;\n\n // Restructuring the type declarations to properly extend rather than replace types\n const typesContent = `// Main types index file - imports all type declarations\nimport './command';\nexport {};\n`;\n\n // Properly set up command types to extend the CommandTypeData interface\n const commandTypesContent = `// Auto-generated command types\n export {};\ndeclare module 'commandkit' {\n type CommandTypeData = string\n}`;\n\n await mkdir(location, { recursive: true }).catch(() => {});\n await writeFile(packageJSON, JSON.stringify(packageJSONContent, null, 2));\n await writeFile(index, indexContent);\n await writeFile(types, typesContent);\n await writeFile(command, commandTypesContent);\n\n return location;\n}\n\n/**\n * @private\n */\nexport async function rewriteCommandDeclaration(data: string) {\n const commandTypesContent = `// Auto-generated command types\n declare module 'commandkit' {\n ${data}\n}\n export {};\n`;\n\n const location = join(COMMANDKIT_CWD, 'node_modules', 'commandkit-types');\n\n if (!existsSync(location)) return;\n\n const type = join(location, 'command.d.ts');\n\n await writeFile(type, commandTypesContent, { encoding: 'utf-8' });\n}\n"],"mappings":";;;;;;;;;;AAQA,eAAsB,qBAAqB,QAAQ,OAAO;CACxD,MAAM,+BAAgB,kCAAgB,gBAAgB;AACtD,KAAI,CAAC,uCAAqB,CAAC,MAAO,QAAO;CACzC,MAAM,kCAAmB,UAAU;CACnC,MAAM,4BAAa,UAAU;CAC7B,MAAM,4BAAa,UAAU;CAC7B,MAAM,8BAAe,UAAU;CAE/B,MAAM,qBAAqB;EACzB,MAAM;EACN,SAAS;EACT,aAAa;EACb,MAAM;EACN,MAAM;EACN,OAAO;;CAGT,MAAM,eAAe;CAGrB,MAAM,eAAe;;;;CAMrB,MAAM,sBAAsB;;;;;AAM5B,mCAAY,UAAU,EAAE,WAAW,QAAQ,YAAY;AACvD,uCAAgB,aAAa,KAAK,UAAU,oBAAoB,MAAM;AACtE,uCAAgB,OAAO;AACvB,uCAAgB,OAAO;AACvB,uCAAgB,SAAS;AAEzB,QAAO;;;;;AAMT,eAAsB,0BAA0B,MAAc;CAC5D,MAAM,sBAAsB;;MAExB,KAAI;;;;CAKR,MAAM,+BAAgB,kCAAgB,gBAAgB;AAEtD,KAAI,yBAAY,UAAW;CAE3B,MAAM,2BAAY,UAAU;AAE5B,uCAAgB,MAAM,qBAAqB,EAAE,UAAU"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CommandContext, CommandData, CommandKitOptions, EventHandler } from "./analytics-engine-
|
|
1
|
+
import { CommandContext, CommandData, CommandKitOptions, CommandMetadata, CommandMetadataFunction, EventHandler, LegacyCommandMetadata } from "./analytics-engine-Iu_1uJop.js";
|
|
2
2
|
import "./EventInterceptor-x-R2qshQ.js";
|
|
3
3
|
import "./ButtonKit-C7r7RPNf.js";
|
|
4
4
|
import "./ModalKit-Btz58KYn.js";
|
|
@@ -23,10 +23,10 @@ import "./text-display-DRw3r_lK.js";
|
|
|
23
23
|
import "./index-DSBMRX9A.js";
|
|
24
24
|
import "./index-DdqikUQD.js";
|
|
25
25
|
import "./MessageCommandParser-ROLibZPX.js";
|
|
26
|
-
import "./signals-
|
|
26
|
+
import "./signals-SHg7J1U_.js";
|
|
27
27
|
import "./helpers-Wg6P5fRQ.js";
|
|
28
28
|
import "./CommandsRouter-CoOA7hkf.js";
|
|
29
29
|
import "./EventsRouter-BacqK6z3.js";
|
|
30
30
|
import "./index-DKcmsWXw.js";
|
|
31
31
|
import "./constants-MKu-Q1jh.js";
|
|
32
|
-
export { CommandContext, CommandData, CommandKitOptions, EventHandler };
|
|
32
|
+
export { CommandContext, CommandData, CommandKitOptions, CommandMetadata, CommandMetadataFunction, EventHandler, LegacyCommandMetadata };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CommandKitHMREvent, IpcMessageCommand, registerDevHooks } from "../analytics-engine-
|
|
1
|
+
import { CommandKitHMREvent, IpcMessageCommand, registerDevHooks } from "../analytics-engine-Iu_1uJop.js";
|
|
2
2
|
import "../EventInterceptor-x-R2qshQ.js";
|
|
3
3
|
import "../ButtonKit-C7r7RPNf.js";
|
|
4
4
|
import "../ModalKit-Btz58KYn.js";
|
|
@@ -23,7 +23,7 @@ import "../text-display-DRw3r_lK.js";
|
|
|
23
23
|
import "../index-DSBMRX9A.js";
|
|
24
24
|
import "../index-DdqikUQD.js";
|
|
25
25
|
import "../MessageCommandParser-ROLibZPX.js";
|
|
26
|
-
import "../signals-
|
|
26
|
+
import "../signals-SHg7J1U_.js";
|
|
27
27
|
import "../helpers-Wg6P5fRQ.js";
|
|
28
28
|
import "../CommandsRouter-CoOA7hkf.js";
|
|
29
29
|
import "../EventsRouter-BacqK6z3.js";
|
package/dist/utils/dev-hooks.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require('../colors-Cd4Oz-r-.js');
|
|
2
2
|
require('../ActionRow-CmTHbo2t.js');
|
|
3
|
-
require('../error-codes-
|
|
4
|
-
const require_commandkit = require('../commandkit-
|
|
3
|
+
require('../error-codes-Ds0bnPvT.js');
|
|
4
|
+
const require_commandkit = require('../commandkit-c0sAaK0G.js');
|
|
5
5
|
require('../common-CcfjYnPG.js');
|
|
6
6
|
require('../common-vnMIelAE.js');
|
|
7
7
|
require('../container-z621KfH5.js');
|
|
@@ -23,10 +23,10 @@ require('../runtime-5fxB4uhe.js');
|
|
|
23
23
|
require('../utils-BnXM4eKk.js');
|
|
24
24
|
require('../resolve-file-url-DHTQj2mU.js');
|
|
25
25
|
require('../types-package-DrnIdWLg.js');
|
|
26
|
-
require('../
|
|
27
|
-
require('../
|
|
28
|
-
require('../
|
|
29
|
-
require('../MessageCommandParser-
|
|
26
|
+
require('../constants-DxfYtA6t.js');
|
|
27
|
+
require('../EventWorkerContext-TZIKxVCu.js');
|
|
28
|
+
require('../signals-r7qPAvOR.js');
|
|
29
|
+
require('../MessageCommandParser-TPEPhjvx.js');
|
|
30
30
|
require('../CommandsRouter-tMAivEfv.js');
|
|
31
31
|
require('../EventsRouter-C_J0M3uO.js');
|
|
32
32
|
require('../router-DHnFRADH.js');
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { CommandKitError, CommandKitErrorCodes, createCommandKitError, isCommandKitError, isErrorType } from "../error-codes-
|
|
1
|
+
import { CommandKitError, CommandKitErrorCodes, createCommandKitError, isCommandKitError, isErrorType } from "../error-codes-B4TyW4Ct.js";
|
|
2
2
|
export { CommandKitError, CommandKitErrorCodes, createCommandKitError, isCommandKitError, isErrorType };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_error_codes = require('../error-codes-
|
|
1
|
+
const require_error_codes = require('../error-codes-Ds0bnPvT.js');
|
|
2
2
|
|
|
3
3
|
exports.CommandKitErrorCodes = require_error_codes.CommandKitErrorCodes;
|
|
4
4
|
exports.createCommandKitError = require_error_codes.createCommandKitError;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async-queue.js","names":[],"sources":["../../../src/utils/useful-stuff/async-queue.ts"],"sourcesContent":["/**\n * Async queue implementation for processing tasks sequentially or with limited concurrency.\n * Useful for rate-limiting, batching, or controlling resource usage.\n */\n\nexport interface AsyncQueueOptions {\n /** Maximum number of concurrent tasks. Default: 1 (sequential) */\n concurrency?: number;\n /** Optional callback invoked when all tasks are completed */\n onDrain?: () => void | Promise<void>;\n /** Optional AbortSignal for cancelling the queue */\n signal?: AbortSignal;\n}\n\nexport type AsyncQueueTask<T> = () => Promise<T>;\n\nexport class AsyncQueue {\n private readonly concurrency: number;\n private readonly onDrain?: () => void | Promise<void>;\n private readonly signal?: AbortSignal;\n private running = 0;\n private paused = false;\n private aborted = false;\n private queue: Array<() => void> = [];\n\n constructor(options: AsyncQueueOptions = {}) {\n this.concurrency = options.concurrency ?? 1;\n this.onDrain = options.onDrain;\n this.signal = options.signal;\n\n if (this.signal) {\n this.signal.addEventListener('abort', () => {\n this.abort();\n });\n }\n }\n\n /**\n * Adds a task to the queue.\n * @param task - The async function to execute.\n * @param signal - Optional AbortSignal for cancelling this specific task.\n * @returns Promise resolving to the result of the task.\n */\n public add<T>(task: AsyncQueueTask<T>, signal?: AbortSignal): Promise<T> {\n if (this.aborted) {\n return Promise.reject(new Error('Queue has been aborted'));\n }\n\n return new Promise<T>((resolve, reject) => {\n const run = async () => {\n if (this.paused || this.aborted) {\n if (this.aborted) {\n reject(new Error('Queue has been aborted'));\n } else {\n this.queue.push(run);\n }\n return;\n }\n\n // Check if task-specific signal is aborted\n if (signal?.aborted) {\n reject(new Error('Task was aborted'));\n return;\n }\n\n this.running++;\n try {\n const result = await task();\n resolve(result);\n } catch (err) {\n reject(err);\n } finally {\n this.running--;\n this.next();\n }\n };\n\n if (this.running < this.concurrency && !this.paused && !this.aborted) {\n run();\n } else {\n this.queue.push(run);\n }\n });\n }\n\n /**\n * Pauses the queue. No new tasks will be started until resumed.\n */\n public pause() {\n this.paused = true;\n }\n\n /**\n * Resumes the queue and processes pending tasks.\n */\n public resume() {\n if (!this.paused) return;\n this.paused = false;\n this.next();\n }\n\n /**\n * Aborts the queue, rejecting all pending tasks.\n */\n public abort() {\n this.aborted = true;\n this.clear();\n }\n\n /**\n * Clears all pending tasks from the queue.\n */\n public clear() {\n this.queue = [];\n }\n\n /**\n * Returns the number of running tasks.\n */\n public getRunning(): number {\n return this.running;\n }\n\n /**\n * Returns the number of pending tasks in the queue.\n */\n public getPending(): number {\n return this.queue.length;\n }\n\n /**\n * Returns true if the queue is currently paused.\n */\n public isPaused(): boolean {\n return this.paused;\n }\n\n /**\n * Returns true if the queue has been aborted.\n */\n public isAborted(): boolean {\n return this.aborted;\n }\n\n private next() {\n if (this.paused || this.aborted) return;\n while (this.running < this.concurrency && this.queue.length > 0) {\n const fn = this.queue.shift();\n if (fn) fn();\n }\n if (this.running === 0 && this.queue.length === 0 && this.onDrain) {\n this.onDrain();\n }\n }\n}\n\n/**\n * Creates a new async queue instance with the specified configuration.\n * @param options - Configuration options for the async queue.\n * @returns New AsyncQueue instance.\n *\n * @example\n * ```typescript\n * const controller = new AbortController();\n * const queue = createAsyncQueue({\n * concurrency: 2,\n * signal: controller.signal\n * });\n * queue.add(async () => await doSomething());\n * controller.abort(); // Aborts the queue\n * ```\n */\nexport function createAsyncQueue(options?: AsyncQueueOptions): AsyncQueue {\n return new AsyncQueue(options);\n}\n"],"mappings":";;AAgBA,IAAa,aAAb,MAAwB;CACtB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAQ,UAAU;CAClB,AAAQ,SAAS;CACjB,AAAQ,UAAU;CAClB,AAAQ,QAA2B
|
|
1
|
+
{"version":3,"file":"async-queue.js","names":[],"sources":["../../../src/utils/useful-stuff/async-queue.ts"],"sourcesContent":["/**\n * Async queue implementation for processing tasks sequentially or with limited concurrency.\n * Useful for rate-limiting, batching, or controlling resource usage.\n */\n\nexport interface AsyncQueueOptions {\n /** Maximum number of concurrent tasks. Default: 1 (sequential) */\n concurrency?: number;\n /** Optional callback invoked when all tasks are completed */\n onDrain?: () => void | Promise<void>;\n /** Optional AbortSignal for cancelling the queue */\n signal?: AbortSignal;\n}\n\nexport type AsyncQueueTask<T> = () => Promise<T>;\n\nexport class AsyncQueue {\n private readonly concurrency: number;\n private readonly onDrain?: () => void | Promise<void>;\n private readonly signal?: AbortSignal;\n private running = 0;\n private paused = false;\n private aborted = false;\n private queue: Array<() => void> = [];\n\n constructor(options: AsyncQueueOptions = {}) {\n this.concurrency = options.concurrency ?? 1;\n this.onDrain = options.onDrain;\n this.signal = options.signal;\n\n if (this.signal) {\n this.signal.addEventListener('abort', () => {\n this.abort();\n });\n }\n }\n\n /**\n * Adds a task to the queue.\n * @param task - The async function to execute.\n * @param signal - Optional AbortSignal for cancelling this specific task.\n * @returns Promise resolving to the result of the task.\n */\n public add<T>(task: AsyncQueueTask<T>, signal?: AbortSignal): Promise<T> {\n if (this.aborted) {\n return Promise.reject(new Error('Queue has been aborted'));\n }\n\n return new Promise<T>((resolve, reject) => {\n const run = async () => {\n if (this.paused || this.aborted) {\n if (this.aborted) {\n reject(new Error('Queue has been aborted'));\n } else {\n this.queue.push(run);\n }\n return;\n }\n\n // Check if task-specific signal is aborted\n if (signal?.aborted) {\n reject(new Error('Task was aborted'));\n return;\n }\n\n this.running++;\n try {\n const result = await task();\n resolve(result);\n } catch (err) {\n reject(err);\n } finally {\n this.running--;\n this.next();\n }\n };\n\n if (this.running < this.concurrency && !this.paused && !this.aborted) {\n run();\n } else {\n this.queue.push(run);\n }\n });\n }\n\n /**\n * Pauses the queue. No new tasks will be started until resumed.\n */\n public pause() {\n this.paused = true;\n }\n\n /**\n * Resumes the queue and processes pending tasks.\n */\n public resume() {\n if (!this.paused) return;\n this.paused = false;\n this.next();\n }\n\n /**\n * Aborts the queue, rejecting all pending tasks.\n */\n public abort() {\n this.aborted = true;\n this.clear();\n }\n\n /**\n * Clears all pending tasks from the queue.\n */\n public clear() {\n this.queue = [];\n }\n\n /**\n * Returns the number of running tasks.\n */\n public getRunning(): number {\n return this.running;\n }\n\n /**\n * Returns the number of pending tasks in the queue.\n */\n public getPending(): number {\n return this.queue.length;\n }\n\n /**\n * Returns true if the queue is currently paused.\n */\n public isPaused(): boolean {\n return this.paused;\n }\n\n /**\n * Returns true if the queue has been aborted.\n */\n public isAborted(): boolean {\n return this.aborted;\n }\n\n private next() {\n if (this.paused || this.aborted) return;\n while (this.running < this.concurrency && this.queue.length > 0) {\n const fn = this.queue.shift();\n if (fn) fn();\n }\n if (this.running === 0 && this.queue.length === 0 && this.onDrain) {\n this.onDrain();\n }\n }\n}\n\n/**\n * Creates a new async queue instance with the specified configuration.\n * @param options - Configuration options for the async queue.\n * @returns New AsyncQueue instance.\n *\n * @example\n * ```typescript\n * const controller = new AbortController();\n * const queue = createAsyncQueue({\n * concurrency: 2,\n * signal: controller.signal\n * });\n * queue.add(async () => await doSomething());\n * controller.abort(); // Aborts the queue\n * ```\n */\nexport function createAsyncQueue(options?: AsyncQueueOptions): AsyncQueue {\n return new AsyncQueue(options);\n}\n"],"mappings":";;AAgBA,IAAa,aAAb,MAAwB;CACtB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAQ,UAAU;CAClB,AAAQ,SAAS;CACjB,AAAQ,UAAU;CAClB,AAAQ,QAA2B;CAEnC,YAAY,UAA6B,IAAI;AAC3C,OAAK,cAAc,QAAQ,eAAe;AAC1C,OAAK,UAAU,QAAQ;AACvB,OAAK,SAAS,QAAQ;AAEtB,MAAI,KAAK,OACP,MAAK,OAAO,iBAAiB,eAAe;AAC1C,QAAK;;;;;;;;;CAWX,AAAO,IAAO,MAAyB,QAAkC;AACvE,MAAI,KAAK,QACP,QAAO,QAAQ,uBAAO,IAAI,MAAM;AAGlC,SAAO,IAAI,SAAY,SAAS,WAAW;GACzC,MAAM,MAAM,YAAY;AACtB,QAAI,KAAK,UAAU,KAAK,SAAS;AAC/B,SAAI,KAAK,QACP,wBAAO,IAAI,MAAM;SAEjB,MAAK,MAAM,KAAK;AAElB;;AAIF,wDAAI,OAAQ,SAAS;AACnB,4BAAO,IAAI,MAAM;AACjB;;AAGF,SAAK;AACL,QAAI;KACF,MAAM,SAAS,MAAM;AACrB,aAAQ;aACD,KAAK;AACZ,YAAO;cACC;AACR,UAAK;AACL,UAAK;;;AAIT,OAAI,KAAK,UAAU,KAAK,eAAe,CAAC,KAAK,UAAU,CAAC,KAAK,QAC3D;OAEA,MAAK,MAAM,KAAK;;;;;;CAQtB,AAAO,QAAQ;AACb,OAAK,SAAS;;;;;CAMhB,AAAO,SAAS;AACd,MAAI,CAAC,KAAK,OAAQ;AAClB,OAAK,SAAS;AACd,OAAK;;;;;CAMP,AAAO,QAAQ;AACb,OAAK,UAAU;AACf,OAAK;;;;;CAMP,AAAO,QAAQ;AACb,OAAK,QAAQ;;;;;CAMf,AAAO,aAAqB;AAC1B,SAAO,KAAK;;;;;CAMd,AAAO,aAAqB;AAC1B,SAAO,KAAK,MAAM;;;;;CAMpB,AAAO,WAAoB;AACzB,SAAO,KAAK;;;;;CAMd,AAAO,YAAqB;AAC1B,SAAO,KAAK;;CAGd,AAAQ,OAAO;AACb,MAAI,KAAK,UAAU,KAAK,QAAS;AACjC,SAAO,KAAK,UAAU,KAAK,eAAe,KAAK,MAAM,SAAS,GAAG;GAC/D,MAAM,KAAK,KAAK,MAAM;AACtB,OAAI,GAAI;;AAEV,MAAI,KAAK,YAAY,KAAK,KAAK,MAAM,WAAW,KAAK,KAAK,QACxD,MAAK;;;;;;;;;;;;;;;;;;;AAqBX,SAAgB,iBAAiB,SAAyC;AACxE,QAAO,IAAI,WAAW"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutex.js","names":[],"sources":["../../../src/utils/useful-stuff/mutex.ts"],"sourcesContent":["/**\n * Async mutex implementation for coordinating access to shared resources.\n * Provides mutual exclusion to ensure only one task can access a resource at a time.\n */\n\n/**\n * Interface for mutex storage implementations.\n * Provides methods to store, retrieve, and delete mutex lock data.\n */\nexport interface MutexStorage {\n /**\n * Attempts to acquire a lock for a given key\n * @param key - The unique identifier for the mutex lock\n * @param timeout - Optional timeout in milliseconds for the lock\n * @param signal - Optional AbortSignal for cancelling the acquisition\n * @returns Promise resolving to true if lock was acquired, false if timeout or already locked\n */\n acquire(\n key: string,\n timeout?: number,\n signal?: AbortSignal,\n ): Promise<boolean>;\n\n /**\n * Releases the lock for a given key\n * @param key - The unique identifier for the mutex lock\n * @returns Promise that resolves when the lock is released\n */\n release(key: string): Promise<void>;\n\n /**\n * Checks if a lock is currently held for a given key\n * @param key - The unique identifier for the mutex lock\n * @returns Promise resolving to true if the lock is held, false otherwise\n */\n isLocked(key: string): Promise<boolean>;\n}\n\n/**\n * Configuration options for mutex\n */\nexport interface MutexOptions {\n /** Default timeout in milliseconds for lock acquisition. Default: 30000 */\n timeout?: number;\n /** Storage implementation for persisting mutex data. Default: {@link MemoryMutexStorage} */\n storage?: MutexStorage;\n}\n\n/**\n * In-memory storage implementation for mutex locks.\n * Suitable for single-instance applications.\n */\nexport class MemoryMutexStorage implements MutexStorage {\n private readonly locks = new Map<\n string,\n { holder: string; acquiredAt: number }\n >();\n\n /**\n * Attempts to acquire a lock for a given key\n * @param key - The unique identifier for the mutex lock\n * @param timeout - Optional timeout in milliseconds for the lock\n * @param signal - Optional AbortSignal for cancelling the acquisition\n * @returns Promise resolving to true if lock was acquired, false if timeout or already locked\n */\n async acquire(\n key: string,\n timeout: number = 30000,\n signal?: AbortSignal,\n ): Promise<boolean> {\n const holder = this.generateHolderId();\n const startTime = Date.now();\n\n while (Date.now() - startTime < timeout) {\n // Check if aborted\n if (signal?.aborted) {\n throw new Error('Lock acquisition was aborted');\n }\n\n if (!this.locks.has(key)) {\n this.locks.set(key, { holder, acquiredAt: Date.now() });\n return true;\n }\n await this.delay(10);\n }\n\n return false;\n }\n\n /**\n * Releases the lock for a given key\n * @param key - The unique identifier for the mutex lock\n * @returns Promise that resolves when the lock is released\n */\n async release(key: string): Promise<void> {\n this.locks.delete(key);\n }\n\n /**\n * Checks if a lock is currently held for a given key\n * @param key - The unique identifier for the mutex lock\n * @returns Promise resolving to true if the lock is held, false otherwise\n */\n async isLocked(key: string): Promise<boolean> {\n return this.locks.has(key);\n }\n\n private generateHolderId(): string {\n return `holder_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n }\n\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n\n/**\n * Async mutex implementation that provides mutual exclusion for shared resources.\n * Ensures only one task can access a protected resource at a time.\n */\nexport class Mutex {\n private storage: MutexStorage;\n private readonly defaultTimeout: number;\n\n /**\n * Creates a new mutex instance\n * @param options - Configuration options for the mutex\n */\n public constructor(options: MutexOptions = {}) {\n this.storage = options.storage || new MemoryMutexStorage();\n this.defaultTimeout = options.timeout || 30000;\n }\n\n /**\n * Sets the storage implementation for the mutex\n * @param storage - The storage implementation to use\n */\n public setStorage(storage: MutexStorage) {\n this.storage = storage;\n }\n\n /**\n * Gets the storage implementation for the mutex\n * @returns The storage implementation\n */\n public getStorage(): MutexStorage {\n return this.storage;\n }\n\n /**\n * Acquires a lock for the given key\n * @param key - The unique identifier for the mutex lock\n * @param timeout - Optional timeout in milliseconds for lock acquisition\n * @param signal - Optional AbortSignal for cancelling the acquisition\n * @returns Promise resolving to true if lock was acquired, false if timeout\n */\n public async acquire(\n key: string,\n timeout?: number,\n signal?: AbortSignal,\n ): Promise<boolean> {\n return this.storage.acquire(key, timeout || this.defaultTimeout, signal);\n }\n\n /**\n * Releases the lock for the given key\n * @param key - The unique identifier for the mutex lock\n * @returns Promise that resolves when the lock is released\n */\n public async release(key: string): Promise<void> {\n return this.storage.release(key);\n }\n\n /**\n * Checks if a lock is currently held for the given key\n * @param key - The unique identifier for the mutex lock\n * @returns Promise resolving to true if the lock is held, false otherwise\n */\n public async isLocked(key: string): Promise<boolean> {\n return this.storage.isLocked(key);\n }\n\n /**\n * Executes a function with exclusive access to the resource\n * @param key - The unique identifier for the mutex lock\n * @param fn - The function to execute with exclusive access\n * @param timeout - Optional timeout in milliseconds for lock acquisition\n * @param signal - Optional AbortSignal for cancelling the lock acquisition\n * @returns Promise resolving to the result of the function execution\n * @throws Error if lock acquisition fails or function execution fails\n */\n public async withLock<T>(\n key: string,\n fn: () => Promise<T> | T,\n timeout?: number,\n signal?: AbortSignal,\n ): Promise<T> {\n const acquired = await this.acquire(key, timeout, signal);\n if (!acquired) {\n throw new Error(`Failed to acquire lock for key: ${key}`);\n }\n\n try {\n return await fn();\n } finally {\n await this.release(key);\n }\n }\n\n /**\n * Gets the current configuration of the mutex\n * @returns Object containing the current timeout value\n */\n public getConfig(): Omit<MutexOptions, 'storage'> {\n return {\n timeout: this.defaultTimeout,\n };\n }\n}\n\n/**\n * Default mutex instance for global use\n */\nexport const defaultMutex = new Mutex();\n\n/**\n * Convenience function to execute a function with exclusive access using the default mutex.\n *\n * @param key - The unique identifier for the mutex lock\n * @param fn - The function to execute with exclusive access\n * @param timeout - Optional timeout in milliseconds for lock acquisition\n * @param signal - Optional AbortSignal for cancelling the lock acquisition\n * @returns Promise resolving to the result of the function execution\n *\n * @example\n * ```typescript\n * const controller = new AbortController();\n * const result = await withMutex('shared-resource', async () => {\n * // This code runs with exclusive access\n * return await updateSharedResource();\n * }, 30000, controller.signal);\n * controller.abort(); // Cancels the lock acquisition\n * ```\n */\nexport async function withMutex<T>(\n key: string,\n fn: () => Promise<T> | T,\n timeout?: number,\n signal?: AbortSignal,\n): Promise<T> {\n return defaultMutex.withLock(key, fn, timeout, signal);\n}\n\n/**\n * Creates a new mutex instance with the specified configuration\n * @param options - Configuration options for the mutex\n * @returns New Mutex instance\n *\n * @example\n * ```typescript\n * const mutex = createMutex({\n * timeout: 60000,\n * storage: new RedisMutexStorage()\n * });\n * ```\n */\nexport function createMutex(options: MutexOptions): Mutex {\n return new Mutex(options);\n}\n\n/**\n * Acquires a lock using the default mutex\n * @param key - The unique identifier for the mutex lock\n * @param timeout - Optional timeout in milliseconds for lock acquisition\n * @param signal - Optional AbortSignal for cancelling the acquisition\n * @returns Promise resolving to true if lock was acquired, false if timeout\n */\nexport async function acquireLock(\n key: string,\n timeout?: number,\n signal?: AbortSignal,\n): Promise<boolean> {\n return defaultMutex.acquire(key, timeout, signal);\n}\n\n/**\n * Releases a lock using the default mutex\n * @param key - The unique identifier for the mutex lock\n * @returns Promise that resolves when the lock is released\n */\nexport async function releaseLock(key: string): Promise<void> {\n return defaultMutex.release(key);\n}\n\n/**\n * Checks if a lock is held using the default mutex\n * @param key - The unique identifier for the mutex lock\n * @returns Promise resolving to true if the lock is held, false otherwise\n */\nexport async function isLocked(key: string): Promise<boolean> {\n return defaultMutex.isLocked(key);\n}\n"],"mappings":";;;;;;AAoDA,IAAa,qBAAb,MAAwD;CACtD,AAAiB,wBAAQ,IAAI;;;;;;;;CAY7B,MAAM,QACN,KACA,UAAkB,KAClB,QACmB;EACjB,MAAM,SAAS,KAAK;EACpB,MAAM,YAAY,KAAK;AAEvB,SAAO,KAAK,QAAQ,YAAY,SAAS;AAEvC,uDAAI,OAAQ,QACV,OAAM,IAAI,MAAM;AAGlB,OAAI,CAAC,KAAK,MAAM,IAAI,MAAM;AACxB,SAAK,MAAM,IAAI,KAAK;KAAE;KAAQ,YAAY,KAAK;KAAO;AACtD,WAAO;GACT;AACA,SAAM,KAAK,MAAM;EACnB;AAEA,SAAO;CACT;;;;;;CAOA,MAAM,QAAQ,KAA4B;AACxC,OAAK,MAAM,OAAO;CACpB;;;;;;CAOA,MAAM,SAAS,KAA+B;AAC5C,SAAO,KAAK,MAAM,IAAI;CACxB;CAEA,AAAQ,mBAA2B;AACjC,SAAO,UAAU,KAAK,MAAM,GAAG,KAAK,SAAS,SAAS,IAAI,OAAO,GAAG;CACtE;CAEA,AAAQ,MAAM,IAA2B;AACvC,SAAO,IAAI,SAAS,YAAY,WAAW,SAAS;CACtD;AACF;;;;;AAMA,IAAa,QAAb,MAAmB;CACjB,AAAQ;CACR,AAAiB;;;;;CAMjB,AAAO,YAAY,UAAwB,EAAE,EAAE;AAC7C,OAAK,UAAU,QAAQ,WAAW,IAAI;AACtC,OAAK,iBAAiB,QAAQ,WAAW;CAC3C;;;;;CAMA,AAAO,WAAW,SAAuB;AACvC,OAAK,UAAU;CACjB;;;;;CAMA,AAAO,aAA2B;AAChC,SAAO,KAAK;CACd;;;;;;;;CASA,MAAa,QACb,KACA,SACA,QACmB;AACjB,SAAO,KAAK,QAAQ,QAAQ,KAAK,WAAW,KAAK,gBAAgB;CACnE;;;;;;CAOA,MAAa,QAAQ,KAA4B;AAC/C,SAAO,KAAK,QAAQ,QAAQ;CAC9B;;;;;;CAOA,MAAa,SAAS,KAA+B;AACnD,SAAO,KAAK,QAAQ,SAAS;CAC/B;;;;;;;;;;CAWA,MAAa,SACb,KACA,IACA,SACA,QACa;EACX,MAAM,WAAW,MAAM,KAAK,QAAQ,KAAK,SAAS;AAClD,MAAI,CAAC,SACH,OAAM,IAAI,MAAM,mCAAmC;AAGrD,MAAI;AACF,UAAO,MAAM;EACd,UAAS;AACR,SAAM,KAAK,QAAQ;EACrB;CACF;;;;;CAMA,AAAO,YAA2C;AAChD,SAAO,EACL,SAAS,KAAK,gBACf;CACH;AACF;;;;AAKA,MAAa,eAAe,IAAI;;;;;;;;;;;;;;;;;;;;AAqBhC,eAAsB,UACtB,KACA,IACA,SACA,QACa;AACX,QAAO,aAAa,SAAS,KAAK,IAAI,SAAS;AACjD;;;;;;;;;;;;;;AAeA,SAAgB,YAAY,SAA8B;AACxD,QAAO,IAAI,MAAM;AACnB;;;;;;;;AASA,eAAsB,YACtB,KACA,SACA,QACmB;AACjB,QAAO,aAAa,QAAQ,KAAK,SAAS;AAC5C;;;;;;AAOA,eAAsB,YAAY,KAA4B;AAC5D,QAAO,aAAa,QAAQ;AAC9B;;;;;;AAOA,eAAsB,SAAS,KAA+B;AAC5D,QAAO,aAAa,SAAS;AAC/B"}
|
|
1
|
+
{"version":3,"file":"mutex.js","names":[],"sources":["../../../src/utils/useful-stuff/mutex.ts"],"sourcesContent":["/**\n * Async mutex implementation for coordinating access to shared resources.\n * Provides mutual exclusion to ensure only one task can access a resource at a time.\n */\n\n/**\n * Interface for mutex storage implementations.\n * Provides methods to store, retrieve, and delete mutex lock data.\n */\nexport interface MutexStorage {\n /**\n * Attempts to acquire a lock for a given key\n * @param key - The unique identifier for the mutex lock\n * @param timeout - Optional timeout in milliseconds for the lock\n * @param signal - Optional AbortSignal for cancelling the acquisition\n * @returns Promise resolving to true if lock was acquired, false if timeout or already locked\n */\n acquire(\n key: string,\n timeout?: number,\n signal?: AbortSignal,\n ): Promise<boolean>;\n\n /**\n * Releases the lock for a given key\n * @param key - The unique identifier for the mutex lock\n * @returns Promise that resolves when the lock is released\n */\n release(key: string): Promise<void>;\n\n /**\n * Checks if a lock is currently held for a given key\n * @param key - The unique identifier for the mutex lock\n * @returns Promise resolving to true if the lock is held, false otherwise\n */\n isLocked(key: string): Promise<boolean>;\n}\n\n/**\n * Configuration options for mutex\n */\nexport interface MutexOptions {\n /** Default timeout in milliseconds for lock acquisition. Default: 30000 */\n timeout?: number;\n /** Storage implementation for persisting mutex data. Default: {@link MemoryMutexStorage} */\n storage?: MutexStorage;\n}\n\n/**\n * In-memory storage implementation for mutex locks.\n * Suitable for single-instance applications.\n */\nexport class MemoryMutexStorage implements MutexStorage {\n private readonly locks = new Map<\n string,\n { holder: string; acquiredAt: number }\n >();\n\n /**\n * Attempts to acquire a lock for a given key\n * @param key - The unique identifier for the mutex lock\n * @param timeout - Optional timeout in milliseconds for the lock\n * @param signal - Optional AbortSignal for cancelling the acquisition\n * @returns Promise resolving to true if lock was acquired, false if timeout or already locked\n */\n async acquire(\n key: string,\n timeout: number = 30000,\n signal?: AbortSignal,\n ): Promise<boolean> {\n const holder = this.generateHolderId();\n const startTime = Date.now();\n\n while (Date.now() - startTime < timeout) {\n // Check if aborted\n if (signal?.aborted) {\n throw new Error('Lock acquisition was aborted');\n }\n\n if (!this.locks.has(key)) {\n this.locks.set(key, { holder, acquiredAt: Date.now() });\n return true;\n }\n await this.delay(10);\n }\n\n return false;\n }\n\n /**\n * Releases the lock for a given key\n * @param key - The unique identifier for the mutex lock\n * @returns Promise that resolves when the lock is released\n */\n async release(key: string): Promise<void> {\n this.locks.delete(key);\n }\n\n /**\n * Checks if a lock is currently held for a given key\n * @param key - The unique identifier for the mutex lock\n * @returns Promise resolving to true if the lock is held, false otherwise\n */\n async isLocked(key: string): Promise<boolean> {\n return this.locks.has(key);\n }\n\n private generateHolderId(): string {\n return `holder_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n }\n\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n\n/**\n * Async mutex implementation that provides mutual exclusion for shared resources.\n * Ensures only one task can access a protected resource at a time.\n */\nexport class Mutex {\n private storage: MutexStorage;\n private readonly defaultTimeout: number;\n\n /**\n * Creates a new mutex instance\n * @param options - Configuration options for the mutex\n */\n public constructor(options: MutexOptions = {}) {\n this.storage = options.storage || new MemoryMutexStorage();\n this.defaultTimeout = options.timeout || 30000;\n }\n\n /**\n * Sets the storage implementation for the mutex\n * @param storage - The storage implementation to use\n */\n public setStorage(storage: MutexStorage) {\n this.storage = storage;\n }\n\n /**\n * Gets the storage implementation for the mutex\n * @returns The storage implementation\n */\n public getStorage(): MutexStorage {\n return this.storage;\n }\n\n /**\n * Acquires a lock for the given key\n * @param key - The unique identifier for the mutex lock\n * @param timeout - Optional timeout in milliseconds for lock acquisition\n * @param signal - Optional AbortSignal for cancelling the acquisition\n * @returns Promise resolving to true if lock was acquired, false if timeout\n */\n public async acquire(\n key: string,\n timeout?: number,\n signal?: AbortSignal,\n ): Promise<boolean> {\n return this.storage.acquire(key, timeout || this.defaultTimeout, signal);\n }\n\n /**\n * Releases the lock for the given key\n * @param key - The unique identifier for the mutex lock\n * @returns Promise that resolves when the lock is released\n */\n public async release(key: string): Promise<void> {\n return this.storage.release(key);\n }\n\n /**\n * Checks if a lock is currently held for the given key\n * @param key - The unique identifier for the mutex lock\n * @returns Promise resolving to true if the lock is held, false otherwise\n */\n public async isLocked(key: string): Promise<boolean> {\n return this.storage.isLocked(key);\n }\n\n /**\n * Executes a function with exclusive access to the resource\n * @param key - The unique identifier for the mutex lock\n * @param fn - The function to execute with exclusive access\n * @param timeout - Optional timeout in milliseconds for lock acquisition\n * @param signal - Optional AbortSignal for cancelling the lock acquisition\n * @returns Promise resolving to the result of the function execution\n * @throws Error if lock acquisition fails or function execution fails\n */\n public async withLock<T>(\n key: string,\n fn: () => Promise<T> | T,\n timeout?: number,\n signal?: AbortSignal,\n ): Promise<T> {\n const acquired = await this.acquire(key, timeout, signal);\n if (!acquired) {\n throw new Error(`Failed to acquire lock for key: ${key}`);\n }\n\n try {\n return await fn();\n } finally {\n await this.release(key);\n }\n }\n\n /**\n * Gets the current configuration of the mutex\n * @returns Object containing the current timeout value\n */\n public getConfig(): Omit<MutexOptions, 'storage'> {\n return {\n timeout: this.defaultTimeout,\n };\n }\n}\n\n/**\n * Default mutex instance for global use\n */\nexport const defaultMutex = new Mutex();\n\n/**\n * Convenience function to execute a function with exclusive access using the default mutex.\n *\n * @param key - The unique identifier for the mutex lock\n * @param fn - The function to execute with exclusive access\n * @param timeout - Optional timeout in milliseconds for lock acquisition\n * @param signal - Optional AbortSignal for cancelling the lock acquisition\n * @returns Promise resolving to the result of the function execution\n *\n * @example\n * ```typescript\n * const controller = new AbortController();\n * const result = await withMutex('shared-resource', async () => {\n * // This code runs with exclusive access\n * return await updateSharedResource();\n * }, 30000, controller.signal);\n * controller.abort(); // Cancels the lock acquisition\n * ```\n */\nexport async function withMutex<T>(\n key: string,\n fn: () => Promise<T> | T,\n timeout?: number,\n signal?: AbortSignal,\n): Promise<T> {\n return defaultMutex.withLock(key, fn, timeout, signal);\n}\n\n/**\n * Creates a new mutex instance with the specified configuration\n * @param options - Configuration options for the mutex\n * @returns New Mutex instance\n *\n * @example\n * ```typescript\n * const mutex = createMutex({\n * timeout: 60000,\n * storage: new RedisMutexStorage()\n * });\n * ```\n */\nexport function createMutex(options: MutexOptions): Mutex {\n return new Mutex(options);\n}\n\n/**\n * Acquires a lock using the default mutex\n * @param key - The unique identifier for the mutex lock\n * @param timeout - Optional timeout in milliseconds for lock acquisition\n * @param signal - Optional AbortSignal for cancelling the acquisition\n * @returns Promise resolving to true if lock was acquired, false if timeout\n */\nexport async function acquireLock(\n key: string,\n timeout?: number,\n signal?: AbortSignal,\n): Promise<boolean> {\n return defaultMutex.acquire(key, timeout, signal);\n}\n\n/**\n * Releases a lock using the default mutex\n * @param key - The unique identifier for the mutex lock\n * @returns Promise that resolves when the lock is released\n */\nexport async function releaseLock(key: string): Promise<void> {\n return defaultMutex.release(key);\n}\n\n/**\n * Checks if a lock is held using the default mutex\n * @param key - The unique identifier for the mutex lock\n * @returns Promise resolving to true if the lock is held, false otherwise\n */\nexport async function isLocked(key: string): Promise<boolean> {\n return defaultMutex.isLocked(key);\n}\n"],"mappings":";;;;;;AAoDA,IAAa,qBAAb,MAAwD;CACtD,AAAiB,wBAAQ,IAAI;;;;;;;;CAY7B,MAAM,QACN,KACA,UAAkB,KAClB,QACmB;EACjB,MAAM,SAAS,KAAK;EACpB,MAAM,YAAY,KAAK;AAEvB,SAAO,KAAK,QAAQ,YAAY,SAAS;AAEvC,uDAAI,OAAQ,QACV,OAAM,IAAI,MAAM;AAGlB,OAAI,CAAC,KAAK,MAAM,IAAI,MAAM;AACxB,SAAK,MAAM,IAAI,KAAK;KAAE;KAAQ,YAAY,KAAK;;AAC/C,WAAO;;AAET,SAAM,KAAK,MAAM;;AAGnB,SAAO;;;;;;;CAQT,MAAM,QAAQ,KAA4B;AACxC,OAAK,MAAM,OAAO;;;;;;;CAQpB,MAAM,SAAS,KAA+B;AAC5C,SAAO,KAAK,MAAM,IAAI;;CAGxB,AAAQ,mBAA2B;AACjC,SAAO,UAAU,KAAK,MAAM,GAAG,KAAK,SAAS,SAAS,IAAI,OAAO,GAAG;;CAGtE,AAAQ,MAAM,IAA2B;AACvC,SAAO,IAAI,SAAS,YAAY,WAAW,SAAS;;;;;;;AAQxD,IAAa,QAAb,MAAmB;CACjB,AAAQ;CACR,AAAiB;;;;;CAMjB,AAAO,YAAY,UAAwB,IAAI;AAC7C,OAAK,UAAU,QAAQ,WAAW,IAAI;AACtC,OAAK,iBAAiB,QAAQ,WAAW;;;;;;CAO3C,AAAO,WAAW,SAAuB;AACvC,OAAK,UAAU;;;;;;CAOjB,AAAO,aAA2B;AAChC,SAAO,KAAK;;;;;;;;;CAUd,MAAa,QACb,KACA,SACA,QACmB;AACjB,SAAO,KAAK,QAAQ,QAAQ,KAAK,WAAW,KAAK,gBAAgB;;;;;;;CAQnE,MAAa,QAAQ,KAA4B;AAC/C,SAAO,KAAK,QAAQ,QAAQ;;;;;;;CAQ9B,MAAa,SAAS,KAA+B;AACnD,SAAO,KAAK,QAAQ,SAAS;;;;;;;;;;;CAY/B,MAAa,SACb,KACA,IACA,SACA,QACa;EACX,MAAM,WAAW,MAAM,KAAK,QAAQ,KAAK,SAAS;AAClD,MAAI,CAAC,SACH,OAAM,IAAI,MAAM,mCAAmC;AAGrD,MAAI;AACF,UAAO,MAAM;YACL;AACR,SAAM,KAAK,QAAQ;;;;;;;CAQvB,AAAO,YAA2C;AAChD,SAAO,EACL,SAAS,KAAK;;;;;;AAQpB,MAAa,eAAe,IAAI;;;;;;;;;;;;;;;;;;;;AAqBhC,eAAsB,UACtB,KACA,IACA,SACA,QACa;AACX,QAAO,aAAa,SAAS,KAAK,IAAI,SAAS;;;;;;;;;;;;;;;AAgBjD,SAAgB,YAAY,SAA8B;AACxD,QAAO,IAAI,MAAM;;;;;;;;;AAUnB,eAAsB,YACtB,KACA,SACA,QACmB;AACjB,QAAO,aAAa,QAAQ,KAAK,SAAS;;;;;;;AAQ5C,eAAsB,YAAY,KAA4B;AAC5D,QAAO,aAAa,QAAQ;;;;;;;AAQ9B,eAAsB,SAAS,KAA+B;AAC5D,QAAO,aAAa,SAAS"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ratelimiter.js","names":[],"sources":["../../../src/utils/useful-stuff/ratelimiter.ts"],"sourcesContent":["/**\n * Default timeout interval for rate limiting in milliseconds\n */\nexport const DEFAULT_TIMEOUT = 60000;\n\n/**\n * Default maximum number of requests allowed per interval\n */\nexport const DEFAULT_MAX_REQUESTS = 10;\n\n/**\n * Interface for rate limit storage implementations.\n * Provides methods to store, retrieve, and delete rate limit data.\n */\nexport interface RateLimitStorage {\n /**\n * Retrieves the current request count for a given key\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise resolving to the current request count\n */\n get(key: string): Promise<number>;\n\n /**\n * Sets the request count for a given key\n * @param key - The unique identifier for the rate limit entry\n * @param value - The request count to store\n * @returns Promise that resolves when the value is stored\n */\n set(key: string, value: number): Promise<void>;\n\n /**\n * Deletes the rate limit entry for a given key\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise that resolves when the entry is deleted\n */\n delete(key: string): Promise<void>;\n}\n\n/**\n * Configuration options for rate limiting\n */\nexport interface RateLimitOptions {\n /** Maximum number of requests allowed per interval. Default: 10 */\n maxRequests?: number;\n /** Time interval in milliseconds for the rate limit window. Default: 60000 */\n interval?: number;\n /** Storage implementation for persisting rate limit data. Default: {@link MemoryRateLimitStorage} */\n storage?: RateLimitStorage;\n}\n\n/**\n * In-memory storage implementation for rate limiting.\n * Suitable for single-instance applications.\n */\nexport class MemoryRateLimitStorage implements RateLimitStorage {\n private readonly store = new Map<string, number>();\n\n /**\n * Retrieves the current request count for a given key\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise resolving to the current request count or 0 if not found\n */\n async get(key: string): Promise<number> {\n return this.store.get(key) || 0;\n }\n\n /**\n * Sets the request count for a given key\n * @param key - The unique identifier for the rate limit entry\n * @param value - The request count to store\n * @returns Promise that resolves immediately\n */\n async set(key: string, value: number): Promise<void> {\n this.store.set(key, value);\n }\n\n /**\n * Deletes the rate limit entry for a given key\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise that resolves immediately\n */\n async delete(key: string): Promise<void> {\n this.store.delete(key);\n }\n}\n\n/**\n * Rate limiter implementation that enforces request limits per key.\n * Supports configurable request limits and time intervals.\n */\nexport class RateLimiter {\n private readonly limits: Map<string, number> = new Map();\n private readonly lastReset: Map<string, number> = new Map();\n private readonly resetInterval: number;\n\n /**\n * Creates a new rate limiter instance\n * @param maxRequests - Maximum number of requests allowed per interval (default: 10)\n * @param interval - Time interval in milliseconds for the rate limit window (default: 60000)\n * @param storage - Optional storage implementation (default: MemoryRateLimitStorage)\n */\n public constructor(\n private readonly maxRequests: number = DEFAULT_MAX_REQUESTS,\n private readonly interval: number = DEFAULT_TIMEOUT,\n private storage: RateLimitStorage = new MemoryRateLimitStorage(),\n ) {\n this.resetInterval = interval;\n }\n\n /**\n * Sets the storage implementation for the rate limiter\n * @param storage - The storage implementation to use\n */\n public setStorage(storage: RateLimitStorage) {\n this.storage = storage;\n }\n\n /**\n * Gets the storage implementation for the rate limiter\n * @returns The storage implementation\n */\n public getStorage(): RateLimitStorage {\n return this.storage;\n }\n\n /**\n * Checks if a request is allowed for the given key and increments the counter if allowed\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise resolving to true if the request is allowed, false if rate limited\n */\n public async limit(key: string): Promise<boolean> {\n const now = Date.now();\n const lastReset = this.lastReset.get(key) || 0;\n const timeSinceReset = now - lastReset;\n\n // Reset counter if interval has passed\n if (timeSinceReset > this.resetInterval) {\n await this.storage.delete(key);\n this.lastReset.set(key, now);\n this.limits.set(key, 0);\n }\n\n // Get current count from storage\n const currentCount = await this.storage.get(key);\n\n // Check if limit exceeded\n if (currentCount >= this.maxRequests) {\n return false;\n }\n\n // Increment counter\n const newCount = currentCount + 1;\n await this.storage.set(key, newCount);\n this.limits.set(key, newCount);\n\n return true;\n }\n\n /**\n * Gets the remaining requests allowed for a given key\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise resolving to the number of remaining requests\n */\n public async getRemaining(key: string): Promise<number> {\n const currentCount = await this.storage.get(key);\n return Math.max(0, this.maxRequests - currentCount);\n }\n\n /**\n * Gets the time until the rate limit resets for a given key\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise resolving to the time in milliseconds until reset, or 0 if no limit is active\n */\n public async getResetTime(key: string): Promise<number> {\n const lastReset = this.lastReset.get(key) || 0;\n const now = Date.now();\n const timeSinceReset = now - lastReset;\n\n if (timeSinceReset >= this.resetInterval) {\n return 0;\n }\n\n return this.resetInterval - timeSinceReset;\n }\n\n /**\n * Resets the rate limit for a given key\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise that resolves when the reset is complete\n */\n public async reset(key: string): Promise<void> {\n await this.storage.delete(key);\n this.limits.delete(key);\n this.lastReset.delete(key);\n }\n\n /**\n * Gets the current configuration of the rate limiter\n * @returns Object containing the current maxRequests and interval values\n */\n public getConfig(): Omit<RateLimitOptions, 'storage'> {\n return {\n maxRequests: this.maxRequests,\n interval: this.interval,\n };\n }\n}\n\n/**\n * Default rate limiter instance for global use\n */\nexport const defaultRateLimiter = new RateLimiter();\n\n/**\n * Convenience function to check if a request is allowed for a given key.\n * Uses the default rate limiter instance.\n *\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise resolving to true if the request is allowed, false if rate limited\n *\n * @example\n * ```typescript\n * const allowed = await ratelimit('user:123');\n * // update the default rate limiter config\n * import { defaultRateLimiter } from 'commandkit/ratelimit';\n *\n * // update max allowed requests\n * defaultRateLimiter.setMaxRequests(10);\n *\n * // update the timeout interval\n * defaultRateLimiter.setInterval(30000);\n *\n * // update the storage implementation\n * defaultRateLimiter.setStorage(new RedisStorage());\n * ```\n */\nexport async function ratelimit(key: string): Promise<boolean> {\n return defaultRateLimiter.limit(key);\n}\n\n/**\n * Creates a new rate limiter instance with the specified configuration\n * @param options - Configuration options for the rate limiter\n * @returns New RateLimiter instance\n *\n * @example\n * ```typescript\n * const limiter = createRateLimiter({\n * maxRequests: 5,\n * interval: 30000,\n * storage: new CustomStorage()\n * });\n * ```\n */\nexport function createRateLimiter(options: RateLimitOptions): RateLimiter {\n return new RateLimiter(\n options.maxRequests,\n options.interval,\n options.storage,\n );\n}\n\n/**\n * Gets the remaining requests for a key using the default rate limiter\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise resolving to the number of remaining requests\n */\nexport async function getRemainingRequests(key: string): Promise<number> {\n return defaultRateLimiter.getRemaining(key);\n}\n\n/**\n * Gets the reset time for a key using the default rate limiter\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise resolving to the time in milliseconds until reset\n */\nexport async function getResetTime(key: string): Promise<number> {\n return defaultRateLimiter.getResetTime(key);\n}\n\n/**\n * Resets the rate limit for a key using the default rate limiter\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise that resolves when the reset is complete\n */\nexport async function resetRateLimit(key: string): Promise<void> {\n return defaultRateLimiter.reset(key);\n}\n"],"mappings":";;;;;AAGA,MAAa,kBAAkB;;;;AAK/B,MAAa,uBAAuB;;;;;AA8CpC,IAAa,yBAAb,MAAgE;CAC9D,AAAiB,wBAAQ,IAAI;;;;;;CAO7B,MAAM,IAAI,KAA8B;AACtC,SAAO,KAAK,MAAM,IAAI,QAAQ;CAChC;;;;;;;CAQA,MAAM,IAAI,KAAa,OAA8B;AACnD,OAAK,MAAM,IAAI,KAAK;CACtB;;;;;;CAOA,MAAM,OAAO,KAA4B;AACvC,OAAK,MAAM,OAAO;CACpB;AACF;;;;;AAMA,IAAa,cAAb,MAAyB;CACvB,AAAiB,yBAA8B,IAAI;CACnD,AAAiB,4BAAiC,IAAI;CACtD,AAAiB;;;;;;;CAQjB,AAAO,YACP,AAAiB,cAAsB,sBACvC,AAAiB,WAAmB,iBACpC,AAAQ,UAA4B,IAAI,0BACxC;EAHiB;EACA;EACT;AAEN,OAAK,gBAAgB;CACvB;;;;;CAMA,AAAO,WAAW,SAA2B;AAC3C,OAAK,UAAU;CACjB;;;;;CAMA,AAAO,aAA+B;AACpC,SAAO,KAAK;CACd;;;;;;CAOA,MAAa,MAAM,KAA+B;EAChD,MAAM,MAAM,KAAK;EACjB,MAAM,YAAY,KAAK,UAAU,IAAI,QAAQ;EAC7C,MAAM,iBAAiB,MAAM;AAG7B,MAAI,iBAAiB,KAAK,eAAe;AACvC,SAAM,KAAK,QAAQ,OAAO;AAC1B,QAAK,UAAU,IAAI,KAAK;AACxB,QAAK,OAAO,IAAI,KAAK;EACvB;EAGA,MAAM,eAAe,MAAM,KAAK,QAAQ,IAAI;AAG5C,MAAI,gBAAgB,KAAK,YACvB,QAAO;EAIT,MAAM,WAAW,eAAe;AAChC,QAAM,KAAK,QAAQ,IAAI,KAAK;AAC5B,OAAK,OAAO,IAAI,KAAK;AAErB,SAAO;CACT;;;;;;CAOA,MAAa,aAAa,KAA8B;EACtD,MAAM,eAAe,MAAM,KAAK,QAAQ,IAAI;AAC5C,SAAO,KAAK,IAAI,GAAG,KAAK,cAAc;CACxC;;;;;;CAOA,MAAa,aAAa,KAA8B;EACtD,MAAM,YAAY,KAAK,UAAU,IAAI,QAAQ;EAC7C,MAAM,MAAM,KAAK;EACjB,MAAM,iBAAiB,MAAM;AAE7B,MAAI,kBAAkB,KAAK,cACzB,QAAO;AAGT,SAAO,KAAK,gBAAgB;CAC9B;;;;;;CAOA,MAAa,MAAM,KAA4B;AAC7C,QAAM,KAAK,QAAQ,OAAO;AAC1B,OAAK,OAAO,OAAO;AACnB,OAAK,UAAU,OAAO;CACxB;;;;;CAMA,AAAO,YAA+C;AACpD,SAAO;GACL,aAAa,KAAK;GAClB,UAAU,KAAK;GAChB;CACH;AACF;;;;AAKA,MAAa,qBAAqB,IAAI;;;;;;;;;;;;;;;;;;;;;;;;AAyBtC,eAAsB,UAAU,KAA+B;AAC7D,QAAO,mBAAmB,MAAM;AAClC;;;;;;;;;;;;;;;AAgBA,SAAgB,kBAAkB,SAAwC;AACxE,QAAO,IAAI,YACT,QAAQ,aACR,QAAQ,UACR,QAAQ;AAEZ;;;;;;AAOA,eAAsB,qBAAqB,KAA8B;AACvE,QAAO,mBAAmB,aAAa;AACzC;;;;;;AAOA,eAAsB,aAAa,KAA8B;AAC/D,QAAO,mBAAmB,aAAa;AACzC;;;;;;AAOA,eAAsB,eAAe,KAA4B;AAC/D,QAAO,mBAAmB,MAAM;AAClC"}
|
|
1
|
+
{"version":3,"file":"ratelimiter.js","names":[],"sources":["../../../src/utils/useful-stuff/ratelimiter.ts"],"sourcesContent":["/**\n * Default timeout interval for rate limiting in milliseconds\n */\nexport const DEFAULT_TIMEOUT = 60000;\n\n/**\n * Default maximum number of requests allowed per interval\n */\nexport const DEFAULT_MAX_REQUESTS = 10;\n\n/**\n * Interface for rate limit storage implementations.\n * Provides methods to store, retrieve, and delete rate limit data.\n */\nexport interface RateLimitStorage {\n /**\n * Retrieves the current request count for a given key\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise resolving to the current request count\n */\n get(key: string): Promise<number>;\n\n /**\n * Sets the request count for a given key\n * @param key - The unique identifier for the rate limit entry\n * @param value - The request count to store\n * @returns Promise that resolves when the value is stored\n */\n set(key: string, value: number): Promise<void>;\n\n /**\n * Deletes the rate limit entry for a given key\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise that resolves when the entry is deleted\n */\n delete(key: string): Promise<void>;\n}\n\n/**\n * Configuration options for rate limiting\n */\nexport interface RateLimitOptions {\n /** Maximum number of requests allowed per interval. Default: 10 */\n maxRequests?: number;\n /** Time interval in milliseconds for the rate limit window. Default: 60000 */\n interval?: number;\n /** Storage implementation for persisting rate limit data. Default: {@link MemoryRateLimitStorage} */\n storage?: RateLimitStorage;\n}\n\n/**\n * In-memory storage implementation for rate limiting.\n * Suitable for single-instance applications.\n */\nexport class MemoryRateLimitStorage implements RateLimitStorage {\n private readonly store = new Map<string, number>();\n\n /**\n * Retrieves the current request count for a given key\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise resolving to the current request count or 0 if not found\n */\n async get(key: string): Promise<number> {\n return this.store.get(key) || 0;\n }\n\n /**\n * Sets the request count for a given key\n * @param key - The unique identifier for the rate limit entry\n * @param value - The request count to store\n * @returns Promise that resolves immediately\n */\n async set(key: string, value: number): Promise<void> {\n this.store.set(key, value);\n }\n\n /**\n * Deletes the rate limit entry for a given key\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise that resolves immediately\n */\n async delete(key: string): Promise<void> {\n this.store.delete(key);\n }\n}\n\n/**\n * Rate limiter implementation that enforces request limits per key.\n * Supports configurable request limits and time intervals.\n */\nexport class RateLimiter {\n private readonly limits: Map<string, number> = new Map();\n private readonly lastReset: Map<string, number> = new Map();\n private readonly resetInterval: number;\n\n /**\n * Creates a new rate limiter instance\n * @param maxRequests - Maximum number of requests allowed per interval (default: 10)\n * @param interval - Time interval in milliseconds for the rate limit window (default: 60000)\n * @param storage - Optional storage implementation (default: MemoryRateLimitStorage)\n */\n public constructor(\n private readonly maxRequests: number = DEFAULT_MAX_REQUESTS,\n private readonly interval: number = DEFAULT_TIMEOUT,\n private storage: RateLimitStorage = new MemoryRateLimitStorage(),\n ) {\n this.resetInterval = interval;\n }\n\n /**\n * Sets the storage implementation for the rate limiter\n * @param storage - The storage implementation to use\n */\n public setStorage(storage: RateLimitStorage) {\n this.storage = storage;\n }\n\n /**\n * Gets the storage implementation for the rate limiter\n * @returns The storage implementation\n */\n public getStorage(): RateLimitStorage {\n return this.storage;\n }\n\n /**\n * Checks if a request is allowed for the given key and increments the counter if allowed\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise resolving to true if the request is allowed, false if rate limited\n */\n public async limit(key: string): Promise<boolean> {\n const now = Date.now();\n const lastReset = this.lastReset.get(key) || 0;\n const timeSinceReset = now - lastReset;\n\n // Reset counter if interval has passed\n if (timeSinceReset > this.resetInterval) {\n await this.storage.delete(key);\n this.lastReset.set(key, now);\n this.limits.set(key, 0);\n }\n\n // Get current count from storage\n const currentCount = await this.storage.get(key);\n\n // Check if limit exceeded\n if (currentCount >= this.maxRequests) {\n return false;\n }\n\n // Increment counter\n const newCount = currentCount + 1;\n await this.storage.set(key, newCount);\n this.limits.set(key, newCount);\n\n return true;\n }\n\n /**\n * Gets the remaining requests allowed for a given key\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise resolving to the number of remaining requests\n */\n public async getRemaining(key: string): Promise<number> {\n const currentCount = await this.storage.get(key);\n return Math.max(0, this.maxRequests - currentCount);\n }\n\n /**\n * Gets the time until the rate limit resets for a given key\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise resolving to the time in milliseconds until reset, or 0 if no limit is active\n */\n public async getResetTime(key: string): Promise<number> {\n const lastReset = this.lastReset.get(key) || 0;\n const now = Date.now();\n const timeSinceReset = now - lastReset;\n\n if (timeSinceReset >= this.resetInterval) {\n return 0;\n }\n\n return this.resetInterval - timeSinceReset;\n }\n\n /**\n * Resets the rate limit for a given key\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise that resolves when the reset is complete\n */\n public async reset(key: string): Promise<void> {\n await this.storage.delete(key);\n this.limits.delete(key);\n this.lastReset.delete(key);\n }\n\n /**\n * Gets the current configuration of the rate limiter\n * @returns Object containing the current maxRequests and interval values\n */\n public getConfig(): Omit<RateLimitOptions, 'storage'> {\n return {\n maxRequests: this.maxRequests,\n interval: this.interval,\n };\n }\n}\n\n/**\n * Default rate limiter instance for global use\n */\nexport const defaultRateLimiter = new RateLimiter();\n\n/**\n * Convenience function to check if a request is allowed for a given key.\n * Uses the default rate limiter instance.\n *\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise resolving to true if the request is allowed, false if rate limited\n *\n * @example\n * ```typescript\n * const allowed = await ratelimit('user:123');\n * // update the default rate limiter config\n * import { defaultRateLimiter } from 'commandkit/ratelimit';\n *\n * // update max allowed requests\n * defaultRateLimiter.setMaxRequests(10);\n *\n * // update the timeout interval\n * defaultRateLimiter.setInterval(30000);\n *\n * // update the storage implementation\n * defaultRateLimiter.setStorage(new RedisStorage());\n * ```\n */\nexport async function ratelimit(key: string): Promise<boolean> {\n return defaultRateLimiter.limit(key);\n}\n\n/**\n * Creates a new rate limiter instance with the specified configuration\n * @param options - Configuration options for the rate limiter\n * @returns New RateLimiter instance\n *\n * @example\n * ```typescript\n * const limiter = createRateLimiter({\n * maxRequests: 5,\n * interval: 30000,\n * storage: new CustomStorage()\n * });\n * ```\n */\nexport function createRateLimiter(options: RateLimitOptions): RateLimiter {\n return new RateLimiter(\n options.maxRequests,\n options.interval,\n options.storage,\n );\n}\n\n/**\n * Gets the remaining requests for a key using the default rate limiter\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise resolving to the number of remaining requests\n */\nexport async function getRemainingRequests(key: string): Promise<number> {\n return defaultRateLimiter.getRemaining(key);\n}\n\n/**\n * Gets the reset time for a key using the default rate limiter\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise resolving to the time in milliseconds until reset\n */\nexport async function getResetTime(key: string): Promise<number> {\n return defaultRateLimiter.getResetTime(key);\n}\n\n/**\n * Resets the rate limit for a key using the default rate limiter\n * @param key - The unique identifier for the rate limit entry\n * @returns Promise that resolves when the reset is complete\n */\nexport async function resetRateLimit(key: string): Promise<void> {\n return defaultRateLimiter.reset(key);\n}\n"],"mappings":";;;;;AAGA,MAAa,kBAAkB;;;;AAK/B,MAAa,uBAAuB;;;;;AA8CpC,IAAa,yBAAb,MAAgE;CAC9D,AAAiB,wBAAQ,IAAI;;;;;;CAO7B,MAAM,IAAI,KAA8B;AACtC,SAAO,KAAK,MAAM,IAAI,QAAQ;;;;;;;;CAShC,MAAM,IAAI,KAAa,OAA8B;AACnD,OAAK,MAAM,IAAI,KAAK;;;;;;;CAQtB,MAAM,OAAO,KAA4B;AACvC,OAAK,MAAM,OAAO;;;;;;;AAQtB,IAAa,cAAb,MAAyB;CACvB,AAAiB,yBAA8B,IAAI;CACnD,AAAiB,4BAAiC,IAAI;CACtD,AAAiB;;;;;;;CAQjB,AAAO,YACP,AAAiB,cAAsB,sBACvC,AAAiB,WAAmB,iBACpC,AAAQ,UAA4B,IAAI,0BACxC;EAHiB;EACA;EACT;AAEN,OAAK,gBAAgB;;;;;;CAOvB,AAAO,WAAW,SAA2B;AAC3C,OAAK,UAAU;;;;;;CAOjB,AAAO,aAA+B;AACpC,SAAO,KAAK;;;;;;;CAQd,MAAa,MAAM,KAA+B;EAChD,MAAM,MAAM,KAAK;EACjB,MAAM,YAAY,KAAK,UAAU,IAAI,QAAQ;EAC7C,MAAM,iBAAiB,MAAM;AAG7B,MAAI,iBAAiB,KAAK,eAAe;AACvC,SAAM,KAAK,QAAQ,OAAO;AAC1B,QAAK,UAAU,IAAI,KAAK;AACxB,QAAK,OAAO,IAAI,KAAK;;EAIvB,MAAM,eAAe,MAAM,KAAK,QAAQ,IAAI;AAG5C,MAAI,gBAAgB,KAAK,YACvB,QAAO;EAIT,MAAM,WAAW,eAAe;AAChC,QAAM,KAAK,QAAQ,IAAI,KAAK;AAC5B,OAAK,OAAO,IAAI,KAAK;AAErB,SAAO;;;;;;;CAQT,MAAa,aAAa,KAA8B;EACtD,MAAM,eAAe,MAAM,KAAK,QAAQ,IAAI;AAC5C,SAAO,KAAK,IAAI,GAAG,KAAK,cAAc;;;;;;;CAQxC,MAAa,aAAa,KAA8B;EACtD,MAAM,YAAY,KAAK,UAAU,IAAI,QAAQ;EAC7C,MAAM,MAAM,KAAK;EACjB,MAAM,iBAAiB,MAAM;AAE7B,MAAI,kBAAkB,KAAK,cACzB,QAAO;AAGT,SAAO,KAAK,gBAAgB;;;;;;;CAQ9B,MAAa,MAAM,KAA4B;AAC7C,QAAM,KAAK,QAAQ,OAAO;AAC1B,OAAK,OAAO,OAAO;AACnB,OAAK,UAAU,OAAO;;;;;;CAOxB,AAAO,YAA+C;AACpD,SAAO;GACL,aAAa,KAAK;GAClB,UAAU,KAAK;;;;;;;AAQrB,MAAa,qBAAqB,IAAI;;;;;;;;;;;;;;;;;;;;;;;;AAyBtC,eAAsB,UAAU,KAA+B;AAC7D,QAAO,mBAAmB,MAAM;;;;;;;;;;;;;;;;AAiBlC,SAAgB,kBAAkB,SAAwC;AACxE,QAAO,IAAI,YACT,QAAQ,aACR,QAAQ,UACR,QAAQ;;;;;;;AASZ,eAAsB,qBAAqB,KAA8B;AACvE,QAAO,mBAAmB,aAAa;;;;;;;AAQzC,eAAsB,aAAa,KAA8B;AAC/D,QAAO,mBAAmB,aAAa;;;;;;;AAQzC,eAAsB,eAAe,KAA4B;AAC/D,QAAO,mBAAmB,MAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"semaphore.js","names":[],"sources":["../../../src/utils/useful-stuff/semaphore.ts"],"sourcesContent":["/**\n * Async semaphore implementation for controlling access to a limited pool of resources.\n * Allows a specified number of concurrent operations while blocking additional requests.\n */\n\n/**\n * Interface for semaphore storage implementations.\n * Provides methods to store, retrieve, and manage semaphore permit data.\n */\nexport interface SemaphoreStorage {\n /**\n * Attempts to acquire a permit for a given key\n * @param key - The unique identifier for the semaphore\n * @param timeout - Optional timeout in milliseconds for permit acquisition\n * @param signal - Optional AbortSignal for cancelling the acquisition\n * @returns Promise resolving to true if permit was acquired, false if timeout or no permits available\n */\n acquire(\n key: string,\n timeout?: number,\n signal?: AbortSignal,\n ): Promise<boolean>;\n\n /**\n * Releases a permit for a given key\n * @param key - The unique identifier for the semaphore\n * @returns Promise that resolves when the permit is released\n */\n release(key: string): Promise<void>;\n\n /**\n * Gets the number of available permits for a given key\n * @param key - The unique identifier for the semaphore\n * @returns Promise resolving to the number of available permits\n */\n getAvailablePermits(key: string): Promise<number>;\n\n /**\n * Gets the total number of permits for a given key\n * @param key - The unique identifier for the semaphore\n * @returns Promise resolving to the total number of permits\n */\n getTotalPermits(key: string): Promise<number>;\n}\n\n/**\n * Configuration options for semaphore\n */\nexport interface SemaphoreOptions {\n /** Maximum number of concurrent permits allowed. Default: 1 */\n permits?: number;\n /** Default timeout in milliseconds for permit acquisition. Default: 30000 */\n timeout?: number;\n /** Storage implementation for persisting semaphore data. Default: {@link MemorySemaphoreStorage} */\n storage?: SemaphoreStorage;\n}\n\n/**\n * In-memory storage implementation for semaphore permits.\n * Suitable for single-instance applications.\n */\nexport class MemorySemaphoreStorage implements SemaphoreStorage {\n private readonly semaphores = new Map<\n string,\n { total: number; available: number }\n >();\n\n /**\n * Attempts to acquire a permit for a given key\n * @param key - The unique identifier for the semaphore\n * @param timeout - Optional timeout in milliseconds for permit acquisition\n * @param signal - Optional AbortSignal for cancelling the acquisition\n * @returns Promise resolving to true if permit was acquired, false if timeout or no permits available\n */\n async acquire(\n key: string,\n timeout: number = 30000,\n signal?: AbortSignal,\n ): Promise<boolean> {\n const startTime = Date.now();\n\n while (Date.now() - startTime < timeout) {\n // Check if aborted\n if (signal?.aborted) {\n throw new Error('Permit acquisition was aborted');\n }\n\n const semaphore = this.semaphores.get(key);\n if (semaphore && semaphore.available > 0) {\n semaphore.available--;\n return true;\n }\n await this.delay(10);\n }\n\n return false;\n }\n\n /**\n * Releases a permit for a given key\n * @param key - The unique identifier for the semaphore\n * @returns Promise that resolves when the permit is released\n */\n async release(key: string): Promise<void> {\n const semaphore = this.semaphores.get(key);\n if (semaphore && semaphore.available < semaphore.total) {\n semaphore.available++;\n }\n }\n\n /**\n * Gets the number of available permits for a given key\n * @param key - The unique identifier for the semaphore\n * @returns Promise resolving to the number of available permits\n */\n async getAvailablePermits(key: string): Promise<number> {\n const semaphore = this.semaphores.get(key);\n return semaphore ? semaphore.available : 0;\n }\n\n /**\n * Gets the total number of permits for a given key\n * @param key - The unique identifier for the semaphore\n * @returns Promise resolving to the total number of permits\n */\n async getTotalPermits(key: string): Promise<number> {\n const semaphore = this.semaphores.get(key);\n return semaphore ? semaphore.total : 0;\n }\n\n /**\n * Initializes a semaphore with the specified number of permits\n * @param key - The unique identifier for the semaphore\n * @param permits - The total number of permits to allocate\n */\n initialize(key: string, permits: number): void {\n this.semaphores.set(key, { total: permits, available: permits });\n }\n\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n\n/**\n * Async semaphore implementation that controls access to a limited pool of resources.\n * Allows a specified number of concurrent operations while blocking additional requests.\n */\nexport class Semaphore {\n private storage: SemaphoreStorage;\n private readonly defaultPermits: number;\n private readonly defaultTimeout: number;\n\n /**\n * Creates a new semaphore instance\n * @param options - Configuration options for the semaphore\n */\n public constructor(options: SemaphoreOptions = {}) {\n this.storage = options.storage || new MemorySemaphoreStorage();\n this.defaultPermits = options.permits || 1;\n this.defaultTimeout = options.timeout || 30000;\n }\n\n /**\n * Sets the storage implementation for the semaphore\n * @param storage - The storage implementation to use\n */\n public setStorage(storage: SemaphoreStorage) {\n this.storage = storage;\n }\n\n /**\n * Gets the storage implementation for the semaphore\n * @returns The storage implementation\n */\n public getStorage(): SemaphoreStorage {\n return this.storage;\n }\n\n /**\n * Acquires a permit for the given key\n * @param key - The unique identifier for the semaphore\n * @param timeout - Optional timeout in milliseconds for permit acquisition\n * @param signal - Optional AbortSignal for cancelling the acquisition\n * @returns Promise resolving to true if permit was acquired, false if timeout\n */\n public async acquire(\n key: string,\n timeout?: number,\n signal?: AbortSignal,\n ): Promise<boolean> {\n // Initialize semaphore if it doesn't exist\n if (this.storage instanceof MemorySemaphoreStorage) {\n const totalPermits = await this.storage.getTotalPermits(key);\n if (totalPermits === 0) {\n (this.storage as MemorySemaphoreStorage).initialize(\n key,\n this.defaultPermits,\n );\n }\n }\n\n return this.storage.acquire(key, timeout || this.defaultTimeout, signal);\n }\n\n /**\n * Releases a permit for the given key\n * @param key - The unique identifier for the semaphore\n * @returns Promise that resolves when the permit is released\n */\n public async release(key: string): Promise<void> {\n return this.storage.release(key);\n }\n\n /**\n * Gets the number of available permits for the given key\n * @param key - The unique identifier for the semaphore\n * @returns Promise resolving to the number of available permits\n */\n public async getAvailablePermits(key: string): Promise<number> {\n return this.storage.getAvailablePermits(key);\n }\n\n /**\n * Gets the total number of permits for the given key\n * @param key - The unique identifier for the semaphore\n * @returns Promise resolving to the total number of permits\n */\n public async getTotalPermits(key: string): Promise<number> {\n return this.storage.getTotalPermits(key);\n }\n\n /**\n * Gets the number of acquired permits for the given key\n * @param key - The unique identifier for the semaphore\n * @returns Promise resolving to the number of acquired permits\n */\n public async getAcquiredPermits(key: string): Promise<number> {\n const total = await this.getTotalPermits(key);\n const available = await this.getAvailablePermits(key);\n return total - available;\n }\n\n /**\n * Executes a function with a permit from the semaphore\n * @param key - The unique identifier for the semaphore\n * @param fn - The function to execute with a permit\n * @param timeout - Optional timeout in milliseconds for permit acquisition\n * @param signal - Optional AbortSignal for cancelling the permit acquisition\n * @returns Promise resolving to the result of the function execution\n * @throws Error if permit acquisition fails or function execution fails\n */\n public async withPermit<T>(\n key: string,\n fn: () => Promise<T> | T,\n timeout?: number,\n signal?: AbortSignal,\n ): Promise<T> {\n const acquired = await this.acquire(key, timeout, signal);\n if (!acquired) {\n throw new Error(`Failed to acquire permit for key: ${key}`);\n }\n\n try {\n return await fn();\n } finally {\n await this.release(key);\n }\n }\n\n /**\n * Gets the current configuration of the semaphore\n * @returns Object containing the current permits and timeout values\n */\n public getConfig(): Omit<SemaphoreOptions, 'storage'> {\n return {\n permits: this.defaultPermits,\n timeout: this.defaultTimeout,\n };\n }\n}\n\n/**\n * Default semaphore instance for global use\n */\nexport const defaultSemaphore = new Semaphore();\n\n/**\n * Convenience function to execute a function with a permit using the default semaphore.\n *\n * @param key - The unique identifier for the semaphore\n * @param fn - The function to execute with a permit\n * @param timeout - Optional timeout in milliseconds for permit acquisition\n * @param signal - Optional AbortSignal for cancelling the permit acquisition\n * @returns Promise resolving to the result of the function execution\n *\n * @example\n * ```typescript\n * const controller = new AbortController();\n * const result = await withPermit('database-connection', async () => {\n * // This code runs with a permit from the semaphore\n * return await executeDatabaseQuery();\n * }, 30000, controller.signal);\n * controller.abort(); // Cancels the permit acquisition\n * ```\n */\nexport async function withPermit<T>(\n key: string,\n fn: () => Promise<T> | T,\n timeout?: number,\n signal?: AbortSignal,\n): Promise<T> {\n return defaultSemaphore.withPermit(key, fn, timeout, signal);\n}\n\n/**\n * Creates a new semaphore instance with the specified configuration\n * @param options - Configuration options for the semaphore\n * @returns New Semaphore instance\n *\n * @example\n * ```typescript\n * const semaphore = createSemaphore({\n * permits: 5,\n * timeout: 60000,\n * storage: new RedisSemaphoreStorage()\n * });\n * ```\n */\nexport function createSemaphore(options: SemaphoreOptions): Semaphore {\n return new Semaphore(options);\n}\n\n/**\n * Acquires a permit using the default semaphore\n * @param key - The unique identifier for the semaphore\n * @param timeout - Optional timeout in milliseconds for permit acquisition\n * @param signal - Optional AbortSignal for cancelling the acquisition\n * @returns Promise resolving to true if permit was acquired, false if timeout\n */\nexport async function acquirePermit(\n key: string,\n timeout?: number,\n signal?: AbortSignal,\n): Promise<boolean> {\n return defaultSemaphore.acquire(key, timeout, signal);\n}\n\n/**\n * Releases a permit using the default semaphore\n * @param key - The unique identifier for the semaphore\n * @returns Promise that resolves when the permit is released\n */\nexport async function releasePermit(key: string): Promise<void> {\n return defaultSemaphore.release(key);\n}\n\n/**\n * Gets the number of available permits using the default semaphore\n * @param key - The unique identifier for the semaphore\n * @returns Promise resolving to the number of available permits\n */\nexport async function getAvailablePermits(key: string): Promise<number> {\n return defaultSemaphore.getAvailablePermits(key);\n}\n\n/**\n * Gets the number of acquired permits using the default semaphore\n * @param key - The unique identifier for the semaphore\n * @returns Promise resolving to the number of acquired permits\n */\nexport async function getAcquiredPermits(key: string): Promise<number> {\n return defaultSemaphore.getAcquiredPermits(key);\n}\n"],"mappings":";;;;;;AA6DA,IAAa,yBAAb,MAAgE;CAC9D,AAAiB,6BAAa,IAAI;;;;;;;;CAYlC,MAAM,QACN,KACA,UAAkB,KAClB,QACmB;EACjB,MAAM,YAAY,KAAK;AAEvB,SAAO,KAAK,QAAQ,YAAY,SAAS;AAEvC,uDAAI,OAAQ,QACV,OAAM,IAAI,MAAM;GAGlB,MAAM,YAAY,KAAK,WAAW,IAAI;AACtC,OAAI,aAAa,UAAU,YAAY,GAAG;AACxC,cAAU;AACV,WAAO;GACT;AACA,SAAM,KAAK,MAAM;EACnB;AAEA,SAAO;CACT;;;;;;CAOA,MAAM,QAAQ,KAA4B;EACxC,MAAM,YAAY,KAAK,WAAW,IAAI;AACtC,MAAI,aAAa,UAAU,YAAY,UAAU,MAC/C,WAAU;CAEd;;;;;;CAOA,MAAM,oBAAoB,KAA8B;EACtD,MAAM,YAAY,KAAK,WAAW,IAAI;AACtC,SAAO,YAAY,UAAU,YAAY;CAC3C;;;;;;CAOA,MAAM,gBAAgB,KAA8B;EAClD,MAAM,YAAY,KAAK,WAAW,IAAI;AACtC,SAAO,YAAY,UAAU,QAAQ;CACvC;;;;;;CAOA,WAAW,KAAa,SAAuB;AAC7C,OAAK,WAAW,IAAI,KAAK;GAAE,OAAO;GAAS,WAAW;GAAS;CACjE;CAEA,AAAQ,MAAM,IAA2B;AACvC,SAAO,IAAI,SAAS,YAAY,WAAW,SAAS;CACtD;AACF;;;;;AAMA,IAAa,YAAb,MAAuB;CACrB,AAAQ;CACR,AAAiB;CACjB,AAAiB;;;;;CAMjB,AAAO,YAAY,UAA4B,EAAE,EAAE;AACjD,OAAK,UAAU,QAAQ,WAAW,IAAI;AACtC,OAAK,iBAAiB,QAAQ,WAAW;AACzC,OAAK,iBAAiB,QAAQ,WAAW;CAC3C;;;;;CAMA,AAAO,WAAW,SAA2B;AAC3C,OAAK,UAAU;CACjB;;;;;CAMA,AAAO,aAA+B;AACpC,SAAO,KAAK;CACd;;;;;;;;CASA,MAAa,QACb,KACA,SACA,QACmB;AAEjB,MAAI,KAAK,mBAAmB,wBAAwB;GAClD,MAAM,eAAe,MAAM,KAAK,QAAQ,gBAAgB;AACxD,OAAI,iBAAiB,EACnB,CAAC,KAAK,QAAmC,WACvC,KACA,KAAK;EAGX;AAEA,SAAO,KAAK,QAAQ,QAAQ,KAAK,WAAW,KAAK,gBAAgB;CACnE;;;;;;CAOA,MAAa,QAAQ,KAA4B;AAC/C,SAAO,KAAK,QAAQ,QAAQ;CAC9B;;;;;;CAOA,MAAa,oBAAoB,KAA8B;AAC7D,SAAO,KAAK,QAAQ,oBAAoB;CAC1C;;;;;;CAOA,MAAa,gBAAgB,KAA8B;AACzD,SAAO,KAAK,QAAQ,gBAAgB;CACtC;;;;;;CAOA,MAAa,mBAAmB,KAA8B;EAC5D,MAAM,QAAQ,MAAM,KAAK,gBAAgB;EACzC,MAAM,YAAY,MAAM,KAAK,oBAAoB;AACjD,SAAO,QAAQ;CACjB;;;;;;;;;;CAWA,MAAa,WACb,KACA,IACA,SACA,QACa;EACX,MAAM,WAAW,MAAM,KAAK,QAAQ,KAAK,SAAS;AAClD,MAAI,CAAC,SACH,OAAM,IAAI,MAAM,qCAAqC;AAGvD,MAAI;AACF,UAAO,MAAM;EACd,UAAS;AACR,SAAM,KAAK,QAAQ;EACrB;CACF;;;;;CAMA,AAAO,YAA+C;AACpD,SAAO;GACL,SAAS,KAAK;GACd,SAAS,KAAK;GACf;CACH;AACF;;;;AAKA,MAAa,mBAAmB,IAAI;;;;;;;;;;;;;;;;;;;;AAqBpC,eAAsB,WACtB,KACA,IACA,SACA,QACa;AACX,QAAO,iBAAiB,WAAW,KAAK,IAAI,SAAS;AACvD;;;;;;;;;;;;;;;AAgBA,SAAgB,gBAAgB,SAAsC;AACpE,QAAO,IAAI,UAAU;AACvB;;;;;;;;AASA,eAAsB,cACtB,KACA,SACA,QACmB;AACjB,QAAO,iBAAiB,QAAQ,KAAK,SAAS;AAChD;;;;;;AAOA,eAAsB,cAAc,KAA4B;AAC9D,QAAO,iBAAiB,QAAQ;AAClC;;;;;;AAOA,eAAsB,oBAAoB,KAA8B;AACtE,QAAO,iBAAiB,oBAAoB;AAC9C;;;;;;AAOA,eAAsB,mBAAmB,KAA8B;AACrE,QAAO,iBAAiB,mBAAmB;AAC7C"}
|
|
1
|
+
{"version":3,"file":"semaphore.js","names":[],"sources":["../../../src/utils/useful-stuff/semaphore.ts"],"sourcesContent":["/**\n * Async semaphore implementation for controlling access to a limited pool of resources.\n * Allows a specified number of concurrent operations while blocking additional requests.\n */\n\n/**\n * Interface for semaphore storage implementations.\n * Provides methods to store, retrieve, and manage semaphore permit data.\n */\nexport interface SemaphoreStorage {\n /**\n * Attempts to acquire a permit for a given key\n * @param key - The unique identifier for the semaphore\n * @param timeout - Optional timeout in milliseconds for permit acquisition\n * @param signal - Optional AbortSignal for cancelling the acquisition\n * @returns Promise resolving to true if permit was acquired, false if timeout or no permits available\n */\n acquire(\n key: string,\n timeout?: number,\n signal?: AbortSignal,\n ): Promise<boolean>;\n\n /**\n * Releases a permit for a given key\n * @param key - The unique identifier for the semaphore\n * @returns Promise that resolves when the permit is released\n */\n release(key: string): Promise<void>;\n\n /**\n * Gets the number of available permits for a given key\n * @param key - The unique identifier for the semaphore\n * @returns Promise resolving to the number of available permits\n */\n getAvailablePermits(key: string): Promise<number>;\n\n /**\n * Gets the total number of permits for a given key\n * @param key - The unique identifier for the semaphore\n * @returns Promise resolving to the total number of permits\n */\n getTotalPermits(key: string): Promise<number>;\n}\n\n/**\n * Configuration options for semaphore\n */\nexport interface SemaphoreOptions {\n /** Maximum number of concurrent permits allowed. Default: 1 */\n permits?: number;\n /** Default timeout in milliseconds for permit acquisition. Default: 30000 */\n timeout?: number;\n /** Storage implementation for persisting semaphore data. Default: {@link MemorySemaphoreStorage} */\n storage?: SemaphoreStorage;\n}\n\n/**\n * In-memory storage implementation for semaphore permits.\n * Suitable for single-instance applications.\n */\nexport class MemorySemaphoreStorage implements SemaphoreStorage {\n private readonly semaphores = new Map<\n string,\n { total: number; available: number }\n >();\n\n /**\n * Attempts to acquire a permit for a given key\n * @param key - The unique identifier for the semaphore\n * @param timeout - Optional timeout in milliseconds for permit acquisition\n * @param signal - Optional AbortSignal for cancelling the acquisition\n * @returns Promise resolving to true if permit was acquired, false if timeout or no permits available\n */\n async acquire(\n key: string,\n timeout: number = 30000,\n signal?: AbortSignal,\n ): Promise<boolean> {\n const startTime = Date.now();\n\n while (Date.now() - startTime < timeout) {\n // Check if aborted\n if (signal?.aborted) {\n throw new Error('Permit acquisition was aborted');\n }\n\n const semaphore = this.semaphores.get(key);\n if (semaphore && semaphore.available > 0) {\n semaphore.available--;\n return true;\n }\n await this.delay(10);\n }\n\n return false;\n }\n\n /**\n * Releases a permit for a given key\n * @param key - The unique identifier for the semaphore\n * @returns Promise that resolves when the permit is released\n */\n async release(key: string): Promise<void> {\n const semaphore = this.semaphores.get(key);\n if (semaphore && semaphore.available < semaphore.total) {\n semaphore.available++;\n }\n }\n\n /**\n * Gets the number of available permits for a given key\n * @param key - The unique identifier for the semaphore\n * @returns Promise resolving to the number of available permits\n */\n async getAvailablePermits(key: string): Promise<number> {\n const semaphore = this.semaphores.get(key);\n return semaphore ? semaphore.available : 0;\n }\n\n /**\n * Gets the total number of permits for a given key\n * @param key - The unique identifier for the semaphore\n * @returns Promise resolving to the total number of permits\n */\n async getTotalPermits(key: string): Promise<number> {\n const semaphore = this.semaphores.get(key);\n return semaphore ? semaphore.total : 0;\n }\n\n /**\n * Initializes a semaphore with the specified number of permits\n * @param key - The unique identifier for the semaphore\n * @param permits - The total number of permits to allocate\n */\n initialize(key: string, permits: number): void {\n this.semaphores.set(key, { total: permits, available: permits });\n }\n\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n\n/**\n * Async semaphore implementation that controls access to a limited pool of resources.\n * Allows a specified number of concurrent operations while blocking additional requests.\n */\nexport class Semaphore {\n private storage: SemaphoreStorage;\n private readonly defaultPermits: number;\n private readonly defaultTimeout: number;\n\n /**\n * Creates a new semaphore instance\n * @param options - Configuration options for the semaphore\n */\n public constructor(options: SemaphoreOptions = {}) {\n this.storage = options.storage || new MemorySemaphoreStorage();\n this.defaultPermits = options.permits || 1;\n this.defaultTimeout = options.timeout || 30000;\n }\n\n /**\n * Sets the storage implementation for the semaphore\n * @param storage - The storage implementation to use\n */\n public setStorage(storage: SemaphoreStorage) {\n this.storage = storage;\n }\n\n /**\n * Gets the storage implementation for the semaphore\n * @returns The storage implementation\n */\n public getStorage(): SemaphoreStorage {\n return this.storage;\n }\n\n /**\n * Acquires a permit for the given key\n * @param key - The unique identifier for the semaphore\n * @param timeout - Optional timeout in milliseconds for permit acquisition\n * @param signal - Optional AbortSignal for cancelling the acquisition\n * @returns Promise resolving to true if permit was acquired, false if timeout\n */\n public async acquire(\n key: string,\n timeout?: number,\n signal?: AbortSignal,\n ): Promise<boolean> {\n // Initialize semaphore if it doesn't exist\n if (this.storage instanceof MemorySemaphoreStorage) {\n const totalPermits = await this.storage.getTotalPermits(key);\n if (totalPermits === 0) {\n (this.storage as MemorySemaphoreStorage).initialize(\n key,\n this.defaultPermits,\n );\n }\n }\n\n return this.storage.acquire(key, timeout || this.defaultTimeout, signal);\n }\n\n /**\n * Releases a permit for the given key\n * @param key - The unique identifier for the semaphore\n * @returns Promise that resolves when the permit is released\n */\n public async release(key: string): Promise<void> {\n return this.storage.release(key);\n }\n\n /**\n * Gets the number of available permits for the given key\n * @param key - The unique identifier for the semaphore\n * @returns Promise resolving to the number of available permits\n */\n public async getAvailablePermits(key: string): Promise<number> {\n return this.storage.getAvailablePermits(key);\n }\n\n /**\n * Gets the total number of permits for the given key\n * @param key - The unique identifier for the semaphore\n * @returns Promise resolving to the total number of permits\n */\n public async getTotalPermits(key: string): Promise<number> {\n return this.storage.getTotalPermits(key);\n }\n\n /**\n * Gets the number of acquired permits for the given key\n * @param key - The unique identifier for the semaphore\n * @returns Promise resolving to the number of acquired permits\n */\n public async getAcquiredPermits(key: string): Promise<number> {\n const total = await this.getTotalPermits(key);\n const available = await this.getAvailablePermits(key);\n return total - available;\n }\n\n /**\n * Executes a function with a permit from the semaphore\n * @param key - The unique identifier for the semaphore\n * @param fn - The function to execute with a permit\n * @param timeout - Optional timeout in milliseconds for permit acquisition\n * @param signal - Optional AbortSignal for cancelling the permit acquisition\n * @returns Promise resolving to the result of the function execution\n * @throws Error if permit acquisition fails or function execution fails\n */\n public async withPermit<T>(\n key: string,\n fn: () => Promise<T> | T,\n timeout?: number,\n signal?: AbortSignal,\n ): Promise<T> {\n const acquired = await this.acquire(key, timeout, signal);\n if (!acquired) {\n throw new Error(`Failed to acquire permit for key: ${key}`);\n }\n\n try {\n return await fn();\n } finally {\n await this.release(key);\n }\n }\n\n /**\n * Gets the current configuration of the semaphore\n * @returns Object containing the current permits and timeout values\n */\n public getConfig(): Omit<SemaphoreOptions, 'storage'> {\n return {\n permits: this.defaultPermits,\n timeout: this.defaultTimeout,\n };\n }\n}\n\n/**\n * Default semaphore instance for global use\n */\nexport const defaultSemaphore = new Semaphore();\n\n/**\n * Convenience function to execute a function with a permit using the default semaphore.\n *\n * @param key - The unique identifier for the semaphore\n * @param fn - The function to execute with a permit\n * @param timeout - Optional timeout in milliseconds for permit acquisition\n * @param signal - Optional AbortSignal for cancelling the permit acquisition\n * @returns Promise resolving to the result of the function execution\n *\n * @example\n * ```typescript\n * const controller = new AbortController();\n * const result = await withPermit('database-connection', async () => {\n * // This code runs with a permit from the semaphore\n * return await executeDatabaseQuery();\n * }, 30000, controller.signal);\n * controller.abort(); // Cancels the permit acquisition\n * ```\n */\nexport async function withPermit<T>(\n key: string,\n fn: () => Promise<T> | T,\n timeout?: number,\n signal?: AbortSignal,\n): Promise<T> {\n return defaultSemaphore.withPermit(key, fn, timeout, signal);\n}\n\n/**\n * Creates a new semaphore instance with the specified configuration\n * @param options - Configuration options for the semaphore\n * @returns New Semaphore instance\n *\n * @example\n * ```typescript\n * const semaphore = createSemaphore({\n * permits: 5,\n * timeout: 60000,\n * storage: new RedisSemaphoreStorage()\n * });\n * ```\n */\nexport function createSemaphore(options: SemaphoreOptions): Semaphore {\n return new Semaphore(options);\n}\n\n/**\n * Acquires a permit using the default semaphore\n * @param key - The unique identifier for the semaphore\n * @param timeout - Optional timeout in milliseconds for permit acquisition\n * @param signal - Optional AbortSignal for cancelling the acquisition\n * @returns Promise resolving to true if permit was acquired, false if timeout\n */\nexport async function acquirePermit(\n key: string,\n timeout?: number,\n signal?: AbortSignal,\n): Promise<boolean> {\n return defaultSemaphore.acquire(key, timeout, signal);\n}\n\n/**\n * Releases a permit using the default semaphore\n * @param key - The unique identifier for the semaphore\n * @returns Promise that resolves when the permit is released\n */\nexport async function releasePermit(key: string): Promise<void> {\n return defaultSemaphore.release(key);\n}\n\n/**\n * Gets the number of available permits using the default semaphore\n * @param key - The unique identifier for the semaphore\n * @returns Promise resolving to the number of available permits\n */\nexport async function getAvailablePermits(key: string): Promise<number> {\n return defaultSemaphore.getAvailablePermits(key);\n}\n\n/**\n * Gets the number of acquired permits using the default semaphore\n * @param key - The unique identifier for the semaphore\n * @returns Promise resolving to the number of acquired permits\n */\nexport async function getAcquiredPermits(key: string): Promise<number> {\n return defaultSemaphore.getAcquiredPermits(key);\n}\n"],"mappings":";;;;;;AA6DA,IAAa,yBAAb,MAAgE;CAC9D,AAAiB,6BAAa,IAAI;;;;;;;;CAYlC,MAAM,QACN,KACA,UAAkB,KAClB,QACmB;EACjB,MAAM,YAAY,KAAK;AAEvB,SAAO,KAAK,QAAQ,YAAY,SAAS;AAEvC,uDAAI,OAAQ,QACV,OAAM,IAAI,MAAM;GAGlB,MAAM,YAAY,KAAK,WAAW,IAAI;AACtC,OAAI,aAAa,UAAU,YAAY,GAAG;AACxC,cAAU;AACV,WAAO;;AAET,SAAM,KAAK,MAAM;;AAGnB,SAAO;;;;;;;CAQT,MAAM,QAAQ,KAA4B;EACxC,MAAM,YAAY,KAAK,WAAW,IAAI;AACtC,MAAI,aAAa,UAAU,YAAY,UAAU,MAC/C,WAAU;;;;;;;CASd,MAAM,oBAAoB,KAA8B;EACtD,MAAM,YAAY,KAAK,WAAW,IAAI;AACtC,SAAO,YAAY,UAAU,YAAY;;;;;;;CAQ3C,MAAM,gBAAgB,KAA8B;EAClD,MAAM,YAAY,KAAK,WAAW,IAAI;AACtC,SAAO,YAAY,UAAU,QAAQ;;;;;;;CAQvC,WAAW,KAAa,SAAuB;AAC7C,OAAK,WAAW,IAAI,KAAK;GAAE,OAAO;GAAS,WAAW;;;CAGxD,AAAQ,MAAM,IAA2B;AACvC,SAAO,IAAI,SAAS,YAAY,WAAW,SAAS;;;;;;;AAQxD,IAAa,YAAb,MAAuB;CACrB,AAAQ;CACR,AAAiB;CACjB,AAAiB;;;;;CAMjB,AAAO,YAAY,UAA4B,IAAI;AACjD,OAAK,UAAU,QAAQ,WAAW,IAAI;AACtC,OAAK,iBAAiB,QAAQ,WAAW;AACzC,OAAK,iBAAiB,QAAQ,WAAW;;;;;;CAO3C,AAAO,WAAW,SAA2B;AAC3C,OAAK,UAAU;;;;;;CAOjB,AAAO,aAA+B;AACpC,SAAO,KAAK;;;;;;;;;CAUd,MAAa,QACb,KACA,SACA,QACmB;AAEjB,MAAI,KAAK,mBAAmB,wBAAwB;GAClD,MAAM,eAAe,MAAM,KAAK,QAAQ,gBAAgB;AACxD,OAAI,iBAAiB,EACnB,CAAC,KAAK,QAAmC,WACvC,KACA,KAAK;;AAKX,SAAO,KAAK,QAAQ,QAAQ,KAAK,WAAW,KAAK,gBAAgB;;;;;;;CAQnE,MAAa,QAAQ,KAA4B;AAC/C,SAAO,KAAK,QAAQ,QAAQ;;;;;;;CAQ9B,MAAa,oBAAoB,KAA8B;AAC7D,SAAO,KAAK,QAAQ,oBAAoB;;;;;;;CAQ1C,MAAa,gBAAgB,KAA8B;AACzD,SAAO,KAAK,QAAQ,gBAAgB;;;;;;;CAQtC,MAAa,mBAAmB,KAA8B;EAC5D,MAAM,QAAQ,MAAM,KAAK,gBAAgB;EACzC,MAAM,YAAY,MAAM,KAAK,oBAAoB;AACjD,SAAO,QAAQ;;;;;;;;;;;CAYjB,MAAa,WACb,KACA,IACA,SACA,QACa;EACX,MAAM,WAAW,MAAM,KAAK,QAAQ,KAAK,SAAS;AAClD,MAAI,CAAC,SACH,OAAM,IAAI,MAAM,qCAAqC;AAGvD,MAAI;AACF,UAAO,MAAM;YACL;AACR,SAAM,KAAK,QAAQ;;;;;;;CAQvB,AAAO,YAA+C;AACpD,SAAO;GACL,SAAS,KAAK;GACd,SAAS,KAAK;;;;;;;AAQpB,MAAa,mBAAmB,IAAI;;;;;;;;;;;;;;;;;;;;AAqBpC,eAAsB,WACtB,KACA,IACA,SACA,QACa;AACX,QAAO,iBAAiB,WAAW,KAAK,IAAI,SAAS;;;;;;;;;;;;;;;;AAiBvD,SAAgB,gBAAgB,SAAsC;AACpE,QAAO,IAAI,UAAU;;;;;;;;;AAUvB,eAAsB,cACtB,KACA,SACA,QACmB;AACjB,QAAO,iBAAiB,QAAQ,KAAK,SAAS;;;;;;;AAQhD,eAAsB,cAAc,KAA4B;AAC9D,QAAO,iBAAiB,QAAQ;;;;;;;AAQlC,eAAsB,oBAAoB,KAA8B;AACtE,QAAO,iBAAiB,oBAAoB;;;;;;;AAQ9C,eAAsB,mBAAmB,KAA8B;AACrE,QAAO,iBAAiB,mBAAmB"}
|