itd-api 0.6.0 → 0.7.1

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 (90) hide show
  1. package/README.md +6 -6
  2. package/dist/index.cjs +473 -9378
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +300 -4830
  5. package/dist/index.d.ts +300 -4830
  6. package/dist/index.js +350 -9259
  7. package/dist/index.js.map +1 -1
  8. package/dist/{node.cjs → node/index.cjs} +16 -14
  9. package/dist/node/index.cjs.map +1 -0
  10. package/dist/{node.d.cts → node/index.d.cts} +4 -3
  11. package/dist/{node.d.ts → node/index.d.ts} +4 -3
  12. package/dist/{node.js → node/index.js} +5 -3
  13. package/dist/node/index.js.map +1 -0
  14. package/dist/realtime/index.cjs +165 -0
  15. package/dist/realtime/index.cjs.map +1 -0
  16. package/dist/realtime/index.d.cts +51 -0
  17. package/dist/realtime/index.d.ts +51 -0
  18. package/dist/realtime/index.js +120 -0
  19. package/dist/realtime/index.js.map +1 -0
  20. package/dist/rest/index.cjs +334 -0
  21. package/dist/rest/index.cjs.map +1 -0
  22. package/dist/rest/index.d.cts +138 -0
  23. package/dist/rest/index.d.ts +138 -0
  24. package/dist/rest/index.js +238 -0
  25. package/dist/rest/index.js.map +1 -0
  26. package/dist/shared/auth-provider-BfogACAb.js +91 -0
  27. package/dist/shared/auth-provider-BfogACAb.js.map +1 -0
  28. package/dist/shared/auth-provider-CTJkKfgy.cjs +108 -0
  29. package/dist/shared/auth-provider-CTJkKfgy.cjs.map +1 -0
  30. package/dist/shared/contracts-BoT7msmq.d.cts +84 -0
  31. package/dist/shared/contracts-BoT7msmq.d.ts +84 -0
  32. package/dist/{multi-storage-Bf84xiO8.cjs → shared/cookies-DZwFq6kr.cjs} +98 -429
  33. package/dist/shared/cookies-DZwFq6kr.cjs.map +1 -0
  34. package/dist/{multi-storage-BUZaLAPO.js → shared/cookies-tX2sNwxb.js} +99 -352
  35. package/dist/shared/cookies-tX2sNwxb.js.map +1 -0
  36. package/dist/{storage-IHdXw52v.js → shared/errors-Bhrd2fJd.js} +2 -229
  37. package/dist/shared/errors-Bhrd2fJd.js.map +1 -0
  38. package/dist/{storage-DnzZPS_9.cjs → shared/errors-DfU8M5eS.cjs} +1 -288
  39. package/dist/shared/errors-DfU8M5eS.cjs.map +1 -0
  40. package/dist/shared/multi-storage--yTEqiod.cjs +150 -0
  41. package/dist/shared/multi-storage--yTEqiod.cjs.map +1 -0
  42. package/dist/shared/multi-storage-CjAPB5Kq.d.cts +72 -0
  43. package/dist/shared/multi-storage-CkvTUC5m.js +121 -0
  44. package/dist/shared/multi-storage-CkvTUC5m.js.map +1 -0
  45. package/dist/shared/multi-storage-DccjD7Ww.d.ts +72 -0
  46. package/dist/shared/options-Dg5N3r1V.cjs +189 -0
  47. package/dist/shared/options-Dg5N3r1V.cjs.map +1 -0
  48. package/dist/shared/options-DtATYdLr.js +142 -0
  49. package/dist/shared/options-DtATYdLr.js.map +1 -0
  50. package/dist/shared/render-DMp_3Nzk.d.cts +2250 -0
  51. package/dist/shared/render-DZrxhC5_.d.ts +2250 -0
  52. package/dist/shared/render-DyeHJNBw.cjs +4147 -0
  53. package/dist/shared/render-DyeHJNBw.cjs.map +1 -0
  54. package/dist/shared/render-vtLixIiU.js +3992 -0
  55. package/dist/shared/render-vtLixIiU.js.map +1 -0
  56. package/dist/shared/storage-BPJR_k4-.cjs +290 -0
  57. package/dist/shared/storage-BPJR_k4-.cjs.map +1 -0
  58. package/dist/{storage-BqMxs76Y.d.ts → shared/storage-C_eICCep.d.cts} +2 -2
  59. package/dist/{storage-BqMxs76Y.d.cts → shared/storage-C_eICCep.d.ts} +2 -2
  60. package/dist/shared/storage-D86edNCB.js +231 -0
  61. package/dist/shared/storage-D86edNCB.js.map +1 -0
  62. package/dist/shared/url-B6-bXHKt.d.cts +2083 -0
  63. package/dist/shared/url-B6-bXHKt.d.ts +2083 -0
  64. package/dist/shared/url-CYXgxqGx.js +3673 -0
  65. package/dist/shared/url-CYXgxqGx.js.map +1 -0
  66. package/dist/shared/url-yjl2c8Ie.cjs +4032 -0
  67. package/dist/shared/url-yjl2c8Ie.cjs.map +1 -0
  68. package/dist/shared/websocket-BMtihD56.d.ts +562 -0
  69. package/dist/shared/websocket-CbzB1Leq.js +1874 -0
  70. package/dist/shared/websocket-CbzB1Leq.js.map +1 -0
  71. package/dist/shared/websocket-D1p32SB2.d.cts +562 -0
  72. package/dist/shared/websocket-sYlynrr0.cjs +1945 -0
  73. package/dist/shared/websocket-sYlynrr0.cjs.map +1 -0
  74. package/dist/{web.cjs → web/index.cjs} +4 -3
  75. package/dist/web/index.cjs.map +1 -0
  76. package/dist/{web.d.cts → web/index.d.cts} +2 -2
  77. package/dist/{web.d.ts → web/index.d.ts} +2 -2
  78. package/dist/{web.js → web/index.js} +3 -2
  79. package/dist/web/index.js.map +1 -0
  80. package/package.json +40 -12
  81. package/dist/multi-storage-BUZaLAPO.js.map +0 -1
  82. package/dist/multi-storage-BUoEoW8f.d.cts +0 -154
  83. package/dist/multi-storage-Bf84xiO8.cjs.map +0 -1
  84. package/dist/multi-storage-CQSlI_kn.d.ts +0 -154
  85. package/dist/node.cjs.map +0 -1
  86. package/dist/node.js.map +0 -1
  87. package/dist/storage-DnzZPS_9.cjs.map +0 -1
  88. package/dist/storage-IHdXw52v.js.map +0 -1
  89. package/dist/web.cjs.map +0 -1
  90. package/dist/web.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options-Dg5N3r1V.cjs","names":["ItdConfigError","ItdFileError","ItdFileErrorReason","ItdConfigError","ItdFileError","ItdFileErrorReason","ItdConfigError"],"sources":["../../src/core/attachments/contracts.ts","../../src/core/attachments/limits.ts","../../src/core/attachments/bounded-stream.ts","../../src/core/attachments/options.ts"],"sourcesContent":["/** Способ передачи содержимого вложения. */\nexport const FileTransferMode = Object.freeze({\n /** Сначала получить файл целиком, затем отправить его как `Blob`. */\n Buffer: 'buffer',\n /** Передавать данные по мере чтения, не собирая файл целиком в памяти. */\n Stream: 'stream',\n} as const);\nexport type FileTransferMode = (typeof FileTransferMode)[keyof typeof FileTransferMode];\n\n/** Размер очереди потокового вложения по умолчанию — 4 МиБ. */\nexport const DEFAULT_FILE_STREAM_BUFFER_BYTES = 4 * 1024 * 1024;\n\n/** Предел размера файла, скачиваемого по адресу, — 100 МиБ. */\nexport const DEFAULT_URL_FILE_MAX_BYTES = 100 * 1024 * 1024;\n\n/** Что доступно источнику при открытии файла. */\nexport interface FileContext {\n /** Общая отмена текущей попытки, включая её таймаут. */\n signal?: AbortSignal | undefined;\n /** `fetch` клиента — с переданными пользователем настройками. */\n fetch: typeof fetch;\n /** Номер попытки отправки, начиная с 1. */\n attempt: number;\n}\n\n/** Готовое содержимое файла. */\nexport interface FileContent {\n file: Blob | ArrayBuffer | Uint8Array;\n /** Имя влияет на определение MIME, если `contentType` не задан. */\n filename?: string | undefined;\n /** MIME без параметров. Если не указан, определяется по имени или `Blob`. */\n contentType?: string | undefined;\n}\n\n/** Открытый поток файла. */\nexport interface FileStreamContent {\n stream: ReadableStream<Uint8Array>;\n filename?: string | undefined;\n contentType?: string | undefined;\n /** Размер, если он известен до чтения. */\n size?: number | undefined;\n /** Освобождает внешний ресурс после завершения попытки. */\n close?: (() => void | Promise<void>) | undefined;\n}\n\n/** Буферный источник, который вычисляется непосредственно перед первой попыткой. */\nexport interface LazyFile {\n load: (context: FileContext) => FileContent | Promise<FileContent>;\n}\n\n/**\n * Повторяемый потоковый источник.\n *\n * `open` вызывается для каждой попытки отправки. Он обязан возвращать новый поток:\n * уже прочитанный `ReadableStream` повторно использовать нельзя.\n */\nexport interface StreamFile {\n open: (context: FileContext) => FileStreamContent | Promise<FileStreamContent>;\n}\n\n/** Общие настройки потоковой передачи. */\nexport interface FileStreamOptions {\n /** Буферный или потоковый режим. По умолчанию `'buffer'`. */\n mode?: FileTransferMode | undefined;\n /** Максимальный размер файла. Без значения размер не ограничивается. */\n maxBytes?: number | undefined;\n /**\n * Верхняя граница очереди, которой управляет библиотека. По умолчанию 4 МиБ.\n *\n * Сетевой рантайм может иметь собственные внутренние буферы сверх этого значения.\n */\n streamBufferBytes?: number | undefined;\n}\n\n/** Файл, который нужно получить по HTTP(S). */\nexport interface UrlFile extends UrlFileOptions {\n url: string;\n}\n\n/** Настройки файла по HTTP(S). */\nexport interface UrlFileOptions extends FileStreamOptions {\n filename?: string | undefined;\n /** По умолчанию берётся из `Content-Type` ответа. */\n contentType?: string | undefined;\n}\n\n/** Файл для загрузки. Голый поток не принимается, потому что его нельзя повторить. */\nexport type FileInput =\n | Blob\n | ArrayBuffer\n | Uint8Array\n | FileContent\n | UrlFile\n | LazyFile\n | StreamFile;\n\n/** Настройки фабрики пользовательского потока. */\nexport interface FromStreamOptions extends Omit<FileStreamOptions, 'mode'> {\n filename?: string | undefined;\n contentType?: string | undefined;\n /** Размер, если он известен заранее. */\n size?: number | undefined;\n}\n","import { ItdConfigError, ItdFileError, ItdFileErrorReason } from '../errors.js';\n\n/** Проверяет числовую границу до обращения к источнику. */\nexport function optionalBytes(value: number | undefined, name: string): number | undefined {\n if (value === undefined) return undefined;\n if (!Number.isFinite(value) || value < 0 || !Number.isInteger(value)) {\n throw new ItdConfigError(\n `${name} должен быть неотрицательным целым числом, получено: ${value}`,\n );\n }\n return value;\n}\n\n/** Создаёт типизированную ошибку превышения размера вложения. */\nexport function fileTooLarge(url: string | undefined, limit: number, actual: number): ItdFileError {\n const source = url ? ` по адресу ${url}` : '';\n return new ItdFileError(`файл${source} больше предела в ${limit} байт: ${actual}`, {\n reason: ItdFileErrorReason.TooLarge,\n ...(url ? { url } : {}),\n limit,\n actual,\n });\n}\n","import { ItdConfigError, ItdFileError, ItdFileErrorReason } from '../errors.js';\nimport { DEFAULT_FILE_STREAM_BUFFER_BYTES } from './contracts.js';\nimport { fileTooLarge, optionalBytes } from './limits.js';\n\n/** Потоки, на которые уже наложены счётчик размера и backpressure. */\nconst BOUNDED_FILE_STREAMS = new WeakSet<ReadableStream<Uint8Array>>();\n\n/** Настройки ограничивающей поток обёртки. */\nexport interface BoundedFileStreamOptions {\n maxBytes?: number | undefined;\n streamBufferBytes?: number | undefined;\n signal?: AbortSignal | undefined;\n url?: string | undefined;\n retryableRead?: boolean | undefined;\n}\n\n/** Приводит произвольный чанк потока к байтам. */\nfunction asBytes(value: Uint8Array): Uint8Array {\n return value instanceof Uint8Array ? value : new Uint8Array(value);\n}\n\n/** Проверяет значение без обращения к методам потенциально чужого объекта. */\nexport function isReadableByteStream(value: unknown): value is ReadableStream<Uint8Array> {\n return typeof ReadableStream !== 'undefined' && value instanceof ReadableStream;\n}\n\n/**\n * Ограничивает размер и очередь потока. Отмена результирующего потока отменяет исходный.\n *\n * @internal\n */\nexport function boundedFileStream(\n source: ReadableStream<Uint8Array>,\n options: BoundedFileStreamOptions = {},\n): ReadableStream<Uint8Array> {\n const maxBytes = optionalBytes(options.maxBytes, 'maxBytes');\n const streamBufferBytes =\n optionalBytes(\n options.streamBufferBytes ?? DEFAULT_FILE_STREAM_BUFFER_BYTES,\n 'streamBufferBytes',\n ) ?? DEFAULT_FILE_STREAM_BUFFER_BYTES;\n if (streamBufferBytes === 0) {\n throw new ItdConfigError('streamBufferBytes должен быть больше нуля');\n }\n\n const reader = source.getReader();\n let total = 0;\n let finished = false;\n let abortListener: (() => void) | undefined;\n\n const cleanup = () => {\n if (abortListener) options.signal?.removeEventListener('abort', abortListener);\n abortListener = undefined;\n };\n\n const stream = new ReadableStream<Uint8Array>(\n {\n start(controller) {\n if (!options.signal) return;\n abortListener = () => {\n finished = true;\n void reader.cancel(options.signal?.reason).catch(() => {});\n controller.error(options.signal?.reason ?? new DOMException('Aborted', 'AbortError'));\n cleanup();\n };\n if (options.signal.aborted) abortListener();\n else options.signal.addEventListener('abort', abortListener, { once: true });\n },\n async pull(controller) {\n if (finished) return;\n\n try {\n const next = await reader.read();\n if (finished) return;\n if (next.done) {\n finished = true;\n cleanup();\n controller.close();\n return;\n }\n\n const chunk = asBytes(next.value);\n total += chunk.byteLength;\n if (maxBytes !== undefined && total > maxBytes) {\n finished = true;\n await reader.cancel().catch(() => {});\n cleanup();\n controller.error(fileTooLarge(options.url, maxBytes, total));\n return;\n }\n\n controller.enqueue(chunk);\n } catch (error) {\n finished = true;\n cleanup();\n if (error instanceof ItdFileError) {\n controller.error(error);\n return;\n }\n controller.error(\n new ItdFileError('не удалось прочитать поток вложения', {\n reason: ItdFileErrorReason.Read,\n ...(options.url ? { url: options.url } : {}),\n retryable: options.retryableRead ?? false,\n cause: error,\n }),\n );\n }\n },\n async cancel(reason) {\n finished = true;\n cleanup();\n await reader.cancel(reason).catch(() => {});\n },\n },\n {\n highWaterMark: streamBufferBytes,\n size: (chunk) => chunk.byteLength,\n },\n );\n BOUNDED_FILE_STREAMS.add(stream);\n return stream;\n}\n\n/** Наложены ли на поток ограничения библиотеки. @internal */\nexport function isBoundedFileStream(stream: ReadableStream<Uint8Array>): boolean {\n return BOUNDED_FILE_STREAMS.has(stream);\n}\n","import { ItdConfigError } from '../errors.js';\nimport {\n DEFAULT_FILE_STREAM_BUFFER_BYTES,\n type FileStreamOptions,\n FileTransferMode,\n} from './contracts.js';\nimport { optionalBytes } from './limits.js';\n\n/** Проверяет и дополняет настройки потока. @internal */\nexport function resolveFileStreamOptions(\n options: FileStreamOptions,\n defaultMaxBytes?: number,\n): {\n mode: FileTransferMode;\n maxBytes: number | undefined;\n streamBufferBytes: number;\n} {\n const mode = options.mode ?? FileTransferMode.Buffer;\n if (mode !== FileTransferMode.Buffer && mode !== FileTransferMode.Stream) {\n throw new ItdConfigError(`mode вложения должен быть 'buffer' или 'stream', получено: ${mode}`);\n }\n if (mode === FileTransferMode.Stream && typeof ReadableStream === 'undefined') {\n throw new ItdConfigError(\n \"эта среда не поддерживает ReadableStream; используйте mode: 'buffer'\",\n );\n }\n\n const maxBytes = optionalBytes(options.maxBytes ?? defaultMaxBytes, 'maxBytes');\n const streamBufferBytes =\n optionalBytes(\n options.streamBufferBytes ?? DEFAULT_FILE_STREAM_BUFFER_BYTES,\n 'streamBufferBytes',\n ) ?? DEFAULT_FILE_STREAM_BUFFER_BYTES;\n if (streamBufferBytes === 0) {\n throw new ItdConfigError('streamBufferBytes должен быть больше нуля');\n }\n\n return { mode, maxBytes, streamBufferBytes };\n}\n"],"mappings":"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"}
@@ -0,0 +1,142 @@
1
+ import { a as ItdConfigError, l as ItdFileError, u as ItdFileErrorReason } from "./errors-Bhrd2fJd.js";
2
+ //#region src/core/attachments/contracts.ts
3
+ /** Способ передачи содержимого вложения. */
4
+ const FileTransferMode = Object.freeze({
5
+ /** Сначала получить файл целиком, затем отправить его как `Blob`. */
6
+ Buffer: "buffer",
7
+ /** Передавать данные по мере чтения, не собирая файл целиком в памяти. */
8
+ Stream: "stream"
9
+ });
10
+ /** Предел размера файла, скачиваемого по адресу, — 100 МиБ. */
11
+ const DEFAULT_URL_FILE_MAX_BYTES = 104857600;
12
+ //#endregion
13
+ //#region src/core/attachments/limits.ts
14
+ /** Проверяет числовую границу до обращения к источнику. */
15
+ function optionalBytes(value, name) {
16
+ if (value === void 0) return void 0;
17
+ if (!Number.isFinite(value) || value < 0 || !Number.isInteger(value)) throw new ItdConfigError(`${name} должен быть неотрицательным целым числом, получено: ${value}`);
18
+ return value;
19
+ }
20
+ /** Создаёт типизированную ошибку превышения размера вложения. */
21
+ function fileTooLarge(url, limit, actual) {
22
+ const source = url ? ` по адресу ${url}` : "";
23
+ return new ItdFileError(`файл${source} больше предела в ${limit} байт: ${actual}`, {
24
+ reason: ItdFileErrorReason.TooLarge,
25
+ ...url ? { url } : {},
26
+ limit,
27
+ actual
28
+ });
29
+ }
30
+ //#endregion
31
+ //#region src/core/attachments/bounded-stream.ts
32
+ /** Потоки, на которые уже наложены счётчик размера и backpressure. */
33
+ const BOUNDED_FILE_STREAMS = /* @__PURE__ */ new WeakSet();
34
+ /** Приводит произвольный чанк потока к байтам. */
35
+ function asBytes(value) {
36
+ return value instanceof Uint8Array ? value : new Uint8Array(value);
37
+ }
38
+ /** Проверяет значение без обращения к методам потенциально чужого объекта. */
39
+ function isReadableByteStream(value) {
40
+ return typeof ReadableStream !== "undefined" && value instanceof ReadableStream;
41
+ }
42
+ /**
43
+ * Ограничивает размер и очередь потока. Отмена результирующего потока отменяет исходный.
44
+ *
45
+ * @internal
46
+ */
47
+ function boundedFileStream(source, options = {}) {
48
+ const maxBytes = optionalBytes(options.maxBytes, "maxBytes");
49
+ const streamBufferBytes = optionalBytes(options.streamBufferBytes ?? 4194304, "streamBufferBytes") ?? 4194304;
50
+ if (streamBufferBytes === 0) throw new ItdConfigError("streamBufferBytes должен быть больше нуля");
51
+ const reader = source.getReader();
52
+ let total = 0;
53
+ let finished = false;
54
+ let abortListener;
55
+ const cleanup = () => {
56
+ if (abortListener) options.signal?.removeEventListener("abort", abortListener);
57
+ abortListener = void 0;
58
+ };
59
+ const stream = new ReadableStream({
60
+ start(controller) {
61
+ if (!options.signal) return;
62
+ abortListener = () => {
63
+ finished = true;
64
+ reader.cancel(options.signal?.reason).catch(() => {});
65
+ controller.error(options.signal?.reason ?? new DOMException("Aborted", "AbortError"));
66
+ cleanup();
67
+ };
68
+ if (options.signal.aborted) abortListener();
69
+ else options.signal.addEventListener("abort", abortListener, { once: true });
70
+ },
71
+ async pull(controller) {
72
+ if (finished) return;
73
+ try {
74
+ const next = await reader.read();
75
+ if (finished) return;
76
+ if (next.done) {
77
+ finished = true;
78
+ cleanup();
79
+ controller.close();
80
+ return;
81
+ }
82
+ const chunk = asBytes(next.value);
83
+ total += chunk.byteLength;
84
+ if (maxBytes !== void 0 && total > maxBytes) {
85
+ finished = true;
86
+ await reader.cancel().catch(() => {});
87
+ cleanup();
88
+ controller.error(fileTooLarge(options.url, maxBytes, total));
89
+ return;
90
+ }
91
+ controller.enqueue(chunk);
92
+ } catch (error) {
93
+ finished = true;
94
+ cleanup();
95
+ if (error instanceof ItdFileError) {
96
+ controller.error(error);
97
+ return;
98
+ }
99
+ controller.error(new ItdFileError("не удалось прочитать поток вложения", {
100
+ reason: ItdFileErrorReason.Read,
101
+ ...options.url ? { url: options.url } : {},
102
+ retryable: options.retryableRead ?? false,
103
+ cause: error
104
+ }));
105
+ }
106
+ },
107
+ async cancel(reason) {
108
+ finished = true;
109
+ cleanup();
110
+ await reader.cancel(reason).catch(() => {});
111
+ }
112
+ }, {
113
+ highWaterMark: streamBufferBytes,
114
+ size: (chunk) => chunk.byteLength
115
+ });
116
+ BOUNDED_FILE_STREAMS.add(stream);
117
+ return stream;
118
+ }
119
+ /** Наложены ли на поток ограничения библиотеки. @internal */
120
+ function isBoundedFileStream(stream) {
121
+ return BOUNDED_FILE_STREAMS.has(stream);
122
+ }
123
+ //#endregion
124
+ //#region src/core/attachments/options.ts
125
+ /** Проверяет и дополняет настройки потока. @internal */
126
+ function resolveFileStreamOptions(options, defaultMaxBytes) {
127
+ const mode = options.mode ?? FileTransferMode.Buffer;
128
+ if (mode !== FileTransferMode.Buffer && mode !== FileTransferMode.Stream) throw new ItdConfigError(`mode вложения должен быть 'buffer' или 'stream', получено: ${mode}`);
129
+ if (mode === FileTransferMode.Stream && typeof ReadableStream === "undefined") throw new ItdConfigError("эта среда не поддерживает ReadableStream; используйте mode: 'buffer'");
130
+ const maxBytes = optionalBytes(options.maxBytes ?? defaultMaxBytes, "maxBytes");
131
+ const streamBufferBytes = optionalBytes(options.streamBufferBytes ?? 4194304, "streamBufferBytes") ?? 4194304;
132
+ if (streamBufferBytes === 0) throw new ItdConfigError("streamBufferBytes должен быть больше нуля");
133
+ return {
134
+ mode,
135
+ maxBytes,
136
+ streamBufferBytes
137
+ };
138
+ }
139
+ //#endregion
140
+ export { fileTooLarge as a, FileTransferMode as c, isReadableByteStream as i, boundedFileStream as n, optionalBytes as o, isBoundedFileStream as r, DEFAULT_URL_FILE_MAX_BYTES as s, resolveFileStreamOptions as t };
141
+
142
+ //# sourceMappingURL=options-DtATYdLr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options-DtATYdLr.js","names":[],"sources":["../../src/core/attachments/contracts.ts","../../src/core/attachments/limits.ts","../../src/core/attachments/bounded-stream.ts","../../src/core/attachments/options.ts"],"sourcesContent":["/** Способ передачи содержимого вложения. */\nexport const FileTransferMode = Object.freeze({\n /** Сначала получить файл целиком, затем отправить его как `Blob`. */\n Buffer: 'buffer',\n /** Передавать данные по мере чтения, не собирая файл целиком в памяти. */\n Stream: 'stream',\n} as const);\nexport type FileTransferMode = (typeof FileTransferMode)[keyof typeof FileTransferMode];\n\n/** Размер очереди потокового вложения по умолчанию — 4 МиБ. */\nexport const DEFAULT_FILE_STREAM_BUFFER_BYTES = 4 * 1024 * 1024;\n\n/** Предел размера файла, скачиваемого по адресу, — 100 МиБ. */\nexport const DEFAULT_URL_FILE_MAX_BYTES = 100 * 1024 * 1024;\n\n/** Что доступно источнику при открытии файла. */\nexport interface FileContext {\n /** Общая отмена текущей попытки, включая её таймаут. */\n signal?: AbortSignal | undefined;\n /** `fetch` клиента — с переданными пользователем настройками. */\n fetch: typeof fetch;\n /** Номер попытки отправки, начиная с 1. */\n attempt: number;\n}\n\n/** Готовое содержимое файла. */\nexport interface FileContent {\n file: Blob | ArrayBuffer | Uint8Array;\n /** Имя влияет на определение MIME, если `contentType` не задан. */\n filename?: string | undefined;\n /** MIME без параметров. Если не указан, определяется по имени или `Blob`. */\n contentType?: string | undefined;\n}\n\n/** Открытый поток файла. */\nexport interface FileStreamContent {\n stream: ReadableStream<Uint8Array>;\n filename?: string | undefined;\n contentType?: string | undefined;\n /** Размер, если он известен до чтения. */\n size?: number | undefined;\n /** Освобождает внешний ресурс после завершения попытки. */\n close?: (() => void | Promise<void>) | undefined;\n}\n\n/** Буферный источник, который вычисляется непосредственно перед первой попыткой. */\nexport interface LazyFile {\n load: (context: FileContext) => FileContent | Promise<FileContent>;\n}\n\n/**\n * Повторяемый потоковый источник.\n *\n * `open` вызывается для каждой попытки отправки. Он обязан возвращать новый поток:\n * уже прочитанный `ReadableStream` повторно использовать нельзя.\n */\nexport interface StreamFile {\n open: (context: FileContext) => FileStreamContent | Promise<FileStreamContent>;\n}\n\n/** Общие настройки потоковой передачи. */\nexport interface FileStreamOptions {\n /** Буферный или потоковый режим. По умолчанию `'buffer'`. */\n mode?: FileTransferMode | undefined;\n /** Максимальный размер файла. Без значения размер не ограничивается. */\n maxBytes?: number | undefined;\n /**\n * Верхняя граница очереди, которой управляет библиотека. По умолчанию 4 МиБ.\n *\n * Сетевой рантайм может иметь собственные внутренние буферы сверх этого значения.\n */\n streamBufferBytes?: number | undefined;\n}\n\n/** Файл, который нужно получить по HTTP(S). */\nexport interface UrlFile extends UrlFileOptions {\n url: string;\n}\n\n/** Настройки файла по HTTP(S). */\nexport interface UrlFileOptions extends FileStreamOptions {\n filename?: string | undefined;\n /** По умолчанию берётся из `Content-Type` ответа. */\n contentType?: string | undefined;\n}\n\n/** Файл для загрузки. Голый поток не принимается, потому что его нельзя повторить. */\nexport type FileInput =\n | Blob\n | ArrayBuffer\n | Uint8Array\n | FileContent\n | UrlFile\n | LazyFile\n | StreamFile;\n\n/** Настройки фабрики пользовательского потока. */\nexport interface FromStreamOptions extends Omit<FileStreamOptions, 'mode'> {\n filename?: string | undefined;\n contentType?: string | undefined;\n /** Размер, если он известен заранее. */\n size?: number | undefined;\n}\n","import { ItdConfigError, ItdFileError, ItdFileErrorReason } from '../errors.js';\n\n/** Проверяет числовую границу до обращения к источнику. */\nexport function optionalBytes(value: number | undefined, name: string): number | undefined {\n if (value === undefined) return undefined;\n if (!Number.isFinite(value) || value < 0 || !Number.isInteger(value)) {\n throw new ItdConfigError(\n `${name} должен быть неотрицательным целым числом, получено: ${value}`,\n );\n }\n return value;\n}\n\n/** Создаёт типизированную ошибку превышения размера вложения. */\nexport function fileTooLarge(url: string | undefined, limit: number, actual: number): ItdFileError {\n const source = url ? ` по адресу ${url}` : '';\n return new ItdFileError(`файл${source} больше предела в ${limit} байт: ${actual}`, {\n reason: ItdFileErrorReason.TooLarge,\n ...(url ? { url } : {}),\n limit,\n actual,\n });\n}\n","import { ItdConfigError, ItdFileError, ItdFileErrorReason } from '../errors.js';\nimport { DEFAULT_FILE_STREAM_BUFFER_BYTES } from './contracts.js';\nimport { fileTooLarge, optionalBytes } from './limits.js';\n\n/** Потоки, на которые уже наложены счётчик размера и backpressure. */\nconst BOUNDED_FILE_STREAMS = new WeakSet<ReadableStream<Uint8Array>>();\n\n/** Настройки ограничивающей поток обёртки. */\nexport interface BoundedFileStreamOptions {\n maxBytes?: number | undefined;\n streamBufferBytes?: number | undefined;\n signal?: AbortSignal | undefined;\n url?: string | undefined;\n retryableRead?: boolean | undefined;\n}\n\n/** Приводит произвольный чанк потока к байтам. */\nfunction asBytes(value: Uint8Array): Uint8Array {\n return value instanceof Uint8Array ? value : new Uint8Array(value);\n}\n\n/** Проверяет значение без обращения к методам потенциально чужого объекта. */\nexport function isReadableByteStream(value: unknown): value is ReadableStream<Uint8Array> {\n return typeof ReadableStream !== 'undefined' && value instanceof ReadableStream;\n}\n\n/**\n * Ограничивает размер и очередь потока. Отмена результирующего потока отменяет исходный.\n *\n * @internal\n */\nexport function boundedFileStream(\n source: ReadableStream<Uint8Array>,\n options: BoundedFileStreamOptions = {},\n): ReadableStream<Uint8Array> {\n const maxBytes = optionalBytes(options.maxBytes, 'maxBytes');\n const streamBufferBytes =\n optionalBytes(\n options.streamBufferBytes ?? DEFAULT_FILE_STREAM_BUFFER_BYTES,\n 'streamBufferBytes',\n ) ?? DEFAULT_FILE_STREAM_BUFFER_BYTES;\n if (streamBufferBytes === 0) {\n throw new ItdConfigError('streamBufferBytes должен быть больше нуля');\n }\n\n const reader = source.getReader();\n let total = 0;\n let finished = false;\n let abortListener: (() => void) | undefined;\n\n const cleanup = () => {\n if (abortListener) options.signal?.removeEventListener('abort', abortListener);\n abortListener = undefined;\n };\n\n const stream = new ReadableStream<Uint8Array>(\n {\n start(controller) {\n if (!options.signal) return;\n abortListener = () => {\n finished = true;\n void reader.cancel(options.signal?.reason).catch(() => {});\n controller.error(options.signal?.reason ?? new DOMException('Aborted', 'AbortError'));\n cleanup();\n };\n if (options.signal.aborted) abortListener();\n else options.signal.addEventListener('abort', abortListener, { once: true });\n },\n async pull(controller) {\n if (finished) return;\n\n try {\n const next = await reader.read();\n if (finished) return;\n if (next.done) {\n finished = true;\n cleanup();\n controller.close();\n return;\n }\n\n const chunk = asBytes(next.value);\n total += chunk.byteLength;\n if (maxBytes !== undefined && total > maxBytes) {\n finished = true;\n await reader.cancel().catch(() => {});\n cleanup();\n controller.error(fileTooLarge(options.url, maxBytes, total));\n return;\n }\n\n controller.enqueue(chunk);\n } catch (error) {\n finished = true;\n cleanup();\n if (error instanceof ItdFileError) {\n controller.error(error);\n return;\n }\n controller.error(\n new ItdFileError('не удалось прочитать поток вложения', {\n reason: ItdFileErrorReason.Read,\n ...(options.url ? { url: options.url } : {}),\n retryable: options.retryableRead ?? false,\n cause: error,\n }),\n );\n }\n },\n async cancel(reason) {\n finished = true;\n cleanup();\n await reader.cancel(reason).catch(() => {});\n },\n },\n {\n highWaterMark: streamBufferBytes,\n size: (chunk) => chunk.byteLength,\n },\n );\n BOUNDED_FILE_STREAMS.add(stream);\n return stream;\n}\n\n/** Наложены ли на поток ограничения библиотеки. @internal */\nexport function isBoundedFileStream(stream: ReadableStream<Uint8Array>): boolean {\n return BOUNDED_FILE_STREAMS.has(stream);\n}\n","import { ItdConfigError } from '../errors.js';\nimport {\n DEFAULT_FILE_STREAM_BUFFER_BYTES,\n type FileStreamOptions,\n FileTransferMode,\n} from './contracts.js';\nimport { optionalBytes } from './limits.js';\n\n/** Проверяет и дополняет настройки потока. @internal */\nexport function resolveFileStreamOptions(\n options: FileStreamOptions,\n defaultMaxBytes?: number,\n): {\n mode: FileTransferMode;\n maxBytes: number | undefined;\n streamBufferBytes: number;\n} {\n const mode = options.mode ?? FileTransferMode.Buffer;\n if (mode !== FileTransferMode.Buffer && mode !== FileTransferMode.Stream) {\n throw new ItdConfigError(`mode вложения должен быть 'buffer' или 'stream', получено: ${mode}`);\n }\n if (mode === FileTransferMode.Stream && typeof ReadableStream === 'undefined') {\n throw new ItdConfigError(\n \"эта среда не поддерживает ReadableStream; используйте mode: 'buffer'\",\n );\n }\n\n const maxBytes = optionalBytes(options.maxBytes ?? defaultMaxBytes, 'maxBytes');\n const streamBufferBytes =\n optionalBytes(\n options.streamBufferBytes ?? DEFAULT_FILE_STREAM_BUFFER_BYTES,\n 'streamBufferBytes',\n ) ?? DEFAULT_FILE_STREAM_BUFFER_BYTES;\n if (streamBufferBytes === 0) {\n throw new ItdConfigError('streamBufferBytes должен быть больше нуля');\n }\n\n return { mode, maxBytes, streamBufferBytes };\n}\n"],"mappings":"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"}