@mtkruto/node 0.1.134 → 0.1.136

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.
Files changed (39) hide show
  1. package/esm/0_deps.d.ts +1 -1
  2. package/esm/0_deps.js +1 -1
  3. package/esm/4_constants.d.ts +1 -1
  4. package/esm/4_constants.js +1 -1
  5. package/esm/client/0_utilities.d.ts +32 -23
  6. package/esm/client/0_utilities.js +27 -0
  7. package/esm/client/1_composer.d.ts +4 -4
  8. package/esm/client/1_composer.js +3 -29
  9. package/esm/client/4_client.d.ts +4 -4
  10. package/esm/client/4_client.js +3 -29
  11. package/esm/deps/raw.githubusercontent.com/MTKruto/mutex/main/mod.d.ts +2 -0
  12. package/esm/deps/raw.githubusercontent.com/MTKruto/mutex/main/mod.js +2 -0
  13. package/esm/deps/raw.githubusercontent.com/MTKruto/mutex/main/mutex.d.ts +26 -0
  14. package/esm/deps/raw.githubusercontent.com/MTKruto/mutex/main/mutex.js +32 -0
  15. package/esm/deps/raw.githubusercontent.com/MTKruto/mutex/main/semaphore.d.ts +41 -0
  16. package/esm/deps/raw.githubusercontent.com/MTKruto/mutex/main/semaphore.js +113 -0
  17. package/esm/storage/1_storage_local_storage.js +2 -2
  18. package/esm/storage/1_storage_session_storage.js +2 -2
  19. package/esm/types/6_update.d.ts +70 -8
  20. package/package.json +1 -2
  21. package/script/0_deps.d.ts +1 -1
  22. package/script/0_deps.js +6 -6
  23. package/script/4_constants.d.ts +1 -1
  24. package/script/4_constants.js +1 -1
  25. package/script/client/0_utilities.d.ts +32 -23
  26. package/script/client/0_utilities.js +29 -1
  27. package/script/client/1_composer.d.ts +4 -4
  28. package/script/client/1_composer.js +3 -29
  29. package/script/client/4_client.d.ts +4 -4
  30. package/script/client/4_client.js +2 -28
  31. package/script/deps/raw.githubusercontent.com/MTKruto/mutex/main/mod.d.ts +2 -0
  32. package/script/deps/raw.githubusercontent.com/MTKruto/mutex/main/mod.js +18 -0
  33. package/script/deps/raw.githubusercontent.com/MTKruto/mutex/main/mutex.d.ts +26 -0
  34. package/script/deps/raw.githubusercontent.com/MTKruto/mutex/main/mutex.js +36 -0
  35. package/script/deps/raw.githubusercontent.com/MTKruto/mutex/main/semaphore.d.ts +41 -0
  36. package/script/deps/raw.githubusercontent.com/MTKruto/mutex/main/semaphore.js +117 -0
  37. package/script/storage/1_storage_local_storage.js +2 -2
  38. package/script/storage/1_storage_session_storage.js +2 -2
  39. package/script/types/6_update.d.ts +70 -8
package/esm/0_deps.d.ts CHANGED
@@ -5,7 +5,7 @@ export { contentType } from "./deps/deno.land/std@0.210.0/media_types/content_ty
5
5
  export declare function extension(mimeType: string): string;
6
6
  export { ctr256, factorize, ige256Decrypt, ige256Encrypt, init as initTgCrypto } from "./deps/deno.land/x/tgcrypto@0.3.3/mod.js";
7
7
  export { gunzip, gzip } from "./deps/raw.githubusercontent.com/MTKruto/compress/main/gzip/gzip.js";
8
- export { Mutex, type MutexInterface } from "async-mutex";
8
+ export { Mutex } from "./deps/raw.githubusercontent.com/MTKruto/mutex/main/mod.js";
9
9
  export { Parser } from "./deps/deno.land/x/html_parser@v0.1.3/src/mod.js";
10
10
  import { debug as debug_ } from "./deps/raw.githubusercontent.com/MTKruto/debug/main/mod.js";
11
11
  export declare const debug: typeof debug_;
package/esm/0_deps.js CHANGED
@@ -13,7 +13,7 @@ export function extension(mimeType) {
13
13
  }
14
14
  export { ctr256, factorize, ige256Decrypt, ige256Encrypt, init as initTgCrypto } from "./deps/deno.land/x/tgcrypto@0.3.3/mod.js";
15
15
  export { gunzip, gzip } from "./deps/raw.githubusercontent.com/MTKruto/compress/main/gzip/gzip.js";
16
- export { Mutex } from "async-mutex";
16
+ export { Mutex } from "./deps/raw.githubusercontent.com/MTKruto/mutex/main/mod.js";
17
17
  export { Parser } from "./deps/deno.land/x/html_parser@v0.1.3/src/mod.js";
18
18
  import { debug as debug_ } from "./deps/raw.githubusercontent.com/MTKruto/debug/main/mod.js";
19
19
  export const debug = (v) => debug_(v);
@@ -4,7 +4,7 @@ export type PublicKeys = readonly [bigint, [bigint, bigint]][];
4
4
  export declare const PUBLIC_KEYS: PublicKeys;
5
5
  export declare const INITIAL_DC: DC;
6
6
  export declare const LAYER = 169;
7
- export declare const APP_VERSION = "MTKruto 0.1.134";
7
+ export declare const APP_VERSION = "MTKruto 0.1.136";
8
8
  export declare const DEVICE_MODEL: string;
9
9
  export declare const LANG_CODE: string;
10
10
  export declare const LANG_PACK = "";
@@ -53,7 +53,7 @@ export const PUBLIC_KEYS = Object.freeze([
53
53
  ]);
54
54
  export const INITIAL_DC = "2";
55
55
  export const LAYER = 169;
56
- export const APP_VERSION = "MTKruto 0.1.134";
56
+ export const APP_VERSION = "MTKruto 0.1.136";
57
57
  // @ts-ignore: lib
58
58
  export const DEVICE_MODEL = typeof dntShim.Deno === "undefined" ? typeof navigator === "undefined" ? typeof process === "undefined" ? "Unknown" : process.platform + "-" + process.arch : navigator.userAgent.split(" ")[0] : dntShim.Deno.build.os + "-" + dntShim.Deno.build.arch;
59
59
  export const LANG_CODE = typeof navigator === "undefined" ? "en" : navigator.language.split("-")[0];
@@ -1,5 +1,5 @@
1
1
  import { enums, types } from "../2_tl.js";
2
- import { ChatP, Message, MessageTypes, UpdateMap, User } from "../3_types.js";
2
+ import { ChatP, MessageTypes, UpdateMap, User } from "../3_types.js";
3
3
  export declare const resolve: () => Promise<void>;
4
4
  export type With<T, K extends keyof T> = T & Required<Pick<T, K>>;
5
5
  export declare function isPtsUpdate(v: enums.Update): v is types.UpdateNewMessage | types.UpdateDeleteMessages | types.UpdateReadHistoryInbox | types.UpdateReadHistoryOutbox | types.UpdatePinnedChannelMessages | types.UpdatePinnedMessages | types.UpdateFolderPeers | types.UpdateChannelWebPage | types.UpdateEditMessage | types.UpdateReadMessagesContents | types.UpdateWebPage;
@@ -12,29 +12,38 @@ export declare function getFileContents(source: FileSource, fileName?: string):
12
12
  export declare function isHttpUrl(string: string): boolean;
13
13
  export declare function getUsername(string: string): string;
14
14
  export declare function getChatListId(chatList: string): 0 | 1;
15
- type MessageWith<T extends keyof MessageTypes, F extends number | string | symbol> = F extends keyof MessageTypes[T] ? MessageTypes[T] & {
16
- [P in F]-?: NonNullable<MessageTypes[T][P]>;
17
- } : MessageTypes[T];
18
- type Me<U extends keyof UpdateMap> = U extends "connectionState" | "authorizationState" ? {
19
- me?: User;
20
- } : {
21
- me: User;
22
- };
23
- type Msg<U extends keyof UpdateMap, T extends keyof MessageTypes, F extends number | string | symbol = ""> = U extends "message" | "editedMessage" ? {
24
- msg: MessageFilter<U, T, F>;
15
+ type AnyLevel1 = keyof UpdateMap;
16
+ type GetLevel1Type<L1 extends AnyLevel1> = UpdateMap[L1];
17
+ type GetAnyLevel2<L1 extends AnyLevel1> = L1 extends "message" | "editedMessage" ? keyof MessageTypes : never;
18
+ type AnyLevel2<L1 extends AnyLevel1 = AnyLevel1> = L1 extends unknown ? `${L1 extends "message" | "editedMessage" ? L1 | "" : L1}:${GetAnyLevel2<L1>}` : never;
19
+ type GetLevel2Type<L1 extends string, L2 extends string> = L2 extends keyof MessageTypes ? L1 extends "" ? {
20
+ [P in "message" | "editedMessage"]?: MessageTypes[L2];
21
+ } : L1 extends "message" | "editedMessage" ? {
22
+ [P in L1]: MessageTypes[L2];
23
+ } : never : never;
24
+ type AnyLevelX = AnyLevel1 | AnyLevel2;
25
+ type FilterCore<Q extends AnyLevelX = AnyLevelX> = Q extends AnyLevel1 ? GetLevel1Type<Q> : Q extends `${infer L1}:${infer L2}` ? GetLevel2Type<L1, L2> : 1;
26
+ type Chat<T> = "msg" extends keyof T ? T & {
25
27
  chat: ChatP;
26
- } : {
27
- msg?: Message;
28
- };
29
- type From<U extends keyof UpdateMap> = U extends "callbackQuery" | "inlineQuery" ? {
28
+ } : T;
29
+ type Msg<T> = "message" extends keyof T ? T & {
30
+ msg: NonNullable<T["message"]>;
31
+ } : "editedMessage" extends keyof T ? T & {
32
+ msg: NonNullable<T["editedMessage"]>;
33
+ } : "callbackQuery" extends keyof T ? "message" extends keyof T["callbackQuery"] ? T & {
34
+ msg: T["callbackQuery"]["message"];
35
+ } : T : T;
36
+ type From<T> = "callbackQuery" | "inlineQuery" extends keyof T ? T & {
30
37
  from: User;
31
- } : {
38
+ } : "message" | "editedMessage" extends keyof T ? {
32
39
  from?: User;
33
- };
34
- type MessageFilter<U extends keyof UpdateMap, T extends keyof MessageTypes, F extends number | string | symbol> = U extends "message" ? {
35
- message: MessageWith<T, F>;
36
- } : U extends "editedMessage" ? {
37
- editedMessage: MessageWith<T, F>;
38
- } : UpdateMap[U];
39
- export type WithUpdate<C, U extends keyof UpdateMap, T extends keyof MessageTypes, F extends number | string | symbol = ""> = C & Me<U> & Msg<U, T, F> & From<U> & MessageFilter<U, T, F>;
40
+ } : T;
41
+ type SenderChat<T> = "message" | "editedMessage" extends keyof T ? {
42
+ senderChat?: ChatP;
43
+ } : T;
44
+ type Shortcuts<T> = SenderChat<From<Chat<Msg<T>>>>;
45
+ type Filter<Q extends AnyLevelX> = Shortcuts<FilterCore<Q>>;
46
+ export type FilterQuery = AnyLevelX;
47
+ export type WithFilter<T, Q extends FilterQuery> = T & Filter<Q>;
48
+ export declare function match<Q extends FilterQuery, T extends object>(filter: Q, value: T): boolean;
40
49
  export {};
@@ -145,3 +145,30 @@ export function getChatListId(chatList) {
145
145
  UNREACHABLE();
146
146
  }
147
147
  }
148
+ export function match(filter, value) {
149
+ let [type, ...other] = filter.split(":");
150
+ if (type != "" && !(type in value)) {
151
+ return false;
152
+ }
153
+ if (type == "") {
154
+ if (other.length != 1) {
155
+ return false;
156
+ }
157
+ if ("message" in value) {
158
+ type = "message";
159
+ }
160
+ else if ("editedMessage" in value) {
161
+ type = "editedMessage";
162
+ }
163
+ else {
164
+ return false;
165
+ }
166
+ }
167
+ const field = other[0];
168
+ if (field) {
169
+ if (!(field in value[type])) {
170
+ return false;
171
+ }
172
+ }
173
+ return true;
174
+ }
@@ -1,5 +1,5 @@
1
- import { MessageTypes, Update, UpdateIntersection, UpdateMap, User } from "../3_types.js";
2
- import { WithUpdate } from "./0_utilities.js";
1
+ import { Update, UpdateIntersection, User } from "../3_types.js";
2
+ import { FilterQuery, WithFilter } from "./0_utilities.js";
3
3
  type MaybePromise<T> = T | Promise<T>;
4
4
  export type NextFunction = () => Promise<void>;
5
5
  export type MiddlewareFn<C> = (ctx: C, next: NextFunction) => MaybePromise<unknown>;
@@ -21,10 +21,10 @@ export declare class Composer<C extends {
21
21
  branch(predicate: (ctx: UpdateIntersection<C>) => MaybePromise<boolean>, trueHandler_: Middleware<UpdateIntersection<C>>, falseHandler_: Middleware<UpdateIntersection<C>>): Composer<UpdateIntersection<C>>;
22
22
  filter<D extends C>(predicate: (ctx: UpdateIntersection<C>) => ctx is D, ...middleware: Middleware<D>[]): Composer<D>;
23
23
  filter(predicate: (ctx: UpdateIntersection<C>) => MaybePromise<boolean>, ...middleware: Middleware<UpdateIntersection<C>>[]): Composer<C>;
24
- on<T extends keyof UpdateMap, F extends string, K extends keyof MessageTypes>(filter: T extends "message" | "editedMessage" ? T | [T, K, ...F[]] : T, ...middleawre: Middleware<WithUpdate<C, T, K, F>>[]): Composer<WithUpdate<C, T, K, F>>;
24
+ on<Q extends FilterQuery>(filter: Q, ...middleawre: Middleware<WithFilter<C, Q>>[]): Composer<UpdateIntersection<WithFilter<C, Q>>>;
25
25
  command(commands: string | RegExp | (string | RegExp)[] | {
26
26
  names: string | RegExp | (string | RegExp)[];
27
27
  prefixes: string | string[];
28
- }, ...middleawre: Middleware<WithUpdate<C, "message", "text">>[]): Composer<WithUpdate<C, "message", "text", string>>;
28
+ }, ...middleawre: Middleware<WithFilter<C, "message:text">>[]): Composer<UpdateIntersection<WithFilter<C, "message:text">>>;
29
29
  }
30
30
  export {};
@@ -10,7 +10,7 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
10
10
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
11
11
  };
12
12
  var _Composer_handle, _Composer_prefixes;
13
- import { assertMessageType } from "../3_types.js";
13
+ import { match } from "./0_utilities.js";
14
14
  export function flatten(mw) {
15
15
  return typeof mw === "function" ? mw : (ctx, next) => mw.middleware()(ctx, next);
16
16
  }
@@ -69,34 +69,8 @@ export class Composer {
69
69
  return composer;
70
70
  }
71
71
  on(filter, ...middleawre) {
72
- const type = typeof filter === "string" ? filter : filter[0];
73
- let keys = Array.isArray(filter) ? filter.slice(1) : [];
74
- let messageType = null;
75
- if (type == "message") {
76
- messageType = keys[0];
77
- keys = keys.slice(1);
78
- }
79
72
  return this.filter((ctx) => {
80
- if (type in ctx) {
81
- if (messageType != null) {
82
- // deno-lint-ignore ban-ts-comment
83
- // @ts-ignore
84
- assertMessageType(ctx[type], messageType);
85
- }
86
- if (keys.length > 0) {
87
- for (const key of keys) {
88
- // deno-lint-ignore ban-ts-comment
89
- // @ts-ignore
90
- if (!(key in ctx[type])) {
91
- return false;
92
- }
93
- }
94
- }
95
- return true;
96
- }
97
- else {
98
- return false;
99
- }
73
+ return match(filter, ctx);
100
74
  }, ...middleawre);
101
75
  }
102
76
  command(commands, ...middleawre) {
@@ -114,7 +88,7 @@ export class Composer {
114
88
  }
115
89
  }
116
90
  }
117
- return this.on(["message", "text"]).filter((ctx) => {
91
+ return this.on("message:text").filter((ctx) => {
118
92
  const prefixes_ = prefixes.length == 0 ? [!ctx.me?.isBot ? "\\" : "/"] : prefixes;
119
93
  if (prefixes_.length == 0) {
120
94
  return false;
@@ -2,10 +2,10 @@ import { MaybePromise } from "../1_utilities.js";
2
2
  import { functions, ReadObject, types } from "../2_tl.js";
3
3
  import { Storage } from "../3_storage.js";
4
4
  import { DC } from "../3_transport.js";
5
- import { BotCommand, Chat, ChatAction, ChatID, ChatP, Document, InlineQueryResult, Message, MessageAnimation, MessageAudio, MessageContact, MessageDice, MessageDocument, MessageLocation, MessagePhoto, MessagePoll, MessageText, MessageTypes, MessageVenue, MessageVideo, MessageVideoNote, MessageVoice, NetworkStatistics, ParseMode, Reaction, Update, UpdateIntersection, UpdateMap, User } from "../3_types.js";
5
+ import { BotCommand, Chat, ChatAction, ChatID, ChatP, Document, InlineQueryResult, Message, MessageAnimation, MessageAudio, MessageContact, MessageDice, MessageDocument, MessageLocation, MessagePhoto, MessagePoll, MessageText, MessageVenue, MessageVideo, MessageVideoNote, MessageVoice, NetworkStatistics, ParseMode, Reaction, Update, UpdateIntersection, User } from "../3_types.js";
6
6
  import { Migrate } from "../4_errors.js";
7
7
  import { ClientAbstract } from "./0_client_abstract.js";
8
- import { FileSource, WithUpdate } from "./0_utilities.js";
8
+ import { FileSource, FilterQuery, WithFilter } from "./0_utilities.js";
9
9
  import { Composer, Middleware } from "./1_composer.js";
10
10
  import { AddReactionParams, AnswerCallbackQueryParams, AnswerInlineQueryParams, AuthorizeUserParams, ClientParams, DeleteMessageParams, DeleteMessagesParams, DownloadParams, EditMessageParams, ForwardMessagesParams, GetChatsParams, GetHistoryParams, GetMyCommandsParams, ReplyParams, SendAnimationParams, SendAudioParams, SendContactParams, SendDiceParams, SendDocumentParams, SendLocationParams, SendMessageParams, SendPhotoParams, SendPollParams, SendVenueParams, SendVideoNoteParams, SendVideoParams, SendVoiceParams, SetMyCommandsParams, SetReactionsParams, UploadParams } from "./3_params.js";
11
11
  export type NextFn<T = void> = () => Promise<T>;
@@ -339,11 +339,11 @@ export declare class Client<C extends Context = Context> extends ClientAbstract
339
339
  branch(predicate: (ctx: UpdateIntersection<C>) => MaybePromise<boolean>, trueHandler_: Middleware<UpdateIntersection<C>>, falseHandler_: Middleware<UpdateIntersection<C>>): Composer<UpdateIntersection<C>>;
340
340
  filter<D extends C>(predicate: (ctx: UpdateIntersection<C>) => ctx is D, ...middleware: Middleware<D>[]): Composer<D>;
341
341
  filter(predicate: (ctx: UpdateIntersection<C>) => MaybePromise<boolean>, ...middleware: Middleware<UpdateIntersection<C>>[]): Composer<C>;
342
- on<T extends keyof UpdateMap, F extends string, K extends keyof MessageTypes>(filter: T extends "message" | "editedMessage" ? T | [T, K, ...F[]] : T, ...middleawre: Middleware<WithUpdate<C, T, K, F>>[]): Composer<WithUpdate<C, T, K, F>>;
342
+ on<Q extends FilterQuery>(filter: Q, ...middleawre: Middleware<WithFilter<C, Q>>[]): Composer<UpdateIntersection<WithFilter<C, Q>>>;
343
343
  command(commands: string | RegExp | (string | RegExp)[] | {
344
344
  names: string | RegExp | (string | RegExp)[];
345
345
  prefixes: string | string[];
346
- }, ...middleawre: Middleware<WithUpdate<C, "message", "text">>[]): Composer<WithUpdate<C, "message", "text", string>>;
346
+ }, ...middleawre: Middleware<WithFilter<C, "message:text">>[]): Composer<UpdateIntersection<WithFilter<C, "message:text">>>;
347
347
  /**
348
348
  * Set the bot's description in the given language. Bot-only.
349
349
  *
@@ -21,7 +21,7 @@ import { ClientAbstract } from "./0_client_abstract.js";
21
21
  import { parseHtml } from "./0_html.js";
22
22
  import { decryptMessage, encryptMessage, getMessageId } from "./0_message.js";
23
23
  import { checkPassword } from "./0_password.js";
24
- import { getChatListId, getFileContents, getUsername, isChannelPtsUpdate, isHttpUrl, isPtsUpdate, resolve } from "./0_utilities.js";
24
+ import { getChatListId, getFileContents, getUsername, isChannelPtsUpdate, isHttpUrl, isPtsUpdate, match, resolve } from "./0_utilities.js";
25
25
  import { Composer, concat, flatten, skip } from "./1_composer.js";
26
26
  import { ClientPlain } from "./2_client_plain.js";
27
27
  const d = debug("Client");
@@ -2231,34 +2231,8 @@ export class Client extends ClientAbstract {
2231
2231
  return composer;
2232
2232
  }
2233
2233
  on(filter, ...middleawre) {
2234
- const type = typeof filter === "string" ? filter : filter[0];
2235
- let keys = Array.isArray(filter) ? filter.slice(1) : [];
2236
- let messageType = null;
2237
- if (type == "message") {
2238
- messageType = keys[0];
2239
- keys = keys.slice(1);
2240
- }
2241
2234
  return this.filter((ctx) => {
2242
- if (type in ctx) {
2243
- if (messageType != null) {
2244
- // deno-lint-ignore ban-ts-comment
2245
- // @ts-ignore
2246
- assertMessageType(ctx[type], messageType);
2247
- }
2248
- if (keys.length > 0) {
2249
- for (const key of keys) {
2250
- // deno-lint-ignore ban-ts-comment
2251
- // @ts-ignore
2252
- if (!(key in ctx[type])) {
2253
- return false;
2254
- }
2255
- }
2256
- }
2257
- return true;
2258
- }
2259
- else {
2260
- return false;
2261
- }
2235
+ return match(filter, ctx);
2262
2236
  }, ...middleawre);
2263
2237
  }
2264
2238
  command(commands, ...middleawre) {
@@ -2276,7 +2250,7 @@ export class Client extends ClientAbstract {
2276
2250
  }
2277
2251
  }
2278
2252
  }
2279
- return this.on(["message", "text"]).filter((ctx) => {
2253
+ return this.on("message:text").filter((ctx) => {
2280
2254
  const prefixes_ = prefixes.length == 0 ? [!ctx.me?.isBot ? "\\" : "/"] : prefixes;
2281
2255
  if (prefixes_.length == 0) {
2282
2256
  return false;
@@ -0,0 +1,2 @@
1
+ export * from "./mutex.js";
2
+ export * from "./semaphore.js";
@@ -0,0 +1,2 @@
1
+ export * from "./mutex.js";
2
+ export * from "./semaphore.js";
@@ -0,0 +1,26 @@
1
+ export interface MutexInterface {
2
+ acquire(): Promise<MutexInterface.Releaser>;
3
+ runExclusive<T>(callback: MutexInterface.Worker<T>): Promise<T>;
4
+ waitForUnlock(): Promise<void>;
5
+ isLocked(): boolean;
6
+ release(): void;
7
+ cancel(): void;
8
+ }
9
+ export declare namespace MutexInterface {
10
+ interface Releaser {
11
+ (): void;
12
+ }
13
+ interface Worker<T> {
14
+ (): Promise<T> | T;
15
+ }
16
+ }
17
+ export declare class Mutex implements MutexInterface {
18
+ constructor(cancelError?: Error);
19
+ acquire(): Promise<MutexInterface.Releaser>;
20
+ runExclusive<T>(callback: MutexInterface.Worker<T>): Promise<T>;
21
+ isLocked(): boolean;
22
+ waitForUnlock(): Promise<void>;
23
+ release(): void;
24
+ cancel(): void;
25
+ private _semaphore;
26
+ }
@@ -0,0 +1,32 @@
1
+ import { Semaphore } from "./semaphore.js";
2
+ export class Mutex {
3
+ constructor(cancelError) {
4
+ Object.defineProperty(this, "_semaphore", {
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true,
8
+ value: void 0
9
+ });
10
+ this._semaphore = new Semaphore(1, cancelError);
11
+ }
12
+ async acquire() {
13
+ const [, releaser] = await this._semaphore.acquire();
14
+ return releaser;
15
+ }
16
+ runExclusive(callback) {
17
+ return this._semaphore.runExclusive(() => callback());
18
+ }
19
+ isLocked() {
20
+ return this._semaphore.isLocked();
21
+ }
22
+ waitForUnlock() {
23
+ return this._semaphore.waitForUnlock();
24
+ }
25
+ release() {
26
+ if (this._semaphore.isLocked())
27
+ this._semaphore.release();
28
+ }
29
+ cancel() {
30
+ return this._semaphore.cancel();
31
+ }
32
+ }
@@ -0,0 +1,41 @@
1
+ export declare const E_CANCELED: Error;
2
+ export interface SemaphoreInterface {
3
+ acquire(weight?: number): Promise<[number, SemaphoreInterface.Releaser]>;
4
+ runExclusive<T>(callback: SemaphoreInterface.Worker<T>, weight?: number): Promise<T>;
5
+ waitForUnlock(weight?: number): Promise<void>;
6
+ isLocked(): boolean;
7
+ getValue(): number;
8
+ setValue(value: number): void;
9
+ release(weight?: number): void;
10
+ cancel(): void;
11
+ }
12
+ export declare namespace SemaphoreInterface {
13
+ interface Releaser {
14
+ (): void;
15
+ }
16
+ interface Worker<T> {
17
+ (value: number): Promise<T> | T;
18
+ }
19
+ }
20
+ export interface QueueEntry {
21
+ resolve(result: [number, SemaphoreInterface.Releaser]): void;
22
+ reject(error: unknown): void;
23
+ }
24
+ export declare class Semaphore implements SemaphoreInterface {
25
+ private _value;
26
+ private _cancelError;
27
+ constructor(_value: number, _cancelError?: Error);
28
+ acquire(weight?: number): Promise<[number, SemaphoreInterface.Releaser]>;
29
+ runExclusive<T>(callback: SemaphoreInterface.Worker<T>, weight?: number): Promise<T>;
30
+ waitForUnlock(weight?: number): Promise<void>;
31
+ isLocked(): boolean;
32
+ getValue(): number;
33
+ setValue(value: number): void;
34
+ release(weight?: number): void;
35
+ cancel(): void;
36
+ private _dispatch;
37
+ private _newReleaser;
38
+ private _drainUnlockWaiters;
39
+ private _weightedQueues;
40
+ private _weightedWaiters;
41
+ }
@@ -0,0 +1,113 @@
1
+ export const E_CANCELED = new Error("request for lock canceled");
2
+ export class Semaphore {
3
+ constructor(_value, _cancelError = E_CANCELED) {
4
+ Object.defineProperty(this, "_value", {
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true,
8
+ value: _value
9
+ });
10
+ Object.defineProperty(this, "_cancelError", {
11
+ enumerable: true,
12
+ configurable: true,
13
+ writable: true,
14
+ value: _cancelError
15
+ });
16
+ Object.defineProperty(this, "_weightedQueues", {
17
+ enumerable: true,
18
+ configurable: true,
19
+ writable: true,
20
+ value: []
21
+ });
22
+ Object.defineProperty(this, "_weightedWaiters", {
23
+ enumerable: true,
24
+ configurable: true,
25
+ writable: true,
26
+ value: []
27
+ });
28
+ }
29
+ acquire(weight = 1) {
30
+ if (weight <= 0) {
31
+ throw new Error(`invalid weight ${weight}: must be positive`);
32
+ }
33
+ return new Promise((resolve, reject) => {
34
+ if (!this._weightedQueues[weight - 1]) {
35
+ this._weightedQueues[weight - 1] = [];
36
+ }
37
+ this._weightedQueues[weight - 1].push({ resolve, reject });
38
+ this._dispatch();
39
+ });
40
+ }
41
+ async runExclusive(callback, weight = 1) {
42
+ const [value, release] = await this.acquire(weight);
43
+ try {
44
+ return await callback(value);
45
+ }
46
+ finally {
47
+ release();
48
+ }
49
+ }
50
+ waitForUnlock(weight = 1) {
51
+ if (weight <= 0) {
52
+ throw new Error(`invalid weight ${weight}: must be positive`);
53
+ }
54
+ return new Promise((resolve) => {
55
+ if (!this._weightedWaiters[weight - 1]) {
56
+ this._weightedWaiters[weight - 1] = [];
57
+ }
58
+ this._weightedWaiters[weight - 1].push(resolve);
59
+ this._dispatch();
60
+ });
61
+ }
62
+ isLocked() {
63
+ return this._value <= 0;
64
+ }
65
+ getValue() {
66
+ return this._value;
67
+ }
68
+ setValue(value) {
69
+ this._value = value;
70
+ this._dispatch();
71
+ }
72
+ release(weight = 1) {
73
+ if (weight <= 0) {
74
+ throw new Error(`invalid weight ${weight}: must be positive`);
75
+ }
76
+ this._value += weight;
77
+ this._dispatch();
78
+ }
79
+ cancel() {
80
+ this._weightedQueues.forEach((queue) => queue.forEach((entry) => entry.reject(this._cancelError)));
81
+ this._weightedQueues = [];
82
+ }
83
+ _dispatch() {
84
+ for (let weight = this._value; weight > 0; weight--) {
85
+ const queueEntry = this._weightedQueues[weight - 1]?.shift();
86
+ if (!queueEntry)
87
+ continue;
88
+ const previousValue = this._value;
89
+ const previousWeight = weight;
90
+ this._value -= weight;
91
+ weight = this._value + 1;
92
+ queueEntry.resolve([previousValue, this._newReleaser(previousWeight)]);
93
+ }
94
+ this._drainUnlockWaiters();
95
+ }
96
+ _newReleaser(weight) {
97
+ let called = false;
98
+ return () => {
99
+ if (called)
100
+ return;
101
+ called = true;
102
+ this.release(weight);
103
+ };
104
+ }
105
+ _drainUnlockWaiters() {
106
+ for (let weight = this._value; weight > 0; weight--) {
107
+ if (!this._weightedWaiters[weight - 1])
108
+ continue;
109
+ this._weightedWaiters[weight - 1].forEach((waiter) => waiter());
110
+ this._weightedWaiters[weight - 1] = [];
111
+ }
112
+ }
113
+ }
@@ -56,7 +56,7 @@ export class StorageLocalStorage extends Storage {
56
56
  if (params?.limit !== undefined) {
57
57
  entries = entries.slice(0, params.limit <= 0 ? 1 : params.limit);
58
58
  }
59
- for (let [key, value] of entries) {
59
+ entries: for (let [key, value] of entries) {
60
60
  if (key.startsWith(this.prefix)) {
61
61
  key = key.slice(this.prefix.length);
62
62
  }
@@ -65,7 +65,7 @@ export class StorageLocalStorage extends Storage {
65
65
  if ("prefix" in filter) {
66
66
  for (const [i, p] of filter.prefix.entries()) {
67
67
  if (toString(p) != toString(parts[i])) {
68
- continue;
68
+ continue entries;
69
69
  }
70
70
  }
71
71
  }
@@ -56,7 +56,7 @@ export class StorageSessionStorage extends Storage {
56
56
  if (params?.limit !== undefined) {
57
57
  entries = entries.slice(0, params.limit <= 0 ? 1 : params.limit);
58
58
  }
59
- for (let [key, value] of entries) {
59
+ entries: for (let [key, value] of entries) {
60
60
  if (key.startsWith(this.prefix)) {
61
61
  key = key.slice(this.prefix.length);
62
62
  }
@@ -65,7 +65,7 @@ export class StorageSessionStorage extends Storage {
65
65
  if ("prefix" in filter) {
66
66
  for (const [i, p] of filter.prefix.entries()) {
67
67
  if (toString(p) != toString(parts[i])) {
68
- continue;
68
+ continue entries;
69
69
  }
70
70
  }
71
71
  }