itd-api 0.0.8 → 0.0.10
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 +155 -253
- package/dist/{chunk-ATCZ4T2K.cjs → chunk-BN4AC3DP.cjs} +3303 -1628
- package/dist/chunk-BN4AC3DP.cjs.map +1 -0
- package/dist/{chunk-JIYN33FG.js → chunk-SMV7TF5P.js} +3283 -1629
- package/dist/chunk-SMV7TF5P.js.map +1 -0
- package/dist/{index-Duh31Wnx.d.cts → index-CSjDNGCE.d.cts} +1298 -410
- package/dist/{index-Duh31Wnx.d.ts → index-CSjDNGCE.d.ts} +1298 -410
- package/dist/index.cjs +169 -89
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/node.cjs +266 -124
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +42 -4
- package/dist/node.d.ts +42 -4
- package/dist/node.js +106 -39
- package/dist/node.js.map +1 -1
- package/guides/README.md +22 -0
- package/guides/authentication/README.md +176 -0
- package/guides/authentication/examples/bot-with-session.mjs +98 -0
- package/guides/authentication/examples/turnstile-login.mjs +56 -0
- package/guides/integrations/README.md +62 -0
- package/guides/integrations/examples/proxy.mjs +26 -0
- package/guides/multi-accounts/README.md +142 -0
- package/guides/multi-accounts/examples/multi-accounts.mjs +71 -0
- package/guides/plugins/README.md +162 -0
- package/guides/plugins/examples/cache.mjs +33 -0
- package/guides/plugins/examples/crypto.mjs +54 -0
- package/guides/quickstart/README.md +124 -0
- package/guides/quickstart/examples/quick-start.mjs +44 -0
- package/guides/quickstart/examples/typescript.ts +90 -0
- package/guides/realtime/README.md +109 -0
- package/guides/realtime/examples/notifications.mjs +62 -0
- package/guides/text-markup/README.md +214 -0
- package/guides/text-markup/examples/create-post.mjs +64 -0
- package/package.json +6 -4
- package/dist/chunk-ATCZ4T2K.cjs.map +0 -1
- package/dist/chunk-JIYN33FG.js.map +0 -1
package/dist/node.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/node.ts"],"names":["ItdClient"],"mappings":";;;;;AAgCO,IAAM,cAAA,GAA6B,OAAO,IAAA,KAAS;AACxD,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,OAAO,aAAkB,CAAA;AACpD,EAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,OAAO,MAAW,CAAA;AAE7C,EAAA,OAAO,EAAE,IAAA,EAAM,IAAI,UAAA,CAAW,MAAM,QAAA,CAAS,IAAI,CAAC,CAAA,EAAG,QAAA,EAAU,QAAA,CAAS,IAAI,CAAA,EAAE;AAChF;AAkBO,IAAM,mBAAN,MAA+C;AAAA,EAC3C,KAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAKT,QAAA,GAA0B,QAAQ,OAAA,EAAQ;AAAA;AAAA,EAG1C,YAAY,IAAA,EAAc;AACxB,IAAA,IAAA,CAAK,KAAA,GAAQ,IAAA;AAAA,EACf;AAAA,EAEA,MAAM,GAAA,GAAkC;AACtC,IAAA,IAAI;AACF,MAAA,MAAM,EAAE,QAAA,EAAS,GAAI,MAAM,OAAO,aAAkB,CAAA;AACpD,MAAA,MAAM,GAAA,GAAM,MAAM,QAAA,CAAS,IAAA,CAAK,OAAO,MAAM,CAAA;AAC7C,MAAA,MAAM,MAAA,GAAkB,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAEtC,MAAA,OAAO,OAAO,MAAA,KAAW,QAAA,IAAY,MAAA,KAAW,OAAQ,MAAA,GAAwB,IAAA;AAAA,IAClF,CAAA,CAAA,MAAQ;AAEN,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AAAA,EAEA,IAAI,OAAA,EAAoC;AACtC,IAAA,IAAA,CAAK,QAAA,GAAW,KAAK,QAAA,CAAS,IAAA;AAAA,MAC5B,MAAM,IAAA,CAAK,MAAA,CAAO,OAAO,CAAA;AAAA,MACzB,MAAM,IAAA,CAAK,MAAA,CAAO,OAAO;AAAA,KAC3B;AACA,IAAA,OAAO,IAAA,CAAK,QAAA;AAAA,EACd;AAAA,EAEA,MAAM,OAAO,OAAA,EAAoC;AAC/C,IAAA,MAAM,EAAE,SAAA,EAAW,MAAA,EAAQ,QAAO,GAAI,MAAM,OAAO,aAAkB,CAAA;AAIrE,IAAA,MAAM,GAAA,GAAM,OAAO,OAAA,KAAY,WAAA,GAAc,QAAQ,GAAA,GAAM,CAAA;AAC3D,IAAA,MAAM,SAAA,GAAY,CAAA,EAAG,IAAA,CAAK,KAAK,IAAI,GAAG,CAAA,CAAA,EAAI,IAAA,CAAK,MAAA,GAAS,QAAA,CAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAC,CAAC,CAAA,IAAA,CAAA;AAE7E,IAAA,IAAI;AACF,MAAA,MAAM,UAAU,SAAA,EAAW,IAAA,CAAK,UAAU,OAAA,EAAS,IAAA,EAAM,CAAC,CAAA,EAAG;AAAA,QAC3D,QAAA,EAAU,MAAA;AAAA,QACV,IAAA,EAAM;AAAA,OACP,CAAA;AACD,MAAA,MAAM,MAAA,CAAO,SAAA,EAAW,IAAA,CAAK,KAAK,CAAA;AAAA,IACpC,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,MAAA,CAAO,SAAS,CAAA,CAAE,KAAA,CAAM,MAAM;AAAA,MAAC,CAAC,CAAA;AACtC,MAAA,MAAM,KAAA;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,KAAA,GAAuB;AAC3B,IAAA,IAAI;AACF,MAAA,MAAM,EAAE,EAAA,EAAG,GAAI,MAAM,OAAO,aAAkB,CAAA;AAC9C,MAAA,MAAM,GAAG,IAAA,CAAK,KAAA,EAAO,EAAE,KAAA,EAAO,MAAM,CAAA;AAAA,IACtC,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AACF;AAQO,IAAMA,UAAAA,GAAN,cAAwBA,2BAAA,CAAW;AAAA,EACxC,WAAA,CAAY,OAAA,GAA4B,EAAC,EAAG;AAG1C,IAAA,KAAA,CAAM,OAAA,EAAS,EAAE,UAAA,EAAY,cAAA,EAAgB,CAAA;AAAA,EAC/C;AACF;AAGO,SAAS,YAAA,CAAa,OAAA,GAA4B,EAAC,EAAc;AACtE,EAAA,OAAO,IAAIA,WAAU,OAAO,CAAA;AAC9B","file":"node.cjs","sourcesContent":["/**\n * `itd-api/node` — то же, что `itd-api`, плюс работа с файловой системой.\n *\n * Импортируйте эту точку входа в Node, Bun и Deno: клиент отсюда умеет загружать файлы\n * по пути (`attach('./photo.jpg')`), а `FileTokenStorage` сохраняет сессию между запусками.\n * В основном бандле работы с файлами нет намеренно — иначе браузерные сборщики пытались бы\n * разрешить `node:fs`.\n *\n * @example\n * ```ts\n * import { ItdClient, FileTokenStorage } from 'itd-api/node';\n *\n * // Когда сессия уже сохранена, `auth` не нужен — токен возьмётся из хранилища.\n * const itd = new ItdClient({ storage: new FileTokenStorage('./.itd-session.json') });\n *\n * await itd.posts.create((p) => p.content('привет').attach('./photo.jpg'));\n * ```\n *\n * @packageDocumentation\n */\n\nimport { ItdClient as BaseClient } from './client.js';\nimport type { ItdSession, TokenStorage } from './core/storage.js';\nimport type { FileReader } from './resources/files.js';\nimport type { ItdClientOptions } from './types/options.js';\n\n/**\n * Читает файл с диска для загрузки.\n *\n * Модуль `node:fs` подключается динамически, поэтому импорт этой точки входа не тянет\n * его в браузерные сборки.\n */\nexport const nodeFileReader: FileReader = async (path) => {\n const { readFile } = await import('node:fs/promises');\n const { basename } = await import('node:path');\n\n return { data: new Uint8Array(await readFile(path)), filename: basename(path) };\n};\n\n/**\n * Хранит сессию в файле.\n *\n * Нужна долгоживущим процессам: без неё бот при каждом запуске входит заново — а вход\n * требует решённой капчи, да и серия входов подряд может привести к временной блокировке\n * аккаунта. С сохранённой сессией опция `auth` не нужна вовсе.\n *\n * Файл создаётся с правами `0600` (чтение и запись только владельцу) — в нём лежат токены.\n * Запись идёт через временный файл с последующим переименованием, поэтому падение процесса\n * посреди сохранения не оставит повреждённую сессию.\n *\n * @example\n * ```ts\n * const itd = new ItdClient({ storage: new FileTokenStorage('./.itd-session.json') });\n * ```\n */\nexport class FileTokenStorage implements TokenStorage {\n readonly #path: string;\n /**\n * Цепочка записей: сохранения идут строго по одному, конкурентные `set` не\n * перемешиваются. Ошибка одной записи цепочку не рвёт.\n */\n #writing: Promise<void> = Promise.resolve();\n\n /** @param path путь к файлу сессии. Добавьте его в `.gitignore`. */\n constructor(path: string) {\n this.#path = path;\n }\n\n async get(): Promise<ItdSession | null> {\n try {\n const { readFile } = await import('node:fs/promises');\n const raw = await readFile(this.#path, 'utf8');\n const parsed: unknown = JSON.parse(raw);\n\n return typeof parsed === 'object' && parsed !== null ? (parsed as ItdSession) : null;\n } catch {\n // Файла нет или он повреждён — считаем, что сессии не было.\n return null;\n }\n }\n\n set(session: ItdSession): Promise<void> {\n this.#writing = this.#writing.then(\n () => this.#write(session),\n () => this.#write(session),\n );\n return this.#writing;\n }\n\n async #write(session: ItdSession): Promise<void> {\n const { writeFile, rename, unlink } = await import('node:fs/promises');\n\n // Временный файл уникален на процесс и вызов — иначе параллельные записи (в том числе\n // из разных процессов) делят один `.tmp`.\n const pid = typeof process !== 'undefined' ? process.pid : 0;\n const temporary = `${this.#path}.${pid}.${Math.random().toString(36).slice(2)}.tmp`;\n\n try {\n await writeFile(temporary, JSON.stringify(session, null, 2), {\n encoding: 'utf8',\n mode: 0o600,\n });\n await rename(temporary, this.#path);\n } catch (error) {\n await unlink(temporary).catch(() => {});\n throw error;\n }\n }\n\n async clear(): Promise<void> {\n try {\n const { rm } = await import('node:fs/promises');\n await rm(this.#path, { force: true });\n } catch {\n // Удалять нечего — это не ошибка.\n }\n }\n}\n\n/**\n * Клиент API итд.com с поддержкой файловой системы.\n *\n * Отличается от базового только тем, что умеет читать файлы по пути:\n * `attach('./photo.jpg')` и `itd.files.upload('./video.mp4')` работают сразу.\n */\nexport class ItdClient extends BaseClient {\n constructor(options: ItdClientOptions = {}) {\n // Чтение файлов передаётся скрытым параметром конструктора, а не мутацией уже\n // собранного клиента: так объект работоспособен сразу и не меняется после создания.\n super(options, { fileReader: nodeFileReader });\n }\n}\n\n/** Создаёт {@link ItdClient} с поддержкой файловой системы. */\nexport function createClient(options: ItdClientOptions = {}): ItdClient {\n return new ItdClient(options);\n}\n\n// Всё остальное — билдеры, типы, ошибки, перечисления — доступно отсюда же,\n// чтобы в Node хватало одного импорта. Объявленные выше имена перекрывают одноимённые.\nexport * from './index.js';\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/node.ts"],"names":["ItdConfigError","copySession","createRecordMultiStorage","ItdClient","ItdAccounts"],"mappings":"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","file":"node.cjs","sourcesContent":["/**\n * `itd-api/node` — то же, что `itd-api`, плюс работа с файловой системой.\n *\n * Импортируйте эту точку входа в Node, Bun и Deno: клиент отсюда умеет загружать файлы\n * по пути (`attach('./photo.jpg')`), а `FileTokenStorage` сохраняет сессию между запусками.\n * В основном бандле работы с файлами нет намеренно — иначе браузерные сборщики пытались бы\n * разрешить `node:fs`.\n *\n * @example\n * ```ts\n * import { ItdClient, FileTokenStorage } from 'itd-api/node';\n *\n * // Когда сессия уже сохранена, `auth` не нужен — токен возьмётся из хранилища.\n * const itd = new ItdClient({ storage: new FileTokenStorage('./.itd-session.json') });\n *\n * await itd.posts.create((p) => p.content('привет').attach('./photo.jpg'));\n * ```\n *\n * @packageDocumentation\n */\n\nimport { ItdAccounts as BaseAccounts, type ItdAccountsOptions } from './accounts.js';\nimport { ItdClient as BaseClient, type ItdClientInternals } from './client.js';\nimport { ItdConfigError } from './core/errors.js';\nimport { createRecordMultiStorage, type MultiTokenStorage } from './core/multi-storage.js';\nimport { copySession, type ItdSession, type TokenStorage } from './core/storage.js';\nimport type { FileReader } from './resources/files.js';\nimport type { ItdClientOptions } from './types/options.js';\n\n/** Проверяет код системной ошибки без привязки к типам конкретного рантайма. */\nfunction hasErrorCode(error: unknown, code: string): boolean {\n return typeof error === 'object' && error !== null && 'code' in error && error.code === code;\n}\n\n/**\n * Читает файл с диска для загрузки.\n *\n * Модуль `node:fs` подключается динамически, поэтому импорт этой точки входа не тянет\n * его в браузерные сборки.\n */\nexport const nodeFileReader: FileReader = async (path) => {\n const { readFile } = await import('node:fs/promises');\n const { basename } = await import('node:path');\n\n return { data: new Uint8Array(await readFile(path)), filename: basename(path) };\n};\n\n/**\n * Читает и разбирает JSON-файл.\n *\n * Отсутствующий файл означает `null`. Остальные ошибки файловой системы не скрываются.\n * Одиночное хранилище ради обратной совместимости считает повреждённый JSON пустым,\n * а мультихранилище требует строгого разбора: молча затереть файл с несколькими токенами\n * особенно опасно.\n */\nasync function readJsonFile(path: string, strict = false): Promise<unknown> {\n let raw: string;\n try {\n const { readFile } = await import('node:fs/promises');\n raw = await readFile(path, 'utf8');\n } catch (error) {\n if (hasErrorCode(error, 'ENOENT')) return null;\n throw error;\n }\n\n try {\n return JSON.parse(raw) as unknown;\n } catch {\n if (!strict) return null;\n throw new ItdConfigError(\n `Файл ${path} повреждён: ожидался JSON с сессиями аккаунтов. ` +\n 'Исправьте файл или перенесите его перед следующим сохранением.',\n );\n }\n}\n\n/**\n * Записывает JSON во временный файл и переименовывает его поверх целевого.\n *\n * Права `0600` (чтение и запись только владельцу) — в файле лежат токены. Переименование\n * атомарно, поэтому падение процесса посреди сохранения не оставит повреждённого файла.\n */\nasync function writeJsonAtomic(path: string, value: unknown): Promise<void> {\n const { writeFile, rename, unlink } = await import('node:fs/promises');\n\n // Временный файл уникален на процесс и вызов — иначе параллельные записи (в том числе\n // из разных процессов) делят один `.tmp`.\n const pid = typeof process !== 'undefined' ? process.pid : 0;\n const temporary = `${path}.${pid}.${Math.random().toString(36).slice(2)}.tmp`;\n\n try {\n await writeFile(temporary, JSON.stringify(value, null, 2), { encoding: 'utf8', mode: 0o600 });\n await rename(temporary, path);\n } catch (error) {\n await unlink(temporary).catch(() => {});\n throw error;\n }\n}\n\n/** Удаляет файл. Благодаря `force` отсутствие файла ошибкой не считается. */\nasync function removeFile(path: string): Promise<void> {\n const { rm } = await import('node:fs/promises');\n await rm(path, { force: true });\n}\n\n/**\n * Хранит сессию в файле.\n *\n * Нужна долгоживущим процессам: без неё бот при каждом запуске входит заново — а вход\n * требует решённой капчи, да и серия входов подряд может привести к временной блокировке\n * аккаунта. С сохранённой сессией опция `auth` не нужна вовсе.\n *\n * Файл создаётся с правами `0600` (чтение и запись только владельцу) — в нём лежат токены.\n * Запись идёт через временный файл с последующим переименованием, поэтому падение процесса\n * посреди сохранения не оставит повреждённую сессию.\n *\n * @example\n * ```ts\n * const itd = new ItdClient({ storage: new FileTokenStorage('./.itd-session.json') });\n * ```\n */\nexport class FileTokenStorage implements TokenStorage {\n readonly #path: string;\n /**\n * Цепочка операций с файлом. Запись и удаление выполняются последовательно\n * в порядке вызова; ошибка одной операции не останавливает следующие.\n */\n #writing: Promise<void> = Promise.resolve();\n\n /** @param path путь к файлу сессии. Добавьте его в `.gitignore`. */\n constructor(path: string) {\n this.#path = path;\n }\n\n async get(): Promise<ItdSession | null> {\n // Чтение, начатое сразу после `set()`/`clear()`, должно видеть результат более ранней\n // операции, даже если вызывающий код не сохранил и не дождался её промиса.\n await this.#writing.then(\n () => undefined,\n () => undefined,\n );\n const parsed = await readJsonFile(this.#path);\n return typeof parsed === 'object' && parsed !== null ? copySession(parsed as ItdSession) : null;\n }\n\n set(session: ItdSession): Promise<void> {\n // Пользователь может изменить переданный объект сразу после вызова `set()`.\n // В очередь должен попасть снимок на момент вызова, а не живая ссылка.\n const snapshot = copySession(session);\n return this.#enqueue(() => writeJsonAtomic(this.#path, snapshot));\n }\n\n clear(): Promise<void> {\n return this.#enqueue(() => removeFile(this.#path));\n }\n\n /** Добавляет файловую операцию в очередь. */\n #enqueue(operation: () => Promise<void>): Promise<void> {\n this.#writing = this.#writing.then(operation, operation);\n return this.#writing;\n }\n}\n\n/** Версия формата файла с сессиями нескольких аккаунтов. */\nconst SESSIONS_FILE_VERSION = 1;\n\n/**\n * Хранит сессии нескольких аккаунтов в одном файле.\n *\n * Разбирается с гонкой «прочитать, изменить, записать»: десять аккаунтов пишут в один файл,\n * и без общего слепка с очередью записей они теряли бы сессии друг друга. Как и\n * {@link FileTokenStorage}, пишет через временный файл с правами `0600`.\n *\n * Формат — конверт `{ version, accounts }`, а не голая карта: так файл нескольких аккаунтов\n * не спутать с однопользовательским, и чужой не будет молча перезаписан.\n *\n * @example\n * ```ts\n * const accounts = new ItdAccounts({\n * storage: new FileMultiTokenStorage('./.itd-sessions.json'),\n * });\n * await accounts.restore();\n * ```\n */\nexport class FileMultiTokenStorage implements MultiTokenStorage {\n readonly #path: string;\n readonly #inner: MultiTokenStorage;\n\n /** @param path путь к файлу сессий. Добавьте его в `.gitignore`. */\n constructor(path: string) {\n this.#path = path;\n this.#inner = createRecordMultiStorage({\n read: () => this.#read(),\n write: (accounts) => writeJsonAtomic(path, { version: SESSIONS_FILE_VERSION, accounts }),\n // Последний аккаунт ушёл — файл с пустой картой выглядел бы мусором.\n remove: () => removeFile(path),\n });\n }\n\n get(account: string): Promise<ItdSession | null> {\n return Promise.resolve(this.#inner.get(account));\n }\n\n set(account: string, session: ItdSession): Promise<void> {\n return Promise.resolve(this.#inner.set(account, session));\n }\n\n clear(account: string): Promise<void> {\n return Promise.resolve(this.#inner.clear(account));\n }\n\n accounts(): Promise<readonly string[]> {\n return Promise.resolve(this.#inner.accounts());\n }\n\n async #read(): Promise<Record<string, ItdSession> | null> {\n const parsed = await readJsonFile(this.#path, true);\n if (parsed === null) return null;\n\n const envelope =\n typeof parsed === 'object' && parsed !== null\n ? (parsed as { version?: unknown; accounts?: unknown })\n : undefined;\n const accounts = envelope?.accounts;\n\n if (\n envelope?.version !== SESSIONS_FILE_VERSION ||\n typeof accounts !== 'object' ||\n accounts === null ||\n Array.isArray(accounts)\n ) {\n throw new ItdConfigError(\n `Файл ${this.#path} имеет неподдерживаемый формат: ожидается ` +\n `{ version: ${SESSIONS_FILE_VERSION}, accounts }. ` +\n 'Возможно, это файл одиночной сессии или файл другой версии — ' +\n 'возьмите отдельный путь, чтобы не перезаписать его.',\n );\n }\n\n return accounts as Record<string, ItdSession>;\n }\n}\n\n/**\n * Клиент API итд.com с поддержкой файловой системы.\n *\n * Отличается от базового только тем, что умеет читать файлы по пути:\n * `attach('./photo.jpg')` и `itd.files.upload('./video.mp4')` работают сразу.\n */\nexport class ItdClient extends BaseClient {\n constructor(options: ItdClientOptions = {}, internals: ItdClientInternals = {}) {\n // Чтение файлов передаётся скрытым параметром конструктора, а не мутацией уже\n // собранного клиента: так объект работоспособен сразу и не меняется после создания.\n // Остальные скрытые параметры (например общая очередь от ItdAccounts) идут дальше.\n super(options, { ...internals, fileReader: nodeFileReader });\n }\n}\n\n/** Создаёт {@link ItdClient} с поддержкой файловой системы. */\nexport function createClient(options: ItdClientOptions = {}): ItdClient {\n return new ItdClient(options);\n}\n\n/**\n * Несколько аккаунтов итд.com с поддержкой файловой системы.\n *\n * Отличается от базового только тем, что заводит клиентов, умеющих читать файлы по пути.\n * В паре с {@link FileMultiTokenStorage} сессии всех аккаунтов живут в одном файле.\n */\nexport class ItdAccounts extends BaseAccounts {\n constructor(options: ItdAccountsOptions = {}) {\n super(options, {\n createClient: (clientOptions, internals) => new ItdClient(clientOptions, internals),\n });\n }\n}\n\n/** Создаёт {@link ItdAccounts} с поддержкой файловой системы. */\nexport function createAccounts(options: ItdAccountsOptions = {}): ItdAccounts {\n return new ItdAccounts(options);\n}\n\n// Всё остальное — билдеры, типы, ошибки, перечисления — доступно отсюда же,\n// чтобы в Node хватало одного импорта. Объявленные выше имена перекрывают одноимённые.\nexport * from './index.js';\n"]}
|
package/dist/node.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { A as ALLOWED_MIME_TYPES,
|
|
1
|
+
import { M as MultiTokenStorage, I as ItdSession, T as TokenStorage, a as ItdAccounts$1, b as ItdAccountsOptions, c as ItdClient$1, d as ItdClientOptions, e as ItdClientInternals, F as FileReader } from './index-CSjDNGCE.cjs';
|
|
2
|
+
export { A as ALLOWED_MIME_TYPES, f as AUDIO_MIME_TYPES, g as AUTH_FLAG_COOKIE, h as AUTH_PATHS, i as AccessType, j as AccountEvents, k as Actor, l as AddAccountOptions, m as AllowedMimeType, n as Announcement, o as AnnouncementButton, p as Attachment, q as AttachmentType, r as AudioMimeType, s as AuthEvents, t as AuthInput, u as AuthResource, v as Author, w as AutoSpansOptions, B as BUILT_IN_SERVICES, x as BuilderInput, C as CaptchaCredentials, y as ChangelogEntry, z as Clan, D as ClientHooks, E as Comment, G as CommentBuilder, H as CommentInput, J as CommentReplyTo, K as CommentSort, L as CommentsParams, N as CommentsResource, O as CreateCommentInput, P as CreatePollInput, Q as CreatePostInput, R as CreateReportInput, S as Credentials, U as CredentialsAuth, V as DEFAULT_BASE_URL, W as DEFAULT_STATUS_BASE_URL, X as DEFAULT_TIMEOUT, Y as DEFAULT_UPLOAD_TIMEOUT, Z as DEFAULT_USER_AGENT, _ as DEVICE_ID_HEADER, $ as DetectedRuntime, a0 as DwellEntry, a1 as ErrorContextHook, a2 as FeedParams, a3 as FeedTab, a4 as FileInput, a5 as FilesResource, a6 as FollowResult, a7 as ForgotPasswordInput, a8 as Hashtag, a9 as HashtagPostsParams, aa as HashtagsResource, ab as IMAGE_MIME_TYPES, ac as ImageMimeType, ad as IncidentKind, ae as InteractionEntry, af as InteractionType, ag as IsoDate, ah as ItdAbortError, ai as ItdApiError, aj as ItdApiErrorInit, ak as ItdApiErrorKind, al as ItdAuthError, am as ItdBuilder, an as ItdConfigError, ao as ItdConflictError, ap as ItdError, aq as ItdErrorCode, ar as ItdErrorKind, as as ItdFieldErrors, at as ItdForbiddenError, au as ItdNetworkError, av as ItdNotFoundError, aw as ItdPhoneVerificationError, ax as ItdPlugin, ay as ItdRateLimitError, az as ItdRealtime, aA as ItdServerError, aB as ItdTimeoutError, aC as ItdValidationError, aD as LIBRARY_VERSION, aE as LikeResult, aF as LikesVisibility, aG as Listener, aH as LocalStorageTokenStorage, aI as Logger, aJ as Loose, aK as MAX_RECONNECT_ATTEMPTS, aL as MarkupBuilder, aM as MarkupContent, aN as MarkupInput, aO as MarkupSpan, aP as MemoryMultiTokenStorage, aQ as MemoryTokenStorage, aR as MyProfile, aS as NOTIFICATION_TYPE_ALIASES, aT as Notification, aU as NotificationEvent, aV as NotificationListParams, aW as NotificationSettings, aX as NotificationType, aY as NotificationsResource, aZ as OAuthProvider, a_ as Page, a$ as PageState, b0 as PaginationMode, b1 as Paginator, b2 as PaginatorOptions, b3 as PaymentMethod, b4 as Pin, b5 as PinPostResult, b6 as PinsResult, b7 as PlatformResource, b8 as PlatformStatus, b9 as PluginContext, ba as Poll, bb as PollBuilder, bc as PollInput, bd as PollOption, be as PollTransportOptions, bf as Portal, bg as Post, bh as PostBuilder, bi as PostInput, bj as PostStats, bk as PostUpdateInput, bl as PostsResource, bm as PrivacySettings, bn as Profile, bo as PublicProfile, bp as QueryParams, bq as QueryValue, br as RECONNECT_BACKOFF, bs as RECONNECT_JITTER, bt as REFRESH_COOKIE, bu as REFRESH_COOKIE_PATH, bv as REQUEST_OPTION_KEYS, bw as RateLimitOptions, bx as RateLimitScope, by as RawRequestOptions, bz as RealtimeDeps, bA as RealtimeEvents, bB as RealtimeOptions, bC as RealtimeStatus, bD as RealtimeTransport, bE as RealtimeTransportKind, bF as ReconnectOptions, bG as RecordStorageSource, bH as RemoveAccountOptions, bI as RenderSpansOptions, bJ as RepliesParams, bK as Report, bL as ReportBuilder, bM as ReportInput, bN as ReportReason, bO as ReportTargetType, bP as ReportsResource, bQ as RequestContext, bR as RequestOptions, bS as ResetPasswordInput, bT as ResponseContext, bU as RetryContext, bV as RetryOptions, bW as RuntimeMode, bX as STATUS_SERVICE, bY as STREAM_PATH, bZ as SearchResource, b_ as SearchResult, b$ as ServiceDefinition, c0 as ServiceRegistry, c1 as ServiceState, c2 as ServiceStatus, c3 as Session, c4 as SignInResult, c5 as SignInStatus, c6 as Span, c7 as SpanRenderFormat, c8 as SpanType, c9 as SseTransportOptions, ca as StatusDay, cb as StatusIncidentLine, cc as Subscription, cd as SubscriptionResource, ce as SubscriptionState, cf as TURNSTILE_SITE_KEY, cg as TelemetryOptions, ch as TelemetryResource, ci as TextMarkup, cj as Transformer, ck as TransportContext, cl as TransportEvent, cm as UnauthorizedStreamError, cn as Unsubscribe, co as UpdateNotificationSettingsInput, cp as UpdatePostInput, cq as UpdatePrivacyInput, cr as UpdateProfileInput, cs as UploadOptions, ct as UploadedFile, cu as UserId, cv as UserListParams, cw as UserPostsParams, cx as UserRef, cy as UserSummary, cz as UsersResource, cA as VIDEO_MIME_TYPES, cB as VerificationResource, cC as VerificationStatus, cD as VideoMimeType, cE as ViewReason, cF as ViewSource, cG as WallAccess, cH as autoSpans, cI as canonicalNotificationType, cJ as comment, cK as createMultiTokenStorage, cL as createRecordMultiStorage, cM as createTokenStorage, cN as formatNotificationText, cO as isBuilder, cP as isItdApiError, cQ as isItdAuthError, cR as isItdConflictError, cS as isItdError, cT as isItdForbiddenError, cU as isItdNotFoundError, cV as isItdPhoneVerificationError, cW as isItdRateLimitError, cX as isItdServerError, cY as isItdValidationError, cZ as isKnownNotificationType, c_ as isMyProfile, c$ as mapPage, d0 as markup, d1 as normalizeNotification, d2 as poll, d3 as post, d4 as readNotificationEvent, d5 as readUnreadCountEvent, d6 as renderSpans, d7 as report, d8 as resolveNotificationUrl, d9 as scopedTokenStorage, da as statusDays, db as toDate, dc as utcStampToIso } from './index-CSjDNGCE.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* `itd-api/node` — то же, что `itd-api`, плюс работа с файловой системой.
|
|
@@ -53,6 +53,33 @@ declare class FileTokenStorage implements TokenStorage {
|
|
|
53
53
|
set(session: ItdSession): Promise<void>;
|
|
54
54
|
clear(): Promise<void>;
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Хранит сессии нескольких аккаунтов в одном файле.
|
|
58
|
+
*
|
|
59
|
+
* Разбирается с гонкой «прочитать, изменить, записать»: десять аккаунтов пишут в один файл,
|
|
60
|
+
* и без общего слепка с очередью записей они теряли бы сессии друг друга. Как и
|
|
61
|
+
* {@link FileTokenStorage}, пишет через временный файл с правами `0600`.
|
|
62
|
+
*
|
|
63
|
+
* Формат — конверт `{ version, accounts }`, а не голая карта: так файл нескольких аккаунтов
|
|
64
|
+
* не спутать с однопользовательским, и чужой не будет молча перезаписан.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* const accounts = new ItdAccounts({
|
|
69
|
+
* storage: new FileMultiTokenStorage('./.itd-sessions.json'),
|
|
70
|
+
* });
|
|
71
|
+
* await accounts.restore();
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
declare class FileMultiTokenStorage implements MultiTokenStorage {
|
|
75
|
+
#private;
|
|
76
|
+
/** @param path путь к файлу сессий. Добавьте его в `.gitignore`. */
|
|
77
|
+
constructor(path: string);
|
|
78
|
+
get(account: string): Promise<ItdSession | null>;
|
|
79
|
+
set(account: string, session: ItdSession): Promise<void>;
|
|
80
|
+
clear(account: string): Promise<void>;
|
|
81
|
+
accounts(): Promise<readonly string[]>;
|
|
82
|
+
}
|
|
56
83
|
/**
|
|
57
84
|
* Клиент API итд.com с поддержкой файловой системы.
|
|
58
85
|
*
|
|
@@ -60,9 +87,20 @@ declare class FileTokenStorage implements TokenStorage {
|
|
|
60
87
|
* `attach('./photo.jpg')` и `itd.files.upload('./video.mp4')` работают сразу.
|
|
61
88
|
*/
|
|
62
89
|
declare class ItdClient extends ItdClient$1 {
|
|
63
|
-
constructor(options?: ItdClientOptions);
|
|
90
|
+
constructor(options?: ItdClientOptions, internals?: ItdClientInternals);
|
|
64
91
|
}
|
|
65
92
|
/** Создаёт {@link ItdClient} с поддержкой файловой системы. */
|
|
66
93
|
declare function createClient(options?: ItdClientOptions): ItdClient;
|
|
94
|
+
/**
|
|
95
|
+
* Несколько аккаунтов итд.com с поддержкой файловой системы.
|
|
96
|
+
*
|
|
97
|
+
* Отличается от базового только тем, что заводит клиентов, умеющих читать файлы по пути.
|
|
98
|
+
* В паре с {@link FileMultiTokenStorage} сессии всех аккаунтов живут в одном файле.
|
|
99
|
+
*/
|
|
100
|
+
declare class ItdAccounts extends ItdAccounts$1 {
|
|
101
|
+
constructor(options?: ItdAccountsOptions);
|
|
102
|
+
}
|
|
103
|
+
/** Создаёт {@link ItdAccounts} с поддержкой файловой системы. */
|
|
104
|
+
declare function createAccounts(options?: ItdAccountsOptions): ItdAccounts;
|
|
67
105
|
|
|
68
|
-
export { FileTokenStorage, ItdClient, ItdClientOptions, ItdSession, TokenStorage, createClient, nodeFileReader };
|
|
106
|
+
export { FileMultiTokenStorage, FileReader, FileTokenStorage, ItdAccounts, ItdAccountsOptions, ItdClient, ItdClientOptions, ItdSession, MultiTokenStorage, TokenStorage, createAccounts, createClient, nodeFileReader };
|
package/dist/node.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { A as ALLOWED_MIME_TYPES,
|
|
1
|
+
import { M as MultiTokenStorage, I as ItdSession, T as TokenStorage, a as ItdAccounts$1, b as ItdAccountsOptions, c as ItdClient$1, d as ItdClientOptions, e as ItdClientInternals, F as FileReader } from './index-CSjDNGCE.js';
|
|
2
|
+
export { A as ALLOWED_MIME_TYPES, f as AUDIO_MIME_TYPES, g as AUTH_FLAG_COOKIE, h as AUTH_PATHS, i as AccessType, j as AccountEvents, k as Actor, l as AddAccountOptions, m as AllowedMimeType, n as Announcement, o as AnnouncementButton, p as Attachment, q as AttachmentType, r as AudioMimeType, s as AuthEvents, t as AuthInput, u as AuthResource, v as Author, w as AutoSpansOptions, B as BUILT_IN_SERVICES, x as BuilderInput, C as CaptchaCredentials, y as ChangelogEntry, z as Clan, D as ClientHooks, E as Comment, G as CommentBuilder, H as CommentInput, J as CommentReplyTo, K as CommentSort, L as CommentsParams, N as CommentsResource, O as CreateCommentInput, P as CreatePollInput, Q as CreatePostInput, R as CreateReportInput, S as Credentials, U as CredentialsAuth, V as DEFAULT_BASE_URL, W as DEFAULT_STATUS_BASE_URL, X as DEFAULT_TIMEOUT, Y as DEFAULT_UPLOAD_TIMEOUT, Z as DEFAULT_USER_AGENT, _ as DEVICE_ID_HEADER, $ as DetectedRuntime, a0 as DwellEntry, a1 as ErrorContextHook, a2 as FeedParams, a3 as FeedTab, a4 as FileInput, a5 as FilesResource, a6 as FollowResult, a7 as ForgotPasswordInput, a8 as Hashtag, a9 as HashtagPostsParams, aa as HashtagsResource, ab as IMAGE_MIME_TYPES, ac as ImageMimeType, ad as IncidentKind, ae as InteractionEntry, af as InteractionType, ag as IsoDate, ah as ItdAbortError, ai as ItdApiError, aj as ItdApiErrorInit, ak as ItdApiErrorKind, al as ItdAuthError, am as ItdBuilder, an as ItdConfigError, ao as ItdConflictError, ap as ItdError, aq as ItdErrorCode, ar as ItdErrorKind, as as ItdFieldErrors, at as ItdForbiddenError, au as ItdNetworkError, av as ItdNotFoundError, aw as ItdPhoneVerificationError, ax as ItdPlugin, ay as ItdRateLimitError, az as ItdRealtime, aA as ItdServerError, aB as ItdTimeoutError, aC as ItdValidationError, aD as LIBRARY_VERSION, aE as LikeResult, aF as LikesVisibility, aG as Listener, aH as LocalStorageTokenStorage, aI as Logger, aJ as Loose, aK as MAX_RECONNECT_ATTEMPTS, aL as MarkupBuilder, aM as MarkupContent, aN as MarkupInput, aO as MarkupSpan, aP as MemoryMultiTokenStorage, aQ as MemoryTokenStorage, aR as MyProfile, aS as NOTIFICATION_TYPE_ALIASES, aT as Notification, aU as NotificationEvent, aV as NotificationListParams, aW as NotificationSettings, aX as NotificationType, aY as NotificationsResource, aZ as OAuthProvider, a_ as Page, a$ as PageState, b0 as PaginationMode, b1 as Paginator, b2 as PaginatorOptions, b3 as PaymentMethod, b4 as Pin, b5 as PinPostResult, b6 as PinsResult, b7 as PlatformResource, b8 as PlatformStatus, b9 as PluginContext, ba as Poll, bb as PollBuilder, bc as PollInput, bd as PollOption, be as PollTransportOptions, bf as Portal, bg as Post, bh as PostBuilder, bi as PostInput, bj as PostStats, bk as PostUpdateInput, bl as PostsResource, bm as PrivacySettings, bn as Profile, bo as PublicProfile, bp as QueryParams, bq as QueryValue, br as RECONNECT_BACKOFF, bs as RECONNECT_JITTER, bt as REFRESH_COOKIE, bu as REFRESH_COOKIE_PATH, bv as REQUEST_OPTION_KEYS, bw as RateLimitOptions, bx as RateLimitScope, by as RawRequestOptions, bz as RealtimeDeps, bA as RealtimeEvents, bB as RealtimeOptions, bC as RealtimeStatus, bD as RealtimeTransport, bE as RealtimeTransportKind, bF as ReconnectOptions, bG as RecordStorageSource, bH as RemoveAccountOptions, bI as RenderSpansOptions, bJ as RepliesParams, bK as Report, bL as ReportBuilder, bM as ReportInput, bN as ReportReason, bO as ReportTargetType, bP as ReportsResource, bQ as RequestContext, bR as RequestOptions, bS as ResetPasswordInput, bT as ResponseContext, bU as RetryContext, bV as RetryOptions, bW as RuntimeMode, bX as STATUS_SERVICE, bY as STREAM_PATH, bZ as SearchResource, b_ as SearchResult, b$ as ServiceDefinition, c0 as ServiceRegistry, c1 as ServiceState, c2 as ServiceStatus, c3 as Session, c4 as SignInResult, c5 as SignInStatus, c6 as Span, c7 as SpanRenderFormat, c8 as SpanType, c9 as SseTransportOptions, ca as StatusDay, cb as StatusIncidentLine, cc as Subscription, cd as SubscriptionResource, ce as SubscriptionState, cf as TURNSTILE_SITE_KEY, cg as TelemetryOptions, ch as TelemetryResource, ci as TextMarkup, cj as Transformer, ck as TransportContext, cl as TransportEvent, cm as UnauthorizedStreamError, cn as Unsubscribe, co as UpdateNotificationSettingsInput, cp as UpdatePostInput, cq as UpdatePrivacyInput, cr as UpdateProfileInput, cs as UploadOptions, ct as UploadedFile, cu as UserId, cv as UserListParams, cw as UserPostsParams, cx as UserRef, cy as UserSummary, cz as UsersResource, cA as VIDEO_MIME_TYPES, cB as VerificationResource, cC as VerificationStatus, cD as VideoMimeType, cE as ViewReason, cF as ViewSource, cG as WallAccess, cH as autoSpans, cI as canonicalNotificationType, cJ as comment, cK as createMultiTokenStorage, cL as createRecordMultiStorage, cM as createTokenStorage, cN as formatNotificationText, cO as isBuilder, cP as isItdApiError, cQ as isItdAuthError, cR as isItdConflictError, cS as isItdError, cT as isItdForbiddenError, cU as isItdNotFoundError, cV as isItdPhoneVerificationError, cW as isItdRateLimitError, cX as isItdServerError, cY as isItdValidationError, cZ as isKnownNotificationType, c_ as isMyProfile, c$ as mapPage, d0 as markup, d1 as normalizeNotification, d2 as poll, d3 as post, d4 as readNotificationEvent, d5 as readUnreadCountEvent, d6 as renderSpans, d7 as report, d8 as resolveNotificationUrl, d9 as scopedTokenStorage, da as statusDays, db as toDate, dc as utcStampToIso } from './index-CSjDNGCE.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* `itd-api/node` — то же, что `itd-api`, плюс работа с файловой системой.
|
|
@@ -53,6 +53,33 @@ declare class FileTokenStorage implements TokenStorage {
|
|
|
53
53
|
set(session: ItdSession): Promise<void>;
|
|
54
54
|
clear(): Promise<void>;
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Хранит сессии нескольких аккаунтов в одном файле.
|
|
58
|
+
*
|
|
59
|
+
* Разбирается с гонкой «прочитать, изменить, записать»: десять аккаунтов пишут в один файл,
|
|
60
|
+
* и без общего слепка с очередью записей они теряли бы сессии друг друга. Как и
|
|
61
|
+
* {@link FileTokenStorage}, пишет через временный файл с правами `0600`.
|
|
62
|
+
*
|
|
63
|
+
* Формат — конверт `{ version, accounts }`, а не голая карта: так файл нескольких аккаунтов
|
|
64
|
+
* не спутать с однопользовательским, и чужой не будет молча перезаписан.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* const accounts = new ItdAccounts({
|
|
69
|
+
* storage: new FileMultiTokenStorage('./.itd-sessions.json'),
|
|
70
|
+
* });
|
|
71
|
+
* await accounts.restore();
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
declare class FileMultiTokenStorage implements MultiTokenStorage {
|
|
75
|
+
#private;
|
|
76
|
+
/** @param path путь к файлу сессий. Добавьте его в `.gitignore`. */
|
|
77
|
+
constructor(path: string);
|
|
78
|
+
get(account: string): Promise<ItdSession | null>;
|
|
79
|
+
set(account: string, session: ItdSession): Promise<void>;
|
|
80
|
+
clear(account: string): Promise<void>;
|
|
81
|
+
accounts(): Promise<readonly string[]>;
|
|
82
|
+
}
|
|
56
83
|
/**
|
|
57
84
|
* Клиент API итд.com с поддержкой файловой системы.
|
|
58
85
|
*
|
|
@@ -60,9 +87,20 @@ declare class FileTokenStorage implements TokenStorage {
|
|
|
60
87
|
* `attach('./photo.jpg')` и `itd.files.upload('./video.mp4')` работают сразу.
|
|
61
88
|
*/
|
|
62
89
|
declare class ItdClient extends ItdClient$1 {
|
|
63
|
-
constructor(options?: ItdClientOptions);
|
|
90
|
+
constructor(options?: ItdClientOptions, internals?: ItdClientInternals);
|
|
64
91
|
}
|
|
65
92
|
/** Создаёт {@link ItdClient} с поддержкой файловой системы. */
|
|
66
93
|
declare function createClient(options?: ItdClientOptions): ItdClient;
|
|
94
|
+
/**
|
|
95
|
+
* Несколько аккаунтов итд.com с поддержкой файловой системы.
|
|
96
|
+
*
|
|
97
|
+
* Отличается от базового только тем, что заводит клиентов, умеющих читать файлы по пути.
|
|
98
|
+
* В паре с {@link FileMultiTokenStorage} сессии всех аккаунтов живут в одном файле.
|
|
99
|
+
*/
|
|
100
|
+
declare class ItdAccounts extends ItdAccounts$1 {
|
|
101
|
+
constructor(options?: ItdAccountsOptions);
|
|
102
|
+
}
|
|
103
|
+
/** Создаёт {@link ItdAccounts} с поддержкой файловой системы. */
|
|
104
|
+
declare function createAccounts(options?: ItdAccountsOptions): ItdAccounts;
|
|
67
105
|
|
|
68
|
-
export { FileTokenStorage, ItdClient, ItdClientOptions, ItdSession, TokenStorage, createClient, nodeFileReader };
|
|
106
|
+
export { FileMultiTokenStorage, FileReader, FileTokenStorage, ItdAccounts, ItdAccountsOptions, ItdClient, ItdClientOptions, ItdSession, MultiTokenStorage, TokenStorage, createAccounts, createClient, nodeFileReader };
|
package/dist/node.js
CHANGED
|
@@ -1,17 +1,55 @@
|
|
|
1
|
-
import { ItdClient } from './chunk-
|
|
2
|
-
export { ALLOWED_MIME_TYPES, AUDIO_MIME_TYPES, AUTH_FLAG_COOKIE, AUTH_PATHS, AccessType, AttachmentType, CommentSort, DEFAULT_BASE_URL, DEFAULT_TIMEOUT, DEFAULT_USER_AGENT, DEVICE_ID_HEADER, DetectedRuntime, FeedTab, IMAGE_MIME_TYPES, InteractionType, ItdAbortError, ItdApiError, ItdApiErrorKind, ItdAuthError, ItdConfigError, ItdConflictError, ItdError, ItdErrorCode, ItdErrorKind, ItdForbiddenError, ItdNetworkError, ItdNotFoundError, ItdPhoneVerificationError, ItdRateLimitError, ItdRealtime, ItdServerError, ItdTimeoutError, ItdValidationError, LIBRARY_VERSION, LikesVisibility, LocalStorageTokenStorage, MAX_RECONNECT_ATTEMPTS, MemoryTokenStorage, NOTIFICATION_TYPE_ALIASES, NotificationType, OAuthProvider, PaginationMode, Paginator, RECONNECT_BACKOFF, RECONNECT_JITTER, REFRESH_COOKIE, REFRESH_COOKIE_PATH, RealtimeStatus, RealtimeTransportKind, ReportReason, ReportTargetType, RuntimeMode, STREAM_PATH, SignInStatus, SpanType, TURNSTILE_SITE_KEY, UnauthorizedStreamError, VIDEO_MIME_TYPES, ViewReason, ViewSource, WallAccess, canonicalNotificationType, comment, createTokenStorage, formatNotificationText, isBuilder, isItdApiError, isItdAuthError, isItdConflictError, isItdError, isItdForbiddenError, isItdNotFoundError, isItdPhoneVerificationError, isItdRateLimitError, isItdServerError, isItdValidationError, isKnownNotificationType, isMyProfile, normalizeNotification, poll, post, readNotificationEvent, readUnreadCountEvent, report, resolveNotificationUrl, toDate } from './chunk-
|
|
1
|
+
import { copySession, createRecordMultiStorage, ItdConfigError, ItdClient, ItdAccounts } from './chunk-SMV7TF5P.js';
|
|
2
|
+
export { ALLOWED_MIME_TYPES, AUDIO_MIME_TYPES, AUTH_FLAG_COOKIE, AUTH_PATHS, AccessType, AttachmentType, BUILT_IN_SERVICES, CommentSort, DEFAULT_BASE_URL, DEFAULT_STATUS_BASE_URL, DEFAULT_TIMEOUT, DEFAULT_UPLOAD_TIMEOUT, DEFAULT_USER_AGENT, DEVICE_ID_HEADER, DetectedRuntime, FeedTab, IMAGE_MIME_TYPES, IncidentKind, InteractionType, ItdAbortError, ItdApiError, ItdApiErrorKind, ItdAuthError, ItdConfigError, ItdConflictError, ItdError, ItdErrorCode, ItdErrorKind, ItdForbiddenError, ItdNetworkError, ItdNotFoundError, ItdPhoneVerificationError, ItdRateLimitError, ItdRealtime, ItdServerError, ItdTimeoutError, ItdValidationError, LIBRARY_VERSION, LikesVisibility, LocalStorageTokenStorage, MAX_RECONNECT_ATTEMPTS, MemoryMultiTokenStorage, MemoryTokenStorage, NOTIFICATION_TYPE_ALIASES, NotificationType, OAuthProvider, PaginationMode, Paginator, RECONNECT_BACKOFF, RECONNECT_JITTER, REFRESH_COOKIE, REFRESH_COOKIE_PATH, REQUEST_OPTION_KEYS, RealtimeStatus, RealtimeTransportKind, ReportReason, ReportTargetType, RuntimeMode, STATUS_SERVICE, STREAM_PATH, ServiceRegistry, ServiceState, SignInStatus, SpanType, TURNSTILE_SITE_KEY, UnauthorizedStreamError, VIDEO_MIME_TYPES, ViewReason, ViewSource, WallAccess, autoSpans, canonicalNotificationType, comment, createMultiTokenStorage, createRecordMultiStorage, createTokenStorage, formatNotificationText, isBuilder, isItdApiError, isItdAuthError, isItdConflictError, isItdError, isItdForbiddenError, isItdNotFoundError, isItdPhoneVerificationError, isItdRateLimitError, isItdServerError, isItdValidationError, isKnownNotificationType, isMyProfile, mapPage, markup, normalizeNotification, poll, post, readNotificationEvent, readUnreadCountEvent, renderSpans, report, resolveNotificationUrl, scopedTokenStorage, statusDays, toDate, utcStampToIso } from './chunk-SMV7TF5P.js';
|
|
3
3
|
|
|
4
4
|
// src/node.ts
|
|
5
|
+
function hasErrorCode(error, code) {
|
|
6
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
7
|
+
}
|
|
5
8
|
var nodeFileReader = async (path) => {
|
|
6
9
|
const { readFile } = await import('fs/promises');
|
|
7
10
|
const { basename } = await import('path');
|
|
8
11
|
return { data: new Uint8Array(await readFile(path)), filename: basename(path) };
|
|
9
12
|
};
|
|
13
|
+
async function readJsonFile(path, strict = false) {
|
|
14
|
+
let raw;
|
|
15
|
+
try {
|
|
16
|
+
const { readFile } = await import('fs/promises');
|
|
17
|
+
raw = await readFile(path, "utf8");
|
|
18
|
+
} catch (error) {
|
|
19
|
+
if (hasErrorCode(error, "ENOENT")) return null;
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(raw);
|
|
24
|
+
} catch {
|
|
25
|
+
if (!strict) return null;
|
|
26
|
+
throw new ItdConfigError(
|
|
27
|
+
`\u0424\u0430\u0439\u043B ${path} \u043F\u043E\u0432\u0440\u0435\u0436\u0434\u0451\u043D: \u043E\u0436\u0438\u0434\u0430\u043B\u0441\u044F JSON \u0441 \u0441\u0435\u0441\u0441\u0438\u044F\u043C\u0438 \u0430\u043A\u043A\u0430\u0443\u043D\u0442\u043E\u0432. \u0418\u0441\u043F\u0440\u0430\u0432\u044C\u0442\u0435 \u0444\u0430\u0439\u043B \u0438\u043B\u0438 \u043F\u0435\u0440\u0435\u043D\u0435\u0441\u0438\u0442\u0435 \u0435\u0433\u043E \u043F\u0435\u0440\u0435\u0434 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u043C \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u0438\u0435\u043C.`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function writeJsonAtomic(path, value) {
|
|
32
|
+
const { writeFile, rename, unlink } = await import('fs/promises');
|
|
33
|
+
const pid = typeof process !== "undefined" ? process.pid : 0;
|
|
34
|
+
const temporary = `${path}.${pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
35
|
+
try {
|
|
36
|
+
await writeFile(temporary, JSON.stringify(value, null, 2), { encoding: "utf8", mode: 384 });
|
|
37
|
+
await rename(temporary, path);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
await unlink(temporary).catch(() => {
|
|
40
|
+
});
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function removeFile(path) {
|
|
45
|
+
const { rm } = await import('fs/promises');
|
|
46
|
+
await rm(path, { force: true });
|
|
47
|
+
}
|
|
10
48
|
var FileTokenStorage = class {
|
|
11
49
|
#path;
|
|
12
50
|
/**
|
|
13
|
-
* Цепочка
|
|
14
|
-
*
|
|
51
|
+
* Цепочка операций с файлом. Запись и удаление выполняются последовательно
|
|
52
|
+
* в порядке вызова; ошибка одной операции не останавливает следующие.
|
|
15
53
|
*/
|
|
16
54
|
#writing = Promise.resolve();
|
|
17
55
|
/** @param path путь к файлу сессии. Добавьте его в `.gitignore`. */
|
|
@@ -19,55 +57,84 @@ var FileTokenStorage = class {
|
|
|
19
57
|
this.#path = path;
|
|
20
58
|
}
|
|
21
59
|
async get() {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
60
|
+
await this.#writing.then(
|
|
61
|
+
() => void 0,
|
|
62
|
+
() => void 0
|
|
63
|
+
);
|
|
64
|
+
const parsed = await readJsonFile(this.#path);
|
|
65
|
+
return typeof parsed === "object" && parsed !== null ? copySession(parsed) : null;
|
|
30
66
|
}
|
|
31
67
|
set(session) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
68
|
+
const snapshot = copySession(session);
|
|
69
|
+
return this.#enqueue(() => writeJsonAtomic(this.#path, snapshot));
|
|
70
|
+
}
|
|
71
|
+
clear() {
|
|
72
|
+
return this.#enqueue(() => removeFile(this.#path));
|
|
73
|
+
}
|
|
74
|
+
/** Добавляет файловую операцию в очередь. */
|
|
75
|
+
#enqueue(operation) {
|
|
76
|
+
this.#writing = this.#writing.then(operation, operation);
|
|
36
77
|
return this.#writing;
|
|
37
78
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
79
|
+
};
|
|
80
|
+
var SESSIONS_FILE_VERSION = 1;
|
|
81
|
+
var FileMultiTokenStorage = class {
|
|
82
|
+
#path;
|
|
83
|
+
#inner;
|
|
84
|
+
/** @param path путь к файлу сессий. Добавьте его в `.gitignore`. */
|
|
85
|
+
constructor(path) {
|
|
86
|
+
this.#path = path;
|
|
87
|
+
this.#inner = createRecordMultiStorage({
|
|
88
|
+
read: () => this.#read(),
|
|
89
|
+
write: (accounts) => writeJsonAtomic(path, { version: SESSIONS_FILE_VERSION, accounts }),
|
|
90
|
+
// Последний аккаунт ушёл — файл с пустой картой выглядел бы мусором.
|
|
91
|
+
remove: () => removeFile(path)
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
get(account) {
|
|
95
|
+
return Promise.resolve(this.#inner.get(account));
|
|
96
|
+
}
|
|
97
|
+
set(account, session) {
|
|
98
|
+
return Promise.resolve(this.#inner.set(account, session));
|
|
99
|
+
}
|
|
100
|
+
clear(account) {
|
|
101
|
+
return Promise.resolve(this.#inner.clear(account));
|
|
53
102
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
103
|
+
accounts() {
|
|
104
|
+
return Promise.resolve(this.#inner.accounts());
|
|
105
|
+
}
|
|
106
|
+
async #read() {
|
|
107
|
+
const parsed = await readJsonFile(this.#path, true);
|
|
108
|
+
if (parsed === null) return null;
|
|
109
|
+
const envelope = typeof parsed === "object" && parsed !== null ? parsed : void 0;
|
|
110
|
+
const accounts = envelope?.accounts;
|
|
111
|
+
if (envelope?.version !== SESSIONS_FILE_VERSION || typeof accounts !== "object" || accounts === null || Array.isArray(accounts)) {
|
|
112
|
+
throw new ItdConfigError(
|
|
113
|
+
`\u0424\u0430\u0439\u043B ${this.#path} \u0438\u043C\u0435\u0435\u0442 \u043D\u0435\u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043C\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442: \u043E\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044F { version: ${SESSIONS_FILE_VERSION}, accounts }. \u0412\u043E\u0437\u043C\u043E\u0436\u043D\u043E, \u044D\u0442\u043E \u0444\u0430\u0439\u043B \u043E\u0434\u0438\u043D\u043E\u0447\u043D\u043E\u0439 \u0441\u0435\u0441\u0441\u0438\u0438 \u0438\u043B\u0438 \u0444\u0430\u0439\u043B \u0434\u0440\u0443\u0433\u043E\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u2014 \u0432\u043E\u0437\u044C\u043C\u0438\u0442\u0435 \u043E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u0439 \u043F\u0443\u0442\u044C, \u0447\u0442\u043E\u0431\u044B \u043D\u0435 \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0438\u0441\u0430\u0442\u044C \u0435\u0433\u043E.`
|
|
114
|
+
);
|
|
59
115
|
}
|
|
116
|
+
return accounts;
|
|
60
117
|
}
|
|
61
118
|
};
|
|
62
119
|
var ItdClient2 = class extends ItdClient {
|
|
63
|
-
constructor(options = {}) {
|
|
64
|
-
super(options, { fileReader: nodeFileReader });
|
|
120
|
+
constructor(options = {}, internals = {}) {
|
|
121
|
+
super(options, { ...internals, fileReader: nodeFileReader });
|
|
65
122
|
}
|
|
66
123
|
};
|
|
67
124
|
function createClient(options = {}) {
|
|
68
125
|
return new ItdClient2(options);
|
|
69
126
|
}
|
|
127
|
+
var ItdAccounts2 = class extends ItdAccounts {
|
|
128
|
+
constructor(options = {}) {
|
|
129
|
+
super(options, {
|
|
130
|
+
createClient: (clientOptions, internals) => new ItdClient2(clientOptions, internals)
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
function createAccounts(options = {}) {
|
|
135
|
+
return new ItdAccounts2(options);
|
|
136
|
+
}
|
|
70
137
|
|
|
71
|
-
export { FileTokenStorage, ItdClient2 as ItdClient, createClient, nodeFileReader };
|
|
138
|
+
export { FileMultiTokenStorage, FileTokenStorage, ItdAccounts2 as ItdAccounts, ItdClient2 as ItdClient, createAccounts, createClient, nodeFileReader };
|
|
72
139
|
//# sourceMappingURL=node.js.map
|
|
73
140
|
//# sourceMappingURL=node.js.map
|
package/dist/node.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/node.ts"],"names":["ItdClient"],"mappings":"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","file":"node.js","sourcesContent":["/**\n * `itd-api/node` — то же, что `itd-api`, плюс работа с файловой системой.\n *\n * Импортируйте эту точку входа в Node, Bun и Deno: клиент отсюда умеет загружать файлы\n * по пути (`attach('./photo.jpg')`), а `FileTokenStorage` сохраняет сессию между запусками.\n * В основном бандле работы с файлами нет намеренно — иначе браузерные сборщики пытались бы\n * разрешить `node:fs`.\n *\n * @example\n * ```ts\n * import { ItdClient, FileTokenStorage } from 'itd-api/node';\n *\n * // Когда сессия уже сохранена, `auth` не нужен — токен возьмётся из хранилища.\n * const itd = new ItdClient({ storage: new FileTokenStorage('./.itd-session.json') });\n *\n * await itd.posts.create((p) => p.content('привет').attach('./photo.jpg'));\n * ```\n *\n * @packageDocumentation\n */\n\nimport { ItdClient as BaseClient } from './client.js';\nimport type { ItdSession, TokenStorage } from './core/storage.js';\nimport type { FileReader } from './resources/files.js';\nimport type { ItdClientOptions } from './types/options.js';\n\n/**\n * Читает файл с диска для загрузки.\n *\n * Модуль `node:fs` подключается динамически, поэтому импорт этой точки входа не тянет\n * его в браузерные сборки.\n */\nexport const nodeFileReader: FileReader = async (path) => {\n const { readFile } = await import('node:fs/promises');\n const { basename } = await import('node:path');\n\n return { data: new Uint8Array(await readFile(path)), filename: basename(path) };\n};\n\n/**\n * Хранит сессию в файле.\n *\n * Нужна долгоживущим процессам: без неё бот при каждом запуске входит заново — а вход\n * требует решённой капчи, да и серия входов подряд может привести к временной блокировке\n * аккаунта. С сохранённой сессией опция `auth` не нужна вовсе.\n *\n * Файл создаётся с правами `0600` (чтение и запись только владельцу) — в нём лежат токены.\n * Запись идёт через временный файл с последующим переименованием, поэтому падение процесса\n * посреди сохранения не оставит повреждённую сессию.\n *\n * @example\n * ```ts\n * const itd = new ItdClient({ storage: new FileTokenStorage('./.itd-session.json') });\n * ```\n */\nexport class FileTokenStorage implements TokenStorage {\n readonly #path: string;\n /**\n * Цепочка записей: сохранения идут строго по одному, конкурентные `set` не\n * перемешиваются. Ошибка одной записи цепочку не рвёт.\n */\n #writing: Promise<void> = Promise.resolve();\n\n /** @param path путь к файлу сессии. Добавьте его в `.gitignore`. */\n constructor(path: string) {\n this.#path = path;\n }\n\n async get(): Promise<ItdSession | null> {\n try {\n const { readFile } = await import('node:fs/promises');\n const raw = await readFile(this.#path, 'utf8');\n const parsed: unknown = JSON.parse(raw);\n\n return typeof parsed === 'object' && parsed !== null ? (parsed as ItdSession) : null;\n } catch {\n // Файла нет или он повреждён — считаем, что сессии не было.\n return null;\n }\n }\n\n set(session: ItdSession): Promise<void> {\n this.#writing = this.#writing.then(\n () => this.#write(session),\n () => this.#write(session),\n );\n return this.#writing;\n }\n\n async #write(session: ItdSession): Promise<void> {\n const { writeFile, rename, unlink } = await import('node:fs/promises');\n\n // Временный файл уникален на процесс и вызов — иначе параллельные записи (в том числе\n // из разных процессов) делят один `.tmp`.\n const pid = typeof process !== 'undefined' ? process.pid : 0;\n const temporary = `${this.#path}.${pid}.${Math.random().toString(36).slice(2)}.tmp`;\n\n try {\n await writeFile(temporary, JSON.stringify(session, null, 2), {\n encoding: 'utf8',\n mode: 0o600,\n });\n await rename(temporary, this.#path);\n } catch (error) {\n await unlink(temporary).catch(() => {});\n throw error;\n }\n }\n\n async clear(): Promise<void> {\n try {\n const { rm } = await import('node:fs/promises');\n await rm(this.#path, { force: true });\n } catch {\n // Удалять нечего — это не ошибка.\n }\n }\n}\n\n/**\n * Клиент API итд.com с поддержкой файловой системы.\n *\n * Отличается от базового только тем, что умеет читать файлы по пути:\n * `attach('./photo.jpg')` и `itd.files.upload('./video.mp4')` работают сразу.\n */\nexport class ItdClient extends BaseClient {\n constructor(options: ItdClientOptions = {}) {\n // Чтение файлов передаётся скрытым параметром конструктора, а не мутацией уже\n // собранного клиента: так объект работоспособен сразу и не меняется после создания.\n super(options, { fileReader: nodeFileReader });\n }\n}\n\n/** Создаёт {@link ItdClient} с поддержкой файловой системы. */\nexport function createClient(options: ItdClientOptions = {}): ItdClient {\n return new ItdClient(options);\n}\n\n// Всё остальное — билдеры, типы, ошибки, перечисления — доступно отсюда же,\n// чтобы в Node хватало одного импорта. Объявленные выше имена перекрывают одноимённые.\nexport * from './index.js';\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/node.ts"],"names":["ItdClient","ItdAccounts"],"mappings":"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","file":"node.js","sourcesContent":["/**\n * `itd-api/node` — то же, что `itd-api`, плюс работа с файловой системой.\n *\n * Импортируйте эту точку входа в Node, Bun и Deno: клиент отсюда умеет загружать файлы\n * по пути (`attach('./photo.jpg')`), а `FileTokenStorage` сохраняет сессию между запусками.\n * В основном бандле работы с файлами нет намеренно — иначе браузерные сборщики пытались бы\n * разрешить `node:fs`.\n *\n * @example\n * ```ts\n * import { ItdClient, FileTokenStorage } from 'itd-api/node';\n *\n * // Когда сессия уже сохранена, `auth` не нужен — токен возьмётся из хранилища.\n * const itd = new ItdClient({ storage: new FileTokenStorage('./.itd-session.json') });\n *\n * await itd.posts.create((p) => p.content('привет').attach('./photo.jpg'));\n * ```\n *\n * @packageDocumentation\n */\n\nimport { ItdAccounts as BaseAccounts, type ItdAccountsOptions } from './accounts.js';\nimport { ItdClient as BaseClient, type ItdClientInternals } from './client.js';\nimport { ItdConfigError } from './core/errors.js';\nimport { createRecordMultiStorage, type MultiTokenStorage } from './core/multi-storage.js';\nimport { copySession, type ItdSession, type TokenStorage } from './core/storage.js';\nimport type { FileReader } from './resources/files.js';\nimport type { ItdClientOptions } from './types/options.js';\n\n/** Проверяет код системной ошибки без привязки к типам конкретного рантайма. */\nfunction hasErrorCode(error: unknown, code: string): boolean {\n return typeof error === 'object' && error !== null && 'code' in error && error.code === code;\n}\n\n/**\n * Читает файл с диска для загрузки.\n *\n * Модуль `node:fs` подключается динамически, поэтому импорт этой точки входа не тянет\n * его в браузерные сборки.\n */\nexport const nodeFileReader: FileReader = async (path) => {\n const { readFile } = await import('node:fs/promises');\n const { basename } = await import('node:path');\n\n return { data: new Uint8Array(await readFile(path)), filename: basename(path) };\n};\n\n/**\n * Читает и разбирает JSON-файл.\n *\n * Отсутствующий файл означает `null`. Остальные ошибки файловой системы не скрываются.\n * Одиночное хранилище ради обратной совместимости считает повреждённый JSON пустым,\n * а мультихранилище требует строгого разбора: молча затереть файл с несколькими токенами\n * особенно опасно.\n */\nasync function readJsonFile(path: string, strict = false): Promise<unknown> {\n let raw: string;\n try {\n const { readFile } = await import('node:fs/promises');\n raw = await readFile(path, 'utf8');\n } catch (error) {\n if (hasErrorCode(error, 'ENOENT')) return null;\n throw error;\n }\n\n try {\n return JSON.parse(raw) as unknown;\n } catch {\n if (!strict) return null;\n throw new ItdConfigError(\n `Файл ${path} повреждён: ожидался JSON с сессиями аккаунтов. ` +\n 'Исправьте файл или перенесите его перед следующим сохранением.',\n );\n }\n}\n\n/**\n * Записывает JSON во временный файл и переименовывает его поверх целевого.\n *\n * Права `0600` (чтение и запись только владельцу) — в файле лежат токены. Переименование\n * атомарно, поэтому падение процесса посреди сохранения не оставит повреждённого файла.\n */\nasync function writeJsonAtomic(path: string, value: unknown): Promise<void> {\n const { writeFile, rename, unlink } = await import('node:fs/promises');\n\n // Временный файл уникален на процесс и вызов — иначе параллельные записи (в том числе\n // из разных процессов) делят один `.tmp`.\n const pid = typeof process !== 'undefined' ? process.pid : 0;\n const temporary = `${path}.${pid}.${Math.random().toString(36).slice(2)}.tmp`;\n\n try {\n await writeFile(temporary, JSON.stringify(value, null, 2), { encoding: 'utf8', mode: 0o600 });\n await rename(temporary, path);\n } catch (error) {\n await unlink(temporary).catch(() => {});\n throw error;\n }\n}\n\n/** Удаляет файл. Благодаря `force` отсутствие файла ошибкой не считается. */\nasync function removeFile(path: string): Promise<void> {\n const { rm } = await import('node:fs/promises');\n await rm(path, { force: true });\n}\n\n/**\n * Хранит сессию в файле.\n *\n * Нужна долгоживущим процессам: без неё бот при каждом запуске входит заново — а вход\n * требует решённой капчи, да и серия входов подряд может привести к временной блокировке\n * аккаунта. С сохранённой сессией опция `auth` не нужна вовсе.\n *\n * Файл создаётся с правами `0600` (чтение и запись только владельцу) — в нём лежат токены.\n * Запись идёт через временный файл с последующим переименованием, поэтому падение процесса\n * посреди сохранения не оставит повреждённую сессию.\n *\n * @example\n * ```ts\n * const itd = new ItdClient({ storage: new FileTokenStorage('./.itd-session.json') });\n * ```\n */\nexport class FileTokenStorage implements TokenStorage {\n readonly #path: string;\n /**\n * Цепочка операций с файлом. Запись и удаление выполняются последовательно\n * в порядке вызова; ошибка одной операции не останавливает следующие.\n */\n #writing: Promise<void> = Promise.resolve();\n\n /** @param path путь к файлу сессии. Добавьте его в `.gitignore`. */\n constructor(path: string) {\n this.#path = path;\n }\n\n async get(): Promise<ItdSession | null> {\n // Чтение, начатое сразу после `set()`/`clear()`, должно видеть результат более ранней\n // операции, даже если вызывающий код не сохранил и не дождался её промиса.\n await this.#writing.then(\n () => undefined,\n () => undefined,\n );\n const parsed = await readJsonFile(this.#path);\n return typeof parsed === 'object' && parsed !== null ? copySession(parsed as ItdSession) : null;\n }\n\n set(session: ItdSession): Promise<void> {\n // Пользователь может изменить переданный объект сразу после вызова `set()`.\n // В очередь должен попасть снимок на момент вызова, а не живая ссылка.\n const snapshot = copySession(session);\n return this.#enqueue(() => writeJsonAtomic(this.#path, snapshot));\n }\n\n clear(): Promise<void> {\n return this.#enqueue(() => removeFile(this.#path));\n }\n\n /** Добавляет файловую операцию в очередь. */\n #enqueue(operation: () => Promise<void>): Promise<void> {\n this.#writing = this.#writing.then(operation, operation);\n return this.#writing;\n }\n}\n\n/** Версия формата файла с сессиями нескольких аккаунтов. */\nconst SESSIONS_FILE_VERSION = 1;\n\n/**\n * Хранит сессии нескольких аккаунтов в одном файле.\n *\n * Разбирается с гонкой «прочитать, изменить, записать»: десять аккаунтов пишут в один файл,\n * и без общего слепка с очередью записей они теряли бы сессии друг друга. Как и\n * {@link FileTokenStorage}, пишет через временный файл с правами `0600`.\n *\n * Формат — конверт `{ version, accounts }`, а не голая карта: так файл нескольких аккаунтов\n * не спутать с однопользовательским, и чужой не будет молча перезаписан.\n *\n * @example\n * ```ts\n * const accounts = new ItdAccounts({\n * storage: new FileMultiTokenStorage('./.itd-sessions.json'),\n * });\n * await accounts.restore();\n * ```\n */\nexport class FileMultiTokenStorage implements MultiTokenStorage {\n readonly #path: string;\n readonly #inner: MultiTokenStorage;\n\n /** @param path путь к файлу сессий. Добавьте его в `.gitignore`. */\n constructor(path: string) {\n this.#path = path;\n this.#inner = createRecordMultiStorage({\n read: () => this.#read(),\n write: (accounts) => writeJsonAtomic(path, { version: SESSIONS_FILE_VERSION, accounts }),\n // Последний аккаунт ушёл — файл с пустой картой выглядел бы мусором.\n remove: () => removeFile(path),\n });\n }\n\n get(account: string): Promise<ItdSession | null> {\n return Promise.resolve(this.#inner.get(account));\n }\n\n set(account: string, session: ItdSession): Promise<void> {\n return Promise.resolve(this.#inner.set(account, session));\n }\n\n clear(account: string): Promise<void> {\n return Promise.resolve(this.#inner.clear(account));\n }\n\n accounts(): Promise<readonly string[]> {\n return Promise.resolve(this.#inner.accounts());\n }\n\n async #read(): Promise<Record<string, ItdSession> | null> {\n const parsed = await readJsonFile(this.#path, true);\n if (parsed === null) return null;\n\n const envelope =\n typeof parsed === 'object' && parsed !== null\n ? (parsed as { version?: unknown; accounts?: unknown })\n : undefined;\n const accounts = envelope?.accounts;\n\n if (\n envelope?.version !== SESSIONS_FILE_VERSION ||\n typeof accounts !== 'object' ||\n accounts === null ||\n Array.isArray(accounts)\n ) {\n throw new ItdConfigError(\n `Файл ${this.#path} имеет неподдерживаемый формат: ожидается ` +\n `{ version: ${SESSIONS_FILE_VERSION}, accounts }. ` +\n 'Возможно, это файл одиночной сессии или файл другой версии — ' +\n 'возьмите отдельный путь, чтобы не перезаписать его.',\n );\n }\n\n return accounts as Record<string, ItdSession>;\n }\n}\n\n/**\n * Клиент API итд.com с поддержкой файловой системы.\n *\n * Отличается от базового только тем, что умеет читать файлы по пути:\n * `attach('./photo.jpg')` и `itd.files.upload('./video.mp4')` работают сразу.\n */\nexport class ItdClient extends BaseClient {\n constructor(options: ItdClientOptions = {}, internals: ItdClientInternals = {}) {\n // Чтение файлов передаётся скрытым параметром конструктора, а не мутацией уже\n // собранного клиента: так объект работоспособен сразу и не меняется после создания.\n // Остальные скрытые параметры (например общая очередь от ItdAccounts) идут дальше.\n super(options, { ...internals, fileReader: nodeFileReader });\n }\n}\n\n/** Создаёт {@link ItdClient} с поддержкой файловой системы. */\nexport function createClient(options: ItdClientOptions = {}): ItdClient {\n return new ItdClient(options);\n}\n\n/**\n * Несколько аккаунтов итд.com с поддержкой файловой системы.\n *\n * Отличается от базового только тем, что заводит клиентов, умеющих читать файлы по пути.\n * В паре с {@link FileMultiTokenStorage} сессии всех аккаунтов живут в одном файле.\n */\nexport class ItdAccounts extends BaseAccounts {\n constructor(options: ItdAccountsOptions = {}) {\n super(options, {\n createClient: (clientOptions, internals) => new ItdClient(clientOptions, internals),\n });\n }\n}\n\n/** Создаёт {@link ItdAccounts} с поддержкой файловой системы. */\nexport function createAccounts(options: ItdAccountsOptions = {}): ItdAccounts {\n return new ItdAccounts(options);\n}\n\n// Всё остальное — билдеры, типы, ошибки, перечисления — доступно отсюда же,\n// чтобы в Node хватало одного импорта. Объявленные выше имена перекрывают одноимённые.\nexport * from './index.js';\n"]}
|
package/guides/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Руководства
|
|
2
|
+
|
|
3
|
+
| Руководство | О чём |
|
|
4
|
+
|---|---|
|
|
5
|
+
| [Быстрый старт](./quickstart/README.md) | установка, клиент, публикация, пагинация и ошибки |
|
|
6
|
+
| [Разметка текста](./text-markup/README.md) | spans, `markup()`, `autoSpans()`, пересечения и `renderSpans()` |
|
|
7
|
+
| [Авторизация](./authentication/README.md) | токены, Turnstile, OTP, продление и хранение сессии |
|
|
8
|
+
| [Realtime](./realtime/README.md) | уведомления, SSE, переподключение и fallback на polling |
|
|
9
|
+
| [Несколько аккаунтов](./multi-accounts/README.md) | `ItdAccounts`, общее хранилище, очереди и личные настройки |
|
|
10
|
+
| [Интеграции](./integrations/README.md) | proxy, Turnstile и точки подключения внешних возможностей |
|
|
11
|
+
| [Плагины](./plugins/README.md) | Cache, Crypto, `ItdPlugin` и создание нового плагина |
|
|
12
|
+
|
|
13
|
+
## Запуск примеров
|
|
14
|
+
|
|
15
|
+
Установите зависимости и соберите библиотеку:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install
|
|
19
|
+
npm run build
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Команды запуска и переменные окружения указаны в каждом руководстве.
|