@mtkruto/node 0.1.102 → 0.1.103

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 (35) hide show
  1. package/esm/4_constants.d.ts +1 -1
  2. package/esm/4_constants.js +1 -1
  3. package/esm/client/3_types.d.ts +25 -1
  4. package/esm/client/4_client.d.ts +28 -32
  5. package/esm/client/4_client.js +21 -8
  6. package/esm/storage/0_storage.d.ts +3 -1
  7. package/esm/storage/0_storage.js +7 -0
  8. package/esm/storage/0_utilities.d.ts +2 -1
  9. package/esm/storage/0_utilities.js +63 -0
  10. package/esm/storage/1_storage_indexed_db.d.ts +2 -1
  11. package/esm/storage/1_storage_indexed_db.js +18 -1
  12. package/esm/storage/1_storage_local_storage.d.ts +1 -0
  13. package/esm/storage/1_storage_local_storage.js +16 -0
  14. package/esm/storage/1_storage_memory.d.ts +1 -0
  15. package/esm/storage/1_storage_memory.js +14 -1
  16. package/esm/storage/1_storage_session_storage.d.ts +1 -0
  17. package/esm/storage/1_storage_session_storage.js +16 -0
  18. package/package.json +1 -1
  19. package/script/4_constants.d.ts +1 -1
  20. package/script/4_constants.js +1 -1
  21. package/script/client/3_types.d.ts +25 -1
  22. package/script/client/4_client.d.ts +28 -32
  23. package/script/client/4_client.js +21 -8
  24. package/script/storage/0_storage.d.ts +3 -1
  25. package/script/storage/0_storage.js +7 -0
  26. package/script/storage/0_utilities.d.ts +2 -1
  27. package/script/storage/0_utilities.js +65 -1
  28. package/script/storage/1_storage_indexed_db.d.ts +2 -1
  29. package/script/storage/1_storage_indexed_db.js +17 -0
  30. package/script/storage/1_storage_local_storage.d.ts +1 -0
  31. package/script/storage/1_storage_local_storage.js +16 -0
  32. package/script/storage/1_storage_memory.d.ts +1 -0
  33. package/script/storage/1_storage_memory.js +13 -0
  34. package/script/storage/1_storage_session_storage.d.ts +1 -0
  35. package/script/storage/1_storage_session_storage.js +16 -0
@@ -5,7 +5,7 @@ export declare const PUBLIC_KEYS: PublicKeys;
5
5
  export declare const VECTOR_CONSTRUCTOR = 481674261;
6
6
  export declare const INITIAL_DC: DC;
7
7
  export declare const LAYER = 161;
8
- export declare const APP_VERSION = "MTKruto 0.1.102";
8
+ export declare const APP_VERSION = "MTKruto 0.1.103";
9
9
  export declare const DEVICE_MODEL: string;
10
10
  export declare const LANG_CODE: string;
11
11
  export declare const LANG_PACK = "";
@@ -54,7 +54,7 @@ export const PUBLIC_KEYS = Object.freeze([
54
54
  export const VECTOR_CONSTRUCTOR = 0x1CB5C415;
55
55
  export const INITIAL_DC = "2-test";
56
56
  export const LAYER = 161;
57
- export const APP_VERSION = "MTKruto 0.1.102";
57
+ export const APP_VERSION = "MTKruto 0.1.103";
58
58
  // @ts-ignore: lib
59
59
  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;
60
60
  export const LANG_CODE = typeof navigator === "undefined" ? "en" : navigator.language.split("-")[0];
@@ -1,6 +1,6 @@
1
1
  import { MaybePromise } from "../1_utilities.js";
2
2
  import { functions, types } from "../2_tl.js";
3
- import { CallbackQuery, ChatID, ForceReply, InlineKeyboardMarkup, InlineQuery, Message, MessageEntity, ReplyKeyboardMarkup, ReplyKeyboardRemove } from "../3_types.js";
3
+ import { BotCommandScope, CallbackQuery, ChatID, ForceReply, InlineKeyboardMarkup, InlineQuery, InlineQueryResultButton, Message, MessageEntity, ReplyKeyboardMarkup, ReplyKeyboardRemove } from "../3_types.js";
4
4
  import { With } from "./0_utilities.js";
5
5
  import { ClientPlainParams } from "./2_client_plain.js";
6
6
  export type ParseMode = "HTML" | null;
@@ -194,6 +194,30 @@ export interface SendPollParams {
194
194
  */
195
195
  protectContent?: boolean;
196
196
  }
197
+ export interface DownloadParams {
198
+ /** Size of each download chunk in bytes. */
199
+ chunkSize?: number;
200
+ }
201
+ export interface UploadParams {
202
+ /** The file name to assign. */
203
+ fileName?: string;
204
+ /** Size of each upload chunk in bytes. */
205
+ chunkSize?: number;
206
+ /** Upload abort signal. */
207
+ signal?: AbortSignal | null;
208
+ }
209
+ export interface AnswerInlineQueryParams {
210
+ cacheTime?: number;
211
+ isPersonal?: boolean;
212
+ nextOffset?: string;
213
+ isGallery?: boolean;
214
+ button?: InlineQueryResultButton;
215
+ }
216
+ export interface SetMyCommandsParams {
217
+ languageCode?: string;
218
+ scope?: BotCommandScope;
219
+ }
220
+ export type GetMyCommandsParams = SetMyCommandsParams;
197
221
  export type ConnectionState = "notConnected" | "updating" | "ready";
198
222
  export type AuthorizationState = {
199
223
  authorized: boolean;
@@ -2,11 +2,11 @@ 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, BotCommandScope, ChatAction, ChatID, InlineQueryResult, InlineQueryResultButton, Message } from "../3_types.js";
5
+ import { BotCommand, ChatAction, ChatID, InlineQueryResult, Message } from "../3_types.js";
6
6
  import { Migrate } from "../4_errors.js";
7
7
  import { With } from "./0_utilities.js";
8
8
  import { ClientAbstract } from "./1_client_abstract.js";
9
- import { AnswerCallbackQueryParams, AuthorizeUserParams, ClientParams, EditMessageParams, FilterableUpdates, FilterUpdate, ForwardMessagesParams, Handler, ParseMode, SendMessagesParams, SendPollParams, Update } from "./3_types.js";
9
+ import { AnswerCallbackQueryParams, AnswerInlineQueryParams, AuthorizeUserParams, ClientParams, DownloadParams, EditMessageParams, FilterableUpdates, FilterUpdate, ForwardMessagesParams, GetMyCommandsParams, Handler, ParseMode, SendMessagesParams, SendPollParams, SetMyCommandsParams, Update, UploadParams } from "./3_types.js";
10
10
  export declare const getEntity: unique symbol;
11
11
  export declare const getStickerSetName: unique symbol;
12
12
  export declare const handleMigrationError: unique symbol;
@@ -168,9 +168,7 @@ export declare class Client extends ClientAbstract {
168
168
  *
169
169
  * @param fileId The identifier of the file to download.
170
170
  */
171
- download(fileId: string, params?: {
172
- chunkSize?: number;
173
- }): Promise<AsyncGenerator<Uint8Array, void, unknown>>;
171
+ download(fileId: string, params?: DownloadParams): Promise<AsyncGenerator<Uint8Array, void, unknown>>;
174
172
  [getStickerSetName](inputStickerSet: types.InputStickerSetID, hash?: number): Promise<string>;
175
173
  /**
176
174
  * Forward multiple messages.
@@ -223,49 +221,47 @@ export declare class Client extends ClientAbstract {
223
221
  *
224
222
  * @param contents The contents of the file.
225
223
  */
226
- upload(contents: Uint8Array, params?: {
227
- fileName?: string;
228
- chunkSize?: number;
229
- signal?: AbortSignal | null;
230
- }): Promise<types.InputFile | types.InputFileBig>;
231
- setMyCommands(commands: BotCommand[], params?: {
232
- languageCode?: string;
233
- scope?: BotCommandScope;
234
- }): Promise<void>;
235
- getMyCommands(params?: {
236
- languageCode?: string;
237
- scope?: BotCommandScope;
238
- }): Promise<BotCommand[]>;
239
- answerInlineQuery(id: string, results: InlineQueryResult[], params?: {
240
- cacheTime?: number;
241
- isPersonal?: boolean;
242
- nextOffset?: string;
243
- isGallery?: boolean;
244
- button: InlineQueryResultButton;
245
- }): Promise<void>;
224
+ upload(contents: Uint8Array, params?: UploadParams): Promise<types.InputFile | types.InputFileBig>;
225
+ /**
226
+ * Set the bot's commands in the given scope and/or language. Bot-only.
227
+ *
228
+ * @param commands The commands to set.
229
+ */
230
+ setMyCommands(commands: BotCommand[], params?: SetMyCommandsParams): Promise<void>;
231
+ /**
232
+ * Get the bot's commands in the given scope and/or language. Bot-only.
233
+ */
234
+ getMyCommands(params?: GetMyCommandsParams): Promise<BotCommand[]>;
235
+ /**
236
+ * Answer an inline query. Bot-only.
237
+ *
238
+ * @param id The ID of the inline query to answer.
239
+ * @param results The results to answer with.
240
+ */
241
+ answerInlineQuery(id: string, results: InlineQueryResult[], params?: AnswerInlineQueryParams): Promise<void>;
246
242
  private handle;
247
243
  use(handler: Handler): void;
248
244
  branch(predicate: (upd: Update) => MaybePromise<boolean>, trueHandler: Handler, falseHandler: Handler): void;
249
245
  filter<D extends Update>(predicate: (update: Update) => update is D, handler: Handler<D>): void;
250
246
  filter(predicate: (update: Update) => MaybePromise<boolean>, handler: Handler): void;
251
247
  on<T extends keyof Update, F extends keyof NonNullable<Update[T]>>(filter: T extends FilterableUpdates ? T | [T, F, ...F[]] : T, handler: Handler<FilterUpdate<Update, T, F>>): void;
252
- setMyInfo(info: Omit<ConstructorParameters<typeof functions["BotsSetBotInfo"]>[0], "bot">): Promise<void>;
248
+ private setMyInfo;
253
249
  /**
254
- * Use this method to change the bot's description, which is shown in the chat with the bot if the chat is empty.
250
+ * Set the bot's description in the given language. Bot-only.
255
251
  */
256
252
  setMyDescription({ description, languageCode }: {
257
253
  description?: string;
258
254
  languageCode?: string;
259
255
  }): Promise<void>;
260
256
  /**
261
- * Use this method to change the bot's name.
257
+ * Set the bot's name in the given language. Bot-only.
262
258
  */
263
259
  setMyName({ name, languageCode }: {
264
260
  name?: string;
265
261
  languageCode?: string;
266
262
  }): Promise<void>;
267
263
  /**
268
- * Use this method to change the bot's short description, which is shown on the bot's profile page and is sent together with the link when users share the bot.
264
+ * Set the bot's short description in the given language. Bot-only.
269
265
  */
270
266
  setMyShortDescription({ shortDescription: about, languageCode }: {
271
267
  shortDescription?: string;
@@ -273,15 +269,15 @@ export declare class Client extends ClientAbstract {
273
269
  }): Promise<void>;
274
270
  private getMyInfo;
275
271
  /**
276
- * Use this method to get the current bot description for the given user language.
272
+ * Get the bot's description in the given language. Bot-only.
277
273
  */
278
274
  getMyDescription(languageCode?: string): Promise<string>;
279
275
  /**
280
- * Use this method to get the current bot name for the given user language.
276
+ * Set the bot's name in the given language. Bot-only.
281
277
  */
282
278
  getMyName(languageCode?: string): Promise<string>;
283
279
  /**
284
- * Use this method to get the current bot short description for the given user language.
280
+ * Get the bot's short description in the given language. Bot-only.
285
281
  */
286
282
  getMyShortDescription(languageCode?: string): Promise<string>;
287
283
  }
@@ -986,8 +986,7 @@ export class Client extends ClientAbstract {
986
986
  }
987
987
  }
988
988
  else if (difference instanceof types.UpdatesDifferenceTooLong) {
989
- // TODO: we actually do now
990
- // stored messages should be invalidated in case we store messages in the future
989
+ await this.storage.deleteMessages();
991
990
  state.pts = difference.pts;
992
991
  dGap("received differenceTooLong");
993
992
  }
@@ -1747,6 +1746,11 @@ export class Client extends ClientAbstract {
1747
1746
  return new types.InputFile({ id: fileId, name, parts: part, md5Checksum: "" });
1748
1747
  }
1749
1748
  }
1749
+ /**
1750
+ * Set the bot's commands in the given scope and/or language. Bot-only.
1751
+ *
1752
+ * @param commands The commands to set.
1753
+ */
1750
1754
  async setMyCommands(commands, params) {
1751
1755
  await this.invoke(new functions.BotsSetBotCommands({
1752
1756
  commands: commands.map((v) => new types.BotCommand(v)),
@@ -1754,6 +1758,9 @@ export class Client extends ClientAbstract {
1754
1758
  scope: await botCommandScopeToTlObject(params?.scope ?? { type: "default" }, this.getInputPeer.bind(this)),
1755
1759
  }));
1756
1760
  }
1761
+ /**
1762
+ * Get the bot's commands in the given scope and/or language. Bot-only.
1763
+ */
1757
1764
  async getMyCommands(params) {
1758
1765
  const commands_ = await this.invoke(new functions.BotsGetBotCommands({
1759
1766
  langCode: params?.languageCode ?? "",
@@ -1761,6 +1768,12 @@ export class Client extends ClientAbstract {
1761
1768
  }));
1762
1769
  return commands_.map((v) => ({ command: v.command, description: v.description }));
1763
1770
  }
1771
+ /**
1772
+ * Answer an inline query. Bot-only.
1773
+ *
1774
+ * @param id The ID of the inline query to answer.
1775
+ * @param results The results to answer with.
1776
+ */
1764
1777
  async answerInlineQuery(id, results, params) {
1765
1778
  await this.invoke(new functions.MessagesSetInlineBotResults({
1766
1779
  queryId: BigInt(id),
@@ -1823,21 +1836,21 @@ export class Client extends ClientAbstract {
1823
1836
  await this.invoke(new functions.BotsSetBotInfo({ bot: new types.InputUserSelf(), ...info }));
1824
1837
  }
1825
1838
  /**
1826
- * Use this method to change the bot's description, which is shown in the chat with the bot if the chat is empty.
1839
+ * Set the bot's description in the given language. Bot-only.
1827
1840
  */
1828
1841
  async setMyDescription({ description, languageCode }) {
1829
1842
  await this.assertBot("setMyDescription");
1830
1843
  await this.setMyInfo({ description, langCode: languageCode ?? "" });
1831
1844
  }
1832
1845
  /**
1833
- * Use this method to change the bot's name.
1846
+ * Set the bot's name in the given language. Bot-only.
1834
1847
  */
1835
1848
  async setMyName({ name, languageCode }) {
1836
1849
  await this.assertBot("setMyName");
1837
1850
  await this.setMyInfo({ name, langCode: languageCode ?? "" });
1838
1851
  }
1839
1852
  /**
1840
- * Use this method to change the bot's short description, which is shown on the bot's profile page and is sent together with the link when users share the bot.
1853
+ * Set the bot's short description in the given language. Bot-only.
1841
1854
  */
1842
1855
  async setMyShortDescription({ shortDescription: about, languageCode }) {
1843
1856
  await this.assertBot("setMyShortDescription");
@@ -1847,21 +1860,21 @@ export class Client extends ClientAbstract {
1847
1860
  return this.invoke(new functions.BotsGetBotInfo({ bot: new types.InputUserSelf(), langCode: languageCode ?? "" }));
1848
1861
  }
1849
1862
  /**
1850
- * Use this method to get the current bot description for the given user language.
1863
+ * Get the bot's description in the given language. Bot-only.
1851
1864
  */
1852
1865
  async getMyDescription(languageCode) {
1853
1866
  await this.assertBot("getMyDescription");
1854
1867
  return await this.getMyInfo(languageCode).then((v) => v.description);
1855
1868
  }
1856
1869
  /**
1857
- * Use this method to get the current bot name for the given user language.
1870
+ * Set the bot's name in the given language. Bot-only.
1858
1871
  */
1859
1872
  async getMyName(languageCode) {
1860
1873
  await this.assertBot("getMyName");
1861
1874
  return await this.getMyInfo(languageCode).then((v) => v.description);
1862
1875
  }
1863
1876
  /**
1864
- * Use this method to get the current bot short description for the given user language.
1877
+ * Get the bot's short description in the given language. Bot-only.
1865
1878
  */
1866
1879
  async getMyShortDescription(languageCode) {
1867
1880
  await this.assertBot("getMyShortDescription");
@@ -1,12 +1,13 @@
1
1
  import { MaybePromise } from "../1_utilities.js";
2
2
  import { TLObject, types } from "../2_tl.js";
3
3
  import { DC } from "../3_transport.js";
4
- export type StorageKeyPart = string | number | bigint | Uint8Array;
4
+ export type StorageKeyPart = string | number | bigint;
5
5
  export declare abstract class Storage {
6
6
  private _authKeyId;
7
7
  abstract init(): MaybePromise<void>;
8
8
  abstract set(key: readonly StorageKeyPart[], value: unknown): MaybePromise<void>;
9
9
  abstract get<T>(key: readonly StorageKeyPart[]): MaybePromise<T | null>;
10
+ abstract getMany<T>(prefix: readonly StorageKeyPart[]): MaybePromise<Generator<[readonly StorageKeyPart[], T]> | AsyncGenerator<[readonly StorageKeyPart[], T]>>;
10
11
  setDc(dc: DC | null): MaybePromise<void>;
11
12
  getDc(): MaybePromise<DC | null>;
12
13
  private resetAuthKeyId;
@@ -24,6 +25,7 @@ export declare abstract class Storage {
24
25
  setState(state: types.UpdatesState): Promise<void>;
25
26
  getState(): Promise<types.UpdatesState | null>;
26
27
  setMessage(chatId: number, messageId: number, message: types.TypeMessage | null): Promise<void>;
28
+ deleteMessages(): Promise<void>;
27
29
  getMessageChat(messageId: number): MaybePromise<number | null>;
28
30
  getMessage(chatId: number, messageId: number): Promise<types.TypeMessage | null>;
29
31
  setChannelPts(channelId: bigint, pts: number): Promise<void>;
@@ -104,6 +104,13 @@ export class Storage {
104
104
  }
105
105
  await this.setTlObject(KPARTS_MESSAGE(chatId, messageId), message);
106
106
  }
107
+ async deleteMessages() {
108
+ const maybePromises = new Array();
109
+ for await (const [k, o] of await this.getMany(["messageRefs"])) {
110
+ maybePromises.push(Promise.all([this.set(k, null), o == null ? Promise.resolve() : this.set(KPARTS_MESSAGE(o, k[1]), null)]).then(() => { }));
111
+ }
112
+ await Promise.all(maybePromises.filter((v) => v instanceof Promise));
113
+ }
107
114
  getMessageChat(messageId) {
108
115
  return this.get(KPARTS_MESSAGE_REF(messageId));
109
116
  }
@@ -10,4 +10,5 @@ export declare enum ValueType {
10
10
  }
11
11
  export declare function toString(value: unknown): string;
12
12
  export declare function fromString<T>(string: string): any;
13
- export declare function fixKey(key: StorageKeyPart[]): (string | number | Uint8Array)[];
13
+ export declare function fixKey(key: readonly StorageKeyPart[]): (string | number)[];
14
+ export declare function getPrefixKeyRange(prefix: any): IDBKeyRange;
@@ -60,3 +60,66 @@ export function fromString(string) {
60
60
  export function fixKey(key) {
61
61
  return key.map((v) => typeof v === "bigint" ? String(v) : v);
62
62
  }
63
+ // Source: https://gist.github.com/inexorabletash/5462871
64
+ // deno-lint-ignore no-explicit-any
65
+ export function getPrefixKeyRange(prefix) {
66
+ // Ensure prefix is a valid key itself:
67
+ if (indexedDB.cmp(prefix, prefix) !== 0)
68
+ throw new TypeError();
69
+ const upperKey = successor(prefix);
70
+ if (upperKey === undefined)
71
+ return IDBKeyRange.lowerBound(prefix);
72
+ return IDBKeyRange.bound(prefix, upperKey, false, true);
73
+ }
74
+ const MAX_DATE_VALUE = 8640000000000000;
75
+ const UPPER_BOUND = {
76
+ NUMBER: new Date(-MAX_DATE_VALUE),
77
+ DATE: "",
78
+ STRING: [],
79
+ ARRAY: undefined,
80
+ };
81
+ // deno-lint-ignore no-explicit-any
82
+ function successor(key) {
83
+ if (typeof key === "number") {
84
+ if (key === Infinity)
85
+ return UPPER_BOUND.NUMBER;
86
+ if (key === -Infinity)
87
+ return -Number.MAX_VALUE;
88
+ if (key === 0)
89
+ return Number.MIN_VALUE;
90
+ let epsilon = Math.abs(key);
91
+ while (key + epsilon / 2 !== key)
92
+ epsilon = epsilon / 2;
93
+ return key + epsilon;
94
+ }
95
+ if (key instanceof Date) {
96
+ if (key.valueOf() + 1 > MAX_DATE_VALUE)
97
+ return UPPER_BOUND.DATE;
98
+ return new Date(key.valueOf() + 1);
99
+ }
100
+ if (typeof key === "string") {
101
+ let len = key.length;
102
+ while (len > 0) {
103
+ const head = key.substring(0, len - 1), tail = key.charCodeAt(len - 1);
104
+ if (tail !== 0xffff)
105
+ return head + String.fromCharCode(tail + 1);
106
+ key = head;
107
+ --len;
108
+ }
109
+ return UPPER_BOUND.STRING;
110
+ }
111
+ if (Array.isArray(key)) {
112
+ key = key.slice(); // Operate on a copy.
113
+ let len = key.length;
114
+ while (len > 0) {
115
+ const tail = successor(key.pop());
116
+ if (tail !== undefined) {
117
+ key.push(tail);
118
+ return key;
119
+ }
120
+ --len;
121
+ }
122
+ return UPPER_BOUND.ARRAY;
123
+ }
124
+ throw new TypeError();
125
+ }
@@ -5,5 +5,6 @@ export declare class StorageIndexedDB extends Storage {
5
5
  constructor(name: string);
6
6
  init(): Promise<void>;
7
7
  set(k: StorageKeyPart[], v: unknown): Promise<void>;
8
- get<T>(k: StorageKeyPart[]): Promise<T | null>;
8
+ get<T>(k: readonly StorageKeyPart[]): Promise<T | null>;
9
+ getMany<T>(prefix: readonly StorageKeyPart[]): AsyncGenerator<[readonly StorageKeyPart[], T], void, unknown>;
9
10
  }
@@ -1,5 +1,5 @@
1
1
  import { Storage } from "./0_storage.js";
2
- import { fixKey } from "./0_utilities.js";
2
+ import { fixKey, getPrefixKeyRange } from "./0_utilities.js";
3
3
  const VERSION = 1;
4
4
  const KV_OBJECT_STORE = "kv";
5
5
  export class StorageIndexedDB extends Storage {
@@ -72,4 +72,21 @@ export class StorageIndexedDB extends Storage {
72
72
  };
73
73
  });
74
74
  }
75
+ async *getMany(prefix) {
76
+ if (!this.database) {
77
+ throw new Error("Not initialized");
78
+ }
79
+ const keys = await new Promise((res, rej) => {
80
+ const tx = this.database.transaction(KV_OBJECT_STORE, "readonly")
81
+ .objectStore(KV_OBJECT_STORE)
82
+ .getAllKeys(getPrefixKeyRange(prefix));
83
+ tx.onerror = rej;
84
+ tx.onsuccess = () => {
85
+ res(tx.result);
86
+ };
87
+ });
88
+ for (const key of keys) {
89
+ yield [key, await this.get(key)];
90
+ }
91
+ }
75
92
  }
@@ -5,5 +5,6 @@ export declare class StorageLocalStorage extends Storage implements Storage {
5
5
  constructor(prefix: string);
6
6
  init(): void;
7
7
  get(key_: readonly StorageKeyPart[]): any;
8
+ getMany<T>(prefix: readonly StorageKeyPart[]): Generator<[readonly StorageKeyPart[], T], void, unknown>;
8
9
  set(key_: readonly StorageKeyPart[], value: unknown): MaybePromise<void>;
9
10
  }
@@ -31,6 +31,22 @@ export class StorageLocalStorage extends Storage {
31
31
  return null;
32
32
  }
33
33
  }
34
+ *getMany(prefix) {
35
+ for (let [key, value] of Object.entries(localStorage)) {
36
+ if (key.startsWith(this.prefix)) {
37
+ key = key.slice(this.prefix.length);
38
+ }
39
+ const parts = fromString(key);
40
+ if (Array.isArray(parts)) {
41
+ for (const [i, p] of prefix.entries()) {
42
+ if (toString(p) != toString(parts[i])) {
43
+ continue;
44
+ }
45
+ yield [parts, fromString(value)];
46
+ }
47
+ }
48
+ }
49
+ }
34
50
  set(key_, value) {
35
51
  const key = this.prefix + toString(key_);
36
52
  if (value != null) {
@@ -4,5 +4,6 @@ export declare class StorageMemory extends Storage implements Storage {
4
4
  protected map: Map<string, unknown>;
5
5
  init(): void;
6
6
  get<T>(key: readonly StorageKeyPart[]): NonNullable<T> | null;
7
+ getMany<T>(prefix: readonly StorageKeyPart[]): Generator<[readonly StorageKeyPart[], T], void, unknown>;
7
8
  set(key_: readonly StorageKeyPart[], value: unknown): MaybePromise<void>;
8
9
  }
@@ -1,5 +1,5 @@
1
1
  import { Storage } from "./0_storage.js";
2
- import { toString } from "./0_utilities.js";
2
+ import { fromString, toString } from "./0_utilities.js";
3
3
  export class StorageMemory extends Storage {
4
4
  constructor() {
5
5
  super(...arguments);
@@ -15,6 +15,19 @@ export class StorageMemory extends Storage {
15
15
  get(key) {
16
16
  return this.map.get(toString(key)) ?? null;
17
17
  }
18
+ *getMany(prefix) {
19
+ for (const [key, value] of this.map.entries()) {
20
+ const parts = fromString(key);
21
+ if (Array.isArray(parts)) {
22
+ for (const [i, p] of prefix.entries()) {
23
+ if (toString(p) != toString(parts[i])) {
24
+ continue;
25
+ }
26
+ yield [parts, value];
27
+ }
28
+ }
29
+ }
30
+ }
18
31
  set(key_, value) {
19
32
  const key = toString(key_);
20
33
  if (value != null) {
@@ -5,5 +5,6 @@ export declare class StorageSessionStorage extends Storage implements Storage {
5
5
  constructor(prefix: string);
6
6
  init(): void;
7
7
  get(key_: readonly StorageKeyPart[]): any;
8
+ getMany<T>(prefix: readonly StorageKeyPart[]): Generator<[readonly StorageKeyPart[], T], void, unknown>;
8
9
  set(key_: readonly StorageKeyPart[], value: unknown): MaybePromise<void>;
9
10
  }
@@ -31,6 +31,22 @@ export class StorageSessionStorage extends Storage {
31
31
  return null;
32
32
  }
33
33
  }
34
+ *getMany(prefix) {
35
+ for (let [key, value] of Object.entries(localStorage)) {
36
+ if (key.startsWith(this.prefix)) {
37
+ key = key.slice(this.prefix.length);
38
+ }
39
+ const parts = fromString(key);
40
+ if (Array.isArray(parts)) {
41
+ for (const [i, p] of prefix.entries()) {
42
+ if (toString(p) != toString(parts[i])) {
43
+ continue;
44
+ }
45
+ yield [parts, fromString(value)];
46
+ }
47
+ }
48
+ }
49
+ }
34
50
  set(key_, value) {
35
51
  const key = this.prefix + toString(key_);
36
52
  if (value != null) {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "module": "./esm/mod.js",
3
3
  "main": "./script/mod.js",
4
4
  "name": "@mtkruto/node",
5
- "version": "0.1.102",
5
+ "version": "0.1.103",
6
6
  "description": "MTKruto for Node.js",
7
7
  "author": "Roj <rojvv@icloud.com>",
8
8
  "license": "LGPL-3.0-or-later",
@@ -5,7 +5,7 @@ export declare const PUBLIC_KEYS: PublicKeys;
5
5
  export declare const VECTOR_CONSTRUCTOR = 481674261;
6
6
  export declare const INITIAL_DC: DC;
7
7
  export declare const LAYER = 161;
8
- export declare const APP_VERSION = "MTKruto 0.1.102";
8
+ export declare const APP_VERSION = "MTKruto 0.1.103";
9
9
  export declare const DEVICE_MODEL: string;
10
10
  export declare const LANG_CODE: string;
11
11
  export declare const LANG_PACK = "";
@@ -80,7 +80,7 @@ exports.PUBLIC_KEYS = Object.freeze([
80
80
  exports.VECTOR_CONSTRUCTOR = 0x1CB5C415;
81
81
  exports.INITIAL_DC = "2-test";
82
82
  exports.LAYER = 161;
83
- exports.APP_VERSION = "MTKruto 0.1.102";
83
+ exports.APP_VERSION = "MTKruto 0.1.103";
84
84
  // @ts-ignore: lib
85
85
  exports.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;
86
86
  exports.LANG_CODE = typeof navigator === "undefined" ? "en" : navigator.language.split("-")[0];
@@ -1,6 +1,6 @@
1
1
  import { MaybePromise } from "../1_utilities.js";
2
2
  import { functions, types } from "../2_tl.js";
3
- import { CallbackQuery, ChatID, ForceReply, InlineKeyboardMarkup, InlineQuery, Message, MessageEntity, ReplyKeyboardMarkup, ReplyKeyboardRemove } from "../3_types.js";
3
+ import { BotCommandScope, CallbackQuery, ChatID, ForceReply, InlineKeyboardMarkup, InlineQuery, InlineQueryResultButton, Message, MessageEntity, ReplyKeyboardMarkup, ReplyKeyboardRemove } from "../3_types.js";
4
4
  import { With } from "./0_utilities.js";
5
5
  import { ClientPlainParams } from "./2_client_plain.js";
6
6
  export type ParseMode = "HTML" | null;
@@ -194,6 +194,30 @@ export interface SendPollParams {
194
194
  */
195
195
  protectContent?: boolean;
196
196
  }
197
+ export interface DownloadParams {
198
+ /** Size of each download chunk in bytes. */
199
+ chunkSize?: number;
200
+ }
201
+ export interface UploadParams {
202
+ /** The file name to assign. */
203
+ fileName?: string;
204
+ /** Size of each upload chunk in bytes. */
205
+ chunkSize?: number;
206
+ /** Upload abort signal. */
207
+ signal?: AbortSignal | null;
208
+ }
209
+ export interface AnswerInlineQueryParams {
210
+ cacheTime?: number;
211
+ isPersonal?: boolean;
212
+ nextOffset?: string;
213
+ isGallery?: boolean;
214
+ button?: InlineQueryResultButton;
215
+ }
216
+ export interface SetMyCommandsParams {
217
+ languageCode?: string;
218
+ scope?: BotCommandScope;
219
+ }
220
+ export type GetMyCommandsParams = SetMyCommandsParams;
197
221
  export type ConnectionState = "notConnected" | "updating" | "ready";
198
222
  export type AuthorizationState = {
199
223
  authorized: boolean;
@@ -2,11 +2,11 @@ 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, BotCommandScope, ChatAction, ChatID, InlineQueryResult, InlineQueryResultButton, Message } from "../3_types.js";
5
+ import { BotCommand, ChatAction, ChatID, InlineQueryResult, Message } from "../3_types.js";
6
6
  import { Migrate } from "../4_errors.js";
7
7
  import { With } from "./0_utilities.js";
8
8
  import { ClientAbstract } from "./1_client_abstract.js";
9
- import { AnswerCallbackQueryParams, AuthorizeUserParams, ClientParams, EditMessageParams, FilterableUpdates, FilterUpdate, ForwardMessagesParams, Handler, ParseMode, SendMessagesParams, SendPollParams, Update } from "./3_types.js";
9
+ import { AnswerCallbackQueryParams, AnswerInlineQueryParams, AuthorizeUserParams, ClientParams, DownloadParams, EditMessageParams, FilterableUpdates, FilterUpdate, ForwardMessagesParams, GetMyCommandsParams, Handler, ParseMode, SendMessagesParams, SendPollParams, SetMyCommandsParams, Update, UploadParams } from "./3_types.js";
10
10
  export declare const getEntity: unique symbol;
11
11
  export declare const getStickerSetName: unique symbol;
12
12
  export declare const handleMigrationError: unique symbol;
@@ -168,9 +168,7 @@ export declare class Client extends ClientAbstract {
168
168
  *
169
169
  * @param fileId The identifier of the file to download.
170
170
  */
171
- download(fileId: string, params?: {
172
- chunkSize?: number;
173
- }): Promise<AsyncGenerator<Uint8Array, void, unknown>>;
171
+ download(fileId: string, params?: DownloadParams): Promise<AsyncGenerator<Uint8Array, void, unknown>>;
174
172
  [getStickerSetName](inputStickerSet: types.InputStickerSetID, hash?: number): Promise<string>;
175
173
  /**
176
174
  * Forward multiple messages.
@@ -223,49 +221,47 @@ export declare class Client extends ClientAbstract {
223
221
  *
224
222
  * @param contents The contents of the file.
225
223
  */
226
- upload(contents: Uint8Array, params?: {
227
- fileName?: string;
228
- chunkSize?: number;
229
- signal?: AbortSignal | null;
230
- }): Promise<types.InputFile | types.InputFileBig>;
231
- setMyCommands(commands: BotCommand[], params?: {
232
- languageCode?: string;
233
- scope?: BotCommandScope;
234
- }): Promise<void>;
235
- getMyCommands(params?: {
236
- languageCode?: string;
237
- scope?: BotCommandScope;
238
- }): Promise<BotCommand[]>;
239
- answerInlineQuery(id: string, results: InlineQueryResult[], params?: {
240
- cacheTime?: number;
241
- isPersonal?: boolean;
242
- nextOffset?: string;
243
- isGallery?: boolean;
244
- button: InlineQueryResultButton;
245
- }): Promise<void>;
224
+ upload(contents: Uint8Array, params?: UploadParams): Promise<types.InputFile | types.InputFileBig>;
225
+ /**
226
+ * Set the bot's commands in the given scope and/or language. Bot-only.
227
+ *
228
+ * @param commands The commands to set.
229
+ */
230
+ setMyCommands(commands: BotCommand[], params?: SetMyCommandsParams): Promise<void>;
231
+ /**
232
+ * Get the bot's commands in the given scope and/or language. Bot-only.
233
+ */
234
+ getMyCommands(params?: GetMyCommandsParams): Promise<BotCommand[]>;
235
+ /**
236
+ * Answer an inline query. Bot-only.
237
+ *
238
+ * @param id The ID of the inline query to answer.
239
+ * @param results The results to answer with.
240
+ */
241
+ answerInlineQuery(id: string, results: InlineQueryResult[], params?: AnswerInlineQueryParams): Promise<void>;
246
242
  private handle;
247
243
  use(handler: Handler): void;
248
244
  branch(predicate: (upd: Update) => MaybePromise<boolean>, trueHandler: Handler, falseHandler: Handler): void;
249
245
  filter<D extends Update>(predicate: (update: Update) => update is D, handler: Handler<D>): void;
250
246
  filter(predicate: (update: Update) => MaybePromise<boolean>, handler: Handler): void;
251
247
  on<T extends keyof Update, F extends keyof NonNullable<Update[T]>>(filter: T extends FilterableUpdates ? T | [T, F, ...F[]] : T, handler: Handler<FilterUpdate<Update, T, F>>): void;
252
- setMyInfo(info: Omit<ConstructorParameters<typeof functions["BotsSetBotInfo"]>[0], "bot">): Promise<void>;
248
+ private setMyInfo;
253
249
  /**
254
- * Use this method to change the bot's description, which is shown in the chat with the bot if the chat is empty.
250
+ * Set the bot's description in the given language. Bot-only.
255
251
  */
256
252
  setMyDescription({ description, languageCode }: {
257
253
  description?: string;
258
254
  languageCode?: string;
259
255
  }): Promise<void>;
260
256
  /**
261
- * Use this method to change the bot's name.
257
+ * Set the bot's name in the given language. Bot-only.
262
258
  */
263
259
  setMyName({ name, languageCode }: {
264
260
  name?: string;
265
261
  languageCode?: string;
266
262
  }): Promise<void>;
267
263
  /**
268
- * Use this method to change the bot's short description, which is shown on the bot's profile page and is sent together with the link when users share the bot.
264
+ * Set the bot's short description in the given language. Bot-only.
269
265
  */
270
266
  setMyShortDescription({ shortDescription: about, languageCode }: {
271
267
  shortDescription?: string;
@@ -273,15 +269,15 @@ export declare class Client extends ClientAbstract {
273
269
  }): Promise<void>;
274
270
  private getMyInfo;
275
271
  /**
276
- * Use this method to get the current bot description for the given user language.
272
+ * Get the bot's description in the given language. Bot-only.
277
273
  */
278
274
  getMyDescription(languageCode?: string): Promise<string>;
279
275
  /**
280
- * Use this method to get the current bot name for the given user language.
276
+ * Set the bot's name in the given language. Bot-only.
281
277
  */
282
278
  getMyName(languageCode?: string): Promise<string>;
283
279
  /**
284
- * Use this method to get the current bot short description for the given user language.
280
+ * Get the bot's short description in the given language. Bot-only.
285
281
  */
286
282
  getMyShortDescription(languageCode?: string): Promise<string>;
287
283
  }
@@ -990,8 +990,7 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
990
990
  }
991
991
  }
992
992
  else if (difference instanceof _2_tl_js_1.types.UpdatesDifferenceTooLong) {
993
- // TODO: we actually do now
994
- // stored messages should be invalidated in case we store messages in the future
993
+ await this.storage.deleteMessages();
995
994
  state.pts = difference.pts;
996
995
  dGap("received differenceTooLong");
997
996
  }
@@ -1751,6 +1750,11 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
1751
1750
  return new _2_tl_js_1.types.InputFile({ id: fileId, name, parts: part, md5Checksum: "" });
1752
1751
  }
1753
1752
  }
1753
+ /**
1754
+ * Set the bot's commands in the given scope and/or language. Bot-only.
1755
+ *
1756
+ * @param commands The commands to set.
1757
+ */
1754
1758
  async setMyCommands(commands, params) {
1755
1759
  await this.invoke(new _2_tl_js_1.functions.BotsSetBotCommands({
1756
1760
  commands: commands.map((v) => new _2_tl_js_1.types.BotCommand(v)),
@@ -1758,6 +1762,9 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
1758
1762
  scope: await (0, _3_types_js_1.botCommandScopeToTlObject)(params?.scope ?? { type: "default" }, this.getInputPeer.bind(this)),
1759
1763
  }));
1760
1764
  }
1765
+ /**
1766
+ * Get the bot's commands in the given scope and/or language. Bot-only.
1767
+ */
1761
1768
  async getMyCommands(params) {
1762
1769
  const commands_ = await this.invoke(new _2_tl_js_1.functions.BotsGetBotCommands({
1763
1770
  langCode: params?.languageCode ?? "",
@@ -1765,6 +1772,12 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
1765
1772
  }));
1766
1773
  return commands_.map((v) => ({ command: v.command, description: v.description }));
1767
1774
  }
1775
+ /**
1776
+ * Answer an inline query. Bot-only.
1777
+ *
1778
+ * @param id The ID of the inline query to answer.
1779
+ * @param results The results to answer with.
1780
+ */
1768
1781
  async answerInlineQuery(id, results, params) {
1769
1782
  await this.invoke(new _2_tl_js_1.functions.MessagesSetInlineBotResults({
1770
1783
  queryId: BigInt(id),
@@ -1827,21 +1840,21 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
1827
1840
  await this.invoke(new _2_tl_js_1.functions.BotsSetBotInfo({ bot: new _2_tl_js_1.types.InputUserSelf(), ...info }));
1828
1841
  }
1829
1842
  /**
1830
- * Use this method to change the bot's description, which is shown in the chat with the bot if the chat is empty.
1843
+ * Set the bot's description in the given language. Bot-only.
1831
1844
  */
1832
1845
  async setMyDescription({ description, languageCode }) {
1833
1846
  await this.assertBot("setMyDescription");
1834
1847
  await this.setMyInfo({ description, langCode: languageCode ?? "" });
1835
1848
  }
1836
1849
  /**
1837
- * Use this method to change the bot's name.
1850
+ * Set the bot's name in the given language. Bot-only.
1838
1851
  */
1839
1852
  async setMyName({ name, languageCode }) {
1840
1853
  await this.assertBot("setMyName");
1841
1854
  await this.setMyInfo({ name, langCode: languageCode ?? "" });
1842
1855
  }
1843
1856
  /**
1844
- * Use this method to change the bot's short description, which is shown on the bot's profile page and is sent together with the link when users share the bot.
1857
+ * Set the bot's short description in the given language. Bot-only.
1845
1858
  */
1846
1859
  async setMyShortDescription({ shortDescription: about, languageCode }) {
1847
1860
  await this.assertBot("setMyShortDescription");
@@ -1851,21 +1864,21 @@ class Client extends _1_client_abstract_js_1.ClientAbstract {
1851
1864
  return this.invoke(new _2_tl_js_1.functions.BotsGetBotInfo({ bot: new _2_tl_js_1.types.InputUserSelf(), langCode: languageCode ?? "" }));
1852
1865
  }
1853
1866
  /**
1854
- * Use this method to get the current bot description for the given user language.
1867
+ * Get the bot's description in the given language. Bot-only.
1855
1868
  */
1856
1869
  async getMyDescription(languageCode) {
1857
1870
  await this.assertBot("getMyDescription");
1858
1871
  return await this.getMyInfo(languageCode).then((v) => v.description);
1859
1872
  }
1860
1873
  /**
1861
- * Use this method to get the current bot name for the given user language.
1874
+ * Set the bot's name in the given language. Bot-only.
1862
1875
  */
1863
1876
  async getMyName(languageCode) {
1864
1877
  await this.assertBot("getMyName");
1865
1878
  return await this.getMyInfo(languageCode).then((v) => v.description);
1866
1879
  }
1867
1880
  /**
1868
- * Use this method to get the current bot short description for the given user language.
1881
+ * Get the bot's short description in the given language. Bot-only.
1869
1882
  */
1870
1883
  async getMyShortDescription(languageCode) {
1871
1884
  await this.assertBot("getMyShortDescription");
@@ -1,12 +1,13 @@
1
1
  import { MaybePromise } from "../1_utilities.js";
2
2
  import { TLObject, types } from "../2_tl.js";
3
3
  import { DC } from "../3_transport.js";
4
- export type StorageKeyPart = string | number | bigint | Uint8Array;
4
+ export type StorageKeyPart = string | number | bigint;
5
5
  export declare abstract class Storage {
6
6
  private _authKeyId;
7
7
  abstract init(): MaybePromise<void>;
8
8
  abstract set(key: readonly StorageKeyPart[], value: unknown): MaybePromise<void>;
9
9
  abstract get<T>(key: readonly StorageKeyPart[]): MaybePromise<T | null>;
10
+ abstract getMany<T>(prefix: readonly StorageKeyPart[]): MaybePromise<Generator<[readonly StorageKeyPart[], T]> | AsyncGenerator<[readonly StorageKeyPart[], T]>>;
10
11
  setDc(dc: DC | null): MaybePromise<void>;
11
12
  getDc(): MaybePromise<DC | null>;
12
13
  private resetAuthKeyId;
@@ -24,6 +25,7 @@ export declare abstract class Storage {
24
25
  setState(state: types.UpdatesState): Promise<void>;
25
26
  getState(): Promise<types.UpdatesState | null>;
26
27
  setMessage(chatId: number, messageId: number, message: types.TypeMessage | null): Promise<void>;
28
+ deleteMessages(): Promise<void>;
27
29
  getMessageChat(messageId: number): MaybePromise<number | null>;
28
30
  getMessage(chatId: number, messageId: number): Promise<types.TypeMessage | null>;
29
31
  setChannelPts(channelId: bigint, pts: number): Promise<void>;
@@ -107,6 +107,13 @@ class Storage {
107
107
  }
108
108
  await this.setTlObject(KPARTS_MESSAGE(chatId, messageId), message);
109
109
  }
110
+ async deleteMessages() {
111
+ const maybePromises = new Array();
112
+ for await (const [k, o] of await this.getMany(["messageRefs"])) {
113
+ maybePromises.push(Promise.all([this.set(k, null), o == null ? Promise.resolve() : this.set(KPARTS_MESSAGE(o, k[1]), null)]).then(() => { }));
114
+ }
115
+ await Promise.all(maybePromises.filter((v) => v instanceof Promise));
116
+ }
110
117
  getMessageChat(messageId) {
111
118
  return this.get(KPARTS_MESSAGE_REF(messageId));
112
119
  }
@@ -10,4 +10,5 @@ export declare enum ValueType {
10
10
  }
11
11
  export declare function toString(value: unknown): string;
12
12
  export declare function fromString<T>(string: string): any;
13
- export declare function fixKey(key: StorageKeyPart[]): (string | number | Uint8Array)[];
13
+ export declare function fixKey(key: readonly StorageKeyPart[]): (string | number)[];
14
+ export declare function getPrefixKeyRange(prefix: any): IDBKeyRange;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fixKey = exports.fromString = exports.toString = exports.ValueType = void 0;
3
+ exports.getPrefixKeyRange = exports.fixKey = exports.fromString = exports.toString = exports.ValueType = void 0;
4
4
  const _0_deps_js_1 = require("../0_deps.js");
5
5
  const _1_utilities_js_1 = require("../1_utilities.js");
6
6
  var ValueType;
@@ -66,3 +66,67 @@ function fixKey(key) {
66
66
  return key.map((v) => typeof v === "bigint" ? String(v) : v);
67
67
  }
68
68
  exports.fixKey = fixKey;
69
+ // Source: https://gist.github.com/inexorabletash/5462871
70
+ // deno-lint-ignore no-explicit-any
71
+ function getPrefixKeyRange(prefix) {
72
+ // Ensure prefix is a valid key itself:
73
+ if (indexedDB.cmp(prefix, prefix) !== 0)
74
+ throw new TypeError();
75
+ const upperKey = successor(prefix);
76
+ if (upperKey === undefined)
77
+ return IDBKeyRange.lowerBound(prefix);
78
+ return IDBKeyRange.bound(prefix, upperKey, false, true);
79
+ }
80
+ exports.getPrefixKeyRange = getPrefixKeyRange;
81
+ const MAX_DATE_VALUE = 8640000000000000;
82
+ const UPPER_BOUND = {
83
+ NUMBER: new Date(-MAX_DATE_VALUE),
84
+ DATE: "",
85
+ STRING: [],
86
+ ARRAY: undefined,
87
+ };
88
+ // deno-lint-ignore no-explicit-any
89
+ function successor(key) {
90
+ if (typeof key === "number") {
91
+ if (key === Infinity)
92
+ return UPPER_BOUND.NUMBER;
93
+ if (key === -Infinity)
94
+ return -Number.MAX_VALUE;
95
+ if (key === 0)
96
+ return Number.MIN_VALUE;
97
+ let epsilon = Math.abs(key);
98
+ while (key + epsilon / 2 !== key)
99
+ epsilon = epsilon / 2;
100
+ return key + epsilon;
101
+ }
102
+ if (key instanceof Date) {
103
+ if (key.valueOf() + 1 > MAX_DATE_VALUE)
104
+ return UPPER_BOUND.DATE;
105
+ return new Date(key.valueOf() + 1);
106
+ }
107
+ if (typeof key === "string") {
108
+ let len = key.length;
109
+ while (len > 0) {
110
+ const head = key.substring(0, len - 1), tail = key.charCodeAt(len - 1);
111
+ if (tail !== 0xffff)
112
+ return head + String.fromCharCode(tail + 1);
113
+ key = head;
114
+ --len;
115
+ }
116
+ return UPPER_BOUND.STRING;
117
+ }
118
+ if (Array.isArray(key)) {
119
+ key = key.slice(); // Operate on a copy.
120
+ let len = key.length;
121
+ while (len > 0) {
122
+ const tail = successor(key.pop());
123
+ if (tail !== undefined) {
124
+ key.push(tail);
125
+ return key;
126
+ }
127
+ --len;
128
+ }
129
+ return UPPER_BOUND.ARRAY;
130
+ }
131
+ throw new TypeError();
132
+ }
@@ -5,5 +5,6 @@ export declare class StorageIndexedDB extends Storage {
5
5
  constructor(name: string);
6
6
  init(): Promise<void>;
7
7
  set(k: StorageKeyPart[], v: unknown): Promise<void>;
8
- get<T>(k: StorageKeyPart[]): Promise<T | null>;
8
+ get<T>(k: readonly StorageKeyPart[]): Promise<T | null>;
9
+ getMany<T>(prefix: readonly StorageKeyPart[]): AsyncGenerator<[readonly StorageKeyPart[], T], void, unknown>;
9
10
  }
@@ -75,5 +75,22 @@ class StorageIndexedDB extends _0_storage_js_1.Storage {
75
75
  };
76
76
  });
77
77
  }
78
+ async *getMany(prefix) {
79
+ if (!this.database) {
80
+ throw new Error("Not initialized");
81
+ }
82
+ const keys = await new Promise((res, rej) => {
83
+ const tx = this.database.transaction(KV_OBJECT_STORE, "readonly")
84
+ .objectStore(KV_OBJECT_STORE)
85
+ .getAllKeys((0, _0_utilities_js_1.getPrefixKeyRange)(prefix));
86
+ tx.onerror = rej;
87
+ tx.onsuccess = () => {
88
+ res(tx.result);
89
+ };
90
+ });
91
+ for (const key of keys) {
92
+ yield [key, await this.get(key)];
93
+ }
94
+ }
78
95
  }
79
96
  exports.StorageIndexedDB = StorageIndexedDB;
@@ -5,5 +5,6 @@ export declare class StorageLocalStorage extends Storage implements Storage {
5
5
  constructor(prefix: string);
6
6
  init(): void;
7
7
  get(key_: readonly StorageKeyPart[]): any;
8
+ getMany<T>(prefix: readonly StorageKeyPart[]): Generator<[readonly StorageKeyPart[], T], void, unknown>;
8
9
  set(key_: readonly StorageKeyPart[], value: unknown): MaybePromise<void>;
9
10
  }
@@ -34,6 +34,22 @@ class StorageLocalStorage extends _0_storage_js_1.Storage {
34
34
  return null;
35
35
  }
36
36
  }
37
+ *getMany(prefix) {
38
+ for (let [key, value] of Object.entries(localStorage)) {
39
+ if (key.startsWith(this.prefix)) {
40
+ key = key.slice(this.prefix.length);
41
+ }
42
+ const parts = (0, _0_utilities_js_1.fromString)(key);
43
+ if (Array.isArray(parts)) {
44
+ for (const [i, p] of prefix.entries()) {
45
+ if ((0, _0_utilities_js_1.toString)(p) != (0, _0_utilities_js_1.toString)(parts[i])) {
46
+ continue;
47
+ }
48
+ yield [parts, (0, _0_utilities_js_1.fromString)(value)];
49
+ }
50
+ }
51
+ }
52
+ }
37
53
  set(key_, value) {
38
54
  const key = this.prefix + (0, _0_utilities_js_1.toString)(key_);
39
55
  if (value != null) {
@@ -4,5 +4,6 @@ export declare class StorageMemory extends Storage implements Storage {
4
4
  protected map: Map<string, unknown>;
5
5
  init(): void;
6
6
  get<T>(key: readonly StorageKeyPart[]): NonNullable<T> | null;
7
+ getMany<T>(prefix: readonly StorageKeyPart[]): Generator<[readonly StorageKeyPart[], T], void, unknown>;
7
8
  set(key_: readonly StorageKeyPart[], value: unknown): MaybePromise<void>;
8
9
  }
@@ -18,6 +18,19 @@ class StorageMemory extends _0_storage_js_1.Storage {
18
18
  get(key) {
19
19
  return this.map.get((0, _0_utilities_js_1.toString)(key)) ?? null;
20
20
  }
21
+ *getMany(prefix) {
22
+ for (const [key, value] of this.map.entries()) {
23
+ const parts = (0, _0_utilities_js_1.fromString)(key);
24
+ if (Array.isArray(parts)) {
25
+ for (const [i, p] of prefix.entries()) {
26
+ if ((0, _0_utilities_js_1.toString)(p) != (0, _0_utilities_js_1.toString)(parts[i])) {
27
+ continue;
28
+ }
29
+ yield [parts, value];
30
+ }
31
+ }
32
+ }
33
+ }
21
34
  set(key_, value) {
22
35
  const key = (0, _0_utilities_js_1.toString)(key_);
23
36
  if (value != null) {
@@ -5,5 +5,6 @@ export declare class StorageSessionStorage extends Storage implements Storage {
5
5
  constructor(prefix: string);
6
6
  init(): void;
7
7
  get(key_: readonly StorageKeyPart[]): any;
8
+ getMany<T>(prefix: readonly StorageKeyPart[]): Generator<[readonly StorageKeyPart[], T], void, unknown>;
8
9
  set(key_: readonly StorageKeyPart[], value: unknown): MaybePromise<void>;
9
10
  }
@@ -34,6 +34,22 @@ class StorageSessionStorage extends _0_storage_js_1.Storage {
34
34
  return null;
35
35
  }
36
36
  }
37
+ *getMany(prefix) {
38
+ for (let [key, value] of Object.entries(localStorage)) {
39
+ if (key.startsWith(this.prefix)) {
40
+ key = key.slice(this.prefix.length);
41
+ }
42
+ const parts = (0, _0_utilities_js_1.fromString)(key);
43
+ if (Array.isArray(parts)) {
44
+ for (const [i, p] of prefix.entries()) {
45
+ if ((0, _0_utilities_js_1.toString)(p) != (0, _0_utilities_js_1.toString)(parts[i])) {
46
+ continue;
47
+ }
48
+ yield [parts, (0, _0_utilities_js_1.fromString)(value)];
49
+ }
50
+ }
51
+ }
52
+ }
37
53
  set(key_, value) {
38
54
  const key = this.prefix + (0, _0_utilities_js_1.toString)(key_);
39
55
  if (value != null) {