itd-api 0.7.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.
- package/dist/index.cjs +465 -9804
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +231 -4950
- package/dist/index.d.ts +231 -4950
- package/dist/index.js +331 -9670
- package/dist/index.js.map +1 -1
- package/dist/{node.cjs → node/index.cjs} +16 -14
- package/dist/node/index.cjs.map +1 -0
- package/dist/{node.d.cts → node/index.d.cts} +4 -3
- package/dist/{node.d.ts → node/index.d.ts} +4 -3
- package/dist/{node.js → node/index.js} +5 -3
- package/dist/node/index.js.map +1 -0
- package/dist/realtime/index.cjs +165 -0
- package/dist/realtime/index.cjs.map +1 -0
- package/dist/realtime/index.d.cts +51 -0
- package/dist/realtime/index.d.ts +51 -0
- package/dist/realtime/index.js +120 -0
- package/dist/realtime/index.js.map +1 -0
- package/dist/rest/index.cjs +334 -0
- package/dist/rest/index.cjs.map +1 -0
- package/dist/rest/index.d.cts +138 -0
- package/dist/rest/index.d.ts +138 -0
- package/dist/rest/index.js +238 -0
- package/dist/rest/index.js.map +1 -0
- package/dist/shared/auth-provider-BfogACAb.js +91 -0
- package/dist/shared/auth-provider-BfogACAb.js.map +1 -0
- package/dist/shared/auth-provider-CTJkKfgy.cjs +108 -0
- package/dist/shared/auth-provider-CTJkKfgy.cjs.map +1 -0
- package/dist/shared/contracts-BoT7msmq.d.cts +84 -0
- package/dist/shared/contracts-BoT7msmq.d.ts +84 -0
- package/dist/{multi-storage-Bf84xiO8.cjs → shared/cookies-DZwFq6kr.cjs} +98 -429
- package/dist/shared/cookies-DZwFq6kr.cjs.map +1 -0
- package/dist/{multi-storage-BUZaLAPO.js → shared/cookies-tX2sNwxb.js} +99 -352
- package/dist/shared/cookies-tX2sNwxb.js.map +1 -0
- package/dist/{storage-IHdXw52v.js → shared/errors-Bhrd2fJd.js} +2 -229
- package/dist/shared/errors-Bhrd2fJd.js.map +1 -0
- package/dist/{storage-DnzZPS_9.cjs → shared/errors-DfU8M5eS.cjs} +1 -288
- package/dist/shared/errors-DfU8M5eS.cjs.map +1 -0
- package/dist/shared/multi-storage--yTEqiod.cjs +150 -0
- package/dist/shared/multi-storage--yTEqiod.cjs.map +1 -0
- package/dist/shared/multi-storage-CjAPB5Kq.d.cts +72 -0
- package/dist/shared/multi-storage-CkvTUC5m.js +121 -0
- package/dist/shared/multi-storage-CkvTUC5m.js.map +1 -0
- package/dist/shared/multi-storage-DccjD7Ww.d.ts +72 -0
- package/dist/shared/options-Dg5N3r1V.cjs +189 -0
- package/dist/shared/options-Dg5N3r1V.cjs.map +1 -0
- package/dist/shared/options-DtATYdLr.js +142 -0
- package/dist/shared/options-DtATYdLr.js.map +1 -0
- package/dist/shared/render-DMp_3Nzk.d.cts +2250 -0
- package/dist/shared/render-DZrxhC5_.d.ts +2250 -0
- package/dist/shared/render-DyeHJNBw.cjs +4147 -0
- package/dist/shared/render-DyeHJNBw.cjs.map +1 -0
- package/dist/shared/render-vtLixIiU.js +3992 -0
- package/dist/shared/render-vtLixIiU.js.map +1 -0
- package/dist/shared/storage-BPJR_k4-.cjs +290 -0
- package/dist/shared/storage-BPJR_k4-.cjs.map +1 -0
- package/dist/{storage-BqMxs76Y.d.ts → shared/storage-C_eICCep.d.cts} +2 -2
- package/dist/{storage-BqMxs76Y.d.cts → shared/storage-C_eICCep.d.ts} +2 -2
- package/dist/shared/storage-D86edNCB.js +231 -0
- package/dist/shared/storage-D86edNCB.js.map +1 -0
- package/dist/shared/url-B6-bXHKt.d.cts +2083 -0
- package/dist/shared/url-B6-bXHKt.d.ts +2083 -0
- package/dist/shared/url-CYXgxqGx.js +3673 -0
- package/dist/shared/url-CYXgxqGx.js.map +1 -0
- package/dist/shared/url-yjl2c8Ie.cjs +4032 -0
- package/dist/shared/url-yjl2c8Ie.cjs.map +1 -0
- package/dist/shared/websocket-BMtihD56.d.ts +562 -0
- package/dist/shared/websocket-CbzB1Leq.js +1874 -0
- package/dist/shared/websocket-CbzB1Leq.js.map +1 -0
- package/dist/shared/websocket-D1p32SB2.d.cts +562 -0
- package/dist/shared/websocket-sYlynrr0.cjs +1945 -0
- package/dist/shared/websocket-sYlynrr0.cjs.map +1 -0
- package/dist/{web.cjs → web/index.cjs} +4 -3
- package/dist/web/index.cjs.map +1 -0
- package/dist/{web.d.cts → web/index.d.cts} +2 -2
- package/dist/{web.d.ts → web/index.d.ts} +2 -2
- package/dist/{web.js → web/index.js} +3 -2
- package/dist/web/index.js.map +1 -0
- package/package.json +40 -12
- package/dist/multi-storage-BUZaLAPO.js.map +0 -1
- package/dist/multi-storage-BUoEoW8f.d.cts +0 -154
- package/dist/multi-storage-Bf84xiO8.cjs.map +0 -1
- package/dist/multi-storage-CQSlI_kn.d.ts +0 -154
- package/dist/node.cjs.map +0 -1
- package/dist/node.js.map +0 -1
- package/dist/storage-DnzZPS_9.cjs.map +0 -1
- package/dist/storage-IHdXw52v.js.map +0 -1
- package/dist/web.cjs.map +0 -1
- package/dist/web.js.map +0 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { et as createDeviceId } from "./url-CYXgxqGx.js";
|
|
2
|
+
//#region src/core/auth-provider.ts
|
|
3
|
+
/**
|
|
4
|
+
* Идентификатор устройства, общий для провайдеров без сессии.
|
|
5
|
+
*
|
|
6
|
+
* Значение своё у каждого провайдера: сервер различает по нему записи в списке сессий,
|
|
7
|
+
* поэтому делить его между клиентами нельзя. Хранилища нет, так что перезапуск процесса
|
|
8
|
+
* значение не переживает — в этом и разница с полноценной сессией.
|
|
9
|
+
*/
|
|
10
|
+
function localDeviceId() {
|
|
11
|
+
let deviceId;
|
|
12
|
+
return () => {
|
|
13
|
+
deviceId ??= createDeviceId();
|
|
14
|
+
return Promise.resolve(deviceId);
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Авторизации нет: заголовок не подставляется, ответ `401` не восстанавливается.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* const api = createRestClient(); // публичные эндпоинты доступны и без токена
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
function anonymousAuth() {
|
|
26
|
+
return {
|
|
27
|
+
token: () => Promise.resolve(null),
|
|
28
|
+
prepare: () => Promise.resolve(),
|
|
29
|
+
currentHeaders: () => ({}),
|
|
30
|
+
recover: () => Promise.resolve(false),
|
|
31
|
+
deviceId: localDeviceId(),
|
|
32
|
+
dispose: () => {}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Готовый Bearer-токен: ни хранилища, ни продления.
|
|
37
|
+
*
|
|
38
|
+
* Ответ `401` уходит вызывающему коду как есть — обновить токен провайдеру нечем.
|
|
39
|
+
* Для сессии, которая продлевает себя сама, нужен полный клиент.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* const api = createRestClient({ auth: bearerToken(process.env.ITD_TOKEN) });
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
function bearerToken(accessToken) {
|
|
47
|
+
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
48
|
+
return {
|
|
49
|
+
token: () => Promise.resolve(accessToken),
|
|
50
|
+
prepare: () => Promise.resolve(),
|
|
51
|
+
currentHeaders: () => headers,
|
|
52
|
+
recover: () => Promise.resolve(false),
|
|
53
|
+
deviceId: localDeviceId(),
|
|
54
|
+
dispose: () => {}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Токен из внешнего источника — хранилища секретов, кэша, соседнего сервиса.
|
|
59
|
+
*
|
|
60
|
+
* Источник спрашивается на стадии подготовки, до входа в очередь: там ожидание безопасно,
|
|
61
|
+
* а слот транспорта ещё не занят. Значение держится до следующей подготовки, потому что
|
|
62
|
+
* подстановка заголовков обязана быть синхронной.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* const api = createRestClient({ auth: tokenProvider(() => vault.read('itd')) });
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
function tokenProvider(getToken) {
|
|
70
|
+
let cached = null;
|
|
71
|
+
const read = async () => {
|
|
72
|
+
cached = await getToken();
|
|
73
|
+
return cached;
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
token: read,
|
|
77
|
+
prepare: async () => {
|
|
78
|
+
await read();
|
|
79
|
+
},
|
|
80
|
+
currentHeaders: () => cached ? { Authorization: `Bearer ${cached}` } : {},
|
|
81
|
+
recover: () => Promise.resolve(false),
|
|
82
|
+
deviceId: localDeviceId(),
|
|
83
|
+
dispose: () => {
|
|
84
|
+
cached = null;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
export { bearerToken as n, tokenProvider as r, anonymousAuth as t };
|
|
90
|
+
|
|
91
|
+
//# sourceMappingURL=auth-provider-BfogACAb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-provider-BfogACAb.js","names":[],"sources":["../../src/core/auth-provider.ts"],"sourcesContent":["import type { UserId } from '../models/common.js';\nimport type { ResolvedRuntimeConfig } from './config.js';\nimport type { CookieJar } from './cookies.js';\nimport type { RequestHandler } from './execution/pipeline.js';\nimport { createDeviceId } from './runtime.js';\n\n/** Области аккаунта и конкретной сессии для локального состояния плагинов. */\nexport interface AuthIdentity {\n /** Идентификатор пользователя; отсутствует у непрозрачного или повреждённого токена. */\n userId?: UserId | undefined;\n /** Идентификатор серверной сессии; отсутствует у непрозрачного или повреждённого токена. */\n sessionId?: string | undefined;\n}\n\n/**\n * Что конвейер запросов спрашивает у авторизации.\n *\n * Узкий контракт вместо полноценного менеджера сессии: pipeline не должен знать ни про\n * refresh-токены, ни про хранилище, ни про вход по паролю. Благодаря этому клиент с готовым\n * токеном не тянет за собой сессионную машинерию — она подставляется вызывающим кодом.\n *\n * Каждый метод соответствует ровно одной стадии конвейера. Готовые реализации —\n * {@link bearerToken}, {@link tokenProvider} и {@link anonymousAuth}.\n */\nexport interface AuthProvider {\n /**\n * Текущий токен доступа.\n *\n * Нужен там, где заголовок не поставить: SSE в браузере и WebSocket передают токен\n * параметром адреса. Конвейеру запросов достаточно {@link currentHeaders}.\n */\n token(): Promise<string | null>;\n /**\n * Готовит состояние авторизации до входа транспортной попытки в очередь.\n *\n * Чтение хранилища, обращение к внешнему источнику токена и отложенный вход асинхронны,\n * поэтому обязаны завершиться до захвата слота очереди.\n */\n prepare(): Promise<void>;\n /**\n * Заголовки уже подготовленной авторизации.\n *\n * Синхронность существенна: слой стоит внутри очереди, непосредственно перед транспортом,\n * и не должен запускать I/O. Зато запрос, отстоявший в очереди, получает самый свежий токен.\n */\n currentHeaders(): Record<string, string>;\n /**\n * Реакция на ответ `401`.\n *\n * @returns `true`, если токен обновлён и повторять попытку имеет смысл\n */\n recover(): Promise<boolean>;\n /** Значение заголовка `X-Device-Id`. Отправляется и с анонимными запросами. */\n deviceId(): Promise<string>;\n /** Снимает подписки при терминальном освобождении владельца. */\n dispose(): void;\n}\n\n/**\n * Что фабрика авторизации получает от собранного runtime.\n *\n * `handler` — тот же обработчик, которым пользуются ресурсы: вход и продление проходят\n * через общий конвейер с точечными skip-флагами, а не через второй, независимо\n * эволюционирующий путь.\n *\n * @internal\n */\nexport interface AuthProviderDeps {\n config: ResolvedRuntimeConfig;\n handler: RequestHandler;\n cookies: CookieJar;\n /** Вызывается синхронно перед сменой владельца авторизации. */\n onAccountChange?: (() => void) | undefined;\n}\n\n/**\n * Идентификатор устройства, общий для провайдеров без сессии.\n *\n * Значение своё у каждого провайдера: сервер различает по нему записи в списке сессий,\n * поэтому делить его между клиентами нельзя. Хранилища нет, так что перезапуск процесса\n * значение не переживает — в этом и разница с полноценной сессией.\n */\nfunction localDeviceId(): () => Promise<string> {\n let deviceId: string | undefined;\n\n return () => {\n deviceId ??= createDeviceId();\n return Promise.resolve(deviceId);\n };\n}\n\n/**\n * Авторизации нет: заголовок не подставляется, ответ `401` не восстанавливается.\n *\n * @example\n * ```ts\n * const api = createRestClient(); // публичные эндпоинты доступны и без токена\n * ```\n */\nexport function anonymousAuth(): AuthProvider {\n return {\n token: () => Promise.resolve(null),\n prepare: () => Promise.resolve(),\n currentHeaders: () => ({}),\n recover: () => Promise.resolve(false),\n deviceId: localDeviceId(),\n dispose: () => {},\n };\n}\n\n/**\n * Готовый Bearer-токен: ни хранилища, ни продления.\n *\n * Ответ `401` уходит вызывающему коду как есть — обновить токен провайдеру нечем.\n * Для сессии, которая продлевает себя сама, нужен полный клиент.\n *\n * @example\n * ```ts\n * const api = createRestClient({ auth: bearerToken(process.env.ITD_TOKEN) });\n * ```\n */\nexport function bearerToken(accessToken: string): AuthProvider {\n const headers = { Authorization: `Bearer ${accessToken}` };\n\n return {\n token: () => Promise.resolve(accessToken),\n prepare: () => Promise.resolve(),\n currentHeaders: () => headers,\n recover: () => Promise.resolve(false),\n deviceId: localDeviceId(),\n dispose: () => {},\n };\n}\n\n/**\n * Токен из внешнего источника — хранилища секретов, кэша, соседнего сервиса.\n *\n * Источник спрашивается на стадии подготовки, до входа в очередь: там ожидание безопасно,\n * а слот транспорта ещё не занят. Значение держится до следующей подготовки, потому что\n * подстановка заголовков обязана быть синхронной.\n *\n * @example\n * ```ts\n * const api = createRestClient({ auth: tokenProvider(() => vault.read('itd')) });\n * ```\n */\nexport function tokenProvider(\n getToken: () => string | null | Promise<string | null>,\n): AuthProvider {\n let cached: string | null = null;\n\n const read = async () => {\n cached = await getToken();\n return cached;\n };\n\n return {\n token: read,\n prepare: async () => {\n await read();\n },\n currentHeaders: () => (cached ? { Authorization: `Bearer ${cached}` } : {}),\n recover: () => Promise.resolve(false),\n deviceId: localDeviceId(),\n dispose: () => {\n cached = null;\n },\n };\n}\n"],"mappings":";;;;;;;;;AAkFA,SAAS,gBAAuC;CAC9C,IAAI;CAEJ,aAAa;EACX,aAAa,eAAe;EAC5B,OAAO,QAAQ,QAAQ,QAAQ;CACjC;AACF;;;;;;;;;AAUA,SAAgB,gBAA8B;CAC5C,OAAO;EACL,aAAa,QAAQ,QAAQ,IAAI;EACjC,eAAe,QAAQ,QAAQ;EAC/B,uBAAuB,CAAC;EACxB,eAAe,QAAQ,QAAQ,KAAK;EACpC,UAAU,cAAc;EACxB,eAAe,CAAC;CAClB;AACF;;;;;;;;;;;;AAaA,SAAgB,YAAY,aAAmC;CAC7D,MAAM,UAAU,EAAE,eAAe,UAAU,cAAc;CAEzD,OAAO;EACL,aAAa,QAAQ,QAAQ,WAAW;EACxC,eAAe,QAAQ,QAAQ;EAC/B,sBAAsB;EACtB,eAAe,QAAQ,QAAQ,KAAK;EACpC,UAAU,cAAc;EACxB,eAAe,CAAC;CAClB;AACF;;;;;;;;;;;;;AAcA,SAAgB,cACd,UACc;CACd,IAAI,SAAwB;CAE5B,MAAM,OAAO,YAAY;EACvB,SAAS,MAAM,SAAS;EACxB,OAAO;CACT;CAEA,OAAO;EACL,OAAO;EACP,SAAS,YAAY;GACnB,MAAM,KAAK;EACb;EACA,sBAAuB,SAAS,EAAE,eAAe,UAAU,SAAS,IAAI,CAAC;EACzE,eAAe,QAAQ,QAAQ,KAAK;EACpC,UAAU,cAAc;EACxB,eAAe;GACb,SAAS;EACX;CACF;AACF"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
const require_url = require("./url-yjl2c8Ie.cjs");
|
|
2
|
+
//#region src/core/auth-provider.ts
|
|
3
|
+
/**
|
|
4
|
+
* Идентификатор устройства, общий для провайдеров без сессии.
|
|
5
|
+
*
|
|
6
|
+
* Значение своё у каждого провайдера: сервер различает по нему записи в списке сессий,
|
|
7
|
+
* поэтому делить его между клиентами нельзя. Хранилища нет, так что перезапуск процесса
|
|
8
|
+
* значение не переживает — в этом и разница с полноценной сессией.
|
|
9
|
+
*/
|
|
10
|
+
function localDeviceId() {
|
|
11
|
+
let deviceId;
|
|
12
|
+
return () => {
|
|
13
|
+
deviceId ??= require_url.createDeviceId();
|
|
14
|
+
return Promise.resolve(deviceId);
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Авторизации нет: заголовок не подставляется, ответ `401` не восстанавливается.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* const api = createRestClient(); // публичные эндпоинты доступны и без токена
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
function anonymousAuth() {
|
|
26
|
+
return {
|
|
27
|
+
token: () => Promise.resolve(null),
|
|
28
|
+
prepare: () => Promise.resolve(),
|
|
29
|
+
currentHeaders: () => ({}),
|
|
30
|
+
recover: () => Promise.resolve(false),
|
|
31
|
+
deviceId: localDeviceId(),
|
|
32
|
+
dispose: () => {}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Готовый Bearer-токен: ни хранилища, ни продления.
|
|
37
|
+
*
|
|
38
|
+
* Ответ `401` уходит вызывающему коду как есть — обновить токен провайдеру нечем.
|
|
39
|
+
* Для сессии, которая продлевает себя сама, нужен полный клиент.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* const api = createRestClient({ auth: bearerToken(process.env.ITD_TOKEN) });
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
function bearerToken(accessToken) {
|
|
47
|
+
const headers = { Authorization: `Bearer ${accessToken}` };
|
|
48
|
+
return {
|
|
49
|
+
token: () => Promise.resolve(accessToken),
|
|
50
|
+
prepare: () => Promise.resolve(),
|
|
51
|
+
currentHeaders: () => headers,
|
|
52
|
+
recover: () => Promise.resolve(false),
|
|
53
|
+
deviceId: localDeviceId(),
|
|
54
|
+
dispose: () => {}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Токен из внешнего источника — хранилища секретов, кэша, соседнего сервиса.
|
|
59
|
+
*
|
|
60
|
+
* Источник спрашивается на стадии подготовки, до входа в очередь: там ожидание безопасно,
|
|
61
|
+
* а слот транспорта ещё не занят. Значение держится до следующей подготовки, потому что
|
|
62
|
+
* подстановка заголовков обязана быть синхронной.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* const api = createRestClient({ auth: tokenProvider(() => vault.read('itd')) });
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
function tokenProvider(getToken) {
|
|
70
|
+
let cached = null;
|
|
71
|
+
const read = async () => {
|
|
72
|
+
cached = await getToken();
|
|
73
|
+
return cached;
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
token: read,
|
|
77
|
+
prepare: async () => {
|
|
78
|
+
await read();
|
|
79
|
+
},
|
|
80
|
+
currentHeaders: () => cached ? { Authorization: `Bearer ${cached}` } : {},
|
|
81
|
+
recover: () => Promise.resolve(false),
|
|
82
|
+
deviceId: localDeviceId(),
|
|
83
|
+
dispose: () => {
|
|
84
|
+
cached = null;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
Object.defineProperty(exports, "anonymousAuth", {
|
|
90
|
+
enumerable: true,
|
|
91
|
+
get: function() {
|
|
92
|
+
return anonymousAuth;
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
Object.defineProperty(exports, "bearerToken", {
|
|
96
|
+
enumerable: true,
|
|
97
|
+
get: function() {
|
|
98
|
+
return bearerToken;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
Object.defineProperty(exports, "tokenProvider", {
|
|
102
|
+
enumerable: true,
|
|
103
|
+
get: function() {
|
|
104
|
+
return tokenProvider;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
//# sourceMappingURL=auth-provider-CTJkKfgy.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-provider-CTJkKfgy.cjs","names":["createDeviceId"],"sources":["../../src/core/auth-provider.ts"],"sourcesContent":["import type { UserId } from '../models/common.js';\nimport type { ResolvedRuntimeConfig } from './config.js';\nimport type { CookieJar } from './cookies.js';\nimport type { RequestHandler } from './execution/pipeline.js';\nimport { createDeviceId } from './runtime.js';\n\n/** Области аккаунта и конкретной сессии для локального состояния плагинов. */\nexport interface AuthIdentity {\n /** Идентификатор пользователя; отсутствует у непрозрачного или повреждённого токена. */\n userId?: UserId | undefined;\n /** Идентификатор серверной сессии; отсутствует у непрозрачного или повреждённого токена. */\n sessionId?: string | undefined;\n}\n\n/**\n * Что конвейер запросов спрашивает у авторизации.\n *\n * Узкий контракт вместо полноценного менеджера сессии: pipeline не должен знать ни про\n * refresh-токены, ни про хранилище, ни про вход по паролю. Благодаря этому клиент с готовым\n * токеном не тянет за собой сессионную машинерию — она подставляется вызывающим кодом.\n *\n * Каждый метод соответствует ровно одной стадии конвейера. Готовые реализации —\n * {@link bearerToken}, {@link tokenProvider} и {@link anonymousAuth}.\n */\nexport interface AuthProvider {\n /**\n * Текущий токен доступа.\n *\n * Нужен там, где заголовок не поставить: SSE в браузере и WebSocket передают токен\n * параметром адреса. Конвейеру запросов достаточно {@link currentHeaders}.\n */\n token(): Promise<string | null>;\n /**\n * Готовит состояние авторизации до входа транспортной попытки в очередь.\n *\n * Чтение хранилища, обращение к внешнему источнику токена и отложенный вход асинхронны,\n * поэтому обязаны завершиться до захвата слота очереди.\n */\n prepare(): Promise<void>;\n /**\n * Заголовки уже подготовленной авторизации.\n *\n * Синхронность существенна: слой стоит внутри очереди, непосредственно перед транспортом,\n * и не должен запускать I/O. Зато запрос, отстоявший в очереди, получает самый свежий токен.\n */\n currentHeaders(): Record<string, string>;\n /**\n * Реакция на ответ `401`.\n *\n * @returns `true`, если токен обновлён и повторять попытку имеет смысл\n */\n recover(): Promise<boolean>;\n /** Значение заголовка `X-Device-Id`. Отправляется и с анонимными запросами. */\n deviceId(): Promise<string>;\n /** Снимает подписки при терминальном освобождении владельца. */\n dispose(): void;\n}\n\n/**\n * Что фабрика авторизации получает от собранного runtime.\n *\n * `handler` — тот же обработчик, которым пользуются ресурсы: вход и продление проходят\n * через общий конвейер с точечными skip-флагами, а не через второй, независимо\n * эволюционирующий путь.\n *\n * @internal\n */\nexport interface AuthProviderDeps {\n config: ResolvedRuntimeConfig;\n handler: RequestHandler;\n cookies: CookieJar;\n /** Вызывается синхронно перед сменой владельца авторизации. */\n onAccountChange?: (() => void) | undefined;\n}\n\n/**\n * Идентификатор устройства, общий для провайдеров без сессии.\n *\n * Значение своё у каждого провайдера: сервер различает по нему записи в списке сессий,\n * поэтому делить его между клиентами нельзя. Хранилища нет, так что перезапуск процесса\n * значение не переживает — в этом и разница с полноценной сессией.\n */\nfunction localDeviceId(): () => Promise<string> {\n let deviceId: string | undefined;\n\n return () => {\n deviceId ??= createDeviceId();\n return Promise.resolve(deviceId);\n };\n}\n\n/**\n * Авторизации нет: заголовок не подставляется, ответ `401` не восстанавливается.\n *\n * @example\n * ```ts\n * const api = createRestClient(); // публичные эндпоинты доступны и без токена\n * ```\n */\nexport function anonymousAuth(): AuthProvider {\n return {\n token: () => Promise.resolve(null),\n prepare: () => Promise.resolve(),\n currentHeaders: () => ({}),\n recover: () => Promise.resolve(false),\n deviceId: localDeviceId(),\n dispose: () => {},\n };\n}\n\n/**\n * Готовый Bearer-токен: ни хранилища, ни продления.\n *\n * Ответ `401` уходит вызывающему коду как есть — обновить токен провайдеру нечем.\n * Для сессии, которая продлевает себя сама, нужен полный клиент.\n *\n * @example\n * ```ts\n * const api = createRestClient({ auth: bearerToken(process.env.ITD_TOKEN) });\n * ```\n */\nexport function bearerToken(accessToken: string): AuthProvider {\n const headers = { Authorization: `Bearer ${accessToken}` };\n\n return {\n token: () => Promise.resolve(accessToken),\n prepare: () => Promise.resolve(),\n currentHeaders: () => headers,\n recover: () => Promise.resolve(false),\n deviceId: localDeviceId(),\n dispose: () => {},\n };\n}\n\n/**\n * Токен из внешнего источника — хранилища секретов, кэша, соседнего сервиса.\n *\n * Источник спрашивается на стадии подготовки, до входа в очередь: там ожидание безопасно,\n * а слот транспорта ещё не занят. Значение держится до следующей подготовки, потому что\n * подстановка заголовков обязана быть синхронной.\n *\n * @example\n * ```ts\n * const api = createRestClient({ auth: tokenProvider(() => vault.read('itd')) });\n * ```\n */\nexport function tokenProvider(\n getToken: () => string | null | Promise<string | null>,\n): AuthProvider {\n let cached: string | null = null;\n\n const read = async () => {\n cached = await getToken();\n return cached;\n };\n\n return {\n token: read,\n prepare: async () => {\n await read();\n },\n currentHeaders: () => (cached ? { Authorization: `Bearer ${cached}` } : {}),\n recover: () => Promise.resolve(false),\n deviceId: localDeviceId(),\n dispose: () => {\n cached = null;\n },\n };\n}\n"],"mappings":";;;;;;;;;AAkFA,SAAS,gBAAuC;CAC9C,IAAI;CAEJ,aAAa;EACX,aAAaA,YAAAA,eAAe;EAC5B,OAAO,QAAQ,QAAQ,QAAQ;CACjC;AACF;;;;;;;;;AAUA,SAAgB,gBAA8B;CAC5C,OAAO;EACL,aAAa,QAAQ,QAAQ,IAAI;EACjC,eAAe,QAAQ,QAAQ;EAC/B,uBAAuB,CAAC;EACxB,eAAe,QAAQ,QAAQ,KAAK;EACpC,UAAU,cAAc;EACxB,eAAe,CAAC;CAClB;AACF;;;;;;;;;;;;AAaA,SAAgB,YAAY,aAAmC;CAC7D,MAAM,UAAU,EAAE,eAAe,UAAU,cAAc;CAEzD,OAAO;EACL,aAAa,QAAQ,QAAQ,WAAW;EACxC,eAAe,QAAQ,QAAQ;EAC/B,sBAAsB;EACtB,eAAe,QAAQ,QAAQ,KAAK;EACpC,UAAU,cAAc;EACxB,eAAe,CAAC;CAClB;AACF;;;;;;;;;;;;;AAcA,SAAgB,cACd,UACc;CACd,IAAI,SAAwB;CAE5B,MAAM,OAAO,YAAY;EACvB,SAAS,MAAM,SAAS;EACxB,OAAO;CACT;CAEA,OAAO;EACL,OAAO;EACP,SAAS,YAAY;GACnB,MAAM,KAAK;EACb;EACA,sBAAuB,SAAS,EAAE,eAAe,UAAU,SAAS,IAAI,CAAC;EACzE,eAAe,QAAQ,QAAQ,KAAK;EACpC,UAAU,cAAc;EACxB,eAAe;GACb,SAAS;EACX;CACF;AACF"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
//#region src/core/attachments/contracts.d.ts
|
|
2
|
+
/** Способ передачи содержимого вложения. */
|
|
3
|
+
declare const FileTransferMode: Readonly<{
|
|
4
|
+
/** Сначала получить файл целиком, затем отправить его как `Blob`. */
|
|
5
|
+
readonly Buffer: "buffer";
|
|
6
|
+
/** Передавать данные по мере чтения, не собирая файл целиком в памяти. */
|
|
7
|
+
readonly Stream: "stream";
|
|
8
|
+
}>;
|
|
9
|
+
type FileTransferMode = (typeof FileTransferMode)[keyof typeof FileTransferMode];
|
|
10
|
+
/** Что доступно источнику при открытии файла. */
|
|
11
|
+
interface FileContext {
|
|
12
|
+
/** Общая отмена текущей попытки, включая её таймаут. */
|
|
13
|
+
signal?: AbortSignal | undefined;
|
|
14
|
+
/** `fetch` клиента — с переданными пользователем настройками. */
|
|
15
|
+
fetch: typeof fetch;
|
|
16
|
+
/** Номер попытки отправки, начиная с 1. */
|
|
17
|
+
attempt: number;
|
|
18
|
+
}
|
|
19
|
+
/** Готовое содержимое файла. */
|
|
20
|
+
interface FileContent {
|
|
21
|
+
file: Blob | ArrayBuffer | Uint8Array;
|
|
22
|
+
/** Имя влияет на определение MIME, если `contentType` не задан. */
|
|
23
|
+
filename?: string | undefined;
|
|
24
|
+
/** MIME без параметров. Если не указан, определяется по имени или `Blob`. */
|
|
25
|
+
contentType?: string | undefined;
|
|
26
|
+
}
|
|
27
|
+
/** Открытый поток файла. */
|
|
28
|
+
interface FileStreamContent {
|
|
29
|
+
stream: ReadableStream<Uint8Array>;
|
|
30
|
+
filename?: string | undefined;
|
|
31
|
+
contentType?: string | undefined;
|
|
32
|
+
/** Размер, если он известен до чтения. */
|
|
33
|
+
size?: number | undefined;
|
|
34
|
+
/** Освобождает внешний ресурс после завершения попытки. */
|
|
35
|
+
close?: (() => void | Promise<void>) | undefined;
|
|
36
|
+
}
|
|
37
|
+
/** Буферный источник, который вычисляется непосредственно перед первой попыткой. */
|
|
38
|
+
interface LazyFile {
|
|
39
|
+
load: (context: FileContext) => FileContent | Promise<FileContent>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Повторяемый потоковый источник.
|
|
43
|
+
*
|
|
44
|
+
* `open` вызывается для каждой попытки отправки. Он обязан возвращать новый поток:
|
|
45
|
+
* уже прочитанный `ReadableStream` повторно использовать нельзя.
|
|
46
|
+
*/
|
|
47
|
+
interface StreamFile {
|
|
48
|
+
open: (context: FileContext) => FileStreamContent | Promise<FileStreamContent>;
|
|
49
|
+
}
|
|
50
|
+
/** Общие настройки потоковой передачи. */
|
|
51
|
+
interface FileStreamOptions {
|
|
52
|
+
/** Буферный или потоковый режим. По умолчанию `'buffer'`. */
|
|
53
|
+
mode?: FileTransferMode | undefined;
|
|
54
|
+
/** Максимальный размер файла. Без значения размер не ограничивается. */
|
|
55
|
+
maxBytes?: number | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Верхняя граница очереди, которой управляет библиотека. По умолчанию 4 МиБ.
|
|
58
|
+
*
|
|
59
|
+
* Сетевой рантайм может иметь собственные внутренние буферы сверх этого значения.
|
|
60
|
+
*/
|
|
61
|
+
streamBufferBytes?: number | undefined;
|
|
62
|
+
}
|
|
63
|
+
/** Файл, который нужно получить по HTTP(S). */
|
|
64
|
+
interface UrlFile extends UrlFileOptions {
|
|
65
|
+
url: string;
|
|
66
|
+
}
|
|
67
|
+
/** Настройки файла по HTTP(S). */
|
|
68
|
+
interface UrlFileOptions extends FileStreamOptions {
|
|
69
|
+
filename?: string | undefined;
|
|
70
|
+
/** По умолчанию берётся из `Content-Type` ответа. */
|
|
71
|
+
contentType?: string | undefined;
|
|
72
|
+
}
|
|
73
|
+
/** Файл для загрузки. Голый поток не принимается, потому что его нельзя повторить. */
|
|
74
|
+
type FileInput = Blob | ArrayBuffer | Uint8Array | FileContent | UrlFile | LazyFile | StreamFile;
|
|
75
|
+
/** Настройки фабрики пользовательского потока. */
|
|
76
|
+
interface FromStreamOptions extends Omit<FileStreamOptions, 'mode'> {
|
|
77
|
+
filename?: string | undefined;
|
|
78
|
+
contentType?: string | undefined;
|
|
79
|
+
/** Размер, если он известен заранее. */
|
|
80
|
+
size?: number | undefined;
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
export { FileStreamOptions as a, LazyFile as c, UrlFileOptions as d, FileStreamContent as i, StreamFile as l, FileContext as n, FileTransferMode as o, FileInput as r, FromStreamOptions as s, FileContent as t, UrlFile as u };
|
|
84
|
+
//# sourceMappingURL=contracts-BoT7msmq.d.cts.map
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
//#region src/core/attachments/contracts.d.ts
|
|
2
|
+
/** Способ передачи содержимого вложения. */
|
|
3
|
+
declare const FileTransferMode: Readonly<{
|
|
4
|
+
/** Сначала получить файл целиком, затем отправить его как `Blob`. */
|
|
5
|
+
readonly Buffer: "buffer";
|
|
6
|
+
/** Передавать данные по мере чтения, не собирая файл целиком в памяти. */
|
|
7
|
+
readonly Stream: "stream";
|
|
8
|
+
}>;
|
|
9
|
+
type FileTransferMode = (typeof FileTransferMode)[keyof typeof FileTransferMode];
|
|
10
|
+
/** Что доступно источнику при открытии файла. */
|
|
11
|
+
interface FileContext {
|
|
12
|
+
/** Общая отмена текущей попытки, включая её таймаут. */
|
|
13
|
+
signal?: AbortSignal | undefined;
|
|
14
|
+
/** `fetch` клиента — с переданными пользователем настройками. */
|
|
15
|
+
fetch: typeof fetch;
|
|
16
|
+
/** Номер попытки отправки, начиная с 1. */
|
|
17
|
+
attempt: number;
|
|
18
|
+
}
|
|
19
|
+
/** Готовое содержимое файла. */
|
|
20
|
+
interface FileContent {
|
|
21
|
+
file: Blob | ArrayBuffer | Uint8Array;
|
|
22
|
+
/** Имя влияет на определение MIME, если `contentType` не задан. */
|
|
23
|
+
filename?: string | undefined;
|
|
24
|
+
/** MIME без параметров. Если не указан, определяется по имени или `Blob`. */
|
|
25
|
+
contentType?: string | undefined;
|
|
26
|
+
}
|
|
27
|
+
/** Открытый поток файла. */
|
|
28
|
+
interface FileStreamContent {
|
|
29
|
+
stream: ReadableStream<Uint8Array>;
|
|
30
|
+
filename?: string | undefined;
|
|
31
|
+
contentType?: string | undefined;
|
|
32
|
+
/** Размер, если он известен до чтения. */
|
|
33
|
+
size?: number | undefined;
|
|
34
|
+
/** Освобождает внешний ресурс после завершения попытки. */
|
|
35
|
+
close?: (() => void | Promise<void>) | undefined;
|
|
36
|
+
}
|
|
37
|
+
/** Буферный источник, который вычисляется непосредственно перед первой попыткой. */
|
|
38
|
+
interface LazyFile {
|
|
39
|
+
load: (context: FileContext) => FileContent | Promise<FileContent>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Повторяемый потоковый источник.
|
|
43
|
+
*
|
|
44
|
+
* `open` вызывается для каждой попытки отправки. Он обязан возвращать новый поток:
|
|
45
|
+
* уже прочитанный `ReadableStream` повторно использовать нельзя.
|
|
46
|
+
*/
|
|
47
|
+
interface StreamFile {
|
|
48
|
+
open: (context: FileContext) => FileStreamContent | Promise<FileStreamContent>;
|
|
49
|
+
}
|
|
50
|
+
/** Общие настройки потоковой передачи. */
|
|
51
|
+
interface FileStreamOptions {
|
|
52
|
+
/** Буферный или потоковый режим. По умолчанию `'buffer'`. */
|
|
53
|
+
mode?: FileTransferMode | undefined;
|
|
54
|
+
/** Максимальный размер файла. Без значения размер не ограничивается. */
|
|
55
|
+
maxBytes?: number | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* Верхняя граница очереди, которой управляет библиотека. По умолчанию 4 МиБ.
|
|
58
|
+
*
|
|
59
|
+
* Сетевой рантайм может иметь собственные внутренние буферы сверх этого значения.
|
|
60
|
+
*/
|
|
61
|
+
streamBufferBytes?: number | undefined;
|
|
62
|
+
}
|
|
63
|
+
/** Файл, который нужно получить по HTTP(S). */
|
|
64
|
+
interface UrlFile extends UrlFileOptions {
|
|
65
|
+
url: string;
|
|
66
|
+
}
|
|
67
|
+
/** Настройки файла по HTTP(S). */
|
|
68
|
+
interface UrlFileOptions extends FileStreamOptions {
|
|
69
|
+
filename?: string | undefined;
|
|
70
|
+
/** По умолчанию берётся из `Content-Type` ответа. */
|
|
71
|
+
contentType?: string | undefined;
|
|
72
|
+
}
|
|
73
|
+
/** Файл для загрузки. Голый поток не принимается, потому что его нельзя повторить. */
|
|
74
|
+
type FileInput = Blob | ArrayBuffer | Uint8Array | FileContent | UrlFile | LazyFile | StreamFile;
|
|
75
|
+
/** Настройки фабрики пользовательского потока. */
|
|
76
|
+
interface FromStreamOptions extends Omit<FileStreamOptions, 'mode'> {
|
|
77
|
+
filename?: string | undefined;
|
|
78
|
+
contentType?: string | undefined;
|
|
79
|
+
/** Размер, если он известен заранее. */
|
|
80
|
+
size?: number | undefined;
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
export { FileStreamOptions as a, LazyFile as c, UrlFileOptions as d, FileStreamContent as i, StreamFile as l, FileContext as n, FileTransferMode as o, FileInput as r, FromStreamOptions as s, FileContent as t, UrlFile as u };
|
|
84
|
+
//# sourceMappingURL=contracts-BoT7msmq.d.ts.map
|