import { spfi, SPFI, SPFx } from '@pnp/sp'; import '@pnp/sp/webs'; import '@pnp/sp/lists'; import '@pnp/sp/items'; import '@pnp/sp/attachments'; import '@pnp/sp/site-users/web'; import { BrowserFetchWithRetry } from '@pnp/queryable'; import { WebPartContext } from '@microsoft/sp-webpart-base'; import { DATA_SITE_URL } from '../config'; // Мягкая ретрай-политика вместо стандартной PnPjs (retries: 3, interval: 200). // Дефолт слишком напорист: при отказе (429 «слишком много запросов», 503) он // начинает повторять почти сразу, что при массовой загрузке только усугубляет // throttling со стороны SharePoint. Здесь меньше попыток и заметно длиннее // первая пауза; интервал в PnPjs удваивается с каждой попыткой, так что выходит // примерно 1.5с → 3с. const RETRY = { retries: 2, interval: 1500 }; // Единый инициализированный клиент PnPjs на весь web part. let _sp: SPFI | undefined; /** * Инициализировать PnPjs контекстом web part. Вызывать один раз в onInit. * Клиент нацелен на сайт данных (DATA_SITE_URL), а не на текущий сайт, — * списки живут там, где бы ни стоял сам web part. Авторизация берётся из context. */ export function initSP(context: WebPartContext): void { _sp = spfi(DATA_SITE_URL).using(SPFx(context), BrowserFetchWithRetry(RETRY)); } /** * Клиент для ПРОИЗВОЛЬНОГО сайта (списки типов заявок живут на разных сайтах, * см. approval-route API). В отличие от getSP() не кэшируется — адрес сайта * приходит из данных. Ретрай-политика та же, что и у основного клиента: * BrowserFetchWithRetry идёт ПОСЛЕ SPFx, поэтому заменяет его обработчик * отправки (у behavior replace: true по умолчанию). */ export function spForSite(siteUrl: string, context: WebPartContext): SPFI { return spfi(siteUrl).using(SPFx(context), BrowserFetchWithRetry(RETRY)); } /** Получить инициализированный клиент. Бросает, если initSP не был вызван. */ export function getSP(): SPFI { if (!_sp) { throw new Error('PnPjs не инициализирован: вызовите initSP(context) в onInit web part.'); } return _sp; }